HMDT - Logic and Intuition -

about HMDT

Cocoa Programming Tips 1001

With Carbon

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 が渡されてくるんだけど、その subtypekEventHotKeyPressedSubtype だったら、ホットキーイベントだ。この記事は、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 は、修飾用のキー。NSShiftMaskNSControlKeyMaskNSCommandKeyMaskNSAlternateKeyMask の OR を指定してやる。objectselector は、ホットキーが押されたときに呼ばれるメソッドだ。登録が成功したら、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 が得られるはず。これをキーにして、ターゲットの objectselector を保持しておこう。削除側のメソッドは簡単だね。

ホットキーが押されたときに呼び出される、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];
}

まず typesubtype を呼んでイベントをチェックする。ホットキーのイベントだったら、data1 を呼んでやる。これを呼ぶと、EventHotKeyRef を得ることができるんだ。それをキーにして、object と selector を取り出して、performSelector: してやればできあがりだ。

サンプルアプリケーションの実行画面は、下のような感じ。Key テキストフィールドに、'0' から '9' か、'A' から 'Z' の一字を入れて、Register ボタンを押せば、ホットキーが登録されるよ。アプリケーションをバックグラウンドに持っていたりして、実験してみてくれ。

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

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

back to top content

Copyright © 2002-2006 HMDT. All rights reserved.