在某些应用程序中,应用程序直接处理键盘快捷键是有意义的,否则键盘快捷键将绑定到系统范围的组合。例如,⌘-Space(通常是Spotlight)或⌘-Tab(通常是app切换器)。这适用于各种Mac应用程序,例如VMWare Fusion,Apple自己的屏幕共享和远程桌面客户端(分别将事件转发到VM或服务器,而不是在本地处理它们),以及应用程序中的一些类似的第三方应用程序存储。
我们希望在我们正在开发的应用程序中实现这样的模式,但是很难确定如何做到这一点。我应该指出,有问题的应用程序是一个常规的前台应用程序,是沙箱,任何解决方案都必须符合App Store规则。事实上,商店中的其他应用程序可以做到这一点意味着必须这样做。
要明确,我们希望:
Apple的Event Architecture document表明前台应用程序应该已经接收了这些事件。 (它只涉及处理电源和弹出按钮等事情的早期级别,这很好。)它继续建议,而NSApplication
sendEvent:
NSApplication
方法是key events document also implies什么检测基于修饰符标记的潜在快捷方式,将它们分派到窗口,如果失败,则转到菜单栏。它没有明确说明全局绑定快捷方式会发生什么。
我尝试了对sendEvent:
进行子类化并覆盖NSevent
。无论我是否将所有事件都传递给超类实现,或者如果我说过滤器修改键事件,当我按⌘-Space时,我会收到按下并释放命令(⌘)键的事件,但不会显示空格键。 Spotlight UI总是会弹出。
我没有从Apple或其他方面找到有关子类化NSApplication及其早期事件处理的更多信息。我似乎无法找出检测和处理全局快捷方式的级别。
有人可以指出我正确的方向吗?
无效的可能解决方案:
建议我在其他Stack Overflow帖子中看到但不适用于我见过的其他应用程序(这会破坏App Store规则):
无论如何,这些都是过度的,因为它们允许您在所有时间拦截所有事件,而不仅仅是当您的应用是前台应用时。
addGlobalMonitorForEventsMatchingMask:handler:
的{{1}}同时不会阻止全局快捷方式处理程序为这些事件触发,所以我甚至都不打算尝试它。
答案 0 :(得分:1)
好的,所以Cocoa事件方法和Quartz事件抽头都没有了,因为它们需要root或accessibility访问权限,或者在dock之前没有捕获事件。
Carbon PushSymbolicHotKeyMode
已经出局,因为根据文档,它需要访问权限。
Carbon RegisterEventHotKey
可能已经出局,因为Apple似乎不允许这样做(请参阅我在评论中的链接)。但是,即便如此,我测试过,你无法使用它来捕获Command + Tab。
我快速证明了这个 的工作原理,但YMMV:
KeyboardWatcher
示例类。您需要链接IOKit。Handle_DeviceEventCallback
将为您提供按下的键。显然,您可以根据需要对其进行修改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用户,或者为您的应用启用辅助功能访问权限。
似乎无法使用当前沙盒规则的事件点击。这已在developer forum中得到确认。该链接仅限登录,但引用该主题:
是否有机会通过阻止启动iTunes来处理来自媒体键的事件。在沙盒之前,可以通过创建CGEventTap但现在使用hid-controll进行沙箱拒绝。
不,目前在App Sandbox中无法实现。
我不确定另一种方法可以做到这一点;我有兴趣知道App Store中的哪些应用可以?
VMWare Fusion显然不是沙盒,Apple自己的应用程序不受规则约束。请记住,沙盒仅在2012年引入后添加的新应用程序上强制执行。在该日期之前添加的应用程序没有强制执行沙盒。请参阅此answer。
答案 1 :(得分:1)
对于正在为全屏应用程序寻找解决方案的其他人,或者如果您愿意接管全屏应用程序,则可以使用:CGDisplayCapture
这将导致您的应用捕获所有键盘输入,甚至无法使用键盘调用Spotlight和应用切换。
import Quartz
// disable keyboard events for all apps, except yours
CGDisplayCapture(CGMainDisplayID())
// reenable keyboard events for other apps
CGDisplayRelease(CGMainDisplayID())
注意:在释放显示器之前,该应用程序将不会收到窗口/应用程序活动/退出事件。因此,也许可以在应用程序处于活动状态时使用鼠标跟踪来释放显示。此外,即使屏保/锁定屏幕也将受到影响。确保根据需要停用捕获。
参考:
答案 2 :(得分:0)
我很久以前解决了这个问题,但是我只是注意到我从未在这里发表过。答案最终涉及CGSSetGlobalHotKeyOperatingMode()
。这不是公共API,但是许多Mac App Store应用程序通过混淆函数名称并动态查找来使用它。苹果似乎并不介意。该API非常易于使用,并且有很多开放的示例源代码在浮动。