#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include "../../screen.h" #include "wl_types.h" struct wl_frame_buf *frame_buf_find(WNativeWindow id); struct wl_toplevel_view *wl_find_view_by_id(WNativeWindow id); /* ------------------------------------------------------------------ */ /* Stacking */ /* ------------------------------------------------------------------ */ /* Map a WM window level integer to a level_trees[] bucket index. * Buckets are ordered 0=lowest (WL_LAYER_BACKGROUND) to 8=highest * (WL_LAYER_OVERLAY), matching the bottom-to-top creation order of * level_trees[]. Does not map WL_LAYER_BACKGROUND (no WM level). */ int level_to_bucket(int level) { if (level < -1) return WL_LAYER_DESKTOP; if (level < 0) return WL_LAYER_SUNKEN; if (level < 3) return WL_LAYER_NORMAL; if (level < 20) return WL_LAYER_FLOATING; if (level < 21) return WL_LAYER_MENU; if (level < 50) return WL_LAYER_DOCK; if (level < 100) return WL_LAYER_FULLSCREEN; return WL_LAYER_OVERLAY; } /* Return the level_trees[] entry for a given WM level integer. * Falls back to the root scene tree only if the scene is not yet * initialised, which cannot happen during normal window management. */ struct wlr_scene_tree * level_tree_for(int level) { int b = level_to_bucket(level); if (wl_state.level_trees[b]) return wl_state.level_trees[b]; /* Scene not yet initialised — this window will be invisible. */ return &wl_state.scene->tree; } void wl_stacking_set_level(WNativeWindow win, int level) { /* Move the frame's grouping tree into the correct level bucket so * that the scene graph structurally enforces level ordering. All * other nodes (background rect, dock panel, OR views, overlays) * are already placed in their fixed buckets at creation time. */ struct wl_frame_buf *fb = frame_buf_find(win); if (!fb || !fb->tree) return; struct wlr_scene_tree *target = level_tree_for(level); if (fb->tree->node.parent != target) wlr_scene_node_reparent(&fb->tree->node, target); } void wl_stacking_raise(WNativeWindow win) { 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->frame_id) { struct wl_frame_buf *fb = frame_buf_find(v->frame_id); if (fb && fb->tree) { wlr_scene_node_raise_to_top(&fb->tree->node); return; } } if (v->scene_node) wlr_scene_node_raise_to_top(&v->scene_node->node); return; } struct wl_frame_buf *fb = frame_buf_find(win); if (fb) { 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); } return; } } void wl_stacking_lower(WNativeWindow win) { /* Frame-buf path: operate on the grouping tree, not the inner buffer * node. Lowering fb->tree moves the whole window (frame pixels + * reparented client surface) together, within its level bucket. */ struct wl_frame_buf *fb = frame_buf_find(win); if (fb) { if (fb->tree) wlr_scene_node_lower_to_bottom(&fb->tree->node); else if (fb->scene_buf) wlr_scene_node_lower_to_bottom(&fb->scene_buf->node); return; } /* Unframed toplevel view (no frame_buf wrapper). */ 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.prev, &v->link); if (v->scene_node) wlr_scene_node_lower_to_bottom(&v->scene_node->node); } } void wl_stacking_restack(WNativeWindow *windows, int count) { /* Reorder scene graph to match the WM stacking order. * * windows[] is ordered top-to-bottom (index 0 = topmost). * 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. */ int i; for (i = count - 1; i >= 0; i--) { WNativeWindow win = windows[i]; struct wl_frame_buf *fb = frame_buf_find(win); 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; } /* 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); } } } int wl_stacking_query(WNativeWindow root, WNativeWindow **out, unsigned int *count) { /* return synthetic IDs in Z-order (top to bottom). * RemakeStackList will find no WM_CTX_STACK entries for them * and set window_count=0, but returning 1 avoids the "could not * get window list" warning. */ struct wl_toplevel_view *v; unsigned int n = 0, i = 0; WNativeWindow *wins; (void)root; wl_list_for_each(v, &wl_state.toplevel_list, link) n++; if (n == 0) { *out = NULL; *count = 0; return 1; } wins = wmalloc(n * sizeof(WNativeWindow)); wl_list_for_each(v, &wl_state.toplevel_list, link) wins[i++] = v->id; *out = wins; *count = n; return 1; }