X11: はじめに

X Window System について
X Window System は、ウィンドウによるグラフィック画面を出力するためのシステムです。
単純に X と呼ばれたり、現在のバージョンが 11 なので、X11 とも呼ばれます。

X を動作させるには「X サーバー」が必要です。
各アプリケーションは、Xlib を使って、起動している X サーバーと接続し、ウィンドウを操作します。
X サーバーと接続している各アプリケーションは、「クライアント」と呼ばれます。

サーバーやクライアントと名のつく通り、X サーバーとクライアントの内部では、ネットワーク通信と同じ方法でやりとりが行われています。
そのため、クライアントは、ネット上の他のマシンで動作している X サーバーと接続することもできます。
(通常のデスクトップでは、同じマシン上で X サーバーが起動しています)

ただし、X 自体には単純なウィンドウを作る仕組みしかないので、ボタンやメニューなどの UI を表示したい場合は、クライアントが独自にその仕組みを作って描画するか、GTK+ や Qt などの既存のツールキットを使う必要があります。

また、X そのものには、ウィンドウフレームの装飾など、デスクトップに関する詳細な機能も定義もないため、それらは、ICCCM や EWMH (Extended Window Manager Hints) で定義されている仕様に基づいて、実装する必要があります。

ここでは、Xlib ライブラリを使った、低レベル API によるクライアントプログラムを作っていきます。
X.Org
X は、元々 XFree86 によって実装が行われていましたが、現在では X.Org によって実装が行われています。

X サーバーや Xlib ライブラリ、各 X 拡張機能やユーティリティアプリケーションなども、X.Org が管理しています。

Xlib の API リファレンスや、各拡張機能のドキュメントは、以下にあります。
https://www.x.org/releases/current/doc/index.html

各ソースコードは、こちらにあります。
https://gitlab.freedesktop.org/xorg
Xlib
Xlib は、クライアントが X を使うために使用する、低レベルライブラリです。

Arch Linux なら libx11 パッケージ、Ubuntu/debian なら libx11-dev パッケージに、ヘッダファイルがあります。
他にも、X の拡張機能を使う場合は、各パッケージが必要になります。

ライブラリlibX11.so
インクルードファイルX11/Xlib.h : メインのヘッダ
X11/Xutil.h : 各ユーティリティ関数など
X11/Xatom.h : 事前定義されているアトムの宣言
X11/keysym.h : キーシンボル (KeySym) の値
X11/Xresource.h : リソースマネージャ関連
汎用タイプ
Xlib では、以下の型や値が定義されています。

Boolブール型 (int)。
True (1) または False (0) が値となります。
Nonenull のリソース ID または Atom を示す値 (0)
XID汎用リソース ID (unsigned long)
XPointer汎用のポインタ型 (char *)
ポインタの解放
X 関数によって、メモリ確保されたポインタが返された場合、基本的には XFree(ptr) で解放します。
ただし、専用の解放関数がある場合は、その関数で解放します。
64bit OS 時
ウィンドウなどのリソース ID は、XID (unsigned long) で定義されているので、64bit OS の場合は 8 byte の整数になりますが、実際には 32bit 整数としてしか扱われていません。

X サーバー内部では 32bit 整数としての扱いになっていても、Xlib ライブラリで値を取得したりする際は、long 型の整数に変換されることになります。

例えば、32bit RGBA イメージを、CARD32 型の配列として送信・受信する場合、1px を long 型の整数として扱わなければならなくなるので、注意してください。
プログラム
クライアントが X サーバーと通信を行うためには、まず XOpenDisplay() を実行して、X サーバーと接続する必要があります。
接続を閉じる場合は、XCloseDisplay() を使います。

<01-connect.c>
#include <stdio.h>
#include <X11/Xlib.h>

int main(int argc,char **argv)
{
    Display *disp;
    
    disp = XOpenDisplay(NULL);
    if(!disp)
    {
        printf("failed\n");
        return 1;
    }

    printf("connected\n");

    XCloseDisplay(disp);

    return 0;
}

-lX11 で、Xlib ライブラリをリンクしてください。

$ cc -o run 01-connect.c -lX11
関数
サーバーと接続
Display *XOpenDisplay(char *display_name);

指定ディスプレイ名の X サーバと接続し、 X サーバーに関する情報を含む Display 構造体のポインタを返します (NULL で失敗)。

X において、「ディスプレイ」はモニタを示す言葉ではなく、返された Display 構造体は、1つのクライアント接続に相当します。

display_name
display_name 引数で、使用するディスプレイや通信ドメインを指定します。

Linux などでは、NULL を指定すると、DISPLAY 環境変数の値が使用されます。
DISPLAY 環境変数の値は、「echo $DISPLAY」で確認でき、基本的に ":0" となっています。

Linux などでは、以下の形式の文字列で指定します。

<protocol>/<hostname>:<number>.<screen_number>

protocolプロトコルファミリまたはプロトコルファミリのエイリアス。
サポートされるプロトコルファミリは実装に依存します。
プロトコルを指定しない場合は、"/" を記述しないこと。
hostnameホストマシンの名前。
ホスト名の後には、単一コロン (:) または二重コロン (::) を続けます。
numberホストマシン上のディスプレイサーバーの番号。
必要に応じて、この後にピリオド (.) を付けることができます。
1 つの CPU に対して複数のディスプレイを搭載でき、通常は 0 から始まる番号が付けられます。
screen_numberサーバーで使用するスクリーン番号 (0〜)。
1つの X サーバーには、複数のスクリーンを含めることができます。

protocol が「tcp」「inet」「inet6」または指定なし、そして hostname がホストマシン名で、単一のコロン (:) でホスト名とディスプレイ番号が区切られている場合、TCP を使用して接続されます。
ホスト名がない場合は、ローカル通信になります。
接続を閉じる
void XCloseDisplay(Display *display);

指定ディスプレイの X サーバーへの接続を閉じ、そのクライアントで作成されたウィンドウや Pixmap などの各リソースを、すべて破棄します。
ただし、XSetCloseDownMode() を使うと、各リソースを破棄せずに X サーバー上に残すことができます。
リソースについて
XCloseDisplay() で接続を閉じた場合、デフォルトで、クライアントが作成したウィンドウなどのリソースはすべて破棄されます。

X では、ウィンドウやその他のリソースは、すべて XID 型 (unsigned long) の整数 ID で管理されます。

クライアントがウィンドウなどの作成を要求した場合、X サーバーは XID 型 (もしくは XID の別名) の整数値を返します。
これはポインタ値ではありません。
X サーバーは、各リソースを作成したあと、それを識別するための一意の整数値を割り当てて、それを返します。

そのため、作成された X リソースは、各クライアントごとに X サーバーによって保持され、管理されているので、クライアントの接続を閉じる際は、不要になった各リソースを個別に削除しなくても、XCloseDisplay() 時にまとめて削除することができます (明示的に XFree 関数などで解放するものは別)。

ただし、場合によっては、特殊な用途により、作成したウィンドウやリソースを X サーバー上に残したい場合があります。
その場合は XSetCloseDownMode() を使います。

XSetCloseDownMode() でリソースを残すように設定した場合、XCloseDisplay() で接続を閉じた後も、クライアントが作成したリソースは残ります。
その後、他のクライアント上で、XKillClient() を使って、そのリソース ID を指定すると、サーバーに残ったリソースを解放させることができます。
XSetCloseDownMode
void XSetCloseDownMode(Display *display, int close_mode);

XCloseDisplay() で接続を閉じた時に、クライアントが作成したリソースをどうするかを指定します。

[close_mode]
DestroyAllクライアントが作成した全てのウィンドウを破棄し、クライアントによって作成された各リソースを解放する。
接続開始時のデフォルト。
RetainPermanentリソースを保持する。
XKillClient でリソースIDを指定した場合、リソースは破棄される。
XKillClient で AllTemporary を指定した場合は、破棄されない。
RetainTemporaryリソースを保持する。
XKillClient でリソースIDを指定した場合、リソースは破棄される。
XKillClient で AllTemporary を指定した場合は、XSetCloseDownMode() で RetainTemporary が設定されている、すべてのクライアントのリソースが破棄される。