本章では、画面に窓を開くプログラムの作りかたを解説します。
まず、これからどの画面に絵を描くかを指定することから プログラムは始まります。通常は描く画面は自分が今見ている画面です。 実は X window system では他人の画面にも絵を描くことができるのですが 本書ではそれについては言及しません。 画面の指定には XOpenDisplay() 関数を使います。
Prototype
Display* XOpenDisplay( char* name_of_display );この引数に画面の名前を指定するのですが、普通ここは NULL にして おきます。NULLを指定すると自分が使っている画面を使うことになります。 もう少し詳しく説明するとプログラム実行時の環境変数 DISPLAY に格納 されている名前の画面を使うことになります。
この XOpenDisplay() はその画面を管理するX serverとの通信経路を 確立し、さまざまな情報をメモリに格納し、そのアドレスを Display型構造体のポインタで返します。 このポインタのことを以後 Displayポインタと呼びます。
Example
Display* dis; dis = XOpenDisplay( NULL );
先にプログラムの終了の際のことを説明します。 終了の際にはXOpenDisplay()が確保したXserverとの通信経路を閉鎖します。 それにはXCloseDisplay()関数を使います。
Prototype
void XCloseDisplay( Diplay* display );
窓を画面に表示させる前にするべきことがまだいくつかあります。 まず、X serverに対して、新しく作る窓の管理を依頼します。 その際に、その窓の大きさや色を設定します。 この管理の依頼と性質の設定には XCleateSimpleWindow()関数を 使うのが一般的です。
Prototype
Window XCleateSimpleWindow( Display* display, Window parent, int xo, int yo, unsigned int width, unsigned int height, unsigned int border_width, unsigned long border_color, unsigned long ground_color );
この関数はX serverに窓の管理に必要なさまざまな設定を依頼して、 その窓の識別子として Window IDを返します。この段階ではまだ窓は 表示されません。この関数にはたくさんの引数があります。それぞれ 以下の通りです。
この Window型の変数は unsigned int型と同じ大きさの変数なので
Window IDの指定にはこの変数の値を用います。
一方、Display型は巨大な構造体なので、Displayポインタはそのアドレス値
を用いるという違いがあることに注意してください。
Example
Window win; win = XCleateSimpleWindow( dis, RootWindow(dis,0), 0, 0, 512, 512, 3, WhitePixel(dis,0), BlackPixel(dis,0) );
次に窓にさまざまな性質を設定します。 窓の性質に関するさまざまな情報はXSetWindowAttributes型構造体 の各要素に記録されています。その詳細については他書を参照してください。 ここではひとつだけ大事な性質を標準設定から変えます。
窓の性質を変えるには、XSetWindowAttributes型構造体の白紙の変数を 用意してそれに変える内容を記して、XChangeWindowAttributes()関数に そのXSetWindowAttributes型構造体変数と変えるべき項目名を記して 実行します。
例として窓が他の窓によって隠された場合でも、その絵の内容を記憶 させておくように窓の性質を変える方法を示します。Example
XSetWindowAttributes att; att.backing_store = WhenMapped; XChangeWindowAttributes( dis, win, CWBackingStore, &att );
XSetWindowAttributes型構造体変数の att のプロパティ backing_store に定数 WhenMapped を代入しています。そして XChangeWindowAttributes()関数で Displayポインタ, Window ID と共に ビットマスク定数 CWBackingStore と attのアドレスを渡しています。
いよいよ窓を画面上に表示します。窓を表示するには XMapWindow()関数 を使います。
Prototype
void XMapWindow( Display* display, Window window );
ついでにいくつかの関数を紹介します。
逆に窓を非表示するには XUnmapWindow()関数を使います。Prototype
void XUnmapWindow( Display* display, Window window );この窓に描かれている絵を消去するには XClearWindow()関数を使います。
Prototype
void XClearWindow( Display* display, Window window );非表示ではなく窓を完全に消滅させるには XDestroyWindow()関数を 使います。
Prototype
void XDestroyWindow( Display* display, Window window );
Xlibの関数の仕事は Xserver に対してX protocolによる 描画依頼を送ることのみです。従って実際にその依頼がXserverに受理 されて実行されるのには少々時間がかかります。
窓を開く関数 XMapWindow()もその例外ではありません。 窓が開くまでには結構時間がかかります。 その間にプログラムはさっさと先に進み、次の描画依頼を送り始める でしょう。ですが、Xserverは開いていない窓への描画依頼はすべて 無視します。
このため XMapWindow()を実行してから次のステップに進む前に 窓が本当に開かれたことを確認するまで待たなければならないのです。
このようにプログラム(Xclient)が Xserverの状況を知る必要から Xserverはその状況をイベントという手法によりXclientに通知する ようになっています。イベントに関する詳しい説明は後にまわして、 ここでは窓が開いたことをXclientに通知するイベントの Exposeイベントを紹介します。
XserverからExposeイベントが送られてくるまで待つ仕組みを備えるには 以下のようにして実現されます。Example
これら関数は後で詳しく説明しますので、今はこれをそのまま 使ってください。XEvent ev; XSelectInput( dis, win, ExposureMask ); XMapWindow( dis, win ); do{ XNextEvent( dis, &ev); }while( ev.type != Expose );
実はXlib関数の送る描画依頼は、関数の実行ですぐにXserverに 送られるのではなく、一旦、リクエストバッファと呼ばれる メモリに蓄えられて、一定数の依頼(リクエスト)が溜ってからまとめて Xserverに送られるのです。従って、2,3のXlib関数が実行されても Xserverは動きだしません。そこでこのバッファに溜っている リクエストを強制的にXserverに送らせるにはXFlush()関数を使います。
Prototype
void XFlush( Display* display );XFlush()関数は描画の要所要所に配置しておきます。
いままで説明した関数を使って窓を開いて閉じるプログラムを 作成します。理解をより完全なものにしてください。
#include <X11/Xlib.h> //Xlibに必要なインクルード #include <X11/Xutil.h> #include <stdio.h> int main( void ) { Display* dis; //Display pointer Window win; //Window ID XSetWindowAttributes att; //窓属性の変数 XEvent ev; //イベント取り込み変数 dis = XOpenDisplay( NULL ); //Xserverとの接続 win = XCreateSimpleWindow( dis, RootWindow(dis,0), 100, 100, 256, 256, 3, WhitePixel(dis,0), BlackPixel(dis,0) ); //窓の生成 att.backing_store = WhenMapped; //絵を保存する設定をする XChangeWindowAttributes( dis, win, CWBackingStore, &att ); XMapWindow( dis, win ); //窓の表示 XFlush( dis ); //リクエストの強制送信 XSelectInput( dis, win, ExposureMask ); do{ //窓が開くの待つループ XNextEvent( dis, &ev); }while( ev.type != Expose ); // Exposeイベントが届くまでここを繰り返す // ここまで来たら真っ黒な窓が登場しているはず。 getchar(); // リターンキーが押されるまで待つ。 XDestroyWindow( dis, win ); //窓の消去 XCloseDisplay( dis ); //Xserverと断線 return(0); }
次にこれをコンパイルします。コンパイル方法が複雑です。
まずXlibのヘッダーファイル Xlib.h が置かれているディレクトリ を探して下さい。 大抵のXWindow Systemでは下記の3つのディレクトリのうちのどれかに ファイル Xlib.h があると思います。
このディレクトリには Xlib のheader fileが他にも たくさん格納されています。
候補1 /usr/X11R6/include/X11/ 候補2 /usr/local/X11R6/include/X11/ 候補3 /usr/openwin/include/X11/
次に Xlib のライブラリファイルを探します。上記の候補に対応して 下記のディレクトリのどれかにファイル libX11.a または libX11.so が あると思います。
この2つの path を覚えておいて下さい。
候補1 /usr/X11R6/lib/ 候補2 /usr/local/X11R6/lib/ 候補3 /usr/openwin/lib/
先のプログラムのソースファイルの名前が例えば gratest.cc ならば 次のようにコンパイルします。ここで先の2つのpathを指定します。
gcc -o gratest gratest.cc -O2 -I/usr/X11R6/include -L/usr/X11R6/lib -lX11 -lm
これで枠が白の黒い窓ができました。めでたし。