mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-07-18 19:11:11 +02:00
Feature: Animation. Need fixes
At current state animation is working. Configs are available for it.
animation enable/disable
animation-duration {duration in ms}
animation-ease -e {ease func. See --help}
Two major problems for my implementation:
1. Google Chrome window is broken and I don't know how to implement fix
for it.
2. Border window does not update properly with window. Dont know how to
fix it.
This commit is contained in:
@@ -0,0 +1,43 @@
|
|||||||
|
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,
|
||||||
|
)]
|
||||||
|
#[strum(serialize_all = "snake_case")]
|
||||||
|
pub enum EaseEnum {
|
||||||
|
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::Display;
|
||||||
use strum::EnumString;
|
use strum::EnumString;
|
||||||
|
|
||||||
|
pub use animation::EaseEnum;
|
||||||
pub use arrangement::Arrangement;
|
pub use arrangement::Arrangement;
|
||||||
pub use arrangement::Axis;
|
pub use arrangement::Axis;
|
||||||
pub use custom_layout::CustomLayout;
|
pub use custom_layout::CustomLayout;
|
||||||
@@ -24,6 +25,7 @@ pub use layout::Layout;
|
|||||||
pub use operation_direction::OperationDirection;
|
pub use operation_direction::OperationDirection;
|
||||||
pub use rect::Rect;
|
pub use rect::Rect;
|
||||||
|
|
||||||
|
pub mod animation;
|
||||||
pub mod arrangement;
|
pub mod arrangement;
|
||||||
pub mod config_generation;
|
pub mod config_generation;
|
||||||
pub mod custom_layout;
|
pub mod custom_layout;
|
||||||
@@ -127,6 +129,9 @@ pub enum SocketMessage {
|
|||||||
WatchConfiguration(bool),
|
WatchConfiguration(bool),
|
||||||
CompleteConfiguration,
|
CompleteConfiguration,
|
||||||
AltFocusHack(bool),
|
AltFocusHack(bool),
|
||||||
|
Animate(bool),
|
||||||
|
AnimateDuration(u64),
|
||||||
|
AnimateEase(EaseEnum),
|
||||||
ActiveWindowBorder(bool),
|
ActiveWindowBorder(bool),
|
||||||
ActiveWindowBorderColour(WindowKind, u32, u32, u32),
|
ActiveWindowBorderColour(WindowKind, u32, u32, u32),
|
||||||
ActiveWindowBorderWidth(i32),
|
ActiveWindowBorderWidth(i32),
|
||||||
|
|||||||
+485
-30
@@ -1,55 +1,510 @@
|
|||||||
use color_eyre::Result;
|
use color_eyre::Result;
|
||||||
|
use komorebi_core::EaseEnum;
|
||||||
use komorebi_core::Rect;
|
use komorebi_core::Rect;
|
||||||
use std::thread::sleep;
|
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
|
||||||
|
use std::f64::consts::PI;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
pub struct Animation;
|
use crate::ANIMATE_EASE;
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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 {
|
||||||
|
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 || 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 || 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 || 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 ease = *ANIMATE_EASE.lock();
|
||||||
|
|
||||||
|
match ease {
|
||||||
|
EaseEnum::Linear => Linear::evaluate(t),
|
||||||
|
EaseEnum::EaseInSine => EaseInSine::evaluate(t),
|
||||||
|
EaseEnum::EaseOutSine => EaseOutSine::evaluate(t),
|
||||||
|
EaseEnum::EaseInOutSine => EaseInOutSine::evaluate(t),
|
||||||
|
EaseEnum::EaseInQuad => EaseInQuad::evaluate(t),
|
||||||
|
EaseEnum::EaseOutQuad => EaseOutQuad::evaluate(t),
|
||||||
|
EaseEnum::EaseInOutQuad => EaseInOutQuad::evaluate(t),
|
||||||
|
EaseEnum::EaseInCubic => EaseInCubic::evaluate(t),
|
||||||
|
EaseEnum::EaseInOutCubic => EaseInOutCubic::evaluate(t),
|
||||||
|
EaseEnum::EaseInQuart => EaseInQuart::evaluate(t),
|
||||||
|
EaseEnum::EaseOutQuart => EaseOutQuart::evaluate(t),
|
||||||
|
EaseEnum::EaseInOutQuart => EaseInOutQuart::evaluate(t),
|
||||||
|
EaseEnum::EaseInQuint => EaseInQuint::evaluate(t),
|
||||||
|
EaseEnum::EaseOutQuint => EaseOutQuint::evaluate(t),
|
||||||
|
EaseEnum::EaseInOutQuint => EaseInOutQuint::evaluate(t),
|
||||||
|
EaseEnum::EaseInExpo => EaseInExpo::evaluate(t),
|
||||||
|
EaseEnum::EaseOutExpo => EaseOutExpo::evaluate(t),
|
||||||
|
EaseEnum::EaseInOutExpo => EaseInOutExpo::evaluate(t),
|
||||||
|
EaseEnum::EaseInCirc => EaseInCirc::evaluate(t),
|
||||||
|
EaseEnum::EaseOutCirc => EaseOutCirc::evaluate(t),
|
||||||
|
EaseEnum::EaseInOutCirc => EaseInOutCirc::evaluate(t),
|
||||||
|
EaseEnum::EaseInBack => EaseInBack::evaluate(t),
|
||||||
|
EaseEnum::EaseOutBack => EaseOutBack::evaluate(t),
|
||||||
|
EaseEnum::EaseInOutBack => EaseInOutBack::evaluate(t),
|
||||||
|
EaseEnum::EaseInElastic => EaseInElastic::evaluate(t),
|
||||||
|
EaseEnum::EaseOutElastic => EaseOutElastic::evaluate(t),
|
||||||
|
EaseEnum::EaseInOutElastic => EaseInOutElastic::evaluate(t),
|
||||||
|
EaseEnum::EaseInBounce => EaseInBounce::evaluate(t),
|
||||||
|
EaseEnum::EaseOutBounce => EaseOutBounce::evaluate(t),
|
||||||
|
EaseEnum::EaseInOutBounce => EaseInOutBounce::evaluate(t),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, 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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Animation {
|
impl Animation {
|
||||||
|
pub fn cancel(&mut self) -> Result<()> {
|
||||||
|
if !self.in_progress {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.is_cancel = true;
|
||||||
|
let max_duration = Duration::from_secs(1);
|
||||||
|
let spent_duration = Instant::now();
|
||||||
|
|
||||||
|
while self.in_progress {
|
||||||
|
if spent_duration.elapsed() >= max_duration {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::thread::sleep(Duration::from_millis(16));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn lerp(x: i32, new_x: i32, t: f64) -> i32 {
|
pub fn lerp(x: i32, new_x: i32, t: f64) -> i32 {
|
||||||
(x as f64 + (new_x - x) as f64 * t) as i32
|
let time = apply_ease_func(t);
|
||||||
|
f64::from(new_x - x).mul_add(time, f64::from(x)) as i32
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lerp_rect(original_rect: &Rect, new_rect: &Rect, t: f64) -> Rect {
|
pub fn lerp_rect(original_rect: &Rect, new_rect: &Rect, t: f64) -> Rect {
|
||||||
let is_half_way = t > 0.5;
|
Rect {
|
||||||
let mut rect = Rect::default();
|
left: Self::lerp(original_rect.left, new_rect.left, t),
|
||||||
rect.top = Animation::lerp(original_rect.top, new_rect.top, t);
|
top: Self::lerp(original_rect.top, new_rect.top, t),
|
||||||
rect.left = Animation::lerp(original_rect.left, new_rect.left, t);
|
right: Self::lerp(original_rect.right, new_rect.right, t),
|
||||||
rect.bottom = if is_half_way {
|
bottom: Self::lerp(original_rect.bottom, new_rect.bottom, t),
|
||||||
new_rect.bottom
|
}
|
||||||
} else {
|
|
||||||
original_rect.bottom
|
|
||||||
};
|
|
||||||
rect.right = if is_half_way {
|
|
||||||
new_rect.right
|
|
||||||
} else {
|
|
||||||
original_rect.right
|
|
||||||
};
|
|
||||||
|
|
||||||
rect
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn animate(duration: Duration, mut f: impl FnMut(f64) -> Result<()>) -> bool {
|
pub fn animate(
|
||||||
|
&mut self,
|
||||||
|
duration: Duration,
|
||||||
|
mut f: impl FnMut(f64) -> Result<()>,
|
||||||
|
) -> Result<()> {
|
||||||
|
self.in_progress = true;
|
||||||
|
// 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
|
||||||
|
// or make it configurable
|
||||||
let target_frame_time = Duration::from_millis(1000 / 240);
|
let target_frame_time = Duration::from_millis(1000 / 240);
|
||||||
let mut progress = 0.0;
|
let mut progress = 0.0;
|
||||||
let &animation_start = &Instant::now();
|
let animation_start = Instant::now();
|
||||||
|
|
||||||
|
// start animation
|
||||||
while progress < 1.0 {
|
while progress < 1.0 {
|
||||||
let tick_start = Instant::now();
|
// check if animation is cancelled
|
||||||
f(progress).unwrap();
|
if self.is_cancel {
|
||||||
progress = animation_start.elapsed().as_millis() as f64 / duration.as_millis() as f64;
|
// cancel animation
|
||||||
|
// set all flags
|
||||||
if progress > 1.0 {
|
self.is_cancel = !self.is_cancel;
|
||||||
progress = 1.0;
|
self.in_progress = false;
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let tick_start = Instant::now();
|
||||||
|
// calculate progress
|
||||||
|
progress = animation_start.elapsed().as_millis() as f64 / duration.as_millis() as f64;
|
||||||
|
f(progress).ok();
|
||||||
|
|
||||||
|
// sleep until next frame
|
||||||
while tick_start.elapsed() < target_frame_time {
|
while tick_start.elapsed() < target_frame_time {
|
||||||
sleep(target_frame_time - tick_start.elapsed());
|
std::thread::sleep(target_frame_time - tick_start.elapsed());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
f(progress).unwrap();
|
self.in_progress = false;
|
||||||
true
|
|
||||||
|
// 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
|
||||||
|
f(progress)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use std::sync::atomic::AtomicBool;
|
|||||||
use std::sync::atomic::AtomicI32;
|
use std::sync::atomic::AtomicI32;
|
||||||
use std::sync::atomic::AtomicIsize;
|
use std::sync::atomic::AtomicIsize;
|
||||||
use std::sync::atomic::AtomicU32;
|
use std::sync::atomic::AtomicU32;
|
||||||
|
use std::sync::atomic::AtomicU64;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
#[cfg(feature = "deadlock_detection")]
|
#[cfg(feature = "deadlock_detection")]
|
||||||
@@ -26,6 +27,7 @@ use color_eyre::Result;
|
|||||||
use crossbeam_channel::Receiver;
|
use crossbeam_channel::Receiver;
|
||||||
use crossbeam_channel::Sender;
|
use crossbeam_channel::Sender;
|
||||||
use crossbeam_utils::Backoff;
|
use crossbeam_utils::Backoff;
|
||||||
|
use komorebi_core::EaseEnum;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use os_info::Version;
|
use os_info::Version;
|
||||||
#[cfg(feature = "deadlock_detection")]
|
#[cfg(feature = "deadlock_detection")]
|
||||||
@@ -215,6 +217,8 @@ lazy_static! {
|
|||||||
static ref BORDER_OFFSET: Arc<Mutex<Option<Rect>>> =
|
static ref BORDER_OFFSET: Arc<Mutex<Option<Rect>>> =
|
||||||
Arc::new(Mutex::new(None));
|
Arc::new(Mutex::new(None));
|
||||||
|
|
||||||
|
static ref ANIMATE_EASE: Arc<Mutex<EaseEnum>> = Arc::new(Mutex::new(EaseEnum::EaseInOutExpo));
|
||||||
|
|
||||||
// Use app-specific titlebar removal options where possible
|
// Use app-specific titlebar removal options where possible
|
||||||
// eg. Windows Terminal, IntelliJ IDEA, Firefox
|
// eg. Windows Terminal, IntelliJ IDEA, Firefox
|
||||||
static ref NO_TITLEBAR: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![]));
|
static ref NO_TITLEBAR: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![]));
|
||||||
@@ -238,6 +242,8 @@ pub static BORDER_WIDTH: AtomicI32 = AtomicI32::new(20);
|
|||||||
// 0 0 0 aka pure black, I doubt anyone will want this as a border colour
|
// 0 0 0 aka pure black, I doubt anyone will want this as a border colour
|
||||||
pub const TRANSPARENCY_COLOUR: u32 = 0;
|
pub const TRANSPARENCY_COLOUR: u32 = 0;
|
||||||
pub static REMOVE_TITLEBARS: AtomicBool = AtomicBool::new(false);
|
pub static REMOVE_TITLEBARS: AtomicBool = AtomicBool::new(false);
|
||||||
|
pub static ANIMATE_ENABLED: AtomicBool = AtomicBool::new(true);
|
||||||
|
pub static ANIMATE_DURATION: AtomicU64 = AtomicU64::new(250);
|
||||||
|
|
||||||
pub static HIDDEN_HWND: AtomicIsize = AtomicIsize::new(0);
|
pub static HIDDEN_HWND: AtomicIsize = AtomicIsize::new(0);
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ use crate::windows_api::WindowsApi;
|
|||||||
use crate::Notification;
|
use crate::Notification;
|
||||||
use crate::NotificationEvent;
|
use crate::NotificationEvent;
|
||||||
use crate::ALT_FOCUS_HACK;
|
use crate::ALT_FOCUS_HACK;
|
||||||
|
use crate::ANIMATE_DURATION;
|
||||||
|
use crate::ANIMATE_EASE;
|
||||||
|
use crate::ANIMATE_ENABLED;
|
||||||
use crate::BORDER_COLOUR_CURRENT;
|
use crate::BORDER_COLOUR_CURRENT;
|
||||||
use crate::BORDER_COLOUR_MONOCLE;
|
use crate::BORDER_COLOUR_MONOCLE;
|
||||||
use crate::BORDER_COLOUR_SINGLE;
|
use crate::BORDER_COLOUR_SINGLE;
|
||||||
@@ -1140,6 +1143,15 @@ impl WindowManager {
|
|||||||
self.hide_border()?;
|
self.hide_border()?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SocketMessage::Animate(enable) => {
|
||||||
|
ANIMATE_ENABLED.store(enable, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
SocketMessage::AnimateDuration(duration) => {
|
||||||
|
ANIMATE_DURATION.store(duration, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
SocketMessage::AnimateEase(ease) => {
|
||||||
|
*ANIMATE_EASE.lock() = ease;
|
||||||
|
}
|
||||||
SocketMessage::ActiveWindowBorderColour(kind, r, g, b) => {
|
SocketMessage::ActiveWindowBorderColour(kind, r, g, b) => {
|
||||||
match kind {
|
match kind {
|
||||||
WindowKind::Single => {
|
WindowKind::Single => {
|
||||||
@@ -1295,7 +1307,7 @@ impl WindowManager {
|
|||||||
| SocketMessage::CycleMoveWindow(_)
|
| SocketMessage::CycleMoveWindow(_)
|
||||||
| SocketMessage::MoveWindow(_) => {
|
| SocketMessage::MoveWindow(_) => {
|
||||||
let foreground = WindowsApi::foreground_window()?;
|
let foreground = WindowsApi::foreground_window()?;
|
||||||
let foreground_window = Window { hwnd: foreground };
|
let foreground_window = Window::new(foreground);
|
||||||
let mut rect = WindowsApi::window_rect(foreground_window.hwnd())?;
|
let mut rect = WindowsApi::window_rect(foreground_window.hwnd())?;
|
||||||
rect.top -= self.invisible_borders.bottom;
|
rect.top -= self.invisible_borders.bottom;
|
||||||
rect.bottom += self.invisible_borders.bottom;
|
rect.bottom += self.invisible_borders.bottom;
|
||||||
|
|||||||
+49
-20
@@ -1,11 +1,12 @@
|
|||||||
use crate::com::SetCloak;
|
use crate::com::SetCloak;
|
||||||
|
use crate::ANIMATE_DURATION;
|
||||||
|
use crate::ANIMATE_ENABLED;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
use std::fmt::Write as _;
|
use std::fmt::Write as _;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use std::thread;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use color_eyre::eyre::anyhow;
|
use color_eyre::eyre::anyhow;
|
||||||
@@ -47,6 +48,7 @@ use crate::WSL2_UI_PROCESSES;
|
|||||||
#[derive(Debug, Clone, Copy, JsonSchema)]
|
#[derive(Debug, Clone, Copy, JsonSchema)]
|
||||||
pub struct Window {
|
pub struct Window {
|
||||||
pub(crate) hwnd: isize,
|
pub(crate) hwnd: isize,
|
||||||
|
animation: Animation,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Window {
|
impl Display for Window {
|
||||||
@@ -106,6 +108,14 @@ impl Serialize for Window {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
|
// for instantiation of animation struct
|
||||||
|
pub fn new(hwnd: isize) -> Self {
|
||||||
|
Window {
|
||||||
|
hwnd,
|
||||||
|
animation: Animation::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub const fn hwnd(self) -> HWND {
|
pub const fn hwnd(self) -> HWND {
|
||||||
HWND(self.hwnd)
|
HWND(self.hwnd)
|
||||||
}
|
}
|
||||||
@@ -125,21 +135,37 @@ impl Window {
|
|||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
pub fn animate_position(hwnd: HWND, layout: &Rect, top: bool) -> Result<()> {
|
pub fn animate_position(&mut self, layout: &Rect, top: bool) -> Result<()> {
|
||||||
let duration = Duration::from_millis(200);
|
let hwnd = self.hwnd();
|
||||||
let curr_rect = WindowsApi::window_rect(hwnd).unwrap();
|
let curr_rect = WindowsApi::window_rect(hwnd).unwrap();
|
||||||
|
|
||||||
if assert_eq!(curr_rect, *layout) {
|
if curr_rect.left == layout.left
|
||||||
WindowsApi::position_window(hwnd, layout, top);
|
&& curr_rect.top == layout.top
|
||||||
|
&& curr_rect.bottom == layout.bottom
|
||||||
|
&& curr_rect.right == layout.right
|
||||||
|
{
|
||||||
|
WindowsApi::position_window(hwnd, layout, top)
|
||||||
|
} else {
|
||||||
|
let target_rect = *layout;
|
||||||
|
let duration = Duration::from_millis(ANIMATE_DURATION.load(Ordering::SeqCst));
|
||||||
|
let mut animation = self.animation;
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
animation
|
||||||
|
.animate(duration, |progress: f64| {
|
||||||
|
let new_rect = Animation::lerp_rect(&curr_rect, &target_rect, progress);
|
||||||
|
if progress < 1.0 {
|
||||||
|
// using MoveWindow because it runs faster than SetWindowPos
|
||||||
|
// so animation have more fps and feel smoother
|
||||||
|
WindowsApi::move_window(hwnd, &new_rect, true)
|
||||||
|
} else {
|
||||||
|
WindowsApi::position_window(hwnd, &new_rect, top)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
let animate_window = |progress: f64| {
|
|
||||||
let new_rect = Animation::lerp_rect(&curr_rect, layout, progress);
|
|
||||||
WindowsApi::position_window(hwnd, &new_rect, top);
|
|
||||||
};
|
|
||||||
|
|
||||||
Animation::animate(duration, animate_window);
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_position(
|
pub fn set_position(
|
||||||
@@ -173,14 +199,17 @@ impl Window {
|
|||||||
rect.bottom += invisible_borders.bottom;
|
rect.bottom += invisible_borders.bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
let hwnd = self.hwnd();
|
if ANIMATE_ENABLED.load(Ordering::SeqCst) {
|
||||||
|
// check if animation is in progress
|
||||||
|
if self.animation.in_progress {
|
||||||
|
// wait for cancle animation
|
||||||
|
self.animation.cancel().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
thread::spawn(move || {
|
self.animate_position(&rect, top)
|
||||||
Window::animate_position(hwnd, &rect, top).unwrap();
|
} else {
|
||||||
});
|
WindowsApi::position_window(self.hwnd(), &rect, top)
|
||||||
Ok(())
|
}
|
||||||
|
|
||||||
// WindowsApi::position_window(self.hwnd(), &rect, top)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hide(self) {
|
pub fn hide(self) {
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ impl WindowManager {
|
|||||||
#[tracing::instrument(skip(self))]
|
#[tracing::instrument(skip(self))]
|
||||||
pub fn show_border(&self) -> Result<()> {
|
pub fn show_border(&self) -> Result<()> {
|
||||||
let foreground = WindowsApi::foreground_window()?;
|
let foreground = WindowsApi::foreground_window()?;
|
||||||
let foreground_window = Window { hwnd: foreground };
|
let foreground_window = Window::new(foreground);
|
||||||
let mut rect = WindowsApi::window_rect(foreground_window.hwnd())?;
|
let mut rect = WindowsApi::window_rect(foreground_window.hwnd())?;
|
||||||
rect.top -= self.invisible_borders.bottom;
|
rect.top -= self.invisible_borders.bottom;
|
||||||
rect.bottom += self.invisible_borders.bottom;
|
rect.bottom += self.invisible_borders.bottom;
|
||||||
@@ -589,7 +589,7 @@ impl WindowManager {
|
|||||||
|
|
||||||
// Hide the window we are about to remove if it is on the currently focused workspace
|
// Hide the window we are about to remove if it is on the currently focused workspace
|
||||||
if op.is_origin(focused_monitor_idx, focused_workspace_idx) {
|
if op.is_origin(focused_monitor_idx, focused_workspace_idx) {
|
||||||
Window { hwnd: op.hwnd }.hide();
|
Window::new(op.hwnd).hide();
|
||||||
should_update_focused_workspace = true;
|
should_update_focused_workspace = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -619,7 +619,7 @@ impl WindowManager {
|
|||||||
.get_mut(op.target_workspace_idx)
|
.get_mut(op.target_workspace_idx)
|
||||||
.ok_or_else(|| anyhow!("there is no workspace with that index"))?;
|
.ok_or_else(|| anyhow!("there is no workspace with that index"))?;
|
||||||
|
|
||||||
target_workspace.new_container_for_window(Window { hwnd: op.hwnd });
|
target_workspace.new_container_for_window(Window::new(op.hwnd));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only re-tile the focused workspace if we need to
|
// Only re-tile the focused workspace if we need to
|
||||||
@@ -663,14 +663,14 @@ impl WindowManager {
|
|||||||
#[tracing::instrument(skip(self))]
|
#[tracing::instrument(skip(self))]
|
||||||
pub fn manage_focused_window(&mut self) -> Result<()> {
|
pub fn manage_focused_window(&mut self) -> Result<()> {
|
||||||
let hwnd = WindowsApi::foreground_window()?;
|
let hwnd = WindowsApi::foreground_window()?;
|
||||||
let event = WindowManagerEvent::Manage(Window { hwnd });
|
let event = WindowManagerEvent::Manage(Window::new(hwnd));
|
||||||
Ok(WINEVENT_CALLBACK_CHANNEL.lock().0.send(event)?)
|
Ok(WINEVENT_CALLBACK_CHANNEL.lock().0.send(event)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(self))]
|
#[tracing::instrument(skip(self))]
|
||||||
pub fn unmanage_focused_window(&mut self) -> Result<()> {
|
pub fn unmanage_focused_window(&mut self) -> Result<()> {
|
||||||
let hwnd = WindowsApi::foreground_window()?;
|
let hwnd = WindowsApi::foreground_window()?;
|
||||||
let event = WindowManagerEvent::Unmanage(Window { hwnd });
|
let event = WindowManagerEvent::Unmanage(Window::new(hwnd));
|
||||||
Ok(WINEVENT_CALLBACK_CHANNEL.lock().0.send(event)?)
|
Ok(WINEVENT_CALLBACK_CHANNEL.lock().0.send(event)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -708,13 +708,14 @@ impl WindowManager {
|
|||||||
];
|
];
|
||||||
|
|
||||||
if !known_hwnd {
|
if !known_hwnd {
|
||||||
let class = Window { hwnd }.class()?;
|
let class = Window::new(hwnd).class()?;
|
||||||
// Some applications (Electron/Chromium-based, explorer) have (invisible?) overlays
|
// Some applications (Electron/Chromium-based, explorer) have (invisible?) overlays
|
||||||
// windows that we need to look beyond to find the actual window to raise
|
// windows that we need to look beyond to find the actual window to raise
|
||||||
if overlay_classes.contains(&class) {
|
if overlay_classes.contains(&class) {
|
||||||
for monitor in self.monitors() {
|
for monitor in self.monitors() {
|
||||||
for workspace in monitor.workspaces() {
|
for workspace in monitor.workspaces() {
|
||||||
if let Some(exe_hwnd) = workspace.hwnd_from_exe(&Window { hwnd }.exe()?)
|
if let Some(exe_hwnd) =
|
||||||
|
workspace.hwnd_from_exe(&Window::new(hwnd).exe()?)
|
||||||
{
|
{
|
||||||
hwnd = exe_hwnd;
|
hwnd = exe_hwnd;
|
||||||
known_hwnd = true;
|
known_hwnd = true;
|
||||||
@@ -725,11 +726,11 @@ impl WindowManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if known_hwnd {
|
if known_hwnd {
|
||||||
let event = WindowManagerEvent::Raise(Window { hwnd });
|
let event = WindowManagerEvent::Raise(Window::new(hwnd));
|
||||||
self.has_pending_raise_op = true;
|
self.has_pending_raise_op = true;
|
||||||
Ok(WINEVENT_CALLBACK_CHANNEL.lock().0.send(event)?)
|
Ok(WINEVENT_CALLBACK_CHANNEL.lock().0.send(event)?)
|
||||||
} else {
|
} else {
|
||||||
tracing::debug!("not raising unknown window: {}", Window { hwnd });
|
tracing::debug!("not raising unknown window: {}", Window::new(hwnd,));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -843,9 +844,7 @@ impl WindowManager {
|
|||||||
} else if let Ok(window) = self.focused_window_mut() {
|
} else if let Ok(window) = self.focused_window_mut() {
|
||||||
window.focus(self.mouse_follows_focus)?;
|
window.focus(self.mouse_follows_focus)?;
|
||||||
} else {
|
} else {
|
||||||
let desktop_window = Window {
|
let desktop_window = Window::new(WindowsApi::desktop_window()?);
|
||||||
hwnd: WindowsApi::desktop_window()?,
|
|
||||||
};
|
|
||||||
|
|
||||||
let rect = self.focused_monitor_size()?;
|
let rect = self.focused_monitor_size()?;
|
||||||
WindowsApi::center_cursor_in_rect(&rect)?;
|
WindowsApi::center_cursor_in_rect(&rect)?;
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ use windows::Win32::UI::WindowsAndMessaging::GetWindowThreadProcessId;
|
|||||||
use windows::Win32::UI::WindowsAndMessaging::IsIconic;
|
use windows::Win32::UI::WindowsAndMessaging::IsIconic;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::IsWindow;
|
use windows::Win32::UI::WindowsAndMessaging::IsWindow;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::IsWindowVisible;
|
use windows::Win32::UI::WindowsAndMessaging::IsWindowVisible;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::MoveWindow;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::PostMessageW;
|
use windows::Win32::UI::WindowsAndMessaging::PostMessageW;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::RealGetWindowClassW;
|
use windows::Win32::UI::WindowsAndMessaging::RealGetWindowClassW;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::RegisterClassA;
|
use windows::Win32::UI::WindowsAndMessaging::RegisterClassA;
|
||||||
@@ -354,6 +355,20 @@ impl WindowsApi {
|
|||||||
.process()
|
.process()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn move_window(hwnd: HWND, layout: &Rect, repaint: bool) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
MoveWindow(
|
||||||
|
hwnd,
|
||||||
|
layout.left,
|
||||||
|
layout.top,
|
||||||
|
layout.right,
|
||||||
|
layout.bottom,
|
||||||
|
repaint,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.process()
|
||||||
|
}
|
||||||
|
|
||||||
fn show_window(hwnd: HWND, command: SHOW_WINDOW_CMD) {
|
fn show_window(hwnd: HWND, command: SHOW_WINDOW_CMD) {
|
||||||
// BOOL is returned but does not signify whether or not the operation was succesful
|
// 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
|
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ pub extern "system" fn enum_window(hwnd: HWND, lparam: LPARAM) -> BOOL {
|
|||||||
let is_minimized = WindowsApi::is_iconic(hwnd);
|
let is_minimized = WindowsApi::is_iconic(hwnd);
|
||||||
|
|
||||||
if is_visible && is_window && !is_minimized {
|
if is_visible && is_window && !is_minimized {
|
||||||
let window = Window { hwnd: hwnd.0 };
|
let window = Window::new(hwnd.0);
|
||||||
|
|
||||||
if let Ok(should_manage) = window.should_manage(None) {
|
if let Ok(should_manage) = window.should_manage(None) {
|
||||||
if should_manage {
|
if should_manage {
|
||||||
@@ -132,7 +132,7 @@ pub extern "system" fn win_event_hook(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let window = Window { hwnd: hwnd.0 };
|
let window = Window::new(hwnd.0);
|
||||||
|
|
||||||
let winevent = unsafe { ::std::mem::transmute(event) };
|
let winevent = unsafe { ::std::mem::transmute(event) };
|
||||||
let event_type = match WindowManagerEvent::from_win_event(winevent, window) {
|
let event_type = match WindowManagerEvent::from_win_event(winevent, window) {
|
||||||
@@ -196,7 +196,7 @@ pub extern "system" fn hidden_window(
|
|||||||
unsafe {
|
unsafe {
|
||||||
match message {
|
match message {
|
||||||
WM_DISPLAYCHANGE => {
|
WM_DISPLAYCHANGE => {
|
||||||
let event_type = WindowManagerEvent::DisplayChange(Window { hwnd: window.0 });
|
let event_type = WindowManagerEvent::DisplayChange(Window::new(window.0));
|
||||||
WINEVENT_CALLBACK_CHANNEL
|
WINEVENT_CALLBACK_CHANNEL
|
||||||
.lock()
|
.lock()
|
||||||
.0
|
.0
|
||||||
@@ -211,7 +211,7 @@ pub extern "system" fn hidden_window(
|
|||||||
if wparam.0 as u32 == SPI_SETWORKAREA.0
|
if wparam.0 as u32 == SPI_SETWORKAREA.0
|
||||||
|| wparam.0 as u32 == SPI_ICONVERTICALSPACING.0
|
|| wparam.0 as u32 == SPI_ICONVERTICALSPACING.0
|
||||||
{
|
{
|
||||||
let event_type = WindowManagerEvent::DisplayChange(Window { hwnd: window.0 });
|
let event_type = WindowManagerEvent::DisplayChange(Window::new(window.0));
|
||||||
WINEVENT_CALLBACK_CHANNEL
|
WINEVENT_CALLBACK_CHANNEL
|
||||||
.lock()
|
.lock()
|
||||||
.0
|
.0
|
||||||
@@ -224,7 +224,7 @@ pub extern "system" fn hidden_window(
|
|||||||
WM_DEVICECHANGE => {
|
WM_DEVICECHANGE => {
|
||||||
#[allow(clippy::cast_possible_truncation)]
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
if wparam.0 as u32 == DBT_DEVNODES_CHANGED {
|
if wparam.0 as u32 == DBT_DEVNODES_CHANGED {
|
||||||
let event_type = WindowManagerEvent::DisplayChange(Window { hwnd: window.0 });
|
let event_type = WindowManagerEvent::DisplayChange(Window::new(window.0));
|
||||||
WINEVENT_CALLBACK_CHANNEL
|
WINEVENT_CALLBACK_CHANNEL
|
||||||
.lock()
|
.lock()
|
||||||
.0
|
.0
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ use color_eyre::Result;
|
|||||||
use fs_tail::TailedFile;
|
use fs_tail::TailedFile;
|
||||||
use heck::ToKebabCase;
|
use heck::ToKebabCase;
|
||||||
use komorebi_core::resolve_home_path;
|
use komorebi_core::resolve_home_path;
|
||||||
|
use komorebi_core::EaseEnum;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use paste::paste;
|
use paste::paste;
|
||||||
use sysinfo::SystemExt;
|
use sysinfo::SystemExt;
|
||||||
@@ -627,6 +628,25 @@ struct ActiveWindowBorderOffset {
|
|||||||
offset: i32,
|
offset: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Parser, AhkFunction)]
|
||||||
|
struct Animate {
|
||||||
|
#[clap(value_enum)]
|
||||||
|
boolean_state: BooleanState,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Parser, AhkFunction)]
|
||||||
|
struct AnimateDuration {
|
||||||
|
/// Desired animation durations in ms
|
||||||
|
duration: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Parser, AhkFunction)]
|
||||||
|
struct AnimateEase {
|
||||||
|
/// Desired ease function for animation
|
||||||
|
#[clap(value_enum, short, long, default_value = "linear")]
|
||||||
|
ease_func: EaseEnum,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Parser, AhkFunction)]
|
#[derive(Parser, AhkFunction)]
|
||||||
#[allow(clippy::struct_excessive_bools)]
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
struct Start {
|
struct Start {
|
||||||
@@ -1059,6 +1079,15 @@ enum SubCommand {
|
|||||||
/// Set the offset for the active window border
|
/// Set the offset for the active window border
|
||||||
#[clap(arg_required_else_help = true)]
|
#[clap(arg_required_else_help = true)]
|
||||||
ActiveWindowBorderOffset(ActiveWindowBorderOffset),
|
ActiveWindowBorderOffset(ActiveWindowBorderOffset),
|
||||||
|
/// Enable or disable the window move animation
|
||||||
|
#[clap(arg_required_else_help = true)]
|
||||||
|
Animate(Animate),
|
||||||
|
/// Set the duration for the window move animation in ms
|
||||||
|
#[clap(arg_required_else_help = true)]
|
||||||
|
AnimateDuration(AnimateDuration),
|
||||||
|
/// Set the ease function for the window move animation
|
||||||
|
#[clap(arg_required_else_help = true)]
|
||||||
|
AnimateEase(AnimateEase),
|
||||||
/// Enable or disable focus follows mouse for the operating system
|
/// Enable or disable focus follows mouse for the operating system
|
||||||
#[clap(arg_required_else_help = true)]
|
#[clap(arg_required_else_help = true)]
|
||||||
FocusFollowsMouse(FocusFollowsMouse),
|
FocusFollowsMouse(FocusFollowsMouse),
|
||||||
@@ -1986,6 +2015,15 @@ Stop-Process -Name:whkd -ErrorAction SilentlyContinue
|
|||||||
SubCommand::ActiveWindowBorderOffset(arg) => {
|
SubCommand::ActiveWindowBorderOffset(arg) => {
|
||||||
send_message(&SocketMessage::ActiveWindowBorderOffset(arg.offset).as_bytes()?)?;
|
send_message(&SocketMessage::ActiveWindowBorderOffset(arg.offset).as_bytes()?)?;
|
||||||
}
|
}
|
||||||
|
SubCommand::Animate(arg) => {
|
||||||
|
send_message(&SocketMessage::Animate(arg.boolean_state.into()).as_bytes()?)?;
|
||||||
|
}
|
||||||
|
SubCommand::AnimateDuration(arg) => {
|
||||||
|
send_message(&SocketMessage::AnimateDuration(arg.duration).as_bytes()?)?;
|
||||||
|
}
|
||||||
|
SubCommand::AnimateEase(arg) => {
|
||||||
|
send_message(&SocketMessage::AnimateEase(arg.ease_func).as_bytes()?)?;
|
||||||
|
}
|
||||||
SubCommand::ResizeDelta(arg) => {
|
SubCommand::ResizeDelta(arg) => {
|
||||||
send_message(&SocketMessage::ResizeDelta(arg.pixels).as_bytes()?)?;
|
send_message(&SocketMessage::ResizeDelta(arg.pixels).as_bytes()?)?;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user