diff --git a/Cargo.lock b/Cargo.lock index 905985bf..4783055f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -127,6 +127,9 @@ name = "bitflags" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +dependencies = [ + "serde", +] [[package]] name = "bumpalo" @@ -196,7 +199,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.59", ] [[package]] @@ -666,9 +669,9 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "hyper" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186548d73ac615b32a73aafe38fb4f56c0d340e110e5a200bcadbaf2e199263a" +checksum = "9f24ce812868d86d19daa79bf3bf9175bc44ea323391147a5e3abde2a283871b" dependencies = [ "bytes", "futures-channel", @@ -1007,7 +1010,7 @@ checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.59", ] [[package]] @@ -1201,7 +1204,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.59", ] [[package]] @@ -1322,7 +1325,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.59", ] [[package]] @@ -1387,9 +1390,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.79" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "a56dea16b0a29e94408b9aa5e2940a4eedbd128a1ba20e8f7ae60fd3d465af0e" dependencies = [ "unicode-ident", ] @@ -1695,7 +1698,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.59", ] [[package]] @@ -1821,7 +1824,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.58", + "syn 2.0.59", ] [[package]] @@ -1858,9 +1861,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.58" +version = "2.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" +checksum = "4a6531ffc7b071655e4ce2e04bd464c4830bb585a61cabb96cf808f05172615a" dependencies = [ "proc-macro2", "quote", @@ -1959,7 +1962,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.59", ] [[package]] @@ -2128,7 +2131,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.59", ] [[package]] @@ -2317,7 +2320,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.59", "wasm-bindgen-shared", ] @@ -2351,7 +2354,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.59", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2470,7 +2473,7 @@ checksum = "942ac266be9249c84ca862f0a164a39533dc2f6f33dc98ec89c8da99b82ea0bd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.59", ] [[package]] @@ -2481,7 +2484,7 @@ checksum = "da33557140a288fae4e1d5f8873aaf9eb6613a9cf82c3e070223ff177f598b60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.59", ] [[package]] diff --git a/komorebi-client/src/lib.rs b/komorebi-client/src/lib.rs index 969fd340..621753db 100644 --- a/komorebi-client/src/lib.rs +++ b/komorebi-client/src/lib.rs @@ -13,6 +13,7 @@ pub use komorebi::ActiveWindowBorderColours; pub use komorebi::GlobalState; pub use komorebi::Notification; pub use komorebi::NotificationEvent; +pub use komorebi::RuleDebug; pub use komorebi::StackbarConfig; pub use komorebi::State; pub use komorebi::TabsConfig; diff --git a/komorebi-core/src/lib.rs b/komorebi-core/src/lib.rs index 8891946a..5081f732 100644 --- a/komorebi-core/src/lib.rs +++ b/komorebi-core/src/lib.rs @@ -173,6 +173,7 @@ pub enum SocketMessage { SocketSchema, StaticConfigSchema, GenerateStaticConfig, + DebugWindow(isize), } impl SocketMessage { diff --git a/komorebi/Cargo.toml b/komorebi/Cargo.toml index b8922a96..45f87b0a 100644 --- a/komorebi/Cargo.toml +++ b/komorebi/Cargo.toml @@ -13,7 +13,7 @@ edition = "2021" [dependencies] komorebi-core = { path = "../komorebi-core" } -bitflags = "2" +bitflags = { version = "2", features = ["serde"] } clap = { version = "4", features = ["derive"] } color-eyre = { workspace = true } crossbeam-channel = "0.5" diff --git a/komorebi/src/lib.rs b/komorebi/src/lib.rs index 016162ae..ea36ca94 100644 --- a/komorebi/src/lib.rs +++ b/komorebi/src/lib.rs @@ -43,6 +43,7 @@ pub use process_command::*; pub use process_event::*; pub use stackbar::*; pub use static_config::*; +pub use window::*; pub use window_manager::*; pub use window_manager_event::*; pub use windows_api::WindowsApi; diff --git a/komorebi/src/process_command.rs b/komorebi/src/process_command.rs index 60a012eb..146a8c06 100644 --- a/komorebi/src/process_command.rs +++ b/komorebi/src/process_command.rs @@ -44,6 +44,7 @@ use crate::colour::Rgb; use crate::current_virtual_desktop; use crate::notify_subscribers; use crate::static_config::StaticConfig; +use crate::window::RuleDebug; use crate::window::Window; use crate::window_manager; use crate::window_manager::WindowManager; @@ -1345,6 +1346,14 @@ impl WindowManager { REMOVE_TITLEBARS.store(!current, Ordering::SeqCst); self.update_focused_workspace(false, false)?; } + SocketMessage::DebugWindow(hwnd) => { + let window = Window { hwnd }; + let mut rule_debug = RuleDebug::default(); + let _ = window.should_manage(None, &mut rule_debug); + let schema = serde_json::to_string_pretty(&rule_debug)?; + + reply.write_all(schema.as_bytes())?; + } // Deprecated commands SocketMessage::AltFocusHack(_) | SocketMessage::IdentifyBorderOverflowApplication(_, _) => {} diff --git a/komorebi/src/process_event.rs b/komorebi/src/process_event.rs index dddddd9f..d16422ea 100644 --- a/komorebi/src/process_event.rs +++ b/komorebi/src/process_event.rs @@ -15,6 +15,7 @@ use crate::border::Border; use crate::current_virtual_desktop; use crate::notify_subscribers; use crate::window::should_act; +use crate::window::RuleDebug; use crate::window_manager::WindowManager; use crate::window_manager_event::WindowManagerEvent; use crate::windows_api::WindowsApi; @@ -66,7 +67,9 @@ impl WindowManager { return Ok(()); } - let should_manage = event.window().should_manage(Some(event))?; + let mut rule_debug = RuleDebug::default(); + + let should_manage = event.window().should_manage(Some(event), &mut rule_debug)?; // Hide or reposition the window based on whether the target is managed. if BORDER_ENABLED.load(Ordering::SeqCst) { @@ -233,7 +236,8 @@ impl WindowManager { path, &tray_and_multi_window_identifiers, ®ex_identifiers, - ); + ) + .is_some(); if !window.is_window() || should_act diff --git a/komorebi/src/styles.rs b/komorebi/src/styles.rs index 54f35f83..6ecf17c5 100644 --- a/komorebi/src/styles.rs +++ b/komorebi/src/styles.rs @@ -1,4 +1,6 @@ use bitflags::bitflags; +use serde::Deserialize; +use serde::Serialize; use windows::Win32::UI::WindowsAndMessaging::WS_BORDER; use windows::Win32::UI::WindowsAndMessaging::WS_CAPTION; use windows::Win32::UI::WindowsAndMessaging::WS_CHILD; @@ -56,7 +58,7 @@ use windows::Win32::UI::WindowsAndMessaging::WS_VSCROLL; // https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles bitflags! { - #[derive(Default)] + #[derive(Default, Debug, Copy, Clone, Serialize, Deserialize)] pub struct WindowStyle: u32 { const BORDER = WS_BORDER.0; const CAPTION = WS_CAPTION.0; @@ -90,7 +92,7 @@ bitflags! { // https://docs.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles bitflags! { - #[derive(Default)] + #[derive(Default, Debug, Copy, Clone, Serialize, Deserialize)] pub struct ExtendedWindowStyle: u32 { const ACCEPTFILES = WS_EX_ACCEPTFILES.0; const APPWINDOW = WS_EX_APPWINDOW.0; diff --git a/komorebi/src/window.rs b/komorebi/src/window.rs index 11539b0e..23770735 100644 --- a/komorebi/src/window.rs +++ b/komorebi/src/window.rs @@ -41,7 +41,7 @@ use crate::WSL2_UI_PROCESSES; #[derive(Debug, Clone, Copy, Deserialize, JsonSchema)] pub struct Window { - pub(crate) hwnd: isize, + pub hwnd: isize, } #[allow(clippy::module_name_repetitions)] @@ -153,6 +153,10 @@ impl Window { WindowsApi::is_iconic(self.hwnd()) } + pub fn is_visible(self) -> bool { + WindowsApi::is_window_visible(self.hwnd()) + } + pub fn hide(self) { let mut programmatically_hidden_hwnds = HIDDEN_HWNDS.lock(); if !programmatically_hidden_hwnds.contains(&self.hwnd) { @@ -313,18 +317,28 @@ impl Window { self.update_style(&style) } - #[tracing::instrument(fields(exe, title))] - pub fn should_manage(self, event: Option) -> Result { + #[tracing::instrument(fields(exe, title), skip(debug))] + pub fn should_manage( + self, + event: Option, + debug: &mut RuleDebug, + ) -> Result { if !self.is_window() { return Ok(false); } + debug.is_window = true; + if self.title().is_err() { return Ok(false); } + debug.has_title = true; + let is_cloaked = self.is_cloaked().unwrap_or_default(); + debug.is_cloaked = is_cloaked; + let mut allow_cloaked = false; if let Some(event) = event { @@ -336,17 +350,27 @@ impl Window { } } + debug.allow_cloaked = allow_cloaked; + match (allow_cloaked, is_cloaked) { // If allowing cloaked windows, we don't need to check the cloaked status (true, _) | // If not allowing cloaked windows, we need to ensure the window is not cloaked (false, false) => { if let (Ok(title), Ok(exe_name), Ok(class), Ok(path)) = (self.title(), self.exe(), self.class(), self.path()) { + debug.title = Some(title.clone()); + debug.exe_name = Some(exe_name.clone()); + debug.class = Some(class.clone()); + debug.path = Some(path.clone()); // calls for styles can fail quite often for events with windows that aren't really "windows" // since we have moved up calls of should_manage to the beginning of the process_event handler, // we should handle failures here gracefully to be able to continue the execution of process_event if let (Ok(style), Ok(ex_style)) = (&self.style(), &self.ex_style()) { - return Ok(window_is_eligible(&title, &exe_name, &class, &path, style, ex_style, event)); + debug.window_style = Some(*style); + debug.extended_window_style = Some(*ex_style); + let eligible = window_is_eligible(&title, &exe_name, &class, &path, style, ex_style, event, debug); + debug.should_manage = eligible; + return Ok(eligible); } } } @@ -357,6 +381,28 @@ impl Window { } } +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct RuleDebug { + pub should_manage: bool, + pub is_window: bool, + pub has_title: bool, + pub is_cloaked: bool, + pub allow_cloaked: bool, + pub window_style: Option, + pub extended_window_style: Option, + pub title: Option, + pub exe_name: Option, + pub class: Option, + pub path: Option, + pub matches_permaignore_class: Option, + pub matches_float_identifier: Option, + pub matches_managed_override: Option, + pub matches_layered_whitelist: Option, + pub matches_wsl2_gui: Option, + pub matches_no_titlebar: Option, +} + +#[allow(clippy::too_many_arguments)] fn window_is_eligible( title: &String, exe_name: &String, @@ -365,10 +411,12 @@ fn window_is_eligible( style: &WindowStyle, ex_style: &ExtendedWindowStyle, event: Option, + debug: &mut RuleDebug, ) -> bool { { let permaignore_classes = PERMAIGNORE_CLASSES.lock(); if permaignore_classes.contains(class) { + debug.matches_permaignore_class = Some(class.clone()); return false; } } @@ -376,45 +424,65 @@ fn window_is_eligible( let regex_identifiers = REGEX_IDENTIFIERS.lock(); let float_identifiers = FLOAT_IDENTIFIERS.lock(); - let should_float = should_act( + let should_float = if let Some(rule) = should_act( title, exe_name, class, path, &float_identifiers, ®ex_identifiers, - ); + ) { + debug.matches_float_identifier = Some(rule); + true + } else { + false + }; let manage_identifiers = MANAGE_IDENTIFIERS.lock(); - let managed_override = should_act( + let managed_override = if let Some(rule) = should_act( title, exe_name, class, path, &manage_identifiers, ®ex_identifiers, - ); + ) { + debug.matches_managed_override = Some(rule); + true + } else { + false + }; if should_float && !managed_override { return false; } let layered_whitelist = LAYERED_WHITELIST.lock(); - let allow_layered = should_act( + let allow_layered = if let Some(rule) = should_act( title, exe_name, class, path, &layered_whitelist, ®ex_identifiers, - ); + ) { + debug.matches_layered_whitelist = Some(rule); + true + } else { + false + }; // TODO: might need this for transparency // let allow_layered = true; let allow_wsl2_gui = { let wsl2_ui_processes = WSL2_UI_PROCESSES.lock(); - wsl2_ui_processes.contains(exe_name) + let allow = wsl2_ui_processes.contains(exe_name); + if allow { + debug.matches_wsl2_gui = Some(exe_name.clone()) + } + + allow }; let allow_titlebar_removed = { @@ -456,10 +524,10 @@ pub fn should_act( path: &str, identifiers: &[MatchingRule], regex_identifiers: &HashMap, -) -> bool { - let mut should_act = false; - for identifier in identifiers { - match identifier { +) -> Option { + let mut matching_rule = None; + for rule in identifiers { + match rule { MatchingRule::Simple(identifier) => { if should_act_individual( title, @@ -469,7 +537,7 @@ pub fn should_act( identifier, regex_identifiers, ) { - should_act = true + matching_rule = Some(rule.clone()); }; } MatchingRule::Composite(identifiers) => { @@ -486,13 +554,13 @@ pub fn should_act( } if composite_results.iter().all(|&x| x) { - should_act = true; + matching_rule = Some(rule.clone()); } } } } - should_act + matching_rule } pub fn should_act_individual( diff --git a/komorebi/src/window_manager_event.rs b/komorebi/src/window_manager_event.rs index afa30873..2726bc3d 100644 --- a/komorebi/src/window_manager_event.rs +++ b/komorebi/src/window_manager_event.rs @@ -153,7 +153,8 @@ impl WindowManagerEvent { path, &object_name_change_on_launch, ®ex_identifiers, - ); + ) + .is_some(); if should_trigger { Option::from(Self::Show(winevent, window)) diff --git a/komorebi/src/windows_callbacks.rs b/komorebi/src/windows_callbacks.rs index 94055081..f1bac55a 100644 --- a/komorebi/src/windows_callbacks.rs +++ b/komorebi/src/windows_callbacks.rs @@ -36,6 +36,7 @@ use windows::Win32::UI::WindowsAndMessaging::WM_SETTINGCHANGE; use crate::container::Container; use crate::monitor::Monitor; use crate::ring::Ring; +use crate::window::RuleDebug; use crate::window::Window; use crate::window_manager_event::WindowManagerEvent; use crate::windows_api::WindowsApi; @@ -157,7 +158,7 @@ pub extern "system" fn enum_window(hwnd: HWND, lparam: LPARAM) -> BOOL { if is_visible && is_window && !is_minimized { let window = Window { hwnd: hwnd.0 }; - if let Ok(should_manage) = window.should_manage(None) { + if let Ok(should_manage) = window.should_manage(None, &mut RuleDebug::default()) { if should_manage { if is_maximized { WindowsApi::restore_window(hwnd);