pull changes from upstream

This commit is contained in:
Felix Salcher 2025-06-29 17:59:21 +02:00
commit d56543e513
No known key found for this signature in database
36 changed files with 461 additions and 383 deletions

View file

@ -7,7 +7,35 @@ jobs:
steps:
- 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)
# - uses: cachix/cachix-action@v12

View file

@ -86,7 +86,8 @@ pkg_check_modules(
pangocairo
libdrm
gbm
hyprutils>=0.5.0
pam
hyprutils>=0.8.0
sdbus-c++>=2.0.0
hyprgraphics)

30
flake.lock generated
View file

@ -13,11 +13,11 @@
]
},
"locked": {
"lastModified": 1743953322,
"narHash": "sha256-prQ5JKopXtzCMX2eT3dXbaVvGmzjMRE2bXStQDdazpM=",
"lastModified": 1750621377,
"narHash": "sha256-8u6b5oAdX0rCuoR8wFenajBRmI+mzbpNig6hSCuWUzE=",
"owner": "hyprwm",
"repo": "hyprgraphics",
"rev": "9d7f2687c84c729afbc3b13f7937655570f2978d",
"rev": "b3d628d01693fb9bb0a6690cd4e7b80abda04310",
"type": "github"
},
"original": {
@ -39,11 +39,11 @@
]
},
"locked": {
"lastModified": 1744468525,
"narHash": "sha256-9HySx+EtsbbKlZDlY+naqqOV679VdxP6x6fP3wxDXJk=",
"lastModified": 1750371198,
"narHash": "sha256-/iuJ1paQOBoSLqHflRNNGyroqfF/yvPNurxzcCT0cAE=",
"owner": "hyprwm",
"repo": "hyprlang",
"rev": "f1000c54d266e6e4e9d646df0774fac5b8a652df",
"rev": "cee01452bca58d6cadb3224e21e370de8bc20f0b",
"type": "github"
},
"original": {
@ -62,11 +62,11 @@
]
},
"locked": {
"lastModified": 1743950287,
"narHash": "sha256-/6IAEWyb8gC/NKZElxiHChkouiUOrVYNq9YqG0Pzm4Y=",
"lastModified": 1751061882,
"narHash": "sha256-g9n8Vrbx+2JYM170P9BbvGHN39Wlkr4U+V2WLHQsXL8=",
"owner": "hyprwm",
"repo": "hyprutils",
"rev": "f2dc70e448b994cef627a157ee340135bd68fbc6",
"rev": "4737241eaf8a1e51671a2a088518071f9a265cf4",
"type": "github"
},
"original": {
@ -85,11 +85,11 @@
]
},
"locked": {
"lastModified": 1739870480,
"narHash": "sha256-SiDN5BGxa/1hAsqhgJsS03C3t2QrLgBT8u+ENJ0Qzwc=",
"lastModified": 1750371869,
"narHash": "sha256-lGk4gLjgZQ/rndUkzmPYcgbHr8gKU5u71vyrjnwfpB4=",
"owner": "hyprwm",
"repo": "hyprwayland-scanner",
"rev": "206367a08dc5ac4ba7ad31bdca391d098082e64b",
"rev": "aa38edd6e3e277ae6a97ea83a69261a5c3aab9fd",
"type": "github"
},
"original": {
@ -100,11 +100,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1744463964,
"narHash": "sha256-LWqduOgLHCFxiTNYi3Uj5Lgz0SR+Xhw3kr/3Xd0GPTM=",
"lastModified": 1751011381,
"narHash": "sha256-krGXKxvkBhnrSC/kGBmg5MyupUUT5R6IBCLEzx9jhMM=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "2631b0b7abcea6e640ce31cd78ea58910d31e650",
"rev": "30e2e2857ba47844aa71991daa6ed1fc678bcbb7",
"type": "github"
},
"original": {

View file

@ -21,7 +21,7 @@ in {
inputs.self.overlays.sdbuscpp
(final: prev: {
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");
inherit (final) hyprlang;
shortRev = self.sourceInfo.shortRev or "dirty";

View file

@ -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();
}
@ -84,7 +84,7 @@ void CAuth::enqueueUnlock() {
g_pHyprlock->addTimer(std::chrono::milliseconds(0), unlockCallback, nullptr);
}
static void passwordFailCallback(std::shared_ptr<CTimer> self, void* data) {
static void passwordFailCallback(ASP<CTimer> self, void* data) {
g_pAuth->m_bDisplayFailText = true;
g_pHyprlock->enqueueForceUpdateTimers();
@ -92,7 +92,7 @@ static void passwordFailCallback(std::shared_ptr<CTimer> self, void* data) {
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) {
g_pAuth->m_bDisplayFailText = false;
g_pHyprlock->renderAllOutputs();

View file

@ -61,7 +61,7 @@ class CAuth {
} m_sCurrentFail;
std::vector<SP<IAuthImplementation>> m_vImpls;
std::shared_ptr<CTimer> m_resetDisplayFailTimer;
ASP<CTimer> m_resetDisplayFailTimer;
};
inline UP<CAuth> g_pAuth;

View file

@ -158,7 +158,7 @@ void CFingerprint::handleVerifyStatus(const std::string& result, bool done) {
} else {
done = false;
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";
}
break;

View file

@ -214,7 +214,6 @@ void CConfigManager::init() {
m_config.addConfigValue("general:text_trim", Hyprlang::INT{1});
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:immediate_render", Hyprlang::INT{0});
m_config.addConfigValue("general:fractional_scaling", Hyprlang::INT{2});

View file

@ -5,6 +5,7 @@
#include <chrono>
#include <sys/mman.h>
#include <unistd.h>
#include <linux/input-event-codes.h>
CSeatManager::~CSeatManager() {
if (m_pXKBState)
@ -76,7 +77,13 @@ void CSeatManager::registerSeat(SP<CCWlSeat> seat) {
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) {
m_pKeeb = makeShared<CCWlKeyboard>(r->sendGetKeyboard());

View file

@ -17,6 +17,7 @@ class CSeatManager {
SP<CCWlKeyboard> m_pKeeb;
SP<CCWlPointer> m_pPointer;
SP<CCWlTouch> m_pTouch;
UP<CCursorShape> m_pCursorShape;

View file

@ -1,6 +1,6 @@
#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) {
expires = std::chrono::system_clock::now() + timeout;
}
@ -17,7 +17,7 @@ bool CTimer::cancelled() {
return wasCancelled;
}
void CTimer::call(std::shared_ptr<CTimer> self) {
void CTimer::call(ASP<CTimer> self) {
cb(self, data);
}

View file

@ -2,10 +2,11 @@
#include <chrono>
#include <functional>
#include "../defines.hpp"
class CTimer {
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();
bool passed();
@ -14,12 +15,12 @@ class CTimer {
float leftMs();
bool cancelled();
void call(std::shared_ptr<CTimer> self);
void call(ASP<CTimer> self);
private:
std::function<void(std::shared_ptr<CTimer> self, void* data)> cb;
void* data = nullptr;
std::chrono::system_clock::time_point expires;
bool wasCancelled = false;
bool allowForceUpdate = false;
std::function<void(ASP<CTimer> self, void* data)> cb;
void* data = nullptr;
std::chrono::system_clock::time_point expires;
bool wasCancelled = false;
bool allowForceUpdate = false;
};

View file

@ -6,6 +6,7 @@
#include "../auth/Auth.hpp"
#include "../auth/Fingerprint.hpp"
#include "Egl.hpp"
#include <chrono>
#include <hyprutils/memory/UniquePtr.hpp>
#include <sys/wait.h>
#include <sys/poll.h>
@ -35,7 +36,7 @@ static void setMallocThreshold() {
#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();
m_sWaylandState.display = wl_display_connect(wlDisplay.empty() ? nullptr : wlDisplay.c_str());
@ -43,10 +44,9 @@ CHyprlock::CHyprlock(const std::string& wlDisplay, const bool immediate, const b
g_pEGL = makeUnique<CEGL>(m_sWaylandState.display);
if (!immediate) {
static const auto GRACE = g_pConfigManager->getValue<Hyprlang::INT>("general:grace");
m_tGraceEnds = *GRACE ? std::chrono::system_clock::now() + std::chrono::seconds(*GRACE) : std::chrono::system_clock::from_time_t(0);
} else
if (graceSeconds > 0)
m_tGraceEnds = std::chrono::system_clock::now() + std::chrono::seconds(graceSeconds);
else
m_tGraceEnds = std::chrono::system_clock::from_time_t(0);
static const auto IMMEDIATERENDER = g_pConfigManager->getValue<Hyprlang::INT>("general:immediate_render");
@ -313,18 +313,52 @@ void CHyprlock::run() {
Debug::log(LOG, "Running on {}", m_sCurrentDesktop);
// Hyprland violates the protocol a bit to allow for this.
if (m_sCurrentDesktop != "Hyprland") {
if (!g_pHyprlock->m_bImmediateRender) {
// Gather background resources and screencopy frames before locking the screen.
// 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.
// Bypass with --immediate-render (can cause the background first rendering a solid color and missing or inaccurate screencopy frames)
const auto MAXDELAYMS = 2000; // 2 Seconds
const auto STARTGATHERTP = std::chrono::system_clock::now();
int fdcount = 1;
pollfd pollfds[2];
pollfds[0] = {
.fd = wl_display_get_fd(m_sWaylandState.display),
.events = POLLIN,
};
if (g_pRenderer->asyncResourceGatherer->gatheredEventfd.isValid()) {
pollfds[1] = {
.fd = g_pRenderer->asyncResourceGatherer->gatheredEventfd.get(),
.events = POLLIN,
};
fdcount++;
}
while (!g_pRenderer->asyncResourceGatherer->gathered) {
wl_display_flush(m_sWaylandState.display);
if (wl_display_prepare_read(m_sWaylandState.display) == 0) {
if (poll(pollfds, fdcount, /* 100ms timeout */ 100) < 0) {
RASSERT(errno == EINTR, "[core] Polling fds failed with {}", errno);
wl_display_cancel_read(m_sWaylandState.display);
continue;
}
wl_display_read_events(m_sWaylandState.display);
wl_display_dispatch_pending(m_sWaylandState.display);
} else {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
wl_display_dispatch(m_sWaylandState.display);
}
std::this_thread::sleep_for(std::chrono::milliseconds(1));
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;
}
}
Debug::log(LOG, "Resources gathered after {} milliseconds",
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - STARTGATHERTP).count());
}
// Failed to lock the session
@ -447,7 +481,7 @@ void CHyprlock::run() {
auto timerscpy = m_vTimers;
m_sLoopState.timersMutex.unlock();
std::vector<std::shared_ptr<CTimer>> passed;
std::vector<ASP<CTimer>> passed;
for (auto& t : timerscpy) {
if (t->passed() && !t->cancelled()) {
@ -499,16 +533,13 @@ void CHyprlock::unlock() {
return;
}
const bool IMMEDIATE = m_sCurrentDesktop != "Hyprland";
g_pRenderer->startFadeOut(true, IMMEDIATE);
m_bUnlockedCalled = true;
g_pRenderer->startFadeOut(true);
renderAllOutputs();
}
bool CHyprlock::isUnlocked() {
return m_bUnlockedCalled || m_bTerminate;
return !m_bLocked;
}
void CHyprlock::clearPasswordBuffer() {
@ -555,7 +586,7 @@ void CHyprlock::startKeyRepeat(xkb_keysym_t sym) {
if (m_iKeebRepeatDelay <= 0)
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) {
@ -566,7 +597,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
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();
}
@ -859,23 +890,22 @@ size_t CHyprlock::getPasswordBufferDisplayLen() {
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,
bool force) {
ASP<CTimer> CHyprlock::addTimer(const std::chrono::system_clock::duration& timeout, std::function<void(ASP<CTimer> self, void* data)> cb_, void* data, bool force) {
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.timerCV.notify_all();
return T;
}
std::vector<std::shared_ptr<CTimer>> CHyprlock::getTimers() {
std::vector<ASP<CTimer>> CHyprlock::getTimers() {
return m_vTimers;
}
void CHyprlock::enqueueForceUpdateTimers() {
addTimer(
std::chrono::milliseconds(1),
[](std::shared_ptr<CTimer> self, void* data) {
[](ASP<CTimer> self, void* data) {
for (auto& t : g_pHyprlock->getTimers()) {
if (t->canForceUpdate()) {
t->call(t);

View file

@ -29,45 +29,44 @@ struct SDMABUFModifier {
class CHyprlock {
public:
CHyprlock(const std::string& wlDisplay, const bool immediate, const bool immediateRender);
CHyprlock(const std::string& wlDisplay, const bool immediateRender, const int gracePeriod);
~CHyprlock();
void run();
void run();
void unlock();
bool isUnlocked();
void unlock();
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,
bool force = false);
ASP<CTimer> addTimer(const std::chrono::system_clock::duration& timeout, std::function<void(ASP<CTimer> self, void* data)> cb_, void* data, bool force = false);
void enqueueForceUpdateTimers();
void enqueueForceUpdateTimers();
void onLockLocked();
void onLockFinished();
void onLockLocked();
void onLockFinished();
bool acquireSessionLock();
void releaseSessionLock();
bool acquireSessionLock();
void releaseSessionLock();
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 repeatKey(xkb_keysym_t sym);
void handleKeySym(xkb_keysym_t sym, bool compose);
void onPasswordCheckTimer();
void clearPasswordBuffer();
bool passwordCheckWaiting();
std::optional<std::string> passwordLastFailReason();
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 repeatKey(xkb_keysym_t sym);
void handleKeySym(xkb_keysym_t sym, bool compose);
void onPasswordCheckTimer();
void clearPasswordBuffer();
bool passwordCheckWaiting();
std::optional<std::string> passwordLastFailReason();
void renderOutput(const std::string& stringPort);
void renderAllOutputs();
void renderOutput(const std::string& stringPort);
void renderAllOutputs();
size_t getPasswordBufferLen();
size_t getPasswordBufferDisplayLen();
std::string getPasswordBuffer();
bool getPasswordShow();
size_t getPasswordBufferLen();
size_t getPasswordBufferDisplayLen();
std::string getPasswordBuffer();
bool getPasswordShow();
void togglePasswordShow();
void togglePasswordShow();
SP<CCExtSessionLockManagerV1> getSessionLockMgr();
SP<CCExtSessionLockV1> getSessionLock();
@ -103,10 +102,10 @@ class CHyprlock {
Vector2D m_vMouseLocation = {};
std::shared_ptr<CTimer> m_pKeyRepeatTimer = nullptr;
ASP<CTimer> m_pKeyRepeatTimer = nullptr;
std::vector<SP<COutput>> m_vOutputs;
std::vector<std::shared_ptr<CTimer>> getTimers();
std::vector<ASP<CTimer>> getTimers();
struct {
SP<CCZwpLinuxDmabufV1> linuxDmabuf = nullptr;
@ -163,11 +162,9 @@ class CHyprlock {
bool timerEvent = false;
} 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;
};
inline UP<CHyprlock> g_pHyprlock;

View file

@ -2,6 +2,7 @@
#include <hyprutils/memory/WeakPtr.hpp>
#include <hyprutils/memory/UniquePtr.hpp>
#include <hyprutils/memory/Atomic.hpp>
#include <hyprgraphics/color/Color.hpp>
using namespace Hyprutils::Memory;
@ -10,5 +11,8 @@ using namespace Hyprgraphics;
#define WP CWeakPointer
#define UP CUniquePointer
#define ASP CAtomicSharedPointer
#define AWP CAtomicWeakPointer
typedef int64_t OUTPUTID;
constexpr OUTPUTID OUTPUT_INVALID = -1;

View file

@ -13,7 +13,7 @@ void help() {
" -q, --quiet - Disable logging\n"
" -c FILE, --config FILE - Specify config file to use\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"
" --no-fade-in - Disable the fade-in animation when the lock screen appears\n"
" -V, --version - Show version information\n"
@ -40,9 +40,9 @@ static void printVersion() {
int main(int argc, char** argv, char** envp) {
std::string configPath;
std::string wlDisplay;
bool immediate = false;
bool immediateRender = false;
bool noFadeIn = false;
int graceSeconds = 0;
std::vector<std::string> args(argv, argv + argc);
@ -77,8 +77,25 @@ int main(int argc, char** argv, char** envp) {
else
return 1;
} else if (arg == "--immediate")
immediate = true;
} else if (arg == "--grace" && i + 1 < (std::size_t)argc) {
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")
immediateRender = true;
@ -107,11 +124,11 @@ int main(int argc, char** argv, char** envp) {
return 1;
}
if (noFadeIn || immediate)
if (noFadeIn)
g_pConfigManager->m_AnimationTree.setConfigForNode("fadeIn", false, 0.f, "default");
try {
g_pHyprlock = makeUnique<CHyprlock>(wlDisplay, immediate, immediateRender);
g_pHyprlock = makeUnique<CHyprlock>(wlDisplay, immediateRender, graceSeconds);
g_pHyprlock->run();
} catch (const std::exception& ex) {
Debug::log(CRIT, "Hyprlock threw: {}", ex.what());

View file

@ -1,16 +1,19 @@
#include "AsyncResourceGatherer.hpp"
#include "../config/ConfigManager.hpp"
#include "../core/Egl.hpp"
#include <cairo/cairo.h>
#include <pango/pangocairo.h>
#include <algorithm>
#include <filesystem>
#include "../core/hyprlock.hpp"
#include "../helpers/Color.hpp"
#include "../helpers/Log.hpp"
#include "../helpers/MiscFunctions.hpp"
#include "src/helpers/Color.hpp"
#include "src/helpers/Log.hpp"
#include <algorithm>
#include <cairo/cairo.h>
#include <filesystem>
#include <pango/pangocairo.h>
#include <sys/eventfd.h>
#include <hyprgraphics/image/Image.hpp>
#include <hyprutils/os/FileDescriptor.hpp>
using namespace Hyprgraphics;
using namespace Hyprutils::OS;
CAsyncResourceGatherer::CAsyncResourceGatherer() {
if (g_pHyprlock->getScreencopy())
@ -18,45 +21,39 @@ CAsyncResourceGatherer::CAsyncResourceGatherer() {
initialGatherThread = std::thread([this]() { this->gather(); });
asyncLoopThread = std::thread([this]() { this->asyncAssetSpinLock(); });
gatheredEventfd = CFileDescriptor{eventfd(0, EFD_CLOEXEC)};
if (!gatheredEventfd.isValid())
Debug::log(ERR, "Failed to create eventfd: {}", strerror(errno));
}
void CAsyncResourceGatherer::enqueueScreencopyFrames() {
// some things can't be done async :(
// gather background textures when needed
const auto FADEINCFG = g_pConfigManager->m_AnimationTree.getConfig("fadeIn");
const auto FADEOUTCFG = g_pConfigManager->m_AnimationTree.getConfig("fadeOut");
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";
});
const auto CWIDGETS = g_pConfigManager->getWidgetConfigs();
// No screenshot background AND no fade in AND no fade out -> we don't need screencopy
if (!BGSCREENSHOT && (!FADEINCFG->pValues || !FADEINCFG->pValues->internalEnabled) && //
(!FADEOUTCFG->pValues || !FADEOUTCFG->pValues->internalEnabled))
return;
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));
for (const auto& MON : g_pHyprlock->m_vOutputs) {
scframes.emplace_back(makeUnique<CScreencopyFrame>(MON));
}
}
SPreloadedAsset* CAsyncResourceGatherer::getAssetByID(const std::string& id) {
if (id.contains(CScreencopyFrame::RESOURCEIDPREFIX)) {
for (auto& frame : scframes) {
if (id == frame->m_resourceID)
return frame->m_asset.ready ? &frame->m_asset : nullptr;
}
return nullptr;
}
for (auto& a : assets) {
if (a.first == id)
return &a.second;
@ -69,16 +66,10 @@ SPreloadedAsset* CAsyncResourceGatherer::getAssetByID(const std::string& id) {
}
};
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());
@ -131,6 +122,9 @@ void CAsyncResourceGatherer::gather() {
}
gathered = true;
// wake hyprlock from poll
if (gatheredEventfd.isValid())
eventfd_write(gatheredEventfd.get(), 1);
}
bool CAsyncResourceGatherer::apply() {

View file

@ -10,13 +10,15 @@
#include <any>
#include "Shared.hpp"
#include <hyprgraphics/cairo/CairoSurface.hpp>
#include <hyprutils/os/FileDescriptor.hpp>
class CAsyncResourceGatherer {
public:
CAsyncResourceGatherer();
std::atomic<bool> gathered = false;
std::atomic<bool> gathered = false;
Hyprutils::OS::CFileDescriptor gatheredEventfd;
std::atomic<float> progress = 0;
std::atomic<float> progress = 0;
/* only call from ogl thread */
SPreloadedAsset* getAssetByID(const std::string& id);

View file

@ -1,6 +1,8 @@
#include "Framebuffer.hpp"
#include "../helpers/Log.hpp"
#include <hyprutils/os/FileDescriptor.hpp>
#include <libdrm/drm_fourcc.h>
#include <utility>
static uint32_t drmFormatToGL(uint32_t drm) {
switch (drm) {
@ -97,7 +99,7 @@ void CFramebuffer::bind() const {
glViewport(0, 0, m_vSize.x, m_vSize.y);
}
void CFramebuffer::release() {
void CFramebuffer::destroyBuffer() {
if (m_iFb != (uint32_t)-1 && m_iFb)
glDeleteFramebuffers(1, &m_iFb);
@ -114,7 +116,7 @@ void CFramebuffer::release() {
}
CFramebuffer::~CFramebuffer() {
release();
destroyBuffer();
}
bool CFramebuffer::isAllocated() const {

View file

@ -8,17 +8,19 @@ class CFramebuffer {
public:
~CFramebuffer();
bool alloc(int w, int h, bool highres = false);
void addStencil();
void bind() const;
void release();
void reset();
bool isAllocated() const;
bool alloc(int w, int h, bool highres = false);
void addStencil();
void bind() const;
void destroyBuffer();
bool isAllocated() const;
Vector2D m_vSize;
Vector2D m_vSize;
CTexture m_cTex;
GLuint m_iFb = -1;
CTexture m_cTex;
GLuint m_iFb = -1;
CTexture* m_pStencilTex = nullptr;
CTexture* m_pStencilTex = nullptr;
CFramebuffer& operator=(CFramebuffer&&) = delete;
CFramebuffer& operator=(const CFramebuffer&) = delete;
};

View file

@ -388,13 +388,13 @@ void CRenderer::renderTextureMix(const CBox& box, const CTexture& tex, const CTe
}
template <class Widget>
static void createWidget(std::vector<SP<IWidget>>& widgets) {
const auto W = makeShared<Widget>();
static void createWidget(std::vector<ASP<IWidget>>& widgets) {
const auto W = makeAtomicShared<Widget>();
W->registerSelf(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!");
if (!widgets.contains(surf.m_outputID)) {
@ -613,11 +613,8 @@ void CRenderer::startFadeIn() {
opacity->setCallbackOnEnd([this](auto) { opacity->setConfig(g_pConfigManager->m_AnimationTree.getConfig("fadeOut")); }, true);
}
void CRenderer::startFadeOut(bool unlock, bool immediate) {
if (immediate)
opacity->setValueAndWarp(0.f);
else
*opacity = 0.f;
void CRenderer::startFadeOut(bool unlock) {
*opacity = 0.f;
if (unlock)
opacity->setCallbackOnEnd([](auto) { g_pHyprlock->releaseSessionLock(); }, true);

View file

@ -12,7 +12,7 @@
#include "widgets/IWidget.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 {
public:
@ -47,8 +47,8 @@ class CRenderer {
void reconfigureWidgetsFor(OUTPUTID id);
void startFadeIn();
void startFadeOut(bool unlock = false, bool immediate = true);
std::vector<SP<IWidget>>& getOrCreateWidgetsFor(const CSessionLockSurface& surf);
void startFadeOut(bool unlock = false);
std::vector<ASP<IWidget>>& getOrCreateWidgetsFor(const CSessionLockSurface& surf);
private:
widgetMap_t widgets;

View file

@ -24,7 +24,7 @@ static PFNEGLQUERYDMABUFMODIFIERSEXTPROC eglQueryDmaBufModifiersEXT = nullpt
//
std::string CScreencopyFrame::getResourceId(SP<COutput> pOutput) {
return std::format("screencopy:{}-{}x{}", pOutput->stringPort, pOutput->size.x, pOutput->size.y);
return RESOURCEIDPREFIX + std::format(":{}-{}x{}", pOutput->stringPort, pOutput->size.x, pOutput->size.y);
}
CScreencopyFrame::CScreencopyFrame(SP<COutput> pOutput) : m_outputRef(pOutput) {

View file

@ -22,7 +22,8 @@ class ISCFrame {
class CScreencopyFrame {
public:
static std::string getResourceId(SP<COutput> pOutput);
static std::string getResourceId(SP<COutput> pOutput);
static constexpr const std::string RESOURCEIDPREFIX = "screencopy";
CScreencopyFrame(SP<COutput> pOutput);
~CScreencopyFrame() = default;

View file

@ -1,19 +1,28 @@
#include "Background.hpp"
#include "../Renderer.hpp"
#include "../Framebuffer.hpp"
#include "../Shared.hpp"
#include "../../core/hyprlock.hpp"
#include "../../helpers/Log.hpp"
#include "../../helpers/MiscFunctions.hpp"
#include "../../core/AnimationManager.hpp"
#include "../../config/ConfigManager.hpp"
#include <chrono>
#include <hyprlang.hpp>
#include <filesystem>
#include <memory>
#include <GLES3/gl32.h>
CBackground::CBackground() {
blurredFB = makeUnique<CFramebuffer>();
pendingBlurredFB = makeUnique<CFramebuffer>();
}
CBackground::~CBackground() {
reset();
}
void CBackground::registerSelf(const SP<CBackground>& self) {
void CBackground::registerSelf(const ASP<CBackground>& 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"));
reloadCommand = std::any_cast<Hyprlang::STRING>(props.at("reload_cmd"));
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) {
RASSERT(false, "Failed to construct CBackground: {}", e.what()); //
@ -42,12 +50,15 @@ void CBackground::configure(const std::unordered_map<std::string, std::any>& pro
isScreenshot = path == "screenshot";
viewport = pOutput->getViewport();
outputPort = pOutput->stringPort;
transform = isScreenshot ? wlTransformToHyprutils(invertTransform(pOutput->transform)) : HYPRUTILS_TRANSFORM_NORMAL;
viewport = pOutput->getViewport();
outputPort = pOutput->stringPort;
transform = isScreenshot ? wlTransformToHyprutils(invertTransform(pOutput->transform)) : HYPRUTILS_TRANSFORM_NORMAL;
scResourceID = CScreencopyFrame::getResourceId(pOutput);
g_pAnimationManager->createAnimation(0.f, crossFadeProgress, g_pConfigManager->m_AnimationTree.getConfig("fadeIn"));
if (isScreenshot) {
resourceID = CScreencopyFrame::getResourceId(pOutput);
resourceID = scResourceID;
// 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) {
@ -78,13 +89,8 @@ void CBackground::reset() {
reloadTimer.reset();
}
if (fade) {
if (fade->crossFadeTimer) {
fade->crossFadeTimer->cancel();
fade->crossFadeTimer.reset();
}
fade.reset();
}
blurredFB->destroyBuffer();
pendingBlurredFB->destroyBuffer();
}
void CBackground::renderRect(CHyprColor color) {
@ -92,105 +98,33 @@ void CBackground::renderRect(CHyprColor color) {
g_pRenderer->renderRect(monbox, color, 0);
}
static void onReloadTimer(WP<CBackground> ref) {
static void onReloadTimer(AWP<CBackground> ref) {
if (auto PBG = ref.lock(); PBG) {
PBG->onReloadTimerUpdate();
PBG->plantReloadTimer();
}
}
static void onCrossFadeTimer(WP<CBackground> ref) {
static void onAssetCallback(AWP<CBackground> ref) {
if (auto PBG = ref.lock(); PBG)
PBG->onCrossFadeTimerUpdate();
PBG->startCrossFade();
}
static void onAssetCallback(WP<CBackground> ref) {
if (auto PBG = ref.lock(); PBG)
PBG->startCrossFadeOrUpdateRender();
}
void CBackground::renderBlur(const CTexture& tex, CFramebuffer& fb) {
if (firstRender)
firstRender = false;
bool CBackground::draw(const SRenderData& data) {
if (resourceID.empty()) {
CHyprColor col = color;
col.a *= data.opacity;
renderRect(col);
return data.opacity < 1.0;
// 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;
}
if (!asset)
asset = g_pRenderer->asyncResourceGatherer->getAssetByID(resourceID);
CBox texbox = {{}, size};
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};
float scaleX = viewport.x / size.x;
float scaleY = viewport.y / size.y;
texbox.w *= std::max(scaleX, scaleY);
texbox.h *= std::max(scaleX, scaleY);
if (scaleX > scaleY)
texbox.y = -(texbox.h - viewport.y) / 2.f;
else
texbox.x = -(texbox.w - viewport.x) / 2.f;
texbox.round();
if (!blurredFB.isAllocated())
blurredFB.alloc(viewport.x, viewport.y); // TODO 10 bit
blurredFB.bind();
if (fade)
g_pRenderer->renderTextureMix(texbox, asset->texture, pendingAsset->texture, 1.0,
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - fade->start).count() / (1000 * crossFadeTime), 0,
transform);
else
g_pRenderer->renderTexture(texbox, asset->texture, 1.0, 0, transform);
if (blurPasses > 0)
g_pRenderer->blurFB(blurredFB,
CRenderer::SBlurParams{.size = blurSize,
.passes = blurPasses,
.noise = noise,
.contrast = contrast,
.brightness = brightness,
.vibrancy = vibrancy,
.vibrancy_darkness = vibrancy_darkness});
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
}
CTexture* tex = blurredFB.isAllocated() ? &blurredFB.m_cTex : &asset->texture;
CBox texbox = {{}, tex->m_vSize};
Vector2D size = tex->m_vSize;
float scaleX = viewport.x / tex->m_vSize.x;
float scaleY = viewport.y / tex->m_vSize.y;
float scaleX = viewport.x / size.x;
float scaleY = viewport.y / size.y;
texbox.w *= std::max(scaleX, scaleY);
texbox.h *= std::max(scaleX, scaleY);
@ -200,9 +134,94 @@ bool CBackground::draw(const SRenderData& data) {
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 (!fb.isAllocated())
fb.alloc(viewport.x, viewport.y); // TODO 10 bit
fb.bind();
g_pRenderer->renderTexture(texbox, tex, 1.0, 0, transform);
if (blurPasses > 0)
g_pRenderer->blurFB(fb,
CRenderer::SBlurParams{.size = blurSize,
.passes = blurPasses,
.noise = noise,
.contrast = contrast,
.brightness = brightness,
.vibrancy = vibrancy,
.vibrancy_darkness = vibrancy_darkness});
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
}
static CBox getScaledBoxForTexture(const CTexture& tex, const Vector2D& viewport) {
CBox texbox = {{}, tex.m_vSize};
Vector2D size = tex.m_vSize;
float scaleX = viewport.x / tex.m_vSize.x;
float scaleY = viewport.y / tex.m_vSize.y;
texbox.w *= std::max(scaleX, scaleY);
texbox.h *= std::max(scaleX, scaleY);
if (scaleX > scaleY)
texbox.y = -(texbox.h - viewport.y) / 2.f;
else
texbox.x = -(texbox.w - viewport.x) / 2.f;
texbox.round();
return texbox;
}
bool CBackground::draw(const SRenderData& data) {
if (!asset && !resourceID.empty())
asset = g_pRenderer->asyncResourceGatherer->getAssetByID(resourceID);
// path=screenshot -> scAsset = asset
if (!scAsset)
scAsset = (asset && isScreenshot) ? asset : g_pRenderer->asyncResourceGatherer->getAssetByID(scResourceID);
if (!asset || resourceID.empty()) {
// fade in/out with a solid color
if (data.opacity < 1.0 && scAsset) {
const auto SCTEXBOX = getScaledBoxForTexture(scAsset->texture, viewport);
g_pRenderer->renderTexture(SCTEXBOX, scAsset->texture, 1, 0, HYPRUTILS_TRANSFORM_FLIPPED_180);
CHyprColor col = color;
col.a *= data.opacity;
renderRect(col);
return true;
}
renderRect(color);
return !asset && !resourceID.empty(); // resource not ready
}
if (asset->texture.m_iType == TEXTURE_INVALID) {
g_pRenderer->asyncResourceGatherer->unloadAsset(asset);
resourceID = "";
renderRect(color);
return false;
}
if (asset && (blurPasses > 0 || isScreenshot) && (!blurredFB->isAllocated() || firstRender))
renderBlur(asset->texture, *blurredFB);
// For crossfading a new asset
if (pendingAsset && blurPasses > 0 && !pendingBlurredFB->isAllocated())
renderBlur(pendingAsset->texture, *pendingBlurredFB);
const auto& TEX = blurredFB->isAllocated() ? blurredFB->m_cTex : asset->texture;
const auto TEXBOX = getScaledBoxForTexture(TEX, viewport);
if (data.opacity < 1.0 && scAsset)
g_pRenderer->renderTextureMix(TEXBOX, scAsset->texture, TEX, 1.0, data.opacity, 0);
else if (crossFadeProgress->isBeingAnimated()) {
const auto& PENDINGTEX = pendingBlurredFB->isAllocated() ? pendingBlurredFB->m_cTex : pendingAsset->texture;
g_pRenderer->renderTextureMix(TEXBOX, TEX, PENDINGTEX, 1.0, crossFadeProgress->value(), 0);
} else
g_pRenderer->renderTexture(TEXBOX, TEX, 1, 0, HYPRUTILS_TRANSFORM_FLIPPED_180);
return crossFadeProgress->isBeingAnimated() || data.opacity < 1.0;
}
void CBackground::plantReloadTimer() {
@ -213,27 +232,6 @@ void CBackground::plantReloadTimer() {
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() {
const std::string OLDPATH = path;
@ -279,37 +277,37 @@ void CBackground::onReloadTimerUpdate() {
g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
}
void CBackground::startCrossFadeOrUpdateRender() {
void CBackground::startCrossFade() {
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!");
pendingResourceID = "";
} 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();
crossFadeProgress->setValueAndWarp(0);
*crossFadeProgress = 1.0;
crossFadeProgress->setCallbackOnEnd(
[REF = m_self](auto) {
if (const auto PSELF = REF.lock()) {
PSELF->asset = PSELF->pendingAsset;
PSELF->pendingAsset = nullptr;
g_pRenderer->asyncResourceGatherer->unloadAsset(PSELF->pendingAsset);
PSELF->resourceID = PSELF->pendingResourceID;
PSELF->pendingResourceID = "";
PSELF->blurredFB->destroyBuffer();
PSELF->blurredFB = std::move(PSELF->pendingBlurredFB);
}
}
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();
}
},
true);
g_pHyprlock->renderOutput(outputPort);
}
} 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);
}

View file

@ -1,6 +1,7 @@
#pragma once
#include "IWidget.hpp"
#include "../../helpers/AnimatedVariable.hpp"
#include "../../helpers/Color.hpp"
#include "../../helpers/Math.hpp"
#include "../../core/Timer.hpp"
@ -16,18 +17,12 @@
struct SPreloadedAsset;
class COutput;
struct SFade {
std::chrono::system_clock::time_point start;
float a = 0;
std::shared_ptr<CTimer> crossFadeTimer = nullptr;
};
class CBackground : public IWidget {
public:
CBackground() = default;
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 bool draw(const SRenderData& data);
@ -36,16 +31,18 @@ class CBackground : public IWidget {
void renderRect(CHyprColor color);
void renderBlur(const CTexture& text, CFramebuffer& fb);
void onReloadTimerUpdate();
void onCrossFadeTimerUpdate();
void plantReloadTimer();
void startCrossFadeOrUpdateRender();
void startCrossFade();
private:
WP<CBackground> m_self;
AWP<CBackground> m_self;
// if needed
CFramebuffer blurredFB;
UP<CFramebuffer> blurredFB;
UP<CFramebuffer> pendingBlurredFB;
int blurSize = 10;
int blurPasses = 3;
@ -61,21 +58,21 @@ class CBackground : public IWidget {
Hyprutils::Math::eTransform transform;
std::string resourceID;
std::string scResourceID;
std::string pendingResourceID;
float crossFadeTime = -1.0;
PHLANIMVAR<float> crossFadeProgress;
CHyprColor color;
SPreloadedAsset* asset = nullptr;
bool isScreenshot = false;
SPreloadedAsset* scAsset = nullptr;
SPreloadedAsset* pendingAsset = nullptr;
bool isScreenshot = false;
bool firstRender = true;
UP<SFade> fade;
int reloadTime = -1;
std::string reloadCommand;
CAsyncResourceGatherer::SPreloadRequest request;
std::shared_ptr<CTimer> reloadTimer;
ASP<CTimer> reloadTimer;
std::filesystem::file_time_type modificationTime;
};

View file

@ -12,18 +12,18 @@ CImage::~CImage() {
reset();
}
void CImage::registerSelf(const SP<CImage>& self) {
void CImage::registerSelf(const ASP<CImage>& self) {
m_self = self;
}
static void onTimer(WP<CImage> ref) {
static void onTimer(AWP<CImage> ref) {
if (auto PIMAGE = ref.lock(); PIMAGE) {
PIMAGE->onTimerUpdate();
PIMAGE->plantTimer();
}
}
static void onAssetCallback(WP<CImage> ref) {
static void onAssetCallback(AWP<CImage> ref) {
if (auto PIMAGE = ref.lock(); PIMAGE)
PIMAGE->renderUpdate();
}
@ -82,7 +82,7 @@ void CImage::configure(const std::unordered_map<std::string, std::any>& props, c
viewport = pOutput->getViewport();
stringPort = pOutput->stringPort;
shadow.configure(m_self.lock(), props, viewport);
shadow.configure(m_self, props, viewport);
try {
size = std::any_cast<Hyprlang::INT>(props.at("size"));
@ -125,7 +125,7 @@ void CImage::reset() {
if (g_pHyprlock->m_bTerminate)
return;
imageFB.release();
imageFB.destroyBuffer();
if (asset && reloadTime > -1) // Don't unload asset if it's a static image
g_pRenderer->asyncResourceGatherer->unloadAsset(asset);
@ -217,7 +217,7 @@ void CImage::renderUpdate() {
g_pRenderer->asyncResourceGatherer->unloadAsset(newAsset);
} else if (resourceID != pendingResourceID) {
g_pRenderer->asyncResourceGatherer->unloadAsset(asset);
imageFB.release();
imageFB.destroyBuffer();
asset = newAsset;
resourceID = pendingResourceID;

View file

@ -20,7 +20,7 @@ class CImage : public IWidget {
CImage() = default;
~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 bool draw(const SRenderData& data);
@ -35,7 +35,7 @@ class CImage : public IWidget {
void plantTimer();
private:
WP<CImage> m_self;
AWP<CImage> m_self;
CFramebuffer imageFB;
@ -56,7 +56,7 @@ class CImage : public IWidget {
std::string onclickCommand;
std::filesystem::file_time_type modificationTime;
std::shared_ptr<CTimer> imageTimer;
ASP<CTimer> imageTimer;
CAsyncResourceGatherer::SPreloadRequest request;
Vector2D viewport;

View file

@ -12,11 +12,11 @@ CLabel::~CLabel() {
reset();
}
void CLabel::registerSelf(const SP<CLabel>& self) {
void CLabel::registerSelf(const ASP<CLabel>& self) {
m_self = self;
}
static void onTimer(WP<CLabel> ref) {
static void onTimer(AWP<CLabel> ref) {
if (auto PLABEL = ref.lock(); PLABEL) {
// update label
PLABEL->onTimerUpdate();
@ -25,7 +25,7 @@ static void onTimer(WP<CLabel> ref) {
}
}
static void onAssetCallback(WP<CLabel> ref) {
static void onAssetCallback(AWP<CLabel> ref) {
if (auto PLABEL = ref.lock(); PLABEL)
PLABEL->renderUpdate();
}
@ -71,7 +71,7 @@ void CLabel::configure(const std::unordered_map<std::string, std::any>& props, c
outputStringPort = pOutput->stringPort;
viewport = pOutput->getViewport();
shadow.configure(m_self.lock(), props, viewport);
shadow.configure(m_self, props, viewport);
try {
configPos = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport);

View file

@ -17,7 +17,7 @@ class CLabel : public IWidget {
CLabel() = default;
~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 bool draw(const SRenderData& data);
@ -32,7 +32,7 @@ class CLabel : public IWidget {
void plantTimer();
private:
WP<CLabel> m_self;
AWP<CLabel> m_self;
std::string getUniqueResourceId();
@ -53,7 +53,7 @@ class CLabel : public IWidget {
CAsyncResourceGatherer::SPreloadRequest request;
std::shared_ptr<CTimer> labelTimer = nullptr;
ASP<CTimer> labelTimer = nullptr;
CShadowable shadow;
bool updateShadow = true;

View file

@ -20,7 +20,7 @@ CPasswordInputField::~CPasswordInputField() {
reset();
}
void CPasswordInputField::registerSelf(const SP<CPasswordInputField>& self) {
void CPasswordInputField::registerSelf(const ASP<CPasswordInputField>& self) {
m_self = self;
}
@ -30,7 +30,7 @@ void CPasswordInputField::configure(const std::unordered_map<std::string, std::a
outputStringPort = pOutput->stringPort;
viewport = pOutput->getViewport();
shadow.configure(m_self.lock(), props, viewport);
shadow.configure(m_self, props, viewport);
try {
pos = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport);
@ -123,12 +123,12 @@ void CPasswordInputField::reset() {
placeholder.currentText.clear();
}
static void fadeOutCallback(WP<CPasswordInputField> ref) {
static void fadeOutCallback(AWP<CPasswordInputField> ref) {
if (const auto PP = ref.lock(); PP)
PP->onFadeOutTimer();
}
static void assetReadyCallback(WP<CPasswordInputField> ref) {
static void assetReadyCallback(AWP<CPasswordInputField> ref) {
if (auto PINPUT = ref.lock(); PINPUT)
PINPUT->renderPasswordUpdate();
}

View file

@ -19,7 +19,7 @@ class CPasswordInputField : public IWidget {
CPasswordInputField() = default;
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 bool draw(const SRenderData& data);
@ -33,35 +33,35 @@ class CPasswordInputField : public IWidget {
void renderPasswordUpdate();
private:
WP<CPasswordInputField> m_self;
AWP<CPasswordInputField> m_self;
void updatePassword();
void updateEye();
void updateDots();
void updateFade();
void updatePlaceholder();
void updateWidth();
void updateHiddenInputState();
void updateInputState();
void updateColors();
void updatePassword();
void updateEye();
void updateDots();
void updateFade();
void updatePlaceholder();
void updateWidth();
void updateHiddenInputState();
void updateInputState();
void updateColors();
bool firstRender = true;
bool redrawShadow = false;
bool checkWaiting = false;
bool displayFail = false;
bool firstRender = true;
bool redrawShadow = false;
bool checkWaiting = false;
bool displayFail = false;
size_t passwordLength = 0;
size_t passwordLength = 0;
PHLANIMVAR<Vector2D> size;
Vector2D pos;
Vector2D viewport;
Vector2D configPos;
Vector2D configSize;
PHLANIMVAR<Vector2D> size;
Vector2D pos;
Vector2D viewport;
Vector2D configPos;
Vector2D configSize;
std::string halign, valign, configFailText, outputStringPort, configPlaceholderText, fontFamily;
uint64_t configFailTimeoutMs = 2000;
std::string halign, valign, configFailText, outputStringPort, configPlaceholderText, fontFamily;
uint64_t configFailTimeoutMs = 2000;
int outThick, rounding;
int outThick, rounding;
struct {
PHLANIMVAR<float> currentAmount;
@ -98,10 +98,10 @@ class CPasswordInputField : public IWidget {
} eye;
struct {
PHLANIMVAR<float> a;
bool appearing = true;
std::shared_ptr<CTimer> fadeOutTimer = nullptr;
bool allowFadeOut = false;
PHLANIMVAR<float> a;
bool appearing = true;
ASP<CTimer> fadeOutTimer = nullptr;
bool allowFadeOut = false;
} fade;
struct {

View file

@ -2,7 +2,7 @@
#include "../Renderer.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_;
viewport = viewport_;

View file

@ -13,19 +13,19 @@ class CShadowable {
public:
virtual ~CShadowable() = default;
CShadowable() = default;
void configure(WP<IWidget> widget_, const std::unordered_map<std::string, std::any>& props, const Vector2D& viewport_ /* TODO: make this not the entire viewport */);
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
void markShadowDirty();
virtual bool draw(const IWidget::SRenderData& data);
private:
WP<IWidget> m_widget;
int size = 10;
int passes = 4;
float boostA = 1.0;
CHyprColor color{0, 0, 0, 1.0};
Vector2D viewport;
AWP<IWidget> m_widget;
int size = 10;
int passes = 4;
float boostA = 1.0;
CHyprColor color{0, 0, 0, 1.0};
Vector2D viewport;
// to avoid recursive shadows
bool ignoreDraw = false;

View file

@ -7,14 +7,14 @@
#include <hyprlang.hpp>
#include <sys/types.h>
void CShape::registerSelf(const SP<CShape>& self) {
void CShape::registerSelf(const ASP<CShape>& self) {
m_self = self;
}
void CShape::configure(const std::unordered_map<std::string, std::any>& props, const SP<COutput>& pOutput) {
viewport = pOutput->getViewport();
shadow.configure(m_self.lock(), props, viewport);
shadow.configure(m_self, props, viewport);
try {
size = CLayoutValueData::fromAnyPv(props.at("size"))->getAbsolute(viewport);

View file

@ -14,7 +14,7 @@ class CShape : public IWidget {
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 bool draw(const SRenderData& data);
@ -23,7 +23,7 @@ class CShape : public IWidget {
virtual void onHover(const Vector2D& pos);
private:
WP<CShape> m_self;
AWP<CShape> m_self;
CFramebuffer shapeFB;