From ec0a94e07730f53336b475666a82daa67a371f99 Mon Sep 17 00:00:00 2001 From: lkn Date: Sun, 21 Jun 2026 18:49:19 +0200 Subject: [PATCH] wayland: fix dock ghost shadow z-order (render below icon) The dock_shadow was rendering above the dragged icon because: 1. It was in WL_LAYER_OVERLAY/DOCK, above the dock icons in WL_LAYER_FLOATING 2. wl_stacking_restack could not order nodes across different parent trees 3. wl_window_map auto-raised on map, undoing any prior restack Fixes: - Place dock_shadow in WL_LAYER_FLOATING (where WMDockLevel=5 maps) - Rewrite wl_stacking_restack to reparent nodes into the same tree and use wlr_scene_node_place_below for guaranteed ordering - Remove auto-raise from wl_window_map (explicit wRaiseFrame handles it) --- src/backend/wayland/wl_screen.c | 4 +-- src/backend/wayland/wl_stacking.c | 44 ++++++++++++++++++------------- src/backend/wayland/wl_window.c | 5 +--- 3 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/backend/wayland/wl_screen.c b/src/backend/wayland/wl_screen.c index 5e8a355..c936e0f 100644 --- a/src/backend/wayland/wl_screen.c +++ b/src/backend/wayland/wl_screen.c @@ -503,8 +503,8 @@ wl_screen_create_internals(WScreen *scr) dsfb->id = wl_state.next_synthetic_id++; dsfb->width = sz; dsfb->height = sz; - struct wlr_scene_tree *ds_parent = wl_state.level_trees[WL_LAYER_NORMAL] - ? wl_state.level_trees[WL_LAYER_NORMAL] : &wl_state.scene->tree; + struct wlr_scene_tree *ds_parent = wl_state.level_trees[WL_LAYER_FLOATING] + ? wl_state.level_trees[WL_LAYER_FLOATING] : &wl_state.scene->tree; dsfb->tree = wlr_scene_tree_create(ds_parent); if (dsfb->tree) { dsfb->scene_buf = wlr_scene_buffer_create(dsfb->tree, &dsfb->base); diff --git a/src/backend/wayland/wl_stacking.c b/src/backend/wayland/wl_stacking.c index 320ab37..1c2b4e6 100644 --- a/src/backend/wayland/wl_stacking.c +++ b/src/backend/wayland/wl_stacking.c @@ -124,30 +124,36 @@ wl_stacking_restack(WNativeWindow *windows, int count) * Process bottom-to-top: last raise_to_top call wins, so the * window at index 0 ends up on top within its level bucket. * - * Each window is represented by its toplevel frame_buf ID. We - * raise fb->tree — the grouping tree that owns both the frame - * pixel buffer and the reparented client surface — so the entire - * window moves as one atomic unit. Child frame_bufs (titlebar, - * resizebar) are already children of fb->tree and move with it. */ + * When windows span different parent trees (level buckets), + * raise_to_top only orders within a parent. Use place_below + * for cross-parent ordering of adjacent entries. */ int i; - for (i = count - 1; i >= 0; i--) { + /* Collect nodes */ + struct wlr_scene_node *nodes[64]; + int n = 0; + for (i = 0; i < count && n < 64; i++) { WNativeWindow win = windows[i]; struct wl_frame_buf *fb = frame_buf_find(win); + struct wlr_scene_node *node = NULL; if (fb) { - /* Raise the grouping tree: moves frame + client together. */ - if (fb->tree) - wlr_scene_node_raise_to_top(&fb->tree->node); - else if (fb->scene_buf) - wlr_scene_node_raise_to_top(&fb->scene_buf->node); - continue; + node = fb->tree ? &fb->tree->node : (fb->scene_buf ? &fb->scene_buf->node : NULL); + } else { + struct wl_toplevel_view *v = wl_find_view_by_id(win); + if (v && v->scene_node) + node = &v->scene_node->node; } - /* Fallback: unframed toplevel view (no frame_buf). */ - struct wl_toplevel_view *v = wl_find_view_by_id(win); - if (v) { - wl_list_remove(&v->link); - wl_list_insert(&wl_state.toplevel_list, &v->link); - if (v->scene_node) - wlr_scene_node_raise_to_top(&v->scene_node->node); + if (node) nodes[n++] = node; + } + /* Place in order: nodes[0] on top, nodes[n-1] on bottom. + * Raise top node, then place each subsequent one below its predecessor. */ + for (i = 0; i < n; i++) { + if (i == 0) { + wlr_scene_node_raise_to_top(nodes[i]); + } else { + /* Reparent to same parent if needed for place_below to work */ + if (nodes[i]->parent != nodes[i-1]->parent) + wlr_scene_node_reparent(nodes[i], (struct wlr_scene_tree *)nodes[i-1]->parent); + wlr_scene_node_place_below(nodes[i], nodes[i-1]); } } } diff --git a/src/backend/wayland/wl_window.c b/src/backend/wayland/wl_window.c index 5bf93f5..21eac08 100644 --- a/src/backend/wayland/wl_window.c +++ b/src/backend/wayland/wl_window.c @@ -94,12 +94,9 @@ wl_window_map(WNativeWindow win) return; /* parent not visible yet */ } wlr_scene_node_set_enabled(&fb->scene_buf->node, true); - /* Raise the tree (raises entire window assembly together) */ if (fb->tree) { wlr_scene_node_set_enabled(&fb->tree->node, true); - wlr_scene_node_raise_to_top(&fb->tree->node); - } else if (!fb->parent_id) - wlr_scene_node_raise_to_top(&fb->scene_buf->node); + } /* Also enable children (and grandchildren) */ struct wl_frame_buf *child; wl_list_for_each(child, &wl_state.frame_list, link) {