A Wayland replacement for xbindkeys.
The way that Wayland works these days means that in general your keybinds are now attached to the desktop environment that you are using. This breaks the Unix philosophy and does not allow you to move a keybind config across desktop environments with ease like you used to be able to with xbindkeys.
wbindkeys allows you to bind keys, key combos, mouse buttons and scroll events to shell commands, all configured in Lua.
While wbindkeys intends to replace xbindkeys, in the spirit of Wayland being a replacement with a better API, wbindkeys will offer a new configuration file language that is easier to handle for both machines and humans alike.
wbindkeys uses Lua for maximum configurability, because sometimes you need an if statement in your config.
wbindkeys reads input directly from your input devices through
libinput rather than through the compositor, so it works the same under
any Wayland compositor. This is also why it needs permission to read
/dev/input (see Permissions).
wbindkeys needs the libinput, libudev and libevdev development packages to build. On Debian/Ubuntu:
sudo apt-get install pkg-config libevdev-dev libudev-dev libinput-devOn other distributions install the equivalent packages with your package manager.
cargo install wbindkeys
sudo ~/.cargo/bin/wbindkeys permissions --set
wbindkeys man --install # optional, so `man wbindkeys` worksThen create a config and run
wbindkeys. To start it automatically when you log in,
install it as a service:
wbindkeys service --installThis detects your system's service manager and sets up a per-user
service that runs the wbindkeys binary you invoked it as.
For now only systemd on Linux is supported, where it writes
~/.config/systemd/user/wbindkeys.service. On anything else
it tells you the system isn't supported yet; please open an
issue to ask for it. wbindkeys service --uninstall
removes the service again.
make builddep # installs Rust (if needed) and the packages above, Debian/Ubuntu only
make permissions # grants access to input devices, see Permissions
make installmake install builds the release binary, copies it to
~/.local/bin (with the man page in
~/.local/share/man/man1), and runs
wbindkeys service --install to install and start a per-user
service that runs it.
wbindkeys reads your keyboard and mouse directly from
/dev/input, which normal users can't read by default. It
runs as your user rather than as root, so it needs to be granted access
once per machine.
sudo wbindkeys permissions --setThis installs a udev rule at
/etc/udev/rules.d/69-wbindkeys.rules that tags input
devices with uaccess, then re-applies it to devices that
are already connected. With that tag, systemd-logind gives whoever is
logged in at the machine (the active local session) read access to input
devices, and should move that access along when you switch users.
Nothing is granted to users logged in remotely or to other accounts.
sudo often can't find binaries in
~/.cargo/bin or ~/.local/bin, so you may need
the full path:
sudo ~/.cargo/bin/wbindkeys permissions --set or
sudo ~/.local/bin/wbindkeys permissions --set. When
building from source, make permissions does this for
you.
wbindkeys permissions --checkRun this as your normal user, not with sudo, since that
is who wbindkeys runs as. It reports whether the udev rule is installed
and lists any input devices you can't read:
[ok] udev rule installed at /etc/udev/rules.d/69-wbindkeys.rules
[fail] 17 of 18 input devices are not readable:
/dev/input/event0 (Power Button): Permission denied (os error 13)
...
It exits with a non-zero status if anything is wrong, so it can also be used in scripts.
sudo wbindkeys permissions --set. Access is
granted when a device is added, so devices that were connected while
another user (or the login screen) was active can end up belonging to
that user. Re-running re-applies access for the current session.input
group (sudo usermod -aG input $USER, then log out and back
in). This works regardless of session, but lets every program you run
read your keyboard, so the udev rule is preferred.wbindkeys loads its config from
$XDG_CONFIG_HOME/wbindkeys/init.lua, which is
~/.config/wbindkeys/init.lua unless you have set
$XDG_CONFIG_HOME. The file must exist, otherwise wbindkeys
exits with an error on startup.
Each binding maps a key combo to a shell command:
-- Run Alacritty on ALT+A
bind("ALT+A", "alacritty")
-- Run Telegram on ALT+T
bind("ALT+T", "flatpak run org.telegram.desktop")
-- Mouse buttons and the scroll wheel can be bound too
bind("MOD+Mouse4", "wofi --show drun")
bind("ALT+ScrollUp", "pactl set-sink-volume @DEFAULT_SINK@ +5%")A binding runs as soon as its combo is pressed. Pass
{ on = "release" } to run it once every key of the combo
has been let go instead. It doesn't run if another key is pressed in
between:
-- Send Super+1 with ydotool. On release, so the Alt you're holding isn't
-- added to it and turned into Alt+Super+1.
bind("ALT+1", "ydotool key 125:1 2:1 2:0 125:0", { on = "release" })
-- Open the launcher by tapping Super on its own, without running it for
-- every Super+key combo
bind("MOD", "wofi --show drun", { on = "release" })After changing the config, restart the service:
systemctl --user restart wbindkeys.service.
Key names are case-insensitive and joined with +.
| Kind | Names |
|---|---|
| Modifiers | ALT, CTRL, SHIFT,
MOD (the Super/Windows key), SPACE |
| One-sided modifiers | LEFTALT, LEFTCTRL, LEFTSHIFT,
LEFTMOD, RIGHTALT, RIGHTCTRL,
RIGHTSHIFT, RIGHTMOD |
| Letters and numbers | A–Z, 0–9 |
| Function keys | F1–F12 |
| Navigation | UP, DOWN, LEFT,
RIGHT, HOME, END,
PAGEUP, PAGEDOWN, INSERT,
DELETE |
| Other keys | ESCAPE, ENTER, BACKSPACE,
TAB, BACKTICK, COMMA,
PERIOD, SLASH, BACKSLASH,
SEMICOLON, QUOTE, DASH,
EQUAL, LEFTSQUARE,
RIGHTSQUARE |
| Mouse | MOUSE1–MOUSE10, SCROLLUP,
SCROLLDOWN, SCROLLLEFT,
SCROLLRIGHT |
ALT, CTRL, SHIFT and
MOD match the key on either side of the keyboard; use the
LEFT… and RIGHT… names to bind only one side.
If a combo matches more than one binding, the one-sided binding wins, so
with both ALT+E and RIGHTALT+E bound, Right
Alt+E runs the RIGHTALT+E command. When a binding has
several modifiers, press them in the order they are written
(CTRL+ALT+T means Ctrl, then Alt, then T). Unknown key
names are ignored; run with --debug to see them.
wbindkeys run wbindkeys (this is what the service does)
wbindkeys --debug run with log output, see "Debugging your config"
wbindkeys permissions --check check wbindkeys can read your input devices
sudo wbindkeys permissions --set install the udev rule that grants that access
wbindkeys service --install start wbindkeys when you log in
wbindkeys service --uninstall stop that and remove the service
wbindkeys record record key presses for a bug report, see below
wbindkeys man print the wbindkeys(1) man page
wbindkeys man --install install it next to the binary, see below
wbindkeys --help show all options
wbindkeys --version show the version
cargo install only installs the binary, so the man page
is built into it instead. wbindkeys man --install writes it
to ../share/man/man1/ relative to the binary, i.e.
~/.cargo/share/man/man1/wbindkeys.1. man
searches there automatically for anything in ~/.cargo/bin
on your PATH, so man wbindkeys works with no
MANPATH changes. make install does the same
for ~/.local/bin. The page's source is man/wbindkeys.1, written by hand
in mdoc; a test checks it mentions every subcommand and flag.
Run with --debug (or -d) to print log
output to stderr: which input devices were opened, the config path being
loaded, each registered binding and the key codes it maps to, any
unrecognised key names, every key press with its combo, and whether a
combo matched a binding.
systemctl --user stop wbindkeys.service
wbindkeys --debugIf the output shows
Failed to open input device ... Permission denied, or key
presses don't show up at all, see Permissions.
If a binding doesn't fire, record what wbindkeys sees while you press it:
wbindkeys recordPress the combination, then Ctrl+C. This saves
wbindkeys-recording.txt (use -o to pick
another file), with each key press, what wbindkeys made of it and which
binding it would run, plus your wbindkeys version, system, keyboard
layout, input devices and bindings (but not their commands). Open an
issue and attach the file. Everything you type while recording is
saved, so don't type passwords.
make builddep
make permissions # once, so you can read input devices without sudo
make build-debug # or `make build-release`; run `make` to list all targets
cargo testTo try your changes:
make run builds and runs wbindkeys against your normal
config.make test-run runs it against
testing/config/wbindkeys/init.lua, where every binding
appends a line to testing/wbindkeys-test.log instead of
launching an app. Watch it with
tail -f testing/wbindkeys-test.log and press the combos
from that file.Stop the service first
(systemctl --user stop wbindkeys.service) so the installed
copy doesn't also react to your key presses.
make e2e runs an end-to-end test without touching your
session: it starts wbindkeys in an LXD virtual machine (created on the
first run), presses key combos on a virtual keyboard there and checks
which bindings fire. It needs LXD with virtual machine support;
see testing/vm/e2e.sh for options.