Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
fix: don't park viewport-hidden windows in hidden_tree — the off-screen reveal delay
Windows whose box is fully outside the viewport get
rendering_requested.hidden=true from the arrange pass, and the WM
render_finish reorder pass parked their trees under the disabled
hidden_tree. But un-hiding happens on camera-motion frames
(update_viewport_local -> arrange -> render_viewport_update), which
deliberately never run a transaction: the motion path re-enabled the
window's node, yet the tree was still a child of the disabled
hidden_tree, so a window scrolling into view during an overview enter or
manual zoom-out rendered nothing until the next unrelated manage
transaction happened to run the reorder pass (status updates made that
~1s — the user-visible pop-in delay; with no transaction it never
appeared at all).
Hidden windows now keep their normal layer parent and stacking slot and
are hidden purely by their disabled scene node — which Window::
render_finish and render_viewport_update both already maintain. The
node-enable flip on a motion frame is then sufficient to reveal them the
frame their box intersects the viewport. Hidden fullscreen windows no
longer set found_fullscreen so shell-surface layering still follows only
visible fullscreen windows. hidden_tree remains in use for
WindowState::Init windows.
Diagnosed with the /tmp/cce-ovdbg scene-state dump: state-side hidden
flipped false the frame the box crossed the viewport edge while the tree
stayed under the disabled hidden_tree until the next reorder.
Co-Authored-By: Claude Fable 5 <[email protected]>
src/server/window_manager.rs | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/src/server/window_manager.rs b/src/server/window_manager.rs
index a941e42..1c958fe 100644
--- a/src/server/window_manager.rs
+++ b/src/server/window_manager.rs
@@ -1549,10 +1549,17 @@ impl WindowManager {
crate::wm_node::WmNodeType::Window(window) => {
(*window).render_finish();
if reorder {
- if (*window).rendering_requested.hidden {
- ffi::wlr_scene_node_reparent((*window).tree as *mut _, (*self.server).scene.hidden_tree);
- ffi::wlr_scene_node_reparent((*window).popup_tree as *mut _, (*self.server).scene.hidden_tree);
- } else {
+ {
+ // Viewport-hidden windows are NOT parked under the
+ // disabled hidden_tree: they keep their normal layer
+ // parent and stacking slot, hidden purely by their
+ // disabled node (render_finish and
+ // render_viewport_update both own that flag).
+ // Un-hiding happens on camera-motion frames, which
+ // never run this reorder pass — a window parked here
+ // stayed invisible after scrolling into view until
+ // the next unrelated transaction reparented it (the
+ // off-screen reveal delay in overview/zoom).
let layer = if (*window).get_app_id_string().as_deref() == Some("cce-wallpaper") {
(*self.server).scene.layers.background
} else if rendered_fullscreen(window) {
@@ -1594,7 +1601,7 @@ impl WindowManager {
} else {
ffi::wlr_scene_node_raise_to_top((*window).tree as *mut _);
}
- if rendered_fullscreen(window) {
+ if !(*window).rendering_requested.hidden && rendered_fullscreen(window) {
found_fullscreen = true;
}