4. 实现Wayland服务端扩展
任务目标
在 Weston 中注册一个自定义 Wayland 全局接口 wl_custom,实现一个简单的请求 ping,使客户端能够发现该接口并成功调用请求,服务端返回确认。
环境与依赖
- Weston 源码(版本 10.0+,本案例基于 10.0.2 验证)
- 构建依赖:meson、ninja、wayland-protocols、libwayland-dev
- 客户端测试工具:
weston自带终端或自定义客户端(使用wayland-scanner生成协议代码)
实现步骤
1. 定义协议文件 custom.xml
在 Weston 源码目录下新建 custom.xml,定义全局接口与请求:
<protocol name="custom">
<interface name="wl_custom" version="1">
<request name="ping">
<arg name="value" type="uint"/>
</request>
<event name="pong">
<arg name="value" type="uint"/>
</event>
</interface>
</protocol>
2. 服务端实现(修改 Weston compositor 代码)
在 compositor/weston.c 中添加自定义接口的初始化和请求处理逻辑。核心代码如下:
/* custom_extension.c - 新建文件 */
#include <wayland-server.h>
#include "custom-server-protocol.h"
static void
custom_ping(struct wl_client *client, struct wl_resource *resource,
uint32_t value)
{
/* 回发 pong 事件 */
wl_custom_send_pong(resource, value);
}
static void
custom_bind(struct wl_client *client, void *data, uint32_t version,
uint32_t id)
{
struct wl_resource *resource;
resource = wl_resource_create(client, &wl_custom_interface, 1, id);
wl_resource_set_implementation(resource, NULL, NULL, NULL);
}
static const struct wl_custom_interface custom_impl = {
custom_ping
};
int
custom_extension_init(struct wl_display *display)
{
if (wl_global_create(display, &wl_custom_interface, 1,
NULL, custom_bind) == NULL)
return -1;
return 0;
}
在 compositor/main.c 的 wet_main() 中调用初始化函数:
#include "custom-extension.h"
/* 在 weston_compositor 创建后调用 */
if (custom_extension_init(ec->wl_display) < 0) {
weston_log("Failed to init custom extension\n");
return -1;
}
3. 构建配置
修改 meson.build,添加协议生成规则:
wayland_protocols = dependency('wayland-protocols')
custom_protocol = custom_target('custom_protocol',
output: ['custom-protocol.h', 'custom-server-protocol.h'],
input: 'custom.xml',
command: [wayland_scanner, '@INPUT@', '@OUTDIR@'])
客户端验证
创建测试客户端 test-client.c:
#include <stdio.h>
#include <wayland-client.h>
#include "custom-client-protocol.h"
static void
handle_pong(void *data, struct wl_custom *custom, uint32_t value)
{
printf("Received pong: %u\n", value);
exit(0);
}
static const struct wl_custom_listener custom_listener = {
handle_pong
};
int main(void)
{
struct wl_display *display = wl_display_connect(NULL);
struct wl_registry *registry = wl_display_get_registry(display);
struct wl_custom *custom;
/* 注册全局接口监听 */
wl_registry_add_listener(registry, ®istry_listener, NULL);
wl_display_roundtrip(display);
/* 获取 wl_custom 全局对象 */
custom = wl_registry_bind(registry, 1, &wl_custom_interface, 1);
wl_custom_add_listener(custom, &custom_listener, NULL);
/* 调用 ping 请求 */
wl_custom_ping(custom, 42);
wl_display_roundtrip(display);
return 0;
}
运行与验证
- 编译 Weston:
meson build && ninja -C build - 启动 Weston:
./build/weston --backend=headless(无头模式便于测试) - 编译客户端:
gcc test-client.c custom-client-protocol.h -o test-client $(pkg-config --cflags --libs wayland-client) - 在 Weston 终端中运行
./test-client
通过条件:客户端输出 Received pong: 42,且 Weston 日志无错误。若客户端无法绑定接口,检查 wl_registry 中接口名称是否为 wl_custom。
常见问题
- 接口绑定失败:确认服务端
wl_global_create调用成功,且客户端 registry 监听到的接口名与协议一致。 - 版本不匹配:确保服务端与客户端使用相同协议版本(本案例为 1)。
- 编译错误:检查
wayland-scanner生成的代码路径是否正确包含在编译命令中。
上述流程展示了客户端通过 registry 发现 wl_custom 接口、绑定后调用 ping 请求,服务端处理并回发 pong 事件的完整链路。实现中需注意协议头文件的生成路径与编译顺序,确保服务端和客户端使用同一份协议定义。