Modify

Opened 3 months ago

Closed 2 months ago

#3990 closed Feature Request (Completed)

_IsPressed() add "indicate whether the key has been pressed since the last query"

Reported by: argumentum Owned by: Jpm
Milestone: 3.3.17.0 Component: AutoIt
Version: Severity: None
Keywords: GetAsyncKeyState, _IsPressed() Cc:

Description

Func _IsPressed($sHexKey, $vDLL = "user32.dll")
    Local $aCall = DllCall($vDLL, "short", "GetAsyncKeyState", "int", "0x" & $sHexKey)
    If @error Then Return SetError(@error, @extended, False)
    Return SetError(0, $aCall[0] = 0xFFFF8000, BitAND($aCall[0], 0x8000) <> 0)
EndFunc   ;==>_IsPressed

The change adds some extra information that I found was needed using a Bluetooth headset. The chatter about its use is at https://www.autoitscript.com/forum/topic/211304-getasynckeystate-_ispressed-vs-the-bluetooth-headset-now-fight/ .
Does not brake prior script code.

Attachments (0)

Change History (52)

comment:1 Changed 3 months ago by TicketCleanup

  • Version Other deleted

Automatic ticket cleanup.

comment:2 Changed 3 months ago by Jpm

Hi, I cannot test it but the code look strange
If the @extended is set and the return is always true
So why to set @extended is this case?

comment:3 Changed 3 months ago by Jpm

Hi,
Sorry unless I get an correction to my post above I will close it

comment:4 Changed 3 months ago by argumentum

In my case the @extended is needed. @extended is not always true. Is only true after some MSec. The first query returns false unless it was triggered again.
If you test it, you'll see that it does.

comment:5 Changed 3 months ago by argumentum <argumentum@…>

( adding this just to get an email upon update )

comment:6 Changed 3 months ago by Jpm

I still don't understand

Return SetError(0, $aCall[0] = 0xFFFF8000, BitAND($aCall[0], 0x8000) <> 0)

if $aCall[0] = 0xFFFF8000 is true
then

BitAND($aCall[0], 0x8000) is also true

so @extended is not needed

as I say i don't have and headset so I cannot reproduce

thanks to confirm

comment:7 Changed 3 months ago by argumentum <argumentum@…>

BitAND($aCall[0], 0x8000) <> 0
vs
$aCall[0] = 0xFFFF8000

On action(click/keypress) is 0xFFFF8001 or 0x00000000

The code returns if "BitAND($aCall[0], 0x8000) <> 0"
am adding if "$aCall[0] = 0xFFFF8000"

Unfortunately the only way to see somewhat what the effects are is to keep a key pressed until autorepeat kicks in on a keyboard to somewhat get the gist of it.

comment:8 Changed 3 months ago by Jpm

Are you sure that with this modification working for you we will not get regression as MSDn state

Although the least significant bit of the return value indicates whether the key has been 
pressed since the last query, due to the preemptive multitasking nature of Windows, another 
application can call GetAsyncKeyState and receive the "recently pressed" bit instead of your 
application. The behavior of the least significant bit of the return value is retained 
strictly for compatibility with 16-bit Windows applications (which are non-preemptive) and 
should not be relied upon.
Last edited 3 months ago by Jpm (previous) (diff)

comment:9 Changed 3 months ago by argumentum <argumentum@…>

No one can tell what M$ is ever gonna do, but is by this feature that the code can tell that the key ( 0xB3 ) and those like it in multimedia ( 0xB0, 0xB1 ) are actively pressed via Bluetooth or not. If it's been there since Win95, I'll bet there's a lot of code depending on it and unlikely to change.
In bluethooth keys, a long press is not the same as a single click/press or a double one. Some even have triple press/click, all with different functionality, faking/masquerading having independent keys, and I believe is a driver thing, but is the M$ driver. There is no alternate driver.
--continue in next post--

comment:10 Changed 3 months ago by argumentum <argumentum@…>

... And the explanation of MSDN gives out is true, but also is true that PCs are way faster than when the warning was presented some 20 years ago when 32 bit was the future and 16 bit was nothing but gone. Actually, one can not run 16 bit code in windows nowadays, it is gone. But the base code is thankfully, backwards compatible.

comment:11 Changed 3 months ago by Jpm

Hi,
thanks for those highlights
Can you confirm that with the change you suggest no other equipment non BlueTooths will not have regression? it seems your post suggest it, I just want to be sure

Thanks for the clarification
Cheers

comment:12 Changed 3 months ago by argumentum <argumentum@…>

As far as I've tested with various hardware, it works as expected.
I don't foresee any problems. I know that it would generally be handled by:

While _IsPressed("B3")
	Sleep(50)
WEnd

But the proposed @extended return, adds that extra GetAsyncKeyState info. that in these particular cases, just saves the day.

By the way, in my testing, this solution is even better than using _WinAPI_SetWindowsHookEx($WH_KEYBOARD_LL,...

comment:13 Changed 3 months ago by Jpm

Thank you
what do you propose for doc change for @extended?

comment:14 Changed 3 months ago by argumentum <argumentum@…>

Return Value
@extended is set by least significant bit. The value specifies whether the key was pressed since the last call. (See MSDN remarks)

See Also
Search "https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getasynckeystate#remarks" in MSDN Library.

Hopefully the above is sufficiently explicit.

comment:15 Changed 3 months ago by Jpm

Are you sure of 0xFFFF8000?
Th least significant bit is 0xFFFF8001
right?

comment:16 Changed 3 months ago by argumentum <argumentum@…>

..I'm did a copy and paste and my math skill ..are missing from my brain =/
Then better use this less specific explanation:

Return Value
@extended returns whether the key is still pressed since the last call. (See MSDN remarks)

comment:17 Changed 3 months ago by argumentum <argumentum@…>

...the above don't sit well with me. Is not so. I'll see it on Tuesday, I'll have a less foggy brain, then I'll have a look at it and discern it.

comment:18 Changed 3 months ago by argumentum <argumentum@…>

ok, I've got my head back. This would be the proper code:

Func _IsPressed($sHexKey, $vDLL = "user32.dll")
	Local $aCall = DllCall($vDLL, "short", "GetAsyncKeyState", "int", "0x" & $sHexKey)
	If @error Then Return SetError(@error, @extended, False)
	Return SetError(0, BitAND($aCall[0], 0x1), BitAND($aCall[0], 0x8000) <> 0)
EndFunc   ;==>_IsPressed

The @extended has the LSB. What is meant is that when the key is first pressed, the LSB is ON. On subsequent calls, the LSB is zero and the claim is to not trust the LSB (BitAND($aCall[0], 0x1)) due to the nature of multitasking, hence the BitAND($aCall[0], 0x8000) return.
So the original description is in fact correct. But is not clear to a non-english native speaker like myself, therefore a better description would be:
@extended returns the LSB of the keypress. (See MSDN remarks)
that way, if the reader wants to know what it really is, can read up on the subject.

comment:19 Changed 3 months ago by argumentum <argumentum@…>

..pardon. The description was correct, the code was wrong. (corrected above)

comment:20 Changed 3 months ago by Jpm

Hi,
I am still confused on how you use @extended with a BlueTooth headset
Can you post an example of how you you it?
For other device the @extended is always set
Thanks for the help

comment:21 Changed 3 months ago by argumentum <argumentum@…>

If _IsPressed("B3", $hDLL_user32) And @extended Then
	;VlcSendCmd("{SPACE}")
EndIf

The "keyboard" gets stuck in 0x8000. But do trigger the 0x8001 when pressed.
Same with prior and next keys. Volume up/down are not "seen" ( captured by the OS ).

My fight with the headset is at https://www.autoitscript.com/forum/topic/211304-getasynckeystate-_ispressed-vs-the-bluetooth-headset-now-fight/.

I scratched my head for a while until I realized what was going on.
And is a hard sell as far as getting enough people to accept this change given that is against MSDN recommendation ( 20 y/o ) and quite a niche occurrence. Yet, is the only thing that worked.

comment:22 Changed 3 months ago by argumentum <argumentum@…>

For other device the @extended is always set

With the new code, @extended is only triggered the 1st time as true(1) / BitAND($aCall[0], 0x1)
2nd time and on it returns false(0)

comment:23 Changed 3 months ago by Jpm

As I understand the return True with only the Most significant bit set is not enought for you (current implementation) you want to be sure it is the first time
so you don't want to check it is released as MSDN suggest (return false) and do some action

in the next beta version default is the same but now if you pass not a string a a VK_* value the return will wait the touch is release if you pass an array of $VK_* you will get a return if one of them has been clicked

comment:24 Changed 3 months ago by argumentum <argumentum@…>

would you paste here or send a PM/DM to me about this new beta code to take a look at ?
..looking at the beta code would give me a better understanding of what is that you are evaluating. A matching array of VK_ would ... hmmm, could return the "LSB += LSB" to satisfy my request.

comment:25 Changed 3 months ago by Jpm

; #FUNCTION# ====================================================================================================================
; Author ........: ezzetabi and Jon
; Modified.......: Jpm
; ===============================================================================================================================
Func _IsPressed($vKey, $vDLL = "user32.dll", $bCheckModifierKey = True)
	Local Static $hDLL = DllOpen("user32.dll")
	Local Static $iKeyDown = 0x8000
	Local Static $aModifierKeys[6] = [0, $VK_SHIFT, $VK_CONTROL, $VK_MENU, $VK_LWIN, $VK_RWIN]

	If IsString($vKey) Then
		Local $aCall = DllCall($vDLL, "short", "GetAsyncKeyState", "int", "0x" & $vKey)
		If @error Then Return SetError(@error, @extended, False)
		Return SetExtended(BitAND($aCall[0], 0x01), BitAND($aCall[0], $iKeyDown) <> 0)
	EndIf

	Local $aKeys[1]
	If IsArray($vKey) Then
		; will check on on of them
		$aKeys = $vKey
	Else
		$aKeys[0] = $vKey
	EndIf

	If $bCheckModifierKey Then     ; check no modifier keys
		For $i = 1 To 5
			If BitAND(DllCall($hDLL, "short", "GetAsyncKeyState", "int", $aModifierKeys[$i])[0], $iKeyDown) Then Return SetExtended($i, False)
		Next
	EndIf

	For $i = 0 To UBound($aKeys) - 1
		If BitAND(DllCall($hDLL, "short", "GetAsyncKeyState", "int", $aKeys[$i])[0], $iKeyDown) Then
			Do     ; wait until is KeyUp
			Until Not BitAND(DllCall($hDLL, "short", "GetAsyncKeyState", "int", $aKeys[$i])[0], $iKeyDown)

			Return $i + 1     ; $aKeys[$i] has been clicked
		EndIf
	Next

EndFunc   ;==>_IsPressed

comment:26 Changed 3 months ago by argumentum <argumentum@…>

Now that I've looked at it I understand. Looks good. Thanks.

In my, quite unexpected case, using the VK_* would have gotten me in a (infinite loop) frozen script.

comment:27 Changed 3 months ago by Jpm

Hi, in which case do you get an infinite loop?

comment:28 Changed 3 months ago by argumentum <argumentum@…>

I get an infinite loop with 0xB3 from the bluetooth at:

Do     ; wait until is KeyUp
Until Not BitAND(DllCall($hDLL, "short", "GetAsyncKeyState", "int", $aKeys[$i])[0], $iKeyDown)

..given that in this case of the bluetooth input device (or driver), signals the LSB ( 0x8001 ) and gets stuck in 0x8000, never to get back to 0x0000 as it should. Is the whole reason for the need to know the LSB. Then again, fortunately, is not something that happens on a keyboard.

As I see it ( and tested ), the beta code is good.

comment:29 Changed 3 months ago by argumentum <argumentum@…>

Side note about the VK_* new addition in beta:
1) A user expects action right after pressing the keys, not after releasing them.
2) The $bCheckModifierKey should in my view, default to false.
Then again I have no idea why the new feature is incorporated nor the way it is. Nonetheless I wanted to bring it up. ( unless is outside the scope of this post and is to be discussed elsewhere and/or by MVPs )

comment:30 Changed 3 months ago by Jpm

I don't remember when the extension was introduced
I agree that they are more to handle clicked keys
The default as true is really to have a return on the the key only without the modifier pressed

I need to think if the function must work on pressed only just propose modification so that addition can work

comment:31 Changed 3 months ago by argumentum <argumentum@…>

The default as true is really to have a return on the the key only without the modifier pressed

Well, the array is the first claim. Say I make an array ( $aArray[2] = [0x12,0x58] ; alt - x ) that by default will not work as expected due to defaulting to exclude the alt key, otherwise the code would have to exclude the modifier key declared on the array. That complicates the code. It'd be easier to just default to false, to keep it as is.
Most people using this func are expecting a function key or a combination of modifier and letter, not just a letter.

The part that only returns after keys released, to make it on keypress, either, call the func again with "NOT func()" as it was done before the change, or have an extra parameter to call a func. for the in between state.

So removing the wait on release and defaulting $bCheckModifierKey to false would make the new beta functional.

Also,

Local Static $hDLL = DllOpen("user32.dll")
If $vDLL = Default Or $vDLL = "user32.dll" Or $vDLL = "" Then $vDLL = $hDLL ; <<<<

would make more sense if the staic declaration is to be kept.

These changes will obviously change the expected functionality of whomever proposed the change but at least it would make the code widely functional other than just a for OP only.

Therefore, the "fixed" code ( with minimal modification, to just make it functional ) I propose would be:

Func _IsPressed($vKey, $vDLL = Default, $bCheckModifierKey = False)
	Local Static $hDLL = DllOpen("user32.dll")
	If $vDLL = Default Or $vDLL = "user32.dll" Or $vDLL = "" Then $vDLL = $hDLL
	Local Static $iKeyDown = 0x8000
	Local Static $aModifierKeys[6] = [0, $VK_SHIFT, $VK_CONTROL, $VK_MENU, $VK_LWIN, $VK_RWIN]

	If IsString($vKey) Then
		Local $aCall = DllCall($vDLL, "short", "GetAsyncKeyState", "int", "0x" & $vKey)
		If @error Then Return SetError(@error, @extended, False)
		Return SetExtended(BitAND($aCall[0], 0x01), BitAND($aCall[0], $iKeyDown) <> 0)
	EndIf

	Local $aKeys[1]
	If IsArray($vKey) Then
		; will check on on of them
		$aKeys = $vKey
	Else
		$aKeys[0] = $vKey
	EndIf

	If $bCheckModifierKey Then     ; check no modifier keys
		For $i = 1 To 5
			If BitAND(DllCall($hDLL, "short", "GetAsyncKeyState", "int", $aModifierKeys[$i])[0], $iKeyDown) Then Return SetExtended($i, False)
		Next
	EndIf

	For $i = 0 To UBound($aKeys) - 1
		If BitAND(DllCall($hDLL, "short", "GetAsyncKeyState", "int", $aKeys[$i])[0], $iKeyDown) Then
;~ 			Do     ; wait until is KeyUp
;~ 			Until Not BitAND(DllCall($hDLL, "short", "GetAsyncKeyState", "int", $aKeys[$i])[0], $iKeyDown)
			Return $i + 1     ; $aKeys[$i] has been clicked
		EndIf
	Next
	Return 0 ; added to be explicit
EndFunc   ;==>_IsPressed

comment:32 Changed 3 months ago by argumentum <argumentum@…>

..the above code is wrong, can't just remove the Do Until loop. I'll fix it in a moment

comment:33 Changed 3 months ago by argumentum <argumentum@…>

Func _IsPressed($vKey, $vDLL = Default, $bCheckModifierKey = False)
	Local Static $hDLL = DllOpen("user32.dll")
	If $vDLL = Default Or $vDLL = "user32.dll" Or $vDLL = "" Then $vDLL = $hDLL
	Local Static $iKeyDown = 0x8000
	Local Static $aModifierKeys[6] = [0, $VK_SHIFT, $VK_CONTROL, $VK_MENU, $VK_LWIN, $VK_RWIN]

	If IsString($vKey) Then
		Local $aCall = DllCall($vDLL, "short", "GetAsyncKeyState", "int", "0x" & $vKey)
		If @error Then Return SetError(@error, @extended, False)
		Return SetExtended(BitAND($aCall[0], 0x01), BitAND($aCall[0], $iKeyDown) <> 0)
	EndIf

	Local $iCounter = 0, $aKeys[1]
	If IsArray($vKey) Then
		; will check on on of them
		$aKeys = $vKey
	Else
		$aKeys[0] = $vKey
	EndIf

	If $bCheckModifierKey Then     ; check no modifier keys
		For $i = 1 To 5
			If BitAND(DllCall($hDLL, "short", "GetAsyncKeyState", "int", $aModifierKeys[$i])[0], $iKeyDown) Then Return SetExtended($i, False)
		Next
	EndIf

	For $i = 0 To UBound($aKeys) - 1
		If BitAND(DllCall($hDLL, "short", "GetAsyncKeyState", "int", $aKeys[$i])[0], $iKeyDown) Then $iCounter += 1
	Next
	Return Int($iCounter = UBound($aKeys)) ; added to be explicit
EndFunc   ;==>_IsPressed

comment:34 Changed 3 months ago by argumentum

I should have waited but ..., anyway, this is better.

Func _IsPressed($vKey, $vDLL = Default, $bCheckModifierKey = False)
	Local Static $hDLL = DllOpen("user32.dll")
	If $vDLL = Default Or $vDLL = "user32.dll" Or $vDLL = "" Then $vDLL = $hDLL
	Local Static $iKeyDown = 0x8000
	Local Static $aModifierKeys[6] = [0, $VK_SHIFT, $VK_CONTROL, $VK_MENU, $VK_LWIN, $VK_RWIN]
	If $bCheckModifierKey = Default Then $bCheckModifierKey = False

	If IsString($vKey) Then $vKey = "0x" & $vKey ; this is enough ; @extended returns last key pressed
	Local $iCounter = 0, $aKeys[1]

	If IsArray($vKey) Then ; will check on on of them
		$aKeys = $vKey
	Else
		$aKeys[0] = $vKey
	EndIf

	If $bCheckModifierKey Then ; check no modifier keys
		For $i = 1 To 5
			If BitAND(DllCall($hDLL, "short", "GetAsyncKeyState", "int", $aModifierKeys[$i])[0], $iKeyDown) Then Return SetExtended($i, False)
		Next
	EndIf

	For $i = 0 To UBound($aKeys) - 1
		$aCall = DllCall($hDLL, "short", "GetAsyncKeyState", "int", $aKeys[$i])
		If BitAND($aCall[0], $iKeyDown) Then $iCounter += 1
	Next
	Return SetExtended(BitAND($aCall[0], 0x01), Int($iCounter = UBound($aKeys)))
EndFunc   ;==>_IsPressed

comment:35 Changed 3 months ago by argumentum

Forgot to declare $aCall

Func _IsPressed($vKey, $vDLL = Default, $bCheckModifierKey = False)
	Local Static $hDLL = DllOpen("user32.dll")
	If $vDLL = Default Or $vDLL = "user32.dll" Or $vDLL = "" Then $vDLL = $hDLL
	Local Static $iKeyDown = 0x8000
	Local Static $aModifierKeys[6] = [0, $VK_SHIFT, $VK_CONTROL, $VK_MENU, $VK_LWIN, $VK_RWIN]
	If $bCheckModifierKey = Default Then $bCheckModifierKey = False

	If IsString($vKey) Then $vKey = "0x" & $vKey ; this is enough ; @extended returns last key pressed
	Local $aCall, $iCounter = 0, $aKeys[1]

	If IsArray($vKey) Then ; will check on on of them
		$aKeys = $vKey
	Else
		$aKeys[0] = $vKey
	EndIf

	If $bCheckModifierKey Then ; check no modifier keys
		For $i = 1 To 5
			If BitAND(DllCall($hDLL, "short", "GetAsyncKeyState", "int", $aModifierKeys[$i])[0], $iKeyDown) Then Return SetExtended($i, False)
		Next
	EndIf

	For $i = 0 To UBound($aKeys) - 1
		$aCall = DllCall($hDLL, "short", "GetAsyncKeyState", "int", $aKeys[$i])
		If BitAND($aCall[0], $iKeyDown) Then $iCounter += 1
	Next
	Return SetExtended(BitAND($aCall[0], 0x01), Int($iCounter = UBound($aKeys)))
EndFunc   ;==>_IsPressed

comment:36 Changed 3 months ago by argumentum <argumentum@…>

the help examples to go with the beta:

#include <Misc.au3>
#include <MsgBoxConstants.au3>
Example()
Func Example()
	Local $aArray[2] = [0x10, 0x12] ; Shift + Alt
	While 1
		If _IsPressed($aArray) Then
			ConsoleWrite("_IsPressed - Shift & Alt Keys were pressed.  (" & @MIN & ':' & @SEC & '.' & @MSEC & ')' & @CRLF)
			Do ; Wait until key is released.
				Sleep(50)
			Until Not _IsPressed($aArray)
			ConsoleWrite("_IsPressed - Shift & Alt Keys were released. (" & @MIN & ':' & @SEC & '.' & @MSEC & ')' & @CRLF)
		ElseIf _IsPressed(0x1B) Then
			MsgBox($MB_SYSTEMMODAL, "_IsPressed", "The Esc Key was pressed, therefore we will close the application.")
			ExitLoop
		EndIf
		Sleep(50)
	WEnd
EndFunc   ;==>Example
#include <Misc.au3>
#include <MsgBoxConstants.au3>
Example()
Func Example()
	While 1
		If _IsPressed(0x71, Default, True) Then
			ConsoleWrite("_IsPressed - F2 (without any modifier keys pressed) was pressed." & @CRLF)
			; Wait until key is released.
			While _IsPressed("71", Default, True)
				Sleep(50)
			WEnd
			ConsoleWrite("_IsPressed - F2 (without any modifier keys pressed) was released." & @CRLF)
		ElseIf _IsPressed("1B") Then
			MsgBox($MB_SYSTEMMODAL, "_IsPressed", "The Esc Key was pressed, therefore we will close the application.")
			ExitLoop
		EndIf
		Sleep(50)
	WEnd
EndFunc   ;==>Example

comment:37 Changed 3 months ago by argumentum <argumentum@…>

hmm, I should not have use the magic number ( 0x71 ), but the VK_ constant.

comment:38 Changed 3 months ago by Jpm

Thanks but the intention with the array is to watch one of the key not both
can you adapt it and your proposal if needed
Cheers

here is the example i want to work with

#include <Misc.au3>
#include <MsgBoxConstants.au3>
#include <WinAPIvkeysConstants.au3>

Local $sModifierKeys[6] = [0, "VK_SHIFT", "VK_CONTROL", "VK_MENU", "VK_LWIN", "VK_RWIN"]

Local $aKeys[3] = [$VK_ESCAPE, $VK_LBUTTON, $VK_TAB]
While 1
	Local $iRet = _IsPressed_($aKeys, Default, True) ; Check no modifier
	If @extended Then ConsoleWrite("The modifier key " & $sModifierKeys[@extended] & " has been stroked. @extended = " & @extended & @CRLF)

	Local $sKey
	Switch $iRet
		Case 1 ; Keyboard ESC
			$sKey = "{ESCAPE}"
			ExitLoop

		Case 2 ; MouseClick Left
			$sKey = "{LBUTTON}"
			ExitLoop

		Case 3 ; Keyboard Tab
			$sKey = "{TAB}"
			ExitLoop

	EndSwitch

	Sleep(100)
WEnd

ConsoleWrite("The key " & $sKey & " has been stroked." & @CRLF)
MsgBox($MB_SYSTEMMODAL, "Result", "The key " & $sKey & " has been stroked.", 2)

comment:39 Changed 3 months ago by argumentum <argumentum@…>

Ok, context. Then retuning TRUE or FALSE. How about retuning my desired data in the return as in

Int($iCounter = UBound($aKeys)) + BitAND($aCall[0], 0x01)

that way, is true either way but truer ( 2 ) if the LSB is present.
Then @extended is reserved for the modifier keys and the return will be true ( by means of 1 or 2 ). That would not brake anyone's existing code either.
I would then use:

If __IsPressed("B3") > 1 Then ....

Is that a good solution ?

comment:40 Changed 3 months ago by argumentum <argumentum@…>

..nope.
Read your original code for the beta and my solution made so sense. ( because it is none sense ). If the extended is the LSB while retuning TRUE then there is no odd result between the expectations.
Cheers

comment:41 Changed 3 months ago by argumentum <argumentum@…>

..then in your example, it'd need to change this line:

	Local $iRet = _IsPressed_($aKeys, Default, True) ; Check no modifier
	If Not $iRet And @extended Then ConsoleWrite("The modifier key " & $sModifierKeys[@extended] & " has been stroked. @extended = " & @extended & @CRLF)

comment:42 Changed 3 months ago by argumentum

..this is what I mean, by adding the not return to the example.
( I this was the way it would be coded )

#include <Misc.au3>
#include <MsgBoxConstants.au3>
#include <WinAPIvkeysConstants.au3>

Local $sModifierKeys[6] = [0, "VK_SHIFT", "VK_CONTROL", "VK_MENU", "VK_LWIN", "VK_RWIN"]

Local $aKeys[3] = [$VK_ESCAPE, $VK_LBUTTON, $VK_TAB]
While 1
	Local $iRet = _IsPressed_($aKeys, Default, True) ; Check no modifier
	If Not $iRet and @extended Then ConsoleWrite("The modifier key " & $sModifierKeys[@extended] & " has been stroked. @extended = " & @extended & @CRLF)

	Local $sKey
	Switch $iRet
		Case 1 ; Keyboard ESC
			$sKey = "{ESCAPE}"
			ExitLoop

		Case 2 ; MouseClick Left
			$sKey = "{LBUTTON}"
			ExitLoop

		Case 3 ; Keyboard Tab
			$sKey = "{TAB}"
			ExitLoop

	EndSwitch

	Sleep(100)
WEnd

ConsoleWrite("The key " & $sKey & " has been stroked. (LSB: " & @extended & ")" & @CRLF)
MsgBox($MB_SYSTEMMODAL, "Result", "The key " & $sKey & " has been stroked.", 2)

Func _IsPressed_($vKey, $vDLL = Default, $bCheckModifierKey = False)
	Local Static $hDLL = DllOpen("user32.dll")
	If $vDLL = Default Or $vDLL = "user32.dll" Or $vDLL = "" Then $vDLL = $hDLL
	Local Static $iKeyDown = 0x8000
	Local Static $aModifierKeys[6] = [0, $VK_SHIFT, $VK_CONTROL, $VK_MENU, $VK_LWIN, $VK_RWIN]
	If $bCheckModifierKey = Default Then $bCheckModifierKey = False

	If IsString($vKey) Then $vKey = "0x" & $vKey

	Local $aCall, $aKeys[1]
	If IsArray($vKey) Then
		; will check on on of them
		$aKeys = $vKey
	Else
		$aKeys[0] = $vKey
	EndIf

	If $bCheckModifierKey Then ; check no modifier keys
		For $i = 1 To 5
			If BitAND(DllCall($hDLL, "short", "GetAsyncKeyState", "int", $aModifierKeys[$i])[0], $iKeyDown) Then Return SetExtended($i, False)
		Next
	EndIf

	For $i = 0 To UBound($aKeys) - 1
		$aCall = DllCall($hDLL, "short", "GetAsyncKeyState", "int", $aKeys[$i])
		If BitAND($aCall[0], $iKeyDown) Then Return SetExtended(BitAND($aCall[0], 0x01), $i + 1) ; $aKeys[$i] has been clicked
	Next

EndFunc   ;==>_IsPressed

comment:43 Changed 2 months ago by Jpm

THanks
do you agree with my last proposal allowing to DllClose te default Dll and also to wait the modifier being unstroke to avoid multiple Modifier notification

#include <Misc.au3>
#include <MsgBoxConstants.au3>
#include <WinAPIvkeysConstants.au3>

Local $sModifierKeys[6] = [0, "VK_SHIFT", "VK_CONTROL", "VK_MENU", "VK_LWIN", "VK_RWIN"]

Local $aKeys[3] = [$VK_ESCAPE, $VK_LBUTTON, $VK_TAB]
While 1
	Local $iRet = _IsPressed_($aKeys, Default, True) ; Check no modifier
	If Not $iRet And @extended Then ConsoleWrite("The modifier key " & $sModifierKeys[@extended] & " has been stroked. @extended = " & @extended & @CRLF)

	Local $sKey
	Switch $iRet
		Case 1 ; Keyboard ESC
			$sKey = "{ESCAPE}"
			ExitLoop

		Case 2 ; MouseClick Left
			$sKey = "{LBUTTON}"
			ExitLoop

		Case 3 ; Keyboard Tab
			$sKey = "{TAB}"
			ExitLoop

	EndSwitch

	Sleep(100)
WEnd

_IsPressed_() ; to DLLClose the the default Dll created by the previous _IsPressed_(...)

ConsoleWrite("The key " & $sKey & " has been stroked. (LSB: " & @extended & ")" & @CRLF)
MsgBox($MB_SYSTEMMODAL, "Result", "The key " & $sKey & " has been stroked.", 2)

Send("^z") ; to avoid change in the Scite Window

Func _IsPressed_($vKey = Default, $vDLL = Default, $bCheckModifierKey = False)
	Local Static $hDLL = Null
	If $vKey = Default Then
		; to DLLClose the the default Dll created by the previous _IsPressed_(...)
		DllClose($hDLL)
		$hDLL = Null
		Return True
	EndIf

	If $vDLL = Default Or $vDLL = "user32.dll" Or $vDLL = "" Then $vDLL = $hDLL
	If $hDLL = Null Then $hDLL = DllOpen("user32.dll")

	Local Static $iKeyDown = 0x8000
	Local Static $aModifierKeys[6] = [0, $VK_SHIFT, $VK_CONTROL, $VK_MENU, $VK_LWIN, $VK_RWIN]
	If $bCheckModifierKey = Default Then $bCheckModifierKey = False

	If IsString($vKey) Then $vKey = "0x" & $vKey

	Local $aCall, $aKeys[1]
	If IsArray($vKey) Then
		; will check on on of them
		$aKeys = $vKey
	Else
		$aKeys[0] = $vKey
	EndIf

	If $bCheckModifierKey Then ; check no modifier keys
		For $i = 1 To 5
			If BitAND(DllCall($hDLL, "short", "GetAsyncKeyState", "int", $aModifierKeys[$i])[0], $iKeyDown) Then
				Do
					Sleep(250)
				Until BitAND(DllCall($hDLL, "short", "GetAsyncKeyState", "int", $aModifierKeys[$i])[0], $iKeyDown) = 0
				Return SetExtended($i, False)
			EndIf
		Next
	EndIf

	For $i = 0 To UBound($aKeys) - 1
		$aCall = DllCall($hDLL, "short", "GetAsyncKeyState", "int", $aKeys[$i])
		If BitAND($aCall[0], $iKeyDown) Then Return SetExtended(BitAND($aCall[0], 0x01), $i + 1) ; $aKeys[$i] has been clicked
	Next

EndFunc   ;==>_IsPressed_

comment:44 Changed 2 months ago by argumentum <argumentum@…>

in the "If $bCheckModifierKey Then"

Do
	Sleep(250)
Until

locks a script. Any function should be as non-locking as possible.

At "Local Static $hDLL = Null", might as well load "user32.dll" as the next line will close it if called. And for what I see, I made the mistake of running everything out of $hDLL instead of $vDLL.

Ok, these are my notes:

#include <Misc.au3>
#include <MsgBoxConstants.au3>
#include <WinAPIvkeysConstants.au3>

Local $sModifierKeys[6] = [0, "VK_SHIFT", "VK_CONTROL", "VK_MENU", "VK_LWIN", "VK_RWIN"]

Local $aKeys[3] = [$VK_ESCAPE, $VK_LBUTTON, $VK_TAB]
While 1
	Local $iRet = _IsPressed_($aKeys, Default, True) ; Check no modifier
	If Not $iRet And @extended Then ConsoleWrite("The modifier key " & $sModifierKeys[@extended] & " has been stroked. @extended = " & @extended & @CRLF)

	Local $sKey
	Switch $iRet
		Case 1 ; Keyboard ESC
			$sKey = "{ESCAPE}"
			ExitLoop

		Case 2 ; MouseClick Left
			$sKey = "{LBUTTON}"
			ExitLoop

		Case 3 ; Keyboard Tab
			$sKey = "{TAB}"
			ExitLoop

	EndSwitch

	Sleep(100)
WEnd

; ..will self clean with OnAutoItExitRegister
;~ _IsPressed_() ; to DLLClose the the default Dll created by the previous _IsPressed_(...)

ConsoleWrite("The key " & $sKey & " has been stroked. (LSB: " & @extended & ")" & @CRLF)
MsgBox($MB_SYSTEMMODAL, "Result", "The key " & $sKey & " has been stroked.", 2)

Send("^z") ; to avoid change in the Scite Window

Func __IsPressed_Close()
	_IsPressed_()
EndFunc

Func _IsPressed_($vKey = Default, $vDLL = Default, $bCheckModifierKey = False)
	Local Static $hDLL = Null
	If $hDLL = Null Then
		$hDLL = DllOpen("user32.dll")
		OnAutoItExitRegister(_IsPressed_Close)
	EndIf
	If $vKey = Default Then
		; to DLLClose the the default Dll created by the previous _IsPressed_(...)
		DllClose($hDLL)
		$hDLL = Null
		Return SetExtended(100, False) ; True should be False as to avoid furure mishaps. An imposible extended would suffice
	EndIf

	If $vDLL = Default Or $vDLL = "user32.dll" Or $vDLL = "" Then $vDLL = $hDLL

	Local Static $iKeyDown = 0x8000
	Local Static $aModifierKeys[6] = [0, $VK_SHIFT, $VK_CONTROL, $VK_MENU, $VK_LWIN, $VK_RWIN]
	If $bCheckModifierKey = Default Then $bCheckModifierKey = False ; unless bitwise, then 0

	If IsString($vKey) Then $vKey = "0x" & $vKey

	Local $aCall, $aKeys[1]
	If IsArray($vKey) Then
		; will check on on of them
		$aKeys = $vKey
	Else
		$aKeys[0] = $vKey
	EndIf

	If $bCheckModifierKey Then ; check no modifier keys
		For $i = 1 To 5
			If BitAND(DllCall($vDLL, "short", "GetAsyncKeyState", "int", $aModifierKeys[$i])[0], $iKeyDown) Then
;~ 				Do ; this should be removed as it'd lock the script until the codition changes, or maybe
;~ 					Sleep(250) ;       make $bCheckModifierKey bitwise( 1 + 2 ) for a user purposely deciding to wait for the condition.
;~ 				Until BitAND(DllCall($vDLL, "short", "GetAsyncKeyState", "int", $aModifierKeys[$i])[0], $iKeyDown) = 0
				Return SetExtended($i, False)
			EndIf
		Next
	EndIf

	For $i = 0 To UBound($aKeys) - 1
		$aCall = DllCall($vDLL, "short", "GetAsyncKeyState", "int", $aKeys[$i])
		If BitAND($aCall[0], $iKeyDown) Then Return SetExtended(BitAND($aCall[0], 0x01), $i + 1) ; $aKeys[$i] has been clicked
	Next

EndFunc   ;==>_IsPressed_

comment:45 Changed 2 months ago by argumentum <argumentum@…>

.. am no used to not edit/change the post =(
"IsPressed_Close" is named wrong at "OnAutoItExitRegister(_IsPressed_Close)", should be "OnAutoItExitRegister(IsPressed_Close)".

comment:46 Changed 2 months ago by argumentum <argumentum@…>

... and "double underscore" is removed while in this text =/

comment:47 Changed 2 months ago by argumentum

Thinking..., Better not to use the Default in $vKey. Better to use an impossible parameter like "CleanUp" to do the clean up given the the clean up is now automated on exit by an internal function.

#include <Misc.au3>
#include <MsgBoxConstants.au3>
#include <WinAPIvkeysConstants.au3>

Local $sModifierKeys[6] = [0, "VK_SHIFT", "VK_CONTROL", "VK_MENU", "VK_LWIN", "VK_RWIN"]

Local $aKeys[3] = [$VK_ESCAPE, $VK_LBUTTON, $VK_TAB]
While 1
	Local $iRet = _IsPressed_($aKeys, Default, True) ; Check no modifier
	If Not $iRet And @extended Then ConsoleWrite("The modifier key " & $sModifierKeys[@extended] & " has been stroked. @extended = " & @extended & @CRLF)

	Local $sKey
	Switch $iRet
		Case 1 ; Keyboard ESC
			$sKey = "{ESCAPE}"
			ExitLoop

		Case 2 ; MouseClick Left
			$sKey = "{LBUTTON}"
			ExitLoop

		Case 3 ; Keyboard Tab
			$sKey = "{TAB}"
			ExitLoop

	EndSwitch

	Sleep(100)
WEnd

; ..will self clean with OnAutoItExitRegister
;~ _IsPressed_() ; to DLLClose the the default Dll created by the previous _IsPressed_(...)

ConsoleWrite("The key " & $sKey & " has been stroked. (LSB: " & @extended & ")" & @CRLF)
MsgBox($MB_SYSTEMMODAL, "Result", "The key " & $sKey & " has been stroked.", 2)

Send("^z") ; to avoid change in the Scite Window

Func __IsPressed_Close()
	_IsPressed_("CleanUp")
EndFunc

Func _IsPressed_($vKey, $vDLL = Default, $bCheckModifierKey = False)
	Local Static $hDLL = Null
	If $hDLL = Null Then
		$hDLL = DllOpen("user32.dll")
		OnAutoItExitRegister(__IsPressed_Close)
	EndIf
	If $vKey = "CleanUp" Then
		; to DLLClose the the default Dll created by the previous _IsPressed_(...)
		DllClose($hDLL)
		$hDLL = Null
		Return SetExtended(100, False) ; True should be False as to avoid furure mishaps. An imposible extended would suffice
	EndIf

	If $vDLL = Default Or $vDLL = "user32.dll" Or $vDLL = "" Then $vDLL = $hDLL

	Local Static $iKeyDown = 0x8000
	Local Static $aModifierKeys[6] = [0, $VK_SHIFT, $VK_CONTROL, $VK_MENU, $VK_LWIN, $VK_RWIN]
	If $bCheckModifierKey = Default Then $bCheckModifierKey = False ; unless bitwise, then 0

	If IsString($vKey) Then $vKey = "0x" & $vKey

	Local $aCall, $aKeys[1]
	If IsArray($vKey) Then
		; will check on on of them
		$aKeys = $vKey
	Else
		$aKeys[0] = $vKey
	EndIf

	If $bCheckModifierKey Then ; check no modifier keys
		For $i = 1 To 5
			If BitAND(DllCall($vDLL, "short", "GetAsyncKeyState", "int", $aModifierKeys[$i])[0], $iKeyDown) Then
;~ 				Do ; this should be removed as it'd lock the script until the codition changes, or maybe
;~ 					Sleep(250) ;       make $bCheckModifierKey bitwise( 1 + 2 ) for a user purposely deciding to wait for the condition.
;~ 				Until BitAND(DllCall($vDLL, "short", "GetAsyncKeyState", "int", $aModifierKeys[$i])[0], $iKeyDown) = 0
				Return SetExtended($i, False)
			EndIf
		Next
	EndIf

	For $i = 0 To UBound($aKeys) - 1
		$aCall = DllCall($vDLL, "short", "GetAsyncKeyState", "int", $aKeys[$i])
		If BitAND($aCall[0], $iKeyDown) Then Return SetExtended(BitAND($aCall[0], 0x01), $i + 1) ; $aKeys[$i] has been clicked
	Next

EndFunc   ;==>_IsPressed_

comment:48 Changed 2 months ago by Jpm

Hi I don't thing the OnAutoItExitRegister(IsPressed_Close)
is needed as the closing of AutoIt will release all resources attached to the process
For the "cleanup" the Null value is different from a valid vKey so why not to use it.

About the Sleep(250), I am not happy to not do it as Getting Same event again without any modification of the user interaction is bad
so I will leave it

comment:49 Changed 2 months ago by argumentum <argumentum@…>

About the Sleep(250), I am not happy to not do it

Then make it a bitwise. At least to have an option to not use it.

For the "cleanup" the Null value is different from a valid vKey so why not to use it.

Yes, that is an invalid call. Sounds good.

comment:50 Changed 2 months ago by argumentum <argumentum@…>

Yes, that is an invalid call. Sounds good.
Should have typed parameter. Is an invalid parameter =)

comment:51 Changed 2 months ago by Jpm

So I will stay with my proposal

comment:52 Changed 2 months ago by Jpm

  • Milestone set to 3.3.17.0
  • Owner set to Jpm
  • Resolution set to Completed
  • Status changed from new to closed

Added by revision [13032] in version: 3.3.17.0

Guidelines for posting comments:

  • You cannot re-open a ticket but you may still leave a comment if you have additional information to add.
  • In-depth discussions should take place on the forum.

For more information see the full version of the ticket guidelines here.

Add Comment

Modify Ticket

Action
as closed The owner will remain Jpm.
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.