From ecff0ee10c335f8f71251e67d615f4f719b6cbe1 Mon Sep 17 00:00:00 2001 From: Caitlyn Date: Tue, 27 May 2025 22:40:14 +0100 Subject: [PATCH 01/23] debug: Colorize output for easier reading Signed-off-by: Caitlyn --- src/connection.c | 29 ++++++++++++++++++++--------- src/wayland-client.c | 30 ++++++++++++++++++++++++------ src/wayland-private.h | 10 +++++++++- src/wayland-server.c | 18 ++++++++++++++++-- 4 files changed, 69 insertions(+), 18 deletions(-) diff --git a/src/connection.c b/src/connection.c index 3ef8688d..593f52f3 100644 --- a/src/connection.c +++ b/src/connection.c @@ -1494,7 +1494,7 @@ wl_closure_queue(struct wl_closure *closure, struct wl_connection *connection) void wl_closure_print(struct wl_closure *closure, struct wl_object *target, int send, int discarded, uint32_t (*n_parse)(union wl_argument *arg), - const char *queue_name) + const char *queue_name, int color) { int i; struct argument_details arg; @@ -1512,17 +1512,28 @@ wl_closure_print(struct wl_closure *closure, struct wl_object *target, clock_gettime(CLOCK_REALTIME, &tp); time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000); + fprintf(f, "%s[%7u.%03u] ", + color ? WL_DEBUG_COLOR_GREEN : "", + time / 1000, time % 1000); - fprintf(f, "[%7u.%03u] ", time / 1000, time % 1000); + if (queue_name) { + fprintf(f, "%s{%s} ", + color ? WL_DEBUG_COLOR_YELLOW : "", + queue_name); + } - if (queue_name) - fprintf(f, "{%s} ", queue_name); - - fprintf(f, "%s%s%s#%u.%s(", + fprintf(f, "%s%s%s%s%s%s%s#%u%s.%s%s(", + color ? WL_DEBUG_COLOR_RED : "", discarded ? "discarded " : "", + color ? WL_DEBUG_COLOR_RESET : "", send ? " -> " : "", - target->interface->name, target->id, - closure->message->name); + color ? WL_DEBUG_COLOR_BLUE : "", + target->interface->name, + color ? WL_DEBUG_COLOR_MAGENTA : "", + target->id, + color ? WL_DEBUG_COLOR_CYAN : "", + closure->message->name, + color ? WL_DEBUG_COLOR_RESET : ""); for (i = 0; i < closure->count; i++) { signature = get_next_argument(signature, &arg); @@ -1587,7 +1598,7 @@ wl_closure_print(struct wl_closure *closure, struct wl_object *target, } } - fprintf(f, ")\n"); + fprintf(f, ")%s\n", color ? WL_DEBUG_COLOR_RESET : ""); if (fclose(f) == 0) { fprintf(stderr, "%s", buffer); diff --git a/src/wayland-client.c b/src/wayland-client.c index 25aa1cd4..d0913c52 100644 --- a/src/wayland-client.c +++ b/src/wayland-client.c @@ -115,6 +115,7 @@ struct wl_display { /** \endcond */ static int debug_client = 0; +static int debug_color = 0; /** * This helper function wakes up all threads that are @@ -936,7 +937,7 @@ wl_proxy_marshal_array_flags(struct wl_proxy *proxy, uint32_t opcode, queue_name = wl_event_queue_get_name(queue); wl_closure_print(closure, &proxy->object, true, false, NULL, - queue_name); + queue_name, debug_color); } if (wl_closure_send(closure, proxy->display->connection)) { @@ -1229,10 +1230,23 @@ wl_display_connect_to_fd(int fd) { struct wl_display *display; const char *debug; + const char *no_color; + const char *force_color; + no_color = getenv("NO_COLOR"); + force_color = getenv("FORCE_COLOR"); debug = getenv("WAYLAND_DEBUG"); - if (debug && (strstr(debug, "client") || strstr(debug, "1"))) + if (debug && (strstr(debug, "client") || strstr(debug, "1"))) { debug_client = 1; + if (isatty(fileno(stderr))) + debug_color = 1; + } + + if (force_color && force_color[0] != '\0') + debug_color = 1; + + if (no_color && no_color[0] != '\0') + debug_color = 0; display = zalloc(sizeof *display); if (display == NULL) { @@ -1578,12 +1592,16 @@ queue_event(struct wl_display *display, int len) if (debug_client) { clock_gettime(CLOCK_REALTIME, &tp); time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000); - - fprintf(stderr, "[%7u.%03u] discarded [%s]#%d.[event %d]" + fprintf(stderr, "%s[%7u.%03u] %sdiscarded %s[%s]%s#%d%s.[event %d]%s" "(%d fd, %d byte)\n", + debug_color ? WL_DEBUG_COLOR_GREEN : "", time / 1000, time % 1000, + debug_color ? WL_DEBUG_COLOR_RED : "", + debug_color ? WL_DEBUG_COLOR_BLUE : "", zombie ? "zombie" : "unknown", - id, opcode, + debug_color ? WL_DEBUG_COLOR_MAGENTA : "", id, + debug_color ? WL_DEBUG_COLOR_BLUE : "", opcode, + debug_color ? WL_DEBUG_COLOR_RESET : "", num_zombie_fds, size); } if (num_zombie_fds > 0) @@ -1668,7 +1686,7 @@ dispatch_event(struct wl_display *display, struct wl_event_queue *queue) !(proxy->dispatcher || proxy->object.implementation); wl_closure_print(closure, &proxy->object, false, discarded, - id_from_object, queue->name); + id_from_object, queue->name, debug_color); } if (proxy_destroyed) { diff --git a/src/wayland-private.h b/src/wayland-private.h index fe9120af..9aace67d 100644 --- a/src/wayland-private.h +++ b/src/wayland-private.h @@ -50,6 +50,14 @@ #define WL_BUFFER_DEFAULT_SIZE_POT 12 #define WL_BUFFER_DEFAULT_MAX_SIZE (1 << WL_BUFFER_DEFAULT_SIZE_POT) +#define WL_DEBUG_COLOR_RESET "\e[0m" +#define WL_DEBUG_COLOR_RED "\e[31m" +#define WL_DEBUG_COLOR_GREEN "\e[32m" +#define WL_DEBUG_COLOR_YELLOW "\e[33m" +#define WL_DEBUG_COLOR_BLUE "\e[34m" +#define WL_DEBUG_COLOR_MAGENTA "\e[35m" +#define WL_DEBUG_COLOR_CYAN "\e[36m" + /** * Argument types used in signatures. */ @@ -230,7 +238,7 @@ void wl_closure_print(struct wl_closure *closure, struct wl_object *target, int send, int discarded, uint32_t (*n_parse)(union wl_argument *arg), - const char *queue_name); + const char *queue_name, int color); void wl_closure_destroy(struct wl_closure *closure); diff --git a/src/wayland-server.c b/src/wayland-server.c index a538519e..96dd417b 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -149,6 +149,7 @@ struct wl_protocol_logger { }; static int debug_server = 0; +static int debug_color = 0; static void log_closure(struct wl_resource *resource, @@ -160,7 +161,7 @@ log_closure(struct wl_resource *resource, struct wl_protocol_logger_message message; if (debug_server) - wl_closure_print(closure, object, send, false, NULL, NULL); + wl_closure_print(closure, object, send, false, NULL, NULL, debug_color); if (!wl_list_empty(&display->protocol_loggers)) { message.resource = resource; @@ -1168,10 +1169,23 @@ wl_display_create(void) { struct wl_display *display; const char *debug; + const char *no_color; + const char *force_color; + no_color = getenv("NO_COLOR"); + force_color = getenv("FORCE_COLOR"); debug = getenv("WAYLAND_DEBUG"); - if (debug && (strstr(debug, "server") || strstr(debug, "1"))) + if (debug && (strstr(debug, "server") || strstr(debug, "1"))) { debug_server = 1; + if (isatty(fileno(stderr))) + debug_color = 1; + } + + if (force_color && force_color[0] != '\0') + debug_color = 1; + + if (no_color && no_color[0] != '\0') + debug_color = 0; display = zalloc(sizeof *display); if (display == NULL) From 6281ccbd3d98ef0a6503425e3e7d705e3075e265 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Wed, 28 May 2025 21:40:17 +0300 Subject: [PATCH 02/23] client: fix conversion specifier in the discarded event log message Signed-off-by: Kirill Primak --- src/wayland-client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wayland-client.c b/src/wayland-client.c index d0913c52..fe14a6b1 100644 --- a/src/wayland-client.c +++ b/src/wayland-client.c @@ -1592,7 +1592,7 @@ queue_event(struct wl_display *display, int len) if (debug_client) { clock_gettime(CLOCK_REALTIME, &tp); time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000); - fprintf(stderr, "%s[%7u.%03u] %sdiscarded %s[%s]%s#%d%s.[event %d]%s" + fprintf(stderr, "%s[%7u.%03u] %sdiscarded %s[%s]%s#%u%s.[event %d]%s" "(%d fd, %d byte)\n", debug_color ? WL_DEBUG_COLOR_GREEN : "", time / 1000, time % 1000, From 1bee7aa4a7d6590f882a61a29da16316ba27c600 Mon Sep 17 00:00:00 2001 From: Tobias Stoeckmann Date: Thu, 12 Sep 2024 20:36:47 +0200 Subject: [PATCH 03/23] cursor: Fix undefined behavior with huge names If an index.theme contains a theme name which gets close to INT_MAX, then creation of full path can lead to a signed integer overflow, which is undefined behavior. Fix this by turning one of the values to size_t. Easy solution for a probably never occurring issue. Signed-off-by: Tobias Stoeckmann --- cursor/xcursor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cursor/xcursor.c b/cursor/xcursor.c index 2b6c47d5..b852a1f9 100644 --- a/cursor/xcursor.c +++ b/cursor/xcursor.c @@ -571,7 +571,7 @@ xcursor_build_theme_dir(const char *dir, const char *theme) * add space for any needed directory separators, one per component, * and one for the trailing null */ - full_size = 1 + homelen + 1 + dirlen + 1 + themelen + 1; + full_size = (size_t) 1 + homelen + 1 + dirlen + 1 + themelen + 1; full = malloc(full_size); if (!full) return NULL; From ce0ac4f29e720688ea94fbe412a0b332304d8ee6 Mon Sep 17 00:00:00 2001 From: Tobias Stoeckmann Date: Thu, 12 Sep 2024 20:52:15 +0200 Subject: [PATCH 04/23] cursor: Gracefully handle out of memory condition If the full path could not be constructed, avoid calling opendir(NULL) which, depending on library, might trigger undefined behavior. Signed-off-by: Tobias Stoeckmann --- cursor/xcursor.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cursor/xcursor.c b/cursor/xcursor.c index b852a1f9..f7156cdb 100644 --- a/cursor/xcursor.c +++ b/cursor/xcursor.c @@ -686,11 +686,15 @@ load_all_cursors_from_dir(const char *path, int size, void *user_data) { FILE *f; - DIR *dir = opendir(path); + DIR *dir; struct dirent *ent; char *full; struct xcursor_images *images; + if (!path) + return; + + dir = opendir(path); if (!dir) return; From 5c2f31d8d6e5f24962300f4608a0d6f887ca3bea Mon Sep 17 00:00:00 2001 From: Tobias Stoeckmann Date: Thu, 12 Sep 2024 22:05:52 +0200 Subject: [PATCH 05/23] cursor: Gracefully handle huge cursor files If cursor files require more than INT_MAX bytes, it is possible to trigger out of boundary writes. Since these sizes are most likely not desired anyway, gracefully handle these situations like out of memory errors. Signed-off-by: Tobias Stoeckmann --- cursor/wayland-cursor.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/cursor/wayland-cursor.c b/cursor/wayland-cursor.c index 636f5166..f3fef157 100644 --- a/cursor/wayland-cursor.c +++ b/cursor/wayland-cursor.c @@ -27,6 +27,7 @@ #include "xcursor.h" #include "wayland-cursor.h" #include "wayland-client.h" +#include #include #include #include @@ -284,7 +285,8 @@ wl_cursor_create_from_xcursor_images(struct xcursor_images *images, { struct cursor *cursor; struct cursor_image *image; - int i, size; + size_t size; + int i; cursor = malloc(sizeof *cursor); if (!cursor) @@ -314,7 +316,12 @@ wl_cursor_create_from_xcursor_images(struct xcursor_images *images, image->image.hotspot_y = images->images[i]->yhot; image->image.delay = images->images[i]->delay; - size = image->image.width * image->image.height * 4; + size = (size_t) image->image.width * image->image.height * 4; + if (size > INT_MAX) { + free(image); + break; + } + image->offset = shm_pool_allocate(theme->pool, size); if (image->offset < 0) { free(image); @@ -389,6 +396,9 @@ wl_cursor_theme_load(const char *name, int size, struct wl_shm *shm) if (!theme) return NULL; + if (size < 0 || (size > 0 && INT_MAX / size / 4 < size)) + return NULL; + if (!name) name = "default"; From 2978fd701a6987668a4ff41f9434f6c0da705596 Mon Sep 17 00:00:00 2001 From: Tobias Stoeckmann Date: Thu, 12 Sep 2024 22:18:30 +0200 Subject: [PATCH 06/23] cursor: Ignore invalid cursor files The header offset must not be smaller than file header length. Ignore such invalid files. Signed-off-by: Tobias Stoeckmann --- cursor/xcursor.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cursor/xcursor.c b/cursor/xcursor.c index f7156cdb..6e54cdbd 100644 --- a/cursor/xcursor.c +++ b/cursor/xcursor.c @@ -259,6 +259,8 @@ xcursor_read_file_header(FILE *file) return NULL; if (!xcursor_read_uint(file, &head.ntoc)) return NULL; + if (head.header < XCURSOR_FILE_HEADER_LEN) + return NULL; skip = head.header - XCURSOR_FILE_HEADER_LEN; if (skip) if (fseek(file, skip, SEEK_CUR) == EOF) From 0de833da296e59e2495738afc450d1d3cb0314b3 Mon Sep 17 00:00:00 2001 From: Tobias Stoeckmann Date: Thu, 12 Sep 2024 23:07:45 +0200 Subject: [PATCH 07/23] cursor: Properly check realloc for errors Do not override realloc's input pointer before checking for errors, otherwise it's not possible to keep old value, as intended. Signed-off-by: Tobias Stoeckmann --- cursor/wayland-cursor.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/cursor/wayland-cursor.c b/cursor/wayland-cursor.c index f3fef157..89ecc9a1 100644 --- a/cursor/wayland-cursor.c +++ b/cursor/wayland-cursor.c @@ -351,6 +351,8 @@ load_callback(struct xcursor_images *images, void *data) { struct wl_cursor_theme *theme = data; struct wl_cursor *cursor; + struct wl_cursor **p; + size_t s; if (wl_cursor_theme_get_cursor(theme, images->name)) { xcursor_images_destroy(images); @@ -360,15 +362,14 @@ load_callback(struct xcursor_images *images, void *data) cursor = wl_cursor_create_from_xcursor_images(images, theme); if (cursor) { - theme->cursor_count++; - theme->cursors = - realloc(theme->cursors, - theme->cursor_count * sizeof theme->cursors[0]); + s = theme->cursor_count + 1; + p = realloc(theme->cursors, s * sizeof theme->cursors[0]); - if (theme->cursors == NULL) { - theme->cursor_count--; + if (p == NULL) { free(cursor); } else { + theme->cursor_count = s; + theme->cursors = p; theme->cursors[theme->cursor_count - 1] = cursor; } } From 387adc6a794df6bee114396612d5a6accab318c3 Mon Sep 17 00:00:00 2001 From: Manuel Stoeckl Date: Tue, 2 Mar 2021 23:24:16 -0500 Subject: [PATCH 08/23] server: Document wl_display_add_socket_auto The exact sequence of names tried has de facto become part of the API. Signed-off-by: Manuel Stoeckl --- src/wayland-server.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/wayland-server.c b/src/wayland-server.c index 96dd417b..15667644 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -1808,6 +1808,24 @@ _wl_display_add_socket(struct wl_display *display, struct wl_socket *s) return 0; } + +/** Automatically pick a Wayland display socket for the clients to connect to. + * + * \param display Wayland display to which the socket should be added. + * \return The socket name if success. NULL if failed. + * + * This adds a Unix socket to Wayland display which can be used by clients to + * connect to Wayland display. The name of the socket is chosen automatically + * as the first available name in the sequence "wayland-0", "wayland-1", + * "wayland-2", ..., "wayland-32". + * + * The string returned by this function is owned by the library and should + * not be freed. + * + * \sa wl_display_add_socket + * + * \memberof wl_display + */ WL_EXPORT const char * wl_display_add_socket_auto(struct wl_display *display) { From 4a0c4e211949bd58fa7e6105487b8dac0d4cfd1c Mon Sep 17 00:00:00 2001 From: Manuel Stoeckl Date: Tue, 2 Mar 2021 22:04:58 -0500 Subject: [PATCH 09/23] doc: Further explain typical display socket lookup This change mentions the case where WAYLAND_SOCKET is used, which helps people avoid just testing 'getenv(WAYLAND_DISPLAY)' to see if a Wayland compositor is available; Signed-off-by: Manuel Stoeckl --- doc/publican/sources/Protocol.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/publican/sources/Protocol.xml b/doc/publican/sources/Protocol.xml index 692f17eb..e4087e9f 100644 --- a/doc/publican/sources/Protocol.xml +++ b/doc/publican/sources/Protocol.xml @@ -97,7 +97,9 @@ in the environment). Beginning in Wayland 1.15, implementations can optionally support server socket endpoints located at arbitrary locations in the filesystem by setting WAYLAND_DISPLAY - to the absolute path at which the server endpoint listens. + to the absolute path at which the server endpoint listens. The socket may + also be provided through file descriptor inheritance, in which case + WAYLAND_SOCKET is set. Every message is structured as 32-bit words; values are represented in the From ca83185e8a28017ff3a2f9edccaa5d35bb86f1d7 Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Thu, 19 Dec 2024 21:44:00 -0500 Subject: [PATCH 10/23] tests: Make `tests` dict elements dicts themselves Previously each value was a list of extra sources. The next commit will add an additional field to each test, so they need to be dicts themselves. Signed-off-by: Matt Turner --- tests/meson.build | 161 ++++++++++++++++++++++++++++------------------ 1 file changed, 97 insertions(+), 64 deletions(-) diff --git a/tests/meson.build b/tests/meson.build index 2c22b82a..cf3c4e87 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -96,72 +96,105 @@ if get_option('scanner') endif tests = { - 'array-test': [], - 'client-test': [ wayland_server_protocol_h ], - 'display-test': [ - wayland_client_protocol_h, - wayland_server_protocol_h, - tests_server_protocol_h, - tests_client_protocol_c, - tests_protocol_c, - ], - 'connection-test': [ - wayland_client_protocol_h, - wayland_server_protocol_h, - ], - 'event-loop-test': [ wayland_server_protocol_h ], - 'fixed-test': [], - 'interface-test': [ wayland_client_protocol_h ], - 'list-test': [], - 'map-test': [], - 'sanity-test' : [ - wayland_client_protocol_h, - wayland_server_protocol_h, - ], - 'socket-test': [ - wayland_client_protocol_h, - wayland_server_protocol_h, - ], - 'queue-test': [ - wayland_client_protocol_h, - wayland_server_protocol_h, - ], - 'signal-test': [ wayland_server_protocol_h ], - 'newsignal-test': [ - # wayland-server.c is needed here to access wl_priv_* functions - files('../src/wayland-server.c'), - wayland_server_protocol_h, - ], - 'resources-test': [ wayland_server_protocol_h ], - 'message-test': [ - wayland_client_protocol_h, - wayland_server_protocol_h, - ], - 'compositor-introspection-test': [ - wayland_client_protocol_h, - wayland_server_protocol_h, - ], - 'protocol-logger-test': [ - wayland_client_protocol_h, - wayland_server_protocol_h, - ], - 'headers-test': [ - wayland_client_protocol_h, - wayland_server_protocol_h, - 'headers-protocol-test.c', - wayland_client_protocol_core_h, - wayland_server_protocol_core_h, - 'headers-protocol-core-test.c', - ], - 'os-wrappers-test': [], - 'proxy-test': [ - wayland_client_protocol_h, - wayland_server_protocol_h, - ], - 'enum-validator-test': [], + 'array-test': {}, + 'client-test': { + 'extra_sources': [ wayland_server_protocol_h ], + }, + 'display-test': { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + tests_server_protocol_h, + tests_client_protocol_c, + tests_protocol_c, + ], + }, + 'connection-test': { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + ], + }, + 'event-loop-test': { + 'extra_sources': [ wayland_server_protocol_h ], + }, + 'fixed-test': {}, + 'interface-test': { + 'extra_sources': [ wayland_client_protocol_h ], + }, + 'list-test': {}, + 'map-test': {}, + 'sanity-test' : { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + ], + }, + 'socket-test': { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + ], + }, + 'queue-test': { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + ], + }, + 'signal-test': { + 'extra_sources': [ wayland_server_protocol_h ], + }, + 'newsignal-test': { + 'extra_sources': [ + # wayland-server.c is needed here to access wl_priv_* functions + files('../src/wayland-server.c'), + wayland_server_protocol_h, + ], + }, + 'resources-test': { + 'extra_sources': [ wayland_server_protocol_h ], + }, + 'message-test': { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + ], + }, + 'compositor-introspection-test': { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + ], + }, + 'protocol-logger-test': { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + ], + }, + 'headers-test': { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + 'headers-protocol-test.c', + wayland_client_protocol_core_h, + wayland_server_protocol_core_h, + 'headers-protocol-core-test.c', + ], + }, + 'os-wrappers-test': {}, + 'proxy-test': { + 'extra_sources': [ + wayland_client_protocol_h, + wayland_server_protocol_h, + ], + }, + 'enum-validator-test': {}, } -foreach test_name, test_extra_sources: tests +foreach test_name, test_extras : tests + test_extra_sources = test_extras.get('extra_sources', []) test_sources = [ test_name + '.c' ] + test_extra_sources test_deps = [test_runner_dep, epoll_dep] bin = executable(test_name, test_sources, dependencies: test_deps) From 6c1da920185955f7c86af38787c8889203ec3fcb Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Thu, 19 Dec 2024 21:48:49 -0500 Subject: [PATCH 11/23] tests: Add support for specifying runtime dependencies Signed-off-by: Matt Turner --- tests/meson.build | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/meson.build b/tests/meson.build index cf3c4e87..44f1233d 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -195,12 +195,14 @@ tests = { foreach test_name, test_extras : tests test_extra_sources = test_extras.get('extra_sources', []) + test_runtime_deps = test_extras.get('runtime_deps', []) test_sources = [ test_name + '.c' ] + test_extra_sources test_deps = [test_runner_dep, epoll_dep] bin = executable(test_name, test_sources, dependencies: test_deps) test( test_name, bin, + depends: test_runtime_deps, env: [ 'TEST_SRC_DIR=@0@'.format(meson.current_source_dir()), 'TEST_BUILD_DIR=@0@'.format(meson.current_build_dir()), From fdac631d1744d50e6e470bb78bf5057664967e32 Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Thu, 19 Dec 2024 21:49:02 -0500 Subject: [PATCH 12/23] tests: Depend on exec-fd-leak-checker Closes: https://gitlab.freedesktop.org/wayland/wayland/-/issues/514 Signed-off-by: Matt Turner --- tests/meson.build | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/meson.build b/tests/meson.build index 44f1233d..6ada5202 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -54,7 +54,7 @@ tests_protocol_c = custom_target( output: 'tests-protocol.c' ) -executable( +exec_fd_leak_checker = executable( 'exec-fd-leak-checker', 'exec-fd-leak-checker.c', dependencies: test_runner_dep @@ -129,6 +129,7 @@ tests = { wayland_client_protocol_h, wayland_server_protocol_h, ], + 'runtime_deps': [ exec_fd_leak_checker ], }, 'socket-test': { 'extra_sources': [ @@ -183,7 +184,9 @@ tests = { 'headers-protocol-core-test.c', ], }, - 'os-wrappers-test': {}, + 'os-wrappers-test': { + 'runtime_deps': [ exec_fd_leak_checker ], + }, 'proxy-test': { 'extra_sources': [ wayland_client_protocol_h, From 53fbc2b0c1dc70b3a96740ab0ceff6a9fe09b940 Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Thu, 19 Dec 2024 21:51:50 -0500 Subject: [PATCH 13/23] egl: Make `wayland-egl symbols check` depend on `wayland_egl` Closes: https://gitlab.freedesktop.org/wayland/wayland/-/issues/515 Signed-off-by: Matt Turner --- egl/meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/egl/meson.build b/egl/meson.build index 5363e808..b72c7a46 100644 --- a/egl/meson.build +++ b/egl/meson.build @@ -22,6 +22,7 @@ if get_option('tests') test( 'wayland-egl symbols check', find_program('wayland-egl-symbols-check'), + depends: wayland_egl, env: [ 'WAYLAND_EGL_LIB=@0@'.format(wayland_egl_shared.full_path()), 'NM=@0@'.format(nm_path) From ba9f9a446f7462a7b41b34e585c7beca020d12f7 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 12 Jun 2025 14:37:32 +0300 Subject: [PATCH 14/23] doc: add a section on color management I think the docbook deserves an introduction to how color management is designed in Wayland, aimed at people who are familiar with pixels but new to the topic. Signed-off-by: Pekka Paalanen --- doc/publican/sources/Color.xml | 139 +++++++++++++++++++++++++++++++ doc/publican/sources/Wayland.xml | 1 + doc/publican/sources/meson.build | 1 + 3 files changed, 141 insertions(+) create mode 100644 doc/publican/sources/Color.xml diff --git a/doc/publican/sources/Color.xml b/doc/publican/sources/Color.xml new file mode 100644 index 00000000..ceee779e --- /dev/null +++ b/doc/publican/sources/Color.xml @@ -0,0 +1,139 @@ + + +%BOOK_ENTITIES; +]> + + + Color management + +
+ Overview + + + Color management in Wayland considers only displays. All pictures in + Wayland are always display-referred, meaning that the pixel values are + intended as-is for some specific display where they would produce the + light emissions (stimuli) the picture's + author desired. Wayland does not support displaying "raw" camera or + scanner images as they are not display-referred, nor are they even + pictures without complex and subjective processing. + + + Stimuli — the picture itself — are only half of the picture reproduction. + The other half is the environment where a display is viewed. A striking + example is comparing a brightly lit office to a dark movie theater, the + stimuli required to produce a good reading of the picture is greatly + different. Therefore display-referred does not include only the display + but the viewing environment as well. + + + Window systems have been very well capable of operating without any + explicit consideration to color management. This is because there used to + be the implicit assumption of the standard display, the sRGB display, + which all computer monitors implemented, more or less. The viewing + environment was and still is accounted by adjusting the display and/or the + room to produce a workable experience. Pictures are authored on a computer + system by drawing, painting and adjusting the picture until it looks right + on the author's monitor. This implicitly builds the standard display and + environment assumption into the picture data. Deviations from the sRGB + specification were minor enough that they often did not matter if not in a + professional context like the printing industry. Displaying video material + required some more attention to the details, because video and television + standards differ enough from the sRGB display. What really made explicit + color management a hard requirement for entertainment is the coming of + wide color gamut (WCG) and high dynamic range (HDR) materials and + displays. + + + The color management design in Wayland follows the general Wayland design + principles: compositors tell clients what would be the optimal thing to + do, clients tell the compositors what kind of pictures they are actually + producing, and then compositors display those pictures the best they can. + +
+ +
+ Protocol Interfaces + + + Color management interfaces in Wayland and divided into two protocols: + color-management + and + color-representation. + They are designed to work together, but they can also be used + independently when the other one is not needed. + + +
+ Color-management + + + Color management protocol has two main purposes. First, it puts the + responsibility of color management on the compositor. This means that + clients do not necessarily need to care about color management at all, + and can display just fine by using the traditional standard display + assumption even when the actual display is wildly different. Clients + can also choose to target some other assumed display and let the + compositor handle it, or they can explicitly render for the actual + display at hand. Second, when the window system has multiple different + monitors, and a wl_surface happens to span more than one monitor, the + compositor can display the surface content correctly on all spanned + monitors simultaneously, as much as physically possible. + + + Color-management protocol concentrates on colorimetry: when you have a + pixel with RGB values, what stimulus do those values represent. The + stimulus definition follows the CIE 1931 two-degree observer model. Some + core concepts here are color primaries, white point, transfer function, + and dynamic range. The viewing environment is represented in an + extremely simplified way as the reference white luminance. The + connection between pixel RGB values and stimulus plus viewing + environment is recorded in an image description + object. Clients can create image description objects and tag + wl_surfaces with them, to indicate what kind of surface + content there will be. Clients can also ask what image description the + compositor would prefer to have on the wl_surface, and that + preference can change over time, e.g. when the wl_surface + is moved from one + wl_output to another. Following the compositor's preference + may provide advantages in image quality and power consumption. + + + Image description objects can come in two flavors: parametric and + ICC-based. The above was written with parametric image descriptions in + mind, and they have first-class support for HDR. ICC-based image + descriptions are wrapping an ICC profile and have no other data. ICC + profiles are the standard tool for standard dynamic range (SDR) display + color management. This means the capabilities between the two flavors + differ, and one cannot always be replaced by the other. Compositor + support for each flavor is optional. + +
+ +
+ Color-representation + + + Color-representation protocol deals with (potentially sub-sampled) + YCbCr-RGB conversion, quantization range, and the inclusion of alpha in + the RGB color channels, a.k.a. pre-multiplication. There are several + different specifications on how an YCbCr-like (including ICtCp) signal, + with chroma sub-sampling or not, is created from a full-resolution RGB + image. Again, a client can tag a wl_surface with + color-representation metadata to tell the compositor what kind of pixel + data will be displayed through the wl_surface. + + + The main purpose of color-representation is to correctly off-load the + YCbCr-RGB conversion to the compositor, which can then opportunistically + off-load it further to very power-efficient fixed-function circuitry in + a display controller. This can significantly reduce power consumption + when watching videos compared to using a GPU for the same, and on some + embedded hardware platforms it is a hard requirement for processing high + resolution video. + +
+
+
diff --git a/doc/publican/sources/Wayland.xml b/doc/publican/sources/Wayland.xml index 0457c15c..7593097e 100644 --- a/doc/publican/sources/Wayland.xml +++ b/doc/publican/sources/Wayland.xml @@ -12,6 +12,7 @@ + diff --git a/doc/publican/sources/meson.build b/doc/publican/sources/meson.build index 52f3a681..a53b3890 100644 --- a/doc/publican/sources/meson.build +++ b/doc/publican/sources/meson.build @@ -54,6 +54,7 @@ publican_sources = [ 'Protocol.xml', 'Xwayland.xml', 'Compositors.xml', + 'Color.xml', 'Client.xml', 'Server.xml' ] From adf84614ca6189fa4efc522408ffbbc4b27ae497 Mon Sep 17 00:00:00 2001 From: Demi Marie Obenour Date: Wed, 14 Aug 2024 21:14:49 -0400 Subject: [PATCH 15/23] connection: Do not busy-loop if a message exceeds the buffer size If the length of a message exceeds the maximum length of the buffer, the buffer size will reach its maximum value and stay there forever, with no message ever being successfully processed. Since libwayland uses level-triggered epoll, this will cause the compositor to loop forever and consume CPU time. In libwayland 1.22 and below, there was an explicit check that caused messages exceeding 4096 bytes to result in an EOVERFLOW error, preventing the loop. However, this check was removed between d074d5290263 ("connection: Dynamically resize connection buffers"). To prevent this problem, always limit the size of messages to 4096 bytes. Since the default and minimum buffer size is 4096 bytes, this ensures that a single message will always fit in the buffer. It would be possible to allow larger messages if the buffer size was larger, but the maximum size of a message should not depend on the buffer size chosen by the compositor. Rejecting messages that exceed 4092 bytes seems to have the advantage of reserving 4 bits, not 3, in the size field for future use. However, message sizes in the range [0x0, 0x7] are invalid, so one can obtain a fourth bit by negating the meaning of bit 12 if bits 0 through 11 (inclusive) are 0. Allowing 4096-byte messages provides the far more important advantage that regressions compared to 1.22 are impossible and regressions compared to 1.23 are extremely unlikely. The only case where a regression is possible is: - The receiving side is using libwayland 1.23. - The sending side is either using libwayland 1.23 or is not using libwayland. - The sender sends a message exceeding 4096 bytes. - If the sender of the large message is the client, the server has increased the buffer size from the default value. This combination is considered extremely unlikely, as libwayland 1.22 and below would disconnect upon receiving such a large message. 4096-byte messages, however, have always worked, so there was no reason to avoid sending them. Fixes: d074d5290263 ("connection: Dynamically resize connection buffers"). Fixes: #494 Signed-off-by: Demi Marie Obenour --- src/wayland-client.c | 22 ++++++++++++++++++++++ src/wayland-private.h | 3 +++ src/wayland-server.c | 23 +++++++++++++++++++++++ src/wayland-util.h | 8 ++++++++ 4 files changed, 56 insertions(+) diff --git a/src/wayland-client.c b/src/wayland-client.c index fe14a6b1..c8633046 100644 --- a/src/wayland-client.c +++ b/src/wayland-client.c @@ -1578,6 +1578,28 @@ queue_event(struct wl_display *display, int len) id = p[0]; opcode = p[1] & 0xffff; size = p[1] >> 16; + + /* + * If the message is larger than the maximum size of the + * connection buffer, the connection buffer will fill to + * its max size and stay there, with no message ever + * successfully being processed. If the user of + * libwayland-client uses a level-triggered event loop, + * this will cause the client to enter a loop that + * consumes CPU. To avoid this, immediately drop the + * connection. Since the maximum size of a message should + * not depend on the max buffer size chosen by the client, + * always compare the message size against the + * limit enforced by libwayland 1.22 and below (4096), + * rather than the actual value the client chose. + */ + if (size > WL_MAX_MESSAGE_SIZE) { + wl_log("Message length %u exceeds limit %d\n", + size, WL_MAX_MESSAGE_SIZE); + errno = E2BIG; + return -1; + } + if (len < size) return 0; diff --git a/src/wayland-private.h b/src/wayland-private.h index 9aace67d..d7ba9dae 100644 --- a/src/wayland-private.h +++ b/src/wayland-private.h @@ -49,6 +49,9 @@ #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) +#if WL_BUFFER_DEFAULT_MAX_SIZE < WL_MAX_MESSAGE_SIZE +# error default buffer cannot hold maximum-sized message +#endif #define WL_DEBUG_COLOR_RESET "\e[0m" #define WL_DEBUG_COLOR_RED "\e[31m" diff --git a/src/wayland-server.c b/src/wayland-server.c index 15667644..482743b3 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -398,6 +398,29 @@ wl_client_connection_data(int fd, uint32_t mask, void *data) wl_connection_copy(connection, p, sizeof p); opcode = p[1] & 0xffff; size = p[1] >> 16; + + /* + * If the message is larger than the maximum size of the + * connection buffer, the connection buffer will fill to + * its max size and stay there, with no message ever + * successfully being processed. Since libwayland-server + * uses level-triggered epoll, it will cause the server to + * enter a loop that consumes CPU. To avoid this, + * immediately disconnect the client with a protocol + * error. Since the maximum size of a message should not + * depend on the buffer size chosen by the compositor, + * always compare the message size against the + * limit enforced by libwayland 1.22 and below (4096), + * rather than the actual value the compositor chose. + */ + if (size > WL_MAX_MESSAGE_SIZE) { + wl_resource_post_error(client->display_resource, + WL_DISPLAY_ERROR_INVALID_METHOD, + "message length %u exceeds %d", + size, WL_MAX_MESSAGE_SIZE); + break; + } + if (len < size) break; diff --git a/src/wayland-util.h b/src/wayland-util.h index 4540f040..98c72fde 100644 --- a/src/wayland-util.h +++ b/src/wayland-util.h @@ -90,6 +90,14 @@ extern "C" { */ struct wl_object; +/** + * The maximum size of a protocol message. + * + * If a message size exceeds this value, the connection will be dropped. + * Servers will send an invalid_method error before disconnecting. + */ +#define WL_MAX_MESSAGE_SIZE 4096 + /** * Protocol message signature * From eecf3f7635586c41d985e0f443fd4f8930751487 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 21 Jun 2025 13:38:28 +0200 Subject: [PATCH 16/23] build: re-open main branch for regular development Signed-off-by: Simon Ser --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index a3126b63..37c14687 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'wayland', 'c', - version: '1.23.91', + version: '1.24.90', license: 'MIT', meson_version: '>= 0.57.0', default_options: [ From 90187031e6ee0ab5cf2b3ec243a314babf4e8561 Mon Sep 17 00:00:00 2001 From: ykla Date: Sun, 22 Jun 2025 15:09:50 +0000 Subject: [PATCH 17/23] ci: upgrade FreeBSD to 14.2 Signed-off-by: ykla --- .gitlab-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index eb6fb0b4..26ceae1f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -43,7 +43,7 @@ include: # API changes. If you need new features from ci-templates you must bump # this to the current SHA you require from the ci-templates repo, however # be aware that you may need to account for API changes when doing so. - ref: f210ea84576f756816da37908771edcee14ef7e6 + ref: 32afe5644697e503af18a736587c8619fa036a72 file: - '/templates/debian.yml' - '/templates/freebsd.yml' @@ -306,11 +306,11 @@ armv7-release-debian-build: .os-freebsd: variables: BUILD_OS: freebsd - FDO_DISTRIBUTION_VERSION: "13.2" + FDO_DISTRIBUTION_VERSION: "14.2" FDO_DISTRIBUTION_PACKAGES: 'libxslt meson ninja pkgconf expat libffi libepoll-shim libxml2' # bump this tag every time you change something which requires rebuilding the # base image - FDO_DISTRIBUTION_TAG: "2023-08-02.0" + FDO_DISTRIBUTION_TAG: "2025-06-23.1" # Don't build documentation since installing the required tools massively # increases the VM image (and therefore container) size. MESON_ARGS: "--fatal-meson-warnings -Dwerror=true -Ddocumentation=false" From cd0d1543c093d079ecf3444a16a4dcb02007a62a Mon Sep 17 00:00:00 2001 From: ykla Date: Sun, 20 Jul 2025 02:09:35 +0000 Subject: [PATCH 18/23] ci: upgrade FreeBSD to 14.3 Signed-off-by: ykla yklaxds@gmail.com --- .gitlab-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 26ceae1f..eeed7899 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -43,7 +43,7 @@ include: # API changes. If you need new features from ci-templates you must bump # this to the current SHA you require from the ci-templates repo, however # be aware that you may need to account for API changes when doing so. - ref: 32afe5644697e503af18a736587c8619fa036a72 + ref: 48c2c583a865bd59be21e8938df247faf460099c file: - '/templates/debian.yml' - '/templates/freebsd.yml' @@ -306,11 +306,11 @@ armv7-release-debian-build: .os-freebsd: variables: BUILD_OS: freebsd - FDO_DISTRIBUTION_VERSION: "14.2" + FDO_DISTRIBUTION_VERSION: "14.3" FDO_DISTRIBUTION_PACKAGES: 'libxslt meson ninja pkgconf expat libffi libepoll-shim libxml2' # bump this tag every time you change something which requires rebuilding the # base image - FDO_DISTRIBUTION_TAG: "2025-06-23.1" + FDO_DISTRIBUTION_TAG: "2025-07-20.0" # Don't build documentation since installing the required tools massively # increases the VM image (and therefore container) size. MESON_ARGS: "--fatal-meson-warnings -Dwerror=true -Ddocumentation=false" From 264da6a92b48ef41661021236c5d51ca52722309 Mon Sep 17 00:00:00 2001 From: YaoBing Xiao Date: Wed, 16 Jul 2025 22:29:40 +0800 Subject: [PATCH 19/23] cursor: Free theme when size check fails to avoid memory leak Signed-off-by: YaoBing Xiao --- cursor/wayland-cursor.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cursor/wayland-cursor.c b/cursor/wayland-cursor.c index 89ecc9a1..2e21db73 100644 --- a/cursor/wayland-cursor.c +++ b/cursor/wayland-cursor.c @@ -398,7 +398,7 @@ wl_cursor_theme_load(const char *name, int size, struct wl_shm *shm) return NULL; if (size < 0 || (size > 0 && INT_MAX / size / 4 < size)) - return NULL; + goto err; if (!name) name = "default"; @@ -409,7 +409,7 @@ wl_cursor_theme_load(const char *name, int size, struct wl_shm *shm) theme->pool = shm_pool_create(shm, size * size * 4); if (!theme->pool) - goto out_error_pool; + goto err; xcursor_load_theme(name, size, load_callback, theme); @@ -421,7 +421,7 @@ wl_cursor_theme_load(const char *name, int size, struct wl_shm *shm) return theme; -out_error_pool: +err: free(theme); return NULL; } From 77730f10a0eaac1c654d1bdc689783292bdb5f2d Mon Sep 17 00:00:00 2001 From: Kyle Brenneman Date: Tue, 17 Sep 2024 17:27:37 -0600 Subject: [PATCH 20/23] connection: Add a function to parse WAYLAND_DEBUG tokens Add a new function, wl_check_env_token, to scan for a token in a comma-separated string. Change wl_display_create in wayland-server.c and wl_display_connect_to_fd in wayland-client.c to use that instead of a simple substring search. This means that WAYLAND_DEBUG will accept a value like "client,server" but not "clientserver". But, this will make it easier to add other tokens without worrying about overlap between them. Signed-off-by: Kyle Brenneman --- src/connection.c | 42 ++++++++++++++++++++++++++++++++++++++++++ src/wayland-client.c | 2 +- src/wayland-private.h | 3 +++ src/wayland-server.c | 2 +- 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/connection.c b/src/connection.c index 593f52f3..9c6a6b01 100644 --- a/src/connection.c +++ b/src/connection.c @@ -1491,6 +1491,48 @@ wl_closure_queue(struct wl_closure *closure, struct wl_connection *connection) return result; } +bool +wl_check_env_token(const char *env, const char *token) +{ + const char *ptr = env; + size_t token_len; + + if (env == NULL) + return false; + + token_len = strlen(token); + + // Scan the string for comma-separated tokens and look for a match. + while (true) { + const char *end; + size_t len; + + // Skip over any leading separators. + while (*ptr == ',') + ptr++; + + if (*ptr == '\x00') + return false; + + end = strchr(ptr + 1, ','); + + // If there isn't another separarator, then the rest of the string + // is one token. + if (end == NULL) + return (strcmp(ptr, token) == 0); + + len = end - ptr; + if (len == token_len && memcmp(ptr, token, len) == 0) { + return true; + } + + // Skip to the next token. + ptr += len; + } + + return false; +} + void wl_closure_print(struct wl_closure *closure, struct wl_object *target, int send, int discarded, uint32_t (*n_parse)(union wl_argument *arg), diff --git a/src/wayland-client.c b/src/wayland-client.c index c8633046..c0b361f0 100644 --- a/src/wayland-client.c +++ b/src/wayland-client.c @@ -1236,7 +1236,7 @@ wl_display_connect_to_fd(int fd) no_color = getenv("NO_COLOR"); force_color = getenv("FORCE_COLOR"); debug = getenv("WAYLAND_DEBUG"); - if (debug && (strstr(debug, "client") || strstr(debug, "1"))) { + if (debug && (wl_check_env_token(debug, "client") || wl_check_env_token(debug, "1"))) { debug_client = 1; if (isatty(fileno(stderr))) debug_color = 1; diff --git a/src/wayland-private.h b/src/wayland-private.h index d7ba9dae..d0e4cfc6 100644 --- a/src/wayland-private.h +++ b/src/wayland-private.h @@ -237,6 +237,9 @@ wl_closure_send(struct wl_closure *closure, struct wl_connection *connection); int wl_closure_queue(struct wl_closure *closure, struct wl_connection *connection); +bool +wl_check_env_token(const char *env, const char *token); + void wl_closure_print(struct wl_closure *closure, struct wl_object *target, int send, int discarded, diff --git a/src/wayland-server.c b/src/wayland-server.c index 482743b3..c81d98f1 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -1198,7 +1198,7 @@ wl_display_create(void) no_color = getenv("NO_COLOR"); force_color = getenv("FORCE_COLOR"); debug = getenv("WAYLAND_DEBUG"); - if (debug && (strstr(debug, "server") || strstr(debug, "1"))) { + if (debug && (wl_check_env_token(debug, "server") || wl_check_env_token(debug, "1"))) { debug_server = 1; if (isatty(fileno(stderr))) debug_color = 1; From 4673ef7e9ce5de21051b64c39816a98187611966 Mon Sep 17 00:00:00 2001 From: Kyle Brenneman Date: Tue, 10 Sep 2024 14:36:06 -0600 Subject: [PATCH 21/23] connection: Add a thread ID to WAYLAND_DEBUG output. If WAYLAND_DEBUG contains the token "thread_id", and gettid() is available, then include the current thread ID in the output from wl_closure_print. If multiple threads are sending requests, then those requests can get interleaved. That's usually fine, but for wl_surface requests and commits, that can cause problems ranging from incorrect behavior to protocol errors. Being able to see which requests are sent by different threads would make such problems much easier to diagnose. Signed-off-by: Kyle Brenneman --- meson.build | 1 + src/connection.c | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/meson.build b/meson.build index 37c14687..ce386a4c 100644 --- a/meson.build +++ b/meson.build @@ -46,6 +46,7 @@ have_funcs = [ 'memfd_create', 'mremap', 'strndup', + 'gettid', ] foreach f: have_funcs config_h.set('HAVE_' + f.underscorify().to_upper(), cc.has_function(f)) diff --git a/src/connection.c b/src/connection.c index 9c6a6b01..2d1e8d1d 100644 --- a/src/connection.c +++ b/src/connection.c @@ -26,6 +26,8 @@ #define _GNU_SOURCE +#include "../config.h" + #include #include #include @@ -1538,6 +1540,9 @@ wl_closure_print(struct wl_closure *closure, struct wl_object *target, int send, int discarded, uint32_t (*n_parse)(union wl_argument *arg), const char *queue_name, int color) { +#if defined(HAVE_GETTID) + static int include_tid = -1; +#endif // defined(HAVE_GETTID) int i; struct argument_details arg; const char *signature = closure->message->signature; @@ -1558,6 +1563,18 @@ wl_closure_print(struct wl_closure *closure, struct wl_object *target, color ? WL_DEBUG_COLOR_GREEN : "", time / 1000, time % 1000); +#if defined(HAVE_GETTID) + if (include_tid < 0) { + include_tid = wl_check_env_token(getenv("WAYLAND_DEBUG"), "thread_id"); + } + + if (include_tid) { + fprintf(f, "%sTID#%d ", + color ? WL_DEBUG_COLOR_CYAN : "", + (int) gettid()); + } +#endif + if (queue_name) { fprintf(f, "%s{%s} ", color ? WL_DEBUG_COLOR_YELLOW : "", From d81525a235e48cc5de3e4005a16ddb1fbdfd9d7c Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Wed, 2 Jul 2025 12:15:33 +0200 Subject: [PATCH 22/23] client: add wl_display_dispatch_pending_single As well as wl_display_dispatch_queue_pending_single. The motivation is writing libwayland bindings for a dynamic language with exceptions/non-local returns. Since it is invalid for a wl_dispatcher_func_t callback provided to libwayland to not return, there is no way to prevent dispatching of further events in the case of an exception in the dynamic language event handler. Furthermore, since creating/destroying Wayland objects in an event handler affects the dispatching of subsequent events by libwayland, it is not possible to collect Wayland events in a queue outside libwayland and dispatch them one-by-one after wl_display_dispatch_pending() returns. Adding libwayland API to dispatch at most one pending event solves this problem cleanly. The bindings can have libwayland dispatch a single event, wait for wl_display_dispatch_pending_single() to return, run the dynamic language event handler (which may longjmp away), and continue the loop for as long as there are more events to dispatch. References: https://codeberg.org/ifreund/janet-wayland Signed-off-by: Isaac Freund --- src/wayland-client-core.h | 7 ++++ src/wayland-client.c | 75 +++++++++++++++++++++++++++++++++++++++ tests/display-test.c | 69 +++++++++++++++++++++++++++++++++++ 3 files changed, 151 insertions(+) diff --git a/src/wayland-client-core.h b/src/wayland-client-core.h index 970e6254..e0523e49 100644 --- a/src/wayland-client-core.h +++ b/src/wayland-client-core.h @@ -268,9 +268,16 @@ int wl_display_dispatch_queue_pending(struct wl_display *display, struct wl_event_queue *queue); +int +wl_display_dispatch_queue_pending_single(struct wl_display *display, + struct wl_event_queue *queue); + int wl_display_dispatch_pending(struct wl_display *display); +int +wl_display_dispatch_pending_single(struct wl_display *display); + int wl_display_get_error(struct wl_display *display); diff --git a/src/wayland-client.c b/src/wayland-client.c index c0b361f0..ed686b5c 100644 --- a/src/wayland-client.c +++ b/src/wayland-client.c @@ -1882,6 +1882,34 @@ err: return -1; } + +static int +dispatch_queue_single(struct wl_display *display, struct wl_event_queue *queue) +{ + if (display->last_error) + goto err; + + while (!wl_list_empty(&display->display_queue.event_list)) { + dispatch_event(display, &display->display_queue); + if (display->last_error) + goto err; + } + + if (!wl_list_empty(&queue->event_list)) { + dispatch_event(display, queue); + if (display->last_error) + goto err; + return 1; + } else { + return 0; + } + +err: + errno = display->last_error; + + return -1; +} + /** Prepare to read events from the display's file descriptor to a queue * * \param display The display context object @@ -2212,6 +2240,34 @@ wl_display_dispatch_queue_pending(struct wl_display *display, return ret; } +/** Dispatch at most one pending event in an event queue + * + * \param display The display context object + * \param queue The event queue to dispatch + * \return The number of dispatched events (0 or 1) on success or -1 on failure + * + * Dispatch at most one pending event for objects assigned to the given + * event queue. On failure -1 is returned and errno set appropriately. + * If there are no events queued, this function returns immediately. + * + * \memberof wl_display + * \since 1.25.0 + */ +WL_EXPORT int +wl_display_dispatch_queue_pending_single(struct wl_display *display, + struct wl_event_queue *queue) +{ + int ret; + + pthread_mutex_lock(&display->mutex); + + ret = dispatch_queue_single(display, queue); + + pthread_mutex_unlock(&display->mutex); + + return ret; +} + /** Process incoming events * * \param display The display context object @@ -2272,6 +2328,25 @@ wl_display_dispatch_pending(struct wl_display *display) &display->default_queue); } +/** Dispatch at most one pending event in the default event queue. + * + * \param display The display context object + * \return The number of dispatched events (0 or 1) on success or -1 on failure + * + * Dispatch at most one pending event for objects assigned to the default + * event queue. On failure -1 is returned and errno set appropriately. + * If there are no events queued, this function returns immediately. + * + * \memberof wl_display + * \since 1.25.0 + */ +WL_EXPORT int +wl_display_dispatch_pending_single(struct wl_display *display) +{ + return wl_display_dispatch_queue_pending_single(display, + &display->default_queue); +} + /** Retrieve the last error that occurred on a display * * \param display The display context object diff --git a/tests/display-test.c b/tests/display-test.c index 89606c73..fe78b521 100644 --- a/tests/display-test.c +++ b/tests/display-test.c @@ -1695,6 +1695,75 @@ TEST(global_remove) display_destroy(d); } +static void +dispatch_single_read_events(struct wl_display *d) +{ + if (wl_display_prepare_read(d) < 0) { + return; + } + + int ret = 0; + do { + ret = wl_display_flush(d); + } while (ret < 0 && (errno == EINTR || errno == EAGAIN)); + assert(ret >= 0); + + struct pollfd pfd[1]; + pfd[0].fd = wl_display_get_fd(d); + pfd[0].events = POLLIN; + + do { + ret = poll(pfd, 1, -1); + } while (ret < 0 && errno == EINTR); + assert(ret > 0); + + wl_display_read_events(d); +} + +static void +dispatch_single_client(void) +{ + struct client *c = client_connect(); + + assert(wl_display_dispatch_pending_single(c->wl_display) == 0); + + struct wl_registry *registry = wl_display_get_registry(c->wl_display); + + dispatch_single_read_events(c->wl_display); + + // [1815110.061] {Default Queue} wl_registry#3.global(1, "test", 1) + assert(wl_display_dispatch_pending_single(c->wl_display) == 1); + + dispatch_single_read_events(c->wl_display); + + // [1815110.067] {Default Queue} wl_registry#3.global(2, "wl_seat", 1) + assert(wl_display_dispatch_pending_single(c->wl_display) == 1); + + // No more events + assert(wl_display_dispatch_pending_single(c->wl_display) == 0); + + wl_registry_destroy(registry); + + client_disconnect(c); +} + +TEST(dispatch_single) +{ + struct display *d = display_create(); + + struct wl_global *global = wl_global_create(d->wl_display, + &wl_seat_interface, + 1, d, bind_seat); + + client_create_noarg(d, dispatch_single_client); + + display_run(d); + + wl_global_destroy(global); + + display_destroy(d); +} + static void terminate_display(void *arg) { From 8198e0c827df826b13610b1342e22517409a9807 Mon Sep 17 00:00:00 2001 From: Manuel Stoeckl Date: Wed, 25 Sep 2024 19:53:35 -0400 Subject: [PATCH 23/23] =?UTF-8?q?connection:=20print=20debug=20time=20mod?= =?UTF-8?q?=2010000=20seconds,=20not=20mod=202^32=20=C2=B5s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes it easier to compare WAYLAND_DEBUG output with timestamps from other sources. Signed-off-by: Manuel Stoeckl --- src/connection.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/connection.c b/src/connection.c index 2d1e8d1d..dcb56650 100644 --- a/src/connection.c +++ b/src/connection.c @@ -1547,7 +1547,7 @@ wl_closure_print(struct wl_closure *closure, struct wl_object *target, struct argument_details arg; const char *signature = closure->message->signature; struct timespec tp; - unsigned int time; + uint64_t time; uint32_t nval; FILE *f; char *buffer; @@ -1558,10 +1558,10 @@ wl_closure_print(struct wl_closure *closure, struct wl_object *target, return; clock_gettime(CLOCK_REALTIME, &tp); - time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000); + time = ((tp.tv_sec % 10000LL) * 1000000LL) + (tp.tv_nsec / 1000); fprintf(f, "%s[%7u.%03u] ", color ? WL_DEBUG_COLOR_GREEN : "", - time / 1000, time % 1000); + (unsigned int)(time / 1000), (unsigned int)(time % 1000)); #if defined(HAVE_GETTID) if (include_tid < 0) {