|
○ はじめに
ここまでの LECTURE で作成したサンプルプログラムの画像を見ると、タイトルバーに "Untitled" と描かれています。自分のアプリケーションを作った時にそのタイトルが "Untitled" では悲しいですね。。。ところでこのタイトルバーはウィンドウマネージャによってつけられたものなので、指定したタイトルをタイトルバーに表示するためには、プログラムからウィンドウマネージャに必要な情報を教えなければいけません。
ここではタイトルバーに表示する文字列に限らず、様々な情報をウィンドウマネージャとやりとりする方法について説明していきます。
○ サンプルプログラム(タイトル文字列の表示、XSetWMProperties() )
タイトルバーに "Sample Program!" と表示するサンプルプログラム xwm.c です。下図において、指定した文字列がちゃんとタイトルバーに表示されていることが分かります。はじめに のページから、全てのサンプルプログラムを固めた tar ボールをダウンロードすることもできます。
--- xwm.c ---
1 #include <X11/Xlib.h> 2 #include <X11/Xutil.h> /* for XTextProperty */ 3 #include <X11/Xatom.h> /* for XA_STRING */ 4 #include <string.h> 5 6 7 #define WIN_W 400 /* ウィンドウの幅 */ 8 #define WIN_H 300 /* ウィンドウの高さ */ 9 #define WIN_X 100 /* ウィンドウ表示位置(X) */ 10 #define WIN_Y 100 /* ウィンドウ表示位置(Y) */ 11 #define BORDER 2 /* ボーダの幅 */ 12 13 14 int 15 main( int argc, char** argv ) 16 { 17 Display* dpy; /* ディスプレイ */ 18 Window root; /* ルートウィンドウ */ 19 Window win; /* 表示するウィンドウ */ 20 int screen; /* スクリーン */ 21 unsigned long black, white; /* 黒と白のピクセル値 */ 22 XEvent evt; 23 XTextProperty w_name; /* ウィンドウ名などを設定する */ 24 25 dpy = XOpenDisplay( "" ); 26 27 root = DefaultRootWindow( dpy ); 28 screen = DefaultScreen( dpy ); 29 white = WhitePixel( dpy, screen ); 30 black = BlackPixel( dpy, screen ); 31 32 win = XCreateSimpleWindow( dpy, root, 33 WIN_X, WIN_Y, WIN_W, WIN_H, BORDER, black, white ); 34 35 /* ウィンドウ名の指定 */ 36 w_name.value = (unsigned char*)"Sample program!"; 37 w_name.encoding = XA_STRING; 38 w_name.format = 8; 39 w_name.nitems = strlen( "Sample program!" ); 40 41 /* ウィンドウマネージャに情報を通知する */ 42 XSetWMProperties( dpy, win, &w_name, NULL, argv, argc, NULL, NULL, NULL ); 43 44 XSelectInput( dpy, win, KeyPressMask ); 45 XMapWindow( dpy, win ); 46 47 while( 1 ) { 48 XNextEvent( dpy, &evt ); 49 50 switch ( evt.type ) { 51 case KeyPress : 52 /* リソースの解放 */ 53 XDestroyWindow( dpy, win ); 54 XCloseDisplay( dpy ); 55 return 0; 56 } 57 } 58 } 59 60 /* End of xwm.c */
○ サンプルプログラムの説明
では、サンプルプログラムについて説明していきます。
- 2 〜 3 行目
ウィンドウマネージャとタイトル名などをやりとりする際に使用する XTextProperty 構造体と、 XTextProperty 構造体のメンバに設定する定数の定義を参照するためのヘッダファイルをインク ルードしています。
- 23 行目
XTextProperty 構造体の変数を宣言しています。この変数に必要な情報を設定して、ウィンドウ マネージャに通知します。
- 35 〜 39 行目
まず、XTextProperty 構造体変数の w_name に値を設定しています。XTextProperty 構造体は、 次のように定義されています。
typedef struct { unsigned char* value; /* 文字列 */ Atom encoding; /* XA_STRING を指定 */ int format; /* 半角英数字を設定する場合、8 を指定 ( 8 ビットのデータ配列として扱う ) */ unsigned long nitems; /* value の文字数 */ } XTextProperty;
上の内容に従って、"Sample program!" という文字列についての情報を設定しています。
設定した値をウィンドウのタイトルバーに表示してもらうために、ウィンドウマネージャに通知 する必要がありますが、これには XSetWMProperties() を使っています。
void XSetWMProperties( Display* dpy, Window w, XTextProperty* window_name, XTextProperty* icon_name, char** argv, int argc, XSizeHints* notmal_hints, XWMHints* wm_hints, XClassHint* class_hints ); window_name : ウィンドウ名 icon_name : アイコン名 argv : アプリケーションの引数 argc : 引数の個数 normal_hints : ウィンドウの大きさ wm_hints : WMヒント class_hints : クラスヒント
上の各引数について、どのような情報を与えることができるのかを以下に示します。
- window_name
ウィンドウ名(タイトルバーに表示する文字列)を指定します。設定の方法は、サンプルプログ ラムを参照してください。
- icon_name
アイコン化された場合に使用する名前を、window_name と同様の方法で指定します。
- argc, argv
main( argc, argv ) で指定されたものをそのまま渡します。
- normal_hints
ウィンドウの大きさ、位置、リサイズの単位、ウィンドウの縦横比といった情報をノーマルヒ ントといいます。ノーマルヒントは XSizeHints 構造体を使って設定します。
#include <X11/Xutil.h> /* XSizeHints */ typedef struct { long flags; /* どのメンバに値を指定したか */ int x, y; /* ウィンドウの位置 */ int width, height; /* ウィンドウの幅と高さ */ int min_width, min_height; /* 最小サイズ */ int max_width, max_height; /* 最大サイズ */ int width_inc, height_inc; /* リサイズ時の増分 */ struct { int x; /* ウィンドウの最小(大)の縦横比(x:y) */ int y; }min_aspect, max_aspect; } XSizeHints;
flags にはどのメンバに値を設定したかを設定します。flags に設定する値とメンバの対応は 以下の通りです。
構造体のメンバ flags x, y USPosition または PPosition width, height USSize または PSize min_width, min_height PMinSize max_width, max_height PMaxSize width_inc, height_inc PResizeInc min_aspect, max_aspect PAspect
- wm_hints
クライアントのキーボードフォーカスモデル、初期動作状態(標準、もしくはアイコン) 、 アイコンに使うピクスマップやウィンドウ、アイコンの初期位置などを設定します。 設定は XWMHints 構造体を使って行います。
#include <X11/Xutil.h> /* XWMHints */ typedef struct { long flags; /* どのメンバに値を指定したか */ Bool input; /* クライアントが使用するキーボードフォーカスモデル */ int initial_state; /* 初期状態が標準かアイコンか */ Pixmap icon_pixmap; /* アイコンに使われるピクスマップ */ Window icon_window; /* アイコンに使われるウィンドウ */ int icon_x, icon_y; /* アイコンの初期表示位置 */ Pixmap icon_mask; /* icon_pixmap に対するマスク */ XID window_group; /* ウィンドウグループに関係する ID */ } XWMHints;
flags にはどのメンバに値を設定したかを設定します。flags に設定する値とメンバの対応は 下表の通りです。
構造体のメンバ flags input InputHint initial_state StateHint icon_pixmap IconPixmapHint icon_window IconWindowHint icon_x, icon_y IconPositionHint icon_mask IconMaskHint window_group WindowGroupHint 全て AllHint input には、クライアントが使用するキーボードフォーカスモデルを指定します。 以下のいずれかを指定します。
input 値 フォーカスモデル 説明 True Passive Input キーボードフォーカスは設定しないがキーボード入力は必要 True Locally Active Input キーボード入力が必要かつクライアントのウィンドウの1つが既にフォーカス を持っているときだけキーボードフォーカスを設定する False Globally Active Input キーボード入力が必要かつほかのクライアントのウィンドウにフォーカスが 設定されていてもキーボードフォーカスを設定する False No input キーボード入力を必要としない 2008/06/02 追記。↑のキーボードフォーカスモデルに応じた input 値の設定表ですが、キーボードフォーカスモデルの説明だけ書いていて、肝心の設定値を書いていないことに今更気づきました。ご迷惑をおかけしました…
initial_state には、初期状態が標準(ウィンドウ)なのかアイコンなのかを指定します。
定数名 状態 WithdrawnState すみません、どういう状態かよく分りません IconicState アイコン状態 NomalState 標準状態 icon_pixmap はアイコンに使うピクスマップを指定します。このピクスマップは深さが 1 でな ければいけません。icon_mask は icon_pixmap のどのピクセルを表示するかを決めるビットマ ップです。icon_window には、自分のアイコンの ID を必要としたり、より複雑なビットマッ プを設定しようとする場合に、自分が生成したウィンドウを指定します。
window_group は、1 つのクライアントが複数の最上位ウィンドウを持つ場合に使います。これ により複数の最上位ウィンドウをグループ化して、ウィンドウマネージャがアイコン化などを 行う際にグループとして扱うことができます。最上位ウィンドウの 1 つをグループリーダと決 め、そのウィンドウの window_group には自分のウィンドウ ID を設定します。その他の最上 位ウィンドウにはグループリーダの ID を設定します。
- class_hints
ウィンドウマネージャが、リソースデータベースからクライアントのリソースを取りだすこと ができるように、アプリケーション名とアプリケーションクラス名を指定します。アプリケー ション名は小文字で、アプリケーションクラス名は頭文字を大文字で指定します(習慣)。ただ し最初が x で始まる場合にはその次の文字も大文字にします。
#include <X11/Xutil.h> /* XClassHint */ typedef struct { char* res_name; /* アプリケーション名 */ char* res_class; /* アプリケーション */ } XClassHint;
以上で、各引数の説明は終りです。ただしこれらの情報はあくまでヒントであって、実際にこれ らの情報を使用するかどうかはウィンドウマネージャに委ねられます。
- window_name
○ タイトル文字列の設定/取得
サンプルプログラムでは、XSetWMProperties を使ってタイトル文字列を設定しましたが、以下の関数による設定/取得も出来ます。XSetWMName における text_prop の設定は、サンプルプログラムでの XSetWMProperties と同様です。
(設定) void XSetWMName( Display* dpy, Window w, XTextProperty* text_prop ); XStoreName( Display* dpy, Window w, char* window_name ); (取得) Status XGetWMName( Display* dpy, Window w, XTextProperty* text_prop_return ); XFetchName( Display* dpy, Window w, char** window_name_return );
--- コード例 ---
XTextProperty prop; prop.value = ( unsigned char* )"Sample Title"; prop.encoding = XA_STRING; prop.format = 8; prop.nitems = strlen( "Sample Title" ); XSetWMName( dpy, w, &prop ); if ( XGetWMName( dpy, w, &prop ) ) { /* 取得成功 */ }
○ アイコン表示時のタイトル文字列の設定/取得
アイコン表示時のタイトル文字列を XSetWMProperties を使わずに設定/取得する場合は、以下の関数を使います。
使い方は、タイトル文字列の設定/取得用関数と同じです。
(設定) void XSetWMIconName( Display* dpy, Window w, XTextProperty* text_prop ); XSetIconName( Display* dpy, Window w, char* icon_name ); (取得) Status XGetWMIconName( Display* dpy, Window w, XTextProperty* text_prop_return ); XGetIconName( Display* dpy, Window w, char** icon_name_return );
○ ノーマルヒント( 最小/最大サイズ、縦横比、リサイズ時の増分など ) の設定/取得
ノーマルヒント ( XSetWMProperties に指定する normal_hints、サンプルプログラムの説明を参照してください ) を XSetWMProperties を使わずに設定/取得する場合は、以下の関数を使います。
#include <X11/Xutil.h> /* XSizeHints */ void XSetWMNormalHints( Display* dpy, Window w, XSizeHints* hints ); Status XGetWMNormalHints( Display* dpy, Window w, XSizeHints* hints_return, long* supplied_return ); supplied_return : ユーザ設定による値を示すビットマスク
--- コード例 ---
#include <X11/Xutil.h> /* XSizeHints */ XSizeHints hints; long supplied_return; hints.min_width = 640; hints.min_height = 480; hints.max_width = 640; hints.max_height = 480; hints.flags = PMinSize | PMaxSize; XSetWMNormalHints( dpy, w, &hints ); if ( XGetWMNormalHints( dpy, w, &hints, &supplied_return ) ) { /* 取得成功 */ }
○ クラスヒント( リソース検索用のアプリケーション名とクラス名 ) の設定/取得
クラスヒント( XSetWMProperties に指定する class_hints、サンプルプログラムの説明を参照してください ) を XSetWMProperties を使わずに設定/取得する場合は、以下の関数を使います。
#include <X11/Xutil.h> /* XClassHint */ XSetClassHint( Display* dpy, Window w, XClassHint* class_hints ); Status XGetClassHint( Display* dpy, Window w, XClassHint* class_hints_return );
--- コード例 ---
#include <X11/Xutil.h> /* XClassHint */ XClassHint class_hints; class_hints.res_name = "sample"; class_hints.res_class = "Sample"; XSetClassHint( dpy, w, &class_hints ); if ( XGetClassHint( dpy, w, &class_hints ) ) { /* 取得成功 */ }
○ 日本語タイトルの表示( XmbSetWMProperties() )
XSetWMProperties() の 引数である window_name および icon_name に、マルチバイト文字列を指定できるようにした関数が XmbSetWMProperties() です。この関数の引数 window_name および icon_name には、XTextProperty 構造体ではなく char の配列を渡します。
void XmbSetWMProperties( Display* dpy, Window w, char* window_name, char* icon_name, char** argv, int argc, XSizeHints* normal_hints, XWMHints* wm_hints, XClassHint* class_hints );
最近(2007/02/23)になって久々に Fedora6 上で試してみたら、日本語が出ませんでした… XmbSetWMProperties() を実行する前に setlocale() でロケールをきちんと設定してからだと出る様です。考えてみれば当たり前ですが。下のソースコードにも追加しておきました。
↓ソースコードは xwm_mb.c です。イベントループで何も処理していませんので、Ctrl-C で終了させてください。
○ ウィンドウクローズを通知してもらう ( XSetWMProtocols() )
XSetWMProtocols() は、ウィンドウマネージャの WM_PROTOCOLS プロパティを設定するための関数です。WM_PROTOCOLS プロパティは、ある条件が成立したときにウィンドウマネージャから通知を受けたい場合に設定します。
WM_PROTOCOLS プロパティに設定できるプロパティはいくつかありますが、ここでは WM_DELETE_WINDOW について説明します。これは最上位ウィンドウの破壊要求を表すプロパティです。これを WM_PROTOCOLS プロパティに設定すると、クライアントの最上位ウィンドウ(複数ある場合はどれか 1 つでも)を終了させた場合に ClientMessage イベントを受け取ることができます。このとき ClientMesage イベントの data.l[0] というメンバに WM_DELETE_WINDOW を表すアトムが格納されています。アトム ( Atom ) とはプロパティの識別子と考えてもらえば良いです。このイベントを受け取った場合に通常の終了処理を行うようにすることで、ユーザが突然ウィンドウを終了した場合にも対応することができます。
実際の処理の流れとしては、まず WM_DELETE_WINDOW プロパティに対応したアトムを取得し、そのアトムを WM_PROTOCOLS に設定します。
アトムを取得するには、XInternAtom() を使います。
Atom XInternAtom( Display* display, char* atom_name, Bool only_if_exists ); atom_name : アトムを取得したいプロパティの名称 only_if_exists : False ならば、アトムが存在しない場合には新しく生成される
取得したアトムを WM_PROTOCOLS に設定するには、XSetWMProtocols() を使います。
Status XSetWMProtocols( Display* display, Window w, Atom* protocols, int count ); protocol : 設定したいプロパティに対応するアトムのリスト count : リスト中のアトムの数
あとはイベントループの中で ClientMessage イベントを受け取って処理するコードを書くだけです。
Atom atom; ... atom = XInternAtom( dpy, "WM_DELETE_WINDOW", True ); XSetWMProtocols( dpy, win, &atom, 1 ); ... while( 1 ) { XNextEvent( dpy, &event ); switch ( event.type ) { ... case ClientMessage : if ( event.xclient.data.l[0] == atom ) { /* 終了(確認)処理 を行う */ } break; } }