基础 Wire 协议
如果仅仅打算使用 libwayland,那么本节选读并可以直接跳至下节。
Wire 协议是由 32 位值所组成的流,使用当前机器的字节顺序进行编码(例如 x86 系列 CPU 上的小端序)。 其中包含以下几种基础类型:
- int, uint
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 所做的一样:
- 如果
WAYLAND_SOCKET
已设置,则假设父进程已经为我们配置了连接,将WAYLAND_SOCKET
解析为文件描述符。 - 如果
WAYLAND_DISPLAY
已设置,则与XDG_RUNTIME_DIR
路径连接,尝试建立 Unix Socket。 - 假设 Socket 名称为
wayland-0
并连接XDG_RUNTIME_DIR
为路径,尝试建立 Unix Socket。 - 失败放弃。