Map scancodes to keycodes (简体中文)

From ArchWiki
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
翻译状态:本文是 Map scancodes to keycodes翻译。上次翻译日期:2021-02-01。如果英文版本有所更改,则您可以帮助同步翻译。

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

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

[1][2][3]

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

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

确定扫描码

您需要知道要重新映射的键的“扫描代码”。详情请见Extra keyboard keys (简体中文)#第一步: 映射扫描码

使用 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

参阅