razzi.abuissa.net

Razzi guide to keyd

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

enter keyd

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).

How I manage my keyd configuration

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

keyboard-specific configuration

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

More configuration examples

[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.


  1. For posterity, here are my old Karabiner-Elements and my Hammerspoon configs. 

  2. This writeups seems to have good coverage on key remapping on Linux.