Modifique NSEvent para enviar una clave diferente a la que se presionó

Estoy tratando de crear un gancho de teclado OS X para fines de tecnología de asistencia (es decir, no se preocupe, no es un keylogger).

Cuando un usuario presiona una tecla, quiero evitar la pulsación de tecla real y enviar una falsa pulsación de tecla (carácter de mi elección).

Tengo el siguiente código:

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

¿Alguna ayuda para lograr cualquiera de esos objectives? Modifica básicamente el “evento de teclado” NSEvent para enviar un personaje diferente. Gracias.

    No puede hacer esto con la API NSEvent , pero puede hacerlo con CGEventTap . Puede crear un evento activo y registrar una callback que recibe un CGEventRef y puede modificarlo (si es necesario) y devolverlo para modificar el flujo de eventos real.


    EDITAR

    Aquí hay un progtwig simple que, mientras se ejecuta, reemplaza cada tecla “b” con una “v”:

     #import  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); } 

    (Historia divertida: mientras editaba esta publicación, seguí intentando escribir “reemplaza cada tecla ‘b'”, pero seguía apareciendo como “reemplaza cada tecla ‘v'”. Estaba confundido. Entonces recordé eso No había detenido la aplicación todavía).

    Pasé por esta respuesta, necesitaba hacer lo mismo pero solo para eventos dentro de mi propia aplicación no global. Hay una solución mucho más simple, para este problema mucho más simple, que estoy haciendo aquí en caso de que sea útil para cualquier otra persona:

    • Intercepté el evento en la ventana, creando una anulación para sendEvent :. Luego verifico los eventos clave (KeyUp o KeyDown) y luego simplemente creo un nuevo evento usando casi todos los datos del evento prevous, luego llamo a la superclase NSWindow con este evento en su lugar.

    Esto parece funcionar perfectamente para mí y no tuve que modificar la parte de keyCode, pero tal vez esto podría ser un problema …

    Ejemplo en 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) } } 

    }