X11: イベント

イベントについて
クライアントが X 関数を使って、X サーバーに対して何らかの要求をした場合、そのリクエストはまず、Xlib 内の出力バッファに追加されます。

クライアントが XFlush 関数 や、XFlush を内部で呼び出す XNextEvent などの関数を実行した場合、出力バッファはフラッシュされ、X サーバーにまとめて送信されます。
X サーバーはそのリクエストを受信した後、実際にリクエストを処理していきます。

X サーバーが受信したリクエストを処理した後、クライアントに対して、何らかのイベント通知が必要になる場合は、そのイベントを、クライアントのイベントキューに追加します。

クライアントは、イベントループ内で XNextEvent 関数などを使うことで、キューにあるイベントを順次読み込み、処理していきます。
イベントマスク
ウィンドウが各イベントを受け取るためには、まず、ウィンドウに対して、必要なイベントのマスクを設定する必要があります。
これを設定していないと、ほとんどのイベントは送られてきません。
(一部のイベントは、イベントマスクに関係なく、常に送られてきます)

ウィンドウの作成時に、XSetWindowAttributes 構造体の event_mask メンバで指定するか、XSelectInput 関数でイベントマスクを変更することができます。
XSelectInput 関数
void XSelectInput(Display *display, Window w, long event_mask);

指定ウィンドウのイベントマスクを変更 (上書き) します。
イベントマスクの値
NoEventMaskイベントマスクなし
KeyPressMaskキーが押された時
KeyReleaseMaskキーが離された時
ButtonPressMaskポインタボタンが押された時
ButtonReleaseMaskポインタボタンが離された時
EnterWindowMaskポインタがウィンドウ上で表示される状態になった
LeaveWindowMaskポインタがウィンドウ上に表示されなくなった
PointerMotionMaskポインタの位置が移動した時 (ボタンが押されていない場合)
PointerMotionHintMask特定のタイミングでのみ、ポインタ移動のイベントを送る。

他の Motion マスクと共に選択されている場合、次のいずれかが行われた時のみ、MotionNotify イベントが1つだけ送られる。
(この時、XMotionEvent 構造体の is_hint メンバーは、NotifyHint に設定されている)

・キーまたはボタンの状態が変化した時。
・ポインタがウィンドウ上で表示された時。
・クライアントが XQueryPointer を呼び出して、ポインタの位置を参照した時。
Button1MotionMaskボタン1が押されている間のポインタ移動
Button2MotionMaskボタン2〃
Button3MotionMaskボタン3〃
Button4MotionMaskボタン4〃
Button5MotionMaskボタン5〃
ButtonMotionMask1つ以上のボタンが押されている間のポインタ移動
KeymapStateMask各キーの押し状態の取得
ExposureMask描画関連
VisibilityChangeMaskウィンドウの可視性が変化した時
StructureNotifyMaskウィンドウの構造が変化した時
ResizeRedirectMaskウィンドウのサイズ変更をリダイレクトする
SubstructureNotifyMask子ウィンドウの構成が変化した時
SubstructureRedirectMask子ウィンドウの構成変更をリダイレクトする
FocusChangeMask入力フォーカスの変更時
PropertyChangeMaskプロパティの変更時
ColormapChangeMaskカラーマップの変更時
OwnerGrabButtonMaskこのマスクがある場合、ボタンが押された時に、X サーバーによって行われる自動グラブについて、XGrabPointer() の owner_events 引数を True に指定してグラブする。
グラブ中、このクライアントがポインタイベントを処理する対象の場合は通常通りのイベントとして送り、他のクライアントが対象となるポインタイベントであれば、グラブウィンドウに送る。
イベントの構造体
イベントは、XEvent 共用体で情報を取得できます。

typedef union _XEvent {
 int                        type;
 XAnyEvent                  xany;
 XKeyEvent                  xkey;
 XButtonEvent               xbutton;
 XMotionEvent               xmotion;
 XCrossingEvent             xcrossing;
 XFocusChangeEvent          xfocus;
 XExposeEvent               xexpose;
 XGraphicsExposeEvent       xgraphicsexpose;
 XNoExposeEvent             xnoexpose;
 XVisibilityEvent           xvisibility;
 XCreateWindowEvent         xcreatewindow;
 XDestroyWindowEvent        xdestroywindow;
 XUnmapEvent                xunmap;
 XMapEvent                  xmap;
 XMapRequestEvent           xmaprequest;
 XReparentEvent             xreparent;
 XConfigureEvent            xconfigure;
 XGravityEvent              xgravity;
 XResizeRequestEvent        xresizerequest;
 XConfigureRequestEvent     xconfigurerequest;
 XCirculateEvent            xcirculate;
 XCirculateRequestEvent     xcirculaterequest;
 XPropertyEvent             xproperty;
 XSelectionClearEvent       xselectionclear;
 XSelectionRequestEvent     xselectionrequest;
 XSelectionEvent            xselection;
 XColormapEvent             xcolormap;
 XClientMessageEvent        xclient;
 XMappingEvent              xmapping;
 XErrorEvent                xerror;
 XKeymapEvent               xkeymap;
 long                       pad[24];
} XEvent;

typedef struct {
 int           type;
 unsigned long serial;
 Bool          send_event;
 Display       *display;
 Window        window;
} XAnyEvent;

XEvent はこのように、各イベントタイプ別の構造体がすべて収まるような共用体になっているので、実際に使う時は、xev.xbutton.button というような形で参照するか、XEvent のポインタを各構造体のポインタに型変換して使います。

XAnyEvent は、すべてのイベントに含まれている先頭の値が定義されています。

typeイベントのタイプ
serialプロトコルのシリアル番号
send_eventXSendEvent 関数によって、他のクライアントから直接送られたイベントか
displayイベントを読み込んだディスプレイ
windowイベントが送られたウィンドウ
マスクとイベントタイプと構造体
各イベントマスクに対応するイベントタイプ、そのイベントの構造体、そして各イベントで共通して使える構造体のリストは、以下になります。

※ClientMessage、SelectionClear、SelectionRequest、SelectionNotify、MappingNotify イベントには、マスク値がないため、常に送られてきます。

イベントマスクイベントタイプ構造体汎用構造体
ButtonMotionMask
Button1MotionMask
Button2MotionMask
Button3MotionMask
Button4MotionMask
Button5MotionMask
MotionNotifyXPointerMovedEventXMotionEvent
ButtonPressMaskButtonPressXButtonPressedEventXButtonEvent
ButtonReleaseMaskButtonReleaseXButtonReleasedEventXButtonEvent
ColormapChangeMaskColormapNotifyXColormapEvent-
EnterWindowMaskEnterNotifyXEnterWindowEventXCrossingEvent
LeaveWindowMaskLeaveNotifyXLeaveWindowEventXCrossingEvent
ExposureMaskExposeXExposeEvent-
グラフィック関数によるイベントGraphicsExpose
NoExpose
XGraphicsExposeEvent
XNoExposeEvent
-
FocusChangeMaskFocusIn
FocusOut
XFocusInEvent
XFocusOutEvent
XFocusChangeEvent
KeymapStateMaskKeymapNotifyXKeymapEvent-
KeyPressMaskKeyPressXKeyPressedEventXKeyEvent
KeyReleaseMaskKeyReleaseXKeyReleasedEventXKeyEvent
OwnerGrabButtonMask---
PointerMotionMaskMotionNotifyXPointerMovedEventXMotionEvent
PointerMotionHintMask---
PropertyChangeMaskPropertyNotifyXPropertyEvent-
ResizeRedirectMaskResizeRequestXResizeRequestEvent-
StructureNotifyMaskCirculateNotify
ConfigureNotify
DestroyNotify
GravityNotify
MapNotify
ReparentNotify
UnmapNotify
XCirculateEvent
XConfigureEvent
XDestroyWindowEvent
XGravityEvent
XMapEvent
XReparentEvent
XUnmapEvent
-
SubstructureNotifyMaskCirculateNotify
ConfigureNotify
CreateNotify
DestroyNotify
GravityNotify
MapNotify
ReparentNotify
UnmapNotify
XCirculateEvent
XConfigureEvent
XCreateWindowEvent
XDestroyWindowEvent
XGravityEvent
XMapEvent
XReparentEvent
XUnmapEvent
-
SubstructureRedirectMaskCirculateRequest
ConfigureRequest
MapRequest
XCirculateRequestEvent
XConfigureRequestEvent
XMapRequestEvent
-
-ClientMessageXClientMessageEvent-
-MappingNotifyXMappingEvent-
-SelectionClearXSelectionClearEvent-
-SelectionNotifyXSelectionEvent-
-SelectionRequestXSelectionRequestEvent-
VisibilityChangeMaskVisibilityNotifyXVisibilityEvent-
イベントの読み込み関数
イベントキューからイベントを読み込む場合は、以下の関数を使います。

キューにイベントがない場合や、条件に一致するイベントがない場合、自動で出力バッファがフラッシュされます。

イベントが来るまで処理をブロックする関数については、イベントを受け取るまで関数が戻らないので、注意してください。

以下以外にも、イベントの読み込み関数はあります。
キュー内のイベントの数
int XPending(Display *display);

キューにあるイベントの数を返します。
キューにイベントがない場合は、出力バッファをフラッシュした後、さらにイベントを読み込み、その数を返します。

0 であれば、現在処理できるイベントはないということです。
先頭から読み込み
void XNextEvent(Display *display, XEvent *event_return);
void XPeekEvent(Display *display, XEvent *event_return);

キューの最初のイベントをコピーして返します。
キューが空の場合、出力バッファをフラッシュし、イベントが受信されるまでブロックします。

XNextEvent は、キューからイベントを削除します。
XPeekEvent は、キューからイベントを削除しません。
条件一致
void XIfEvent(Display *display, XEvent *event_return, Bool (*predicate)(), XPointer arg);
Bool XCheckIfEvent(Display *display, XEvent *event_return, Bool (*predicate)(), XPointer arg);
void XPeekIfEvent(Display *display, XEvent *event_return, Bool (*predicate)(), XPointer arg);

//比較関数
Bool func(Display *display, XEvent *event, XPointer arg);

指定した関数でキュー内のイベントを検索し、一致したイベント (関数が True を返したもの) を返します。

XIfEvent は、一致するイベントが見つかるまでブロックし、イベントをキューから削除します。
XCheckIfEvent はブロックせず、イベントが見つからなかった場合は False を返します。見つかったイベントはキューから削除されます。
XPeekIfEvent はブロックしますが、キューからイベントを削除しません。