2009-10-21 15 views
10

Estoy usando un código de carbono en mi proyecto Cocoa para manejar eventos clave globales (accesos directos) desde otras aplicaciones. Actualmente he configurado un controlador de eventos kEventHotKeyReleased y puedo obtener teclas de acceso rápido cuando mi aplicación no está activa. Eso desencadena alguna operación en mi aplicación.Cómo controlar el estado de la tecla modificadora global (en cualquier aplicación)?

El problema que tengo con el comportamiento de kEventHotKeyReleased es:

decir, por ejemplo presiono la combinación de teclas Cmd-Shift-P. Tan pronto como suelte la tecla "P" se activa el evento de la tecla de acceso rápido. Necesito poder desencadenar el evento (o desencadenarlo manualmente) cuando todas las de las claves están sin presionar (es decir, las teclas Cmd y Shift están liberadas también).

Es fácil monitorear teclas rápidas pero no he visto nada para monitorear las teclas individuales. Si pudiera controlar los estados clave de modificación estaría en el negocio.

¿Alguna pista sobre cómo hacer esto?

¡Gracias de antemano!


ACTUALIZACIÓN:

He intentado usar kEventRawKeyUp y kEventRawKeyModifiersChanged pero mientras kEventHotKeyReleased obras los dos no a pesar de que las crean de la misma forma exacta como kEventHotKeyReleased.

EventTypeSpec eventTypes[] = {{kEventClassKeyboard, kEventHotKeyReleased}, {kEventClassKeyboard, kEventRawKeyUp}}; 
// Changing the order in the list does not help, nor does removing kEventHotKeyReleased 
OSStatus err = InstallApplicationEventHandler(&globalHotkeyHandler, GetEventTypeCount(eventTypes), eventTypes, NULL, NULL); 
// err == noErr after this line 

El método se llama globalHotKeyHandler para kEventHotKeyReleased, pero no para kEventRawKeyUp por alguna razón me parece que no puede captar. Esto es lo que mi método globalHotKeyHandler parece:

OSStatus globalHotkeyHandler(EventHandlerCallRef nextHandler, EventRef anEvent, void *userData) { 
    NSLog(@"Something happened!"); 
} 

¿Hay una llamada adicional que necesita ser hecho o algo más se me olvidó?

N.B: A primera vista, parece que podría ser que el acceso para dispositivos de ayuda está desactivada, pero no es. Así que estoy bastante despistado.


ACTUALIZACIÓN 2:

Investigué un poco en el CGEventTap Leibowitzn sugirió y me ocurrió con esta configuración:

CFMachPortRef keyUpEventTap = CGEventTapCreate(kCGHIDEventTap,kCGHeadInsertEventTap,kCGEventTapOptionListenOnly,kCGEventKeyUp,&keyUpCallback,NULL); 
CFRunLoopSourceRef keyUpRunLoopSourceRef = CFMachPortCreateRunLoopSource(NULL, keyUpEventTap, 0); 
CFRelease(keyUpEventTap); 
CFRunLoopAddSource(CFRunLoopGetCurrent(), keyUpRunLoopSourceRef, kCFRunLoopDefaultMode); 
CFRelease(keyUpRunLoopSourceRef); 

... y la devolución de llamada:

CGEventRef keyUpCallback (CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) { 
    NSLog(@"KeyUp event tapped!"); 
    return event; 
} 

Como puedes ver, estoy usando kCGEventKeyUp como la máscara para el evento tap pero de alguna manera estoy recibiendo mouse abajo events ??! ??


Actualización 3:

Ok olvidar que, pasé por alto la línea en el documento que dice que el uso CGEventMaskBit (kCGEventKeyUp) para este parámetro, por lo que la llamada correcta es:

CGEventTapCreate(kCGHIDEventTap,kCGHeadInsertEventTap,kCGEventTapOptionListenOnly,CGEventMaskBit(kCGEventKeyUp),&keyUpCallback,NULL); 

Todavía estoy teniendo un problema: las teclas modificadoras no activan el kCGEventKeyUp ...


ACTUALIZACIÓN 4:

Ok hay que olvidar que de nuevo ... estoy obligado a responder a mis propias preguntas 5 minutos después de preguntar a ellos hoy eh!

para interceptar las teclas modificadoras, utilice kCGEventFlagsChanged:

CGEventTapCreate(kCGHIDEventTap,kCGHeadInsertEventTap,kCGEventTapOptionListenOnly,CGEventMaskBit(kCGEventFlagsChanged),&callbackFunction,NULL); 

Así que en esencia me dieron la llave y la clave de modificador de trabajo de detección de estado, pero Todavía estoy interesado en saber qué kEventRawKeyUp no funciona ...


NB: también tenga en cuenta que estoy desarrollando en tigre con el objetivo de tener soporte para nuevos y más versiones del sistema operativo cuanto más se pueda. CGEventTap tiene una calificación de 10.4 o superior, así que lo usaré por ahora, pero una solución compatible con versiones anteriores sería bienvenida.

Respuesta

3

Una opción es usar EventTaps. Esto le permite controlar todos los eventos del teclado. Ver: http://developer.apple.com/mac/library/documentation/Carbon/Reference/QuartzEventServicesRef/Reference/reference.html#//apple_ref/c/func/CGEventTapCreate

Lamentablemente, las pulsaciones de eventos dejarán de funcionar si una aplicación solicita una entrada segura. Por ejemplo, Quicken.

+0

Creo que hay una forma de controlar solo las teclas modificadoras. Verifique el código fuente en el cuadro de búsqueda rápida de Google. Tiene un código para detectar cuando el usuario toca dos veces la tecla de comando. Ver: http://www.google.com/codesearch/p?hl=en&sa=N&cd=2&ct=rc#qWBe9JkJhME/trunk/QuickSearchBox/QSB/QSBApplicationDelegate.m&q=modifiersChangedWhileInactive%20package:http://qsb-mac \ .googlecode \.com – Leibowitzn

+0

Ahh, solo se están registrando para el siguiente evento de carbono: {kEventClassKeyboard, kEventRawKeyModifiersChanged} – Leibowitzn

+0

Me he dado cuenta de kEventRawKeyModifiersChanged y kEventRawKeyUp pero parece que no puedo hacer que funcionen para mi vida. Ver la actualización en mi pregunta. (gracias por su ayuda, por cierto!) – Form

1
OSStatus err = InstallApplicationEventHandler(&globalHotkeyHandler, GetEventTypeCount(eventTypes), eventTypes, NULL, NULL); 

Esto no es global. Esto instala el controlador solo cuando su propia aplicación está activa, y (creo) después de los filtros de eventos propios del Carbon Event Manager.

Debe usar InstallEventHandler, que toma un destino de evento como su primer parámetro (InstallApplicationEventHandler es una macro que pasa el destino del evento de la aplicación).

Para los eventos que ocurren mientras la aplicación no está activa, el destino que desea es GetEventMonitorTarget(). Para eventos que ocurran mientras su aplicación es activa, el objetivo que desea es GetEventDispatcherTarget(). Para detectar eventos sin importar qué aplicación esté activa, instale su controlador en ambos objetivos.

Ahora, sin embargo, solo usaría CGEventTaps, como sugirió Leibowitzn.

+0

Es extraño porque InstallApplicationEventHandler instala mi clave de acceso rápido en todas partes, no solo mientras mi aplicación está activa. Puedo activar las teclas de acceso rápido desde cualquier aplicación. Además, cuando intento usar InstallEventHandler con GetEventMonitorTarget(), parece que no funciona, pero debo estar haciendo algo mal. ¿Qué ventaja hay para usar CGEventTap en lugar de lo que estoy usando en este momento? – Form

+0

Es mucho más fácil. –

+0

Además, si solo desea registrar una tecla de acceso rápido, hágalo usando [la función RegisterEventHotKey] (http://developer.apple.com/legacy/mac/library/documentation/Carbon/Reference/Carbon_Event_Manager_Ref/Reference/reference.html # // apple_ref/c/func/RegisterEventHotKey), que existe para ese propósito. –

Cuestiones relacionadas