Help us shape the future of Go! Take the 2026 Go Developer Survey

neferwl

module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 2, 2026 License: GPL-3.0

README

NeferWL

Last commit GitHub stars

A Wayland compositor for people who live in a terminal and die in games.

[!WARNING] Early alpha. NeferWL is developed and tested mostly on AMD CPUs and GPUs. NVIDIA support is incomplete and untested; expect bugs and breaking config changes.


Why NeferWL

NeferWL exists because I wanted a compositor that puts performance first and picks up new Wayland protocols and kernel features as soon as they land. Games get the whole GPU, and the compositor stays out of the way.

The design is deliberately small, and it will stay small: no animations, no themes, no built-in bar or wallpaper. Anything that adds latency or work per frame is left out. About 40–80 MB of RAM with two 4K monitors, and almost no CPU while the screen does not change.

Features

  • Games. Fullscreen is exclusive: nothing is drawn above a fullscreen window but a locker, so it is scanned out directly, with tearing, VRR and explicit sync when the client asks for them. Wine runs natively on Wayland; Steam and other X11 clients run through xwayland-satellite. See the performance path.
  • HDR. HDR10 output on capable displays. HDR clients (games, browsers, video players) are shown at full range; SDR content is shown at a configured brightness.
  • Column tiling. Up to max-columns windows share the screen, as in Sway; past that, columns scroll to the right, as in Niri. Limits are set per output or per workspace.
  • Workspaces. Numbered workspaces are created and removed as needed. Named workspaces declare their columns (width and command); NeferWL starts the commands and places each window.
  • Multi-monitor. Each output has its own workspaces. When an output is unplugged its workspaces move to another one, and return when it is plugged back. Fractional scale per output.
  • External clients. No built-in bar, launcher or notifications. Waybar, fuzzel, mako, nefercap, cliphist, swayidle, wlr-randr and input methods such as fcitx5 work through their standard protocols. See Desktop integration.
  • Config. One key = value file, reloaded on save without closing windows. A JSON state file describes outputs, workspaces and windows for scripts.

Config example:

keyboard.layout = fr
terminal.auto-open = first
startup = waybar

output.DP-1 = 3840x2160@144
output.DP-1.scale = 1.5

layout.max-columns = 3

# A bind-only dev workspace: editor and terminal, started and placed for you.
workspace.dev.monitor = DP-1
workspace.dev.column.1 = 67%, code --new-window
workspace.dev.column.2 = 33%, foot
bind.cmd+d = workspace dev

The terminal command comes from terminal, then $TERMINAL, then foot. terminal.auto-open = first opens it once on the initial numbered workspace; all opens it on each visible empty workspace without slots, and off disables automatic opening. --no-terminal overrides automatic opening for one run; spawn-terminal still works. Launched programs inherit $SHELL, $EDITOR, and $VISUAL.

The performance path

These protocols and kernel features cut copies, waits and latency between the app and the screen:

  • Zero copy. GPU clients (zwp_linux_dmabuf_v1 v4) are sampled in place. Vulkan composes straight into exported scanout images, and the frame fence goes to KMS as IN_FENCE_FD: no CPU copy, no CPU wait before a flip.
  • Direct scanout. A fullscreen window's buffer goes straight to the display plane; dmabuf feedback sends the game a scanout-ready format first. A lone opaque window can use an overlay plane.
  • Tearing and VRR. wp_tearing_control_v1 flips without waiting for vblank when the game requests it; variable refresh runs while a fullscreen window covers the output, scanned out or composed.
  • Explicit sync. wp_linux_drm_syncobj_v1 passes GPU fences from the app to KMS. NVIDIA drivers need it.
  • Frame pacing. Apps get real flip times through wp_presentation, and can queue frames with wp_fifo_v1 and wp_commit_timing_v1.
  • Only what changed. NeferWL redraws only damaged regions, skips surfaces hidden behind opaque ones, and leaves idle outputs alone. Frame, cursor, overlay and VRR go to the kernel in one atomic commit.
  • Input. Relative pointer, pointer constraints and keyboard shortcuts inhibit for games. Input at 1 kHz and more never waits on rendering, and the cursor has its own hardware plane. With CAP_SYS_NICE, input and output threads request real-time scheduling.
  • X11 games. Steam and X11-only games run through xwayland-satellite, started on the first X11 connection. Wine with its Wayland driver does not need it.
  • VR. Non-desktop headset connectors are leased to SteamVR and Monado via wp_drm_lease_v1.

Hardware support

NeferWL runs on Linux. It needs a GPU and kernel driver with atomic KMS, and a Vulkan 1.3 device that can export images as dmabufs. Without them, it stops at startup with an error.

  • AMD: developed and tested on radv.
  • NVIDIA: untested. Explicit sync, which NVIDIA drivers need, is implemented. Reports from NVIDIA users are welcome.
  • Intel: untested.

Install (Arch Linux)

From the AUR:

paru -S neferwl-bin   # latest release, pre-built for x86_64 and aarch64
paru -S neferwl-git   # latest main, built from source

From a checkout (needs Go 1.27):

make install   # builds dist/neferwl-*.pkg.tar.zst from HEAD, then installs it with pacman

make pkg only builds the package. The package installs neferwl with CAP_SYS_NICE (real-time scheduling, see performance) and a NeferWL session for display managers such as Ly, GDM and SDDM. The session runs neferwl-session, which starts neferwl.service under systemd. This starts graphical-session.target and services bound to it (bars, notification daemons, portals, and XDG autostart) with NeferWL and stops them with it. Running neferwl --session alone still exports the display environment to D-Bus and systemd user services.

On other distributions, download a linux_amd64 or linux_arm64 archive from Releases and check it against checksums.txt, or run make bin to build bin/neferwl.

Releases

Pushing a v* tag runs .github/workflows/release.yml. It runs the CI checks, then GoReleaser builds the archives, checksums.txt, a provenance attestation and the GitHub release. Last, packaging/aur/publish.sh builds neferwl-bin and neferwl-git with makepkg, checks every source checksum for x86_64 and aarch64, and pushes them to the AUR. Pre-release tags (v0.2.0-rc1) get a GitHub release and are kept off the AUR.

The workflow needs the AUR_SSH_PRIVATE_KEY, AUR_USERNAME and AUR_EMAIL repository secrets.

Configuration

NeferWL reads $XDG_CONFIG_HOME/neferwl/config, or ~/.config/neferwl/config. neferwl --config <path> selects another file, and neferwl validate-config [path] checks a file without starting the compositor.

The file has one key = value per line, and # starts a comment. A missing file or key means the default. An invalid line logs a warning and keeps that key's default; the rest of the file still applies. Changes apply when you save, and each reload logs the keys that changed.

docs/config.md lists every key, action and default bind. examples/config is a commented copy of the defaults.

Documentation

  • Configuration: every key, action and default bind, and HDR.
  • Desktop integration: running commands, clipboard, input methods, X11 apps, idle and screen off, bars, and the state file for scripts.
  • Session locking: external lockers, output confirmation, owner recovery and security limits.
  • Screen capture: capture protocols, the indicator, the executable allowlist, sandboxed clients and portals.
  • Headless mode: run without a screen, take screenshots, play input scripts and test input methods.
  • Performance: how to profile, the allocation guards, and reference numbers.

Why not Rust?

Most Wayland compositors are written in C (wlroots, Sway, Mutter), C++ (KWin, Hyprland, gamescope) or Rust (niri, COSMIC). NeferWL uses Go on purpose:

  • Readable. Go is small and explicit. Goroutines and channels fit a compositor where input, each output and the clients own their own state.
  • Tooling. A full build takes seconds, and tests, the race detector, profiling and formatting come with the language.
  • No cgo. NeferWL builds with CGO_ENABLED=0: libwayland, Vulkan and libinput are loaded at runtime through purego.
  • Speed. With care for allocations and the garbage collector, input, rendering and buffer handling run close to native code.

I like bringing more tools to the Go ecosystem. NeferWL grew its own libraries along the way, purego-libwayland, purego-vulkan and wlturbo, and other Go projects can use them.

Developing the bindings

NeferWL uses tagged releases of purego-libwayland and purego-vulkan. To change a binding and NeferWL together, clone the binding next to NeferWL and use a Go workspace, which is not committed:

go work init . ../purego-libwayland ../purego-vulkan

make pkg ignores the workspace and builds the versions in go.mod. Tag the binding change and update go.mod before packaging.

Contributing

Contributions are welcome. Open an issue first, for a bug fix or a feature, and wait until it is accepted before you send a pull request. Pull requests without an accepted issue are closed.

Before you open a pull request, make check, make race, make mocks-check and staticcheck ./... must pass. Repository rules are in AGENTS.md.

License

NeferWL is licensed under the GNU General Public License v3.0.

Directories

Path Synopsis
cmd
neferwl command
examples
testime command
Command testime is a minimal zwp_input_method_v2 client for testing text input.
Command testime is a minimal zwp_input_method_v2 client for testing text input.
internal
adapters/capture
Package capture transfers each request to an output: the output closes its destination fd exactly once and attempts one reply, never blocking after its context is done.
Package capture transfers each request to an output: the output closes its destination fd exactly once and attempts one reply, never blocking after its context is done.
adapters/captureallow
Package captureallow reads the screen-capture allowlist: the executables that may capture the screen.
Package captureallow reads the screen-capture allowlist: the executables that may capture the screen.
adapters/clock
Package clock is the wall clock behind ports.Clock.
Package clock is the wall clock behind ports.Clock.
adapters/config
Package config reads the flat `key = value` config file (ADR 012).
Package config reads the flat `key = value` config file (ADR 012).
adapters/drm
Package drm drives KMS outputs with raw ioctls and atomic commits.
Package drm drives KMS outputs with raw ioctls and atomic commits.
adapters/headlessinput
Package headlessinput converts scripted text into keyboard events.
Package headlessinput converts scripted text into keyboard events.
adapters/libinput
Package libinput reads keyboards and pointers through libinput's udev backend.
Package libinput reads keyboards and pointers through libinput's udev backend.
adapters/presented
Package presented queues the OutputPresented reports an output owner sends to wayland: what the output read, held and flipped.
Package presented queues the OutputPresented reports an output owner sends to wayland: what the output read, held and flipped.
adapters/sched
Package sched configures latency-sensitive compositor threads.
Package sched configures latency-sensitive compositor threads.
adapters/seat
Package seat opens a libseat session (logind or seatd) and hands out device fds.
Package seat opens a libseat session (logind or seatd) and hands out device fds.
adapters/sessionsecurity
Package sessionsecurity supplies a coherent defensive session gate to boundaries outside the Wayland display goroutine.
Package sessionsecurity supplies a coherent defensive session gate to boundaries outside the Wayland display goroutine.
adapters/statefile
Package statefile publishes core's state as JSON for scripts, and reads it back for the `neferwl state` command.
Package statefile publishes core's state as JSON for scripts, and reads it back for the `neferwl state` command.
adapters/syncfile
Package syncfile waits for Linux sync files (dma-fence fds), such as the GPU fence a frame render returns.
Package syncfile waits for Linux sync files (dma-fence fds), such as the GPU fence a frame render returns.
adapters/workspaceid
Package workspaceid names workspaces for programs outside the compositor: the `id` of ext_workspace_handle_v1 and the `workspace_id` of the state file are the same string.
Package workspaceid names workspaces for programs outside the compositor: the `id` of ext_workspace_handle_v1 and the `workspace_id` of the state file are the same string.
adapters/xcursor
Package xcursor loads cursor images from Xcursor themes in pure Go.
Package xcursor loads cursor images from Xcursor themes in pure Go.
adapters/xkb
Package xkb provides a cgo-free libxkbcommon keymap and keyboard state.
Package xkb provides a cgo-free libxkbcommon keymap and keyboard state.
adapters/xwayland
Package xwayland runs X11 clients through xwayland-satellite, started on demand: neferwl owns the X11 display sockets and starts the satellite when the first X11 client connects, and again after it exits.
Package xwayland runs X11 clients through xwayland-satellite, started on demand: neferwl owns the X11 display sockets and starts the satellite when the first X11 client connects, and again after it exits.
app
Package app coordinates the application lifecycle.
Package app coordinates the application lifecycle.
core
Package core holds pure layout, focus, and command domain logic.
Package core holds pure layout, focus, and command domain logic.
logging
Package logging configures component-scoped zerowrap logging.
Package logging configures component-scoped zerowrap logging.
ports
Package ports defines boundary interfaces and channel message types.
Package ports defines boundary interfaces and channel message types.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL