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, &registry_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;
}

运行与验证

  1. 编译 Weston:meson build && ninja -C build
  2. 启动 Weston:./build/weston --backend=headless(无头模式便于测试)
  3. 编译客户端:gcc test-client.c custom-client-protocol.h -o test-client $(pkg-config --cflags --libs wayland-client)
  4. 在 Weston 终端中运行 ./test-client

通过条件:客户端输出 Received pong: 42,且 Weston 日志无错误。若客户端无法绑定接口,检查 wl_registry 中接口名称是否为 wl_custom。

常见问题

  • 接口绑定失败:确认服务端 wl_global_create 调用成功,且客户端 registry 监听到的接口名与协议一致。
  • 版本不匹配:确保服务端与客户端使用相同协议版本(本案例为 1)。
  • 编译错误:检查 wayland-scanner 生成的代码路径是否正确包含在编译命令中。
Wayland 自定义扩展调用流程 客户端 wl_registry_bind wl_custom_ping(42) 监听 pong 事件 Weston 服务端 wl_global_create custom_ping 处理 wl_custom_send_pong ① ping 请求 ② pong 事件 wl_registry 全局接口发现与绑定

上述流程展示了客户端通过 registry 发现 wl_custom 接口、绑定后调用 ping 请求,服务端处理并回发 pong 事件的完整链路。实现中需注意协议头文件的生成路径与编译顺序,确保服务端和客户端使用同一份协议定义。