X11: ウィンドウの作成

ウィンドウの作成
それでは、実際に X のウィンドウを作成してみます。

(0, 0) の位置に 200 x 200 のサイズのウィンドウを作成して、表示した後、イベントを処理します。
ウィンドウ内でマウスを左クリックするなどして、なんらかのボタンを押すと、イベントのループを抜けて終了します。

※ウィンドウの装飾上にある「閉じる」ボタンを押した場合、プログラムが強制終了します。閉じるボタンが押された時のイベントを処理していないためです。

※ウィンドウの位置は、ウィンドウマネージャによって再配置される場合があるため、(0, 0) の位置に表示されない場合があります。
プログラム
<04-window.c>
#include <X11/Xlib.h>

int main(int argc,char **argv)
{
    Display *disp;
    XSetWindowAttributes attr;
    Window win;
    XEvent ev;
    
    disp = XOpenDisplay(NULL);
    if(!disp) return 1;

    //ウィンドウ作成

    attr.background_pixel = 0;
    attr.event_mask = ButtonPressMask;

    win = XCreateWindow(disp, DefaultRootWindow(disp),
        0, 0, 200, 200, 0,
        CopyFromParent, CopyFromParent, CopyFromParent,
        CWBackPixel | CWEventMask, &attr);

    XMapWindow(disp, win);

    //イベント

    while(1)
    {
        XNextEvent(disp, &ev);

        switch(ev.type)
        {
            case ButtonPress:
                goto END;
        }
    }

    //

END:
    XDestroyWindow(disp, win);

    XCloseDisplay(disp);

    return 0;
}
関数
イベント処理に関しては、後述します。
ウィンドウの作成
Window XCreateWindow(Display *display, Window parent,
    int x, int y, unsigned int width, unsigned int height,
    unsigned int border_width, int depth, unsigned int class, Visual *visual,
    unsigned long valuemask, XSetWindowAttributes *attributes);

XCreateWindow は、X のウィンドウを作成します (子ウィンドウも含む)。
ほとんどの属性をデフォルト値にする、よりシンプルな XCreateSimpleWindow 関数もありますが、ここでは説明しません。

返される Window は XID 型です。

ウィンドウを作成した時、ウィンドウは常に非表示の状態です。

parent親ウィンドウ。
ルートウィンドウを指定すると、トップレベルウィンドウになります。

スクリーンごとに一つのルートウィンドウがあるので、X サーバーと接続した時に指定された、デフォルトのスクリーンにおけるルートウィンドウを指定したい場合は、DefaultRootWindow(display) マクロなどを使います。
x,yウィンドウの位置を、親ウィンドウの左上原点を基準にして指定します。

ただし、ウィンドウの初期位置 (ウィンドウの作成後、最初にウィンドウが表示される時の位置) は、ウィンドウマネージャが管理する場合があるので、指定した位置が適用されないこともあります。
width,heightウィンドウのサイズを指定します。
ウィンドウの境界枠や、ウィンドウマネージャーによって付けられるウィンドウ装飾のサイズは含みません。
border_widthウィンドウの境界枠の幅 (px)。
タイトルバーなどのウィンドウ装飾とは別の、ウィンドウ中身の周りに付く境界線です。
通常のウィンドウでは、ウィンドウマネージャによって別途ウィンドウ装飾が付けられるので、基本的に 0 にします。
depthウィンドウのイメージの深さ (ビット数)。
CopyFromParent の場合、親ウィンドウと同じ値にする。
ウィンドウが表示されるスクリーンでサポートされている必要があるが、親と同じ深さである必要はない。
class作成されるウィンドウのクラス。

InputOutput : 画面に出力できるウィンドウ。
InputOnly : 画面に出力されず、入力イベントのみを制御するウィンドウ。
CopyFromParent : 親と同じ。
visual使用するビジュアル。
CopyFromParent の場合、親と同じものを使う。

スクリーンでは複数のビジュアルがサポートされているので、いずれかを使用します。
スクリーンにはデフォルトのビジュアルが指定されているので、基本的にはそれを使います。
valuemaskXSetWindowAttributes 構造体で使用する値のマスクを指定する。
以下の値を OR で結合。

CWBackPixmap, CWBackPixel, CWBorderPixmap, CWBorderPixel, CWBitGravity, CWWinGravity,
CWBackingStore, CWBackingPlanes, CWBackingPixel, CWOverrideRedirect, CWSaveUnder,
CWEventMask, CWDontPropagate, CWColormap, CWCursor
attributesウィンドウの属性を指定した XSetWindowAttributes 構造体のポインタ
戻り値作成されたウィンドウの XID が返る。
ウィンドウを表示
void XMapWindow(Display *display, Window w);

ウィンドウをマップ (表示) します。
X では、ウィンドウを表示することを「マップ」、非表示にすることを「アンマップ」と呼びます。
ウィンドウを破棄
void XDestroyWindow(Display *display, Window w);

指定されたウィンドウと、そのすべての子ウィンドウを破棄し、ウィンドウごとに DestroyNotify イベントを生成させます。

XCloseDisplay() で接続を閉じた場合、クライアントが作成したすべてのウィンドウはデフォルトで破棄されるので、明示的にウィンドウを破棄しなくても、問題はありません。
ウィンドウの属性
ウィンドウの属性は、XSetWindowAttributes 構造体で定義します。

typedef struct {
   Pixmap background_pixmap;
   unsigned long background_pixel;
   Pixmap border_pixmap;
   unsigned long border_pixel;
   int bit_gravity;
   int win_gravity;
   int backing_store;
   unsigned long backing_planes;
   unsigned long backing_pixel;
   Bool save_under;
   long event_mask;
   long do_not_propagate_mask;
   Bool override_redirect;
   Colormap colormap;
   Cursor cursor;
} XSetWindowAttributes;

ウィンドウ属性は、ウィンドウ作成時に指定するか、作成後に XChangeWindowAttributes() などの関数で変更することができます。
Pixmap background_pixmap
ウィンドウの背景のパターンとして描画される Pixmap (バッファ操作できないイメージ)。
ウィンドウと同じ depth である必要がある。

None (デフォルト) で、背景ピクスマップなし。

ParentRelative で、親ウィンドウの background_pixmap を使用する (親が None なら、None となる)。
ただし、その Pixmap が、ウィンドウと同じ depth である必要がある。
親の Pixmap のコピーは作成されず、背景描画で必要になるたびに、親の Pixmap が参照される。

ウィンドウに設定した後、指定した Pixmap のリソースは X サーバー内で保持されるので、以降プログラム内で使用することがないのであれば、設定後に削除しても良い。
unsigned long background_pixel
ウィンドウの背景色のピクセル値。
デフォルトで未定義。
Pixmap border_pixmap
ウィンドウの境界枠のパターンとして描画される Pixmap。

CopyFromParent (デフォルト) で、親の border_pixmap をコピーする。
unsigned long border_pixel
ウィンドウの境界枠のピクセル値。
デフォルトで未定義。
int bit_gravity
ウィンドウのサイズが変更された時、現在のウィンドウの描画内容を、どのような原点を基準にして保持するか。
デフォルトは ForgetGravity。

#define ForgetGravity       0
#define NorthWestGravity    1
#define NorthGravity        2
#define NorthEastGravity    3
#define WestGravity         4
#define CenterGravity       5
#define EastGravity         6
#define SouthWestGravity    7
#define SouthGravity        8
#define SouthEastGravity    9
#define StaticGravity       10

たとえば、SouthEastGravity に設定した場合、ウィンドウがリサイズされた時は、ウィンドウの右下を基準にして、現在の描画内容を維持します (拡張された左上部分は、描画が行われなければ未定義)。

ForgetGravity の場合、バッキングストアやセーブアンダーで、ウィンドウの内容を保持することが要求された場合でも、サイズ変更後は常にウィンドウの内容が破棄され、ウィンドウの背景を描画した上で、Expose イベントが生成されます。

基本的にはデフォルトのままで、ウィンドウサイズが変更された時は、常にウィンドウ全体を再描画する方が望ましいです。
int win_gravity
親のウィンドウサイズが変更された時、子ウィンドウである自分のウィンドウを、どのような原点を基準にして移動するか。
デフォルトは NorthWestGravity (親の左上原点から移動しない)。

#define UnmapGravity        0 //ForgetGravity を置き換え
#define NorthWestGravity    1
#define NorthGravity        2
#define NorthEastGravity    3
#define WestGravity         4
#define CenterGravity       5
#define EastGravity         6
#define SouthWestGravity    7
#define SouthGravity        8
#define SouthEastGravity    9
#define StaticGravity       10

たとえば SouthEastGravity に設定した場合、親の右下を原点として、子ウィンドウが同じ位置になるように調整されます。
再配置された場合、GravityNotify イベントが生成されます。

UnmapGravity の場合、親がリサイズされたとき、子はアンマップ (非表示に) され、UnmapNotify イベントが生成されます。
int backing_store
ウィンドウの描画内容を保存するか。
DoesBackingStore(Screen *) マクロで、指定スクリーンがバッキングストアをサポートしているかを判断できます。

NotUseful内容を維持しない (デフォルト)
WhenMappedウィンドウがマップされたときに、内容を保存する
Alwaysウィンドウがマップされていないときでも、常に内容を維持させ、親ウィンドウからはみ出る部分も保持する
unsigned long backing_planes
backing_store および save_under で保存する必要があるデータにおいて、どのビットプレーンを保持するか。
デフォルトでは、すべてのビットが 1 に設定されています。
unsigned long backing_pixel
backing_store および save_under で保存したデータを復元する時、backing_planes のビットが 0 の部分は、この値が使われます。
Bool save_under
このウィンドウの下に隠れているウィンドウの描画内容を保存するか。
デフォルトは False。

DoesSaveUnders(Screen *) マクロで、指定スクリーンがサーブアンダーをサポートしているか判断できます。
long event_mask
このウィンドウが受け取るイベントのマスク。
デフォルトは NoEventMask (0)。
詳細は後述します。
long do_not_propagate_mask
このウィンドウが、X サーバーから送られてきたポインタ/キーイベントを受け取らなかった場合 (event_mask でそのイベントのマスクが設定されていなかった場合)、親ウィンドウや、さらにその上のウィンドウにイベントを送ることができます。
親ウィンドウに送らずに、破棄するイベントのマスクを指定します。

デフォルトは NoEventMask (0) で、すべてのイベントが親に送られます。

指定できるのは、以下のマスクだけです。

KeyPressMask、KeyReleaseMask、ButtonPressMask、ButtonReleaseMask、PointerMotionMask
Button1MotionMask、Button2MotionMask、Button3MotionMask
Button4MotionMask、Button5MotionMask、ButtonMotionMask
Bool override_redirect
デフォルトは False。
ポップアップウィンドウなどで、ウィンドウマネージャによる制御を行いたくない時に True にします。

True の場合、このウィンドウに対して、表示状態や位置・サイズの変更などが行われた時、親が SubstructureRedirectMask のイベントマスクを選択していても、それが無効になるようにします。

ウィンドウマネージャは、ルートウィンドウで SubstructureRedirectMask のイベントマスクを選択することで、他のクライアントが作成したウィンドウが表示された時や、位置・サイズが変更された時などに、そのイベントを自身のクライアントで受け取って処理した後、イベントをリダイレクトします。
これによって、ウィンドウが表示された時に、ウィンドウマネージャがウィンドウ装飾を付けたりすることができます。

ポップアップウィンドウなどで、ウィンドウ装飾が必要なく、ウィンドウ位置やサイズも調整されたくない場合は、この値を True にして、ウィンドウマネージャによる制御を無効にします。
Colormap colormap
ウィンドウに関連付けられるカラーマップ。

CopyFromParent (デフォルト) の場合、親ウィンドウのカラーマップがコピーされます。
ただし、このウィンドウが、親ウィンドウと同じビジュアルであること。
Cursor cursor
このウィンドウ内にマウスカーソルがある場合、表示されるカーソル画像 (XID)。

None (デフォルト) の場合、親ウィンドウのカーソルが使用され、親のカーソルが変更された場合は、このウィンドウのカーソルも即座に変更されます。

つまり、ルートウィンドウに対して、デフォルトのカーソルが設定されていて、各子ウィンドウのカーソルが None の場合、ルートウィンドウのカーソルが変更された時も、すべてのウィンドウで常に同じカーソルが使用されるということです。

指定したカーソルは X サーバー内で保持されるので、設定後すぐに XFreeCursor() で解放することもできます。