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; }