summaryrefslogtreecommitdiff
path: root/gdk/x11/gdkclipboard-x11.c
diff options
context:
space:
mode:
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);
}