Feeds

Digging into Apple's hot-key mechanism

Go global

The essential guide to IT transformation

Mac Secrets Before Christmas, I explained how to use an undocumented class within PreferencePanes.framework to easily convert key codes and modifiers into human readable string representations.

This time, I make good on my promise to describe the global hot-key mechanism used by Apple software such as Universal Access, Expose, and Dashboard.

For Cocoa developers, a number of different open source hot-key implementations can be found on the internet, one of the most popular being a set of classes developed by (I think!) Quentin Carnicelli. Type "KeyCombo.m" into Google, and you'll soon find an example.

I was never really happy with this stuff, partly because you end up adding several extra classes and source files to your application. It seemed unnecessarily complex to me. In the end, I wrote my own implementation by sub-classing NSApplication and adding just a few methods to that.

However, what had always intrigued me was the built-in hot-key mechanism used by Universal Access, Expose, and so on. Two months ago, I hadn't figured out enough of the details to make this useful, but now things are a lot clearer. The real hot-key mechanism appears to be implemented at a very low-level, probably inside the window server process. Code inside CoreGraphics.framework implements a symbolic hot-key API, which communicates directly with the windows server through Mach IPC calls.

Why "symbolic"? As I mentioned last time, Apple's global hot keys assign a unique, system-wide number to each hot key. Thus, the number eight identifies the hot key that moves focus to the Dock (normally Control-F3). The user might change the actual key-code and modifiers used for the hot-key, but it will always be identified as symbolic hot key eight within the operating system.

Suppose we want to use hot-key number 144, which in Tiger and Leopard isn't currently assigned or enabled. First of all, we'll want to enable the hot key as below: This tells the windows server that the hot key is in use.

CGSSetSymbolicHotKeyEnabled (144, YES);

Next, tell the window server to send notifications for this hot key to our application:

CGSSetSymbolicHotKey (CGSMainConnectionID(), 999, 144);

You may already be familiar with CGSMainConnectionID. If not, suffice to say that it returns a connection ID used to communicate between your application and the window server. This is needed for the call. The second parameter provides an internal, application-specific identifier by which the hot key should be known. We'll see how this works in just a moment.

By default, symbolic hot key 144 corresponds to Control-Option-1 ("1" on the number keypad). Once you've executed the above two statements, the Windows server will start sending events to your app anytime this key combination is pressed. These events can be easily intercepted by sub-classing NSApplication and overriding the sendEvent method as shown below:

- (void) sendEvent: (NSEvent *) event
{
        if ([event type] == NSSystemDefined && 
      [event subtype] == 6 && 
      [event data1] == 999) 
        {
                NSLog (@"Voila!!!");
                return;
        }
        
        [super sendEvent: event];
}

There are a couple of things to note here. First, the hot-key events are received as NSSystemDefined events, not as regular key-up or key-down events. Second, notice the use of the event's subtype field in the above code: A value of six indicates that the hot key has been pressed, whereas nine would indicate that it's been released.

You'll also see that we check the value of the data1 field. This should correspond to the arbitrary value (999) that we used earlier in the call to CGSSetSymbolicHotKey. Because sendEvent: never gets to "see" the symbolic hot-key number (144 in this case), we can use the data1 field to easily identify, which hot key has been pressed. This is obviously essential for an application that uses more than one.

Since this value is completely arbitrary, you could get sneaky and store something like an NSInvocation instance into the data1 field. That way, the hot key processing code inside sendEvent: could be pretty much self contained and could invoke your target method without needing to know about any other classes in your app.

Symbolic keys in OS X

Proceed with caution: undocumented calls can change all these

Cool though this stuff is, it wouldn't be a great deal of use if you couldn't actually change the key code and modifier mask of the hot key or keys you're using. This can be done through a couple of calls as shown below:

CGSGetSymbolicHotKeyValue
CGSSetSymbolicHotKeyValue

For the full function prototypes, consult the SymbolicKeys.h file included with this month's project, below. These routines allow you to interrogate the current key combination and change it to something as specified by the user of your application. Note that when calling CGSSetSymbolicHotKeyValue, you should generally use 0xffff for the event flags parameter.

But what about persistence, I hear you say. The Core Graphics routines described above only affect OS X's current state. To persistently change a symbolic hot-key, you'll need to modify the dictionary in the "com.apple.symbolichotkeys" domain. A small program showing how to access this dictionary was included in the earlier article.

For full source code and demo project click here. ®

Secure remote control for conventional and virtual desktops

More from The Register

next story
BBC: We're going to slip CODING into kids' TV
Pureed-carrot-in-ice cream C++ surprise
China: You, Microsoft. Office-Windows 'compatibility'. You have 20 days to explain
Told to cough up more details as antitrust probe goes deeper
Linux turns 23 and Linus Torvalds celebrates as only he can
No, not with swearing, but by controlling the release cycle
Scratched PC-dispatch patch patched, hatched in batch rematch
Windows security update fixed after triggering blue screens (and screams) of death
This is how I set about making a fortune with my own startup
Would you leave your well-paid job to chase your dream?
prev story

Whitepapers

Endpoint data privacy in the cloud is easier than you think
Innovations in encryption and storage resolve issues of data privacy and key requirements for companies to look for in a solution.
Implementing global e-invoicing with guaranteed legal certainty
Explaining the role local tax compliance plays in successful supply chain management and e-business and how leading global brands are addressing this.
Advanced data protection for your virtualized environments
Find a natural fit for optimizing protection for the often resource-constrained data protection process found in virtual environments.
Boost IT visibility and business value
How building a great service catalog relieves pressure points and demonstrates the value of IT service management.
Next gen security for virtualised datacentres
Legacy security solutions are inefficient due to the architectural differences between physical and virtual environments.