nixcfg/overlays/mods/gnome-console-Add-image-and-file-path-pasting-support-for.patch
Harald Hoyer 45cb8db57f feat(gnome-console): add image and file path pasting support
- Implemented detection of clipboard content types (text, files, and images).
- Added support for pasting file paths and temporary image paths.
- Ensured plain text pasting remains compatible with security checks.
2025-07-04 16:47:17 +02:00

224 lines
7.3 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From 366641e9188df09d7bef6c091e916d277ddc37a1 Mon Sep 17 00:00:00 2001
From: Harald Hoyer <harald@hoyer.xyz>
Date: Fri, 4 Jul 2025 14:47:45 +0200
Subject: [PATCH] terminal: Add image and file path pasting support for
Shift+Ctrl+V
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Enhance the paste functionality to detect different clipboard content types:
- File URIs are pasted as shell-quoted file paths
- Images are saved to temporary files and their paths are pasted
- Plain text continues to work as before with security checks
This provides a consistent experience with the drag-and-drop functionality
and allows users to easily paste file paths from file managers or
temporary paths for copied images.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
---
src/kgx-terminal.c | 160 +++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 155 insertions(+), 5 deletions(-)
diff --git a/src/kgx-terminal.c b/src/kgx-terminal.c
index fffbb92..fd9f936 100644
--- a/src/kgx-terminal.c
+++ b/src/kgx-terminal.c
@@ -28,10 +28,13 @@
#include "kgx-config.h"
#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <unistd.h>
#include <vte/vte.h>
#define PCRE2_CODE_UNIT_WIDTH 0
#include <pcre2.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
#include "kgx-despatcher.h"
#include "kgx-marshals.h"
@@ -41,6 +44,16 @@
#include "kgx-terminal.h"
+/* Translators: The user ctrl-clicked, or used Open Link, on a URI that,
+ * for whatever reason, we were unable to open. */
+#define URI_FAILED_MESSAGE C_("toast-message", "Couldn't Open Link")
+
+/* MIME types for clipboard content detection */
+#define MIME_TEXT_URI_LIST "text/uri-list"
+#define MIME_TEXT_PLAIN "text/plain;charset=utf-8"
+#define MIME_PORTAL_FILES "application/vnd.portal.filetransfer"
+#define MIME_PORTAL_FILES_OLD "application/vnd.portal.files"
+
/* Regex adapted from TerminalWidget.vala in Pantheon Terminal */
#define USERCHARS "-[:alnum:]"
@@ -365,6 +378,110 @@ copy_activated (KgxTerminal *self)
}
+static char *
+quote_file_path (GFile *file)
+{
+ g_autofree char *path = g_file_get_path (file);
+
+ if (G_LIKELY (path)) {
+ return g_shell_quote (path);
+ } else {
+ /* Fall back to URI if path extraction fails */
+ g_autofree char *uri = g_file_get_uri (file);
+ return g_strdup (uri);
+ }
+}
+
+
+static void
+got_file_list (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr (KgxTerminal) self = user_data;
+ g_autoptr (GError) error = NULL;
+ g_autoptr (GStrvBuilder) builder = NULL;
+ g_auto (GStrv) items = NULL;
+ g_autofree char *text = NULL;
+ const GValue *value;
+ GSList *files;
+
+ /* Get the resulting file list */
+ value = gdk_clipboard_read_value_finish (GDK_CLIPBOARD (source), result, &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ return;
+ } else if (error) {
+ g_critical ("Couldn't paste files: %s\n", error->message);
+ return;
+ }
+
+ builder = g_strv_builder_new ();
+ files = g_value_get_boxed (value);
+
+ for (GSList *l = files; l; l = g_slist_next (l)) {
+ g_autofree char *quoted = quote_file_path (G_FILE (l->data));
+ g_strv_builder_add (builder, quoted);
+ }
+
+ /* Add empty string to facilitate consecutive pastes */
+ g_strv_builder_add (builder, "");
+ items = g_strv_builder_end (builder);
+ text = g_strjoinv (" ", items);
+
+ kgx_terminal_accept_paste (self, text);
+}
+
+
+static void
+got_image (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr (KgxTerminal) self = user_data;
+ g_autoptr (GError) error = NULL;
+ g_autoptr (GdkTexture) texture = NULL;
+ g_autofree char *text = NULL;
+ g_autofree char *temp_path = NULL;
+ g_autofree char *quoted_path = NULL;
+ GBytes *bytes;
+ int fd;
+
+ /* Get the resulting texture */
+ texture = gdk_clipboard_read_texture_finish (GDK_CLIPBOARD (source), result, &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ return;
+ } else if (error) {
+ g_critical ("Couldn't paste image: %s\n", error->message);
+ return;
+ }
+
+ /* Create a temporary file for the image */
+ fd = g_file_open_tmp ("kgx-clipboard-XXXXXX.png", &temp_path, &error);
+ if (fd == -1) {
+ g_critical ("Failed to create temporary file for clipboard image: %s\n", error->message);
+ return;
+ }
+ close (fd);
+
+ /* Save texture data directly */
+ bytes = gdk_texture_save_to_png_bytes (texture);
+ if (!g_file_set_contents (temp_path, g_bytes_get_data (bytes, NULL),
+ g_bytes_get_size (bytes), &error)) {
+ g_bytes_unref (bytes);
+ g_unlink (temp_path);
+ g_critical ("Failed to save clipboard image to temporary file: %s\n", error->message);
+ return;
+ }
+ g_bytes_unref (bytes);
+
+ /* Quote the path and paste it */
+ quoted_path = g_shell_quote (temp_path);
+ kgx_terminal_accept_paste (self, quoted_path);
+}
+
+
static void
got_text (GObject *source,
GAsyncResult *result,
@@ -392,11 +509,44 @@ static void
paste_activated (KgxTerminal *self)
{
GdkClipboard *cb = gtk_widget_get_clipboard (GTK_WIDGET (self));
-
- gdk_clipboard_read_text_async (cb,
- self->cancellable,
- got_text,
- g_object_ref (self));
+ GdkContentFormats *formats = gdk_clipboard_get_formats (cb);
+ const char *const *mimes = gdk_content_formats_get_mime_types (formats, NULL);
+
+ /* Check clipboard content types in priority order */
+ if (G_LIKELY (g_strv_contains (mimes, MIME_PORTAL_FILES)) ||
+ g_strv_contains (mimes, MIME_PORTAL_FILES_OLD)) {
+ /* File list from modern applications */
+ gdk_clipboard_read_value_async (cb,
+ GDK_TYPE_FILE_LIST,
+ G_PRIORITY_DEFAULT,
+ self->cancellable,
+ got_file_list,
+ g_object_ref (self));
+ } else if (g_strv_contains (mimes, MIME_TEXT_URI_LIST)) {
+ /* URI list from older applications */
+ gdk_clipboard_read_value_async (cb,
+ GDK_TYPE_FILE_LIST,
+ G_PRIORITY_DEFAULT,
+ self->cancellable,
+ got_file_list,
+ g_object_ref (self));
+ } else if (g_strv_contains (mimes, "image/png") ||
+ g_strv_contains (mimes, "image/jpeg") ||
+ g_strv_contains (mimes, "image/gif") ||
+ g_strv_contains (mimes, "image/bmp") ||
+ g_strv_contains (mimes, "image/webp")) {
+ /* Image data - save to temp file */
+ gdk_clipboard_read_texture_async (cb,
+ self->cancellable,
+ got_image,
+ g_object_ref (self));
+ } else {
+ /* Default: try to read as text */
+ gdk_clipboard_read_text_async (cb,
+ self->cancellable,
+ got_text,
+ g_object_ref (self));
+ }
}
--
2.49.0