atagen

monolith

public
Star0
Watch0

Commit fd834695f723

tarball

atagen <boss@atagen.co>2026-10-19 07:51 UTC

2 files changed+71-5
windows: glide once when a toggled float learns its size

A tiled window toggled to floating glided toward the default float rect while the client was still answering the configure, then snapped to its natural rect when the commit arrived. It now stays where it was drawn, through Layout::hold_float_rect, and glides once from there to the centred rect. A client that never answers is given its committed geometry after 300 ms.
Modified file: crates/layout/src/lib.rs+8 additions
@@ -652,6 +652,14 @@ impl Layout {
652652 }
653653 }
654654
655+ /// Places a float without settling its size, so a window still deciding
656+ /// on one is asked again after being tiled.
657+ pub fn hold_float_rect(&mut self, w: WindowId, rect: Rect) {
658+ if let Some(win) = self.windows.get_mut(w) {
659+ win.float_rect = Some(rect);
660+ }
661+ }
662+
655663 pub fn toggle_fullscreen(&mut self, w: WindowId) {
656664 if let Some(win) = self.windows.get_mut(w) {
657665 win.fullscreen = !win.fullscreen;
Modified file: crates/monolith/src/windows.rs+63 additions−5 deletions
@@ -1,6 +1,6 @@
11 use crate::anim::Ghost;
22 use crate::state::{State, now_ns};
3-use crate::wm::{LastFrame, identity, root};
3+use crate::wm::{Kind, LastFrame, identity, root};
44 use monolith_layout::{OutputId, Rect, WindowId};
55 use monolith_render_vk::VulkanTexture;
66 use smithay::backend::renderer::Renderer;
@@ -257,12 +257,17 @@ impl State {
257257 (Some(x), _) => crate::xwayland::fixed_size(x),
258258 (_, Some(t)) if crate::xdg::fixed_size(t).is_some() => current,
259259 (_, Some(t)) => {
260+ let held = self.visual_rect(id);
260261 let serial = crate::wm::configure_free(t);
261262 if let Some(w) = self.wm.get_mut(id)
262- && serial.is_some()
263+ && let Some(serial) = serial
263264 {
264265 w.natural_size = true;
265- w.natural_after = serial;
266+ w.natural_after = Some(serial);
267+ if let Some(rect) = held {
268+ self.wm.layout.hold_float_rect(id, rect);
269+ }
270+ self.await_natural_size(id, serial);
266271 return;
267272 }
268273 current
@@ -280,7 +285,51 @@ impl State {
280285 }
281286 }
282287
283- /// Centres a window over its parent, or else its output.
288+ /// Where a placed window is drawn right now.
289+ fn visual_rect(&self, id: WindowId) -> Option<Rect> {
290+ let now = now_ns();
291+ let (kind, rect, o) = self.wm.get(id)?.placed?;
292+ self.wm
293+ .anim
294+ .gliding(id, now)
295+ .or_else(|| {
296+ (kind == Kind::Tiled)
297+ .then(|| self.wm.anim.tiled(o, id, now))
298+ .flatten()
299+ })
300+ .or(Some(rect))
301+ }
302+
303+ /// A client that never answers a request for its size takes whatever
304+ /// size it has.
305+ fn await_natural_size(&mut self, id: WindowId, serial: Serial) {
306+ let timeout = Timer::from_duration(Duration::from_millis(300));
307+ let _ = self.core.handle.insert_source(timeout, move |_, _, state| {
308+ state.give_up_natural_size(id, serial);
309+ TimeoutAction::Drop
310+ });
311+ }
312+
313+ fn give_up_natural_size(&mut self, id: WindowId, serial: Serial) {
314+ let Some(w) = self.wm.get_mut(id) else {
315+ return;
316+ };
317+ if !w.natural_size || w.natural_after != Some(serial) {
318+ return;
319+ }
320+ w.natural_size = false;
321+ w.natural_after = None;
322+ let size = w.window.geometry().size;
323+ if size.w > 0 && size.h > 0 {
324+ self.float_at_natural_size(id, f64::from(size.w), f64::from(size.h));
325+ } else if let Some(rect) = self.wm.layout.window(id).and_then(|w| w.float_rect) {
326+ self.wm.layout.set_float_rect(id, rect);
327+ }
328+ self.apply_layout();
329+ }
330+
331+ /// Centres a window over its parent, or else its output. One already
332+ /// floating glides there from where it is drawn.
284333 fn float_at_natural_size(&mut self, id: WindowId, w: f64, h: f64) {
285334 let parent = self
286335 .wm
@@ -299,7 +348,16 @@ impl State {
299348 if let Some(around) = parent.or(output) {
300349 let x = around.x + (around.w - w) / 2.0;
301350 let y = around.y + (around.h - h) / 2.0;
302- self.wm.layout.set_float_rect(id, Rect::new(x, y, w, h));
351+ let rect = Rect::new(x, y, w, h);
352+ self.wm.layout.set_float_rect(id, rect);
353+ if let Some((Kind::Floating, from, o)) = self.wm.get(id).and_then(|s| s.placed) {
354+ let now = now_ns();
355+ let from = self.wm.anim.gliding(id, now).unwrap_or(from);
356+ self.wm.anim.glide(id, from, rect, now);
357+ if let Some(s) = self.wm.get_mut(id) {
358+ s.placed = Some((Kind::Floating, rect, o));
359+ }
360+ }
303361 }
304362 }