I tried all possible alternatives one by one, and eventually achieved it by creating a global EventTap
with CGEventTap
.
The code basically looks like this:
// Create an event tap.
CGEventMask eventMask = ((1 << kCGEventKeyDown) | (1 << kCGEventKeyUp));
CFMachPortRef eventTap = CGEventTapCreate(kCGSessionEventTap,
kCGHeadInsertEventTap,
0,
eventMask,
myCGEventCallback,
NULL);
if (!eventTap) {
NSLog(@"failed to create event tap\n");
return NO;
} else {
// Create a run loop source.
runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
// Add to the current run loop.
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
// Enable the event tap.
CGEventTapEnable(eventTap, true);
return YES;
}
where myCGEventCallback
handles the global states.
Meanwhile here are some of what I've found out:
- According to The Key-Input Message Sequence document, the application only passes the keydown event to the Input Method Kit, after trying other bunch of handlers in the chain. You cannot let
IMKServerInput
'catch' the NSKeyUp event. Just adding anNSKeyUpMask
torecognizedEvents:
would not work. addLocalMonitorForEventsMatchingMask:handler:
andCGEventTapCreateForPSN
would not catch the event. I suppose this is because though an Input Method may run as a separate process, the event itself is fired from the application, like TextEdit, and handed over to the Input `Method.IOHIDManager:
is for adding new hardware devices and making drivers.- Creating a global EventTap requires either running the process with sudo privilege -- copying the Input Method to
/Library/Input Methods
does not run with sudo privilege --, or registering the application to the Accessibility control. That's in System Preferences → Security & Privacy → Privacy tab → Accessibility, in Mavericks.