ThinkPad T series (also X1 Carbon) laptops have a great keyboard that I’ve been using delightfully for years. However, there’s a minor issue with its keyboard layout: they replaced the
Menu key with a
PrtSc. In my day to day work, I almost always accidentally hit that key while using my lovely
Alt keys, upon which my laptop happily plays a shatter sound, flashes my screen white for a split second and spawns a screenshot file under my
Pictures/ (thank you, GNOME). Whereas when I wanted to use my
Menu key, it’s nowhere to be found.
However, there’s still an
Insert key lying quietly in the top-right corner, which I never used (except for checking if some app even supports it). So why not make my old
PrtSc and my old
PrtSc the new
Moreover, there are also 4 special keys (
F12) that could have been my media keys, but are by default strange things like
Search. Why not map them to media keys as well?
xmodmap was the go-to tool for remapping keys, and it works on the X11 server level so the only thing you need to care about is the X11 key symbols (no key codes, scan codes and other nightmares). However, naturally this tool won’t work under Wayland (which supports fractional scaling etc), and it cannot get automatically loaded by my GNOME 3 (even with autostart). Some say it’s been deprecated, and people should use
xkb instead, so no luck here.
The other way is to modify the
xkb key symbol database. Although it doesn’t provide any means of overriding in
/etc/, you can directly edit the files under
/usr/share/X11/xkb/symbols/. The interesting files are
pc for the standard keys, and
inet for the special keys, and here’s the patch I’ve been using for two years:
diff --git a/pc b/pc.zhnew
It works perfectly, without any overhead. The only problem with it, is that because these things live inside
/usr/, which is managed by
pacman, it is reverted to packaged version every time
xkeyboard-config is updated, and it actually does get updated sometimes. In that case I’ll find me suddenly making screenshots again, and need to patch those files and reboot for things to work.
Recently when I was doing some brief research about new ThinkPads, I came across the ArchWiki for ThinkPad T480, which mentioned using something called hwdb to add support for its two special buttons. It looked promising, and I finally took some hours today to figure it out for my own remapping.
The hwdb in udev works on a much lower level: it maps the scan codes from your keyboard to standard key codes, and
/etc/udev/hwdb.d/ provides a means of customization, which allows overriding the way scan codes are mapped. Some more detail can be found out on Arch Wiki.
And here is the final hwdb file I came up with:
The hwdb rules we need to write consists of two parts: matching and mapping. The matching expression is a shell glob that matches the device, where as the mapping maps the scan code (in hex) to key code macro names in kernel’s
man hwdb provided some simple example, but actually comments in the built-in hwdb file provides much more details about this file.
In order to find out the scan codes for my keys, I tried both methods in Arch Wiki. The traditional
showkey --scancodes didn’t work well for me, requiring switching to a tty, and was printing multiple bytes of hex for a single keystroke of mine. In contrast,
evtest was just the right tool. Just execute
sudo evtest in a terminal and select something like
AT Translated Set 2 keyboard for the builtin keyboard (mine is
3), and you can test your keystrokes to find out its scan code in output like
(MSC_SCAN), value <SCAN CODE HERE>.
Another caveat is that, for the special keys on ThinkPad keyboard, unlike regular keys they are not listed under the
AT Translated Set 2 keyboard, but actually under another input device named
ThinkPad Extra Buttons. It took me some time to realize this, and I also tried
showkey which disappointed me again.
After we’ve got the scan codes from
evtest and key codes from
input-event-codes.h, it’s time to write the rule. We need the matching part for the two devices, whose format specification is available in the comment inside systemd’s bulitin hwdb file. The exact info for your current machine can be obtained from
cat /sys/class/dmi/id/modalias, and combining with other existing ThinkPad rules in
/usr/lib/udev/hwdb.d/60-keyboard.hwdb I derived mine successfully.
The actual rules read by udev upon boot is a compiled binary file called
hwdb.bin, so one will need to compile the configuration files into binary with
sudo systemd-hwdb update. To make the changes take effect immediately, run
sudo udevadm trigger, and finally, try out the new key mapping!
Most of my key mapping worked, except for my new
Menu key. I double checked the scan code and the key code name – they both seemed correct. In
input-event-codes.h, I also found
KEY_CONTEXT_MENU, but neither of them worked as
Menu key as well.
So I tried
xev. Interestingly, it printed
XF86MenuKB as the key symbol on the X11 level, instead of
Menu. This must be something with the X11 key symbol database. I did some grep, played around for around half an hour, and finally found out the answer when I expanded my search into
alias <MENU> = <COMP>;
Combined with the output from
xmodmap -pke | grep Menu:
keycode 135 = Menu NoSymbol Menu
I surprisingly found out that my key code
KEY_MENU was mapped to key symbol
XF86MenuKB. And to map to key symbol
Menu, I actually need to map my scan code to key code
So one last change, save, and
sudo systemd-hwdb update && sudo udevadm trigger. Hooray! All my keys are working flawlessly now, and I don’t need to worry about package updates anymore (just forget about
xkeyboard-config). Although the obscure and scattered documentation put me through these tedious trial-and-error attempts, it still kinda excited me when I finally got my keys remapped correctly, after all these years.