LECTURE 1 ウィンドウの表示

○ はじめに

GUI プログラミングを行う場合、何よりもまずはウィンドウを表示できなければ話しになりません!ここで紹介するのはウィンドウを表示するだけのサンプルプログラムです。しかし、Xlib プログラミングにおける基本事項がいくつも出て来ますので、しっかりと理解して下さい。

○ Xlib プログラムの構成

Xlib プログラム構成の基本は、次のような流れとなります。

  1. XOpenDisplay() を使って X サーバと接続する。
  2. XCrateSimpleWindow() ( あるいは XCreateWindow() ) を使って、表示するウィンドウを作成する。
  3. XSelectInput() を使って必要なイベントを選択する。
  4. XMapWindow() を使って、作成したウィンドウを画面に表示する。
  5. XNextEvent() を使って X サーバからイベントを読み込み、イベントに応じた処理を行う。
  6. 使用したリソースを解放して終了する。

Xlib プログラムに限らず、一般的に GUI アプリケーションは外部から送られてくるイベントと呼ばれるトリガーを受けて動作します( イベント駆動方式 )。基本的には自分から能動的に動作することはありません。よって、アプリケーションを作成する上で最も重要なのは X サーバからイベントを読み込み、イベントに応じた処理を行う部分と言えます。

ここで紹介するサンプルプログラムでは、何かキーを押した時に通知されるイベントを選択していて、そのイベントを受け取ったら終了するというイベントループを構成しています。

○ サンプルプログラム

幅 400、高さ 300、ウィンドウ枠(ボーダ)の幅 2、ボーダの色は黒、背景色は白のウィンドウを、デスクトップ左上を原点として (x、y) = (100、100)の位置に表示するプログラム xwin.c です。はじめに のページから、全てのサンプルプログラムを固めた tar ボールをダウンロードすることもできます。

/usr/X11R6 に X ウィンドウシステムがインストールされていると仮定した場合、例として次のようにしてコンパイルを行います。

gcc -I/usr/X11R6/include -L/usr/X11R6/lib -o xwin xwin.c -lX11

コンパイルに成功すると、xwin という名前の実行ファイルが作成されます。この xwin を実行すると、画面にウィンドウが表示されます。ウィンドウにフォーカスが当たっている時に何かキーを押すと、xwin は終了します。

001window.jpg

--- xwin.c ---

    1	#include <X11/Xlib.h>
    2	
    3	#define WIN_W  400  /* ウィンドウの幅   */
    4	#define WIN_H  300  /* ウィンドウの高さ */
    5	#define WIN_X  100  /* ウィンドウ表示位置(X) */
    6	#define WIN_Y  100  /* ウィンドウ表示位置(Y) */
    7	#define BORDER 2    /* ボーダの幅      */
    8	
    9	
   10	int 
   11	main( void )
   12	{
   13		Display*       dpy;          /* ディスプレイ ( X サーバのこと) */
   14		Window         root;         /* ルートウィンドウ ( デスクトップ ) */
   15		Window         win;          /* 表示するウィンドウ */
   16		int            screen;       /* スクリーン */
   17		unsigned long  black, white; /* 黒と白のピクセル値 */
   18		XEvent         evt;          /* イベント構造体変数 */
   19	
   20	
   21		/* Xサーバと接続する */
   22		dpy = XOpenDisplay( "" );
   23	
   24	
   25		/* ディスプレイ変数の取得 */
   26		root   = DefaultRootWindow( dpy );
   27		screen = DefaultScreen( dpy );
   28	
   29	
   30		/* 白、黒のピクセル値を取得 */
   31		white  = WhitePixel( dpy, screen );
   32		black  = BlackPixel( dpy, screen );
   33	
   34	
   35		/* ウィンドウを作成する。この時点ではまだ画面に表示されない。*/
   36		win = XCreateSimpleWindow( dpy, root,
   37			   WIN_X, WIN_Y, WIN_W, WIN_H, BORDER, black, white);
   38	
   39	
   40		/* Xサーバから通知してもらうイベントを指定
   41		   ここではキー押下イベントを選択している */
   42		XSelectInput( dpy, win, KeyPressMask );
   43	
   44	
   45		/* ウィンドウを画面に表示する(マップする)。 */
   46		XMapWindow( dpy, win );
   47	
   48	
   49		/* 僕の環境では XCreateSimpleWindow() での座標指定だけでは希望の座標
   50		   にウィンドウが表示されないので、明示的に希望の座標にウィンドウを
   51		   移動させるために XMoveWindow() を使いました。もし (WIN_X, WIN_Y)に
   52		   ちゃんと表示されない場合はやってみてください。 */
   53	
   54		/* XMoveWindow( dpy, win, WIN_X, WIN_Y ); */
   55	
   56	
   57		/* Xサーバへのリクエストバッファをフラッシュする。 */
   58		/* XFlush( dpy ); */
   59	
   60	
   61		/* Xサーバからイベントを読みこんで随時処理していくイベントループ */
   62		while( 1 ) {
   63			XNextEvent( dpy, &evt );
   64	
   65			switch ( evt.type ) {
   66				case KeyPress:
   67					/* リソースの解放 */
   68					XDestroyWindow( dpy, win );
   69					XCloseDisplay( dpy );
   70					return 0;
   71			}
   72		}
   73	}
   74	
   75	/* End of xwin.c */

○ サンプルプログラムの説明

では、サンプルプログラムについて説明していきます。

  • 1 行目

    Xlib プログラミングに必要なヘッダファイル <X11/Xlib.h> をインクルードしています。

  • 3 〜 7 行目

    ウィンドウの幅や高さなどの、プログラムに必要な定数をマクロ定義しています。

  • 13 〜 18 行目

    xlib プログラミング専用の変数型がいくつかでてきています。Display 型は、接続した X サー バを識別するための変数型です。Window 型は、 文字通り画面に表示されるウィンドウを識別す るための型変数です。root と win という 2 つの変数を定義していますが、root とはデスクト ップそのものを表しており、その他の全てのウィンドウはこの root ウィンドウの子ウィンドウ (または、さらにその子ウィンドウ)となります。XEvent 型は、 X サーバから通知される各種の イベントを読みこむために必要な変数型です。

    その他の変数を含めて具体的に何のために使うかについては、実際にその変数を使用している個 所およびその説明を読んでイメージしてください。

  • 22 行目

    全ての xlib プログラミングでは、まず何よりも最初に X サーバと接続しなくてはなりません。 X サーバと接続するには、XOpenDisplay() 関数を使用します。接続に成功した場合には、X サ ーバの情報を持つ Display 構造体へのポインタが返ってきます。接続に失敗した場合には NULL となります。

      Display* XOpenDisplay( char* display_name );
    
      display_name : 接続したい X サーバ名。NULL の場合は、DISPLAY 環境変数に
                     設定されている値が使用される。
    
    

  • 25 〜 32 行目

    ディスプレイマクロと呼ばれるマクロを使って、Display 構造体から各種のディスプレイ情報を 取得しています。サンプルプログラムでは、DefaultRootWindow でルートウィンドウを取得し、 DefaultScreen でスクリーンを取得しています。いずれも Display 構造体へのポインタを引数 とします。

    その後、WhitePixel、BlackPixel で表示に使用する黒、白色のピクセル値を取得しています。 ピクセル値とは、色を指定するためのインデックスのようなものです。RGB のような色情報その ものではありません。色情報はカラーマップと呼ばれるテーブルに設定されており、その中のど の色を使用するかを指定するためにピクセル値を使います。このマクロは、Display 構造体への ポインタと、スクリーンを引数とします。

    ここで使用していないディスプレイマクロで、使いそうだなと思ったものを以下に示します。 今は用途がよく分からないものもあると思いますが、用語が分かるようになった時にあらためて 見るとどう使えば良いのか分かると思います。ここでは、こういうディスプレイマクロがあると いうことを覚えておけば良いです。

    ディスプレイマクロ名 概要
    ConnectionNumber( dpy ) Xサーバを表すファイル記述子
    DisplayWidth( dpy, scr ) ディスプレイの幅(ピクセル単位)
    DisplayHeight( dpy, scr ) ディスプレイの高さ(ピクセル単位)
    DisplayString( dpy ) ディスプレイ名
    DefaultDepth( dpy, scr ) スクリーンのデフォルトの深さ
    DefaultColormap( dpy, scr ) デフォルトのカラーマップID
    DefaultVisual( display, scr ) スクリーンのデフォルトのビジュアル (Visual 構造体へのポインタ)

  • 35 〜 37 行目

    表示するウィンドウを作成しています。簡単にウィンドウを作る関数として、 XCreateSimpleWindow() を使っています。引数に作成するウィンドウの親となるウィンドウを指 定しなければなりませんが、サンプルプログラムではルートウィンドウを指定しています。

    返り値は、作成したウィンドウの識別子(ウィンドウ ID)です。

      Window XCreateSimpleWindow( Display*      display, 
                                  Window        parant,
                                  int           x, y,
                                  unsigned int  width, height,
                                  unsigned int  border_width,
                                  unsigned long border,
                                  unsigned long background
                                );
    
      display       : Display 構造体へのポインタ
      parent        : 作成するウィンドウの親ウィンドウを指定
      x, y          : ウィンドウの表示位置
      width, height : ウィンドウの幅、高さ
      border_width  : ウィンドウの境界(ボーダ)の幅
      border        : ボーダの表示色をピクセル値で指定
      background    : ウィンドウの背景色をピクセル値で指定
    
    

  • 40 〜 42 行目

    前述したように、GUI アプリケーションでは、外部からのイベントを受け取って それに対する処理を行うという動作をします。Windows では基本的に全てのイベント( Windows ではメッセージと呼ぶ) が送られてきて、必要なイベントを選んで処理するという構造になるのですが、 X ウィンドウシステムでは こちらが必要なイベントを X サーバに通知することで、 必要なイベントのみが送られて来るようになります(一部例外有)。

    必要なイベントを通知するには、XSelectInput() を使います。

      XSelectInput( Display* display, Window w, long event_mask );
    
      event_mask : 必要なイベントをイベントマスクで指定する。
    
    

    ここでは KeyPressMask というイベントマスクを使って、キー押下イベントを選択しています。 その他にどんなイベントがあるかについては、LECTURE 4でイ ベントについて少し詳しく説明します。

  • 45 〜 46 行目

    Xlib プログラミングでは、作成したウィンドウはマップという操作をしなければ画面に表示され ません。ウィンドウをマップするためには XMapWindow() 関数を使用します。

      XMapWindow( Display* dpy, Window win );
    
    

    XCreateSimpleWindow() で作成するウィンドウの親となるウィンドウを指定しましたが、ウィン ドウを画面に表示する際にはそのウィンドウの親ウィンドウを先にマップしなければいけません。 ただしルートウィンドウが親の場合はその限りではありません (サンプルプログラムはこのケー ス)。

    補足ですが、ウィンドウを画面から消去するにはアンマップという操作を行います。これには XUnmapWindow() 関数を使います。指定したウィンドウが画面から消去されますが、ウィンドウ のリソース自体が解放されるわけではありません。再度マップすれば表示されます。

    更に、ウィンドウが幾つも重なっているような場合に指定したウィンドウを一番上にマップする には XMapRaised() を使います。また、あるウィンドウの子ウィンドウを全てマップするには XMapSubwindows() を使います。

      XUnmapWindow( Display* display, Window w );
      XMapRaised( Display* display, Window w );
      XMapSubwindows( Display* display, Window w );
    
    

  • 49 〜 54 行目

    本来は XCreateSimpleWindow() に指定した座標に作成したウィンドウが表示されるべきですが、 使用しているウィンドウマネージャのポリシーなどでこの位置指定が有効にならない場合があり ます。そのような場合には、XMoveWindow() を使ってウィンドウを希望の座標に移動させること ができます。

      XMoveWindow( Display* display, Window w, int x, int y );
    
      x, y : win を移動させたい座標
    
    

  • 57 〜 58 行目

    Xlib 関数は、X サーバへの要求をリクエストバッファと呼ばれるバッファにためておいて、あ とで実際に送信するという動作をしています。イベント関連の Xlib 関数を実行するとリクエス トバッファが自動的にフラッシュされるのですが、そうでない場合は、明示的にリクエストバッ ファをフラッシュしなければいけません。このような場合は XFlush() を使います。

      XFlush( Display* display );
    
    

    ところで xwin.c ではイベント処理を行っているので XFlush() は必要無いのですが、前バージ ョンではイベント処理を行っておらず必要だったため、残しておいてついでに説明しています (^_^)。

  • 61 〜 72 行目

    X サーバからイベントを読みこんで処理を行うメインループです(イベントループ)。ここでは while ループを使って、イベントの読み込み、イベント種別を判別してキー押下イベントであ ればリソースを解放して終了、という処理をキーが押されるまで続けています。

    X サーバからイベントを読みこむには、XNextEvent() を使います。

      XNextEvent( Display* display, XEvent* event_return );
    
      event_return : 読みこんだイベント情報を格納する XEvent 構造体へのポインタ
    
    

    実行すると、X サーバから読みこんだイベント情報が XEvent 構造体に格納されます。XEvent 構造体については、別の LECTURE で少し詳しく説明します。

    XEvent 構造体の type メンバに、イベントの種類が格納されます。キー押下イベントの場合、 KeyPress が設定されます。これを受け取った時点で、67 〜 70 行目でリソースを解放してプ ログラムを終了しています。XDestroyWindow() と XCloseDisplay() はそれぞれ作成したウィ ンドウの破棄、X サーバとの接続の切断を行うための関数です。XDestroyWindow() では、指 定したウィンドウとその全ての子ウィンドウが破棄されます。

      XDestroyWindow( Display* display, Window w );
      XCloseDisplay( Display* display );
    
    

以上で xwin.c の解説は終わりです。ここで説明した xwin.c は Xlib プログラミングの基本形です。今後はここで説明した xwin.c をベースとしてサンプルプログラムを作成していきますので、その構造についてはしっかり理解しておいてください。