mirror of
				https://gitlab.freedesktop.org/wayland/wayland.git
				synced 2025-11-03 09:01:42 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			443 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			443 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Copyright © 2010 Intel Corporation
 | 
						|
 *
 | 
						|
 * This program is free software; you can redistribute it and/or modify
 | 
						|
 * it under the terms of the GNU General Public License as published by
 | 
						|
 * the Free Software Foundation; either version 2 of the License, or
 | 
						|
 * (at your option) any later version.
 | 
						|
 *
 | 
						|
 * This program is distributed in the hope that it will be useful,
 | 
						|
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
 * GNU General Public License for more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU General Public License
 | 
						|
 * along with this program; if not, write to the Free Software Foundation,
 | 
						|
 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 | 
						|
 */
 | 
						|
 | 
						|
#include <stdio.h>
 | 
						|
#include <string.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <ctype.h>
 | 
						|
#include <expat.h>
 | 
						|
 | 
						|
#include "wayland-util.h"
 | 
						|
 | 
						|
static const char copyright[] =
 | 
						|
	"/*\n"
 | 
						|
	" * Copyright © 2010 Kristian Høgsberg\n"
 | 
						|
	" *\n"
 | 
						|
	" * Permission to use, copy, modify, distribute, and sell this software and its\n"
 | 
						|
	" * documentation for any purpose is hereby granted without fee, provided that\n"
 | 
						|
	" * the above copyright notice appear in all copies and that both that copyright\n"
 | 
						|
	" * notice and this permission notice appear in supporting documentation, and\n"
 | 
						|
	" * that the name of the copyright holders not be used in advertising or\n"
 | 
						|
	" * publicity pertaining to distribution of the software without specific,\n"
 | 
						|
	" * written prior permission.  The copyright holders make no representations\n"
 | 
						|
	" * about the suitability of this software for any purpose.  It is provided \"as\n"
 | 
						|
	" * is\" without express or implied warranty.\n"
 | 
						|
	" *\n"
 | 
						|
	" * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,\n"
 | 
						|
	" * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO\n"
 | 
						|
	" * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR\n"
 | 
						|
	" * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,\n"
 | 
						|
	" * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER\n"
 | 
						|
	" * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE\n"
 | 
						|
	" * OF THIS SOFTWARE.\n"
 | 
						|
	" */\n";
 | 
						|
 | 
						|
static int
 | 
						|
usage(int ret)
 | 
						|
{
 | 
						|
	fprintf(stderr, "usage: ./scanner [header|code]\n");
 | 
						|
	exit(ret);
 | 
						|
}
 | 
						|
 | 
						|
#define XML_BUFFER_SIZE 4096
 | 
						|
 | 
						|
struct protocol {
 | 
						|
	struct wl_list interface_list;
 | 
						|
};
 | 
						|
 | 
						|
struct interface {
 | 
						|
	char *name;
 | 
						|
	char *uppercase_name;
 | 
						|
	int version;
 | 
						|
	struct wl_list request_list;
 | 
						|
	struct wl_list event_list;
 | 
						|
	struct wl_list link;
 | 
						|
};
 | 
						|
 | 
						|
struct message {
 | 
						|
	char *name;
 | 
						|
	char *uppercase_name;
 | 
						|
	struct wl_list arg_list;
 | 
						|
	struct wl_list link;
 | 
						|
};
 | 
						|
 | 
						|
enum arg_type {
 | 
						|
	NEW_ID,
 | 
						|
	INT,
 | 
						|
	UNSIGNED,
 | 
						|
	STRING,
 | 
						|
	OBJECT,
 | 
						|
	ARRAY
 | 
						|
};
 | 
						|
 | 
						|
struct arg {
 | 
						|
	char *name;
 | 
						|
	enum arg_type type;
 | 
						|
	char *object_name;
 | 
						|
	struct wl_list link;
 | 
						|
};
 | 
						|
 | 
						|
struct parse_context {
 | 
						|
	struct protocol *protocol;
 | 
						|
	struct interface *interface;
 | 
						|
	struct message *message;
 | 
						|
};
 | 
						|
 | 
						|
static char *
 | 
						|
uppercase_dup(const char *src)
 | 
						|
{
 | 
						|
	char *u;
 | 
						|
	int i;
 | 
						|
 | 
						|
	u = strdup(src);
 | 
						|
	for (i = 0; u[i]; i++)
 | 
						|
		u[i] = toupper(u[i]);
 | 
						|
	u[i] = '\0';
 | 
						|
 | 
						|
	return u;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
start_element(void *data, const char *element_name, const char **atts)
 | 
						|
{
 | 
						|
	struct parse_context *ctx = data;
 | 
						|
	struct interface *interface;
 | 
						|
	struct message *message;
 | 
						|
	struct arg *arg;
 | 
						|
	const char *name, *type;
 | 
						|
	int i, version;
 | 
						|
 | 
						|
	name = 0;
 | 
						|
	type = 0;
 | 
						|
	version = 0;
 | 
						|
	for (i = 0; atts[i]; i += 2) {
 | 
						|
		if (strcmp(atts[i], "name") == 0)
 | 
						|
			name = atts[i + 1];
 | 
						|
		if (strcmp(atts[i], "version") == 0)
 | 
						|
			version = atoi(atts[i + 1]);
 | 
						|
		if (strcmp(atts[i], "type") == 0)
 | 
						|
			type = atts[i + 1];
 | 
						|
	}
 | 
						|
 | 
						|
	if (strcmp(element_name, "interface") == 0) {
 | 
						|
		if (name == NULL) {
 | 
						|
			fprintf(stderr, "no interface name given\n");
 | 
						|
			exit(EXIT_FAILURE);
 | 
						|
		}
 | 
						|
 | 
						|
		if (version == 0) {
 | 
						|
			fprintf(stderr, "no interface version given\n");
 | 
						|
			exit(EXIT_FAILURE);
 | 
						|
		}
 | 
						|
 | 
						|
		interface = malloc(sizeof *interface);
 | 
						|
		interface->name = strdup(name);
 | 
						|
		interface->uppercase_name = uppercase_dup(name);
 | 
						|
		interface->version = version;
 | 
						|
		wl_list_init(&interface->request_list);
 | 
						|
		wl_list_init(&interface->event_list);
 | 
						|
		wl_list_insert(ctx->protocol->interface_list.prev,
 | 
						|
			       &interface->link);
 | 
						|
		ctx->interface = interface;
 | 
						|
	} else if (strcmp(element_name, "request") == 0 ||
 | 
						|
		   strcmp(element_name, "event") == 0) {
 | 
						|
		if (name == NULL) {
 | 
						|
			fprintf(stderr, "no request name given\n");
 | 
						|
			exit(EXIT_FAILURE);
 | 
						|
		}
 | 
						|
 | 
						|
		message = malloc(sizeof *message);
 | 
						|
		message->name = strdup(name);
 | 
						|
		message->uppercase_name = uppercase_dup(name);
 | 
						|
		wl_list_init(&message->arg_list);
 | 
						|
 | 
						|
		if (strcmp(element_name, "request") == 0)
 | 
						|
			wl_list_insert(ctx->interface->request_list.prev,
 | 
						|
				       &message->link);
 | 
						|
		else
 | 
						|
			wl_list_insert(ctx->interface->event_list.prev,
 | 
						|
				       &message->link);
 | 
						|
 | 
						|
		ctx->message = message;
 | 
						|
	} else if (strcmp(element_name, "arg") == 0) {
 | 
						|
		arg = malloc(sizeof *arg);
 | 
						|
		arg->name = strdup(name);
 | 
						|
 | 
						|
		if (strcmp(type, "new_id") == 0)
 | 
						|
			arg->type = NEW_ID;
 | 
						|
		else if (strcmp(type, "int") == 0)
 | 
						|
			arg->type = INT;
 | 
						|
		else if (strcmp(type, "uint") == 0)
 | 
						|
			arg->type = UNSIGNED;
 | 
						|
		else if (strcmp(type, "string") == 0)
 | 
						|
			arg->type = STRING;
 | 
						|
		else if (strcmp(type, "array") == 0)
 | 
						|
			arg->type = ARRAY;
 | 
						|
		else {
 | 
						|
			arg->type = OBJECT;
 | 
						|
			arg->object_name = strdup(type);
 | 
						|
		}
 | 
						|
 | 
						|
		wl_list_insert(ctx->message->arg_list.prev,
 | 
						|
			       &arg->link);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
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 WL_%s_%s\t%d\n",
 | 
						|
		       interface->uppercase_name, m->uppercase_name, opcode++);
 | 
						|
 | 
						|
	printf("\n");
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
emit_structs(struct wl_list *message_list, struct interface *interface)
 | 
						|
{
 | 
						|
	struct message *m;
 | 
						|
	struct arg *a;
 | 
						|
	int is_interface;
 | 
						|
 | 
						|
	is_interface = message_list == &interface->request_list;
 | 
						|
	printf("struct wl_%s_%s {\n", interface->name,
 | 
						|
	       is_interface ? "interface" : "listener");
 | 
						|
 | 
						|
	wl_list_for_each(m, message_list, link) {
 | 
						|
		printf("\tvoid (*%s)(", m->name);
 | 
						|
 | 
						|
		if (is_interface) {
 | 
						|
			printf("struct wl_client *client, struct wl_%s *%s",
 | 
						|
			       interface->name, interface->name);
 | 
						|
		} else {
 | 
						|
			printf("void *data, struct wl_%s *%s",
 | 
						|
			       interface->name, interface->name);
 | 
						|
		}
 | 
						|
 | 
						|
		if (!wl_list_empty(&m->arg_list))
 | 
						|
			printf(", ");
 | 
						|
 | 
						|
		wl_list_for_each(a, &m->arg_list, link) {
 | 
						|
			switch (a->type) {
 | 
						|
			default:
 | 
						|
			case INT:
 | 
						|
				printf("int32_t ");
 | 
						|
				break;
 | 
						|
			case NEW_ID:
 | 
						|
			case UNSIGNED:
 | 
						|
				printf("uint32_t ");
 | 
						|
				break;
 | 
						|
			case STRING:
 | 
						|
				printf("const char *");
 | 
						|
				break;
 | 
						|
			case OBJECT:
 | 
						|
				printf("struct wl_%s *", a->object_name);
 | 
						|
				break;
 | 
						|
			case ARRAY:
 | 
						|
				printf("struct wl_array *");
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			printf("%s%s",
 | 
						|
			       a->name,
 | 
						|
			       a->link.next == &m->arg_list ? "" : ", ");
 | 
						|
		}
 | 
						|
 | 
						|
		printf(");\n");
 | 
						|
	}
 | 
						|
 | 
						|
	printf("};\n\n");
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
emit_header(struct protocol *protocol, int server)
 | 
						|
{
 | 
						|
	struct interface *i;
 | 
						|
 | 
						|
	printf("%s\n\n"
 | 
						|
	       "#ifndef WAYLAND_PROTOCOL_H\n"
 | 
						|
	       "#define WAYLAND_PROTOCOL_H\n"
 | 
						|
	       "\n"
 | 
						|
	       "#ifdef  __cplusplus\n"
 | 
						|
	       "extern \"C\" {\n"
 | 
						|
	       "#endif\n"
 | 
						|
	       "\n"
 | 
						|
	       "#include <stdint.h>\n"
 | 
						|
	       "#include \"wayland-util.h\"\n\n"
 | 
						|
	       "struct wl_client;\n\n", copyright);
 | 
						|
 | 
						|
	wl_list_for_each(i, &protocol->interface_list, link)
 | 
						|
		printf("struct wl_%s;\n", i->name);
 | 
						|
	printf("\n");
 | 
						|
 | 
						|
	wl_list_for_each(i, &protocol->interface_list, link) {
 | 
						|
 | 
						|
		if (server) {
 | 
						|
			emit_structs(&i->request_list, i);
 | 
						|
			emit_opcodes(&i->event_list, i);
 | 
						|
		} else {
 | 
						|
			emit_structs(&i->event_list, i);
 | 
						|
			emit_opcodes(&i->request_list, i);
 | 
						|
		}
 | 
						|
 | 
						|
		printf("extern const struct wl_interface "
 | 
						|
		       "wl_%s_interface;\n\n",
 | 
						|
		       i->name);
 | 
						|
	}
 | 
						|
 | 
						|
	printf("#ifdef  __cplusplus\n"
 | 
						|
	       "}\n"
 | 
						|
	       "#endif\n"
 | 
						|
	       "\n"
 | 
						|
	       "#endif\n");
 | 
						|
}
 | 
						|
 | 
						|
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) {
 | 
						|
			switch (a->type) {
 | 
						|
			default:
 | 
						|
			case INT:
 | 
						|
				printf("i");
 | 
						|
				break;
 | 
						|
			case NEW_ID:
 | 
						|
				printf("n");
 | 
						|
				break;
 | 
						|
			case UNSIGNED:
 | 
						|
				printf("u");
 | 
						|
				break;
 | 
						|
			case STRING:
 | 
						|
				printf("s");
 | 
						|
				break;
 | 
						|
			case OBJECT:
 | 
						|
				printf("o");
 | 
						|
				break;
 | 
						|
			case ARRAY:
 | 
						|
				printf("a");
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		printf("\" },\n");
 | 
						|
	}
 | 
						|
 | 
						|
	printf("};\n\n");
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
emit_code(struct protocol *protocol)
 | 
						|
{
 | 
						|
	struct interface *i;
 | 
						|
 | 
						|
	printf("%s\n\n"
 | 
						|
	       "#include <stdlib.h>\n"
 | 
						|
	       "#include <stdint.h>\n"
 | 
						|
	       "#include \"wayland-util.h\"\n\n",
 | 
						|
	       copyright);
 | 
						|
 | 
						|
	wl_list_for_each(i, &protocol->interface_list, link) {
 | 
						|
 | 
						|
		emit_messages(&i->request_list, i, "requests");
 | 
						|
		emit_messages(&i->event_list, i, "events");
 | 
						|
 | 
						|
		printf("WL_EXPORT const struct wl_interface "
 | 
						|
		       "wl_%s_interface = {\n"
 | 
						|
		       "\t\"%s\", %d,\n",
 | 
						|
		       i->name, i->name, i->version);
 | 
						|
 | 
						|
		if (!wl_list_empty(&i->request_list))
 | 
						|
			printf("\tARRAY_LENGTH(%s_requests), %s_requests,\n",
 | 
						|
			       i->name, i->name);
 | 
						|
		else
 | 
						|
			printf("\t0, NULL,\n");
 | 
						|
 | 
						|
		if (!wl_list_empty(&i->event_list))
 | 
						|
			printf("\tARRAY_LENGTH(%s_events), %s_events,\n",
 | 
						|
			       i->name, i->name);
 | 
						|
		else
 | 
						|
			printf("\t0, NULL,\n");
 | 
						|
 | 
						|
		printf("};\n\n");
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
int main(int argc, char *argv[])
 | 
						|
{
 | 
						|
	struct parse_context ctx;
 | 
						|
	struct protocol protocol;
 | 
						|
	XML_Parser parser;
 | 
						|
	int len;
 | 
						|
	void *buf;
 | 
						|
 | 
						|
	if (argc != 2)
 | 
						|
		usage(EXIT_FAILURE);
 | 
						|
 | 
						|
	wl_list_init(&protocol.interface_list);
 | 
						|
	ctx.protocol = &protocol;
 | 
						|
 | 
						|
	parser = XML_ParserCreate(NULL);
 | 
						|
	XML_SetUserData(parser, &ctx);
 | 
						|
	if (parser == NULL) {
 | 
						|
		fprintf(stderr, "failed to create parser\n");
 | 
						|
		exit(EXIT_FAILURE);
 | 
						|
	}
 | 
						|
 | 
						|
	XML_SetElementHandler(parser, start_element, NULL);
 | 
						|
	do {
 | 
						|
		buf = XML_GetBuffer(parser, XML_BUFFER_SIZE);
 | 
						|
		len = fread(buf, 1, XML_BUFFER_SIZE, stdin);
 | 
						|
		if (len < 0) {
 | 
						|
			fprintf(stderr, "fread: %s\n", strerror(errno));
 | 
						|
			exit(EXIT_FAILURE);
 | 
						|
		}
 | 
						|
		XML_ParseBuffer(parser, len, len == 0);
 | 
						|
 | 
						|
	} while (len > 0);
 | 
						|
 | 
						|
	XML_ParserFree(parser);
 | 
						|
 | 
						|
	if (strcmp(argv[1], "client-header") == 0) {
 | 
						|
		emit_header(&protocol, 0);
 | 
						|
	} else if (strcmp(argv[1], "server-header") == 0) {
 | 
						|
		emit_header(&protocol, 1);
 | 
						|
	} else if (strcmp(argv[1], "code") == 0) {
 | 
						|
		emit_code(&protocol);
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 |