本文特色图像是pixiv的pen助的作品

  1. 圣诞节快到了你最担心的是___?
  2. 我愿意加入罗德岛
  3. 鉴于独立国家联合体成立后形成的局势,我停止自己作为苏联总统职务的活动。作出这一决定是出于原则性考虑
  4. 从孤高的,. 上空坠落而下吧,阿尔忒弥斯!我也会,陪着你起凋零的。
  5. 盖提亚,让我教你最后的魔术吧,所罗门王还有另一个宝具,虽说如此,你是不可能知道其真名的吧,不,是无法知晓其真名。[Ars.Nova]
  6. 自己喜欢的Vtuber圣诞节没直播的话干什么去了这种事要自己察觉啊!
  7. 雪猫社没有更新文章。

引用自萌娘百科,一夜之间就变成40条了太快了。

好的,那么我来更新了。

首先这篇文章不会描述Mac OS鼠标/键盘事件的传递原理。本文要做的是补充一下这篇非常好的文章没有提到的一些问题。

文章中介绍了一个可以在事件的传递链中拦截事件的方法:

[code lang="objectivec" line_number=1]- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
CGEventMask eventMask = CGEventMaskBit(kCGEventRightMouseDown) | CGEventMaskBit(kCGEventRightMouseUp);
CFMachPortRef eventTap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, eventMask, eventCallback, NULL);
CFRunLoopSourceRef runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
CGEventTapEnable(eventTap, true);
CFRelease(eventTap);
CFRelease(runLoopSource);
[/code]

如果你直接复制这段代码,然后尝试运行的话,一般会在CFRunLoopSourceRef()这里遇到EXC_BAD_ACCESS的错误。调试会发现CFRunLoopSourceRef()的第二个参数eventTap的值是0x00,意味着它是一个空值(nil)。

eventTap没被赋值的原因来自于CGEventTapCreate(),根据官方文档,这个函数会在以下条件都不符合时返回空值:

  1. 当前进程以root身份运行。(The current process is running as the root user.)
  2. 辅助设备访问功能在启用状态。(Access for assistive devices is enabled. In OS X v10.4, you can enable this feature using System Preferences, Universal Access panel, Keyboard view.)在Big Sur中,这个权限在“隐私”下的“键盘监听”打开。

与模拟鼠标输入不同,尝试监听键盘输入时不会触发“权限请求”,即不会让用户确认是否允许应用程序获得权限,而是silently fail,给你返回一个空值。

要解决权限问题,自然是要把程序加入白名单中。在XCode中,在工程的product文件夹下,一般能找到你的工程生成的可执行文件。

然而,把这个文件加入白名单并不会让你拥有权限,就很过分。

我第一反应是能不能在XCode启动调试的时候sudo一下,然后找了一圈没有找到类似的东西,连调试配置也没找到。

堆栈溢出上有人推荐直接sudo xcode。不过我不是很推荐这样做。XCode的自动语法纠正有时会对框架的头文件动手。但因为你默认没有这些文件的写权限,事实上不会真的写进去。但更主要的原因是你万一写错代码,那么没有root权限还能稍微挽救一下你。或许你有自信不会写错代码,但是你的自动化编译工具也可能会因为配置不当造成灾难性的后果(今天刚挨一次源代码没了,虽然和root权限没有啥关系)总之我觉得root还是少用为好。

最后我想到的是,调试应用程序的时候权限应该是从XCode上获得吧,给了XCode权限后真的成功了,可喜可贺。

赋予XCode输入监听权限
eventTap有值了!!!