Compare commits

..

No commits in common. "main" and "v0.4.1" have entirely different histories.
main ... v0.4.1

17 changed files with 110 additions and 283 deletions

View file

@ -1,101 +0,0 @@
WarningsAsErrors: '*'
HeaderFilterRegex: '.*\.hpp'
FormatStyle: 'file'
Checks: >
-*,
bugprone-*,
-bugprone-easily-swappable-parameters,
-bugprone-forward-declaration-namespace,
-bugprone-forward-declaration-namespace,
-bugprone-macro-parentheses,
-bugprone-narrowing-conversions,
-bugprone-branch-clone,
-bugprone-assignment-in-if-condition,
concurrency-*,
-concurrency-mt-unsafe,
cppcoreguidelines-*,
-cppcoreguidelines-owning-memory,
-cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-pro-bounds-constant-array-index,
-cppcoreguidelines-avoid-const-or-ref-data-members,
-cppcoreguidelines-non-private-member-variables-in-classes,
-cppcoreguidelines-avoid-goto,
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
-cppcoreguidelines-avoid-do-while,
-cppcoreguidelines-avoid-non-const-global-variables,
-cppcoreguidelines-special-member-functions,
-cppcoreguidelines-explicit-virtual-functions,
-cppcoreguidelines-avoid-c-arrays,
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
-cppcoreguidelines-narrowing-conversions,
-cppcoreguidelines-pro-type-union-access,
-cppcoreguidelines-pro-type-member-init,
-cppcoreguidelines-macro-usage,
-cppcoreguidelines-macro-to-enum,
-cppcoreguidelines-init-variables,
-cppcoreguidelines-pro-type-cstyle-cast,
-cppcoreguidelines-pro-type-vararg,
-cppcoreguidelines-pro-type-reinterpret-cast,
google-global-names-in-headers,
-google-readability-casting,
google-runtime-operator,
misc-*,
-misc-unused-parameters,
-misc-no-recursion,
-misc-non-private-member-variables-in-classes,
-misc-include-cleaner,
-misc-use-anonymous-namespace,
-misc-const-correctness,
modernize-*,
-modernize-return-braced-init-list,
-modernize-use-trailing-return-type,
-modernize-use-using,
-modernize-use-override,
-modernize-avoid-c-arrays,
-modernize-macro-to-enum,
-modernize-loop-convert,
-modernize-use-nodiscard,
-modernize-pass-by-value,
-modernize-use-auto,
performance-*,
-performance-avoid-endl,
-performance-unnecessary-value-param,
portability-std-allocator-const,
readability-*,
-readability-function-cognitive-complexity,
-readability-function-size,
-readability-identifier-length,
-readability-magic-numbers,
-readability-uppercase-literal-suffix,
-readability-braces-around-statements,
-readability-redundant-access-specifiers,
-readability-else-after-return,
-readability-container-data-pointer,
-readability-implicit-bool-conversion,
-readability-avoid-nested-conditional-operator,
-readability-redundant-member-init,
-readability-redundant-string-init,
-readability-avoid-const-params-in-decls,
-readability-named-parameter,
-readability-convert-member-functions-to-static,
-readability-qualified-auto,
-readability-make-member-function-const,
-readability-isolate-declaration,
-readability-inconsistent-declaration-parameter-name,
-clang-diagnostic-error,
CheckOptions:
performance-for-range-copy.WarnOnAllAutoCopies: true
performance-inefficient-string-concatenation.StrictMode: true
readability-braces-around-statements.ShortStatementLines: 0
readability-identifier-naming.ClassCase: CamelCase
readability-identifier-naming.ClassIgnoredRegexp: I.*
readability-identifier-naming.ClassPrefix: C # We can't use regex here?!?!?!?
readability-identifier-naming.EnumCase: CamelCase
readability-identifier-naming.EnumPrefix: e
readability-identifier-naming.EnumConstantCase: UPPER_CASE
readability-identifier-naming.FunctionCase: camelBack
readability-identifier-naming.NamespaceCase: CamelCase
readability-identifier-naming.NamespacePrefix: N
readability-identifier-naming.StructPrefix: S
readability-identifier-naming.StructCase: CamelCase

View file

@ -5,7 +5,7 @@ string(STRIP ${VER_RAW} VERSION)
project( project(
hyprpicker hyprpicker
DESCRIPTION "A wlroots-compatible Wayland color picker that does not suck" DESCRIPTION "A blazing fast wayland wallpaper utility"
VERSION ${VERSION}) VERSION ${VERSION})
set(CMAKE_MESSAGE_LOG_LEVEL "STATUS") set(CMAKE_MESSAGE_LOG_LEVEL "STATUS")
@ -42,15 +42,15 @@ execute_process(
include_directories(.) include_directories(.)
set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD 23)
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
add_compile_options(-DWLR_USE_UNSTABLE) add_compile_options(-DWLR_USE_UNSTABLE)
add_compile_options( add_compile_options(
-Wall -Wall
-Wextra -Wextra
-Wuseless-cast
-Wno-unused-parameter -Wno-unused-parameter
-Wno-unused-value -Wno-unused-value
-Wno-missing-field-initializers) -Wno-missing-field-initializers
-Wno-narrowing
-Wno-pointer-arith)
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)

View file

@ -10,7 +10,17 @@ Launch it. Click. That's it.
## Options ## Options
See `hyprpicker --help`. `-f | --format=[fmt]` specifies the output format (`cmyk`, `hex`, `rgb`, `hsl`, `hsv`)
`-n | --no-fancy` disables the "fancy" (aka. colored) outputting
`-h | --help` prints a help message
`-a | --autocopy` automatically copies the output to the clipboard (requires [wl-clipboard](https://github.com/bugaevc/wl-clipboard))
`-r | --render-inactive` render (freeze) inactive displays too
`-z | --no-zoom` disable the zoom lens
# Building # Building

View file

@ -1 +1 @@
0.4.5 0.4.1

View file

@ -10,11 +10,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1737632363, "lastModified": 1727300645,
"narHash": "sha256-X9I8POSlHxBVjD0fiX1O2j7U9Zi1+4rIkrsyHP0uHXY=", "narHash": "sha256-OvAtVLaSRPnbXzOwlR1fVqCXR7i+ICRX3aPMCdIiv+c=",
"owner": "hyprwm", "owner": "hyprwm",
"repo": "hyprutils", "repo": "hyprutils",
"rev": "006620eb29d54ea9086538891404c78563d1bae1", "rev": "3f5293432b6dc6a99f26aca2eba3876d2660665c",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -33,11 +33,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1735493474, "lastModified": 1726874836,
"narHash": "sha256-fktzv4NaqKm94VAkAoVqO/nqQlw+X0/tJJNAeCSfzK4=", "narHash": "sha256-VKR0sf0PSNCB0wPHVKSAn41mCNVCnegWmgkrneKDhHM=",
"owner": "hyprwm", "owner": "hyprwm",
"repo": "hyprwayland-scanner", "repo": "hyprwayland-scanner",
"rev": "de913476b59ee88685fdc018e77b8f6637a2ae0b", "rev": "500c81a9e1a76760371049a8d99e008ea77aa59e",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -48,11 +48,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1737469691, "lastModified": 1727122398,
"narHash": "sha256-nmKOgAU48S41dTPIXAq0AHZSehWUn6ZPrUKijHAMmIk=", "narHash": "sha256-o8VBeCWHBxGd4kVMceIayf5GApqTavJbTa44Xcg5Rrk=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "9e4d5190a9482a1fb9d18adf0bdb83c6e506eaab", "rev": "30439d93eb8b19861ccbe3e581abf97bdc91b093",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -45,7 +45,7 @@
inputs.hyprwayland-scanner.overlays.default inputs.hyprwayland-scanner.overlays.default
(final: prev: { (final: prev: {
hyprpicker = prev.callPackage ./nix/default.nix { hyprpicker = prev.callPackage ./nix/default.nix {
stdenv = prev.gcc14Stdenv; stdenv = prev.gcc13Stdenv;
version = version + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty"); version = version + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty");
}; };
hyprpicker-debug = final.hyprpicker.override {debug = true;}; hyprpicker-debug = final.hyprpicker.override {debug = true;};

View file

@ -17,7 +17,7 @@
pango, pango,
pcre, pcre,
pcre2, pcre2,
util-linux, utillinux,
wayland, wayland,
wayland-protocols, wayland-protocols,
wayland-scanner, wayland-scanner,
@ -56,7 +56,7 @@ stdenv.mkDerivation {
pango pango
pcre pcre
pcre2 pcre2
util-linux utillinux
wayland wayland
wayland-protocols wayland-protocols
wayland-scanner wayland-scanner

View file

@ -2,7 +2,6 @@
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <print>
#include "../includes.hpp" #include "../includes.hpp"
@ -39,7 +38,7 @@ void Debug::log(LogLevel level, const char* fmt, ...) {
outputStr = (char*)malloc(logLen + 1); outputStr = (char*)malloc(logLen + 1);
if (!outputStr) { if (!outputStr) {
std::print("CRITICAL: Cannot alloc size {} for log! (Out of memory?)", logLen + 1); printf("CRITICAL: Cannot alloc size %d for log! (Out of memory?)", logLen + 1);
return; return;
} }

View file

@ -2,7 +2,9 @@
#include "../hyprpicker.hpp" #include "../hyprpicker.hpp"
CLayerSurface::CLayerSurface(SMonitor* pMonitor) : m_pMonitor(pMonitor) { CLayerSurface::CLayerSurface(SMonitor* pMonitor) {
m_pMonitor = pMonitor;
pSurface = makeShared<CCWlSurface>(g_pHyprpicker->m_pCompositor->sendCreateSurface()); pSurface = makeShared<CCWlSurface>(g_pHyprpicker->m_pCompositor->sendCreateSurface());
if (!pSurface) { if (!pSurface) {
@ -19,9 +21,6 @@ CLayerSurface::CLayerSurface(SMonitor* pMonitor) : m_pMonitor(pMonitor) {
pFractionalScale = makeShared<CCWpFractionalScaleV1>(g_pHyprpicker->m_pFractionalMgr->sendGetFractionalScale(pSurface->resource())); pFractionalScale = makeShared<CCWpFractionalScaleV1>(g_pHyprpicker->m_pFractionalMgr->sendGetFractionalScale(pSurface->resource()));
pFractionalScale->setPreferredScale([this](CCWpFractionalScaleV1* r, uint32_t scale120) { // pFractionalScale->setPreferredScale([this](CCWpFractionalScaleV1* r, uint32_t scale120) { //
Debug::log(TRACE, "Received a preferredScale for %s: %.2f", m_pMonitor->name.c_str(), scale120 / 120.F); Debug::log(TRACE, "Received a preferredScale for %s: %.2f", m_pMonitor->name.c_str(), scale120 / 120.F);
fractionalScale = scale120 / 120.F;
wantsReload = true;
g_pHyprpicker->recheckACK();
}); });
} }
@ -66,17 +65,15 @@ CLayerSurface::~CLayerSurface() {
static void onCallbackDone(CLayerSurface* surf, uint32_t when) { static void onCallbackDone(CLayerSurface* surf, uint32_t when) {
surf->frameCallback.reset(); surf->frameCallback.reset();
g_pHyprpicker->renderSurface(surf); if (surf->dirty || !surf->rendered)
g_pHyprpicker->renderSurface(g_pHyprpicker->m_pLastSurface);
} }
void CLayerSurface::sendFrame() { void CLayerSurface::sendFrame() {
lastBuffer = lastBuffer == 0 ? 1 : 0;
const auto& PBUFFER = buffers[lastBuffer];
frameCallback = makeShared<CCWlCallback>(pSurface->sendFrame()); frameCallback = makeShared<CCWlCallback>(pSurface->sendFrame());
frameCallback->setDone([this](CCWlCallback* r, uint32_t when) { onCallbackDone(this, when); }); frameCallback->setDone([this](CCWlCallback* r, uint32_t when) { onCallbackDone(this, when); });
pSurface->sendDamageBuffer(0, 0, 0xFFFF, 0xFFFF); const auto& PBUFFER = lastBuffer == 0 ? buffers[0] : buffers[1];
pSurface->sendAttach(PBUFFER->buffer.get(), 0, 0); pSurface->sendAttach(PBUFFER->buffer.get(), 0, 0);
if (!g_pHyprpicker->m_bNoFractional) { if (!g_pHyprpicker->m_bNoFractional) {
@ -85,12 +82,16 @@ void CLayerSurface::sendFrame() {
} else } else
pSurface->sendSetBufferScale(m_pMonitor->scale); pSurface->sendSetBufferScale(m_pMonitor->scale);
pSurface->sendDamageBuffer(0, 0, 0xFFFF, 0xFFFF);
pSurface->sendCommit(); pSurface->sendCommit();
dirty = false;
} }
void CLayerSurface::markDirty() { void CLayerSurface::markDirty() {
frameCallback = makeShared<CCWlCallback>(pSurface->sendFrame()); frameCallback = makeShared<CCWlCallback>(pSurface->sendFrame());
frameCallback->setDone([this](CCWlCallback* r, uint32_t when) { onCallbackDone(this, when); }); frameCallback->setDone([this](CCWlCallback* r, uint32_t when) { onCallbackDone(this, when); });
pSurface->sendCommit();
dirty = true; dirty = true;
} }

View file

@ -20,11 +20,9 @@ class CLayerSurface {
SP<CCWpViewport> pViewport = nullptr; SP<CCWpViewport> pViewport = nullptr;
SP<CCWpFractionalScaleV1> pFractionalScale = nullptr; SP<CCWpFractionalScaleV1> pFractionalScale = nullptr;
float fractionalScale = 1.F; bool wantsACK = false;
bool wantsACK = false; uint32_t ACKSerial = 0;
bool wantsReload = false; bool working = false;
uint32_t ACKSerial = 0;
bool working = false;
int lastBuffer = 0; int lastBuffer = 0;
SP<SPoolBuffer> buffers[2]; SP<SPoolBuffer> buffers[2];

View file

@ -39,8 +39,6 @@ void SMonitor::initSCFrame() {
if (pLS->m_pMonitor->transform % 2 == 1) if (pLS->m_pMonitor->transform % 2 == 1)
std::swap(transformedSize.x, transformedSize.y); std::swap(transformedSize.x, transformedSize.y);
Debug::log(TRACE, "Frame ready: pixel %.0fx%.0f, xfmd: %.0fx%.0f", pLS->screenBuffer->pixelSize.x, pLS->screenBuffer->pixelSize.y, transformedSize.x, transformedSize.y);
SP<SPoolBuffer> newBuf = makeShared<SPoolBuffer>(transformedSize, pLS->screenBufferFormat, transformedSize.x * 4); SP<SPoolBuffer> newBuf = makeShared<SPoolBuffer>(transformedSize, pLS->screenBufferFormat, transformedSize.x * 4);
int bytesPerPixel = pLS->screenBuffer->stride / (int)pLS->screenBuffer->pixelSize.x; int bytesPerPixel = pLS->screenBuffer->stride / (int)pLS->screenBuffer->pixelSize.x;
@ -114,7 +112,7 @@ void SMonitor::initSCFrame() {
pSCFrame.reset(); pSCFrame.reset();
}); });
pSCFrame->setFailed([](CCZwlrScreencopyFrameV1* r) { pSCFrame->setFailed([this](CCZwlrScreencopyFrameV1* r) {
Debug::log(CRIT, "Failed to get a Screencopy!"); Debug::log(CRIT, "Failed to get a Screencopy!");
g_pHyprpicker->finish(1); g_pHyprpicker->finish(1);
}); });

View file

@ -11,7 +11,7 @@ SPoolBuffer::SPoolBuffer(const Vector2D& pixelSize_, uint32_t format_, uint32_t
g_pHyprpicker->finish(1); g_pHyprpicker->finish(1);
} }
const auto DATA = mmap(nullptr, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, FD, 0); const auto DATA = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, FD, 0);
size = SIZE; size = SIZE;
data = DATA; data = DATA;

View file

@ -1,5 +1,5 @@
#include "hyprpicker.hpp" #include "hyprpicker.hpp"
#include <csignal> #include <signal.h>
void sigHandler(int sig) { void sigHandler(int sig) {
g_pHyprpicker->m_vLayerSurfaces.clear(); g_pHyprpicker->m_vLayerSurfaces.clear();
@ -147,17 +147,13 @@ void CHyprpicker::finish(int code) {
void CHyprpicker::recheckACK() { void CHyprpicker::recheckACK() {
for (auto& ls : m_vLayerSurfaces) { for (auto& ls : m_vLayerSurfaces) {
if ((ls->wantsACK || ls->wantsReload) && ls->screenBuffer) { if (ls->wantsACK) {
if (ls->wantsACK) ls->wantsACK = false;
ls->pLayerSurface->sendAckConfigure(ls->ACKSerial); ls->pLayerSurface->sendAckConfigure(ls->ACKSerial);
ls->wantsACK = false;
ls->wantsReload = false;
const auto MONITORSIZE = const auto MONITORSIZE = ls->screenBuffer && !g_pHyprpicker->m_bNoFractional ? ls->screenBuffer->pixelSize : ls->m_pMonitor->size * ls->m_pMonitor->scale;
(ls->screenBuffer && !g_pHyprpicker->m_bNoFractional ? ls->m_pMonitor->size * ls->fractionalScale : ls->m_pMonitor->size * ls->m_pMonitor->scale).round();
if (!ls->buffers[0] || ls->buffers[0]->pixelSize != MONITORSIZE) { if (!ls->buffers[0] || ls->buffers[0]->pixelSize != MONITORSIZE) {
Debug::log(TRACE, "making new buffers: size changed to %.0fx%.0f", MONITORSIZE.x, MONITORSIZE.y);
ls->buffers[0] = makeShared<SPoolBuffer>(MONITORSIZE, WL_SHM_FORMAT_ARGB8888, MONITORSIZE.x * 4); ls->buffers[0] = makeShared<SPoolBuffer>(MONITORSIZE, WL_SHM_FORMAT_ARGB8888, MONITORSIZE.x * 4);
ls->buffers[1] = makeShared<SPoolBuffer>(MONITORSIZE, WL_SHM_FORMAT_ARGB8888, MONITORSIZE.x * 4); ls->buffers[1] = makeShared<SPoolBuffer>(MONITORSIZE, WL_SHM_FORMAT_ARGB8888, MONITORSIZE.x * 4);
} }
@ -248,7 +244,7 @@ void CHyprpicker::convertBuffer(SP<SPoolBuffer> pBuffer) {
unsigned char green; unsigned char green;
unsigned char red; unsigned char red;
unsigned char alpha; unsigned char alpha;
}* px = (struct pixel*)(data + (y * (int)pBuffer->pixelSize.x * 4) + (x * 4)); }* px = (struct pixel*)(data + y * (int)pBuffer->pixelSize.x * 4 + x * 4);
std::swap(px->red, px->blue); std::swap(px->red, px->blue);
} }
@ -262,7 +258,7 @@ void CHyprpicker::convertBuffer(SP<SPoolBuffer> pBuffer) {
for (int y = 0; y < pBuffer->pixelSize.y; ++y) { for (int y = 0; y < pBuffer->pixelSize.y; ++y) {
for (int x = 0; x < pBuffer->pixelSize.x; ++x) { for (int x = 0; x < pBuffer->pixelSize.x; ++x) {
uint32_t* px = (uint32_t*)(data + (y * (int)pBuffer->pixelSize.x * 4) + (x * 4)); uint32_t* px = (uint32_t*)(data + y * (int)pBuffer->pixelSize.x * 4 + x * 4);
// conv to 8 bit // conv to 8 bit
uint8_t R = (uint8_t)std::round((255.0 * (((*px) & 0b00000000000000000000001111111111) >> 0) / 1023.0)); uint8_t R = (uint8_t)std::round((255.0 * (((*px) & 0b00000000000000000000001111111111) >> 0) / 1023.0));
@ -297,15 +293,15 @@ void* CHyprpicker::convert24To32Buffer(SP<SPoolBuffer> pBuffer) {
unsigned char blue; unsigned char blue;
unsigned char green; unsigned char green;
unsigned char red; unsigned char red;
}* srcPx = (struct pixel3*)(oldBuffer + (y * pBuffer->stride) + (x * 3)); }* srcPx = (struct pixel3*)(oldBuffer + y * pBuffer->stride + x * 3);
struct pixel4 { struct pixel4 {
// little-endian ARGB // little-endian ARGB
unsigned char blue; unsigned char blue;
unsigned char green; unsigned char green;
unsigned char red; unsigned char red;
unsigned char alpha; unsigned char alpha;
}* dstPx = (struct pixel4*)(newBuffer + (y * newBufferStride) + (x * 4)); }* dstPx = (struct pixel4*)(newBuffer + y * newBufferStride + x * 4);
*dstPx = {.blue = srcPx->red, .green = srcPx->green, .red = srcPx->blue, .alpha = 0xFF}; *dstPx = {srcPx->red, srcPx->green, srcPx->blue, 0xFF};
} }
} }
} break; } break;
@ -317,15 +313,15 @@ void* CHyprpicker::convert24To32Buffer(SP<SPoolBuffer> pBuffer) {
unsigned char red; unsigned char red;
unsigned char green; unsigned char green;
unsigned char blue; unsigned char blue;
}* srcPx = (struct pixel3*)(oldBuffer + (y * pBuffer->stride) + (x * 3)); }* srcPx = (struct pixel3*)(oldBuffer + y * pBuffer->stride + x * 3);
struct pixel4 { struct pixel4 {
// big-endian ARGB // big-endian ARGB
unsigned char alpha; unsigned char alpha;
unsigned char red; unsigned char red;
unsigned char green; unsigned char green;
unsigned char blue; unsigned char blue;
}* dstPx = (struct pixel4*)(newBuffer + (y * newBufferStride) + (x * 4)); }* dstPx = (struct pixel4*)(newBuffer + y * newBufferStride + x * 4);
*dstPx = {.alpha = 0xFF, .red = srcPx->red, .green = srcPx->green, .blue = srcPx->blue}; *dstPx = {0xFF, srcPx->red, srcPx->green, srcPx->blue};
} }
} }
} break; } break;
@ -341,7 +337,6 @@ void CHyprpicker::renderSurface(CLayerSurface* pSurface, bool forceInactive) {
const auto PBUFFER = getBufferForLS(pSurface); const auto PBUFFER = getBufferForLS(pSurface);
if (!PBUFFER || !pSurface->screenBuffer) { if (!PBUFFER || !pSurface->screenBuffer) {
// Spammy log, doesn't matter.
// Debug::log(ERR, PBUFFER ? "renderSurface: pSurface->screenBuffer null" : "renderSurface: PBUFFER null"); // Debug::log(ERR, PBUFFER ? "renderSurface: pSurface->screenBuffer null" : "renderSurface: PBUFFER null");
return; return;
} }
@ -365,8 +360,6 @@ void CHyprpicker::renderSurface(CLayerSurface* pSurface, bool forceInactive) {
const auto MOUSECOORDSABS = m_vLastCoords.floor() / pSurface->m_pMonitor->size; const auto MOUSECOORDSABS = m_vLastCoords.floor() / pSurface->m_pMonitor->size;
const auto CLICKPOS = MOUSECOORDSABS * PBUFFER->pixelSize; const auto CLICKPOS = MOUSECOORDSABS * PBUFFER->pixelSize;
Debug::log(TRACE, "renderSurface: scalebufs %.2fx%.2f", SCALEBUFS.x, SCALEBUFS.y);
const auto PATTERNPRE = cairo_pattern_create_for_surface(pSurface->screenBuffer->surface); const auto PATTERNPRE = cairo_pattern_create_for_surface(pSurface->screenBuffer->surface);
cairo_pattern_set_filter(PATTERNPRE, CAIRO_FILTER_BILINEAR); cairo_pattern_set_filter(PATTERNPRE, CAIRO_FILTER_BILINEAR);
cairo_matrix_t matrixPre; cairo_matrix_t matrixPre;
@ -389,7 +382,6 @@ void CHyprpicker::renderSurface(CLayerSurface* pSurface, bool forceInactive) {
// | | // | |
// | --------- | // | --------- |
// //
// (hex code here)
cairo_restore(PCAIRO); cairo_restore(PCAIRO);
if (!m_bNoZoom) { if (!m_bNoZoom) {
@ -419,69 +411,17 @@ void CHyprpicker::renderSurface(CLayerSurface* pSurface, bool forceInactive) {
cairo_matrix_init_identity(&matrix); cairo_matrix_init_identity(&matrix);
cairo_matrix_translate(&matrix, CLICKPOSBUF.x + 0.5f, CLICKPOSBUF.y + 0.5f); cairo_matrix_translate(&matrix, CLICKPOSBUF.x + 0.5f, CLICKPOSBUF.y + 0.5f);
cairo_matrix_scale(&matrix, 0.1f, 0.1f); cairo_matrix_scale(&matrix, 0.1f, 0.1f);
cairo_matrix_translate(&matrix, (-CLICKPOSBUF.x / SCALEBUFS.x) - 0.5f, (-CLICKPOSBUF.y / SCALEBUFS.y) - 0.5f); cairo_matrix_translate(&matrix, -CLICKPOSBUF.x / SCALEBUFS.x - 0.5f, -CLICKPOSBUF.y / SCALEBUFS.y - 0.5f);
cairo_pattern_set_matrix(PATTERN, &matrix); cairo_pattern_set_matrix(PATTERN, &matrix);
cairo_set_source(PCAIRO, PATTERN); cairo_set_source(PCAIRO, PATTERN);
cairo_arc(PCAIRO, CLICKPOS.x, CLICKPOS.y, 100 / SCALEBUFS.x, 0, 2 * M_PI); cairo_arc(PCAIRO, CLICKPOS.x, CLICKPOS.y, 100 / SCALEBUFS.x, 0, 2 * M_PI);
cairo_clip(PCAIRO); cairo_clip(PCAIRO);
cairo_paint(PCAIRO); cairo_paint(PCAIRO);
if (!m_bDisableHexPreview) { cairo_surface_flush(PBUFFER->surface);
const auto currentColor = getColorFromPixel(pSurface, CLICKPOS);
std::string hexBuffer;
if (m_bUseLowerCase)
hexBuffer = std::format("#{:02x}{:02x}{:02x}", currentColor.r, currentColor.g, currentColor.b);
else
hexBuffer = std::format("#{:02X}{:02X}{:02X}", currentColor.r, currentColor.g, currentColor.b);
cairo_set_source_rgba(PCAIRO, 0.0, 0.0, 0.0, 0.5);
double x, y, width = 85, height = 28, radius = 6;
if (CLICKPOS.y > (PBUFFER->pixelSize.y - 50) && CLICKPOS.x > (PBUFFER->pixelSize.x - 100)) {
x = CLICKPOS.x - 80;
y = CLICKPOS.y - 40;
} else if (CLICKPOS.y > (PBUFFER->pixelSize.y - 50)) {
x = CLICKPOS.x;
y = CLICKPOS.y - 40;
} else if (CLICKPOS.x > (PBUFFER->pixelSize.x - 100)) {
x = CLICKPOS.x - 80;
y = CLICKPOS.y + 20;
} else {
x = CLICKPOS.x;
y = CLICKPOS.y + 20;
}
cairo_move_to(PCAIRO, x + radius, y);
cairo_arc(PCAIRO, x + width - radius, y + radius, radius, -M_PI_2, 0);
cairo_arc(PCAIRO, x + width - radius, y + height - radius, radius, 0, M_PI_2);
cairo_arc(PCAIRO, x + radius, y + height - radius, radius, M_PI_2, M_PI);
cairo_arc(PCAIRO, x + radius, y + radius, radius, M_PI, -M_PI_2);
cairo_close_path(PCAIRO);
cairo_fill(PCAIRO);
cairo_set_source_rgba(PCAIRO, 1.0, 1.0, 1.0, 1.0);
cairo_select_font_face(PCAIRO, "monospace", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size(PCAIRO, 18);
double padding = 5.0;
double textX = x + padding;
if (CLICKPOS.y > (PBUFFER->pixelSize.y - 50) && CLICKPOS.x > (PBUFFER->pixelSize.x - 100))
cairo_move_to(PCAIRO, textX, CLICKPOS.y - 20);
else if (CLICKPOS.y > (PBUFFER->pixelSize.y - 50))
cairo_move_to(PCAIRO, textX, CLICKPOS.y - 20);
else if (CLICKPOS.x > (PBUFFER->pixelSize.x - 100))
cairo_move_to(PCAIRO, textX, CLICKPOS.y + 40);
else
cairo_move_to(PCAIRO, textX, CLICKPOS.y + 40);
cairo_show_text(PCAIRO, hexBuffer.c_str());
cairo_surface_flush(PBUFFER->surface);
}
cairo_restore(PCAIRO); cairo_restore(PCAIRO);
cairo_pattern_destroy(PATTERN); cairo_pattern_destroy(PATTERN);
} }
} else if (!m_bRenderInactive) { } else if (!m_bRenderInactive) {
@ -501,6 +441,7 @@ void CHyprpicker::renderSurface(CLayerSurface* pSurface, bool forceInactive) {
cairo_paint(PCAIRO); cairo_paint(PCAIRO);
cairo_surface_flush(PBUFFER->surface); cairo_surface_flush(PBUFFER->surface);
cairo_pattern_destroy(PATTERNPRE); cairo_pattern_destroy(PATTERNPRE);
} }
@ -516,20 +457,15 @@ void CHyprpicker::renderSurface(CLayerSurface* pSurface, bool forceInactive) {
} }
CColor CHyprpicker::getColorFromPixel(CLayerSurface* pLS, Vector2D pix) { CColor CHyprpicker::getColorFromPixel(CLayerSurface* pLS, Vector2D pix) {
pix = pix.floor();
if (pix.x >= pLS->screenBuffer->pixelSize.x || pix.y >= pLS->screenBuffer->pixelSize.y || pix.x < 0 || pix.y < 0)
return CColor{.r = 0, .g = 0, .b = 0, .a = 0};
void* dataSrc = pLS->screenBuffer->paddedData ? pLS->screenBuffer->paddedData : pLS->screenBuffer->data; void* dataSrc = pLS->screenBuffer->paddedData ? pLS->screenBuffer->paddedData : pLS->screenBuffer->data;
struct pixel { struct pixel {
unsigned char blue; unsigned char blue;
unsigned char green; unsigned char green;
unsigned char red; unsigned char red;
unsigned char alpha; unsigned char alpha;
}* px = (struct pixel*)((char*)dataSrc + ((ptrdiff_t)pix.y * (int)pLS->screenBuffer->pixelSize.x * 4) + ((ptrdiff_t)pix.x * 4)); }* px = (struct pixel*)((char*)dataSrc + (int)pix.y * (int)pLS->screenBuffer->pixelSize.x * 4 + (int)pix.x * 4);
return CColor{.r = px->red, .g = px->green, .b = px->blue, .a = px->alpha}; return CColor{(uint8_t)px->red, (uint8_t)px->green, (uint8_t)px->blue, (uint8_t)px->alpha};
} }
void CHyprpicker::initKeyboard() { void CHyprpicker::initKeyboard() {
@ -542,7 +478,7 @@ void CHyprpicker::initKeyboard() {
return; return;
} }
const char* buf = (const char*)mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0); const char* buf = (const char*)mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
if (buf == MAP_FAILED) { if (buf == MAP_FAILED) {
Debug::log(ERR, "Failed to mmap xkb keymap: %d", errno); Debug::log(ERR, "Failed to mmap xkb keymap: %d", errno);
return; return;
@ -578,12 +514,14 @@ void CHyprpicker::initKeyboard() {
} }
void CHyprpicker::initMouse() { void CHyprpicker::initMouse() {
m_pPointer->setEnter([this](CCWlPointer* r, uint32_t serial, wl_proxy* surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { m_pPointer->setEnter([this](CCWlPointer* r, uint32_t serial, wl_resource* surface, wl_fixed_t surface_x, wl_fixed_t surface_y) {
auto x = wl_fixed_to_double(surface_x); auto x = wl_fixed_to_double(surface_x);
auto y = wl_fixed_to_double(surface_y); auto y = wl_fixed_to_double(surface_y);
m_vLastCoords = {x, y}; m_vLastCoords = {x, y};
markDirty();
for (auto& ls : m_vLayerSurfaces) { for (auto& ls : m_vLayerSurfaces) {
if (ls->pSurface->resource() == surface) { if (ls->pSurface->resource() == surface) {
m_pLastSurface = ls.get(); m_pLastSurface = ls.get();
@ -592,19 +530,13 @@ void CHyprpicker::initMouse() {
} }
m_pCursorShapeDevice->sendSetShape(serial, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CROSSHAIR); m_pCursorShapeDevice->sendSetShape(serial, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CROSSHAIR);
markDirty();
}); });
m_pPointer->setLeave([this](CCWlPointer* r, uint32_t timeMs, wl_proxy* surface) { m_pPointer->setLeave([this](CCWlPointer* r, uint32_t timeMs, wl_resource* surf) {
for (auto& ls : m_vLayerSurfaces) { for (auto& ls : m_vLayerSurfaces) {
if (ls->pSurface->resource() == surface) { if (ls->pSurface->resource() == surf) {
if (m_pLastSurface == ls.get()) renderSurface(ls.get(), true);
m_pLastSurface = nullptr;
break;
} }
} }
markDirty();
}); });
m_pPointer->setMotion([this](CCWlPointer* r, uint32_t timeMs, wl_fixed_t surface_x, wl_fixed_t surface_y) { m_pPointer->setMotion([this](CCWlPointer* r, uint32_t timeMs, wl_fixed_t surface_x, wl_fixed_t surface_y) {
auto x = wl_fixed_to_double(surface_x); auto x = wl_fixed_to_double(surface_x);
@ -636,7 +568,7 @@ void CHyprpicker::initMouse() {
case OUTPUT_CMYK: { case OUTPUT_CMYK: {
// http://www.codeproject.com/KB/applications/xcmyk.aspx // http://www.codeproject.com/KB/applications/xcmyk.aspx
float r = 1 - (COL.r / 255.0f), g = 1 - (COL.g / 255.0f), b = 1 - (COL.b / 255.0f); float r = 1 - COL.r / 255.0f, g = 1 - COL.g / 255.0f, b = 1 - COL.b / 255.0f;
float k = fmin3(r, g, b), K = (k == 1) ? 1 : 1 - k; float k = fmin3(r, g, b), K = (k == 1) ? 1 : 1 - k;
float c = (r - k) / K, m = (g - k) / K, y = (b - k) / K; float c = (r - k) / K, m = (g - k) / K, y = (b - k) / K;
@ -656,8 +588,8 @@ void CHyprpicker::initMouse() {
break; break;
} }
case OUTPUT_HEX: { case OUTPUT_HEX: {
auto toHex = [this](int i) -> std::string { auto toHex = [](int i) -> std::string {
const char* DS = m_bUseLowerCase ? "0123456789abcdef" : "0123456789ABCDEF"; const char* DS = "0123456789ABCDEF";
std::string result = ""; std::string result = "";
@ -667,10 +599,6 @@ void CHyprpicker::initMouse() {
return result; return result;
}; };
auto hexR = toHex(COL.r);
auto hexG = toHex(COL.g);
auto hexB = toHex(COL.b);
if (m_bFancyOutput) if (m_bFancyOutput)
Debug::log(NONE, "\033[38;2;%i;%i;%i;48;2;%i;%i;%im#%s%s%s\033[0m", FG, FG, FG, COL.r, COL.g, COL.b, toHex(COL.r).c_str(), toHex(COL.g).c_str(), Debug::log(NONE, "\033[38;2;%i;%i;%i;48;2;%i;%i;%im#%s%s%s\033[0m", FG, FG, FG, COL.r, COL.g, COL.b, toHex(COL.r).c_str(), toHex(COL.g).c_str(),
toHex(COL.b).c_str()); toHex(COL.b).c_str());
@ -727,7 +655,7 @@ void CHyprpicker::initMouse() {
l_or_v = std::round(v * 100); l_or_v = std::round(v * 100);
} }
h = std::round(h < 0 ? h + 360 : h); h = std::round(h);
s = std::round(s * 100); s = std::round(s * 100);
if (m_bFancyOutput) if (m_bFancyOutput)

View file

@ -40,12 +40,10 @@ class CHyprpicker {
bool m_bFancyOutput = true; bool m_bFancyOutput = true;
bool m_bAutoCopy = false; bool m_bAutoCopy = false;
bool m_bRenderInactive = false; bool m_bRenderInactive = false;
bool m_bNoZoom = false; bool m_bNoZoom = false;
bool m_bNoFractional = false; bool m_bNoFractional = false;
bool m_bDisableHexPreview = false;
bool m_bUseLowerCase = false;
bool m_bRunning = true; bool m_bRunning = true;

View file

@ -4,11 +4,12 @@
#include <deque> #include <deque>
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include <cstring> #include <string.h>
#include <string> #include <string>
#include <pthread.h> #include <pthread.h>
#include <cmath> #include <cmath>
#include <math.h>
#include "protocols/cursor-shape-v1.hpp" #include "protocols/cursor-shape-v1.hpp"
#include "protocols/fractional-scale-v1.hpp" #include "protocols/fractional-scale-v1.hpp"
@ -17,13 +18,14 @@
#include "protocols/viewporter.hpp" #include "protocols/viewporter.hpp"
#include "protocols/wayland.hpp" #include "protocols/wayland.hpp"
#include <cassert> #include <assert.h>
#include <cairo.h> #include <cairo.h>
#include <cairo/cairo.h> #include <cairo/cairo.h>
#include <fcntl.h> #include <fcntl.h>
#include <getopt.h> #include <getopt.h>
#include <cstdio> #include <stdio.h>
#include <cstdlib> #include <stdlib.h>
#include <string.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <unistd.h> #include <unistd.h>
#include <wayland-client.h> #include <wayland-client.h>

View file

@ -4,20 +4,18 @@
#include "hyprpicker.hpp" #include "hyprpicker.hpp"
static void help() { static void help(void) {
std::cout << "Hyprpicker usage: hyprpicker [arg [...]].\n\nArguments:\n" std::cout << "Hyprpicker usage: hyprpicker [arg [...]].\n\nArguments:\n"
<< " -a | --autocopy | Automatically copies the output to the clipboard (requires wl-clipboard)\n" << " -a | --autocopy | Automatically copies the output to the clipboard (requires wl-clipboard)\n"
<< " -f | --format=fmt | Specifies the output format (cmyk, hex, rgb, hsl, hsv)\n" << " -f | --format=fmt | Specifies the output format (cmyk, hex, rgb, hsl, hsv)\n"
<< " -n | --no-fancy | Disables the \"fancy\" (aka. colored) outputting\n" << " -n | --no-fancy | Disables the \"fancy\" (aka. colored) outputting\n"
<< " -h | --help | Show this help message\n" << " -h | --help | Show this help message\n"
<< " -r | --render-inactive | Render (freeze) inactive displays\n" << " -r | --render-inactive | Render (freeze) inactive displays\n"
<< " -z | --no-zoom | Disable the zoom lens\n" << " -z | --no-zoom | Disable the zoom lens\n"
<< " -q | --quiet | Disable most logs (leaves errors)\n" << " -q | --quiet | Disable most logs (leaves errors)\n"
<< " -v | --verbose | Enable more logs\n" << " -v | --verbose | Enable more logs\n"
<< " -t | --no-fractional | Disable fractional scaling support\n" << " -t | --no-fractional | Disable fractional scaling support\n"
<< " -d | --disable-hex-preview | Disable live preview of Hex code\n" << " -V | --version | Print version info\n";
<< " -l | --lowercase-hex | Outputs the hexcode in lowercase\n"
<< " -V | --version | Print version info\n";
} }
int main(int argc, char** argv, char** envp) { int main(int argc, char** argv, char** envp) {
@ -25,21 +23,19 @@ int main(int argc, char** argv, char** envp) {
while (true) { while (true) {
int option_index = 0; int option_index = 0;
static struct option long_options[] = {{"autocopy", no_argument, nullptr, 'a'}, static struct option long_options[] = {{"autocopy", no_argument, NULL, 'a'},
{"format", required_argument, nullptr, 'f'}, {"format", required_argument, NULL, 'f'},
{"help", no_argument, nullptr, 'h'}, {"help", no_argument, NULL, 'h'},
{"no-fancy", no_argument, nullptr, 'n'}, {"no-fancy", no_argument, NULL, 'n'},
{"render-inactive", no_argument, nullptr, 'r'}, {"render-inactive", no_argument, NULL, 'r'},
{"no-zoom", no_argument, nullptr, 'z'}, {"no-zoom", no_argument, NULL, 'z'},
{"no-fractional", no_argument, nullptr, 't'}, {"no-fractional", no_argument, NULL, 't'},
{"quiet", no_argument, nullptr, 'q'}, {"quiet", no_argument, NULL, 'q'},
{"verbose", no_argument, nullptr, 'v'}, {"verbose", no_argument, NULL, 'v'},
{"disable-hex-preview", no_argument, nullptr, 'd'}, {"version", no_argument, NULL, 'V'},
{"lowercase-hex", no_argument, nullptr, 'l'}, {NULL, 0, NULL, 0}};
{"version", no_argument, nullptr, 'V'},
{nullptr, 0, nullptr, 0}};
int c = getopt_long(argc, argv, ":f:hnarzqvtdlV", long_options, &option_index); int c = getopt_long(argc, argv, ":f:hnarzqvtV", long_options, &option_index);
if (c == -1) if (c == -1)
break; break;
@ -68,8 +64,6 @@ int main(int argc, char** argv, char** envp) {
case 't': g_pHyprpicker->m_bNoFractional = true; break; case 't': g_pHyprpicker->m_bNoFractional = true; break;
case 'q': Debug::quiet = true; break; case 'q': Debug::quiet = true; break;
case 'v': Debug::verbose = true; break; case 'v': Debug::verbose = true; break;
case 'd': g_pHyprpicker->m_bDisableHexPreview = true; break;
case 'l': g_pHyprpicker->m_bUseLowerCase = true; break;
case 'V': { case 'V': {
std::cout << "hyprpicker v" << HYPRPICKER_VERSION << "\n"; std::cout << "hyprpicker v" << HYPRPICKER_VERSION << "\n";
exit(0); exit(0);