diff options
author | Kristian Høgsberg <krh@bitplanet.net> | 2011-08-12 15:09:00 -0400 |
---|---|---|
committer | Kristian Høgsberg <krh@bitplanet.net> | 2011-08-12 16:25:14 -0400 |
commit | 4c260db68c028f05401727d1911582ae8b87cd70 (patch) | |
tree | 89d6d7fdfa7e1eb7ee39194a38d6711cde66e6bc /src | |
parent | 3733157d55df862ad5a89d1911a0500af67e7e08 (diff) |
Rename source subdir from wayland to src
Diffstat (limited to 'src')
-rw-r--r-- | src/.gitignore | 4 | ||||
-rw-r--r-- | src/Makefile.am | 63 | ||||
-rw-r--r-- | src/connection.c | 749 | ||||
-rw-r--r-- | src/connection.h | 68 | ||||
-rw-r--r-- | src/event-loop.c | 465 | ||||
-rw-r--r-- | src/scanner.c | 868 | ||||
-rw-r--r-- | src/scanner.mk | 8 | ||||
-rw-r--r-- | src/wayland-client.c | 604 | ||||
-rw-r--r-- | src/wayland-client.h | 90 | ||||
-rw-r--r-- | src/wayland-client.pc.in | 10 | ||||
-rw-r--r-- | src/wayland-egl.h | 68 | ||||
-rw-r--r-- | src/wayland-hash.c | 296 | ||||
-rw-r--r-- | src/wayland-server.c | 933 | ||||
-rw-r--r-- | src/wayland-server.h | 307 | ||||
-rw-r--r-- | src/wayland-server.pc.in | 10 | ||||
-rw-r--r-- | src/wayland-shm.c | 223 | ||||
-rw-r--r-- | src/wayland-util.c | 123 | ||||
-rw-r--r-- | src/wayland-util.h | 152 |
18 files changed, 5041 insertions, 0 deletions
diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..1813f89 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,4 @@ +wayland-scanner +wayland-client-protocol.h +wayland-protocol.c +wayland-server-protocol.h diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..156ca2e --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,63 @@ +lib_LTLIBRARIES = libwayland-server.la libwayland-client.la +noinst_LTLIBRARIES = libwayland-util.la + +include_HEADERS = \ + wayland-util.h \ + wayland-server-protocol.h \ + wayland-server.h \ + wayland-client-protocol.h \ + wayland-client.h \ + wayland-egl.h + +libwayland_util_la_SOURCES = \ + connection.c \ + connection.h \ + wayland-util.c \ + wayland-util.h \ + wayland-hash.c + +libwayland_server_la_LIBADD = $(FFI_LIBS) libwayland-util.la -lrt +libwayland_server_la_SOURCES = \ + wayland-protocol.c \ + wayland-server.c \ + wayland-shm.c \ + event-loop.c + +libwayland_client_la_LIBADD = $(FFI_LIBS) libwayland-util.la -lrt +libwayland_client_la_SOURCES = \ + wayland-protocol.c \ + wayland-client.c + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = wayland-client.pc wayland-server.pc + +AM_CPPFLAGS = $(FFI_CFLAGS) +AM_CFLAGS = $(GCC_CFLAGS) + +protocoldir = $(top_srcdir)/protocol + +if ENABLE_SCANNER +wayland_scanner = $(top_builddir)/src/wayland-scanner +else +wayland_scanner = wayland-scanner +endif + +include $(top_srcdir)/src/scanner.mk + +if ENABLE_SCANNER +bin_PROGRAMS = wayland-scanner + +wayland_scanner_SOURCES = \ + scanner.c + +wayland_scanner_LDADD = $(EXPAT_LIBS) libwayland-util.la + +$(BUILT_SOURCES) : wayland-scanner +endif + +BUILT_SOURCES = \ + wayland-server-protocol.h \ + wayland-client-protocol.h \ + wayland-protocol.c + +CLEANFILES = $(BUILT_SOURCES) diff --git a/src/connection.c b/src/connection.c new file mode 100644 index 0000000..467a2d0 --- /dev/null +++ b/src/connection.c @@ -0,0 +1,749 @@ +/* + * Copyright © 2008 Kristian Høgsberg + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#define _GNU_SOURCE + +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <sys/uio.h> +#include <ffi.h> +#include <assert.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <time.h> + +#include "wayland-util.h" +#include "connection.h" + +#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) + +struct wl_buffer { + char data[4096]; + int head, tail; +}; + +#define MASK(i) ((i) & 4095) + +struct wl_closure { + int count; + const struct wl_message *message; + ffi_type *types[20]; + ffi_cif cif; + void *args[20]; + uint32_t buffer[64]; + uint32_t *start; +}; + +struct wl_connection { + struct wl_buffer in, out; + struct wl_buffer fds_in, fds_out; + int fd; + void *data; + wl_connection_update_func_t update; + struct wl_closure receive_closure, send_closure; +}; + +union wl_value { + uint32_t uint32; + char *string; + struct wl_object *object; + uint32_t new_id; + struct wl_array *array; +}; + +static void +wl_buffer_put(struct wl_buffer *b, const void *data, size_t count) +{ + int head, size; + + head = MASK(b->head); + if (head + count <= sizeof b->data) { + memcpy(b->data + head, data, count); + } else { + size = sizeof b->data - head; + memcpy(b->data + head, data, size); + memcpy(b->data, (const char *) data + size, count - size); + } + + b->head += count; +} + +static void +wl_buffer_put_iov(struct wl_buffer *b, struct iovec *iov, int *count) +{ + int head, tail; + + head = MASK(b->head); + tail = MASK(b->tail); + if (head < tail) { + iov[0].iov_base = b->data + head; + iov[0].iov_len = tail - head; + *count = 1; + } else if (tail == 0) { + iov[0].iov_base = b->data + head; + iov[0].iov_len = sizeof b->data - head; + *count = 1; + } else { + iov[0].iov_base = b->data + head; + iov[0].iov_len = sizeof b->data - head; + iov[1].iov_base = b->data; + iov[1].iov_len = tail; + *count = 2; + } +} + +static void +wl_buffer_get_iov(struct wl_buffer *b, struct iovec *iov, int *count) +{ + int head, tail; + + head = MASK(b->head); + tail = MASK(b->tail); + if (tail < head) { + iov[0].iov_base = b->data + tail; + iov[0].iov_len = head - tail; + *count = 1; + } else if (head == 0) { + iov[0].iov_base = b->data + tail; + iov[0].iov_len = sizeof b->data - tail; + *count = 1; + } else { + iov[0].iov_base = b->data + tail; + iov[0].iov_len = sizeof b->data - tail; + iov[1].iov_base = b->data; + iov[1].iov_len = head; + *count = 2; + } +} + +static void +wl_buffer_copy(struct wl_buffer *b, void *data, size_t count) +{ + int tail, size; + + tail = MASK(b->tail); + if (tail + count <= sizeof b->data) { + memcpy(data, b->data + tail, count); + } else { + size = sizeof b->data - tail; + memcpy(data, b->data + tail, size); + memcpy((char *) data + size, b->data, count - size); + } +} + +struct wl_connection * +wl_connection_create(int fd, + wl_connection_update_func_t update, + void *data) +{ + struct wl_connection *connection; + + connection = malloc(sizeof *connection); + if (connection == NULL) + return NULL; + memset(connection, 0, sizeof *connection); + connection->fd = fd; + connection->update = update; + connection->data = data; + + connection->update(connection, + WL_CONNECTION_READABLE, + connection->data); + + return connection; +} + +void +wl_connection_destroy(struct wl_connection *connection) +{ + close(connection->fd); + free(connection); +} + +void +wl_connection_copy(struct wl_connection *connection, void *data, size_t size) +{ + wl_buffer_copy(&connection->in, data, size); +} + +void +wl_connection_consume(struct wl_connection *connection, size_t size) +{ + connection->in.tail += size; +} + +static void +build_cmsg(struct wl_buffer *buffer, char *data, int *clen) +{ + struct cmsghdr *cmsg; + size_t size; + + size = buffer->head - buffer->tail; + if (size > 0) { + cmsg = (struct cmsghdr *) data; + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(size); + wl_buffer_copy(buffer, CMSG_DATA(cmsg), size); + *clen = cmsg->cmsg_len; + } else { + *clen = 0; + } +} + +static void +close_fds(struct wl_buffer *buffer) +{ + int fds[32], i, count; + size_t size; + + size = buffer->head - buffer->tail; + if (size == 0) + return; + + wl_buffer_copy(buffer, fds, size); + count = size / sizeof fds[0]; + for (i = 0; i < count; i++) + close(fds[i]); + buffer->tail += size; +} + +static void +decode_cmsg(struct wl_buffer *buffer, struct msghdr *msg) +{ + struct cmsghdr *cmsg; + size_t size; + + for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS) { + size = cmsg->cmsg_len - CMSG_LEN(0); + wl_buffer_put(buffer, CMSG_DATA(cmsg), size); + } + } +} + +int +wl_connection_data(struct wl_connection *connection, uint32_t mask) +{ + struct iovec iov[2]; + struct msghdr msg; + char cmsg[128]; + int len, count, clen; + + if (mask & WL_CONNECTION_WRITABLE) { + wl_buffer_get_iov(&connection->out, iov, &count); + + build_cmsg(&connection->fds_out, cmsg, &clen); + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = count; + msg.msg_control = cmsg; + msg.msg_controllen = clen; + msg.msg_flags = 0; + + do { + len = sendmsg(connection->fd, &msg, MSG_NOSIGNAL); + } while (len < 0 && errno == EINTR); + + if (len == -1 && errno == EPIPE) { + return -1; + } else if (len < 0) { + fprintf(stderr, + "write error for connection %p, fd %d: %m\n", + connection, connection->fd); + return -1; + } + + close_fds(&connection->fds_out); + + connection->out.tail += len; + if (connection->out.tail == connection->out.head) + connection->update(connection, + WL_CONNECTION_READABLE, + connection->data); + } + + if (mask & WL_CONNECTION_READABLE) { + wl_buffer_put_iov(&connection->in, iov, &count); + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = count; + msg.msg_control = cmsg; + msg.msg_controllen = sizeof cmsg; + msg.msg_flags = 0; + + do { + len = recvmsg(connection->fd, &msg, MSG_CMSG_CLOEXEC); + } while (len < 0 && errno == EINTR); + + if (len < 0) { + fprintf(stderr, + "read error from connection %p: %m (%d)\n", + connection, errno); + return -1; + } else if (len == 0) { + /* FIXME: Handle this better? */ + return -1; + } + + decode_cmsg(&connection->fds_in, &msg); + + connection->in.head += len; + } + + return connection->in.head - connection->in.tail; +} + +void +wl_connection_write(struct wl_connection *connection, + const void *data, size_t count) +{ + if (connection->out.head - connection->out.tail + + count > ARRAY_LENGTH(connection->out.data)) + wl_connection_data(connection, WL_CONNECTION_WRITABLE); + + wl_buffer_put(&connection->out, data, count); + + if (connection->out.head - connection->out.tail == count) + connection->update(connection, + WL_CONNECTION_READABLE | + WL_CONNECTION_WRITABLE, + connection->data); +} + +static int +wl_message_size_extra(const struct wl_message *message) +{ + int i, extra; + + for (i = 0, extra = 0; message->signature[i]; i++) { + + switch (message->signature[i]) { + case 's': + case 'o': + extra += sizeof (void *); + break; + case 'a': + extra += sizeof (void *) + sizeof (struct wl_array); + break; + case 'h': + extra += sizeof (int); + break; + default: + break; + } + } + + return extra; +} + +struct wl_closure * +wl_connection_vmarshal(struct wl_connection *connection, + struct wl_object *sender, + uint32_t opcode, va_list ap, + const struct wl_message *message) +{ + struct wl_closure *closure = &connection->send_closure; + struct wl_object **objectp, *object; + uint32_t length, *p, *start, size; + int dup_fd; + struct wl_array **arrayp, *array; + const char **sp, *s; + char *extra; + int i, count, fd, extra_size, *fd_ptr; + + extra_size = wl_message_size_extra(message); + count = strlen(message->signature) + 2; + extra = (char *) closure->buffer; + start = &closure->buffer[DIV_ROUNDUP(extra_size, sizeof *p)]; + p = &start[2]; + for (i = 2; i < count; i++) { + switch (message->signature[i - 2]) { + case 'u': + closure->types[i] = &ffi_type_uint32; + closure->args[i] = p; + *p++ = va_arg(ap, uint32_t); + break; + case 'i': + closure->types[i] = &ffi_type_sint32; + closure->args[i] = p; + *p++ = va_arg(ap, int32_t); + break; + case 's': + closure->types[i] = &ffi_type_pointer; + closure->args[i] = extra; + sp = (const char **) extra; + extra += sizeof *sp; + + s = va_arg(ap, const char *); + length = s ? strlen(s) + 1: 0; + *p++ = length; + + if (length > 0) + *sp = (const char *) p; + else + *sp = NULL; + + memcpy(p, s, length); + p += DIV_ROUNDUP(length, sizeof *p); + break; + case 'o': + closure->types[i] = &ffi_type_pointer; + closure->args[i] = extra; + objectp = (struct wl_object **) extra; + extra += sizeof *objectp; + + object = va_arg(ap, struct wl_object *); + *objectp = object; + *p++ = object ? object->id : 0; + break; + + case 'n': + closure->types[i] = &ffi_type_uint32; + closure->args[i] = p; + object = va_arg(ap, struct wl_object *); + *p++ = object->id; + break; + + case 'a': + closure->types[i] = &ffi_type_pointer; + closure->args[i] = extra; + arrayp = (struct wl_array **) extra; + extra += sizeof *arrayp; + + *arrayp = (struct wl_array *) extra; + extra += sizeof **arrayp; + + array = va_arg(ap, struct wl_array *); + if (array == NULL || array->size == 0) { + *p++ = 0; + break; + } + *p++ = array->size; + memcpy(p, array->data, array->size); + + (*arrayp)->size = array->size; + (*arrayp)->alloc = array->alloc; + (*arrayp)->data = p; + + p += DIV_ROUNDUP(array->size, sizeof *p); + break; + + case 'h': + closure->types[i] = &ffi_type_sint; + closure->args[i] = extra; + fd_ptr = (int *) extra; + extra += sizeof *fd_ptr; + + fd = va_arg(ap, int); + dup_fd = fcntl(fd, F_DUPFD_CLOEXEC, 0); + if (dup_fd < 0) { + fprintf(stderr, "dup failed: %m"); + abort(); + } + *fd_ptr = dup_fd; + wl_buffer_put(&connection->fds_out, + &dup_fd, sizeof dup_fd); + break; + default: + assert(0); + break; + } + } + + size = (p - start) * sizeof *p; + start[0] = sender->id; + start[1] = opcode | (size << 16); + + closure->start = start; + closure->message = message; + closure->count = count; + + return closure; +} + +struct wl_closure * +wl_connection_demarshal(struct wl_connection *connection, + uint32_t size, + struct wl_hash_table *objects, + const struct wl_message *message) +{ + uint32_t *p, *next, *end, length; + int *fd; + char *extra, **s; + int i, count, extra_space; + struct wl_object **object; + struct wl_array **array; + struct wl_closure *closure = &connection->receive_closure; + + count = strlen(message->signature) + 2; + if (count > ARRAY_LENGTH(closure->types)) { + printf("too many args (%d)\n", count); + errno = EINVAL; + wl_connection_consume(connection, size); + return NULL; + } + + extra_space = wl_message_size_extra(message); + if (sizeof closure->buffer < size + extra_space) { + printf("request too big, should malloc tmp buffer here\n"); + errno = ENOMEM; + wl_connection_consume(connection, size); + return NULL; + } + + closure->message = message; + closure->types[0] = &ffi_type_pointer; + closure->types[1] = &ffi_type_pointer; + + wl_connection_copy(connection, closure->buffer, size); + p = &closure->buffer[2]; + end = (uint32_t *) ((char *) p + size); + extra = (char *) end; + for (i = 2; i < count; i++) { + if (p + 1 > end) { + printf("message too short, " + "object (%d), message %s(%s)\n", + *p, message->name, message->signature); + errno = EINVAL; + goto err; + } + + switch (message->signature[i - 2]) { + case 'u': + closure->types[i] = &ffi_type_uint32; + closure->args[i] = p++; + break; + case 'i': + closure->types[i] = &ffi_type_sint32; + closure->args[i] = p++; + break; + case 's': + closure->types[i] = &ffi_type_pointer; + length = *p++; + + next = p + DIV_ROUNDUP(length, sizeof *p); + if (next > end) { + printf("message too short, " + "object (%d), message %s(%s)\n", + *p, message->name, message->signature); + errno = EINVAL; + goto err; + } + + s = (char **) extra; + extra += sizeof *s; + closure->args[i] = s; + + if (length == 0) { + *s = NULL; + } else { + *s = (char *) p; + } + + if (length > 0 && (*s)[length - 1] != '\0') { + printf("string not nul-terminated, " + "message %s(%s)\n", + message->name, message->signature); + errno = EINVAL; + goto err; + } + p = next; + break; + case 'o': + closure->types[i] = &ffi_type_pointer; + object = (struct wl_object **) extra; + extra += sizeof *object; + closure->args[i] = object; + + *object = wl_hash_table_lookup(objects, *p); + if (*object == NULL && *p != 0) { + printf("unknown object (%d), message %s(%s)\n", + *p, message->name, message->signature); + errno = EINVAL; + goto err; + } + + p++; + break; + case 'n': + closure->types[i] = &ffi_type_uint32; + closure->args[i] = p; + object = wl_hash_table_lookup(objects, *p); + if (object != NULL) { + printf("not a new object (%d), " + "message %s(%s)\n", + *p, message->name, message->signature); + errno = EINVAL; + goto err; + } + p++; + break; + case 'a': + closure->types[i] = &ffi_type_pointer; + length = *p++; + + next = p + DIV_ROUNDUP(length, sizeof *p); + if (next > end) { + printf("message too short, " + "object (%d), message %s(%s)\n", + *p, message->name, message->signature); + errno = EINVAL; + goto err; + } + + array = (struct wl_array **) extra; + extra += sizeof *array; + closure->args[i] = array; + + *array = (struct wl_array *) extra; + extra += sizeof **array; + + (*array)->size = length; + (*array)->alloc = 0; + (*array)->data = p; + p = next; + break; + case 'h': + closure->types[i] = &ffi_type_sint; + + fd = (int *) extra; + extra += sizeof *fd; + closure->args[i] = fd; + + wl_buffer_copy(&connection->fds_in, fd, sizeof *fd); + connection->fds_in.tail += sizeof *fd; + break; + default: + printf("unknown type\n"); + assert(0); + break; + } + } + + closure->count = i; + ffi_prep_cif(&closure->cif, FFI_DEFAULT_ABI, + closure->count, &ffi_type_uint32, closure->types); + + wl_connection_consume(connection, size); + + return closure; + + err: + closure->count = i; + wl_closure_destroy(closure); + wl_connection_consume(connection, size); + + return NULL; +} + +void +wl_closure_invoke(struct wl_closure *closure, + struct wl_object *target, void (*func)(void), void *data) +{ + int result; + + closure->args[0] = &data; + closure->args[1] = ⌖ + + ffi_call(&closure->cif, func, &result, closure->args); +} + +void +wl_closure_send(struct wl_closure *closure, struct wl_connection *connection) +{ + uint32_t size; + + size = closure->start[1] >> 16; + wl_connection_write(connection, closure->start, size); +} + +void +wl_closure_print(struct wl_closure *closure, struct wl_object *target, int send) +{ + union wl_value *value; + int i; + struct timespec tp; + unsigned int time; + + clock_gettime(CLOCK_REALTIME, &tp); + time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000); + + fprintf(stderr, "[%10.3f] %s%s@%d.%s(", + time / 1000.0, + send ? " -> " : "", + target->interface->name, target->id, + closure->message->name); + + for (i = 2; i < closure->count; i++) { + if (i > 2) + fprintf(stderr, ", "); + + value = closure->args[i]; + switch (closure->message->signature[i - 2]) { + case 'u': + fprintf(stderr, "%u", value->uint32); + break; + case 'i': + fprintf(stderr, "%d", value->uint32); + break; + case 's': + fprintf(stderr, "\"%s\"", value->string); + break; + case 'o': + if (value->object) + fprintf(stderr, "%s@%u", + value->object->interface->name, + value->object->id); + else + fprintf(stderr, "nil"); + break; + case 'n': + fprintf(stderr, "new id %u", value->uint32); + break; + case 'a': + fprintf(stderr, "array"); + break; + case 'h': + fprintf(stderr, "fd %d", value->uint32); + break; + } + } + + fprintf(stderr, ")\n"); +} + +void +wl_closure_destroy(struct wl_closure *closure) +{ +} diff --git a/src/connection.h b/src/connection.h new file mode 100644 index 0000000..5f4588b --- /dev/null +++ b/src/connection.h @@ -0,0 +1,68 @@ +/* + * Copyright © 2008 Kristian Høgsberg + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef _CONNECTION_H_ +#define _CONNECTION_H_ + +#include <stdarg.h> +#include "wayland-util.h" + +struct wl_connection; +struct wl_closure; + +#define WL_CONNECTION_READABLE 0x01 +#define WL_CONNECTION_WRITABLE 0x02 + +typedef int (*wl_connection_update_func_t)(struct wl_connection *connection, + uint32_t mask, void *data); + +struct wl_connection *wl_connection_create(int fd, + wl_connection_update_func_t update, + void *data); +void wl_connection_destroy(struct wl_connection *connection); +void wl_connection_copy(struct wl_connection *connection, void *data, size_t size); +void wl_connection_consume(struct wl_connection *connection, size_t size); +int wl_connection_data(struct wl_connection *connection, uint32_t mask); +void wl_connection_write(struct wl_connection *connection, const void *data, size_t count); + +struct wl_closure * +wl_connection_vmarshal(struct wl_connection *connection, + struct wl_object *sender, + uint32_t opcode, va_list ap, + const struct wl_message *message); + +struct wl_closure * +wl_connection_demarshal(struct wl_connection *connection, + uint32_t size, + struct wl_hash_table *objects, + const struct wl_message *message); +void +wl_closure_invoke(struct wl_closure *closure, + struct wl_object *target, void (*func)(void), void *data); +void +wl_closure_send(struct wl_closure *closure, struct wl_connection *connection); +void +wl_closure_print(struct wl_closure *closure, struct wl_object *target, int send); +void +wl_closure_destroy(struct wl_closure *closure); + +#endif diff --git a/src/event-loop.c b/src/event-loop.c new file mode 100644 index 0000000..615ec1d --- /dev/null +++ b/src/event-loop.c @@ -0,0 +1,465 @@ +/* + * Copyright © 2008 Kristian Høgsberg + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include <stddef.h> +#include <stdio.h> +#include <errno.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/epoll.h> +#include <sys/signalfd.h> +#include <sys/timerfd.h> +#include <unistd.h> +#include <assert.h> +#include "wayland-server.h" + +struct wl_event_loop { + int epoll_fd; + struct wl_list check_list; + struct wl_list idle_list; +}; + +struct wl_event_source_interface { + int (*dispatch)(struct wl_event_source *source, + struct epoll_event *ep); + int (*remove)(struct wl_event_source *source); +}; + +struct wl_event_source { + struct wl_event_source_interface *interface; + struct wl_event_loop *loop; + struct wl_list link; + void *data; +}; + +struct wl_event_source_fd { + struct wl_event_source base; + int fd; + wl_event_loop_fd_func_t func; +}; + +static int +wl_event_source_fd_dispatch(struct wl_event_source *source, + struct epoll_event *ep) +{ + struct wl_event_source_fd *fd_source = (struct wl_event_source_fd *) source; + uint32_t mask; + + mask = 0; + if (ep->events & EPOLLIN) + mask |= WL_EVENT_READABLE; + if (ep->events & EPOLLOUT) + mask |= WL_EVENT_WRITEABLE; + + return fd_source->func(fd_source->fd, mask, fd_source->base.data); +} + +static int +wl_event_source_fd_remove(struct wl_event_source *source) +{ + struct wl_event_source_fd *fd_source = + (struct wl_event_source_fd *) source; + struct wl_event_loop *loop = source->loop; + int fd; + + fd = fd_source->fd; + free(source); + + return epoll_ctl(loop->epoll_fd, EPOLL_CTL_DEL, fd, NULL); +} + +struct wl_event_source_interface fd_source_interface = { + wl_event_source_fd_dispatch, + wl_event_source_fd_remove +}; + +WL_EXPORT struct wl_event_source * +wl_event_loop_add_fd(struct wl_event_loop *loop, + int fd, uint32_t mask, + wl_event_loop_fd_func_t func, + void *data) +{ + struct wl_event_source_fd *source; + struct epoll_event ep; + + source = malloc(sizeof *source); + if (source == NULL) + return NULL; + + source->base.interface = &fd_source_interface; + source->base.loop = loop; + wl_list_init(&source->base.link); + source->fd = fd; + source->func = func; + source->base.data = data; + + memset(&ep, 0, sizeof ep); + if (mask & WL_EVENT_READABLE) + ep.events |= EPOLLIN; + if (mask & WL_EVENT_WRITEABLE) + ep.events |= EPOLLOUT; + ep.data.ptr = source; + + if (epoll_ctl(loop->epoll_fd, EPOLL_CTL_ADD, fd, &ep) < 0) { + free(source); + return NULL; + } + + return &source->base; +} + +WL_EXPORT int +wl_event_source_fd_update(struct wl_event_source *source, uint32_t mask) +{ + struct wl_event_source_fd *fd_source = + (struct wl_event_source_fd *) source; + struct wl_event_loop *loop = source->loop; + struct epoll_event ep; + + memset(&ep, 0, sizeof ep); + if (mask & WL_EVENT_READABLE) + ep.events |= EPOLLIN; + if (mask & WL_EVENT_WRITEABLE) + ep.events |= EPOLLOUT; + ep.data.ptr = source; + + return epoll_ctl(loop->epoll_fd, + EPOLL_CTL_MOD, fd_source->fd, &ep); +} + +struct wl_event_source_timer { + struct wl_event_source base; + int fd; + wl_event_loop_timer_func_t func; +}; + +static int +wl_event_source_timer_dispatch(struct wl_event_source *source, + struct epoll_event *ep) +{ + struct wl_event_source_timer *timer_source = + (struct wl_event_source_timer *) source; + uint64_t expires; + + read(timer_source->fd, &expires, sizeof expires); + + return timer_source->func(timer_source->base.data); +} + +static int +wl_event_source_timer_remove(struct wl_event_source *source) +{ + struct wl_event_source_timer *timer_source = + (struct wl_event_source_timer *) source; + + close(timer_source->fd); + free(source); + return 0; +} + +struct wl_event_source_interface timer_source_interface = { + wl_event_source_timer_dispatch, + wl_event_source_timer_remove +}; + +WL_EXPORT struct wl_event_source * +wl_event_loop_add_timer(struct wl_event_loop *loop, + wl_event_loop_timer_func_t func, + void *data) +{ + struct wl_event_source_timer *source; + struct epoll_event ep; + + source = malloc(sizeof *source); + if (source == NULL) + return NULL; + + source->base.interface = &timer_source_interface; + source->base.loop = loop; + wl_list_init(&source->base.link); + + source->fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); + if (source->fd < 0) { + fprintf(stderr, "could not create timerfd\n: %m"); + free(source); + return NULL; + } + + source->func = func; + source->base.data = data; + + memset(&ep, 0, sizeof ep); + ep.events = EPOLLIN; + ep.data.ptr = source; + + if (epoll_ctl(loop->epoll_fd, EPOLL_CTL_ADD, source->fd, &ep) < 0) { + close(source->fd); + free(source); + return NULL; + } + + return &source->base; +} + +WL_EXPORT int +wl_event_source_timer_update(struct wl_event_source *source, int ms_delay) +{ + struct wl_event_source_timer *timer_source = + (struct wl_event_source_timer *) source; + struct itimerspec its; + + its.it_interval.tv_sec = 0; + its.it_interval.tv_nsec = 0; + its.it_value.tv_sec = ms_delay / 1000; + its.it_value.tv_nsec = (ms_delay % 1000) * 1000 * 1000; + if (timerfd_settime(timer_source->fd, 0, &its, NULL) < 0) { + fprintf(stderr, "could not set timerfd\n: %m"); + return -1; + } + + return 0; +} + +struct wl_event_source_signal { + struct wl_event_source base; + int fd; + int signal_number; + wl_event_loop_signal_func_t func; +}; + +static int +wl_event_source_signal_dispatch(struct wl_event_source *source, + struct epoll_event *ep) +{ + struct wl_event_source_signal *signal_source = + (struct wl_event_source_signal *) source; + struct signalfd_siginfo signal_info; + + read(signal_source->fd, &signal_info, sizeof signal_info); + + return signal_source->func(signal_source->signal_number, + signal_source->base.data); +} + +static int +wl_event_source_signal_remove(struct wl_event_source *source) +{ + struct wl_event_source_signal *signal_source = + (struct wl_event_source_signal *) source; + + close(signal_source->fd); + free(source); + return 0; +} + +struct wl_event_source_interface signal_source_interface = { + wl_event_source_signal_dispatch, + wl_event_source_signal_remove +}; + +WL_EXPORT struct wl_event_source * +wl_event_loop_add_signal(struct wl_event_loop *loop, + int signal_number, + wl_event_loop_signal_func_t func, + void *data) +{ + struct wl_event_source_signal *source; + struct epoll_event ep; + sigset_t mask; + + source = malloc(sizeof *source); + if (source == NULL) + return NULL; + + source->base.interface = &signal_source_interface; + source->base.loop = loop; + wl_list_init(&source->base.link); + source->signal_number = signal_number; + + sigemptyset(&mask); + sigaddset(&mask, signal_number); + source->fd = signalfd(-1, &mask, SFD_CLOEXEC); + if (source->fd < 0) { + fprintf(stderr, "could not create fd to watch signal\n: %m"); + free(source); + return NULL; + } + sigprocmask(SIG_BLOCK, &mask, NULL); + + source->func = func; + source->base.data = data; + + memset(&ep, 0, sizeof ep); + ep.events = EPOLLIN; + ep.data.ptr = source; + + if (epoll_ctl(loop->epoll_fd, EPOLL_CTL_ADD, source->fd, &ep) < 0) { + close(source->fd); + free(source); + return NULL; + } + + return &source->base; +} + +struct wl_event_source_idle { + struct wl_event_source base; + wl_event_loop_idle_func_t func; +}; + +static int +wl_event_source_idle_remove(struct wl_event_source *source) +{ + free(source); + + return 0; +} + +struct wl_event_source_interface idle_source_interface = { + NULL, + wl_event_source_idle_remove +}; + +WL_EXPORT struct wl_event_source * +wl_event_loop_add_idle(struct wl_event_loop *loop, + wl_event_loop_idle_func_t func, + void *data) +{ + struct wl_event_source_idle *source; + + source = malloc(sizeof *source); + if (source == NULL) + return NULL; + + source->base.interface = &idle_source_interface; + source->base.loop = loop; + + source->func = func; + source->base.data = data; + + wl_list_insert(loop->idle_list.prev, &source->base.link); + + return &source->base; +} + +WL_EXPORT void +wl_event_source_check(struct wl_event_source *source) +{ + wl_list_insert(source->loop->check_list.prev, &source->link); +} + +WL_EXPORT int +wl_event_source_remove(struct wl_event_source *source) +{ + if (!wl_list_empty(&source->link)) + wl_list_remove(&source->link); + + source->interface->remove(source); + + return 0; +} + +WL_EXPORT struct wl_event_loop * +wl_event_loop_create(void) +{ + struct wl_event_loop *loop; + + loop = malloc(sizeof *loop); + if (loop == NULL) + return NULL; + + loop->epoll_fd = epoll_create1(EPOLL_CLOEXEC); + if (loop->epoll_fd < 0) { + free(loop); + return NULL; + } + wl_list_init(&loop->check_list); + wl_list_init(&loop->idle_list); + + return loop; +} + +WL_EXPORT void +wl_event_loop_destroy(struct wl_event_loop *loop) +{ + close(loop->epoll_fd); + free(loop); +} + +static int +post_dispatch_check(struct wl_event_loop *loop) +{ + struct epoll_event ep; + struct wl_event_source *source, *next; + int n; + + ep.events = 0; + n = 0; + wl_list_for_each_safe(source, next, &loop->check_list, link) + n += source->interface->dispatch(source, &ep); + + return n; +} + +static void +dispatch_idle_sources(struct wl_event_loop *loop) +{ + struct wl_event_source_idle *source, *next; + + wl_list_for_each_safe(source, next, &loop->idle_list, base.link) { + source->func(source->base.data); + wl_event_source_remove(&source->base); + } +} + +WL_EXPORT int +wl_event_loop_dispatch(struct wl_event_loop *loop, int timeout) +{ + struct epoll_event ep[32]; + struct wl_event_source *source; + int i, count, n; + + dispatch_idle_sources(loop); + + count = epoll_wait(loop->epoll_fd, ep, ARRAY_LENGTH(ep), timeout); + if (count < 0) + return -1; + n = 0; + for (i = 0; i < count; i++) { + source = ep[i].data.ptr; + n += source->interface->dispatch(source, &ep[i]); + } + + while (n > 0) + n = post_dispatch_check(loop); + + return 0; +} + +WL_EXPORT int +wl_event_loop_get_fd(struct wl_event_loop *loop) +{ + return loop->epoll_fd; +} diff --git a/src/scanner.c b/src/scanner.c new file mode 100644 index 0000000..9584046 --- /dev/null +++ b/src/scanner.c @@ -0,0 +1,868 @@ +/* + * Copyright © 2008-2011 Kristian Høgsberg + * Copyright © 2011 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <expat.h> + +#include "wayland-util.h" + +static int +usage(int ret) +{ + fprintf(stderr, "usage: ./scanner [client-header|server-header|code]\n"); + exit(ret); +} + +#define XML_BUFFER_SIZE 4096 + +struct protocol { + char *name; + char *uppercase_name; + struct wl_list interface_list; + int type_index; + int null_run_length; + char *copyright; +}; + +struct interface { + char *name; + char *uppercase_name; + int version; + struct wl_list request_list; + struct wl_list event_list; + struct wl_list enumeration_list; + struct wl_list link; +}; + +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; +}; + +enum arg_type { + NEW_ID, + INT, + UNSIGNED, + STRING, + OBJECT, + ARRAY, + FD +}; + +struct arg { + char *name; + enum arg_type type; + char *interface_name; + struct wl_list link; +}; + +struct enumeration { + char *name; + char *uppercase_name; + struct wl_list entry_list; + struct wl_list link; +}; + +struct entry { + char *name; + char *uppercase_name; + char *value; + struct wl_list link; +}; + +struct parse_context { + const char *filename; + XML_Parser parser; + struct protocol *protocol; + struct interface *interface; + struct message *message; + struct enumeration *enumeration; + char character_data[8192]; + int character_data_length; +}; + +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 +fail(struct parse_context *ctx, const char *msg) +{ + fprintf(stderr, "%s:%ld: %s\n", + ctx->filename, XML_GetCurrentLineNumber(ctx->parser), msg); + exit(EXIT_FAILURE); +} + +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; + struct enumeration *enumeration; + struct entry *entry; + const char *name, *type, *interface_name, *value; + int i, version; + + name = NULL; + type = NULL; + version = 0; + interface_name = NULL; + value = NULL; + 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(atts[i], "value") == 0) + value = atts[i + 1]; + if (strcmp(atts[i], "interface") == 0) + interface_name = atts[i + 1]; + } + + ctx->character_data_length = 0; + if (strcmp(element_name, "protocol") == 0) { + if (name == NULL) + fail(ctx, "no protocol name given"); + + ctx->protocol->name = strdup(name); + ctx->protocol->uppercase_name = uppercase_dup(name); + } else if (strcmp(element_name, "copyright") == 0) { + + } else if (strcmp(element_name, "interface") == 0) { + if (name == NULL) + fail(ctx, "no interface name given"); + + if (version == 0) + fail(ctx, "no interface version given"); + + 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_init(&interface->enumeration_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) + fail(ctx, "no request name given"); + + message = malloc(sizeof *message); + message->name = strdup(name); + message->uppercase_name = uppercase_dup(name); + wl_list_init(&message->arg_list); + message->arg_count = 0; + + 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); + + if (type != NULL && strcmp(type, "destructor") == 0) + message->destructor = 1; + else + message->destructor = 0; + + if (strcmp(name, "destroy") == 0 && !message->destructor) + fail(ctx, "destroy request should be destructor type"); + + ctx->message = message; + } else if (strcmp(element_name, "arg") == 0) { + arg = malloc(sizeof *arg); + arg->name = strdup(name); + + 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 if (strcmp(type, "fd") == 0) + arg->type = FD; + else if (strcmp(type, "new_id") == 0) { + if (interface_name == NULL) + fail(ctx, "no interface name given"); + arg->type = NEW_ID; + arg->interface_name = strdup(interface_name); + } else if (strcmp(type, "object") == 0) { + if (interface_name == NULL) + fail(ctx, "no interface name given"); + arg->type = OBJECT; + arg->interface_name = strdup(interface_name); + } else { + fail(ctx, "unknown type"); + } + + wl_list_insert(ctx->message->arg_list.prev, &arg->link); + ctx->message->arg_count++; + } else if (strcmp(element_name, "enum") == 0) { + if (name == NULL) + fail(ctx, "no enum name given"); + + enumeration = malloc(sizeof *enumeration); + enumeration->name = strdup(name); + enumeration->uppercase_name = uppercase_dup(name); + wl_list_init(&enumeration->entry_list); + + wl_list_insert(ctx->interface->enumeration_list.prev, + &enumeration->link); + + ctx->enumeration = enumeration; + } else if (strcmp(element_name, "entry") == 0) { + entry = malloc(sizeof *entry); + entry->name = strdup(name); + entry->uppercase_name = uppercase_dup(name); + entry->value = strdup(value); + wl_list_insert(ctx->enumeration->entry_list.prev, + &entry->link); + } +} + +static void +end_element(void *data, const XML_Char *name) +{ + struct parse_context *ctx = data; + + if (strcmp(name, "copyright") == 0) { + ctx->protocol->copyright = + strndup(ctx->character_data, + ctx->character_data_length); + } +} + +static void +character_data(void *data, const XML_Char *s, int len) +{ + struct parse_context *ctx = data; + + if (ctx->character_data_length + len > sizeof (ctx->character_data)) { + fprintf(stderr, "too much character data"); + exit(EXIT_FAILURE); + } + + memcpy(ctx->character_data + ctx->character_data_length, s, len); + ctx->character_data_length += len; +} + +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"); +} + +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 STRING: + printf("const char *"); + break; + case OBJECT: + printf("struct %s *", a->interface_name); + break; + case ARRAY: + printf("struct wl_array *"); + break; + } +} + +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 constructor for the display object */ + if (strcmp(interface->name, "wl_display") != 0) + printf("static inline struct %s *\n" + "%s_create(struct wl_display *display, uint32_t id, uint32_t version)\n" + "{\n" + "\twl_display_bind(display, id, \"%s\", version);\n\n" + "\treturn (struct %s *)\n" + "\t\twl_proxy_create_for_id(display, &%s_interface, id);\n" + "}\n\n", + interface->name, + interface->name, + interface->name, + interface->name, + interface->name); + + 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); + } + + /* And we have a hand-written display destructor */ + if (!has_destructor && strcmp(interface->name, "wl_display") != 0) + 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 const char *indent(int n) +{ + const char *whitespace[] = { + "\t\t\t\t\t\t\t\t\t\t\t\t", + "\t\t\t\t\t\t\t\t\t\t\t\t ", + "\t\t\t\t\t\t\t\t\t\t\t\t ", + "\t\t\t\t\t\t\t\t\t\t\t\t ", + "\t\t\t\t\t\t\t\t\t\t\t\t ", + "\t\t\t\t\t\t\t\t\t\t\t\t ", + "\t\t\t\t\t\t\t\t\t\t\t\t ", + "\t\t\t\t\t\t\t\t\t\t\t\t " + }; + + return whitespace[n % 8] + 12 - n / 8; +} + +static void +emit_enumerations(struct interface *interface) +{ + struct enumeration *e; + struct entry *entry; + + wl_list_for_each(e, &interface->enumeration_list, link) { + printf("#ifndef %s_%s_ENUM\n", + interface->uppercase_name, e->uppercase_name); + printf("#define %s_%s_ENUM\n", + interface->uppercase_name, e->uppercase_name); + printf("enum %s_%s {\n", interface->name, e->name); + wl_list_for_each(entry, &e->entry_list, link) + printf("\t%s_%s_%s = %s,\n", + interface->uppercase_name, + e->uppercase_name, + entry->uppercase_name, entry->value); + printf("};\n"); + printf("#endif /* %s_%s_ENUM */\n\n", + interface->uppercase_name, e->uppercase_name); + } +} + +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; + printf("struct %s_%s {\n", interface->name, + is_interface ? "interface" : "listener"); + + wl_list_for_each(m, message_list, link) { + printf("\tvoid (*%s)(", m->name); + + n = strlen(m->name) + 17; + if (is_interface) { + printf("struct wl_client *client,\n" + "%sstruct %s *%s", + indent(n), + interface->name, interface->name); + } 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)); + + 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(17 + strlen(interface->name)), + interface->name, + interface->name, + indent(37)); + } +} + +static void +format_copyright(const char *copyright) +{ + int bol = 1, start = 0, i; + + for (i = 0; copyright[i]; i++) { + if (bol && (copyright[i] == ' ' || copyright[i] == '\t')) { + continue; + } else if (bol) { + bol = 0; + start = i; + } + + if (copyright[i] == '\n' || copyright[i] == '\0') { + printf("%s %.*s\n", + i == 0 ? "/*" : " *", + i - start, copyright + start); + bol = 1; + } + } + printf(" */\n\n"); +} + +static void +emit_header(struct protocol *protocol, int server) +{ + struct interface *i; + const char *s = server ? "SERVER" : "CLIENT"; + + if (protocol->copyright) + format_copyright(protocol->copyright); + + printf("#ifndef %s_%s_PROTOCOL_H\n" + "#define %s_%s_PROTOCOL_H\n" + "\n" + "#ifdef __cplusplus\n" + "extern \"C\" {\n" + "#endif\n" + "\n" + "#include <stdint.h>\n" + "#include <stddef.h>\n" + "#include \"wayland-util.h\"\n\n" + "struct wl_client;\n\n", + protocol->uppercase_name, s, + protocol->uppercase_name, s); + + wl_list_for_each(i, &protocol->interface_list, link) + printf("struct %s;\n", i->name); + printf("\n"); + + wl_list_for_each(i, &protocol->interface_list, link) { + printf("extern const struct wl_interface " + "%s_interface;\n", + i->name); + } + printf("\n"); + + wl_list_for_each(i, &protocol->interface_list, link) { + + emit_enumerations(i); + + 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); + emit_stubs(&i->request_list, i); + } + } + + printf("#ifdef __cplusplus\n" + "}\n" + "#endif\n" + "\n" + "#endif\n"); +} + +static void +emit_types_forward_declarations(struct protocol *protocol, + struct wl_list *message_list) +{ + struct message *m; + struct arg *a; + int length; + + wl_list_for_each(m, message_list, link) { + length = 0; + m->all_null = 1; + wl_list_for_each(a, &m->arg_list, link) { + length++; + switch (a->type) { + case NEW_ID: + case OBJECT: + m->all_null = 0; + printf("extern const struct wl_interface %s_interface;\n", + a->interface_name); + break; + default: + break; + } + } + + if (m->all_null && length > protocol->null_run_length) + protocol->null_run_length = length; + } +} + +static void +emit_null_run(struct protocol *protocol) +{ + int i; + + for (i = 0; i < protocol->null_run_length; i++) + printf("\tNULL,\n"); +} + +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 (strcmp(a->interface_name, + "wl_object") != 0) + printf("\t&%s_interface,\n", + a->interface_name); + else + printf("\tNULL,\n"); + break; + default: + printf("\tNULL,\n"); + break; + } + } + } +} + +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; + case FD: + printf("h"); + break; + } + } + printf("\", types + %d },\n", m->type_index); + } + + printf("};\n\n"); +} + +static void +emit_code(struct protocol *protocol) +{ + struct interface *i; + + if (protocol->copyright) + format_copyright(protocol->copyright); + + printf("#include <stdlib.h>\n" + "#include <stdint.h>\n" + "#include \"wayland-util.h\"\n\n"); + + wl_list_for_each(i, &protocol->interface_list, link) { + emit_types_forward_declarations(protocol, &i->request_list); + emit_types_forward_declarations(protocol, &i->event_list); + } + printf("\n"); + + 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"); + + 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 " + "%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; + int len; + void *buf; + + if (argc != 2) + usage(EXIT_FAILURE); + + wl_list_init(&protocol.interface_list); + protocol.type_index = 0; + protocol.null_run_length = 0; + protocol.copyright = NULL; + ctx.protocol = &protocol; + + ctx.filename = "<stdin>"; + ctx.parser = XML_ParserCreate(NULL); + XML_SetUserData(ctx.parser, &ctx); + if (ctx.parser == NULL) { + fprintf(stderr, "failed to create parser\n"); + exit(EXIT_FAILURE); + } + + XML_SetElementHandler(ctx.parser, start_element, end_element); + XML_SetCharacterDataHandler(ctx.parser, character_data); + + do { + buf = XML_GetBuffer(ctx.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(ctx.parser, len, len == 0); + + } while (len > 0); + + XML_ParserFree(ctx.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; +} diff --git a/src/scanner.mk b/src/scanner.mk new file mode 100644 index 0000000..1b6963c --- /dev/null +++ b/src/scanner.mk @@ -0,0 +1,8 @@ +%-protocol.c : $(protocoldir)/%.xml + $(AM_V_GEN)$(wayland_scanner) code < $< > $@ + +%-server-protocol.h : $(protocoldir)/%.xml + $(AM_V_GEN)$(wayland_scanner) server-header < $< > $@ + +%-client-protocol.h : $(protocoldir)/%.xml + $(AM_V_GEN)$(wayland_scanner) client-header < $< > $@ diff --git a/src/wayland-client.c b/src/wayland-client.c new file mode 100644 index 0000000..9d1f66b --- /dev/null +++ b/src/wayland-client.c @@ -0,0 +1,604 @@ +/* + * Copyright © 2008 Kristian Høgsberg + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <stddef.h> +#include <stdio.h> +#include <stdbool.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <ctype.h> +#include <assert.h> +#include <fcntl.h> +#include <sys/poll.h> + +#include "connection.h" +#include "wayland-util.h" +#include "wayland-client.h" + +struct wl_global_listener { + wl_display_global_func_t handler; + void *data; + struct wl_list link; +}; + +struct wl_proxy { + struct wl_object object; + struct wl_display *display; + void *user_data; +}; + +struct wl_sync_handler { + wl_display_sync_func_t func; + uint32_t key; + void *data; + struct wl_list link; +}; + +struct wl_frame_handler { + wl_display_frame_func_t func; + uint32_t key; + void *data; + struct wl_surface *surface; + struct wl_list link; +}; + +struct wl_global { + uint32_t id; + char *interface; + uint32_t version; + struct wl_list link; +}; + +struct wl_display { + struct wl_proxy proxy; + struct wl_connection *connection; + int fd; + uint32_t id, id_count, next_range; + uint32_t mask; + struct wl_hash_table *objects; + struct wl_list global_listener_list; + struct wl_list global_list; + + wl_display_update_func_t update; + void *update_data; + + wl_display_global_func_t global_handler; + void *global_handler_data; + + struct wl_list sync_list, frame_list; + uint32_t key; +}; + +static int wl_debug = 0; + +static int +connection_update(struct wl_connection *connection, + uint32_t mask, void *data) +{ + struct wl_display *display = data; + + display->mask = mask; + if (display->update) + return display->update(display->mask, + display->update_data); + + return 0; +} + +WL_EXPORT struct wl_global_listener * +wl_display_add_global_listener(struct wl_display *display, + wl_display_global_func_t handler, void *data) +{ + struct wl_global_listener *listener; + struct wl_global *global; + + listener = malloc(sizeof *listener); + if (listener == NULL) + return NULL; + + listener->handler = handler; + listener->data = data; + wl_list_insert(display->global_listener_list.prev, &listener->link); + + wl_list_for_each(global, &display->global_list, link) + (*listener->handler)(display, global->id, global->interface, + global->version, listener->data); + + return listener; +} + +WL_EXPORT void +wl_display_remove_global_listener(struct wl_display *display, + struct wl_global_listener *listener) +{ + wl_list_remove(&listener->link); + free(listener); +} + +WL_EXPORT struct wl_proxy * +wl_proxy_create_for_id(struct wl_display *display, + const struct wl_interface *interface, uint32_t id) +{ + struct wl_proxy *proxy; + + proxy = malloc(sizeof *proxy); + if (proxy == NULL) + return NULL; + + proxy->object.interface = interface; + proxy->object.implementation = NULL; + proxy->object.id = id; + proxy->display = display; + wl_hash_table_insert(display->objects, proxy->object.id, proxy); + + return proxy; +} + +WL_EXPORT struct wl_proxy * +wl_proxy_create(struct wl_proxy *factory, + const struct wl_interface *interface) +{ + return wl_proxy_create_for_id(factory->display, interface, + wl_display_allocate_id(factory->display)); +} + +WL_EXPORT void +wl_proxy_destroy(struct wl_proxy *proxy) +{ + wl_hash_table_remove(proxy->display->objects, proxy->object.id); + free(proxy); +} + +WL_EXPORT int +wl_proxy_add_listener(struct wl_proxy *proxy, + void (**implementation)(void), void *data) +{ + if (proxy->object.implementation) { + fprintf(stderr, "proxy already has listener\n"); + return -1; + } + + proxy->object.implementation = implementation; + proxy->user_data = data; + + return 0; +} + +WL_EXPORT void +wl_proxy_marshal(struct wl_proxy *proxy, uint32_t opcode, ...) +{ + struct wl_closure *closure; + va_list ap; + + va_start(ap, opcode); + closure = wl_connection_vmarshal(proxy->display->connection, + &proxy->object, opcode, ap, + &proxy->object.interface->methods[opcode]); + va_end(ap); + + wl_closure_send(closure, proxy->display->connection); + + if (wl_debug) + wl_closure_print(closure, &proxy->object, true); + + wl_closure_destroy(closure); +} + +/* Can't do this, there may be more than one instance of an + * interface... */ +WL_EXPORT uint32_t +wl_display_get_global(struct wl_display *display, + const char *interface, uint32_t version) +{ + struct wl_global *global; + + wl_list_for_each(global, &display->global_list, link) + if (strcmp(interface, global->interface) == 0 && + version <= global->version) + return global->id; + + return 0; +} + +static void +display_handle_error(void *data, + struct wl_display *display, struct wl_object *object, + uint32_t code, const char *message) +{ + fprintf(stderr, "%s@%d: error %d: %s\n", + object->interface->name, object->id, code, message); + abort(); +} + +static void +display_handle_global(void *data, + struct wl_display *display, + uint32_t id, const char *interface, uint32_t version) +{ + struct wl_global_listener *listener; + struct wl_global *global; + + if (strcmp(interface, "wl_display") == 0) + wl_hash_table_insert(display->objects, + id, &display->proxy.object); + + global = malloc(sizeof *global); + global->id = id; + global->interface = strdup(interface); + global->version = version; + wl_list_insert(display->global_list.prev, &global->link); + + wl_list_for_each(listener, &display->global_listener_list, link) + (*listener->handler)(display, + id, interface, version, listener->data); +} + +static void +display_handle_global_remove(void *data, + struct wl_display *display, uint32_t id) +{ + struct wl_global *global; + + wl_list_for_each(global, &display->global_list, link) + if (global->id == id) { + wl_list_remove(&global->link); + free(global); + break; + } +} + +static void +display_handle_range(void *data, + struct wl_display *display, uint32_t range) +{ + display->next_range = range; +} + +static void +display_handle_key(void *data, + struct wl_display *display, uint32_t key, uint32_t time) +{ + struct wl_sync_handler *sync_handler; + struct wl_frame_handler *frame_handler; + + sync_handler = container_of(display->sync_list.next, + struct wl_sync_handler, link); + if (!wl_list_empty(&display->sync_list) && sync_handler->key == key) { + wl_list_remove(&sync_handler->link); + sync_handler->func(sync_handler->data); + free(sync_handler); + return; + } + + frame_handler = container_of(display->frame_list. next, + struct wl_frame_handler, link); + if (!wl_list_empty(&display->frame_list) && + frame_handler->key == key) { + wl_list_remove(&frame_handler->link); + frame_handler->func(frame_handler->surface, + frame_handler->data, time); + free(frame_handler); + return; + } + + fprintf(stderr, "unsolicited sync event, client gone?\n"); +} + +static const struct wl_display_listener display_listener = { + display_handle_error, + display_handle_global, + display_handle_global_remove, + display_handle_range, + display_handle_key +}; + +static int +connect_to_socket(struct wl_display *display, const char *name) +{ + struct sockaddr_un addr; + socklen_t size; + const char *runtime_dir; + size_t name_size; + + display->fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (display->fd < 0) + return -1; + + runtime_dir = getenv("XDG_RUNTIME_DIR"); + if (runtime_dir == NULL) { + runtime_dir = "."; + fprintf(stderr, + "XDG_RUNTIME_DIR not set, falling back to %s\n", + runtime_dir); + } + + if (name == NULL) + name = getenv("WAYLAND_DISPLAY"); + if (name == NULL) + name = "wayland-0"; + + memset(&addr, 0, sizeof addr); + addr.sun_family = AF_LOCAL; + name_size = + snprintf(addr.sun_path, sizeof addr.sun_path, + "%s/%s", runtime_dir, name) + 1; + + size = offsetof (struct sockaddr_un, sun_path) + name_size; + + if (connect(display->fd, (struct sockaddr *) &addr, size) < 0) { + close(display->fd); + return -1; + } + + return 0; +} + +WL_EXPORT struct wl_display * +wl_display_connect(const char *name) +{ + struct wl_display *display; + const char *debug; + char *connection, *end; + int flags; + + debug = getenv("WAYLAND_DEBUG"); + if (debug) + wl_debug = 1; + + display = malloc(sizeof *display); + if (display == NULL) + return NULL; + + memset(display, 0, sizeof *display); + connection = getenv("WAYLAND_SOCKET"); + if (connection) { + display->fd = strtol(connection, &end, 0); + if (*end != '\0') { + free(display); + return NULL; + } + flags = fcntl(display->fd, F_GETFD); + if (flags != -1) + fcntl(display->fd, F_SETFD, flags | FD_CLOEXEC); + } else if (connect_to_socket(display, name) < 0) { + free(display); + return NULL; + } + + display->objects = wl_hash_table_create(); + if (display->objects == NULL) { + close(display->fd); + free(display); + return NULL; + } + wl_list_init(&display->global_listener_list); + wl_list_init(&display->global_list); + + display->proxy.object.interface = &wl_display_interface; + display->proxy.object.id = 1; + display->proxy.display = display; + + wl_list_init(&display->sync_list); + wl_list_init(&display->frame_list); + + display->proxy.object.implementation = + (void(**)(void)) &display_listener; + display->proxy.user_data = display; + + display->connection = wl_connection_create(display->fd, + connection_update, + display); + if (display->connection == NULL) { + wl_hash_table_destroy(display->objects); + close(display->fd); + free(display); + return NULL; + } + + wl_display_bind(display, 1, "wl_display", 1); + + return display; +} + +WL_EXPORT void +wl_display_destroy(struct wl_display *display) +{ + struct wl_global *global, *gnext; + struct wl_global_listener *listener, *lnext; + + wl_connection_destroy(display->connection); + wl_hash_table_destroy(display->objects); + wl_list_for_each_safe(global, gnext, + &display->global_list, link) + free(global); + wl_list_for_each_safe(listener, lnext, + &display->global_listener_list, link) + free(listener); + + close(display->fd); + free(display); +} + +WL_EXPORT int +wl_display_get_fd(struct wl_display *display, + wl_display_update_func_t update, void *data) +{ + display->update = update; + display->update_data = data; + + display->update(display->mask, display->update_data); + + return display->fd; +} + +WL_EXPORT int +wl_display_sync_callback(struct wl_display *display, + wl_display_sync_func_t func, void *data) +{ + struct wl_sync_handler *handler; + + handler = malloc(sizeof *handler); + if (handler == NULL) + return -1; + + handler->func = func; + handler->key = display->key++; + handler->data = data; + + wl_list_insert(display->sync_list.prev, &handler->link); + wl_display_sync(display, handler->key); + + return 0; +} + +WL_EXPORT int +wl_display_frame_callback(struct wl_display *display, + struct wl_surface *surface, + wl_display_frame_func_t func, void *data) +{ + struct wl_frame_handler *handler; + + handler = malloc(sizeof *handler); + if (handler == NULL) + return -1; + + handler->func = func; + handler->key = display->key++; + handler->data = data; + handler->surface = surface; + + wl_list_insert(display->frame_list.prev, &handler->link); + wl_display_frame(display, handler->surface, handler->key); + + return 0; +} + +static void +handle_event(struct wl_display *display, + uint32_t id, uint32_t opcode, uint32_t size) +{ + uint32_t p[32]; + struct wl_proxy *proxy; + struct wl_closure *closure; + const struct wl_message *message; + + wl_connection_copy(display->connection, p, size); + if (id == 1) + proxy = &display->proxy; + else + proxy = wl_hash_table_lookup(display->objects, id); + + if (proxy == NULL || proxy->object.implementation == NULL) { + wl_connection_consume(display->connection, size); + return; + } + + message = &proxy->object.interface->events[opcode]; + closure = wl_connection_demarshal(display->connection, + size, display->objects, message); + + if (closure == NULL) { + fprintf(stderr, "Error demarshalling event: %m\n"); + abort(); + } + + if (wl_debug) + wl_closure_print(closure, &proxy->object, false); + + wl_closure_invoke(closure, &proxy->object, + proxy->object.implementation[opcode], + proxy->user_data); + + wl_closure_destroy(closure); +} + +WL_EXPORT void +wl_display_iterate(struct wl_display *display, uint32_t mask) +{ + uint32_t p[2], object, opcode, size; + int len; + + mask &= display->mask; + if (mask == 0) { + fprintf(stderr, + "wl_display_iterate called with unsolicited flags"); + return; + } + + len = wl_connection_data(display->connection, mask); + while (len > 0) { + if (len < sizeof p) + break; + + wl_connection_copy(display->connection, p, sizeof p); + object = p[0]; + opcode = p[1] & 0xffff; + size = p[1] >> 16; + if (len < size) + break; + + handle_event(display, object, opcode, size); + len -= size; + } + + if (len < 0) { + fprintf(stderr, "read error: %m\n"); + exit(EXIT_FAILURE); + } +} + +WL_EXPORT void +wl_display_flush(struct wl_display *display) +{ + while (display->mask & WL_DISPLAY_WRITABLE) + wl_display_iterate (display, WL_DISPLAY_WRITABLE); +} + +WL_EXPORT uint32_t +wl_display_allocate_id(struct wl_display *display) +{ + if (display->id_count == 0) { + display->id_count = 256; + display->id = display->next_range; + } + + display->id_count--; + + return display->id++; +} + +WL_EXPORT void +wl_proxy_set_user_data(struct wl_proxy *proxy, void *user_data) +{ + proxy->user_data = user_data; +} + +WL_EXPORT void * +wl_proxy_get_user_data(struct wl_proxy *proxy) +{ + return proxy->user_data; +} diff --git a/src/wayland-client.h b/src/wayland-client.h new file mode 100644 index 0000000..385dc16 --- /dev/null +++ b/src/wayland-client.h @@ -0,0 +1,90 @@ +/* + * Copyright © 2008 Kristian Høgsberg + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef _WAYLAND_CLIENT_H +#define _WAYLAND_CLIENT_H + +#include "wayland-util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct wl_proxy; +struct wl_display; + +void wl_proxy_marshal(struct wl_proxy *p, uint32_t opcode, ...); +struct wl_proxy *wl_proxy_create(struct wl_proxy *factory, + const struct wl_interface *interface); +struct wl_proxy *wl_proxy_create_for_id(struct wl_display *display, + const struct wl_interface *interface, + uint32_t id); +void wl_proxy_destroy(struct wl_proxy *proxy); +int wl_proxy_add_listener(struct wl_proxy *proxy, + void (**implementation)(void), void *data); +void wl_proxy_set_user_data(struct wl_proxy *proxy, void *user_data); +void *wl_proxy_get_user_data(struct wl_proxy *proxy); + +#include "wayland-client-protocol.h" + +#define WL_DISPLAY_READABLE 0x01 +#define WL_DISPLAY_WRITABLE 0x02 + +typedef int (*wl_display_update_func_t)(uint32_t mask, void *data); +typedef void (*wl_display_sync_func_t)(void *data); +typedef void (*wl_display_frame_func_t)(struct wl_surface *surface, + void *data, uint32_t time); + +struct wl_display *wl_display_connect(const char *name); +void wl_display_destroy(struct wl_display *display); +int wl_display_get_fd(struct wl_display *display, + wl_display_update_func_t update, void *data); +uint32_t wl_display_allocate_id(struct wl_display *display); +void wl_display_iterate(struct wl_display *display, uint32_t mask); +void wl_display_flush(struct wl_display *display); +int wl_display_sync_callback(struct wl_display *display, + wl_display_sync_func_t func, void *data); +int wl_display_frame_callback(struct wl_display *display, + struct wl_surface *surface, + wl_display_frame_func_t func, void *data); + +struct wl_global_listener; +typedef void (*wl_display_global_func_t)(struct wl_display *display, + uint32_t id, + const char *interface, + uint32_t version, + void *data); +void +wl_display_remove_global_listener(struct wl_display *display, + struct wl_global_listener *listener); +struct wl_global_listener * +wl_display_add_global_listener(struct wl_display *display, + wl_display_global_func_t handler, void *data); +WL_EXPORT uint32_t +wl_display_get_global(struct wl_display *display, + const char *interface, uint32_t version); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/wayland-client.pc.in b/src/wayland-client.pc.in new file mode 100644 index 0000000..59a925d --- /dev/null +++ b/src/wayland-client.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: Wayland Client +Description: Wayland client side library +Version: 0.1 +Cflags: -I${includedir} +Libs: -L${libdir} -lwayland-client diff --git a/src/wayland-egl.h b/src/wayland-egl.h new file mode 100644 index 0000000..85fe73d --- /dev/null +++ b/src/wayland-egl.h @@ -0,0 +1,68 @@ +/* + * Copyright © 2011 Kristian Høgsberg + * Copyright © 2011 Benjamin Franzke + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef _WAYLAND_EGL_H +#define _WAYLAND_EGL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <wayland-client.h> + +#define WL_EGL_PLATFORM 1 + +struct wl_egl_window; +struct wl_egl_pixmap; + +struct wl_egl_window * +wl_egl_window_create(struct wl_surface *surface, + int width, int height, + struct wl_visual *visual); + +void +wl_egl_window_destroy(struct wl_egl_window *egl_window); + +void +wl_egl_window_resize(struct wl_egl_window *egl_window, + int width, int height, + int dx, int dy); + +void +wl_egl_window_get_attached_size(struct wl_egl_window *egl_window, + int *width, int *height); + +struct wl_egl_pixmap * +wl_egl_pixmap_create(int width, int height, + struct wl_visual *visual, uint32_t flags); +void +wl_egl_pixmap_destroy(struct wl_egl_pixmap *egl_pixmap); + +struct wl_buffer * +wl_egl_pixmap_create_buffer(struct wl_egl_pixmap *egl_pixmap); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/wayland-hash.c b/src/wayland-hash.c new file mode 100644 index 0000000..b299a33 --- /dev/null +++ b/src/wayland-hash.c @@ -0,0 +1,296 @@ +/* + * Copyright © 2009 Intel Corporation + * Copyright © 1988-2004 Keith Packard and Bart Massey. + * + * 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. + * + * Except as contained in this notice, the names of the authors + * or their institutions shall not be used in advertising or + * otherwise to promote the sale, use or other dealings in this + * Software without prior written authorization from the + * authors. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * Keith Packard <keithp@keithp.com> + */ + +#include <stdlib.h> + +#include "wayland-util.h" + +struct hash_entry { + uint32_t hash; + void *data; +}; + +struct wl_hash_table { + struct hash_entry *table; + uint32_t size; + uint32_t rehash; + uint32_t max_entries; + uint32_t size_index; + uint32_t entries; + uint32_t deleted_entries; +}; + +#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) + +/* + * From Knuth -- a good choice for hash/rehash values is p, p-2 where + * p and p-2 are both prime. These tables are sized to have an extra 10% + * free to avoid exponential performance degradation as the hash table fills + */ + +static const uint32_t deleted_data; + +static const struct { + uint32_t max_entries, size, rehash; +} hash_sizes[] = { + { 2, 5, 3 }, + { 4, 7, 5 }, + { 8, 13, 11 }, + { 16, 19, 17 }, + { 32, 43, 41 }, + { 64, 73, 71 }, + { 128, 151, 149 }, + { 256, 283, 281 }, + { 512, 571, 569 }, + { 1024, 1153, 1151 }, + { 2048, 2269, 2267 }, + { 4096, 4519, 4517 }, + { 8192, 9013, 9011 }, + { 16384, 18043, 18041 }, + { 32768, 36109, 36107 }, + { 65536, 72091, 72089 }, + { 131072, 144409, 144407 }, + { 262144, 288361, 288359 }, + { 524288, 576883, 576881 }, + { 1048576, 1153459, 1153457 }, + { 2097152, 2307163, 2307161 }, + { 4194304, 4613893, 4613891 }, + { 8388608, 9227641, 9227639 }, + { 16777216, 18455029, 18455027 }, + { 33554432, 36911011, 36911009 }, + { 67108864, 73819861, 73819859 }, + { 134217728, 147639589, 147639587 }, + { 268435456, 295279081, 295279079 }, + { 536870912, 590559793, 590559791 }, + { 1073741824, 1181116273, 1181116271}, + { 2147483648ul, 2362232233ul, 2362232231ul} +}; + +static int +entry_is_free(struct hash_entry *entry) +{ + return entry->data == NULL; +} + +static int +entry_is_deleted(struct hash_entry *entry) +{ + return entry->data == &deleted_data; +} + +static int +entry_is_present(struct hash_entry *entry) +{ + return entry->data != NULL && entry->data != &deleted_data; +} + +WL_EXPORT struct wl_hash_table * +wl_hash_table_create(void) +{ + struct wl_hash_table *ht; + + ht = malloc(sizeof(*ht)); + if (ht == NULL) + return NULL; + + ht->size_index = 0; + ht->size = hash_sizes[ht->size_index].size; + ht->rehash = hash_sizes[ht->size_index].rehash; + ht->max_entries = hash_sizes[ht->size_index].max_entries; + ht->table = calloc(ht->size, sizeof(*ht->table)); + ht->entries = 0; + ht->deleted_entries = 0; + + if (ht->table == NULL) { + free(ht); + return NULL; + } + + return ht; +} + +/** + * Frees the given hash table. + */ +WL_EXPORT void +wl_hash_table_destroy(struct wl_hash_table *ht) +{ + if (!ht) + return; + + free(ht->table); + free(ht); +} + +/** + * Finds a hash table entry with the given key and hash of that key. + * + * Returns NULL if no entry is found. Note that the data pointer may be + * modified by the user. + */ +static void * +hash_table_search(struct wl_hash_table *ht, uint32_t hash) +{ + uint32_t hash_address; + + hash_address = hash % ht->size; + do { + uint32_t double_hash; + + struct hash_entry *entry = ht->table + hash_address; + + if (entry_is_free(entry)) { + return NULL; + } else if (entry_is_present(entry) && entry->hash == hash) { + return entry; + } + + double_hash = hash % ht->rehash; + if (double_hash == 0) + double_hash = 1; + + hash_address = (hash_address + double_hash) % ht->size; + } while (hash_address != hash % ht->size); + + return NULL; +} + +WL_EXPORT void * +wl_hash_table_lookup(struct wl_hash_table *ht, uint32_t hash) +{ + struct hash_entry *entry; + + entry = hash_table_search(ht, hash); + if (entry != NULL) + return entry->data; + + return NULL; +} + +static void +hash_table_rehash(struct wl_hash_table *ht, int new_size_index) +{ + struct wl_hash_table old_ht; + struct hash_entry *table, *entry; + + if (new_size_index >= ARRAY_SIZE(hash_sizes)) + return; + + table = calloc(hash_sizes[new_size_index].size, sizeof(*ht->table)); + if (table == NULL) + return; + + old_ht = *ht; + + ht->table = table; + ht->size_index = new_size_index; + ht->size = hash_sizes[ht->size_index].size; + ht->rehash = hash_sizes[ht->size_index].rehash; + ht->max_entries = hash_sizes[ht->size_index].max_entries; + ht->entries = 0; + ht->deleted_entries = 0; + + for (entry = old_ht.table; + entry != old_ht.table + old_ht.size; + entry++) { + if (entry_is_present(entry)) { + wl_hash_table_insert(ht, entry->hash, entry->data); + } + } + + free(old_ht.table); +} + +/** + * Inserts the data with the given hash into the table. + * + * Note that insertion may rearrange the table on a resize or rehash, + * so previously found hash_entries are no longer valid after this function. + */ +WL_EXPORT int +wl_hash_table_insert(struct wl_hash_table *ht, uint32_t hash, void *data) +{ + uint32_t hash_address; + + if (ht->entries >= ht->max_entries) { + hash_table_rehash(ht, ht->size_index + 1); + } else if (ht->deleted_entries + ht->entries >= ht->max_entries) { + hash_table_rehash(ht, ht->size_index); + } + + hash_address = hash % ht->size; + do { + struct hash_entry *entry = ht->table + hash_address; + uint32_t double_hash; + + if (!entry_is_present(entry)) { + if (entry_is_deleted(entry)) + ht->deleted_entries--; + entry->hash = hash; + entry->data = data; + ht->entries++; + return 0; + } + + double_hash = hash % ht->rehash; + if (double_hash == 0) + double_hash = 1; + + hash_address = (hash_address + double_hash) % ht->size; + } while (hash_address != hash % ht->size); + + /* We could hit here if a required resize failed. An unchecked-malloc + * application could ignore this result. + */ + return -1; +} + +/** + * This function deletes the given hash table entry. + * + * Note that deletion doesn't otherwise modify the table, so an iteration over + * the table deleting entries is safe. + */ +WL_EXPORT void +wl_hash_table_remove(struct wl_hash_table *ht, uint32_t hash) +{ + struct hash_entry *entry; + + entry = hash_table_search(ht, hash); + if (entry != NULL) { + entry->data = (void *) &deleted_data; + ht->entries--; + ht->deleted_entries++; + } +} diff --git a/src/wayland-server.c b/src/wayland-server.c new file mode 100644 index 0000000..2019cb4 --- /dev/null +++ b/src/wayland-server.c @@ -0,0 +1,933 @@ +/* + * Copyright © 2008 Kristian Høgsberg + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#define _GNU_SOURCE + +#include <stdlib.h> +#include <stdint.h> +#include <stddef.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdbool.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <dlfcn.h> +#include <assert.h> +#include <sys/time.h> +#include <fcntl.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <ffi.h> + +#include "wayland-server.h" +#include "wayland-server-protocol.h" +#include "connection.h" + +struct wl_socket { + int fd; + int fd_lock; + struct sockaddr_un addr; + char lock_addr[113]; + struct wl_list link; +}; + +struct wl_client { + struct wl_connection *connection; + struct wl_event_source *source; + struct wl_display *display; + struct wl_list resource_list; + uint32_t id_count; + uint32_t mask; + struct wl_list link; +}; + +struct wl_display { + struct wl_object object; + struct wl_event_loop *loop; + struct wl_hash_table *objects; + int run; + + struct wl_list frame_list; + uint32_t client_id_range; + uint32_t id; + + struct wl_list global_list; + struct wl_list socket_list; + struct wl_list client_list; +}; + +struct wl_frame_listener { + struct wl_resource resource; + struct wl_client *client; + uint32_t key; + struct wl_surface *surface; + struct wl_list link; +}; + +struct wl_global { + struct wl_object *object; + wl_global_bind_func_t func; + struct wl_list link; +}; + +static int wl_debug = 0; + +WL_EXPORT void +wl_client_post_event(struct wl_client *client, struct wl_object *sender, + uint32_t opcode, ...) +{ + struct wl_closure *closure; + va_list ap; + + va_start(ap, opcode); + closure = wl_connection_vmarshal(client->connection, + sender, opcode, ap, + &sender->interface->events[opcode]); + va_end(ap); + + wl_closure_send(closure, client->connection); + + if (wl_debug) + wl_closure_print(closure, sender, true); + + wl_closure_destroy(closure); +} + +WL_EXPORT void +wl_client_post_error(struct wl_client *client, struct wl_object *object, + uint32_t code, const char *msg, ...) +{ + char buffer[128]; + va_list ap; + + va_start(ap, msg); + vsnprintf(buffer, sizeof buffer, msg, ap); + va_end(ap); + + wl_client_post_event(client, &client->display->object, + WL_DISPLAY_ERROR, object, code, buffer); +} + +static int +wl_client_connection_data(int fd, uint32_t mask, void *data) +{ + struct wl_client *client = data; + struct wl_connection *connection = client->connection; + struct wl_object *object; + struct wl_closure *closure; + const struct wl_message *message; + uint32_t p[2], opcode, size; + uint32_t cmask = 0; + int len; + + if (mask & WL_EVENT_READABLE) + cmask |= WL_CONNECTION_READABLE; + if (mask & WL_EVENT_WRITEABLE) + cmask |= WL_CONNECTION_WRITABLE; + + len = wl_connection_data(connection, cmask); + if (len < 0) { + wl_client_destroy(client); + return 1; + } + + while (len >= sizeof p) { + wl_connection_copy(connection, p, sizeof p); + opcode = p[1] & 0xffff; + size = p[1] >> 16; + if (len < size) + break; + + object = wl_hash_table_lookup(client->display->objects, p[0]); + if (object == NULL) { + wl_client_post_error(client, &client->display->object, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "invalid object %d", p[0]); + wl_connection_consume(connection, size); + len -= size; + continue; + } + + if (opcode >= object->interface->method_count) { + wl_client_post_error(client, &client->display->object, + WL_DISPLAY_ERROR_INVALID_METHOD, + "invalid method %d, object %s@%d", + object->interface->name, + object->id, opcode); + wl_connection_consume(connection, size); + len -= size; + continue; + } + + message = &object->interface->methods[opcode]; + closure = wl_connection_demarshal(client->connection, size, + client->display->objects, + message); + len -= size; + + if (closure == NULL && errno == EINVAL) { + wl_client_post_error(client, &client->display->object, + WL_DISPLAY_ERROR_INVALID_METHOD, + "invalid arguments for %s@%d.%s", + object->interface->name, + object->id, message->name); + continue; + } else if (closure == NULL && errno == ENOMEM) { + wl_client_post_no_memory(client); + continue; + } + + + if (wl_debug) + wl_closure_print(closure, object, false); + + wl_closure_invoke(closure, object, + object->implementation[opcode], client); + + wl_closure_destroy(closure); + } + + return 1; +} + +static int +wl_client_connection_update(struct wl_connection *connection, + uint32_t mask, void *data) +{ + struct wl_client *client = data; + uint32_t emask = 0; + + client->mask = mask; + if (mask & WL_CONNECTION_READABLE) + emask |= WL_EVENT_READABLE; + if (mask & WL_CONNECTION_WRITABLE) + emask |= WL_EVENT_WRITEABLE; + + return wl_event_source_fd_update(client->source, emask); +} + +WL_EXPORT void +wl_client_flush(struct wl_client *client) +{ + if (client->mask & WL_CONNECTION_WRITABLE) + wl_connection_data(client->connection, WL_CONNECTION_WRITABLE); +} + +WL_EXPORT struct wl_display * +wl_client_get_display(struct wl_client *client) +{ + return client->display; +} + +static void +wl_display_post_range(struct wl_display *display, struct wl_client *client) +{ + wl_client_post_event(client, &client->display->object, + WL_DISPLAY_RANGE, display->client_id_range); + display->client_id_range += 256; + client->id_count += 256; +} + +WL_EXPORT struct wl_client * +wl_client_create(struct wl_display *display, int fd) +{ + struct wl_client *client; + struct wl_global *global; + + client = malloc(sizeof *client); + if (client == NULL) + return NULL; + + memset(client, 0, sizeof *client); + client->display = display; + client->source = wl_event_loop_add_fd(display->loop, fd, + WL_EVENT_READABLE, + wl_client_connection_data, client); + client->connection = + wl_connection_create(fd, wl_client_connection_update, client); + if (client->connection == NULL) { + free(client); + return NULL; + } + + wl_list_insert(display->client_list.prev, &client->link); + + wl_list_init(&client->resource_list); + + wl_display_post_range(display, client); + + wl_list_for_each(global, &display->global_list, link) + wl_client_post_global(client, global->object); + + return client; +} + +WL_EXPORT void +wl_client_add_resource(struct wl_client *client, + struct wl_resource *resource) +{ + struct wl_display *display = client->display; + + if (client->id_count-- < 64) + wl_display_post_range(display, client); + + wl_list_init(&resource->destroy_listener_list); + + wl_hash_table_insert(client->display->objects, + resource->object.id, resource); + wl_list_insert(client->resource_list.prev, &resource->link); +} + +WL_EXPORT void +wl_client_post_no_memory(struct wl_client *client) +{ + wl_client_post_error(client, &client->display->object, + WL_DISPLAY_ERROR_NO_MEMORY, "no memory"); +} + +WL_EXPORT void +wl_client_post_global(struct wl_client *client, struct wl_object *object) +{ + wl_client_post_event(client, + &client->display->object, + WL_DISPLAY_GLOBAL, + object->id, + object->interface->name, + object->interface->version); +} + +WL_EXPORT void +wl_resource_destroy(struct wl_resource *resource, + struct wl_client *client, uint32_t time) +{ + struct wl_display *display = client->display; + struct wl_listener *l, *next; + + wl_list_for_each_safe(l, next, + &resource->destroy_listener_list, link) + l->func(l, resource, time); + + wl_list_remove(&resource->link); + if (resource->object.id > 0) + wl_hash_table_remove(display->objects, resource->object.id); + resource->destroy(resource, client); +} + +WL_EXPORT void +wl_client_destroy(struct wl_client *client) +{ + struct wl_resource *resource, *tmp; + + printf("disconnect from client %p\n", client); + + wl_list_for_each_safe(resource, tmp, &client->resource_list, link) + wl_resource_destroy(resource, client, 0); + + wl_event_source_remove(client->source); + wl_connection_destroy(client->connection); + wl_list_remove(&client->link); + free(client); +} + +static void +lose_pointer_focus(struct wl_listener *listener, + struct wl_resource *resource, uint32_t time) +{ + struct wl_input_device *device = + container_of(listener, struct wl_input_device, + pointer_focus_listener); + + wl_input_device_set_pointer_focus(device, NULL, time, 0, 0, 0, 0); +} + +static void +lose_keyboard_focus(struct wl_listener *listener, + struct wl_resource *resource, uint32_t time) +{ + struct wl_input_device *device = + container_of(listener, struct wl_input_device, + keyboard_focus_listener); + + wl_input_device_set_keyboard_focus(device, NULL, time); +} + +WL_EXPORT void +wl_input_device_init(struct wl_input_device *device, + struct wl_compositor *compositor) +{ + wl_list_init(&device->pointer_focus_listener.link); + device->pointer_focus_listener.func = lose_pointer_focus; + wl_list_init(&device->keyboard_focus_listener.link); + device->keyboard_focus_listener.func = lose_keyboard_focus; + + device->x = 100; + device->y = 100; + device->compositor = compositor; +} + +WL_EXPORT void +wl_input_device_set_pointer_focus(struct wl_input_device *device, + struct wl_surface *surface, + uint32_t time, + int32_t x, int32_t y, + int32_t sx, int32_t sy) +{ + if (device->pointer_focus == surface) + return; + + if (device->pointer_focus && + (!surface || device->pointer_focus->client != surface->client)) + wl_client_post_event(device->pointer_focus->client, + &device->object, + WL_INPUT_DEVICE_POINTER_FOCUS, + time, NULL, 0, 0, 0, 0); + if (device->pointer_focus) + wl_list_remove(&device->pointer_focus_listener.link); + + if (surface) { + wl_client_post_event(surface->client, + &device->object, + WL_INPUT_DEVICE_POINTER_FOCUS, + time, surface, x, y, sx, sy); + wl_list_insert(surface->resource.destroy_listener_list.prev, + &device->pointer_focus_listener.link); + } + + device->pointer_focus = surface; + device->pointer_focus_time = time; + +} + +WL_EXPORT void +wl_input_device_set_keyboard_focus(struct wl_input_device *device, + struct wl_surface *surface, + uint32_t time) +{ + if (device->keyboard_focus == surface) + return; + + if (device->keyboard_focus && + (!surface || device->keyboard_focus->client != surface->client)) + wl_client_post_event(device->keyboard_focus->client, + &device->object, + WL_INPUT_DEVICE_KEYBOARD_FOCUS, + time, NULL, &device->keys); + if (device->keyboard_focus) + wl_list_remove(&device->keyboard_focus_listener.link); + + if (surface) { + wl_client_post_event(surface->client, + &device->object, + WL_INPUT_DEVICE_KEYBOARD_FOCUS, + time, surface, &device->keys); + wl_list_insert(surface->resource.destroy_listener_list.prev, + &device->keyboard_focus_listener.link); + } + + device->keyboard_focus = surface; + device->keyboard_focus_time = time; +} + +WL_EXPORT void +wl_input_device_end_grab(struct wl_input_device *device, uint32_t time) +{ + const struct wl_grab_interface *interface; + + interface = device->grab->interface; + interface->end(device->grab, time); + device->grab->input_device = NULL; + device->grab = NULL; + + wl_list_remove(&device->grab_listener.link); +} + +static void +lose_grab_surface(struct wl_listener *listener, + struct wl_resource *resource, uint32_t time) +{ + struct wl_input_device *device = + container_of(listener, + struct wl_input_device, grab_listener); + + wl_input_device_end_grab(device, time); +} + +WL_EXPORT void +wl_input_device_start_grab(struct wl_input_device *device, + struct wl_grab *grab, + uint32_t button, uint32_t time) +{ + struct wl_surface *focus = device->pointer_focus; + + device->grab = grab; + device->grab_button = button; + device->grab_time = time; + device->grab_x = device->x; + device->grab_y = device->y; + + device->grab_listener.func = lose_grab_surface; + wl_list_insert(focus->resource.destroy_listener_list.prev, + &device->grab_listener.link); + + grab->input_device = device; +} + +WL_EXPORT int +wl_input_device_update_grab(struct wl_input_device *device, + struct wl_grab *grab, + struct wl_surface *surface, uint32_t time) +{ + if (device->grab != &device->motion_grab || + device->grab_time != time || + device->pointer_focus != surface) + return -1; + + device->grab = grab; + grab->input_device = device; + + return 0; +} + +static void +display_bind(struct wl_client *client, + struct wl_display *display, uint32_t id, + const char *interface, uint32_t version) +{ + struct wl_global *global; + + wl_list_for_each(global, &display->global_list, link) + if (global->object->id == id) + break; + + if (&global->link == &display->global_list) + wl_client_post_error(client, &client->display->object, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "invalid object %d", id); + else if (global->func) + global->func(client, global->object, version); +} + +static void +display_sync(struct wl_client *client, + struct wl_display *display, uint32_t key) +{ + wl_client_post_event(client, &display->object, WL_DISPLAY_KEY, key, 0); +} + +static void +destroy_frame_listener(struct wl_resource *resource, struct wl_client *client) +{ + struct wl_frame_listener *listener = + container_of(resource, struct wl_frame_listener, resource); + + wl_list_remove(&listener->link); + free(listener); +} + +static void +display_frame(struct wl_client *client, + struct wl_display *display, + struct wl_surface *surface, + uint32_t key) +{ + struct wl_frame_listener *listener; + + listener = malloc(sizeof *listener); + if (listener == NULL) { + wl_client_post_no_memory(client); + return; + } + + /* The listener is a resource so we destroy it when the client + * goes away. */ + listener->resource.destroy = destroy_frame_listener; + listener->resource.object.id = 0; + listener->client = client; + listener->key = key; + listener->surface = surface; + wl_list_init(&listener->resource.destroy_listener_list); + wl_list_insert(client->resource_list.prev, &listener->resource.link); + wl_list_insert(display->frame_list.prev, &listener->link); +} + +struct wl_display_interface display_interface = { + display_bind, + display_sync, + display_frame +}; + + +WL_EXPORT struct wl_display * +wl_display_create(void) +{ + struct wl_display *display; + const char *debug; + + debug = getenv("WAYLAND_DEBUG"); + if (debug) + wl_debug = 1; + + display = malloc(sizeof *display); + if (display == NULL) + return NULL; + + display->loop = wl_event_loop_create(); + if (display->loop == NULL) { + free(display); + return NULL; + } + + display->objects = wl_hash_table_create(); + if (display->objects == NULL) { + wl_event_loop_destroy(display->loop); + free(display); + return NULL; + } + + wl_list_init(&display->frame_list); + wl_list_init(&display->global_list); + wl_list_init(&display->socket_list); + wl_list_init(&display->client_list); + + display->client_id_range = 256; /* Gah, arbitrary... */ + + display->id = 1; + display->object.interface = &wl_display_interface; + display->object.implementation = (void (**)(void)) &display_interface; + wl_display_add_object(display, &display->object); + if (wl_display_add_global(display, &display->object, NULL)) { + wl_hash_table_destroy(display->objects); + wl_event_loop_destroy(display->loop); + free(display); + return NULL; + } + + return display; +} + +WL_EXPORT void +wl_display_destroy(struct wl_display *display) +{ + struct wl_socket *s, *next; + struct wl_global *global, *gnext; + + wl_event_loop_destroy(display->loop); + wl_hash_table_destroy(display->objects); + wl_list_for_each_safe(s, next, &display->socket_list, link) { + close(s->fd); + unlink(s->addr.sun_path); + close(s->fd_lock); + unlink(s->lock_addr); + free(s); + } + + wl_list_for_each_safe(global, gnext, &display->global_list, link) + free(global); + + free(display); +} + +WL_EXPORT void +wl_display_add_object(struct wl_display *display, struct wl_object *object) +{ + object->id = display->id++; + wl_hash_table_insert(display->objects, object->id, object); +} + +WL_EXPORT int +wl_display_add_global(struct wl_display *display, + struct wl_object *object, wl_global_bind_func_t func) +{ + struct wl_global *global; + + global = malloc(sizeof *global); + if (global == NULL) + return -1; + + global->object = object; + global->func = func; + wl_list_insert(display->global_list.prev, &global->link); + + return 0; +} + +WL_EXPORT int +wl_display_remove_global(struct wl_display *display, + struct wl_object *object) +{ + struct wl_global *global; + struct wl_client *client; + + wl_list_for_each(global, &display->global_list, link) + if (global->object == object) + break; + + if (&global->link == &display->global_list) + return -1; + + wl_list_for_each(client, &display->client_list, link) + wl_client_post_event(client, + &client->display->object, + WL_DISPLAY_GLOBAL_REMOVE, + global->object->id); + wl_list_remove(&global->link); + free(global); + + return 0; +} + +WL_EXPORT void +wl_display_post_frame(struct wl_display *display, struct wl_surface *surface, + uint32_t time) +{ + struct wl_frame_listener *listener, *next; + + wl_list_for_each_safe(listener, next, &display->frame_list, link) { + if (listener->surface != surface) + continue; + wl_client_post_event(listener->client, &display->object, + WL_DISPLAY_KEY, listener->key, time); + wl_resource_destroy(&listener->resource, listener->client, 0); + } +} + +WL_EXPORT struct wl_event_loop * +wl_display_get_event_loop(struct wl_display *display) +{ + return display->loop; +} + +WL_EXPORT void +wl_display_terminate(struct wl_display *display) +{ + display->run = 0; +} + +WL_EXPORT void +wl_display_run(struct wl_display *display) +{ + display->run = 1; + + while (display->run) + wl_event_loop_dispatch(display->loop, -1); +} + +static int +socket_data(int fd, uint32_t mask, void *data) +{ + struct wl_display *display = data; + struct sockaddr_un name; + socklen_t length; + int client_fd; + + length = sizeof name; + client_fd = + accept4(fd, (struct sockaddr *) &name, &length, SOCK_CLOEXEC); + if (client_fd < 0 && errno == ENOSYS) { + client_fd = accept(fd, (struct sockaddr *) &name, &length); + if (client_fd >= 0 && fcntl(client_fd, F_SETFD, FD_CLOEXEC) == -1) + fprintf(stderr, "failed to set FD_CLOEXEC flag on client fd, errno: %d\n", errno); + } + + if (client_fd < 0) + fprintf(stderr, "failed to accept, errno: %d\n", errno); + + wl_client_create(display, client_fd); + + return 1; +} + +static int +get_socket_lock(struct wl_socket *socket, socklen_t name_size) +{ + struct stat socket_stat; + int lock_size = name_size + 5; + + snprintf(socket->lock_addr, lock_size, + "%s.lock", socket->addr.sun_path); + + socket->fd_lock = open(socket->lock_addr, O_CREAT | O_CLOEXEC, + (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)); + + if (socket->fd_lock < 0) { + fprintf(stderr, + "unable to open lockfile %s check permissions\n", + socket->lock_addr); + return -1; + } + + if (flock(socket->fd_lock, LOCK_EX | LOCK_NB) < 0) { + fprintf(stderr, + "unable to lock lockfile %s, maybe another compositor is running\n", + socket->lock_addr); + close(socket->fd_lock); + return -1; + } + + if (stat(socket->addr.sun_path, &socket_stat) < 0 ) { + if (errno != ENOENT) { + fprintf(stderr, "did not manage to stat file %s\n", + socket->addr.sun_path); + close(socket->fd_lock); + return -1; + } + } else if (socket_stat.st_mode & S_IWUSR || + socket_stat.st_mode & S_IWGRP) { + unlink(socket->addr.sun_path); + } + + return 0; +} + +WL_EXPORT int +wl_display_add_socket(struct wl_display *display, const char *name) +{ + struct wl_socket *s; + socklen_t size, name_size; + const char *runtime_dir; + + s = malloc(sizeof *s); + if (s == NULL) + return -1; + + s->fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (s->fd < 0) { + free(s); + return -1; + } + + runtime_dir = getenv("XDG_RUNTIME_DIR"); + if (runtime_dir == NULL) { + runtime_dir = "."; + fprintf(stderr, + "XDG_RUNTIME_DIR not set, falling back to %s\n", + runtime_dir); + } + + if (name == NULL) + name = getenv("WAYLAND_DISPLAY"); + if (name == NULL) + name = "wayland-0"; + + memset(&s->addr, 0, sizeof s->addr); + s->addr.sun_family = AF_LOCAL; + name_size = snprintf(s->addr.sun_path, sizeof s->addr.sun_path, + "%s/%s", runtime_dir, name) + 1; + fprintf(stderr, "using socket %s\n", s->addr.sun_path); + + if (get_socket_lock(s,name_size) < 0) { + close(s->fd); + free(s); + return -1; + } + + size = offsetof (struct sockaddr_un, sun_path) + name_size; + if (bind(s->fd, (struct sockaddr *) &s->addr, size) < 0) { + close(s->fd); + free(s); + return -1; + } + + if (listen(s->fd, 1) < 0) { + close(s->fd); + unlink(s->addr.sun_path); + free(s); + return -1; + } + + if (wl_event_loop_add_fd(display->loop, s->fd, + WL_EVENT_READABLE, + socket_data, display) == NULL) { + close(s->fd); + unlink(s->addr.sun_path); + free(s); + return -1; + } + wl_list_insert(display->socket_list.prev, &s->link); + + return 0; +} + +static void +compositor_bind(struct wl_client *client, + struct wl_object *global, uint32_t version) +{ + struct wl_compositor *compositor = + container_of(global, struct wl_compositor, object); + + wl_client_post_event(client, global, + WL_COMPOSITOR_TOKEN_VISUAL, + &compositor->argb_visual.object, + WL_COMPOSITOR_VISUAL_ARGB32); + + wl_client_post_event(client, global, + WL_COMPOSITOR_TOKEN_VISUAL, + &compositor->premultiplied_argb_visual.object, + WL_COMPOSITOR_VISUAL_PREMULTIPLIED_ARGB32); + + wl_client_post_event(client, global, + WL_COMPOSITOR_TOKEN_VISUAL, + &compositor->rgb_visual.object, + WL_COMPOSITOR_VISUAL_XRGB32); +} + +WL_EXPORT int +wl_compositor_init(struct wl_compositor *compositor, + const struct wl_compositor_interface *interface, + struct wl_display *display) +{ + compositor->object.interface = &wl_compositor_interface; + compositor->object.implementation = (void (**)(void)) interface; + wl_display_add_object(display, &compositor->object); + if (wl_display_add_global(display, + &compositor->object, compositor_bind)) + return -1; + + compositor->argb_visual.object.interface = &wl_visual_interface; + compositor->argb_visual.object.implementation = NULL; + wl_display_add_object(display, &compositor->argb_visual.object); + if (wl_display_add_global(display, + &compositor->argb_visual.object, NULL)) + return -1; + + compositor->premultiplied_argb_visual.object.interface = + &wl_visual_interface; + compositor->premultiplied_argb_visual.object.implementation = NULL; + wl_display_add_object(display, + &compositor->premultiplied_argb_visual.object); + if (wl_display_add_global(display, + &compositor->premultiplied_argb_visual.object, + NULL)) + return -1; + + compositor->rgb_visual.object.interface = &wl_visual_interface; + compositor->rgb_visual.object.implementation = NULL; + wl_display_add_object(display, &compositor->rgb_visual.object); + if (wl_display_add_global(display, + &compositor->rgb_visual.object, NULL)) + return -1; + + return 0; +} diff --git a/src/wayland-server.h b/src/wayland-server.h new file mode 100644 index 0000000..69d0d51 --- /dev/null +++ b/src/wayland-server.h @@ -0,0 +1,307 @@ +/* + * Copyright © 2008 Kristian Høgsberg + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef WAYLAND_H +#define WAYLAND_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> +#include "wayland-util.h" +#include "wayland-server-protocol.h" + +enum { + WL_EVENT_READABLE = 0x01, + WL_EVENT_WRITEABLE = 0x02 +}; + +struct wl_event_loop; +struct wl_event_source; +typedef int (*wl_event_loop_fd_func_t)(int fd, uint32_t mask, void *data); +typedef int (*wl_event_loop_timer_func_t)(void *data); +typedef int (*wl_event_loop_signal_func_t)(int signal_number, void *data); +typedef void (*wl_event_loop_idle_func_t)(void *data); + +struct wl_event_loop *wl_event_loop_create(void); +void wl_event_loop_destroy(struct wl_event_loop *loop); +struct wl_event_source *wl_event_loop_add_fd(struct wl_event_loop *loop, + int fd, uint32_t mask, + wl_event_loop_fd_func_t func, + void *data); +int wl_event_source_fd_update(struct wl_event_source *source, uint32_t mask); +struct wl_event_source *wl_event_loop_add_timer(struct wl_event_loop *loop, + wl_event_loop_timer_func_t func, + void *data); +struct wl_event_source * +wl_event_loop_add_signal(struct wl_event_loop *loop, + int signal_number, + wl_event_loop_signal_func_t func, + void *data); + +int wl_event_source_timer_update(struct wl_event_source *source, + int ms_delay); +int wl_event_source_remove(struct wl_event_source *source); +void wl_event_source_check(struct wl_event_source *source); + + +int wl_event_loop_dispatch(struct wl_event_loop *loop, int timeout); +struct wl_event_source *wl_event_loop_add_idle(struct wl_event_loop *loop, + wl_event_loop_idle_func_t func, + void *data); +int wl_event_loop_get_fd(struct wl_event_loop *loop); + +struct wl_client; +struct wl_display; +struct wl_input_device; + +struct wl_display *wl_display_create(void); +void wl_display_destroy(struct wl_display *display); +struct wl_event_loop *wl_display_get_event_loop(struct wl_display *display); +int wl_display_add_socket(struct wl_display *display, const char *name); +void wl_display_terminate(struct wl_display *display); +void wl_display_run(struct wl_display *display); + +void wl_display_add_object(struct wl_display *display, + struct wl_object *object); + +typedef void (*wl_global_bind_func_t)(struct wl_client *client, + struct wl_object *global, + uint32_t version); + +int wl_display_add_global(struct wl_display *display, + struct wl_object *object, + wl_global_bind_func_t func); + +int wl_display_remove_global(struct wl_display *display, + struct wl_object *object); + +struct wl_client *wl_client_create(struct wl_display *display, int fd); +void wl_client_destroy(struct wl_client *client); +void wl_client_post_error(struct wl_client *client, struct wl_object *object, + uint32_t code, const char *msg, ...); +void wl_client_post_no_memory(struct wl_client *client); +void wl_client_post_global(struct wl_client *client, struct wl_object *object); +void wl_client_flush(struct wl_client *client); + +struct wl_visual { + struct wl_object object; +}; + +struct wl_shm_callbacks { + void (*buffer_created)(struct wl_buffer *buffer); + + void (*buffer_damaged)(struct wl_buffer *buffer, + int32_t x, int32_t y, + int32_t width, int32_t height); + + void (*buffer_destroyed)(struct wl_buffer *buffer); +}; + +struct wl_compositor { + struct wl_object object; + struct wl_visual argb_visual; + struct wl_visual premultiplied_argb_visual; + struct wl_visual rgb_visual; +}; + +struct wl_resource { + struct wl_object object; + void (*destroy)(struct wl_resource *resource, + struct wl_client *client); + struct wl_list link; + struct wl_list destroy_listener_list; +}; + +struct wl_buffer { + struct wl_resource resource; + struct wl_client *client; + struct wl_visual *visual; + int32_t width, height; + uint32_t busy_count; + void *user_data; +}; + +struct wl_listener { + struct wl_list link; + void (*func)(struct wl_listener *listener, + struct wl_resource *resource, uint32_t time); +}; + +struct wl_surface { + struct wl_resource resource; + struct wl_client *client; +}; + +struct wl_grab; +struct wl_grab_interface { + void (*motion)(struct wl_grab *grab, + uint32_t time, int32_t x, int32_t y); + void (*button)(struct wl_grab *grab, + uint32_t time, int32_t button, int32_t state); + void (*end)(struct wl_grab *grab, uint32_t time); +}; + +struct wl_grab { + const struct wl_grab_interface *interface; + struct wl_input_device *input_device; +}; + +struct wl_input_device { + struct wl_object object; + struct wl_compositor *compositor; + struct wl_surface *pointer_focus; + struct wl_surface *keyboard_focus; + struct wl_array keys; + uint32_t pointer_focus_time; + uint32_t keyboard_focus_time; + struct wl_listener pointer_focus_listener; + struct wl_listener keyboard_focus_listener; + + int32_t x, y; + struct wl_grab *grab; + struct wl_grab motion_grab; + uint32_t grab_time; + int32_t grab_x, grab_y; + uint32_t grab_button; + struct wl_listener grab_listener; +}; + +struct wl_drag_offer { + struct wl_object object; +}; + +struct wl_drag { + struct wl_resource resource; + struct wl_grab grab; + struct wl_drag_offer drag_offer; + struct wl_surface *source; + struct wl_surface *drag_focus; + struct wl_client *target; + int32_t x, y, sx, sy; + struct wl_array types; + const char *type; + uint32_t pointer_focus_time; + struct wl_listener drag_focus_listener; +}; + +struct wl_selection_offer { + struct wl_object object; +}; + +struct wl_selection { + struct wl_resource resource; + struct wl_client *client; + struct wl_input_device *input_device; + struct wl_selection_offer selection_offer; + struct wl_surface *selection_focus; + struct wl_client *target; + struct wl_array types; + struct wl_listener selection_focus_listener; +}; + +void +wl_client_post_event(struct wl_client *client, + struct wl_object *sender, + uint32_t event, ...); + +int +wl_display_set_compositor(struct wl_display *display, + struct wl_compositor *compositor, + const struct wl_compositor_interface *implementation); + +void +wl_display_post_frame(struct wl_display *display, struct wl_surface *surface, + uint32_t msecs); + +void +wl_client_add_resource(struct wl_client *client, + struct wl_resource *resource); + +struct wl_display * +wl_client_get_display(struct wl_client *client); + +void +wl_resource_destroy(struct wl_resource *resource, + struct wl_client *client, uint32_t time); + +void +wl_input_device_init(struct wl_input_device *device, + struct wl_compositor *compositor); + +void +wl_input_device_set_pointer_focus(struct wl_input_device *device, + struct wl_surface *surface, + uint32_t time, + int32_t x, int32_t y, + int32_t sx, int32_t sy); + +void +wl_input_device_set_keyboard_focus(struct wl_input_device *device, + struct wl_surface *surface, + uint32_t time); + +void +wl_input_device_end_grab(struct wl_input_device *device, uint32_t time); +void +wl_input_device_start_grab(struct wl_input_device *device, + struct wl_grab *grab, + uint32_t button, uint32_t time); +int +wl_input_device_update_grab(struct wl_input_device *device, + struct wl_grab *grab, + struct wl_surface *surface, uint32_t time); + +struct wl_shm; + +void * +wl_shm_buffer_get_data(struct wl_buffer *buffer); + +int32_t +wl_shm_buffer_get_stride(struct wl_buffer *buffer); + +struct wl_buffer * +wl_shm_buffer_create(struct wl_shm *shm, int width, int height, + int stride, struct wl_visual *visual, + void *data); + +int +wl_buffer_is_shm(struct wl_buffer *buffer); + +struct wl_shm * +wl_shm_init(struct wl_display *display, + const struct wl_shm_callbacks *callbacks); + +void +wl_shm_finish(struct wl_shm *shm); + +int +wl_compositor_init(struct wl_compositor *compositor, + const struct wl_compositor_interface *interface, + struct wl_display *display); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/wayland-server.pc.in b/src/wayland-server.pc.in new file mode 100644 index 0000000..b317461 --- /dev/null +++ b/src/wayland-server.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: Wayland Server +Description: Server side implementation of the Wayland protocol +Version: 0.1 +Cflags: -I${includedir} +Libs: -L${libdir} -lwayland-server diff --git a/src/wayland-shm.c b/src/wayland-shm.c new file mode 100644 index 0000000..90d2fcf --- /dev/null +++ b/src/wayland-shm.c @@ -0,0 +1,223 @@ +/* + * Copyright © 2008 Kristian Høgsberg + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + * + * Authors: + * Kristian Høgsberg <krh@bitplanet.net> + * Benjamin Franzke <benjaminfranzke@googlemail.com> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <unistd.h> + +#include "wayland-server.h" + +struct wl_shm { + struct wl_object object; + const struct wl_shm_callbacks *callbacks; +}; + +struct wl_shm_buffer { + struct wl_buffer buffer; + struct wl_shm *shm; + int32_t stride; + void *data; +}; + +static void +destroy_buffer(struct wl_resource *resource, struct wl_client *client) +{ + struct wl_shm_buffer *buffer = + container_of(resource, struct wl_shm_buffer, buffer.resource); + + munmap(buffer->data, buffer->stride * buffer->buffer.height); + + buffer->shm->callbacks->buffer_destroyed(&buffer->buffer); + + free(buffer); +} + +static void +shm_buffer_damage(struct wl_client *client, struct wl_buffer *buffer_base, + int32_t x, int32_t y, int32_t width, int32_t height) +{ + struct wl_shm_buffer *buffer = (struct wl_shm_buffer *) buffer_base; + + buffer->shm->callbacks->buffer_damaged(buffer_base, x, y, + width, height); +} + +static void +shm_buffer_destroy(struct wl_client *client, struct wl_buffer *buffer) +{ + wl_resource_destroy(&buffer->resource, client, 0); +} + +const static struct wl_buffer_interface shm_buffer_interface = { + shm_buffer_damage, + shm_buffer_destroy +}; + +static struct wl_shm_buffer * +wl_shm_buffer_init(struct wl_shm *shm, struct wl_client *client, uint32_t id, + int32_t width, int32_t height, + int32_t stride, struct wl_visual *visual, + void *data) +{ + struct wl_shm_buffer *buffer; + + buffer = calloc(1, sizeof *buffer); + if (buffer == NULL) + return NULL; + + buffer->buffer.width = width; + buffer->buffer.height = height; + buffer->buffer.visual = visual; + buffer->buffer.client = client; + buffer->stride = stride; + buffer->data = data; + + buffer->buffer.resource.object.id = id; + buffer->buffer.resource.object.interface = &wl_buffer_interface; + buffer->buffer.resource.object.implementation = (void (**)(void)) + &shm_buffer_interface; + + buffer->buffer.resource.destroy = destroy_buffer; + + buffer->shm = shm; + + buffer->shm->callbacks->buffer_created(&buffer->buffer); + + return buffer; +} + +static void +shm_create_buffer(struct wl_client *client, struct wl_shm *shm, + uint32_t id, int fd, int32_t width, int32_t height, + uint32_t stride, struct wl_visual *visual) +{ + struct wl_shm_buffer *buffer; + void *data; + + if (!visual || visual->object.interface != &wl_visual_interface) { + wl_client_post_error(client, &shm->object, + WL_SHM_ERROR_INVALID_VISUAL, + "invalid visual"); + close(fd); + return; + } + + if (width < 0 || height < 0 || stride < width) { + wl_client_post_error(client, &shm->object, + WL_SHM_ERROR_INVALID_STRIDE, + "invalid width, height or stride (%dx%d, %u)", + width, height, stride); + close(fd); + return; + } + + data = mmap(NULL, stride * height, + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + + close(fd); + if (data == MAP_FAILED) { + wl_client_post_error(client, &shm->object, + WL_SHM_ERROR_INVALID_FD, + "failed mmap fd %d", fd); + return; + } + + buffer = wl_shm_buffer_init(shm, client, id, + width, height, stride, visual, + data); + if (buffer == NULL) { + munmap(data, stride * height); + wl_client_post_no_memory(client); + return; + } + + wl_client_add_resource(client, &buffer->buffer.resource); +} + +const static struct wl_shm_interface shm_interface = { + shm_create_buffer +}; + + +WL_EXPORT struct wl_shm * +wl_shm_init(struct wl_display *display, + const struct wl_shm_callbacks *callbacks) +{ + struct wl_shm *shm; + + shm = malloc(sizeof *shm); + if (!shm) + return NULL; + + shm->object.interface = &wl_shm_interface; + shm->object.implementation = (void (**)(void)) &shm_interface; + wl_display_add_object(display, &shm->object); + wl_display_add_global(display, &shm->object, NULL); + + shm->callbacks = callbacks; + + return shm; +} + +WL_EXPORT void +wl_shm_finish(struct wl_shm *shm) +{ + /* FIXME: add wl_display_del_{object,global} */ + + free(shm); +} + +WL_EXPORT int +wl_buffer_is_shm(struct wl_buffer *buffer) +{ + return buffer->resource.object.implementation == + (void (**)(void)) &shm_buffer_interface; +} + +WL_EXPORT int32_t +wl_shm_buffer_get_stride(struct wl_buffer *buffer_base) +{ + struct wl_shm_buffer *buffer = (struct wl_shm_buffer *) buffer_base; + + if (!wl_buffer_is_shm(buffer_base)) + return 0; + + return buffer->stride; +} + +WL_EXPORT void * +wl_shm_buffer_get_data(struct wl_buffer *buffer_base) +{ + struct wl_shm_buffer *buffer = (struct wl_shm_buffer *) buffer_base; + + if (!wl_buffer_is_shm(buffer_base)) + return NULL; + + return buffer->data; +} diff --git a/src/wayland-util.c b/src/wayland-util.c new file mode 100644 index 0000000..3643274 --- /dev/null +++ b/src/wayland-util.c @@ -0,0 +1,123 @@ +/* + * Copyright © 2008 Kristian Høgsberg + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include "wayland-util.h" + +WL_EXPORT void +wl_list_init(struct wl_list *list) +{ + list->prev = list; + list->next = list; +} + +WL_EXPORT void +wl_list_insert(struct wl_list *list, struct wl_list *elm) +{ + elm->prev = list; + elm->next = list->next; + list->next = elm; + elm->next->prev = elm; +} + +WL_EXPORT void +wl_list_remove(struct wl_list *elm) +{ + elm->prev->next = elm->next; + elm->next->prev = elm->prev; +} + +WL_EXPORT int +wl_list_length(struct wl_list *list) +{ + struct wl_list *e; + int count; + + count = 0; + e = list->next; + while (e != list) { + e = e->next; + count++; + } + + return count; +} + +WL_EXPORT int +wl_list_empty(struct wl_list *list) +{ + return list->next == list; +} + +WL_EXPORT void +wl_array_init(struct wl_array *array) +{ + memset(array, 0, sizeof *array); +} + +WL_EXPORT void +wl_array_release(struct wl_array *array) +{ + free(array->data); +} + +WL_EXPORT void * +wl_array_add(struct wl_array *array, int size) +{ + int alloc; + void *data, *p; + + if (array->alloc > 0) + alloc = array->alloc; + else + alloc = 16; + + while (alloc < array->size + size) + alloc *= 2; + + if (array->alloc < alloc) { + if (array->alloc > 0) + data = realloc(array->data, alloc); + else + data = malloc(alloc); + + if (data == NULL) + return 0; + array->data = data; + array->alloc = alloc; + } + + p = array->data + array->size; + array->size += size; + + return p; +} + +WL_EXPORT void +wl_array_copy(struct wl_array *array, struct wl_array *source) +{ + array->size = 0; + wl_array_add(array, source->size); + memcpy(array->data, source->data, source->size); +} diff --git a/src/wayland-util.h b/src/wayland-util.h new file mode 100644 index 0000000..a9f869a --- /dev/null +++ b/src/wayland-util.h @@ -0,0 +1,152 @@ +/* + * Copyright © 2008 Kristian Høgsberg + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef WAYLAND_UTIL_H +#define WAYLAND_UTIL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <inttypes.h> + +/* GCC visibility */ +#if defined(__GNUC__) && __GNUC__ >= 4 +#define WL_EXPORT __attribute__ ((visibility("default"))) +#else +#define WL_EXPORT +#endif + +#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) +#define ALIGN(n, a) ( ((n) + ((a) - 1)) & ~((a) - 1) ) +#define DIV_ROUNDUP(n, a) ( ((n) + ((a) - 1)) / (a) ) + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +struct wl_message { + const char *name; + const char *signature; + const struct wl_interface **types; +}; + +struct wl_interface { + const char *name; + int version; + int method_count; + const struct wl_message *methods; + int event_count; + const struct wl_message *events; +}; + +struct wl_object { + const struct wl_interface *interface; + void (**implementation)(void); + uint32_t id; +}; + +struct wl_hash_table; +struct wl_hash_table *wl_hash_table_create(void); +void wl_hash_table_destroy(struct wl_hash_table *ht); +void *wl_hash_table_lookup(struct wl_hash_table *ht, uint32_t hash); +int wl_hash_table_insert(struct wl_hash_table *ht, uint32_t hash, void *data); +void wl_hash_table_remove(struct wl_hash_table *ht, uint32_t hash); + +/** + * wl_list - linked list + * + * The list head is of "struct wl_list" type, and must be initialized + * using wl_list_init(). All entries in the list must be of the same + * type. The item type must have a "struct wl_list" member. This + * member will be initialized by wl_list_insert(). There is no need to + * call wl_list_init() on the individual item. To query if the list is + * empty in O(1), use wl_list_empty(). + * + * Let's call the list reference "struct wl_list foo_list", the item type as + * "item_t", and the item member as "struct wl_list link". The following code + * + * The following code will initialize a list: + * + * wl_list_init(foo_list); + * wl_list_insert(foo_list, item1); Pushes item1 at the head + * wl_list_insert(foo_list, item2); Pushes item2 at the head + * wl_list_insert(item2, item3); Pushes item3 after item2 + * + * The list now looks like [item2, item3, item1] + * + * Will iterate the list in ascending order: + * + * item_t *item; + * wl_list_for_each(item, foo_list, link) { + * Do_something_with_item(item); + * } + */ +struct wl_list { + struct wl_list *prev; + struct wl_list *next; +}; + +void wl_list_init(struct wl_list *list); +void wl_list_insert(struct wl_list *list, struct wl_list *elm); +void wl_list_remove(struct wl_list *elm); +int wl_list_length(struct wl_list *list); +int wl_list_empty(struct wl_list *list); + +#define __container_of(ptr, sample, member) \ + (void *)((char *)(ptr) - \ + ((char *)&(sample)->member - (char *)(sample))) + +#define wl_list_for_each(pos, head, member) \ + for (pos = 0, pos = __container_of((head)->next, pos, member); \ + &pos->member != (head); \ + pos = __container_of(pos->member.next, pos, member)) + +#define wl_list_for_each_safe(pos, tmp, head, member) \ + for (pos = 0, tmp = 0, \ + pos = __container_of((head)->next, pos, member), \ + tmp = __container_of((pos)->member.next, tmp, member); \ + &pos->member != (head); \ + pos = tmp, \ + tmp = __container_of(pos->member.next, tmp, member)) + +#define wl_list_for_each_reverse(pos, head, member) \ + for (pos = 0, pos = __container_of((head)->prev, pos, member); \ + &pos->member != (head); \ + pos = __container_of(pos->member.prev, pos, member)) + +struct wl_array { + uint32_t size; + uint32_t alloc; + void *data; +}; + +void wl_array_init(struct wl_array *array); +void wl_array_release(struct wl_array *array); +void *wl_array_add(struct wl_array *array, int size); +void wl_array_copy(struct wl_array *array, struct wl_array *source); + +#ifdef __cplusplus +} +#endif + +#endif |