# Buffer Capture (XTCAPTURE)

> A proposal for a program to ask its terminal for the text of its last lines, scrollback included, for a fuzzy finder or the like to search.

- **Sequence:** `CSI > Pl ; Pr , t`
- **Specification:** [Buffer Capture, Contour VT extensions](https://github.com/contour-terminal/contour/blob/3cdeed6c07ea892c3419a58d1d0a4c8b2d932519/docs/vt-extensions/buffer-capture.md)

*Buffer Capture* lets a program ask its terminal for the text of the last
lines on it, reaching back into the scrollback, so that a shell can hand
what was on the screen to a tool such as `fzf` to search and pick from. It
is Contour's, which names it XTCAPTURE. Its definition is
[Buffer Capture](https://github.com/contour-terminal/contour/blob/3cdeed6c07ea892c3419a58d1d0a4c8b2d932519/docs/vt-extensions/buffer-capture.md),
kept with Contour's other VT extensions, read at commit `3cdeed6`, and the
details below that the specification leaves out are from Contour's source
at release 0.8.0. No other terminal is known to implement it, and
libghostty-vt does not.

## The request

```
CSI > Pl ; Pr , t
```

`Pl` is `1` to count logical lines, where a line that wrapped across
several rows of the screen is one line, and `0` to count rows; Contour
takes `0` when it is left out and ignores the request for any other value.
`Pr` is how many to capture, counting up from the bottom of the page; left
out, it is the height of the page
([`CAPTURE`](https://github.com/contour-terminal/contour/blob/v0.8.0.9335/src/vtbackend/screen/Screen.cpp#L4481-L4500)).
A count larger than the page reaches into the scrollback, as far as the
scrollback goes.

The `,` intermediate is new. Contour first used `CSI > Pl ; Pr t`, which is
xterm's XTSMTITLE, the sequence that sets how xterm encodes titles, and
the specification now gives that form up to xterm.

## The answer

```
PM 314 ; data ST
```

The text comes back in [privacy messages](https://control-codes.page/esc/sos/index.html.md), as many as it takes,
followed by one with no data, `PM 314 ; ST`, which ends the answer. `data`
is the captured text in UTF-8, each line followed by LF.

Contour sends at most 4096 bytes of text in each message, and cuts the text
wherever that limit falls, so one character's bytes can be split between
two messages; a reader joins the messages before decoding them. Each line
loses its trailing blanks, and a line left empty is left out altogether
rather than sent as a bare LF. Counting logical lines, a row that continues
a wrapped line is joined to the one before without an LF. Contour always
reads the main screen, even while the alternate screen is showing
([`Screen::captureBuffer`](https://github.com/contour-terminal/contour/blob/v0.8.0.9335/src/vtbackend/screen/Screen.cpp#L2576-L2647)).

## Permission

Handing over the screen and its scrollback is something the user may not
want, so a capture waits on Contour's `capture_buffer` permission: `allow`,
`deny`, or `ask`, which is the default and asks the user. A refused request
is still answered, with the closing `PM 314 ; ST` and nothing before it,
because the protocol has no way to say that no answer is coming and a
program reading up to the end would otherwise wait forever. A refusal
therefore cannot be told apart from capturing a blank screen, and the
specification says that is deliberate
([`TerminalSession.cpp`](https://github.com/contour-terminal/contour/blob/v0.8.0.9335/src/contour/session/TerminalSession.cpp#L960-L992)).

---

This is the Markdown version of <https://control-codes.page/proposals/xtcapture/>. On that page every validation case runs live in libghostty-vt, the terminal emulation core of Ghostty, compiled to WebAssembly.
