When I switched my laptop to dual boot Ubuntu (and later Debian) one thing I hugely missed from MacOS was a key remapping utility.
Perhaps even more so because the default keyboard shortcuts on a Windows-oriented keyboard were foreign to me after using Mac for so long.
On MacOS there is the great Karabiner-Elements which I used constantly, except for a short stint where I upgraded to the latest MacOS which was not supported by the original Karabiner, wherein I furiously tried to port my config over to Hammerspoon1.
Anyways, when I switched to Ubuntu I tried a couple options such as xmodmap and sxhkd, but either they didn’t work or were glitchy. I then found Interception Tools which worked (!) but was kinda cumbersome; its design is actually pretty elegant and it goes into great lengths to describe its functionality but it’s still pretty low level and requires you to write c code and compile your own executables. If I hadn’t discovered keyd I’d probably still be using Interception Tools today.2
keyd doesn’t have much fanfare and be pretty much a self-contained repository, which might be why I didn’t find it for a while.
Installation is pretty standard for a standalone C executable:
$ git clone https://github.com/rvaiya/keyd
$ cd keyd
$ make && sudo make install
$ sudo systemctl enable --now keyd
And then we get to their first configuration example for /etc/keyd/default.conf
:
[ids]
*
[main]
# Maps capslock to escape when pressed and control when held.
capslock = overload(control, esc)
Now we’re talking!! We already have caps lock mapped to control (good) and caps lock sending escape when pressed alone (cool but I don’t use this).
Rather than editing the /etc/keyd/default.conf
file
which is typically owned by root, I make /etc/keyd
a symlink
to my dotfiles:
$ git clone git@git.sr.ht:~razzi/.dotfiles ~/.dotfiles
$ ln -s (pwd)/keyd_config /etc/keyd
Now I can edit the user-writable keyd_config/default.conf
and apply my changes like so:
$ vim ~/.dotfiles/keyd_config/default.conf
$ sudo keyd reload
Unless you only have 1 device that only uses 1 keyboard, you’ll probably want to customize per keyboard a bit.
For example, I have a Razer Huntsman Mini has an escape where the `/~ key would be. I have alt configured to send escape when pressed alone:
alt = overload(alt, esc)
so I don’t need the escape key actually, and I remap escape to ` and shift+escape to ~:
esc = `
[shift]
esc = ~
But these keybindings don’t make sense on other keyboards.
The key to keyboard-specific configuration is the device id.
I have a config file keyd_config/macbook_keyboard.conf
like so:
[ids]
# Apple macbook internal keyboard
05ac:0259:21458be1
[main]
include common
# use right option as control since there is no right control
rightalt = layer(control)
I have my config factored to use a “common” keybindings file for keybindings I want on all my keyboards. For this keyboard I have the specific keybinding that right option should act as a control key.
You can get device IDs using sudo keyd monitor
, it starts by printing device IDs:
$ sudo keyd monitor
device added: 05ac:0259:21458be1 Apple Inc. Apple Internal Keyboard / Trackpad (/dev/input/event4)
...
Finally, note that if you have a default.conf with
[ids]
*
[main]
# keybindings here...
and you have a device-specific config file, it will only use the device-specific config file and your default.conf will not apply.
Make sure you understand that or you’ll be left scratching your head when keybindings mysteriously stop working! Mystery solved: adding a device-specific config file turns off the default.conf.
To solve more mysteries, check the logs:
sudo journalctl -u keyd.service
You’ll find useful messages like:
keyd[1572]: DEVICE: match 05ac:0259:21458be1 /etc/keyd/macbook_keyboard.conf
This is also where you’ll see if your keyd config has any syntax errors:
keyd[1572]: CONFIG: parsing /etc/keyd/default.conf
keyd[1572]: WARNING: invalid binding on line 19
[ids]
*
[main]
# Maps capslock to control
capslock = layer(control)
# Maps alt to escape when pressed alone and alt when held
alt = overload(alt, esc)
# c-h to backspace
[control]
h = backspace
# Rightalt+space to alt+tab
[altgr]
space = A-tab
You can see all my keyd config in my dotfiles.
For posterity, here are my old Karabiner-Elements and my Hammerspoon configs. ↩
This writeups seems to have good coverage on key remapping on Linux. ↩