Linux, клавиатура и udev hwdb

3 мин

Решил я обновить свою рабочую лошадку Lenovo на почти такую же, только чуть свежее. Все супер, но инженеры Lenovo, зачем-то решили вместо клавиши Ins впихнуть End, а Ins вовсе перенести на функциональную клавишу. Инженеры Lenovo, вы в курсе, что в Linux, сочетание Ctrl+Ins и Shift+Ins - это аналог Ctrl+c/Ctrl+v? Зачем вы перенесли Ins? Зачем там End? Я даже не помню, когда последний раз пользовался клавишей End! Это все риторические вопросы, на которые я вряд ли узнаю логичные ответы. Но с этим нужно что-то делать.

Я уже делал заметку, как с помощью xmodmap в Xorg отключать клавиши (еще один пламенный привет Lenovo и инженерному гению, который разместил PgUp и PgDown над стрелками). Таким же способом, можно и переназначать клавиши. Но иногда этот метод не работает. Например, если у вас Wayland. В таком случае мы воспользуемся более низкоуровневым решением с помощью udev.

Udev предоставляет встроенную функцию, которая называется hwdb и служит для обслуживания базы данных оборудования. База данных собирается из разных файлов *.hwdb. Более подробно о hwdb можно почитать в официальной документации. Нас же интересует файл /usr/lib/udev/hwdb.d/60-keyboard.hwdb, в котором описывается соответствие сканкодов к кейкодам по умолчанию. Формат этого файла простой - строка, которая начинается с префикса evdev: содержащая идентификатор устройства (или ряда устройств) и последующего блока соответствий. Каждая строка блока выглядит как KEYBOARD_KEY_<scancode>=<keycode>, где scancode - это шестнадцатеричное значение, а keycode - строка из /usr/include/linux/input-event-codes.h без KEY_, строчными буквами, например insert или tab. C кейкодом все понятно, осталось найти сканкод.

Для начала найдем нашу клавиатуру. Для этого выполним команду cat /proc/bus/input/devices и поищем там нашу клаву. Вывод команды для моей клавиатуры следующий:

I: Bus=0011 Vendor=0001 Product=0001 Version=ab83
N: Name="AT Translated Set 2 keyboard"
P: Phys=isa0060/serio0/input0
S: Sysfs=/devices/platform/i8042/serio0/input/input2
U: Uniq=
H: Handlers=sysrq kbd leds event2 
B: PROP=0
B: EV=120013
B: KEY=402000000 3803078f800d001 feffd6dfffefffff fffffffffffffffe
B: MSC=10
B: LED=7

Запоминаем хендлер - event2. Теперь нам понадобиться утилита evtest, которая есть в репозиториях популярных дистрибутивов. Запускаем ее командой sudo evtest /dev/input/event2, подставляя хендлер. Затем жмем нужную клавишу. У меня вывод такой:

Event: time 1710420949.452477, -------------- SYN_REPORT ------------
Event: time 1710420956.404526, type 4 (EV_MSC), code 4 (MSC_SCAN), value cf
Event: time 1710420956.404526, type 1 (EV_KEY), code 109 (KEY_END), value 1

Нам нужно значение MSC_SCAN - cf.

Если мы не хотим сильно вдаваться в подробности, то можем переназначить клавиши для всех устройств типа клавиатура. В моем случае файл hwdb выглядит так:

cat /etc/udev/hwdb.d/61-keyboard-local.hwdb
evdev:atkbd:*
 KEYBOARD_KEY_cf=insert
 KEYBOARD_KEY_c9=reserved
 KEYBOARD_KEY_d1=reserved

Здесь видно, что я, используя найденный сканкод cf, переназначаю Ins на клавишу End. А так же отключаю PgUp и PgDown с помощью ключевого слова reserved. Их сканкоды я нашел уже вышеупомянутым путем.

После того, как мы создали конфиг, нужно обновить базу

sudo systemd-hwdb update

и применить изменения

sudo udevadm trigger /dev/input/event2

Если же мы хотим переназначить клавиши только для определенной клавиатуры, то нам необходимо найти ее идентификатор. Это может быть идентификатор общих устройств ввода, который имеет формат

evdev:input:b<bus_id>v<vendor_id>p<product_id>e<version_id>-<input_modalias>

Всю информацию можно найти в выводе команды cat /proc/bus/input/devices или cat /sys/class/input/<handler>/device/modalias. Все, после <version_id> можно опустить, заменив на * и тогда строка идентификатора, в моем случае, будет выглядеть следующим образом:

evdev:input:b0011v0001p0001eAB83*

Так же это может быть идентификатор с именем устройства и DMI данными, следующего формата:

evdev:name:<input device name>:dmi:bvn*:bvr*:bd*:svn<vendor>:pn*

Для того, чтобы найти эти данные, вы можете воспользоваться утилитой evemu-describe, которая является частью пакета evemu или evemu-tools, в зависимости от вашего дистрибутива.