From 9e88d3e3f86a1248b72fdb9bab59d9936a3323a6 Mon Sep 17 00:00:00 2001 From: srdusr <99972264+srdusr@users.noreply.github.com> Date: Mon, 1 Dec 2025 19:28:00 +0200 Subject: Let a new window pick its own size instead of forcing a guessed placeholder Root-caused "windows spawn small and square, not remembering placement or size": new_managed_window hardcoded a fresh toplevel's geometry to 800x632 before the client had said anything about its own preferred size, and sync_geometry forced that guess onto the client's very first xdg_toplevel::configure unconditionally. Per xdg-shell, size: None on that first configure is how every mainstream compositor lets a client pick its own natural size instead; this one never did, so every app converged on the same placeholder rectangle regardless of what it would have chosen. Window::size_is_provisional marks a size that really was just the guess (not a remembered geometry, a rule's explicit geometry action, or a maximize/phone-mode fill, none of which are guesses). sync_geometry sends size: None for such a window's first configure; a new adopt_provisional_size, called from the commit handler, adopts the client's own real first size into Window::geometry the moment it commits one, clamping only position so a bigger-than-guessed window can't hang off its monitor's edge. Live-verified in a nested compositor: a zenity dialog now renders at its own compact natural size instead of being stretched to the old guess. --- crates/core/src/manager/windows.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'crates/core/src/manager/windows.rs') diff --git a/crates/core/src/manager/windows.rs b/crates/core/src/manager/windows.rs index 40b4c17..f29d19a 100644 --- a/crates/core/src/manager/windows.rs +++ b/crates/core/src/manager/windows.rs @@ -151,10 +151,17 @@ impl WindowManager { let step = self.next_cascade_step.get(); self.next_cascade_step.set(step.wrapping_add(1)); window.geometry = SmartPlacement::place(monitor, &existing, size, &self.placement, step); + // See `Window::size_is_provisional`'s own doc comment: + // `size` above is whatever a backend hardcoded before this + // window's real content was known, not a genuine + // preference - reset below if a rule or maximize goes on + // to give this window a real, deliberate size instead. + window.size_is_provisional = true; } } if let Some(geometry) = actions.as_ref().and_then(|a| a.geometry) { window.geometry = geometry; + window.size_is_provisional = false; } // `general.phone_mode`'s own real default (see its doc comment on // `WindowManager` for the full "optional phone mode" reasoning): @@ -176,6 +183,11 @@ impl WindowManager { self.restack_pinned(); if maximize { self.toggle_maximize(id); + // Deliberately full-monitor, not a guess - see `Window:: + // size_is_provisional`'s own doc comment. + if let Some(w) = self.windows.get_mut(&id) { + w.size_is_provisional = false; + } } id } -- cgit v1.2.3