Map scancodes to keycodes (简体中文)

From ArchWiki
翻译状态:本文是 Map scancodes to keycodes翻译。上次翻译日期:2021-02-01。如果英文版本有所更改,则您可以帮助同步翻译。

该页面假定您已阅读 Extra keyboard keys (简体中文) ,它提供了更多的内容。

将“扫描码”映射为“键位码”的动作,要比 Xorg 和 Linux 控制台更接近底层,所以对这个映射的更改在两者中均有效。

[1][2][3]

将“扫描码”映射到“键位码”方式有两种:

首选方法是使用 “udev”,因为它使用硬件信息(这是一个非常可靠的来源)来选择数据库中的键盘型号。如果能在数据库中找到键盘型号,则可以“开箱即用”地识别您的按键。

确定扫描码

您需要知道要重新映射的键的“扫描代码”。详情请见Keyboard input#Identifying scancodes

使用 udev

udev (简体中文)提供了一个称为"hwdb"的内置函数,用于维护 /etc/udev/hwdb.bin 中的硬件数据库索引。 数据库是从位于/usr/lib/udev/hwdb.d//run/udev/hwdb.d//etc/udev/hwdb.d/ 目录中的扩展名为.hwdb的文件编译而成的。 默认的 "扫描码到键码" 映射文件是 /usr/lib/udev/hwdb.d/60-keyboard.hwdb 。 有关详细信息,请参见udev(7)

注意: 从systemd 220开始,udev ABI进行了更改。 使用自定义udev hwdb规则的用户应根据新的ABI更新它们

.hwdb 文件可以包含不同键盘的多个映射块,也可以将一个块应用于多个键盘。 evdev: 前缀用于将块与硬件进行匹配,支持以下硬件匹配:

  • USB内核模式标识的通用输入设备(也包括USB键盘):
evdev:input:b<bus_id>v<vendor_id>p<product_id>e<version_id>-<modalias> 

其中 <vendor_id>, <product_id><version_id> 是 4位十六进制大写(不够4位在前面补0)的供应商,产品和版本ID ( 你可以通过运行lsusb 命令来得到它们), <modalias> 是描述设备功能的任意长度的输入模态 (input-modalias). <bus_id> 是4位数的十六进制总线ID,对于USB设备,应为0003。<bus_id> 可能的值在 /usr/include/linux/input.h 中定义 (你可以运行 awk '/BUS_/ {print $2, $3}' /usr/include/linux/input.h 得到一个列表).

evtest或者 cat /proc/bus/input/devices 可以一次性得到<bus_id>、<vendor_id>、 <product_id> 和 <version_id>

很多时候可以不用那么精确,就可以用 *来作为通配符。

  • AT键盘DMI数据匹配:
evdev:atkbd:dmi:bvn*:bvr*:bd*:svn<vendor>:pn<product>:pvr*

其中 <vendor><product> 是由内核 DMI Modalias 导出的固件提供的字符串。

  • 输入驱动程序设备名称和DMI数据匹配:
evdev:name:<input device name>:dmi:bvn*:bvr*:bd*:svn<vendor>:pn*

其中 <input_device_name> 是驱动程序指定的名称设备 ,<vendor> 是由内核DMI modalias导出的固件提供的字符串。

块主体中每一行的格式为 KEYBOARD_KEY_<scancode>=<keycode>. <scancode> 的值是十六进制的,但是没有前导0x(即,是a0而不是0xa0), 而 <keycode> 的值是小写的键名名称字符串,就像在/usr/include/linux/input-event-codes.h中列的一样(请参阅KEY_<KEYCODE> 变量),[4] 处有一个排序列表。注意不能在<keycode>中指定十进制值。

注意: 用root身份运行evemu-describe来获取 这个设备(要自定义hwdb 规则的设备)的标识符, 该实用程序由 evemu 软件包提供。 通过以root用户身份运行 evtest 这个设备(要自定义hwdb 规则的设备)的标识符以及要使用的按键的扫描码。 该实用程序由evtest软件包提供。

自定义hwdb的示例

示例hwdb文件将匹配所有AT键盘:

/etc/udev/hwdb.d/90-custom-keyboard.hwdb
evdev:atkbd:dmi:bvn*:bvr*:bd*:svn*:pn*:pvr*
 KEYBOARD_KEY_10=suspend
 KEYBOARD_KEY_a0=search

这是在笔记本电脑和USB键盘上重新绑定修饰符(modifiers)的示例:

/etc/udev/hwdb.d/10-my-modifiers.hwdb
evdev:input:b0003v05AFp8277* # 在Kensington Slim Type USB(使用旧的ABI)上进行了测试
 KEYBOARD_KEY_70039=leftalt  # 将capslock绑定到leftalt
 KEYBOARD_KEY_700e2=leftctrl # 将leftalt绑定到leftctrl

evdev:atkbd:dmi:*            # 内置键盘:匹配所有AT键盘
 KEYBOARD_KEY_3a=leftalt     # 将capslock绑定到leftalt
 KEYBOARD_KEY_38=leftctrl    # 将leftalt绑定到leftctrl
提示: 上面的绑定的含义 A绑定到B,是指:在物理上按下A按键,计算机输入B按键

要阻止 Sleep键,请将其绑定到 "reserved" 关键字。 或者,您可以使用 "unknown" 将其映射到 NoSymbol键。 例如:

/etc/udev/hwdb.d/90-block-sleep.hwdb
evdev:input:b0003v03F0p020C* # hp 5308 keyboard controller
 KEYBOARD_KEY_10082=reserved

更新硬件数据库索引

更改配置文件后,需要重建硬件数据库索引 hwdb.bin

  • 通过运行一下命令手动更新 hwdb.bin
# systemd-hwdb update

Tango-view-refresh-red.pngThis article or section is out of date.Tango-view-refresh-red.png

Reason: ConditionNeedsUpdate=/etc seems to be commented out by default in systemd-hwdb-update.service (checked in systemd 232). (Discuss in Talk:Map scancodes to keycodes (简体中文))

Tango-inaccurate.pngThe factual accuracy of this article or section is disputed.Tango-inaccurate.png

Reason: Do not edit in /usr/lib/, use systemctl edit. (Discuss in Talk:Map scancodes to keycodes (简体中文))
  • 通过注释掉systemd-hwdb-update.service 中的ConditionNeedsUpdate,在每次重新启动时自动更新。
/usr/lib/systemd/system/systemd-hwdb-update.service
#  该文件是systemd的一部分。
.
.
#ConditionNeedsUpdate=/etc
.
.

systemd-hwdb-update.service完成加载后,systemd-trigger.service将从hwdb.bin重新加载更改。

Systemd (简体中文)的每次升级中,安装脚本都通过以root用户身份运行udevadm hwdb --update来重建hwdb.bin,因此我们不需要关心它 。

重新加载硬件数据库索引

内核在启动过程中加载hwdb.bin,重新启动系统将保证加载更新后的hwdb.bin

使用 udevadm,可以通过运行下面命令来从更新后的hwdb.bin加载新的键映射。

# udevadm trigger

Be aware that with udevadm only added or changed key mapping are loaded so if we delete a mapping from the config file, rebuild hwdb.bin and run udevadm trigger as the root user, then the deleted mapping still kept by the kernel, at least until a reboot. 请注意,使用 udevadm 仅加载已添加或更改的键映射,因此,如果我们从配置文件中删除映射,重建 hwdb.bin 并以root用户身份运行 udevadm trigger ,那么删除的映射仍将保留在内核中,直到重新启动为止。

查询数据库

您可以通过按键或运行 udevadm info 来检查配置是否已加载。 对于上面示例中的USB键盘,这将输出我们配置的映射,如下所示:

# udevadm info /dev/input/by-path/*-usb-*-kbd | grep KEYBOARD_KEY
E: KEYBOARD_KEY_70039=leftalt
E: KEYBOARD_KEY_700e2=leftctrl

使用setkeycodes

setkeycodes 是一个将 scancodes-to-keycodes 映射表加载到Linux内核中的工具。 它的用法是:

# setkeycodes scancode keycode ...

可以一次指定多个对。 扫描码(Scancodes) 以十六进制给出, 键位码 以十进制给出。

注意: 显然, setkeycodes 不适用于USB键盘(Linux 3.14.44-1-lts):
# setkeycodes 45 30     # 将NumLock(0x45)绑定到AT键盘上的KEY_A(30)
(successful)
# setkeycodes 70053 30  # 将NumLock(0x70053)绑定到USB键盘上的KEY_A(30)
KDSETKEYCODE: Invalid argument
failed to set scancode 620d3 to keycode 31

参阅