A trackpad-first infinite canvas Wayland compositor.
See the code
A trackpad-first infinite canvas Wayland compositor.
Primary repository: GitHub · Mirror: Codeberg
https://github.com/user-attachments/assets/155511a6-0a6e-4681-9061-21be1e93e02a
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.
https://github.com/user-attachments/assets/97c2ff83-acfa-40ec-ae02-b16ad9a47318
Infinite 2D canvas with viewport panning, zoom, and scroll momentum. A quick flick carries the viewport smoothly until friction stops it.
| 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 | — |
https://github.com/user-attachments/assets/ab80b545-817d-4d9f-beea-d332b3bb3dfa
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.
| 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.
https://github.com/user-attachments/assets/6bfc3458-664f-4746-a176-18f40337d94d
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-4to jump to a bookmark orMod+Ato 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.
| 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) |
https://github.com/user-attachments/assets/35316541-ad39-4c36-95ab-4093bd48c172
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.
| 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 |
https://github.com/user-attachments/assets/b1581182-5e21-45c8-8559-99ab54bb5093
https://github.com/user-attachments/assets/fb1cd5a1-242c-45d7-b302-952a15aaa24d
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/…)
https://github.com/user-attachments/assets/e30e3821-1e84-4f6e-be60-adcb0ee58d3c
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_idortitle, rundriftwm msg state— it lists every open window with its app ID, title, position, and size.
See docs/window-rules.md for more details.
https://github.com/user-attachments/assets/3f6cc3a8-a4ed-4d78-80fc-d5a92478c48f
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.
| Input | Action |
|---|---|
Mod+Alt + arrow | Send window to adjacent output |
https://github.com/user-attachments/assets/31c235e6-baae-4843-bb43-aca749e41f04
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.
https://github.com/user-attachments/assets/a91d92bd-55af-4646-8be8-81f35f8f38ca
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 keeps your whole canvas saved
as you work 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.
Mod+Edriftwm msg (full command/flag reference: docs/cli.md)yay -S driftwm
or for latest main:
yay -S driftwm-git
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.
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.
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 prunevendor/either — cargo checksums it againstCargo.lock, so dropping a crate you think is unused, or a file inside one, breaks--offline.
System build dependencies still come from your distro.
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-fto follow). Run directly and logs go to stderr.
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].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.
sudo pacman -S xwayland-satellitesudo dnf install xwayland-satellitepkgs.xwayland-satellitecargo install --locked xwayland-satelliteIf 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].
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.
driftwm is just a compositor — everything else is standard Wayland tooling. A few ways to get a desktop going on top of it:
extras/ is an installable starting point — a driftwm
config, a Quickshell "home dashboard" (time, battery,
Wi-Fi, Bluetooth, audio, brightness, CPU/RAM, media, tray, power menu) pinned at
the canvas origin, GLSL shader wallpapers, and helper scripts such as a fuzzel
spotlight that searches open windows, suspended windows, and apps together, wired
into a curated setup. Run extras/install.sh to try it, or steal pieces.
Full Wayland shells like noctalia, wayle, and dank-material-shell
are compositor-agnostic and give you a complete desktop out of the box (driftwm
supports the wlr-layer-shell protocol), just without compositor-specific features
— a good newcomer-friendly option.
Or assemble your own from standard pieces:
~/Desktop contents on canvas as draggable icons.See CONTRIBUTING.md.
TL;DR: open an issue before writing non-trivial code, keep PRs small and focused.
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.
GPL-3.0-or-later
(top 24 of 27)
678 followers · starred Mar 2026
593 followers · starred Apr 2026
1,114 followers · starred Jun 2026
1,252 followers · starred Mar 2026
A trackpad-first infinite canvas Wayland compositor.
See the code
A trackpad-first infinite canvas Wayland compositor.
Primary repository: GitHub · Mirror: Codeberg
https://github.com/user-attachments/assets/155511a6-0a6e-4681-9061-21be1e93e02a
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.
https://github.com/user-attachments/assets/97c2ff83-acfa-40ec-ae02-b16ad9a47318
Infinite 2D canvas with viewport panning, zoom, and scroll momentum. A quick flick carries the viewport smoothly until friction stops it.
| 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 | — |
https://github.com/user-attachments/assets/ab80b545-817d-4d9f-beea-d332b3bb3dfa
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.
| 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.
https://github.com/user-attachments/assets/6bfc3458-664f-4746-a176-18f40337d94d
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-4to jump to a bookmark orMod+Ato 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.
| 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) |
https://github.com/user-attachments/assets/35316541-ad39-4c36-95ab-4093bd48c172
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.
| 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 |
https://github.com/user-attachments/assets/b1581182-5e21-45c8-8559-99ab54bb5093
https://github.com/user-attachments/assets/fb1cd5a1-242c-45d7-b302-952a15aaa24d
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/…)
https://github.com/user-attachments/assets/e30e3821-1e84-4f6e-be60-adcb0ee58d3c
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_idortitle, rundriftwm msg state— it lists every open window with its app ID, title, position, and size.
See docs/window-rules.md for more details.
https://github.com/user-attachments/assets/3f6cc3a8-a4ed-4d78-80fc-d5a92478c48f
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.
| Input | Action |
|---|---|
Mod+Alt + arrow | Send window to adjacent output |
https://github.com/user-attachments/assets/31c235e6-baae-4843-bb43-aca749e41f04
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.
https://github.com/user-attachments/assets/a91d92bd-55af-4646-8be8-81f35f8f38ca
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 keeps your whole canvas saved
as you work 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.
Mod+Edriftwm msg (full command/flag reference: docs/cli.md)yay -S driftwm
or for latest main:
yay -S driftwm-git
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.
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.
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 prunevendor/either — cargo checksums it againstCargo.lock, so dropping a crate you think is unused, or a file inside one, breaks--offline.
System build dependencies still come from your distro.
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-fto follow). Run directly and logs go to stderr.
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].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.
sudo pacman -S xwayland-satellitesudo dnf install xwayland-satellitepkgs.xwayland-satellitecargo install --locked xwayland-satelliteIf 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].
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.
driftwm is just a compositor — everything else is standard Wayland tooling. A few ways to get a desktop going on top of it:
extras/ is an installable starting point — a driftwm
config, a Quickshell "home dashboard" (time, battery,
Wi-Fi, Bluetooth, audio, brightness, CPU/RAM, media, tray, power menu) pinned at
the canvas origin, GLSL shader wallpapers, and helper scripts such as a fuzzel
spotlight that searches open windows, suspended windows, and apps together, wired
into a curated setup. Run extras/install.sh to try it, or steal pieces.
Full Wayland shells like noctalia, wayle, and dank-material-shell
are compositor-agnostic and give you a complete desktop out of the box (driftwm
supports the wlr-layer-shell protocol), just without compositor-specific features
— a good newcomer-friendly option.
Or assemble your own from standard pieces:
~/Desktop contents on canvas as draggable icons.See CONTRIBUTING.md.
TL;DR: open an issue before writing non-trivial code, keep PRs small and focused.
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.
GPL-3.0-or-later
(top 24 of 27)
678 followers · starred Mar 2026
593 followers · starred Apr 2026
1,114 followers · starred Jun 2026
1,252 followers · starred Mar 2026