diff --git a/src/app/ControllerManager.cpp b/src/app/ControllerManager.cpp index 02dfddf..ab85151 100644 --- a/src/app/ControllerManager.cpp +++ b/src/app/ControllerManager.cpp @@ -42,6 +42,12 @@ struct ControllerManager::Slot { // again on release, so rebinding a paddle mid-hold still releases cleanly. BackButtonBinding paddleHeld[4]; + // Auto-repeat for held key bindings. When the next repeat is due, and the + // gap to use after that — both captured at press time from the user's + // keyboard settings, so the rate cannot shift mid-hold. + std::chrono::steady_clock::time_point paddleRepeatAt[4]{}; + std::chrono::milliseconds paddleRepeatGap[4]{}; + // Haptic edge-detection state for touch (movement ticks). bool hapticWasRightTouching = false; bool hapticWasLeftTouching = false; @@ -823,7 +829,12 @@ void ControllerManager::ReadLoop(Slot* slot) { const auto& e = edges[i]; if (e.cur == e.prev) continue; if (e.cur) { - if (SendPaddleInput(e.binding, true)) slot->paddleHeld[i] = e.binding; + if (SendPaddleInput(e.binding, true)) { + slot->paddleHeld[i] = e.binding; + slot->paddleRepeatGap[i] = KeyRepeatInterval(); + slot->paddleRepeatAt[i] = + std::chrono::steady_clock::now() + KeyRepeatDelay(); + } } else { // Release what the press sent, not what the binding says // now — the two differ if the user rebound mid-hold. @@ -833,6 +844,22 @@ void ControllerManager::ReadLoop(Slot* slot) { } } + // Auto-repeat held keys. A physical keyboard repeats because its own + // firmware keeps sending the key, not because Windows tracks the key as + // down, so injected input has to reproduce that itself. Keys only — + // mice do not repeat when held, and gamepad bindings are held state in + // the pad report, where a repeat would be meaningless. + for (size_t i = 0; i < std::size(slot->paddleHeld); ++i) { + const auto& held = slot->paddleHeld[i]; + if (held.kind != BackButtonBinding::Kind::Key) continue; + + const auto now = std::chrono::steady_clock::now(); + if (now < slot->paddleRepeatAt[i]) continue; + + SendKeyInput(held.code, true); + slot->paddleRepeatAt[i] = now + slot->paddleRepeatGap[i]; + } + // Button capture for the remap window (press-to-bind). // Only fires on a rising edge to avoid repeat-triggering on hold. if (m_capturing.load() && hasPrev) { diff --git a/src/app/KeyInput.cpp b/src/app/KeyInput.cpp index f322b27..80fd15f 100644 --- a/src/app/KeyInput.cpp +++ b/src/app/KeyInput.cpp @@ -151,3 +151,21 @@ void SendKeyInput(uint16_t vk, bool down) { if (!down) input.ki.dwFlags |= KEYEVENTF_KEYUP; SendInput(1, &input, sizeof(INPUT)); } + +std::chrono::milliseconds KeyRepeatDelay() { + // 0-3, meaning roughly 250ms to 1000ms in 250ms steps. + DWORD setting = 1; + if (!SystemParametersInfoW(SPI_GETKEYBOARDDELAY, 0, &setting, 0) || setting > 3) + setting = 1; + return std::chrono::milliseconds(250 * (static_cast(setting) + 1)); +} + +std::chrono::milliseconds KeyRepeatInterval() { + // 0-31, meaning roughly 2.5 to 30 repeats per second. Windows documents only + // the endpoints, so interpolate the rate between them. + DWORD setting = 31; + if (!SystemParametersInfoW(SPI_GETKEYBOARDSPEED, 0, &setting, 0) || setting > 31) + setting = 31; + const double perSecond = 2.5 + (30.0 - 2.5) * (static_cast(setting) / 31.0); + return std::chrono::milliseconds(static_cast(1000.0 / perSecond + 0.5)); +} diff --git a/src/app/KeyInput.h b/src/app/KeyInput.h index 20eadca..c840685 100644 --- a/src/app/KeyInput.h +++ b/src/app/KeyInput.h @@ -1,4 +1,5 @@ #pragma once +#include #include #include @@ -22,4 +23,15 @@ std::wstring KeyDisplayName(uint16_t vk); // Presses or releases a key. Sends a scan code rather than a virtual key, and // flags the extended keys, because software that reads scan codes directly // (games, in particular) sees nothing useful otherwise. +// +// Holding a key down does not repeat on its own: auto-repeat comes from the +// keyboard's own typematic behaviour, not from Windows noticing a key is held, +// and injected input has no firmware behind it. A caller that wants a held key +// to repeat has to re-send the press itself, paced by the two values below. void SendKeyInput(uint16_t vk, bool down); + +// The user's Windows keyboard settings: how long a key must be held before it +// starts repeating, and the gap between repeats after that. Read fresh rather +// than cached at startup so a change in Control Panel is picked up. +std::chrono::milliseconds KeyRepeatDelay(); +std::chrono::milliseconds KeyRepeatInterval();