Wayland协议了解

一 什么是wayland

Wayland协议,通俗的讲就是一个定义怎么来进行窗口管理的协议。

什么叫窗口管理呢? 我们知道在Windows操作系统上有很多很多的应用程序,在启动该应用程序之后,一般会弹出一个该应用程序的窗口。

这个窗口是怎么产生的? 太深入理解没必要,我们只需要知道,我们在创建窗口的时候都是必须调用指定的接口才可以创建,是吧? 这个接口就相当于窗口管理的协议提供的接口

各种形形色色的接口在不同的操作系统上是不一样的,因此如果我们要创建窗口,在不同的操作系统上就需要不同的代码,因此,跨平台就是件很麻烦的事情. 然而,在这种情况下,涌现出了大量跨平台的框架,他们把和操作系统相关的创建窗口的接口封装了起来,然后通过提供统一的接口给用户。这样一来,用户就可以使用相同的代码在不同的操作系统上创建出窗口, 比如Qt等。

上面说了,窗口在不同操作系统上调用不同的接口去创建窗口,其实是不准确的,因为,其实窗口管理理论上于操作系统不是强绑定的关系.只是有些操作系统把窗口管理给绑定到内核了,比如windows(这里我也是看资料了解的,至于正确性,我不做保证,但是现象还是很明确的).但是也有明确的例子证明,窗口管理和操作系统是不绑定的,比如高版本的ubuntu(18.04),该系统提供了两种窗口管理的方式,一种就是本文的wayland,另一种是原有的x11,但由于支持wayland协议的软件比较少,现在还比较难以流行起来。

为什么需要窗口管理呢? 我们都知道,那个窗口啥的都是通过显示器显示给我们看的,然后通过鼠标键盘等外设来操作的,但这些东西都只有一份(当然,你有多个显示器多个鼠标键盘什么的,也是可以的,但是总不能同时操作吧,显示屏多个的问题自己想想怎么反驳吧),如果现在有两个进程都需要显示出来,他们都去争夺这个显示屏去绘制,最终出来的不就花屏了?因为这两个进程是不知道对方的存在,不知道对方现在占用了哪里,因此,需要一个东西来协调才能让多个进程完美的同时显示出来,这个就是窗口管理的作用。

说实话,就上面讲的这些东西,都是在我理解wayland是什么之后才彻底明白的,因此,也希望读者能够通过本文对wayland有所了解,并且开始理解窗口管理的本质。

好了,开始说说wayland。

上面我说了wayland是一种窗口管理的协议,它和x11协议是同等的。那么,是什么样的协议呢?看下面

--------------------------------------------------------------------------------------------------------------------------------------------------

<interface name="wl_buffer" version="1">

    <description summary="content for a wl_surface">

      A buffer provides the content for a wl_surface. Buffers are

      created through factory interfaces such as wl_drm, wl_shm or

      similar. It has a width and a height and can be attached to a

      wl_surface, but the mechanism by which a client provides and

      updates the contents is defined by the buffer factory interface.

    </description>

 

    <request name="destroy" type="destructor">

      <description summary="destroy a buffer">

         Destroy a buffer. If and how you need to release the backing

         storage is defined by the buffer factory interface.

 

         For possible side-effects to a surface, see wl_surface.attach.

      </description>

    </request>

 

    <event name="release">

      <description summary="compositor releases buffer">

         Sent when this wl_buffer is no longer used by the compositor.

         The client is now free to reuse or destroy this buffer and its

         backing storage.

 

         If a client receives a release event before the frame callback

         requested in the same wl_surface.commit that attaches this

         wl_buffer to a surface, then the client is immediately free to

         reuse the buffer and its backing storage, and does not need a

         second buffer for the next surface content update. Typically

         this is possible, when the compositor maintains a copy of the

         wl_surface contents, e.g. as a GL texture. This is an important

         optimization for GL(ES) compositors with wl_shm clients.

      </description>

    </event>

</interface>

--------------------------------------------------------------------------------------------------------------------------------------------------

上面的片段是从wayland.xml里面截取出来的,以此来介绍。 Wayland的核心协议全部在wayland.xml这个xml文件里面.也有很多wayland的扩展协议,是为了实现一些其他目的而添加,

当然,读者如果学了该协议也可以自己添加一些协议内容. 为了实现一些功能,这个是非常有必要的

解析上面的xml可以分为以下标签:

Interface (接口类)
Description (描述, 可能是接口的描述,请求的描述,事件的描述等)
Request (请求,客户端主动调用的接口, 实际就是个函数)
Event (事件,服务器端主动调用的接口, 实际就是个函数)
整个wayland.xml协议文件还有以下标签:

Arg (参数, 请求或者事件的参数)
Enum (枚举, 用来更形象的代表一些值)
Entry (枚举的成员)
这些是什么东西? 如果第一次接触肯定不知道是什么了,是吧! 我这里就给大家解释一下.

Wayland协议就是通过这样一个文件(wayland/protocol/wayland.xml):描述一个客户端和服务器端相互知道的调用接口. 转换成C++的方式来理解这些你们肯定会很容易的.

首先,这些标签是有包含关系的, 一个wayland协议xml文件里包含一个或多个interface, 一个interface里面可能包含一个或多个request和event, 有些可能包含enum,也可能没有,根据实际情况.

其中最简单的enum最终就是直接转换成C语言的枚举类型,Entry即是它的成员。

现在结合上面的定义来解释:

wayland.xml协议文件里面定义一个类wl_buffer,该类是干什么的,可以通过description标签的描述知道。然后,该类有两个成员函数,一个是request(destroy),还有一个event(release), 这两个函数又有个description标签的描述该函数是用来干什么的. 但是截取的这个片段两个成员函数都没有参数,所以没有arg标签. 但是我们知道,一个函数定义的组成一般都有参数, 这个arg标签就是用来描述参数的,所以有些request和event包含arg标签, 用来描述他的参数. 然后我们知道,一个参数我们肯定得知道参数的名字,类型, 因此arg标签有一些属性是用来表示这些的,看下面:

<interface name="wl_surface" version="4">

<request name="attach">

<arg name="buffer" type="object" interface="wl_buffer" allow-null="true" summary="buffer of surface contents"/>

<arg name="x" type="int" summary="surface-local x coordinate"/>

<arg name="y" type="int" summary="surface-local y coordinate"/>

</request>

</interface>

一个参数可能有5个属性:

Name (参数的名字)
Type (参数的类型)
Interface (参数的类型)

Allow-null (是否可以为空)
Summary (描述参数是做什么的)

一个参数有几个要素, 名字类型, 然后也可能是一个类的指针(interface就是描述是指针类型)

通过上面的解释来分析buffer、x、y这个参数:

buffer 是一个wl_buffer类型的指针参数,名字是buffer,并且这个指针可以为空, 对这个指针参数的描述是” buffer of surface contents”.

X是一个int类型的参数,名字是x, 对这个参数的描述是” surface-local x coordinate”

Y是一个int类型的参数,名字是y, 对这个参数的描述是” surface-local y coordinate”

最后把上面的函数翻译成: attach(wl_buffer *buffer, int x, int y), 这样看好像有点问题,确实是的。一个函数必须有返回值,但是wayland协议里面没有描述返回值的,因此,大部分函数的返回值都是void,这里说了,只是大部分,还有小部分怎么描述呢?这个就是有一种arg类型是new_id的会作为函数的返回值类型。因此上面的函数最终会转换成:

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

然后,是不是感觉又不对? 咋多了个参数?

是这样的,wayland协议在翻译协议文件的时候,会给所有的函数添加一个默认的参数,就是它所在的interface,作为第一个参数传入进去,就相当于给一个类成员函数传递this指针一样。告诉是哪个具体对象调用的这个接口

 

好了,到这里该总结性的介绍下wayland的参数类型到底有哪些,不然你肯定不能自己翻译出来的。

首先,我必须说明一点,wayland是由C语言实现的,因此,最终协议文件肯定是翻译成c语言的语法。只是,这个协议其实理解成C++面向对象更通俗易懂。看下面:

union wl_argument {
       int32_t i;           /**< `int`    */

       uint32_t u;         /**< `uint`   */

       wl_fixed_t f;        /**< `fixed`  */

       const char *s;       /**< `string` */

       struct wl_object *o;  /**< `object` */

       uint32_t n;         /**< `new_id` */

       struct wl_array *a;   /**< `array`  */

       int32_t h;          /**< `fd`     */

};

上面这个结构体是从wayland源码里面截取出来的,由此可以看出来,wayland协议里面的参数类型有以上几种。

其中int、uint比较简单,就直接转换成32位的有符号整数和无符号整数。
Fixed类型就相当于是一个double和int的兼容类型,wayland有一段对它的解释:
/**

 * Fixed-point number

 *

 * A `wl_fixed_t` is a 24.8 signed fixed-point number with a sign bit, 23 bits

 * of integer precision and 8 bits of decimal precision. Consider `wl_fixed_t`

 * as an opaque struct with methods that facilitate conversion to and from

 * `double` and `int` types.

 */

然后实际定义为: typedef int32_t wl_fixed_t; ,并且wayland提供了几个对这种类型转换成int或者double的接口wl_fixed_to_doublewl_fixed_from_doublewl_fixed_to_intwl_fixed_from_int

String类型很简单就是char *类型
Object类型比较特殊,他表示是一种基类指针,可以接受保存所有interface的指针,因此,object类型的参数会多一个描述interface的属性,指明是哪一个interface,但是C语言哪里来的基类指针这回事?在C语言里,只有结构体,结构体的第一个成员可以看做是它的基类,不信你试试?
new_id类型就更特殊了。前面我说了,这个类型是会作为函数的返回值的,但是说的不准确,这个类型的参数在客户端被解释的时候是返回值,被返回回来给调用者使用,但是在服务器端,这个参数被转换成uint32_t类型传递过来一个id,具体为什么这么做,我们后面再说。所以,这个也算是个参数。
array类型,这个是wayland定义的一种数据结构,具体如下:
struct wl_array {
/** Array size */

size_t size;

/** Allocated space */

size_t alloc;

/** Array data */

void *data;

};

并且wayland提供了一系列对该数据结构操作的接口。

fd类型,这个类型比较简单,就是表面这个参数是个文件描述符,为什么要特别指出呢?因为底层在传递这种参数的时候,和其他的方式不一样,具体可以参考linux 进程间文件描述符传递方法。另外需要强调,文件描述符在进程间传递之后,大多数情况下,是不一样的值。

好了,所有的wayland可传递的参数都说完了,你们能不能总结出来些什么?

是的,进程间传递基本类型还好,传递文件描述符有系统的支持也还好,但是传递一个独属于一个进程的指针,这不合理了吧???哈哈哈,确实是的,因此wayland协议底层根本不是传递的指针,而是传递的一个对象的id,就是说,wayland的所有的interface对象在客户端和服务器端都有个对应关系,通过一个id来查找对应的对象。因此,进程间传递的时候只需要传递这个id,对应进程就知道是哪个具体对象了。具体的原理后面再说。这个id就和new_id类型的参数类似。

Wayland有一个专门解析wayland协议文件的工具,源码是scanner.c, 就是把wayland协议文件的内容转换成C语言的源文件,分为以下:

注意: 其中有几个interface是wayland特殊处理的,规则和一般的有点不一样,被wayland特殊处理(wl_display、wl_registry)

因此,如果想更多的了解wayland协议,可以尝试阅读此源码. 另外,Qt也实现了一个解析wayland协议的工具, 但是Qtwayland把wayland协议解释为C++语法, 就像我之前列举那样interface就解释成了一个类. 更多的可以去查看Qtwayland的源码.

我大篇幅的介绍其实是在说明怎么去解析wayland协议,但是我前面说了,wayland协议是一个窗口管理的协议, 因此,协议内容才是最关键的. 协议内容里面给出了怎么创建窗口的标准接口,对窗口的操作的标准接口,以及一些输入(键盘鼠标)获取的标准接口.只有通过这些标准接口创建的窗口管理器,就满足wayland协议,即使客户端不知道服务器端的存在,也可以根据这些标准来实现.

 

二 Wayland中的数据结构解析

为了更好的学习wayland,我们可以先学习wayland中定义的一些数据结构.因为贯穿wayland的所有东西都是基于这些数据结构.

1. wl_array


struct wl_array {
/** Array size */

size_t size;

/** Allocated space */

size_t alloc;

/** Array data */

void *data;

};

Wayland定义的数组数据结构. 其中data保存实际的数据,size保存实际数据的大小,alloc保存当前data总共分配的大小(malloc/realloc分配的大小)。

注意,alloc总是大于size,因为空间总要比保存的数据大才行,另外当往数组里面插入数据的时候,alloc不够大了,

那么就会以当前alloc大小翻倍的大小重新分配内存。因此在进行网络传输的时候,只需要把data里面的数据发送出去即可.

 

2. wl_map


struct wl_map {
struct wl_array client_entries;

struct wl_array server_entries;

uint32_t side;

uint32_t free_list;

};

union map_entry {
uintptr_t next;

 void *data;

};

这个数据结构是wayland的核心,用来保存进程间通信的实际对象指针,并得到对于指针的ID,用于进程间传递。

wl_map结构体存放wayland客户端和服务器端对应的对象。其中:

client_entries: 用wl_array数组保存客户端的对象。       (这种情况server_entries不使用)

server_entries:用wl_array数组保存服务器端的对象。   (这种情况client_entries不使用)

side:表明当前map保存的是客户端还是服务器端对象 (通过这个变量,确定client_entries/server_entries里面保存有对象,并且这个变量只在客户端初始化为WL_MAP_CLIENT_SIDE,在服务器端初始化为WL_MAP_SERVER_SIDE

free_list:这个变量用来记录当前已经被删除了的对象的存放位置,但是对这个位置做了个处理。((i << 1) | 1  : i代表下标, 也就是指针的最后一位置为1),然后,这个下标所指的位置的map_entry.next变量记录着下一个被删除的位置(直到为0,free_list初始值为0),形成链表。

提示: map的节点是map_entry结构。data保存实际的对象的地址。但是这个地址做了处理(原理上data是指针,编译器为了4字节对齐,最后两位都是0,map利用了这两位。把倒数第二位保存flags的值(0/1),最后一位表示当前对象是否已经被删除了(1表示删除,0表示没删除)),map_entry是个联合体,data成员保存实际的对象指针,而next是在对象被删除的时候,用来保存下一个被删除的对象的下标。

  flags: entry->next |= (flags & 0x1) << 1;

  deleted: map->free_list = (i << 1) | 1;

数据保存关系: wl_map保存两个wl_array数组, wl_array保存map_entry联合体的节点元素。

struct wl_array {
/** Array size */

size_t size;

/** Allocated space */

size_t alloc;

/** Array data */

void *data;

};

 

wayland协议实现基本决定了,创建对象都是客户端发起的流程是客户端请求创建对象,通过wl_map_insert_new函数插入对象,并返回对象的ID(其实是下标)然后把ID传递到服务器端,在服务器端通过wl_map_insert_at函数把创建的对象插入到指定的下标(ID),这样就建立了两个对象的对应关系。只有一个对象是特殊的,就是wl_display.这个对象是写死的下标(为1),因为这个对象肯定是第一个创建的。

3. wl_list 链表


struct wl_list {
/** Previous list element */

struct wl_list *prev;

/** Next list element */

struct wl_list *next;

};

wayland实现的这种链表在很多优秀的代码里面都有过,这种链表非常优秀,它可以保存任何类型的元素。为什么可以保存任何类型的元素呢? 我举个例子,你一定很好理解。

在中国有很多很多的家庭,如果想要把这些家庭给串联起来,可以怎么做呢?有个非常好的办法就是,我给每一个家庭一个电话簿,电话簿里面保存两个电话号码,一个是这个家庭前面一家的号码,另一个是后面一个家庭的号码。所有家庭都是这样,是不是就把他们的关系给串联起来了?任何一个家庭都可以通过他家里的电话簿联系到所有家庭。

怎么样? Wayland里面的链表就是这个电话簿一样的角色。

但是这里有个问题,就是链表里面保存的都是电话簿,要怎么把这个电话簿转换成家庭呢? 这个就是C语言的语言特性。如果知道一个结构体成员的地址,就可以反推到这个结构体的地址。

#define wl_container_of(ptr, sample, member)                \

(__typeof__(sample))((char *)(ptr) -                                    \

                         offsetof(__typeof__(*sample), member))

 

offsetof 这个是C语言标准里面提供的获取成员偏移量的宏,整个宏就是提供成员变量的地址获取到整个结构体的地址。有兴趣的读者可以多翻阅资料。

Wayland源码里面大量使用了这个结构体,来保存各种不同的结构体对象的链表。并且wayland提供了接口来更好的操作这种链表。

4. 客户端真正的对象结构体


struct wl_proxy {
struct wl_object object;

struct wl_display *display;

struct wl_event_queue *queue;

uint32_t flags;

int refcount;

void *user_data;

wl_dispatcher_func_t dispatcher;

uint32_t version;

};

 

在这里我要告诉大家一个事实:

Wayland协议里面的那些interface的对象在客户端其实真正的结构体是wl_proxy而那些结构体都是不存在的,只是一个声明而已,根本不存在。如果读者有看过wayland的源码,会发现一个问题,就是wayland协议里面的那些interface对象,从来都不是程序员自己创建出来的,而是通过wayland的一些接口返回回来的全部都是,无一例外。读者如果不相信可以自己去找找,并且可以自己尝试创建那些对象,肯定是会报错的,因为那些结构体都是不存在的,创建会编译出错。

 

5. 服务器端真正的对象结构体


struct wl_resource {
struct wl_object object;

wl_resource_destroy_func_t destroy;

struct wl_list link;

struct wl_signal destroy_signal;

struct wl_client *client;

void *data;

};

 

和客户端一样,服务器端所有的interface对象全部都是wl_resource结构体对象。

 

6. 总结一下结构体

看下图:

从上图中可知,所有的对象都有个wl_object成员来记录它是属于哪个interface的,并用implementation成员来保存真正需要调用的函数指针,这个id就是用来进行客户端和服务器端对象映射的关键。

然后wl_interface就是wayland协议里面的interface,一个inerface有名字属性,还有版本号,版本号是非常有用的东西。而interface里面的request和event就是由wl_message结构体保存,表示一个函数。wl_message结构体包含函数名,以及函数的参数(所有的参数都保存在signature变量里面,通过字符的方式表示),方法如下:(wayland源码里面的解释)

/**

 * Protocol message signature

 *

 * A wl_message describes the signature of an actual protocol message, such as a

 * request or event, that adheres to the Wayland protocol wire format. The

 * protocol implementation uses a wl_message within its demarshal machinery for

 * decoding messages between a compositor and its clients. In a sense, a

 * wl_message is to a protocol message like a class is to an object.

 *

 * The `name` of a wl_message is the name of the corresponding protocol message.

 *

 * The `signature` is an ordered list of symbols representing the data types

 * of message arguments and, optionally, a protocol version and indicators for

 * nullability. A leading integer in the `signature` indicates the _since_

 * version of the protocol message. A `?` preceding a data type symbol indicates

 * that the following argument type is nullable. While it is a protocol violation

 * to send messages with non-nullable arguments set to `NULL`, event handlers in

 * clients might still get called with non-nullable object arguments set to

 * `NULL`. This can happen when the client destroyed the object being used as

 * argument on its side and an event referencing that object was sent before the

 * server knew about its destruction. As this race cannot be prevented, clients

 * should - as a general rule - program their event handlers such that they can

 * handle object arguments declared non-nullable being `NULL` gracefully.

 *

 * When no arguments accompany a message, `signature` is an empty string.

 *

 * Symbols:

 *

 * * `i`: int

 * * `u`: uint

 * * `f`: fixed

 * * `s`: string

 * * `o`: object

 * * `n`: new_id

 * * `a`: array

 * * `h`: fd

 * * `?`: following argument is nullable

 *

 * While demarshaling primitive arguments is straightforward, when demarshaling

 * messages containing `object` or `new_id` arguments, the protocol

 * implementation often must determine the type of the object. The `types` of a

 * wl_message is an array of wl_interface references that correspond to `o` and

 * `n` arguments in `signature`, with `NULL` placeholders for arguments with

 * non-object types.

 *

 * Consider the protocol event wl_display `delete_id` that has a single `uint`

 * argument. The wl_message is:

 *

 * \code

 * { "delete_id", "u", [NULL] }

 * \endcode

 *

 * Here, the message `name` is `"delete_id"`, the `signature` is `"u"`, and the

 * argument `types` is `[NULL]`, indicating that the `uint` argument has no

 * corresponding wl_interface since it is a primitive argument.

 *

 * In contrast, consider a `wl_foo` interface supporting protocol request `bar`

 * that has existed since version 2, and has two arguments: a `uint` and an

 * object of type `wl_baz_interface` that may be `NULL`. Such a `wl_message`

 * might be:

 *

 * \code

 * { "bar", "2u?o", [NULL, &wl_baz_interface] }

 * \endcode

 *

 * Here, the message `name` is `"bar"`, and the `signature` is `"2u?o"`. Notice

 * how the `2` indicates the protocol version, the `u` indicates the first

 * argument type is `uint`, and the `?o` indicates that the second argument

 * is an object that may be `NULL`. Lastly, the argument `types` array indicates

 * that no wl_interface corresponds to the first argument, while the type

 * `wl_baz_interface` corresponds to the second argument.

 **/

我简单解释一下:wayland参数不是就那几种吗?这个地方就把这几个参数再缩短标记,用一个字符来表示,但是有个特殊的‘?’,它表示后面这个参数可以为空。在参数的最前面有一个数字,这个数字代表着版本号。

但是呢,参数中有可能是interface的对象,那么就必须指明到底是哪个interface的对象,因此wl_message的最后一个成员types,就用一个数组的方式记录这个参数是哪个interface的对象,如果是非interface的参数就为空。

最后,我们来得出,wayland解析协议文件产生了些什么,其实,看上面的图片就能知道很多东西。看下面的文件:

产生一个协议源文件, 里面保存了协议文件里面所有的interface转换而成的wl_interface结构体变量,包括wl_message结构体记录的request和event函数。
产生一个客户端使用的头文件,里面封装了wl_proxy转换成指定interface假声明的结构体操作的一些接口函数以及request函数的实现,但是这个实现只是把请求发送到服务器端而已,实际调用在服务器端进行。最后,文件里面还封装了一个回调函数的结构体,成员就是所有的event函数指针,需要客户端去实现,并设置到interface的对象里面,该文件生成了这个设置的接口,实际就是填充到wl_object结构体的implementation变量中。
产生一个服务器端头文件,里面基本和客户端一样的组成。只是结构体是wl_resource,函数结构体的成员是所有request的函数指针。以及所有的event的实现。
也就是说,客户端需要程序员自己实现事件(event),服务器端需要程序员实现请求(request)。

三 Wayland的工作原理

 

首先,需要了解wayland的工作原理,需要了解几个其他的内容。

int mkstemp(char *template); // 创建临时文件 (标C接口)
long int strtol(const char *nptr,char **endptr,int base); // 字符串转数字
ssize_t sendmsg (int s, const struct msghdr *msg, int flags); // 可以往其他进程发送fd的函数
众所周知,子进程会继承父进程已经打开的文件描述符fd,但是fork之后的是不会被继承的,这个时候是否无能无力了?答应是NO。Linux提供了一个系统调用sendmsg,借助它,可以实现进程间传递文件描述符fd,而且不仅限于父进程到子进程。sendmsg函数的原型如下:

-----------------------------------------------------------------------------------------------------------------

#include <sys/socket.h>

ssize_t sendmsg(int socket, const struct msghdr *message, int flags);

ssize_t recvmsg(int socket, struct msghdr *message, int flags);

-----------------------------------------------------------------------------------------------------------------

recvmsg函数用来接收fd,这里的socket必须为UnixSocket(AF_UNIX),在Linux上执行man 7 unix,并搜索SCM_RIGHTS,即看到有关说明:Send or receive a set of open file descriptors from another process。

通过sendmsg发送的fd,并不是将fd值传递给目标进程,而是活生生地在目标进程空间里复制指向同一个file结构体的fd,所以不要期望在两个进程中,fd值相同。

具体的使用示例,请baidu或google关键词:sendmsg fd,即可找到,这里就不多说了。

 

int ftruncate(int fd,off_t length);   // 修改文件大小
epoll函数  // 实现一个fd监听多个fd的效果
int epoll_create(int size)   // 创建epoll fd描述符

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) // 监听其他的文件描述符

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)   // 启动监听

epoll函数(百度百科)

int socket(int domain, int type, int protocol); // 创建一个网络描述符.如果第一个参数是AF_UNIX, 获取到的描述符可以传递文件描述符(通过sendmsg函数), 实现本地的进程间通信
int bind(SOCKET socket, const struct sockaddr* address, socklen_t address_len);

// 在上一个函数使用AF_UNIX参数创建时, bind函数指定一个文件路径来实现进程间通信, 使用结构体(sockaddr_un)保存文件路径

int listen( int sockfd, int backlog); // 启动监听网络套接字

int accept( int fd, struct socketaddr* addr, socklen_t* len); // 接受连接进来的客户端

/* Request notification for delivery of signals in MASK to be
performed using descriptor FD.*/

extern int signalfd (int __fd, const sigset_t *__mask, int __flags) // 产生信号监听fd

/* Clear all signals from SET.  */

extern int sigemptyset (sigset_t *__set) __THROW __nonnull ((1));

/* Set all signals in SET.  */

extern int sigfillset (sigset_t *__set) __THROW __nonnull ((1));

/* Add SIGNO to SET.  */

extern int sigaddset (sigset_t *__set, int __signo) __THROW __nonnull ((1));

// 让信号在被处理之前程序都处于阻塞状态

extern int sigprocmask (int __how, const sigset_t *__restrict __set,

                    sigset_t *__restrict __oset)

// 以上API  创建一个信号的fd

Libffi库,FFI(Foreign Function Interface)允许以一种语言编写的代码调用另一种语言的代码,而libffi库提供了最底层的、与架构相关的、完整的FFI

libffi的作用就相当于编译器,它为多种调用规则提供了一系列高级语言编程接口,然后通过相应接口完成函数调用,底层会根据对应的规则,完成数据准备,生成相应的汇编指令代码。
 

我来描述一下,wayland是怎么把上面的知识点给联系起来的。

首先,我们必须要清楚wayland是一个CS架构的协议,所以肯定涉及到通信,但是wayland的CS局限于同一个操作系统,因为底层会利用操作系统的特性来传递进程中的文件描述符(文件句柄),接下来我们分为服务器端和客户端来分析:

首先服务器端创建一个epoll_fd,为什么需要创建呢?因为服务器端会监听非常多的fd,而epoll_fd是linux最高效的用来监听大量fd的方法(sylixos操作系统的epoll_fd是模拟出来的,不具有linux那样的高效特性,但是确实还是支持,只是不能去select epoll_fd)。
服务器端创建一个绑定到指定文件(默认文件名为wayland-%d,路径由XDG_RUNTIME_DIR环境变量指定)的socket,并把这个socket添加到epoll_fd,在此之前,服务器端还需要注册很多客户端能用的服务(通过这个接口注册:wl_global_create),并开始等待客户端的连接,随后连接进来的socket也会添加到epoll_fd里面一起监听。
这时客户端可以开始连接服务器端了,通过wl_display_connect返回客户端wl_display句柄,这个结构体内部就包含了socket连接的fd,并可以和服务器进行通信了。
客户端调用的第一个请求只能是获取服务器端有哪些服务可以使用(因为其他的接口都是各种服务提供的请求):wl_display_get_registry该请求返回一个结构体wl_registry,就是我前面说的客户端所有的interface对象实际上都是wl_proxy。然后给这个结构体设置事件处理函数这样客户端在接收到服务器端消息的时候会转换成函数调用,调用到这个事件处理函数。(这里我要说明一点。客户端和服务器通信的数据全是函数调用的序列化数据(包括函数调用的参数及函数名(函数名用一个数字代表)),接收端再反序列化为函数调用,最后去调用该函数),这个函数调用用到了libffi的接口,函数序列化数据传递用的sendmsg接口,可以额外传递文件描述符,但是传递方式和一般的不太一样,所以才会大费周章的把fd做为特殊的参数类型,就是在这里用的。
 

四 Wayland的所有接口

在介绍这些类的时候我必须给大家把waylandinterface分个类.

Global Object, 我把它叫做服务类, 就是提供给客户端各种功能的接口。

就相当于是说,对于客户端来说,我给你提供了哪些服务,你就只能通过这些服务来进行各种操作,因此,服务越多,肯定功能越多。

服务类,根据实际情况是不一样的,并且可以根据需要服务端再添加更多的服务,wayland标准提供的服务接口有以下一些:
wl_compositor
wl_shm
wl_shell
wl_seat
wl_output
wl_subcompositor
wl_data_device_manager

 

依赖服务创建的接口类. 为什么这么说呢? 因为这样的接口对象,都是通过服务类提供的接口产生的.

理解wayland就要明白, wayland的所有对象都是道生一,一生二,二生三,三生万物. 通俗一点,就是wayland给你提供了道(wl_display), 后续的所有对象都是基于它产生的. 你最终能产生多少对象完全看你怎么去发挥了。

 

1. wl_display


这个是wayland协议的核心类,它作为客户端的标识,一个wl_display对象代表着一个客户端,因为这个对象建立了和wayland服务器通信的socket,所有和服务器交互都是通过这个socket。这个也是客户端必须第一个创建的wayland对象。也就是我上面所说的‘道’。

wl_display接口类提供了两个请求函数,和两个事件函数。前面我说过了,关于什么是请求和事件。

但是,对于wl_display这个接口对象,它的事件处理函数,是wayland源码里面提供的。用户在不修改源码的情况下,是不能再设置的。接下来说说这几个函数:

请求函数:

① static inline struct wl_callback * wl_display_sync(struct wl_display *wl_display)
{
    struct wl_proxy *callback;

    callback = wl_proxy_marshal_constructor((struct wl_proxy *) wl_display,
             WL_DISPLAY_SYNC, &wl_callback_interface, NULL);

    return (struct wl_callback *) callback;
}

sync,这个请求函数的作用是返回一个wl_callback对象,这个对象有个事件release,是当服务器端处理你这个sync请求后主动调用的,也就是说,当你调用sync之后,你设置了release事件处理函数,那么当你的release处理函数被调用的时候,就代表你当前的请求已经得到响应了这个接口的作用是提供客户端一个确认你前面所有的请求都被处理的保证。(wayland有个接口wl_display_roundtrip就是利用了这一点,因此,你完全可以自己实现这个功能,通过这个请求。)

②static inline struct wl_registry * wl_display_get_registry(struct wl_display *wl_display)
{
    struct wl_proxy *registry;

    registry = wl_proxy_marshal_constructor((struct wl_proxy *) wl_display,
             WL_DISPLAY_GET_REGISTRY, &wl_registry_interface, NULL);

    return (struct wl_registry *) registry;

get_registry这个请求基本是客户端调用的第二个wayland函数(第一个是获取wl_display的函数),然后返回一个wl_registry对象,这个对象是干什么的?这个对象就是可以告诉你,wayland服务器端给你通过了哪些你可以使用的服务

这些服务才是你创建wayland窗口的核心。

事件函数:

①  void (*error)(void *data,struct wl_display *wl_display,void *object_id,uint32_t code,const char *message);

error, 这个函数是在服务器端向客户端报错用的。

②     void (*delete_id)(void *data,struct wl_display *wl_display, uint32_t id);

delete_id,这个接口是在服务器端删除客户端对象的时候用的,目的是告诉客户端,某一个对象可以安全的delete了。

 

2. wl_registry


这个也是wayland的核心类,这个类对象通过wl_display的请求函数(wl_display_get_registry得到,然后服务器端收到这个请求之后就会传递给客户端,目前可以使用的所有服务信息。客户端再根据自己的需要,通过bind这个请求函数得到服务的对象,就可以使用这些服务提供的接口了。

① static inline void * wl_registry_bind(struct wl_registry *wl_registry, uint32_t name, const struct wl_interface *interface, uint32_t version)
{
    struct wl_proxy *id;

    id = wl_proxy_marshal_constructor_versioned((struct wl_proxy *) wl_registry,
             WL_REGISTRY_BIND, interface, version, name, interface->name, version, NULL);

    return (void *) id;
}

bind请求函数,得到指定服务的对象,后面的操作就需要这些服务对象来调用接口。

②  void (*global)(void *data,struct wl_registry *wl_registry,uint32_t name,const char *interface,uint32_t version);
global,事件函数,通过这个函数客户端(一次调用得到一个服务信息,服务器端有多少服务,这个事件函数就会调用多少次。

③  void (*global_remove)(void *data,struct wl_registry *wl_registry, uint32_t name);

global_remove, 事件函数,这个函数也很明了,和上一个相反,当服务器端某一个服务停止了,就会调用这个事件告诉客户端这个服务已经不提供了,不能再通过这个服务获取请求。
 

3. wl_callback


这个接口就是单纯通过给客户端,同步各类请求的接口

void (*done)(void *data,struct wl_callback *wl_callback,uint32_t callback_data);

只有一个事件函数,done,当服务器端收到创建这个对象的时候,立马就往客户端发送done事件

done,事件函数,当wl_callback对象创建请求被服务器端收到并创建的时候,立马调用这个函数有个参数,提供给客户端用来修改,方便知道这个函数已经被调用了。
 

4. wl_compositor


这个就是创建窗口的灵魂,因为它是wayland提供创建窗口的服务

static inline struct wl_surface * wl_compositor_create_surface(struct wl_compositor *wl_compositor)
{
    struct wl_proxy *id;

    id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_compositor,
             WL_COMPOSITOR_CREATE_SURFACE, &wl_surface_interface, NULL);

    return (struct wl_surface *) id;
}

create_surface,这个请求函数用来创建窗口绘制的实体,但是它不是真正的窗口,只是窗口上面的内容,意思就是,可能存在很多的窗口,这些窗口上面的内容都是这个surface。后面我会专门拿一章来详细说明这一点。这个接口返回wl_surface对象

static inline struct wl_region * wl_compositor_create_region(struct wl_compositor *wl_compositor)
{
    struct wl_proxy *id;

    id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_compositor,
             WL_COMPOSITOR_CREATE_REGION, &wl_region_interface, NULL);

    return (struct wl_region *) id;
}
 

create_region,这个请求函数返回一个wl_region对象,这个对象的作用是描述一块wl_surface上的区域,以便对这个区域进行操作。
 

5. wl_region

窗口区域

请求函数:

① static inline void wl_region_destroy(struct wl_region *wl_region)
{
    wl_proxy_marshal((struct wl_proxy *) wl_region,
             WL_REGION_DESTROY);

    wl_proxy_destroy((struct wl_proxy *) wl_region);
}

destroy

② static inline void wl_region_add(struct wl_region *wl_region, int32_t x, int32_t y, int32_t width, int32_t height)
{
    wl_proxy_marshal((struct wl_proxy *) wl_region,
             WL_REGION_ADD, x, y, width, height);
}
add

③ static inline void wl_region_subtract(struct wl_region *wl_region, int32_t x, int32_t y, int32_t width, int32_t height)
{
    wl_proxy_marshal((struct wl_proxy *) wl_region,
             WL_REGION_SUBTRACT, x, y, width, height);
}

subtract

6. wl_shm


这个是wayland提供的客户端和服务器端共享内存的服务。共享内存一般是用来作为窗口绘制内容的容器(相当于保存图片内容的内存)。因此,这个接口提供了一个枚举类型format来描述这个内存是什么格式的,因为不同的屏幕使用不同的图片格式。这个服务还提供了一个错误类型的枚举error

请求函数:

static inline struct wl_shm_pool * wl_shm_create_pool(struct wl_shm *wl_shm, int32_t fd, int32_t size)
{
    struct wl_proxy *id;

    id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_shm,
             WL_SHM_CREATE_POOL, &wl_shm_pool_interface, NULL, fd, size);

    return (struct wl_shm_pool *) id;
}
create_pool,这个是请求函数,用来得到一个共享内存的对象wl_shm_pool,用于客户端和服务器端的共享。(原理是客户端传递给服务器端内存的文件描述符句柄fd,服务器端再通过这个文件描述符句柄操作那段内存)
format,这个是事件函数,当服务器端收到这个服务的绑定请求的时候就会发送给客户端。告诉客户端目前支持的图片格式有哪些。(应该也是一种格式调用一次)
 

7. wl_shm_pool


这个就是共享内存对象,一般操作方式就是客户端创建一个fd,然后mmap到一段内存,以供客户端使用,最后把这个fd传递给服务器端,让服务器端去读取它(一般情况是,客户端写,服务器端读)

但是,对于窗口的绘制,不是使用这个对象,而是通过这个对象的请求函数create_buffer函数返回的wl_buffer对象作为真正保存客户端绘制窗口内容的实体。

因此,很多情况下都是需要多大的wl_buffer就创建对应多大的wl_shm_pool,至少我在Qtwayland的源码里面就发现是这样的,因此wl_buffer和wl_shm_pool是1:1的关系。

请求函数:

① static inline struct wl_buffer * wl_shm_pool_create_buffer(struct wl_shm_pool *wl_shm_pool, int32_t offset, int32_t width, int32_t height, int32_t stride, uint32_t format)
{
    struct wl_proxy *id;

    id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_shm_pool,
             WL_SHM_POOL_CREATE_BUFFER, &wl_buffer_interface, NULL, offset, width, height, stride, format);

    return (struct wl_buffer *) id;
}

create_buffer,请求函数,利用共享内存的空间来创建窗口绘制使用的wl_buffer。如果,想一个wl_shm_pool对象创建多个wl_buffer就需要去切割内存,并管理,这个是比较麻烦的一件事,因此使wl_shm_pool和wl_buffer 1:1对应,最简单。

② static inline void wl_shm_pool_destroy(struct wl_shm_pool *wl_shm_pool)
{
    wl_proxy_marshal((struct wl_proxy *) wl_shm_pool,
             WL_SHM_POOL_DESTROY);

    wl_proxy_destroy((struct wl_proxy *) wl_shm_pool);
}
 

destroy, 请求函数,用来销毁共享内存池

③ static inline void wl_shm_pool_resize(struct wl_shm_pool *wl_shm_pool, int32_t size)
{
    wl_proxy_marshal((struct wl_proxy *) wl_shm_pool,
             WL_SHM_POOL_RESIZE, size);
}

resize,请求函数,用来改变内存池大小,这个是很麻烦的,因为客户端需要你自己去重新映射内存,并且只能变大不能变小。因此,一般不用这个接口,想要更大的空间就重新再申请一个,也不去调用这个接口扩大
 

8. wl_buffer


这个对象就是wl_shm_pool提供的接口返回的(wayland文档描述,wl_buffer还可以通过其他的途径创建,比如wl_drm),并且这个wl_buffer基本是和wl_surface大小绑定的,也就是wl_surface有多大,wl_buffer就有多大。

请求函数:

static inline void wl_buffer_destroy(struct wl_buffer *wl_buffer)
{
    wl_proxy_marshal((struct wl_proxy *) wl_buffer,
             WL_BUFFER_DESTROY);

    wl_proxy_destroy((struct wl_proxy *) wl_buffer);
}
 

destroy,这个请求函数用来销毁一个wl_buffer

事件函数:

void (*release)(void *data,struct wl_buffer *wl_buffer);

release,这个事件函数,是在服务器端已经读取过该wl_buffer之后,主动发送的事件,告诉客户端这个buffer客户端可以重复使用了,一般用来再重绘。如果没有收到这个事件,那么这个wl_buffer理论上就不能再修改。

 

9. wl_surface


这个是由wl_compositor通过的接口创建的窗口绘制的抽象实体,但并不是真正的显示屏上看到的窗口,所以这个就是合成器管理的核心,他把所有的窗口都抽象成这样的数据结构,然后最终再输出到不同的外设,显示真正的窗口(wl_shell_surface等)。这个对象实际的内容保存在wl_buffer中。

请求函数:

① static inline void wl_surface_destroy(struct wl_surface *wl_surface)
{
    wl_proxy_marshal((struct wl_proxy *) wl_surface,
             WL_SURFACE_DESTROY);

    wl_proxy_destroy((struct wl_proxy *) wl_surface);
}

destroy

 static inline void wl_surface_attach(struct wl_surface *wl_surface, struct wl_buffer *buffer, int32_t x, int32_t y)
{
    wl_proxy_marshal((struct wl_proxy *) wl_surface,
             WL_SURFACE_ATTACH, buffer, x, y);
}

attach,绑定wl_buffer到wl_surface

③ static inline void wl_surface_damage(struct wl_surface *wl_surface, int32_t x, int32_t y, int32_t width, int32_t height)
{
    wl_proxy_marshal((struct wl_proxy *) wl_surface,
             WL_SURFACE_DAMAGE, x, y, width, height);
}
damage

④ static inline struct wl_callback * wl_surface_frame(struct wl_surface *wl_surface)
{
    struct wl_proxy *callback;

    callback = wl_proxy_marshal_constructor((struct wl_proxy *) wl_surface,
             WL_SURFACE_FRAME, &wl_callback_interface, NULL);

    return (struct wl_callback *) callback;
}
frame

⑤ static inline void
wl_surface_set_opaque_region(struct wl_surface *wl_surface, struct wl_region *region)
{
    wl_proxy_marshal((struct wl_proxy *) wl_surface,
             WL_SURFACE_SET_OPAQUE_REGION, region);
}
set_opaque_region

⑥ static inline void
wl_surface_set_input_region(struct wl_surface *wl_surface, struct wl_region *region)
{
    wl_proxy_marshal((struct wl_proxy *) wl_surface,
             WL_SURFACE_SET_INPUT_REGION, region);
}
set_input_region

⑦ static inline void
wl_surface_set_buffer_transform(struct wl_surface *wl_surface, int32_t transform)
{
    wl_proxy_marshal((struct wl_proxy *) wl_surface,
             WL_SURFACE_SET_BUFFER_TRANSFORM, transform);
}
set_buffer_transform

⑧ static inline void
wl_surface_set_buffer_scale(struct wl_surface *wl_surface, int32_t scale)
{
    wl_proxy_marshal((struct wl_proxy *) wl_surface,
             WL_SURFACE_SET_BUFFER_SCALE, scale);
}
set_buffer_scale

⑨ static inline void
wl_surface_damage_buffer(struct wl_surface *wl_surface, int32_t x, int32_t y, int32_t width, int32_t height)
{
    wl_proxy_marshal((struct wl_proxy *) wl_surface,
             WL_SURFACE_DAMAGE_BUFFER, x, y, width, height);
}
damage_buffer

⑩ static inline void
wl_surface_commit(struct wl_surface *wl_surface)
{
    wl_proxy_marshal((struct wl_proxy *) wl_surface,
             WL_SURFACE_COMMIT);
}
commit


事件函数:

①  void (*enter)(void *data,
              struct wl_surface *wl_surface,
              struct wl_output *output)

enter

②  void (*leave)(void *data,
              struct wl_surface *wl_surface,
              struct wl_output *output);
leave

10. wl_shell


wl_shell是管理真正窗口的服务接口类。通过这个接口,可以创建某一个wl_surface的显示用窗口。当然,你也可以创建多个同一个wl_surface的窗口,这样就显示多个一模一样的窗口了。这个服务接口有很多同类的服务(比如xdg_shell等)。就相当于,你可以创建linux下的窗口,也可以创建winodws下的窗口一样。这个有点像Qt的思想,这个就相当于Qt的显示后端一样。

请求函数:get_shell_surface

static inline struct wl_shell_surface *
wl_shell_get_shell_surface(struct wl_shell *wl_shell, struct wl_surface *surface)
{
    struct wl_proxy *id;

    id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_shell,
             WL_SHELL_GET_SHELL_SURFACE, &wl_shell_surface_interface, NULL, surface);

    return (struct wl_shell_surface *) id;
}

创建指定wl_surface的显示窗口对象wl_shell_surface。一般情况下,一个wl_surface对应一个wl_shell_surface。但,你完全可以一对多

11. wl_shell_surface


这个就是真正的窗口了。但是由于wl_shell协议功能不完善,无法支持一些功能特性,所以官方在文档都说明了,将会用xdg_shell替代,创建xdg_shell_surface窗口。但,这两种协议的窗口本来就是同等的,可以同时存在。

请求函数:

① static inline void
wl_shell_surface_pong(struct wl_shell_surface *wl_shell_surface, uint32_t serial)
{
    wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
             WL_SHELL_SURFACE_PONG, serial);
}

pong, 用来响应ping事件函数,相当于网络通信的心跳包,确认有效性,如果不调用,就相当于窗口无响应,可能被杀掉该窗口。

static inline void
wl_shell_surface_move(struct wl_shell_surface *wl_shell_surface, struct wl_seat *seat, uint32_t serial)
{
    wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
             WL_SHELL_SURFACE_MOVE, seat, serial);
}

move

③ static inline void
wl_shell_surface_resize(struct wl_shell_surface *wl_shell_surface, struct wl_seat *seat, uint32_t serial, uint32_t edges)
{
    wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
             WL_SHELL_SURFACE_RESIZE, seat, serial, edges);
}

resize

④ static inline void
wl_shell_surface_set_toplevel(struct wl_shell_surface *wl_shell_surface)
{
    wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
             WL_SHELL_SURFACE_SET_TOPLEVEL);
}

set_toplevel

enum wl_shell_surface_transient {
    /**
     * do not set keyboard focus
     */
    WL_SHELL_SURFACE_TRANSIENT_INACTIVE = 0x1,
};

transient

⑥ static inline void
wl_shell_surface_set_transient(struct wl_shell_surface *wl_shell_surface, struct wl_surface *parent, int32_t x, int32_t y, uint32_t flags)
{
    wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
             WL_SHELL_SURFACE_SET_TRANSIENT, parent, x, y, flags);
}

set_transient
⑦ static inline void
wl_shell_surface_set_fullscreen(struct wl_shell_surface *wl_shell_surface, uint32_t method, uint32_t framerate, struct wl_output *output)
{
    wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
             WL_SHELL_SURFACE_SET_FULLSCREEN, method, framerate, output);
}

set_fullscreen
⑧ static inline void
wl_shell_surface_set_popup(struct wl_shell_surface *wl_shell_surface, struct wl_seat *seat, uint32_t serial, struct wl_surface *parent, int32_t x, int32_t y, uint32_t flags)
{
    wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
             WL_SHELL_SURFACE_SET_POPUP, seat, serial, parent, x, y, flags);
}

set_popup

⑨ static inline void
wl_shell_surface_set_maximized(struct wl_shell_surface *wl_shell_surface, struct wl_output *output)
{
    wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
             WL_SHELL_SURFACE_SET_MAXIMIZED, output);
}

set_maximized

⑩static inline void
wl_shell_surface_set_title(struct wl_shell_surface *wl_shell_surface, const char *title)
{
    wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
             WL_SHELL_SURFACE_SET_TITLE, title);
}

set_title
static inline void
wl_shell_surface_set_class(struct wl_shell_surface *wl_shell_surface, const char *class_)
{
    wl_proxy_marshal((struct wl_proxy *) wl_shell_surface,
             WL_SHELL_SURFACE_SET_CLASS, class_);
}

set_class


事件函数:

① void (*ping)(void *data,
             struct wl_shell_surface *wl_shell_surface,
             uint32_t serial);

ping

 void (*ping)(void *data,
             struct wl_shell_surface *wl_shell_surface,
             uint32_t serial)

configure
③ void (*popup_done)(void *data,
               struct wl_shell_surface *wl_shell_surface);

popup_done
 

12. wl_seat


这个是服务类接口,提供客户端获取键盘鼠标事件的能力以及触摸屏。并且设备可能有很多套,因此,用name去区分不同的设备

请求函数:

① static inline struct wl_pointer *
wl_seat_get_pointer(struct wl_seat *wl_seat)
{
    struct wl_proxy *id;

    id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_seat,
             WL_SEAT_GET_POINTER, &wl_pointer_interface, NULL);

    return (struct wl_pointer *) id;
}
get_pointer

② static inline struct wl_keyboard *
wl_seat_get_keyboard(struct wl_seat *wl_seat)
{
    struct wl_proxy *id;

    id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_seat,
             WL_SEAT_GET_KEYBOARD, &wl_keyboard_interface, NULL);

    return (struct wl_keyboard *) id;
} 
get_keyboard

③ static inline struct wl_touch *
wl_seat_get_touch(struct wl_seat *wl_seat)
{
    struct wl_proxy *id;

    id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_seat,
             WL_SEAT_GET_TOUCH, &wl_touch_interface, NULL);

    return (struct wl_touch *) id;
}

get_touch

④ static inline void
wl_seat_release(struct wl_seat *wl_seat)
{
    wl_proxy_marshal((struct wl_proxy *) wl_seat,
             WL_SEAT_RELEASE);

    wl_proxy_destroy((struct wl_proxy *) wl_seat);
}

release
事件函数:

① void (*capabilities)(void *data,
                 struct wl_seat *wl_seat,
                 uint32_t capabilities);

capabilities,首先提供事件事件函数获取到,当前这个seat支持哪些事件的监听,再创建对应的可以使用的对象(请求函数的返回对象)。
② void (*name)(void *data,
             struct wl_seat *wl_seat,
             const char *name);

name
 

13. wl_pointer

监听鼠标数据的对象

请求函数:

① static inline void
wl_pointer_set_cursor(struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, int32_t hotspot_x, int32_t hotspot_y)
{
    wl_proxy_marshal((struct wl_proxy *) wl_pointer,
             WL_POINTER_SET_CURSOR, serial, surface, hotspot_x, hotspot_y);
}

set_cursor

② static inline void
wl_pointer_release(struct wl_pointer *wl_pointer)
{
    wl_proxy_marshal((struct wl_proxy *) wl_pointer,
             WL_POINTER_RELEASE);

    wl_proxy_destroy((struct wl_proxy *) wl_pointer);
}
release
事件函数:

①     void (*enter)(void *data,
              struct wl_pointer *wl_pointer,
              uint32_t serial,
              struct wl_surface *surface,
              wl_fixed_t surface_x,
              wl_fixed_t surface_y);

enter
②    void (*leave)(void *data,
              struct wl_pointer *wl_pointer,
              uint32_t serial,
              struct wl_surface *surface);

leave
③     void (*motion)(void *data,
               struct wl_pointer *wl_pointer,
               uint32_t time,
               wl_fixed_t surface_x,
               wl_fixed_t surface_y);

motion
④     void (*button)(void *data,
               struct wl_pointer *wl_pointer,
               uint32_t serial,
               uint32_t time,
               uint32_t button,
               uint32_t state);

button
⑤     void (*axis)(void *data,
             struct wl_pointer *wl_pointer,
             uint32_t time,
             uint32_t axis,
             wl_fixed_t value);

axis
⑥     void (*frame)(void *data,
              struct wl_pointer *wl_pointer);

frame
⑦    void (*axis_source)(void *data,
                struct wl_pointer *wl_pointer,
                uint32_t axis_source);

axis_source
⑧    void (*axis_stop)(void *data,
              struct wl_pointer *wl_pointer,
              uint32_t time,
              uint32_t axis);

axis_stop
⑨    void (*axis_discrete)(void *data,
                  struct wl_pointer *wl_pointer,
                  uint32_t axis,
                  int32_t discrete);

axis_discrete
 

14. wl_keyboard


监听键盘事件的对象

 请求函数:

static inline void
wl_keyboard_release(struct wl_keyboard *wl_keyboard)
{
    wl_proxy_marshal((struct wl_proxy *) wl_keyboard,
             WL_KEYBOARD_RELEASE);

    wl_proxy_destroy((struct wl_proxy *) wl_keyboard);
}

release
事件函数:

①    void (*keymap)(void *data,
               struct wl_keyboard *wl_keyboard,
               uint32_t format,
               int32_t fd,
               uint32_t size);

keymap

②     void (*enter)(void *data,
              struct wl_keyboard *wl_keyboard,
              uint32_t serial,
              struct wl_surface *surface,
              struct wl_array *keys);

enter

③     void (*leave)(void *data,
              struct wl_keyboard *wl_keyboard,
              uint32_t serial,
              struct wl_surface *surface);
leave

④     void (*key)(void *data,
            struct wl_keyboard *wl_keyboard,
            uint32_t serial,
            uint32_t time,
            uint32_t key,
            uint32_t state);

key

⑤     void (*repeat_info)(void *data,
                struct wl_keyboard *wl_keyboard,
                int32_t rate,
                int32_t delay);
repeat_info

15. wl_touch


监听触摸屏事件的对象

请求函数

static inline void
wl_touch_release(struct wl_touch *wl_touch)
{
    wl_proxy_marshal((struct wl_proxy *) wl_touch,
             WL_TOUCH_RELEASE);

    wl_proxy_destroy((struct wl_proxy *) wl_touch);
}

 

release
事件函数:

①    void (*down)(void *data,
             struct wl_touch *wl_touch,
             uint32_t serial,
             uint32_t time,
             struct wl_surface *surface,
             int32_t id,
             wl_fixed_t x,
             wl_fixed_t y);
 

down

②    void (*up)(void *data,
           struct wl_touch *wl_touch,
           uint32_t serial,
           uint32_t time,
           int32_t id);

up
③    void (*motion)(void *data,
               struct wl_touch *wl_touch,
               uint32_t time,
               int32_t id,
               wl_fixed_t x,
               wl_fixed_t y);
 

motion
④     void (*frame)(void *data,
              struct wl_touch *wl_touch);

frame
⑤     void (*cancel)(void *data,
               struct wl_touch *wl_touch);
 

cancel
⑥     void (*shape)(void *data,
              struct wl_touch *wl_touch,
              int32_t id,
              wl_fixed_t major,
              wl_fixed_t minor);

shape,看到没有,纯天然的手势支持服务(当然要看服务端有没有实现了T_T)
⑦    void (*orientation)(void *data,
                struct wl_touch *wl_touch,
                int32_t id,
                wl_fixed_t orientation);

orientation

16. wl_output


这个是服务对象,代表着屏幕一个屏幕对应一个服务对象,因此,如果有多屏幕,这个服务就有多个。屏幕可以通过事件函数获取它的大小,刷新率等属性

请求函数:

static inline void
wl_output_release(struct wl_output *wl_output)
{
    wl_proxy_marshal((struct wl_proxy *) wl_output,
             WL_OUTPUT_RELEASE);

    wl_proxy_destroy((struct wl_proxy *) wl_output);
}

release
事件函数:

①     void (*geometry)(void *data,
             struct wl_output *wl_output,
             int32_t x,
             int32_t y,
             int32_t physical_width,
             int32_t physical_height,
             int32_t subpixel,
             const char *make,
             const char *model,
             int32_t transform); 

geometry
②    void (*mode)(void *data,
             struct wl_output *wl_output,
             uint32_t flags,
             int32_t width,
             int32_t height,
             int32_t refresh);

mode
③    void (*done)(void *data,
             struct wl_output *wl_output);

done
④     void (*scale)(void *data,
              struct wl_output *wl_output,
              int32_t factor);

scale
 

以下子窗口系统暂时没怎么用过,不熟悉,暂时不说

--------------------------------------------------------------------------------------------------------------------------------------

wl_subcompositor
子窗口服务提供类。感觉没有这个也不影响。

wl_subsurface
wl_subcompositor接口创建的子窗口。

 

以下是用于进程间拖拽使用的接口类,我没怎么用过,暂时不说。

wl_data_device_manager
拖拽服务接口类

wl_data_source
wl_data_device

--------------------------------------------------------------------------------------------------------------------------------------

以上就是所有wayland.xml里面的接口类了,通过这些接口就可以创建窗口,并完成操作,但是还有一些功能这里还不够,因为目前还有一些协议在weston这个项目里面使用的,还没有纳入wayland标准协议,比如:xdg_shell: 另一种替代wl_shell的窗口协议,zwp_text_input_v1: 输入法管理协议,wl_drm协议(这个是在libdrm这个库里面提供的)等等。(具体的可以查看:https://github.com/wayland-project/wayland-protocols
 

五 Wayland窗口创建流程

 

相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页