006 - How to Process Keyboard Input
Table of Contents
To handle the keyboard input we need to use the WPARAM
and LPARAM
parameters from process message queue function (or window procecure callback).
we also need to handle the WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN and WM_SYSKEYUP cases.
Handle the parameters
The documentation says the 31th-bit is equivalent to transition_state.
So, when the key is down, that bit will be 0. When key is a transition to up, the value is 1.
transition_state now is (KEYDOWN) = 0 transition_state now is (KEYUP) = 1
The 30th-bit is equivalent to previous_state.
So, when the previous state of WM_KEYDOWN was down, the value is 1. If the previous state of WM_KEYUP, the value is 1 as well.
previous_state was (KEYDOWN) = 1 previous_state was (KEYUP) = 1
int transition_state = (1 << 31) bool is_down = (flag & transition_state) == 0 bool was_down = (flag & previous_state) != 0
When I pressed the key, the values is:
is_down = 1 was_down = 0
When I hold the key, the values is:
is_down = 1 was_down = 1
When I release the key, the values is:
is_down = 0 was_down = 1
When I idle the key, the value is:
is_down = 0 was_down = 0
Which means, to handle only the transition (up-to-down and down-to-up), we need to process if is_down
is different to was_down
.
bool is_down = (state_flag & (1 << 31)) == 0; bool was_down = (state_flag & (1 << 30)) != 0; if (was_down != is_down) { switch (vk_code) { case 'W': { OutputDebugStringA("W changed"); } break; } OutputDebugString("\n"); }
Warning: Compare bits
Another way to compare these bitwise is verify whether the & operator
result had a match.
Let's see:
int transition_state = (1 << 31); bool is_down = (state_flag & transition_state) != transition_state;
But, there is a catch!
The previous code compares the result of & operator
which was int64 to the transition_state
, which was int32.
So, as result, the program will compare 0x80000000 = -2147483648
with 0x0000000080000000 = 2147483648
, resulting a wrong value.
flag 0000000000000000000000000000000011000000000100010000000000000001 __int64 0x00000000c0110001 __int64 transition_state 10000000000000000000000000000000 int 0x80000000 int -2147483648 int state_flag & transition_state 0000000000000000000000000000000010000000000000000000000000000000 __int64 0x0000000080000000 __int64 2147483648 __int64
To do this correctly, we MUST compare with an unsigned integer.
unsigned int transition_state = (1 << 31); bool is_down = (state_flag & transition_state) != transition_state;
Now, the result is equal.
Let's see the final code.
switch (msg.message) { case WM_SYSKEYUP: case WM_SYSKEYDOWN: case WM_KEYUP: case WM_KEYDOWN: { int transition_state = (1 << 31); int previous_state = (1 << 30); bool is_down = (msg.lParam & transition_state) == 0; bool was_down = (msg.lParam & previous_state) != 0; if (was_down != is_down) { if (msg.wParam == VK_ESCAPE) { should_quit = true; } else if (msg.wParam == 'W') { *yo += 1; } else if (msg.wParam == 'S') { *yo -= 1; } else if (msg.wParam == 'A') { *xo -= 1; } else if (msg.wParam == 'D') { *xo += 1; } } } break; }