扫描码映射至键位码 - Arch Linux 中文维基

跳转到内容

# 扫描码映射至键位码

来自 Arch Linux 中文维基
翻译状态：

- 本文（或部分内容）译自 Map scancodes to keycodes，最近一次同步于 2021-02-01，若英文版本有所更改，则您可以帮助同步与翻译更改的内容。
- 您可以在 ArchWiki 的对应页面找到本文翻译的原始修订历史。
- 本文可能与英文原文存在出入。

该页面假定您已阅读 Extra keyboard keys ，它提供了更多的内容。

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

[1][2][3]

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

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

注意：从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

本文或本节内容已经过时。

原因： `ConditionNeedsUpdate=/etc` seems to be commented out by default in `systemd-hwdb-update.service` (checked in systemd包 232). (在Talk:扫描码映射至键位码讨论)

本文或本章节的事实准确性存在争议。

原因： Do not edit in `/usr/lib/`, use systemctl edit.（在 Talk:扫描码映射至键位码 中讨论）

- 通过注释掉`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 键盘调教记

分类：​

- 键盘配置
- 使用“模板:过时”标记的页面或章节

隐藏分类：​

- 使用“模板:准确性”标记的页面或章节

扫描码映射至键位码
添加话题