CGEventTapCreate загадочно ломается с событиями «ключ вниз» - PullRequest
6 голосов
/ 03 июня 2010

Я использую 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 ]

9 голосов
/ 04 июня 2010

Что-то убивает мое событие, если обратный вызов занимает слишком много времени?

Некоторые люди подозревают, что в Snow Leopard есть ошибка, которая иногда отключает события, даже если они не занимают много времени. Чтобы справиться с этим, вы можете наблюдать за типом события kCGEventTapDisabledByTimeout и ответить, повторно включив ваш кран с помощью CGEventTapEnable.

3 голосов
/ 04 июня 2010

Прежде всего, почему ваше первое "если" позволяет проходить событиям нажатия клавиш и нажатия клавиш? Ваше второе «если» только позволяет системным событиям проходить в любом случае. Таким образом, для всех событий нажатия клавиш вверх-вверх вы создаете 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 голосов
/ 13 марта 2015

В вашем обработчике проверьте следующий тип и просто включите прослушиватель.

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