OSX:检测系统级keyDown事件?

我正在为Mac OSX的打字导师应用程序工作,即使应用程序没有焦点,也需要将击键转发给它。

有没有办法通过NSDistributedNotificationCenter让系统向应用程序发送按键? 我傻傻地search了自己,还没有find答案…

编辑: 下面的示例代码

感谢@ NSGod指向我正确的方向 – 我最终使用方法addGlobalMonitorForEventsMatchingMask:处理程序:,它的工作很好,添加一个全局事件监视器 。 为了完整性,我的实现如下所示:

// register for keys throughout the device... [NSEvent addGlobalMonitorForEventsMatchingMask:NSKeyDownMask handler:^(NSEvent *event){ NSString *chars = [[event characters] lowercaseString]; unichar character = [chars characterAtIndex:0]; NSLog(@"keydown globally! Which key? This key: %c", character); }]; 

对我来说,棘手的部分是使用块,所以我会给一些描述,以防止任何人的帮助:

关于上面的代码的事情是,这是NSEvent上的所有方法调用 。 该块作为参数直接提供函数。 你可以想象它像一个内联的委托方法。 只是因为这一段时间才沉迷于我,我将在这里一步一步地努力:

 [NSEvent addGlobalMonitorForEventsMatchingMask:NSKeyDownMask 

这一点是没有问题的。 你正在调用NSEvent的类方法,并告诉它你正在监视哪个事件,在这种情况下NSKeyDownMask。 支持的事件types的掩码列表可以在这里find。

现在,我们来到棘手的部分:处理程序,它期望一个块:

 handler:^(NSEvent *event){ 

这花了我一些编译错误,以得到这个权利,但(谢谢你苹果)他们是非常build设性的错误信息。 首先要注意的是克拉^。 这标志着该块的开始。 之后,在括号内,

 NSEvent *event 

其中声明了您将在块中使用的variables来捕获事件。 你可以打电话

 NSEvent *someCustomNameForAnEvent 

没关系,你只需要在块中使用这个名字。 那么,这就是所有这一切。 确保closures大括号,然后括号完成方法调用:

 }]; 

你完成了! 这真的是一种“一线”。 在应用程序中执行此调用的位置并不重要 – 我在AppDelegate的applicationDidFinishLaunching方法中执行此操作。 然后,在该块内,您可以从您的应用程序中调用其他方法。

如果您可以满足OS X 10.6+的最低要求,并且可以对事件stream进行“只读”访问,则可以在Cocoa: Cocoa事件处理指南:监控事件中安装全局事件监视器。

如果您需要支持OS X 10.5及更早版本,并且只读访问没问题,并且不介意使用Carbon事件pipe理器,则基本上可以使用GetEventMonitorTarget()来执行碳等价物。 (虽然你会很难find任何(官方)文档)。 我相信这个API在OS X 10.3中首次可用。

如果您需要对事件stream进行读写访问,则需要查看属于ApplicationServices> CoreGraphics: CGEventTapCreate()和朋友的稍低级别的API。 这在10.4是第一个可用的。

请注意,所有3种方法都要求用户在“系统预置”>“通用访问”首选项窗格中启用“启用对辅助设备的访问”(至less对于键事件)。

我发布了适用于我的案例的代码。

我在应用程序启动后添加全局事件处理程序。 我的快捷键使ctrl + alt + cmd + T打开我的应用程序。

 - (void) applicationWillFinishLaunching:(NSNotification *)aNotification { // Register global key handler, passing a block as a callback function [NSEvent addGlobalMonitorForEventsMatchingMask:NSKeyDownMask handler:^(NSEvent *event){ // Activate app when pressing cmd+ctrl+alt+T if([event modifierFlags] == 1835305 && [[event charactersIgnoringModifiers] compare:@"t"] == 0) { [NSApp activateIgnoringOtherApps:YES]; } }]; } 

我发现这个问题是,任何其他应用程序全球注册的密钥将不会是…或者至less在我的情况下,也许我做错了什么。

如果你的程序需要显示所有的键,例如“Command-Shift-3”,那么它将不会看到显示它,因为它被操作系统占用。

或者有人弄清楚了吗? 我很想知道…

正如NSGod已经指出的,你也可以使用CoreGraphics。

在你的class级(例如在-init):

 CFRunLoopRef runloop = (CFRunLoopRef)CFRunLoopGetCurrent(); CGEventMask interestedEvents = NSKeyDown; CFMachPortRef eventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, 0, interestedEvents, myCGEventCallback, self); // by passing self as last argument, you can later send events to this class instance CFRunLoopSourceRef source = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0); CFRunLoopAddSource((CFRunLoopRef)runloop, source, kCFRunLoopCommonModes); CFRunLoopRun(); 

在课外,但在同一个.m文件中:

 CGEventRef myCGEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) { if(type == NX_KEYDOWN) { // we convert our event into plain unicode UniChar myUnichar[2]; UniCharCount actualLength; UniCharCount outputLength = 1; CGEventKeyboardGetUnicodeString(event, outputLength, &actualLength, myUnichar); // do something with the key NSLog(@"Character: %c", *myUnichar); NSLog(@"Int Value: %i", *myUnichar); // you can now also call your class instance with refcon [(id)refcon sendUniChar:*myUnichar]; } // send event to next application return event; }