Xlib 編 (1)

Xlib 編 (2) >> 次へ

とりあえずウィンドウを出すぞ〜

X Window System (以下 X) はクライアント・サーバで、ネットワーク透過性を…と なんて書いてもつまらんので、略。 要は、X というのは画面にウィンドウを表示し、そのウィンドウの中で いろんな表示を行うための仕組み。 で、Xlib というのは最も低レベルなライブラリのことなのね。 低レベルだから手続きも多くてめんどくさいけど、 ウィンドウを出すために何をやっているかを把握するのは、 無駄なことではないよ。

とりあえず、ウィンドウを出してみましょ。はい、サンプル。

xlib-1.c

    1: #include <X11/Xlib.h>
    2: #include <X11/Xutil.h>
    3: 
    4: int main(int argc, char *argv[]){
    5:     Display *display;
    6:     Window window;
    7:     GC     gc;
    8:     char title[]      = "This is TITLE";
    9:     char message[]    = "Hello New World!";
   10:     char icon_title[] = "ICON!";
   11:     unsigned long background;
   12:     unsigned long foreground;
   13: 
   14:     display = XOpenDisplay(NULL);
   15: 
   16:     background = WhitePixel(display, 0);
   17:     foreground = BlackPixel(display, 0);
   18: 
   19:     window = XCreateSimpleWindow(display,
   20:                                  DefaultRootWindow(display),
   21:                                  0, 0, 200, 100,
   22:                                  0, 0, background);
   23: 
   24:     XSetStandardProperties(display, window, title, icon_title,
   25:                            None, argv, argc, NULL);
   26: 
   27:     gc = XCreateGC(display, window, 0, 0);
   28: 
   29:     XSetBackground(display, gc, background);
   30:     XSetForeground(display, gc, foreground);
   31: 
   32:     XSelectInput(display, window, ExposureMask);
   33: 
   34:     XMapRaised(display, window);
   35: 
   36:     while (1){
   37:         XEvent event;
   38:         XNextEvent(display, &event);
   39:         switch ( event.type ){
   40:           case Expose:
   41:             XDrawImageString(display, window, gc, 20, 50, message, strlen(message));
   42:             break;
   43:         }
   44:     }
   45: }
コンパイルは、
% cc -o xlib-1 xlib-1.c -I /usr/X11R6/include -lX11 -L /usr/X11R6/lib
とします。-o で生成する実行ファイル名を指定、-I で インクルードファイルの場所 (/usr/X11R6/include) を指定、 -lX11 でライブラリ libX11.so.* とリンクすることを指定、 -L で libX11.so.* のある場所を指定、しているわけね。

xlib-1 を実行すると

[Xlib のサンプル画像(1)]
というウィンドウが出てきた? それはよかった。 タイトルやウィンドウ枠は、どんなウィンドウマネージャを 使っているかによって違う。これは Afterstep というウィンドウマネージャ。 でも、枠の中は同じはず。

てきとーな説明

初めに断っておくけど、この説明はとってもいい加減です。 理解しやすくするためにあえて説明しない部分がたくさんあるので、 そのつもりで。
    1: #include <X11/Xlib.h>
    2: #include <X11/Xutil.h>
Xlib 用の構造体やマクロなどは X11/Xlib.h と X11/Xutil.h で 宣言されてるので、それを include すると。お約束です。
    5:     Display *display;
    6:     Window window;
    7:     GC     gc;
Display、Window、GC の概念について。 Display というのは、画面にウィンドウを出すための権利みたいなもの。 Window は、ウィンドウ1つにつき1つ使われる構造体。今回ウィンドウは 1つだけだから、変数も1つしか使わない。 GC とは Graphic Context の略。色とかフォントとかの情報を管理するわけ。
   14:     display = XOpenDisplay(NULL);
XOpenDisplay で、ウィンドウを出す権利を取得する。 戻り値として、Display 構造体へのポインタ、言い替えれば 「X の使用権利書」が返される。 今後使用する X の関数は、全て引数に「構造体 Display へのポインタ」を 渡すことに注意。X Window System の上で何かしたい場合は、常に 「X の使用権利書」を渡さなければいけないわけね。
   16:     background = WhitePixel(display, 0);
   17:     foreground = BlackPixel(display, 0);
背景を白に、前景を黒にしたいので、白と黒の色番号を取得。
   19:     window = XCreateSimpleWindow(display,
   20:                                  DefaultRootWindow(display),
   21:                                  0, 0, 200, 100,
   22:                                  0, 0, background);
ウィンドウを作成。 第2引数で、どのウィンドウの下にウィンドウを作るかを指定するが、 このように DefaultRootWindow を指定すると普通のウィンドウが生成される (ここらへんは今後詳しく解説する)。

その後の「0,0,200,100」は、XY座標 0,0 に、 サイズ 200 x 100 のウィンドウを表示することを表す。 しかし普通は、ウィンドウマネージャにより ウィンドウの座標値 (ここでは 0,0) は無視される。

その後の「0,0」は、ウィンドウ枠の幅とウィンドウ枠の色についての情報なんだけど、 そこらへんはウィンドウマネージャが勝手にやってくれるので 0,0 を指定しておけばよし。 その次は背景色を指定する。background には WhitePixel で取得した 白色の色情報が入っているので、生成されたウィンドウの背景が 白になるのね。


   24:     XSetStandardProperties(display, window, title, icon_title,
   25:                            None, argv, argc, NULL);
タイトル・アイコンなどの情報を設定。第3引数の title の部分で ウィンドウマネージャの表示するタイトル部分の文字列を設定している。 その次の icon_title では、アイコン化したときのタイトルを指定している。 このため。アイコン化すると、
[アイコン化した画像]
「ICON!」と表示される。

その次の None には、アイコン化したときの画像ファイル名を設定する。 xpm というファイル形式のフルパスを指定すればよいのだが、 ここでは省略して None としているので、このアイコンの画像は ウィンドウマネージャ (afterstep) が適当に選んだものである。


   27:     gc = XCreateGC(display, window, 0, 0);
   28: 
   29:     XSetBackground(display, gc, background);
   30:     XSetForeground(display, gc, foreground);
先に少し触れたように、GC は色やフォントの情報を管理する構造体である。 文字を表示したり、線を引いたりする関数を使う際は、必ず引数に GC を 渡さないといけない。それらの関数は引数で渡された GC の内容に従って 色やフォントを選択するわけね。

XCreateGC を使って、戻り値で GC を得ている。 そして、XSetForeground と XSetBackground で背景色と前景色を 設定している。先に

   16:     background = WhitePixel(display, 0);
   17:     foreground = BlackPixel(display, 0);
としているので、前景色は黒、背景色は白になる。
   32:     XSelectInput(display, window, ExposureMask);
受け付けるイベントを設定する。

ウィンドウに対するマウスクリック・キー入力・ウィンドウ移動などの入力は、 イベントという仕組みで管理されている。 例えば、マウスがクリックされると ButtonPress、マウスボタンが離されると ButtonRelease、キーボードのキーが押されると KeyPress、ウィンドウの中に マウスポインタが入ると EnterNotify、などと、種類によって異なるイベントが発生する。 X アプリケーションでは、 「キーが押されたから〜という動作をしよう」「マウスが押されたから〜という動作をしよう」 というふうに、イベントの種類に従って処理を変えるのである。

イベントは全部で33種類ある。33種類のイベントが起こるたびに いちいち X アプリケーションが処理をすると、動作が重くなる。 そのため、デフォルトでは全てのイベントを受け取らないようになっている。 X アプリケーションは、33種類のイベントの中から、 「イベント A とイベント B とイベント C が起こったときだけ連絡してね」 と最初に設定しなければならない。そのための関数が XSelectInput である。

ここでは、ExposureMask だけを設定しているので、Expose 以外のイベントは X アプリケーションには届かない。つまり、キー入力やマウスクリックが あっても、X アプリケーションからは認識できないのである。

さて、Expose とは何かを説明しよう。これは、

ときに発生する。例えば、このように
[Expose イベントが発生するとき]
ウィンドウが他のウィンドウで隠されているとしよう。ここで ウィンドウを前面に出して
[Expose イベントが発生するとき(2)]
とすると、隠れていた「ew World!」の部分が露出する。このとき Expose イベントが発生する。このように隠れていた部分が表れたとき、 X アプリケーション自身が再描画しないといけないのである。
   34:     XMapRaised(display, window);
ウィンドウをマップする。 ウィンドウは XCreateSimpleWindow で生成しただけでは表示されない。 マップして初めて実際に画面上に表示されるわけである。
   36:     while (1){
   37:         XEvent event;
   38:         XNextEvent(display, &event);
   39:         switch ( event.type ){
   40:           case Expose:
   41:             XDrawImageString(display, window, gc, 20, 50, message, strlen(message));
   42:             break;
   43:         }
   44:     }
   45: }
while (1) で無限ループしている。先に説明した通り、 Expose イベントが発生したときにいつでも対応するためである。 XNextEvent でイベントを取得し、その種類に応じて 動作を変えている。
ただし、XSelectInput で Expose 以外を設定しなかったので、 Expose イベント以外はやってこない。
Expose イベントなら、XDrawImageString で XY 座標 (20,50) に message の内容を表示する。

ここで、X アプリケーションの動作を止めてみよう。

% xlib-1
^Z (Ctrl-Z でサスペンド)
Suspended
そして、ウィンドウの一部を他のウィンドウの下に隠して、 再度露出させると、
[Expose イベントが発生するとき(3)]
と、一部が表示されない。 Xlib で X アプリケーションを作る場合は、 常に Expose イベントに対応できるようにしておかなければいけないわけだ。

これは Xlib プログラムの最低限のスケルトンである。 細かいことを言えば、タイトルやアイコンタイトルなんていらない。 しかし、

は必須なのね。

Xlib 編 (2) >> 次へ

$Id: xlib-1.html,v 1.2 2004/06/12 02:03:55 68user Exp $