walyand学习笔记(三) 建立各个数据结构之间的联系
客户端真正的对象结构体
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的一些接口返回回来的。全部都是,无一例外。读者如果不相信可以自己去找找,并且可以自己尝试创建那些对象,肯定是会报错的,因为那些结构体都是不存在的,创建会编译出错
这所谓的interface对象是啥呢,看看wayland.xml的描述
<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>
一个wl_buffer就是一个Interface对象.
【服务器端的interface对象是在server端创建并返回的,服务器端所有的interface对象全部都是wl_resource结构体对象.】
服务器端真正的对象结构体
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; };
现在,我们开始通过对结构体之间关系的剖析,逐步把各个零散的概念串联起来

可以看到wl_interface就是贯穿这些结构的核心,也正是wayland.xml中大费周章描述的interface对象。
一个wayland协议xml包含一个或者多个interface, 一个interface里面包含一个或者多个request和event
举个栗子:
<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>
这里wl_surface这个interface对象,有一个名叫attach的方法,这个方法的参数如下:
第一个参数名是buffer, 类型是一个wl_buffer的指针,并且可以为空,其注释为 buffer of surface contents
第二个参数是 int x, 描述是surface-local x coordinate
第三个参数是 int y, 描述是surface-local y coordinate
翻译下来,这个函数大概就是:
void wl_surface_attach(struct wl_surface *wl_surface, struct wl_buffer *buffer, int32_t x, int32_t y);
第一个参数可以理解为wl_surface的this指针,指出是哪个对象要调用和这个接口。
好了,这里都是xml层面的描述,到了C的层面,这些内容都由wl_message结构体来描述。
其中以signature(函数签名)相对比较重要, 签名类型简单罗列如下:
`i`: int `u`: uint `f`: fixed `s`: string `o`: object //demarshal 消息的过程中,如果传过来的参数中有 object 或者 new_id,那么我们设置的 implementation 必须要知道对象的类型是什么,wl_message中的types 就是用来做这个的 `n`: new_id //n 表示 new_id,也就是说客户端在发出请求的时候会传递一个 wl_interface * 的参数,服务端在处理的时候对应创建一个新的对象 `a`: array `h`: fd `?`: 问号表示后面接的参数可空
比如上面讲到的那个wl_surface的attach方法:
其对应的wl_message结构体就应该是{ “attach”, "4?oii", [&wl_buffer, NULL, NULL] }
4是version, ?o 代表第一个参数是个object类型,对应的type是wl_buffer, 后面俩参数是int, 对应的type是NULL
后两个NULL的意思是对于后面两个int参数,没有对应的interface对象.
再举个栗子: 以wl_display 的 delete_id为例,它只有一个uint的参数,那么这个结构体就是
{“delete_id”, "u", [NULL]} //NULL意味着uint的参数是一个元对象(primitive), 没有对应的wl_interface
这里有个地方比较让人困惑,wl_message的最后一个参数是一个wl_interface** 的type,
也就是说是一个wl_interface*数组的指针,这是为了可以逐个对应函数签名的参数的type.
这里的写法写成[&wl_buffer, NULL, NULL, NULL], 我实验了一下,这个写法是无法通过编译的。
所以我猜测这里这么写只是一种伪代码,大概是要告诉读者这种对应关系。 我看实际的生成的protocal.c种,也的确没有这种写法,
而是用一个大的wl_interface*数组type,然后每个message有自己的偏移,这种形式是ok的
好的,到这一步就可以比较深入的了解wayland解析协议文件的输出文件了:
一个协议源文件:里面保存了xml协议文件里面所有的interface转换而成的wl_interface结构体变量,包括wl_message结构体记录的request和event函数。
一个客户端使用的头文件:里面封装了wl_proxy转换成指定interface假声明的结构体操作的接口函数,以及request函数的实现,但是这个request只是把请求发送到服务器端,实际调用是在服务器端进行。最后,文件里面还封装了一个回调函数的结构体,成员就是所有的event函数指针,需要客户端去实现,并设置到interface的对象里面,该文件生成了这个设置的接口,实际就是填充到wl_object结构体的implementation变量中。
一个服务器端头文件,里面基本和客户端一样的组成。只是结构体是wl_resource,函数结构体的成员是所有request的函数指针。以及所有的event的实现。
也就是说,客户端需要程序员自己实现事件(event),服务器端需要程序员实现请求(request)。
浙公网安备 33010602011771号