5
votes

I'm having a problem with OS X detecting keystrokes. I need to detect a key down AND a key release or key up whenever a keyboard key is pressed. This is fairly straight-forward when intercepting the application's event handling chain with [ NSEvent addLocalMonitorForEventsMatchingMask: handler: ]. That lets you intercept and modify NSEvents for a variety of event types, including NSKeyUp and NSKeyDown for the regular printing keys, as well as NSFlagsChanged which can be used to detect the shift, ctrl, alt, and cmd keys. In fact, because the modifier flags change on both the key up and key down for shift, ctrl, alt, and cmd keys, NSFlagsChanged can be used as a key up and key down event for those keys by checking the [NSEvent modifierFlags] along with the [NSEvent keyCode].

Capslock is different, though. Because the capslock modifier really only acts on a key down, when you press capslock you only get a NSFlagsChanged with capslock is pressed, not when it's released. And NSKeyUp and NSKeyDown aren't emitted with modifier keys like capslock and shift and ctrl, etc.

Can anyone suggest a way, maybe even a lower-level interface, for getting capslock key up events? Am I going to have to resort to using kqueues or something?

1
The first thing I'd try is to see if CGEventTap does something different from NSEvent here. Unlike most of Cocoa, NSEvent isn't just a simple bridge over the lower-level equivalent. Also, even if all else fails, you don't have to go all the way to kqueues; you could use IOHIDLib. (Even without really learning the IOHIDLib model, you could just get triggered on each HID event and then check the capslock state at the Cocoa level…)abarnert
I just tried it, and unfortunately, I'm getting the same result with CGEventTap - capslock is kCGEventFlagsChanged rather than kCGEventKeyDown and kCGEventKeyUp. And of course kCGEventFlagsChanged is only emitted with capslock is pressed, not released. Worse, CGEventTap seems to require either root privileges or "enabling assistive access", even though I was only interested in keyboard events in my own process. I'll try IOHIDLib next.Ted Middleton
Yeah, if kCGEventFlagsChanged doesn't give you a released, it looks like IOHIDLib is the answer. Check out the samples that come with Xcode and, when you really get stuck, the source (IOHIDFamily); IIRC, when they were a lot more useful to me than the documentation.abarnert
I'd really like to know if you've found a solution for this problem.rdb
Yup - see the answer I just posted.Ted Middleton

1 Answers

3
votes

IOHIDLib seems to be the only way of doing this. I (or a teammate actually) used IOHIDManager to set up an event callback that successfully intercepted capslock key-up and key-down events.

The awful thing about this is that this is completely outside the Cocoa/CoreFoundation event dispatch mechanism, which means that you get events even when your application is out of focus. You end up having to do a lot of window management and focus detection yourself.

But it does work.