diff --git a/Cargo.lock b/Cargo.lock index 19637e89..c3717904 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1172,9 +1172,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.20.4" +version = "0.20.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffff4a02fa61eee51f95210fc9c98ea6eeb46bb071adeafd61e1a0b9b22c6a6d" +checksum = "e223c65cd36b485a34c2ce6e38efa40777d31c4166d9076030c74cdcf971679f" dependencies = [ "cfg-if 1.0.0", "core-foundation-sys", diff --git a/README.md b/README.md index f859d44e..c5836483 100644 --- a/README.md +++ b/README.md @@ -242,6 +242,7 @@ cycle-monitor Focus the monitor in the given cycle direction cycle-workspace Focus the workspace in the given cycle direction new-workspace Create and append a new workspace on the focused monitor invisible-borders Set the invisible border dimensions around each window +work-area-offset Set offsets to exclude parts of the work area from tiling adjust-container-padding Adjust container padding on the focused workspace adjust-workspace-padding Adjust workspace padding on the focused workspace change-layout Set the layout on the focused workspace @@ -313,6 +314,7 @@ used [is available here](komorebi.sample.with.lib.ahk). - [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] Configure work area offsets to preserve space for custom taskbars - [x] Configure and compensate for the size of Windows 10's invisible borders - [x] Toggle floating windows - [x] Toggle monocle window diff --git a/komorebi-core/src/lib.rs b/komorebi-core/src/lib.rs index e1b29666..8755acce 100644 --- a/komorebi-core/src/lib.rs +++ b/komorebi-core/src/lib.rs @@ -72,6 +72,7 @@ pub enum SocketMessage { ReloadConfiguration, WatchConfiguration(bool), InvisibleBorders(Rect), + WorkAreaOffset(Rect), WorkspaceRule(ApplicationIdentifier, String, usize, usize), FloatRule(ApplicationIdentifier, String), ManageRule(ApplicationIdentifier, String), diff --git a/komorebi/src/monitor.rs b/komorebi/src/monitor.rs index c6bd33e8..f459f24a 100644 --- a/komorebi/src/monitor.rs +++ b/komorebi/src/monitor.rs @@ -146,12 +146,16 @@ impl Monitor { self.workspaces().len() } - pub fn update_focused_workspace(&mut self, invisible_borders: &Rect) -> Result<()> { + pub fn update_focused_workspace( + &mut self, + offset: Option, + invisible_borders: &Rect, + ) -> Result<()> { let work_area = *self.work_area_size(); self.focused_workspace_mut() .ok_or_else(|| anyhow!("there is no workspace"))? - .update(&work_area, invisible_borders)?; + .update(&work_area, offset, invisible_borders)?; Ok(()) } diff --git a/komorebi/src/process_command.rs b/komorebi/src/process_command.rs index 0dedc6dd..6a86706e 100644 --- a/komorebi/src/process_command.rs +++ b/komorebi/src/process_command.rs @@ -371,6 +371,10 @@ impl WindowManager { self.invisible_borders = rect; self.retile_all()?; } + SocketMessage::WorkAreaOffset(rect) => { + self.work_area_offset = Option::from(rect); + self.retile_all()?; + } SocketMessage::QuickSave => { let workspace = self.focused_workspace()?; let resize = workspace.resize_dimensions(); diff --git a/komorebi/src/process_event.rs b/komorebi/src/process_event.rs index e9f94aab..4d6c9ad2 100644 --- a/komorebi/src/process_event.rs +++ b/komorebi/src/process_event.rs @@ -66,13 +66,14 @@ impl WindowManager { } let invisible_borders = self.invisible_borders; + let offset = self.work_area_offset; for (i, monitor) in self.monitors_mut().iter_mut().enumerate() { let work_area = *monitor.work_area_size(); for (j, workspace) in monitor.workspaces_mut().iter_mut().enumerate() { let reaped_orphans = workspace.reap_orphans()?; if reaped_orphans.0 > 0 || reaped_orphans.1 > 0 { - workspace.update(&work_area, &invisible_borders)?; + workspace.update(&work_area, offset, &invisible_borders)?; tracing::info!( "reaped {} orphan window(s) and {} orphaned container(s) on monitor: {}, workspace: {}", reaped_orphans.0, diff --git a/komorebi/src/window_manager.rs b/komorebi/src/window_manager.rs index 251057e5..7697ea6b 100644 --- a/komorebi/src/window_manager.rs +++ b/komorebi/src/window_manager.rs @@ -45,6 +45,7 @@ pub struct WindowManager { pub command_listener: UnixListener, pub is_paused: bool, pub invisible_borders: Rect, + pub work_area_offset: Option, pub focus_follows_mouse: Option, pub hotwatch: Hotwatch, pub virtual_desktop_id: Option, @@ -56,6 +57,7 @@ pub struct State { pub monitors: Ring, pub is_paused: bool, pub invisible_borders: Rect, + pub work_area_offset: Option, pub focus_follows_mouse: Option, pub has_pending_raise_op: bool, pub float_identifiers: Vec, @@ -72,6 +74,7 @@ impl From<&mut WindowManager> for State { monitors: wm.monitors.clone(), is_paused: wm.is_paused, invisible_borders: wm.invisible_borders, + work_area_offset: wm.work_area_offset, focus_follows_mouse: wm.focus_follows_mouse.clone(), has_pending_raise_op: wm.has_pending_raise_op, float_identifiers: FLOAT_IDENTIFIERS.lock().clone(), @@ -143,6 +146,7 @@ impl WindowManager { right: 14, bottom: 7, }, + work_area_offset: None, focus_follows_mouse: None, hotwatch: Hotwatch::new()?, virtual_desktop_id, @@ -265,6 +269,7 @@ impl WindowManager { self.monitors_mut().retain(|m| !invalid.contains(&m.id())); let invisible_borders = self.invisible_borders; + let offset = self.work_area_offset; for monitor in self.monitors_mut() { let mut should_update = false; @@ -293,7 +298,7 @@ impl WindowManager { } if should_update { - monitor.update_focused_workspace(&invisible_borders)?; + monitor.update_focused_workspace(offset, &invisible_borders)?; } } @@ -423,6 +428,8 @@ impl WindowManager { #[tracing::instrument(skip(self))] pub fn retile_all(&mut self) -> Result<()> { let invisible_borders = self.invisible_borders; + let offset = self.work_area_offset; + for monitor in self.monitors_mut() { let work_area = *monitor.work_area_size(); let workspace = monitor @@ -434,7 +441,7 @@ impl WindowManager { *resize = None; } - workspace.update(&work_area, &invisible_borders)?; + workspace.update(&work_area, offset, &invisible_borders)?; } Ok(()) @@ -534,10 +541,11 @@ impl WindowManager { tracing::info!("updating"); let invisible_borders = self.invisible_borders; + let offset = self.work_area_offset; self.focused_monitor_mut() .ok_or_else(|| anyhow!("there is no monitor"))? - .update_focused_workspace(&invisible_borders)?; + .update_focused_workspace(offset, &invisible_borders)?; if mouse_follows_focus { if let Some(window) = self.focused_workspace()?.maximized_window() { @@ -660,6 +668,7 @@ impl WindowManager { tracing::info!("moving container"); let invisible_borders = self.invisible_borders; + let offset = self.work_area_offset; let monitor = self .focused_monitor_mut() @@ -685,7 +694,7 @@ impl WindowManager { target_monitor.add_container(container)?; target_monitor.load_focused_workspace()?; - target_monitor.update_focused_workspace(&invisible_borders)?; + target_monitor.update_focused_workspace(offset, &invisible_borders)?; if follow { self.focus_monitor(idx)?; @@ -1075,6 +1084,7 @@ impl WindowManager { tracing::info!("setting workspace layout"); let invisible_borders = self.invisible_borders; + let offset = self.work_area_offset; let focused_monitor_idx = self.focused_monitor_idx(); let monitor = self @@ -1094,7 +1104,7 @@ impl WindowManager { // If this is the focused workspace on a non-focused screen, let's update it if focused_monitor_idx != monitor_idx && focused_workspace_idx == workspace_idx { - workspace.update(&work_area, &invisible_borders)?; + workspace.update(&work_area, offset, &invisible_borders)?; Ok(()) } else { Ok(self.update_focused_workspace(false)?) diff --git a/komorebi/src/workspace.rs b/komorebi/src/workspace.rs index 83c005c2..fa6840ed 100644 --- a/komorebi/src/workspace.rs +++ b/komorebi/src/workspace.rs @@ -138,8 +138,25 @@ impl Workspace { Ok(()) } - pub fn update(&mut self, work_area: &Rect, invisible_borders: &Rect) -> Result<()> { - let mut adjusted_work_area = *work_area; + pub fn update( + &mut self, + work_area: &Rect, + offset: Option, + invisible_borders: &Rect, + ) -> Result<()> { + let mut adjusted_work_area = offset.map_or_else( + || *work_area, + |offset| { + let mut with_offset = *work_area; + with_offset.left += offset.left; + with_offset.top += offset.top; + with_offset.right -= offset.right; + with_offset.bottom -= offset.bottom; + + with_offset + }, + ); + adjusted_work_area.add_padding(self.workspace_padding()); self.enforce_resize_constraints(); diff --git a/komorebic.lib.sample.ahk b/komorebic.lib.sample.ahk index 870903b2..75dbd8be 100644 --- a/komorebic.lib.sample.ahk +++ b/komorebic.lib.sample.ahk @@ -108,6 +108,10 @@ InvisibleBorders(left, top, right, bottom) { Run, komorebic.exe invisible-borders %left% %top% %right% %bottom%, , Hide } +WorkAreaOffset(left, top, right, bottom) { + Run, komorebic.exe work-area-offset %left% %top% %right% %bottom%, , Hide +} + AdjustContainerPadding(sizing, adjustment) { Run, komorebic.exe adjust-container-padding %sizing% %adjustment%, , Hide } diff --git a/komorebic/src/main.rs b/komorebic/src/main.rs index bdf0bf65..31dd3f11 100644 --- a/komorebic/src/main.rs +++ b/komorebic/src/main.rs @@ -167,6 +167,18 @@ struct InvisibleBorders { bottom: i32, } +#[derive(Clap, AhkFunction)] +struct WorkAreaOffset { + /// Size of the left work area offset (set right to left * 2 to maintain right padding) + left: i32, + /// Size of the top work area offset (set bottom to the same value to maintain bottom padding) + top: i32, + /// Size of the right work area offset + right: i32, + /// Size of the bottom work area offset + bottom: i32, +} + #[derive(Clap, AhkFunction)] struct EnsureWorkspaces { /// Monitor index (zero-indexed) @@ -366,6 +378,9 @@ enum SubCommand { /// Set the invisible border dimensions around each window #[clap(setting = AppSettings::ArgRequiredElseHelp)] InvisibleBorders(InvisibleBorders), + /// Set offsets to exclude parts of the work area from tiling + #[clap(setting = AppSettings::ArgRequiredElseHelp)] + WorkAreaOffset(WorkAreaOffset), /// Adjust container padding on the focused workspace #[clap(setting = AppSettings::ArgRequiredElseHelp)] AdjustContainerPadding(AdjustContainerPadding), @@ -538,6 +553,17 @@ fn main() -> Result<()> { .as_bytes()?, )?; } + SubCommand::WorkAreaOffset(arg) => { + send_message( + &*SocketMessage::WorkAreaOffset(Rect { + left: arg.left, + top: arg.top, + right: arg.right, + bottom: arg.bottom, + }) + .as_bytes()?, + )?; + } SubCommand::ContainerPadding(arg) => { send_message( &*SocketMessage::ContainerPadding(arg.monitor, arg.workspace, arg.size)