macOS下编程关闭Caps Lock及按键映射异常问题咨询
解决Caps Lock映射为Return时的大小写触发问题
这个问题我之前折腾过好一阵!核心矛盾在于你只修改了按键的keyCode,但Caps Lock自带的系统级状态切换逻辑依然在后台运行——当你按下Caps Lock时,系统会触发flagsChanged事件来切换大小写状态,这就导致哪怕你把它改成了Return,后续输入还是会变成大写。而且你之前尝试复制事件发送的方式,会让回调被反复触发,形成死循环,这是因为新发送的事件又会被你的事件tap捕获处理。
从根源解决的方案:阻止Caps Lock的状态切换事件
我们需要做两件事:
- 把Caps Lock的按键事件替换成Return;
- 拦截掉触发Caps Lock状态切换的
flagsChanged事件,不让系统处理它。
下面是修改后的完整代码,每一步都加了注释:
import CoreGraphics // 监听按键按下、抬起和标志位变化事件 let eventMask = (1 << CGEventType.keyDown.rawValue) | (1 << CGEventType.keyUp.rawValue) | (1 << CGEventType.flagsChanged.rawValue) // 创建事件tap,这里用guard处理创建失败的情况更严谨 guard let eventTap = CGEvent.tapCreate(tap: .cghidEventTap, place: .headInsertEventTap, options: .defaultTap, eventsOfInterest: CGEventMask(eventMask), callback: myCGEventCallback, userInfo: nil) else { print("failed to create eventTap - 请检查是否已启用辅助功能权限") exit(1) } // 事件回调函数 func myCGEventCallback(proxy: CGEventTapProxy, type: CGEventType, event: CGEvent, refcon: UnsafeMutableRawPointer?) -> Unmanaged<CGEvent>? { let keyCode = event.getIntegerValueField(.keyboardEventKeycode) // 匹配Caps Lock的keyCode(0x39) if keyCode == 0x39 { switch type { case .keyDown, .keyUp: // 1. 将当前按键替换为Return(keyCode 0x24) event.setIntegerValueField(.keyboardEventKeycode, value: 0x24) // 2. 清除Caps Lock的标志位,确保状态不会残留 var currentFlags = event.flags currentFlags.remove(.maskCapsLock) event.flags = currentFlags // 返回修改后的事件,让系统继续处理成Return按键 return Unmanaged.passRetained(event) case .flagsChanged: // 拦截Caps Lock的状态切换事件:返回nil表示不把这个事件传递给系统 // 这样系统就不会切换Caps Lock的大小写状态了 return nil default: // 其他事件类型正常传递 return Unmanaged.passRetained(event) } } // 非Caps Lock事件直接返回,正常处理 return Unmanaged.passRetained(event) } // 别忘了启动事件循环(示例代码) let runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0) CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, .commonModes) CGEventTapEnable(eventTap, true) CFRunLoopRun()
关键细节说明
- 拦截
flagsChanged事件:这是阻止Caps Lock开启的核心——当你按下Caps Lock时,系统会发送这个事件来切换大小写状态,我们直接返回nil,不让系统处理这个事件,从根源上避免了状态切换。 - 清除标志位:在替换按键时,手动移除Caps Lock的标志位,防止系统因为残留的标志位误判状态。
- 避免循环:不需要复制事件重新发送,直接修改当前事件并控制是否传递给系统,这样就不会触发回调的循环调用了。
注意事项
- 运行这个程序需要启用辅助功能权限:在系统设置的「隐私与安全性」→「辅助功能」里找到你的应用,勾选允许。如果权限没开,
eventTap会创建失败。
内容的提问来源于stack exchange,提问作者Abdulrhman Alrifai
相关产品推荐
相关产品推荐

