2010-06-03 110 views
6

我在使用CGEventTapCreate在我的应用运行时从iTunes中“窃取”媒体密钥。我传递给CGEventTapCreate的回调中的代码检查事件,如果它发现它是媒体密钥之一,则将适当的通知发布到默认通知中心。CGEventTapCreate利用“按键”事件神秘地分解了

现在,这工作正常,如果我发布“关键”事件通知。如果我这样做是因为“关键”事件,我的应用程序最终会停止获取媒体关键事件,iTunes会接管。任何想法都可能造成这种情况?代码的相关部分低于

enum { 
... 
    PlayPauseKeyDown = 0x100A00, 
    PlayPauseKeyUp = 0x100B00, 
... 
}; 

static CGEventRef event_tap_callback(CGEventTapProxy proxy, 
            CGEventType type, 
            CGEventRef event, 
            void *refcon) 
{ 
    if (!(type == NX_SYSDEFINED) || (type == NX_KEYUP) || (type == NX_KEYDOWN)) 
     return event; 

    NSEvent* keyEvent = [NSEvent eventWithCGEvent: event]; 
    if (keyEvent.type != NSSystemDefined) return event; 

    switch(keyEvent.data1) 
    { 
    case PlayPauseKeyUp: // <--- this works reliably 
    //case PlayPauseKeyDown: // <--- this will break eventually 
     post_notification(@"PlayPauseMediaKeyPressed", nil, nil); 
     return NULL; 

    ... and so on ... 
+0

似乎是一个计时问题。通过睡眠取代post_notification(1),并在那里,在几次按键之后,如果使用PlayPauseKeyDown,iTunes会盗取媒体密钥。如果我使用PlayPauseKeyUp,仍然有效。 – svintus 2010-06-03 19:40:44

回答

9

如果回调时间过长,是否会导致我的事件触发?

有些人怀疑Snow Leopard有一个错误,即使不会花太长时间,它也有时会禁用事件触发。为了解决这个问题,你可以注意事件类型kCGEventTapDisabledByTimeout,并通过CGEventTapEnable重新启用你的水龙头来响应。

+1

是的,就是这样。 kCGEventTapDisabledByTimeout会诀窍 – svintus 2010-06-04 15:49:26

+0

谢谢!这让我疯狂! – 2011-01-22 14:31:26

3

首先,为什么是你的第一个“如果”允许键下和键起事件传递?你的第二个“if”只允许系统事件通过。因此,对于所有键入/发生事件,您创建一个NSEvent,只需将事件向下拖放一行即可。这没什么意义。事件点击应始终尽可能快,否则会减慢整个系统的所有事件处理。由于系统事件不是键盘事件/事件,它们是系统事件,所以甚至不应该为回调/事件事件调用回调。如果它们是关键事件,那么肯定不会访问data1,而是使用“type”和“keyCode”方法从它们获取相关信息。

static CGEventRef event_tap_callback(CGEventTapProxy proxy, 
            CGEventType type, 
            CGEventRef event, 
            void *refcon) 
{ 
    NSEvent * sysEvent; 

    // No event we care for? return ASAP 
    if (type != NX_SYSDEFINED) return event; 

    sysEvent = [NSEvent eventWithCGEvent:event]; 
    // No need to test event type, we know it is NSSystemDefined, 
    // becuase that is the same as NX_SYSDEFINED 

你也不能确定,如果这是通过看的数据正确的情况下,你还必须验证的亚型,这必须是8对这种事件的:

if ([sysEvent subtype] != 8) return event; 

下一个逻辑步骤是将数据分开到它的组成部分:

int data = [sysEvent data1]; 
    int keyCode = (data & 0xFFFF0000) >> 16; 
    int keyFlags = (data & 0xFFFF); 
    int keyState = (keyFlags & 0xFF00) >> 8; 
    BOOL keyIsRepeat = (keyFlags & 0x1) > 0; 

而且你可能不会对重复按键事件无关(这是当我不断按下的键,并持续发送相同的事件一遍又一遍地)。

// You probably won't care for repeating events 
    if (keyIsRepeat) return event; 

最后,你不应该定义任何自己的常量,系统有准备使用常量这些按键:

// Analyze the key 
    switch (keyCode) { 
    case NX_KEYTYPE_PLAY: 
     // Play/Pause key 
     if (keyState == 0x0A) { 
     // Key down 
     // ...do your stuff here... 
     return NULL; 
     } else if (keyState == 0x0B) { 
     // Key Up 
     // ...do your stuff here... 
     return NULL; 
     } 
     // If neither down nor up, we don't know 
     // what it is and better ignore it 
     break; 


    case NX_KEYTYPE_FAST: 
     // (Fast) Forward 
     break; 

    case NX_KEYTYPE_REWIND: 
     // Rewind key 
     break; 
    } 

    // If we get here, we have not handled 
    // the event and want system to handle it 
    return event; 
} 

如果这仍然没有工作,我的下一个问题是你的post_notification功能看起来像,你是否也看到了描述的问题,如果你不打电话post_notification那里,但只是让你看到的事件的NSLog电话?

+0

谢谢,Mecki!我修复了我的代码,但问题仍然存在。如果我把post_notification放在“if(keyState == 0x0A)...”“,iTunes最终会”接管“媒体密钥,如果我把它放在”if(keyState == 0x0B)“之下,一切似乎都可以正常工作,post_notification只需调用默认NSNotificationCenter的postNotificationName:... 现在,如果我将post_notification替换为sleep(1),问题会更快发生。如果回调时间过长,是否会导致事件触发?是否发送通知太慢而无法从回调中调用? – svintus 2010-06-03 21:26:52

0

在您的处理程序中,检查以下类型,并重新启用侦听器。

if (type == kCGEventTapDisabledByTimeout) { 
    NSLog(@"Event Taps Disabled! Re-enabling"); 
      CGEventTapEnable(eventTap, true); 
    return event; 
}