JWM is a tag-based window manager and compositor with Compiz-style aesthetics
and a built-in desktop shell. The primary production surface is Wayland DRM/KMS
(wayland-udev); X11 (x11rb / xcb) remains a first-class compatibility
surface with the same policy and IPC. Nested Wayland backends exist for CI and
development only.
It combines tag-based tiling, multiple layouts, multi-monitor control, animations
and compositor effects with a JSON IPC control plane that is designed to be
diagnosable (doctor, health, capabilities, support bundles).
Release status: JWM has not published a stable release. The manifest version identifies development builds, not a production-support commitment. See compatibility, the tested upgrade/rollback lifecycle, the maintainer release process, and the hardware validation gate for the first release.
| Reliability | Built-in shell | Effects | |
|---|---|---|---|
| JWM | Doctor / health / capabilities / support bundles; dual X11 + Wayland | Control center, notifications, launcher, lock, clipboard, idle | Cube/prism, expose, wobbly, glow — damage-aware |
| Hyprland | Large community daily-drive | Typically external (Quickshell, …) | Strong visual polish |
| niri | Focused scrollable model | Typically external | Clean, lighter chrome |
| Sway | Protocol maturity and trust | External | Minimal by design |
JWM's bet is shell + effects + control-plane hardness on one policy tree — not a new tiling paradigm and not a feature-count race.
- Production backend: direct Wayland DRM/KMS (
wayland-udev). Barejwmselects it when compiled in (JWM_BACKEND/--backendstill override). - X11RB and XCB window-manager backends with an integrated X11 compositor (equal policy parity; not the default session).
- Nested X11 and nested winit Wayland backends with XWayland for development.
- Tile, monocle, floating, scrolling, grid, deck, fibonacci, centered-master, bstack, three-column, tatami, fullscreen, and vertical-stack layouts.
- Tags, per-monitor state, overview/expose, display layout UI, screenshots, screen/audio recording, session restore, gestures, accessibility filters, HDR/VRR/color-management plumbing, and direct-scanout diagnostics.
- Full-screen WaterLily.jl simulation frames on the X11RB/XCB compositor, produced externally on CPU, CUDA, or ROCm.
- Live configuration reload and a newline-delimited JSON IPC API exposed through
jwm-tool. - Authenticated JWM-to-JWM remote viewing and XTEST control for trusted X11
LANs, through
jwm-remote(x11rb and xcb sessions). - Read-only startup health checks, semantic configuration diagnostics, and privacy-aware support bundles.
JWM requires the normal Linux X11, Wayland, DRM/GBM, libinput, libseat, EGL/GL,
ALSA, D-Bus, and font/rendering development packages for your distribution.
The built-in MP4 recorder additionally requires the ffmpeg and ffprobe
executables (the ffmpeg package on Debian/Ubuntu).
The minimum supported Rust version with the committed Cargo.lock is 1.89;
it is declared in Cargo.toml and checked in CI.
On a fresh Debian/Ubuntu machine, scripts/bootstrap_deps.sh installs every
native dependency plus the Rust toolchain (via rustup, since distro packages are
older than the 1.89 floor) in one step:
bash scripts/bootstrap_deps.sh # apt packages + rustup toolchain
JWM_CN_MIRROR=1 bash scripts/bootstrap_deps.sh # China: rustup + cargo via rsproxy.cn
bash scripts/bootstrap_deps.sh --help # options include --with-portal, --with-tauri, --cn--with-portal (or JWM_WITH_PORTAL=1) adds the PipeWire headers needed by the
screencast portal. On non-Debian distros the script prints the required library
groups to map to your package manager. Then build:
cargo build --locked --release
cargo test --locked --lib --bins --testsThe release build produces jwm, jwm-tool, jwm-support, and jwm-remote.
Before starting a display backend, inspect the environment and configuration:
target/release/jwm --backend wayland-udev --doctor
target/release/jwm --backend x11rb --doctor --jsonDoctor is the daily-drive gate: a blocking error means do not treat the session as production-ready. See hardware validation.
X11 and Wayland use separate files under ~/.config/jwm. Prefer a Wayland
session for day-to-day use:
target/release/jwm --gen-config
target/release/jwm --backend wayland-udev --check-config
target/release/jwm --backend x11rb --check-config
# Primary production session (also the CLI default when compiled in):
target/release/jwm --backend wayland-udev
# X11 compatibility:
target/release/jwm --backend x11rbSupported backend names are x11rb, xcb, wayland-udev, wayland-x11, and
wayland-winit. See startup and configuration for aliases,
logging, benchmarking, restart behavior, and doctor output.
The installation helper builds JWM and the official status bar
(tao_glow_bar by default; other crates under bars/ are examples), installs
the session files (Wayland session recommended), and keeps existing
configuration unless --gen-config is used:
scripts/install_jwm_scripts.sh --helpNative bars need only the default bootstrap dependencies. Selecting a Tauri web
bar also needs the Tauri 2 Linux libraries (bootstrap_deps.sh --with-tauri) and
builds its frontend: React/Solid/Svelte/Vue variants require Node.js plus
pnpm; Leptos/Yew variants require trunk, Tauri CLI 2, and the
wasm32-unknown-unknown Rust target. The helper checks these prerequisites up
front and prints the exact install command for anything missing.
jwm-tool sends typed JSON commands and queries over JWM's private Unix socket:
jwm-tool msg get_windows
jwm-tool msg view --args '{"tag":2}'
jwm-tool msg setlayout --args '{"layout":"scrolling"}'
jwm-tool msg spawn --args '{"cmd":["alacritty"]}'
jwm-tool msg '' --subscribe 'window,tag,layout'
jwm-tool health
jwm-tool health --json
jwm-tool capabilities --jsonMalformed JSON, invalid argument types, overflow, empty spawn commands, unknown
commands, and { "success": false } responses produce a non-zero exit status,
so the tool is safe to use from scripts.
health is a backend-neutral live snapshot of the running JWM instance. Its
versioned JSON includes the actual selected backend, uptime, configuration
health, window/monitor/workspace counts, active features, and compositor metrics
when the backend exposes them. capabilities discovers the supported IPC
commands, queries, and subscription topics. The older jwm-tool status command
retains its existing meaning: it reports the optional process supervisor rather
than querying JWM's live IPC socket.
save_session writes a private, atomic snapshot under
$XDG_STATE_HOME/jwm/session.json (normally
~/.local/state/jwm/session.json); restore also recognizes the legacy cache
location. restore_session reapplies monitor, tag, per-monitor tiling order,
and floating-window state.
jwm-support combines the offline startup doctor with optional live health and
capability queries in a versioned JSON document:
jwm-support --backend wayland-udev --output jwm-support.json
jwm-support --backend x11rb --offline --output jwm-support.json
jwm-support --strict --compact > jwm-support.jsonFile output is private (0600) and atomically replaced. The collector uses a
small environment allowlist and redacts configuration, executable, runtime,
and IPC error details; it excludes HOME, PATH, D-Bus addresses, process command
lines, window titles, and arbitrary environment variables. Review
support bundles before attaching a
report to a public issue.
Generate and securely copy one private key, explicitly expose the host on the trusted LAN, then connect from the other JWM machine:
# Managed release bundle only; source installs already use /usr/local/bin.
export PATH="/usr/local/lib/jwm/current/bin:$PATH"
jwm-remote keygen --output ~/.config/jwm/remote.key
jwm-remote host --listen 0.0.0.0:48221 --allow-lan --allow-input \
--key-file ~/.config/jwm/remote.key
jwm-remote connect 192.168.1.50:48221 --grab-input \
--key-file ~/.config/jwm/remote.keyDirect LAN traffic is authenticated and encrypted with ChaCha20-Poly1305, but without forward secrecy: a leaked key file decrypts previously recorded sessions. Rotate the key, or carry the default loopback listener through SSH, when that matters. See the complete setup, security boundary, tuning, and current limits in JWM remote control.
The default modifier is Alt (Mod1). Useful built-in bindings include:
| Binding | Action |
|---|---|
| Alt+Shift+Return | Launch terminal |
| Alt+R | Application launcher (type / for open windows) |
| Alt+Control+Escape | Lock screen |
| Alt+Control+O | Display layout |
| Alt+S / Alt+Shift+S | Interactive / immediate desktop screenshot |
| Alt+Control+R | Interactively choose a source and start/stop screen recording |
| Alt+Control+Shift+R | Move, resize, or replace the active recording source |
| XF86AudioMicMute | Toggle the default microphone's mute (labeled OSD confirms) |
| Alt+Shift+C | Close focused client |
| Alt+Shift+Left / Alt+Shift+Right | Snap the focused floating window to that half of its monitor |
| Alt+Shift+Up | Maximize the focused floating window |
Mouse users get the same geometry: dragging a floating window to a screen
edge snaps it to that half (or maximizes at the top), and dropping it into a
corner quarters it there. The quarters have no default bindings, but
snap_window top-left / top-right / bottom-left / bottom-right are
bindable and scriptable over IPC like the three defaults.
| Binding | Action |
|---|---|
| Alt+Control+C | Calculator scratchpad |
| Alt+Control+S | Toggle sticky window |
| Alt+Shift+F11 | Toggle the WaterLily simulation |
| Alt+Shift+F10 | Cycle the WaterLily simulation case |
| Alt+Shift+F9 | Cycle the WaterLily render palette |
| Alt+Shift+/ | Show all key and touchpad-gesture bindings (click outside to close) |
During interactive screenshot or recording selection, hover highlights the
window under the pointer with an outside-dim veil (same blue language as the
screenshot editor); a left click picks that window. A bottom-center hint chip lists the mode and primary actions (and the hovered
window title while soft-probing). Press G, W, M, or D to choose a
dragged region, window mode, the monitor under the pointer, or the entire
desktop. Tab and Shift+Tab cycle the same choices; middle-click is the
pointer twin of Tab. Dragging still draws a free region; a near-zero click
over empty desktop leaves selection armed. Recording shows resize handles only
after a source is armed, and the cursor switches to move/resize intents over
those handles (a hand while soft-probing a window). Enter or Space saves a
screenshot or starts/commits recording (and nudges with a toast if nothing is
selected yet). Double-click inside an armed recording region also starts
capture; double-click the dimmed veil in the screenshot editor saves the file.
Arrow keys nudge a committed selection (Shift uses 10-pixel steps), while
Escape, right-click, or the recording shortcut again cancels safely.
Once a screenshot region is committed the selection becomes an editor, and a toolbar floats just outside it — below the selection, or above when there is no room below. The grab cursor follows the active tool inside the selection (I-beam for text, crosshair for drawing) and falls back to a plain pointer over the toolbar or outside the crop. Every tool has both a button and a key, so neither the mouse nor the keyboard is required:
| Tool | Key | Draws |
|---|---|---|
| Pencil | P / F |
Freehand stroke |
| Line | L |
Straight line |
| Arrow | A |
Line with a head |
| Rectangle | R |
Hollow rectangle |
| Filled rectangle | B |
Solid redaction bar |
| Ellipse | C / O |
Hollow ellipse |
| Marker | H |
Translucent highlighter |
| Text | T |
Typed label — click, then type |
| Counter | N |
Auto-numbered bubble; click to place the next one |
| Pixelate | X |
Drag a region down to blocks |
| Invert | I |
Invert a region's colors |
The rest of the strip is the selection's pixel size, the stroke controls, the ink swatch, and the four ways out:
| Control | Key |
|---|---|
| Thinner / thicker stroke | - / +, Ctrl+Down / Ctrl+Up, or the wheel |
| Ink (8-colour ring) | 1…8, or click the swatch to step |
| Undo / redo | Ctrl+Z, Backspace / Ctrl+Shift+Z, Ctrl+Y |
| Save to file | Enter or Ctrl+S |
| Copy to clipboard | Ctrl+C |
| Cancel | Escape |
While a text label is open every key is text; Enter commits it, Escape
drops it, and switching tools commits it. A control with nothing to do — undo
with an empty history, thinner at the minimum width — is dimmed rather than
removed, so the row never reflows under the pointer. The strip itself eases
in when the editor appears: one blank frame after it is published, then a
120 ms ease-out fade to full opacity carrying the track, the button chips
and the icons together — a pointer moving across the buttons does not
restart it, withdrawing the strip is instant, and with animations disabled
it is at full opacity from its first frame, rendering no extra frames.
Hovering a button eases its wash in over 120 ms instead of flipping it on;
the leave is still instant, settled states are exactly what they were, and
the hit geometry is unchanged.
The status bars' screenshot pill drives exactly this editor over the control
socket (the take_screenshot IPC command) rather than launching an external
grabber. Clipboard PNG offers prefer a native owner: X11 uses JWM's ICCCM/INCR
path; Wayland uses the compositor data-device selection
(Backend::set_clipboard_png). wl-copy remains a last-resort helper only when
neither native path is available. take_screenshot_fullscreen captures the
whole desktop with no interaction.
A finished capture announces itself: a toast carries the saved path (or
confirms the clipboard copy), and a failed one surfaces even while Do Not
Disturb is on. While a recording runs, a small REC chip with the running
time sits in the bottom-right corner of the recorded output — drawn after
the frame's pixels are read, so it never lands in the video or in
screenshots — and starting and stopping both raise a toast with the output
path. Standalone audio recording (Alt+Ctrl+M) gets the same cue: a red
dot and a static MIC label take the corner while the recorder runs —
stacking directly above the REC chip when both run together — appearing
only once the recorder actually started and clearing on stop, success or
failure.
The X11 compositor freezes the desktop behind the interactive selector/editor
by default. To keep clients and animations live instead, set this in the
[behavior] section; config reload and set_config take effect immediately:
[behavior]
screenshot_freeze_enabled = falseThe optional portal/ crate provides JWM's screencast portal backend. Its
installer builds the independent manifest, installs a per-user D-Bus activation
service with the correct home path, and restarts an older activated backend:
scripts/install-portal.sh
scripts/test-portal.shPortal builds require PipeWire 1.2 development files, pkg-config, and libclang.
System installations are discovered automatically. For a private PipeWire
prefix, set JWM_PIPEWIRE_PREFIX; the installer derives the pkg-config search
path and runtime rpath, and it also honors CARGO_TARGET_DIR:
JWM_PIPEWIRE_PREFIX=/opt/pipewire-1.2 scripts/install-portal.shThe built-in shell — the control center, native
notifications with a freedesktop D-Bus service,
media controls driven from MPRIS, the
calendar, the application launcher,
the wallpaper picker and its colour theming,
clipboard history, the idle policy,
the resource rows, and the
session menu and night light — is documented per
feature. Every status bar in bars/ carries an entry that opens the
same Shell Hub,
so a pointer-driven session reaches it without Alt+F10. Every panel key is a
toggle: Alt+F10, Alt+F11, Alt+F12, Alt+F9 and the rest each take their
own surface back down, so nobody has to reach for Esc. They are mutually
exclusive too — one panel's key replaces whatever other panel is up, keeping
the grabs — and none of them replaces the lock screen.
The Alt+Ctrl+Tab window switcher and the cube tag-switch transition are both
the Compiz-style lit prism documented in cube effects.
Alt+Tab is a hold-the-modifier MRU window switcher,
Alt+E spreads the visible windows into an expose grid,
Alt+O shows every tag of the monitor at once in the
tags overview, and a
monitor with two or more tiled windows shows a
window tab strip across the top of its tiling area.
A window that comes back without JWM launching it, opened from a terminal,
by an agent inside one, or by a running application, returns to the monitor
and tag where that application was last closed; see
window placement.
Alt+Space cycles layouts over a film strip of live thumbnails; see
the layout picker.
Minimized windows fold into the bar, expose a magnifying Dock shelf and show a
compositor-owned hover preview; the cross-process lifecycle is documented in
the minimized-window Dock.
The compositor's live counters are on Alt+Shift+F12; see
the debug HUD.
On direct DRM/KMS sessions, jwm-tool msg get_wayland_status now exposes a
versioned color_delivery snapshot. Its last_policy_decision describes the
most recent composited-route selection and, when evaluated, inventories any
capture, cursor, drag, lock, or top/overlay elements still outside the common
linear tail. This is an observed diagnostic inventory, not a claim that one
listed class caused the selected route. [] means the inventory ran and found
none; a missing/null value means older or non-applicable status. Each output's last_success
records its actual route only after a page-flip/vblank confirms presentation.
The render-decisions view keeps its legacy linear_tail_observation field and
also reports linear_tail_inventory_state (unknown, observed_clear,
observed_blocked, or malformed), a stable issue code, and bounded
total/known/future counts. Future canonical blocker names remain valid; a bad
shape, duplicate, invalid name, safe/list mismatch, or over-limit inventory is
reported as malformed rather than silently becoming unknown.
Every success carries the policy sequence it was queued under, so partial or
mixed multi-output cohorts remain auditable. Hardware color-property and
participation transitions invalidate prior evidence until a replacement frame
reaches vblank. This keeps configured HDR intent, EDID capability, blocked
decisions, and JWM's KMS-tracked presentation state distinct without claiming
to measure the downstream link or panel; get_hdr_status and
get_color_management_status include the same snapshot.
HDR signalling itself — what asks for it, the eleven named reasons an output
can refuse it, and why it is withdrawn and re-asserted per frame — is
documented in HDR and colour delivery.
Additional operational tools are documented in tools/README.md.
The external Julia simulation worker and frame protocol are documented in
docs/waterlily.md.
Architecture boundaries and the incremental migration plan are in
docs/architecture.md. The delivery sequence for larger
changes is tracked in the evolution roadmap. Daily-drive
acceptance loops and the control-plane selling points are in
docs/daily-drive.md; remaining Wayland gaps are queued in
docs/sota-gap-queue.md. First-release hardware
sign-off uses docs/hardware-validation.md.
Release automation accepts only jwm-v<semver> tags whose version exactly
matches the root jwm package. It builds an x86_64 Linux bundle on Ubuntu 22.04,
a tagged source archive, SHA256SUMS, and artifact provenance. Other systems
should build from source; no multi-architecture binary support is claimed.
Monorepo components use independent SemVer. A JWM tag does not change the version of the bridge, portal, shared protocol, bar core/providers, or bars. See CHANGELOG.md and the config, IPC, session, backend, and driver policies in compatibility.
Before opening a pull request, read CONTRIBUTING.md. Please report security-sensitive problems through the private process described in SECURITY.md, not a public issue.
jwm is distributed under the MIT License. The portal/ crate
(the xdg-desktop-portal ScreenCast backend) is covered by the same license,
as already declared in its manifest.