feat(animation): implement window transparency animation

this commit also fixes graceful shutdown of animations by disabling them
before exit and wait for all remaining animations for 20 seconds
This commit is contained in:
thearturca
2024-09-28 20:26:34 +03:00
parent 8290f143a6
commit 2400d757fe
8 changed files with 171 additions and 24 deletions

View File

@@ -17,6 +17,21 @@ use super::ANIMATION_MANAGER;
pub struct Animation;
impl Animation {
pub fn wait_for_all_animations() {
let max_duration = Duration::from_secs(20);
let spent_duration = Instant::now();
while ANIMATION_MANAGER.lock().count() > 0 {
if spent_duration.elapsed() >= max_duration {
break;
}
std::thread::sleep(Duration::from_millis(
ANIMATION_DURATION.load(Ordering::SeqCst),
));
}
}
/// Returns true if the animation needs to continue
pub fn cancel(animation_key: &str) -> bool {
// should be more than 0
@@ -29,9 +44,7 @@ impl Animation {
ANIMATION_MANAGER.lock().end(animation_key);
}
std::thread::sleep(Duration::from_millis(
ANIMATION_DURATION.load(Ordering::SeqCst) / 2,
));
std::thread::sleep(Duration::from_millis(250 / 2));
}
let latest_cancel_idx = ANIMATION_MANAGER.lock().latest_cancel_idx(animation_key);

View File

@@ -108,4 +108,8 @@ impl AnimationManager {
.filter(|key| key.starts_with(animation_key_prefix.to_string().as_str()))
.count()
}
pub fn count(&self) -> usize {
self.animations.len()
}
}

View File

@@ -24,6 +24,12 @@ impl Lerp for f64 {
}
}
impl Lerp for u8 {
fn lerp(self, end: u8, time: f64, style: AnimationStyle) -> u8 {
(self as f64).lerp(end as f64, time, style) as u8
}
}
impl Lerp for Rect {
fn lerp(self, end: Rect, time: f64, style: AnimationStyle) -> Rect {
Rect {

View File

@@ -10,6 +10,7 @@ use strum::EnumString;
)]
pub enum AnimationPrefix {
WindowMove,
WindowTransparency,
}
pub fn new_animation_key(prefix: AnimationPrefix, key: String) -> String {

View File

@@ -17,6 +17,8 @@ use std::time::Duration;
use clap::Parser;
use color_eyre::Result;
use crossbeam_utils::Backoff;
use komorebi::animation::Animation;
use komorebi::animation::ANIMATION_ENABLED;
#[cfg(feature = "deadlock_detection")]
use parking_lot::deadlock;
use parking_lot::Mutex;
@@ -287,7 +289,9 @@ fn main() -> Result<()> {
tracing::error!("received ctrl-c, restoring all hidden windows and terminating process");
ANIMATION_ENABLED.store(false, Ordering::SeqCst);
wm.lock().restore_all_windows()?;
Animation::wait_for_all_animations();
if WindowsApi::focus_follows_mouse()? {
WindowsApi::disable_focus_follows_mouse()?;

View File

@@ -22,6 +22,7 @@ use schemars::gen::SchemaSettings;
use schemars::schema_for;
use uds_windows::UnixStream;
use crate::animation::Animation;
use crate::core::config_generation::ApplicationConfiguration;
use crate::core::config_generation::IdWithIdentifier;
use crate::core::config_generation::MatchingRule;
@@ -876,7 +877,10 @@ impl WindowManager {
tracing::info!(
"received stop command, restoring all hidden windows and terminating process"
);
ANIMATION_ENABLED.store(false, Ordering::SeqCst);
self.restore_all_windows()?;
Animation::wait_for_all_animations();
if WindowsApi::focus_follows_mouse()? {
WindowsApi::disable_focus_follows_mouse()?;

View File

@@ -167,7 +167,6 @@ struct WindowMoveRenderDispatcher {
impl WindowMoveRenderDispatcher {
pub fn new(
prefix: AnimationPrefix,
hwnd: isize,
start_rect: Rect,
target_rect: Rect,
@@ -175,7 +174,7 @@ impl WindowMoveRenderDispatcher {
style: AnimationStyle,
) -> Self {
Self {
prefix,
prefix: AnimationPrefix::WindowMove,
hwnd,
start_rect,
target_rect,
@@ -226,6 +225,71 @@ impl RenderDispatcher for WindowMoveRenderDispatcher {
}
}
struct WindowTransparencyRenderDispatcher {
prefix: AnimationPrefix,
hwnd: isize,
start_opacity: u8,
target_opacity: u8,
style: AnimationStyle,
is_opaque: bool,
is_transparent: bool,
}
impl WindowTransparencyRenderDispatcher {
pub fn new(
hwnd: isize,
is_opaque: bool,
is_transparent: bool,
start_opacity: u8,
target_opacity: u8,
style: AnimationStyle,
) -> Self {
Self {
prefix: AnimationPrefix::WindowTransparency,
hwnd,
start_opacity,
target_opacity,
style,
is_opaque,
is_transparent,
}
}
}
impl RenderDispatcher for WindowTransparencyRenderDispatcher {
fn pre_render(&self) -> Result<()> {
//transparent
if self.is_transparent {
let window = Window::from(self.hwnd);
let mut ex_style = window.ex_style()?;
ex_style.insert(ExtendedWindowStyle::LAYERED);
window.update_ex_style(&ex_style)?;
}
Ok(())
}
fn render(&self, progress: f64) -> Result<()> {
WindowsApi::set_transparent(
self.hwnd,
self.start_opacity
.lerp(self.target_opacity, progress, self.style),
)
}
fn post_render(&self) -> Result<()> {
//opaque
if self.is_opaque {
let window = Window::from(self.hwnd);
let mut ex_style = window.ex_style()?;
ex_style.remove(ExtendedWindowStyle::LAYERED);
window.update_ex_style(&ex_style)?;
}
Ok(())
}
}
impl Window {
pub const fn hwnd(self) -> HWND {
HWND(windows_api::as_ptr!(self.hwnd))
@@ -282,17 +346,11 @@ impl Window {
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,
);
let render_dispatcher =
WindowMoveRenderDispatcher::new(self.hwnd, window_rect, *layout, top, style);
Animation::animate(
new_animation_key(AnimationPrefix::WindowMove, self.hwnd.to_string()),
new_animation_key(render_dispatcher.prefix, self.hwnd.to_string()),
duration,
render_dispatcher,
)
@@ -404,19 +462,60 @@ impl Window {
}
pub fn transparent(self) -> Result<()> {
let mut ex_style = self.ex_style()?;
ex_style.insert(ExtendedWindowStyle::LAYERED);
self.update_ex_style(&ex_style)?;
WindowsApi::set_transparent(
self.hwnd,
transparency_manager::TRANSPARENCY_ALPHA.load_consume(),
)
if ANIMATION_ENABLED.load(Ordering::SeqCst) {
let duration = Duration::from_millis(ANIMATION_DURATION.load(Ordering::SeqCst));
let style = *ANIMATION_STYLE.lock();
let render_dispatcher = WindowTransparencyRenderDispatcher::new(
self.hwnd,
false,
true,
WindowsApi::get_transparent(self.hwnd).unwrap_or(255),
transparency_manager::TRANSPARENCY_ALPHA.load_consume(),
style,
);
Animation::animate(
new_animation_key(render_dispatcher.prefix, self.hwnd.to_string()),
duration,
render_dispatcher,
)
} else {
let mut ex_style = self.ex_style()?;
ex_style.insert(ExtendedWindowStyle::LAYERED);
self.update_ex_style(&ex_style)?;
WindowsApi::set_transparent(
self.hwnd,
transparency_manager::TRANSPARENCY_ALPHA.load_consume(),
)
}
}
pub fn opaque(self) -> Result<()> {
let mut ex_style = self.ex_style()?;
ex_style.remove(ExtendedWindowStyle::LAYERED);
self.update_ex_style(&ex_style)
if ANIMATION_ENABLED.load(Ordering::SeqCst) {
let duration = Duration::from_millis(ANIMATION_DURATION.load(Ordering::SeqCst));
let style = *ANIMATION_STYLE.lock();
let render_dispatcher = WindowTransparencyRenderDispatcher::new(
self.hwnd,
true,
false,
WindowsApi::get_transparent(self.hwnd)
.unwrap_or(transparency_manager::TRANSPARENCY_ALPHA.load_consume()),
255,
style,
);
Animation::animate(
new_animation_key(render_dispatcher.prefix, self.hwnd.to_string()),
duration,
render_dispatcher,
)
} else {
let mut ex_style = self.ex_style()?;
ex_style.remove(ExtendedWindowStyle::LAYERED);
self.update_ex_style(&ex_style)
}
}
pub fn set_accent(self, colour: u32) -> Result<()> {

View File

@@ -77,6 +77,7 @@ use windows::Win32::UI::WindowsAndMessaging::EnumWindows;
use windows::Win32::UI::WindowsAndMessaging::GetCursorPos;
use windows::Win32::UI::WindowsAndMessaging::GetDesktopWindow;
use windows::Win32::UI::WindowsAndMessaging::GetForegroundWindow;
use windows::Win32::UI::WindowsAndMessaging::GetLayeredWindowAttributes;
use windows::Win32::UI::WindowsAndMessaging::GetTopWindow;
use windows::Win32::UI::WindowsAndMessaging::GetWindow;
use windows::Win32::UI::WindowsAndMessaging::GetWindowLongPtrW;
@@ -1127,6 +1128,21 @@ impl WindowsApi {
Ok(())
}
pub fn get_transparent(hwnd: isize) -> Result<u8> {
unsafe {
let mut alpha: u8 = u8::default();
let mut color_ref = COLORREF(-1i32 as u32);
let mut flags = LWA_ALPHA;
GetLayeredWindowAttributes(
HWND(as_ptr!(hwnd)),
Some(std::ptr::addr_of_mut!(color_ref)),
Some(std::ptr::addr_of_mut!(alpha)),
Some(std::ptr::addr_of_mut!(flags)),
)?;
Ok(alpha)
}
}
pub fn create_hidden_window(name: PCWSTR, instance: isize) -> Result<isize> {
unsafe {
CreateWindowExW(