2 * Copyright © 2008-2011 Kristian Høgsberg
3 * Copyright © 2011 Intel Corporation
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that copyright
8 * notice and this permission notice appear in supporting documentation, and
9 * that the name of the copyright holders not be used in advertising or
10 * publicity pertaining to distribution of the software without specific,
11 * written prior permission. The copyright holders make no representations
12 * about the suitability of this software for any purpose. It is provided "as
13 * is" without express or implied warranty.
15 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
31 #include "wayland-util.h"
36 fprintf(stderr, "usage: ./scanner [client-header|server-header|code]\n");
40 #define XML_BUFFER_SIZE 4096
50 struct wl_list interface_list;
54 struct description *description;
62 struct wl_list request_list;
63 struct wl_list event_list;
64 struct wl_list enumeration_list;
66 struct description *description;
72 struct wl_list arg_list;
79 struct description *description;
104 char *uppercase_name;
105 struct wl_list entry_list;
107 struct description *description;
112 char *uppercase_name;
118 struct parse_context {
119 const char *filename;
121 struct protocol *protocol;
122 struct interface *interface;
123 struct message *message;
124 struct enumeration *enumeration;
125 struct description *description;
126 char character_data[8192];
127 unsigned int character_data_length;
131 uppercase_dup(const char *src)
137 for (i = 0; u[i]; i++)
138 u[i] = toupper(u[i]);
144 static const char *indent(int n)
146 const char *whitespace[] = {
147 "\t\t\t\t\t\t\t\t\t\t\t\t",
148 "\t\t\t\t\t\t\t\t\t\t\t\t ",
149 "\t\t\t\t\t\t\t\t\t\t\t\t ",
150 "\t\t\t\t\t\t\t\t\t\t\t\t ",
151 "\t\t\t\t\t\t\t\t\t\t\t\t ",
152 "\t\t\t\t\t\t\t\t\t\t\t\t ",
153 "\t\t\t\t\t\t\t\t\t\t\t\t ",
154 "\t\t\t\t\t\t\t\t\t\t\t\t "
157 return whitespace[n % 8] + 12 - n / 8;
161 desc_dump(const char *fmt, ...)
164 char buf[128], *desc, hang;
165 int col, i, j, k, startcol;
168 vsnprintf(buf, sizeof buf, fmt, ap);
169 desc = va_arg(ap, char *);
172 for (i = 0, col = 0; buf[i] != '*'; i++) {
174 col = (col + 8) & ~7;
187 col += strlen(&buf[i]);
188 if (col - startcol > 2)
193 for (i = 0; desc[i]; ) {
195 while (desc[i] && isspace(desc[i]))
201 while (desc[i] && !isspace(desc[i]))
204 if (col + i - j > 72) {
205 printf("\n%s*%c", indent(startcol), hang);
209 if (col > startcol && k > 0)
211 col += printf("%.*s", i - j, &desc[j]);
217 fail(struct parse_context *ctx, const char *msg)
219 fprintf(stderr, "%s:%ld: %s\n",
220 ctx->filename, XML_GetCurrentLineNumber(ctx->parser), msg);
225 is_nullable_type(struct arg *arg)
228 /* Strings, objects, and arrays are possibly nullable */
240 start_element(void *data, const char *element_name, const char **atts)
242 struct parse_context *ctx = data;
243 struct interface *interface;
244 struct message *message;
246 struct enumeration *enumeration;
248 struct description *description;
249 const char *name, *type, *interface_name, *value, *summary, *since;
250 const char *allow_null;
257 interface_name = NULL;
263 for (i = 0; atts[i]; i += 2) {
264 if (strcmp(atts[i], "name") == 0)
266 if (strcmp(atts[i], "version") == 0)
267 version = atoi(atts[i + 1]);
268 if (strcmp(atts[i], "type") == 0)
270 if (strcmp(atts[i], "value") == 0)
272 if (strcmp(atts[i], "interface") == 0)
273 interface_name = atts[i + 1];
274 if (strcmp(atts[i], "summary") == 0)
275 summary = atts[i + 1];
276 if (strcmp(atts[i], "since") == 0)
278 if (strcmp(atts[i], "allow-null") == 0)
279 allow_null = atts[i + 1];
282 ctx->character_data_length = 0;
283 if (strcmp(element_name, "protocol") == 0) {
285 fail(ctx, "no protocol name given");
287 ctx->protocol->name = strdup(name);
288 ctx->protocol->uppercase_name = uppercase_dup(name);
289 ctx->protocol->description = NULL;
290 } else if (strcmp(element_name, "copyright") == 0) {
292 } else if (strcmp(element_name, "interface") == 0) {
294 fail(ctx, "no interface name given");
297 fail(ctx, "no interface version given");
299 interface = malloc(sizeof *interface);
300 interface->name = strdup(name);
301 interface->uppercase_name = uppercase_dup(name);
302 interface->version = version;
303 interface->description = NULL;
304 interface->since = 1;
305 wl_list_init(&interface->request_list);
306 wl_list_init(&interface->event_list);
307 wl_list_init(&interface->enumeration_list);
308 wl_list_insert(ctx->protocol->interface_list.prev,
310 ctx->interface = interface;
311 } else if (strcmp(element_name, "request") == 0 ||
312 strcmp(element_name, "event") == 0) {
314 fail(ctx, "no request name given");
316 message = malloc(sizeof *message);
317 message->name = strdup(name);
318 message->uppercase_name = uppercase_dup(name);
319 wl_list_init(&message->arg_list);
320 message->arg_count = 0;
321 message->description = NULL;
323 if (strcmp(element_name, "request") == 0)
324 wl_list_insert(ctx->interface->request_list.prev,
327 wl_list_insert(ctx->interface->event_list.prev,
330 if (type != NULL && strcmp(type, "destructor") == 0)
331 message->destructor = 1;
333 message->destructor = 0;
336 version = strtol(since, &end, 0);
337 if (errno == EINVAL || end == since || *end != '\0')
338 fail(ctx, "invalid integer\n");
339 if (version <= ctx->interface->since)
340 fail(ctx, "since version not increasing\n");
341 ctx->interface->since = version;
344 message->since = ctx->interface->since;
346 if (strcmp(name, "destroy") == 0 && !message->destructor)
347 fail(ctx, "destroy request should be destructor type");
349 ctx->message = message;
350 } else if (strcmp(element_name, "arg") == 0) {
351 arg = malloc(sizeof *arg);
352 arg->name = strdup(name);
354 if (strcmp(type, "int") == 0)
356 else if (strcmp(type, "uint") == 0)
357 arg->type = UNSIGNED;
358 else if (strcmp(type, "fixed") == 0)
360 else if (strcmp(type, "string") == 0)
362 else if (strcmp(type, "array") == 0)
364 else if (strcmp(type, "fd") == 0)
366 else if (strcmp(type, "new_id") == 0) {
368 } else if (strcmp(type, "object") == 0) {
371 fail(ctx, "unknown type");
378 arg->interface_name = strdup(interface_name);
381 if (interface_name != NULL)
382 fail(ctx, "interface not allowed");
386 if (allow_null == NULL || strcmp(allow_null, "false") == 0)
388 else if (strcmp(allow_null, "true") == 0)
391 fail(ctx, "invalid value for allow-null attribute");
393 if (allow_null != NULL && !is_nullable_type(arg))
394 fail(ctx, "allow-null is only valid for objects, strings, and arrays");
398 arg->summary = strdup(summary);
400 wl_list_insert(ctx->message->arg_list.prev, &arg->link);
401 ctx->message->arg_count++;
402 } else if (strcmp(element_name, "enum") == 0) {
404 fail(ctx, "no enum name given");
406 enumeration = malloc(sizeof *enumeration);
407 enumeration->name = strdup(name);
408 enumeration->uppercase_name = uppercase_dup(name);
409 enumeration->description = NULL;
410 wl_list_init(&enumeration->entry_list);
412 wl_list_insert(ctx->interface->enumeration_list.prev,
415 ctx->enumeration = enumeration;
416 } else if (strcmp(element_name, "entry") == 0) {
418 fail(ctx, "no entry name given");
420 entry = malloc(sizeof *entry);
421 entry->name = strdup(name);
422 entry->uppercase_name = uppercase_dup(name);
423 entry->value = strdup(value);
425 entry->summary = strdup(summary);
427 entry->summary = NULL;
428 wl_list_insert(ctx->enumeration->entry_list.prev,
430 } else if (strcmp(element_name, "description") == 0) {
432 fail(ctx, "description without summary");
434 description = malloc(sizeof *description);
436 description->summary = strdup(summary);
438 description->summary = NULL;
441 ctx->message->description = description;
442 else if (ctx->enumeration)
443 ctx->enumeration->description = description;
444 else if (ctx->interface)
445 ctx->interface->description = description;
447 ctx->protocol->description = description;
448 ctx->description = description;
453 end_element(void *data, const XML_Char *name)
455 struct parse_context *ctx = data;
457 if (strcmp(name, "copyright") == 0) {
458 ctx->protocol->copyright =
459 strndup(ctx->character_data,
460 ctx->character_data_length);
461 } else if (strcmp(name, "description") == 0) {
462 char *text = strndup(ctx->character_data,
463 ctx->character_data_length);
465 ctx->description->text = text;
466 ctx->description = NULL;
467 } else if (strcmp(name, "request") == 0 ||
468 strcmp(name, "event") == 0) {
470 } else if (strcmp(name, "enum") == 0) {
471 ctx->enumeration = NULL;
476 character_data(void *data, const XML_Char *s, int len)
478 struct parse_context *ctx = data;
480 if (ctx->character_data_length + len > sizeof (ctx->character_data)) {
481 fprintf(stderr, "too much character data");
485 memcpy(ctx->character_data + ctx->character_data_length, s, len);
486 ctx->character_data_length += len;
490 emit_opcodes(struct wl_list *message_list, struct interface *interface)
495 if (wl_list_empty(message_list))
499 wl_list_for_each(m, message_list, link)
500 printf("#define %s_%s\t%d\n",
501 interface->uppercase_name, m->uppercase_name, opcode++);
507 emit_type(struct arg *a)
520 printf("wl_fixed_t ");
523 printf("const char *");
526 printf("struct %s *", a->interface_name);
529 printf("struct wl_array *");
535 emit_stubs(struct wl_list *message_list, struct interface *interface)
539 int has_destructor, has_destroy;
541 printf("static inline void\n"
542 "%s_set_user_data(struct %s *%s, void *user_data)\n"
544 "\twl_proxy_set_user_data((struct wl_proxy *) %s, user_data);\n"
546 interface->name, interface->name, interface->name,
549 printf("static inline void *\n"
550 "%s_get_user_data(struct %s *%s)\n"
552 "\treturn wl_proxy_get_user_data((struct wl_proxy *) %s);\n"
554 interface->name, interface->name, interface->name,
559 wl_list_for_each(m, message_list, link) {
562 if (strcmp(m->name, "destroy)") == 0)
566 if (!has_destructor && has_destroy) {
568 "interface %s has method named destroy but"
569 "no destructor", interface->name);
573 if (!has_destructor && strcmp(interface->name, "wl_display") != 0)
574 printf("static inline void\n"
575 "%s_destroy(struct %s *%s)\n"
577 "\twl_proxy_destroy("
578 "(struct wl_proxy *) %s);\n"
580 interface->name, interface->name, interface->name,
583 if (wl_list_empty(message_list))
586 wl_list_for_each(m, message_list, link) {
588 wl_list_for_each(a, &m->arg_list, link) {
589 if (a->type == NEW_ID)
593 if (ret && ret->interface_name == NULL)
594 printf("static inline void *\n");
596 printf("static inline struct %s *\n",
597 ret->interface_name);
599 printf("static inline void\n");
601 printf("%s_%s(struct %s *%s",
602 interface->name, m->name,
603 interface->name, interface->name);
605 wl_list_for_each(a, &m->arg_list, link) {
606 if (a->type == NEW_ID && a->interface_name == NULL) {
607 printf(", const struct wl_interface *interface"
608 ", uint32_t version");
610 } else if (a->type == NEW_ID)
614 printf("%s", a->name);
620 printf("\tstruct wl_proxy *%s;\n\n"
621 "\t%s = wl_proxy_create("
622 "(struct wl_proxy *) %s,\n",
623 ret->name, ret->name, interface->name);
624 if (ret->interface_name == NULL)
625 printf("\t\t\t interface);\n");
627 printf("\t\t\t &%s_interface);\n",
628 ret->interface_name);
630 printf("\tif (!%s)\n"
631 "\t\treturn NULL;\n\n",
635 printf("\twl_proxy_marshal((struct wl_proxy *) %s,\n"
638 interface->uppercase_name,
641 wl_list_for_each(a, &m->arg_list, link) {
642 if (a->type == NEW_ID && a->interface_name == NULL)
643 printf(", interface->name, version");
645 printf("%s", a->name);
650 printf("\n\twl_proxy_destroy("
651 "(struct wl_proxy *) %s);\n",
654 if (ret && ret->interface_name == NULL)
655 printf("\n\treturn (void *) %s;\n", ret->name);
657 printf("\n\treturn (struct %s *) %s;\n",
658 ret->interface_name, ret->name);
665 emit_event_wrappers(struct wl_list *message_list, struct interface *interface)
670 /* We provide hand written functions for the display object */
671 if (strcmp(interface->name, "wl_display") == 0)
674 wl_list_for_each(m, message_list, link) {
675 printf("static inline void\n"
676 "%s_send_%s(struct wl_resource *resource_",
677 interface->name, m->name);
679 wl_list_for_each(a, &m->arg_list, link) {
684 printf("struct wl_resource *");
689 printf("%s", a->name);
694 "\twl_resource_post_event(resource_, %s_%s",
695 interface->uppercase_name, m->uppercase_name);
697 wl_list_for_each(a, &m->arg_list, link)
698 printf(", %s", a->name);
706 emit_enumerations(struct interface *interface)
708 struct enumeration *e;
711 wl_list_for_each(e, &interface->enumeration_list, link) {
712 struct description *desc = e->description;
714 printf("#ifndef %s_%s_ENUM\n",
715 interface->uppercase_name, e->uppercase_name);
716 printf("#define %s_%s_ENUM\n",
717 interface->uppercase_name, e->uppercase_name);
721 desc_dump(" * %s_%s - ",
722 interface->name, e->name, desc->summary);
723 wl_list_for_each(entry, &e->entry_list, link) {
724 desc_dump(" * @%s_%s_%s: ",
725 interface->uppercase_name,
727 entry->uppercase_name,
732 desc_dump(" * ", desc->text);
736 printf("enum %s_%s {\n", interface->name, e->name);
737 wl_list_for_each(entry, &e->entry_list, link)
738 printf("\t%s_%s_%s = %s,\n",
739 interface->uppercase_name,
741 entry->uppercase_name, entry->value);
743 printf("#endif /* %s_%s_ENUM */\n\n",
744 interface->uppercase_name, e->uppercase_name);
749 emit_structs(struct wl_list *message_list, struct interface *interface)
755 if (wl_list_empty(message_list))
758 is_interface = message_list == &interface->request_list;
759 if (interface->description) {
760 struct description *desc = interface->description;
762 desc_dump(" * %s - ", interface->name, desc->summary);
763 wl_list_for_each(m, message_list, link) {
764 struct description *mdesc = m->description;
765 desc_dump(" * @%s: ",
766 m->name, mdesc ? mdesc->summary : "(none)");
769 desc_dump(" * ", desc->text);
772 printf("struct %s_%s {\n", interface->name,
773 is_interface ? "interface" : "listener");
775 wl_list_for_each(m, message_list, link) {
776 struct description *mdesc = m->description;
779 desc_dump("\t * %s - ",
780 m->name, mdesc ? mdesc->summary : "(none)");
781 wl_list_for_each(a, &m->arg_list, link) {
783 if (is_interface && a->type == NEW_ID &&
784 a->interface_name == NULL)
785 printf("\t * @interface: name of the objects interface\n"
786 "\t * @version: version of the objects interface\n");
789 desc_dump("\t * @%s: ",
790 a->name, a->summary ? a->summary : "(none)");
794 desc_dump("\t * ", mdesc->text, 8, 0);
797 printf("\t * @since: %d\n", m->since);
800 printf("\tvoid (*%s)(", m->name);
802 n = strlen(m->name) + 17;
804 printf("struct wl_client *client,\n"
805 "%sstruct wl_resource *resource",
808 printf("void *data,\n"),
809 printf("%sstruct %s *%s",
810 indent(n), interface->name, interface->name);
813 wl_list_for_each(a, &m->arg_list, link) {
814 printf(",\n%s", indent(n));
816 if (is_interface && a->type == OBJECT)
817 printf("struct wl_resource *");
818 else if (is_interface && a->type == NEW_ID && a->interface_name == NULL)
819 printf("const char *interface, uint32_t version, uint32_t ");
820 else if (!is_interface && a->type == OBJECT && a->interface_name == NULL)
821 printf("struct wl_object *");
823 else if (!is_interface && a->type == NEW_ID)
824 printf("struct %s *", a->interface_name);
828 printf("%s", a->name);
837 printf("static inline int\n"
838 "%s_add_listener(struct %s *%s,\n"
839 "%sconst struct %s_listener *listener, void *data)\n"
841 "\treturn wl_proxy_add_listener((struct wl_proxy *) %s,\n"
842 "%s(void (**)(void)) listener, data);\n"
844 interface->name, interface->name, interface->name,
845 indent(14 + strlen(interface->name)),
853 format_copyright(const char *copyright)
855 int bol = 1, start = 0, i;
857 for (i = 0; copyright[i]; i++) {
858 if (bol && (copyright[i] == ' ' || copyright[i] == '\t')) {
865 if (copyright[i] == '\n' || copyright[i] == '\0') {
867 i == 0 ? "/*" : " *",
868 i - start, copyright + start);
876 emit_header(struct protocol *protocol, int server)
879 const char *s = server ? "SERVER" : "CLIENT";
881 if (protocol->copyright)
882 format_copyright(protocol->copyright);
884 printf("#ifndef %s_%s_PROTOCOL_H\n"
885 "#define %s_%s_PROTOCOL_H\n"
887 "#ifdef __cplusplus\n"
891 "#include <stdint.h>\n"
892 "#include <stddef.h>\n"
893 "#include \"%s\"\n\n"
894 "struct wl_client;\n"
895 "struct wl_resource;\n\n",
896 protocol->uppercase_name, s,
897 protocol->uppercase_name, s,
898 server ? "wayland-util.h" : "wayland-client.h");
900 wl_list_for_each(i, &protocol->interface_list, link)
901 printf("struct %s;\n", i->name);
904 wl_list_for_each(i, &protocol->interface_list, link) {
905 printf("extern const struct wl_interface "
911 wl_list_for_each(i, &protocol->interface_list, link) {
913 emit_enumerations(i);
916 emit_structs(&i->request_list, i);
917 emit_opcodes(&i->event_list, i);
918 emit_event_wrappers(&i->event_list, i);
920 emit_structs(&i->event_list, i);
921 emit_opcodes(&i->request_list, i);
922 emit_stubs(&i->request_list, i);
926 printf("#ifdef __cplusplus\n"
934 emit_types_forward_declarations(struct protocol *protocol,
935 struct wl_list *message_list)
941 wl_list_for_each(m, message_list, link) {
944 wl_list_for_each(a, &m->arg_list, link) {
949 if (!a->interface_name)
953 printf("extern const struct wl_interface %s_interface;\n",
961 if (m->all_null && length > protocol->null_run_length)
962 protocol->null_run_length = length;
967 emit_null_run(struct protocol *protocol)
971 for (i = 0; i < protocol->null_run_length; i++)
976 emit_types(struct protocol *protocol, struct wl_list *message_list)
981 wl_list_for_each(m, message_list, link) {
988 protocol->null_run_length + protocol->type_index;
989 protocol->type_index += m->arg_count;
991 wl_list_for_each(a, &m->arg_list, link) {
995 if (a->interface_name)
996 printf("\t&%s_interface,\n",
1002 printf("\tNULL,\n");
1010 emit_messages(struct wl_list *message_list,
1011 struct interface *interface, const char *suffix)
1016 if (wl_list_empty(message_list))
1019 printf("static const struct wl_message "
1021 interface->name, suffix);
1023 wl_list_for_each(m, message_list, link) {
1024 printf("\t{ \"%s\", \"", m->name);
1025 wl_list_for_each(a, &m->arg_list, link) {
1026 if (is_nullable_type(a) && a->nullable)
1035 if (a->interface_name == NULL)
1059 printf("\", types + %d },\n", m->type_index);
1066 emit_code(struct protocol *protocol)
1068 struct interface *i;
1070 if (protocol->copyright)
1071 format_copyright(protocol->copyright);
1073 printf("#include <stdlib.h>\n"
1074 "#include <stdint.h>\n"
1075 "#include \"wayland-util.h\"\n\n");
1077 wl_list_for_each(i, &protocol->interface_list, link) {
1078 emit_types_forward_declarations(protocol, &i->request_list);
1079 emit_types_forward_declarations(protocol, &i->event_list);
1083 printf("static const struct wl_interface *types[] = {\n");
1084 emit_null_run(protocol);
1085 wl_list_for_each(i, &protocol->interface_list, link) {
1086 emit_types(protocol, &i->request_list);
1087 emit_types(protocol, &i->event_list);
1091 wl_list_for_each(i, &protocol->interface_list, link) {
1093 emit_messages(&i->request_list, i, "requests");
1094 emit_messages(&i->event_list, i, "events");
1096 printf("WL_EXPORT const struct wl_interface "
1097 "%s_interface = {\n"
1099 i->name, i->name, i->version);
1101 if (!wl_list_empty(&i->request_list))
1102 printf("\tARRAY_LENGTH(%s_requests), %s_requests,\n",
1105 printf("\t0, NULL,\n");
1107 if (!wl_list_empty(&i->event_list))
1108 printf("\tARRAY_LENGTH(%s_events), %s_events,\n",
1111 printf("\t0, NULL,\n");
1117 int main(int argc, char *argv[])
1119 struct parse_context ctx;
1120 struct protocol protocol;
1125 usage(EXIT_FAILURE);
1127 wl_list_init(&protocol.interface_list);
1128 protocol.type_index = 0;
1129 protocol.null_run_length = 0;
1130 protocol.copyright = NULL;
1131 ctx.protocol = &protocol;
1133 ctx.filename = "<stdin>";
1134 ctx.parser = XML_ParserCreate(NULL);
1135 XML_SetUserData(ctx.parser, &ctx);
1136 if (ctx.parser == NULL) {
1137 fprintf(stderr, "failed to create parser\n");
1141 XML_SetElementHandler(ctx.parser, start_element, end_element);
1142 XML_SetCharacterDataHandler(ctx.parser, character_data);
1145 buf = XML_GetBuffer(ctx.parser, XML_BUFFER_SIZE);
1146 len = fread(buf, 1, XML_BUFFER_SIZE, stdin);
1148 fprintf(stderr, "fread: %m\n");
1151 XML_ParseBuffer(ctx.parser, len, len == 0);
1155 XML_ParserFree(ctx.parser);
1157 if (strcmp(argv[1], "client-header") == 0) {
1158 emit_header(&protocol, 0);
1159 } else if (strcmp(argv[1], "server-header") == 0) {
1160 emit_header(&protocol, 1);
1161 } else if (strcmp(argv[1], "code") == 0) {
1162 emit_code(&protocol);