I'm a linux user and I want to swap control and capslock. The traditional way to do this is xmodmap, which has several limitations (notably it's X only, rebinds all attached keyboards[0], and you lose the bindings if the keyboard gets unplugged). The 'udev' system is a better way to do this, but the how has changed a few times and figuring out what needs to be done is painful. Which is why I'm writing the process down for myself.
The keyboard should be configured in some file in /etc/udev/hwdb.d/
. I used 61-keyboard.hwdb
because /lib/udev/hwdb.d/60-keyboard.hwdb
holds the defaults and I don't want to override those (which the man page tells me will happen if the name is an exact match).
The man hwdb
, Stack Overflow, and various blogs tell me the configuration has the following format:
evdev:input:b003v<VENDOR-ID>*
KEYBOARD_KEY_<SCAN-CODE>=<KEY-CODE>
[1]
The <KEY-CODE>
is a lowercase string value. The /usr/include/linux/input-event-codes.h
file is apparently used to generate valid strings by stripping the KEY_
prefix and lowercasing the constants defined there.
The <SCAN-CODE>
value is not the value in /usr/include/linux/input-event-codes.h
, nor it's hex equivalent. It is the MSC_SCAN code you can find with the evtest
utility, eg on my computer running sudo evtest /dev/input/event25
and hitting control
generates lines like:
Event: time 1626781053.126813, -------------- SYN_REPORT ------------
Event: time 1626781056.976805, type 4 (EV_MSC), code 4 (MSC_SCAN), value 70039
Event: time 1626781056.976805, type 1 (EV_KEY), code 29 (KEY_LEFTCTRL), value 1
And the code I needed is 70039
(the value after MSCSCAN, not_ the code 4
or code 29
or 1d
).
Looking at the documentation and various examples, it seems like the numeric values in input-event-codes.h should work (if you translate them to hex since, naturally, the source file uses decimal and the config wants hex) but they don't work for me and the experience was mildly infuriating.
Once everything is configured, systemd-hwdb udpate -s
and udevadm trigger
apply the new rules.
Anyway. This was aggrevating to figure out since there is a lot of obsolete documentation telling me how to write a script and trigger it with a udev
using utilities which are no longer shipped since systemd apparently absorbed udev and changed how things are done. I also found the documentation unhelpful around what scan code to use. I can find correct information, but maybe adding one more update to the pile will save someone some trouble.
[0]: I also have a Kinesis keyboard which I don't want to remap since it already puts control in a more useful place.
[1]: The evdev:input line accepts additional parameters: evdev:input:b<bus_id>v<vendor_id>p<product_id>e<version_id>-<modalias>
and the *
glob can be used to accept arbitrary values. The b003
is usb (codes are the BUS_*
values are in /usr/include/linux/input.h
). The vendor string is the USB vendor-id (found with lsusb
) in UPPERCASE hex (why not).