拯救一个物理损坏的 P 键:udev hwdb 改键实战

症状

笔记本(ThinkPad X230 Tablet,Ubuntu 24.04,GNOME Wayland)的 P 键突然完全无响应,其他按键一切正常。P 是英文里最高频的字母之一,这个键坏了基本没法打字。

排查:先排除软件原因

单键失效的”凶手”很多时候不是硬件,而是软件层拦截。逐项排查:

  1. 改键守护进程:keyd / kanata / kmonad / input-remapper / interception 等,一个都没在跑,也没有它们的配置文件
  2. udev hwdb 覆盖:/etc/udev/hwdb.d/ 为空,udevadm info 里没有任何针对 P 键的 KEYBOARD_KEY_* 属性
  3. XKB 选项:GNOME 的 xkb-options 是空的
  4. 无障碍功能:Slow Keys / Bounce Keys 都是关闭状态
  5. 输入法:ibus 运行的是 passthrough 的 “simple” 引擎,布局就是普通 us

全部排除后,进入硬件验证。

事件捕获:确认按键是否产生任何事件

Wayland 下没有 root 权限读不了 /dev/input/eventX(需要 input 组),常见的 xev 又依赖 XWayland 焦点转发,实测不可靠(窗口反复拿到 KeymapNotify,却一个 KeyPress 都没有)。换用 GTK3 原生窗口直接监听 key-press-event,这是 Wayland 原生路径,和平时打字走的是同一条链路:

1
2
3
4
5
6
7
# 关键部分:窗口上直接打印 keyval / hardware_keycode
def on_key(widget, event):
print(f"keyval={event.keyval} hwcode={event.hardware_keycode} "
f"scancode={getattr(event, 'scancode', None)}", flush=True)
return True

win.connect('key-press-event', on_key)

测试结果:

1
2
3
O 键    → keyval=111(o)      hwcode=32   ✓ 正常上报
Enter → keyval=65293(Return) hwcode=36 ✓ 正常上报
P 键 → (按了 5 次,零事件)

O 和 P 走完全相同的路径(键盘 → 内核 → evdev → compositor → 应用),O 正常而 P 连内核层都没到,结论就是 P 键的机械开关/导电膜物理损坏。

选一个”替身”键

改键的候选键需要满足两个条件:

  1. 硬件上可用:键盘矩阵断线通常是一整列坏掉。P 所在的列是 P ; / Right Shift,所以必须先用上面的捕获窗口验证候选键本身是好的
  2. hwdb 能干净处理:udev hwdb 的 KEYBOARD_KEY_<scancode> 对非扩展扫描码最可靠。AT 键盘(Set 1 转译)里:
    • CapsLock = 0x3a,Right Shift = 0x36 —— 都是非扩展码 ✓
    • Right Ctrl(E0 1D)、Right Alt(E0 38)—— 扩展码,hwdb 处理起来很别扭 ✗

最终选择 Right Shift:打字时右手小指本来就停在 P 键下方这一列,往下滑一格就是它,几乎不改变肌肉记忆。代价是大写全部改用 Left Shift(左右手交替按 Shift 本来就是标准指法,影响不大)。

修复:一条 hwdb 规则

新建 /etc/udev/hwdb.d/61-p-remap.hwdb:

1
2
3
4
# Make Right Shift emit P (broken P key replacement)
# ThinkPad X230 Tablet AT keyboard — 复用系统自带 60-keyboard.hwdb 的匹配块
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*:pvrThinkPad*X2*T*:rvn*
KEYBOARD_KEY_36=p

匹配块直接复用 /lib/udev/hwdb.d/60-keyboard.hwdb 里本机已有的 ThinkPad 条目,保证规则精确落在内部 AT 键盘上。KEYBOARD_KEY_36=p 的含义:扫描码 0x36(Right Shift)现在上报为 KEY_P。

生效:

1
2
3
sudo cp 61-p-remap.hwdb /etc/udev/hwdb.d/
sudo systemd-hwdb update
sudo udevadm trigger --subsystem-match=input

不需要重启。原理:/lib/udev/rules.d/60-evdev.rules 对非 remove 的设备事件都会重新导入 hwdb 属性并运行 keyboard builtin,后者通过 EVIOCSKEYCODE ioctl 现场改写键码映射。

验证:按 Right Shift 直接打出 p,Left Shift + Right Shift 打出大写 P。

撤销方法

如果键盘换新了,想恢复原样:

1
2
3
sudo rm /etc/udev/hwdb.d/61-p-remap.hwdb
sudo systemd-hwdb update
sudo udevadm trigger --subsystem-match=input

小结

  • 单键失灵先查软件层(改键工具、hwdb、xkb、无障碍、输入法),再上事件捕获
  • Wayland 下抓键事件用 GTK 原生窗口比 xev 靠谱
  • 改键前先验证”替身”键本身可用(矩阵断线往往是一整列)
  • hwdb 改键无需额外守护进程,开机、登录界面、TTY 全部生效,持久且可随时撤销