mcpField
E
emit-fn-key-on-macos

emit-fn-key-on-macos

github.com/terrylica生产力145 次安装今天更新
代码仓库

关于 emit-fn-key-on-macos

用于在 macOS 上配置按键映射、通过 Karabiner-Elements 的 DriverKit VirtualHIDDevice 发出真实 Fn 事件的技能。面向希望将外部按键绑定到 Fn 操作的用户,说明所需规则、验证步骤、前置条件及失效方案。

主要功能

  1. 01配置 Karabiner-Elements 发出 Fn
  2. 02使用 Apple Top Case 键码
  3. 03在 Karabiner-EventViewer 检查事件
  4. 04检查权限和 Typeless 设置
  5. 05规避已记录的 Fn 映射故障

适用场景

  1. 01将外部按键映射为 Typeless 按键说话
  2. 02触发 macOS 听写或表情选择器
  3. 03排查无法触发应用的 Fn 映射

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/terrylica/cc-skills --skill emit-fn-key-on-macos

安装后在 Claude Code 中输入/emit-fn-key-on-macos确认已加载。

附带文件CLAUDE.mdreferences/failed-approaches.md

详细介绍

在 macOS 上发出真实的 Fn 键

自我演进技能:此技能会在使用过程中不断改进。如果说明有误、参数发生偏移,或需要采用变通方案,请立即修复此文件,不要拖延。仅针对真实且可复现的问题进行更新。

Macos 的 Fn 键(现代 MacBook 上也称为“地球”键)很特殊。它不是普通修饰键——它带有 kCGEventFlagMaskSecondaryFn,macOS 的输入子系统会丢弃通过 CGEventPost 从用户空间设置的此标志。许多“任意按键重映射”工具都假设 Fn 的行为与 Cmd / Option / Ctrl 相同,并试图通过 CGEventPost 发出 Fn——结果是听写、Typeless、macOS 听写快捷键、通过地球键触发的 Spotlight、表情符号选择器等功能都不会响应,且不会显示错误。

唯一能发出内核接受的 Fn 的用户空间途径是 DriverKit VirtualHIDDevice。Karabiner-Elements 内置了此设备。这就是为什么在 2026 年,Karabiner 是这项用途唯一的开源工具。

何时使用此技能

  • 用户想让宏键盘按钮、鼠标按钮或任何外接按键触发:
    • Typeless 按住说话(默认值为 pushToTalk: "Fn")
    • macOS 听写(双击 Fn Fn,或在系统设置中设置为单按 Fn)
    • 表情符号选择器(Fn+E)
    • 通过地球键触发的 Spotlight(Fn)
    • 使用 BTT 重映射后无法正常工作的特定应用 Fn 组合键
  • 用户反馈“BTT 显示快捷键已触发,但 Typeless 没有反应”
  • 用户尝试使用 hidutil property --set 设置 Fn 映射,但没有效果
  • 用户在键盘上刷入 QMK/ZMK 后,Fn 键无法正常发出

前置条件检查

# 1. Karabiner-Elements installed (the only userland tool that can emit real Fn)?
test -d /Applications/Karabiner-Elements.app && echo OK || brew install --cask karabiner-elements

# 2. Karabiner has Input Monitoring + Accessibility?
#    System Settings → Privacy & Security → Input Monitoring → Karabiner = ON
#    System Settings → Privacy & Security → Accessibility → Karabiner = ON

# 3. On macOS Sequoia+: privileged daemon approved in Login Items?
#    System Settings → General → Login Items → Allow in the Background
#    → Karabiner-Elements Privileged Daemon = ON
#    Without this, Karabiner's DriverKit VirtualHIDDevice won't load and
#    your Fn remap will silently do nothing.

如果在内置键盘上按 Fn 时功能正常,但通过重映射触发时不正常,说明 DriverKit 守护进程已加载,但重映射规则有误——请跳到下方的“唯一的神奇调用”。如果内置键盘上的 Fn 也失效,请重新检查前置条件 3。

核心事实

┌───────────────────────────┐          ┌───────────────────────────┐
│  Real Fn (from keyboard)  │          │  Fn via CGEventPost(...)  │
│  Hardware → DriverKit →   │          │  Userland app → Quartz →  │
│  HIDEvent with            │          │  kCGEventFlagMaskSecon-   │
│  NX_DEVICE_CAPABILITY_    │          │  daryFn flag is DROPPED   │
│  INPUTKEYBOARD_FUNCTION   │          │  before reaching input    │
│  flag set                 │          │  subsystem.               │
│                           │          │                           │
│  ✅ Typeless / dictation  │          │  ❌ Typeless / dictation  │
│     accept it.            │          │     never fire.           │
└───────────────────────────┘          └───────────────────────────┘

Karabiner-Elements 会注册一个 DriverKit VirtualHIDDevice。当重映射规则指示“发出 Fn”时,它会通过与硬件键盘相同的内核途径发出该按键。NX_DEVICE_CAPABILITY_INPUTKEYBOARD_FUNCTION 标志会保留,因此 Typeless/听写会接受此按键。

唯一的神奇调用

在 Karabiner 操作器的 to 块中:

{ "apple_vendor_top_case_key_code": "keyboard_fn" }

不要使用:

  • ❌ {"key_code": "fn"}——不存在这样的 key_code,会被静默忽略
  • ❌ {"modifiers": ["fn"]}——只有修饰键标志,没有按键按下事件,因此不会触发 Fn 语义
  • ❌ {"consumer_key_code": "..."}——HID 用途页不正确
  • ❌ {"key_code": "function"}——这不是 Karabiner 关键字

apple_vendor_top_case_key_code 命名空间是 Karabiner 对 HID 用途页 0x00FF(Apple Top Case)和用途 0x03(键盘 Fn)的映射。这正是 Apple 自家内置键盘使用的描述符。

最简可用示例

将 Caps Lock 键(仅限特定外接键盘——请调整 VID/PID)重映射为发出真实的 Fn:

{
  "description": "Caps Lock → Fn (on external keyboard)",
  "manipulators": [
    {
      "type": "basic",
      "from": { "key_code": "caps_lock" },
      "to": [{ "apple_vendor_top_case_key_code": "keyboard_fn" }],
      "conditions": [
        {
          "type": "device_if",
          "identifiers": [{ "vendor_id": 19530, "product_id": 16725 }]
        }
      ]
    }
  ]
}

将其添加到 ~/.config/karabiner/karabiner.json → 配置文件 0 → complex_modifications.rules。

验证

# Open Karabiner-EventViewer
open -a "Karabiner-EventViewer"

# Press your remapped key; the Main tab should show:
# name:  fn (not apple_vendor_top_case_key_code)
# ↑ Karabiner normalizes the display back to "fn" but under the hood it's emitting
#   the Apple-Top-Case variant that carries the kernel flag.

# Then test the real consumer (e.g., Typeless with pushToTalk: "Fn"):
#   Press and hold → mic opens
#   Release → mic closes, transcript appears

如果 EventViewer 显示了正确事件,但 Typeless 没有触发:

  1. 检查 Typeless 是否拥有辅助功能权限
  2. 确认 Typeless app-settings.json 中的 pushToTalk 已设置为 "Fn"(不是 "fn",也不是 "Function")
  3. 确认没有其他工具也在捕获 Fn——设置了 Fn 触发器的 BTT 会抢先拦截事件,导致 Typeless 收不到

按下与按住:不要将 Fn 设置为按住时触发的目标

尝试过的模式:“轻按顶部按钮 → Return,按住顶部按钮 → Fn”,使用 Karabiner 的 to_if_alone + to_if_held_down。

这会导致 Fn 在整个系统范围内失效。已验证的故障发生于 2026-04-21:加载包含 to_if_held_down: [{"apple_vendor_top_case_key_code": "keyboard_fn"}] 的规则后,即使直接按内置键盘上的 Fn,macOS 原生 Fn 键也无法再发出 Fn。必须完整回滚。

原因仍不明确——可能是 Karabiner 的按住状态机以某种方式保持 Fn 标志,与真实硬件 Fn 事件发生冲突。在 Karabiner 上游修复此问题之前,请为 Fn 使用单独的按钮。

BTT 失败的原因(一句话版)

BTT 的“Trigger Key Sequence”和“Send Shortcut”操作会通过 CGEventCreateKeyboardEvent + CGEventSetFlags 调用 CGEventPost。kCGEventFlagMaskSecondaryFn 标志会被 CGEventSetFlags 接受(不会报错),但会在传递前被输入事件管理器丢弃。Apple 从未记录过这一限制;在 macOS 13 / 14 / 15 上都一致如此。

hidutil 失败的原因

hidutil property --set '{"UserKeyMapping":[...]}' 在 HID 报告层运行,可以交换键码,但无法合成 Fn 标志。该标志由键盘驱动程序(DriverKit 或 Apple 的内部键盘驱动程序)根据按下的按键设置——你无法在 HID 报告层伪造它,因为它是按事件计算的属性,而非持久化属性。

为什么可刷写的板子上的 QMK 能用(而 Jieli 类设备不行)

带有 NKRO + Apple Fn keycode 的 QMK 板,如果使用实际的 USB HID 报告描述符并声明 Usage Page 0x00FF、Usage 0x03,就能原生发送 Fn。内核会接受它,因为这是真正的硬件报告。

但廉价的 Jieli/Realtek/CH57x 键盘垫无法刷写——它们的固件是预先烧录的,并且会暴露固定的 HID 描述符(通常是标准键盘 Usage Page 0x07)。无论怎样用 QMK/VIA/Vial 重新刷写都无济于事,因为引导加载程序不会接受新固件。

可刷写的板子请参阅:https://github.com/qmk/qmk_firmware → APPLE_FN_ENABLE。

深入参考

相关技能

  • configure-macro-keyboard — 端到端设置工作流,其中会将此技能作为一个步骤(在更大的 Karabiner 规则中,将外接宏键盘的按键连接到 Fn)
  • diagnose-hid-keycodes — 如果你还不知道硬件会发出哪个键码,请先使用此技能;之后再回到这里将其重新映射为 Fn

执行后反思

此技能完成后,请在结束任务前进行反思:

  1. 确认位置。 — 在进行任何编辑前,确认此 SKILL.md 是规范文件。
  2. 哪里失败了? — 修复导致失败的说明。
  3. 哪些做得比预期更好? — 将其提升为推荐实践。
  4. 有哪些内容已过时? — 如果实际情况与文档不符,请更新供应商 ID、键码或 FOSS 工具版本。
  5. 记录下来。 — 添加一条演进日志记录(或在 04-anti-patterns.md 中添加一行),包含触发条件、修复方法和证据。

请勿拖延。下一次调用会继承你留下的一切。

评论

支持 Markdown