Linux 将键盘传输层报告与稳定的输入键码,以及键盘布局产生的文本或动作分离。保持这些层次的区别,可以解释 PS/2 和 USB 键盘为何能够生成不同的硬件数据,却都报告 KEY_F。
概览 #
输入遵循四个概念阶段:
- 传输驱动程序接收字节或报告。
atkbd驱动程序处理 AT 和 PS/2 扫描码集合,而usbhid解析 USB HID 用法。 - 驱动程序解析按下、释放、前缀和重复状态,然后报告 Linux 输入键码,例如带有按下、释放或重复值的
KEY_F(33)。 - 输入核心通过 evdev 发布事件。图形合成器或输入库通常将自己的布局应用于这些事件。
- 对于 Linux 虚拟控制台,内核键盘层应用活动控制台键映射,并向终端层发出字节、Unicode 字符、字符串或控制台动作。
因此,扫描码不是字符,Linux 键码也不是 X11、Wayland 或 Unicode 码点。
IBM PC、XT 和 AT 协议 #
最初的 83 键 IBM PC 和 PC/XT 键盘建立了现在称为扫描码集合 1 的字节分配。它们的接口实际上是键盘到主机:键盘提供时钟串行数据,但主机没有用于控制 LED、重复和扫描码集合选择的 AT 命令协议。普通集合 1 的按下操作使用 0x01 到 0x7f 的按下字节;释放该键时设置第 7 位。例如,Escape 按下产生 0x01,释放产生 0x81。
84 键 IBM PC/AT 键盘引入双向接口和扫描码集合 2。集合 2 改变的是位置,而不是在数值上扩展集合 1:左 Control 在集合 2 中为 0x14,在集合 1 中为 0x1d。释放序列使用 0xf0 前缀,因此左 Control 按下产生 14,释放产生 f0 14。主机可以发送命令来设置 LED、重复行为和活动集合。
PC 兼容 AT 控制器通常会将键盘集合 2 的字节转换为集合 1 形式,以实现软件兼容。因此 Linux 将许多内置键盘识别为 AT Translated Set 2 keyboard:物理键盘使用集合 2,而控制器公开转换后的字节。与 IBM 终端及后来的 PS/2 设计相关的扫描码集合 3 独立分配其他键,并支持逐键按下、释放和重复配置。当前 Linux atkbd 源代码支持转换集合 2、原始集合 2 和集合 3;集合 2 仍然是默认值。
USB 键盘不发送这些 AT 字节流。它们发送包含用法标识符的 HID 报告,HID 输入驱动程序将这些用法映射到相同的 Linux KEY_* 命名空间。
扫描码序列 #
按下也称为按下码,释放称为释放码。按住的自动重复键会在配置的延迟后生成重复的按下报告;它不会交替生成按下码和释放码。Linux 输入接口将硬件或软件自动重复表示为 EV_KEY 值 2。
0xe0 前缀扩展集合 1 和集合 2。例如,集合 1 的左 Control 使用 1d/9d,右 Control 使用 e0 1d/e0 9d。集合 2 则使用 14/f0 14 和 e0 14/e0 f0 14。0xe1 前缀引入 Pause 等特殊序列。Print Screen 和 Pause 是多字节兼容序列,而不是使用单个字节的普通按键。驱动程序必须将流解析为状态机,因为逐字节处理会产生虚假的 Shift 或 Control 事件。
“扫描码”一词也用于更高层的传输标识符。例如,evdev 的 EV_MSC/MSC_SCAN 值可以编码 USB HID 用法,如 0x70009,而关联的 EV_KEY 事件仍为 KEY_F(33)。旧集合 1 值和早期 Linux 键码之间的数值巧合是历史现象,而不是接口不变量。
内核扫描码到键码映射 #
每个低层驱动程序拥有自己的协议映射。AT 驱动程序索引一个默认且可替换的扫描码到键码表;HID 驱动程序从 HID 用法推导键码。Linux 键码在 UAPI input-event-codes.h 中定义,并扩展到原始 1–255 控制台范围之外。
setkeycodes 修改控制台 AT 键盘路径的选定映射。它接受转义的集合 1 扫描码和十进制键码等成对参数:
sudo setkeycodes e06f 112该命令不会重新映射任意 USB HID 设备,也不会定义键码 112 产生的字符。使用 loadkeys 加载的控制台键映射执行第二项操作。现代桌面输入栈改为在用户空间应用布局。
控制台键映射、修饰键和字符集 #
keymaps(5) 文件描述 Linux 虚拟控制台的转换表。keycode N = ... 根据活动修饰键掩码选择动作。内核键盘层为 Shift(1)、AltGr(2)、Control(4)、Alt(8)、ShiftL(16)、ShiftR(32)、CtrlL(64)、CtrlR(128)和 CapsShift(256)定义权重。映射文件只列出需要的组合:
keymaps 0-2,4-5,8,12
keycode 30 = +a A
control alt keycode 111 = Boot键符号可以输出字符和字符串、改变修饰键或锁定状态、切换虚拟控制台,或请求其他控制台动作。前导 + 表示字母对 Caps Lock 敏感。string F100 = "text" 绑定函数字符串动作,而组合定义会在 Compose 或死键动作之后组合两个输入字符:
compose '`' 'a' to 'à'charset "iso-8859-1" 声明控制后续符号键符号的解释方式。数字键符号在内核版本之间不可移植;应优先使用符号名称或 U+hhhh Unicode 表示法。虚拟控制台的 K_UNICODE 模式为 Unicode 键符号输出 UTF-8,而 K_XLATE 应用面向传统字节的转换。K_MEDIUMRAW 暴露面向键码的数据,K_RAW 暴露原始扫描字节的近似值。这些控制台模式不会改变 evdev ABI。
输入和控制台设备节点 #
/proc/bus/input/devices 和 /sys/class/input 枚举输入设备及其处理程序。/dev/input/eventN 是由原生 struct input_event 记录组成的二进制 evdev 流。键盘记录通常包括:
- 驱动程序提供时,用于传输标识符的
EV_MSC/MSC_SCAN; - 带有
KEY_*键码和值 0(释放)、1(按下)或 2(重复)的EV_KEY;以及 - 用于结束一个事件帧的
EV_SYN/SYN_REPORT。
使用 evtest 或 libevdev,而不是解码 hexdump,因为记录大小和时间字段遵循读取进程的用户空间 ABI:
grep -A5 -B1 -i keyboard /proc/bus/input/devices
sudo evtest /dev/input/event4读取事件节点需要系统输入设备策略分配的权限,并可能暴露在该设备上输入的任何密码。
/dev/tty1、/dev/tty2 和其他虚拟终端节点公开经过控制台键映射转换和行规程处理后的终端输入。/dev/tty0 指活动虚拟终端;/dev/console 指配置的系统控制台,而不是原始键盘设备。
控制台工具 #
应从真实虚拟终端而不是终端模拟器运行控制台模式工具:
showkey -s、showkey -k和showkey -a显示扫描码、键码和面向字符的视图。程序被中断时,原始模式可能使控制台暂时不可用。getkeycodes显示 AT 驱动程序当前的扫描码映射。setkeycodes SCANCODE KEYCODE ...更改选定的 AT 映射。dumpkeys以keymaps(5)语法写入当前控制台布局;dumpkeys --long-info还会列出已知键符号。loadkeys FILE将键映射编译并安装到内核控制台表中。
上游 Linux 输入文档、事件码规范、当前 atkbd 源代码 和上游 keymaps(5) 定义了此处描述的接口。
评论
在 GitHub 上阅读或评论(发表评论需要登录)