Win32 Direct Keyboard State

Reporter: RobertBColton  |  Status: closed  |  Last Modified: June 30, 2020, 09:39:27 AM

So apparently I implemented (d08c3d9) this way back and never tested it, then copied it to Linux where Josh later fixed it. Regardless, it doesn't work on master and it's also really slow because I didn't know how to write it and had it polling the keyboard state twice.

Anyway I made a little test to see if it was working and discovered that apparently the logical vk_nokey and vk_anykey were never supported by keyboard_check_direct(), not in GM8.1 or GMSv1.4 afaict.

var text;
text = string(keyboard_check_direct(vk_nokey)) + " " + string(keyboard_check_direct(vk_anykey));
draw_text(0,0,text);

Now when I benchmark this with the #2059 test I had, it's still slower (100,000~ microseconds) than our SDL (5000~ microseconds) and GMSv1.4 (50,000~ microseconds) even after the fix. There could be two reasons for this. either SDL & GMSv1.4 are caching the direct keyboard state once in a frame or GetKeyState/GetAsyncKeyState should be used here. The latter seems more likely since again GM never supported the vk_nokey and vk_anykey logical keys for this function, and there would be no way to tell their state if this function only polled a single key instead of the entire keyboard. Regardless, when I tested removing that and just using GetKeyState/GetAsyncKeyState the function then performed around 30,000~ microseconds suggesting it was closer to GM at that point and that SDL might be caching or not as low-level as it appears.

The documentation also seems to suggest that GetKeyboardState is message based hardly distinguishing it from our existing high-level input that is based on messaging.

The status changes as a thread removes keyboard messages from its message queue.
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getkeyboardstate

Since GetKeyState also says the same & more, it seems like GetAsyncKeyState really is the real candidate.

The status does not reflect the interrupt-level state associated with the hardware.
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getkeystate

Finally, GetAsyncKeyState does mention that it checks at the time the function is called.
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getasynckeystate

RobertBColton  
Alright for reasons above and because when I tested GetAsyncKeyState completes in 33,000~ microseconds (closest to GMSv1.4), I decided to use it. I assume GMSv1.4's extra 20,000 microseconds are the fact I was using the Windows target and not the Windows YYC target.

Another clue that this is correct is that the keyboard_check_direct() function is still only available on Windows, suggesting there was no SDL equivalent for YYG to port it easily.

NOTE: This function is only available for the standard Windows target and the result is independent of which application has focus.
https://docs.yoyogames.com/source/dadiospice/002_reference/mouse,%20keyboard%20and%20other%20controls/keyboard%20input/keyboard_check_direct.html

RobertBColton  

Alright, I've devised the definitive test about this pull request's correctness. At first glance the test will tell you they always arrive at the same time in GM8.1, but it's not true. If we look a little closer and use spacebar (key we are testing) to dismiss the message, then another message will show with 0 0 1 or saying that direct had arrived. GMSv1.4 is the same except you have to hold space down and click ok because it doesn't dismiss unless you release the space. Another way to test the two of them is to focus another window and press space like I am doing while typing this message and then be annoyed at the fact that it's telling you the direct message was received first.

var message, pressed, direct;
message = keyboard_check(vk_space);
pressed = keyboard_check_pressed(vk_space);
direct = keyboard_check_direct(vk_space);

if (message || pressed || direct)
    show_message(string(message) + " " + string(pressed) + " " + string(direct));

NOTE: Please remember to shut "Freeze on lose focus" setting off before testing in ENIGMA!

Now this is where it gets awesome, this pull request behaves like GMSv1.4 and GM8.1. It does this regardless of whether GetAsyncKeyState or GetKeyState is used, so it's not as definitive as I'd like. However, GetKeyboardState when used is not able to get the direct input when the window is not focused. This definitely rules it out as the implementation for this function. Finally, it does indicate that SDL_GetKeyboardState is wrong too because our SDL system is always reporting them all arriving at the same time, indicating its not as low-level as one would hope for. SDL also won't say anything if the window isn't focused, another indicator.

codecov[bot]  

Codecov Report

Merging #2060 into master will not change coverage.
The diff coverage is n/a.

Impacted file tree graph

@@           Coverage Diff           @@
##           master    #2060   +/-   ##
=======================================
  Coverage   30.93%   30.93%           
=======================================
  Files         197      197           
  Lines       19102    19102           
=======================================
  Hits         5910     5910           
  Misses      13192    13192           

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update dab5494...f9d6b83. Read the comment docs.

RobertBColton  

Alright, now I have the real definitive test.

NOTE: Just to be sure UP is released between testing sessions, tap it a few times before you switch programs.

var message, pressed, direct;
keyboard_key_release(vk_up); // for good measure
keyboard_key_press(vk_up);
message = keyboard_check(vk_up);
pressed = keyboard_check_pressed(vk_up);
direct = keyboard_check_direct(vk_up);
keyboard_key_release(vk_up);
show_message(string(message) + " " + string(pressed) + " " + string(direct));

As we can see ENIGMA is only the same as GM8.1 and GMSv1.4 when using GetAsyncKeyState as we had predicted.

000 SDL
000 GetKeyState
000 GetKeyboardState
001 GetAsyncKeyState
001 GM8.1
001 GMSv1.4 

RobertBColton  

Again, the reason why I would be so adamant about this is because if keyboard_check_direct doesn't actually provide DIRECT keyboard state, there is no purpose to it. If it's in any way message based, then it's no different than keyboard_check and we would be wasting our time. Although implementing the function as message based on our platforms which do not yet have direct keyboard state functionality, like xlib/SDL, is sensible for people to easily port games and totally acceptable.
Please sign in to post comments, or you can view this issue on GitHub.