[gtk+] x11: Refactor code



commit 5df527edaf47b3cc1e85c6a0d537bf94dd35db93
Author: Benjamin Otte <otte redhat com>
Date:   Tue Dec 12 15:25:34 2017 +0100

    x11: Refactor code
    
    This is in preparation for DND.
    
    It moves a lot of code from gdkclipboard-x11.c to
    gdkselectionoutputstream-x11.c to untangle it from GdkX11Clipboard
    usage.

 gdk/x11/gdkclipboard-x11.c             |  347 ++---------------------
 gdk/x11/gdkclipboard-x11.h             |    4 +
 gdk/x11/gdkselectionoutputstream-x11.c |  495 ++++++++++++++++++++++++++++----
 gdk/x11/gdkselectionoutputstream-x11.h |   28 +--
 4 files changed, 476 insertions(+), 398 deletions(-)
---
diff --git a/gdk/x11/gdkclipboard-x11.c b/gdk/x11/gdkclipboard-x11.c
index 817792e..78edb2a 100644
--- a/gdk/x11/gdkclipboard-x11.c
+++ b/gdk/x11/gdkclipboard-x11.c
@@ -92,12 +92,12 @@ gdk_x11_clipboard_default_output_done (GObject      *clipboard,
 }
 
 static void
-gdk_x11_clipboard_default_output_handler (GdkX11Clipboard *cb,
-                                          const char      *target,
-                                          GOutputStream   *stream)
+gdk_x11_clipboard_default_output_handler (GOutputStream   *stream,
+                                          const char      *mime_type,
+                                          gpointer         user_data)
 {
-  gdk_clipboard_write_async (GDK_CLIPBOARD (cb),
-                             g_intern_string (target),
+  gdk_clipboard_write_async (GDK_CLIPBOARD (user_data),
+                             mime_type,
                              stream,
                              G_PRIORITY_DEFAULT,
                              NULL,
@@ -106,108 +106,6 @@ gdk_x11_clipboard_default_output_handler (GdkX11Clipboard *cb,
   g_object_unref (stream);
 }
 
-static void
-handle_targets_done (GObject      *stream,
-                     GAsyncResult *result,
-                     gpointer      user_data)
-{
-  GError *error = NULL;
-  gsize bytes_written;
-
-  if (!g_output_stream_write_all_finish (G_OUTPUT_STREAM (stream), result, &bytes_written, &error))
-    {
-      GDK_NOTE(CLIPBOARD, g_printerr ("---: failed to send targets after %zu bytes: %s\n",
-                                      bytes_written, error->message));
-      g_error_free (error);
-    }
-
-  g_free (user_data);
-}
-
-static Atom *
-gdk_x11_clipboard_formats_to_atoms (GdkDisplay        *display,
-                                    gboolean           include_special,
-                                    GdkContentFormats *formats,
-                                    gsize             *n_atoms);
-
-static void
-handle_targets (GdkX11Clipboard *cb,
-                const char      *target,
-                const char      *encoding,
-                int              format,
-                GOutputStream   *stream)
-{
-  GdkClipboard *clipboard = GDK_CLIPBOARD (cb);
-  Atom *atoms;
-  gsize n_atoms;
-
-  atoms = gdk_x11_clipboard_formats_to_atoms (gdk_clipboard_get_display (clipboard),
-                                              TRUE,
-                                              gdk_clipboard_get_formats (clipboard),
-                                              &n_atoms);
-  print_atoms (cb, "sending targets", atoms, n_atoms);
-  g_output_stream_write_all_async (stream,
-                                     atoms,
-                                     n_atoms * sizeof (Atom),
-                                     G_PRIORITY_DEFAULT,
-                                     NULL,
-                                     handle_targets_done,
-                                     atoms);
-  g_object_unref (stream);
-}
-
-static void
-handle_timestamp_done (GObject      *stream,
-                       GAsyncResult *result,
-                       gpointer      user_data)
-{
-  GError *error = NULL;
-  gsize bytes_written;
-
-  if (!g_output_stream_write_all_finish (G_OUTPUT_STREAM (stream), result, &bytes_written, &error))
-    {
-      GDK_NOTE(CLIPBOARD, g_printerr ("---: failed to send timestamp after %zu bytes: %s\n",
-                                      bytes_written, error->message));
-      g_error_free (error);
-    }
-
-  g_slice_free (gulong, user_data);
-}
-
-static void
-handle_timestamp (GdkX11Clipboard *cb,
-                  const char      *target,
-                  const char      *encoding,
-                  int              format,
-                  GOutputStream   *stream)
-{
-  gulong *timestamp;
-
-  timestamp = g_slice_new (gulong);
-  *timestamp = cb->timestamp;
-
-  g_output_stream_write_all_async (stream,
-                                   timestamp,
-                                   sizeof (gulong),
-                                   G_PRIORITY_DEFAULT,
-                                   NULL,
-                                   handle_timestamp_done,
-                                   timestamp);
-  g_object_unref (stream);
-}
-
-static void
-handle_save_targets (GdkX11Clipboard *cb,
-                     const char      *target,
-                     const char      *encoding,
-                     int              format,
-                     GOutputStream   *stream)
-{
-  /* Don't do anything */
-
-  g_object_unref (stream);
-}
-
 static GInputStream * 
 text_list_convert (GdkX11Clipboard *cb,
                    GInputStream    *stream,
@@ -228,37 +126,6 @@ text_list_convert (GdkX11Clipboard *cb,
   return converter_stream;
 }
 
-static void
-handle_text_list (GdkX11Clipboard *cb,
-                  const char      *target,
-                  const char      *encoding,
-                  int              format,
-                  GOutputStream   *stream)
-{
-  GOutputStream *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_output_stream_new (stream, converter);
-
-  g_object_unref (converter);
-  g_object_unref (stream);
-
-  gdk_x11_clipboard_default_output_handler (cb, "text/plain;charset=utf-8", converter_stream);
-}
-
-static void
-handle_utf8 (GdkX11Clipboard *cb,
-             const char      *target,
-             const char      *encoding,
-             int              format,
-             GOutputStream   *stream)
-{
-  gdk_x11_clipboard_default_output_handler (cb, "text/plain;charset=utf-8", stream);
-}
-
 static GInputStream * 
 no_convert (GdkX11Clipboard *cb,
             GInputStream    *stream,
@@ -268,23 +135,20 @@ no_convert (GdkX11Clipboard *cb,
   return stream;
 }
 
-typedef void (* MimeTypeHandleFunc) (GdkX11Clipboard *, const char *, const char *, int, GOutputStream *);
-
 static const struct {
   const char *x_target;
   const char *mime_type;
   GInputStream * (* convert) (GdkX11Clipboard *, GInputStream *, const char *, int);
   const char *type;
   gint format;
-  MimeTypeHandleFunc handler;
 } special_targets[] = {
-  { "UTF8_STRING",   "text/plain;charset=utf-8", no_convert,        "UTF8_STRING",   8,  handle_utf8 },
-  { "COMPOUND_TEXT", "text/plain;charset=utf-8", text_list_convert, "COMPOUND_TEXT", 8,  handle_text_list },
-  { "TEXT",          "text/plain;charset=utf-8", text_list_convert, "STRING",        8,  handle_text_list },
-  { "STRING",        "text/plain;charset=utf-8", text_list_convert, "STRING",        8,  handle_text_list },
-  { "TARGETS",       NULL,                       NULL,              "ATOM",          32, handle_targets },
-  { "TIMESTAMP",     NULL,                       NULL,              "INTEGER",       32, handle_timestamp },
-  { "SAVE_TARGETS",  NULL,                       NULL,              "NULL",          32, handle_save_targets 
}
+  { "UTF8_STRING",   "text/plain;charset=utf-8", no_convert,        "UTF8_STRING",   8 },
+  { "COMPOUND_TEXT", "text/plain;charset=utf-8", text_list_convert, "COMPOUND_TEXT", 8 },
+  { "TEXT",          "text/plain;charset=utf-8", text_list_convert, "STRING",        8 },
+  { "STRING",        "text/plain;charset=utf-8", text_list_convert, "STRING",        8 },
+  { "TARGETS",       NULL,                       NULL,              "ATOM",          32 },
+  { "TIMESTAMP",     NULL,                       NULL,              "INTEGER",       32 },
+  { "SAVE_TARGETS",  NULL,                       NULL,              "NULL",          32 }
 };
 
 GSList *
@@ -313,7 +177,7 @@ gdk_x11_clipboard_formats_to_targets (GdkContentFormats *formats)
   return g_slist_reverse (targets);
 }
 
-static Atom *
+Atom *
 gdk_x11_clipboard_formats_to_atoms (GdkDisplay        *display,
                                     gboolean           include_special,
                                     GdkContentFormats *formats,
@@ -332,8 +196,7 @@ gdk_x11_clipboard_formats_to_atoms (GdkDisplay        *display,
           if (special_targets[i].mime_type != NULL)
             continue;
 
-          if (special_targets[i].handler)
-            targets = g_slist_prepend (targets, (gpointer) g_intern_string (special_targets[i].x_target));
+          targets = g_slist_prepend (targets, (gpointer) g_intern_string (special_targets[i].x_target));
         }
     }
 
@@ -509,164 +372,6 @@ gdk_x11_clipboard_claim_remote (GdkX11Clipboard *cb,
   gdk_x11_clipboard_request_targets (cb);
 }
 
-static gboolean
-gdk_x11_clipboard_request_selection (GdkX11Clipboard              *cb,
-                                     GdkX11PendingSelectionNotify *notify,
-                                     Window                        requestor,
-                                     const char                   *target,
-                                     const char                   *property,
-                                     gulong                        timestamp)
-{
-  const char *mime_type;
-  GdkDisplay *display;
-  gsize i;
-
-  display = gdk_clipboard_get_display (GDK_CLIPBOARD (cb));
-  mime_type = gdk_intern_mime_type (target);
-
-  if (mime_type)
-    {
-      if (gdk_content_formats_contain_mime_type (gdk_clipboard_get_formats (GDK_CLIPBOARD (cb)), mime_type))
-        {
-          GOutputStream *stream;
-
-          stream = gdk_x11_selection_output_stream_new (display,
-                                                        notify,
-                                                        requestor,
-                                                        cb->selection,
-                                                        target,
-                                                        property,
-                                                        target,
-                                                        8,
-                                                        timestamp);
-          gdk_x11_clipboard_default_output_handler (cb, target, stream);
-          return TRUE;
-        }
-    }
-  else if (g_str_equal (target, "MULTIPLE"))
-    {
-      gulong n_atoms;
-      gulong nbytes;
-      Atom prop_type;
-      gint prop_format;
-      Atom *atoms = NULL;
-      int error;
-
-      error = XGetWindowProperty (gdk_x11_display_get_xdisplay (display),
-                                  requestor,
-                                  gdk_x11_get_xatom_by_name_for_display (display, property),
-                                  0, 0x1FFFFFFF, False,
-                                  AnyPropertyType,
-                                  &prop_type, &prop_format,
-                                  &n_atoms, &nbytes, (guchar **) &atoms);
-      if (error != Success)
-        {
-          GDK_NOTE(CLIPBOARD, g_printerr ("%s: XGetProperty() during MULTIPLE failed with %d\n",
-                                          cb->selection, error));
-        }
-      else if (prop_format != 32 ||
-               prop_type != gdk_x11_get_xatom_by_name_for_display (display, "ATOM_PAIR"))
-        {
-          GDK_NOTE(CLIPBOARD, g_printerr ("%s: XGetProperty() type/format should be ATOM_PAIR/32 but is 
%s/%d\n",
-                                          cb->selection, gdk_x11_get_xatom_name_for_display (display, 
prop_type), prop_format));
-        }
-      else if (n_atoms < 2)
-        {
-          print_atoms (cb, "ignoring MULTIPLE request with too little elements", atoms, n_atoms);
-        }
-      else
-        {
-          gulong i;
-
-          print_atoms (cb, "MULTIPLE request", atoms, n_atoms);
-          if (n_atoms % 2)
-            {
-              GDK_NOTE(CLIPBOARD, g_printerr ("%s: Number of atoms is uneven at %lu, ignoring last 
element\n",
-                                              cb->selection, n_atoms));
-              n_atoms &= ~1;
-            }
-
-          gdk_x11_pending_selection_notify_require (notify, n_atoms / 2);
-
-          for (i = 0; i < n_atoms / 2; i++)
-            {
-              gboolean success;
-
-              if (atoms[2 * i] == None || atoms[2 * i + 1] == None)
-                {
-                  success = FALSE;
-                  GDK_NOTE(CLIPBOARD, g_printerr ("%s: None not allowed as atom in MULTIPLE request\n",
-                                                  cb->selection));
-                  gdk_x11_pending_selection_notify_send (notify, display, FALSE);
-                }
-              else if (atoms[2 * i] == gdk_x11_get_xatom_by_name_for_display (display, "MULTIPLE"))
-                {
-                  success = FALSE;
-                  GDK_NOTE(CLIPBOARD, g_printerr ("%s: MULTIPLE as target in MULTIPLE request would cause 
recursion\n",
-                                                  cb->selection));
-                  gdk_x11_pending_selection_notify_send (notify, display, FALSE);
-                }
-              else
-                {
-                  success = gdk_x11_clipboard_request_selection (cb,
-                                                                 notify,
-                                                                 requestor,
-                                                                 gdk_x11_get_xatom_name_for_display 
(display, atoms[2 * i]),
-                                                                 gdk_x11_get_xatom_name_for_display 
(display, atoms[2 * i + 1]),
-                                                                 timestamp);
-                }
-
-              if (!success)
-                atoms[2 * i + 1] = None;
-            }
-        }
-
-      XChangeProperty (gdk_x11_display_get_xdisplay (display),
-                       requestor,
-                       gdk_x11_get_xatom_by_name_for_display (display, property),
-                       prop_type, 32,
-                       PropModeReplace, (guchar *)atoms, n_atoms);
-
-      if (atoms)
-        XFree (atoms);
-
-      gdk_x11_pending_selection_notify_send (notify, display, TRUE);
-      return TRUE;
-    }
-  else
-    {
-      for (i = 0; i < G_N_ELEMENTS (special_targets); i++)
-        {
-          if (g_str_equal (target, special_targets[i].x_target) &&
-              special_targets[i].handler)
-            {
-              GOutputStream *stream;
-
-              if (special_targets[i].mime_type)
-                mime_type = gdk_intern_mime_type (special_targets[i].mime_type);
-              stream = gdk_x11_selection_output_stream_new (display,
-                                                            notify,
-                                                            requestor,
-                                                            cb->selection,
-                                                            target,
-                                                            property,
-                                                            special_targets[i].type,
-                                                            special_targets[i].format,
-                                                            timestamp);
-              special_targets[i].handler (cb,
-                                          target,
-                                          special_targets[i].type,
-                                          special_targets[i].format,
-                                          stream);
-              return TRUE;
-            }
-        }
-    }
-
-  gdk_x11_pending_selection_notify_send (notify, display, FALSE);
-  return FALSE;
-}
-
 static GdkFilterReturn
 gdk_x11_clipboard_filter_event (GdkXEvent *xev,
                                 GdkEvent  *gdkevent,
@@ -732,7 +437,6 @@ gdk_x11_clipboard_filter_event (GdkXEvent *xev,
 
     case SelectionRequest:
       {
-        GdkX11PendingSelectionNotify *notify;
         const char *target, *property;
 
         if (xevent->xselectionrequest.selection != cb->xselection)
@@ -760,19 +464,16 @@ gdk_x11_clipboard_filter_event (GdkXEvent *xev,
         GDK_NOTE(CLIPBOARD, g_printerr ("%s: got SelectionRequest for %s @ %s\n",
                                         cb->selection, target, property));
 
-        notify = gdk_x11_pending_selection_notify_new (xevent->xselectionrequest.requestor,
-                                                       xevent->xselectionrequest.selection,
-                                                       xevent->xselectionrequest.target,
-                                                       xevent->xselectionrequest.property ? 
xevent->xselectionrequest.property
-                                                                                          : 
xevent->xselectionrequest.target,
-                                                       xevent->xselectionrequest.time);
-
-        gdk_x11_clipboard_request_selection (cb,
-                                             notify,
-                                             xevent->xselectionrequest.requestor,
-                                             target,
-                                             property,
-                                             xevent->xselectionrequest.time);
+        gdk_x11_selection_output_streams_create (display,
+                                                 gdk_clipboard_get_formats (GDK_CLIPBOARD (cb)),
+                                                 xevent->xselectionrequest.requestor,
+                                                 xevent->xselectionrequest.selection,
+                                                 xevent->xselectionrequest.target,
+                                                 xevent->xselectionrequest.property ? 
xevent->xselectionrequest.property
+                                                                                    : 
xevent->xselectionrequest.target,
+                                                 xevent->xselectionrequest.time,
+                                                 gdk_x11_clipboard_default_output_handler,
+                                                 cb);
         return GDK_FILTER_REMOVE;
       }
 
diff --git a/gdk/x11/gdkclipboard-x11.h b/gdk/x11/gdkclipboard-x11.h
index 0c0322d..e4b82ee 100644
--- a/gdk/x11/gdkclipboard-x11.h
+++ b/gdk/x11/gdkclipboard-x11.h
@@ -37,6 +37,10 @@ GdkClipboard *          gdk_x11_clipboard_new                   (GdkDisplay
                                                                  const gchar            *selection);
 
 GSList *                gdk_x11_clipboard_formats_to_targets    (GdkContentFormats      *formats);
+Atom *                  gdk_x11_clipboard_formats_to_atoms      (GdkDisplay             *display,
+                                                                 gboolean                include_special,
+                                                                 GdkContentFormats      *formats,
+                                                                 gsize                  *n_atoms);
 
 G_END_DECLS
 
diff --git a/gdk/x11/gdkselectionoutputstream-x11.c b/gdk/x11/gdkselectionoutputstream-x11.c
index 5a42421..2219004 100644
--- a/gdk/x11/gdkselectionoutputstream-x11.c
+++ b/gdk/x11/gdkselectionoutputstream-x11.c
@@ -23,12 +23,15 @@
 
 #include "gdkselectionoutputstream-x11.h"
 
+#include "gdkclipboard-x11.h"
 #include "gdkdisplay-x11.h"
 #include "gdkintl.h"
+#include "gdktextlistconverter-x11.h"
 #include "gdkx11display.h"
 #include "gdkx11property.h"
 #include "gdkx11window.h"
 
+typedef struct _GdkX11PendingSelectionNotify GdkX11PendingSelectionNotify;
 typedef struct _GdkX11SelectionOutputStreamPrivate  GdkX11SelectionOutputStreamPrivate;
 
 struct _GdkX11SelectionOutputStreamPrivate {
@@ -66,6 +69,84 @@ struct _GdkX11PendingSelectionNotify
 
 G_DEFINE_TYPE_WITH_PRIVATE (GdkX11SelectionOutputStream, gdk_x11_selection_output_stream, 
G_TYPE_OUTPUT_STREAM);
 
+static GdkX11PendingSelectionNotify *
+gdk_x11_pending_selection_notify_new (Window window,
+                                      Atom   selection,
+                                      Atom   target,
+                                      Atom   property,
+                                      Time   timestamp)
+{
+  GdkX11PendingSelectionNotify *pending;
+
+  pending = g_slice_new0 (GdkX11PendingSelectionNotify);
+  pending->n_pending = 1;
+
+  pending->xevent.type = SelectionNotify;
+  pending->xevent.serial = 0;
+  pending->xevent.send_event = True;
+  pending->xevent.requestor = window;
+  pending->xevent.selection = selection;
+  pending->xevent.target = target;
+  pending->xevent.property = property;
+  pending->xevent.time = timestamp;
+
+  return pending;
+}
+
+static void
+gdk_x11_pending_selection_notify_require (GdkX11PendingSelectionNotify *notify,
+                                          guint                         n_sends)
+{
+  notify->n_pending += n_sends;
+}
+
+static void
+gdk_x11_pending_selection_notify_send (GdkX11PendingSelectionNotify *notify,
+                                       GdkDisplay                   *display,
+                                       gboolean                      success)
+{
+  Display *xdisplay;
+  int error;
+
+  notify->n_pending--;
+  if (notify->n_pending)
+    {
+      GDK_NOTE(SELECTION, g_printerr ("%s:%s: not sending SelectionNotify yet, %zu streams still pending\n",
+                                      gdk_x11_get_xatom_name_for_display (display, notify->xevent.selection),
+                                      gdk_x11_get_xatom_name_for_display (display, notify->xevent.target),
+                                      notify->n_pending));
+      return;
+    }
+
+  GDK_NOTE(SELECTION, g_printerr ("%s:%s: sending SelectionNotify reporting %s\n",
+                                  gdk_x11_get_xatom_name_for_display (display, notify->xevent.selection),
+                                  gdk_x11_get_xatom_name_for_display (display, notify->xevent.target),
+                                  success ? "success" : "failure"));
+  if (!success)
+    notify->xevent.property = None;
+
+  xdisplay = gdk_x11_display_get_xdisplay (display);
+
+  gdk_x11_display_error_trap_push (display);
+
+  if (XSendEvent (xdisplay, notify->xevent.requestor, False, NoEventMask, (XEvent*) &notify->xevent) == 0)
+    {
+      GDK_NOTE(SELECTION, g_printerr ("%s:%s: failed to XSendEvent()\n",
+                                      gdk_x11_get_xatom_name_for_display (display, notify->xevent.selection),
+                                      gdk_x11_get_xatom_name_for_display (display, notify->xevent.target)));
+    }
+  XSync (xdisplay, False);
+
+  error = gdk_x11_display_error_trap_pop (display);
+  if (error != Success)
+    {
+      GDK_NOTE(SELECTION, g_printerr ("%s:%s: X error during write: %d\n",
+                                      gdk_x11_get_xatom_name_for_display (display, notify->xevent.selection),
+                                      gdk_x11_get_xatom_name_for_display (display, notify->xevent.target),
+                                      error));
+    }
+}
+
 static GdkFilterReturn
 gdk_x11_selection_output_stream_filter_event (GdkXEvent *xevent,
                                               GdkEvent  *gdkevent,
@@ -539,7 +620,7 @@ gdk_x11_selection_output_stream_filter_event (GdkXEvent *xev,
     }
 }
 
-GOutputStream *
+static GOutputStream *
 gdk_x11_selection_output_stream_new (GdkDisplay                   *display,
                                      GdkX11PendingSelectionNotify *notify,
                                      Window                        window,
@@ -576,82 +657,386 @@ gdk_x11_selection_output_stream_new (GdkDisplay                   *display,
   return G_OUTPUT_STREAM (stream);
 }
 
-GdkX11PendingSelectionNotify *
-gdk_x11_pending_selection_notify_new (Window window,
-                                      Atom   selection,
-                                      Atom   target,
-                                      Atom   property,
-                                      Time   timestamp)
+static void
+print_atoms (GdkDisplay *display,
+             const char *selection,
+             const char *prefix,
+             const Atom *atoms,
+             gsize       n_atoms)
 {
-  GdkX11PendingSelectionNotify *pending;
+  GDK_NOTE(CLIPBOARD,
+           gsize i;
+            
+           g_printerr ("%s: %s [ ", 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");
+          ); 
+}
 
-  pending = g_slice_new0 (GdkX11PendingSelectionNotify);
-  pending->n_pending = 1;
+static void
+handle_targets_done (GObject      *stream,
+                     GAsyncResult *result,
+                     gpointer      user_data)
+{
+  GError *error = NULL;
+  gsize bytes_written;
 
-  pending->xevent.type = SelectionNotify;
-  pending->xevent.serial = 0;
-  pending->xevent.send_event = True;
-  pending->xevent.requestor = window;
-  pending->xevent.selection = selection;
-  pending->xevent.target = target;
-  pending->xevent.property = property;
-  pending->xevent.time = timestamp;
+  if (!g_output_stream_write_all_finish (G_OUTPUT_STREAM (stream), result, &bytes_written, &error))
+    {
+      GDK_NOTE(CLIPBOARD, g_printerr ("---: failed to send targets after %zu bytes: %s\n",
+                                      bytes_written, error->message));
+      g_error_free (error);
+    }
 
-  return pending;
+  g_free (user_data);
 }
 
-void
-gdk_x11_pending_selection_notify_require (GdkX11PendingSelectionNotify *notify,
-                                          guint                         n_sends)
+static void
+handle_targets (GOutputStream                *stream,
+                GdkDisplay                   *display,
+                GdkContentFormats            *formats,
+                const char                   *target,
+                const char                   *encoding,
+                int                           format,
+                gulong                        timestamp,
+                GdkX11SelectionOutputHandler  handler,
+                gpointer                      user_data)
 {
-  notify->n_pending += n_sends;
+  Atom *atoms;
+  gsize n_atoms;
+
+  atoms = gdk_x11_clipboard_formats_to_atoms (display,
+                                              TRUE,
+                                              formats,
+                                              &n_atoms);
+  print_atoms (display, "---", "sending targets", atoms, n_atoms);
+  g_output_stream_write_all_async (stream,
+                                   atoms,
+                                   n_atoms * sizeof (Atom),
+                                   G_PRIORITY_DEFAULT,
+                                   NULL,
+                                   handle_targets_done,
+                                   atoms);
+  g_object_unref (stream);
 }
 
-void
-gdk_x11_pending_selection_notify_send (GdkX11PendingSelectionNotify *notify,
-                                       GdkDisplay                   *display,
-                                       gboolean                      success)
+static void
+handle_timestamp_done (GObject      *stream,
+                       GAsyncResult *result,
+                       gpointer      user_data)
 {
-  Display *xdisplay;
-  int error;
+  GError *error = NULL;
+  gsize bytes_written;
 
-  notify->n_pending--;
-  if (notify->n_pending)
+  if (!g_output_stream_write_all_finish (G_OUTPUT_STREAM (stream), result, &bytes_written, &error))
     {
-      GDK_NOTE(SELECTION, g_printerr ("%s:%s: not sending SelectionNotify yet, %zu streams still pending\n",
-                                      gdk_x11_get_xatom_name_for_display (display, notify->xevent.selection),
-                                      gdk_x11_get_xatom_name_for_display (display, notify->xevent.target),
-                                      notify->n_pending));
-      return;
+      GDK_NOTE(CLIPBOARD, g_printerr ("---: failed to send timestamp after %zu bytes: %s\n",
+                                      bytes_written, error->message));
+      g_error_free (error);
     }
 
-  GDK_NOTE(SELECTION, g_printerr ("%s:%s: sending SelectionNotify reporting %s\n",
-                                  gdk_x11_get_xatom_name_for_display (display, notify->xevent.selection),
-                                  gdk_x11_get_xatom_name_for_display (display, notify->xevent.target),
-                                  success ? "success" : "failure"));
-  if (!success)
-    notify->xevent.property = None;
+  g_slice_free (gulong, user_data);
+}
 
-  xdisplay = gdk_x11_display_get_xdisplay (display);
+static void
+handle_timestamp (GOutputStream                *stream,
+                  GdkDisplay                   *display,
+                  GdkContentFormats            *formats,
+                  const char                   *target,
+                  const char                   *encoding,
+                  int                           format,
+                  gulong                        timestamp,
+                  GdkX11SelectionOutputHandler  handler,
+                  gpointer                      user_data)
+{
+  gulong *time_;
+
+  time_ = g_slice_new (gulong);
+  *time_ = timestamp;
+
+  g_output_stream_write_all_async (stream,
+                                   time_,
+                                   sizeof (gulong),
+                                   G_PRIORITY_DEFAULT,
+                                   NULL,
+                                   handle_timestamp_done,
+                                   time_);
+  g_object_unref (stream);
+}
 
-  gdk_x11_display_error_trap_push (display);
+static void
+handle_save_targets (GOutputStream                *stream,
+                     GdkDisplay                   *display,
+                     GdkContentFormats            *formats,
+                     const char                   *target,
+                     const char                   *encoding,
+                     int                           format,
+                     gulong                        timestamp,
+                     GdkX11SelectionOutputHandler  handler,
+                     gpointer                      user_data)
+{
+  /* Don't do anything */
 
-  if (XSendEvent (xdisplay, notify->xevent.requestor, False, NoEventMask, (XEvent*) &notify->xevent) == 0)
+  g_object_unref (stream);
+}
+
+static void
+handle_text_list (GOutputStream                *stream,
+                  GdkDisplay                   *display,
+                  GdkContentFormats            *formats,
+                  const char                   *target,
+                  const char                   *encoding,
+                  int                           format,
+                  gulong                        timestamp,
+                  GdkX11SelectionOutputHandler  handler,
+                  gpointer                      user_data)
+{
+  GOutputStream *converter_stream;
+  GConverter *converter;
+
+  converter = gdk_x11_text_list_converter_to_utf8_new (display,
+                                                       encoding,
+                                                       format);
+  converter_stream = g_converter_output_stream_new (stream, converter);
+
+  g_object_unref (converter);
+  g_object_unref (stream);
+
+  handler (converter_stream, gdk_intern_mime_type ("text/plain;charset=utf-8"), user_data);
+}
+
+static void
+handle_utf8 (GOutputStream                *stream,
+             GdkDisplay                   *display,
+             GdkContentFormats            *formats,
+             const char                   *target,
+             const char                   *encoding,
+             int                           format,
+             gulong                        timestamp,
+             GdkX11SelectionOutputHandler  handler,
+             gpointer                      user_data)
+{
+  handler (stream, gdk_intern_mime_type ("text/plain;charset=utf-8"), user_data);
+}
+
+typedef void (* MimeTypeHandleFunc) (GOutputStream *, GdkDisplay *, GdkContentFormats *, const char *, const 
char *, int, gulong, GdkX11SelectionOutputHandler, gpointer);
+
+static const struct {
+  const char *x_target;
+  const char *mime_type;
+  const char *type;
+  gint format;
+  MimeTypeHandleFunc handler;
+} special_targets[] = {
+  { "UTF8_STRING",   "text/plain;charset=utf-8", "UTF8_STRING",   8,  handle_utf8 },
+  { "COMPOUND_TEXT", "text/plain;charset=utf-8", "COMPOUND_TEXT", 8,  handle_text_list },
+  { "TEXT",          "text/plain;charset=utf-8", "STRING",        8,  handle_text_list },
+  { "STRING",        "text/plain;charset=utf-8", "STRING",        8,  handle_text_list },
+  { "TARGETS",       NULL,                       "ATOM",          32, handle_targets },
+  { "TIMESTAMP",     NULL,                       "INTEGER",       32, handle_timestamp },
+  { "SAVE_TARGETS",  NULL,                       "NULL",          32, handle_save_targets }
+};
+
+static gboolean
+gdk_x11_selection_output_streams_request (GdkDisplay                   *display,
+                                          GdkX11PendingSelectionNotify *notify,
+                                          GdkContentFormats            *formats,
+                                          Window                        requestor,
+                                          Atom                          xselection,
+                                          Atom                          xtarget,
+                                          Atom                          xproperty,
+                                          gulong                        timestamp,
+                                          GdkX11SelectionOutputHandler  handler,
+                                          gpointer                      user_data)
+{
+  const char *mime_type, *selection, *target, *property;
+  gsize i;
+
+  selection = gdk_x11_get_xatom_name_for_display (display, xselection);
+  target = gdk_x11_get_xatom_name_for_display (display, xtarget);
+  property = gdk_x11_get_xatom_name_for_display (display, xproperty);
+  mime_type = gdk_intern_mime_type (target);
+
+  if (mime_type)
     {
-      GDK_NOTE(SELECTION, g_printerr ("%s:%s: failed to XSendEvent()\n",
-                                      gdk_x11_get_xatom_name_for_display (display, notify->xevent.selection),
-                                      gdk_x11_get_xatom_name_for_display (display, notify->xevent.target)));
+      if (gdk_content_formats_contain_mime_type (formats, mime_type))
+        {
+          GOutputStream *stream;
+
+          stream = gdk_x11_selection_output_stream_new (display,
+                                                        notify,
+                                                        requestor,
+                                                        selection,
+                                                        target,
+                                                        property,
+                                                        target,
+                                                        8,
+                                                        timestamp);
+          handler (stream, target, user_data);
+          return TRUE;
+        }
     }
-  XSync (xdisplay, False);
+  else if (g_str_equal (target, "MULTIPLE"))
+    {
+      gulong n_atoms;
+      gulong nbytes;
+      Atom prop_type;
+      gint prop_format;
+      Atom *atoms = NULL;
+      int error;
+
+      error = XGetWindowProperty (gdk_x11_display_get_xdisplay (display),
+                                  requestor,
+                                  xproperty,
+                                  0, 0x1FFFFFFF, False,
+                                  AnyPropertyType,
+                                  &prop_type, &prop_format,
+                                  &n_atoms, &nbytes, (guchar **) &atoms);
+      if (error != Success)
+        {
+          GDK_NOTE(SELECTION, g_printerr ("%s: XGetProperty() during MULTIPLE failed with %d\n",
+                                          selection, error));
+        }
+      else if (prop_format != 32 ||
+               prop_type != gdk_x11_get_xatom_by_name_for_display (display, "ATOM_PAIR"))
+        {
+          GDK_NOTE(SELECTION, g_printerr ("%s: XGetProperty() type/format should be ATOM_PAIR/32 but is 
%s/%d\n",
+                                          selection, gdk_x11_get_xatom_name_for_display (display, 
prop_type), prop_format));
+        }
+      else if (n_atoms < 2)
+        {
+          print_atoms (display, selection, "ignoring MULTIPLE request with too little elements", atoms, 
n_atoms);
+        }
+      else
+        {
+          gulong i;
+
+          print_atoms (display, selection, "MULTIPLE request", atoms, n_atoms);
+          if (n_atoms % 2)
+            {
+              GDK_NOTE(SELECTION, g_printerr ("%s: Number of atoms is uneven at %lu, ignoring last 
element\n",
+                                              selection, n_atoms));
+              n_atoms &= ~1;
+            }
+
+          gdk_x11_pending_selection_notify_require (notify, n_atoms / 2);
+
+          for (i = 0; i < n_atoms / 2; i++)
+            {
+              gboolean success;
+
+              if (atoms[2 * i] == None || atoms[2 * i + 1] == None)
+                {
+                  success = FALSE;
+                  GDK_NOTE(SELECTION, g_printerr ("%s: None not allowed as atom in MULTIPLE request\n",
+                                                  selection));
+                  gdk_x11_pending_selection_notify_send (notify, display, FALSE);
+                }
+              else if (atoms[2 * i] == gdk_x11_get_xatom_by_name_for_display (display, "MULTIPLE"))
+                {
+                  success = FALSE;
+                  GDK_NOTE(SELECTION, g_printerr ("%s: MULTIPLE as target in MULTIPLE request would cause 
recursion\n",
+                                                  selection));
+                  gdk_x11_pending_selection_notify_send (notify, display, FALSE);
+                }
+              else
+                {
+                  success = gdk_x11_selection_output_streams_request (display,
+                                                                      notify,
+                                                                      formats,
+                                                                      requestor,
+                                                                      xselection,
+                                                                      atoms[2 * i],
+                                                                      atoms[2 * i + 1],
+                                                                      timestamp,
+                                                                      handler,
+                                                                      user_data);
+                }
+
+              if (!success)
+                atoms[2 * i + 1] = None;
+            }
+        }
 
-  error = gdk_x11_display_error_trap_pop (display);
-  if (error != Success)
+      XChangeProperty (gdk_x11_display_get_xdisplay (display),
+                       requestor,
+                       xproperty,
+                       prop_type, 32,
+                       PropModeReplace, (guchar *)atoms, n_atoms);
+
+      if (atoms)
+        XFree (atoms);
+
+      gdk_x11_pending_selection_notify_send (notify, display, TRUE);
+      return TRUE;
+    }
+  else
     {
-      GDK_NOTE(SELECTION, g_printerr ("%s:%s: X error during write: %d\n",
-                                      gdk_x11_get_xatom_name_for_display (display, notify->xevent.selection),
-                                      gdk_x11_get_xatom_name_for_display (display, notify->xevent.target),
-                                      error));
+      for (i = 0; i < G_N_ELEMENTS (special_targets); i++)
+        {
+          if (g_str_equal (target, special_targets[i].x_target) &&
+              special_targets[i].handler)
+            {
+              GOutputStream *stream;
+
+              if (special_targets[i].mime_type)
+                mime_type = gdk_intern_mime_type (special_targets[i].mime_type);
+              stream = gdk_x11_selection_output_stream_new (display,
+                                                            notify,
+                                                            requestor,
+                                                            selection,
+                                                            target,
+                                                            property,
+                                                            special_targets[i].type,
+                                                            special_targets[i].format,
+                                                            timestamp);
+              special_targets[i].handler (stream,
+                                          display,
+                                          formats,
+                                          target,
+                                          special_targets[i].type,
+                                          special_targets[i].format,
+                                          timestamp,
+                                          handler,
+                                          user_data);
+              return TRUE;
+            }
+        }
     }
+
+  gdk_x11_pending_selection_notify_send (notify, display, FALSE);
+  return FALSE;
 }
 
+void
+gdk_x11_selection_output_streams_create (GdkDisplay                   *display,
+                                         GdkContentFormats            *formats,
+                                         Window                        requestor,
+                                         Atom                          selection,
+                                         Atom                          target,
+                                         Atom                          property,
+                                         gulong                        timestamp,
+                                         GdkX11SelectionOutputHandler  handler,
+                                         gpointer                      user_data)
+{
+  GdkX11PendingSelectionNotify *notify;
 
+  notify = gdk_x11_pending_selection_notify_new (requestor,
+                                                 selection,
+                                                 target,
+                                                 property,
+                                                 timestamp);
+  gdk_x11_selection_output_streams_request (display,
+                                            notify,
+                                            formats,
+                                            requestor,
+                                            selection,
+                                            target,
+                                            property,
+                                            timestamp,
+                                            handler,
+                                            user_data);
+}
diff --git a/gdk/x11/gdkselectionoutputstream-x11.h b/gdk/x11/gdkselectionoutputstream-x11.h
index 7b4c09c..03a0d30 100644
--- a/gdk/x11/gdkselectionoutputstream-x11.h
+++ b/gdk/x11/gdkselectionoutputstream-x11.h
@@ -36,11 +36,11 @@ G_BEGIN_DECLS
 #define GDK_IS_X11_SELECTION_OUTPUT_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), 
GDK_TYPE_X11_SELECTION_OUTPUT_STREAM))
 #define GDK_X11_SELECTION_OUTPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), 
GDK_TYPE_X11_SELECTION_OUTPUT_STREAM, GdkX11SelectionOutputStreamClass))
 
-typedef struct _GdkX11PendingSelectionNotify GdkX11PendingSelectionNotify;
-
 typedef struct GdkX11SelectionOutputStream         GdkX11SelectionOutputStream;
 typedef struct GdkX11SelectionOutputStreamClass    GdkX11SelectionOutputStreamClass;
 
+typedef void (* GdkX11SelectionOutputHandler) (GOutputStream *stream, const char *mime_type, gpointer 
user_data);
+
 struct GdkX11SelectionOutputStream
 {
   GOutputStream parent_instance;
@@ -54,27 +54,15 @@ struct GdkX11SelectionOutputStreamClass
 
 GType           gdk_x11_selection_output_stream_get_type        (void) G_GNUC_CONST;
 
-GOutputStream * gdk_x11_selection_output_stream_new             (GdkDisplay             *display,
-                                                                 GdkX11PendingSelectionNotify *notify,
-                                                                 Window                  window,
-                                                                 const char             *selection,
-                                                                 const char             *target,
-                                                                 const char             *property,
-                                                                 const char             *type,
-                                                                 int                     format,
-                                                                 gulong                  timestamp);
-
-GdkX11PendingSelectionNotify *
-                gdk_x11_pending_selection_notify_new            (Window                  window,
+void            gdk_x11_selection_output_streams_create         (GdkDisplay             *display,
+                                                                 GdkContentFormats      *formats,
+                                                                 Window                  requestor,
                                                                  Atom                    selection,
                                                                  Atom                    target,
                                                                  Atom                    property,
-                                                                 Time                    timestamp);
-void            gdk_x11_pending_selection_notify_require        (GdkX11PendingSelectionNotify *notify,
-                                                                 guint                   n_sends);
-void            gdk_x11_pending_selection_notify_send           (GdkX11PendingSelectionNotify *notify,
-                                                                 GdkDisplay             *display,
-                                                                 gboolean                success);
+                                                                 gulong                  timestamp,
+                                                                 GdkX11SelectionOutputHandler handler,
+                                                                 gpointer                user_data);
 
 G_END_DECLS
 



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