From ef2eff8f654884406c8989baf1ad345a2da43a58 Mon Sep 17 00:00:00 2001 From: srdusr <99972264+srdusr@users.noreply.github.com> Date: Thu, 6 Feb 2025 09:28:00 +0200 Subject: Fix layer surfaces spuriously hiding/re-showing on their own realization sync_layer_visibility could not tell a real hide (null-buffer commit on an already-visible surface) apart from a layer-shell client's ordinary realization sequence (commit with no buffer -> configure -> ack-commit with no buffer again -> attach real content): both look like "committed, no buffer" from has_buffer alone. Every layer surface's first realization was spuriously unmapped and immediately remapped, doubling LayerMap arrange() passes on every single popup open. Live-reproduced via an AGS peer session: a full-monitor click-outside-to- close popup surface came back from a hit-test with geometry wider than the real output after several open/close cycles on a wl_surface GTK had reused across role destroy/recreate, and sat in the Top layer above every real window with no input region set - silently swallowing clicks meant for windows, dropdowns, and CSD title bars alike. layer_surfaces_shown_once now gates the hide path on a surface having actually shown a buffer at least once, and is cleared in layer_destroyed so a reused wl_surface's next role starts clean rather than inheriting the previous role's flag. --- crates/wayland/src/state/mod.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'crates/wayland/src/state/mod.rs') diff --git a/crates/wayland/src/state/mod.rs b/crates/wayland/src/state/mod.rs index e5b4d6a..5643ebc 100644 --- a/crates/wayland/src/state/mod.rs +++ b/crates/wayland/src/state/mod.rs @@ -303,6 +303,27 @@ pub(crate) struct CompState { /// again on its own - this is the only way `sync_layer_visibility` /// can re-map it once the client commits real content again. pub(crate) hidden_layer_surfaces: HashMap, + /// Layer surfaces `sync_layer_visibility` has seen commit an actual + /// buffer at least once. A layer-shell client's realization sequence is + /// (commit with no buffer -> receive configure -> commit with no buffer + /// again to ack it -> *then* attach and commit real content), and that + /// middle ack-commit is indistinguishable from a real "hide" (a null- + /// buffer commit on an already-visible surface) by buffer-presence + /// alone - both are "committed, no buffer". Without this, every + /// layer-shell surface's very first realization spuriously unmapped and + /// immediately remapped itself through `sync_layer_visibility`, doubling + /// the number of `LayerMap::arrange()` passes on every single popup + /// open (confirmed live: an AGS popup toggle logged unmapped/re-mapped + /// within the same ~300ms window every time) and giving a second, + /// needless remap for `arrange()`'s zone/size math to disagree with + /// itself across - the leading suspect for a live-reproduced bug where + /// a full-monitor click-catcher popup's hit-tested geometry came back + /// wider than the real output after several open/close cycles. Real + /// hides (a role kept alive, buffer later reattached) still work: + /// `sync_layer_visibility`'s own `has_buffer` branch inserts here before + /// this set is ever consulted, so a surface only reaches the unmap path + /// once it has legitimately shown something. + pub(crate) layer_surfaces_shown_once: HashSet, pub(crate) decorations: HashMap, /// The top border strip's rounded-corner bitmap, cached the same way /// and at the same trigger points as `decorations` (built in -- cgit v1.2.3