第2章 窓(window)を開く


本章では、画面に窓を開くプログラムの作りかたを解説します。


X serverとの接続

まず、これからどの画面に絵を描くかを指定することから プログラムは始まります。通常は描く画面は自分が今見ている画面です。 実は 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を返します。この段階ではまだ窓は 表示されません。この関数にはたくさんの引数があります。それぞれ 以下の通りです。

  • Display* display
    Displayポインタを指定します。
  • Window parent
    親窓の Window IDを指定します。作る窓がどの窓の子窓であるかを 指定します。通例背景の窓(ルートウィンドウ)の子窓として窓を作るので ここには背景の窓のWindow IDを指定します。
  • int xo, int yo
    窓を画面に最初に表示する時のその左上隅の親窓上での座標です。 ある種のウィンドウマネージャではこの指定は意味がありません。
  • unsigned int width,unsigned int height
    窓の縦と横の大きさです。dot単位で指定します。
  • unsigned int border_width
    窓の枠の太さです。
  • unsigned long border_color
    窓の枠の色のpixel番号です。
  • unsigned long ground_color
    窓の背景の色のpixel番号です。
  • この 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 );
    

    これら関数は後で詳しく説明しますので、今はこれをそのまま 使ってください。

    Xserverに依頼を送る

    実は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 があると思います。

    候補1 /usr/X11R6/include/X11/
    候補2 /usr/local/X11R6/include/X11/
    候補3 /usr/openwin/include/X11/
    このディレクトリには Xlib のheader fileが他にも たくさん格納されています。

    次に Xlib のライブラリファイルを探します。上記の候補に対応して 下記のディレクトリのどれかにファイル libX11.a または libX11.so が あると思います。

    候補1 /usr/X11R6/lib/
    候補2 /usr/local/X11R6/lib/
    候補3 /usr/openwin/lib/
    この2つの path を覚えておいて下さい。

    先のプログラムのソースファイルの名前が例えば gratest.cc ならば 次のようにコンパイルします。ここで先の2つのpathを指定します。

    gcc -o gratest gratest.cc -O2 -I/usr/X11R6/include -L/usr/X11R6/lib -lX11 -lm
    

    これで枠が白の黒い窓ができました。めでたし。


  • 第3章 描画コマンドをあやつる
  • 目次
    Copyright(C) by Naoki Watanabe. Oct 21st, 1995.
    渡辺尚貴 naoki@cms.phys.s.u-tokyo.ac.jp