fix: resync Windows keys from low-level hook state

This commit is contained in:
barrelltitor 2026-08-08 21:29:12 +03:00 committed by Nick Bolton
parent 079dee7682
commit 141779be1d
3 changed files with 32 additions and 6 deletions

View file

@ -10,6 +10,9 @@
#include "base/Log.h" #include "base/Log.h"
#include "deskflow/ScreenException.h" #include "deskflow/ScreenException.h"
#include <cstring>
#include <mutex>
#ifndef WM_MOUSEHWHEEL #ifndef WM_MOUSEHWHEEL
#define WM_MOUSEHWHEEL 0x020E #define WM_MOUSEHWHEEL 0x020E
#endif #endif
@ -34,6 +37,8 @@ static WPARAM g_deadRelease = 0;
static LPARAM g_deadLParam = 0; static LPARAM g_deadLParam = 0;
static BYTE g_deadKeyState[256] = {0}; static BYTE g_deadKeyState[256] = {0};
static BYTE g_keyState[256] = {0}; static BYTE g_keyState[256] = {0};
static bool g_keyStateValid = false;
static std::mutex g_keyStateMutex;
static DWORD g_hookThread = 0; static DWORD g_hookThread = 0;
static bool g_fakeServerInput = false; static bool g_fakeServerInput = false;
static BOOL g_isPrimary = TRUE; static BOOL g_isPrimary = TRUE;
@ -142,6 +147,15 @@ void MSWindowsHook::setMode(EHookMode mode)
g_mode = mode; g_mode = mode;
} }
bool MSWindowsHook::getPhysicalKeyState(BYTE keys[256])
{
std::lock_guard<std::mutex> 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) static void keyboardGetState(BYTE keys[256], DWORD vkCode, bool kf_up)
{ {
// we have to use GetAsyncKeyState() rather than GetKeyState() because // 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; return;
} }
std::lock_guard<std::mutex> lock(g_keyStateMutex);
// Keep track of key state on our own in case GetAsyncKeyState() fails // Keep track of key state on our own in case GetAsyncKeyState() fails
g_keyState[vkCode] = kf_up ? 0 : 0x80; g_keyState[vkCode] = kf_up ? 0 : 0x80;
g_keyState[VK_SHIFT] = g_keyState[VK_LSHIFT] | g_keyState[VK_RSHIFT]; 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); key = GetKeyState(VK_CAPITAL);
keys[VK_CAPITAL] = (BYTE)(((key < 0) ? 0x80 : 0) | (key & 1)); keys[VK_CAPITAL] = (BYTE)(((key < 0) ? 0x80 : 0) | (key & 1));
g_keyStateValid = true;
} }
static WPARAM makeKeyMsg(UINT virtKey, WCHAR wc, bool noAltGr) static WPARAM makeKeyMsg(UINT virtKey, WCHAR wc, bool noAltGr)
@ -240,17 +257,17 @@ static bool keyboardHookHandler(WPARAM wParam, LPARAM lParam)
// tell server about event // tell server about event
PostThreadMessage(g_threadID, DESKFLOW_MSG_DEBUG, wParam, lParam); 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) { if ((g_deadVirtKey == wParam || g_deadRelease == wParam) && (lParam & 0x80000000u) != 0) {
g_deadRelease = 0; g_deadRelease = 0;
PostThreadMessage(g_threadID, DESKFLOW_MSG_DEBUG, wParam | 0x04000000, lParam); PostThreadMessage(g_threadID, DESKFLOW_MSG_DEBUG, wParam | 0x04000000, lParam);
return false; 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 // ToAscii() maps ctrl+letter to the corresponding control code
// and ctrl+backspace to delete. we don't want those translations // and ctrl+backspace to delete. we don't want those translations
// so clear the control modifier state. however, if we want to // so clear the control modifier state. however, if we want to
@ -671,6 +688,10 @@ int MSWindowsHook::uninstall()
g_getMessage = nullptr; g_getMessage = nullptr;
} }
std::lock_guard<std::mutex> lock(g_keyStateMutex);
std::memset(g_keyState, 0, sizeof(g_keyState));
g_keyStateValid = false;
return 1; return 1;
} }

View file

@ -62,6 +62,9 @@ public:
void setMode(EHookMode mode); 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 EHookResult install();
static int uninstall(); static int uninstall();

View file

@ -13,6 +13,7 @@
#include "common/Constants.h" #include "common/Constants.h"
#include "platform/MSWindowsDesks.h" #include "platform/MSWindowsDesks.h"
#include "platform/MSWindowsHandle.h" #include "platform/MSWindowsHandle.h"
#include "platform/MSWindowsHook.h"
// extended mouse buttons // extended mouse buttons
#if !defined(VK_XBUTTON1) #if !defined(VK_XBUTTON1)
@ -844,7 +845,8 @@ int32_t MSWindowsKeyState::pollActiveGroup() const
void MSWindowsKeyState::pollPressedKeys(KeyButtonSet &pressedKeys) const void MSWindowsKeyState::pollPressedKeys(KeyButtonSet &pressedKeys) const
{ {
BYTE keyState[256]; 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_WARN("keyboard state is unexpected");
LOG_DEBUG("function 'GetKeyboardState' returned false on 'pollPressedKeys'"); LOG_DEBUG("function 'GetKeyboardState' returned false on 'pollPressedKeys'");
return; return;