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 Ctrl
and 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 Insert
PrtSc
and my old PrtSc
the new Menu
?
Moreover, there are also 4 special keys (Fn
+ F9
–F12
) that could have been my media keys, but are by default strange things like Settings
and Search
. Why not map them to media keys as well?
Things I tried
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:
1 | 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.
Using udev hwdb
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:
1 | # /etc/udev/hwdb.d/90-zh-thinkpad.hwdb |
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 include/uapi/linux/input-event-codes.h
. 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!
One last thing… the Menu key
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_OPITON
and 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 /usr/share/X11/xkb/keycodes/evdev
:
1 | alias <MENU> = <COMP>; |
Combined with the output from xmodmap -pke | grep Menu
:
1 | 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 KEY_COMP
.
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.