Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

App Specific Bindings #51

Closed
canadaduane opened this issue Nov 19, 2021 · 8 comments
Closed

App Specific Bindings #51

canadaduane opened this issue Nov 19, 2021 · 8 comments
Labels

Comments

@canadaduane
Copy link
Contributor

I'm curious if there's interest in supporting getting the current active window, and automatically switching to different keyboard layers.

A simple use case: VSCode uses Ctrl+, as the "preferences" shortcut key, but Firefox uses "Alt+e, n" (and can't be configured without a special plugin). I'd like to unify this behavior so that Ctrl+, always gets me to preferences.

Two other key remapping tools accomplish this in X11: xkeysnail and autokey. Unfortunately, they both do not support Wayland.

It turns out that in Wayland, getting the "current active window" is not supported. Instead, they punt on it and ask that a layer above handles that task, e.g. at gnome-shell:

gdbus call --session \
  --dest org.gnome.Shell \
  --object-path /org/gnome/Shell \
  --method org.gnome.Shell.Eval "global.display.focus_window.get_wm_class()"

It may also be possible to write a dbus listener that gets notified of window focus changes in gnome. That seems like it could be a separate project--one that perhaps ties in to keyd. Perhaps such a daemon could tap into each of the major shells/window managers and report the necessary information?

I think the "key remapping community" is in need of a solution here, because in general, I don't think it has been solved.

@rvaiya
Copy link
Owner

rvaiya commented Nov 19, 2021

Given your end goal I suspected this request was in the pipeline :P.

A simple use case: VSCode uses Ctrl+, as the "preferences" shortcut key, but
Firefox uses "Alt+e, n" (and can't be configured without a special plugin). I'd
like to unify this behavior so that Ctrl+, always gets me to preferences.

The best way to achieve this is to do the remapping in the application since in the end it has to be done a per application basis anyway (you have to know what the destination keystrokes and window class are).

There will be a few instances in which the application does not support such remapping, but these are relatively rare in my experience.

The other problem is that some applications just don't have bindings for things. The example you list above is one of them, A-e opens a menu and n selects an item in that menu. This is actually two separate bindings and requires a UI element to be opened in the interval. You can still simulate this in keyd with macro(A-e 50ms n) but it is a hack which inevitably involves a visual glitch and isn't seamless. OSX has a strong culture of consistent bindings which linux unfortunately lacks, trying to emulate this without first party support will always be a stop gap kludge.

Perhaps such a daemon could tap into each of the major shells/window managers and report the necessary information?

It should be fairly trivial to write a script which does window detection logic for compositors which support it. The main missing piece would be hot config reloading from the keyd side, which shouldn't be too hard to add. The other problem is that it would likely require root (like keyd), about which I have mixed feelings.

Another option for X might be to do additional application specific remapping at the xinput layer, but the end result would not be portable. Wayland's security model seems hostile to implementing something like this at the protocol level, though I believe there are some non standard extensions in the wlroots project which might offer the necessary flexibility.

I think the "key remapping community" is in need of a solution here,
because in general, I don't think it has been solved.

My personal opinion is that there is no real alternative to the slow long march towards order (sane defaults). But I suppose that doesn't stop people from trying :P.

@canadaduane
Copy link
Contributor Author

The other problem is that some applications just don't have bindings for things. The example you list above is one of them, A-e opens a menu and n selects an item in that menu. This is actually two separate bindings and requires a UI element to be opened in the interval. You can still simulate this in keyd with macro(A-e 50ms n) but it is a hack which inevitably involves a visual glitch and isn't seamless. OSX has a strong culture of consistent bindings which linux unfortunately lacks, trying to emulate this without first party support will always be a stop gap kludge.

Yes, unfortunately.

Perhaps such a daemon could tap into each of the major shells/window managers and report the necessary information?

It should be fairly trivial to write a script which does window detection logic for compositors which support it. The main missing piece would be hot config reloading from the keyd side, which shouldn't be too hard to add. The other problem is that it would likely require root (like keyd), about which I have mixed feelings.

I'm not sure if I understand the hot config reloading part. Were you thinking of a way to side-load the entire config?

I was thinking it might be easier to have a single config (as it is today) and treat app/window activation like an event--maybe even a synthetic key event?--and have keyd listening to that event stream. To list those two variants separately:

  1. synthetic key events: I could send a synthetic f13 keydown/keyup event (for example) that triggers keyd to jump to a specific layer
  2. another kind of event stream: keyd could be listening to a unix socket where newline-terminated names of layers arrive. When "meta_mac" or "firefox" is sent to the socket, for example, then keyd enters "meta_mac" layer state, or "firefox" layer state, depending on the string.

Another option for X might be to do additional application specific remapping at the xinput layer, but the end result would not be portable. Wayland's security model seems hostile to implementing something like this at the protocol level, though I believe there are some non standard extensions in the wlroots project which might offer the necessary flexibility.

Yes, these are the same considerations I had.

I think the "key remapping community" is in need of a solution here,
because in general, I don't think it has been solved.

My personal opinion is that there is no real alternative to the slow long march towards order (sane defaults). But I suppose that doesn't stop people from trying :P.

Says the author of keyd! :D

@rvaiya
Copy link
Owner

rvaiya commented Nov 20, 2021

I'm not sure if I understand the hot config reloading part. Were you thinking of a way to side-load the entire config?

Potentially, or just a way to trigger a reload of config files without restarting the daemon (to reduce latency).

I was thinking it might be easier to have a single config (as it is today) and
treat app/window activation like an event--maybe even a synthetic key
event?--and have keyd listening to that event stream. To list those two
variants separately:

These events would presumably have to be generated by a third party script which is aware of the display server. At this point it makes more sense to treat keyd as the primitive it was designed to be (a key->key remapping tool) rather than trying to bake some knowledge of the display server partially into it.

synthetic key events: I could send a synthetic f13 keydown/keyup event (for
example) that triggers keyd to jump to a specific layer.

You wouldn't want the event itself to trigger a layer change, but rather cause a given key to trigger a different application specific layer. This requires changing the main layout which is the equivalent of reloading a different configuration anyway.

stream: keyd could be listening to a unix socket where newline-terminated names
of layers arrive. When "meta_mac" or "firefox" is sent to the socket, for
example, then keyd enters "meta_mac" layer state, or "firefox" layer state

I considered writing a client which can interact with the daemon but ultimately decided against it because it would add unnecessary complexity and potentially increase the attack surface (especially if it permitted non root users to load config files).

Says the author of keyd! :D

keyd takes a keyboard centric view of the world (like QMK) and deliberately makes no assumptions about the layers on top of it. It is up to the desktop environment and attendant programs to provide a coherent set of keybindings (since these are the only things which can do it properly). Having said that, I acknowledge the gap that you describe and think there is a role for third party scripts which are application aware (and which can potentially make use of keyd).

@canadaduane
Copy link
Contributor Author

These events would presumably have to be generated by a third party script which is aware of the display server. At this point it makes more sense to treat keyd as the primitive it was designed to be (a key->key remapping tool) rather than trying to bake some knowledge of the display server partially into it.

Ok, cool. That's what I was thinking, too.

You wouldn't want the event itself to trigger a layer change, but rather cause a given key to trigger a different application specific layer. This requires changing the main layout which is the equivalent of reloading a different configuration anyway.

So it sounds like the "MVP" here would be a way to signal to a running keyd process that it should load a specific config file? i.e. not just a HUP to reload the current one, as we wouldn't want to overwrite default.cfg for example. How would that work--it seems to me we need a unix socket or other form of IPC?

@rvaiya
Copy link
Owner

rvaiya commented Nov 20, 2021

So it sounds like the "MVP" here would be a way to signal to a running keyd process that it should load a specific config file? i.e. not just a HUP to reload the current one, as we wouldn't want to overwrite default.cfg for example. How would that work--it seems to me we need a unix socket or other form of IPC?

The simplest thing would be a signal (USR1) which triggers a reload of all config files. The script would handle application detection and swapping (potentially generating) config files as necessary.

i.e. As we wouldn't want to overwrite default.cfg

The script would spawn keyd with a dedicated config directory that it owns.

@rvaiya
Copy link
Owner

rvaiya commented Nov 20, 2021

Here is a simple proof of concept

You can run it with sudo DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS python3 script.py. At the moment it overwrites default.cfg so you will want to backup your /etc/keyd before running it. The keyd specific logic is relatively small as most of the script is concerned with trying to wrangle window info out of gnome.

The main bottleneck comes from keyd's initialization cost, but this should be nearly instantaneous once hot reloading has been implemented.

@canadaduane
Copy link
Contributor Author

canadaduane commented Nov 22, 2021

Extending your proof of concept to a set of config files, stitched together:

https://github.com/canadaduane/meta-mac/blob/main/src/watch.py

Config files here:
https://github.com/canadaduane/meta-mac/tree/main/config

@rvaiya rvaiya changed the title Get current active window / behave differently depending on which app has focus App Specific Bindings Nov 25, 2021
@rvaiya
Copy link
Owner

rvaiya commented Nov 25, 2021

Closing this in favour of #59.

@rvaiya rvaiya closed this as completed Nov 25, 2021
rvaiya added a commit that referenced this issue Jan 8, 2022
 - Added IPC (-e) to facilitate app remapping (#51, #48, #43)
 - Added (unofficially) official support for application remapping :P
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants