diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 97db9ead..590694d7 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -46,6 +46,7 @@ void CConfigManager::init() { m_config.addConfigValue("general:hide_cursor", Hyprlang::INT{0}); m_config.addConfigValue("general:grace", Hyprlang::INT{0}); m_config.addConfigValue("general:no_fade_in", Hyprlang::INT{0}); + m_config.addConfigValue("general:input_empty_fade_timeout", Hyprlang::INT{1000}); m_config.addSpecialCategory("background", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true}); m_config.addSpecialConfigValue("background", "monitor", Hyprlang::STRING{""}); diff --git a/src/core/hyprlock.cpp b/src/core/hyprlock.cpp index b33ac7aa..731cd595 100644 --- a/src/core/hyprlock.cpp +++ b/src/core/hyprlock.cpp @@ -30,8 +30,10 @@ CHyprlock::CHyprlock(const std::string& wlDisplay) { if (!m_pXKBContext) Debug::log(ERR, "Failed to create xkb context"); - const auto GRACE = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:grace"); - m_tGraceEnds = **GRACE ? std::chrono::system_clock::now() + std::chrono::seconds(**GRACE) : std::chrono::system_clock::from_time_t(0); + const auto GRACE = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:grace"); + m_tGraceEnds = **GRACE ? std::chrono::system_clock::now() + std::chrono::seconds(**GRACE) : std::chrono::system_clock::from_time_t(0); + const auto FADETIMEOUT = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:input_empty_fade_timeout"); + m_iEmptyPasswordTimeoutMs = **FADETIMEOUT; } // wl_seat @@ -654,6 +656,14 @@ static const ext_session_lock_v1_listener sessionLockListener = { // end session_lock +static void passwordEmptyTimerCallback(std::shared_ptr self, void* data) { + g_pRenderer->onEmptyPasswordFade(); + + for (auto& o : g_pHyprlock->m_vOutputs) { + o->sessionLockSurface->render(); + } +} + void CHyprlock::onPasswordCheckTimer() { // check result if (m_sPasswordState.result->success) { @@ -669,6 +679,12 @@ void CHyprlock::onPasswordCheckTimer() { } m_sPasswordState.result.reset(); + + if (m_sPasswordState.emptyBufferTimer.get()) { + m_sPasswordState.emptyBufferTimer->cancel(); + m_sPasswordState.emptyBufferTimer.reset(); + } + m_sPasswordState.emptyBufferTimer = addTimer(std::chrono::milliseconds(m_iEmptyPasswordTimeoutMs), passwordEmptyTimerCallback, nullptr); } bool CHyprlock::passwordCheckWaiting() { @@ -730,6 +746,13 @@ void CHyprlock::onKey(uint32_t key, bool down) { m_sPasswordState.passBuffer += std::string{buf, len - 1}; } + if (m_sPasswordState.passBuffer.empty() && !m_sPasswordState.emptyBufferTimer.get()) + m_sPasswordState.emptyBufferTimer = addTimer(std::chrono::milliseconds(m_iEmptyPasswordTimeoutMs), passwordEmptyTimerCallback, nullptr); + else if (!m_sPasswordState.passBuffer.empty() && m_sPasswordState.emptyBufferTimer.get()) { + m_sPasswordState.emptyBufferTimer->cancel(); + m_sPasswordState.emptyBufferTimer.reset(); + } + for (auto& o : m_vOutputs) { o->sessionLockSurface->render(); } @@ -920,4 +943,4 @@ void CHyprlock::attemptRestoreOnDeath() { spawnSync("hyprctl keyword misc:allow_session_lock_restore true"); spawnAsync("sleep 2 && hyprlock & disown"); -} \ No newline at end of file +} diff --git a/src/core/hyprlock.hpp b/src/core/hyprlock.hpp index d21e3811..24bb2bcb 100644 --- a/src/core/hyprlock.hpp +++ b/src/core/hyprlock.hpp @@ -78,7 +78,8 @@ class CHyprlock { // std::chrono::system_clock::time_point m_tGraceEnds; - Vector2D m_vLastEnterCoords = {}; + Vector2D m_vLastEnterCoords = {}; + uint64_t m_iEmptyPasswordTimeoutMs = 0; std::vector> m_vOutputs; @@ -117,6 +118,7 @@ class CHyprlock { std::string passBuffer = ""; std::shared_ptr result; std::optional lastFailReason; + std::shared_ptr emptyBufferTimer; } m_sPasswordState; struct { diff --git a/src/renderer/Renderer.cpp b/src/renderer/Renderer.cpp index af8c042a..65f86db0 100644 --- a/src/renderer/Renderer.cpp +++ b/src/renderer/Renderer.cpp @@ -489,3 +489,11 @@ void CRenderer::popFb() { boundFBs.pop_back(); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, boundFBs.empty() ? 0 : boundFBs.back()); } + +void CRenderer::onEmptyPasswordFade() { + for (auto& [surf, w] : widgets) { + for (auto& widget : w) { + widget->onEmptyPasswordTimer(); + } + } +} \ No newline at end of file diff --git a/src/renderer/Renderer.hpp b/src/renderer/Renderer.hpp index 65997409..c558174d 100644 --- a/src/renderer/Renderer.hpp +++ b/src/renderer/Renderer.hpp @@ -34,6 +34,7 @@ class CRenderer { void renderRect(const CBox& box, const CColor& col, int rounding = 0); void renderTexture(const CBox& box, const CTexture& tex, float a = 1.0, int rounding = 0, std::optional tr = {}); void blurFB(const CFramebuffer& outfb, SBlurParams params); + void onEmptyPasswordFade(); std::unique_ptr asyncResourceGatherer; std::chrono::system_clock::time_point gatheredAt; diff --git a/src/renderer/widgets/IWidget.hpp b/src/renderer/widgets/IWidget.hpp index e81b53a0..c66d9e9b 100644 --- a/src/renderer/widgets/IWidget.hpp +++ b/src/renderer/widgets/IWidget.hpp @@ -11,6 +11,8 @@ class IWidget { virtual bool draw(const SRenderData& data) = 0; + virtual void onEmptyPasswordTimer(){}; + virtual Vector2D posFromHVAlign(const Vector2D& viewport, const Vector2D& size, const Vector2D& offset, const std::string& halign, const std::string& valign); struct SFormatResult { diff --git a/src/renderer/widgets/PasswordInputField.cpp b/src/renderer/widgets/PasswordInputField.cpp index fd09a7ed..cf8bafb7 100644 --- a/src/renderer/widgets/PasswordInputField.cpp +++ b/src/renderer/widgets/PasswordInputField.cpp @@ -37,6 +37,10 @@ CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::u } } +void CPasswordInputField::onEmptyPasswordTimer() { + fade.allowFadeOut = true; +} + void CPasswordInputField::updateFade() { const auto PASSLEN = g_pHyprlock->getPasswordBufferLen(); @@ -45,11 +49,12 @@ void CPasswordInputField::updateFade() { return; } - if (PASSLEN == 0 && fade.a != 0.0 && (!fade.animated || fade.appearing)) { - fade.a = 1.0; - fade.animated = true; - fade.appearing = false; - fade.start = std::chrono::system_clock::now(); + if (PASSLEN == 0 && fade.a != 0.0 && fade.allowFadeOut && (!fade.animated || fade.appearing)) { + fade.a = 1.0; + fade.animated = true; + fade.appearing = false; + fade.start = std::chrono::system_clock::now(); + fade.allowFadeOut = false; } else if (PASSLEN > 0 && fade.a != 1.0 && (!fade.animated || !fade.appearing)) { fade.a = 0.0; fade.animated = true; @@ -79,11 +84,6 @@ void CPasswordInputField::updateDots() { dots.lastFrame = std::chrono::system_clock::now(); } - if (PASSLEN == 0 && !placeholder.failID.empty()) { - dots.currentAmount = PASSLEN; - return; - } - const auto DELTA = std::clamp((int)std::chrono::duration_cast(std::chrono::system_clock::now() - dots.lastFrame).count(), 0, 20000); const float TOADD = DELTA / 1000000.0 * dots.speedPerSecond; @@ -216,7 +216,7 @@ bool CPasswordInputField::draw(const SRenderData& data) { forceReload = true; } - return dots.currentAmount != PASSLEN || data.opacity < 1.0 || forceReload; + return dots.currentAmount != PASSLEN || fade.animated || data.opacity < 1.0 || forceReload; } void CPasswordInputField::updateFailTex() { diff --git a/src/renderer/widgets/PasswordInputField.hpp b/src/renderer/widgets/PasswordInputField.hpp index 433e1147..6bce72dd 100644 --- a/src/renderer/widgets/PasswordInputField.hpp +++ b/src/renderer/widgets/PasswordInputField.hpp @@ -16,6 +16,7 @@ class CPasswordInputField : public IWidget { CPasswordInputField(const Vector2D& viewport, const std::unordered_map& props); virtual bool draw(const SRenderData& data); + virtual void onEmptyPasswordTimer(); private: void updateDots(); @@ -45,9 +46,10 @@ class CPasswordInputField : public IWidget { struct { std::chrono::system_clock::time_point start; - float a = 0; - bool appearing = true; - bool animated = false; + float a = 0; + bool appearing = true; + bool animated = false; + bool allowFadeOut = false; } fade; struct {