2010-06-03 10 views
6

Estoy usando CGEventTapCreate para "robar" las claves multimedia de iTunes cuando se está ejecutando mi aplicación. El código dentro de la devolución de llamada que paso al CGEventTapCreate examina el evento, y si encuentra que es una de las claves de medios, publica una notificación apropiada al centro de notificaciones predeterminado.CGEventTapCreate se descompone misteriosamente con eventos "key down"

Ahora, esto funciona bien si publico una notificación para el evento de "activación". Si hago eso para eventos de "reducción de teclas", eventualmente mi aplicación deja de recibir eventos de tecla multimedia y iTunes toma el control. ¿Alguna idea sobre lo que puede estar causando esto? La parte pertinente del código está por debajo

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

Parece ser un problema de tiempo. Se reemplazó la post_notificación por suspensión (1) y allí está, después de unas pocas pulsaciones de teclas, iTunes roba las teclas multimedia si estoy usando PlayPauseKeyDown. Todavía funciona si estoy usando PlayPauseKeyUp. – svintus

Respuesta

9

¿Hay algo que mate mi evento si la devolución de llamada tarda demasiado?

Algunas personas sospechan que Snow Leopard tiene un error que a veces desactiva los golpes del evento, incluso si no tardan demasiado. Para manejar eso, puede mirar para el tipo de evento kCGEventTapDisabledByTimeout y responder volviendo a habilitar su toque con CGEventTapEnable.

+1

Sí, esto fue todo. kCGEventTapDisabledByTimeout es el truco. – svintus

+0

¡Gracias! ¡Eso me estaba volviendo loco! –

3

En primer lugar, ¿por qué es su primer "si" dejar que los sucesos claves en marcha llave hacia abajo y pasen? Su segundo "si" solo deja pasar los eventos del sistema de todos modos. Entonces, para todos los eventos key-down/up crea un NSEvent, solo para soltar el evento una línea más hacia abajo. Eso tiene poco sentido. Un evento de toque debe ser siempre lo más rápido posible, de lo contrario ralentizará todo el procesamiento de eventos de todo el sistema. Su devolución de llamada ni siquiera se debe invocar para eventos key-down/up, ya que los eventos del sistema no son eventos key-down/up, son eventos del sistema. Si fueran eventos clave, seguramente nunca accedería a data1, sino que usaría los métodos "type" y "keyCode" para obtener la información relevante de ellos.

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 

Además no se puede determinar si ese es el tipo de evento con sólo mirar los datos, también debe verificar el subtipo, que debe ser 8 para este tipo de eventos:

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

el siguiente paso lógico es dividir los datos en sus componentes:

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

y es probable que no se preocupa por la repetición de los eventos clave (que es cuando me mantenga pulsada la tecla y se mantiene el envío de un mismo evento una y otra vez).

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

Por último, no hay que definir cualquier constante propia, el sistema tiene listo para usar constantes para esas llaves:

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

Y si esto todavía no funciona, mi siguiente pregunta sería cuál es su función post_notification parece y también ves el problema descrito si no llamas post_notification allí, ¿pero solo haces una llamada NSLog sobre el evento que acabas de ver?

+0

¡Gracias, Mecki! He arreglado mi código, pero el problema persiste. Si pongo post_notification debajo de "if (keyState == 0x0A) ...", iTunes se hará cargo de las claves de medios eventualmente, si lo pongo en" if (keyState == 0x0B) ", todo parece funcionar bien. post_notification simplemente llama a postNotificationName: ... del predeterminado NSNotificationCenter. Ahora, si reemplazo post_notification con sleep (1), el problema ocurre mucho más rápido. ¿Algo mata a mi evento, toca si la devolución de llamada tarda demasiado? ¿Está enviando una notificación demasiado lenta para llamar desde la devolución de llamada? – svintus

0

En su controlador, busque el siguiente tipo, y simplemente vuelva a habilitar al oyente.

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