diff --git a/komorebi/src/core/mod.rs b/komorebi/src/core/mod.rs index 8611bde3..94b069db 100644 --- a/komorebi/src/core/mod.rs +++ b/komorebi/src/core/mod.rs @@ -529,6 +529,16 @@ impl Sizing { } } +#[derive( + Clone, Copy, Debug, Default, Serialize, Deserialize, Display, EnumString, ValueEnum, PartialEq, +)] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +pub enum WindowHandlingBehaviour { + #[default] + Sync, + Async, +} + #[cfg(test)] mod tests { use super::*; diff --git a/komorebi/src/lib.rs b/komorebi/src/lib.rs index f6460168..dcce4d90 100644 --- a/komorebi/src/lib.rs +++ b/komorebi/src/lib.rs @@ -63,6 +63,7 @@ use crate::core::config_generation::MatchingRule; use crate::core::config_generation::MatchingStrategy; use crate::core::config_generation::WorkspaceMatchingRule; use color_eyre::Result; +use crossbeam_utils::atomic::AtomicCell; use os_info::Version; use parking_lot::Mutex; use parking_lot::RwLock; @@ -240,7 +241,8 @@ pub static REMOVE_TITLEBARS: AtomicBool = AtomicBool::new(false); pub static SLOW_APPLICATION_COMPENSATION_TIME: AtomicU64 = AtomicU64::new(20); -pub static ASYNC_WINDOW_HANDLING_ENABLED: AtomicBool = AtomicBool::new(false); +pub static WINDOW_HANDLING_BEHAVIOUR: AtomicCell = + AtomicCell::new(WindowHandlingBehaviour::Sync); shadow_rs::shadow!(build); diff --git a/komorebi/src/static_config.rs b/komorebi/src/static_config.rs index 2ded4804..0fbd7235 100644 --- a/komorebi/src/static_config.rs +++ b/komorebi/src/static_config.rs @@ -63,7 +63,7 @@ use crate::FloatingLayerBehaviour; use crate::Placement; use crate::PredefinedAspectRatio; use crate::ResolvedPathBuf; -use crate::ASYNC_WINDOW_HANDLING_ENABLED; +use crate::WindowHandlingBehaviour; use crate::DATA_DIR; use crate::DEFAULT_CONTAINER_PADDING; use crate::DEFAULT_WORKSPACE_PADDING; @@ -84,6 +84,7 @@ use crate::SLOW_APPLICATION_IDENTIFIERS; use crate::TRANSPARENCY_BLACKLIST; use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS; use crate::WINDOWS_11; +use crate::WINDOW_HANDLING_BEHAVIOUR; use crate::WORKSPACE_MATCHING_RULES; use color_eyre::Result; use crossbeam_channel::Receiver; @@ -561,9 +562,9 @@ pub struct StaticConfig { /// Aspect ratio to resize with when toggling floating mode for a window #[serde(skip_serializing_if = "Option::is_none")] pub floating_window_aspect_ratio: Option, - /// Use asynchronous window handling to avoid blocking the main thread when a window is not responding (default: false) + /// Which Windows API behaviour to use when manipulating windows (default: Sync) #[serde(skip_serializing_if = "Option::is_none")] - pub async_window_handling: Option, + pub window_handling_behaviour: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] @@ -925,7 +926,7 @@ impl From<&WindowManager> for StaticConfig { bar_configurations: None, remove_titlebar_applications: Option::from(NO_TITLEBAR.lock().clone()), floating_window_aspect_ratio: Option::from(*FLOATING_WINDOW_TOGGLE_ASPECT_RATIO.lock()), - async_window_handling: Option::from(false), + window_handling_behaviour: Option::from(WINDOW_HANDLING_BEHAVIOUR.load()), } } } @@ -1213,8 +1214,8 @@ impl StaticConfig { } } - if let Some(async_enabled) = self.async_window_handling { - ASYNC_WINDOW_HANDLING_ENABLED.store(async_enabled, Ordering::SeqCst); + if let Some(behaviour) = self.window_handling_behaviour { + WINDOW_HANDLING_BEHAVIOUR.store(behaviour); } Ok(()) diff --git a/komorebi/src/window.rs b/komorebi/src/window.rs index f4b162fd..d64c9c27 100644 --- a/komorebi/src/window.rs +++ b/komorebi/src/window.rs @@ -27,6 +27,7 @@ use crate::window_manager_event::WindowManagerEvent; use crate::windows_api; use crate::windows_api::WindowsApi; use crate::AnimationStyle; +use crate::WindowHandlingBehaviour; use crate::FLOATING_APPLICATIONS; use crate::FLOATING_WINDOW_TOGGLE_ASPECT_RATIO; use crate::HIDDEN_HWNDS; @@ -39,6 +40,7 @@ use crate::PERMAIGNORE_CLASSES; use crate::REGEX_IDENTIFIERS; use crate::SLOW_APPLICATION_COMPENSATION_TIME; use crate::SLOW_APPLICATION_IDENTIFIERS; +use crate::WINDOW_HANDLING_BEHAVIOUR; use crate::WSL2_UI_PROCESSES; use color_eyre::eyre; use color_eyre::Result; @@ -203,8 +205,15 @@ impl RenderDispatcher for MovementRenderDispatcher { fn render(&self, progress: f64) -> Result<()> { let new_rect = self.start_rect.lerp(self.target_rect, progress, self.style); - // MoveWindow runs faster than SetWindowPos, but it doesn't support async window pos - WindowsApi::position_window(self.hwnd, &new_rect, self.top, true)?; + match WINDOW_HANDLING_BEHAVIOUR.load() { + WindowHandlingBehaviour::Sync => { + WindowsApi::move_window(self.hwnd, &new_rect, false)?; + } + WindowHandlingBehaviour::Async => { + // MoveWindow runs faster than SetWindowPos, but it doesn't support async window pos + WindowsApi::position_window(self.hwnd, &new_rect, self.top, true)?; + } + } WindowsApi::invalidate_rect(self.hwnd, None, false); Ok(()) diff --git a/komorebi/src/windows_api.rs b/komorebi/src/windows_api.rs index 4c4f61f3..9d19fb8b 100644 --- a/komorebi/src/windows_api.rs +++ b/komorebi/src/windows_api.rs @@ -8,7 +8,6 @@ use std::collections::VecDeque; use std::convert::TryFrom; use std::mem::size_of; use std::path::Path; -use std::sync::atomic::Ordering; use windows::core::Result as WindowsCrateResult; use windows::core::PCWSTR; use windows::core::PWSTR; @@ -160,11 +159,12 @@ use crate::ring::Ring; use crate::set_window_position::SetWindowPosition; use crate::windows_callbacks; use crate::Window; +use crate::WindowHandlingBehaviour; use crate::WindowManager; -use crate::ASYNC_WINDOW_HANDLING_ENABLED; use crate::DISPLAY_INDEX_PREFERENCES; use crate::DUPLICATE_MONITOR_SERIAL_IDS; use crate::MONITOR_INDEX_PREFERENCES; +use crate::WINDOW_HANDLING_BEHAVIOUR; macro_rules! as_ptr { ($value:expr) => { @@ -481,7 +481,7 @@ impl WindowsApi { hwnd: isize, layout: &Rect, top: bool, - async_window_pos: bool, + supports_async: bool, ) -> Result<()> { let hwnd = HWND(as_ptr!(hwnd)); @@ -497,7 +497,12 @@ impl WindowsApi { // By default SetWindowPos waits for target window's WindowProc thread // to process the message, so we have to use ASYNC_WINDOW_POS to avoid // blocking our thread in case the target window is not responding. - if async_window_pos && ASYNC_WINDOW_HANDLING_ENABLED.load(Ordering::SeqCst) { + if supports_async + && matches!( + WINDOW_HANDLING_BEHAVIOUR.load(), + WindowHandlingBehaviour::Async + ) + { flags |= SetWindowPosition::ASYNC_WINDOW_POS; } @@ -540,7 +545,10 @@ impl WindowsApi { | SetWindowPosition::NO_ACTIVATE | SetWindowPosition::SHOW_WINDOW; - if ASYNC_WINDOW_HANDLING_ENABLED.load(Ordering::SeqCst) { + if matches!( + WINDOW_HANDLING_BEHAVIOUR.load(), + WindowHandlingBehaviour::Async + ) { flags |= SetWindowPosition::ASYNC_WINDOW_POS; } @@ -561,7 +569,10 @@ impl WindowsApi { | SetWindowPosition::NO_ACTIVATE | SetWindowPosition::SHOW_WINDOW; - if ASYNC_WINDOW_HANDLING_ENABLED.load(Ordering::SeqCst) { + if matches!( + WINDOW_HANDLING_BEHAVIOUR.load(), + WindowHandlingBehaviour::Async + ) { flags |= SetWindowPosition::ASYNC_WINDOW_POS; } @@ -580,7 +591,10 @@ impl WindowsApi { | SetWindowPosition::NO_REDRAW | SetWindowPosition::SHOW_WINDOW; - if ASYNC_WINDOW_HANDLING_ENABLED.load(Ordering::SeqCst) { + if matches!( + WINDOW_HANDLING_BEHAVIOUR.load(), + WindowHandlingBehaviour::Async + ) { flags |= SetWindowPosition::ASYNC_WINDOW_POS; } @@ -626,7 +640,10 @@ impl WindowsApi { // 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 // TODO: error handling - if ASYNC_WINDOW_HANDLING_ENABLED.load(Ordering::SeqCst) { + if matches!( + WINDOW_HANDLING_BEHAVIOUR.load(), + WindowHandlingBehaviour::Async + ) { unsafe { let _ = ShowWindowAsync(HWND(as_ptr!(hwnd)), command); }; diff --git a/schema.json b/schema.json index bdb4e38b..6689b7e0 100644 --- a/schema.json +++ b/schema.json @@ -217,10 +217,6 @@ } ] }, - "async_window_handling": { - "description": "Use asynchronous window handling to avoid blocking the main thread when a window is not responding (default: false)", - "type": "boolean" - }, "bar_configurations": { "description": "Komorebi status bar configuration files for multiple instances on different monitors", "type": "array", @@ -4619,6 +4615,14 @@ } ] }, + "window_handling_behaviour": { + "description": "Which Windows API behaviour to use when manipulating windows (default: Sync)", + "type": "string", + "enum": [ + "Sync", + "Async" + ] + }, "window_hiding_behaviour": { "description": "Which Windows signal to use when hiding windows (default: Cloak)", "oneOf": [