WLROOTS Infinite-Spatial Grid WM based on DWL. Inspired by DriftWM, VXWM, Hevel, Halley, Niri, and Hyprland.
  • C 56.4%
  • Lua 41.7%
  • GLSL 1.1%
  • Makefile 0.4%
  • Roff 0.3%
Find a file
2026-10-05 21:02:39 -05:00
.gitea/issue_template history clear 2026-05-30 18:40:39 -06:00
protocols history clear 2026-05-30 18:40:39 -06:00
SHADER_EXAMPLE readme update 2026-08-27 10:56:30 -06:00
shells window selection 2026-10-05 21:02:39 -05:00
.gitignore we dont ship those... 2026-09-02 06:08:11 -06:00
.mailmap history clear 2026-05-30 18:40:39 -06:00
CHANGELOG.md history clear 2026-05-30 18:40:39 -06:00
client.h wlroots 0.20 + explicit sync 2026-06-12 12:30:20 -06:00
compile_commands.json UWWM 7.1 2026-10-03 21:03:05 -05:00
config.def.h UWWM 6 is here 2026-09-05 13:01:37 -06:00
config.mk JPEG / HTTP support for music player 2026-09-22 20:40:39 -05:00
config_uwwm checkpoint 2 2026-09-07 19:39:29 -06:00
CursorFX.md history clear 2026-05-30 18:40:39 -06:00
dwl-dbg window selection 2026-10-05 21:02:39 -05:00
dwl.1 history clear 2026-05-30 18:40:39 -06:00
dwl.c window selection 2026-10-05 21:02:39 -05:00
dwl.desktop history clear 2026-05-30 18:40:39 -06:00
dwl.so window selection 2026-10-05 21:02:39 -05:00
LICENSE history clear 2026-05-30 18:40:39 -06:00
LICENSE.dwm history clear 2026-05-30 18:40:39 -06:00
LICENSE.sway history clear 2026-05-30 18:40:39 -06:00
LICENSE.tinywl history clear 2026-05-30 18:40:39 -06:00
Makefile shell link created 2 2026-09-05 13:26:54 -06:00
Makefile.asan strip fixes 2026-09-02 06:07:21 -06:00
README.md window selection 2026-10-05 21:02:39 -05:00
shell.nix UWWM 6 is here 2026-09-05 13:00:48 -06:00
util.c history clear 2026-05-30 18:40:39 -06:00
util.h history clear 2026-05-30 18:40:39 -06:00

UWWM — Unlimited Wayland Window Manager

UWWM is a fast Wayland compositor built on wlroots that treats your desktop as a real 3D space instead of a grid of fixed rectangles.

Windows sit on an infinite canvas you pan, zoom, and lean into. The camera has weight. Windows have depth: they float above the wallpaper, tilt as you move them, and drop shadows that sell the gap. No workspace boxes, no columns deciding your layout, no daemon just to show a clock.

  • A true 3D desktop. Parallax depth, spring physics, a camera you fly around.
  • Live settings. Most changes apply the moment you save the file. No rebuild, no logout, nothing closes.
  • Built for games. Fullscreen windows go straight to the display, bypassing the compositor, so you get the frame rate your hardware can actually deliver.
  • Yours to take apart. One readable C file, a Lua layer for the on-screen interface, and no mandated workflow.

Getting UWWM

Dependencies

Required:

  • wlroots (built with the libinput backend)
  • wayland, xkbcommon, libinput, dbus
  • grim (screenshots)
  • cava, which publishes the audio levels that drive the reactive shader effects
  • gdk-pixbuf, which decodes album art, so JPEG and WebP covers work as well as PNG
  • undefined-medium font (the default face the config references)

curl is used to fetch album art published as an http or https URL. Without it those covers simply do not appear, and nothing else is affected.

Recommended for the built-in workflows:

  • wmenu, the application launcher and drag-to-spawn menu
  • satty, to annotate screenshots as soon as you take them
  • wf-recorder for video, ffmpeg for GIF capture

For X11 applications:

UWWM prefers xwayland-satellite, which runs X11 programs as native Wayland surfaces with correct positioning. If it isn't found at startup, UWWM falls back to its built-in XWayland server. Building with that internal support needs libxcb, libxcb-wm, and a wlroots built with X11 enabled.

Building

make
sudo make install

This installs the compositor and its companion runtime module, which loads at startup. make also builds a separate debug pair (dwl-dbg and dwl-debg.so) carrying the diagnostic tracing. The regular dwl binary has none of it.

TODO: the shell (control center, HUD, widgets) is being split into its own repository and added back as a submodule. Until then, install it by copying the shells/ directory into ~/.config/uwwm/. These instructions get updated once the repo is public.

Shells are documented in their own file: shells/README.md.


Quick reference

Mod is the Super / Windows key.

Camera and canvas

Input Action
Left click + drag on empty space Pan the camera, with momentum
Mouse wheel on empty space, or with Mod Zoom in and out
Mod + - / Mod + = Step zoom out and in
Mod + A Fly the camera back to the origin
Mod + M Cycle the workspace layout mode (zones)

Windows

Shortcut Action
Mod + Arrow keys Move focus across the canvas
Mod + Ctrl + Arrows Slide the focused window across the canvas
Mod + Shift + Arrows Resize the focused window
Mod + Space Float or tile the focused window
Mod + Shift + F Toggle fullscreen
Mod + F Toggle true fullscreen, where the buffer goes straight to the display
Mod + Q Close the focused window
Mod + I Cluster mode, to see and edit window groups

Free-form windows snap to each other magnetically when their edges meet, within zone_snap_threshold and only where the windows actually overlap on the other axis. A guide line in snap_guide_color marks the join while you drag. It sticks window to window, never to invisible layout lines, so it stays predictable.

The hidden workspace

Shortcut Action
Mod + H Show or hide the special workspace
Mod + Shift + H Send the current window to it
Mod + Ctrl + H Cycle between special workspaces

What's in there recedes when you put it away: it dims, desaturates, and blurs, so you always know which side of the wall you're on.

Launching and placing

Shortcut Action
Mod + Return Terminal
Mod + P Application launcher
Mod + Ctrl + Left click + drag Drag out a region. The next window opens exactly there
Escape Cancel an in-progress region drag

Effects

Shortcut Action
Mod + S / Mod + Shift + S Cycle / toggle background effects
Mod + C / Mod + Shift + C Cycle / toggle cursor effects
Mod + W / Mod + Shift + W Cycle / toggle window effects

Capture and session

Shortcut Action
Print Screenshot with an interactive crop, opened for annotation
Ctrl + Print Start or stop video recording
Shift + Print Start or stop GIF capture
Mod + Shift + Q Quit UWWM

A recording indicator stays on screen while a capture is running, so you can't forget one.


Configuring UWWM

Two layers. You'll probably only need the first.

Runtime settings live in ~/.config/uwwm/config.conf: appearance, animation feel, border styles, colours, effects, HDR, per-monitor options. UWWM watches the file, so saving applies changes immediately without restarting or interrupting anything. Per-monitor sections override the defaults, so one display can be HDR while another is SDR, with different zoom and layout behaviour.

The on-screen interface, meaning the control center, status widgets, clock and media card, is written in Lua and reloads the same way. Almost every runtime setting has a slider or toggle there, so you can watch it change instead of guessing at numbers in a file.

Build-time settings, the default keybindings and compiled-in defaults, live in config.h in the traditional dwl manner. Note that config.h is generated once from config.def.h and is not re-copied on later builds, so edit config.h itself. Changing it needs a rebuild, and the running session picks it up on next start.

The compositor also listens on a socket at /tmp/uwwm_ipc, so external scripts and bars can drive it. See Scripting UWWM.


Features

The 3D canvas

Windows are placed in a concentric, non-overlapping grid on an infinite plane rather than trapped inside one monitor's rectangle. Panning has momentum, and everything (windows, the camera, fullscreen transitions) moves on spring physics with a little overshoot and settle, Windows bank away from the direction they are moving, with a soft ceiling rather than a clamp, so a flick leans harder than a drag but the two stay distinguishable. so the desktop feels handled rather than rendered.

With window parallax on, windows gain real depth. They lean as the camera moves, separate from the wallpaper behind them, and their shadows shift to sell the gap. Cursor and background effects share the same spatial model, which is why the screen reads as one scene instead of a stack of layers.

Workspace zones (Mod + M)

Zones split the canvas into named, resizable regions that behave like traditional tiling areas without giving up freeform placement. Cycle the layout mode to rearrange everything at once, or drop a window into a zone and let it fit.

Window clustering (Mod + I)

Cluster mode shows the relationships between your windows so you can edit them by hand.

  • Badges appear above windows. Click a badge's O handle and drag to another window to link them.
  • Linked windows move together, proportionally, across the canvas.
  • Click the X at a link's midpoint to cut it.
  • Click a cluster's name label to rename it.
  • A badge shows how many notifications a window has received. Click it to expand the list, with icons and summaries.

Shader pipelines

Three independent GLSL fragment-shader pipelines run over the desktop.

  • Background effects process your live wallpaper (awww or swww): bloom on bass hits, edge-detection outlines that sharpen artwork, directional smear, and surface lighting computed from the image itself.
  • Cursor effects replace the static pointer with a physical one. It tilts, rotates, stretches with velocity, magnifies when you flick it, and scales up several times when you shake it, so you can find it across multiple monitors.
  • Window effects draw the neon edge glow, highlights, and motion trails you see when windows move.

Defaults are built into the compositor and written out to ~/.config/uwwm/shaders/ on first run, so you can start from something that already works. Shaders reload live. UWWM checks each shader directory once a second (shader_hotreload_interval_ms, 0 to turn it off), so saving a file applies immediately with no restart and nothing closing. If a shader fails to compile, that pipeline falls back to its built-in source instead of breaking, so a typo costs you the effect and not the session.

Audio reactivity is driven by [cava], an external analyser. On first run UWWM writes a ready-made config to ~/.config/cava/config_uwwm, pointed at /tmp/uwwm_audio, so the only step left is starting it:

cava -p ~/.config/cava/config_uwwm &

Put that in your autostart and every audio-reactive effect is live. The generated file follows your system default output. Uncomment the source line inside it to bind a specific device. UWWM never overwrites that file once it exists, so your edits survive upgrades.

HDR and colour

UWWM drives HDR through the display hardware rather than through the compositor, which is what keeps fullscreen content fast.

  • Per-monitor HDR in PQ or HLG, detected from what the display actually reports.
  • SDR white level sets how bright ordinary windows sit inside an HDR signal. This is the single most useful setting for making a desktop look right on an HDR panel.
  • Desktop gamma, a shadow lift that raises crushed near-blacks, and highlight knee controls for both transfer functions.
  • Brightness, contrast, and saturation grading, applied on the display side.
  • Colour management reports honest capabilities to applications, so a game or video player is told what your panel can actually reach.

Because all of it happens on the display hardware, it also applies to directly-scanned-out games. You get HDR and colour grading for free, with no frame cost.

ShellFX

The whole on-screen interface is scriptable, and it runs inside the compositor. No bar daemon, no layer-shell client, no separate process to configure and keep alive.

A shell is a directory of Lua files under ~/.config/uwwm/shells/. The compositor loads it, gives you a GPU-backed drawing surface per monitor, and calls your functions each frame:

shells/
    default/            the shipped shell: bar, control center, clock, media card
        init.lua        entry point, returns a table of callbacks
        widgets/        optional sub-modules
    fallback/           a minimal HUD that loads if your shell errors

The callback you care about first is on_render(monitor, width, height, dt). Draw with the supplied primitives (rounded rects, gradients, circles, text, icons, glass panels, plus 3D versions of all of them that lean with the desktop), and read live state through the FFI: focused window, camera position and zoom, cursor, audio levels, CPU and memory, Wi-Fi and Bluetooth, media player metadata. You can also drive the compositor back: spawn programs, switch workspaces, change zoom, set volume.

Saving a file reloads it. Nothing restarts, nothing closes, and if your shell throws the compositor falls back to the built-in HUD instead of leaving you with a dead screen. That makes it safe to experiment live, which is the point.

Full reference, including every function signature and a copy-paste skeleton, is in shells/README.md. It is written to be handed to an LLM and used to build a shell from scratch.

ScreenFX

A compositor-side effect layer over the composited desktop, rather than per window. All six work: vignette, scanlines, film grain, chromatic aberration, bloom, and motion blur.

Motion blur is directional rather than a crossfade. A velocity buffer records how far each window actually travelled between frames, so a moving window smears along its own axis while a still desktop stays sharp, and the camera's own pan and zoom is folded in so the whole view blurs together when you move around.

Every strength ships at 0.00, so the pass is skipped entirely until you raise one. That means it costs nothing when switched off, and it never runs in front of a fullscreen game, which keeps direct scanout intact. Colour and HDR are complete and independent of this.

Per monitor. Each display has its own strengths in its [monitor.*] section, so one panel can be graded heavily and another left alone. A key that is absent inherits [general], and a key that is present and 0 is genuinely off for that panel, which is what lets you switch an effect off on one display while it stays on everywhere else. The ScreenFX page in the control center previews each effect on a simulated desktop as you move the sliders, and a SYNC TO ALL button copies the selected monitor's whole grade to every other output.

Performance

UWWM tries hard to stay out of the way of anything that cares about frame timing.

  • Direct scanout with a hardware cursor. When a window fills the display, its buffer goes straight to the screen and the compositor steps aside. No extra copy, no composition cost. The display hardware makes that call, so any application presenting a compatible fullscreen buffer gets it, not just games. The pointer stays live over scanned-out content: it is driven through the Wayland cursor object onto the KMS cursor plane, so a fullscreen game keeps both its zero-copy path and a visible mouse, and the CursorFX shader cursor and the plane never draw at the same time. If an application offers a cursor image the compositor cannot upload, the theme pointer is used instead.
  • Tearing option. Allow unsynchronised presentation for the lowest possible latency, when you'd rather have every frame now than wait for the next refresh.
  • Adaptive sync (VRR) per monitor, where the display supports it.
  • Idle throttling. The interface, physics, and animation loops drop to a low rate when nothing is moving and wake instantly on input, so an untouched desktop costs almost nothing.
  • Presentation-time pacing. Frames are gated on the display's own presentation clock rather than on a fixed tick rate, so a 165Hz panel is asked for at most 165 frames a second. Damage still wins, a window that repaints is composited immediately.
  • Viewport culling. Windows that are on your current workspace but scrolled or panned clean off the display are skipped entirely, so they cost no geometry, no border render and no texture upload.
  • Off-screen frame throttling. Applications only get a frame callback when their window is actually visible, otherwise at offscreen_frame_hz, default 15. That is what stops a browser two workspaces away from rendering pixels nobody can see. Set it to 0 for full rate.
  • Batched drawing. The interface groups its draw calls into single GPU submissions instead of one per element.
  • Window velocity lean. Dragging a window banks it away from its direction of travel, tuned by window_move_tilt_deg in [3d_window], the soft ceiling in degrees, and window_move_tilt_knee, the screen px/s that reaches half of it. It straightens the moment a snap engages, because a rotated edge cannot line up with an axis-aligned guide.
  • Input is dispatched before drawing, with no forced compositor latency.

Media, clock, notifications, coordinates

All drawn natively, with no extra process.

  • Media player. A glassmorphic card driven over D-Bus, with cover art and a progress bar that tracks seeking in real time, in a compact or wide layout. Cover art is decoded by gdk-pixbuf, so JPEG, WebP and AVIF work as well as PNG, and an http or https art URL from a server-backed player is fetched in the background and cached, never on the render thread. Target it at one monitor or all of them, put it behind your windows or in front, or expose it as an ordinary movable window.
  • Clock. Configurable face, per-monitor placement, three depth layers.
  • Notifications. Handled internally over D-Bus, with a stealth mode that tells you something arrived without putting the content on screen, and an option to prefix the window that caused it.

Screen capture and recording

Screenshots, video, and GIF capture are bound out of the box and route through grim, wf-recorder, and ffmpeg. Screenshots open a crop selection and hand straight to satty for annotation. UWWM also speaks the ext-image-capture protocol directly: wlr-shot (or any client of that protocol) can capture the whole output, a region, or a single window by listing toplevels and passing an identifier. Output capture is live today; window capture is wired to the same protocol (a toplevel capture request supplies the window's scene node as its source) and is pending verification.

Edge panning

Dragging or resizing near a display edge pans the camera at a controlled rate so the action stays on screen, with configurable threshold and acceleration, and protection against the runaway-expansion feedback loop this feature is notorious for. An option decides whether panning happens at boundaries you share with another monitor.

Scripting UWWM

A socket at /tmp/uwwm_ipc takes simple space-separated commands, so bars, keybinds, and scripts can drive the compositor without an embedded interpreter.

  • Camera and layout: recenter, pancamera <0-3>, zoomcamera <factor>, view <1-9>, tag <1-9>
  • Windows: focusdirection <0-3>, focusstack <±1>, togglefloating, togglefullscreen, killclient
  • Effects: toggle_shader_manager / _cursor / _window, cycle_manager_shader / _cursor_shader / _window_shader, togglehelpers
  • Interface shell: get_shells, get_shell_status, switch_shell <name>, debug_shell
  • State: get_coords, get_active_window, get_monitors
  • System: spawn <command>, quit

Two examples:

echo "get_active_window" | nc -U /tmp/uwwm_ipc
echo "view 3" | nc -U /tmp/uwwm_ipc

VR

When a headset is connected, UWWM recognises its non-desktop display modes and leases the hardware directly to SteamVR. Plugging in is the whole setup.

Fullscreen games and aspect ratio

Fullscreen content can either stretch to fill the panel or keep its native aspect ratio with clean black bars. It's a per-monitor choice, useful for older titles and handheld-mode resolutions.


Troubleshooting

The background is black with effects enabled. A wallpaper daemon (awww, swww) has to be running. The background pipeline filters whatever is already being drawn; it doesn't generate an image itself. Also check that foreground layering is enabled for the background shader.

Audio-reactive effects aren't reacting. Check cava is running (cava -p ~/.config/cava/config_uwwm &) and that /tmp/uwwm_audio exists and is receiving data. If you route audio somewhere unusual, check PipeWire's default sink or set source explicitly in that config. If the file is missing, start UWWM once and it writes the config on launch.

Notifications don't appear. Source /tmp/uwwm_dbus_env in your session so applications find the compositor's bus, then test with notify-send "Test".

A game isn't getting the direct, compositor-free path. That's the display hardware deciding whether it can scan the window's buffer out, which depends on the buffer's size and format. Windowed and borderless content with visible borders won't qualify; true fullscreen usually will. This is a correctness check, not a failure. Committing a buffer the display rejects would show as a black screen instead.

The media card stopped advancing. After a restart or reload, D-Bus property signals can desync. Press Stop (not Pause) in the player, then Play, to re-register the track.

Something looks wrong after editing the config. Most settings apply live. A few that change GPU buffer sizes or quality levels need a restart to reallocate cleanly.

Roblox / Sober has odd mouse input. Let it run natively on Wayland:

flatpak override --user --nosocket=wayland org.vinegarhq.Sober

Philosophy

UWWM exists to stop telling you how to use your computer.

The modern desktop has settled into two failure modes. One kind is soft, heavy, and fragile. The other uses "minimal" as an excuse to remove things you need and dictate the one layout it permits. UWWM takes the opposite position: maximum freedom, nothing compulsory, no compromise on frame timing.

  • No forced paradigms. Infinite freeform canvas, keyboard tiling, zone layouts, a hidden scratchpad workspace, or pixel-perfect placement by dragging a box. All available, none mandatory.
  • No daemon tax. The clock, notifications, media card, coordinates, and the control interface are drawn by the compositor itself. You are not running a 300 MB runtime to see the time.
  • No gatekeeping. Layer-shell bars, external notification servers, wallpaper daemons, and floating menus all work, because it's standard Wayland underneath.
  • No hidden latency. Input is processed before drawing, fullscreen content reaches the display directly, and the compositor's cost is something you can measure.

Your desktop is yours. Make it do whatever the fuck you want.


Videos

Click to play:

Watch the video

Watch the video

Pre-chording

Drag out a region first, then launch. The window arrives already the size and position you asked for:

Watch the video

Does VR really work?

Heh, I got you ~

VR working on UWWM


Roadmap

  • ScreenFX. Fix bloom and motion blur. The pass itself composites correctly now, so this is the remaining two effects rather than the whole feature.
  • Shell as a submodule. Move the Lua interface into its own repository and wire it into the build and install instructions.
  • Per-monitor colour presets. Save and swap HDR/SDR grading setups between panels.
  • CAVA control panel. Expose sensitivity, framerate, bars, smoothing, and input source as live controls that write ~/.config/cava/config_uwwm and restart cava.

License

UWWM is derived from dwl and inherits its licensing. See LICENSE, plus LICENSE.dwm, LICENSE.sway, and LICENSE.tinywl for the terms of the code it builds on.