2011-01-20 78 views
54

我正在研究Mac OSX的打字导师应用程序,需要将击键转发给它,即使应用程序不在焦点中。OSX:检测系统级keyDown事件?

有没有办法让系统向应用程序转发按键,可能是通过NSDistributedNotificationCenter?我GOOGLE了自己傻了,一直没能找到答案......

编辑:示例代码下面

谢谢@NSGod指向我在正确的方向 - 我结束了使用方法addGlobalMonitorForEventsMatchingMask:处理程序:,它运行得非常漂亮的global events monitor。为了完整起见,我的实现看起来是这样的:

// 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。 A list of masks for supported event types可以发现here。现在

,我们来到了棘手的部分:处理器,它需要一个块:

handler:^(NSEvent *event){ 

我花了几个编译错误得到这个权利,但(谢谢你,苹果),他们非常有建设性的错误消息。首先要注意的是克拉^。这表示该块的开始。在此之后,括号内,

NSEvent *event 

其中宣布,将要使用的块中捕获事件的变量。你可以称它

NSEvent *someCustomNameForAnEvent 

没关系,你只需要在块中使用该名称。然后,这就是它的一切。确保关闭您的大括号和支架以完成方法调用:

}]; 

然后您就完成了!这真的是一种'单线'。在你的应用中执行这个调用的地方并不重要 - 我在AppDelegate的applicationDidFinishLaunching方法中执行。然后,在该块内,您可以从您的应用程序中调用其他方法。

+1

嘿@Pirripli感谢这么多解释块语法对我来说。真的帮助那些学习!顺便工作:) – lab12 2011-07-31 02:23:25

+1

没问题。 ;)当别人为我这样做的时候,我总是很感激,所以我尽力帮忙。 – 2011-08-09 02:34:02

+0

当我尝试这个代码块时,在声明中出现错误:'不兼容的块指针类型发送'void(^)(struct NSEvent *)'`到void(^)(NSEvent *)类型的参数 - @Pirripli ,我忘了添加一些东西吗?你是否必须包含一些特别的东西来支持块语法? [截图](https://img.skitch.com/20111024-tnmnka59j9fqn6u145g2jppk5d.png) – Tronathan 2011-10-24 14:57:52

回答

24

如果你是没事的OS X 10.6+的最低要求,并能与足够了“只读”权限事件流,您可以在可可安装全局事件监视器: Cocoa Event-Handling Guide: Monitoring Events

如果您需要支持OS X 10。5和更早的版本,只读访问没问题,不介意与碳事件管理器一起工作,基本上可以使用GetEventMonitorTarget()来完成碳当量。 (尽管如此,你将很难找到任何关于该方法的(官方)文档)。我相信这个API在OS X 10.3中首次可用。

如果您需要对事件流进行读写访问,则需要查看ApplicationServices> CoreGraphics:CGEventTapCreate()和朋友中包含的稍低级API。这在10.4中首次提供。

注意,这三种方法都需要用户具有“启用辅助设备的访问”,在系统偏好设置启用>通用访问偏好窗格(至少在关键事件)。

3

,我觉得这个问题是由其他应用程序全球注册的任何键不会毫无遗漏地......或者至少在我的情况,也许是我做错了什么。

如果你的程序需要显示所有的键,如“命令移3”为例,那么就不会看到去的,以显示它...因为它是采取了由操作系统。

还是有人认为呢?我很想知道......

14

我张贴,对于我来说,工作的代码。

我在应用程序启动后添加全局事件处理程序。我的快捷键使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]; 
     } 
    }]; 

} 
3

由于NSGod已经指出,你也可以使用CoreGraphics。

在你的类(如-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; 
}