Wayland workspace overview daemon

woven

Press a key, see all your workspaces and windows at once, click to focus.

Super+` overlay appears click a window focused
✓ Hyprland ⚠ Niri ⚠ Sway ✕ GNOME

What It Does

Woven runs as a persistent background daemon. When you trigger it, a fullscreen overlay appears showing all your workspaces as columns with interactive window cards. Click any card to focus that window and close the overlay. Right-click or press any key to dismiss.

It doesn't replace your bar, your compositor's built-in features, or anything else. It just adds a keyboard-driven spatial overview that works consistently across Hyprland, Niri, and Sway.

Window Cards

Each window in the overview is a card showing its title. Hover a card to reveal action buttons:

ButtonAction
Close window
Toggle float
Toggle fullscreen
Toggle pin

Overview Controls

ActionResult
Click a window cardFocus that window, close overlay
Hover a window cardShow action buttons
Right-click / any keyClose overlay
ScrollScroll through workspaces

Compositor Support

CompositorStatusNotes
Hyprland✓ Full supportPrimary target
Niri⚠ Performance issuesKnown in v1, tracked for v2
Sway⚠ Implemented, untestedReport issues on GitHub
GNOME✕ Not supportedDoes not implement wlr-layer-shell
KDE→ Planned v2.5/3Limited support, not for daily use

Project Structure

woven/
├── woven-sys/      main process — Lua VM, IPC server, compositor backend
├── woven-render/   render thread — Wayland surface, tiny-skia painter
├── woven-common/   shared types and IPC protocol
├── woven-ctrl/     Iced GUI + CLI control panel
├── runtime/        Lua runtime files shipped with the install
├── woven.lua       default config
├── woven.service   systemd user service
└── get.sh          curl installer
PathContents
~/.config/woven/woven.luaYour config
~/.config/woven/runtime/Lua runtime files
/run/user/$UID/woven.sockIPC socket

Installation

Quick Install

The fastest path. Downloads a prebuilt release, extracts it, and copies everything to the right places.

curl -fsSL https://raw.githubusercontent.com/viewerofall/woven/main/get.sh | bash

get.sh handles: binaries to ~/.local/bin/, runtime + config to ~/.config/woven/, service to ~/.config/systemd/user/, enables the service.

Make sure ~/.local/bin is in your $PATH. Add export PATH="$HOME/.local/bin:$PATH" to your shell rc if not.

Manual Install — From Release Archive

Download comp.tar.gz from the releases page, then:

tar -xzf comp.tar.gz
cp woven.service ~/.config/systemd/user/
cp -r runtime ~/.config/woven/
cp woven.lua ~/.config/woven/
cargo build --release
cp target/release/woven ~/.local/bin/
cp target/release/woven-ctrl ~/.local/bin/

Manual Install — From Git

git clone https://github.com/viewerofall/woven.git && cd woven
mkdir -p ~/.config/woven
cp -r runtime ~/.config/woven/
cp woven.lua ~/.config/woven/
cp woven.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable woven.service
cargo build --release
cp target/release/woven ~/.local/bin/
cp target/release/woven-ctrl ~/.local/bin/

Shell Environment

Woven needs WOVEN_ROOT set to find its config and runtime files:

sudo touch /etc/profile.d/woven.sh
echo 'export WOVEN_ROOT="$HOME/.config/woven"' | sudo tee -a /etc/profile.d/woven.sh
source /etc/profile.d/woven.sh

First-Time Setup

On first launch, if no config exists, Woven opens the setup wizard automatically. It handles compositor detection, theme selection, and keybind instructions — no terminal interaction required. You can also run it manually:

woven-ctrl --setup

Compositor Keybind Setup

Hyprland

exec-once = woven
bind = SUPER, grave, exec, woven-ctrl --toggle

Niri

spawn-at-startup "woven"

binds {
    Super+Grave { spawn "woven-ctrl" "--toggle"; }
}
⚠ Niri has known performance issues in v1.

Sway

exec woven
bindsym Super+grave exec woven-ctrl --toggle
⚠ Sway support is implemented but untested.

Verify

systemctl --user status woven
woven-ctrl --toggle   # should open the overlay

Uninstall

systemctl --user stop woven && systemctl --user disable woven
rm ~/.local/bin/woven ~/.local/bin/woven-ctrl
rm -rf ~/.config/woven
rm ~/.config/systemd/user/woven.service
systemctl --user daemon-reload

Configuration

Config lives at ~/.config/woven/woven.lua. Edit directly or open woven-ctrl for the GUI editor. Apply changes without restarting:

woven-ctrl --reload

woven.theme()

Controls all visual properties of the overlay.

woven.theme({
    background    = "#1e1e2e",
    border        = "#6c7086",
    text          = "#cdd6f4",
    accent        = "#cba6f7",
    border_radius = 12,
    font          = "JetBrainsMono Nerd Font",
    font_size     = 13,
    opacity       = 0.92,
})
KeyTypeDescription
backgroundhex stringOverlay background color
borderhex stringWindow card border color
texthex stringWindow title text color
accenthex stringFocused window / active highlight
border_radiusnumberCard corner radius in pixels
fontstringFont name — match exact fc-list output
font_sizenumberFont size in points
opacityfloat 0–1Overall overlay opacity
Built-in presets selectable in woven-ctrl without editing woven.lua: Catppuccin Mocha, Dracula, Nord, Tokyo Night, Gruvbox

woven.workspaces()

woven.workspaces({
    show_empty = false,
    min_width  = 200,
    max_width  = 400,
})
KeyTypeDefaultDescription
show_emptyboolfalseShow workspaces with no windows
min_widthnumber200Min workspace column width (px)
max_widthnumber400Max workspace column width (px)

woven.settings()

woven.settings({
    scroll_dir      = "horizontal",
    overlay_opacity = 0.92,
})
KeyTypeDefaultDescription
scroll_dirstring"horizontal""horizontal" or "vertical"
overlay_opacityfloat 0–10.92Overlay opacity

woven.animations()

woven.animations({
    overlay_open  = { curve = "ease_out_cubic",    duration_ms = 180 },
    overlay_close = { curve = "ease_in_cubic",     duration_ms = 120 },
    scroll        = { curve = "ease_in_out_cubic", duration_ms = 200 },
})

Available Curves

ValueDescription
linearConstant speed
ease_out_cubicFast start, slow end — good for opening
ease_in_cubicSlow start, fast end — good for closing
ease_in_out_cubicSlow at both ends — good for scrolling
springPhysics-based spring motion

Full Default Config

woven.theme({
    background    = "#1e1e2e",
    border        = "#6c7086",
    text          = "#cdd6f4",
    accent        = "#cba6f7",
    border_radius = 12,
    font          = "JetBrainsMono Nerd Font",
    font_size     = 13,
    opacity       = 0.92,
})

woven.workspaces({
    show_empty = false,
    min_width  = 200,
    max_width  = 400,
})

woven.settings({
    scroll_dir      = "horizontal",
    overlay_opacity = 0.92,
})

woven.animations({
    overlay_open  = { curve = "ease_out_cubic",    duration_ms = 180 },
    overlay_close = { curve = "ease_in_cubic",     duration_ms = 120 },
    scroll        = { curve = "ease_in_out_cubic", duration_ms = 200 },
})

Example: Deep Purple Theme

woven.theme({
    background    = "#0a0010",
    border        = "#331144",
    text          = "#d0c0ff",
    accent        = "#c792ea",
    border_radius = 8,
    font          = "JetBrainsMono Nerd Font",
    font_size     = 12,
    opacity       = 0.95,
})

woven.workspaces({ show_empty = false, min_width = 220, max_width = 380 })

woven.settings({ scroll_dir = "horizontal" })

woven.animations({
    overlay_open  = { curve = "spring",            duration_ms = 200 },
    overlay_close = { curve = "ease_in_cubic",     duration_ms = 100 },
    scroll        = { curve = "ease_in_out_cubic", duration_ms = 180 },
})

woven-ctrl

woven-ctrl is both a GUI control panel and a CLI tool. Run it bare for the GUI, pass flags to drive it from the terminal or compositor keybinds.

GUI Mode

woven-ctrl

Opens the graphical control panel where you can edit your theme visually, switch presets, use the built-in Lua editor, toggle the overlay, and run the setup wizard.

CLI Reference

CommandDescription
woven-ctrlOpen the GUI control panel
woven-ctrl --toggleToggle overlay open/closed
woven-ctrl --showForce overlay open
woven-ctrl --hideForce overlay closed
woven-ctrl --reloadReload woven.lua from disk
woven-ctrl --setupRun the first-time setup wizard

--toggle

The primary keybind command. Bind this in your compositor config. See Installation for examples.

--show / --hide

Force a specific state regardless of current visibility. Useful in scripts where you need predictable state rather than a toggle.

--reload

Hot-reloads woven.lua without restarting the daemon or recreating the Wayland surface. The built-in editor in GUI mode calls this internally when you save.

--setup

Runs the first-time setup wizard. Handles compositor auto-detection, color theme selection, and keybind instructions. Also runs automatically on first launch if no config is found.

IPC Socket

woven-ctrl communicates with the daemon via:

/run/user/<UID>/woven.sock

If the socket isn't found (daemon not running), CLI commands will fail with a clear error.

Scripting Examples

Hide on game launch (Steam)

woven-ctrl --hide; %command%; woven-ctrl --show

Auto-reload on config save

while inotifywait -e close_write ~/.config/woven/woven.lua; do
  woven-ctrl --reload
done

Check daemon status

if systemctl --user is-active --quiet woven; then
  echo "woven is running"
fi

Architecture

How Woven works internally. Useful for contributors, debuggers, or anyone who wants to understand what's running.

Crate Layout

CrateRole
woven-sysMain process: Lua VM, IPC server, compositor backend
woven-renderRender thread: Wayland surface, painter, input handling
woven-commonShared types and IPC protocol definitions
woven-ctrlIced GUI + CLI control panel

Daemon Lifecycle

systemd --user starts woven (woven.service)
  └─ woven-sys initializes
  └─ Lua VM loads ~/.config/woven/woven.lua
  └─ IPC socket created at /run/user/$UID/woven.sock
  └─ Compositor backend connects
  └─ daemon idles, waiting for IPC commands

woven-ctrl --toggle received
  └─ if overlay hidden → woven-render shows surface, queries
                         compositor, renders workspace cards
  └─ if overlay visible → surface hidden

Wayland Surface

A single fullscreen zwlr_layer_shell_v1 surface kept alive between uses:

layer:          TOP
anchor:         TOP | BOTTOM | LEFT | RIGHT  (fullscreen)
exclusive_zone: -1  (overlay — doesn't push other surfaces)
keyboard:       exclusive while visible, None when hidden

Show/hide uses attach(None)+commit rather than destroying and recreating the surface, avoiding protocol re-negotiation and visual glitches on toggle.

Compositor Backends

Auto-detected on startup by checking environment variables. Each backend implements the same internal trait — woven-render doesn't need to know which compositor it's talking to.

CompositorDetectionIPC mechanism
Hyprland$HYPRLAND_INSTANCE_SIGNATUREHyprland socket
Niri$NIRI_SOCKETNiri IPC socket
Sway$SWAYSOCKSway IPC socket

Lua VM

The Lua runtime (mlua, Lua 5.4) runs in woven-sys and is purely for config parsing. It loads woven.lua on startup and on --reload, converts the resulting config tables to Rust structs, then sits idle. It does not run a tick loop or drive rendering.

Render Pipeline

When the overlay opens:

1. Query compositor backend → Vec<Workspace { windows: Vec<Window> }>
2. Compute layout: workspace columns, card positions
3. tiny-skia renders to back buffer:
   - background fill with opacity
   - workspace column separators
   - window cards (border, title, accent on focused window)
4. attach buffer → commit → overlay visible
5. Input loop:
   click       → focus command to compositor → close overlay
   scroll      → repaint
   key / RMB   → close overlay

IPC Protocol

Newline-delimited text over Unix socket:

CLIENT → SERVER:  "toggle\n"
SERVER → CLIENT:  "ok\n"

CLIENT → SERVER:  "reload\n"
SERVER → CLIENT:  "ok\n"  |  "err:<message>\n"

Key Dependencies

CratePurpose
smithay-client-toolkitWayland client abstractions
wayland-clientRaw Wayland protocol bindings
wayland-protocolszwlr_layer_shell_v1 and friends
mluaLua 5.4 runtime
tiny-skiaSoftware rasterizer (painter backend)
fontdueFont rasterization
icedwoven-ctrl GUI framework
tokioAsync runtime (IPC server)

Troubleshooting

Overlay doesn't appear on --toggle

Check the daemon is running:

systemctl --user status woven

If not running:

systemctl --user start woven

Check the IPC socket exists:

ls /run/user/$(id -u)/woven.sock

If not there, the daemon crashed. Check logs:

journalctl --user -u woven -n 50

Check WOVEN_ROOT is set:

echo $WOVEN_ROOT
# expected: /home/<you>/.config/woven

Daemon starts then immediately exits

Check logs for the reason:

journalctl --user -u woven -n 50

Common causes: Lua syntax error in woven.lua (line number shown), compositor not detected (check the relevant env var is set), or ~/.config/woven/runtime/ missing.

Lua error on startup / reload

The error message shows the file and line number. Common mistakes: missing comma between table fields, unclosed string, mismatched braces. Validate before reloading:

luac -p ~/.config/woven/woven.lua

Overlay appears but is completely blank

The compositor backend query is failing. Woven can't get workspace/window data.

Hyprland: check $HYPRLAND_INSTANCE_SIGNATURE is set and the socket exists at /tmp/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket.sock.

Niri: run niri msg workspaces — if it fails, Niri IPC isn't working.

Performance issues on Niri

Known issue in v1. Niri's IPC response time for workspace queries is slower than Hyprland's, causing the overlay to feel sluggish. Tracked for v2.

Workaround: increase animation duration_ms slightly to mask the delay. Use show_empty = false to reduce rendered columns.

Overlay doesn't close on keypress

Another layer-shell surface may be grabbing exclusive keyboard focus. Check that no bar or other surface is set to keyboard = exclusive. Workaround: bind woven-ctrl --hide to a key explicitly.

Font not rendering / showing boxes

Check the exact font name:

fc-list | grep -i "jetbrains"

Use that exact string in woven.lua — font names are case sensitive and must match fc-list output exactly.

woven-ctrl GUI won't open

Check woven-ctrl is in PATH (which woven-ctrl) and that $WAYLAND_DISPLAY is set (echo $WAYLAND_DISPLAY).

Getting Help

File an issue at github.com/viewerofall/woven/issues. Include: Woven version, compositor + version, journalctl --user -u woven -n 50 output, your woven.lua.

Roadmap

v2 — In Progress

Window Thumbnails

Live window previews inside cards so you can visually identify windows without reading titles. The render pipeline will query compositor screencopy for each visible window and composite the thumbnail into the card. Windows without available thumbnails fall back to a colored placeholder.

Lua Plugin API

Load custom Lua modules from ~/.config/woven/plugins/. Plugins can extend the overlay with custom cards, inject data into the workspace display, or respond to overlay open/close events. Plugins hot-reload alongside woven.lua on --reload.

River Backend

River uses the river-status protocol rather than Hyprland/Niri/Sway IPC. v2 adds a River compositor backend so Woven works for River users without any config changes.

Niri-Style Popout + Top Bar

Two visual upgrades inspired by Niri's native overview:

Better X11-to-Wayland Compatibility

Improved handling of XWayland windows — better title detection, window class resolution for icons, correct geometry for windows that don't report Wayland-native size hints.

v2.5 / v3

KDE Support

KDE uses a completely different window management stack and does not implement wlr-layer-shell. Support is planned but will require a separate backend and won't be intended for daily use.

Not Planned