A modular Wayland compositor built around tiling, customization, and fast iteration.
  • C 73.7%
  • C++ 25.3%
  • CMake 1%
Find a file
Sandro Alves a7a4f6e283 chore: track the docs/ directory
The documentation tree is now committed, so remove the docs/ ignore rule.
2026-10-09 23:32:56 +01:00
docs fix(window): reapply opacity per frame to cover subsurfaces and popups 2026-10-09 13:37:50 +01:00
extra feat: add interactive region screenshot with clipboard integration 2026-09-08 18:21:46 +01:00
include fix(window): reapply opacity per frame to cover subsurfaces and popups 2026-10-09 13:37:50 +01:00
LICENSES chore: Remove dead code, restore third-party licenses, update docs 2026-09-08 18:46:37 +01:00
protocols refactor: improve IPC socket handling and update window geometry management logic 2026-05-19 14:08:44 +01:00
src fix(window): reapply opacity per frame to cover subsurfaces and popups 2026-10-09 13:37:50 +01:00
.gitignore chore: track the docs/ directory 2026-10-09 23:32:56 +01:00
AGENTS.md docs: add structured documentation and refresh README 2026-10-09 01:19:17 +01:00
CMakeLists.txt refactor(config): centralize action parsing for keybinds, gestures and IPC 2026-10-09 00:54:12 +01:00
config.toml config: document new options and action examples 2026-10-09 09:48:21 +01:00
LICENSE refactor: improve IPC socket handling and update window geometry management logic 2026-05-19 14:08:44 +01:00
README.md docs: add structured documentation and refresh README 2026-10-09 01:19:17 +01:00

Zurin

A modular Wayland compositor with Dwindle tiling layout and TOML-based runtime configuration.

Features

  • Dwindle Layout: Smart binary tree tiling with cursor-aware window placement
  • Directional Control: focusdir, swapwindow and keyboard resizeactive for keyboard-driven layouts
  • Runtime Configuration: TOML-based configuration with functional hot-reload (keybinds, appearance, wallpaper and input settings are reapplied live on save) and zurin -t validation
  • Window Rules: match by app id substring or regex, title regex, floating geometry, monitor by name, opacity, borders, gaps and fullscreen
  • Per-window Opacity: global active/inactive opacity, rule overrides and runtime setopacity/IPC control
  • Keybind Submaps: named modes ([keybinds.resize]) with a safe Escape exit
  • Modular Architecture: Focus on scalability and customization
  • Screencast with Consent: PipeWire screen sharing through the XDG Desktop Portal with a user consent menu and source selection (entire screen, output or window), including an optional "always allow" permission store
  • Interactive Screenshot: Region/window/output capture (bound to Super+Shift+S) with clipboard integration
  • Wallpaper Modes: fill/fit/tile/center drawing modes with live reload of both path and mode
  • Hardened IPC: Non-blocking Unix-socket control interface (zbus) with peer authentication, per-client timeouts, runtime commands (dispatch, reload, gaps, opacity, …) and a JSON event stream (zbus subscribe)

Documentation

Full reference in docs/:

  • Building and running — dependencies, CMake options, sessions, config validation
  • Configuration — every TOML table with defaults and examples
  • Actions — complete action reference for keybinds, gestures and zbus dispatch
  • IPC — commands, events, subscribe and examples
  • Architecture — module map, scene layers, dwindle tree, internals

Dependencies

Required

Package Version Description
wlroots 0.19+ Wayland compositor library
wayland 1.21+ Wayland protocol libraries
wayland-protocols 1.32+ Wayland protocol definitions
xkbcommon 1.5+ Keyboard handling library
libinput 1.21+ Input device handling
pixman 0.42+ Pixel manipulation library
libdrm 2.4+ Direct Rendering Manager
libpipewire 0.3+ Audio/Video processing graph
libsystemd - System and Service Manager library

Optional

Package Description
xcb, xcb-icccm Required for XWayland support

Runtime

These tools are configured by default but can be changed in config.toml.

Tool Description
kitty Default terminal emulator

Build Tools

  • CMake 3.16+
  • pkg-config
  • GCC or Clang with C11 support
  • wayland-scanner

Building

# Create build directory
mkdir build && cd build

# Configure (RELEASE)
cmake ..

# Configure (DEBUG)
cmake -DDEBUG_MODE=ON ..

# Configure (with XWayland support)
cmake -DENABLE_XWAYLAND=ON ..

# Build
make -j$(nproc)

# Install
sudo make install

Running

zurin

Configuration

Edit config.toml in the project directory or ~/.config/zurin/config.toml. Validate changes without restarting the session with:

zurin -t          # checks the resolved config
zurin -t -c file  # checks a specific file

The complete reference (all tables, defaults and examples) is in docs/configuration.md.

Default Keybinds

Keybind Action
Super+Q Open terminal (kitty)
Super+P Open launcher (wmenu)
Super+Shift+S Screenshot region
Super+Tab Close window
Super+(1-0) Switch workspace
Super+Shift+(1-0) Move to workspace
Super+Shift+M Quit
Super+Mouse Left Move window
Super+Mouse Right Resize window

All keybinds are configurable in config.toml ([keybinds] and [keybinds.mouse]). Directional focus/swap, keyboard resize, opacity and keybind submaps are available as actions — see docs/actions.md for the full list with examples.

Window rules and opacity

Rules match by app id (substring key or app_id regex) and can set floating state, workspace, monitor (by name), size/position/center, opacity, borders, gaps and fullscreen:

[rules]
"gimp" = { floating = true, size = [1200, 800], center = true }
"kitty" = { border = false, gaps = false, opacity = 0.95 }

See docs/configuration.md#rules.

Input

[settings.input]
natural_scroll = false
accel_speed = 0.0
accel_profile = "adaptive"
invert_buttons = false

Appearance

Wallpaper rendering mode is set with wallpaper_mode under [settings.appearance]:

Value Mode
0 fill (default)
1 fit
2 tile
3 center

The wallpaper path and mode are watched on config reload: changing either one respawns the wallpaper client live.

Screenshot

The screenshot actions can be bound to any keybind via the [keybinds] table:

"Super+Shift+S" = "screenshot region"   # interactive region selection
"Super+Shift+W" = "screenshot window"   # focused window
"Super+Shift+O" = "screenshot output"   # focused output

region opens an interactive selection overlay (slurp style); the captured image is copied to the clipboard. Super+Shift+S is bound to screenshot region by default.

When an app requests screen sharing, Zurin shows a consent menu and, for window sources, an interactive window picker. The menu entry under [apps] must point to a dmenu-compatible picker:

[apps]
menu = "fuzzel --dmenu"   # or "wmenu"

The picker reads the options from stdin and prints the selection to stdout.

IPC

Zurin exposes a control socket at $XDG_RUNTIME_DIR/zurin.sock. The zbus client is the supported way to talk to it:

zbus workspaces              # List active workspaces (JSON)
zbus workspace set N         # Switch to workspace N (or next|prev)
zbus monitors                # List connected monitors (JSON)
zbus clients                 # List open windows (JSON)
zbus apps                    # List configured applications (JSON)
zbus open <app_name>         # Launch an application from [apps]
zbus close <id>              # Close a specific window
zbus focus <id|next|prev|left|right|up|down>
zbus show-focus              # Show details of the focused window (JSON)
zbus version                 # Compositor and protocol version
zbus status                  # Runtime status (mode, layout, gaps, …)
zbus dispatch <action> …     # Run any keybind action (see docs/actions.md)
zbus spawn <command>         # Run a shell command detached
zbus reload                  # Reload the TOML config
zbus gaps set <in> <out>     # Runtime gaps (gaps reset restores the TOML values)
zbus opacity <id|active> <v> # Per-window opacity (opacity reset <id> too)
zbus move <id> <monitor>     # Move a window to another monitor
zbus monitor focus <name>    # Focus a monitor
zbus screenshot region|window|output

zbus subscribe [event...] streams JSON events (workspace, focus, open, close, title, urgent, state, mode, monitor) on a persistent connection — ideal for status bars and scripts:

zbus subscribe workspace focus
{"event":"workspace_changed","monitor":"eDP-1","workspace":3}
{"event":"client_focused","id":"0x...","title":"kitty","appid":"kitty"}

The socket is created with mode 0600 and every connection is verified with SO_PEERCRED (only the same UID is accepted), so a foreign local user cannot drive the compositor even if the socket path leaks. Full protocol reference: docs/ipc.md.

License

See LICENSES/ directory:

  • LICENSE.dwl — dwl (GPL-3.0)
  • LICENSE.wlroots — wlroots (MIT)
  • LICENSE.tomlc99 — tomlc99 TOML parser (MIT)
  • LICENSE.stb — stb_image (public domain / MIT dual-licensed; used under the public-domain terms)

Contributing

Contributions are welcome! Please submit a pull request and open an issue for bug reports and feature requests.

Thanks to

  • Hyprland for the excellent work, which I have appreciated over the last two years as a Hyprland user ❤️
  • DWL for its simplicity, which taught me how to implement basic features of a Wayland compositor
  • wlroots for the modules that I have used to write this compositor