You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

macOS下编程关闭Caps Lock及按键映射异常问题咨询

解决Caps Lock映射为Return时的大小写触发问题

这个问题我之前折腾过好一阵!核心矛盾在于你只修改了按键的keyCode,但Caps Lock自带的系统级状态切换逻辑依然在后台运行——当你按下Caps Lock时,系统会触发flagsChanged事件来切换大小写状态,这就导致哪怕你把它改成了Return,后续输入还是会变成大写。而且你之前尝试复制事件发送的方式,会让回调被反复触发,形成死循环,这是因为新发送的事件又会被你的事件tap捕获处理。

从根源解决的方案:阻止Caps Lock的状态切换事件

我们需要做两件事:

  1. 把Caps Lock的按键事件替换成Return;
  2. 拦截掉触发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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 09:29:40