diff options
author | Jørgen Lind <jorgen.lind@nokia.com> | 2011-03-02 13:26:40 +0100 |
---|---|---|
committer | Jørgen Lind <jorgen.lind@nokia.com> | 2011-03-02 13:46:19 +0100 |
commit | 1691e9ed575bf21121da522acb388f99c4771fc8 (patch) | |
tree | 830fedad558bb531c80ec47cd2e63ec1b9f3a840 /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.c | 729 |
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] = ⌖ + + 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) +{ +} |