diff --git a/README.md b/README.md index 2de67762..1d5474b5 100644 --- a/README.md +++ b/README.md @@ -234,6 +234,7 @@ float-rule Add a rule to always float the specified applicati manage-rule Add a rule to always manage the specified application workspace-rule Add a rule to associate an application with a workspace identify-tray-application Identify an application that closes to the system tray +identify-border-overflow Identify an application that has overflowing borders focus-follows-mouse Enable or disable focus follows mouse for the operating system toggle-focus-follows-mouse Toggle focus follows mouse for the operating system ahk-library Generate a library of AutoHotKey helper functions @@ -274,6 +275,7 @@ used [is available here](komorebi.sample.with.lib.ahk). - [x] Floating rules based on exe name, window title and class - [x] Workspace rules based on exe name and window class - [x] Additional manage rules based on exe name and window class +- [x] Identify applications which overflow their borders by exe name and class - [x] Identify 'close/minimize to tray' applications by exe name and class - [x] Toggle floating windows - [x] Toggle monocle window diff --git a/komorebi-core/src/lib.rs b/komorebi-core/src/lib.rs index 445d6a16..c4cd6e1e 100644 --- a/komorebi-core/src/lib.rs +++ b/komorebi-core/src/lib.rs @@ -66,6 +66,7 @@ pub enum SocketMessage { FloatRule(ApplicationIdentifier, String), ManageRule(ApplicationIdentifier, String), IdentifyTrayApplication(ApplicationIdentifier, String), + IdentifyBorderOverflow(ApplicationIdentifier, String), State, Query(StateQuery), FocusFollowsMouse(FocusFollowsMouseImplementation, bool), diff --git a/komorebi.sample.ahk b/komorebi.sample.ahk index 46e2e2bd..a4ed6ef6 100644 --- a/komorebi.sample.ahk +++ b/komorebi.sample.ahk @@ -52,6 +52,9 @@ Run, komorebic.exe manage-rule exe TIM.exe, , Hide ; Identify applications that close to the tray Run, komorebic.exe identify-tray-application exe Discord.exe, , Hide +; Identify applications that have overflowing borders +Run, komorebic.exe identify-border-overflow exe Discord.exe, , Hide + ; Change the focused window, Alt + Vim direction keys !h:: Run, komorebic.exe focus left, , Hide diff --git a/komorebi.sample.with.lib.ahk b/komorebi.sample.with.lib.ahk index 6f553345..57e194ac 100644 --- a/komorebi.sample.with.lib.ahk +++ b/komorebi.sample.with.lib.ahk @@ -42,6 +42,11 @@ FloatRule("exe", "Wox.exe") IdentifyTrayApplication("exe", "Discord.exe") IdentifyTrayApplication("exe", "Spotify.exe") +; Identify Electron applications with overflowing borders +IdentifyBorderOverflow("exe", "Discord.exe") +IdentifyBorderOverflow("exe", "Spotify.exe") +IdentifyBorderOverflow("class", "ZPFTEWndClass") + ; Change the focused window, Alt + Vim direction keys !h:: Focus("left") diff --git a/komorebi/src/main.rs b/komorebi/src/main.rs index 7a20fa8a..135b692c 100644 --- a/komorebi/src/main.rs +++ b/komorebi/src/main.rs @@ -53,16 +53,15 @@ lazy_static! { static ref HIDDEN_HWNDS: Arc>> = Arc::new(Mutex::new(vec![])); static ref LAYERED_EXE_WHITELIST: Arc>> = Arc::new(Mutex::new(vec!["steam.exe".to_string()])); - static ref TRAY_AND_MULTI_WINDOW_CLASSES: Arc>> = - Arc::new(Mutex::new(vec![])); - static ref TRAY_AND_MULTI_WINDOW_EXES: Arc>> = Arc::new(Mutex::new(vec![ - "explorer.exe".to_string(), - "firefox.exe".to_string(), - "chrome.exe".to_string(), - "idea64.exe".to_string(), - "ApplicationFrameHost.exe".to_string(), - "steam.exe".to_string(), - ])); + static ref TRAY_AND_MULTI_WINDOW_IDENTIFIERS: Arc>> = + Arc::new(Mutex::new(vec![ + "explorer.exe".to_string(), + "firefox.exe".to_string(), + "chrome.exe".to_string(), + "idea64.exe".to_string(), + "ApplicationFrameHost.exe".to_string(), + "steam.exe".to_string(), + ])); static ref OBJECT_NAME_CHANGE_ON_LAUNCH: Arc>> = Arc::new(Mutex::new(vec![ "firefox.exe".to_string(), "idea64.exe".to_string(), @@ -71,6 +70,7 @@ lazy_static! { Arc::new(Mutex::new(HashMap::new())); static ref MANAGE_IDENTIFIERS: Arc>> = Arc::new(Mutex::new(vec![])); static ref FLOAT_IDENTIFIERS: Arc>> = Arc::new(Mutex::new(vec![])); + static ref BORDER_OVERFLOW_IDENTIFIERS: Arc>> = Arc::new(Mutex::new(vec![])); } fn setup() -> Result<(WorkerGuard, WorkerGuard)> { diff --git a/komorebi/src/process_command.rs b/komorebi/src/process_command.rs index 50c321df..ae37147f 100644 --- a/komorebi/src/process_command.rs +++ b/komorebi/src/process_command.rs @@ -10,7 +10,6 @@ use color_eyre::Result; use parking_lot::Mutex; use uds_windows::UnixStream; -use komorebi_core::ApplicationIdentifier; use komorebi_core::FocusFollowsMouseImplementation; use komorebi_core::SocketMessage; use komorebi_core::StateQuery; @@ -18,10 +17,10 @@ use komorebi_core::StateQuery; use crate::window_manager; use crate::window_manager::WindowManager; use crate::windows_api::WindowsApi; +use crate::BORDER_OVERFLOW_IDENTIFIERS; use crate::FLOAT_IDENTIFIERS; use crate::MANAGE_IDENTIFIERS; -use crate::TRAY_AND_MULTI_WINDOW_CLASSES; -use crate::TRAY_AND_MULTI_WINDOW_EXES; +use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS; use crate::WORKSPACE_RULES; #[tracing::instrument] @@ -295,21 +294,18 @@ impl WindowManager { SocketMessage::WatchConfiguration(enable) => { self.watch_configuration(enable)?; } - SocketMessage::IdentifyTrayApplication(identifier, id) => match identifier { - ApplicationIdentifier::Exe => { - let mut exes = TRAY_AND_MULTI_WINDOW_EXES.lock(); - if !exes.contains(&id) { - exes.push(id); - } + SocketMessage::IdentifyBorderOverflow(_, id) => { + let mut identifiers = BORDER_OVERFLOW_IDENTIFIERS.lock(); + if !identifiers.contains(&id) { + identifiers.push(id); } - ApplicationIdentifier::Class => { - let mut classes = TRAY_AND_MULTI_WINDOW_CLASSES.lock(); - if !classes.contains(&id) { - classes.push(id); - } + } + SocketMessage::IdentifyTrayApplication(_, id) => { + let mut identifiers = TRAY_AND_MULTI_WINDOW_IDENTIFIERS.lock(); + if !identifiers.contains(&id) { + identifiers.push(id); } - ApplicationIdentifier::Title => {} - }, + } SocketMessage::ManageFocusedWindow => { self.manage_focused_window()?; } diff --git a/komorebi/src/process_event.rs b/komorebi/src/process_event.rs index 4e28ee0f..1cfe143e 100644 --- a/komorebi/src/process_event.rs +++ b/komorebi/src/process_event.rs @@ -15,8 +15,7 @@ use crate::window_manager::WindowManager; use crate::window_manager_event::WindowManagerEvent; use crate::windows_api::WindowsApi; use crate::HIDDEN_HWNDS; -use crate::TRAY_AND_MULTI_WINDOW_CLASSES; -use crate::TRAY_AND_MULTI_WINDOW_EXES; +use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS; #[tracing::instrument] pub fn listen_for_events(wm: Arc>) { @@ -110,15 +109,16 @@ impl WindowManager { // and will have is_window() return true, as the process is still running even if // the window is not visible. { - let tray_and_multi_window_exes = TRAY_AND_MULTI_WINDOW_EXES.lock(); - let tray_and_multi_window_classes = TRAY_AND_MULTI_WINDOW_CLASSES.lock(); + let tray_and_multi_window_identifiers = + TRAY_AND_MULTI_WINDOW_IDENTIFIERS.lock(); // We don't want to purge windows that have been deliberately hidden by us, eg. when // they are not on the top of a container stack. let programmatically_hidden_hwnds = HIDDEN_HWNDS.lock(); - if (!window.is_window() || tray_and_multi_window_exes.contains(&window.exe()?)) - || tray_and_multi_window_classes.contains(&window.class()?) + if (!window.is_window() + || tray_and_multi_window_identifiers.contains(&window.exe()?)) + || tray_and_multi_window_identifiers.contains(&window.class()?) && !programmatically_hidden_hwnds.contains(&window.hwnd) { hide = true; diff --git a/komorebi/src/window.rs b/komorebi/src/window.rs index 4868333a..253fd501 100644 --- a/komorebi/src/window.rs +++ b/komorebi/src/window.rs @@ -15,6 +15,7 @@ use crate::styles::GwlExStyle; use crate::styles::GwlStyle; use crate::window_manager_event::WindowManagerEvent; use crate::windows_api::WindowsApi; +use crate::BORDER_OVERFLOW_IDENTIFIERS; use crate::FLOAT_IDENTIFIERS; use crate::HIDDEN_HWNDS; use crate::LAYERED_EXE_WHITELIST; @@ -107,18 +108,31 @@ impl Window { // }; let mut rect = *layout; - let border = Rect { - left: 12, - top: 0, - right: 24, - bottom: 12, - }; - // Remove the invisible border - rect.left -= border.left; - rect.top -= border.top; - rect.right += border.right; - rect.bottom += border.bottom; + let mut should_remove_border = true; + + let border_overflows = BORDER_OVERFLOW_IDENTIFIERS.lock(); + if border_overflows.contains(&self.title()?) + || border_overflows.contains(&self.exe()?) + || border_overflows.contains(&self.class()?) + { + should_remove_border = false; + } + + if should_remove_border { + let border = Rect { + left: 12, + top: 0, + right: 24, + bottom: 12, + }; + + // Remove the invisible border + rect.left -= border.left; + rect.top -= border.top; + rect.right += border.right; + rect.bottom += border.bottom; + } WindowsApi::position_window(self.hwnd(), &rect, top) } diff --git a/komorebi/src/window_manager.rs b/komorebi/src/window_manager.rs index 7eb11fbb..43289152 100644 --- a/komorebi/src/window_manager.rs +++ b/komorebi/src/window_manager.rs @@ -32,11 +32,11 @@ use crate::window_manager_event::WindowManagerEvent; use crate::windows_api::WindowsApi; use crate::winevent_listener::WINEVENT_CALLBACK_CHANNEL; use crate::workspace::Workspace; +use crate::BORDER_OVERFLOW_IDENTIFIERS; use crate::FLOAT_IDENTIFIERS; use crate::LAYERED_EXE_WHITELIST; use crate::MANAGE_IDENTIFIERS; -use crate::TRAY_AND_MULTI_WINDOW_CLASSES; -use crate::TRAY_AND_MULTI_WINDOW_EXES; +use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS; use crate::WORKSPACE_RULES; #[derive(Debug)] @@ -60,8 +60,8 @@ pub struct State { pub float_identifiers: Vec, pub manage_identifiers: Vec, pub layered_exe_whitelist: Vec, - pub tray_and_multi_window_exes: Vec, - pub tray_and_multi_window_classes: Vec, + pub tray_and_multi_window_identifiers: Vec, + pub border_overflow_identifiers: Vec, } #[allow(clippy::fallible_impl_from)] @@ -75,8 +75,8 @@ impl From<&mut WindowManager> for State { float_identifiers: FLOAT_IDENTIFIERS.lock().clone(), manage_identifiers: MANAGE_IDENTIFIERS.lock().clone(), layered_exe_whitelist: LAYERED_EXE_WHITELIST.lock().clone(), - tray_and_multi_window_exes: TRAY_AND_MULTI_WINDOW_EXES.lock().clone(), - tray_and_multi_window_classes: TRAY_AND_MULTI_WINDOW_CLASSES.lock().clone(), + tray_and_multi_window_identifiers: TRAY_AND_MULTI_WINDOW_IDENTIFIERS.lock().clone(), + border_overflow_identifiers: BORDER_OVERFLOW_IDENTIFIERS.lock().clone(), } } } diff --git a/komorebic.lib.sample.ahk b/komorebic.lib.sample.ahk index 157d836e..b351166f 100644 --- a/komorebic.lib.sample.ahk +++ b/komorebic.lib.sample.ahk @@ -176,6 +176,10 @@ IdentifyTrayApplication(identifier, id) { Run, komorebic.exe identify-tray-application %identifier% %id%, , Hide } +IdentifyBorderOverflow(identifier, id) { + Run, komorebic.exe identify-border-overflow %identifier% %id%, , Hide +} + FocusFollowsMouse(boolean_state, implementation) { Run, komorebic.exe focus-follows-mouse %boolean_state% --implementation %implementation%, , Hide } diff --git a/komorebic/src/main.rs b/komorebic/src/main.rs index 8e8a5532..cf6a4809 100644 --- a/komorebic/src/main.rs +++ b/komorebic/src/main.rs @@ -219,6 +219,7 @@ gen_application_target_subcommand_args! { FloatRule, ManageRule, IdentifyTrayApplication, + IdentifyBorderOverflow, } #[derive(Clap, AhkFunction)] @@ -371,6 +372,9 @@ enum SubCommand { /// Identify an application that closes to the system tray #[clap(setting = AppSettings::ArgRequiredElseHelp)] IdentifyTrayApplication(IdentifyTrayApplication), + /// Identify an application that has overflowing borders + #[clap(setting = AppSettings::ArgRequiredElseHelp)] + IdentifyBorderOverflow(IdentifyBorderOverflow), /// Enable or disable focus follows mouse for the operating system #[clap(setting = AppSettings::ArgRequiredElseHelp)] FocusFollowsMouse(FocusFollowsMouse), @@ -701,6 +705,11 @@ fn main() -> Result<()> { .as_bytes()?, )?; } + SubCommand::IdentifyBorderOverflow(target) => { + send_message( + &*SocketMessage::IdentifyBorderOverflow(target.identifier, target.id).as_bytes()?, + )?; + } SubCommand::Manage => { send_message(&*SocketMessage::ManageFocusedWindow.as_bytes()?)?; }