feat(config): adds per animation configuration options

Originally static config only allows global config for animations.
Since this refactor introduces abilty to add more type of animations,
this change allows to configure `enabled`, `duration` and `style` state
per animation type.
Now each of them take either raw value or JSON object where keys
are the animation types and values are desired config value.
Also adds support for per animation configuration for komorebic commands.
This commit is contained in:
thearturca
2024-11-10 15:24:28 +03:00
parent e502cb3ffb
commit 369107f5e0
10 changed files with 237 additions and 62 deletions

View File

@@ -1,6 +1,7 @@
#![warn(clippy::all)]
#![allow(clippy::missing_errors_doc)]
pub use komorebi::animation::prefix::AnimationPrefix;
pub use komorebi::asc::ApplicationSpecificConfiguration;
pub use komorebi::colour::Colour;
pub use komorebi::colour::Rgb;

View File

@@ -9,7 +9,7 @@ use std::time::Duration;
use std::time::Instant;
use super::RenderDispatcher;
use super::ANIMATION_DURATION;
use super::ANIMATION_DURATION_GLOBAL;
use super::ANIMATION_FPS;
use super::ANIMATION_MANAGER;
@@ -27,7 +27,7 @@ impl AnimationEngine {
}
std::thread::sleep(Duration::from_millis(
ANIMATION_DURATION.load(Ordering::SeqCst),
ANIMATION_DURATION_GLOBAL.load(Ordering::SeqCst),
));
}
}

View File

@@ -2,6 +2,8 @@ use crate::animation::animation_manager::AnimationManager;
use crate::core::animation::AnimationStyle;
use lazy_static::lazy_static;
use prefix::AnimationPrefix;
use std::collections::HashMap;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::AtomicU64;
use std::sync::Arc;
@@ -16,14 +18,37 @@ pub mod prefix;
pub mod render_dispatcher;
pub use render_dispatcher::RenderDispatcher;
pub mod style;
use schemars::JsonSchema;
use serde::Deserialize;
use serde::Serialize;
lazy_static! {
pub static ref ANIMATION_STYLE: Arc<Mutex<AnimationStyle>> =
Arc::new(Mutex::new(AnimationStyle::Linear));
pub static ref ANIMATION_MANAGER: Arc<Mutex<AnimationManager>> =
Arc::new(Mutex::new(AnimationManager::new()));
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(untagged)]
pub enum PerAnimationPrefixConfig<T> {
Prefix(HashMap<AnimationPrefix, T>),
Global(T),
}
pub static ANIMATION_ENABLED: AtomicBool = AtomicBool::new(false);
pub static ANIMATION_DURATION: AtomicU64 = AtomicU64::new(250);
pub static ANIMATION_FPS: AtomicU64 = AtomicU64::new(60);
pub const DEFAULT_ANIMATION_ENABLED: bool = false;
pub const DEFAULT_ANIMATION_STYLE: AnimationStyle = AnimationStyle::Linear;
pub const DEFAULT_ANIMATION_DURATION: u64 = 250;
pub const DEFAULT_ANIMATION_FPS: u64 = 60;
lazy_static! {
pub static ref ANIMATION_MANAGER: Arc<Mutex<AnimationManager>> =
Arc::new(Mutex::new(AnimationManager::new()));
pub static ref ANIMATION_STYLE_GLOBAL: Arc<Mutex<AnimationStyle>> =
Arc::new(Mutex::new(DEFAULT_ANIMATION_STYLE));
pub static ref ANIMATION_ENABLED_GLOBAL: Arc<AtomicBool> =
Arc::new(AtomicBool::new(DEFAULT_ANIMATION_ENABLED));
pub static ref ANIMATION_DURATION_GLOBAL: Arc<AtomicU64> =
Arc::new(AtomicU64::new(DEFAULT_ANIMATION_DURATION));
pub static ref ANIMATION_STYLE_PER_ANIMATION: Arc<Mutex<HashMap<AnimationPrefix, AnimationStyle>>> =
Arc::new(Mutex::new(HashMap::new()));
pub static ref ANIMATION_ENABLED_PER_ANIMATION: Arc<Mutex<HashMap<AnimationPrefix, bool>>> =
Arc::new(Mutex::new(HashMap::new()));
pub static ref ANIMATION_DURATION_PER_ANIMATION: Arc<Mutex<HashMap<AnimationPrefix, u64>>> =
Arc::new(Mutex::new(HashMap::new()));
}
pub static ANIMATION_FPS: AtomicU64 = AtomicU64::new(DEFAULT_ANIMATION_FPS);

View File

@@ -6,8 +6,21 @@ use strum::Display;
use strum::EnumString;
#[derive(
Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, JsonSchema,
Copy,
Clone,
Debug,
Hash,
PartialEq,
Eq,
Serialize,
Deserialize,
Display,
EnumString,
ValueEnum,
JsonSchema,
)]
#[strum(serialize_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum AnimationPrefix {
WindowMove,
WindowTransparency,

View File

@@ -14,6 +14,7 @@ use serde::Serialize;
use strum::Display;
use strum::EnumString;
use crate::animation::prefix::AnimationPrefix;
use crate::KomorebiTheme;
pub use animation::AnimationStyle;
pub use arrangement::Arrangement;
@@ -146,10 +147,10 @@ pub enum SocketMessage {
CompleteConfiguration,
AltFocusHack(bool),
Theme(KomorebiTheme),
Animation(bool),
AnimationDuration(u64),
Animation(bool, Option<AnimationPrefix>),
AnimationDuration(u64, Option<AnimationPrefix>),
AnimationFps(u64),
AnimationStyle(AnimationStyle),
AnimationStyle(AnimationStyle, Option<AnimationPrefix>),
#[serde(alias = "ActiveWindowBorder")]
Border(bool),
#[serde(alias = "ActiveWindowBorderColour")]

View File

@@ -18,7 +18,8 @@ use clap::Parser;
use color_eyre::Result;
use crossbeam_utils::Backoff;
use komorebi::animation::AnimationEngine;
use komorebi::animation::ANIMATION_ENABLED;
use komorebi::animation::ANIMATION_ENABLED_GLOBAL;
use komorebi::animation::ANIMATION_ENABLED_PER_ANIMATION;
#[cfg(feature = "deadlock_detection")]
use parking_lot::deadlock;
use parking_lot::Mutex;
@@ -289,7 +290,8 @@ fn main() -> Result<()> {
tracing::error!("received ctrl-c, restoring all hidden windows and terminating process");
ANIMATION_ENABLED.store(false, Ordering::SeqCst);
ANIMATION_ENABLED_PER_ANIMATION.lock().clear();
ANIMATION_ENABLED_GLOBAL.store(false, Ordering::SeqCst);
wm.lock().restore_all_windows()?;
AnimationEngine::wait_for_all_animations();

View File

@@ -23,6 +23,9 @@ use schemars::schema_for;
use uds_windows::UnixStream;
use crate::animation::AnimationEngine;
use crate::animation::ANIMATION_DURATION_PER_ANIMATION;
use crate::animation::ANIMATION_ENABLED_PER_ANIMATION;
use crate::animation::ANIMATION_STYLE_PER_ANIMATION;
use crate::core::config_generation::ApplicationConfiguration;
use crate::core::config_generation::IdWithIdentifier;
use crate::core::config_generation::MatchingRule;
@@ -41,10 +44,10 @@ use crate::core::StateQuery;
use crate::core::WindowContainerBehaviour;
use crate::core::WindowKind;
use crate::animation::ANIMATION_DURATION;
use crate::animation::ANIMATION_ENABLED;
use crate::animation::ANIMATION_DURATION_GLOBAL;
use crate::animation::ANIMATION_ENABLED_GLOBAL;
use crate::animation::ANIMATION_FPS;
use crate::animation::ANIMATION_STYLE;
use crate::animation::ANIMATION_STYLE_GLOBAL;
use crate::border_manager;
use crate::border_manager::IMPLEMENTATION;
use crate::border_manager::STYLE;
@@ -878,7 +881,8 @@ impl WindowManager {
"received stop command, restoring all hidden windows and terminating process"
);
ANIMATION_ENABLED.store(false, Ordering::SeqCst);
ANIMATION_ENABLED_PER_ANIMATION.lock().clear();
ANIMATION_ENABLED_GLOBAL.store(false, Ordering::SeqCst);
self.restore_all_windows()?;
AnimationEngine::wait_for_all_animations();
@@ -1523,18 +1527,41 @@ 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::Animation(enable, prefix) => match prefix {
Some(prefix) => {
ANIMATION_ENABLED_PER_ANIMATION
.lock()
.insert(prefix, enable);
}
None => {
ANIMATION_ENABLED_GLOBAL.store(enable, Ordering::SeqCst);
ANIMATION_ENABLED_PER_ANIMATION.lock().clear();
}
},
SocketMessage::AnimationDuration(duration, prefix) => match prefix {
Some(prefix) => {
ANIMATION_DURATION_PER_ANIMATION
.lock()
.insert(prefix, duration);
}
None => {
ANIMATION_DURATION_GLOBAL.store(duration, Ordering::SeqCst);
ANIMATION_DURATION_PER_ANIMATION.lock().clear();
}
},
SocketMessage::AnimationFps(fps) => {
ANIMATION_FPS.store(fps, Ordering::SeqCst);
}
SocketMessage::AnimationStyle(style) => {
*ANIMATION_STYLE.lock() = style;
}
SocketMessage::AnimationStyle(style, prefix) => match prefix {
Some(prefix) => {
ANIMATION_STYLE_PER_ANIMATION.lock().insert(prefix, style);
}
None => {
let mut animation_style = ANIMATION_STYLE_GLOBAL.lock();
*animation_style = style;
ANIMATION_STYLE_PER_ANIMATION.lock().clear();
}
},
SocketMessage::ToggleTransparency => {
let current = transparency_manager::TRANSPARENCY_ENABLED.load(Ordering::SeqCst);
transparency_manager::TRANSPARENCY_ENABLED.store(!current, Ordering::SeqCst);

View File

@@ -1,7 +1,12 @@
use crate::animation::ANIMATION_DURATION;
use crate::animation::ANIMATION_ENABLED;
use crate::animation::PerAnimationPrefixConfig;
use crate::animation::ANIMATION_DURATION_GLOBAL;
use crate::animation::ANIMATION_DURATION_PER_ANIMATION;
use crate::animation::ANIMATION_ENABLED_GLOBAL;
use crate::animation::ANIMATION_ENABLED_PER_ANIMATION;
use crate::animation::ANIMATION_FPS;
use crate::animation::ANIMATION_STYLE;
use crate::animation::ANIMATION_STYLE_GLOBAL;
use crate::animation::ANIMATION_STYLE_PER_ANIMATION;
use crate::animation::DEFAULT_ANIMATION_FPS;
use crate::border_manager;
use crate::border_manager::ZOrder;
use crate::border_manager::IMPLEMENTATION;
@@ -374,11 +379,11 @@ pub struct StaticConfig {
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct AnimationsConfig {
/// Enable or disable animations (default: false)
enabled: bool,
enabled: PerAnimationPrefixConfig<bool>,
/// Set the animation duration in ms (default: 250)
duration: Option<u64>,
duration: Option<PerAnimationPrefixConfig<u64>>,
/// Set the animation style (default: Linear)
style: Option<AnimationStyle>,
style: Option<PerAnimationPrefixConfig<AnimationStyle>>,
/// Set the animation FPS (default: 60)
fps: Option<u64>,
}
@@ -654,11 +659,43 @@ impl StaticConfig {
}
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);
match &animations.enabled {
PerAnimationPrefixConfig::Prefix(enabled) => {
ANIMATION_ENABLED_PER_ANIMATION.lock().clone_from(enabled);
}
PerAnimationPrefixConfig::Global(enabled) => {
ANIMATION_ENABLED_GLOBAL.store(*enabled, Ordering::SeqCst);
ANIMATION_ENABLED_PER_ANIMATION.lock().clear();
}
}
match &animations.style {
Some(PerAnimationPrefixConfig::Prefix(style)) => {
ANIMATION_STYLE_PER_ANIMATION.lock().clone_from(style);
}
Some(PerAnimationPrefixConfig::Global(style)) => {
let mut animation_style = ANIMATION_STYLE_GLOBAL.lock();
*animation_style = *style;
ANIMATION_STYLE_PER_ANIMATION.lock().clear();
}
None => {}
}
match &animations.duration {
Some(PerAnimationPrefixConfig::Prefix(duration)) => {
ANIMATION_DURATION_PER_ANIMATION.lock().clone_from(duration);
}
Some(PerAnimationPrefixConfig::Global(duration)) => {
ANIMATION_DURATION_GLOBAL.store(*duration, Ordering::SeqCst);
ANIMATION_DURATION_PER_ANIMATION.lock().clear();
}
None => {}
}
ANIMATION_FPS.store(
animations.fps.unwrap_or(DEFAULT_ANIMATION_FPS),
Ordering::SeqCst,
);
}
if let Some(container) = self.default_container_padding {

View File

@@ -3,10 +3,13 @@ use crate::animation::prefix::new_animation_key;
use crate::animation::prefix::AnimationPrefix;
use crate::animation::AnimationEngine;
use crate::animation::RenderDispatcher;
use crate::animation::ANIMATION_DURATION;
use crate::animation::ANIMATION_ENABLED;
use crate::animation::ANIMATION_DURATION_GLOBAL;
use crate::animation::ANIMATION_DURATION_PER_ANIMATION;
use crate::animation::ANIMATION_ENABLED_GLOBAL;
use crate::animation::ANIMATION_ENABLED_PER_ANIMATION;
use crate::animation::ANIMATION_MANAGER;
use crate::animation::ANIMATION_STYLE;
use crate::animation::ANIMATION_STYLE_GLOBAL;
use crate::animation::ANIMATION_STYLE_PER_ANIMATION;
use crate::border_manager;
use crate::com::SetCloak;
use crate::focus_manager;
@@ -156,7 +159,6 @@ impl Serialize for Window {
}
struct WindowMoveRenderDispatcher {
prefix: AnimationPrefix,
hwnd: isize,
start_rect: Rect,
target_rect: Rect,
@@ -165,6 +167,8 @@ struct WindowMoveRenderDispatcher {
}
impl WindowMoveRenderDispatcher {
const PREFIX: AnimationPrefix = AnimationPrefix::WindowMove;
pub fn new(
hwnd: isize,
start_rect: Rect,
@@ -173,7 +177,6 @@ impl WindowMoveRenderDispatcher {
style: AnimationStyle,
) -> Self {
Self {
prefix: AnimationPrefix::WindowMove,
hwnd,
start_rect,
target_rect,
@@ -185,7 +188,7 @@ impl WindowMoveRenderDispatcher {
impl RenderDispatcher for WindowMoveRenderDispatcher {
fn get_animation_key(&self) -> String {
new_animation_key(self.prefix, self.hwnd.to_string())
new_animation_key(WindowMoveRenderDispatcher::PREFIX, self.hwnd.to_string())
}
fn pre_render(&self) -> Result<()> {
@@ -211,7 +214,11 @@ impl RenderDispatcher for WindowMoveRenderDispatcher {
fn post_render(&self) -> Result<()> {
WindowsApi::position_window(self.hwnd, &self.target_rect, self.top)?;
if ANIMATION_MANAGER.lock().count_in_progress(self.prefix) == 0 {
if ANIMATION_MANAGER
.lock()
.count_in_progress(WindowMoveRenderDispatcher::PREFIX)
== 0
{
if WindowsApi::foreground_window().unwrap_or_default() == self.hwnd {
focus_manager::send_notification(self.hwnd)
}
@@ -229,7 +236,6 @@ impl RenderDispatcher for WindowMoveRenderDispatcher {
}
struct WindowTransparencyRenderDispatcher {
prefix: AnimationPrefix,
hwnd: isize,
start_opacity: u8,
target_opacity: u8,
@@ -238,6 +244,8 @@ struct WindowTransparencyRenderDispatcher {
}
impl WindowTransparencyRenderDispatcher {
const PREFIX: AnimationPrefix = AnimationPrefix::WindowTransparency;
pub fn new(
hwnd: isize,
is_opaque: bool,
@@ -246,7 +254,6 @@ impl WindowTransparencyRenderDispatcher {
style: AnimationStyle,
) -> Self {
Self {
prefix: AnimationPrefix::WindowTransparency,
hwnd,
start_opacity,
target_opacity,
@@ -258,7 +265,10 @@ impl WindowTransparencyRenderDispatcher {
impl RenderDispatcher for WindowTransparencyRenderDispatcher {
fn get_animation_key(&self) -> String {
new_animation_key(self.prefix, self.hwnd.to_string())
new_animation_key(
WindowTransparencyRenderDispatcher::PREFIX,
self.hwnd.to_string(),
)
}
fn pre_render(&self) -> Result<()> {
@@ -346,9 +356,22 @@ impl Window {
return Ok(());
}
if ANIMATION_ENABLED.load(Ordering::SeqCst) {
let duration = Duration::from_millis(ANIMATION_DURATION.load(Ordering::SeqCst));
let style = *ANIMATION_STYLE.lock();
let animation_enabled = ANIMATION_ENABLED_PER_ANIMATION.lock();
let move_enabled = animation_enabled.get(&WindowMoveRenderDispatcher::PREFIX);
if move_enabled.is_some_and(|enabled| *enabled)
|| ANIMATION_ENABLED_GLOBAL.load(Ordering::SeqCst)
{
let duration = Duration::from_millis(
*ANIMATION_DURATION_PER_ANIMATION
.lock()
.get(&WindowMoveRenderDispatcher::PREFIX)
.unwrap_or(&ANIMATION_DURATION_GLOBAL.load(Ordering::SeqCst)),
);
let style = *ANIMATION_STYLE_PER_ANIMATION
.lock()
.get(&WindowMoveRenderDispatcher::PREFIX)
.unwrap_or(&ANIMATION_STYLE_GLOBAL.lock());
let render_dispatcher =
WindowMoveRenderDispatcher::new(self.hwnd, window_rect, *layout, top, style);
@@ -462,9 +485,23 @@ impl Window {
}
pub fn transparent(self) -> Result<()> {
if ANIMATION_ENABLED.load(Ordering::SeqCst) {
let duration = Duration::from_millis(ANIMATION_DURATION.load(Ordering::SeqCst));
let style = *ANIMATION_STYLE.lock();
let animation_enabled = ANIMATION_ENABLED_PER_ANIMATION.lock();
let transparent_enabled =
animation_enabled.get(&WindowTransparencyRenderDispatcher::PREFIX);
if transparent_enabled.is_some_and(|enabled| *enabled)
|| ANIMATION_ENABLED_GLOBAL.load(Ordering::SeqCst)
{
let duration = Duration::from_millis(
*ANIMATION_DURATION_PER_ANIMATION
.lock()
.get(&WindowTransparencyRenderDispatcher::PREFIX)
.unwrap_or(&ANIMATION_DURATION_GLOBAL.load(Ordering::SeqCst)),
);
let style = *ANIMATION_STYLE_PER_ANIMATION
.lock()
.get(&WindowTransparencyRenderDispatcher::PREFIX)
.unwrap_or(&ANIMATION_STYLE_GLOBAL.lock());
let render_dispatcher = WindowTransparencyRenderDispatcher::new(
self.hwnd,
@@ -487,9 +524,23 @@ impl Window {
}
pub fn opaque(self) -> Result<()> {
if ANIMATION_ENABLED.load(Ordering::SeqCst) {
let duration = Duration::from_millis(ANIMATION_DURATION.load(Ordering::SeqCst));
let style = *ANIMATION_STYLE.lock();
let animation_enabled = ANIMATION_ENABLED_PER_ANIMATION.lock();
let transparent_enabled =
animation_enabled.get(&WindowTransparencyRenderDispatcher::PREFIX);
if transparent_enabled.is_some_and(|enabled| *enabled)
|| ANIMATION_ENABLED_GLOBAL.load(Ordering::SeqCst)
{
let duration = Duration::from_millis(
*ANIMATION_DURATION_PER_ANIMATION
.lock()
.get(&WindowTransparencyRenderDispatcher::PREFIX)
.unwrap_or(&ANIMATION_DURATION_GLOBAL.load(Ordering::SeqCst)),
);
let style = *ANIMATION_STYLE_PER_ANIMATION
.lock()
.get(&WindowTransparencyRenderDispatcher::PREFIX)
.unwrap_or(&ANIMATION_STYLE_GLOBAL.lock());
let render_dispatcher = WindowTransparencyRenderDispatcher::new(
self.hwnd,

View File

@@ -723,12 +723,18 @@ struct BorderImplementation {
struct Animation {
#[clap(value_enum)]
boolean_state: BooleanState,
/// Animation type to apply the state to. If not specified, sets global state
#[clap(value_enum, short, long)]
animation_type: Option<komorebi_client::AnimationPrefix>,
}
#[derive(Parser)]
struct AnimationDuration {
/// Desired animation durations in ms
duration: u64,
/// Animation type to apply the duration to. If not specified, sets global duration
#[clap(value_enum, short, long)]
animation_type: Option<komorebi_client::AnimationPrefix>,
}
#[derive(Parser)]
@@ -742,6 +748,9 @@ struct AnimationStyle {
/// Desired ease function for animation
#[clap(value_enum, short, long, default_value = "linear")]
style: komorebi_client::AnimationStyle,
/// Animation type to apply the style to. If not specified, sets global style
#[clap(value_enum, short, long)]
animation_type: Option<komorebi_client::AnimationPrefix>,
}
#[derive(Parser)]
@@ -2539,16 +2548,25 @@ Stop-Process -Name:komorebi -ErrorAction SilentlyContinue
send_message(&SocketMessage::ToggleTransparency)?;
}
SubCommand::Animation(arg) => {
send_message(&SocketMessage::Animation(arg.boolean_state.into()))?;
send_message(&SocketMessage::Animation(
arg.boolean_state.into(),
arg.animation_type,
))?;
}
SubCommand::AnimationDuration(arg) => {
send_message(&SocketMessage::AnimationDuration(arg.duration))?;
send_message(&SocketMessage::AnimationDuration(
arg.duration,
arg.animation_type,
))?;
}
SubCommand::AnimationFps(arg) => {
send_message(&SocketMessage::AnimationFps(arg.fps))?;
}
SubCommand::AnimationStyle(arg) => {
send_message(&SocketMessage::AnimationStyle(arg.style))?;
send_message(&SocketMessage::AnimationStyle(
arg.style,
arg.animation_type,
))?;
}
SubCommand::ResizeDelta(arg) => {