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.
This commit is contained in:
Roland Winklmeier
2018-09-14 17:28:39 +02:00
committed by Klaus Basan
parent 324e16121e
commit 89ab1a0138
2 changed files with 51 additions and 8 deletions

View File

@@ -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<LPCTSTR>(&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<int> 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);
}
}

View File

@@ -19,6 +19,7 @@
#include "blackmisc/input/keyboardkey.h"
#include "blackmisc/input/keyboardkeylist.h"
#include <QHash>
#include <QTimer>
#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<int> m_pressedKeys;
};
}