diff --git a/komorebi/src/animation/animation.rs b/komorebi/src/animation/animation.rs new file mode 100644 index 00000000..39e33571 --- /dev/null +++ b/komorebi/src/animation/animation.rs @@ -0,0 +1,124 @@ +use crate::core::Rect; +use color_eyre::Result; + +use schemars::JsonSchema; + +use serde::Deserialize; +use serde::Serialize; +use std::sync::atomic::Ordering; +use std::time::Duration; +use std::time::Instant; + +use super::style::apply_ease_func; +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 hwnd: isize, +} + +impl Animation { + pub fn new(hwnd: isize) -> Self { + Self { hwnd } + } + + /// Returns true if the animation needs to continue + pub fn cancel(&mut self) -> bool { + if !ANIMATION_MANAGER.lock().in_progress(self.hwnd) { + return true; + } + + // should be more than 0 + let cancel_idx = ANIMATION_MANAGER.lock().init_cancel(self.hwnd); + let max_duration = Duration::from_secs(1); + let spent_duration = Instant::now(); + + while ANIMATION_MANAGER.lock().in_progress(self.hwnd) { + if spent_duration.elapsed() >= max_duration { + ANIMATION_MANAGER.lock().end(self.hwnd); + } + + std::thread::sleep(Duration::from_millis( + ANIMATION_DURATION.load(Ordering::SeqCst) / 2, + )); + } + + let latest_cancel_idx = ANIMATION_MANAGER.lock().latest_cancel_idx(self.hwnd); + + ANIMATION_MANAGER.lock().end_cancel(self.hwnd); + + latest_cancel_idx == cancel_idx + } + + #[allow(clippy::cast_possible_truncation)] + pub fn lerp(start: i32, end: i32, t: f64) -> i32 { + let time = apply_ease_func(t); + f64::from(end - start) + .mul_add(time, f64::from(start)) + .round() as i32 + } + + pub fn lerp_rect(start_rect: &Rect, end_rect: &Rect, t: f64) -> Rect { + Rect { + left: Self::lerp(start_rect.left, end_rect.left, t), + top: Self::lerp(start_rect.top, end_rect.top, t), + right: Self::lerp(start_rect.right, end_rect.right, t), + bottom: Self::lerp(start_rect.bottom, end_rect.bottom, t), + } + } + + #[allow(clippy::cast_precision_loss)] + pub fn animate( + &mut self, + duration: Duration, + mut render_callback: impl FnMut(f64) -> Result<()>, + ) -> Result<()> { + if ANIMATION_MANAGER.lock().in_progress(self.hwnd) { + let should_animate = self.cancel(); + + if !should_animate { + return Ok(()); + } + } + + ANIMATION_MANAGER.lock().start(self.hwnd); + + 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(self.hwnd) { + // cancel animation + ANIMATION_MANAGER.lock().cancel(self.hwnd); + 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(); + + // 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(self.hwnd); + + // 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) + } +} diff --git a/komorebi/src/animation_manager.rs b/komorebi/src/animation/animation_manager.rs similarity index 96% rename from komorebi/src/animation_manager.rs rename to komorebi/src/animation/animation_manager.rs index ce0308cd..e425a086 100644 --- a/komorebi/src/animation_manager.rs +++ b/komorebi/src/animation/animation_manager.rs @@ -1,10 +1,8 @@ +use super::ANIMATIONS_IN_PROGRESS; use std::collections::hash_map::Entry; use std::collections::HashMap; -use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; -pub static ANIMATIONS_IN_PROGRESS: AtomicUsize = AtomicUsize::new(0); - #[derive(Debug, Clone, Copy)] struct AnimationState { pub in_progress: bool, diff --git a/komorebi/src/animation/mod.rs b/komorebi/src/animation/mod.rs new file mode 100644 index 00000000..149c8847 --- /dev/null +++ b/komorebi/src/animation/mod.rs @@ -0,0 +1,27 @@ +use crate::animation::animation_manager::AnimationManager; +use crate::core::animation::AnimationStyle; + +use lazy_static::lazy_static; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::AtomicU64; +use std::sync::atomic::AtomicUsize; +use std::sync::Arc; + +use parking_lot::Mutex; + +pub mod animation; +pub use animation::Animation; +pub mod animation_manager; +pub mod style; + +lazy_static! { + pub static ref ANIMATION_STYLE: Arc> = + Arc::new(Mutex::new(AnimationStyle::Linear)); + pub static ref ANIMATION_MANAGER: Arc> = + Arc::new(Mutex::new(AnimationManager::new())); +} + +pub static ANIMATION_ENABLED: AtomicBool = AtomicBool::new(false); +pub static ANIMATION_DURATION: AtomicU64 = AtomicU64::new(250); +pub static ANIMATION_FPS: AtomicU64 = AtomicU64::new(60); +pub static ANIMATIONS_IN_PROGRESS: AtomicUsize = AtomicUsize::new(0); diff --git a/komorebi/src/animation.rs b/komorebi/src/animation/style.rs similarity index 70% rename from komorebi/src/animation.rs rename to komorebi/src/animation/style.rs index c0461d8b..7ba4c75a 100644 --- a/komorebi/src/animation.rs +++ b/komorebi/src/animation/style.rs @@ -1,22 +1,7 @@ +use super::ANIMATION_STYLE; use crate::core::AnimationStyle; -use crate::core::Rect; -use color_eyre::Result; -use schemars::JsonSchema; - -use serde::Deserialize; -use serde::Serialize; use std::f64::consts::PI; -use std::sync::atomic::AtomicU64; -use std::sync::atomic::Ordering; -use std::time::Duration; -use std::time::Instant; - -use crate::ANIMATION_DURATION; -use crate::ANIMATION_MANAGER; -use crate::ANIMATION_STYLE; - -pub static ANIMATION_FPS: AtomicU64 = AtomicU64::new(60); pub trait Ease { fn evaluate(t: f64) -> f64; @@ -370,7 +355,8 @@ impl Ease for EaseInOutBounce { } } } -fn apply_ease_func(t: f64) -> f64 { + +pub fn apply_ease_func(t: f64) -> f64 { let style = *ANIMATION_STYLE.lock(); match style { @@ -406,112 +392,3 @@ fn apply_ease_func(t: f64) -> f64 { AnimationStyle::EaseInOutBounce => EaseInOutBounce::evaluate(t), } } - -#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq)] -pub struct Animation { - pub hwnd: isize, -} - -impl Animation { - pub fn new(hwnd: isize) -> Self { - Self { hwnd } - } - - /// Returns true if the animation needs to continue - pub fn cancel(&mut self) -> bool { - if !ANIMATION_MANAGER.lock().in_progress(self.hwnd) { - return true; - } - - // should be more than 0 - let cancel_idx = ANIMATION_MANAGER.lock().init_cancel(self.hwnd); - let max_duration = Duration::from_secs(1); - let spent_duration = Instant::now(); - - while ANIMATION_MANAGER.lock().in_progress(self.hwnd) { - if spent_duration.elapsed() >= max_duration { - ANIMATION_MANAGER.lock().end(self.hwnd); - } - - std::thread::sleep(Duration::from_millis( - ANIMATION_DURATION.load(Ordering::SeqCst) / 2, - )); - } - - let latest_cancel_idx = ANIMATION_MANAGER.lock().latest_cancel_idx(self.hwnd); - - ANIMATION_MANAGER.lock().end_cancel(self.hwnd); - - latest_cancel_idx == cancel_idx - } - - #[allow(clippy::cast_possible_truncation)] - pub fn lerp(start: i32, end: i32, t: f64) -> i32 { - let time = apply_ease_func(t); - f64::from(end - start) - .mul_add(time, f64::from(start)) - .round() as i32 - } - - pub fn lerp_rect(start_rect: &Rect, end_rect: &Rect, t: f64) -> Rect { - Rect { - left: Self::lerp(start_rect.left, end_rect.left, t), - top: Self::lerp(start_rect.top, end_rect.top, t), - right: Self::lerp(start_rect.right, end_rect.right, t), - bottom: Self::lerp(start_rect.bottom, end_rect.bottom, t), - } - } - - #[allow(clippy::cast_precision_loss)] - pub fn animate( - &mut self, - duration: Duration, - mut render_callback: impl FnMut(f64) -> Result<()>, - ) -> Result<()> { - if ANIMATION_MANAGER.lock().in_progress(self.hwnd) { - let should_animate = self.cancel(); - - if !should_animate { - return Ok(()); - } - } - - ANIMATION_MANAGER.lock().start(self.hwnd); - - 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(self.hwnd) { - // cancel animation - ANIMATION_MANAGER.lock().cancel(self.hwnd); - 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(); - - // 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(self.hwnd); - - // 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) - } -} diff --git a/komorebi/src/lib.rs b/komorebi/src/lib.rs index 0d0078b2..82a33cfb 100644 --- a/komorebi/src/lib.rs +++ b/komorebi/src/lib.rs @@ -1,7 +1,6 @@ #![warn(clippy::all)] pub mod animation; -pub mod animation_manager; pub mod border_manager; pub mod com; #[macro_use] @@ -47,8 +46,6 @@ use std::sync::atomic::AtomicU64; use std::sync::atomic::Ordering; use std::sync::Arc; -pub use animation::*; -pub use animation_manager::*; pub use colour::*; pub use core::*; pub use process_command::*; @@ -216,12 +213,6 @@ lazy_static! { ) }; - static ref ANIMATION_STYLE: Arc> = - Arc::new(Mutex::new(AnimationStyle::Linear)); - - static ref ANIMATION_MANAGER: Arc> = - Arc::new(Mutex::new(AnimationManager::new())); - // Use app-specific titlebar removal options where possible // eg. Windows Terminal, IntelliJ IDEA, Firefox static ref NO_TITLEBAR: Arc>> = Arc::new(Mutex::new(vec![])); @@ -238,8 +229,6 @@ pub static CUSTOM_FFM: AtomicBool = AtomicBool::new(false); pub static SESSION_ID: AtomicU32 = AtomicU32::new(0); pub static REMOVE_TITLEBARS: AtomicBool = AtomicBool::new(false); -pub static ANIMATION_ENABLED: AtomicBool = AtomicBool::new(false); -pub static ANIMATION_DURATION: AtomicU64 = AtomicU64::new(250); pub static SLOW_APPLICATION_COMPENSATION_TIME: AtomicU64 = AtomicU64::new(20); diff --git a/komorebi/src/process_command.rs b/komorebi/src/process_command.rs index 18fe5661..cf8c704c 100644 --- a/komorebi/src/process_command.rs +++ b/komorebi/src/process_command.rs @@ -40,6 +40,10 @@ use crate::core::StateQuery; use crate::core::WindowContainerBehaviour; use crate::core::WindowKind; +use crate::animation::ANIMATION_DURATION; +use crate::animation::ANIMATION_ENABLED; +use crate::animation::ANIMATION_FPS; +use crate::animation::ANIMATION_STYLE; use crate::border_manager; use crate::border_manager::IMPLEMENTATION; use crate::border_manager::STYLE; @@ -63,10 +67,6 @@ use crate::GlobalState; use crate::Notification; use crate::NotificationEvent; use crate::State; -use crate::ANIMATION_DURATION; -use crate::ANIMATION_ENABLED; -use crate::ANIMATION_FPS; -use crate::ANIMATION_STYLE; use crate::CUSTOM_FFM; use crate::DATA_DIR; use crate::DISPLAY_INDEX_PREFERENCES; diff --git a/komorebi/src/static_config.rs b/komorebi/src/static_config.rs index 0f48ae90..978c40b8 100644 --- a/komorebi/src/static_config.rs +++ b/komorebi/src/static_config.rs @@ -1,3 +1,7 @@ +use crate::animation::ANIMATION_DURATION; +use crate::animation::ANIMATION_ENABLED; +use crate::animation::ANIMATION_FPS; +use crate::animation::ANIMATION_STYLE; use crate::border_manager; use crate::border_manager::ZOrder; use crate::border_manager::IMPLEMENTATION; @@ -28,10 +32,6 @@ use crate::window_manager_event::WindowManagerEvent; use crate::windows_api::WindowsApi; use crate::workspace::Workspace; use crate::CrossBoundaryBehaviour; -use crate::ANIMATION_DURATION; -use crate::ANIMATION_ENABLED; -use crate::ANIMATION_FPS; -use crate::ANIMATION_STYLE; use crate::DATA_DIR; use crate::DEFAULT_CONTAINER_PADDING; use crate::DEFAULT_WORKSPACE_PADDING; diff --git a/komorebi/src/window.rs b/komorebi/src/window.rs index a39060d5..6b956648 100644 --- a/komorebi/src/window.rs +++ b/komorebi/src/window.rs @@ -1,11 +1,11 @@ +use crate::animation::ANIMATIONS_IN_PROGRESS; +use crate::animation::ANIMATION_DURATION; +use crate::animation::ANIMATION_ENABLED; use crate::border_manager; use crate::com::SetCloak; use crate::focus_manager; use crate::stackbar_manager; use crate::windows_api; -use crate::ANIMATIONS_IN_PROGRESS; -use crate::ANIMATION_DURATION; -use crate::ANIMATION_ENABLED; use crate::SLOW_APPLICATION_COMPENSATION_TIME; use crate::SLOW_APPLICATION_IDENTIFIERS; use std::collections::HashMap; @@ -35,7 +35,7 @@ use crate::core::ApplicationIdentifier; use crate::core::HidingBehaviour; use crate::core::Rect; -use crate::animation::Animation; +use crate::animation::animation::Animation; use crate::styles::ExtendedWindowStyle; use crate::styles::WindowStyle; use crate::transparency_manager;