Wayland - ウィンドウ用イメージの作成について

ウィンドウ用イメージの作成について
まず Wayland でウィンドウを作成しないと何も始まらないのですが、Wayland では、「ウィンドウを作成する」 という関数はありません。

Wayland の場合、ウィンドウを表示するまでには複数の手順が必要です。
以下は、共有メモリを使ってイメージバッファを作る場合の手順です。


上記のように、クライアントでは、ウィンドウの中身となるイメージを用意する必要があり、ウィンドウを操作するには、それ専用のオブジェクトが必要になります。

イメージの種類
Wayland では、画面上に表示できるイメージは、2種類あります。
共有メモリを使って作る直接アクセス出来るバッファと、OpenGL ES (EGL) のイメージです。

OpenGL を使って内容を描画するなら EGL を使った方が良いですが、
直接バッファにアクセスして描画したいなら、共有メモリのバッファを使った方が良いでしょう。
ただし、両方共、イメージを作成するまでの手順が長いです。

この講座では、基本的に、共有メモリを使ったイメージバッファを使っていきます。
イメージフォーマットの列挙
まずは、サーバーが対応しているイメージのフォーマットを列挙してみます。

$ cc -o test 03_shmformat.c -lwayland-client

<03_shmformat.c>
#include <stdio.h>
#include <string.h>
#include <wayland-client.h>

struct wl_shm *g_shm;

//------------- wl_shm

static void _shm_format(void *data,struct wl_shm *shm,uint32_t format)
{
    if(format == WL_SHM_FORMAT_ARGB8888)
        printf("%u: ARGB8888\n", format);
    else if(format == WL_SHM_FORMAT_XRGB8888)
        printf("%u: XRGB8888\n", format);
    else
    {
        printf("0x%08X: %c%c%c%c\n",
            format,
            (char)(format >> 24),
            (char)(format >> 16),
            (char)(format >> 8),
            (char)format);
    }
}

static const struct wl_shm_listener g_shm_listener = { _shm_format };

//------------- wl_registry

static void _registry_global(
    void *data,struct wl_registry *registry,
    uint32_t id,const char *interface,uint32_t version)
{
    if(strcmp(interface, "wl_shm") == 0)
    {
        g_shm = wl_registry_bind(registry, id, &wl_shm_interface, 1);

        wl_shm_add_listener(g_shm, &g_shm_listener, NULL);
    }
}

static void _registry_global_remove(void *data,struct wl_registry *registry,uint32_t id)
{

}

static const struct wl_registry_listener g_reg_listener = {
    _registry_global, _registry_global_remove
};

//------------

int main(void)
{
    struct wl_display *display;
    struct wl_registry *reg;

    display = wl_display_connect(NULL);
    if(!display) return 1;

    reg = wl_display_get_registry(display);
    wl_registry_add_listener(reg, &g_reg_listener, NULL);

    //wl_registry のイベントを待つ
    wl_display_roundtrip(display);

    //wl_shm のイベントを待つ
    wl_display_roundtrip(display);

    //

    wl_shm_destroy(g_shm);
    wl_registry_destroy(reg);

    wl_display_disconnect(display);

    return 0;
}

実行結果
## GNOME

0: ARGB8888
1: XRGB8888

## weston

0: ARGB8888
1: XRGB8888
0x36314752: 61GR
0x32315559: 21UY
0x3231564E: 21VN
0x56595559: VYUY
解説
wl_shm
まず、共有メモリを使うために、wl_shm をバインドします。
ver 1 の API しか使わないので、バージョンは 1 で固定しています。

そして、バインドと同時に、wl_shm_add_listener() でフォーマット列挙のハンドラをセットしています。

format イベント
使えるフォーマットの識別子が、一つずつ送られてきます。
識別子は、enum wl_shm_format の列挙型で、wayland-client-protocol.h で定義されています。

値を見てみると、WL_SHM_FORMAT_ARGB8888 = 0、WL_SHM_FORMAT_XRGB8888 = 1 で、
それ以外は、上位バイトから順に ASCII 文字×4 で構成されていることがわかります。
WL_SHM_FORMAT_ARGB8888WL_SHM_FORMAT_XRGB8888 は基本的なフォーマットです。

実際の Wayland 環境の GNOME で試した所、ARGB と XRGB しか対応していませんでしたが、weston 上では他の形式も対応していました。
この辺りは、Wayland サーバー側が各種フォーマットを実装するかによって、対応が分かれるようです。
wl_display_roundtrip()
ここでは、wl_display_roundtrip() を2回実行していますが、
1回目は wl_registry のイベントが処理され、2回目は wl_shm のイベントが処理されています。

wl_registry のイベントハンドラ内で追加生成された wl_shm のイベントは、1回目の wl_display_roundtrip() 内では実行されないので、もう一回実行しています。

Wayland では、バインドしてオブジェクトのポインタを取得しないと何も出来ないので、
wl_registry で行う初期化処理に関しては、イベントループ内で実行させるのではなく、
すべてのイベントが終わるまで待って、すべてのポインタや初期情報を取得する方が良いです。

もう少し複雑になってきたら、きちんとした同期方法を解説しますが、今は wl_display_roundtrip() を複数回実行するだけで十分です。