windowmaker-wl/.claude
Window Maker 245d0077b5 wayland: implement correct stacking model with scene-graph level trees
Three interlocking problems were present in the Wayland stacking
implementation.

1. Wrong scene-graph nodes targeted by stacking operations

Each managed window groups its frame pixels and client surface under a
single wlr_scene_tree (fb->tree):

  fb->tree
    fb->scene_buf       (frame decoration pixels)
    child_frame_bufs    (titlebar / resizebar sub-frames)
    v->scene_node       (client surface, reparented in)

wl_stacking_restack was raising fb->scene_buf -- a child inside fb->tree
-- instead of fb->tree itself, a no-op for inter-window ordering. Three
redundant loops then tried to separately raise the client surface and child
frames, also to no effect. wl_stacking_lower had the same bug.

Fix: raise/lower fb->tree in both functions so the entire window assembly
moves as one atomic unit.

2. Flat scene graph -- no structural level ordering

All nodes were direct children of scene->tree, so creation order and
raise_to_top calls determined Z-order. A normal window could accidentally
render above a floating window after any CommitStacking call.

Fix: introduce 9 wlr_scene_tree level buckets created at scene init time
in bottom-to-top order:

  0 WL_LAYER_BACKGROUND  background rect / wallpaper
  1 WL_LAYER_DESKTOP     WMDesktopLevel
  2 WL_LAYER_SUNKEN      WMSunkenLevel
  3 WL_LAYER_NORMAL      WMNormalLevel  (default for new frames)
  4 WL_LAYER_FLOATING    WMFloatingLevel / WINGs dialogs
  5 WL_LAYER_MENU        WMMainMenuWindowLevel / OR-redirect windows
  6 WL_LAYER_DOCK        WMStatusWindowLevel / dock panel surface
  7 WL_LAYER_FULLSCREEN  WMFullscreenLevel
  8 WL_LAYER_OVERLAY     WMModalPanel / WMPopUp / XOR overlay

Every scene node is assigned to the correct bucket at creation.
A new stacking_set_level vtable slot called from AddToStackList moves
fb->tree to the right bucket whenever a window is added or its level
changes via ChangeStackingLevel. X11 backend implements it as a no-op.

3. XDG client surface not reparented into fb->tree

For XDG toplevels the map handler called wManageWindow before creating
view->scene_node, so the reparent inside wl_client_reparent was a no-op.
The client surface then lived as a flat sibling of fb->tree in
WL_LAYER_NORMAL, never moving with its frame during stacking operations.

Fix: after creating view->scene_node, check if frame_id is already set
and do the deferred reparent immediately.

4. Hit-testing broken by level-tree hierarchy

frame_buf_at walked up from the hit scene node to the first direct child
of scene->tree. With level buckets that stopped at the level_tree node
instead of fb->tree, making every window in the same bucket appear to be
the same window. Client surfaces were not blocking clicks on frames
beneath them, and titlebar sub-frames were not distinguished from the
top-level frame, breaking move and resize initiation.

Fix: rewrite frame_buf_at with a two-pass approach:
  Pass 1 -- exact match: check if the topmost node is a child frame_buf
    scene_buf such as titlebar, resizebar, or button; return it directly.
  Pass 2 -- walk up stopping when parent is a level_tree bucket, then
    match that window-root node to a top-level frame_buf tree.
  Return NULL if the hit was on a client surface.

ROADMAP: mark A1 done.
2026-05-15 09:11:46 +02:00
..
settings.local.json wayland: implement correct stacking model with scene-graph level trees 2026-05-15 09:11:46 +02:00