mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-03-20 00:24:10 +01:00
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:
@@ -2,7 +2,7 @@ use serde::Serialize;
|
||||
|
||||
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 left: i32,
|
||||
pub top: i32,
|
||||
@@ -10,17 +10,6 @@ pub struct Rect {
|
||||
pub bottom: i32,
|
||||
}
|
||||
|
||||
impl Default for Rect {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RECT> for Rect {
|
||||
fn from(rect: RECT) -> Self {
|
||||
Self {
|
||||
|
||||
@@ -6,6 +6,7 @@ use color_eyre::Result;
|
||||
use getset::CopyGetters;
|
||||
use getset::Getters;
|
||||
use getset::MutGetters;
|
||||
use getset::Setters;
|
||||
use serde::Serialize;
|
||||
|
||||
use komorebi_core::Rect;
|
||||
@@ -14,9 +15,9 @@ use crate::container::Container;
|
||||
use crate::ring::Ring;
|
||||
use crate::workspace::Workspace;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Getters, CopyGetters, MutGetters)]
|
||||
#[derive(Debug, Clone, Serialize, Getters, CopyGetters, MutGetters, Setters)]
|
||||
pub struct Monitor {
|
||||
#[getset(get_copy = "pub")]
|
||||
#[getset(get_copy = "pub", set = "pub")]
|
||||
id: isize,
|
||||
monitor_size: Rect,
|
||||
#[getset(get = "pub")]
|
||||
@@ -30,11 +31,14 @@ pub struct Monitor {
|
||||
impl_ring_elements!(Monitor, Workspace);
|
||||
|
||||
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 {
|
||||
id,
|
||||
monitor_size,
|
||||
work_area_size,
|
||||
workspaces: Ring::default(),
|
||||
workspaces,
|
||||
workspace_names: HashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,8 +55,9 @@ impl WindowManager {
|
||||
WindowManagerEvent::FocusChange(_, window)
|
||||
| WindowManagerEvent::Show(_, window)
|
||||
| WindowManagerEvent::MoveResizeEnd(_, window) => {
|
||||
let monitor_idx = self
|
||||
.monitor_idx_from_window(*window)
|
||||
self.reconcile_monitors()?;
|
||||
|
||||
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"))?;
|
||||
|
||||
self.focus_monitor(monitor_idx)?;
|
||||
|
||||
@@ -224,6 +224,44 @@ impl WindowManager {
|
||||
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))]
|
||||
pub fn enforce_workspace_rules(&mut self) -> Result<()> {
|
||||
let mut to_move = vec![];
|
||||
|
||||
@@ -86,7 +86,6 @@ use crate::monitor::Monitor;
|
||||
use crate::ring::Ring;
|
||||
use crate::set_window_position::SetWindowPosition;
|
||||
use crate::windows_callbacks;
|
||||
use crate::workspace::Workspace;
|
||||
|
||||
pub enum WindowsResult<T, 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<()> {
|
||||
Self::enum_display_monitors(
|
||||
windows_callbacks::enum_display_monitor,
|
||||
@@ -181,9 +191,8 @@ impl WindowsApi {
|
||||
|
||||
pub fn load_workspace_information(monitors: &mut Ring<Monitor>) -> Result<()> {
|
||||
for monitor in monitors.elements_mut() {
|
||||
if monitor.workspaces().is_empty() {
|
||||
let mut workspace = Workspace::default();
|
||||
|
||||
let monitor_id = monitor.id();
|
||||
if let Some(workspace) = monitor.workspaces_mut().front_mut() {
|
||||
// EnumWindows will enumerate through windows on all monitors
|
||||
Self::enum_windows(
|
||||
windows_callbacks::enum_window,
|
||||
@@ -200,7 +209,7 @@ impl WindowsApi {
|
||||
|
||||
for container in workspace.containers_mut() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -209,8 +218,6 @@ impl WindowsApi {
|
||||
for hwnd in windows_on_other_monitors {
|
||||
workspace.remove_window(hwnd)?;
|
||||
}
|
||||
|
||||
monitor.workspaces_mut().push_back(workspace);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,17 @@ use crate::window_manager_event::WindowManagerEvent;
|
||||
use crate::windows_api::WindowsApi;
|
||||
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(
|
||||
hmonitor: HMONITOR,
|
||||
_: HDC,
|
||||
@@ -23,6 +34,14 @@ pub extern "system" fn enum_display_monitor(
|
||||
lparam: LPARAM,
|
||||
) -> BOOL {
|
||||
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) {
|
||||
monitors.elements_mut().push_back(m);
|
||||
}
|
||||
|
||||
@@ -527,11 +527,10 @@ fn main() -> Result<()> {
|
||||
None
|
||||
};
|
||||
|
||||
let script = if let Some(exec) = exec {
|
||||
format!("Start-Process '{}' -WindowStyle hidden", exec)
|
||||
} else {
|
||||
String::from("Start-Process komorebi -WindowStyle hidden")
|
||||
};
|
||||
let script = exec.map_or_else(
|
||||
|| String::from("Start-Process komorebi -WindowStyle hidden"),
|
||||
|exec| format!("Start-Process '{}' -WindowStyle hidden", exec),
|
||||
);
|
||||
|
||||
match powershell_script::run(&script, true) {
|
||||
Ok(output) => {
|
||||
|
||||
Reference in New Issue
Block a user