docs: split user and developer documentation
- commit
- 84068942908de19810e6f6e6be074044af567f7c
- author
- brice · · 2026-10-21 04:39 UTC
- parent
- b2b939a
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
10 files · +85 -79
M AGENTS.md +2 -2
| @@ -26,10 +26,10 @@ Test doubles are always generated by Mockery v3 (`testify` template). Never hand | |||
| 26 | 26 | ||
| 27 | 27 | ## End-to-end tests (headless backend) | |
| 28 | 28 | ||
| 29 | - | Unit tests are not enough for user-facing behavior (binds, gestures, window placement, config keys). Test it end to end on the headless backend before declaring it done; see [docs/headless.md](docs/headless.md). | |
| 29 | + | Unit tests are not enough for user-facing behavior (binds, gestures, window placement, config keys). Test it end to end on the headless backend before declaring it done; see [docs/dev/headless.md](docs/dev/headless.md). | |
| 30 | 30 | ||
| 31 | 31 | - Build the branch binary (`CGO_ENABLED=0 go build -o <tmp>/neferwl ./cmd/neferwl`) and run it with a throwaway config: `<tmp>/neferwl --backend=headless --config <tmp>/config --no-xwayland --screenshot <tmp>/shots --input <script> --timeout 10s`. | |
| 32 | - | - Input scripts drive keys and gestures (`key Super+s`, `swipe4 down`, `sleep 1s`); see the table in docs/headless.md. | |
| 32 | + | - Input scripts drive keys and gestures (`key Super+s`, `swipe4 down`, `sleep 1s`); see the table in docs/dev/headless.md. | |
| 33 | 33 | - Logs go to `$XDG_STATE_HOME/neferwl/runs/headless/latest.log`, not stderr. Read the headless `WAYLAND_DISPLAY` from its `listening` entry. | |
| 34 | 34 | - Isolate from the user's live session: run the headless compositor, its clients and `neferwl state` with `env -u NEFERWL_STATE -u NEFERWL_SLOT WAYLAND_DISPLAY=<headless>`. `NEFERWL_STATE` wins over `WAYLAND_DISPLAY` and would read the live session's state. | |
| 35 | 35 | - Check results with `WAYLAND_DISPLAY=<headless> neferwl state` (windows, stash index/count, visibility) and the `latest.png` screenshot. | |
| @@ -26,10 +26,10 @@ Test doubles are always generated by Mockery v3 (`testify` template). Never hand | |||||
| 26 | 26 | ||||
| 27 | ## End-to-end tests (headless backend) | 27 | ## End-to-end tests (headless backend) | ||
| 28 | 28 | ||||
| 29 | - | Unit tests are not enough for user-facing behavior (binds, gestures, window placement, config keys). Test it end to end on the headless backend before declaring it done; see [docs/headless.md](docs/headless.md). | 29 | + | Unit tests are not enough for user-facing behavior (binds, gestures, window placement, config keys). Test it end to end on the headless backend before declaring it done; see [docs/dev/headless.md](docs/dev/headless.md). |
| 30 | 30 | ||||
| 31 | - Build the branch binary (`CGO_ENABLED=0 go build -o <tmp>/neferwl ./cmd/neferwl`) and run it with a throwaway config: `<tmp>/neferwl --backend=headless --config <tmp>/config --no-xwayland --screenshot <tmp>/shots --input <script> --timeout 10s`. | 31 | - Build the branch binary (`CGO_ENABLED=0 go build -o <tmp>/neferwl ./cmd/neferwl`) and run it with a throwaway config: `<tmp>/neferwl --backend=headless --config <tmp>/config --no-xwayland --screenshot <tmp>/shots --input <script> --timeout 10s`. | ||
| 32 | - | - Input scripts drive keys and gestures (`key Super+s`, `swipe4 down`, `sleep 1s`); see the table in docs/headless.md. | 32 | + | - Input scripts drive keys and gestures (`key Super+s`, `swipe4 down`, `sleep 1s`); see the table in docs/dev/headless.md. |
| 33 | - Logs go to `$XDG_STATE_HOME/neferwl/runs/headless/latest.log`, not stderr. Read the headless `WAYLAND_DISPLAY` from its `listening` entry. | 33 | - Logs go to `$XDG_STATE_HOME/neferwl/runs/headless/latest.log`, not stderr. Read the headless `WAYLAND_DISPLAY` from its `listening` entry. | ||
| 34 | - Isolate from the user's live session: run the headless compositor, its clients and `neferwl state` with `env -u NEFERWL_STATE -u NEFERWL_SLOT WAYLAND_DISPLAY=<headless>`. `NEFERWL_STATE` wins over `WAYLAND_DISPLAY` and would read the live session's state. | 34 | - Isolate from the user's live session: run the headless compositor, its clients and `neferwl state` with `env -u NEFERWL_STATE -u NEFERWL_SLOT WAYLAND_DISPLAY=<headless>`. `NEFERWL_STATE` wins over `WAYLAND_DISPLAY` and would read the live session's state. | ||
| 35 | - Check results with `WAYLAND_DISPLAY=<headless> neferwl state` (windows, stash index/count, visibility) and the `latest.png` screenshot. | 35 | - Check results with `WAYLAND_DISPLAY=<headless> neferwl state` (windows, stash index/count, visibility) and the `latest.png` screenshot. | ||
M README.md +7 -6
| @@ -16,7 +16,7 @@ _Demo video coming soon._ | |||
| 16 | 16 | ||
| 17 | 17 | ## Why NeferWL | |
| 18 | 18 | ||
| 19 | - | - **Performance:** allocation-free hot paths, no wasted CPU passes and no cgo; about 40–80 MB of RAM with two 4K monitors. On 4K HDR video, GC takes under 9% of a process using 13% of a core; see [Architecture](docs/architecture.md#keeping-the-garbage-collector-out-of-the-way) and [Performance](docs/performance.md). | |
| 19 | + | - **Performance:** allocation-free hot paths, no wasted CPU passes and no cgo; about 40–80 MB of RAM with two 4K monitors. On 4K HDR video, GC takes under 9% of a process using 13% of a core; see [Architecture](docs/dev/architecture.md#keeping-the-garbage-collector-out-of-the-way) and [Performance](docs/performance.md). | |
| 20 | 20 | - **Games:** direct scanout, VRR, tearing, explicit sync and [HDR](docs/config.md#hdr) give games the whole GPU instead of spending it on composition; see [Performance](docs/performance.md). | |
| 21 | 21 | - **Overview:** `cmd+o` or a four-finger swipe up shows live workspace previews. Select a window with the keyboard, touchpad or mouse; see [Overview](docs/config.md#overview). | |
| 22 | 22 | - **Stash:** `cmd+s` toggles a floating strip of windows set aside for the current workspace; `cmd+shift+s` moves a window into or out of it. See [Stash](docs/config.md#stash). | |
| @@ -25,7 +25,7 @@ _Demo video coming soon._ | |||
| 25 | 25 | ||
| 26 | 26 | Go fits a compositor's independent event streams: each piece of state has one owner goroutine, connected to others by channels.<br> | |
| 27 | 27 | Hot paths reuse memory, client buffers stay off the Go heap, and runtime library calls use purego rather than cgo.<br> | |
| 28 | - | See [Architecture](docs/architecture.md) for the concurrency model, GC strategy and reusable Go libraries. | |
| 28 | + | See [Architecture](docs/dev/architecture.md) for the concurrency model, GC strategy and reusable Go libraries. | |
| 29 | 29 | ||
| 30 | 30 | Small on purpose, and staying small: no blur, shadows, rounded corners or themes. No built-in bar or wallpaper either: bring your own, any layer-shell client works. | |
| 31 | 31 | ||
| @@ -179,13 +179,14 @@ Reports from NVIDIA and Intel users are welcome. | |||
| 179 | 179 | ## Documentation | |
| 180 | 180 | ||
| 181 | 181 | - [Documentation index](docs/index.md): start here. | |
| 182 | - | - [Architecture](docs/architecture.md): concurrency, GC strategy and purego libraries. | |
| 182 | + | - [Architecture](docs/dev/architecture.md): concurrency, GC strategy and purego libraries. | |
| 183 | 183 | - [Configuration](docs/config.md): keys, actions, bindings and HDR. | |
| 184 | 184 | - [Desktop integration](docs/desktop.md): sessions, clipboard, input methods, X11 apps and scripting. | |
| 185 | - | - [Session locking](docs/session-lock.md): external lockers, recovery and security limits. | |
| 185 | + | - [Session locking](docs/desktop.md#session-locking): external lockers, recovery and security limits. | |
| 186 | 186 | - [Screen capture](docs/capture.md): protocols, indicator, executable allowlist and portals. | |
| 187 | - | - [Headless mode](docs/headless.md): screenshots, input scripts and input-method testing without a screen. | |
| 188 | - | - [Performance](docs/performance.md): VRR, real-time priority, profiling and frame stats. | |
| 187 | + | - [Headless mode](docs/dev/headless.md): screenshots, input scripts and input-method testing without a screen. | |
| 188 | + | - [Performance](docs/performance.md): VRR and real-time priority. | |
| 189 | + | - [Developer documentation](docs/dev/index.md): architecture, headless testing, profiling and frame stats. | |
| 189 | 190 | ||
| 190 | 191 | ## Contributing | |
| 191 | 192 | ||
| @@ -16,7 +16,7 @@ _Demo video coming soon._ | |||||
| 16 | 16 | ||||
| 17 | ## Why NeferWL | 17 | ## Why NeferWL | ||
| 18 | 18 | ||||
| 19 | - | - **Performance:** allocation-free hot paths, no wasted CPU passes and no cgo; about 40–80 MB of RAM with two 4K monitors. On 4K HDR video, GC takes under 9% of a process using 13% of a core; see [Architecture](docs/architecture.md#keeping-the-garbage-collector-out-of-the-way) and [Performance](docs/performance.md). | 19 | + | - **Performance:** allocation-free hot paths, no wasted CPU passes and no cgo; about 40–80 MB of RAM with two 4K monitors. On 4K HDR video, GC takes under 9% of a process using 13% of a core; see [Architecture](docs/dev/architecture.md#keeping-the-garbage-collector-out-of-the-way) and [Performance](docs/performance.md). |
| 20 | - **Games:** direct scanout, VRR, tearing, explicit sync and [HDR](docs/config.md#hdr) give games the whole GPU instead of spending it on composition; see [Performance](docs/performance.md). | 20 | - **Games:** direct scanout, VRR, tearing, explicit sync and [HDR](docs/config.md#hdr) give games the whole GPU instead of spending it on composition; see [Performance](docs/performance.md). | ||
| 21 | - **Overview:** `cmd+o` or a four-finger swipe up shows live workspace previews. Select a window with the keyboard, touchpad or mouse; see [Overview](docs/config.md#overview). | 21 | - **Overview:** `cmd+o` or a four-finger swipe up shows live workspace previews. Select a window with the keyboard, touchpad or mouse; see [Overview](docs/config.md#overview). | ||
| 22 | - **Stash:** `cmd+s` toggles a floating strip of windows set aside for the current workspace; `cmd+shift+s` moves a window into or out of it. See [Stash](docs/config.md#stash). | 22 | - **Stash:** `cmd+s` toggles a floating strip of windows set aside for the current workspace; `cmd+shift+s` moves a window into or out of it. See [Stash](docs/config.md#stash). | ||
| @@ -25,7 +25,7 @@ _Demo video coming soon._ | |||||
| 25 | 25 | ||||
| 26 | Go fits a compositor's independent event streams: each piece of state has one owner goroutine, connected to others by channels.<br> | 26 | Go fits a compositor's independent event streams: each piece of state has one owner goroutine, connected to others by channels.<br> | ||
| 27 | Hot paths reuse memory, client buffers stay off the Go heap, and runtime library calls use purego rather than cgo.<br> | 27 | Hot paths reuse memory, client buffers stay off the Go heap, and runtime library calls use purego rather than cgo.<br> | ||
| 28 | - | See [Architecture](docs/architecture.md) for the concurrency model, GC strategy and reusable Go libraries. | 28 | + | See [Architecture](docs/dev/architecture.md) for the concurrency model, GC strategy and reusable Go libraries. |
| 29 | 29 | ||||
| 30 | Small on purpose, and staying small: no blur, shadows, rounded corners or themes. No built-in bar or wallpaper either: bring your own, any layer-shell client works. | 30 | Small on purpose, and staying small: no blur, shadows, rounded corners or themes. No built-in bar or wallpaper either: bring your own, any layer-shell client works. | ||
| 31 | 31 | ||||
| @@ -179,13 +179,14 @@ Reports from NVIDIA and Intel users are welcome. | |||||
| 179 | ## Documentation | 179 | ## Documentation | ||
| 180 | 180 | ||||
| 181 | - [Documentation index](docs/index.md): start here. | 181 | - [Documentation index](docs/index.md): start here. | ||
| 182 | - | - [Architecture](docs/architecture.md): concurrency, GC strategy and purego libraries. | 182 | + | - [Architecture](docs/dev/architecture.md): concurrency, GC strategy and purego libraries. |
| 183 | - [Configuration](docs/config.md): keys, actions, bindings and HDR. | 183 | - [Configuration](docs/config.md): keys, actions, bindings and HDR. | ||
| 184 | - [Desktop integration](docs/desktop.md): sessions, clipboard, input methods, X11 apps and scripting. | 184 | - [Desktop integration](docs/desktop.md): sessions, clipboard, input methods, X11 apps and scripting. | ||
| 185 | - | - [Session locking](docs/session-lock.md): external lockers, recovery and security limits. | 185 | + | - [Session locking](docs/desktop.md#session-locking): external lockers, recovery and security limits. |
| 186 | - [Screen capture](docs/capture.md): protocols, indicator, executable allowlist and portals. | 186 | - [Screen capture](docs/capture.md): protocols, indicator, executable allowlist and portals. | ||
| 187 | - | - [Headless mode](docs/headless.md): screenshots, input scripts and input-method testing without a screen. | 187 | + | - [Headless mode](docs/dev/headless.md): screenshots, input scripts and input-method testing without a screen. |
| 188 | - | - [Performance](docs/performance.md): VRR, real-time priority, profiling and frame stats. | 188 | + | - [Performance](docs/performance.md): VRR and real-time priority. |
| 189 | + | - [Developer documentation](docs/dev/index.md): architecture, headless testing, profiling and frame stats. | |||
| 189 | 190 | ||||
| 190 | ## Contributing | 191 | ## Contributing | ||
| 191 | 192 | ||||
M docs/desktop.md +24 -1
| @@ -60,7 +60,30 @@ xwayland = off # no X11 support | |||
| 60 | 60 | ||
| 61 | 61 | ## Session locking | |
| 62 | 62 | ||
| 63 | - | Use an external locker such as swaylock or hyprlock. See [Session locking](session-lock.md). | |
| 63 | + | NeferWL supports `ext-session-lock-v1` with an external locker such as swaylock or hyprlock. The locker checks the password; NeferWL hides the desktop and blocks input. | |
| 64 | + | ||
| 65 | + | ### While locked | |
| 66 | + | ||
| 67 | + | - Only the locker gets input. Binds, clipboard requests and new captures are refused. | |
| 68 | + | - Outputs show black and the locker's surfaces only. | |
| 69 | + | - Running captures stop. | |
| 70 | + | - `Ctrl+Alt+Backspace` is disabled. VT switching still works. | |
| 71 | + | - A locker that uses a screenshot as background (hyprlock `screenshot`, swaylock-effects `--screenshots`) must be in the [capture allowlist](capture.md#the-allowlist). | |
| 72 | + | ||
| 73 | + | The locker is told `locked` only once every output shows a protected frame or is off. | |
| 74 | + | ||
| 75 | + | ### If the locker dies | |
| 76 | + | ||
| 77 | + | The session stays locked and black. Start a new locker to take over. There is no emergency unlock key and no timeout. | |
| 78 | + | ||
| 79 | + | A locker that exits right after unlocking must do a Wayland roundtrip first, or its exit counts as a crash and the session stays locked. | |
| 80 | + | ||
| 81 | + | ### Limits | |
| 82 | + | ||
| 83 | + | - NeferWL does not authenticate: no PAM, no stored credentials. | |
| 84 | + | - Any process running as your user can start its own locker and unlock. | |
| 85 | + | - Clipboard data and window contents a client received before the lock stay with that client. | |
| 86 | + | - The [state file](desktop.md#state-for-scripts) is not cleared while locked. | |
| 64 | 87 | ||
| 65 | 88 | ## Screen capture | |
| 66 | 89 | ||
| @@ -60,7 +60,30 @@ xwayland = off # no X11 support | |||||
| 60 | 60 | ||||
| 61 | ## Session locking | 61 | ## Session locking | ||
| 62 | 62 | ||||
| 63 | - | Use an external locker such as swaylock or hyprlock. See [Session locking](session-lock.md). | 63 | + | NeferWL supports `ext-session-lock-v1` with an external locker such as swaylock or hyprlock. The locker checks the password; NeferWL hides the desktop and blocks input. |
| 64 | + | ||||
| 65 | + | ### While locked | |||
| 66 | + | ||||
| 67 | + | - Only the locker gets input. Binds, clipboard requests and new captures are refused. | |||
| 68 | + | - Outputs show black and the locker's surfaces only. | |||
| 69 | + | - Running captures stop. | |||
| 70 | + | - `Ctrl+Alt+Backspace` is disabled. VT switching still works. | |||
| 71 | + | - A locker that uses a screenshot as background (hyprlock `screenshot`, swaylock-effects `--screenshots`) must be in the [capture allowlist](capture.md#the-allowlist). | |||
| 72 | + | ||||
| 73 | + | The locker is told `locked` only once every output shows a protected frame or is off. | |||
| 74 | + | ||||
| 75 | + | ### If the locker dies | |||
| 76 | + | ||||
| 77 | + | The session stays locked and black. Start a new locker to take over. There is no emergency unlock key and no timeout. | |||
| 78 | + | ||||
| 79 | + | A locker that exits right after unlocking must do a Wayland roundtrip first, or its exit counts as a crash and the session stays locked. | |||
| 80 | + | ||||
| 81 | + | ### Limits | |||
| 82 | + | ||||
| 83 | + | - NeferWL does not authenticate: no PAM, no stored credentials. | |||
| 84 | + | - Any process running as your user can start its own locker and unlock. | |||
| 85 | + | - Clipboard data and window contents a client received before the lock stay with that client. | |||
| 86 | + | - The [state file](desktop.md#state-for-scripts) is not cleared while locked. | |||
| 64 | 87 | ||||
| 65 | ## Screen capture | 88 | ## Screen capture | ||
| 66 | 89 | ||||
R docs/architecture.md → docs/dev/architecture.md +1 -1 renamed, 98% similar
| @@ -19,7 +19,7 @@ About 35 channels connect them, all created in one place, so the full data flow | |||
| 19 | 19 | ### Keeping the garbage collector out of the way | |
| 20 | 20 | ||
| 21 | 21 | - **The hot path does not allocate.** Frame callbacks, presentation reports, DRM flips, Vulkan submissions and surface updates reuse memory owned by the goroutine that handles them. `make check` runs allocation guards that fail the build when one of these paths starts allocating. | |
| 22 | - | - **The GC has almost nothing to collect.** Client buffers live in shared and GPU memory, not on the Go heap, so the live heap is a few megabytes. Playing a 4K HDR video with 65 tiled subsurfaces committing every frame, GC takes under 9 % of a process that itself takes 13 % of a core. See [Performance](performance.md). | |
| 22 | + | - **The GC has almost nothing to collect.** Client buffers live in shared and GPU memory, not on the Go heap, so the live heap is a few megabytes. Playing a 4K HDR video with 65 tiled subsurfaces committing every frame, GC takes under 9 % of a process that itself takes 13 % of a core. See [Performance](../performance.md). | |
| 23 | 23 | - **No cgo.** NeferWL builds with `CGO_ENABLED=0`. libwayland, Vulkan and libinput are loaded at runtime through [purego](https://github.com/ebitengine/purego) with fixed-arity calls: no reflection, no allocation at the boundary. | |
| 24 | 24 | - **Work only when something changes.** Damage tracking redraws only what changed, surfaces hidden behind opaque ones are skipped, and an idle output does nothing. | |
| 25 | 25 | - **Tuned on real hardware.** Under VRR, cursor moves ride on the game's next frame instead of forcing their own refresh, and game frames keep a short gap after each flip so the panel does not fall back to its slowest rate. | |
| @@ -19,7 +19,7 @@ About 35 channels connect them, all created in one place, so the full data flow | |||||
| 19 | ### Keeping the garbage collector out of the way | 19 | ### Keeping the garbage collector out of the way | ||
| 20 | 20 | ||||
| 21 | - **The hot path does not allocate.** Frame callbacks, presentation reports, DRM flips, Vulkan submissions and surface updates reuse memory owned by the goroutine that handles them. `make check` runs allocation guards that fail the build when one of these paths starts allocating. | 21 | - **The hot path does not allocate.** Frame callbacks, presentation reports, DRM flips, Vulkan submissions and surface updates reuse memory owned by the goroutine that handles them. `make check` runs allocation guards that fail the build when one of these paths starts allocating. | ||
| 22 | - | - **The GC has almost nothing to collect.** Client buffers live in shared and GPU memory, not on the Go heap, so the live heap is a few megabytes. Playing a 4K HDR video with 65 tiled subsurfaces committing every frame, GC takes under 9 % of a process that itself takes 13 % of a core. See [Performance](performance.md). | 22 | + | - **The GC has almost nothing to collect.** Client buffers live in shared and GPU memory, not on the Go heap, so the live heap is a few megabytes. Playing a 4K HDR video with 65 tiled subsurfaces committing every frame, GC takes under 9 % of a process that itself takes 13 % of a core. See [Performance](../performance.md). |
| 23 | - **No cgo.** NeferWL builds with `CGO_ENABLED=0`. libwayland, Vulkan and libinput are loaded at runtime through [purego](https://github.com/ebitengine/purego) with fixed-arity calls: no reflection, no allocation at the boundary. | 23 | - **No cgo.** NeferWL builds with `CGO_ENABLED=0`. libwayland, Vulkan and libinput are loaded at runtime through [purego](https://github.com/ebitengine/purego) with fixed-arity calls: no reflection, no allocation at the boundary. | ||
| 24 | - **Work only when something changes.** Damage tracking redraws only what changed, surfaces hidden behind opaque ones are skipped, and an idle output does nothing. | 24 | - **Work only when something changes.** Damage tracking redraws only what changed, surfaces hidden behind opaque ones are skipped, and an idle output does nothing. | ||
| 25 | - **Tuned on real hardware.** Under VRR, cursor moves ride on the game's next frame instead of forcing their own refresh, and game frames keep a short gap after each flip so the panel does not fall back to its slowest rate. | 25 | - **Tuned on real hardware.** Under VRR, cursor moves ride on the game's next frame instead of forcing their own refresh, and game frames keep a short gap after each flip so the panel does not fall back to its slowest rate. | ||
R docs/headless.md → docs/dev/headless.md +1 -1 renamed, 96% similar
| @@ -12,7 +12,7 @@ WAYLAND_DISPLAY=<name from the log> foot | |||
| 12 | 12 | for testing `focus-monitor-left/right` and moving columns between outputs. | |
| 13 | 13 | - With several outputs, each writes to `<dir>/HEADLESS-<n>/`. | |
| 14 | 14 | - `--timeout 5s` stops NeferWL after 5 seconds. | |
| 15 | - | - `grim` and recorders work too; see [Screen capture](capture.md). | |
| 15 | + | - `grim` and recorders work too; see [Screen capture](../capture.md). | |
| 16 | 16 | ||
| 17 | 17 | ## Input scripts | |
| 18 | 18 | ||
| @@ -12,7 +12,7 @@ WAYLAND_DISPLAY=<name from the log> foot | |||||
| 12 | for testing `focus-monitor-left/right` and moving columns between outputs. | 12 | for testing `focus-monitor-left/right` and moving columns between outputs. | ||
| 13 | - With several outputs, each writes to `<dir>/HEADLESS-<n>/`. | 13 | - With several outputs, each writes to `<dir>/HEADLESS-<n>/`. | ||
| 14 | - `--timeout 5s` stops NeferWL after 5 seconds. | 14 | - `--timeout 5s` stops NeferWL after 5 seconds. | ||
| 15 | - | - `grim` and recorders work too; see [Screen capture](capture.md). | 15 | + | - `grim` and recorders work too; see [Screen capture](../capture.md). |
| 16 | 16 | ||||
| 17 | ## Input scripts | 17 | ## Input scripts | ||
| 18 | 18 | ||||
A docs/dev/index.md +8 -0
| @@ -0,0 +1,8 @@ | |||
| 1 | + | # Developer documentation | |
| 2 | + | ||
| 3 | + | These guides cover NeferWL's internals and tools for development, testing and performance investigation. For configuration and desktop setup, see the [user documentation](../index.md). | |
| 4 | + | ||
| 5 | + | - [Architecture](architecture.md): owner goroutines, GC strategy and reusable purego libraries. | |
| 6 | + | - [Headless mode](headless.md): screenshots, scripted input and testing without a screen. | |
| 7 | + | - [Profiling](profiling.md): CPU and allocation profiling, logging and frame statistics. | |
| 8 | + | - [Repository rules](../../AGENTS.md): architecture constraints, test requirements and checks before merge. | |
| @@ -0,0 +1,8 @@ | |||||
| 1 | + | # Developer documentation | |||
| 2 | + | ||||
| 3 | + | These guides cover NeferWL's internals and tools for development, testing and performance investigation. For configuration and desktop setup, see the [user documentation](../index.md). | |||
| 4 | + | ||||
| 5 | + | - [Architecture](architecture.md): owner goroutines, GC strategy and reusable purego libraries. | |||
| 6 | + | - [Headless mode](headless.md): screenshots, scripted input and testing without a screen. | |||
| 7 | + | - [Profiling](profiling.md): CPU and allocation profiling, logging and frame statistics. | |||
| 8 | + | - [Repository rules](../../AGENTS.md): architecture constraints, test requirements and checks before merge. | |||
A docs/dev/profiling.md +36 -0
| @@ -0,0 +1,36 @@ | |||
| 1 | + | # Profiling | |
| 2 | + | ||
| 3 | + | Run these from another TTY or over SSH: | |
| 4 | + | ||
| 5 | + | ```sh | |
| 6 | + | # CPU, Go and C frames | |
| 7 | + | perf record -g -p (pgrep -f bin/neferwl) -- sleep 8 | |
| 8 | + | perf report --children --percent-limit 1 | |
| 9 | + | ||
| 10 | + | # Allocations | |
| 11 | + | neferwl --backend=drm --pprof=localhost:6060 | |
| 12 | + | go tool pprof -sample_index=alloc_objects -top http://localhost:6060/debug/pprof/heap | |
| 13 | + | ||
| 14 | + | # One line per GC cycle | |
| 15 | + | GODEBUG=gctrace=1 neferwl --backend=drm | |
| 16 | + | ``` | |
| 17 | + | ||
| 18 | + | `make tty` serves pprof on `localhost:6060` by default (`make tty PPROF=` turns it off). | |
| 19 | + | ||
| 20 | + | Logs are in `~/.local/state/neferwl/runs/<backend>/`, with `latest.log` pointing at the newest run. `--debug=all` enables every category except the noisy `drm-flip`, `input-motion`, `input-keys`, `wayland-damage` (one `client damage` entry per client commit) and `render-damage` (one `frame damage` entry per rendered frame); name them to turn them on. | |
| 21 | + | ||
| 22 | + | ## Frame stats | |
| 23 | + | ||
| 24 | + | Every 10 s, each output logs a `stats` entry: | |
| 25 | + | ||
| 26 | + | | Field | Meaning | | |
| 27 | + | |---|---| | |
| 28 | + | | `missed_vblanks` | Frames that flipped more than 1.5 refresh periods late (not counted under VRR) | | |
| 29 | + | | `max_flip_interval_ms` | Longest gap between two flips | | |
| 30 | + | | `max_commit_delay_ms` | Longest wait from "frame due" to commit | | |
| 31 | + | | `late_fences` | Missed vblanks where the GPU finished too late (needs `--debug=drm-flip`) | | |
| 32 | + | | `redrawn_pixels` | Pixels drawn | | |
| 33 | + | ||
| 34 | + | If outputs report flips faster than the display loop reads them, wayland merges them and logs a warning with `merged` at most every 10 s. | |
| 35 | + | ||
| 36 | + | Missed vblanks with `late_fences` mean the GPU was late. Without `late_fences` but with a large `max_commit_delay_ms`, the CPU was late. | |
| @@ -0,0 +1,36 @@ | |||||
| 1 | + | # Profiling | |||
| 2 | + | ||||
| 3 | + | Run these from another TTY or over SSH: | |||
| 4 | + | ||||
| 5 | + | ```sh | |||
| 6 | + | # CPU, Go and C frames | |||
| 7 | + | perf record -g -p (pgrep -f bin/neferwl) -- sleep 8 | |||
| 8 | + | perf report --children --percent-limit 1 | |||
| 9 | + | ||||
| 10 | + | # Allocations | |||
| 11 | + | neferwl --backend=drm --pprof=localhost:6060 | |||
| 12 | + | go tool pprof -sample_index=alloc_objects -top http://localhost:6060/debug/pprof/heap | |||
| 13 | + | ||||
| 14 | + | # One line per GC cycle | |||
| 15 | + | GODEBUG=gctrace=1 neferwl --backend=drm | |||
| 16 | + | ``` | |||
| 17 | + | ||||
| 18 | + | `make tty` serves pprof on `localhost:6060` by default (`make tty PPROF=` turns it off). | |||
| 19 | + | ||||
| 20 | + | Logs are in `~/.local/state/neferwl/runs/<backend>/`, with `latest.log` pointing at the newest run. `--debug=all` enables every category except the noisy `drm-flip`, `input-motion`, `input-keys`, `wayland-damage` (one `client damage` entry per client commit) and `render-damage` (one `frame damage` entry per rendered frame); name them to turn them on. | |||
| 21 | + | ||||
| 22 | + | ## Frame stats | |||
| 23 | + | ||||
| 24 | + | Every 10 s, each output logs a `stats` entry: | |||
| 25 | + | ||||
| 26 | + | | Field | Meaning | | |||
| 27 | + | |---|---| | |||
| 28 | + | | `missed_vblanks` | Frames that flipped more than 1.5 refresh periods late (not counted under VRR) | | |||
| 29 | + | | `max_flip_interval_ms` | Longest gap between two flips | | |||
| 30 | + | | `max_commit_delay_ms` | Longest wait from "frame due" to commit | | |||
| 31 | + | | `late_fences` | Missed vblanks where the GPU finished too late (needs `--debug=drm-flip`) | | |||
| 32 | + | | `redrawn_pixels` | Pixels drawn | | |||
| 33 | + | ||||
| 34 | + | If outputs report flips faster than the display loop reads them, wayland merges them and logs a warning with `merged` at most every 10 s. | |||
| 35 | + | ||||
| 36 | + | Missed vblanks with `late_fences` mean the GPU was late. Without `late_fences` but with a large `max_commit_delay_ms`, the CPU was late. | |||
M docs/index.md +5 -6
| @@ -1,11 +1,10 @@ | |||
| 1 | 1 | # NeferWL documentation | |
| 2 | 2 | ||
| 3 | - | NeferWL is a small Wayland compositor in Go. Start with Configuration to set up your keyboard, outputs and workflows, then use the guides below for desktop integration, architecture and troubleshooting. | |
| 3 | + | NeferWL is a small Wayland compositor in Go. Start with Configuration to set up your keyboard, outputs and workflows, then use the guides below for desktop integration, screen capture and performance. | |
| 4 | 4 | ||
| 5 | 5 | - [Configuration](config.md): keys, actions, default bindings, layouts and HDR. | |
| 6 | - | - [Architecture](architecture.md): owner goroutines, GC strategy and reusable purego libraries. | |
| 7 | - | - [Desktop integration](desktop.md): sessions, clipboard, input methods, X11 apps and scripting. | |
| 8 | - | - [Session locking](session-lock.md): external lockers, recovery and security limits. | |
| 6 | + | - [Desktop integration](desktop.md): sessions, clipboard, input methods, X11 apps, session locking and scripting. | |
| 9 | 7 | - [Screen capture](capture.md): protocols, indicator, executable allowlist and portals. | |
| 10 | - | - [Headless mode](headless.md): screenshots, scripted input and testing without a screen. | |
| 11 | - | - [Performance](performance.md): VRR, real-time priority, profiling and frame stats. | |
| 8 | + | - [Performance](performance.md): resource use, VRR and real-time priority. | |
| 9 | + | ||
| 10 | + | Developer documentation: [docs/dev](dev/index.md) | |
| @@ -1,11 +1,10 @@ | |||||
| 1 | # NeferWL documentation | 1 | # NeferWL documentation | ||
| 2 | 2 | ||||
| 3 | - | NeferWL is a small Wayland compositor in Go. Start with Configuration to set up your keyboard, outputs and workflows, then use the guides below for desktop integration, architecture and troubleshooting. | 3 | + | NeferWL is a small Wayland compositor in Go. Start with Configuration to set up your keyboard, outputs and workflows, then use the guides below for desktop integration, screen capture and performance. |
| 4 | 4 | ||||
| 5 | - [Configuration](config.md): keys, actions, default bindings, layouts and HDR. | 5 | - [Configuration](config.md): keys, actions, default bindings, layouts and HDR. | ||
| 6 | - | - [Architecture](architecture.md): owner goroutines, GC strategy and reusable purego libraries. | 6 | + | - [Desktop integration](desktop.md): sessions, clipboard, input methods, X11 apps, session locking and scripting. |
| 7 | - | - [Desktop integration](desktop.md): sessions, clipboard, input methods, X11 apps and scripting. | |||
| 8 | - | - [Session locking](session-lock.md): external lockers, recovery and security limits. | |||
| 9 | - [Screen capture](capture.md): protocols, indicator, executable allowlist and portals. | 7 | - [Screen capture](capture.md): protocols, indicator, executable allowlist and portals. | ||
| 10 | - | - [Headless mode](headless.md): screenshots, scripted input and testing without a screen. | 8 | + | - [Performance](performance.md): resource use, VRR and real-time priority. |
| 11 | - | - [Performance](performance.md): VRR, real-time priority, profiling and frame stats. | 9 | + | |
| 10 | + | Developer documentation: [docs/dev](dev/index.md) | |||
M docs/performance.md +1 -36
| @@ -22,39 +22,4 @@ sudo setcap cap_sys_nice+ep bin/neferwl | |||
| 22 | 22 | ||
| 23 | 23 | `performance.realtime = off` turns it off. With the capability set, the loader ignores `LD_PRELOAD` and `LD_LIBRARY_PATH`, and debuggers need root to attach. | |
| 24 | 24 | ||
| 25 | - | ## Profiling | |
| 26 | - | ||
| 27 | - | Run these from another TTY or over SSH: | |
| 28 | - | ||
| 29 | - | ```sh | |
| 30 | - | # CPU, Go and C frames | |
| 31 | - | perf record -g -p (pgrep -f bin/neferwl) -- sleep 8 | |
| 32 | - | perf report --children --percent-limit 1 | |
| 33 | - | ||
| 34 | - | # Allocations | |
| 35 | - | neferwl --backend=drm --pprof=localhost:6060 | |
| 36 | - | go tool pprof -sample_index=alloc_objects -top http://localhost:6060/debug/pprof/heap | |
| 37 | - | ||
| 38 | - | # One line per GC cycle | |
| 39 | - | GODEBUG=gctrace=1 neferwl --backend=drm | |
| 40 | - | ``` | |
| 41 | - | ||
| 42 | - | `make tty` serves pprof on `localhost:6060` by default (`make tty PPROF=` turns it off). | |
| 43 | - | ||
| 44 | - | Logs are in `~/.local/state/neferwl/runs/<backend>/`, with `latest.log` pointing at the newest run. `--debug=all` enables every category except the noisy `drm-flip`, `input-motion`, `input-keys`, `wayland-damage` (one `client damage` entry per client commit) and `render-damage` (one `frame damage` entry per rendered frame); name them to turn them on. | |
| 45 | - | ||
| 46 | - | ## Frame stats | |
| 47 | - | ||
| 48 | - | Every 10 s, each output logs a `stats` entry: | |
| 49 | - | ||
| 50 | - | | Field | Meaning | | |
| 51 | - | |---|---| | |
| 52 | - | | `missed_vblanks` | Frames that flipped more than 1.5 refresh periods late (not counted under VRR) | | |
| 53 | - | | `max_flip_interval_ms` | Longest gap between two flips | | |
| 54 | - | | `max_commit_delay_ms` | Longest wait from "frame due" to commit | | |
| 55 | - | | `late_fences` | Missed vblanks where the GPU finished too late (needs `--debug=drm-flip`) | | |
| 56 | - | | `redrawn_pixels` | Pixels drawn | | |
| 57 | - | ||
| 58 | - | If outputs report flips faster than the display loop reads them, wayland merges them and logs a warning with `merged` at most every 10 s. | |
| 59 | - | ||
| 60 | - | Missed vblanks with `late_fences` mean the GPU was late. Without `late_fences` but with a large `max_commit_delay_ms`, the CPU was late. | |
| 25 | + | For profiling tools and frame statistics, see [Profiling](dev/profiling.md). | |
| @@ -22,39 +22,4 @@ sudo setcap cap_sys_nice+ep bin/neferwl | |||||
| 22 | 22 | ||||
| 23 | `performance.realtime = off` turns it off. With the capability set, the loader ignores `LD_PRELOAD` and `LD_LIBRARY_PATH`, and debuggers need root to attach. | 23 | `performance.realtime = off` turns it off. With the capability set, the loader ignores `LD_PRELOAD` and `LD_LIBRARY_PATH`, and debuggers need root to attach. | ||
| 24 | 24 | ||||
| 25 | - | ## Profiling | 25 | + | For profiling tools and frame statistics, see [Profiling](dev/profiling.md). |
| 26 | - | ||||
| 27 | - | Run these from another TTY or over SSH: | |||
| 28 | - | ||||
| 29 | - | ```sh | |||
| 30 | - | # CPU, Go and C frames | |||
| 31 | - | perf record -g -p (pgrep -f bin/neferwl) -- sleep 8 | |||
| 32 | - | perf report --children --percent-limit 1 | |||
| 33 | - | ||||
| 34 | - | # Allocations | |||
| 35 | - | neferwl --backend=drm --pprof=localhost:6060 | |||
| 36 | - | go tool pprof -sample_index=alloc_objects -top http://localhost:6060/debug/pprof/heap | |||
| 37 | - | ||||
| 38 | - | # One line per GC cycle | |||
| 39 | - | GODEBUG=gctrace=1 neferwl --backend=drm | |||
| 40 | - | ``` | |||
| 41 | - | ||||
| 42 | - | `make tty` serves pprof on `localhost:6060` by default (`make tty PPROF=` turns it off). | |||
| 43 | - | ||||
| 44 | - | Logs are in `~/.local/state/neferwl/runs/<backend>/`, with `latest.log` pointing at the newest run. `--debug=all` enables every category except the noisy `drm-flip`, `input-motion`, `input-keys`, `wayland-damage` (one `client damage` entry per client commit) and `render-damage` (one `frame damage` entry per rendered frame); name them to turn them on. | |||
| 45 | - | ||||
| 46 | - | ## Frame stats | |||
| 47 | - | ||||
| 48 | - | Every 10 s, each output logs a `stats` entry: | |||
| 49 | - | ||||
| 50 | - | | Field | Meaning | | |||
| 51 | - | |---|---| | |||
| 52 | - | | `missed_vblanks` | Frames that flipped more than 1.5 refresh periods late (not counted under VRR) | | |||
| 53 | - | | `max_flip_interval_ms` | Longest gap between two flips | | |||
| 54 | - | | `max_commit_delay_ms` | Longest wait from "frame due" to commit | | |||
| 55 | - | | `late_fences` | Missed vblanks where the GPU finished too late (needs `--debug=drm-flip`) | | |||
| 56 | - | | `redrawn_pixels` | Pixels drawn | | |||
| 57 | - | ||||
| 58 | - | If outputs report flips faster than the display loop reads them, wayland merges them and logs a warning with `merged` at most every 10 s. | |||
| 59 | - | ||||
| 60 | - | Missed vblanks with `late_fences` mean the GPU was late. Without `late_fences` but with a large `max_commit_delay_ms`, the CPU was late. | |||
D docs/session-lock.md +0 -26
| @@ -1,26 +0,0 @@ | |||
| 1 | - | # Session locking | |
| 2 | - | ||
| 3 | - | NeferWL supports `ext-session-lock-v1` with an external locker such as swaylock or hyprlock. The locker checks the password; NeferWL hides the desktop and blocks input. | |
| 4 | - | ||
| 5 | - | ## While locked | |
| 6 | - | ||
| 7 | - | - Only the locker gets input. Binds, clipboard requests and new captures are refused. | |
| 8 | - | - Outputs show black and the locker's surfaces only. | |
| 9 | - | - Running captures stop. | |
| 10 | - | - `Ctrl+Alt+Backspace` is disabled. VT switching still works. | |
| 11 | - | - A locker that uses a screenshot as background (hyprlock `screenshot`, swaylock-effects `--screenshots`) must be in the [capture allowlist](capture.md#the-allowlist). | |
| 12 | - | ||
| 13 | - | The locker is told `locked` only once every output shows a protected frame or is off. | |
| 14 | - | ||
| 15 | - | ## If the locker dies | |
| 16 | - | ||
| 17 | - | The session stays locked and black. Start a new locker to take over. There is no emergency unlock key and no timeout. | |
| 18 | - | ||
| 19 | - | A locker that exits right after unlocking must do a Wayland roundtrip first, or its exit counts as a crash and the session stays locked. | |
| 20 | - | ||
| 21 | - | ## Limits | |
| 22 | - | ||
| 23 | - | - NeferWL does not authenticate: no PAM, no stored credentials. | |
| 24 | - | - Any process running as your user can start its own locker and unlock. | |
| 25 | - | - Clipboard data and window contents a client received before the lock stay with that client. | |
| 26 | - | - The [state file](desktop.md#state-for-scripts) is not cleared while locked. | |
| @@ -1,26 +0,0 @@ | |||||
| 1 | - | # Session locking | |||
| 2 | - | ||||
| 3 | - | NeferWL supports `ext-session-lock-v1` with an external locker such as swaylock or hyprlock. The locker checks the password; NeferWL hides the desktop and blocks input. | |||
| 4 | - | ||||
| 5 | - | ## While locked | |||
| 6 | - | ||||
| 7 | - | - Only the locker gets input. Binds, clipboard requests and new captures are refused. | |||
| 8 | - | - Outputs show black and the locker's surfaces only. | |||
| 9 | - | - Running captures stop. | |||
| 10 | - | - `Ctrl+Alt+Backspace` is disabled. VT switching still works. | |||
| 11 | - | - A locker that uses a screenshot as background (hyprlock `screenshot`, swaylock-effects `--screenshots`) must be in the [capture allowlist](capture.md#the-allowlist). | |||
| 12 | - | ||||
| 13 | - | The locker is told `locked` only once every output shows a protected frame or is off. | |||
| 14 | - | ||||
| 15 | - | ## If the locker dies | |||
| 16 | - | ||||
| 17 | - | The session stays locked and black. Start a new locker to take over. There is no emergency unlock key and no timeout. | |||
| 18 | - | ||||
| 19 | - | A locker that exits right after unlocking must do a Wayland roundtrip first, or its exit counts as a crash and the session stays locked. | |||
| 20 | - | ||||
| 21 | - | ## Limits | |||
| 22 | - | ||||
| 23 | - | - NeferWL does not authenticate: no PAM, no stored credentials. | |||
| 24 | - | - Any process running as your user can start its own locker and unlock. | |||
| 25 | - | - Clipboard data and window contents a client received before the lock stay with that client. | |||
| 26 | - | - The [state file](desktop.md#state-for-scripts) is not cleared while locked. | |||