Scancodes, keycodes, evdev, X
Если вы вдруг захотите собрать себе кастомную клавиатуру (например на QMK), добавить к ней нестандартных клавиш и использовать эти клавиши в иксах, то надо понимать некоторые взаимосвязанные термины. Увы, материалов, внятно их объясняющих, не так уж и много. Поэтому себе для памяти и вдруг кому пригодится, пишу эту заметку.
Клавиатура, скорее всего, подключается через USB. USB-клавиатуры используют протокол HID для передачи сигнала о нажатии клавиши. Какая клавиша нажата – определяется так называемым scancode-ом.
Оговорка по поводу QMK – scancode-ы не прямо соответствуют кодам клавиш в QMK, это верно только для идентификаторов, объявленных в перечислении hid_keyboard_keypad_usage
в quantum/keycode.h
, прочие обрабатываются особым образом. Следует отметить, что scancode-ы могут быть как 8-битными, так и 16-битными. 8-битные считаются “стандартными”, хотя не все scancode-ы распознаются во всех ОС, а 16-битные – “vendor-specific”.
Scancode-ы затем драйвер клавиатуры преобразует в keycode-ы. Keycode – это внутреннее представление кода клавиши в ядре. Список всех keycode-ов можно найти в исходниках ядра.
Нестандартные сопоставления определяются через hwdb в udev, см. /lib/udev/hwdb.d/60-keyboard.hwdb
. Найти табличку стандартных мне сходу не удалось, но всегда можно выяснить методом тыка, используя скажем showkey
или evtest
.
UPD: В исходниках qemu нашлась табличка сопоставлений между scancode-ами, keycode-ами и keysym-ами для разных драйверов: https://github.com/qemu/keycodemapdb/blob/master/data/keymaps.csv. Если вдруг файл переместится куда-то, то вот форк
Подробности добавления своих записей в hwdb есть в Arch Wiki.
Когда keycode попадает в иксы, видимо чтобы нам не было скучно, к нему прибавляется 8. Поэтому showkey
будет показывать keycode на 8 меньше, чем скажем xev
.
В иксах, keycode-ы преобразуются в keysym-ы. Происходит это через xkb. Собственно, подробное обсуждение xkb выходит за рамки этой заметки, но посмотреть таблицу сопоставлений можно используя xmodmap -pke
. Каждому keycode-у может быть сопоставлено до 8 keysym-ов (потенциально до 10, но я не видел примеров), нужный выберется в зависимости от активных модификаторов (shift, level3, mode_switch, level5). Сами по себе модификаторы тоже назначаются на клавиши, но эту тему мы пока оставим (посмотреть можно сделав xmodmap -pm
).
Наконец, keysym-ы получает собственно что там их должно получать (например, менеджер окон или приложение).
Если вдруг хочется странного – можно это самое “странное” добавить на любом этапе. Увы, для действительно странного – на практике это часто выливается в “добавить на каждом этапе”.
Пример: я хочу использовать какую-нибудь бесполезную клавишу как модификатор для управления менеджером окон (xmonad
). Теоретически, достаточно назначить соответствующий scancode для qmk и вперёд? Но нет, не тут то было.
Допустим, я выбрал scancode 143 (в спецификации HID это “international 9”, и насколько мне известно, он не используется нигде). Но не тут-то было! evdev не знает про такую клавишу. Придётся добавить преобразование в keycode руками.
Какой keycode выбрать? Навскидку кажется что тоже какой-нибудь бесполезный, но вот беда, бесполезных-то не так уж и много. Зато есть пропущенные, например keycode 84 никак нельзя выдать из evdev (что неудачно, поскольку в иксах это 92 = ISO_Level3_Shift, и с ним дальше было бы всё проще). Если не патчить ядро, приходится использовать какой-нибудь теоретически полезный, но не используемый лично в моей раскладке keycode.
Но в X11 этот keycode уже имеет какой-то мистический смысл, а мне надо назначить его на модификатор. Приходится ко всему прочему ковырять раскладку xkb чтобы переназначить.
Конечно, проще взять scancode, сопоставленный с каким-нибудь неиспользуемым keysym-ом, и перебить только раскладку в xkb. Например, переключатель хираганы/катаканы с японской клавиатуры (если, конечно, клавиатура не японская).
Использованные материалы:
- https://www.reddit.com/r/olkb/comments/8me48x/understanding_qmktmk_scancodes_and_adding_our_own/
- https://github.com/torvalds/linux/blob/334c0ef6429f261c7f53dc035632435ffbc0c60d/include/uapi/linux/input-event-codes.h
- https://github.com/kmonad/kmonad/blob/2f9707b6e4ec16319dbccfcceaf741b7d9876cfa/src/KMonad/Keyboard/Keycode.hs