From 878aebfb74f0a83e9839db9c3b214c4063e67862 Mon Sep 17 00:00:00 2001 From: David96 Date: Sun, 2 Feb 2025 20:47:56 +0100 Subject: [PATCH 01/18] 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 02/18] =?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 03/18] 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 04/18] 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 05/18] 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 06/18] 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 07/18] 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 08/18] 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 09/18] 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 10/18] 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 11/18] 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 12/18] 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 13/18] 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 14/18] 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 15/18] 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 16/18] 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 17/18] 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 18/18] 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