Reputation: 61
I've got a C++ (MFC) app that needs to check the keystate on a timer. If the user is holding down a key, we delay processing of some code.
Here's the check for the keydown
:
if (!GetKeyboardState(keyState))
{
s_executeDeferredResult = e_executeDeferredButtonCheckFailed;
return;
}
s_executeDeferredStuckKeys.clear();
for (int index=0; index<arrsize(keyState); ++index)
{
if (keyState[index] & 0x80)
{
s_executeDeferredStuckKeys.insert(index);
}
}
if (!s_executeDeferredStuckKeys.empty())
{
s_executeDeferredResult = e_executeDeferredButtonsActive;
return;
}
But, there are some key combos that get stuck:
GetKeyboardState
will now report that VK_UP
is pressed.
The events that happen are (corresponding to the actions above).
<None>
WM_KEYDOWN
, VK_SHIFT
WM_KEYUP
, VK_SHIFT
WM_KEYDOWN
, VK_UP
WM_KEYDOWN
, VK_SHIFT
WM_KEYUP
, VK_SHIFT
WM_KEYUP
, VK_NUMPAD8
So, Windows doesn't recognize that the Up key came up, and now GetKeyboardState
is broken.
Is there any good way to check the real state of the key? GetAsyncKeyState
and GetKeyState
both report that the key is down as well.
Upvotes: 4
Views: 1379
Reputation: 61
Solved it.
I hooked into the keyboard events in InitInstance and am tracking the ups and downs by scan code (a map with the scan code as key and the virtual keys as the value).
m_keyboardHook = SetWindowsHookEx(WH_KEYBOARD, &KeyboardHook, NULL, GetCurrentThreadId());
static LRESULT CALLBACK KeyboardHook(
__in int nCode,
__in WPARAM wParam,
__in LPARAM lParam
)
{
// According to the docs, we're not allowed to do any "further processing" if nCode is < 0.
// According to the docs, we should process messages if we get code HC_ACTION.
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms644984(v=vs.85).aspx
// It doesn't specify what to do if another code comes in, so we will just ignore any other codes.
if (nCode == HC_ACTION)
{
uint8 scanCode = (uint8)((lParam & 0x00FF0000) >> 16);
uint8 virtKey = (uint8)wParam;
if (lParam & 0x80000000) // key up
KeyState::SetKeyUp(scanCode);
else
KeyState::SetKeyDown(scanCode, virtKey);
}
// We must call the next hook in the chain, according to http://msdn.microsoft.com/en-us/library/windows/desktop/ms644975%28v=vs.85%29.aspx
// First param is ignored, according to http://msdn.microsoft.com/en-us/library/windows/desktop/ms644974%28v=vs.85%29.aspx )
return CallNextHookEx(0, nCode, wParam, lParam);
}
So, my defer checking becomes:
// Similarly, don't process deferred events if there are keys or mouse buttons which are currently down.
s_executeDeferredStuckKeys.clear();
if (KeyState::AnyKeysDown(s_excludeKeys, arrsize(s_excludeKeys)))
{
s_executeDeferredResult = e_executeDeferredButtonsActive;
KeyState::GetDownKeys(s_executeDeferredStuckKeys);
return;
}
Upvotes: 2