Wayland 首先是一种协议(Compositor 与客户端之间进行通信的协议),然后才是对协议的实现。所以第一步是协议的定义。

 

1. Wayland 协议的定义

Wayland 协议使用 XML 文件来定义。其核心协议文件为:

wayland/protocol/wayland.xml

 

下面的代码片段节选自 wayland.xml,从中我们可以看到 Wayland 协议定义的基本结构:

 

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

    <description summary="core global object">

      The core global object.  This is a special singleton object.  It

      is used for internal wayland protocol features.

    </description>

    <request name="bind">

      <description summary="bind an object to the display">

Binds a new, client-created object to the server using @name as

the identifier.

      </description>

      <arg name="name" type="uint" summary="unique number id for object"/>

      <arg name="interface" type="string"/>

      <arg name="version" type="uint"/>

      <arg name="id" type="new_id" interface="wl_object"/>

    </request>

 

    <request name="sync">

      <description summary="asynchronous roundtrip">

The sync request asks the server to invoke the 'done' request

on the provided wl_callback object.  Since requests are

handled in-order, this can be used as a barrier to ensure all

previous requests have been handled.

      </description>

      <arg name="callback" type="new_id" interface="wl_callback"/>

    </request>

 

    <event name="error">

      <description summary="fatal error event">

The error event is sent out when a fatal (non-recoverable)

error has occurred.

      </description>

      <arg name="object_id" type="object" interface="wl_object"/>

      <arg name="code" type="uint"/>

      <arg name="message" type="string"/>

    </event>

 

    <enum name="error">

      <description summary="global error values">

These errors are global and can be emitted in response to any

server request.

      </description>

      <entry name="invalid_object" value="0"

    summary="server couldn't find object"/>

      <entry name="invalid_method" value="1"

    summary="method doesn't exist on the specified interface"/>

      <entry name="no_memory" value="2"

    summary="server is out of memory"/>

    </enum>

 

    <event name="global">

      <description summary="announce global object">

Notify the client of global objects.  These are objects that

are created by the server.  Globals are published on the

initial client connection sequence, upon device hotplugs,

device disconnects, reconfiguration or other events.  A client

can 'bind' to a global object by using the bind request.  This

creates a client side handle that lets the object emit events

to the client and lets the client invoke requests on the

object.

      </description>

      <arg name="name" type="uint"/>

      <arg name="interface" type="string"/>

      <arg name="version" type="uint"/>

    </event>

 

    <event name="global_remove">

      <description summary="announce removal of global object">

Notify the client of removed global objects.

      </description>

      <arg name="name" type="uint"/>

    </event>

 

    <event name="delete_id">

      <description summary="acknowledge object id deletion">

Server has deleted the id and client can now reuse it.

      </description>

      <arg name="id" type="uint" />

    </event>

</interface>

 

这里对上面这段代码稍微做一下解释:定义了一个接口,接口名叫做 display,该接口中又定义了一些 request、event 和 enum,其中 request 与 event 又带有参数。

 

2. Wayland 协议的解析

XML 文件只是协议的文本定义,只有解析该协议后,才能真正实现该协议。

 

在 Wayland 中使用 wayland-scanner 工具来解析协议,并生成相应的源码,供服务器(compositor)和客户端使用,为真正实现该协议提供基础,其用途是:

1) 生成供客户端使用的桩代码(stub code)

2) 生成用于通信打包的数据结构

3) 待实现的接口定义,在客户端是事件(event),在Compositor 端是请求(request)

 

wayland-scanner 工具包含在 Wayland 中。实际上,在编译 wayland 时,第一步编译的程序就是 wayland-scanner,然后编译程序(makefile)会使用 wayland-scanner 生成相应的源文件:

wayland/src/wayland-server-protocol.h                       

wayland/src/wayland-client-protocol.h                            

wayland/src/wayland-protocol.c

 

wayland-scanner 对应的源码是:

wayland/src/scanner.c

 

分析 wayland-scanner 的实现是透彻理解 Wayland 协议的基础,也是理解connection模块中实现进程间通信功能的基础。下面就对其代码文件作一个简要的分析。

 

主要的数据结构

struct protocol {

char *name;

char *uppercase_name;

struct wl_list interface_list;

int type_index;

int null_run_length;

char *copyright;

struct description *description;

};

 

struct interface {

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 {

char *name;

char *uppercase_name;

struct wl_list arg_list;

struct wl_list link;

int arg_count;

int type_index;

int all_null;

int destructor;

int since;

struct description *description;

};

 

struct arg {

char *name;

enum arg_type type;

int nullable;

char *interface_name;

struct wl_list link;

char *summary;

};

 

struct enumeration {

char *name;

char *uppercase_name;

struct wl_list entry_list;

struct wl_list link;

struct description *description;

};

 

struct entry {

char *name;

char *uppercase_name;

char *value;

char *summary;

struct wl_list link;

};

从以上数据结构可知,协议(protocol)包含一系列接口(interface),接口包含请求、接口、枚举(enumeration),请求与接口又合并为一个接口:message,其内包含各自的参数列表;而枚举包含 entry 列表(常量)。

 

main 函数

1 int main(int argc, char *argv[])

2 {

3 struct parse_context ctx;

4 struct protocol protocol;

5 int len;

6 void *buf;

7

8 if (argc != 2)

9 usage(EXIT_FAILURE);

10

11 wl_list_init(&protocol.interface_list);

12 protocol.type_index = 0;

13 protocol.null_run_length = 0;

14 protocol.copyright = NULL;

15 ctx.protocol = &protocol;

16

17 ctx.filename = "<stdin>";

18 ctx.parser = XML_ParserCreate(NULL);

19 XML_SetUserData(ctx.parser, &ctx);

20 if (ctx.parser == NULL) {

21 fprintf(stderr, "failed to create parser\n");

22 exit(EXIT_FAILURE);

23 }

24

25 XML_SetElementHandler(ctx.parser, start_element, end_element);

26 XML_SetCharacterDataHandler(ctx.parser, character_data);

27

28 do {

29 buf = XML_GetBuffer(ctx.parser, XML_BUFFER_SIZE);

30 len = fread(buf, 1, XML_BUFFER_SIZE, stdin);

31 if (len < 0) {

32 fprintf(stderr, "fread: %m\n");

33 exit(EXIT_FAILURE);

34 }

35 XML_ParseBuffer(ctx.parser, len, len == 0);

36

37 } while (len > 0);

 

38 XML_ParserFree(ctx.parser);

39

40 if (strcmp(argv[1], "client-header") == 0) {

41 emit_header(&protocol, 0);

42 } else if (strcmp(argv[1], "server-header") == 0) {

43 emit_header(&protocol, 1);

44 } else if (strcmp(argv[1], "code") == 0) {

45 emit_code(&protocol);

46 }

47

48 return 0;

49 }

 

3-38行:main 函数的前面部分主要是解析 XML 文件,并读入内存中相应的数据结构中。

41行:生成客户端头文件,即wayland-client-protocol.h

43行:生成 Compositor 端头文件,即 wayland-server-protocol.h

45行:生成源码文件,即wayland-protocol.c

 

emit_header 函数:生成头文件

1 static void

2 emit_header(struct protocol *protocol, int server)

3 {

4 struct interface *i;

5 const char *s = server ? "SERVER" : "CLIENT";

6

7 if (protocol->copyright)

8 format_copyright(protocol->copyright);

9

10 printf("#ifndef %s_%s_PROTOCOL_H\n"

11       "#define %s_%s_PROTOCOL_H\n"

12         "\n"

13       "#ifdef  __cplusplus\n"

14       "extern \"C\" {\n"

15       "#endif\n"

16       "\n"

17       "#include <stdint.h>\n"

18       "#include <stddef.h>\n"

19       "#include \"%s\"\n\n"

20       "struct wl_client;\n"

21       "struct wl_resource;\n\n",

22       protocol->uppercase_name, s,

23       protocol->uppercase_name, s,

24       server ? "wayland-util.h" : "wayland-client.h");

25

26 wl_list_for_each(i, &protocol->interface_list, link)

27 printf("struct %s;\n", i->name);

28 printf("\n");

29

30 wl_list_for_each(i, &protocol->interface_list, link) {

31 printf("extern const struct wl_interface "

32       "%s_interface;\n",

33       i->name);

34 }

35 printf("\n");

36

37 wl_list_for_each(i, &protocol->interface_list, link) {

38

39 emit_enumerations(i);

40

41 if (server) {

42 emit_structs(&i->request_list, i);

43 emit_opcodes(&i->event_list, i);

44 emit_event_wrappers(&i->event_list, i);

45 } else {

46 emit_structs(&i->event_list, i);

47 emit_opcodes(&i->request_list, i);

48 emit_stubs(&i->request_list, i);

49 }

50 }

51

52 printf("#ifdef  __cplusplus\n"

53       "}\n"

54       "#endif\n"

55       "\n"

56       "#endif\n");

57 }

参数  server 为 1 时,生成服务器端头文件:wayland-server-protocol.h;

 server 为 0 时,生成客户端头文件:wayland-client-protocol.h。

 

4-24行:生成要包含的其它头文件,以及前置声明

26-28行:生成接口声明

如:struct wl_display;

30-35行:生成 wl_interface 接口变量声明

如: extern const struct wl_interface wl_display_interface;

39行:生成枚举值

如:

#ifndef WL_DISPLAY_ERROR_ENUM

#define WL_DISPLAY_ERROR_ENUM

/**

 * wl_display_error - global error values

 * @WL_DISPLAY_ERROR_INVALID_OBJECT: server couldn't find object

 * @WL_DISPLAY_ERROR_INVALID_METHOD: method doesn't exist on the

 * specified interface

 * @WL_DISPLAY_ERROR_NO_MEMORY: server is out of memory

 *

 * These errors are global and can be emitted in response to any server

 * request.

 */

enum wl_display_error {

WL_DISPLAY_ERROR_INVALID_OBJECT = 0,

WL_DISPLAY_ERROR_INVALID_METHOD = 1,

WL_DISPLAY_ERROR_NO_MEMORY = 2,

};

#endif /* WL_DISPLAY_ERROR_ENUM */

42-44行:生成 wayland-server-protocol.h 的其余部分

42行:emit_structs(&i->request_list, i);   

  生成用于 request 的结构定义

 

emit_structs 的定义如下:

static void

emit_structs(struct wl_list *message_list, struct interface *interface)

{

struct message *m;

struct arg *a;

int is_interface, n;

 

if (wl_list_empty(message_list))

return;

 

is_interface = message_list == &interface->request_list;

if (interface->description) {

struct description *desc = interface->description;

printf("/**\n");

desc_dump(" * %s - ", interface->name, desc->summary);

wl_list_for_each(m, message_list, link) {

struct description *mdesc = m->description;

desc_dump(" * @%s: ",

 m->name, mdesc ? mdesc->summary : "(none)");

}

printf(" *\n");

desc_dump(" * ", desc->text);

printf(" */\n");

}

printf("struct %s_%s {\n", interface->name,

      is_interface ? "interface" : "listener");

 

wl_list_for_each(m, message_list, link) {

struct description *mdesc = m->description;

 

printf("\t/**\n");

desc_dump("\t * %s - ",

 m->name, mdesc ? mdesc->summary : "(none)");

wl_list_for_each(a, &m->arg_list, link) {

desc_dump("\t * @%s: ",

 a->name, a->summary ? a->summary : "(none)");

}

if (mdesc) {

printf("\t *\n");

desc_dump("\t * ", mdesc->text, 8, 0);

}

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 (is_interface) {

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 (is_interface && a->type == OBJECT)

printf("struct wl_resource *");

else if (!is_interface && a->type == NEW_ID)

printf("struct %s *", a->interface_name);

else

emit_type(a);

 

printf("%s", a->name);

}

 

printf(");\n");

}

 

printf("};\n\n");

 

if (!is_interface) {

   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));

}

}

 

其生成的结构定义例子:

/**

 * wl_display - core global object

 * @bind: bind an object to the display

 * @sync: asynchronous roundtrip

 *

 * The core global object. This is a special singleton object. It is used

 * for internal wayland protocol features.

 */

struct wl_display_interface {

/**

* bind - bind an object to the display

* @name: unique number id for object

* @interface: (none)

* @version: (none)

* @id: (none)

*

* Binds a new, client-created object to the server using @name

* as the identifier.

*/

void (*bind)(struct wl_client *client,

    struct wl_resource *resource,

    uint32_t name,

    const char *interface,

    uint32_t version,

    uint32_t id);

/**

* sync - asynchronous roundtrip

* @callback: (none)

*

* The sync request asks the server to invoke the 'done' request

* on the provided wl_callback object. Since requests are handled

* in-order, this can be used as a barrier to ensure all previous

* requests have been handled.

*/

void (*sync)(struct wl_client *client,

    struct wl_resource *resource,

    uint32_t callback);

};

 

43行:生成事件的操作码(用来识别事件)

emit_opcodes 的定义如下:

static void

emit_opcodes(struct wl_list *message_list, struct interface *interface)

{

struct message *m;

int opcode;

 

if (wl_list_empty(message_list))

return;

 

opcode = 0;

wl_list_for_each(m, message_list, link)

printf("#define %s_%s\t%d\n",

      interface->uppercase_name, m->uppercase_name, opcode++);

 

printf("\n");

}

 

其生成的代码例子:

#define WL_DISPLAY_ERROR 0

#define WL_DISPLAY_GLOBAL 1

#define WL_DISPLAY_GLOBAL_REMOVE 2

#define WL_DISPLAY_DELETE_ID 3

 

44行:生成事件封装函数,用于通知事件

emit_event_wrappers 函数的定义:

static void

emit_event_wrappers(struct wl_list *message_list, struct interface *interface)

{

struct message *m;

struct arg *a;

 

/* We provide hand written functions for the display object */

if (strcmp(interface->name, "wl_display") == 0)

return;

 

wl_list_for_each(m, message_list, link) {

printf("static inline void\n"

      "%s_send_%s(struct wl_resource *resource_",

      interface->name, m->name);

 

wl_list_for_each(a, &m->arg_list, link) {

printf(", ");

switch (a->type) {

case NEW_ID:

case OBJECT:

printf("struct wl_resource *");

break;

default:

emit_type(a);

}

printf("%s", a->name);

}

 

printf(")\n"

      "{\n"

      "\twl_resource_post_event(resource_, %s_%s",

      interface->uppercase_name, m->uppercase_name);

 

wl_list_for_each(a, &m->arg_list, link)

printf(", %s", a->name);

 

printf(");\n");

printf("}\n\n");

}

}

其生成代码的例子:

static inline void

wl_callback_send_done(struct wl_resource *resource_, uint32_t serial)

{

wl_resource_post_event(resource_, WL_CALLBACK_DONE, serial);

}

注意,emit_event_wrappers 函数不会生成  wl_display 的事件封装函数,对于 wl_display,会有特殊的处理,在Wayland通信机制中会详细分析。

 

46-48行:生成wayland-client-protocol.h的其余部分

46行:生成用于事件处理的结构定义,与42行的功能相同,只是供客户端使用;不同于服务器端的是,会生成 add_listener 函数,用于添加事件处理函数

 

47行:生成请求的操作码(用来识别请求),与43行的功能相同,只是供客户端使用

48行:生成客户端的桩代码,用来向服务器端发出请求

 

emit_stubs 函数的定义:

static void

emit_stubs(struct wl_list *message_list, struct interface *interface)

{

struct message *m;

struct arg *a, *ret;

int has_destructor, has_destroy;

 

/* We provide a hand written functions for the display object */

if (strcmp(interface->name, "wl_display") == 0)

return;

 

printf("static inline void\n"

      "%s_set_user_data(struct %s *%s, void *user_data)\n"

      "{\n"

      "\twl_proxy_set_user_data((struct wl_proxy *) %s, user_data);\n"

      "}\n\n",

      interface->name, interface->name, interface->name,

      interface->name);

 

printf("static inline void *\n"

      "%s_get_user_data(struct %s *%s)\n"

      "{\n"

      "\treturn wl_proxy_get_user_data((struct wl_proxy *) %s);\n"

      "}\n\n",

      interface->name, interface->name, interface->name,

      interface->name);

 

has_destructor = 0;

has_destroy = 0;

wl_list_for_each(m, message_list, link) {

if (m->destructor)

has_destructor = 1;

if (strcmp(m->name, "destroy)") == 0)

has_destroy = 1;

}

 

if (!has_destructor && has_destroy) {

fprintf(stderr,

"interface %s has method named destroy but"

"no destructor", interface->name);

exit(EXIT_FAILURE);

}

 

if (!has_destructor)

printf("static inline void\n"

      "%s_destroy(struct %s *%s)\n"

      "{\n"

      "\twl_proxy_destroy("

      "(struct wl_proxy *) %s);\n"

      "}\n\n",

      interface->name, interface->name, interface->name,

      interface->name);

 

if (wl_list_empty(message_list))

return;

 

wl_list_for_each(m, message_list, link) {

ret = NULL;

wl_list_for_each(a, &m->arg_list, link) {

if (a->type == NEW_ID)

ret = a;

}

 

if (ret)

printf("static inline struct %s *\n",

      ret->interface_name);

else

printf("static inline void\n");

 

printf("%s_%s(struct %s *%s",

      interface->name, m->name,

      interface->name, interface->name);

 

wl_list_for_each(a, &m->arg_list, link) {

if (a->type == NEW_ID)

continue;

printf(", ");

emit_type(a);

printf("%s", a->name);

}

 

printf(")\n"

      "{\n");

if (ret)

printf("\tstruct wl_proxy *%s;\n\n"

      "\t%s = wl_proxy_create("

      "(struct wl_proxy *) %s,\n"

      "\t\t\t     &%s_interface);\n"

      "\tif (!%s)\n"

      "\t\treturn NULL;\n\n",

      ret->name,

      ret->name,

      interface->name, ret->interface_name,

      ret->name);

 

printf("\twl_proxy_marshal((struct wl_proxy *) %s,\n"

      "\t\t\t %s_%s",

      interface->name,

      interface->uppercase_name,

      m->uppercase_name);

 

wl_list_for_each(a, &m->arg_list, link) {

printf(", ");

printf("%s", a->name);

}

printf(");\n");

 

if (m->destructor)

printf("\n\twl_proxy_destroy("

      "(struct wl_proxy *) %s);\n",

      interface->name);

 

if (ret)

printf("\n\treturn (struct %s *) %s;\n",

      ret->interface_name, ret->name);

 

printf("}\n\n");

}

}

 

其生成的桩代码例子:

static inline struct wl_surface *

wl_compositor_create_surface(struct wl_compositor *wl_compositor)

{

struct wl_proxy *id;

 

id = wl_proxy_create((struct wl_proxy *) wl_compositor,

    &wl_surface_interface);

if (!id)

return NULL;

 

wl_proxy_marshal((struct wl_proxy *) wl_compositor,

WL_COMPOSITOR_CREATE_SURFACE, id);

 

return (struct wl_surface *) id;

}

 

注意,emit_stubs函数不会生成  wl_display 的桩函数,对于 wl_display,会有特殊的处理,在Wayland通信机制中会详细分析。

 

emit_code 函数

生成源码文件 wayland-protocol.c,其作用主要是为了进程间通信及消息打包提供方便。

在分析 emit_code 源码之前,有必要先介绍2个数据结构的定义,emit_code基于这2个数据结构生成代码。这2个数据结构定义在 wayland-util.h 中。

1) wl_message

其定义如下:

struct wl_message {

const char *name;

const char *signature;

const struct wl_interface **types;

};

wl_message 用来指定一条消息,消息可以是 request 或 event,其中name指定消息名,signature 指定参数类型,types指定参数的接口类型,如果参数类型为对象,则指向实际的对象接口类型,否则为 NULL 。

 

2) wl_interface

其定义如下:

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 用来指定一个接口, name 是接口名,version 指定版本,method_count 指定 method 数(就是接口的 request数),methods 指向 wl_message 数组(request 数组),event_count 指定 event 数,enents 指向 wl_message 数组(event 数组)。

 

下面我们来看实际的实现代码:

1 static void

2 emit_code(struct protocol *protocol)

3 {

4 struct interface *i;

 

5 if (protocol->copyright)

6 format_copyright(protocol->copyright);

 

7 printf("#include <stdlib.h>\n"

8 "#include <stdint.h>\n"

9 "#include \"wayland-util.h\"\n\n");

10

11 wl_list_for_each(i, &protocol->interface_list, link) {

12 emit_types_forward_declarations(protocol, &i->request_list);

13 emit_types_forward_declarations(protocol, &i->event_list);

14 }

15 printf("\n");

16

17 printf("static const struct wl_interface *types[] = {\n");

18 emit_null_run(protocol);

19 wl_list_for_each(i, &protocol->interface_list, link) {

20 emit_types(protocol, &i->request_list);

21 emit_types(protocol, &i->event_list);

22 }

23 printf("};\n\n");

24

25 wl_list_for_each(i, &protocol->interface_list, link) {

26

27 emit_messages(&i->request_list, i, "requests");

28 emit_messages(&i->event_list, i, "events");

29

30 printf("WL_EXPORT const struct wl_interface "

31 "%s_interface = {\n"

32 "\t\"%s\", %d,\n",

33 i->name, i->name, i->version);

34

35 if (!wl_list_empty(&i->request_list))

36 printf("\tARRAY_LENGTH(%s_requests), %s_requests,\n",

37 i->name, i->name);

38 else

39 printf("\t0, NULL,\n");

40

41 if (!wl_list_empty(&i->event_list))

42 printf("\tARRAY_LENGTH(%s_events), %s_events,\n",

43 i->name, i->name);

44 else

45 printf("\t0, NULL,\n");

46

47 printf("};\n\n");

48  }

49  }

 

11-15行:根据 request 和 event 列表中用到的对象类型,生成前置声明

如:extern const struct wl_interface wl_object_interface;

 

17-23行:生成 request 和 event 列表中用到的参数类型数组。 NULL 表示非对象类型。

18 行 emit_null_run(protocol); 的目的是在数组 types 的前面空间中存放 NULL,从而使参数都不是对象的 request 或 event 消息的types 指向这一索引 0,从而达到节省空间的目的。

 

27-28行:生成 message 的数组定义。

emit_message 函数定义:

static void

emit_messages(struct wl_list *message_list,

     struct interface *interface, const char *suffix)

{

struct message *m;

struct arg *a;

 

if (wl_list_empty(message_list))

return;

 

printf("static const struct wl_message "

      "%s_%s[] = {\n",

      interface->name, suffix);

 

wl_list_for_each(m, message_list, link) {

printf("\t{ \"%s\", \"", m->name);

wl_list_for_each(a, &m->arg_list, link) {

if (is_nullable_type(a) && a->nullable)

printf("?");

 

switch (a->type) {

default:

case INT:

printf("i");

break;

case NEW_ID:

printf("n");

break;

case UNSIGNED:

printf("u");

break;

case FIXED:

printf("f");

break;

case STRING:

printf("s");

break;

case OBJECT:

printf("o");

break;

case ARRAY:

printf("a");

break;

case FD:

printf("h");

break;

}

}

printf("\", types + %d },\n", m->type_index);

}

 

printf("};\n\n");

}

 

从该函数的定义我们可以看到 wl_message 结构中signature与数据类型之间的对应关系:

signature 类型

i         int

n         新对象(new id)

u         uint

f         fixed

s         string

o         object

a         array

h         fd (文件描述符)

 

生成代码的例子:

static const struct wl_message wl_display_requests[] = {

{ "bind", "usun", types + 8 },

{ "sync", "n", types + 12 },

};

 

static const struct wl_message wl_display_events[] = {

{ "error", "ous", types + 13 },

{ "global", "usu", types + 0 },

{ "global_remove", "u", types + 0 },

{ "delete_id", "u", types + 0 },

};

 

30-47行:为每个接口生成接口定义

 

生成代码的例子:

WL_EXPORT const struct wl_interface wl_display_interface = {

"wl_display", 1,

ARRAY_LENGTH(wl_display_requests), wl_display_requests,

ARRAY_LENGTH(wl_display_events), wl_display_events,

};

 

3. Wayland 协议的实现

我们知道,Wayland 协议中接口定义中包含3种类型:

1) request

2) event

3) enum

对于 enum 来说,由于其只是一个常量,仅仅只要定义一下就行。

而对于 request 与 event 来说,则在合成器与客户端两方都要实现。对于 request,需要做的事情:

1) 客户端能够调用 request,即客户端能向合成器发出请求

2) 在合成器端的 request 的真正实现

这其中的第一点已经由 wayland-scanner 生成的代码完成。

 

对于 event,需要做的事情:

1) 在发生事件时,合成器能够通知客户端

2) 客户端能够监听感兴趣的事件

这其中的第二点已经由 wayland-scanner 生成的代码完成。

综上所述,我们可以知道,实现 Wayland 协议的工作主要是:

1) 在合成器端的 request 的真正实现

2) 在发生事件时,合成器能够通知客户端

 

这里仅对 Wayland 协议的实现做一个总体的概述,其具体实现会在其它的文章中仔细分析。

 

4. Wayland 协议总结

Wayland 协议定义在 XML 文件中,接口定义包含3种:请求(request)、事件(event)、枚举(enum)。其中枚举是常量值;请求由客户端向 Compositor 发出,在 Compositor 端实现;事件由 Compositor 向客户端发出,事件处理函数在客户端实现。

 

解析 Wayland 协议使用工具 wayland-scanner 来完成,其目的是生成桩代码、接口定义、用于通信打包的数据结构等。

 

解析完成后,需要提供对接口的实现,主要在 Compositor 端进行,客户端只要处理感兴趣的事件。