ウィンドウを表示する手順
Wayland には「ウィンドウを作成する」という関数はありません。
ウィンドウを表示する場合は、ウィンドウのイメージを用意し、その中にウィンドウのイメージを書き込み、それをウィンドウとして表示させる、という手順が必要になります。
ウィンドウを表示する場合は、ウィンドウのイメージを用意し、その中にウィンドウのイメージを書き込み、それをウィンドウとして表示させる、という手順が必要になります。
イメージの種類
Wayland では、画面上に表示できるイメージは、2種類あります。
「共有メモリ」を使って作る、直接アクセス出来るバッファと、「OpenGL ES (EGL)」のイメージです。
OpenGL を使って描画するなら EGL を使った方が良いですが、直接バッファにアクセスして描画したいなら、共有メモリのバッファを使います。
ただし、両方とも、イメージを作成するまでの手順は長いです。
ここでは、共有メモリを使ったイメージバッファを使用します。
「共有メモリ」を使って作る、直接アクセス出来るバッファと、「OpenGL ES (EGL)」のイメージです。
OpenGL を使って描画するなら EGL を使った方が良いですが、直接バッファにアクセスして描画したいなら、共有メモリのバッファを使います。
ただし、両方とも、イメージを作成するまでの手順は長いです。
ここでは、共有メモリを使ったイメージバッファを使用します。
共有メモリのイメージを使う場合
- wl_shell、wl_compositor、wl_shm をバインド。
- wl_compositor から wl_surface を作成 (ウィンドウに表示するイメージの操作)
- wl_shell から wl_shell_surface を作成 (ウィンドウの操作)
- wl_shm から wl_shm_pool を作成。
- wl_shm_pool から wl_buffer を作成 (イメージのバッファ)
- ウィンドウの内容をイメージバッファに書き込んで描画し、その内容を画面上に表示するように、wl_surface に要求する。
- その後、サーバーがイメージバッファにアクセスし、デスクトップ画面に合成する。
プログラム
まずは、サーバーが対応している、共有メモリイメージのフォーマットを列挙してみます。
<03_shmformat.c>
$ 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) { //wl_shm をバインド 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; //wl_registry 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 をバインドします。
現時点での最大バージョンは「1」なので、1 で固定しています。
そして、バインドと同時に、wl_shm_add_listener() で、フォーマット列挙のためのハンドラをセットしています。
サーバーで対応しているイメージフォーマットの識別子が、一つずつ送られてきます。
識別子は wayland-client-protocol.h にて、enum wl_shm_format の列挙型で定義されており、色々なフォーマットがあります。
値を見てみると、WL_SHM_FORMAT_ARGB8888、WL_SHM_FORMAT_XRGB8888 以外は、上位バイトから順に「ASCII 文字 x 4」で構成されていることがわかります。
基本的に、WL_SHM_FORMAT_ARGB8888 と WL_SHM_FORMAT_XRGB8888 は常に対応されていると見てよさそうです。
それ以外のフォーマットに関しては、各サーバーの実装によります。
現時点での最大バージョンは「1」なので、1 で固定しています。
そして、バインドと同時に、wl_shm_add_listener() で、フォーマット列挙のためのハンドラをセットしています。
wl_shm : format イベント
static void _shm_format(void *data,struct wl_shm *shm,uint32_t format) { ... }
サーバーで対応しているイメージフォーマットの識別子が、一つずつ送られてきます。
識別子は wayland-client-protocol.h にて、enum wl_shm_format の列挙型で定義されており、色々なフォーマットがあります。
enum wl_shm_format { /** * 32-bit ARGB format, [31:0] A:R:G:B 8:8:8:8 little endian */ WL_SHM_FORMAT_ARGB8888 = 0, /** * 32-bit RGB format, [31:0] x:R:G:B 8:8:8:8 little endian */ WL_SHM_FORMAT_XRGB8888 = 1, /** * 8-bit color index format, [7:0] C */ WL_SHM_FORMAT_C8 = 0x20203843, ...
値を見てみると、WL_SHM_FORMAT_ARGB8888、WL_SHM_FORMAT_XRGB8888 以外は、上位バイトから順に「ASCII 文字 x 4」で構成されていることがわかります。
基本的に、WL_SHM_FORMAT_ARGB8888 と WL_SHM_FORMAT_XRGB8888 は常に対応されていると見てよさそうです。
それ以外のフォーマットに関しては、各サーバーの実装によります。
wl_display_roundtrip()
wl_display_roundtrip() は、クライアントからの要求を、サーバーが処理するまで待つ関数です。
ここでは、この関数を2回実行していますが、
1回目は wl_registry のイベントが処理され、2回目は wl_shm のイベントが処理されています。
wl_registry の global イベント内で、wl_shm をバインドしていますが、ここで生成された wl_shm のイベントは、1回目の wl_display_roundtrip() では実行されないので、もう一度実行しています。
よって、wl_registry を使って行う、バインドなどの初期化処理に関しては、イベントループ内で実行させるのではなく、wl_display_roundtrip() を使って、すべてのイベントなどの処理が終わるまで待ち、必要な情報をすべて取得してから、イベントループへ移行するのが一番良い方法です。
今は wl_display_roundtrip() を数回実行するだけで十分ですが、もう少し複雑になってきたら、それらの方法を解説します。
ここでは、この関数を2回実行していますが、
1回目は wl_registry のイベントが処理され、2回目は wl_shm のイベントが処理されています。
wl_registry の global イベント内で、wl_shm をバインドしていますが、ここで生成された wl_shm のイベントは、1回目の wl_display_roundtrip() では実行されないので、もう一度実行しています。
初期化処理に関して
Wayland におけるクライアントプログラムでは、必要な各グローバルオブジェクトをバインドして、ポインタを取得しなければ、何事も始まりません。よって、wl_registry を使って行う、バインドなどの初期化処理に関しては、イベントループ内で実行させるのではなく、wl_display_roundtrip() を使って、すべてのイベントなどの処理が終わるまで待ち、必要な情報をすべて取得してから、イベントループへ移行するのが一番良い方法です。
今は wl_display_roundtrip() を数回実行するだけで十分ですが、もう少し複雑になってきたら、それらの方法を解説します。