Modifier NSEvent pour envoyer une clé différente de celle qui a été pressée

j'essaie de créer un crochet de clavier OS X à des fins de technologie d'assistance (c.-à-d. ne vous inquiétez pas, pas un keylogger).

Quand un utilisateur appuie sur une touche, je veux prévenir le vrai keypress et envoyer un faux clavier (caractère de mon choix) à la place.

j'ai le code suivant:

- (void) hookTheKeyboard {
    CGEventMask keyboardMask = CGEventMaskBit(kCGEventKeyDown);
    id eventHandler = [NSEvent addGlobalMonitorForEventsMatchingMask:keyboardMask handler:^(NSEvent *keyboardEvent) {
        NSLog(@"keyDown: %c", [[keyboardEvent characters] characterAtIndex:0]);
        //Want to: Stop the keyboard input
        //Want to: Send another key input instead
    }];
}

avez-vous de l'aide pour atteindre l'un ou l'autre de ces objectifs? Fondamentalement modifier le NSEvent "keyboardEvent" pour envoyer un caractère différent. Grâce.

19
demandé sur cksubs 2011-04-26 07:37:13

2 réponses

Vous ne pouvez pas faire cela avec l' NSEvent API, mais vous le faire avec un CGEventTap. Vous pouvez créer un event tap actif et enregistrer rappel qui reçoit un CGEventRef et peut les modifier (si nécessaire) et le retourner à modifier le réel des flux d'événements.


EDIT

voici un programme simple qui, en cours d'exécution, remplace chaque touche" b "par un"v":

#import <Cocoa/Cocoa.h>

CGEventRef myCGEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
  //0x0b is the virtual keycode for "b"
  //0x09 is the virtual keycode for "v"
  if (CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode) == 0x0B) {
    CGEventSetIntegerValueField(event, kCGKeyboardEventKeycode, 0x09);
  }

  return event;
}

int main(int argc, char *argv[]) {
  NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
  CFRunLoopSourceRef runLoopSource;

  CFMachPortRef eventTap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, kCGEventMaskForAllEvents, myCGEventCallback, NULL);

  if (!eventTap) {
    NSLog(@"Couldn't create event tap!");
    exit(1);
  }

  runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);

  CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);

  CGEventTapEnable(eventTap, true);

  CFRunLoopRun();

  CFRelease(eventTap);
  CFRelease(runLoopSource);
  [pool release];

  exit(0);
}

(histoire drôle: comme j'étais en éditant ce post, j'ai continué à essayer d'écrire "remplace chaque 'b' frappe", mais il a continué à sortir comme "remplace chaque 'V' frappe". J'étais confus. Puis je me suis souvenu que je n'avais pas encore arrêté l'application.)

40
répondu Dave DeLong 2011-04-26 05:49:17

j'ai passé à travers cette réponse, ayant besoin de faire la même chose mais seulement pour les événements ma demande pas global . Il y a une solution beaucoup plus simple, pour ce problème beaucoup plus simple, que je note ici incase il est utile pour n'importe qui d'autre:

  • j'ai intercepté l'événement à la fenêtre, en créant un override pour sendEvent:. Je vérifie ensuite les événements clés (KeyUp ou KeyDown), puis je crée simplement un nouvel événement en utilisant presque toutes les données du prévous. événement, puis appelez nswindow superclass avec cet événement à la place.

cela semble fonctionner parfaitement pour moi et je n'ai même pas eu à modifier le code - clé partie-mais peut-être que cela pourrait être un problème...

exemple dans Swift:

class KeyInterceptorWindow : NSWindow {


    override func sendEvent(theEvent: NSEvent) {

        if theEvent.type == .KeyDown || theEvent.type == .KeyUp {
            println(theEvent.description)
            let newEvent = NSEvent.keyEventWithType(theEvent.type, 
                location: theEvent.locationInWindow, 
                modifierFlags: theEvent.modifierFlags, 
                timestamp: theEvent.timestamp, 
                windowNumber: theEvent.windowNumber, 
                context: theEvent.context, 
                characters: "H", 
                charactersIgnoringModifiers: theEvent.charactersIgnoringModifiers!, 
                isARepeat: theEvent.ARepeat, 
                keyCode: theEvent.keyCode)
        super.sendEvent(newEvent!)
    } else {
        super.sendEvent(theEvent)
    }
}

}

3
répondu james_alvarez 2016-04-11 07:36:26