Home row mod with kmonad and systemd
Here are a few notes and explanations about how I use kmonad under linux to apply home row modifier keys. For a fantastic introduction and configuration guide about home row modifiiers, read this first: A guide to home row mods
I’ll base my kmonad configuration files on what’s written in this aforementioned guide.
If you first want to read about kmonad and what it can do (spoiler: a lot of things I thought only possible with firmware), the official project is hosted there: https://github.com/kmonad/kmonad
Making it work with hotplug
The thing that I find missing, is how to have this working for devices that you hotplug, may it be USB or Bluetooth. This is where both systemd and udev come in handy.
udev is used for device identification and reacting to changes. systemd and udev can work together to manage the associated kmonad process and its arguments, as a service. Then it’s the kmonad process that will do the work of making the home row modifiers magic.
Device and udev rule creation
Let’s start with the device and its file. We’ll need a way to identify
it with the udevadm
tool and let kmonad know which device file to
use. If you know the input event device file, you can use something like
that to help you build a udev rule:
udevadm info --attribute-walk /dev/input/by-id/usb-SONiX_USB_DEVICE-event-kbd
For a bluetooth device, you can identify the device by its Bluetooth
address as displayed by bluetoothctl
. But you need to convert it to
lowercase if you want to find it in udev’s tree.
BT_ADDRESS="00:1F:20:76:41:30"
BT_ADDRESS_LC=$(echo ${BT_ADDRESS,,}) # bash lowercase
udevadm info --tree | grep -C 20 $BT_ADDRESS_LC
You should try to find a device from the input subsystem and its event
sub-device. Once you have it, use the same kind of udevadm
command
as for the USB keyboard. For my USB keyboard, I have a device alias (a
symbolic link) that is stable in /dev/input/by-id/
, so I’ll use that
in the kmonad config. For the Bluetooth device, I’ll use the udev
rule SYMLINK field to create a stable device name in /dev
.
For udev rules, here is an exemple file to put in /etc/udev/rules.d/60-keyboard-homerowmod.rules
:
SUBSYSTEMS=="input", TAG="systemd", ATTRS{name}=="SONiX USB DEVICE Keyboard", ENV{SYSTEMD_WANTS}+="kmonad@rk61.service", ENV{ID_MODEL}="Royal Kludge RK61 USB keyboard"
Or, for my bluetooth variant, also creating an alias to find it easily later:
SUBSYSTEM=="input", TAG="systemd", ATTRS{name}=="RK-Bluetooth keyboard", ENV{SYSTEMD_WANTS}+="kmonad@rk61bt.service", SYMLINK+="rk61bt"
As you might have seen in the rules, there is a way to make udev and systemd work together.
systemd to the rescue
For the systemd service unit, here is a possible solution using
templating, defined as a /etc/systemd/system/kmonad@.service
file:
[Unit]
Description=kmonad advanced keyboard daemon
StopWhenUnneeded=true
[Service]
Restart=on-abort
RestartSec=3
ExecStart=-/usr/local/bin/kmonad %E/kmonad/%i.kbd
Nice=-20
[Install]
WantedBy=default.target
The %i
part is the name of your configuration file and the instance
name of this template.
kmonad configuration
For each instance of the service, as defined in your udev rules,
you’ll need a kmonad configuration file. They should be at the path
defined in the systemd service. For instance: /etc/kmonad/rk61bt.kbd
Inside your configuration files, you need to use the same stable device files as the ones created by udev.
For my case, here are the two stable devices files:
/dev/rk61bt
for the Bluetooth device, as requested in the udev rule as a SYMLINK/dev/input/by-id/usb-SONiX_USB_DEVICE-event-kbd
for the USB one
And here is a snippet of the lines for the two inputs in my two kbd files:
input (device-file "/dev/rk61bt")
input (device-file "/dev/input/by-id/usb-SONiX_USB_DEVICE-event-kbd")
With all this, the kmonad process is started when the device is plugged and stopped when it disappears.