diff --git a/protocol/wayland.xml b/protocol/wayland.xml index bee74a10..aa0aba1f 100644 --- a/protocol/wayland.xml +++ b/protocol/wayland.xml @@ -3296,4 +3296,18 @@ + + + + + + + + + + + + + + diff --git a/src/connection.c b/src/connection.c index 28d27538..8b7b9a11 100644 --- a/src/connection.c +++ b/src/connection.c @@ -69,6 +69,7 @@ struct wl_connection { struct wl_ring_buffer fds_in, fds_out; int fd; int want_flush; + bool v2; }; static inline size_t @@ -619,6 +620,12 @@ wl_connection_get_fd(struct wl_connection *connection) return connection->fd; } +void +wl_connection_enable_v2(struct wl_connection *connection) +{ + connection->v2 = true; +} + size_t wl_connection_header_size(struct wl_connection *connection) { @@ -631,14 +638,21 @@ wl_connection_parse_header(struct wl_connection *connection, uint32_t *header, uint32_t *sender_id, int *size, - int *opcode) + int *opcode, + int *num_fds) { *sender_id = header[0]; uint32_t field2 = header[1]; uint32_t field2_hi = field2 >> 16; uint32_t field2_lo = field2 & 0xffff; *size = (int)field2_hi; - *opcode = (int)field2_lo; + if (connection->v2) { + *opcode = (int)(field2_lo >> 8); + *num_fds = (int)(field2_lo & 0xff); + } else { + *opcode = (int)field2_lo; + *num_fds = -1; + } } static int @@ -807,6 +821,7 @@ wl_closure_init(const struct wl_message *message, uint32_t size, closure->message = message; closure->count = count; + closure->num_fds = -1; /* Set these all to -1 so we can close any that have been * set to a real value during wl_closure_destroy(). @@ -826,7 +841,7 @@ wl_closure_marshal(struct wl_object *sender, uint32_t opcode, { struct wl_closure *closure; struct wl_object *object; - int i, count, fd, dup_fd; + int i, count, fd, dup_fd, num_fds = 0; const char *signature; struct argument_details arg; @@ -874,6 +889,7 @@ wl_closure_marshal(struct wl_object *sender, uint32_t opcode, return NULL; } closure->args[i].h = dup_fd; + num_fds += 1; break; default: wl_abort("unhandled format code: '%c'\n", arg.type); @@ -883,6 +899,7 @@ wl_closure_marshal(struct wl_object *sender, uint32_t opcode, closure->sender_id = sender->id; closure->opcode = opcode; + closure->num_fds = num_fds; return closure; @@ -943,7 +960,7 @@ wl_connection_demarshal(struct wl_connection *connection, end = p + size / sizeof *p; wl_connection_copy(connection, p, size); - wl_connection_parse_header(connection, p, &closure->sender_id, &ssize, &opcode); + wl_connection_parse_header(connection, p, &closure->sender_id, &ssize, &opcode, &closure->num_fds); closure->opcode = (uint32_t)opcode; p += wl_connection_header_size(connection) / sizeof *p; @@ -1438,7 +1455,11 @@ serialize_closure(struct wl_connection *connection, struct wl_closure *closure, size = (p - buffer) * sizeof *p; buffer[0] = closure->sender_id; + if (connection->v2) { + buffer[1] = size << 16 | ((closure->opcode << 8) & 0x0000ff00) | (closure->num_fds & 0x000000ff); + } else { buffer[1] = size << 16 | (closure->opcode & 0x0000ffff); + } return size; diff --git a/src/wayland-client.c b/src/wayland-client.c index aa04ed70..e7f1aebb 100644 --- a/src/wayland-client.c +++ b/src/wayland-client.c @@ -1214,6 +1214,118 @@ connect_to_socket(const char *name) return fd; } +struct upgrade_data { + struct wl_fixes **fixes; + struct wl_upgrade **upgrade; + bool *upgraded; +}; + +static void upgrade_global(void *data, + struct wl_registry *wl_registry, + uint32_t name, + const char *interface, + uint32_t version) +{ + struct upgrade_data *upgrade = data; + if (strcmp(interface, "wl_fixes") == 0) { + *upgrade->fixes = wl_registry_bind(wl_registry, name, &wl_fixes_interface, 1); + } else if (strcmp(interface, "wl_upgrade") == 0) { + *upgrade->upgrade = wl_registry_bind(wl_registry, name, &wl_upgrade_interface, 1); + } +} + +static void upgrade_global_removed(void *data, + struct wl_registry *wl_registry, + uint32_t name) +{ + // nothing +} + +static const struct wl_registry_listener upgrade_registry_listener = { + .global = upgrade_global, + .global_remove = upgrade_global_removed, +}; + +static void upgrade_upgraded(void *data, + struct wl_upgrade *wl_upgrade) +{ + struct upgrade_data *upgrade = data; + *upgrade->upgraded = true; +} + +static const struct wl_upgrade_listener upgrade_upgrade_listener = { + .upgraded = upgrade_upgraded, +}; + +static void +try_upgrade(struct wl_display *display) +{ + struct wl_event_queue *queue = NULL; + struct wl_display *display_wrapper = NULL; + struct wl_registry *registry = NULL; + struct wl_fixes *fixes = NULL; + struct wl_upgrade *upgrade = NULL; + bool upgraded = false; + struct upgrade_data data = { + .fixes = &fixes, + .upgrade = &upgrade, + .upgraded = &upgraded, + }; + + queue = wl_display_create_queue_with_name(display, "Upgrade Queue"); + if (!queue) { + goto out; + } + display_wrapper = wl_proxy_create_wrapper(display); + if (!display_wrapper) { + goto out; + } + wl_proxy_set_queue((struct wl_proxy *)display_wrapper, queue); + registry = wl_display_get_registry(display_wrapper); + if (!registry) { + goto out; + } + wl_registry_add_listener(registry, &upgrade_registry_listener, &data); + wl_display_roundtrip_queue(display, queue); + if (!upgrade) { + goto out; + } + assert(fixes); + wl_fixes_destroy_registry(fixes, registry); + wl_registry_destroy(registry); + registry = NULL; + wl_fixes_destroy(fixes); + fixes = NULL; + wl_upgrade_add_listener(upgrade, &upgrade_upgrade_listener, &data); + wl_upgrade_upgrade(upgrade); + while (!upgraded) { + if (wl_display_dispatch_queue(display, queue) == -1) { + goto out; + } + } + wl_connection_enable_v2(display->connection); + +out: + if (upgrade) { + wl_upgrade_destroy(upgrade); + } + if (registry) { + if (fixes) { + wl_fixes_destroy_registry(fixes, registry); + } + wl_registry_destroy(registry); + } + if (fixes) { + wl_fixes_destroy(fixes); + } + if (display_wrapper) { + wl_proxy_wrapper_destroy(display_wrapper); + } + if (queue) { + wl_event_queue_release(queue); + } +} + /** Connect to Wayland display on an already open fd * * \param fd The fd to use for the connection @@ -1302,6 +1414,8 @@ wl_display_connect_to_fd(int fd) if (display->connection == NULL) goto err_connection; + try_upgrade(display); + return display; err_connection: @@ -1575,7 +1689,7 @@ queue_event(struct wl_display *display, int len) int num_zombie_fds; wl_connection_copy(display->connection, p, wl_connection_header_size(display->connection)); - wl_connection_parse_header(display->connection, p, &id, &size, &opcode); + wl_connection_parse_header(display->connection, p, &id, &size, &opcode, &num_zombie_fds); /* * If the message is larger than the maximum size of the diff --git a/src/wayland-private.h b/src/wayland-private.h index b2e14a83..7fd62657 100644 --- a/src/wayland-private.h +++ b/src/wayland-private.h @@ -166,6 +166,9 @@ wl_connection_queue(struct wl_connection *connection, int wl_connection_get_fd(struct wl_connection *connection); +void +wl_connection_enable_v2(struct wl_connection *connection); + size_t wl_connection_header_size(struct wl_connection *connection); @@ -174,13 +177,15 @@ wl_connection_parse_header(struct wl_connection *connection, uint32_t *header, uint32_t *sender_id, int *size, - int *opcode); + int *opcode, + int *num_fds); struct wl_closure { int count; const struct wl_message *message; uint32_t opcode; uint32_t sender_id; + int32_t num_fds; union wl_argument args[WL_CLOSURE_MAX_ARGS]; struct wl_list link; struct wl_proxy *proxy; diff --git a/src/wayland-server.c b/src/wayland-server.c index cd954f33..a8931399 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -360,7 +360,7 @@ wl_client_connection_data(int fd, uint32_t mask, void *data) uint32_t p[WL_MAX_HEADER_U32]; uint32_t resource_flags; uint32_t object_id; - int opcode, size, since; + int opcode, size, since, num_fds; int len; size_t header_size = wl_connection_header_size(connection); @@ -398,7 +398,7 @@ wl_client_connection_data(int fd, uint32_t mask, void *data) while (len >= 0 && (size_t) len >= header_size) { wl_connection_copy(connection, p, header_size); - wl_connection_parse_header(connection, p, &object_id, &size, &opcode); + wl_connection_parse_header(connection, p, &object_id, &size, &opcode, &num_fds); /* * If the message is larger than the maximum size of the