mirror of
https://github.com/hyprwm/hyprpicker.git
synced 2025-12-20 08:20:02 +01:00
Compare commits
36 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ab0e1aaa4 | ||
|
|
b645b892b1 | ||
|
|
cd7ba93fad | ||
|
|
b3f3f230c9 | ||
|
|
6f32582d22 | ||
| d6a1363a86 | |||
|
|
166dce0fae | ||
|
|
68bc7875fd | ||
|
|
b01491ac4e | ||
|
|
500c46185d | ||
|
|
8f886f9e9b | ||
|
|
1efe0faea0 | ||
| b0c0db66fa | |||
| 980ebd486b | |||
|
|
5dcb341c13 | ||
|
|
5bbeaeebd3 | ||
| fd77aea026 | |||
|
|
23664963a1 | ||
| 6692091d56 | |||
| 09101f77a4 | |||
|
|
e990630562 | ||
|
|
b85b06577d | ||
|
|
36a24e61be | ||
| c3777320b3 | |||
| 0b044884d9 | |||
|
|
18af93f0ff | ||
| 444c40e5e3 | |||
|
|
46d2f5a817 | ||
|
|
d26cb2f439 | ||
|
|
4e8837ddab | ||
| 89b9352d26 | |||
| 17e1ebe9dc | |||
|
|
185be7cd73 | ||
| 4411a6dc0d | |||
| 116cec14a5 | |||
| 3b42f6bee2 |
26 changed files with 709 additions and 288 deletions
101
.clang-tidy
Normal file
101
.clang-tidy
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
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
|
||||
40
.github/workflows/nix-build.yaml
vendored
40
.github/workflows/nix-build.yaml
vendored
|
|
@ -10,17 +10,41 @@ jobs:
|
|||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install nix
|
||||
uses: cachix/install-nix-action@v20
|
||||
|
||||
- name: Install Nix
|
||||
uses: nixbuild/nix-quick-install-action@v31
|
||||
with:
|
||||
install_url: https://nixos.org/nix/install
|
||||
extra_nix_config: |
|
||||
auto-optimise-store = true
|
||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||
experimental-features = nix-command flakes
|
||||
- uses: cachix/cachix-action@v12
|
||||
nix_conf: |
|
||||
keep-env-derivations = true
|
||||
keep-outputs = true
|
||||
|
||||
- name: Restore and save Nix store
|
||||
uses: nix-community/cache-nix-action@v6
|
||||
with:
|
||||
# restore and save a cache using this key
|
||||
primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/flake.lock') }}
|
||||
# if there's no cache hit, restore a cache by this prefix
|
||||
restore-prefixes-first-match: nix-${{ runner.os }}-
|
||||
# collect garbage until the Nix store size (in bytes) is at most this number
|
||||
# before trying to save a new cache
|
||||
# 1G = 1073741824
|
||||
gc-max-store-size-linux: 1G
|
||||
# do purge caches
|
||||
purge: true
|
||||
# purge all versions of the cache
|
||||
purge-prefixes: nix-${{ runner.os }}-
|
||||
# created more than this number of seconds ago
|
||||
purge-created: 0
|
||||
# or, last accessed more than this number of seconds ago
|
||||
# relative to the start of the `Post Restore and save Nix store` phase
|
||||
purge-last-accessed: 0
|
||||
# except any version with the key that is the same as the `primary-key`
|
||||
purge-primary-key: never
|
||||
|
||||
- uses: cachix/cachix-action@v16
|
||||
with:
|
||||
name: hyprland
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
|
||||
- name: Build Hyprpicker with default settings
|
||||
run: nix build --print-build-logs --accept-flake-config
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ string(STRIP ${VER_RAW} VERSION)
|
|||
|
||||
project(
|
||||
hyprpicker
|
||||
DESCRIPTION "A blazing fast wayland wallpaper utility"
|
||||
DESCRIPTION "A wlroots-compatible Wayland color picker that does not suck"
|
||||
VERSION ${VERSION})
|
||||
|
||||
set(CMAKE_MESSAGE_LOG_LEVEL "STATUS")
|
||||
|
|
@ -42,15 +42,15 @@ execute_process(
|
|||
|
||||
include_directories(.)
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
|
||||
add_compile_options(-DWLR_USE_UNSTABLE)
|
||||
add_compile_options(
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wuseless-cast
|
||||
-Wno-unused-parameter
|
||||
-Wno-unused-value
|
||||
-Wno-missing-field-initializers
|
||||
-Wno-narrowing
|
||||
-Wno-pointer-arith)
|
||||
-Wno-missing-field-initializers)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
|
|
|||
19
README.md
19
README.md
|
|
@ -10,25 +10,15 @@ Launch it. Click. That's it.
|
|||
|
||||
## Options
|
||||
|
||||
`-f | --format=[fmt]` specifies the output format (`cmyk`, `hex`, `rgb`, `hsl`, `hsv`)
|
||||
See `hyprpicker --help`.
|
||||
|
||||
`-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
|
||||
# Installation
|
||||
|
||||
## Arch
|
||||
|
||||
`yay -S hyprpicker-git`
|
||||
`sudo pacman -S hyprpicker`
|
||||
|
||||
## Manual
|
||||
## Manual (Building)
|
||||
|
||||
Install dependencies:
|
||||
- cmake
|
||||
|
|
@ -56,3 +46,4 @@ cmake --install ./build
|
|||
# Caveats
|
||||
|
||||
"Freezes" your displays when picking the color.
|
||||
|
||||
|
|
|
|||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
0.4.0
|
||||
0.4.5
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ Get a pixels color in HSL, wrapped in a CSS
|
|||
.Fn hsl
|
||||
function:
|
||||
.Pp
|
||||
.Dl $ hyprpicker -f hsl | sed 's/^/rgb(/; s/$/)/; y/ /,/'
|
||||
.Dl $ hyprpicker -f hsl | sed 's/^/hsl(/; s/$/)/; y/ /,/'
|
||||
.Sh SEE ALSO
|
||||
.Xr hyprctl 1 ,
|
||||
.Xr hyprland 1 ,
|
||||
|
|
|
|||
18
flake.lock
generated
18
flake.lock
generated
|
|
@ -10,11 +10,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1727300645,
|
||||
"narHash": "sha256-OvAtVLaSRPnbXzOwlR1fVqCXR7i+ICRX3aPMCdIiv+c=",
|
||||
"lastModified": 1749135356,
|
||||
"narHash": "sha256-Q8mAKMDsFbCEuq7zoSlcTuxgbIBVhfIYpX0RjE32PS0=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprutils",
|
||||
"rev": "3f5293432b6dc6a99f26aca2eba3876d2660665c",
|
||||
"rev": "e36db00dfb3a3d3fdcc4069cb292ff60d2699ccb",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -33,11 +33,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1726874836,
|
||||
"narHash": "sha256-VKR0sf0PSNCB0wPHVKSAn41mCNVCnegWmgkrneKDhHM=",
|
||||
"lastModified": 1749145760,
|
||||
"narHash": "sha256-IHaGWpGrv7seFWdw/1A+wHtTsPlOGIKMrk1TUIYJEFI=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprwayland-scanner",
|
||||
"rev": "500c81a9e1a76760371049a8d99e008ea77aa59e",
|
||||
"rev": "817918315ea016cc2d94004bfb3223b5fd9dfcc6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -48,11 +48,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1727122398,
|
||||
"narHash": "sha256-o8VBeCWHBxGd4kVMceIayf5GApqTavJbTa44Xcg5Rrk=",
|
||||
"lastModified": 1748929857,
|
||||
"narHash": "sha256-lcZQ8RhsmhsK8u7LIFsJhsLh/pzR9yZ8yqpTzyGdj+Q=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "30439d93eb8b19861ccbe3e581abf97bdc91b093",
|
||||
"rev": "c2a03962b8e24e669fb37b7df10e7c79531ff1a4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@
|
|||
inputs.hyprwayland-scanner.overlays.default
|
||||
(final: prev: {
|
||||
hyprpicker = prev.callPackage ./nix/default.nix {
|
||||
stdenv = prev.gcc13Stdenv;
|
||||
stdenv = prev.gcc15Stdenv;
|
||||
version = version + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty");
|
||||
};
|
||||
hyprpicker-debug = final.hyprpicker.override {debug = true;};
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
pango,
|
||||
pcre,
|
||||
pcre2,
|
||||
utillinux,
|
||||
util-linux,
|
||||
wayland,
|
||||
wayland-protocols,
|
||||
wayland-scanner,
|
||||
|
|
@ -56,7 +56,7 @@ stdenv.mkDerivation {
|
|||
pango
|
||||
pcre
|
||||
pcre2
|
||||
utillinux
|
||||
util-linux
|
||||
wayland
|
||||
wayland-protocols
|
||||
wayland-scanner
|
||||
|
|
|
|||
|
|
@ -1,20 +1,12 @@
|
|||
#include "Clipboard.hpp"
|
||||
|
||||
#include "../includes.hpp"
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
void Clipboard::copy(const char* fmt, ...) {
|
||||
char buf[CLIPBOARDMESSAGESIZE] = "";
|
||||
char* outputStr;
|
||||
void NClipboard::copy(std::string data) {
|
||||
Hyprutils::OS::CProcess copy("wl-copy", {data});
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vsnprintf(buf, sizeof buf, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
outputStr = strdup(buf);
|
||||
|
||||
if (fork() == 0)
|
||||
execlp("wl-copy", "wl-copy", outputStr, NULL);
|
||||
|
||||
free(outputStr);
|
||||
}
|
||||
copy.runAsync();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#define CLIPBOARDMESSAGESIZE 24
|
||||
#include <string>
|
||||
|
||||
namespace Clipboard {
|
||||
void copy(const char* fmt, ...);
|
||||
};
|
||||
namespace NClipboard {
|
||||
void copy(std::string data);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <print>
|
||||
|
||||
#include "../includes.hpp"
|
||||
|
||||
|
|
@ -38,7 +39,7 @@ void Debug::log(LogLevel level, const char* fmt, ...) {
|
|||
outputStr = (char*)malloc(logLen + 1);
|
||||
|
||||
if (!outputStr) {
|
||||
printf("CRITICAL: Cannot alloc size %d for log! (Out of memory?)", logLen + 1);
|
||||
std::print("CRITICAL: Cannot alloc size {} for log! (Out of memory?)", logLen + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include "helpers/Monitor.hpp"
|
||||
#include "helpers/Color.hpp"
|
||||
#include "clipboard/Clipboard.hpp"
|
||||
#include "notify/Notify.hpp"
|
||||
|
||||
// git stuff
|
||||
#ifndef GIT_COMMIT_HASH
|
||||
|
|
|
|||
80
src/helpers/Color.cpp
Normal file
80
src/helpers/Color.cpp
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#include "Color.hpp"
|
||||
#include <algorithm>
|
||||
#include "../hyprpicker.hpp"
|
||||
|
||||
static float fmax3(float a, float b, float c) {
|
||||
return (a > b && a > c) ? a : (b > c) ? b : c;
|
||||
}
|
||||
|
||||
static float fmin3(float a, float b, float c) {
|
||||
return (a < b && a < c) ? a : (b < c) ? b : c;
|
||||
}
|
||||
|
||||
static bool floatEq(float a, float b) {
|
||||
return std::nextafter(a, std::numeric_limits<double>::lowest()) <= b && std::nextafter(a, std::numeric_limits<double>::max()) >= b;
|
||||
}
|
||||
|
||||
void CColor::getCMYK(float& c, float& m, float& y, float& k) const {
|
||||
// http://www.codeproject.com/KB/applications/xcmyk.aspx
|
||||
|
||||
float rf = 1 - (r / 255.0f), gf = 1 - (g / 255.0f), bf = 1 - (b / 255.0f);
|
||||
k = fmin3(rf, gf, bf);
|
||||
float K = (k == 1) ? 1 : 1 - k;
|
||||
c = (rf - k) / K;
|
||||
m = (gf - k) / K;
|
||||
y = (bf - k) / K;
|
||||
|
||||
c = std::round(c * 100);
|
||||
m = std::round(m * 100);
|
||||
y = std::round(y * 100);
|
||||
k = std::round(k * 100);
|
||||
}
|
||||
void CColor::getHSV(float& h, float& s, float& v) const {
|
||||
// https://en.wikipedia.org/wiki/HSL_and_HSV#From_RGB
|
||||
|
||||
float rf = r / 255.0f, gf = g / 255.0f, bf = b / 255.0f;
|
||||
float max = fmax3(rf, gf, bf), min = fmin3(rf, gf, bf);
|
||||
float c = max - min;
|
||||
|
||||
v = max;
|
||||
if (c == 0)
|
||||
h = 0;
|
||||
else if (v == rf)
|
||||
h = 60 * (0 + (gf - bf) / c);
|
||||
else if (v == gf)
|
||||
h = 60 * (2 + (bf - rf) / c);
|
||||
else /* v == bf */
|
||||
h = 60 * (4 + (rf - gf) / c);
|
||||
|
||||
v = max;
|
||||
s = floatEq(v, 0.0f) ? 0 : c / v;
|
||||
|
||||
h = std::round(h < 0 ? h + 360 : h);
|
||||
v = std::round(v * 100);
|
||||
s = std::round(s * 100);
|
||||
}
|
||||
void CColor::getHSL(float& h, float& s, float& l) const {
|
||||
// https://en.wikipedia.org/wiki/HSL_and_HSV#From_RGB
|
||||
|
||||
float rf = r / 255.0f, gf = g / 255.0f, bf = b / 255.0f, v;
|
||||
float max = fmax3(rf, gf, bf), min = fmin3(rf, gf, bf);
|
||||
float c = max - min;
|
||||
|
||||
v = max;
|
||||
if (c == 0)
|
||||
h = 0;
|
||||
else if (v == rf)
|
||||
h = 60 * (0 + (gf - bf) / c);
|
||||
else if (v == gf)
|
||||
h = 60 * (2 + (bf - rf) / c);
|
||||
else /* v == bf */
|
||||
h = 60 * (4 + (rf - gf) / c);
|
||||
|
||||
v = max;
|
||||
s = floatEq(v, 0.0f) ? 0 : c / v;
|
||||
l = (max + min) / 2;
|
||||
s = (floatEq(l, 0.0f) || floatEq(l, 1.0f)) ? 0 : (v - l) / std::min(l, 1 - l);
|
||||
h = std::round(h < 0 ? h + 360 : h);
|
||||
s = std::round(s * 100);
|
||||
l = std::round(l * 100);
|
||||
}
|
||||
|
|
@ -5,4 +5,7 @@
|
|||
class CColor {
|
||||
public:
|
||||
uint8_t r = 0, g = 0, b = 0, a = 0;
|
||||
};
|
||||
void getCMYK(float& c, float& m, float& y, float& k) const;
|
||||
void getHSV(float& h, float& s, float& v) const;
|
||||
void getHSL(float& h, float& s, float& l) const;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,9 +2,7 @@
|
|||
|
||||
#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());
|
||||
|
||||
if (!pSurface) {
|
||||
|
|
@ -21,6 +19,9 @@ CLayerSurface::CLayerSurface(SMonitor* pMonitor) {
|
|||
pFractionalScale = makeShared<CCWpFractionalScaleV1>(g_pHyprpicker->m_pFractionalMgr->sendGetFractionalScale(pSurface->resource()));
|
||||
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);
|
||||
fractionalScale = scale120 / 120.F;
|
||||
wantsReload = true;
|
||||
g_pHyprpicker->recheckACK();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -65,15 +66,17 @@ CLayerSurface::~CLayerSurface() {
|
|||
static void onCallbackDone(CLayerSurface* surf, uint32_t when) {
|
||||
surf->frameCallback.reset();
|
||||
|
||||
if (surf->dirty || !surf->rendered)
|
||||
g_pHyprpicker->renderSurface(g_pHyprpicker->m_pLastSurface);
|
||||
g_pHyprpicker->renderSurface(surf);
|
||||
}
|
||||
|
||||
void CLayerSurface::sendFrame() {
|
||||
lastBuffer = lastBuffer == 0 ? 1 : 0;
|
||||
const auto& PBUFFER = buffers[lastBuffer];
|
||||
|
||||
frameCallback = makeShared<CCWlCallback>(pSurface->sendFrame());
|
||||
frameCallback->setDone([this](CCWlCallback* r, uint32_t when) { onCallbackDone(this, when); });
|
||||
|
||||
const auto& PBUFFER = lastBuffer == 0 ? buffers[0] : buffers[1];
|
||||
pSurface->sendDamageBuffer(0, 0, 0xFFFF, 0xFFFF);
|
||||
|
||||
pSurface->sendAttach(PBUFFER->buffer.get(), 0, 0);
|
||||
if (!g_pHyprpicker->m_bNoFractional) {
|
||||
|
|
@ -82,21 +85,12 @@ void CLayerSurface::sendFrame() {
|
|||
} else
|
||||
pSurface->sendSetBufferScale(m_pMonitor->scale);
|
||||
|
||||
pSurface->sendDamageBuffer(0, 0, 0xFFFF, 0xFFFF);
|
||||
pSurface->sendCommit();
|
||||
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
void CLayerSurface::markDirty() {
|
||||
frameCallback = makeShared<CCWlCallback>(pSurface->sendFrame());
|
||||
frameCallback->setDone([this](CCWlCallback* r, uint32_t when) {
|
||||
frameCallback.reset();
|
||||
|
||||
if (dirty || !rendered)
|
||||
g_pHyprpicker->renderSurface(g_pHyprpicker->m_pLastSurface);
|
||||
});
|
||||
pSurface->sendCommit();
|
||||
frameCallback->setDone([this](CCWlCallback* r, uint32_t when) { onCallbackDone(this, when); });
|
||||
|
||||
dirty = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,9 +20,11 @@ class CLayerSurface {
|
|||
SP<CCWpViewport> pViewport = nullptr;
|
||||
SP<CCWpFractionalScaleV1> pFractionalScale = nullptr;
|
||||
|
||||
bool wantsACK = false;
|
||||
uint32_t ACKSerial = 0;
|
||||
bool working = false;
|
||||
float fractionalScale = 1.F;
|
||||
bool wantsACK = false;
|
||||
bool wantsReload = false;
|
||||
uint32_t ACKSerial = 0;
|
||||
bool working = false;
|
||||
|
||||
int lastBuffer = 0;
|
||||
SP<SPoolBuffer> buffers[2];
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ void SMonitor::initSCFrame() {
|
|||
if (pLS->m_pMonitor->transform % 2 == 1)
|
||||
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);
|
||||
|
||||
int bytesPerPixel = pLS->screenBuffer->stride / (int)pLS->screenBuffer->pixelSize.x;
|
||||
|
|
@ -112,8 +114,8 @@ void SMonitor::initSCFrame() {
|
|||
|
||||
pSCFrame.reset();
|
||||
});
|
||||
pSCFrame->setFailed([this](CCZwlrScreencopyFrameV1* r) {
|
||||
pSCFrame->setFailed([](CCZwlrScreencopyFrameV1* r) {
|
||||
Debug::log(CRIT, "Failed to get a Screencopy!");
|
||||
g_pHyprpicker->finish(1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,11 +14,11 @@ struct SMonitor {
|
|||
SP<CCWlOutput> output = nullptr;
|
||||
uint32_t wayland_name = 0;
|
||||
Vector2D size;
|
||||
int scale;
|
||||
int32_t scale;
|
||||
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
|
||||
|
||||
bool ready = false;
|
||||
|
||||
CLayerSurface* pLS = nullptr;
|
||||
SP<CCZwlrScreencopyFrameV1> pSCFrame = nullptr;
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ SPoolBuffer::SPoolBuffer(const Vector2D& pixelSize_, uint32_t format_, uint32_t
|
|||
g_pHyprpicker->finish(1);
|
||||
}
|
||||
|
||||
const auto DATA = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, FD, 0);
|
||||
const auto DATA = mmap(nullptr, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, FD, 0);
|
||||
|
||||
size = SIZE;
|
||||
data = DATA;
|
||||
|
|
@ -39,4 +39,4 @@ SPoolBuffer::~SPoolBuffer() {
|
|||
|
||||
if (paddedData)
|
||||
free(paddedData);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,16 @@
|
|||
#include "hyprpicker.hpp"
|
||||
#include <signal.h>
|
||||
#include "src/debug/Log.hpp"
|
||||
#include "src/notify/Notify.hpp"
|
||||
#include <csignal>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <format>
|
||||
#include <hyprutils/math/Vector2D.hpp>
|
||||
#include <wayland-client-protocol.h>
|
||||
#include <xkbcommon/xkbcommon-keysyms.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
|
||||
void sigHandler(int sig) {
|
||||
static void sigHandler(int sig) {
|
||||
g_pHyprpicker->m_vLayerSurfaces.clear();
|
||||
exit(0);
|
||||
}
|
||||
|
|
@ -9,7 +18,7 @@ void sigHandler(int sig) {
|
|||
void CHyprpicker::init() {
|
||||
m_pXKBContext = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||
if (!m_pXKBContext)
|
||||
Debug::log(ERR, "Failed to create xkb context");
|
||||
Debug::log(ERR, "Failed to create xkb context, keyboard movement not supported");
|
||||
|
||||
m_pWLDisplay = wl_display_connect(nullptr);
|
||||
|
||||
|
|
@ -145,15 +154,129 @@ void CHyprpicker::finish(int code) {
|
|||
exit(code);
|
||||
}
|
||||
|
||||
void CHyprpicker::outputColor() {
|
||||
|
||||
// relative brightness of a color
|
||||
// https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
|
||||
const auto FLUMI = [](const float& c) -> float { return c <= 0.03928 ? c / 12.92 : powf((c + 0.055) / 1.055, 2.4); };
|
||||
|
||||
// get the px and print it
|
||||
const auto MOUSECOORDSABS = m_vLastCoords.floor() / m_pLastSurface->m_pMonitor->size;
|
||||
const auto CLICKPOS = MOUSECOORDSABS * m_pLastSurface->screenBuffer->pixelSize;
|
||||
|
||||
const auto COL = getColorFromPixel(m_pLastSurface, CLICKPOS);
|
||||
|
||||
// threshold: (lumi_white + 0.05) / (x + 0.05) == (x + 0.05) / (lumi_black + 0.05)
|
||||
// https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef
|
||||
const uint8_t FG = 0.2126 * FLUMI(COL.r / 255.0f) + 0.7152 * FLUMI(COL.g / 255.0f) + 0.0722 * FLUMI(COL.b / 255.0f) > 0.17913 ? 0 : 255;
|
||||
|
||||
std::string hexColor = std::format("#{0:02x}{1:02x}{2:02x}", COL.r, COL.g, COL.b);
|
||||
|
||||
switch (m_bSelectedOutputMode) {
|
||||
case OUTPUT_CMYK: {
|
||||
float c, m, y, k;
|
||||
COL.getCMYK(c, m, y, k);
|
||||
|
||||
std::string formattedColor = std::vformat(m_sOutputFormat, std::make_format_args(c, m, y, k));
|
||||
|
||||
if (m_bFancyOutput)
|
||||
Debug::log(NONE, "\033[38;2;%i;%i;%i;48;2;%i;%i;%im%s\033[0m", FG, FG, FG, COL.r, COL.g, COL.b, formattedColor.c_str());
|
||||
else
|
||||
Debug::log(NONE, formattedColor.c_str());
|
||||
|
||||
if (m_bAutoCopy)
|
||||
NClipboard::copy(formattedColor);
|
||||
|
||||
if (m_bNotify)
|
||||
NNotify::send(hexColor, formattedColor);
|
||||
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
case OUTPUT_HEX: {
|
||||
std::string rHex, gHex, bHex;
|
||||
if (m_bUseLowerCase) {
|
||||
rHex = std::format("{:02x}", COL.r);
|
||||
gHex = std::format("{:02x}", COL.g);
|
||||
bHex = std::format("{:02x}", COL.b);
|
||||
} else {
|
||||
rHex = std::format("{:02X}", COL.r);
|
||||
gHex = std::format("{:02X}", COL.g);
|
||||
bHex = std::format("{:02X}", COL.b);
|
||||
}
|
||||
std::string formattedColor = std::vformat(m_sOutputFormat, std::make_format_args(rHex, gHex, bHex));
|
||||
if (m_bFancyOutput)
|
||||
Debug::log(NONE, "\033[38;2;%i;%i;%i;48;2;%i;%i;%im%s\033[0m", FG, FG, FG, COL.r, COL.g, COL.b, formattedColor.c_str());
|
||||
else
|
||||
Debug::log(NONE, formattedColor.c_str());
|
||||
|
||||
if (m_bAutoCopy)
|
||||
NClipboard::copy(hexColor);
|
||||
|
||||
if (m_bNotify)
|
||||
NNotify::send(hexColor, hexColor);
|
||||
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
case OUTPUT_RGB: {
|
||||
std::string formattedColor = std::vformat(m_sOutputFormat, std::make_format_args(COL.r, COL.g, COL.b));
|
||||
|
||||
if (m_bFancyOutput)
|
||||
Debug::log(NONE, "\033[38;2;%i;%i;%i;48;2;%i;%i;%im%s\033[0m", FG, FG, FG, COL.r, COL.g, COL.b, formattedColor.c_str());
|
||||
else
|
||||
Debug::log(NONE, formattedColor.c_str());
|
||||
|
||||
if (m_bAutoCopy)
|
||||
NClipboard::copy(formattedColor);
|
||||
|
||||
if (m_bNotify)
|
||||
NNotify::send(hexColor, formattedColor);
|
||||
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
case OUTPUT_HSL:
|
||||
case OUTPUT_HSV: {
|
||||
float h, s, l_or_v;
|
||||
if (m_bSelectedOutputMode == OUTPUT_HSV)
|
||||
COL.getHSV(h, s, l_or_v);
|
||||
else
|
||||
COL.getHSL(h, s, l_or_v);
|
||||
|
||||
std::string formattedColor = std::vformat(m_sOutputFormat, std::make_format_args(h, s, l_or_v));
|
||||
if (m_bFancyOutput)
|
||||
Debug::log(NONE, "\033[38;2;%i;%i;%i;48;2;%i;%i;%im%s\033[0m", FG, FG, FG, COL.r, COL.g, COL.b, formattedColor.c_str());
|
||||
else
|
||||
Debug::log(NONE, formattedColor.c_str());
|
||||
|
||||
if (m_bAutoCopy)
|
||||
NClipboard::copy(formattedColor);
|
||||
|
||||
if (m_bNotify)
|
||||
NNotify::send(hexColor, formattedColor);
|
||||
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
void CHyprpicker::recheckACK() {
|
||||
for (auto& ls : m_vLayerSurfaces) {
|
||||
if (ls->wantsACK) {
|
||||
ls->wantsACK = false;
|
||||
ls->pLayerSurface->sendAckConfigure(ls->ACKSerial);
|
||||
if ((ls->wantsACK || ls->wantsReload) && ls->screenBuffer) {
|
||||
if (ls->wantsACK)
|
||||
ls->pLayerSurface->sendAckConfigure(ls->ACKSerial);
|
||||
ls->wantsACK = false;
|
||||
ls->wantsReload = false;
|
||||
|
||||
const auto MONITORSIZE = ls->screenBuffer && !g_pHyprpicker->m_bNoFractional ? ls->screenBuffer->pixelSize : ls->m_pMonitor->size * ls->m_pMonitor->scale;
|
||||
const auto MONITORSIZE =
|
||||
(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) {
|
||||
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[1] = makeShared<SPoolBuffer>(MONITORSIZE, WL_SHM_FORMAT_ARGB8888, MONITORSIZE.x * 4);
|
||||
}
|
||||
|
|
@ -238,13 +361,13 @@ void CHyprpicker::convertBuffer(SP<SPoolBuffer> pBuffer) {
|
|||
|
||||
for (int y = 0; y < pBuffer->pixelSize.y; ++y) {
|
||||
for (int x = 0; x < pBuffer->pixelSize.x; ++x) {
|
||||
struct pixel {
|
||||
struct SPixel {
|
||||
// little-endian ARGB
|
||||
unsigned char blue;
|
||||
unsigned char green;
|
||||
unsigned char red;
|
||||
unsigned char alpha;
|
||||
}* px = (struct pixel*)(data + y * (int)pBuffer->pixelSize.x * 4 + x * 4);
|
||||
}* px = (struct SPixel*)(data + (static_cast<ptrdiff_t>(y * (int)pBuffer->pixelSize.x * 4)) + (static_cast<ptrdiff_t>(x * 4)));
|
||||
|
||||
std::swap(px->red, px->blue);
|
||||
}
|
||||
|
|
@ -258,7 +381,7 @@ void CHyprpicker::convertBuffer(SP<SPoolBuffer> pBuffer) {
|
|||
|
||||
for (int y = 0; y < pBuffer->pixelSize.y; ++y) {
|
||||
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 + (static_cast<ptrdiff_t>(y * (int)pBuffer->pixelSize.x * 4)) + (static_cast<ptrdiff_t>(x * 4)));
|
||||
|
||||
// conv to 8 bit
|
||||
uint8_t R = (uint8_t)std::round((255.0 * (((*px) & 0b00000000000000000000001111111111) >> 0) / 1023.0));
|
||||
|
|
@ -288,40 +411,40 @@ void* CHyprpicker::convert24To32Buffer(SP<SPoolBuffer> pBuffer) {
|
|||
case WL_SHM_FORMAT_BGR888: {
|
||||
for (int y = 0; y < pBuffer->pixelSize.y; ++y) {
|
||||
for (int x = 0; x < pBuffer->pixelSize.x; ++x) {
|
||||
struct pixel3 {
|
||||
struct SPixel3 {
|
||||
// little-endian RGB
|
||||
unsigned char blue;
|
||||
unsigned char green;
|
||||
unsigned char red;
|
||||
}* srcPx = (struct pixel3*)(oldBuffer + y * pBuffer->stride + x * 3);
|
||||
struct pixel4 {
|
||||
}* srcPx = (struct SPixel3*)(oldBuffer + (static_cast<size_t>(y * pBuffer->stride)) + (static_cast<ptrdiff_t>(x * 3)));
|
||||
struct SPixel4 {
|
||||
// little-endian ARGB
|
||||
unsigned char blue;
|
||||
unsigned char green;
|
||||
unsigned char red;
|
||||
unsigned char alpha;
|
||||
}* dstPx = (struct pixel4*)(newBuffer + y * newBufferStride + x * 4);
|
||||
*dstPx = {srcPx->red, srcPx->green, srcPx->blue, 0xFF};
|
||||
}* dstPx = (struct SPixel4*)(newBuffer + (static_cast<ptrdiff_t>(y * newBufferStride)) + (static_cast<ptrdiff_t>(x * 4)));
|
||||
*dstPx = {.blue = srcPx->red, .green = srcPx->green, .red = srcPx->blue, .alpha = 0xFF};
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case WL_SHM_FORMAT_RGB888: {
|
||||
for (int y = 0; y < pBuffer->pixelSize.y; ++y) {
|
||||
for (int x = 0; x < pBuffer->pixelSize.x; ++x) {
|
||||
struct pixel3 {
|
||||
struct SPixel3 {
|
||||
// big-endian RGB
|
||||
unsigned char red;
|
||||
unsigned char green;
|
||||
unsigned char blue;
|
||||
}* srcPx = (struct pixel3*)(oldBuffer + y * pBuffer->stride + x * 3);
|
||||
struct pixel4 {
|
||||
}* srcPx = (struct SPixel3*)(oldBuffer + (y * pBuffer->stride) + (x * 3));
|
||||
struct SPixel4 {
|
||||
// big-endian ARGB
|
||||
unsigned char alpha;
|
||||
unsigned char red;
|
||||
unsigned char green;
|
||||
unsigned char blue;
|
||||
}* dstPx = (struct pixel4*)(newBuffer + y * newBufferStride + x * 4);
|
||||
*dstPx = {0xFF, srcPx->red, srcPx->green, srcPx->blue};
|
||||
}* dstPx = (struct SPixel4*)(newBuffer + (y * newBufferStride) + (x * 4));
|
||||
*dstPx = {.alpha = 0xFF, .red = srcPx->red, .green = srcPx->green, .blue = srcPx->blue};
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
|
@ -337,6 +460,7 @@ void CHyprpicker::renderSurface(CLayerSurface* pSurface, bool forceInactive) {
|
|||
const auto PBUFFER = getBufferForLS(pSurface);
|
||||
|
||||
if (!PBUFFER || !pSurface->screenBuffer) {
|
||||
// Spammy log, doesn't matter.
|
||||
// Debug::log(ERR, PBUFFER ? "renderSurface: pSurface->screenBuffer null" : "renderSurface: PBUFFER null");
|
||||
return;
|
||||
}
|
||||
|
|
@ -355,11 +479,13 @@ void CHyprpicker::renderSurface(CLayerSurface* pSurface, bool forceInactive) {
|
|||
cairo_rectangle(PCAIRO, 0, 0, PBUFFER->pixelSize.x, PBUFFER->pixelSize.y);
|
||||
cairo_fill(PCAIRO);
|
||||
|
||||
if (pSurface == m_pLastSurface && !forceInactive) {
|
||||
if (pSurface == m_pLastSurface && !forceInactive && m_bCoordsInitialized) {
|
||||
const auto SCALEBUFS = pSurface->screenBuffer->pixelSize / PBUFFER->pixelSize;
|
||||
const auto MOUSECOORDSABS = m_vLastCoords.floor() / pSurface->m_pMonitor->size;
|
||||
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);
|
||||
cairo_pattern_set_filter(PATTERNPRE, CAIRO_FILTER_BILINEAR);
|
||||
cairo_matrix_t matrixPre;
|
||||
|
|
@ -382,6 +508,7 @@ void CHyprpicker::renderSurface(CLayerSurface* pSurface, bool forceInactive) {
|
|||
// | |
|
||||
// | --------- |
|
||||
//
|
||||
// (hex code here)
|
||||
|
||||
cairo_restore(PCAIRO);
|
||||
if (!m_bNoZoom) {
|
||||
|
|
@ -394,7 +521,7 @@ void CHyprpicker::renderSurface(CLayerSurface* pSurface, bool forceInactive) {
|
|||
|
||||
cairo_scale(PCAIRO, 1, 1);
|
||||
|
||||
cairo_arc(PCAIRO, CLICKPOS.x, CLICKPOS.y, 105 / SCALEBUFS.x, 0, 2 * M_PI);
|
||||
cairo_arc(PCAIRO, CLICKPOS.x, CLICKPOS.y, m_iCircleRadius + 5 / SCALEBUFS.x, 0, 2 * M_PI);
|
||||
cairo_clip(PCAIRO);
|
||||
|
||||
cairo_fill(PCAIRO);
|
||||
|
|
@ -410,26 +537,111 @@ void CHyprpicker::renderSurface(CLayerSurface* pSurface, bool forceInactive) {
|
|||
cairo_matrix_t matrix;
|
||||
cairo_matrix_init_identity(&matrix);
|
||||
cairo_matrix_translate(&matrix, CLICKPOSBUF.x + 0.5f, CLICKPOSBUF.y + 0.5f);
|
||||
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_scale(&matrix, 1.0 / m_fZoomScale, 1.0 / m_fZoomScale);
|
||||
cairo_matrix_translate(&matrix, (-CLICKPOSBUF.x / SCALEBUFS.x) - 0.5f, (-CLICKPOSBUF.y / SCALEBUFS.y) - 0.5f);
|
||||
cairo_pattern_set_matrix(PATTERN, &matrix);
|
||||
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, m_iCircleRadius / SCALEBUFS.x, 0, 2 * M_PI);
|
||||
cairo_clip(PCAIRO);
|
||||
cairo_paint(PCAIRO);
|
||||
|
||||
cairo_surface_flush(PBUFFER->surface);
|
||||
if (!m_bDisablePreview) {
|
||||
const auto currentColor = getColorFromPixel(pSurface, CLICKPOS);
|
||||
std::string previewBuffer;
|
||||
switch (m_bSelectedOutputMode) {
|
||||
case OUTPUT_HEX: {
|
||||
std::string rHex, gHex, bHex;
|
||||
if (m_bUseLowerCase) {
|
||||
rHex = std::format("{:02x}", currentColor.r);
|
||||
gHex = std::format("{:02x}", currentColor.g);
|
||||
bHex = std::format("{:02x}", currentColor.b);
|
||||
} else {
|
||||
rHex = std::format("{:02X}", currentColor.r);
|
||||
gHex = std::format("{:02X}", currentColor.g);
|
||||
bHex = std::format("{:02X}", currentColor.b);
|
||||
}
|
||||
previewBuffer = std::vformat(m_sOutputFormat, std::make_format_args(rHex, gHex, bHex));
|
||||
break;
|
||||
};
|
||||
case OUTPUT_RGB: {
|
||||
previewBuffer = std::vformat(m_sOutputFormat, std::make_format_args(currentColor.r, currentColor.g, currentColor.b));
|
||||
break;
|
||||
};
|
||||
case OUTPUT_HSL: {
|
||||
float h, s, l;
|
||||
currentColor.getHSL(h, s, l);
|
||||
previewBuffer = std::vformat(m_sOutputFormat, std::make_format_args(h, s, l));
|
||||
break;
|
||||
};
|
||||
case OUTPUT_HSV: {
|
||||
float h, s, v;
|
||||
currentColor.getHSV(h, s, v);
|
||||
previewBuffer = std::vformat(m_sOutputFormat, std::make_format_args(h, s, v));
|
||||
break;
|
||||
};
|
||||
case OUTPUT_CMYK: {
|
||||
float c, m, y, k;
|
||||
currentColor.getCMYK(c, m, y, k);
|
||||
previewBuffer = std::vformat(m_sOutputFormat, std::make_format_args(c, m, y, k));
|
||||
break;
|
||||
};
|
||||
};
|
||||
cairo_set_source_rgba(PCAIRO, 0.0, 0.0, 0.0, 0.75);
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
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);
|
||||
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, previewBuffer.c_str());
|
||||
|
||||
cairo_surface_flush(PBUFFER->surface);
|
||||
}
|
||||
cairo_restore(PCAIRO);
|
||||
|
||||
cairo_pattern_destroy(PATTERN);
|
||||
}
|
||||
} else if (!m_bRenderInactive) {
|
||||
} else if (!m_bRenderInactive && m_bCoordsInitialized) {
|
||||
cairo_set_operator(PCAIRO, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_set_source_rgba(PCAIRO, 0, 0, 0, 0);
|
||||
cairo_rectangle(PCAIRO, 0, 0, PBUFFER->pixelSize.x, PBUFFER->pixelSize.y);
|
||||
cairo_fill(PCAIRO);
|
||||
} else {
|
||||
} else if (m_bCoordsInitialized) {
|
||||
const auto SCALEBUFS = pSurface->screenBuffer->pixelSize / PBUFFER->pixelSize;
|
||||
const auto PATTERNPRE = cairo_pattern_create_for_surface(pSurface->screenBuffer->surface);
|
||||
cairo_pattern_set_filter(PATTERNPRE, CAIRO_FILTER_BILINEAR);
|
||||
|
|
@ -441,7 +653,6 @@ void CHyprpicker::renderSurface(CLayerSurface* pSurface, bool forceInactive) {
|
|||
cairo_paint(PCAIRO);
|
||||
|
||||
cairo_surface_flush(PBUFFER->surface);
|
||||
|
||||
cairo_pattern_destroy(PATTERNPRE);
|
||||
}
|
||||
|
||||
|
|
@ -457,15 +668,21 @@ void CHyprpicker::renderSurface(CLayerSurface* pSurface, bool forceInactive) {
|
|||
}
|
||||
|
||||
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;
|
||||
struct pixel {
|
||||
|
||||
struct SPixel {
|
||||
unsigned char blue;
|
||||
unsigned char green;
|
||||
unsigned char red;
|
||||
unsigned char alpha;
|
||||
}* px = (struct pixel*)((char*)dataSrc + (int)pix.y * (int)pLS->screenBuffer->pixelSize.x * 4 + (int)pix.x * 4);
|
||||
}* px = (struct SPixel*)((char*)dataSrc + ((ptrdiff_t)pix.y * (int)pLS->screenBuffer->pixelSize.x * 4) + ((ptrdiff_t)pix.x * 4));
|
||||
|
||||
return CColor{(uint8_t)px->red, (uint8_t)px->green, (uint8_t)px->blue, (uint8_t)px->alpha};
|
||||
return CColor{.r = px->red, .g = px->green, .b = px->blue, .a = px->alpha};
|
||||
}
|
||||
|
||||
void CHyprpicker::initKeyboard() {
|
||||
|
|
@ -478,7 +695,7 @@ void CHyprpicker::initKeyboard() {
|
|||
return;
|
||||
}
|
||||
|
||||
const char* buf = (const char*)mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
const char* buf = (const char*)mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
if (buf == MAP_FAILED) {
|
||||
Debug::log(ERR, "Failed to mmap xkb keymap: %d", errno);
|
||||
return;
|
||||
|
|
@ -504,23 +721,32 @@ void CHyprpicker::initKeyboard() {
|
|||
m_pKeyboard->setKey([this](CCWlKeyboard* r, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
|
||||
if (state != WL_KEYBOARD_KEY_STATE_PRESSED)
|
||||
return;
|
||||
|
||||
if (m_pXKBState) {
|
||||
if (xkb_state_key_get_one_sym(m_pXKBState, key + 8) == XKB_KEY_Escape)
|
||||
finish();
|
||||
int32_t XKBKey = xkb_state_key_get_one_sym(m_pXKBState, key + 8);
|
||||
if (XKBKey == XKB_KEY_Right)
|
||||
m_vLastCoords.x += m_vLastCoords.x < m_pLastSurface->m_pMonitor->size.x;
|
||||
else if (XKBKey == XKB_KEY_Left)
|
||||
m_vLastCoords.x -= m_vLastCoords.x > 0;
|
||||
else if (XKBKey == XKB_KEY_Up)
|
||||
m_vLastCoords.y -= m_vLastCoords.y > 0;
|
||||
else if (XKBKey == XKB_KEY_Down)
|
||||
m_vLastCoords.y += m_vLastCoords.y < m_pLastSurface->m_pMonitor->size.y;
|
||||
else if (XKBKey == XKB_KEY_Return)
|
||||
outputColor();
|
||||
else if (XKBKey == XKB_KEY_Escape)
|
||||
finish(2);
|
||||
} else if (key == 1) // Assume keycode 1 is escape
|
||||
finish();
|
||||
finish(2);
|
||||
});
|
||||
}
|
||||
|
||||
void CHyprpicker::initMouse() {
|
||||
m_pPointer->setEnter([this](CCWlPointer* r, uint32_t serial, wl_resource* surface, wl_fixed_t surface_x, wl_fixed_t surface_y) {
|
||||
m_pPointer->setEnter([this](CCWlPointer* r, uint32_t serial, wl_proxy* surface, wl_fixed_t surface_x, wl_fixed_t surface_y) {
|
||||
auto x = wl_fixed_to_double(surface_x);
|
||||
auto y = wl_fixed_to_double(surface_y);
|
||||
|
||||
m_vLastCoords = {x, y};
|
||||
|
||||
markDirty();
|
||||
m_vLastCoords = {x, y};
|
||||
m_bCoordsInitialized = true;
|
||||
|
||||
for (auto& ls : m_vLayerSurfaces) {
|
||||
if (ls->pSurface->resource() == surface) {
|
||||
|
|
@ -529,14 +755,21 @@ void CHyprpicker::initMouse() {
|
|||
}
|
||||
}
|
||||
|
||||
m_pCursorShapeDevice->sendSetShape(serial, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CROSSHAIR);
|
||||
if (m_pCursorShapeDevice)
|
||||
m_pCursorShapeDevice->sendSetShape(serial, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CROSSHAIR);
|
||||
|
||||
markDirty();
|
||||
});
|
||||
m_pPointer->setLeave([this](CCWlPointer* r, uint32_t timeMs, wl_resource* surf) {
|
||||
m_pPointer->setLeave([this](CCWlPointer* r, uint32_t timeMs, wl_proxy* surface) {
|
||||
for (auto& ls : m_vLayerSurfaces) {
|
||||
if (ls->pSurface->resource() == surf) {
|
||||
renderSurface(ls.get(), true);
|
||||
if (ls->pSurface->resource() == surface) {
|
||||
if (m_pLastSurface == ls.get())
|
||||
m_pLastSurface = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
markDirty();
|
||||
});
|
||||
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);
|
||||
|
|
@ -546,130 +779,18 @@ void CHyprpicker::initMouse() {
|
|||
|
||||
markDirty();
|
||||
});
|
||||
m_pPointer->setButton([this](CCWlPointer* r, uint32_t serial, uint32_t time, uint32_t button, uint32_t button_state) {
|
||||
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; };
|
||||
m_pPointer->setButton([this](CCWlPointer* r, uint32_t serial, uint32_t time, uint32_t button, uint32_t button_state) { outputColor(); });
|
||||
m_pPointer->setAxis([this](CCWlPointer* r, uint32_t time, uint32_t axis, wl_fixed_t value) {
|
||||
if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL)
|
||||
return;
|
||||
|
||||
// relative brightness of a color
|
||||
// https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
|
||||
const auto FLUMI = [](const float& c) -> float { return c <= 0.03928 ? c / 12.92 : powf((c + 0.055) / 1.055, 2.4); };
|
||||
double delta = wl_fixed_to_double(value);
|
||||
|
||||
// get the px and print it
|
||||
const auto MOUSECOORDSABS = m_vLastCoords.floor() / m_pLastSurface->m_pMonitor->size;
|
||||
const auto CLICKPOS = MOUSECOORDSABS * m_pLastSurface->screenBuffer->pixelSize;
|
||||
if (delta < 0)
|
||||
m_fZoomScale = std::min(m_fZoomScale + 1.0, 100.0);
|
||||
else
|
||||
m_fZoomScale = std::max(m_fZoomScale - 1.0, 1.0);
|
||||
|
||||
const auto COL = getColorFromPixel(m_pLastSurface, CLICKPOS);
|
||||
|
||||
// threshold: (lumi_white + 0.05) / (x + 0.05) == (x + 0.05) / (lumi_black + 0.05)
|
||||
// https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef
|
||||
const uint8_t FG = 0.2126 * FLUMI(COL.r / 255.0f) + 0.7152 * FLUMI(COL.g / 255.0f) + 0.0722 * FLUMI(COL.b / 255.0f) > 0.17913 ? 0 : 255;
|
||||
|
||||
switch (m_bSelectedOutputMode) {
|
||||
case OUTPUT_CMYK: {
|
||||
// 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 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);
|
||||
|
||||
if (m_bFancyOutput)
|
||||
Debug::log(NONE, "\033[38;2;%i;%i;%i;48;2;%i;%i;%im%g%% %g%% %g%% %g%%\033[0m", FG, FG, FG, COL.r, COL.g, COL.b, c, m, y, k);
|
||||
else
|
||||
Debug::log(NONE, "%g%% %g%% %g%% %g%%", c, m, y, k);
|
||||
|
||||
if (m_bAutoCopy)
|
||||
Clipboard::copy("%g%% %g%% %g%% %g%%", c, m, y, k);
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
case OUTPUT_HEX: {
|
||||
auto toHex = [](int i) -> std::string {
|
||||
const char* DS = "0123456789ABCDEF";
|
||||
|
||||
std::string result = "";
|
||||
|
||||
result += DS[i / 16];
|
||||
result += DS[i % 16];
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
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(),
|
||||
toHex(COL.b).c_str());
|
||||
else
|
||||
Debug::log(NONE, "#%s%s%s", toHex(COL.r).c_str(), toHex(COL.g).c_str(), toHex(COL.b).c_str());
|
||||
|
||||
if (m_bAutoCopy)
|
||||
Clipboard::copy("#%s%s%s", toHex(COL.r).c_str(), toHex(COL.g).c_str(), toHex(COL.b).c_str());
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
case OUTPUT_RGB: {
|
||||
if (m_bFancyOutput)
|
||||
Debug::log(NONE, "\033[38;2;%i;%i;%i;48;2;%i;%i;%im%i %i %i\033[0m", FG, FG, FG, COL.r, COL.g, COL.b, COL.r, COL.g, COL.b);
|
||||
else
|
||||
Debug::log(NONE, "%i %i %i", COL.r, COL.g, COL.b);
|
||||
|
||||
if (m_bAutoCopy)
|
||||
Clipboard::copy("%i %i %i", COL.r, COL.g, COL.b);
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
case OUTPUT_HSL:
|
||||
case OUTPUT_HSV: {
|
||||
// https://en.wikipedia.org/wiki/HSL_and_HSV#From_RGB
|
||||
|
||||
auto floatEq = [](float a, float b) -> bool {
|
||||
return std::nextafter(a, std::numeric_limits<double>::lowest()) <= b && std::nextafter(a, std::numeric_limits<double>::max()) >= b;
|
||||
};
|
||||
|
||||
float h, s, l, v;
|
||||
float r = COL.r / 255.0f, g = COL.g / 255.0f, b = COL.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);
|
||||
s = std::round(s * 100);
|
||||
|
||||
if (m_bFancyOutput)
|
||||
Debug::log(NONE, "\033[38;2;%i;%i;%i;48;2;%i;%i;%im%g %g%% %g%%\033[0m", FG, FG, FG, COL.r, COL.g, COL.b, h, s, l_or_v);
|
||||
else
|
||||
Debug::log(NONE, "%g %g%% %g%%", h, s, l_or_v);
|
||||
|
||||
if (m_bAutoCopy)
|
||||
Clipboard::copy("%g %g%% %g%%", h, s, l_or_v);
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
finish();
|
||||
markDirty();
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,14 +4,17 @@
|
|||
#include "helpers/LayerSurface.hpp"
|
||||
#include "helpers/PoolBuffer.hpp"
|
||||
|
||||
enum eOutputMode {
|
||||
// OUTPUT_COUNT is being used to count the number of output formats, it should always be last in the enum
|
||||
enum eOutputMode : uint8_t {
|
||||
OUTPUT_CMYK = 0,
|
||||
OUTPUT_HEX,
|
||||
OUTPUT_RGB,
|
||||
OUTPUT_HSL,
|
||||
OUTPUT_HSV
|
||||
OUTPUT_HSV,
|
||||
};
|
||||
|
||||
const std::array<uint8_t, 5> numOutputValues = {4, 3, 3, 3, 3};
|
||||
|
||||
class CHyprpicker {
|
||||
public:
|
||||
void init();
|
||||
|
|
@ -37,15 +40,21 @@ class CHyprpicker {
|
|||
xkb_state* m_pXKBState = nullptr;
|
||||
|
||||
eOutputMode m_bSelectedOutputMode = OUTPUT_HEX;
|
||||
std::string m_sOutputFormat = "";
|
||||
|
||||
bool m_bFancyOutput = true;
|
||||
|
||||
bool m_bAutoCopy = false;
|
||||
bool m_bNotify = false;
|
||||
bool m_bRenderInactive = false;
|
||||
bool m_bNoZoom = false;
|
||||
bool m_bNoFractional = false;
|
||||
bool m_bDisablePreview = false;
|
||||
bool m_bUseLowerCase = false;
|
||||
|
||||
bool m_bRunning = true;
|
||||
bool m_bRunning = true;
|
||||
float m_fZoomScale = 10.0;
|
||||
int m_iCircleRadius = 100;
|
||||
|
||||
std::vector<std::unique_ptr<SMonitor>> m_vMonitors;
|
||||
std::vector<std::unique_ptr<CLayerSurface>> m_vLayerSurfaces;
|
||||
|
|
@ -53,6 +62,7 @@ class CHyprpicker {
|
|||
CLayerSurface* m_pLastSurface;
|
||||
|
||||
Vector2D m_vLastCoords;
|
||||
bool m_bCoordsInitialized = false;
|
||||
|
||||
void renderSurface(CLayerSurface*, bool forceInactive = false);
|
||||
|
||||
|
|
@ -70,10 +80,11 @@ class CHyprpicker {
|
|||
void markDirty();
|
||||
|
||||
void finish(int code = 0);
|
||||
void outputColor();
|
||||
|
||||
CColor getColorFromPixel(CLayerSurface*, Vector2D);
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
inline std::unique_ptr<CHyprpicker> g_pHyprpicker;
|
||||
inline std::unique_ptr<CHyprpicker> g_pHyprpicker;
|
||||
|
|
|
|||
|
|
@ -4,12 +4,11 @@
|
|||
#include <deque>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string.h>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <cmath>
|
||||
#include <math.h>
|
||||
|
||||
#include "protocols/cursor-shape-v1.hpp"
|
||||
#include "protocols/fractional-scale-v1.hpp"
|
||||
|
|
@ -18,14 +17,13 @@
|
|||
#include "protocols/viewporter.hpp"
|
||||
#include "protocols/wayland.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
#include <cassert>
|
||||
#include <cairo.h>
|
||||
#include <cairo/cairo.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <wayland-client.h>
|
||||
|
|
@ -37,7 +35,9 @@
|
|||
#include <unordered_map>
|
||||
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
using namespace Hyprutils::Memory;
|
||||
using namespace Hyprutils::OS;
|
||||
|
||||
#define SP CSharedPointer
|
||||
#define WP CWeakPointer
|
||||
|
|
|
|||
119
src/main.cpp
119
src/main.cpp
|
|
@ -1,21 +1,31 @@
|
|||
#include <cstdint>
|
||||
#include <format>
|
||||
#include <regex>
|
||||
#include <strings.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "hyprpicker.hpp"
|
||||
#include "src/debug/Log.hpp"
|
||||
|
||||
static void help(void) {
|
||||
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"
|
||||
<< " -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"
|
||||
<< " -z | --no-zoom | Disable the zoom lens\n"
|
||||
<< " -q | --quiet | Disable most logs (leaves errors)\n"
|
||||
<< " -v | --verbose | Enable more logs\n"
|
||||
<< " -t | --no-fractional | Disable fractional scaling support\n"
|
||||
<< " -V | --version | Print version info\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"
|
||||
<< " -o | --output-format=fmt | Specifies how the output color should be formatted e.g. rgb({0}, {1}, {2}) would output rgb(red, green, blue) if --format=rgb\n"
|
||||
<< " -n | --notify | Sends a desktop notification when a color is picked (requires notify-send and a notification daemon like dunst)\n"
|
||||
<< " -b | --no-fancy | Disables the \"fancy\" (aka. colored) outputting\n"
|
||||
<< " -h | --help | Show this help message\n"
|
||||
<< " -r | --render-inactive | Render (freeze) inactive displays\n"
|
||||
<< " -z | --no-zoom | Disable the zoom lens\n"
|
||||
<< " -q | --quiet | Disable most logs (leaves errors)\n"
|
||||
<< " -v | --verbose | Enable more logs\n"
|
||||
<< " -t | --no-fractional | Disable fractional scaling support\n"
|
||||
<< " -d | --disable-preview | Disable live preview of color\n"
|
||||
<< " -l | --lowercase-hex | Outputs the hexcode in lowercase\n"
|
||||
<< " -s | --scale=scale | Set the zoom scale (between 1 and 10)\n"
|
||||
<< " -u | --radius=radius | Set the circle radius (between 1 and 1000)\n"
|
||||
<< " -V | --version | Print version info\n";
|
||||
}
|
||||
|
||||
int main(int argc, char** argv, char** envp) {
|
||||
|
|
@ -23,19 +33,25 @@ int main(int argc, char** argv, char** envp) {
|
|||
|
||||
while (true) {
|
||||
int option_index = 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'},
|
||||
{"version", no_argument, NULL, 'V'},
|
||||
{NULL, 0, NULL, 0}};
|
||||
static struct option long_options[] = {{"autocopy", no_argument, nullptr, 'a'},
|
||||
{"format", required_argument, nullptr, 'f'},
|
||||
{"output-format", required_argument, nullptr, 'o'},
|
||||
{"help", no_argument, nullptr, 'h'},
|
||||
{"no-fancy", no_argument, nullptr, 'b'},
|
||||
{"notify", 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-preview", no_argument, nullptr, 'd'},
|
||||
{"lowercase-hex", no_argument, nullptr, 'l'},
|
||||
{"version", no_argument, nullptr, 'V'},
|
||||
{"scale", required_argument, nullptr, 's'},
|
||||
{"radius", required_argument, nullptr, 'u'},
|
||||
{nullptr, 0, nullptr, 0}};
|
||||
|
||||
int c = getopt_long(argc, argv, ":f:hnarzqvtV", long_options, &option_index);
|
||||
int c = getopt_long(argc, argv, ":f:o:hnbarzqvtdlVs:u:", long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
|
|
@ -56,19 +72,57 @@ int main(int argc, char** argv, char** envp) {
|
|||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'o': g_pHyprpicker->m_sOutputFormat = optarg; break;
|
||||
case 'h': help(); exit(0);
|
||||
case 'n': g_pHyprpicker->m_bFancyOutput = false; break;
|
||||
case 'b': g_pHyprpicker->m_bFancyOutput = false; break;
|
||||
case 'n': g_pHyprpicker->m_bNotify = true; break;
|
||||
case 'a': g_pHyprpicker->m_bAutoCopy = true; break;
|
||||
case 'r': g_pHyprpicker->m_bRenderInactive = true; break;
|
||||
case 'z': g_pHyprpicker->m_bNoZoom = true; break;
|
||||
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_bDisablePreview = true; break;
|
||||
case 'l': g_pHyprpicker->m_bUseLowerCase = true; break;
|
||||
case 'V': {
|
||||
std::cout << "hyprpicker v" << HYPRPICKER_VERSION << "\n";
|
||||
exit(0);
|
||||
}
|
||||
case 's': {
|
||||
float value;
|
||||
auto result = std::from_chars(optarg, optarg + strlen(optarg), value);
|
||||
|
||||
if (result.ec != std::errc() || result.ptr != optarg + strlen(optarg)) {
|
||||
std::cerr << "Invalid scale value: " << optarg << "\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (value < 1.0f || value > 10.0f) {
|
||||
std::cerr << "Scale must be between 1 and 10!\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
g_pHyprpicker->m_fZoomScale = value;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'u': {
|
||||
int value;
|
||||
auto result = std::from_chars(optarg, optarg + strlen(optarg), value);
|
||||
|
||||
if (result.ec != std::errc() || result.ptr != optarg + strlen(optarg)) {
|
||||
std::cerr << "Invalid radius value: " << optarg << "\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (value < 1 || value > 1000) {
|
||||
std::cerr << "Radius must be between 1 and 1000!\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
g_pHyprpicker->m_iCircleRadius = value;
|
||||
break;
|
||||
}
|
||||
default: help(); exit(1);
|
||||
}
|
||||
}
|
||||
|
|
@ -76,6 +130,23 @@ int main(int argc, char** argv, char** envp) {
|
|||
if (!isatty(fileno(stdout)) || getenv("NO_COLOR"))
|
||||
g_pHyprpicker->m_bFancyOutput = false;
|
||||
|
||||
if (g_pHyprpicker->m_sOutputFormat.empty()) {
|
||||
switch (g_pHyprpicker->m_bSelectedOutputMode) {
|
||||
case OUTPUT_CMYK: g_pHyprpicker->m_sOutputFormat = "{}% {}% {}% {}%"; break;
|
||||
case OUTPUT_HEX: g_pHyprpicker->m_sOutputFormat = "#{}{}{}"; break;
|
||||
case OUTPUT_RGB: g_pHyprpicker->m_sOutputFormat = "{} {} {}"; break;
|
||||
case OUTPUT_HSL: g_pHyprpicker->m_sOutputFormat = "{} {}% {}%"; break;
|
||||
case OUTPUT_HSV: g_pHyprpicker->m_sOutputFormat = "{} {}% {}%"; break;
|
||||
}
|
||||
}
|
||||
try {
|
||||
std::array<uint8_t, 4> dummy = {0, 0, 0, 0};
|
||||
(void)std::vformat(g_pHyprpicker->m_sOutputFormat, std::make_format_args(dummy[0], dummy[1], dummy[2], dummy[3]));
|
||||
} catch (const std::format_error& e) {
|
||||
Debug::log(NONE, "Invalid --output-format: %s", e.what());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
g_pHyprpicker->init();
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
19
src/notify/Notify.cpp
Normal file
19
src/notify/Notify.cpp
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#include "Notify.hpp"
|
||||
|
||||
#include "../includes.hpp"
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <format>
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
|
||||
void NNotify::send(std::string hexColor, std::string formattedColor) {
|
||||
std::string notifyBody = std::format("<span>Selected color: <span color='{}'><b>{}</b></span></span>", hexColor, formattedColor);
|
||||
|
||||
Hyprutils::OS::CProcess notify("notify-send", {"-t", "5000", "-i", "color-select-symbolic", "Color Picker", notifyBody});
|
||||
|
||||
notify.runAsync();
|
||||
}
|
||||
8
src/notify/Notify.hpp
Normal file
8
src/notify/Notify.hpp
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace NNotify {
|
||||
void send(std::string hexColor, std::string formattedColor);
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue