From 7f87e258b2bcfd3ec85aceeb25ef9a51fbd59a19 Mon Sep 17 00:00:00 2001 From: liupeng Date: Mon, 2 Feb 2026 09:11:41 +0800 Subject: [PATCH 01/40] render/drm_syncobj: drop unnecessary drmSyncobjTimelineWait() arg Signed-off-by: liupeng --- render/drm_syncobj.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/render/drm_syncobj.c b/render/drm_syncobj.c index 912a2b6e5..38541ca98 100644 --- a/render/drm_syncobj.c +++ b/render/drm_syncobj.c @@ -167,8 +167,7 @@ bool wlr_drm_syncobj_timeline_check(struct wlr_drm_syncobj_timeline *timeline, etime = ETIME; #endif - uint32_t signaled_point; - int ret = drmSyncobjTimelineWait(timeline->drm_fd, &timeline->handle, &point, 1, 0, flags, &signaled_point); + int ret = drmSyncobjTimelineWait(timeline->drm_fd, &timeline->handle, &point, 1, 0, flags, NULL); if (ret != 0 && ret != -etime) { wlr_log_errno(WLR_ERROR, "drmSyncobjWait() failed"); return false; From 12c9502edfa52186248d75ae4187d268d8fd165f Mon Sep 17 00:00:00 2001 From: liupeng Date: Mon, 2 Feb 2026 09:12:39 +0800 Subject: [PATCH 02/40] render/drm_syncobj: fix function name in drmSyncobjTimelineWait() error log Signed-off-by: liupeng --- render/drm_syncobj.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render/drm_syncobj.c b/render/drm_syncobj.c index 38541ca98..e1a407a1e 100644 --- a/render/drm_syncobj.c +++ b/render/drm_syncobj.c @@ -169,7 +169,7 @@ bool wlr_drm_syncobj_timeline_check(struct wlr_drm_syncobj_timeline *timeline, int ret = drmSyncobjTimelineWait(timeline->drm_fd, &timeline->handle, &point, 1, 0, flags, NULL); if (ret != 0 && ret != -etime) { - wlr_log_errno(WLR_ERROR, "drmSyncobjWait() failed"); + wlr_log_errno(WLR_ERROR, "drmSyncobjTimelineWait() failed"); return false; } From 4fe51aa43982cafc3881b1380d7b491d245d40f5 Mon Sep 17 00:00:00 2001 From: rewine Date: Sat, 31 Jan 2026 17:59:31 +0800 Subject: [PATCH 03/40] types: Simplify wlr_keyboard_group_destroy If the wlr_keyboard_group_remove_keyboard function is expanded, the code is equivalent to: ``` wl_list_for_each_safe(device, tmp_device, &group->devices, link) { struct wlr_keyboard_group *_group = group; struct wlr_keyboard *_keyboard = device->keyboard; struct keyboard_group_device *_device, *_tmp; wl_list_for_each_safe(_device, _tmp, &_group->devices, link) { if (_device->keyboard == _keyboard) { remove_keyboard_group_device(_device); continue; } } } ``` It's just running one more loop meaninglessly. --- types/wlr_keyboard_group.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/wlr_keyboard_group.c b/types/wlr_keyboard_group.c index 0ff6d93bb..6005ba647 100644 --- a/types/wlr_keyboard_group.c +++ b/types/wlr_keyboard_group.c @@ -308,7 +308,7 @@ void wlr_keyboard_group_remove_keyboard(struct wlr_keyboard_group *group, void wlr_keyboard_group_destroy(struct wlr_keyboard_group *group) { struct keyboard_group_device *device, *tmp_device; wl_list_for_each_safe(device, tmp_device, &group->devices, link) { - wlr_keyboard_group_remove_keyboard(group, device->keyboard); + remove_keyboard_group_device(device); } // Now group->keys might not be empty if a wlr_keyboard has emitted From 98196bbd898234203a177a4e4ca553604d6ad588 Mon Sep 17 00:00:00 2001 From: rewine Date: Tue, 3 Feb 2026 21:24:32 +0800 Subject: [PATCH 04/40] wlr_cursor: add comments for signal parameters --- include/wlr/types/wlr_cursor.h | 40 +++++++++++++++++----------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/include/wlr/types/wlr_cursor.h b/include/wlr/types/wlr_cursor.h index 041f735cf..aaa18a437 100644 --- a/include/wlr/types/wlr_cursor.h +++ b/include/wlr/types/wlr_cursor.h @@ -49,30 +49,30 @@ struct wlr_cursor { * your responsibility. */ struct { - struct wl_signal motion; - struct wl_signal motion_absolute; - struct wl_signal button; - struct wl_signal axis; + struct wl_signal motion; // struct wlr_pointer_motion_event + struct wl_signal motion_absolute; // struct wlr_pointer_motion_absolute_event + struct wl_signal button; // struct wlr_pointer_button_event + struct wl_signal axis; // struct wlr_pointer_axis_event struct wl_signal frame; - struct wl_signal swipe_begin; - struct wl_signal swipe_update; - struct wl_signal swipe_end; - struct wl_signal pinch_begin; - struct wl_signal pinch_update; - struct wl_signal pinch_end; - struct wl_signal hold_begin; - struct wl_signal hold_end; + struct wl_signal swipe_begin; // struct wlr_pointer_swipe_begin_event + struct wl_signal swipe_update; // struct wlr_pointer_swipe_update_event + struct wl_signal swipe_end; // struct wlr_pointer_swipe_end_event + struct wl_signal pinch_begin; // struct wlr_pointer_pinch_begin_event + struct wl_signal pinch_update; // struct wlr_pointer_pinch_update_event + struct wl_signal pinch_end; // struct wlr_pointer_pinch_end_event + struct wl_signal hold_begin; // struct wlr_pointer_hold_begin_event + struct wl_signal hold_end; // struct wlr_pointer_hold_end_event - struct wl_signal touch_up; - struct wl_signal touch_down; - struct wl_signal touch_motion; - struct wl_signal touch_cancel; + struct wl_signal touch_up; // struct wlr_touch_up_event + struct wl_signal touch_down; // struct wlr_touch_down_event + struct wl_signal touch_motion; // struct wlr_touch_motion_event + struct wl_signal touch_cancel; // struct wlr_touch_cancel_event struct wl_signal touch_frame; - struct wl_signal tablet_tool_axis; - struct wl_signal tablet_tool_proximity; - struct wl_signal tablet_tool_tip; - struct wl_signal tablet_tool_button; + struct wl_signal tablet_tool_axis; // struct wlr_tablet_tool_axis_event + struct wl_signal tablet_tool_proximity; // struct wlr_tablet_tool_proximity_event + struct wl_signal tablet_tool_tip; // struct wlr_tablet_tool_tip_event + struct wl_signal tablet_tool_button; // struct wlr_tablet_tool_button_event } events; void *data; From 7cb4e30bfdc3f1043867aaac667ca5c47d2d1cbd Mon Sep 17 00:00:00 2001 From: rewine Date: Tue, 3 Feb 2026 21:25:50 +0800 Subject: [PATCH 05/40] wlr_cursor: fix event type in handle_tablet_tool_button --- types/wlr_cursor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/wlr_cursor.c b/types/wlr_cursor.c index f2269db0f..8d0d77475 100644 --- a/types/wlr_cursor.c +++ b/types/wlr_cursor.c @@ -956,7 +956,7 @@ static void handle_tablet_tool_axis(struct wl_listener *listener, void *data) { static void handle_tablet_tool_button(struct wl_listener *listener, void *data) { - struct wlr_tablet_tool_button *event = data; + struct wlr_tablet_tool_button_event *event = data; struct wlr_cursor_device *device; device = wl_container_of(listener, device, tablet_tool_button); wl_signal_emit_mutable(&device->cursor->events.tablet_tool_button, event); From 90f9f59041b9d02809d4d64f45ca6ff2e61a722e Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 4 Feb 2026 00:11:35 +0100 Subject: [PATCH 06/40] xwayland: try flushing immediately in xwm_schedule_flush() wl_event_source_fd_update() goes through another event loop cycle, which delays writes. This extra cycle was introduced in 6ada67da9bb0 ("xwayland/xwm: implement somewhat asynchronous request flushing"). Try flushing the X11 WM FD immediately if we can. References: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/4044 --- xwayland/xwm.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/xwayland/xwm.c b/xwayland/xwm.c index c8eac2ce1..e44a11743 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -2881,5 +2882,20 @@ xcb_connection_t *wlr_xwayland_get_xwm_connection( } void xwm_schedule_flush(struct wlr_xwm *xwm) { + struct pollfd pollfd = { + .fd = xcb_get_file_descriptor(xwm->xcb_conn), + .events = POLLOUT, + }; + if (poll(&pollfd, 1, 0) < 0) { + wlr_log(WLR_ERROR, "poll() failed"); + return; + } + + // If we can write immediately, do so + if (pollfd.revents & POLLOUT) { + xcb_flush(xwm->xcb_conn); + return; + } + wl_event_source_fd_update(xwm->event_source, WL_EVENT_READABLE | WL_EVENT_WRITABLE); } From d362ed1eb9a6f3f91aff43df5b3a32c948c6a827 Mon Sep 17 00:00:00 2001 From: YaoBing Xiao Date: Wed, 4 Feb 2026 21:11:29 +0800 Subject: [PATCH 07/40] xwayland: fix wl_array rollback when adding selection targets Ensure mime_types and mime_types_atoms remain in sync when wl_array_add() fails. Roll back the partially added entry and free the allocated mime type to avoid leaks and inconsistent state. --- xwayland/selection/incoming.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/xwayland/selection/incoming.c b/xwayland/selection/incoming.c index 72f82c279..85d7775cd 100644 --- a/xwayland/selection/incoming.c +++ b/xwayland/selection/incoming.c @@ -381,13 +381,15 @@ static bool source_get_targets(struct wlr_xwm_selection *selection, free(mime_type); break; } - *mime_type_ptr = mime_type; xcb_atom_t *atom_ptr = wl_array_add(mime_types_atoms, sizeof(*atom_ptr)); if (atom_ptr == NULL) { + mime_types->size -= sizeof(*mime_type_ptr); + free(mime_type); break; } + *mime_type_ptr = mime_type; *atom_ptr = value[i]; } } From bc11ac92ab328cc45c29dd27f114ce4af756688e Mon Sep 17 00:00:00 2001 From: rewine Date: Fri, 6 Feb 2026 10:41:42 +0800 Subject: [PATCH 08/40] ext_image_capture_source_v1: remove unused variable --- types/ext_image_capture_source_v1/scene.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/types/ext_image_capture_source_v1/scene.c b/types/ext_image_capture_source_v1/scene.c index d3bf86b6c..99d34e012 100644 --- a/types/ext_image_capture_source_v1/scene.c +++ b/types/ext_image_capture_source_v1/scene.c @@ -173,8 +173,6 @@ static void source_update_buffer_constraints(struct scene_node_source *source, } static bool output_test(struct wlr_output *output, const struct wlr_output_state *state) { - struct scene_node_source *source = wl_container_of(output, source, output); - uint32_t supported = WLR_OUTPUT_STATE_BACKEND_OPTIONAL | WLR_OUTPUT_STATE_BUFFER | From dfccf5ff02eaea45d76edb40d2f92683de190800 Mon Sep 17 00:00:00 2001 From: YaoBing Xiao Date: Fri, 6 Feb 2026 10:18:21 +0800 Subject: [PATCH 09/40] output/cursor: fix missing newline at end of file --- types/output/cursor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/output/cursor.c b/types/output/cursor.c index 11f442cdf..5e93b0b2e 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -535,4 +535,4 @@ bool output_cursor_refresh_color_transform(struct wlr_output_cursor *output_curs wlr_color_transform_unref(transforms[0]); wlr_color_transform_unref(transforms[1]); return output_cursor->color_transform != NULL; -} \ No newline at end of file +} From 19d682960158df3c7c2c28a54d180ededdb84977 Mon Sep 17 00:00:00 2001 From: Steve Williams Date: Sat, 31 Jan 2026 15:00:57 +0400 Subject: [PATCH 10/40] render/pixel-format: add function to determine YCbCr from drm fourcc --- include/render/pixel_format.h | 5 +++ render/pixel_format.c | 65 +++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/include/render/pixel_format.h b/include/render/pixel_format.h index e0b500c75..b5f2b2411 100644 --- a/include/render/pixel_format.h +++ b/include/render/pixel_format.h @@ -63,4 +63,9 @@ enum wl_shm_format convert_drm_format_to_wl_shm(uint32_t fmt); */ bool pixel_format_has_alpha(uint32_t fmt); +/** + * Return true if the DRM FourCC fmt belongs to a YCbCr colorspace family, false otherwise. + */ +bool pixel_format_is_ycbcr(uint32_t fmt); + #endif diff --git a/render/pixel_format.c b/render/pixel_format.c index 5f2e8644a..c60dd9d2a 100644 --- a/render/pixel_format.c +++ b/render/pixel_format.c @@ -307,3 +307,68 @@ bool pixel_format_has_alpha(uint32_t fmt) { } return true; } + +bool pixel_format_is_ycbcr(uint32_t format) { + switch (format) { + case DRM_FORMAT_AYUV: + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV15: + case DRM_FORMAT_NV16: + case DRM_FORMAT_NV20: + case DRM_FORMAT_NV21: + case DRM_FORMAT_NV24: + case DRM_FORMAT_NV30: + case DRM_FORMAT_NV42: + case DRM_FORMAT_NV61: + case DRM_FORMAT_P010: + case DRM_FORMAT_P012: + case DRM_FORMAT_P016: + case DRM_FORMAT_P030: + case DRM_FORMAT_P210: + case DRM_FORMAT_Q401: + case DRM_FORMAT_Q410: + case DRM_FORMAT_S010: + case DRM_FORMAT_S012: + case DRM_FORMAT_S016: + case DRM_FORMAT_S210: + case DRM_FORMAT_S212: + case DRM_FORMAT_S216: + case DRM_FORMAT_S410: + case DRM_FORMAT_S412: + case DRM_FORMAT_S416: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_VUY101010: + case DRM_FORMAT_VUY888: + case DRM_FORMAT_VYUY: + case DRM_FORMAT_X0L0: + case DRM_FORMAT_X0L2: + case DRM_FORMAT_XVYU12_16161616: + case DRM_FORMAT_XVYU16161616: + case DRM_FORMAT_XVYU2101010: + case DRM_FORMAT_XYUV8888: + case DRM_FORMAT_Y0L0: + case DRM_FORMAT_Y0L2: + case DRM_FORMAT_Y210: + case DRM_FORMAT_Y212: + case DRM_FORMAT_Y216: + case DRM_FORMAT_Y410: + case DRM_FORMAT_Y412: + case DRM_FORMAT_Y416: + case DRM_FORMAT_YUV410: + case DRM_FORMAT_YUV411: + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YUV420_10BIT: + case DRM_FORMAT_YUV420_8BIT: + case DRM_FORMAT_YUV422: + case DRM_FORMAT_YUV444: + case DRM_FORMAT_YUYV: + case DRM_FORMAT_YVU410: + case DRM_FORMAT_YVU411: + case DRM_FORMAT_YVU420: + case DRM_FORMAT_YVU422: + case DRM_FORMAT_YVU444: + case DRM_FORMAT_YVYU: + return true; + } + return false; +} From 4c81cb1b9e94cc9be72831b8973c6b6b83e3746a Mon Sep 17 00:00:00 2001 From: Steve Williams Date: Sat, 31 Jan 2026 15:03:37 +0400 Subject: [PATCH 11/40] vulkan: make use of new pixel_format_is_ycbcr function --- include/render/vulkan.h | 2 +- render/vulkan/pass.c | 7 ++++--- render/vulkan/pixel_format.c | 24 ++++++++---------------- render/vulkan/renderer.c | 4 ++-- render/vulkan/texture.c | 10 +++++----- 5 files changed, 20 insertions(+), 27 deletions(-) diff --git a/include/render/vulkan.h b/include/render/vulkan.h index bb56b5534..2f8d9d241 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -86,7 +86,6 @@ struct wlr_vk_format { uint32_t drm; VkFormat vk; VkFormat vk_srgb; // sRGB version of the format, or 0 if nonexistent - bool is_ycbcr; }; extern const VkImageUsageFlags vulkan_render_usage, vulkan_shm_tex_usage, vulkan_dma_tex_usage; @@ -125,6 +124,7 @@ void vulkan_format_props_query(struct wlr_vk_device *dev, const struct wlr_vk_format_modifier_props *vulkan_format_props_find_modifier( const struct wlr_vk_format_props *props, uint64_t mod, bool render); void vulkan_format_props_finish(struct wlr_vk_format_props *props); +bool vulkan_format_is_ycbcr(const struct wlr_vk_format *format); struct wlr_vk_pipeline_layout_key { enum wlr_scale_filter_mode filter_mode; diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 31f5116bd..271b426cc 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -822,12 +822,13 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, } enum wlr_color_encoding color_encoding = options->color_encoding; - if (texture->format->is_ycbcr && color_encoding == WLR_COLOR_ENCODING_NONE) { + bool is_ycbcr = vulkan_format_is_ycbcr(texture->format); + if (is_ycbcr && color_encoding == WLR_COLOR_ENCODING_NONE) { color_encoding = WLR_COLOR_ENCODING_BT601; } enum wlr_color_range color_range = options->color_range; - if (texture->format->is_ycbcr && color_range == WLR_COLOR_RANGE_NONE) { + if (is_ycbcr && color_range == WLR_COLOR_RANGE_NONE) { color_range = WLR_COLOR_RANGE_LIMITED; } @@ -837,7 +838,7 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, .source = WLR_VK_SHADER_SOURCE_TEXTURE, .layout = { .ycbcr = { - .format = texture->format->is_ycbcr ? texture->format : NULL, + .format = is_ycbcr ? texture->format : NULL, .encoding = color_encoding, .range = color_range, }, diff --git a/render/vulkan/pixel_format.c b/render/vulkan/pixel_format.c index 902feac9a..9b17d079a 100644 --- a/render/vulkan/pixel_format.c +++ b/render/vulkan/pixel_format.c @@ -182,37 +182,30 @@ static const struct wlr_vk_format formats[] = { { .drm = DRM_FORMAT_UYVY, .vk = VK_FORMAT_B8G8R8G8_422_UNORM, - .is_ycbcr = true, }, { .drm = DRM_FORMAT_YUYV, .vk = VK_FORMAT_G8B8G8R8_422_UNORM, - .is_ycbcr = true, }, { .drm = DRM_FORMAT_NV12, .vk = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM, - .is_ycbcr = true, }, { .drm = DRM_FORMAT_NV16, .vk = VK_FORMAT_G8_B8R8_2PLANE_422_UNORM, - .is_ycbcr = true, }, { .drm = DRM_FORMAT_YUV420, .vk = VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM, - .is_ycbcr = true, }, { .drm = DRM_FORMAT_YUV422, .vk = VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM, - .is_ycbcr = true, }, { .drm = DRM_FORMAT_YUV444, .vk = VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM, - .is_ycbcr = true, }, // 3PACK16 formats split the memory in three 16-bit words, so they have an // inverted channel order compared to DRM formats. @@ -220,27 +213,22 @@ static const struct wlr_vk_format formats[] = { { .drm = DRM_FORMAT_P010, .vk = VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16, - .is_ycbcr = true, }, { .drm = DRM_FORMAT_P210, .vk = VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16, - .is_ycbcr = true, }, { .drm = DRM_FORMAT_P012, .vk = VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16, - .is_ycbcr = true, }, { .drm = DRM_FORMAT_P016, .vk = VK_FORMAT_G16_B16R16_2PLANE_420_UNORM, - .is_ycbcr = true, }, { .drm = DRM_FORMAT_Q410, .vk = VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16, - .is_ycbcr = true, }, #endif // TODO: add DRM_FORMAT_NV24/VK_FORMAT_G8_B8R8_2PLANE_444_UNORM (requires @@ -446,7 +434,7 @@ static bool query_modifier_support(struct wlr_vk_device *dev, // check that specific modifier for render usage const char *errmsg = "unknown error"; if ((m.drmFormatModifierTilingFeatures & render_features) == render_features && - !props->format.is_ycbcr) { + !vulkan_format_is_ycbcr(&props->format)) { struct wlr_vk_format_modifier_props p = {0}; bool supported = false; if (query_modifier_usage_support(dev, props->format.vk, @@ -477,7 +465,7 @@ static bool query_modifier_support(struct wlr_vk_device *dev, // check that specific modifier for texture usage errmsg = "unknown error"; VkFormatFeatureFlags features = dma_tex_features; - if (props->format.is_ycbcr) { + if (vulkan_format_is_ycbcr(&props->format)) { features |= ycbcr_tex_features; } if ((m.drmFormatModifierTilingFeatures & features) == features) { @@ -522,7 +510,7 @@ static bool query_modifier_support(struct wlr_vk_device *dev, void vulkan_format_props_query(struct wlr_vk_device *dev, const struct wlr_vk_format *format) { - if (format->is_ycbcr && !dev->sampler_ycbcr_conversion) { + if (vulkan_format_is_ycbcr(format) && !dev->sampler_ycbcr_conversion) { return; } @@ -551,7 +539,7 @@ void vulkan_format_props_query(struct wlr_vk_device *dev, char shm_texture_status[256]; const char *errmsg = "unknown error"; if ((fmtp.formatProperties.optimalTilingFeatures & shm_tex_features) == shm_tex_features && - !format->is_ycbcr && format_info != NULL) { + !vulkan_format_is_ycbcr(format) && format_info != NULL) { VkImageFormatProperties ifmtp; bool supported = false, has_mutable_srgb = false; if (query_shm_support(dev, format->vk, format->vk_srgb, &ifmtp, &errmsg)) { @@ -621,3 +609,7 @@ const struct wlr_vk_format_modifier_props *vulkan_format_props_find_modifier( } return NULL; } + +bool vulkan_format_is_ycbcr(const struct wlr_vk_format *format) { + return pixel_format_is_ycbcr(format->drm); +} diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 0b411f5dd..cfa05e077 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -1630,8 +1630,8 @@ static bool init_blend_to_output_layouts(struct wlr_vk_renderer *renderer) { static bool pipeline_layout_key_equals( const struct wlr_vk_pipeline_layout_key *a, const struct wlr_vk_pipeline_layout_key *b) { - assert(!a->ycbcr.format || a->ycbcr.format->is_ycbcr); - assert(!b->ycbcr.format || b->ycbcr.format->is_ycbcr); + assert(!a->ycbcr.format || vulkan_format_is_ycbcr(a->ycbcr.format)); + assert(!b->ycbcr.format || vulkan_format_is_ycbcr(b->ycbcr.format)); if (a->filter_mode != b->filter_mode) { return false; diff --git a/render/vulkan/texture.c b/render/vulkan/texture.c index 499178f5d..ffea87795 100644 --- a/render/vulkan/texture.c +++ b/render/vulkan/texture.c @@ -297,7 +297,7 @@ struct wlr_vk_texture_view *vulkan_texture_get_or_create_view(struct wlr_vk_text .components.r = VK_COMPONENT_SWIZZLE_IDENTITY, .components.g = VK_COMPONENT_SWIZZLE_IDENTITY, .components.b = VK_COMPONENT_SWIZZLE_IDENTITY, - .components.a = texture->has_alpha || texture->format->is_ycbcr + .components.a = texture->has_alpha || vulkan_format_is_ycbcr(texture->format) ? VK_COMPONENT_SWIZZLE_IDENTITY : VK_COMPONENT_SWIZZLE_ONE, .subresourceRange = (VkImageSubresourceRange){ @@ -311,7 +311,7 @@ struct wlr_vk_texture_view *vulkan_texture_get_or_create_view(struct wlr_vk_text }; VkSamplerYcbcrConversionInfo ycbcr_conversion_info; - if (texture->format->is_ycbcr) { + if (vulkan_format_is_ycbcr(texture->format)) { assert(pipeline_layout->ycbcr.conversion != VK_NULL_HANDLE); ycbcr_conversion_info = (VkSamplerYcbcrConversionInfo){ .sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO, @@ -355,7 +355,7 @@ struct wlr_vk_texture_view *vulkan_texture_get_or_create_view(struct wlr_vk_text static void texture_set_format(struct wlr_vk_texture *texture, const struct wlr_vk_format *format, bool has_mutable_srgb) { - assert(!(format->is_ycbcr && has_mutable_srgb)); + assert(!(vulkan_format_is_ycbcr(format) && has_mutable_srgb)); texture->format = format; texture->using_mutable_srgb = has_mutable_srgb; @@ -366,7 +366,7 @@ static void texture_set_format(struct wlr_vk_texture *texture, texture->has_alpha = pixel_format_has_alpha(format->drm); } else { // We don't have format info for multi-planar formats - assert(texture->format->is_ycbcr); + assert(vulkan_format_is_ycbcr(texture->format)); } } @@ -378,7 +378,7 @@ static struct wlr_texture *vulkan_texture_from_pixels( const struct wlr_vk_format_props *fmt = vulkan_format_props_from_drm(renderer->dev, drm_fmt); - if (fmt == NULL || fmt->format.is_ycbcr) { + if (fmt == NULL || vulkan_format_is_ycbcr(&fmt->format)) { char *format_name = drmGetFormatName(drm_fmt); wlr_log(WLR_ERROR, "Unsupported pixel format %s (0x%08"PRIX32")", format_name, drm_fmt); From 439985fe95efec4ccb06f5e690b33422a40dc512 Mon Sep 17 00:00:00 2001 From: Steve Williams Date: Sat, 31 Jan 2026 15:10:49 +0400 Subject: [PATCH 12/40] color_representation: ensure encoding/range/drm formats compatibility --- types/wlr_color_representation_v1.c | 38 +++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/types/wlr_color_representation_v1.c b/types/wlr_color_representation_v1.c index a447d77a7..a69ea46e5 100644 --- a/types/wlr_color_representation_v1.c +++ b/types/wlr_color_representation_v1.c @@ -1,13 +1,16 @@ #include +#include #include #include +#include #include #include #include #include #include "color-representation-v1-protocol.h" +#include "render/pixel_format.h" #include "util/mem.h" #define WP_COLOR_REPRESENTATION_VERSION 1 @@ -230,8 +233,42 @@ static void color_repr_manager_handle_destroy(struct wl_client *client, wl_resource_destroy(resource); } +static void surface_synced_commit(struct wlr_surface_synced *synced) { + struct wlr_color_representation_v1 *color_repr = wl_container_of(synced, color_repr, synced); + + if (color_repr->current.coefficients == 0 && color_repr->current.range == 0) { + return; + } + + struct wlr_dmabuf_attributes dmabuf; + + if (!color_repr->surface->buffer || + !wlr_buffer_get_dmabuf(&color_repr->surface->buffer->base, &dmabuf)) { + return; + } + + bool is_ycbcr = pixel_format_is_ycbcr(dmabuf.format); + + bool is_identity_full = + color_repr->current.coefficients == WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_IDENTITY && + color_repr->current.range == WP_COLOR_REPRESENTATION_SURFACE_V1_RANGE_FULL; + + if (is_ycbcr) { + if (is_identity_full) { + wl_resource_post_error(color_repr->resource, + WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_PIXEL_FORMAT, + "unexpected encoding/range for yuv"); + } + } else /* rgb */ { + wl_resource_post_error(color_repr->resource, + WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_PIXEL_FORMAT, + "unexpected encoding/range for rgb"); + } +} + static const struct wlr_surface_synced_impl surface_synced_impl = { .state_size = sizeof(struct wlr_color_representation_v1_surface_state), + .commit = surface_synced_commit }; static struct wlr_color_representation_v1 *color_repr_from_surface( @@ -276,6 +313,7 @@ static void color_repr_manager_handle_get_surface(struct wl_client *client, } color_repr->manager = manager_from_resource(manager_resource); + color_repr->surface = surface; if (!wlr_surface_synced_init(&color_repr->synced, surface, &surface_synced_impl, &color_repr->pending, &color_repr->current)) { From bb78861ca90e2dbac5e8823e1706e44cdf34b37b Mon Sep 17 00:00:00 2001 From: Steve Williams Date: Sat, 31 Jan 2026 15:11:14 +0400 Subject: [PATCH 13/40] color-representation: add support for identity+full --- render/vulkan/renderer.c | 4 ++-- types/scene/wlr_scene.c | 8 ++++++-- types/wlr_color_representation_v1.c | 8 +++++++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index cfa05e077..7790d40b2 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -2039,8 +2039,8 @@ struct wlr_vk_pipeline_layout *get_or_create_pipeline_layout( }; sampler_create_info.pNext = &conversion_info; } else { - assert(key->ycbcr.encoding == WLR_COLOR_ENCODING_NONE); - assert(key->ycbcr.range == WLR_COLOR_RANGE_NONE); + assert(key->ycbcr.encoding == WLR_COLOR_ENCODING_NONE || key->ycbcr.encoding == WLR_COLOR_ENCODING_IDENTITY); + assert(key->ycbcr.range == WLR_COLOR_RANGE_NONE || key->ycbcr.range == WLR_COLOR_RANGE_FULL); } res = vkCreateSampler(renderer->dev->dev, &sampler_create_info, NULL, &pipeline_layout->sampler); diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 19617b11f..aaf9a8d74 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -2086,8 +2086,12 @@ static enum scene_direct_scanout_result scene_entry_try_direct_scanout( return SCANOUT_INELIGIBLE; } - if (buffer->color_encoding != WLR_COLOR_ENCODING_NONE || - buffer->color_range != WLR_COLOR_RANGE_NONE) { + bool is_color_repr_none = buffer->color_encoding == WLR_COLOR_ENCODING_NONE && + buffer->color_range == WLR_COLOR_RANGE_NONE; + bool is_color_repr_identity_full = buffer->color_encoding == WLR_COLOR_ENCODING_IDENTITY && + buffer->color_range == WLR_COLOR_RANGE_FULL; + + if (!(is_color_repr_none || is_color_repr_identity_full)) { return SCANOUT_INELIGIBLE; } diff --git a/types/wlr_color_representation_v1.c b/types/wlr_color_representation_v1.c index a69ea46e5..432511b91 100644 --- a/types/wlr_color_representation_v1.c +++ b/types/wlr_color_representation_v1.c @@ -260,9 +260,11 @@ static void surface_synced_commit(struct wlr_surface_synced *synced) { "unexpected encoding/range for yuv"); } } else /* rgb */ { - wl_resource_post_error(color_repr->resource, + if (!is_identity_full) { + wl_resource_post_error(color_repr->resource, WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_PIXEL_FORMAT, "unexpected encoding/range for rgb"); + } } } @@ -465,6 +467,10 @@ struct wlr_color_representation_manager_v1 *wlr_color_representation_manager_v1_ struct wlr_color_representation_v1_coeffs_and_range coeffs_and_ranges[COEFFICIENTS_LEN * RANGES_LEN]; size_t coeffs_and_ranges_len = 0; + coeffs_and_ranges[coeffs_and_ranges_len++] = (struct wlr_color_representation_v1_coeffs_and_range){ + .coeffs = WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_IDENTITY, + .range = WP_COLOR_REPRESENTATION_SURFACE_V1_RANGE_FULL, + }; for (size_t i = 0; i < COEFFICIENTS_LEN; i++) { enum wp_color_representation_surface_v1_coefficients coeffs = coefficients[i]; enum wlr_color_encoding enc = wlr_color_representation_v1_color_encoding_to_wlr(coeffs); From ef882466421492061a776c2025513a03598faeb5 Mon Sep 17 00:00:00 2001 From: Steve Williams Date: Thu, 5 Feb 2026 21:26:29 +0400 Subject: [PATCH 14/40] types/wlr_buffer: add buffer_get_drm_format helper function --- backend/x11/output.c | 12 +++--------- include/types/wlr_buffer.h | 6 ++++++ types/buffer/buffer.c | 23 ++++++++++++++++------- types/wlr_color_representation_v1.c | 13 +++++++------ 4 files changed, 32 insertions(+), 22 deletions(-) diff --git a/backend/x11/output.c b/backend/x11/output.c index 0b63a7088..181c2de14 100644 --- a/backend/x11/output.c +++ b/backend/x11/output.c @@ -20,6 +20,7 @@ #include "backend/x11.h" #include "util/time.h" +#include "types/wlr_buffer.h" #include "types/wlr_output.h" static const uint32_t SUPPORTED_OUTPUT_STATE = @@ -166,15 +167,8 @@ static bool output_test(struct wlr_output *wlr_output, if (state->committed & WLR_OUTPUT_STATE_BUFFER) { struct wlr_buffer *buffer = state->buffer; - struct wlr_dmabuf_attributes dmabuf_attrs; - struct wlr_shm_attributes shm_attrs; - uint32_t format = DRM_FORMAT_INVALID; - if (wlr_buffer_get_dmabuf(buffer, &dmabuf_attrs)) { - format = dmabuf_attrs.format; - } else if (wlr_buffer_get_shm(buffer, &shm_attrs)) { - format = shm_attrs.format; - } - if (format != x11->x11_format->drm) { + + if (buffer_get_drm_format(buffer) != x11->x11_format->drm) { wlr_log(WLR_DEBUG, "Unsupported buffer format"); return false; } diff --git a/include/types/wlr_buffer.h b/include/types/wlr_buffer.h index 9d882d47d..45acf7a1d 100644 --- a/include/types/wlr_buffer.h +++ b/include/types/wlr_buffer.h @@ -65,4 +65,10 @@ struct wlr_client_buffer *wlr_client_buffer_create(struct wlr_buffer *buffer, bool wlr_client_buffer_apply_damage(struct wlr_client_buffer *client_buffer, struct wlr_buffer *next, const pixman_region32_t *damage); +/** + * Return the DRM format of the buffer. If this buffer isn't shared + * memory or a DMA-BUF, returns DRM_FORMAT_INVALID. + */ +uint32_t buffer_get_drm_format(struct wlr_buffer *buffer); + #endif diff --git a/types/buffer/buffer.c b/types/buffer/buffer.c index d56255b0d..48a10d9f0 100644 --- a/types/buffer/buffer.c +++ b/types/buffer/buffer.c @@ -109,14 +109,11 @@ bool wlr_buffer_get_shm(struct wlr_buffer *buffer, bool wlr_buffer_is_opaque(struct wlr_buffer *buffer) { void *data; - uint32_t format; + uint32_t format = buffer_get_drm_format(buffer); size_t stride; - struct wlr_dmabuf_attributes dmabuf; - struct wlr_shm_attributes shm; - if (wlr_buffer_get_dmabuf(buffer, &dmabuf)) { - format = dmabuf.format; - } else if (wlr_buffer_get_shm(buffer, &shm)) { - format = shm.format; + + if (format != DRM_FORMAT_INVALID) { + // pass } else if (wlr_buffer_begin_data_ptr_access(buffer, WLR_BUFFER_DATA_PTR_ACCESS_READ, &data, &format, &stride)) { bool opaque = false; @@ -135,3 +132,15 @@ bool wlr_buffer_is_opaque(struct wlr_buffer *buffer) { return !pixel_format_has_alpha(format); } + +uint32_t buffer_get_drm_format(struct wlr_buffer *buffer) { + uint32_t format = DRM_FORMAT_INVALID; + struct wlr_dmabuf_attributes dmabuf; + struct wlr_shm_attributes shm; + if (wlr_buffer_get_dmabuf(buffer, &dmabuf)) { + format = dmabuf.format; + } else if (wlr_buffer_get_shm(buffer, &shm)) { + format = shm.format; + } + return format; +} diff --git a/types/wlr_color_representation_v1.c b/types/wlr_color_representation_v1.c index 432511b91..ab7df30b0 100644 --- a/types/wlr_color_representation_v1.c +++ b/types/wlr_color_representation_v1.c @@ -11,6 +11,7 @@ #include "color-representation-v1-protocol.h" #include "render/pixel_format.h" +#include "types/wlr_buffer.h" #include "util/mem.h" #define WP_COLOR_REPRESENTATION_VERSION 1 @@ -240,14 +241,14 @@ static void surface_synced_commit(struct wlr_surface_synced *synced) { return; } - struct wlr_dmabuf_attributes dmabuf; - - if (!color_repr->surface->buffer || - !wlr_buffer_get_dmabuf(&color_repr->surface->buffer->base, &dmabuf)) { + uint32_t drm_format = DRM_FORMAT_INVALID; + if (!color_repr->surface->buffer){ + drm_format = buffer_get_drm_format(&color_repr->surface->buffer->base); + } + if (drm_format == DRM_FORMAT_INVALID) { return; } - - bool is_ycbcr = pixel_format_is_ycbcr(dmabuf.format); + bool is_ycbcr = pixel_format_is_ycbcr(drm_format); bool is_identity_full = color_repr->current.coefficients == WP_COLOR_REPRESENTATION_SURFACE_V1_COEFFICIENTS_IDENTITY && From 436bcf9a4755b24f7fadc100ba8deaa7f343ba86 Mon Sep 17 00:00:00 2001 From: Steve Williams Date: Sat, 7 Feb 2026 08:36:29 +0400 Subject: [PATCH 15/40] color-representation-v1: fix condition in surface commit --- types/wlr_color_representation_v1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/wlr_color_representation_v1.c b/types/wlr_color_representation_v1.c index ab7df30b0..856dc84f8 100644 --- a/types/wlr_color_representation_v1.c +++ b/types/wlr_color_representation_v1.c @@ -242,7 +242,7 @@ static void surface_synced_commit(struct wlr_surface_synced *synced) { } uint32_t drm_format = DRM_FORMAT_INVALID; - if (!color_repr->surface->buffer){ + if (color_repr->surface->buffer){ drm_format = buffer_get_drm_format(&color_repr->surface->buffer->base); } if (drm_format == DRM_FORMAT_INVALID) { From c1452d88114710f5772662b1d8efb9c71edaa34c Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 7 Feb 2026 17:48:16 -0800 Subject: [PATCH 16/40] backend/libinput: fix build with libinput 1.31 --- 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 3676ab4df0e7b31efd9c72f543fd8e6326af95cc Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sat, 7 Feb 2026 17:48:16 -0800 Subject: [PATCH 17/40] backend/libinput: add support for LIBINPUT_SWITCH_KEYPAD_SLIDE --- 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 910fd264fb78f1ee6d68b79c883285c57419c7f3 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 12 Feb 2026 22:47:22 +0100 Subject: [PATCH 18/40] build: bump version to 0.20.0-rc2 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 5b8f12073..b5b756bae 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'wlroots', 'c', - version: '0.20.0-rc1', + version: '0.20.0-rc2', license: 'MIT', meson_version: '>=1.3', default_options: [ From 25f94c59657e45f087f3429c0fa979eae5a136e0 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Fri, 13 Feb 2026 11:48:17 +0100 Subject: [PATCH 19/40] backend/x11: reject shm buffers with non-min strides This fixes an X11 backend direct scanout bug with foot. Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/4046 --- backend/x11/output.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/backend/x11/output.c b/backend/x11/output.c index 181c2de14..51a9c1441 100644 --- a/backend/x11/output.c +++ b/backend/x11/output.c @@ -19,6 +19,7 @@ #include #include "backend/x11.h" +#include "render/pixel_format.h" #include "util/time.h" #include "types/wlr_buffer.h" #include "types/wlr_output.h" @@ -167,11 +168,20 @@ static bool output_test(struct wlr_output *wlr_output, if (state->committed & WLR_OUTPUT_STATE_BUFFER) { struct wlr_buffer *buffer = state->buffer; - - if (buffer_get_drm_format(buffer) != x11->x11_format->drm) { + uint32_t format = buffer_get_drm_format(buffer); + if (format != x11->x11_format->drm) { wlr_log(WLR_DEBUG, "Unsupported buffer format"); return false; } + struct wlr_shm_attributes shm; + if (wlr_buffer_get_shm(buffer, &shm)) { + const struct wlr_pixel_format_info *info = drm_get_pixel_format_info(format); + if (shm.stride != pixel_format_info_min_stride(info, shm.width)) { + // xcb_shm_create_pixmap() does not allow arbitrary strides. + wlr_log(WLR_DEBUG, "Unsupported shm buffer stride"); + return false; + } + } } if (state->committed & WLR_OUTPUT_STATE_MODE) { @@ -261,6 +271,12 @@ static xcb_pixmap_t import_shm(struct wlr_x11_output *output, return XCB_PIXMAP_NONE; } + const struct wlr_pixel_format_info *info = drm_get_pixel_format_info(shm->format); + if (shm->stride != pixel_format_info_min_stride(info, shm->width)) { + // xcb_shm_create_pixmap() does not allow arbitrary strides. + return XCB_PIXMAP_NONE; + } + // xcb closes the FD after sending it int fd = fcntl(shm->fd, F_DUPFD_CLOEXEC, 0); if (fd < 0) { From 884d29e5f31b5dec41fdbaa7876458e1b88a28be Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 12 Feb 2026 22:37:41 +0100 Subject: [PATCH 20/40] 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. --- 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 b2430a445..782e56a86 100644 --- a/backend/libinput/tablet_tool.c +++ b/backend/libinput/tablet_tool.c @@ -78,27 +78,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( @@ -110,14 +144,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 = @@ -209,14 +248,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) == @@ -251,14 +288,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); } @@ -277,13 +311,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 1efb216c6d85ec0d311d8e8f8ce7cf0ac0f840ae Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 19 Feb 2026 14:17:33 +0100 Subject: [PATCH 21/40] 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. --- backend/drm/drm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 86b52c684..bd0147a42 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -2104,6 +2104,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 25bec59c75e7f6c95d29686915d1ddd0c62ab539 Mon Sep 17 00:00:00 2001 From: hrdl Date: Thu, 19 Feb 2026 22:53:47 +0100 Subject: [PATCH 22/40] CONTRIBUTING.md: update git host gitlab.freedesktop.org -> ssh.gitlab.freedesktop.org --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index aea7d6960..d9c49f7c2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,7 +22,7 @@ don't, however, allow me to make a suggestion: feature branches pulled from upstream. Try this: 1. Fork wlroots -2. `git clone git@gitlab.freedesktop.org:/wlroots.git && cd wlroots` +2. `git clone git@ssh.gitlab.freedesktop.org:/wlroots.git && cd wlroots` 3. `git remote add upstream https://gitlab.freedesktop.org/wlroots/wlroots.git` You only need to do this once. You're never going to use your fork's master From 7be5e3689c02e5105e6fbef2d5539ffe21775056 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 22 Feb 2026 23:38:52 +0100 Subject: [PATCH 23/40] build: bump version to 0.20.0-rc3 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index b5b756bae..f054cc250 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'wlroots', 'c', - version: '0.20.0-rc2', + version: '0.20.0-rc3', license: 'MIT', meson_version: '>=1.3', default_options: [ From a6e5807e8640c038f0b3d7b0a9e01826eb81a101 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 14 Feb 2026 17:53:54 +0100 Subject: [PATCH 24/40] render/vulkan: introduce buffer_import_sync_file() Will be used in two spots in a following commit. --- 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 7790d40b2..352e5f344 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -1013,6 +1013,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) { @@ -1046,18 +1063,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 73bbad8433a916ca342f19975b7eb2aaf3a8fc6f Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 14 Feb 2026 17:55:52 +0100 Subject: [PATCH 25/40] 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. --- 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 2f8d9d241..6e1e721d1 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -473,9 +473,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 271b426cc..44dc68553 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -606,8 +606,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 352e5f344..5ca9f7a7d 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -1030,12 +1030,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); } @@ -1057,13 +1057,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 43b37e34d662ca893ebaae6e813634ee216c352f Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 14 Feb 2026 17:57:42 +0100 Subject: [PATCH 26/40] 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. --- render/vulkan/renderer.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 5ca9f7a7d..15ed7e17f 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -1066,6 +1066,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 ff4ce121796081dec656fc4106dbf6b6a043fa77 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 14 Feb 2026 18:04:58 +0100 Subject: [PATCH 27/40] render/vulkan: introduce buffer_export_sync_file() Same as buffer_import_sync_file(), but for the export side. --- 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 15ed7e17f..8f5ad85b5 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -968,13 +968,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; } @@ -984,7 +982,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); @@ -1001,7 +999,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; @@ -1013,6 +1011,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 8c8d6363a19e04054071f6983423c4eb760bf86b Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 14 Feb 2026 18:06:40 +0100 Subject: [PATCH 28/40] render/vulkan: add "acquire" to vulkan_sync_foreign_texture() Makes it more obvious that this is about the acquire side, not the release side. --- 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 6e1e721d1..a1d1c2646 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -475,7 +475,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 44dc68553..0539bae1d 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -386,7 +386,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 8f5ad85b5..feafcbc34 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -1011,7 +1011,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 2367d78c3c1de2428cbbf9444d6eff632698baf6 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 14 Feb 2026 18:17:32 +0100 Subject: [PATCH 29/40] 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. --- 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 a1d1c2646..903d1c33e 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -477,6 +477,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 0539bae1d..503e37c07 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -146,6 +146,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 unwrap_color_transform(struct wlr_color_transform *transform, float matrix[static 9], enum wlr_color_transfer_function *tf) { if (transform == NULL) { @@ -308,7 +342,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"); @@ -413,6 +447,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->render_buffer_out->transitioned) { diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index feafcbc34..da17a4703 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -1016,6 +1016,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 1ce992d7cb5023a7a7c91ed6ce156529e3709657 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 24 Feb 2026 11:52:00 +0100 Subject: [PATCH 30/40] scene/layer_shell_v1: Add support for exclusive_edge The v5 layer shell interface allows the client to specify which edge the exclusive zone will apply to, instead of deducing it from the anchor points. Add support for this to the layer shell scene helper. --- types/scene/layer_shell_v1.c | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/types/scene/layer_shell_v1.c b/types/scene/layer_shell_v1.c index 4ae736ddf..234227df1 100644 --- a/types/scene/layer_shell_v1.c +++ b/types/scene/layer_shell_v1.c @@ -1,6 +1,7 @@ #include #include #include +#include static void scene_layer_surface_handle_tree_destroy( struct wl_listener *listener, void *data) { @@ -21,36 +22,23 @@ static void scene_layer_surface_handle_layer_surface_destroy( static void layer_surface_exclusive_zone( struct wlr_layer_surface_v1_state *state, + enum wlr_edges edge, struct wlr_box *usable_area) { - switch (state->anchor) { - case ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP: - case (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT): - // Anchor top + switch (edge) { + case WLR_EDGE_NONE: + return; + case WLR_EDGE_TOP: usable_area->y += state->exclusive_zone + state->margin.top; usable_area->height -= state->exclusive_zone + state->margin.top; break; - case ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM: - case (ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT): - // Anchor bottom + case WLR_EDGE_BOTTOM: usable_area->height -= state->exclusive_zone + state->margin.bottom; break; - case ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT: - case (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT): - // Anchor left + case WLR_EDGE_LEFT: usable_area->x += state->exclusive_zone + state->margin.left; usable_area->width -= state->exclusive_zone + state->margin.left; break; - case ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT: - case (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT): - // Anchor right + case WLR_EDGE_RIGHT: usable_area->width -= state->exclusive_zone + state->margin.right; break; } @@ -121,7 +109,8 @@ void wlr_scene_layer_surface_v1_configure( wlr_layer_surface_v1_configure(layer_surface, box.width, box.height); if (layer_surface->surface->mapped && state->exclusive_zone > 0) { - layer_surface_exclusive_zone(state, usable_area); + enum wlr_edges edge = wlr_layer_surface_v1_get_exclusive_edge(layer_surface); + layer_surface_exclusive_zone(state, edge, usable_area); } } From 6d9aa17572c12cc22d2c9f7f0afc3e942a699d2a Mon Sep 17 00:00:00 2001 From: Diego Viola Date: Sat, 28 Feb 2026 04:26:42 -0300 Subject: [PATCH 31/40] treewide: fix typos Signed-off-by: Diego Viola --- backend/drm/drm.c | 2 +- backend/wayland/seat.c | 2 +- include/render/color.h | 2 +- include/render/vulkan.h | 2 +- include/wlr/xcursor.h | 2 +- render/egl.c | 2 +- render/pixman/pass.c | 2 +- render/vulkan/shaders/common.vert | 2 +- render/vulkan/texture.c | 2 +- render/vulkan/vulkan.c | 2 +- tinywl/tinywl.c | 6 +++--- types/scene/wlr_scene.c | 2 +- util/array.c | 2 +- 13 files changed, 15 insertions(+), 15 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index bd0147a42..fe9dcc341 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -2139,7 +2139,7 @@ struct wlr_drm_lease *wlr_drm_create_lease(struct wlr_output **outputs, wlr_log(WLR_DEBUG, "Connector %d", conn->id); if (!drm_connector_alloc_crtc(conn)) { - wlr_log(WLR_ERROR, "Failled to allocate connector CRTC"); + wlr_log(WLR_ERROR, "Failed to allocate connector CRTC"); return NULL; } diff --git a/backend/wayland/seat.c b/backend/wayland/seat.c index a66357bf8..f178d9baf 100644 --- a/backend/wayland/seat.c +++ b/backend/wayland/seat.c @@ -246,7 +246,7 @@ void init_seat_touch(struct wlr_wl_seat *seat) { struct wlr_wl_output *output; wl_list_for_each(output, &seat->backend->outputs, link) { - /* Multi-output touch not supproted */ + /* Multi-output touch not supported */ seat->wlr_touch.output_name = strdup(output->wlr_output.name); break; } diff --git a/include/render/color.h b/include/render/color.h index 57d2b6a96..60c841d12 100644 --- a/include/render/color.h +++ b/include/render/color.h @@ -91,7 +91,7 @@ struct wlr_color_transform_lut_3x1d *color_transform_lut_3x1d_from_base( * Create a simplified / normalized wlr_color_transform pipeline. * `transforms` may contain NULL transforms, they will be interpreted as the * identity transform, and removed. - * `*result` may be set to a tranform of a type different from + * `*result` may be set to a transform of a type different from * `wlr_color_transform_pipeline`, or to NULL if all input transforms are NULL */ bool color_transform_compose(struct wlr_color_transform **result, diff --git a/include/render/vulkan.h b/include/render/vulkan.h index 903d1c33e..f96619f2c 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -224,7 +224,7 @@ struct wlr_vk_render_buffer_out { bool transitioned; }; -// Renderer-internal represenation of an wlr_buffer imported for rendering. +// Renderer-internal representation of an wlr_buffer imported for rendering. struct wlr_vk_render_buffer { struct wlr_buffer *wlr_buffer; struct wlr_addon addon; diff --git a/include/wlr/xcursor.h b/include/wlr/xcursor.h index 7cf333d70..5e9c140ae 100644 --- a/include/wlr/xcursor.h +++ b/include/wlr/xcursor.h @@ -47,7 +47,7 @@ /** * A still cursor image. * - * The buffer contains pixels layed out in a packed DRM_FORMAT_ARGB8888 format. + * The buffer contains pixels laid out in a packed DRM_FORMAT_ARGB8888 format. */ struct wlr_xcursor_image { uint32_t width; /* actual width */ diff --git a/render/egl.c b/render/egl.c index 6f3e9c8ca..8673acabf 100644 --- a/render/egl.c +++ b/render/egl.c @@ -156,7 +156,7 @@ static void init_dmabuf_formats(struct wlr_egl *egl) { } if (modifiers_len == 0) { - // Asume the linear layout is supported if the driver doesn't + // Assume the linear layout is supported if the driver doesn't // explicitly say otherwise wlr_drm_format_set_add(&egl->dmabuf_texture_formats, fmt, DRM_FORMAT_MOD_LINEAR); diff --git a/render/pixman/pass.c b/render/pixman/pass.c index 4ae742cab..d3ee17dca 100644 --- a/render/pixman/pass.c +++ b/render/pixman/pass.c @@ -78,7 +78,7 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, if (options->transform != WL_OUTPUT_TRANSFORM_NORMAL || src_box_transformed.width != dst_box.width || src_box_transformed.height != dst_box.height) { - // Cosinus/sinus values are extact integers for enum wl_output_transform entries + // Cosinus/sinus values are exact integers for enum wl_output_transform entries int tr_cos = 1, tr_sin = 0, tr_x = 0, tr_y = 0; switch (options->transform) { case WL_OUTPUT_TRANSFORM_NORMAL: diff --git a/render/vulkan/shaders/common.vert b/render/vulkan/shaders/common.vert index c6175d248..f1579790d 100644 --- a/render/vulkan/shaders/common.vert +++ b/render/vulkan/shaders/common.vert @@ -1,7 +1,7 @@ #version 450 // we use a mat4 since it uses the same size as mat3 due to -// alignment. Easier to deal with (tighly-packed) mat4 though. +// alignment. Easier to deal with (tightly-packed) mat4 though. layout(push_constant, row_major) uniform UBO { mat4 proj; vec2 uv_offset; diff --git a/render/vulkan/texture.c b/render/vulkan/texture.c index ffea87795..57db97a56 100644 --- a/render/vulkan/texture.c +++ b/render/vulkan/texture.c @@ -189,7 +189,7 @@ void vulkan_texture_destroy(struct wlr_vk_texture *texture) { // when we recorded a command to fill this image _this_ frame, // it has to be executed before the texture can be destroyed. // Add it to the renderer->destroy_textures list, destroying - // _after_ the stage command buffer has exectued + // _after_ the stage command buffer has executed if (texture->last_used_cb != NULL) { assert(texture->destroy_link.next == NULL); // not already inserted wl_list_insert(&texture->last_used_cb->destroy_textures, diff --git a/render/vulkan/vulkan.c b/render/vulkan/vulkan.c index 78bc25941..ee7adc011 100644 --- a/render/vulkan/vulkan.c +++ b/render/vulkan/vulkan.c @@ -290,7 +290,7 @@ VkPhysicalDevice vulkan_find_drm_phdev(struct wlr_vk_instance *ini, int drm_fd) log_phdev(&phdev_props); if (phdev_props.apiVersion < VK_API_VERSION_1_1) { - // NOTE: we could additionaly check whether the + // NOTE: we could additionally check whether the // VkPhysicalDeviceProperties2KHR extension is supported but // implementations not supporting 1.1 are unlikely in future continue; diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c index b3d902c7e..fe242e1a9 100644 --- a/tinywl/tinywl.c +++ b/tinywl/tinywl.c @@ -614,7 +614,7 @@ static void server_new_output(struct wl_listener *listener, void *data) { struct wlr_output *wlr_output = data; /* Configures the output created by the backend to use our allocator - * and our renderer. Must be done once, before commiting the output */ + * and our renderer. Must be done once, before committing the output */ wlr_output_init_render(wlr_output, server->allocator, server->renderer); /* The output may be disabled, switch it on. */ @@ -723,7 +723,7 @@ static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) { static void begin_interactive(struct tinywl_toplevel *toplevel, enum tinywl_cursor_mode mode, uint32_t edges) { /* This function sets up an interactive move or resize operation, where the - * compositor stops propegating pointer events to clients and instead + * compositor stops propagating pointer events to clients and instead * consumes them itself, to move or resize windows. */ struct tinywl_server *server = toplevel->server; @@ -906,7 +906,7 @@ int main(int argc, char *argv[]) { struct tinywl_server server = {0}; /* The Wayland display is managed by libwayland. It handles accepting - * clients from the Unix socket, manging Wayland globals, and so on. */ + * clients from the Unix socket, managing Wayland globals, and so on. */ server.wl_display = wl_display_create(); /* The backend is a wlroots feature which abstracts the underlying input and * output hardware. The autocreate option will choose the most suitable diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index aaf9a8d74..51373e15a 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -2391,7 +2391,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, pixman_region32_init(&acc_damage); struct highlight_region *damage, *tmp_damage; wl_list_for_each_safe(damage, tmp_damage, regions, link) { - // remove overlaping damage regions + // remove overlapping damage regions pixman_region32_subtract(&damage->region, &damage->region, &acc_damage); pixman_region32_union(&acc_damage, &acc_damage, &damage->region); diff --git a/util/array.c b/util/array.c index ec16a7b13..d4771e817 100644 --- a/util/array.c +++ b/util/array.c @@ -14,7 +14,7 @@ bool array_realloc(struct wl_array *arr, size_t size) { // If the size is less than 1/4th of the allocation size, we shrink it. // 1/4th is picked to provide hysteresis, without which an array with size // arr->alloc would constantly reallocate if an element is added and then - // removed continously. + // removed continuously. size_t alloc; if (arr->alloc > 0 && size > arr->alloc / 4) { alloc = arr->alloc; From a55b85e2e1138f9534890e4dc6a224ec2efb9d4d Mon Sep 17 00:00:00 2001 From: Wang Yu Date: Tue, 3 Mar 2026 14:37:42 +0800 Subject: [PATCH 32/40] 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 --- 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 55bb69e2c4443d9ab2672ed9e4dfe10cca9f2663 Mon Sep 17 00:00:00 2001 From: YaoBing Xiao Date: Fri, 6 Feb 2026 23:30:35 +0800 Subject: [PATCH 33/40] render/gles: use optimized clears for unblended rects --- render/gles2/pass.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/render/gles2/pass.c b/render/gles2/pass.c index b10ac047d..a70ea1320 100644 --- a/render/gles2/pass.c +++ b/render/gles2/pass.c @@ -260,17 +260,26 @@ static void render_pass_add_rect(struct wlr_render_pass *wlr_pass, const struct wlr_render_color *color = &options->color; struct wlr_box box; - wlr_render_rect_options_get_box(options, pass->buffer->buffer, &box); + struct wlr_buffer *wlr_buffer = pass->buffer->buffer; + wlr_render_rect_options_get_box(options, wlr_buffer, &box); push_gles2_debug(renderer); - setup_blending(color->a == 1.0 ? WLR_RENDER_BLEND_MODE_NONE : options->blend_mode); - - glUseProgram(renderer->shaders.quad.program); - - set_proj_matrix(renderer->shaders.quad.proj, pass->projection_matrix, &box); - glUniform4f(renderer->shaders.quad.color, color->r, color->g, color->b, color->a); - - render(&box, options->clip, renderer->shaders.quad.pos_attrib); + enum wlr_render_blend_mode blend_mode = + color->a == 1.0 ? WLR_RENDER_BLEND_MODE_NONE : options->blend_mode; + if (blend_mode == WLR_RENDER_BLEND_MODE_NONE && + options->clip == NULL && + box.x == 0 && box.y == 0 && + box.width == wlr_buffer->width && + box.height == wlr_buffer->height) { + glClearColor(color->r, color->g, color->b, color->a); + glClear(GL_COLOR_BUFFER_BIT); + } else { + setup_blending(blend_mode); + glUseProgram(renderer->shaders.quad.program); + set_proj_matrix(renderer->shaders.quad.proj, pass->projection_matrix, &box); + glUniform4f(renderer->shaders.quad.color, color->r, color->g, color->b, color->a); + render(&box, options->clip, renderer->shaders.quad.pos_attrib); + } pop_gles2_debug(renderer); } From 1b8c3ea8c5f084f9844476c7a8491951c5646d47 Mon Sep 17 00:00:00 2001 From: liupeng Date: Wed, 4 Mar 2026 09:45:15 +0800 Subject: [PATCH 34/40] screencopy: simplify capture error handling Signed-off-by: liupeng --- types/wlr_screencopy_v1.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/types/wlr_screencopy_v1.c b/types/wlr_screencopy_v1.c index 58e8dc8ba..9916a604c 100644 --- a/types/wlr_screencopy_v1.c +++ b/types/wlr_screencopy_v1.c @@ -505,7 +505,7 @@ static void capture_output(struct wl_client *wl_client, wl_resource_set_implementation(frame->resource, &frame_impl, frame, frame_handle_resource_destroy); - if (output == NULL) { + if (output == NULL || !output->enabled) { wl_resource_set_user_data(frame->resource, NULL); zwlr_screencopy_frame_v1_send_failed(frame->resource); free(frame); @@ -522,10 +522,6 @@ static void capture_output(struct wl_client *wl_client, wl_signal_add(&output->events.destroy, &frame->output_destroy); frame->output_destroy.notify = frame_handle_output_destroy; - if (output == NULL || !output->enabled) { - goto error; - } - struct wlr_renderer *renderer = output->renderer; assert(renderer); From 2d3916614600f3df63257a8db8edb2e32a021916 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 5 Mar 2026 19:54:06 +0100 Subject: [PATCH 35/40] build: bump version to 0.20.0-rc4 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index f054cc250..d9e215afd 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'wlroots', 'c', - version: '0.20.0-rc3', + version: '0.20.0-rc4', license: 'MIT', meson_version: '>=1.3', default_options: [ From 3336d28813698eb6fba908a4dbcf38b79ed2e3db Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Thu, 5 Mar 2026 10:23:06 +0000 Subject: [PATCH 36/40] 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. --- 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 75fa15f4d..0e3a57823 100644 --- a/types/ext_image_capture_source_v1/output.c +++ b/types/ext_image_capture_source_v1/output.c @@ -110,6 +110,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; } @@ -123,7 +127,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 14b3c96c1e4d5d9952b2e59c0bb3283707610930 Mon Sep 17 00:00:00 2001 From: YaoBing Xiao Date: Fri, 6 Mar 2026 15:59:18 +0800 Subject: [PATCH 37/40] treewide: make type-check helpers take const pointers --- backend/drm/backend.c | 2 +- backend/drm/drm.c | 2 +- backend/headless/backend.c | 2 +- backend/headless/output.c | 2 +- backend/libinput/backend.c | 2 +- backend/multi/backend.c | 2 +- backend/wayland/backend.c | 2 +- backend/wayland/output.c | 2 +- backend/x11/backend.c | 2 +- backend/x11/output.c | 2 +- include/wlr/backend/drm.h | 4 ++-- include/wlr/backend/headless.h | 4 ++-- include/wlr/backend/libinput.h | 2 +- include/wlr/backend/multi.h | 2 +- include/wlr/backend/wayland.h | 4 ++-- include/wlr/backend/x11.h | 4 ++-- include/wlr/render/gles2.h | 6 +++--- include/wlr/render/pixman.h | 4 ++-- include/wlr/render/vulkan.h | 4 ++-- include/xwayland/selection.h | 4 ++-- render/gles2/renderer.c | 4 ++-- render/gles2/texture.c | 2 +- render/pixman/renderer.c | 4 ++-- render/vulkan/renderer.c | 2 +- render/vulkan/texture.c | 2 +- xwayland/selection/incoming.c | 4 ++-- 26 files changed, 38 insertions(+), 38 deletions(-) diff --git a/backend/drm/backend.c b/backend/drm/backend.c index 5a545b192..c2806152b 100644 --- a/backend/drm/backend.c +++ b/backend/drm/backend.c @@ -95,7 +95,7 @@ static const struct wlr_backend_impl backend_impl = { .commit = backend_commit, }; -bool wlr_backend_is_drm(struct wlr_backend *b) { +bool wlr_backend_is_drm(const struct wlr_backend *b) { return b->impl == &backend_impl; } diff --git a/backend/drm/drm.c b/backend/drm/drm.c index fe9dcc341..6c37ab668 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -1299,7 +1299,7 @@ static const struct wlr_output_impl output_impl = { .get_primary_formats = drm_connector_get_primary_formats, }; -bool wlr_output_is_drm(struct wlr_output *output) { +bool wlr_output_is_drm(const struct wlr_output *output) { return output->impl == &output_impl; } diff --git a/backend/headless/backend.c b/backend/headless/backend.c index d03f520b8..0f21b53f4 100644 --- a/backend/headless/backend.c +++ b/backend/headless/backend.c @@ -81,6 +81,6 @@ struct wlr_backend *wlr_headless_backend_create(struct wl_event_loop *loop) { return &backend->backend; } -bool wlr_backend_is_headless(struct wlr_backend *backend) { +bool wlr_backend_is_headless(const struct wlr_backend *backend) { return backend->impl == &backend_impl; } diff --git a/backend/headless/output.c b/backend/headless/output.c index a4cdb17c3..0464e0d1b 100644 --- a/backend/headless/output.c +++ b/backend/headless/output.c @@ -106,7 +106,7 @@ static const struct wlr_output_impl output_impl = { .move_cursor = output_move_cursor, }; -bool wlr_output_is_headless(struct wlr_output *wlr_output) { +bool wlr_output_is_headless(const struct wlr_output *wlr_output) { return wlr_output->impl == &output_impl; } diff --git a/backend/libinput/backend.c b/backend/libinput/backend.c index 121731b31..0c72c6849 100644 --- a/backend/libinput/backend.c +++ b/backend/libinput/backend.c @@ -155,7 +155,7 @@ static const struct wlr_backend_impl backend_impl = { .destroy = backend_destroy, }; -bool wlr_backend_is_libinput(struct wlr_backend *b) { +bool wlr_backend_is_libinput(const struct wlr_backend *b) { return b->impl == &backend_impl; } diff --git a/backend/multi/backend.c b/backend/multi/backend.c index 3d8fb96f5..3a9aca7e6 100644 --- a/backend/multi/backend.c +++ b/backend/multi/backend.c @@ -173,7 +173,7 @@ struct wlr_backend *wlr_multi_backend_create(struct wl_event_loop *loop) { return &backend->backend; } -bool wlr_backend_is_multi(struct wlr_backend *b) { +bool wlr_backend_is_multi(const struct wlr_backend *b) { return b->impl == &backend_impl; } diff --git a/backend/wayland/backend.c b/backend/wayland/backend.c index 14a783b67..0d294b543 100644 --- a/backend/wayland/backend.c +++ b/backend/wayland/backend.c @@ -577,7 +577,7 @@ static const struct wlr_backend_impl backend_impl = { .get_drm_fd = backend_get_drm_fd, }; -bool wlr_backend_is_wl(struct wlr_backend *b) { +bool wlr_backend_is_wl(const struct wlr_backend *b) { return b->impl == &backend_impl; } diff --git a/backend/wayland/output.c b/backend/wayland/output.c index fb4d1f914..c3442f411 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -1027,7 +1027,7 @@ static const struct wlr_output_impl output_impl = { .get_primary_formats = output_get_formats, }; -bool wlr_output_is_wl(struct wlr_output *wlr_output) { +bool wlr_output_is_wl(const struct wlr_output *wlr_output) { return wlr_output->impl == &output_impl; } diff --git a/backend/x11/backend.c b/backend/x11/backend.c index 0983bad0a..20118016d 100644 --- a/backend/x11/backend.c +++ b/backend/x11/backend.c @@ -218,7 +218,7 @@ static const struct wlr_backend_impl backend_impl = { .get_drm_fd = backend_get_drm_fd, }; -bool wlr_backend_is_x11(struct wlr_backend *backend) { +bool wlr_backend_is_x11(const struct wlr_backend *backend) { return backend->impl == &backend_impl; } diff --git a/backend/x11/output.c b/backend/x11/output.c index 51a9c1441..40e601f0b 100644 --- a/backend/x11/output.c +++ b/backend/x11/output.c @@ -711,7 +711,7 @@ void handle_x11_configure_notify(struct wlr_x11_output *output, wlr_output_state_finish(&state); } -bool wlr_output_is_x11(struct wlr_output *wlr_output) { +bool wlr_output_is_x11(const struct wlr_output *wlr_output) { return wlr_output->impl == &output_impl; } diff --git a/include/wlr/backend/drm.h b/include/wlr/backend/drm.h index 3ca6390ab..233bbcb74 100644 --- a/include/wlr/backend/drm.h +++ b/include/wlr/backend/drm.h @@ -39,8 +39,8 @@ struct wlr_drm_lease { struct wlr_backend *wlr_drm_backend_create(struct wlr_session *session, struct wlr_device *dev, struct wlr_backend *parent); -bool wlr_backend_is_drm(struct wlr_backend *backend); -bool wlr_output_is_drm(struct wlr_output *output); +bool wlr_backend_is_drm(const struct wlr_backend *backend); +bool wlr_output_is_drm(const struct wlr_output *output); /** * Get the parent DRM backend, if any. diff --git a/include/wlr/backend/headless.h b/include/wlr/backend/headless.h index 126f1ff21..5487c6085 100644 --- a/include/wlr/backend/headless.h +++ b/include/wlr/backend/headless.h @@ -25,7 +25,7 @@ struct wlr_backend *wlr_headless_backend_create(struct wl_event_loop *loop); struct wlr_output *wlr_headless_add_output(struct wlr_backend *backend, unsigned int width, unsigned int height); -bool wlr_backend_is_headless(struct wlr_backend *backend); -bool wlr_output_is_headless(struct wlr_output *output); +bool wlr_backend_is_headless(const struct wlr_backend *backend); +bool wlr_output_is_headless(const struct wlr_output *output); #endif diff --git a/include/wlr/backend/libinput.h b/include/wlr/backend/libinput.h index 2a4e1996e..cb5ef824d 100644 --- a/include/wlr/backend/libinput.h +++ b/include/wlr/backend/libinput.h @@ -29,7 +29,7 @@ struct libinput_device *wlr_libinput_get_device_handle( struct libinput_tablet_tool *wlr_libinput_get_tablet_tool_handle( struct wlr_tablet_tool *wlr_tablet_tool); -bool wlr_backend_is_libinput(struct wlr_backend *backend); +bool wlr_backend_is_libinput(const struct wlr_backend *backend); bool wlr_input_device_is_libinput(struct wlr_input_device *device); #endif diff --git a/include/wlr/backend/multi.h b/include/wlr/backend/multi.h index c4322d98b..cc3e41b4e 100644 --- a/include/wlr/backend/multi.h +++ b/include/wlr/backend/multi.h @@ -26,7 +26,7 @@ bool wlr_multi_backend_add(struct wlr_backend *multi, void wlr_multi_backend_remove(struct wlr_backend *multi, struct wlr_backend *backend); -bool wlr_backend_is_multi(struct wlr_backend *backend); +bool wlr_backend_is_multi(const struct wlr_backend *backend); bool wlr_multi_is_empty(struct wlr_backend *backend); void wlr_multi_for_each_backend(struct wlr_backend *backend, diff --git a/include/wlr/backend/wayland.h b/include/wlr/backend/wayland.h index c732de68c..7a41b3d09 100644 --- a/include/wlr/backend/wayland.h +++ b/include/wlr/backend/wayland.h @@ -46,7 +46,7 @@ struct wlr_output *wlr_wl_output_create_from_surface(struct wlr_backend *backend /** * Check whether the provided backend is a Wayland backend. */ -bool wlr_backend_is_wl(struct wlr_backend *backend); +bool wlr_backend_is_wl(const struct wlr_backend *backend); /** * Check whether the provided input device is a Wayland input device. @@ -56,7 +56,7 @@ bool wlr_input_device_is_wl(struct wlr_input_device *device); /** * Check whether the provided output device is a Wayland output device. */ -bool wlr_output_is_wl(struct wlr_output *output); +bool wlr_output_is_wl(const struct wlr_output *output); /** * Sets the title of a struct wlr_output which is a Wayland toplevel. diff --git a/include/wlr/backend/x11.h b/include/wlr/backend/x11.h index 1791041ce..8e8bc9870 100644 --- a/include/wlr/backend/x11.h +++ b/include/wlr/backend/x11.h @@ -31,7 +31,7 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend); /** * Check whether this backend is an X11 backend. */ -bool wlr_backend_is_x11(struct wlr_backend *backend); +bool wlr_backend_is_x11(const struct wlr_backend *backend); /** * Check whether this input device is an X11 input device. @@ -41,7 +41,7 @@ bool wlr_input_device_is_x11(struct wlr_input_device *device); /** * Check whether this output device is an X11 output device. */ -bool wlr_output_is_x11(struct wlr_output *output); +bool wlr_output_is_x11(const struct wlr_output *output); /** * Sets the title of a struct wlr_output which is an X11 window. diff --git a/include/wlr/render/gles2.h b/include/wlr/render/gles2.h index 454e7eb0e..87a57fa54 100644 --- a/include/wlr/render/gles2.h +++ b/include/wlr/render/gles2.h @@ -42,9 +42,9 @@ struct wlr_gles2_texture_attribs { bool has_alpha; }; -bool wlr_renderer_is_gles2(struct wlr_renderer *wlr_renderer); -bool wlr_render_timer_is_gles2(struct wlr_render_timer *timer); -bool wlr_texture_is_gles2(struct wlr_texture *texture); +bool wlr_renderer_is_gles2(const struct wlr_renderer *wlr_renderer); +bool wlr_render_timer_is_gles2(const struct wlr_render_timer *timer); +bool wlr_texture_is_gles2(const struct wlr_texture *texture); void wlr_gles2_texture_get_attribs(struct wlr_texture *texture, struct wlr_gles2_texture_attribs *attribs); diff --git a/include/wlr/render/pixman.h b/include/wlr/render/pixman.h index 206dd78c6..2951f5e9e 100644 --- a/include/wlr/render/pixman.h +++ b/include/wlr/render/pixman.h @@ -14,8 +14,8 @@ struct wlr_renderer *wlr_pixman_renderer_create(void); -bool wlr_renderer_is_pixman(struct wlr_renderer *wlr_renderer); -bool wlr_texture_is_pixman(struct wlr_texture *texture); +bool wlr_renderer_is_pixman(const struct wlr_renderer *wlr_renderer); +bool wlr_texture_is_pixman(const struct wlr_texture *texture); pixman_image_t *wlr_pixman_renderer_get_buffer_image( struct wlr_renderer *wlr_renderer, struct wlr_buffer *wlr_buffer); diff --git a/include/wlr/render/vulkan.h b/include/wlr/render/vulkan.h index 50f8c558d..170665dd0 100644 --- a/include/wlr/render/vulkan.h +++ b/include/wlr/render/vulkan.h @@ -25,8 +25,8 @@ VkPhysicalDevice wlr_vk_renderer_get_physical_device(struct wlr_renderer *render VkDevice wlr_vk_renderer_get_device(struct wlr_renderer *renderer); uint32_t wlr_vk_renderer_get_queue_family(struct wlr_renderer *renderer); -bool wlr_renderer_is_vk(struct wlr_renderer *wlr_renderer); -bool wlr_texture_is_vk(struct wlr_texture *texture); +bool wlr_renderer_is_vk(const struct wlr_renderer *wlr_renderer); +bool wlr_texture_is_vk(const struct wlr_texture *texture); void wlr_vk_texture_get_image_attribs(struct wlr_texture *texture, struct wlr_vk_image_attribs *attribs); diff --git a/include/xwayland/selection.h b/include/xwayland/selection.h index 308310232..2f333f8e0 100644 --- a/include/xwayland/selection.h +++ b/include/xwayland/selection.h @@ -82,9 +82,9 @@ void xwm_handle_selection_notify(struct wlr_xwm *xwm, xcb_selection_notify_event_t *event); int xwm_handle_xfixes_selection_notify(struct wlr_xwm *xwm, xcb_xfixes_selection_notify_event_t *event); -bool data_source_is_xwayland(struct wlr_data_source *wlr_source); +bool data_source_is_xwayland(const struct wlr_data_source *wlr_source); bool primary_selection_source_is_xwayland( - struct wlr_primary_selection_source *wlr_source); + const struct wlr_primary_selection_source *wlr_source); void xwm_seat_handle_start_drag(struct wlr_xwm *xwm, struct wlr_drag *drag); diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c index e362daee8..7f01b8acd 100644 --- a/render/gles2/renderer.c +++ b/render/gles2/renderer.c @@ -29,7 +29,7 @@ static const struct wlr_renderer_impl renderer_impl; static const struct wlr_render_timer_impl render_timer_impl; -bool wlr_renderer_is_gles2(struct wlr_renderer *wlr_renderer) { +bool wlr_renderer_is_gles2(const struct wlr_renderer *wlr_renderer) { return wlr_renderer->impl == &renderer_impl; } @@ -40,7 +40,7 @@ struct wlr_gles2_renderer *gles2_get_renderer( return renderer; } -bool wlr_render_timer_is_gles2(struct wlr_render_timer *timer) { +bool wlr_render_timer_is_gles2(const struct wlr_render_timer *timer) { return timer->impl == &render_timer_impl; } diff --git a/render/gles2/texture.c b/render/gles2/texture.c index 9a967ebdb..7460755cd 100644 --- a/render/gles2/texture.c +++ b/render/gles2/texture.c @@ -16,7 +16,7 @@ static const struct wlr_texture_impl texture_impl; -bool wlr_texture_is_gles2(struct wlr_texture *wlr_texture) { +bool wlr_texture_is_gles2(const struct wlr_texture *wlr_texture) { return wlr_texture->impl == &texture_impl; } diff --git a/render/pixman/renderer.c b/render/pixman/renderer.c index 4631a33ab..25c558a9d 100644 --- a/render/pixman/renderer.c +++ b/render/pixman/renderer.c @@ -12,7 +12,7 @@ static const struct wlr_renderer_impl renderer_impl; -bool wlr_renderer_is_pixman(struct wlr_renderer *wlr_renderer) { +bool wlr_renderer_is_pixman(const struct wlr_renderer *wlr_renderer) { return wlr_renderer->impl == &renderer_impl; } @@ -69,7 +69,7 @@ static struct wlr_pixman_buffer *get_buffer( static const struct wlr_texture_impl texture_impl; -bool wlr_texture_is_pixman(struct wlr_texture *texture) { +bool wlr_texture_is_pixman(const struct wlr_texture *texture) { return texture->impl == &texture_impl; } diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index da17a4703..68c5e96eb 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -46,7 +46,7 @@ static bool default_debug = true; static const struct wlr_renderer_impl renderer_impl; -bool wlr_renderer_is_vk(struct wlr_renderer *wlr_renderer) { +bool wlr_renderer_is_vk(const struct wlr_renderer *wlr_renderer) { return wlr_renderer->impl == &renderer_impl; } diff --git a/render/vulkan/texture.c b/render/vulkan/texture.c index 57db97a56..452d787da 100644 --- a/render/vulkan/texture.c +++ b/render/vulkan/texture.c @@ -15,7 +15,7 @@ static const struct wlr_texture_impl texture_impl; -bool wlr_texture_is_vk(struct wlr_texture *wlr_texture) { +bool wlr_texture_is_vk(const struct wlr_texture *wlr_texture) { return wlr_texture->impl == &texture_impl; } diff --git a/xwayland/selection/incoming.c b/xwayland/selection/incoming.c index 85d7775cd..4fa851b2f 100644 --- a/xwayland/selection/incoming.c +++ b/xwayland/selection/incoming.c @@ -249,7 +249,7 @@ struct x11_data_source { static const struct wlr_data_source_impl data_source_impl; bool data_source_is_xwayland( - struct wlr_data_source *wlr_source) { + const struct wlr_data_source *wlr_source) { return wlr_source->impl == &data_source_impl; } @@ -292,7 +292,7 @@ static const struct wlr_primary_selection_source_impl primary_selection_source_impl; bool primary_selection_source_is_xwayland( - struct wlr_primary_selection_source *wlr_source) { + const struct wlr_primary_selection_source *wlr_source) { return wlr_source->impl == &primary_selection_source_impl; } From 67ce318b1fb4a5973e80b1ea721fbb23f210d665 Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Fri, 6 Mar 2026 09:37:31 -0500 Subject: [PATCH 38/40] ci: update dalligi upstream repo --- .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 3dafaa4df313f607730a8f95ac37f822b8f94101 Mon Sep 17 00:00:00 2001 From: hrdl Date: Thu, 19 Feb 2026 20:18:16 +0000 Subject: [PATCH 39/40] render/vulkan: relax minimum Vulkan API version to 1.0 This allows using the vulkan renderer on platforms that provide all the necessary Vulkan extensions. Tested on a Mali G52 platform with Mesa 26.0.0 and 25.3.5, which only support Vulkan API 1.0. --- include/render/vulkan.h | 4 ++++ render/vulkan/renderer.c | 6 ++--- render/vulkan/texture.c | 4 ++-- render/vulkan/vulkan.c | 50 ++++++++++++++++++---------------------- 4 files changed, 32 insertions(+), 32 deletions(-) diff --git a/include/render/vulkan.h b/include/render/vulkan.h index f96619f2c..c5d571ef7 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -56,6 +56,10 @@ struct wlr_vk_device { PFN_vkGetSemaphoreFdKHR vkGetSemaphoreFdKHR; PFN_vkImportSemaphoreFdKHR vkImportSemaphoreFdKHR; PFN_vkQueueSubmit2KHR vkQueueSubmit2KHR; + PFN_vkBindImageMemory2KHR vkBindImageMemory2KHR; + PFN_vkCreateSamplerYcbcrConversionKHR vkCreateSamplerYcbcrConversionKHR; + PFN_vkDestroySamplerYcbcrConversionKHR vkDestroySamplerYcbcrConversionKHR; + PFN_vkGetImageMemoryRequirements2KHR vkGetImageMemoryRequirements2KHR; } api; uint32_t format_prop_count; diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 68c5e96eb..52c87e9e2 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -1188,7 +1188,7 @@ static void vulkan_destroy(struct wlr_renderer *wlr_renderer) { vkDestroyPipelineLayout(dev->dev, pipeline_layout->vk, NULL); vkDestroyDescriptorSetLayout(dev->dev, pipeline_layout->ds, NULL); vkDestroySampler(dev->dev, pipeline_layout->sampler, NULL); - vkDestroySamplerYcbcrConversion(dev->dev, pipeline_layout->ycbcr.conversion, NULL); + renderer->dev->api.vkDestroySamplerYcbcrConversionKHR(dev->dev, pipeline_layout->ycbcr.conversion, NULL); free(pipeline_layout); } @@ -2049,10 +2049,10 @@ struct wlr_vk_pipeline_layout *get_or_create_pipeline_layout( .yChromaOffset = VK_CHROMA_LOCATION_MIDPOINT, .chromaFilter = VK_FILTER_LINEAR, }; - res = vkCreateSamplerYcbcrConversion(renderer->dev->dev, + res = renderer->dev->api.vkCreateSamplerYcbcrConversionKHR(renderer->dev->dev, &conversion_create_info, NULL, &pipeline_layout->ycbcr.conversion); if (res != VK_SUCCESS) { - wlr_vk_error("vkCreateSamplerYcbcrConversion", res); + wlr_vk_error("vkCreateSamplerYcbcrConversionKHR", res); free(pipeline_layout); return NULL; } diff --git a/render/vulkan/texture.c b/render/vulkan/texture.c index 452d787da..d769a0b9e 100644 --- a/render/vulkan/texture.c +++ b/render/vulkan/texture.c @@ -653,7 +653,7 @@ VkImage vulkan_import_dmabuf(struct wlr_vk_renderer *renderer, .sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, }; - vkGetImageMemoryRequirements2(dev, &memri, &memr); + renderer->dev->api.vkGetImageMemoryRequirements2KHR(dev, &memri, &memr); int mem = vulkan_find_mem_type(renderer->dev, 0, memr.memoryRequirements.memoryTypeBits & fdp.memoryTypeBits); if (mem < 0) { @@ -712,7 +712,7 @@ VkImage vulkan_import_dmabuf(struct wlr_vk_renderer *renderer, } } - res = vkBindImageMemory2(dev, mem_count, bindi); + res = renderer->dev->api.vkBindImageMemory2KHR(dev, mem_count, bindi); if (res != VK_SUCCESS) { wlr_vk_error("vkBindMemory failed", res); goto error_image; diff --git a/render/vulkan/vulkan.c b/render/vulkan/vulkan.c index ee7adc011..d1a0d77d3 100644 --- a/render/vulkan/vulkan.c +++ b/render/vulkan/vulkan.c @@ -81,21 +81,6 @@ static VKAPI_ATTR VkBool32 debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT } struct wlr_vk_instance *vulkan_instance_create(bool debug) { - // we require vulkan 1.1 - PFN_vkEnumerateInstanceVersion pfEnumInstanceVersion = - (PFN_vkEnumerateInstanceVersion) - vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceVersion"); - if (!pfEnumInstanceVersion) { - wlr_log(WLR_ERROR, "wlroots requires vulkan 1.1 which is not available"); - return NULL; - } - - uint32_t ini_version; - if (pfEnumInstanceVersion(&ini_version) != VK_SUCCESS || - ini_version < VK_API_VERSION_1_1) { - wlr_log(WLR_ERROR, "wlroots requires vulkan 1.1 which is not available"); - return NULL; - } uint32_t avail_extc = 0; VkResult res; @@ -125,7 +110,18 @@ struct wlr_vk_instance *vulkan_instance_create(bool debug) { } size_t extensions_len = 0; - const char *extensions[1] = {0}; + const char *extensions[8] = {0}; + extensions[extensions_len++] = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME; + extensions[extensions_len++] = VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME; + extensions[extensions_len++] = VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME; + + for (size_t i = 0; i < extensions_len; i++) { + if (!check_extension(avail_ext_props, avail_extc, extensions[i])) { + wlr_log(WLR_ERROR, "vulkan: required instance extension %s not found", + extensions[i]); + goto error; + } + } bool debug_utils_found = false; if (debug && check_extension(avail_ext_props, avail_extc, @@ -140,7 +136,7 @@ struct wlr_vk_instance *vulkan_instance_create(bool debug) { .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .pEngineName = "wlroots", .engineVersion = WLR_VERSION_NUM, - .apiVersion = VK_API_VERSION_1_1, + .apiVersion = VK_API_VERSION_1_0, }; VkInstanceCreateInfo instance_info = { @@ -282,20 +278,10 @@ VkPhysicalDevice vulkan_find_drm_phdev(struct wlr_vk_instance *ini, int drm_fd) for (uint32_t i = 0; i < num_phdevs; ++i) { VkPhysicalDevice phdev = phdevs[i]; - // check whether device supports vulkan 1.1, needed for - // vkGetPhysicalDeviceProperties2 VkPhysicalDeviceProperties phdev_props; vkGetPhysicalDeviceProperties(phdev, &phdev_props); - log_phdev(&phdev_props); - if (phdev_props.apiVersion < VK_API_VERSION_1_1) { - // NOTE: we could additionally check whether the - // VkPhysicalDeviceProperties2KHR extension is supported but - // implementations not supporting 1.1 are unlikely in future - continue; - } - // check for extensions uint32_t avail_extc = 0; res = vkEnumerateDeviceExtensionProperties(phdev, NULL, @@ -474,6 +460,12 @@ struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini, extensions[extensions_len++] = VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME; extensions[extensions_len++] = VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME; // or vulkan 1.2 extensions[extensions_len++] = VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME; // or vulkan 1.3 + extensions[extensions_len++] = VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME; // or vulkan 1.1 + extensions[extensions_len++] = VK_KHR_BIND_MEMORY_2_EXTENSION_NAME; // or vulkan 1.1 + extensions[extensions_len++] = VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME; // or vulkan 1.1 + extensions[extensions_len++] = VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME; // or vulkan 1.1 + extensions[extensions_len++] = VK_KHR_MAINTENANCE_1_EXTENSION_NAME; // or vulkan 1.1 + extensions[extensions_len++] = VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME; // or vulkan 1.1 for (size_t i = 0; i < extensions_len; i++) { if (!check_extension(avail_ext_props, avail_extc, extensions[i])) { @@ -630,6 +622,10 @@ struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini, load_device_proc(dev, "vkGetSemaphoreCounterValueKHR", &dev->api.vkGetSemaphoreCounterValueKHR); load_device_proc(dev, "vkQueueSubmit2KHR", &dev->api.vkQueueSubmit2KHR); + load_device_proc(dev, "vkBindImageMemory2KHR", &dev->api.vkBindImageMemory2KHR); + load_device_proc(dev, "vkCreateSamplerYcbcrConversionKHR", &dev->api.vkCreateSamplerYcbcrConversionKHR); + load_device_proc(dev, "vkDestroySamplerYcbcrConversionKHR", &dev->api.vkDestroySamplerYcbcrConversionKHR); + load_device_proc(dev, "vkGetImageMemoryRequirements2KHR", &dev->api.vkGetImageMemoryRequirements2KHR); if (has_external_semaphore_fd) { load_device_proc(dev, "vkGetSemaphoreFdKHR", &dev->api.vkGetSemaphoreFdKHR); From 9a931d9ffa1f8771c5dc957e2c224192589633b0 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Fri, 6 Mar 2026 18:44:26 -0800 Subject: [PATCH 40/40] scene: fix color format compare bool doesn't really support negative values. Fixes: 7cb3393e7 (scene: send color_management_v1 surface feedback) --- types/scene/surface.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/types/scene/surface.c b/types/scene/surface.c index bce8c74a6..7b4d7fd36 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -38,7 +38,7 @@ static struct wlr_output *get_surface_frame_pacing_output(struct wlr_surface *su return frame_pacing_output; } -static bool get_tf_preference(enum wlr_color_transfer_function tf) { +static int get_tf_preference(enum wlr_color_transfer_function tf) { switch (tf) { case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22: return 0; @@ -52,7 +52,7 @@ static bool get_tf_preference(enum wlr_color_transfer_function tf) { abort(); // unreachable } -static bool get_primaries_preference(enum wlr_color_named_primaries primaries) { +static int get_primaries_preference(enum wlr_color_named_primaries primaries) { switch (primaries) { case WLR_COLOR_NAMED_PRIMARIES_SRGB: return 0;