将扫描码映射到键码
本页面假定您已阅读 键盘输入,其中提供了更广泛的背景信息。
扫描码到键码的映射在 Xorg 和 Linux 控制台之下的层级实现,这意味着对此映射的更改在两者中都有效。[1][2][3] 请注意,此方法只能用于简单的 1:1 按键重映射;有关允许在同一低级别进行更复杂重映射的程序,请参阅 输入重映射实用程序。
有两种方法可以将扫描码映射到键码
- 使用 udev
- 使用 setkeycodes(8)
首选方法是使用 udev,因为它使用硬件信息(这是一个非常可靠的来源)在数据库中选择键盘型号。这意味着如果您的键盘型号已在数据库中找到,则您的按键将被开箱即用地识别。
识别扫描码
您需要知道要重映射的按键的扫描码。有关详细信息,请参阅 键盘输入#识别扫描码。
使用 udev
udev 提供了一个名为 hwdb 的内置函数,用于维护 /etc/udev/hwdb.bin
中的硬件数据库索引。该数据库从扩展名为 .hwdb 的文件编译而来,这些文件位于目录 /usr/lib/udev/hwdb.d/
、/run/udev/hwdb.d/
和 /etc/udev/hwdb.d/
中。默认的扫描码到键码映射文件是 /usr/lib/udev/hwdb.d/60-keyboard.hwdb
。有关详细信息,请参阅 hwdb(7)。
.hwdb 文件可以基于硬件 ID glob 模式将按键映射应用于一个或多个键盘。您可以通过以 root 用户身份运行 evemu-describe(1) 来获取设备识别信息。此命令由 evemu 软件包提供。
evdev:
前缀用于将硬件与映射块进行匹配。支持以下硬件匹配
- 由 usb 内核 modalias 识别的通用输入设备(也包括 USB 键盘)
evdev:input:b<bus_id>v<vendor_id>p<product_id>e<version_id>-<input_modalias>
- 带有以下 4 位十六进制大写字段
<vendor_id>
、<product_id>
和<version_id>
:供应商 ID、产品 ID 和版本 ID,与lsusb
命令的输出匹配。<bus_id>
是 4 位十六进制总线 ID,对于 usb 设备应为 0003。可能的<bus_id>
值在/usr/include/linux/input.h
中定义(您可以运行awk '/BUS_/ {print $2, $3}' /usr/include/linux/input.h
来获取列表)。<input_modalias>
是描述设备功能的任意长度的 input-modalias。其他字段足以唯一标识设备,因此您可以在此处使用 glob。
- 如果您当前已将设备插入计算机,则可以使用 sysfs 一次获取整个 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>
中指定十进制值。
示例
重映射所有设备
假设我们要为所有 AT 键盘重映射几个常用按键
/etc/udev/hwdb.d/90-custom-keyboard.hwdb
evdev:atkbd:* KEYBOARD_KEY_10=suspend KEYBOARD_KEY_a0=search
重映射特定设备
假设我们要重映射当前碰巧已插入的设备。您应该已经有了 evdev 路径(例如 /dev/input/event17
)以及扫描码(例如 caps lock 的 70039
)。现在,使用事件编号,您可以查询 sysfs 以获取 modalias
cat /sys/class/input/event17/device/modalias
input:b0003v32ACp0012e0111-e0,1,4,1...
此设备可以使用以下 hwdb 规则进行匹配
/etc/udev/hwdb.d/90-remap.hwdb
evdev:input:b0003v32ACp0012e0111* KEYBOARD_KEY_70039=rightctrl # This example maps the 70039 scancode to the "rightctrl" keycode.
禁用按键
要阻止 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
- 通过使用 替换单元文件 注释掉
systemd-hwdb-update.service
中的ConditionNeedsUpdate
,在每次重启时自动更新
/etc/systemd/system/systemd-hwdb-update.service
# This file is part of systemd. . . #ConditionNeedsUpdate=/etc . .
在 systemd-hwdb-update.service
完成加载后,systemd-trigger.service
将从 hwdb.bin
重新加载更改。
- 在 systemd 升级后自动更新。
在每次升级 systemd 时,30-systemd-hwdb.hook
会通过以 root 用户身份运行 systemd-hwdb --usr update
来重建 hwdb.bin
,因此我们无需担心。
重新加载硬件数据库索引
内核在启动过程中加载 hwdb.bin
,重启系统将确保加载更新后的 hwdb.bin
。
使用 udevadm
可以通过运行以下命令从更新后的 hwdb.bin
加载新的按键映射
# udevadm trigger
请注意,使用 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 是一个将扫描码到键码映射表加载到 Linux 内核的工具。其用法是
# setkeycodes scancode keycode ...
可以一次指定多个对。扫描码以十六进制给出,键码以十进制给出。
显然 setkeycodes 不适用于 USB 键盘 (Linux 3.14.44-1-lts)
# setkeycodes 45 30 # bind NumLock (0x45) to KEY_A (30) on AT keyboard (successful) # setkeycodes 70053 30 # bind NumLock (0x70053) to KEY_A (30) on USB keyboard KDSETKEYCODE: Invalid argument failed to set scancode 620d3 to keycode 31
如果使用此简单命令,更改将在重启后丢失。可以通过创建新服务使更改永久生效
/etc/systemd/system/setkeycodes.service
[Unit] Description=Change keycodes at boot [Service] Type=oneshot ExecStart=/usr/bin/setkeycodes [scancode] [keycode] [scancode] [keycode] [...] [Install] WantedBy=multi-user.target
并启用 setkeycodes.service
。