macOS 应用程序:处理绑定到全局键盘快捷键的组合键

2024-01-02

在某些应用程序中,应用程序直接处理键盘快捷键是有意义的,否则这些快捷键将绑定到系统范围的组合。例如,⌘-Space(通常是 Spotlight)或 ⌘-Tab(通常是应用程序切换器)。这适用于各种 Mac 应用程序,例如 VMWare Fusion、Apple 自己的屏幕共享和远程桌面客户端(分别将事件转发到 VM 或服务器,而不是在本地处理它们),以及应用程序中的一些类似的第三方应用程序店铺。

我们希望在我们正在开发的应用程序中实现这样的模式,但很难弄清楚如何做到这一点。我应该指出,有问题的应用程序是常规的前台应用程序,是沙盒的,任何解决方案都必须遵守应用程序商店规则。商店中的其他应用程序可以做到这一点这一事实意味着这一定是可能的。

需要明确的是,我们想要:

  • 检测并处理所有按键,包括那些绑定到全局快捷键的按键。
  • 防止全局快捷方式触发其全局绑定效果。

Apple's 事件架构文档 https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/EventOverview/EventArchitecture/EventArchitecture.html#//apple_ref/doc/uid/10000060i-CH3-SW11表明前台应用程序应该已经接收到这些事件。 (它只讨论早期级别处理诸如电源和弹出按钮之类的事情,这很好。)它继续建议,并且关键事件文件还暗示 https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/EventOverview/HandlingKeyEvents/HandlingKeyEvents.html#//apple_ref/doc/uid/10000060i-CH7-SW11 that NSApplication's sendEvent:方法是根据修饰符标志检测潜在的快捷方式,将它们分派到窗口,如果失败,则分派到菜单栏。没有明确说明全局绑定的快捷方式会发生什么。

我尝试过子类化NSApplication并压倒一切sendEvent:。无论我是否将所有事件传递给超类实现,或者如果我说过滤修饰键事件,当我按 ⌘-Space 时,我都会收到按下和释放命令 (⌘) 键的事件,但不会收到空格键的事件。 Spotlight UI 始终会弹出。

我还没有从 Apple 或其他地方找到太多有关 NSApplication 子类及其早期事件处理的信息。我似乎无法找出在什么级别检测和处理全局快捷方式。

有人可以指出我正确的方向吗?

可能无效的解决方案:

我在其他 Stack Overflow 帖子中看到的建议,但不适用于我见过的执行此操作的其他应用程序(并且会违反 App Store 规则):

  • 辅助功能 API(需要特殊许可)
  • 事件点击/挂钩(需要以 root 身份运行)

无论如何,这两种方法都太过分了,因为它们可以让你拦截all活动于all次,而不仅仅是当您的应用程序是前台应用程序时。

NSevent's addGlobalMonitorForEventsMatchingMask:handler:同时并不能阻止全局快捷方式处理程序为这些事件触发,所以我什至懒得尝试它。


好吧,Cocoa 事件方法和 Quartz 事件点击已经过时了,因为它们要么需要 root 访问权限,要么需要可访问权限,或者不会在 Dock 之前捕获事件。

碳的PushSymbolicHotKeyMode已退出,因为根据文档,它需要可访问性访问。

碳的RegisterEventHotKey可能是因为苹果似乎不允许它(请参阅我对问题的评论中的链接)。然而,即便如此,我测试过,你不能用它来捕获 Command+Tab。

我快速证明了这一点can工作,但是 YMMV:

  • 实施KeyboardWatcher来自此的示例类answer https://stackoverflow.com/a/39834889/233944。您需要链接 IOKit。
  • 添加硬件 - USB (com.apple.security.device.usb) 沙箱权利。这是必要的,因为 KeyboardWatcher 使用 HID 来捕获按键操作
  • the Handle_DeviceEventCallback会给你按下的键。显然你可以根据你的需要修改它
  • Use SetSystemUIMode阻止任务切换器和 Spotlight。您需要链接 Carbon。

SetSystemUIMode(kUIModeContentSuppressed, kUIOptionDisableProcessSwitch);

请注意,这仅在您的应用程序位于前台时有效(可能是您想要的)。我使用跟踪矩形在我的视图上设置了此选项,因此只有当鼠标位于我的视图上时它才会生效(就像在 Remotix 中一样):

- (void)viewDidLoad {
[super viewDidLoad];

NSTrackingArea* trackingArea = [[NSTrackingArea alloc] initWithRect:[self.view bounds] options: (NSTrackingMouseEnteredAndExited | 

NSTrackingActiveAlways) owner:self userInfo:nil];
    [self.view addTrackingArea:trackingArea];
}

- (void) mouseEntered:(NSEvent*)theEvent {
    SetSystemUIMode(kUIModeContentSuppressed, kUIOptionDisableProcessSwitch);
}

- (void) mouseExited:(NSEvent*)theEvent {
    SetSystemUIMode(kUIModeNormal, 0);
}

Remotix 似乎链接了 Carbon 和 IOKit,但我看不到它们是否有 USB 授权(我尝试了演示,而不是 App Store 版本)。他们有可能正在做这样的事情。


实现此目的的正常方法是安装 Quartz 事件分接头。但是,要接收针对其他应用程序的事件,您需要(如您所说)成为 root 用户,或者为您的应用程序启用辅助功能访问权限。

在当前的沙箱规则中似乎不可能使用事件点击。这在开发者论坛 https://devforums.apple.com/message/742845#742845。该链接仅供登录,但引用线程:

是否有机会通过阻止启动 iTunes 来处理来自媒体键的事件。在沙箱之前,可以通过创建 CGEvent Tap 来实现,但不能使用 hid-controll 来拒绝沙箱。

不,目前在应用程序沙箱中不可能实现这一点。

我不确定还有其他方法可以做到这一点;我有兴趣知道 App Store 中的哪些应用程序可以?

VMWare Fusion 显然没有沙盒化,苹果自己的应用程序不受这些规则的约束。请记住,沙盒仅对 2012 年推出后添加的新应用程序强制执行。该日期之前添加的应用程序不会强制执行沙盒。看到这个answer https://stackoverflow.com/a/32248238/233944.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

macOS 应用程序:处理绑定到全局键盘快捷键的组合键 的相关文章

随机推荐