srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/wayland/src/state
diff options
context:
space:
mode:
Diffstat (limited to 'crates/wayland/src/state')
-rw-r--r--crates/wayland/src/state/geometry.rs42
-rw-r--r--crates/wayland/src/state/lifecycle.rs18
-rw-r--r--crates/wayland/src/state/mod.rs45
3 files changed, 54 insertions, 51 deletions
diff --git a/crates/wayland/src/state/geometry.rs b/crates/wayland/src/state/geometry.rs
index d3c7ab8..9780ae3 100644
--- a/crates/wayland/src/state/geometry.rs
+++ b/crates/wayland/src/state/geometry.rs
@@ -315,29 +315,25 @@ impl CompState {
}
/// True when this window has something on screen to draw a frame
- /// around - it has committed a buffer at least once.
+ /// around, i.e. its client has painted at least once.
///
- /// A toplevel is placed and decorated the moment its role is created,
- /// which is well before the client paints. Drawing it then puts a
- /// border, a titlebar and a shadow around bare desktop, at the guessed
- /// placeholder size (`Window::size_is_provisional`), and that empty
- /// frame then jumps when the real buffer arrives at the real size.
- ///
- /// A window that cannot be resolved to a surface at all counts as
- /// drawable, deliberately: this hides a window only on positive
- /// evidence that it has never drawn, so nothing whose surface plumbing
- /// works differently (an XWayland window, say) can be hidden by a
- /// lookup that simply did not apply to it.
- ///
- /// See `windows_shown_once` for why the answer latches once true.
+ /// Reads one set and nothing else - see `awaiting_first_buffer` for
+ /// why the answer must never depend on a lookup that can fail. Takes
+ /// the set rather than `&self` so a render loop can call it while it
+ /// already holds `self.udev` mutably borrowed.
+ pub(crate) fn has_content(awaiting_first_buffer: &HashSet<WindowId>, id: WindowId) -> bool {
+ !awaiting_first_buffer.contains(&id)
+ }
+
+ /// Whether this window's own surface has a buffer attached right now.
///
- /// Takes the two maps rather than `&self` so a render loop can call it
- /// while it already holds `self.udev` mutably borrowed.
- pub(crate) fn has_content(shown_once: &HashSet<WindowId>, id_to_window: &HashMap<WindowId, DWindow>, id: WindowId) -> bool {
- if shown_once.contains(&id) {
- return true;
- }
- let Some(surface) = id_to_window.get(&id).and_then(crate::elements::window_wl_surface) else { return true };
+ /// Only `CompositorHandler::commit` asks this, about a surface it was
+ /// just handed, to decide whether a window can stop
+ /// `awaiting_first_buffer`. Nothing on the render path may ask it: a
+ /// window whose surface state this does not describe would answer
+ /// "no" and be hidden for ever.
+ pub(crate) fn surface_has_buffer(&self, id: WindowId) -> bool {
+ let Some(surface) = self.id_to_window.get(&id).and_then(crate::elements::window_wl_surface) else { return false };
smithay::backend::renderer::utils::with_renderer_surface_state(
&surface,
|state: &mut smithay::backend::renderer::utils::RendererSurfaceState| state.buffer().is_some(),
@@ -345,10 +341,6 @@ impl CompState {
.unwrap_or(false)
}
- pub(crate) fn window_has_content(&self, id: WindowId) -> bool {
- Self::has_content(&self.windows_shown_once, &self.id_to_window, id)
- }
-
pub(crate) fn effective_frame(&self, id: WindowId, geom: srdwm_core::Rect) -> srdwm_core::Rect {
Self::effective_frame_of(&self.wm, &self.id_to_window, &self.pending_size_configure, id, geom)
}
diff --git a/crates/wayland/src/state/lifecycle.rs b/crates/wayland/src/state/lifecycle.rs
index b6c7f09..f2cddd9 100644
--- a/crates/wayland/src/state/lifecycle.rs
+++ b/crates/wayland/src/state/lifecycle.rs
@@ -19,13 +19,15 @@ impl CompState {
if wm.window(id).is_some_and(|w| w.size_is_provisional) {
self.provisional_size.insert(id);
}
- // The open-slide tween is NOT started here, deliberately.
- // A toplevel role exists well before its client paints
- // anything, so starting it here ran the animation against an
- // empty frame and left the window simply appearing, already at
- // rest. `CompositorHandler::commit` starts it at the first
- // commit that carries a buffer instead - see
- // `windows_shown_once`.
+ // Nothing is drawn for this window until its client paints,
+ // and the open-slide tween starts then rather than here - a
+ // toplevel role exists well before a client's first buffer, so
+ // starting the animation here ran it against an empty frame and
+ // left the window simply appearing, already at rest.
+ // `CompositorHandler::commit` does both. This is the only place
+ // anything is ever put into `awaiting_first_buffer`; see that
+ // field for why that matters.
+ self.awaiting_first_buffer.insert(id);
id
};
@@ -378,7 +380,7 @@ impl CompState {
self.shadow_buffers.remove(&id);
self.border_side_buffers.remove(&id);
self.decoration_signatures.remove(&id);
- self.windows_shown_once.remove(&id);
+ self.awaiting_first_buffer.remove(&id);
self.last_synced_size.remove(&id);
self.content_epoch.remove(&id);
self.rounded_content_buffers.remove(&id);
diff --git a/crates/wayland/src/state/mod.rs b/crates/wayland/src/state/mod.rs
index 44ef772..64990c3 100644
--- a/crates/wayland/src/state/mod.rs
+++ b/crates/wayland/src/state/mod.rs
@@ -461,27 +461,36 @@ pub(crate) struct CompState {
/// 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<WlSurface>,
- /// Windows whose surface has committed a buffer at least once.
+ /// Native Wayland toplevels that srdwm has created but whose client
+ /// has not yet drawn anything.
///
- /// A toplevel exists, and is placed and decorated, from the moment its
- /// role is created - which is well before the client has drawn
- /// anything. Rendering it at that point paints a border and a titlebar
- /// around empty desktop: an empty frame stands there on its own, then
- /// snaps to a different size once the real buffer arrives and the
- /// guessed `800x600` placeholder (`Window::size_is_provisional`) is
- /// replaced. Measured in a nested session: the frame was drawn ~800ms
- /// before any content, one full `TITLEBAR_HEIGHT` too tall, which is
- /// what "the border corners look funny before a window spawns" is.
+ /// A toplevel is placed and decorated from the moment its role is
+ /// created, which is well before the client paints. Rendering it then
+ /// paints a border and a titlebar around empty desktop: an empty frame
+ /// stands there on its own, then snaps to a different size once the
+ /// real buffer arrives and the guessed `800x600` placeholder
+ /// (`Window::size_is_provisional`) is replaced. Measured in a nested
+ /// session: ~800ms of empty frame, one full `TITLEBAR_HEIGHT` too tall.
///
- /// So this gates two things: nothing is drawn for a window that has
- /// never had a buffer, and the open-slide starts at the first buffer
- /// rather than at role creation, so the animation plays where it can
- /// actually be seen instead of finishing against an empty frame.
+ /// This gates two things: nothing is drawn for a window still in this
+ /// set, and the open-slide starts when a window leaves it, so the
+ /// animation plays where it can be seen instead of finishing against
+ /// an empty frame.
///
- /// Same shape, and the same reason, as `layer_surfaces_shown_once`
- /// above: a window that has legitimately shown something once is never
- /// hidden again by this, however its buffer state changes afterward.
- pub(crate) windows_shown_once: HashSet<WindowId>,
+ /// Membership is the fail-safe direction, and that is the whole point
+ /// of the design. A window is hidden only when srdwm itself put it
+ /// here - `new_managed_window`, the one path that creates a native
+ /// toplevel - and `CompositorHandler::commit` takes it out again on
+ /// the first commit that carries a buffer. Nothing else can ever land
+ /// in it, so no window whose plumbing works differently can be hidden
+ /// by a lookup that did not apply to it. The first version of this
+ /// asked the renderer "does this surface have a buffer right now" from
+ /// inside the render loop, defaulting to hidden when the answer was
+ /// no; an XWayland window, whose surface state that lookup does not
+ /// describe, went invisible in the owner's live session while staying
+ /// clickable - reported as "I can click close where the button would
+ /// be and it does close, but it is still invisible".
+ pub(crate) awaiting_first_buffer: HashSet<WindowId>,
pub(crate) decorations: HashMap<WindowId, MemoryRenderBuffer>,
/// The top border strip's rounded-corner bitmap, cached the same way
/// and at the same trigger points as `decorations` (built in