4. Wayland协议基础
Wayland 协议本质上是一套基于 Unix 域套接字的对象化 RPC 协议。客户端与合成器之间并不传递“窗口”或“按钮”这类语义对象,而是围绕一组由接口(interface)定义的对象进行方法调用与事件通知。理解 wl_display、wl_registry、wl_compositor、wl_surface、wl_shm、wl_seat 这几个核心接口,就掌握了 Wayland 图形栈的骨架。
一、对象模型:接口、对象与消息
Wayland 协议中的每个对象都有唯一的数字 ID,并绑定到一个接口。接口定义了若干 request(客户端发给合成器)和 event(合成器发给客户端)。消息通过 wl_display 建立的连接传输,携带对象 ID、操作码和参数。对象之间可以建立父子或关联关系,例如 wl_surface 由 wl_compositor 创建,wl_buffer 由 wl_shm 创建。
二、核心接口职责
| 接口 | 角色 | 关键请求/事件 | 典型用途 |
|---|---|---|---|
wl_display |
连接与全局调度入口 | get_registry、sync、error |
建立连接、获取注册表、同步往返 |
wl_registry |
全局对象发现 | bind、global、global_remove |
枚举并绑定 compositor、shm、seat 等 |
wl_compositor |
表面工厂 | create_surface、create_region |
创建 wl_surface 与区域 |
wl_surface |
可绘制表面 | attach、commit、damage |
提交缓冲区、声明损伤区域 |
wl_shm |
共享内存缓冲 | create_pool、format |
CPU 可写像素缓冲,软件渲染 |
wl_seat |
输入设备集合 | get_pointer、get_keyboard、capabilities |
获取指针、键盘、触摸输入 |
三、连接建立与对象绑定流程
客户端首先连接合成器提供的 socket,创建 wl_display。随后通过 wl_display.get_registry 获得 wl_registry,并监听 global 事件。每个全局对象会携带接口名、版本号和名称。客户端根据自身需要调用 wl_registry.bind,将全局名称绑定为本地代理对象。
/* 伪代码:绑定 wl_compositor 与 wl_shm */
registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, ®istry_listener, NULL);
wl_display_roundtrip(display);
/* 在 global 回调中 */
if (strcmp(interface, wl_compositor_interface.name) == 0) {
compositor = wl_registry_bind(registry, name,
&wl_compositor_interface, 4);
}
if (strcmp(interface, wl_shm_interface.name) == 0) {
shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
}
绑定完成后,客户端即可调用 wl_compositor.create_surface 创建表面,再通过 wl_shm.create_pool 创建共享内存池并生成 wl_buffer,最后用 wl_surface.attach 与 commit 提交内容。
四、信号链视角:从输入到提交
下面这张图展示了核心接口之间的对象创建关系与数据流方向。左侧是客户端侧对象,右侧是合成器侧处理,中间通过协议消息连接。
五、常见误区与复盘
- 误区一:绑定后立即使用。
wl_registry.bind是异步的,必须通过wl_display_roundtrip或事件循环确认全局对象已就绪,否则可能拿到空代理。 - 误区二:忽略版本号。 每个接口有版本范围,绑定版本过高会导致协议错误。应使用
wl_registry.global事件中的 version 与本地支持版本取较小值。 - 误区三:忘记释放缓冲区。
wl_buffer在合成器使用完毕后会发送release事件,客户端必须监听并回收,否则共享内存池会耗尽。 - 误区四:混淆
wl_surface与窗口。wl_surface只是可绘制区域,窗口语义由xdg_shell等扩展协议提供,核心协议本身不定义窗口管理。
WAYLAND_DEBUG=1 运行客户端,可以打印所有协议消息,快速定位对象 ID、请求与事件顺序问题。
掌握这些核心接口后,后续的 xdg_shell、wl_output、wl_pointer 等扩展都只是在同一对象模型上的自然延伸。理解 wl_display 的连接调度、wl_registry 的发现机制、wl_compositor 与 wl_surface 的创建提交链路、wl_shm 的缓冲管理以及 wl_seat 的输入分发,就具备了阅读和编写 Wayland 客户端的基础能力。