Question

As an experiment, I am trying to achieve the following:

  • Let spacebar work as a modifier key - like the Shift key - where holding the spacebar key down and typing keys print different letters. Releasing the spacebar would set the state back to normal, and just pressing it behaves like a normal space key.

I was thinking of handling the keydown and keyup event, but apparently handleEvent:client: in IMKServerInput Protocol seems to only catch key down and mouse events.

Without much experience with cocoa, I’ve tried some methods with no success:

I reached to a conclusion that IMKit is only capable of passively receiving events. Since it is not an application, there is no keyUp: method to override - AFAIK, IMKit does not inherit NSResponder class.

Unfortunately cocoa is way too broad and has much less (or overflowed with non-helping) documentations for a novice like me to dive in.

Can anyone help me to the right direction?

Was it helpful?

Solution

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 an NSKeyUpMask to recognizedEvents: would not work.
  • addLocalMonitorForEventsMatchingMask:handler: and CGEventTapCreateForPSN 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.

OTHER TIPS

IMKStateSetting protocol's recognizedEvents: method should enable NSKeyUp events:

- (NSUInteger)recognizedEvents:(id)sender {
    return NSKeyDownMask | NSKeyUpMask;
}

A client calls this method to check whether an input method supports an event. The default implementation returns NSKeyDownMask.

However, in testing my Input Method still does not catch NSKeyUp events. This seems like a bug.

I've filed the following Radar, and hope others will duplicate it:

rdar://21376535

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top