srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/wayland/src/state/geometry.rs
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2024-07-30 22:10:00 +0200
committersrdusr <[email protected]>2024-07-30 22:10:00 +0200
commit38683f069a039f9da6d7ddc64167850560fd07ed (patch)
treec9c240dfb91639c59e27779cd654c96202b25688 /crates/wayland/src/state/geometry.rs
parent5fe599437ab9d1d9cfa749e861ce2f3acc02aa3c (diff)
downloadsrdwm-38683f069a039f9da6d7ddc64167850560fd07ed.tar.gz
srdwm-38683f069a039f9da6d7ddc64167850560fd07ed.zip
Split crates/wayland/src/state.rs (1276 lines) into state/
Pure reorganization, no behavior change - verified by diffing the function-name and struct-name sets before/after (both identical) plus a full cargo test pass. mod.rs keeps ClientState/OutputEntry/CompState/ WindowAnim/RepeatState's definitions, the key-repeat impl, and the output-lookup impl (all small and tightly coupled to the type definitions), plus mod declarations. The one large impl CompState block (previously ~600 lines) splits by concern: - lifecycle.rs: new_managed_window, set_decorated_from_mode, redraw_decoration_buffer, remove_window. - layers.rs: ensure_layer_initial_configure. - focus.rs: set_keyboard_focus, set_window_activated. - menu.rs: open/close/run_context_menu_action, is_double_click. - geometry.rs: raise_pinned, sync_geometry. - tick.rs: tick_dirty_broadcasts, tick_animations, resync_stacking_order. - toplevel.rs: the with_toplevel_title/app_id/sync_toplevel_metadata free functions. - tests.rs: unsplit, same reasoning as every other split this pass. CompState's fields were already pub(crate) (this crate's existing convention, unlike core's/config's plain-private), so no field- visibility changes were needed - only resync_stacking_order (called from geometry.rs, defined in tick.rs) needed bumping from private to pub(crate), matching that same convention.
Diffstat (limited to 'crates/wayland/src/state/geometry.rs')
-rw-r--r--crates/wayland/src/state/geometry.rs105
1 files changed, 105 insertions, 0 deletions
diff --git a/crates/wayland/src/state/geometry.rs b/crates/wayland/src/state/geometry.rs
new file mode 100644
index 0000000..b902d16
--- /dev/null
+++ b/crates/wayland/src/state/geometry.rs
@@ -0,0 +1,105 @@
+use super::*;
+
+impl CompState {
+
+ /// Re-raises always-on-top windows in the `Space`.
+ ///
+ /// `WindowManager` keeps pinned windows last in its own stacking order,
+ /// but the `Space` has an order of its own that decides what actually
+ /// draws on top - so pinning is only real once it is pushed here.
+ /// Called after anything that raises a window.
+ pub(crate) fn raise_pinned(&mut self) {
+ let pinned: Vec<WindowId> = self.wm.borrow().stacking_order().filter(|w| w.always_on_top).map(|w| w.id).collect();
+ for id in pinned {
+ if let Some(w) = self.id_to_window.get(&id).cloned() {
+ self.space.raise_element(&w, false);
+ }
+ }
+ }
+
+ pub(crate) fn sync_geometry(&mut self, id: WindowId) {
+ // A pending `anim_from` (set by `toggle_maximize`/`toggle_fullscreen`,
+ // or by `new_managed_window` for the open-slide) means the target
+ // geometry below is where this window is *headed*, not where it
+ // should appear right now - register (or replace) a tween and use
+ // `WindowAnim::current_rect` in its place for this call and every
+ // `tick_animations` call afterward, until it completes. `take()`
+ // both reads and clears it, so a later, non-animated `sync_geometry`
+ // call for the same window (an ordinary drag/resize frame) goes
+ // straight back to applying `geometry` immediately, as before.
+ let anim_from = self.wm.borrow_mut().window_mut(id).and_then(|w| w.anim_from.take());
+ let Some((target, decorated)) = self.wm.borrow().window(id).map(|w| (w.geometry, w.decorated)) else { return };
+ if let Some(from) = anim_from {
+ let duration_ms = self.wm.borrow().animation_duration_ms;
+ if from != target && duration_ms > 0 {
+ self.window_anims
+ .insert(id, WindowAnim { from, to: target, start: Instant::now(), duration: Duration::from_millis(duration_ms as u64) });
+ }
+ }
+ let geom = self.window_anims.get(&id).map(WindowAnim::current_rect).unwrap_or(target);
+ // The titlebar band is only actually reserved when there is one --
+ // an undecorated window (client-side decoration, see
+ // `set_decorated_from_mode`) gets the whole of `geom` as content,
+ // not `geom` minus a band that's no longer being drawn. Without
+ // this, a window that negotiated client-side decoration kept the
+ // same 30px gap at its top anyway: our titlebar wasn't drawn there
+ // (correctly), but the content was still offset down and told it
+ // was 30px shorter than the window actually is, leaving a blank
+ // strip and the frame sitting visibly wrong relative to what's
+ // inside it.
+ let band = if decorated { TITLEBAR_HEIGHT as i32 } else { 0 };
+ // Position always moves with the pointer; only a size change needs a
+ // client configure or a titlebar re-render (see `last_synced_size`'s
+ // doc comment).
+ let size = (geom.width as i32, geom.height as i32 - band);
+ let size_changed = self.last_synced_size.insert(id, size) != Some(size);
+ let mut moved = false;
+ if let Some(w) = self.id_to_window.get(&id) {
+ self.space.map_element(w.clone(), (geom.x, geom.y + band), false);
+ moved = true;
+ if let Some(top) = w.toplevel() {
+ // xdg-shell position is a purely compositor-side concept --
+ // the client is never told it - so only a size change
+ // needs a configure here.
+ if size_changed {
+ top.with_pending_state(|state| {
+ state.size = Some(size.into());
+ });
+ top.send_configure();
+ }
+ } else if let Some(x11) = w.x11_surface() {
+ // Unlike xdg-shell, an X11 client's real on-screen position
+ // is part of its own window state - it has to be told on
+ // every move, not just every resize, the same way a real
+ // X11 window manager sends continuous `ConfigureNotify`
+ // during an interactive drag. Without this branch at all,
+ // `sync_geometry` never reconfigured an XWayland window a
+ // second time past its initial map: `space.map_element`
+ // above still moved smithay's own tracked position (see
+ // `resync_stacking_order`'s doc comment for the real
+ // z-order side effect that has, since fixed below) and the
+ // border/titlebar still redrew at the new `Window.geometry`
+ // (both read it fresh every frame), but the real X11
+ // client window was never told to move or resize - any
+ // drag, resize, maximize, edge-snap, or tiling re-layout of
+ // an XWayland-backed app left its actual content frozen at
+ // its original position/size forever while srdwm's own
+ // decoration moved freely around it.
+ let _ = x11.configure(Rectangle::new((geom.x, geom.y + band).into(), size.into()));
+ }
+ }
+ if size_changed && self.decorations.contains_key(&id) {
+ self.redraw_decoration_buffer(id);
+ }
+ // See `resync_stacking_order`'s doc comment: `map_element` above
+ // always re-stacks its target to the top of `Space`'s own order as
+ // a side effect of updating position, `activate` or not - and
+ // `sync_geometry` runs for reasons with nothing to do with raising
+ // a window (a title changing, an ordinary resize frame), so left
+ // uncorrected this silently, non-deterministically desynced
+ // `Space`'s notion of "on top" from `WindowManager`'s.
+ if moved {
+ self.resync_stacking_order();
+ }
+ }
+}