【问题标题】:CGEventTapCreate breaks down mysteriously with "key down" eventsCGEventTapCreate 用“key down”事件神秘地崩溃了
【发布时间】:2011-02-27 11:41:09
【问题描述】:

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

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

【问题讨论】:

  • 似乎是时间问题。将 post_notification 替换为 sleep(1) 后,如果我使用 PlayPauseKeyDown,在几次按键后,iTunes 会窃取媒体密钥。如果我使用 PlayPauseKeyUp 仍然有效。

标签: cocoa macos macos-carbon core-foundation


【解决方案1】:

如果回调时间过长,是否会杀死我的事件点击?

有些人怀疑 Snow Leopard 有一个错误,它有时会禁用事件点击,即使它们不需要太长时间。要解决这个问题,您可以查看事件类型 kCGEventTapDisabledByTimeout,并通过使用 CGEventTapEnable 重新启用您的点击来做出响应。

【讨论】:

  • 是的,就是这样。处理 kCGEventTapDisabledByTimeout 就可以了。
  • 谢谢!这让我发疯了!
【解决方案2】:

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

【讨论】:

  • 谢谢,Mecki!我已经修复了我的代码,但问题仍然存在。如果我将 post_notification 放在“if (keyState == 0x0A) ...”下,iTunes 最终将“接管”媒体密钥,如果我将它放在“if(keyState == 0x0B)”下,一切似乎都正常。 post_notification 只是调用默认 NSNotificationCenter 的 postNotificationName: ...。现在,如果我用 sleep(1) 替换 post_notification,问题就会发生得更快。如果回调时间过长,是否会杀死我的事件点击?发送通知是否太慢而无法从回调中调用?
【解决方案3】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-21
    • 1970-01-01
    相关资源
    最近更新 更多