Comment surveiller l'état de la clé de modification globale (dans n'importe quelle application)?
-
05-07-2019 - |
Question
J'utilise du code carbone dans mon projet Cocoa pour gérer les événements clés globaux (raccourcis) à partir d'autres applications. Actuellement, j'ai configuré un gestionnaire d'événements kEventHotKeyReleased
et je peux obtenir des touches de raccourci avec succès lorsque mon application n'est pas active. Cela déclenche une opération dans mon application.
Le problème que j'ai avec le comportement de kEventHotKeyReleased
est:
Supposons, par exemple, que j'appuie sur la combinaison de touches Cmd-Shift-P. Dès que je relâche le "P" key l'événement de raccourci clavier est déclenché. Je dois pouvoir déclencher l'événement (ou le déclencher manuellement) lorsque toutes les touches ne sont pas pressées (les touches Cmd et Shift sont également relâchées).
Il est facile de surveiller les touches de raccourci, mais je n’ai rien vu pour contrôler les frappes individuelles. Si je pouvais surveiller la touche de modification, je serais en affaires.
Avez-vous des conseils sur la manière de procéder?
Merci d'avance!
UPDATE:
J'ai essayé d'utiliser kEventRawKeyUp
et kEventRawKeyModifiersChanged
mais alors que kEventHotKeyReleased
ne fonctionne pas, même si je les ai configurés exactement de la même manière que 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
La méthode globalHotKeyHandler
est appelée pour kEventHotKeyReleased
, mais pas pour kEventRawKeyUp
pour une raison quelconque, je n'arrive pas à saisir. Voici à quoi ressemble ma méthode globalHotKeyHandler
:
OSStatus globalHotkeyHandler(EventHandlerCallRef nextHandler, EventRef anEvent, void *userData) {
NSLog(@"Something happened!");
}
Y a-t-il un appel supplémentaire à faire ou quelque chose que j'ai oublié?
N.B: À première vue, il semblerait que l'accès pour dispositifs d'assistance soit désactivé mais il ne l'est pas . Donc, je suis assez désemparé.
UPDATE 2:
J'ai un peu exploré le CGEventTap
suggéré par Leibowitzn et j'ai proposé cette configuration:
CFMachPortRef keyUpEventTap = CGEventTapCreate(kCGHIDEventTap,kCGHeadInsertEventTap,kCGEventTapOptionListenOnly,kCGEventKeyUp,&keyUpCallback,NULL);
CFRunLoopSourceRef keyUpRunLoopSourceRef = CFMachPortCreateRunLoopSource(NULL, keyUpEventTap, 0);
CFRelease(keyUpEventTap);
CFRunLoopAddSource(CFRunLoopGetCurrent(), keyUpRunLoopSourceRef, kCFRunLoopDefaultMode);
CFRelease(keyUpRunLoopSourceRef);
... et le rappel:
CGEventRef keyUpCallback (CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
NSLog(@"KeyUp event tapped!");
return event;
}
Comme vous pouvez le constater, j'utilise kCGEventKeyUp
comme masque pour la sélection d'événement, mais je reçois en quelque sorte des événements survolés avec la souris ? ?! ??
UPDATE 3:
Ok, oubliez ça, j'ai oublié la ligne dans la doc qui disait d'utiliser CGEventMaskBit (kCGEventKeyUp) pour ce paramètre, donc le bon appel est:
CGEventTapCreate(kCGHIDEventTap,kCGHeadInsertEventTap,kCGEventTapOptionListenOnly,CGEventMaskBit(kCGEventKeyUp),&keyUpCallback,NULL);
Je rencontre toujours un problème: les touches de modification ne déclenchent pas kCGEventKeyUp ...
MISE À JOUR 4:
Ok, oublie ça encore une fois ... Je suis obligé de répondre à mes propres questions 5 minutes après leur avoir posé la question aujourd'hui, hein!
Pour intercepter les touches de modification, utilisez kCGEventFlagsChanged
:
CGEventTapCreate(kCGHIDEventTap,kCGHeadInsertEventTap,kCGEventTapOptionListenOnly,CGEventMaskBit(kCGEventFlagsChanged),&callbackFunction,NULL);
Donc, en gros, la détection de l'état de la clé et du modificateur fonctionnait, mais je suis toujours intéressé par savoir pourquoi kEventRawKeyUp
ne fonctionne pas ...
N.B: Notez également que je développe sur Tiger dans le but de prendre en charge autant que possible les versions nouvelles et anciennes du système d'exploitation. CGEventTap n’ayant que 10.4+, j’utiliserai cette solution pour le moment, mais une solution rétro-compatible serait la bienvenue.
La solution
Une option consiste à utiliser EventTaps. Cela vous permet de surveiller tous les événements du clavier. Voir:
Autres conseils
OSStatus err = InstallApplicationEventHandler(&globalHotkeyHandler, GetEventTypeCount(eventTypes), eventTypes, NULL, NULL);
Ce n'est pas global. Cela installe le gestionnaire uniquement lorsque votre propre application est active et (je crois) après les propres filtres d’événements de Carbon Event Manager.
Vous devez utiliser InstallEventHandler
, qui prend une cible d'événement comme premier paramètre ( InstallApplicationEventHandler
est une macro qui transmet la cible d'événement d'application).
Pour les événements qui se produisent lorsque votre application n'est pas active, la cible souhaitée est GetEventMonitorTarget ()
. Pour les événements qui se produisent lorsque votre application est active, la cible souhaitée est GetEventDispatcherTarget ()
. Pour attraper les événements quelle que soit l'application active, installez votre gestionnaire sur les deux cibles.
De nos jours, j’utiliserais simplement CGEventTaps, comme l’a suggéré Leibowitzn.