From 7610326b8d39bca7ae14b7993ebab6b22db2bce8 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Mon, 30 Mar 2026 11:45:54 +0200 Subject: [PATCH 1/8] ext_image_capture_source_v1/scene: fix extents Currently the width/height of the extents is too small if the first node visited has position/dimensions 0,0,100,100 and the second node has position/dimensions -20,-20,10,10. In this case the current code calculates total extents as -20,-20,100,100 but the correct extents are -20,-20,120,120. References: https://codeberg.org/river/river-classic/issues/17 (cherry picked from commit e22084f63967aa72cb56873aeb48b01fb8dc2c9b) --- types/ext_image_capture_source_v1/scene.c | 28 +++++++++++++---------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/types/ext_image_capture_source_v1/scene.c b/types/ext_image_capture_source_v1/scene.c index 99d34e012..7d4b8928a 100644 --- a/types/ext_image_capture_source_v1/scene.c +++ b/types/ext_image_capture_source_v1/scene.c @@ -34,13 +34,14 @@ struct scene_node_source_frame_event { static size_t last_output_num = 0; -static void _get_scene_node_extents(struct wlr_scene_node *node, struct wlr_box *box, int lx, int ly) { +static void _get_scene_node_extents(struct wlr_scene_node *node, int lx, int ly, + int *x_min, int *y_min, int *x_max, int *y_max) { switch (node->type) { case WLR_SCENE_NODE_TREE:; struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); struct wlr_scene_node *child; wl_list_for_each(child, &scene_tree->children, link) { - _get_scene_node_extents(child, box, lx + child->x, ly + child->y); + _get_scene_node_extents(child, lx + child->x, ly + child->y, x_min, y_min, x_max, y_max); } break; case WLR_SCENE_NODE_RECT: @@ -48,27 +49,30 @@ static void _get_scene_node_extents(struct wlr_scene_node *node, struct wlr_box struct wlr_box node_box = { .x = lx, .y = ly }; scene_node_get_size(node, &node_box.width, &node_box.height); - if (node_box.x < box->x) { - box->x = node_box.x; + if (node_box.x < *x_min) { + *x_min = node_box.x; } - if (node_box.y < box->y) { - box->y = node_box.y; + if (node_box.y < *y_min) { + *y_min = node_box.y; } - if (node_box.x + node_box.width > box->x + box->width) { - box->width = node_box.x + node_box.width - box->x; + if (node_box.x + node_box.width > *x_max) { + *x_max = node_box.x + node_box.width; } - if (node_box.y + node_box.height > box->y + box->height) { - box->height = node_box.y + node_box.height - box->y; + if (node_box.y + node_box.height > *y_max) { + *y_max = node_box.y + node_box.height; } break; } } static void get_scene_node_extents(struct wlr_scene_node *node, struct wlr_box *box) { - *box = (struct wlr_box){ .x = INT_MAX, .y = INT_MAX }; int lx = 0, ly = 0; wlr_scene_node_coords(node, &lx, &ly); - _get_scene_node_extents(node, box, lx, ly); + *box = (struct wlr_box){ .x = INT_MAX, .y = INT_MAX }; + int x_max = INT_MIN, y_max = INT_MIN; + _get_scene_node_extents(node, lx, ly, &box->x, &box->y, &x_max, &y_max); + box->width = x_max - box->x; + box->height = y_max - box->y; } static void source_render(struct scene_node_source *source) { From 13bac746d60ba68cd46eb348c6bf8d2c46b6a9da Mon Sep 17 00:00:00 2001 From: Consolatis <40171-Consolatis@users.noreply.gitlab.freedesktop.org> Date: Fri, 3 Apr 2026 23:03:44 +0200 Subject: [PATCH 2/8] toplevel_capture: allocate new_request argument on the stack This fixes a memory leak. --- .../foreign_toplevel.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/types/ext_image_capture_source_v1/foreign_toplevel.c b/types/ext_image_capture_source_v1/foreign_toplevel.c index e5c75e280..b1110d155 100644 --- a/types/ext_image_capture_source_v1/foreign_toplevel.c +++ b/types/ext_image_capture_source_v1/foreign_toplevel.c @@ -34,18 +34,13 @@ static void foreign_toplevel_manager_handle_create_source(struct wl_client *clie return; } - struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request *request = - calloc(1, sizeof(*request)); - if (request == NULL) { - wl_resource_post_no_memory(manager_resource); - return; - } + struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request request = { + .toplevel_handle = toplevel_handle, + .client = client, + .new_id = new_id, + }; - request->toplevel_handle = toplevel_handle; - request->client = client; - request->new_id = new_id; - - wl_signal_emit_mutable(&manager->events.new_request, request); + wl_signal_emit_mutable(&manager->events.new_request, &request); } static void foreign_toplevel_manager_handle_destroy(struct wl_client *client, From d0fbe1ce56a45b5bc76318d9b68e768b7ec43487 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sun, 12 Apr 2026 00:23:59 +0900 Subject: [PATCH 3/8] wlr-foreign-toplevel-management: add new toplevels to end of list Prior to this patch, when the client binds the manager, the existing toplevel handles were notified in the opposite order of their creation, as wl_list_insert() adds a new handle to the head of the list and wl_list_for_each() iterates from head to tail. (cherry picked from commit 84b45e41578164d1a47bde85ac6a5da3b63f5685) --- types/wlr_foreign_toplevel_management_v1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/wlr_foreign_toplevel_management_v1.c b/types/wlr_foreign_toplevel_management_v1.c index c98457a3d..739e4c185 100644 --- a/types/wlr_foreign_toplevel_management_v1.c +++ b/types/wlr_foreign_toplevel_management_v1.c @@ -530,7 +530,7 @@ wlr_foreign_toplevel_handle_v1_create( return NULL; } - wl_list_insert(&manager->toplevels, &toplevel->link); + wl_list_insert(manager->toplevels.prev, &toplevel->link); toplevel->manager = manager; wl_list_init(&toplevel->resources); From 61ed450247f24a13155990bc57dbe44a7318ccaa Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sun, 12 Apr 2026 00:22:32 +0900 Subject: [PATCH 4/8] ext-foreign-toplevel-list: add new toplevels to end of list Same as the prior patch for wlr-foreign-toplevel-management. (cherry picked from commit 8dd1a7761521bba3b79a1009f1a95377bdfb6488) --- types/wlr_ext_foreign_toplevel_list_v1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/wlr_ext_foreign_toplevel_list_v1.c b/types/wlr_ext_foreign_toplevel_list_v1.c index f20ddc877..cb2f21272 100644 --- a/types/wlr_ext_foreign_toplevel_list_v1.c +++ b/types/wlr_ext_foreign_toplevel_list_v1.c @@ -168,7 +168,7 @@ wlr_ext_foreign_toplevel_handle_v1_create(struct wlr_ext_foreign_toplevel_list_v return NULL; } - wl_list_insert(&list->toplevels, &toplevel->link); + wl_list_insert(list->toplevels.prev, &toplevel->link); toplevel->list = list; if (state->app_id) { toplevel->app_id = strdup(state->app_id); From b9ca8fd6d6f6669ef89e035ab80eba4629453647 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Sat, 11 Apr 2026 13:40:22 +0100 Subject: [PATCH 5/8] wlr_ext_workspace_v1.c: add new workspaces to end of list ...so that panels/bars like xfce4-panel and waybar show workspaces in the right order. (cherry picked from commit e8c03e9ce9aa54b6c1866c8086cdcd8830399830) --- types/wlr_ext_workspace_v1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/wlr_ext_workspace_v1.c b/types/wlr_ext_workspace_v1.c index 7aaf80338..95b8a4997 100644 --- a/types/wlr_ext_workspace_v1.c +++ b/types/wlr_ext_workspace_v1.c @@ -790,7 +790,7 @@ struct wlr_ext_workspace_handle_v1 *wlr_ext_workspace_handle_v1_create( wl_array_init(&workspace->coordinates); wl_signal_init(&workspace->events.destroy); - wl_list_insert(&manager->workspaces, &workspace->link); + wl_list_insert(manager->workspaces.prev, &workspace->link); struct wlr_ext_workspace_manager_v1_resource *manager_res; wl_list_for_each(manager_res, &manager->resources, link) { From 7310756297003c8cfc3c280f305b8ed9b2b0797b Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 6 Apr 2026 15:16:12 +0200 Subject: [PATCH 6/8] wlr_virtual_keyboard_v1: specify size when creating keymap xkb_keymap_new_from_string() assumes that the string is NULL-terminated, but we don't check this so the function might access outside the mmap'ed region. Use the safer xkb_keymap_new_from_buffer() function. Reported-by: Julian Orth Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/work_items/4072 (cherry picked from commit c393fb6bfa994e18c1f3cecd7cc306b0f6b49191) --- types/wlr_virtual_keyboard_v1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/wlr_virtual_keyboard_v1.c b/types/wlr_virtual_keyboard_v1.c index 011fbfec7..465b97dd3 100644 --- a/types/wlr_virtual_keyboard_v1.c +++ b/types/wlr_virtual_keyboard_v1.c @@ -52,7 +52,7 @@ static void virtual_keyboard_keymap(struct wl_client *client, if (data == MAP_FAILED) { goto fd_fail; } - struct xkb_keymap *keymap = xkb_keymap_new_from_string(context, data, + struct xkb_keymap *keymap = xkb_keymap_new_from_buffer(context, data, size, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); munmap(data, size); if (!keymap) { From 5d2172b7faf6e6e197e2040d1ebe073dc0145d0c Mon Sep 17 00:00:00 2001 From: Consolatis <40171-Consolatis@users.noreply.gitlab.freedesktop.org> Date: Thu, 2 Apr 2026 00:53:06 +0200 Subject: [PATCH 7/8] render/pixman: fix bilinear filtering to match gles2 renderer Before this patch the pixman renderer would use "constant padding" for bilinear scaling which meant that the edges would either be dark or turn transparent. The effect was most obvious when trying to scale a single row buffer to a height like 100. The center would have the desired color but the edges to both sides would fade into transparency. We now use PIXMAN_REPEAT_PAD which clamps out-of-bound pixels and seems to match the behavior of the gles2 renderer. (cherry picked from commit c66a910753941c905299e62d9e31a4e90c0bbe98) --- render/pixman/pass.c | 1 + 1 file changed, 1 insertion(+) diff --git a/render/pixman/pass.c b/render/pixman/pass.c index d3ee17dca..af738bb42 100644 --- a/render/pixman/pass.c +++ b/render/pixman/pass.c @@ -159,6 +159,7 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, switch (options->filter_mode) { case WLR_SCALE_FILTER_BILINEAR: + pixman_image_set_repeat(texture->image, PIXMAN_REPEAT_PAD); pixman_image_set_filter(texture->image, PIXMAN_FILTER_BILINEAR, NULL, 0); break; case WLR_SCALE_FILTER_NEAREST: From 1688cfb814ea1b09f626c5d6b0b06ae28ab8b2cc Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Mon, 30 Mar 2026 17:13:57 +0200 Subject: [PATCH 8/8] keyboard: fix modifiers event when no keymap set Actually send the modifiers event when there is no keymap set. Compositors may need lower-level "raw" access to the key/modifiers events from the backend. Currently it is impossible for a compositor to get modifier events from the backend without them being filtered through an xkb_state controlled by wlroots. I need this feature for river in order to fix some keyboard state synchronization bugs. Note that setting a keymap resets the modifiers so I don't think setting wlr_keyboard->modifiers directly here is a concern. (cherry picked from commit 9de0ec308917262f8380e5a0c6d6110464f64314) --- types/wlr_keyboard.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/types/wlr_keyboard.c b/types/wlr_keyboard.c index a24335b3a..adc705db5 100644 --- a/types/wlr_keyboard.c +++ b/types/wlr_keyboard.c @@ -84,6 +84,16 @@ void wlr_keyboard_notify_modifiers(struct wlr_keyboard *keyboard, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { if (keyboard->xkb_state == NULL) { + if (keyboard->modifiers.depressed != mods_depressed || + keyboard->modifiers.latched != mods_latched || + keyboard->modifiers.locked != mods_locked || + keyboard->modifiers.group != group) { + keyboard->modifiers.depressed = mods_depressed; + keyboard->modifiers.latched = mods_latched; + keyboard->modifiers.locked = mods_locked; + keyboard->modifiers.group = group; + wl_signal_emit_mutable(&keyboard->events.modifiers, keyboard); + } return; } xkb_state_update_mask(keyboard->xkb_state, mods_depressed, mods_latched,