windowmaker-wl/src/backend/wayland/wl_stacking.c

176 lines
6.1 KiB
C

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include <stdlib.h>
#include <wayland-server-core.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/xwayland.h>
#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;
}