diff --git a/src/marshal.list b/src/marshal.list index 241128c3..a40acc49 100644 --- a/src/marshal.list +++ b/src/marshal.list @@ -1,3 +1,4 @@ VOID:STRING,BOXED VOID:STRING,UINT +VOID:STRING,POINTER,UINT VOID:UINT,UINT diff --git a/src/vte.cc b/src/vte.cc index 335a8a07..87b99e8b 100644 --- a/src/vte.cc +++ b/src/vte.cc @@ -961,6 +961,13 @@ Terminal::emit_paste_clipboard() g_signal_emit(m_terminal, signals[SIGNAL_PASTE_CLIPBOARD], 0); } +void +Terminal::emit_write_clipboard(const char *clipboards, const guchar *text, guint text_size) +{ + _vte_debug_print(VTE_DEBUG_SIGNALS, "Emitting 'write-clipboard'.\n"); + g_signal_emit(m_terminal, signals[SIGNAL_WRITE_CLIPBOARD], 0, clipboards, text, text_size); +} + /* Emit a "hyperlink_hover_uri_changed" signal. */ void Terminal::emit_hyperlink_hover_uri_changed(const GdkRectangle *bbox) diff --git a/src/vte/vteterminal.h b/src/vte/vteterminal.h index a9e1e494..ad73ce2f 100644 --- a/src/vte/vteterminal.h +++ b/src/vte/vteterminal.h @@ -122,13 +122,18 @@ struct _VteTerminalClass { void (*setup_context_menu)(VteTerminal* terminal, VteEventContext const* context); + void (*write_clipboard)(VteTerminal* terminal, + const char *clipboards, + const char *text, + guint text_size); + /* Add new vfuncs just above, and subtract from the padding below. */ /* Padding for future expansion. */ #if _VTE_GTK == 3 - gpointer _padding[12]; + gpointer _padding[11]; #elif _VTE_GTK == 4 - gpointer _padding[15]; + gpointer _padding[14]; #endif /* _VTE_GTK */ // FIXMEgtk4 use class private data instead diff --git a/src/vtegtk.cc b/src/vtegtk.cc index 521f7450..bc7d5f3a 100644 --- a/src/vtegtk.cc +++ b/src/vtegtk.cc @@ -1383,6 +1383,8 @@ vte_terminal_class_init(VteTerminalClass *klass) klass->bell = NULL; + klass->write_clipboard = NULL; + /* GtkScrollable interface properties */ g_object_class_override_property (gobject_class, PROP_HADJUSTMENT, "hadjustment"); g_object_class_override_property (gobject_class, PROP_VADJUSTMENT, "vadjustment"); @@ -2053,6 +2055,45 @@ vte_terminal_class_init(VteTerminalClass *klass) G_OBJECT_CLASS_TYPE(klass), g_cclosure_marshal_VOID__POINTERv); + /** + * VteTerminal::write-clipboard: + * @vteterminal: the object which received the signal + * @clipboards: a string describing which clipboard(s) to set + * @text (nullable) (array length=text_size) (element-type guint8): + * a string that should be written to the clipboard + * @text_size: the length of that string + * + * Emitted when the terminal receives an OSC 52 (write clipboard) ESC + * sequence. + * + * The clipboards argument contains 0 or more characters from + * the set [cpqs0-7]. The letters represent clipboard, primary, + * secondary and select respectively; the numbers represent cut-buffers. + * + * The text argument is decoded from the base 64. It is unsanitized and + * may or may not include a 0 terminator, so always check text_size. + * + * If text is NULL or empty, the clipboard should be cleared. Attempts + * to query the clipboard (child sent "?" instead of base64) are ignored. + * + * Note: vte has a limit of 4096 characters in control sequences, + * including encoding overhead, so the amount of text you can copy at a + * time is somewhat limited. + */ + signals[SIGNAL_WRITE_CLIPBOARD] = + g_signal_new(I_("write-clipboard"), + G_OBJECT_CLASS_TYPE(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(VteTerminalClass, write_clipboard), + NULL, + NULL, + _vte_marshal_VOID__STRING_POINTER_UINT, + G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT); + g_signal_set_va_marshaller(signals[SIGNAL_WRITE_CLIPBOARD], + G_OBJECT_CLASS_TYPE(klass), + _vte_marshal_VOID__STRING_POINTER_UINTv); + + /** * VteTerminal:allow-bold: * diff --git a/src/vtegtk.hh b/src/vtegtk.hh index 1d1383af..1551d558 100644 --- a/src/vtegtk.hh +++ b/src/vtegtk.hh @@ -54,6 +54,7 @@ enum { SIGNAL_SELECTION_CHANGED, SIGNAL_SETUP_CONTEXT_MENU, SIGNAL_WINDOW_TITLE_CHANGED, + SIGNAL_WRITE_CLIPBOARD, LAST_SIGNAL }; extern guint signals[LAST_SIGNAL]; diff --git a/src/vteinternal.hh b/src/vteinternal.hh index 90f45197..7d7caaa6 100644 --- a/src/vteinternal.hh +++ b/src/vteinternal.hh @@ -1345,6 +1345,9 @@ public: guint rows); void emit_copy_clipboard(); void emit_paste_clipboard(); + + void emit_write_clipboard(const char *clipboards, const guchar *text, guint text_size); + void emit_hyperlink_hover_uri_changed(const GdkRectangle *bbox); void hyperlink_invalidate_and_get_bbox(vte::base::Ring::hyperlink_idx_t idx, GdkRectangle *bbox); diff --git a/src/vteseq.cc b/src/vteseq.cc index 3c552dc6..48f0607e 100644 --- a/src/vteseq.cc +++ b/src/vteseq.cc @@ -6678,6 +6678,24 @@ Terminal::OSC(vte::parser::Sequence const& seq) reset_color(VTE_HIGHLIGHT_FG, VTE_COLOR_SOURCE_ESCAPE); break; + case VTE_OSC_XTERM_SET_XSELECTION: { + auto clipboards = *it; + if (clipboards.find_first_not_of("cpqs01234567") != std::string::npos) + break; + auto payload = *++it; + guchar *decoded = NULL; + gsize decoded_len = 0; + if (payload.size()) + { + if (payload[0] == '?') + break; + decoded = g_base64_decode(payload.c_str(), &decoded_len); + } + emit_write_clipboard(clipboards.c_str(), decoded, decoded_len); + g_free(decoded); + break; + } + case VTE_OSC_XTERM_SET_ICON_TITLE: case VTE_OSC_XTERM_SET_XPROPERTY: case VTE_OSC_XTERM_SET_COLOR_MOUSE_CURSOR_FG: @@ -6688,7 +6706,6 @@ Terminal::OSC(vte::parser::Sequence const& seq) case VTE_OSC_XTERM_SET_CURSOR_NAME: case VTE_OSC_XTERM_LOGFILE: case VTE_OSC_XTERM_SET_FONT: - case VTE_OSC_XTERM_SET_XSELECTION: case VTE_OSC_XTERM_SET_COLOR_MODE: case VTE_OSC_XTERM_RESET_COLOR_MOUSE_CURSOR_FG: case VTE_OSC_XTERM_RESET_COLOR_MOUSE_CURSOR_BG: