今回はWeston用に拡張モジュールを作成し独自のインターフェイスを実装してみる。
開発環境
開発環境は以下を想定する。
- OS: Debian trixie(testing)
- 言語: C++20
- Weston14
異なる環境を使用する場合は適宜記述を読み替えること。
ハンズオン
今回はmy-custom-moduleという名前のモジュールを作成する。Linuxの場合はビルドするとmy-custom-module.soが生成され、--modules=
を指定することでモジュールを組み込んだwestonが起動する。
$ weston --modules=my-custom-module.so
必要パッケージ
Debian系であれば、weston, libweston-14-devをインストールしておく。
$ sudo apt install weston libweston-14-dev
空のモジュールを作成
まずはメッセージを表示するだけの空モジュールを作成する。
#include <wayland-server-core.h>
#include <weston.h>
extern "C" {
WL_EXPORT int wet_module_init(weston_compositor *compositor,
int *argc, char* argv[])
{
weston_log("Hello my custom module\n");
return 0;
}
}
- モジュールは必ず最初に
wet_module_init()
関数が呼ばれる。C関数として呼ばれるためextern "C"
が必須
WL_EXPORT
はwet_module_init()
を外部公開するために必要
CMakeLists.txtは以下の通り。
cmake_minimum_required (VERSION 3.10)
project(my-custom-module)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(PkgConfig REQUIRED)
pkg_check_modules(WAYLAND_CLIENT wayland-server REQUIRED)
pkg_check_modules(WESTON libweston-14 REQUIRED)
add_library(${CMAKE_PROJECT_NAME} MODULE
module.cpp
)
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "")
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
${WAYLAND_SERVER_INCLUDE_DIRS}
${WESTON_INCLUDE_DIRS}
)
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE
${WAYLAND_SERVER_LIBRARIES}
${WESTON_LIBRARIES}
)
cmakeでライブラリをビルドするとlibmy-custom-module.soのような名前となるが、westonモジュールは慣習的にlibプレフィックスを付けないため、以下のオプションで抑制している。
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "")
これによりビルドした結果my-custom.module.soという名前でビルドされる。
ビルド
$ cmake -S . -B build
$ cmake --build build
build/my-custom-module.soがビルドされる。
実行
$ weston --modules=$(pwd)/build/my-custom-module.so
ログに出力があることを確認する。
[14:08:46.123] Hello my custom module
greetingプロトコル定義
以下のXMLで今回使用するプロトコルを定義した。greetingインターフェイスに対して
<protocol name="greeting_protocol">
<interface name="greeting" version="1">
<request name="greet">
<arg name="name" type="string"/>
</request>
<event name="done">
</event>
</interface>
</protocol>
-
greeting()
リクエストでname
を送信するとログに挨拶を出力 -
done()
イベントで処理の完了を通知
という仕様にする。
クライアントのときど同様にXMLからコードを生成しビルドをおこなう。
find_program(WAYLAND_SCANNER wayland-scanner REQUIRED)
set(GREETING_XML ${CMAKE_CURRENT_SOURCE_DIR}/greeting.xml)
set(GREETING_HEADER ${CMAKE_CURRENT_BINARY_DIR}/greeting-server-protocol.h)
set(GREETING_SOURCE ${CMAKE_CURRENT_BINARY_DIR}/greeting-server-protocol.c)
add_custom_command(
OUTPUT ${GREETING_HEADER}
COMMAND ${WAYLAND_SCANNER} server-header ${GREETING_XML} ${GREETING_HEADER}
DEPENDS ${GREETING_XML}
VERBATIM
)
add_custom_command(
OUTPUT ${GREETING_SOURCE}
COMMAND ${WAYLAND_SCANNER} private-code ${GREETING_XML} ${GREETING_SOURCE}
DEPENDS ${GREETING_XML}
VERBATIM
)
add_custom_target(protocol ALL DEPENDS ${GREETING_SOURCE} ${GREETING_HEADER})
既存部分の修正も忘れずに。
add_library(${CMAKE_PROJECT_NAME} MODULE
module.cpp
${GREETING_SOURCE}
)
add_dependencies(${CMAKE_PROJECT_NAME} protocol)
greeting
グローバルオブジェクト作成
wet_module_init()
内にてwl_global_create()
関数でgreeting
インターフェイスに対応するグローバルオブジェクトを作成する。
extern "C" {
WL_EXPORT int wet_module_init(weston_compositor *compositor,
int *argc, char* argv[])
{
weston_log("my-custom-module: Hello my custom module\n");
if (nullptr == wl_global_create(compositor->wl_display, &greeting_interface, 1,
nullptr, bind_greeting)) {
weston_log("my-custom-module: Failed to create global\n");
return -1;
}
return 0;
}
}
bind_greeting()
はクライアントがgreeting
をバインドしたときに呼び出される関数。
wl_resource_create()
でクライアントに対応するインスタンス(wl_resource
)を生成し、greet()
リクエストが呼び出されたときのコールバックをwl_resource_set_implementation()
にて設定する。
done()
イベントは関数として呼び出すだけで良い。(greeting_send_done()
)
void greet(struct wl_client *client,
struct wl_resource *resource,
const char *name)
{
weston_log("Hello %s\n", name);
greeting_send_done(resource);
}
static const struct greeting_interface greeting_impl = {
.greet = greet
};
static void unbind_resource(wl_resource* resource)
{
}
static void bind_greeting(wl_client* client, void* data, uint32_t version, uint32_t id)
{
weston_log("my-custom-module: bind_greeting\n");
wl_resource* resource = wl_resource_create(client, &greeting_interface, version, id);
wl_resource_set_implementation(resource, &greeting_impl, nullptr, unbind_resource);
}
課題:クライアント実装
この時点でgreeting.xmlを用いたクライアントを実装することができる。
前回を参考に、実際にクライアントを実装し動作を確認しよう。
実行例:
クライアントを実行すると
$ ./greeting-client Taro
Westonのログに以下のように表示されるはずである。
[12:48:59.066] Hello Taro
C/C++初級者への課題
課題
- Westonがモジュールを呼び出している箇所をソースコード上で確認する
-
wet_module_init()
にはなぜextern "C"
が必要なのか、他の関数等に不要なのはなぜか