From 316c37746df323df563dc1acd87c68801df833d8 Mon Sep 17 00:00:00 2001 From: Maximilian Seidler <78690852+PaideiaDilemma@users.noreply.github.com> Date: Tue, 14 May 2024 17:11:45 +0200 Subject: [PATCH] core: add key repeat for backspace and make del clear the input (#277) * core: add support for repeating backspace when held * core: move keysym handling to a seperate function in order to reuse it for key repeat * core: get keyboard repeat info from wayland * core: defaults for keyboard repeat and delay --- src/core/hyprlock.cpp | 110 +++++++++++++++++++++++++++++------------- src/core/hyprlock.hpp | 8 +++ 2 files changed, 84 insertions(+), 34 deletions(-) diff --git a/src/core/hyprlock.cpp b/src/core/hyprlock.cpp index 61485ec..ba157c5 100644 --- a/src/core/hyprlock.cpp +++ b/src/core/hyprlock.cpp @@ -695,7 +695,8 @@ static void handleKeyboardModifiers(void* data, wl_keyboard* wl_keyboard, uint s } static void handleRepeatInfo(void* data, struct wl_keyboard* wl_keyboard, int32_t rate, int32_t delay) { - ; + g_pHyprlock->m_iKeebRepeatRate = rate; + g_pHyprlock->m_iKeebRepeatDelay = delay; } inline const wl_keyboard_listener keyboardListener = { @@ -793,6 +794,35 @@ void CHyprlock::renderOutput(const std::string& stringPort) { PMONITOR->sessionLockSurface->render(); } +void CHyprlock::startKeyRepeat(xkb_keysym_t sym) { + if (m_pKeyRepeatTimer) { + m_pKeyRepeatTimer->cancel(); + m_pKeyRepeatTimer.reset(); + } + + if (m_iKeebRepeatDelay <= 0) + return; + + m_pKeyRepeatTimer = addTimer( + std::chrono::milliseconds(m_iKeebRepeatDelay), [sym](std::shared_ptr self, void* data) { g_pHyprlock->repeatKey(sym); }, nullptr); +} + +void CHyprlock::repeatKey(xkb_keysym_t sym) { + if (m_iKeebRepeatRate <= 0) + return; + + handleKeySym(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 self, void* data) { g_pHyprlock->repeatKey(sym); }, nullptr); + + for (auto& o : m_vOutputs) { + o->sessionLockSurface->render(); + } +} + void CHyprlock::onKey(uint32_t key, bool down) { if (m_bFadeStarted) return; @@ -812,8 +842,13 @@ void CHyprlock::onKey(uint32_t key, bool down) { if (down) m_vPressedKeys.push_back(key); - else + else { std::erase(m_vPressedKeys, key); + if (m_pKeyRepeatTimer) { + m_pKeyRepeatTimer->cancel(); + m_pKeyRepeatTimer.reset(); + } + } if (g_pAuth->checkWaiting()) { for (auto& o : m_vOutputs) { @@ -829,38 +864,9 @@ void CHyprlock::onKey(uint32_t key, bool down) { m_bNumLock = xkb_state_mod_name_is_active(g_pHyprlock->m_pXKBState, XKB_MOD_NAME_NUM, XKB_STATE_MODS_LOCKED); m_bCtrl = xkb_state_mod_name_is_active(m_pXKBState, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE); - if (SYM == XKB_KEY_Escape || (m_bCtrl && (SYM == XKB_KEY_u || SYM == XKB_KEY_BackSpace))) { - Debug::log(LOG, "Clearing password buffer"); - - m_sPasswordState.passBuffer = ""; - } else if (SYM == XKB_KEY_Return || SYM == XKB_KEY_KP_Enter) { - Debug::log(LOG, "Authenticating"); - - static auto* const PIGNOREEMPTY = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:ignore_empty_input"); - - if (m_sPasswordState.passBuffer.empty() && **PIGNOREEMPTY) { - Debug::log(LOG, "Ignoring empty input"); - return; - } - - g_pAuth->submitInput(m_sPasswordState.passBuffer); - } else if (SYM == XKB_KEY_BackSpace) { - if (m_sPasswordState.passBuffer.length() > 0) { - // handle utf-8 - while ((m_sPasswordState.passBuffer.back() & 0xc0) == 0x80) - m_sPasswordState.passBuffer.pop_back(); - m_sPasswordState.passBuffer = m_sPasswordState.passBuffer.substr(0, m_sPasswordState.passBuffer.length() - 1); - } - } else if (SYM == XKB_KEY_Caps_Lock) { - m_bCapsLock = !m_bCapsLock; - } else if (SYM == XKB_KEY_Num_Lock) { - m_bNumLock = !m_bNumLock; - } else { - char buf[16] = {0}; - int len = xkb_keysym_to_utf8(SYM, buf, 16); - if (len > 1) - m_sPasswordState.passBuffer += std::string{buf, len - 1}; - } + handleKeySym(SYM); + if (SYM == XKB_KEY_BackSpace || SYM == XKB_KEY_Delete) // keys allowed to repeat + startKeyRepeat(SYM); } for (auto& o : m_vOutputs) { @@ -868,6 +874,42 @@ void CHyprlock::onKey(uint32_t key, bool down) { } } +void CHyprlock::handleKeySym(xkb_keysym_t sym) { + const auto SYM = sym; + if (SYM == XKB_KEY_Escape || (m_bCtrl && (SYM == XKB_KEY_u || SYM == XKB_KEY_BackSpace))) { + Debug::log(LOG, "Clearing password buffer"); + + m_sPasswordState.passBuffer = ""; + } else if (SYM == XKB_KEY_Return || SYM == XKB_KEY_KP_Enter) { + Debug::log(LOG, "Authenticating"); + + static auto* const PIGNOREEMPTY = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:ignore_empty_input"); + + if (m_sPasswordState.passBuffer.empty() && **PIGNOREEMPTY) { + Debug::log(LOG, "Ignoring empty input"); + return; + } + + g_pAuth->submitInput(m_sPasswordState.passBuffer); + } else if (SYM == XKB_KEY_BackSpace || SYM == XKB_KEY_Delete) { + if (m_sPasswordState.passBuffer.length() > 0) { + // handle utf-8 + while ((m_sPasswordState.passBuffer.back() & 0xc0) == 0x80) + m_sPasswordState.passBuffer.pop_back(); + m_sPasswordState.passBuffer = m_sPasswordState.passBuffer.substr(0, m_sPasswordState.passBuffer.length() - 1); + } + } else if (SYM == XKB_KEY_Caps_Lock) { + m_bCapsLock = !m_bCapsLock; + } else if (SYM == XKB_KEY_Num_Lock) { + m_bNumLock = !m_bNumLock; + } else { + char buf[16] = {0}; + int len = xkb_keysym_to_utf8(SYM, buf, 16); + if (len > 1) + m_sPasswordState.passBuffer += std::string{buf, len - 1}; + } +} + void CHyprlock::acquireSessionLock() { Debug::log(LOG, "Locking session"); m_sLockState.lock = ext_session_lock_manager_v1_lock(m_sWaylandState.sessionLock); diff --git a/src/core/hyprlock.hpp b/src/core/hyprlock.hpp index a24df22..192c286 100644 --- a/src/core/hyprlock.hpp +++ b/src/core/hyprlock.hpp @@ -54,6 +54,9 @@ class CHyprlock { std::string spawnSync(const std::string& cmd); void onKey(uint32_t key, bool down); + void handleKeySym(xkb_keysym_t sym); + void startKeyRepeat(xkb_keysym_t sym); + void repeatKey(xkb_keysym_t sym); void onPasswordCheckTimer(); void clearPasswordBuffer(); bool passwordCheckWaiting(); @@ -82,6 +85,9 @@ class CHyprlock { xkb_keymap* m_pXKBKeymap = nullptr; xkb_state* m_pXKBState = nullptr; + int32_t m_iKeebRepeatRate = 25; + int32_t m_iKeebRepeatDelay = 600; + xkb_layout_index_t m_uiActiveLayout = 0; bool m_bTerminate = false; @@ -97,6 +103,8 @@ class CHyprlock { std::chrono::system_clock::time_point m_tFadeEnds; Vector2D m_vLastEnterCoords = {}; + std::shared_ptr m_pKeyRepeatTimer = nullptr; + std::vector> m_vOutputs; std::vector> getTimers();