evilC / AutoHotInterception

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

Ignore key up when using subscriptions #23

Closed Revvilo closed 5 years ago

Revvilo commented 5 years ago

In an effort to retain performance for gaming, I've decided on using key subscriptions to avoid possible delays Contextual Hotkeys may cause on my main keyboard. But the key up being fired has created a bit of a roadblock for me. Currently, it seems like I will have to add a check inside every single key's callback to suppress the key up event, since, for most hotkeys, I only want them firing once when tapped. Obviously, that is pretty cumbersome and messy looking, which is why I would like to ask; Is there something I can do that I'm missing, or is it just something that hasn't been added?

Of course, I might not actually be avoiding possible delays in the first place, and in which case, I could probably just use contextual binds. I just prefer the way functions look/behave in most cases.

Revvilo commented 5 years ago

Actually, from having a look in the Monitor.ahk script, it looks like I might be able to simply catch all keypresses from my secondary keyboard, which would be way cleaner than adding a subscription for every single key. ~Unfortunately, since I couldn't find any docs on it, nor do I seem clued-up enough to reverse engineer what the script is doing, I don't think I'll be able to work out how to use it correctly on my own.~ Well nevermind. I was able to get the entire keyboard captured by just doing a SetDeviceFilterState on an AutoHotInterception "monitor," like Monitor.ahk does. My issue now is blocking the keypress from the OS.

I would still prefer using subscriptions in the long run as it would provide a cleaner AHK script for things like modifier keys and the likes, rather than just a tower of If/Elses. So the original issue still stands.

evilC commented 5 years ago

The Monitor is incapable of blocking.
As the code currently stands, you would have to declare a subscription to every key on the keyboard, and set the block parameter to true.
Then, for things you did not want to block, send the output (eg using AHI.SendKeyEvent())
Do you want to subscribe to all keys, or just block everything and subscribe to some?
If you want keys to behave differently if you tap or hold them, then you can use TapHoldManager, which is compatible with AHI.

Revvilo commented 5 years ago

Ah, okay. Too bad. I'm actually finding the key up event useful a lot more than I thought I would, so it doesn't seem to be as big of an issue as it looked like it'd be. And yes, subscribing and blocking the whole keyboard is the goal. TapHoldManager looks awesome, I'll look into using it. The documentation is great too, but is this line correct? You need to add the files from AutoHotInterception's lib folder to AutoHotInterception's lib folder. It doesn't seem to make sense in my head. What I originally had in mind was a way to blanket-cancel the key up event to avoid script bloat, but honestly at this point I seem to be chugging a long fine, was probably a bit too apprehensive. So I'd say we could close this one. I appreciate you letting me know about THM, though.

evilC commented 5 years ago

I corrected the docs, thanks.

If you want to subscribe to every key on the keyboard, you could use a function like this:

AHI := new AutoHotInterception()

keyboardId := AHI.GetKeyboardId(0x04F2, 0x0112)

Loop 512 {
    code := Format("{:x}", A_Index)
    name := GetKeyName("sc" code)
    if (name != ""){
        AHI.SubscribeKey(keyboardId, A_Index, true, Func("KeyEvent").Bind(code, name))
    }
}

KeyEvent(code, name, state){

}

All the hotkeys would go through one function though, but it's either that or 200-odd functions

Revvilo commented 5 years ago

Oh jeez. Ok yeah, that might have been a good idea to use first... might've taken me a while to come up with that, so I appreciate the jump-start! I just finished migrating the big tower of If\Elses into their own little functions for each key on the keyboard, though, so I've gotta decide what I'm going to use. Real quick tho, what happens if I add a key subscription after all the keys have been registered using your above script? Will it override the original callback and use my custom one, or will I have to skip keys from the loop that I want to have custom callbacks set later in the script?

evilC commented 5 years ago

It will override.
I suppose you don't have to go with either one func per key or one func for all, you could split it up.
eg you could maybe have a "NumberKeys" func and a "FunctionKeys" func.
Bear in mind AHI.SubscribeKey(keyboardId, A_Index, true, Func("KeyEvent").Bind(code, name)) just passes a string ("KeyEvent") as the function name, so you could have an array that told it which function name to use for each key name or code.
eg:

numberNames := ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]
functionNames := ["F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12"] 

Loop 512 {
    code := Format("{:x}", A_Index)
    name := GetKeyName("sc" code)
    if (name != ""){
        if (numberNames.HasKey(name)){
            funcName := "NumberKeyEvent"
        } else if (functionNames.HasKey(name)){
            funcName := "FuncKeyEvent"
        } else {
            funcName := "KeyEvent"
        }
        AHI.SubscribeKey(keyboardId, A_Index, true, Func(funcName).Bind(code, name))
    }
}
Revvilo commented 5 years ago

Awesome. And yes, that's what I was originally doing to avoid a big copy-paste tower of subscriptions, but man, this is taking it to a new level. So many ideas, but it's very late here. You've given me a heap of ammo to throw at this, so I'll get some sleep and put a refreshed mind to it and see how well I can do in getting a layout that works well with my mode and modifier key scripts. Many thanks for your suggestions. Extremely helpful :)

Revvilo commented 5 years ago

So in regards to the original issue and now knowing that I can use .Bind(); I subscribed all keys to the same callback, binding the code and name like evilC said, along with another boolean argument to skip the key up event:

AHI.SubscribeKey(keyboardId, A_Index, true, Func("SecondKeyboard").Bind(code, name, skipKeyUp))

and then did all the processing I needed inside SecondKeyboard(code, name, skipKeyUp, state) based on the arguments, for example;

; If skipKeyUp is true, and it's the key up event, stop processing.
If ((!state) && skipKeyUp)
    Return

Then by using the name argument (and overwriting it for keys like "[" and "\"), calling the key's corresponding callback which have now been put in their own classes to allow for mode changing. Pretty happy with how it turned out because now I can do basically anything I need inside the "proxy" (?) function that all the keys rout through.