A small (<1000 lines for the base wm), experimental X11 window manager where all windows live on an infinite 2D canvas. Navigation is spatial: Super+scroll to zoom, Super+middle-drag to pan.
Four binaries: zzwm (the main window manager) plus three small utility apps in
utility-apps/:
zzwm-run— a small app launcher (Super+Space). A plain client window, so it's managed like any other window: it zooms and pans with the canvas.zzwm-bar— a minimal status app that doubles as the "base window" (see below): zzwm anchors it at the canvas origin (0,0) and won't let it be closed or moved, so it acts as a "you are here" reference point as you zoom/pan. Its contents are entirely driven by runningBAR_CMD(see Configuration), one line of output per line shown.zzwm-help— a keybinding reference (Super+H). Readsconfig.h's bindings table directly, so it always matches the real configuration; any keypress closes it, not a click.
(mostly reconfigurable)
| Input | Action |
|---|---|
| Super + Scroll wheel | Zoom in / out, centred on cursor |
| Super + Middle-click drag | Pan the canvas |
| Left-click window | Focus (and raise to top) |
| Super + left-drag | Move window on canvas (no-op on the base window; snaps to other windows' edges, see Configuration) |
| Super + right-drag | Resize window (snaps to other windows' edges, and to screen/2ⁿ sizes — see Configuration) |
| Super + Return | Spawn xterm |
| Super + Space | Spawn zzwm-run (type a command, Enter to launch) |
| Super + H | Spawn zzwm-help (keybinding reference) |
| Super + Q | Close focused window (no-op on the base window) |
| Super + Shift + R | Restart zzwm in place, keeping every window open (see Hot restart) |
| Super + F | Centre and fit the view on the focused window |
| Super + A | Fit every window on screen at once |
| Super + 0 | Jump back to native scale (zoom 1.0) |
| Super + = / Super + - | Grow / shrink the focused window one screen/2ⁿ detent |
Edit config.h and rebuild to change functionality:
-
ANCHOR_NAME— the X window name (WM_NAME/XStoreName) of the "base window". Whatever window has this name is anchored at the canvas origin instead of the viewport, and can't be closed or moved. Defaults to"zzwm-bar", matchingutility-apps/statusbar.c. -
Keybindings — each line is
BIND(modifier, keysym, action, arg), e.g.BIND(Mod4Mask, XK_Return, ACT_SPAWN, "xterm &"). Available actions:ACT_SPAWN(runargas a shell command),ACT_CLOSE(close the focused window),ACT_RESTART(restart zzwm in place without closing any windows, see Hot restart),ACT_VIEW_FOCUSED(centre and fit the view on the focused window),ACT_VIEW_ALL(fit every window on screen),ACT_ZOOM_FULL(back to native scale),ACT_GROW/ACT_SHRINK(step the focused window one size detent, keeping its top-left corner).The view actions never magnify past
ZOOM_MAX, so with the defaultZOOM_MAXof1.0they zoom out to fit when something doesn't fit and otherwise just recentre. -
ZOOM_MIN/ZOOM_MAX— how far out and in Super+scroll will go, as a scale factor (1.0is native size). Defaults0.05(1/20th) and1.0, i.e. you can zoom out to survey the canvas but not magnify past native size, where windows are sharpest — raiseZOOM_MAXif you want to. Setting both to1.0pins the canvas at native scale. -
ZOOM_SPEED— factor one scroll click multiplies the zoom by, so bigger is coarser. Default1.1(~7 clicks to double). -
SNAP_ENABLED— set to0to disable edge snapping entirely. Default1. -
SNAP_DIST— how close (in canvas/native pixels, independent of zoom) an edge has to get to another window's edge before it snaps. Default12. -
SNAP_GAP— space (same units) left between two windows when they snap side-by-side instead of sitting flush. Default10. -
SIZE_DETENT_ENABLED— during a Super+right-drag resize, snap the width and height to the screen divided by a power of two (full, 1/2, 1/4, ...), so windows land on sizes that tile the monitor without measuring. Set to0to disable. Default1. -
SIZE_DETENT_DIST— how close (canvas units) a width or height has to get to a detent before it locks on. Default16. -
SIZE_DETENT_LEVELS— how many halvings are offered, counting full screen as the first. Default5(full, 1/2, 1/4, 1/8, 1/16).ACT_GROW/ACT_SHRINKstep through these same levels. -
VIEW_MARGIN— air left around whatACT_VIEW_FOCUSED/ACT_VIEW_ALLfit.1.0is edge to edge, default1.06leaves a 6% margin.An axis that snapped to another window's edge keeps that snap rather than the detent — the edge alignment is the more specific intent, and letting both fight over one axis would make each feel unreliable.
-
BAR_CMD— a shell command; each line of its output becomes one centred line in zzwm-bar (window resizes to fit), re-run everyBAR_CMD_INTERVALseconds. Defaults to a one-liner that prints a clock and a help hint. -
BAR_CMD_INTERVAL— how oftenBAR_CMDis re-run, in seconds. Default1.
No changes to zzwm.c are needed for either.
Edit appearance.h and rebuild to change aesthetics:
CANVAS_BG_*— zzwm's canvas background, behind all windows. Dark navy by default.BG_*/FG_*/DIM_*— background, foreground, and dim text forzzwm-run,zzwm-bar, andzzwm-help. Background is white by default.BORDER_R/BORDER_G/BORDER_B— color of the border drawn around every managed window.BORDER_THICKNESS— border thickness in canvas pixels at zoom 1.0 (it scales with the window as you zoom). Set to0to disable borders.FONT_NAME— X font name (XLoadQueryFont) used by zzwm-run and zzwm-bar. Default"fixed".
No changes to any .c files are needed.
makeRequires: libX11, libXrender, libXcomposite, libXdamage, libXpresent, and libXInput.
# Debian/Ubuntu:
apt install libx11-dev libxrender-dev libxcomposite-dev libxdamage-dev libxpresent-dev libxi-dev xserver-xephyrAlways test inside a nested X server:
Xephyr :1 -screen 1280x800 &
DISPLAY=:1 ./zzwm &
DISPLAY=:1 ./zzwm-bar &Then open windows on :1:
DISPLAY=:1 xterm &Everything in zzwm is compile-time configurable, so picking up a change means running a new binary — but not losing your session. zzwm never reparents windows, so it can re-exec itself and re-adopt every client that's already open. Canvas positions, stacking order, focus, and the viewport (pan + zoom) are saved to a root-window property across the exec and restored on the way back up, so a restart is visually a no-op.
Three ways to trigger it:
make reload # rebuild + install + restart the running zzwm
pkill -USR1 -x zzwm # restart whatever's already installedor press Super+Shift+R (the ACT_RESTART binding in config.h).
Placement survives an ungraceful exit too — a crash, a kill, or running a
build with no restart support. Each window's canvas position is mirrored onto
the window itself as a _ZZWM_CANVAS_GEOM property whenever it changes, and
the viewport onto the root as _ZZWM_VIEWPORT. Properties belong to the
window rather than to zzwm's connection, so they outlive the process however
it dies, and the next instance puts everything back. Without this a fresh
instance would adopt each window at its real X geometry — the off-screen spot
zzwm parks windows in — and come up with an empty canvas.
Caveats: only zzwm's own state survives — a client that can't cope with a
brief moment of no window manager is on its own. Stacking order and focus are
restored on a graceful restart only; after a crash, windows come back in
whatever order X reports them. make reload restarts the WM only; zzwm-bar
and other utility apps keep running as the ordinary clients they are, so
restart them by hand if you rebuilt those.
make
sudo make install # installs to /usr/local/bin
# or, without root:
make install PREFIX=~/.localzzwm launches zzwm-run/zzwm-bar/zzwm-help via system() (e.g.
"zzwm-run &"), so they must be on $PATH for keybindings like Super+Space
to work — make install puts all four binaries plus startZZWM.sh in the
same directory for that reason. make install also installs zzwm.desktop
to /usr/share/xsessions (independent of PREFIX, since that's the fixed
location display managers scan), so ZZWM shows up as a session choice on
your login screen. make uninstall removes all of it again (respects the
same PREFIX/DESTDIR).
To start ZZWM via a display manager (GDM, LightDM, SDDM, etc.), just select
the "ZZWM Session" entry at the login screen after sudo make install.
To run zzwm as your actual X session window manager without a display
manager, add to ~/.xinitrc:
zzwm-bar &
exec zzwmthen start X with startx. (Test in Xephyr first, per above — zzwm replaces
whatever WM is currently running on the display, so a bad keybinding config
could leave you without a way to spawn a terminal.)
MIT — see LICENSE.
