wayland.png

  1. wayland 协议是一种彻底的面向对象的异步操作协议,客户端到服务端叫请求,服务端到客户端叫做事件。客户端发起的虽然叫做请求,但是并不存在响应,也就不会阻塞了。

  2. 面向对象指的是每次发送信息,最开始都有一个对象 ID。要创建一个新的对象,也是调用某个对象的某个方法孵化出来的新对象。所以某个对象来自哪里都是有迹可循的,最终都能汇总到 wl_display 对象

  3. 对象可以来自客户端,也可以来自服务端。客户端和服务端各自维护一个不重叠的 ID 空间,客户端要新建一个对象,其实就是发送一个新的 ID 到服务端,然后服务端就记录这个对象 ID 和类型。正常网络服务都是客户端请求新建对象,服务端新建完之后,返回一个 ID,但是这么操作多了一个返回。wayland 协议需要高效一点的做法,是请求没有返回。关于对象的新增、分配 ID 原理这一点非常重要,理解了这个之后,后续的流程看起来才会觉得自然。

  4. 客户端连接到服务端之后,需要做的第一将事情就是获取到 wl_registry 对象,至于 wl_display的对象 ID 是 1 以及是协议约定好的。

  5. 监听wl_registryglobal事件可以获取到后续流程需要用到的全局对象,这些包括:

    • wl_output 代表显示器,包含了显示器的分辨率、名称、颜色模式、刷新频率等必要信息。有几个显示器就会广播几个显示器对象。这个对象是支持热插拔的,新增显示器和移除显示器都通知客户端。
    • wl_shm 共享内存管理器,用来管理客户端发送来的 buffer。
    • wl_compositor 合成器对象,负责孵化窗口的。
    • wl_seat 输入设备,目前有鼠标、触摸板、键盘这三类设备,不够以后在扩展。
  6. 如果你需要创建一个新的窗口,那么你需要把窗口大小还有渲染用到的数据哪里读取告诉服务端。

  7. 经典的 wayland 合成器工作流程都是客户端发送 fd 告诉服务端那里读取数据,服务端不会自己分配 buffer 供客户端读写。这个 buffer 可能在系统内存里面,也可能在显卡的内存里面。

  8. 所以创建一个 wl_shm_pool 关联到一个 fd,在这个池子里面在添加一个偏移 offset 就是窗口渲染需要用到数据来源了,也就是图中wl_buffer 对象。这么做的好处就是一个 fd 里面可以包含几个帧的数据,避免产生内存碎片,但是具体怎么实现是客户端需要考虑的事情。

  9. wl_compositor对象那边获取一个 wl_surface 对象,在将wl_buffer对象绑定到这个表面。调用damage 标记需要重新渲染的区域,最后调用commit方法提交渲染事物。

  10. 这个过程中有很多细节需要考虑,比如 wl_buffer 对应的色彩模式、分辨率大小。在 wl_surface 上渲染的位置,一个 wl_surface 可以关联多个 wl_buffer 等。细节要考虑,全局的流程也要考虑,如果一开始就过于纠结细节,很容易疲惫也很容易绕到坑里面去。

  11. 同时监听鼠标键盘等输入设备传递过来的事件,注意这里的输入事件并不跟某个 wl_surface 绑定,假设一个 wl_surface 代表一个应用窗口,你的程序可能有很多窗口。合成器只会承诺遇到你几个窗口中的哪个给你发送对应的事件,事件信息里面包含是哪个窗口,相对于窗口的哪个位置(鼠标事件有)。

  12. 有了输入事件,就需要依据输入事件触发动作进行重绘。首先更新像素所在内存区域,然后标记需要重绘区域(damage方法),最后提交 (commit方法) 重绘区域。合成器重绘的时候就是去查询之前注册的内存 fd + offset 偏移,使用里面的共享内存进行合成。

  13. 上面的流程还是少了一些东西没有说明,比如窗口标题、最大化、最小化、移动、改变大小等等这些很正常的操作。这一部分原本是使用 wl_shell 这个对象相关的操作完成的,但是因为某些原因弃用了,改用 xdg-shell 扩展协议实现。如果查看手册,就会发现核心协议是 wl_ 前缀开头,其他扩展协议都采用各自的命名空间。图片上**xdg_**开头的节点,就是 xdg-shell 扩展。

  14. 获取一个常规窗口的路径是:wl_surface -> xdg_surface -> xdg_toplevel ,具体流程就是调用 xdg_wm_base::surface 方法,将前面获取到的 wl_surface 作为参数传递,然后在调用 xdg_surface::get_toplevel 方法获取到一个 xdg_toplevel对象。xdg_wm_base 对象是全局单例对象,如果合成器支持,在获取 wl_registry 之后就可以接受到这个对象的 ID,反之如果没有收到那就是合成器不支持这个扩展协议了。接口上你可以多次调用,但是工程上你必须保证只调用一次。因为一个 wl_surface 只能是一种角色,并且角色确定下来就无法更改。

  15. 除了常规窗口 xdg_toplevel 这种角色,还有另外一个临时角色 xdg_popup,这是一个弹出一个浮动小窗没有标题、固定大小,失去焦点就会关闭消失的角色。

  16. wayland 协议有一个在线查看的地址:wayland.app,非常方便好用。