Register Hotkey with Only Modifiers in Linux

Is there a way for my binary to react to some global hotkeys in Linux?

How global do your hotkeys need to be? Is it enough for them to be global for a X session? In that case you should be able to open an Xlib connection and listen for the events you need.

Ordinarily keyboard events in X are delivered to the window that currently has the focus, and propagated up the hierarchy until they are handled. Clearly this is not what we want. We need to process the event before any other window can get to it. We need to call XGrabKey on the root window with the keycode and modifiers of our hotkey to accomplish this.

I found a good example here.

Programming a hotkey utility

For C#/.Net and a low-level hook (using Interops), have a look at Stephen Toubs example here (complete with source):

Low-Level Keyboard Hook in C#

For a more complete example (also C#/.Net and Interops), but with everthing neatly contained in a separate class, including trapping key presses using normal Events on the client side, check this out (the link to the actual source is in the blog text, a bit tricky to spot):

Global hotkeys with .NET

Prevent Hotkeys Overlapping [Unity]

Okay, so although I said that I decided to leave it as it was in my comment to Daniel Bereza I had one last idea which really seemed the solution I was searching for, and it worked!

I decided to answer myself in case any other person needs a workaround to this.

Basically, what I was doing was checking at every frame if the modifier keys of the Hotkey were pressed and if none of the others was pressed. This was done by 2 loops (one for the modifiers which needed to be pressed and another for the ones which didn't need to be pressed).

To replace those loops I used bitwise operations. The trick is to store a flagged enum (in my case of type ModifierKeys) which tells what modifier keys are currently pressed and another flagged enum which is the complementary, lets say:

private static ModifierKeys pressedModifiers
private static ModifierKeys unpressedModifiers

These two fields are updated via InputAction events previously bounded to each of the modifier keys, so when a modifier key gets pressed, you add the ModifierKey flag to the pressedModifiers and at the same time you update the complementary unpressedModifiers.

Then to check if it's pressed, for example, it's simple:

public bool IsPressed => modifiersToBePressed == pressedModifiers && ((int) (unpressedModifiers & modifiersToBePressed) == 0) && Keyboard.current[this.Key].isPressed

And that's it, if you want to check if it has been pressed or released on the current frame simply change the Keyboard.current[this.Key].isPressed part for whatever you need.

I hope I have explained myself well. I tested it and it seems to work as expected, unless there's something that I didn't take into account. I also tested the performance and while the other way took in average 100-200 microseconds for 600 hotkey evaluations this workaround only takes 5-15. It's quite an improvement for me.



Related Topics



Leave a reply



Submit