176 lines
6.1 KiB
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;
|
|
}
|