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.
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#define GS(s) ((GhosttyString){.ptr = (const uint8_t*)(s), .len = sizeof(s) - 1})
static void print_escaped(const uint8_t* data, size_t len) {
for (size_t i = 0; i < len; i++) {
switch (data[i]) {
case 0x1b: printf("ESC"); break;
case '\r': printf("\\r"); break;
case '\n': printf("\\n"); break;
default: putchar(data[i]); break;
}
}
}
static char event_pw[128];
void* userdata,
const uint8_t* data,
size_t len) {
(void)terminal;
(void)userdata;
printf(" -> pty (%zu bytes): ", len);
print_escaped(data, len);
printf("\n");
const char* prefix = "\x1b]5522;type=read:status=OK:pw=";
size_t prefix_len = strlen(prefix);
if (len > prefix_len && memcmp(data, prefix, prefix_len) == 0) {
size_t end = prefix_len;
while (end < len && data[end] != 0x1b) end++;
size_t pw_len = end - prefix_len;
if (pw_len < sizeof(event_pw)) {
memcpy(event_pw, data + prefix_len, pw_len);
event_pw[pw_len] = 0;
}
}
}
void* userdata,
(void)terminal;
(void)userdata;
printf(" clipboard read: name=\"");
printf(
"\" granted=%s\n", read->
granted ?
"yes (no prompt needed)" :
"no");
const char* text = "hello from the clipboard";
.mime = GS("text/plain"),
.data = {.ptr = (const uint8_t*)text, .len = strlen(text)},
};
.size = sizeof(reply),
.contents = &content,
.contents_len = 1,
.available = NULL,
.available_len = 0,
.remember = false,
};
read->
reply(read, &reply);
}
static bool confirm_with_user(void) {
printf(" paste could inject commands; user confirmed\n");
return true;
}
typedef struct {
const char* text;
} clipboard_t;
clipboard_t* clipboard = userdata;
if (mime.
len == strlen(
"text/plain") &&
memcmp(mime.
ptr,
"text/plain", mime.
len) == 0) {
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;
}
clipboard_t clipboard = {.text = text};
GS("text/plain"),
GS("image/png"),
};
.size = sizeof(paste),
.mimes = mimes,
.mimes_len = sizeof(mimes) / sizeof(mimes[0]),
.reader = {.read = read_clipboard, .userdata = &clipboard},
.allow_unsafe = false,
};
bool written = false;
if (!confirm_with_user()) return;
}
fprintf(stderr, "paste failed: %d\n", (int)result);
return;
}
printf(" %s\n", written ? "written" : "nothing to paste");
}
void safety_example() {
const char* safe_data = "hello world";
const char* unsafe_data = "rm -rf /\n";
printf("Safe to paste\n");
}
printf("Unsafe! Contains newline\n");
}
}
void encode_example() {
char data[] = "hello\nworld";
char buf[64];
size_t written = 0;
data, strlen(data), true, buf, sizeof(buf), &written);
printf("Encoded %zu bytes: ", written);
print_escaped((const uint8_t*)buf, written);
printf("\n");
}
}
}
int main() {
fprintf(stderr, "Failed to create terminal\n");
return 1;
}
(const void*)on_write_pty);
(const void*)on_clipboard_read);
printf("Plain paste:\n");
paste_clipboard(terminal, "hello world");
printf("Paste with a newline (refused, then confirmed):\n");
paste_clipboard(terminal, "echo hi\n");
printf("Bracketed paste (mode 2004):\n");
vt_write(terminal, "\x1b[?2004h");
paste_clipboard(terminal, "line one\nline two");
printf("Paste event (mode 5522):\n");
vt_write(terminal, "\x1b[?5522h");
paste_clipboard(terminal, "hello world");
if (event_pw[0] != 0) {
printf("Program reads with the event password:\n");
char read_seq[256];
snprintf(read_seq, sizeof(read_seq),
"\x1b]5522;type=read:pw=%s:name=UGFzdGUgZXZlbnQ=;dGV4dC9wbGFpbg==\x1b\\",
event_pw);
vt_write(terminal, read_seq);
}
printf("IME text with mode 5522 enabled:\n");
{
clipboard_t clipboard = {.text = "committed"};
.size = sizeof(paste),
.mimes = &mime,
.mimes_len = 1,
.reader = {.read = read_clipboard, .userdata = &clipboard},
.allow_unsafe = true,
};
bool written = false;
written) {
printf(" written\n");
}
}
printf("\nTerminal-free building blocks:\n");
safety_example();
encode_example();
return 0;
}
GHOSTTY_API GhosttyResult ghostty_paste_encode(char *data, size_t data_len, bool bracketed, char *buf, size_t buf_len, size_t *out_written)
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_PASTE_SOURCE_CLIPBOARD
@ GHOSTTY_PASTE_SOURCE_TEXT
GHOSTTY_API GhosttyResult ghostty_terminal_set(GhosttyTerminal terminal, GhosttyTerminalOption option, const void *value)
struct GhosttyTerminalImpl * GhosttyTerminal
GHOSTTY_API void ghostty_terminal_free(GhosttyTerminal terminal)
GHOSTTY_API void ghostty_terminal_vt_write(GhosttyTerminal terminal, const uint8_t *data, size_t len)
GHOSTTY_API GhosttyResult ghostty_terminal_new(const GhosttyAllocator *allocator, GhosttyTerminal *terminal, uint16_t cols, uint16_t rows)
@ GHOSTTY_CLIPBOARD_READ_RESULT_SUCCESS
@ GHOSTTY_CLIPBOARD_LOCATION_STANDARD
@ GHOSTTY_TERMINAL_OPT_WRITE_PTY
@ GHOSTTY_TERMINAL_OPT_CLIPBOARD_READ
GhosttyClipboardReadReplyFn reply