wayland核心协议为wayland/protocol/wayland.xml,其中部分代码如下所示:
The core global object. This is a special singleton object. It
is used for internal Wayland protocol features.
The sync request asks the server to emit the 'done' event
on the returned wl_callback object. Since requests are
handled in-order and events are delivered in-order, this can
be used as a barrier to ensure all previous requests and the
resulting events have been handled.
The object returned by this request will be destroyed by the
compositor after the callback is fired and as such the client must not
attempt to use it after that point.
The callback_data passed in the callback is the event serial.
This request creates a registry object that allows the client
to list and bind the global objects available from the
compositor.
It should be noted that the server side resources consumed in
response to a get_registry request can only be released when the
client disconnects, not when the client side proxy is destroyed.
Therefore, clients should invoke get_registry as infrequently as
possible to avoid wasting memory.
The error event is sent out when a fatal (non-recoverable)
error has occurred. The object_id argument is the object
where the error occurred, most often in response to a request
to that object. The code identifies the error and is defined
by the object interface. As such, each interface defines its
own set of error codes. The message is a brief description
of the error, for (debugging) convenience.
These errors are global and can be emitted in response to any
server request.
This event is used internally by the object ID management
logic. When a client deletes an object, the server will send
this event to acknowledge that it has seen the delete request.
When the client receives this event, it will know that it can
safely reuse the object ID.
通过wayland自带工具wayland-scanner可以生成wayland-client-protocol.h,wayland-protocol.c,wayland-server-protocol.h三个文件,该工具的具体实现为wayland/src/scanner.c,下面对其实现进行具体分析。
代码中定义了以下几个主要类型:
struct protocol {
char *name;
char *uppercase_name;
struct wl_list interface_list;
int type_index;
int null_run_length;
char *copyright;
struct description *description;
bool core_headers;
};
struct interface {
struct location loc;
char *name;
char *uppercase_name;
int version;
int since;
struct wl_list request_list;
struct wl_list event_list;
struct wl_list enumeration_list;
struct wl_list link;
struct description *description;
};
struct message {
struct location loc;
char *name;
char *uppercase_name;
struct wl_list arg_list;
struct wl_list link;
int arg_count;
int new_id_count;
int type_index;
int all_null;
int destructor;
int since;
struct description *description;
};
enum arg_type {
NEW_ID,
INT,
UNSIGNED,
FIXED,
STRING,
OBJECT,
ARRAY,
FD
};
struct parse_context {
struct location loc;
XML_Parser parser;
struct protocol *protocol;
struct interface *interface;
struct message *message;
struct enumeration *enumeration;
struct description *description;
char character_data[8192];
unsigned int character_data_length;
};
包括协议,接口,消息,参数类型,解析上下文,着重看下接口的定义,其中包含三个wl_list,分别为xml文件中定义的request,event,enum。request为wayland中client端向server端发送的请求,event为wayland中server端向client端发送的事件,event和request都被定义为一种message,enum为协议中定义的enum值。
生成两个头文件的函数为emit_header,其中
printf("/* Generated by %s %s */\n\n", PROGRAM_NAME, WAYLAND_VERSION);
printf("#ifndef %s_%s_PROTOCOL_H\n"
"#define %s_%s_PROTOCOL_H\n"
"\n"
"#include \n"
"#include \n"
"#include \"%s\"\n\n"
"#ifdef __cplusplus\n"
"extern \"C\" {\n"
"#endif\n\n",
protocol->uppercase_name, s,
protocol->uppercase_name, s,
get_include_name(protocol->core_headers, side));
if (side == SERVER)
printf("struct wl_client;\n"
"struct wl_resource;\n\n");
emit_mainpage_blurb(protocol, side);
生成头文件前缀相关代码,以wayland-server-protocol.h为例,生成代码如下:
/* Generated by wayland-scanner 1.14.90 */
#ifndef WAYLAND_SERVER_PROTOCOL_H
#define WAYLAND_SERVER_PROTOCOL_H
#include
#include
#include "wayland-server.h"
#ifdef __cplusplus
extern "C" {
#endif
struct wl_client;
struct wl_resource;
/**
* @page page_wayland The wayland protocol
* @section page_ifaces_wayland Interfaces
* - @subpage page_iface_wl_display - core global object
* - @subpage page_iface_wl_registry - global registry object
* - @subpage page_iface_wl_callback - callback object
* - @subpage page_iface_wl_compositor - the compositor singleton
* - @subpage page_iface_wl_shm_pool - a shared memory pool
* - @subpage page_iface_wl_shm - shared memory support
* - @subpage page_iface_wl_buffer - content for a wl_surface
* - @subpage page_iface_wl_data_offer - offer to transfer data
* - @subpage page_iface_wl_data_source - offer to transfer data
* - @subpage page_iface_wl_data_device - data transfer device
* - @subpage page_iface_wl_data_device_manager - data transfer interface
* - @subpage page_iface_wl_shell - create desktop-style surfaces
* - @subpage page_iface_wl_shell_surface - desktop-style metadata interface
* - @subpage page_iface_wl_surface - an onscreen surface
* - @subpage page_iface_wl_seat - group of input devices
* - @subpage page_iface_wl_pointer - pointer input device
* - @subpage page_iface_wl_keyboard - keyboard input device
* - @subpage page_iface_wl_touch - touchscreen input device
* - @subpage page_iface_wl_output - compositor output region
* - @subpage page_iface_wl_region - region interface
* - @subpage page_iface_wl_subcompositor - sub-surface compositing
* - @subpage page_iface_wl_subsurface - sub-surface interface to a wl_surface
* @section page_copyright_wayland Copyright
*
*
* Copyright © 2008-2011 Kristian Høgsberg
* Copyright © 2010-2011 Intel Corporation
* Copyright © 2012-2013 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
随后将xml文件中定义的接口名称保存在动态数组wl_array(该动态数组的具体实现会在后文具体分析) types中,实现代码如下所示:
wl_array_init(&types);
wl_list_for_each(i, &protocol->interface_list, link) {
emit_types_forward_declarations(protocol, &i->request_list, &types);
emit_types_forward_declarations(protocol, &i->event_list, &types);
}
wl_list_for_each(i, &protocol->interface_list, link) {
p = fail_on_null(wl_array_add(&types, sizeof *p));
*p = i->name;
}
对该数组按照名称进行排列
qsort(types.data, types.size / sizeof *p, sizeof *p, cmp_names);
在头文件中定义各接口的结构体
prev = NULL;
wl_array_for_each(p, &types) {
if (prev && strcmp(*p, prev) == 0)
continue;
printf("struct %s;\n", *p);
prev = *p;
}
wl_array_release(&types);
printf("\n");
生成以下代码
struct wl_buffer;
struct wl_callback;
struct wl_compositor;
struct wl_data_device;
struct wl_data_device_manager;
struct wl_data_offer;
struct wl_data_source;
struct wl_display;
struct wl_keyboard;
struct wl_output;
struct wl_pointer;
struct wl_region;
struct wl_registry;
struct wl_seat;
struct wl_shell;
struct wl_shell_surface;
struct wl_shm;
struct wl_shm_pool;
struct wl_subcompositor;
struct wl_subsurface;
struct wl_surface;
struct wl_touch;
对于client端和server端,生成代码区别在于
if (side == SERVER) {
emit_structs(&i->request_list, i, side);
emit_opcodes(&i->event_list, i);
emit_opcode_versions(&i->event_list, i);
emit_opcode_versions(&i->request_list, i);
emit_event_wrappers(&i->event_list, i);
} else {
emit_structs(&i->event_list, i, side);
emit_opcodes(&i->request_list, i);
emit_opcode_versions(&i->event_list, i);
emit_opcode_versions(&i->request_list, i);
emit_stubs(&i->request_list, i);
}
函数emit_structs实现如下:
static void
emit_structs(struct wl_list *message_list, struct interface *interface, enum side side)
{
struct message *m;
struct arg *a;
int n;
if (wl_list_empty(message_list))
return;
printf("/**\n");
printf(" * @ingroup iface_%s\n", interface->name);
printf(" * @struct %s_%s\n", interface->name,
(side == SERVER) ? "interface" : "listener");
printf(" */\n");
printf("struct %s_%s {\n", interface->name,
(side == SERVER) ? "interface" : "listener");
wl_list_for_each(m, message_list, link) {
struct description *mdesc = m->description;
printf("\t/**\n");
if (mdesc) {
if (mdesc->summary)
printf("\t * %s\n", mdesc->summary);
printf("\t *\n");
desc_dump(mdesc->text, "\t * ");
}
wl_list_for_each(a, &m->arg_list, link) {
if (side == SERVER && a->type == NEW_ID &&
a->interface_name == NULL)
printf("\t * @param interface name of the objects interface\n"
"\t * @param version version of the objects interface\n");
if (a->summary)
printf("\t * @param %s %s\n", a->name,
a->summary);
}
if (m->since > 1) {
printf("\t * @since %d\n", m->since);
}
printf("\t */\n");
printf("\tvoid (*%s)(", m->name);
n = strlen(m->name) + 17;
if (side == SERVER) {
printf("struct wl_client *client,\n"
"%sstruct wl_resource *resource",
indent(n));
} else {
printf("void *data,\n"),
printf("%sstruct %s *%s",
indent(n), interface->name, interface->name);
}
wl_list_for_each(a, &m->arg_list, link) {
printf(",\n%s", indent(n));
if (side == SERVER && a->type == OBJECT)
printf("struct wl_resource *");
else if (side == SERVER && a->type == NEW_ID && a->interface_name == NULL)
printf("const char *interface, uint32_t version, uint32_t ");
else if (side == CLIENT && a->type == OBJECT && a->interface_name == NULL)
printf("void *");
else if (side == CLIENT && a->type == NEW_ID)
printf("struct %s *", a->interface_name);
else
emit_type(a);
printf("%s", a->name);
}
printf(");\n");
}
printf("};\n\n");
if (side == CLIENT) {
printf("/**\n"
" * @ingroup iface_%s\n"
" */\n", interface->name);
printf("static inline int\n"
"%s_add_listener(struct %s *%s,\n"
"%sconst struct %s_listener *listener, void *data)\n"
"{\n"
"\treturn wl_proxy_add_listener((struct wl_proxy *) %s,\n"
"%s(void (**)(void)) listener, data);\n"
"}\n\n",
interface->name, interface->name, interface->name,
indent(14 + strlen(interface->name)),
interface->name,
interface->name,
indent(37));
}
}
可以看到在client端生成的结构体为interface_name_listener,在server端生成的结构体为interface_name_interface,实现代码如下所示:
printf("struct %s_%s {\n", interface->name,
(side == SERVER) ? "interface" : "listener");
生成结果client端为
struct wl_display_listener
server端为
struct wl_display_interface
其中client端listener中定义对event响应的处理函数,而server端interface定义对request请求的处理函数。定义函数名称代码如下:
printf("\t */\n");
printf("\tvoid (*%s)(", m->name);
n = strlen(m->name) + 17;
if (side == SERVER) {
printf("struct wl_client *client,\n"
"%sstruct wl_resource *resource",
indent(n));
} else {
printf("void *data,\n"),
printf("%sstruct %s *%s",
indent(n), interface->name, interface->name);
}
若在server端,前两个参数为struct wl_client *client,struct wl_resource *resurce,若在client端,前两个参数为void* data,struct interface_name *interface_name。接下来的参数为xml文件中自己对该接口定义得到,以request请求get_register为例,参数如下所示:
该参数名称为register,类型为new_id,通过以下代码进行生成:
wl_list_for_each(a, &m->arg_list, link) {
printf(",\n%s", indent(n));
if (side == SERVER && a->type == OBJECT)
printf("struct wl_resource *");
else if (side == SERVER && a->type == NEW_ID && a->interface_name == NULL)
printf("const char *interface, uint32_t version, uint32_t ");
else if (side == CLIENT && a->type == OBJECT && a->interface_name == NULL)
printf("void *");
else if (side == CLIENT && a->type == NEW_ID)
printf("struct %s *", a->interface_name);
else
emit_type(a);
printf("%s", a->name);
}
进入emit_type分支,代码实现如下:
static void
emit_type(struct arg *a)
{
switch (a->type) {
default:
case INT:
case FD:
printf("int32_t ");
break;
case NEW_ID:
case UNSIGNED:
printf("uint32_t ");
break;
case FIXED:
printf("wl_fixed_t ");
break;
case STRING:
printf("const char *");
break;
case OBJECT:
printf("struct %s *", a->interface_name);
break;
case ARRAY:
printf("struct wl_array *");
break;
}
}
生成参数类型uint32_t,最终生成的代码为
struct wl_display_interface {
/**
* asynchronous roundtrip
*
* The sync request asks the server to emit the 'done' event on
* the returned wl_callback object. Since requests are handled
* in-order and events are delivered in-order, this can be used as
* a barrier to ensure all previous requests and the resulting
* events have been handled.
*
* The object returned by this request will be destroyed by the
* compositor after the callback is fired and as such the client
* must not attempt to use it after that point.
*
* The callback_data passed in the callback is the event serial.
* @param callback callback object for the sync request
*/
void (*sync)(struct wl_client *client,
struct wl_resource *resource,
uint32_t callback);
/**
* get global registry object
*
* This request creates a registry object that allows the client
* to list and bind the global objects available from the
* compositor.
*
* It should be noted that the server side resources consumed in
* response to a get_registry request can only be released when the
* client disconnects, not when the client side proxy is destroyed.
* Therefore, clients should invoke get_registry as infrequently as
* possible to avoid wasting memory.
* @param registry global registry object
*/
void (*get_registry)(struct wl_client *client,
struct wl_resource *resource,
uint32_t registry);
};
对于client端还需要生成相关proxy的注册listener操作,该操作其实是对wl_proxy_add_listener的封装,代码如下:
/**
* @ingroup iface_wl_display
*/
static inline int
wl_display_add_listener(struct wl_display *wl_display,
const struct wl_display_listener *listener, void *data)
{
return wl_proxy_add_listener((struct wl_proxy *) wl_display,
(void (**)(void)) listener, data);
}
接下来对相关的request和event的op code进行宏定义,代码如下:
#define WL_DISPLAY_ERROR 0
#define WL_DISPLAY_DELETE_ID 1
#define WL_DISPLAY_SYNC 0
#define WL_DISPLAY_GET_REGISTRY 1
对于server端,除了在interface_name为wl_display时,其他接口需要wrapper event事件,以wl_registry为例,该接口中包含两个event,代码如下:
生成代码函数为
emit_event_wrappers(&i->event_list, i);
生成出来的函数其实为wl_resource_post_event的封装,代码如下:
static inline void
wl_registry_send_global(struct wl_resource *resource_, uint32_t name, const char *interface, uint32_t version)
{
wl_resource_post_event(resource_, WL_REGISTRY_GLOBAL, name, interface, version);
}
static inline void
wl_registry_send_global_remove(struct wl_resource *resource_, uint32_t name)
{
wl_resource_post_event(resource_, WL_REGISTRY_GLOBAL_REMOVE, name);
}
而对于client端,需要生成该接口的stub代码:
emit_stubs(&i->request_list, i);
对于每个request得到的代码为:
static inline void
wl_display_set_user_data(struct wl_display *wl_display, void *user_data)
{
wl_proxy_set_user_data((struct wl_proxy *) wl_display, user_data);
}
/** @ingroup iface_wl_display */
static inline void *
wl_display_get_user_data(struct wl_display *wl_display)
{
return wl_proxy_get_user_data((struct wl_proxy *) wl_display);
}
static inline uint32_t
wl_display_get_version(struct wl_display *wl_display)
{
return wl_proxy_get_version((struct wl_proxy *) wl_display);
}
而对于request参数不同,向server端发送request的函数也不相同,代码如下:
if (ret && ret->interface_name == NULL) {
/* an arg has type ="new_id" but interface is not
* provided, such as in wl_registry.bind */
printf("\tstruct wl_proxy *%s;\n\n"
"\t%s = wl_proxy_marshal_constructor_versioned("
"(struct wl_proxy *) %s,\n"
"\t\t\t %s_%s, interface, version",
ret->name, ret->name,
interface->name,
interface->uppercase_name,
m->uppercase_name);
} else if (ret) {
/* Normal factory case, an arg has type="new_id" and
* an interface is provided */
printf("\tstruct wl_proxy *%s;\n\n"
"\t%s = wl_proxy_marshal_constructor("
"(struct wl_proxy *) %s,\n"
"\t\t\t %s_%s, &%s_interface",
ret->name, ret->name,
interface->name,
interface->uppercase_name,
m->uppercase_name,
ret->interface_name);
} else {
/* No args have type="new_id" */
printf("\twl_proxy_marshal((struct wl_proxy *) %s,\n"
"\t\t\t %s_%s",
interface->name,
interface->uppercase_name,
m->uppercase_name);
}
若参数类型中包含new_id,但未提供interface,使用wl_proxy_marshal_constructor_versioned,例如:
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;
}
若参数类型中包含new_id,且提供了interface,使用wl_proxy_marshal_constructor,例如:
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;
}
若参数类型中不包含new_id,使用wl_proxy_marshal,例如:
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);
}
依次对xml文件中各个接口进行生成,即可得client和server端的两个头文件,下面对wayland-protocol.c中代码生成进行分析,生成代码的函数为:
emit_code(&protocol);
该函数中前面与emit_header类似,生成相关前缀代码,随后该函数生成一个wl_interface类型数组,代码如下:
printf("static const struct wl_interface *types[] = {\n");
emit_null_run(protocol);
wl_list_for_each(i, &protocol->interface_list, link) {
emit_types(protocol, &i->request_list);
emit_types(protocol, &i->event_list);
}
printf("};\n\n");
其中emit_types实现如下所示:
static void
emit_types(struct protocol *protocol, struct wl_list *message_list)
{
struct message *m;
struct arg *a;
wl_list_for_each(m, message_list, link) {
if (m->all_null) {
m->type_index = 0;
continue;
}
m->type_index =
protocol->null_run_length + protocol->type_index;
protocol->type_index += m->arg_count;
wl_list_for_each(a, &m->arg_list, link) {
switch (a->type) {
case NEW_ID:
case OBJECT:
if (a->interface_name)
printf("\t&%s_interface,\n",
a->interface_name);
else
printf("\tNULL,\n");
break;
default:
printf("\tNULL,\n");
break;
}
}
}
}
request或者event中的参数类型为NEW_ID或者OBJECT时,数组在该处的元素为参数的interface_name,否则为NULL,生成结果如下所示:
static const struct wl_interface *types[] = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
&wl_callback_interface,
&wl_registry_interface,
&wl_surface_interface,
&wl_region_interface,
&wl_buffer_interface,
NULL,
NULL,
NULL,
NULL,
NULL,
&wl_shm_pool_interface,
NULL,
NULL,
&wl_data_source_interface,
&wl_surface_interface,
&wl_surface_interface,
NULL,
&wl_data_source_interface,
NULL,
&wl_data_offer_interface,
NULL,
&wl_surface_interface,
NULL,
NULL,
&wl_data_offer_interface,
&wl_data_offer_interface,
&wl_data_source_interface,
&wl_data_device_interface,
&wl_seat_interface,
&wl_shell_surface_interface,
&wl_surface_interface,
&wl_seat_interface,
NULL,
&wl_seat_interface,
NULL,
NULL,
&wl_surface_interface,
NULL,
NULL,
NULL,
NULL,
NULL,
&wl_output_interface,
&wl_seat_interface,
NULL,
&wl_surface_interface,
NULL,
NULL,
NULL,
&wl_output_interface,
&wl_buffer_interface,
NULL,
NULL,
&wl_callback_interface,
&wl_region_interface,
&wl_region_interface,
&wl_output_interface,
&wl_output_interface,
&wl_pointer_interface,
&wl_keyboard_interface,
&wl_touch_interface,
NULL,
&wl_surface_interface,
NULL,
NULL,
NULL,
&wl_surface_interface,
NULL,
NULL,
NULL,
&wl_surface_interface,
NULL,
&wl_surface_interface,
NULL,
NULL,
&wl_surface_interface,
NULL,
NULL,
&wl_surface_interface,
NULL,
NULL,
NULL,
&wl_subsurface_interface,
&wl_surface_interface,
&wl_surface_interface,
&wl_surface_interface,
&wl_surface_interface,
};
static const struct wl_message wl_display_requests[] = {
{ "sync", "n", types + 8 },
{ "get_registry", "n", types + 9 },
};
static const struct wl_message wl_display_events[] = {
{ "error", "ous", types + 0 },
{ "delete_id", "u", types + 0 },
};
WL_EXPORT const struct wl_interface wl_display_interface = {
"wl_display", 1,
2, wl_display_requests,
2, wl_display_events,
};
随后将wl_interface中的request和event封装为wl_message类型,wl_message类型定义在文件wayland-util中,代码如下:
struct wl_message {
const char *name;
const char *signature;
const struct wl_interface **types;
};
可以看出该结构体定义了消息的名称,签名和参数为对象时,该对象参数的接口,生成得到代码如下:
static const struct wl_message wl_display_requests[] = {
{ "sync", "n", types + 8 },
{ "get_registry", "n", types + 9 },
};
static const struct wl_message wl_display_events[] = {
{ "error", "ous", types + 0 },
{ "delete_id", "u", types + 0 },
};
最后生成该interface的定义:
WL_EXPORT const struct wl_interface wl_display_interface = {
"wl_display", 1,
2, wl_display_requests,
2, wl_display_events,
};
WL_EXPORT const struct wl_interface wl_display_interface = {
"wl_display", 1,
2, wl_display_requests,
2, wl_display_events,
};
其中wl_interface的定义在wayland_util.c中,定义如下:
struct wl_interface {
const char *name;
int version;
int method_count;
const struct wl_message *methods;
int event_count;
const struct wl_message *events;
};
wl_interface结构体中定义了该接口的名称,版本号,request数目,requeset方法,event数目,event方法。
以上。