3. Wayland库的实现


我们在1.3章简单介绍过Wayland库——这是最流行的Wayland实现。本书的大部分内容适用于任何实现,但我们将用接下来的两章来让您熟悉这一实现。

Wayland软件包包括用于wayland-client和wayland-server的pkg-config规范——请查阅您的构建系统文档以了解如何链接它们。当然,大多数应用程序只会链接到其中一个。该库包括一些简单的原语(例如链表)和预先编译的wayland.xml版本——这是核心的Wayland协议。

我们将从介绍原语开始。

3.1 Wayland库的实用程序原语

在客户端和服务器库中常见的是wayland-util.h,它定义了许多结构、实用程序函数和宏,为Wayland应用程序建立了一些原语。其中包括:

  • 用于在生成的代码中序列化和反序列化Wayland协议消息的结构
  • 一个链表(wl_list)实现
  • 一个数组(wl_array)实现(与相应的Wayland原语相关联)
  • 用于转换Wayland标量(例如定点数)和C类型的实用程序
  • 从libwayland内部冒泡信息的调试日志设施

该头文件本身包含许多注释,其中包含相当好的文档-您应该自己阅读它们。我们将在接下来的几页中详细介绍如何应用这些原语。

3.2 wayland-scanner

Wayland软件包附带一个二进制文件:wayland-scanner。此工具用于从第2.3章讨论的Wayland协议XML文件生成C头文件和粘合代码。该工具在“wayland”包的构建过程中用于预生成核心协议wayland.xml的头文件和粘合代码。头文件成为wayland-client-protocol.h和wayland-server-protocol.h,不过你通常包括wayland-client.h和wayland-server.h而不是直接使用这些。

此工具的使用相当简单(可通过wayland-scanner -h进行总结),但可以总结如下。要生成客户端头文件:

wayland-scanner client-header < protocol.xml > protocol.h

要生成服务器端头文件:

wayland-scanner server-header < protocol.xml > protocol.h

要生成粘合代码:

wayland-scanner private-code < protocol.xml > protocol.c

不同的构建系统将采用不同的方法来配置自定义命令-请查阅您构建系统的文档。一般来说,您想在构建时运行wayland-scanner,然后将您的应用程序编译并链接到粘合代码。

如果您现在手头有任何Wayland协议,请继续进行(例如,wayland.xml可能在/usr/share/wayland中可用)。打开粘合代码和头文件,并在阅读以下章节时参考它,以了解libwayland中提供的原语如何在生成的代码中实际应用。

3.3 代理和资源

对象是客户端和服务器双方都知晓的实体,它具有一定的状态,通过协商在电线上进行更改。在客户端方面,libwayland通过wl_proxy接口引用这些对象。这些都是对抽象对象的实际友好的C“代理”,并提供间接供客户端使用的函数,以便将请求转换为电线的格式。如果您查看wayland-client-core.h文件,您会发现几个用于此目的的低级函数。一般来说,您不直接使用这些。

在服务器上,通过wl_resource对对象进行抽象,这是非常相似的,但具有额外的复杂性-服务器必须跟踪哪个对象属于哪个客户端。每个wl_resource由单个客户端拥有。除此之外,该接口非常相似,并为向相关客户端发送Marshall事件提供低级抽象。您在服务器上直接使用wl_resource的频率将比在客户端上直接使用wl_proxy的频率更高。这样使用的一个例子是,当您正在上下文之外操作资源时,获取对拥有该资源的wl_client的引用,或者在客户端尝试执行无效操作时发送协议错误。

再往上一层是另一组更高级别的接口,大多数Wayland客户端和服务器都与之交互以完成其大部分任务。我们将在下一部分中介绍它们。

3.4 接口和监听器

最后,我们达到了libwayland抽象的顶峰:接口和监听器。前几章讨论的想法-wl_proxy和wl_resource以及原始类型-是单例实现,它们存在于libwayland中,并且它们的存在为这一层提供支持。当您通过wayland-scanner运行XML文件时,它生成接口和监听器,以及它们之间和低级电报协议接口之间的粘合代码,所有这些都是针对每个高级协议中的接口的。

请记住,Wayland连接上的每个参与者都可以接收和发送消息。客户端正在监听事件并发送请求,而服务器正在监听请求并发送事件。每一方都在使用一个恰当地称为wl_listener的接口来监听另一方的消息。以下是这个接口的一个例子:

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的客户端监听器。wayland-scanner用于生成此监听器的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生成以下原型,以及Marshals此消息的粘合代码。

void wl_surface_attach(struct wl_surface *wl_surface,
    struct wl_buffer *buffer, int32_t x, int32_t y);

接口和监听器在服务器端代码上是相同的,但方向相反-它为请求生成监听器,并为事件生成粘合代码。当libwayland收到消息时,它会查找对象ID及其接口,然后使用该ID解码消息的其余部分。然后,它会在该对象上查找监听器并使用消息的参数调用您的函数。

这就是全部!我们经过了几层抽象才到达这里,但您现在应该理解了事件如何在您的服务器代码中启动,如何在电线上成为消息,被客户端理解,并分派到您的客户端代码中。然而,仍有一个未解决的问题。所有这些都假定您已经有了对Wayland对象的引用。您是如何获得这些引用的?

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值