mirror of
https://gitlab.freedesktop.org/wayland/wayland.git
synced 2025-10-29 05:40:16 -04:00
Merge branch 'jorth/wl-upgrade' into 'main'
Draft: Implement transparent wire format upgrade See merge request wayland/wayland!492
This commit is contained in:
commit
788f16a300
5 changed files with 228 additions and 31 deletions
|
|
@ -3296,4 +3296,18 @@
|
|||
</request>
|
||||
</interface>
|
||||
|
||||
<interface name="wl_upgrade" version="1">
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroys this object"/>
|
||||
</request>
|
||||
|
||||
<request name="upgrade">
|
||||
<description summary=""/>
|
||||
</request>
|
||||
|
||||
<event name="upgraded">
|
||||
<description summary=""/>
|
||||
</event>
|
||||
</interface>
|
||||
|
||||
</protocol>
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ struct wl_connection {
|
|||
struct wl_ring_buffer fds_in, fds_out;
|
||||
int fd;
|
||||
int want_flush;
|
||||
bool v2;
|
||||
};
|
||||
|
||||
static inline size_t
|
||||
|
|
@ -621,6 +622,41 @@ 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)
|
||||
{
|
||||
// Adjust WL_HEADER_MAX_SIZE when increasing this.
|
||||
return 8;
|
||||
}
|
||||
|
||||
void
|
||||
wl_connection_parse_header(struct wl_connection *connection,
|
||||
uint32_t *header,
|
||||
uint32_t *sender_id,
|
||||
int *size,
|
||||
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;
|
||||
if (connection->v2) {
|
||||
*opcode = (int)(field2_lo >> 8);
|
||||
*num_fds = (int)(field2_lo & 0xff);
|
||||
} else {
|
||||
*opcode = (int)field2_lo;
|
||||
*num_fds = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
wl_connection_put_fd(struct wl_connection *connection, int32_t fd)
|
||||
{
|
||||
|
|
@ -787,6 +823,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().
|
||||
|
|
@ -806,7 +843,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;
|
||||
|
||||
|
|
@ -854,6 +891,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);
|
||||
|
|
@ -863,6 +901,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;
|
||||
|
||||
|
|
@ -896,14 +935,14 @@ wl_connection_demarshal(struct wl_connection *connection,
|
|||
uint32_t *p, *next, *end, length, length_in_u32, id;
|
||||
int fd;
|
||||
char *s;
|
||||
int i, count, num_arrays;
|
||||
int i, count, num_arrays, ssize, opcode;
|
||||
const char *signature;
|
||||
struct argument_details arg;
|
||||
struct wl_closure *closure;
|
||||
struct wl_array *array_extra;
|
||||
|
||||
/* Space for sender_id and opcode */
|
||||
if (size < 2 * sizeof *p) {
|
||||
if (size < wl_connection_header_size(connection)) {
|
||||
wl_log("message too short, invalid header\n");
|
||||
wl_connection_consume(connection, size);
|
||||
errno = EINVAL;
|
||||
|
|
@ -923,8 +962,9 @@ wl_connection_demarshal(struct wl_connection *connection,
|
|||
end = p + size / sizeof *p;
|
||||
|
||||
wl_connection_copy(connection, p, size);
|
||||
closure->sender_id = *p++;
|
||||
closure->opcode = *p++ & 0x0000ffff;
|
||||
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;
|
||||
|
||||
signature = message->signature;
|
||||
for (i = 0; i < count; i++) {
|
||||
|
|
@ -1285,7 +1325,7 @@ copy_fds_to_connection(struct wl_closure *closure,
|
|||
|
||||
|
||||
static uint32_t
|
||||
buffer_size_for_closure(struct wl_closure *closure)
|
||||
buffer_size_for_closure(struct wl_connection *connection, struct wl_closure *closure)
|
||||
{
|
||||
const struct wl_message *message = closure->message;
|
||||
int i, count;
|
||||
|
|
@ -1331,23 +1371,24 @@ buffer_size_for_closure(struct wl_closure *closure)
|
|||
}
|
||||
}
|
||||
|
||||
return buffer_size + 2;
|
||||
return buffer_size + wl_connection_header_size(connection) / sizeof(uint32_t);
|
||||
}
|
||||
|
||||
static int
|
||||
serialize_closure(struct wl_closure *closure, uint32_t *buffer,
|
||||
size_t buffer_count)
|
||||
serialize_closure(struct wl_connection *connection, struct wl_closure *closure,
|
||||
uint32_t *buffer, size_t buffer_count)
|
||||
{
|
||||
const struct wl_message *message = closure->message;
|
||||
unsigned int i, count, size;
|
||||
uint32_t *p, *end;
|
||||
struct argument_details arg;
|
||||
const char *signature;
|
||||
uint32_t header_words = wl_connection_header_size(connection) / sizeof(*buffer);
|
||||
|
||||
if (buffer_count < 2)
|
||||
if (buffer_count < header_words)
|
||||
goto overflow;
|
||||
|
||||
p = buffer + 2;
|
||||
p = buffer + header_words;
|
||||
end = buffer + buffer_count;
|
||||
|
||||
signature = message->signature;
|
||||
|
|
@ -1416,7 +1457,11 @@ serialize_closure(struct wl_closure *closure, uint32_t *buffer,
|
|||
size = (p - buffer) * sizeof *p;
|
||||
|
||||
buffer[0] = closure->sender_id;
|
||||
buffer[1] = size << 16 | (closure->opcode & 0x0000ffff);
|
||||
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;
|
||||
|
||||
|
|
@ -1438,7 +1483,7 @@ wl_closure_send(struct wl_closure *closure, struct wl_connection *connection)
|
|||
if (copy_fds_to_connection(closure, connection))
|
||||
return -1;
|
||||
|
||||
buffer_size = buffer_size_for_closure(closure);
|
||||
buffer_size = buffer_size_for_closure(connection, closure);
|
||||
buffer = zalloc(buffer_size * sizeof buffer[0]);
|
||||
if (buffer == NULL) {
|
||||
wl_log("wl_closure_send error: buffer allocation failure of "
|
||||
|
|
@ -1448,7 +1493,7 @@ wl_closure_send(struct wl_closure *closure, struct wl_connection *connection)
|
|||
return -1;
|
||||
}
|
||||
|
||||
size = serialize_closure(closure, buffer, buffer_size);
|
||||
size = serialize_closure(connection, closure, buffer, buffer_size);
|
||||
if (size < 0) {
|
||||
free(buffer);
|
||||
return -1;
|
||||
|
|
@ -1471,7 +1516,7 @@ wl_closure_queue(struct wl_closure *closure, struct wl_connection *connection)
|
|||
if (copy_fds_to_connection(closure, connection))
|
||||
return -1;
|
||||
|
||||
buffer_size = buffer_size_for_closure(closure);
|
||||
buffer_size = buffer_size_for_closure(connection, closure);
|
||||
buffer = malloc(buffer_size * sizeof buffer[0]);
|
||||
if (buffer == NULL) {
|
||||
wl_log("wl_closure_queue error: buffer allocation failure of "
|
||||
|
|
@ -1481,7 +1526,7 @@ wl_closure_queue(struct wl_closure *closure, struct wl_connection *connection)
|
|||
return -1;
|
||||
}
|
||||
|
||||
size = serialize_closure(closure, buffer, buffer_size);
|
||||
size = serialize_closure(connection, closure, buffer, buffer_size);
|
||||
if (size < 0) {
|
||||
free(buffer);
|
||||
return -1;
|
||||
|
|
@ -1657,6 +1702,13 @@ wl_closure_print(struct wl_closure *closure, struct wl_object *target,
|
|||
}
|
||||
}
|
||||
|
||||
if (closure->num_fds > 0) {
|
||||
if (closure->count) {
|
||||
fprintf(f, ", ");
|
||||
}
|
||||
fprintf(f, "num_fds = %d", closure->num_fds);
|
||||
}
|
||||
|
||||
fprintf(f, ")%s\n", color ? WL_DEBUG_COLOR_RESET : "");
|
||||
|
||||
if (fclose(f) == 0) {
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
@ -1564,7 +1678,7 @@ increase_closure_args_refcount(struct wl_closure *closure)
|
|||
static int
|
||||
queue_event(struct wl_display *display, int len)
|
||||
{
|
||||
uint32_t p[2], id;
|
||||
uint32_t p[WL_MAX_HEADER_U32], id;
|
||||
int opcode, size;
|
||||
struct wl_proxy *proxy;
|
||||
struct wl_closure *closure;
|
||||
|
|
@ -1574,10 +1688,8 @@ queue_event(struct wl_display *display, int len)
|
|||
unsigned int time;
|
||||
int num_zombie_fds;
|
||||
|
||||
wl_connection_copy(display->connection, p, sizeof p);
|
||||
id = p[0];
|
||||
opcode = p[1] & 0xffff;
|
||||
size = p[1] >> 16;
|
||||
wl_connection_copy(display->connection, p, wl_connection_header_size(display->connection));
|
||||
wl_connection_parse_header(display->connection, p, &id, &size, &opcode, &num_zombie_fds);
|
||||
|
||||
/*
|
||||
* If the message is larger than the maximum size of the
|
||||
|
|
@ -1736,6 +1848,7 @@ read_events(struct wl_display *display)
|
|||
{
|
||||
int total, rem, size;
|
||||
uint32_t serial;
|
||||
int header_size = wl_connection_header_size(display->connection);
|
||||
|
||||
display->reader_count--;
|
||||
if (display->reader_count == 0) {
|
||||
|
|
@ -1760,7 +1873,7 @@ read_events(struct wl_display *display)
|
|||
return -1;
|
||||
}
|
||||
|
||||
for (rem = total; rem >= 8; rem -= size) {
|
||||
for (rem = total; rem >= header_size; rem -= size) {
|
||||
size = queue_event(display, rem);
|
||||
if (size == -1) {
|
||||
display_fatal_error(display, errno);
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@
|
|||
#define WL_CLOSURE_MAX_ARGS 20
|
||||
#define WL_BUFFER_DEFAULT_SIZE_POT 12
|
||||
#define WL_BUFFER_DEFAULT_MAX_SIZE (1 << WL_BUFFER_DEFAULT_SIZE_POT)
|
||||
#define WL_MAX_HEADER_SIZE 8
|
||||
#define WL_MAX_HEADER_U32 (WL_MAX_HEADER_SIZE / 4)
|
||||
#if WL_BUFFER_DEFAULT_MAX_SIZE < WL_MAX_MESSAGE_SIZE
|
||||
# error default buffer cannot hold maximum-sized message
|
||||
#endif
|
||||
|
|
@ -164,11 +166,26 @@ 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);
|
||||
|
||||
void
|
||||
wl_connection_parse_header(struct wl_connection *connection,
|
||||
uint32_t *header,
|
||||
uint32_t *sender_id,
|
||||
int *size,
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -357,10 +357,12 @@ wl_client_connection_data(int fd, uint32_t mask, void *data)
|
|||
struct wl_object *object;
|
||||
struct wl_closure *closure;
|
||||
const struct wl_message *message;
|
||||
uint32_t p[2];
|
||||
uint32_t p[WL_MAX_HEADER_U32];
|
||||
uint32_t resource_flags;
|
||||
int opcode, size, since;
|
||||
uint32_t object_id;
|
||||
int opcode, size, since, num_fds;
|
||||
int len;
|
||||
size_t header_size = wl_connection_header_size(connection);
|
||||
|
||||
if (mask & WL_EVENT_HANGUP) {
|
||||
wl_client_destroy(client);
|
||||
|
|
@ -394,10 +396,9 @@ wl_client_connection_data(int fd, uint32_t mask, void *data)
|
|||
}
|
||||
}
|
||||
|
||||
while (len >= 0 && (size_t) len >= sizeof p) {
|
||||
wl_connection_copy(connection, p, sizeof p);
|
||||
opcode = p[1] & 0xffff;
|
||||
size = p[1] >> 16;
|
||||
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, &num_fds);
|
||||
|
||||
/*
|
||||
* If the message is larger than the maximum size of the
|
||||
|
|
@ -424,12 +425,12 @@ wl_client_connection_data(int fd, uint32_t mask, void *data)
|
|||
if (len < size)
|
||||
break;
|
||||
|
||||
resource = wl_map_lookup(&client->objects, p[0]);
|
||||
resource_flags = wl_map_lookup_flags(&client->objects, p[0]);
|
||||
resource = wl_map_lookup(&client->objects, object_id);
|
||||
resource_flags = wl_map_lookup_flags(&client->objects, object_id);
|
||||
if (resource == NULL) {
|
||||
wl_resource_post_error(client->display_resource,
|
||||
WL_DISPLAY_ERROR_INVALID_OBJECT,
|
||||
"invalid object %u", p[0]);
|
||||
"invalid object %u", object_id);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue