From 9f1ff2af51cdef98ae3f02fc646bd6f7e47ff74a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Rodon?= Date: Fri, 20 Feb 2026 17:18:58 +0100 Subject: [PATCH] fix(fingerprint): prevent sensor timeout on long-running sessions Introduces an inactivity timeout mechanism that pauses fingerprint verification after a configurable period (default: 0 second/disabled) when no user input is detected. This prevents some device drivers from disconnecting or disabling the sensor during extended lock sessions. The verification automatically resumes when user activity is detected (mouse movement, clicks, or keyboard input). The sensor is properly released when paused and re-claimed when resuming. May fixe #702 though that's unclear as the issue is not well documented --- src/auth/Fingerprint.cpp | 51 +++++++++++++++++++++++++++++++++++- src/auth/Fingerprint.hpp | 8 ++++++ src/config/ConfigManager.cpp | 1 + src/core/Seat.cpp | 2 ++ src/core/hyprlock.cpp | 16 +++++++++++ src/core/hyprlock.hpp | 1 + 6 files changed, 78 insertions(+), 1 deletion(-) diff --git a/src/auth/Fingerprint.cpp b/src/auth/Fingerprint.cpp index 780c079..8fc37aa 100644 --- a/src/auth/Fingerprint.cpp +++ b/src/auth/Fingerprint.cpp @@ -40,6 +40,8 @@ CFingerprint::CFingerprint() { m_sFingerprintReady = *FINGERPRINTREADY; static const auto FINGERPRINTPRESENT = g_pConfigManager->getValue("auth:fingerprint:present_message"); m_sFingerprintPresent = *FINGERPRINTPRESENT; + static const auto INACTIVETIMEOUT = g_pConfigManager->getValue("auth:fingerprint:inactive_timeout"); + m_sInactiveTimeout = *INACTIVETIMEOUT; } CFingerprint::~CFingerprint() { @@ -96,10 +98,54 @@ bool CFingerprint::checkWaiting() { } void CFingerprint::terminate() { + if (m_pInactivityTimer) { + m_pInactivityTimer->cancel(); + m_pInactivityTimer.reset(); + } + if (!m_sDBUSState.abort) releaseDevice(); } +void CFingerprint::setupInactivityTimer() { + if (m_sInactiveTimeout <= 0 || m_sDBUSState.abort || m_sDBUSState.done) + return; + + if (m_pInactivityTimer) { + m_pInactivityTimer->cancel(); + m_pInactivityTimer.reset(); + } + + m_pInactivityTimer = g_pHyprlock->addTimer(std::chrono::milliseconds(m_sInactiveTimeout * 1000), + [](ASP self, void* data) { ((CFingerprint*)data)->onInactivityTimeout(); }, this); +} + +void CFingerprint::onActivity() { + if (!m_sDBUSState.verifying) { + Debug::log(LOG, "fprint: activity detected, resuming verification"); + startVerify(); + } +} + +void CFingerprint::onInactivityTimeout() { + if (m_sDBUSState.abort || m_sDBUSState.done || !m_sDBUSState.verifying) + return; + + Debug::log(LOG, "fprint: inactivity timeout, pausing verification"); + + stopVerify(); + + releaseDevice(); + + m_sDBUSState.device.reset(); + + // Clear the prompt text to provide user feedback + m_sPrompt = ""; + g_pHyprlock->enqueueForceUpdateTimers(); + + m_pInactivityTimer.reset(); +} + std::shared_ptr CFingerprint::getConnection() { return m_sDBUSState.connection; } @@ -237,8 +283,11 @@ void CFingerprint::startVerify(bool isRetry) { if (isRetry) { m_sDBUSState.retries++; m_sPrompt = "Could not match fingerprint. Try again."; - } else + } else { m_sPrompt = m_sFingerprintReady; + + setupInactivityTimer(); + } } g_pHyprlock->enqueueForceUpdateTimers(); }); diff --git a/src/auth/Fingerprint.hpp b/src/auth/Fingerprint.hpp index 404a5ca..b940063 100644 --- a/src/auth/Fingerprint.hpp +++ b/src/auth/Fingerprint.hpp @@ -22,6 +22,8 @@ class CFingerprint : public IAuthImplementation { virtual std::optional getLastPrompt(); virtual void terminate(); + void onActivity(); + std::shared_ptr getConnection(); private: @@ -39,10 +41,13 @@ class CFingerprint : public IAuthImplementation { std::string m_sFingerprintReady; std::string m_sFingerprintPresent; + int m_sInactiveTimeout; std::string m_sPrompt{""}; std::string m_sFailureReason{""}; + ASP m_pInactivityTimer; + void handleVerifyStatus(const std::string& result, const bool done); bool createDeviceProxy(); @@ -50,4 +55,7 @@ class CFingerprint : public IAuthImplementation { void startVerify(bool isRetry = false); bool stopVerify(); bool releaseDevice(); + + void onInactivityTimeout(); + void setupInactivityTimer(); }; diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 2c200f8..6a9991d 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -249,6 +249,7 @@ void CConfigManager::init() { m_config.addConfigValue("auth:fingerprint:ready_message", Hyprlang::STRING{"(Scan fingerprint to unlock)"}); m_config.addConfigValue("auth:fingerprint:present_message", Hyprlang::STRING{"Scanning fingerprint"}); m_config.addConfigValue("auth:fingerprint:retry_delay", Hyprlang::INT{250}); + m_config.addConfigValue("auth:fingerprint:inactive_timeout", Hyprlang::INT{0}); m_config.addConfigValue("animations:enabled", Hyprlang::INT{1}); diff --git a/src/core/Seat.cpp b/src/core/Seat.cpp index 6fe27ca..4bf10e6 100644 --- a/src/core/Seat.cpp +++ b/src/core/Seat.cpp @@ -34,6 +34,8 @@ void CSeatManager::registerSeat(SP seat) { 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)}; + g_pHyprlock->onMouseMove(g_pHyprlock->m_vMouseLocation); + if (!*HIDECURSOR) g_pHyprlock->onHover(g_pHyprlock->m_vMouseLocation); diff --git a/src/core/hyprlock.cpp b/src/core/hyprlock.cpp index 3b25667..c782615 100644 --- a/src/core/hyprlock.cpp +++ b/src/core/hyprlock.cpp @@ -696,6 +696,13 @@ void CHyprlock::onClick(uint32_t button, bool down, const Vector2D& pos) { if (!m_focusedOutput->m_sessionLockSurface) return; + if (g_pAuth) { + auto fpImpl = g_pAuth->getImpl(AUTH_IMPL_FINGERPRINT); + if (fpImpl) { + ((CFingerprint*)fpImpl.get())->onActivity(); + } + } + const auto SCALEDPOS = pos * m_focusedOutput->m_sessionLockSurface->fractionalScale; const auto widgets = g_pRenderer->getOrCreateWidgetsFor(*m_focusedOutput->m_sessionLockSurface); for (const auto& widget : widgets) { @@ -743,6 +750,15 @@ void CHyprlock::onHover(const Vector2D& pos) { m_focusedOutput->m_sessionLockSurface->render(); } +void CHyprlock::onMouseMove(const Vector2D& pos) { + if (g_pAuth) { + auto fpImpl = g_pAuth->getImpl(AUTH_IMPL_FINGERPRINT); + if (fpImpl) { + ((CFingerprint*)fpImpl.get())->onActivity(); + } + } +} + bool CHyprlock::acquireSessionLock() { Log::logger->log(Log::INFO, "Locking session"); m_sLockState.lock = makeShared(m_sWaylandState.sessionLock->sendLock()); diff --git a/src/core/hyprlock.hpp b/src/core/hyprlock.hpp index db25aec..3964f19 100644 --- a/src/core/hyprlock.hpp +++ b/src/core/hyprlock.hpp @@ -49,6 +49,7 @@ class CHyprlock { void onKey(uint32_t key, bool down); void onClick(uint32_t button, bool down, const Vector2D& pos); void onHover(const Vector2D& pos); + void onMouseMove(const Vector2D& pos); void startKeyRepeat(xkb_keysym_t sym); void repeatKey(xkb_keysym_t sym); void handleKeySym(xkb_keysym_t sym, bool compose);