From f3fe6b9a4394503f18d1c602768c32ac86b153b0 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Wed, 18 Jun 2025 13:30:21 +0200 Subject: [PATCH 01/44] util/box: set dest to empty if boxes don't intersect Currently if both box_a and box_b are non-empty but do not intersect, this function does not set dest to an empty box. This contradicts the doc comments and is surprising for users. (cherry picked from commit f5e7caf59994cfa08650cade41374e23779a24a4) --- util/box.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/util/box.c b/util/box.c index 6a6becf33..a9b42579a 100644 --- a/util/box.c +++ b/util/box.c @@ -67,7 +67,12 @@ bool wlr_box_intersection(struct wlr_box *dest, const struct wlr_box *box_a, dest->width = x2 - x1; dest->height = y2 - y1; - return !wlr_box_empty(dest); + if (wlr_box_empty(dest)) { + *dest = (struct wlr_box){0}; + return false; + } + + return true; } bool wlr_box_contains_point(const struct wlr_box *box, double x, double y) { From 68dea559708e0aa8a311b1495cd40d1fcbc013ec Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 29 Jun 2025 10:57:56 +0200 Subject: [PATCH 02/44] render/egl: fix software rendering check Commit b4ce0d8b39a4 ("render/egl: accept negative DRM FD to select software rendering") added an EXT_device_drm check to figure out whether the user selected a device with a DRM FD or without one. However, for KMS-only devices, Mesa will never advertise the selected KMS node: https://gitlab.freedesktop.org/mesa/mesa/-/blob/3f1d40d230c98d4ca9d2e20b214723a7b7d265d2/src/egl/main/egldevice.c#L109 Instead, pass down a parameter to indicate whether a DRM FD was passed in. Fixes: b4ce0d8b39a4 ("render/egl: accept negative DRM FD to select software rendering") (cherry picked from commit 48bd1831feff89ac94aacc2218601c2b569ef70c) --- render/egl.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/render/egl.c b/render/egl.c index 07d3a1425..6f3e9c8ca 100644 --- a/render/egl.c +++ b/render/egl.c @@ -260,7 +260,8 @@ static struct wlr_egl *egl_create(void) { return egl; } -static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display) { +static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display, + bool allow_software) { egl->display = display; EGLint major, minor; @@ -326,9 +327,8 @@ static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display) { // The only way a non-DRM device is selected is when the user // explicitly picks software rendering - if (check_egl_ext(device_exts_str, "EGL_MESA_device_software") && - egl->exts.EXT_device_drm) { - if (env_parse_bool("WLR_RENDERER_ALLOW_SOFTWARE")) { + if (check_egl_ext(device_exts_str, "EGL_MESA_device_software")) { + if (allow_software || env_parse_bool("WLR_RENDERER_ALLOW_SOFTWARE")) { wlr_log(WLR_INFO, "Using software rendering"); } else { wlr_log(WLR_ERROR, "Software rendering detected, please use " @@ -382,7 +382,7 @@ static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display) { } static bool egl_init(struct wlr_egl *egl, EGLenum platform, - void *remote_display) { + void *remote_display, bool allow_software) { EGLint display_attribs[3] = {0}; size_t display_attribs_len = 0; @@ -401,7 +401,7 @@ static bool egl_init(struct wlr_egl *egl, EGLenum platform, return false; } - if (!egl_init_display(egl, display)) { + if (!egl_init_display(egl, display, allow_software)) { if (egl->exts.KHR_display_reference) { eglTerminate(display); } @@ -556,6 +556,8 @@ static int open_render_node(int drm_fd) { } struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) { + bool allow_software = drm_fd < 0; + struct wlr_egl *egl = egl_create(); if (egl == NULL) { wlr_log(WLR_ERROR, "Failed to create EGL context"); @@ -569,7 +571,7 @@ struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) { */ EGLDeviceEXT egl_device = get_egl_device_from_drm_fd(egl, drm_fd); if (egl_device != EGL_NO_DEVICE_EXT) { - if (egl_init(egl, EGL_PLATFORM_DEVICE_EXT, egl_device)) { + if (egl_init(egl, EGL_PLATFORM_DEVICE_EXT, egl_device, allow_software)) { wlr_log(WLR_DEBUG, "Using EGL_PLATFORM_DEVICE_EXT"); return egl; } @@ -594,7 +596,7 @@ struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) { goto error; } - if (egl_init(egl, EGL_PLATFORM_GBM_KHR, egl->gbm_device)) { + if (egl_init(egl, EGL_PLATFORM_GBM_KHR, egl->gbm_device, allow_software)) { wlr_log(WLR_DEBUG, "Using EGL_PLATFORM_GBM_KHR"); return egl; } @@ -633,7 +635,7 @@ struct wlr_egl *wlr_egl_create_with_context(EGLDisplay display, return NULL; } - if (!egl_init_display(egl, display)) { + if (!egl_init_display(egl, display, true)) { free(egl); return NULL; } From 5aa8c192a51c3fd0c1c21e6c00ae04bf72ca4e8b Mon Sep 17 00:00:00 2001 From: David Turner Date: Mon, 30 Jun 2025 15:45:03 +0100 Subject: [PATCH 03/44] scene: Block damage on single-pixel buffer textures We cache whether buffers are single-pixel buffers (and if so what color they are) to allow rendering optimizations. But this breaks if the client changes out the single-pixel buffer for one with a different color, because this updates the texture in-place instead of actually changing the buffer. We can fix this by blocking in-place texture updates for single pixel buffers. Original bug: https://codeberg.org/ifreund/waylock/issues/121 See also: !5092 (cherry picked from commit 58c3680d96bffa1b8f59c274050a6ecfa27e7c23) --- types/scene/surface.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/types/scene/surface.c b/types/scene/surface.c index e5b3b0f7d..354b5ab92 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -96,8 +96,11 @@ static void scene_buffer_unmark_client_buffer(struct wlr_scene_buffer *scene_buf return; } - assert(buffer->n_ignore_locks > 0); - buffer->n_ignore_locks--; + // If the buffer was a single-pixel buffer where we cached its color + // then it won't have been marked as damage-allowed. + if (buffer->n_ignore_locks > 0) { + buffer->n_ignore_locks--; + } } static int min(int a, int b) { @@ -165,7 +168,22 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { scene_buffer_unmark_client_buffer(scene_buffer); if (surface->buffer) { - client_buffer_mark_next_can_damage(surface->buffer); + // If we've cached the buffer's single-pixel buffer color + // then any in-place updates to the texture wouldn't be + // reflected in rendering. So only allow in-place texture + // updates if it's not a single pixel buffer. Note that we + // can't use the cached scene_buffer->is_single_pixel_buffer + // because that's only set later on. + bool is_single_pixel_buffer = false; + struct wlr_client_buffer *client_buffer = wlr_client_buffer_get(&surface->buffer->base); + if (client_buffer != NULL && client_buffer->source != NULL) { + struct wlr_single_pixel_buffer_v1 *spb = + wlr_single_pixel_buffer_v1_try_from_buffer(client_buffer->source); + is_single_pixel_buffer = spb != NULL; + } + if (!is_single_pixel_buffer) { + client_buffer_mark_next_can_damage(surface->buffer); + } struct wlr_linux_drm_syncobj_surface_v1_state *syncobj_surface_state = wlr_linux_drm_syncobj_v1_get_surface_state(surface); From f935404e68e48d781e3419ac7dd751698d44b16a Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Thu, 29 May 2025 17:38:32 +0800 Subject: [PATCH 04/44] render/pass: Ensure the precision is consistent during comparison (cherry picked from commit a08acfcee0261ae9b084c217dd70dd52eea2904a) --- render/pass.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/render/pass.c b/render/pass.c index 902228273..23bdf96dd 100644 --- a/render/pass.c +++ b/render/pass.c @@ -21,8 +21,8 @@ void wlr_render_pass_add_texture(struct wlr_render_pass *render_pass, if (!wlr_fbox_empty(&options->src_box)) { const struct wlr_fbox *box = &options->src_box; assert(box->x >= 0 && box->y >= 0 && - box->x + box->width <= options->texture->width && - box->y + box->height <= options->texture->height); + (uint32_t)(box->x + box->width) <= options->texture->width && + (uint32_t)(box->y + box->height) <= options->texture->height); } render_pass->impl->add_texture(render_pass, options); From b7205866c071fe935b2f3bdb868d254a1de3b035 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Tue, 20 May 2025 01:14:16 +0900 Subject: [PATCH 05/44] backend/libinput: don't leak udev_device (cherry picked from commit 170f7e070603f0ecdadc4527c65bc08b62073e58) --- backend/libinput/tablet_pad.c | 1 + backend/libinput/tablet_tool.c | 1 + 2 files changed, 2 insertions(+) diff --git a/backend/libinput/tablet_pad.c b/backend/libinput/tablet_pad.c index 98b3e4f4b..2fbfb6a6c 100644 --- a/backend/libinput/tablet_pad.c +++ b/backend/libinput/tablet_pad.c @@ -104,6 +104,7 @@ void init_device_tablet_pad(struct wlr_libinput_input_device *dev) { struct udev_device *udev = libinput_device_get_udev_device(handle); char **dst = wl_array_add(&wlr_tablet_pad->paths, sizeof(char *)); *dst = strdup(udev_device_get_syspath(udev)); + udev_device_unref(udev); int groups = libinput_device_tablet_pad_get_num_mode_groups(handle); for (int i = 0; i < groups; ++i) { diff --git a/backend/libinput/tablet_tool.c b/backend/libinput/tablet_tool.c index eec697c49..d43c6cd0a 100644 --- a/backend/libinput/tablet_tool.c +++ b/backend/libinput/tablet_tool.c @@ -37,6 +37,7 @@ void init_device_tablet(struct wlr_libinput_input_device *dev) { struct udev_device *udev = libinput_device_get_udev_device(dev->handle); char **dst = wl_array_add(&wlr_tablet->paths, sizeof(char *)); *dst = strdup(udev_device_get_syspath(udev)); + udev_device_unref(udev); wl_list_init(&dev->tablet_tools); } From 30c06024574cf9a0d39e3b5830c45d593e49e23e Mon Sep 17 00:00:00 2001 From: Consolatis <40171-Consolatis@users.noreply.gitlab.freedesktop.org> Date: Thu, 10 Jul 2025 19:46:17 +0200 Subject: [PATCH 06/44] transient_seat: initialize seat destroy listener This fixes a `wl_list_remove()` from an uninitialized listener when using `wlr_transient_seat_v1_deny()` in a `create_seat` handler. (cherry picked from commit c39b3ce7a3c4380d7a77f4cb97b3604c227c04d0) --- types/wlr_transient_seat_v1.c | 1 + 1 file changed, 1 insertion(+) diff --git a/types/wlr_transient_seat_v1.c b/types/wlr_transient_seat_v1.c index af8f87ccf..d99ee6edb 100644 --- a/types/wlr_transient_seat_v1.c +++ b/types/wlr_transient_seat_v1.c @@ -64,6 +64,7 @@ static void manager_create_transient_seat(struct wl_client *client, wl_resource_set_implementation(seat->resource, &transient_seat_impl, seat, transient_seat_handle_resource_destroy); + wl_list_init(&seat->seat_destroy.link); wl_signal_emit_mutable(&manager->events.create_seat, seat); return; From 52e1ad01e3074b7022ac776faa94ca6e307ae4c7 Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Wed, 23 Jul 2025 15:05:54 +0000 Subject: [PATCH 07/44] ext-image-capture-source: output: Apply transform to cursor The cursor can be expected to also be transformed if the output is transformed. (cherry picked from commit 80c7e0f772e38f56376e7953b148b480ca49ef3a) --- types/ext_image_capture_source_v1/output.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/types/ext_image_capture_source_v1/output.c b/types/ext_image_capture_source_v1/output.c index e5ef78f93..e661aad3a 100644 --- a/types/ext_image_capture_source_v1/output.c +++ b/types/ext_image_capture_source_v1/output.c @@ -283,7 +283,8 @@ static void output_cursor_source_copy_frame(struct wlr_ext_image_capture_source_ struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - wlr_ext_image_copy_capture_frame_v1_ready(frame, WL_OUTPUT_TRANSFORM_NORMAL, &now); + wlr_ext_image_copy_capture_frame_v1_ready(frame, + cursor_source->output->transform, &now); } static const struct wlr_ext_image_capture_source_v1_interface output_cursor_source_impl = { From aa904ccf06eb342ec323849bec95ff362d0db9b3 Mon Sep 17 00:00:00 2001 From: Jesper Jensen Date: Tue, 5 Aug 2025 16:16:50 +0200 Subject: [PATCH 08/44] output/cursor: Fix double cursor bug When we fail to render the cursor (in my case because the cursor is too large) we bail out of the output_cursor_attempt_hardware function. This causes output_cursor_set_texture to clean up after us, but we've already cleared the hardware_cursor, and so output_disable_hardware_cursor thinks we don't have a hardware cursor to disable. We shouldn't modify the hardware_cursor variable before we've successfully changed the hardware cursor, this way the caller can clean up after us like it expect to. This was brought up by an actual bug when playing the game Kaizen. Which uses oddly sized cursors, that fell back to software cursors for me, and left the hardware cursor hanging around. This change has been tested to fix that. During the testing of this change, I have noticed that the previous code worked fine the first time the cursor was switch to software. It only failed on subsequent attempts. I haven't figured out why that is. (cherry picked from commit 07e92fb86816783acb5a08d628b961398216ab8e) --- types/output/cursor.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/types/output/cursor.c b/types/output/cursor.c index 154b91120..b3ec152ce 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -298,8 +298,6 @@ static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) { return false; } - output->hardware_cursor = NULL; - struct wlr_texture *texture = cursor->texture; // If the cursor was hidden or was a software cursor, the hardware From c1938f79c3448350436784bac602952ee8f6717a Mon Sep 17 00:00:00 2001 From: liupeng Date: Wed, 23 Jul 2025 10:53:42 +0800 Subject: [PATCH 09/44] cursor: update output cursor even if output is disabled During suspend, we first disable output and then remove the input device. This causes cursor->state->surface released while cursor->texture leaves. Which leads to use-after-free after resume. (cherry picked from commit be5e2662113ca40a21f48a5af35a219f50f28794) --- types/wlr_cursor.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/types/wlr_cursor.c b/types/wlr_cursor.c index 44b74e926..4eeb63cec 100644 --- a/types/wlr_cursor.c +++ b/types/wlr_cursor.c @@ -530,10 +530,6 @@ static void cursor_output_cursor_update(struct wlr_cursor_output_cursor *output_ struct wlr_cursor *cur = output_cursor->cursor; struct wlr_output *output = output_cursor->output_cursor->output; - if (!output->enabled) { - return; - } - cursor_output_cursor_reset_image(output_cursor); if (cur->state->buffer != NULL) { From 3118ca5c3ede0bb6f3dd55209dc3c61e761210cb Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Fri, 4 Jul 2025 15:34:14 -0400 Subject: [PATCH 10/44] drm-lease-v1: remove connector active_lease & lease connectors Upon leasing, the wlr_drm_lease_connector_v1 will be automatically clean up by the wlr_output destroy handler. There is no need for the wlr_drm_lease_manager to keep track of leased connectors. (cherry picked from commit 0166fd9eb778761295ea14fdff0515ada1a1cb17) --- include/wlr/types/wlr_drm_lease_v1.h | 5 ---- types/wlr_drm_lease_v1.c | 37 ---------------------------- 2 files changed, 42 deletions(-) diff --git a/include/wlr/types/wlr_drm_lease_v1.h b/include/wlr/types/wlr_drm_lease_v1.h index 752f6efa7..b29a5f6dc 100644 --- a/include/wlr/types/wlr_drm_lease_v1.h +++ b/include/wlr/types/wlr_drm_lease_v1.h @@ -62,8 +62,6 @@ struct wlr_drm_lease_connector_v1 { struct wlr_output *output; struct wlr_drm_lease_device_v1 *device; - /** NULL if no client is currently leasing this connector */ - struct wlr_drm_lease_v1 *active_lease; struct wl_list link; // wlr_drm_lease_device_v1.connectors @@ -93,9 +91,6 @@ struct wlr_drm_lease_v1 { struct wlr_drm_lease_device_v1 *device; - struct wlr_drm_lease_connector_v1 **connectors; - size_t n_connectors; - struct wl_list link; // wlr_drm_lease_device_v1.leases void *data; diff --git a/types/wlr_drm_lease_v1.c b/types/wlr_drm_lease_v1.c index c64bb0896..892a401b3 100644 --- a/types/wlr_drm_lease_v1.c +++ b/types/wlr_drm_lease_v1.c @@ -68,10 +68,6 @@ static void drm_lease_connector_v1_destroy( wlr_log(WLR_DEBUG, "Destroying connector %s", connector->output->name); - if (connector->active_lease) { - wlr_drm_lease_terminate(connector->active_lease->drm_lease); - } - struct wl_resource *resource, *tmp; wl_resource_for_each_safe(resource, tmp, &connector->resources) { wp_drm_lease_connector_v1_send_withdrawn(resource); @@ -140,14 +136,9 @@ static void lease_handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&lease->destroy.link); - for (size_t i = 0; i < lease->n_connectors; ++i) { - lease->connectors[i]->active_lease = NULL; - } - wl_list_remove(&lease->link); wl_resource_set_user_data(lease->resource, NULL); - free(lease->connectors); free(lease); } @@ -180,20 +171,6 @@ struct wlr_drm_lease_v1 *wlr_drm_lease_request_v1_grant( return NULL; } - lease->connectors = calloc(request->n_connectors, sizeof(*lease->connectors)); - if (!lease->connectors) { - wlr_log(WLR_ERROR, "Failed to allocate lease connectors list"); - close(fd); - wp_drm_lease_v1_send_finished(lease->resource); - free(lease); - return NULL; - } - lease->n_connectors = request->n_connectors; - for (size_t i = 0; i < request->n_connectors; ++i) { - lease->connectors[i] = request->connectors[i]; - lease->connectors[i]->active_lease = lease; - } - lease->destroy.notify = lease_handle_destroy; wl_signal_add(&lease->drm_lease->events.destroy, &lease->destroy); @@ -338,16 +315,6 @@ static void drm_lease_request_v1_handle_submit( return; } - for (size_t i = 0; i < request->n_connectors; ++i) { - struct wlr_drm_lease_connector_v1 *conn = request->connectors[i]; - if (conn->active_lease) { - wlr_log(WLR_ERROR, "Failed to create lease, connector %s has " - "already been leased", conn->output->name); - wp_drm_lease_v1_send_finished(lease_resource); - return; - } - } - request->lease_resource = lease_resource; wl_signal_emit_mutable(&request->device->manager->events.request, @@ -440,10 +407,6 @@ static struct wp_drm_lease_connector_v1_interface lease_connector_impl = { static void drm_lease_connector_v1_send_to_client( struct wlr_drm_lease_connector_v1 *connector, struct wl_resource *resource) { - if (connector->active_lease) { - return; - } - struct wl_client *client = wl_resource_get_client(resource); uint32_t version = wl_resource_get_version(resource); From 907938049880011bbff45a614a4f23009e264175 Mon Sep 17 00:00:00 2001 From: liupeng Date: Sat, 30 Aug 2025 14:42:14 +0800 Subject: [PATCH 11/44] drm_lease_v1: initialize device resource link during abnormal exit Signed-off-by: liupeng (cherry picked from commit 5e5842cb1a8e11a7f274638e124ca473025734bc) --- types/wlr_drm_lease_v1.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/types/wlr_drm_lease_v1.c b/types/wlr_drm_lease_v1.c index 892a401b3..14846f21d 100644 --- a/types/wlr_drm_lease_v1.c +++ b/types/wlr_drm_lease_v1.c @@ -453,10 +453,12 @@ static void lease_device_bind(struct wl_client *wl_client, void *data, if (!device) { wlr_log(WLR_DEBUG, "Failed to bind lease device, " "the wlr_drm_lease_device_v1 has been destroyed"); + wl_list_init(wl_resource_get_link(device_resource)); return; } wl_resource_set_user_data(device_resource, device); + wl_list_insert(&device->resources, wl_resource_get_link(device_resource)); int fd = wlr_drm_backend_get_non_master_fd(device->backend); if (fd < 0) { @@ -468,8 +470,6 @@ static void lease_device_bind(struct wl_client *wl_client, void *data, wp_drm_lease_device_v1_send_drm_fd(device_resource, fd); close(fd); - wl_list_insert(&device->resources, wl_resource_get_link(device_resource)); - struct wlr_drm_lease_connector_v1 *connector; wl_list_for_each(connector, &device->connectors, link) { drm_lease_connector_v1_send_to_client(connector, device_resource); From 73aa61686fa75e8b9e5685fbac81b88130bcb441 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 7 Aug 2025 11:56:49 +0200 Subject: [PATCH 12/44] output/cursor: fix missing second cursor When attaching more than one cursor to wlr_output, the first one will pick the output's hardware cursor, then for the second one output_set_hardware_cursor() would fail (since the hardware cursor was already taken), but we still ended up resetting the current hardware cursor (by calling output_disable_hardware_cursor() below). As a result only the second cursor would be displayed. To fix this, move the current hardware cursor check to the caller. Fixes: 510664e79bfc ("output: disable hardware cursor when falling back to software") (cherry picked from commit fd069ad4f2793c812bd47e40f52f92eb8af7ca34) --- types/output/cursor.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/types/output/cursor.c b/types/output/cursor.c index b3ec152ce..70647afb7 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -288,13 +288,7 @@ static struct wlr_buffer *render_cursor_buffer(struct wlr_output_cursor *cursor) static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) { struct wlr_output *output = cursor->output; - if (!output->impl->set_cursor || - output->software_cursor_locks > 0) { - return false; - } - - struct wlr_output_cursor *hwcur = output->hardware_cursor; - if (hwcur != NULL && hwcur != cursor) { + if (!output->impl->set_cursor || output->software_cursor_locks > 0) { return false; } @@ -422,12 +416,15 @@ bool output_cursor_set_texture(struct wlr_output_cursor *cursor, wl_list_init(&cursor->renderer_destroy.link); } - if (output_cursor_attempt_hardware(cursor)) { - return true; + if (output->hardware_cursor == NULL || output->hardware_cursor == cursor) { + if (output_cursor_attempt_hardware(cursor)) { + return true; + } + + wlr_log(WLR_DEBUG, "Falling back to software cursor on output '%s'", output->name); + output_disable_hardware_cursor(output); } - wlr_log(WLR_DEBUG, "Falling back to software cursor on output '%s'", output->name); - output_disable_hardware_cursor(output); output_cursor_damage_whole(cursor); return true; } From bb5180ce9e34815bc925245dc83eeaf3372fde89 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 3 Aug 2025 17:25:05 +0200 Subject: [PATCH 13/44] scene/surface: simplify single-pixel-buffer check in surface_reconfigure() No need to call wlr_client_buffer_get() on wlr_client_buffer.base: we're already manipulating a wlr_client_buffer. (cherry picked from commit b62c6878e116015d34827f66a8c4fee986e4ebfc) --- types/scene/surface.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/types/scene/surface.c b/types/scene/surface.c index 354b5ab92..47977bf4b 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -175,10 +175,9 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { // can't use the cached scene_buffer->is_single_pixel_buffer // because that's only set later on. bool is_single_pixel_buffer = false; - struct wlr_client_buffer *client_buffer = wlr_client_buffer_get(&surface->buffer->base); - if (client_buffer != NULL && client_buffer->source != NULL) { + if (surface->buffer->source != NULL) { struct wlr_single_pixel_buffer_v1 *spb = - wlr_single_pixel_buffer_v1_try_from_buffer(client_buffer->source); + wlr_single_pixel_buffer_v1_try_from_buffer(surface->buffer->source); is_single_pixel_buffer = spb != NULL; } if (!is_single_pixel_buffer) { From d092e40dec35ce501c6b7809bec4e1b5e08e4688 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 3 Aug 2025 17:28:13 +0200 Subject: [PATCH 14/44] scene/surface: fix NULL deref when source buffer is destroyed Fixes the following crash, witnessed after a GPU reset: #0 0x00007fba9a32774c n/a (libc.so.6 + 0x9774c) #1 0x00007fba9a2cddc0 raise (libc.so.6 + 0x3ddc0) #2 0x00007fba9a2b557a abort (libc.so.6 + 0x2557a) #3 0x00007fba9a2b54e3 n/a (libc.so.6 + 0x254e3) #4 0x00007fba9a53fb78 wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer (libwlroots-0.20.so + 0x26b78) #5 0x00007fba9a590846 surface_reconfigure (libwlroots-0.20.so + 0x77846) #6 0x00007fba9a590cbb scene_surface_set_clip (libwlroots-0.20.so + 0x77cbb) #7 0x00007fba9a590efa subsurface_tree_set_clip (libwlroots-0.20.so + 0x77efa) #8 0x00007fba9a590f1f subsurface_tree_set_clip (libwlroots-0.20.so + 0x77f1f) #9 0x00007fba9a590f1f subsurface_tree_set_clip (libwlroots-0.20.so + 0x77f1f) #10 0x00007fba9a590f8d wlr_scene_subsurface_tree_set_clip (libwlroots-0.20.so + 0x77f8d) Reported-by: Hubert Hirtz (cherry picked from commit bd566225eacdda2b72b967fb5168314924871052) --- types/scene/surface.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/types/scene/surface.c b/types/scene/surface.c index 47977bf4b..f0651bc38 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -203,7 +203,8 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { &surface->buffer->base, &options); if (syncobj_surface_state != NULL && - (surface->current.committed & WLR_SURFACE_STATE_BUFFER)) { + (surface->current.committed & WLR_SURFACE_STATE_BUFFER) && + surface->buffer->source != NULL) { wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer(syncobj_surface_state, surface->buffer->source); } From d4009183a13511d336255c78f04a7d539f082770 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 3 Aug 2025 17:30:07 +0200 Subject: [PATCH 15/44] cursor: use source buffer to signal release timeline point Same as 128cd07e9156 ("scene/surface: use source buffer to signal release timeline point"), but for the cursor. (cherry picked from commit 462046ffdcdaacfc38ac606ed74397c360e23606) --- types/wlr_cursor.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/types/wlr_cursor.c b/types/wlr_cursor.c index 4eeb63cec..407d30620 100644 --- a/types/wlr_cursor.c +++ b/types/wlr_cursor.c @@ -585,10 +585,11 @@ static void cursor_output_cursor_update(struct wlr_cursor_output_cursor *output_ &src_box, dst_width, dst_height, surface->current.transform, hotspot_x, hotspot_y, wait_timeline, wait_point); - if (syncobj_surface_state != NULL && surface->buffer != NULL && + if (syncobj_surface_state != NULL && + surface->buffer != NULL && surface->buffer->source != NULL && (surface->current.committed & WLR_SURFACE_STATE_BUFFER)) { wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer(syncobj_surface_state, - &surface->buffer->base); + surface->buffer->source); } if (output_cursor->output_cursor->visible) { From 7a5278892979eaa86df29e77621c61ab94eca7da Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 8 Sep 2025 15:40:55 +0200 Subject: [PATCH 16/44] render/vulkan: Handle multi-descriptor sets A combined image sampler may need several descriptors in a descriptor set. We are not currently checking how many descriptors are required, nor is it presumably guaranteed that such multi-descriptor allocation will not fail due to fragmentation. If the pool free counter is not zero, try to allocate but continue with the next pool and fall back to creating a new pool if the allocation failed. Fixes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/4010 (cherry picked from commit dd7f5431891cad6b3980c8a7d5fdc1c1f668c1b1) --- render/vulkan/renderer.c | 109 ++++++++++++++++++++++----------------- 1 file changed, 61 insertions(+), 48 deletions(-) diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index b2feca013..1a696734a 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -66,59 +66,72 @@ static struct wlr_vk_descriptor_pool *alloc_ds( struct wl_list *pool_list, size_t *last_pool_size) { VkResult res; - bool found = false; - struct wlr_vk_descriptor_pool *pool; - wl_list_for_each(pool, pool_list, link) { - if (pool->free > 0) { - found = true; - break; - } - } - - if (!found) { // create new pool - pool = calloc(1, sizeof(*pool)); - if (!pool) { - wlr_log_errno(WLR_ERROR, "allocation failed"); - return NULL; - } - - size_t count = 2 * (*last_pool_size); - if (!count) { - count = start_descriptor_pool_size; - } - - pool->free = count; - VkDescriptorPoolSize pool_size = { - .descriptorCount = count, - .type = type, - }; - - VkDescriptorPoolCreateInfo dpool_info = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, - .maxSets = count, - .poolSizeCount = 1, - .pPoolSizes = &pool_size, - .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, - }; - - res = vkCreateDescriptorPool(renderer->dev->dev, &dpool_info, NULL, - &pool->pool); - if (res != VK_SUCCESS) { - wlr_vk_error("vkCreateDescriptorPool", res); - free(pool); - return NULL; - } - - *last_pool_size = count; - wl_list_insert(pool_list, &pool->link); - } - VkDescriptorSetAllocateInfo ds_info = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, .descriptorSetCount = 1, .pSetLayouts = layout, - .descriptorPool = pool->pool, }; + + struct wlr_vk_descriptor_pool *pool; + wl_list_for_each(pool, pool_list, link) { + if (pool->free > 0) { + ds_info.descriptorPool = pool->pool; + res = vkAllocateDescriptorSets(renderer->dev->dev, &ds_info, ds); + switch (res) { + case VK_ERROR_FRAGMENTED_POOL: + case VK_ERROR_OUT_OF_POOL_MEMORY: + // Descriptor sets with more than one descriptor can cause us + // to run out of pool memory early or lead to fragmentation + // that makes the pool unable to service our allocation + // request. Try the next pool or allocate a new one. + continue; + case VK_SUCCESS: + --pool->free; + return pool; + default: + wlr_vk_error("vkAllocateDescriptorSets", res); + return NULL; + } + } + } + + pool = calloc(1, sizeof(*pool)); + if (!pool) { + wlr_log_errno(WLR_ERROR, "allocation failed"); + return NULL; + } + + size_t count = 2 * (*last_pool_size); + if (!count) { + count = start_descriptor_pool_size; + } + + pool->free = count; + VkDescriptorPoolSize pool_size = { + .descriptorCount = count, + .type = type, + }; + + VkDescriptorPoolCreateInfo dpool_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .maxSets = count, + .poolSizeCount = 1, + .pPoolSizes = &pool_size, + .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, + }; + + res = vkCreateDescriptorPool(renderer->dev->dev, &dpool_info, NULL, + &pool->pool); + if (res != VK_SUCCESS) { + wlr_vk_error("vkCreateDescriptorPool", res); + free(pool); + return NULL; + } + + *last_pool_size = count; + wl_list_insert(pool_list, &pool->link); + + ds_info.descriptorPool = pool->pool; res = vkAllocateDescriptorSets(renderer->dev->dev, &ds_info, ds); if (res != VK_SUCCESS) { wlr_vk_error("vkAllocateDescriptorSets", res); From 9b42c1901d967a6a05209de741131053722e9be1 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 21 Sep 2025 23:03:29 +0200 Subject: [PATCH 17/44] build: bump version to 0.19.1 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 016e59842..24d812dc6 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'wlroots', 'c', - version: '0.19.0', + version: '0.19.1', license: 'MIT', meson_version: '>=1.3', default_options: [ From eba71d59d4905a22c3a5e6294c166d15edf0ce63 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 12 Aug 2025 19:00:11 +0200 Subject: [PATCH 18/44] backend, output: send commit events after applying all in wlr_backend_commit() We were iterating over involved outputs, applying the new state and sending the commit event for each one. This resulted in commit events being fired while we weren't done applying the new state for all outputs. Fix this by first applying all of the states, then firing all of the events. Closes: https://github.com/swaywm/sway/issues/8829 (cherry picked from commit 7392b3313a7b483c61f4fea648ba8f2aa4ce8798) --- backend/backend.c | 5 +++++ include/types/wlr_output.h | 1 + types/output/output.c | 3 +++ 3 files changed, 9 insertions(+) diff --git a/backend/backend.c b/backend/backend.c index a130d9045..3d84aa636 100644 --- a/backend/backend.c +++ b/backend/backend.c @@ -485,5 +485,10 @@ bool wlr_backend_commit(struct wlr_backend *backend, output_apply_commit(state->output, &state->base); } + for (size_t i = 0; i < states_len; i++) { + const struct wlr_backend_output_state *state = &states[i]; + output_send_commit_event(state->output, &state->base); + } + return true; } diff --git a/include/types/wlr_output.h b/include/types/wlr_output.h index 2dc979c66..b0acac4e8 100644 --- a/include/types/wlr_output.h +++ b/include/types/wlr_output.h @@ -25,6 +25,7 @@ void output_defer_present(struct wlr_output *output, struct wlr_output_event_pre bool output_prepare_commit(struct wlr_output *output, const struct wlr_output_state *state); void output_apply_commit(struct wlr_output *output, const struct wlr_output_state *state); +void output_send_commit_event(struct wlr_output *output, const struct wlr_output_state *state); void output_state_get_buffer_src_box(const struct wlr_output_state *state, struct wlr_fbox *out); diff --git a/types/output/output.c b/types/output/output.c index a352a5e59..aa2384575 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -745,7 +745,9 @@ void output_apply_commit(struct wlr_output *output, const struct wlr_output_stat } output_apply_state(output, state); +} +void output_send_commit_event(struct wlr_output *output, const struct wlr_output_state *state) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); struct wlr_output_event_commit event = { @@ -787,6 +789,7 @@ bool wlr_output_commit_state(struct wlr_output *output, } output_apply_commit(output, &pending); + output_send_commit_event(output, &pending); if (new_back_buffer) { wlr_buffer_unlock(pending.buffer); From 0d20e464983478719df623087ac7ed98431e4219 Mon Sep 17 00:00:00 2001 From: JiDe Zhang Date: Mon, 15 Sep 2025 15:58:51 +0800 Subject: [PATCH 19/44] xwayland: fix assertion failure in wlr_xwayland_shell_v1 The issue occurred when `wlr_xwayland_shell_v1` was destroyed before `wlr_xwayland`. This happened because `wlr_xwayland` didn't remove the listener for the shell's destroy event in `handle_shell_destroy`. (cherry picked from commit d7ae9a866bf2372e0e86a9cf51a963b4fbc30c08) --- xwayland/xwayland.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/xwayland/xwayland.c b/xwayland/xwayland.c index d3b2a6c84..2d07825ff 100644 --- a/xwayland/xwayland.c +++ b/xwayland/xwayland.c @@ -69,6 +69,11 @@ static void handle_shell_destroy(struct wl_listener *listener, void *data) { struct wlr_xwayland *xwayland = wl_container_of(listener, xwayland, shell_destroy); xwayland->shell_v1 = NULL; + wl_list_remove(&xwayland->shell_destroy.link); + // Will remove this list in handle_shell_destroy(). + // This ensures the link is always initialized and + // avoids the need to keep check conditions in sync. + wl_list_init(&xwayland->shell_destroy.link); } void wlr_xwayland_destroy(struct wlr_xwayland *xwayland) { From 761927bbbdd5088a68d57dc39b3d1668ccb9074a Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 1 Oct 2025 16:58:49 +0200 Subject: [PATCH 20/44] backend/session: fix crash on udev device remove event libwayland adds phantom listeners here: https://gitlab.freedesktop.org/wayland/wayland/-/blob/d81525a235e48cc5de3e4005a16ddb1fbdfd9d7c/src/wayland-server.c#L2378 Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3982 (cherry picked from commit 406aa5f7f5649a9774fd89880037be3a731e96fa) --- backend/session/session.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/session/session.c b/backend/session/session.c index dcf07c708..48f4ab187 100644 --- a/backend/session/session.c +++ b/backend/session/session.c @@ -367,7 +367,10 @@ void wlr_session_close_file(struct wlr_session *session, } assert(wl_list_empty(&dev->events.change.listener_list)); - assert(wl_list_empty(&dev->events.remove.listener_list)); + // TODO: assert that the "remove" listener list is empty as well. Listeners + // will typically call wlr_session_close_file() in response, and + // wl_signal_emit_mutable() installs two phantom listeners, so we'd count + // these two. close(dev->fd); wl_list_remove(&dev->link); From 8fc64ae8d5fb807c9eec2867ca9969c5d28d356a Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Tue, 23 Sep 2025 23:44:52 +0900 Subject: [PATCH 21/44] util/box.c: use 1/256 instead of 1/65536 in wlr_box_closest_point() This fixes the issue that a scrollbar in a maximized GTK/Chromium window cannot be dragged when cursor is on the right/bottom edge of the output. The issue was caused by rounding in `wl_fixed_from_double()` ([1]); if `wlr_cursor_move()` constrains the x-position of the cursor to `(output width)-1/65536`, `wl_fixed_from_double()` converts it to just `(output width)`, which is perceived as outside of the window by GTK/Chromium. Using 1/256 (minimal unit of `wl_fixed_t`) instead of 1/65536 avoids this rounding issue. [1]: https://gitlab.freedesktop.org/wayland/wayland/-/commit/f246e619d17deb92f414315d1747a9b7aca659b9 (cherry picked from commit 19c5d22beb1af30e5fcd831751f404caafdcd2f5) --- util/box.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/util/box.c b/util/box.c index a9b42579a..aae09888f 100644 --- a/util/box.c +++ b/util/box.c @@ -19,16 +19,15 @@ void wlr_box_closest_point(const struct wlr_box *box, double x, double y, // // In order to be consistent with e.g. wlr_box_contains_point(), // this function returns a point inside the bottom and right edges - // of the box by at least 1/65536 of a unit (pixel). 1/65536 is + // of the box by at least 1/256 of a unit (pixel). 1/256 is // small enough to avoid a "dead zone" with high-resolution mice - // but large enough to avoid rounding to zero (due to loss of - // significant digits) in simple floating-point calculations. + // but large enough to avoid rounding to zero in wl_fixed_from_double(). // find the closest x point if (x < box->x) { *dest_x = box->x; - } else if (x > box->x + box->width - 1/65536.0) { - *dest_x = box->x + box->width - 1/65536.0; + } else if (x > box->x + box->width - 1/256.0) { + *dest_x = box->x + box->width - 1/256.0; } else { *dest_x = x; } @@ -36,8 +35,8 @@ void wlr_box_closest_point(const struct wlr_box *box, double x, double y, // find closest y point if (y < box->y) { *dest_y = box->y; - } else if (y > box->y + box->height - 1/65536.0) { - *dest_y = box->y + box->height - 1/65536.0; + } else if (y > box->y + box->height - 1/256.0) { + *dest_y = box->y + box->height - 1/256.0; } else { *dest_y = y; } From f56a69aa7627ae3b3f335e6a436d691e7a2e4537 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 16 Oct 2025 10:42:20 +0200 Subject: [PATCH 22/44] linux_drm_syncobj_v1: fix use-after-free in surface_commit_destroy() surface_commit_destroy() accesses a field from struct wlr_linux_drm_syncobj_surface_v1, however that struct may have been free'd earlier: ==1103==ERROR: AddressSanitizer: heap-use-after-free on address 0x7cdef7a6e288 at pc 0x7feefaac335a bp 0x7ffc4de8f570 sp 0x7ffc4de8f560 READ of size 8 at 0x7cdef7a6e288 thread T0 #0 0x7feefaac3359 in surface_commit_destroy ../subprojects/wlroots/types/wlr_linux_drm_syncobj_v1.c:195 #1 0x7feefaac34cd in surface_commit_handle_surface_destroy ../subprojects/wlroots/types/wlr_linux_drm_syncobj_v1.c:211 #2 0x7feefbd194cf in wl_signal_emit_mutable (/usr/lib/libwayland-server.so.0+0x84cf) (BuildId: b9664217748f523995e3f855fa197cf8e59942d1) #3 0x7feefaa52b22 in surface_handle_resource_destroy ../subprojects/wlroots/types/wlr_compositor.c:730 #4 0x7feefbd1bb9f (/usr/lib/libwayland-server.so.0+0xab9f) (BuildId: b9664217748f523995e3f855fa197cf8e59942d1) #5 0x7feefaa46a18 in surface_handle_destroy ../subprojects/wlroots/types/wlr_compositor.c:65 #6 0x7feef89afac5 (/usr/lib/libffi.so.8+0x7ac5) (BuildId: d5e3b0d8921923f35438adefa9f864745abc5e90) #7 0x7feef89ac76a (/usr/lib/libffi.so.8+0x476a) (BuildId: d5e3b0d8921923f35438adefa9f864745abc5e90) #8 0x7feef89af06d in ffi_call (/usr/lib/libffi.so.8+0x706d) (BuildId: d5e3b0d8921923f35438adefa9f864745abc5e90) #9 0x7feefbd17531 (/usr/lib/libwayland-server.so.0+0x6531) (BuildId: b9664217748f523995e3f855fa197cf8e59942d1) #10 0x7feefbd1cd2f (/usr/lib/libwayland-server.so.0+0xbd2f) (BuildId: b9664217748f523995e3f855fa197cf8e59942d1) #11 0x7feefbd1b181 in wl_event_loop_dispatch (/usr/lib/libwayland-server.so.0+0xa181) (BuildId: b9664217748f523995e3f855fa197cf8e59942d1) #12 0x7feefbd1d296 in wl_display_run (/usr/lib/libwayland-server.so.0+0xc296) (BuildId: b9664217748f523995e3f855fa197cf8e59942d1) #13 0x555bf0a55a40 in server_run ../sway/server.c:615 #14 0x555bf0a4a654 in main ../sway/main.c:376 #15 0x7feef9227674 (/usr/lib/libc.so.6+0x27674) (BuildId: 4fe011c94a88e8aeb6f2201b9eb369f42b4a1e9e) #16 0x7feef9227728 in __libc_start_main (/usr/lib/libc.so.6+0x27728) (BuildId: 4fe011c94a88e8aeb6f2201b9eb369f42b4a1e9e) #17 0x555bf0a03f54 in _start (/home/leo/code/stuff/sway/build/sway/sway+0x390f54) (BuildId: e3d4e653af1aa0885f0426c403e16fc87c086d33) 0x7cdef7a6e288 is located 8 bytes inside of 176-byte region [0x7cdef7a6e280,0x7cdef7a6e330) freed by thread T0 here: #0 0x7feefb71f79d in free /usr/src/debug/gcc/gcc/libsanitizer/asan/asan_malloc_linux.cpp:51 #1 0x7feefaac29f1 in surface_destroy ../subprojects/wlroots/types/wlr_linux_drm_syncobj_v1.c:84 #2 0x7feefaac2e47 in surface_handle_resource_destroy ../subprojects/wlroots/types/wlr_linux_drm_syncobj_v1.c:143 #3 0x7feefbd1bb9f (/usr/lib/libwayland-server.so.0+0xab9f) (BuildId: b9664217748f523995e3f855fa197cf8e59942d1) #4 0x7feefaac2a12 in surface_handle_destroy ../subprojects/wlroots/types/wlr_linux_drm_syncobj_v1.c:89 #5 0x7feef89afac5 (/usr/lib/libffi.so.8+0x7ac5) (BuildId: d5e3b0d8921923f35438adefa9f864745abc5e90) previously allocated by thread T0 here: #0 0x7feefb7205dd in calloc /usr/src/debug/gcc/gcc/libsanitizer/asan/asan_malloc_linux.cpp:74 #1 0x7feefaac4abd in manager_handle_get_surface ../subprojects/wlroots/types/wlr_linux_drm_syncobj_v1.c:313 #2 0x7feef89afac5 (/usr/lib/libffi.so.8+0x7ac5) (BuildId: d5e3b0d8921923f35438adefa9f864745abc5e90) Fix this by storing the struct wlr_surface in the field. Closes: https://github.com/swaywm/sway/issues/8917 (cherry picked from commit 6d63871f059192b15d2fa0dfacfb391709f4952d) --- types/wlr_linux_drm_syncobj_v1.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/types/wlr_linux_drm_syncobj_v1.c b/types/wlr_linux_drm_syncobj_v1.c index 7873a09c4..988d44e01 100644 --- a/types/wlr_linux_drm_syncobj_v1.c +++ b/types/wlr_linux_drm_syncobj_v1.c @@ -26,7 +26,7 @@ struct wlr_linux_drm_syncobj_surface_v1 { }; struct wlr_linux_drm_syncobj_surface_v1_commit { - struct wlr_linux_drm_syncobj_surface_v1 *surface; + struct wlr_surface *surface; struct wlr_drm_syncobj_timeline_waiter waiter; uint32_t cached_seq; @@ -192,7 +192,7 @@ static struct wlr_linux_drm_syncobj_surface_v1 *surface_from_wlr_surface( } static void surface_commit_destroy(struct wlr_linux_drm_syncobj_surface_v1_commit *commit) { - wlr_surface_unlock_cached(commit->surface->surface, commit->cached_seq); + wlr_surface_unlock_cached(commit->surface, commit->cached_seq); wl_list_remove(&commit->surface_destroy.link); wlr_drm_syncobj_timeline_waiter_finish(&commit->waiter); free(commit); @@ -237,7 +237,7 @@ static bool lock_surface_commit(struct wlr_linux_drm_syncobj_surface_v1 *surface return false; } - commit->surface = surface; + commit->surface = surface->surface; commit->cached_seq = wlr_surface_lock_pending(surface->surface); commit->surface_destroy.notify = surface_commit_handle_surface_destroy; From ea1ade5e5dd953d51db53e141312f6d3174a3010 Mon Sep 17 00:00:00 2001 From: David Turner Date: Mon, 20 Oct 2025 13:55:00 +0100 Subject: [PATCH 23/44] xwm: Fix double-close When an FD is passed to xcb_connect_to_fd(), xcb takes ownership of that FD and is responsible for closing it, which it does when xcb_disconnect() is called. But the xwayland handler code also keeps a copy of the FD and closes it via safe_close() in server_finish_process(). This double-close can cause all sorts of problems if another part of wlroots allocates another FD between the two closes - the latter close will close the wrong FD and things go horribly wrong (in my case leading to use-after-free and segfaults). Fix this by setting wm_fd[0]=-1 after calling xwm_create(), and ensuring that xwm_create() closes the FD if startup errors occur. (cherry picked from commit 879243e370de6167d2c49510396f937b1a93fab5) --- include/xwayland/xwm.h | 1 + xwayland/xwayland.c | 3 +++ xwayland/xwm.c | 3 +++ 3 files changed, 7 insertions(+) diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h index 799cebc47..6dafd6529 100644 --- a/include/xwayland/xwm.h +++ b/include/xwayland/xwm.h @@ -162,6 +162,7 @@ struct wlr_xwm { struct wl_listener drop_focus_destroy; }; +// xwm_create takes ownership of wm_fd and will close it under all circumstances. struct wlr_xwm *xwm_create(struct wlr_xwayland *wlr_xwayland, int wm_fd); void xwm_destroy(struct wlr_xwm *xwm); diff --git a/xwayland/xwayland.c b/xwayland/xwayland.c index 2d07825ff..63af99c51 100644 --- a/xwayland/xwayland.c +++ b/xwayland/xwayland.c @@ -42,6 +42,9 @@ static void handle_server_start(struct wl_listener *listener, void *data) { static void xwayland_mark_ready(struct wlr_xwayland *xwayland) { assert(xwayland->server->wm_fd[0] >= 0); xwayland->xwm = xwm_create(xwayland, xwayland->server->wm_fd[0]); + // xwm_create takes ownership of wm_fd[0] under all circumstances + xwayland->server->wm_fd[0] = -1; + if (!xwayland->xwm) { return; } diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 8cd1d41ab..3caf335e3 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -2470,6 +2470,7 @@ void xwm_set_cursor(struct wlr_xwm *xwm, const uint8_t *pixels, uint32_t stride, struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) { struct wlr_xwm *xwm = calloc(1, sizeof(*xwm)); if (xwm == NULL) { + close(wm_fd); return NULL; } @@ -2484,11 +2485,13 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) { xwm->ping_timeout = 10000; + // xcb_connect_to_fd takes ownership of the FD regardless of success/failure xwm->xcb_conn = xcb_connect_to_fd(wm_fd, NULL); int rc = xcb_connection_has_error(xwm->xcb_conn); if (rc) { wlr_log(WLR_ERROR, "xcb connect failed: %d", rc); + xcb_disconnect(xwm->xcb_conn); free(xwm); return NULL; } From e432b7bd1ccf1b042f3973981baa10f937fb2150 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 16 Oct 2025 11:04:25 +0200 Subject: [PATCH 24/44] ci: fix VKMS lookup after faux bus migration VKMS has been migrated to the new faux bus. This causes breakage in CI, because we used the platform bus to find the right device. udev hasn't been updated yet to support the faux bus, so just use sysfs instead. --- .builds/archlinux.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml index 8457ed585..fae04ab31 100644 --- a/.builds/archlinux.yml +++ b/.builds/archlinux.yml @@ -41,9 +41,10 @@ tasks: cd wlroots/build-gcc/tinywl sudo modprobe vkms udevadm settle + card="/dev/dri/$(ls /sys/devices/faux/vkms/drm/ | grep ^card)" export WLR_BACKENDS=drm export WLR_RENDERER=pixman - export WLR_DRM_DEVICES=/dev/dri/by-path/platform-vkms-card + export WLR_DRM_DEVICES="$card" export UBSAN_OPTIONS=halt_on_error=1 - sudo chmod ugo+rw /dev/dri/by-path/platform-vkms-card + sudo chmod ugo+rw "$card" sudo -E seatd-launch -- ./tinywl -s 'kill $PPID' || [ $? = 143 ] From 8c9e6b7c9f3c5344f456e97dc29dcd8d8a5f015b Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 22 Oct 2025 00:21:53 +0200 Subject: [PATCH 25/44] build: bump version to 0.19.2 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 24d812dc6..75682e6e4 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'wlroots', 'c', - version: '0.19.1', + version: '0.19.2', license: 'MIT', meson_version: '>=1.3', default_options: [ From 878aebfb74f0a83e9839db9c3b214c4063e67862 Mon Sep 17 00:00:00 2001 From: David96 Date: Sun, 2 Feb 2025 20:47:56 +0100 Subject: [PATCH 26/44] wlr_virtual_pointer: Set axis source on all axis Currently it is possible to crash a wlroots compositor by setting any axis source other than 0 and sending an axis event in the HORIZONTAL direction from wlr_virtual_pointer since the axis source is only set on the first axis. This then hits the assert in wlr_seat_pointer.c:332. Fix by always setting the source on all axis. (cherry picked from commit aef84f0e4d506dda18992f0213e3233a4ed2ac20) --- types/wlr_virtual_pointer_v1.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/types/wlr_virtual_pointer_v1.c b/types/wlr_virtual_pointer_v1.c index b867c8fb1..fed5790a9 100644 --- a/types/wlr_virtual_pointer_v1.c +++ b/types/wlr_virtual_pointer_v1.c @@ -134,8 +134,11 @@ static void virtual_pointer_axis_source(struct wl_client *client, if (pointer == NULL) { return; } - pointer->axis_event[pointer->axis].pointer = &pointer->pointer; - pointer->axis_event[pointer->axis].source = source; + int n_axis = sizeof(pointer->axis_event) / sizeof(pointer->axis_event[0]); + for (int i = 0; i < n_axis; i++) { + pointer->axis_event[i].pointer = &pointer->pointer; + pointer->axis_event[i].source = source; + } } static void virtual_pointer_axis_stop(struct wl_client *client, From aabe351207c6f57d5637f042dd523f643cf28c20 Mon Sep 17 00:00:00 2001 From: Dale Turner Date: Wed, 3 Dec 2025 20:02:29 -0400 Subject: [PATCH 27/44] =?UTF-8?q?Add=20"const"=20to=20eliminate=20"error:?= =?UTF-8?q?=20initialization=20discards=20=E2=80=98const=E2=80=99=20qualif?= =?UTF-8?q?ier=20from=20pointer=20target=20type"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 47486545b196987f6f07fffe2929bba8f515b8e9) --- xcursor/xcursor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xcursor/xcursor.c b/xcursor/xcursor.c index b5edb9de8..6627fb6cd 100644 --- a/xcursor/xcursor.c +++ b/xcursor/xcursor.c @@ -602,7 +602,7 @@ xcursor_build_fullname(const char *dir, const char *subdir, const char *file) static const char * xcursor_next_path(const char *path) { - char *colon = strchr(path, ':'); + const char *colon = strchr(path, ':'); if (!colon) return NULL; From 4279ee509110b020f197351b3fef37fe23ea9e04 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Sat, 20 Dec 2025 14:57:24 -0500 Subject: [PATCH 28/44] render/allocator: add missing wlr_buffer_finish() in destroy impls Fixes use-after-free on exit of labwc running nested: ==50906== Invalid write of size 8 ==50906== at 0x4A85403: wl_list_remove (wayland-util.c:57) ==50906== by 0x40BBAF9: destroy_wl_buffer (output.c:146) ==50906== by 0x40B9B4F: backend_destroy (backend.c:488) ==50906== by 0x409E96F: wlr_backend_destroy (backend.c:68) ==50906== by 0x40B78A6: multi_backend_destroy (backend.c:62) ==50906== by 0x409E96F: wlr_backend_destroy (backend.c:68) ==50906== by 0x4043DA0: server_finish (server.c:788) ==50906== by 0x403AA85: main (main.c:277) ==50906== Address 0xb4435e8 is 40 bytes inside a block of size 136 free'd ==50906== at 0x4A3E8EF: free (vg_replace_malloc.c:989) ==50906== by 0x409C954: buffer_destroy (shm.c:28) ==50906== by 0x40E96F4: buffer_consider_destroy (buffer.c:42) ==50906== by 0x40E9754: wlr_buffer_drop (buffer.c:52) ==50906== by 0x41498DA: slot_reset (swapchain.c:44) ==50906== by 0x4149933: wlr_swapchain_destroy (swapchain.c:53) ==50906== by 0x40CB1FA: wlr_output_finish (output.c:410) ==50906== by 0x40BE00B: output_destroy (output.c:957) ==50906== by 0x40CB2FC: wlr_output_destroy (output.c:436) ==50906== by 0x40B9AFC: backend_destroy (backend.c:481) ==50906== by 0x409E96F: wlr_backend_destroy (backend.c:68) ==50906== by 0x40B78A6: multi_backend_destroy (backend.c:62) ==50906== Block was alloc'd at ==50906== at 0x4A42C13: calloc (vg_replace_malloc.c:1675) ==50906== by 0x409CA84: allocator_create_buffer (shm.c:68) ==50906== by 0x409C7BA: wlr_allocator_create_buffer (allocator.c:186) ==50906== by 0x4149B80: wlr_swapchain_acquire (swapchain.c:102) ==50906== by 0x40C90DA: render_cursor_buffer (cursor.c:246) ==50906== by 0x40C93DC: output_cursor_attempt_hardware (cursor.c:303) ==50906== by 0x40C9A61: output_cursor_set_texture (cursor.c:420) ==50906== by 0x40C9738: wlr_output_cursor_set_buffer (cursor.c:352) ==50906== by 0x40F13A0: output_cursor_set_xcursor_image (wlr_cursor.c:507) ==50906== by 0x40F1B28: cursor_output_cursor_update (wlr_cursor.c:630) ==50906== by 0x40F1C2A: cursor_update_outputs (wlr_cursor.c:657) ==50906== by 0x40F1CF9: wlr_cursor_set_xcursor (wlr_cursor.c:674) Fixes: 7963ba6a0deb5b696050d914ac395bca9c4c06b2 ("buffer: introduce wlr_buffer_finish()") (cherry picked from commit 16cb509a6e21c8d9d74f4dfa98c7df5f176720c5) --- render/allocator/shm.c | 1 + render/allocator/udmabuf.c | 1 + 2 files changed, 2 insertions(+) diff --git a/render/allocator/shm.c b/render/allocator/shm.c index 2622f99aa..b5be7d014 100644 --- a/render/allocator/shm.c +++ b/render/allocator/shm.c @@ -23,6 +23,7 @@ static struct wlr_shm_buffer *shm_buffer_from_buffer( static void buffer_destroy(struct wlr_buffer *wlr_buffer) { struct wlr_shm_buffer *buffer = shm_buffer_from_buffer(wlr_buffer); + wlr_buffer_finish(wlr_buffer); munmap(buffer->data, buffer->size); close(buffer->shm.fd); free(buffer); diff --git a/render/allocator/udmabuf.c b/render/allocator/udmabuf.c index e0b01b70a..8a7109aa5 100644 --- a/render/allocator/udmabuf.c +++ b/render/allocator/udmabuf.c @@ -31,6 +31,7 @@ static bool buffer_get_dmabuf(struct wlr_buffer *wlr_buffer, struct wlr_dmabuf_a static void buffer_destroy(struct wlr_buffer *wlr_buffer) { struct wlr_udmabuf_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); + wlr_buffer_finish(wlr_buffer); wlr_dmabuf_attributes_finish(&buffer->dmabuf); close(buffer->shm.fd); free(buffer); From b08af0962615ae8f54eef1fbdfe1cebbf2436839 Mon Sep 17 00:00:00 2001 From: liupeng Date: Wed, 17 Dec 2025 20:47:33 +0800 Subject: [PATCH 29/44] backend/session: respond to event hangup or error Signed-off-by: liupeng (cherry picked from commit 0ad8395ae67d4cbd32b7974246ab6c91bb217b0c) --- backend/session/session.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/backend/session/session.c b/backend/session/session.c index 48f4ab187..9a36dd953 100644 --- a/backend/session/session.c +++ b/backend/session/session.c @@ -36,6 +36,15 @@ static void handle_disable_seat(struct libseat *seat, void *data) { static int libseat_event(int fd, uint32_t mask, void *data) { struct wlr_session *session = data; + if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) { + if (mask & WL_EVENT_ERROR) { + wlr_log(WLR_ERROR, "Failed to wait for libseat event"); + } else { + wlr_log(WLR_INFO, "Failed to wait for libseat event"); + } + wlr_session_destroy(session); + return 0; + } if (libseat_dispatch(session->seat_handle, 0) == -1) { wlr_log_errno(WLR_ERROR, "Failed to dispatch libseat"); wlr_session_destroy(session); From b85823039488d35941d8e6344455dd6c90ab538a Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 18 Dec 2025 15:22:33 +0100 Subject: [PATCH 30/44] tinywl: fix duplicate object files passed to linker On BSD make, $> is an alias for $^. On both GNU and BSD make, $^ is supported. Specifying both resulted in duplicate object files passed to the linker: ld: error: duplicate symbol: main >>> defined at tinywl.c:887 >>> tinywl.o:(main) >>> defined at tinywl.c:887 >>> tinywl.o:(.text+0x0) cc: error: linker command failed with exit code 1 (use -v to see invocation) Only use $^ and remove $>. (cherry picked from commit 322291cdcfe1ea29fd6987f4922c90c5a2798d18) --- tinywl/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tinywl/Makefile b/tinywl/Makefile index a5cedfcc8..76c33bb31 100644 --- a/tinywl/Makefile +++ b/tinywl/Makefile @@ -19,7 +19,7 @@ xdg-shell-protocol.h: tinywl.o: tinywl.c xdg-shell-protocol.h $(CC) -c $< -g -Werror $(CFLAGS) -I. -DWLR_USE_UNSTABLE -o $@ tinywl: tinywl.o - $(CC) $^ $> -g -Werror $(CFLAGS) $(LDFLAGS) $(LIBS) -o $@ + $(CC) $^ -g -Werror $(CFLAGS) $(LDFLAGS) $(LIBS) -o $@ clean: rm -f tinywl tinywl.o xdg-shell-protocol.h From 11f5ba8bf22ea88327420b46992cd09d49412ede Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 19 Feb 2026 14:17:33 +0100 Subject: [PATCH 31/44] backend/drm: Close non-master drm fd on failure If we are not able to prepare the fd for non-master usage, close the fd before returning an error. (cherry picked from commit 1efb216c6d85ec0d311d8e8f8ce7cf0ac0f840ae) --- backend/drm/drm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index e9e4c6db2..054c24a4b 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -2085,6 +2085,7 @@ int wlr_drm_backend_get_non_master_fd(struct wlr_backend *backend) { if (drmIsMaster(fd) && drmDropMaster(fd) < 0) { wlr_log_errno(WLR_ERROR, "Failed to drop master"); + close(fd); return -1; } From 31dc115b9f73471b56fa6508b7cf864f5826458a Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 7 Feb 2026 17:48:16 -0800 Subject: [PATCH 32/44] backend/libinput: fix build with libinput 1.31 (cherry picked from commit c1452d88114710f5772662b1d8efb9c71edaa34c) --- backend/libinput/meson.build | 4 ++++ backend/libinput/switch.c | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/backend/libinput/meson.build b/backend/libinput/meson.build index c244eb77a..091b0e0eb 100644 --- a/backend/libinput/meson.build +++ b/backend/libinput/meson.build @@ -29,3 +29,7 @@ features += { 'libinput-backend': true } wlr_deps += libinput internal_config.set10('HAVE_LIBINPUT_BUSTYPE', libinput.version().version_compare('>=1.26.0')) +internal_config.set10( + 'HAVE_LIBINPUT_SWITCH_KEYPAD_SLIDE', + libinput.version().version_compare('>=1.30.901') +) diff --git a/backend/libinput/switch.c b/backend/libinput/switch.c index abeec86d7..9dde3c9cb 100644 --- a/backend/libinput/switch.c +++ b/backend/libinput/switch.c @@ -2,6 +2,7 @@ #include #include #include "backend/libinput.h" +#include "config.h" const struct wlr_switch_impl libinput_switch_impl = { .name = "libinput-switch", @@ -36,6 +37,10 @@ void handle_switch_toggle(struct libinput_event *event, case LIBINPUT_SWITCH_TABLET_MODE: wlr_event.switch_type = WLR_SWITCH_TYPE_TABLET_MODE; break; +#if HAVE_LIBINPUT_SWITCH_KEYPAD_SLIDE + case LIBINPUT_SWITCH_KEYPAD_SLIDE: + return; +#endif } switch (libinput_event_switch_get_switch_state(sevent)) { case LIBINPUT_SWITCH_STATE_OFF: From 29bba098bdcf30b5de692522c392dc59b38ed8d3 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 7 Feb 2026 17:48:16 -0800 Subject: [PATCH 33/44] backend/libinput: add support for LIBINPUT_SWITCH_KEYPAD_SLIDE (cherry picked from commit 3676ab4df0e7b31efd9c72f543fd8e6326af95cc) --- backend/libinput/switch.c | 3 ++- include/wlr/types/wlr_switch.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/libinput/switch.c b/backend/libinput/switch.c index 9dde3c9cb..a78d37abe 100644 --- a/backend/libinput/switch.c +++ b/backend/libinput/switch.c @@ -39,7 +39,8 @@ void handle_switch_toggle(struct libinput_event *event, break; #if HAVE_LIBINPUT_SWITCH_KEYPAD_SLIDE case LIBINPUT_SWITCH_KEYPAD_SLIDE: - return; + wlr_event.switch_type = WLR_SWITCH_TYPE_KEYPAD_SLIDE; + break; #endif } switch (libinput_event_switch_get_switch_state(sevent)) { diff --git a/include/wlr/types/wlr_switch.h b/include/wlr/types/wlr_switch.h index 641df1991..2b9e91266 100644 --- a/include/wlr/types/wlr_switch.h +++ b/include/wlr/types/wlr_switch.h @@ -36,6 +36,7 @@ struct wlr_switch { enum wlr_switch_type { WLR_SWITCH_TYPE_LID, WLR_SWITCH_TYPE_TABLET_MODE, + WLR_SWITCH_TYPE_KEYPAD_SLIDE, }; enum wlr_switch_state { From 0280a03136b22e0f995d9d1ec7412fbdc697b25e Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 12 Feb 2026 22:37:41 +0100 Subject: [PATCH 34/44] backend/libinput: guard against new enum entries When libinput introduces new enum entries, we'd abort or send bogus events to the compositor. Instead, log a message and ignore the event. Keep all enums without a default case so that the compiler warns when we're missing a case. (cherry picked from commit 884d29e5f31b5dec41fdbaa7876458e1b88a28be) --- backend/libinput/events.c | 12 ++++ backend/libinput/keyboard.c | 23 +++++--- backend/libinput/pointer.c | 60 ++++++++++++------- backend/libinput/switch.c | 57 +++++++++++------- backend/libinput/tablet_pad.c | 12 ++-- backend/libinput/tablet_tool.c | 102 ++++++++++++++++++++++----------- include/backend/libinput.h | 2 + 7 files changed, 179 insertions(+), 89 deletions(-) diff --git a/backend/libinput/events.c b/backend/libinput/events.c index 6cfe34e08..9be5def87 100644 --- a/backend/libinput/events.c +++ b/backend/libinput/events.c @@ -249,3 +249,15 @@ void handle_libinput_event(struct wlr_libinput_backend *backend, break; } } + +bool button_state_from_libinput(enum libinput_button_state state, enum wlr_button_state *out) { + switch (state) { + case LIBINPUT_BUTTON_STATE_RELEASED: + *out = WLR_BUTTON_RELEASED; + return true; + case LIBINPUT_BUTTON_STATE_PRESSED: + *out = WLR_BUTTON_PRESSED; + return true; + } + return false; +} diff --git a/backend/libinput/keyboard.c b/backend/libinput/keyboard.c index 7518453e6..a99461040 100644 --- a/backend/libinput/keyboard.c +++ b/backend/libinput/keyboard.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "backend/libinput.h" struct wlr_libinput_input_device *device_from_keyboard( @@ -30,6 +31,18 @@ void init_device_keyboard(struct wlr_libinput_input_device *dev) { libinput_device_led_update(dev->handle, 0); } +static bool key_state_from_libinput(enum libinput_key_state state, enum wl_keyboard_key_state *out) { + switch (state) { + case LIBINPUT_KEY_STATE_RELEASED: + *out = WL_KEYBOARD_KEY_STATE_RELEASED; + return true; + case LIBINPUT_KEY_STATE_PRESSED: + *out = WL_KEYBOARD_KEY_STATE_PRESSED; + return true; + } + return false; +} + void handle_keyboard_key(struct libinput_event *event, struct wlr_keyboard *kb) { struct libinput_event_keyboard *kbevent = @@ -39,13 +52,9 @@ void handle_keyboard_key(struct libinput_event *event, .keycode = libinput_event_keyboard_get_key(kbevent), .update_state = true, }; - switch (libinput_event_keyboard_get_key_state(kbevent)) { - case LIBINPUT_KEY_STATE_RELEASED: - wlr_event.state = WL_KEYBOARD_KEY_STATE_RELEASED; - break; - case LIBINPUT_KEY_STATE_PRESSED: - wlr_event.state = WL_KEYBOARD_KEY_STATE_PRESSED; - break; + if (!key_state_from_libinput(libinput_event_keyboard_get_key_state(kbevent), &wlr_event.state)) { + wlr_log(WLR_DEBUG, "Unhandled libinput key state"); + return; } wlr_keyboard_notify_key(kb, &wlr_event); } diff --git a/backend/libinput/pointer.c b/backend/libinput/pointer.c index 9b9996780..d09164a71 100644 --- a/backend/libinput/pointer.c +++ b/backend/libinput/pointer.c @@ -1,6 +1,7 @@ #include #include #include +#include #include "backend/libinput.h" const struct wlr_pointer_impl libinput_pointer_impl = { @@ -52,6 +53,38 @@ void handle_pointer_motion_abs(struct libinput_event *event, wl_signal_emit_mutable(&pointer->events.frame, pointer); } +static bool pointer_button_state_from_libinput(enum libinput_button_state state, + enum wl_pointer_button_state *out) { + switch (state) { + case LIBINPUT_BUTTON_STATE_PRESSED: + *out = WL_POINTER_BUTTON_STATE_PRESSED; + return true; + case LIBINPUT_BUTTON_STATE_RELEASED: + *out = WL_POINTER_BUTTON_STATE_RELEASED; + return true; + } + return false; +} + +static bool axis_source_from_libinput(enum libinput_pointer_axis_source source, + enum wl_pointer_axis_source *out) { + switch (source) { + case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL: + *out = WL_POINTER_AXIS_SOURCE_WHEEL; + return true; + case LIBINPUT_POINTER_AXIS_SOURCE_FINGER: + *out = WL_POINTER_AXIS_SOURCE_FINGER; + return true; + case LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS: + *out = WL_POINTER_AXIS_SOURCE_CONTINUOUS; + return true; + case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL_TILT: + *out = WL_POINTER_AXIS_SOURCE_WHEEL_TILT; + return true; + } + return false; +} + void handle_pointer_button(struct libinput_event *event, struct wlr_pointer *pointer) { struct libinput_event_pointer *pevent = @@ -61,13 +94,10 @@ void handle_pointer_button(struct libinput_event *event, .time_msec = usec_to_msec(libinput_event_pointer_get_time_usec(pevent)), .button = libinput_event_pointer_get_button(pevent), }; - switch (libinput_event_pointer_get_button_state(pevent)) { - case LIBINPUT_BUTTON_STATE_PRESSED: - wlr_event.state = WL_POINTER_BUTTON_STATE_PRESSED; - break; - case LIBINPUT_BUTTON_STATE_RELEASED: - wlr_event.state = WL_POINTER_BUTTON_STATE_RELEASED; - break; + if (!pointer_button_state_from_libinput(libinput_event_pointer_get_button_state(pevent), + &wlr_event.state)) { + wlr_log(WLR_DEBUG, "Unhandled libinput button state"); + return; } wlr_pointer_notify_button(pointer, &wlr_event); wl_signal_emit_mutable(&pointer->events.frame, pointer); @@ -81,19 +111,9 @@ void handle_pointer_axis(struct libinput_event *event, .pointer = pointer, .time_msec = usec_to_msec(libinput_event_pointer_get_time_usec(pevent)), }; - switch (libinput_event_pointer_get_axis_source(pevent)) { - case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL: - wlr_event.source = WL_POINTER_AXIS_SOURCE_WHEEL; - break; - case LIBINPUT_POINTER_AXIS_SOURCE_FINGER: - wlr_event.source = WL_POINTER_AXIS_SOURCE_FINGER; - break; - case LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS: - wlr_event.source = WL_POINTER_AXIS_SOURCE_CONTINUOUS; - break; - case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL_TILT: - wlr_event.source = WL_POINTER_AXIS_SOURCE_WHEEL_TILT; - break; + if (!axis_source_from_libinput(libinput_event_pointer_get_axis_source(pevent), &wlr_event.source)) { + wlr_log(WLR_DEBUG, "Unhandled libinput pointer axis source"); + return; } const enum libinput_pointer_axis axes[] = { LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, diff --git a/backend/libinput/switch.c b/backend/libinput/switch.c index a78d37abe..2055d88f5 100644 --- a/backend/libinput/switch.c +++ b/backend/libinput/switch.c @@ -1,6 +1,7 @@ #include #include #include +#include #include "backend/libinput.h" #include "config.h" @@ -23,33 +24,49 @@ struct wlr_libinput_input_device *device_from_switch( return dev; } +static bool switch_type_from_libinput(enum libinput_switch type, enum wlr_switch_type *out) { + switch (type) { + case LIBINPUT_SWITCH_LID: + *out = WLR_SWITCH_TYPE_LID; + return true; + case LIBINPUT_SWITCH_TABLET_MODE: + *out = WLR_SWITCH_TYPE_TABLET_MODE; + return true; +#if HAVE_LIBINPUT_SWITCH_KEYPAD_SLIDE + case LIBINPUT_SWITCH_KEYPAD_SLIDE: + *out = WLR_SWITCH_TYPE_KEYPAD_SLIDE; + return true; +#endif + } + return false; +} + +static bool switch_state_from_libinput(enum libinput_switch_state state, enum wlr_switch_state *out) { + switch (state) { + case LIBINPUT_SWITCH_STATE_OFF: + *out = WLR_SWITCH_STATE_OFF; + return true; + case LIBINPUT_SWITCH_STATE_ON: + *out = WLR_SWITCH_STATE_ON; + return true; + } + return false; +} + void handle_switch_toggle(struct libinput_event *event, struct wlr_switch *wlr_switch) { struct libinput_event_switch *sevent = - libinput_event_get_switch_event (event); + libinput_event_get_switch_event(event); struct wlr_switch_toggle_event wlr_event = { .time_msec = usec_to_msec(libinput_event_switch_get_time_usec(sevent)), }; - switch (libinput_event_switch_get_switch(sevent)) { - case LIBINPUT_SWITCH_LID: - wlr_event.switch_type = WLR_SWITCH_TYPE_LID; - break; - case LIBINPUT_SWITCH_TABLET_MODE: - wlr_event.switch_type = WLR_SWITCH_TYPE_TABLET_MODE; - break; -#if HAVE_LIBINPUT_SWITCH_KEYPAD_SLIDE - case LIBINPUT_SWITCH_KEYPAD_SLIDE: - wlr_event.switch_type = WLR_SWITCH_TYPE_KEYPAD_SLIDE; - break; -#endif + if (!switch_type_from_libinput(libinput_event_switch_get_switch(sevent), &wlr_event.switch_type)) { + wlr_log(WLR_DEBUG, "Unhandled libinput switch type"); + return; } - switch (libinput_event_switch_get_switch_state(sevent)) { - case LIBINPUT_SWITCH_STATE_OFF: - wlr_event.switch_state = WLR_SWITCH_STATE_OFF; - break; - case LIBINPUT_SWITCH_STATE_ON: - wlr_event.switch_state = WLR_SWITCH_STATE_ON; - break; + if (!switch_state_from_libinput(libinput_event_switch_get_switch_state(sevent), &wlr_event.switch_state)) { + wlr_log(WLR_DEBUG, "Unhandled libinput switch state"); + return; } wl_signal_emit_mutable(&wlr_switch->events.toggle, &wlr_event); } diff --git a/backend/libinput/tablet_pad.c b/backend/libinput/tablet_pad.c index 2fbfb6a6c..9d090198a 100644 --- a/backend/libinput/tablet_pad.c +++ b/backend/libinput/tablet_pad.c @@ -148,13 +148,9 @@ void handle_tablet_pad_button(struct libinput_event *event, .group = libinput_tablet_pad_mode_group_get_index( libinput_event_tablet_pad_get_mode_group(pevent)), }; - switch (libinput_event_tablet_pad_get_button_state(pevent)) { - case LIBINPUT_BUTTON_STATE_PRESSED: - wlr_event.state = WLR_BUTTON_PRESSED; - break; - case LIBINPUT_BUTTON_STATE_RELEASED: - wlr_event.state = WLR_BUTTON_RELEASED; - break; + if (!button_state_from_libinput(libinput_event_tablet_pad_get_button_state(pevent), &wlr_event.state)) { + wlr_log(WLR_DEBUG, "Unhandled libinput button state"); + return; } wl_signal_emit_mutable(&tablet_pad->events.button, &wlr_event); } @@ -168,6 +164,7 @@ void handle_tablet_pad_ring(struct libinput_event *event, .ring = libinput_event_tablet_pad_get_ring_number(pevent), .position = libinput_event_tablet_pad_get_ring_position(pevent), .mode = libinput_event_tablet_pad_get_mode(pevent), + .source = WLR_TABLET_PAD_RING_SOURCE_UNKNOWN, }; switch (libinput_event_tablet_pad_get_ring_source(pevent)) { case LIBINPUT_TABLET_PAD_RING_SOURCE_UNKNOWN: @@ -189,6 +186,7 @@ void handle_tablet_pad_strip(struct libinput_event *event, .strip = libinput_event_tablet_pad_get_strip_number(pevent), .position = libinput_event_tablet_pad_get_strip_position(pevent), .mode = libinput_event_tablet_pad_get_mode(pevent), + .source = WLR_TABLET_PAD_STRIP_SOURCE_UNKNOWN, }; switch (libinput_event_tablet_pad_get_strip_source(pevent)) { case LIBINPUT_TABLET_PAD_STRIP_SOURCE_UNKNOWN: diff --git a/backend/libinput/tablet_tool.c b/backend/libinput/tablet_tool.c index d43c6cd0a..86d2353cc 100644 --- a/backend/libinput/tablet_tool.c +++ b/backend/libinput/tablet_tool.c @@ -68,27 +68,61 @@ struct wlr_libinput_input_device *device_from_tablet( return dev; } -static enum wlr_tablet_tool_type wlr_type_from_libinput_type( - enum libinput_tablet_tool_type value) { - switch (value) { +static bool type_from_libinput(enum libinput_tablet_tool_type type, + enum wlr_tablet_tool_type *out) { + switch (type) { case LIBINPUT_TABLET_TOOL_TYPE_PEN: - return WLR_TABLET_TOOL_TYPE_PEN; + *out = WLR_TABLET_TOOL_TYPE_PEN; + return true; case LIBINPUT_TABLET_TOOL_TYPE_ERASER: - return WLR_TABLET_TOOL_TYPE_ERASER; + *out = WLR_TABLET_TOOL_TYPE_ERASER; + return true; case LIBINPUT_TABLET_TOOL_TYPE_BRUSH: - return WLR_TABLET_TOOL_TYPE_BRUSH; + *out = WLR_TABLET_TOOL_TYPE_BRUSH; + return true; case LIBINPUT_TABLET_TOOL_TYPE_PENCIL: - return WLR_TABLET_TOOL_TYPE_PENCIL; + *out = WLR_TABLET_TOOL_TYPE_PENCIL; + return true; case LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH: - return WLR_TABLET_TOOL_TYPE_AIRBRUSH; + *out = WLR_TABLET_TOOL_TYPE_AIRBRUSH; + return true; case LIBINPUT_TABLET_TOOL_TYPE_MOUSE: - return WLR_TABLET_TOOL_TYPE_MOUSE; + *out = WLR_TABLET_TOOL_TYPE_MOUSE; + return true; case LIBINPUT_TABLET_TOOL_TYPE_LENS: - return WLR_TABLET_TOOL_TYPE_LENS; + *out = WLR_TABLET_TOOL_TYPE_LENS; + return true; case LIBINPUT_TABLET_TOOL_TYPE_TOTEM: - return WLR_TABLET_TOOL_TYPE_TOTEM; + *out = WLR_TABLET_TOOL_TYPE_TOTEM; + return true; } - abort(); // unreachable + return false; +} + +static bool proximity_state_from_libinput(enum libinput_tablet_tool_proximity_state state, + enum wlr_tablet_tool_proximity_state *out) { + switch (state) { + case LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT: + *out = WLR_TABLET_TOOL_PROXIMITY_OUT; + return true; + case LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN: + *out = WLR_TABLET_TOOL_PROXIMITY_IN; + return true; + } + return false; +} + +static bool tip_state_from_libinput(enum libinput_tablet_tool_tip_state state, + enum wlr_tablet_tool_tip_state *out) { + switch (state) { + case LIBINPUT_TABLET_TOOL_TIP_UP: + *out = WLR_TABLET_TOOL_TIP_UP; + return true; + case LIBINPUT_TABLET_TOOL_TIP_DOWN: + *out = WLR_TABLET_TOOL_TIP_DOWN; + return true; + } + return false; } static struct tablet_tool *get_tablet_tool( @@ -100,14 +134,19 @@ static struct tablet_tool *get_tablet_tool( return tool; } + enum wlr_tablet_tool_type type; + if (!type_from_libinput(libinput_tablet_tool_get_type(libinput_tool), &type)) { + wlr_log(WLR_DEBUG, "Unhandled libinput tablet tool type"); + return NULL; + } + tool = calloc(1, sizeof(*tool)); if (tool == NULL) { wlr_log_errno(WLR_ERROR, "failed to allocate wlr_libinput_tablet_tool"); return NULL; } - tool->wlr_tool.type = wlr_type_from_libinput_type( - libinput_tablet_tool_get_type(libinput_tool)); + tool->wlr_tool.type = type; tool->wlr_tool.hardware_serial = libinput_tablet_tool_get_serial(libinput_tool); tool->wlr_tool.hardware_wacom = @@ -199,14 +238,12 @@ void handle_tablet_tool_proximity(struct libinput_event *event, .y = libinput_event_tablet_tool_get_y_transformed(tevent, 1), }; - switch (libinput_event_tablet_tool_get_proximity_state(tevent)) { - case LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT: - wlr_event.state = WLR_TABLET_TOOL_PROXIMITY_OUT; - break; - case LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN: - wlr_event.state = WLR_TABLET_TOOL_PROXIMITY_IN; - break; + if (!proximity_state_from_libinput(libinput_event_tablet_tool_get_proximity_state(tevent), + &wlr_event.state)) { + wlr_log(WLR_DEBUG, "Unhandled libinput tablet tool proximity state"); + return; } + wl_signal_emit_mutable(&wlr_tablet->events.proximity, &wlr_event); if (libinput_event_tablet_tool_get_proximity_state(tevent) == @@ -241,14 +278,11 @@ void handle_tablet_tool_tip(struct libinput_event *event, .y = libinput_event_tablet_tool_get_y_transformed(tevent, 1), }; - switch (libinput_event_tablet_tool_get_tip_state(tevent)) { - case LIBINPUT_TABLET_TOOL_TIP_UP: - wlr_event.state = WLR_TABLET_TOOL_TIP_UP; - break; - case LIBINPUT_TABLET_TOOL_TIP_DOWN: - wlr_event.state = WLR_TABLET_TOOL_TIP_DOWN; - break; + if (!tip_state_from_libinput(libinput_event_tablet_tool_get_tip_state(tevent), &wlr_event.state)) { + wlr_log(WLR_DEBUG, "Unhandled libinput tablet tool tip state"); + return; } + wl_signal_emit_mutable(&wlr_tablet->events.tip, &wlr_event); } @@ -267,13 +301,11 @@ void handle_tablet_tool_button(struct libinput_event *event, .time_msec = usec_to_msec(libinput_event_tablet_tool_get_time_usec(tevent)), .button = libinput_event_tablet_tool_get_button(tevent), }; - switch (libinput_event_tablet_tool_get_button_state(tevent)) { - case LIBINPUT_BUTTON_STATE_RELEASED: - wlr_event.state = WLR_BUTTON_RELEASED; - break; - case LIBINPUT_BUTTON_STATE_PRESSED: - wlr_event.state = WLR_BUTTON_PRESSED; - break; + + if (!button_state_from_libinput(libinput_event_tablet_tool_get_button_state(tevent), &wlr_event.state)) { + wlr_log(WLR_DEBUG, "Unhandled libinput button state"); + return; } + wl_signal_emit_mutable(&wlr_tablet->events.button, &wlr_event); } diff --git a/include/backend/libinput.h b/include/backend/libinput.h index 874e9aa1f..e7123884e 100644 --- a/include/backend/libinput.h +++ b/include/backend/libinput.h @@ -132,4 +132,6 @@ void handle_tablet_pad_ring(struct libinput_event *event, void handle_tablet_pad_strip(struct libinput_event *event, struct wlr_tablet_pad *tablet_pad); +bool button_state_from_libinput(enum libinput_button_state state, enum wlr_button_state *out); + #endif From 0f9a1f144334965e0293f7f6dafad5425a18c85e Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Thu, 5 Mar 2026 10:23:06 +0000 Subject: [PATCH 35/44] image_capture_source/output: Update constraints on enable Without observing the enable event, clients receive no pixel formats and buffer dimensions are reported as 0 after an output has been re-enabled. (cherry picked from commit 3336d28813698eb6fba908a4dbcf38b79ed2e3db) --- types/ext_image_capture_source_v1/output.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/types/ext_image_capture_source_v1/output.c b/types/ext_image_capture_source_v1/output.c index e661aad3a..66ba2cdca 100644 --- a/types/ext_image_capture_source_v1/output.c +++ b/types/ext_image_capture_source_v1/output.c @@ -107,6 +107,10 @@ static const struct wlr_ext_image_capture_source_v1_interface output_source_impl static void source_update_buffer_constraints(struct wlr_ext_output_image_capture_source_v1 *source) { struct wlr_output *output = source->output; + if (!output->enabled) { + return; + } + if (!wlr_output_configure_primary_swapchain(output, NULL, &output->swapchain)) { return; } @@ -120,7 +124,8 @@ static void source_handle_output_commit(struct wl_listener *listener, struct wlr_ext_output_image_capture_source_v1 *source = wl_container_of(listener, source, output_commit); struct wlr_output_event_commit *event = data; - if (event->state->committed & (WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_RENDER_FORMAT)) { + if (event->state->committed & (WLR_OUTPUT_STATE_MODE | + WLR_OUTPUT_STATE_RENDER_FORMAT | WLR_OUTPUT_STATE_ENABLED)) { source_update_buffer_constraints(source); } From d834de6167140211058cdea4ddeacb79bab0a727 Mon Sep 17 00:00:00 2001 From: Wang Yu Date: Tue, 3 Mar 2026 14:37:42 +0800 Subject: [PATCH 36/44] xwayland: fix memory leak on pipe() failure When pipe() fails in xwm_selection_send_data(), the function returns without cleaning up the allocated transfer structure and initialized wl_array. This causes a memory leak. Add wl_array_release() and free() to clean up resources when pipe() fails. Signed-off-by: Wang Yu (cherry picked from commit a55b85e2e1138f9534890e4dc6a224ec2efb9d4d) --- xwayland/selection/outgoing.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/xwayland/selection/outgoing.c b/xwayland/selection/outgoing.c index 795a4768b..6216abbb4 100644 --- a/xwayland/selection/outgoing.c +++ b/xwayland/selection/outgoing.c @@ -283,6 +283,8 @@ static bool xwm_selection_send_data(struct wlr_xwm_selection *selection, int p[2]; if (pipe(p) == -1) { wlr_log_errno(WLR_ERROR, "pipe() failed"); + wl_array_release(&transfer->source_data); + free(transfer); return false; } From 4080e2f6271118ef02c654f421b75b384b0d0db9 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 14 Feb 2026 17:53:54 +0100 Subject: [PATCH 37/44] render/vulkan: introduce buffer_import_sync_file() Will be used in two spots in a following commit. (cherry picked from commit a6e5807e8640c038f0b3d7b0a9e01826eb81a101) --- render/vulkan/renderer.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 1a696734a..b9de7f747 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -992,6 +992,23 @@ bool vulkan_sync_foreign_texture(struct wlr_vk_texture *texture, return true; } +static bool buffer_import_sync_file(struct wlr_buffer *buffer, uint32_t flags, int sync_file_fd) { + struct wlr_dmabuf_attributes dmabuf = {0}; + if (!wlr_buffer_get_dmabuf(buffer, &dmabuf)) { + wlr_log(WLR_ERROR, "wlr_buffer_get_dmabuf() failed"); + return false; + } + + for (int i = 0; i < dmabuf.n_planes; i++) { + if (!dmabuf_import_sync_file(dmabuf.fd[i], flags, + sync_file_fd)) { + return false; + } + } + + return true; +} + bool vulkan_sync_render_buffer(struct wlr_vk_renderer *renderer, struct wlr_vk_render_buffer *render_buffer, struct wlr_vk_command_buffer *cb, struct wlr_drm_syncobj_timeline *signal_timeline, uint64_t signal_point) { @@ -1025,18 +1042,9 @@ bool vulkan_sync_render_buffer(struct wlr_vk_renderer *renderer, goto out; } } else { - struct wlr_dmabuf_attributes dmabuf = {0}; - if (!wlr_buffer_get_dmabuf(render_buffer->wlr_buffer, &dmabuf)) { - wlr_log(WLR_ERROR, "wlr_buffer_get_dmabuf failed"); + if (!buffer_import_sync_file(render_buffer->wlr_buffer, DMA_BUF_SYNC_WRITE, sync_file_fd)) { goto out; } - - for (int i = 0; i < dmabuf.n_planes; i++) { - if (!dmabuf_import_sync_file(dmabuf.fd[i], DMA_BUF_SYNC_WRITE, - sync_file_fd)) { - goto out; - } - } } ok = true; From 004806fae26a4aa291c8877a473eb43e635e9bbd Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 14 Feb 2026 17:55:52 +0100 Subject: [PATCH 38/44] render/vulkan: take render pass in vulkan_sync_render_buffer() We'll need to grab textures from there in the next commit. Also rename it to better reflect what it does: synchronize release fences after a render pass has been submitted. (cherry picked from commit 73bbad8433a916ca342f19975b7eb2aaf3a8fc6f) --- include/render/vulkan.h | 5 ++--- render/vulkan/pass.c | 3 +-- render/vulkan/renderer.c | 16 ++++++++-------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/include/render/vulkan.h b/include/render/vulkan.h index abeb11cc5..4f078dcb4 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -433,9 +433,8 @@ void vulkan_reset_command_buffer(struct wlr_vk_command_buffer *cb); bool vulkan_wait_command_buffer(struct wlr_vk_command_buffer *cb, struct wlr_vk_renderer *renderer); -bool vulkan_sync_render_buffer(struct wlr_vk_renderer *renderer, - struct wlr_vk_render_buffer *render_buffer, struct wlr_vk_command_buffer *cb, - struct wlr_drm_syncobj_timeline *signal_timeline, uint64_t signal_point); +bool vulkan_sync_render_pass_release(struct wlr_vk_renderer *renderer, + struct wlr_vk_render_pass *pass); bool vulkan_sync_foreign_texture(struct wlr_vk_texture *texture, int sync_file_fds[static WLR_DMABUF_MAX_PLANES]); diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 3f662b203..ef6dd8f0d 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -538,8 +538,7 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { wl_list_insert(&stage_cb->stage_buffers, &stage_buf->link); } - if (!vulkan_sync_render_buffer(renderer, render_buffer, render_cb, - pass->signal_timeline, pass->signal_point)) { + if (!vulkan_sync_render_pass_release(renderer, pass)) { wlr_log(WLR_ERROR, "Failed to sync render buffer"); } diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index b9de7f747..168668e82 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -1009,12 +1009,12 @@ static bool buffer_import_sync_file(struct wlr_buffer *buffer, uint32_t flags, i return true; } -bool vulkan_sync_render_buffer(struct wlr_vk_renderer *renderer, - struct wlr_vk_render_buffer *render_buffer, struct wlr_vk_command_buffer *cb, - struct wlr_drm_syncobj_timeline *signal_timeline, uint64_t signal_point) { +bool vulkan_sync_render_pass_release(struct wlr_vk_renderer *renderer, + struct wlr_vk_render_pass *pass) { VkResult res; + struct wlr_vk_command_buffer *cb = pass->command_buffer; - if (!renderer->dev->implicit_sync_interop && signal_timeline == NULL) { + if (!renderer->dev->implicit_sync_interop && pass->signal_timeline == NULL) { // We have no choice but to block here sadly return vulkan_wait_command_buffer(cb, renderer); } @@ -1036,13 +1036,13 @@ bool vulkan_sync_render_buffer(struct wlr_vk_renderer *renderer, } bool ok = false; - if (signal_timeline != NULL) { - if (!wlr_drm_syncobj_timeline_import_sync_file(signal_timeline, - signal_point, sync_file_fd)) { + if (pass->signal_timeline != NULL) { + if (!wlr_drm_syncobj_timeline_import_sync_file(pass->signal_timeline, + pass->signal_point, sync_file_fd)) { goto out; } } else { - if (!buffer_import_sync_file(render_buffer->wlr_buffer, DMA_BUF_SYNC_WRITE, sync_file_fd)) { + if (!buffer_import_sync_file(pass->render_buffer->wlr_buffer, DMA_BUF_SYNC_WRITE, sync_file_fd)) { goto out; } } From f7e7d8f18372672ef552fb6b0a11deab2145e4c3 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 14 Feb 2026 17:57:42 +0100 Subject: [PATCH 39/44] render/vulkan: fix missing DMA-BUF implicit read fence for textures When we're reading from a DMA-BUF texture using implicit sync, we need to (1) wait for any writer to be done and (2) prevent any writers from mutating the texture while we're still reading. We were doing (1) but not (2). Fix this by calling dmabuf_import_sync_file() with DMA_BUF_SYNC_READ for all DMA-BUF textures we've used in the render pass. (cherry picked from commit 43b37e34d662ca893ebaae6e813634ee216c352f) --- render/vulkan/renderer.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 168668e82..d9d8876cd 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -1045,6 +1045,13 @@ bool vulkan_sync_render_pass_release(struct wlr_vk_renderer *renderer, if (!buffer_import_sync_file(pass->render_buffer->wlr_buffer, DMA_BUF_SYNC_WRITE, sync_file_fd)) { goto out; } + + struct wlr_vk_render_pass_texture *pass_texture; + wl_array_for_each(pass_texture, &pass->textures) { + if (!buffer_import_sync_file(pass_texture->texture->buffer, DMA_BUF_SYNC_READ, sync_file_fd)) { + goto out; + } + } } ok = true; From a34411c2feef438076b0ffd0547c9f63ea9800b0 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 14 Feb 2026 18:04:58 +0100 Subject: [PATCH 40/44] render/vulkan: introduce buffer_export_sync_file() Same as buffer_import_sync_file(), but for the export side. (cherry picked from commit ff4ce121796081dec656fc4106dbf6b6a043fa77) --- render/vulkan/renderer.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index d9d8876cd..35c7185ba 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -947,13 +947,11 @@ static struct wlr_vk_render_buffer *get_render_buffer( return buffer; } -bool vulkan_sync_foreign_texture(struct wlr_vk_texture *texture, - int sync_file_fds[static WLR_DMABUF_MAX_PLANES]) { - struct wlr_vk_renderer *renderer = texture->renderer; - +static bool buffer_export_sync_file(struct wlr_vk_renderer *renderer, struct wlr_buffer *buffer, + uint32_t flags, int sync_file_fds[static WLR_DMABUF_MAX_PLANES]) { struct wlr_dmabuf_attributes dmabuf = {0}; - if (!wlr_buffer_get_dmabuf(texture->buffer, &dmabuf)) { - wlr_log(WLR_ERROR, "Failed to get texture DMA-BUF"); + if (!wlr_buffer_get_dmabuf(buffer, &dmabuf)) { + wlr_log(WLR_ERROR, "wlr_buffer_get_dmabuf() failed"); return false; } @@ -963,7 +961,7 @@ bool vulkan_sync_foreign_texture(struct wlr_vk_texture *texture, for (int i = 0; i < dmabuf.n_planes; i++) { struct pollfd pollfd = { .fd = dmabuf.fd[i], - .events = POLLIN, + .events = (flags & DMA_BUF_SYNC_WRITE) ? POLLOUT : POLLIN, }; int timeout_ms = 1000; int ret = poll(&pollfd, 1, timeout_ms); @@ -980,7 +978,7 @@ bool vulkan_sync_foreign_texture(struct wlr_vk_texture *texture, } for (int i = 0; i < dmabuf.n_planes; i++) { - int sync_file_fd = dmabuf_export_sync_file(dmabuf.fd[i], DMA_BUF_SYNC_READ); + int sync_file_fd = dmabuf_export_sync_file(dmabuf.fd[i], flags); if (sync_file_fd < 0) { wlr_log(WLR_ERROR, "Failed to extract DMA-BUF fence"); return false; @@ -992,6 +990,11 @@ bool vulkan_sync_foreign_texture(struct wlr_vk_texture *texture, return true; } +bool vulkan_sync_foreign_texture(struct wlr_vk_texture *texture, + int sync_file_fds[static WLR_DMABUF_MAX_PLANES]) { + return buffer_export_sync_file(texture->renderer, texture->buffer, DMA_BUF_SYNC_READ, sync_file_fds); +} + static bool buffer_import_sync_file(struct wlr_buffer *buffer, uint32_t flags, int sync_file_fd) { struct wlr_dmabuf_attributes dmabuf = {0}; if (!wlr_buffer_get_dmabuf(buffer, &dmabuf)) { From 6fee18f373218d827a2df33b1c93b71df202ee41 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 14 Feb 2026 18:06:40 +0100 Subject: [PATCH 41/44] render/vulkan: add "acquire" to vulkan_sync_foreign_texture() Makes it more obvious that this is about the acquire side, not the release side. (cherry picked from commit 8c8d6363a19e04054071f6983423c4eb760bf86b) --- include/render/vulkan.h | 2 +- render/vulkan/pass.c | 2 +- render/vulkan/renderer.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/render/vulkan.h b/include/render/vulkan.h index 4f078dcb4..f2a28aa3c 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -435,7 +435,7 @@ bool vulkan_wait_command_buffer(struct wlr_vk_command_buffer *cb, bool vulkan_sync_render_pass_release(struct wlr_vk_renderer *renderer, struct wlr_vk_render_pass *pass); -bool vulkan_sync_foreign_texture(struct wlr_vk_texture *texture, +bool vulkan_sync_foreign_texture_acquire(struct wlr_vk_texture *texture, int sync_file_fds[static WLR_DMABUF_MAX_PLANES]); bool vulkan_read_pixels(struct wlr_vk_renderer *vk_renderer, diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index ef6dd8f0d..1e1fadbee 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -314,7 +314,7 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { sync_file_fds[0] = sync_file_fd; } else { struct wlr_vk_texture *texture = pass_texture->texture; - if (!vulkan_sync_foreign_texture(texture, sync_file_fds)) { + if (!vulkan_sync_foreign_texture_acquire(texture, sync_file_fds)) { wlr_log(WLR_ERROR, "Failed to wait for foreign texture DMA-BUF fence"); continue; } diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 35c7185ba..2fc9e68cf 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -990,7 +990,7 @@ static bool buffer_export_sync_file(struct wlr_vk_renderer *renderer, struct wlr return true; } -bool vulkan_sync_foreign_texture(struct wlr_vk_texture *texture, +bool vulkan_sync_foreign_texture_acquire(struct wlr_vk_texture *texture, int sync_file_fds[static WLR_DMABUF_MAX_PLANES]) { return buffer_export_sync_file(texture->renderer, texture->buffer, DMA_BUF_SYNC_READ, sync_file_fds); } From 8c4a74717f0a1e67cb57717ebc69875917d1fdf7 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 14 Feb 2026 18:17:32 +0100 Subject: [PATCH 42/44] render/vulkan: fix missing DMA-BUF implicit write fence for render buffer Same as previous commit for the read side, but this one waits for all readers to be done before starting to write. (cherry picked from commit 2367d78c3c1de2428cbbf9444d6eff632698baf6) --- include/render/vulkan.h | 2 ++ render/vulkan/pass.c | 40 +++++++++++++++++++++++++++++++++++++++- render/vulkan/renderer.c | 6 ++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/include/render/vulkan.h b/include/render/vulkan.h index f2a28aa3c..5bcf001f4 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -437,6 +437,8 @@ bool vulkan_sync_render_pass_release(struct wlr_vk_renderer *renderer, struct wlr_vk_render_pass *pass); bool vulkan_sync_foreign_texture_acquire(struct wlr_vk_texture *texture, int sync_file_fds[static WLR_DMABUF_MAX_PLANES]); +bool vulkan_sync_render_buffer_acquire(struct wlr_vk_render_buffer *render_buffer, + int sync_file_fds[static WLR_DMABUF_MAX_PLANES]); bool vulkan_read_pixels(struct wlr_vk_renderer *vk_renderer, VkFormat src_format, VkImage src_image, diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 1e1fadbee..6bb6392eb 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -141,6 +141,40 @@ static VkSemaphore render_pass_wait_sync_file(struct wlr_vk_render_pass *pass, return *sem_ptr; } +static bool render_pass_wait_render_buffer(struct wlr_vk_render_pass *pass, + VkSemaphoreSubmitInfoKHR *render_wait, uint32_t *render_wait_len_ptr) { + int sync_file_fds[WLR_DMABUF_MAX_PLANES]; + for (size_t i = 0; i < WLR_DMABUF_MAX_PLANES; i++) { + sync_file_fds[i] = -1; + } + + if (!vulkan_sync_render_buffer_acquire(pass->render_buffer, sync_file_fds)) { + return false; + } + + for (size_t i = 0; i < WLR_DMABUF_MAX_PLANES; i++) { + if (sync_file_fds[i] < 0) { + continue; + } + + VkSemaphore sem = render_pass_wait_sync_file(pass, *render_wait_len_ptr, sync_file_fds[i]); + if (sem == VK_NULL_HANDLE) { + close(sync_file_fds[i]); + continue; + } + + render_wait[*render_wait_len_ptr] = (VkSemaphoreSubmitInfoKHR){ + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO_KHR, + .semaphore = sem, + .stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT_KHR, + }; + + (*render_wait_len_ptr)++; + } + + return true; +} + static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { struct wlr_vk_render_pass *pass = get_render_pass(wlr_pass); struct wlr_vk_renderer *renderer = pass->renderer; @@ -236,7 +270,7 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { vkCmdEndRenderPass(render_cb->vk); size_t pass_textures_len = pass->textures.size / sizeof(struct wlr_vk_render_pass_texture); - size_t render_wait_cap = pass_textures_len * WLR_DMABUF_MAX_PLANES; + size_t render_wait_cap = (1 + pass_textures_len) * WLR_DMABUF_MAX_PLANES; render_wait = calloc(render_wait_cap, sizeof(*render_wait)); if (render_wait == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); @@ -341,6 +375,10 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { } } + if (!render_pass_wait_render_buffer(pass, render_wait, &render_wait_len)) { + wlr_log(WLR_ERROR, "Failed to wait for render buffer DMA-BUF fence"); + } + // also add acquire/release barriers for the current render buffer VkImageLayout src_layout = VK_IMAGE_LAYOUT_GENERAL; if (pass->srgb_pathway) { diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 2fc9e68cf..e87a00e09 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -995,6 +995,12 @@ bool vulkan_sync_foreign_texture_acquire(struct wlr_vk_texture *texture, return buffer_export_sync_file(texture->renderer, texture->buffer, DMA_BUF_SYNC_READ, sync_file_fds); } +bool vulkan_sync_render_buffer_acquire(struct wlr_vk_render_buffer *render_buffer, + int sync_file_fds[static WLR_DMABUF_MAX_PLANES]) { + return buffer_export_sync_file(render_buffer->renderer, render_buffer->wlr_buffer, + DMA_BUF_SYNC_WRITE, sync_file_fds); +} + static bool buffer_import_sync_file(struct wlr_buffer *buffer, uint32_t flags, int sync_file_fd) { struct wlr_dmabuf_attributes dmabuf = {0}; if (!wlr_buffer_get_dmabuf(buffer, &dmabuf)) { From 2bc71d19d4e13825f328511a0b56a92338cf769c Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Fri, 6 Mar 2026 09:37:31 -0500 Subject: [PATCH 43/44] ci: update dalligi upstream repo (cherry picked from commit 67ce318b1fb4a5973e80b1ea721fbb23f210d665) --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e02463a9c..3b02ef16f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -include: https://git.sr.ht/~emersion/dalligi/blob/master/templates/multi.yml +include: https://gitlab.freedesktop.org/emersion/dalligi/-/raw/master/templates/multi.yml alpine: extends: .dalligi pages: true From 88a869855742281c98c22cab9641b317b8d065ef Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 19 Mar 2026 20:13:09 +0100 Subject: [PATCH 44/44] build: bump version to 0.19.3 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 75682e6e4..7aea9f0df 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'wlroots', 'c', - version: '0.19.2', + version: '0.19.3', license: 'MIT', meson_version: '>=1.3', default_options: [