mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-09-20 20:48:06 +00:00
libghostty: add ghostty_terminal_paste C API with paste events example
This commit is contained in:
@@ -34,7 +34,7 @@
|
||||
* - @ref snapshot "Terminal Snapshot" - Encode and incrementally restore terminal state
|
||||
* - @ref osc "OSC Parser" - Parse OSC (Operating System Command) sequences
|
||||
* - @ref sgr "SGR Parser" - Parse SGR (Select Graphic Rendition) sequences
|
||||
* - @ref paste "Paste Utilities" - Validate paste data safety
|
||||
* - @ref paste "Paste" - Paste into a terminal, validate and encode paste data
|
||||
* - @ref unicode "Unicode Utilities" - Codepoint properties for text layout
|
||||
* - @ref build_info "Build Info" - Query compile-time build configuration
|
||||
* - @ref allocator "Memory Management" - Memory management and custom allocators
|
||||
@@ -53,7 +53,7 @@
|
||||
* - @ref c-vt/src/main.c - OSC parser example
|
||||
* - @ref c-vt-encode-key/src/main.c - Key encoding example
|
||||
* - @ref c-vt-encode-mouse/src/main.c - Mouse encoding example
|
||||
* - @ref c-vt-paste/src/main.c - Paste safety check example
|
||||
* - @ref c-vt-paste/src/main.c - Paste example
|
||||
* - @ref c-vt-sgr/src/main.c - SGR parser example
|
||||
* - @ref c-vt-formatter/src/main.c - Terminal formatter example
|
||||
* - @ref c-vt-grid-traverse/src/main.c - Grid traversal example using grid refs
|
||||
@@ -83,8 +83,10 @@
|
||||
*/
|
||||
|
||||
/** @example c-vt-paste/src/main.c
|
||||
* This example demonstrates how to use the paste utilities to check if
|
||||
* paste data is safe before sending it to the terminal.
|
||||
* This example demonstrates how to paste into a terminal, including the
|
||||
* unsafe-paste confirmation flow and Kitty clipboard protocol paste events
|
||||
* (mode 5522), as well as the terminal-free paste safety and encoding
|
||||
* utilities.
|
||||
*/
|
||||
|
||||
/** @example c-vt-sgr/src/main.c
|
||||
|
||||
@@ -1,26 +1,57 @@
|
||||
/**
|
||||
* @file paste.h
|
||||
*
|
||||
* Paste utilities - validate and encode paste data for terminal input.
|
||||
* Paste - paste into a terminal, and validate and encode paste data.
|
||||
*/
|
||||
|
||||
#ifndef GHOSTTY_VT_PASTE_H
|
||||
#define GHOSTTY_VT_PASTE_H
|
||||
|
||||
/** @defgroup paste Paste Utilities
|
||||
/** @defgroup paste Paste
|
||||
*
|
||||
* Utilities for validating and encoding paste data for terminal input.
|
||||
* Pasting into a terminal, plus the terminal-free utilities for
|
||||
* validating and encoding paste data.
|
||||
*
|
||||
* ## Basic Usage
|
||||
* ## Pasting into a Terminal
|
||||
*
|
||||
* Use ghostty_paste_is_safe() to check if paste data contains potentially
|
||||
* dangerous sequences before sending it to the 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 what the clipboard holds as MIME-typed contents (just
|
||||
* `text/plain` for an ordinary paste) and where it came from, and the
|
||||
* terminal decides how its current modes apply:
|
||||
*
|
||||
* Use ghostty_paste_encode() to encode paste data for writing to the pty,
|
||||
* - 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.
|
||||
* - 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.
|
||||
*
|
||||
* Text that could inject commands (a newline when unbracketed, or the
|
||||
* bracketed paste terminator when bracketed) is refused with
|
||||
* GHOSTTY_REJECTED 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.
|
||||
*
|
||||
* Output is delivered through the write_pty callback
|
||||
* (GHOSTTY_TERMINAL_OPT_WRITE_PTY) in a single call.
|
||||
*
|
||||
* @snippet c-vt-paste/src/main.c terminal-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.
|
||||
*
|
||||
* ## Examples
|
||||
*
|
||||
* ### Safety Check
|
||||
*
|
||||
* @snippet c-vt-paste/src/main.c paste-safety
|
||||
@@ -35,11 +66,99 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <ghostty/vt/types.h>
|
||||
#include <ghostty/vt/terminal.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Why a paste happened.
|
||||
*/
|
||||
typedef enum GHOSTTY_ENUM_TYPED {
|
||||
/** The user pasted from a clipboard: keybind, menu, middle click. */
|
||||
GHOSTTY_PASTE_SOURCE_CLIPBOARD = 0,
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
GHOSTTY_PASTE_SOURCE_TEXT = 1,
|
||||
GHOSTTY_PASTE_SOURCE_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
|
||||
} GhosttyPasteSource;
|
||||
|
||||
/**
|
||||
* A paste of clipboard contents into the terminal.
|
||||
*
|
||||
* This is a sized struct; set `size` to `sizeof(GhosttyPaste)`. The
|
||||
* contents array and the strings it points to are borrowed only for the
|
||||
* duration of the ghostty_terminal_paste() call.
|
||||
*/
|
||||
typedef struct {
|
||||
/** Size of this struct in bytes. */
|
||||
size_t size;
|
||||
|
||||
/**
|
||||
* The clipboard the contents came from. Reported to the program on a
|
||||
* paste event (the selection and primary locations are both reported
|
||||
* as the primary selection, the protocol knows only two); no effect
|
||||
* on a text paste.
|
||||
*/
|
||||
GhosttyClipboardLocation location;
|
||||
|
||||
/** Why this paste happened. */
|
||||
GhosttyPasteSource source;
|
||||
|
||||
/**
|
||||
* Borrowed array of the representations available, in preferred
|
||||
* order. A text paste writes the first entry with a text MIME type
|
||||
* such as "text/plain" and ignores the rest. A paste event lists every
|
||||
* entry's MIME type and never reads data, so non-text entries may have
|
||||
* empty data. May be NULL when contents_len is zero.
|
||||
*/
|
||||
const GhosttyClipboardContent* contents;
|
||||
|
||||
/** Number of entries in contents. */
|
||||
size_t contents_len;
|
||||
|
||||
/**
|
||||
* Write text that could inject commands. Call with false, confirm
|
||||
* with the user on GHOSTTY_REJECTED, and call again with true.
|
||||
*/
|
||||
bool allow_unsafe;
|
||||
} GhosttyPaste;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* goes through the write_pty callback in a single call. The viewport is
|
||||
* not scrolled; that is up to the embedder, as for key input.
|
||||
*
|
||||
* @param terminal The terminal handle
|
||||
* @param paste The paste request, borrowed for the duration of the call
|
||||
* @param[out] out_written On 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.
|
||||
* @return GHOSTTY_SUCCESS on success (see @p 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, or when no
|
||||
* write_pty callback is installed; GHOSTTY_OUT_OF_MEMORY;
|
||||
* GHOSTTY_IO_ERROR if there is no secure entropy source to mint
|
||||
* a paste event password (wasm32-freestanding without
|
||||
* GHOSTTY_SYS_OPT_RANDOM_SECURE set), in which case nothing was
|
||||
* written and no grant was recorded.
|
||||
*/
|
||||
GHOSTTY_API GhosttyResult ghostty_terminal_paste(
|
||||
GhosttyTerminal terminal,
|
||||
const GhosttyPaste* paste,
|
||||
bool* out_written);
|
||||
|
||||
/**
|
||||
* Check if paste data is safe to paste into the terminal.
|
||||
*
|
||||
@@ -49,7 +168,9 @@ extern "C" {
|
||||
* to exit bracketed paste mode and inject commands
|
||||
*
|
||||
* This check is conservative and considers data unsafe regardless of
|
||||
* current terminal state.
|
||||
* 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.
|
||||
*
|
||||
* @param data The paste data to check (must not be NULL)
|
||||
* @param len The length of the data in bytes
|
||||
@@ -74,6 +195,9 @@ GHOSTTY_API bool ghostty_paste_is_safe(const char* data, size_t len);
|
||||
* GHOSTTY_OUT_OF_SPACE and sets the required size in @p 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.
|
||||
*
|
||||
* @param data The paste data to encode (modified in place, may be NULL)
|
||||
* @param data_len The length of the input data in bytes
|
||||
* @param bracketed Whether bracketed paste mode is active
|
||||
|
||||
@@ -724,6 +724,11 @@ struct GhosttyClipboardRead {
|
||||
* serves a request for only the targets listing (`list` with no `mimes`)
|
||||
* without prompting.
|
||||
*
|
||||
* Installing this callback also enables Kitty paste events (mode 5522):
|
||||
* ghostty_terminal_paste() sends the program an event instead of the text,
|
||||
* and the program's follow-up read arrives here with `granted` set since
|
||||
* the user already pasted. See ghostty_terminal_paste().
|
||||
*
|
||||
* @param terminal The terminal handle
|
||||
* @param userdata The userdata pointer set via GHOSTTY_TERMINAL_OPT_USERDATA
|
||||
* @param read Borrowed clipboard read request
|
||||
|
||||
@@ -100,6 +100,12 @@ typedef enum GHOSTTY_ENUM_TYPED {
|
||||
GHOSTTY_IO_ERROR = -5,
|
||||
/** Operation failed because encoded input exceeded a configured limit */
|
||||
GHOSTTY_LIMIT_EXCEEDED = -6,
|
||||
/**
|
||||
* Operation was rejected by a safety check (e.g. pasted text that could
|
||||
* inject commands). Nothing was done. Confirm with the user and retry
|
||||
* with the operation's allow flag set.
|
||||
*/
|
||||
GHOSTTY_REJECTED = -7,
|
||||
GHOSTTY_RESULT_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
|
||||
} GhosttyResult;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user