summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKristian Høgsberg <krh@bitplanet.net>2010-08-30 09:47:36 -0400
committerKristian Høgsberg <krh@bitplanet.net>2010-08-30 09:47:36 -0400
commit45b7789a8b59d86d35ff29acb1b1d48a8e666f6e (patch)
tree09dacc23a5718446a175f9f1e263484c2b4a7cd5
parent8ca1cc20a1db53f670ed5d53b0b74287c0c8d8ba (diff)
Return a closure object from wl_connection_demarshal
This way we can invoke it multiple times without having to demarshal and build the cfi every time.
-rw-r--r--TODO4
-rw-r--r--connection.c169
-rw-r--r--connection.h17
-rw-r--r--wayland-client.c28
-rw-r--r--wayland-server.c30
5 files changed, 143 insertions, 105 deletions
diff --git a/TODO b/TODO
index 3fe9b99..8f8486a 100644
--- a/TODO
+++ b/TODO
@@ -1,5 +1,9 @@
Core wayland protocol
+ - The message format has to include information about number of fds
+ in the message so we can skip a message correctly. Or we should
+ just give up on trying to recover from unknown messages.
+
- generate pointer_focus (and drag focus) on raise/lower, move
windows, all kinds of changes in surface stacking.
diff --git a/connection.c b/connection.c
index cd9a45d..8d4f19f 100644
--- a/connection.c
+++ b/connection.c
@@ -45,6 +45,21 @@ struct wl_buffer {
#define MASK(i) ((i) & 4095)
+struct wl_closure {
+ int count;
+ const struct wl_message *message;
+ ffi_type *types[20];
+ ffi_cif cif;
+ union {
+ uint32_t uint32;
+ char *string;
+ void *object;
+ uint32_t new_id;
+ struct wl_array *array;
+ } values[20];
+ void *args[20];
+};
+
struct wl_connection {
struct wl_buffer in, out;
struct wl_buffer fds_in, fds_out;
@@ -52,6 +67,7 @@ struct wl_connection {
int fd;
void *data;
wl_connection_update_func_t update;
+ struct wl_closure closure;
};
static void
@@ -380,31 +396,20 @@ wl_connection_vmarshal(struct wl_connection *connection,
wl_connection_write(connection, args, size);
}
-int
+struct wl_closure *
wl_connection_demarshal(struct wl_connection *connection,
uint32_t size,
struct wl_hash_table *objects,
- void (*func)(void),
- void *data, struct wl_object *target,
const struct wl_message *message)
{
- ffi_type *types[20];
- ffi_cif cif;
- uint32_t *p, *next, *end, result, length;
- int i, count, fds_tail, ret = 0;
- union {
- uint32_t uint32;
- char *string;
- void *object;
- uint32_t new_id;
- struct wl_array *array;
- } values[20];
- void *args[20];
+ uint32_t *p, *next, *end, length;
+ int i, count;
struct wl_object *object;
uint32_t buffer[64];
+ struct wl_closure *closure = &connection->closure;
count = strlen(message->signature) + 2;
- if (count > ARRAY_LENGTH(types)) {
+ if (count > ARRAY_LENGTH(closure->types)) {
printf("too many args (%d)\n", count);
assert(0);
}
@@ -414,36 +419,33 @@ wl_connection_demarshal(struct wl_connection *connection,
assert(0);
}
- types[0] = &ffi_type_pointer;
- values[0].object = data;
- args[0] = &values[0];
+ closure->message = message;
+ closure->types[0] = &ffi_type_pointer;
+ closure->args[0] = &closure->values[0];
- types[1] = &ffi_type_pointer;
- values[1].object = target;
- args[1] = &values[1];
+ closure->types[1] = &ffi_type_pointer;
+ closure->args[1] = &closure->values[1];
wl_connection_copy(connection, buffer, size);
p = &buffer[2];
end = (uint32_t *) ((char *) (p + size));
- fds_tail = connection->fds_in.tail;
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;
- ret = -1;
- goto out;
+ goto err;
}
switch (message->signature[i - 2]) {
case 'u':
case 'i':
- types[i] = &ffi_type_uint32;
- values[i].uint32 = *p++;
+ closure->types[i] = &ffi_type_uint32;
+ closure->values[i].uint32 = *p++;
break;
case 's':
- types[i] = &ffi_type_pointer;
+ closure->types[i] = &ffi_type_pointer;
length = *p++;
next = p + DIV_ROUNDUP(length, sizeof *p);
@@ -452,53 +454,49 @@ wl_connection_demarshal(struct wl_connection *connection,
"object (%d), message %s(%s)\n",
*p, message->name, message->signature);
errno = EINVAL;
- ret = -1;
- goto out;
+ goto err;
}
if (length == 0) {
- values[i].string = NULL;
+ closure->values[i].string = NULL;
} else {
- values[i].string = malloc(length + 1);
- if (values[i].string == NULL) {
+ closure->values[i].string = malloc(length + 1);
+ if (closure->values[i].string == NULL) {
errno = ENOMEM;
- ret = -1;
- goto out;
+ goto err;
}
- memcpy(values[i].string, p, length);
- values[i].string[length] = '\0';
+ memcpy(closure->values[i].string, p, length);
+ closure->values[i].string[length] = '\0';
}
p = next;
break;
case 'o':
- types[i] = &ffi_type_pointer;
+ closure->types[i] = &ffi_type_pointer;
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;
- ret = -1;
- goto out;
+ goto err;
}
- values[i].object = object;
+ closure->values[i].object = object;
p++;
break;
case 'n':
- types[i] = &ffi_type_uint32;
- values[i].new_id = *p;
+ closure->types[i] = &ffi_type_uint32;
+ closure->values[i].new_id = *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;
- ret = -1;
- goto out;
+ goto err;
}
p++;
break;
case 'a':
- types[i] = &ffi_type_pointer;
+ closure->types[i] = &ffi_type_pointer;
length = *p++;
next = p + DIV_ROUNDUP(length, sizeof *p);
@@ -507,58 +505,77 @@ wl_connection_demarshal(struct wl_connection *connection,
"object (%d), message %s(%s)\n",
*p, message->name, message->signature);
errno = EINVAL;
- ret = -1;
- goto out;
+ goto err;
}
- values[i].array =
- malloc(length + sizeof *values[i].array);
- if (values[i].array == NULL) {
+ closure->values[i].array =
+ malloc(length + sizeof *closure->values[i].array);
+ if (closure->values[i].array == NULL) {
errno = ENOMEM;
- ret = -1;
- goto out;
+ goto err;
}
- values[i].array->size = length;
- values[i].array->alloc = 0;
- values[i].array->data = values[i].array + 1;
- memcpy(values[i].array->data, p, length);
+ closure->values[i].array->size = length;
+ closure->values[i].array->alloc = 0;
+ closure->values[i].array->data = closure->values[i].array + 1;
+ memcpy(closure->values[i].array->data, p, length);
p = next;
break;
case 'h':
- types[i] = &ffi_type_uint32;
+ closure->types[i] = &ffi_type_uint32;
wl_buffer_copy(&connection->fds_in,
- &values[i].uint32,
- sizeof values[i].uint32);
- connection->fds_in.tail += sizeof values[i].uint32;
+ &closure->values[i].uint32,
+ sizeof closure->values[i].uint32);
+ connection->fds_in.tail +=
+ sizeof closure->values[i].uint32;
break;
default:
printf("unknown type\n");
assert(0);
break;
}
- args[i] = &values[i];
+ closure->args[i] = &closure->values[i];
}
- ffi_prep_cif(&cif, FFI_DEFAULT_ABI, count, &ffi_type_uint32, types);
- ffi_call(&cif, func, &result, args);
+ closure->count = i;
+ ffi_prep_cif(&closure->cif, FFI_DEFAULT_ABI,
+ closure->count, &ffi_type_uint32, closure->types);
- /* Slight hack here. We store the tail of fds_in here and
- * consume will set fds_in.tail to that value */
- connection->fds_in_tail = connection->fds_in.tail;
- connection->fds_in.tail = fds_tail;
+ wl_connection_consume(connection, size);
- out:
- count = i;
- for (i = 2; i < count; i++) {
- switch (message->signature[i - 2]) {
+ return closure;
+
+ err:
+ closure->count = i;
+ wl_closure_destroy(closure);
+
+ return NULL;
+}
+
+void
+wl_closure_invoke(struct wl_closure *closure,
+ struct wl_object *target, void (*func)(void), void *data)
+{
+ int result;
+
+ closure->values[0].object = data;
+ closure->values[1].object = target;
+
+ ffi_call(&closure->cif, func, &result, closure->args);
+}
+
+void
+wl_closure_destroy(struct wl_closure *closure)
+{
+ int i;
+
+ for (i = 2; i < closure->count; i++) {
+ switch (closure->message->signature[i - 2]) {
case 's':
- free(values[i].string);
+ free(closure->values[i].string);
break;
case 'a':
- free(values[i].array);
+ free(closure->values[i].array);
break;
}
}
-
- return ret;
}
diff --git a/connection.h b/connection.h
index 7f65e87..5f28888 100644
--- a/connection.h
+++ b/connection.h
@@ -27,6 +27,7 @@
#include "wayland-util.h"
struct wl_connection;
+struct wl_closure;
#define WL_CONNECTION_READABLE 0x01
#define WL_CONNECTION_WRITABLE 0x02
@@ -48,11 +49,15 @@ void wl_connection_vmarshal(struct wl_connection *connection,
uint32_t opcode, va_list ap,
const struct wl_message *message);
-int wl_connection_demarshal(struct wl_connection *connection,
- uint32_t size,
- struct wl_hash_table *objects,
- void (*func)(void),
- void *data, struct wl_object *target,
- 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_destroy(struct wl_closure *closure);
#endif
diff --git a/wayland-client.c b/wayland-client.c
index c79cc65..3dc5544 100644
--- a/wayland-client.c
+++ b/wayland-client.c
@@ -340,6 +340,8 @@ handle_event(struct wl_display *display,
uint32_t p[32];
struct wl_listener *listener;
struct wl_proxy *proxy;
+ struct wl_closure *closure;
+ struct wl_message *message;
wl_connection_copy(display->connection, p, size);
if (id == 1)
@@ -347,17 +349,21 @@ handle_event(struct wl_display *display,
else
proxy = wl_hash_table_lookup(display->objects, id);
- if (proxy != NULL)
- wl_list_for_each(listener, &proxy->listener_list, link)
- wl_connection_demarshal(display->connection,
- size,
- display->objects,
- listener->implementation[opcode],
- listener->data,
- &proxy->base,
- &proxy->base.interface->events[opcode]);
-
- wl_connection_consume(display->connection, size);
+ if (proxy == NULL) {
+ wl_connection_consume(display->connection, size);
+ return;
+ }
+
+ message = &proxy->base.interface->events[opcode];
+ closure = wl_connection_demarshal(display->connection,
+ size, display->objects, message);
+
+ wl_list_for_each(listener, &proxy->listener_list, link)
+ wl_closure_invoke(closure, &proxy->base,
+ listener->implementation[opcode],
+ listener->data);
+
+ wl_closure_destroy(closure);
}
WL_EXPORT void
diff --git a/wayland-server.c b/wayland-server.c
index 196e67f..869bdd9 100644
--- a/wayland-server.c
+++ b/wayland-server.c
@@ -86,9 +86,11 @@ 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, ret;
+ int len;
if (mask & WL_EVENT_READABLE)
cmask |= WL_CONNECTION_READABLE;
@@ -125,24 +127,28 @@ wl_client_connection_data(int fd, uint32_t mask, void *data)
continue;
}
- ret = wl_connection_demarshal(client->connection,
- size,
- client->display->objects,
- object->implementation[opcode],
- client,
- object,
- &object->interface->methods[opcode]);
+ message = &object->interface->methods[opcode];
+ closure = wl_connection_demarshal(client->connection, size,
+ client->display->objects,
+ message);
+ len -= size;
- if (ret < 0 && errno == EINVAL)
+ if (closure == NULL && errno == EINVAL) {
wl_client_post_event(client, &client->display->base,
WL_DISPLAY_INVALID_METHOD,
p[0], opcode);
- if (ret < 0 && errno == ENOMEM)
+ continue;
+ } else if (closure == NULL && errno == ENOMEM) {
wl_client_post_event(client, &client->display->base,
WL_DISPLAY_NO_MEMORY);
+ continue;
+ }
+
+ wl_closure_invoke(closure, object,
+ object->implementation[opcode], client);
+
+ wl_closure_destroy(closure);
- wl_connection_consume(connection, size);
- len -= size;
}
}