From 369107f5e067e5adf6d82b91551e7384f4f66dcd Mon Sep 17 00:00:00 2001 From: thearturca Date: Sun, 10 Nov 2024 15:24:28 +0300 Subject: [PATCH] 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. --- komorebi-client/src/lib.rs | 1 + komorebi/src/animation/engine.rs | 4 +- komorebi/src/animation/mod.rs | 41 ++++++++++++--- komorebi/src/animation/prefix.rs | 15 +++++- komorebi/src/core/mod.rs | 7 +-- komorebi/src/main.rs | 6 ++- komorebi/src/process_command.rs | 53 ++++++++++++++----- komorebi/src/static_config.rs | 59 +++++++++++++++++---- komorebi/src/window.rs | 89 +++++++++++++++++++++++++------- komorebic/src/main.rs | 24 +++++++-- 10 files changed, 237 insertions(+), 62 deletions(-) diff --git a/komorebi-client/src/lib.rs b/komorebi-client/src/lib.rs index 8b64952c..13afc3cd 100644 --- a/komorebi-client/src/lib.rs +++ b/komorebi-client/src/lib.rs @@ -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; diff --git a/komorebi/src/animation/engine.rs b/komorebi/src/animation/engine.rs index 0104c891..ab12663c 100644 --- a/komorebi/src/animation/engine.rs +++ b/komorebi/src/animation/engine.rs @@ -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), )); } } diff --git a/komorebi/src/animation/mod.rs b/komorebi/src/animation/mod.rs index 3fe6f0c2..47bf763c 100644 --- a/komorebi/src/animation/mod.rs +++ b/komorebi/src/animation/mod.rs @@ -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> = - Arc::new(Mutex::new(AnimationStyle::Linear)); - pub static ref ANIMATION_MANAGER: Arc> = - Arc::new(Mutex::new(AnimationManager::new())); +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +#[serde(untagged)] +pub enum PerAnimationPrefixConfig { + Prefix(HashMap), + 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> = + Arc::new(Mutex::new(AnimationManager::new())); + pub static ref ANIMATION_STYLE_GLOBAL: Arc> = + Arc::new(Mutex::new(DEFAULT_ANIMATION_STYLE)); + pub static ref ANIMATION_ENABLED_GLOBAL: Arc = + Arc::new(AtomicBool::new(DEFAULT_ANIMATION_ENABLED)); + pub static ref ANIMATION_DURATION_GLOBAL: Arc = + Arc::new(AtomicU64::new(DEFAULT_ANIMATION_DURATION)); + pub static ref ANIMATION_STYLE_PER_ANIMATION: Arc>> = + Arc::new(Mutex::new(HashMap::new())); + pub static ref ANIMATION_ENABLED_PER_ANIMATION: Arc>> = + Arc::new(Mutex::new(HashMap::new())); + pub static ref ANIMATION_DURATION_PER_ANIMATION: Arc>> = + Arc::new(Mutex::new(HashMap::new())); +} + +pub static ANIMATION_FPS: AtomicU64 = AtomicU64::new(DEFAULT_ANIMATION_FPS); diff --git a/komorebi/src/animation/prefix.rs b/komorebi/src/animation/prefix.rs index 86eadfba..8512dcdc 100644 --- a/komorebi/src/animation/prefix.rs +++ b/komorebi/src/animation/prefix.rs @@ -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, diff --git a/komorebi/src/core/mod.rs b/komorebi/src/core/mod.rs index 3f2a2399..104c6c59 100644 --- a/komorebi/src/core/mod.rs +++ b/komorebi/src/core/mod.rs @@ -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), + AnimationDuration(u64, Option), AnimationFps(u64), - AnimationStyle(AnimationStyle), + AnimationStyle(AnimationStyle, Option), #[serde(alias = "ActiveWindowBorder")] Border(bool), #[serde(alias = "ActiveWindowBorderColour")] diff --git a/komorebi/src/main.rs b/komorebi/src/main.rs index 1507c683..4f601975 100644 --- a/komorebi/src/main.rs +++ b/komorebi/src/main.rs @@ -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(); diff --git a/komorebi/src/process_command.rs b/komorebi/src/process_command.rs index 1a302feb..12e869ad 100644 --- a/komorebi/src/process_command.rs +++ b/komorebi/src/process_command.rs @@ -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); diff --git a/komorebi/src/static_config.rs b/komorebi/src/static_config.rs index 978c40b8..40a2dcae 100644 --- a/komorebi/src/static_config.rs +++ b/komorebi/src/static_config.rs @@ -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, /// Set the animation duration in ms (default: 250) - duration: Option, + duration: Option>, /// Set the animation style (default: Linear) - style: Option, + style: Option>, /// Set the animation FPS (default: 60) fps: Option, } @@ -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 { diff --git a/komorebi/src/window.rs b/komorebi/src/window.rs index ccf80763..48270209 100644 --- a/komorebi/src/window.rs +++ b/komorebi/src/window.rs @@ -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, diff --git a/komorebic/src/main.rs b/komorebic/src/main.rs index 7c60031e..032f8e4b 100644 --- a/komorebic/src/main.rs +++ b/komorebic/src/main.rs @@ -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, } #[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, } #[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, } #[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) => {