4. 核心概念

Wayland 协议本身只定义“客户端与合成器如何对话”,真正决定画面长什么样、输入落到哪里、窗口如何被管理的,是一组核心对象。理解这些对象之间的职责边界,是排查多屏座舱显示问题的前提。本节把它们放在同一条渲染与输入链路上讲解,而不是孤立地背名词。

核心心智模型:buffer 是像素数据,surface 是像素的承载与提交单元,region 是 surface 上的局部区域,output 是显示目标,seat 是输入源,shell 决定窗口语义,subsurface 与 layer-shell 是两种不同的层级组织手段。

一、渲染链路:buffer → surface → region → output

wl_buffer 是最底层的像素容器,它描述一块共享内存或 GPU 纹理的格式、宽高与步幅。客户端把绘制结果写入 buffer,然后通过 wl_surface.attach 把 buffer 挂到 surface 上,再用 commit 提交。合成器收到 commit 后,才真正把这块 buffer 合成到屏幕上。

wl_surface 是客户端可见的“画布”,它本身不持有像素,只持有当前挂载的 buffer、变换、缩放和损坏区域。多屏场景下,同一个 surface 可能被合成器映射到不同 output,因此 surface 的坐标是逻辑坐标,最终由合成器结合 output 的缩放与变换换算成物理像素。

wl_region 表示 surface 上的一个矩形集合,常用于 set_opaque_region 和 set_input_region。前者告诉合成器“这块区域不透明,可以跳过下层绘制”,后者决定“哪些区域能接收输入”。在座舱中,仪表盘中央的圆形表盘常配合 region 做输入裁剪,避免透明角落误触。

wl_output 代表一个物理或虚拟显示输出,携带分辨率、刷新率、缩放、物理尺寸与变换信息。多屏座舱里,中控屏、仪表屏、副驾屏各自对应一个 output,合成器负责把 surface 分配到合适的 output 上。

Wayland 核心对象在渲染与输入链路中的位置 wl_buffer 像素数据 / 格式 wl_surface 画布 / 提交单元 wl_region 不透明 / 输入裁剪 wl_output 显示目标 / 缩放 wl_seat 键盘 / 指针 / 触摸 shell 窗口语义 / 角色 subsurface 父子同步 / 局部 layer-shell 层级 / 锚定 / 独占 合成器(Compositor):接收 commit,按 shell / layer-shell 规则决定层级, 结合 output 缩放与 region 裁剪,把 buffer 合成到对应屏幕,并把 seat 事件路由到目标 surface。

二、输入链路:seat 与 surface 的绑定

wl_seat 是输入能力的集合,一个 seat 可以包含键盘、指针、触摸等 capability。客户端通过 wl_seat.get_pointer、get_keyboard、get_touch 获取具体设备对象。合成器根据指针位置、焦点策略和 surface 的 input region,决定把事件发给哪个 surface。

多屏座舱中常见“一个 seat 覆盖多屏”或“每屏独立 seat”两种设计。前者适合统一交互,后者适合仪表屏只接受方向盘按键、中控屏只接受触摸的隔离场景。选择哪种,取决于功能安全与交互隔离要求。

三、窗口语义:shell 与 layer-shell 的分工

shell 不是单一协议,而是一类“窗口角色”协议的统称。传统桌面用 xdg-shell 定义顶层窗口、弹出菜单、最大化等语义;座舱中则大量使用 layer-shell,它允许客户端声明自己属于 background、bottom、top、overlay 等层级,并支持锚定到屏幕边缘、设置独占区域。

subsurface 解决的是“同一窗口内部的局部更新”。子 surface 依附于父 surface,可以独立提交 buffer,并支持同步模式,保证父子内容在同一帧生效。视频播放、仪表指针动画常用 subsurface,避免整窗重绘。

对象核心职责座舱典型用法
wl_buffer承载像素与格式仪表帧、视频帧、UI 纹理
wl_surface提交与合成单元每个可视窗口或图层
wl_region局部不透明/输入区域圆形表盘输入裁剪
wl_output显示目标与参数中控屏、仪表屏、副驾屏
wl_seat输入能力集合触摸、按键、方向盘输入
shell窗口角色与生命周期普通应用窗口
subsurface父子局部合成视频层、动画指针
layer-shell层级、锚定、独占状态栏、HUD、背景层

四、案例与排障:多屏座舱中的典型问题

场景一:仪表屏上的指针动画卡顿。若整窗每帧重绘,带宽与合成开销都高。改用 subsurface 只提交指针区域,并配合 region 标记不透明区,可显著降低负载。

场景二:中控屏弹出菜单被状态栏遮挡。这通常是层级问题:状态栏使用 layer-shell 的 top 层,而菜单属于 xdg-shell 顶层窗口。需要调整 layer-shell 层级或让菜单使用 overlay 层。

场景三:触摸点落在透明区域却触发按钮。检查 set_input_region 是否设置正确,透明区域应排除在输入区域之外。

常见误区:把 surface 当成“窗口”。surface 只是画布,窗口语义由 shell 或 layer-shell 赋予;同一个 surface 在不同 shell 下行为完全不同。另一个误区是忽略 output 缩放,导致逻辑坐标与物理像素不一致,触摸位置偏移。
调试建议:用 wayland-info 查看 output 与 seat 能力,用合成器日志确认 surface 被分配到哪个 output,再核对 region 与 layer 层级。先定位对象,再定位协议调用,最后才怀疑渲染性能。
# 查看 Wayland 输出与输入能力
wayland-info | grep -A5 "wl_output"
wayland-info | grep -A5 "wl_seat"

# 合成器日志中观察 surface 与 layer 分配
journalctl -u compositor --since "5 min ago" | grep -i "surface\|layer"

把这八个对象串起来看:buffer 提供像素,surface 负责提交,region 做局部裁剪,output 决定显示目标,seat 提供输入,shell 与 layer-shell 决定窗口语义与层级,subsurface 处理窗口内部的局部合成。多屏座舱的绝大多数显示与交互问题,都可以沿这条链路逐段定位。