summaryrefslogtreecommitdiff
path: root/gdk/x11/gdkclipboard-x11.c
diff options
context:
space:
mode:
authorBenjamin Otte <otte@redhat.com>2017-11-23 01:59:19 +0100
committerBenjamin Otte <otte@redhat.com>2017-12-03 05:46:46 +0100
commit9b78d76873c075aaac0e5d62382bfb1cd1ba065e (patch)
treeec470bb511cb56a35d07c87bba621ce25989c62f /gdk/x11/gdkclipboard-x11.c
parent88684baecf164875f5947a9eff40f567aa172c67 (diff)
downloadgtk+-9b78d76873c075aaac0e5d62382bfb1cd1ba065e.tar.gz
x11: Improve fallbacks for text
(1) Try all passed in formats in order if one of them fails. (2) Don't blindly accept all formats, make sure they are mime types (3) Add a bunch of special non-mime types that plug converters to get to mime types
Diffstat (limited to 'gdk/x11/gdkclipboard-x11.c')
-rw-r--r--gdk/x11/gdkclipboard-x11.c233
1 files changed, 204 insertions, 29 deletions
diff --git a/gdk/x11/gdkclipboard-x11.c b/gdk/x11/gdkclipboard-x11.c
index a88c86d1ce..aef23b7ee3 100644
--- a/gdk/x11/gdkclipboard-x11.c
+++ b/gdk/x11/gdkclipboard-x11.c
@@ -19,8 +19,11 @@
#include "gdkclipboardprivate.h"
#include "gdkclipboard-x11.h"
-#include "gdkselectioninputstream-x11.h"
+
+#include "gdkintl.h"
#include "gdkprivate-x11.h"
+#include "gdkselectioninputstream-x11.h"
+#include "gdktextlistconverter-x11.h"
#include <string.h>
#include <X11/Xatom.h>
@@ -57,6 +60,121 @@ G_DEFINE_TYPE (GdkX11Clipboard, gdk_x11_clipboard, GDK_TYPE_CLIPBOARD)
? XMaxRequestSize (GDK_DISPLAY_XDISPLAY (display)) - 100 \
: XExtendedMaxRequestSize (GDK_DISPLAY_XDISPLAY (display)) - 100)
+static GInputStream *
+text_list_convert (GdkX11Clipboard *cb,
+ GInputStream *stream,
+ const char *encoding,
+ int format)
+{
+ GInputStream *converter_stream;
+ GConverter *converter;
+
+ converter = gdk_x11_text_list_converter_to_utf8_new (gdk_clipboard_get_display (GDK_CLIPBOARD (cb)),
+ encoding,
+ format);
+ converter_stream = g_converter_input_stream_new (stream, converter);
+
+ g_object_unref (converter);
+ g_object_unref (stream);
+
+ return converter_stream;
+}
+
+static GInputStream *
+no_convert (GdkX11Clipboard *cb,
+ GInputStream *stream,
+ const char *encoding,
+ int format)
+{
+ return stream;
+}
+
+static const struct {
+ const char *x_target;
+ const char *mime_type;
+ GInputStream * (* convert) (GdkX11Clipboard *, GInputStream *, const char *, int);
+} special_targets[] = {
+ { "UTF8_STRING", "text/plain;charset=utf-8", no_convert },
+ { "COMPOUND_TEXT", "text/plain;charset=utf-8", text_list_convert },
+ { "TEXT", "text/plain;charset=utf-8", text_list_convert },
+ { "STRING", "text/plain;charset=utf-8", text_list_convert }
+};
+
+static void
+print_atoms (GdkX11Clipboard *cb,
+ const char *prefix,
+ const Atom *atoms,
+ gsize n_atoms)
+{
+ GDK_NOTE(CLIPBOARD,
+ GdkDisplay *display = gdk_clipboard_get_display (GDK_CLIPBOARD (cb));
+ gsize i;
+
+ g_printerr ("%s: %s [ ", cb->selection, prefix);
+ for (i = 0; i < n_atoms; i++)
+ {
+ g_printerr ("%s%s", i > 0 ? ", " : "", gdk_x11_get_xatom_name_for_display (display , atoms[i]));
+ }
+ g_printerr (" ]\n");
+ );
+}
+
+static GSList *
+gdk_x11_clipboard_formats_to_targets (GdkContentFormats *formats)
+{
+ GSList *targets;
+ const char * const *mime_types;
+ gsize i, j, n_mime_types;
+
+ targets = NULL;
+ mime_types = gdk_content_formats_get_mime_types (formats, &n_mime_types);
+
+ for (i = 0; i < n_mime_types; i++)
+ {
+ for (j = 0; j < G_N_ELEMENTS (special_targets); j++)
+ {
+ if (g_str_equal (mime_types[i], special_targets[j].mime_type))
+ targets = g_slist_prepend (targets, (gpointer) g_intern_string (special_targets[i].x_target));
+ }
+ targets = g_slist_prepend (targets, (gpointer) mime_types[i]);
+ }
+
+ return g_slist_reverse (targets);
+}
+
+static GdkContentFormats *
+gdk_x11_clipboard_formats_from_atoms (GdkDisplay *display,
+ const Atom *atoms,
+ gsize n_atoms)
+{
+ GdkContentFormatsBuilder *builder;
+ gsize i, j;
+
+ builder = gdk_content_formats_builder_new ();
+ for (i = 0; i < n_atoms; i++)
+ {
+ const char *name;
+
+ name = gdk_x11_get_xatom_name_for_display (display , atoms[i]);
+ if (strchr (name, '/'))
+ {
+ gdk_content_formats_builder_add_mime_type (builder, name);
+ continue;
+ }
+
+ for (j = 0; j < G_N_ELEMENTS (special_targets); j++)
+ {
+ if (g_str_equal (name, special_targets[j].x_target))
+ {
+ gdk_content_formats_builder_add_mime_type (builder, special_targets[j].mime_type);
+ break;
+ }
+ }
+ }
+
+ return gdk_content_formats_builder_free (builder);
+}
+
static void
gdk_x11_clipboard_request_targets_finish (GObject *source_object,
GAsyncResult *res,
@@ -66,11 +184,8 @@ gdk_x11_clipboard_request_targets_finish (GObject *source_object,
GdkX11Clipboard *cb = user_data;
GdkDisplay *display;
GdkContentFormats *formats;
- GdkContentFormatsBuilder *builder;
GBytes *bytes;
GError *error = NULL;
- const Atom *atoms;
- guint i, n_atoms;
bytes = g_input_stream_read_bytes_finish (stream, res, &error);
if (bytes == NULL)
@@ -88,17 +203,15 @@ gdk_x11_clipboard_request_targets_finish (GObject *source_object,
return;
}
- display = gdk_clipboard_get_display (GDK_CLIPBOARD (cb));
+ print_atoms (cb,
+ "received targets",
+ g_bytes_get_data (bytes, NULL),
+ g_bytes_get_size (bytes) / sizeof (Atom));
- atoms = g_bytes_get_data (bytes, NULL);
- n_atoms = g_bytes_get_size (bytes) / sizeof (Atom);
- builder = gdk_content_formats_builder_new ();
- for (i = 0; i < n_atoms; i++)
- {
- gdk_content_formats_builder_add_mime_type (builder, gdk_x11_get_xatom_name_for_display (display , atoms[i]));
- }
- gdk_content_formats_builder_add_formats (builder, gdk_clipboard_get_formats (GDK_CLIPBOARD (cb)));
- formats = gdk_content_formats_builder_free (builder);
+ display = gdk_clipboard_get_display (GDK_CLIPBOARD (cb));
+ formats = gdk_x11_clipboard_formats_from_atoms (display,
+ g_bytes_get_data (bytes, NULL),
+ g_bytes_get_size (bytes) / sizeof (Atom));
GDK_NOTE(CLIPBOARD, char *s = gdk_content_formats_to_string (formats); g_printerr ("%s: got formats: %s\n", cb->selection, s); g_free (s));
/* union with previously loaded formats */
@@ -122,13 +235,22 @@ gdk_x11_clipboard_request_targets_got_stream (GObject *source,
GInputStream *stream;
GdkDisplay *display;
GError *error = NULL;
+ const char *type;
+ int format;
- stream = gdk_x11_selection_input_stream_new_finish (result, &error);
+ stream = gdk_x11_selection_input_stream_new_finish (result, &type, &format, &error);
if (stream == NULL)
{
g_object_unref (cb);
return;
}
+ else if (!g_str_equal (type, "ATOM") || format != 32)
+ {
+ g_input_stream_close (stream, NULL, NULL);
+ g_object_unref (stream);
+ g_object_unref (cb);
+ return;
+ }
display = gdk_clipboard_get_display (GDK_CLIPBOARD (cb));
@@ -213,13 +335,61 @@ gdk_x11_clipboard_read_got_stream (GObject *source,
GTask *task = data;
GError *error = NULL;
GInputStream *stream;
+ const char *type;
+ int format;
- stream = gdk_x11_selection_input_stream_new_finish (res, &error);
- /* XXX: We could try more types here */
+ stream = gdk_x11_selection_input_stream_new_finish (res, &type, &format, &error);
if (stream == NULL)
- g_task_return_error (task, error);
+ {
+ GSList *targets, *next;
+
+ targets = g_task_get_task_data (task);
+ next = targets->next;
+ if (next)
+ {
+ GdkX11Clipboard *cb = GDK_X11_CLIPBOARD (g_task_get_source_object (task));
+
+ GDK_NOTE(CLIPBOARD, g_printerr ("%s: reading %s failed, trying %s next\n",
+ cb->selection, (char *) targets->data, (char *) next->data));
+ targets->next = NULL;
+ g_task_set_task_data (task, next, (GDestroyNotify) g_slist_free);
+ gdk_x11_selection_input_stream_new_async (gdk_clipboard_get_display (GDK_CLIPBOARD (cb)),
+ cb->selection,
+ next->data,
+ cb->timestamp,
+ g_task_get_priority (task),
+ g_task_get_cancellable (task),
+ gdk_x11_clipboard_read_got_stream,
+ task);
+ g_error_free (error);
+ return;
+ }
+
+ g_task_return_error (task, error);
+ }
else
- g_task_return_pointer (task, stream, g_object_unref);
+ {
+ GdkX11Clipboard *cb = GDK_X11_CLIPBOARD (g_task_get_source_object (task));
+ const char *mime_type = ((GSList *) g_task_get_task_data (task))->data;
+ gsize i;
+
+ for (i = 0; i < G_N_ELEMENTS (special_targets); i++)
+ {
+ if (g_str_equal (mime_type, special_targets[i].x_target))
+ {
+ GDK_NOTE(CLIPBOARD, g_printerr ("%s: reading with converter from %s to %s\n",
+ cb->selection, mime_type, special_targets[i].mime_type));
+ mime_type = g_intern_string (special_targets[i].mime_type);
+ g_task_set_task_data (task, g_slist_prepend (NULL, (gpointer) mime_type), (GDestroyNotify) g_slist_free);
+ stream = special_targets[i].convert (cb, stream, type, format);
+ break;
+ }
+ }
+
+ GDK_NOTE(CLIPBOARD, g_printerr ("%s: reading clipboard as %s now\n",
+ cb->selection, mime_type));
+ g_task_return_pointer (task, stream, g_object_unref);
+ }
g_object_unref (task);
}
@@ -233,20 +403,27 @@ gdk_x11_clipboard_read_async (GdkClipboard *clipboard,
gpointer user_data)
{
GdkX11Clipboard *cb = GDK_X11_CLIPBOARD (clipboard);
- const char * const *mime_types;
+ GSList *targets;
GTask *task;
task = g_task_new (clipboard, cancellable, callback, user_data);
g_task_set_priority (task, io_priority);
g_task_set_source_tag (task, gdk_x11_clipboard_read_async);
- g_task_set_task_data (task, gdk_content_formats_ref (formats), (GDestroyNotify) gdk_content_formats_unref);
- /* XXX: Sort differently? */
- mime_types = gdk_content_formats_get_mime_types (formats, NULL);
+ targets = gdk_x11_clipboard_formats_to_targets (formats);
+ g_task_set_task_data (task, targets, (GDestroyNotify) g_slist_free);
+ if (targets == NULL)
+ {
+ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("No compatible transfer format found"));
+ return;
+ }
+ GDK_NOTE(CLIPBOARD, g_printerr ("%s: new read for %s (%u other options)\n",
+ cb->selection, (char *) targets->data, g_slist_length (targets->next)));
gdk_x11_selection_input_stream_new_async (gdk_clipboard_get_display (GDK_CLIPBOARD (cb)),
cb->selection,
- mime_types[0],
+ targets->data,
cb->timestamp,
io_priority,
cancellable,
@@ -273,12 +450,10 @@ gdk_x11_clipboard_read_finish (GdkClipboard *clipboard,
{
if (out_mime_type)
{
- GdkContentFormats *formats;
- const char * const *mime_types;
+ GSList *targets;
- formats = g_task_get_task_data (task);
- mime_types = gdk_content_formats_get_mime_types (formats, NULL);
- *out_mime_type = mime_types[0];
+ targets = g_task_get_task_data (task);
+ *out_mime_type = targets->data;
}
g_object_ref (stream);
}