diff options
| -rw-r--r-- | niri-visual-tests/src/cases/gradient_angle.rs | 11 | ||||
| -rw-r--r-- | niri-visual-tests/src/cases/gradient_area.rs | 11 | ||||
| -rw-r--r-- | niri-visual-tests/src/cases/layout.rs | 15 | ||||
| -rw-r--r-- | niri-visual-tests/src/cases/tile.rs | 4 | ||||
| -rw-r--r-- | niri-visual-tests/src/main.rs | 14 | ||||
| -rw-r--r-- | niri-visual-tests/src/smithay_view.rs | 33 | ||||
| -rw-r--r-- | src/animation/clock.rs | 191 | ||||
| -rw-r--r-- | src/animation/mod.rs | 133 | ||||
| -rw-r--r-- | src/input/mod.rs | 7 | ||||
| -rw-r--r-- | src/ipc/server.rs | 3 | ||||
| -rw-r--r-- | src/layout/closing_window.rs | 7 | ||||
| -rw-r--r-- | src/layout/mod.rs | 28 | ||||
| -rw-r--r-- | src/layout/monitor.rs | 9 | ||||
| -rw-r--r-- | src/layout/opening_window.rs | 5 | ||||
| -rw-r--r-- | src/layout/tile.rs | 19 | ||||
| -rw-r--r-- | src/layout/workspace.rs | 339 | ||||
| -rw-r--r-- | src/main.rs | 8 | ||||
| -rw-r--r-- | src/niri.rs | 69 | ||||
| -rw-r--r-- | src/ui/config_error_notification.rs | 10 | ||||
| -rw-r--r-- | src/ui/screen_transition.rs | 40 | ||||
| -rw-r--r-- | src/ui/screenshot_ui.rs | 11 |
21 files changed, 534 insertions, 433 deletions
diff --git a/niri-visual-tests/src/cases/gradient_angle.rs b/niri-visual-tests/src/cases/gradient_angle.rs index 64c953c4..39a2e4ef 100644 --- a/niri-visual-tests/src/cases/gradient_angle.rs +++ b/niri-visual-tests/src/cases/gradient_angle.rs @@ -1,8 +1,6 @@ use std::f32::consts::{FRAC_PI_2, PI}; -use std::sync::atomic::Ordering; use std::time::Duration; -use niri::animation::ANIMATION_SLOWDOWN; use niri::render_helpers::border::BorderRenderElement; use niri_config::{Color, CornerRadius, GradientInterpolation}; use smithay::backend::renderer::element::RenderElement; @@ -31,20 +29,13 @@ impl TestCase for GradientAngle { } fn advance_animations(&mut self, current_time: Duration) { - let mut delta = if self.prev_time.is_zero() { + let delta = if self.prev_time.is_zero() { Duration::ZERO } else { current_time.saturating_sub(self.prev_time) }; self.prev_time = current_time; - let slowdown = ANIMATION_SLOWDOWN.load(Ordering::SeqCst); - if slowdown == 0. { - delta = Duration::ZERO - } else { - delta = delta.div_f64(slowdown); - } - self.angle += delta.as_secs_f32() * PI; if self.angle >= PI * 2. { diff --git a/niri-visual-tests/src/cases/gradient_area.rs b/niri-visual-tests/src/cases/gradient_area.rs index 2fa275f2..6f0cf4f3 100644 --- a/niri-visual-tests/src/cases/gradient_area.rs +++ b/niri-visual-tests/src/cases/gradient_area.rs @@ -1,8 +1,6 @@ use std::f32::consts::{FRAC_PI_4, PI}; -use std::sync::atomic::Ordering; use std::time::Duration; -use niri::animation::ANIMATION_SLOWDOWN; use niri::layout::focus_ring::FocusRing; use niri::render_helpers::border::BorderRenderElement; use niri_config::{Color, CornerRadius, FloatOrInt, GradientInterpolation}; @@ -43,20 +41,13 @@ impl TestCase for GradientArea { } fn advance_animations(&mut self, current_time: Duration) { - let mut delta = if self.prev_time.is_zero() { + let delta = if self.prev_time.is_zero() { Duration::ZERO } else { current_time.saturating_sub(self.prev_time) }; self.prev_time = current_time; - let slowdown = ANIMATION_SLOWDOWN.load(Ordering::SeqCst); - if slowdown == 0. { - delta = Duration::ZERO - } else { - delta = delta.div_f64(slowdown); - } - self.progress += delta.as_secs_f32() * PI; if self.progress >= PI * 2. { diff --git a/niri-visual-tests/src/cases/layout.rs b/niri-visual-tests/src/cases/layout.rs index f979debb..0dbe004d 100644 --- a/niri-visual-tests/src/cases/layout.rs +++ b/niri-visual-tests/src/cases/layout.rs @@ -69,7 +69,7 @@ impl Layout { let mut layout = niri::layout::Layout::with_options(clock.clone(), options); layout.add_output(output.clone()); - let start_time = clock.now(); + let start_time = clock.now_unadjusted(); Self { output, @@ -207,24 +207,25 @@ impl TestCase for Layout { self.layout.are_animations_ongoing(Some(&self.output)) || !self.steps.is_empty() } - fn advance_animations(&mut self, current_time: Duration) { + fn advance_animations(&mut self, _current_time: Duration) { + let now_unadjusted = self.clock.now_unadjusted(); let run = self .steps .keys() .copied() - .filter(|delay| self.start_time + *delay <= current_time) + .filter(|delay| self.start_time + *delay <= now_unadjusted) .collect::<Vec<_>>(); for delay in &run { let now = self.start_time + *delay; - self.clock.set_time_override(Some(now)); - self.layout.advance_animations(now); + self.clock.set_unadjusted(now); + self.layout.advance_animations(); let f = self.steps.remove(delay).unwrap(); f(self); } - self.clock.set_time_override(None); - self.layout.advance_animations(current_time); + self.clock.set_unadjusted(now_unadjusted); + self.layout.advance_animations(); } fn render( diff --git a/niri-visual-tests/src/cases/tile.rs b/niri-visual-tests/src/cases/tile.rs index e2637698..f50edf37 100644 --- a/niri-visual-tests/src/cases/tile.rs +++ b/niri-visual-tests/src/cases/tile.rs @@ -90,8 +90,8 @@ impl TestCase for Tile { self.tile.are_animations_ongoing() } - fn advance_animations(&mut self, current_time: Duration) { - self.tile.advance_animations(current_time); + fn advance_animations(&mut self, _current_time: Duration) { + self.tile.advance_animations(); } fn render( diff --git a/niri-visual-tests/src/main.rs b/niri-visual-tests/src/main.rs index fa035c75..59563651 100644 --- a/niri-visual-tests/src/main.rs +++ b/niri-visual-tests/src/main.rs @@ -2,15 +2,11 @@ extern crate tracing; use std::env; -use std::sync::atomic::Ordering; use adw::prelude::{AdwApplicationWindowExt, NavigationPageExt}; use cases::Args; -use gtk::prelude::{ - AdjustmentExt, ApplicationExt, ApplicationExtManual, BoxExt, GtkWindowExt, WidgetExt, -}; +use gtk::prelude::{ApplicationExt, ApplicationExtManual, BoxExt, GtkWindowExt, WidgetExt}; use gtk::{gdk, gio, glib}; -use niri::animation::ANIMATION_SLOWDOWN; use smithay_view::SmithayView; use tracing_subscriber::EnvFilter; @@ -66,20 +62,23 @@ fn on_startup(_app: &adw::Application) { fn build_ui(app: &adw::Application) { let stack = gtk::Stack::new(); + let anim_adjustment = gtk::Adjustment::new(1., 0., 10., 0.1, 0.5, 0.); struct S { stack: gtk::Stack, + anim_adjustment: gtk::Adjustment, } impl S { fn add<T: TestCase + 'static>(&self, make: impl Fn(Args) -> T + 'static, title: &str) { - let view = SmithayView::new(make); + let view = SmithayView::new(make, &self.anim_adjustment); self.stack.add_titled(&view, None, title); } } let s = S { stack: stack.clone(), + anim_adjustment: anim_adjustment.clone(), }; s.add(Window::freeform, "Freeform Window"); @@ -133,9 +132,6 @@ fn build_ui(app: &adw::Application) { let content_headerbar = adw::HeaderBar::new(); - let anim_adjustment = gtk::Adjustment::new(1., 0., 10., 0.1, 0.5, 0.); - anim_adjustment - .connect_value_changed(|adj| ANIMATION_SLOWDOWN.store(adj.value(), Ordering::SeqCst)); let anim_scale = gtk::Scale::new(gtk::Orientation::Horizontal, Some(&anim_adjustment)); anim_scale.set_hexpand(true); diff --git a/niri-visual-tests/src/smithay_view.rs b/niri-visual-tests/src/smithay_view.rs index 68c5d67d..416729f0 100644 --- a/niri-visual-tests/src/smithay_view.rs +++ b/niri-visual-tests/src/smithay_view.rs @@ -1,4 +1,5 @@ use gtk::glib; +use gtk::prelude::*; use gtk::subclass::prelude::*; use smithay::utils::Size; @@ -7,13 +8,13 @@ use crate::cases::{Args, TestCase}; mod imp { use std::cell::{Cell, OnceCell, RefCell}; use std::ptr::null; + use std::time::Duration; use anyhow::{ensure, Context}; use gtk::gdk; use gtk::prelude::*; use niri::animation::Clock; use niri::render_helpers::{resources, shaders}; - use niri::utils::get_monotonic_time; use smithay::backend::egl::ffi::egl; use smithay::backend::egl::EGLContext; use smithay::backend::renderer::gles::GlesRenderer; @@ -31,7 +32,7 @@ mod imp { renderer: RefCell<Option<Result<GlesRenderer, ()>>>, pub make_test_case: OnceCell<DynMakeTestCase>, test_case: RefCell<Option<Box<dyn TestCase>>>, - clock: RefCell<Clock>, + pub clock: RefCell<Clock>, } #[glib::object_subclass] @@ -127,6 +128,10 @@ mod imp { let size = self.size.get(); + let frame_clock = self.obj().frame_clock().unwrap(); + let time = Duration::from_micros(frame_clock.frame_time() as u64); + self.clock.borrow_mut().set_unadjusted(time); + // Create the test case if missing. let mut case = self.test_case.borrow_mut(); let case = case.get_or_insert_with(|| { @@ -138,7 +143,7 @@ mod imp { make(args) }); - case.advance_animations(get_monotonic_time()); + case.advance_animations(self.clock.borrow_mut().now()); let rect: Rectangle<i32, Physical> = Rectangle::from_loc_and_size((0, 0), size); @@ -238,13 +243,33 @@ glib::wrapper! { } impl SmithayView { - pub fn new<T: TestCase + 'static>(make_test_case: impl Fn(Args) -> T + 'static) -> Self { + pub fn new<T: TestCase + 'static>( + make_test_case: impl Fn(Args) -> T + 'static, + anim_adjustment: >k::Adjustment, + ) -> Self { let obj: Self = glib::Object::builder().build(); let make = move |args| Box::new(make_test_case(args)) as Box<dyn TestCase>; let make_test_case = Box::new(make) as _; let _ = obj.imp().make_test_case.set(make_test_case); + anim_adjustment.connect_value_changed({ + let obj = obj.downgrade(); + move |adj| { + if let Some(obj) = obj.upgrade() { + let mut clock = obj.imp().clock.borrow_mut(); + let instantly = adj.value() == 0.0; + let rate = if instantly { + 1.0 + } else { + 1.0 / adj.value().max(0.001) + }; + clock.set_rate(rate); + clock.set_complete_instantly(instantly); + } + } + }); + obj } } diff --git a/src/animation/clock.rs b/src/animation/clock.rs index 3cfa727b..30f8bd45 100644 --- a/src/animation/clock.rs +++ b/src/animation/clock.rs @@ -1,41 +1,202 @@ -use std::cell::Cell; +use std::cell::RefCell; use std::rc::Rc; use std::time::Duration; use crate::utils::get_monotonic_time; -/// Clock that can have its time value overridden. +/// Shareable lazy clock that can change rate. /// -/// Can be cloned to share the same clock. +/// The clock will fetch the time once and then retain it until explicitly cleared with +/// [`Clock::clear`]. #[derive(Debug, Default, Clone)] pub struct Clock { - time_override: Rc<Cell<Option<Duration>>>, + inner: Rc<RefCell<AdjustableClock>>, +} + +#[derive(Debug, Default)] +struct LazyClock { + time: Option<Duration>, +} + +/// Clock that can adjust its rate. +#[derive(Debug)] +struct AdjustableClock { + inner: LazyClock, + current_time: Duration, + last_seen_time: Duration, + rate: f64, + complete_instantly: bool, } impl Clock { - /// Creates a new [`Clock`] with time override in place. - pub fn with_override(time: Duration) -> Self { + /// Creates a new clock with the given time. + pub fn with_time(time: Duration) -> Self { + let clock = AdjustableClock::new(LazyClock::with_time(time)); Self { - time_override: Rc::new(Cell::new(Some(time))), + inner: Rc::new(RefCell::new(clock)), } } - /// Sets the current time override. - pub fn set_time_override(&mut self, time: Option<Duration>) { - self.time_override.set(time); + /// Returns the current time. + pub fn now(&self) -> Duration { + self.inner.borrow_mut().now() } - /// Gets the current time. - #[inline] - pub fn now(&self) -> Duration { - self.time_override.get().unwrap_or_else(get_monotonic_time) + /// Returns the underlying time not adjusted for rate change. + pub fn now_unadjusted(&self) -> Duration { + self.inner.borrow_mut().inner.now() + } + + /// Sets the unadjusted clock time. + pub fn set_unadjusted(&mut self, time: Duration) { + self.inner.borrow_mut().inner.set(time); + } + + /// Clears the stored time so it's re-fetched again next. + pub fn clear(&mut self) { + self.inner.borrow_mut().inner.clear(); + } + + /// Gets the clock rate. + pub fn rate(&self) -> f64 { + self.inner.borrow().rate() + } + + /// Sets the clock rate. + pub fn set_rate(&mut self, rate: f64) { + self.inner.borrow_mut().set_rate(rate); + } + + /// Returns whether animations should complete instantly. + pub fn should_complete_instantly(&self) -> bool { + self.inner.borrow().should_complete_instantly() + } + + /// Sets whether animations should complete instantly. + pub fn set_complete_instantly(&mut self, value: bool) { + self.inner.borrow_mut().set_complete_instantly(value); } } impl PartialEq for Clock { fn eq(&self, other: &Self) -> bool { - Rc::ptr_eq(&self.time_override, &other.time_override) + Rc::ptr_eq(&self.inner, &other.inner) } } impl Eq for Clock {} + +impl LazyClock { + pub fn with_time(time: Duration) -> Self { + Self { time: Some(time) } + } + + pub fn clear(&mut self) { + self.time = None; + } + + pub fn set(&mut self, time: Duration) { + self.time = Some(time); + } + + pub fn now(&mut self) -> Duration { + *self.time.get_or_insert_with(get_monotonic_time) + } +} + +impl AdjustableClock { + pub fn new(mut inner: LazyClock) -> Self { + let time = inner.now(); + Self { + inner, + current_time: time, + last_seen_time: time, + rate: 1., + complete_instantly: false, + } + } + + pub fn rate(&self) -> f64 { + self.rate + } + + pub fn set_rate(&mut self, rate: f64) { + self.rate = rate.clamp(0., 1000.); + } + + pub fn should_complete_instantly(&self) -> bool { + self.complete_instantly + } + + pub fn set_complete_instantly(&mut self, value: bool) { + self.complete_instantly = value; + } + + pub fn now(&mut self) -> Duration { + let time = self.inner.now(); + + if self.last_seen_time == time { + return self.current_time; + } + + if self.last_seen_time < time { + let delta = time - self.last_seen_time; + let delta = delta.mul_f64(self.rate); + self.current_time = self.current_time.saturating_add(delta); + } else { + let delta = self.last_seen_time - time; + let delta = delta.mul_f64(self.rate); + self.current_time = self.current_time.saturating_sub(delta); + } + + self.last_seen_time = time; + self.current_time + } +} + +impl Default for AdjustableClock { + fn default() -> Self { + Self::new(LazyClock::default()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn frozen_clock() { + let mut clock = Clock::with_time(Duration::ZERO); + assert_eq!(clock.now(), Duration::ZERO); + + clock.set_unadjusted(Duration::from_millis(100)); + assert_eq!(clock.now(), Duration::from_millis(100)); + + clock.set_unadjusted(Duration::from_millis(200)); + assert_eq!(clock.now(), Duration::from_millis(200)); + } + + #[test] + fn rate_change() { + let mut clock = Clock::with_time(Duration::ZERO); + clock.set_rate(0.5); + + clock.set_unadjusted(Duration::from_millis(100)); + assert_eq!(clock.now_unadjusted(), Duration::from_millis(100)); + assert_eq!(clock.now(), Duration::from_millis(50)); + + clock.set_unadjusted(Duration::from_millis(200)); + assert_eq!(clock.now_unadjusted(), Duration::from_millis(200)); + assert_eq!(clock.now(), Duration::from_millis(100)); + + clock.set_unadjusted(Duration::from_millis(150)); + assert_eq!(clock.now_unadjusted(), Duration::from_millis(150)); + assert_eq!(clock.now(), Duration::from_millis(75)); + + clock.set_rate(2.0); + + clock.set_unadjusted(Duration::from_millis(250)); + assert_eq!(clock.now_unadjusted(), Duration::from_millis(250)); + assert_eq!(clock.now(), Duration::from_millis(275)); + } +} diff --git a/src/animation/mod.rs b/src/animation/mod.rs index 5efb0f16..50dfc195 100644 --- a/src/animation/mod.rs +++ b/src/animation/mod.rs @@ -2,7 +2,6 @@ use std::time::Duration; use keyframe::functions::{EaseOutCubic, EaseOutQuad}; use keyframe::EasingFunction; -use portable_atomic::{AtomicF64, Ordering}; mod spring; pub use spring::{Spring, SpringParams}; @@ -10,8 +9,6 @@ pub use spring::{Spring, SpringParams}; mod clock; pub use clock::Clock; -pub static ANIMATION_SLOWDOWN: AtomicF64 = AtomicF64::new(1.); - #[derive(Debug, Clone)] pub struct Animation { from: f64, @@ -24,7 +21,7 @@ pub struct Animation { /// Best effort; not always exactly precise. clamped_duration: Duration, start_time: Duration, - current_time: Duration, + clock: Clock, kind: Kind, } @@ -50,23 +47,16 @@ pub enum Curve { impl Animation { pub fn new( - current_time: Duration, + clock: Clock, from: f64, to: f64, initial_velocity: f64, config: niri_config::Animation, ) -> Self { - // Scale the velocity by slowdown to keep the touchpad gestures feeling right. - let initial_velocity = initial_velocity * ANIMATION_SLOWDOWN.load(Ordering::Relaxed); + // Scale the velocity by rate to keep the touchpad gestures feeling right. + let initial_velocity = initial_velocity / clock.rate().max(0.001); - let mut rv = Self::ease( - current_time, - from, - to, - initial_velocity, - 0, - Curve::EaseOutCubic, - ); + let mut rv = Self::ease(clock, from, to, initial_velocity, 0, Curve::EaseOutCubic); if config.off { rv.is_off = true; return rv; @@ -85,7 +75,6 @@ impl Animation { } let start_time = self.start_time; - let current_time = self.current_time; match config.kind { niri_config::AnimationKind::Spring(p) => { @@ -97,11 +86,11 @@ impl Animation { initial_velocity: self.initial_velocity, params, }; - *self = Self::spring(current_time, spring); + *self = Self::spring(self.clock.clone(), spring); } niri_config::AnimationKind::Easing(p) => { *self = Self::ease( - current_time, + self.clock.clone(), self.from, self.to, self.initial_velocity, @@ -112,27 +101,20 @@ impl Animation { } self.start_time = start_time; - self.current_time = current_time; } /// Restarts the animation using the previous config. - pub fn restarted( - &self, - current_time: Duration, - from: f64, - to: f64, - initial_velocity: f64, - ) -> Self { + pub fn restarted(&self, from: f64, to: f64, initial_velocity: f64) -> Self { if self.is_off { return self.clone(); } - // Scale the velocity by slowdown to keep the touchpad gestures feeling right. - let initial_velocity = initial_velocity * ANIMATION_SLOWDOWN.load(Ordering::Relaxed); + // Scale the velocity by rate to keep the touchpad gestures feeling right. + let initial_velocity = initial_velocity / self.clock.rate().max(0.001); match self.kind { Kind::Easing { curve } => Self::ease( - current_time, + self.clock.clone(), from, to, initial_velocity, @@ -146,7 +128,7 @@ impl Animation { initial_velocity: self.initial_velocity, params: spring.params, }; - Self::spring(current_time, spring) + Self::spring(self.clock.clone(), spring) } Kind::Deceleration { initial_velocity, @@ -154,7 +136,7 @@ impl Animation { } => { let threshold = 0.001; // FIXME Self::decelerate( - current_time, + self.clock.clone(), from, initial_velocity, deceleration_rate, @@ -165,7 +147,7 @@ impl Animation { } pub fn ease( - current_time: Duration, + clock: Clock, from: f64, to: f64, initial_velocity: f64, @@ -183,13 +165,13 @@ impl Animation { duration, // Our current curves never overshoot. clamped_duration: duration, - start_time: current_time, - current_time, + start_time: clock.now(), + clock, kind, } } - pub fn spring(current_time: Duration, spring: Spring) -> Self { + pub fn spring(clock: Clock, spring: Spring) -> Self { let _span = tracy_client::span!("Animation::spring"); let duration = spring.duration(); @@ -203,14 +185,14 @@ impl Animation { is_off: false, duration, clamped_duration, - start_time: current_time, - current_time, + start_time: clock.now(), + clock, kind, } } pub fn decelerate( - current_time: Duration, + clock: Clock, from: f64, initial_velocity: f64, deceleration_rate: f64, @@ -238,77 +220,26 @@ impl Animation { is_off: false, duration, clamped_duration: duration, - start_time: current_time, - current_time, + start_time: clock.now(), + clock, kind, } } - pub fn set_current_time(&mut self, time: Duration) { - if self.duration.is_zero() { - self.current_time = time; - return; - } - - let end_time = self.start_time + self.duration; - if end_time <= self.current_time { - return; - } - - let slowdown = ANIMATION_SLOWDOWN.load(Ordering::Relaxed); - if slowdown <= f64::EPSILON { - // Zero slowdown will cause the animation to end right away. - self.current_time = end_time; - return; - } - - // We can't change current_time (since the incoming time values are always real-time), so - // apply the slowdown by shifting the start time to compensate. - if self.current_time <= time { - let delta = time - self.current_time; - - let max_delta = end_time - self.current_time; - let min_slowdown = delta.as_secs_f64() / max_delta.as_secs_f64(); - if slowdown <= min_slowdown { - // Our slowdown value will cause the animation to end right away. - self.current_time = end_time; - return; - } - - let adjusted_delta = delta.div_f64(slowdown); - if adjusted_delta >= delta { - self.start_time -= adjusted_delta - delta; - } else { - self.start_time += delta - adjusted_delta; - } - } else { - let delta = self.current_time - time; - - let min_slowdown = delta.as_secs_f64() / self.current_time.as_secs_f64(); - if slowdown <= min_slowdown { - // Current time was about to jump to before the animation had started; let's just - // cancel the animation in this case. - self.current_time = end_time; - return; - } - - let adjusted_delta = delta.div_f64(slowdown); - if adjusted_delta >= delta { - self.start_time += adjusted_delta - delta; - } else { - self.start_time -= delta - adjusted_delta; - } + pub fn is_done(&self) -> bool { + if self.clock.should_complete_instantly() { + return true; } - self.current_time = time; - } - - pub fn is_done(&self) -> bool { - self.current_time >= self.start_time + self.duration + self.clock.now() >= self.start_time + self.duration } pub fn is_clamped_done(&self) -> bool { - self.current_time >= self.start_time + self.clamped_duration + if self.clock.should_complete_instantly() { + return true; + } + + self.clock.now() >= self.start_time + self.clamped_duration } pub fn value(&self) -> f64 { @@ -316,7 +247,7 @@ impl Animation { return self.to; } - let passed = self.current_time.saturating_sub(self.start_time); + let passed = self.clock.now().saturating_sub(self.start_time); match self.kind { Kind::Easing { curve } => { diff --git a/src/input/mod.rs b/src/input/mod.rs index eec8fbe4..bafa1505 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -83,11 +83,8 @@ impl State { { let _span = tracy_client::span!("process_input_event"); - // A bit of a hack, but animation end runs some logic (i.e. workspace clean-up) and it - // doesn't always trigger due to damage, etc. So run it here right before it might prove - // important. Besides, animations affect the input, so it's best to have up-to-date values - // here. - self.niri.advance_animations(get_monotonic_time()); + // Make sure some logic like workspace clean-up has a chance to run before doing actions. + self.niri.advance_animations(); if self.niri.monitors_active { // Notify the idle-notifier of activity. diff --git a/src/ipc/server.rs b/src/ipc/server.rs index 16cc5524..facb59d3 100644 --- a/src/ipc/server.rs +++ b/src/ipc/server.rs @@ -314,6 +314,9 @@ async fn process(ctx: &ClientCtx, request: Request) -> Reply { let action = niri_config::Action::from(action); ctx.event_loop.insert_idle(move |state| { + // Make sure some logic like workspace clean-up has a chance to run before doing + // actions. + state.niri.advance_animations(); state.do_action(action, false); let _ = tx.send_blocking(()); }); diff --git a/src/layout/closing_window.rs b/src/layout/closing_window.rs index 744c099e..12a77f92 100644 --- a/src/layout/closing_window.rs +++ b/src/layout/closing_window.rs @@ -1,5 +1,4 @@ use std::collections::HashMap; -use std::time::Duration; use anyhow::Context as _; use glam::{Mat3, Vec2}; @@ -138,15 +137,15 @@ impl ClosingWindow { }) } - pub fn advance_animations(&mut self, current_time: Duration) { + pub fn advance_animations(&mut self) { match &mut self.anim_state { AnimationState::Waiting { blocker, anim } => { if blocker.state() != BlockerState::Pending { - let anim = anim.restarted(current_time, 0., 1., 0.); + let anim = anim.restarted(0., 1., 0.); self.anim_state = AnimationState::Animating(anim); } } - AnimationState::Animating(anim) => anim.set_current_time(current_time), + AnimationState::Animating(_anim) => (), } } diff --git a/src/layout/mod.rs b/src/layout/mod.rs index f4e59af3..45228038 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -219,6 +219,8 @@ pub struct Layout<W: LayoutElement> { interactive_move: Option<InteractiveMoveState<W>>, /// Clock for driving animations. clock: Clock, + /// Time that we last updated render elements for. + update_render_elements_time: Duration, /// Configurable properties of the layout. options: Rc<Options>, } @@ -447,6 +449,7 @@ impl<W: LayoutElement> Layout<W> { last_active_workspace_id: HashMap::new(), interactive_move: None, clock, + update_render_elements_time: Duration::ZERO, options: Rc::new(options), } } @@ -468,6 +471,7 @@ impl<W: LayoutElement> Layout<W> { last_active_workspace_id: HashMap::new(), interactive_move: None, clock, + update_render_elements_time: Duration::ZERO, options: opts, } } @@ -2194,22 +2198,22 @@ impl<W: LayoutElement> Layout<W> { } } - pub fn advance_animations(&mut self, current_time: Duration) { + pub fn advance_animations(&mut self) { let _span = tracy_client::span!("Layout::advance_animations"); if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move { - move_.tile.advance_animations(current_time); + move_.tile.advance_animations(); } match &mut self.monitor_set { MonitorSet::Normal { monitors, .. } => { for mon in monitors { - mon.advance_animations(current_time); + mon.advance_animations(); } } MonitorSet::NoOutputs { workspaces, .. } => { for ws in workspaces { - ws.advance_animations(current_time); + ws.advance_animations(); } } } @@ -2242,6 +2246,8 @@ impl<W: LayoutElement> Layout<W> { pub fn update_render_elements(&mut self, output: Option<&Output>) { let _span = tracy_client::span!("Layout::update_render_elements"); + self.update_render_elements_time = self.clock.now(); + if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move { if output.map_or(true, |output| move_.output == *output) { let pos_within_output = move_.tile_render_location(); @@ -3475,6 +3481,10 @@ impl& |
