feat(wm): add cmd to id apps that overflow borders

Applications like Spotify and Discord draw over the default invisible
borders of Windows 10, which means that when komorebi is setting their
positions, the offset is always off by the amount of pixels of the
invisible borders on each side.

This commit makes it possible to identify applications that have
overflowing borders so that they can be handled appropriately by the
window manager.

This commit also takes the opportunity to consolidate the tray and multi
window identifiers into a single vector instead of spreading them across
multiple vectors by identifier type.

resolve #32
This commit is contained in:
LGUG2Z
2021-09-13 09:03:38 -07:00
parent c4c8bd7d4b
commit ff53533da0
11 changed files with 83 additions and 49 deletions

View File

@@ -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

View File

@@ -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),

View File

@@ -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

View File

@@ -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")

View File

@@ -53,16 +53,15 @@ lazy_static! {
static ref HIDDEN_HWNDS: Arc<Mutex<Vec<isize>>> = Arc::new(Mutex::new(vec![]));
static ref LAYERED_EXE_WHITELIST: Arc<Mutex<Vec<String>>> =
Arc::new(Mutex::new(vec!["steam.exe".to_string()]));
static ref TRAY_AND_MULTI_WINDOW_CLASSES: Arc<Mutex<Vec<String>>> =
Arc::new(Mutex::new(vec![]));
static ref TRAY_AND_MULTI_WINDOW_EXES: Arc<Mutex<Vec<String>>> = 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<Mutex<Vec<String>>> =
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<Mutex<Vec<String>>> = 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<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![]));
static ref FLOAT_IDENTIFIERS: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![]));
static ref BORDER_OVERFLOW_IDENTIFIERS: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![]));
}
fn setup() -> Result<(WorkerGuard, WorkerGuard)> {

View File

@@ -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()?;
}

View File

@@ -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<Mutex<WindowManager>>) {
@@ -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;

View File

@@ -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)
}

View File

@@ -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<String>,
pub manage_identifiers: Vec<String>,
pub layered_exe_whitelist: Vec<String>,
pub tray_and_multi_window_exes: Vec<String>,
pub tray_and_multi_window_classes: Vec<String>,
pub tray_and_multi_window_identifiers: Vec<String>,
pub border_overflow_identifiers: Vec<String>,
}
#[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(),
}
}
}

View File

@@ -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
}

View File

@@ -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()?)?;
}