Xlib

データ型

Xlib での主要なデータ型は Display 構造体と各種識別子型である。
ディスプレイとは、いわばグラフィカルな操作が行われる物理的あるいは仮想的な機器である。Xlib における Display 構造体には、ディスプレイに関する情報が格納されているが、より重要な情報としてクライアントとサーバ間のチャンネルに関する情報も格納している。例えば、UNIX系OSでは Display 構造体には、そのチャンネルのソケットのファイルハンドルが含まれる（ConnectionNumber マクロで取り出し可能）。Xlib の関数の多くは Display 構造体を引数としている。これは、Xlib の関数の多くがチャンネルに対して操作したり、特定のチャンネルに関する操作をするためである。特にサーバとやり取りする Xlib の関数は、チャンネルにアクセスするのにこの構造体を必要とする。ローカルな処理しかしない関数でも、特定のチャンネルに関連したデータを操作するためにこの構造体を必要とする。例えば、イベントキューの操作はチャンネルごとのイベントキューを操作する。
ウィンドウ、カラーマップなどはサーバが管理する。つまり、実際の見た目に関するデータはサーバ側にある。クライアントは識別子を使ってそれらのオブジェクトを操作する。クライアントはオブジェクトを直接操作できず、サーバにオブジェクトの識別子を指定して操作を要求する。
Windows、Pixmap、Font、Colormap などの型はすべて識別子であり、実際には32ビットの整数である。クライアントはサーバに対してウィンドウの生成を要求することでウィンドウを生成する。これは、ウィンドウの識別子（つまり番号）を返す Xlib 関数を呼び出すことで行われる。その後、その識別子を使ってクライアントがサーバに対してそのウィンドウへの各種操作を要求する。
識別子はサーバ内で一意である。その多くは別々のアプリケーションであっても同じオブジェクトを指すのに使われる。例えば、1つのサーバと接続した2つのアプリケーションがあるとき、1つの識別子でどちらも同じウィンドウを参照することができる。それぞれのアプリケーションは使用しているチャンネルが異なるため、それぞれ別々の Display 構造体を持っている。しかし、同じ識別子についての操作を要求すれば、同じオブジェクトに対して操作が行われる。

プロトコルとイベント

サーバに要求を送る Xlib 関数は即座に要求を送るのではなく、output buffer というバッファに一旦格納する。この場合の output はクライアントからサーバに向けての出力である。output buffer はサーバへのあらゆる種類の要求を格納でき、それは必ずしも画面に見える効果だけではない。output buffer は、関数 XSync や XFlush を呼び出したとき、あるいはサーバからの戻り値がある関数を呼び出したとき（応答があるまでそれら関数はブロックする）、あるいはその他の条件でフラッシュ（バッファ上のすべての要求がサーバに送られ、バッファを空にする）される。
Xlib は受け取ったイベントをキューに格納する。クライアント・アプリケーションは、そのキューを調べて、イベントを取り出す。Xサーバがイベントを非同期に送るのに対して、Xlib を使うアプリケーションはキュー上のイベントにアクセスするのに Xlib 関数を明示的に呼び出さなければならない。そのような関数の中にはブロックするものもあり、その時点でも output buffer がフラッシュされる。
エラーは非同期に受け取られ、扱われる。アプリケーションは、エラー発生時にエラーメッセージをサーバから受け取るエラーハンドラを登録しておく。
ウィンドウの一部が見えない状態のとき、ウィンドウの内容が保持されているとは限らない。その場合、隠れていたウィンドウが見える状態になると Expose イベントがアプリケーションに送られる。アプリケーションは、そのイベントを受けてウィンドウの内容を再描画しなければならない。

例

以下のプログラムは、ウィンドウを表示し、その中に小さな黒い四角形を描画するものである。

 /*   ウィンドウに四角形を描画する簡単な Xlib アプリケーション */

 #include <X11/Xlib.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>

 int main() {
   Display *d;
   int s;
   Window w;
   XEvent e;

                        /* サーバとのコネクションを開く */
   d=XOpenDisplay(NULL);
   if(d==NULL) {
     printf("Cannot open display\n");
     exit(1);
   }
   s=DefaultScreen(d);

                        /* ウィンドウ生成 */
   w=XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 100, 100, 1,
                         BlackPixel(d, s), WhitePixel(d, s));

                        /* 受け付けるイベントの種類を選択 */
   XSelectInput(d, w, ExposureMask | KeyPressMask);

                        /* ウィンドウを可視化 */
   XMapWindow(d, w);

                        /* イベントループ */
   while(1) {
     XNextEvent(d, &e);
                        /* ウィンドウの描画と再描画 */
     if(e.type==Expose) {
       XFillRectangle(d, w, DefaultGC(d, s), 20, 20, 10, 10);
       XDrawString(d, w, DefaultGC(d, s), 50, 50, "Hello, World!",strlen("Hello, World!"));
     }
                        /* キー押下で終了 */
     if(e.type==KeyPress)
       break;
   }

                        /* サーバとのコネクションを閉じる */
   XCloseDisplay(d);

   return 0;
 }
クライアントは XOpenDisplay を呼び出すことでサーバとのコネクションを生成する。そして XCreateSimpleWindow でウィンドウ生成を要求する。XMapWindow を別途呼び出すことで、ウィンドウが画面上に見えるようになる。
四角形は XFillRectangle 呼び出しで描画される。この操作はウィンドウが生成された後でないと実行できない。しかも、一回呼び出すだけでは十分ではない。前述したようにウィンドウの内容は常に保持されるとは限らない。例えば、ウィンドウが別のウィンドウの下に隠され、再び上に出てきたとき、再描画が必要な場合がある。このため、上記プログラムでは、Expose イベントを受け取るたびに再描画を行っている。
したがって、ウィンドウの中身の描画はイベントループ内で行われる。ループに入る前に、アプリケーションは受け取りたいイベントを選択しており、上記の例では XSelectInput でそれを行っている。イベントループは入って来るイベントを待ちうける。イベントがキー押下だった場合、このアプリケーションは終了する。expose イベントなら、ウィンドウの中身を再描画している。XNextEvent を呼び出すと、キュー上にイベントがなければ output buffer がフラッシュされる。