LECTURE 10 キーボード入力処理の方法

○ はじめに

LECTURE 9 のマウス操作に関するイベントに続いて、もう 1 つの重要なユーザ入力であるキーボード操作に関するイベントについて説明します。

キーボード操作に関するイベントには、ユーザがキーを押した場合( KeyPress イベント)、離した場合( KeyRelease イベント)の 2 種類があります。また、キーイベントそのものではありませんが関連するイベントとして MappingNotify というイベントがあります。ここでは

の順に説明していきます。

○ キーイベント

ボタンイベントの時と同様に、ユーザがキーを押した場合には KeyPress イベントが発生し、離した場合には KeyRelease イベントが発生します。これらのイベントをキーイベントと呼びます。キーイベントに関する情報を格納するためのキーイベント構造体を以下に示します。

typedef struct
{
  int           type;           /* イベントの種類 */
  unsigned long serial;         /* サーバが処理した最後のリクエストの番号 */
  Bool          send_event;     /* クライアントから送られたイベントならTrue */
  Display*      display;        /* イベントが読み込まれたdisplay */
  Window        window;         /* イベントが発生したウィンドウのID */
  Window        root;           /* イベントが起こったウィンドウのID */
  Window        subwindow;      /* windowの子ウィンドウID */
  Time          time;           /* イベント発生時刻 */
  int           x, y;           /* イベントが起こった座標(window内) */
  int           x_root, y_root; /* イベントが起こった座標(root内) */
  unsigned int  state;          /* 押されていたボタンや修飾キー */
  unsigned int  keycode;        /* pressあるいはreleaseされたキーのコード */
  Bool          same_screen;    /* マウスカーソルがwindowと同スクリーンにあればTrue */
} XKeyEvent;

typedef XKeyEvent XKeyPressedEvent;
typedef XKeyEvent XKeyReleasedEvent;

type には 定数 KeyPress か KeyRelease のいずれかが入ります。x, yにはイベントが発生したときのマウスカーソルの位置座標が入ります。

ユーザが押したり離したりしたキーの情報として、キーコード番号( keycode )が送られて来ます。このキーコード番号はキーに一意に割り付けられたハードウェア依存の番号です。通常のプログラムでは、このキーコード番号を KeySym 名に変換して KeySym 名を用います。

○ オートリピート

X サーバのキー設定がオートリピートになっている場合、オートリピート中のキーは KeyPress イベントと KeyRelease イベントが交互に送られて来ます。この様な場合にキーを押しっぱなしであると判定したい場合は、オートリピートを解除する必要があるかもしれません。X クライアントからオートリピート設定を操作するには、以下のいずれかを使用します。

  • XAutoRepeatOn/XAutoRepeatOff を使う
      XAutoRepeatOn( Display* dpy );
      XAutoRepeatOff( Display* dpy );

    それぞれ、接続した X サーバのキーボードのオートリピートを有効/無効にします。 この場合、指定キーのみのオートリピート設定を操作することは出来ません。

  • XChangeKeyboardControl を使う
      XChangeKeyboardControl( 
          Display* dpy, unsigned long value_mask, XKeyboardControl* values );
      
      value_mask : values のどのメンバが有効な設定値であるかをマスク指定
      values     : value_mask に設定されているマスクに対応したメンバの指定値を設定
      

    この関数では

    • キークリック音量
    • ベル音量
    • ベル音調
    • ベル鳴動時間
    • LED 状態
    • オートリピート状態

    を設定することが出来ます。ここではオートリピート状態のみに注目します。 オートリピート状態の設定に必要な XKeyboardControl のメンバは int key と int auto_repeat_mode です。 key と auto_repeat_mode の関係は、下表の通りです。

    key auto_repeat_mode 効果
    未設定 未設定 効果無し
    未設定 設定 キーボード全体に対するオートリピート設定
    設定 未設定 BadMatch
    設定 設定 指定キーに対するオートリピート設定

    key には恐らくキーコードを指定するのだと思うのですが、自分は実際には使ったことが無いので未調査です。。。(ごめんなさい)auto_repeat_mode に設定できる値は以下の通りです。

    定数名 説明
    AutoRepeatModeOn 有効
    AutoRepeatModeOff 無効
    AutoRepeatModeDefault デフォルト実装に従う

    valuemask には、key を指定する場合には KBKey、auto_repeat_mode の場合は KBAutoRepeatMode を設定します ( 例:valuemask = KBKey | KBAutoRepeatMode; )。

以上はオートリピート状態を設定するための関数ですが、恐らく通常はこれらを呼び出す前の状態を保存しておいて、不要になった時点で元の設定に戻したいだろうと思います。現在のキーボードの制御値を取得するには、XGetKeyboardControl を使います。

XGetKeyboardControl( Display* dpy, XKeyboardState* values_return );

values_return : 現在の制御値

XKeyboardState のメンバで、オートリピートに関係するメンバは int global_auto_repeat および char auto_repeats[32] です。global_auto_repeat には前述の AutoRepeatModeOn あるいは AutoRepeatModeOff が格納されます。auto_repeats にはキーに対応したビットにオートリピート設定が格納されます。auto_repeats については、自分は実際に使ったことが無いので未調査です、再度ごめんなさい。

○ キーコードの変換

押された(離された)キーの情報として X サーバから送られて来るのはキーコード番号です。キーコード番号はハードウェアに依存した番号であり、メーカによって様々な値となります。どのキーが押されたかをプログラム内で処理する場合には、通常はキーコード番号を X ウィンドウシステムで一意に定められている KeySym 名に変換して処理します。

キーコード番号を KeySym 名に変換するには XLookupString() を使います。

#include <Xutil.h>

int XLookupString( XKeyEvent*      event, 
                   char*           buffer, 
                   int             nbuffer, 
                   KeySym*         keysym, 
                   XComposeStatus* status 
                 );

event   : キーボードイベント
buffer  : ASCIIに変換した文字列をいれるバッファ
nbuffer : bufferのサイズ( バイト数 )
keysym  : KeySymを返す。いらない場合は NULLを指定
status  : キー構成の状態情報。いらない場合は NULL を指定

この関数は、event で指定したキーイベント情報を使って KeySym 名と ASCII に変換します。関数の返り値は buffer に格納された文字数です。キーコード番号から KeySym 名への変換は、キーコードと KeySym 名の対応表を用いて行われます。この対応表は X サーバとの接続時に X サーバからコピーされます。

主な KeySym 名(僕の独断と偏見で選んだ KeySym 名であって、全てではありません)を下表に示します。全ての KeySym 名については、<X11/keysym.h> を参照してください。

KeySym 名 キー
XK_space スペース
XK_Escape Esc
XK_Control_L, RCTRL左、右
XK_Alt_L, R ALT左、右
XK_Delete Del
XK_Insert Ins
XK_Left
XK_Right
XK_Up
XK_Down
XK_F1〜XK_F10 F1〜F10
XK_exclam !
XK_at @
XK_0 〜 XK_9 0〜9
XK_A〜XK_Z A〜Z
XK_a〜XK_z a〜z

○ MappingNotify イベント

キーコードの変換の所で、キーコードと KeySym 名の対応表を X サーバからコピーしてくると説明しました。ところで、この対応表は常に一定ではなく、X のクライアントである xmodmap などによってキーボードのマッピングが変えられることがあります。マッピングが変わってしまうと、キーコードを KeySym 名に変換して使っているプログラムは正しく動作しなくなってしまうので、新しい対応表を X サーバからもらってこなければなりません。

このようにキーボードのマッピングが変わった時には、X サーバから MappingNotify イベントが送られて来ます。このイベントは XSelectInput() で指定しなくても無条件に全てのアプリケーションに送られて来ます(このようなイベントをノンマスカブルイベントと呼びます)。よって、MappingNotify イベントを受け取ったら X サーバに問い合わせて最新の対応表をもらってくるようにコーディングしておけば良いというわけです。

持っている対応表を最新にするには、XRefreshKeyboardMapping() を使います。この関数を実行するだけで最新の対応表がコピーされます。

switch ( event.type )
{
  case MappingNotify : 

    XRefreshKeyboardMapping( &event );
    break;
}