feat(wm): reconcile monitor state

When monitors turn on and off, they do not retain their hmonitor id,
therefore this commit introduces an initial attempt to reconcile invalid
and valid hmonitors after monitor changes based on the windows that are
assigned to them.

If a monitor has at least one window, and has been assigned a new
hmonitor id, komorebi will look up the current hmonitor of that window's
hwnd and update Monitor.id in-place.

When reconciling monitors, any monitor marked as invalid will be purged
from the window manager state.

This commit also applies some of the new clippy lints that come along
with the latest nightly release of Rust.

resolve #31
This commit is contained in:
LGUG2Z
2021-09-10 12:46:49 -07:00
parent e1bd0e9fcb
commit c4c8bd7d4b
7 changed files with 86 additions and 29 deletions

View File

@@ -2,7 +2,7 @@ use serde::Serialize;
use bindings::Windows::Win32::Foundation::RECT; use bindings::Windows::Win32::Foundation::RECT;
#[derive(Debug, Clone, Copy, Serialize, Eq, PartialEq)] #[derive(Debug, Default, Clone, Copy, Serialize, Eq, PartialEq)]
pub struct Rect { pub struct Rect {
pub left: i32, pub left: i32,
pub top: i32, pub top: i32,
@@ -10,17 +10,6 @@ pub struct Rect {
pub bottom: i32, pub bottom: i32,
} }
impl Default for Rect {
fn default() -> Self {
Self {
left: 0,
top: 0,
right: 0,
bottom: 0,
}
}
}
impl From<RECT> for Rect { impl From<RECT> for Rect {
fn from(rect: RECT) -> Self { fn from(rect: RECT) -> Self {
Self { Self {

View File

@@ -6,6 +6,7 @@ use color_eyre::Result;
use getset::CopyGetters; use getset::CopyGetters;
use getset::Getters; use getset::Getters;
use getset::MutGetters; use getset::MutGetters;
use getset::Setters;
use serde::Serialize; use serde::Serialize;
use komorebi_core::Rect; use komorebi_core::Rect;
@@ -14,9 +15,9 @@ use crate::container::Container;
use crate::ring::Ring; use crate::ring::Ring;
use crate::workspace::Workspace; use crate::workspace::Workspace;
#[derive(Debug, Clone, Serialize, Getters, CopyGetters, MutGetters)] #[derive(Debug, Clone, Serialize, Getters, CopyGetters, MutGetters, Setters)]
pub struct Monitor { pub struct Monitor {
#[getset(get_copy = "pub")] #[getset(get_copy = "pub", set = "pub")]
id: isize, id: isize,
monitor_size: Rect, monitor_size: Rect,
#[getset(get = "pub")] #[getset(get = "pub")]
@@ -30,11 +31,14 @@ pub struct Monitor {
impl_ring_elements!(Monitor, Workspace); impl_ring_elements!(Monitor, Workspace);
pub fn new(id: isize, monitor_size: Rect, work_area_size: Rect) -> Monitor { pub fn new(id: isize, monitor_size: Rect, work_area_size: Rect) -> Monitor {
let mut workspaces = Ring::default();
workspaces.elements_mut().push_back(Workspace::default());
Monitor { Monitor {
id, id,
monitor_size, monitor_size,
work_area_size, work_area_size,
workspaces: Ring::default(), workspaces,
workspace_names: HashMap::default(), workspace_names: HashMap::default(),
} }
} }

View File

@@ -55,8 +55,9 @@ impl WindowManager {
WindowManagerEvent::FocusChange(_, window) WindowManagerEvent::FocusChange(_, window)
| WindowManagerEvent::Show(_, window) | WindowManagerEvent::Show(_, window)
| WindowManagerEvent::MoveResizeEnd(_, window) => { | WindowManagerEvent::MoveResizeEnd(_, window) => {
let monitor_idx = self self.reconcile_monitors()?;
.monitor_idx_from_window(*window)
let monitor_idx = self.monitor_idx_from_window(*window)
.ok_or_else(|| anyhow!("there is no monitor associated with this window, it may have already been destroyed"))?; .ok_or_else(|| anyhow!("there is no monitor associated with this window, it may have already been destroyed"))?;
self.focus_monitor(monitor_idx)?; self.focus_monitor(monitor_idx)?;

View File

@@ -224,6 +224,44 @@ impl WindowManager {
Ok(()) Ok(())
} }
#[tracing::instrument(skip(self))]
pub fn reconcile_monitors(&mut self) -> Result<()> {
let valid_hmonitors = WindowsApi::valid_hmonitors()?;
let mut invalid = vec![];
for monitor in self.monitors_mut() {
if !valid_hmonitors.contains(&monitor.id()) {
let mut mark_as_invalid = true;
// If an invalid hmonitor has at least one window in the window manager state,
// we can attempt to update its hmonitor id in-place so that it doesn't get reaped
if let Some(workspace) = monitor.focused_workspace() {
if let Some(container) = workspace.focused_container() {
if let Some(window) = container.focused_window() {
let actual_hmonitor = WindowsApi::monitor_from_window(window.hwnd());
if actual_hmonitor != monitor.id() {
monitor.set_id(actual_hmonitor);
mark_as_invalid = false;
}
}
}
}
if mark_as_invalid {
invalid.push(monitor.id());
}
}
}
// Remove any invalid monitors from our state
self.monitors_mut().retain(|m| !invalid.contains(&m.id()));
// Check for and add any new monitors that may have been plugged in
WindowsApi::load_monitor_information(&mut self.monitors)?;
Ok(())
}
#[tracing::instrument(skip(self))] #[tracing::instrument(skip(self))]
pub fn enforce_workspace_rules(&mut self) -> Result<()> { pub fn enforce_workspace_rules(&mut self) -> Result<()> {
let mut to_move = vec![]; let mut to_move = vec![];

View File

@@ -86,7 +86,6 @@ use crate::monitor::Monitor;
use crate::ring::Ring; use crate::ring::Ring;
use crate::set_window_position::SetWindowPosition; use crate::set_window_position::SetWindowPosition;
use crate::windows_callbacks; use crate::windows_callbacks;
use crate::workspace::Workspace;
pub enum WindowsResult<T, E> { pub enum WindowsResult<T, E> {
Err(E), Err(E),
@@ -166,6 +165,17 @@ impl WindowsApi {
})) }))
} }
pub fn valid_hmonitors() -> Result<Vec<isize>> {
let mut monitors: Vec<isize> = vec![];
let monitors_ref: &mut Vec<isize> = monitors.as_mut();
Self::enum_display_monitors(
windows_callbacks::valid_display_monitors,
monitors_ref as *mut Vec<isize> as isize,
)?;
Ok(monitors)
}
pub fn load_monitor_information(monitors: &mut Ring<Monitor>) -> Result<()> { pub fn load_monitor_information(monitors: &mut Ring<Monitor>) -> Result<()> {
Self::enum_display_monitors( Self::enum_display_monitors(
windows_callbacks::enum_display_monitor, windows_callbacks::enum_display_monitor,
@@ -181,9 +191,8 @@ impl WindowsApi {
pub fn load_workspace_information(monitors: &mut Ring<Monitor>) -> Result<()> { pub fn load_workspace_information(monitors: &mut Ring<Monitor>) -> Result<()> {
for monitor in monitors.elements_mut() { for monitor in monitors.elements_mut() {
if monitor.workspaces().is_empty() { let monitor_id = monitor.id();
let mut workspace = Workspace::default(); if let Some(workspace) = monitor.workspaces_mut().front_mut() {
// EnumWindows will enumerate through windows on all monitors // EnumWindows will enumerate through windows on all monitors
Self::enum_windows( Self::enum_windows(
windows_callbacks::enum_window, windows_callbacks::enum_window,
@@ -200,7 +209,7 @@ impl WindowsApi {
for container in workspace.containers_mut() { for container in workspace.containers_mut() {
for window in container.windows() { for window in container.windows() {
if Self::monitor_from_window(window.hwnd()) != monitor.id() { if Self::monitor_from_window(window.hwnd()) != monitor_id {
windows_on_other_monitors.push(window.hwnd().0); windows_on_other_monitors.push(window.hwnd().0);
} }
} }
@@ -209,8 +218,6 @@ impl WindowsApi {
for hwnd in windows_on_other_monitors { for hwnd in windows_on_other_monitors {
workspace.remove_window(hwnd)?; workspace.remove_window(hwnd)?;
} }
monitor.workspaces_mut().push_back(workspace);
} }
} }

View File

@@ -16,6 +16,17 @@ use crate::window_manager_event::WindowManagerEvent;
use crate::windows_api::WindowsApi; use crate::windows_api::WindowsApi;
use crate::winevent_listener::WINEVENT_CALLBACK_CHANNEL; use crate::winevent_listener::WINEVENT_CALLBACK_CHANNEL;
pub extern "system" fn valid_display_monitors(
hmonitor: HMONITOR,
_: HDC,
_: *mut RECT,
lparam: LPARAM,
) -> BOOL {
let monitors = unsafe { &mut *(lparam.0 as *mut Vec<isize>) };
monitors.push(hmonitor.0);
true.into()
}
pub extern "system" fn enum_display_monitor( pub extern "system" fn enum_display_monitor(
hmonitor: HMONITOR, hmonitor: HMONITOR,
_: HDC, _: HDC,
@@ -23,6 +34,14 @@ pub extern "system" fn enum_display_monitor(
lparam: LPARAM, lparam: LPARAM,
) -> BOOL { ) -> BOOL {
let monitors = unsafe { &mut *(lparam.0 as *mut Ring<Monitor>) }; let monitors = unsafe { &mut *(lparam.0 as *mut Ring<Monitor>) };
// Don't duplicate a monitor that is already being managed
for monitor in monitors.elements() {
if monitor.id() == hmonitor.0 {
return true.into();
}
}
if let Ok(m) = WindowsApi::monitor(hmonitor) { if let Ok(m) = WindowsApi::monitor(hmonitor) {
monitors.elements_mut().push_back(m); monitors.elements_mut().push_back(m);
} }

View File

@@ -527,11 +527,10 @@ fn main() -> Result<()> {
None None
}; };
let script = if let Some(exec) = exec { let script = exec.map_or_else(
format!("Start-Process '{}' -WindowStyle hidden", exec) || String::from("Start-Process komorebi -WindowStyle hidden"),
} else { |exec| format!("Start-Process '{}' -WindowStyle hidden", exec),
String::from("Start-Process komorebi -WindowStyle hidden") );
};
match powershell_script::run(&script, true) { match powershell_script::run(&script, true) {
Ok(output) => { Ok(output) => {