diff --git a/.github/workflows/nix-build.yaml b/.github/workflows/nix-build.yaml index e0a0d3d..3157198 100644 --- a/.github/workflows/nix-build.yaml +++ b/.github/workflows/nix-build.yaml @@ -7,11 +7,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Clone repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive - name: Install nix - uses: cachix/install-nix-action@v20 + uses: cachix/install-nix-action@v27 with: install_url: https://nixos.org/nix/install extra_nix_config: | diff --git a/CMakeLists.txt b/CMakeLists.txt index 6fcfbc1..69b6d57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ project( VERSION ${VERSION}) set(CMAKE_MESSAGE_LOG_LEVEL "STATUS") +set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE) add_compile_definitions(HYPRPICKER_VERSION="${VERSION}") diff --git a/flake.lock b/flake.lock index f14cabb..a5f9209 100644 --- a/flake.lock +++ b/flake.lock @@ -10,11 +10,11 @@ ] }, "locked": { - "lastModified": 1737632363, - "narHash": "sha256-X9I8POSlHxBVjD0fiX1O2j7U9Zi1+4rIkrsyHP0uHXY=", + "lastModified": 1739891528, + "narHash": "sha256-h8HOCZ/rw2Buzku+GKF77VXxrGjCSOQkLhptiEKMYg0=", "owner": "hyprwm", "repo": "hyprutils", - "rev": "006620eb29d54ea9086538891404c78563d1bae1", + "rev": "61a5382f4b1ab578064d470b1b3d3f0df396b8ba", "type": "github" }, "original": { @@ -33,11 +33,11 @@ ] }, "locked": { - "lastModified": 1735493474, - "narHash": "sha256-fktzv4NaqKm94VAkAoVqO/nqQlw+X0/tJJNAeCSfzK4=", + "lastModified": 1739870480, + "narHash": "sha256-SiDN5BGxa/1hAsqhgJsS03C3t2QrLgBT8u+ENJ0Qzwc=", "owner": "hyprwm", "repo": "hyprwayland-scanner", - "rev": "de913476b59ee88685fdc018e77b8f6637a2ae0b", + "rev": "206367a08dc5ac4ba7ad31bdca391d098082e64b", "type": "github" }, "original": { @@ -48,11 +48,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1737469691, - "narHash": "sha256-nmKOgAU48S41dTPIXAq0AHZSehWUn6ZPrUKijHAMmIk=", + "lastModified": 1739866667, + "narHash": "sha256-EO1ygNKZlsAC9avfcwHkKGMsmipUk1Uc0TbrEZpkn64=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "9e4d5190a9482a1fb9d18adf0bdb83c6e506eaab", + "rev": "73cf49b8ad837ade2de76f87eb53fc85ed5d4680", "type": "github" }, "original": { diff --git a/src/hyprpicker.cpp b/src/hyprpicker.cpp index f33d94f..6c4e044 100644 --- a/src/hyprpicker.cpp +++ b/src/hyprpicker.cpp @@ -425,17 +425,82 @@ void CHyprpicker::renderSurface(CLayerSurface* pSurface, bool forceInactive) { cairo_clip(PCAIRO); cairo_paint(PCAIRO); - if (!m_bDisableHexPreview) { + if (!m_bDisablePreview) { 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); + std::string previewBuffer; + + auto fmax3 = [](float a, float b, float c) -> float { return (a > b && a > c) ? a : (b > c) ? b : c; }; + auto fmin3 = [](float a, float b, float c) -> float { return (a < b && a < c) ? a : (b < c) ? b : c; }; + + switch (m_bSelectedOutputMode) { + case OUTPUT_CMYK: { + float r = 1 - currentColor.r / 255.0f, g = 1 - currentColor.g / 255.0f, b = 1 - currentColor.b / 255.0f; + 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; + + c = std::round(c * 100); + m = std::round(m * 100); + y = std::round(y * 100); + k = std::round(k * 100); + + previewBuffer = std::format("{}% {}% {}% {}%", c, m, y, k); + break; + } + case OUTPUT_HEX: { + previewBuffer = std::format("#{:02X}{:02X}{:02X}", currentColor.r, currentColor.g, currentColor.b); + break; + } + case OUTPUT_LHEX: { + previewBuffer = std::format("#{:02x}{:02x}{:02x}", currentColor.r, currentColor.g, currentColor.b); + break; + } + case OUTPUT_HSL: + case OUTPUT_HSV: { + auto floatEq = [](float a, float b) -> bool { + return std::nextafter(a, std::numeric_limits::lowest()) <= b && std::nextafter(a, std::numeric_limits::max()) >= b; + }; + + float h, s, l, v; + float r = currentColor.r / 255.0f, g = currentColor.g / 255.0f, b = currentColor.b / 255.0f; + float max = fmax3(r, g, b), min = fmin3(r, g, b); + float c = max - min; + + v = max; + if (c == 0) + h = 0; + else if (v == r) + h = 60 * (0 + (g - b) / c); + else if (v == g) + h = 60 * (2 + (b - r) / c); + else /* v == b */ + h = 60 * (4 + (r - g) / c); + + float l_or_v; + if (m_bSelectedOutputMode == OUTPUT_HSL) { + l = (max + min) / 2; + s = (floatEq(l, 0.0f) || floatEq(l, 1.0f)) ? 0 : (v - l) / std::min(l, 1 - l); + l_or_v = std::round(l * 100); + } else { + v = max; + s = floatEq(v, 0.0f) ? 0 : c / v; + l_or_v = std::round(v * 100); + } + + h = std::round(h < 0 ? h + 360 : h); + s = std::round(s * 100); + + previewBuffer = std::format("{} {}% {}%", h, s, l_or_v); + break; + } + case OUTPUT_RGB: { + previewBuffer = std::format("{} {} {}", currentColor.r, currentColor.g, currentColor.b); + break; + } + } cairo_set_source_rgba(PCAIRO, 0.0, 0.0, 0.0, 0.5); - double x, y, width = 85, height = 28, radius = 6; + double x, y, width = 8 + (11 * previewBuffer.length()), height = 28, radius = 6; if (CLICKPOS.y > (PBUFFER->pixelSize.y - 50) && CLICKPOS.x > (PBUFFER->pixelSize.x - 100)) { x = CLICKPOS.x - 80; @@ -450,7 +515,7 @@ void CHyprpicker::renderSurface(CLayerSurface* pSurface, bool forceInactive) { x = CLICKPOS.x; y = CLICKPOS.y + 20; } - + x -= 5.5 * previewBuffer.length(); 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); @@ -476,7 +541,7 @@ void CHyprpicker::renderSurface(CLayerSurface* pSurface, bool forceInactive) { else cairo_move_to(PCAIRO, textX, CLICKPOS.y + 40); - cairo_show_text(PCAIRO, hexBuffer.c_str()); + cairo_show_text(PCAIRO, previewBuffer.c_str()); cairo_surface_flush(PBUFFER->surface); } @@ -650,9 +715,10 @@ void CHyprpicker::initMouse() { finish(); break; } - case OUTPUT_HEX: { + case OUTPUT_HEX: + case OUTPUT_LHEX: { auto toHex = [this](int i) -> std::string { - const char* DS = m_bUseLowerCase ? "0123456789abcdef" : "0123456789ABCDEF"; + const char* DS = m_bSelectedOutputMode == OUTPUT_LHEX ? "0123456789abcdef" : "0123456789ABCDEF"; std::string result = ""; diff --git a/src/hyprpicker.hpp b/src/hyprpicker.hpp index 8890019..be1702e 100644 --- a/src/hyprpicker.hpp +++ b/src/hyprpicker.hpp @@ -7,6 +7,7 @@ enum eOutputMode { OUTPUT_CMYK = 0, OUTPUT_HEX, + OUTPUT_LHEX, OUTPUT_RGB, OUTPUT_HSL, OUTPUT_HSV @@ -40,12 +41,11 @@ class CHyprpicker { bool m_bFancyOutput = true; - bool m_bAutoCopy = false; - bool m_bRenderInactive = false; - bool m_bNoZoom = false; - bool m_bNoFractional = false; - bool m_bDisableHexPreview = false; - bool m_bUseLowerCase = false; + bool m_bAutoCopy = false; + bool m_bRenderInactive = false; + bool m_bNoZoom = false; + bool m_bNoFractional = false; + bool m_bDisablePreview = false; bool m_bRunning = true; diff --git a/src/main.cpp b/src/main.cpp index 0692feb..8e6a2da 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,7 +7,7 @@ static void help() { std::cout << "Hyprpicker usage: hyprpicker [arg [...]].\n\nArguments:\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, lhex, hex, rgb, hsl, hsv)\n" << " -n | --no-fancy | Disables the \"fancy\" (aka. colored) outputting\n" << " -h | --help | Show this help message\n" << " -r | --render-inactive | Render (freeze) inactive displays\n" @@ -15,8 +15,7 @@ static void help() { << " -q | --quiet | Disable most logs (leaves errors)\n" << " -v | --verbose | Enable more logs\n" << " -t | --no-fractional | Disable fractional scaling support\n" - << " -d | --disable-hex-preview | Disable live preview of Hex code\n" - << " -l | --lowercase-hex | Outputs the hexcode in lowercase\n" + << " -d | --disable-preview | Disable live preview of color\n" << " -V | --version | Print version info\n"; } @@ -25,21 +24,20 @@ int main(int argc, char** argv, char** envp) { while (true) { int option_index = 0; - static struct option long_options[] = {{"autocopy", no_argument, nullptr, 'a'}, - {"format", required_argument, nullptr, 'f'}, - {"help", no_argument, nullptr, 'h'}, - {"no-fancy", no_argument, nullptr, 'n'}, - {"render-inactive", no_argument, nullptr, 'r'}, - {"no-zoom", no_argument, nullptr, 'z'}, - {"no-fractional", no_argument, nullptr, 't'}, - {"quiet", no_argument, nullptr, 'q'}, - {"verbose", no_argument, nullptr, 'v'}, - {"disable-hex-preview", no_argument, nullptr, 'd'}, - {"lowercase-hex", no_argument, nullptr, 'l'}, - {"version", no_argument, nullptr, 'V'}, - {nullptr, 0, nullptr, 0}}; + static struct option long_options[] = {{"autocopy", no_argument, NULL, 'a'}, + {"format", required_argument, NULL, 'f'}, + {"help", no_argument, NULL, 'h'}, + {"no-fancy", no_argument, NULL, 'n'}, + {"render-inactive", no_argument, NULL, 'r'}, + {"no-zoom", no_argument, NULL, 'z'}, + {"no-fractional", no_argument, NULL, 't'}, + {"quiet", no_argument, NULL, 'q'}, + {"verbose", no_argument, NULL, 'v'}, + {"disable-preview", no_argument, NULL, 'd'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0}}; - int c = getopt_long(argc, argv, ":f:hnarzqvtdlV", long_options, &option_index); + int c = getopt_long(argc, argv, ":f:hnarzqvtdV", long_options, &option_index); if (c == -1) break; @@ -49,6 +47,8 @@ int main(int argc, char** argv, char** envp) { g_pHyprpicker->m_bSelectedOutputMode = OUTPUT_CMYK; else if (strcasecmp(optarg, "hex") == 0) g_pHyprpicker->m_bSelectedOutputMode = OUTPUT_HEX; + else if (strcasecmp(optarg, "lhex") == 0) + g_pHyprpicker->m_bSelectedOutputMode = OUTPUT_LHEX; else if (strcasecmp(optarg, "rgb") == 0) g_pHyprpicker->m_bSelectedOutputMode = OUTPUT_RGB; else if (strcasecmp(optarg, "hsl") == 0) @@ -68,8 +68,7 @@ int main(int argc, char** argv, char** envp) { case 't': g_pHyprpicker->m_bNoFractional = true; break; case 'q': Debug::quiet = 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 'd': g_pHyprpicker->m_bDisablePreview = true; break; case 'V': { std::cout << "hyprpicker v" << HYPRPICKER_VERSION << "\n"; exit(0);