出力画面
ここでは、出力画面の情報 (モニタの解像度など) を取得します。
10_output.c
$ cc -o test 10_output.c -lwayland-client
10_output.c
/****************************** * 出力画面の情報 ******************************/ #include <stdio.h> #include <string.h> #include <stdlib.h> #include <wayland-client.h> //------------ typedef struct { struct wl_list link; struct wl_output *output; uint32_t id,ver; }output_item; struct wl_list g_list; struct wl_display *g_display; int g_display_callback_cnt = 0; //------------ //---------------------- // リストデータ //---------------------- /* 全アイテムを削除 */ static void _list_destroy(void) { output_item *pi,*tmp; wl_list_for_each_safe(pi, tmp, &g_list, link) { if(pi->ver >= WL_OUTPUT_RELEASE_SINCE_VERSION) wl_output_release(pi->output); else wl_output_destroy(pi->output); free(pi); } } /* アイテム追加 */ static void _list_append(uint32_t id,uint32_t ver,struct wl_output *output) { output_item *pi; pi = (output_item *)calloc(1, sizeof(output_item)); if(!pi) return; pi->output = output; pi->id = id; pi->ver = ver; wl_list_insert(g_list.prev, &pi->link); } /* 指定 ID を削除 */ static void _list_delete(uint32_t id) { output_item *pi; wl_list_for_each(pi, &g_list, link) { if(pi->id == id) { if(pi->ver >= WL_OUTPUT_RELEASE_SINCE_VERSION) wl_output_release(pi->output); else wl_output_destroy(pi->output); wl_list_remove(&pi->link); free(pi); break; } } } //---------------------- // wl_output //---------------------- static void _output_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, int32_t physical_width, int32_t physical_height, int32_t subpixel, const char *make, const char *model, int32_t transform) { printf("<geometry>\n" " x:%d, y:%d, physical_width:%d mm, physical_height:%d mm\n" " subpixel:%d, make:'%s', model:'%s', transform:%d\n", x, y, physical_width, physical_height, subpixel, make, model, transform); } static void _output_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { printf("mode | flags:0x%X, width:%d, heigh:%d, refresh:%d mHz\n", flags, width, height, refresh); } static void _output_done(void *data, struct wl_output *wl_output) { printf("done\n"); } static void _output_scale(void *data, struct wl_output *wl_output, int32_t factor) { printf("scale | factor:%d\n", factor); } static const struct wl_output_listener g_output_listener = { _output_geometry, _output_mode, _output_done, _output_scale, }; //------------------------- // wl_callback (display) //------------------------- static void _display_callback_done(void *data,struct wl_callback *callback,uint32_t time) { wl_callback_destroy(callback); g_display_callback_cnt--; printf("-- display callback done\n"); } static const struct wl_callback_listener g_display_callback_listener = { _display_callback_done }; //------------------------- // sub //------------------------- /* イベントの同期コールバック追加 */ static void _add_display_sync(void) { struct wl_callback *callback; callback = wl_display_sync(g_display); wl_callback_add_listener(callback, &g_display_callback_listener, NULL); g_display_callback_cnt++; } /* wl_output 追加 */ static void _add_output(struct wl_registry *reg,uint32_t id,uint32_t ver) { struct wl_output *output; //output 追加 output = wl_registry_bind(reg, id, &wl_output_interface, ver); _list_append(id, ver, output); wl_output_add_listener(output, &g_output_listener, NULL); //同期 _add_display_sync(); } //--------------------- // wl_registry //--------------------- static void _registry_global( void *data,struct wl_registry *reg,uint32_t id,const char *name,uint32_t ver) { if(strcmp(name, "wl_output") == 0) _add_output(reg, id, (ver > 3)? 3: ver); } static void _registry_global_remove(void *data,struct wl_registry *registry,uint32_t id) { _list_delete(id); } static const struct wl_registry_listener g_reg_listener = { _registry_global, _registry_global_remove }; //---------------- int main(void) { struct wl_registry *reg; g_display = wl_display_connect(NULL); if(!g_display) return 1; //wl_list 初期化 wl_list_init(&g_list); //wl_registry reg = wl_display_get_registry(g_display); wl_registry_add_listener(reg, &g_reg_listener, NULL); _add_display_sync(); //すべてのイベントが終わるまで待つ while(wl_display_dispatch(g_display) != -1 && g_display_callback_cnt != 0); // _list_destroy(); wl_registry_destroy(reg); wl_display_disconnect(g_display); return 0; }
wl_output
モニタの情報を取得するには、wl_output が必要です。
ver 3 では、wl_output_release() が追加されているので、
ver 3 以上を使う場合は、破棄時に wl_output_release() を使い、
それ未満の場合は、wl_output_destroy() を使います。
wl_registry の global イベントでバインドするのですが、他のグローバルオブジェクトと違い、複数のモニタが接続されている場合は、複数の wl_output が存在するため、複数の wl_output をバインドする必要があります。
モニタの情報が送られてきます。
バインドされた時と、各プロパティのいずれかが変更された時に呼ばれます。
モニタが設定可能なモードの情報が送られてきます。
1回は必ず呼ばれ、複数のモードが使用可能な場合は、複数回呼ばれます。
また、現在のモニタのモードが変更された時も、再度送信されます。
ver 3 では、wl_output_release() が追加されているので、
ver 3 以上を使う場合は、破棄時に wl_output_release() を使い、
それ未満の場合は、wl_output_destroy() を使います。
wl_registry の global イベントでバインドするのですが、他のグローバルオブジェクトと違い、複数のモニタが接続されている場合は、複数の wl_output が存在するため、複数の wl_output をバインドする必要があります。
複数の wl_output
複数のモニタが接続されている場合、複数の wl_output が、異なる識別子で存在します。
複数の wl_output に対応するために、各識別子と、バインドした wl_output ポインタを記録しておく必要があります。
今回は、複数の wl_output を格納するリストデータとして、wayland-util.h で定義されている wl_list を使いますが、別のリスト構造を使っても構いません。
複数の wl_output に対応するために、各識別子と、バインドした wl_output ポインタを記録しておく必要があります。
今回は、複数の wl_output を格納するリストデータとして、wayland-util.h で定義されている wl_list を使いますが、別のリスト構造を使っても構いません。
wl_registry : global イベント処理
wl_registry の global イベントで、"wl_output" が来たら、バインドとハンドラ設定をして、識別子と wl_output ポインタをリストデータに追加します。
global_remove イベントが来た時は、そのモニタが外されたりして使えなくなったことを意味するので、リストデータ内からその識別子を検索して、wl_output を破棄します。
global_remove イベントが来た時は、そのモニタが外されたりして使えなくなったことを意味するので、リストデータ内からその識別子を検索して、wl_output を破棄します。
wl_output : geometry イベント
void (*geometry)(void *data, struct wl_output *wl_output, int32_t x, int32_t y, int32_t physical_width, int32_t physical_height, int32_t subpixel, const char *make, const char *model, int32_t transform);
モニタの情報が送られてきます。
バインドされた時と、各プロパティのいずれかが変更された時に呼ばれます。
x, y | 左上の px 位置 |
---|---|
physical_width physical_height | 物理的な幅と高さ (mm)。 モニタの表示部分のサイズとなっています。 VirtualBox 上では、0 となりました。 |
subpixel | モニタのサブピクセルの並び。WL_OUTPUT_SUBPIXEL_UNKNOWN WL_OUTPUT_SUBPIXEL_NONE WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR WL_OUTPUT_SUBPIXEL_VERTICAL_RGB WL_OUTPUT_SUBPIXEL_VERTICAL_BGR |
make | モニタのメーカー名 |
model | モニタのモデル名 |
transform | 回転や反転の状態。WL_OUTPUT_TRANSFORM_NORMAL WL_OUTPUT_TRANSFORM_90 WL_OUTPUT_TRANSFORM_180 WL_OUTPUT_TRANSFORM_270 WL_OUTPUT_TRANSFORM_FLIPPED WL_OUTPUT_TRANSFORM_FLIPPED_90 WL_OUTPUT_TRANSFORM_FLIPPED_180 WL_OUTPUT_TRANSFORM_FLIPPED_270 |
mode イベント
void (*mode)(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh);
モニタが設定可能なモードの情報が送られてきます。
1回は必ず呼ばれ、複数のモードが使用可能な場合は、複数回呼ばれます。
また、現在のモニタのモードが変更された時も、再度送信されます。
flags | フラグ。 WL_OUTPUT_MODE_CURRENT (1) : 現在使われているモード WL_OUTPUT_MODE_PREFERRED (2) : 優先されるモード |
---|---|
width height | 幅と高さ (px)。画面全体のサイズ。 |
refresh | リフレッシュレート (mHz)。 Hz 単位にする場合は、1000 で割る。 |
done イベント
※ ver 2 から
mode イベントなどで、すべての情報の送信が終わった後に送られてきます。
情報をすべて取得した後に何かしらの処理を行いたい場合は、このイベントを受け取った時に行います。
void (*done)(void *data, struct wl_output *wl_output);
mode イベントなどで、すべての情報の送信が終わった後に送られてきます。
情報をすべて取得した後に何かしらの処理を行いたい場合は、このイベントを受け取った時に行います。
scale イベント
※ ver 2 から
画面のスケーリング (拡大) 情報を受け取ります。
バインド直後、またはスケールが変更された時に送信されます。
もし送られてこなかった場合は、値を 1 として仮定する必要があります。
factor の値が 1 より大きい場合、コンポーザがレンダリング時にサーフェスバッファを拡大させます。
主にモバイル端末などで、モニタが高解像度なことにより、そのままでは画面が見にくい場合に使われます。
クライアントがスケーリングに対応する場合は、wl_surface_set_buffer_scale() に factor の値を渡して、倍率を適用します。
void (*scale)(void *data, struct wl_output *wl_output, int32_t factor);
画面のスケーリング (拡大) 情報を受け取ります。
バインド直後、またはスケールが変更された時に送信されます。
もし送られてこなかった場合は、値を 1 として仮定する必要があります。
factor の値が 1 より大きい場合、コンポーザがレンダリング時にサーフェスバッファを拡大させます。
主にモバイル端末などで、モニタが高解像度なことにより、そのままでは画面が見にくい場合に使われます。
クライアントがスケーリングに対応する場合は、wl_surface_set_buffer_scale() に factor の値を渡して、倍率を適用します。
イベントの同期
今回の場合、wl_output が複数回バインド&ハンドラ設定される可能性があるので、wl_registry のハンドラ内で生成されたイベントが、すべて終わるまで待ちたい場合、これまでのように wl_display_roundtrip() を複数回実行するのではなく、wl_display の同期コールバックを使うのが適切です。
今回は、wl_display に同期コールバックを設定して、すべてのイベントが終わるまで待つようにしています。
戻り値に wl_callback のポインタが返るので、それを使ってコールバックのハンドラをセットします。
wl_display_sync() を呼んだ時点で生成されている、すべてのイベントのハンドラ関数が、クライアントで処理されて終了した時点で、指定したコールバック関数が呼ばれます。
なお、wl_display_sync() の後に生成されたイベントに関しては対象外となるため、同期させたいイベントが生成されるたびに、この同期処理を実行する必要があります。
カウンタが 0 になった場合は、待っているイベントが一つもないということになります。
wl_display_dispatch() は、キューにあるイベントを処理する関数です。
イベントキューが空の場合は、イベントが発生するまで待ちます。
同期のコールバックがすべて呼ばれて、カウンタが 0 になったら、終了しています。
この2つにおいて、オブジェクトの作成と同時に生成されるイベントが、クライアント内ですべて処理されたら、プログラムを終了しています。
つまり、サーバーの接続時に最初から使えるグローバルオブジェクトがすべて列挙され、すべての wl_output で初期情報の送信が終わった時に、すべての同期コールバックが終了することになります。
今回は、wl_display に同期コールバックを設定して、すべてのイベントが終わるまで待つようにしています。
static void _display_callback_done(void *data,struct wl_callback *callback,uint32_t time) { wl_callback_destroy(callback); g_display_callback_cnt--; printf("-- display callback done\n"); } static const struct wl_callback_listener g_display_callback_listener = { _display_callback_done }; /* イベントの同期コールバック追加 */ static void _add_display_sync(void) { struct wl_callback *callback; callback = wl_display_sync(g_display); wl_callback_add_listener(callback, &g_display_callback_listener, NULL); g_display_callback_cnt++; }
wl_display_sync()
wl_display_sync() を使うと、現時点で生成されているすべてのイベントが終了した時に、コールバックを呼ぶことができます。戻り値に wl_callback のポインタが返るので、それを使ってコールバックのハンドラをセットします。
wl_display_sync() を呼んだ時点で生成されている、すべてのイベントのハンドラ関数が、クライアントで処理されて終了した時点で、指定したコールバック関数が呼ばれます。
なお、wl_display_sync() の後に生成されたイベントに関しては対象外となるため、同期させたいイベントが生成されるたびに、この同期処理を実行する必要があります。
コールバックの処理
今回は、同期のコールバックが追加される度にカウンタを加算し、コールバック関数が呼ばれたら、カウンタを減算するという形で実装しています。カウンタが 0 になった場合は、待っているイベントが一つもないということになります。
while(wl_display_dispatch(g_display) != -1 && g_display_callback_cnt != 0);
wl_display_dispatch() は、キューにあるイベントを処理する関数です。
イベントキューが空の場合は、イベントが発生するまで待ちます。
同期のコールバックがすべて呼ばれて、カウンタが 0 になったら、終了しています。
同期の追加タイミング
今回の場合は、wl_registry と wl_output の作成後に、同期を追加しています。この2つにおいて、オブジェクトの作成と同時に生成されるイベントが、クライアント内ですべて処理されたら、プログラムを終了しています。
つまり、サーバーの接続時に最初から使えるグローバルオブジェクトがすべて列挙され、すべての wl_output で初期情報の送信が終わった時に、すべての同期コールバックが終了することになります。
同期のキャンセル
途中で同期をキャンセルしたい場合は、まだ呼ばれていないコールバックを wl_callback_destroy() で破棄しなければならないため、コールバックをキャンセルする必要がある場合、wl_callback のポインタを保持しておく必要があります。