Add new header format version

Signed-off-by: Julian Orth <ju.orth@gmail.com>
This commit is contained in:
Julian Orth 2025-09-11 14:08:26 +02:00
parent 8d54c4e22d
commit 5f4add98a2
5 changed files with 162 additions and 8 deletions

View file

@ -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>

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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