Modify NSEvent to send a different key than the one that was pressed
Solution 1:
You can't do this with the NSEvent
API, but you can do this with a CGEventTap
. You can create an active event tap and register a callback that receives a CGEventRef
and can modify it (if necessary) and return it to modify the actual event stream.
EDIT
Here's a simple program that, while running, replaces every "b" keystroke with a "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);
}
(Funny story: as I was editing this post, I kept on trying to write "replaces every 'b' keystroke", but it kept on coming out as "replaces every 'v' keystroke". I was confused. Then I remembered that I hadn't stopped the app yet.)
Solution 2:
I happened across this answer, needing to do the same but only for events within my own application not global . There is a much simpler solution, for this much simpler problem, which I am noting here incase it's useful for anyone else:
- I intercepted the event at the window, by creating an override for sendEvent:. I then check for key events (KeyUp or KeyDown) and then simply create a new event using nearly all the data from the prevous event, then call NSWindow superclass with this event instead.
This seems to work perfectly for me and I didn't have to even modify the keyCode part - but maybe this could be an issue...
Example in 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)
}
}
}