terminal: make clipboard writes protocol neutral

#13182

Replace the OSC 52-specific kind and encoded payload callback with an
atomic clipboard write containing a normalized destination and decoded
MIME representations. This keeps protocol details out of embedders and
lets iTerm2 Copy use the same semantic path.

Represent clears with an empty content list, preserve binary payloads,
and return a generic result for protocols that acknowledge writes. Add
the C ABI descriptors, layout metadata, and effects example so future
multipart protocols can reuse the callback without another API break.
This commit is contained in:
Mitchell Hashimoto
2026-07-10 09:17:26 -07:00
parent 0a410f18e5
commit 634ef71986
8 changed files with 582 additions and 118 deletions

View File

@@ -1,9 +1,13 @@
# Example: `ghostty-vt` Terminal Effects
This contains a simple example of how to register and use terminal
effect callbacks (`write_pty`, `bell`, `title_changed`) with the
effect callbacks (`write_pty`, `bell`, `title_changed`, and
`clipboard_write`) with the
`ghostty-vt` C library.
The clipboard effect receives one atomic write with decoded, binary-safe
MIME representations rather than protocol-specific OSC 52 data.
This uses a `build.zig` and `Zig` to build the C program so that we
can reuse a lot of our build logic and depend directly on our source
tree, but Ghostty emits a standard C library that can be used with any

View File

@@ -39,6 +39,37 @@ void on_title_changed(GhosttyTerminal terminal, void* userdata) {
}
//! [effects-title-changed]
//! [effects-clipboard-write]
GhosttyClipboardWriteResult on_clipboard_write(
GhosttyTerminal terminal,
void* userdata,
const GhosttyClipboardWrite* write) {
(void)terminal;
(void)userdata;
printf(" clipboard write (location=%d, contents=%zu)\n",
(int)write->location, write->contents_len);
if (write->contents_len == 0) {
printf(" clear\n");
}
for (size_t i = 0; i < write->contents_len; i++) {
const GhosttyClipboardContent* content = &write->contents[i];
printf(" ");
if (content->mime.len > 0) {
fwrite(content->mime.ptr, 1, content->mime.len, stdout);
}
printf(" (%zu bytes): ", content->data.len);
if (content->data.len > 0) {
fwrite(content->data.ptr, 1, content->data.len, stdout);
}
printf("\n");
}
return GHOSTTY_CLIPBOARD_WRITE_RESULT_SUCCESS;
}
//! [effects-clipboard-write]
//! [effects-register]
int main() {
// Create a terminal
@@ -64,6 +95,8 @@ int main() {
(const void *)on_bell);
ghostty_terminal_set(terminal, GHOSTTY_TERMINAL_OPT_TITLE_CHANGED,
(const void *)on_title_changed);
ghostty_terminal_set(terminal, GHOSTTY_TERMINAL_OPT_CLIPBOARD_WRITE,
(const void *)on_clipboard_write);
// Feed VT data that triggers effects:
@@ -85,7 +118,14 @@ int main() {
ghostty_terminal_vt_write(terminal, (const uint8_t*)decrqm,
strlen(decrqm));
// 4. Another bell to show the counter increments
// 4. Clipboard write (OSC 52 ; c ; <base64 data> ST)
printf("Sending clipboard write:\n");
const char* clipboard_seq =
"\x1B]52;c;SGVsbG8gY2xpcGJvYXJk\x1B\\";
ghostty_terminal_vt_write(terminal, (const uint8_t*)clipboard_seq,
strlen(clipboard_seq));
// 5. Another bell to show the counter increments
printf("Sending another BEL:\n");
ghostty_terminal_vt_write(terminal, &bel, 1);