Customize your keyboard layout (and have it work under Wayland)

Customizing keyboard layout is a bit of a jungle. What works when you’re on the console may not work when you’re running X which again may not work under Wayland.

I had to jettison my old .Xmodmap to get something that worked regardless of whether I was logged into Openbox (X) or GNOME on Wayland.

The job was relative simple: I wanted the Right Shift key to work as a Less Than key. The reason: I’m using a Danish layout on a keyboard that is physically a British layout. The second row from the bottom – the one with the shift keys – contain one less key on a British layout than a Danish one, so something obviously had to be sacrificed and apparently that’s the key responsible for the Less Than symbol (and the Greater Than, the Backslash and some weird dash-like character with a hook on the end).

Under X I have always favoured Xmodmap because it’s so simple: A single config file in the user home directory. A quick test under GNOME 3.30 under Wayland yielded nothing, though, so I thought I’d better move on. When the Arch wiki mentioned a udev based approach I figured that was the way to go. Udev isn’t going anywhere, is it?

Scancode

First I need the scancode of the key in question. Running evtest with root privileges seemed to be simplest way to do this. Evtest presents a list of input devices (the contents of /dev/input) along with their human readable names and it isn’t difficult to guess which one represents the keyboard:

No device specified, trying to scan all of /dev/input/event*
Available devices:
/dev/input/event0: Lid Switch
/dev/input/event1: Sleep Button
/dev/input/event2: Power Button
/dev/input/event3: AT Translated Set 2 keyboard
/dev/input/event4: SynPS/2 Synaptics TouchPad
/dev/input/event5: Wacom Pen and multitouch sensor Finger
/dev/input/event6: Video Bus
/dev/input/event7: Wacom Pen and multitouch sensor Pen
/dev/input/event8: TPPS/2 IBM TrackPoint
/dev/input/event9: ThinkPad Extra Buttons
/dev/input/event10: Integrated Camera: Integrated C
/dev/input/event11: HDA Intel PCH Mic
/dev/input/event12: HDA Intel PCH Headphone
/dev/input/event13: HDA Intel PCH HDMI/DP,pcm=3
/dev/input/event14: HDA Intel PCH HDMI/DP,pcm=7
/dev/input/event15: HDA Intel PCH HDMI/DP,pcm=8
/dev/input/event16: HDA Intel PCH HDMI/DP,pcm=9
/dev/input/event17: HDA Intel PCH HDMI/DP,pcm=10
Select the device event number [0-17]

Entering 3 I was presented with a list of all the keycodes and a prompt. The list wasn’t particularly useful apart from verifying that I was indeed working with a keyboard. But hitting the key I wanted to configure on the evtest prompt produed the following output:

Testing … (interrupt to exit)
Event: time 1547223952.646259, type 4 (EV_MSC), code 4 (MSC_SCAN), value 36
Event: time 1547223952.646259, type 1 (EV_KEY), code 54 (KEY_RIGHTSHIFT), value 1
Event: time 1547223952.646259, -------------- SYN_REPORT ------------
<Event: time 1547223952.700299, type 4 (EV_MSC), code 4 (MSC_SCAN), value 36
Event: time 1547223952.700299, type 1 (EV_KEY), 54 (KEY_RIGHTSHIFT), value 0
Event: time 1547223952.700299, -------------- SYN_REPORT -----------

The two sets of events represent the act of pressing the key and of letting it go (the values 1 and 0 in the second lines). In each set the first line seems to be the ‘hardware event’ with scancodes as the value at the end and the second line is the’software event’ that outputs the interpreted value, the keycode.  In my case I was just after the “value 36” bit, indicating that 36 was the scancode for my right shift key.

Keycode

In order to get the keycode (or rather, keycode name) I was after I consulted this list. In my case it was not obvious what the relevant name was – there was no ‘lessthan’ keycode name – so I attached my USB keyboard that had the correct key and ran evtest again and pressed the key in question. Turns out that it has the less than (no pun intended) obvious name ‘102nd’. The ‘102nd’ keycode seems to be whatever it needs to be to various keyboard layouts, Scandinavian, German, French, whatever. On a Danish keyboard layout it’s ‘less than’.

Udev

Final piece of the puzzle was figuring out how to target the keyboard. I have tried matching with udev before on harddisks and I guess I’ll never find it’s identification particularly intuitive. While I was able to extract the exact identifier with evemu-describe (look the long line beginning with ‘dmi:’) I found it easier to match my keyboard against this ‘all keyboards wildcard match’ from the Arch wiki (my laptop only the one keyboard anyway…):

evdev:atkbd:dmi:bvn*:bvr*:bd*:svn*:pn*:pvr*

Putting it all together, the following lines go into a udev config file:

evdev:atkbd:dmi:bvn*:bvr*:bd*:svn*:pn*:pvr*
KEYBOARD_KEY_36=102nd

So, KEYBOARD_KEY_[scancode]=[lowercase name of keycode]. These kinds of definitions go into a ‘.hwdb’ file in the /etc/udev/hwdb.d/ directory and once there, the hardware database needs to be rebuilt manually by running systemd-hwdb update with root privileges.

After a reboot the right shift key took on it’s new role as no. 102. Neat.

Searching….Question Mark? © Leo Leung (CC BY 2.0)

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.