You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
569 lines
15 KiB
569 lines
15 KiB
% KEYD(1) |
|
|
|
# NAME |
|
|
|
**keyd** - A key remapping daemon. |
|
|
|
# SYNOPSIS |
|
|
|
**keyd** \[options\] |
|
|
|
# OPTIONS |
|
|
|
`-m, --monitor` |
|
: Start keyd in monitor mode. Mainly useful for discovering key codes and debugging. |
|
|
|
`-e, --expression <expression> [<expression>...]` |
|
: Modify bindings of the currently active keyboard. See *Expressions* for details. |
|
|
|
`-l, --list` |
|
: List all internal key names. |
|
|
|
`-d, --daemonize` |
|
: Start keyd as a daemon logging out all output to */var/log/keyd.log*. |
|
|
|
`-v, --version` |
|
: Print the current version and exit. |
|
|
|
`-h, --help` |
|
: Print help and exit. |
|
|
|
# DESCRIPTION |
|
|
|
keyd is a system wide key remapping daemon which supports features like |
|
layering, oneshot modifiers, and macros. In its most basic form it can be used |
|
to define a custom key layout that persists across display server boundaries |
|
(e.g wayland/X/tty). |
|
|
|
The program is intended to run as a systemd service but is capable of running |
|
as a standalone daemon. The default behaviour is to run in the foreground and print |
|
to stderr, unless **-d** is supplied, in which case in which case log output |
|
will be stored in */var/log/keyd.log*. |
|
|
|
**NOTE** |
|
|
|
Because keyd modifies your primary input device it is possible to render your |
|
machine unusable with a bad config file. If you find yourself in this |
|
situation the sequence *\<backspace\>+\<escape\>+\<enter\>* will force keyd |
|
to terminate. |
|
|
|
# CONFIGURATION |
|
|
|
All configuration files are stored in */etc/keyd/* and are loaded upon |
|
initialization. A reload can be triggered by restarting the daemon or by |
|
sending SIGUSR1 to the process (e.g sudo pkill -usr1 keyd). |
|
|
|
A valid config file has the extension .conf and must begin with an *ids* section that has the following form: |
|
|
|
[ids] |
|
|
|
<id 1> |
|
<id 2> |
|
... |
|
|
|
Where each \<id\> is one of: |
|
|
|
- A device id of the form <vendor id>:<product id> (obtained with -m). |
|
- The wildcard "*". |
|
|
|
A wildcard indicates that the file should apply to all keyboards |
|
which are not explicitly listed in another configuration file and |
|
may optionally be followed by one or more lines of the form: |
|
|
|
-<vendor id>:<product id> |
|
|
|
representing a device to be excluded from the matching policy. Thus the following |
|
config will match all devices except 0123:4567: |
|
|
|
[ids] |
|
* |
|
-0123:4567 |
|
|
|
|
|
The monitor flag (**-m**) can be used to interactively obtain device ids and key names like so: |
|
|
|
> sudo systemctl stop keyd # Avoid loopback. |
|
> sudo keyd -m |
|
|
|
Magic Keyboard 0ade:0fac capslock down |
|
Magic Keyboard 0ade:0fac capslock up |
|
... |
|
|
|
Every subsequent section of the file corresponds to a layer and has the form: |
|
|
|
[<name>[:<type>]] |
|
|
|
Where `<type>` is either a valid modifier set (see *MODIFIERS*) or "layout". |
|
|
|
Each line within a layer is a binding of the form: |
|
|
|
<key> = <action>|<keyseq> |
|
|
|
Where `<keyseq>` has the form: `[<modifier1>[-<modifier2>...]-<key>` |
|
|
|
and each modifier is one of: |
|
|
|
\ **C** - Control\ |
|
\ **M** - Meta/Super\ |
|
\ **A** - Alt\ |
|
\ **S** - Shift\ |
|
\ **G** - AltGr |
|
|
|
In addition to key sequences, keyd can remap keys to actions which |
|
conditionally send keystrokes or transform the state of the keymap. |
|
|
|
It is, for instance, possible to map a key to escape when tapped and control |
|
when held by assigning it to `overload(C, esc)`. A complete list of available |
|
actions can be found in *ACTIONS*. |
|
|
|
## Layers |
|
|
|
Each layer is a keymap unto itself and can be transiently activated by a key |
|
mapped to the corresponding *layer()* action. |
|
|
|
For example, the following configuration creates a new layer called 'symbols' which |
|
is activated by holding the capslock key. |
|
|
|
[ids] |
|
* |
|
|
|
[main] |
|
capslock = layer(symbols) |
|
|
|
[symbols] |
|
f = ~ |
|
d = / |
|
... |
|
|
|
Pressing `capslock+f` thus produces a tilde. |
|
|
|
Key sequences within a layer are fully descriptive and completely self |
|
contained. That is, the sequence 'A-b' corresponds exactly to the combination |
|
`<alt>+<b>`. If any additional modifiers are active they will be deactivated |
|
for the duration of the corresponding key stroke. |
|
|
|
## Layouts |
|
|
|
The *layout* is a special kind of layer from which bindings are drawn if no |
|
other layers are active. By default all keys are mapped to themselves within a |
|
layout. Every config has at least one layout called *main*, but additional |
|
layouts may be defined and subsequently activated using the `layout()` action. |
|
|
|
Layouts also have the additional property of being affected by the active modifier |
|
set. That is, unlike layers, key sequences mapped within them are not |
|
interpreted literally. |
|
|
|
If you wish to use an alternative letter arrangement this is the appropriate |
|
place to define it. |
|
|
|
E.G |
|
|
|
[main] |
|
|
|
rightshift = layout(dvorak) |
|
|
|
[dvorak:layout] |
|
|
|
rightshift = layout(main) |
|
s = o |
|
d = e |
|
... |
|
|
|
## Modifiers |
|
|
|
Unlike most other remapping tools keyd provides first class support for |
|
modifiers. A valid modifier set may optionally be used as a layer type, |
|
causing the layer to behave as the modifier set in all instances except |
|
where an explicit mapping overrides the default behaviour. |
|
|
|
These layers play nicely with other modifiers and preserve existing stacking |
|
semantics. |
|
|
|
For example: |
|
|
|
[main] |
|
|
|
leftalt = layer(myalt) |
|
rightalt = layer(myalt) |
|
|
|
[myalt:A] |
|
|
|
1 = C-A-f1 |
|
|
|
Will cause the leftalt key to behave as alt in all instances except when |
|
alt+1 is pressed, in which case the key sequence `C-A-f1` will be emitted. |
|
|
|
By default each modifier key is mapped to an eponymously named modifier layer. |
|
|
|
Thus the above config can be shortened to: |
|
|
|
[alt] |
|
|
|
1 = C-A-f1 |
|
|
|
since leftalt and rightalt are already assigned to `layer(alt)`. |
|
|
|
Additionally, left hand values which are modifier names are expanded to both |
|
associated keycodes. |
|
|
|
E.G |
|
control = esc |
|
|
|
is the equivalent of |
|
|
|
rightcontrol = esc |
|
leftcontrol = esc |
|
|
|
Finally any set of valid modifiers is also a valid layer. For example, the |
|
layer `M-C` corresponds to a layer which behaves like the modifiers meta and |
|
control, which means the following: |
|
|
|
capslock = layer(M-A) |
|
|
|
will cause capslock to behave as meta and alt when held. |
|
|
|
**NOTE**: While it is technically possible to use individual modifier key codes |
|
like `rightalt` and `rightcontrol` as target sequences, the user is strongly |
|
encouraged to avoid these as they can produce unintuitive results when paired |
|
with their layer counterparts. To this end, it is best to think of modifiers |
|
as just a another kind of layer. |
|
|
|
Thus instead of: |
|
|
|
meta = rightcontrol |
|
|
|
one should do: |
|
|
|
meta = layer(control) |
|
|
|
### Lookup Rules |
|
|
|
In order to achieve this (un)holy union, the following lookup rules are used: |
|
|
|
if len(active_layers) > 0: |
|
if key in most_recent_layer: |
|
action = most_recent_layer[key] |
|
else if has_modifiers(most_recent_layer): |
|
for layer in active_layers: |
|
activate_modifiers(layer) |
|
|
|
action = layout[key] |
|
else: |
|
action = layout[key] |
|
|
|
The upshot of all this is that things should mostly just work™. The |
|
majority of users needn't be explicitly conscious of the lookup rules |
|
unless they are doing something unorthodox (e.g nesting hybrid layers). |
|
|
|
## IPC |
|
|
|
To facilitate extensibility keyd employs a client-server model. The keymap can |
|
thus be conceived of as a 'living entity' that can be modified at run time. |
|
|
|
In addition to allowing the user to try new bindings on the fly, this |
|
enables the user to fully leverage keyd's expressive power from other programs |
|
without incurring a performance penalty. |
|
|
|
For instance, the user may use this functionality to write a script which |
|
alters the keymap when they switch between different tmux sessions. |
|
|
|
The application remapping tool (keyd-application-mapper) which ships with keyd |
|
is a good example of this. It is a small python script which performs |
|
event detection for the various platforms (e.g X/sway/gnome, etc) and feeds the |
|
desired mappings to the core using `-e`. |
|
|
|
### Expressions |
|
|
|
The `-e` flag accepts one or more *expressions*, each of which must have one of the following forms: |
|
|
|
[<layer>.]<key> = <action>|<key sequence> |
|
reset |
|
|
|
Where `<layer>` is the name of an (existing) layer in which the key is to be bound. |
|
|
|
As a special case, an expression may be the string 'reset', in which case the |
|
current keymap will revert to its original state (all dynamically applied |
|
bindings will be dropped). |
|
|
|
Examples: |
|
|
|
$ keyd -e 'rightshift = layout(dvorak)' # Map rightshift to layout(dvorak) in [main]. |
|
$ keyd -e 'dvorak.rightshift = layout(main)' # Map rightshift to layout(main) in [dvorak]. |
|
$ keyd -e 'reset' # Reset the state of the keymap so it reflects /etc/keyd/. |
|
|
|
By default expressions apply to the most recently active keyboard. |
|
|
|
### Application Support |
|
|
|
keyd ships with a python script called **keyd-application-mapper** which |
|
reads a file called *~/.config/keyd/app.conf* and applies the supplied bindings |
|
whenever a window with a matching class comes into focus. |
|
|
|
You can think of each section as a set of application specific masks applied |
|
over the global rules defined in `/etc/keyd/*.conf`. |
|
|
|
The config file has the following form: |
|
|
|
[<filter>] |
|
|
|
<expression 1> |
|
<expression 2...> |
|
|
|
Where `<filter>` has one of the following forms: |
|
|
|
[<class exp>] # Match by window class |
|
[<class exp>|<title exp>] # Match by class and title |
|
|
|
and each `<expression>` is a valid argument to `-e` (see *Expressions*). |
|
|
|
`<class exp>` and `<title exp>` are strings which describe window class and title names |
|
to be matched and may optionally contain unix style wildcards (*). For example, |
|
'`[gnome|*find*]`' will match any window with a class of 'gnome' and a title |
|
which contains 'find'. |
|
|
|
E.G |
|
|
|
[kitty] |
|
|
|
alt.] = C-tab |
|
alt.[ = C-S-tab |
|
alt.t = C-S-t |
|
|
|
[st-*] |
|
|
|
alt.1 = macro(Inside space st) |
|
|
|
[chromium] |
|
|
|
control.1 = macro(Inside space chrome!) |
|
alt.] = C-tab |
|
alt.[ = C-S-tab |
|
alt.t = C-t |
|
|
|
Will remap `A-1` to the the string 'Inside st' when a window with a class |
|
that begins with 'st-' (e.g st-256color) is active. |
|
|
|
Window class and title names can be obtained by inspecting the log output while the |
|
daemon is running (e.g `tail -f ~/.config/keyd/app.log`). |
|
|
|
At the moment X, Sway and Gnome are supported. |
|
|
|
#### Installation |
|
|
|
Installation is a simple matter of running the daemon `keyd-application-mapper -d` |
|
somewhere in your display server initialization logic (e.g ~/.xinitrc or |
|
~/.xsession). The exception to this is if you are running Gnome, in which case |
|
running `keyd-application-mapper` for the first time will install an extension |
|
which manages the script lifecycle. |
|
|
|
In order for this to work, keyd must be running and the user must have access |
|
to */var/run/keyd.socket* (i.e be a member of the *keyd* group). |
|
|
|
### A note on security |
|
|
|
Any user which can interact with a program that directly controls input devices |
|
should be assumed to have full access to the system. |
|
|
|
While keyd offers slightly better isolation compared to other remappers by dint |
|
of mediating access through an IPC mechanism (rather than granting users |
|
blanket access to /dev/input/* and /dev/uinput), it still provides an |
|
opportunity for abuse and should be treated with due deference. |
|
|
|
Specifically, access to */var/run/keyd.socket* should only be granted to |
|
trusted users and the group `keyd` should be regarded with the same reverence |
|
as `wheel`. |
|
|
|
## ACTIONS |
|
|
|
**oneshot(\<layer\>)** |
|
|
|
: If tapped, activate the supplied layer for the duration of the next keypress. |
|
If `<layer>` is a modifier layer then it will cause the key to behave as the |
|
corresponding modifiers while held. |
|
|
|
**layer(\<layer\>)** |
|
|
|
: Activates the given layer while held. |
|
|
|
**toggle(\<layer\>)** |
|
|
|
: Toggles the state of the given layer. Note this is intended for layers and is |
|
distinct from `layout()` which should be used for letter layouts. |
|
|
|
**overload(\<layer\>,\<keyseq\>[,\<timeout\>])** |
|
|
|
: Activates the given layer while held and emits the given key sequence when |
|
tapped. A timeout in milliseconds may optionally be supplied to disambiguate |
|
a tap and a hold. |
|
|
|
If a timeout is present depression of the corresponding key is only interpreted |
|
as a layer activation in the event that it is sustained for more than |
|
\<timeout\> a milliseconds. This is useful if the overloaded key is frequently |
|
used on its own (e.g space) and only occasionally treated as a modifier (the opposite |
|
of the default assumption). |
|
|
|
**layout(\<layer\>)** |
|
|
|
: Sets the current layout to the given layer. You will likely want to ensure |
|
you have a way to switch layouts within the newly activated one. |
|
|
|
**swap(\<layer\>[, \<keyseq\>])** |
|
|
|
: Swap the currently active layer with the supplied one. The supplied layer is |
|
active for the duration of the depression of the current layer's activation |
|
key. A key sequence may optionally be supplied to be performed before the layer |
|
change. |
|
|
|
**macro(\<macro\>)** |
|
|
|
: Perform the key sequence described in `<macro>` |
|
|
|
Where `<macro>` has the form `<token1> [<token2>...]` where each token is one of: |
|
|
|
- a valid key sequence. |
|
- a contiguous group of characters each of which is a valid key sequence. |
|
- a group of key codes delimited by + to be depressed as a unit. |
|
- a timeout of the form `<time>ms` (where `<time>` < 1024). |
|
|
|
Examples: |
|
|
|
# Sends alt+p, waits 100ms (allowing the launcher time to start) and then sends 'chromium' before sending enter. |
|
macro(A-p 100ms chromium enter) |
|
|
|
# Types 'Hello World' |
|
macro(h e l l o space w o r ld) |
|
|
|
# Identical to the above |
|
macro(Hello space World) |
|
|
|
# Produces control + b |
|
macro(control + b) |
|
|
|
# Produces the sequence <control down> <b down> <control up> <b up> |
|
macro(control+b) |
|
|
|
# Produces the sequence <control down> <control up> <b down> <b up> |
|
macro(control b) |
|
|
|
# EXAMPLES |
|
|
|
## Example 1 |
|
|
|
Make `esc+q/w` set the letter layout. |
|
|
|
[ids] |
|
* |
|
|
|
[main] |
|
esc = layer(esc) |
|
|
|
[dvorak:layout] |
|
s = o |
|
d = e |
|
... |
|
|
|
[esc] |
|
q = layout(main) |
|
w = layout(dvorak) |
|
|
|
## Example 3 |
|
|
|
Invert the behaviour of the shift key without breaking modifier behaviour. |
|
|
|
[ids] |
|
* |
|
|
|
[main] |
|
1 = ! |
|
2 = @ |
|
3 = # |
|
4 = $ |
|
5 = % |
|
6 = ^ |
|
7 = & |
|
8 = * |
|
9 = ( |
|
0 = ) |
|
|
|
[shift] |
|
0 = 0 |
|
1 = 1 |
|
2 = 2 |
|
3 = 3 |
|
4 = 4 |
|
5 = 5 |
|
6 = 6 |
|
7 = 7 |
|
8 = 8 |
|
9 = 9 |
|
|
|
|
|
## Example 3 |
|
|
|
Tapping control once causes it to apply to the next key, tapping it twice |
|
activates it until it is pressed again, and holding it produces expected |
|
behaviour. |
|
|
|
[main] |
|
|
|
control = oneshot(control) |
|
|
|
[control] |
|
|
|
toggle(control) |
|
|
|
# Example 4 |
|
|
|
Meta behaves as normal except when \` is pressed, after which the alt_tab layer |
|
is activated for the duration of the leftmeta keypress. Subsequent actuations |
|
of \` will thus produce A-tab instead of M-\`. |
|
|
|
[meta] |
|
|
|
` = swap(alt_tab, A-tab) |
|
|
|
[alt_tab:A] |
|
|
|
tab = A-S-tab |
|
` = A-tab |
|
|
|
# Example 5 |
|
|
|
# Uses the compose key functionality of the display server to generate |
|
# international glyphs. # For this to work 'setxkbmap -option |
|
# compose:menu' must # be run after keyd has started. |
|
|
|
# A list of sequences can be found in /usr/share/X11/locale/en_US.UTF-8/Compose |
|
# on most systems. |
|
|
|
|
|
[main] |
|
|
|
rightalt = layer(dia) |
|
|
|
[dia] |
|
|
|
# Map o to ö |
|
o = macro(compose o ") |
|
|
|
# Map e to € |
|
e = macro(compose c =) |
|
|
|
# Example 6 |
|
|
|
# Tapping both shift keys will activate capslock. |
|
|
|
[shift] |
|
|
|
leftshift = capslock |
|
rightshift = capslock |
|
|
|
# AUTHOR |
|
|
|
Written by Raheman Vaiya (2017-). |
|
|
|
# BUGS |
|
|
|
Please file any bugs or feature requests at the following url: |
|
|
|
<https://github.com/rvaiya/keyd/issues>
|
|
|