From 26c419e046a70521e07b849faae901c7521d90df Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Wed, 8 May 2024 20:54:19 +0200 Subject: [PATCH 01/78] protocol: clarify divergence in compositor behaviour This is intended to only document the current situation. Whether further behaviour will be defined is out of scope and left for protocol v7. See: https://gitlab.freedesktop.org/wayland/wayland/-/merge_requests/363 Signed-off-by: Hugo Osvaldo Barrera --- protocol/wayland.xml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/protocol/wayland.xml b/protocol/wayland.xml index 97ad2eb2..9418c62f 100644 --- a/protocol/wayland.xml +++ b/protocol/wayland.xml @@ -1514,9 +1514,15 @@ mutates the underlying buffer storage, the surface contents become undefined immediately. - If wl_surface.attach is sent with a NULL wl_buffer, or the pending - wl_buffer has been destroyed, the following wl_surface.commit will - remove the surface content. + If wl_surface.attach is sent with a NULL wl_buffer, the + following wl_surface.commit will remove the surface content. + + If a pending wl_buffer has been destroyed, the result is not specified. + Many compositors are known to remove the surface content on the following + wl_surface.commit, but this behaviour is not universal. Clients seeking to + maximise compatibility should not destroy pending buffers and should + ensure that they explicitly remove content from surfaces, even after + destroying buffers. From a156431ea66fe67d69c9fbba8a8ad34dabbab81c Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 30 May 2024 20:59:51 +0200 Subject: [PATCH 02/78] build: bump to version 1.23.0 for the official release Signed-off-by: Simon Ser --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index e2976afc..8e28f2a2 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'wayland', 'c', - version: '1.22.93', + version: '1.23.0', license: 'MIT', meson_version: '>= 0.57.0', default_options: [ From 1d5772b7b9d0bbfbc27557721f62a9f805b66929 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 30 May 2024 21:07:24 +0200 Subject: [PATCH 03/78] 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 8e28f2a2..409cc3c0 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'wayland', 'c', - version: '1.23.0', + version: '1.23.90', license: 'MIT', meson_version: '>= 0.57.0', default_options: [ From caaa308c0d4025928917115c98c75edf7c796cd4 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 7 Jul 2024 18:21:30 +0200 Subject: [PATCH 04/78] scanner: extract validator function emission to helper function This function will grow in the next commit. Signed-off-by: Simon Ser --- src/scanner.c | 59 ++++++++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/src/scanner.c b/src/scanner.c index 95524513..a4f3cc96 100644 --- a/src/scanner.c +++ b/src/scanner.c @@ -1378,6 +1378,37 @@ emit_event_wrappers(struct wl_list *message_list, struct interface *interface) } } +static void +emit_validator(struct interface *interface, struct enumeration *e) +{ + struct entry *entry; + + printf("/**\n" + " * @ingroup iface_%s\n" + " * Validate a %s %s value.\n" + " *\n" + " * @return true on success, false on error.\n" + " * @ref %s_%s\n" + " */\n" + "static inline bool\n" + "%s_%s_is_valid(uint32_t value, uint32_t version) {\n" + " switch (value) {\n", + interface->name, interface->name, e->name, + interface->name, e->name, + interface->name, e->name); + wl_list_for_each(entry, &e->entry_list, link) { + printf(" case %s%s_%s_%s:\n" + " return version >= %d;\n", + entry->value[0] == '-' ? "(uint32_t)" : "", + interface->uppercase_name, e->uppercase_name, + entry->uppercase_name, entry->since); + } + printf(" default:\n" + " return false;\n" + " }\n" + "}\n"); +} + static void emit_enumerations(struct interface *interface, bool with_validators) { @@ -1439,32 +1470,8 @@ emit_enumerations(struct interface *interface, bool with_validators) } - if (with_validators) { - printf("/**\n" - " * @ingroup iface_%s\n" - " * Validate a %s %s value.\n" - " *\n" - " * @return true on success, false on error.\n" - " * @ref %s_%s\n" - " */\n" - "static inline bool\n" - "%s_%s_is_valid(uint32_t value, uint32_t version) {\n" - " switch (value) {\n", - interface->name, interface->name, e->name, - interface->name, e->name, - interface->name, e->name); - wl_list_for_each(entry, &e->entry_list, link) { - printf(" case %s%s_%s_%s:\n" - " return version >= %d;\n", - entry->value[0] == '-' ? "(uint32_t)" : "", - interface->uppercase_name, e->uppercase_name, - entry->uppercase_name, entry->since); - } - printf(" default:\n" - " return false;\n" - " }\n" - "}\n"); - } + if (with_validators) + emit_validator(interface, e); printf("#endif /* %s_%s_ENUM */\n\n", interface->uppercase_name, e->uppercase_name); From c669d992599d74af2762fbf71a4336af2e311c45 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 7 Jul 2024 18:39:51 +0200 Subject: [PATCH 05/78] scanner: fix validator for bitfields Bitfields are valid if the value only contains bits inside of the supported entries for the given version. Signed-off-by: Simon Ser --- src/scanner.c | 37 +++++++++---- tests/data/example-server.h | 106 ++++++++++++++++-------------------- 2 files changed, 73 insertions(+), 70 deletions(-) diff --git a/src/scanner.c b/src/scanner.c index a4f3cc96..3cd05d13 100644 --- a/src/scanner.c +++ b/src/scanner.c @@ -1391,22 +1391,35 @@ emit_validator(struct interface *interface, struct enumeration *e) " * @ref %s_%s\n" " */\n" "static inline bool\n" - "%s_%s_is_valid(uint32_t value, uint32_t version) {\n" - " switch (value) {\n", + "%s_%s_is_valid(uint32_t value, uint32_t version) {\n", interface->name, interface->name, e->name, interface->name, e->name, interface->name, e->name); - wl_list_for_each(entry, &e->entry_list, link) { - printf(" case %s%s_%s_%s:\n" - " return version >= %d;\n", - entry->value[0] == '-' ? "(uint32_t)" : "", - interface->uppercase_name, e->uppercase_name, - entry->uppercase_name, entry->since); + + if (e->bitfield) { + printf(" uint32_t valid = 0;\n"); + wl_list_for_each(entry, &e->entry_list, link) { + printf(" if (version >= %d)\n" + " valid |= %s_%s_%s;\n", + entry->since, + interface->uppercase_name, e->uppercase_name, + entry->uppercase_name); + } + printf(" return (value & ~valid) == 0;\n"); + } else { + printf(" switch (value) {\n"); + wl_list_for_each(entry, &e->entry_list, link) { + printf(" case %s%s_%s_%s:\n" + " return version >= %d;\n", + entry->value[0] == '-' ? "(uint32_t)" : "", + interface->uppercase_name, e->uppercase_name, + entry->uppercase_name, entry->since); + } + printf(" default:\n" + " return false;\n" + " }\n"); } - printf(" default:\n" - " return false;\n" - " }\n" - "}\n"); + printf("}\n"); } static void diff --git a/tests/data/example-server.h b/tests/data/example-server.h index b0d9226d..2fad7097 100644 --- a/tests/data/example-server.h +++ b/tests/data/example-server.h @@ -2396,18 +2396,16 @@ enum wl_data_device_manager_dnd_action { */ static inline bool wl_data_device_manager_dnd_action_is_valid(uint32_t value, uint32_t version) { - switch (value) { - case WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE: - return version >= 1; - case WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY: - return version >= 1; - case WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE: - return version >= 1; - case WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK: - return version >= 1; - default: - return false; - } + uint32_t valid = 0; + if (version >= 1) + valid |= WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; + if (version >= 1) + valid |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + if (version >= 1) + valid |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; + if (version >= 1) + valid |= WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK; + return (value & ~valid) == 0; } #endif /* WL_DATA_DEVICE_MANAGER_DND_ACTION_ENUM */ @@ -2560,28 +2558,26 @@ enum wl_shell_surface_resize { */ static inline bool wl_shell_surface_resize_is_valid(uint32_t value, uint32_t version) { - switch (value) { - case WL_SHELL_SURFACE_RESIZE_NONE: - return version >= 1; - case WL_SHELL_SURFACE_RESIZE_TOP: - return version >= 1; - case WL_SHELL_SURFACE_RESIZE_BOTTOM: - return version >= 1; - case WL_SHELL_SURFACE_RESIZE_LEFT: - return version >= 1; - case WL_SHELL_SURFACE_RESIZE_TOP_LEFT: - return version >= 1; - case WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT: - return version >= 1; - case WL_SHELL_SURFACE_RESIZE_RIGHT: - return version >= 1; - case WL_SHELL_SURFACE_RESIZE_TOP_RIGHT: - return version >= 1; - case WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT: - return version >= 1; - default: - return false; - } + uint32_t valid = 0; + if (version >= 1) + valid |= WL_SHELL_SURFACE_RESIZE_NONE; + if (version >= 1) + valid |= WL_SHELL_SURFACE_RESIZE_TOP; + if (version >= 1) + valid |= WL_SHELL_SURFACE_RESIZE_BOTTOM; + if (version >= 1) + valid |= WL_SHELL_SURFACE_RESIZE_LEFT; + if (version >= 1) + valid |= WL_SHELL_SURFACE_RESIZE_TOP_LEFT; + if (version >= 1) + valid |= WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT; + if (version >= 1) + valid |= WL_SHELL_SURFACE_RESIZE_RIGHT; + if (version >= 1) + valid |= WL_SHELL_SURFACE_RESIZE_TOP_RIGHT; + if (version >= 1) + valid |= WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT; + return (value & ~valid) == 0; } #endif /* WL_SHELL_SURFACE_RESIZE_ENUM */ @@ -2609,12 +2605,10 @@ enum wl_shell_surface_transient { */ static inline bool wl_shell_surface_transient_is_valid(uint32_t value, uint32_t version) { - switch (value) { - case WL_SHELL_SURFACE_TRANSIENT_INACTIVE: - return version >= 1; - default: - return false; - } + uint32_t valid = 0; + if (version >= 1) + valid |= WL_SHELL_SURFACE_TRANSIENT_INACTIVE; + return (value & ~valid) == 0; } #endif /* WL_SHELL_SURFACE_TRANSIENT_ENUM */ @@ -3486,16 +3480,14 @@ enum wl_seat_capability { */ static inline bool wl_seat_capability_is_valid(uint32_t value, uint32_t version) { - switch (value) { - case WL_SEAT_CAPABILITY_POINTER: - return version >= 1; - case WL_SEAT_CAPABILITY_KEYBOARD: - return version >= 1; - case WL_SEAT_CAPABILITY_TOUCH: - return version >= 1; - default: - return false; - } + uint32_t valid = 0; + if (version >= 1) + valid |= WL_SEAT_CAPABILITY_POINTER; + if (version >= 1) + valid |= WL_SEAT_CAPABILITY_KEYBOARD; + if (version >= 1) + valid |= WL_SEAT_CAPABILITY_TOUCH; + return (value & ~valid) == 0; } #endif /* WL_SEAT_CAPABILITY_ENUM */ @@ -4567,14 +4559,12 @@ enum wl_output_mode { */ static inline bool wl_output_mode_is_valid(uint32_t value, uint32_t version) { - switch (value) { - case WL_OUTPUT_MODE_CURRENT: - return version >= 1; - case WL_OUTPUT_MODE_PREFERRED: - return version >= 1; - default: - return false; - } + uint32_t valid = 0; + if (version >= 1) + valid |= WL_OUTPUT_MODE_CURRENT; + if (version >= 1) + valid |= WL_OUTPUT_MODE_PREFERRED; + return (value & ~valid) == 0; } #endif /* WL_OUTPUT_MODE_ENUM */ From fa1811ce3e1475a95aa39e00cfa083797661d651 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 7 Jul 2024 18:48:44 +0200 Subject: [PATCH 06/78] tests: add enum bitfield test Signed-off-by: Simon Ser --- tests/data/small-client-core.h | 23 +++++++++++++++++++ tests/data/small-client.h | 23 +++++++++++++++++++ tests/data/small-server-core.h | 41 ++++++++++++++++++++++++++++++++++ tests/data/small-server.h | 41 ++++++++++++++++++++++++++++++++++ tests/data/small.xml | 7 ++++++ tests/enum-validator-test.c | 15 +++++++++++++ 6 files changed, 150 insertions(+) diff --git a/tests/data/small-client-core.h b/tests/data/small-client-core.h index 0e722441..03f889c8 100644 --- a/tests/data/small-client-core.h +++ b/tests/data/small-client-core.h @@ -106,6 +106,29 @@ enum intf_A_foo { #define INTF_A_FOO_DEPRECATED_SINCE_VERSION 2 #endif /* INTF_A_FOO_ENUM */ +#ifndef INTF_A_BAR_ENUM +#define INTF_A_BAR_ENUM +enum intf_A_bar { + /** + * this is the first + */ + INTF_A_BAR_FIRST = 0x01, + /** + * this is the second + */ + INTF_A_BAR_SECOND = 0x02, + /** + * this is the third + * @since 2 + */ + INTF_A_BAR_THIRD = 0x04, +}; +/** + * @ingroup iface_intf_A + */ +#define INTF_A_BAR_THIRD_SINCE_VERSION 2 +#endif /* INTF_A_BAR_ENUM */ + /** * @ingroup iface_intf_A * @struct intf_A_listener diff --git a/tests/data/small-client.h b/tests/data/small-client.h index ad435923..0d5b6055 100644 --- a/tests/data/small-client.h +++ b/tests/data/small-client.h @@ -106,6 +106,29 @@ enum intf_A_foo { #define INTF_A_FOO_DEPRECATED_SINCE_VERSION 2 #endif /* INTF_A_FOO_ENUM */ +#ifndef INTF_A_BAR_ENUM +#define INTF_A_BAR_ENUM +enum intf_A_bar { + /** + * this is the first + */ + INTF_A_BAR_FIRST = 0x01, + /** + * this is the second + */ + INTF_A_BAR_SECOND = 0x02, + /** + * this is the third + * @since 2 + */ + INTF_A_BAR_THIRD = 0x04, +}; +/** + * @ingroup iface_intf_A + */ +#define INTF_A_BAR_THIRD_SINCE_VERSION 2 +#endif /* INTF_A_BAR_ENUM */ + /** * @ingroup iface_intf_A * @struct intf_A_listener diff --git a/tests/data/small-server-core.h b/tests/data/small-server-core.h index e696cde7..d4476959 100644 --- a/tests/data/small-server-core.h +++ b/tests/data/small-server-core.h @@ -133,6 +133,47 @@ intf_A_foo_is_valid(uint32_t value, uint32_t version) { } #endif /* INTF_A_FOO_ENUM */ +#ifndef INTF_A_BAR_ENUM +#define INTF_A_BAR_ENUM +enum intf_A_bar { + /** + * this is the first + */ + INTF_A_BAR_FIRST = 0x01, + /** + * this is the second + */ + INTF_A_BAR_SECOND = 0x02, + /** + * this is the third + * @since 2 + */ + INTF_A_BAR_THIRD = 0x04, +}; +/** + * @ingroup iface_intf_A + */ +#define INTF_A_BAR_THIRD_SINCE_VERSION 2 +/** + * @ingroup iface_intf_A + * Validate a intf_A bar value. + * + * @return true on success, false on error. + * @ref intf_A_bar + */ +static inline bool +intf_A_bar_is_valid(uint32_t value, uint32_t version) { + uint32_t valid = 0; + if (version >= 1) + valid |= INTF_A_BAR_FIRST; + if (version >= 1) + valid |= INTF_A_BAR_SECOND; + if (version >= 2) + valid |= INTF_A_BAR_THIRD; + return (value & ~valid) == 0; +} +#endif /* INTF_A_BAR_ENUM */ + /** * @ingroup iface_intf_A * @struct intf_A_interface diff --git a/tests/data/small-server.h b/tests/data/small-server.h index 009d9cdd..9cafcd93 100644 --- a/tests/data/small-server.h +++ b/tests/data/small-server.h @@ -133,6 +133,47 @@ intf_A_foo_is_valid(uint32_t value, uint32_t version) { } #endif /* INTF_A_FOO_ENUM */ +#ifndef INTF_A_BAR_ENUM +#define INTF_A_BAR_ENUM +enum intf_A_bar { + /** + * this is the first + */ + INTF_A_BAR_FIRST = 0x01, + /** + * this is the second + */ + INTF_A_BAR_SECOND = 0x02, + /** + * this is the third + * @since 2 + */ + INTF_A_BAR_THIRD = 0x04, +}; +/** + * @ingroup iface_intf_A + */ +#define INTF_A_BAR_THIRD_SINCE_VERSION 2 +/** + * @ingroup iface_intf_A + * Validate a intf_A bar value. + * + * @return true on success, false on error. + * @ref intf_A_bar + */ +static inline bool +intf_A_bar_is_valid(uint32_t value, uint32_t version) { + uint32_t valid = 0; + if (version >= 1) + valid |= INTF_A_BAR_FIRST; + if (version >= 1) + valid |= INTF_A_BAR_SECOND; + if (version >= 2) + valid |= INTF_A_BAR_THIRD; + return (value & ~valid) == 0; +} +#endif /* INTF_A_BAR_ENUM */ + /** * @ingroup iface_intf_A * @struct intf_A_interface diff --git a/tests/data/small.xml b/tests/data/small.xml index ac527795..ab297490 100644 --- a/tests/data/small.xml +++ b/tests/data/small.xml @@ -58,5 +58,12 @@ + + + + + + + diff --git a/tests/enum-validator-test.c b/tests/enum-validator-test.c index 92037cff..8fb05b43 100644 --- a/tests/enum-validator-test.c +++ b/tests/enum-validator-test.c @@ -10,4 +10,19 @@ main(int argc, char *argv[]) { assert(intf_A_foo_is_valid(INTF_A_FOO_THIRD, 2)); assert(intf_A_foo_is_valid(INTF_A_FOO_NEGATIVE, 2)); + + assert(intf_A_bar_is_valid(INTF_A_BAR_FIRST, 1)); + assert(intf_A_bar_is_valid(INTF_A_BAR_FIRST, 2)); + assert(intf_A_bar_is_valid(INTF_A_BAR_SECOND, 1)); + assert(intf_A_bar_is_valid(INTF_A_BAR_SECOND, 2)); + assert(intf_A_bar_is_valid(INTF_A_BAR_FIRST | INTF_A_BAR_SECOND, 1)); + assert(intf_A_bar_is_valid(INTF_A_BAR_FIRST | INTF_A_BAR_SECOND, 2)); + + assert(!intf_A_bar_is_valid(INTF_A_BAR_THIRD, 1)); + assert(!intf_A_bar_is_valid(INTF_A_BAR_FIRST | INTF_A_BAR_THIRD, 1)); + assert(intf_A_bar_is_valid(INTF_A_BAR_THIRD, 2)); + assert(intf_A_bar_is_valid(INTF_A_BAR_FIRST | INTF_A_BAR_THIRD, 2)); + + assert(!intf_A_bar_is_valid(0xFF, 1)); + assert(!intf_A_bar_is_valid(0xFF, 2)); } From 0cecde304f4f78b439d2edaaab6d9b543b30a5c0 Mon Sep 17 00:00:00 2001 From: meltq Date: Sun, 30 Jun 2024 22:36:11 +0530 Subject: [PATCH 07/78] src: switch asserts to wl_abort assert()s can be compiled away by #defining NDEBUG. Some build systems do this. Using wl_abort gives a human readable error message and it isn't compiled away. This commit closes issue #230. Signed-off-by: meltq --- src/connection.c | 3 ++- src/event-loop.c | 6 ++++-- src/wayland-client.c | 17 ++++++++++++----- src/wayland-server.c | 3 ++- src/wayland-shm.c | 23 ++++++++++++++++------- 5 files changed, 36 insertions(+), 16 deletions(-) diff --git a/src/connection.c b/src/connection.c index 8870fd2d..fb6a4be8 100644 --- a/src/connection.c +++ b/src/connection.c @@ -75,7 +75,8 @@ struct wl_connection { static inline size_t size_pot(uint32_t size_bits) { - assert(size_bits < 8 * sizeof(size_t)); + if (!(size_bits < 8 * sizeof(size_t))) + wl_abort("Too many bits for size_t\n"); return ((size_t)1) << size_bits; } diff --git a/src/event-loop.c b/src/event-loop.c index 45222f71..f6392521 100644 --- a/src/event-loop.c +++ b/src/event-loop.c @@ -447,7 +447,8 @@ wl_timer_heap_disarm(struct wl_timer_heap *timers, struct wl_event_source_timer *last_end_evt; int old_source_idx; - assert(source->heap_idx >= 0); + if (!(source->heap_idx >= 0)) + wl_abort("Timer has already been disarmed\n"); old_source_idx = source->heap_idx; source->heap_idx = -1; @@ -476,7 +477,8 @@ wl_timer_heap_arm(struct wl_timer_heap *timers, struct wl_event_source_timer *source, struct timespec deadline) { - assert(source->heap_idx == -1); + if (!(source->heap_idx == -1)) + wl_abort("Timer is already armed\n"); source->deadline = deadline; timers->data[timers->active] = source; diff --git a/src/wayland-client.c b/src/wayland-client.c index 9cf27939..db5bcb58 100644 --- a/src/wayland-client.c +++ b/src/wayland-client.c @@ -235,13 +235,16 @@ wl_event_queue_init(struct wl_event_queue *queue, static void wl_proxy_unref(struct wl_proxy *proxy) { - assert(proxy->refcount > 0); + if (!(proxy->refcount > 0)) + wl_abort("Proxy requested for unref has no references\n"); if (--proxy->refcount > 0) return; /* If we get here, the client must have explicitly requested * deletion. */ - assert(proxy->flags & WL_PROXY_FLAG_DESTROYED); + if (!(proxy->flags & WL_PROXY_FLAG_DESTROYED)) + wl_abort("Proxy with no references not yet explicitly" + "destroyed\n"); free(proxy); } @@ -1172,7 +1175,8 @@ connect_to_socket(const char *name) "%s", name) + 1; } - assert(name_size > 0); + if (!(name_size > 0)) + wl_abort("Error assigning path name for socket connection\n"); if (name_size > (int)sizeof addr.sun_path) { if (!path_is_absolute) { wl_log("error: socket path \"%s/%s\" plus null terminator" @@ -2453,7 +2457,9 @@ wl_proxy_set_queue(struct wl_proxy *proxy, struct wl_event_queue *queue) wl_list_remove(&proxy->queue_link); if (queue) { - assert(proxy->display == queue->display); + if (!(proxy->display == queue->display)) + wl_abort("Proxy and queue point to different " + "wl_displays"); proxy->queue = queue; } else { proxy->queue = &proxy->display->default_queue; @@ -2581,7 +2587,8 @@ wl_proxy_wrapper_destroy(void *proxy_wrapper) wl_abort("Tried to destroy non-wrapper proxy with " "wl_proxy_wrapper_destroy\n"); - assert(wrapper->refcount == 1); + if (!(wrapper->refcount == 1)) + wl_abort("Expected proxy wrapper's refcount to be 1\n"); pthread_mutex_lock(&wrapper->display->mutex); diff --git a/src/wayland-server.c b/src/wayland-server.c index 2e185634..95f69e7f 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -1714,7 +1714,8 @@ wl_socket_init_for_display_name(struct wl_socket *s, const char *name) name_size = snprintf(s->addr.sun_path, sizeof s->addr.sun_path, "%s%s%s", runtime_dir, separator, name) + 1; - assert(name_size > 0); + if (!(name_size > 0)) + wl_abort("Error assigning path name for socket address\n"); if (name_size > (int)sizeof s->addr.sun_path) { wl_log("error: socket path \"%s%s%s\" plus null terminator" " exceeds 108 bytes\n", runtime_dir, separator, name); diff --git a/src/wayland-shm.c b/src/wayland-shm.c index 0a11736a..70685e6c 100644 --- a/src/wayland-shm.c +++ b/src/wayland-shm.c @@ -144,12 +144,17 @@ shm_pool_unref(struct wl_shm_pool *pool, bool external) { if (external) { pool->external_refcount--; - assert(pool->external_refcount >= 0); + if (!(pool->external_refcount >= 0)) + wl_abort("Requested to unref an external reference to " + "pool but none found\n"); if (pool->external_refcount == 0) shm_pool_finish_resize(pool); } else { pool->internal_refcount--; - assert(pool->internal_refcount >= 0); + if (!(pool->internal_refcount >= 0)) + wl_abort("Requested to unref an internal reference to " + "pool but none found\n"); + } if (pool->internal_refcount + pool->external_refcount > 0) @@ -509,8 +514,9 @@ wl_shm_buffer_get_height(struct wl_shm_buffer *buffer) WL_EXPORT struct wl_shm_pool * wl_shm_buffer_ref_pool(struct wl_shm_buffer *buffer) { - assert(buffer->pool->internal_refcount + - buffer->pool->external_refcount); + if (!(buffer->pool->internal_refcount + + buffer->pool->external_refcount)) + wl_abort("Can't get reference to pool that has been freed\n"); buffer->pool->external_refcount++; return buffer->pool; @@ -658,8 +664,9 @@ wl_shm_buffer_begin_access(struct wl_shm_buffer *buffer) pthread_setspecific(wl_shm_sigbus_data_key, sigbus_data); } - assert(sigbus_data->current_pool == NULL || - sigbus_data->current_pool == pool); + if (!(sigbus_data->current_pool == NULL || + sigbus_data->current_pool == pool)) + wl_abort("Incorrect pool passed for current thread\n"); sigbus_data->current_pool = pool; sigbus_data->access_count++; @@ -686,7 +693,9 @@ wl_shm_buffer_end_access(struct wl_shm_buffer *buffer) return; sigbus_data = pthread_getspecific(wl_shm_sigbus_data_key); - assert(sigbus_data && sigbus_data->access_count >= 1); + if (!(sigbus_data && sigbus_data->access_count >= 1)) + wl_abort("sigbus_data is NULL or wl_shm_buffer_begin_access " + "wasn't called before\n"); if (--sigbus_data->access_count == 0) { if (sigbus_data->fallback_mapping_used) { From f6f0a3cdec1c23c0adfe1deb9c53e8fdbb998ea4 Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Fri, 26 Jul 2024 16:35:15 +0200 Subject: [PATCH 08/78] client: Handle proxies with no queue wl_proxy_get_queue can return NULL if the queue of the proxy was already destroyed with wl_event_queue_destroy. In this case, the queue also has no name anymore. Fixes: b42218f ("client: Allow setting names for queues") Signed-off-by: Sebastian Wick --- src/wayland-client.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/wayland-client.c b/src/wayland-client.c index db5bcb58..a2c9ad95 100644 --- a/src/wayland-client.c +++ b/src/wayland-client.c @@ -924,10 +924,14 @@ wl_proxy_marshal_array_flags(struct wl_proxy *proxy, uint32_t opcode, if (debug_client) { struct wl_event_queue *queue; + const char *queue_name = NULL; queue = wl_proxy_get_queue(proxy); + if (queue) + queue_name = wl_event_queue_get_name(queue); + wl_closure_print(closure, &proxy->object, true, false, NULL, - wl_event_queue_get_name(queue)); + queue_name); } if (wl_closure_send(closure, proxy->display->connection)) { From a6a4e081da12140ffe1881d83be0981966ba6daf Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Sat, 29 Jun 2024 15:05:00 +0300 Subject: [PATCH 09/78] Put WL_DEPRECATED in front of the function declarations This fixes the following clang error when using C23: ../src/wayland-server-core.h:680:41: error: 'deprecated' attribute cannot be applied to types 680 | int32_t stride, uint32_t format) WL_DEPRECATED; | ^ ../src/wayland-util.h:52:25: note: expanded from macro 'WL_DEPRECATED' 52 | #define WL_DEPRECATED [[deprecated]] | ^ Signed-off-by: Kirill Primak --- src/wayland-server-core.h | 3 ++- src/wayland-server.c | 15 ++++++++++----- src/wayland-server.h | 15 ++++++++++----- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/wayland-server-core.h b/src/wayland-server-core.h index 63d0f02d..fb900a91 100644 --- a/src/wayland-server-core.h +++ b/src/wayland-server-core.h @@ -674,10 +674,11 @@ wl_display_init_shm(struct wl_display *display); uint32_t * wl_display_add_shm_format(struct wl_display *display, uint32_t format); +WL_DEPRECATED struct wl_shm_buffer * wl_shm_buffer_create(struct wl_client *client, uint32_t id, int32_t width, int32_t height, - int32_t stride, uint32_t format) WL_DEPRECATED; + int32_t stride, uint32_t format); void wl_log_set_handler_server(wl_log_func_t handler); diff --git a/src/wayland-server.c b/src/wayland-server.c index 95f69e7f..2fe69afe 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -2484,9 +2484,10 @@ wl_priv_signal_final_emit(struct wl_priv_signal *signal, void *data) /** \cond */ /* Deprecated functions below. */ +WL_DEPRECATED uint32_t wl_client_add_resource(struct wl_client *client, - struct wl_resource *resource) WL_DEPRECATED; + struct wl_resource *resource); WL_EXPORT uint32_t wl_client_add_resource(struct wl_client *client, @@ -2515,11 +2516,12 @@ wl_client_add_resource(struct wl_client *client, return resource->object.id; } +WL_DEPRECATED struct wl_resource * wl_client_add_object(struct wl_client *client, const struct wl_interface *interface, const void *implementation, - uint32_t id, void *data) WL_DEPRECATED; + uint32_t id, void *data); WL_EXPORT struct wl_resource * wl_client_add_object(struct wl_client *client, @@ -2538,10 +2540,11 @@ wl_client_add_object(struct wl_client *client, return resource; } +WL_DEPRECATED struct wl_resource * wl_client_new_object(struct wl_client *client, const struct wl_interface *interface, - const void *implementation, void *data) WL_DEPRECATED; + const void *implementation, void *data); WL_EXPORT struct wl_resource * wl_client_new_object(struct wl_client *client, @@ -2600,10 +2603,11 @@ wl_client_get_user_data(struct wl_client *client) return client->data; } +WL_DEPRECATED struct wl_global * wl_display_add_global(struct wl_display *display, const struct wl_interface *interface, - void *data, wl_global_bind_func_t bind) WL_DEPRECATED; + void *data, wl_global_bind_func_t bind); WL_EXPORT struct wl_global * wl_display_add_global(struct wl_display *display, @@ -2613,9 +2617,10 @@ wl_display_add_global(struct wl_display *display, return wl_global_create(display, interface, interface->version, data, bind); } +WL_DEPRECATED void wl_display_remove_global(struct wl_display *display, - struct wl_global *global) WL_DEPRECATED; + struct wl_global *global); WL_EXPORT void wl_display_remove_global(struct wl_display *display, struct wl_global *global) diff --git a/src/wayland-server.h b/src/wayland-server.h index 1be565f2..48fab1dd 100644 --- a/src/wayland-server.h +++ b/src/wayland-server.h @@ -70,30 +70,35 @@ struct wl_resource { void *data; }; +WL_DEPRECATED uint32_t wl_client_add_resource(struct wl_client *client, - struct wl_resource *resource) WL_DEPRECATED; + struct wl_resource *resource); +WL_DEPRECATED struct wl_resource * wl_client_add_object(struct wl_client *client, const struct wl_interface *interface, const void *implementation, - uint32_t id, void *data) WL_DEPRECATED; + uint32_t id, void *data); +WL_DEPRECATED struct wl_resource * wl_client_new_object(struct wl_client *client, const struct wl_interface *interface, - const void *implementation, void *data) WL_DEPRECATED; + const void *implementation, void *data); +WL_DEPRECATED struct wl_global * wl_display_add_global(struct wl_display *display, const struct wl_interface *interface, void *data, - wl_global_bind_func_t bind) WL_DEPRECATED; + wl_global_bind_func_t bind); +WL_DEPRECATED void wl_display_remove_global(struct wl_display *display, - struct wl_global *global) WL_DEPRECATED; + struct wl_global *global); #endif From 64248963d31395b373e095972f46f61fb95bf462 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Thu, 23 May 2024 19:25:05 +0300 Subject: [PATCH 10/78] server: add wl_resource_post_error() docs Signed-off-by: Kirill Primak --- src/wayland-server-core.h | 1 - src/wayland-server.c | 9 +++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/wayland-server-core.h b/src/wayland-server-core.h index fb900a91..26c527ef 100644 --- a/src/wayland-server-core.h +++ b/src/wayland-server-core.h @@ -550,7 +550,6 @@ void wl_resource_queue_event_array(struct wl_resource *resource, uint32_t opcode, union wl_argument *args); -/* msg is a printf format string, variable args are its args. */ void wl_resource_post_error(struct wl_resource *resource, uint32_t code, const char *msg, ...) WL_PRINTF(3, 4); diff --git a/src/wayland-server.c b/src/wayland-server.c index 2fe69afe..c54a6d64 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -313,6 +313,15 @@ wl_resource_post_error_vargs(struct wl_resource *resource, } +/** Post a protocol error + * + * \param resource The resource object + * \param code The error code + * \param msg The error message format string + * \param ... The format string arguments + * + * \memberof wl_resource + */ WL_EXPORT void wl_resource_post_error(struct wl_resource *resource, uint32_t code, const char *msg, ...) From 65454cf7db29933d4931dfbe14461a59cc64944d Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Thu, 23 May 2024 19:25:40 +0300 Subject: [PATCH 11/78] server: expose wl_resource_post_error_vargs() Signed-off-by: Kirill Primak --- src/wayland-server-core.h | 4 ++++ src/wayland-server.c | 12 ++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/wayland-server-core.h b/src/wayland-server-core.h index 26c527ef..b1baa25f 100644 --- a/src/wayland-server-core.h +++ b/src/wayland-server-core.h @@ -550,6 +550,10 @@ void wl_resource_queue_event_array(struct wl_resource *resource, uint32_t opcode, union wl_argument *args); +void +wl_resource_post_error_vargs(struct wl_resource *resource, + uint32_t code, const char *msg, va_list argp); + void wl_resource_post_error(struct wl_resource *resource, uint32_t code, const char *msg, ...) WL_PRINTF(3, 4); diff --git a/src/wayland-server.c b/src/wayland-server.c index c54a6d64..5ea9101b 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -288,7 +288,16 @@ wl_resource_queue_event(struct wl_resource *resource, uint32_t opcode, ...) wl_resource_queue_event_array(resource, opcode, args); } -static void +/** Post a protocol error + * + * \param resource The resource object + * \param code The error code + * \param msg The error message format string + * \param argp The format string argument list + * + * \memberof wl_resource + */ +WL_EXPORT void wl_resource_post_error_vargs(struct wl_resource *resource, uint32_t code, const char *msg, va_list argp) { @@ -310,7 +319,6 @@ wl_resource_post_error_vargs(struct wl_resource *resource, wl_resource_post_event(client->display_resource, WL_DISPLAY_ERROR, resource, code, buffer); client->error = true; - } /** Post a protocol error From 2bbd80c8df129ee6810f6ec6d8d379ed3190760b Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Tue, 23 Apr 2024 19:17:22 +0200 Subject: [PATCH 12/78] doc: Require strings to be UTF-8 Nothing checks this yet but this gives us the opportunity to do so when we want. Signed-off-by: Sebastian Wick --- doc/publican/sources/Protocol.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/publican/sources/Protocol.xml b/doc/publican/sources/Protocol.xml index 89d76d8e..38243fa7 100644 --- a/doc/publican/sources/Protocol.xml +++ b/doc/publican/sources/Protocol.xml @@ -150,9 +150,9 @@ Starts with an unsigned 32-bit length (including null terminator), - followed by the string contents, including terminating null byte, - then padding to a 32-bit boundary. A null value is represented - with a length of 0. + followed by the UTF-8 encoded string contents, including + terminating null byte, then padding to a 32-bit boundary. A null + value is represented with a length of 0. From 58bb6c721126c12ab644bee557bef6825562440a Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Tue, 16 Jul 2024 06:31:13 -0500 Subject: [PATCH 13/78] src: Finish assert() clean-up From cleanup commit 0cecde304: assert()s can be compiled away by #defining NDEBUG. Some build systems do this. Using wl_abort gives a human readable error message and it isn't compiled away. That commit missed one final assert, presumably due to missing it with grep because of a coding style issue. Fix that up, and remove inclusion of as appropriate. Signed-off-by: Derek Foreman --- cursor/convert_font.c | 1 - src/connection.c | 1 - src/event-loop.c | 1 - src/wayland-client.c | 1 - src/wayland-server.c | 4 ++-- src/wayland-shm.c | 1 - 6 files changed, 2 insertions(+), 7 deletions(-) diff --git a/cursor/convert_font.c b/cursor/convert_font.c index 74e45fbc..77e34c20 100644 --- a/cursor/convert_font.c +++ b/cursor/convert_font.c @@ -29,7 +29,6 @@ * http://fontforge.org/pcf-format.html */ -#include #include #include #include diff --git a/src/connection.c b/src/connection.c index fb6a4be8..e1b751ac 100644 --- a/src/connection.c +++ b/src/connection.c @@ -26,7 +26,6 @@ #define _GNU_SOURCE -#include #include #include #include diff --git a/src/event-loop.c b/src/event-loop.c index f6392521..51c9b9d0 100644 --- a/src/event-loop.c +++ b/src/event-loop.c @@ -23,7 +23,6 @@ * SOFTWARE. */ -#include #include #include #include diff --git a/src/wayland-client.c b/src/wayland-client.c index a2c9ad95..f7d38d4b 100644 --- a/src/wayland-client.c +++ b/src/wayland-client.c @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include diff --git a/src/wayland-server.c b/src/wayland-server.c index 5ea9101b..1d6be3ec 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include @@ -1534,7 +1533,8 @@ wl_display_terminate(struct wl_display *display) display->run = false; ret = write(display->terminate_efd, &terminate, sizeof(terminate)); - assert (ret >= 0 || errno == EAGAIN); + if (ret < 0 && errno != EAGAIN) + wl_abort("Write failed at shutdown\n"); } WL_EXPORT void diff --git a/src/wayland-shm.c b/src/wayland-shm.c index 70685e6c..87917dad 100644 --- a/src/wayland-shm.c +++ b/src/wayland-shm.c @@ -40,7 +40,6 @@ #include #include #include -#include #include #include #include From efa648056a1f2574708ec1a36a5f23733dd8c063 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 9 Aug 2024 22:14:10 +0200 Subject: [PATCH 14/78] ci: use detached MR pipelines See the freedesktop wiki [1]. This allows external contributors to have CI run properly. [1]: https://gitlab.freedesktop.org/freedesktop/freedesktop/-/wikis/GitLab-CI#for-project-developers Signed-off-by: Simon Ser --- .gitlab-ci.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7dc7e33f..079ad8ba 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -62,6 +62,13 @@ stages: - "Build and test" - "Other build configurations" +workflow: + rules: + - if: $CI_PIPELINE_SOURCE == "merge_request_event" + - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS + when: never + - if: $CI_COMMIT_BRANCH + .ci-rules: rules: - when: on_success From 5b692b50b9e3d379005633d4ac20068d2069849d Mon Sep 17 00:00:00 2001 From: Fangzhou Ge Date: Wed, 7 Aug 2024 18:21:20 +0000 Subject: [PATCH 15/78] client: Log the object and methods when marshalling or sending fails The log that appears before a display_error can be captured as crash signature. Useful to know what it is. This is cherry-picked from chromium https://crrev.com/c/4697877 Signed-off-by: Fangzhou Ge --- src/wayland-client.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/wayland-client.c b/src/wayland-client.c index f7d38d4b..2f6e00c2 100644 --- a/src/wayland-client.c +++ b/src/wayland-client.c @@ -916,7 +916,9 @@ wl_proxy_marshal_array_flags(struct wl_proxy *proxy, uint32_t opcode, closure = wl_closure_marshal(&proxy->object, opcode, args, message); if (closure == NULL) { - wl_log("Error marshalling request: %s\n", strerror(errno)); + wl_log("Error marshalling request for %s.%s: %s\n", + proxy->object.interface->name, message->name, + strerror(errno)); display_fatal_error(proxy->display, errno); goto err_unlock; } @@ -934,7 +936,9 @@ wl_proxy_marshal_array_flags(struct wl_proxy *proxy, uint32_t opcode, } if (wl_closure_send(closure, proxy->display->connection)) { - wl_log("Error sending request: %s\n", strerror(errno)); + wl_log("Error sending request for %s.%s: %s\n", + proxy->object.interface->name, message->name, + strerror(errno)); display_fatal_error(proxy->display, errno); } From 0239b082b9d312275ce1112b61d795072596ce2d Mon Sep 17 00:00:00 2001 From: Joaquim Monteiro Date: Fri, 31 May 2024 02:57:29 +0100 Subject: [PATCH 16/78] meson: Fix use of install_data() without specifying install_dir This was broken (when in a subproject) before Meson 1.3.0, and so Meson warns against this unless the project targets 1.3.0 or newer. Signed-off-by: Joaquim Monteiro --- meson.build | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 409cc3c0..6b05bb52 100644 --- a/meson.build +++ b/meson.build @@ -131,7 +131,9 @@ if get_option('scanner') 'wayland-scanner.mk', 'protocol/wayland.xml', 'protocol/wayland.dtd', - ]) + ], + install_dir: join_paths(get_option('prefix'), get_option('datadir'), 'wayland'), + ) install_data( [ 'wayland-scanner.m4' ], From 6c4a695045155583a99f3fbce7bb745f79c2e726 Mon Sep 17 00:00:00 2001 From: Demi Marie Obenour Date: Wed, 24 Jul 2024 21:20:12 -0400 Subject: [PATCH 17/78] connection: Reject strings containing NUL bytes libwayland cannot construct these messages as it uses strlen() to determine string lengths. libwayland is also guaranteed to misinterpret these messages, since message handlers only get a pointer and no length. Therefore, reject strings containing NUL bytes. Also remove a redundant check from the unmarshalling code. The zero-length case has already been checked for. Signed-off-by: Demi Marie Obenour --- doc/publican/sources/Protocol.xml | 3 ++- src/connection.c | 10 +++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/doc/publican/sources/Protocol.xml b/doc/publican/sources/Protocol.xml index 38243fa7..692f17eb 100644 --- a/doc/publican/sources/Protocol.xml +++ b/doc/publican/sources/Protocol.xml @@ -152,7 +152,8 @@ Starts with an unsigned 32-bit length (including null terminator), followed by the UTF-8 encoded string contents, including terminating null byte, then padding to a 32-bit boundary. A null - value is represented with a length of 0. + value is represented with a length of 0. Interior null bytes are + not permitted. diff --git a/src/connection.c b/src/connection.c index e1b751ac..6b28d21d 100644 --- a/src/connection.c +++ b/src/connection.c @@ -975,7 +975,7 @@ wl_connection_demarshal(struct wl_connection *connection, s = (char *) p; - if (length > 0 && s[length - 1] != '\0') { + if (s[length - 1] != '\0') { wl_log("string not nul-terminated, " "message %s(%s)\n", message->name, message->signature); @@ -983,6 +983,14 @@ wl_connection_demarshal(struct wl_connection *connection, goto err; } + if (strlen(s) != length - 1) { + wl_log("string has embedded nul at offset %zu, " + "message %s(%s)\n", strlen(s), + message->name, message->signature); + errno = EINVAL; + goto err; + } + closure->args[i].s = s; p = next; break; From 7c6259e9ad7562d824b024975b80e471b365d5fc Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Fri, 6 Sep 2024 13:28:07 +0200 Subject: [PATCH 18/78] protocol: clients should not emulate key-press events on enter The previous change introducing the logical state caused some confusion. Clarify that most application should not use the list of pressed keys. Signed-off-by: Julian Orth --- protocol/wayland.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/protocol/wayland.xml b/protocol/wayland.xml index 9418c62f..3b279a0b 100644 --- a/protocol/wayland.xml +++ b/protocol/wayland.xml @@ -2479,6 +2479,9 @@ the surface argument and the keys currently logically down to the keys in the keys argument. The compositor must not send this event if the wl_keyboard already had an active surface immediately before this event. + + Clients should not use the list of pressed keys to emulate key-press + events. The order of keys in the list is unspecified. From 1b0d45e9c6bc05399092f50b6c27bcdb43869e58 Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Wed, 21 Feb 2024 21:43:36 +0000 Subject: [PATCH 19/78] Add wl_keyboard key repeat events This allows the compositor to take over the responsibility of repeating keys. Signed-off-by: Andri Yngvason --- protocol/wayland.xml | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/protocol/wayland.xml b/protocol/wayland.xml index 3b279a0b..e0a67380 100644 --- a/protocol/wayland.xml +++ b/protocol/wayland.xml @@ -1880,7 +1880,7 @@ - + A seat is a group of keyboards, pointer and touch devices. This object is published as a global during start up, or when such a @@ -2013,7 +2013,7 @@ - + The wl_pointer interface represents one or more input devices, such as mice, which control the pointer location and pointer_focus @@ -2426,7 +2426,7 @@ - + The wl_keyboard interface represents one or more keyboards associated with a seat. @@ -2508,9 +2508,18 @@ Describes the physical state of a key that produced the key event. + + Since version 10, the key can be in a "repeated" pseudo-state which + means the same as "pressed", but is used to signal repetition in the + key event. + + The key may only enter the repeated state after entering the pressed + state and before entering the released state. This event may be + generated multiple times while the key is down. + @@ -2533,6 +2542,11 @@ compositor must not send this event if state is pressed (resp. released) and the key was already logically down (resp. was not logically down) immediately before this event. + + Since version 10, compositors may send key events with the "repeated" + key state when a wl_keyboard.repeat_info event with a rate argument of + 0 has been received. This allows the compositor to take over the + responsibility of key repetition. @@ -2593,7 +2607,7 @@ - + The wl_touch interface represents a touchscreen associated with a seat. From 38f91fe6adb1c4e6347dc34111e17514dac4a439 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 19 Oct 2023 16:34:26 +0200 Subject: [PATCH 20/78] protocol: document that wl_surface.offset is role-specific This request doesn't make sense for all surface roles. For instance, for maximized/tiled/fullscreen xdg_toplevel, for xdg_popup, for layer-shell surfaces, etc. Signed-off-by: Simon Ser --- protocol/wayland.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/protocol/wayland.xml b/protocol/wayland.xml index e0a67380..cd089e1b 100644 --- a/protocol/wayland.xml +++ b/protocol/wayland.xml @@ -1832,6 +1832,9 @@ x and y, combined with the new surface size define in which directions the surface's size changes. + The exact semantics of wl_surface.offset are role-specific. Refer to + the documentation of specific roles for more information. + Surface location offset is double-buffered state, see wl_surface.commit. From f67db75ec118f6ec64e60113dbdecb20b4ea1abd Mon Sep 17 00:00:00 2001 From: YaoBing Xiao Date: Fri, 18 Oct 2024 10:25:21 +0800 Subject: [PATCH 21/78] cursor: add check to ensure wl_shm_create_pool succeeded Signed-off-by: YaoBing Xiao --- cursor/wayland-cursor.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cursor/wayland-cursor.c b/cursor/wayland-cursor.c index 156f0a80..636f5166 100644 --- a/cursor/wayland-cursor.c +++ b/cursor/wayland-cursor.c @@ -68,11 +68,16 @@ shm_pool_create(struct wl_shm *shm, int size) goto err_close; pool->pool = wl_shm_create_pool(shm, pool->fd, size); + if (!pool->pool) + goto err_unmap; + pool->size = size; pool->used = 0; return pool; +err_unmap: + munmap(pool->data, size); err_close: close(pool->fd); err_free: From 10df74c240d3cff4bd56bf17ebfc1a54883e00f5 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Wed, 24 Apr 2024 16:17:58 +0200 Subject: [PATCH 22/78] protocol: add wl_fixes interface This commit describes a new wl_fixes interface that can be used to destroy wl_registry objects. Users of libwayland-client should use it as follows: - call wl_fixes_destroy_registry(registry) - call wl_registry_destroy(registry) Users of libwayland-server should, in their implementation of the request, call wl_resource_destroy(registry). It should be similar in other protocol implementations. Signed-off-by: Julian Orth --- protocol/wayland.xml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/protocol/wayland.xml b/protocol/wayland.xml index cd089e1b..f32918e9 100644 --- a/protocol/wayland.xml +++ b/protocol/wayland.xml @@ -3265,4 +3265,31 @@ + + + This global fixes problems with other core-protocol interfaces that + cannot be fixed in these interfaces themselves. + + + + + + + + + This request destroys a wl_registry object. + + The client should no longer use the wl_registry after making this + request. + + The compositor will emit a wl_display.delete_id event with the object ID + of the registry and will no longer emit any events on the registry. The + client should re-use the object ID once it receives the + wl_display.delete_id event. + + + + + From 4273a5edc862bd5f620ad0c7f11d20cd040e005c Mon Sep 17 00:00:00 2001 From: Demi Marie Obenour Date: Wed, 24 Jul 2024 21:18:40 -0400 Subject: [PATCH 23/78] connection: Avoid undefined pointer arithmetic Creating a pointer that is more than one element past the end of an array is undefined behavior, even if the pointer is not dereferenced. Avoid this undefined behavior by using `p >= end` instead of `p + 1 > end` and `SOMETHING > end - p` instead of `p + SOMETHING > end`. Signed-off-by: Demi Marie Obenour --- src/connection.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/connection.c b/src/connection.c index 6b28d21d..1177d667 100644 --- a/src/connection.c +++ b/src/connection.c @@ -928,7 +928,7 @@ wl_connection_demarshal(struct wl_connection *connection, for (i = 0; i < count; i++) { signature = get_next_argument(signature, &arg); - if (arg.type != WL_ARG_FD && p + 1 > end) { + if (arg.type != WL_ARG_FD && p >= end) { wl_log("message too short, " "object (%d), message %s(%s)\n", closure->sender_id, message->name, @@ -1351,7 +1351,7 @@ serialize_closure(struct wl_closure *closure, uint32_t *buffer, if (arg.type == WL_ARG_FD) continue; - if (p + 1 > end) + if (p >= end) goto overflow; switch (arg.type) { @@ -1379,7 +1379,7 @@ serialize_closure(struct wl_closure *closure, uint32_t *buffer, size = strlen(closure->args[i].s) + 1; *p++ = size; - if (p + div_roundup(size, sizeof *p) > end) + if (div_roundup(size, sizeof *p) > (uint32_t)(end - p)) goto overflow; memcpy(p, closure->args[i].s, size); @@ -1394,7 +1394,7 @@ serialize_closure(struct wl_closure *closure, uint32_t *buffer, size = closure->args[i].a->size; *p++ = size; - if (p + div_roundup(size, sizeof *p) > end) + if (div_roundup(size, sizeof *p) > (uint32_t)(end - p)) goto overflow; if (size != 0) From 290c36bc507d7d2f391b7eb1dc8fd9841c37e770 Mon Sep 17 00:00:00 2001 From: Demi Marie Obenour Date: Mon, 5 Aug 2024 12:49:49 -0400 Subject: [PATCH 24/78] tests: Avoid calling function with wrong type Calling a function with the wrong type is immediate undefined behavior, even if the ABI says it should be harmless. UBSAN picks it up immediately, and any decent control-flow integrity mechanism will as well. Signed-off-by: Demi Marie Obenour --- tests/display-test.c | 18 +++++++++--------- tests/test-compositor.c | 2 +- tests/test-compositor.h | 14 +++++++++++++- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/tests/display-test.c b/tests/display-test.c index c2def444..89606c73 100644 --- a/tests/display-test.c +++ b/tests/display-test.c @@ -924,7 +924,7 @@ TEST(versions) } static void -check_error_on_destroyed_object(void *data) +check_error_on_destroyed_object(void) { struct client *c; struct wl_seat *seat; @@ -1043,7 +1043,7 @@ TEST(filtered_global_is_hidden) 1, d, bind_data_offer); wl_display_set_global_filter(d->wl_display, global_filter, NULL); - client_create_noarg(d, get_globals); + client_create(d, get_globals, NULL); display_run(d); wl_global_destroy(g); @@ -1052,13 +1052,13 @@ TEST(filtered_global_is_hidden) } static void -get_dynamic_globals(void *data) +get_dynamic_globals(void) { struct client *c = client_connect(); struct wl_registry *registry; registry = wl_display_get_registry(c->wl_display); - wl_registry_add_listener(registry, ®istry_listener_filtered, data); + wl_registry_add_listener(registry, ®istry_listener_filtered, NULL); wl_display_roundtrip(c->wl_display); /* Wait for the server to create a new global */ @@ -1206,7 +1206,7 @@ static const struct wl_registry_listener zombie_fd_registry_listener = { }; static void -zombie_client(void *data) +zombie_client(void) { struct client *c = client_connect(); struct wl_registry *registry; @@ -1376,7 +1376,7 @@ static const struct wl_registry_listener double_zombie_fd_registry_listener = { }; static void -double_zombie_client(void *data) +double_zombie_client(void) { struct client *c = client_connect(); struct wl_registry *registry; @@ -1436,7 +1436,7 @@ static const struct wl_registry_listener bind_interface_mismatch_registry_listen }; static void -registry_bind_interface_mismatch_client(void *data) +registry_bind_interface_mismatch_client(void) { struct client *c = client_connect(); struct wl_registry *registry; @@ -1598,7 +1598,7 @@ static const struct wl_registry_listener global_remove_before_registry_listener }; static void -global_remove_before_client(void *data) +global_remove_before_client(void) { struct client *c = client_connect(); struct wl_registry *registry; @@ -1648,7 +1648,7 @@ static const struct wl_registry_listener global_remove_after_registry_listener = }; static void -global_remove_after_client(void *data) +global_remove_after_client(void) { struct client *c = client_connect(); struct wl_registry *registry; diff --git a/tests/test-compositor.c b/tests/test-compositor.c index 8648fb69..8ec0631b 100644 --- a/tests/test-compositor.c +++ b/tests/test-compositor.c @@ -507,7 +507,7 @@ static const struct wl_registry_listener registry_listener = NULL }; -struct client *client_connect() +struct client *client_connect(void) { struct wl_registry *reg; struct client *c = calloc(1, sizeof *c); diff --git a/tests/test-compositor.h b/tests/test-compositor.h index 3fb390c2..662a81c3 100644 --- a/tests/test-compositor.h +++ b/tests/test-compositor.h @@ -118,5 +118,17 @@ struct client_info *client_create_with_name(struct display *d, void *data, const char *name); #define client_create(d, c, data) client_create_with_name((d), (c), data, (#c)) +static inline void noarg_cb(void *data) +{ + void (*cb)(void) = data; + cb(); +} +static inline struct client_info *client_create_with_name_noarg(struct display *d, + void (*client_main)(void), + const char *name) +{ + return client_create_with_name(d, noarg_cb, client_main, name); +} + #define client_create_noarg(d, c) \ - client_create_with_name((d), (void(*)(void *)) (c), NULL, (#c)) + client_create_with_name_noarg((d), (c), (#c)) From 9cb3d7aa9dc995ffafdbdef7ab86a949d0fb0e7d Mon Sep 17 00:00:00 2001 From: Demi Marie Obenour Date: Mon, 5 Aug 2024 12:59:21 -0400 Subject: [PATCH 25/78] connection: Fix wrong format string Prevents undefined behavior if there is not enough space in the buffer for a queued message. Signed-off-by: Demi Marie Obenour --- src/connection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/connection.c b/src/connection.c index 1177d667..34495211 100644 --- a/src/connection.c +++ b/src/connection.c @@ -260,7 +260,7 @@ ring_buffer_ensure_space(struct wl_ring_buffer *b, size_t count) * allowed). */ if (net_size > size_pot(size_bits)) { - wl_log("Data too big for buffer (%d + %zd > %zd).\n", + wl_log("Data too big for buffer (%zu + %zu > %zu).\n", ring_buffer_size(b), count, size_pot(size_bits)); errno = E2BIG; return -1; From f246e619d17deb92f414315d1747a9b7aca659b9 Mon Sep 17 00:00:00 2001 From: Haihua Hu Date: Mon, 6 Jan 2025 21:55:37 +0900 Subject: [PATCH 26/78] util: reduce error of wl_fixed_from_double() when cast double to fixed pointer, there will be big error, eg 1919.9998 to 1919. Call round before cast to get nearest value 1920 of 1919.9998 Signed-off-by: Haihua Hu --- src/wayland-util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wayland-util.h b/src/wayland-util.h index 929a34f3..4edec8b0 100644 --- a/src/wayland-util.h +++ b/src/wayland-util.h @@ -635,7 +635,7 @@ wl_fixed_to_double(wl_fixed_t f) static inline wl_fixed_t wl_fixed_from_double(double d) { - return (wl_fixed_t) (d * 256.0); + return (wl_fixed_t) (round(d * 256.0)); } /** From 597a6b94f52d6f875410593e09a4bb3adaefbea5 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 21 Jan 2025 16:20:05 +0000 Subject: [PATCH 27/78] ci: Update ci-templates This includes an explicit way to specify the container architecture, which fixes our rebuilds on ARMv7. Signed-off-by: Daniel Stone --- .gitlab-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 079ad8ba..eb6fb0b4 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: b791bd48996e3ced9ca13f1c5ee82be8540b8adb + ref: f210ea84576f756816da37908771edcee14ef7e6 file: - '/templates/debian.yml' - '/templates/freebsd.yml' @@ -82,7 +82,7 @@ workflow: FDO_DISTRIBUTION_EXEC: 'pip3 install --break-system-packages meson~=0.57.2' # bump this tag every time you change something which requires rebuilding the # base image - FDO_DISTRIBUTION_TAG: "2024-03-28.2" + FDO_DISTRIBUTION_TAG: "2025-01-21.1" .debian-x86_64: extends: @@ -101,6 +101,7 @@ workflow: - .os-debian variables: BUILD_ARCH: "armv7" + FDO_DISTRIBUTION_PLATFORM: "linux/arm/v7" # Does not inherit .ci-rules as we only want it to run in MR context. @@ -154,7 +155,6 @@ armv7-debian-container_prep: stage: "Base container" variables: GIT_STRATEGY: none - FDO_BASE_IMAGE: "arm32v7/debian:$FDO_DISTRIBUTION_VERSION" # Core build environment. From bdba21ec92bf582f606806ad5fd15107979d8947 Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Thu, 6 Jul 2023 18:18:37 +0200 Subject: [PATCH 28/78] server: add const qualifier to function arguments where possible Makes it possible to e.g. `call wl_client_get_credentials` with a `const struct wl_client *` from a global filter callback. Signed-off-by: Sebastian Wick --- src/wayland-server-core.h | 19 ++++++++++--------- src/wayland-server.c | 10 +++++----- src/wayland-shm.c | 8 ++++---- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/wayland-server-core.h b/src/wayland-server-core.h index b1baa25f..15c3b762 100644 --- a/src/wayland-server-core.h +++ b/src/wayland-server-core.h @@ -227,7 +227,7 @@ typedef void (*wl_global_bind_func_t)(struct wl_client *client, void *data, uint32_t version, uint32_t id); uint32_t -wl_display_get_serial(struct wl_display *display); +wl_display_get_serial(const struct wl_display *display); uint32_t wl_display_next_serial(struct wl_display *display); @@ -324,7 +324,7 @@ void wl_client_flush(struct wl_client *client); void -wl_client_get_credentials(struct wl_client *client, +wl_client_get_credentials(const struct wl_client *client, pid_t *pid, uid_t *uid, gid_t *gid); int @@ -586,7 +586,7 @@ void wl_resource_destroy(struct wl_resource *resource); uint32_t -wl_resource_get_id(struct wl_resource *resource); +wl_resource_get_id(const struct wl_resource *resource); struct wl_list * wl_resource_get_link(struct wl_resource *resource); @@ -607,7 +607,7 @@ void * wl_resource_get_user_data(struct wl_resource *resource); int -wl_resource_get_version(struct wl_resource *resource); +wl_resource_get_version(const struct wl_resource *resource); void wl_resource_set_destructor(struct wl_resource *resource, @@ -617,8 +617,9 @@ int wl_resource_instance_of(struct wl_resource *resource, const struct wl_interface *interface, const void *implementation); + const char * -wl_resource_get_class(struct wl_resource *resource); +wl_resource_get_class(const struct wl_resource *resource); void wl_resource_add_destroy_listener(struct wl_resource *resource, @@ -654,16 +655,16 @@ void * wl_shm_buffer_get_data(struct wl_shm_buffer *buffer); int32_t -wl_shm_buffer_get_stride(struct wl_shm_buffer *buffer); +wl_shm_buffer_get_stride(const struct wl_shm_buffer *buffer); uint32_t -wl_shm_buffer_get_format(struct wl_shm_buffer *buffer); +wl_shm_buffer_get_format(const struct wl_shm_buffer *buffer); int32_t -wl_shm_buffer_get_width(struct wl_shm_buffer *buffer); +wl_shm_buffer_get_width(const struct wl_shm_buffer *buffer); int32_t -wl_shm_buffer_get_height(struct wl_shm_buffer *buffer); +wl_shm_buffer_get_height(const struct wl_shm_buffer *buffer); struct wl_shm_pool * wl_shm_buffer_ref_pool(struct wl_shm_buffer *buffer); diff --git a/src/wayland-server.c b/src/wayland-server.c index 1d6be3ec..e5805669 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -619,7 +619,7 @@ err_client: * \memberof wl_client */ WL_EXPORT void -wl_client_get_credentials(struct wl_client *client, +wl_client_get_credentials(const struct wl_client *client, pid_t *pid, uid_t *uid, gid_t *gid) { if (pid) @@ -799,7 +799,7 @@ wl_resource_destroy(struct wl_resource *resource) } WL_EXPORT uint32_t -wl_resource_get_id(struct wl_resource *resource) +wl_resource_get_id(const struct wl_resource *resource) { return resource->object.id; } @@ -853,7 +853,7 @@ wl_resource_get_user_data(struct wl_resource *resource) } WL_EXPORT int -wl_resource_get_version(struct wl_resource *resource) +wl_resource_get_version(const struct wl_resource *resource) { return resource->version; } @@ -900,7 +900,7 @@ wl_resource_get_destroy_listener(struct wl_resource *resource, * \memberof wl_resource */ WL_EXPORT const char * -wl_resource_get_class(struct wl_resource *resource) +wl_resource_get_class(const struct wl_resource *resource) { return resource->object.interface->name; } @@ -1496,7 +1496,7 @@ wl_global_set_user_data(struct wl_global *global, void *data) * \memberof wl_display */ WL_EXPORT uint32_t -wl_display_get_serial(struct wl_display *display) +wl_display_get_serial(const struct wl_display *display) { return display->serial; } diff --git a/src/wayland-shm.c b/src/wayland-shm.c index 87917dad..9b3aac61 100644 --- a/src/wayland-shm.c +++ b/src/wayland-shm.c @@ -445,7 +445,7 @@ wl_shm_buffer_get(struct wl_resource *resource) } WL_EXPORT int32_t -wl_shm_buffer_get_stride(struct wl_shm_buffer *buffer) +wl_shm_buffer_get_stride(const struct wl_shm_buffer *buffer) { return buffer->stride; } @@ -479,19 +479,19 @@ wl_shm_buffer_get_data(struct wl_shm_buffer *buffer) } WL_EXPORT uint32_t -wl_shm_buffer_get_format(struct wl_shm_buffer *buffer) +wl_shm_buffer_get_format(const struct wl_shm_buffer *buffer) { return buffer->format; } WL_EXPORT int32_t -wl_shm_buffer_get_width(struct wl_shm_buffer *buffer) +wl_shm_buffer_get_width(const struct wl_shm_buffer *buffer) { return buffer->width; } WL_EXPORT int32_t -wl_shm_buffer_get_height(struct wl_shm_buffer *buffer) +wl_shm_buffer_get_height(const struct wl_shm_buffer *buffer) { return buffer->height; } From 37469d5ceda49ef7551a0fe94f7886f863f76408 Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Wed, 7 Feb 2024 19:00:37 +0100 Subject: [PATCH 29/78] timespec: Pull in timespec.h from weston Signed-off-by: Sebastian Wick --- src/timespec-util.h | 259 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 src/timespec-util.h diff --git a/src/timespec-util.h b/src/timespec-util.h new file mode 100644 index 00000000..f79969bb --- /dev/null +++ b/src/timespec-util.h @@ -0,0 +1,259 @@ +/* + * Copyright © 2014 - 2015 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef TIMESPEC_UTIL_H +#define TIMESPEC_UTIL_H + +#include +#include +#include +#include + +#define NSEC_PER_SEC 1000000000 + +/* Subtract timespecs + * + * \param r[out] result: a - b + * \param a[in] operand + * \param b[in] operand + */ +static inline void +timespec_sub(struct timespec *r, + const struct timespec *a, const struct timespec *b) +{ + r->tv_sec = a->tv_sec - b->tv_sec; + r->tv_nsec = a->tv_nsec - b->tv_nsec; + if (r->tv_nsec < 0) { + r->tv_sec--; + r->tv_nsec += NSEC_PER_SEC; + } +} + +/* Add a nanosecond value to a timespec + * + * \param r[out] result: a + b + * \param a[in] base operand as timespec + * \param b[in] operand in nanoseconds + */ +static inline void +timespec_add_nsec(struct timespec *r, const struct timespec *a, int64_t b) +{ + r->tv_sec = a->tv_sec + (b / NSEC_PER_SEC); + r->tv_nsec = a->tv_nsec + (b % NSEC_PER_SEC); + + if (r->tv_nsec >= NSEC_PER_SEC) { + r->tv_sec++; + r->tv_nsec -= NSEC_PER_SEC; + } else if (r->tv_nsec < 0) { + r->tv_sec--; + r->tv_nsec += NSEC_PER_SEC; + } +} + +/* Add a millisecond value to a timespec + * + * \param r[out] result: a + b + * \param a[in] base operand as timespec + * \param b[in] operand in milliseconds + */ +static inline void +timespec_add_msec(struct timespec *r, const struct timespec *a, int64_t b) +{ + timespec_add_nsec(r, a, b * 1000000); +} + +/* Convert timespec to nanoseconds + * + * \param a timespec + * \return nanoseconds + */ +static inline int64_t +timespec_to_nsec(const struct timespec *a) +{ + return (int64_t)a->tv_sec * NSEC_PER_SEC + a->tv_nsec; +} + +/* Subtract timespecs and return result in nanoseconds + * + * \param a[in] operand + * \param b[in] operand + * \return to_nanoseconds(a - b) + */ +static inline int64_t +timespec_sub_to_nsec(const struct timespec *a, const struct timespec *b) +{ + struct timespec r; + timespec_sub(&r, a, b); + return timespec_to_nsec(&r); +} + +/* Convert timespec to milliseconds + * + * \param a timespec + * \return milliseconds + * + * Rounding to integer milliseconds happens always down (floor()). + */ +static inline int64_t +timespec_to_msec(const struct timespec *a) +{ + return (int64_t)a->tv_sec * 1000 + a->tv_nsec / 1000000; +} + +/* Subtract timespecs and return result in milliseconds + * + * \param a[in] operand + * \param b[in] operand + * \return to_milliseconds(a - b) + */ +static inline int64_t +timespec_sub_to_msec(const struct timespec *a, const struct timespec *b) +{ + return timespec_sub_to_nsec(a, b) / 1000000; +} + +/* Convert timespec to microseconds + * + * \param a timespec + * \return microseconds + * + * Rounding to integer microseconds happens always down (floor()). + */ +static inline int64_t +timespec_to_usec(const struct timespec *a) +{ + return (int64_t)a->tv_sec * 1000000 + a->tv_nsec / 1000; +} + +/* Convert timespec to protocol data + * + * \param a timespec + * \param tv_sec_hi[out] the high bytes of the seconds part + * \param tv_sec_lo[out] the low bytes of the seconds part + * \param tv_nsec[out] the nanoseconds part + * + * The input timespec must be normalized (the nanoseconds part should + * be less than 1 second) and non-negative. + */ +static inline void +timespec_to_proto(const struct timespec *a, uint32_t *tv_sec_hi, + uint32_t *tv_sec_lo, uint32_t *tv_nsec) +{ + assert(a->tv_sec >= 0); + assert(a->tv_nsec >= 0 && a->tv_nsec < NSEC_PER_SEC); + + uint64_t sec64 = a->tv_sec; + + *tv_sec_hi = sec64 >> 32; + *tv_sec_lo = sec64 & 0xffffffff; + *tv_nsec = a->tv_nsec; +} + +/* Convert nanoseconds to timespec + * + * \param a timespec + * \param b nanoseconds + */ +static inline void +timespec_from_nsec(struct timespec *a, int64_t b) +{ + a->tv_sec = b / NSEC_PER_SEC; + a->tv_nsec = b % NSEC_PER_SEC; +} + +/* Convert microseconds to timespec + * + * \param a timespec + * \param b microseconds + */ +static inline void +timespec_from_usec(struct timespec *a, int64_t b) +{ + timespec_from_nsec(a, b * 1000); +} + +/* Convert milliseconds to timespec + * + * \param a timespec + * \param b milliseconds + */ +static inline void +timespec_from_msec(struct timespec *a, int64_t b) +{ + timespec_from_nsec(a, b * 1000000); +} + +/* Convert protocol data to timespec + * + * \param a[out] timespec + * \param tv_sec_hi the high bytes of seconds part + * \param tv_sec_lo the low bytes of seconds part + * \param tv_nsec the nanoseconds part + */ +static inline void +timespec_from_proto(struct timespec *a, uint32_t tv_sec_hi, + uint32_t tv_sec_lo, uint32_t tv_nsec) +{ + a->tv_sec = ((uint64_t)tv_sec_hi << 32) + tv_sec_lo; + a->tv_nsec = tv_nsec; +} + +/* Check if a timespec is zero + * + * \param a timespec + * \return whether the timespec is zero + */ +static inline bool +timespec_is_zero(const struct timespec *a) +{ + return a->tv_sec == 0 && a->tv_nsec == 0; +} + +/* Check if two timespecs are equal + * + * \param a[in] timespec to check + * \param b[in] timespec to check + * \return whether timespecs a and b are equal + */ +static inline bool +timespec_eq(const struct timespec *a, const struct timespec *b) +{ + return a->tv_sec == b->tv_sec && + a->tv_nsec == b->tv_nsec; +} + +/* Convert milli-Hertz to nanoseconds + * + * \param mhz frequency in mHz, not zero + * \return period in nanoseconds + */ +static inline int64_t +millihz_to_nsec(uint32_t mhz) +{ + assert(mhz > 0); + return 1000000000000LL / mhz; +} + +#endif /* TIMESPEC_UTIL_H */ From 9d5de6062ba82a56d25d8a53ccace3afecbf50e5 Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Wed, 7 Feb 2024 18:51:39 +0100 Subject: [PATCH 30/78] timespec: Pull in timespec_after and timespec_add from mesa Signed-off-by: Sebastian Wick --- src/timespec-util.h | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/timespec-util.h b/src/timespec-util.h index f79969bb..d37568d2 100644 --- a/src/timespec-util.h +++ b/src/timespec-util.h @@ -256,4 +256,38 @@ millihz_to_nsec(uint32_t mhz) return 1000000000000LL / mhz; } +/** + * Checks whether a timespec value is after another + * + * \param a[in] timespec to compare + * \param b[in] timespec to compare + * \return whether a is after b + */ +static inline bool +timespec_after(const struct timespec *a, const struct timespec *b) +{ + return (a->tv_sec == b->tv_sec) ? + (a->tv_nsec > b->tv_nsec) : + (a->tv_sec > b->tv_sec); +} + +/** + * Add timespecs + * + * \param r[out] result: a + b + * \param a[in] operand + * \param b[in] operand + */ +static inline void +timespec_add(struct timespec *r, + const struct timespec *a, const struct timespec *b) +{ + r->tv_sec = a->tv_sec + b->tv_sec; + r->tv_nsec = a->tv_nsec + b->tv_nsec; + if (r->tv_nsec > NSEC_PER_SEC) { + r->tv_sec++; + r->tv_nsec -= NSEC_PER_SEC; + } +} + #endif /* TIMESPEC_UTIL_H */ From 893e4fc46d31d1cc51c1e39481f5659a40152233 Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Wed, 7 Feb 2024 18:53:16 +0100 Subject: [PATCH 31/78] timespec: Implement saturating timespec substraction Signed-off-by: Sebastian Wick --- src/timespec-util.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/timespec-util.h b/src/timespec-util.h index d37568d2..d3c53bd7 100644 --- a/src/timespec-util.h +++ b/src/timespec-util.h @@ -290,4 +290,22 @@ timespec_add(struct timespec *r, } } +/** + * Saturating timespec subtraction + * + * \param r[out] result: max(a - b, 0) + * \param a[in] operand + * \param b[in] operand + */ +static inline void +timespec_sub_saturate(struct timespec *r, + const struct timespec *a, const struct timespec *b) +{ + timespec_sub(r, a, b); + if (r->tv_sec < 0) { + r->tv_sec = 0; + r->tv_nsec = 0; + } +} + #endif /* TIMESPEC_UTIL_H */ From ff8b88552358b6977043db0d6ee0cac7d7abfa0c Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Wed, 7 Feb 2024 18:46:58 +0100 Subject: [PATCH 32/78] event-loop: Use timespec utils instead of hand-rolling our own Signed-off-by: Sebastian Wick --- src/event-loop.c | 61 ++++++------------------------------------------ 1 file changed, 7 insertions(+), 54 deletions(-) diff --git a/src/event-loop.c b/src/event-loop.c index 51c9b9d0..89294fd9 100644 --- a/src/event-loop.c +++ b/src/event-loop.c @@ -38,6 +38,7 @@ #include #include #include +#include "timespec-util.h" #include "wayland-util.h" #include "wayland-private.h" #include "wayland-server-core.h" @@ -973,57 +974,6 @@ wl_event_loop_dispatch_idle(struct wl_event_loop *loop) } } -static int -timespec_to_ms(struct timespec value) -{ - return (value.tv_sec * 1000) + (value.tv_nsec / 1000000); -} - -static struct timespec -ms_to_timespec(int ms) -{ - struct timespec val; - val.tv_sec = ms / 1000; - val.tv_nsec = (ms % 1000) * 1000000; - return val; -} - -static struct timespec -timespec_normalize(struct timespec value) -{ - struct timespec result = value; - - while (result.tv_nsec >= 1000000000) { - result.tv_nsec -= 1000000000; - result.tv_sec++; - } - - while (result.tv_nsec < 0) { - result.tv_nsec += 1000000000; - result.tv_sec--; - } - - return result; -} - -static struct timespec -timespec_add(struct timespec a, struct timespec b) -{ - struct timespec result; - result.tv_sec = a.tv_sec + b.tv_sec; - result.tv_nsec = a.tv_nsec + b.tv_nsec; - return timespec_normalize(result); -} - -static struct timespec -timespec_sub(struct timespec a, struct timespec b) -{ - struct timespec result; - result.tv_sec = a.tv_sec - b.tv_sec; - result.tv_nsec = a.tv_nsec - b.tv_nsec; - return timespec_normalize(result); -} - /** Wait for events and dispatch them * * \param loop The event loop whose sources to wait for. @@ -1052,13 +1002,15 @@ wl_event_loop_dispatch(struct wl_event_loop *loop, int timeout) int i, count; bool has_timers = false; bool use_timeout = timeout > 0; - struct timespec now, end; + struct timespec now; + struct timespec deadline = {0}; + struct timespec result; wl_event_loop_dispatch_idle(loop); if (use_timeout) { clock_gettime(CLOCK_MONOTONIC, &now); - end = timespec_add(now, ms_to_timespec(timeout)); + timespec_add_msec(&deadline, &now, timeout); } while (true) { @@ -1070,7 +1022,8 @@ wl_event_loop_dispatch(struct wl_event_loop *loop, int timeout) if (use_timeout) { clock_gettime(CLOCK_MONOTONIC, &now); - timeout = timespec_to_ms(timespec_sub(end, now)); + timespec_sub(&result, &deadline, &now); + timeout = timespec_to_msec(&result); if (timeout <= 0) { /* too late */ count = 0; From ddd348da7ea0889056843cf252729185d306b7b8 Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Wed, 7 Feb 2024 19:00:46 +0100 Subject: [PATCH 33/78] client: Add wl_display_dispatch_queue_timeout For dispatching messages on a queue with a timeout. This slightly changes the samantics of wl_display_dispatch. Previously it was possible for it to return even though there wasn't a single dispatched event. The function correctly returned 0 in this case but it is now used to indicate a timeout. Signed-off-by: Sebastian Wick --- src/wayland-client-core.h | 6 ++ src/wayland-client.c | 155 +++++++++++++++++++++++++++++--------- 2 files changed, 127 insertions(+), 34 deletions(-) diff --git a/src/wayland-client-core.h b/src/wayland-client-core.h index a4ca4e5d..6d45dc03 100644 --- a/src/wayland-client-core.h +++ b/src/wayland-client-core.h @@ -27,6 +27,7 @@ #define WAYLAND_CLIENT_CORE_H #include +#include #include "wayland-util.h" #include "wayland-version.h" @@ -250,6 +251,11 @@ int wl_display_dispatch_queue(struct wl_display *display, struct wl_event_queue *queue); +int +wl_display_dispatch_queue_timeout(struct wl_display *display, + struct wl_event_queue *queue, + const struct timespec *timeout); + int wl_display_dispatch_queue_pending(struct wl_display *display, struct wl_event_queue *queue); diff --git a/src/wayland-client.c b/src/wayland-client.c index 2f6e00c2..a60e5673 100644 --- a/src/wayland-client.c +++ b/src/wayland-client.c @@ -45,6 +45,7 @@ #include "wayland-os.h" #include "wayland-client.h" #include "wayland-private.h" +#include "timespec-util.h" /** \cond */ @@ -1953,20 +1954,133 @@ wl_display_cancel_read(struct wl_display *display) } static int -wl_display_poll(struct wl_display *display, short int events) +wl_display_poll(struct wl_display *display, + short int events, + const struct timespec *timeout) { int ret; struct pollfd pfd[1]; + struct timespec now; + struct timespec deadline = {0}; + struct timespec result; + struct timespec *remaining_timeout = NULL; + + if (timeout) { + clock_gettime(CLOCK_MONOTONIC, &now); + timespec_add(&deadline, &now, timeout); + } pfd[0].fd = display->fd; pfd[0].events = events; do { - ret = poll(pfd, 1, -1); + if (timeout) { + clock_gettime(CLOCK_MONOTONIC, &now); + timespec_sub_saturate(&result, &deadline, &now); + remaining_timeout = &result; + } + ret = ppoll(pfd, 1, remaining_timeout, NULL); } while (ret == -1 && errno == EINTR); return ret; } +/** Dispatch events in an event queue with a timeout + * + * \param display The display context object + * \param queue The event queue to dispatch + * \param timeout A timeout describing how long the call should block trying to + * dispatch events + * \return The number of dispatched events on success, -1 on failure + * + * This function behaves identical to wl_display_dispatch_queue() except + * that it also takes a timeout and returns 0 if the timeout elapsed. + * + * Passing NULL as a timeout means an infinite timeout. An empty timespec + * causes wl_display_dispatch_queue_timeout() to return immediately even if no + * events have been dispatched. + * + * If a timeout is passed to wl_display_dispatch_queue_timeout() it is updated + * to the remaining time. + * + * \sa wl_display_dispatch_queue() + * + * \memberof wl_display + */ +WL_EXPORT int +wl_display_dispatch_queue_timeout(struct wl_display *display, + struct wl_event_queue *queue, + const struct timespec *timeout) +{ + int ret; + struct timespec now; + struct timespec deadline = {0}; + struct timespec result; + struct timespec *remaining_timeout = NULL; + + if (timeout) { + clock_gettime(CLOCK_MONOTONIC, &now); + timespec_add(&deadline, &now, timeout); + } + + if (wl_display_prepare_read_queue(display, queue) == -1) + return wl_display_dispatch_queue_pending(display, queue); + + while (true) { + ret = wl_display_flush(display); + + if (ret != -1 || errno != EAGAIN) + break; + + if (timeout) { + clock_gettime(CLOCK_MONOTONIC, &now); + timespec_sub_saturate(&result, &deadline, &now); + remaining_timeout = &result; + } + ret = wl_display_poll(display, POLLOUT, remaining_timeout); + + if (ret <= 0) { + wl_display_cancel_read(display); + return ret; + } + } + + /* Don't stop if flushing hits an EPIPE; continue so we can read any + * protocol error that may have triggered it. */ + if (ret < 0 && errno != EPIPE) { + wl_display_cancel_read(display); + return -1; + } + + while (true) { + if (timeout) { + clock_gettime(CLOCK_MONOTONIC, &now); + timespec_sub_saturate(&result, &deadline, &now); + remaining_timeout = &result; + } + + ret = wl_display_poll(display, POLLIN, remaining_timeout); + if (ret <= 0) { + wl_display_cancel_read(display); + break; + } + + ret = wl_display_read_events(display); + if (ret == -1) + break; + + ret = wl_display_dispatch_queue_pending(display, queue); + if (ret != 0) + break; + + /* We managed to read data from the display but there is no + * complete event to dispatch yet. Try reading again. */ + if (wl_display_prepare_read_queue(display, queue) == -1) + return wl_display_dispatch_queue_pending(display, queue); + } + + return ret; +} + /** Dispatch events in an event queue * * \param display The display context object @@ -1998,8 +2112,8 @@ wl_display_poll(struct wl_display *display, short int events) * \note Since Wayland 1.5 the display has an extra queue * for its own events (i. e. delete_id). This queue is dispatched always, * no matter what queue we passed as an argument to this function. - * That means that this function can return non-0 value even when it - * haven't dispatched any event for the given queue. + * That means that this function can return even when it has not dispatched any + * event for the given queue. * * \sa wl_display_dispatch(), wl_display_dispatch_pending(), * wl_display_dispatch_queue_pending(), wl_display_prepare_read_queue() @@ -2012,37 +2126,10 @@ wl_display_dispatch_queue(struct wl_display *display, { int ret; - if (wl_display_prepare_read_queue(display, queue) == -1) - return wl_display_dispatch_queue_pending(display, queue); + ret = wl_display_dispatch_queue_timeout(display, queue, NULL); + assert(ret == -1 || ret > 0); - while (true) { - ret = wl_display_flush(display); - - if (ret != -1 || errno != EAGAIN) - break; - - if (wl_display_poll(display, POLLOUT) == -1) { - wl_display_cancel_read(display); - return -1; - } - } - - /* Don't stop if flushing hits an EPIPE; continue so we can read any - * protocol error that may have triggered it. */ - if (ret < 0 && errno != EPIPE) { - wl_display_cancel_read(display); - return -1; - } - - if (wl_display_poll(display, POLLIN) == -1) { - wl_display_cancel_read(display); - return -1; - } - - if (wl_display_read_events(display) == -1) - return -1; - - return wl_display_dispatch_queue_pending(display, queue); + return ret; } /** Dispatch pending events in an event queue From 00dcf6b32386a63055fc04817256dc71efad1800 Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Wed, 7 Feb 2024 18:59:14 +0100 Subject: [PATCH 34/78] client: Add wl_display_dispatch_timeout A variant of wl_display_dispatch_queue_timeout for the default queue. Signed-off-by: Sebastian Wick --- src/wayland-client-core.h | 4 ++++ src/wayland-client.c | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/src/wayland-client-core.h b/src/wayland-client-core.h index 6d45dc03..157da431 100644 --- a/src/wayland-client-core.h +++ b/src/wayland-client-core.h @@ -251,6 +251,10 @@ int wl_display_dispatch_queue(struct wl_display *display, struct wl_event_queue *queue); +int +wl_display_dispatch_timeout(struct wl_display *display, + const struct timespec *timeout); + int wl_display_dispatch_queue_timeout(struct wl_display *display, struct wl_event_queue *queue, diff --git a/src/wayland-client.c b/src/wayland-client.c index a60e5673..8df160b4 100644 --- a/src/wayland-client.c +++ b/src/wayland-client.c @@ -2081,6 +2081,15 @@ wl_display_dispatch_queue_timeout(struct wl_display *display, return ret; } +WL_EXPORT int +wl_display_dispatch_timeout(struct wl_display *display, + const struct timespec *timeout) +{ + return wl_display_dispatch_queue_timeout(display, + &display->default_queue, + timeout); +} + /** Dispatch events in an event queue * * \param display The display context object From 74f322c35a4eef1332456ce9985255df7269f82f Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Tue, 28 Jan 2025 21:24:46 +0100 Subject: [PATCH 35/78] tests: Add dispatch timeout tests Add tests which verify that... * wl_display_dispatch_timeout with a big enough timeout behaves the same as wl_display_dispatch * wl_display_dispatch_timeout will time out when there are no messages to dispatch Signed-off-by: Sebastian Wick --- tests/queue-test.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/tests/queue-test.c b/tests/queue-test.c index cb61a85b..7dfdd306 100644 --- a/tests/queue-test.c +++ b/tests/queue-test.c @@ -40,6 +40,7 @@ #include "wayland-server.h" #include "test-runner.h" #include "test-compositor.h" +#include "../src/timespec-util.h" #define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) @@ -571,6 +572,68 @@ client_test_queue_names(void) wl_display_disconnect(display); } +static void +dispatch_timeout_sync_callback(void *data, struct wl_callback *callback, + uint32_t serial) +{ + bool *done = data; + + *done = true; + wl_callback_destroy(callback); +} + +static const struct wl_callback_listener dispatch_timeout_sync_listener = { + dispatch_timeout_sync_callback +}; + +static void +client_test_queue_dispatch_simple(void) +{ + struct wl_display *display; + struct timespec timeout; + struct wl_callback *callback; + bool done = false; + int ret = 0; + + display = wl_display_connect(NULL); + assert(display); + + callback = wl_display_sync(display); + assert(callback != NULL); + wl_callback_add_listener(callback, &dispatch_timeout_sync_listener, &done); + + timespec_from_msec(&timeout, 1000); + + while (!done) { + ret = wl_display_dispatch_timeout(display, &timeout); + assert(ret > 0); + } + + wl_display_disconnect(display); + + exit(0); +} + +static void +client_test_queue_dispatch_timeout(void) +{ + struct wl_display *display; + struct timespec timeout; + int ret = 0; + + display = wl_display_connect(NULL); + assert(display); + + timespec_from_msec(&timeout, 100); + + ret = wl_display_dispatch_timeout(display, &timeout); + assert(ret == 0); + + wl_display_disconnect(display); + + exit(0); +} + static void dummy_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) @@ -709,3 +772,27 @@ TEST(queue_names) display_destroy(d); } + +TEST(queue_dispatch_simple) +{ + struct display *d = display_create(); + + test_set_timeout(2); + + client_create_noarg(d, client_test_queue_dispatch_simple); + display_run(d); + + display_destroy(d); +} + +TEST(queue_dispatch_timeout) +{ + struct display *d = display_create(); + + test_set_timeout(2); + + client_create_noarg(d, client_test_queue_dispatch_timeout); + display_run(d); + + display_destroy(d); +} From 02ad102e2d2d0dbb9529d00ab52ca4ff276d2ae3 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 29 Jan 2025 13:27:44 +0000 Subject: [PATCH 36/78] build: Add -lm to pkg-config dependencies Now that wl_fixed_from_double() calls round() from a function declared in a header, our users need to explicitly pick that dependency up in order to avoid build errors. Signed-off-by: Daniel Stone Closes: wayland/weston#991 --- src/meson.build | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/meson.build b/src/meson.build index 5d04334e..984e34ab 100644 --- a/src/meson.build +++ b/src/meson.build @@ -212,6 +212,7 @@ if get_option('libraries') description: 'Server side implementation of the Wayland protocol', version: meson.project_version(), filebase: 'wayland-server', + libraries: mathlib_dep, variables: [ 'datarootdir=' + join_paths('${prefix}', get_option('datadir')), 'pkgdatadir=' + join_paths('${pc_sysrootdir}${datarootdir}', meson.project_name()) @@ -251,6 +252,7 @@ if get_option('libraries') description: 'Wayland client side library', version: meson.project_version(), filebase: 'wayland-client', + libraries: mathlib_dep, variables: [ 'datarootdir=' + join_paths('${prefix}', get_option('datadir')), 'pkgdatadir=' + join_paths('${pc_sysrootdir}${datarootdir}', meson.project_name()) From 7c2ffb0d712ca1aafbfa94c71f80226490bcbd28 Mon Sep 17 00:00:00 2001 From: David Redondo Date: Wed, 5 Feb 2025 09:21:43 +0100 Subject: [PATCH 37/78] Make wayland-util.h -Wundef safe when compiled by a C++ compiler Fixes #522 Signed-off-by: David Redondo --- src/wayland-util.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wayland-util.h b/src/wayland-util.h index 4edec8b0..c929a1ab 100644 --- a/src/wayland-util.h +++ b/src/wayland-util.h @@ -48,7 +48,7 @@ extern "C" { #endif /** Deprecated attribute */ -#if __STDC_VERSION__ >= 202311L +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L #define WL_DEPRECATED [[deprecated]] #elif defined(__GNUC__) && __GNUC__ >= 4 #define WL_DEPRECATED __attribute__ ((deprecated)) @@ -70,7 +70,7 @@ extern "C" { #define WL_PRINTF(x, y) #endif -#if __STDC_VERSION__ >= 202311L +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L #define WL_TYPEOF(expr) typeof(expr) #else #define WL_TYPEOF(expr) __typeof__(expr) From afd498b6f561b82b88bf1b647209c9ef0dca9e94 Mon Sep 17 00:00:00 2001 From: David Redondo Date: Wed, 5 Feb 2025 09:27:22 +0100 Subject: [PATCH 38/78] Also use [[deprecated]] when compiling with at least C++14 Signed-off-by: David Redondo --- src/wayland-util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wayland-util.h b/src/wayland-util.h index c929a1ab..4540f040 100644 --- a/src/wayland-util.h +++ b/src/wayland-util.h @@ -48,7 +48,7 @@ extern "C" { #endif /** Deprecated attribute */ -#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L) || (defined(__cplusplus) && __cplusplus >= 201402L) #define WL_DEPRECATED [[deprecated]] #elif defined(__GNUC__) && __GNUC__ >= 4 #define WL_DEPRECATED __attribute__ ((deprecated)) From 1ab6b693b16e1d9734496fe60c8a6ed277e4dec3 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Wed, 5 Feb 2025 22:41:33 +0200 Subject: [PATCH 39/78] Forward declarate timespec struct The `timespec` struct is defined in `time.h` header but only if `_POSIX_C_SOURCE` is set or when using the C11 standard. Signed-off-by: Vlad Zahorodnii --- src/wayland-client-core.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/wayland-client-core.h b/src/wayland-client-core.h index 157da431..a70d4f09 100644 --- a/src/wayland-client-core.h +++ b/src/wayland-client-core.h @@ -27,7 +27,6 @@ #define WAYLAND_CLIENT_CORE_H #include -#include #include "wayland-util.h" #include "wayland-version.h" @@ -35,6 +34,8 @@ extern "C" { #endif +struct timespec; + /** \class wl_proxy * * \brief Represents a protocol object on the client side. From dbfa8d784eed0949f3dd50c19a58873b5deb32c6 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 23 Feb 2025 23:38:15 +0100 Subject: [PATCH 40/78] scanner: use separate guards for validator functions Generated XXX_is_valid() functions for enums are guarded behind the same #define as the enum itself. This worked fine until recently, but since fbd7460737c9 ("scanner: add new enum-header mode") we're also generating enum-only headers. When including the enum-only header first, and then the server header, the validator functions are missing. Define a separate guard to fix this. Signed-off-by: Simon Ser --- src/scanner.c | 14 +++- tests/data/example-server.h | 144 +++++++++++++++++++++++++++------ tests/data/small-server-core.h | 12 ++- tests/data/small-server.h | 12 ++- 4 files changed, 151 insertions(+), 31 deletions(-) diff --git a/src/scanner.c b/src/scanner.c index 3cd05d13..1b71e60c 100644 --- a/src/scanner.c +++ b/src/scanner.c @@ -1383,6 +1383,11 @@ emit_validator(struct interface *interface, struct enumeration *e) { struct entry *entry; + printf("#ifndef %s_%s_ENUM_IS_VALID\n", + interface->uppercase_name, e->uppercase_name); + printf("#define %s_%s_ENUM_IS_VALID\n", + interface->uppercase_name, e->uppercase_name); + printf("/**\n" " * @ingroup iface_%s\n" " * Validate a %s %s value.\n" @@ -1420,6 +1425,9 @@ emit_validator(struct interface *interface, struct enumeration *e) " }\n"); } printf("}\n"); + + printf("#endif /* %s_%s_ENUM_IS_VALID */\n\n", + interface->uppercase_name, e->uppercase_name); } static void @@ -1483,11 +1491,11 @@ emit_enumerations(struct interface *interface, bool with_validators) } - if (with_validators) - emit_validator(interface, e); - printf("#endif /* %s_%s_ENUM */\n\n", interface->uppercase_name, e->uppercase_name); + + if (with_validators) + emit_validator(interface, e); } } diff --git a/tests/data/example-server.h b/tests/data/example-server.h index 2fad7097..0ddc73db 100644 --- a/tests/data/example-server.h +++ b/tests/data/example-server.h @@ -901,6 +901,10 @@ enum wl_display_error { */ WL_DISPLAY_ERROR_NO_MEMORY = 2, }; +#endif /* WL_DISPLAY_ERROR_ENUM */ + +#ifndef WL_DISPLAY_ERROR_ENUM_IS_VALID +#define WL_DISPLAY_ERROR_ENUM_IS_VALID /** * @ingroup iface_wl_display * Validate a wl_display error value. @@ -921,7 +925,7 @@ wl_display_error_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_DISPLAY_ERROR_ENUM */ +#endif /* WL_DISPLAY_ERROR_ENUM_IS_VALID */ /** * @ingroup iface_wl_display @@ -1194,6 +1198,10 @@ enum wl_shm_error { */ WL_SHM_ERROR_INVALID_FD = 2, }; +#endif /* WL_SHM_ERROR_ENUM */ + +#ifndef WL_SHM_ERROR_ENUM_IS_VALID +#define WL_SHM_ERROR_ENUM_IS_VALID /** * @ingroup iface_wl_shm * Validate a wl_shm error value. @@ -1214,7 +1222,7 @@ wl_shm_error_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_SHM_ERROR_ENUM */ +#endif /* WL_SHM_ERROR_ENUM_IS_VALID */ #ifndef WL_SHM_FORMAT_ENUM #define WL_SHM_FORMAT_ENUM @@ -1466,6 +1474,10 @@ enum wl_shm_format { */ WL_SHM_FORMAT_YVU444 = 0x34325659, }; +#endif /* WL_SHM_FORMAT_ENUM */ + +#ifndef WL_SHM_FORMAT_ENUM_IS_VALID +#define WL_SHM_FORMAT_ENUM_IS_VALID /** * @ingroup iface_wl_shm * Validate a wl_shm format value. @@ -1596,7 +1608,7 @@ wl_shm_format_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_SHM_FORMAT_ENUM */ +#endif /* WL_SHM_FORMAT_ENUM_IS_VALID */ /** * @ingroup iface_wl_shm @@ -1706,6 +1718,10 @@ enum wl_data_offer_error { */ WL_DATA_OFFER_ERROR_INVALID_OFFER = 3, }; +#endif /* WL_DATA_OFFER_ERROR_ENUM */ + +#ifndef WL_DATA_OFFER_ERROR_ENUM_IS_VALID +#define WL_DATA_OFFER_ERROR_ENUM_IS_VALID /** * @ingroup iface_wl_data_offer * Validate a wl_data_offer error value. @@ -1728,7 +1744,7 @@ wl_data_offer_error_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_DATA_OFFER_ERROR_ENUM */ +#endif /* WL_DATA_OFFER_ERROR_ENUM_IS_VALID */ /** * @ingroup iface_wl_data_offer @@ -1940,6 +1956,10 @@ enum wl_data_source_error { */ WL_DATA_SOURCE_ERROR_INVALID_SOURCE = 1, }; +#endif /* WL_DATA_SOURCE_ERROR_ENUM */ + +#ifndef WL_DATA_SOURCE_ERROR_ENUM_IS_VALID +#define WL_DATA_SOURCE_ERROR_ENUM_IS_VALID /** * @ingroup iface_wl_data_source * Validate a wl_data_source error value. @@ -1958,7 +1978,7 @@ wl_data_source_error_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_DATA_SOURCE_ERROR_ENUM */ +#endif /* WL_DATA_SOURCE_ERROR_ENUM_IS_VALID */ /** * @ingroup iface_wl_data_source @@ -2130,6 +2150,10 @@ enum wl_data_device_error { */ WL_DATA_DEVICE_ERROR_ROLE = 0, }; +#endif /* WL_DATA_DEVICE_ERROR_ENUM */ + +#ifndef WL_DATA_DEVICE_ERROR_ENUM_IS_VALID +#define WL_DATA_DEVICE_ERROR_ENUM_IS_VALID /** * @ingroup iface_wl_data_device * Validate a wl_data_device error value. @@ -2146,7 +2170,7 @@ wl_data_device_error_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_DATA_DEVICE_ERROR_ENUM */ +#endif /* WL_DATA_DEVICE_ERROR_ENUM_IS_VALID */ /** * @ingroup iface_wl_data_device @@ -2387,6 +2411,10 @@ enum wl_data_device_manager_dnd_action { */ WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK = 4, }; +#endif /* WL_DATA_DEVICE_MANAGER_DND_ACTION_ENUM */ + +#ifndef WL_DATA_DEVICE_MANAGER_DND_ACTION_ENUM_IS_VALID +#define WL_DATA_DEVICE_MANAGER_DND_ACTION_ENUM_IS_VALID /** * @ingroup iface_wl_data_device_manager * Validate a wl_data_device_manager dnd_action value. @@ -2407,7 +2435,7 @@ wl_data_device_manager_dnd_action_is_valid(uint32_t value, uint32_t version) { valid |= WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK; return (value & ~valid) == 0; } -#endif /* WL_DATA_DEVICE_MANAGER_DND_ACTION_ENUM */ +#endif /* WL_DATA_DEVICE_MANAGER_DND_ACTION_ENUM_IS_VALID */ /** * @ingroup iface_wl_data_device_manager @@ -2454,6 +2482,10 @@ enum wl_shell_error { */ WL_SHELL_ERROR_ROLE = 0, }; +#endif /* WL_SHELL_ERROR_ENUM */ + +#ifndef WL_SHELL_ERROR_ENUM_IS_VALID +#define WL_SHELL_ERROR_ENUM_IS_VALID /** * @ingroup iface_wl_shell * Validate a wl_shell error value. @@ -2470,7 +2502,7 @@ wl_shell_error_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_SHELL_ERROR_ENUM */ +#endif /* WL_SHELL_ERROR_ENUM_IS_VALID */ /** * @ingroup iface_wl_shell @@ -2549,6 +2581,10 @@ enum wl_shell_surface_resize { */ WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT = 10, }; +#endif /* WL_SHELL_SURFACE_RESIZE_ENUM */ + +#ifndef WL_SHELL_SURFACE_RESIZE_ENUM_IS_VALID +#define WL_SHELL_SURFACE_RESIZE_ENUM_IS_VALID /** * @ingroup iface_wl_shell_surface * Validate a wl_shell_surface resize value. @@ -2579,7 +2615,7 @@ wl_shell_surface_resize_is_valid(uint32_t value, uint32_t version) { valid |= WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT; return (value & ~valid) == 0; } -#endif /* WL_SHELL_SURFACE_RESIZE_ENUM */ +#endif /* WL_SHELL_SURFACE_RESIZE_ENUM_IS_VALID */ #ifndef WL_SHELL_SURFACE_TRANSIENT_ENUM #define WL_SHELL_SURFACE_TRANSIENT_ENUM @@ -2596,6 +2632,10 @@ enum wl_shell_surface_transient { */ WL_SHELL_SURFACE_TRANSIENT_INACTIVE = 0x1, }; +#endif /* WL_SHELL_SURFACE_TRANSIENT_ENUM */ + +#ifndef WL_SHELL_SURFACE_TRANSIENT_ENUM_IS_VALID +#define WL_SHELL_SURFACE_TRANSIENT_ENUM_IS_VALID /** * @ingroup iface_wl_shell_surface * Validate a wl_shell_surface transient value. @@ -2610,7 +2650,7 @@ wl_shell_surface_transient_is_valid(uint32_t value, uint32_t version) { valid |= WL_SHELL_SURFACE_TRANSIENT_INACTIVE; return (value & ~valid) == 0; } -#endif /* WL_SHELL_SURFACE_TRANSIENT_ENUM */ +#endif /* WL_SHELL_SURFACE_TRANSIENT_ENUM_IS_VALID */ #ifndef WL_SHELL_SURFACE_FULLSCREEN_METHOD_ENUM #define WL_SHELL_SURFACE_FULLSCREEN_METHOD_ENUM @@ -2640,6 +2680,10 @@ enum wl_shell_surface_fullscreen_method { */ WL_SHELL_SURFACE_FULLSCREEN_METHOD_FILL = 3, }; +#endif /* WL_SHELL_SURFACE_FULLSCREEN_METHOD_ENUM */ + +#ifndef WL_SHELL_SURFACE_FULLSCREEN_METHOD_ENUM_IS_VALID +#define WL_SHELL_SURFACE_FULLSCREEN_METHOD_ENUM_IS_VALID /** * @ingroup iface_wl_shell_surface * Validate a wl_shell_surface fullscreen_method value. @@ -2662,7 +2706,7 @@ wl_shell_surface_fullscreen_method_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_SHELL_SURFACE_FULLSCREEN_METHOD_ENUM */ +#endif /* WL_SHELL_SURFACE_FULLSCREEN_METHOD_ENUM_IS_VALID */ /** * @ingroup iface_wl_shell_surface @@ -2994,6 +3038,10 @@ enum wl_surface_error { */ WL_SURFACE_ERROR_INVALID_TRANSFORM = 1, }; +#endif /* WL_SURFACE_ERROR_ENUM */ + +#ifndef WL_SURFACE_ERROR_ENUM_IS_VALID +#define WL_SURFACE_ERROR_ENUM_IS_VALID /** * @ingroup iface_wl_surface * Validate a wl_surface error value. @@ -3012,7 +3060,7 @@ wl_surface_error_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_SURFACE_ERROR_ENUM */ +#endif /* WL_SURFACE_ERROR_ENUM_IS_VALID */ /** * @ingroup iface_wl_surface @@ -3471,6 +3519,10 @@ enum wl_seat_capability { */ WL_SEAT_CAPABILITY_TOUCH = 4, }; +#endif /* WL_SEAT_CAPABILITY_ENUM */ + +#ifndef WL_SEAT_CAPABILITY_ENUM_IS_VALID +#define WL_SEAT_CAPABILITY_ENUM_IS_VALID /** * @ingroup iface_wl_seat * Validate a wl_seat capability value. @@ -3489,7 +3541,7 @@ wl_seat_capability_is_valid(uint32_t value, uint32_t version) { valid |= WL_SEAT_CAPABILITY_TOUCH; return (value & ~valid) == 0; } -#endif /* WL_SEAT_CAPABILITY_ENUM */ +#endif /* WL_SEAT_CAPABILITY_ENUM_IS_VALID */ /** * @ingroup iface_wl_seat @@ -3613,6 +3665,10 @@ enum wl_pointer_error { */ WL_POINTER_ERROR_ROLE = 0, }; +#endif /* WL_POINTER_ERROR_ENUM */ + +#ifndef WL_POINTER_ERROR_ENUM_IS_VALID +#define WL_POINTER_ERROR_ENUM_IS_VALID /** * @ingroup iface_wl_pointer * Validate a wl_pointer error value. @@ -3629,7 +3685,7 @@ wl_pointer_error_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_POINTER_ERROR_ENUM */ +#endif /* WL_POINTER_ERROR_ENUM_IS_VALID */ #ifndef WL_POINTER_BUTTON_STATE_ENUM #define WL_POINTER_BUTTON_STATE_ENUM @@ -3650,6 +3706,10 @@ enum wl_pointer_button_state { */ WL_POINTER_BUTTON_STATE_PRESSED = 1, }; +#endif /* WL_POINTER_BUTTON_STATE_ENUM */ + +#ifndef WL_POINTER_BUTTON_STATE_ENUM_IS_VALID +#define WL_POINTER_BUTTON_STATE_ENUM_IS_VALID /** * @ingroup iface_wl_pointer * Validate a wl_pointer button_state value. @@ -3668,7 +3728,7 @@ wl_pointer_button_state_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_POINTER_BUTTON_STATE_ENUM */ +#endif /* WL_POINTER_BUTTON_STATE_ENUM_IS_VALID */ #ifndef WL_POINTER_AXIS_ENUM #define WL_POINTER_AXIS_ENUM @@ -3688,6 +3748,10 @@ enum wl_pointer_axis { */ WL_POINTER_AXIS_HORIZONTAL_SCROLL = 1, }; +#endif /* WL_POINTER_AXIS_ENUM */ + +#ifndef WL_POINTER_AXIS_ENUM_IS_VALID +#define WL_POINTER_AXIS_ENUM_IS_VALID /** * @ingroup iface_wl_pointer * Validate a wl_pointer axis value. @@ -3706,7 +3770,7 @@ wl_pointer_axis_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_POINTER_AXIS_ENUM */ +#endif /* WL_POINTER_AXIS_ENUM_IS_VALID */ #ifndef WL_POINTER_AXIS_SOURCE_ENUM #define WL_POINTER_AXIS_SOURCE_ENUM @@ -3754,6 +3818,10 @@ enum wl_pointer_axis_source { * @ingroup iface_wl_pointer */ #define WL_POINTER_AXIS_SOURCE_WHEEL_TILT_SINCE_VERSION 6 +#endif /* WL_POINTER_AXIS_SOURCE_ENUM */ + +#ifndef WL_POINTER_AXIS_SOURCE_ENUM_IS_VALID +#define WL_POINTER_AXIS_SOURCE_ENUM_IS_VALID /** * @ingroup iface_wl_pointer * Validate a wl_pointer axis_source value. @@ -3776,7 +3844,7 @@ wl_pointer_axis_source_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_POINTER_AXIS_SOURCE_ENUM */ +#endif /* WL_POINTER_AXIS_SOURCE_ENUM_IS_VALID */ /** * @ingroup iface_wl_pointer @@ -4037,6 +4105,10 @@ enum wl_keyboard_keymap_format { */ WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1 = 1, }; +#endif /* WL_KEYBOARD_KEYMAP_FORMAT_ENUM */ + +#ifndef WL_KEYBOARD_KEYMAP_FORMAT_ENUM_IS_VALID +#define WL_KEYBOARD_KEYMAP_FORMAT_ENUM_IS_VALID /** * @ingroup iface_wl_keyboard * Validate a wl_keyboard keymap_format value. @@ -4055,7 +4127,7 @@ wl_keyboard_keymap_format_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_KEYBOARD_KEYMAP_FORMAT_ENUM */ +#endif /* WL_KEYBOARD_KEYMAP_FORMAT_ENUM_IS_VALID */ #ifndef WL_KEYBOARD_KEY_STATE_ENUM #define WL_KEYBOARD_KEY_STATE_ENUM @@ -4075,6 +4147,10 @@ enum wl_keyboard_key_state { */ WL_KEYBOARD_KEY_STATE_PRESSED = 1, }; +#endif /* WL_KEYBOARD_KEY_STATE_ENUM */ + +#ifndef WL_KEYBOARD_KEY_STATE_ENUM_IS_VALID +#define WL_KEYBOARD_KEY_STATE_ENUM_IS_VALID /** * @ingroup iface_wl_keyboard * Validate a wl_keyboard key_state value. @@ -4093,7 +4169,7 @@ wl_keyboard_key_state_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_KEYBOARD_KEY_STATE_ENUM */ +#endif /* WL_KEYBOARD_KEY_STATE_ENUM_IS_VALID */ /** * @ingroup iface_wl_keyboard @@ -4419,6 +4495,10 @@ enum wl_output_subpixel { */ WL_OUTPUT_SUBPIXEL_VERTICAL_BGR = 5, }; +#endif /* WL_OUTPUT_SUBPIXEL_ENUM */ + +#ifndef WL_OUTPUT_SUBPIXEL_ENUM_IS_VALID +#define WL_OUTPUT_SUBPIXEL_ENUM_IS_VALID /** * @ingroup iface_wl_output * Validate a wl_output subpixel value. @@ -4445,7 +4525,7 @@ wl_output_subpixel_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_OUTPUT_SUBPIXEL_ENUM */ +#endif /* WL_OUTPUT_SUBPIXEL_ENUM_IS_VALID */ #ifndef WL_OUTPUT_TRANSFORM_ENUM #define WL_OUTPUT_TRANSFORM_ENUM @@ -4499,6 +4579,10 @@ enum wl_output_transform { */ WL_OUTPUT_TRANSFORM_FLIPPED_270 = 7, }; +#endif /* WL_OUTPUT_TRANSFORM_ENUM */ + +#ifndef WL_OUTPUT_TRANSFORM_ENUM_IS_VALID +#define WL_OUTPUT_TRANSFORM_ENUM_IS_VALID /** * @ingroup iface_wl_output * Validate a wl_output transform value. @@ -4529,7 +4613,7 @@ wl_output_transform_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_OUTPUT_TRANSFORM_ENUM */ +#endif /* WL_OUTPUT_TRANSFORM_ENUM_IS_VALID */ #ifndef WL_OUTPUT_MODE_ENUM #define WL_OUTPUT_MODE_ENUM @@ -4550,6 +4634,10 @@ enum wl_output_mode { */ WL_OUTPUT_MODE_PREFERRED = 0x2, }; +#endif /* WL_OUTPUT_MODE_ENUM */ + +#ifndef WL_OUTPUT_MODE_ENUM_IS_VALID +#define WL_OUTPUT_MODE_ENUM_IS_VALID /** * @ingroup iface_wl_output * Validate a wl_output mode value. @@ -4566,7 +4654,7 @@ wl_output_mode_is_valid(uint32_t value, uint32_t version) { valid |= WL_OUTPUT_MODE_PREFERRED; return (value & ~valid) == 0; } -#endif /* WL_OUTPUT_MODE_ENUM */ +#endif /* WL_OUTPUT_MODE_ENUM_IS_VALID */ /** * @ingroup iface_wl_output @@ -4734,6 +4822,10 @@ enum wl_subcompositor_error { */ WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE = 0, }; +#endif /* WL_SUBCOMPOSITOR_ERROR_ENUM */ + +#ifndef WL_SUBCOMPOSITOR_ERROR_ENUM_IS_VALID +#define WL_SUBCOMPOSITOR_ERROR_ENUM_IS_VALID /** * @ingroup iface_wl_subcompositor * Validate a wl_subcompositor error value. @@ -4750,7 +4842,7 @@ wl_subcompositor_error_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_SUBCOMPOSITOR_ERROR_ENUM */ +#endif /* WL_SUBCOMPOSITOR_ERROR_ENUM_IS_VALID */ /** * @ingroup iface_wl_subcompositor @@ -4805,6 +4897,10 @@ enum wl_subsurface_error { */ WL_SUBSURFACE_ERROR_BAD_SURFACE = 0, }; +#endif /* WL_SUBSURFACE_ERROR_ENUM */ + +#ifndef WL_SUBSURFACE_ERROR_ENUM_IS_VALID +#define WL_SUBSURFACE_ERROR_ENUM_IS_VALID /** * @ingroup iface_wl_subsurface * Validate a wl_subsurface error value. @@ -4821,7 +4917,7 @@ wl_subsurface_error_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* WL_SUBSURFACE_ERROR_ENUM */ +#endif /* WL_SUBSURFACE_ERROR_ENUM_IS_VALID */ /** * @ingroup iface_wl_subsurface diff --git a/tests/data/small-server-core.h b/tests/data/small-server-core.h index d4476959..4248d455 100644 --- a/tests/data/small-server-core.h +++ b/tests/data/small-server-core.h @@ -107,6 +107,10 @@ enum intf_A_foo { * @ingroup iface_intf_A */ #define INTF_A_FOO_DEPRECATED_SINCE_VERSION 2 +#endif /* INTF_A_FOO_ENUM */ + +#ifndef INTF_A_FOO_ENUM_IS_VALID +#define INTF_A_FOO_ENUM_IS_VALID /** * @ingroup iface_intf_A * Validate a intf_A foo value. @@ -131,7 +135,7 @@ intf_A_foo_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* INTF_A_FOO_ENUM */ +#endif /* INTF_A_FOO_ENUM_IS_VALID */ #ifndef INTF_A_BAR_ENUM #define INTF_A_BAR_ENUM @@ -154,6 +158,10 @@ enum intf_A_bar { * @ingroup iface_intf_A */ #define INTF_A_BAR_THIRD_SINCE_VERSION 2 +#endif /* INTF_A_BAR_ENUM */ + +#ifndef INTF_A_BAR_ENUM_IS_VALID +#define INTF_A_BAR_ENUM_IS_VALID /** * @ingroup iface_intf_A * Validate a intf_A bar value. @@ -172,7 +180,7 @@ intf_A_bar_is_valid(uint32_t value, uint32_t version) { valid |= INTF_A_BAR_THIRD; return (value & ~valid) == 0; } -#endif /* INTF_A_BAR_ENUM */ +#endif /* INTF_A_BAR_ENUM_IS_VALID */ /** * @ingroup iface_intf_A diff --git a/tests/data/small-server.h b/tests/data/small-server.h index 9cafcd93..743fc8d7 100644 --- a/tests/data/small-server.h +++ b/tests/data/small-server.h @@ -107,6 +107,10 @@ enum intf_A_foo { * @ingroup iface_intf_A */ #define INTF_A_FOO_DEPRECATED_SINCE_VERSION 2 +#endif /* INTF_A_FOO_ENUM */ + +#ifndef INTF_A_FOO_ENUM_IS_VALID +#define INTF_A_FOO_ENUM_IS_VALID /** * @ingroup iface_intf_A * Validate a intf_A foo value. @@ -131,7 +135,7 @@ intf_A_foo_is_valid(uint32_t value, uint32_t version) { return false; } } -#endif /* INTF_A_FOO_ENUM */ +#endif /* INTF_A_FOO_ENUM_IS_VALID */ #ifndef INTF_A_BAR_ENUM #define INTF_A_BAR_ENUM @@ -154,6 +158,10 @@ enum intf_A_bar { * @ingroup iface_intf_A */ #define INTF_A_BAR_THIRD_SINCE_VERSION 2 +#endif /* INTF_A_BAR_ENUM */ + +#ifndef INTF_A_BAR_ENUM_IS_VALID +#define INTF_A_BAR_ENUM_IS_VALID /** * @ingroup iface_intf_A * Validate a intf_A bar value. @@ -172,7 +180,7 @@ intf_A_bar_is_valid(uint32_t value, uint32_t version) { valid |= INTF_A_BAR_THIRD; return (value & ~valid) == 0; } -#endif /* INTF_A_BAR_ENUM */ +#endif /* INTF_A_BAR_ENUM_IS_VALID */ /** * @ingroup iface_intf_A From 7033e74857a01cd8fc07cd2e6336d8bfe43cbed5 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Wed, 12 Mar 2025 21:13:40 +0100 Subject: [PATCH 41/78] client: document get_listener behavior for dispatchers This seems to have been the case since 2013. This is useful for wrappers that need two pointers to identify proxies. One pointer (stored in the user data) pointing to a singleton object to identify that the proxy has a known structure. And one pointer (stored in the dispatcher data) pointing to per-proxy data. Signed-off-by: Julian Orth --- src/wayland-client.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/wayland-client.c b/src/wayland-client.c index 8df160b4..3d0ef7b4 100644 --- a/src/wayland-client.c +++ b/src/wayland-client.c @@ -674,6 +674,9 @@ wl_proxy_add_listener(struct wl_proxy *proxy, * This function is useful in clients with multiple listeners on the same * interface to allow the identification of which code to execute. * + * If \ref wl_proxy_add_dispatcher was used, this function returns the + * dispatcher_data pointer instead. + * * \memberof wl_proxy */ WL_EXPORT const void * From 6137c8c21383d7ebb92cf3331be56ca0aecccd5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Tue, 21 Jan 2025 15:27:22 +0100 Subject: [PATCH 42/78] protocol: Clarify wl_buffer.release description MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sebastian pointed out that the existing text could be read as wl_buffer.destroy not being allowed before the wl_buffer.release event arrives, contrary to what the wl_surface.attach description says. Clarify to be consistent with the latter. This is a follow-up for https://gitlab.freedesktop.org/wayland/wayland/-/merge_requests/141 . Signed-off-by: Michel Dänzer v2: * Simplify clarification, don't talk about callbacks. (Julian Orth) * Add reference to details in the description of wl_surface.attach. (Daniel Stone) v3: * Tweak clarification again. (Sebastian Wick) v4: * Make clarification even less ambiguous. (Simon Ser, Julian Orth) v5: * Just refer to the description of wl_surface.attach instead of trying to clarify anything here. (Sebastian Wick) --- protocol/wayland.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/protocol/wayland.xml b/protocol/wayland.xml index f32918e9..1af51d36 100644 --- a/protocol/wayland.xml +++ b/protocol/wayland.xml @@ -501,8 +501,10 @@ Sent when this wl_buffer is no longer used by the compositor. - The client is now free to reuse or destroy this buffer and its - backing storage. + + For more information on when release events may or may not be sent, + and what consequences it has, please see the description of + wl_surface.attach. If a client receives a release event before the frame callback requested in the same wl_surface.commit that attaches this From 9ec01ab2dc08c1c60ec4fcf7df8defa652c52ae8 Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Tue, 20 May 2025 21:30:56 +0200 Subject: [PATCH 43/78] shm: Linkify wl_shm_pool_unref in the ref_pool documentation Signed-off-by: Sebastian Wick --- src/wayland-shm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wayland-shm.c b/src/wayland-shm.c index 9b3aac61..d22353c6 100644 --- a/src/wayland-shm.c +++ b/src/wayland-shm.c @@ -503,7 +503,7 @@ wl_shm_buffer_get_height(const struct wl_shm_buffer *buffer) * Returns a pointer to a buffer's shm_pool and increases the * shm_pool refcount. * - * The compositor must remember to call wl_shm_pool_unref when + * The compositor must remember to call wl_shm_pool_unref() when * it no longer needs the reference to ensure proper destruction * of the pool. * From af453f876e44f4cb990acc92b50d130e6efed667 Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Tue, 20 May 2025 21:39:45 +0200 Subject: [PATCH 44/78] shm: Remove refcount check which cannot be triggered If the pool refcount reaches zero, it is freed, so accessing its members is UB which ASan would catch. Also simplify check for negative refcounts. Signed-off-by: Sebastian Wick --- src/wayland-shm.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/wayland-shm.c b/src/wayland-shm.c index d22353c6..27db8c65 100644 --- a/src/wayland-shm.c +++ b/src/wayland-shm.c @@ -143,17 +143,16 @@ shm_pool_unref(struct wl_shm_pool *pool, bool external) { if (external) { pool->external_refcount--; - if (!(pool->external_refcount >= 0)) + if (pool->external_refcount < 0) wl_abort("Requested to unref an external reference to " "pool but none found\n"); if (pool->external_refcount == 0) shm_pool_finish_resize(pool); } else { pool->internal_refcount--; - if (!(pool->internal_refcount >= 0)) + if (pool->internal_refcount < 0) wl_abort("Requested to unref an internal reference to " "pool but none found\n"); - } if (pool->internal_refcount + pool->external_refcount > 0) @@ -513,10 +512,6 @@ wl_shm_buffer_get_height(const struct wl_shm_buffer *buffer) WL_EXPORT struct wl_shm_pool * wl_shm_buffer_ref_pool(struct wl_shm_buffer *buffer) { - if (!(buffer->pool->internal_refcount + - buffer->pool->external_refcount)) - wl_abort("Can't get reference to pool that has been freed\n"); - buffer->pool->external_refcount++; return buffer->pool; } From 9367c4da76c9fd118d505e40ad5d554e8fe249a1 Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Fri, 21 Mar 2025 19:42:52 +0100 Subject: [PATCH 45/78] shm: Add wl_shm_buffer ref and unref functions Shared memory buffers are currently tied to the lifetime of their underlying wl_buffer resource. This becomes problematic when the client destroys the resource after committing new state which references the wl_buffer because a compositor might have to defer applying the commit. This commit adds methods to keep the wl_shm_buffer alive longer than the underlying resource. This implicitly also keeps the buffer pool alive and because the wl_shm_buffer uses offsets into the pool, it even works when the underlying storage gets remapped somewhere else, which can happen when the client resizes the pool. Signed-off-by: Sebastian Wick --- src/wayland-server-core.h | 6 +++ src/wayland-shm.c | 78 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 79 insertions(+), 5 deletions(-) diff --git a/src/wayland-server-core.h b/src/wayland-server-core.h index 15c3b762..005a3249 100644 --- a/src/wayland-server-core.h +++ b/src/wayland-server-core.h @@ -666,6 +666,12 @@ wl_shm_buffer_get_width(const struct wl_shm_buffer *buffer); int32_t wl_shm_buffer_get_height(const struct wl_shm_buffer *buffer); +struct wl_shm_buffer * +wl_shm_buffer_ref(struct wl_shm_buffer *buffer); + +void +wl_shm_buffer_unref(struct wl_shm_buffer *buffer); + struct wl_shm_pool * wl_shm_buffer_ref_pool(struct wl_shm_buffer *buffer); diff --git a/src/wayland-shm.c b/src/wayland-shm.c index 27db8c65..bb622b60 100644 --- a/src/wayland-shm.c +++ b/src/wayland-shm.c @@ -84,6 +84,8 @@ struct wl_shm_pool { */ struct wl_shm_buffer { struct wl_resource *resource; + int internal_refcount; + int external_refcount; int32_t width, height; int32_t stride; uint32_t format; @@ -165,13 +167,36 @@ shm_pool_unref(struct wl_shm_pool *pool, bool external) free(pool); } +static void +shm_buffer_unref(struct wl_shm_buffer *buffer, bool external) +{ + if (external) { + buffer->external_refcount--; + if (buffer->external_refcount < 0) { + wl_abort("Requested to unref an external reference to " + "buffer but none found\n"); + } + } else { + buffer->internal_refcount--; + if (buffer->internal_refcount < 0) { + wl_abort("Requested to unref an internal reference to " + "buffer but none found\n"); + } + } + + if (buffer->internal_refcount + buffer->external_refcount > 0) + return; + + shm_pool_unref(buffer->pool, false); + free(buffer); +} + static void destroy_buffer(struct wl_resource *resource) { struct wl_shm_buffer *buffer = wl_resource_get_user_data(resource); - shm_pool_unref(buffer->pool, false); - free(buffer); + shm_buffer_unref(buffer, false); } static void @@ -237,6 +262,8 @@ shm_pool_create_buffer(struct wl_client *client, struct wl_resource *resource, return; } + buffer->internal_refcount = 1; + buffer->external_refcount = 0; buffer->width = width; buffer->height = height; buffer->format = format; @@ -495,6 +522,45 @@ wl_shm_buffer_get_height(const struct wl_shm_buffer *buffer) return buffer->height; } +/** Reference a shm_buffer + * + * \param buffer The buffer object + * + * Returns a pointer to the buffer and increases the refcount. + * + * The compositor must remember to call wl_shm_buffer_unref() when + * it no longer needs the reference to ensure proper destruction + * of the buffer. + * + * \memberof wl_shm_buffer + * \sa wl_shm_buffer_unref + */ +WL_EXPORT struct wl_shm_buffer * +wl_shm_buffer_ref(struct wl_shm_buffer *buffer) +{ + buffer->external_refcount++; + return buffer; +} + +/** Unreference a shm_buffer + * + * \param buffer The buffer object + * + * Drops a reference to a buffer object. + * + * This is only necessary if the compositor has explicitly + * taken a reference with wl_shm_buffer_ref(), otherwise + * the buffer will be automatically destroyed when appropriate. + * + * \memberof wl_shm_buffer + * \sa wl_shm_buffer_ref + */ +WL_EXPORT void +wl_shm_buffer_unref(struct wl_shm_buffer *buffer) +{ + shm_buffer_unref(buffer, true); +} + /** Get a reference to a shm_buffer's shm_pool * * \param buffer The buffer object @@ -693,9 +759,11 @@ wl_shm_buffer_end_access(struct wl_shm_buffer *buffer) if (--sigbus_data->access_count == 0) { if (sigbus_data->fallback_mapping_used) { - wl_resource_post_error(buffer->resource, - WL_SHM_ERROR_INVALID_FD, - "error accessing SHM buffer"); + if (buffer->resource) { + wl_resource_post_error(buffer->resource, + WL_SHM_ERROR_INVALID_FD, + "error accessing SHM buffer"); + } sigbus_data->fallback_mapping_used = 0; } From d2a3d33063ea0266eee55a54c8bb1d65a7acaca9 Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Fri, 21 Mar 2025 19:43:23 +0100 Subject: [PATCH 46/78] shm: Generate an error when shm access failed even without a resource Signed-off-by: Sebastian Wick --- src/wayland-shm.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/wayland-shm.c b/src/wayland-shm.c index bb622b60..b8c73731 100644 --- a/src/wayland-shm.c +++ b/src/wayland-shm.c @@ -86,6 +86,8 @@ struct wl_shm_buffer { struct wl_resource *resource; int internal_refcount; int external_refcount; + struct wl_client *client; + struct wl_listener client_destroy_listener; int32_t width, height; int32_t stride; uint32_t format; @@ -187,6 +189,8 @@ shm_buffer_unref(struct wl_shm_buffer *buffer, bool external) if (buffer->internal_refcount + buffer->external_refcount > 0) return; + if (buffer->client) + wl_list_remove(&buffer->client_destroy_listener.link); shm_pool_unref(buffer->pool, false); free(buffer); } @@ -230,6 +234,17 @@ format_is_supported(struct wl_client *client, uint32_t format) return false; } + +static void +shm_buffer_client_destroy_notify(struct wl_listener *listener, void *data) +{ + struct wl_shm_buffer *buffer = + wl_container_of(listener, buffer, client_destroy_listener); + + buffer->client = NULL; + wl_list_remove(&buffer->client_destroy_listener.link); +} + static void shm_pool_create_buffer(struct wl_client *client, struct wl_resource *resource, uint32_t id, int32_t offset, @@ -262,6 +277,12 @@ shm_pool_create_buffer(struct wl_client *client, struct wl_resource *resource, return; } + buffer->client = client; + buffer->client_destroy_listener.notify = + shm_buffer_client_destroy_notify; + wl_client_add_destroy_listener(buffer->client, + &buffer->client_destroy_listener); + buffer->internal_refcount = 1; buffer->external_refcount = 0; buffer->width = width; @@ -763,6 +784,11 @@ wl_shm_buffer_end_access(struct wl_shm_buffer *buffer) wl_resource_post_error(buffer->resource, WL_SHM_ERROR_INVALID_FD, "error accessing SHM buffer"); + } else if (buffer->client) { + wl_client_post_implementation_error(buffer->client, + "Error accessing SHM buffer of a " + "wl_buffer resource which has " + "already been destroyed"); } sigbus_data->fallback_mapping_used = 0; } From 9dd1b2d7e3eb89b5161431c99a6aca8f284621ae Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 15 Mar 2025 23:49:43 +0100 Subject: [PATCH 47/78] shm: fix comment about wl_shm_buffer_begin_access() safety The paragraph later says that accessing different buffers is allowed. The function checks whether the same pool is accessed. Signed-off-by: Simon Ser --- src/wayland-shm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wayland-shm.c b/src/wayland-shm.c index b8c73731..b87a5b8b 100644 --- a/src/wayland-shm.c +++ b/src/wayland-shm.c @@ -717,7 +717,7 @@ init_sigbus_data_key(void) * terminate. * * It is safe to nest calls to these functions as long as the nested - * calls are all accessing the same buffer. The number of calls to + * calls are all accessing the same pool. The number of calls to * wl_shm_buffer_end_access must match the number of calls to * wl_shm_buffer_begin_access. These functions are thread-safe and it * is allowed to simultaneously access different buffers or the same From 66fc3f007dca7b3e526e86475d3ec74d30d363b8 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 15 Mar 2025 23:52:50 +0100 Subject: [PATCH 48/78] shm: linkify function references in docs Parentheses make it so the generated HTML documentation contains links, which makes navigation easier. Signed-off-by: Simon Ser --- src/wayland-shm.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/wayland-shm.c b/src/wayland-shm.c index b87a5b8b..3ac4add2 100644 --- a/src/wayland-shm.c +++ b/src/wayland-shm.c @@ -509,8 +509,8 @@ wl_shm_buffer_get_stride(const struct wl_shm_buffer *buffer) * SIGBUS signals. This can happen if the client claims that the * buffer is larger than it is or if something truncates the * underlying file. To prevent this signal from causing the compositor - * to crash you should call wl_shm_buffer_begin_access and - * wl_shm_buffer_end_access around code that reads from the memory. + * to crash you should call wl_shm_buffer_begin_access() and + * wl_shm_buffer_end_access() around code that reads from the memory. * * \memberof wl_shm_buffer */ @@ -700,7 +700,7 @@ init_sigbus_data_key(void) * In order to make the compositor robust against clients that change * the size of the underlying file or lie about its size, you should * protect access to the buffer by calling this function before - * reading from the memory and call wl_shm_buffer_end_access + * reading from the memory and call wl_shm_buffer_end_access() * afterwards. This will install a signal handler for SIGBUS which * will prevent the compositor from crashing. * @@ -711,15 +711,15 @@ init_sigbus_data_key(void) * * If a SIGBUS signal is received for an address within the range of * the SHM pool of the given buffer then the client will be sent an - * error event when wl_shm_buffer_end_access is called. If the signal + * error event when wl_shm_buffer_end_access() is called. If the signal * is for an address outside that range then the signal handler will * reraise the signal which would will likely cause the compositor to * terminate. * * It is safe to nest calls to these functions as long as the nested * calls are all accessing the same pool. The number of calls to - * wl_shm_buffer_end_access must match the number of calls to - * wl_shm_buffer_begin_access. These functions are thread-safe and it + * wl_shm_buffer_end_access() must match the number of calls to + * wl_shm_buffer_begin_access(). These functions are thread-safe and it * is allowed to simultaneously access different buffers or the same * buffer from multiple threads. * @@ -753,11 +753,11 @@ wl_shm_buffer_begin_access(struct wl_shm_buffer *buffer) sigbus_data->access_count++; } -/** Ends the access to a buffer started by wl_shm_buffer_begin_access +/** Ends the access to a buffer started by wl_shm_buffer_begin_access() * * \param buffer The SHM buffer * - * This should be called after wl_shm_buffer_begin_access once the + * This should be called after wl_shm_buffer_begin_access() once the * buffer is no longer being accessed. If a SIGBUS signal was * generated in-between these two calls then the resource for the * given buffer will be sent an error. From 3214f858e2bc226c16c280d832a836eae8a671c4 Mon Sep 17 00:00:00 2001 From: David Edmundson Date: Thu, 26 Sep 2024 12:15:37 +0100 Subject: [PATCH 49/78] protocol: Clarify sending of wl_seat.capabilities It wasn't explicitly stated that wl_seat.capabilities should also be sent on bind. Everyone did because it was obviously sensible. This also clarifies that static seat name should be sent before announcing capabilities so clients can associate these devices with the right seat name. Signed-off-by: David Edmundson --- protocol/wayland.xml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/protocol/wayland.xml b/protocol/wayland.xml index 1af51d36..a87d07fe 100644 --- a/protocol/wayland.xml +++ b/protocol/wayland.xml @@ -1913,9 +1913,10 @@ - This is emitted whenever a seat gains or loses the pointer, - keyboard or touch capabilities. The argument is a capability - enum containing the complete set of capabilities this seat has. + This is sent on binding to the seat global or whenever a seat gains + or loses the pointer, keyboard or touch capabilities. + The argument is a capability enum containing the complete set of + capabilities this seat has. When the pointer capability is added, a client may create a wl_pointer object using the wl_seat.get_pointer request. This object @@ -1997,9 +1998,9 @@ The same seat names are used for all clients. Thus, the name can be shared across processes to refer to a specific wl_seat global. - The name event is sent after binding to the seat global. This event is - only sent once per seat object, and the name does not change over the - lifetime of the wl_seat global. + The name event is sent after binding to the seat global, and should be sent + before announcing capabilities. This event only sent once per seat object, + and the name does not change over the lifetime of the wl_seat global. Compositors may re-use the same seat name if the wl_seat global is destroyed and re-created later. From 44972321025e6b34cd5544c2053559c64f1b29af Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Tue, 6 May 2025 23:45:17 +0200 Subject: [PATCH 50/78] client: add wl_proxy_get_interface() This is useful for the wayland bindings/scanner I'm working on for a dynamically typed language. Signed-off-by: Isaac Freund --- src/wayland-client-core.h | 3 +++ src/wayland-client.c | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/wayland-client-core.h b/src/wayland-client-core.h index a70d4f09..970e6254 100644 --- a/src/wayland-client-core.h +++ b/src/wayland-client-core.h @@ -221,6 +221,9 @@ wl_proxy_get_tag(struct wl_proxy *proxy); const char * wl_proxy_get_class(struct wl_proxy *proxy); +const struct wl_interface * +wl_proxy_get_interface(struct wl_proxy *proxy); + struct wl_display * wl_proxy_get_display(struct wl_proxy *proxy); diff --git a/src/wayland-client.c b/src/wayland-client.c index 3d0ef7b4..25aa1cd4 100644 --- a/src/wayland-client.c +++ b/src/wayland-client.c @@ -2513,6 +2513,20 @@ wl_proxy_get_class(struct wl_proxy *proxy) return proxy->object.interface->name; } +/** Get the interface of a proxy object + * + * \param proxy The proxy object + * \return The interface of the object associated with the proxy + * + * \memberof wl_proxy + * \since 1.24 + */ +WL_EXPORT const struct wl_interface * +wl_proxy_get_interface(struct wl_proxy *proxy) +{ + return proxy->object.interface; +} + /** Get the display of a proxy object * * \param proxy The proxy object From 8cad6f7b826546e4faa2d366aed898b5651f73b4 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Tue, 6 May 2025 23:48:57 +0200 Subject: [PATCH 51/78] server: add wl_resource_get_interface() This is useful for the wayland bindings/scanner I'm working on for a dynamically typed language. Signed-off-by: Isaac Freund --- src/wayland-server-core.h | 3 +++ src/wayland-server.c | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/wayland-server-core.h b/src/wayland-server-core.h index 005a3249..c2dcc218 100644 --- a/src/wayland-server-core.h +++ b/src/wayland-server-core.h @@ -621,6 +621,9 @@ wl_resource_instance_of(struct wl_resource *resource, const char * wl_resource_get_class(const struct wl_resource *resource); +const struct wl_interface * +wl_resource_get_interface(struct wl_resource *resource); + void wl_resource_add_destroy_listener(struct wl_resource *resource, struct wl_listener *listener); diff --git a/src/wayland-server.c b/src/wayland-server.c index e5805669..fb99eb23 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -905,6 +905,20 @@ wl_resource_get_class(const struct wl_resource *resource) return resource->object.interface->name; } +/** Get the interface of a resource object + * + * \param resource The resource object + * \return The interface of the object associated with the resource + * + * \memberof wl_resource + * \since 1.24 + */ +WL_EXPORT const struct wl_interface * +wl_resource_get_interface(struct wl_resource *resource) +{ + return resource->object.interface; +} + /** * Add a listener to be called at the beginning of wl_client destruction * From cc06c3825f38ab069ff787311c9ec9f172579347 Mon Sep 17 00:00:00 2001 From: Tobias Stoeckmann Date: Thu, 12 Sep 2024 15:26:26 +0200 Subject: [PATCH 52/78] Fix typos Typos found with codespell and during code audit. Signed-off-by: Tobias Stoeckmann --- cursor/xcursor.c | 6 +++--- doc/publican/sources/Introduction.xml | 2 +- src/wayland-server.c | 2 +- tests/connection-test.c | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cursor/xcursor.c b/cursor/xcursor.c index 0d5761f2..2b6c47d5 100644 --- a/cursor/xcursor.c +++ b/cursor/xcursor.c @@ -798,14 +798,14 @@ xcursor_load_theme_protected(const char *theme, int size, free(xcursor_path); } -/** Load all the cursor of a theme +/** Load all the cursors of a theme * * This function loads all the cursor images of a given theme and its - * inherited themes. Each cursor is loaded into an struct xcursor_images object + * inherited themes. Each cursor is loaded into a struct xcursor_images object * which is passed to the caller's load callback. If a cursor appears * more than once across all the inherited themes, the load callback * will be called multiple times, with possibly different struct xcursor_images - * object which have the same name. The user is expected to destroy the + * objects which have the same name. The user is expected to destroy the * struct xcursor_images objects passed to the callback with * xcursor_images_destroy(). * diff --git a/doc/publican/sources/Introduction.xml b/doc/publican/sources/Introduction.xml index 276db2da..f2a82744 100644 --- a/doc/publican/sources/Introduction.xml +++ b/doc/publican/sources/Introduction.xml @@ -87,7 +87,7 @@ Overall, the philosophy of Wayland is to provide clients with a way to - manage windows and how their contents is displayed. Rendering is left + manage windows and how their contents are displayed. Rendering is left to clients, and system wide memory management interfaces are used to pass buffer handles between clients and the compositing manager. diff --git a/src/wayland-server.c b/src/wayland-server.c index fb99eb23..a538519e 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -2074,7 +2074,7 @@ wl_log_set_handler_server(wl_log_func_t handler) * \param func The function to call to log a new protocol message * \param user_data The user data pointer to pass to \a func * - * \return The protol logger object on success, NULL on failure. + * \return The protocol logger object on success, NULL on failure. * * \sa wl_protocol_logger_destroy * diff --git a/tests/connection-test.c b/tests/connection-test.c index dde5d89c..aed97a0a 100644 --- a/tests/connection-test.c +++ b/tests/connection-test.c @@ -686,7 +686,7 @@ TEST(connection_marshal_big_enough) TEST(connection_marshal_unbounded_boundary_size) { - /* A string of lenth 8178 requires a buffer size of exactly 2^13. */ + /* A string of length 8178 requires a buffer size of exactly 2^13. */ struct marshal_data data; char *big_string = malloc(8178); assert(big_string); From 9b169ff945a8fdddc3a92b1990bddc29a7d24465 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 16 Mar 2025 00:13:17 +0100 Subject: [PATCH 53/78] protocol: drop reference to linux-explicit-synchronization This protocol has been superseded. Replace this outdated reference with a generic hint that protocol extensions may provide this functionality. Signed-off-by: Simon Ser --- protocol/wayland.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/protocol/wayland.xml b/protocol/wayland.xml index a87d07fe..bee74a10 100644 --- a/protocol/wayland.xml +++ b/protocol/wayland.xml @@ -1506,7 +1506,8 @@ the delivery of wl_buffer.release events becomes undefined. A well behaved client should not rely on wl_buffer.release events in this case. Alternatively, a client could create multiple wl_buffer objects - from the same backing storage or use wp_linux_buffer_release. + from the same backing storage or use a protocol extension providing + per-commit release notifications. Destroying the wl_buffer after wl_buffer.release does not change the surface contents. Destroying the wl_buffer before wl_buffer.release From 62cd0990e843095dc91811e2668131783df8f055 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 22 May 2025 21:00:30 +0200 Subject: [PATCH 54/78] build: bump version to 1.23.90 for the RC1 release Signed-off-by: Simon Ser --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 6b05bb52..a3126b63 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'wayland', 'c', - version: '1.23.90', + version: '1.23.91', license: 'MIT', meson_version: '>= 0.57.0', default_options: [ From 827d0c30adc4519fafa7a9c725ff355b1d4fa3bd Mon Sep 17 00:00:00 2001 From: Caitlyn Stewart Date: Tue, 27 May 2025 13:09:24 +0100 Subject: [PATCH 55/78] connection: fix segfault in wl_closure_invoke() Signed-off-by: Caitlyn Stewart --- src/connection.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/connection.c b/src/connection.c index 34495211..3ef8688d 100644 --- a/src/connection.c +++ b/src/connection.c @@ -1229,6 +1229,11 @@ wl_closure_invoke(struct wl_closure *closure, uint32_t flags, count + 2, &ffi_type_void, ffi_types); implementation = target->implementation; + if (!implementation) { + wl_abort("Implementation of resource %d of %s is NULL\n", + target->id, target->interface->name); + } + if (!implementation[opcode]) { wl_abort("listener function for opcode %u of %s is NULL\n", opcode, target->interface->name); From ecff0ee10c335f8f71251e67d615f4f719b6cbe1 Mon Sep 17 00:00:00 2001 From: Caitlyn Date: Tue, 27 May 2025 22:40:14 +0100 Subject: [PATCH 56/78] 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 57/78] 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 58/78] 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 59/78] 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 60/78] 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 61/78] 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 62/78] 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 63/78] 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 64/78] 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 65/78] 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 66/78] 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 67/78] 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 68/78] 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 69/78] 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 70/78] 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 71/78] 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 72/78] 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 73/78] 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 74/78] 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 75/78] 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 76/78] 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 77/78] 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 7fc400ffff4d930d56fe5cf5eff2e4759eacf86b Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Tue, 28 May 2024 21:04:35 +0200 Subject: [PATCH 78/78] protocol: Define Content Update behavior The protocol currently is in a state where we define that commits create content updates and they are queued up until they are applied, and the old view that commit applies the state or caches it in the parent state. This commit moves the protocol completely to the new model which retains the old behavior when no constraints are being used but allows for constraints to be used to hold back a group of synchronized content updates. To convince yourself that this indeed retains the original behavior I suggest to play around with a few examples and look at the resulting graphs, as is done here: https://gitlab.freedesktop.org/wayland/wayland/-/issues/457#note_2403135 Signed-off-by: Sebastian Wick --- protocol/wayland.xml | 117 ++++++++++++++++++++++--------------------- 1 file changed, 61 insertions(+), 56 deletions(-) diff --git a/protocol/wayland.xml b/protocol/wayland.xml index bee74a10..c6c22019 100644 --- a/protocol/wayland.xml +++ b/protocol/wayland.xml @@ -1667,21 +1667,48 @@ etc.) is double-buffered. Protocol requests modify the pending state, as opposed to the active state in use by the compositor. - A commit request atomically creates a content update from the pending - state, even if the pending state has not been touched. The content - update is placed in a queue until it becomes active. After commit, the - new pending state is as documented for each related request. - - When the content update is applied, the wl_buffer is applied before all - other state. This means that all coordinates in double-buffered state - are relative to the newly attached wl_buffers, except for - wl_surface.attach itself. If there is no newly attached wl_buffer, the - coordinates are relative to the previous content update. - All requests that need a commit to become effective are documented to affect double-buffered state. Other interfaces may add further double-buffered surface state. + + A commit request atomically creates a Content Update (CU) from the + pending state, even if the pending state has not been touched. The + content update is placed at the end of a per-surface queue until it + becomes active. After commit, the new pending state is as documented for + each related request. + + A CU is either a Desync Content Update (DCU) or a Sync Content Update + (SCU). If the surface is effectively synchronized at the commit request, + it is a SCU, otherwise a DCU. + + When a surface transitions from effectively synchronized to effectively + desynchronized, all SCUs in its queue which are not reachable by any + DCU become DCUs and dependency edges from outside the queue to these CUs + are removed. + + See wl_subsurface for the definition of 'effectively synchronized' and + 'effectively desynchronized'. + + When a CU is placed in the queue, the CU has a dependency on the CU in + front of it and to the SCU at end of the queue of every direct child + surface if that SCU exists and does not have another dependent. This can + form a directed acyclic graph of CUs with dependencies as edges. + + In addition to surface state, the CU can have constraints that must be + satisfied before it can be applied. Other interfaces may add CU + constraints. + + All DCUs which do not have a SCU in front of themselves in their queue, + are candidates. If the graph that's reachable by a candidate does not + have any unsatisfied constraints, the entire graph must be applied + atomically. + + When a CU is applied, the wl_buffer is applied before all other state. + This means that all coordinates in double-buffered state are relative to + the newly attached wl_buffers, except for wl_surface.attach itself. If + there is no newly attached wl_buffer, the coordinates are relative to + the previous content update.
@@ -3120,23 +3147,9 @@ hidden, or if a NULL wl_buffer is applied. These rules apply recursively through the tree of surfaces. - The behaviour of a wl_surface.commit request on a sub-surface - depends on the sub-surface's mode. The possible modes are - synchronized and desynchronized, see methods - wl_subsurface.set_sync and wl_subsurface.set_desync. Synchronized - mode caches the wl_surface state to be applied when the parent's - state gets applied, and desynchronized mode applies the pending - wl_surface state directly. A sub-surface is initially in the - synchronized mode. - - Sub-surfaces also have another kind of state, which is managed by - wl_subsurface requests, as opposed to wl_surface requests. This - state includes the sub-surface position relative to the parent - surface (wl_subsurface.set_position), and the stacking order of - the parent and its sub-surfaces (wl_subsurface.place_above and - .place_below). This state is applied when the parent surface's - wl_surface state is applied, regardless of the sub-surface's mode. - As the exception, set_sync and set_desync are effective immediately. + A sub-surface can be in one of two modes. The possible modes are + synchronized and desynchronized, see methods wl_subsurface.set_sync and + wl_subsurface.set_desync. The main surface can be thought to be always in desynchronized mode, since it does not have a parent in the sub-surfaces sense. @@ -3148,6 +3161,22 @@ synchronized mode, and then assume that all its child and grand-child sub-surfaces are synchronized, too, without explicitly setting them. + If a surface behaves as in synchronized mode, it is effectively + synchronized, otherwise it is effectively desynchronized. + + A sub-surface is initially in the synchronized mode. + + Sub-surfaces also have another kind of state, which is managed by + wl_subsurface requests, as opposed to wl_surface requests. This + state includes the sub-surface position relative to the parent + surface (wl_subsurface.set_position), and the stacking order of + the parent and its sub-surfaces (wl_subsurface.place_above and + .place_below). + + This state is double-buffered on the parent's surface regardless of the + sub-surface's mode. As the exception, set_sync and set_desync are + effective immediately. + Destroying a sub-surface takes effect immediately. If you need to synchronize the removal of a sub-surface to the parent surface update, unmap the sub-surface first by attaching a NULL wl_buffer, update parent, @@ -3229,42 +3258,18 @@ Change the commit behaviour of the sub-surface to synchronized - mode, also described as the parent dependent mode. + mode. - In synchronized mode, wl_surface.commit on a sub-surface will - accumulate the committed state in a cache, but the state will - not be applied and hence will not change the compositor output. - The cached state is applied to the sub-surface immediately after - the parent surface's state is applied. This ensures atomic - updates of the parent and all its synchronized sub-surfaces. - Applying the cached state will invalidate the cache, so further - parent surface commits do not (re-)apply old state. - - See wl_subsurface for the recursive effect of this mode. + See wl_subsurface and wl_surface.commit for more information. Change the commit behaviour of the sub-surface to desynchronized - mode, also described as independent or freely running mode. + mode. - In desynchronized mode, wl_surface.commit on a sub-surface will - apply the pending state directly, without caching, as happens - normally with a wl_surface. Calling wl_surface.commit on the - parent surface has no effect on the sub-surface's wl_surface - state. This mode allows a sub-surface to be updated on its own. - - If cached state exists when wl_surface.commit is called in - desynchronized mode, the pending state is added to the cached - state, and applied as a whole. This invalidates the cache. - - Note: even if a sub-surface is set to desynchronized, a parent - sub-surface may override it to behave as synchronized. For details, - see wl_subsurface. - - If a surface's parent surface behaves as desynchronized, then - the cached state is applied on set_desync. + See wl_subsurface and wl_surface.commit for more information.