diff options
author | Benjamin Otte <otte@redhat.com> | 2017-11-23 01:59:19 +0100 |
---|---|---|
committer | Benjamin Otte <otte@redhat.com> | 2017-12-03 05:46:46 +0100 |
commit | 9b78d76873c075aaac0e5d62382bfb1cd1ba065e (patch) | |
tree | ec470bb511cb56a35d07c87bba621ce25989c62f /gdk/x11/gdkclipboard-x11.c | |
parent | 88684baecf164875f5947a9eff40f567aa172c67 (diff) | |
download | gtk+-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.c | 233 |
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); } |