carbon event

Carbon Event

With Carbon - Carbon Event

ホットキーを登録する

Keywords: RegisterEventHotKey(), UnregisterEventHotKey()

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 ボタンを押せば、ホットキーが登録されるよ。アプリケーションをバックグラウンドに持っていたりして、実験してみてくれ。

hotKey.jpg

■関連リンク:
ホットキーに反応する(あまつぶ)
Doing Carbon things in Cocoa (Unsanity.org)

■サンプルダウンロード:
HotKey.tar