あまつぶ

8.26【ホットキーに反応する】

 まずは昨日の続きから。
 9.2.1には、CarbonLib 1.4が入っているようだ。CarbonLib 1.4 SDKはダウンロードしてあるがまだ使用していない。Universal Interfacesの次のバージョンが出たらまとめて入れ替えようと思っていたのだが、いつになることやら……。

 CD-ROMドライブのイジェクトについては、Audio Strip GHというコントロールバー項目のパッケージの中に、「CD 取り出し GH」というものを発見。これだけを使うならフリーウェアということなので、しばらく使ってみることにしよう。
 コントロールバーといえば、他に、KKopenFolder CSMをインストール。これまで、システムフォルダ関係のフォルダはアップルメニューから選ぶかふぉるだ振り分けで該当するフォルダを選んでCmd+Fかを利用していたのだが、こちらの方がスマートだ。カスタマイズもできるようなので、そのうち試してみることにしよう。

 Xのキー配列については、検索してみたら、XFree86のdumpkeymapというコマンドのマニュアルページ(?)を見つけた。keymappingファイルの構造が解説してあるが、やはりかなり複雑だ。dumpkeymapというコマンドを使えば構造をテキストで表示してくれるようだが、Xにもそんなコマンドがあるんだろうか……?
 KCHRについては、Modifying the KCHR resources in OS Xというページを見つけた。キー配列をDvorak配列に変更する方法の説明のようだが、このページによると、調べていたのとはまったく違うフォルダにある、Localize.rsrcというファイルを変更している。このファイルには、データフォークにリソースが記録されていて、リソースフォークにデータを移すことによってResEditで確認することができた。確かに、KCHRリソースがある。
 このページの説明では、Developer Toolsに付属するDerezコマンドを使ってRezファイルに戻している。なるほど、こんなことができるのか。ResEditを立ち上げたくない時なんかに使えるかも知れない。
 もう一つ、気になることが。ページの最初のところに、keymappingを変更するためのツールがMacOS X Server 1.0に付属しているというようなことが書いてある。なぬ!?

 ひさしぶりに、新しいソフトをリリース(いや、リメイクなので新しくはないかも知れないが……)。WebColor_FKEYのX対応版とでもいうべき、WebColorX 1.0b1。
 XではFKEYが使えないようなので(インストールするような場所自体なさそうだし)、アプリケーションとしてリニューアル。kEventClassKeyboardクラスのCarbon Eventsに対応すればアプリケーションでも任意のキー入力を引っ掛けることができるみたいなので、早速挑戦してみる。
 CarbonEvents.hを見ると、イベントとして、kEventRawKeyDownなどが定義されている。中でも目を引くのは、kEventHotKeyPressedだ。ホットキーを押した時にイベントが送られてくるのに違いない。
 詳細は、ドキュメントをあたる……というのが鉄則なのだろうが、用意されているかどうかもわからないし、とりあえずヘッダファイルで調べてみる。もう少し下を見ると、kEventHotKeyPressedが送られてくる時には、kEventParamDirectObjectにtypeEventHotKeyIDなるデータが送られてくるらしいということがわかる。typeEventHotKeyIDの正体は、さらに下の方で'hkid'だと定義されている。その横には、EventHotKeyIDというデータ型らしきものがあり、今度はそれをさがすとこの構造体を定義しているところに辿り着く。そのすぐ下には、RegisterEventHotKeyという関数があるではないか。きっとこれだ。
 呼び出しに必要なものは、ホットキーにするキーコードとモディファイアキーの状態、ホットキーに割り当てるID(上記のEventHotKeyID型)、イベントのターゲット(EventTargetRef)、それにオプションだ。ターゲットに指定するものがなにかわからないが、EventTargetRefを検索すると、ウィンドウやコントロールをターゲットに指定するための関数が定義されたところが見つかる。その下に、GetApplicationEventTargetという関数を発見。ホットキーは、アプリケーションをターゲットにするものだろう。ということで、これに決まりだ。オプションは、今のところは使われていないということなので、0を指定する。
 さて、こちらもひさしぶりのソース掲載。

/* install carbon event handlers */
void InstallCarbonEventHandler(void)
{
  EventHandlerRef  eref;
  EventTypeSpec  list[] = {
                { kEventClassCommand, kEventCommandProcess },
                { kEventClassKeyboard, kEventHotKeyPressed },
              };
  EventHotKeyID  hkey = { kApplicationSignature, kMyHotKeyGetWebColor };
  OSStatus    status;
  
  
/* register hot key */
  /* OSStatus RegisterEventHotKey(
      UInt32      inHotKeyCode,    // virtual key code
      UInt32      inHotKeyModifiers, // modifiers
      EventHotKeyID   inHotKeyID,     // hot key id
      EventTargetRef  inTarget,      // GetApplicationEventTarget()
      OptionBits    inOptions,     // unused -> 0
      EventHotKeyRef  * outRef      // hot key reference
  ) */

  status = RegisterEventHotKey(0x1c, cmdKey+shiftKey, hkey,
    GetApplicationEventTarget(),
    0, &
gHotKeyRef);
  
  
/* install */
  InstallApplicationEventHandler(NewEventHandlerUPP
    (MyApplicationEventHandler),2,list,0,&eref);
}

/* my event handler (application) */
static pascal OSStatus MyApplicationEventHandler(EventHandlerCallRef myHandler,
    EventRef event,
void* userData)
{
  
#pragma unused(myHandler,userData)
  
unsigned long  ekind;
  HICommand    cmd;
  
long      cls;
  EventHotKeyID  hkey;
  
  cls = GetEventClass(event);
  ekind = GetEventKind(event);
  
  
switch(cls)
  {
    
case kEventClassCommand:
      
switch(ekind)
      {
        
case kEventCommandProcess:
          GetEventParameter( event,kEventParamDirectObject,
              typeHICommand,NULL,
              
sizeof(HICommand),NULL,&cmd );
          
if( DoMenu( cmd.commandID ) )
            
return( eventNotHandledErr );
          
else
            
return( noErr );
          
break;
      }
      
break;
    
    
case kEventClassKeyboard:
      
switch(ekind)
      {
        
case kEventHotKeyPressed:
          GetEventParameter( event,kEventParamDirectObject,
              typeEventHotKeyID,NULL,
              
sizeof(EventHotKeyID),NULL,&hkey );
          
/* for this application ? */
          
if (hkey.signature == kApplicationSignature)
          {
            
switch (hkey.id)
            {
              
case kMyHotKeyGetWebColor:
                WebColorXMain();
                
return noErr;
                
break;
            }
          }
          
break;
      }
      
break;
  }
  return( eventNotHandledErr );
}


 まず、RegisterEventHotKeyを使って、ホットキーを登録する。コマンド+シフト+8で呼び出すために、「8」のキーコード0x1cと、モディファイアキーの状態を使う。モディファイアキーの指定は、Event Managerで使っていたものと同じだ。EventHotKeyIDは、アプリケーションのシグネチャと、アプリケーション内でのIDを指定する。別にそうするように指定があるわけではないが、他のアプリケーションとの重複をさけるため、そうするのがいいと思う。アプリケーション内でのIDは、ホットキーがいくつかある時に区別するためのもの。ひとつだけなら、なんでもいいと思う(とりあえず1にした)。
 ホットキーの登録がうまくいくと、そのホットキーのリファレンスが返ってくる。これは、アプリケーション終了時に、UnregisterEventHotKeyでホットキーの登録を取り消すまで使わない。とりあえず、グローバル変数に入れておくことにする。
 ホットキーに対応するためには、kEventClassKeyboard暮らすのkEventHotKeyPressedイベントに対応する必要がある。そのためのハンドラをInstallApplicationEventHandlerを使って組み込む。イベントハンドラでは、クラスとイベントの種類を調べて、ホットキーのイベントであればGetEventParameterでホットキーのIDを調べる。ホットキーが、自分自身のものであれば、対応してnoErrを返す。違えば、eventNotHandledErrを返す。

 以上で、ホットキーの登録は終わりだ。あとは、RunApplicationEventLoopを呼ぶだけですべて自動的に処理される。ホットキーが押されれば、上記のイベントハンドラが呼ばれ、めでたく処理される。
 しかし、すべて勘で作ったのだが、なんの問題もなくあっさり動いてしまったのは驚いた。ショートカットを変更するための処理を追加するという課題はあるが、こんなに簡単に作ることができるなら、FKEYの類いは楽に移植できそうな感じだ。ただ、アプリケーションになっているため、Dockに表示されてしまうのが難点かな。なにかうまい方法はないものか……。

 ソース掲載で長くなったので、今回はこんなところ。

August 25, 2001 ↑ August index → September 9, 2001