libghostty
Loading...
Searching...
No Matches
Paste

Detailed Description

Pasting into a terminal, plus the terminal-free utilities for validating and encoding paste data.

Pasting into a Terminal

What a paste writes to the pty depends on the terminal's state, so the recommended way to paste is ghostty_terminal_paste(). The embedder hands over the MIME types the clipboard holds (just text/plain for an ordinary paste), a GhosttyMimeReader that produces the data of any one of them, and where the paste came from, and the terminal decides how its current modes apply:

  • If Kitty clipboard protocol paste events (mode 5522, GHOSTTY_MODE_PASTE_EVENTS) are enabled, the paste was user-initiated (GHOSTTY_PASTE_SOURCE_CLIPBOARD), and a clipboard_read callback is installed, the terminal sends the program a paste event listing the clipboard's MIME types with a one-time password instead of the data. The program then reads what it wants through the clipboard_read callback, which arrives with granted set so no permission prompt is needed. No data is read for the event.
  • Otherwise the first text representation is written: unsafe control bytes are replaced with spaces, and it is wrapped in bracketed paste sequences if mode 2004 (GHOSTTY_MODE_BRACKETED_PASTE) is enabled, or has its newlines converted to carriage returns if not.

The data is pulled through GhosttyPaste::reader only when a representation is actually pasted (so a clipboard holding a large image next to some text costs nothing), and the encoded bytes stream to the write_pty callback (GHOSTTY_TERMINAL_OPT_WRITE_PTY) in chunks as they are produced, never in one piece. The callback may be invoked several times for a single paste; the pieces must be written to the pty in order.

Text that could inject commands (a newline when unbracketed, or the bracketed paste terminator when bracketed) is refused with GHOSTTY_REJECTED and nothing written unless GhosttyPaste::allow_unsafe is set. The usual flow is to call once, confirm with the user on GHOSTTY_REJECTED, and call again with allow_unsafe set. Each call reads the text at most once and buffers it whole while the rule is applied, so the source needs no stability across reads (the confirmed retry simply pastes whatever the source holds then) and a refused or failed paste writes nothing at all.

// What the clipboard holds. A real embedder would keep a handle to the
// pasteboard or its items here; the data is only produced on demand.
typedef struct {
const char* text;
} clipboard_t;
// Produces the data of one representation when the terminal needs it.
// Only the text is ever read: the image is listed on a paste event
// but never requested, so a large image costs nothing to paste.
// Nothing written to the writer is retained, so the data can be
// streamed from anywhere in pieces of any size.
static bool read_clipboard(void* userdata, GhosttyString mime, GhosttyWriter writer) {
clipboard_t* clipboard = userdata;
if (mime.len == strlen("text/plain") &&
memcmp(mime.ptr, "text/plain", mime.len) == 0) {
// Stream the text in small pieces just to show that it works.
const uint8_t* data = (const uint8_t*)clipboard->text;
size_t len = strlen(clipboard->text);
for (size_t offset = 0; offset < len; offset += 4) {
size_t n = len - offset < 4 ? len - offset : 4;
if (!writer.write(writer.userdata, data + offset, n)) return false;
}
return true;
}
printf(" image read requested, which never happens\n");
return false;
}
// Paste whatever the clipboard holds. The terminal applies its own
// state: bracketed paste framing (mode 2004) or a Kitty paste event
// (mode 5522) instead of the text.
static void paste_clipboard(GhosttyTerminal terminal, const char* text) {
clipboard_t clipboard = {.text = text};
GhosttyString mimes[] = {
// The first text representation is what a text paste writes.
GS("text/plain"),
// Listed on a paste event, never read.
GS("image/png"),
};
GhosttyPaste paste = {
.size = sizeof(paste),
.mimes = mimes,
.mimes_len = sizeof(mimes) / sizeof(mimes[0]),
.reader = {.read = read_clipboard, .userdata = &clipboard},
.allow_unsafe = false,
};
bool written = false;
GhosttyResult result = ghostty_terminal_paste(terminal, &paste, &written);
if (result == GHOSTTY_REJECTED) {
// The text could inject commands (e.g. a newline outside of a
// bracketed paste). Nothing was written; ask, then retry.
if (!confirm_with_user()) return;
paste.allow_unsafe = true;
result = ghostty_terminal_paste(terminal, &paste, &written);
}
if (result != GHOSTTY_SUCCESS) {
fprintf(stderr, "paste failed: %d\n", (int)result);
return;
}
// Whether the pty got the text or a paste event depends on the
// terminal's modes; either way it went through write_pty above, in
// chunks as the text was read.
printf(" %s\n", written ? "written" : "nothing to paste");
}

Building Blocks

For embedders that encode without a terminal, ghostty_paste_is_safe() checks if paste data contains potentially dangerous sequences (conservatively, regardless of terminal state) and ghostty_paste_encode() encodes paste data for writing to the pty, including bracketed paste wrapping and unsafe byte stripping.

Safety Check

void safety_example() {
const char* safe_data = "hello world";
const char* unsafe_data = "rm -rf /\n";
if (ghostty_paste_is_safe(safe_data, strlen(safe_data))) {
printf("Safe to paste\n");
}
if (!ghostty_paste_is_safe(unsafe_data, strlen(unsafe_data))) {
printf("Unsafe! Contains newline\n");
}
}

Encoding

void encode_example() {
// The input buffer is modified in place (unsafe bytes are stripped).
char data[] = "hello\nworld";
char buf[64];
size_t written = 0;
data, strlen(data), true, buf, sizeof(buf), &written);
if (result == GHOSTTY_SUCCESS) {
printf("Encoded %zu bytes: ", written);
print_escaped((const uint8_t*)buf, written);
printf("\n");
}
}

Enumerations

enum  GhosttyPasteSource { GHOSTTY_PASTE_SOURCE_CLIPBOARD = 0 , GHOSTTY_PASTE_SOURCE_TEXT = 1 , GHOSTTY_PASTE_SOURCE_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE }

Functions

GHOSTTY_API GhosttyResult ghostty_terminal_paste (GhosttyTerminal terminal, const GhosttyPaste *paste, bool *out_written)
GHOSTTY_API bool ghostty_paste_is_safe (const char *data, size_t len)
GHOSTTY_API GhosttyResult ghostty_paste_encode (char *data, size_t data_len, bool bracketed, char *buf, size_t buf_len, size_t *out_written)

Data Structures

struct  GhosttyPaste

Enumeration Type Documentation

◆ GhosttyPasteSource

Why a paste happened.

Enumerator
GHOSTTY_PASTE_SOURCE_CLIPBOARD 

The user pasted from a clipboard: keybind, menu, middle click.

GHOSTTY_PASTE_SOURCE_TEXT 

Text inserted some other way: IME commit, drag and drop, scripted input. Always written as text, never as a paste event, matching kitty. This is not a way to opt out of paste events; an embedder that doesn't want them doesn't install a clipboard_read callback.

Definition at line 89 of file paste.h.

Function Documentation

◆ ghostty_paste_encode()

GHOSTTY_API GhosttyResult ghostty_paste_encode ( char * data,
size_t data_len,
bool bracketed,
char * buf,
size_t buf_len,
size_t * out_written )

Encode paste data for writing to the terminal pty.

This function prepares paste data for terminal input by:

  • Stripping unsafe control bytes (NUL, ESC, DEL, etc.) by replacing them with spaces
  • Wrapping the data in bracketed paste sequences if bracketed is true
  • Replacing newlines with carriage returns if bracketed is false

The input data buffer is modified in place during encoding. The encoded result (potentially with bracketed paste prefix/suffix) is written to the output buffer.

If the output buffer is too small, the function returns GHOSTTY_OUT_OF_SPACE and sets the required size in out_written. The caller can then retry with a sufficiently sized buffer.

This is the encoder ghostty_terminal_paste() uses for a text paste; use it directly when there is no terminal to paste into.

Parameters
dataThe paste data to encode (modified in place, may be NULL)
data_lenThe length of the input data in bytes
bracketedWhether bracketed paste mode is active
bufOutput buffer to write the encoded result into (may be NULL)
buf_lenSize of the output buffer in bytes
[out]out_writtenOn success, the number of bytes written. On GHOSTTY_OUT_OF_SPACE, the required buffer size.
Returns
GHOSTTY_SUCCESS on success, GHOSTTY_OUT_OF_SPACE if the buffer is too small
Examples
c-vt-paste/src/main.c.

References ghostty_paste_encode().

Referenced by ghostty_paste_encode().

◆ ghostty_paste_is_safe()

GHOSTTY_API bool ghostty_paste_is_safe ( const char * data,
size_t len )

Check if paste data is safe to paste into the terminal.

Data is considered unsafe if it contains:

  • Newlines (\n) which can inject commands
  • The bracketed paste end sequence (\x1b[201~) which can be used to exit bracketed paste mode and inject commands

This check is conservative and considers data unsafe regardless of current terminal state. ghostty_terminal_paste() applies the terminal-state-aware rule itself (newlines are safe inside a bracketed paste); use this to apply the stricter rule on top.

Parameters
dataThe paste data to check (must not be NULL)
lenThe length of the data in bytes
Returns
true if the data is safe to paste, false otherwise
Examples
c-vt-paste/src/main.c.

References ghostty_paste_is_safe().

Referenced by ghostty_paste_is_safe().

◆ ghostty_terminal_paste()

GHOSTTY_API GhosttyResult ghostty_terminal_paste ( GhosttyTerminal terminal,
const GhosttyPaste * paste,
bool * out_written )

Paste into the terminal according to its current state: a Kitty clipboard protocol paste event if mode 5522 is enabled and a clipboard_read callback is installed, otherwise the text framed per mode 2004. See the group documentation for the full behavior. Output streams through the write_pty callback in chunks. The viewport is not scrolled; that is up to the embedder, as for key input.

A paste event records a session grant for its one-time password only once the event is written; a failed call never leaves a grant for an event that was never sent.

Parameters
terminalThe terminal handle
pasteThe paste request, borrowed for the duration of the call
[out]out_writtenOn success, whether anything was written to the pty (the encoded text or a paste event). False means there was nothing to paste: no non-empty text representation. May be NULL.
Returns
GHOSTTY_SUCCESS on success (see out_written); GHOSTTY_REJECTED if the text could inject commands and GhosttyPaste::allow_unsafe is false (nothing was written); GHOSTTY_INVALID_VALUE for a NULL terminal or paste, MIME types without a reader, or when no write_pty callback is installed; GHOSTTY_OUT_OF_MEMORY; GHOSTTY_IO_ERROR if the reader failed or there is no secure entropy source to mint a paste event password (wasm32-freestanding without GHOSTTY_SYS_OPT_RANDOM_SECURE set). Errors write nothing.
Examples
c-vt-paste/src/main.c.

References ghostty_terminal_paste().

Referenced by ghostty_terminal_paste().