From 89ab1a01386281ab12f583888a873ca057829c37 Mon Sep 17 00:00:00 2001 From: Roland Winklmeier Date: Fri, 14 Sep 2018 17:28:39 +0200 Subject: [PATCH] Add alternative method to poll keyboard state on Windows It seems that Windows is frequently disconnecting the keyboard hook (eventually if swift was too busy processing messages) and caused all hotkeys to be unreliable. Instead use a polling mechanism. The previous method is kept in code for testing, but is disabled by compile time switch. --- src/blackinput/win/keyboardwindows.cpp | 50 +++++++++++++++++++++++--- src/blackinput/win/keyboardwindows.h | 9 +++-- 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/src/blackinput/win/keyboardwindows.cpp b/src/blackinput/win/keyboardwindows.cpp index aae609662..3048b41fd 100644 --- a/src/blackinput/win/keyboardwindows.cpp +++ b/src/blackinput/win/keyboardwindows.cpp @@ -68,7 +68,9 @@ namespace BlackInput CKeyboardWindows::CKeyboardWindows(QObject *parent) : IKeyboard(parent), m_keyboardHook(nullptr) - { } + { + connect(&m_pollTimer, &QTimer::timeout, this, &CKeyboardWindows::pollKeyboardState); + } CKeyboardWindows::~CKeyboardWindows() { @@ -77,9 +79,17 @@ namespace BlackInput bool CKeyboardWindows::init() { - Q_ASSERT_X(g_keyboardWindows == nullptr, "CKeyboardWindows::init", "Windows supports only one keyboard instance. Cannot initialize a second one!"); - g_keyboardWindows = this; - m_keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, CKeyboardWindows::keyboardProc, GetModuleHandle(nullptr), 0); + if (useWindowsHook) + { + Q_ASSERT_X(g_keyboardWindows == nullptr, "CKeyboardWindows::init", "Windows supports only one keyboard instance. Cannot initialize a second one!"); + g_keyboardWindows = this; + HMODULE module; + GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, reinterpret_cast(&CKeyboardWindows::keyboardProc), &module); + m_keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, CKeyboardWindows::keyboardProc, module, 0); + } + { + m_pollTimer.start(50); + } return true; } @@ -105,6 +115,36 @@ namespace BlackInput } } + void CKeyboardWindows::pollKeyboardState() + { + BlackMisc::Input::CHotkeyCombination oldCombination(m_keyCombination); + QList vkeys = keyMapping().keys(); + for (int vkey : vkeys) + { + if ((GetKeyState(vkey) & 0x8000) && !m_pressedKeys.contains(vkey)) + { + // key down + auto key = keyMapping().value(vkey); + if (key == Key_Unknown) { return; } + m_pressedKeys.push_back(vkey); + m_keyCombination.addKeyboardKey(CKeyboardKey(key)); + } + else if (!(GetKeyState(vkey) & 0x8000) && m_pressedKeys.contains(vkey)) + { + // key up + auto key = keyMapping().value(vkey); + if (key == Key_Unknown) { return; } + m_pressedKeys.removeAll(vkey); + m_keyCombination.removeKeyboardKey(CKeyboardKey(key)); + } + } + + if (oldCombination != m_keyCombination) + { + emit keyCombinationChanged(m_keyCombination); + } + } + LRESULT CALLBACK CKeyboardWindows::keyboardProc(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode == HC_ACTION) @@ -113,6 +153,6 @@ namespace BlackInput DWORD vkCode = keyboardEvent->vkCode; g_keyboardWindows->processKeyEvent(vkCode, wParam); } - return CallNextHookEx(g_keyboardWindows->keyboardHook(), nCode, wParam, lParam); + return CallNextHookEx(g_keyboardWindows->m_keyboardHook, nCode, wParam, lParam); } } diff --git a/src/blackinput/win/keyboardwindows.h b/src/blackinput/win/keyboardwindows.h index 0cf940509..50f715f89 100644 --- a/src/blackinput/win/keyboardwindows.h +++ b/src/blackinput/win/keyboardwindows.h @@ -19,6 +19,7 @@ #include "blackmisc/input/keyboardkey.h" #include "blackmisc/input/keyboardkeylist.h" #include +#include #ifndef NOMINMAX #define NOMINMAX #endif @@ -41,9 +42,6 @@ namespace BlackInput //! Destructor virtual ~CKeyboardWindows() override; - //! Keyboard hook handle - HHOOK keyboardHook() const { return m_keyboardHook; } - protected: //! \copydoc IKeyboard::init() virtual bool init() override; @@ -57,12 +55,17 @@ namespace BlackInput void addKey(WPARAM vkcode); void removeKey(WPARAM vkcode); void processKeyEvent(DWORD vkCode, WPARAM event); + void pollKeyboardState(); + + const bool useWindowsHook = false; //! Keyboard hook procedure static LRESULT CALLBACK keyboardProc(int nCode, WPARAM wParam, LPARAM lParam); BlackMisc::Input::CHotkeyCombination m_keyCombination; //!< Set of virtual keys pressed in the last cycle HHOOK m_keyboardHook; //!< Keyboard hook handle + QTimer m_pollTimer; + QVector m_pressedKeys; }; }