# komorebi Architecture Digest > Behavioral study of [komorebi](https://github.com/LGUG2Z/komorebi) (source-available under the > Komorebi License 2.0.0), produced 2026-08-10 by a multi-agent code-reading pass over the komorebi > daemon, CLI, and layout crates. It describes observed behavior, architecture, and Windows API usage > so that an independent implementation can be written: there is no komorebi code here — no source > listings, algorithm bodies, data-structure definitions, or pseudocode — and none is used in YTile. > > **Scope of this file.** It does contain short quoted fragments of komorebi's own comments, log > strings, and TODO text, cited by file and line, where the exact wording is the evidence for a claim. > Those fragments remain the work of komorebi's authors under the Komorebi License 2.0.0; they are > reproduced here as brief attributed citations, and **YTile's MIT licence does not extend to them**. > Everything else in this document — the analysis, structure, and wording — is YTile's own and is > MIT-licensed with the rest of the repository. If you are reusing this file, that split is the line > to respect. ## Event capture and processing (WinEvent hook -> channel -> WindowManager::process_event) in komorebi Capture side: `winevent_listener::start()` (called early in `main()`) spawns a dedicated thread that installs a single global out-of-context accessibility hook via `SetWinEventHook(EVENT_MIN, EVENT_MAX, None, win_event_hook, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS)` — i.e. it subscribes to the entire WinEvent range for all processes/threads, excluding komorebi's own windows (borders, etc.) to avoid feedback loops. Because WINEVENT_OUTOFCONTEXT hooks are delivered as messages to the installing thread, that same thread must run a Win32 message pump; it immediately enters a `GetMessageW`/`TranslateMessage`/`DispatchMessageW` loop (with an extra, technically unnecessary 10ms sleep per iteration). Without this pump the callback would never fire. The callback `windows_callbacks::win_event_hook` does coarse filtering: it drops anything where `id_object != OBJID_WINDOW` (child/UI-element events), converts the raw u32 to the `WinEvent` enum via `TryFrom` (unknown values dropped), does two border-manager fast paths (forwards ObjectLocationChange/ObjectDestroy directly to the window's border HWND via `SendNotifyMessageW` so borders track moves with low latency, and re-pokes the border manager on SystemForeground because GetForegroundWindow can lag the event), then maps the WinEvent to a semantic `WindowManagerEvent` via `WindowManagerEvent::from_win_event`. Unmapped WinEvents are trace-logged and dropped. The mapping is small: ObjectDestroy->Destroy; ObjectHide->Hide; ObjectCloaked/Uncloaked->Cloak/Uncloak; SystemMinimizeStart->Minimize; ObjectShow|SystemMinimizeEnd->Show; ObjectFocus|SystemForeground->FocusChange; SystemMoveSizeStart/End->MoveResizeStart/End; SystemCaptureStart/End->MouseCapture; ObjectNameChange->Show-or-TitleUpdate (a per-app hack, see gotchas). Mapped events are pushed into a global `OnceLock`-held crossbeam bounded(20) channel (`winevent_listener::event_tx()`); a full channel makes the hook callback block, and a closed one panics via `expect`. Processing side: the receiver half of the same channel is handed to `WindowManager::new` as `wm.incoming_events`. `process_event::listen_for_events` spawns a consumer thread that loops `receiver.recv()`, takes the global `Arc>` lock, and calls `wm.process_event(event)`, logging errors and continuing. This serializes all window-management mutations through one mutex that is shared with the command socket thread, border/stackbar/transparency/reaper/monitor-reconciliator threads. The same channel is also fed by the WM itself: `komorebic manage/unmanage` inject `Manage`/`Unmanage` events, and the custom focus-follows-mouse thread (`process_movement.rs`, a winput mouse hook loop) injects `Raise` events, so synthetic and OS-originated events flow through one queue. `process_event` first drops everything while paused, then runs the fine-grained filter `Window::should_manage` (window.rs): must be a real window (`IsWindow`), meet minimum width/height, have a title, pass the cloak check (`DwmGetWindowAttribute` DWMWA_CLOAKED; cloaked windows are only allowed for Hide/Cloak events so cloaked-window removals still process), and pass `window_is_eligible`: not in PERMAIGNORE_CLASSES or user ignore rules (unless force-managed), and style-wise have WS_CAPTION+WS_EX_WINDOWEDGE, not WS_EX_DLGMODALFRAME, and not WS_EX_LAYERED unless whitelisted (layered windows generate duplicate events that cause redraw storms). Then it optionally gates on virtual-desktop identity, updates the focused monitor from FocusChange/Show/MoveResizeEnd coordinates, enforces workspace rules, and dispatches per event type: Destroy/Unmanage -> remove window from focused workspace, retile, refocus only if the OS promoted no foreground successor; Minimize -> remove+retile unless komorebi itself hid the window (HIDDEN_HWNDS) or a display-change is in progress; Hide -> remove only if the window is truly gone or the app is on the tray/multi-window list (many apps send Hide when minimizing or closing to tray); FocusChange -> drop if stale (window is no longer `GetForegroundWindow`), then sync focused container/floating layer; Show/Manage/Uncloak -> alt-tab reconciliation if the hwnd is already known on another workspace/stack, dedup checks against `known_hwnds` and all workspaces to prevent ghost tiles, then insert as floating or into a new/appended container and retile; MoveResizeStart -> record pending (monitor, workspace, hwnd) op; MoveResizeEnd -> compare new rect vs the layout's old rect — zero right/bottom delta means a drag (swap containers at cursor, or transfer across monitors), otherwise a resize translated into layout resize ops; MouseCapture/Cloak/TitleUpdate -> no-op (rules/reaping only). Finally it updates `known_hwnds`, notifies IPC subscribers, and pokes the border/transparency/stackbar managers. There is no time-based debouncing anywhere: "deduplication" is entirely state-based (stale-foreground check, known_hwnds/contains_window membership checks, HIDDEN_HWNDS self-inflicted-event suppression, pending_move_op hwnd matching, uncloack_to_ignore counter), plus the coarse OBJID_WINDOW/style filters that discard the vast majority of the firehose before it reaches the channel. A separate 20ms polling "reaper" thread backstops missed Destroy events by removing hwnds that no longer pass `IsWindow`/`IsWindowVisible`. Windows APIs: SetWinEventHook (Accessibility) — single global hook over EVENT_MIN..EVENT_MAX with WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS; the sole source of window lifecycle/focus/move notifications; GetMessageW / TranslateMessage / DispatchMessageW — message pump on the hook-installing thread, required for WINEVENT_OUTOFCONTEXT hook delivery; SendNotifyMessageW — hook callback forwards ObjectLocationChange/ObjectDestroy raw events straight to the tracked window's border HWND (border follows window without a round-trip through the WM); GetWindowLongW (GWL_STYLE / GWL_EXSTYLE) — filtering WS_CHILD, WS_EX_TOOLWINDOW, WS_EX_NOACTIVATE in the callback; full style/exstyle eligibility rules (WS_CAPTION, WS_EX_WINDOWEDGE, WS_EX_DLGMODALFRAME, WS_EX_LAYERED) in window_is_eligible; GetForegroundWindow (via WindowsApi::foreground_window) — stale FocusChange rejection and post-Destroy refocus decision; IsWindow / IsWindowVisible / IsIconic / IsZoomed — validity checks in enum callbacks, should_manage, and the reaper's orphan detection; EnumWindows (via enum_window / alt_tab_windows extern callbacks) — initial workspace population and alt-tab candidate listing; GetWindowRect (via WindowsApi::window_rect) — minimum-size filtering and MoveResizeEnd move-vs-resize delta computation; DwmGetWindowAttribute DWMWA_CLOAKED (via window.is_cloaked) — excluding cloaked (e.g. other-virtual-desktop / UWP suspended) windows from management except on Hide/Cloak events; AllowSetForegroundWindow + SystemParametersInfo foreground lock timeout (WindowsApi::foreground_lock_timeout) — startup permission to steal focus later; SetProcessDpiAwarenessContext — DPI awareness so window rects are in physical pixels; OBJID_WINDOW constant — callback discards accessibility events for non-window objects (menus, carets, child UI elements); winput low-level mouse message loop (process_movement) — synthesizes Raise events for the custom focus-follows-mouse implementation Mechanisms: Dedicated hook thread: winevent_listener::start() spawns one thread that both installs the hook and pumps messages; hook handle/thread stored in a OnceLock so it is started exactly once; Global static crossbeam bounded(20) channel in a OnceLock; event_tx()/event_rx() clone the halves. Producer = hook callback (plus WM-synthesized Manage/Unmanage/Raise events); consumer = single event-processing thread; Two-stage filtering: cheap coarse filter in the hook callback (OBJID_WINDOW, WinEvent enum TryFrom, from_win_event mapping returning None for ~90% of event types) keeps the channel small; expensive rule-based should_manage/window_is_eligible filtering runs on the consumer side under the WM lock; Typed event translation: raw u32 WinEvent -> WinEvent enum (winevent.rs, #[repr(u32)] with exhaustive TryFrom) -> semantic WindowManagerEvent enum carrying the source WinEvent plus a Copy Window{hwnd} struct; Single-mutex actor model: Arc> shared by the event thread, command socket thread, and all manager threads (border, stackbar, transparency, reaper, monitor reconciliator, focus manager); process_event holds the lock for the whole handling of one event, so events are strictly serialized; State-based dedup instead of debouncing: known_hwnds map (hwnd -> (monitor, workspace)), workspace contains_window checks, HIDDEN_HWNDS set for self-initiated hides, pending_move_op tuple for pairing MoveResizeStart/End, has_pending_raise_op flag, uncloack_to_ignore counter, and a stale-focus check comparing event hwnd against GetForegroundWindow; Alt-tab reconciliation: on Show/Uncloak of an hwnd already tracked on another monitor/workspace (or unfocused in a stack), focus is re-pointed to that workspace/container instead of re-adding the window; Reaper backstop: a 20ms polling thread diffs known hwnds against IsWindow/IsWindowVisible and pushes orphan batches over its own channel for removal — covers Destroy/Hide events that never arrive; Border fast path: raw event forwarding from the hook callback to border windows via SendNotifyMessageW, bypassing the WM channel entirely for move-tracking latency; Post-event fan-out: after each processed event the WM notifies IPC subscribers (serialized state diff) and pokes border/transparency/stackbar manager channels via non-blocking try_send (dropped when full) Gotchas: Firefox (and similar) never send ObjectCreate/ObjectShow on launch — only ObjectNameChange + ObjectLocationChange. from_win_event therefore promotes ObjectNameChange to a Show event for exes on the OBJECT_NAME_CHANGE_ON_LAUNCH list, with a title-regex ignore list on top, and skips minimized windows (Firefox fires NameChange when YouTube autoplay changes the title, issue #941); Many apps send ObjectHide when minimizing or 'closing to tray' while the process stays alive and IsWindow stays true — Hide only removes the window if !is_window() OR the app matches TRAY_AND_MULTI_WINDOW_IDENTIFIERS, and never if komorebi itself hid it (HIDDEN_HWNDS); SystemForeground can fire before GetForegroundWindow reflects the new foreground window, so border focus colors were painted stale — the callback fires an extra border_manager notification for SystemForeground to repaint once the OS catches up; FocusChange events can be stale by the time they are processed — should_skip_focus_change drops them if the event hwnd is no longer the actual foreground window; After destroying a managed window, Windows sometimes promotes no foreground successor (foreground is null) — should_refocus_after_destroy refocuses the surviving selection only in that case, otherwise it defers to the incoming FocusChange to avoid fighting the OS; OleMainThreadWndClass (explorer.exe's hidden OLEChannelWnd COM support window) is always tied to the primary monitor; if it triggers monitor reconciliation the focused monitor index gets stuck at 0 — its class is explicitly excluded from focused-monitor updates; Firefox fires an extra Show when focused during a workspace switch, associating it with both workspaces and creating ghost tiles — process_event checks known_hwnds and every workspace before adding, and hides the window if it belongs to a non-focused workspace elsewhere; Windows fires SystemMinimizeStart for windows on a monitor that briefly disconnects/reconnects — minimizes are ignored while a display change is in progress (10s window) so the monitor reconciliator can restore the layout; WS_EX_LAYERED windows are excluded because they generate duplicate events that send redrawing haywire on FocusChange — but a layered whitelist exists because apps like Steam legitimately need it; WINWORD.EXE keeps an invisible WS_EX_LAYERED window alive after closing one of several docs — the reaper treats non-visible windows as orphans as a hack (disabled under HidingBehaviour::Hide since legitimately hidden windows would be yeeted); the source comment next to it vents at the Office 365 team in memorable terms; Some windows send FocusChange before Show; on an empty workspace komorebi focuses the desktop on the FocusChange, so after adding the first window of a workspace it explicitly re-focuses that window; SLOW_APPLICATION_IDENTIFIERS: some apps report styles/state late, so eligibility checking sleeps a configurable compensation time before evaluating them; JetBrains IDEs spam OBJECT_NAMECHANGE — processed Show(ObjectNameChange) events are logged at trace instead of info to keep logs usable; MoveResizeEnd distinguishes a drag from a resize purely by rect deltas (right/bottom delta == 0 means move); a leftover BORDER_WIDTH+BORDER_OFFSET constant is consulted for resize edge detection with a 'TODO: Determine if this is still needed'; The hook callback blocks on a bounded(20) channel send and panics (expect) if the channel is closed; the message pump loop also sleeps 10ms per message even though GetMessageW already blocks; should_manage only allows cloaked windows through for Hide/Cloak events, so windows on other virtual desktops still get removed but never get managed; Hide events are also always allowed through the layered-transparency gate to avoid ghost tiles; WINEVENT_SKIPOWNPROCESS is load-bearing: without it komorebi's own border/stackbar windows would generate events and feedback loops; MouseCapture (SystemCaptureStart/End) events are consumed only to trigger workspace-rule enforcement and are otherwise explicitly no-ops Minimal-WM essentials: Essential: (1) a dedicated thread that calls SetWinEventHook with WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS and then runs a GetMessageW message pump — without the pump the callback never fires; hooking the full EVENT_MIN..EVENT_MAX range is not required, a minimal WM can register only the handful it consumes (OBJECT_SHOW/HIDE/DESTROY/CLOAKED/UNCLOAKED/FOCUS, SYSTEM_FOREGROUND, SYSTEM_MINIMIZESTART/END, SYSTEM_MOVESIZESTART/END) or install a few narrow hooks; (2) the OBJID_WINDOW filter in the callback — non-window accessibility events dominate the firehose; (3) a channel handing events from the hook thread to a single WM thread that serializes all state mutation (never do layout work inside the hook callback: it runs on the message pump thread and blocking it delays all further events); (4) an eligibility filter equivalent to should_manage's core: IsWindow + visible + has title + not cloaked + style check (WS_CAPTION/WS_EX_WINDOWEDGE present, WS_EX_TOOLWINDOW/WS_EX_NOACTIVATE/WS_CHILD/WS_EX_DLGMODALFRAME absent, layered excluded) plus minimum size — without this you will try to tile tooltips, IME windows, and shell internals; (5) handlers for Show (insert + retile), Destroy/Hide/Minimize (remove + retile), FocusChange (update focused tile, with the stale-foreground check to avoid focus loops), and MoveResizeStart/End (drag-to-swap or ignore); (6) membership dedup against a known-hwnds map, since Windows delivers overlapping Show/Foreground/NameChange bursts for one logical appearance; (7) an initial EnumWindows pass to adopt existing windows. Strongly recommended though not strictly minimal: the ObjectNameChange-as-Show hack (without it Firefox-class apps are simply never tiled) and a reaper-style liveness poll (without it missed Destroy events leave permanent ghost tiles). Optional polish: border/stackbar/transparency managers and their fast-path event forwarding, alt-tab stack reconciliation, virtual-desktop gating, monitor reconciliation and display-change minimize suppression, focus-follows-mouse Raise synthesis, tray/multi-window and slow-application app-specific lists, IPC subscriber notifications, and state dump/restore. ## Windows API surface (windows_api.rs, set_window_position.rs, com/, window.rs, plus winevent/border/monitor/ghost helpers) komorebi funnels nearly all OS interaction through a single stateless facade, `WindowsApi` in komorebi/src/windows_api.rs, which wraps raw `windows`-crate calls in eyre Results (macros convert 0/NULL returns into `last_os_error`). The core tiling loop is: a WinEvent hook (SetWinEventHook, EVENT_MIN..EVENT_MAX, WINEVENT_OUTOFCONTEXT|WINEVENT_SKIPOWNPROCESS, on a dedicated thread with a GetMessageW pump) turns shell events (show/hide/cloak/destroy/foreground/minimize/movesize) into channel messages; window eligibility is decided by inspecting HWND properties (GetWindowTextW, RealGetWindowClassW, GWL_STYLE/GWL_EXSTYLE via GetWindowLongPtrW, DwmGetWindowAttribute DWMWA_CLOAKED, plus exe name via OpenProcess/QueryFullProcessImageNameW); and layout is applied with SetWindowPos (flags modeled as a bitflags type in set_window_position.rs) after correcting every rect by a "shadow rect" — the delta between GetWindowRect (outer bounds incl. invisible DWM drop shadow) and DWMWA_EXTENDED_FRAME_BOUNDS (painted region) — so tiles visually touch. Focus changes use the classic foreground-lock bypass: SendInput of a dummy mouse event to the WM's own process, SetWindowPos(HWND_TOP), then SetForegroundWindow; startup additionally calls AllowSetForegroundWindow and zeroes SPI_SETFOREGROUNDLOCKTIMEOUT. Monitors come from two sources: plain Win32 (EnumDisplayMonitors, MonitorFromWindow/MonitorFromPoint with MONITOR_DEFAULTTONEAREST, GetMonitorInfoW for work areas, GetDpiForMonitor MDT_EFFECTIVE_DPI) and the external `win32-display-data` crate (LGUG2Z's wrapper over the DisplayConfig APIs) which supplies stable device paths and serial-number IDs for monitor identity across hotplug. A hidden message-only-style window (monitor_reconciliator/hidden.rs) subscribes to WM_DEVICECHANGE (RegisterDeviceNotificationW for monitor/display-adapter/video-output interface GUIDs), WM_DISPLAYCHANGE, WM_SETTINGCHANGE(SPI_SETWORKAREA), WM_POWERBROADCAST (lid switch via RegisterPowerSettingNotification), and WM_WTSSESSION_CHANGE (WTSRegisterSessionNotification) to drive monitor reconciliation. The process declares DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 at startup. COM usage is small but notable. Documented: IDesktopWallpaper (CoCreateInstance of DesktopWallpaper) for per-monitor wallpaper. Undocumented: com/interfaces.rs hand-transcribes (from Ciantic/AltTabAccessor) the internal shell interfaces — CLSID_ImmersiveShell → IServiceProvider::QueryService → IApplicationViewCollection (IID 1841c6d7-4f9d-42c0-af41-8747538f10e5) → get_view_for_hwnd → IApplicationView (IID 372E1D3B-38D3-42E4-A15B-8AB2B178F513), whose vtable includes dozens of methods (many literally named unknown1..12) just to reach `SetCloak`. This is the only way to cloak *foreign* windows (used for workspace switching with HidingBehaviour::Cloak and for hiding a window behind a DWM-thumbnail "ghost" during movement animations), because the documented DwmSetWindowAttribute(DWMWA_CLOAK) returns E_ACCESSDENIED for HWNDs you don't own. Notably absent from this snapshot: IVirtualDesktopManager, AttachThreadInput, and any direct SetWindowsHookEx (the optional "komorebi" focus-follows-mouse implementation gets mouse movement through the `winput` crate's message loop, which installs low-level hooks internally). Visual chrome is separate machinery: border windows are WS_POPUP + WS_EX_TOOLWINDOW|WS_EX_TOPMOST|WS_EX_NOACTIVATE windows rendered with Direct2D (ID2D1Factory → CreateHwndRenderTarget → DrawRoundedRectangle) on their own message-loop threads, kept in sync with tracked windows by forwarding EVENT_OBJECT_LOCATIONCHANGE/DESTROY from the WinEvent callback via SendNotifyMessageW; stackbars use classic GDI (DrawTextW, RoundRect, fonts); movement animations use DWM live thumbnails (DwmRegisterThumbnail/DwmUpdateThumbnailProperties/DwmFlush) hosted in a click-through WS_EX_TRANSPARENT ghost window while the real window is cloaked. Windows APIs: EnumWindows + WNDENUMPROC (Win32) — initial workspace population, alt-tab candidate list, finding stray 'komoborder' windows (windows_api.rs, windows_callbacks.rs); SetWindowPos (Win32) — THE tiling primitive; flags wrapped as bitflags in set_window_position.rs; positioning uses NO_ACTIVATE|NO_SEND_CHANGING|NO_COPY_BITS|FRAME_CHANGED, optionally SWP_ASYNCWINDOWPOS; z-order raise/lower via HWND_TOP/HWND_BOTTOM; MoveWindow (Win32) — legacy animation frame path; comment warns it cannot be async and may hang on unresponsive apps; ShowWindow / ShowWindowAsync (Win32) — SW_HIDE, SW_MINIMIZE, SW_MAXIMIZE, SW_NORMAL, SW_SHOWNOACTIVATE for hide/restore/minimize/maximize; async variant selected by WINDOW_HANDLING_BEHAVIOUR; BringWindowToTop, GetTopWindow, GetWindow(GW_HWNDNEXT), GetDesktopWindow (Win32) — z-order walking; GetForegroundWindow / SetForegroundWindow (Win32) — focus tracking and focus setting; SendInput (Win32) — dummy INPUT_MOUSE event to own process to defeat the foreground lock before SetForegroundWindow; also synthetic left click helper; AllowSetForegroundWindow (Win32) — called at startup (5 retries) so the daemon can hand off foreground rights; SystemParametersInfoW (Win32) — SPI_GET/SETFOREGROUNDLOCKTIMEOUT (zeroed at startup to enable keyboard-driven focus), SPI_GET/SETACTIVEWINDOWTRACKING (native focus-follows-mouse); GetWindowRect (Win32) — outer bounds incl. invisible drop shadow; used with extended frame bounds to compute the shadow offset; GetWindowTextW, RealGetWindowClassW, GetWindowThreadProcessId (Win32) — title/class/pid for window rules; GetWindowLongPtrW / SetWindowLongPtrW GWL_STYLE, GWL_EXSTYLE (Win32) — style-based eligibility (CAPTION+WINDOWEDGE required, DLGMODALFRAME/LAYERED excluded), toggling WS_EX_LAYERED for transparency, removing CAPTION|THICKFRAME for no-titlebar mode; GWLP_USERDATA carries the Rust Border pointer; IsWindow, IsWindowVisible, IsIconic, IsZoomed (Win32) — liveness/visibility/minimized/maximized checks; SetLayeredWindowAttributes / GetLayeredWindowAttributes LWA_ALPHA (Win32) — per-window transparency; LWA_COLORKEY in stackbar; PostMessageW WM_CLOSE (Win32) — closing managed windows; also custom WM_USER messages to border threads; SendNotifyMessageW forwards WinEvents to border windows; CreateWindowExW / RegisterClassW / DefWindowProcW / DestroyWindow / PostQuitMessage (Win32) — helper windows: border (WS_POPUP|WS_SYSMENU + WS_EX_TOOLWINDOW|WS_EX_TOPMOST|WS_EX_NOACTIVATE), ghost host (WS_EX_TOOLWINDOW|WS_EX_NOACTIVATE|WS_EX_TRANSPARENT), hidden notification window (WS_DISABLED|WS_EX_NOACTIVATE); GetMessageW / PeekMessageW / TranslateMessage / DispatchMessageW (Win32) — message pumps on WinEvent thread, border threads, ghost owner thread, hidden monitor-listener thread; EnumDisplayMonitors, MonitorFromWindow, MonitorFromPoint (MONITOR_DEFAULTTONEAREST), GetMonitorInfoW/MONITORINFOEXW (Win32) — monitor enumeration, window→monitor mapping, work areas; win32-display-data crate (external, wraps DisplayConfig/QueryDisplayConfig APIs) — device path, device name, serial-number id, size/work-area per display; the basis of stable monitor identity; GetDpiForMonitor MDT_EFFECTIVE_DPI + SetProcessDpiAwarenessContext DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 (Win32, shcore/user32) — per-monitor scale comparison and process DPI declaration; DwmGetWindowAttribute DWMWA_CLOAKED (DWM, documented) — detect cloaked windows (DWM_CLOAKED_APP/SHELL/INHERITED), i.e. UWP suspended or other-virtual-desktop windows; DwmGetWindowAttribute DWMWA_EXTENDED_FRAME_BOUNDS (DWM, documented) — true painted bounds; preferred over GetWindowRect for all layout math; DwmSetWindowAttribute DWMWA_WINDOW_CORNER_PREFERENCE=DWMWCP_ROUND (DWM) — Win11 rounded corners on managed/border windows; DwmSetWindowAttribute DWMWA_BORDER_COLOR (DWM) — native accent border color, DWMWA_COLOR_NONE to clear; DwmRegisterThumbnail / DwmUpdateThumbnailProperties / DwmUnregisterThumbnail / DwmFlush (DWM) — live 'ghost' preview windows for movement animation, opacity crossfade synced to vblank via DwmFlush; DwmEnableBlurBehindWindow + CreateRectRgn (DWM/GDI) — 1px off-screen blur region hack to remove black aliasing around rounded D2D border corners (borrowed from PowerToys); SetWinEventHook EVENT_MIN..EVENT_MAX, WINEVENT_OUTOFCONTEXT|WINEVENT_SKIPOWNPROCESS (Win32 accessibility) — the entire event source of the WM; callback filters OBJID_WINDOW; OpenProcess PROCESS_QUERY_INFORMATION + QueryFullProcessImageNameW PROCESS_NAME_WIN32 + CloseHandle (Win32) — exe path/name for rules; GetCurrentProcessId, ProcessIdToSessionId (session id); WTSRegisterSessionNotification + WM_WTSSESSION_CHANGE (Win32 wtsapi) — lock/unlock detection for monitor reconciliation; RegisterPowerSettingNotification GUID_LIDSWITCH_STATE_CHANGE + WM_POWERBROADCAST (Win32) — laptop lid open/close and suspend/resume; RegisterDeviceNotificationW DBT_DEVTYP_DEVICEINTERFACE with GUID_DEVINTERFACE_MONITOR/DISPLAY_ADAPTER/VIDEO_OUTPUT_ARRIVAL + WM_DEVICECHANGE/WM_DISPLAYCHANGE/WM_SETTINGCHANGE (Win32) — display hotplug/resolution/work-area change detection; GetCursorPos, SetCursorPos, WindowFromPoint (Win32) — mouse-follows-focus (center cursor) and focus-follows-mouse (raise window under cursor); GetKeyState VK_MENU / VK_LBUTTON (Win32) — alt-pressed and drag-in-progress detection; winput crate message loop (dependency; installs low-level input hooks internally) — mouse-move events for the custom 'komorebi' focus-follows-mouse implementation (process_movement.rs); CoInitializeEx COINIT_MULTITHREADED / CoUninitialize / CoCreateInstance (COM) — thread-local COM init in com/mod.rs; IDesktopWallpaper via DesktopWallpaper CLSID (COM, documented) — per-monitor wallpaper get/set with monitor device path; IServiceProvider::QueryService on CLSID_ImmersiveShell (COM, UNDOCUMENTED shell internal) — gateway to shell view services; IApplicationViewCollection IID 1841c6d7-4f9d-42c0-af41-8747538f10e5 (COM, UNDOCUMENTED) — get_view_for_hwnd; vtable transcribed from Ciantic/AltTabAccessor; IApplicationView IID 372E1D3B-38D3-42E4-A15B-8AB2B178F513 (COM, UNDOCUMENTED) — SetCloak(cloak_type, flags) to cloak/uncloak foreign windows (workspace switch hiding + animation ghosting); vtable has 12 unknown methods, IID is build-fragile; D2D1CreateFactory / ID2D1Factory / CreateHwndRenderTarget / ID2D1SolidColorBrush / DrawRoundedRectangle (Direct2D COM, documented) — border rendering; GDI: CreateSolidBrush, CreatePen, Rectangle, RoundRect, DrawTextW, CreateFontIndirectW, SelectObject, SetTextColor, SetBkColor, GetDC/ReleaseDC, GetDeviceCaps LOGPIXELSY, MulDiv, InvalidateRect/ValidateRect, GetSystemMetrics SM_CXVIRTUALSCREEN (Win32) — stackbar tab drawing, border repaint plumbing; GetModuleHandleW (Win32) — HINSTANCE for window class registration Mechanisms: Single stateless facade struct (WindowsApi) wrapping every Win32 call; macros convert 0/NULL returns to Result via std::io::Error::last_os_error; HWNDs stored as plain isize everywhere for Send/Sync and serde; Event-driven core: SetWinEventHook callback → WinEvent enum → crossbeam bounded channel → window-manager event loop thread; hook thread runs its own GetMessageW pump (required for WINEVENT_OUTOFCONTEXT delivery); Heavy thread-per-window-class model: each border window, the ghost owner, the stackbar, the hidden monitor-listener window each own a dedicated thread with its own message loop, because HWNDs have thread affinity; cross-thread work is handed over via PostMessageW with WM_USER messages (including Box::into_raw ownership transfer in WM_ANIMATE_RECT); Shadow-rect compensation: every positioning call computes GetWindowRect minus DWMWA_EXTENDED_FRAME_BOUNDS and offsets the target rect so the painted region (not the shadow) fills the tile; SetWindowPosition bitflags newtype over SWP_* constants; async-vs-sync window handling behavior globally switchable (SWP_ASYNCWINDOWPOS / ShowWindowAsync) to avoid blocking on hung apps; Window eligibility pipeline (window.rs should_manage/window_is_eligible): is_window → min size → has title → not cloaked (DWMWA_CLOAKED) → title/exe/class/path rules (ignore/manage/float regex identifiers) → style heuristics (CAPTION+WINDOWEDGE, reject DLGMODALFRAME, reject LAYERED unless whitelisted); Undocumented-shell cloaking as a hiding strategy: SetCloak(hwnd, 1, 2) cloaks, SetCloak(hwnd, 1, 0) uncloaks, giving workspace switches without minimize animations or taskbar churn; COM apartment initialized thread-locally (COINIT_MULTITHREADED + thread_local ComInit guard); Ghost movement animation: cloak source via IApplicationView::SetCloak → pre-position source at target (except Chromium) → DwmRegisterThumbnail into a click-through host window → lerp host rect each frame → uncloak → 8-step DwmFlush-paced opacity crossfade → destroy; Monitor identity model: device path/serial-number from win32-display-data, deduped serial ids, user index preferences mapped through a monitor_usr_idx_map; reconciliation driven by a hidden window receiving device/power/session broadcast messages; Reaper thread polls IsWindow/IsWindowVisible every 20ms to garbage-collect vanished HWNDs from state (WinEvents alone are not reliable for destruction); Focus manager: dedicated channel/thread that re-applies focus (and centers cursor for mouse-follows-focus) after animations complete; Border rendering: one D2D HwndRenderTarget per border window, brushes pre-created per WindowKind (Single/Stack/Monocle/Unfocused/Floating/UnfocusedLocked); borders track their target via forwarded EVENT_OBJECT_LOCATIONCHANGE messages rather than polling Gotchas: Foreground lock bypass (windows_api.rs raise_and_focus_window): Windows refuses SetForegroundWindow from a process that hasn't received input; komorebi sends itself a zeroed INPUT_MOUSE via SendInput first so it 'recently received input', then calls SetForegroundWindow — the classic documented-behavior workaround; SPI_SETFOREGROUNDLOCKTIMEOUT is set to 0 at startup (foreground_lock_timeout) 'to enable keyboard-driven focus updating'; AllowSetForegroundWindow(own pid) is retried 5 times at startup and the process aborts if it never succeeds; HWND_TOP, never HWND_TOPMOST (long comment in position_window): TOPMOST is a sticky, viral z-order state that propagates to owned windows and breaks apps that rely on their own TOPMOST tool windows/dialogs; SWP_ASYNCWINDOWPOS everywhere (configurable): SetWindowPos synchronously waits on the target's WndProc thread, so a hung app would hang the tiler; MoveWindow has no async variant and is flagged in a comment as 'might hang'; Invisible borders/drop shadow: SetWindowPos coordinates include the DWM shadow, so tiling by GetWindowRect leaves visible gaps; shadow_rect() computes the GetWindowRect-vs-DWMWA_EXTENDED_FRAME_BOUNDS delta and every layout rect is adjusted by it; DwmSetWindowAttribute(DWMWA_CLOAK) returns E_ACCESSDENIED for foreign HWNDs (comment in window.rs) — hence the undocumented IApplicationView::SetCloak via CLSID_ImmersiveShell/IApplicationViewCollection, whose IIDs are Windows-build-fragile and whose vtable contains 12 'unknown' methods that only exist to keep slot offsets correct; Cloaked-window trap: DWMWA_CLOAKED (DWM_CLOAKED_APP/SHELL/INHERITED) must be checked during enumeration or you will manage invisible UWP frames and windows on other virtual desktops; cloaked windows are only allowed through for Hide/Cloak events; Chromium/Electron quirk (window.rs MovementRenderDispatcher): NativeWindowOcclusionTrackerWin suspends the renderer whenever DWMWA_CLOAKED is non-zero, so the cloak→reposition→capture animation trick leaves a stale/black DComp swap chain; windows with class Chrome_WidgetWin_* get a capture-at-start fallback where the uncloak itself wakes the Viz compositor; DWM thumbnails capture at DWMWA_EXTENDED_FRAME_BOUNDS, not GetWindowRect — the ghost host must be sized to the logical rect or the texture is stretched by the shadow ratio (comment in pre_render); Ghost opacity fade must call DwmUpdateThumbnailProperties synchronously on the animating thread: routing it through the owner-thread channel races the following DwmFlush and collapses the multi-frame fade into one step (comment on GhostWindow::set_opacity); rect updates conversely must go to the owner thread because SetWindowPos has thread affinity; Border corner black-pixel hack (border.rs create): DwmEnableBlurBehindWindow with a 1x1 CreateRectRgn positioned off-screen (-GetSystemMetrics(SM_CXVIRTUALSCREEN)-8) removes black aliasing around rounded D2D corners — comment admits the value was borrowed from PowerToys without a full explanation of why it works; WINWORD.EXE reaper hack (reaper.rs): Word keeps an invisible WS_EX_LAYERED window alive after closing one of several docs (LAYERED being a style normally required for managed windows), so the reaper also evicts non-visible windows — but must skip that when HidingBehaviour::Hide is active, since komorebi itself makes legitimate windows non-visible; OleMainThreadWndClass / OLEChannelWnd quirk (process_event.rs): a hidden COM helper window spawned by explorer.exe is always tied to the primary monitor, so events from it would repeatedly reset the focused monitor to index 0; filtered by class name; WS_EX_LAYERED windows are excluded from management by default because they generate duplicate FocusChange events that send redrawing haywire, with an explicit whitelist for apps like Steam; Hide events for known layered windows are always processed to avoid ghost tiles; GetWindowLongPtrW returns 0 both for errors and legitimate zero values: komorebi calls SetLastError(0) before and checks GetLastError after to disambiguate; ShowWindow's BOOL return does not indicate success (comments cite MSDN); EVENT_SYSTEM_FOREGROUND races GetForegroundWindow (windows_callbacks.rs): by the time the border manager inspects the foreground window it may still be stale, so a second notification is fired at the border manager once the system catches up; a related edge case re-processes borders after focusing an elevated (admin) window, whose events komorebi cannot fully observe, leaves the previous window's border stuck Unfocused; WM_DEVICECHANGE fires ~4 times for one monitor plug event on Win11 (comment in hidden.rs); WM_SETTINGCHANGE with SPI_SETWORKAREA must be handled for taskbar-hiders like ButteryTaskbar that change the work area without any display change; Monitor serial-number IDs are not unique across identical cheap monitors: DUPLICATE_MONITOR_SERIAL_IDS tracks collisions and falls back to device-path identity; MONITOR_DEFAULTTONEAREST is used specifically because it guarantees MonitorFromWindow/Point never returns NULL (comments cite MSDN); Windows 11 detection is build-number >= 22000 via os_info, used to decide rounded vs square border style for BorderStyle::System; Slow-application compensation: some apps (SLOW_APPLICATION_IDENTIFIERS) need an artificial sleep before the manage decision or their windows are inspected before they are ready; window_rect has a TODO noting that once DPI awareness is declared, DWMWA_EXTENDED_FRAME_BOUNDS rects will need scaling by GetDpiForWindow/GetDpiForSystem; A border's WM_DESTROY handler has to release the render target and clear the userdata pointer; leaving either until WM_NCDESTROY means touching state Direct2D has already reclaimed; EVENT_OBJECT_DESTROY forwarded to a border only hides it for fast visual feedback, actual destruction happens on the manager loop; destroy() sleeps 10ms to let in-flight renders finish; RegisterClassW returns ERROR_CLASS_ALREADY_EXISTS as a benign failure when re-registering (ghost.rs comment tolerates it) Minimal-WM essentials: Essential core (a minimal tiling WM cannot work without these): (1) SetWinEventHook with WINEVENT_OUTOFCONTEXT|WINEVENT_SKIPOWNPROCESS plus a message pump on its thread, handling at least SHOW/HIDE/DESTROY/SYSTEM_FOREGROUND/MINIMIZESTART-END/MOVESIZEEND/CLOAKED-UNCLOAKED; (2) EnumWindows for the initial scan; (3) the eligibility filter stack — IsWindow/IsWindowVisible/IsIconic, GetWindowTextW, RealGetWindowClassW, GWL_STYLE/GWL_EXSTYLE checks, DwmGetWindowAttribute(DWMWA_CLOAKED), and exe name via GetWindowThreadProcessId + OpenProcess + QueryFullProcessImageNameW (without style- and cloak-filtering you tile invisible UWP frames and popups); (4) SetWindowPos with SWP_NOACTIVATE (+ ideally SWP_ASYNCWINDOWPOS) corrected by the shadow rect derived from DWMWA_EXTENDED_FRAME_BOUNDS vs GetWindowRect — skipping this leaves ~7px gaps between tiles; (5) ShowWindow for hide/restore/minimize when switching workspaces; (6) monitor plumbing: MonitorFromWindow(MONITOR_DEFAULTTONEAREST), GetMonitorInfoW work areas, EnumDisplayMonitors; (7) SetProcessDpiAwarenessContext(PER_MONITOR_AWARE_V2) so all rects are physical pixels; (8) focus control: GetForegroundWindow, SetForegroundWindow preceded by the SendInput foreground-lock bypass, AllowSetForegroundWindow at startup; (9) some liveness reaping (polling IsWindow) because destroy events are unreliable. Optional polish (komorebi works degraded without them): the entire undocumented COM cloaking path (SW_HIDE/SW_MINIMIZE are the documented fallbacks komorebi itself offers as HidingBehaviour), DWM thumbnail ghost animations and DwmFlush pacing, Direct2D border windows and GDI stackbars, DWMWA_WINDOW_CORNER_PREFERENCE / DWMWA_BORDER_COLOR cosmetics, SetLayeredWindowAttributes transparency, IDesktopWallpaper per-monitor wallpaper, focus-follows-mouse (both SPI_SETACTIVEWINDOWTRACKING and the winput-hook variant), mouse-follows-focus SetCursorPos, and the monitor-reconciliation hidden window (device/power/session notifications) — the last is optional for a single-monitor MVP but becomes near-essential for laptop/dock users. ## Data model and tiling/layout subsystem of komorebi (WindowManager > Monitor > Workspace > Container > Window hierarchy, Ring structure, komorebi-layouts algorithms, resize deltas, focus/movement, monocle/maximize/float/stack handling, layout-to-SetWindowPos pipeline, and State serialization for IPC) komorebi models everything as a tree of "Rings": `WindowManager` holds `Ring`, each `Monitor` holds `Ring`, each `Workspace` holds `Ring`, and each `Container` holds `Ring`. A `Ring` is just `VecDeque + focused: usize`; a macro (`impl_ring_elements!`) stamps out `xs()/xs_mut()/focused_x()/focused_x_idx()/focused_x_mut()` accessors on each owner. A `Window` is nothing but `{ hwnd: isize }` — all metadata (title, exe, class, rect, styles) is queried live from Win32 on demand. A `Container` is the tiling unit (a "stack" of one or more windows sharing one tile; only the focused window is shown, the rest are hidden/cloaked). A `Workspace` additionally carries three special layers outside the container ring: `monocle_container: Option` (+ `monocle_container_restore_idx`), `maximized_window: Option` (+ restore idx), and `floating_windows: Ring`, plus per-workspace layout config (`layout`, `layout_flip`, `layout_rules` thresholds, paddings), the mutable tiling state (`resize_dimensions: Vec>`, `latest_layout: Vec`, `tile: bool`), and a `WorkspaceGlobals` struct that the Monitor pushes down (work area, offsets, border width/offset, default paddings) so `Workspace::update()` can run without reaching back up the tree. `WindowManager` also maintains `known_hwnds: HashMap` as a reverse index, persisted to komorebi.hwnd.json for the reaper. The heart of tiling is `Workspace::update()` (komorebi/src/workspace.rs): prune empty containers → compute adjusted work area (work_area minus work_area_offset, minus window-based offset when container count <= limit, minus workspace padding) → enforce per-layout resize constraints → apply threshold-based layout/behaviour rules → then branch: monocle container gets the whole padded area via one `set_position`; a maximized window just gets `ShowWindow(SW_MAXIMIZE)`; otherwise `layout.as_boxed_arrangement().calculate(area, len, padding, flip, resize_dimensions, focused_idx, layout_options, latest_layout)` returns `Vec` (one rect per container, index-aligned). Each rect then gets border offset/width padding and a stackbar tab-height reservation, and every window in the container is positioned to it (focused ones also get titlebar removal/restore handling). The rects are cached in `latest_layout` (used for cursor hit-testing and by the Scrolling layout to recover scroll position). Layout algorithms live in the separate platform-agnostic `komorebi-layouts` crate behind the `Arrangement` trait: BSP (recursive fibonacci/dwindle split with configurable column/row ratios), Columns, Rows, VerticalStack, RightMainVerticalStack (borrowed from LeftWM), HorizontalStack, UltrawideVerticalStack (center primary, left secondary, right stack), Grid (ported from LeftWM, sqrt-based column count), Scrolling (PaperWM-style horizontal strip, N visible columns, off-screen windows get out-of-area rects, scroll position derived from latest_layout, optional centered focus), and CustomLayout (user JSON/YAML column definitions: one Primary, optional Secondaries with capacity, one trailing Tertiary that absorbs overflow). Resizing is delta-based, not ratio-based: `resize_dimensions[i]` is an `Option` of per-edge pixel deltas for container i; `DefaultLayout::resize` adds ±delta to the dragged edge (clamped against the unaltered tile size), `enforce_resize_constraints*` zeroes edges that make no sense per layout (e.g. in BSP even-indexed containers cannot grow bottom, odd cannot grow right; first can never grow left/top), and each layout's `calculate_*_adjustment` converts the per-container deltas into symmetric adjustments of the container and its neighbors (`resize_left/right/top/bottom` split each delta /2 between the two adjacent tiles). Directional focus/movement is index arithmetic, not geometry (except for floating windows and cross-monitor hops): the `Direction` trait (komorebi-layouts/src/direction.rs) implements `is_valid_direction` + `up/down/left/right_index` per layout (BSP parity tricks, grid neighbor computation, custom-layout column accumulation), with `OperationDirection::flip` remapping the requested direction when the workspace has a `layout_flip`. `focus_container_in_direction`/`move_container_in_direction` first try the in-workspace index; if none, they either wrap to the adjacent workspace (CrossBoundaryBehaviour::Workspace, PaperWM-style, landing on the layout's leftmost/rightmost index via `cross_boundary_edge_index`) or find the adjacent monitor by comparing monitor edge coordinates (`monitor_idx_in_direction`), moving/swapping the container across (MoveBehaviour::Swap/Insert/NoOp) with `Monitor::add_container_with_direction` choosing front/back/at-focus insertion. Monocle and maximize are toggles that physically remove the focused container/window from the ring, park it in the dedicated Option field with a restore index, and reinsert at that exact index on toggle-off (deliberately using plain VecDeque insert/remove rather than the lock-respecting variants). Stacking moves a window from the focused container into a neighbor (`move_window_to_container`), and `Container::load_focused_window` shows the focused stack member and cloaks the rest. Floating windows live per-workspace, are moved/resized by pixel deltas clamped to the monitor work area, and a `WorkspaceLayer` enum (Tiling/Floating) tracks which layer has focus. The layout-to-OS pipeline is NOT batched: there is no BeginDeferWindowPos/HDWP anywhere — each window gets its own `SetWindowPos` call via `Window::set_position` → `WindowsApi::position_window`, with flags SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_NOCOPYBITS | SWP_FRAMECHANGED, plus SWP_NOZORDER unless the window should be raised (HWND_TOP, deliberately never HWND_TOPMOST), plus SWP_ASYNCWINDOWPOS when WindowHandlingBehaviour::Async so a hung app cannot block the WM thread. The target rect is pre-adjusted by the window's DWM drop-shadow margins (`shadow_rect` = GetWindowRect minus DWMWA_EXTENDED_FRAME_BOUNDS) so the visible frame lands exactly on the computed tile, and set_position early-returns when the rect is unchanged. If animations are enabled, the same call instead drives an AnimationEngine dispatcher that lerps rects on a worker thread (legacy MoveWindow per frame, or the ghost-window path using cloaking plus DWM thumbnails). Threading: one `Arc>` shared by the WinEvent listener thread (events via crossbeam channel → process_event), the UDS socket command listener (process_command), and satellite manager threads (border, stackbar, transparency, focus, reaper, monitor reconciliator) that each lock the WM. For IPC, `State::from(&WindowManager)` clones the whole monitor/workspace/container tree (stripping `workspace_config` and transient fields), serde-serializes it (Window serializes as hwnd+title+exe+class+live rect), and is written to komorebi.state.json on exit (reapplied on restart via `apply_state`) and pushed to subscriber sockets by `notify_subscribers` after each event/command, gated by `State::has_been_modified` diffing. Windows APIs: SetWindowPos — the sole positioning primitive; one call per window (no HDWP/DeferWindowPos batching) with SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_NOCOPYBITS | SWP_FRAMECHANGED, plus SWP_NOZORDER unless raising, plus SWP_ASYNCWINDOWPOS under WindowHandlingBehaviour::Async to avoid blocking on hung apps; also used for raise (HWND_TOP) / lower (HWND_BOTTOM) with SWP_NOMOVE|SWP_NOSIZE; MoveWindow — legacy (non-ghost) animation frames only; cannot be async so it may hang; GetWindowRect + DwmGetWindowAttribute(DWMWA_EXTENDED_FRAME_BOUNDS) — computing the drop-shadow margin (shadow_rect) so tiles are positioned by visible frame, and reading current rects for diffing/serialization; ShowWindow / ShowWindowAsync (SW_HIDE, SW_MINIMIZE, SW_MAXIMIZE, SW_NORMAL, SW_SHOWNOACTIVATE) — hide/restore/minimize/maximize per HidingBehaviour; IApplicationView::SetCloak (undocumented COM, com.rs) — default window-hiding mechanism; used because DwmSetWindowAttribute(DWMWA_CLOAK) returns E_ACCESSDENIED for foreign HWNDs; DWMWA_CLOAKED read for is_cloaked eligibility checks; GetForegroundWindow / SetForegroundWindow — focus tracking and focus changes; preceded by a synthetic SendInput(INPUT_MOUSE) to self to defeat the foreground-lock check; BringWindowToTop — raise on focus; GetWindowLongPtrW / SetWindowLongPtrW (GWL_STYLE / GWL_EXSTYLE) — WS_CAPTION/WS_THICKFRAME removal for no-titlebar mode, WS_EX_LAYERED toggling for transparency, style-based manage eligibility (CAPTION+WINDOWEDGE, reject DLGMODALFRAME/LAYERED); SetLayeredWindowAttributes — transparency alpha; IsZoomed / IsIconic / IsWindowVisible / IsWindow — window state predicates; MonitorFromWindow / MonitorFromPoint (MONITOR_DEFAULTTONEAREST) + win32_display_data enumeration — monitor topology, work areas, stale-HMONITOR recovery by device_id; GetCursorPos / SetCursorPos — mouse-follows-focus (center cursor in rect) and cursor hit-testing against latest_layout; SystemParametersInfoW (SPI_SET/GETACTIVEWINDOWTRACKING, SPI_SETFOREGROUNDLOCKTIMEOUT) — focus-follows-mouse implementation; SetWinEventHook (winevent_listener) — window show/hide/cloak/focus events feeding the crossbeam event channel; GetWindowTextW / RealGetWindowClassW / OpenProcess + QueryFullProcessImageNameW — window title/class/exe/path for rule matching (Window stores only hwnd); DwmFlush / DWM thumbnail APIs (DwmRegisterThumbnail via GhostWindow) — animation vsync waits and ghost-window movement animation; DwmUpdateThumbnailProperties for crossfade; uds_windows UnixListener/UnixStream — command socket (komorebi.sock) and subscription sockets over which serialized State is sent; IDesktopWallpaper (set_wallpaper) — per-workspace wallpapers (polish) Mechanisms: Ring = VecDeque + focused index; the single collection abstraction used at every level (monitors, workspaces, containers, windows, floating windows), with impl_ring_elements! macro generating typed accessors per owner; Strict containment hierarchy WindowManager > Monitor > Workspace > Container > Window; Window is only { hwnd: isize } (stateless, Copy), everything else queried from the OS on demand; Arrangement trait (calculate(area, len, padding, flip, resize_dimensions, focused_idx, layout_options, latest_layout) -> Vec) implemented for DefaultLayout and CustomLayout; Layout enum dispatches via Box; index-aligned rects map 1:1 to containers; Direction trait per layout (is_valid_direction + up/down/left/right_index) drives directional focus/move as pure index arithmetic; OperationDirection::flip remaps direction under layout_flip; cross_boundary_edge_index picks leftmost/rightmost container when crossing monitor/workspace boundaries; Delta-based resize model: resize_dimensions: Vec> of per-edge pixel deltas per container; DefaultLayout::resize mutates one edge by ±delta with bounds checks; enforce_resize_constraints_* zeroes illegal edges per layout; calculate_*_adjustment distributes each delta half-and-half between adjacent tiles at calculate time; Workspace::update() as the single retile entry point: prune empties, resolve threshold rules (layout_rules, layout_options_rules, work_area_offset_rules, window_container_behaviour_rules), compute adjusted work area, dispatch monocle/maximized/tiled branches, cache latest_layout, resize resize_dimensions to container count; WorkspaceGlobals pushed down from Monitor (work area, offsets, border width/offset, default paddings) so workspaces can retile without upward references; monitor.update_workspace_globals before workspace.update; Monocle/maximize as park-and-restore: remove focused container/window from the ring (plain remove, not lock-respecting), store in Option field + restore_idx, reinsert at the same index on toggle-off; resize_dimensions deliberately not truncated while monocled; Stacking = multi-window Container: move_window_to_container merges, new_container_for_focused_window splits, load_focused_window shows focused stack member and cloaks siblings; stackbar reserves tab height at the top of the container rect; LockableSequence trait on VecDeque (insert/remove/swap_respecting_locks) keeps 'locked' containers at absolute indexes as other containers churn; known_hwnds: HashMap reverse index, rebuilt after mutations, shared with the reaper thread and dumped to komorebi.hwnd.json; Threading: Arc> shared across WinEvent-listener thread (crossbeam channel -> process_event), UDS command thread (process_command), and satellite manager threads (border/stackbar/transparency/focus/reaper/monitor-reconciliator), each locking the whole WM; animations run on separate threads via AnimationEngine + RenderDispatcher; State/GlobalState serialization: State::from(&WindowManager) deep-clones the tree stripping workspace_config/transient fields; written to temp_dir/komorebi.state.json on stop and re-applied on start (apply_state validates every hwnd still resolves an exe); notify_subscribers pushes State JSON to subscriber sockets gated by State::has_been_modified structural diff; Rule engine (should_act / should_act_individual) matching title/exe/class/path with strategies (Legacy/Equals/StartsWith/EndsWith/Contains/Regex + negations, Composite AND-rules) used for ignore/manage/float/no-titlebar/workspace-placement decisions; two-phase EnforceWorkspaceRuleOp (collect ops, then remove from origin and insert into target); Scrolling layout statefulness: previous scroll offset recovered from latest_layout (first rect with left >= area.left) instead of stored state; off-strip windows get out-of-area rects and remain technically positioned Gotchas: Rect is NOT a Win32 RECT: right = width and bottom = height (top-left + extent). From/rect() convert. Padding shrinks: left+=p, top+=p, right-=2p, bottom-=2p.; SetWindowPos coordinates must be compensated by the DWM drop-shadow margin: shadow_rect = GetWindowRect minus DWMWA_EXTENDED_FRAME_BOUNDS, added to the target rect so the visible frame (not the invisible shadow) aligns with the tile.; No positioning batching: no BeginDeferWindowPos/HDWP anywhere; one SetWindowPos per window per retile. Instead SWP_NOSENDCHANGING + SWP_NOCOPYBITS + SWP_FRAMECHANGED reduce churn, and set_position early-returns when the window is already at the target rect.; SetWindowPos blocks on the target window's WndProc thread by default; komorebi adds SWP_ASYNCWINDOWPOS (WindowHandlingBehaviour::Async) so a hung app cannot deadlock the WM. MoveWindow (used in legacy animation) has no async variant and can hang.; Raising uses HWND_TOP, never HWND_TOPMOST — a code comment warns that TOPMOST behaves virally (propagates to owned windows) and apps rely on their own TOPMOST dialogs/tool windows staying above their main window.; Hiding windows uses the undocumented IApplicationView::SetCloak COM call because DwmSetWindowAttribute(DWMWA_CLOAK) returns E_ACCESSDENIED for foreign HWNDs; HidingBehaviour::Hide/Minimize are legacy alternatives. Programmatically hidden hwnds are tracked in HIDDEN_HWNDS so hide events can be distinguished from user minimizes.; Chromium/Electron windows (class prefix Chrome_WidgetWin_) suspend their renderer when any non-zero DWMWA_CLOAKED state is present, so the ghost-animation pre-paint trick leaves stale/black frames — they get a capture-at-start fallback path.; SetForegroundWindow is preceded by a synthetic SendInput mouse event sent to komorebi's own process to defeat Windows' foreground-lock check, then SetWindowPos(HWND_TOP, SWP_SHOWWINDOW) before SetForegroundWindow.; BSP resize constraints are parity-based: even-indexed containers cannot be resized on the bottom edge, odd-indexed cannot on the right; first container never left/top, last never bottom/right. Resize deltas are split /2 between the two adjacent tiles (resize_left also subtracts half from the width).; DefaultLayout::resize comments admit the out-of-bounds checks cannot account for adjacent-edge changes, so you can still push windows off-screen by maxing Increase-Left on index 1 then Decrease-Right on index 0 — the documented remedy is the retile command, not defensive code.; resize_dimensions is NOT truncated while a monocle container exists: with exactly 2 containers, toggling monocle would shrink the vec to len 1 and permanently lose the reintegrated container's resize adjustment (explicit comment in Workspace::update).; Monocle/maximize toggles intentionally bypass the lock-respecting insert/remove helpers (plain VecDeque::insert/remove) so the container returns to its exact pre-toggle index; everything else must use insert_respecting_locks/remove_respecting_locks to keep 'locked' containers pinned.; After transferring a monocle container across monitors, the uncloak events generated by restoring the origin workspace must be ignored (wm.uncloack_to_ignore counter — note the typo 'uncloack') or workspace reconciliation steals focus back to the origin monitor.; move_container_to_monitor calls target_monitor.update_focused_workspace(offset) twice on purpose: the second call fixes layout when the target monitor has a different DPI; move_container_in_direction likewise re-updates when monitors_have_same_dpi is false.; HMONITOR handles go stale after display changes: monitor lookups fall back to matching device_id via fresh win32_display_data and patch monitor.id in place.; Windows are managed only if (WS_CAPTION && WS_EX_WINDOWEDGE) unless overridden, never with WS_EX_DLGMODALFRAME, and WS_EX_LAYERED only when whitelisted (e.g. Steam) or already known to the transparency manager; there are also minimum width/height gates and a PERMAIGNORE_CLASSES list.; SLOW_APPLICATION_IDENTIFIERS triggers a deliberate thread::sleep before managing (compensation for apps that need time before their window is positionable).; Integer-division remainders are explicitly absorbed by the last column/row (columns_with_ratios, rows_with_ratios, Grid, Scrolling) so tiles cover the full work area without 1px gaps.; Scrolling layout: refuses to activate with more than one monitor; scroll position is reconstructed from latest_layout (first rect whose left >= area.left) rather than stored — an empty latest_layout means scroll position 0.; Workspace::update() no-ops until INITIAL_CONFIGURATION_LOADED, and silently retains 'PRESELECT' placeholder containers when pruning empty ones.; Config hot-reload watcher handles editor quirks: Notepad emits Modify while (Neo)Vim emits Remove (swap-file rename), both must trigger reload.; Window-based work-area offset only applies when container count <= window_based_work_area_offset_limit (or a monocle exists and limit > 0) — used for centering single windows on ultrawides.; State stripping: State::from removes workspace_config/preselected/promotion indexes from every workspace before serializing so subscribers and `komorebic state` don't see internal config echoes; apply_state refuses to restore if any serialized hwnd no longer resolves to a process. Minimal-WM essentials: Essential core (roughly 15% of this code): (1) the Ring hierarchy — Monitor {size, work_area_size} > Workspace {containers, focused idx} > Container {windows} > Window {hwnd}; you can even drop Container and tile Windows directly if you skip stacking. (2) One layout function: Arrangement::calculate-style pure function (work_area, count, padding, resize hints) -> Vec; BSP (recursive_fibonacci, ~100 lines) or Columns (~30 lines) is enough — Rows/Grid/stack variants/Scrolling/Custom are additive per-enum-variant work. (3) Workspace::update()'s skeleton: adjust work area by padding, call calculate, add border/padding to each rect, call SetWindowPos per window — with the two non-negotiable Windows-specific pieces: shadow_rect compensation (DWMWA_EXTENDED_FRAME_BOUNDS vs GetWindowRect) and the SWP_NOACTIVATE|SWP_ASYNCWINDOWPOS|SWP_FRAMECHANGED flag set (no HDWP batching needed — komorebi ships without it). (4) Window eligibility filtering (should_manage: is_window, has title, not cloaked, WS_CAPTION+WS_EX_WINDOWEDGE heuristic, minimum size, an ignore list) — without this you will tile tooltips and shell popups. (5) Hide/restore for workspace switching — cloaking via IApplicationView::SetCloak (or plain ShowWindow(SW_HIDE) as a simpler start) plus a hidden-hwnds set. (6) Directional focus/move: the Direction trait's index arithmetic for your chosen layout, plus SetForegroundWindow with the SendInput foreground-lock workaround. (7) A known_hwnds map and an event loop (WinEvent hook -> channel -> single-threaded manager) to add/remove windows on show/destroy. Optional polish, safely deferred: monocle/maximize toggles (simple park-and-restore once the core works), resize deltas and their per-layout constraint tables, stacking/stackbars, floating layer and float rules, multi-monitor moves with DPI double-update, layout_flip, locked containers/LockableSequence, threshold rules (layout_rules etc.), custom layouts, Scrolling layout, animations/ghost windows (large and subtle — all the Chromium/cloak/DwmFlush lore), borders/transparency/wallpapers/themes, workspace rules engine, State serialization + subscriber IPC + state restore, and the reaper/monitor-reconciliator satellite threads. ## IPC and CLI (komorebi daemon command socket, komorebic CLI, komorebi-client library, event subscriptions) Transport: komorebi's primary IPC channel is a Unix domain socket on Windows, via the `uds_windows` crate (Windows has supported AF_UNIX since Win10 1803). The daemon binds `%LOCALAPPDATA%\komorebi\komorebi.sock` at WindowManager::new (window_manager.rs:137-150), deleting any stale socket file first (with a comment noting `Path::exists()` is unreliable on Windows). An optional TCP listener on `0.0.0.0:` can additionally be enabled with `komorebi.exe --tcp-port` (process_command.rs:147-185) for remote control; connections are kept in a global TCP_CONNECTIONS map with 30s keepalive. Named pipes are used only for the legacy event-subscription path, not for commands. Protocol: messages are the `SocketMessage` enum (komorebi/src/core/mod.rs:63-265, ~187 variants), serialized as serde JSON with adjacent tagging `#[serde(tag = "type", content = "content")]`, e.g. `{"type":"FocusWorkspaceNumber","content":5}`. Commands are newline-delimited JSON: the client writes one JSON object per line; komorebi-client's send_message opens a fresh UnixStream per invocation, writes, and closes (fire-and-forget); send_batch joins many messages with '\n' on one connection; send_query additionally does `shutdown(Write)` then reads the reply to EOF with a 1s timeout. There is deliberately no framing protocol for replies (TODO comment at process_command.rs:2344-2347) — a reply is a whole pretty-printed JSON blob (State, GlobalState, VisibleWindows, Query results, schemas, DebugWindow output) and termination is signalled by socket shutdown. Server side, listen_for_commands (process_command.rs:104-144) spawns a thread that clones the UnixListener, accepts connections, and spawns a thread per client which reads lines, parses with SocketMessage::from_str (serde_json), acquires the WindowManager mutex with try_lock_for(1s), and calls WindowManager::process_command(message, reply) — one giant match statement (~2,100 lines) where each arm mutates the WM or writes a reply to the same stream. While paused, only TogglePause/State/GlobalState/Stop are processed. CLI mapping: komorebic (komorebic/src/main.rs) is a clap-derive binary with a SubCommand enum of ~170 subcommands (lines 1040-1582). Arg structs are mass-generated by macros (gen_enum_subcommand_args!, gen_target_subcommand_args!, gen_workspace_subcommand_args!, etc.) so a subcommand like `focus left` parses an OperationDirection and maps 1:1 to `send_message(&SocketMessage::FocusWindow(dir))`. Query-style subcommands (state, query, visible-windows, monitor-information, generate-static-config) use print_query -> send_query and print the JSON response. A handful of subcommands never touch the socket: start/stop/kill spawn or kill komorebi.exe, whkd, komorebi-bar, masir via PowerShell scripts (Start-Process/Stop-Process); log tails the daemon's log file in temp; check/quickstart/autostart manage config files and shortcuts. komorebic-no-console is a tiny #![windows_subsystem = "windows"] wrapper that re-execs komorebic.exe with CREATE_NO_WINDOW so hotkey daemons like whkd can invoke it without a console flash — this is the price of the CLI-driven design: every keypress spawns a process. Event subscriptions: external clients (komorebi-bar, status bars like yasb/zebar) subscribe via two mechanisms. (1) UDS: the subscriber binds its own socket file in the komorebi data dir (komorebi-client::subscribe binds `%LOCALAPPDATA%\komorebi\` then sends AddSubscriberSocket(name)); on every processed command/WM event, notify_subscribers (komorebi/src/lib.rs:355-437) serializes a Notification { event: NotificationEvent (Socket|WindowManager|Monitor|VirtualDesktop), state: full State } to JSON and does a fresh UnixStream::connect + write per subscriber per notification; connect failures mark the subscription stale and remove it. AddSubscriberSocketWithOptions supports filter_state_changes so subscribers only get notifications when the state actually changed (computed by diffing initial_state vs post-command state, with a few override events always delivered). (2) Named pipes (legacy, `komorebic subscribe-pipe`): the subscriber creates `\\.\pipe\` as the pipe server; komorebi connects as a client via miow::pipe::connect (process_command.rs:1985-1993) and keeps the File open, writing newline-delimited JSON notifications; OS errors 2 (FILE_NOT_FOUND) and 232 (ERROR_NO_DATA / pipe closing) trigger unsubscription. Hotwatch (inotify-style file watching) is not IPC: it watches komorebi.json / legacy komorebi.ahk/ps1 for changes (window_manager.rs watch_configuration, static_config.rs:1367-1386) and triggers config reload; komorebi-bar does the same for its own config. Windows APIs: AF_UNIX Unix domain sockets via uds_windows crate (UnixListener/UnixStream) — the primary command transport and the subscriber-socket event channel; requires Windows 10 1803+; Named pipes (\\.\pipe\) via miow::pipe::connect — komorebi acts as pipe *client* to a subscriber-created pipe server for the legacy event subscription channel; stale pipes detected via raw OS errors 2 (ERROR_FILE_NOT_FOUND) and 232 (ERROR_NO_DATA); std::net::TcpListener + net2::TcpStreamExt::set_keepalive — optional remote-control command channel (--tcp-port); AllowSetForegroundWindow + SystemParametersInfo foreground lock timeout (WindowsApi wrappers, called at daemon startup so IPC-triggered focus changes actually work); ShowWindow(SW_RESTORE) — used directly by komorebic restore-windows / stop fallback to un-hide windows recorded in komorebi.hwnd.json; SetCursorPos/mouse_event style helpers (WindowsApi::center_cursor_in_rect, left_click) — used by the ForceFocus command; PowerShell subprocesses (powershell_script crate) — komorebic start/stop use Start-Process/Stop-Process to manage komorebi.exe, whkd, komorebi-bar, masir; not a Win32 API but the actual process-management mechanism; CreateProcess with CREATE_NO_WINDOW creation flag (komorebic-no-console wrapper) so hotkey daemons can run CLI commands without console windows flashing Mechanisms: Single SocketMessage enum (~187 variants) shared by daemon, client library, and CLI — serde JSON with tag="type"/content="content" adjacent tagging; FromStr = serde_json::from_str; strum Display for logging; optional schemars JSON-schema generation served over the socket itself (SocketSchema command); Newline-delimited JSON framing for requests (BufReader::lines server-side; send_batch joins with '\n'); replies are unframed whole-JSON with termination via socket shutdown — send_query does shutdown(Write) then read_to_string to EOF; Threading: dedicated listener thread wrapped in an outer restart loop (thread::spawn + join; if the accept loop panics it logs 'restarting failed thread' and re-spawns), plus one thread per client connection; all command processing funnels through Arc> (parking_lot), acquired with try_lock_for(Duration::from_secs(1)) so a wedged WM drops commands instead of deadlocking; CLI is a pure 1:1 command mapper: clap-derive SubCommand enum (~170 subcommands) with macro-generated arg structs (gen_enum_subcommand_args! etc.); fire-and-forget send_message for actions, send_query/print_query for reads; komorebic itself holds no WM state; Pub/sub event fan-out: notify_subscribers called after every processed command and WM event with Notification{event, state} where state is the FULL serialized WM state (no diffs); UDS subscribers get one connect+write per notification (subscriber binds the listener, daemon is the connector — inverted roles); pipe subscribers get newline-delimited writes on a persistent File handle; stale subscribers are garbage-collected on write/connect failure; SubscribeOptions.filter_state_changes: server diffs State::from(self) before/after each command (initial_state.has_been_modified) so chatty subscribers can opt into state-change-only notifications, with a whitelist of always-delivered override events; Global lazy_static registries in komorebi/src/lib.rs (SUBSCRIPTION_SOCKETS: HashMap, SUBSCRIPTION_PIPES: HashMap, TCP_CONNECTIONS) guarded by parking_lot Mutexes — subscriptions survive because they are data, not connections; Pause gating in the read loops (not in process_command): when wm.is_paused only TogglePause/State/GlobalState/Stop are processed, everything else silently dropped; Hotwatch (notify-based file watcher) for config hot-reload — a separate control channel from the socket: watch_configuration unwatches-then-watches to avoid duplicate registrations; state persistence across restarts via komorebi.state.json in temp dir + the low-level ApplyState socket message Gotchas: Unix domain sockets DO work on Windows (uds_windows crate) — but socket files must be manually unlinked before bind; window_manager.rs:139-148 removes the old komorebi.sock with a comment that Path::exists() is unreliable on Windows when running under IntelliJ, so it attempts remove_file and ignores only NotFound; No reply framing protocol exists — acknowledged TODO at process_command.rs:2344: replies are 'whole-json objects... termination is signalled by socket shutdown'. Clients must half-close (shutdown write) and read to EOF; both sides use 1-second read/write timeouts as the real failure backstop; Named-pipe subscription inverts client/server: the SUBSCRIBER must create \\.\pipe\ first; komorebi connects to it with miow and errors out with a help message if the pipe doesn't exist yet (process_command.rs:1988-1990); Pipe-death detection relies on matching raw OS error codes 2 (ERROR_FILE_NOT_FOUND) and 232 (ERROR_NO_DATA 'the pipe is being closed') on write — any other write error keeps a dead pipe subscribed; The TCP command path reads into a fixed 1024-byte buffer with a single read() call (process_command.rs:2387) — a SocketMessage longer than 1KB (e.g. Theme or ApplyState) will be truncated and disconnect the client; the UDS path has no such limit since it uses line reading; Daemon-side listener thread is wrapped in an infinite respawn loop specifically because the accept loop can die; each client connection also gets stream.set_read_timeout(1s) so lingering clients can't pin threads; wm.try_lock_for(1s) in read_commands_uds: if the WM lock is held too long the command is silently DROPPED with only a warning log — CLI gets no error because send_message is fire-and-forget; Foreground-permission dance at startup: AllowSetForegroundWindow retried 5 times + WindowsApi::foreground_lock_timeout() — without this, focus commands arriving over IPC from a background process can't steal foreground (classic Win32 SetForegroundWindow restriction); komorebi ignores all commands/events when the associated virtual desktop isn't active (process_command.rs:197-206, current_virtual_desktop check) — commands sent on another vdesktop are dropped, not queued; StaticConfigSchema handler in process_command.rs:2257-2265 is a copy-paste bug: it returns the SocketMessage schema, not the StaticConfig schema (the CLI works around it by generating that schema locally in komorebic); While paused, the daemon whitelists only TogglePause/State/GlobalState/Stop — and this filter lives in the socket read loops, duplicated for UDS and TCP, not in process_command itself; Every notification carries the entire serialized WM state (containers, workspaces, monitors), and UDS delivery opens a new connection per subscriber per event — simple and robust but O(state) per keypress; the filter_state_changes option exists precisely because of this firehose; Ctrl-C/stop handling dumps state to temp komorebi.state.json and replays it via ApplyState on next start; stop() also connects to every subscriber socket and shuts it down, then deletes komorebi.sock; komorebic start/stop don't use the socket for process management — they shell out to PowerShell Start-Process/Stop-Process, poll with sysinfo for up to 3 attempts, and fall back to running komorebi.exe in the foreground to surface stderr Minimal-WM essentials: Essential for a CLI-driven minimal tiling WM (the komorebic model, where whkd/AHK bind hotkeys to CLI invocations): (1) one local listener — a uds_windows UnixListener on a well-known socket path in %LOCALAPPDATA% (or, equally valid and simpler, a loopback-only TcpListener or a named-pipe server; komorebi proves AF_UNIX works fine on Windows but nothing about the design depends on it); (2) a single serde-tagged Command enum shared between daemon and CLI via a small client crate — this one type IS the protocol, the CLI arg surface, and (free with schemars) the documentation; (3) newline-delimited JSON request framing plus reply-then-close for queries — trivially debuggable with any socket tool and avoids inventing a framing protocol; (4) the accept-thread + thread-per-connection + Arc> funnel with a bounded lock wait; (5) a thin clap CLI where each subcommand constructs one enum variant and calls send_message/send_query; (6) the AllowSetForegroundWindow/foreground-lock-timeout startup dance, without which IPC-driven focus commands silently fail; (7) stale-socket unlink before bind and socket cleanup on stop. A no-console wrapper binary (or building the CLI with windows_subsystem="windows") is practically essential too, or every hotkey flashes a console. Optional polish: the TCP remote-control listener, the entire pub/sub notification system (both UDS and named-pipe flavors, SubscribeOptions filtering, full-state broadcasting) — only needed once you want status bars or external integrations; schema-generation commands; batch sends; Hotwatch config hot-reload; state dump/ApplyState restart persistence; process-management subcommands (start/stop/kill via PowerShell); and the ~150 long-tail commands (borders, stackbars, animations, transparency, per-workspace rules). A minimal WM needs perhaps 25-30 verbs: focus/move/stack/resize in 4 directions, workspace focus/send, layout change, retile, toggle-float/monocle/pause, state/query, stop. Note the alternative: built-in hotkeys (RegisterHotKey in the daemon) would eliminate the CLI+socket+wrapper machinery entirely, at the cost of config-driven rebinding flexibility — komorebi's socket design exists precisely to keep keybindings out of the WM core, and it also gets you scriptability (any language can drive the WM) for free. ## Configuration and rules (komorebi.json static config, window matching rules, application-specific configurations, hot-reload) komorebi's entire user-facing configuration is one serde struct, `StaticConfig` (komorebi/src/static_config.rs), deserialized from komorebi.json. Every field is `Option` so any subset can be specified; old field names are kept alive via `#[serde(alias)]` (e.g. `float_rules` -> `ignore_rules`, `active_window_border*` -> `border*`) and helper fns print deprecation/EOL warnings by raw substring scan of the file. The shape covers: global behaviour knobs (window_container_behaviour Create/Append, float_override, floating_layer_behaviour, four Placement fields, cross_monitor_move_behaviour, cross_boundary_behaviour, monocle_focus_behaviour, unmanaged_window_operation_behaviour, mouse_follows_focus, resize_delta, window_hiding_behaviour Hide/Minimize/Cloak, window_handling_behaviour Sync/Async, min window width/height), visuals (border width/offset/style/implementation/colours, transparency + alpha, stackbar, animation incl. ghost movement, theme, wallpaper), layout defaults (default_workspace_padding, default_container_padding, layout_defaults per layout name, global_work_area_offset), a `monitors: Vec` tree where each monitor has `workspaces: Vec` (name, layout, layout_options, threshold-keyed rule maps layout_rules/layout_options_rules/work_area_offset_rules/window_container_behaviour_rules where the key is a container-count threshold, paddings, tile on/off, layout_flip, float_override, initial_workspace_rules and workspace_rules for app->workspace assignment, per-workspace wallpaper), monitor_index_preferences/display_index_preferences for stable monitor ordering, and about ten lists of `MatchingRule` window rules plus `app_specific_configuration_path` (single path or list). All paths in the config are `ResolvedPathBuf` (core/pathext.rs) which expands `%VAR%`, `$Env:VAR`, `$VAR`, `~` and `$HOME` per path component at deserialize time, leaving unresolvable variables literal. Config loading has three phases: `preload()` (read + `apply_globals()` + construct WindowManager + bind komorebi.sock + register file watcher), `postload()` (map monitor configs onto live monitors and populate workspace rules), and `reload()` (both again on change). Window rules are `MatchingRule` (core/config_generation.rs): either `Simple(IdWithIdentifier)` or `Composite(Vec)` where composite is a logical AND of all clauses. An `IdWithIdentifier` is {kind: Exe|Class|Title|Path (core/mod.rs, with lowercase serde aliases), id: String, matching_strategy}. `MatchingStrategy` offers Legacy, Equals, StartsWith, EndsWith, Contains, Regex and the four negations (DoesNotEqual/-StartWith/-EndWith/-Contain). When strategy is omitted it defaults to Legacy, whose semantics differ by kind: Exe/Path use exact equality but Title/Class match if the string starts_with OR ends_with the id. Regex patterns are compiled once at config load into the global `REGEX_IDENTIFIERS: HashMap` keyed by pattern string. `apply_globals()` pushes each rule list into a corresponding global `Mutex>`: IGNORE_IDENTIFIERS (ignore_rules), MANAGE_IDENTIFIERS (manage_rules), FLOATING_APPLICATIONS, TRAY_AND_MULTI_WINDOW_IDENTIFIERS, LAYERED_WHITELIST, OBJECT_NAME_CHANGE_ON_LAUNCH (+ a separate regex title-ignore list), TRANSPARENCY_BLACKLIST, SLOW_APPLICATION_IDENTIFIERS, NO_TITLEBAR. Consumption happens in window.rs: `should_manage` -> `window_is_eligible` checks hardcoded PERMAIGNORE_CLASSES, then evaluates ignore vs manage (a manage_rules match overrides an ignore_rules match), then the core Win32 heuristic: a window tiles only if it has WS_CAPTION + WS_EX_WINDOWEDGE, lacks WS_EX_DLGMODALFRAME, and lacks WS_EX_LAYERED unless whitelisted via layered_applications or already tracked by the transparency manager — unless force-managed, a WSL2 GUI process, or a remove_titlebar_applications match, which bypass the style heuristic. `should_act()` iterates all rules and returns the last match; slow_application matches cause a literal thread::sleep of slow_application_compensation_time before proceeding. The ASC (application-specific configuration) system is the community workaround database (github.com/LGUG2Z/komorebi-application-specific-configuration), pointed at by `app_specific_configuration_path`. The modern JSON format (core/asc.rs) is a BTreeMap of app name -> `AscApplicationRules` with eight optional rule lists: ignore (never tile: splash screens, PiP, popups), manage (force-tile windows that fail the style heuristic), floating (manage as floating), transparency_ignore, tray_and_multi_window (apps that minimize/close to tray or spawn multiple windows, so hide events must be handled specially), layered (apps like Steam whose windows carry WS_EX_LAYERED and would otherwise be rejected), object_name_change (rare apps that only announce themselves via EVENT_OBJECT_NAMECHANGE at launch, so that event must be treated as a Show), and slow_application (apps whose event notifications lag, needing a sleep before querying state). A top-level "$schema" key is absorbed by the untagged `AscApplicationRulesOrSchema::Schema(String)` variant. `handle_asc_file` in static_config.rs merges every ASC entry into the same global rule vectors as user rules; it also still parses the legacy YAML format (`ApplicationConfiguration` with `options: [object_name_change|layered|tray_and_multi_window|force|border_overflow]` plus `ignore_identifiers`, alias float_identifiers), and `ApplicationConfigurationGenerator` can emit PowerShell/AutoHotkey scripts of `komorebic.exe` identify-* commands from it — the historical pre-static-config mechanism. `border_overflow` is fully deprecated: it is a no-op in ASC handling and `unreachable!()` in the generator, so that identifier category patches nothing anymore. Hot-reload works by loopback IPC rather than direct mutation: `preload()` registers a Hotwatch (notify/ReadDirectoryChangesW) watcher on komorebi.json; on Modify OR Remove events it connects to the komorebi.sock AF_UNIX socket (uds_windows) and writes `SocketMessage::ReloadStaticConfiguration(path)`, so the reload executes serialized on the main command-processing thread. `reload()`/`postload()` re-map monitor configs to live monitors — first by display_index_preferences (config index -> monitor serial_number_id or device_id string), then first-fit over unclaimed configs — cache preferred configs for currently disconnected displays via monitor_reconciliator::insert_in_monitor_cache (for hot-plug), rebuild WORKSPACE_MATCHING_RULES (cleared and repopulated, with initial_only flag distinguishing initial_workspace_rules from workspace_rules) and call enforce_workspace_rules. Notably `apply_globals()` never clears the identifier vectors (populate_rules only appends unseen rules), so deleting a window rule from komorebi.json and reloading does not un-register it until restart. The reverse direction also exists: `StaticConfig::from(&WindowManager)` serializes runtime state back into a config for `komorebic generate-static-config`. Windows APIs: AF_UNIX sockets on Windows via uds_windows (UnixListener/UnixStream on %LOCALAPPDATA%\komorebi\komorebi.sock) — IPC channel used both for komorebic commands and for the hot-reload loopback message; ReadDirectoryChangesW (indirectly, via hotwatch/notify crate) — watching komorebi.json for edits; GetWindowLongPtrW window styles consulted by rule system: WS_CAPTION + WS_THICKFRAME (also toggled by remove_titlebar_applications), WS_EX_WINDOWEDGE (required for eligibility), WS_EX_LAYERED (rejected unless layered_applications whitelisted), WS_EX_DLGMODALFRAME (always rejected); EVENT_OBJECT_NAMECHANGE WinEvent — object_name_change_applications treat this event as a window-Show signal at app launch; a regex title ignore list suppresses false positives from ordinary title changes; DWM cloaking: DwmGetWindowAttribute(DWMWA_CLOAKED) for the is_cloaked eligibility check, and the undocumented SetCloak function for HidingBehaviour::Cloak (vs ShowWindow SW_HIDE / SW_MINIMIZE for the deprecated Hide/Minimize behaviours); SystemParametersInfo active window tracking (via WindowsApi::enable/disable_focus_follows_mouse) for the end-of-life FocusFollowsMouseImplementation::Windows option; GetWindowRect (WindowsApi::window_rect) for minimum_window_width/height eligibility thresholds; Monitor device identification (device_id, serial_number_id from EnumDisplayDevices/DisplayConfig APIs elsewhere) — consumed here by display_index_preferences to pin configs to physical displays Mechanisms: Single Option-heavy serde struct (StaticConfig) as the whole config surface; apply_globals() fans values out into global statics (AtomicI32/AtomicBool, Mutex>, RwLock maps) that manager threads read — config is not passed around, it is published to globals; MatchingRule = Simple | Composite(AND of clauses); IdWithIdentifier {kind: Exe/Class/Title/Path, id, matching_strategy}; strategies Equals/StartsWith/EndsWith/Contains/Regex + negations + Legacy; regexes precompiled at load into REGEX_IDENTIFIERS HashMap keyed by pattern string; Each rule category is a global Mutex> (IGNORE_IDENTIFIERS, MANAGE_IDENTIFIERS, FLOATING_APPLICATIONS, TRAY_AND_MULTI_WINDOW_IDENTIFIERS, LAYERED_WHITELIST, OBJECT_NAME_CHANGE_ON_LAUNCH, TRANSPARENCY_BLACKLIST, SLOW_APPLICATION_IDENTIFIERS, NO_TITLEBAR); window.rs should_act() linearly scans and returns the last match; user rules and ASC entries merge into the same lists; Eligibility pipeline in window_is_eligible: permaignore classes -> ignore vs force-manage (manage wins) -> layered whitelist -> WSL2/no-titlebar bypasses -> style heuristic (CAPTION+WINDOWEDGE, no DLGMODALFRAME, no LAYERED unless allowed) with managed_override as final OR; Hot-reload by loopback IPC: file watcher thread writes SocketMessage::ReloadStaticConfiguration to komorebi.sock so reload runs serialized on the main command loop instead of mutating WM state cross-thread; Monitor-config mapping: display_index_preferences (config idx -> serial/device id) first, then first-fit over unclaimed configs; unused preferred configs are pre-instantiated as blank monitors in monitor_reconciliator's cache so hot-plugged displays pick up the right config; Workspace assignment rules: WorkspaceMatchingRule {monitor_index, workspace_index, matching_rule, initial_only} in a global list, rebuilt (cleared) on each reload, enforced by wm.enforce_workspace_rules(); Threshold-keyed rule maps (HashMap) for layout_rules, layout_options_rules, work_area_offset_rules, window_container_behaviour_rules — highest threshold <= container count wins; Env-var expansion at deserialize time via ResolvedPathBuf/serde_as: %VAR%, $Env:VAR, $VAR, ~/$HOME->USERPROFILE, resolved per path component, unresolved left literal; Bidirectional config: StaticConfig::from(&WindowManager) regenerates a config from live state (generate-static-config); ApplicationConfigurationGenerator converts legacy YAML ASC into pwsh/ahk komorebic command scripts Gotchas: Legacy matching strategy (the default when matching_strategy is omitted): Exe/Path compare with equality but Title/Class match on starts_with OR ends_with — a surprising implicit prefix/suffix match kept for backward compatibility; manage_rules override ignore_rules: window_is_eligible returns false only if should_ignore && !managed_override, and managed_override also bypasses the entire window-style heuristic; Layered (WS_EX_LAYERED) windows are rejected by default because they generate duplicate FocusChange events that send redrawing haywire; apps like Steam need the layered_applications whitelist; Hide events are always allowed through for transparency-tracked windows to avoid 'ghost tiles'; Hot-reload watcher must handle both Modify AND Remove events: comment in preload() notes Notepad sends NoticeWrite while (Neo)Vim sends NoticeRemove 'presumably because of the use of swap files'; apply_globals() never clears the identifier vectors and populate_rules only appends unseen rules — deleting a window rule from komorebi.json and hot-reloading does NOT unregister it; a restart is required (WORKSPACE_MATCHING_RULES, by contrast, IS cleared and rebuilt); slow_application_identifiers cause a synchronous thread::sleep(SLOW_APPLICATION_COMPENSATION_TIME, default in ms) inside the eligibility check itself — the event thread literally blocks to let slow apps (e.g. games, JVM apps) settle; The core tiling heuristic is style-based: WS_CAPTION + WS_EX_WINDOWEDGE required, WS_EX_DLGMODALFRAME always rejected; WSL2 GUI apps need a hardcoded process whitelist (WSL2_UI_PROCESSES) because WSLg windows lack normal styles; remove_titlebar_applications windows keep matching after CAPTION is stripped via the same bypass; should_act() returns the LAST matching rule in list order (it keeps overwriting matching_rule), so later rules win for match attribution; Socket file cleanup treats ErrorKind::NotFound as success with the comment that ::exists() is unreliable on Windows under IntelliJ; The '$schema' key in applications.json is absorbed by an untagged enum variant (AscApplicationRulesOrSchema::Schema(String)) rather than being skipped by key name; object_name_change_applications treat EVENT_OBJECT_NAMECHANGE as a launch/Show signal (very rare apps), and object_name_change_title_ignore_list regexes stop routine title changes from spawning phantom Show events; BorderImplementation::Windows is silently refused on Windows 10 (error log, keeps old value); Scrolling layout is force-downgraded to Columns with a warning when more than one monitor is attached; border_overflow_applications still parses in the config for compat but is dead: ApplicationOptions::BorderOverflow is a no-op in ASC handling and unreachable!() in the command generator; In reload() monitor.work_area_offset is only applied if the monitor currently has None (preserving runtime changes), whereas postload() overwrites it unconditionally — subtle behavioural difference between first load and hot reload; Every path in the config resolves env vars per component (%VAR%, $Env:VAR, $VAR, ~, $HOME); a component that is not a resolvable variable is passed through literally, so typos silently become literal directory names Minimal-WM essentials: Essential: (1) the window-eligibility heuristic and its escape hatches — on Windows you cannot tile every HWND, so a minimal WM needs the style-based filter (WS_CAPTION + WS_EX_WINDOWEDGE, reject WS_EX_DLGMODALFRAME and WS_EX_LAYERED, minimum size, has-title, not-cloaked) plus ignore rules AND force-manage rules to correct the heuristic in both directions; (2) rule matching by exe/class/title with at least an Equals strategy (Regex is a cheap, high-value addition; Composite/negations/Legacy are optional); (3) some subset of the ASC quirk categories, because real Windows apps break naive tiling constantly — at minimum ignore rules (splash screens, popups, PiP), tray_and_multi_window handling (otherwise minimize-to-tray apps leave dead tiles), and the layered whitelist (otherwise Steam-class apps never tile); shipping or consuming the community applications.json is the pragmatic way to get this coverage without rediscovering hundreds of per-app quirks; (4) a basic config file declaring workspaces per monitor with a layout and padding, and the window_hiding_behaviour choice (Cloak is the only good option; the others are deprecated for cause). Optional polish: hot-reload (nice DX but restart works), object_name_change and slow_application compensation (fixes a handful of rare apps), floating_applications and all Placement options, workspace assignment rules, threshold-keyed rule maps, monitor/display index preferences and the hot-plug monitor cache, env-var path expansion, borders/transparency/stackbar/animation/theme/wallpaper, remove_titlebar_applications (heavily discouraged even upstream), the legacy YAML ASC format and pwsh/ahk generators (pure backward compatibility), generate-static-config round-tripping, and JSON schema generation. ## Ancillary managers (border_manager, monitor_reconciliator, reaper, focus_manager, transparency_manager, stackbar_manager, animation, theme_manager) in komorebi Komorebi surrounds its core event loop with eight ancillary managers that all follow the same architectural template: a module-level `static CHANNEL: OnceLock<(Sender, Receiver)>` (crossbeam bounded channel, 20-50 slots), a `send_notification()` helper that `try_send`s and logs-and-drops on a full channel, and a `listen_for_notifications(Arc>)` that spawns a dedicated thread running `handle_notifications` in an infinite restart-on-error loop. Each manager thread locks the shared WindowManager, snapshots the state it needs, and acts on the OS. All are started once from main.rs (lines 329-335). This decouples slow/cosmetic OS work (painting borders, alpha fades, monitor re-enumeration) from the latency-sensitive WinEvent processing loop, and makes each concern crash-isolated (a panicking manager thread just restarts). border_manager draws focus-colored frames. It has two implementations: `BorderImplementation::Windows` simply sets DWMWA_BORDER_COLOR accents on the app windows themselves (cheap, Win11-only, no extra windows); `BorderImplementation::Komorebi` creates one real top-level window per container ("komoborder-{id}" class, WS_EX_TOOLWINDOW|WS_EX_TOPMOST|WS_EX_NOACTIVATE, WS_POPUP), each owned by its own thread running a GetMessage loop, painted with Direct2D (ID2D1HwndRenderTarget, solid brushes per WindowKind: Single/Stack/Monocle/Unfocused/UnfocusedLocked/Floating). The manager thread diff-checks a snapshot of the monitor Ring against the previous notification to skip redundant work, then creates/repositions/destroys borders to mirror the focused workspace of every monitor. Live movement tracking does NOT go through the manager: the WinEvent hook callback (windows_callbacks.rs) forwards EVENT_OBJECT_LOCATIONCHANGE/EVENT_OBJECT_DESTROY directly to the tracked window's border HWND via SendNotifyMessageW, and the border's own WndProc repositions and re-renders itself; SetWindowPos with the tracked hwnd as the insert-after reference keeps the border glued just above its window in the Z-order. monitor_reconciliator exists because Windows actively fights a WM during display topology changes: when a monitor disconnects (undock, lid close, sleep, DP link flap), the OS minimizes windows, reshuffles them onto surviving monitors, and invalidates HMONITOR handles, which would silently corrupt komorebi's monitor->workspace->container state. It runs two threads: (1) hidden.rs creates an invisible message-only-style window ("komorebi-hidden", WS_DISABLED/WS_EX_NOACTIVATE) on its own message-loop thread that registers for WM_DISPLAYCHANGE (resolution/scaling), WM_SETTINGCHANGE+SPI_SETWORKAREA (work area/taskbar changes), WM_DEVICECHANGE with RegisterDeviceNotificationW for monitor/display-adapter/video-output device interface GUIDs (hotplug arrival/removal), WM_POWERBROADCAST (suspend/resume + lid switch via RegisterPowerSettingNotification GUID_LIDSWITCH_STATE_CHANGE), and WM_WTSSESSION_CHANGE (lock/unlock via WTSRegisterSessionNotification); it translates these into a 7-variant MonitorNotification enum. (2) The consumer thread handles them: WorkAreaChanged/ResolutionScalingChanged re-query MonitorInfo and re-tile if the cached work_area_size/size differ; DisplayConnectionChange re-enumerates displays (win32-display-data crate, with 5 retries at 150ms), matches state monitors to attached devices by serial_number_id or device_id, and reconciles. Removal is debounced: it waits 3 seconds (releasing the WM lock) and re-queries, because Windows fires spurious removal events; if monitors came back it just restores minimized windows. On verified removal it minimizes focused windows on dead monitors (so Windows won't teleport them), snapshots each removed Monitor (full workspace tree) into a MONITOR_CACHE keyed by serial/device id, prunes known_hwnds and workspace rules, and refocuses monitor 0 if needed. On (re)connect, load_monitor_information detects new device ids and the cache is consulted so a re-docked monitor gets its exact workspace layout back (with dead windows retained-out), then retile_all runs twice to shake out DPI jank. It also stamps LAST_DISPLAY_CHANGE_TIMESTAMP so process_event ignores OS-initiated minimize events within a 10-second grace period, and suspends itself between lock/sleep and unlock/resume. reaper is the garbage collector for dead windows: many windows die without komorebi ever receiving a Destroy/Hide WinEvent (crashed apps, elevated windows, apps that just stop being visible), which would leave ghost entries in the layout. It runs two threads: a poller (find_orphans) that wakes every 20ms and checks every hwnd in HWNDS_CACHE (a mirror of wm.known_hwnds mapping hwnd -> (monitor_idx, workspace_idx), kept in sync by WindowManager::update_known_hwnds) with IsWindow, plus a visibility heuristic (IsWindowVisible) to catch Word's invisible zombie windows; and a consumer thread that receives the orphan batch, locks the WM, removes each window from its recorded workspace via workspace.remove_window, re-tiles only if it was on a focused workspace, notifies the border_manager and IPC subscribers, and persists the surviving hwnd list to komorebi.hwnd.json. The remaining managers are simpler: focus_manager is a trivial thread that focuses a hwnd asynchronously (used post-animation for mouse-follows-focus); transparency_manager walks the state and applies per-window alpha via layered windows (unfocused windows get TRANSPARENCY_ALPHA, focused/monocle/maximized get opaque, with a blacklist); stackbar_manager draws GDI tab bars over stacked containers (one window per container on its own thread, color-keyed layered window, DrawTextW tabs, WM_LBUTTONDOWN switches stack focus); animation is not a channel manager but a thread-per-animation engine (AnimationEngine::animate spawns a thread that ticks a RenderDispatcher at ANIMATION_FPS with easing, keyed cancellation via ANIMATION_MANAGER map, plus DWM-thumbnail "ghost" windows for movement); theme_manager converts Catppuccin/Base16/custom palettes into the atomic color statics consumed by border/stackbar managers and force-refreshes them. Windows APIs: CreateWindowExW with WS_EX_TOOLWINDOW|WS_EX_TOPMOST|WS_EX_NOACTIVATE + WS_POPUP - border overlay windows that never take focus or appear in alt-tab (windows_api.rs create_border_window); Direct2D: D2D1CreateFactory (multi-threaded), ID2D1Factory::CreateHwndRenderTarget, CreateSolidColorBrush, DrawRoundedRectangle/DrawRectangle, BeginDraw/EndDraw/Resize - GPU-accelerated border frame rendering (border_manager/border.rs); DwmEnableBlurBehindWindow with an off-screen 1px CreateRectRgn blur region - makes the border window's background truly transparent without black fringing on rounded corners (PowerToys trick); DwmSetWindowAttribute DWMWA_BORDER_COLOR / DWMWA_COLOR_NONE - the alternative 'Windows' border implementation that tints the native Win11 frame instead of creating overlay windows; InvalidateRect / ValidateRect - trigger and acknowledge WM_PAINT on border windows; SetWindowPos with tracked-window hwnd as insertAfter reference (set_border_pos) - keeps border directly above its window in Z-order; SWP_NOACTIVATE|NOREDRAW|SHOWWINDOW flags; SendNotifyMessageW forwarding EVENT_OBJECT_LOCATIONCHANGE / EVENT_OBJECT_DESTROY winevent constants as window messages from the SetWinEventHook callback to border HWNDs - live border tracking during window drags; PostMessageW with custom WM_USER messages (WM_UPDATE_BRUSHES, WM_ANIMATE_RECT carrying a Box ownership transfer) - cross-thread requests marshalled onto the border's own message loop; GetMessageW/TranslateMessage/DispatchMessageW loops - one per border window, per stackbar window, and for the hidden monitor-event window; RegisterDeviceNotificationW with DBT_DEVTYP_DEVICEINTERFACE for GUID_DEVINTERFACE_MONITOR, GUID_DEVINTERFACE_DISPLAY_ADAPTER, GUID_DEVINTERFACE_VIDEO_OUTPUT_ARRIVAL -> WM_DEVICECHANGE (DBT_DEVICEARRIVAL/DBT_DEVICEREMOVECOMPLETE/DBT_DEVNODES_CHANGED/DBT_CONFIGCHANGED) - monitor hotplug detection; WM_DISPLAYCHANGE - resolution/scaling change detection; WM_SETTINGCHANGE with wparam SPI_SETWORKAREA - work-area changes (taskbar auto-hide tools like ButteryTaskbar); RegisterPowerSettingNotification GUID_LIDSWITCH_STATE_CHANGE + WM_POWERBROADCAST (PBT_APMSUSPEND, PBT_APMRESUMEAUTOMATIC/RESUMESUSPEND, PBT_POWERSETTINGCHANGE) - laptop lid open/close and sleep/resume; WTSRegisterSessionNotification + WM_WTSSESSION_CHANGE (WTS_SESSION_LOCK/UNLOCK) - session lock/unlock gating of the reconciliator; EnumDisplayMonitors/GetMonitorInfoW + win32-display-data device paths (serial_number_id, device_id) - display enumeration and stable monitor identity across HMONITOR churn; IsWindow - reaper liveness check for every managed hwnd every 20ms; IsWindowVisible - reaper zombie-window heuristic and border visibility checks; ShowWindow/ShowWindowAsync (SW_MINIMIZE, restore) - minimizing focused windows on disconnected monitors and restoring OS-minimized windows after reconnect; SetLayeredWindowAttributes LWA_ALPHA / GetLayeredWindowAttributes - transparency_manager per-window alpha on unfocused windows; SetLayeredWindowAttributes LWA_COLORKEY on WS_EX_LAYERED stackbar windows - color-keyed transparent stackbar background; GDI: CreateFontIndirectW, DrawTextW, CreatePen, CreateSolidBrush, RoundRect/Rectangle, SetTextColor/SetBkColor, GetDC/ReleaseDC - stackbar tab drawing; DwmRegisterThumbnail/DwmUpdateThumbnailProperties/DwmUnregisterThumbnail - live window preview 'ghost' during movement animations (animation/ghost.rs); GetForegroundWindow / IsZoomed - focus-kind and maximized-window decisions in border and transparency managers; GetWindowLongPtrW/SetWindowLongPtrW GWLP_USERDATA - stashing the Border struct pointer for the WndProc; GWL_STYLE/GWL_EXSTYLE filtering (WS_CHILD, WS_EX_TOOLWINDOW, WS_EX_NOACTIVATE) before forwarding move events to borders Mechanisms: Uniform manager template: static OnceLock crossbeam bounded channel + send_notification() with lossy try_send + dedicated listener thread wrapping handle_notifications in an infinite restart loop (crash isolation, no supervision tree needed); Shared state is one Arc> (parking_lot); manager threads lock it, snapshot/clone what they need (border_manager clones the whole monitor Ring), then drop the lock before doing OS calls where possible; Configuration knobs are lock-free globals: AtomicI32/AtomicU32/AtomicBool/AtomicCell statics (BORDER_WIDTH, colors, STACKBAR_MODE, TRANSPARENCY_ALPHA, ANIMATION_FPS...) written by config/theme code, read by manager threads; Border diffing: previous monitor-Ring snapshot + previous notification hwnd + pause/layer/pending-move-op edge cases decide whether to skip a notification entirely (avoids re-laying-out borders on every event); Thread-per-native-window: every border, stackbar, ghost host, and the hidden monitor window each own a thread with a Win32 message pump; the creating thread hands the struct back through an mpsc channel; window struct pointer lives in GWLP_USERDATA; Two border state maps: BORDER_STATE (container-id -> Box) and WINDOWS_BORDERS (tracking hwnd -> container-id), so both container-driven updates and hwnd-driven winevent forwarding can find the border; Border destruction protocol: Box::into_raw so the manager never drops the Border struct (the window's own thread owns it); is_destroying AtomicBool + 10ms sleep to let in-flight D2D renders finish; the userdata pointer is nulled and the render target released during WM_DESTROY, ahead of WM_NCDESTROY; Monitor identity: serial_number_id preferred over device_id (with duplicate-serial detection falling back to device_id) so monitors keep identity across HMONITOR/display-index churn; DISPLAY_INDEX_PREFERENCES let users pin indices; MONITOR_CACHE HashMap: full workspace-tree snapshot of every removed monitor, consumed once on reconnect to restore layout exactly (dead/moved windows filtered out via exe() check and known_hwnds); Transient-removal debounce: on apparent monitor removal, release all locks, sleep 3s, re-enumerate; if the count recovered treat it as a spurious event and just restore minimized windows; Grace-period timestamp (LAST_DISPLAY_CHANGE_TIMESTAMP, checked via display_change_in_progress(10s)) so the main event loop ignores OS-initiated SystemMinimizeStart events during display transitions; Reaper split into poller + actor: 20ms IsWindow polling over a mirrored hwnd->(monitor,workspace) cache (HWNDS_CACHE, refreshed wholesale by WindowManager::update_known_hwnds on every state change), batched orphan removal under one WM lock, border refresh only when a focused workspace changed, persisted hwnd list for crash recovery; Animation engine: thread per animation keyed by string, cooperative cancellation counters (init_cancel/latest_cancel_idx so the newest cancel wins), fixed-fps frame loop with easing progress, RenderDispatcher trait so the same engine drives position, transparency, and ghost-thumbnail animations; Theme manager fans out: converts palette to u32s, stores into border/stackbar atomic color statics, then triggers border force-update and stackbar refresh (borders must rebuild D2D brushes on their own threads via WM_UPDATE_BRUSHES) Gotchas: Windows fires spurious monitor-removal events: a DP monitor can 'disappear' for a moment (link retraining, sleep) and come back; komorebi waits 3 seconds and re-enumerates before believing a removal, and even for spurious ones must restore windows the OS already minimized; On real monitor disconnect Windows tries to move the focused window to another monitor on its own; komorebi preemptively minimizes focused windows on the dying monitor to stop the OS from relocating them; OS-initiated minimizes during display changes are indistinguishable from user minimizes at the WinEvent level; komorebi uses a 10-second wall-clock grace period after any display-change notification to suppress minimize handling (process_event.rs:335); HMONITOR handles and display indices are unstable across hotplug/suspend; stable identity requires the device path/serial from DisplayConfig, and even serial numbers can be duplicated across identical monitors (DUPLICATE_MONITOR_SERIAL_IDS falls back to device_id); display enumeration can transiently fail mid-hotplug: attached_display_devices retries 5 times with 150ms sleeps before giving up, and skips reconciliation entirely if zero devices are returned ('to avoid breaking state'); WINWORD.EXE keeps an invisible WS_EX_LAYERED window alive after closing one of several documents, so IsWindow alone can't detect the zombie; the reaper also reaps invisible windows - but must NOT do that when HidingBehaviour::Hide is active, because komorebi itself makes legitimate windows invisible (reaper.rs:186-199 calls the hack out with an extended rant); Border transparency needs the PowerToys DwmEnableBlurBehindWindow hack with a blur region positioned off-screen (-GetSystemMetrics(SM_CXVIRTUALSCREEN)-8) to remove black pixels around rounded corners; the comment admits nobody knows exactly why the value works; Only the thread that owns the D2D target may mutate it, so brush rebuilds are posted to the border's own message loop under a private WM_USER code; a manager-thread swap of the target could otherwise land in the middle of a WndProc paint; Border Box must never be dropped normally - destroy_border converts to a raw pointer and lets the window's message-loop thread dispose of it after the window closes (explicit IMPORTANT comment at border_manager/mod.rs:783); Release the D2D target and null out the userdata pointer while handling WM_DESTROY; by the time WM_NCDESTROY arrives the presenter is already torn down, and anything still holding the target is holding freed state; GetForegroundWindow lags behind the actual focus change: SystemForeground winevents arrive before the foreground value updates, so the winevent callback fires an extra border notification once the system has caught up (windows_callbacks.rs:122-130); Refocusing a window after an admin/elevated window leaves the border stuck at Unfocused because WM state is unchanged; the border manager has an explicit edge case checking the notification hwnd's current border kind; EVENT_OBJECT_LOCATIONCHANGE forwarding must filter out WS_CHILD/WS_EX_TOOLWINDOW/WS_EX_NOACTIVATE windows or borders would chase tooltip/child-window move events; WM_SETTINGCHANGE with SPI_SETWORKAREA is the only signal for taskbar auto-hide utilities changing the work area; WM_DISPLAYCHANGE alone misses it; Laptop lid close does not reliably produce device-removal events; komorebi listens for the lid-switch power setting GUID and treats lid transitions as DisplayConnectionChange; Border channel notifications are lossy by design (try_send, drop-on-full) - every consumer re-reads full WM state per notification, so dropped notifications only delay convergence, never corrupt it; Reaper polls at 20ms; the visibility heuristic plus polling means a window that momentarily reports !IsWindowVisible can be reaped incorrectly if hiding behaviour assumptions are violated; retile_all is called twice after monitor changes 'to fix DPI/resolution related jank' - one pass is not enough when per-monitor DPI changes window sizes as they move Minimal-WM essentials: ESSENTIAL: (1) monitor_reconciliator - a Windows tiling WM that ignores display topology changes corrupts its state the first time a laptop docks/undocks, sleeps, or changes DPI; the minimum viable subset is the hidden message window listening for WM_DISPLAYCHANGE + WM_DEVICECHANGE + WM_POWERBROADCAST resume, re-enumeration with stable device-id matching, work-area/size refresh, and removal/addition of monitors with a debounce; the workspace-cache-restore, session-lock gating, and lid-switch GUID are polish you can add later, but the 'verify removal after 3s' debounce and the minimize-suppression grace period are load-bearing against real OS behavior. (2) reaper - WinEvents genuinely miss window deaths (crashed processes, elevated windows, tray apps), so ghost entries accumulate without a liveness sweep; a minimal version is small: a mirror map of managed hwnds, an IsWindow poll (20ms in komorebi; 100-250ms is fine), and batched removal + retile. STRONGLY RECOMMENDED: border_manager, but in its cheap form - on Windows 11 the DWMWA_BORDER_COLOR 'Windows' implementation gives a focused-window indicator in ~30 lines with no extra windows, threads, or Direct2D; without any indicator a tiling WM is genuinely hard to use. The full komorebi-style D2D overlay-window border system (per-container windows, message pumps, winevent forwarding, destruction protocol) is the single largest source of complexity and gotchas in this list and is entirely skippable polish. SKIPPABLE: focus_manager (only needed as an async helper for animations; a minimal WM focuses synchronously), transparency_manager (pure eye candy; also forces you into layered-window alpha management), stackbar_manager (only meaningful if you implement container stacking with tabs, and explicitly tied to the no-bar-UX komorebi wants; you said no bar), animation/* (pure polish, and its thread-per-animation + cancellation + ghost-thumbnail machinery leaks complexity into window.rs and border code), theme_manager (config sugar over border/stackbar colors; pointless without the komorebi border implementation). Architecturally, the one pattern worth copying wholesale even for a minimal WM is the channel + dedicated-thread + restart-loop template and the rule that consumers re-derive everything from a state snapshot per notification (lossy channels stay safe); the one pattern to avoid copying is per-window native message-pump threads unless you actually build overlay borders.