macOS 15 Sequoia下模拟fn-E按键的CGEvent失效问题求助
macOS 15 Sequoia中模拟fn-E触发Emoji选择器失效问题
我开发了一款Mac菜单栏应用,希望通过菜单项控制前台应用显示/隐藏Emoji选择器(字符面板)。此前采用向前台应用发送模拟按键事件触发系统快捷键fn-E(Globe-E)的方案,代码如下:
pid_t psn = NSWorkspace.sharedWorkspace.frontmostApplication.processIdentifier; CGEventRef keyup, keydown; CGEventFlags flags = kCGEventFlagMaskSecondaryFn; keydown = CGEventCreateKeyboardEvent (NULL, (CGKeyCode)kVK_ANSI_E, true); CGEventSetFlags(keydown, flags); keyup = CGEventCreateKeyboardEvent (NULL, (CGKeyCode)kVK_ANSI_E, false); CGEventSetFlags(keyup, flags); CGEventPostToPid(psn, keydown); CGEventPostToPid(psn, keyup); CFRelease(keydown); CFRelease(keyup);
但这段代码在macOS 15 Sequoia中无报错却完全失效。我尝试过为事件添加时间戳:
CGEventSetTimestamp(keydown, clock_gettime_nsec_np(CLOCK_UPTIME_RAW)); CGEventSetTimestamp(keyup, clock_gettime_nsec_np(CLOCK_UPTIME_RAW));
还试过创建带时间戳和windowId的NSEvent按键事件,同样无效。想知道是时间戳设置有误,还是存在其他遗漏?
问题分析与解决方案
权限限制升级
macOS 15 Sequoia大幅收紧了跨进程输入模拟的权限校验,哪怕旧系统里已经授权,升级后也可能需要重新确认:
- 打开系统设置 > 隐私与安全性 > 辅助功能,确保你的应用在列表中并已勾选
- 同样在输入监控选项中开启应用权限
缺少这两个权限的话,所有模拟输入都会被系统静默拦截,不会报错但完全无效。
Globe键模拟逻辑错误
之前用kCGEventFlagMaskSecondaryFn作为Fn掩码的方式在Sequoia里不再适用,Globe键有独立的键码kVK_Globe,正确的模拟顺序应该是先按Globe,再按E,最后松开两者:
pid_t psn = NSWorkspace.sharedWorkspace.frontmostApplication.processIdentifier; // 按下Globe键 CGEventRef globeDown = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)kVK_Globe, true); CGEventPostToPid(psn, globeDown); // 按下并松开E键 CGEventRef eDown = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)kVK_ANSI_E, true); CGEventRef eUp = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)kVK_ANSI_E, false); CGEventPostToPid(psn, eDown); CGEventPostToPid(psn, eUp); // 松开Globe键 CGEventRef globeUp = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)kVK_Globe, false); CGEventPostToPid(psn, globeUp); // 释放资源 CFRelease(globeDown); CFRelease(eDown); CFRelease(eUp); CFRelease(globeUp);
时间戳设置问题
如果要设置时间戳,别用CLOCK_UPTIME_RAW,系统期望的是基于CFAbsoluteTime的纳秒值,而且keyup的时间戳要比keydown稍晚,模拟真实按键的间隔:
uint64_t baseTimestamp = (uint64_t)(CFAbsoluteTimeGetCurrent() * 1000000000ULL); CGEventSetTimestamp(keydown, baseTimestamp); CGEventSetTimestamp(keyup, baseTimestamp + 10000000); // 10ms间隔
替代方案:直接调用AppleScript
要是模拟按键还是不行,换用AppleScript触发更稳定,同样需要辅助功能权限:
NSAppleScript *emojiScript = [[NSAppleScript alloc] initWithSource:@"tell application \"System Events\" to keystroke \"e\" using {globe down}"]; NSError *error = nil; [emojiScript executeAndReturnError:&error]; if (error) { // 处理权限不足等错误 }
内容的提问来源于stack exchange,提问作者Adam
相关产品推荐
相关产品推荐

