blob: 0fa5079e4b698335f1d63f93a3775fe6f62a3995 [file] [log] [blame]
Kristian Høgsberga1f3f602010-08-03 09:26:44 -04001/*
2 * Copyright © 2010 Intel Corporation
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19#include <stdio.h>
20#include <string.h>
21#include <errno.h>
22#include <ctype.h>
23#include <expat.h>
24
25#include "wayland-util.h"
26
27static const char copyright[] =
28 "/*\n"
29 " * Copyright © 2010 Kristian Høgsberg\n"
30 " *\n"
31 " * Permission to use, copy, modify, distribute, and sell this software and its\n"
32 " * documentation for any purpose is hereby granted without fee, provided that\n"
33 " * the above copyright notice appear in all copies and that both that copyright\n"
34 " * notice and this permission notice appear in supporting documentation, and\n"
35 " * that the name of the copyright holders not be used in advertising or\n"
36 " * publicity pertaining to distribution of the software without specific,\n"
37 " * written prior permission. The copyright holders make no representations\n"
38 " * about the suitability of this software for any purpose. It is provided \"as\n"
39 " * is\" without express or implied warranty.\n"
40 " *\n"
41 " * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,\n"
42 " * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO\n"
43 " * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR\n"
44 " * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,\n"
45 " * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER\n"
46 " * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE\n"
47 " * OF THIS SOFTWARE.\n"
48 " */\n";
49
50static int
51usage(int ret)
52{
53 fprintf(stderr, "usage: ./scanner [header|code]\n");
54 exit(ret);
55}
56
57#define XML_BUFFER_SIZE 4096
58
59struct protocol {
60 struct wl_list interface_list;
61};
62
63struct interface {
64 char *name;
65 char *uppercase_name;
66 int version;
67 struct wl_list request_list;
68 struct wl_list event_list;
69 struct wl_list link;
70};
71
72struct message {
73 char *name;
74 char *uppercase_name;
75 struct wl_list arg_list;
76 struct wl_list link;
77};
78
79enum arg_type {
80 NEW_ID,
81 INT,
82 UNSIGNED,
83 STRING,
84 OBJECT,
85 ARRAY
86};
87
88struct arg {
89 char *name;
90 enum arg_type type;
Kristian Høgsberg3862e432010-08-09 21:25:50 -040091 char *interface_name;
Kristian Høgsberga1f3f602010-08-03 09:26:44 -040092 struct wl_list link;
93};
94
95struct parse_context {
96 struct protocol *protocol;
97 struct interface *interface;
98 struct message *message;
99};
100
101static char *
102uppercase_dup(const char *src)
103{
104 char *u;
105 int i;
106
107 u = strdup(src);
108 for (i = 0; u[i]; i++)
109 u[i] = toupper(u[i]);
110 u[i] = '\0';
111
112 return u;
113}
114
115static void
116start_element(void *data, const char *element_name, const char **atts)
117{
118 struct parse_context *ctx = data;
119 struct interface *interface;
120 struct message *message;
121 struct arg *arg;
Kristian Høgsberg3862e432010-08-09 21:25:50 -0400122 const char *name, *type, *interface_name;
Kristian Høgsberga1f3f602010-08-03 09:26:44 -0400123 int i, version;
124
Kristian Høgsberg3862e432010-08-09 21:25:50 -0400125 name = NULL;
126 type = NULL;
Kristian Høgsberga1f3f602010-08-03 09:26:44 -0400127 version = 0;
Kristian Høgsberg3862e432010-08-09 21:25:50 -0400128 interface_name = NULL;
Kristian Høgsberga1f3f602010-08-03 09:26:44 -0400129 for (i = 0; atts[i]; i += 2) {
130 if (strcmp(atts[i], "name") == 0)
131 name = atts[i + 1];
132 if (strcmp(atts[i], "version") == 0)
133 version = atoi(atts[i + 1]);
134 if (strcmp(atts[i], "type") == 0)
135 type = atts[i + 1];
Kristian Høgsberg3862e432010-08-09 21:25:50 -0400136 if (strcmp(atts[i], "interface") == 0)
137 interface_name = atts[i + 1];
Kristian Høgsberga1f3f602010-08-03 09:26:44 -0400138 }
139
140 if (strcmp(element_name, "interface") == 0) {
141 if (name == NULL) {
142 fprintf(stderr, "no interface name given\n");
143 exit(EXIT_FAILURE);
144 }
145
146 if (version == 0) {
147 fprintf(stderr, "no interface version given\n");
148 exit(EXIT_FAILURE);
149 }
150
151 interface = malloc(sizeof *interface);
152 interface->name = strdup(name);
153 interface->uppercase_name = uppercase_dup(name);
154 interface->version = version;
155 wl_list_init(&interface->request_list);
156 wl_list_init(&interface->event_list);
157 wl_list_insert(ctx->protocol->interface_list.prev,
158 &interface->link);
159 ctx->interface = interface;
160 } else if (strcmp(element_name, "request") == 0 ||
161 strcmp(element_name, "event") == 0) {
162 if (name == NULL) {
163 fprintf(stderr, "no request name given\n");
164 exit(EXIT_FAILURE);
165 }
166
167 message = malloc(sizeof *message);
168 message->name = strdup(name);
169 message->uppercase_name = uppercase_dup(name);
170 wl_list_init(&message->arg_list);
171
172 if (strcmp(element_name, "request") == 0)
173 wl_list_insert(ctx->interface->request_list.prev,
174 &message->link);
175 else
176 wl_list_insert(ctx->interface->event_list.prev,
177 &message->link);
178
179 ctx->message = message;
180 } else if (strcmp(element_name, "arg") == 0) {
181 arg = malloc(sizeof *arg);
182 arg->name = strdup(name);
183
Kristian Høgsberg3862e432010-08-09 21:25:50 -0400184 if (strcmp(type, "int") == 0)
Kristian Høgsberga1f3f602010-08-03 09:26:44 -0400185 arg->type = INT;
186 else if (strcmp(type, "uint") == 0)
187 arg->type = UNSIGNED;
188 else if (strcmp(type, "string") == 0)
189 arg->type = STRING;
190 else if (strcmp(type, "array") == 0)
191 arg->type = ARRAY;
Kristian Høgsberg3862e432010-08-09 21:25:50 -0400192 else if (strcmp(type, "new_id") == 0) {
193 if (interface_name == NULL) {
194 fprintf(stderr, "no interface name given\n");
195 exit(EXIT_FAILURE);
196 }
197 arg->type = NEW_ID;
198 arg->interface_name = strdup(interface_name);
199 } else if (strcmp(type, "object") == 0) {
200 if (interface_name == NULL) {
201 fprintf(stderr, "no interface name given\n");
202 exit(EXIT_FAILURE);
203 }
Kristian Høgsberga1f3f602010-08-03 09:26:44 -0400204 arg->type = OBJECT;
Kristian Høgsberg3862e432010-08-09 21:25:50 -0400205 arg->interface_name = strdup(interface_name);
206 } else {
207 fprintf(stderr, "unknown type: %s\n", type);
208 exit(EXIT_FAILURE);
Kristian Høgsberga1f3f602010-08-03 09:26:44 -0400209 }
210
211 wl_list_insert(ctx->message->arg_list.prev,
212 &arg->link);
213 }
214}
215
216static void
217emit_opcodes(struct wl_list *message_list, struct interface *interface)
218{
219 struct message *m;
220 int opcode;
221
222 if (wl_list_empty(message_list))
223 return;
224
225 opcode = 0;
226 wl_list_for_each(m, message_list, link)
227 printf("#define WL_%s_%s\t%d\n",
228 interface->uppercase_name, m->uppercase_name, opcode++);
229
230 printf("\n");
231}
232
233static void
Kristian Høgsberg3862e432010-08-09 21:25:50 -0400234emit_type(struct arg *a)
235{
236 switch (a->type) {
237 default:
238 case INT:
239 printf("int32_t ");
240 break;
241 case NEW_ID:
242 case UNSIGNED:
243 printf("uint32_t ");
244 break;
245 case STRING:
246 printf("const char *");
247 break;
248 case OBJECT:
249 printf("struct wl_%s *", a->interface_name);
250 break;
251 case ARRAY:
252 printf("struct wl_array *");
253 break;
254 }
255}
256
257static void
258emit_stubs(struct wl_list *message_list, struct interface *interface)
259{
260 struct message *m;
261 struct arg *a, *ret;
262
Kristian Høgsberg4fe1a3e2010-08-10 14:02:48 -0400263 /* We provide a hand written constructor for the display object */
264 if (strcmp(interface->name, "display") != 0)
265 printf("static inline struct wl_%s *\n"
266 "wl_%s_create(struct wl_display *display, uint32_t id)\n"
267 "{\n"
268 "\treturn (struct wl_%s *)\n"
269 "\t\twl_proxy_create_for_id(display, &wl_%s_interface, id);\n"
270 "}\n\n",
271 interface->name,
272 interface->name,
273 interface->name,
274 interface->name);
275
Kristian Høgsbergeef08fb2010-08-17 21:23:10 -0400276 printf("static inline void\n"
277 "wl_%s_set_user_data(struct wl_%s *%s, void *user_data)\n"
278 "{\n"
279 "\twl_proxy_set_user_data((struct wl_proxy *) %s, user_data);\n"
280 "}\n\n",
281 interface->name, interface->name, interface->name,
282 interface->name);
283
284 printf("static inline void *\n"
285 "wl_%s_get_user_data(struct wl_%s *%s)\n"
286 "{\n"
287 "\treturn wl_proxy_get_user_data((struct wl_proxy *) %s);\n"
288 "}\n\n",
289 interface->name, interface->name, interface->name,
290 interface->name);
291
Kristian Høgsberg3862e432010-08-09 21:25:50 -0400292 if (wl_list_empty(message_list))
293 return;
294
295 wl_list_for_each(m, message_list, link) {
296 ret = NULL;
297 wl_list_for_each(a, &m->arg_list, link) {
298 if (a->type == NEW_ID)
299 ret = a;
300 }
301
302 if (ret)
303 printf("static inline struct wl_%s *\n",
304 ret->interface_name);
305 else
306 printf("static inline void\n");
307
308 printf("wl_%s_%s(struct wl_%s *%s",
309 interface->name, m->name,
310 interface->name, interface->name);
311
312 wl_list_for_each(a, &m->arg_list, link) {
313 if (a->type == NEW_ID)
314 continue;
315 printf(", ");
316 emit_type(a);
317 printf("%s", a->name);
318 }
319
320 printf(")\n"
321 "{\n");
322 if (ret)
323 printf("\tstruct wl_proxy *%s;\n\n"
324 "\t%s = wl_proxy_create("
Kristian Høgsbergccb75862010-08-10 10:53:44 -0400325 "(struct wl_proxy *) %s,\n"
326 "\t\t\t &wl_%s_interface);\n"
Kristian Høgsberg3862e432010-08-09 21:25:50 -0400327 "\tif (!%s)\n"
328 "\t\treturn NULL;\n\n",
329 ret->name,
330 ret->name,
331 interface->name, ret->interface_name,
332 ret->name);
333
Kristian Høgsbergccb75862010-08-10 10:53:44 -0400334 printf("\twl_proxy_marshal((struct wl_proxy *) %s,\n"
335 "\t\t\t WL_%s_%s",
Kristian Høgsberg3862e432010-08-09 21:25:50 -0400336 interface->name,
337 interface->uppercase_name,
338 m->uppercase_name);
339
340 wl_list_for_each(a, &m->arg_list, link) {
341 printf(", ");
342 printf("%s", a->name);
343 }
344 printf(");\n");
345
346 if (ret)
347 printf("\n\treturn (struct wl_%s *) %s;\n",
348 ret->interface_name, ret->name);
349
350 printf("}\n\n");
351 }
352}
353
Kristian Høgsbergccb75862010-08-10 10:53:44 -0400354static const char *indent(int n)
355{
356 const char *whitespace[] = {
357 "\t\t\t\t\t\t\t\t\t\t\t\t",
358 "\t\t\t\t\t\t\t\t\t\t\t\t ",
359 "\t\t\t\t\t\t\t\t\t\t\t\t ",
360 "\t\t\t\t\t\t\t\t\t\t\t\t ",
361 "\t\t\t\t\t\t\t\t\t\t\t\t ",
362 "\t\t\t\t\t\t\t\t\t\t\t\t ",
363 "\t\t\t\t\t\t\t\t\t\t\t\t ",
364 "\t\t\t\t\t\t\t\t\t\t\t\t "
365 };
366
367 return whitespace[n % 8] + 12 - n / 8;
368}
369
Kristian Høgsberg3862e432010-08-09 21:25:50 -0400370static void
Kristian Høgsberga1f3f602010-08-03 09:26:44 -0400371emit_structs(struct wl_list *message_list, struct interface *interface)
372{
373 struct message *m;
374 struct arg *a;
Kristian Høgsbergccb75862010-08-10 10:53:44 -0400375 int is_interface, n;
Kristian Høgsberga1f3f602010-08-03 09:26:44 -0400376
Kristian Høgsberg3862e432010-08-09 21:25:50 -0400377 if (wl_list_empty(message_list))
378 return;
379
Kristian Høgsberga1f3f602010-08-03 09:26:44 -0400380 is_interface = message_list == &interface->request_list;
381 printf("struct wl_%s_%s {\n", interface->name,
382 is_interface ? "interface" : "listener");
383
384 wl_list_for_each(m, message_list, link) {
385 printf("\tvoid (*%s)(", m->name);
386
Kristian Høgsbergccb75862010-08-10 10:53:44 -0400387 n = strlen(m->name) + 17;
Kristian Høgsberga1f3f602010-08-03 09:26:44 -0400388 if (is_interface) {
Kristian Høgsbergccb75862010-08-10 10:53:44 -0400389 printf("struct wl_client *client,\n"
390 "%sstruct wl_%s *%s",
391 indent(n),
Kristian Høgsberga1f3f602010-08-03 09:26:44 -0400392 interface->name, interface->name);
393 } else {
Kristian Høgsbergccb75862010-08-10 10:53:44 -0400394 printf("void *data,\n"),
395 printf("%sstruct wl_%s *%s",
396 indent(n), interface->name, interface->name);
Kristian Høgsberga1f3f602010-08-03 09:26:44 -0400397 }
398
Kristian Høgsberga1f3f602010-08-03 09:26:44 -0400399 wl_list_for_each(a, &m->arg_list, link) {
Kristian Høgsbergccb75862010-08-10 10:53:44 -0400400 printf(",\n%s", indent(n));
401
Kristian Høgsberg3862e432010-08-09 21:25:50 -0400402 emit_type(a);
Kristian Høgsbergccb75862010-08-10 10:53:44 -0400403 printf("%s", a->name);
Kristian Høgsberga1f3f602010-08-03 09:26:44 -0400404 }
405
406 printf(");\n");
407 }
408
409 printf("};\n\n");
Kristian Høgsbergccb75862010-08-10 10:53:44 -0400410
411 if (!is_interface) {
412 printf("static inline int\n"
413 "wl_%s_add_listener(struct wl_%s *%s,\n"
414 "%sconst struct wl_%s_listener *listener, void *data)\n"
415 "{\n"
416 "\treturn wl_proxy_add_listener((struct wl_proxy *) %s,\n"
417 "%s(void (**)(void)) listener, data);\n"
418 "}\n\n",
419 interface->name, interface->name, interface->name,
420 indent(17 + strlen(interface->name)),
421 interface->name,
422 interface->name,
423 indent(37));
424 }
Kristian Høgsberga1f3f602010-08-03 09:26:44 -0400425}
426
Kristian Høgsberg4fe1a3e2010-08-10 14:02:48 -0400427static const char client_prototypes[] =
428 "struct wl_proxy;\n\n"
429
430 "extern void\n"
431 "wl_proxy_marshal(struct wl_proxy *p, uint32_t opcode, ...);\n"
432
433 "extern struct wl_proxy *\n"
434 "wl_proxy_create(struct wl_proxy *factory,\n"
435 "\t\tconst struct wl_interface *interface);\n"
436
437 "extern struct wl_proxy *\n"
438 "wl_proxy_create_for_id(struct wl_display *display,\n"
439 "\t\t const struct wl_interface *interface, uint32_t id);\n"
440
441 "extern int\n"
442 "wl_proxy_add_listener(struct wl_proxy *proxy,\n"
Kristian Høgsbergeef08fb2010-08-17 21:23:10 -0400443 "\t\t void (**implementation)(void), void *data);\n\n"
444
445 "extern void\n"
446 "wl_proxy_set_user_data(struct wl_proxy *proxy, void *user_data);\n\n"
447
448 "extern void *\n"
449 "wl_proxy_get_user_data(struct wl_proxy *proxy);\n\n";
450
Kristian Høgsberg4fe1a3e2010-08-10 14:02:48 -0400451
Kristian Høgsberga1f3f602010-08-03 09:26:44 -0400452static void
453emit_header(struct protocol *protocol, int server)
454{
455 struct interface *i;
456
457 printf("%s\n\n"
458 "#ifndef WAYLAND_PROTOCOL_H\n"
459 "#define WAYLAND_PROTOCOL_H\n"
460 "\n"
461 "#ifdef __cplusplus\n"
462 "extern \"C\" {\n"
463 "#endif\n"
464 "\n"
465 "#include <stdint.h>\n"
466 "#include \"wayland-util.h\"\n\n"
467 "struct wl_client;\n\n", copyright);
468
469 wl_list_for_each(i, &protocol->interface_list, link)
470 printf("struct wl_%s;\n", i->name);
471 printf("\n");
472
Kristian Høgsberg3862e432010-08-09 21:25:50 -0400473 if (!server)
Kristian Høgsberg4fe1a3e2010-08-10 14:02:48 -0400474 printf(client_prototypes);
Kristian Høgsberg3862e432010-08-09 21:25:50 -0400475
476 wl_list_for_each(i, &protocol->interface_list, link) {
477 printf("extern const struct wl_interface "
478 "wl_%s_interface;\n",
479 i->name);
480 }
481 printf("\n");
482
Kristian Høgsberga1f3f602010-08-03 09:26:44 -0400483 wl_list_for_each(i, &protocol->interface_list, link) {
484
485 if (server) {
486 emit_structs(&i->request_list, i);
487 emit_opcodes(&i->event_list, i);
488 } else {
489 emit_structs(&i->event_list, i);
490 emit_opcodes(&i->request_list, i);
Kristian Høgsberg3862e432010-08-09 21:25:50 -0400491 emit_stubs(&i->request_list, i);
Kristian Høgsberga1f3f602010-08-03 09:26:44 -0400492 }
Kristian Høgsberga1f3f602010-08-03 09:26:44 -0400493 }
494
495 printf("#ifdef __cplusplus\n"
496 "}\n"
497 "#endif\n"
498 "\n"
499 "#endif\n");
500}
501
502static void
503emit_messages(struct wl_list *message_list,
504 struct interface *interface, const char *suffix)
505{
506 struct message *m;
507 struct arg *a;
508
509 if (wl_list_empty(message_list))
510 return;
511
512 printf("static const struct wl_message "
513 "%s_%s[] = {\n",
514 interface->name, suffix);
515
516 wl_list_for_each(m, message_list, link) {
517 printf("\t{ \"%s\", \"", m->name);
518 wl_list_for_each(a, &m->arg_list, link) {
519 switch (a->type) {
520 default:
521 case INT:
522 printf("i");
523 break;
524 case NEW_ID:
525 printf("n");
526 break;
527 case UNSIGNED:
528 printf("u");
529 break;
530 case STRING:
531 printf("s");
532 break;
533 case OBJECT:
534 printf("o");
535 break;
536 case ARRAY:
537 printf("a");
538 break;
539 }
540 }
541 printf("\" },\n");
542 }
543
544 printf("};\n\n");
545}
546
547static void
548emit_code(struct protocol *protocol)
549{
550 struct interface *i;
551
552 printf("%s\n\n"
553 "#include <stdlib.h>\n"
554 "#include <stdint.h>\n"
555 "#include \"wayland-util.h\"\n\n",
556 copyright);
557
558 wl_list_for_each(i, &protocol->interface_list, link) {
559
560 emit_messages(&i->request_list, i, "requests");
561 emit_messages(&i->event_list, i, "events");
562
563 printf("WL_EXPORT const struct wl_interface "
564 "wl_%s_interface = {\n"
565 "\t\"%s\", %d,\n",
566 i->name, i->name, i->version);
567
568 if (!wl_list_empty(&i->request_list))
569 printf("\tARRAY_LENGTH(%s_requests), %s_requests,\n",
570 i->name, i->name);
571 else
572 printf("\t0, NULL,\n");
573
574 if (!wl_list_empty(&i->event_list))
575 printf("\tARRAY_LENGTH(%s_events), %s_events,\n",
576 i->name, i->name);
577 else
578 printf("\t0, NULL,\n");
579
580 printf("};\n\n");
581 }
582}
583
584int main(int argc, char *argv[])
585{
586 struct parse_context ctx;
587 struct protocol protocol;
588 XML_Parser parser;
589 int len;
590 void *buf;
591
592 if (argc != 2)
593 usage(EXIT_FAILURE);
594
595 wl_list_init(&protocol.interface_list);
596 ctx.protocol = &protocol;
597
598 parser = XML_ParserCreate(NULL);
599 XML_SetUserData(parser, &ctx);
600 if (parser == NULL) {
601 fprintf(stderr, "failed to create parser\n");
602 exit(EXIT_FAILURE);
603 }
604
605 XML_SetElementHandler(parser, start_element, NULL);
606 do {
607 buf = XML_GetBuffer(parser, XML_BUFFER_SIZE);
608 len = fread(buf, 1, XML_BUFFER_SIZE, stdin);
609 if (len < 0) {
610 fprintf(stderr, "fread: %s\n", strerror(errno));
611 exit(EXIT_FAILURE);
612 }
613 XML_ParseBuffer(parser, len, len == 0);
614
615 } while (len > 0);
616
617 XML_ParserFree(parser);
618
619 if (strcmp(argv[1], "client-header") == 0) {
620 emit_header(&protocol, 0);
621 } else if (strcmp(argv[1], "server-header") == 0) {
622 emit_header(&protocol, 1);
623 } else if (strcmp(argv[1], "code") == 0) {
624 emit_code(&protocol);
625 }
626
627 return 0;
628}