summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/wayland/connection.c
diff options
context:
space:
mode:
authorJørgen Lind <jorgen.lind@nokia.com>2011-03-02 13:26:40 +0100
committerJørgen Lind <jorgen.lind@nokia.com>2011-03-02 13:46:19 +0100
commit1691e9ed575bf21121da522acb388f99c4771fc8 (patch)
tree830fedad558bb531c80ec47cd2e63ec1b9f3a840 /src/3rdparty/wayland/connection.c
Initial commit for the Qt-Compositor.
The project goal is to be a toolbox which all Qt based Wayland compositors use. Initial work is done by: Samuel Rødal <samuel.rodal@nokia.com> Paul Olav Tvete <paul.tvete@nokia.com> and myself
Diffstat (limited to 'src/3rdparty/wayland/connection.c')
-rw-r--r--src/3rdparty/wayland/connection.c729
1 files changed, 729 insertions, 0 deletions
diff --git a/src/3rdparty/wayland/connection.c b/src/3rdparty/wayland/connection.c
new file mode 100644
index 000000000..4a004705b
--- /dev/null
+++ b/src/3rdparty/wayland/connection.c
@@ -0,0 +1,729 @@
+/*
+ * 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 <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 "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);
+ 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, 0);
+ } 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)
+{
+ 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 (uint32_t);
+ 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 = dup(fd);
+ 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);
+ assert(0);
+ }
+
+ extra_space = wl_message_size_extra(message);
+ if (sizeof closure->buffer < size + extra_space) {
+ printf("request too big, should malloc tmp buffer here\n");
+ assert(0);
+ }
+
+ 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] = &target;
+
+ 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)
+{
+ union wl_value *value;
+ int i;
+
+ fprintf(stderr, "%s@%d.%s(",
+ 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)
+{
+}