diff options
Diffstat (limited to 'gdk')
-rw-r--r-- | gdk/x11/gdkclipboard-x11.c | 233 | ||||
-rw-r--r-- | gdk/x11/gdkselectioninputstream-x11.c | 28 | ||||
-rw-r--r-- | gdk/x11/gdkselectioninputstream-x11.h | 2 | ||||
-rw-r--r-- | gdk/x11/gdktextlistconverter-x11.c | 169 | ||||
-rw-r--r-- | gdk/x11/gdktextlistconverter-x11.h | 44 | ||||
-rw-r--r-- | gdk/x11/meson.build | 1 |
6 files changed, 444 insertions, 33 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); } diff --git a/gdk/x11/gdkselectioninputstream-x11.c b/gdk/x11/gdkselectioninputstream-x11.c index a694d05cb7..9ba704d6d0 100644 --- a/gdk/x11/gdkselectioninputstream-x11.c +++ b/gdk/x11/gdkselectioninputstream-x11.c @@ -40,6 +40,9 @@ struct GdkX11SelectionInputStreamPrivate { Atom xtarget; char *property; Atom xproperty; + const char *type; + Atom xtype; + int format; GTask *pending_task; guchar *pending_data; @@ -150,6 +153,8 @@ gdk_x11_selection_input_stream_complete (GdkX11SelectionInputStream *stream) if (priv->complete) return; + GDK_NOTE(SELECTION, g_printerr ("%s:%s: transfer complete\n", + priv->selection, priv->target)); priv->complete = TRUE; g_async_queue_push (priv->chunks, g_bytes_new (NULL, 0)); @@ -457,7 +462,8 @@ gdk_x11_selection_input_stream_filter_event (GdkXEvent *xev, } else { - bytes = get_selection_property (xdisplay, xwindow, xevent->xselection.property, &type, &format); + bytes = get_selection_property (xdisplay, xwindow, xevent->xselection.property, &priv->xtype, &priv->format); + priv->type = gdk_x11_get_xatom_name_for_display (priv->display, priv->xtype); g_task_return_pointer (task, g_object_ref (stream), g_object_unref); @@ -477,6 +483,9 @@ gdk_x11_selection_input_stream_filter_event (GdkXEvent *xev, } else { + GDK_NOTE(SELECTION, g_printerr ("%s:%s: reading %zu bytes\n", + priv->selection, priv->target, + g_bytes_get_size (bytes))); g_async_queue_push (priv->chunks, bytes); gdk_x11_selection_input_stream_complete (stream); @@ -536,9 +545,11 @@ gdk_x11_selection_input_stream_new_async (GdkDisplay *display, GInputStream * gdk_x11_selection_input_stream_new_finish (GAsyncResult *result, + const char **type, + int *format, GError **error) { - GInputStream *stream; + GdkX11SelectionInputStream *stream; GTask *task; g_return_val_if_fail (g_task_is_valid (result, NULL), NULL); @@ -547,7 +558,16 @@ gdk_x11_selection_input_stream_new_finish (GAsyncResult *result, stream = g_task_propagate_pointer (task, error); if (stream) - g_object_ref (stream); - return stream; + { + GdkX11SelectionInputStreamPrivate *priv = gdk_x11_selection_input_stream_get_instance_private (stream); + + if (type) + *type = priv->type; + if (format) + *format = priv->format; + g_object_ref (stream); + } + + return G_INPUT_STREAM (stream); } diff --git a/gdk/x11/gdkselectioninputstream-x11.h b/gdk/x11/gdkselectioninputstream-x11.h index c8bd19d55e..18c0763025 100644 --- a/gdk/x11/gdkselectioninputstream-x11.h +++ b/gdk/x11/gdkselectioninputstream-x11.h @@ -59,6 +59,8 @@ void gdk_x11_selection_input_stream_new_async (GdkDisplay GAsyncReadyCallback callback, gpointer user_data); GInputStream * gdk_x11_selection_input_stream_new_finish (GAsyncResult *result, + const char **type, + int *format, GError **error); diff --git a/gdk/x11/gdktextlistconverter-x11.c b/gdk/x11/gdktextlistconverter-x11.c new file mode 100644 index 0000000000..7fe56d1734 --- /dev/null +++ b/gdk/x11/gdktextlistconverter-x11.c @@ -0,0 +1,169 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2017 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Benjamin Otte <otte@gnome.org> + */ + +#include "config.h" + +#include "gdktextlistconverter-x11.h" + +#include "gdkintl.h" +#include "gdkprivate-x11.h" + +#define GDK_X11_TEXT_LIST_CONVERTER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDK_TYPE_X11_TEXT_LIST_CONVERTER, GdkX11TextListConverterClass)) +#define GDK_IS_X11_TEXT_LIST_CONVERTER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDK_TYPE_X11_TEXT_LIST_CONVERTER)) +#define GDK_X11_TEXT_LIST_CONVERTER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDK_TYPE_X11_TEXT_LIST_CONVERTER, GdkX11TextListConverterClass)) + +typedef struct _GdkX11TextListConverterClass GdkX11TextListConverterClass; + +struct _GdkX11TextListConverter +{ + GObject parent_instance; + + GdkDisplay *display; + + const char *encoding; /* interned */ + gint format; +}; + +struct _GdkX11TextListConverterClass +{ + GObjectClass parent_class; +}; + +static GConverterResult +gdk_x11_text_list_converter_convert (GConverter *converter, + const void *inbuf, + gsize inbuf_size, + void *outbuf, + gsize outbuf_size, + GConverterFlags flags, + gsize *bytes_read, + gsize *bytes_written, + GError **error) +{ + GdkX11TextListConverter *conv = GDK_X11_TEXT_LIST_CONVERTER (converter); + gint count; + char **list; + + if (!(flags & G_CONVERTER_INPUT_AT_END)) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT, + _("Need complete input to do conversion")); + return G_CONVERTER_ERROR; + } + + count = _gdk_x11_display_text_property_to_utf8_list (conv->display, + conv->encoding, + conv->format, + inbuf, + inbuf_size, + &list); + if (count < 0) + { + /* XXX: add error handling from gdkselection-x11.c */ + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE, + _("Not enough space in destination")); + return G_CONVERTER_ERROR; + } + else if (count == 0) + { + if (outbuf_size < 1) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE, + _("Not enough space in destination")); + return G_CONVERTER_ERROR; + } + ((gchar *) outbuf)[0] = 0; + *bytes_read = inbuf_size; + *bytes_written = 1; + return G_CONVERTER_FINISHED; + } + else + { + gsize len = strlen (list[0]) + 1; + + if (outbuf_size < len) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE, + _("Not enough space in destination")); + return G_CONVERTER_ERROR; + } + memcpy (outbuf, list[0], len); + g_strfreev (list); + *bytes_read = inbuf_size; + *bytes_written = len; + return G_CONVERTER_FINISHED; + } +} + +static void +gdk_x11_text_list_converter_reset (GConverter *converter) +{ +} + +static void +gdk_x11_text_list_converter_iface_init (GConverterIface *iface) +{ + iface->convert = gdk_x11_text_list_converter_convert; + iface->reset = gdk_x11_text_list_converter_reset; +} + +G_DEFINE_TYPE_WITH_CODE (GdkX11TextListConverter, gdk_x11_text_list_converter, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_CONVERTER, + gdk_x11_text_list_converter_iface_init)) + +static void +gdk_x11_text_list_converter_finalize (GObject *object) +{ + GdkX11TextListConverter *conv = GDK_X11_TEXT_LIST_CONVERTER (object); + + g_object_unref (conv->display); + + G_OBJECT_CLASS (gdk_x11_text_list_converter_parent_class)->finalize (object); +} + +static void +gdk_x11_text_list_converter_class_init (GdkX11TextListConverterClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gdk_x11_text_list_converter_finalize; +} + +static void +gdk_x11_text_list_converter_init (GdkX11TextListConverter *local) +{ +} + +GConverter * +gdk_x11_text_list_converter_to_utf8_new (GdkDisplay *display, + const char *encoding, + int format) +{ + GdkX11TextListConverter *conv; + + conv = g_object_new (GDK_TYPE_X11_TEXT_LIST_CONVERTER, NULL); + + conv->display = g_object_ref (display); + conv->encoding = g_intern_string (encoding); + conv->format = format; + + return G_CONVERTER (conv); +} + diff --git a/gdk/x11/gdktextlistconverter-x11.h b/gdk/x11/gdktextlistconverter-x11.h new file mode 100644 index 0000000000..9017a8b7e1 --- /dev/null +++ b/gdk/x11/gdktextlistconverter-x11.h @@ -0,0 +1,44 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2017 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Benjamin Otte <otte@gnome.org> + */ + +#ifndef __GDK_X11_TEXT_LIST_CONVERTER_H__ +#define __GDK_X11_TEXT_LIST_CONVERTER_H__ + +#include <gio/gio.h> +#include <gdk/gdktypes.h> + +G_BEGIN_DECLS + +#define GDK_TYPE_X11_TEXT_LIST_CONVERTER (gdk_x11_text_list_converter_get_type ()) +#define GDK_X11_TEXT_LIST_CONVERTER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_X11_TEXT_LIST_CONVERTER, GdkX11TextListConverter)) +#define GDK_IS_X11_TEXT_LIST_CONVERTER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_X11_TEXT_LIST_CONVERTER)) + +typedef struct _GdkX11TextListConverter GdkX11TextListConverter; + +GType gdk_x11_text_list_converter_get_type (void) G_GNUC_CONST; + +GConverter * gdk_x11_text_list_converter_to_utf8_new (GdkDisplay *display, + const char *encoding, + int format); + + +G_END_DECLS + +#endif /* __GDK_X11_TEXT_LIST_CONVERTER_H__ */ diff --git a/gdk/x11/meson.build b/gdk/x11/meson.build index 57ed429df4..db74295d8a 100644 --- a/gdk/x11/meson.build +++ b/gdk/x11/meson.build @@ -22,6 +22,7 @@ gdk_x11_sources = files([ 'gdkscreen-x11.c', 'gdkselection-x11.c', 'gdkselectioninputstream-x11.c', + 'gdktextlistconverter-x11.c', 'gdkvisual-x11.c', 'gdkvulkancontext-x11.c', 'gdkwindow-x11.c', |