CGEventTapCreate breaks down mysteriously with “key down” events

人盡茶涼 提交于 2019-12-21 04:52:25

问题


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

回答1:


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.




回答2:


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?




回答3:


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


来源:https://stackoverflow.com/questions/2969110/cgeventtapcreate-breaks-down-mysteriously-with-key-down-events

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!