X11: キーイベント

キーイベント
キーボード上のキーが押された時は KeyPress イベント、キーが離された時は KeyRelease イベントが来ます。

各キーの押し状態をまとめて取得したい時は、KeymapNotify イベントを処理します。

キーボードの入力イベントは、入力フォーカスがあるウィンドウに対してのみ送られてきます。
入力フォーカスがこのウィンドウに来た時は FocusIn イベント、入力フォーカスがこのウィンドウから離れた時は FocusOut イベントが来ます。
KeyPress/KeyRelease イベント
typedef struct {
  int            type;
  unsigned long  serial;
  Bool           send_event;
  Display        *display;
  Window         window;
  Window         root;
  Window         subwindow;
  Time           time;
  int            x, y;
  int            x_root, y_root;
  unsigned int   state;
  unsigned int   keycode;
  Bool           same_screen;
} XKeyEvent;

typedef XKeyEvent XKeyPressedEvent;
typedef XKeyEvent XKeyReleasedEvent;

キーボードのキーが押された時/離された時に来ます。

KeyPressMask, KeyReleaseMask のイベントマスクが必要です。
XEvent 構造体の xkey から参照できます。

基本的に、ポインタイベントの時と同じような値になります。

x,y 座標は、現在のポインタの位置です。このウィンドウ上にポインタがなくても、常に window の原点を基準とした相対位置になります。

keycode は、キーの物理番号です。
FocusIn/FocusOut イベント
typedef struct {
  int           type;
  unsigned long serial;
  Bool          send_event;
  Display       *display;
  Window        window;
  int           mode;
  int           detail;
} XFocusChangeEvent;

typedef XFocusChangeEvent XFocusInEvent;
typedef XFocusChangeEvent XFocusOutEvent;

入力フォーカスが変わった時に来ます。

FocusChangeMask イベントマスクが必要です。
XEvent 構造体の xfocus から参照できます。

modeNotifyNormal (0) : 通常イベント
NotifyGrab (1) : キーボードグラブが開始されたときのフォーカスイベント
NotifyUngrab (2) : キーボードグラブが解放されたときのフォーカスイベント
NotifyWhileGrabbed (3) : キーボードグラブ中のフォーカスイベント
detailフォーカスが変化したウィンドウの関係を表す。

NotifyAncestor, NotifyVirtual, NotifyInferior, NotifyNonlinear, NotifyNonlinearVirtual, NotifyPointer, NotifyPointerRoot, NotifyDetailNone
KeymapNotify イベント
typedef struct {
  int            type;
  unsigned long  serial;
  Bool           send_event;
  Display        *display;
  Window         window;
  char           key_vector[32];
} XKeymapEvent;

すべての物理キーごとの押し状態が送られてきます。
EnterNotify および FocusIn イベントの直後に、このイベントが生成されます。

KeymapStateMask イベントマスクが必要です。
XEvent 構造体の xkeymap から参照できます。

key_vector は、256 個の物理キーの押し状態のフラグです。
[0] の下位ビットから順に並んでいます。

※各キーの押し状態が変化した時には来ません。常に現在の押し状態のデータを維持したい場合、このイベントの後、KeyPress/KeyRelease を自分で処理して、フラグを更新する必要があります。

XQueryKeymap 関数を使って、現在のデータを取得することも出来ます。

vpod XQueryKeymap(Display *display, char keys_return[32]);
プログラム
キーイベントを確認するプログラムです。
ウィンドウに入力フォーカスがある状態で、キーを押してみてください。

ウィンドウ内でポインタのボタンを押すと終了します。

<13-key.c>
#include <stdio.h>
#include <X11/Xlib.h>
#include "util.h"

int main(int argc,char **argv)
{
    Display *disp;
    XSetWindowAttributes attr;
    Window win;
    XEvent ev;
    
    disp = XOpenDisplay(NULL);
    if(!disp) return 1;

    //ウィンドウ作成

    attr.background_pixel = 0;
    attr.event_mask = ButtonPressMask | KeymapStateMask
        | KeyPressMask | KeyReleaseMask | FocusChangeMask;

    win = XCreateWindow(disp, DefaultRootWindow(disp),
        0, 0, 200, 200, 0,
        CopyFromParent, CopyFromParent, CopyFromParent,
        CWBackPixel | CWEventMask, &attr);

    XMapWindow(disp, win);

    //イベント

    while(1)
    {
        XNextEvent(disp, &ev);

        switch(ev.type)
        {
            case KeyPress:
            case KeyRelease:
                printf("- [Key%s] x(%d) y(%d) keycode(%u)\n",
                    (ev.type == KeyPress)? "Press": "Release",
                    ev.xkey.x, ev.xkey.y, ev.xkey.keycode);

                put_state(ev.xkey.state);
                fflush(stdout);
                break;
            case FocusIn:
            case FocusOut:
                printf("* [Focus%s] mode(%d) detail(%d)\n",
                    (ev.type == FocusIn)? "In": "Out",
                    ev.xfocus.mode, ev.xfocus.detail);
                fflush(stdout);
                break;
            case KeymapNotify:
                printf("# [Keymap]\n");
                fflush(stdout);
                break;
            case ButtonPress:
                goto END;
        }
    }

END:
    XCloseDisplay(disp);

    return 0;
}