mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-01-14 14:23:36 +01:00
Compare commits
1 Commits
feature/aa
...
feature/an
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
06f79544ce |
42
komorebi-core/src/animation.rs
Normal file
42
komorebi-core/src/animation.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use clap::ValueEnum;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use strum::Display;
|
||||
use strum::EnumString;
|
||||
|
||||
#[derive(
|
||||
Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, JsonSchema,
|
||||
)]
|
||||
pub enum AnimationStyle {
|
||||
Linear,
|
||||
EaseInSine,
|
||||
EaseOutSine,
|
||||
EaseInOutSine,
|
||||
EaseInQuad,
|
||||
EaseOutQuad,
|
||||
EaseInOutQuad,
|
||||
EaseInCubic,
|
||||
EaseInOutCubic,
|
||||
EaseInQuart,
|
||||
EaseOutQuart,
|
||||
EaseInOutQuart,
|
||||
EaseInQuint,
|
||||
EaseOutQuint,
|
||||
EaseInOutQuint,
|
||||
EaseInExpo,
|
||||
EaseOutExpo,
|
||||
EaseInOutExpo,
|
||||
EaseInCirc,
|
||||
EaseOutCirc,
|
||||
EaseInOutCirc,
|
||||
EaseInBack,
|
||||
EaseOutBack,
|
||||
EaseInOutBack,
|
||||
EaseInElastic,
|
||||
EaseOutElastic,
|
||||
EaseInOutElastic,
|
||||
EaseInBounce,
|
||||
EaseOutBounce,
|
||||
EaseInOutBounce,
|
||||
}
|
||||
@@ -14,6 +14,7 @@ use serde::Serialize;
|
||||
use strum::Display;
|
||||
use strum::EnumString;
|
||||
|
||||
pub use animation::AnimationStyle;
|
||||
pub use arrangement::Arrangement;
|
||||
pub use arrangement::Axis;
|
||||
pub use custom_layout::CustomLayout;
|
||||
@@ -24,6 +25,7 @@ pub use layout::Layout;
|
||||
pub use operation_direction::OperationDirection;
|
||||
pub use rect::Rect;
|
||||
|
||||
pub mod animation;
|
||||
pub mod arrangement;
|
||||
pub mod config_generation;
|
||||
pub mod custom_layout;
|
||||
@@ -134,6 +136,10 @@ pub enum SocketMessage {
|
||||
WatchConfiguration(bool),
|
||||
CompleteConfiguration,
|
||||
AltFocusHack(bool),
|
||||
Animation(bool),
|
||||
AnimationDuration(u64),
|
||||
AnimationFps(u64),
|
||||
AnimationStyle(AnimationStyle),
|
||||
#[serde(alias = "ActiveWindowBorder")]
|
||||
Border(bool),
|
||||
#[serde(alias = "ActiveWindowBorderColour")]
|
||||
|
||||
@@ -84,4 +84,14 @@ impl Rect {
|
||||
bottom: (self.bottom * rect_dpi) / system_dpi,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn rect(&self) -> RECT {
|
||||
RECT {
|
||||
left: self.left,
|
||||
top: self.top,
|
||||
right: self.left + self.right,
|
||||
bottom: self.top + self.bottom,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,7 +218,7 @@ extern "system" fn enum_window(
|
||||
lparam: windows::Win32::Foundation::LPARAM,
|
||||
) -> windows::Win32::Foundation::BOOL {
|
||||
let windows = unsafe { &mut *(lparam.0 as *mut Vec<Window>) };
|
||||
let window = Window { hwnd: hwnd.0 };
|
||||
let window = Window::from(hwnd.0);
|
||||
|
||||
if window.is_window()
|
||||
&& !window.is_miminized()
|
||||
@@ -246,9 +246,7 @@ impl eframe::App for KomorebiGui {
|
||||
ui.set_width(ctx.screen_rect().width());
|
||||
ui.collapsing("Debugging", |ui| {
|
||||
ui.collapsing("Window Rules", |ui| {
|
||||
let window = Window {
|
||||
hwnd: self.debug_hwnd,
|
||||
};
|
||||
let window = Window::from(self.debug_hwnd);
|
||||
|
||||
let label = if let (Ok(title), Ok(exe)) = (window.title(), window.exe()) {
|
||||
format!("{title} ({exe})")
|
||||
|
||||
503
komorebi/src/animation.rs
Normal file
503
komorebi/src/animation.rs
Normal file
@@ -0,0 +1,503 @@
|
||||
use color_eyre::Result;
|
||||
use komorebi_core::AnimationStyle;
|
||||
use komorebi_core::Rect;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
pub struct Linear;
|
||||
|
||||
impl Ease for Linear {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
t
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInSine;
|
||||
|
||||
impl Ease for EaseInSine {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
1.0 - f64::cos((t * PI) / 2.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutSine;
|
||||
|
||||
impl Ease for EaseOutSine {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
f64::sin((t * PI) / 2.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutSine;
|
||||
|
||||
impl Ease for EaseInOutSine {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
-(f64::cos(PI * t) - 1.0) / 2.0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInQuad;
|
||||
|
||||
impl Ease for EaseInQuad {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
t * t
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutQuad;
|
||||
|
||||
impl Ease for EaseOutQuad {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
(1.0 - t).mul_add(-1.0 - t, 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutQuad;
|
||||
|
||||
impl Ease for EaseInOutQuad {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if t < 0.5 {
|
||||
2.0 * t * t
|
||||
} else {
|
||||
1.0 - (-2.0f64).mul_add(t, 2.0).powi(2) / 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInCubic;
|
||||
|
||||
impl Ease for EaseInCubic {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
t * t * t
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutCubic;
|
||||
|
||||
impl Ease for EaseOutCubic {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
1.0 - (1.0 - t).powi(3)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutCubic;
|
||||
|
||||
impl Ease for EaseInOutCubic {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if t < 0.5 {
|
||||
4.0 * t * t * t
|
||||
} else {
|
||||
1.0 - (-2.0f64).mul_add(t, 2.0).powi(3) / 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInQuart;
|
||||
|
||||
impl Ease for EaseInQuart {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
t * t * t * t
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutQuart;
|
||||
|
||||
impl Ease for EaseOutQuart {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
1.0 - (1.0 - t).powi(4)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutQuart;
|
||||
|
||||
impl Ease for EaseInOutQuart {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if t < 0.5 {
|
||||
8.0 * t * t * t * t
|
||||
} else {
|
||||
1.0 - (-2.0f64).mul_add(t, 2.0).powi(4) / 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInQuint;
|
||||
|
||||
impl Ease for EaseInQuint {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
t * t * t * t * t
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutQuint;
|
||||
|
||||
impl Ease for EaseOutQuint {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
1.0 - (1.0 - t).powi(5)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutQuint;
|
||||
|
||||
impl Ease for EaseInOutQuint {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if t < 0.5 {
|
||||
16.0 * t * t * t * t
|
||||
} else {
|
||||
1.0 - (-2.0f64).mul_add(t, 2.0).powi(5) / 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInExpo;
|
||||
|
||||
impl Ease for EaseInExpo {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if t == 0.0 {
|
||||
return t;
|
||||
}
|
||||
|
||||
10.0f64.mul_add(t, -10.0).exp2()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutExpo;
|
||||
|
||||
impl Ease for EaseOutExpo {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if (t - 1.0).abs() < f64::EPSILON {
|
||||
return t;
|
||||
}
|
||||
|
||||
1.0 - (-10.0 * t).exp2()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutExpo;
|
||||
|
||||
impl Ease for EaseInOutExpo {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if t == 0.0 || (t - 1.0).abs() < f64::EPSILON {
|
||||
return t;
|
||||
}
|
||||
|
||||
if t < 0.5 {
|
||||
20.0f64.mul_add(t, -10.0).exp2() / 2.0
|
||||
} else {
|
||||
(2.0 - (-20.0f64).mul_add(t, 10.0).exp2()) / 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInCirc;
|
||||
|
||||
impl Ease for EaseInCirc {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
1.0 - f64::sqrt(t.mul_add(-t, 1.0))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutCirc;
|
||||
|
||||
impl Ease for EaseOutCirc {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
f64::sqrt((t - 1.0).mul_add(-(t - 1.0), 1.0))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutCirc;
|
||||
|
||||
impl Ease for EaseInOutCirc {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if t < 0.5 {
|
||||
(1.0 - f64::sqrt((2.0 * t).mul_add(-(2.0 * t), 1.0))) / 2.0
|
||||
} else {
|
||||
(f64::sqrt(
|
||||
(-2.0f64)
|
||||
.mul_add(t, 2.0)
|
||||
.mul_add(-(-2.0f64).mul_add(t, 2.0), 1.0),
|
||||
) + 1.0)
|
||||
/ 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInBack;
|
||||
|
||||
impl Ease for EaseInBack {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
let c1 = 1.70158;
|
||||
let c3 = c1 + 1.0;
|
||||
|
||||
(c3 * t * t).mul_add(t, -c1 * t * t)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutBack;
|
||||
|
||||
impl Ease for EaseOutBack {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
let c1: f64 = 1.70158;
|
||||
let c3: f64 = c1 + 1.0;
|
||||
|
||||
c1.mul_add((t - 1.0).powi(2), c3.mul_add((t - 1.0).powi(3), 1.0))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutBack;
|
||||
|
||||
impl Ease for EaseInOutBack {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
let c1: f64 = 1.70158;
|
||||
let c2: f64 = c1 * 1.525;
|
||||
|
||||
if t < 0.5 {
|
||||
((2.0 * t).powi(2) * ((c2 + 1.0) * 2.0).mul_add(t, -c2)) / 2.0
|
||||
} else {
|
||||
((2.0f64.mul_add(t, -2.0))
|
||||
.powi(2)
|
||||
.mul_add((c2 + 1.0).mul_add(t.mul_add(2.0, -2.0), c2), 2.0))
|
||||
/ 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInElastic;
|
||||
|
||||
impl Ease for EaseInElastic {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if (t - 1.0).abs() < f64::EPSILON || t == 0.0 {
|
||||
return t;
|
||||
}
|
||||
|
||||
let c4 = (2.0 * PI) / 3.0;
|
||||
|
||||
-(10.0f64.mul_add(t, -10.0).exp2()) * f64::sin(t.mul_add(10.0, -10.75) * c4)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutElastic;
|
||||
|
||||
impl Ease for EaseOutElastic {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if (t - 1.0).abs() < f64::EPSILON || t == 0.0 {
|
||||
return t;
|
||||
}
|
||||
|
||||
let c4 = (2.0 * PI) / 3.0;
|
||||
|
||||
(-10.0 * t)
|
||||
.exp2()
|
||||
.mul_add(f64::sin(t.mul_add(10.0, -0.75) * c4), 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutElastic;
|
||||
|
||||
impl Ease for EaseInOutElastic {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if (t - 1.0).abs() < f64::EPSILON || t == 0.0 {
|
||||
return t;
|
||||
}
|
||||
|
||||
let c5 = (2.0 * PI) / 4.5;
|
||||
|
||||
if t < 0.5 {
|
||||
-(20.0f64.mul_add(t, -10.0).exp2() * f64::sin(20.0f64.mul_add(t, -11.125) * c5)) / 2.0
|
||||
} else {
|
||||
((-20.0f64).mul_add(t, 10.0).exp2() * f64::sin(20.0f64.mul_add(t, -11.125) * c5)) / 2.0
|
||||
+ 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInBounce;
|
||||
|
||||
impl Ease for EaseInBounce {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
1.0 - EaseOutBounce::evaluate(1.0 - t)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutBounce;
|
||||
|
||||
impl Ease for EaseOutBounce {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
let mut time = t;
|
||||
let n1 = 7.5625;
|
||||
let d1 = 2.75;
|
||||
|
||||
if t < 1.0 / d1 {
|
||||
n1 * time * time
|
||||
} else if time < 2.0 / d1 {
|
||||
time -= 1.5 / d1;
|
||||
(n1 * time).mul_add(time, 0.75)
|
||||
} else if time < 2.5 / d1 {
|
||||
time -= 2.25 / d1;
|
||||
(n1 * time).mul_add(time, 0.9375)
|
||||
} else {
|
||||
time -= 2.625 / d1;
|
||||
(n1 * time).mul_add(time, 0.984_375)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutBounce;
|
||||
|
||||
impl Ease for EaseInOutBounce {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if t < 0.5 {
|
||||
(1.0 - EaseOutBounce::evaluate(2.0f64.mul_add(-t, 1.0))) / 2.0
|
||||
} else {
|
||||
(1.0 + EaseOutBounce::evaluate(2.0f64.mul_add(t, -1.0))) / 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
fn apply_ease_func(t: f64) -> f64 {
|
||||
let style = *ANIMATION_STYLE.lock();
|
||||
|
||||
match style {
|
||||
AnimationStyle::Linear => Linear::evaluate(t),
|
||||
AnimationStyle::EaseInSine => EaseInSine::evaluate(t),
|
||||
AnimationStyle::EaseOutSine => EaseOutSine::evaluate(t),
|
||||
AnimationStyle::EaseInOutSine => EaseInOutSine::evaluate(t),
|
||||
AnimationStyle::EaseInQuad => EaseInQuad::evaluate(t),
|
||||
AnimationStyle::EaseOutQuad => EaseOutQuad::evaluate(t),
|
||||
AnimationStyle::EaseInOutQuad => EaseInOutQuad::evaluate(t),
|
||||
AnimationStyle::EaseInCubic => EaseInCubic::evaluate(t),
|
||||
AnimationStyle::EaseInOutCubic => EaseInOutCubic::evaluate(t),
|
||||
AnimationStyle::EaseInQuart => EaseInQuart::evaluate(t),
|
||||
AnimationStyle::EaseOutQuart => EaseOutQuart::evaluate(t),
|
||||
AnimationStyle::EaseInOutQuart => EaseInOutQuart::evaluate(t),
|
||||
AnimationStyle::EaseInQuint => EaseInQuint::evaluate(t),
|
||||
AnimationStyle::EaseOutQuint => EaseOutQuint::evaluate(t),
|
||||
AnimationStyle::EaseInOutQuint => EaseInOutQuint::evaluate(t),
|
||||
AnimationStyle::EaseInExpo => EaseInExpo::evaluate(t),
|
||||
AnimationStyle::EaseOutExpo => EaseOutExpo::evaluate(t),
|
||||
AnimationStyle::EaseInOutExpo => EaseInOutExpo::evaluate(t),
|
||||
AnimationStyle::EaseInCirc => EaseInCirc::evaluate(t),
|
||||
AnimationStyle::EaseOutCirc => EaseOutCirc::evaluate(t),
|
||||
AnimationStyle::EaseInOutCirc => EaseInOutCirc::evaluate(t),
|
||||
AnimationStyle::EaseInBack => EaseInBack::evaluate(t),
|
||||
AnimationStyle::EaseOutBack => EaseOutBack::evaluate(t),
|
||||
AnimationStyle::EaseInOutBack => EaseInOutBack::evaluate(t),
|
||||
AnimationStyle::EaseInElastic => EaseInElastic::evaluate(t),
|
||||
AnimationStyle::EaseOutElastic => EaseOutElastic::evaluate(t),
|
||||
AnimationStyle::EaseInOutElastic => EaseInOutElastic::evaluate(t),
|
||||
AnimationStyle::EaseInBounce => EaseInBounce::evaluate(t),
|
||||
AnimationStyle::EaseOutBounce => EaseOutBounce::evaluate(t),
|
||||
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 }
|
||||
}
|
||||
pub fn cancel(&mut self) {
|
||||
if !ANIMATION_MANAGER.lock().in_progress(self.hwnd) {
|
||||
return;
|
||||
}
|
||||
|
||||
ANIMATION_MANAGER.lock().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,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
#[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) {
|
||||
self.cancel();
|
||||
}
|
||||
|
||||
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
|
||||
// set all flags
|
||||
ANIMATION_MANAGER.lock().end(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
|
||||
if frame_start.elapsed() < target_frame_time {
|
||||
std::thread::sleep(target_frame_time - frame_start.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)
|
||||
}
|
||||
}
|
||||
79
komorebi/src/animation_manager.rs
Normal file
79
komorebi/src/animation_manager.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
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,
|
||||
pub is_cancelled: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AnimationManager {
|
||||
animations: HashMap<isize, AnimationState>,
|
||||
}
|
||||
|
||||
impl Default for AnimationManager {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl AnimationManager {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
animations: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_cancelled(&self, hwnd: isize) -> bool {
|
||||
if let Some(animation_state) = self.animations.get(&hwnd) {
|
||||
animation_state.is_cancelled
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn in_progress(&self, hwnd: isize) -> bool {
|
||||
if let Some(animation_state) = self.animations.get(&hwnd) {
|
||||
animation_state.in_progress
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cancel(&mut self, hwnd: isize) {
|
||||
if let Some(animation_state) = self.animations.get_mut(&hwnd) {
|
||||
animation_state.is_cancelled = true;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(&mut self, hwnd: isize) {
|
||||
if let Entry::Vacant(e) = self.animations.entry(hwnd) {
|
||||
e.insert(AnimationState {
|
||||
in_progress: true,
|
||||
is_cancelled: false,
|
||||
});
|
||||
|
||||
ANIMATIONS_IN_PROGRESS.store(self.animations.len(), Ordering::Release);
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(animation_state) = self.animations.get_mut(&hwnd) {
|
||||
animation_state.in_progress = true;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn end(&mut self, hwnd: isize) {
|
||||
if let Some(animation_state) = self.animations.get_mut(&hwnd) {
|
||||
animation_state.in_progress = false;
|
||||
animation_state.is_cancelled = false;
|
||||
|
||||
self.animations.remove(&hwnd);
|
||||
ANIMATIONS_IN_PROGRESS.store(self.animations.len(), Ordering::Release);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
#![warn(clippy::all)]
|
||||
|
||||
pub mod animation;
|
||||
pub mod animation_manager;
|
||||
pub mod border_manager;
|
||||
pub mod com;
|
||||
#[macro_use]
|
||||
@@ -38,9 +40,12 @@ use std::process::Command;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::atomic::AtomicI32;
|
||||
use std::sync::atomic::AtomicU32;
|
||||
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 process_command::*;
|
||||
pub use process_event::*;
|
||||
@@ -55,6 +60,7 @@ use color_eyre::Result;
|
||||
use komorebi_core::config_generation::IdWithIdentifier;
|
||||
use komorebi_core::config_generation::MatchingRule;
|
||||
use komorebi_core::config_generation::MatchingStrategy;
|
||||
use komorebi_core::AnimationStyle;
|
||||
use komorebi_core::ApplicationIdentifier;
|
||||
use komorebi_core::HidingBehaviour;
|
||||
use komorebi_core::Rect;
|
||||
@@ -197,6 +203,12 @@ 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![]));
|
||||
@@ -214,6 +226,8 @@ 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);
|
||||
|
||||
#[must_use]
|
||||
pub fn current_virtual_desktop() -> Option<Vec<u8>> {
|
||||
|
||||
@@ -58,6 +58,10 @@ use crate::windows_api::WindowsApi;
|
||||
use crate::GlobalState;
|
||||
use crate::Notification;
|
||||
use crate::NotificationEvent;
|
||||
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;
|
||||
@@ -561,6 +565,7 @@ impl WindowManager {
|
||||
self.update_focused_workspace(self.mouse_follows_focus, true)?;
|
||||
}
|
||||
SocketMessage::Retile => {
|
||||
border_manager::BORDER_TEMPORARILY_DISABLED.store(false, Ordering::SeqCst);
|
||||
border_manager::destroy_all_borders()?;
|
||||
self.retile_all(false)?
|
||||
}
|
||||
@@ -1322,6 +1327,18 @@ impl WindowManager {
|
||||
SocketMessage::BorderOffset(offset) => {
|
||||
border_manager::BORDER_OFFSET.store(offset, Ordering::SeqCst);
|
||||
}
|
||||
SocketMessage::Animation(enable) => {
|
||||
ANIMATION_ENABLED.store(enable, Ordering::SeqCst);
|
||||
}
|
||||
SocketMessage::AnimationDuration(duration) => {
|
||||
ANIMATION_DURATION.store(duration, Ordering::SeqCst);
|
||||
}
|
||||
SocketMessage::AnimationFps(fps) => {
|
||||
ANIMATION_FPS.store(fps, Ordering::SeqCst);
|
||||
}
|
||||
SocketMessage::AnimationStyle(style) => {
|
||||
*ANIMATION_STYLE.lock() = style;
|
||||
}
|
||||
SocketMessage::Transparency(enable) => {
|
||||
transparency_manager::TRANSPARENCY_ENABLED.store(enable, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
@@ -606,7 +606,7 @@ impl WindowManager {
|
||||
};
|
||||
|
||||
// If we unmanaged a window, it shouldn't be immediately hidden behind managed windows
|
||||
if let WindowManagerEvent::Unmanage(window) = event {
|
||||
if let WindowManagerEvent::Unmanage(mut window) = event {
|
||||
window.center(&self.focused_monitor_work_area()?)?;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,10 @@ use lazy_static::lazy_static;
|
||||
use parking_lot::Mutex;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::atomic::AtomicI32;
|
||||
use std::sync::atomic::AtomicU32;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
use std::sync::OnceLock;
|
||||
use windows::Win32::Foundation::HWND;
|
||||
@@ -30,6 +32,8 @@ pub static STACKBAR_TAB_WIDTH: AtomicI32 = AtomicI32::new(200);
|
||||
pub static STACKBAR_LABEL: AtomicCell<StackbarLabel> = AtomicCell::new(StackbarLabel::Process);
|
||||
pub static STACKBAR_MODE: AtomicCell<StackbarMode> = AtomicCell::new(StackbarMode::OnStack);
|
||||
|
||||
pub static STACKBAR_TEMPORARILY_DISABLED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
lazy_static! {
|
||||
pub static ref STACKBAR_STATE: Mutex<HashMap<String, Stackbar>> = Mutex::new(HashMap::new());
|
||||
pub static ref STACKBAR_FONT_FAMILY: Mutex<Option<String>> = Mutex::new(None);
|
||||
@@ -93,7 +97,9 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
||||
let mut state = wm.lock();
|
||||
|
||||
// If stackbars are disabled
|
||||
if matches!(STACKBAR_MODE.load(), StackbarMode::Never) {
|
||||
if matches!(STACKBAR_MODE.load(), StackbarMode::Never)
|
||||
|| STACKBAR_TEMPORARILY_DISABLED.load(Ordering::SeqCst)
|
||||
{
|
||||
for (_, stackbar) in stackbars.iter() {
|
||||
stackbar.destroy()?;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,10 @@ use crate::window_manager::WindowManager;
|
||||
use crate::window_manager_event::WindowManagerEvent;
|
||||
use crate::windows_api::WindowsApi;
|
||||
use crate::workspace::Workspace;
|
||||
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;
|
||||
@@ -52,6 +56,7 @@ use komorebi_core::config_generation::IdWithIdentifier;
|
||||
use komorebi_core::config_generation::MatchingRule;
|
||||
use komorebi_core::config_generation::MatchingStrategy;
|
||||
use komorebi_core::resolve_home_path;
|
||||
use komorebi_core::AnimationStyle;
|
||||
use komorebi_core::ApplicationIdentifier;
|
||||
use komorebi_core::BorderStyle;
|
||||
use komorebi_core::DefaultLayout;
|
||||
@@ -346,6 +351,21 @@ pub struct StaticConfig {
|
||||
/// Stackbar configuration options
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub stackbar: Option<StackbarConfig>,
|
||||
/// Animations configuration options
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub animation: Option<AnimationsConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct AnimationsConfig {
|
||||
/// Enable or disable animations (default: false)
|
||||
enabled: bool,
|
||||
/// Set the animation duration in ms (default: 250)
|
||||
duration: Option<u64>,
|
||||
/// Set the animation style (default: Linear)
|
||||
style: Option<AnimationStyle>,
|
||||
/// Set the animation FPS (default: 60)
|
||||
fps: Option<u64>,
|
||||
}
|
||||
|
||||
impl StaticConfig {
|
||||
@@ -584,6 +604,7 @@ impl From<&WindowManager> for StaticConfig {
|
||||
monitor_index_preferences: Option::from(MONITOR_INDEX_PREFERENCES.lock().clone()),
|
||||
display_index_preferences: Option::from(DISPLAY_INDEX_PREFERENCES.lock().clone()),
|
||||
stackbar: None,
|
||||
animation: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -614,6 +635,14 @@ impl StaticConfig {
|
||||
window::MINIMUM_WIDTH.store(width, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
if let Some(animations) = &self.animation {
|
||||
ANIMATION_ENABLED.store(animations.enabled, Ordering::SeqCst);
|
||||
ANIMATION_DURATION.store(animations.duration.unwrap_or(250), Ordering::SeqCst);
|
||||
ANIMATION_FPS.store(animations.fps.unwrap_or(60), Ordering::SeqCst);
|
||||
let mut animation_style = ANIMATION_STYLE.lock();
|
||||
*animation_style = animations.style.unwrap_or(AnimationStyle::Linear);
|
||||
}
|
||||
|
||||
if let Some(container) = self.default_container_padding {
|
||||
DEFAULT_CONTAINER_PADDING.store(container, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
use crate::border_manager;
|
||||
use crate::com::SetCloak;
|
||||
use crate::stackbar_manager;
|
||||
use crate::ANIMATIONS_IN_PROGRESS;
|
||||
use crate::ANIMATION_DURATION;
|
||||
use crate::ANIMATION_ENABLED;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt::Display;
|
||||
@@ -26,6 +31,7 @@ use komorebi_core::ApplicationIdentifier;
|
||||
use komorebi_core::HidingBehaviour;
|
||||
use komorebi_core::Rect;
|
||||
|
||||
use crate::animation::Animation;
|
||||
use crate::styles::ExtendedWindowStyle;
|
||||
use crate::styles::WindowStyle;
|
||||
use crate::transparency_manager;
|
||||
@@ -47,17 +53,24 @@ pub static MINIMUM_HEIGHT: AtomicI32 = AtomicI32::new(0);
|
||||
#[derive(Debug, Default, Clone, Copy, Deserialize, JsonSchema, PartialEq)]
|
||||
pub struct Window {
|
||||
pub hwnd: isize,
|
||||
animation: Animation,
|
||||
}
|
||||
|
||||
impl From<isize> for Window {
|
||||
fn from(value: isize) -> Self {
|
||||
Self { hwnd: value }
|
||||
Self {
|
||||
hwnd: value,
|
||||
animation: Animation::new(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HWND> for Window {
|
||||
fn from(value: HWND) -> Self {
|
||||
Self { hwnd: value.0 }
|
||||
Self {
|
||||
hwnd: value.0,
|
||||
animation: Animation::new(value.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +154,7 @@ impl Window {
|
||||
HWND(self.hwnd)
|
||||
}
|
||||
|
||||
pub fn center(&self, work_area: &Rect) -> Result<()> {
|
||||
pub fn center(&mut self, work_area: &Rect) -> Result<()> {
|
||||
let half_width = work_area.right / 2;
|
||||
let half_weight = work_area.bottom / 2;
|
||||
|
||||
@@ -156,13 +169,61 @@ impl Window {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn animate_position(&self, layout: &Rect, top: bool) -> Result<()> {
|
||||
let hwnd = self.hwnd();
|
||||
let curr_rect = WindowsApi::window_rect(hwnd).unwrap();
|
||||
|
||||
let target_rect = *layout;
|
||||
let duration = Duration::from_millis(ANIMATION_DURATION.load(Ordering::SeqCst));
|
||||
let mut animation = self.animation;
|
||||
|
||||
border_manager::BORDER_TEMPORARILY_DISABLED.store(true, Ordering::SeqCst);
|
||||
border_manager::send_notification();
|
||||
|
||||
stackbar_manager::STACKBAR_TEMPORARILY_DISABLED.store(true, Ordering::SeqCst);
|
||||
stackbar_manager::send_notification();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
animation.animate(duration, |progress: f64| {
|
||||
let new_rect = Animation::lerp_rect(&curr_rect, &target_rect, progress);
|
||||
|
||||
if progress == 1.0 {
|
||||
WindowsApi::position_window(hwnd, &new_rect, top)?;
|
||||
|
||||
if ANIMATIONS_IN_PROGRESS.load(Ordering::Acquire) == 0 {
|
||||
border_manager::BORDER_TEMPORARILY_DISABLED.store(false, Ordering::SeqCst);
|
||||
stackbar_manager::STACKBAR_TEMPORARILY_DISABLED
|
||||
.store(false, Ordering::SeqCst);
|
||||
|
||||
border_manager::send_notification();
|
||||
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::position_window(hwnd, &new_rect, top)?;
|
||||
WindowsApi::invalidate_rect(hwnd, None, false);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_position(&self, layout: &Rect, top: bool) -> Result<()> {
|
||||
if WindowsApi::window_rect(self.hwnd())?.eq(layout) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let rect = *layout;
|
||||
WindowsApi::position_window(self.hwnd(), &rect, top)
|
||||
if ANIMATION_ENABLED.load(Ordering::SeqCst) {
|
||||
self.animate_position(layout, top)
|
||||
} else {
|
||||
WindowsApi::position_window(self.hwnd(), layout, top)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_maximized(self) -> bool {
|
||||
|
||||
@@ -18,6 +18,7 @@ use windows::Win32::Foundation::HMODULE;
|
||||
use windows::Win32::Foundation::HWND;
|
||||
use windows::Win32::Foundation::LPARAM;
|
||||
use windows::Win32::Foundation::POINT;
|
||||
use windows::Win32::Foundation::RECT;
|
||||
use windows::Win32::Foundation::WPARAM;
|
||||
use windows::Win32::Graphics::Dwm::DwmGetWindowAttribute;
|
||||
use windows::Win32::Graphics::Dwm::DwmSetWindowAttribute;
|
||||
@@ -34,6 +35,7 @@ use windows::Win32::Graphics::Dwm::DWM_CLOAKED_SHELL;
|
||||
use windows::Win32::Graphics::Gdi::CreateSolidBrush;
|
||||
use windows::Win32::Graphics::Gdi::EnumDisplayMonitors;
|
||||
use windows::Win32::Graphics::Gdi::GetMonitorInfoW;
|
||||
use windows::Win32::Graphics::Gdi::InvalidateRect;
|
||||
use windows::Win32::Graphics::Gdi::MonitorFromPoint;
|
||||
use windows::Win32::Graphics::Gdi::MonitorFromWindow;
|
||||
use windows::Win32::Graphics::Gdi::Rectangle;
|
||||
@@ -84,6 +86,7 @@ use windows::Win32::UI::WindowsAndMessaging::IsIconic;
|
||||
use windows::Win32::UI::WindowsAndMessaging::IsWindow;
|
||||
use windows::Win32::UI::WindowsAndMessaging::IsWindowVisible;
|
||||
use windows::Win32::UI::WindowsAndMessaging::IsZoomed;
|
||||
use windows::Win32::UI::WindowsAndMessaging::MoveWindow;
|
||||
use windows::Win32::UI::WindowsAndMessaging::PostMessageW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::RealGetWindowClassW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::RegisterClassW;
|
||||
@@ -429,6 +432,17 @@ impl WindowsApi {
|
||||
.process()
|
||||
}
|
||||
|
||||
pub fn move_window(hwnd: HWND, layout: &Rect, repaint: bool) -> Result<()> {
|
||||
let shadow_rect = Self::shadow_rect(hwnd).unwrap_or_default();
|
||||
let rect = Rect {
|
||||
left: layout.left + shadow_rect.left,
|
||||
top: layout.top + shadow_rect.top,
|
||||
right: layout.right + shadow_rect.right,
|
||||
bottom: layout.bottom + shadow_rect.bottom,
|
||||
};
|
||||
unsafe { MoveWindow(hwnd, rect.left, rect.top, rect.right, rect.bottom, repaint) }.process()
|
||||
}
|
||||
|
||||
pub fn show_window(hwnd: HWND, command: SHOW_WINDOW_CMD) {
|
||||
// BOOL is returned but does not signify whether or not the operation was succesful
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow
|
||||
@@ -1023,6 +1037,11 @@ impl WindowsApi {
|
||||
.process()
|
||||
}
|
||||
|
||||
pub fn invalidate_rect(hwnd: HWND, rect: Option<&Rect>, erase: bool) -> bool {
|
||||
let rect = rect.map(|rect| &rect.rect() as *const RECT);
|
||||
unsafe { InvalidateRect(hwnd, rect, erase) }.as_bool()
|
||||
}
|
||||
|
||||
pub fn alt_is_pressed() -> bool {
|
||||
let state = unsafe { GetKeyState(i32::from(VK_MENU.0)) };
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
|
||||
@@ -714,6 +714,31 @@ struct BorderImplementation {
|
||||
style: komorebi_core::BorderImplementation,
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
struct Animation {
|
||||
#[clap(value_enum)]
|
||||
boolean_state: BooleanState,
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
struct AnimationDuration {
|
||||
/// Desired animation durations in ms
|
||||
duration: u64,
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
struct AnimationFps {
|
||||
/// Desired animation frames per second
|
||||
fps: u64,
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
struct AnimationStyle {
|
||||
/// Desired ease function for animation
|
||||
#[clap(value_enum, short, long, default_value = "linear")]
|
||||
style: komorebi_core::AnimationStyle,
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
struct Start {
|
||||
@@ -1225,6 +1250,18 @@ enum SubCommand {
|
||||
/// Set the alpha value for unfocused window transparency
|
||||
#[clap(arg_required_else_help = true)]
|
||||
TransparencyAlpha(TransparencyAlpha),
|
||||
/// Enable or disable the window move animation
|
||||
#[clap(arg_required_else_help = true)]
|
||||
Animation(Animation),
|
||||
/// Set the duration for the window move animation in ms
|
||||
#[clap(arg_required_else_help = true)]
|
||||
AnimationDuration(AnimationDuration),
|
||||
/// Set the frames per second for the window move animation
|
||||
#[clap(arg_required_else_help = true)]
|
||||
AnimationFps(AnimationFps),
|
||||
/// Set the ease function for the window move animation
|
||||
#[clap(arg_required_else_help = true)]
|
||||
AnimationStyle(AnimationStyle),
|
||||
/// Enable or disable focus follows mouse for the operating system
|
||||
#[clap(arg_required_else_help = true)]
|
||||
FocusFollowsMouse(FocusFollowsMouse),
|
||||
@@ -2335,6 +2372,19 @@ Stop-Process -Name:komorebi -ErrorAction SilentlyContinue
|
||||
SubCommand::TransparencyAlpha(arg) => {
|
||||
send_message(&SocketMessage::TransparencyAlpha(arg.alpha).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::Animation(arg) => {
|
||||
send_message(&SocketMessage::Animation(arg.boolean_state.into()).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::AnimationDuration(arg) => {
|
||||
send_message(&SocketMessage::AnimationDuration(arg.duration).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::AnimationFps(arg) => {
|
||||
send_message(&SocketMessage::AnimationFps(arg.fps).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::AnimationStyle(arg) => {
|
||||
send_message(&SocketMessage::AnimationStyle(arg.style).as_bytes()?)?;
|
||||
}
|
||||
|
||||
SubCommand::ResizeDelta(arg) => {
|
||||
send_message(&SocketMessage::ResizeDelta(arg.pixels).as_bytes()?)?;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user