Compare commits

...

34 commits
v0.7.0 ... main

Author SHA1 Message Date
Max Woolf
f7f0c9c6b0
example: Add config location hint to help new users (#771)
Some checks failed
Build / nix (push) Has been cancelled
2025-05-09 18:54:54 +02:00
nyx
c12cf8e509
core: disable fade in when using --immediate (#763)
Some checks failed
Build / nix (push) Has been cancelled
2025-05-07 07:13:41 +00:00
Virt
0c5fd97d61
renderer: properly treat monitor desc: prefix (#765) 2025-05-07 07:12:58 +00:00
Maximilian Seidler
fae1c4f6fe
misc: readme cleanup, remove deps required by hyprgraphics (#762)
Some checks failed
Build / nix (push) Has been cancelled
2025-05-05 23:45:32 +02:00
Maximilian Seidler
e3bd47e177
widgets: add onclick feature (#736)
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>
2025-05-05 15:11:24 +00:00
Vaxry
6c64630df8
version: bump to 0.8.2
Some checks failed
Build / nix (push) Has been cancelled
2025-05-03 15:04:47 +01:00
Maximilian Seidler
0e3e7206bc
core: use enqueueUnlock for unlocks via SIGUSR1 (#756)
Some checks failed
Build / nix (push) Has been cancelled
2025-04-30 07:12:40 +00:00
Maximilian Seidler
867a71dd78
core: avoid calling wl_display_read_events after poll returned due to EINTR (#757)
Some checks are pending
Build / nix (push) Waiting to run
2025-04-30 06:11:57 +00:00
Maximilian Seidler
82808290d9
core: correct $LAYOUT replacement (#755)
Some checks failed
Build / nix (push) Has been cancelled
* core: remove fake declaration in header

* widgets: fix layout rendering

* core: remove CSeatManager::getActiveKbLayoutName

---------

Co-authored-by: Heorhi Valakhanovich <code@mail.geov.name>
2025-04-25 13:01:34 +00:00
Honkazel
eb28a71756
clang-tidy: fix some errors (#751)
I did c+p .clang-tidy from Hyprland and didn't check it for any errors, lol. Still works though
Thanks to hyprwm/Hyprland#9543
2025-04-21 20:17:15 +02:00
Vaxry
b3f1aa7580
version: bump to 0.8.1 2025-04-17 18:07:52 +01:00
Mihai Fufezan
248dfb09f7
flake.lock: update 2025-04-17 10:18:52 +03:00
Maximilian Seidler
656704aeb0
config: default to center for label halign and valign (#748) 2025-04-15 09:05:42 +00:00
Maximilian Seidler
d953296227
renderer: fix gradient copy size in renderBorder (#742) 2025-04-12 11:47:53 +00:00
Maximilian Seidler
71d35aa75f
image: remove left over raw pointer to COutput (#735) 2025-04-10 07:36:54 +00:00
Vaxry
8f73c39f07 version: bump to 0.8.0 2025-04-08 16:37:50 +01:00
Maximilian Seidler
a8de918cc4
auth: use static for getValue (#732) 2025-04-08 16:37:13 +02:00
Brayden Zee
6daab0517c
fingerprint: update widgets after changing prompt (#730) 2025-04-08 04:49:41 +00:00
Maximilian Seidler
854235e1c8
output: refuse to create session lock surfaces with size 0x0 (#729) 2025-04-07 09:15:41 +00:00
Brayden Zee
dd4c1d5034
fingerprint: allow fprint to suspend and cancel verify for us (#722)
* fingerprint: allow fprint to suspend and cancel verify for us

* fingerprint: fix formatting
2025-04-07 09:14:05 +00:00
Maximilian Seidler
0b1f2a97ef
input-field: decouple outer color and base for hidden input random colors (#727) 2025-04-02 22:15:59 +02:00
Maximilian Seidler
ce1eb7b5f9
core: move fail_timeout from input-field to general (#718) 2025-04-02 22:13:22 +02:00
Maximilian Seidler
1ebbc35c55
core: remove attemptRestoreOnDeath and replace some exits with RASSERT (#720) 2025-03-30 01:33:34 +01:00
davc0n
d9a1625315
assets: update example.conf (#709)
Includes examples from wiki with minor modifications.
Date and time labels inspired by https://github.com/catppuccin/hyprlock

Aims to improve out-of-the-box experience and basic stuff reference.
2025-03-27 08:02:32 +00:00
davc0n
9e54d02590
renderer: remove loading bar (#714) 2025-03-25 06:23:37 +00:00
Maximilian Seidler
f883e669d1
CMake: require wayland-protocols>=1.35 (#713)
tablet-v2 was moved to stable in 1.35. Hyprlock will fail to build if a
earlier version is used.
2025-03-20 08:52:02 +00:00
Maximilian Seidler
ee8ee1f9f7
core: move password buffer clearing to handleInput (#708)
Makes more sense than clearing the input buffer in the auth impl.
Also added a check for the password buffer length to reset the fail
color as soon as the password length > 0.
2025-03-17 11:25:51 +00:00
André Silva
7ab3162d66 nix: mesa -> libgbm
d209d800b7
2025-03-14 08:36:46 +02:00
Maximilian Seidler
9e82fe3547
core: some guards for reconnecting monitors (#704) 2025-03-08 10:58:29 +01:00
Maximilian Seidler
a13b6f0d1a
core: print hyprlock version in the logs (#703) 2025-03-08 10:49:01 +01:00
Maximilian Seidler
78ad1d46b5
label: fix crashes when keymap is a nullptr after suspend (#699) 2025-03-06 08:37:43 +01:00
Maximilian Seidler
cb1c504b38
image: set resourceId in configure (#701)
Fixes a regression caused by #686 (Images don't render cause of a missing resourceId)
2025-03-06 08:25:53 +01:00
Maximilian Seidler
9f37c1c8e9
core: more hyprutils smart pointer usage and safe references to widgets (#686)
* core: move to UP and make widgets use SPs

* widgets: make widgets have a self ref to avoid UB

* fix shadows and let them have a WP to widgets
2025-03-05 08:35:43 +01:00
Maximilian Seidler
712ab62a42
config: make sure disabled animation don't need a valid speed or bezier (#698) 2025-03-05 08:05:19 +01:00
52 changed files with 1099 additions and 701 deletions

View file

@ -1,12 +1,12 @@
WarningsAsErrors: '*' WarningsAsErrors: '*'
HeaderFilterRegex: '.*\.hpp' HeaderFilterRegex: '.*\.hpp'
FormatStyle: file FormatStyle: 'file'
Checks: > Checks: >
-*, -*,
bugprone-*, bugprone-*,
-bugprone-easily-swappable-parameters, -bugprone-easily-swappable-parameters,
-bugprone-forward-declararion-namespace, -bugprone-forward-declaration-namespace,
-bugprone-forward-declararion-namespace, -bugprone-forward-declaration-namespace,
-bugprone-macro-parentheses, -bugprone-macro-parentheses,
-bugprone-narrowing-conversions, -bugprone-narrowing-conversions,
-bugprone-branch-clone, -bugprone-branch-clone,

View file

@ -77,14 +77,11 @@ pkg_check_modules(
REQUIRED REQUIRED
IMPORTED_TARGET IMPORTED_TARGET
wayland-client wayland-client
wayland-protocols wayland-protocols>=1.35
wayland-egl wayland-egl
hyprlang>=0.6.0 hyprlang>=0.6.0
egl egl
xkbcommon xkbcommon
libjpeg
libwebp
libmagic
cairo cairo
pangocairo pangocairo
libdrm libdrm

View file

@ -2,10 +2,14 @@
Hyprland's simple, yet multi-threaded and GPU-accelerated screen locking utility. Hyprland's simple, yet multi-threaded and GPU-accelerated screen locking utility.
## Features ## Features
- uses the secure ext-session-lock protocol - Uses the ext-session-lock protocol
- full support for fractional-scale - Support for fractional-scale
- fully GPU accelerated - Fully GPU accelerated
- multi-threaded resource acquisition for no hitches - Multi-threaded resource acquisition
- Blurred screenshot as the background
- Native fingerprint support (using libfprint's dbus interface)
- Some of Hyprland's eyecandy: gradient borders, blur, animations, shadows, etc.
- and more...
## How it looks ## How it looks
@ -25,26 +29,23 @@ yay -S hyprlock-git # compiles from latest source
### Deps ### Deps
You need the following dependencies You need the following dependencies
- wayland-client
- wayland-protocols
- mesa
- hyprwayland-scanner
And the development libraries for the following
- cairo - cairo
- libdrm - hyprgraphics
- pango - hyprland-protocols
- xkbcommon
- pam
- hyprlang - hyprlang
- hyprutils - hyprutils
- hyprgraphics - hyprwayland-scanner
- libmagic (file-devel on Fedora) - mesa (required is libgbm, libdrm and the opengl runtime)
- pam
- pango
- sdbus-cpp (>= 2.0.0)
- wayland-client
- wayland-protocols
- xkbcommon
Development libraries are usually suffixed with `-devel` or `-dev` in most distro repos. Sometimes distro packages are missing required development files.
Such distros usually offer development versions of library package - commonly suffixed with `-devel` or `-dev`.
You also need to install `mesa-libgbm-devel` on some distros like RPM based ones where its not
bundled with the mesa package.
### Building ### Building

View file

@ -1 +1 @@
0.7.0 0.8.2

View file

@ -1,18 +1,106 @@
# sample hyprlock.conf # sample hyprlock.conf
# for more configuration options, refer https://wiki.hyprland.org/Hypr-Ecosystem/hyprlock # for more configuration options, refer https://wiki.hyprland.org/Hypr-Ecosystem/hyprlock
#
# rendered text in all widgets supports pango markup (e.g. <b> or <i> tags)
# ref. https://wiki.hyprland.org/Hypr-Ecosystem/hyprlock/#general-remarks
#
# shortcuts to clear password buffer: ESC, Ctrl+U, Ctrl+Backspace
#
# you can get started by copying this config to ~/.config/hypr/hyprlock.conf
#
$font = Monospace
general {
hide_cursor = false
}
# uncomment to enable fingerprint authentication
# auth {
# fingerprint {
# enabled = true
# ready_message = Scan fingerprint to unlock
# present_message = Scanning...
# retry_delay = 250 # in milliseconds
# }
# }
animations { animations {
enabled = true enabled = true
bezier = linear, 1, 1, 0, 0 bezier = linear, 1, 1, 0, 0
animation = fadeIn, 1, 5, linear animation = fadeIn, 1, 5, linear
animation = fadeOut, 1, 5, linear animation = fadeOut, 1, 5, linear
animation = inputFieldDots, 1, 2, linear
}
background {
monitor =
path = screenshot
blur_passes = 3
} }
input-field { input-field {
monitor = monitor =
size = 20%, 5%
outline_thickness = 3
inner_color = rgba(0, 0, 0, 0.0) # no fill
outer_color = rgba(33ccffee) rgba(00ff99ee) 45deg
check_color = rgba(00ff99ee) rgba(ff6633ee) 120deg
fail_color = rgba(ff6633ee) rgba(ff0066ee) 40deg
font_color = rgb(143, 143, 143)
fade_on_empty = false fade_on_empty = false
rounding = 15
font_family = $font
placeholder_text = Input password...
fail_text = $PAMFAIL
# uncomment to use a letter instead of a dot to indicate the typed password
# dots_text_format = *
# dots_size = 0.4
dots_spacing = 0.3
# uncomment to use an input indicator that does not show the password length (similar to swaylock's input indicator)
# hide_input = true
position = 0, -20
halign = center
valign = center
} }
background { # TIME
color = rgb(23, 39, 41) label {
monitor =
text = $TIME # ref. https://wiki.hyprland.org/Hypr-Ecosystem/hyprlock/#variable-substitution
font_size = 90
font_family = $font
position = -30, 0
halign = right
valign = top
}
# DATE
label {
monitor =
text = cmd[update:60000] date +"%A, %d %B %Y" # update every 60 seconds
font_size = 25
font_family = $font
position = -30, -150
halign = right
valign = top
}
label {
monitor =
text = $LAYOUT[en,ru]
font_size = 24
onclick = hyprctl switchxkblayout all next
position = 250, -20
halign = center
valign = center
} }

View file

@ -13,11 +13,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1737634889, "lastModified": 1743953322,
"narHash": "sha256-9JZE3KxcXOqZH9zs3UeadngDiK/yIACTiAR8HSA/TNI=", "narHash": "sha256-prQ5JKopXtzCMX2eT3dXbaVvGmzjMRE2bXStQDdazpM=",
"owner": "hyprwm", "owner": "hyprwm",
"repo": "hyprgraphics", "repo": "hyprgraphics",
"rev": "0d77b4895ad5f1bb3b0ee43103a5246c58b65591", "rev": "9d7f2687c84c729afbc3b13f7937655570f2978d",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -39,11 +39,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1737634606, "lastModified": 1744468525,
"narHash": "sha256-W7W87Cv6wqZ9PHegI6rH1+ve3zJPiyevMFf0/HwdbCQ=", "narHash": "sha256-9HySx+EtsbbKlZDlY+naqqOV679VdxP6x6fP3wxDXJk=",
"owner": "hyprwm", "owner": "hyprwm",
"repo": "hyprlang", "repo": "hyprlang",
"rev": "f41271d35cc0f370d300413d756c2677f386af9d", "rev": "f1000c54d266e6e4e9d646df0774fac5b8a652df",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -62,11 +62,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1737978343, "lastModified": 1743950287,
"narHash": "sha256-TfFS0HCEJh63Kahrkp1h9hVDMdLU8a37Zz+IFucxyfA=", "narHash": "sha256-/6IAEWyb8gC/NKZElxiHChkouiUOrVYNq9YqG0Pzm4Y=",
"owner": "hyprwm", "owner": "hyprwm",
"repo": "hyprutils", "repo": "hyprutils",
"rev": "6a8bc9d2a4451df12f5179dc0b1d2d46518a90ab", "rev": "f2dc70e448b994cef627a157ee340135bd68fbc6",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -85,11 +85,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1735493474, "lastModified": 1739870480,
"narHash": "sha256-fktzv4NaqKm94VAkAoVqO/nqQlw+X0/tJJNAeCSfzK4=", "narHash": "sha256-SiDN5BGxa/1hAsqhgJsS03C3t2QrLgBT8u+ENJ0Qzwc=",
"owner": "hyprwm", "owner": "hyprwm",
"repo": "hyprwayland-scanner", "repo": "hyprwayland-scanner",
"rev": "de913476b59ee88685fdc018e77b8f6637a2ae0b", "rev": "206367a08dc5ac4ba7ad31bdca391d098082e64b",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -100,11 +100,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1737469691, "lastModified": 1744463964,
"narHash": "sha256-nmKOgAU48S41dTPIXAq0AHZSehWUn6ZPrUKijHAMmIk=", "narHash": "sha256-LWqduOgLHCFxiTNYi3Uj5Lgz0SR+Xhw3kr/3Xd0GPTM=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "9e4d5190a9482a1fb9d18adf0bdb83c6e506eaab", "rev": "2631b0b7abcea6e640ce31cd78ea58910d31e650",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -4,13 +4,10 @@
cmake, cmake,
pkg-config, pkg-config,
cairo, cairo,
file,
libdrm, libdrm,
libGL, libGL,
libjpeg,
libwebp,
libxkbcommon, libxkbcommon,
mesa, libgbm,
hyprgraphics, hyprgraphics,
hyprlang, hyprlang,
hyprutils, hyprutils,
@ -40,13 +37,10 @@ stdenv.mkDerivation {
buildInputs = [ buildInputs = [
cairo cairo
file
libdrm libdrm
libGL libGL
libjpeg
libwebp
libxkbcommon libxkbcommon
mesa libgbm
hyprgraphics hyprgraphics
hyprlang hyprlang
hyprutils hyprutils

View file

@ -11,10 +11,10 @@
CAuth::CAuth() { CAuth::CAuth() {
static const auto ENABLEPAM = g_pConfigManager->getValue<Hyprlang::INT>("auth:pam:enabled"); static const auto ENABLEPAM = g_pConfigManager->getValue<Hyprlang::INT>("auth:pam:enabled");
if (*ENABLEPAM) if (*ENABLEPAM)
m_vImpls.push_back(std::make_shared<CPam>()); m_vImpls.emplace_back(makeShared<CPam>());
static const auto ENABLEFINGERPRINT = g_pConfigManager->getValue<Hyprlang::INT>("auth:fingerprint:enabled"); static const auto ENABLEFINGERPRINT = g_pConfigManager->getValue<Hyprlang::INT>("auth:fingerprint:enabled");
if (*ENABLEFINGERPRINT) if (*ENABLEFINGERPRINT)
m_vImpls.push_back(std::make_shared<CFingerprint>()); m_vImpls.emplace_back(makeShared<CFingerprint>());
RASSERT(!m_vImpls.empty(), "At least one authentication method must be enabled!"); RASSERT(!m_vImpls.empty(), "At least one authentication method must be enabled!");
} }
@ -29,15 +29,12 @@ void CAuth::submitInput(const std::string& input) {
for (const auto& i : m_vImpls) { for (const auto& i : m_vImpls) {
i->handleInput(input); i->handleInput(input);
} }
g_pHyprlock->clearPasswordBuffer();
} }
bool CAuth::checkWaiting() { bool CAuth::checkWaiting() {
for (const auto& i : m_vImpls) { return std::ranges::any_of(m_vImpls, [](const auto& i) { return i->checkWaiting(); });
if (i->checkWaiting())
return true;
}
return false;
} }
const std::string& CAuth::getCurrentFailText() { const std::string& CAuth::getCurrentFailText() {
@ -64,7 +61,7 @@ size_t CAuth::getFailedAttempts() {
return m_sCurrentFail.failedAttempts; return m_sCurrentFail.failedAttempts;
} }
std::shared_ptr<IAuthImplementation> CAuth::getImpl(eAuthImplementations implType) { SP<IAuthImplementation> CAuth::getImpl(eAuthImplementations implType) {
for (const auto& i : m_vImpls) { for (const auto& i : m_vImpls) {
if (i->getImplType() == implType) if (i->getImplType() == implType)
return i; return i;
@ -79,30 +76,49 @@ void CAuth::terminate() {
} }
} }
static void unlockCallback(std::shared_ptr<CTimer> self, void* data) {
g_pHyprlock->unlock();
}
void CAuth::enqueueUnlock() {
g_pHyprlock->addTimer(std::chrono::milliseconds(0), unlockCallback, nullptr);
}
static void passwordFailCallback(std::shared_ptr<CTimer> self, void* data) { static void passwordFailCallback(std::shared_ptr<CTimer> self, void* data) {
g_pAuth->m_bDisplayFailText = true; g_pAuth->m_bDisplayFailText = true;
g_pHyprlock->clearPasswordBuffer();
g_pHyprlock->enqueueForceUpdateTimers(); g_pHyprlock->enqueueForceUpdateTimers();
g_pHyprlock->renderAllOutputs(); g_pHyprlock->renderAllOutputs();
} }
static void passwordUnlockCallback(std::shared_ptr<CTimer> self, void* data) { static void displayFailTimeoutCallback(std::shared_ptr<CTimer> self, void* data) {
g_pHyprlock->unlock(); if (g_pAuth->m_bDisplayFailText) {
g_pAuth->m_bDisplayFailText = false;
g_pHyprlock->renderAllOutputs();
}
} }
void CAuth::enqueueFail(const std::string& failText, eAuthImplementations implType) { void CAuth::enqueueFail(const std::string& failText, eAuthImplementations implType) {
static const auto FAILTIMEOUT = g_pConfigManager->getValue<Hyprlang::INT>("general:fail_timeout");
m_sCurrentFail.failText = failText; m_sCurrentFail.failText = failText;
m_sCurrentFail.failSource = implType; m_sCurrentFail.failSource = implType;
m_sCurrentFail.failedAttempts++; m_sCurrentFail.failedAttempts++;
Debug::log(LOG, "Failed attempts: {}", m_sCurrentFail.failedAttempts); Debug::log(LOG, "Failed attempts: {}", m_sCurrentFail.failedAttempts);
if (m_resetDisplayFailTimer) {
m_resetDisplayFailTimer->cancel();
m_resetDisplayFailTimer.reset();
}
g_pHyprlock->addTimer(std::chrono::milliseconds(0), passwordFailCallback, nullptr); g_pHyprlock->addTimer(std::chrono::milliseconds(0), passwordFailCallback, nullptr);
m_resetDisplayFailTimer = g_pHyprlock->addTimer(std::chrono::milliseconds(*FAILTIMEOUT), displayFailTimeoutCallback, nullptr);
} }
void CAuth::enqueueUnlock() { void CAuth::resetDisplayFail() {
g_pHyprlock->addTimer(std::chrono::milliseconds(0), passwordUnlockCallback, nullptr); g_pAuth->m_bDisplayFailText = false;
m_resetDisplayFailTimer->cancel();
m_resetDisplayFailTimer.reset();
} }

View file

@ -1,9 +1,11 @@
#pragma once #pragma once
#include <memory>
#include <optional> #include <optional>
#include <vector> #include <vector>
#include "../defines.hpp"
#include "../core/Timer.hpp"
enum eAuthImplementations { enum eAuthImplementations {
AUTH_IMPL_PAM = 0, AUTH_IMPL_PAM = 0,
AUTH_IMPL_FINGERPRINT = 1, AUTH_IMPL_FINGERPRINT = 1,
@ -39,13 +41,15 @@ class CAuth {
std::optional<std::string> getPrompt(eAuthImplementations implType); std::optional<std::string> getPrompt(eAuthImplementations implType);
size_t getFailedAttempts(); size_t getFailedAttempts();
std::shared_ptr<IAuthImplementation> getImpl(eAuthImplementations implType); SP<IAuthImplementation> getImpl(eAuthImplementations implType);
void terminate(); void terminate();
void enqueueUnlock(); void enqueueUnlock();
void enqueueFail(const std::string& failText, eAuthImplementations implType); void enqueueFail(const std::string& failText, eAuthImplementations implType);
void resetDisplayFail();
// Should only be set via the main thread // Should only be set via the main thread
bool m_bDisplayFailText = false; bool m_bDisplayFailText = false;
@ -56,7 +60,8 @@ class CAuth {
size_t failedAttempts = 0; size_t failedAttempts = 0;
} m_sCurrentFail; } m_sCurrentFail;
std::vector<std::shared_ptr<IAuthImplementation>> m_vImpls; std::vector<SP<IAuthImplementation>> m_vImpls;
std::shared_ptr<CTimer> m_resetDisplayFailTimer;
}; };
inline std::unique_ptr<CAuth> g_pAuth; inline UP<CAuth> g_pAuth;

View file

@ -65,20 +65,13 @@ void CFingerprint::init() {
// When entering sleep, the wake signal will trigger startVerify(). // When entering sleep, the wake signal will trigger startVerify().
if (m_sDBUSState.sleeping) if (m_sDBUSState.sleeping)
return; return;
inhibitSleep();
startVerify(); startVerify();
}); });
m_sDBUSState.login->uponSignal("PrepareForSleep").onInterface(LOGIN_MANAGER).call([this](bool start) { m_sDBUSState.login->uponSignal("PrepareForSleep").onInterface(LOGIN_MANAGER).call([this](bool start) {
Debug::log(LOG, "fprint: PrepareForSleep (start: {})", start); Debug::log(LOG, "fprint: PrepareForSleep (start: {})", start);
if (start) { m_sDBUSState.sleeping = start;
m_sDBUSState.sleeping = true; if (!m_sDBUSState.sleeping && !m_sDBUSState.verifying)
stopVerify();
m_sDBUSState.inhibitLock.reset();
} else {
m_sDBUSState.sleeping = false;
inhibitSleep();
startVerify(); startVerify();
}
}); });
} }
@ -111,18 +104,6 @@ std::shared_ptr<sdbus::IConnection> CFingerprint::getConnection() {
return m_sDBUSState.connection; return m_sDBUSState.connection;
} }
void CFingerprint::inhibitSleep() {
m_sDBUSState.login->callMethodAsync("Inhibit")
.onInterface(LOGIN_MANAGER)
.withArguments("sleep", "hyprlock", "Fingerprint verifcation must be stopped before sleep", "delay")
.uponReplyInvoke([this](std::optional<sdbus::Error> e, sdbus::UnixFd fd) {
if (e)
Debug::log(WARN, "fprint: could not inhibit sleep: {}", e->what());
else
m_sDBUSState.inhibitLock = fd;
});
}
bool CFingerprint::createDeviceProxy() { bool CFingerprint::createDeviceProxy() {
auto proxy = sdbus::createProxy(*m_sDBUSState.connection, FPRINT, sdbus::ObjectPath{"/net/reactivated/Fprint/Manager"}); auto proxy = sdbus::createProxy(*m_sDBUSState.connection, FPRINT, sdbus::ObjectPath{"/net/reactivated/Fprint/Manager"});
@ -163,8 +144,11 @@ void CFingerprint::handleVerifyStatus(const std::string& result, bool done) {
auto matchResult = s_mapStringToTestType[result]; auto matchResult = s_mapStringToTestType[result];
bool authenticated = false; bool authenticated = false;
bool retry = false; bool retry = false;
if (m_sDBUSState.sleeping && matchResult != MATCH_DISCONNECTED) if (m_sDBUSState.sleeping) {
stopVerify();
Debug::log(LOG, "fprint: device suspended");
return; return;
}
switch (matchResult) { switch (matchResult) {
case MATCH_INVALID: Debug::log(WARN, "fprint: unknown status: {}", result); break; case MATCH_INVALID: Debug::log(WARN, "fprint: unknown status: {}", result); break;
case MATCH_NO_MATCH: case MATCH_NO_MATCH:
@ -211,6 +195,8 @@ void CFingerprint::handleVerifyStatus(const std::string& result, bool done) {
if (!authenticated && !retry) if (!authenticated && !retry)
g_pAuth->enqueueFail(m_sFailureReason, AUTH_IMPL_FINGERPRINT); g_pAuth->enqueueFail(m_sFailureReason, AUTH_IMPL_FINGERPRINT);
else if (retry)
g_pHyprlock->enqueueForceUpdateTimers();
if (done || m_sDBUSState.abort) if (done || m_sDBUSState.abort)
m_sDBUSState.done = true; m_sDBUSState.done = true;
@ -229,6 +215,7 @@ void CFingerprint::claimDevice() {
} }
void CFingerprint::startVerify(bool isRetry) { void CFingerprint::startVerify(bool isRetry) {
m_sDBUSState.verifying = true;
if (!m_sDBUSState.device) { if (!m_sDBUSState.device) {
if (!createDeviceProxy()) if (!createDeviceProxy())
return; return;
@ -256,6 +243,7 @@ void CFingerprint::startVerify(bool isRetry) {
} }
bool CFingerprint::stopVerify() { bool CFingerprint::stopVerify() {
m_sDBUSState.verifying = false;
if (!m_sDBUSState.device) if (!m_sDBUSState.device)
return false; return false;
try { try {

View file

@ -29,12 +29,12 @@ class CFingerprint : public IAuthImplementation {
std::shared_ptr<sdbus::IConnection> connection; std::shared_ptr<sdbus::IConnection> connection;
std::unique_ptr<sdbus::IProxy> login; std::unique_ptr<sdbus::IProxy> login;
std::unique_ptr<sdbus::IProxy> device; std::unique_ptr<sdbus::IProxy> device;
sdbus::UnixFd inhibitLock;
bool abort = false; bool abort = false;
bool done = false; bool done = false;
int retries = 0; int retries = 0;
bool sleeping = false; bool sleeping = false;
bool verifying = false;
} m_sDBUSState; } m_sDBUSState;
std::string m_sFingerprintReady; std::string m_sFingerprintReady;
@ -45,8 +45,6 @@ class CFingerprint : public IAuthImplementation {
void handleVerifyStatus(const std::string& result, const bool done); void handleVerifyStatus(const std::string& result, const bool done);
void inhibitSleep();
bool createDeviceProxy(); bool createDeviceProxy();
void claimDevice(); void claimDevice();
void startVerify(bool isRetry = false); void startVerify(bool isRetry = false);

View file

@ -138,14 +138,7 @@ bool CPam::auth() {
return true; return true;
} }
// clearing the input must be done from the main thread
static void clearInputTimerCallback(std::shared_ptr<CTimer> self, void* data) {
g_pHyprlock->clearPasswordBuffer();
}
void CPam::waitForInput() { void CPam::waitForInput() {
g_pHyprlock->addTimer(std::chrono::milliseconds(1), clearInputTimerCallback, nullptr);
std::unique_lock<std::mutex> lk(m_sConversationState.inputMutex); std::unique_lock<std::mutex> lk(m_sConversationState.inputMutex);
m_bBlockInput = false; m_bBlockInput = false;
m_sConversationState.waitingForPamAuth = false; m_sConversationState.waitingForPamAuth = false;

View file

@ -27,7 +27,7 @@ class ICustomConfigValueData {
class CLayoutValueData : public ICustomConfigValueData { class CLayoutValueData : public ICustomConfigValueData {
public: public:
CLayoutValueData() {}; CLayoutValueData() = default;
virtual ~CLayoutValueData() {}; virtual ~CLayoutValueData() {};
virtual eConfigValueDataTypes getDataType() { virtual eConfigValueDataTypes getDataType() {

View file

@ -209,7 +209,9 @@ void CConfigManager::init() {
m_config.addSpecialConfigValue(name, "shadow_passes", Hyprlang::INT{0}); \ m_config.addSpecialConfigValue(name, "shadow_passes", Hyprlang::INT{0}); \
m_config.addSpecialConfigValue(name, "shadow_color", Hyprlang::INT{0xFF000000}); \ m_config.addSpecialConfigValue(name, "shadow_color", Hyprlang::INT{0xFF000000}); \
m_config.addSpecialConfigValue(name, "shadow_boost", Hyprlang::FLOAT{1.2}); m_config.addSpecialConfigValue(name, "shadow_boost", Hyprlang::FLOAT{1.2});
m_config.addConfigValue("general:disable_loading_bar", Hyprlang::INT{0});
#define CLICKABLE(name) m_config.addSpecialConfigValue(name, "onclick", Hyprlang::STRING{""});
m_config.addConfigValue("general:text_trim", Hyprlang::INT{1}); m_config.addConfigValue("general:text_trim", Hyprlang::INT{1});
m_config.addConfigValue("general:hide_cursor", Hyprlang::INT{0}); m_config.addConfigValue("general:hide_cursor", Hyprlang::INT{0});
m_config.addConfigValue("general:grace", Hyprlang::INT{0}); m_config.addConfigValue("general:grace", Hyprlang::INT{0});
@ -217,6 +219,7 @@ void CConfigManager::init() {
m_config.addConfigValue("general:immediate_render", Hyprlang::INT{0}); m_config.addConfigValue("general:immediate_render", Hyprlang::INT{0});
m_config.addConfigValue("general:fractional_scaling", Hyprlang::INT{2}); m_config.addConfigValue("general:fractional_scaling", Hyprlang::INT{2});
m_config.addConfigValue("general:screencopy_mode", Hyprlang::INT{0}); m_config.addConfigValue("general:screencopy_mode", Hyprlang::INT{0});
m_config.addConfigValue("general:fail_timeout", Hyprlang::INT{2000});
m_config.addConfigValue("auth:pam:enabled", Hyprlang::INT{1}); m_config.addConfigValue("auth:pam:enabled", Hyprlang::INT{1});
m_config.addConfigValue("auth:pam:module", Hyprlang::STRING{"hyprlock"}); m_config.addConfigValue("auth:pam:module", Hyprlang::STRING{"hyprlock"});
@ -257,6 +260,7 @@ void CConfigManager::init() {
m_config.addSpecialConfigValue("shape", "xray", Hyprlang::INT{0}); m_config.addSpecialConfigValue("shape", "xray", Hyprlang::INT{0});
m_config.addSpecialConfigValue("shape", "zindex", Hyprlang::INT{0}); m_config.addSpecialConfigValue("shape", "zindex", Hyprlang::INT{0});
SHADOWABLE("shape"); SHADOWABLE("shape");
CLICKABLE("shape");
m_config.addSpecialCategory("image", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true}); m_config.addSpecialCategory("image", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
m_config.addSpecialConfigValue("image", "monitor", Hyprlang::STRING{""}); m_config.addSpecialConfigValue("image", "monitor", Hyprlang::STRING{""});
@ -273,6 +277,7 @@ void CConfigManager::init() {
m_config.addSpecialConfigValue("image", "reload_cmd", Hyprlang::STRING{""}); m_config.addSpecialConfigValue("image", "reload_cmd", Hyprlang::STRING{""});
m_config.addSpecialConfigValue("image", "zindex", Hyprlang::INT{0}); m_config.addSpecialConfigValue("image", "zindex", Hyprlang::INT{0});
SHADOWABLE("image"); SHADOWABLE("image");
CLICKABLE("image");
m_config.addSpecialCategory("input-field", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true}); m_config.addSpecialCategory("input-field", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
m_config.addSpecialConfigValue("input-field", "monitor", Hyprlang::STRING{""}); m_config.addSpecialConfigValue("input-field", "monitor", Hyprlang::STRING{""});
@ -294,11 +299,11 @@ void CConfigManager::init() {
m_config.addSpecialConfigValue("input-field", "position", LAYOUTCONFIG("0,0")); m_config.addSpecialConfigValue("input-field", "position", LAYOUTCONFIG("0,0"));
m_config.addSpecialConfigValue("input-field", "placeholder_text", Hyprlang::STRING{"<i>Input Password</i>"}); m_config.addSpecialConfigValue("input-field", "placeholder_text", Hyprlang::STRING{"<i>Input Password</i>"});
m_config.addSpecialConfigValue("input-field", "hide_input", Hyprlang::INT{0}); m_config.addSpecialConfigValue("input-field", "hide_input", Hyprlang::INT{0});
m_config.addSpecialConfigValue("input-field", "hide_input_base_color", Hyprlang::INT{0xEE00FF99});
m_config.addSpecialConfigValue("input-field", "rounding", Hyprlang::INT{-1}); m_config.addSpecialConfigValue("input-field", "rounding", Hyprlang::INT{-1});
m_config.addSpecialConfigValue("input-field", "check_color", GRADIENTCONFIG("0xFF22CC88")); m_config.addSpecialConfigValue("input-field", "check_color", GRADIENTCONFIG("0xFF22CC88"));
m_config.addSpecialConfigValue("input-field", "fail_color", GRADIENTCONFIG("0xFFCC2222")); m_config.addSpecialConfigValue("input-field", "fail_color", GRADIENTCONFIG("0xFFCC2222"));
m_config.addSpecialConfigValue("input-field", "fail_text", Hyprlang::STRING{"<i>$FAIL</i>"}); m_config.addSpecialConfigValue("input-field", "fail_text", Hyprlang::STRING{"<i>$FAIL</i>"});
m_config.addSpecialConfigValue("input-field", "fail_timeout", Hyprlang::INT{2000});
m_config.addSpecialConfigValue("input-field", "capslock_color", GRADIENTCONFIG("")); m_config.addSpecialConfigValue("input-field", "capslock_color", GRADIENTCONFIG(""));
m_config.addSpecialConfigValue("input-field", "numlock_color", GRADIENTCONFIG("")); m_config.addSpecialConfigValue("input-field", "numlock_color", GRADIENTCONFIG(""));
m_config.addSpecialConfigValue("input-field", "bothlock_color", GRADIENTCONFIG("")); m_config.addSpecialConfigValue("input-field", "bothlock_color", GRADIENTCONFIG(""));
@ -314,12 +319,13 @@ void CConfigManager::init() {
m_config.addSpecialConfigValue("label", "font_size", Hyprlang::INT{16}); m_config.addSpecialConfigValue("label", "font_size", Hyprlang::INT{16});
m_config.addSpecialConfigValue("label", "text", Hyprlang::STRING{"Sample Text"}); m_config.addSpecialConfigValue("label", "text", Hyprlang::STRING{"Sample Text"});
m_config.addSpecialConfigValue("label", "font_family", Hyprlang::STRING{"Sans"}); m_config.addSpecialConfigValue("label", "font_family", Hyprlang::STRING{"Sans"});
m_config.addSpecialConfigValue("label", "halign", Hyprlang::STRING{"none"}); m_config.addSpecialConfigValue("label", "halign", Hyprlang::STRING{"center"});
m_config.addSpecialConfigValue("label", "valign", Hyprlang::STRING{"none"}); m_config.addSpecialConfigValue("label", "valign", Hyprlang::STRING{"center"});
m_config.addSpecialConfigValue("label", "rotate", Hyprlang::FLOAT{0}); m_config.addSpecialConfigValue("label", "rotate", Hyprlang::FLOAT{0});
m_config.addSpecialConfigValue("label", "text_align", Hyprlang::STRING{""}); m_config.addSpecialConfigValue("label", "text_align", Hyprlang::STRING{""});
m_config.addSpecialConfigValue("label", "zindex", Hyprlang::INT{0}); m_config.addSpecialConfigValue("label", "zindex", Hyprlang::INT{0});
SHADOWABLE("label"); SHADOWABLE("label");
CLICKABLE("label");
m_config.registerHandler(&::handleSource, "source", {.allowFlags = false}); m_config.registerHandler(&::handleSource, "source", {.allowFlags = false});
m_config.registerHandler(&::handleBezier, "bezier", {.allowFlags = false}); m_config.registerHandler(&::handleBezier, "bezier", {.allowFlags = false});
@ -356,6 +362,7 @@ void CConfigManager::init() {
Debug::log(ERR, "Config has errors:\n{}\nProceeding ignoring faulty entries", result.getError()); Debug::log(ERR, "Config has errors:\n{}\nProceeding ignoring faulty entries", result.getError());
#undef SHADOWABLE #undef SHADOWABLE
#undef CLICKABLE
} }
std::vector<CConfigManager::SWidgetConfig> CConfigManager::getWidgetConfigs() { std::vector<CConfigManager::SWidgetConfig> CConfigManager::getWidgetConfigs() {
@ -367,6 +374,8 @@ std::vector<CConfigManager::SWidgetConfig> CConfigManager::getWidgetConfigs() {
"shadow_boost", m_config.getSpecialConfigValue(name, "shadow_boost", k.c_str()) \ "shadow_boost", m_config.getSpecialConfigValue(name, "shadow_boost", k.c_str()) \
} }
#define CLICKABLE(name) {"onclick", m_config.getSpecialConfigValue(name, "onclick", k.c_str())}
// //
auto keys = m_config.listKeysForSpecialCategory("background"); auto keys = m_config.listKeysForSpecialCategory("background");
result.reserve(keys.size()); result.reserve(keys.size());
@ -414,6 +423,7 @@ std::vector<CConfigManager::SWidgetConfig> CConfigManager::getWidgetConfigs() {
{"xray", m_config.getSpecialConfigValue("shape", "xray", k.c_str())}, {"xray", m_config.getSpecialConfigValue("shape", "xray", k.c_str())},
{"zindex", m_config.getSpecialConfigValue("shape", "zindex", k.c_str())}, {"zindex", m_config.getSpecialConfigValue("shape", "zindex", k.c_str())},
SHADOWABLE("shape"), SHADOWABLE("shape"),
CLICKABLE("shape"),
} }
}); });
// clang-format on // clang-format on
@ -440,6 +450,7 @@ std::vector<CConfigManager::SWidgetConfig> CConfigManager::getWidgetConfigs() {
{"reload_cmd", m_config.getSpecialConfigValue("image", "reload_cmd", k.c_str())}, {"reload_cmd", m_config.getSpecialConfigValue("image", "reload_cmd", k.c_str())},
{"zindex", m_config.getSpecialConfigValue("image", "zindex", k.c_str())}, {"zindex", m_config.getSpecialConfigValue("image", "zindex", k.c_str())},
SHADOWABLE("image"), SHADOWABLE("image"),
CLICKABLE("image"),
} }
}); });
// clang-format on // clang-format on
@ -470,11 +481,11 @@ std::vector<CConfigManager::SWidgetConfig> CConfigManager::getWidgetConfigs() {
{"position", m_config.getSpecialConfigValue("input-field", "position", k.c_str())}, {"position", m_config.getSpecialConfigValue("input-field", "position", k.c_str())},
{"placeholder_text", m_config.getSpecialConfigValue("input-field", "placeholder_text", k.c_str())}, {"placeholder_text", m_config.getSpecialConfigValue("input-field", "placeholder_text", k.c_str())},
{"hide_input", m_config.getSpecialConfigValue("input-field", "hide_input", k.c_str())}, {"hide_input", m_config.getSpecialConfigValue("input-field", "hide_input", k.c_str())},
{"hide_input_base_color", m_config.getSpecialConfigValue("input-field", "hide_input_base_color", k.c_str())},
{"rounding", m_config.getSpecialConfigValue("input-field", "rounding", k.c_str())}, {"rounding", m_config.getSpecialConfigValue("input-field", "rounding", k.c_str())},
{"check_color", m_config.getSpecialConfigValue("input-field", "check_color", k.c_str())}, {"check_color", m_config.getSpecialConfigValue("input-field", "check_color", k.c_str())},
{"fail_color", m_config.getSpecialConfigValue("input-field", "fail_color", k.c_str())}, {"fail_color", m_config.getSpecialConfigValue("input-field", "fail_color", k.c_str())},
{"fail_text", m_config.getSpecialConfigValue("input-field", "fail_text", k.c_str())}, {"fail_text", m_config.getSpecialConfigValue("input-field", "fail_text", k.c_str())},
{"fail_timeout", m_config.getSpecialConfigValue("input-field", "fail_timeout", k.c_str())},
{"capslock_color", m_config.getSpecialConfigValue("input-field", "capslock_color", k.c_str())}, {"capslock_color", m_config.getSpecialConfigValue("input-field", "capslock_color", k.c_str())},
{"numlock_color", m_config.getSpecialConfigValue("input-field", "numlock_color", k.c_str())}, {"numlock_color", m_config.getSpecialConfigValue("input-field", "numlock_color", k.c_str())},
{"bothlock_color", m_config.getSpecialConfigValue("input-field", "bothlock_color", k.c_str())}, {"bothlock_color", m_config.getSpecialConfigValue("input-field", "bothlock_color", k.c_str())},
@ -505,6 +516,7 @@ std::vector<CConfigManager::SWidgetConfig> CConfigManager::getWidgetConfigs() {
{"text_align", m_config.getSpecialConfigValue("label", "text_align", k.c_str())}, {"text_align", m_config.getSpecialConfigValue("label", "text_align", k.c_str())},
{"zindex", m_config.getSpecialConfigValue("label", "zindex", k.c_str())}, {"zindex", m_config.getSpecialConfigValue("label", "zindex", k.c_str())},
SHADOWABLE("label"), SHADOWABLE("label"),
CLICKABLE("label"),
} }
}); });
// clang-format on // clang-format on
@ -603,6 +615,11 @@ std::optional<std::string> CConfigManager::handleAnimation(const std::string& co
if (enabledInt > 1 || enabledInt < 0) if (enabledInt > 1 || enabledInt < 0)
return "invalid animation on/off state"; return "invalid animation on/off state";
if (!enabledInt) {
m_AnimationTree.setConfigForNode(ANIMNAME, 0, 1, "default");
return {};
}
int64_t speed = -1; int64_t speed = -1;
// speed // speed

View file

@ -5,7 +5,6 @@
#include <hyprlang.hpp> #include <hyprlang.hpp>
#include <optional> #include <optional>
#include <vector> #include <vector>
#include <memory>
#include <unordered_map> #include <unordered_map>
#include "../defines.hpp" #include "../defines.hpp"
@ -41,4 +40,4 @@ class CConfigManager {
Hyprlang::CConfig m_config; Hyprlang::CConfig m_config;
}; };
inline std::unique_ptr<CConfigManager> g_pConfigManager; inline UP<CConfigManager> g_pConfigManager;

View file

@ -4,7 +4,6 @@
#include <hyprutils/animation/AnimatedVariable.hpp> #include <hyprutils/animation/AnimatedVariable.hpp>
#include "../helpers/AnimatedVariable.hpp" #include "../helpers/AnimatedVariable.hpp"
#include "../helpers/Math.hpp"
#include "../defines.hpp" #include "../defines.hpp"
class CHyprlockAnimationManager : public Hyprutils::Animation::CAnimationManager { class CHyprlockAnimationManager : public Hyprutils::Animation::CAnimationManager {
@ -31,4 +30,4 @@ class CHyprlockAnimationManager : public Hyprutils::Animation::CAnimationManager
bool m_bTickScheduled = false; bool m_bTickScheduled = false;
}; };
inline std::unique_ptr<CHyprlockAnimationManager> g_pAnimationManager; inline UP<CHyprlockAnimationManager> g_pAnimationManager;

View file

@ -9,9 +9,12 @@ CCursorShape::CCursorShape(SP<CCWpCursorShapeManagerV1> mgr) : mgr(mgr) {
} }
void CCursorShape::setShape(const wpCursorShapeDeviceV1Shape shape) { void CCursorShape::setShape(const wpCursorShapeDeviceV1Shape shape) {
if (!dev) if (!g_pSeatManager->m_pPointer)
return; return;
if (!dev)
dev = makeShared<CCWpCursorShapeDeviceV1>(mgr->sendGetPointer(g_pSeatManager->m_pPointer->resource()));
dev->sendSetShape(lastCursorSerial, shape); dev->sendSetShape(lastCursorSerial, shape);
} }

View file

@ -1,10 +1,10 @@
#pragma once #pragma once
#include <memory>
#include <EGL/egl.h> #include <EGL/egl.h>
#include <EGL/eglext.h> #include <EGL/eglext.h>
#include "../defines.hpp"
class CEGL { class CEGL {
public: public:
CEGL(wl_display*); CEGL(wl_display*);
@ -19,4 +19,4 @@ class CEGL {
void makeCurrent(EGLSurface surf); void makeCurrent(EGLSurface surf);
}; };
inline std::unique_ptr<CEGL> g_pEGL; inline UP<CEGL> g_pEGL;

View file

@ -11,13 +11,9 @@ CSessionLockSurface::~CSessionLockSurface() {
wl_egl_window_destroy(eglWindow); wl_egl_window_destroy(eglWindow);
} }
CSessionLockSurface::CSessionLockSurface(COutput* output) : output(output) { CSessionLockSurface::CSessionLockSurface(const SP<COutput>& pOutput) : m_outputRef(pOutput), m_outputID(pOutput->m_ID) {
surface = makeShared<CCWlSurface>(g_pHyprlock->getCompositor()->sendCreateSurface()); surface = makeShared<CCWlSurface>(g_pHyprlock->getCompositor()->sendCreateSurface());
RASSERT(surface, "Couldn't create wl_surface");
if (!surface) {
Debug::log(CRIT, "Couldn't create wl_surface");
exit(1);
}
static const auto FRACTIONALSCALING = g_pConfigManager->getValue<Hyprlang::INT>("general:fractional_scaling"); static const auto FRACTIONALSCALING = g_pConfigManager->getValue<Hyprlang::INT>("general:fractional_scaling");
const auto ENABLE_FSV1 = *FRACTIONALSCALING == 1 || /* auto enable */ (*FRACTIONALSCALING == 2); const auto ENABLE_FSV1 = *FRACTIONALSCALING == 1 || /* auto enable */ (*FRACTIONALSCALING == 2);
@ -45,12 +41,8 @@ CSessionLockSurface::CSessionLockSurface(COutput* output) : output(output) {
if (!PVIEWPORTER) if (!PVIEWPORTER)
Debug::log(LOG, "No viewporter support! Oops, won't be able to scale!"); Debug::log(LOG, "No viewporter support! Oops, won't be able to scale!");
lockSurface = makeShared<CCExtSessionLockSurfaceV1>(g_pHyprlock->getSessionLock()->sendGetLockSurface(surface->resource(), output->output->resource())); lockSurface = makeShared<CCExtSessionLockSurfaceV1>(g_pHyprlock->getSessionLock()->sendGetLockSurface(surface->resource(), pOutput->m_wlOutput->resource()));
RASSERT(lockSurface, "Couldn't create ext_session_lock_surface_v1");
if (!lockSurface) {
Debug::log(CRIT, "Couldn't create ext_session_lock_surface_v1");
exit(1);
}
lockSurface->setConfigure([this](CCExtSessionLockSurfaceV1* r, uint32_t serial, uint32_t width, uint32_t height) { configure({(double)width, (double)height}, serial); }); lockSurface->setConfigure([this](CCExtSessionLockSurfaceV1* r, uint32_t serial, uint32_t width, uint32_t height) { configure({(double)width, (double)height}, serial); });
} }
@ -62,6 +54,8 @@ void CSessionLockSurface::configure(const Vector2D& size_, uint32_t serial_) {
const bool SAMESIZE = logicalSize == size_; const bool SAMESIZE = logicalSize == size_;
const bool SAMESCALE = appliedScale == fractionalScale; const bool SAMESCALE = appliedScale == fractionalScale;
const auto POUTPUT = m_outputRef.lock();
serial = serial_; serial = serial_;
logicalSize = size_; logicalSize = size_;
appliedScale = fractionalScale; appliedScale = fractionalScale;
@ -71,8 +65,8 @@ void CSessionLockSurface::configure(const Vector2D& size_, uint32_t serial_) {
viewport->sendSetDestination(logicalSize.x, logicalSize.y); viewport->sendSetDestination(logicalSize.x, logicalSize.y);
surface->sendSetBufferScale(1); surface->sendSetBufferScale(1);
} else { } else {
size = size_ * output->scale; size = size_ * POUTPUT->scale;
surface->sendSetBufferScale(output->scale); surface->sendSetBufferScale(POUTPUT->scale);
} }
if (!SAMESERIAL) if (!SAMESERIAL)
@ -84,27 +78,18 @@ void CSessionLockSurface::configure(const Vector2D& size_, uint32_t serial_) {
if (!eglWindow) { if (!eglWindow) {
eglWindow = wl_egl_window_create((wl_surface*)surface->resource(), size.x, size.y); eglWindow = wl_egl_window_create((wl_surface*)surface->resource(), size.x, size.y);
if (!eglWindow) { RASSERT(eglWindow, "Couldn't create eglWindow");
Debug::log(CRIT, "Couldn't create eglWindow");
exit(1);
}
} else } else
wl_egl_window_resize(eglWindow, size.x, size.y, 0, 0); wl_egl_window_resize(eglWindow, size.x, size.y, 0, 0);
if (!eglSurface) { if (!eglSurface) {
eglSurface = g_pEGL->eglCreatePlatformWindowSurfaceEXT(g_pEGL->eglDisplay, g_pEGL->eglConfig, eglWindow, nullptr); eglSurface = g_pEGL->eglCreatePlatformWindowSurfaceEXT(g_pEGL->eglDisplay, g_pEGL->eglConfig, eglWindow, nullptr);
if (!eglSurface) { RASSERT(eglSurface, "Couldn't create eglSurface");
Debug::log(CRIT, "Couldn't create eglSurface: {}", eglGetError());
// Clean up resources to prevent leaks
wl_egl_window_destroy(eglWindow);
eglWindow = nullptr;
exit(1); // Consider graceful exit or fallback
}
} }
if (readyForFrame && !(SAMESIZE && SAMESCALE)) { if (readyForFrame && !(SAMESIZE && SAMESCALE)) {
g_pRenderer->removeWidgetsFor(this); Debug::log(LOG, "output {} changed, reloading widgets!", POUTPUT->stringPort);
Debug::log(LOG, "Reloading widgets"); g_pRenderer->reconfigureWidgetsFor(POUTPUT->m_ID);
} }
readyForFrame = true; readyForFrame = true;
@ -129,7 +114,10 @@ void CSessionLockSurface::render() {
if (g_pHyprlock->m_bTerminate) if (g_pHyprlock->m_bTerminate)
return; return;
Debug::log(TRACE, "[{}] frame {}, Current fps: {:.2f}", output->stringPort, m_frames, 1000.f / (frameTime - m_lastFrameTime)); if (Debug::verbose) {
const auto POUTPUT = m_outputRef.lock();
Debug::log(TRACE, "[{}] frame {}, Current fps: {:.2f}", POUTPUT->stringPort, m_frames, 1000.f / (frameTime - m_lastFrameTime));
}
m_lastFrameTime = frameTime; m_lastFrameTime = frameTime;
@ -151,3 +139,7 @@ void CSessionLockSurface::onCallback() {
render(); render();
} }
} }
SP<CCWlSurface> CSessionLockSurface::getWlSurface() {
return surface;
}

View file

@ -14,7 +14,7 @@ class CRenderer;
class CSessionLockSurface { class CSessionLockSurface {
public: public:
CSessionLockSurface(COutput* output); CSessionLockSurface(const SP<COutput>& pOutput);
~CSessionLockSurface(); ~CSessionLockSurface();
void configure(const Vector2D& size, uint32_t serial); void configure(const Vector2D& size, uint32_t serial);
@ -26,9 +26,12 @@ class CSessionLockSurface {
void render(); void render();
void onCallback(); void onCallback();
void onScaleUpdate(); void onScaleUpdate();
SP<CCWlSurface> getWlSurface();
private: private:
COutput* output = nullptr; WP<COutput> m_outputRef;
OUTPUTID m_outputID = OUTPUT_INVALID;
SP<CCWlSurface> surface = nullptr; SP<CCWlSurface> surface = nullptr;
SP<CCExtSessionLockSurfaceV1> lockSurface = nullptr; SP<CCExtSessionLockSurfaceV1> lockSurface = nullptr;
uint32_t serial = 0; uint32_t serial = 0;
@ -49,4 +52,5 @@ class CSessionLockSurface {
SP<CCWlCallback> frameCallback = nullptr; SP<CCWlCallback> frameCallback = nullptr;
friend class CRenderer; friend class CRenderer;
friend class COutput;
}; };

View file

@ -1,32 +1,35 @@
#include "Output.hpp" #include "Output.hpp"
#include "../helpers/Log.hpp" #include "../helpers/Log.hpp"
#include "hyprlock.hpp" #include "hyprlock.hpp"
#include "../renderer/Renderer.hpp"
COutput::COutput(SP<CCWlOutput> output_, uint32_t name_) : name(name_), output(output_) { void COutput::create(WP<COutput> pSelf, SP<CCWlOutput> pWlOutput, uint32_t _name) {
output->setDescription([this](CCWlOutput* r, const char* description) { m_ID = _name;
m_wlOutput = pWlOutput;
m_self = pSelf;
m_wlOutput->setDescription([this](CCWlOutput* r, const char* description) {
stringDesc = description ? std::string{description} : ""; stringDesc = description ? std::string{description} : "";
Debug::log(LOG, "output {} description {}", name, stringDesc); Debug::log(LOG, "output {} description {}", m_ID, stringDesc);
}); });
output->setName([this](CCWlOutput* r, const char* name) { m_wlOutput->setName([this](CCWlOutput* r, const char* name) {
stringName = std::string{name} + stringName; stringName = std::string{name} + stringName;
stringPort = std::string{name}; stringPort = std::string{name};
Debug::log(LOG, "output {} name {}", name, name); Debug::log(LOG, "output {} name {}", name, name);
}); });
output->setScale([this](CCWlOutput* r, int32_t sc) { scale = sc; }); m_wlOutput->setScale([this](CCWlOutput* r, int32_t sc) { scale = sc; });
output->setDone([this](CCWlOutput* r) { m_wlOutput->setDone([this](CCWlOutput* r) {
done = true; done = true;
Debug::log(LOG, "output {} done", name); Debug::log(LOG, "output {} done", m_ID);
if (g_pHyprlock->m_lockAquired && !sessionLockSurface) { if (g_pHyprlock->m_lockAquired && !m_sessionLockSurface) {
Debug::log(LOG, "output {} creating a new lock surface", name); Debug::log(LOG, "output {} creating a new lock surface", m_ID);
sessionLockSurface = std::make_unique<CSessionLockSurface>(this); createSessionLockSurface();
} }
}); });
output->setMode([this](CCWlOutput* r, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { m_wlOutput->setMode([this](CCWlOutput* r, uint32_t flags, int32_t width, int32_t height, int32_t refresh) {
// handle portrait mode and flipped cases // handle portrait mode and flipped cases
if (transform % 2 == 1) if (transform % 2 == 1)
size = {height, width}; size = {height, width};
@ -34,10 +37,33 @@ COutput::COutput(SP<CCWlOutput> output_, uint32_t name_) : name(name_), output(o
size = {width, height}; size = {width, height};
}); });
output->setGeometry( m_wlOutput->setGeometry(
[this](CCWlOutput* r, int32_t x, int32_t y, int32_t physical_width, int32_t physical_height, int32_t subpixel, const char* make, const char* model, int32_t transform_) { [this](CCWlOutput* r, int32_t x, int32_t y, int32_t physical_width, int32_t physical_height, int32_t subpixel, const char* make, const char* model, int32_t transform_) {
transform = (wl_output_transform)transform_; transform = (wl_output_transform)transform_;
Debug::log(LOG, "output {} make {} model {}", name, make ? make : "", model ? model : ""); Debug::log(LOG, "output {} make {} model {}", m_ID, make ? make : "", model ? model : "");
}); });
} }
void COutput::createSessionLockSurface() {
if (!m_self.valid()) {
Debug::log(ERR, "output {} dead??", m_ID);
return;
}
if (m_sessionLockSurface) {
Debug::log(ERR, "output {} already has a session lock surface", m_ID);
return;
}
if (size == Vector2D{0, 0}) {
Debug::log(WARN, "output {} refusing to create a lock surface with size 0x0", m_ID);
return;
}
m_sessionLockSurface = makeUnique<CSessionLockSurface>(m_self.lock());
}
Vector2D COutput::getViewport() const {
return (m_sessionLockSurface) ? m_sessionLockSurface->size : size;
}

View file

@ -2,15 +2,16 @@
#include "../defines.hpp" #include "../defines.hpp"
#include "wayland.hpp" #include "wayland.hpp"
#include "../helpers/Math.hpp"
#include "LockSurface.hpp" #include "LockSurface.hpp"
#include <memory>
class COutput { class COutput {
public: public:
COutput(SP<CCWlOutput> output, uint32_t name); COutput() = default;
~COutput() = default;
uint32_t name = 0; void create(WP<COutput> pSelf, SP<CCWlOutput> pWlOutput, uint32_t name);
OUTPUTID m_ID = 0;
bool focused = false; bool focused = false;
bool done = false; bool done = false;
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
@ -20,9 +21,13 @@ class COutput {
std::string stringPort = ""; std::string stringPort = "";
std::string stringDesc = ""; std::string stringDesc = "";
std::unique_ptr<CSessionLockSurface> sessionLockSurface; UP<CSessionLockSurface> m_sessionLockSurface;
SP<CCWlOutput> output = nullptr; SP<CCWlOutput> m_wlOutput = nullptr;
private: WP<COutput> m_self;
void createSessionLockSurface();
Vector2D getViewport() const;
}; };

View file

@ -26,7 +26,13 @@ void CSeatManager::registerSeat(SP<CCWlSeat> seat) {
if (caps & WL_SEAT_CAPABILITY_POINTER) { if (caps & WL_SEAT_CAPABILITY_POINTER) {
m_pPointer = makeShared<CCWlPointer>(r->sendGetPointer()); m_pPointer = makeShared<CCWlPointer>(r->sendGetPointer());
static const auto HIDECURSOR = g_pConfigManager->getValue<Hyprlang::INT>("general:hide_cursor");
m_pPointer->setMotion([](CCWlPointer* r, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { m_pPointer->setMotion([](CCWlPointer* r, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
g_pHyprlock->m_vMouseLocation = {wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y)};
if (!*HIDECURSOR)
g_pHyprlock->onHover(g_pHyprlock->m_vMouseLocation);
if (std::chrono::system_clock::now() > g_pHyprlock->m_tGraceEnds) if (std::chrono::system_clock::now() > g_pHyprlock->m_tGraceEnds)
return; return;
@ -40,16 +46,34 @@ void CSeatManager::registerSeat(SP<CCWlSeat> seat) {
if (!m_pCursorShape) if (!m_pCursorShape)
return; return;
static const auto HIDE = g_pConfigManager->getValue<Hyprlang::INT>("general:hide_cursor");
m_pCursorShape->lastCursorSerial = serial; m_pCursorShape->lastCursorSerial = serial;
if (*HIDE) if (*HIDECURSOR)
m_pCursorShape->hideCursor(); m_pCursorShape->hideCursor();
else else
m_pCursorShape->setShape(wpCursorShapeDeviceV1Shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT); m_pCursorShape->setShape(wpCursorShapeDeviceV1Shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT);
g_pHyprlock->m_vLastEnterCoords = {wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y)}; g_pHyprlock->m_vLastEnterCoords = {wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y)};
if (*HIDECURSOR)
return;
for (const auto& POUTPUT : g_pHyprlock->m_vOutputs) {
if (!POUTPUT->m_sessionLockSurface)
continue;
const auto& PWLSURFACE = POUTPUT->m_sessionLockSurface->getWlSurface();
if (PWLSURFACE->resource() == surf)
g_pHyprlock->m_focusedOutput = POUTPUT;
}
});
m_pPointer->setLeave([](CCWlPointer* r, uint32_t serial, wl_proxy* surf) { g_pHyprlock->m_focusedOutput.reset(); });
m_pPointer->setButton([](CCWlPointer* r, uint32_t serial, uint32_t time, uint32_t button, wl_pointer_button_state state) {
if (*HIDECURSOR)
return;
g_pHyprlock->onClick(button, state == WL_POINTER_BUTTON_STATE_PRESSED, g_pHyprlock->m_vMouseLocation);
}); });
} }
@ -131,7 +155,7 @@ void CSeatManager::registerSeat(SP<CCWlSeat> seat) {
} }
void CSeatManager::registerCursorShape(SP<CCWpCursorShapeManagerV1> shape) { void CSeatManager::registerCursorShape(SP<CCWpCursorShapeManagerV1> shape) {
m_pCursorShape = std::make_unique<CCursorShape>(shape); m_pCursorShape = makeUnique<CCursorShape>(shape);
} }
bool CSeatManager::registered() { bool CSeatManager::registered() {

View file

@ -5,7 +5,6 @@
#include "wayland.hpp" #include "wayland.hpp"
#include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-compose.h> #include <xkbcommon/xkbcommon-compose.h>
#include <memory>
class CSeatManager { class CSeatManager {
public: public:
@ -19,7 +18,7 @@ class CSeatManager {
SP<CCWlKeyboard> m_pKeeb; SP<CCWlKeyboard> m_pKeeb;
SP<CCWlPointer> m_pPointer; SP<CCWlPointer> m_pPointer;
std::unique_ptr<CCursorShape> m_pCursorShape; UP<CCursorShape> m_pCursorShape;
xkb_context* m_pXKBContext = nullptr; xkb_context* m_pXKBContext = nullptr;
xkb_keymap* m_pXKBKeymap = nullptr; xkb_keymap* m_pXKBKeymap = nullptr;
@ -30,4 +29,4 @@ class CSeatManager {
SP<CCWlSeat> m_pSeat; SP<CCWlSeat> m_pSeat;
}; };
inline std::unique_ptr<CSeatManager> g_pSeatManager = std::make_unique<CSeatManager>(); inline UP<CSeatManager> g_pSeatManager = makeUnique<CSeatManager>();

View file

@ -6,6 +6,7 @@
#include "../auth/Auth.hpp" #include "../auth/Auth.hpp"
#include "../auth/Fingerprint.hpp" #include "../auth/Fingerprint.hpp"
#include "Egl.hpp" #include "Egl.hpp"
#include <hyprutils/memory/UniquePtr.hpp>
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/poll.h> #include <sys/poll.h>
#include <sys/mman.h> #include <sys/mman.h>
@ -38,12 +39,9 @@ CHyprlock::CHyprlock(const std::string& wlDisplay, const bool immediate, const b
setMallocThreshold(); setMallocThreshold();
m_sWaylandState.display = wl_display_connect(wlDisplay.empty() ? nullptr : wlDisplay.c_str()); m_sWaylandState.display = wl_display_connect(wlDisplay.empty() ? nullptr : wlDisplay.c_str());
if (!m_sWaylandState.display) { RASSERT(m_sWaylandState.display, "Couldn't connect to a wayland compositor");
Debug::log(CRIT, "Couldn't connect to a wayland compositor");
exit(1);
}
g_pEGL = std::make_unique<CEGL>(m_sWaylandState.display); g_pEGL = makeUnique<CEGL>(m_sWaylandState.display);
if (!immediate) { if (!immediate) {
static const auto GRACE = g_pConfigManager->getValue<Hyprlang::INT>("general:grace"); static const auto GRACE = g_pConfigManager->getValue<Hyprlang::INT>("general:grace");
@ -75,7 +73,7 @@ static void registerSignalAction(int sig, void (*handler)(int), int sa_flags = 0
static void handleUnlockSignal(int sig) { static void handleUnlockSignal(int sig) {
if (sig == SIGUSR1) { if (sig == SIGUSR1) {
Debug::log(LOG, "Unlocking with a SIGUSR1"); Debug::log(LOG, "Unlocking with a SIGUSR1");
g_pHyprlock->releaseSessionLock(); g_pAuth->enqueueUnlock();
} }
} }
@ -94,20 +92,6 @@ static void handlePollTerminate(int sig) {
; ;
} }
static void handleCriticalSignal(int sig) {
g_pHyprlock->attemptRestoreOnDeath();
// remove our handlers
struct sigaction sa;
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGABRT, &sa, nullptr);
sigaction(SIGSEGV, &sa, nullptr);
abort();
}
static char* gbm_find_render_node(drmDevice* device) { static char* gbm_find_render_node(drmDevice* device) {
drmDevice* devices[64]; drmDevice* devices[64];
char* render_node = nullptr; char* render_node = nullptr;
@ -243,10 +227,7 @@ void CHyprlock::addDmabufListener() {
memcpy(&device, device_arr->data, sizeof(device)); memcpy(&device, device_arr->data, sizeof(device));
drmDevice* drmDev; drmDevice* drmDev;
if (drmGetDeviceFromDevId(device, /* flags */ 0, &drmDev) != 0) { RASSERT(drmGetDeviceFromDevId(device, /* flags */ 0, &drmDev) == 0, "unable to open main device?");
Debug::log(WARN, "[dmabuf] unable to open main device?");
exit(1);
}
dma.gbmDevice = createGBMDevice(drmDev); dma.gbmDevice = createGBMDevice(drmDev);
drmFreeDevice(&drmDev); drmFreeDevice(&drmDev);
@ -283,10 +264,11 @@ void CHyprlock::run() {
} else if (IFACE == ext_session_lock_manager_v1_interface.name) } else if (IFACE == ext_session_lock_manager_v1_interface.name)
m_sWaylandState.sessionLock = m_sWaylandState.sessionLock =
makeShared<CCExtSessionLockManagerV1>((wl_proxy*)wl_registry_bind((wl_registry*)r->resource(), name, &ext_session_lock_manager_v1_interface, 1)); makeShared<CCExtSessionLockManagerV1>((wl_proxy*)wl_registry_bind((wl_registry*)r->resource(), name, &ext_session_lock_manager_v1_interface, 1));
else if (IFACE == wl_output_interface.name) else if (IFACE == wl_output_interface.name) {
m_vOutputs.emplace_back( const auto POUTPUT = makeShared<COutput>();
std::make_unique<COutput>(makeShared<CCWlOutput>((wl_proxy*)wl_registry_bind((wl_registry*)r->resource(), name, &wl_output_interface, 4)), name)); POUTPUT->create(POUTPUT, makeShared<CCWlOutput>((wl_proxy*)wl_registry_bind((wl_registry*)r->resource(), name, &wl_output_interface, 4)), name);
else if (IFACE == wp_cursor_shape_manager_v1_interface.name) m_vOutputs.emplace_back(POUTPUT);
} else if (IFACE == wp_cursor_shape_manager_v1_interface.name)
g_pSeatManager->registerCursorShape( g_pSeatManager->registerCursorShape(
makeShared<CCWpCursorShapeManagerV1>((wl_proxy*)wl_registry_bind((wl_registry*)r->resource(), name, &wp_cursor_shape_manager_v1_interface, 1))); makeShared<CCWpCursorShapeManagerV1>((wl_proxy*)wl_registry_bind((wl_registry*)r->resource(), name, &wp_cursor_shape_manager_v1_interface, 1)));
else if (IFACE == wl_compositor_interface.name) else if (IFACE == wl_compositor_interface.name)
@ -308,9 +290,9 @@ void CHyprlock::run() {
}); });
m_sWaylandState.registry->setGlobalRemove([this](CCWlRegistry* r, uint32_t name) { m_sWaylandState.registry->setGlobalRemove([this](CCWlRegistry* r, uint32_t name) {
Debug::log(LOG, " | removed iface {}", name); Debug::log(LOG, " | removed iface {}", name);
auto outputIt = std::ranges::find_if(m_vOutputs, [name](const auto& other) { return other->name == name; }); auto outputIt = std::ranges::find_if(m_vOutputs, [id = name](const auto& other) { return other->m_ID == id; });
if (outputIt != m_vOutputs.end()) { if (outputIt != m_vOutputs.end()) {
g_pRenderer->removeWidgetsFor(outputIt->get()->sessionLockSurface.get()); g_pRenderer->removeWidgetsFor((*outputIt)->m_ID);
m_vOutputs.erase(outputIt); m_vOutputs.erase(outputIt);
} }
}); });
@ -325,8 +307,8 @@ void CHyprlock::run() {
// gather info about monitors // gather info about monitors
wl_display_roundtrip(m_sWaylandState.display); wl_display_roundtrip(m_sWaylandState.display);
g_pRenderer = std::make_unique<CRenderer>(); g_pRenderer = makeUnique<CRenderer>();
g_pAuth = std::make_unique<CAuth>(); g_pAuth = makeUnique<CAuth>();
g_pAuth->start(); g_pAuth->start();
Debug::log(LOG, "Running on {}", m_sCurrentDesktop); Debug::log(LOG, "Running on {}", m_sCurrentDesktop);
@ -361,8 +343,6 @@ void CHyprlock::run() {
registerSignalAction(SIGUSR1, handleUnlockSignal, SA_RESTART); registerSignalAction(SIGUSR1, handleUnlockSignal, SA_RESTART);
registerSignalAction(SIGUSR2, handleForceUpdateSignal); registerSignalAction(SIGUSR2, handleForceUpdateSignal);
registerSignalAction(SIGRTMIN, handlePollTerminate); registerSignalAction(SIGRTMIN, handlePollTerminate);
registerSignalAction(SIGSEGV, handleCriticalSignal);
registerSignalAction(SIGABRT, handleCriticalSignal);
pollfd pollfds[2]; pollfd pollfds[2];
pollfds[0] = { pollfds[0] = {
@ -386,24 +366,13 @@ void CHyprlock::run() {
events = poll(pollfds, fdcount, 5000); events = poll(pollfds, fdcount, 5000);
if (events < 0) { if (events < 0) {
RASSERT(errno == EINTR, "[core] Polling fds failed with {}", errno);
wl_display_cancel_read(m_sWaylandState.display); wl_display_cancel_read(m_sWaylandState.display);
if (errno == EINTR)
continue; continue;
Debug::log(CRIT, "[core] Polling fds failed with {}", errno);
attemptRestoreOnDeath();
m_bTerminate = true;
exit(1);
} }
for (size_t i = 0; i < fdcount; ++i) { for (size_t i = 0; i < fdcount; ++i) {
if (pollfds[i].revents & POLLHUP) { RASSERT(!(pollfds[i].revents & POLLHUP), "[core] Disconnected from pollfd id {}", i);
Debug::log(CRIT, "[core] Disconnected from pollfd id {}", i);
attemptRestoreOnDeath();
m_bTerminate = true;
exit(1);
}
} }
wl_display_read_events(m_sWaylandState.display); wl_display_read_events(m_sWaylandState.display);
@ -554,23 +523,23 @@ void CHyprlock::clearPasswordBuffer() {
void CHyprlock::renderOutput(const std::string& stringPort) { void CHyprlock::renderOutput(const std::string& stringPort) {
const auto MON = std::ranges::find_if(m_vOutputs, [stringPort](const auto& other) { return other->stringPort == stringPort; }); const auto MON = std::ranges::find_if(m_vOutputs, [stringPort](const auto& other) { return other->stringPort == stringPort; });
if (MON == m_vOutputs.end() || !MON->get()) if (MON == m_vOutputs.end() || !*MON)
return; return;
const auto PMONITOR = MON->get(); const auto& PMONITOR = *MON;
if (!PMONITOR->sessionLockSurface) if (!PMONITOR->m_sessionLockSurface)
return; return;
PMONITOR->sessionLockSurface->render(); PMONITOR->m_sessionLockSurface->render();
} }
void CHyprlock::renderAllOutputs() { void CHyprlock::renderAllOutputs() {
for (auto& o : m_vOutputs) { for (auto& o : m_vOutputs) {
if (!o->sessionLockSurface) if (!o->m_sessionLockSurface)
continue; continue;
o->sessionLockSurface->render(); o->m_sessionLockSurface->render();
} }
} }
@ -634,6 +603,9 @@ void CHyprlock::onKey(uint32_t key, bool down) {
return; return;
} }
if (g_pAuth->m_bDisplayFailText)
g_pAuth->resetDisplayFail();
if (down) { if (down) {
m_bCapsLock = xkb_state_mod_name_is_active(g_pSeatManager->m_pXKBState, XKB_MOD_NAME_CAPS, XKB_STATE_MODS_LOCKED); m_bCapsLock = xkb_state_mod_name_is_active(g_pSeatManager->m_pXKBState, XKB_MOD_NAME_CAPS, XKB_STATE_MODS_LOCKED);
m_bNumLock = xkb_state_mod_name_is_active(g_pSeatManager->m_pXKBState, XKB_MOD_NAME_NUM, XKB_STATE_MODS_LOCKED); m_bNumLock = xkb_state_mod_name_is_active(g_pSeatManager->m_pXKBState, XKB_MOD_NAME_NUM, XKB_STATE_MODS_LOCKED);
@ -697,6 +669,61 @@ void CHyprlock::handleKeySym(xkb_keysym_t sym, bool composed) {
} }
} }
void CHyprlock::onClick(uint32_t button, bool down, const Vector2D& pos) {
if (!m_focusedOutput.lock())
return;
// TODO: add the UNLIKELY marco from Hyprland
if (!m_focusedOutput->m_sessionLockSurface)
return;
const auto SCALEDPOS = pos * m_focusedOutput->m_sessionLockSurface->fractionalScale;
const auto widgets = g_pRenderer->getOrCreateWidgetsFor(*m_focusedOutput->m_sessionLockSurface);
for (const auto& widget : widgets) {
if (widget->containsPoint(SCALEDPOS))
widget->onClick(button, down, pos);
}
}
void CHyprlock::onHover(const Vector2D& pos) {
if (!m_focusedOutput.lock())
return;
if (!m_focusedOutput->m_sessionLockSurface)
return;
bool outputNeedsRedraw = false;
bool cursorChanged = false;
const auto SCALEDPOS = pos * m_focusedOutput->m_sessionLockSurface->fractionalScale;
const auto widgets = g_pRenderer->getOrCreateWidgetsFor(*m_focusedOutput->m_sessionLockSurface);
for (const auto& widget : widgets) {
const bool CONTAINSPOINT = widget->containsPoint(SCALEDPOS);
const bool HOVERED = widget->isHovered();
if (CONTAINSPOINT) {
if (!HOVERED) {
widget->setHover(true);
widget->onHover(pos);
outputNeedsRedraw = true;
}
if (!cursorChanged)
cursorChanged = true;
} else if (HOVERED) {
widget->setHover(false);
outputNeedsRedraw = true;
}
}
if (!cursorChanged)
g_pSeatManager->m_pCursorShape->setShape(WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT);
if (outputNeedsRedraw)
m_focusedOutput->m_sessionLockSurface->render();
}
bool CHyprlock::acquireSessionLock() { bool CHyprlock::acquireSessionLock() {
Debug::log(LOG, "Locking session"); Debug::log(LOG, "Locking session");
m_sLockState.lock = makeShared<CCExtSessionLockV1>(m_sWaylandState.sessionLock->sendLock()); m_sLockState.lock = makeShared<CCExtSessionLockV1>(m_sWaylandState.sessionLock->sendLock());
@ -723,7 +750,7 @@ bool CHyprlock::acquireSessionLock() {
if (!o->done) if (!o->done)
continue; continue;
o->sessionLockSurface = std::make_unique<CSessionLockSurface>(o.get()); o->createSessionLockSurface();
} }
return true; return true;
@ -845,19 +872,6 @@ void CHyprlock::enqueueForceUpdateTimers() {
nullptr, false); nullptr, false);
} }
std::string CHyprlock::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();
}
SP<CCZwlrScreencopyManagerV1> CHyprlock::getScreencopy() { SP<CCZwlrScreencopyManagerV1> CHyprlock::getScreencopy() {
return m_sWaylandState.screencopy; return m_sWaylandState.screencopy;
} }
@ -865,53 +879,3 @@ SP<CCZwlrScreencopyManagerV1> CHyprlock::getScreencopy() {
SP<CCWlShm> CHyprlock::getShm() { SP<CCWlShm> CHyprlock::getShm() {
return m_sWaylandState.shm; return m_sWaylandState.shm;
} }
void CHyprlock::attemptRestoreOnDeath() {
if (m_bTerminate || m_sCurrentDesktop != "Hyprland")
return;
const auto XDG_RUNTIME_DIR = getenv("XDG_RUNTIME_DIR");
const auto HIS = getenv("HYPRLAND_INSTANCE_SIGNATURE");
if (!XDG_RUNTIME_DIR || !HIS)
return;
// dirty hack
uint64_t timeNowMs = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - std::chrono::system_clock::from_time_t(0)).count();
const auto LASTRESTARTPATH = std::string{XDG_RUNTIME_DIR} + "/.hyprlockrestart";
if (std::filesystem::exists(LASTRESTARTPATH)) {
std::ifstream ifs(LASTRESTARTPATH);
std::string content((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
uint64_t timeEncoded = 0;
try {
timeEncoded = std::stoull(content);
} catch (std::exception& e) {
// oops?
ifs.close();
std::filesystem::remove(LASTRESTARTPATH);
return;
}
ifs.close();
if (timeNowMs - timeEncoded < 4000 /* 4s, seems reasonable */) {
Debug::log(LOG, "Not restoring on death; less than 4s since last death");
return;
}
}
std::ofstream ofs(LASTRESTARTPATH, std::ios::trunc);
ofs << timeNowMs;
ofs.close();
if (m_bLocked && m_sLockState.lock) {
m_sLockState.lock.reset();
// Destroy sessionLockSurfaces
m_vOutputs.clear();
}
spawnSync("hyprctl keyword misc:allow_session_lock_restore true");
spawnSync("hyprctl dispatch exec \"hyprlock --immediate --immediate-render\"");
}

View file

@ -37,9 +37,6 @@ class CHyprlock {
void unlock(); void unlock();
bool isUnlocked(); bool isUnlocked();
void onGlobal(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version);
void onGlobalRemoved(void* data, struct wl_registry* registry, uint32_t name);
std::shared_ptr<CTimer> addTimer(const std::chrono::system_clock::duration& timeout, std::function<void(std::shared_ptr<CTimer> self, void* data)> cb_, void* data, std::shared_ptr<CTimer> addTimer(const std::chrono::system_clock::duration& timeout, std::function<void(std::shared_ptr<CTimer> self, void* data)> cb_, void* data,
bool force = false); bool force = false);
@ -51,11 +48,9 @@ class CHyprlock {
bool acquireSessionLock(); bool acquireSessionLock();
void releaseSessionLock(); void releaseSessionLock();
void attemptRestoreOnDeath();
std::string spawnSync(const std::string& cmd);
void onKey(uint32_t key, bool down); void onKey(uint32_t key, bool down);
void onClick(uint32_t button, bool down, const Vector2D& pos);
void onHover(const Vector2D& pos);
void startKeyRepeat(xkb_keysym_t sym); void startKeyRepeat(xkb_keysym_t sym);
void repeatKey(xkb_keysym_t sym); void repeatKey(xkb_keysym_t sym);
void handleKeySym(xkb_keysym_t sym, bool compose); void handleKeySym(xkb_keysym_t sym, bool compose);
@ -100,10 +95,13 @@ class CHyprlock {
// //
std::chrono::system_clock::time_point m_tGraceEnds; std::chrono::system_clock::time_point m_tGraceEnds;
Vector2D m_vLastEnterCoords = {}; Vector2D m_vLastEnterCoords = {};
WP<COutput> m_focusedOutput;
Vector2D m_vMouseLocation = {};
std::shared_ptr<CTimer> m_pKeyRepeatTimer = nullptr; std::shared_ptr<CTimer> m_pKeyRepeatTimer = nullptr;
std::vector<std::unique_ptr<COutput>> m_vOutputs; std::vector<SP<COutput>> m_vOutputs;
std::vector<std::shared_ptr<CTimer>> getTimers(); std::vector<std::shared_ptr<CTimer>> getTimers();
struct { struct {
@ -167,4 +165,4 @@ class CHyprlock {
std::vector<uint32_t> m_vPressedKeys; std::vector<uint32_t> m_vPressedKeys;
}; };
inline std::unique_ptr<CHyprlock> g_pHyprlock; inline UP<CHyprlock> g_pHyprlock;

View file

@ -1,8 +1,14 @@
#pragma once #pragma once
#include <hyprutils/memory/WeakPtr.hpp> #include <hyprutils/memory/WeakPtr.hpp>
#include <hyprutils/memory/UniquePtr.hpp>
#include <hyprgraphics/color/Color.hpp> #include <hyprgraphics/color/Color.hpp>
using namespace Hyprutils::Memory; using namespace Hyprutils::Memory;
using namespace Hyprgraphics; using namespace Hyprgraphics;
#define SP CSharedPointer #define SP CSharedPointer
#define WP CWeakPointer #define WP CWeakPointer
#define UP CUniquePointer
typedef int64_t OUTPUTID;
constexpr OUTPUTID OUTPUT_INVALID = -1;

View file

@ -5,9 +5,11 @@
#include "MiscFunctions.hpp" #include "MiscFunctions.hpp"
#include "Log.hpp" #include "Log.hpp"
#include <hyprutils/string/String.hpp> #include <hyprutils/string/String.hpp>
#include <hyprutils/os/Process.hpp>
#include <unistd.h> #include <unistd.h>
using namespace Hyprutils::String; using namespace Hyprutils::String;
using namespace Hyprutils::OS;
std::string absolutePath(const std::string& rawpath, const std::string& currentDir) { std::string absolutePath(const std::string& rawpath, const std::string& currentDir) {
std::filesystem::path path(rawpath); std::filesystem::path path(rawpath);
@ -137,3 +139,22 @@ int createPoolFile(size_t size, std::string& name) {
return FD; 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);
}

View file

@ -7,3 +7,5 @@
std::string absolutePath(const std::string&, const std::string&); std::string absolutePath(const std::string&, const std::string&);
int64_t configStringToInt(const std::string& VALUE); int64_t configStringToInt(const std::string& VALUE);
int createPoolFile(size_t size, std::string& name); int createPoolFile(size_t size, std::string& name);
std::string spawnSync(const std::string& cmd);
void spawnAsync(const std::string& cmd);

View file

@ -29,6 +29,14 @@ std::optional<std::string> parseArg(const std::vector<std::string>& args, const
} }
} }
static void printVersion() {
constexpr bool ISTAGGEDRELEASE = std::string_view(HYPRLOCK_COMMIT) == HYPRLOCK_VERSION_COMMIT;
if (ISTAGGEDRELEASE)
std::println("Hyprlock version v{}", HYPRLOCK_VERSION);
else
std::println("Hyprlock version v{} (commit {})", HYPRLOCK_VERSION, HYPRLOCK_COMMIT);
}
int main(int argc, char** argv, char** envp) { int main(int argc, char** argv, char** envp) {
std::string configPath; std::string configPath;
std::string wlDisplay; std::string wlDisplay;
@ -47,12 +55,7 @@ int main(int argc, char** argv, char** envp) {
} }
if (arg == "--version" || arg == "-V") { if (arg == "--version" || arg == "-V") {
constexpr bool ISTAGGEDRELEASE = std::string_view(HYPRLOCK_COMMIT) == HYPRLOCK_VERSION_COMMIT; printVersion();
if (ISTAGGEDRELEASE)
std::println("Hyprlock version v{}", HYPRLOCK_VERSION);
else
std::println("Hyprlock version v{} (commit {})", HYPRLOCK_VERSION, HYPRLOCK_COMMIT);
return 0; return 0;
} }
@ -90,10 +93,11 @@ int main(int argc, char** argv, char** envp) {
} }
} }
g_pAnimationManager = std::make_unique<CHyprlockAnimationManager>(); printVersion();
g_pAnimationManager = makeUnique<CHyprlockAnimationManager>();
try { try {
g_pConfigManager = std::make_unique<CConfigManager>(configPath); g_pConfigManager = makeUnique<CConfigManager>(configPath);
g_pConfigManager->init(); g_pConfigManager->init();
} catch (const std::exception& ex) { } catch (const std::exception& ex) {
Debug::log(CRIT, "ConfigManager threw: {}", ex.what()); Debug::log(CRIT, "ConfigManager threw: {}", ex.what());
@ -103,11 +107,11 @@ int main(int argc, char** argv, char** envp) {
return 1; return 1;
} }
if (noFadeIn) if (noFadeIn || immediate)
g_pConfigManager->m_AnimationTree.setConfigForNode("fadeIn", false, 0.f, "default"); g_pConfigManager->m_AnimationTree.setConfigForNode("fadeIn", false, 0.f, "default");
try { try {
g_pHyprlock = std::make_unique<CHyprlock>(wlDisplay, immediate, immediateRender); g_pHyprlock = makeUnique<CHyprlock>(wlDisplay, immediate, immediateRender);
g_pHyprlock->run(); g_pHyprlock->run();
} catch (const std::exception& ex) { } catch (const std::exception& ex) {
Debug::log(CRIT, "Hyprlock threw: {}", ex.what()); Debug::log(CRIT, "Hyprlock threw: {}", ex.what());

View file

@ -2,7 +2,6 @@
#include "../config/ConfigManager.hpp" #include "../config/ConfigManager.hpp"
#include "../core/Egl.hpp" #include "../core/Egl.hpp"
#include <cairo/cairo.h> #include <cairo/cairo.h>
#include <magic.h>
#include <pango/pangocairo.h> #include <pango/pangocairo.h>
#include <algorithm> #include <algorithm>
#include <filesystem> #include <filesystem>
@ -48,15 +47,12 @@ void CAsyncResourceGatherer::enqueueScreencopyFrames() {
} }
for (auto& mon : mons) { for (auto& mon : mons) {
const auto MON = std::find_if(g_pHyprlock->m_vOutputs.begin(), g_pHyprlock->m_vOutputs.end(), const auto MON = std::ranges::find_if(g_pHyprlock->m_vOutputs, [mon](const auto& other) { return other->stringPort == mon || other->stringDesc.starts_with(mon); });
[mon](const auto& other) { return other->stringPort == mon || other->stringDesc.starts_with(mon); });
if (MON == g_pHyprlock->m_vOutputs.end()) if (MON == g_pHyprlock->m_vOutputs.end())
continue; continue;
const auto PMONITOR = MON->get(); scframes.emplace_back(makeUnique<CScreencopyFrame>(*MON));
scframes.emplace_back(std::make_unique<CScreencopyFrame>(PMONITOR));
} }
} }
@ -221,7 +217,7 @@ void CAsyncResourceGatherer::renderText(const SPreloadRequest& rq) {
const bool ISCMD = rq.props.contains("cmd") ? std::any_cast<bool>(rq.props.at("cmd")) : false; const bool ISCMD = rq.props.contains("cmd") ? std::any_cast<bool>(rq.props.at("cmd")) : false;
static const auto TRIM = g_pConfigManager->getValue<Hyprlang::INT>("general:text_trim"); static const auto TRIM = g_pConfigManager->getValue<Hyprlang::INT>("general:text_trim");
std::string text = ISCMD ? g_pHyprlock->spawnSync(rq.asset) : rq.asset; std::string text = ISCMD ? spawnSync(rq.asset) : rq.asset;
if (*TRIM) { if (*TRIM) {
text.erase(0, text.find_first_not_of(" \n\r\t")); text.erase(0, text.find_first_not_of(" \n\r\t"));
@ -304,17 +300,6 @@ void CAsyncResourceGatherer::renderText(const SPreloadRequest& rq) {
preloadTargets.push_back(target); preloadTargets.push_back(target);
} }
struct STimerCallbackData {
void (*cb)(void*) = nullptr;
void* data = nullptr;
};
static void timerCallback(std::shared_ptr<CTimer> self, void* data_) {
auto data = (STimerCallbackData*)data_;
data->cb(data->data);
delete data;
}
void CAsyncResourceGatherer::asyncAssetSpinLock() { void CAsyncResourceGatherer::asyncAssetSpinLock() {
while (!g_pHyprlock->m_bTerminate) { while (!g_pHyprlock->m_bTerminate) {
@ -349,7 +334,7 @@ void CAsyncResourceGatherer::asyncAssetSpinLock() {
// plant timer for callback // plant timer for callback
if (r.callback) if (r.callback)
g_pHyprlock->addTimer(std::chrono::milliseconds(0), timerCallback, new STimerCallbackData{.cb = r.callback, .data = r.callbackData}); g_pHyprlock->addTimer(std::chrono::milliseconds(0), [cb = r.callback](auto, auto) { cb(); }, nullptr);
} }
} }
} }

View file

@ -37,8 +37,7 @@ class CAsyncResourceGatherer {
// optional. Callbacks will be dispatched from the main thread, // optional. Callbacks will be dispatched from the main thread,
// so wayland/gl calls are OK. // so wayland/gl calls are OK.
// will fire once the resource is fully loaded and ready. // will fire once the resource is fully loaded and ready.
void (*callback)(void*) = nullptr; std::function<void()> callback = nullptr;
void* callbackData = nullptr;
}; };
void requestAsyncAssetPreload(const SPreloadRequest& request); void requestAsyncAssetPreload(const SPreloadRequest& request);
@ -75,7 +74,7 @@ class CAsyncResourceGatherer {
Vector2D size; Vector2D size;
}; };
std::vector<std::unique_ptr<CScreencopyFrame>> scframes; std::vector<UP<CScreencopyFrame>> scframes;
std::vector<SPreloadTarget> preloadTargets; std::vector<SPreloadTarget> preloadTargets;
std::mutex preloadTargetsMutex; std::mutex preloadTargetsMutex;

View file

@ -117,6 +117,6 @@ CFramebuffer::~CFramebuffer() {
release(); release();
} }
bool CFramebuffer::isAllocated() { bool CFramebuffer::isAllocated() const {
return m_iFb != (GLuint)-1; return m_iFb != (GLuint)-1;
} }

View file

@ -13,7 +13,7 @@ class CFramebuffer {
void bind() const; void bind() const;
void release(); void release();
void reset(); void reset();
bool isAllocated(); bool isAllocated() const;
Vector2D m_vSize; Vector2D m_vSize;

View file

@ -194,15 +194,13 @@ CRenderer::CRenderer() {
borderShader.gradientLerp = glGetUniformLocation(prog, "gradientLerp"); borderShader.gradientLerp = glGetUniformLocation(prog, "gradientLerp");
borderShader.alpha = glGetUniformLocation(prog, "alpha"); borderShader.alpha = glGetUniformLocation(prog, "alpha");
asyncResourceGatherer = std::make_unique<CAsyncResourceGatherer>(); asyncResourceGatherer = makeUnique<CAsyncResourceGatherer>();
g_pAnimationManager->createAnimation(0.f, opacity, g_pConfigManager->m_AnimationTree.getConfig("fadeIn")); g_pAnimationManager->createAnimation(0.f, opacity, g_pConfigManager->m_AnimationTree.getConfig("fadeIn"));
} }
// //
CRenderer::SRenderFeedback CRenderer::renderLock(const CSessionLockSurface& surf) { CRenderer::SRenderFeedback CRenderer::renderLock(const CSessionLockSurface& surf) {
static const auto DISABLEBAR = g_pConfigManager->getValue<Hyprlang::INT>("general:disable_loading_bar");
projection = Mat3x3::outputProjection(surf.size, HYPRUTILS_TRANSFORM_NORMAL); projection = Mat3x3::outputProjection(surf.size, HYPRUTILS_TRANSFORM_NORMAL);
g_pEGL->makeCurrent(surf.eglSurface); g_pEGL->makeCurrent(surf.eglSurface);
@ -221,18 +219,10 @@ CRenderer::SRenderFeedback CRenderer::renderLock(const CSessionLockSurface& surf
SRenderFeedback feedback; SRenderFeedback feedback;
const bool WAITFORASSETS = !g_pHyprlock->m_bImmediateRender && !asyncResourceGatherer->gathered; const bool WAITFORASSETS = !g_pHyprlock->m_bImmediateRender && !asyncResourceGatherer->gathered;
if (WAITFORASSETS) { if (!WAITFORASSETS) {
// render status
if (!*DISABLEBAR) {
CBox progress = {0, 0, asyncResourceGatherer->progress * surf.size.x, 2};
renderRect(progress, CHyprColor{0.2f, 0.1f, 0.1f, 1.f}, 0);
}
} else {
// render widgets // render widgets
const auto WIDGETS = getOrCreateWidgetsFor(&surf); const auto WIDGETS = getOrCreateWidgetsFor(surf);
for (auto& w : *WIDGETS) { for (auto& w : WIDGETS) {
feedback.needsFrame = w->draw({opacity->value()}) || feedback.needsFrame; feedback.needsFrame = w->draw({opacity->value()}) || feedback.needsFrame;
} }
} }
@ -282,7 +272,7 @@ void CRenderer::renderBorder(const CBox& box, const CGradientValueData& gradient
glUniformMatrix3fv(borderShader.proj, 1, GL_TRUE, glMatrix.getMatrix().data()); glUniformMatrix3fv(borderShader.proj, 1, GL_TRUE, glMatrix.getMatrix().data());
glUniform4fv(borderShader.gradient, gradient.m_vColorsOkLabA.size(), (float*)gradient.m_vColorsOkLabA.data()); glUniform4fv(borderShader.gradient, gradient.m_vColorsOkLabA.size() / 4, (float*)gradient.m_vColorsOkLabA.data());
glUniform1i(borderShader.gradientLength, gradient.m_vColorsOkLabA.size() / 4); glUniform1i(borderShader.gradientLength, gradient.m_vColorsOkLabA.size() / 4);
glUniform1f(borderShader.angle, (int)(gradient.m_fAngle / (M_PI / 180.0)) % 360 * (M_PI / 180.0)); glUniform1f(borderShader.angle, (int)(gradient.m_fAngle / (M_PI / 180.0)) % 360 * (M_PI / 180.0));
glUniform1f(borderShader.alpha, alpha); glUniform1f(borderShader.alpha, alpha);
@ -397,62 +387,49 @@ void CRenderer::renderTextureMix(const CBox& box, const CTexture& tex, const CTe
glBindTexture(tex.m_iTarget, 0); glBindTexture(tex.m_iTarget, 0);
} }
std::vector<std::unique_ptr<IWidget>>* CRenderer::getOrCreateWidgetsFor(const CSessionLockSurface* surf) { template <class Widget>
if (!widgets.contains(surf)) { static void createWidget(std::vector<SP<IWidget>>& widgets) {
const auto W = makeShared<Widget>();
W->registerSelf(W);
widgets.emplace_back(W);
}
std::vector<SP<IWidget>>& CRenderer::getOrCreateWidgetsFor(const CSessionLockSurface& surf) {
RASSERT(surf.m_outputID != OUTPUT_INVALID, "Invalid output ID!");
if (!widgets.contains(surf.m_outputID)) {
auto CWIDGETS = g_pConfigManager->getWidgetConfigs(); auto CWIDGETS = g_pConfigManager->getWidgetConfigs();
std::ranges::sort(CWIDGETS, [](CConfigManager::SWidgetConfig& a, CConfigManager::SWidgetConfig& b) { std::ranges::sort(CWIDGETS, [](CConfigManager::SWidgetConfig& a, CConfigManager::SWidgetConfig& b) {
return std::any_cast<Hyprlang::INT>(a.values.at("zindex")) < std::any_cast<Hyprlang::INT>(b.values.at("zindex")); return std::any_cast<Hyprlang::INT>(a.values.at("zindex")) < std::any_cast<Hyprlang::INT>(b.values.at("zindex"));
}); });
const auto POUTPUT = surf.m_outputRef.lock();
for (auto& c : CWIDGETS) { for (auto& c : CWIDGETS) {
if (!c.monitor.empty() && c.monitor != surf->output->stringPort && !surf->output->stringDesc.starts_with(c.monitor) && if (!c.monitor.empty() && c.monitor != POUTPUT->stringPort && !POUTPUT->stringDesc.starts_with(c.monitor) && !("desc:" + POUTPUT->stringDesc).starts_with(c.monitor))
!surf->output->stringDesc.starts_with("desc:" + c.monitor))
continue; continue;
// by type // by type
if (c.type == "background") { if (c.type == "background") {
const std::string PATH = std::any_cast<Hyprlang::STRING>(c.values.at("path")); createWidget<CBackground>(widgets[surf.m_outputID]);
std::string resourceID = "";
if (PATH == "screenshot") {
resourceID = CScreencopyFrame::getResourceId(surf->output);
// When the initial gather of the asyncResourceGatherer is completed (ready), all DMAFrames are available.
// Dynamic ones are tricky, because a screencopy would copy hyprlock itself.
if (asyncResourceGatherer->gathered) {
if (!asyncResourceGatherer->getAssetByID(resourceID))
resourceID = ""; // Fallback to solid color (background:color)
}
if (!g_pHyprlock->getScreencopy()) {
Debug::log(ERR, "No screencopy support! path=screenshot won't work. Falling back to background color.");
resourceID = "";
}
} else if (!PATH.empty())
resourceID = "background:" + PATH;
widgets[surf].emplace_back(std::make_unique<CBackground>(surf->size, surf->output, resourceID, c.values, PATH == "screenshot"));
} else if (c.type == "input-field") { } else if (c.type == "input-field") {
widgets[surf].emplace_back(std::make_unique<CPasswordInputField>(surf->size, c.values, surf->output->stringPort)); createWidget<CPasswordInputField>(widgets[surf.m_outputID]);
} else if (c.type == "label") { } else if (c.type == "label") {
widgets[surf].emplace_back(std::make_unique<CLabel>(surf->size, c.values, surf->output->stringPort)); createWidget<CLabel>(widgets[surf.m_outputID]);
} else if (c.type == "shape") { } else if (c.type == "shape") {
widgets[surf].emplace_back(std::make_unique<CShape>(surf->size, c.values)); createWidget<CShape>(widgets[surf.m_outputID]);
} else if (c.type == "image") { } else if (c.type == "image") {
const std::string PATH = std::any_cast<Hyprlang::STRING>(c.values.at("path")); createWidget<CImage>(widgets[surf.m_outputID]);
} else {
std::string resourceID = ""; Debug::log(ERR, "Unknown widget type: {}", c.type);
if (!PATH.empty()) continue;
resourceID = "image:" + PATH;
widgets[surf].emplace_back(std::make_unique<CImage>(surf->size, surf->output, resourceID, c.values));
} }
widgets[surf.m_outputID].back()->configure(c.values, POUTPUT);
} }
} }
return &widgets[surf]; return widgets[surf.m_outputID];
} }
void CRenderer::blurFB(const CFramebuffer& outfb, SBlurParams params) { void CRenderer::blurFB(const CFramebuffer& outfb, SBlurParams params) {
@ -618,8 +595,15 @@ void CRenderer::popFb() {
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, boundFBs.empty() ? 0 : boundFBs.back()); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, boundFBs.empty() ? 0 : boundFBs.back());
} }
void CRenderer::removeWidgetsFor(const CSessionLockSurface* surf) { void CRenderer::removeWidgetsFor(OUTPUTID id) {
widgets.erase(surf); widgets.erase(id);
}
void CRenderer::reconfigureWidgetsFor(OUTPUTID id) {
// TODO: reconfigure widgets by just calling their configure method again.
// Requires a way to get a widgets config properties.
// I think the best way would be to store the anonymos key of the widget config.
removeWidgetsFor(id);
} }
void CRenderer::startFadeIn() { void CRenderer::startFadeIn() {

View file

@ -1,9 +1,9 @@
#pragma once #pragma once
#include <memory>
#include <chrono> #include <chrono>
#include <optional> #include <optional>
#include "Shader.hpp" #include "Shader.hpp"
#include "../defines.hpp"
#include "../core/LockSurface.hpp" #include "../core/LockSurface.hpp"
#include "../helpers/AnimatedVariable.hpp" #include "../helpers/AnimatedVariable.hpp"
#include "../helpers/Color.hpp" #include "../helpers/Color.hpp"
@ -12,7 +12,7 @@
#include "widgets/IWidget.hpp" #include "widgets/IWidget.hpp"
#include "Framebuffer.hpp" #include "Framebuffer.hpp"
typedef std::unordered_map<const CSessionLockSurface*, std::vector<std::unique_ptr<IWidget>>> widgetMap_t; typedef std::unordered_map<OUTPUTID, std::vector<SP<IWidget>>> widgetMap_t;
class CRenderer { class CRenderer {
public: public:
@ -29,7 +29,7 @@ class CRenderer {
float boostA = 1.0; float boostA = 1.0;
}; };
SRenderFeedback renderLock(const CSessionLockSurface& surface); SRenderFeedback renderLock(const CSessionLockSurface& surf);
void renderRect(const CBox& box, const CHyprColor& col, int rounding = 0); void renderRect(const CBox& box, const CHyprColor& col, int rounding = 0);
void renderBorder(const CBox& box, const CGradientValueData& gradient, int thickness, int rounding = 0, float alpha = 1.0); void renderBorder(const CBox& box, const CGradientValueData& gradient, int thickness, int rounding = 0, float alpha = 1.0);
@ -37,22 +37,22 @@ class CRenderer {
void renderTextureMix(const CBox& box, const CTexture& tex, const CTexture& tex2, float a = 1.0, float mixFactor = 0.0, int rounding = 0, std::optional<eTransform> tr = {}); void renderTextureMix(const CBox& box, const CTexture& tex, const CTexture& tex2, float a = 1.0, float mixFactor = 0.0, int rounding = 0, std::optional<eTransform> tr = {});
void blurFB(const CFramebuffer& outfb, SBlurParams params); void blurFB(const CFramebuffer& outfb, SBlurParams params);
std::unique_ptr<CAsyncResourceGatherer> asyncResourceGatherer; UP<CAsyncResourceGatherer> asyncResourceGatherer;
std::chrono::system_clock::time_point firstFullFrameTime; std::chrono::system_clock::time_point firstFullFrameTime;
void pushFb(GLint fb); void pushFb(GLint fb);
void popFb(); void popFb();
void removeWidgetsFor(const CSessionLockSurface* surf); void removeWidgetsFor(OUTPUTID id);
void reconfigureWidgetsFor(OUTPUTID id);
void startFadeIn(); void startFadeIn();
void startFadeOut(bool unlock = false, bool immediate = true); void startFadeOut(bool unlock = false, bool immediate = true);
std::vector<SP<IWidget>>& getOrCreateWidgetsFor(const CSessionLockSurface& surf);
private: private:
widgetMap_t widgets; widgetMap_t widgets;
std::vector<std::unique_ptr<IWidget>>* getOrCreateWidgetsFor(const CSessionLockSurface* surf);
CShader rectShader; CShader rectShader;
CShader texShader; CShader texShader;
CShader texMixShader; CShader texMixShader;
@ -70,4 +70,4 @@ class CRenderer {
std::vector<GLint> boundFBs; std::vector<GLint> boundFBs;
}; };
inline std::unique_ptr<CRenderer> g_pRenderer; inline UP<CRenderer> g_pRenderer;

View file

@ -11,6 +11,7 @@
#include <array> #include <array>
#include <cstdint> #include <cstdint>
#include <gbm.h> #include <gbm.h>
#include <hyprutils/memory/UniquePtr.hpp>
#include <unistd.h> #include <unistd.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <libdrm/drm_fourcc.h> #include <libdrm/drm_fourcc.h>
@ -22,24 +23,27 @@ static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES = nullpt
static PFNEGLQUERYDMABUFMODIFIERSEXTPROC eglQueryDmaBufModifiersEXT = nullptr; static PFNEGLQUERYDMABUFMODIFIERSEXTPROC eglQueryDmaBufModifiersEXT = nullptr;
// //
std::string CScreencopyFrame::getResourceId(COutput* output) { std::string CScreencopyFrame::getResourceId(SP<COutput> pOutput) {
return std::format("screencopy:{}-{}x{}", output->stringPort, output->size.x, output->size.y); return std::format("screencopy:{}-{}x{}", pOutput->stringPort, pOutput->size.x, pOutput->size.y);
} }
CScreencopyFrame::CScreencopyFrame(COutput* output) : m_output(output) { CScreencopyFrame::CScreencopyFrame(SP<COutput> pOutput) : m_outputRef(pOutput) {
m_resourceID = getResourceId(m_output);
captureOutput(); captureOutput();
static const auto SCMODE = g_pConfigManager->getValue<Hyprlang::INT>("general:screencopy_mode"); static const auto SCMODE = g_pConfigManager->getValue<Hyprlang::INT>("general:screencopy_mode");
if (*SCMODE == 1) if (*SCMODE == 1)
m_frame = std::make_unique<CSCSHMFrame>(m_sc); m_frame = makeUnique<CSCSHMFrame>(m_sc);
else else
m_frame = std::make_unique<CSCDMAFrame>(m_sc); m_frame = makeUnique<CSCDMAFrame>(m_sc);
} }
void CScreencopyFrame::captureOutput() { void CScreencopyFrame::captureOutput() {
m_sc = makeShared<CCZwlrScreencopyFrameV1>(g_pHyprlock->getScreencopy()->sendCaptureOutput(false, m_output->output->resource())); const auto POUTPUT = m_outputRef.lock();
RASSERT(POUTPUT, "Screencopy, but no valid output");
m_resourceID = getResourceId(POUTPUT);
m_sc = makeShared<CCZwlrScreencopyFrameV1>(g_pHyprlock->getScreencopy()->sendCaptureOutput(false, POUTPUT->m_wlOutput->resource()));
m_sc->setBufferDone([this](CCZwlrScreencopyFrameV1* r) { m_sc->setBufferDone([this](CCZwlrScreencopyFrameV1* r) {
Debug::log(TRACE, "[sc] wlrOnBufferDone for {}", (void*)this); Debug::log(TRACE, "[sc] wlrOnBufferDone for {}", (void*)this);

View file

@ -22,9 +22,9 @@ class ISCFrame {
class CScreencopyFrame { class CScreencopyFrame {
public: public:
static std::string getResourceId(COutput* output); static std::string getResourceId(SP<COutput> pOutput);
CScreencopyFrame(COutput* mon); CScreencopyFrame(SP<COutput> pOutput);
~CScreencopyFrame() = default; ~CScreencopyFrame() = default;
void captureOutput(); void captureOutput();
@ -35,8 +35,8 @@ class CScreencopyFrame {
SPreloadedAsset m_asset; SPreloadedAsset m_asset;
private: private:
COutput* m_output = nullptr; WP<COutput> m_outputRef;
std::unique_ptr<ISCFrame> m_frame = nullptr; UP<ISCFrame> m_frame = nullptr;
bool m_dmaFailed = false; bool m_dmaFailed = false;
}; };

View file

@ -10,23 +10,15 @@
#include <GLES3/gl32.h> #include <GLES3/gl32.h>
CBackground::~CBackground() { CBackground::~CBackground() {
reset();
if (reloadTimer) {
reloadTimer->cancel();
reloadTimer.reset();
}
if (fade) {
if (fade->crossFadeTimer) {
fade->crossFadeTimer->cancel();
fade->crossFadeTimer.reset();
}
fade.reset();
}
} }
CBackground::CBackground(const Vector2D& viewport_, COutput* output_, const std::string& resourceID_, const std::unordered_map<std::string, std::any>& props, bool ss) : void CBackground::registerSelf(const SP<CBackground>& self) {
viewport(viewport_), resourceID(resourceID_), output(output_), isScreenshot(ss) { m_self = self;
}
void CBackground::configure(const std::unordered_map<std::string, std::any>& props, const SP<COutput>& pOutput) {
reset();
try { try {
color = std::any_cast<Hyprlang::INT>(props.at("color")); color = std::any_cast<Hyprlang::INT>(props.at("color"));
@ -48,6 +40,29 @@ CBackground::CBackground(const Vector2D& viewport_, COutput* output_, const std:
RASSERT(false, "Missing propperty for CBackground: {}", e.what()); // RASSERT(false, "Missing propperty for CBackground: {}", e.what()); //
} }
isScreenshot = path == "screenshot";
viewport = pOutput->getViewport();
outputPort = pOutput->stringPort;
transform = isScreenshot ? wlTransformToHyprutils(invertTransform(pOutput->transform)) : HYPRUTILS_TRANSFORM_NORMAL;
if (isScreenshot) {
resourceID = CScreencopyFrame::getResourceId(pOutput);
// When the initial gather of the asyncResourceGatherer is completed (ready), all DMAFrames are available.
// Dynamic ones are tricky, because a screencopy would copy hyprlock itself.
if (g_pRenderer->asyncResourceGatherer->gathered) {
if (!g_pRenderer->asyncResourceGatherer->getAssetByID(resourceID))
resourceID = ""; // Fallback to solid color (background:color)
}
if (!g_pHyprlock->getScreencopy()) {
Debug::log(ERR, "No screencopy support! path=screenshot won't work. Falling back to background color.");
resourceID = "";
}
} else if (!path.empty())
resourceID = "background:" + path;
if (!isScreenshot && reloadTime > -1) { if (!isScreenshot && reloadTime > -1) {
try { try {
modificationTime = std::filesystem::last_write_time(absolutePath(path, "")); modificationTime = std::filesystem::last_write_time(absolutePath(path, ""));
@ -57,32 +72,43 @@ CBackground::CBackground(const Vector2D& viewport_, COutput* output_, const std:
} }
} }
void CBackground::reset() {
if (reloadTimer) {
reloadTimer->cancel();
reloadTimer.reset();
}
if (fade) {
if (fade->crossFadeTimer) {
fade->crossFadeTimer->cancel();
fade->crossFadeTimer.reset();
}
fade.reset();
}
}
void CBackground::renderRect(CHyprColor color) { void CBackground::renderRect(CHyprColor color) {
CBox monbox = {0, 0, viewport.x, viewport.y}; CBox monbox = {0, 0, viewport.x, viewport.y};
g_pRenderer->renderRect(monbox, color, 0); g_pRenderer->renderRect(monbox, color, 0);
} }
static void onReloadTimer(std::shared_ptr<CTimer> self, void* data) { static void onReloadTimer(WP<CBackground> ref) {
const auto PBG = (CBackground*)data; if (auto PBG = ref.lock(); PBG) {
PBG->onReloadTimerUpdate(); PBG->onReloadTimerUpdate();
PBG->plantReloadTimer(); PBG->plantReloadTimer();
}
} }
static void onCrossFadeTimer(std::shared_ptr<CTimer> self, void* data) { static void onCrossFadeTimer(WP<CBackground> ref) {
const auto PBG = (CBackground*)data; if (auto PBG = ref.lock(); PBG)
PBG->onCrossFadeTimerUpdate(); PBG->onCrossFadeTimerUpdate();
} }
static void onAssetCallback(void* data) { static void onAssetCallback(WP<CBackground> ref) {
const auto PBG = (CBackground*)data; if (auto PBG = ref.lock(); PBG)
PBG->startCrossFadeOrUpdateRender(); PBG->startCrossFadeOrUpdateRender();
} }
static void onAssetCallbackTimer(std::shared_ptr<CTimer> self, void* data) {
onAssetCallback(data);
}
bool CBackground::draw(const SRenderData& data) { bool CBackground::draw(const SRenderData& data) {
if (resourceID.empty()) { if (resourceID.empty()) {
@ -115,7 +141,7 @@ bool CBackground::draw(const SRenderData& data) {
// make it brah // make it brah
Vector2D size = asset->texture.m_vSize; Vector2D size = asset->texture.m_vSize;
if (output->transform % 2 == 1 && isScreenshot) { if (transform % 2 == 1 && isScreenshot) {
size.x = asset->texture.m_vSize.y; size.x = asset->texture.m_vSize.y;
size.y = asset->texture.m_vSize.x; size.y = asset->texture.m_vSize.x;
} }
@ -142,16 +168,19 @@ bool CBackground::draw(const SRenderData& data) {
if (fade) if (fade)
g_pRenderer->renderTextureMix(texbox, asset->texture, pendingAsset->texture, 1.0, g_pRenderer->renderTextureMix(texbox, asset->texture, pendingAsset->texture, 1.0,
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - fade->start).count() / (1000 * crossFadeTime), 0, std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - fade->start).count() / (1000 * crossFadeTime), 0,
HYPRUTILS_TRANSFORM_NORMAL); transform);
else else
g_pRenderer->renderTexture(texbox, asset->texture, 1.0, 0, g_pRenderer->renderTexture(texbox, asset->texture, 1.0, 0, transform);
isScreenshot ?
wlTransformToHyprutils(invertTransform(output->transform)) :
HYPRUTILS_TRANSFORM_NORMAL); // this could be omitted but whatever it's only once and makes code cleaner plus less blurring on large texs
if (blurPasses > 0) if (blurPasses > 0)
g_pRenderer->blurFB(blurredFB, CRenderer::SBlurParams{ g_pRenderer->blurFB(blurredFB,
.size = blurSize, .passes = blurPasses, .noise = noise, .contrast = contrast, .brightness = brightness, .vibrancy = vibrancy, .vibrancy_darkness = vibrancy_darkness}); CRenderer::SBlurParams{.size = blurSize,
.passes = blurPasses,
.noise = noise,
.contrast = contrast,
.brightness = brightness,
.vibrancy = vibrancy,
.vibrancy_darkness = vibrancy_darkness});
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
} }
@ -179,9 +208,9 @@ bool CBackground::draw(const SRenderData& data) {
void CBackground::plantReloadTimer() { void CBackground::plantReloadTimer() {
if (reloadTime == 0) if (reloadTime == 0)
reloadTimer = g_pHyprlock->addTimer(std::chrono::hours(1), onReloadTimer, this, true); reloadTimer = g_pHyprlock->addTimer(std::chrono::hours(1), [REF = m_self](auto, auto) { onReloadTimer(REF); }, nullptr, true);
else if (reloadTime > 0) else if (reloadTime > 0)
reloadTimer = g_pHyprlock->addTimer(std::chrono::seconds(reloadTime), onReloadTimer, this, false); reloadTimer = g_pHyprlock->addTimer(std::chrono::seconds(reloadTime), [REF = m_self](auto, auto) { onReloadTimer(REF); }, nullptr, true);
} }
void CBackground::onCrossFadeTimerUpdate() { void CBackground::onCrossFadeTimerUpdate() {
@ -190,7 +219,7 @@ void CBackground::onCrossFadeTimerUpdate() {
if (fade) { if (fade) {
fade->crossFadeTimer.reset(); fade->crossFadeTimer.reset();
fade.reset(nullptr); fade.reset();
} }
if (blurPasses <= 0 && !isScreenshot) if (blurPasses <= 0 && !isScreenshot)
@ -202,7 +231,7 @@ void CBackground::onCrossFadeTimerUpdate() {
pendingAsset = nullptr; pendingAsset = nullptr;
firstRender = true; firstRender = true;
g_pHyprlock->renderOutput(output->stringPort); g_pHyprlock->renderOutput(outputPort);
} }
void CBackground::onReloadTimerUpdate() { void CBackground::onReloadTimerUpdate() {
@ -211,7 +240,7 @@ void CBackground::onReloadTimerUpdate() {
// Path parsing and early returns // Path parsing and early returns
if (!reloadCommand.empty()) { if (!reloadCommand.empty()) {
path = g_pHyprlock->spawnSync(reloadCommand); path = spawnSync(reloadCommand);
if (path.ends_with('\n')) if (path.ends_with('\n'))
path.pop_back(); path.pop_back();
@ -245,8 +274,7 @@ void CBackground::onReloadTimerUpdate() {
request.asset = path; request.asset = path;
request.type = CAsyncResourceGatherer::eTargetType::TARGET_IMAGE; request.type = CAsyncResourceGatherer::eTargetType::TARGET_IMAGE;
request.callback = onAssetCallback; request.callback = [REF = m_self]() { onAssetCallback(REF); };
request.callbackData = this;
g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request); g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
} }
@ -262,7 +290,7 @@ void CBackground::startCrossFadeOrUpdateRender() {
if (crossFadeTime > 0) { if (crossFadeTime > 0) {
// Start a fade // Start a fade
if (!fade) if (!fade)
fade = std::make_unique<SFade>(std::chrono::system_clock::now(), 0, nullptr); fade = makeUnique<SFade>(std::chrono::system_clock::now(), 0, nullptr);
else { else {
// Maybe we where already fading so reset it just in case, but should'nt be happening. // Maybe we where already fading so reset it just in case, but should'nt be happening.
if (fade->crossFadeTimer) { if (fade->crossFadeTimer) {
@ -272,15 +300,16 @@ void CBackground::startCrossFadeOrUpdateRender() {
} }
fade->start = std::chrono::system_clock::now(); fade->start = std::chrono::system_clock::now();
fade->a = 0; fade->a = 0;
fade->crossFadeTimer = g_pHyprlock->addTimer(std::chrono::milliseconds((int)(1000.0 * crossFadeTime)), onCrossFadeTimer, this); fade->crossFadeTimer =
g_pHyprlock->addTimer(std::chrono::milliseconds((int)(1000.0 * crossFadeTime)), [REF = m_self](auto, auto) { onCrossFadeTimer(REF); }, nullptr);
} else { } else {
onCrossFadeTimerUpdate(); onCrossFadeTimerUpdate();
} }
} }
} else if (!pendingResourceID.empty()) { } else if (!pendingResourceID.empty()) {
Debug::log(WARN, "Asset {} not available after the asyncResourceGatherer's callback!", pendingResourceID); Debug::log(WARN, "Asset {} not available after the asyncResourceGatherer's callback!", pendingResourceID);
g_pHyprlock->addTimer(std::chrono::milliseconds(100), onAssetCallbackTimer, this); g_pHyprlock->addTimer(std::chrono::milliseconds(100), [REF = m_self](auto, auto) { onAssetCallback(REF); }, nullptr);
} }
g_pHyprlock->renderOutput(output->stringPort); g_pHyprlock->renderOutput(outputPort);
} }

View file

@ -6,6 +6,7 @@
#include "../../core/Timer.hpp" #include "../../core/Timer.hpp"
#include "../Framebuffer.hpp" #include "../Framebuffer.hpp"
#include "../AsyncResourceGatherer.hpp" #include "../AsyncResourceGatherer.hpp"
#include <hyprutils/math/Misc.hpp>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <any> #include <any>
@ -23,10 +24,16 @@ struct SFade {
class CBackground : public IWidget { class CBackground : public IWidget {
public: public:
CBackground(const Vector2D& viewport, COutput* output_, const std::string& resourceID, const std::unordered_map<std::string, std::any>& props, bool ss_); CBackground() = default;
~CBackground(); ~CBackground();
void registerSelf(const SP<CBackground>& self);
virtual void configure(const std::unordered_map<std::string, std::any>& props, const SP<COutput>& pOutput);
virtual bool draw(const SRenderData& data); virtual bool draw(const SRenderData& data);
void reset(); // Unload assets, remove timers, etc.
void renderRect(CHyprColor color); void renderRect(CHyprColor color);
void onReloadTimerUpdate(); void onReloadTimerUpdate();
@ -35,6 +42,8 @@ class CBackground : public IWidget {
void startCrossFadeOrUpdateRender(); void startCrossFadeOrUpdateRender();
private: private:
WP<CBackground> m_self;
// if needed // if needed
CFramebuffer blurredFB; CFramebuffer blurredFB;
@ -48,6 +57,9 @@ class CBackground : public IWidget {
Vector2D viewport; Vector2D viewport;
std::string path = ""; std::string path = "";
std::string outputPort;
Hyprutils::Math::eTransform transform;
std::string resourceID; std::string resourceID;
std::string pendingResourceID; std::string pendingResourceID;
@ -55,12 +67,11 @@ class CBackground : public IWidget {
CHyprColor color; CHyprColor color;
SPreloadedAsset* asset = nullptr; SPreloadedAsset* asset = nullptr;
COutput* output = nullptr;
bool isScreenshot = false; bool isScreenshot = false;
SPreloadedAsset* pendingAsset = nullptr; SPreloadedAsset* pendingAsset = nullptr;
bool firstRender = true; bool firstRender = true;
std::unique_ptr<SFade> fade; UP<SFade> fade;
int reloadTime = -1; int reloadTime = -1;
std::string reloadCommand; std::string reloadCommand;

View file

@ -22,7 +22,7 @@ namespace std {
} }
#endif #endif
Vector2D rotateVector(const Vector2D& vec, const double& ang) { static Vector2D rotateVector(const Vector2D& vec, const double& ang) {
const double COS = std::abs(std::cos(ang)); const double COS = std::abs(std::cos(ang));
const double SIN = std::abs(std::sin(ang)); const double SIN = std::abs(std::sin(ang));
return Vector2D((vec.x * COS) + (vec.y * SIN), (vec.x * SIN) + (vec.y * COS)); return Vector2D((vec.x * COS) + (vec.y * SIN), (vec.x * SIN) + (vec.y * COS));
@ -100,22 +100,31 @@ static void replaceAllAttempts(std::string& str) {
} }
static void replaceAllLayout(std::string& str) { static void replaceAllLayout(std::string& str) {
std::string layoutName = "error";
const auto LAYOUTIDX = g_pHyprlock->m_uiActiveLayout; const auto LAYOUTIDX = g_pHyprlock->m_uiActiveLayout;
const auto LAYOUTNAME = xkb_keymap_layout_get_name(g_pSeatManager->m_pXKBKeymap, LAYOUTIDX);
const std::string STR = LAYOUTNAME ? LAYOUTNAME : "error";
size_t pos = 0;
if (g_pSeatManager->m_pXKBKeymap) {
const auto PNAME = xkb_keymap_layout_get_name(g_pSeatManager->m_pXKBKeymap, LAYOUTIDX);
if (PNAME)
layoutName = PNAME;
}
size_t pos = 0;
while ((pos = str.find("$LAYOUT", pos)) != std::string::npos) { while ((pos = str.find("$LAYOUT", pos)) != std::string::npos) {
if (str.substr(pos, 8).ends_with('[') && str.substr(pos).contains(']')) { if (str.substr(pos, 8).ends_with('[') && str.substr(pos).contains(']')) {
const std::string REPL = str.substr(pos + 8, str.find_first_of(']', pos) - 8 - pos); const std::string REPL = str.substr(pos + 8, str.find_first_of(']', pos) - 8 - pos);
const CVarList LANGS(REPL); const CVarList LANGS(REPL);
const std::string LANG = LANGS[LAYOUTIDX].empty() ? STR : LANGS[LAYOUTIDX] == "!" ? "" : LANGS[LAYOUTIDX]; if (LAYOUTIDX >= LANGS.size()) {
Debug::log(ERR, "Layout index {} out of bounds. Max is {}.", LAYOUTIDX, LANGS.size() - 1);
continue;
}
const std::string LANG = LANGS[LAYOUTIDX].empty() ? layoutName : LANGS[LAYOUTIDX] == "!" ? "" : LANGS[LAYOUTIDX];
str.replace(pos, 9 + REPL.length(), LANG); str.replace(pos, 9 + REPL.length(), LANG);
pos += LANG.length(); pos += LANG.length();
} else { } else {
str.replace(pos, 7, STR); str.replace(pos, 7, layoutName);
pos += STR.length(); pos += layoutName.length();
} }
} }
} }
@ -257,3 +266,15 @@ IWidget::SFormatResult IWidget::formatString(std::string in) {
result.formatted = in; result.formatted = in;
return result; return result;
} }
void IWidget::setHover(bool hover) {
hovered = hover;
}
bool IWidget::isHovered() const {
return hovered;
}
bool IWidget::containsPoint(const Vector2D& pos) const {
return getBoundingBoxWl().containsPoint(pos);
}

View file

@ -1,15 +1,22 @@
#pragma once #pragma once
#include "../../defines.hpp"
#include "../../helpers/Math.hpp" #include "../../helpers/Math.hpp"
#include <string> #include <string>
#include <unordered_map>
#include <any>
class COutput;
class IWidget { class IWidget {
public: public:
struct SRenderData { struct SRenderData {
float opacity = 1; float opacity = 1;
}; };
virtual ~IWidget() = default; virtual ~IWidget() = default;
virtual void configure(const std::unordered_map<std::string, std::any>& prop, const SP<COutput>& pOutput) = 0;
virtual bool draw(const SRenderData& data) = 0; virtual bool draw(const SRenderData& data) = 0;
static Vector2D posFromHVAlign(const Vector2D& viewport, const Vector2D& size, const Vector2D& offset, const std::string& halign, const std::string& valign, static Vector2D posFromHVAlign(const Vector2D& viewport, const Vector2D& size, const Vector2D& offset, const std::string& halign, const std::string& valign,
@ -17,6 +24,13 @@ class IWidget {
static int roundingForBox(const CBox& box, int roundingConfig); static int roundingForBox(const CBox& box, int roundingConfig);
static int roundingForBorderBox(const CBox& borderBox, int roundingConfig, int thickness); static int roundingForBorderBox(const CBox& borderBox, int roundingConfig, int thickness);
virtual CBox getBoundingBoxWl() const {
return CBox();
};
virtual void onClick(uint32_t button, bool down, const Vector2D& pos) {}
virtual void onHover(const Vector2D& pos) {}
bool containsPoint(const Vector2D& pos) const;
struct SFormatResult { struct SFormatResult {
std::string formatted; std::string formatted;
float updateEveryMs = 0; // 0 means don't (static) float updateEveryMs = 0; // 0 means don't (static)
@ -26,4 +40,10 @@ class IWidget {
}; };
static SFormatResult formatString(std::string in); static SFormatResult formatString(std::string in);
void setHover(bool hover);
bool isHovered() const;
private:
bool hovered = false;
}; };

View file

@ -6,35 +6,33 @@
#include "../../config/ConfigDataValues.hpp" #include "../../config/ConfigDataValues.hpp"
#include <cmath> #include <cmath>
#include <hyprlang.hpp> #include <hyprlang.hpp>
#include <hyprutils/math/Vector2D.hpp>
CImage::~CImage() { CImage::~CImage() {
if (imageTimer) { reset();
imageTimer->cancel(); }
imageTimer.reset();
void CImage::registerSelf(const SP<CImage>& self) {
m_self = self;
}
static void onTimer(WP<CImage> ref) {
if (auto PIMAGE = ref.lock(); PIMAGE) {
PIMAGE->onTimerUpdate();
PIMAGE->plantTimer();
} }
} }
static void onTimer(std::shared_ptr<CTimer> self, void* data) { static void onAssetCallback(WP<CImage> ref) {
const auto PIMAGE = (CImage*)data; if (auto PIMAGE = ref.lock(); PIMAGE)
PIMAGE->onTimerUpdate();
PIMAGE->plantTimer();
}
static void onAssetCallback(void* data) {
const auto PIMAGE = (CImage*)data;
PIMAGE->renderUpdate(); PIMAGE->renderUpdate();
} }
static void onAssetCallbackTimer(std::shared_ptr<CTimer> self, void* data) {
onAssetCallback(data);
}
void CImage::onTimerUpdate() { void CImage::onTimerUpdate() {
const std::string OLDPATH = path; const std::string OLDPATH = path;
if (!reloadCommand.empty()) { if (!reloadCommand.empty()) {
path = g_pHyprlock->spawnSync(reloadCommand); path = spawnSync(reloadCommand);
if (path.ends_with('\n')) if (path.ends_with('\n'))
path.pop_back(); path.pop_back();
@ -65,9 +63,7 @@ void CImage::onTimerUpdate() {
pendingResourceID = request.id; pendingResourceID = request.id;
request.asset = path; request.asset = path;
request.type = CAsyncResourceGatherer::eTargetType::TARGET_IMAGE; request.type = CAsyncResourceGatherer::eTargetType::TARGET_IMAGE;
request.callback = [REF = m_self]() { onAssetCallback(REF); };
request.callback = onAssetCallback;
request.callbackData = this;
g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request); g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
} }
@ -75,20 +71,25 @@ void CImage::onTimerUpdate() {
void CImage::plantTimer() { void CImage::plantTimer() {
if (reloadTime == 0) { if (reloadTime == 0) {
imageTimer = g_pHyprlock->addTimer(std::chrono::hours(1), onTimer, this, true); imageTimer = g_pHyprlock->addTimer(std::chrono::hours(1), [REF = m_self](auto, auto) { onTimer(REF); }, nullptr, true);
} else if (reloadTime > 0) } else if (reloadTime > 0)
imageTimer = g_pHyprlock->addTimer(std::chrono::seconds(reloadTime), onTimer, this, false); imageTimer = g_pHyprlock->addTimer(std::chrono::seconds(reloadTime), [REF = m_self](auto, auto) { onTimer(REF); }, nullptr, false);
} }
CImage::CImage(const Vector2D& viewport_, COutput* output_, const std::string& resourceID_, const std::unordered_map<std::string, std::any>& props) : void CImage::configure(const std::unordered_map<std::string, std::any>& props, const SP<COutput>& pOutput) {
viewport(viewport_), resourceID(resourceID_), output(output_), shadow(this, props, viewport_) { reset();
viewport = pOutput->getViewport();
stringPort = pOutput->stringPort;
shadow.configure(m_self.lock(), props, viewport);
try { try {
size = std::any_cast<Hyprlang::INT>(props.at("size")); size = std::any_cast<Hyprlang::INT>(props.at("size"));
rounding = std::any_cast<Hyprlang::INT>(props.at("rounding")); rounding = std::any_cast<Hyprlang::INT>(props.at("rounding"));
border = std::any_cast<Hyprlang::INT>(props.at("border_size")); border = std::any_cast<Hyprlang::INT>(props.at("border_size"));
color = *CGradientValueData::fromAnyPv(props.at("border_color")); color = *CGradientValueData::fromAnyPv(props.at("border_color"));
pos = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport_); configPos = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport);
halign = std::any_cast<Hyprlang::STRING>(props.at("halign")); halign = std::any_cast<Hyprlang::STRING>(props.at("halign"));
valign = std::any_cast<Hyprlang::STRING>(props.at("valign")); valign = std::any_cast<Hyprlang::STRING>(props.at("valign"));
angle = std::any_cast<Hyprlang::FLOAT>(props.at("rotate")); angle = std::any_cast<Hyprlang::FLOAT>(props.at("rotate"));
@ -96,12 +97,14 @@ CImage::CImage(const Vector2D& viewport_, COutput* output_, const std::string& r
path = std::any_cast<Hyprlang::STRING>(props.at("path")); path = std::any_cast<Hyprlang::STRING>(props.at("path"));
reloadTime = std::any_cast<Hyprlang::INT>(props.at("reload_time")); reloadTime = std::any_cast<Hyprlang::INT>(props.at("reload_time"));
reloadCommand = std::any_cast<Hyprlang::STRING>(props.at("reload_cmd")); reloadCommand = std::any_cast<Hyprlang::STRING>(props.at("reload_cmd"));
onclickCommand = std::any_cast<Hyprlang::STRING>(props.at("onclick"));
} catch (const std::bad_any_cast& e) { } catch (const std::bad_any_cast& e) {
RASSERT(false, "Failed to construct CImage: {}", e.what()); // RASSERT(false, "Failed to construct CImage: {}", e.what()); //
} catch (const std::out_of_range& e) { } catch (const std::out_of_range& e) {
RASSERT(false, "Missing propperty for CImage: {}", e.what()); // RASSERT(false, "Missing propperty for CImage: {}", e.what()); //
} }
resourceID = "image:" + path;
angle = angle * M_PI / 180.0; angle = angle * M_PI / 180.0;
if (reloadTime > -1) { if (reloadTime > -1) {
@ -113,6 +116,25 @@ CImage::CImage(const Vector2D& viewport_, COutput* output_, const std::string& r
} }
} }
void CImage::reset() {
if (imageTimer) {
imageTimer->cancel();
imageTimer.reset();
}
if (g_pHyprlock->m_bTerminate)
return;
imageFB.release();
if (asset && reloadTime > -1) // Don't unload asset if it's a static image
g_pRenderer->asyncResourceGatherer->unloadAsset(asset);
asset = nullptr;
pendingResourceID = "";
resourceID = "";
}
bool CImage::draw(const SRenderData& data) { bool CImage::draw(const SRenderData& data) {
if (resourceID.empty()) if (resourceID.empty())
@ -176,10 +198,10 @@ bool CImage::draw(const SRenderData& data) {
shadow.draw(data); shadow.draw(data);
const auto TEXPOS = posFromHVAlign(viewport, tex->m_vSize, pos, halign, valign, angle); pos = posFromHVAlign(viewport, tex->m_vSize, configPos, halign, valign, angle);
texbox.x = TEXPOS.x; texbox.x = pos.x;
texbox.y = TEXPOS.y; texbox.y = pos.y;
texbox.round(); texbox.round();
texbox.rot = angle; texbox.rot = angle;
@ -204,9 +226,32 @@ void CImage::renderUpdate() {
pendingResourceID = ""; pendingResourceID = "";
} else if (!pendingResourceID.empty()) { } else if (!pendingResourceID.empty()) {
Debug::log(WARN, "Asset {} not available after the asyncResourceGatherer's callback!", pendingResourceID); Debug::log(WARN, "Asset {} not available after the asyncResourceGatherer's callback!", pendingResourceID);
pendingResourceID = "";
} else if (!pendingResourceID.empty()) {
Debug::log(WARN, "Asset {} not available after the asyncResourceGatherer's callback!", pendingResourceID);
g_pHyprlock->addTimer(std::chrono::milliseconds(100), onAssetCallbackTimer, this); g_pHyprlock->addTimer(std::chrono::milliseconds(100), [REF = m_self](auto, auto) { onAssetCallback(REF); }, nullptr);
} }
g_pHyprlock->renderOutput(output->stringPort); g_pHyprlock->renderOutput(stringPort);
}
CBox CImage::getBoundingBoxWl() const {
if (!imageFB.isAllocated())
return CBox{};
return {
Vector2D{pos.x, viewport.y - pos.y - imageFB.m_cTex.m_vSize.y},
imageFB.m_cTex.m_vSize,
};
}
void CImage::onClick(uint32_t button, bool down, const Vector2D& pos) {
if (down && !onclickCommand.empty())
spawnAsync(onclickCommand);
}
void CImage::onHover(const Vector2D& pos) {
if (!onclickCommand.empty())
g_pSeatManager->m_pCursorShape->setShape(WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER);
} }

View file

@ -17,16 +17,26 @@ class COutput;
class CImage : public IWidget { class CImage : public IWidget {
public: public:
CImage(const Vector2D& viewport, COutput* output_, const std::string& resourceID, const std::unordered_map<std::string, std::any>& props); CImage() = default;
~CImage(); ~CImage();
void registerSelf(const SP<CImage>& self);
virtual void configure(const std::unordered_map<std::string, std::any>& props, const SP<COutput>& pOutput);
virtual bool draw(const SRenderData& data); virtual bool draw(const SRenderData& data);
virtual CBox getBoundingBoxWl() const;
virtual void onClick(uint32_t button, bool down, const Vector2D& pos);
virtual void onHover(const Vector2D& pos);
void reset();
void renderUpdate(); void renderUpdate();
void onTimerUpdate(); void onTimerUpdate();
void plantTimer(); void plantTimer();
private: private:
WP<CImage> m_self;
CFramebuffer imageFB; CFramebuffer imageFB;
int size; int size;
@ -35,6 +45,7 @@ class CImage : public IWidget {
double angle; double angle;
CGradientValueData color; CGradientValueData color;
Vector2D pos; Vector2D pos;
Vector2D configPos;
std::string halign, valign, path; std::string halign, valign, path;
@ -42,14 +53,17 @@ class CImage : public IWidget {
int reloadTime; int reloadTime;
std::string reloadCommand; std::string reloadCommand;
std::string onclickCommand;
std::filesystem::file_time_type modificationTime; std::filesystem::file_time_type modificationTime;
std::shared_ptr<CTimer> imageTimer; std::shared_ptr<CTimer> imageTimer;
CAsyncResourceGatherer::SPreloadRequest request; CAsyncResourceGatherer::SPreloadRequest request;
Vector2D viewport; Vector2D viewport;
std::string stringPort;
std::string resourceID; std::string resourceID;
std::string pendingResourceID; // if reloading image std::string pendingResourceID; // if reloading image
SPreloadedAsset* asset = nullptr; SPreloadedAsset* asset = nullptr;
COutput* output = nullptr;
CShadowable shadow; CShadowable shadow;
}; };

View file

@ -3,38 +3,33 @@
#include "../../helpers/Log.hpp" #include "../../helpers/Log.hpp"
#include "../../core/hyprlock.hpp" #include "../../core/hyprlock.hpp"
#include "../../helpers/Color.hpp" #include "../../helpers/Color.hpp"
#include "../../helpers/MiscFunctions.hpp"
#include "../../config/ConfigDataValues.hpp" #include "../../config/ConfigDataValues.hpp"
#include <hyprlang.hpp> #include <hyprlang.hpp>
#include <stdexcept> #include <stdexcept>
CLabel::~CLabel() { CLabel::~CLabel() {
if (labelTimer) { reset();
labelTimer->cancel(); }
labelTimer.reset();
void CLabel::registerSelf(const SP<CLabel>& self) {
m_self = self;
}
static void onTimer(WP<CLabel> ref) {
if (auto PLABEL = ref.lock(); PLABEL) {
// update label
PLABEL->onTimerUpdate();
// plant new timer
PLABEL->plantTimer();
} }
} }
static void onTimer(std::shared_ptr<CTimer> self, void* data) { static void onAssetCallback(WP<CLabel> ref) {
if (data == nullptr) if (auto PLABEL = ref.lock(); PLABEL)
return;
const auto PLABEL = (CLabel*)data;
// update label
PLABEL->onTimerUpdate();
// plant new timer
PLABEL->plantTimer();
}
static void onAssetCallback(void* data) {
const auto PLABEL = (CLabel*)data;
PLABEL->renderUpdate(); PLABEL->renderUpdate();
} }
static void onAssetCallbackTimer(std::shared_ptr<CTimer> self, void* data) {
onAssetCallback(data);
}
std::string CLabel::getUniqueResourceId() { std::string CLabel::getUniqueResourceId() {
return std::string{"label:"} + std::to_string((uintptr_t)this) + ",time:" + std::to_string(std::chrono::system_clock::now().time_since_epoch().count()); return std::string{"label:"} + std::to_string((uintptr_t)this) + ",time:" + std::to_string(std::chrono::system_clock::now().time_since_epoch().count());
} }
@ -57,28 +52,35 @@ void CLabel::onTimerUpdate() {
pendingResourceID = request.id; pendingResourceID = request.id;
request.asset = label.formatted; request.asset = label.formatted;
request.callback = onAssetCallback; request.callback = [REF = m_self]() { onAssetCallback(REF); };
request.callbackData = this;
g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request); g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
} }
void CLabel::plantTimer() { void CLabel::plantTimer() {
if (label.updateEveryMs != 0) if (label.updateEveryMs != 0)
labelTimer = g_pHyprlock->addTimer(std::chrono::milliseconds((int)label.updateEveryMs), onTimer, this, label.allowForceUpdate); labelTimer = g_pHyprlock->addTimer(std::chrono::milliseconds((int)label.updateEveryMs), [REF = m_self](auto, auto) { onTimer(REF); }, this, label.allowForceUpdate);
else if (label.updateEveryMs == 0 && label.allowForceUpdate) else if (label.updateEveryMs == 0 && label.allowForceUpdate)
labelTimer = g_pHyprlock->addTimer(std::chrono::hours(1), onTimer, this, true); labelTimer = g_pHyprlock->addTimer(std::chrono::hours(1), [REF = m_self](auto, auto) { onTimer(REF); }, this, true);
} }
CLabel::CLabel(const Vector2D& viewport_, const std::unordered_map<std::string, std::any>& props, const std::string& output) : void CLabel::configure(const std::unordered_map<std::string, std::any>& props, const SP<COutput>& pOutput) {
outputStringPort(output), shadow(this, props, viewport_) { reset();
outputStringPort = pOutput->stringPort;
viewport = pOutput->getViewport();
shadow.configure(m_self.lock(), props, viewport);
try { try {
pos = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport_); configPos = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport);
labelPreFormat = std::any_cast<Hyprlang::STRING>(props.at("text")); labelPreFormat = std::any_cast<Hyprlang::STRING>(props.at("text"));
halign = std::any_cast<Hyprlang::STRING>(props.at("halign")); halign = std::any_cast<Hyprlang::STRING>(props.at("halign"));
valign = std::any_cast<Hyprlang::STRING>(props.at("valign")); valign = std::any_cast<Hyprlang::STRING>(props.at("valign"));
angle = std::any_cast<Hyprlang::FLOAT>(props.at("rotate")); angle = std::any_cast<Hyprlang::FLOAT>(props.at("rotate"));
angle = angle * M_PI / 180.0; angle = angle * M_PI / 180.0;
onclickCommand = std::any_cast<Hyprlang::STRING>(props.at("onclick"));
std::string textAlign = std::any_cast<Hyprlang::STRING>(props.at("text_align")); std::string textAlign = std::any_cast<Hyprlang::STRING>(props.at("text_align"));
std::string fontFamily = std::any_cast<Hyprlang::STRING>(props.at("font_family")); std::string fontFamily = std::any_cast<Hyprlang::STRING>(props.at("font_family"));
@ -105,14 +107,30 @@ CLabel::CLabel(const Vector2D& viewport_, const std::unordered_map<std::string,
RASSERT(false, "Missing property for CLabel: {}", e.what()); // RASSERT(false, "Missing property for CLabel: {}", e.what()); //
} }
configPos = pos; pos = configPos; // Label size not known yet
viewport = viewport_;
g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request); g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
plantTimer(); plantTimer();
} }
void CLabel::reset() {
if (labelTimer) {
labelTimer->cancel();
labelTimer.reset();
}
if (g_pHyprlock->m_bTerminate)
return;
if (asset)
g_pRenderer->asyncResourceGatherer->unloadAsset(asset);
asset = nullptr;
pendingResourceID.clear();
resourceID.clear();
}
bool CLabel::draw(const SRenderData& data) { bool CLabel::draw(const SRenderData& data) {
if (!asset) { if (!asset) {
asset = g_pRenderer->asyncResourceGatherer->getAssetByID(resourceID); asset = g_pRenderer->asyncResourceGatherer->getAssetByID(resourceID);
@ -150,9 +168,29 @@ void CLabel::renderUpdate() {
} else { } else {
Debug::log(WARN, "Asset {} not available after the asyncResourceGatherer's callback!", pendingResourceID); Debug::log(WARN, "Asset {} not available after the asyncResourceGatherer's callback!", pendingResourceID);
g_pHyprlock->addTimer(std::chrono::milliseconds(100), onAssetCallbackTimer, this); g_pHyprlock->addTimer(std::chrono::milliseconds(100), [REF = m_self](auto, auto) { onAssetCallback(REF); }, nullptr);
return; return;
} }
g_pHyprlock->renderOutput(outputStringPort); g_pHyprlock->renderOutput(outputStringPort);
} }
CBox CLabel::getBoundingBoxWl() const {
if (!asset)
return CBox{};
return {
Vector2D{pos.x, viewport.y - pos.y - asset->texture.m_vSize.y},
asset->texture.m_vSize,
};
}
void CLabel::onClick(uint32_t button, bool down, const Vector2D& pos) {
if (down && !onclickCommand.empty())
spawnAsync(onclickCommand);
}
void CLabel::onHover(const Vector2D& pos) {
if (!onclickCommand.empty())
g_pSeatManager->m_pCursorShape->setShape(WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER);
}

View file

@ -14,16 +14,26 @@ class CSessionLockSurface;
class CLabel : public IWidget { class CLabel : public IWidget {
public: public:
CLabel(const Vector2D& viewport, const std::unordered_map<std::string, std::any>& props, const std::string& output); CLabel() = default;
~CLabel(); ~CLabel();
void registerSelf(const SP<CLabel>& self);
virtual void configure(const std::unordered_map<std::string, std::any>& prop, const SP<COutput>& pOutput);
virtual bool draw(const SRenderData& data); virtual bool draw(const SRenderData& data);
virtual CBox getBoundingBoxWl() const;
virtual void onClick(uint32_t button, bool down, const Vector2D& pos);
virtual void onHover(const Vector2D& pos);
void reset();
void renderUpdate(); void renderUpdate();
void onTimerUpdate(); void onTimerUpdate();
void plantTimer(); void plantTimer();
private: private:
WP<CLabel> m_self;
std::string getUniqueResourceId(); std::string getUniqueResourceId();
std::string labelPreFormat; std::string labelPreFormat;
@ -36,6 +46,7 @@ class CLabel : public IWidget {
std::string resourceID; std::string resourceID;
std::string pendingResourceID; // if dynamic label std::string pendingResourceID; // if dynamic label
std::string halign, valign; std::string halign, valign;
std::string onclickCommand;
SPreloadedAsset* asset = nullptr; SPreloadedAsset* asset = nullptr;
std::string outputStringPort; std::string outputStringPort;

View file

@ -15,11 +15,25 @@
using namespace Hyprutils::String; using namespace Hyprutils::String;
CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::unordered_map<std::string, std::any>& props, const std::string& output) : CPasswordInputField::~CPasswordInputField() {
viewport(viewport_), outputStringPort(output), shadow(this, props, viewport_) { reset();
}
void CPasswordInputField::registerSelf(const SP<CPasswordInputField>& self) {
m_self = self;
}
void CPasswordInputField::configure(const std::unordered_map<std::string, std::any>& props, const SP<COutput>& pOutput) {
reset();
outputStringPort = pOutput->stringPort;
viewport = pOutput->getViewport();
shadow.configure(m_self.lock(), props, viewport);
try { try {
pos = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport_); pos = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport);
configSize = CLayoutValueData::fromAnyPv(props.at("size"))->getAbsolute(viewport_); configSize = CLayoutValueData::fromAnyPv(props.at("size"))->getAbsolute(viewport);
halign = std::any_cast<Hyprlang::STRING>(props.at("halign")); halign = std::any_cast<Hyprlang::STRING>(props.at("halign"));
valign = std::any_cast<Hyprlang::STRING>(props.at("valign")); valign = std::any_cast<Hyprlang::STRING>(props.at("valign"));
outThick = std::any_cast<Hyprlang::INT>(props.at("outline_thickness")); outThick = std::any_cast<Hyprlang::INT>(props.at("outline_thickness"));
@ -34,7 +48,6 @@ CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::u
rounding = std::any_cast<Hyprlang::INT>(props.at("rounding")); rounding = std::any_cast<Hyprlang::INT>(props.at("rounding"));
configPlaceholderText = std::any_cast<Hyprlang::STRING>(props.at("placeholder_text")); configPlaceholderText = std::any_cast<Hyprlang::STRING>(props.at("placeholder_text"));
configFailText = std::any_cast<Hyprlang::STRING>(props.at("fail_text")); configFailText = std::any_cast<Hyprlang::STRING>(props.at("fail_text"));
configFailTimeoutMs = std::any_cast<Hyprlang::INT>(props.at("fail_timeout"));
fontFamily = std::any_cast<Hyprlang::STRING>(props.at("font_family")); fontFamily = std::any_cast<Hyprlang::STRING>(props.at("font_family"));
colorConfig.outer = CGradientValueData::fromAnyPv(props.at("outer_color")); colorConfig.outer = CGradientValueData::fromAnyPv(props.at("outer_color"));
colorConfig.inner = std::any_cast<Hyprlang::INT>(props.at("inner_color")); colorConfig.inner = std::any_cast<Hyprlang::INT>(props.at("inner_color"));
@ -46,6 +59,7 @@ CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::u
colorConfig.num = CGradientValueData::fromAnyPv(props.at("numlock_color")); colorConfig.num = CGradientValueData::fromAnyPv(props.at("numlock_color"));
colorConfig.invertNum = std::any_cast<Hyprlang::INT>(props.at("invert_numlock")); colorConfig.invertNum = std::any_cast<Hyprlang::INT>(props.at("invert_numlock"));
colorConfig.swapFont = std::any_cast<Hyprlang::INT>(props.at("swap_font_color")); colorConfig.swapFont = std::any_cast<Hyprlang::INT>(props.at("swap_font_color"));
colorConfig.hiddenBase = std::any_cast<Hyprlang::INT>(props.at("hide_input_base_color"));
} catch (const std::bad_any_cast& e) { } catch (const std::bad_any_cast& e) {
RASSERT(false, "Failed to construct CPasswordInputField: {}", e.what()); // RASSERT(false, "Failed to construct CPasswordInputField: {}", e.what()); //
} catch (const std::out_of_range& e) { } catch (const std::out_of_range& e) {
@ -61,6 +75,16 @@ CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::u
colorConfig.caps = colorConfig.caps->m_bIsFallback ? colorConfig.fail : colorConfig.caps; colorConfig.caps = colorConfig.caps->m_bIsFallback ? colorConfig.fail : colorConfig.caps;
g_pAnimationManager->createAnimation(0.f, fade.a, g_pConfigManager->m_AnimationTree.getConfig("inputFieldFade"));
g_pAnimationManager->createAnimation(0.f, dots.currentAmount, g_pConfigManager->m_AnimationTree.getConfig("inputFieldDots"));
g_pAnimationManager->createAnimation(configSize, size, g_pConfigManager->m_AnimationTree.getConfig("inputFieldWidth"));
g_pAnimationManager->createAnimation(colorConfig.inner, colorState.inner, g_pConfigManager->m_AnimationTree.getConfig("inputFieldColors"));
g_pAnimationManager->createAnimation(*colorConfig.outer, colorState.outer, g_pConfigManager->m_AnimationTree.getConfig("inputFieldColors"));
srand(std::chrono::system_clock::now().time_since_epoch().count());
pos = posFromHVAlign(viewport, size->goal(), configPos, halign, valign);
if (!dots.textFormat.empty()) { if (!dots.textFormat.empty()) {
dots.textResourceID = std::format("input:{}-{}", (uintptr_t)this, dots.textFormat); dots.textResourceID = std::format("input:{}-{}", (uintptr_t)this, dots.textFormat);
CAsyncResourceGatherer::SPreloadRequest request; CAsyncResourceGatherer::SPreloadRequest request;
@ -74,23 +98,30 @@ CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::u
g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request); g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
} }
g_pAnimationManager->createAnimation(0.f, fade.a, g_pConfigManager->m_AnimationTree.getConfig("inputFieldFade"));
g_pAnimationManager->createAnimation(0.f, dots.currentAmount, g_pConfigManager->m_AnimationTree.getConfig("inputFieldDots"));
g_pAnimationManager->createAnimation(configSize, size, g_pConfigManager->m_AnimationTree.getConfig("inputFieldWidth"));
g_pAnimationManager->createAnimation(colorConfig.inner, colorState.inner, g_pConfigManager->m_AnimationTree.getConfig("inputFieldColors"));
g_pAnimationManager->createAnimation(*colorConfig.outer, colorState.outer, g_pConfigManager->m_AnimationTree.getConfig("inputFieldColors"));
srand(std::chrono::system_clock::now().time_since_epoch().count());
// request the inital placeholder asset // request the inital placeholder asset
updatePlaceholder(); updatePlaceholder();
} }
static void fadeOutCallback(std::shared_ptr<CTimer> self, void* data) { void CPasswordInputField::reset() {
CPasswordInputField* p = (CPasswordInputField*)data; if (fade.fadeOutTimer.get()) {
fade.fadeOutTimer->cancel();
fade.fadeOutTimer.reset();
}
p->onFadeOutTimer(); if (g_pHyprlock->m_bTerminate)
return;
if (placeholder.asset)
g_pRenderer->asyncResourceGatherer->unloadAsset(placeholder.asset);
placeholder.asset = nullptr;
placeholder.resourceID.clear();
placeholder.currentText.clear();
}
static void fadeOutCallback(WP<CPasswordInputField> ref) {
if (const auto PP = ref.lock(); PP)
PP->onFadeOutTimer();
} }
void CPasswordInputField::onFadeOutTimer() { void CPasswordInputField::onFadeOutTimer() {
@ -121,7 +152,8 @@ void CPasswordInputField::updateFade() {
*fade.a = 0.0; *fade.a = 0.0;
fade.allowFadeOut = false; fade.allowFadeOut = false;
} else if (!fade.fadeOutTimer.get()) } else if (!fade.fadeOutTimer.get())
fade.fadeOutTimer = g_pHyprlock->addTimer(std::chrono::milliseconds(fadeTimeoutMs), fadeOutCallback, this); fade.fadeOutTimer = g_pHyprlock->addTimer(std::chrono::milliseconds(fadeTimeoutMs), [REF = m_self](auto, auto) { fadeOutCallback(REF); }, nullptr);
} else if (INPUTUSED && fade.a->goal() != 1.0) } else if (INPUTUSED && fade.a->goal() != 1.0)
*fade.a = 1.0; *fade.a = 1.0;
@ -133,6 +165,9 @@ void CPasswordInputField::updateDots() {
if (dots.currentAmount->goal() == passwordLength) if (dots.currentAmount->goal() == passwordLength)
return; return;
if (checkWaiting)
return;
if (passwordLength == 0) if (passwordLength == 0)
dots.currentAmount->setValueAndWarp(passwordLength); dots.currentAmount->setValueAndWarp(passwordLength);
else else
@ -266,7 +301,7 @@ bool CPasswordInputField::draw(const SRenderData& data) {
} }
} }
if (passwordLength == 0 && !placeholder.resourceID.empty()) { if (passwordLength == 0 && !checkWaiting && !placeholder.resourceID.empty()) {
SPreloadedAsset* currAsset = nullptr; SPreloadedAsset* currAsset = nullptr;
if (!placeholder.asset) if (!placeholder.asset)
@ -291,13 +326,6 @@ bool CPasswordInputField::draw(const SRenderData& data) {
return redrawShadow || forceReload; return redrawShadow || forceReload;
} }
static void failTimeoutCallback(std::shared_ptr<CTimer> self, void* data) {
if (g_pAuth->m_bDisplayFailText) {
g_pAuth->m_bDisplayFailText = false;
g_pHyprlock->renderAllOutputs();
}
}
void CPasswordInputField::updatePlaceholder() { void CPasswordInputField::updatePlaceholder() {
if (passwordLength != 0) { if (passwordLength != 0) {
if (placeholder.asset && /* keep prompt asset cause it is likely to be used again */ displayFail) { if (placeholder.asset && /* keep prompt asset cause it is likely to be used again */ displayFail) {
@ -316,12 +344,7 @@ void CPasswordInputField::updatePlaceholder() {
placeholder.failedAttempts = g_pAuth->getFailedAttempts(); placeholder.failedAttempts = g_pAuth->getFailedAttempts();
std::string newText; std::string newText = (displayFail) ? formatString(configFailText).formatted : formatString(configPlaceholderText).formatted;
if (displayFail) {
g_pHyprlock->addTimer(std::chrono::milliseconds(configFailTimeoutMs), failTimeoutCallback, nullptr);
newText = formatString(configFailText).formatted;
} else
newText = formatString(configPlaceholderText).formatted;
// if the text is unchanged we don't need to do anything, unless we are swapping font color // if the text is unchanged we don't need to do anything, unless we are swapping font color
const auto ALLOWCOLORSWAP = outThick == 0 && colorConfig.swapFont; const auto ALLOWCOLORSWAP = outThick == 0 && colorConfig.swapFont;
@ -353,6 +376,10 @@ void CPasswordInputField::updatePlaceholder() {
request.props["font_family"] = fontFamily; request.props["font_family"] = fontFamily;
request.props["color"] = colorState.font; request.props["color"] = colorState.font;
request.props["font_size"] = (int)size->value().y / 4; request.props["font_size"] = (int)size->value().y / 4;
request.callback = [REF = m_self] {
if (const auto SELF = REF.lock(); SELF)
g_pHyprlock->renderOutput(SELF->outputStringPort);
};
g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request); g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
} }
@ -385,7 +412,7 @@ void CPasswordInputField::updateHiddenInputState() {
// randomize new thang // randomize new thang
hiddenInputState.lastPasswordLength = passwordLength; hiddenInputState.lastPasswordLength = passwordLength;
const auto BASEOK = colorConfig.outer->m_vColors.front().asOkLab(); const auto BASEOK = colorConfig.hiddenBase.asOkLab();
// convert to polar coordinates // convert to polar coordinates
const auto OKICHCHROMA = std::sqrt(std::pow(BASEOK.a, 2) + std::pow(BASEOK.b, 2)); const auto OKICHCHROMA = std::sqrt(std::pow(BASEOK.a, 2) + std::pow(BASEOK.b, 2));
@ -419,7 +446,7 @@ void CPasswordInputField::updateColors() {
if (checkWaiting) if (checkWaiting)
targetGrad = colorConfig.check; targetGrad = colorConfig.check;
else if (displayFail) else if (displayFail && passwordLength == 0)
targetGrad = colorConfig.fail; targetGrad = colorConfig.fail;
CGradientValueData* outerTarget = colorConfig.outer; CGradientValueData* outerTarget = colorConfig.outer;
@ -446,3 +473,14 @@ void CPasswordInputField::updateColors() {
colorState.font = fontTarget; colorState.font = fontTarget;
} }
CBox CPasswordInputField::getBoundingBoxWl() const {
return {
Vector2D{pos.x, viewport.y - pos.y - size->value().y},
size->value(),
};
}
void CPasswordInputField::onHover(const Vector2D& pos) {
g_pSeatManager->m_pCursorShape->setShape(WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_TEXT);
}

View file

@ -7,7 +7,7 @@
#include "Shadowable.hpp" #include "Shadowable.hpp"
#include "../../config/ConfigDataValues.hpp" #include "../../config/ConfigDataValues.hpp"
#include "../../helpers/AnimatedVariable.hpp" #include "../../helpers/AnimatedVariable.hpp"
#include <chrono> #include <hyprutils/math/Vector2D.hpp>
#include <vector> #include <vector>
#include <any> #include <any>
#include <unordered_map> #include <unordered_map>
@ -16,12 +16,22 @@ struct SPreloadedAsset;
class CPasswordInputField : public IWidget { class CPasswordInputField : public IWidget {
public: public:
CPasswordInputField(const Vector2D& viewport, const std::unordered_map<std::string, std::any>& props, const std::string& output); CPasswordInputField() = default;
virtual ~CPasswordInputField();
void registerSelf(const SP<CPasswordInputField>& self);
virtual void configure(const std::unordered_map<std::string, std::any>& prop, const SP<COutput>& pOutput);
virtual bool draw(const SRenderData& data); virtual bool draw(const SRenderData& data);
virtual void onHover(const Vector2D& pos);
virtual CBox getBoundingBoxWl() const;
void reset();
void onFadeOutTimer(); void onFadeOutTimer();
private: private:
WP<CPasswordInputField> m_self;
void updateDots(); void updateDots();
void updateFade(); void updateFade();
void updatePlaceholder(); void updatePlaceholder();
@ -74,7 +84,6 @@ class CPasswordInputField : public IWidget {
size_t failedAttempts = 0; size_t failedAttempts = 0;
std::vector<std::string> registeredResourceIDs; std::vector<std::string> registeredResourceIDs;
} placeholder; } placeholder;
struct { struct {
@ -94,6 +103,8 @@ class CPasswordInputField : public IWidget {
CGradientValueData* num = nullptr; CGradientValueData* num = nullptr;
CGradientValueData* both = nullptr; CGradientValueData* both = nullptr;
CHyprColor hiddenBase;
int transitionMs = 0; int transitionMs = 0;
bool invertNum = false; bool invertNum = false;
bool swapFont = false; bool swapFont = false;

View file

@ -2,7 +2,10 @@
#include "../Renderer.hpp" #include "../Renderer.hpp"
#include <hyprlang.hpp> #include <hyprlang.hpp>
CShadowable::CShadowable(IWidget* widget_, const std::unordered_map<std::string, std::any>& props, const Vector2D& viewport_) : widget(widget_), viewport(viewport_) { void CShadowable::configure(WP<IWidget> widget_, const std::unordered_map<std::string, std::any>& props, const Vector2D& viewport_) {
m_widget = widget_;
viewport = viewport_;
size = std::any_cast<Hyprlang::INT>(props.at("shadow_size")); size = std::any_cast<Hyprlang::INT>(props.at("shadow_size"));
passes = std::any_cast<Hyprlang::INT>(props.at("shadow_passes")); passes = std::any_cast<Hyprlang::INT>(props.at("shadow_passes"));
color = std::any_cast<Hyprlang::INT>(props.at("shadow_color")); color = std::any_cast<Hyprlang::INT>(props.at("shadow_color"));
@ -10,6 +13,10 @@ CShadowable::CShadowable(IWidget* widget_, const std::unordered_map<std::string,
} }
void CShadowable::markShadowDirty() { void CShadowable::markShadowDirty() {
const auto WIDGET = m_widget.lock();
if (!m_widget)
return;
if (passes == 0) if (passes == 0)
return; return;
@ -22,7 +29,7 @@ void CShadowable::markShadowDirty() {
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
ignoreDraw = true; ignoreDraw = true;
widget->draw(IWidget::SRenderData{.opacity = 1.0}); WIDGET->draw(IWidget::SRenderData{.opacity = 1.0});
ignoreDraw = false; ignoreDraw = false;
g_pRenderer->blurFB(shadowFB, CRenderer::SBlurParams{.size = size, .passes = passes, .colorize = color, .boostA = boostA}); g_pRenderer->blurFB(shadowFB, CRenderer::SBlurParams{.size = size, .passes = passes, .colorize = color, .boostA = boostA});
@ -31,7 +38,7 @@ void CShadowable::markShadowDirty() {
} }
bool CShadowable::draw(const IWidget::SRenderData& data) { bool CShadowable::draw(const IWidget::SRenderData& data) {
if (passes == 0) if (!m_widget || passes == 0)
return true; return true;
if (!shadowFB.isAllocated() || ignoreDraw) if (!shadowFB.isAllocated() || ignoreDraw)

View file

@ -11,14 +11,16 @@
class CShadowable { class CShadowable {
public: public:
CShadowable(IWidget* widget_, const std::unordered_map<std::string, std::any>& props, const Vector2D& viewport_ /* TODO: make this not the entire viewport */); virtual ~CShadowable() = default;
CShadowable() = default;
void configure(WP<IWidget> widget_, const std::unordered_map<std::string, std::any>& props, const Vector2D& viewport_ /* TODO: make this not the entire viewport */);
// instantly re-renders the shadow using the widget's draw() method // instantly re-renders the shadow using the widget's draw() method
void markShadowDirty(); void markShadowDirty();
virtual bool draw(const IWidget::SRenderData& data); virtual bool draw(const IWidget::SRenderData& data);
private: private:
IWidget* widget = nullptr; WP<IWidget> m_widget;
int size = 10; int size = 10;
int passes = 4; int passes = 4;
float boostA = 1.0; float boostA = 1.0;

View file

@ -1,29 +1,39 @@
#include "Shape.hpp" #include "Shape.hpp"
#include "../Renderer.hpp" #include "../Renderer.hpp"
#include "../../config/ConfigDataValues.hpp" #include "../../config/ConfigDataValues.hpp"
#include "../../core/hyprlock.hpp"
#include "../../helpers/MiscFunctions.hpp"
#include <cmath> #include <cmath>
#include <hyprlang.hpp> #include <hyprlang.hpp>
#include <sys/types.h>
CShape::CShape(const Vector2D& viewport_, const std::unordered_map<std::string, std::any>& props) : shadow(this, props, viewport_) { void CShape::registerSelf(const SP<CShape>& self) {
m_self = self;
}
void CShape::configure(const std::unordered_map<std::string, std::any>& props, const SP<COutput>& pOutput) {
viewport = pOutput->getViewport();
shadow.configure(m_self.lock(), props, viewport);
try { try {
size = CLayoutValueData::fromAnyPv(props.at("size"))->getAbsolute(viewport_); size = CLayoutValueData::fromAnyPv(props.at("size"))->getAbsolute(viewport);
rounding = std::any_cast<Hyprlang::INT>(props.at("rounding")); rounding = std::any_cast<Hyprlang::INT>(props.at("rounding"));
border = std::any_cast<Hyprlang::INT>(props.at("border_size")); border = std::any_cast<Hyprlang::INT>(props.at("border_size"));
color = std::any_cast<Hyprlang::INT>(props.at("color")); color = std::any_cast<Hyprlang::INT>(props.at("color"));
borderGrad = *CGradientValueData::fromAnyPv(props.at("border_color")); borderGrad = *CGradientValueData::fromAnyPv(props.at("border_color"));
pos = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport_); pos = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport);
halign = std::any_cast<Hyprlang::STRING>(props.at("halign")); halign = std::any_cast<Hyprlang::STRING>(props.at("halign"));
valign = std::any_cast<Hyprlang::STRING>(props.at("valign")); valign = std::any_cast<Hyprlang::STRING>(props.at("valign"));
angle = std::any_cast<Hyprlang::FLOAT>(props.at("rotate")); angle = std::any_cast<Hyprlang::FLOAT>(props.at("rotate"));
xray = std::any_cast<Hyprlang::INT>(props.at("xray")); xray = std::any_cast<Hyprlang::INT>(props.at("xray"));
onclickCommand = std::any_cast<Hyprlang::STRING>(props.at("onclick"));
} catch (const std::bad_any_cast& e) { } catch (const std::bad_any_cast& e) {
RASSERT(false, "Failed to construct CShape: {}", e.what()); // RASSERT(false, "Failed to construct CShape: {}", e.what()); //
} catch (const std::out_of_range& e) { } catch (const std::out_of_range& e) {
RASSERT(false, "Missing property for CShape: {}", e.what()); // RASSERT(false, "Missing property for CShape: {}", e.what()); //
} }
viewport = viewport_;
angle = angle * M_PI / 180.0; angle = angle * M_PI / 180.0;
const Vector2D VBORDER = {border, border}; const Vector2D VBORDER = {border, border};
@ -94,3 +104,19 @@ bool CShape::draw(const SRenderData& data) {
return data.opacity < 1.0; return data.opacity < 1.0;
} }
CBox CShape::getBoundingBoxWl() const {
return {
Vector2D{pos.x, viewport.y - pos.y - size.y},
size,
};
}
void CShape::onClick(uint32_t button, bool down, const Vector2D& pos) {
if (down && !onclickCommand.empty())
spawnAsync(onclickCommand);
}
void CShape::onHover(const Vector2D& pos) {
if (!onclickCommand.empty())
g_pSeatManager->m_pCursorShape->setShape(WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER);
}

View file

@ -11,11 +11,20 @@
class CShape : public IWidget { class CShape : public IWidget {
public: public:
CShape(const Vector2D& viewport, const std::unordered_map<std::string, std::any>& props); CShape() = default;
virtual ~CShape() = default;
void registerSelf(const SP<CShape>& self);
virtual void configure(const std::unordered_map<std::string, std::any>& prop, const SP<COutput>& pOutput);
virtual bool draw(const SRenderData& data); virtual bool draw(const SRenderData& data);
virtual CBox getBoundingBoxWl() const;
virtual void onClick(uint32_t button, bool down, const Vector2D& pos);
virtual void onHover(const Vector2D& pos);
private: private:
WP<CShape> m_self;
CFramebuffer shapeFB; CFramebuffer shapeFB;
int rounding; int rounding;
@ -32,6 +41,7 @@ class CShape : public IWidget {
std::string halign, valign; std::string halign, valign;
bool firstRender = true; bool firstRender = true;
std::string onclickCommand;
Vector2D viewport; Vector2D viewport;
CShadowable shadow; CShadowable shadow;