# Map scancodes to keycodes (简体中文)

From ArchWiki
Jump to navigation Jump to search
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 (简体中文)
- Using setkeycodes(8)

首选方法是使用 “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

This article or section is out of date.

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 (简体中文)#)

The factual accuracy of this article or section is disputed.

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(简体中文)升级后自动。

在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

## 参阅

- B站 JimMoen：Linux通用键位修改（中）-实际操作
- 依云's Blog：Poker II 键盘调教记

Category:

- Keyboard configuration (简体中文)

Hidden categories:

- Pages or sections flagged with Template:Out of date
- Pages or sections flagged with Template:Accuracy