From 141779be1d7ce5efcba013c5a9609c9397f7516c Mon Sep 17 00:00:00 2001 From: barrelltitor Date: Sat, 8 Aug 2026 21:29:12 +0300 Subject: [PATCH] fix: resync Windows keys from low-level hook state --- src/lib/platform/MSWindowsHook.cpp | 31 +++++++++++++++++++++----- src/lib/platform/MSWindowsHook.h | 3 +++ src/lib/platform/MSWindowsKeyState.cpp | 4 +++- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/lib/platform/MSWindowsHook.cpp b/src/lib/platform/MSWindowsHook.cpp index 9c849847d..05a7b82ea 100644 --- a/src/lib/platform/MSWindowsHook.cpp +++ b/src/lib/platform/MSWindowsHook.cpp @@ -10,6 +10,9 @@ #include "base/Log.h" #include "deskflow/ScreenException.h" +#include +#include + #ifndef WM_MOUSEHWHEEL #define WM_MOUSEHWHEEL 0x020E #endif @@ -34,6 +37,8 @@ static WPARAM g_deadRelease = 0; static LPARAM g_deadLParam = 0; static BYTE g_deadKeyState[256] = {0}; static BYTE g_keyState[256] = {0}; +static bool g_keyStateValid = false; +static std::mutex g_keyStateMutex; static DWORD g_hookThread = 0; static bool g_fakeServerInput = false; static BOOL g_isPrimary = TRUE; @@ -142,6 +147,15 @@ void MSWindowsHook::setMode(EHookMode mode) g_mode = mode; } +bool MSWindowsHook::getPhysicalKeyState(BYTE keys[256]) +{ + std::lock_guard lock(g_keyStateMutex); + if (g_keyStateValid) { + std::memcpy(keys, g_keyState, sizeof(g_keyState)); + } + return g_keyStateValid; +} + static void keyboardGetState(BYTE keys[256], DWORD vkCode, bool kf_up) { // we have to use GetAsyncKeyState() rather than GetKeyState() because @@ -152,6 +166,8 @@ static void keyboardGetState(BYTE keys[256], DWORD vkCode, bool kf_up) return; } + std::lock_guard lock(g_keyStateMutex); + // Keep track of key state on our own in case GetAsyncKeyState() fails g_keyState[vkCode] = kf_up ? 0 : 0x80; g_keyState[VK_SHIFT] = g_keyState[VK_LSHIFT] | g_keyState[VK_RSHIFT]; @@ -179,6 +195,7 @@ static void keyboardGetState(BYTE keys[256], DWORD vkCode, bool kf_up) key = GetKeyState(VK_CAPITAL); keys[VK_CAPITAL] = (BYTE)(((key < 0) ? 0x80 : 0) | (key & 1)); + g_keyStateValid = true; } static WPARAM makeKeyMsg(UINT virtKey, WCHAR wc, bool noAltGr) @@ -240,17 +257,17 @@ static bool keyboardHookHandler(WPARAM wParam, LPARAM lParam) // tell server about event PostThreadMessage(g_threadID, DESKFLOW_MSG_DEBUG, wParam, lParam); - // ignore dead key release + // we need the keyboard state for ToAscii() + BYTE keys[256]; + keyboardGetState(keys, vkCode, kf_up); + + // Update the physical state before ignoring a dead key release so it cannot remain down in the resync snapshot. if ((g_deadVirtKey == wParam || g_deadRelease == wParam) && (lParam & 0x80000000u) != 0) { g_deadRelease = 0; PostThreadMessage(g_threadID, DESKFLOW_MSG_DEBUG, wParam | 0x04000000, lParam); return false; } - // we need the keyboard state for ToAscii() - BYTE keys[256]; - keyboardGetState(keys, vkCode, kf_up); - // ToAscii() maps ctrl+letter to the corresponding control code // and ctrl+backspace to delete. we don't want those translations // so clear the control modifier state. however, if we want to @@ -671,6 +688,10 @@ int MSWindowsHook::uninstall() g_getMessage = nullptr; } + std::lock_guard lock(g_keyStateMutex); + std::memset(g_keyState, 0, sizeof(g_keyState)); + g_keyStateValid = false; + return 1; } diff --git a/src/lib/platform/MSWindowsHook.h b/src/lib/platform/MSWindowsHook.h index e767d8790..a96a7295c 100644 --- a/src/lib/platform/MSWindowsHook.h +++ b/src/lib/platform/MSWindowsHook.h @@ -62,6 +62,9 @@ public: void setMode(EHookMode mode); + //! Copy the hook's physical key state; false until it has observed an event. + static bool getPhysicalKeyState(BYTE keys[256]); + static EHookResult install(); static int uninstall(); diff --git a/src/lib/platform/MSWindowsKeyState.cpp b/src/lib/platform/MSWindowsKeyState.cpp index b42ca8547..efaa54708 100644 --- a/src/lib/platform/MSWindowsKeyState.cpp +++ b/src/lib/platform/MSWindowsKeyState.cpp @@ -13,6 +13,7 @@ #include "common/Constants.h" #include "platform/MSWindowsDesks.h" #include "platform/MSWindowsHandle.h" +#include "platform/MSWindowsHook.h" // extended mouse buttons #if !defined(VK_XBUTTON1) @@ -844,7 +845,8 @@ int32_t MSWindowsKeyState::pollActiveGroup() const void MSWindowsKeyState::pollPressedKeys(KeyButtonSet &pressedKeys) const { BYTE keyState[256]; - if (!GetKeyboardState(keyState)) { + // The caller's GetKeyboardState queue can lag the low-level hook during a screen switch. + if (!MSWindowsHook::getPhysicalKeyState(keyState) && !GetKeyboardState(keyState)) { LOG_WARN("keyboard state is unexpected"); LOG_DEBUG("function 'GetKeyboardState' returned false on 'pollPressedKeys'"); return;