Carbon Event を使えば、ホットキーを登録できるぜ。ホットキーとは、自分のアプリケーションが前面に出ていなくても、受け取れるキーイベント。これを使えば、どこからでもショートカットキーでアプリケーションを呼び出せる。(この記事は、いいむらさんのホットキーに反応するを参考にさせてもらいました。)
ホットキーの登録、削除には、RegisterEventHotKey()、UnregisterEventHotKey() を使う。
HIToolbox/CarbonEvents.h
extern OSStatus
RegisterEventHotKey(
UInt32 inHotKeyCode,
UInt32 inHotKeyModifiers,
EventHotKeyID inHotKeyID,
EventTargetRef inTarget,
OptionBits inOptions,
EventHotKeyRef * outRef)
AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER;
extern OSStatus
UnregisterEventHotKey(EventHotKeyRef inHotKey)
AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER;
RegisterEventHotKey() では、ホットキーの virtual key code、修飾用のキー(Shift とか Command とか)を登録する。EventHotKeyID は、ホットキーごとにユニークな値を設定するようにするんだ。登録に成功すると EventHotKeyRef が返ってくるので、これは保持しておこう。UnregisterEventHotKey() は、その EventHotKeyRef とともに呼び出すんだ。
これで登録はできるけど、どうやって呼び出されるんだ?Carbon では InstallApplicationEventHandler() を使ったりするんだが、ここは Cocoa らしくいこう。NSApplication の sendEvent: メソッドの中で捕まえることができる。このメソッドには NSEvent が渡されてくるんだけど、その subtype が kEventHotKeyPressedSubtype だったら、ホットキーイベントだ。この記事は、Doing Carbon things in Cocoa を参考にしました。)
HotKeyApplication.m (sample)
// Undocumented event subtype
enum {
kEventHotKeyPressedSubtype = 6,
kEventHotKeyReleasedSubtype = 9,
};
- (void)sendEvent:(NSEvent*)event
{
// For hot key event
if ([event type] == NSSystemDefined &&
[event subtype] == kEventHotKeyPressedSubtype)
{
// Hot key is pressed!
これで、ホットキーを登録するのに必要な情報は全部そろった。では、サンプル。
NSApplication のサブクラス、HotKeyApplication を作ってみた。こいつが、ホットキーを登録するためのメソッドを提供する。まず登録用のメソッドは、registerHotKeyCode:withModifier:target:selector:。
HotKeyApplication.h (sample)
- (void*)registerHotKeyCode:(unsigned int)keyCode
withModifier:(unsigned int)keyModifier
target:(id)object
selector:(SEL)selector;
このメソッドの仕様は、まず keyCode は virtual key code を指定する。keyModifier は、修飾用のキー。NSShiftMask、NSControlKeyMask、NSCommandKeyMask、NSAlternateKeyMask の OR を指定してやる。object と selector は、ホットキーが押されたときに呼ばれるメソッドだ。登録が成功したら、EventHotKeyRef を返す。
続いて、削除するためのメソッド unregisterHotKey:。
HotKeyApplication.h (sample)
- (void)unregisterHotKey:(void*)hotKeyRef;
つくるときに得られた EventHotKeyRef を渡してやる。これで削除しよう。
という感じの仕様で実装する。そうすると、ソースコードはざっとこんな感じに。
HotKeyApplication.h (sample)
- (void*)registerHotKeyCode:(unsigned int)keyCode
withModifier:(unsigned int)keyModifier
target:(id)object
selector:(SEL)selector
{
static UInt32 _id = 0;
OSStatus status;
UInt32 modifier;
EventHotKeyID keyId;
EventHotKeyRef hotKeyRef;
int buf[2];
// Make key modifier
modifier = 0;
if (keyModifier & NSShiftKeyMask) { modifier |= shiftKey; }
if (keyModifier & NSControlKeyMask) { modifier |= controlKey; }
if (keyModifier & NSCommandKeyMask) { modifier |= cmdKey; }
if (keyModifier & NSAlternateKeyMask) { modifier |= optionKey; }
// Make hot key ID
keyId.signature = 'HtKe';
keyId.id = _id++;
// Register hot key
status = RegisterEventHotKey(
keyCode,
modifier,
keyId,
GetApplicationEventTarget(),
0,
&hotKeyRef);
if (status != noErr) {
return NULL;
}
// Set hot key reference into dictionary
buf[0] = (int)object;
buf[1] = (int)selector;
[_targetDict setObject:[NSData dataWithBytes:buf length:sizeof(buf)]
forKey:[NSNumber numberWithInt:(int)hotKeyRef]];
return (void*)hotKeyRef;
}
- (void)unregisterHotKey:(void*)hotKeyRef
{
OSStatus status;
// Unregister hot key
status = UnregisterEventHotKey(hotKeyRef);
// Remove hot key reference from dictionary
[_targetDict removeObjectForKey:
[NSNumber numberWithInt:(int)hotKeyRef]];
}
登録するときは、まず修飾キーのコードを変換する。続いて、ユニークな値になるように EventHotKeyID を作ってやる。signature としては、適当な値を使ってみた。RegisterEventHotKey() の呼び出しが成功すれば、EventHotKeyRef が得られるはず。これをキーにして、ターゲットの object と selector を保持しておこう。削除側のメソッドは簡単だね。
ホットキーが押されたときに呼び出される、sendEvent: の実装はこんな感じだ。
HotKeyApplication.h (sample)
- (void)sendEvent:(NSEvent*)event
{
// For hot key event
if ([event type] == NSSystemDefined &&
[event subtype] == kEventHotKeyPressedSubtype)
{
EventHotKeyRef hotKeyRef;
NSData* data;
// Get hot key refrence
hotKeyRef = (EventHotKeyRef)[event data1];
// Get object and selector
data = (NSData*)[_targetDict objectForKey:
[NSNumber numberWithInt:(int)hotKeyRef]];
if (data) {
int buf[2];
id object;
SEL selector;
[data getBytes:buf length:sizeof(buf)];
object = (id)buf[0];
selector = (SEL)buf[1];
if (object && selector) {
[object performSelector:selector];
}
}
}
[super sendEvent:event];
}
まず type と subtype を呼んでイベントをチェックする。ホットキーのイベントだったら、data1 を呼んでやる。これを呼ぶと、EventHotKeyRef を得ることができるんだ。それをキーにして、object と selector を取り出して、performSelector: してやればできあがりだ。
サンプルアプリケーションの実行画面は、下のような感じ。Key テキストフィールドに、'0' から '9' か、'A' から 'Z' の一字を入れて、Register ボタンを押せば、ホットキーが登録されるよ。アプリケーションをバックグラウンドに持っていたりして、実験してみてくれ。

(あまつぶ)
(Unsanity.org)
■関連リンク:
ホットキーに反応する(あまつぶ)
Doing Carbon things in Cocoa (Unsanity.org)
■サンプルダウンロード:
HotKey.tar.gz