evilC / AutoHotInterception

An AutoHotkey wrapper for the Interception driver
MIT License
707 stars 64 forks source link

Press-and-hold character input delay in full remap of HID remote #87

Open hmhackmaster opened 2 years ago

hmhackmaster commented 2 years ago

I am attempting to remap a USB wireless remote thingy that, by default, sends single-letter keyboard inputs to the OS and instead sent a "+KEY" input to Windows. I have successfully ran the monitor and figured out the corresponding key names and scancodes and built out a quick test but I have encountered a problem: When I hold down a key on my remote I am wanting a constant output of keystrokes without the Windows default behavior of inserting a short delay after the first keystroke.

Without the below script running, if I single-press the Up button on my remote, Windows would receive a 'w' key. If I press and hold the up button, I will get a single key, a short pause, then the keys again as if the keyboard character is being held down: "w.....wwwwwwwww" (thanks to Windows' Character Repeat Delay, I reckon)

With the script running, the same delay occurs, but only with my new key. My assumption prior to drafting this issue was my delay was coming from the inefficient script (the full script will end up having 14 buttons to translate), but now I am beginning to suspect that perhaps Windows is adding the delay before AHK/Interception is able to receive the key inputs.

Do you have any thoughts @evilC? Thanks!

#SingleInstance force
#Persistent
#include Lib\AutoHotInterception.ahk

global AHI := new AutoHotInterception()
keyboardId := AHI.GetKeyboardId(0x348F, 0x1010) ; HID Keyboard ID for Remote's USB Dongle
AHI.SubscribeKeyboard(keyboardId, true, Func("KeyEvent"))

KeyEvent(code,state){
    ;ToolTip % "Keyboard Key - Code: " code ", State: " state

    if (code = 2) 
    {
        SendInput t ; for testing
    }
    else if (code = 17) ; key: w
    {
        SendInput !{up} ; send the hotkey ALT + Up arrow
        ; SendInput u   ; TESTING: just send a u
    }
    else if (code = 31) ; key: s
    {
        SendInput !{down} ; send the hotkey ALT + Down arrow
    }   
    else if (code = 30) ; key: a
    {
        SendInput !{left} ; send the hotkey ALT + Left arrow
    }   
    else if (code = 32) ; key d
    {
        SendInput !{right} ; send the hotkey ALT + Right arrow
    }
    else if (code = 38) ; key: l
    {
        ExitApp ; Testing: Map a key on the remote to quit the script
    }
    else
    {
        ToolTip % "Keyboard Key - Code: " code ", State: " state ; If input is unrecognized, let's output the code
    }
}

^Esc::
    ExitApp
evilC commented 1 year ago

Could do with clarification here It's the line "With the script running, the same delay occurs, but only with my new key" that is confusing me, specifically the "only with my new key" bit.

Key repeat is a tricky one, and it highly depends on how the application that is receiving inputs behaves. Key repeat only affects press events - that is, if you hold a key, you (normally) get presss, press, press, press while you hold the key, then one release at the end when you release the key. If your device behaves differently, it is off-spec and more difficult to deal with.

In general though, you can use code like this to morph repeated keys in various ways ie here is code that will filter key repeats

#SingleInstance force
#Persistent
#include Lib\AutoHotInterception.ahk

global AHI := new AutoHotInterception()
keyboardId := AHI.GetKeyboardId(0x348F, 0x1010) ; HID Keyboard ID for Remote's USB Dongle
AHI.SubscribeKeyboard(keyboardId, true, Func("KeyEvent"))

KeyEvent(code,state){
    static states := {} ; Array used to hold current state of keys. STATIC is important!

    if (state && states[code]){  ; evaluates to true if key is already held
        return ; filter key repeat
    }

    if (code = 2) 
    {
        SendInput t ;
    }
    else if (code = 17) ; key: w
    {
        SendInput !{up}
    }

     ; Update the states array with the current state of this key
    states[code] := state
}

If you want key repeat, but want to remove the delay, you can use similar code, but add a call to SetTimer to repeatedly send the key while it is held