接口与监听
终于,我们到达了 libwayland 顶层的抽象:接口与监听。 前几章讨论的 wl_proxy、wl_resource 以及原语是 libwayland 中的单一实现,它们的存在是为了支持本层抽象。 调用 wayland-scanner 处理 XML 文件时,它会针对高级协议中的每个接口,生成接口、监听及其与底层 Wire 协议之间的胶水代码。
回想一下,Wayland 连接上的每个参与者都可以接收和发送消息。客户端监听事件并发送请求,服务端监听请求并发送事件。 各方都使用特定名称的 wl_listener 监听另一方的消息。 下面是这个接口的一个例子:
译者注:surface 有多重含义,但就 wayland 的使用场景来说往往指代窗口上的内容,而 shell surface 实际上就指代传统意义上的窗口。 下面这个监听器包含进入和离开事件
struct wl_surface_listener {
/** surface enters an output */
void (*enter)(
void *data,
struct wl_surface *wl_surface,
struct wl_output *output);
/** surface leaves an output */
void (*leave)(
void *data,
struct wl_surface *wl_surface,
struct wl_output *output);
};
这是客户端 wl_surface 对应的监听。 用来生成该片段的 XML 如下:
<interface name="wl_surface" version="4">
<event name="enter">
<arg name="output"
type="object"
interface="wl_output"/>
</event>
<event name="leave">
<arg name="output"
type="object"
interface="wl_output"/>
</event>
<!-- additional details omitted for brevity -->
</interface>
应该非常清晰地可以看到事件是如何转变成监听接口的: 每个函数指针接收用户数据、事件涉及资源的引用、事件的参数。 我们可以像这样将监听绑定到 wl_surface 上:
static void wl_surface_enter(
void *data,
struct wl_surface *wl_surface,
struct wl_output *output) {
// ...
}
static void wl_surface_leave(
void *data,
struct wl_surface *wl_surface,
struct wl_output *output) {
// ...
}
static const struct wl_surface_listener surface_listener = {
.enter = wl_surface_enter,
.leave = wl_surface_leave,
};
// ...略...
struct wl_surface *surf;
wl_surface_add_listener(surf, &surface_listener, NULL);
wl_surface 接口也定义了客户端可以进行的一些请求:
<interface name="wl_surface" version="4">
<request name="attach">
<arg name="buffer"
type="object"
interface="wl_buffer"
allow-null="true"/>
<arg name="x" type="int"/>
<arg name="y" type="int"/>
</request>
<!-- additional details omitted for brevity -->
</interface>
wayland-scanner 生成以下原型,以及序列化消息的胶水代码:
void wl_surface_attach(
struct wl_surface *wl_surface,
struct wl_buffer *buffer,
int32_t x, int32_t y);
服务端接口和监听的代码是相同的,但要逆转过来——为请求生成监听,为事件生成胶水代码。 当 libwayland 收到消息时,它会查找对象的 ID、接口,然后用找到的接口解码消息的剩余部分。 再寻找这个对象上的监听,用消息上带的参数调用监听函数。
这便是 libwayland 的全貌! 我们花了好几层抽象才到这一步,您现在应该了解事件如何从服务端开始,成为 Wire 的消息,再被客户端理解并分派。 然而,还有一个悬而未决的问题: 所有这些都假设你已经拥有了对 Wayland 对象的引用,而它又是如何得到的呢?