No description
  • Zig 85.8%
  • Nix 10.8%
  • Rust 2.1%
  • Shell 1.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-06 20:34:40 +02:00
contrib protocol: add legacy parsing for ashell integration + test + update completions 2026-09-26 19:40:56 +02:00
nix nix: general clean up of interaction with cnix 2026-10-06 20:34:40 +02:00
protocol nix: fix river package patch 2026-10-02 17:53:55 +02:00
src eddy: spawn logic rewrite 2026-10-04 14:59:33 +02:00
.envrc first commit 2026-08-26 11:04:04 +02:00
.gitignore first commit 2026-08-26 11:04:04 +02:00
build.zig wlroots: temp patch for dma 2026-10-02 17:25:23 +02:00
build.zig.zon eddy: spawn logic rewrite 2026-10-04 14:59:33 +02:00
example-config.zon wlroots: temp patch for dma 2026-10-02 17:25:23 +02:00
flake.lock eddy: spawn logic rewrite 2026-10-04 14:59:33 +02:00
flake.nix nix: remove wlroots debug patch, misc nix updates 2026-10-03 12:08:24 +02:00
LICENSE ops year 2026-08-26 11:09:28 +02:00
README.md nix: update, add clipboard option 2026-09-27 12:53:09 +02:00

\Delta delta

A window manager for river.

River 0.5 splits the compositor from the window manager: river handles rendering, input and output configuration, and a separate client decides where windows go. delta is such a client, speaking river-window-management-v1.

Written in Zig. Two dependencies: zig-wayland and zig-xkbcommon.

Status & Contribution

Usable, I'm currently daily-driving it, however, it should not be considered stable. Expect the config format to change, and things to break; I do this on my spare time as a hobby. Contributions are appreciated, but perhaps forking the code and building something for yourself is more advisible.

Works: bsp-ish tiling, workspaces, floating windows, fullscreen, keyboard and pointer resize, directional focus, key repeat, pointer warping, layer shell (bars, launchers, lock screens), window rules, live config reload, an IPC with a CLI (delctl, which can also trigger any keybinding action), multi-seat, basic animations.

Missing: rules that re-evaluate when a window renames itself. Multi-seat is untested with more than one real seat. General hardening, optimization, and time.

Notes: I have only tested this on NixOS and I have no immediate plans to change this fact. There is a functioning ashell patch packaged with my nixosModule. This enables ashell's workspace widget. I'm also tinkering with what will be an optional screen locker, stay tuned (as if I'm not the only one reading this).

Disclaimer: What you read below might not be completely up to date.

Building

zig build

Needs Zig 0.16, wayland-client, libxkbcommon, and the Linux input headers.

Run it under river:

river -c /path/to/delta-wm

Nix

The flake exposes packages.delta-wm, packages.river (river 0.5 built with the Vulkan renderer available), and nixosModules.default.

{
  inputs.delta.url = "git+https://git.cnst.dev/cnst/delta";

  # in your NixOS configuration
  imports = [ inputs.delta.nixosModules.default ];

  programs.river-delta = {
    enable = true;
    renderer = "vulkan";
  };
}

The module sets up a systemd user session, an XDG portal, and optionally kanshi and clipboard.persist (wl-clip-persist, so a copy survives its app exiting). Its river build carries a small wlroots patch so Wine and other X11 apps that read the clipboard only when it changes still see Wayland copies. programs.river-delta.sessionScript is the entry point to hand to greetd directly, rather than scraping Exec= out of a desktop entry.

The config file is not generated. Write ~/.config/delta/config.zon by hand, delta reloads it when you save.

Configuration

$XDG_CONFIG_HOME/delta/config.zon, falling back to ~/.config/delta/config.zon. See example-config.zon for the full set of options, everything has a default and anything omitted keeps it.

.{
    .gaps = .{ .between = 8, .edge = 8 },

    .bindings = .{
        .{ .mods = .{.super}, .keys = .{"Return"}, .action = .{ .spawn = .{"foot"} } },
        .{ .mods = .{.super}, .keys = .{"q"}, .action = .close },
        .{ .mods = .{.super}, .keys = .{ "h", "Left" }, .action = .{ .focus_direction = .left } },
    },

    .window_rules = .{
        .{ .matches = .{.{ .app_id = "vesktop" }}, .workspace = 4 },
        .{ .matches = .{.{ .app_id = "*", .dialog = true }}, .float = true },
    },
}

A window counts as a .dialog if it has a parent or a fixed size. Dialogs float by default, and floating windows always stack above tiled ones.

ZON rather than a bespoke format: no parser to write, no dependency to add, type and field errors for free, and it generates cleanly from Nix.

delctl

delctl queries and drives a running delta over its IPC socket:

delctl windows                      # also: outputs, workspaces, layers, focused, watch
delctl action focus_workspace 3
delctl action spawn foot -e htop
delctl action                       # list every action and its arguments

--json prints delta's raw reply instead of a table.

Design

Four rules the code follows, listed because they explain most of its shape:

Dependencies point inward. main -> Delta -> entities -> utilities. Nothing imports main.

Listeners record, they never act. The protocol allows window management state to be modified only inside a manage sequence, so events set fields and manage turns them into requests.

Compositor state is edge-triggered. Every request is sent when its answer changes and not otherwise, which is why Window carries a record of what the compositor has already been told.

Layouts know windows and rectangles. A layout divides an area into boxes that tile it exactly, layouts/rules.zig turns a box into a content rect. Gaps and borders are decided in one place, so a second layout inherits them for free.

Name

Delta, because river delta.

License

0BSD. Derived from Isaac Freund & Vladyslav Khardel's tinywm.