Feeds

Digging into Apple's hot-key mechanism

Go global

Securing Web Applications Made Simple and Scalable

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. ®

Bridging the IT gap between rising business demands and ageing tools

More from The Register

next story
NO MORE ALL CAPS and other pleasures of Visual Studio 14
Unpicking a packed preview that breaks down ASP.NET
Cheer up, Nokia fans. It can start making mobes again in 18 months
The real winner of the Nokia sale is *drumroll* ... Nokia
Mozilla fixes CRITICAL security holes in Firefox, urges v31 upgrade
Misc memory hazards 'could be exploited' - and guess what, one's a Javascript vuln
Put down that Oracle database patch: It could cost $23,000 per CPU
On-by-default INMEMORY tech a boon for developers ... as long as they can afford it
Google shows off new Chrome OS look
Athena springs full-grown from Chromium project's head
Apple: We'll unleash OS X Yosemite beta on the MASSES on 24 July
Starting today, regular fanbois will be guinea pigs, it tells Reg
HIDDEN packet sniffer spy tech in MILLIONS of iPhones, iPads – expert
Don't panic though – Apple's backdoor is not wide open to all, guru tells us
prev story

Whitepapers

Designing a Defense for Mobile Applications
Learn about the various considerations for defending mobile applications - from the application architecture itself to the myriad testing technologies.
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.
Top 8 considerations to enable and simplify mobility
In this whitepaper learn how to successfully add mobile capabilities simply and cost effectively.
Seven Steps to Software Security
Seven practical steps you can begin to take today to secure your applications and prevent the damages a successful cyber-attack can cause.
Boost IT visibility and business value
How building a great service catalog relieves pressure points and demonstrates the value of IT service management.