Westonではウインドウ等をどのように配置して表示するかといったことはシェルと呼ばれる特別なモジュールが実現している。
例えばタイトルバーをドラッグしてウインドウを移動させるという当たり前の操作もWestonにおいてはシェルが担っている。
それどころかどのウインドウを表示するかといったことすらもシェルが制御しているため、シェルが何もしない場合、Weston上には何も表示されないことになる。
今回は「サーフェスが作られたらデフォルト位置(原点)に表示する」という機能だけを持った非常に簡単なシェルを実装する。
開発環境
コードは前回までと同様以下の環境で開発している。
- OS: Debian trixie(testing)
- 言語: C++20
- Weston14
シェルとシェルプロトコルについて
Westonはxdg_shell
やivi_application
といったシェルプロトコルをサポートしており、シェルモジュールは必ずどれか(もしくは複数)のシェルプロトコルを実装している。
しかし本来シェルはウインドウサーフェスの管理をすることが目的であり、クライアントからの要求等を一切受け付けず、内部で完結した管理をするのであれば「シェルプロトコルを実装しないシェル」というのも実現可能である。
ただし現時点においてはほぼすべてのクライアントが何からのシェルプロトコルのバインドを要求するため、もし「シェルプロトコルを実装しないシェル」というものを実装してもほとんど全てのクライアントが動作しないシェルとなってしまう。
まとめると「シェルにシェルプロトコル実装は必須ではない、しかし実装しないと役立たずになる」というわけである。
今回はシェルプロトコルを実装していなくても動作するEGLWLMocknavigation
を利用して確認していくこととする。
wayland-ivi-extensionインストール
EGLWLMocknavigation
は以下のプロジェクトにある。
プロジェクトのREADME.mdに従ってインストールをおこなう。
注意
おそらくweston13以降だとlibweston.hの互換性がなくなっているためwayland-ivi-extensionのインストールができない。
その場合は次回分まで実装して動作確認はQtアプリでおこなってほしい。
スケルトン実装
いつもどおり何もしない実装をまず書いてみる。基本的にはモジュールのときと同じだが唯一違うのが呼び出される関数がwet_module_init()
からwet_shell_init()
になることである。
#include <libweston/libweston.h>
extern "C" {
WL_EXPORT int wet_shell_init(struct weston_compositor *compositor, int *argc, char *argv[])
{
weston_log("my-shell: init\n");
return 0;
}
}
CMakeLists.txtについては前回とほぼ同じになるため割愛するが、my-shell.so
が作られるように書いてほしい。
いつもどおりビルドする。
$ cmake -S . -B build
$ cmake --build build
weston
コマンドに--shell
オプションを指定することでシェルとして実行できる。
$ weston --shell=$(pwd)/build/my-shell.so
シェルとしての機能を一切果たしていないため、黒画面が表示されるだけとなる。
wet_shell_init()
が実行されたことをログで確認する。
[12:26:58.068] my-shell: init
この時点ではEGLWLMocknavigation
を起動してももちろん表示はされない。
サーフェス表示の実装
最低限表示をおこなうためには以下の処理が必要となる。
- 表示領域である
weston_layer
を作成する - サーフェス作成のコールバック作成
- サーフェスコミットのコールバック作成
- 初回コミット時に表示処理を行う
順にみていく前に、まずはシェルの状態を保持するためのMyShell
を作る。
struct MyShell {
wl_listener surface_created_listener;
weston_layer layer;
};
表示領域であるweston_layer
を作成する
wet_shell_init()
内でweston_layer_init
で表示領域を初期化する。
auto *shell = new MyShell{};
weston_layer_init(&shell->layer, compositor);
weston_layer_set_position(&shell->layer, WESTON_LAYER_POSITION_NORMAL);
サーフェス作成のコールバック作成
wet_shell_init()
内でサーフェスのコールバックとしてsurface_created()
(後述)を設定する。
shell->surface_created_listener.notify = surface_created;
wl_signal_add(&compositor->create_surface_signal, &shell->surface_created_listener);
サーフェスコミットのコールバック作成
サーフェス作成時に呼び出されるsurface_created()
内でサーフェスコミット(サーフェス変更内容反映要求)のコールバックとしてsurface_committed()
(後述)を設定する。
static void surface_created(struct wl_listener *listener, void *data)
{
MyShell *shell = wl_container_of(listener, shell, surface_created_listener);
auto *wet_surf = static_cast<weston_surface *>(data);
wet_surf->committed = surface_committed;
wet_surf->committed_private = shell;
}
ここではweston_surface
が持っているcommitted
コールバックを設定することでコミット時に処理がおこなわれる。
なおcommitted
コールバックは基本的にシェルのみが設定することが期待されている。
初回コミット時に表示処理を行う
ビューを作成し、レイヤーに登録することで描画処理が行われる。
static void surface_committed(struct weston_surface *wet_surf, struct weston_coord_surface)
{
auto *shell = static_cast<MyShell*>(wet_surf->committed_private);
weston_view *view = nullptr;
if (!weston_surface_has_content(wet_surf)) {
return;
}
if (wl_list_empty(&wet_surf->views)) {
view = weston_view_create(wet_surf);
if (!view)
return;
} else {
view = wl_container_of(wet_surf->views.next, view, surface_link);
}
if (weston_surface_is_mapped(wet_surf)) {
return;
}
weston_surface_map(wet_surf);
weston_view_move_to_layer(view, &shell->layer.view_list);
}
Westonにはバッファがアタッチされていないサーフェスに対してコミット処理をおこなってはいけないという暗黙の了解があるために冒頭でweston_surface_has_content(wet_surf)
によるガードが入っている。
実行
ビルドし前述同様に実行してサーフェスが表示されることを確認する。
C/C++初級者への課題
課題
-
前々回、クライアントを実装したときにはクライアントの状態を保持する
App
はスタック上に確保した。今回シェルの状態を保持するMyShell
はnew
している。2つの違いについて理由を考えよう
変更履歴
- 2025-07-09 weston13以降でwayland-ivi-extensionがインストールできないことが判明したため注意を記載
- 2025-07-02 コミット時処理を
commit_signal
からweston_surface.committed
コールバックに変更