Domanda

Sto lavorando a un'applicazione di battitura-tutor per Mac OSX che ha bisogno di avere sequenze di tasti inoltrate ad esso, anche quando l'applicazione non è a fuoco.

C'è un modo per avere il sistema in avanti sequenze di tasti per l'applicazione, possibilmente attraverso NSDistributedNotificationCenter? Io stesso Googled stupido, e non sono stati in grado di trovare una risposta ...

EDIT:. Il codice di esempio seguente

Grazie @NSGod per avermi nella giusta direzione - ho finito per l'aggiunta di un eventi globali monitorare utilizzando il metodo addGlobalMonitorForEventsMatchingMask:handler:, che funziona a meraviglia. Per completezza, il mio aspetto di implementazione come questo:

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

}];

Per me, la parte difficile stava usando blocchi, quindi darò un po 'di descrizione in caso aiuta qualcuno:

La cosa da notare circa il codice di cui sopra è che è tutto una sola chiamata al metodo sul NSEvent. Il blocco è fornito come argomento, direttamente la funzione. Si potrebbe pensare che un po 'come un metodo in linea delegato. Solo perché questo ha preso un po 'per lavandino in per me, ho intenzione di lavoro attraverso un passo alla volta qui:

[NSEvent addGlobalMonitorForEventsMatchingMask:NSKeyDownMask

Questo primo bit non è un problema. Si sta chiamando un metodo di classe su NSEvent, e dirgli quale evento che stai cercando di monitor, in questo caso NSKeyDownMask. Un elenco di maschere per tipi di evento supportati possono essere trovati qui

Ora, veniamo alla parte difficile: gestore, che si aspetta un blocco:

handler:^(NSEvent *event){

Mi ci sono voluti un paio di errori di compilazione per ottenere questo diritto, ma (grazie Apple) sono stati i messaggi di errore molto costruttive. La prima cosa da notare è il carato ^. Che segnala l'inizio del blocco. Dopo di che, tra parentesi,

NSEvent *event

Il che dichiara la variabile che sarete usando all'interno del blocco per catturare l'evento. Si potrebbe chiamare

NSEvent *someCustomNameForAnEvent

Non importa, ti basta utilizzare quel nome all'interno del blocco. Quindi, questo è quasi tutto ciò che devi fare. Assicurarsi di chiudere la parentesi graffa, e la staffa per terminare la chiamata di metodo:

}];

E il gioco è fatto! Questa è davvero una specie di 'one-liner'. Non importa dove si esegue questa chiamata all'interno della vostra app - lo faccio in modo applicationDidFinishLaunching del AppDelegate. Poi, all'interno del blocco, è possibile chiamare altri metodi all'interno del tuo app.

È stato utile?

Soluzione

Se si sta bene con un requisito minimo di OS X 10.6+, e può bastare con "sola lettura" l'accesso al flusso degli eventi, è possibile installare un monitor evento globale a Cocoa: Guida cacao di gestione degli eventi: Monitoraggio di eventi .

Se è necessario supportare OS X 10.5 e versioni precedenti, e di sola lettura l'accesso è a posto, e non la mente a lavorare con l'Event Manager Carbon, si può sostanzialmente fare il carbonio equivalente utilizzando GetEventMonitorTarget(). (Sarete fatica a trovare alcuna documentazione (ufficiale) su quel metodo però). Quella API per la prima volta disponibile in OS X 10.3, credo.

Se avete bisogno di lettura-scrittura l'accesso al flusso di eventi, allora si avrà bisogno di guardare un po 'API di livello inferiore che fa parte di ApplicationServices> CoreGraphics: CGEventTapCreate() e amici. Questo è stato il primo disponibile in 10.4.

Si noti che tutti i 3 metodi richiedono che l'utente ha "Abilita l'accesso a dispositivi d'assistenza" abilitata nelle Preferenze di sistema> Accesso Universale pannello delle preferenze (almeno per eventi chiave).

Altri suggerimenti

sto postando il codice che ha lavorato per il mio caso.

sto aggiungendo il gestore di eventi globale dopo i lanci di app. Il mio collegamento rende ctrl + alt + cmd + T aprire la mia app.

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

}

Il problema che ho trovato con questo è che un qualsiasi tasto registrata a livello globale da un'altra applicazione non sarà cought ... o almeno nel mio caso, forse sto facendo qualcosa di sbagliato.

Se le vostre esigenze programma per visualizzare tutte le chiavi, come "Comando-Maiuscole-3", per esempio, allora non vedere che passano per visualizzarla ... dal momento che è ripreso dal sistema operativo.

O qualcuno ha capirlo? Mi piacerebbe sapere ...

Come NSGod già sottolineato è anche possibile utilizzare CoreGraphics.

Nella classe (ad esempio in -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();

Al di fuori della classe, ma nello stesso file .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;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top