Input hook's OnKeyUp callback mysteriously catching Key Up events when it should not

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
User avatar
child_fs
Posts: 5
Joined: 05 Feb 2022, 12:58
Contact:

Input hook's OnKeyUp callback mysteriously catching Key Up events when it should not

Post by child_fs » 19 Mar 2023, 20:38

I'm having a hard time with some weird input hook behavior which I can't explain.

I'm trying to code some interaction between a pair of hotkeys and an input hook that goes something like what follows:

I'll have a pair of down/up hotkeys defined for a certain key (both at Input Level 100). In their function bodies I'll make calls to SendEvent "{Blind}{Thiskey DownR/Up}" (both at Send Level 100).

At some point during execution, I'll have an input hook in progress with OnKeyDown/Up callbacks defined for that same key.

What I want to do is: I want to control whether the input hook will capture that key's down/up inputs by setting the input hook's MinSendLevel back and forth from 100 to 101 (that is, without having to turn off/on the input hook).

By reading the documentation for InputHook, it seemed like a simple enough task, so I set out to do it and the code structure looks somewhat like this:

Code: Select all

#Requires AutoHotkey v2.-  

; These are here just to support debugging
    ttx := A_ScreenWidth //3
    tty := A_ScreenHeight//3
    
ih := InputHook("I101")
ih.KeyOpt("z", "+N")
ih.OnKeyDown := OnKeyDown
ih.OnKeyUp := OnKeyUp
ih.VisibleText := True

OnKeyDown(ih, vk, sc) {
    ; These are here just to support debugging
        ToolTip , , , 2
        ToolTip "ih Down `nA_SendLevel: " A_SendLevel, ttx, tty, 1
}
OnKeyUp(ih, vk, sc) {
    ; These are here just to support debugging
        ToolTip "ih Up `nA_SendLevel: " A_SendLevel, ttx, tty+(18)*2, 2
        SetTimer ()=>(ToolTip(,,,1),ToolTip(,,,2)), -4000
}
    
#InputLevel 100
z:: 
d(hk) {
    ; These are here just to support debugging
        ToolTip , , , 4
        ToolTip "hk Down `nA_SendLevel: " A_SendLevel, ttx-200, tty, 3

    ; At SendLevel 100
    SendEvent "{Blind}{z DownR}"
}
z Up::
u(hk) {
    ; These are here just to support debugging
        ToolTip "hk Up `nA_SendLevel: " A_SendLevel, ttx-200, tty+(18)*2, 4
        SetTimer ()=>(ToolTip(,,,3),ToolTip(,,,4)), -4000

    ; At SendLevel 100
    SendEvent "{Blind}{z Up}"
}
; 

; At some point during execution:
ih.Start()	; It starts out with MinSendLevel 101, so it should neither capture z Down, nor z Up from my hotkeys

; At some later time during execution:
ih.MinSendLevel := 100	; it should start capturing z Down/Up inputs from the hotkeys

; And then:
; ih.MinSendLevel := 101	; it should stop capturing z Down/Up inputs without turning the input hook off
In my mind, that was pretty simple and straight forward. An input hook with MinSendLevel 101 will capture absolutely no inputs from SendEvent, while the same input hook, at MinSendLevel 100, will only capture inputs from SendEvent at Send Level 100.

However, what happens is pretty strange: the input hook always captures Key Up events, even when MinSendLevel is set to 101.

What boggles my mind is that when it comes to Key Down events, the interaction hotkeys/input hook seems to work perfectly fine: the input hook will ignore or capture Key Down events according to how its MinSendLevel compares to the event's Send Level, exactly as you would expect; as for Key Up events, it will always capture them...

To add some more context, here's a picture of the crime scene at MinSendLevel 101:
Image

And here's a pic of it at MinSendLevel 100:
Image

At first glance, you'd think it's behaving normally at MinSendLevel 100. However, looking closely at the picture, you can see that it's still misbehaving and capturing the Key Up event before, as well as AFTER, SendEvent is called.

That's pretty strange to me and I can't put my finger on what is going on... Is this a bug? Am I missing something from the documentation? I've been scratching my head for so long I think I'm going bald...

Can somebody help me, please?

Thanks in advance, anyways!

Btw, I'm using version 2.0.2 and had no other scripts active during the tests.

lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: Input hook's OnKeyUp callback mysteriously catching Key Up events when it should not

Post by lexikos » 02 Jul 2023, 01:23

The issue isn't InputHook catching events it shouldn't, but that the physical release of z is not being suppressed due to interference from Send. Because it is not suppressed, it is passed on to the InputHook.

The hook is designed to pass or suppress a key-up event based on whether the corresponding key-down was suppressed. If the key-down was not suppressed, we generally don't want to suppress the key-up since that would cause the key to effectively become stuck down. If the key-down was suppressed, there's less harm in failing to suppress the key-up, but in some cases an orphaned key-up does have an unwanted effect, so the hook is designed to suppress it.

For example, if you have z:: and z up:: (or just the latter) and they are disabled via #HotIf at the time the z key is pressed, it won't be suppressed. If the condition then changes and allows z up:: to fire, z won't have been suppressed, so the hook is not supposed to suppress it even though it lacks the ~ modifier. Complication arises because #InputLevel needs to work like #HotIf, but at the same time, simulated (SendLevel > 0) input sometimes needs to affect hotkey state and sometimes not.

In this case, the hook is finding a potential hotkey for your sent {z DownR}, but then deciding that none of its variants meet both the #HotIf and #InputLevel conditions for firing. This is assumed to be an actual key-press, and since it is still possible for the key-release to trigger a hotkey, a flag is set to prevent suppression of the next key-release. The next key-release happens to be from the physical z key, so that key-up isn't suppressed.

There is some logic in place to identify events sent by the script and ignore them more completely if they aren't firing a hotkey, but this was reached after the flag had already been set - in v2.0.3 and v1.1.36.02. This will be fixed.

Post Reply

Return to “Ask for Help (v2)”