Skip to content

Commit

Permalink
fix(animation): Fixed cancelling logic
Browse files Browse the repository at this point in the history
Added static animation state manager for tracking "in_progress" and
"is_cancelled" states. The idea is not to have states in Animation
struct but to keep them in HashMap<hwnd, AnimationState> behind
reference (Arc<Mutex<>>). So we each animation frame we have access to
state and can cancel animation if we have to.

Need review and testings
  • Loading branch information
thearturca authored and LGUG2Z committed Apr 30, 2024
1 parent 198b5e8 commit 57e9b2f
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 40 deletions.
57 changes: 21 additions & 36 deletions komorebi/src/animation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ use schemars::JsonSchema;
use serde::Deserialize;
use serde::Serialize;
use std::f64::consts::PI;
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 trait Ease {
Expand Down Expand Up @@ -403,48 +406,30 @@ fn apply_ease_func(t: f64) -> f64 {

#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize, JsonSchema)]
pub struct Animation {
// is_cancel: AtomicBool,
// pub in_progress: AtomicBool,
is_cancel: bool,
pub in_progress: bool,
}

// impl Default for Animation {
// fn default() -> Self {
// Animation {
// // I'm not sure if this is the right way to do it
// // I've tried to use Arc<Mutex<bool>> but it dooes not implement Copy trait
// // and I dont want to rewrite everything cause I'm not experienced with rust
// // Down here you can see the idea I've tried to achive like in any other OOP language
// // My thought is that in order to prevent Google Chrome breaking render window
// // I need to cancel animation if user starting new window movement. So window stops
// // moving at one point and then fires new animation.
// // But my approach does not work because of rust borrowing rules and wired pointers
// // lifetime annotation that I dont know how to use.
// is_cancel: false,
// in_progress: false,
// // is_cancel: AtomicBool::new(false),
// // in_progress: AtomicBool::new(false),
// }
// }
// }
pub hwnd: isize,
}

impl Animation {
pub fn new(hwnd: isize) -> Self {
Self { hwnd }
}
pub fn cancel(&mut self) {
if !self.in_progress {
if !ANIMATION_MANAGER.lock().in_progress(self.hwnd) {
return;
}

self.is_cancel = true;
ANIMATION_MANAGER.lock().cancel(self.hwnd);
let max_duration = Duration::from_secs(1);
let spent_duration = Instant::now();

while self.in_progress {
while ANIMATION_MANAGER.lock().in_progress(self.hwnd) {
if spent_duration.elapsed() >= max_duration {
self.in_progress = false;
ANIMATION_MANAGER.lock().end(self.hwnd);
}

std::thread::sleep(Duration::from_millis(16));
std::thread::sleep(Duration::from_millis(
ANIMATION_DURATION.load(Ordering::SeqCst) / 2,
));
}
}

Expand All @@ -469,9 +454,10 @@ impl Animation {
duration: Duration,
mut f: impl FnMut(f64) -> Result<()>,
) -> Result<()> {
self.in_progress = true;
ANIMATION_MANAGER.lock().start(self.hwnd);

// set target frame time to match 240 fps (my max refresh rate of monitor)
// probably not the best way to do it is take actual monitor refresh rate
// probably the best way to do it is take actual monitor refresh rate
// or make it configurable
let target_frame_time = Duration::from_millis(1000 / 240);
let mut progress = 0.0;
Expand All @@ -480,11 +466,10 @@ impl Animation {
// start animation
while progress < 1.0 {
// check if animation is cancelled
if self.is_cancel {
if ANIMATION_MANAGER.lock().is_cancelled(self.hwnd) {
// cancel animation
// set all flags
self.is_cancel = !self.is_cancel;
self.in_progress = false;
ANIMATION_MANAGER.lock().end(self.hwnd);
return Ok(());
}

Expand All @@ -499,7 +484,7 @@ impl Animation {
}
}

self.in_progress = false;
ANIMATION_MANAGER.lock().end(self.hwnd);

// limit progress to 1.0 if animation took longer
if progress > 1.0 {
Expand Down
76 changes: 76 additions & 0 deletions komorebi/src/animation_manager.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use std::collections::HashMap;

#[derive(Debug, Clone, Copy)]
struct AnimationState {
pub in_progress: bool,
pub is_cancelled: bool,
}

#[derive(Debug)]
pub struct AnimationManager {
animations: HashMap<isize, AnimationState>,
}

impl AnimationManager {
pub fn new() -> Self {
Self {
animations: HashMap::new(),
}
}

pub fn is_cancelled(&self, hwnd: isize) -> bool {
if !self.animations.contains_key(&hwnd) {
return false;
}

self.animations.get(&hwnd).unwrap().is_cancelled
}

pub fn in_progress(&self, hwnd: isize) -> bool {
if !self.animations.contains_key(&hwnd) {
return false;
}

self.animations.get(&hwnd).unwrap().in_progress
}

pub fn cancel(&mut self, hwnd: isize) {
if !self.animations.contains_key(&hwnd) {
return;
}

let state = self.animations.get_mut(&hwnd).unwrap();
state.is_cancelled = true;
}

pub fn start(&mut self, hwnd: isize) {
if !self.animations.contains_key(&hwnd) {
self.animations.insert(
hwnd,
AnimationState {
in_progress: true,
is_cancelled: false,
},
);
return;
}

let state = self.animations.get_mut(&hwnd).unwrap();

if !state.in_progress {
state.in_progress = true;
}
}

pub fn end(&mut self, hwnd: isize) {
if !self.animations.contains_key(&hwnd) {
return;
}

let state = self.animations.get_mut(&hwnd).unwrap();
state.in_progress = false;
state.is_cancelled = false;

self.animations.remove(&hwnd);
}
}
5 changes: 5 additions & 0 deletions komorebi/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod animation;
pub mod animation_manager;
pub mod border;
pub mod com;
#[macro_use]
Expand Down Expand Up @@ -40,6 +41,7 @@ use std::sync::atomic::Ordering;
use std::sync::Arc;

pub use animation::*;
pub use animation_manager::*;
pub use colour::*;
pub use hidden::*;
pub use process_command::*;
Expand Down Expand Up @@ -211,6 +213,9 @@ lazy_static! {
static ref ANIMATION_STYLE: Arc<Mutex<AnimationStyle >> =
Arc::new(Mutex::new(AnimationStyle::Linear));

static ref ANIMATION_MANAGER: Arc<Mutex<AnimationManager>> =
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<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![]));
Expand Down
7 changes: 3 additions & 4 deletions komorebi/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::com::SetCloak;
use crate::winevent_listener;
use crate::ANIMATION_DURATION;
use crate::ANIMATION_ENABLED;
use crate::ANIMATION_MANAGER;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::fmt::Display;
Expand Down Expand Up @@ -131,7 +132,7 @@ impl Window {
pub fn new(hwnd: isize) -> Self {
Self {
hwnd,
animation: Animation::default(),
animation: Animation::new(hwnd),
}
}

Expand Down Expand Up @@ -198,9 +199,7 @@ impl Window {
pub fn set_position(&mut self, layout: &Rect, top: bool) -> Result<()> {
let rect = *layout;
if ANIMATION_ENABLED.load(Ordering::SeqCst) {
// check if animation is in progress
if self.animation.in_progress {
// wait for cancel animation
if ANIMATION_MANAGER.lock().in_progress(self.hwnd) {
self.animation.cancel();
}

Expand Down

0 comments on commit 57e9b2f

Please sign in to comment.