Keyboard Input & the Win32 message loop
Use char c = MapVirtualKey(param,MAPVK_VK_TO_CHAR);
to convert virtual key codes to char, and process WM_KEYUP and WM_KEYDOWN and their wParams.
if (PeekMessage (&mssg, hwnd, 0, 0, PM_REMOVE))
{
switch (mssg.message)
{
case WM_QUIT:
PostQuitMessage (0);
notdone = false;
quit = true;
break;
case WM_KEYDOWN:
WPARAM param = mssg.wParam;
char c = MapVirtualKey (param, MAPVK_VK_TO_CHAR);
this->p->Input ()->Keyboard ()->Listeners ()->OnKeyDown (c);
break;
case WM_KEYUP:
WPARAM param = mssg.wParam;
char c = MapVirtualKey (param, MAPVK_VK_TO_CHAR);
this->p->Input ()->Keyboard ()->Listeners ()->OnKeyUp (c);
break;
}
// dispatch the message
TranslateMessage (&mssg);
DispatchMessage (&mssg);
}
Keyboard Input in Win32 Message Loop
You never set the elements when a key is pressed.
You are also using the keyboardState
array as a local variable. You must make it global or static, because each time the MsgProc
function is called the keyboardState
is allocated on the stack, and there is no guarantee of its contents, which may have been modified by previous function calls using the same memory space.
In your code basically each element of keyboardState
has 255/256 chance of being non-zero, and you are detecting that instead of real key presses.
If you are not accessing keyboard states from outside, don't bother with the array - just use the wParam
argument of the MsgProc
function:
case WM_KEYDOWN:
if (wParam == DIK_W) {
OutputDebugStringW(L"W Button Pressed\n");
}
if (wParam == DIK_A) {
OutputDebugStringW(L"A Button Pressed\n");
}
if (wParam == DIK_S) {
OutputDebugStringW(L"S Button Pressed\n");
}
if (wParam == DIK_D) {
OutputDebugStringW(L"D Button Pressed\n");
}
return 0;
(Assuming your DIK_ key values are the same as WinAPI's VK_ key codes)
Cannot exit message loop from thread using Windows API and C++
I think this is your problem:
while (WaitForSingleObject(ghStopEvent, 1) == WAIT_TIMEOUT)
{
// Retrieve the current messaged from message queue.
GetMessage(&msg, NULL, 0, 0);
TranslateMessage(&msg);
DispatchMessage(&msg);
}
The reason is that currently your loop can get stuck on the GetMessage()
step forever and never again look at the manual-reset event.
The fix is simply to replace the combination of WaitForSingleObject
+ GetMessage
with MsgWaitForMultipleObjects
+ PeekMessage
.
The reason you've made this mistake is that you didn't know GetMessage
only returns posted messages to the message loop. If it finds a sent message, it calls the handler from inside GetMessage
, and continues looking for posted message. Since you haven't created any windows that can receive messages, and you aren't calling PostThreadMessage
1, GetMessage
never returns.
while (MsgWaitForMultipleObjects(1, &ghStopEvent, FALSE, INFINITE, QS_ALLINPUT) > WAIT_OBJECT_0) {
// check if there's a posted message
// sent messages will be processed internally by PeekMessage and return false
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
1 You've got logic to post WM_QUIT
but it is conditioned on receiving WM_DESTROY
in a low-level keyboard hook, and WM_DESTROY
is not a keyboard message. Some hook types could see a WM_DESTROY
but WH_KEYBOARD_LL
can't.
Should I use DirectInput or Windows message loop?
Since you pretty much have to run a message pump in order to have a window, you might as well use that pump to handle keyboard and mouse input as well. It's entirely up to your pump whether you hand keyboard events on to a child window, you can handle them in the pump if you prefer.
Your typical message pump looks like this:
while (GetMessage(&msg, NULL, 0, 0))
{
if (WM_QUIT == msg.message)
break;
TranslateMessage(&msg); // post a WM_CHAR message if this is a WM_KEYDOWN
DispatchMessage(&msg); // this sends messages to the msg.hwnd
}
For a game, your pump might look more like this
while (true)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD))
{
bool fHandled = false;
if (msg.message >= WM_MOUSEFIRST && msg.message <= WM_MOUSELAST)
fHandled = MyHandleMouseEvent(&msg);
else if (msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST)
fHandled = MyHandleKeyEvent(&msg);
else if (WM_QUIT == msg.message)
break;
if ( ! fHandled)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
{
// if there are no more messages to handle right now, do some
// game slice processing.
//
}
}
Of course, your actual pump will likely be even more complex than that, possibly with a MsgWaitForMultipleObjects
so that you can wake periodically even if there a no messages to process, but immediatelly when there are messages.
Keyboard messages from child controls
The comments above are correct. The button with focus should be getting the key messages. But buttons don't (by themselves) respond to Enter--they respond to Space. It sounds like what you're missing is the typical dialog keyboard navigation, like Tab key moving the focus and Enter activating the "default" button.
If you've got a typical Windows message pump, and you want the keyboard behavior normally associated with dialogs, then you need to use the IsDialogMessage API in your message loop. This means your window is essentially a "modeless dialog".
Related Topics
How to Draw Text Using Only Opengl Methods
Why Do Some People Use Swap for Move Assignments
How to Make Reading from 'Std::Cin' Timeout After a Particular Amount of Time
.C VS .Cc VS. .Cpp VS .Hpp VS .H VS .Cxx
Unsigned and Signed Comparison
Compile a Dll in C/C++, Then Call It from Another Program
Std::String Length() and Size() Member Functions
How to Specify Setprecision Rounding
Visual Studio Code Formatting for "{ }"
How to Navigate Through a Vector Using Iterators? (C++)
Scope VS Life of Variable in C
What Does String::Npos Mean in This Code
Inlining Failed in Call to Always_Inline '_M256D _Mm256_Broadcast_Sd(Const Double*)'
Constexpr Initializing Static Member Using Static Function
How Do Compilers Treat Variable Length Arrays
Do Class Functions/Variables Have to Be Declared Before Being Used