前回シェルプロトコルなしでシェルを実装してみた。
今回はそのシェルにシェルプロトコルを実装していく。
ivi_application
プロトコル
Westonはいくつかのシェルプロトコルをサポートしているが、その中で今回はivi_application
プロトコルを実装してみる。
ivi_application
を選んだ理由は簡単で「実装項目が非常に少ない」からである。
後の回で詳しく説明する予定だが、ivi_application
はIVI(in-vehicle infotainment)向けのプロトコルであり、サーバー側でクライアントの位置やサイズ等表示方法を完全に制御するのに適したシェルプロトコルとなっている。
通常のデスクトップシェルと異なりサーバー側で完全にコントロールするためクライアントからの要求等が非常に少ないプロトコルとなっているのが特徴である。
プロトコル概要
wl_surface
を引数として作成するivi_surface
に対して、サーバー側からサイズを伝えるconfigure
イベントとクライアント側から削除を要求するdestroy()
リクエストがあるだけの非常にシンプルな構成となっている。
課題
ivi_application
プロトコルを定義しているファイルivi-application.xmlを確認しよう。
実装
ivi_application
インターフェイス
まずはivi_application
インターフェイスのバインドを実装する。1
CMakeLists.txtでivi-application.xmlからヘッダ、ソースコードを生成する部分は第三回と同様であるため割愛する。2
インターフェイスのバインド処理も第三回と違いはない。
(ivi_application_surface_create()
は後ほど実装する)
static const struct ivi_application_interface ivi_application_impl = {
.surface_create = ivi_application_surface_create,
};
static void unbind_ivi_application(wl_resource *resource)
{
// noop
}
static void bind_ivi_application(wl_client *client, void *data, uint32_t version, uint32_t id)
{
weston_log("my-shell: bind_ivi_application\n");
MyShell *shell = static_cast<MyShell *>(data);
wl_resource *resource = wl_resource_create(client, &ivi_application_interface, version, id);
wl_resource_set_implementation(resource, &ivi_application_impl, shell, unbind_ivi_application);
}
wet_shell_init()
内でグローバルオブジェクトを作成する。
if (!wl_global_create(compositor->wl_display, &ivi_application_interface, 1, shell,
bind_ivi_application)) {
weston_log("my-shell: Failed to create global\n");
return -1;
}
これでクライアンがivi_application
プロトコルをバインドできるようになる。
ivi_surface
実装
続いてivi_surface
を実装する。
前回シェルプロトコルなしでシェルを実装したときはコンポジタのcreate_surface_signal
にフックする形でサーフェスコミットのコールバックを実装したが、今回は素直にivi_surface
を作る際にいっしょにサーフェスコミットのコールバックを設定すれば良い。
struct MyShellSurface {
MyShell* shell;
weston_surface *wet_surf;
wl_listener destroy_listener;
};
static const struct ivi_surface_interface ivi_surface_impl = {
.destroy = [](wl_client*, wl_resource*){}, // noop
};
void ivi_application_surface_create(struct wl_client *client, struct wl_resource *resource,
uint32_t ivi_id, struct wl_resource *surface, uint32_t id)
{
weston_log("my-shell: ivi_application_surface_create\n");
auto *shell = static_cast<MyShell *>(wl_resource_get_user_data(resource));
auto *wet_surf = static_cast<weston_surface *>(wl_resource_get_user_data(surface));
auto *shell_surf = new MyShellSurface{shell, wet_surf};
wl_resource *res = wl_resource_create(client, &ivi_surface_interface, 1, id);
wl_resource_set_implementation(res, &ivi_surface_impl, shell_surf,
[](wl_resource *r) {
auto *surf = static_cast<MyShellSurface*>(wl_resource_get_user_data(r));
delete surf;
});
wet_surf->committed = surface_committed;
wet_surf->committed_private = shell_surf;
}
いままでコールバックの空実装は空の関数を用意してきたがC++であればivi_surface_impl.destroy
のように空のlambda関数を登録しても良い。
また今回はivi_surface
のアンバインド関数もlambda関数にしている。
注意
surface_committed()
は前回の記事に記載されているものをそのまま流用すると落ちるため上記コードに適合するよう改良すること。
アプリの実行
前回使ったEGLWLMockNavigation
はivi_application
に対応しているため前回同様表示することができる。
またQtアプリケーションも以下の環境変数付を利用することでivi_application
に対応するため、表示することが可能となる。
$ QT_WAYLAND_SHELL_INTEGRATION=ivi-shell QT_QPA_PLATFORM=wayland qalculate