CGEventTapCreate以“关键”事件神秘地崩溃

时间:2010-06-03 19:27:21

标签: cocoa macos macos-carbon core-foundation

当我的应用运行时,我正在使用CGEventTapCreate从iTunes中“窃取”媒体密钥。我传递给CGEventTapCreate的回调内部代码检查事件,如果发现它是媒体密钥之一,则向默认通知中心发布适当的通知。

现在,如果我发布“key up”事件的通知,这样可以正常工作。如果我为“关键”事件这样做,最终我的应用程序停止获取媒体键事件和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 ...

3 个答案:

答案 0 :(得分:9)

  

如果回调花费的时间太长,会不会触发我的事件?

有些人怀疑Snow Leopard有一个错误,有时会禁用事件点击,即使他们不花太长时间。要处理此问题,您可以查看事件类型kCGEventTapDisabledByTimeout,并通过CGEventTapEnable重新启用您的点按来回复。

答案 1 :(得分:3)

首先,为什么你的第一个“if”允许关键和关键事件通过?你的第二个“如果”只允许系统事件通过。因此,对于所有按键/向上事件,您创建一个NSEvent,只是为了将事件向下放一行。这没什么意义。事件点击应该始终尽可能快,否则会减慢整个系统的所有事件处理速度。甚至不应该为key-down / -up事件调用你的回调,因为系统事件不是key-down / -up事件,它们是系统事件。如果它们是关键事件,您肯定永远不会访问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调用?

答案 2 :(得分:0)

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

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