mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-03-29 05:31:55 +02:00
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:
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -10,6 +10,7 @@ use strum::EnumString;
|
||||
)]
|
||||
pub enum AnimationPrefix {
|
||||
WindowMove,
|
||||
WindowTransparency,
|
||||
}
|
||||
|
||||
pub fn new_animation_key(prefix: AnimationPrefix, key: String) -> String {
|
||||
|
||||
@@ -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()?;
|
||||
|
||||
@@ -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()?;
|
||||
|
||||
@@ -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<()> {
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user