Merge pull request #2 from bnema/docs/readme-refresh
docs(readme): restructure around usage, config and commands
1 file · +79 -58
M README.md +79 -58
| @@ -1,34 +1,56 @@ | |||
| 1 | - | # nefercap | |
| 1 | + | <h1 align="center">nefercap</h1> | |
| 2 | 2 | ||
| 3 | - | Fast Wayland screenshots and silent video recording with a NeferGUI layer-shell selector. | |
| 3 | + | <p align="center"> | |
| 4 | + | <a href="LICENSE"><img src="https://img.shields.io/badge/license-GPLv3-blue?style=flat-square" alt="License: GPLv3"></a> | |
| 5 | + | <a href="https://github.com/bnema/nefercap"><img src="https://img.shields.io/badge/platform-Linux-blue?style=flat-square" alt="Platform: Linux"></a> | |
| 6 | + | <a href="https://github.com/bnema/nefercap/commits/main"><img src="https://badgen.net/github/last-commit/bnema/nefercap/main?icon=github" alt="Last commit"></a> | |
| 7 | + | <a href="https://github.com/bnema/nefercap/stargazers"><img src="https://badgen.net/github/stars/bnema/nefercap?icon=github" alt="GitHub stars"></a> | |
| 8 | + | </p> | |
| 4 | 9 | ||
| 5 | - | ## Quick capture | |
| 10 | + | <p align="center">Screenshots and silent screen recording for Wayland, from one shortcut.</p> | |
| 6 | 11 | ||
| 7 | - | ```sh | |
| 8 | - | make bin | |
| 9 | - | ./bin/nefercap | |
| 10 | - | ./bin/nefercap shot | |
| 11 | - | ./bin/nefercap shot -clipboard | |
| 12 | - | ./bin/nefercap shot -clipboard -file capture.png | |
| 13 | - | ./bin/nefercap rec | |
| 14 | - | ./bin/nefercap stop | |
| 15 | - | ./bin/nefercap status | |
| 12 | + | > [!WARNING] | |
| 13 | + | > **Early alpha.** Built for [NeferWL](https://github.com/bnema/neferwl). Other compositors can take screenshots but cannot record. Expect bugs and breaking config changes. | |
| 14 | + | ||
| 15 | + | --- | |
| 16 | + | ||
| 17 | + | ## Usage | |
| 18 | + | ||
| 19 | + | Bind `nefercap` to a key in your compositor. For NeferWL: | |
| 20 | + | ||
| 21 | + | ```text | |
| 22 | + | bind.cmd+p = spawn nefercap | |
| 16 | 23 | ``` | |
| 17 | 24 | ||
| 18 | - | `nefercap` without a command is all-in-one: it stops an active recording, otherwise opens the selector in shot mode. **Tab** switches shot ⇄ rec and keeps the chosen region, monitor or workspace; Enter or a click confirms in the current mode. It takes no flags except `-debug` and reads its settings from the config file. `shot` and `rec` keep a fixed mode (no Tab) and accept `-file`/`-clipboard` (shot) to override the config per run. A neutral badge at the top left of every overlay always shows the mode: `SHOT` or `REC`. | |
| 25 | + | Press the key: a selector covers every monitor. Pick what to capture, press **Tab** to switch between screenshot and recording, then confirm. A badge at the top left shows the mode: `SHOT` or `REC`. | |
| 19 | 26 | ||
| 20 | - | The selector covers the connected outputs, up to nine: | |
| 27 | + | While recording, a red border marks the target and a small **REC / Stop** HUD shows the time. Neither appears in the video. Press the key again, or click Stop, to finish the file. | |
| 21 | 28 | ||
| 22 | - | - **Drag and release:** capture a rectangle, clamped to its monitor. | |
| 23 | - | - **Click:** capture the monitor; small pointer jitter counts as a click. | |
| 24 | - | - **M, then Enter:** select the monitor. | |
| 25 | - | - **W, then Enter:** select the current workspace when native workspace metadata is available. | |
| 26 | - | - **R:** return to rectangle selection. **G:** toggle full-monitor center and thirds guides. | |
| 27 | - | - **1–9:** select that monitor. **Esc:** cancel without creating a capture. | |
| 29 | + | | Input | Action | | |
| 30 | + | | --- | --- | | |
| 31 | + | | Drag | Capture a rectangle | | |
| 32 | + | | Click | Capture the monitor | | |
| 33 | + | | `W`, then Enter | Capture the current workspace | | |
| 34 | + | | `M`, then Enter | Capture the monitor | | |
| 35 | + | | `1`–`9` | Pick that monitor | | |
| 36 | + | | `R` | Back to rectangle selection | | |
| 37 | + | | `G` | Show or hide the thirds guides | | |
| 38 | + | | `Tab` | Switch shot ⇄ rec | | |
| 39 | + | | `Esc` | Cancel | | |
| 28 | 40 | ||
| 29 | - | The selector shows mode keys and the current confirmation action in a two-line legend. Workspace hints appear only when a current workspace is available. Text uses a 1rem base (16 logical pixels), with proportional spacing and controls; the output's fractional scaling determines physical size. | |
| 41 | + | A workspace recording follows that workspace, even when you switch to another one. A monitor recording follows whatever the monitor shows. | |
| 30 | 42 | ||
| 31 | - | The guides are visible by default: three vertical and three horizontal medium-gray lines, one physical pixel wide, aligned to each monitor's scaling. They stay fixed while dragging. Settings live in `$XDG_CONFIG_HOME/nefercap/config` (default `~/.config/nefercap/config`): | |
| 43 | + | ## Features | |
| 44 | + | ||
| 45 | + | - **One shortcut.** Screenshot and recording share one selector; the same key stops a recording. | |
| 46 | + | - **Clean video.** The compositor keeps the border and HUD out of the recording. | |
| 47 | + | - **Clipboard.** Screenshots can be saved, copied, or both. | |
| 48 | + | - **Small footprint.** One capture in flight, reused buffers, no redraw while idle. Allocation tests guard the hot paths. | |
| 49 | + | - **Scriptable.** `nefercap screenshot` and `nefercap record` run without the selector. | |
| 50 | + | ||
| 51 | + | ## Configuration | |
| 52 | + | ||
| 53 | + | `~/.config/nefercap/config` (or `$XDG_CONFIG_HOME/nefercap/config`): | |
| 32 | 54 | ||
| 33 | 55 | ```ini | |
| 34 | 56 | selector.grid = on | |
| @@ -37,68 +59,67 @@ screenshot.output = file+clipboard | |||
| 37 | 59 | video.dir = ~/Videos | |
| 38 | 60 | ``` | |
| 39 | 61 | ||
| 40 | - | - `selector.grid`: `on` (default) or `off` to hide the guides on startup. | |
| 41 | - | - `screenshot.dir`, `video.dir`: an absolute path or `~/…`; relative paths are errors. Absent, the XDG defaults below apply. Before the selector opens, nefercap creates (private) and checks each directory the command can use: both for all-in-one, one for `shot` or `rec`. | |
| 42 | - | - `screenshot.output`: `file` (default), `file+clipboard` (save, then copy the same PNG) or `clipboard` (copy only; no file, no path printed). | |
| 62 | + | | Key | Values | Default | | |
| 63 | + | | --- | --- | --- | | |
| 64 | + | | `selector.grid` | `on`, `off` | `on` | | |
| 65 | + | | `screenshot.dir` | absolute or `~/` path | `$XDG_PICTURES_DIR/Screenshots` | | |
| 66 | + | | `screenshot.output` | `file`, `file+clipboard`, `clipboard` | `file` | | |
| 67 | + | | `video.dir` | absolute or `~/` path | `$XDG_VIDEOS_DIR` | | |
| 43 | 68 | ||
| 44 | - | Each key may appear once; blank lines and `#` comment lines are allowed; unknown keys, duplicates and invalid values are errors reported before the selector opens. `screenshot.output` and the directories apply to all-in-one, `shot` and `rec`; the scripted `screenshot` and `record` never read them. On `shot`, `-clipboard` alone copies only, `-clipboard -file PATH` saves and copies, and `-file PATH` alone saves only, whatever `screenshot.output` says. **G** changes visibility for the current monitor's overlay without rewriting the config. Selection dimensions are logical pixels; the captured file's dimensions depend on the monitor scaling. | |
| 69 | + | Unknown keys, duplicates and invalid values are reported before the selector opens. Files are private, named with a timestamp, never overwritten, and their path is printed on stdout. | |
| 45 | 70 | ||
| 46 | - | A screenshot saves immediately. Recording displays a red border around the visible target and a small **REC / Stop** HUD. Both are excluded from nefercap's video. The HUD takes no keyboard focus; clicks outside Stop pass through. Press the recording shortcut again, click Stop, or run `nefercap stop` to finish the file. | |
| 71 | + | ## Commands | |
| 47 | 72 | ||
| 48 | - | Monitor recording follows workspace changes. A workspace recording follows its stable identity and continues when another workspace is displayed. A fixed region stays attached to its monitor. A hidden workspace has no border on the unrelated visible workspace; the recording HUD remains available. | |
| 73 | + | ```sh | |
| 74 | + | nefercap # all-in-one: stop the recording, else open the selector | |
| 75 | + | nefercap shot # selector, screenshot only | |
| 76 | + | nefercap rec # selector, recording only | |
| 77 | + | nefercap stop # stop the active recording | |
| 78 | + | nefercap status # print the recording state | |
| 79 | + | ``` | |
| 49 | 80 | ||
| 50 | - | Without `screenshot.dir`/`video.dir`, screenshots go to the configured XDG Pictures directory's `Screenshots` subdirectory, and videos to the XDG Videos directory. Names include a nanosecond timestamp. `-file` chooses an explicit destination. Files are private to their owner and are never overwritten. The saved path is printed to stdout, including for scripted commands. Cancelling before any frame is written creates no file and prints no path. | |
| 81 | + | Scripted commands never open the selector: | |
| 51 | 82 | ||
| 52 | - | `shot -clipboard` copies an `image/png` screenshot using `wl-copy` without creating a capture file or printing a path. Add `-file PATH` to save and copy the same PNG. A successfully saved file is retained and its path printed even if clipboard copying later fails. The clipboard owner intentionally survives nefercap's exit. This CLI-only option also works with scripted `screenshot`; other commands reject it. Install `wl-clipboard` to use it. | |
| 83 | + | ```sh | |
| 84 | + | nefercap outputs | |
| 85 | + | nefercap screenshot -output DP-1 -file shot.png | |
| 86 | + | nefercap screenshot -output DP-1 -region 0,0,1920x1080 -clipboard | |
| 87 | + | nefercap record -output DP-1 -fps 30 -duration 10s -file clip.mp4 | |
| 88 | + | ``` | |
| 53 | 89 | ||
| 54 | - | `shot` while a recording is active is refused: its overlay is not part of the existing session's authorized controls. A scripted `screenshot` still uses standard capture and includes the active HUD and border. | |
| 90 | + | `nefercap -h` lists every flag. Regions use output-local logical coordinates. `-size WxH` sets an even output resolution for odd-sized sources. | |
| 55 | 91 | ||
| 56 | 92 | ## Requirements | |
| 57 | 93 | ||
| 58 | - | - Go 1.27 for development; normal builds use `CGO_ENABLED=0`. | |
| 59 | - | - For selection: layer-shell v4, Vulkan, libxkbcommon, linux-dmabuf and linux-drm-syncobj. | |
| 60 | - | - For capture: `zwlr_screencopy_manager_v1`. | |
| 61 | - | - For recording indicators and workspace targets: NeferWL's native capture-session protocol. | |
| 62 | - | - FFmpeg with `libx264` for silent H.264 video in fragmented MP4. | |
| 94 | + | - Go 1.27 to build. | |
| 95 | + | - A Wayland compositor with `zwlr_layer_shell_v1` v4 and `zwlr_screencopy_manager_v1`. | |
| 96 | + | - Vulkan, libxkbcommon, linux-dmabuf and linux-drm-syncobj for the selector. | |
| 97 | + | - NeferWL's capture-session protocol to record. | |
| 98 | + | - FFmpeg with `libx264` to record. | |
| 63 | 99 | - `wl-copy` (wl-clipboard) for clipboard screenshots. | |
| 64 | 100 | ||
| 65 | - | A standard compositor can support monitor/region screenshots. Recording refuses to start without native session support rather than capture its own controls. Standard capture clients keep their normal behavior and see the HUD and border. | |
| 101 | + | Output is SDR, and video is silent H.264 in fragmented MP4. Audio, pause and cursor capture are not supported. | |
| 66 | 102 | ||
| 67 | - | ## Scripted commands | |
| 103 | + | ## Install | |
| 68 | 104 | ||
| 69 | 105 | ```sh | |
| 70 | - | ./bin/nefercap outputs | |
| 71 | - | ./bin/nefercap screenshot -output DP-1 -file capture.png | |
| 72 | - | ./bin/nefercap screenshot -output DP-1 -region 0,0,1920x1080 -file region.png | |
| 73 | - | ./bin/nefercap record -output DP-1 -fps 30 -duration 10s -file capture.mp4 | |
| 74 | - | ./bin/nefercap rec -size 1920x1080 -file presentation.mp4 | |
| 106 | + | make bin # builds bin/nefercap | |
| 75 | 107 | ``` | |
| 76 | 108 | ||
| 77 | - | Use an output name from `outputs`. Scripted `screenshot` requires `-file` unless `-clipboard` is used; `record` always requires `-file`; omitting `-output` is allowed with exactly one output. Regions use output-local logical coordinates, not desktop-global coordinates. Capture dimensions are physical pixels negotiated with the compositor. | |
| 78 | - | ||
| 79 | - | `-fps` accepts 1–120, default 30. `-size` requires two even dimensions and changes encoded resolution, not application scaling. Odd source dimensions require an explicit even size; pixels are not silently cropped. `-debug` enables diagnostic logging. | |
| 80 | - | ||
| 81 | - | Untimed recording stops with the HUD, the control command, Ctrl+C or SIGTERM and finalizes the file. The `stop` command acknowledges the request; finalization finishes in the recorder process, not before the control reply. Timed recording encodes `ceil(duration × fps)` frames. Slow capture can repeat frames; encoder backpressure can extend wall time without adding an unbounded queue. | |
| 82 | - | ||
| 83 | - | Screenshots and video are opaque SDR. Audio, pause and cursor guarantees are not supported. NeferWL ignores the capture protocol's cursor option. | |
| 84 | - | ||
| 85 | - | ## Architecture and footprint | |
| 86 | - | ||
| 87 | - | `internal/core` owns capture and selection state and imports only stdlib and `internal/ports`. `internal/adapters` contains Wayland, selection, indicator, control, PNG and FFmpeg integrations. `internal/app` wires them together. | |
| 109 | + | Copy `bin/nefercap` to a directory on your `PATH`. | |
| 88 | 110 | ||
| 89 | - | One capture is in flight. Writers synchronously consume borrowed, reusable shared-memory storage. Video strips stride padding and handles vertical inversion without a full-frame heap copy. The selector opens one transient layer surface per output; the HUD is one small surface, not a fullscreen GPU buffer. Idle UI does not redraw continuously. | |
| 111 | + | ## Development | |
| 90 | 112 | ||
| 91 | - | The encoder uses `veryfast`, `zerolatency`, two encoding threads and one filter thread. Its separate-process footprint is not the Go heap; lower buffering can produce larger files than slower presets. | |
| 113 | + | `internal/core` owns capture and selection state and imports only the standard library and `internal/ports`. `internal/adapters` holds Wayland, selector, HUD, PNG and FFmpeg code; `internal/app` wires them. | |
| 92 | 114 | ||
| 93 | 115 | ```sh | |
| 94 | 116 | make check | |
| 95 | 117 | make race | |
| 96 | 118 | make mocks-check | |
| 97 | 119 | staticcheck ./... | |
| 98 | - | go test ./... -run '^$' -bench . -benchmem | |
| 99 | 120 | ``` | |
| 100 | 121 | ||
| 101 | - | Mockery v3 generates test doubles. Allocation guards cover frame views, selection state, unchanged overlay geometry, HUD refresh, video rows, encoder writes and output lookup. Protocol capture has a measured allocation budget; the application does not claim zero total allocations. | |
| 122 | + | Test doubles are generated by Mockery v3. | |
| 102 | 123 | ||
| 103 | 124 | ## License | |
| 104 | 125 | ||
| @@ -1,34 +1,56 @@ | |||||
| 1 | - | # nefercap | 1 | + | <h1 align="center">nefercap</h1> |
| 2 | 2 | ||||
| 3 | - | Fast Wayland screenshots and silent video recording with a NeferGUI layer-shell selector. | 3 | + | <p align="center"> |
| 4 | + | <a href="LICENSE"><img src="https://img.shields.io/badge/license-GPLv3-blue?style=flat-square" alt="License: GPLv3"></a> | |||
| 5 | + | <a href="https://github.com/bnema/nefercap"><img src="https://img.shields.io/badge/platform-Linux-blue?style=flat-square" alt="Platform: Linux"></a> | |||
| 6 | + | <a href="https://github.com/bnema/nefercap/commits/main"><img src="https://badgen.net/github/last-commit/bnema/nefercap/main?icon=github" alt="Last commit"></a> | |||
| 7 | + | <a href="https://github.com/bnema/nefercap/stargazers"><img src="https://badgen.net/github/stars/bnema/nefercap?icon=github" alt="GitHub stars"></a> | |||
| 8 | + | </p> | |||
| 4 | 9 | ||||
| 5 | - | ## Quick capture | 10 | + | <p align="center">Screenshots and silent screen recording for Wayland, from one shortcut.</p> |
| 6 | 11 | ||||
| 7 | - | ```sh | 12 | + | > [!WARNING] |
| 8 | - | make bin | 13 | + | > **Early alpha.** Built for [NeferWL](https://github.com/bnema/neferwl). Other compositors can take screenshots but cannot record. Expect bugs and breaking config changes. |
| 9 | - | ./bin/nefercap | 14 | + | |
| 10 | - | ./bin/nefercap shot | 15 | + | --- |
| 11 | - | ./bin/nefercap shot -clipboard | 16 | + | |
| 12 | - | ./bin/nefercap shot -clipboard -file capture.png | 17 | + | ## Usage |
| 13 | - | ./bin/nefercap rec | 18 | + | |
| 14 | - | ./bin/nefercap stop | 19 | + | Bind `nefercap` to a key in your compositor. For NeferWL: |
| 15 | - | ./bin/nefercap status | 20 | + | |
| 21 | + | ```text | |||
| 22 | + | bind.cmd+p = spawn nefercap | |||
| 16 | ``` | 23 | ``` | ||
| 17 | 24 | ||||
| 18 | - | `nefercap` without a command is all-in-one: it stops an active recording, otherwise opens the selector in shot mode. **Tab** switches shot ⇄ rec and keeps the chosen region, monitor or workspace; Enter or a click confirms in the current mode. It takes no flags except `-debug` and reads its settings from the config file. `shot` and `rec` keep a fixed mode (no Tab) and accept `-file`/`-clipboard` (shot) to override the config per run. A neutral badge at the top left of every overlay always shows the mode: `SHOT` or `REC`. | 25 | + | Press the key: a selector covers every monitor. Pick what to capture, press **Tab** to switch between screenshot and recording, then confirm. A badge at the top left shows the mode: `SHOT` or `REC`. |
| 19 | 26 | ||||
| 20 | - | The selector covers the connected outputs, up to nine: | 27 | + | While recording, a red border marks the target and a small **REC / Stop** HUD shows the time. Neither appears in the video. Press the key again, or click Stop, to finish the file. |
| 21 | 28 | ||||
| 22 | - | - **Drag and release:** capture a rectangle, clamped to its monitor. | 29 | + | | Input | Action | |
| 23 | - | - **Click:** capture the monitor; small pointer jitter counts as a click. | 30 | + | | --- | --- | |
| 24 | - | - **M, then Enter:** select the monitor. | 31 | + | | Drag | Capture a rectangle | |
| 25 | - | - **W, then Enter:** select the current workspace when native workspace metadata is available. | 32 | + | | Click | Capture the monitor | |
| 26 | - | - **R:** return to rectangle selection. **G:** toggle full-monitor center and thirds guides. | 33 | + | | `W`, then Enter | Capture the current workspace | |
| 27 | - | - **1–9:** select that monitor. **Esc:** cancel without creating a capture. | 34 | + | | `M`, then Enter | Capture the monitor | |
| 35 | + | | `1`–`9` | Pick that monitor | | |||
| 36 | + | | `R` | Back to rectangle selection | | |||
| 37 | + | | `G` | Show or hide the thirds guides | | |||
| 38 | + | | `Tab` | Switch shot ⇄ rec | | |||
| 39 | + | | `Esc` | Cancel | | |||
| 28 | 40 | ||||
| 29 | - | The selector shows mode keys and the current confirmation action in a two-line legend. Workspace hints appear only when a current workspace is available. Text uses a 1rem base (16 logical pixels), with proportional spacing and controls; the output's fractional scaling determines physical size. | 41 | + | A workspace recording follows that workspace, even when you switch to another one. A monitor recording follows whatever the monitor shows. |
| 30 | 42 | ||||
| 31 | - | The guides are visible by default: three vertical and three horizontal medium-gray lines, one physical pixel wide, aligned to each monitor's scaling. They stay fixed while dragging. Settings live in `$XDG_CONFIG_HOME/nefercap/config` (default `~/.config/nefercap/config`): | 43 | + | ## Features |
| 44 | + | ||||
| 45 | + | - **One shortcut.** Screenshot and recording share one selector; the same key stops a recording. | |||
| 46 | + | - **Clean video.** The compositor keeps the border and HUD out of the recording. | |||
| 47 | + | - **Clipboard.** Screenshots can be saved, copied, or both. | |||
| 48 | + | - **Small footprint.** One capture in flight, reused buffers, no redraw while idle. Allocation tests guard the hot paths. | |||
| 49 | + | - **Scriptable.** `nefercap screenshot` and `nefercap record` run without the selector. | |||
| 50 | + | ||||
| 51 | + | ## Configuration | |||
| 52 | + | ||||
| 53 | + | `~/.config/nefercap/config` (or `$XDG_CONFIG_HOME/nefercap/config`): | |||
| 32 | 54 | ||||
| 33 | ```ini | 55 | ```ini | ||
| 34 | selector.grid = on | 56 | selector.grid = on | ||
| @@ -37,68 +59,67 @@ screenshot.output = file+clipboard | |||||
| 37 | video.dir = ~/Videos | 59 | video.dir = ~/Videos | ||
| 38 | ``` | 60 | ``` | ||
| 39 | 61 | ||||
| 40 | - | - `selector.grid`: `on` (default) or `off` to hide the guides on startup. | 62 | + | | Key | Values | Default | |
| 41 | - | - `screenshot.dir`, `video.dir`: an absolute path or `~/…`; relative paths are errors. Absent, the XDG defaults below apply. Before the selector opens, nefercap creates (private) and checks each directory the command can use: both for all-in-one, one for `shot` or `rec`. | 63 | + | | --- | --- | --- | |
| 42 | - | - `screenshot.output`: `file` (default), `file+clipboard` (save, then copy the same PNG) or `clipboard` (copy only; no file, no path printed). | 64 | + | | `selector.grid` | `on`, `off` | `on` | |
| 65 | + | | `screenshot.dir` | absolute or `~/` path | `$XDG_PICTURES_DIR/Screenshots` | | |||
| 66 | + | | `screenshot.output` | `file`, `file+clipboard`, `clipboard` | `file` | | |||
| 67 | + | | `video.dir` | absolute or `~/` path | `$XDG_VIDEOS_DIR` | | |||
| 43 | 68 | ||||
| 44 | - | Each key may appear once; blank lines and `#` comment lines are allowed; unknown keys, duplicates and invalid values are errors reported before the selector opens. `screenshot.output` and the directories apply to all-in-one, `shot` and `rec`; the scripted `screenshot` and `record` never read them. On `shot`, `-clipboard` alone copies only, `-clipboard -file PATH` saves and copies, and `-file PATH` alone saves only, whatever `screenshot.output` says. **G** changes visibility for the current monitor's overlay without rewriting the config. Selection dimensions are logical pixels; the captured file's dimensions depend on the monitor scaling. | 69 | + | Unknown keys, duplicates and invalid values are reported before the selector opens. Files are private, named with a timestamp, never overwritten, and their path is printed on stdout. |
| 45 | 70 | ||||
| 46 | - | A screenshot saves immediately. Recording displays a red border around the visible target and a small **REC / Stop** HUD. Both are excluded from nefercap's video. The HUD takes no keyboard focus; clicks outside Stop pass through. Press the recording shortcut again, click Stop, or run `nefercap stop` to finish the file. | 71 | + | ## Commands |
| 47 | 72 | ||||
| 48 | - | Monitor recording follows workspace changes. A workspace recording follows its stable identity and continues when another workspace is displayed. A fixed region stays attached to its monitor. A hidden workspace has no border on the unrelated visible workspace; the recording HUD remains available. | 73 | + | ```sh |
| 74 | + | nefercap # all-in-one: stop the recording, else open the selector | |||
| 75 | + | nefercap shot # selector, screenshot only | |||
| 76 | + | nefercap rec # selector, recording only | |||
| 77 | + | nefercap stop # stop the active recording | |||
| 78 | + | nefercap status # print the recording state | |||
| 79 | + | ``` | |||
| 49 | 80 | ||||
| 50 | - | Without `screenshot.dir`/`video.dir`, screenshots go to the configured XDG Pictures directory's `Screenshots` subdirectory, and videos to the XDG Videos directory. Names include a nanosecond timestamp. `-file` chooses an explicit destination. Files are private to their owner and are never overwritten. The saved path is printed to stdout, including for scripted commands. Cancelling before any frame is written creates no file and prints no path. | 81 | + | Scripted commands never open the selector: |
| 51 | 82 | ||||
| 52 | - | `shot -clipboard` copies an `image/png` screenshot using `wl-copy` without creating a capture file or printing a path. Add `-file PATH` to save and copy the same PNG. A successfully saved file is retained and its path printed even if clipboard copying later fails. The clipboard owner intentionally survives nefercap's exit. This CLI-only option also works with scripted `screenshot`; other commands reject it. Install `wl-clipboard` to use it. | 83 | + | ```sh |
| 84 | + | nefercap outputs | |||
| 85 | + | nefercap screenshot -output DP-1 -file shot.png | |||
| 86 | + | nefercap screenshot -output DP-1 -region 0,0,1920x1080 -clipboard | |||
| 87 | + | nefercap record -output DP-1 -fps 30 -duration 10s -file clip.mp4 | |||
| 88 | + | ``` | |||
| 53 | 89 | ||||
| 54 | - | `shot` while a recording is active is refused: its overlay is not part of the existing session's authorized controls. A scripted `screenshot` still uses standard capture and includes the active HUD and border. | 90 | + | `nefercap -h` lists every flag. Regions use output-local logical coordinates. `-size WxH` sets an even output resolution for odd-sized sources. |
| 55 | 91 | ||||
| 56 | ## Requirements | 92 | ## Requirements | ||
| 57 | 93 | ||||
| 58 | - | - Go 1.27 for development; normal builds use `CGO_ENABLED=0`. | 94 | + | - Go 1.27 to build. |
| 59 | - | - For selection: layer-shell v4, Vulkan, libxkbcommon, linux-dmabuf and linux-drm-syncobj. | 95 | + | - A Wayland compositor with `zwlr_layer_shell_v1` v4 and `zwlr_screencopy_manager_v1`. |
| 60 | - | - For capture: `zwlr_screencopy_manager_v1`. | 96 | + | - Vulkan, libxkbcommon, linux-dmabuf and linux-drm-syncobj for the selector. |
| 61 | - | - For recording indicators and workspace targets: NeferWL's native capture-session protocol. | 97 | + | - NeferWL's capture-session protocol to record. |
| 62 | - | - FFmpeg with `libx264` for silent H.264 video in fragmented MP4. | 98 | + | - FFmpeg with `libx264` to record. |
| 63 | - `wl-copy` (wl-clipboard) for clipboard screenshots. | 99 | - `wl-copy` (wl-clipboard) for clipboard screenshots. | ||
| 64 | 100 | ||||
| 65 | - | A standard compositor can support monitor/region screenshots. Recording refuses to start without native session support rather than capture its own controls. Standard capture clients keep their normal behavior and see the HUD and border. | 101 | + | Output is SDR, and video is silent H.264 in fragmented MP4. Audio, pause and cursor capture are not supported. |
| 66 | 102 | ||||
| 67 | - | ## Scripted commands | 103 | + | ## Install |
| 68 | 104 | ||||
| 69 | ```sh | 105 | ```sh | ||
| 70 | - | ./bin/nefercap outputs | 106 | + | make bin # builds bin/nefercap |
| 71 | - | ./bin/nefercap screenshot -output DP-1 -file capture.png | |||
| 72 | - | ./bin/nefercap screenshot -output DP-1 -region 0,0,1920x1080 -file region.png | |||
| 73 | - | ./bin/nefercap record -output DP-1 -fps 30 -duration 10s -file capture.mp4 | |||
| 74 | - | ./bin/nefercap rec -size 1920x1080 -file presentation.mp4 | |||
| 75 | ``` | 107 | ``` | ||
| 76 | 108 | ||||
| 77 | - | Use an output name from `outputs`. Scripted `screenshot` requires `-file` unless `-clipboard` is used; `record` always requires `-file`; omitting `-output` is allowed with exactly one output. Regions use output-local logical coordinates, not desktop-global coordinates. Capture dimensions are physical pixels negotiated with the compositor. | 109 | + | Copy `bin/nefercap` to a directory on your `PATH`. |
| 78 | - | ||||
| 79 | - | `-fps` accepts 1–120, default 30. `-size` requires two even dimensions and changes encoded resolution, not application scaling. Odd source dimensions require an explicit even size; pixels are not silently cropped. `-debug` enables diagnostic logging. | |||
| 80 | - | ||||
| 81 | - | Untimed recording stops with the HUD, the control command, Ctrl+C or SIGTERM and finalizes the file. The `stop` command acknowledges the request; finalization finishes in the recorder process, not before the control reply. Timed recording encodes `ceil(duration × fps)` frames. Slow capture can repeat frames; encoder backpressure can extend wall time without adding an unbounded queue. | |||
| 82 | - | ||||
| 83 | - | Screenshots and video are opaque SDR. Audio, pause and cursor guarantees are not supported. NeferWL ignores the capture protocol's cursor option. | |||
| 84 | - | ||||
| 85 | - | ## Architecture and footprint | |||
| 86 | - | ||||
| 87 | - | `internal/core` owns capture and selection state and imports only stdlib and `internal/ports`. `internal/adapters` contains Wayland, selection, indicator, control, PNG and FFmpeg integrations. `internal/app` wires them together. | |||
| 88 | 110 | ||||
| 89 | - | One capture is in flight. Writers synchronously consume borrowed, reusable shared-memory storage. Video strips stride padding and handles vertical inversion without a full-frame heap copy. The selector opens one transient layer surface per output; the HUD is one small surface, not a fullscreen GPU buffer. Idle UI does not redraw continuously. | 111 | + | ## Development |
| 90 | 112 | ||||
| 91 | - | The encoder uses `veryfast`, `zerolatency`, two encoding threads and one filter thread. Its separate-process footprint is not the Go heap; lower buffering can produce larger files than slower presets. | 113 | + | `internal/core` owns capture and selection state and imports only the standard library and `internal/ports`. `internal/adapters` holds Wayland, selector, HUD, PNG and FFmpeg code; `internal/app` wires them. |
| 92 | 114 | ||||
| 93 | ```sh | 115 | ```sh | ||
| 94 | make check | 116 | make check | ||
| 95 | make race | 117 | make race | ||
| 96 | make mocks-check | 118 | make mocks-check | ||
| 97 | staticcheck ./... | 119 | staticcheck ./... | ||
| 98 | - | go test ./... -run '^$' -bench . -benchmem | |||
| 99 | ``` | 120 | ``` | ||
| 100 | 121 | ||||
| 101 | - | Mockery v3 generates test doubles. Allocation guards cover frame views, selection state, unchanged overlay geometry, HUD refresh, video rows, encoder writes and output lookup. Protocol capture has a measured allocation budget; the application does not claim zero total allocations. | 122 | + | Test doubles are generated by Mockery v3. |
| 102 | 123 | ||||
| 103 | ## License | 124 | ## License | ||
| 104 | 125 | ||||