Map scancodes to keycodes (简体中文)
该页面假定您已阅读 Extra keyboard keys (简体中文) ,它提供了更多的内容。
将“扫描码”映射为“键位码”的动作,要比 Xorg 和 Linux 控制台更接近底层,所以对这个映射的更改在两者中均有效。
将“扫描码”映射到“键位码”方式有两种:
- 使用 udev (简体中文)
- Using setkeycodes(8)
首选方法是使用 “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) 。
.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>中指定十进制值。
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
要阻止 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
,在每次重新启动时自动更新。
/usr/lib/systemd/system/systemd-hwdb-update.service
# 该文件是systemd的一部分。 . . #ConditionNeedsUpdate=/etc . .
在systemd-hwdb-update.service
完成加载后,systemd-trigger.service
将从hwdb.bin
重新加载更改。
- Systemd(简体中文)升级后自动。
在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 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