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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 18:56:04