Skip to content

Screenshots

Capture the screen reproducibly — the same path the docs’ own screenshots use, on every backend including --headless.

Terminal window
scoot msg screenshot --out /tmp/shot.png
scoot msg screenshot --output 2 --out /tmp/second.png
scoot msg screenshot --no-cursor --out /tmp/clean.png
Flag Type Default Meaning
--output ID int (primary output) Which output to capture. An id naming no output is refused rather than answered with another output’s pixels. A powered-off output is refused too, naming the recovery.
--out FILE path stdout Where the PNG goes; without it, the PNG bytes go to stdout.
--no-cursor switch draw it in Leave the pointer out (below).

A screenshot taken straight after a scootbg set shows it — the set returns once the change is on screen.

screenshot captures the framebuffer at full physical resolution, while windows and outputs report logical rectangles. Convert with physical = logical * scale, rounded down where an edge lands mid-pixel — and per output: convert with the scale of the output the window is on (its output), never the first output’s. (Full rule in Rules an agent needs.)

Pre-LUT: captures show the unmodified frame

Section titled “Pre-LUT: captures show the unmodified frame”

screenshot reads the framebuffer, which sits before the display’s gamma LUT and color matrix — so a night light (wlsunset, gammastep, or the desktop profile’s night light) never shows in a capture. The file holds the compositor’s own pixels: two screenshots across a warming boundary are byte-identical (modulo the pointer, below). To observe the ramp itself, read the CRTC color state back on the --tty session (drm_info, modetest).

Captures are paced: one per connection per 16 ms frame (a second inside the same frame is refused, and its refusal arrives first, so match replies by content, not position), one in flight per connection, four in flight across every client. On the --tty GPU scanout tier a capture can also be refused with a retry while the screen shows a client’s buffer directly — treat both refusals as retryable. The full list is what the socket refuses.

A screenshot shows the pointer, the same way on every backend and renderer, unless the request says not to. On the wire that is an optional cursor field on the request:

{"type": "screenshot"}
{"type": "screenshot", "cursor": false}

Omitted means drawn in (SCREENSHOT_CURSOR_DEFAULT in scoot-ipc); false leaves it out, and scoot msg screenshot --no-cursor sends that. Scripts see the pointer the same way under --headless and --nested (whose screens have no drawn cursor at all), on --tty’s CPU renderer, and on the GPU scanout tier (whose cursor rides a hardware plane): the pointer’s own image, hotspot and scale, at its position on the captured output and on no other output. A fresh session’s pointer sits at the centre of the first output, so a default screenshot shows it there until something moves it. Leave it out to diff two screenshots, or to read the pixels it would cover.

The field is additive: PROTOCOL_VERSION did not change. A server that predates it silently ignores it rather than refusing it, so cursor: false has no effect there, and the pointer shows only under --tty (and is missing where a cursor plane carries it). version reports the same protocol number either way, so a client cannot tell such a server apart.