dorkhub

driftwm

A trackpad-first infinite canvas Wayland compositor.

malbiruk
Rust1.5k63 forksupdated 1 day ago
visit the demogit clone https://github.com/malbiruk/driftwm.gitmalbiruk/driftwm

driftwm

A trackpad-first infinite canvas Wayland compositor.

License: GPL-3.0-or-later GitHub Release Packaging status

Primary repository: GitHub · Mirror: Codeberg

demo.mp4

Traditional window managers arrange windows to fit your screen. Stacking compositors do so by piling windows on top of each other; tiling compositors do so by squeezing them to fit and utilizing workspaces.

driftwm is an infinite-canvas compositor: windows live at their native size on an infinite 2D canvas, and your display is a camera viewing it. When two windows come close, they snap together, forming implicit groups that can be moved, resized, and viewed together. No tiling, no workspaces, window overlaps happen only on purpose.

Designed with laptops in mind: navigation and window management are trackpad-first; the infinite canvas makes the most of a small screen.

Built on smithay. Inspired by vxwm; borrows implementation details from niri.

Warning

This is experimental software, primarily built with AI.

Features

Pan & zoom

pan_zoom.mp4

Infinite 2D canvas with viewport panning, zoom, and scroll momentum. A quick flick carries the viewport smoothly until friction stops it.

Pan & zoom bindings
Input Action Context
3-finger swipe Pan viewport anywhere
Trackpad scroll Pan viewport on-canvas
Mod + LMB drag Pan viewport anywhere
Mod+Ctrl + arrow Pan viewport
2-finger pinch Zoom on-canvas
3-finger pinch Zoom anywhere
Mouse wheel Zoom on-canvas
Mod + scroll Zoom at cursor anywhere
Mod+= / Mod+- Zoom in / out
Mod+0 / Mod+Z Reset zoom to 1.0

Window navigation

navigation.mp4

Jump to the nearest window in any direction. MRU cycling (Alt-Tab) with hold-to-commit. Zoom-to-fit shows all windows at once. Configurable anchors act as navigation targets for directional jumps even with no window there — useful for areas with pinned widgets. Bookmarks: named canvas spots the camera jumps straight to.

Navigation bindings
Input Action
4-finger swipe Jump to nearest window (natural direction)
Mod+Ctrl + LMB drag Jump to nearest window (natural direction)
Mod + arrow Jump to nearest window in direction
Alt-Tab / Alt-Shift-Tab Cycle windows (MRU)
4-finger pinch in / Mod+W Zoom-to-fit (overview)
4-finger pinch out / Mod+A Home toggle (origin and back)
4-finger hold / Mod+C Center focused window
Mod+1-4 Jump to bookmarked canvas position
Mod+Shift+1-4 Save the current position as that bookmark

All 4-finger navigation gestures also work as Mod + 3-finger for smaller trackpads.

Snapping

snap.mp4

Move window with 3-finger doubletap-swipe or Alt + drag. Resize with Alt + 3-finger swipe. Snapping kicks in as edges approach each other. Drag past the viewport edge and the canvas auto-pans.

Snapped windows form a cluster. Neighbors stay visible at your view's edge for spatial context, and Shift + any move/resize/fit action acts on the whole cluster. Shuffle a layout in one drag, resize a row of panes proportionally, or scope an overview to just the cluster (Mod+Shift+W). No explicit grouping to manage.

Tip

While dragging a window, keyboard shortcuts still work. Use Mod+1-4 to jump to a bookmark or Mod+A to go home — your held window comes with you.

Fit-window (Mod+M) is the maximize analogue — centers the viewport, resets zoom to 1.0, and resizes the window to fill the screen. Toggle again to restore. Fullscreen (Mod+F) fills the screen with the focused window until any canvas action — launching an app, navigating — exits it.

Snapping & window bindings
Input Action
3-finger doubletap-swipe Move window
Alt + LMB drag Move window
Alt+Shift + LMB drag Move snapped windows
Alt + 3-finger swipe Resize window
Alt+Shift + 3-finger swipe Resize snapped window
Alt + RMB drag Resize window
Alt + MMB click / Mod+M Fit window (maximize/restore)
Alt+Shift + MMB click / Mod+Shift+M Fit snapped window
Mod + 4-finger pinch in / Mod+Shift+W Zoom-to-fit snapped windows
Alt + 2-finger pinch in/out Fit window
Alt + 3-finger pinch in/out Toggle fullscreen
Mod + MMB click / Mod+F Toggle fullscreen
Mod+Shift + arrow Nudge window (default 20px)

Touchscreen

touch.mp4

Everything works by touch too: pan and zoom the canvas, jump between windows, and move or resize windows — even whole window groups — exactly as you would on a trackpad.

Touch gestures
Input Action Context
1-finger swipe Pan viewport on-canvas
2-finger swipe Pan viewport on-canvas
3-finger swipe Pan viewport anywhere
2-finger pinch Zoom on-canvas
3-finger pinch Zoom anywhere
4-finger swipe Jump to nearest window anywhere
4-finger pinch in / out Zoom-to-fit / home toggle anywhere
3-finger tap Center window anywhere
3-finger double-tap Fit window on-window
3-finger doubletap-swipe Move window (hold: cluster) on-window
3-finger hold-swipe Resize window on-window

Infinite background

background.mp4
gigapixel_wp.mp4

The background is part of the canvas — it scrolls and zooms with the viewport, not stuck to the screen.

Five modes:

  • default — the built-in dot grid, what you get with no configuration.
  • shader — procedural GLSL, animated or static, optionally sampling an image via texture. See docs/shaders.md to write your own. Bundled shaders live in extras/wallpapers/{static,animated,textured}/.
  • tile — PNG/JPG (single texture, tiled infinitely), or a tiled pyramidal TIFF for gigapixel wallpapers. Set mirror_tile = true to mirror-fold a non-seamless image so it tiles without seams (kaleidoscope look).
  • wallpaper — single image scaled to cover the viewport, aspect-preserving (does not scroll/zoom) — a classic desktop wallpaper.
  • none — no built-in background, so an external wlr-layer-shell wallpaper daemon (swaybg, swww, mpvpaper for live video) becomes the wallpaper instead.
[background]
type = "shader"
path = "~/.config/driftwm/bg.glsl"
# texture = "~/Pictures/img.jpg"  # if it's a texture-based shader

# Or: type = "tile",      path = "~/Pictures/tile.png"
# Or: type = "tile",      path = "~/Pictures/world.tif"   # pyramidal TIFF
# Or: type = "wallpaper", path = "~/Pictures/wallpaper.jpg"
# Or: type = "none"                                       # external wallpaper daemon (swaybg/mpvpaper/…)

Window rules

window_rules.mp4

Match windows by app_id and/or title (glob patterns) and control position, size, decorations, blur, opacity, key pass-through, and placement — fields combine freely.

Two special placement modes: widget = true fixes a window to the canvas (immovable, below normal windows, out of Alt-Tab — clocks, trays, and layer-shell surfaces like waybar); pinned_to_screen = true fixes it to the screen instead, so it ignores pan/zoom and floats above normal windows (Picture-in-Picture, call toolbars) — toggleable live with Mod+T.

# Frosted-glass terminal
[[window_rules]]
app_id = "Alacritty"
opacity = 0.85
blur = true

# Desktop widget — pinned to the canvas, borderless
[[window_rules]]
app_id = "my-clock"
position = [50, 50]
widget = true
decoration = "none"

Tip

To find a window's app_id or title, run driftwm msg state — it lists every open window with its app ID, title, position, and size.

See docs/window-rules.md for more details.

Multi-monitor

multi_monitor.mp4

Multiple monitors are independent viewports on the same canvas. An outline on each monitor shows where the other monitors' viewports are. Cursor crosses between monitors freely; dragged windows teleport to the target viewport's canvas position.

Multi-monitor bindings
Input Action
Mod+Alt + arrow Send window to adjacent output

Panels, docks & taskbars

docks.mp4

Layer shell surfaces (waybar, fuzzel, mako) work as expected. Docks and taskbars see every window — click one and the viewport pans to it and centers it. Bookmarks are exported as workspaces over ext-workspace-v1 — bars that support it can list them and click to jump. It's an export for tooling, not real workspaces; driftwm is still one canvas.

Window suspend & session restore

session.mp4

Close a window and leave a placeholder behind instead of losing it: suspend-window swaps the window for a compositor-drawn stand-in at the same canvas spot — press Enter or click its name to bring the app right back, in the same place. suspend_on_close does this automatically for every client-initiated close. [session].restore_windows saves your whole canvas on quit/logout and restores it (dormant, nothing auto-launches) on the next start, with restore_camera bringing each output's view back too.

See docs/session.md.

Everything else

  • New window placement: in viewport center (default), under cursor, or snapped adjacent to whatever is in view
  • Click-to-focus (default) or focus-follows-mouse (sloppy focus)
  • Hot corners: any keybinding action fires when the cursor reaches a screen corner, configured per monitor
  • Cursor edge-pan: push the pointer against a screen edge and the viewport pans, toggled with Mod+E
  • Session lock and idle notification
  • Screen capture: screencasting (OBS, Firefox, Discord) and screenshots, incl. built-in canvas/DPI capture
  • 40+ Wayland protocols
  • IPC control: script the compositor over a Unix socket with driftwm msg (full command/flag reference: docs/cli.md)

Install

Arch Linux (AUR)

yay -S driftwm

or for latest main:

yay -S driftwm-git

NixOS / Nix

A flake.nix is included. To build:

nix build

For development (provides native deps, uses your system Rust):

nix develop
cargo build
cargo run

To enable driftwm on NixOS, you can import and use the provided NixOS module in your configuration.

Using Flakes:

# flake.nix
{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    driftwm.url = "github:malbiruk/driftwm";
  };

  outputs = { self, nixpkgs, driftwm, ... }: {
    nixosConfigurations.myHost = nixpkgs.lib.nixosSystem {
      modules = [
        driftwm.nixosModules.default
        ./configuration.nix
      ];
    };
  };
}

Then, enable it in your configuration:

# configuration.nix
{
  programs.driftwm.enable = true;
}

Without flakes, import the flake's output directly:

let
  driftwm-flake = builtins.getFlake "github:malbiruk/driftwm";
in
{
  imports = [ driftwm-flake.nixosModules.default ];
  programs.driftwm.enable = true;
}

programs.driftwm.package selects the package providing the compositor binary. XWayland via xwayland-satellite is on by default; set programs.xwayland.enable = false; to disable it.

Build from source

Requires Rust 1.88+ (edition 2024).

Install build dependencies:

Fedora:

sudo dnf install libseat-devel libdisplay-info-devel libinput-devel mesa-libgbm-devel libxkbcommon-devel

Ubuntu/Debian:

sudo apt install libseat-dev libdisplay-info-dev libinput-dev libudev-dev libgbm-dev libxkbcommon-dev libwayland-dev

Arch Linux:

sudo pacman -S libdisplay-info libinput seatd mesa libxkbcommon

Note

Ubuntu 24.04 ships Rust 1.75 which is too old. Install via rustup instead of apt install rustc.

Then build and install:

git clone https://github.com/malbiruk/driftwm.git
cd driftwm
make build
sudo make install

To uninstall, run sudo make uninstall from the repository.

Offline build

Every release carries a driftwm-<version>-vendor.tar.xz asset holding all Rust dependencies plus the .cargo/config.toml that points at them, for building without network access:

sha256sum -c driftwm-<version>-vendor.tar.xz.sha256 &&
  tar xf driftwm-<version>.tar.gz &&
  tar xf driftwm-<version>-vendor.tar.xz -C driftwm-<version> &&
  cd driftwm-<version> &&
  cargo build --offline --locked --release &&
  sudo make install

Important

The vendor tarball has no top-level directory: it unpacks vendor/ and .cargo/ into whatever directory it is extracted in, hence the -C. Don't prune vendor/ either — cargo checksums it against Cargo.lock, so dropping a crate you think is unused, or a file inside one, breaks --offline.

System build dependencies still come from your distro.

Running

driftwm auto-detects whether it's running nested (inside an existing Wayland session) or on real hardware (from a TTY). Just run driftwm. For display manager integration, select "driftwm" from the session menu.

mod is Super by default. The terminal is $TERMINAL, else the first of foot, alacritty, ptyxis, kitty, wezterm, gnome-terminal, konsole; the launcher is $LAUNCHER, else the first of fuzzel, wofi, rofi, bemenu-run, wmenu-run, tofi-drun, mew-run. Both are overridable in config.

Shortcut Action
mod+return Open terminal
mod+d Open launcher
mod+q Close window
mod+l Lock screen
mod+ctrl+shift+q Quit

Feature-specific bindings (navigation, zoom, snap) are in their respective sections above.

Nested sessions don't save or restore state unless you pass --session-file <path>.

Tip

When launched by a display manager, driftwm runs as a systemd user service — view logs with journalctl --user -u driftwm.service (add -f to follow). Run directly and logs go to stderr.

Optional runtime dependencies

Each of these enables or improves a feature:

  • xwayland-satellite (≥ 0.7) — X11 app support (see below).
  • xdg-desktop-portal + xdg-desktop-portal-wlr (≥ 0.8.0) or xdg-desktop-portal-cosmic — screencasting, and screenshot apps that go through the portal (e.g. Flameshot). wlr needs a dmenu-style picker in $PATH (wmenu/wofi/rofi/bemenu/mew/fuzzel) to choose what to share.
  • grim + slurp — screenshots (+ cropping to region).
  • adwaita-fonts — renders SSD title bars in Adwaita Sans to match GTK apps; without it a generic sans-serif is substituted. Font, size, weight, and alignment are configurable under [decorations].
  • A cursor theme — most desktops set one up already; on a bare install driftwm falls back to a basic built-in arrow.

X11 apps run through xwayland-satellite, which driftwm spawns at startup, exporting DISPLAY=:N so X11 clients connect transparently — no extra config beyond having the binary in $PATH.

  • Arch: sudo pacman -S xwayland-satellite
  • Fedora: sudo dnf install xwayland-satellite
  • NixOS: pkgs.xwayland-satellite
  • Debian/Ubuntu: not yet packaged — cargo install --locked xwayland-satellite

If satellite isn't found at startup, driftwm logs a warning and continues without X11 support. You can override the binary path or disable the integration in config.reference.toml under [xwayland].

Configuration

Config file: ~/.config/driftwm/config.toml (respects XDG_CONFIG_HOME).

mkdir -p ~/.config/driftwm
cp /etc/driftwm/config.reference.toml ~/.config/driftwm/config.toml

Missing file uses built-in defaults. Partial configs merge with defaults — only specify what you want to change. Use "none" to unbind a default binding. Validate without starting: driftwm --check-config.

# Launch programs at startup
autostart = ["waybar", "swaync", "swayosd-server"]

Every option is documented in docs/config.md, generated from config.reference.toml.

Example setup

driftwm is just a compositor — everything else is standard Wayland tooling. Here are some tools that work well with it:

  • waybar — Status bar / taskbar
  • crystal-dock — macOS-style dock
  • fuzzel / wofi — App launcher
  • mako / swaync — Notifications
  • swaylock — Lock screen
  • swayidle / hypridle — Idle timeout (lock, suspend)
  • swayosd — Volume/brightness OSD
  • wlr-randr / wdisplays — Output configuration
  • COSMIC Settings — Wi-Fi, Bluetooth, sound (or nm-applet + blueman + pavucontrol)

Compositor-agnostic full Wayland shells like noctalia, wayle, and dank-material-shell should work too (driftwm supports wlr-layer-shell protocol) but without compositor-specific features.

The extras/ directory contains a complete setup — driftwm config, GLSL shader wallpapers, Python widgets (clock, calendar, system stats, power menu), waybar with taskbar/tray, a fuzzel spotlight script that lists open windows, suspended windows, and installed apps together, and window rules tying it all together. Use it as a starting point or steal pieces.

Community

Contributing

See CONTRIBUTING.md.

TL;DR: open an issue before writing non-trivial code, keep PRs small and focused.

Merch

If you want to support the project (or just want a shirt), this is the way.

XL

100 GEL · 37 USD · 2800 RUB

Ships worldwide from Tbilisi.

Order via Telegram, Instagram, or email 2601074@gmail.com.

Revenue goes to me as driftwm's primary maintainer.

License

GPL-3.0-or-later

more like this

anne-key

Firmware for Anne Pro Keyboard written in Rust

Rust572

TachiSnap

TachiSnap — Pixel Snapper for animation pixel artists. Rust + WebAssembly client-side tool for cleaning up AI-generated…

HTML50

bit_gossip

Pathfinding library for calculating all node pairs' shortest paths in an unweighted undirected graph.

Rust50

search

search projects, people, and tags