mirror of
https://github.com/hyprwm/hyprlock.git
synced 2025-05-12 21:30:37 +01:00

Some checks are pending
Build / nix (push) Waiting to run
* widget: add click handling and point containment methods to IWidget interface * core: add onClick method to handle mouse click events - renderer: move getOrCreateWidgetsFor method declaration to public section * core: update mouse event handling to track mouse location and button clicks * widget: add onclick command handling and point containment to CLabel - config: add onclick special config value to label * assets: add label configuration for keyboard layout switching * config: add onclick configuration for label widgets - add CLICKABLE macro for onclick configuration - replace direct onclick assignment with CLICKABLE macro * core: fix cursor shape initialization and pointer handling - ensure pointer is available before setting cursor shape - initialize cursor shape device if not already done * core: add hover handling and cursor shape updates - implement onHover method to manage widget hover states - update cursor shape based on hover status - ensure all outputs are redrawn after state changes * widgets: add hover state management and bounding box calculations - add setHover and isHovered methods to manage hover state - implement containsPoint method for hit testing - override getBoundingBox in CLabel for accurate positioning - add onHover method in CLabel to change cursor shape * core: add hover handling in pointer motion - invoke onHover method with current mouse location * widgets: add hover handling and bounding box for password input field - add getBoundingBox method to calculate the widget's bounding box - implement onHover method to update cursor shape on hover * widgets: update hover behavior for label widget - modify cursor shape setting to only apply when onclickCommand is not empty * core: optimize hover handling and rendering for lock surfaces - Improve hover state tracking for widgets - reduce unnecessary redraw calls by tracking hover changes - remove redundant renderAllOutputs() call * widgets: add onclick and hover to shape and image * core: trigger hover and onclick only for the currently focused surface * core: handle fractionalScale in onclick and hover * core: don't trigger onclick or hover when hide_cursor is set * misc: remove braces * core: run onclick commands asnychronously --------- Co-authored-by: Memoraike <memoraike@gmail.com>
160 lines
6 KiB
C++
160 lines
6 KiB
C++
#include <filesystem>
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <fcntl.h>
|
|
#include "MiscFunctions.hpp"
|
|
#include "Log.hpp"
|
|
#include <hyprutils/string/String.hpp>
|
|
#include <hyprutils/os/Process.hpp>
|
|
#include <unistd.h>
|
|
|
|
using namespace Hyprutils::String;
|
|
using namespace Hyprutils::OS;
|
|
|
|
std::string absolutePath(const std::string& rawpath, const std::string& currentDir) {
|
|
std::filesystem::path path(rawpath);
|
|
|
|
// Handling where rawpath starts with '~'
|
|
if (!rawpath.empty() && rawpath[0] == '~') {
|
|
static const char* const ENVHOME = getenv("HOME");
|
|
path = std::filesystem::path(ENVHOME) / path.relative_path().string().substr(2);
|
|
}
|
|
|
|
// Handling e.g. ./, ../
|
|
if (path.is_relative()) {
|
|
return std::filesystem::weakly_canonical(std::filesystem::path(currentDir) / path);
|
|
} else {
|
|
return std::filesystem::weakly_canonical(path);
|
|
}
|
|
}
|
|
|
|
int64_t configStringToInt(const std::string& VALUE) {
|
|
auto parseHex = [](const std::string& value) -> int64_t {
|
|
try {
|
|
size_t position;
|
|
auto result = stoll(value, &position, 16);
|
|
if (position == value.size())
|
|
return result;
|
|
} catch (const std::exception&) {}
|
|
throw std::invalid_argument("invalid hex " + value);
|
|
};
|
|
if (VALUE.starts_with("0x")) {
|
|
// Values with 0x are hex
|
|
return parseHex(VALUE);
|
|
} else if (VALUE.starts_with("rgba(") && VALUE.ends_with(')')) {
|
|
const auto VALUEWITHOUTFUNC = trim(VALUE.substr(5, VALUE.length() - 6));
|
|
|
|
// try doing it the comma way first
|
|
if (std::count(VALUEWITHOUTFUNC.begin(), VALUEWITHOUTFUNC.end(), ',') == 3) {
|
|
// cool
|
|
std::string rolling = VALUEWITHOUTFUNC;
|
|
auto r = configStringToInt(trim(rolling.substr(0, rolling.find(','))));
|
|
rolling = rolling.substr(rolling.find(',') + 1);
|
|
auto g = configStringToInt(trim(rolling.substr(0, rolling.find(','))));
|
|
rolling = rolling.substr(rolling.find(',') + 1);
|
|
auto b = configStringToInt(trim(rolling.substr(0, rolling.find(','))));
|
|
rolling = rolling.substr(rolling.find(',') + 1);
|
|
uint8_t a = 0;
|
|
try {
|
|
a = std::round(std::stof(trim(rolling.substr(0, rolling.find(',')))) * 255.f);
|
|
} catch (std::exception& e) { throw std::invalid_argument("failed parsing " + VALUEWITHOUTFUNC); }
|
|
|
|
return (a * (Hyprlang::INT)0x1000000) + (r * (Hyprlang::INT)0x10000) + (g * (Hyprlang::INT)0x100) + b;
|
|
} else if (VALUEWITHOUTFUNC.length() == 8) {
|
|
const auto RGBA = parseHex(VALUEWITHOUTFUNC);
|
|
// now we need to RGBA -> ARGB. The config holds ARGB only.
|
|
return (RGBA >> 8) + (0x1000000 * (RGBA & 0xFF));
|
|
}
|
|
|
|
throw std::invalid_argument("rgba() expects length of 8 characters (4 bytes) or 4 comma separated values");
|
|
|
|
} else if (VALUE.starts_with("rgb(") && VALUE.ends_with(')')) {
|
|
const auto VALUEWITHOUTFUNC = trim(VALUE.substr(4, VALUE.length() - 5));
|
|
|
|
// try doing it the comma way first
|
|
if (std::count(VALUEWITHOUTFUNC.begin(), VALUEWITHOUTFUNC.end(), ',') == 2) {
|
|
// cool
|
|
std::string rolling = VALUEWITHOUTFUNC;
|
|
auto r = configStringToInt(trim(rolling.substr(0, rolling.find(','))));
|
|
rolling = rolling.substr(rolling.find(',') + 1);
|
|
auto g = configStringToInt(trim(rolling.substr(0, rolling.find(','))));
|
|
rolling = rolling.substr(rolling.find(',') + 1);
|
|
auto b = configStringToInt(trim(rolling.substr(0, rolling.find(','))));
|
|
|
|
return (Hyprlang::INT)0xFF000000 + (r * (Hyprlang::INT)0x10000) + (g * (Hyprlang::INT)0x100) + b;
|
|
} else if (VALUEWITHOUTFUNC.length() == 6) {
|
|
return parseHex(VALUEWITHOUTFUNC) + 0xFF000000;
|
|
}
|
|
|
|
throw std::invalid_argument("rgb() expects length of 6 characters (3 bytes) or 3 comma separated values");
|
|
} else if (VALUE.starts_with("true") || VALUE.starts_with("on") || VALUE.starts_with("yes")) {
|
|
return 1;
|
|
} else if (VALUE.starts_with("false") || VALUE.starts_with("off") || VALUE.starts_with("no")) {
|
|
return 0;
|
|
}
|
|
|
|
if (VALUE.empty() || !isNumber(VALUE, false))
|
|
throw std::invalid_argument("cannot parse \"" + VALUE + "\" as an int.");
|
|
|
|
try {
|
|
const auto RES = std::stoll(VALUE);
|
|
return RES;
|
|
} catch (std::exception& e) { throw std::invalid_argument(std::string{"stoll threw: "} + e.what()); }
|
|
|
|
return 0;
|
|
}
|
|
|
|
int createPoolFile(size_t size, std::string& name) {
|
|
const auto XDGRUNTIMEDIR = getenv("XDG_RUNTIME_DIR");
|
|
if (!XDGRUNTIMEDIR) {
|
|
Debug::log(CRIT, "XDG_RUNTIME_DIR not set!");
|
|
return -1;
|
|
}
|
|
|
|
name = std::string(XDGRUNTIMEDIR) + "/.hyprlock_sc_XXXXXX";
|
|
|
|
const auto FD = mkstemp((char*)name.c_str());
|
|
if (FD < 0) {
|
|
Debug::log(CRIT, "createPoolFile: fd < 0");
|
|
return -1;
|
|
}
|
|
// set cloexec
|
|
long flags = fcntl(FD, F_GETFD);
|
|
if (flags == -1) {
|
|
close(FD);
|
|
return -1;
|
|
}
|
|
|
|
if (fcntl(FD, F_SETFD, flags | FD_CLOEXEC) == -1) {
|
|
close(FD);
|
|
Debug::log(CRIT, "createPoolFile: fcntl < 0");
|
|
return -1;
|
|
}
|
|
|
|
if (ftruncate(FD, size) < 0) {
|
|
close(FD);
|
|
Debug::log(CRIT, "createPoolFile: ftruncate < 0");
|
|
return -1;
|
|
}
|
|
|
|
return FD;
|
|
}
|
|
|
|
std::string spawnSync(const std::string& cmd) {
|
|
CProcess proc("/bin/sh", {"-c", cmd});
|
|
if (!proc.runSync()) {
|
|
Debug::log(ERR, "Failed to run \"{}\"", cmd);
|
|
return "";
|
|
}
|
|
|
|
if (!proc.stdErr().empty())
|
|
Debug::log(ERR, "Shell command \"{}\" STDERR:\n{}", cmd, proc.stdErr());
|
|
|
|
return proc.stdOut();
|
|
}
|
|
|
|
void spawnAsync(const std::string& cmd) {
|
|
CProcess proc("/bin/sh", {"-c", cmd});
|
|
if (!proc.runAsync())
|
|
Debug::log(ERR, "Failed to start \"{}\"", cmd);
|
|
}
|