关于 emit-fn-key-on-macos
用于在 macOS 上配置按键映射、通过 Karabiner-Elements 的 DriverKit VirtualHIDDevice 发出真实 Fn 事件的技能。面向希望将外部按键绑定到 Fn 操作的用户,说明所需规则、验证步骤、前置条件及失效方案。
主要功能
- 01配置 Karabiner-Elements 发出 Fn
- 02使用 Apple Top Case 键码
- 03在 Karabiner-EventViewer 检查事件
- 04检查权限和 Typeless 设置
- 05规避已记录的 Fn 映射故障
适用场景
- 01将外部按键映射为 Typeless 按键说话
- 02触发 macOS 听写或表情选择器
- 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 组合键
- Typeless 按住说话(默认值为
- 用户反馈“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 没有触发:
- 检查 Typeless 是否拥有辅助功能权限
- 确认 Typeless
app-settings.json中的pushToTalk已设置为"Fn"(不是"fn",也不是"Function") - 确认没有其他工具也在捕获 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/references/03-patterns.md— “Apple vendor Fn encoding”模式及其完整规则摘录../configure-macro-keyboard/references/04-anti-patterns.md— BTTCGEventPost失败、Fn 点按与长按失败、在 Jieli 上使用 QMK/VIA 失败./references/failed-approaches.md— 此特定任务的失败方案简明目录
相关技能
configure-macro-keyboard— 端到端设置工作流,其中会将此技能作为一个步骤(在更大的 Karabiner 规则中,将外接宏键盘的按键连接到 Fn)diagnose-hid-keycodes— 如果你还不知道硬件会发出哪个键码,请先使用此技能;之后再回到这里将其重新映射为 Fn
执行后反思
此技能完成后,请在结束任务前进行反思:
- 确认位置。 — 在进行任何编辑前,确认此 SKILL.md 是规范文件。
- 哪里失败了? — 修复导致失败的说明。
- 哪些做得比预期更好? — 将其提升为推荐实践。
- 有哪些内容已过时? — 如果实际情况与文档不符,请更新供应商 ID、键码或 FOSS 工具版本。
- 记录下来。 — 添加一条演进日志记录(或在
04-anti-patterns.md中添加一行),包含触发条件、修复方法和证据。
请勿拖延。下一次调用会继承你留下的一切。