mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-07-12 04:09:40 +00:00
terminal: add clipboard_set effect for OSC 52 clipboard writes (#13182)
libghostty-vt was already parsing OSC 52 into the clipboard_contents action but the stream handler dropped, so there was no way to observe clipboard writes (in this case, a program using go-libghostty). This adds a clipboard_set effect following the existing bell/title_changed pattern and expose it through the C API as `GHOSTTY_TERMINAL_OPT_CLIPBOARD_SET`. The callback receives the OSC 52 kind byte and the base64 payload exactly as received; decoding and kind interpretation are left to the embedder, matching how decoding is typically deferred. Note this intentionally does not deal with clipboard read requests given the security implications. AI disclosure: Fable (via Claude Code) did the majority of the work here, I validated it on the client side and fully understand the pattern we're fitting into.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user