diff --git a/komorebi-client/src/lib.rs b/komorebi-client/src/lib.rs index dea7c6a6..4614fc20 100644 --- a/komorebi-client/src/lib.rs +++ b/komorebi-client/src/lib.rs @@ -4,6 +4,7 @@ pub use komorebi::animation::prefix::AnimationPrefix; pub use komorebi::animation::PerAnimationPrefixConfig; pub use komorebi::asc::ApplicationSpecificConfiguration; +pub use komorebi::border_manager::BorderInfo; pub use komorebi::colour::Colour; pub use komorebi::colour::Rgb; pub use komorebi::config_generation::ApplicationConfiguration; diff --git a/komorebi/src/border_manager/border.rs b/komorebi/src/border_manager/border.rs index 9c99ae47..c156956c 100644 --- a/komorebi/src/border_manager/border.rs +++ b/komorebi/src/border_manager/border.rs @@ -3,8 +3,6 @@ use crate::border_manager::RenderTarget; use crate::border_manager::WindowKind; use crate::border_manager::BORDER_OFFSET; use crate::border_manager::BORDER_WIDTH; -use crate::border_manager::FOCUS_STATE; -use crate::border_manager::RENDER_TARGETS; use crate::border_manager::STYLE; use crate::core::BorderStyle; use crate::core::Rect; @@ -114,6 +112,8 @@ pub extern "system" fn border_hwnds(hwnd: HWND, lparam: LPARAM) -> BOOL { #[derive(Debug, Clone)] pub struct Border { pub hwnd: isize, + pub id: String, + pub monitor_idx: Option, pub render_target: OnceLock, pub tracking_hwnd: isize, pub window_rect: Rect, @@ -130,6 +130,8 @@ impl From for Border { fn from(value: isize) -> Self { Self { hwnd: value, + id: String::new(), + monitor_idx: None, render_target: OnceLock::new(), tracking_hwnd: 0, window_rect: Rect::default(), @@ -149,7 +151,11 @@ impl Border { HWND(windows_api::as_ptr!(self.hwnd)) } - pub fn create(id: &str, tracking_hwnd: isize) -> color_eyre::Result { + pub fn create( + id: &str, + tracking_hwnd: isize, + monitor_idx: usize, + ) -> color_eyre::Result> { let name: Vec = format!("komoborder-{id}\0").encode_utf16().collect(); let class_name = PCWSTR(name.as_ptr()); @@ -168,9 +174,12 @@ impl Border { let (border_sender, border_receiver) = mpsc::channel(); let instance = h_module.0 as isize; + let container_id = id.to_owned(); std::thread::spawn(move || -> color_eyre::Result<()> { let mut border = Self { hwnd: 0, + id: container_id, + monitor_idx: Some(monitor_idx), render_target: OnceLock::new(), tracking_hwnd, window_rect: WindowsApi::window_rect(tracking_hwnd).unwrap_or_default(), @@ -183,12 +192,15 @@ impl Border { brushes: HashMap::new(), }; - let border_pointer = std::ptr::addr_of!(border); + let border_pointer = &raw mut border; let hwnd = WindowsApi::create_border_window(PCWSTR(name.as_ptr()), instance, border_pointer)?; - border.hwnd = hwnd; - border_sender.send(border_pointer as isize)?; + let boxed = unsafe { + (*border_pointer).hwnd = hwnd; + Box::from_raw(border_pointer) + }; + border_sender.send(boxed)?; let mut msg: MSG = MSG::default(); @@ -207,8 +219,7 @@ impl Border { Ok(()) }); - let border_ref = border_receiver.recv()?; - let border = unsafe { &mut *(border_ref as *mut Border) }; + let mut border = border_receiver.recv()?; // I have literally no idea, apparently this is to get rid of the black pixels // around the edges of rounded corners? @lukeyou05 borrowed this from PowerToys @@ -292,17 +303,13 @@ impl Border { } }; - let mut render_targets = RENDER_TARGETS.lock(); - render_targets.insert(border.hwnd, RenderTarget(render_target)); - Ok(border.clone()) + Ok(border) }, Err(error) => Err(error.into()), } } pub fn destroy(&self) -> color_eyre::Result<()> { - let mut render_targets = RENDER_TARGETS.lock(); - render_targets.remove(&self.hwnd); WindowsApi::close_window(self.hwnd) } @@ -361,7 +368,7 @@ impl Border { return LRESULT(0); } - let reference_hwnd = lparam.0; + let reference_hwnd = (*border_pointer).tracking_hwnd; let old_rect = (*border_pointer).window_rect; let rect = WindowsApi::window_rect(reference_hwnd).unwrap_or_default(); @@ -475,11 +482,6 @@ impl Border { }); // Get window kind and color - (*border_pointer).window_kind = FOCUS_STATE - .lock() - .get(&(window.0 as isize)) - .copied() - .unwrap_or(WindowKind::Unfocused); let window_kind = (*border_pointer).window_kind; if let Some(brush) = (*border_pointer).brushes.get(&window_kind) { render_target.BeginDraw(); diff --git a/komorebi/src/border_manager/mod.rs b/komorebi/src/border_manager/mod.rs index f0e80a34..0df4075d 100644 --- a/komorebi/src/border_manager/mod.rs +++ b/komorebi/src/border_manager/mod.rs @@ -5,6 +5,7 @@ use crate::core::BorderImplementation; use crate::core::BorderStyle; use crate::core::WindowKind; use crate::ring::Ring; +use crate::windows_api; use crate::workspace::WorkspaceLayer; use crate::workspace_reconciliator::ALT_TAB_HWND; use crate::Colour; @@ -31,6 +32,7 @@ use std::sync::atomic::Ordering; use std::sync::Arc; use std::sync::OnceLock; use strum::Display; +use windows::Win32::Foundation::HWND; use windows::Win32::Graphics::Direct2D::ID2D1HwndRenderTarget; pub static BORDER_WIDTH: AtomicI32 = AtomicI32::new(8); @@ -54,11 +56,8 @@ lazy_static! { } lazy_static! { - static ref BORDERS_MONITORS: Mutex> = Mutex::new(HashMap::new()); - static ref BORDER_STATE: Mutex> = Mutex::new(HashMap::new()); - static ref WINDOWS_BORDERS: Mutex> = Mutex::new(HashMap::new()); - static ref FOCUS_STATE: Mutex> = Mutex::new(HashMap::new()); - static ref RENDER_TARGETS: Mutex> = Mutex::new(HashMap::new()); + static ref BORDER_STATE: Mutex>> = Mutex::new(HashMap::new()); + static ref WINDOWS_BORDERS: Mutex> = Mutex::new(HashMap::new()); } #[derive(Debug, Clone)] @@ -75,6 +74,18 @@ impl Deref for RenderTarget { pub struct Notification(pub Option); +#[derive(Debug, Default, Clone, Copy, PartialEq)] +pub struct BorderInfo { + pub border_hwnd: isize, + pub window_kind: WindowKind, +} + +impl BorderInfo { + pub fn hwnd(&self) -> HWND { + HWND(windows_api::as_ptr!(self.border_hwnd)) + } +} + static CHANNEL: OnceLock<(Sender, Receiver)> = OnceLock::new(); pub fn channel() -> &'static (Sender, Receiver) { @@ -89,8 +100,13 @@ fn event_rx() -> Receiver { channel().1.clone() } -pub fn window_border(hwnd: isize) -> Option { - WINDOWS_BORDERS.lock().get(&hwnd).cloned() +pub fn window_border(hwnd: isize) -> Option { + WINDOWS_BORDERS.lock().get(&hwnd).and_then(|id| { + BORDER_STATE.lock().get(id).map(|b| BorderInfo { + border_hwnd: b.hwnd, + window_kind: b.window_kind, + }) + }) } pub fn send_notification(hwnd: Option) { @@ -106,15 +122,11 @@ pub fn destroy_all_borders() -> color_eyre::Result<()> { borders.iter().map(|b| b.1.hwnd).collect::>() ); - for (_, border) in borders.iter() { - let _ = border.destroy(); + for (_, border) in borders.drain() { + let _ = destroy_border(border); } - borders.clear(); - BORDERS_MONITORS.lock().clear(); WINDOWS_BORDERS.lock().clear(); - FOCUS_STATE.lock().clear(); - RENDER_TARGETS.lock().clear(); let mut remaining_hwnds = vec![]; @@ -127,7 +139,7 @@ pub fn destroy_all_borders() -> color_eyre::Result<()> { tracing::info!("purging unknown borders: {:?}", remaining_hwnds); for hwnd in remaining_hwnds { - let _ = Border::from(hwnd).destroy(); + let _ = destroy_border(Box::new(Border::from(hwnd))); } } @@ -276,8 +288,7 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result // border. (fw != &foreground_window && window_border(*fw) - .map(|b| b.window_kind == WindowKind::Floating) - .unwrap_or_default()) + .is_some_and(|b| b.window_kind == WindowKind::Floating)) }); // when the focused window has an `Unfocused` border kind, usually this happens if @@ -309,9 +320,7 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result } let mut borders = BORDER_STATE.lock(); - let mut borders_monitors = BORDERS_MONITORS.lock(); let mut windows_borders = WINDOWS_BORDERS.lock(); - let mut focus_state = FOCUS_STATE.lock(); // If borders are disabled if !BORDER_ENABLED.load_consume() @@ -321,14 +330,11 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result || ALT_TAB_HWND.load().is_some() { // Destroy the borders we know about - for (_, border) in borders.iter() { - border.destroy()?; + for (_, border) in borders.drain() { + destroy_border(border)?; } - borders.clear(); - borders_monitors.clear(); windows_borders.clear(); - focus_state.clear(); previous_is_paused = is_paused; continue 'receiver; @@ -343,8 +349,6 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result remove_borders( &mut borders, &mut windows_borders, - &mut focus_state, - &mut borders_monitors, monitor_idx, |_, _| true, )?; @@ -355,12 +359,16 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result // Handle the monocle container separately if let Some(monocle) = ws.monocle_container() { let mut new_border = false; - let border = match borders.entry(monocle.id().clone()) { + let focused_window_hwnd = + monocle.focused_window().map(|w| w.hwnd).unwrap_or_default(); + let id = monocle.id().clone(); + let border = match borders.entry(id.clone()) { Entry::Occupied(entry) => entry.into_mut(), Entry::Vacant(entry) => { if let Ok(border) = Border::create( monocle.id(), - monocle.focused_window().copied().unwrap_or_default().hwnd, + focused_window_hwnd, + monitor_idx, ) { new_border = true; entry.insert(border) @@ -376,32 +384,41 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result WindowKind::Monocle }; border.window_kind = new_focus_state; - focus_state.insert(border.hwnd, new_focus_state); - let reference_hwnd = - monocle.focused_window().copied().unwrap_or_default().hwnd; + // Update the borders tracking_hwnd in case it changed and remove the + // old `tracking_hwnd` from `WINDOWS_BORDERS` if needed. + if border.tracking_hwnd != focused_window_hwnd { + if let Some(previous) = windows_borders.get(&border.tracking_hwnd) { + // Only remove the border from `windows_borders` if it + // still corresponds to the same border, if doesn't then + // it means it was already updated by another border for + // that window and in that case we don't want to remove it. + if previous == &id { + windows_borders.remove(&border.tracking_hwnd); + } + } + border.tracking_hwnd = focused_window_hwnd; + if !WindowsApi::is_window_visible(border.hwnd) { + WindowsApi::restore_window(border.hwnd); + } + } - let rect = WindowsApi::window_rect(reference_hwnd)?; + let rect = WindowsApi::window_rect(focused_window_hwnd)?; + border.window_rect = rect; if new_border { - border.set_position(&rect, reference_hwnd)?; + border.set_position(&rect, focused_window_hwnd)?; } border.invalidate(); - borders_monitors.insert(monocle.id().clone(), monitor_idx); - windows_borders.insert( - monocle.focused_window().cloned().unwrap_or_default().hwnd, - border.clone(), - ); + windows_borders.insert(focused_window_hwnd, id); let border_hwnd = border.hwnd; // Remove all borders on this monitor except monocle remove_borders( &mut borders, &mut windows_borders, - &mut focus_state, - &mut borders_monitors, monitor_idx, |_, b| border_hwnd != b.hwnd, )?; @@ -420,8 +437,6 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result remove_borders( &mut borders, &mut windows_borders, - &mut focus_state, - &mut borders_monitors, monitor_idx, |_, _| true, )?; @@ -444,56 +459,22 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result remove_borders( &mut borders, &mut windows_borders, - &mut focus_state, - &mut borders_monitors, monitor_idx, |id, _| !container_and_floating_window_ids.contains(id), )?; 'containers: for (idx, c) in ws.containers().iter().enumerate() { - // In case this container is a stack we need to check it's - // unfocused windows to remove any attached border - let is_stack = c.windows().len() > 1; - if is_stack { - let focused_window_idx = c.focused_window_idx(); - let potential_stacked_border_handles = c - .windows() - .iter() - .enumerate() - .flat_map(|(i, w)| { - if i != focused_window_idx { - windows_borders.get(&w.hwnd).map(|b| b.hwnd) - } else { - None - } - }) - .collect::>(); - - if !potential_stacked_border_handles.is_empty() { - tracing::debug!( - "purging stacked borders: {:?}", - potential_stacked_border_handles - ); - remove_borders( - &mut borders, - &mut windows_borders, - &mut focus_state, - &mut borders_monitors, - monitor_idx, - |_, b| potential_stacked_border_handles.contains(&b.hwnd), - )?; - } - } - let focused_window_hwnd = c.focused_window().map(|w| w.hwnd).unwrap_or_default(); + let id = c.id().clone(); // Get the border entry for this container from the map or create one let mut new_border = false; - let border = match borders.entry(c.id().clone()) { + let border = match borders.entry(id.clone()) { Entry::Occupied(entry) => entry.into_mut(), Entry::Vacant(entry) => { - if let Ok(border) = Border::create(c.id(), focused_window_hwnd) + if let Ok(border) = + Border::create(c.id(), focused_window_hwnd, monitor_idx) { new_border = true; entry.insert(border) @@ -503,8 +484,7 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result } }; - #[allow(unused_assignments)] - let mut last_focus_state = None; + let last_focus_state = border.window_kind; let new_focus_state = if idx != ws.focused_container_idx() || monitor_idx != focused_monitor_idx @@ -516,40 +496,24 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result } else { WindowKind::Single }; + border.window_kind = new_focus_state; - last_focus_state = focus_state.get(&border.hwnd).copied(); - - // If this container's border was previously tracking a different - // window, then we need to destroy that border and create a new one - // tracking the correct window. + // Update the borders `tracking_hwnd` in case it changed and remove the + // old `tracking_hwnd` from `WINDOWS_BORDERS` if needed. if border.tracking_hwnd != focused_window_hwnd { - // Create new border - if let Ok(b) = Border::create( - c.id(), - c.focused_window().copied().unwrap_or_default().hwnd, - ) { - // Destroy previously stacked border window and remove its hwnd - // and tracking_hwnd. - border.destroy()?; - focus_state.remove(&border.hwnd); - if let Some(previous) = - windows_borders.get(&border.tracking_hwnd) - { - // Only remove the border from `windows_borders` if it - // still is the same border, if it isn't then it means it - // was already updated by another border for that window - // and in that case we don't want to remove it. - if previous.hwnd == border.hwnd { - windows_borders.remove(&border.tracking_hwnd); - } + if let Some(previous) = windows_borders.get(&border.tracking_hwnd) { + // Only remove the border from `windows_borders` if it + // still corresponds to the same border, if doesn't then + // it means it was already updated by another border for + // that window and in that case we don't want to remove it. + if previous == &id { + windows_borders.remove(&border.tracking_hwnd); } - - // Replace with new border - new_border = true; - *border = b; - } else { - continue 'monitors; + } + border.tracking_hwnd = focused_window_hwnd; + if !WindowsApi::is_window_visible(border.hwnd) { + WindowsApi::restore_window(border.hwnd); } } @@ -559,51 +523,38 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result let rect = match WindowsApi::window_rect(focused_window_hwnd) { Ok(rect) => rect, Err(_) => { - remove_border( - c.id(), - &mut borders, - &mut windows_borders, - &mut focus_state, - &mut borders_monitors, - )?; + remove_border(c.id(), &mut borders, &mut windows_borders)?; continue 'containers; } }; + border.window_rect = rect; let layer_changed = previous_layer != workspace_layer; - let should_invalidate = match last_focus_state { - None => true, - Some(last_focus_state) => { - (last_focus_state != new_focus_state) || layer_changed - } - }; - - if new_border || should_invalidate { - border.set_position(&rect, focused_window_hwnd)?; - } + let should_invalidate = new_border + || (last_focus_state != new_focus_state) + || layer_changed; if should_invalidate { + border.set_position(&rect, focused_window_hwnd)?; border.invalidate(); } - borders_monitors.insert(c.id().clone(), monitor_idx); - windows_borders.insert( - c.focused_window().cloned().unwrap_or_default().hwnd, - border.clone(), - ); - focus_state.insert(border.hwnd, new_focus_state); + windows_borders.insert(focused_window_hwnd, id); } { for window in ws.floating_windows() { let mut new_border = false; - let border = match borders.entry(window.hwnd.to_string()) { + let id = window.hwnd.to_string(); + let border = match borders.entry(id.clone()) { Entry::Occupied(entry) => entry.into_mut(), Entry::Vacant(entry) => { - if let Ok(border) = - Border::create(&window.hwnd.to_string(), window.hwnd) - { + if let Ok(border) = Border::create( + &window.hwnd.to_string(), + window.hwnd, + monitor_idx, + ) { new_border = true; entry.insert(border) } else { @@ -612,39 +563,31 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result } }; - #[allow(unused_assignments)] - let mut last_focus_state = None; - let mut new_focus_state = WindowKind::Unfocused; + let last_focus_state = border.window_kind; - if foreground_window == window.hwnd { - new_focus_state = WindowKind::Floating; - } + let new_focus_state = if foreground_window == window.hwnd { + WindowKind::Floating + } else { + WindowKind::Unfocused + }; border.window_kind = new_focus_state; - last_focus_state = focus_state.get(&border.hwnd).copied(); let rect = WindowsApi::window_rect(window.hwnd)?; + border.window_rect = rect; let layer_changed = previous_layer != workspace_layer; - let should_invalidate = match last_focus_state { - None => true, - Some(last_focus_state) => { - last_focus_state != new_focus_state || layer_changed - } - }; - - if new_border { - border.set_position(&rect, window.hwnd)?; - } + let should_invalidate = new_border + || (last_focus_state != new_focus_state) + || layer_changed; if should_invalidate { + border.set_position(&rect, window.hwnd)?; border.invalidate(); } - borders_monitors.insert(window.hwnd.to_string(), monitor_idx); - windows_borders.insert(window.hwnd, border.clone()); - focus_state.insert(border.hwnd, new_focus_state); + windows_borders.insert(window.hwnd, id); } } } @@ -667,24 +610,27 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result /// the container id and the border and returns a bool, if true that border /// will be removed. fn remove_borders( - borders: &mut HashMap, - windows_borders: &mut HashMap, - focus_state: &mut HashMap, - borders_monitors: &mut HashMap, + borders: &mut HashMap>, + windows_borders: &mut HashMap, monitor_idx: usize, condition: impl Fn(&String, &Border) -> bool, ) -> color_eyre::Result<()> { let mut to_remove = vec![]; for (id, border) in borders.iter() { - if borders_monitors.get(id).copied().unwrap_or_default() == monitor_idx + // if border is on this monitor + if border.monitor_idx.is_some_and(|idx| idx == monitor_idx) + // and the condition applies && condition(id, border) + // and the border is visible (we don't remove hidden borders) + && WindowsApi::is_window_visible(border.hwnd) { + // we mark it to be removed to_remove.push(id.clone()); } } for id in &to_remove { - remove_border(id, borders, windows_borders, focus_state, borders_monitors)?; + remove_border(id, borders, windows_borders)?; } Ok(()) @@ -693,21 +639,71 @@ fn remove_borders( /// Removes the border with `id` and all its related info from all maps fn remove_border( id: &str, - borders: &mut HashMap, - windows_borders: &mut HashMap, - focus_state: &mut HashMap, - borders_monitors: &mut HashMap, + borders: &mut HashMap>, + windows_borders: &mut HashMap, ) -> color_eyre::Result<()> { if let Some(removed_border) = borders.remove(id) { - removed_border.destroy()?; windows_borders.remove(&removed_border.tracking_hwnd); - focus_state.remove(&removed_border.hwnd); + destroy_border(removed_border)?; } - borders_monitors.remove(id); Ok(()) } +/// IMPORTANT: BEWARE when changing this function. We need to make sure that we don't let the +/// `Box` be dropped normally. We need to turn the `Box` into the raw pointer and use that +/// pointer to call the `.destroy()` funtion of the border so it closes the window. This way the +/// `Box` is consumed and the pointer is dropped like a normal `Copy` number instead of trying to +/// drop the struct it points to. The actual border is owned by the thread that created the window +/// and once the window closes that thread gets out of its loop, finishes and properly disposes of +/// the border. +fn destroy_border(border: Box) -> color_eyre::Result<()> { + let raw_pointer = Box::into_raw(border); + unsafe { + (*raw_pointer).destroy()?; + } + Ok(()) +} + +/// Removes the border around window with `tracking_hwnd` if it exists +pub fn delete_border(tracking_hwnd: isize) { + std::thread::spawn(move || { + let id = { + WINDOWS_BORDERS + .lock() + .get(&tracking_hwnd) + .cloned() + .unwrap_or_default() + }; + + let mut borders = BORDER_STATE.lock(); + let mut windows_borders = WINDOWS_BORDERS.lock(); + + if let Err(error) = remove_border(&id, &mut borders, &mut windows_borders) { + tracing::error!("Failed to delete border: {}", error); + } + }); +} + +/// Shows the border around window with `tracking_hwnd` if it exists +pub fn show_border(tracking_hwnd: isize) { + std::thread::spawn(move || { + if let Some(border_info) = window_border(tracking_hwnd) { + WindowsApi::restore_window(border_info.border_hwnd); + } + }); +} + +/// Hides the border around window with `tracking_hwnd` if it exists, unless the border kind is a +/// `Stack` border. +pub fn hide_border(tracking_hwnd: isize) { + std::thread::spawn(move || { + if let Some(border_info) = window_border(tracking_hwnd) { + WindowsApi::hide_window(border_info.border_hwnd); + } + }); +} + #[derive(Debug, Copy, Clone, Display, Serialize, Deserialize, PartialEq)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub enum ZOrder { diff --git a/komorebi/src/container.rs b/komorebi/src/container.rs index 7dbf98ca..c53acb32 100644 --- a/komorebi/src/container.rs +++ b/komorebi/src/container.rs @@ -63,6 +63,17 @@ impl Container { } } + pub fn load_focused_window_ignore_borders(&mut self) { + let focused_idx = self.focused_window_idx(); + for (i, window) in self.windows_mut().iter_mut().enumerate() { + if i == focused_idx { + window.restore_with_border(false); + } else { + window.hide_with_border(false); + } + } + } + pub fn hwnd_from_exe(&self, exe: &str) -> Option { for window in self.windows() { if let Ok(window_exe) = window.exe() { diff --git a/komorebi/src/core/mod.rs b/komorebi/src/core/mod.rs index 49297f97..59260318 100644 --- a/komorebi/src/core/mod.rs +++ b/komorebi/src/core/mod.rs @@ -291,13 +291,14 @@ pub enum BorderImplementation { } #[derive( - Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, PartialEq, Eq, Hash, + Copy, Clone, Debug,Default, Serialize, Deserialize, Display, EnumString, ValueEnum, PartialEq, Eq, Hash, )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub enum WindowKind { Single, Stack, Monocle, + #[default] Unfocused, Floating, } diff --git a/komorebi/src/stackbar_manager/stackbar.rs b/komorebi/src/stackbar_manager/stackbar.rs index a03f120e..2e547ebe 100644 --- a/komorebi/src/stackbar_manager/stackbar.rs +++ b/komorebi/src/stackbar_manager/stackbar.rs @@ -350,7 +350,7 @@ impl Stackbar { } // Restore the window corresponding to the tab we have clicked - window.restore(); + window.restore_with_border(false); if let Err(err) = window.focus(false) { tracing::error!( "stackbar WMLBUTTONDOWN focus error: hwnd {} ({})", @@ -361,7 +361,7 @@ impl Stackbar { } else { // Hide any windows in the stack that don't correspond to the window // we have clicked - window.hide(); + window.hide_with_border(false); } } } diff --git a/komorebi/src/window.rs b/komorebi/src/window.rs index d2367d33..df61dd43 100644 --- a/komorebi/src/window.rs +++ b/komorebi/src/window.rs @@ -10,6 +10,7 @@ use crate::animation::ANIMATION_ENABLED_PER_ANIMATION; use crate::animation::ANIMATION_MANAGER; use crate::animation::ANIMATION_STYLE_GLOBAL; use crate::animation::ANIMATION_STYLE_PER_ANIMATION; +use crate::border_manager; use crate::com::SetCloak; use crate::core::config_generation::IdWithIdentifier; use crate::core::config_generation::MatchingRule; @@ -476,7 +477,7 @@ impl Window { WindowsApi::is_window_visible(self.hwnd) } - pub fn hide(self) { + pub fn hide_with_border(self, hide_border: bool) { let mut programmatically_hidden_hwnds = HIDDEN_HWNDS.lock(); if !programmatically_hidden_hwnds.contains(&self.hwnd) { programmatically_hidden_hwnds.push(self.hwnd); @@ -488,9 +489,16 @@ impl Window { HidingBehaviour::Minimize => WindowsApi::minimize_window(self.hwnd), HidingBehaviour::Cloak => SetCloak(self.hwnd(), 1, 2), } + if hide_border { + border_manager::hide_border(self.hwnd); + } } - pub fn restore(self) { + pub fn hide(self) { + self.hide_with_border(true); + } + + pub fn restore_with_border(self, restore_border: bool) { let mut programmatically_hidden_hwnds = HIDDEN_HWNDS.lock(); if let Some(idx) = programmatically_hidden_hwnds .iter() @@ -506,6 +514,13 @@ impl Window { } HidingBehaviour::Cloak => SetCloak(self.hwnd(), 1, 0), } + if restore_border { + border_manager::show_border(self.hwnd); + } + } + + pub fn restore(self) { + self.restore_with_border(true); } pub fn minimize(self) { @@ -738,8 +753,8 @@ impl Window { /// it raises it as well. pub fn raise(self) -> Result<()> { WindowsApi::raise_window(self.hwnd)?; - if let Some(border) = crate::border_manager::window_border(self.hwnd) { - WindowsApi::raise_window(border.hwnd)?; + if let Some(border_info) = crate::border_manager::window_border(self.hwnd) { + WindowsApi::raise_window(border_info.border_hwnd)?; } Ok(()) } @@ -750,8 +765,8 @@ impl Window { /// it lowers it as well. pub fn lower(self) -> Result<()> { WindowsApi::lower_window(self.hwnd)?; - if let Some(border) = crate::border_manager::window_border(self.hwnd) { - WindowsApi::lower_window(border.hwnd)?; + if let Some(border_info) = crate::border_manager::window_border(self.hwnd) { + WindowsApi::lower_window(border_info.border_hwnd)?; } Ok(()) } diff --git a/komorebi/src/window_manager.rs b/komorebi/src/window_manager.rs index fce739fb..1bd92b7d 100644 --- a/komorebi/src/window_manager.rs +++ b/komorebi/src/window_manager.rs @@ -2530,7 +2530,7 @@ impl WindowManager { let next_idx = direction.next_idx(current_idx, len); container.focus_window(next_idx); - container.load_focused_window(); + container.load_focused_window_ignore_borders(); self.update_focused_workspace(self.mouse_follows_focus, true) } @@ -2563,7 +2563,7 @@ impl WindowManager { container.windows_mut().swap(current_idx, next_idx); container.focus_window(next_idx); - container.load_focused_window(); + container.load_focused_window_ignore_borders(); self.update_focused_workspace(self.mouse_follows_focus, true) } diff --git a/komorebi/src/windows_api.rs b/komorebi/src/windows_api.rs index 6ffd1508..7b7ab78c 100644 --- a/komorebi/src/windows_api.rs +++ b/komorebi/src/windows_api.rs @@ -1160,7 +1160,7 @@ impl WindowsApi { pub fn create_border_window( name: PCWSTR, instance: isize, - border: *const Border, + border: *mut Border, ) -> Result { unsafe { CreateWindowExW( diff --git a/komorebi/src/windows_callbacks.rs b/komorebi/src/windows_callbacks.rs index a5786cc8..a608a8fa 100644 --- a/komorebi/src/windows_callbacks.rs +++ b/komorebi/src/windows_callbacks.rs @@ -105,12 +105,16 @@ pub extern "system" fn win_event_hook( WinEvent::ObjectLocationChange | WinEvent::ObjectDestroy ) && !has_filtered_style(hwnd) { - let border_window = border_manager::window_border(hwnd.0 as isize); + let border_info = border_manager::window_border(hwnd.0 as isize); - if let Some(border) = border_window { + if let Some(border_info) = border_info { unsafe { - let _ = - SendNotifyMessageW(border.hwnd(), event, WPARAM(0), LPARAM(hwnd.0 as isize)); + let _ = SendNotifyMessageW( + border_info.hwnd(), + event, + WPARAM(0), + LPARAM(hwnd.0 as isize), + ); } } } diff --git a/komorebi/src/workspace.rs b/komorebi/src/workspace.rs index db2c9f20..68e7cca5 100644 --- a/komorebi/src/workspace.rs +++ b/komorebi/src/workspace.rs @@ -13,6 +13,7 @@ use getset::Setters; use serde::Deserialize; use serde::Serialize; +use crate::border_manager; use crate::core::Axis; use crate::core::CustomLayout; use crate::core::CycleDirection; @@ -846,6 +847,8 @@ impl Workspace { } pub fn remove_window(&mut self, hwnd: isize) -> Result<()> { + border_manager::delete_border(hwnd); + if self.floating_windows().iter().any(|w| w.hwnd == hwnd) { self.floating_windows_mut().retain(|w| w.hwnd != hwnd); return Ok(());