Fake key code for remapped keyboard keys
I have remapped my caps lock to backspace.
/etc/default/keyboard
XKBLAYOUT="us"
XKBVARIANT="altgr-intl"
BACKSPACE="guess"
XKBOPTIONS="caps:backspace"
This works pretty great, except that it doesn’t work for some specific tools. I used xev
to find out what’s going on.
Backspace pressed
KeyPress event, serial 38, synthetic NO, window 0x2400001,
root 0x159, subw 0x0, time 1028211, (335,635), root:(452,749),
state 0x10, keycode 22 (keysym 0xff08, BackSpace), same_screen YES,
XLookupString gives 1 bytes: (08) "
XmbLookupString gives 1 bytes: (08) "
XFilterEvent returns: False
KeyRelease event, serial 38, synthetic NO, window 0x2400001,
root 0x159, subw 0x0, time 1028272, (335,635), root:(452,749),
state 0x10, keycode 22 (keysym 0xff08, BackSpace), same_screen YES,
XLookupString gives 1 bytes: (08) "
XFilterEvent returns: False
Caps lock pressed
KeyPress event, serial 38, synthetic NO, window 0x2400001,
root 0x159, subw 0x0, time 859789, (391,558), root:(508,672),
state 0x10, keycode 66 (keysym 0xff08, BackSpace), same_screen YES,
XKeysymToKeycode returns keycode: 22
XLookupString gives 1 bytes: (08) "
XmbLookupString gives 1 bytes: (08) "
XFilterEvent returns: False
KeyRelease event, serial 38, synthetic NO, window 0x2400001,
root 0x159, subw 0x0, time 859875, (391,558), root:(508,672),
state 0x10, keycode 66 (keysym 0xff08, BackSpace), same_screen YES,
XKeysymToKeycode returns keycode: 22
XLookupString gives 1 bytes: (08) "
XFilterEvent returns: False
I also tried it in the browser using
addEventListener('keyup', event => {
console.log(event.keyCode, event.key, event.code)
})
This logs the following when I press backspace and caps lock
8 "Backspace" "Backspace"
8 "Backspace" "CapsLock"
So basically my caps lock is remapped to backspace, but it only works if tools and websites use proper checks. I don’s want to file a bug report for every tool or website I ever use that implements this incorrectly.
Is it possible to map caps lock in such a way that it fully emulates a backspace instead of acting like a remapped caps lock?
First, let's see how a key press is processed (taken from this answer):
/keyboard/ → scancode
→ /input driver/ → keycode
→ /X server XKB/ → keysym
The scancode
is a device specific code that is bound to a specific key and can differ between different vendors/products. keycode
and keysym
are propagated to applications. The keycode
serves as an abstraction layer as it is device agnostic and locale agnostic. The same keycode
can produce different keysym
s, depending on the locale and the state of modifier keys. That is the reason why some applications only look for the keycode
, especially when dealing with keyboard shortcuts.
So our goal is to map the scancode
of your CapsLock key to the keycode
of the BackSpace key. Applications will then see the same keycode
and keysym
regardless whether you press BackSpace or CapsLock.
This mapping is done by udev using the hardwaredatabase file (hwdb.bin) which is compiled from .hwdb files in both /lib/udev/hwdb.d/
and /etc/udev/hwdb.d/
.
How to change scancode
->keycode
mapping
Gather required information
First you have to determine the bustype
, vendor
, product
and version
of your input device (keyboard), as well as the scancode
of the key you want to remap and the key code identifier
you want to map it to.
Run evtest
(you may have to install it first) and identify your keyboard in the list of devices. On keyboards with additional keys like Play/Pause, WWW, etc. these keys are often exposed as a different input device. If you don't get any output when pressing a key, hit Control+C and try a different device. Once you have identified your keyboard, remember the first column (/dev/input/eventX
) and press the key you want to remap. The value after (MSC_SCAN)
is the scancode
. On my keyboard:
$ evtest
Available devices:
/dev/input/event0: Power Button
/dev/input/event1: Power Button
/dev/input/event2: G19 Gaming Keyboard
/dev/input/event3: G19 Gaming Keyboard
...
Select the device event number [0-18]:2
...
Event: time 1522111203.117945, -------------- SYN_REPORT ------------
Event: time 1522111220.778787, type 4 (EV_MSC), code 4 (MSC_SCAN),value 70039
Event: time 1522111220.778787, type 1 (EV_KEY), code 14 (KEY_BACKSPACE), value 1
... the scancode
is 70039.
Now run the following command, where eventX
is the one you chose before:
$ grep "" /sys/class/input/eventX/device/id/*
The output for my keyboard is
/sys/class/input/event2/device/id/bustype:0003
/sys/class/input/event2/device/id/product:c228
/sys/class/input/event2/device/id/vendor:046d
/sys/class/input/event2/device/id/version:0110
To get the key code identifier
, either use the output of evtest
or look at the Keys and buttons section in /usr/include/linux/input-event-codes.h
for a complete list. The identifier is the part after KEY_
converted to lowercase, e.g. KEY_BACKSPACE
becomes backspace.
Configure udev
Take a look at /lib/udev/hwdb.d/
. We will create a text file in /etc/udev/hwdb.d/
with a filename beginning with a number greater than the file corresponding to our device type. For a keyboard, this can be any number greater than 60, while for a pointing stick it should be greater than 70. For example 65-keyboard-custom.hwdb
.
Use your favorite text editor, but keep in mind that you have to start it as root
, e.g.
$ sudo gedit /etc/udev/hwdb.d/65-keyboard-custom.hwdb
Add the following content
evdev:input:b[bustype]v[vendor]p[product]e[version]*
KEYBOARD_KEY_[scancode]=[key code identifier]
...where
- [bustype], [vendor], [product] and [version] have exactly 4 characters (pad with zeros if needed) and letters need to be uppercase
- [scancode] does not need padding but letters have to be lowercase
- the
evdev:...
line has no preceding space - the
KEYBOARD_KEY...
line has exactly one preceding space
In my example, the file looks like this:
evdev:input:b0003v046DpC228e0110*
KEYBOARD_KEY_70039=backspace # map CapsLock to BackSpace
The first line will be matched to your device. You can specify additional evdev:
lines and you can use more than one wildcard (*
) to match additional devices, but keep in mind that scancodes are device specific. You can also add more than one scancode mapping. Have a look at /lib/udev/hwdb.d/60-keyboard.hwdb
for inspiration. A more detailed and up-to-date version of that file can be found in the online repository.
Apply new configuration
Compile the new configuration to the hardware database:
$ sudo systemd-hwdb update
If you want to apply the changes immediately, inform udev:
$ sudo udevadm trigger
Please note that configuration values can only be added or changed while the system is running. If you remove a configuration (e.g. scancode mapping), you have to reboot for the changes to take effect.
Remember to also revert the remapping you did before (using /etc/default/keyboard
), because that will still be applied to all keyboards.