XCBの基本

[[http://xcb.freedesktop.org/tutorial/|XCBのチュートリアル]]を見ながら写経。

== Xlibと何が違うのか ==
今のところの理解。
 * Xlibよりダイレクトにプロトコルを操作する
  * 後発だがより低レイヤー。ほぼプロトコルと1対1対応
   * Xlibには複数のリクエストを送る関数がある
  * ライブラリの実装自体が仕様(xcb-proto)から自動生成されているらしい
 * 同期/非同期の自由度
  * Xlib: 同期/非同期が選べない
   * レスポンスを得るものについては、リクエストの送信とレスポンスの取得が不可分
   * リクエストだけのものでエラーが発生した場合、事前に設定したエラーハンドラが呼ばれる
  * XCB: 同期/非同期が選べる
   * リクエストを投げる際に関数の戻り値としてcookieを得る
   * cookieを使ってレスポンスあるいはエラーを得る
    * 複数のリクエストを送って複数のレスポンスを得る場合、先にリクエストを全部送ってからレスポンスを取得すると効率が良い
    * エラー取得のタイミングを自分で制御できる
   * リクエストキューをflushするタイミングをある程度制御できる。逆に言えば、必要な場面でflushを忘れるとリクエストが実行されない
 * 適切に使えばXlibよりかなり速い
  * [[http://xcb.freedesktop.org/tutorial/|ここ]]にある500個のアトム値を作るサンプルだと、手元の環境ではXCBの方が10から20倍速かった
 * そもそも今のXlibはXCBの上に互換レイヤーとして実装されているとかいないとか

== 矩形を描画するだけの何の面白みもないプログラム ==
{{{#!highlight c
#include <stdio.h>
#include <stdlib.h>
#include <xcb/xcb.h>

xcb_connection_t *conn;
xcb_screen_t *screen;
xcb_window_t win;
xcb_gcontext_t gc;

int main(void)
{
    int screen_num, i;
    xcb_screen_iterator_t iter;
    uint32_t mask, values[10];

    conn = xcb_connect(NULL, &screen_num);

    /* get screen information */
    iter = xcb_setup_roots_iterator(xcb_get_setup(conn));
    for (i = 0; i < screen_num; ++i)
        xcb_screen_next(&iter);
    screen = iter.data;
    puts("screen information:");
    printf("%dx%d, root = %x\n", screen->width_in_pixels,
        screen->height_in_pixels, screen->root);

    /* create gc */
    gc = xcb_generate_id(conn);
    mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES;
    values[0] = screen->black_pixel;
    values[1] = 0;
    xcb_create_gc(conn, gc, screen->root, mask, values);

    /* create window */
    win = xcb_generate_id(conn);
    mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
    values[0] = screen->white_pixel;
    values[1] = XCB_EVENT_MASK_EXPOSURE;
    xcb_create_window(conn, XCB_COPY_FROM_PARENT,
        win, screen->root, 0, 0, 200, 100, 1,
        XCB_WINDOW_CLASS_INPUT_OUTPUT,
        screen->root_visual, mask, values);

    /* show window */
    xcb_map_window(conn, win);
    xcb_flush(conn);

    xcb_generic_event_t *event;
    while (event = xcb_wait_for_event(conn)) {
        /* SendEventリクエストによって送信されたイベントの場合、response_typeのMSBが1になる
           そのままではイベントの種類を正しく判別できないので~0x80でマスクしてMSBを除去する
           詳しくはxcb_event.hを参照*/
        switch (event->response_type & ~0x80) {
        case XCB_EXPOSE:
            xcb_poly_rectangle(conn, win, gc, 1,
                &(xcb_rectangle_t){ 10, 10, 100, 50 }); /* C99の複合リテラル */
            /* どうやらXCBでもリクエストはバッファリングされるらしい
               この場合特にバッファをフラッシュする要因が無いようなので、
               明示的にxcb_flushしないとリクエストがサーバに送られない = 描画されない */
            xcb_flush(conn);
            break;
        default:
            break;
        }
        free(event);
    }
    xcb_disconnect(conn);
    return 0;
}
}}}
 * ウィンドウやGCのためのXIDは先に`xcb_generate_id`によって自分で作る
 * `values`はただの配列なので値の順序が重要(`mask`の値の小さい順に並べる)