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)
This commit is contained in:
lkn 2026-06-21 18:49:19 +02:00
parent c90355b663
commit ec0a94e077
3 changed files with 28 additions and 25 deletions

View File

@ -503,8 +503,8 @@ wl_screen_create_internals(WScreen *scr)
dsfb->id = wl_state.next_synthetic_id++; dsfb->id = wl_state.next_synthetic_id++;
dsfb->width = sz; dsfb->width = sz;
dsfb->height = sz; dsfb->height = sz;
struct wlr_scene_tree *ds_parent = wl_state.level_trees[WL_LAYER_NORMAL] struct wlr_scene_tree *ds_parent = wl_state.level_trees[WL_LAYER_FLOATING]
? wl_state.level_trees[WL_LAYER_NORMAL] : &wl_state.scene->tree; ? wl_state.level_trees[WL_LAYER_FLOATING] : &wl_state.scene->tree;
dsfb->tree = wlr_scene_tree_create(ds_parent); dsfb->tree = wlr_scene_tree_create(ds_parent);
if (dsfb->tree) { if (dsfb->tree) {
dsfb->scene_buf = wlr_scene_buffer_create(dsfb->tree, &dsfb->base); dsfb->scene_buf = wlr_scene_buffer_create(dsfb->tree, &dsfb->base);

View File

@ -124,30 +124,36 @@ wl_stacking_restack(WNativeWindow *windows, int count)
* Process bottom-to-top: last raise_to_top call wins, so the * Process bottom-to-top: last raise_to_top call wins, so the
* window at index 0 ends up on top within its level bucket. * window at index 0 ends up on top within its level bucket.
* *
* Each window is represented by its toplevel frame_buf ID. We * When windows span different parent trees (level buckets),
* raise fb->tree — the grouping tree that owns both the frame * raise_to_top only orders within a parent. Use place_below
* pixel buffer and the reparented client surface — so the entire * for cross-parent ordering of adjacent entries. */
* window moves as one atomic unit. Child frame_bufs (titlebar,
* resizebar) are already children of fb->tree and move with it. */
int i; 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]; WNativeWindow win = windows[i];
struct wl_frame_buf *fb = frame_buf_find(win); struct wl_frame_buf *fb = frame_buf_find(win);
struct wlr_scene_node *node = NULL;
if (fb) { if (fb) {
/* Raise the grouping tree: moves frame + client together. */ node = fb->tree ? &fb->tree->node : (fb->scene_buf ? &fb->scene_buf->node : NULL);
if (fb->tree) } else {
wlr_scene_node_raise_to_top(&fb->tree->node); struct wl_toplevel_view *v = wl_find_view_by_id(win);
else if (fb->scene_buf) if (v && v->scene_node)
wlr_scene_node_raise_to_top(&fb->scene_buf->node); node = &v->scene_node->node;
continue;
} }
/* Fallback: unframed toplevel view (no frame_buf). */ if (node) nodes[n++] = node;
struct wl_toplevel_view *v = wl_find_view_by_id(win); }
if (v) { /* Place in order: nodes[0] on top, nodes[n-1] on bottom.
wl_list_remove(&v->link); * Raise top node, then place each subsequent one below its predecessor. */
wl_list_insert(&wl_state.toplevel_list, &v->link); for (i = 0; i < n; i++) {
if (v->scene_node) if (i == 0) {
wlr_scene_node_raise_to_top(&v->scene_node->node); 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]);
} }
} }
} }

View File

@ -94,12 +94,9 @@ wl_window_map(WNativeWindow win)
return; /* parent not visible yet */ return; /* parent not visible yet */
} }
wlr_scene_node_set_enabled(&fb->scene_buf->node, true); wlr_scene_node_set_enabled(&fb->scene_buf->node, true);
/* Raise the tree (raises entire window assembly together) */
if (fb->tree) { if (fb->tree) {
wlr_scene_node_set_enabled(&fb->tree->node, true); 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) */ /* Also enable children (and grandchildren) */
struct wl_frame_buf *child; struct wl_frame_buf *child;
wl_list_for_each(child, &wl_state.frame_list, link) { wl_list_for_each(child, &wl_state.frame_list, link) {