diff --git a/komorebi/src/animation/animation.rs b/komorebi/src/animation/animation.rs index aca08ca9..992312f9 100644 --- a/komorebi/src/animation/animation.rs +++ b/komorebi/src/animation/animation.rs @@ -8,23 +8,20 @@ use std::sync::atomic::Ordering; use std::time::Duration; use std::time::Instant; +use super::RenderDispatcher; use super::ANIMATION_DURATION; use super::ANIMATION_FPS; use super::ANIMATION_MANAGER; #[derive(Debug, Default, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq)] -pub struct Animation {} +pub struct Animation; impl Animation { /// Returns true if the animation needs to continue pub fn cancel(animation_key: &str) -> bool { - if !ANIMATION_MANAGER.lock().in_progress(animation_key) { - return true; - } - // should be more than 0 let cancel_idx = ANIMATION_MANAGER.lock().init_cancel(animation_key); - let max_duration = Duration::from_secs(1); + let max_duration = Duration::from_secs(5); let spent_duration = Instant::now(); while ANIMATION_MANAGER.lock().in_progress(animation_key) { @@ -46,54 +43,67 @@ impl Animation { #[allow(clippy::cast_precision_loss)] pub fn animate( - animation_key: &str, + animation_key: String, duration: Duration, - mut render_callback: impl FnMut(f64) -> Result<()>, + render_dispatcher: (impl RenderDispatcher + Send + 'static), ) -> Result<()> { - if ANIMATION_MANAGER.lock().in_progress(animation_key) { - let should_animate = Self::cancel(animation_key); + std::thread::spawn(move || { + if ANIMATION_MANAGER.lock().in_progress(animation_key.as_str()) { + let should_animate = Self::cancel(animation_key.as_str()); - if !should_animate { - return Ok(()); - } - } - - ANIMATION_MANAGER.lock().start(animation_key); - - let target_frame_time = Duration::from_millis(1000 / ANIMATION_FPS.load(Ordering::Relaxed)); - let mut progress = 0.0; - let animation_start = Instant::now(); - - // start animation - while progress < 1.0 { - // check if animation is cancelled - if ANIMATION_MANAGER.lock().is_cancelled(animation_key) { - // cancel animation - ANIMATION_MANAGER.lock().cancel(animation_key); - return Ok(()); + if !should_animate { + return Ok(()); + } } - let frame_start = Instant::now(); - // calculate progress - progress = animation_start.elapsed().as_millis() as f64 / duration.as_millis() as f64; - render_callback(progress).ok(); + render_dispatcher.pre_render()?; - // sleep until next frame - let frame_time_elapsed = frame_start.elapsed(); + ANIMATION_MANAGER.lock().start(animation_key.as_str()); - if frame_time_elapsed < target_frame_time { - std::thread::sleep(target_frame_time - frame_time_elapsed); + let target_frame_time = + Duration::from_millis(1000 / ANIMATION_FPS.load(Ordering::Relaxed)); + let mut progress = 0.0; + let animation_start = Instant::now(); + + // start animation + while progress < 1.0 { + // check if animation is cancelled + if ANIMATION_MANAGER + .lock() + .is_cancelled(animation_key.as_str()) + { + // cancel animation + ANIMATION_MANAGER.lock().cancel(animation_key.as_str()); + return Ok(()); + } + + let frame_start = Instant::now(); + // calculate progress + progress = + animation_start.elapsed().as_millis() as f64 / duration.as_millis() as f64; + render_dispatcher.render(progress).ok(); + + // sleep until next frame + let frame_time_elapsed = frame_start.elapsed(); + + if frame_time_elapsed < target_frame_time { + std::thread::sleep(target_frame_time - frame_time_elapsed); + } } - } - ANIMATION_MANAGER.lock().end(animation_key); + ANIMATION_MANAGER.lock().end(animation_key.as_str()); - // limit progress to 1.0 if animation took longer - if progress > 1.0 { - progress = 1.0; - } + // limit progress to 1.0 if animation took longer + if progress != 1.0 { + progress = 1.0; - // process animation for 1.0 to set target position - render_callback(progress) + // process animation for 1.0 to set target position + render_dispatcher.render(progress).ok(); + } + + render_dispatcher.post_render() + }); + + Ok(()) } } diff --git a/komorebi/src/animation/animation_manager.rs b/komorebi/src/animation/animation_manager.rs index b610c8a6..a1448031 100644 --- a/komorebi/src/animation/animation_manager.rs +++ b/komorebi/src/animation/animation_manager.rs @@ -1,6 +1,8 @@ use std::collections::hash_map::Entry; use std::collections::HashMap; +use super::prefix::AnimationPrefix; + #[derive(Debug, Clone, Copy)] struct AnimationState { pub in_progress: bool, @@ -100,10 +102,10 @@ impl AnimationManager { } } - pub fn animations_in_progress(&self, animation_key_prefix: &str) -> usize { + pub fn count_in_progress(&self, animation_key_prefix: AnimationPrefix) -> usize { self.animations .keys() - .filter(|key| key.starts_with(animation_key_prefix)) + .filter(|key| key.starts_with(animation_key_prefix.to_string().as_str())) .count() } } diff --git a/komorebi/src/animation/mod.rs b/komorebi/src/animation/mod.rs index 6fb54cf4..cd5ef3bb 100644 --- a/komorebi/src/animation/mod.rs +++ b/komorebi/src/animation/mod.rs @@ -13,6 +13,8 @@ pub use animation::Animation; pub mod animation_manager; pub mod lerp; pub mod prefix; +pub mod render_dispatcher; +pub use render_dispatcher::RenderDispatcher; pub mod style; lazy_static! { diff --git a/komorebi/src/animation/prefix.rs b/komorebi/src/animation/prefix.rs index 38247931..f7ff5886 100644 --- a/komorebi/src/animation/prefix.rs +++ b/komorebi/src/animation/prefix.rs @@ -13,7 +13,5 @@ pub enum AnimationPrefix { } pub fn new_animation_key(prefix: AnimationPrefix, key: String) -> String { - match prefix { - AnimationPrefix::WindowMove => format!("window_move:{}", key), - } + format!("{}:{}", prefix, key) } diff --git a/komorebi/src/animation/render_dispatcher.rs b/komorebi/src/animation/render_dispatcher.rs new file mode 100644 index 00000000..e7930624 --- /dev/null +++ b/komorebi/src/animation/render_dispatcher.rs @@ -0,0 +1,7 @@ +use color_eyre::Result; + +pub trait RenderDispatcher { + fn pre_render(&self) -> Result<()>; + fn render(&self, delta: f64) -> Result<()>; + fn post_render(&self) -> Result<()>; +} diff --git a/komorebi/src/window.rs b/komorebi/src/window.rs index 2de01638..0723bdb3 100644 --- a/komorebi/src/window.rs +++ b/komorebi/src/window.rs @@ -1,6 +1,8 @@ use crate::animation::lerp::Lerp; use crate::animation::prefix::new_animation_key; use crate::animation::prefix::AnimationPrefix; +use crate::animation::RenderDispatcher; +// use crate::animation::renderer::Renderer; use crate::animation::ANIMATION_DURATION; use crate::animation::ANIMATION_ENABLED; use crate::animation::ANIMATION_MANAGER; @@ -10,6 +12,7 @@ use crate::com::SetCloak; use crate::focus_manager; use crate::stackbar_manager; use crate::windows_api; +use crate::AnimationStyle; use crate::SLOW_APPLICATION_COMPENSATION_TIME; use crate::SLOW_APPLICATION_IDENTIFIERS; use std::collections::HashMap; @@ -153,6 +156,76 @@ impl Serialize for Window { } } +struct WindowMoveRenderDispatcher { + prefix: AnimationPrefix, + hwnd: isize, + start_rect: Rect, + target_rect: Rect, + top: bool, + style: AnimationStyle, +} + +impl WindowMoveRenderDispatcher { + pub fn new( + prefix: AnimationPrefix, + hwnd: isize, + start_rect: Rect, + target_rect: Rect, + top: bool, + style: AnimationStyle, + ) -> Self { + Self { + prefix, + hwnd, + start_rect, + target_rect, + top, + style, + } + } +} + +impl RenderDispatcher for WindowMoveRenderDispatcher { + fn pre_render(&self) -> Result<()> { + border_manager::BORDER_TEMPORARILY_DISABLED.store(true, Ordering::SeqCst); + border_manager::send_notification(Some(self.hwnd)); + + stackbar_manager::STACKBAR_TEMPORARILY_DISABLED.store(true, Ordering::SeqCst); + stackbar_manager::send_notification(); + + Ok(()) + } + + fn render(&self, progress: f64) -> Result<()> { + let new_rect = self.start_rect.lerp(self.target_rect, progress, self.style); + + // using MoveWindow because it runs faster than SetWindowPos + // so animation have more fps and feel smoother + WindowsApi::move_window(self.hwnd, &new_rect, false)?; + WindowsApi::invalidate_rect(self.hwnd, None, false); + + Ok(()) + } + + fn post_render(&self) -> Result<()> { + WindowsApi::position_window(self.hwnd, &self.target_rect, self.top)?; + if ANIMATION_MANAGER.lock().count_in_progress(self.prefix) == 0 { + if WindowsApi::foreground_window().unwrap_or_default() == self.hwnd { + focus_manager::send_notification(self.hwnd) + } + + border_manager::BORDER_TEMPORARILY_DISABLED.store(false, Ordering::SeqCst); + stackbar_manager::STACKBAR_TEMPORARILY_DISABLED.store(false, Ordering::SeqCst); + + border_manager::send_notification(Some(self.hwnd)); + stackbar_manager::send_notification(); + transparency_manager::send_notification(); + } + + Ok(()) + } +} + impl Window { pub const fn hwnd(self) -> HWND { HWND(windows_api::as_ptr!(self.hwnd)) @@ -198,62 +271,6 @@ impl Window { ) } - pub fn animate_position(&self, start_rect: &Rect, target_rect: &Rect, top: bool) -> Result<()> { - let start_rect = *start_rect; - let target_rect = *target_rect; - let duration = Duration::from_millis(ANIMATION_DURATION.load(Ordering::SeqCst)); - let style = *ANIMATION_STYLE.lock(); - - border_manager::BORDER_TEMPORARILY_DISABLED.store(true, Ordering::SeqCst); - border_manager::send_notification(Some(self.hwnd)); - - stackbar_manager::STACKBAR_TEMPORARILY_DISABLED.store(true, Ordering::SeqCst); - stackbar_manager::send_notification(); - - let hwnd = self.hwnd; - - std::thread::spawn(move || { - Animation::animate( - new_animation_key(AnimationPrefix::WindowMove, hwnd.to_string()).as_str(), - duration, - |progress: f64| { - let new_rect = start_rect.lerp(target_rect, progress, style); - - if progress == 1.0 { - WindowsApi::position_window(hwnd, &new_rect, top)?; - if WindowsApi::foreground_window().unwrap_or_default() == hwnd { - focus_manager::send_notification(hwnd) - } - - if ANIMATION_MANAGER - .lock() - .animations_in_progress("window_move") - == 0 - { - border_manager::BORDER_TEMPORARILY_DISABLED - .store(false, Ordering::SeqCst); - stackbar_manager::STACKBAR_TEMPORARILY_DISABLED - .store(false, Ordering::SeqCst); - - border_manager::send_notification(Some(hwnd)); - stackbar_manager::send_notification(); - transparency_manager::send_notification(); - } - } else { - // using MoveWindow because it runs faster than SetWindowPos - // so animation have more fps and feel smoother - WindowsApi::move_window(hwnd, &new_rect, false)?; - WindowsApi::invalidate_rect(hwnd, None, false); - } - - Ok(()) - }, - ) - }); - - Ok(()) - } - pub fn set_position(&self, layout: &Rect, top: bool) -> Result<()> { let window_rect = WindowsApi::window_rect(self.hwnd)?; @@ -262,7 +279,23 @@ impl Window { } if ANIMATION_ENABLED.load(Ordering::SeqCst) { - self.animate_position(&window_rect, layout, top) + let duration = Duration::from_millis(ANIMATION_DURATION.load(Ordering::SeqCst)); + let style = *ANIMATION_STYLE.lock(); + + let render_dispatcher = WindowMoveRenderDispatcher::new( + AnimationPrefix::WindowMove, + self.hwnd, + window_rect, + *layout, + top, + style, + ); + + Animation::animate( + new_animation_key(AnimationPrefix::WindowMove, self.hwnd.to_string()), + duration, + render_dispatcher, + ) } else { WindowsApi::position_window(self.hwnd, layout, top) }