Why you should avoid while(GetKeyState("a", "P")) to detect release of key a

Put simple Tips and Tricks that are not entire Tutorials in this forum
kyuuuri
Posts: 340
Joined: 09 Jan 2016, 19:20

Re: Why you should avoid while(GetKeyState("a", "P")) to detect release of key a

14 Mar 2018, 05:41

nnnik wrote:I think you should ask in Ask for Help and or inform yourself about threads and their limitations.
Thank you!, made the post here: https://autohotkey.com/boards/viewtopic.php?f=5&t=45585
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: Why you should avoid while(GetKeyState("a", "P")) to detect release of key a

26 Apr 2018, 11:04

Sorry, just re-reading and noticed this comment:
Nextron wrote:On a side note, there are useful situations to wait for a key to be released by halting the execution of the hotkey like in the first example, for example to prevent key-repeat from executing a hotkey ~50 times per second as it would in the second example
1) The key would not repeat 50 times a second
2) "Useful"? Yes. "Works properly"? No - only when there is only one of them.

To properly enforce repeat suppression:

Code: Select all

a::
	if (a_pressed)
		return
	a_pressed := 1
	; [...]
	return

a up::
	a_pressed := 0
	return
User avatar
Nextron
Posts: 1391
Joined: 01 Oct 2013, 08:23
Location: Netherlands OS: Win10 AHK: Unicode x32

Re: Why you should avoid while(GetKeyState("a", "P")) to detect release of key a

26 Apr 2018, 17:10

I guess the topic 'How to disable repeat?' made you think about this topic too? ;) I was considering whether to reply here or there.

1) The key would not repeat 50 times a second
My key history showed a held key sending a down event every 0.02 seconds, minus an activation delay of half a second. A F1::Tooltip % i++ script shows an execution rate close to the value above.
2) "Useful"? Yes. "Works properly"? No - only when there is only one of them.
So it has its limitations. So does Looping Input,foobar,L1 or AHK's hook detecting its own SendInput. Perhaps your use of held keys is is different from mine so that you strongly oppose KeyWait, but I use it frequently without problems. I even use a wrapper function that makes it easier to use and adds functionality. Either to prevent key repeat, to delay execution until the hotkey is released or segment the execution in consecutive blocks triggered by the same hotkey.

Sure, under specific circumstances it will not work as one would want: If KeyWait is waiting AND a new pseudo thread interrupts AND that thread also does not return timely AND the first hotkey is pushed again. Perhaps that is a frequent occurrence in gaming or input emulation, but I doubt it is in office situations. Considering its ease of use, it shouldn't be dismissed outright. I would hate to create an accompanying up-hotkey every time I want to wait for a key or even update any existing up-hotkeys for each #If duplicate. Even then, I would need three different blocks of code for each of my three use cases instead of one command.

Besides that, although I use #UseHook,On for my main script, I tend to be conservative in applying the invasiveness of a hook in separate scripts or scripts that may be used by others. I wouldn't want to use a hook just to prevent key repeat unless there is a real indication that using KeyWait would actually be blocking.
User avatar
submeg
Posts: 326
Joined: 14 Apr 2017, 20:39
Contact:

Re: Why you should avoid while(GetKeyState("a", "P")) to detect release of key a

28 May 2021, 23:04

Thanks for this topic @evilC!

I have been having problems in my scripts where I will discovered my LWin or LAlt (or something!) key is getting "stuck" and it's giving me quite the headache!

I will take the ideas in this thread and start working through my scripts to find the one (or more) that is causing this error. In the meantime, is there a way to notify me when a key is stuck down / on programatically, even when I have physically released the key(s)?

I found this topic that would pop up a tooltip when you held down the left or right win keys...

I was thinking of creating something that will check all keys so I know if something is stuck. Is there a better way than just making a timer for all the keys I want to check?
____________________________________
Check out my site, submeg.com
Connect with me on LinkedIn
Courses on AutoHotkey :ugeek:
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: Why you should avoid while(GetKeyState("a", "P")) to detect release of key a

29 May 2021, 06:43

You could create an array, and whenever you programmatically hold or release a key, update the array.
Then, if you want to know if anything is stuck, just check the state of the array

Code: Select all

keyStates := {}
return

a::
   SendKey("x", true)
   while (GetKeyState("a")){
      Sleep 100
   }
   SendKey("x", false)
   return

F1::Gosub CheckKeys

SendKey(key, state){
   global keyStates
   Send % "{" key (state ? " down" : " up") "}"
   keyStates[key] := state
   return
}

CheckKeys:
   for key, state in keyStates {
      if (state){
         msgbox % "Key " key " is held"
      }
   }
   return

^Esc:: ExitApp

Last edited by evilC on 30 May 2021, 05:09, edited 1 time in total.
User avatar
submeg
Posts: 326
Joined: 14 Apr 2017, 20:39
Contact:

Re: Why you should avoid while(GetKeyState("a", "P")) to detect release of key a

29 May 2021, 16:48

evilC wrote:
29 May 2021, 06:43
You could create an array, and whenever you programmatically hold or release a key, update the array.
Then, if you want to know if anything is stuck, just check the state of the array

Hey @evilC,

Thanks for the idea! I could see the idea in the code and I modified slightly to get my head around what was going on. What I notice is that even when you programmatically "force" a key down, (LWin in the example below), the array is not remembering it? When you check out KeyHistory, it's definitely down - pressing F1 brings up Windows help, so it has to be down, but it's not in the array?

Code: Select all

global keyStates
keyStates := {}

return

a::
   
   KeyHistory
   
   StuckKey("LShift")
   StuckKey("LCtrl")
   StuckKey("LAlt")
   StuckKey("a")
   
   Send, {LWin down}
   StuckKey("LWin")
   
return


F1::

CheckKeys()

Return



StuckKey(key)
{
   
   ;Send,  %key% (state ? " down" : " up")
   ;CheckState :=  %key% (state ? " down" : " up")
   
   GetKeyState, state, %key%  
   
   If %state% = "down"
   {
      
      CheckState := "down"
      
   }
   else
   {
      
      CheckState := "up"
      
   }
   
   msgbox % "key is: " key ", and it is " CheckState
   
   If CheckState = "down"
   {
      keyStates[key] := CheckState
   }
   
   return
}

CheckKeys()
{
   
   CheckedStates := 0       ;No keys are stuck
   
   for key, state in keyStates 
   {
      if (state = "down")
      {
         
         msgbox % "Key " key " is down"
         CheckedStates := 1     ;At least one key is stuck
         
      }
   }
   
   If CheckedStates = 0
   {
      msgbox % "All keys are released."
   }
   
}
return

____________________________________
Check out my site, submeg.com
Connect with me on LinkedIn
Courses on AutoHotkey :ugeek:
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: Why you should avoid while(GetKeyState("a", "P")) to detect release of key a

30 May 2021, 05:16

Your code is not doing anything like what mine was doing.
Granted, my code contained some typos (I wrote it from memory, didn't test it) - I was missing a bracket on the GetKeyState, and my send syntax was a bit garbled but once I fixed that it exhibits what I am talking about (Post edited to fix code).
Hold a, hit F1

Or, to demonstrate it another way

Code: Select all

keyStates := {}
return

a::
   SendKey("x", true)
   ;~ while (GetKeyState("a")){
      ;~ Sleep 100
   ;~ }
   ;~ SendKey("x", false)
   return

F1::Gosub CheckKeys

SendKey(key, state){
   global keyStates
   Send % "{" key (state ? " down" : " up") "}"
   keyStates[key] := state
   return
}

CheckKeys:
   for key, state in keyStates {
      if (state){
         msgbox % "Key " key " is held"
      }
   }
   return

^Esc:: ExitApp
Tap a, then hit F1
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: Why you should avoid while(GetKeyState("a", "P")) to detect release of key a

30 May 2021, 05:20

Or, to demonstrate the kind of bug that this thread is about:

Code: Select all

keyStates := {}
return

a::
   SendKey("x", true)
   while (GetKeyState("a")){
      Sleep 100
   }
   SendKey("x", false)
   return

b::
   SendKey("y", true)
   while (GetKeyState("b")){
      Sleep 100
   }
   SendKey("y", false)
   return

F1::Gosub CheckKeys

SendKey(key, state){
   global keyStates
   Send % "{" key (state ? " down" : " up") "}"
   keyStates[key] := state
   return
}

CheckKeys:
   for key, state in keyStates {
      if (state){
         msgbox % "Key " key " is held"
      }
   }
   return

^Esc:: ExitApp
Hold a, hold b, release a
And then hit F1 while b is still held

It will say that both x and y are held, despite the fact that you released a, so x should be not held
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: Why you should avoid while(GetKeyState("a", "P")) to detect release of key a

30 May 2021, 05:34

Or, to demonstrate a slightly more elegant way of doing it:

Code: Select all

keyStates := {}
return

a::
   SendKey("x", true)
   while (GetKeyState("a")){
      Sleep 100
   }
   SendKey("x", false)
   return

b::
   SendKey("y", true)
   while (GetKeyState("b")){
      Sleep 100xy
   }
   SendKey("y", false)
   return

F1::Gosub CheckKeys

SendKey(key, state){
	global keyStates
	Send % "{" key (state ? " down" : " up") "}"
	if (state){
		keyStates[key] := 1
	} else {
		keyStates.Delete(key)
	}
	return
}

CheckKeys:
	if (keyStates.length() == 0){
		msgbox Nothing is held
	} else {
		for key, state in keyStates {
			msgbox % "Key " key " is held"
		}
	}
	return
In this example, you don't even need to iterate the array to see if anything is stuck, because when the key is released, instead of setting the array entry to say that the key is released, it completely removes that key from the array
Therefore, a simple keyStates.length() == 0 check will tell you if there is anything held
User avatar
submeg
Posts: 326
Joined: 14 Apr 2017, 20:39
Contact:

Re: Why you should avoid while(GetKeyState("a", "P")) to detect release of key a

30 May 2021, 06:25

Your code is not doing anything like what mine was doing.
Yep, I worked that out quick smart! :lol:

I was able to get my idea working, but it's definitely not elegant. I will give you examples a test tomorrow; my brain is too tired to do any more coding tonight.

Thank you for this thread (and scripts), I know that using KeyWait and GetKeyState are currently causing me issues and this thread will help me iron them out.

Absolute champion @evilC.
____________________________________
Check out my site, submeg.com
Connect with me on LinkedIn
Courses on AutoHotkey :ugeek:

Return to “Tips and Tricks (v1)”

Who is online

Users browsing this forum: No registered users and 24 guests