A scrollable tiling window manager for Windows.
hero.webm
Most Windows tilers use tree or BSP layouts. LeopardWM is scroll-first: windows sit on a horizontal strip, and your monitor acts as a viewport that scrolls over them. Navigation stays spatially consistent as windows are added — you move through context instead of constantly rebuilding split trees.
- Vsync-aligned animations — smooth scrolling powered by a
DwmFlush-driven animation engine - First-class touchpad gestures — three-finger swipes drive focus and scroll out of the box
- Disables Windows 11 Snap Layouts on managed windows — no more accidental edge-snap when you drag a tile
- Auto-detected per-window rounded corners and high-contrast/reduced-motion/battery awareness — system integration that respects user settings
- WebView2 settings GUI with Mica backdrop and live theme switching — not just a config file
- GPL-3.0 — commercial use without a paid license, written in safe Rust
Overview — zoom out to a map of your non-empty workspaces and jump anywhere
overview.webm
Workspaces — per-monitor workspaces; switch between them and move windows across
workspaces.webm
Tabbed columns — collapse a column into a tab strip, only the active tab fills the rect
tabbed.webm
Scratchpad — stash a window out of the layout and summon it back as a floating overlay
scratchpad.webm
Sticky windows — pin a window so it follows you across workspaces, tiled or floating
sticky.webm
A few deliberate non-features, so you know what you're getting:
- Scroll-first, not multi-layout. No BSP, no DWindle, no Equal/Stair/UltrawideVerticalStack — and we won't add them. niri (Wayland) and PaperWM (GNOME) stay scrolling-only by choice; the horizontal strip is the identity. If you want 9 layout variants, komorebi is the right tool.
- No Virtual Desktop bridging. Per-monitor workspaces don't map cleanly to Windows' global Virtual Desktops, and the only library that bridges them (
winvd) breaks every 3-6 months on Windows feature updates. Instead,Win+Ctrl+Arrowis intercepted and routed to LeopardWM's workspace prev/next so the native muscle memory still works. - Named-pipe IPC, not WebSocket. Lower latency, no port allocation, no firewall prompts. If browser-based bar integration becomes a real ask, we'll add a thin bridge rather than make the daemon serve sockets directly.
- Multi-monitor workspaces with monitor-aware focus and move (9 workspaces per monitor)
- Global hotkeys with live config reload
- Smooth scroll animations with layout transition effects (vsync-locked)
- Touchpad gestures with configurable swipe actions
- Drag-and-drop column reorder (Shift+drag to merge windows)
- Tabbed columns — toggle a column between vertical-stack and tab-strip mode (
Ctrl+Alt+T); only the active tab fills the column rect, the rest sit in a clickable strip above - Scratchpad — stash the focused window out of the layout (
Ctrl+Alt+Shift+S) and summon it back as a floating, centered overlay on demand (Ctrl+Alt+S); stash it again to release it back to tiling - Sticky windows — pin a window (
Ctrl+Alt+Y) so it follows you across workspaces, keeping its current mode: a tiled window stays tiled (a column you can cycle to), a floating window stays a floating overlay - Overview mode —
Ctrl+Alt+Spaceopens a map of the monitor's non-empty workspaces; click a window card to jump to it, click a row to switch workspace, or drive it with arrows/Enter/digits - Per-app window rules — float, ignore, or tile by class/title/executable, plus per-app open behavior: target workspace, initial column width, open maximized
- Floating and fullscreen toggles
- Width and height presets with column equalization, maximize-column, center-column
- Active focus border with auto-detected rounded corners
- System tray with pause, reload, settings, and diagnostics
- WebView-based settings GUI (Mica backdrop, live theme switching, dark mode)
- Safe mode for troubleshooting (
--safe-mode) - Built-in diagnostics (
lwm doctor) - Workspace persistence and session recovery
- Autostart via Registry, configurable from CLI / Settings / tray
- In-app update notifier — daily check against GitHub Releases, opt-out
- Windows 11 Snap Layouts disabled for managed tiled windows
- Battery-aware: animations auto-disable on battery / power saver
- Respects Windows reduced-motion and high-contrast settings
- DPI-aware gap and border scaling per-monitor
winget install jcardama.LeopardWM # Windows Package Manager
scoop install extras/leopardwm # Scoop (after `scoop bucket add extras`)Both fetch the signed MSI installer and put leopardwm, leopardwm-cli, and lwm on your PATH. winget upgrade / scoop update keep you on the latest release.
Download LeopardWM-x.y.z-x86_64.msi from GitHub Releases and run it. Re-running a newer MSI upgrades in place — no manual uninstall needed.
For users who prefer not to install:
- Download
LeopardWM-x.y.z-x86_64-windows.zipfrom GitHub Releases - Extract to a permanent location
- Run
leopardwm.exe - (Optional) Enable autostart:
lwm autostart enable
Releases are signed via the SignPath Foundation program.
Prerequisites: Rust with the MSVC toolchain (stable-x86_64-pc-windows-msvc)
git clone https://github.com/jcardama/LeopardWM.git
cd LeopardWM
cargo build --releaseStart the daemon:
./target/release/leopardwm.exeA default config is created automatically at %APPDATA%\leopardwm\config\config.toml. Customize via the tray icon → Settings, or edit the file directly.
Most hotkeys use Ctrl+Alt as the base modifier. Layered pattern: base = focus, +Shift = move, +Win = monitor scope. Every hotkey is rebindable in config.toml. Combos Windows reserves (like Win+Ctrl+Arrow) can't be bound directly, but the opt-in Reclaim Windows-reserved shortcuts setting lets you use them anyway.
| Key | Action |
|---|---|
Ctrl+Alt+H/L/J/K |
Focus left / right / down / up |
Ctrl+Alt+Home / End |
Focus start / end of strip |
Ctrl+Alt+Shift+H/L |
Move column left / right |
Ctrl+Alt+Shift+Home / End |
Move column to start / end of strip |
Ctrl+Alt+Shift+J/K |
Move window down / up in column |
Ctrl+Alt+[ / ] |
Move window to left / right column |
Ctrl+Alt+Shift+[ / ] |
Expel window to new column left / right |
Ctrl+Alt+, / . |
Consume left / right column's window into the focused column |
Ctrl+Alt+Minus / Ctrl+Alt+Equals |
Cycle column width down / up |
Ctrl+Alt+Shift+Minus / Ctrl+Alt+Shift+Equals |
Cycle window height down / up |
Ctrl+Alt+0 |
Equalize all column widths |
Ctrl+Alt+Shift+0 |
Equalize window heights in column |
Ctrl+Alt+M |
Maximize focused column to viewport width |
Ctrl+Alt+C |
Center focused column in viewport |
Ctrl+Alt+Win+,/. |
Focus monitor left / right |
Ctrl+Alt+Win+Shift+,/. |
Move window to monitor |
Ctrl+Alt+1...9 |
Switch to workspace 1–9 |
Ctrl+Alt+Shift+1...9 |
Move focused window to workspace 1–9 |
Ctrl+Alt+Space |
Toggle workspace overview |
Ctrl+Alt+Shift+Left / Right |
Workspace prev / next (cycles) |
Ctrl+Alt+W |
Close focused window |
Ctrl+Alt+F |
Toggle floating |
Ctrl+Alt+Shift+F |
Toggle fullscreen |
Ctrl+Alt+T |
Toggle tabbed mode on focused column |
Ctrl+Alt+S |
Toggle scratchpad (summon / hide) |
Ctrl+Alt+Shift+S |
Stash focused window to scratchpad (or release it back to tiling) |
Ctrl+Alt+Y |
Toggle sticky (follow across workspaces, keeping tiled/floating mode) |
Ctrl+Alt+P |
Toggle pause |
Ctrl+Alt+R |
Refresh (re-enumerate windows) |
Ctrl+Alt+Shift+R |
Reload config |
Win+Ctrl+Escape |
Emergency restore + panic-revert |
The scratchpad and sticky pins are session-scoped: they are keyed by window handle and reset when the daemon restarts.
Stack multiple windows into a clickable tab strip inside any column. Combine with the scrolling viewport for niri-style tabs that also pan horizontally — a combination no other Windows window manager ships today.
Basics
Ctrl+Alt+Ton the focused column toggles between vertical stacking (the default) and tabbed modeCtrl+Alt+J/Ctrl+Alt+Kcycle the active tab — same keys as intra-column focus, no new bindings to learn- Click any tab in the strip to activate it; the click is a real focus change, so the border, foreground state, and IPC events all follow
- Tab titles and icons update live as windows rename themselves or swap notification badges
Per-tab actions
- Hover any tab to reveal a close-X at its right edge — click to close the tabbed window
- Middle-click does the same as the close-X
- Right-click any tab for a context menu:
Close window/Untab this window/Rename tab… - The implicit close gesture (X-button / middle-click) is configurable in Settings → Behavior → "Tab close action" —
close_window(default, browser-style) oruntab(rip the tab out into a new vertical column to the right) - Right-click menu items always carry their literal action —
Close windowalways closes regardless of the toggle,Untab this windowalways untabs - "Rename tab…" opens a modal dialog seeded with the current tab title. Submitting saves a per-window override that survives untab, workspace moves, and daemon restart. Clearing the field removes the override and the live title returns
Drag-and-drop (Chrome semantics)
- Drop a window onto a tabbed column from anywhere — body or strip — and it appends as the rightmost tab and becomes active
- The drop-zone ghost spans the whole column rect so the target is unambiguous
Lifecycle
- A tabbed column with one window auto-reverts to vertical mode
- Tabbed state (and which tab is active) survives daemon restart, along with any per-tab title overrides
- Tab overrides for windows that no longer exist are pruned automatically at daemon startup
- The strip hides during fullscreen, pause, and on workspaces with no tabbed column
Customization — strip height, background, active/inactive text colours, active highlight, opacity, and the tab close action are configurable from the Settings UI or [appearance] / [behavior] (tab_strip_height, tab_strip_bg, tab_strip_active_bg, tab_strip_active_text, tab_strip_inactive_text, tab_strip_opacity, tab_close_action).
LeopardWM ships two interchangeable CLI binaries — both invoke the same code:
| Binary | When to use |
|---|---|
leopardwm-cli |
Canonical name. Use in docs, scripts, and shared examples. |
lwm |
Short alias for daily typing. |
Examples below use whichever is shorter for the line.
lwm run # start the daemon (idempotent — no-op if already running)
lwm stop # stop the daemon
lwm status # show version, monitor count, window count, uptimelwm query workspace # current workspace placements as JSON
lwm query focused # focused window info
lwm query all-windows # every managed window across all workspacesMost users drive the layout via hotkeys, but every hotkey has a CLI equivalent — useful for scripting or AutoHotkey integration.
lwm focus left | right | up | down
lwm move left | right # move focused column
lwm move-window up | down # reorder within a column
lwm workspace 3 # switch to workspace 3
lwm toggle-floating
lwm toggle-fullscreen
lwm scratchpad-stash # stash focused window (or release the scratchpad)
lwm scratchpad-toggle # summon / hide the scratchpad
lwm toggle-sticky # pin / unpin focused window on every workspacelwm autostart enable # writes HKCU\Software\Microsoft\Windows\CurrentVersion\Run
lwm autostart disable # removes itThis is also exposed as a Settings UI toggle and a tray menu item.
lwm subscribe # all events, newline-delimited JSON
lwm subscribe --events workspace,focused_window # filtered subset
lwm subscribe | jq # pretty-printed in another terminalAfter the daemon answers Subscribed, the connection stays open and streams IpcEvent frames (workspace_changed, focused_window_changed, layout_changed, config_reloaded, heartbeat) as state changes occur. Pipe into a status bar (Yasb, eww, custom Tauri/Electron widgets) to re-render on each event without polling. Full schemas + sample clients in agent_docs/ipc-events.md.
lwm doctor # diagnostic checks (config valid, daemon reachable, hotkey conflicts, etc.)
lwm collect-logs # bundles logs + crash reports into a zip for bug reports
lwm reload # reload config from disk without restarting
lwm refresh # re-enumerate windows after weird state
lwm panic-revert # emergency: uncloak everything, drop daemon out of managementRun lwm help (or lwm <subcommand> --help) for the full surface — there are ~40 subcommands.
Note: Crate names and on-disk paths still use
leopardwminternally. A full crate rename is future work.
| Item | Path |
|---|---|
| Config | %APPDATA%\leopardwm\config\config.toml |
| State | %APPDATA%\leopardwm\data\workspace-state.json |
| Log (stdout) | %TEMP%\leopardwm-daemon.log |
| Log (stderr) | %TEMP%\leopardwm-daemon.err.log |
LeopardWM is a Rust workspace with five crates:
| Crate | Responsibility |
|---|---|
leopardwm-core-layout |
Platform-agnostic scrolling layout engine |
leopardwm-platform-win32 |
Win32 integration, window operations, DwmFlush animation engine |
leopardwm-ipc |
Named-pipe command/response protocol |
leopardwm-daemon |
Runtime event loop, state management, dedicated message-pump threads |
leopardwm-cli |
User-facing CLI (also installed as lwm for shorter typing) |
LeopardWM is a window controller, not a compositor. DWM remains the compositor. Elevated or protected windows may reject placement/styling changes, and behavior can vary across app frameworks (Win32, WPF, Electron, UWP).
LeopardWM automatically skips certain windows that should never be tiled. You can add your own rules via [[window_rules]] in the config, but these are always active.
These windows are filtered out during enumeration and never enter the layout engine:
| Class | Why |
|---|---|
Progman |
Program Manager (desktop) |
Shell_TrayWnd / Shell_SecondaryTrayWnd |
Taskbar |
WorkerW |
Desktop worker |
Windows.UI.Core.CoreWindow |
UWP system windows |
XamlExplorerHostIslandWindow / TopLevelWindowForOverflowXamlIsland |
XAML islands |
RAIL_WINDOW |
WSLg RemoteApp — RDP-projected Linux windows that break when repositioned |
Ghost |
DWM hung-window replacement — tiling would duplicate the original |
#32770 |
Standard Win32 dialog (Open/Save/Print/Properties) |
Chrome_RenderWidgetHostHWND |
Internal Electron/Chrome render widget, not a real window |
These processes are ignored via built-in window rules (action = ignore):
| Executable | Why |
|---|---|
smartscreen.exe |
Windows Defender SmartScreen |
consent.exe |
UAC elevation prompt |
msiexec.exe |
Windows Installer |
CredentialUIBroker.exe |
Windows credential/login prompt |
SnippingTool.exe |
Screen capture overlay |
The focus border tries to match each window's actual corner radius. Apps that explicitly set DWMWA_WINDOW_CORNER_PREFERENCE are honored (DONOTROUND → 0 px, ROUNDSMALL → 4 px, ROUND → 8 px); everything else falls back to the 8 px Win11 default.
Some apps draw their own non-DWM-composited frame with square corners while still reporting the OS default — Firefox / Zen Picture-in-Picture popups are the most common example. Override the corner style per window rule:
[[window_rules]]
match_class = "MozillaDialogClass"
corner_style = "square" # also: "rounded" | "small_rounded"The MozillaDialogClass → square rule ships in the default config as a working example. Open Settings → Window rules and use the Corners column (Auto / Square / Rounded / Small rounded) to edit, remove, or add new rules for other apps.
If you find LeopardWM useful, consider supporting development:
See CONTRIBUTING.md.
