Pregunta

Estoy trabajando en una aplicación de maquetación-tutor para Mac OS X que necesite disponer de pulsaciones de teclas que se le presenten, incluso cuando la aplicación no está en foco.

¿Hay una manera de tener el sistema hacia adelante pulsaciones de teclas a la aplicación, posiblemente a través de NSDistributedNotificationCenter? He buscado en Google yo tonto, y no he sido capaz de encontrar una respuesta ...

EDIT:. código de ejemplo siguiente

Gracias @NSGod para mí apuntando en la dirección correcta - Terminé la adición de un eventos globales controlan mediante el addGlobalMonitorForEventsMatchingMask:handler: método, que funciona muy bien. Para completar, mi implementación es similar al siguiente:

// register for keys throughout the device...
[NSEvent addGlobalMonitorForEventsMatchingMask:NSKeyDownMask
                                       handler:^(NSEvent *event){

    NSString *chars = [[event characters] lowercaseString];
    unichar character = [chars characterAtIndex:0];

    NSLog(@"keydown globally! Which key? This key: %c", character);

}];

Para mí, la parte difícil estaba usando bloques, así que voy a dar una pequeña descripción en caso de que ayuda a nadie:

Lo que hay que aviso sobre el código anterior es que todo es una sola llamada al método en NSEvent. El bloque se suministra como un argumento, directamente a la función. Se podría pensar que es algo así como un método en línea delegado. El hecho de que este tomó un tiempo para lavabo en para mí, voy a trabajar a través de él paso a paso aquí:

[NSEvent addGlobalMonitorForEventsMatchingMask:NSKeyDownMask

Este primer bit no es ningún problema. Que está llamando un método de clase en NSEvent, y diciéndole qué evento que está mirando a un monitor, en este caso NSKeyDownMask. A lista de máscaras para los tipos de eventos apoyados se pueden encontrar aquí

Ahora, llegamos a la parte difícil: manipulador, que espera un bloque:

handler:^(NSEvent *event){

Me tomó un par de errores de compilación para obtener este derecho, pero (gracias Apple) que eran mensajes de error muy constructivas. La primera cosa a notar es el quilate ^. Que señala el comienzo del bloque. Después de eso, dentro de los paréntesis,

NSEvent *event

¿Qué declara la variable que se va a utilizar dentro del bloque para capturar el evento. Se podría definir como

NSEvent *someCustomNameForAnEvent

No importa, vas a estar usando ese nombre dentro del bloque. Entonces, eso es casi todo lo que hay que hacer. Asegúrese de cerrar la llave de cierre, y el soporte para terminar la llamada al método:

}];

Y ya está! Esto realmente es una especie de 'one-liner'. No importa donde se ejecuta esta llamada dentro de su aplicación - lo hago en el método de la applicationDidFinishLaunching AppDelegate. Entonces, dentro del bloque, puede llamar a otros métodos desde la aplicación.

¿Fue útil?

Solución

Si estás bien con un requisito mínimo de OS X 10.6+, y puede ser suficiente con "sólo lectura" el acceso a la corriente de los acontecimientos, se puede instalar un monitor evento global en Cocoa: Cacao de control de eventos Guía: Monitoreo de Eventos .

Si necesita apoyar OS X 10.5 y versiones anteriores, y el acceso de sólo lectura está bien, y no les importa trabajar con el director de eventos de carbono, se puede hacer básicamente el uso de GetEventMonitorTarget() de carbono equivalente. (Se le apuros para encontrar ninguna documentación (oficial) en ese método sin embargo). API que fue el primero disponible en OS X 10.3, creo.

Si necesita de lectura-escritura de acceso a la secuencia de eventos, entonces usted tendrá que mirar un poco API de nivel inferior que forma parte de ApplicationServices> CoreGraphics: CGEventTapCreate() y amigos. Esta fue la primera disponible en 10.4.

Tenga en cuenta que los 3 métodos requieren que el usuario tenga "Habilitar el acceso de los dispositivos de ayuda" activada en las Preferencias del Sistema> panel de preferencias de Acceso Universal (al menos para los eventos clave).

Otros consejos

Estoy poniendo el código que funcionó para mi caso.

Estoy agregando el controlador de eventos global después de los lanzamientos de aplicaciones. Mi acceso directo hace Ctrl + Alt + Cmd + T abre mi aplicación.

- (void) applicationWillFinishLaunching:(NSNotification *)aNotification
{
    // Register global key handler, passing a block as a callback function
    [NSEvent addGlobalMonitorForEventsMatchingMask:NSKeyDownMask
                                           handler:^(NSEvent *event){

        // Activate app when pressing cmd+ctrl+alt+T
        if([event modifierFlags] == 1835305 && [[event charactersIgnoringModifiers] compare:@"t"] == 0) {

              [NSApp activateIgnoringOtherApps:YES];
        }
    }];

}

El problema que encuentro con esto es que cualquier tecla registrado a nivel mundial por otra aplicación no será cought ... o por lo menos en mi caso, tal vez estoy haciendo algo mal.

Si sus necesidades de programa para mostrar todas las teclas, como "Comando-Shift-3", por ejemplo, entonces no va a ver que pasan a mostrar que ... ya que es absorbido por el sistema operativo.

O es que alguien darse cuenta de eso? Me gustaría saber ...

Como ya se ha señalado NSGod También puede utilizar CoreGraphics.

En su clase (por ejemplo, en -init):

CFRunLoopRef runloop = (CFRunLoopRef)CFRunLoopGetCurrent();

CGEventMask interestedEvents = NSKeyDown;
CFMachPortRef eventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, 
                                        0, interestedEvents, myCGEventCallback, self);
// by passing self as last argument, you can later send events to this class instance

CFRunLoopSourceRef source = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, 
                                                           eventTap, 0);
CFRunLoopAddSource((CFRunLoopRef)runloop, source, kCFRunLoopCommonModes);
CFRunLoopRun();

Fuera de la clase, pero en el mismo archivo .m:

CGEventRef myCGEventCallback(CGEventTapProxy proxy, 
                             CGEventType type, 
                             CGEventRef event, 
                             void *refcon)
{

    if(type == NX_KEYDOWN)
    {
       // we convert our event into plain unicode
       UniChar myUnichar[2];
       UniCharCount actualLength;
       UniCharCount outputLength = 1;
       CGEventKeyboardGetUnicodeString(event, outputLength, &actualLength, myUnichar);

       // do something with the key

       NSLog(@"Character: %c", *myUnichar);
       NSLog(@"Int Value: %i", *myUnichar);

       // you can now also call your class instance with refcon
       [(id)refcon sendUniChar:*myUnichar];
   }

   // send event to next application
   return event;
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top