CGEventTapCreate 因“按下键”而神秘崩溃事件

发布于 2024-09-03 22:27:33 字数 1041 浏览 10 评论 0原文

当我的应用程序运行时,我使用 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 ...

I'm using CGEventTapCreate to "steal" media keys from iTunes when my app is running. The code inside of the callback that I pass to CGEventTapCreate examines the event, and if it finds that it's one of the media keys, posts an appropriate notification to the default notification center.

Now, this works fine if I post a notification for the "key up" event. If I do that for "key down" events, eventually my app stops getting media key events and iTunes takes over. Any ideas on what can be causing this? The relevant part of the code is below

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 ...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

小姐丶请自重 2024-09-10 22:27:34

如果回调时间太长,是否会终止我的事件点击?

有些人怀疑 Snow Leopard 有一个错误,有时会禁用事件点击,即使它们不会花费太长时间。要处理此问题,您可以监视事件类型 kCGEventTapDisabledByTimeout,并通过使用 CGEventTapEnable 重新启用点击来进行响应。

Does something kill my event tap if the callback takes too long?

Some people suspect that Snow Leopard has a bug that sometimes disables event taps even if they don't take too long. To handle that, you can watch for the event type kCGEventTapDisabledByTimeout, and respond by re-enabling your tap with CGEventTapEnable.

Oo萌小芽oO 2024-09-10 22:27:34

首先,为什么你的第一个“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 调用?

First of all, why is your first "if" allowing key-down and key-up events to pass? Your second "if" only lets system events pass through anyway. So for all key-down/-up events you create a NSEvent, just to drop the event one line further downwards. That makes little sense. An Event Tap should always be as fast as possible, otherwise it will slow down all event processing of the whole system. Your callback should not even be called for key-down/-up events, since system events are not key-down/-up events, they are system events. If they were key events, you would for sure never access data1, but instead use the "type" and "keyCode" methods to get the relevant information from them.

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

Also you cannot determine if that is the right kind of event by just looking at the data, you must also verify the subtype, that must be 8 for this kind of event:

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

The next logical step is to split the data up into its components:

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

And you probably don't care for repeating key events (that is when I keep the key pressed and it keeps sending the same event over and over again).

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

Finally you should not define any own constant, the system has ready to use constants for those keys:

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

And if this still not works, my next question would be what your post_notification function looks like and do you also see the described problem if you don't call post_notification there, but just make a NSLog call about the event you just saw?

长梦不多时 2024-09-10 22:27:34

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

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

In your handler, check for the following type, and just re-enable the listener.

if (type == kCGEventTapDisabledByTimeout) {
    NSLog(@"Event Taps Disabled! Re-enabling");
            CGEventTapEnable(eventTap, true);
    return event;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文