From f5a299aa9ef035e7f38b18d2e83d219dbfef426c Mon Sep 17 00:00:00 2001 From: LGUG2Z Date: Sat, 31 Oct 2026 10:11:57 -0700 Subject: [PATCH] refactor(windows): minimize, close and raise go through backend() process_command (close and minimize of the foreground window, four raise calls) and monitor_reconciliator (four minimize calls) now call backend().close/minimize/raise. WindowsBackend runs Window::close, minimize and raise unchanged (minimize still leaves komorebi-bar alone). docs/dev/backend-switch.md now holds the full table of what is switched and what is left, including why the queries (window_rect, foreground_window, cursor_pos, monitors) were NOT switched: each would drop an error that callers use or report, or fabricate a default. BEHAVIOUR: a dead window is skipped by an IsWindow check first; minimize results are ignored as before. e2e gate needed: commands, monitor reconciliation (monitor_reconciliator), lifecycle. Verified: Windows clippy -D warnings via tools/fast-check PASS; e2e not run. --- docs/dev/backend-switch.md | 24 +++++++++++++---------- komorebi/src/monitor_reconciliator/mod.rs | 14 +++++++++---- komorebi/src/process_command.rs | 12 ++++++------ 3 files changed, 30 insertions(+), 20 deletions(-) diff --git a/docs/dev/backend-switch.md b/docs/dev/backend-switch.md index 1bf88630..b60b26d2 100644 --- a/docs/dev/backend-switch.md +++ b/docs/dev/backend-switch.md @@ -2,16 +2,20 @@ Step 8 of the unified-binaries plan. `komorebi::windows_backend::backend()` returns the shared `komorebi_backend::Backend` (a stateless `WindowsBackend` over `WindowsApi` and `Window`). The daemon -switches one cluster at a time; each switch is its own revision marked BEHAVIOUR. +is switched one cluster at a time; each switch is its own revision marked BEHAVIOUR. -What every switch changes, for the Windows daemon: `WindowsBackend` first asks `IsWindow` and fails -with `Error::WindowGone` for a dead window, and a failure of a window that died meanwhile is reported -as `WindowGone` (the message text changes: it was the Win32 error). Call sites use `?` or ignore the -result, none inspects the error. +What every switched call changes in the Windows daemon: `WindowsBackend` first asks `IsWindow` and +fails with `Error::WindowGone` for a dead window, and a failure of a window that died meanwhile is +reported as `WindowGone` (the error text changes from the Win32 one). Call sites use `?` or ignore the +result; none inspects the error. The operations return `Result` where `Window::hide`, `restore` and +`minimize` returned `()`: those sites ignore it (`let _ =`), as the old code could not fail. -| Cluster | Operation | Status | Sites | Left on `Window`/`WindowsApi`, and why | e2e needed | +| Cluster | Operation | Status | Sites | Left on `Window` / `WindowsApi`, and why | e2e needed | |---|---|---|---|---|---| -| focus | `focus(id, mouse_follows)` | switched | process_event (4), process_command (3), workspace (7), window_manager (24), focus_manager, stackbar | `animation/transition.rs:534` calls `Window::focus` from inside the slide-in code that `Window::focus` itself defers to (it would recurse through the backend for no gain) | commands, focus, focus_follows_mouse, stacking, workspaces, lifecycle | -| frame / position | `set_rect`, `window_rect` | not switched | | `Window::set_position` has the `top` flag, animation/slide-in/live-resize guards that the backend `set_rect` (always `top = false`) cannot carry | | -| hide / show | `hide`, `restore`, `minimize`, `unminimize`, `raise`, `close` | not switched | | | | -| queries | `window`, `foreground_window`, `cursor_pos`, `monitors`, `monitor_from_point` | not switched | | | | +| focus | `focus(id, mouse_follows)` | switched | process_event 4, process_command 3, workspace 7, window_manager 24, focus_manager 1, stackbar 1 | `animation/transition.rs:534` calls `Window::focus` from inside the slide-in code that `Window::focus` itself defers to | commands, focus, focus_follows_mouse, stacking, workspaces, lifecycle | +| frame | `set_rect(id, rect)` | switched for `top = false` | workspace.rs (layout update), stackbar tab click | `set_position(.., true)` callers (window.rs `move_to_area`/`center`, workspace maximize area): the shared `set_rect` has no `top` flag | commands, resize, layouts, stacking, scrolling, workspaces | +| frame | `window_rect(id)` | NOT switched | 0 of about 60 | the shared `window_rect` is built on `window(id)`, which asks title, class, exe and path of the window, gives `None` for a failure and the call sites rely on `Err` or `unwrap_or_default`. Needs a cheap frame query on the trait (a `frame(id) -> Result`) or an override of the provided method with an error | | +| hide / show | `hide`, `restore` | switched for the plain forms | container, workspace, monitor, process_command, process_event, window_manager (23) | `hide_with_border` and `restore_with_border` (no border flag in the trait); `animation/transition.rs` and `ghost_dispatcher.rs` (inside the slide-in machinery); the `Container`/`Workspace` methods stay, they call these | commands, workspaces, stacking, lifecycle_hidden | +| hide / show | `minimize`, `close`, `raise` | switched | process_command (close 1, minimize 1, raise 4), monitor_reconciliator (minimize 4) | | commands, monitor reconciliation, lifecycle | +| hide / show | `unminimize`, `maximize` | not switched | | `unminimize` has no daemon caller (the daemon uses `restore`); `maximize`/`unmaximize` need the `Capability::Maximize` check and keep their own bookkeeping in workspace.rs | | +| queries | `foreground_window`, `cursor_pos`, `monitors`, `monitor_from_point`, `window` | NOT switched | 0 | `WindowsApi::foreground_window` is `Result` (null handle is an `Err` with a message the callers pass on), the trait gives `Option`; `cursor_pos` is `Result` vs `Option<(i32, i32)>`; `monitors` rebuilds `MonitorInfo` (drops the `Monitor` fields the daemon needs); `monitor_from_point` is never `None` on Windows. Each switch would drop an error the caller reports or fabricate a default | | diff --git a/komorebi/src/monitor_reconciliator/mod.rs b/komorebi/src/monitor_reconciliator/mod.rs index bb00eacb..582b0b98 100644 --- a/komorebi/src/monitor_reconciliator/mod.rs +++ b/komorebi/src/monitor_reconciliator/mod.rs @@ -15,9 +15,11 @@ use crate::monitor::Monitor; use crate::monitor_reconciliator::hidden::Hidden; use crate::notify_subscribers; use crate::state::State; +use crate::windows_backend::backend; use crossbeam_channel::Receiver; use crossbeam_channel::Sender; use crossbeam_utils::atomic::AtomicConsume; +use komorebi_backend::WindowId; use parking_lot::Mutex; use serde::Deserialize; use serde::Serialize; @@ -593,7 +595,8 @@ where // Minimize the focused window since Windows might try // to move it to another monitor if it was focused. if window.is_focused() { - window.minimize(); + let _ = backend() + .minimize(WindowId::from_hwnd(window.hwnd)); } } } @@ -604,7 +607,8 @@ where // Minimize the focused window since Windows might try // to move it to another monitor if it was focused. if maximized.is_focused() { - maximized.minimize(); + let _ = + backend().minimize(WindowId::from_hwnd(maximized.hwnd)); } } @@ -616,7 +620,8 @@ where // Minimize the focused window since Windows might try // to move it to another monitor if it was focused. if window.is_focused() { - window.minimize(); + let _ = backend() + .minimize(WindowId::from_hwnd(window.hwnd)); } } } @@ -626,7 +631,8 @@ where // Minimize the focused window since Windows might try // to move it to another monitor if it was focused. if window.is_focused() { - window.minimize(); + let _ = + backend().minimize(WindowId::from_hwnd(window.hwnd)); } } } diff --git a/komorebi/src/process_command.rs b/komorebi/src/process_command.rs index e81d69c3..b123aadb 100644 --- a/komorebi/src/process_command.rs +++ b/komorebi/src/process_command.rs @@ -406,10 +406,10 @@ impl WindowManager { WindowsApi::left_click(); } SocketMessage::Close => { - Window::from(WindowsApi::foreground_window()?).close()?; + backend().close(WindowId::from_hwnd(WindowsApi::foreground_window()?))?; } SocketMessage::Minimize => { - Window::from(WindowsApi::foreground_window()?).minimize(); + let _ = backend().minimize(WindowId::from_hwnd(WindowsApi::foreground_window()?)); } SocketMessage::LockMonitorWorkspaceContainer( monitor_idx, @@ -1309,7 +1309,7 @@ impl WindowManager { to_focus = Some(*window); } else { let _ = backend().restore(WindowId::from_hwnd(window.hwnd)); - window.raise()?; + backend().raise(WindowId::from_hwnd(window.hwnd))?; } } @@ -1317,7 +1317,7 @@ impl WindowManager { // The focused window should be the last one raised to make sure it is // on top let _ = backend().restore(WindowId::from_hwnd(focused_window.hwnd)); - focused_window.raise()?; + backend().raise(WindowId::from_hwnd(focused_window.hwnd))?; } for container in workspace.containers() { @@ -1338,7 +1338,7 @@ impl WindowManager { if let Some(monocle) = &workspace.monocle_container { if let Some(window) = monocle.focused_window() { to_focus = Some(*window); - window.raise()?; + backend().raise(WindowId::from_hwnd(window.hwnd))?; } for window in workspace.floating_windows() { let _ = backend().hide(WindowId::from_hwnd(window.hwnd)); @@ -1351,7 +1351,7 @@ impl WindowManager { if i == focused_container_idx { to_focus = Some(*window); } - window.raise()?; + backend().raise(WindowId::from_hwnd(window.hwnd))?; } }