WM_PROTOCOLS
WM_PROTOCOLS プロパティには、type = "ATOM", format = 32 で、Atom のリストをセットします。
クライアントとウィンドウマネージャ間で、ClientMessage イベントを使用した通信を行うために、必要となる機能のアトムのリストを指定します。
ICCCM では、以下が定義されています。
クライアントとウィンドウマネージャ間で、ClientMessage イベントを使用した通信を行うために、必要となる機能のアトムのリストを指定します。
ICCCM では、以下が定義されています。
WM_TAKE_FOCUS | 入力フォーカスを受け取る |
---|---|
WM_DELETE_WINDOW | トップレベルウィンドウの削除要求を受け取る。 (ウィンドウ装飾の閉じるボタンが押された時) |
ClientMessage イベント
typedef struct { int type; unsigned long serial; Bool send_event; Display *display; Window window; Atom message_type; int format; union{ char b[20]; short s[10]; long l[5]; } data; } XClientMessageEvent;
ClientMessage イベントは、クライアントが、XSendEvent 関数を使って ClientMessage イベントを送信した時に来ます。
別のクライアントやウィンドウに、イベントを通知したい場合に使います。
message_type | データをどのように解釈するかを示すアトム |
---|---|
format | データのフォーマット。 「8 = data.b」「16 = data.s」「32 = data.l」を使います。 |
data | 渡されたデータ。 最大で 20 byte (format = 32 は 32bit 扱い)。 |
WM_DELETE_WINDOW
WM_PROTOCOLS プロパティのアトムリスト内に、WM_DELETE_WINDOW のアトムがある場合、ウィンドウマネージャが作成したウィンドウ装飾上の「閉じる」ボタンが押されたり、ウィンドウマネージャによって、クライアントウィンドウを閉じる要求が行われた時に、以下の値で ClientMessage イベントが送られてきます。
クライアントがこれを受け取った場合、ウィンドウを実際に閉じたり、ダイアログを表示して、本当に終了するかを尋ねたりする必要があります。
結果として閉じる処理をキャンセルする場合は、何も行う必要はありません。
WM_PROTOCOLS プロパティに WM_DELETE_WINDOW が含まれていないウィンドウで、ウィンドウマネージャからの閉じる要求が行われた場合は、クライアントが強制終了される場合があります。
message_type = (Atom) WM_PROTOCOLS format = 32 data.l[0] = (Atom) WM_DELETE_WINDOW
クライアントがこれを受け取った場合、ウィンドウを実際に閉じたり、ダイアログを表示して、本当に終了するかを尋ねたりする必要があります。
結果として閉じる処理をキャンセルする場合は、何も行う必要はありません。
WM_PROTOCOLS プロパティに WM_DELETE_WINDOW が含まれていないウィンドウで、ウィンドウマネージャからの閉じる要求が行われた場合は、クライアントが強制終了される場合があります。
WM_TAKE_FOCUS
WM_PROTOCOLS プロパティのアトムリスト内に、WM_TAKE_FOCUS のアトムがある場合、ウィンドウが入力フォーカスを受け取る (または受け取った) タイミングで、ClientMessage イベントが送られてきます。
message_type = (Atom) WM_PROTOCOLS format = 32 data.l[0] = (Atom) WM_TAKE_FOCUS data.l[1] = (Time) タイムスタンプ
応答
クライアントがこれを受け取った時、フォーカスが必要な場合は、XSetInputFocus() で入力フォーカスをセットします。
この時、revert_to 引数には RevertToParent を指定してください。この値以外は問題があります。
time 引数には、イベントからのタイムスタンプを指定します。
XSetInputFocus(disp, ev.xclient.window, RevertToParent, ev.xclient.data.l[1]);
この時、revert_to 引数には RevertToParent を指定してください。この値以外は問題があります。
time 引数には、イベントからのタイムスタンプを指定します。
WM_HINTS
WM_HINTS プロパティの input の値が True (通常はデフォルト) の場合、ウィンドウマネージャが入力フォーカスを制御し、適切なウィンドウに自動で入力フォーカスをセットします。
ウィンドウマネージャによって XSetInputFocus() が実行された後、クライアントに FocusIn イベントが来て、その後に WM_TAKE_FOCUS が来ます。
通常は自動で入力フォーカスがセットされるので、ウィンドウが入力フォーカスを取得した時に、(子ウィンドウを含む) 他のウィンドウに対して入力フォーカスを設定するようなことがない場合は、WM_TAKE_FOCUS を受け取る必要はありません。
トップレベルウィンドウで入力フォーカスを受け取って、WM_TAKE_FOCUS が来た時に、そのウィンドウ以外のウィンドウに入力フォーカスを設定したい場合は、WM_TAKE_FOCUS が必要になります。
input の値が False の場合は、ウィンドウマネージャが XSetInputFocus() を行わないので、WM_TAKE_FOCUS が来たタイミングで、クライアントが明示的に入力フォーカスを設定する必要があります。
ウィンドウマネージャによって XSetInputFocus() が実行された後、クライアントに FocusIn イベントが来て、その後に WM_TAKE_FOCUS が来ます。
通常は自動で入力フォーカスがセットされるので、ウィンドウが入力フォーカスを取得した時に、(子ウィンドウを含む) 他のウィンドウに対して入力フォーカスを設定するようなことがない場合は、WM_TAKE_FOCUS を受け取る必要はありません。
トップレベルウィンドウで入力フォーカスを受け取って、WM_TAKE_FOCUS が来た時に、そのウィンドウ以外のウィンドウに入力フォーカスを設定したい場合は、WM_TAKE_FOCUS が必要になります。
input の値が False の場合は、ウィンドウマネージャが XSetInputFocus() を行わないので、WM_TAKE_FOCUS が来たタイミングで、クライアントが明示的に入力フォーカスを設定する必要があります。
WM_TAKE_FOCUS が必要な理由
XSetInputFocus() で入力フォーカスを設定する場合は、time 引数に適切なタイムスタンプ (CurrentTime 以外) が必要になります。
例えば、FocusIn イベントでフォーカスを取得した時に、(子ウィンドウを含む) 他のウィンドウにフォーカスを与えたい場合、FocusIn イベントにはタイムスタンプがないので、適切に XSetInputFocus() を実行することができません。
そのため、タイムスタンプを持った WM_TAKE_FOCUS が必要になります。
例えば、FocusIn イベントでフォーカスを取得した時に、(子ウィンドウを含む) 他のウィンドウにフォーカスを与えたい場合、FocusIn イベントにはタイムスタンプがないので、適切に XSetInputFocus() を実行することができません。
そのため、タイムスタンプを持った WM_TAKE_FOCUS が必要になります。
入力モード
WM_HINTS の input 値と、WM_TAKE_FOCUS を受け取るかによって、4つの入力モードがあります。
通常はローカルアクティブを使います。
入力なし | input = Fale, WM_TAKE_FOCUS = なし。 ウィンドウマネージャは、入力フォーカスに全く関与しない。 |
---|---|
パッシブ | input = True, WM_TAKE_FOCUS = なし。 ウィンドウマネージャが自動で XSetInputFocus を行う。クライアントがそのタイミングを知る必要はない。 |
ローカルアクティブ | input = True, WM_TAKE_FOCUS = あり。 ウィンドウマネージャが自動で XSetInputFocus を行った後、入力フォーカスが必要なタイミングで WM_TAKE_FOCUS が来る。 |
グローバルアクティブ | input = False, WM_TAKE_FOCUS = あり。 ウィンドウマネージャによる XSetInputFocus は行わず、WM_TAKE_FOCUS が来た時にクライアントが明示的に行う。 |
通常はローカルアクティブを使います。
- トップレベルウィンドウに対する入力フォーカスのセットは、ウィンドウマネージャに任せた上で、クライアントが任意の子ウィンドウに入力フォーカスをセットしたい場合は、ローカルアクティブを使います。
- 入力フォーカスが必要なタイミングで、クライアントの判断によって XSetInputFocus() を行いたい場合は、グローバルアクティブを使います。
- トップレベルウィンドウへの入力フォーカスを、すべてウィンドウマネージャに任せたい場合は、パッシブにします。
プログラム
ウィンドウ装飾の閉じるボタンを押すと、WM_DELETE_WINDOW が来ます (ウィンドウは閉じない)。
入力フォーカスが来ると、WM_TAKE_FOCUS が来ます。
ポインタボタンを押すと終了します。
<d06-protocol.c>
入力フォーカスが来ると、WM_TAKE_FOCUS が来ます。
ポインタボタンを押すと終了します。
$ cc -o run d06-protocol.c util.c -lX11
<d06-protocol.c>
#include <stdio.h> #include <X11/Xlib.h> #include <X11/Xutil.h> #include <X11/Xatom.h> #include "util.h" const char *g_atom_names[] = { "WM_PROTOCOLS", "WM_DELETE_WINDOW", "WM_TAKE_FOCUS" }; int main(int argc,char **argv) { Display *disp; Window win; XEvent ev; Atom atom[3],type; disp = XOpenDisplay(NULL); if(!disp) return 1; win = create_test_window(disp, ButtonPressMask | FocusChangeMask); //アトム取得 XInternAtoms(disp, (char **)g_atom_names, 3, False, atom); //WM_PROTOCOLS XChangeProperty(disp, win, atom[0], XA_ATOM, 32, PropModeReplace, (unsigned char *)&atom[1], 2); //イベント XMapWindow(disp, win); while(1) { XNextEvent(disp, &ev); switch(ev.type) { case ClientMessage: if(ev.xclient.message_type == atom[0]) { type = (Atom)ev.xclient.data.l[0]; if(type == atom[1]) printf("WM_DELETE_WINDOW\n"); else if(type == atom[2]) { printf("WM_TAKE_FOCUS: time=%lu\n", ev.xclient.data.l[1]); XSetInputFocus(disp, ev.xclient.window, RevertToParent, ev.xclient.data.l[1]); } } break; case FocusIn: printf("FocusIn\n"); break; case ButtonPress: goto END; } } END: XCloseDisplay(disp); return 0; }
タイミング
WM_TAKE_FOCUS は、最初にマップされた時や、ポインタボタンが押されたときにも来ます。
また、装飾フレームウィンドウ上でポインタボタンを押した時にも来ます。
(入力フォーカスをセットするかどうかは、ウィンドウマネージャが制御しているので、実装によるかもしれません)
他のウィンドウに入力フォーカスがある状態で、入力フォーカスのないウィンドウ上でボタンを押した場合、入力フォーカスがそのウィンドウに移ることになるので、ポインタボタンが押された時にも入力フォーカスがセットされます。
FocusIn イベントは、実際に入力フォーカスが変化した時にしか来ませんが、WM_TAKE_FOCUS は、ウィンドウに入力フォーカスがあるかどうかに関係なく、入力フォーカスをセットする必要があるタイミングで、常に来ます。
また、装飾フレームウィンドウ上でポインタボタンを押した時にも来ます。
(入力フォーカスをセットするかどうかは、ウィンドウマネージャが制御しているので、実装によるかもしれません)
他のウィンドウに入力フォーカスがある状態で、入力フォーカスのないウィンドウ上でボタンを押した場合、入力フォーカスがそのウィンドウに移ることになるので、ポインタボタンが押された時にも入力フォーカスがセットされます。
FocusIn イベントは、実際に入力フォーカスが変化した時にしか来ませんが、WM_TAKE_FOCUS は、ウィンドウに入力フォーカスがあるかどうかに関係なく、入力フォーカスをセットする必要があるタイミングで、常に来ます。