Skip to content

Repository files navigation

ZZWM (Zoe's Zooming Window Manager)

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.

ZZWM screenshot

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 running BAR_CMD (see Configuration), one line of output per line shown.
  • zzwm-help — a keybinding reference (Super+H). Reads config.h's bindings table directly, so it always matches the real configuration; any keypress closes it, not a click.

Controls

(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

Configuration

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", matching utility-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 (run arg as 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 default ZOOM_MAX of 1.0 they 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.0 is native size). Defaults 0.05 (1/20th) and 1.0, i.e. you can zoom out to survey the canvas but not magnify past native size, where windows are sharpest — raise ZOOM_MAX if you want to. Setting both to 1.0 pins the canvas at native scale.

  • ZOOM_SPEED — factor one scroll click multiplies the zoom by, so bigger is coarser. Default 1.1 (~7 clicks to double).

  • SNAP_ENABLED — set to 0 to disable edge snapping entirely. Default 1.

  • SNAP_DIST — how close (in canvas/native pixels, independent of zoom) an edge has to get to another window's edge before it snaps. Default 12.

  • SNAP_GAP — space (same units) left between two windows when they snap side-by-side instead of sitting flush. Default 10.

  • 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 to 0 to disable. Default 1.

  • SIZE_DETENT_DIST — how close (canvas units) a width or height has to get to a detent before it locks on. Default 16.

  • SIZE_DETENT_LEVELS — how many halvings are offered, counting full screen as the first. Default 5 (full, 1/2, 1/4, 1/8, 1/16). ACT_GROW/ ACT_SHRINK step through these same levels.

  • VIEW_MARGIN — air left around what ACT_VIEW_FOCUSED/ACT_VIEW_ALL fit. 1.0 is edge to edge, default 1.06 leaves 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 every BAR_CMD_INTERVAL seconds. Defaults to a one-liner that prints a clock and a help hint.

  • BAR_CMD_INTERVAL — how often BAR_CMD is re-run, in seconds. Default 1.

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 for zzwm-run, zzwm-bar, and zzwm-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 to 0 to 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.

Building and running

make

Requires: libX11, libXrender, libXcomposite, libXdamage, libXpresent, and libXInput.

# Debian/Ubuntu:
apt install libx11-dev libxrender-dev libxcomposite-dev libxdamage-dev libxpresent-dev libxi-dev xserver-xephyr

Always 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 &

Hot restart

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 installed

or 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.

Installation

make
sudo make install         # installs to /usr/local/bin
# or, without root:
make install PREFIX=~/.local

zzwm 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 zzwm

then 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.)

License

MIT — see LICENSE.

About

An experimental zooming window manager.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages