Author Topic: Get Key presses using GetAsyncKeyState%  (Read 4246 times)

0 Members and 1 Guest are viewing this topic.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Get Key presses using GetAsyncKeyState%
« on: December 26, 2019, 09:31:24 am »
Code: QB64: [Select]
  1. DECLARE LIBRARY 'function is already used by QB64 so "User32" is not required
  2.     FUNCTION GetAsyncKeyState% (BYVAL vkey AS LONG)
  3.  
  4.  
  5.     k = GetKey(.15)
  6.     IF k <> 0 THEN PRINT k
  7.     _LIMIT 120
  8.  
  9. FUNCTION GetKey% (Delay##)
  10.     'The delay is here so we can slow down how quickly we check for keypresses.
  11.     'Set it to 0 and see what happens when you hit a key...
  12.     'We read the keyboard state so quickly, we get multiple returns for each press.
  13.  
  14.     STATIC LastReturn##
  15.     STATIC v2 AS INTEGER
  16.     IF v2 <> 0 THEN GetKey = v2: v2 = 0: EXIT FUNCTION 'return a secondary key for LEFT/RIGHT shift, ctrl
  17.     IF LastReturn## = 0 THEN LastReturn## = TIMER
  18.     IF _WINDOWHASFOCUS AND TIMER > LastReturn## THEN
  19.         FOR i = 8 TO 222
  20.             SELECT CASE i
  21.                 CASE 8, 9, 13, 27: IF GetAsyncKeyState(i) THEN v = i: EXIT FOR 'backspace, tab, enter, esc
  22.                 CASE 16 TO 18 'shift, ctrl, alt, which we deal with below with the left/right returns
  23.                 CASE 19 TO 20: IF GetAsyncKeyState(i) THEN v = i: EXIT FOR ' pause, capslock
  24.                 CASE 32 TO 40: IF GetAsyncKeyState(i) THEN v = i: EXIT FOR 'space, pageup, pagedown, end, home,
  25.                     '37 to 40 left arrow, up arrow, right arrow, down arrow
  26.                 CASE 44 TO 46: IF GetAsyncKeyState(i) THEN v = i: EXIT FOR 'system, insert, delete
  27.                 CASE 48 TO 49: IF GetAsyncKeyState(i) THEN v = i: EXIT FOR '0-9
  28.                 CASE 65 TO 93: IF GetAsyncKeyState(i) THEN v = i: EXIT FOR 'A-Z, left win, right win, menu
  29.                 CASE 96 TO 123: IF GetAsyncKeyState(i) THEN v = i: EXIT FOR 'numpad keys and F1-F12
  30.                 CASE 144, 145: IF GetAsyncKeyState(i) THEN v = i: EXIT FOR 'screenlock, numlock
  31.                 CASE 160 TO 165: IF GetAsyncKeyState(i) THEN v = i: EXIT FOR 'shift, ctrl, alt
  32.                 CASE 187 TO 192: IF GetAsyncKeyState(i) THEN v = i: EXIT FOR '-+<>/~
  33.                 CASE 219 TO 221: IF GetAsyncKeyState(i) THEN v = i: EXIT FOR '[\]
  34.                 CASE ELSE
  35.                     'It's not a key which QB64 has included on the virtual map listing at https://www.qb64.org/wiki/Keyboard_scancodes
  36.                     'BEEP
  37.                     IF GetAsyncKeyState(i) THEN PRINT i; " key pressed, which is not recognized.  Please report this to Steve!"
  38.                     'END
  39.             END SELECT
  40.         NEXT
  41.         IF v THEN
  42.             GetKey = v
  43.             LastReturn## = TIMER + Delay##
  44.             v2 = 0
  45.             IF v = 160 OR v = 161 THEN v2 = 16
  46.             IF v = 162 OR v = 163 THEN v2 = 17
  47.             IF v = 164 OR v = 165 THEN v2 = 18
  48.         END IF
  49.     END IF
  50.  

Working on various things over the last few days, as I've tried to relax and do some coding between the hustle and bustle of Christmas, I've sorted out that _KEYHIT and _KEYDOWN have a ton of flaws. 

Shift-Tab returns the value for a CTRL-I key press.
The Menu key doesn't return a down value at all, and tries to report itself as an up bracket "]" keypress.
Various other mappings are off and unreliable, but I don't remember exactly what they are now, off the top of my head.

So, in an attempt to make use of a few of those "lost" keys, I sat down and wrote the above little routine for us -- which only works in Windows (as GetAsyncKeyState is a Windows library call).  As far as I can tell, this properly tells us if we hit a key, or not.   

The only issues I've found with it are:

1) No distinction between uppercase or lowercase keypresses.  You'd need to write a toggle into your code for IF _KEYDOWN(100303) or _KEYDOWN(100304) THEN, and then use it to toggle between uppercase input and lowercase.

2) It returns repeat values so dang fast, I had to code in a delay system to stop from getting multiple responses from a single key press. 

I don't know if this would be useful for anyone else, as nobody else seems to ever complain about missing/incorrect key returns (maybe most people simply don't key for such events as a menu key hit, or shift-tab actions), but I thought I'd share it anyway.  ;)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline 40wattstudio

  • Newbie
  • Posts: 82
    • View Profile
    • 40wattstudio
Re: Get Key presses using GetAsyncKeyState%
« Reply #1 on: October 25, 2020, 05:29:15 pm »
Thanks for posting this SMcNeill, it actually did come in handy as I have a menu in my game and one of the things I've had problems with is a key being triggered hundreds or thousands of times from a single key press.

Here's an alternate version of the delay feature I implemented if anyone wants to use it for their own projects:
Code: QB64: [Select]
  1. DIM StartKeyTimer AS _FLOAT
  2. DIM CurrentTime AS _FLOAT
  3. StartKeyTimer = TIMER ' set starting time
  4.     CurrentTime = TIMER ' get current time
  5.     IF CurrentTime > StartKeyTimer + 0.15 THEN ' if 0.15 seconds has passed . . .
  6.         IF _KEYDOWN(13) THEN PRINT "ENTER key pressed"
  7.         IF _KEYDOWN(32) THEN PRINT "SPACE BAR pressed"
  8.         StartKeyTimer = TIMER ' reset first timer
  9.     END IF
  10.  
  11. LOOP UNTIL _KEYDOWN(27) ' loop until ESC key pressed
  12.  

Offline SierraKen

  • Forum Resident
  • Posts: 1454
    • View Profile
Re: Get Key presses using GetAsyncKeyState%
« Reply #2 on: October 25, 2020, 11:15:37 pm »
Really cool Steve. I did find some keys that it doesn't recognize and it says:  "57 key pressed which is not recognized. Please report this to Steve!" The keys are the number keys that are above the letters on your keyboard from 2 to 9. 1 and 0 are fine. The number pad works fine though.
« Last Edit: October 25, 2020, 11:18:58 pm by SierraKen »

Offline SierraKen

  • Forum Resident
  • Posts: 1454
    • View Profile
Re: Get Key presses using GetAsyncKeyState%
« Reply #3 on: October 25, 2020, 11:20:42 pm »
Could you make something like this to find the KeyDown command numbers? I see these are different than what you used on the moving map code I've been using.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Get Key presses using GetAsyncKeyState%
« Reply #4 on: October 26, 2020, 04:58:50 am »
Could you make something like this to find the KeyDown command numbers? I see these are different than what you used on the moving map code I've been using.

You might want to try out the custom keyboard input routine I wrote here:  https://www.qb64.org/forum/index.php?topic=2042.0

So far, it's been configured to work with US, German, and Italian keyboard layouts, but you can customize it to work with whatever language/keyboard you might have.  Just read through the topic a bit, and it explains how to remap any keys to whatever unicode value you want them to represent.  ;)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SierraKen

  • Forum Resident
  • Posts: 1454
    • View Profile
Re: Get Key presses using GetAsyncKeyState%
« Reply #5 on: October 26, 2020, 01:02:49 pm »
That's a bit much for me to get into Steve lol. But I found a great chart on the Wiki pages with what I need:
http://www.qb64.org/wiki/KEYDOWN