diff options
| author | srdusr <[email protected]> | 2025-02-06 09:28:00 +0200 |
|---|---|---|
| committer | srdusr <[email protected]> | 2025-02-06 09:28:00 +0200 |
| commit | ef2eff8f654884406c8989baf1ad345a2da43a58 (patch) | |
| tree | 6638e1b0d5fb573ecc8b8bc5e51ebeb3dfddee42 /crates/wayland/src/state/layers.rs | |
| parent | d25f28c5a779438bef01106ba2b28874e24d5029 (diff) | |
| download | srdwm-ef2eff8f654884406c8989baf1ad345a2da43a58.tar.gz srdwm-ef2eff8f654884406c8989baf1ad345a2da43a58.zip | |
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.
Diffstat (limited to 'crates/wayland/src/state/layers.rs')
| -rw-r--r-- | crates/wayland/src/state/layers.rs | 25 |
1 files changed, 8 insertions, 17 deletions
diff --git a/crates/wayland/src/state/layers.rs b/crates/wayland/src/state/layers.rs index 34db3f1..ee4c022 100644 --- a/crates/wayland/src/state/layers.rs +++ b/crates/wayland/src/state/layers.rs @@ -61,29 +61,13 @@ impl CompState { } let has_buffer = with_renderer_surface_state(surface, |state: &mut RendererSurfaceState| state.buffer().is_some()).unwrap_or(false); - // TEMPORARY diagnostic for the "AGS dock reserves its exclusive - // zone but never paints" investigation, relayed from the AGS peer - // session - this function's own `has_buffer`-based hide/show logic - // (see the doc comment above) is the leading suspect: if a client - // ever legitimately commits a null buffer for a reason other than - // an intentional hide (an internal `Gtk.Revealer` transition - // artifact, a resize-in-progress commit), this treats it as a hide, - // `unmap_layer`s it, and requires a *later* has-buffer commit to - // ever come back - which would look exactly like this symptom if - // the client's own state machine doesn't expect the compositor to - // have done that and never re-triggers one. Logs every call for - // every layer surface, not just suspected ones, since which - // surface is actually affected isn't confirmed yet. Remove once - // resolved. - log::warn!("LAYER-VIS-DIAG surface={:?} has_buffer={has_buffer} already_hidden={}", surface.id(), self.hidden_layer_surfaces.contains_key(surface)); - if has_buffer { + self.layer_surfaces_shown_once.insert(surface.clone()); let Some((output, layer)) = self.hidden_layer_surfaces.remove(surface) else { return }; let mut map = layer_map_for_output(&output); let zone_before = map.non_exclusive_zone(); let _ = map.map_layer(&layer); let zone_after = map.non_exclusive_zone(); - log::warn!("LAYER-VIS-DIAG re-mapped namespace={:?} zone_before={zone_before:?} zone_after={zone_after:?}", layer.namespace()); if zone_after != zone_before { self.pending.borrow_mut().push(CoreEvent::MonitorAdded(srdwm_core::Monitor::new(0, "", srdwm_core::Rect::new(0, 0, 0, 0)))); } @@ -93,6 +77,13 @@ impl CompState { if self.hidden_layer_surfaces.contains_key(surface) { return; } + // A commit with no buffer on a surface that has never shown one + // yet is the ack-configure step of realization, not a hide - see + // `layer_surfaces_shown_once`'s own doc comment. Only a surface + // that has genuinely been visible at least once can be hidden. + if !self.layer_surfaces_shown_once.contains(surface) { + return; + } for output in self.outputs().cloned().collect::<Vec<_>>() { let mut map = layer_map_for_output(&output); let Some(layer) = map.layers().find(|l| l.wl_surface() == surface).cloned() else { continue }; |