第三章:Wayland核心概念
3.1 Wayland协议架构
Wayland的协议架构,说白了就是一套「谁该干什么」的规矩。我刚开始接触时也觉得抽象,但后来发现它比X11简单太多了。
Wayland协议的核心思想是:把显示控制权完全交给Compositor。X11里那些复杂的窗口管理、渲染、输入处理,在Wayland中被拆解得清清楚楚。
协议本身分为两层:
- 核心协议(Core Protocol):定义最基本的通信机制,比如创建surface、处理输入事件
- 扩展协议(Extension Protocol):比如xdg-shell(窗口管理)、wlr-layer-shell(图层管理)
我个人习惯把Wayland协议想象成「客户端-服务器」模型,只不过这里的服务器就是Compositor。每个Client通过Unix socket与Compositor通信,发送请求、接收事件。
关键点:Wayland协议是异步的、基于事件的。Client不能直接操作显存,只能通过Compositor间接完成。
举个例子,当Client想画一个窗口:
// Client端伪代码
wl_surface *surface = wl_compositor_create_surface(compositor);
wl_surface_attach(surface, buffer, 0, 0);
wl_surface_damage(surface, 0, 0, width, height);
wl_surface_commit(surface);
嗯,这里要注意:commit之后,Client并不知道什么时候真正显示。这就是异步设计——Compositor会在合适的时机处理。
3.2 Compositor与Client模型
这个模型是Wayland的基石。我经常跟团队说:Compositor是国王,Client是臣子。
Compositor负责:
- 管理所有显示资源(buffer、surface)
- 合成最终画面(用OpenGL ES或DRM/KMS)
- 处理输入事件(键盘、触摸、鼠标)
- 决定窗口的布局和可见性
Client负责:
- 创建自己的surface
- 渲染内容到buffer
- 把buffer交给Compositor
- 接收输入事件并响应
我在项目中遇到过一个问题:某个Client疯狂提交surface更新,导致Compositor来不及处理。后来发现是Client没有做帧率控制。解决方案很简单——用wl_callback来同步。
避坑指南:我曾经在车载项目中,因为Client和Compositor的buffer管理不一致,导致画面撕裂。后来统一使用wl_drm或linux-dmabuf协议才解决。
为什么Compositor要这么强势?你想想看,在车载系统中,仪表盘和中控屏的显示优先级完全不同。Compositor必须能强制某些Client的显示行为,比如紧急警告必须覆盖所有窗口。
3.3 wl_display与wl_surface
这两个是Wayland里最基础的对象,我几乎每天都在跟它们打交道。
wl_display
wl_display代表一个Wayland连接。它管理着:
- 与Compositor的socket通信
- 事件循环(event loop)
- 所有对象的创建和销毁
每个Client只有一个wl_display。我习惯把它比作「大门」——所有通信都得经过它。
// 典型的Client初始化流程
struct wl_display *display = wl_display_connect(NULL);
struct wl_registry *registry = wl_display_get_registry(display);
// 注册全局对象监听
wl_registry_add_listener(registry, ®istry_listener, NULL);
// 进入事件循环
wl_display_dispatch(display);
注意:wl_display_dispatch是阻塞的。在车载系统中,我建议用wl_display_dispatch_pending配合event loop使用,避免阻塞UI线程。
wl_surface
wl_surface是Client的「画布」。每个窗口、每个UI元素都需要一个surface。
surface的核心操作:
| 操作 | 说明 | 我的经验 |
|---|---|---|
| attach | 绑定一个buffer到surface | buffer必须是共享内存或dma-buf |
| damage | 标记需要更新的区域 | 尽量精确标记,减少合成开销 |
| commit | 提交更新给Compositor | 不要频繁commit,用callback控制节奏 |
| frame | 请求帧同步回调 | 车载中必须用,否则画面会抖动 |
我记得有一次调试车载导航的渲染性能,发现surface的damage区域总是全屏。后来改成只标记变化的道路区域,帧率从30fps提升到了60fps。
核心原则:surface是轻量级的,不要在上面做复杂操作。真正的渲染逻辑应该在Client内部完成,surface只负责提交最终结果。
最后说一句:wl_surface和wl_buffer的关系,就像「画框」和「画纸」。画框(surface)告诉Compositor「我要展示什么」,画纸(buffer)告诉Compositor「内容是什么」。两者缺一不可。
嗯,这一章的内容就到这里。下一章我们会深入buffer管理,讲讲shm和dma-buf的实际应用。