用途
该技能用于诊断一种特定故障:macOS 全局快捷键(如截图工具、启动器、剪贴板管理器)在其他地方都正常,唯独某个应用处于前台、全屏或仅仅在运行时失效。按下快捷键要么毫无反应,要么在该应用内部触发了别的行为。技能的目标是找出快捷键在输入路径的哪一层丢失或被改写,然后只修复那一层。
输入路径模型
技能把输入路径自下而上描述为:
键盘 → DriverKit 重映射器(Karabiner-Elements)→ HID tap 点 → Session tap 点 → 快捷键分发(Carbon RegisterEventHotKey)→ 前台应用
其中重映射器工作在所有 tap 层之下,其规则(例如针对前台应用的 frontmost_application_if 规则,把 Command 改成 Control 以适配远程桌面查看器)会在任何事件 tap、日志器或快捷键看到之前就改变按键组合。
tap-census.swift 还能报告第三个点 AnnotatedSession 上的 tap。窗口服务器会在事件到达该点之前处理部分快捷键,因此该点的 tap 不太可能是元凶,日志器也不监视它。如果四个位置都干净,而普查显示问题应用有一个 ACTIVE 的 AnnotatedSession tap,则应怀疑它。
两个关键事实贯穿整个方法:
- 重映射器工作在 tap 层之下。Karabiner 的规则会在任何事件 tap、日志器或快捷键看到之前改变按键组合。
- 合成测试按键进入点在重映射器之上。CGEventPost 探针可能报告“快捷键触发”,而物理按键仍然失败。绝不能仅凭合成探针下结论,必须用日志器中的真实按键确认。
主要能力(脚本)
脚本位于 scripts/ 目录,用 swift <file> 运行,无需构建步骤。先解析目录:S="$(cc-plugin-root macro-keyboard)/skills/diagnose-dead-global-hotkey/scripts"。
- tap-census.swift:列出每个键盘事件 tap 的归属、HID 或 Session 点、ACTIVE(可吞掉事件)或仅监听状态。只读。
- key-path-logger.swift:在四个位置(HID 头/尾、Session 头/尾)设置仅监听 tap。只记录按住 Cmd、Ctrl 或 Opt 的 key-down:修饰键、键码、前台应用,以及被监视应用是否打开了窗口。
- hotkey-probe.swift:在 HID tap 处发送合成按键组合,报告被监视应用是否响应(FIRED / DID NOT FIRE)。适合测试 tap 和窗口状态,但对重映射器不可见。
日志器需要运行它的终端拥有“输入监控”权限。它从不记录普通打字。
使用方式(工作流)
1. 建立基线与故障状态
- 在问题应用关闭时运行 tap-census.swift。
- 打开问题应用,复现故障状态(窗口化?全屏?聚焦?)。
- 再次运行 tap-census.swift,对比差异:问题应用拥有的新 ACTIVE tap 是头号嫌疑。
- 运行 ioreg -l -w 0 | grep -o '"kCGSSessionSecureInputPID"=[0-9]*' | sort -u,非空表示安全事件输入已开启(对应 pid)。
记录用户所处的确切状态。全屏与窗口化有区别:某些查看器只在全屏时抓取键盘。
2. 观察真实按键
运行 swift "$S/key-path-logger.swift" 600 "<快捷键归属进程名>",让用户先在 Finder 等中性应用中按快捷键,再在问题应用前台时按。根据日志判断:
- 修饰键与按下不符(如按下 Cmd+Shift+X 却记录为 Ctrl+Shift+X),且 front= 问题应用:该应用作用域内的重映射规则改写了组合,需给规则加例外(修复 A)。
- 在 HID 头可见、在后续位置缺失:两者之间的 ACTIVE tap 吞掉了它。用 tap-census.swift 找归属;若是问题应用,用修复 B。
- 到处可见、未改变,但快捷键归属应用没有打开任何窗口:归属应用注册了不同的组合,或注册失败。检查其快捷键设置并重新注册。
- 问题应用前台时到处都看不到,但中性应用可见:安全事件输入。它会把按键事件对其他进程的 tap 和快捷键隐藏。找到安全输入 pid(步骤 1),归属应用必须关闭它(通常是聚焦的密码框)。
- 即使中性应用也到处看不到:在窗口服务器之前就丢失了,可能是硬件、KVM 或共享软件,或丢弃该键的重映射规则。检查 Karabiner-EventViewer 和重映射规则。
3. 修复失败层并用真实按键复验
重新运行日志器,让用户再按一次。当日志在四个位置都显示原始组合,且被监视应用的窗口数增加时,即完成。
修复 A:Karabiner 按应用修饰键重映射
类似“远程桌面查看器前台时 Command → Control”的规则(使 Cmd+C/V 在远程机器上表现为 Ctrl+C/V)也会把快捷键组合变成 Control 组合,被远程机器接收。在该规则的修饰键 manipulator 之前加入例外 manipulator,用 Command 重新发出该组合。技能给出了两段 JSON 示例,分别匹配 command+shift 和 control+shift 两种 from 情况,并建议两个变体都保留,因为无法确定 from.modifiers 看到的是物理 Command 还是已被改写的 Control,保留两者可保证规则在两种情况下都正确。需告知用户的副作用:在该应用中物理按下 Ctrl+Shift+X 现在也会变成该快捷键。Karabiner 保存 karabiner.json 后会自动重载;可通过日志确认。若文件由 dotfile 管理器(如 chezmoi)管理,也要更新其源文件。
为什么“先点另一个应用”没用:浮动、非激活窗口(时钟、HUD、调色板)永远不会成为前台应用,因此 frontmost_application_if 仍然匹配查看器。应看日志器中的 front=,而不是用户点了什么。
修复 B:应用的键盘抓取
远程桌面查看器、虚拟机和游戏常安装 ACTIVE 的 Session 级 CGEventTap,接管每个按键事件并重新发给自己,使系统快捷键到达远程端。快捷键分发永远看不到该键。需寻找禁用抓取的选项。技能给出的例子:TigerVNC viewer 在全屏且 FullscreenSystemKeys 开启(默认)或手动抓取开关时触发抓取,可通过命令行 -FullscreenSystemKeys=0 或查看器配置文件中的 FullscreenSystemKeys=0 禁用。用 tap-census.swift 验证故障状态下该应用的 ACTIVE tap 消失,并用 hotkey-probe.swift 验证组合触发(tap 在重映射器之上,因此探针对此修复有效)。代价:Cmd+Tab 等系统快捷键将作用于 Mac 而非远程机器。
适用场景
- 全局快捷键只在某个远程桌面查看器、虚拟机或游戏前台/全屏时失效。
- 需要区分是重映射器改写、事件 tap 吞掉、安全事件输入,还是应用键盘抓取导致的问题。
- 需要在修复后验证真实按键在四个位置都保持原始组合。
限制与注意事项
- 日志器需要终端拥有“输入监控”权限。
- 只记录带 Cmd、Ctrl 或 Opt 的按键,不记录普通打字。
- 合成探针无法证明重映射器相关问题,必须用真实按键确认。
- 一个修复可能不够:查看器可能同时命中两个原因(重映射规则在任何窗口状态、键盘抓取仅全屏),需在用户使用的每种状态下重新测试。
- 探针的组合在快捷键未触发时会去往某处:前台应用,或经查看器到远程机器。应在前面放一个无害应用再运行。
- 技能为自演进型:若说明有误、API 或应用选项变化、或需要变通,应立即修正文件和脚本,但仅针对真实、可复现的问题更新。
执行后反思
技能完成后检查:诊断是否匹配表格某一行,若日志出现未列出的模式则补充该行及修复;应用选项或 API 是否变化,若是则更新表格或脚本;是否需要变通,若是则并入工作流或注意事项。仅在问题真实且可复现时更新,不做推测性修改。