SuperAPI AI 工具集

diagnose-hid-keycodes

通用与其它

查看项目

更新于 10-16 02:06

技能介绍

用途

该技能用于在 macOS 上查明未知 USB 或蓝牙 HID 设备(宏键盘、鼠标按键、脚踏板、HID 小工具等)的每个按键在操作系统层面实际发出的键码,而不是根据标签、厂商文档或照片猜测。廉价 HID 键盘常带有任意或错误标注的键码,例如 Jieli/Free3-P 的 top/middle/bottom 按键实际发出 Ctrl+C/Ctrl+V/Ctrl+X,并非符合剪切/复制/粘贴惯例,而是硬件随机分配。

何时使用

  • 新 HID 设备到货,不知道其按键发出什么。
  • 键盘有多个固件模式,需要映射每个模式的键码。
  • 某条规则未触发,怀疑猜错了 from.key_code。
  • 需要为可复现的配置记录设备信息。

三工具工作流

工具 用途
Karabiner ignore: true 让 Karabiner 观察设备而不抓取它
Karabiner-EventViewer 以文本形式显示原始 HID 事件
Quartz screencapture -l 在不抢占焦点的情况下捕获 EventViewer 窗口

关键点在于 ignore: true:启用后 Karabiner 不重映射任何内容,但仍记录设备事件,因此可以看到固件发出的原始键码。

工作流

步骤 1 — 识别设备的 VID/PID

# USB
ioreg -p IOUSB -l -w 0 | grep -B 2 -A 6 "<product name or partial>"

# Bluetooth (after pairing)
system_profiler SPBluetoothDataType | grep -A 15 "<pad name>"

以十进制记录 VID/PID(Karabiner 的 JSON 格式)。

步骤 2 — 添加 no-op 诊断规则(强制 Karabiner 抓取设备)

为什么不在 devices[] 中直接使用 "ignore": true?那会告诉 Karabiner 完全不管该设备,EventViewer 也就看不到它的事件。ignore: true 用于“不要动这个设备”,而不是“检查这个设备”。

正确做法:添加一条限定到该设备的惰性 complex_modifications 规则。Karabiner 会抓取设备(因此 EventViewer 能捕获每个 HID 报告),但规则本身不做任何事。编辑 ~/.config/karabiner/karabiner.json → profile 0 → complex_modifications.rules 并插入:

{
  "description": "[DIAGNOSTIC] Grab <pad> (no remap)",
  "manipulators": [
    {
      "type": "basic",
      "from": { "key_code": "vk_none" },
      "to": [{ "key_code": "vk_none" }],
      "conditions": [
        {
          "type": "device_if",
          "identifiers": [{ "vendor_id": 19530, "product_id": 16725 }]
        }
      ]
    }
  ]
}

vk_none 是 Karabiner 的虚拟键,永远不会匹配真实输入,因此该 manipulator 是惰性的。device_if 限定使 Karabiner 抓取设备以供检查。

重新加载 Karabiner:Karabiner-Elements 菜单栏图标 → Restart Karabiner-Elements。

步骤 3 — 打开 EventViewer 并按下每个按键

open -a "Karabiner-EventViewer"
  • Main 标签页:显示 key_down / key_up 及解码后的键码名称(c、left_control、page_up 等)。
  • Devices 标签页:显示每个事件来自哪个设备,确认抓取的是正确的 VID/PID。
  • Unknown Events 标签页:显示 Karabiner 无法解码的事件,适用于消费者按键或自定义 HID 描述符。

缓慢按下每个按键。对于在一个 HID 报告中发出的修饰键组合(廉价键盘常见),会看到多个 key_down 事件紧密连续出现:

13:44:02.123  key_down  left_control
13:44:02.123  key_down  c
13:44:02.198  key_up    c
13:44:02.198  key_up    left_control

left_control + c 具有相同的微秒时间戳 = 在一个 HID 报告中发出,因此需要同时匹配器。

步骤 4 — 在不抢占焦点的情况下捕获

如果把 EventViewer 带到前台来读取,就会失去在测试窗口上按键的能力。变通方法——按窗口 ID 捕获:

# List windows; find EventViewer's window ID
python3 -c '
from Quartz import CGWindowListCopyWindowInfo, kCGWindowListOptionAll, kCGNullWindowID
for w in CGWindowListCopyWindowInfo(kCGWindowListOptionAll, kCGNullWindowID):
    if "EventViewer" in w.get("kCGWindowOwnerName", "") or "EventViewer" in w.get("kCGWindowName", ""):
        print(w["kCGWindowNumber"], w.get("kCGWindowName"))
'

# Screenshot that window without focusing it
screencapture -l <WID> -o -x /tmp/eventviewer.png

-l <WID> 捕获特定窗口,-o 排除阴影,-x 抑制捕获声音。窗口无需处于前台。

步骤 5 — 对每个固件模式重复(蓝牙键盘)

许多廉价蓝牙键盘有未记录的固件模式,由按键组合触发(按住所有 3 个键 5 秒、按住顶部键 10 秒等)。每个模式可能发出完全不同的键码。对于发现的每个模式:

  1. 将键盘切换到该模式。
  2. 重复步骤 3——记录每个按键的键码。
  3. 记录到表格中。

示例(Jieli/Free3-P):

模式 Top Middle Bottom
1 volume_increment volume_decrement spacebar (play/pause)
4 page_up page_down equal_sign

步骤 6 — 清理

从 complex_modifications.rules 中移除 [DIAGNOSTIC] 规则并重新加载 Karabiner。或者通过将 vk_none 替换为实际的 from / to 绑定,将其转换为真正的重映射规则。

避免触发 Touch ID 的审计

在此工作流中,不要查询 TCC.db 或 /Library/Application Support/com.apple.TCC/ 下的 SQLite 文件来“审计权限”——这些查询需要 sudo,并且每次调用都会触发 Touch ID 提示。改用:

# Non-sudo audit: is Karabiner actually grabbing the device?
karabiner_cli --list-connected-devices | jq '.[] | select(.product == "<pad-name>")'
# Returns { ..., "is_grabbed": true/false } — same info, no biometric prompt

可用的工具本身就是审计。这是通过艰难方式发现的;参见 ../configure-macro-keyboard/references/04-anti-patterns.md → “Sudo-based TCC.db audits trigger Touch ID”。

深入参考

  • ../configure-macro-keyboard/references/03-patterns.md — “ignore: true diagnostic” + “Quartz window-ID capture” 模式的完整内容。
  • ../configure-macro-keyboard/references/04-anti-patterns.md — 顶层 {"any": "key_code"} 会静默失败;位置推断错误。
  • ./references/diagnostic-workflow.md — 带截图的扩展分步说明。

兄弟技能

  • configure-macro-keyboard — 一旦知道按键发出什么,就用它编写限定到设备的 Karabiner 规则。此处步骤 2 的 vk_none no-op 规则可通过交换 from / to 绑定直接转换为真正的规则。
  • emit-fn-key-on-macos — 如果发现的某个键码应重映射为真正的 Fn(用于 Typeless、听写、地球键),该兄弟技能解释了唯一正确的 Karabiner 咒语。

执行后反思

该技能完成后,在关闭任务前反思:

  • 定位自己。 — 在任何编辑之前确认此 SKILL.md 是规范文件。
  • 什么失败了? — 修复导致失败的指令。
  • 什么比预期更好? — 提升为推荐实践。
  • 什么漂移了? — 如果现实与文档不符,更新厂商 ID、键码或 FOSS 工具版本。
  • 记录它。 — 添加一条 evolution-log 条目(或 04-anti-patterns.md 行),包含触发条件、修复、证据。

不要推迟。下一次调用会继承你留下的一切。