mirror of
https://github.com/hyprwm/hyprlock.git
synced 2025-12-20 08:50:03 +01:00
Compare commits
42 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d099f87d36 | ||
|
|
e2da7c6b1f | ||
|
|
98b86752fe | ||
|
|
36ec73f166 | ||
|
|
bdc44ab5d6 | ||
|
|
de2cc5bd54 | ||
|
|
c8a6768dca | ||
|
|
3cb799b184 | ||
| c48279d1e0 | |||
|
|
7f769fa993 | ||
|
|
1380ca04ae | ||
|
|
61b36c64a8 | ||
|
|
a7f2634a9e | ||
|
|
450ae1e5f0 | ||
|
|
04cfdc4e5b | ||
|
|
cedbb24472 | ||
|
|
8d0e56998e | ||
|
|
a356bf055b | ||
|
|
347e05a40e | ||
| bdf0ef8282 | |||
|
|
71691634e4 | ||
|
|
1e5e62d6e3 | ||
|
|
d993bdc105 | ||
|
|
8ebcee2969 | ||
|
|
1553dd78fc | ||
| 31297a871b | |||
|
|
17fefce86e | ||
|
|
46498da4a0 | ||
|
|
fee04f6406 | ||
|
|
d84b44e695 | ||
|
|
7999f448d7 | ||
|
|
a9638986c3 | ||
| e67036e8cc | |||
|
|
f9d8dfab7a | ||
|
|
1303bb4b7e | ||
|
|
8455fc8ca6 | ||
|
|
da1d076d84 | ||
|
|
f7f0c9c6b0 | ||
|
|
c12cf8e509 | ||
|
|
0c5fd97d61 | ||
|
|
fae1c4f6fe | ||
|
|
e3bd47e177 |
58 changed files with 1665 additions and 1287 deletions
30
.github/workflows/nix.yml
vendored
30
.github/workflows/nix.yml
vendored
|
|
@ -7,7 +7,35 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- uses: DeterminateSystems/nix-installer-action@main
|
- name: Install Nix
|
||||||
|
uses: nixbuild/nix-quick-install-action@v31
|
||||||
|
with:
|
||||||
|
nix_conf: |
|
||||||
|
keep-env-derivations = true
|
||||||
|
keep-outputs = true
|
||||||
|
|
||||||
|
- name: Restore and save Nix store
|
||||||
|
uses: nix-community/cache-nix-action@v6
|
||||||
|
with:
|
||||||
|
# restore and save a cache using this key
|
||||||
|
primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/flake.lock') }}
|
||||||
|
# if there's no cache hit, restore a cache by this prefix
|
||||||
|
restore-prefixes-first-match: nix-${{ runner.os }}-
|
||||||
|
# collect garbage until the Nix store size (in bytes) is at most this number
|
||||||
|
# before trying to save a new cache
|
||||||
|
# 1G = 1073741824
|
||||||
|
gc-max-store-size-linux: 1G
|
||||||
|
# do purge caches
|
||||||
|
purge: true
|
||||||
|
# purge all versions of the cache
|
||||||
|
purge-prefixes: nix-${{ runner.os }}-
|
||||||
|
# created more than this number of seconds ago
|
||||||
|
purge-created: 0
|
||||||
|
# or, last accessed more than this number of seconds ago
|
||||||
|
# relative to the start of the `Post Restore and save Nix store` phase
|
||||||
|
purge-last-accessed: 0
|
||||||
|
# except any version with the key that is the same as the `primary-key`
|
||||||
|
purge-primary-key: never
|
||||||
|
|
||||||
# not needed (yet)
|
# not needed (yet)
|
||||||
# - uses: cachix/cachix-action@v12
|
# - uses: cachix/cachix-action@v12
|
||||||
|
|
|
||||||
|
|
@ -82,20 +82,30 @@ pkg_check_modules(
|
||||||
hyprlang>=0.6.0
|
hyprlang>=0.6.0
|
||||||
egl
|
egl
|
||||||
xkbcommon
|
xkbcommon
|
||||||
libjpeg
|
|
||||||
libwebp
|
|
||||||
libmagic
|
|
||||||
cairo
|
cairo
|
||||||
pangocairo
|
pangocairo
|
||||||
libdrm
|
libdrm
|
||||||
gbm
|
gbm
|
||||||
hyprutils>=0.5.0
|
hyprutils>=0.11.0
|
||||||
sdbus-c++>=2.0.0
|
sdbus-c++>=2.0.0
|
||||||
hyprgraphics)
|
hyprgraphics>=0.1.6)
|
||||||
|
find_library(PAM_FOUND NAMES pam libpam)
|
||||||
|
if(PAM_FOUND)
|
||||||
|
set(PAM_LIB ${PAM_FOUND})
|
||||||
|
else()
|
||||||
|
pkg_check_modules(PAM IMPORTED_TARGET pam libpam)
|
||||||
|
if(PAM_FOUND)
|
||||||
|
set(PAM_LIB PkgConfig::PAM)
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "The required library libpam was not found.")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message(STATUS "Found pam at ${PAM_LIB}")
|
||||||
|
|
||||||
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
|
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
|
||||||
add_executable(hyprlock ${SRCFILES})
|
add_executable(hyprlock ${SRCFILES})
|
||||||
target_link_libraries(hyprlock PRIVATE pam rt Threads::Threads PkgConfig::deps
|
target_link_libraries(hyprlock PRIVATE ${PAM_LIB} rt Threads::Threads PkgConfig::deps
|
||||||
OpenGL::EGL OpenGL::GLES3)
|
OpenGL::EGL OpenGL::GLES3)
|
||||||
|
|
||||||
# protocols
|
# protocols
|
||||||
|
|
|
||||||
38
README.md
38
README.md
|
|
@ -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,22 @@ 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
|
|
||||||
- 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
|
||||||
|
|
||||||
|
|
|
||||||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
0.8.2
|
0.9.2
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,14 @@
|
||||||
# ref. https://wiki.hyprland.org/Hypr-Ecosystem/hyprlock/#general-remarks
|
# ref. https://wiki.hyprland.org/Hypr-Ecosystem/hyprlock/#general-remarks
|
||||||
#
|
#
|
||||||
# shortcuts to clear password buffer: ESC, Ctrl+U, Ctrl+Backspace
|
# 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
|
$font = Monospace
|
||||||
|
|
||||||
general {
|
general {
|
||||||
hide_cursor = true
|
hide_cursor = false
|
||||||
}
|
}
|
||||||
|
|
||||||
# uncomment to enable fingerprint authentication
|
# uncomment to enable fingerprint authentication
|
||||||
|
|
@ -90,3 +93,14 @@ label {
|
||||||
halign = right
|
halign = right
|
||||||
valign = top
|
valign = top
|
||||||
}
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
monitor =
|
||||||
|
text = $LAYOUT[en,ru]
|
||||||
|
font_size = 24
|
||||||
|
onclick = hyprctl switchxkblayout all next
|
||||||
|
|
||||||
|
position = 250, -20
|
||||||
|
halign = center
|
||||||
|
valign = center
|
||||||
|
}
|
||||||
|
|
|
||||||
30
flake.lock
generated
30
flake.lock
generated
|
|
@ -13,11 +13,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1743953322,
|
"lastModified": 1763733840,
|
||||||
"narHash": "sha256-prQ5JKopXtzCMX2eT3dXbaVvGmzjMRE2bXStQDdazpM=",
|
"narHash": "sha256-JnET78yl5RvpGuDQy3rCycOCkiKoLr5DN1fPhRNNMco=",
|
||||||
"owner": "hyprwm",
|
"owner": "hyprwm",
|
||||||
"repo": "hyprgraphics",
|
"repo": "hyprgraphics",
|
||||||
"rev": "9d7f2687c84c729afbc3b13f7937655570f2978d",
|
"rev": "8f1bec691b2d198c60cccabca7a94add2df4ed1a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -39,11 +39,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1744468525,
|
"lastModified": 1764612430,
|
||||||
"narHash": "sha256-9HySx+EtsbbKlZDlY+naqqOV679VdxP6x6fP3wxDXJk=",
|
"narHash": "sha256-54ltTSbI6W+qYGMchAgCR6QnC1kOdKXN6X6pJhOWxFg=",
|
||||||
"owner": "hyprwm",
|
"owner": "hyprwm",
|
||||||
"repo": "hyprlang",
|
"repo": "hyprlang",
|
||||||
"rev": "f1000c54d266e6e4e9d646df0774fac5b8a652df",
|
"rev": "0d00dc118981531aa731150b6ea551ef037acddd",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -62,11 +62,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1743950287,
|
"lastModified": 1764962281,
|
||||||
"narHash": "sha256-/6IAEWyb8gC/NKZElxiHChkouiUOrVYNq9YqG0Pzm4Y=",
|
"narHash": "sha256-rGbEMhTTyTzw4iyz45lch5kXseqnqcEpmrHdy+zHsfo=",
|
||||||
"owner": "hyprwm",
|
"owner": "hyprwm",
|
||||||
"repo": "hyprutils",
|
"repo": "hyprutils",
|
||||||
"rev": "f2dc70e448b994cef627a157ee340135bd68fbc6",
|
"rev": "fe686486ac867a1a24f99c753bb40ffed338e4b0",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -85,11 +85,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1739870480,
|
"lastModified": 1763640274,
|
||||||
"narHash": "sha256-SiDN5BGxa/1hAsqhgJsS03C3t2QrLgBT8u+ENJ0Qzwc=",
|
"narHash": "sha256-Uan1Nl9i4TF/kyFoHnTq1bd/rsWh4GAK/9/jDqLbY5A=",
|
||||||
"owner": "hyprwm",
|
"owner": "hyprwm",
|
||||||
"repo": "hyprwayland-scanner",
|
"repo": "hyprwayland-scanner",
|
||||||
"rev": "206367a08dc5ac4ba7ad31bdca391d098082e64b",
|
"rev": "f6cf414ca0e16a4d30198fd670ec86df3c89f671",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -100,11 +100,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1744463964,
|
"lastModified": 1765186076,
|
||||||
"narHash": "sha256-LWqduOgLHCFxiTNYi3Uj5Lgz0SR+Xhw3kr/3Xd0GPTM=",
|
"narHash": "sha256-hM20uyap1a0M9d344I692r+ik4gTMyj60cQWO+hAYP8=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "2631b0b7abcea6e640ce31cd78ea58910d31e650",
|
"rev": "addf7cf5f383a3101ecfba091b98d0a1263dc9b8",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,8 @@
|
||||||
cmake,
|
cmake,
|
||||||
pkg-config,
|
pkg-config,
|
||||||
cairo,
|
cairo,
|
||||||
file,
|
|
||||||
libdrm,
|
libdrm,
|
||||||
libGL,
|
libGL,
|
||||||
libjpeg,
|
|
||||||
libwebp,
|
|
||||||
libxkbcommon,
|
libxkbcommon,
|
||||||
libgbm,
|
libgbm,
|
||||||
hyprgraphics,
|
hyprgraphics,
|
||||||
|
|
@ -40,11 +37,8 @@ stdenv.mkDerivation {
|
||||||
|
|
||||||
buildInputs = [
|
buildInputs = [
|
||||||
cairo
|
cairo
|
||||||
file
|
|
||||||
libdrm
|
libdrm
|
||||||
libGL
|
libGL
|
||||||
libjpeg
|
|
||||||
libwebp
|
|
||||||
libxkbcommon
|
libxkbcommon
|
||||||
libgbm
|
libgbm
|
||||||
hyprgraphics
|
hyprgraphics
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ in {
|
||||||
inputs.self.overlays.sdbuscpp
|
inputs.self.overlays.sdbuscpp
|
||||||
(final: prev: {
|
(final: prev: {
|
||||||
hyprlock = prev.callPackage ./default.nix {
|
hyprlock = prev.callPackage ./default.nix {
|
||||||
stdenv = prev.gcc14Stdenv;
|
stdenv = prev.gcc15Stdenv;
|
||||||
version = version + "+date=" + (mkDate (inputs.self.lastModifiedDate or "19700101")) + "_" + (inputs.self.shortRev or "dirty");
|
version = version + "+date=" + (mkDate (inputs.self.lastModifiedDate or "19700101")) + "_" + (inputs.self.shortRev or "dirty");
|
||||||
inherit (final) hyprlang;
|
inherit (final) hyprlang;
|
||||||
shortRev = self.sourceInfo.shortRev or "dirty";
|
shortRev = self.sourceInfo.shortRev or "dirty";
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ void CAuth::terminate() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void unlockCallback(std::shared_ptr<CTimer> self, void* data) {
|
static void unlockCallback(ASP<CTimer> self, void* data) {
|
||||||
g_pHyprlock->unlock();
|
g_pHyprlock->unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -84,7 +84,7 @@ void CAuth::enqueueUnlock() {
|
||||||
g_pHyprlock->addTimer(std::chrono::milliseconds(0), unlockCallback, nullptr);
|
g_pHyprlock->addTimer(std::chrono::milliseconds(0), unlockCallback, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void passwordFailCallback(std::shared_ptr<CTimer> self, void* data) {
|
static void passwordFailCallback(ASP<CTimer> self, void* data) {
|
||||||
g_pAuth->m_bDisplayFailText = true;
|
g_pAuth->m_bDisplayFailText = true;
|
||||||
|
|
||||||
g_pHyprlock->enqueueForceUpdateTimers();
|
g_pHyprlock->enqueueForceUpdateTimers();
|
||||||
|
|
@ -92,7 +92,7 @@ static void passwordFailCallback(std::shared_ptr<CTimer> self, void* data) {
|
||||||
g_pHyprlock->renderAllOutputs();
|
g_pHyprlock->renderAllOutputs();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void displayFailTimeoutCallback(std::shared_ptr<CTimer> self, void* data) {
|
static void displayFailTimeoutCallback(ASP<CTimer> self, void* data) {
|
||||||
if (g_pAuth->m_bDisplayFailText) {
|
if (g_pAuth->m_bDisplayFailText) {
|
||||||
g_pAuth->m_bDisplayFailText = false;
|
g_pAuth->m_bDisplayFailText = false;
|
||||||
g_pHyprlock->renderAllOutputs();
|
g_pHyprlock->renderAllOutputs();
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ class CAuth {
|
||||||
} m_sCurrentFail;
|
} m_sCurrentFail;
|
||||||
|
|
||||||
std::vector<SP<IAuthImplementation>> m_vImpls;
|
std::vector<SP<IAuthImplementation>> m_vImpls;
|
||||||
std::shared_ptr<CTimer> m_resetDisplayFailTimer;
|
ASP<CTimer> m_resetDisplayFailTimer;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline UP<CAuth> g_pAuth;
|
inline UP<CAuth> g_pAuth;
|
||||||
|
|
|
||||||
|
|
@ -158,7 +158,7 @@ void CFingerprint::handleVerifyStatus(const std::string& result, bool done) {
|
||||||
} else {
|
} else {
|
||||||
done = false;
|
done = false;
|
||||||
static const auto RETRYDELAY = g_pConfigManager->getValue<Hyprlang::INT>("auth:fingerprint:retry_delay");
|
static const auto RETRYDELAY = g_pConfigManager->getValue<Hyprlang::INT>("auth:fingerprint:retry_delay");
|
||||||
g_pHyprlock->addTimer(std::chrono::milliseconds(*RETRYDELAY), [](std::shared_ptr<CTimer> self, void* data) { ((CFingerprint*)data)->startVerify(true); }, this);
|
g_pHyprlock->addTimer(std::chrono::milliseconds(*RETRYDELAY), [](ASP<CTimer> self, void* data) { ((CFingerprint*)data)->startVerify(true); }, this);
|
||||||
m_sFailureReason = "Fingerprint did not match";
|
m_sFailureReason = "Fingerprint did not match";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -209,9 +209,11 @@ 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});
|
||||||
|
|
||||||
|
#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:ignore_empty_input", Hyprlang::INT{0});
|
m_config.addConfigValue("general:ignore_empty_input", Hyprlang::INT{0});
|
||||||
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});
|
||||||
|
|
@ -257,6 +259,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 +276,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{""});
|
||||||
|
|
@ -320,6 +324,7 @@ void CConfigManager::init() {
|
||||||
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 +361,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 +373,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 +422,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 +449,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
|
||||||
|
|
@ -505,6 +515,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
|
||||||
|
|
|
||||||
|
|
@ -78,8 +78,7 @@ void updateGradientVariable(CAnimatedVariable<CGradientValueData>& av, const flo
|
||||||
|
|
||||||
void CHyprlockAnimationManager::tick() {
|
void CHyprlockAnimationManager::tick() {
|
||||||
static const auto ANIMATIONSENABLED = g_pConfigManager->getValue<Hyprlang::INT>("animations:enabled");
|
static const auto ANIMATIONSENABLED = g_pConfigManager->getValue<Hyprlang::INT>("animations:enabled");
|
||||||
for (size_t i = 0; i < m_vActiveAnimatedVariables.size(); i++) {
|
for (const auto& PAV : m_vActiveAnimatedVariables) {
|
||||||
const auto PAV = m_vActiveAnimatedVariables[i].lock();
|
|
||||||
if (!PAV || !PAV->ok())
|
if (!PAV || !PAV->ok())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,10 @@ class CHyprlockAnimationManager : public Hyprutils::Animation::CAnimationManager
|
||||||
template <Animable VarType>
|
template <Animable VarType>
|
||||||
void createAnimation(const VarType& v, PHLANIMVAR<VarType>& pav, SP<SAnimationPropertyConfig> pConfig) {
|
void createAnimation(const VarType& v, PHLANIMVAR<VarType>& pav, SP<SAnimationPropertyConfig> pConfig) {
|
||||||
constexpr const eAnimatedVarType EAVTYPE = typeToeAnimatedVarType<VarType>;
|
constexpr const eAnimatedVarType EAVTYPE = typeToeAnimatedVarType<VarType>;
|
||||||
const auto PAV = makeShared<CAnimatedVariable<VarType>>();
|
pav = makeUnique<CAnimatedVariable<VarType>>();
|
||||||
|
|
||||||
PAV->create(EAVTYPE, static_cast<Hyprutils::Animation::CAnimationManager*>(this), PAV, v);
|
pav->create2(EAVTYPE, static_cast<Hyprutils::Animation::CAnimationManager*>(this), pav, v);
|
||||||
PAV->setConfig(pConfig);
|
pav->setConfig(pConfig);
|
||||||
|
|
||||||
pav = std::move(PAV);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool m_bTickScheduled = false;
|
bool m_bTickScheduled = false;
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,13 @@ 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()));
|
||||||
|
|
||||||
|
shapeChanged = true;
|
||||||
dev->sendSetShape(lastCursorSerial, shape);
|
dev->sendSetShape(lastCursorSerial, shape);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ class CCursorShape {
|
||||||
void hideCursor();
|
void hideCursor();
|
||||||
|
|
||||||
uint32_t lastCursorSerial = 0;
|
uint32_t lastCursorSerial = 0;
|
||||||
|
bool shapeChanged = false;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SP<CCWpCursorShapeManagerV1> mgr = nullptr;
|
SP<CCWpCursorShapeManagerV1> mgr = nullptr;
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ CEGL::CEGL(wl_display* display) {
|
||||||
if (eglCreatePlatformWindowSurfaceEXT == nullptr)
|
if (eglCreatePlatformWindowSurfaceEXT == nullptr)
|
||||||
throw std::runtime_error("Failed to get eglCreatePlatformWindowSurfaceEXT");
|
throw std::runtime_error("Failed to get eglCreatePlatformWindowSurfaceEXT");
|
||||||
|
|
||||||
|
const char* vendorString = nullptr;
|
||||||
eglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_WAYLAND_EXT, display, nullptr);
|
eglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_WAYLAND_EXT, display, nullptr);
|
||||||
EGLint matched = 0;
|
EGLint matched = 0;
|
||||||
if (eglDisplay == EGL_NO_DISPLAY) {
|
if (eglDisplay == EGL_NO_DISPLAY) {
|
||||||
|
|
@ -65,6 +66,9 @@ CEGL::CEGL(wl_display* display) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vendorString = eglQueryString(eglDisplay, EGL_VENDOR);
|
||||||
|
m_isNvidia = (vendorString) ? std::string{vendorString}.contains("NVIDIA") : false;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ class CEGL {
|
||||||
PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC eglCreatePlatformWindowSurfaceEXT;
|
PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC eglCreatePlatformWindowSurfaceEXT;
|
||||||
|
|
||||||
void makeCurrent(EGLSurface surf);
|
void makeCurrent(EGLSurface surf);
|
||||||
|
|
||||||
|
bool m_isNvidia = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline UP<CEGL> g_pEGL;
|
inline UP<CEGL> g_pEGL;
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,12 @@
|
||||||
#include "../renderer/Renderer.hpp"
|
#include "../renderer/Renderer.hpp"
|
||||||
|
|
||||||
CSessionLockSurface::~CSessionLockSurface() {
|
CSessionLockSurface::~CSessionLockSurface() {
|
||||||
|
if (frameCallback)
|
||||||
|
frameCallback.reset();
|
||||||
|
|
||||||
|
if (eglSurface)
|
||||||
|
eglDestroySurface(g_pEGL->eglDisplay, eglSurface);
|
||||||
|
|
||||||
if (eglWindow)
|
if (eglWindow)
|
||||||
wl_egl_window_destroy(eglWindow);
|
wl_egl_window_destroy(eglWindow);
|
||||||
}
|
}
|
||||||
|
|
@ -139,3 +145,7 @@ void CSessionLockSurface::onCallback() {
|
||||||
render();
|
render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SP<CCWlSurface> CSessionLockSurface::getWlSurface() {
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ class CSessionLockSurface {
|
||||||
void render();
|
void render();
|
||||||
void onCallback();
|
void onCallback();
|
||||||
void onScaleUpdate();
|
void onScaleUpdate();
|
||||||
|
SP<CCWlSurface> getWlSurface();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
WP<COutput> m_outputRef;
|
WP<COutput> m_outputRef;
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,12 @@
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <linux/input-event-codes.h>
|
||||||
|
|
||||||
CSeatManager::~CSeatManager() {
|
CSeatManager::~CSeatManager() {
|
||||||
|
if (m_pCursorShape && m_pCursorShape->shapeChanged)
|
||||||
|
m_pCursorShape->setShape(wpCursorShapeDeviceV1Shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT);
|
||||||
|
|
||||||
if (m_pXKBState)
|
if (m_pXKBState)
|
||||||
xkb_state_unref(m_pXKBState);
|
xkb_state_unref(m_pXKBState);
|
||||||
if (m_pXKBKeymap)
|
if (m_pXKBKeymap)
|
||||||
|
|
@ -26,7 +30,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,19 +50,43 @@ 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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (caps & WL_SEAT_CAPABILITY_TOUCH) {
|
||||||
|
m_pTouch = makeShared<CCWlTouch>(r->sendGetTouch());
|
||||||
|
m_pTouch->setDown([](CCWlTouch* r, uint32_t serial, uint32_t time, wl_proxy* surface, int32_t id, wl_fixed_t x, wl_fixed_t y) {
|
||||||
|
g_pHyprlock->onClick(BTN_LEFT, true, {wl_fixed_to_double(x), wl_fixed_to_double(y)});
|
||||||
|
});
|
||||||
|
m_pTouch->setUp([](CCWlTouch* r, uint32_t serial, uint32_t time, int32_t id) { g_pHyprlock->onClick(BTN_LEFT, false, {0, 0}); });
|
||||||
|
};
|
||||||
if (caps & WL_SEAT_CAPABILITY_KEYBOARD) {
|
if (caps & WL_SEAT_CAPABILITY_KEYBOARD) {
|
||||||
m_pKeeb = makeShared<CCWlKeyboard>(r->sendGetKeyboard());
|
m_pKeeb = makeShared<CCWlKeyboard>(r->sendGetKeyboard());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ class CSeatManager {
|
||||||
|
|
||||||
SP<CCWlKeyboard> m_pKeeb;
|
SP<CCWlKeyboard> m_pKeeb;
|
||||||
SP<CCWlPointer> m_pPointer;
|
SP<CCWlPointer> m_pPointer;
|
||||||
|
SP<CCWlTouch> m_pTouch;
|
||||||
|
|
||||||
UP<CCursorShape> m_pCursorShape;
|
UP<CCursorShape> m_pCursorShape;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#include "Timer.hpp"
|
#include "Timer.hpp"
|
||||||
|
|
||||||
CTimer::CTimer(std::chrono::system_clock::duration timeout, std::function<void(std::shared_ptr<CTimer> self, void* data)> cb_, void* data_, bool force) :
|
CTimer::CTimer(std::chrono::system_clock::duration timeout, std::function<void(ASP<CTimer> self, void* data)> cb_, void* data_, bool force) :
|
||||||
cb(cb_), data(data_), allowForceUpdate(force) {
|
cb(cb_), data(data_), allowForceUpdate(force) {
|
||||||
expires = std::chrono::system_clock::now() + timeout;
|
expires = std::chrono::system_clock::now() + timeout;
|
||||||
}
|
}
|
||||||
|
|
@ -17,7 +17,7 @@ bool CTimer::cancelled() {
|
||||||
return wasCancelled;
|
return wasCancelled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CTimer::call(std::shared_ptr<CTimer> self) {
|
void CTimer::call(ASP<CTimer> self) {
|
||||||
cb(self, data);
|
cb(self, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,11 @@
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include "../defines.hpp"
|
||||||
|
|
||||||
class CTimer {
|
class CTimer {
|
||||||
public:
|
public:
|
||||||
CTimer(std::chrono::system_clock::duration timeout, std::function<void(std::shared_ptr<CTimer> self, void* data)> cb_, void* data_, bool force);
|
CTimer(std::chrono::system_clock::duration timeout, std::function<void(ASP<CTimer> self, void* data)> cb_, void* data_, bool force);
|
||||||
|
|
||||||
void cancel();
|
void cancel();
|
||||||
bool passed();
|
bool passed();
|
||||||
|
|
@ -14,10 +15,10 @@ class CTimer {
|
||||||
float leftMs();
|
float leftMs();
|
||||||
|
|
||||||
bool cancelled();
|
bool cancelled();
|
||||||
void call(std::shared_ptr<CTimer> self);
|
void call(ASP<CTimer> self);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::function<void(std::shared_ptr<CTimer> self, void* data)> cb;
|
std::function<void(ASP<CTimer> self, void* data)> cb;
|
||||||
void* data = nullptr;
|
void* data = nullptr;
|
||||||
std::chrono::system_clock::time_point expires;
|
std::chrono::system_clock::time_point expires;
|
||||||
bool wasCancelled = false;
|
bool wasCancelled = false;
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
#include "hyprlock.hpp"
|
#include "hyprlock.hpp"
|
||||||
#include "AnimationManager.hpp"
|
|
||||||
#include "../helpers/Log.hpp"
|
#include "../helpers/Log.hpp"
|
||||||
#include "../config/ConfigManager.hpp"
|
#include "../config/ConfigManager.hpp"
|
||||||
#include "../renderer/Renderer.hpp"
|
#include "../renderer/Renderer.hpp"
|
||||||
|
#include "../renderer/AsyncResourceManager.hpp"
|
||||||
#include "../auth/Auth.hpp"
|
#include "../auth/Auth.hpp"
|
||||||
#include "../auth/Fingerprint.hpp"
|
#include "../auth/Fingerprint.hpp"
|
||||||
#include "Egl.hpp"
|
#include "./Egl.hpp"
|
||||||
|
#include "./Seat.hpp"
|
||||||
|
#include <chrono>
|
||||||
#include <hyprutils/memory/UniquePtr.hpp>
|
#include <hyprutils/memory/UniquePtr.hpp>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <sys/poll.h>
|
#include <sys/poll.h>
|
||||||
|
|
@ -16,8 +18,6 @@
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <xf86drm.h>
|
#include <xf86drm.h>
|
||||||
#include <filesystem>
|
|
||||||
#include <fstream>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <sdbus-c++/sdbus-c++.h>
|
#include <sdbus-c++/sdbus-c++.h>
|
||||||
#include <hyprutils/os/Process.hpp>
|
#include <hyprutils/os/Process.hpp>
|
||||||
|
|
@ -35,7 +35,7 @@ static void setMallocThreshold() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
CHyprlock::CHyprlock(const std::string& wlDisplay, const bool immediate, const bool immediateRender) {
|
CHyprlock::CHyprlock(const std::string& wlDisplay, const bool immediateRender, const int graceSeconds) {
|
||||||
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());
|
||||||
|
|
@ -43,10 +43,9 @@ CHyprlock::CHyprlock(const std::string& wlDisplay, const bool immediate, const b
|
||||||
|
|
||||||
g_pEGL = makeUnique<CEGL>(m_sWaylandState.display);
|
g_pEGL = makeUnique<CEGL>(m_sWaylandState.display);
|
||||||
|
|
||||||
if (!immediate) {
|
if (graceSeconds > 0)
|
||||||
static const auto GRACE = g_pConfigManager->getValue<Hyprlang::INT>("general:grace");
|
m_tGraceEnds = std::chrono::system_clock::now() + std::chrono::seconds(graceSeconds);
|
||||||
m_tGraceEnds = *GRACE ? std::chrono::system_clock::now() + std::chrono::seconds(*GRACE) : std::chrono::system_clock::from_time_t(0);
|
else
|
||||||
} else
|
|
||||||
m_tGraceEnds = std::chrono::system_clock::from_time_t(0);
|
m_tGraceEnds = std::chrono::system_clock::from_time_t(0);
|
||||||
|
|
||||||
static const auto IMMEDIATERENDER = g_pConfigManager->getValue<Hyprlang::INT>("general:immediate_render");
|
static const auto IMMEDIATERENDER = g_pConfigManager->getValue<Hyprlang::INT>("general:immediate_render");
|
||||||
|
|
@ -238,6 +237,18 @@ void CHyprlock::addDmabufListener() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CHyprlock::removeDmabufListener() {
|
||||||
|
if (dma.linuxDmabufFeedback) {
|
||||||
|
dma.linuxDmabufFeedback->sendDestroy();
|
||||||
|
dma.linuxDmabufFeedback.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dma.linuxDmabuf) {
|
||||||
|
dma.linuxDmabuf->sendDestroy();
|
||||||
|
dma.linuxDmabuf.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CHyprlock::run() {
|
void CHyprlock::run() {
|
||||||
m_sWaylandState.registry = makeShared<CCWlRegistry>((wl_proxy*)wl_display_get_registry(m_sWaylandState.display));
|
m_sWaylandState.registry = makeShared<CCWlRegistry>((wl_proxy*)wl_display_get_registry(m_sWaylandState.display));
|
||||||
m_sWaylandState.registry->setGlobal([this](CCWlRegistry* r, uint32_t name, const char* interface, uint32_t version) {
|
m_sWaylandState.registry->setGlobal([this](CCWlRegistry* r, uint32_t name, const char* interface, uint32_t version) {
|
||||||
|
|
@ -308,31 +319,25 @@ void CHyprlock::run() {
|
||||||
wl_display_roundtrip(m_sWaylandState.display);
|
wl_display_roundtrip(m_sWaylandState.display);
|
||||||
|
|
||||||
g_pRenderer = makeUnique<CRenderer>();
|
g_pRenderer = makeUnique<CRenderer>();
|
||||||
|
g_asyncResourceManager = makeUnique<CAsyncResourceManager>();
|
||||||
g_pAuth = makeUnique<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);
|
||||||
|
|
||||||
// Hyprland violates the protocol a bit to allow for this.
|
g_asyncResourceManager->enqueueStaticAssets();
|
||||||
if (m_sCurrentDesktop != "Hyprland") {
|
g_asyncResourceManager->enqueueScreencopyFrames();
|
||||||
while (!g_pRenderer->asyncResourceGatherer->gathered) {
|
|
||||||
wl_display_flush(m_sWaylandState.display);
|
if (!g_pHyprlock->m_bImmediateRender)
|
||||||
if (wl_display_prepare_read(m_sWaylandState.display) == 0) {
|
// Gather background resources and screencopy frames before locking the screen.
|
||||||
wl_display_read_events(m_sWaylandState.display);
|
// We need to do this because as soon as we lock the screen, workspaces frames can no longer be captured. It either won't work at all, or we will capture hyprlock itself.
|
||||||
wl_display_dispatch_pending(m_sWaylandState.display);
|
// Bypass with --immediate-render (can cause the background first rendering a solid color and missing or inaccurate screencopy frames)
|
||||||
} else {
|
g_asyncResourceManager->gatherInitialResources(m_sWaylandState.display);
|
||||||
wl_display_dispatch(m_sWaylandState.display);
|
|
||||||
}
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Failed to lock the session
|
// Failed to lock the session
|
||||||
if (!acquireSessionLock()) {
|
if (!acquireSessionLock()) {
|
||||||
m_sLoopState.timerEvent = true;
|
m_sLoopState.timerEvent = true;
|
||||||
m_sLoopState.timerCV.notify_all();
|
m_sLoopState.timerCV.notify_all();
|
||||||
g_pRenderer->asyncResourceGatherer->notify();
|
|
||||||
g_pRenderer->asyncResourceGatherer->await();
|
|
||||||
g_pAuth->terminate();
|
g_pAuth->terminate();
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
@ -442,43 +447,21 @@ void CHyprlock::run() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// do timers
|
processTimers();
|
||||||
m_sLoopState.timersMutex.lock();
|
|
||||||
auto timerscpy = m_vTimers;
|
|
||||||
m_sLoopState.timersMutex.unlock();
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<CTimer>> passed;
|
|
||||||
|
|
||||||
for (auto& t : timerscpy) {
|
|
||||||
if (t->passed() && !t->cancelled()) {
|
|
||||||
t->call(t);
|
|
||||||
passed.push_back(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t->cancelled())
|
|
||||||
passed.push_back(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_sLoopState.timersMutex.lock();
|
|
||||||
std::erase_if(m_vTimers, [passed](const auto& timer) { return std::find(passed.begin(), passed.end(), timer) != passed.end(); });
|
|
||||||
m_sLoopState.timersMutex.unlock();
|
|
||||||
|
|
||||||
passed.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto DPY = m_sWaylandState.display;
|
const auto DPY = m_sWaylandState.display;
|
||||||
|
|
||||||
m_sLoopState.timerEvent = true;
|
m_sLoopState.timerEvent = true;
|
||||||
m_sLoopState.timerCV.notify_all();
|
m_sLoopState.timerCV.notify_all();
|
||||||
g_pRenderer->asyncResourceGatherer->notify();
|
|
||||||
g_pRenderer->asyncResourceGatherer->await();
|
|
||||||
m_sWaylandState = {};
|
m_sWaylandState = {};
|
||||||
dma = {};
|
dma = {};
|
||||||
|
|
||||||
m_vOutputs.clear();
|
m_vOutputs.clear();
|
||||||
g_pEGL.reset();
|
|
||||||
g_pRenderer.reset();
|
|
||||||
g_pSeatManager.reset();
|
g_pSeatManager.reset();
|
||||||
|
g_asyncResourceManager.reset();
|
||||||
|
g_pRenderer.reset();
|
||||||
|
g_pEGL.reset();
|
||||||
|
|
||||||
wl_display_disconnect(DPY);
|
wl_display_disconnect(DPY);
|
||||||
|
|
||||||
|
|
@ -499,16 +482,13 @@ void CHyprlock::unlock() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool IMMEDIATE = m_sCurrentDesktop != "Hyprland";
|
g_pRenderer->startFadeOut(true);
|
||||||
|
|
||||||
g_pRenderer->startFadeOut(true, IMMEDIATE);
|
|
||||||
m_bUnlockedCalled = true;
|
|
||||||
|
|
||||||
renderAllOutputs();
|
renderAllOutputs();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CHyprlock::isUnlocked() {
|
bool CHyprlock::isUnlocked() {
|
||||||
return m_bUnlockedCalled || m_bTerminate;
|
return !m_bLocked;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHyprlock::clearPasswordBuffer() {
|
void CHyprlock::clearPasswordBuffer() {
|
||||||
|
|
@ -555,7 +535,7 @@ void CHyprlock::startKeyRepeat(xkb_keysym_t sym) {
|
||||||
if (m_iKeebRepeatDelay <= 0)
|
if (m_iKeebRepeatDelay <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_pKeyRepeatTimer = addTimer(std::chrono::milliseconds(m_iKeebRepeatDelay), [sym](std::shared_ptr<CTimer> self, void* data) { g_pHyprlock->repeatKey(sym); }, nullptr);
|
m_pKeyRepeatTimer = addTimer(std::chrono::milliseconds(m_iKeebRepeatDelay), [sym](ASP<CTimer> self, void* data) { g_pHyprlock->repeatKey(sym); }, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHyprlock::repeatKey(xkb_keysym_t sym) {
|
void CHyprlock::repeatKey(xkb_keysym_t sym) {
|
||||||
|
|
@ -566,7 +546,7 @@ void CHyprlock::repeatKey(xkb_keysym_t sym) {
|
||||||
|
|
||||||
// This condition is for backspace and delete keys, but should also be ok for other keysyms since our buffer won't be empty anyways
|
// This condition is for backspace and delete keys, but should also be ok for other keysyms since our buffer won't be empty anyways
|
||||||
if (bool CONTINUE = m_sPasswordState.passBuffer.length() > 0; CONTINUE)
|
if (bool CONTINUE = m_sPasswordState.passBuffer.length() > 0; CONTINUE)
|
||||||
m_pKeyRepeatTimer = addTimer(std::chrono::milliseconds(m_iKeebRepeatRate), [sym](std::shared_ptr<CTimer> self, void* data) { g_pHyprlock->repeatKey(sym); }, nullptr);
|
m_pKeyRepeatTimer = addTimer(std::chrono::milliseconds(m_iKeebRepeatRate), [sym](ASP<CTimer> self, void* data) { g_pHyprlock->repeatKey(sym); }, nullptr);
|
||||||
|
|
||||||
renderAllOutputs();
|
renderAllOutputs();
|
||||||
}
|
}
|
||||||
|
|
@ -633,7 +613,7 @@ void CHyprlock::onKey(uint32_t key, bool down) {
|
||||||
void CHyprlock::handleKeySym(xkb_keysym_t sym, bool composed) {
|
void CHyprlock::handleKeySym(xkb_keysym_t sym, bool composed) {
|
||||||
const auto SYM = sym;
|
const auto SYM = sym;
|
||||||
|
|
||||||
if (SYM == XKB_KEY_Escape || (m_bCtrl && (SYM == XKB_KEY_u || SYM == XKB_KEY_BackSpace))) {
|
if (SYM == XKB_KEY_Escape || (m_bCtrl && (SYM == XKB_KEY_u || SYM == XKB_KEY_BackSpace || SYM == XKB_KEY_a))) {
|
||||||
Debug::log(LOG, "Clearing password buffer");
|
Debug::log(LOG, "Clearing password buffer");
|
||||||
|
|
||||||
m_sPasswordState.passBuffer = "";
|
m_sPasswordState.passBuffer = "";
|
||||||
|
|
@ -669,6 +649,64 @@ void CHyprlock::handleKeySym(xkb_keysym_t sym, bool composed) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CHyprlock::onClick(uint32_t button, bool down, const Vector2D& pos) {
|
||||||
|
if (!down)
|
||||||
|
return;
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
@ -790,23 +828,47 @@ size_t CHyprlock::getPasswordBufferDisplayLen() {
|
||||||
return std::count_if(m_sPasswordState.passBuffer.begin(), m_sPasswordState.passBuffer.end(), [](char c) { return (c & 0xc0) != 0x80; });
|
return std::count_if(m_sPasswordState.passBuffer.begin(), m_sPasswordState.passBuffer.end(), [](char c) { return (c & 0xc0) != 0x80; });
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<CTimer> CHyprlock::addTimer(const std::chrono::system_clock::duration& timeout, std::function<void(std::shared_ptr<CTimer> self, void* data)> cb_, void* data,
|
ASP<CTimer> CHyprlock::addTimer(const std::chrono::system_clock::duration& timeout, std::function<void(ASP<CTimer> self, void* data)> cb_, void* data, bool force) {
|
||||||
bool force) {
|
|
||||||
std::lock_guard<std::mutex> lg(m_sLoopState.timersMutex);
|
std::lock_guard<std::mutex> lg(m_sLoopState.timersMutex);
|
||||||
const auto T = m_vTimers.emplace_back(std::make_shared<CTimer>(timeout, cb_, data, force));
|
const auto T = m_vTimers.emplace_back(makeAtomicShared<CTimer>(timeout, cb_, data, force));
|
||||||
m_sLoopState.timerEvent = true;
|
m_sLoopState.timerEvent = true;
|
||||||
m_sLoopState.timerCV.notify_all();
|
m_sLoopState.timerCV.notify_all();
|
||||||
return T;
|
return T;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::shared_ptr<CTimer>> CHyprlock::getTimers() {
|
void CHyprlock::processTimers() {
|
||||||
|
// do timers
|
||||||
|
m_sLoopState.timersMutex.lock();
|
||||||
|
auto timerscpy = m_vTimers;
|
||||||
|
m_sLoopState.timersMutex.unlock();
|
||||||
|
|
||||||
|
std::vector<ASP<CTimer>> passed;
|
||||||
|
|
||||||
|
for (auto& t : timerscpy) {
|
||||||
|
if (t->passed() && !t->cancelled()) {
|
||||||
|
t->call(t);
|
||||||
|
passed.push_back(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t->cancelled())
|
||||||
|
passed.push_back(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_sLoopState.timersMutex.lock();
|
||||||
|
std::erase_if(m_vTimers, [passed](const auto& timer) { return std::find(passed.begin(), passed.end(), timer) != passed.end(); });
|
||||||
|
m_sLoopState.timersMutex.unlock();
|
||||||
|
|
||||||
|
passed.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ASP<CTimer>> CHyprlock::getTimers() {
|
||||||
return m_vTimers;
|
return m_vTimers;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHyprlock::enqueueForceUpdateTimers() {
|
void CHyprlock::enqueueForceUpdateTimers() {
|
||||||
addTimer(
|
addTimer(
|
||||||
std::chrono::milliseconds(1),
|
std::chrono::milliseconds(1),
|
||||||
[](std::shared_ptr<CTimer> self, void* data) {
|
[](ASP<CTimer> self, void* data) {
|
||||||
for (auto& t : g_pHyprlock->getTimers()) {
|
for (auto& t : g_pHyprlock->getTimers()) {
|
||||||
if (t->canForceUpdate()) {
|
if (t->canForceUpdate()) {
|
||||||
t->call(t);
|
t->call(t);
|
||||||
|
|
@ -817,19 +879,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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,7 @@
|
||||||
#include "linux-dmabuf-v1.hpp"
|
#include "linux-dmabuf-v1.hpp"
|
||||||
#include "viewporter.hpp"
|
#include "viewporter.hpp"
|
||||||
#include "Output.hpp"
|
#include "Output.hpp"
|
||||||
#include "Seat.hpp"
|
|
||||||
#include "CursorShape.hpp"
|
|
||||||
#include "Timer.hpp"
|
#include "Timer.hpp"
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
@ -29,7 +26,7 @@ struct SDMABUFModifier {
|
||||||
|
|
||||||
class CHyprlock {
|
class CHyprlock {
|
||||||
public:
|
public:
|
||||||
CHyprlock(const std::string& wlDisplay, const bool immediate, const bool immediateRender);
|
CHyprlock(const std::string& wlDisplay, const bool immediateRender, const int gracePeriod);
|
||||||
~CHyprlock();
|
~CHyprlock();
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
|
|
@ -37,8 +34,8 @@ class CHyprlock {
|
||||||
void unlock();
|
void unlock();
|
||||||
bool isUnlocked();
|
bool isUnlocked();
|
||||||
|
|
||||||
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,
|
ASP<CTimer> addTimer(const std::chrono::system_clock::duration& timeout, std::function<void(ASP<CTimer> self, void* data)> cb_, void* data, bool force = false);
|
||||||
bool force = false);
|
void processTimers();
|
||||||
|
|
||||||
void enqueueForceUpdateTimers();
|
void enqueueForceUpdateTimers();
|
||||||
|
|
||||||
|
|
@ -48,9 +45,9 @@ class CHyprlock {
|
||||||
bool acquireSessionLock();
|
bool acquireSessionLock();
|
||||||
void releaseSessionLock();
|
void releaseSessionLock();
|
||||||
|
|
||||||
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);
|
||||||
|
|
@ -95,11 +92,14 @@ 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;
|
||||||
|
|
||||||
std::shared_ptr<CTimer> m_pKeyRepeatTimer = nullptr;
|
Vector2D m_vMouseLocation = {};
|
||||||
|
|
||||||
|
ASP<CTimer> m_pKeyRepeatTimer = nullptr;
|
||||||
|
|
||||||
std::vector<SP<COutput>> m_vOutputs;
|
std::vector<SP<COutput>> m_vOutputs;
|
||||||
std::vector<std::shared_ptr<CTimer>> getTimers();
|
std::vector<ASP<CTimer>> getTimers();
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
SP<CCZwpLinuxDmabufV1> linuxDmabuf = nullptr;
|
SP<CCZwpLinuxDmabufV1> linuxDmabuf = nullptr;
|
||||||
|
|
@ -116,6 +116,9 @@ class CHyprlock {
|
||||||
} dma;
|
} dma;
|
||||||
gbm_device* createGBMDevice(drmDevice* dev);
|
gbm_device* createGBMDevice(drmDevice* dev);
|
||||||
|
|
||||||
|
void addDmabufListener();
|
||||||
|
void removeDmabufListener();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct {
|
struct {
|
||||||
wl_display* display = nullptr;
|
wl_display* display = nullptr;
|
||||||
|
|
@ -128,8 +131,6 @@ class CHyprlock {
|
||||||
SP<CCWlShm> shm = nullptr;
|
SP<CCWlShm> shm = nullptr;
|
||||||
} m_sWaylandState;
|
} m_sWaylandState;
|
||||||
|
|
||||||
void addDmabufListener();
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
SP<CCExtSessionLockV1> lock = nullptr;
|
SP<CCExtSessionLockV1> lock = nullptr;
|
||||||
} m_sLockState;
|
} m_sLockState;
|
||||||
|
|
@ -155,9 +156,7 @@ class CHyprlock {
|
||||||
bool timerEvent = false;
|
bool timerEvent = false;
|
||||||
} m_sLoopState;
|
} m_sLoopState;
|
||||||
|
|
||||||
bool m_bUnlockedCalled = false;
|
std::vector<ASP<CTimer>> m_vTimers;
|
||||||
|
|
||||||
std::vector<std::shared_ptr<CTimer>> m_vTimers;
|
|
||||||
|
|
||||||
std::vector<uint32_t> m_vPressedKeys;
|
std::vector<uint32_t> m_vPressedKeys;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,20 @@
|
||||||
|
|
||||||
#include <hyprutils/memory/WeakPtr.hpp>
|
#include <hyprutils/memory/WeakPtr.hpp>
|
||||||
#include <hyprutils/memory/UniquePtr.hpp>
|
#include <hyprutils/memory/UniquePtr.hpp>
|
||||||
|
#include <hyprutils/memory/Atomic.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;
|
||||||
|
|
||||||
|
using ResourceID = size_t;
|
||||||
|
|
||||||
#define SP CSharedPointer
|
#define SP CSharedPointer
|
||||||
#define WP CWeakPointer
|
#define WP CWeakPointer
|
||||||
#define UP CUniquePointer
|
#define UP CUniquePointer
|
||||||
|
|
||||||
|
#define ASP CAtomicSharedPointer
|
||||||
|
#define AWP CAtomicWeakPointer
|
||||||
|
|
||||||
typedef int64_t OUTPUTID;
|
typedef int64_t OUTPUTID;
|
||||||
constexpr OUTPUTID OUTPUT_INVALID = -1;
|
constexpr OUTPUTID OUTPUT_INVALID = -1;
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ template <Animable VarType>
|
||||||
using CAnimatedVariable = Hyprutils::Animation::CGenericAnimatedVariable<VarType, SAnimationContext>;
|
using CAnimatedVariable = Hyprutils::Animation::CGenericAnimatedVariable<VarType, SAnimationContext>;
|
||||||
|
|
||||||
template <Animable VarType>
|
template <Animable VarType>
|
||||||
using PHLANIMVAR = SP<CAnimatedVariable<VarType>>;
|
using PHLANIMVAR = UP<CAnimatedVariable<VarType>>;
|
||||||
|
|
||||||
template <Animable VarType>
|
template <Animable VarType>
|
||||||
using PHLANIMVARREF = WP<CAnimatedVariable<VarType>>;
|
using PHLANIMVARREF = WP<CAnimatedVariable<VarType>>;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
27
src/main.cpp
27
src/main.cpp
|
|
@ -13,7 +13,7 @@ void help() {
|
||||||
" -q, --quiet - Disable logging\n"
|
" -q, --quiet - Disable logging\n"
|
||||||
" -c FILE, --config FILE - Specify config file to use\n"
|
" -c FILE, --config FILE - Specify config file to use\n"
|
||||||
" --display NAME - Specify the Wayland display to connect to\n"
|
" --display NAME - Specify the Wayland display to connect to\n"
|
||||||
" --immediate - Lock immediately, ignoring any configured grace period\n"
|
" --grace SECONDS - Set grace period in seconds before requiring authentication\n"
|
||||||
" --immediate-render - Do not wait for resources before drawing the background\n"
|
" --immediate-render - Do not wait for resources before drawing the background\n"
|
||||||
" --no-fade-in - Disable the fade-in animation when the lock screen appears\n"
|
" --no-fade-in - Disable the fade-in animation when the lock screen appears\n"
|
||||||
" -V, --version - Show version information\n"
|
" -V, --version - Show version information\n"
|
||||||
|
|
@ -40,9 +40,9 @@ static void printVersion() {
|
||||||
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;
|
||||||
bool immediate = false;
|
|
||||||
bool immediateRender = false;
|
bool immediateRender = false;
|
||||||
bool noFadeIn = false;
|
bool noFadeIn = false;
|
||||||
|
int graceSeconds = 0;
|
||||||
|
|
||||||
std::vector<std::string> args(argv, argv + argc);
|
std::vector<std::string> args(argv, argv + argc);
|
||||||
|
|
||||||
|
|
@ -77,8 +77,25 @@ int main(int argc, char** argv, char** envp) {
|
||||||
else
|
else
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
} else if (arg == "--immediate")
|
} else if (arg == "--grace" && i + 1 < (std::size_t)argc) {
|
||||||
immediate = true;
|
if (auto value = parseArg(args, arg, i); value) {
|
||||||
|
try {
|
||||||
|
graceSeconds = std::stoi(*value);
|
||||||
|
if (graceSeconds < 0) {
|
||||||
|
std::println(stderr, "Error: Grace period must be non-negative.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} catch (const std::exception&) {
|
||||||
|
std::println(stderr, "Error: Invalid grace period value: {}", *value);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
} else if (arg == "--immediate") {
|
||||||
|
graceSeconds = 0;
|
||||||
|
Debug::log(WARN, R"("--immediate" is deprecated. Use the "--grace" option instead.)");
|
||||||
|
}
|
||||||
|
|
||||||
else if (arg == "--immediate-render")
|
else if (arg == "--immediate-render")
|
||||||
immediateRender = true;
|
immediateRender = true;
|
||||||
|
|
@ -111,7 +128,7 @@ int main(int argc, char** argv, char** envp) {
|
||||||
g_pConfigManager->m_AnimationTree.setConfigForNode("fadeIn", false, 0.f, "default");
|
g_pConfigManager->m_AnimationTree.setConfigForNode("fadeIn", false, 0.f, "default");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
g_pHyprlock = makeUnique<CHyprlock>(wlDisplay, immediate, immediateRender);
|
g_pHyprlock = makeUnique<CHyprlock>(wlDisplay, immediateRender, graceSeconds);
|
||||||
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());
|
||||||
|
|
|
||||||
|
|
@ -1,368 +0,0 @@
|
||||||
#include "AsyncResourceGatherer.hpp"
|
|
||||||
#include "../config/ConfigManager.hpp"
|
|
||||||
#include "../core/Egl.hpp"
|
|
||||||
#include <cairo/cairo.h>
|
|
||||||
#include <magic.h>
|
|
||||||
#include <pango/pangocairo.h>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <filesystem>
|
|
||||||
#include "../core/hyprlock.hpp"
|
|
||||||
#include "../helpers/MiscFunctions.hpp"
|
|
||||||
#include "src/helpers/Color.hpp"
|
|
||||||
#include "src/helpers/Log.hpp"
|
|
||||||
#include <hyprgraphics/image/Image.hpp>
|
|
||||||
using namespace Hyprgraphics;
|
|
||||||
|
|
||||||
CAsyncResourceGatherer::CAsyncResourceGatherer() {
|
|
||||||
if (g_pHyprlock->getScreencopy())
|
|
||||||
enqueueScreencopyFrames();
|
|
||||||
|
|
||||||
initialGatherThread = std::thread([this]() { this->gather(); });
|
|
||||||
asyncLoopThread = std::thread([this]() { this->asyncAssetSpinLock(); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void CAsyncResourceGatherer::enqueueScreencopyFrames() {
|
|
||||||
// some things can't be done async :(
|
|
||||||
// gather background textures when needed
|
|
||||||
|
|
||||||
const auto CWIDGETS = g_pConfigManager->getWidgetConfigs();
|
|
||||||
|
|
||||||
std::vector<std::string> mons;
|
|
||||||
|
|
||||||
for (auto& c : CWIDGETS) {
|
|
||||||
if (c.type != "background")
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (std::string{std::any_cast<Hyprlang::STRING>(c.values.at("path"))} != "screenshot")
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// mamma mia
|
|
||||||
if (c.monitor.empty()) {
|
|
||||||
mons.clear();
|
|
||||||
for (auto& m : g_pHyprlock->m_vOutputs) {
|
|
||||||
mons.push_back(m->stringPort);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} else
|
|
||||||
mons.push_back(c.monitor);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& mon : mons) {
|
|
||||||
const auto MON = std::ranges::find_if(g_pHyprlock->m_vOutputs, [mon](const auto& other) { return other->stringPort == mon || other->stringDesc.starts_with(mon); });
|
|
||||||
|
|
||||||
if (MON == g_pHyprlock->m_vOutputs.end())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
scframes.emplace_back(makeUnique<CScreencopyFrame>(*MON));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SPreloadedAsset* CAsyncResourceGatherer::getAssetByID(const std::string& id) {
|
|
||||||
for (auto& a : assets) {
|
|
||||||
if (a.first == id)
|
|
||||||
return &a.second;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (apply()) {
|
|
||||||
for (auto& a : assets) {
|
|
||||||
if (a.first == id)
|
|
||||||
return &a.second;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (auto& frame : scframes) {
|
|
||||||
if (id == frame->m_resourceID)
|
|
||||||
return frame->m_asset.ready ? &frame->m_asset : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static SP<CCairoSurface> getCairoSurfaceFromImageFile(const std::filesystem::path& path) {
|
|
||||||
|
|
||||||
auto image = CImage(path);
|
|
||||||
if (!image.success()) {
|
|
||||||
Debug::log(ERR, "Image {} could not be loaded: {}", path.string(), image.getError());
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return image.cairoSurface();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CAsyncResourceGatherer::gather() {
|
|
||||||
const auto CWIDGETS = g_pConfigManager->getWidgetConfigs();
|
|
||||||
|
|
||||||
g_pEGL->makeCurrent(nullptr);
|
|
||||||
|
|
||||||
// gather resources to preload
|
|
||||||
// clang-format off
|
|
||||||
int preloads = std::count_if(CWIDGETS.begin(), CWIDGETS.end(), [](const auto& w) {
|
|
||||||
return w.type == "background" || w.type == "image";
|
|
||||||
});
|
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
progress = 0;
|
|
||||||
for (auto& c : CWIDGETS) {
|
|
||||||
if (c.type == "background" || c.type == "image") {
|
|
||||||
#if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION < 180100
|
|
||||||
progress = progress + 1.0 / (preloads + 1.0);
|
|
||||||
#else
|
|
||||||
progress += 1.0 / (preloads + 1.0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
std::string path = std::any_cast<Hyprlang::STRING>(c.values.at("path"));
|
|
||||||
|
|
||||||
if (path.empty() || path == "screenshot")
|
|
||||||
continue;
|
|
||||||
|
|
||||||
std::string id = (c.type == "background" ? std::string{"background:"} : std::string{"image:"}) + path;
|
|
||||||
|
|
||||||
// render the image directly, since we are in a seperate thread
|
|
||||||
CAsyncResourceGatherer::SPreloadRequest rq;
|
|
||||||
rq.type = CAsyncResourceGatherer::TARGET_IMAGE;
|
|
||||||
rq.asset = path;
|
|
||||||
rq.id = id;
|
|
||||||
|
|
||||||
renderImage(rq);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!g_pHyprlock->m_bTerminate && std::ranges::any_of(scframes, [](const auto& d) { return !d->m_asset.ready; })) {
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
gathered = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CAsyncResourceGatherer::apply() {
|
|
||||||
preloadTargetsMutex.lock();
|
|
||||||
|
|
||||||
if (preloadTargets.empty()) {
|
|
||||||
preloadTargetsMutex.unlock();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto currentPreloadTargets = preloadTargets;
|
|
||||||
preloadTargets.clear();
|
|
||||||
preloadTargetsMutex.unlock();
|
|
||||||
|
|
||||||
for (auto& t : currentPreloadTargets) {
|
|
||||||
if (t.type == TARGET_IMAGE) {
|
|
||||||
const auto ASSET = &assets[t.id];
|
|
||||||
|
|
||||||
const cairo_status_t SURFACESTATUS = (cairo_status_t)t.cairosurface->status();
|
|
||||||
const auto CAIROFORMAT = cairo_image_surface_get_format(t.cairosurface->cairo());
|
|
||||||
const GLint glIFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB32F : GL_RGBA;
|
|
||||||
const GLint glFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB : GL_RGBA;
|
|
||||||
const GLint glType = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_FLOAT : GL_UNSIGNED_BYTE;
|
|
||||||
|
|
||||||
if (SURFACESTATUS != CAIRO_STATUS_SUCCESS) {
|
|
||||||
Debug::log(ERR, "Resource {} invalid ({})", t.id, cairo_status_to_string(SURFACESTATUS));
|
|
||||||
ASSET->texture.m_iType = TEXTURE_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSET->texture.m_vSize = t.size;
|
|
||||||
ASSET->texture.allocate();
|
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, ASSET->texture.m_iTexID);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
||||||
if (CAIROFORMAT != CAIRO_FORMAT_RGB96F) {
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
|
|
||||||
}
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, glIFormat, ASSET->texture.m_vSize.x, ASSET->texture.m_vSize.y, 0, glFormat, glType, t.data);
|
|
||||||
|
|
||||||
cairo_destroy((cairo_t*)t.cairo);
|
|
||||||
t.cairosurface.reset();
|
|
||||||
} else
|
|
||||||
Debug::log(ERR, "Unsupported type in ::apply(): {}", (int)t.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CAsyncResourceGatherer::renderImage(const SPreloadRequest& rq) {
|
|
||||||
SPreloadTarget target;
|
|
||||||
target.type = TARGET_IMAGE;
|
|
||||||
target.id = rq.id;
|
|
||||||
|
|
||||||
std::filesystem::path ABSOLUTEPATH(absolutePath(rq.asset, ""));
|
|
||||||
const auto CAIROISURFACE = getCairoSurfaceFromImageFile(ABSOLUTEPATH);
|
|
||||||
|
|
||||||
if (!CAIROISURFACE) {
|
|
||||||
Debug::log(ERR, "renderImage: No cairo surface!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto CAIRO = cairo_create(CAIROISURFACE->cairo());
|
|
||||||
cairo_scale(CAIRO, 1, 1);
|
|
||||||
|
|
||||||
target.cairo = CAIRO;
|
|
||||||
target.cairosurface = CAIROISURFACE;
|
|
||||||
target.data = CAIROISURFACE->data();
|
|
||||||
target.size = CAIROISURFACE->size();
|
|
||||||
|
|
||||||
std::lock_guard lg{preloadTargetsMutex};
|
|
||||||
preloadTargets.push_back(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CAsyncResourceGatherer::renderText(const SPreloadRequest& rq) {
|
|
||||||
SPreloadTarget target;
|
|
||||||
target.type = TARGET_IMAGE; /* text is just an image lol */
|
|
||||||
target.id = rq.id;
|
|
||||||
|
|
||||||
const int FONTSIZE = rq.props.contains("font_size") ? std::any_cast<int>(rq.props.at("font_size")) : 16;
|
|
||||||
const CHyprColor FONTCOLOR = rq.props.contains("color") ? std::any_cast<CHyprColor>(rq.props.at("color")) : CHyprColor(1.0, 1.0, 1.0, 1.0);
|
|
||||||
const std::string FONTFAMILY = rq.props.contains("font_family") ? std::any_cast<std::string>(rq.props.at("font_family")) : "Sans";
|
|
||||||
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");
|
|
||||||
std::string text = ISCMD ? g_pHyprlock->spawnSync(rq.asset) : rq.asset;
|
|
||||||
|
|
||||||
if (*TRIM) {
|
|
||||||
text.erase(0, text.find_first_not_of(" \n\r\t"));
|
|
||||||
text.erase(text.find_last_not_of(" \n\r\t") + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CAIROSURFACE = makeShared<CCairoSurface>(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1920, 1080 /* dummy value */));
|
|
||||||
auto CAIRO = cairo_create(CAIROSURFACE->cairo());
|
|
||||||
|
|
||||||
// draw title using Pango
|
|
||||||
PangoLayout* layout = pango_cairo_create_layout(CAIRO);
|
|
||||||
|
|
||||||
PangoFontDescription* fontDesc = pango_font_description_from_string(FONTFAMILY.c_str());
|
|
||||||
pango_font_description_set_size(fontDesc, FONTSIZE * PANGO_SCALE);
|
|
||||||
pango_layout_set_font_description(layout, fontDesc);
|
|
||||||
pango_font_description_free(fontDesc);
|
|
||||||
|
|
||||||
if (rq.props.contains("text_align")) {
|
|
||||||
const std::string TEXTALIGN = std::any_cast<std::string>(rq.props.at("text_align"));
|
|
||||||
PangoAlignment align = PANGO_ALIGN_LEFT;
|
|
||||||
if (TEXTALIGN == "center")
|
|
||||||
align = PANGO_ALIGN_CENTER;
|
|
||||||
else if (TEXTALIGN == "right")
|
|
||||||
align = PANGO_ALIGN_RIGHT;
|
|
||||||
|
|
||||||
pango_layout_set_alignment(layout, align);
|
|
||||||
}
|
|
||||||
|
|
||||||
PangoAttrList* attrList = nullptr;
|
|
||||||
GError* gError = nullptr;
|
|
||||||
char* buf = nullptr;
|
|
||||||
if (pango_parse_markup(text.c_str(), -1, 0, &attrList, &buf, nullptr, &gError))
|
|
||||||
pango_layout_set_text(layout, buf, -1);
|
|
||||||
else {
|
|
||||||
Debug::log(ERR, "Pango markup parsing for {} failed: {}", text, gError->message);
|
|
||||||
g_error_free(gError);
|
|
||||||
pango_layout_set_text(layout, text.c_str(), -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!attrList)
|
|
||||||
attrList = pango_attr_list_new();
|
|
||||||
|
|
||||||
if (buf)
|
|
||||||
free(buf);
|
|
||||||
|
|
||||||
pango_attr_list_insert(attrList, pango_attr_scale_new(1));
|
|
||||||
pango_layout_set_attributes(layout, attrList);
|
|
||||||
pango_attr_list_unref(attrList);
|
|
||||||
|
|
||||||
int layoutWidth, layoutHeight;
|
|
||||||
pango_layout_get_size(layout, &layoutWidth, &layoutHeight);
|
|
||||||
|
|
||||||
// TODO: avoid this?
|
|
||||||
cairo_destroy(CAIRO);
|
|
||||||
CAIROSURFACE = makeShared<CCairoSurface>(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, layoutWidth / PANGO_SCALE, layoutHeight / PANGO_SCALE));
|
|
||||||
CAIRO = cairo_create(CAIROSURFACE->cairo());
|
|
||||||
|
|
||||||
// clear the pixmap
|
|
||||||
cairo_save(CAIRO);
|
|
||||||
cairo_set_operator(CAIRO, CAIRO_OPERATOR_CLEAR);
|
|
||||||
cairo_paint(CAIRO);
|
|
||||||
cairo_restore(CAIRO);
|
|
||||||
|
|
||||||
// render the thing
|
|
||||||
cairo_set_source_rgba(CAIRO, FONTCOLOR.r, FONTCOLOR.g, FONTCOLOR.b, FONTCOLOR.a);
|
|
||||||
|
|
||||||
cairo_move_to(CAIRO, 0, 0);
|
|
||||||
pango_cairo_show_layout(CAIRO, layout);
|
|
||||||
|
|
||||||
g_object_unref(layout);
|
|
||||||
|
|
||||||
cairo_surface_flush(CAIROSURFACE->cairo());
|
|
||||||
|
|
||||||
target.cairo = CAIRO;
|
|
||||||
target.cairosurface = CAIROSURFACE;
|
|
||||||
target.data = CAIROSURFACE->data();
|
|
||||||
target.size = {layoutWidth / (double)PANGO_SCALE, layoutHeight / (double)PANGO_SCALE};
|
|
||||||
|
|
||||||
std::lock_guard lg{preloadTargetsMutex};
|
|
||||||
preloadTargets.push_back(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CAsyncResourceGatherer::asyncAssetSpinLock() {
|
|
||||||
while (!g_pHyprlock->m_bTerminate) {
|
|
||||||
|
|
||||||
std::unique_lock lk(asyncLoopState.requestsMutex);
|
|
||||||
if (!asyncLoopState.pending) // avoid a lock if a thread managed to request something already since we .unlock()ed
|
|
||||||
asyncLoopState.requestsCV.wait_for(lk, std::chrono::seconds(5), [this] { return asyncLoopState.pending; }); // wait for events
|
|
||||||
|
|
||||||
asyncLoopState.pending = false;
|
|
||||||
|
|
||||||
if (asyncLoopState.requests.empty()) {
|
|
||||||
lk.unlock();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto requests = asyncLoopState.requests;
|
|
||||||
asyncLoopState.requests.clear();
|
|
||||||
|
|
||||||
lk.unlock();
|
|
||||||
|
|
||||||
// process requests
|
|
||||||
for (auto& r : requests) {
|
|
||||||
Debug::log(TRACE, "Processing requested resourceID {}", r.id);
|
|
||||||
|
|
||||||
if (r.type == TARGET_TEXT) {
|
|
||||||
renderText(r);
|
|
||||||
} else if (r.type == TARGET_IMAGE) {
|
|
||||||
renderImage(r);
|
|
||||||
} else {
|
|
||||||
Debug::log(ERR, "Unsupported async preload type {}??", (int)r.type);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// plant timer for callback
|
|
||||||
if (r.callback)
|
|
||||||
g_pHyprlock->addTimer(std::chrono::milliseconds(0), [cb = r.callback](auto, auto) { cb(); }, nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CAsyncResourceGatherer::requestAsyncAssetPreload(const SPreloadRequest& request) {
|
|
||||||
Debug::log(TRACE, "Requesting label resource {}", request.id);
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lg(asyncLoopState.requestsMutex);
|
|
||||||
asyncLoopState.requests.push_back(request);
|
|
||||||
asyncLoopState.pending = true;
|
|
||||||
asyncLoopState.requestsCV.notify_all();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CAsyncResourceGatherer::unloadAsset(SPreloadedAsset* asset) {
|
|
||||||
std::erase_if(assets, [asset](const auto& a) { return &a.second == asset; });
|
|
||||||
}
|
|
||||||
|
|
||||||
void CAsyncResourceGatherer::notify() {
|
|
||||||
std::lock_guard<std::mutex> lg(asyncLoopState.requestsMutex);
|
|
||||||
asyncLoopState.requests.clear();
|
|
||||||
asyncLoopState.pending = true;
|
|
||||||
asyncLoopState.requestsCV.notify_all();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CAsyncResourceGatherer::await() {
|
|
||||||
if (initialGatherThread.joinable())
|
|
||||||
initialGatherThread.join();
|
|
||||||
if (asyncLoopThread.joinable())
|
|
||||||
asyncLoopThread.join();
|
|
||||||
}
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Screencopy.hpp"
|
|
||||||
#include <thread>
|
|
||||||
#include <atomic>
|
|
||||||
#include <vector>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <any>
|
|
||||||
#include "Shared.hpp"
|
|
||||||
#include <hyprgraphics/cairo/CairoSurface.hpp>
|
|
||||||
|
|
||||||
class CAsyncResourceGatherer {
|
|
||||||
public:
|
|
||||||
CAsyncResourceGatherer();
|
|
||||||
std::atomic<bool> gathered = false;
|
|
||||||
|
|
||||||
std::atomic<float> progress = 0;
|
|
||||||
|
|
||||||
/* only call from ogl thread */
|
|
||||||
SPreloadedAsset* getAssetByID(const std::string& id);
|
|
||||||
|
|
||||||
bool apply();
|
|
||||||
|
|
||||||
enum eTargetType {
|
|
||||||
TARGET_IMAGE = 0,
|
|
||||||
TARGET_TEXT
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SPreloadRequest {
|
|
||||||
eTargetType type;
|
|
||||||
std::string asset;
|
|
||||||
std::string id;
|
|
||||||
|
|
||||||
std::unordered_map<std::string, std::any> props;
|
|
||||||
|
|
||||||
// optional. Callbacks will be dispatched from the main thread,
|
|
||||||
// so wayland/gl calls are OK.
|
|
||||||
// will fire once the resource is fully loaded and ready.
|
|
||||||
std::function<void()> callback = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
void requestAsyncAssetPreload(const SPreloadRequest& request);
|
|
||||||
void unloadAsset(SPreloadedAsset* asset);
|
|
||||||
void notify();
|
|
||||||
void await();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::thread asyncLoopThread;
|
|
||||||
std::thread initialGatherThread;
|
|
||||||
|
|
||||||
void asyncAssetSpinLock();
|
|
||||||
void renderText(const SPreloadRequest& rq);
|
|
||||||
void renderImage(const SPreloadRequest& rq);
|
|
||||||
|
|
||||||
struct {
|
|
||||||
std::condition_variable requestsCV;
|
|
||||||
std::mutex requestsMutex;
|
|
||||||
|
|
||||||
std::vector<SPreloadRequest> requests;
|
|
||||||
bool pending = false;
|
|
||||||
|
|
||||||
bool busy = false;
|
|
||||||
} asyncLoopState;
|
|
||||||
|
|
||||||
struct SPreloadTarget {
|
|
||||||
eTargetType type = TARGET_IMAGE;
|
|
||||||
std::string id = "";
|
|
||||||
|
|
||||||
void* data = nullptr;
|
|
||||||
void* cairo = nullptr;
|
|
||||||
SP<Hyprgraphics::CCairoSurface> cairosurface;
|
|
||||||
|
|
||||||
Vector2D size;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<UP<CScreencopyFrame>> scframes;
|
|
||||||
|
|
||||||
std::vector<SPreloadTarget> preloadTargets;
|
|
||||||
std::mutex preloadTargetsMutex;
|
|
||||||
|
|
||||||
std::unordered_map<std::string, SPreloadedAsset> assets;
|
|
||||||
|
|
||||||
void gather();
|
|
||||||
void enqueueScreencopyFrames();
|
|
||||||
};
|
|
||||||
351
src/renderer/AsyncResourceManager.cpp
Normal file
351
src/renderer/AsyncResourceManager.cpp
Normal file
|
|
@ -0,0 +1,351 @@
|
||||||
|
#include "AsyncResourceManager.hpp"
|
||||||
|
|
||||||
|
#include "./resources/TextCmdResource.hpp"
|
||||||
|
#include "../helpers/Log.hpp"
|
||||||
|
#include "../helpers/MiscFunctions.hpp"
|
||||||
|
#include "../core/hyprlock.hpp"
|
||||||
|
#include "../config/ConfigManager.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <functional>
|
||||||
|
#include <sys/eventfd.h>
|
||||||
|
#include <sys/poll.h>
|
||||||
|
|
||||||
|
using namespace Hyprgraphics;
|
||||||
|
using namespace Hyprutils::OS;
|
||||||
|
|
||||||
|
static inline ResourceID scopeResourceID(uint8_t scope, size_t in) {
|
||||||
|
return (in & ~0x0f) | scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceID CAsyncResourceManager::resourceIDForTextRequest(const CTextResource::STextResourceData& s) {
|
||||||
|
// TODO: Currently ignores the font string and resulting distribution is probably not perfect.
|
||||||
|
const auto H1 = std::hash<std::string>{}(s.text);
|
||||||
|
const auto H2 = std::hash<double>{}(s.color.asRgb().r);
|
||||||
|
const auto H3 = std::hash<double>{}(s.color.asRgb().g) + s.fontSize;
|
||||||
|
const auto H4 = std::hash<double>{}(s.color.asRgb().b) + s.align;
|
||||||
|
|
||||||
|
return scopeResourceID(1, H1 ^ (H2 << 1) ^ (H3 << 2) ^ (H4 << 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceID CAsyncResourceManager::resourceIDForTextCmdRequest(const CTextResource::STextResourceData& s, size_t revision) {
|
||||||
|
return scopeResourceID(2, resourceIDForTextRequest(s) ^ (revision << 32));
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceID CAsyncResourceManager::resourceIDForImageRequest(const std::string& path, size_t revision) {
|
||||||
|
return scopeResourceID(3, std::hash<std::string>{}(path) ^ (revision << 32));
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceID CAsyncResourceManager::resourceIDForScreencopy(const std::string& port) {
|
||||||
|
return scopeResourceID(4, std::hash<std::string>{}(port));
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceID CAsyncResourceManager::requestText(const CTextResource::STextResourceData& params, const AWP<IWidget>& widget) {
|
||||||
|
const auto RESOURCEID = resourceIDForTextRequest(params);
|
||||||
|
if (request(RESOURCEID, widget)) {
|
||||||
|
Debug::log(TRACE, "Reusing text resource \"{}\" (resourceID: {})", params.text, RESOURCEID, (uintptr_t)widget.get());
|
||||||
|
return RESOURCEID;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto resource = makeAtomicShared<CTextResource>(CTextResource::STextResourceData{params});
|
||||||
|
CAtomicSharedPointer<IAsyncResource> resourceGeneric{resource};
|
||||||
|
|
||||||
|
Debug::log(TRACE, "Requesting text resource \"{}\" (resourceID: {})", params.text, RESOURCEID, (uintptr_t)widget.get());
|
||||||
|
enqueue(RESOURCEID, resourceGeneric, widget);
|
||||||
|
return RESOURCEID;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceID CAsyncResourceManager::requestTextCmd(const CTextResource::STextResourceData& params, size_t revision, const AWP<IWidget>& widget) {
|
||||||
|
const auto RESOURCEID = resourceIDForTextCmdRequest(params, revision);
|
||||||
|
if (request(RESOURCEID, widget)) {
|
||||||
|
Debug::log(TRACE, "Reusing text cmd resource \"{}\" revision {} (resourceID: {})", params.text, revision, RESOURCEID, (uintptr_t)widget.get());
|
||||||
|
return RESOURCEID;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto resource = makeAtomicShared<CTextCmdResource>(CTextResource::STextResourceData{params});
|
||||||
|
CAtomicSharedPointer<IAsyncResource> resourceGeneric{resource};
|
||||||
|
|
||||||
|
Debug::log(TRACE, "Requesting text cmd resource \"{}\" revision {} (resourceID: {})", params.text, revision, RESOURCEID, (uintptr_t)widget.get());
|
||||||
|
enqueue(RESOURCEID, resourceGeneric, widget);
|
||||||
|
return RESOURCEID;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceID CAsyncResourceManager::requestImage(const std::string& path, size_t revision, const AWP<IWidget>& widget) {
|
||||||
|
const auto RESOURCEID = resourceIDForImageRequest(path, revision);
|
||||||
|
if (request(RESOURCEID, widget)) {
|
||||||
|
Debug::log(TRACE, "Reusing image resource {} revision {} (resourceID: {})", path, revision, RESOURCEID, (uintptr_t)widget.get());
|
||||||
|
return RESOURCEID;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto resource = makeAtomicShared<CImageResource>(absolutePath(path, ""));
|
||||||
|
CAtomicSharedPointer<IAsyncResource> resourceGeneric{resource};
|
||||||
|
|
||||||
|
Debug::log(TRACE, "Requesting image resource {} revision {} (resourceID: {})", path, revision, RESOURCEID, (uintptr_t)widget.get());
|
||||||
|
enqueue(RESOURCEID, resourceGeneric, widget);
|
||||||
|
return RESOURCEID;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASP<CTexture> CAsyncResourceManager::getAssetByID(size_t id) {
|
||||||
|
if (!m_assets.contains(id))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return m_assets[id].texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAsyncResourceManager::enqueueStaticAssets() {
|
||||||
|
const auto CWIDGETS = g_pConfigManager->getWidgetConfigs();
|
||||||
|
|
||||||
|
for (auto& c : CWIDGETS) {
|
||||||
|
if (c.type == "background" || c.type == "image") {
|
||||||
|
std::string path = std::any_cast<Hyprlang::STRING>(c.values.at("path"));
|
||||||
|
|
||||||
|
if (path.empty() || path == "screenshot")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
requestImage(path, 0, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAsyncResourceManager::enqueueScreencopyFrames() {
|
||||||
|
if (g_pHyprlock->m_vOutputs.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
static const auto ANIMATIONSENABLED = g_pConfigManager->getValue<Hyprlang::INT>("animations:enabled");
|
||||||
|
|
||||||
|
const auto FADEINCFG = g_pConfigManager->m_AnimationTree.getConfig("fadeIn");
|
||||||
|
const auto FADEOUTCFG = g_pConfigManager->m_AnimationTree.getConfig("fadeOut");
|
||||||
|
|
||||||
|
const bool FADENEEDSSC = *ANIMATIONSENABLED &&
|
||||||
|
((FADEINCFG->pValues && FADEINCFG->pValues->internalEnabled) || // fadeIn or fadeOut enabled
|
||||||
|
(FADEOUTCFG->pValues && FADEOUTCFG->pValues->internalEnabled));
|
||||||
|
|
||||||
|
const auto BGSCREENSHOT = std::ranges::any_of(g_pConfigManager->getWidgetConfigs(), [](const auto& w) { //
|
||||||
|
return w.type == "background" && std::string{std::any_cast<Hyprlang::STRING>(w.values.at("path"))} == "screenshot";
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!BGSCREENSHOT && !FADENEEDSSC) {
|
||||||
|
Debug::log(LOG, "Skipping screencopy");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& MON : g_pHyprlock->m_vOutputs) {
|
||||||
|
m_scFrames.emplace_back(makeUnique<CScreencopyFrame>());
|
||||||
|
auto* frame = m_scFrames.back().get();
|
||||||
|
frame->capture(MON);
|
||||||
|
m_assets.emplace(frame->m_resourceID, SPreloadedTexture{.texture = nullptr, .refs = 1});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAsyncResourceManager::screencopyToTexture(const CScreencopyFrame& scFrame) {
|
||||||
|
if (!scFrame.m_ready || !m_assets.contains(scFrame.m_resourceID)) {
|
||||||
|
Debug::log(ERR, "Bogus call to CAsyncResourceManager::screencopyToTexture. This is a bug!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_assets[scFrame.m_resourceID].texture = scFrame.m_asset;
|
||||||
|
|
||||||
|
Debug::log(TRACE, "Done sc frame {}", scFrame.m_resourceID);
|
||||||
|
|
||||||
|
std::erase_if(m_scFrames, [&scFrame](const auto& f) { return f.get() == &scFrame; });
|
||||||
|
|
||||||
|
if (m_scFrames.empty()) {
|
||||||
|
Debug::log(LOG, "Gathered all screencopy frames - removing dmabuf listeners");
|
||||||
|
g_pHyprlock->removeDmabufListener();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAsyncResourceManager::gatherInitialResources(wl_display* display) {
|
||||||
|
const auto MAXDELAYMS = 2000; // 2 Seconds
|
||||||
|
const auto STARTGATHERTP = std::chrono::system_clock::now();
|
||||||
|
|
||||||
|
m_gatheredEventfd = CFileDescriptor{eventfd(0, EFD_CLOEXEC)};
|
||||||
|
|
||||||
|
int fdcount = 1;
|
||||||
|
pollfd pollfds[2];
|
||||||
|
pollfds[0] = {
|
||||||
|
.fd = wl_display_get_fd(display),
|
||||||
|
.events = POLLIN,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (m_gatheredEventfd.isValid()) {
|
||||||
|
pollfds[1] = {
|
||||||
|
.fd = m_gatheredEventfd.get(),
|
||||||
|
.events = POLLIN,
|
||||||
|
};
|
||||||
|
|
||||||
|
fdcount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool gathered = false;
|
||||||
|
while (!gathered) {
|
||||||
|
wl_display_flush(display);
|
||||||
|
if (wl_display_prepare_read(display) == 0) {
|
||||||
|
if (poll(pollfds, fdcount, /* 100ms timeout */ 100) < 0) {
|
||||||
|
RASSERT(errno == EINTR, "[core] Polling fds failed with {}", errno);
|
||||||
|
wl_display_cancel_read(display);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
wl_display_read_events(display);
|
||||||
|
wl_display_dispatch_pending(display);
|
||||||
|
} else {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
|
wl_display_dispatch(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_pHyprlock->processTimers();
|
||||||
|
|
||||||
|
if (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - STARTGATHERTP).count() > MAXDELAYMS) {
|
||||||
|
Debug::log(WARN, "Gathering resources timed out after {} milliseconds. Backgrounds may be delayed and render `background:color` at first.", MAXDELAYMS);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
gathered = m_resources.empty() && m_scFrames.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug::log(LOG, "Resources gathered after {} milliseconds", std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - STARTGATHERTP).count());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CAsyncResourceManager::checkIdPresent(ResourceID id) {
|
||||||
|
return m_assets.contains(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAsyncResourceManager::unload(ASP<CTexture> texture) {
|
||||||
|
auto preload = std::ranges::find_if(m_assets, [texture](const auto& a) { return a.second.texture == texture; });
|
||||||
|
if (preload == m_assets.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
preload->second.refs--;
|
||||||
|
|
||||||
|
if (preload->second.refs == 0) {
|
||||||
|
Debug::log(TRACE, "Releasing resourceID: {}!", preload->first);
|
||||||
|
m_assets.erase(preload->first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAsyncResourceManager::unloadById(ResourceID id) {
|
||||||
|
if (!m_assets.contains(id))
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_assets[id].refs--;
|
||||||
|
|
||||||
|
if (m_assets[id].refs == 0) {
|
||||||
|
Debug::log(TRACE, "Releasing resourceID: {}!", id);
|
||||||
|
m_assets.erase(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CAsyncResourceManager::request(ResourceID id, const AWP<IWidget>& widget) {
|
||||||
|
if (!m_assets.contains(id)) {
|
||||||
|
// New asset!!
|
||||||
|
m_assets.emplace(id, SPreloadedTexture{.texture = nullptr, .refs = 1});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_assets[id].refs++;
|
||||||
|
|
||||||
|
if (m_assets[id].texture) {
|
||||||
|
// Asset already present. Dispatch the asset callback function.
|
||||||
|
const auto& TEXTURE = m_assets[id].texture;
|
||||||
|
if (widget)
|
||||||
|
widget->onAssetUpdate(id, TEXTURE);
|
||||||
|
|
||||||
|
// TODO: add a centalized mechanism to render in one place in the event loop to avoid duplicate render calls
|
||||||
|
g_pHyprlock->addTimer(std::chrono::milliseconds(0), [](auto, auto) { g_pHyprlock->renderAllOutputs(); }, nullptr);
|
||||||
|
} else if (widget) {
|
||||||
|
// Asset currently in-flight. Add the widget reference to in order for the callback to get dispatched later.
|
||||||
|
m_resourcesMutex.lock();
|
||||||
|
if (!m_resources.contains(id)) {
|
||||||
|
Debug::log(ERR, "In-flight resourceID: {} not found! This is a bug.", id);
|
||||||
|
m_resourcesMutex.unlock();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
m_resources[id].second.emplace_back(widget);
|
||||||
|
m_resourcesMutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAsyncResourceManager::enqueue(ResourceID resourceID, const ASP<IAsyncResource>& resource, const AWP<IWidget>& widget) {
|
||||||
|
m_gatherer.enqueue(resource);
|
||||||
|
|
||||||
|
m_resourcesMutex.lock();
|
||||||
|
if (m_resources.contains(resourceID))
|
||||||
|
Debug::log(ERR, "Resource already enqueued! This is a bug.");
|
||||||
|
|
||||||
|
m_resources[resourceID] = {resource, {widget}};
|
||||||
|
m_resourcesMutex.unlock();
|
||||||
|
|
||||||
|
resource->m_events.finished.listenStatic([resourceID]() {
|
||||||
|
g_pHyprlock->addTimer(std::chrono::milliseconds(0), [](auto, void* resourceID) { g_asyncResourceManager->onResourceFinished((size_t)resourceID); }, (void*)resourceID);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAsyncResourceManager::onResourceFinished(ResourceID id) {
|
||||||
|
m_resourcesMutex.lock();
|
||||||
|
if (!m_resources.contains(id)) {
|
||||||
|
m_resourcesMutex.unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto RESOURCE = m_resources[id].first;
|
||||||
|
const auto WIDGETS = m_resources[id].second;
|
||||||
|
m_resources.erase(id);
|
||||||
|
m_resourcesMutex.unlock();
|
||||||
|
|
||||||
|
if (!m_assets.contains(id) || m_assets[id].refs == 0) // Not referenced? Drop it
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!RESOURCE || !RESOURCE->m_asset.cairoSurface)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Debug::log(TRACE, "Resource to texture id:{}", id);
|
||||||
|
|
||||||
|
const auto texture = makeAtomicShared<CTexture>();
|
||||||
|
|
||||||
|
const cairo_status_t SURFACESTATUS = (cairo_status_t)RESOURCE->m_asset.cairoSurface->status();
|
||||||
|
const auto CAIROFORMAT = cairo_image_surface_get_format(RESOURCE->m_asset.cairoSurface->cairo());
|
||||||
|
const GLint glIFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB32F : GL_RGBA;
|
||||||
|
const GLint glFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_RGB : GL_RGBA;
|
||||||
|
const GLint glType = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_FLOAT : GL_UNSIGNED_BYTE;
|
||||||
|
|
||||||
|
if (SURFACESTATUS != CAIRO_STATUS_SUCCESS) {
|
||||||
|
Debug::log(ERR, "resourceID: {} invalid ({})", id, cairo_status_to_string(SURFACESTATUS));
|
||||||
|
texture->m_iType = TEXTURE_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
texture->m_vSize = RESOURCE->m_asset.pixelSize;
|
||||||
|
texture->allocate();
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, texture->m_iTexID);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
if (CAIROFORMAT != CAIRO_FORMAT_RGB96F) {
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
|
||||||
|
}
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, glIFormat, texture->m_vSize.x, texture->m_vSize.y, 0, glFormat, glType, RESOURCE->m_asset.cairoSurface->data());
|
||||||
|
|
||||||
|
m_assets[id].texture = texture;
|
||||||
|
|
||||||
|
for (const auto& widget : WIDGETS) {
|
||||||
|
if (widget)
|
||||||
|
widget->onAssetUpdate(id, texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_pHyprlock->renderAllOutputs();
|
||||||
|
|
||||||
|
if (!m_gathered && !g_pHyprlock->m_bImmediateRender) {
|
||||||
|
m_resourcesMutex.lock();
|
||||||
|
if (m_resources.empty()) {
|
||||||
|
m_gathered = true;
|
||||||
|
if (m_gatheredEventfd.isValid())
|
||||||
|
eventfd_write(m_gatheredEventfd.get(), 1);
|
||||||
|
|
||||||
|
m_gatheredEventfd.reset();
|
||||||
|
}
|
||||||
|
m_resourcesMutex.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
91
src/renderer/AsyncResourceManager.hpp
Normal file
91
src/renderer/AsyncResourceManager.hpp
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../defines.hpp"
|
||||||
|
#include "./Texture.hpp"
|
||||||
|
#include "./Screencopy.hpp"
|
||||||
|
#include "./widgets/IWidget.hpp"
|
||||||
|
|
||||||
|
#include <hyprgraphics/resource/AsyncResourceGatherer.hpp>
|
||||||
|
#include <hyprgraphics/resource/resources/AsyncResource.hpp>
|
||||||
|
#include <hyprgraphics/resource/resources/TextResource.hpp>
|
||||||
|
#include <hyprgraphics/resource/resources/ImageResource.hpp>
|
||||||
|
#include <hyprutils/os/FileDescriptor.hpp>
|
||||||
|
|
||||||
|
class CAsyncResourceManager {
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Notes on resource lifetimes:
|
||||||
|
// Resources id's are the result of hashing the requested resource parameters.
|
||||||
|
// When a new request is made, adding a new entry to the m_assets map is done immediatly.
|
||||||
|
// Subsequent calls through this section with the same resource id will increment the texture's references.
|
||||||
|
// The manager will release the resource when refs reaches 0, while the resource itelf may outlife it's reference in the manager.
|
||||||
|
// Why not use ASP/AWP for this?
|
||||||
|
// The problem is that we want to to increment the reference as soon as requesting the resource id.
|
||||||
|
// Not only when actually retrieving the asset with `getAssetById`.
|
||||||
|
//
|
||||||
|
// Improvement idea: Make a wrapper object that is returned when requesting and contains the resource id. Then we can unload with RAII.
|
||||||
|
|
||||||
|
// Those are hash functions that return the id for a requested resource.
|
||||||
|
static ResourceID resourceIDForTextRequest(const CTextResource::STextResourceData& s);
|
||||||
|
// Consumer needs to increment the revision parameter to get a new command evaluation.
|
||||||
|
static ResourceID resourceIDForTextCmdRequest(const CTextResource::STextResourceData& s, size_t revision);
|
||||||
|
// Image paths may be file system links, thus this function supports a revision parameter that gets factored into the resource id.
|
||||||
|
static ResourceID resourceIDForImageRequest(const std::string& path, size_t revision);
|
||||||
|
static ResourceID resourceIDForScreencopy(const std::string& port);
|
||||||
|
|
||||||
|
struct SPreloadedTexture {
|
||||||
|
ASP<CTexture> texture;
|
||||||
|
size_t refs = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
CAsyncResourceManager() = default;
|
||||||
|
~CAsyncResourceManager() = default;
|
||||||
|
|
||||||
|
ResourceID requestText(const CTextResource::STextResourceData& params, const AWP<IWidget>& widget);
|
||||||
|
// Same as requestText but substitute the text with what launching sh -c request.text returns.
|
||||||
|
ResourceID requestTextCmd(const CTextResource::STextResourceData& params, size_t revision, const AWP<IWidget>& widget);
|
||||||
|
ResourceID requestImage(const std::string& path, size_t revision, const AWP<IWidget>& widget);
|
||||||
|
|
||||||
|
ASP<CTexture> getAssetByID(ResourceID id);
|
||||||
|
|
||||||
|
void unload(ASP<CTexture> resource);
|
||||||
|
void unloadById(ResourceID id);
|
||||||
|
|
||||||
|
void enqueueStaticAssets();
|
||||||
|
void enqueueScreencopyFrames();
|
||||||
|
void screencopyToTexture(const CScreencopyFrame& scFrame);
|
||||||
|
void gatherInitialResources(wl_display* display);
|
||||||
|
|
||||||
|
bool checkIdPresent(ResourceID id);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Returns whether or not the id was already requested.
|
||||||
|
// Makes sure the widgets onAssetCallback function gets called.
|
||||||
|
bool request(ResourceID id, const AWP<IWidget>& widget);
|
||||||
|
// Adds a new resource to m_resources and passes it to m_gatherer.
|
||||||
|
void enqueue(ResourceID resourceID, const ASP<IAsyncResource>& resource, const AWP<IWidget>& widget);
|
||||||
|
// Callback for finished resources.
|
||||||
|
// Copies the resources cairo surface to a GL_TEXTURE_2D and sets it in the asset map.
|
||||||
|
// Removes the entry in m_resources.
|
||||||
|
// Call onAssetUpdate for all stored widget references.
|
||||||
|
void onResourceFinished(ResourceID id);
|
||||||
|
|
||||||
|
// For polling when using gatherInitialResources.
|
||||||
|
bool m_gathered = false;
|
||||||
|
Hyprutils::OS::CFileDescriptor m_gatheredEventfd;
|
||||||
|
|
||||||
|
bool m_exit = false;
|
||||||
|
|
||||||
|
int m_loadedAssets = 0;
|
||||||
|
|
||||||
|
// not shared between threads
|
||||||
|
std::unordered_map<ResourceID, SPreloadedTexture> m_assets;
|
||||||
|
std::vector<UP<CScreencopyFrame>> m_scFrames;
|
||||||
|
// shared between threads
|
||||||
|
std::mutex m_resourcesMutex;
|
||||||
|
std::unordered_map<ResourceID, std::pair<ASP<Hyprgraphics::IAsyncResource>, std::vector<AWP<IWidget>>>> m_resources;
|
||||||
|
|
||||||
|
Hyprgraphics::CAsyncResourceGatherer m_gatherer;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline UP<CAsyncResourceManager> g_asyncResourceManager;
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
#include "Framebuffer.hpp"
|
#include "Framebuffer.hpp"
|
||||||
#include "../helpers/Log.hpp"
|
#include "../helpers/Log.hpp"
|
||||||
|
#include <hyprutils/os/FileDescriptor.hpp>
|
||||||
#include <libdrm/drm_fourcc.h>
|
#include <libdrm/drm_fourcc.h>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
static uint32_t drmFormatToGL(uint32_t drm) {
|
static uint32_t drmFormatToGL(uint32_t drm) {
|
||||||
switch (drm) {
|
switch (drm) {
|
||||||
|
|
@ -97,7 +99,7 @@ void CFramebuffer::bind() const {
|
||||||
glViewport(0, 0, m_vSize.x, m_vSize.y);
|
glViewport(0, 0, m_vSize.x, m_vSize.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CFramebuffer::release() {
|
void CFramebuffer::destroyBuffer() {
|
||||||
if (m_iFb != (uint32_t)-1 && m_iFb)
|
if (m_iFb != (uint32_t)-1 && m_iFb)
|
||||||
glDeleteFramebuffers(1, &m_iFb);
|
glDeleteFramebuffers(1, &m_iFb);
|
||||||
|
|
||||||
|
|
@ -114,9 +116,9 @@ void CFramebuffer::release() {
|
||||||
}
|
}
|
||||||
|
|
||||||
CFramebuffer::~CFramebuffer() {
|
CFramebuffer::~CFramebuffer() {
|
||||||
release();
|
destroyBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CFramebuffer::isAllocated() {
|
bool CFramebuffer::isAllocated() const {
|
||||||
return m_iFb != (GLuint)-1;
|
return m_iFb != (GLuint)-1;
|
||||||
}
|
}
|
||||||
|
|
@ -11,9 +11,8 @@ class CFramebuffer {
|
||||||
bool alloc(int w, int h, bool highres = false);
|
bool alloc(int w, int h, bool highres = false);
|
||||||
void addStencil();
|
void addStencil();
|
||||||
void bind() const;
|
void bind() const;
|
||||||
void release();
|
void destroyBuffer();
|
||||||
void reset();
|
bool isAllocated() const;
|
||||||
bool isAllocated();
|
|
||||||
|
|
||||||
Vector2D m_vSize;
|
Vector2D m_vSize;
|
||||||
|
|
||||||
|
|
@ -21,4 +20,7 @@ class CFramebuffer {
|
||||||
GLuint m_iFb = -1;
|
GLuint m_iFb = -1;
|
||||||
|
|
||||||
CTexture* m_pStencilTex = nullptr;
|
CTexture* m_pStencilTex = nullptr;
|
||||||
|
|
||||||
|
CFramebuffer& operator=(CFramebuffer&&) = delete;
|
||||||
|
CFramebuffer& operator=(const CFramebuffer&) = delete;
|
||||||
};
|
};
|
||||||
|
|
@ -25,7 +25,7 @@ inline const float fullVerts[] = {
|
||||||
0, 1, // bottom left
|
0, 1, // bottom left
|
||||||
};
|
};
|
||||||
|
|
||||||
GLuint compileShader(const GLuint& type, std::string src) {
|
static GLuint compileShader(const GLuint& type, std::string src) {
|
||||||
auto shader = glCreateShader(type);
|
auto shader = glCreateShader(type);
|
||||||
|
|
||||||
auto shaderSource = src.c_str();
|
auto shaderSource = src.c_str();
|
||||||
|
|
@ -41,7 +41,7 @@ GLuint compileShader(const GLuint& type, std::string src) {
|
||||||
return shader;
|
return shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
GLuint createProgram(const std::string& vert, const std::string& frag) {
|
static GLuint createProgram(const std::string& vert, const std::string& frag) {
|
||||||
auto vertCompiled = compileShader(GL_VERTEX_SHADER, vert);
|
auto vertCompiled = compileShader(GL_VERTEX_SHADER, vert);
|
||||||
|
|
||||||
RASSERT(vertCompiled, "Compiling shader failed. VERTEX NULL! Shader source:\n\n{}", vert);
|
RASSERT(vertCompiled, "Compiling shader failed. VERTEX NULL! Shader source:\n\n{}", vert);
|
||||||
|
|
@ -194,8 +194,6 @@ CRenderer::CRenderer() {
|
||||||
borderShader.gradientLerp = glGetUniformLocation(prog, "gradientLerp");
|
borderShader.gradientLerp = glGetUniformLocation(prog, "gradientLerp");
|
||||||
borderShader.alpha = glGetUniformLocation(prog, "alpha");
|
borderShader.alpha = glGetUniformLocation(prog, "alpha");
|
||||||
|
|
||||||
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"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -217,17 +215,11 @@ CRenderer::SRenderFeedback CRenderer::renderLock(const CSessionLockSurface& surf
|
||||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
SRenderFeedback feedback;
|
SRenderFeedback feedback;
|
||||||
const bool WAITFORASSETS = !g_pHyprlock->m_bImmediateRender && !asyncResourceGatherer->gathered;
|
|
||||||
|
|
||||||
if (!WAITFORASSETS) {
|
|
||||||
// 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;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
feedback.needsFrame = feedback.needsFrame || !asyncResourceGatherer->gathered;
|
|
||||||
|
|
||||||
glDisable(GL_BLEND);
|
glDisable(GL_BLEND);
|
||||||
|
|
||||||
|
|
@ -388,13 +380,13 @@ void CRenderer::renderTextureMix(const CBox& box, const CTexture& tex, const CTe
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Widget>
|
template <class Widget>
|
||||||
static void createWidget(std::vector<SP<IWidget>>& widgets) {
|
static void createWidget(std::vector<ASP<IWidget>>& widgets) {
|
||||||
const auto W = makeShared<Widget>();
|
const auto W = makeAtomicShared<Widget>();
|
||||||
W->registerSelf(W);
|
W->registerSelf(W);
|
||||||
widgets.emplace_back(W);
|
widgets.emplace_back(W);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<SP<IWidget>>& CRenderer::getOrCreateWidgetsFor(const CSessionLockSurface& surf) {
|
std::vector<ASP<IWidget>>& CRenderer::getOrCreateWidgetsFor(const CSessionLockSurface& surf) {
|
||||||
RASSERT(surf.m_outputID != OUTPUT_INVALID, "Invalid output ID!");
|
RASSERT(surf.m_outputID != OUTPUT_INVALID, "Invalid output ID!");
|
||||||
|
|
||||||
if (!widgets.contains(surf.m_outputID)) {
|
if (!widgets.contains(surf.m_outputID)) {
|
||||||
|
|
@ -406,7 +398,7 @@ std::vector<SP<IWidget>>& CRenderer::getOrCreateWidgetsFor(const CSessionLockSur
|
||||||
|
|
||||||
const auto POUTPUT = surf.m_outputRef.lock();
|
const auto POUTPUT = surf.m_outputRef.lock();
|
||||||
for (auto& c : CWIDGETS) {
|
for (auto& c : CWIDGETS) {
|
||||||
if (!c.monitor.empty() && c.monitor != POUTPUT->stringPort && !POUTPUT->stringDesc.starts_with(c.monitor) && !POUTPUT->stringDesc.starts_with("desc:" + c.monitor))
|
if (!c.monitor.empty() && c.monitor != POUTPUT->stringPort && !POUTPUT->stringDesc.starts_with(c.monitor) && !("desc:" + POUTPUT->stringDesc).starts_with(c.monitor))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// by type
|
// by type
|
||||||
|
|
@ -613,12 +605,13 @@ void CRenderer::startFadeIn() {
|
||||||
opacity->setCallbackOnEnd([this](auto) { opacity->setConfig(g_pConfigManager->m_AnimationTree.getConfig("fadeOut")); }, true);
|
opacity->setCallbackOnEnd([this](auto) { opacity->setConfig(g_pConfigManager->m_AnimationTree.getConfig("fadeOut")); }, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CRenderer::startFadeOut(bool unlock, bool immediate) {
|
void CRenderer::startFadeOut(bool unlock) {
|
||||||
if (immediate)
|
|
||||||
opacity->setValueAndWarp(0.f);
|
|
||||||
else
|
|
||||||
*opacity = 0.f;
|
*opacity = 0.f;
|
||||||
|
|
||||||
if (unlock)
|
if (unlock)
|
||||||
opacity->setCallbackOnEnd([](auto) { g_pHyprlock->releaseSessionLock(); }, true);
|
opacity->setCallbackOnEnd([](auto) { g_pHyprlock->releaseSessionLock(); }, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CRenderer::warpOpacity(float newOpacity) {
|
||||||
|
opacity->setValueAndWarp(newOpacity);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,11 @@
|
||||||
#include "../core/LockSurface.hpp"
|
#include "../core/LockSurface.hpp"
|
||||||
#include "../helpers/AnimatedVariable.hpp"
|
#include "../helpers/AnimatedVariable.hpp"
|
||||||
#include "../helpers/Color.hpp"
|
#include "../helpers/Color.hpp"
|
||||||
#include "AsyncResourceGatherer.hpp"
|
|
||||||
#include "../config/ConfigDataValues.hpp"
|
#include "../config/ConfigDataValues.hpp"
|
||||||
#include "widgets/IWidget.hpp"
|
#include "widgets/IWidget.hpp"
|
||||||
#include "Framebuffer.hpp"
|
#include "Framebuffer.hpp"
|
||||||
|
|
||||||
typedef std::unordered_map<OUTPUTID, std::vector<SP<IWidget>>> widgetMap_t;
|
typedef std::unordered_map<OUTPUTID, std::vector<ASP<IWidget>>> widgetMap_t;
|
||||||
|
|
||||||
class CRenderer {
|
class CRenderer {
|
||||||
public:
|
public:
|
||||||
|
|
@ -37,7 +36,6 @@ 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);
|
||||||
|
|
||||||
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);
|
||||||
|
|
@ -47,13 +45,13 @@ class CRenderer {
|
||||||
void reconfigureWidgetsFor(OUTPUTID id);
|
void reconfigureWidgetsFor(OUTPUTID id);
|
||||||
|
|
||||||
void startFadeIn();
|
void startFadeIn();
|
||||||
void startFadeOut(bool unlock = false, bool immediate = true);
|
void startFadeOut(bool unlock = false);
|
||||||
|
void warpOpacity(float warpOpacity);
|
||||||
|
std::vector<ASP<IWidget>>& getOrCreateWidgetsFor(const CSessionLockSurface& surf);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
widgetMap_t widgets;
|
widgetMap_t widgets;
|
||||||
|
|
||||||
std::vector<SP<IWidget>>& getOrCreateWidgetsFor(const CSessionLockSurface& surf);
|
|
||||||
|
|
||||||
CShader rectShader;
|
CShader rectShader;
|
||||||
CShader texShader;
|
CShader texShader;
|
||||||
CShader texMixShader;
|
CShader texMixShader;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "Screencopy.hpp"
|
#include "Screencopy.hpp"
|
||||||
|
#include "./AsyncResourceManager.hpp"
|
||||||
#include "../helpers/Log.hpp"
|
#include "../helpers/Log.hpp"
|
||||||
#include "../helpers/MiscFunctions.hpp"
|
#include "../helpers/MiscFunctions.hpp"
|
||||||
#include "../core/hyprlock.hpp"
|
#include "../core/hyprlock.hpp"
|
||||||
|
|
@ -23,27 +24,20 @@ static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES = nullpt
|
||||||
static PFNEGLQUERYDMABUFMODIFIERSEXTPROC eglQueryDmaBufModifiersEXT = nullptr;
|
static PFNEGLQUERYDMABUFMODIFIERSEXTPROC eglQueryDmaBufModifiersEXT = nullptr;
|
||||||
|
|
||||||
//
|
//
|
||||||
std::string CScreencopyFrame::getResourceId(SP<COutput> pOutput) {
|
void CScreencopyFrame::capture(SP<COutput> pOutput) {
|
||||||
return std::format("screencopy:{}-{}x{}", pOutput->stringPort, pOutput->size.x, pOutput->size.y);
|
RASSERT(pOutput, "Screencopy, but no valid output");
|
||||||
}
|
|
||||||
|
|
||||||
CScreencopyFrame::CScreencopyFrame(SP<COutput> pOutput) : m_outputRef(pOutput) {
|
|
||||||
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");
|
||||||
|
|
||||||
|
m_asset = makeAtomicShared<CTexture>();
|
||||||
|
m_resourceID = CAsyncResourceManager::resourceIDForScreencopy(pOutput->stringPort);
|
||||||
|
|
||||||
|
m_sc = makeShared<CCZwlrScreencopyFrameV1>(g_pHyprlock->getScreencopy()->sendCaptureOutput(false, pOutput->m_wlOutput->resource()));
|
||||||
|
|
||||||
if (*SCMODE == 1)
|
if (*SCMODE == 1)
|
||||||
m_frame = makeUnique<CSCSHMFrame>(m_sc);
|
m_frame = makeUnique<CSCSHMFrame>(m_sc);
|
||||||
else
|
else
|
||||||
m_frame = makeUnique<CSCDMAFrame>(m_sc);
|
m_frame = makeUnique<CSCDMAFrame>(m_sc);
|
||||||
}
|
|
||||||
|
|
||||||
void CScreencopyFrame::captureOutput() {
|
|
||||||
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);
|
||||||
|
|
@ -73,6 +67,8 @@ void CScreencopyFrame::captureOutput() {
|
||||||
}
|
}
|
||||||
|
|
||||||
m_sc.reset();
|
m_sc.reset();
|
||||||
|
m_ready = true;
|
||||||
|
g_asyncResourceManager->screencopyToTexture(*this);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -80,11 +76,21 @@ CSCDMAFrame::CSCDMAFrame(SP<CCZwlrScreencopyFrameV1> sc) : m_sc(sc) {
|
||||||
if (!glEGLImageTargetTexture2DOES) {
|
if (!glEGLImageTargetTexture2DOES) {
|
||||||
glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress("glEGLImageTargetTexture2DOES");
|
glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress("glEGLImageTargetTexture2DOES");
|
||||||
if (!glEGLImageTargetTexture2DOES) {
|
if (!glEGLImageTargetTexture2DOES) {
|
||||||
Debug::log(ERR, "No glEGLImageTargetTexture2DOES??");
|
Debug::log(ERR, "[sc] No glEGLImageTargetTexture2DOES??");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!g_pHyprlock->dma.linuxDmabuf) {
|
||||||
|
Debug::log(ERR, "[sc] No DMABUF support?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!g_pHyprlock->dma.gbmDevice) {
|
||||||
|
Debug::log(ERR, "[sc] No gbmDevice for DMABUF was created?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!eglQueryDmaBufModifiersEXT)
|
if (!eglQueryDmaBufModifiersEXT)
|
||||||
eglQueryDmaBufModifiersEXT = (PFNEGLQUERYDMABUFMODIFIERSEXTPROC)eglGetProcAddress("eglQueryDmaBufModifiersEXT");
|
eglQueryDmaBufModifiersEXT = (PFNEGLQUERYDMABUFMODIFIERSEXTPROC)eglGetProcAddress("eglQueryDmaBufModifiersEXT");
|
||||||
|
|
||||||
|
|
@ -191,7 +197,7 @@ bool CSCDMAFrame::onBufferDone() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CSCDMAFrame::onBufferReady(SPreloadedAsset& asset) {
|
bool CSCDMAFrame::onBufferReady(ASP<CTexture> texture) {
|
||||||
static constexpr struct {
|
static constexpr struct {
|
||||||
EGLAttrib fd;
|
EGLAttrib fd;
|
||||||
EGLAttrib offset;
|
EGLAttrib offset;
|
||||||
|
|
@ -245,9 +251,9 @@ bool CSCDMAFrame::onBufferReady(SPreloadedAsset& asset) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
asset.texture.allocate();
|
texture->allocate();
|
||||||
asset.texture.m_vSize = {m_w, m_h};
|
texture->m_vSize = {m_w, m_h};
|
||||||
glBindTexture(GL_TEXTURE_2D, asset.texture.m_iTexID);
|
glBindTexture(GL_TEXTURE_2D, texture->m_iTexID);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
|
@ -255,9 +261,7 @@ bool CSCDMAFrame::onBufferReady(SPreloadedAsset& asset) {
|
||||||
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_image);
|
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_image);
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
Debug::log(LOG, "Got dma frame with size {}", asset.texture.m_vSize);
|
Debug::log(LOG, "Got dma frame with size {}", texture->m_vSize);
|
||||||
|
|
||||||
asset.ready = true;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -449,14 +453,14 @@ void CSCSHMFrame::convertBuffer() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CSCSHMFrame::onBufferReady(SPreloadedAsset& asset) {
|
bool CSCSHMFrame::onBufferReady(ASP<CTexture> texture) {
|
||||||
convertBuffer();
|
convertBuffer();
|
||||||
|
|
||||||
asset.texture.allocate();
|
texture->allocate();
|
||||||
asset.texture.m_vSize.x = m_w;
|
texture->m_vSize.x = m_w;
|
||||||
asset.texture.m_vSize.y = m_h;
|
texture->m_vSize.y = m_h;
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, asset.texture.m_iTexID);
|
glBindTexture(GL_TEXTURE_2D, texture->m_iTexID);
|
||||||
|
|
||||||
void* buffer = m_convBuffer ? m_convBuffer : m_shmData;
|
void* buffer = m_convBuffer ? m_convBuffer : m_shmData;
|
||||||
|
|
||||||
|
|
@ -467,9 +471,7 @@ bool CSCSHMFrame::onBufferReady(SPreloadedAsset& asset) {
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_w, m_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_w, m_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
Debug::log(LOG, "[sc] [shm] Got screenshot with size {}", asset.texture.m_vSize);
|
Debug::log(LOG, "[sc] [shm] Got screenshot with size {}", texture->m_vSize);
|
||||||
|
|
||||||
asset.ready = true;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,9 @@
|
||||||
|
|
||||||
#include "../defines.hpp"
|
#include "../defines.hpp"
|
||||||
#include "../core/Output.hpp"
|
#include "../core/Output.hpp"
|
||||||
|
#include "../renderer/Texture.hpp"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <gbm.h>
|
#include <gbm.h>
|
||||||
#include <memory>
|
|
||||||
#include "Shared.hpp"
|
|
||||||
#include "linux-dmabuf-v1.hpp"
|
#include "linux-dmabuf-v1.hpp"
|
||||||
#include "wlr-screencopy-unstable-v1.hpp"
|
#include "wlr-screencopy-unstable-v1.hpp"
|
||||||
|
|
||||||
|
|
@ -15,27 +14,26 @@ class ISCFrame {
|
||||||
virtual ~ISCFrame() = default;
|
virtual ~ISCFrame() = default;
|
||||||
|
|
||||||
virtual bool onBufferDone() = 0;
|
virtual bool onBufferDone() = 0;
|
||||||
virtual bool onBufferReady(SPreloadedAsset& asset) = 0;
|
virtual bool onBufferReady(ASP<CTexture> asset) = 0;
|
||||||
|
|
||||||
SP<CCWlBuffer> m_wlBuffer = nullptr;
|
SP<CCWlBuffer> m_wlBuffer = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CScreencopyFrame {
|
class CScreencopyFrame {
|
||||||
public:
|
public:
|
||||||
static std::string getResourceId(SP<COutput> pOutput);
|
CScreencopyFrame() = default;
|
||||||
|
|
||||||
CScreencopyFrame(SP<COutput> pOutput);
|
|
||||||
~CScreencopyFrame() = default;
|
~CScreencopyFrame() = default;
|
||||||
|
|
||||||
void captureOutput();
|
void capture(SP<COutput> pOutput);
|
||||||
|
|
||||||
SP<CCZwlrScreencopyFrameV1> m_sc = nullptr;
|
SP<CCZwlrScreencopyFrameV1> m_sc = nullptr;
|
||||||
|
|
||||||
std::string m_resourceID;
|
size_t m_resourceID;
|
||||||
SPreloadedAsset m_asset;
|
ASP<CTexture> m_asset;
|
||||||
|
|
||||||
|
bool m_ready = false;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
WP<COutput> m_outputRef;
|
|
||||||
UP<ISCFrame> m_frame = nullptr;
|
UP<ISCFrame> m_frame = nullptr;
|
||||||
|
|
||||||
bool m_dmaFailed = false;
|
bool m_dmaFailed = false;
|
||||||
|
|
@ -47,7 +45,7 @@ class CSCDMAFrame : public ISCFrame {
|
||||||
CSCDMAFrame(SP<CCZwlrScreencopyFrameV1> sc);
|
CSCDMAFrame(SP<CCZwlrScreencopyFrameV1> sc);
|
||||||
virtual ~CSCDMAFrame();
|
virtual ~CSCDMAFrame();
|
||||||
|
|
||||||
virtual bool onBufferReady(SPreloadedAsset& asset);
|
virtual bool onBufferReady(ASP<CTexture> asset);
|
||||||
virtual bool onBufferDone();
|
virtual bool onBufferDone();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -77,7 +75,7 @@ class CSCSHMFrame : public ISCFrame {
|
||||||
virtual bool onBufferDone() {
|
virtual bool onBufferDone() {
|
||||||
return m_ok;
|
return m_ok;
|
||||||
}
|
}
|
||||||
virtual bool onBufferReady(SPreloadedAsset& asset);
|
virtual bool onBufferReady(ASP<CTexture> texture);
|
||||||
void convertBuffer();
|
void convertBuffer();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
||||||
31
src/renderer/resources/TextCmdResource.cpp
Normal file
31
src/renderer/resources/TextCmdResource.cpp
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
|
||||||
|
#include "TextCmdResource.hpp"
|
||||||
|
|
||||||
|
#include "../../config/ConfigManager.hpp"
|
||||||
|
#include "../../helpers/MiscFunctions.hpp"
|
||||||
|
#include <hyprgraphics/resource/resources/TextResource.hpp>
|
||||||
|
|
||||||
|
using namespace Hyprgraphics;
|
||||||
|
|
||||||
|
CTextCmdResource::CTextCmdResource(CTextResource::STextResourceData&& data) : m_data(std::move(data)) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTextCmdResource::render() {
|
||||||
|
static const auto TRIM = g_pConfigManager->getValue<Hyprlang::INT>("general:text_trim");
|
||||||
|
|
||||||
|
CTextResource::STextResourceData textData = m_data;
|
||||||
|
|
||||||
|
textData.text = spawnSync(m_data.text);
|
||||||
|
|
||||||
|
if (*TRIM) {
|
||||||
|
textData.text.erase(0, textData.text.find_first_not_of(" \n\r\t"));
|
||||||
|
textData.text.erase(textData.text.find_last_not_of(" \n\r\t") + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Hyprgraphics::CTextResource textResource(std::move(textData));
|
||||||
|
|
||||||
|
textResource.render();
|
||||||
|
|
||||||
|
std::swap(m_asset, textResource.m_asset);
|
||||||
|
}
|
||||||
15
src/renderer/resources/TextCmdResource.hpp
Normal file
15
src/renderer/resources/TextCmdResource.hpp
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <hyprgraphics/resource/resources/AsyncResource.hpp>
|
||||||
|
#include <hyprgraphics/resource/resources/TextResource.hpp>
|
||||||
|
|
||||||
|
class CTextCmdResource : public Hyprgraphics::IAsyncResource {
|
||||||
|
public:
|
||||||
|
CTextCmdResource(Hyprgraphics::CTextResource::STextResourceData&& data);
|
||||||
|
virtual ~CTextCmdResource() = default;
|
||||||
|
|
||||||
|
virtual void render();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Hyprgraphics::CTextResource::STextResourceData m_data;
|
||||||
|
};
|
||||||
|
|
@ -1,19 +1,28 @@
|
||||||
#include "Background.hpp"
|
#include "Background.hpp"
|
||||||
#include "../Renderer.hpp"
|
#include "../Renderer.hpp"
|
||||||
|
#include "../AsyncResourceManager.hpp"
|
||||||
|
#include "../Framebuffer.hpp"
|
||||||
#include "../../core/hyprlock.hpp"
|
#include "../../core/hyprlock.hpp"
|
||||||
#include "../../helpers/Log.hpp"
|
#include "../../helpers/Log.hpp"
|
||||||
#include "../../helpers/MiscFunctions.hpp"
|
#include "../../helpers/MiscFunctions.hpp"
|
||||||
|
#include "../../core/AnimationManager.hpp"
|
||||||
|
#include "../../config/ConfigManager.hpp"
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <hyprlang.hpp>
|
#include <hyprlang.hpp>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <memory>
|
|
||||||
#include <GLES3/gl32.h>
|
#include <GLES3/gl32.h>
|
||||||
|
|
||||||
|
CBackground::CBackground() {
|
||||||
|
blurredFB = makeUnique<CFramebuffer>();
|
||||||
|
pendingBlurredFB = makeUnique<CFramebuffer>();
|
||||||
|
transformedScFB = makeUnique<CFramebuffer>();
|
||||||
|
}
|
||||||
|
|
||||||
CBackground::~CBackground() {
|
CBackground::~CBackground() {
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CBackground::registerSelf(const SP<CBackground>& self) {
|
void CBackground::registerSelf(const ASP<CBackground>& self) {
|
||||||
m_self = self;
|
m_self = self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -32,7 +41,6 @@ void CBackground::configure(const std::unordered_map<std::string, std::any>& pro
|
||||||
path = std::any_cast<Hyprlang::STRING>(props.at("path"));
|
path = std::any_cast<Hyprlang::STRING>(props.at("path"));
|
||||||
reloadCommand = std::any_cast<Hyprlang::STRING>(props.at("reload_cmd"));
|
reloadCommand = std::any_cast<Hyprlang::STRING>(props.at("reload_cmd"));
|
||||||
reloadTime = std::any_cast<Hyprlang::INT>(props.at("reload_time"));
|
reloadTime = std::any_cast<Hyprlang::INT>(props.at("reload_time"));
|
||||||
crossFadeTime = std::any_cast<Hyprlang::FLOAT>(props.at("crossfade_time"));
|
|
||||||
|
|
||||||
} catch (const std::bad_any_cast& e) {
|
} catch (const std::bad_any_cast& e) {
|
||||||
RASSERT(false, "Failed to construct CBackground: {}", e.what()); //
|
RASSERT(false, "Failed to construct CBackground: {}", e.what()); //
|
||||||
|
|
@ -44,31 +52,33 @@ void CBackground::configure(const std::unordered_map<std::string, std::any>& pro
|
||||||
|
|
||||||
viewport = pOutput->getViewport();
|
viewport = pOutput->getViewport();
|
||||||
outputPort = pOutput->stringPort;
|
outputPort = pOutput->stringPort;
|
||||||
transform = isScreenshot ? wlTransformToHyprutils(invertTransform(pOutput->transform)) : HYPRUTILS_TRANSFORM_NORMAL;
|
transform = wlTransformToHyprutils(invertTransform(pOutput->transform));
|
||||||
|
scResourceID = CAsyncResourceManager::resourceIDForScreencopy(pOutput->stringPort);
|
||||||
|
|
||||||
|
g_pAnimationManager->createAnimation(0.f, crossFadeProgress, g_pConfigManager->m_AnimationTree.getConfig("fadeIn"));
|
||||||
|
|
||||||
|
if (!g_asyncResourceManager->checkIdPresent(scResourceID)) {
|
||||||
|
Debug::log(LOG, "Missing screenshot for output {}", outputPort);
|
||||||
|
scResourceID = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (isScreenshot) {
|
if (isScreenshot) {
|
||||||
resourceID = CScreencopyFrame::getResourceId(pOutput);
|
resourceID = scResourceID; // Fallback to solid background:color when scResourceID==0
|
||||||
// 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()) {
|
if (!g_pHyprlock->getScreencopy()) {
|
||||||
Debug::log(ERR, "No screencopy support! path=screenshot won't work. Falling back to background color.");
|
Debug::log(ERR, "No screencopy support! path=screenshot won't work. Falling back to background color.");
|
||||||
resourceID = "";
|
resourceID = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (!path.empty())
|
} else if (!path.empty())
|
||||||
resourceID = "background:" + path;
|
resourceID = g_asyncResourceManager->requestImage(path, m_imageRevision, nullptr);
|
||||||
|
|
||||||
if (!isScreenshot && reloadTime > -1) {
|
if (!reloadCommand.empty() && reloadTime > -1) {
|
||||||
try {
|
try {
|
||||||
|
if (!isScreenshot)
|
||||||
modificationTime = std::filesystem::last_write_time(absolutePath(path, ""));
|
modificationTime = std::filesystem::last_write_time(absolutePath(path, ""));
|
||||||
} catch (std::exception& e) { Debug::log(ERR, "{}", e.what()); }
|
} catch (std::exception& e) { Debug::log(ERR, "{}", e.what()); }
|
||||||
|
|
||||||
plantReloadTimer(); // No reloads for screenshots.
|
plantReloadTimer(); // No reloads if reloadCommand is empty
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,13 +88,59 @@ void CBackground::reset() {
|
||||||
reloadTimer.reset();
|
reloadTimer.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fade) {
|
blurredFB->destroyBuffer();
|
||||||
if (fade->crossFadeTimer) {
|
pendingBlurredFB->destroyBuffer();
|
||||||
fade->crossFadeTimer->cancel();
|
}
|
||||||
fade->crossFadeTimer.reset();
|
|
||||||
}
|
void CBackground::updatePrimaryAsset() {
|
||||||
fade.reset();
|
if (asset || resourceID == 0)
|
||||||
}
|
return;
|
||||||
|
|
||||||
|
asset = g_asyncResourceManager->getAssetByID(resourceID);
|
||||||
|
if (!asset)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const bool NEEDFB = (isScreenshot || blurPasses > 0 || asset->m_vSize != viewport || transform != HYPRUTILS_TRANSFORM_NORMAL) && (!blurredFB->isAllocated() || firstRender);
|
||||||
|
if (NEEDFB)
|
||||||
|
renderToFB(*asset, *blurredFB, blurPasses, isScreenshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBackground::updatePendingAsset() {
|
||||||
|
// For crossfading a new asset
|
||||||
|
if (!pendingAsset || blurPasses == 0 || pendingBlurredFB->isAllocated())
|
||||||
|
return;
|
||||||
|
|
||||||
|
renderToFB(*pendingAsset, *pendingBlurredFB, blurPasses);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBackground::updateScAsset() {
|
||||||
|
if (scAsset || scResourceID == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// path=screenshot -> scAsset = asset
|
||||||
|
scAsset = (asset && isScreenshot) ? asset : g_asyncResourceManager->getAssetByID(scResourceID);
|
||||||
|
if (!scAsset)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const bool NEEDSCTRANSFORM = transform != HYPRUTILS_TRANSFORM_NORMAL;
|
||||||
|
if (NEEDSCTRANSFORM)
|
||||||
|
renderToFB(*scAsset, *transformedScFB, 0, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const CTexture& CBackground::getPrimaryAssetTex() const {
|
||||||
|
// This case is only for background:path=screenshot with blurPasses=0
|
||||||
|
if (isScreenshot && blurPasses == 0 && transformedScFB->isAllocated())
|
||||||
|
return transformedScFB->m_cTex;
|
||||||
|
|
||||||
|
return (blurredFB->isAllocated()) ? blurredFB->m_cTex : *asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CTexture& CBackground::getPendingAssetTex() const {
|
||||||
|
return (pendingBlurredFB->isAllocated()) ? pendingBlurredFB->m_cTex : *pendingAsset;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CTexture& CBackground::getScAssetTex() const {
|
||||||
|
return (transformedScFB->isAllocated()) ? transformedScFB->m_cTex : *scAsset;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CBackground::renderRect(CHyprColor color) {
|
void CBackground::renderRect(CHyprColor color) {
|
||||||
|
|
@ -92,60 +148,14 @@ void CBackground::renderRect(CHyprColor color) {
|
||||||
g_pRenderer->renderRect(monbox, color, 0);
|
g_pRenderer->renderRect(monbox, color, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void onReloadTimer(WP<CBackground> ref) {
|
static void onReloadTimer(AWP<CBackground> ref) {
|
||||||
if (auto PBG = ref.lock(); PBG) {
|
if (auto PBG = ref.lock(); PBG) {
|
||||||
PBG->onReloadTimerUpdate();
|
PBG->onReloadTimerUpdate();
|
||||||
PBG->plantReloadTimer();
|
PBG->plantReloadTimer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void onCrossFadeTimer(WP<CBackground> ref) {
|
static CBox getScaledBoxForTextureSize(const Vector2D& size, const Vector2D& viewport) {
|
||||||
if (auto PBG = ref.lock(); PBG)
|
|
||||||
PBG->onCrossFadeTimerUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void onAssetCallback(WP<CBackground> ref) {
|
|
||||||
if (auto PBG = ref.lock(); PBG)
|
|
||||||
PBG->startCrossFadeOrUpdateRender();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CBackground::draw(const SRenderData& data) {
|
|
||||||
|
|
||||||
if (resourceID.empty()) {
|
|
||||||
CHyprColor col = color;
|
|
||||||
col.a *= data.opacity;
|
|
||||||
renderRect(col);
|
|
||||||
return data.opacity < 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!asset)
|
|
||||||
asset = g_pRenderer->asyncResourceGatherer->getAssetByID(resourceID);
|
|
||||||
|
|
||||||
if (!asset) {
|
|
||||||
CHyprColor col = color;
|
|
||||||
col.a *= data.opacity;
|
|
||||||
renderRect(col);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (asset->texture.m_iType == TEXTURE_INVALID) {
|
|
||||||
g_pRenderer->asyncResourceGatherer->unloadAsset(asset);
|
|
||||||
resourceID = "";
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fade || ((blurPasses > 0 || isScreenshot) && (!blurredFB.isAllocated() || firstRender))) {
|
|
||||||
|
|
||||||
if (firstRender)
|
|
||||||
firstRender = false;
|
|
||||||
|
|
||||||
// make it brah
|
|
||||||
Vector2D size = asset->texture.m_vSize;
|
|
||||||
if (transform % 2 == 1 && isScreenshot) {
|
|
||||||
size.x = asset->texture.m_vSize.y;
|
|
||||||
size.y = asset->texture.m_vSize.x;
|
|
||||||
}
|
|
||||||
|
|
||||||
CBox texbox = {{}, size};
|
CBox texbox = {{}, size};
|
||||||
|
|
||||||
float scaleX = viewport.x / size.x;
|
float scaleX = viewport.x / size.x;
|
||||||
|
|
@ -160,49 +170,113 @@ bool CBackground::draw(const SRenderData& data) {
|
||||||
texbox.x = -(texbox.w - viewport.x) / 2.f;
|
texbox.x = -(texbox.w - viewport.x) / 2.f;
|
||||||
texbox.round();
|
texbox.round();
|
||||||
|
|
||||||
if (!blurredFB.isAllocated())
|
return texbox;
|
||||||
blurredFB.alloc(viewport.x, viewport.y); // TODO 10 bit
|
}
|
||||||
|
|
||||||
blurredFB.bind();
|
void CBackground::renderToFB(const CTexture& tex, CFramebuffer& fb, int passes, bool applyTransform) {
|
||||||
|
if (firstRender)
|
||||||
|
firstRender = false;
|
||||||
|
|
||||||
if (fade)
|
// make it brah
|
||||||
g_pRenderer->renderTextureMix(texbox, asset->texture, pendingAsset->texture, 1.0,
|
Vector2D size = tex.m_vSize;
|
||||||
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - fade->start).count() / (1000 * crossFadeTime), 0,
|
if (applyTransform && transform % 2 == 1) {
|
||||||
transform);
|
size.x = tex.m_vSize.y;
|
||||||
else
|
size.y = tex.m_vSize.x;
|
||||||
g_pRenderer->renderTexture(texbox, asset->texture, 1.0, 0, transform);
|
}
|
||||||
|
|
||||||
|
const auto TEXBOX = getScaledBoxForTextureSize(size, viewport);
|
||||||
|
|
||||||
|
if (!fb.isAllocated())
|
||||||
|
fb.alloc(viewport.x, viewport.y); // TODO 10 bit
|
||||||
|
|
||||||
|
fb.bind();
|
||||||
|
|
||||||
|
g_pRenderer->renderTexture(TEXBOX, tex, 1.0, 0, applyTransform ? transform : HYPRUTILS_TRANSFORM_NORMAL);
|
||||||
|
|
||||||
if (blurPasses > 0)
|
if (blurPasses > 0)
|
||||||
g_pRenderer->blurFB(blurredFB,
|
g_pRenderer->blurFB(fb,
|
||||||
CRenderer::SBlurParams{.size = blurSize,
|
CRenderer::SBlurParams{
|
||||||
.passes = blurPasses,
|
.size = blurSize,
|
||||||
|
.passes = passes,
|
||||||
.noise = noise,
|
.noise = noise,
|
||||||
.contrast = contrast,
|
.contrast = contrast,
|
||||||
.brightness = brightness,
|
.brightness = brightness,
|
||||||
.vibrancy = vibrancy,
|
.vibrancy = vibrancy,
|
||||||
.vibrancy_darkness = vibrancy_darkness});
|
.vibrancy_darkness = vibrancy_darkness,
|
||||||
|
});
|
||||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CBackground::draw(const SRenderData& data) {
|
||||||
|
updatePrimaryAsset();
|
||||||
|
updatePendingAsset();
|
||||||
|
updateScAsset();
|
||||||
|
|
||||||
|
if (asset && asset->m_iType == TEXTURE_INVALID) {
|
||||||
|
g_asyncResourceManager->unload(asset);
|
||||||
|
resourceID = 0;
|
||||||
|
renderRect(color);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CTexture* tex = blurredFB.isAllocated() ? &blurredFB.m_cTex : &asset->texture;
|
if (!asset || resourceID == 0) {
|
||||||
|
// fade in/out with a solid color
|
||||||
|
if (data.opacity < 1.0 && scAsset) {
|
||||||
|
const auto& SCTEX = getScAssetTex();
|
||||||
|
const auto SCTEXBOX = getScaledBoxForTextureSize(SCTEX.m_vSize, viewport);
|
||||||
|
g_pRenderer->renderTexture(SCTEXBOX, SCTEX, 1, 0, HYPRUTILS_TRANSFORM_FLIPPED_180);
|
||||||
|
CHyprColor col = color;
|
||||||
|
col.a *= data.opacity;
|
||||||
|
renderRect(col);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
CBox texbox = {{}, tex->m_vSize};
|
renderRect(color);
|
||||||
|
return !asset && resourceID > 0; // resource not ready
|
||||||
|
}
|
||||||
|
|
||||||
Vector2D size = tex->m_vSize;
|
const auto& TEX = getPrimaryAssetTex();
|
||||||
float scaleX = viewport.x / tex->m_vSize.x;
|
const auto TEXBOX = getScaledBoxForTextureSize(TEX.m_vSize, viewport);
|
||||||
float scaleY = viewport.y / tex->m_vSize.y;
|
if (data.opacity < 1.0 && scAsset) {
|
||||||
|
const auto& SCTEX = getScAssetTex();
|
||||||
|
g_pRenderer->renderTextureMix(TEXBOX, SCTEX, TEX, 1.0, data.opacity, 0);
|
||||||
|
} else if (crossFadeProgress->isBeingAnimated()) {
|
||||||
|
const auto& PENDINGTEX = getPendingAssetTex();
|
||||||
|
g_pRenderer->renderTextureMix(TEXBOX, TEX, PENDINGTEX, 1.0, crossFadeProgress->value(), 0);
|
||||||
|
} else
|
||||||
|
g_pRenderer->renderTexture(TEXBOX, TEX, 1, 0);
|
||||||
|
|
||||||
texbox.w *= std::max(scaleX, scaleY);
|
return crossFadeProgress->isBeingAnimated() || data.opacity < 1.0;
|
||||||
texbox.h *= std::max(scaleX, scaleY);
|
}
|
||||||
|
|
||||||
if (scaleX > scaleY)
|
void CBackground::onAssetUpdate(ResourceID id, ASP<CTexture> newAsset) {
|
||||||
texbox.y = -(texbox.h - viewport.y) / 2.f;
|
pendingResource = false;
|
||||||
else
|
|
||||||
texbox.x = -(texbox.w - viewport.x) / 2.f;
|
|
||||||
texbox.round();
|
|
||||||
g_pRenderer->renderTexture(texbox, *tex, data.opacity, 0, HYPRUTILS_TRANSFORM_FLIPPED_180);
|
|
||||||
|
|
||||||
return fade || data.opacity < 1.0; // actively render during fading
|
if (!newAsset)
|
||||||
|
Debug::log(ERR, "Background asset update failed, resourceID: {} not available on update!", id);
|
||||||
|
else if (newAsset->m_iType == TEXTURE_INVALID) {
|
||||||
|
g_asyncResourceManager->unload(newAsset);
|
||||||
|
Debug::log(ERR, "New background asset has an invalid texture!");
|
||||||
|
} else {
|
||||||
|
pendingAsset = newAsset;
|
||||||
|
crossFadeProgress->setValueAndWarp(0);
|
||||||
|
*crossFadeProgress = 1.0;
|
||||||
|
|
||||||
|
crossFadeProgress->setCallbackOnEnd(
|
||||||
|
[REF = m_self, id](auto) {
|
||||||
|
if (const auto PSELF = REF.lock()) {
|
||||||
|
if (PSELF->asset)
|
||||||
|
g_asyncResourceManager->unload(PSELF->asset);
|
||||||
|
PSELF->asset = PSELF->pendingAsset;
|
||||||
|
PSELF->pendingAsset = nullptr;
|
||||||
|
PSELF->resourceID = id;
|
||||||
|
|
||||||
|
PSELF->blurredFB->destroyBuffer();
|
||||||
|
PSELF->blurredFB = std::move(PSELF->pendingBlurredFB);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CBackground::plantReloadTimer() {
|
void CBackground::plantReloadTimer() {
|
||||||
|
|
@ -213,34 +287,13 @@ void CBackground::plantReloadTimer() {
|
||||||
reloadTimer = g_pHyprlock->addTimer(std::chrono::seconds(reloadTime), [REF = m_self](auto, auto) { onReloadTimer(REF); }, nullptr, true);
|
reloadTimer = g_pHyprlock->addTimer(std::chrono::seconds(reloadTime), [REF = m_self](auto, auto) { onReloadTimer(REF); }, nullptr, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CBackground::onCrossFadeTimerUpdate() {
|
|
||||||
|
|
||||||
// Animation done: Unload previous asset, deinitialize the fade and pass the asset
|
|
||||||
|
|
||||||
if (fade) {
|
|
||||||
fade->crossFadeTimer.reset();
|
|
||||||
fade.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (blurPasses <= 0 && !isScreenshot)
|
|
||||||
blurredFB.release();
|
|
||||||
|
|
||||||
asset = pendingAsset;
|
|
||||||
resourceID = pendingResourceID;
|
|
||||||
pendingResourceID = "";
|
|
||||||
pendingAsset = nullptr;
|
|
||||||
firstRender = true;
|
|
||||||
|
|
||||||
g_pHyprlock->renderOutput(outputPort);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBackground::onReloadTimerUpdate() {
|
void CBackground::onReloadTimerUpdate() {
|
||||||
const std::string OLDPATH = path;
|
const std::string OLDPATH = path;
|
||||||
|
|
||||||
// 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();
|
||||||
|
|
@ -258,58 +311,22 @@ void CBackground::onReloadTimerUpdate() {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
modificationTime = MTIME;
|
modificationTime = MTIME;
|
||||||
|
if (OLDPATH == path)
|
||||||
|
m_imageRevision++;
|
||||||
|
else
|
||||||
|
m_imageRevision = 0;
|
||||||
} catch (std::exception& e) {
|
} catch (std::exception& e) {
|
||||||
path = OLDPATH;
|
path = OLDPATH;
|
||||||
Debug::log(ERR, "{}", e.what());
|
Debug::log(ERR, "{}", e.what());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pendingResourceID.empty())
|
if (pendingResource)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
pendingResource = true;
|
||||||
|
|
||||||
// Issue the next request
|
// Issue the next request
|
||||||
|
AWP<IWidget> widget(m_self);
|
||||||
request.id = std::string{"background:"} + path + ",time:" + std::to_string((uint64_t)modificationTime.time_since_epoch().count());
|
g_asyncResourceManager->requestImage(path, m_imageRevision, widget);
|
||||||
pendingResourceID = request.id;
|
|
||||||
request.asset = path;
|
|
||||||
request.type = CAsyncResourceGatherer::eTargetType::TARGET_IMAGE;
|
|
||||||
|
|
||||||
request.callback = [REF = m_self]() { onAssetCallback(REF); };
|
|
||||||
|
|
||||||
g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBackground::startCrossFadeOrUpdateRender() {
|
|
||||||
auto newAsset = g_pRenderer->asyncResourceGatherer->getAssetByID(pendingResourceID);
|
|
||||||
if (newAsset) {
|
|
||||||
if (newAsset->texture.m_iType == TEXTURE_INVALID) {
|
|
||||||
g_pRenderer->asyncResourceGatherer->unloadAsset(newAsset);
|
|
||||||
Debug::log(ERR, "New asset had an invalid texture!");
|
|
||||||
} else if (resourceID != pendingResourceID) {
|
|
||||||
pendingAsset = newAsset;
|
|
||||||
if (crossFadeTime > 0) {
|
|
||||||
// Start a fade
|
|
||||||
if (!fade)
|
|
||||||
fade = makeUnique<SFade>(std::chrono::system_clock::now(), 0, nullptr);
|
|
||||||
else {
|
|
||||||
// Maybe we where already fading so reset it just in case, but should'nt be happening.
|
|
||||||
if (fade->crossFadeTimer) {
|
|
||||||
fade->crossFadeTimer->cancel();
|
|
||||||
fade->crossFadeTimer.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fade->start = std::chrono::system_clock::now();
|
|
||||||
fade->a = 0;
|
|
||||||
fade->crossFadeTimer =
|
|
||||||
g_pHyprlock->addTimer(std::chrono::milliseconds((int)(1000.0 * crossFadeTime)), [REF = m_self](auto, auto) { onCrossFadeTimer(REF); }, nullptr);
|
|
||||||
} else {
|
|
||||||
onCrossFadeTimerUpdate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (!pendingResourceID.empty()) {
|
|
||||||
Debug::log(WARN, "Asset {} not available after the asyncResourceGatherer's callback!", pendingResourceID);
|
|
||||||
g_pHyprlock->addTimer(std::chrono::milliseconds(100), [REF = m_self](auto, auto) { onAssetCallback(REF); }, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_pHyprlock->renderOutput(outputPort);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,51 +1,55 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "IWidget.hpp"
|
#include "IWidget.hpp"
|
||||||
|
#include "../../defines.hpp"
|
||||||
|
#include "../../helpers/AnimatedVariable.hpp"
|
||||||
#include "../../helpers/Color.hpp"
|
#include "../../helpers/Color.hpp"
|
||||||
#include "../../helpers/Math.hpp"
|
|
||||||
#include "../../core/Timer.hpp"
|
#include "../../core/Timer.hpp"
|
||||||
#include "../Framebuffer.hpp"
|
#include "../Framebuffer.hpp"
|
||||||
#include "../AsyncResourceGatherer.hpp"
|
|
||||||
#include <hyprutils/math/Misc.hpp>
|
#include <hyprutils/math/Misc.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <any>
|
#include <any>
|
||||||
#include <chrono>
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
struct SPreloadedAsset;
|
struct SPreloadedAsset;
|
||||||
class COutput;
|
class COutput;
|
||||||
|
|
||||||
struct SFade {
|
|
||||||
std::chrono::system_clock::time_point start;
|
|
||||||
float a = 0;
|
|
||||||
std::shared_ptr<CTimer> crossFadeTimer = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CBackground : public IWidget {
|
class CBackground : public IWidget {
|
||||||
public:
|
public:
|
||||||
CBackground() = default;
|
CBackground();
|
||||||
~CBackground();
|
~CBackground();
|
||||||
|
|
||||||
void registerSelf(const SP<CBackground>& self);
|
void registerSelf(const ASP<CBackground>& self);
|
||||||
|
|
||||||
virtual void configure(const std::unordered_map<std::string, std::any>& props, const SP<COutput>& pOutput);
|
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 void onAssetUpdate(ResourceID id, ASP<CTexture> newAsset);
|
||||||
|
|
||||||
void reset(); // Unload assets, remove timers, etc.
|
void reset(); // Unload assets, remove timers, etc.
|
||||||
|
|
||||||
|
void updatePrimaryAsset();
|
||||||
|
void updatePendingAsset();
|
||||||
|
void updateScAsset();
|
||||||
|
|
||||||
|
const CTexture& getPrimaryAssetTex() const;
|
||||||
|
const CTexture& getPendingAssetTex() const;
|
||||||
|
const CTexture& getScAssetTex() const;
|
||||||
|
|
||||||
void renderRect(CHyprColor color);
|
void renderRect(CHyprColor color);
|
||||||
|
void renderToFB(const CTexture& text, CFramebuffer& fb, int passes, bool applyTransform = false);
|
||||||
|
|
||||||
void onReloadTimerUpdate();
|
void onReloadTimerUpdate();
|
||||||
void onCrossFadeTimerUpdate();
|
|
||||||
void plantReloadTimer();
|
void plantReloadTimer();
|
||||||
void startCrossFadeOrUpdateRender();
|
void startCrossFade();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
WP<CBackground> m_self;
|
AWP<CBackground> m_self;
|
||||||
|
|
||||||
// if needed
|
// if needed
|
||||||
CFramebuffer blurredFB;
|
UP<CFramebuffer> blurredFB;
|
||||||
|
UP<CFramebuffer> pendingBlurredFB;
|
||||||
|
UP<CFramebuffer> transformedScFB;
|
||||||
|
|
||||||
int blurSize = 10;
|
int blurSize = 10;
|
||||||
int blurPasses = 3;
|
int blurPasses = 3;
|
||||||
|
|
@ -60,22 +64,22 @@ class CBackground : public IWidget {
|
||||||
std::string outputPort;
|
std::string outputPort;
|
||||||
Hyprutils::Math::eTransform transform;
|
Hyprutils::Math::eTransform transform;
|
||||||
|
|
||||||
std::string resourceID;
|
ResourceID resourceID = 0;
|
||||||
std::string pendingResourceID;
|
ResourceID scResourceID = 0;
|
||||||
|
bool pendingResource = false;
|
||||||
|
|
||||||
float crossFadeTime = -1.0;
|
PHLANIMVAR<float> crossFadeProgress;
|
||||||
|
|
||||||
CHyprColor color;
|
CHyprColor color;
|
||||||
SPreloadedAsset* asset = nullptr;
|
ASP<CTexture> asset = nullptr;
|
||||||
|
ASP<CTexture> scAsset = nullptr;
|
||||||
|
ASP<CTexture> pendingAsset = nullptr;
|
||||||
bool isScreenshot = false;
|
bool isScreenshot = false;
|
||||||
SPreloadedAsset* pendingAsset = nullptr;
|
|
||||||
bool firstRender = true;
|
bool firstRender = true;
|
||||||
|
|
||||||
UP<SFade> fade;
|
|
||||||
|
|
||||||
int reloadTime = -1;
|
int reloadTime = -1;
|
||||||
std::string reloadCommand;
|
std::string reloadCommand;
|
||||||
CAsyncResourceGatherer::SPreloadRequest request;
|
ASP<CTimer> reloadTimer;
|
||||||
std::shared_ptr<CTimer> reloadTimer;
|
|
||||||
std::filesystem::file_time_type modificationTime;
|
std::filesystem::file_time_type modificationTime;
|
||||||
|
size_t m_imageRevision = 0;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#include "../../core/hyprlock.hpp"
|
#include "../../core/hyprlock.hpp"
|
||||||
#include "../../auth/Auth.hpp"
|
#include "../../auth/Auth.hpp"
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <hyprgraphics/resource/resources/TextResource.hpp>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <hyprutils/string/String.hpp>
|
#include <hyprutils/string/String.hpp>
|
||||||
|
|
@ -10,7 +11,7 @@
|
||||||
|
|
||||||
using namespace Hyprutils::String;
|
using namespace Hyprutils::String;
|
||||||
|
|
||||||
#if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION < 190100
|
#if defined(_LIBCPP_VERSION)
|
||||||
#pragma comment(lib, "date-tz")
|
#pragma comment(lib, "date-tz")
|
||||||
#include <date/tz.h>
|
#include <date/tz.h>
|
||||||
namespace std {
|
namespace std {
|
||||||
|
|
@ -76,6 +77,16 @@ int IWidget::roundingForBorderBox(const CBox& borderBox, int roundingConfig, int
|
||||||
return std::clamp(roundingConfig + thickness, 0, MINHALFBORDER);
|
return std::clamp(roundingConfig + thickness, 0, MINHALFBORDER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Hyprgraphics::CTextResource::eTextAlignmentMode IWidget::parseTextAlignment(const std::string& alignment) {
|
||||||
|
Hyprgraphics::CTextResource::eTextAlignmentMode align = Hyprgraphics::CTextResource::TEXT_ALIGN_LEFT;
|
||||||
|
if (alignment == "center")
|
||||||
|
align = Hyprgraphics::CTextResource::TEXT_ALIGN_CENTER;
|
||||||
|
else if (alignment == "right")
|
||||||
|
align = Hyprgraphics::CTextResource::TEXT_ALIGN_RIGHT;
|
||||||
|
|
||||||
|
return align;
|
||||||
|
}
|
||||||
|
|
||||||
static void replaceAllAttempts(std::string& str) {
|
static void replaceAllAttempts(std::string& str) {
|
||||||
|
|
||||||
const size_t ATTEMPTS = g_pAuth->getFailedAttempts();
|
const size_t ATTEMPTS = g_pAuth->getFailedAttempts();
|
||||||
|
|
@ -116,7 +127,7 @@ static void replaceAllLayout(std::string& str) {
|
||||||
const CVarList LANGS(REPL);
|
const CVarList LANGS(REPL);
|
||||||
if (LAYOUTIDX >= LANGS.size()) {
|
if (LAYOUTIDX >= LANGS.size()) {
|
||||||
Debug::log(ERR, "Layout index {} out of bounds. Max is {}.", LAYOUTIDX, LANGS.size() - 1);
|
Debug::log(ERR, "Layout index {} out of bounds. Max is {}.", LAYOUTIDX, LANGS.size() - 1);
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string LANG = LANGS[LAYOUTIDX].empty() ? layoutName : LANGS[LAYOUTIDX] == "!" ? "" : LANGS[LAYOUTIDX];
|
const std::string LANG = LANGS[LAYOUTIDX].empty() ? layoutName : LANGS[LAYOUTIDX] == "!" ? "" : LANGS[LAYOUTIDX];
|
||||||
|
|
@ -175,8 +186,8 @@ static std::string getTime12h() {
|
||||||
IWidget::SFormatResult IWidget::formatString(std::string in) {
|
IWidget::SFormatResult IWidget::formatString(std::string in) {
|
||||||
|
|
||||||
auto uidPassword = getpwuid(getuid());
|
auto uidPassword = getpwuid(getuid());
|
||||||
char* username = uidPassword->pw_name;
|
char* username = uidPassword ? uidPassword->pw_name : nullptr;
|
||||||
char* user_gecos = uidPassword->pw_gecos;
|
char* user_gecos = uidPassword ? uidPassword->pw_gecos : nullptr;
|
||||||
|
|
||||||
if (!username)
|
if (!username)
|
||||||
Debug::log(ERR, "Error in formatString, username null. Errno: ", errno);
|
Debug::log(ERR, "Error in formatString, username null. Errno: ", errno);
|
||||||
|
|
@ -266,3 +277,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);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../../helpers/Math.hpp"
|
|
||||||
#include "../../defines.hpp"
|
#include "../../defines.hpp"
|
||||||
|
#include "../../helpers/Math.hpp"
|
||||||
|
#include "../../core/Seat.hpp"
|
||||||
|
#include "../Texture.hpp"
|
||||||
|
|
||||||
|
#include <hyprgraphics/resource/resources/TextResource.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <any>
|
#include <any>
|
||||||
|
|
@ -18,11 +22,21 @@ class IWidget {
|
||||||
|
|
||||||
virtual void configure(const std::unordered_map<std::string, std::any>& prop, const SP<COutput>& pOutput) = 0;
|
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;
|
||||||
|
// Never render within onAssetUpdate!
|
||||||
|
virtual void onAssetUpdate(ResourceID id, ASP<CTexture> newAsset) = 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,
|
||||||
const double& ang = 0);
|
const double& ang = 0);
|
||||||
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);
|
||||||
|
static Hyprgraphics::CTextResource::eTextAlignmentMode parseTextAlignment(const std::string& alignment);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
@ -33,4 +47,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;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,37 +1,39 @@
|
||||||
#include "Image.hpp"
|
#include "Image.hpp"
|
||||||
#include "../Renderer.hpp"
|
#include "../Renderer.hpp"
|
||||||
|
#include "../AsyncResourceManager.hpp"
|
||||||
#include "../../core/hyprlock.hpp"
|
#include "../../core/hyprlock.hpp"
|
||||||
#include "../../helpers/Log.hpp"
|
#include "../../helpers/Log.hpp"
|
||||||
#include "../../helpers/MiscFunctions.hpp"
|
#include "../../helpers/MiscFunctions.hpp"
|
||||||
#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() {
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CImage::registerSelf(const SP<CImage>& self) {
|
void CImage::registerSelf(const ASP<CImage>& self) {
|
||||||
m_self = self;
|
m_self = self;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void onTimer(WP<CImage> ref) {
|
static void onTimer(AWP<CImage> ref) {
|
||||||
if (auto PIMAGE = ref.lock(); PIMAGE) {
|
if (auto PIMAGE = ref.lock(); PIMAGE) {
|
||||||
PIMAGE->onTimerUpdate();
|
PIMAGE->onTimerUpdate();
|
||||||
PIMAGE->plantTimer();
|
PIMAGE->plantTimer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void onAssetCallback(WP<CImage> ref) {
|
|
||||||
if (auto PIMAGE = ref.lock(); PIMAGE)
|
|
||||||
PIMAGE->renderUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CImage::onTimerUpdate() {
|
void CImage::onTimerUpdate() {
|
||||||
|
if (m_pendingResource) {
|
||||||
|
Debug::log(WARN, "Trying to update image, but a resource is still pending! Skipping update.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
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();
|
||||||
|
|
@ -49,22 +51,20 @@ void CImage::onTimerUpdate() {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
modificationTime = MTIME;
|
modificationTime = MTIME;
|
||||||
|
if (OLDPATH == path)
|
||||||
|
m_imageRevision++;
|
||||||
|
else
|
||||||
|
m_imageRevision = 0;
|
||||||
} catch (std::exception& e) {
|
} catch (std::exception& e) {
|
||||||
path = OLDPATH;
|
path = OLDPATH;
|
||||||
Debug::log(ERR, "{}", e.what());
|
Debug::log(ERR, "{}", e.what());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pendingResourceID.empty())
|
m_pendingResource = true;
|
||||||
return;
|
|
||||||
|
|
||||||
request.id = std::string{"image:"} + path + ",time:" + std::to_string((uint64_t)modificationTime.time_since_epoch().count());
|
AWP<IWidget> widget(m_self);
|
||||||
pendingResourceID = request.id;
|
g_asyncResourceManager->requestImage(path, m_imageRevision, widget);
|
||||||
request.asset = path;
|
|
||||||
request.type = CAsyncResourceGatherer::eTargetType::TARGET_IMAGE;
|
|
||||||
request.callback = [REF = m_self]() { onAssetCallback(REF); };
|
|
||||||
|
|
||||||
g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CImage::plantTimer() {
|
void CImage::plantTimer() {
|
||||||
|
|
@ -81,14 +81,14 @@ void CImage::configure(const std::unordered_map<std::string, std::any>& props, c
|
||||||
viewport = pOutput->getViewport();
|
viewport = pOutput->getViewport();
|
||||||
stringPort = pOutput->stringPort;
|
stringPort = pOutput->stringPort;
|
||||||
|
|
||||||
shadow.configure(m_self.lock(), props, viewport);
|
shadow.configure(m_self, 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,13 +96,14 @@ void CImage::configure(const std::unordered_map<std::string, std::any>& props, c
|
||||||
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;
|
resourceID = g_asyncResourceManager->requestImage(path, m_imageRevision, nullptr);
|
||||||
angle = angle * M_PI / 180.0;
|
angle = angle * M_PI / 180.0;
|
||||||
|
|
||||||
if (reloadTime > -1) {
|
if (reloadTime > -1) {
|
||||||
|
|
@ -123,30 +124,30 @@ void CImage::reset() {
|
||||||
if (g_pHyprlock->m_bTerminate)
|
if (g_pHyprlock->m_bTerminate)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
imageFB.release();
|
imageFB.destroyBuffer();
|
||||||
|
|
||||||
if (asset && reloadTime > -1) // Don't unload asset if it's a static image
|
if (asset && reloadTime > -1) // Don't unload asset if it's a static image
|
||||||
g_pRenderer->asyncResourceGatherer->unloadAsset(asset);
|
g_asyncResourceManager->unload(asset);
|
||||||
|
|
||||||
asset = nullptr;
|
asset = nullptr;
|
||||||
pendingResourceID = "";
|
m_pendingResource = false;
|
||||||
resourceID = "";
|
resourceID = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CImage::draw(const SRenderData& data) {
|
bool CImage::draw(const SRenderData& data) {
|
||||||
|
|
||||||
if (resourceID.empty())
|
if (resourceID == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!asset)
|
if (!asset)
|
||||||
asset = g_pRenderer->asyncResourceGatherer->getAssetByID(resourceID);
|
asset = g_asyncResourceManager->getAssetByID(resourceID);
|
||||||
|
|
||||||
if (!asset)
|
if (!asset)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (asset->texture.m_iType == TEXTURE_INVALID) {
|
if (asset->m_iType == TEXTURE_INVALID) {
|
||||||
g_pRenderer->asyncResourceGatherer->unloadAsset(asset);
|
g_asyncResourceManager->unload(asset);
|
||||||
resourceID = "";
|
resourceID = 0;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -154,7 +155,7 @@ bool CImage::draw(const SRenderData& data) {
|
||||||
|
|
||||||
const Vector2D IMAGEPOS = {border, border};
|
const Vector2D IMAGEPOS = {border, border};
|
||||||
const Vector2D BORDERPOS = {0.0, 0.0};
|
const Vector2D BORDERPOS = {0.0, 0.0};
|
||||||
const Vector2D TEXSIZE = asset->texture.m_vSize;
|
const Vector2D TEXSIZE = asset->m_vSize;
|
||||||
const float SCALEX = size / TEXSIZE.x;
|
const float SCALEX = size / TEXSIZE.x;
|
||||||
const float SCALEY = size / TEXSIZE.y;
|
const float SCALEY = size / TEXSIZE.y;
|
||||||
|
|
||||||
|
|
@ -182,7 +183,7 @@ bool CImage::draw(const SRenderData& data) {
|
||||||
g_pRenderer->renderBorder(borderBox, color, border, BORDERROUND, 1.0);
|
g_pRenderer->renderBorder(borderBox, color, border, BORDERROUND, 1.0);
|
||||||
|
|
||||||
texbox.round();
|
texbox.round();
|
||||||
g_pRenderer->renderTexture(texbox, asset->texture, 1.0, ROUND, HYPRUTILS_TRANSFORM_NORMAL);
|
g_pRenderer->renderTexture(texbox, *asset, 1.0, ROUND, HYPRUTILS_TRANSFORM_NORMAL);
|
||||||
g_pRenderer->popFb();
|
g_pRenderer->popFb();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -196,10 +197,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;
|
||||||
|
|
@ -208,28 +209,40 @@ bool CImage::draw(const SRenderData& data) {
|
||||||
return data.opacity < 1.0;
|
return data.opacity < 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CImage::renderUpdate() {
|
void CImage::onAssetUpdate(ResourceID id, ASP<CTexture> newAsset) {
|
||||||
auto newAsset = g_pRenderer->asyncResourceGatherer->getAssetByID(pendingResourceID);
|
m_pendingResource = false;
|
||||||
if (newAsset) {
|
|
||||||
if (newAsset->texture.m_iType == TEXTURE_INVALID) {
|
if (!newAsset)
|
||||||
g_pRenderer->asyncResourceGatherer->unloadAsset(newAsset);
|
Debug::log(ERR, "asset update failed, resourceID: {} not available on update!", id);
|
||||||
} else if (resourceID != pendingResourceID) {
|
else if (newAsset->m_iType == TEXTURE_INVALID) {
|
||||||
g_pRenderer->asyncResourceGatherer->unloadAsset(asset);
|
g_asyncResourceManager->unload(newAsset);
|
||||||
imageFB.release();
|
Debug::log(ERR, "New image asset has an invalid texture!");
|
||||||
|
} else {
|
||||||
|
g_asyncResourceManager->unload(asset);
|
||||||
|
imageFB.destroyBuffer();
|
||||||
|
|
||||||
asset = newAsset;
|
asset = newAsset;
|
||||||
resourceID = pendingResourceID;
|
resourceID = id;
|
||||||
firstRender = true;
|
firstRender = true;
|
||||||
}
|
}
|
||||||
pendingResourceID = "";
|
}
|
||||||
} else if (!pendingResourceID.empty()) {
|
|
||||||
Debug::log(WARN, "Asset {} not available after the asyncResourceGatherer's callback!", pendingResourceID);
|
CBox CImage::getBoundingBoxWl() const {
|
||||||
pendingResourceID = "";
|
if (!imageFB.isAllocated())
|
||||||
} else if (!pendingResourceID.empty()) {
|
return CBox{};
|
||||||
Debug::log(WARN, "Asset {} not available after the asyncResourceGatherer's callback!", pendingResourceID);
|
|
||||||
|
return {
|
||||||
g_pHyprlock->addTimer(std::chrono::milliseconds(100), [REF = m_self](auto, auto) { onAssetCallback(REF); }, nullptr);
|
Vector2D{pos.x, viewport.y - pos.y - imageFB.m_cTex.m_vSize.y},
|
||||||
}
|
imageFB.m_cTex.m_vSize,
|
||||||
|
};
|
||||||
g_pHyprlock->renderOutput(stringPort);
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "IWidget.hpp"
|
#include "IWidget.hpp"
|
||||||
|
#include "../../defines.hpp"
|
||||||
#include "../../helpers/Color.hpp"
|
#include "../../helpers/Color.hpp"
|
||||||
#include "../../helpers/Math.hpp"
|
#include "../../helpers/Math.hpp"
|
||||||
#include "../../config/ConfigDataValues.hpp"
|
#include "../../config/ConfigDataValues.hpp"
|
||||||
#include "../../core/Timer.hpp"
|
#include "../../core/Timer.hpp"
|
||||||
#include "../AsyncResourceGatherer.hpp"
|
|
||||||
#include "Shadowable.hpp"
|
#include "Shadowable.hpp"
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
@ -20,10 +20,15 @@ class CImage : public IWidget {
|
||||||
CImage() = default;
|
CImage() = default;
|
||||||
~CImage();
|
~CImage();
|
||||||
|
|
||||||
void registerSelf(const SP<CImage>& self);
|
void registerSelf(const ASP<CImage>& self);
|
||||||
|
|
||||||
virtual void configure(const std::unordered_map<std::string, std::any>& props, const SP<COutput>& pOutput);
|
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 void onAssetUpdate(ResourceID id, ASP<CTexture> newAsset);
|
||||||
|
|
||||||
|
virtual CBox getBoundingBoxWl() const;
|
||||||
|
virtual void onClick(uint32_t button, bool down, const Vector2D& pos);
|
||||||
|
virtual void onHover(const Vector2D& pos);
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
|
|
@ -32,16 +37,17 @@ class CImage : public IWidget {
|
||||||
void plantTimer();
|
void plantTimer();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
WP<CImage> m_self;
|
AWP<CImage> m_self;
|
||||||
|
|
||||||
CFramebuffer imageFB;
|
CFramebuffer imageFB;
|
||||||
|
|
||||||
int size;
|
int size = 0;
|
||||||
int rounding;
|
int rounding = 0;
|
||||||
double border;
|
double border = 0;
|
||||||
double angle;
|
double angle = 0;
|
||||||
CGradientValueData color;
|
CGradientValueData color;
|
||||||
Vector2D pos;
|
Vector2D pos;
|
||||||
|
Vector2D configPos;
|
||||||
|
|
||||||
std::string halign, valign, path;
|
std::string halign, valign, path;
|
||||||
|
|
||||||
|
|
@ -49,15 +55,19 @@ 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;
|
size_t m_imageRevision = 0;
|
||||||
CAsyncResourceGatherer::SPreloadRequest request;
|
|
||||||
|
ASP<CTimer> imageTimer;
|
||||||
|
|
||||||
Vector2D viewport;
|
Vector2D viewport;
|
||||||
std::string stringPort;
|
std::string stringPort;
|
||||||
|
|
||||||
std::string resourceID;
|
ResourceID resourceID = 0;
|
||||||
std::string pendingResourceID; // if reloading image
|
bool m_pendingResource = false;
|
||||||
SPreloadedAsset* asset = nullptr;
|
|
||||||
|
ASP<CTexture> asset = nullptr;
|
||||||
CShadowable shadow;
|
CShadowable shadow;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
#include "Label.hpp"
|
#include "Label.hpp"
|
||||||
#include "../Renderer.hpp"
|
#include "../Renderer.hpp"
|
||||||
|
#include "../AsyncResourceManager.hpp"
|
||||||
#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 "src/defines.hpp"
|
||||||
#include <hyprlang.hpp>
|
#include <hyprlang.hpp>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
|
@ -11,11 +14,11 @@ CLabel::~CLabel() {
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLabel::registerSelf(const SP<CLabel>& self) {
|
void CLabel::registerSelf(const ASP<CLabel>& self) {
|
||||||
m_self = self;
|
m_self = self;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void onTimer(WP<CLabel> ref) {
|
static void onTimer(AWP<CLabel> ref) {
|
||||||
if (auto PLABEL = ref.lock(); PLABEL) {
|
if (auto PLABEL = ref.lock(); PLABEL) {
|
||||||
// update label
|
// update label
|
||||||
PLABEL->onTimerUpdate();
|
PLABEL->onTimerUpdate();
|
||||||
|
|
@ -24,16 +27,12 @@ static void onTimer(WP<CLabel> ref) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void onAssetCallback(WP<CLabel> ref) {
|
|
||||||
if (auto PLABEL = ref.lock(); PLABEL)
|
|
||||||
PLABEL->renderUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
|
|
||||||
void CLabel::onTimerUpdate() {
|
void CLabel::onTimerUpdate() {
|
||||||
|
if (m_pendingResource) {
|
||||||
|
Debug::log(WARN, "Trying to update label, but a resource is still pending! Skipping update.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::string oldFormatted = label.formatted;
|
std::string oldFormatted = label.formatted;
|
||||||
|
|
||||||
label = formatString(labelPreFormat);
|
label = formatString(labelPreFormat);
|
||||||
|
|
@ -41,19 +40,17 @@ void CLabel::onTimerUpdate() {
|
||||||
if (label.formatted == oldFormatted && !label.alwaysUpdate)
|
if (label.formatted == oldFormatted && !label.alwaysUpdate)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!pendingResourceID.empty()) {
|
|
||||||
Debug::log(WARN, "Trying to update label, but resource {} is still pending! Skipping update.", pendingResourceID);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// request new
|
// request new
|
||||||
request.id = getUniqueResourceId();
|
request.text = label.formatted;
|
||||||
pendingResourceID = request.id;
|
m_pendingResource = true;
|
||||||
request.asset = label.formatted;
|
|
||||||
|
|
||||||
request.callback = [REF = m_self]() { onAssetCallback(REF); };
|
AWP<IWidget> widget(m_self);
|
||||||
|
if (label.cmd) {
|
||||||
g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
|
// Don't increment by one to avoid clashes with multiple widget using the same label command.
|
||||||
|
m_dynamicRevision += label.updateEveryMs;
|
||||||
|
g_asyncResourceManager->requestTextCmd(request, m_dynamicRevision, widget.lock());
|
||||||
|
} else
|
||||||
|
g_asyncResourceManager->requestText(request, widget.lock());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLabel::plantTimer() {
|
void CLabel::plantTimer() {
|
||||||
|
|
@ -70,7 +67,7 @@ void CLabel::configure(const std::unordered_map<std::string, std::any>& props, c
|
||||||
outputStringPort = pOutput->stringPort;
|
outputStringPort = pOutput->stringPort;
|
||||||
viewport = pOutput->getViewport();
|
viewport = pOutput->getViewport();
|
||||||
|
|
||||||
shadow.configure(m_self.lock(), props, viewport);
|
shadow.configure(m_self, props, viewport);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
configPos = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport);
|
configPos = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport);
|
||||||
|
|
@ -79,6 +76,7 @@ void CLabel::configure(const std::unordered_map<std::string, std::any>& props, c
|
||||||
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"));
|
||||||
|
|
@ -87,17 +85,13 @@ void CLabel::configure(const std::unordered_map<std::string, std::any>& props, c
|
||||||
|
|
||||||
label = formatString(labelPreFormat);
|
label = formatString(labelPreFormat);
|
||||||
|
|
||||||
request.id = getUniqueResourceId();
|
request.text = label.formatted;
|
||||||
resourceID = request.id;
|
request.font = fontFamily;
|
||||||
request.asset = label.formatted;
|
request.fontSize = fontSize;
|
||||||
request.type = CAsyncResourceGatherer::eTargetType::TARGET_TEXT;
|
request.color = labelColor.asRGB();
|
||||||
request.props["font_family"] = fontFamily;
|
|
||||||
request.props["color"] = labelColor;
|
|
||||||
request.props["font_size"] = fontSize;
|
|
||||||
request.props["cmd"] = label.cmd;
|
|
||||||
|
|
||||||
if (!textAlign.empty())
|
if (!textAlign.empty())
|
||||||
request.props["text_align"] = textAlign;
|
request.align = parseTextAlignment(textAlign);
|
||||||
|
|
||||||
} catch (const std::bad_any_cast& e) {
|
} catch (const std::bad_any_cast& e) {
|
||||||
RASSERT(false, "Failed to construct CLabel: {}", e.what()); //
|
RASSERT(false, "Failed to construct CLabel: {}", e.what()); //
|
||||||
|
|
@ -107,7 +101,10 @@ void CLabel::configure(const std::unordered_map<std::string, std::any>& props, c
|
||||||
|
|
||||||
pos = configPos; // Label size not known yet
|
pos = configPos; // Label size not known yet
|
||||||
|
|
||||||
g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
|
if (label.cmd) {
|
||||||
|
resourceID = g_asyncResourceManager->requestTextCmd(request, m_dynamicRevision, nullptr);
|
||||||
|
} else
|
||||||
|
resourceID = g_asyncResourceManager->requestText(request, nullptr);
|
||||||
|
|
||||||
plantTimer();
|
plantTimer();
|
||||||
}
|
}
|
||||||
|
|
@ -122,16 +119,16 @@ void CLabel::reset() {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (asset)
|
if (asset)
|
||||||
g_pRenderer->asyncResourceGatherer->unloadAsset(asset);
|
g_asyncResourceManager->unload(asset);
|
||||||
|
|
||||||
asset = nullptr;
|
asset = nullptr;
|
||||||
pendingResourceID.clear();
|
m_pendingResource = false;
|
||||||
resourceID.clear();
|
resourceID = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CLabel::draw(const SRenderData& data) {
|
bool CLabel::draw(const SRenderData& data) {
|
||||||
if (!asset) {
|
if (!asset) {
|
||||||
asset = g_pRenderer->asyncResourceGatherer->getAssetByID(resourceID);
|
asset = g_asyncResourceManager->getAssetByID(resourceID);
|
||||||
|
|
||||||
if (!asset)
|
if (!asset)
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -145,30 +142,49 @@ bool CLabel::draw(const SRenderData& data) {
|
||||||
shadow.draw(data);
|
shadow.draw(data);
|
||||||
|
|
||||||
// calc pos
|
// calc pos
|
||||||
pos = posFromHVAlign(viewport, asset->texture.m_vSize, configPos, halign, valign, angle);
|
pos = posFromHVAlign(viewport, asset->m_vSize, configPos, halign, valign, angle);
|
||||||
|
|
||||||
CBox box = {pos.x, pos.y, asset->texture.m_vSize.x, asset->texture.m_vSize.y};
|
CBox box = {pos.x, pos.y, asset->m_vSize.x, asset->m_vSize.y};
|
||||||
box.rot = angle;
|
box.rot = angle;
|
||||||
g_pRenderer->renderTexture(box, asset->texture, data.opacity);
|
g_pRenderer->renderTexture(box, *asset, data.opacity);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLabel::renderUpdate() {
|
void CLabel::onAssetUpdate(ResourceID id, ASP<CTexture> newAsset) {
|
||||||
auto newAsset = g_pRenderer->asyncResourceGatherer->getAssetByID(pendingResourceID);
|
Debug::log(TRACE, "Label update for resourceID {}", id);
|
||||||
if (newAsset) {
|
m_pendingResource = false;
|
||||||
// new asset is ready :D
|
|
||||||
g_pRenderer->asyncResourceGatherer->unloadAsset(asset);
|
if (!newAsset)
|
||||||
asset = newAsset;
|
Debug::log(ERR, "asset update failed, resourceID: {} not available on update!", id);
|
||||||
resourceID = pendingResourceID;
|
else if (newAsset->m_iType == TEXTURE_INVALID) {
|
||||||
pendingResourceID = "";
|
g_asyncResourceManager->unload(newAsset);
|
||||||
updateShadow = true;
|
Debug::log(ERR, "New image asset has an invalid texture!");
|
||||||
} else {
|
} else {
|
||||||
Debug::log(WARN, "Asset {} not available after the asyncResourceGatherer's callback!", pendingResourceID);
|
// new asset is ready :D
|
||||||
|
g_asyncResourceManager->unload(asset);
|
||||||
g_pHyprlock->addTimer(std::chrono::milliseconds(100), [REF = m_self](auto, auto) { onAssetCallback(REF); }, nullptr);
|
asset = newAsset;
|
||||||
return;
|
resourceID = id;
|
||||||
|
updateShadow = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
g_pHyprlock->renderOutput(outputStringPort);
|
|
||||||
|
CBox CLabel::getBoundingBoxWl() const {
|
||||||
|
if (!asset)
|
||||||
|
return CBox{};
|
||||||
|
|
||||||
|
return {
|
||||||
|
Vector2D{pos.x, viewport.y - pos.y - asset->m_vSize.y},
|
||||||
|
asset->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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "../../defines.hpp"
|
||||||
#include "IWidget.hpp"
|
#include "IWidget.hpp"
|
||||||
#include "Shadowable.hpp"
|
#include "Shadowable.hpp"
|
||||||
#include "../../helpers/Math.hpp"
|
|
||||||
#include "../../core/Timer.hpp"
|
#include "../../core/Timer.hpp"
|
||||||
#include "../AsyncResourceGatherer.hpp"
|
#include <hyprgraphics/resource/resources/AsyncResource.hpp>
|
||||||
|
#include <hyprgraphics/resource/resources/TextResource.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <any>
|
#include <any>
|
||||||
|
|
@ -17,10 +18,15 @@ class CLabel : public IWidget {
|
||||||
CLabel() = default;
|
CLabel() = default;
|
||||||
~CLabel();
|
~CLabel();
|
||||||
|
|
||||||
void registerSelf(const SP<CLabel>& self);
|
void registerSelf(const ASP<CLabel>& self);
|
||||||
|
|
||||||
virtual void configure(const std::unordered_map<std::string, std::any>& prop, const SP<COutput>& pOutput);
|
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 onAssetUpdate(ResourceID id, ASP<CTexture> newAsset);
|
||||||
|
|
||||||
|
virtual CBox getBoundingBoxWl() const;
|
||||||
|
virtual void onClick(uint32_t button, bool down, const Vector2D& pos);
|
||||||
|
virtual void onHover(const Vector2D& pos);
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
|
|
@ -29,27 +35,31 @@ class CLabel : public IWidget {
|
||||||
void plantTimer();
|
void plantTimer();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
WP<CLabel> m_self;
|
AWP<CLabel> m_self;
|
||||||
|
|
||||||
std::string getUniqueResourceId();
|
|
||||||
|
|
||||||
std::string labelPreFormat;
|
std::string labelPreFormat;
|
||||||
IWidget::SFormatResult label;
|
IWidget::SFormatResult label;
|
||||||
|
|
||||||
|
std::string halign, valign;
|
||||||
|
std::string onclickCommand;
|
||||||
|
|
||||||
Vector2D viewport;
|
Vector2D viewport;
|
||||||
Vector2D pos;
|
Vector2D pos;
|
||||||
Vector2D configPos;
|
Vector2D configPos;
|
||||||
double angle;
|
double angle;
|
||||||
std::string resourceID;
|
|
||||||
std::string pendingResourceID; // if dynamic label
|
ResourceID resourceID = 0;
|
||||||
std::string halign, valign;
|
bool m_pendingResource = false;
|
||||||
SPreloadedAsset* asset = nullptr;
|
|
||||||
|
size_t m_dynamicRevision = 0;
|
||||||
|
|
||||||
|
ASP<CTexture> asset = nullptr;
|
||||||
|
|
||||||
std::string outputStringPort;
|
std::string outputStringPort;
|
||||||
|
|
||||||
CAsyncResourceGatherer::SPreloadRequest request;
|
Hyprgraphics::CTextResource::STextResourceData request;
|
||||||
|
|
||||||
std::shared_ptr<CTimer> labelTimer = nullptr;
|
ASP<CTimer> labelTimer = nullptr;
|
||||||
|
|
||||||
CShadowable shadow;
|
CShadowable shadow;
|
||||||
bool updateShadow = true;
|
bool updateShadow = true;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "PasswordInputField.hpp"
|
#include "PasswordInputField.hpp"
|
||||||
|
#include "../AsyncResourceManager.hpp"
|
||||||
#include "../Renderer.hpp"
|
#include "../Renderer.hpp"
|
||||||
#include "../../core/hyprlock.hpp"
|
#include "../../core/hyprlock.hpp"
|
||||||
#include "../../auth/Auth.hpp"
|
#include "../../auth/Auth.hpp"
|
||||||
|
|
@ -8,6 +9,7 @@
|
||||||
#include "../../core/AnimationManager.hpp"
|
#include "../../core/AnimationManager.hpp"
|
||||||
#include "../../helpers/Color.hpp"
|
#include "../../helpers/Color.hpp"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <hyprgraphics/resource/resources/TextResource.hpp>
|
||||||
#include <hyprutils/math/Vector2D.hpp>
|
#include <hyprutils/math/Vector2D.hpp>
|
||||||
#include <hyprutils/string/String.hpp>
|
#include <hyprutils/string/String.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
@ -19,7 +21,7 @@ CPasswordInputField::~CPasswordInputField() {
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPasswordInputField::registerSelf(const SP<CPasswordInputField>& self) {
|
void CPasswordInputField::registerSelf(const ASP<CPasswordInputField>& self) {
|
||||||
m_self = self;
|
m_self = self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -29,7 +31,7 @@ void CPasswordInputField::configure(const std::unordered_map<std::string, std::a
|
||||||
outputStringPort = pOutput->stringPort;
|
outputStringPort = pOutput->stringPort;
|
||||||
viewport = pOutput->getViewport();
|
viewport = pOutput->getViewport();
|
||||||
|
|
||||||
shadow.configure(m_self.lock(), props, viewport);
|
shadow.configure(m_self, props, viewport);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
pos = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport);
|
pos = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport);
|
||||||
|
|
@ -86,16 +88,12 @@ void CPasswordInputField::configure(const std::unordered_map<std::string, std::a
|
||||||
pos = posFromHVAlign(viewport, size->goal(), configPos, halign, valign);
|
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);
|
Hyprgraphics::CTextResource::STextResourceData request;
|
||||||
CAsyncResourceGatherer::SPreloadRequest request;
|
request.text = dots.textFormat;
|
||||||
request.id = dots.textResourceID;
|
request.font = fontFamily;
|
||||||
request.asset = dots.textFormat;
|
request.color = colorConfig.font.asRGB();
|
||||||
request.type = CAsyncResourceGatherer::eTargetType::TARGET_TEXT;
|
request.fontSize = (int)(std::nearbyint(configSize.y * dots.size * 0.5f) * 2.f);
|
||||||
request.props["font_family"] = fontFamily;
|
dots.textResourceID = g_asyncResourceManager->requestText(request, nullptr);
|
||||||
request.props["color"] = colorConfig.font;
|
|
||||||
request.props["font_size"] = (int)(std::nearbyint(configSize.y * dots.size * 0.5f) * 2.f);
|
|
||||||
|
|
||||||
g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// request the inital placeholder asset
|
// request the inital placeholder asset
|
||||||
|
|
@ -103,7 +101,7 @@ void CPasswordInputField::configure(const std::unordered_map<std::string, std::a
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPasswordInputField::reset() {
|
void CPasswordInputField::reset() {
|
||||||
if (fade.fadeOutTimer.get()) {
|
if (fade.fadeOutTimer) {
|
||||||
fade.fadeOutTimer->cancel();
|
fade.fadeOutTimer->cancel();
|
||||||
fade.fadeOutTimer.reset();
|
fade.fadeOutTimer.reset();
|
||||||
}
|
}
|
||||||
|
|
@ -112,14 +110,14 @@ void CPasswordInputField::reset() {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (placeholder.asset)
|
if (placeholder.asset)
|
||||||
g_pRenderer->asyncResourceGatherer->unloadAsset(placeholder.asset);
|
g_asyncResourceManager->unload(placeholder.asset);
|
||||||
|
|
||||||
placeholder.asset = nullptr;
|
placeholder.asset = nullptr;
|
||||||
placeholder.resourceID.clear();
|
placeholder.resourceID = 0;
|
||||||
placeholder.currentText.clear();
|
placeholder.currentText.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fadeOutCallback(WP<CPasswordInputField> ref) {
|
static void fadeOutCallback(AWP<CPasswordInputField> ref) {
|
||||||
if (const auto PP = ref.lock(); PP)
|
if (const auto PP = ref.lock(); PP)
|
||||||
PP->onFadeOutTimer();
|
PP->onFadeOutTimer();
|
||||||
}
|
}
|
||||||
|
|
@ -151,7 +149,7 @@ void CPasswordInputField::updateFade() {
|
||||||
if (fade.allowFadeOut || fadeTimeoutMs == 0) {
|
if (fade.allowFadeOut || fadeTimeoutMs == 0) {
|
||||||
*fade.a = 0.0;
|
*fade.a = 0.0;
|
||||||
fade.allowFadeOut = false;
|
fade.allowFadeOut = false;
|
||||||
} else if (!fade.fadeOutTimer.get())
|
} else if (!fade.fadeOutTimer)
|
||||||
fade.fadeOutTimer = g_pHyprlock->addTimer(std::chrono::milliseconds(fadeTimeoutMs), [REF = m_self](auto, auto) { fadeOutCallback(REF); }, nullptr);
|
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)
|
||||||
|
|
@ -242,12 +240,12 @@ bool CPasswordInputField::draw(const SRenderData& data) {
|
||||||
|
|
||||||
if (!dots.textFormat.empty()) {
|
if (!dots.textFormat.empty()) {
|
||||||
if (!dots.textAsset)
|
if (!dots.textAsset)
|
||||||
dots.textAsset = g_pRenderer->asyncResourceGatherer->getAssetByID(dots.textResourceID);
|
dots.textAsset = g_asyncResourceManager->getAssetByID(dots.textResourceID);
|
||||||
|
|
||||||
if (!dots.textAsset)
|
if (!dots.textAsset)
|
||||||
forceReload = true;
|
forceReload = true;
|
||||||
else {
|
else {
|
||||||
passSize = dots.textAsset->texture.m_vSize;
|
passSize = dots.textAsset->m_vSize;
|
||||||
passSpacing = std::floor(passSize.x * dots.spacing);
|
passSpacing = std::floor(passSize.x * dots.spacing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -293,7 +291,7 @@ bool CPasswordInputField::draw(const SRenderData& data) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_pRenderer->renderTexture(box, dots.textAsset->texture, fontCol.a, dots.rounding);
|
g_pRenderer->renderTexture(box, *dots.textAsset, fontCol.a, dots.rounding);
|
||||||
} else
|
} else
|
||||||
g_pRenderer->renderRect(box, fontCol, dots.rounding);
|
g_pRenderer->renderRect(box, fontCol, dots.rounding);
|
||||||
|
|
||||||
|
|
@ -301,22 +299,22 @@ bool CPasswordInputField::draw(const SRenderData& data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (passwordLength == 0 && !checkWaiting && !placeholder.resourceID.empty()) {
|
if (passwordLength == 0 && !checkWaiting && placeholder.resourceID > 0) {
|
||||||
SPreloadedAsset* currAsset = nullptr;
|
ASP<CTexture> currAsset = nullptr;
|
||||||
|
|
||||||
if (!placeholder.asset)
|
if (!placeholder.asset)
|
||||||
placeholder.asset = g_pRenderer->asyncResourceGatherer->getAssetByID(placeholder.resourceID);
|
placeholder.asset = g_asyncResourceManager->getAssetByID(placeholder.resourceID);
|
||||||
|
|
||||||
currAsset = placeholder.asset;
|
currAsset = placeholder.asset;
|
||||||
|
|
||||||
if (currAsset) {
|
if (currAsset) {
|
||||||
const Vector2D ASSETPOS = inputFieldBox.pos() + inputFieldBox.size() / 2.0 - currAsset->texture.m_vSize / 2.0;
|
const Vector2D ASSETPOS = inputFieldBox.pos() + inputFieldBox.size() / 2.0 - currAsset->m_vSize / 2.0;
|
||||||
const CBox ASSETBOX{ASSETPOS, currAsset->texture.m_vSize};
|
const CBox ASSETBOX{ASSETPOS, currAsset->m_vSize};
|
||||||
|
|
||||||
// Cut the texture to the width of the input field
|
// Cut the texture to the width of the input field
|
||||||
glEnable(GL_SCISSOR_TEST);
|
glEnable(GL_SCISSOR_TEST);
|
||||||
glScissor(inputFieldBox.x, inputFieldBox.y, inputFieldBox.w, inputFieldBox.h);
|
glScissor(inputFieldBox.x, inputFieldBox.y, inputFieldBox.w, inputFieldBox.h);
|
||||||
g_pRenderer->renderTexture(ASSETBOX, currAsset->texture, data.opacity * fade.a->value(), 0);
|
g_pRenderer->renderTexture(ASSETBOX, *currAsset, data.opacity * fade.a->value(), 0);
|
||||||
glScissor(0, 0, viewport.x, viewport.y);
|
glScissor(0, 0, viewport.x, viewport.y);
|
||||||
glDisable(GL_SCISSOR_TEST);
|
glDisable(GL_SCISSOR_TEST);
|
||||||
} else
|
} else
|
||||||
|
|
@ -329,10 +327,9 @@ bool CPasswordInputField::draw(const SRenderData& data) {
|
||||||
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) {
|
||||||
std::erase(placeholder.registeredResourceIDs, placeholder.resourceID);
|
g_asyncResourceManager->unload(placeholder.asset);
|
||||||
g_pRenderer->asyncResourceGatherer->unloadAsset(placeholder.asset);
|
|
||||||
placeholder.asset = nullptr;
|
placeholder.asset = nullptr;
|
||||||
placeholder.resourceID = "";
|
placeholder.resourceID = 0;
|
||||||
redrawShadow = true;
|
redrawShadow = true;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
@ -351,43 +348,30 @@ void CPasswordInputField::updatePlaceholder() {
|
||||||
if (!ALLOWCOLORSWAP && newText == placeholder.currentText)
|
if (!ALLOWCOLORSWAP && newText == placeholder.currentText)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const auto NEWRESOURCEID =
|
Debug::log(LOG, "Updating placeholder text: {}", newText);
|
||||||
std::format("placeholder:{}{}{}{}{}{}", placeholder.currentText, (uintptr_t)this, colorState.font.r, colorState.font.g, colorState.font.b, colorState.font.a);
|
|
||||||
|
|
||||||
if (placeholder.resourceID == NEWRESOURCEID)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Debug::log(TRACE, "Updating placeholder text: {}", newText);
|
|
||||||
placeholder.currentText = newText;
|
placeholder.currentText = newText;
|
||||||
placeholder.asset = nullptr;
|
placeholder.asset = nullptr;
|
||||||
placeholder.resourceID = NEWRESOURCEID;
|
|
||||||
|
|
||||||
if (std::ranges::find(placeholder.registeredResourceIDs, placeholder.resourceID) != placeholder.registeredResourceIDs.end())
|
|
||||||
return;
|
|
||||||
|
|
||||||
Debug::log(TRACE, "Requesting new placeholder asset: {}", placeholder.resourceID);
|
|
||||||
placeholder.registeredResourceIDs.push_back(placeholder.resourceID);
|
|
||||||
|
|
||||||
// query
|
// query
|
||||||
CAsyncResourceGatherer::SPreloadRequest request;
|
Hyprgraphics::CTextResource::STextResourceData request;
|
||||||
request.id = placeholder.resourceID;
|
request.text = placeholder.currentText;
|
||||||
request.asset = placeholder.currentText;
|
request.font = fontFamily;
|
||||||
request.type = CAsyncResourceGatherer::eTargetType::TARGET_TEXT;
|
request.color = colorState.font.asRGB();
|
||||||
request.props["font_family"] = fontFamily;
|
request.fontSize = (int)size->value().y / 4;
|
||||||
request.props["color"] = colorState.font;
|
|
||||||
request.props["font_size"] = (int)size->value().y / 4;
|
AWP<IWidget> widget(m_self);
|
||||||
request.callback = [REF = m_self] {
|
placeholder.resourceID = g_asyncResourceManager->requestText(request, widget);
|
||||||
if (const auto SELF = REF.lock(); SELF)
|
}
|
||||||
g_pHyprlock->renderOutput(SELF->outputStringPort);
|
|
||||||
};
|
void CPasswordInputField::onAssetUpdate(ResourceID id, ASP<CTexture> newAsset) {
|
||||||
g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPasswordInputField::updateWidth() {
|
void CPasswordInputField::updateWidth() {
|
||||||
double targetSizeX = configSize.x;
|
double targetSizeX = configSize.x;
|
||||||
|
|
||||||
if (passwordLength == 0 && placeholder.asset)
|
if (passwordLength == 0 && placeholder.asset)
|
||||||
targetSizeX = placeholder.asset->texture.m_vSize.x + size->goal().y;
|
targetSizeX = placeholder.asset->m_vSize.x + size->goal().y;
|
||||||
|
|
||||||
targetSizeX = std::max(targetSizeX, configSize.x);
|
targetSizeX = std::max(targetSizeX, configSize.x);
|
||||||
|
|
||||||
|
|
@ -473,3 +457,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);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "IWidget.hpp"
|
#include "IWidget.hpp"
|
||||||
|
#include "../../defines.hpp"
|
||||||
#include "../../helpers/Color.hpp"
|
#include "../../helpers/Color.hpp"
|
||||||
#include "../../helpers/Math.hpp"
|
#include "../../helpers/Math.hpp"
|
||||||
#include "../../core/Timer.hpp"
|
#include "../../core/Timer.hpp"
|
||||||
|
|
@ -19,16 +20,20 @@ class CPasswordInputField : public IWidget {
|
||||||
CPasswordInputField() = default;
|
CPasswordInputField() = default;
|
||||||
virtual ~CPasswordInputField();
|
virtual ~CPasswordInputField();
|
||||||
|
|
||||||
void registerSelf(const SP<CPasswordInputField>& self);
|
void registerSelf(const ASP<CPasswordInputField>& self);
|
||||||
|
|
||||||
virtual void configure(const std::unordered_map<std::string, std::any>& prop, const SP<COutput>& pOutput);
|
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 onAssetUpdate(ResourceID id, ASP<CTexture> newAsset);
|
||||||
|
|
||||||
|
virtual void onHover(const Vector2D& pos);
|
||||||
|
virtual CBox getBoundingBoxWl() const;
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
void onFadeOutTimer();
|
void onFadeOutTimer();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
WP<CPasswordInputField> m_self;
|
AWP<CPasswordInputField> m_self;
|
||||||
|
|
||||||
void updateDots();
|
void updateDots();
|
||||||
void updateFade();
|
void updateFade();
|
||||||
|
|
@ -62,26 +67,24 @@ class CPasswordInputField : public IWidget {
|
||||||
float size = 0;
|
float size = 0;
|
||||||
float spacing = 0;
|
float spacing = 0;
|
||||||
int rounding = 0;
|
int rounding = 0;
|
||||||
|
size_t textResourceID = 0;
|
||||||
std::string textFormat = "";
|
std::string textFormat = "";
|
||||||
std::string textResourceID;
|
ASP<CTexture> textAsset = nullptr;
|
||||||
SPreloadedAsset* textAsset = nullptr;
|
|
||||||
} dots;
|
} dots;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
PHLANIMVAR<float> a;
|
PHLANIMVAR<float> a;
|
||||||
bool appearing = true;
|
bool appearing = true;
|
||||||
std::shared_ptr<CTimer> fadeOutTimer = nullptr;
|
ASP<CTimer> fadeOutTimer = nullptr;
|
||||||
bool allowFadeOut = false;
|
bool allowFadeOut = false;
|
||||||
} fade;
|
} fade;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
std::string resourceID = "";
|
size_t resourceID = 0;
|
||||||
SPreloadedAsset* asset = nullptr;
|
ASP<CTexture> asset = nullptr;
|
||||||
|
|
||||||
std::string currentText = "";
|
std::string currentText = "";
|
||||||
size_t failedAttempts = 0;
|
size_t failedAttempts = 0;
|
||||||
|
|
||||||
std::vector<std::string> registeredResourceIDs;
|
|
||||||
} placeholder;
|
} placeholder;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
#include "../Renderer.hpp"
|
#include "../Renderer.hpp"
|
||||||
#include <hyprlang.hpp>
|
#include <hyprlang.hpp>
|
||||||
|
|
||||||
void CShadowable::configure(WP<IWidget> widget_, const std::unordered_map<std::string, std::any>& props, const Vector2D& viewport_) {
|
void CShadowable::configure(AWP<IWidget> widget_, const std::unordered_map<std::string, std::any>& props, const Vector2D& viewport_) {
|
||||||
m_widget = widget_;
|
m_widget = widget_;
|
||||||
viewport = viewport_;
|
viewport = viewport_;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,14 +13,14 @@ class CShadowable {
|
||||||
public:
|
public:
|
||||||
virtual ~CShadowable() = default;
|
virtual ~CShadowable() = default;
|
||||||
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 */);
|
void configure(AWP<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:
|
||||||
WP<IWidget> m_widget;
|
AWP<IWidget> m_widget;
|
||||||
int size = 10;
|
int size = 10;
|
||||||
int passes = 4;
|
int passes = 4;
|
||||||
float boostA = 1.0;
|
float boostA = 1.0;
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,20 @@
|
||||||
#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>
|
||||||
|
|
||||||
void CShape::registerSelf(const SP<CShape>& self) {
|
void CShape::registerSelf(const ASP<CShape>& self) {
|
||||||
m_self = self;
|
m_self = self;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CShape::configure(const std::unordered_map<std::string, std::any>& props, const SP<COutput>& pOutput) {
|
void CShape::configure(const std::unordered_map<std::string, std::any>& props, const SP<COutput>& pOutput) {
|
||||||
viewport = pOutput->getViewport();
|
viewport = pOutput->getViewport();
|
||||||
|
|
||||||
shadow.configure(m_self.lock(), props, viewport);
|
shadow.configure(m_self, props, viewport);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
size = CLayoutValueData::fromAnyPv(props.at("size"))->getAbsolute(viewport);
|
size = CLayoutValueData::fromAnyPv(props.at("size"))->getAbsolute(viewport);
|
||||||
|
|
@ -24,6 +27,7 @@ void CShape::configure(const std::unordered_map<std::string, std::any>& props, c
|
||||||
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) {
|
||||||
|
|
@ -100,3 +104,24 @@ bool CShape::draw(const SRenderData& data) {
|
||||||
|
|
||||||
return data.opacity < 1.0;
|
return data.opacity < 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CShape::onAssetUpdate(ResourceID id, ASP<CTexture> newAsset) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,13 +14,18 @@ class CShape : public IWidget {
|
||||||
CShape() = default;
|
CShape() = default;
|
||||||
virtual ~CShape() = default;
|
virtual ~CShape() = default;
|
||||||
|
|
||||||
void registerSelf(const SP<CShape>& self);
|
void registerSelf(const ASP<CShape>& self);
|
||||||
|
|
||||||
virtual void configure(const std::unordered_map<std::string, std::any>& prop, const SP<COutput>& pOutput);
|
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 onAssetUpdate(ResourceID id, ASP<CTexture> newAsset);
|
||||||
|
|
||||||
|
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;
|
AWP<CShape> m_self;
|
||||||
|
|
||||||
CFramebuffer shapeFB;
|
CFramebuffer shapeFB;
|
||||||
|
|
||||||
|
|
@ -38,6 +43,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;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue