[gtk+/wip/otte/clipboard: 2/2] x11: Improve fallbacks for text



commit fbc40d167bd2686db20886341c52f0a73adbb81f
Author: Benjamin Otte <otte redhat com>
Date:   Thu Nov 23 01:59:19 2017 +0100

    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

 gdk/x11/gdkclipboard-x11.c            |  233 +++++++++++++++++++++++++++++----
 gdk/x11/gdkselectioninputstream-x11.c |   28 ++++-
 gdk/x11/gdkselectioninputstream-x11.h |    2 +
 gdk/x11/gdktextlistconverter-x11.c    |  169 ++++++++++++++++++++++++
 gdk/x11/gdktextlistconverter-x11.h    |   44 ++++++
 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 31b3bfe..ea710d5 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_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 f03b3d9..e62db50 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 c8bd19d..18c0763 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 0000000..7fe56d1
--- /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 0000000..9017a8b
--- /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 57ed429..db74295 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',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]