..
#gamedev

006 - How to Process Keyboard Input

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;
 }