基础 Wire 协议

如果仅仅打算使用 libwayland,那么本节选读并可以直接跳至下节。

Wire 协议是由 32 位值所组成的流,使用当前机器的字节顺序进行编码(例如 x86 系列 CPU 上的小端序)。 其中包含以下几种基础类型:

  • intuint
    32 位 有符号及无符号整型
  • fixed
    24 位整数 + 8 位小数 有符号浮点数
  • object
    32 位 对象 ID
  • new_id
    32 位 对象 ID(收到对象时需要分配)

除了上述基础类型之外,还有一些常用的类型:

  • string
    字符串以 32 位整数开头,这个整数表示字符串的长度(以字节为单位), 接下来是字符串的内容和 NUL 终结符,最后用未定义数据对齐填充 32 位。 编码没有规定,但是实践中使用 UTF-8。

  • array
    任意数据的二进制块,以 32 位整数开头,指定块长度(以字节为单位), 然后是数组的逐字内容,最后用未定义数据对齐 32 位。

  • fd
    主体传输的是一个 0 bit 的值,但是会通过 Unix Socket 消息(msg_control)中的辅助数据将文件描述符(fd)传输到另一端。

  • enum
    一个单独的值(或 bitmap),用于已知常量的枚举,编码为 32 位整型。

消息

Wire 协议是使用这些原语构建而成的消息流。 每条消息都代表着某个对象 object 相关的一次 event 事件(服务端到客户端)或 request 请求 (客户端到服务端)。

消息头由两个字段组成。 第一个字段是操作的对象 ID。 第二个字段是两个 16 位值:高 16 位是这条消息的大小(包括头本身),低 16 位是这次事件或请求的操作码。 接下来是基于双方事先约定的消息签名的消息参数。 接收方会查找对象 ID 的接口、事件或请求的操作码,以确认消息的签名和属性。

为了解析一条消息,客户端和服务端必须先创建对象。 ID 1 预分配给了 Wayland 显示单例对象,它被用于引导产生其它对象。 我们将在第四章中对此进行讨论。 下一章将假设您已经有了一个对象 ID,进一步讨论什么是接口、请求和事件怎么运行。

对象 ID

new_id 参数随某条消息而来,发送者会给它分配一个对象 ID (新对象的接口通过其它额外的参数传递,或事先双方约定)。 此对象 ID 能在后续的消息头或者其它对象的参数中使用。 客户端在 [1, 0xFEFFFFFF] 而服务端在 [0xFF000000, 0xFFFFFFFF] 内分配 ID。 ID 从低位边界开始,并随每次新对象的分配递增。

对象的 ID 为 0 代表空(null)对象,即对象不存在或者空缺。

传输

迄今为止,所有的 Wayland 实现均通过 Unix Socket 工作。 这有个很特别的原因:文件描述符消息。 Unix Socket 是最实用的跨进程文件描述符传输方法,这对大文件传输(如键盘映射、像素缓冲区、剪切板)来说非常必要。 理论上其它传输协议(比如 TCP)是可行的,但是需要开发者实现大文件传输的替代方案。

为了找到 Unix Socket 并连接,大部分实现要做的事和 libwayland 所做的一样:

  1. 如果 WAYLAND_SOCKET 已设置,则假设父进程已经为我们配置了连接,将 WAYLAND_SOCKET 解析为文件描述符。
  2. 如果 WAYLAND_DISPLAY 已设置,则与 XDG_RUNTIME_DIR 路径连接,尝试建立 Unix Socket。
  3. 假设 Socket 名称为 wayland-0 并连接 XDG_RUNTIME_DIR 为路径,尝试建立 Unix Socket。
  4. 失败放弃。