[gtk+/wip/otte/clipboard: 2/11] x11: Add an initial clipboard implementation



commit 4be60b28280ed1f89f8d0ebd8da785651df3b618
Author: Benjamin Otte <otte redhat com>
Date:   Sun Nov 19 18:06:13 2017 +0100

    x11: Add an initial clipboard implementation
    
    This does nothing but download the targets and debug-print them.

 gdk/gdk.c                             |    4 +-
 gdk/gdkinternals.h                    |    4 +-
 gdk/x11/gdkclipboard-x11.c            |  192 ++++++++++++++
 gdk/x11/gdkclipboard-x11.h            |   46 ++++
 gdk/x11/gdkdisplay-x11.c              |   40 ++-
 gdk/x11/gdkdisplay-x11.h              |    3 +
 gdk/x11/gdkselectioninputstream-x11.c |  443 +++++++++++++++++++++++++++++++++
 gdk/x11/gdkselectioninputstream-x11.h |   59 +++++
 gdk/x11/meson.build                   |    2 +
 9 files changed, 779 insertions(+), 14 deletions(-)
---
diff --git a/gdk/gdk.c b/gdk/gdk.c
index d04bf41..28eb0fe 100644
--- a/gdk/gdk.c
+++ b/gdk/gdk.c
@@ -166,7 +166,9 @@ static const GDebugKey gdk_debug_keys[] = {
   { "frames",        GDK_DEBUG_FRAMES },
   { "settings",      GDK_DEBUG_SETTINGS },
   { "opengl",        GDK_DEBUG_OPENGL },
-  { "vulkan",        GDK_DEBUG_VULKAN }
+  { "vulkan",        GDK_DEBUG_VULKAN },
+  { "selection",     GDK_DEBUG_SELECTION },
+  { "clipboard",     GDK_DEBUG_CLIPBOARD }
 };
 #endif
 
diff --git a/gdk/gdkinternals.h b/gdk/gdkinternals.h
index 0ef2aaa..0411997 100644
--- a/gdk/gdkinternals.h
+++ b/gdk/gdkinternals.h
@@ -70,7 +70,9 @@ typedef enum {
   GDK_DEBUG_FRAMES        = 1 << 10,
   GDK_DEBUG_SETTINGS      = 1 << 11,
   GDK_DEBUG_OPENGL        = 1 << 12,
-  GDK_DEBUG_VULKAN        = 1 << 13
+  GDK_DEBUG_VULKAN        = 1 << 13,
+  GDK_DEBUG_SELECTION     = 1 << 14,
+  GDK_DEBUG_CLIPBOARD     = 1 << 15
 } GdkDebugFlag;
 
 typedef enum {
diff --git a/gdk/x11/gdkclipboard-x11.c b/gdk/x11/gdkclipboard-x11.c
new file mode 100644
index 0000000..4a90d8a
--- /dev/null
+++ b/gdk/x11/gdkclipboard-x11.c
@@ -0,0 +1,192 @@
+/* GDK - The GIMP Drawing Kit
+ * 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 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/>.
+ */
+
+#include "config.h"
+
+#include "gdkclipboardprivate.h"
+#include "gdkclipboard-x11.h"
+#include "gdkselectioninputstream-x11.h"
+#include "gdkprivate-x11.h"
+
+#include <string.h>
+#include <X11/Xatom.h>
+
+#ifdef HAVE_XFIXES
+#include <X11/extensions/Xfixes.h>
+#endif
+
+#define IDLE_ABORT_TIME 30 /* seconds */
+
+typedef struct _GdkX11ClipboardClass GdkX11ClipboardClass;
+
+typedef struct _RetrievalInfo RetrievalInfo;
+
+struct _GdkX11Clipboard
+{
+  GdkClipboard parent;
+
+  char       *selection;
+  Atom        xselection;
+  guint32     time;
+};
+
+struct _GdkX11ClipboardClass
+{
+  GdkClipboardClass parent_class;
+};
+
+G_DEFINE_TYPE (GdkX11Clipboard, gdk_x11_clipboard, GDK_TYPE_CLIPBOARD)
+
+static void
+gdk_x11_clipboard_finalize (GObject *object)
+{
+  GdkX11Clipboard *cb = GDK_X11_CLIPBOARD (object);
+
+  g_free (cb->selection);
+
+  G_OBJECT_CLASS (gdk_x11_clipboard_parent_class)->finalize (object);
+}
+
+static void
+gdk_x11_clipboard_class_init (GdkX11ClipboardClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+  //GdkClipboardClass *clipboard_class = GDK_CLIPBOARD_CLASS (class);
+
+  object_class->finalize = gdk_x11_clipboard_finalize;
+}
+
+static void
+gdk_x11_clipboard_init (GdkX11Clipboard *clipboard)
+{
+}
+
+#define SELECTION_MAX_SIZE(display)                                     \
+  MIN(262144,                                                           \
+      XExtendedMaxRequestSize (GDK_DISPLAY_XDISPLAY (display)) == 0     \
+       ? XMaxRequestSize (GDK_DISPLAY_XDISPLAY (display)) - 100         \
+       : XExtendedMaxRequestSize (GDK_DISPLAY_XDISPLAY (display)) - 100)
+
+static void
+gdk_x11_clipboard_request_targets_finish (GObject      *source_object,
+                                          GAsyncResult *res,
+                                          gpointer      user_data)
+{
+  GInputStream *stream = G_INPUT_STREAM (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 || g_bytes_get_size (bytes) == 0)
+    {
+      if (bytes)
+        g_bytes_unref (bytes);
+      g_object_unref (stream);
+      g_object_unref (cb);
+      return;
+    }
+
+  display = gdk_clipboard_get_display (GDK_CLIPBOARD (cb));
+
+  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]));
+    }
+  formats = gdk_content_formats_builder_free (builder);
+  GDK_NOTE(CLIPBOARD, char *s = gdk_content_formats_to_string (formats); g_printerr ("%s: got formats: 
%s\n", cb->selection, s); g_free (s));
+
+  g_input_stream_read_bytes_async (stream,
+                                   SELECTION_MAX_SIZE (display),
+                                   G_PRIORITY_DEFAULT,
+                                   NULL,
+                                   gdk_x11_clipboard_request_targets_finish,
+                                   cb);
+}
+
+static void
+gdk_x11_clipboard_request_targets (GdkX11Clipboard *cb)
+{
+  GInputStream *stream;
+  GdkDisplay *display;
+  
+  display = gdk_clipboard_get_display (GDK_CLIPBOARD (cb));
+
+  stream = gdk_x11_selection_input_stream_new (gdk_clipboard_get_display (GDK_CLIPBOARD (cb)),
+                                               cb->selection,
+                                               "TARGETS");
+
+  g_input_stream_read_bytes_async (stream,
+                                   SELECTION_MAX_SIZE (display),
+                                   G_PRIORITY_DEFAULT,
+                                   NULL,
+                                   gdk_x11_clipboard_request_targets_finish,
+                                   g_object_ref (cb));
+}
+
+GdkClipboard *
+gdk_x11_clipboard_new (GdkDisplay  *display,
+                       const gchar *selection)
+{
+  GdkX11Clipboard *cb;
+
+  cb = g_object_new (GDK_TYPE_X11_CLIPBOARD,
+                     "display", display,
+                     NULL);
+
+  cb->selection = g_strdup (selection);
+  cb->xselection = gdk_x11_get_xatom_by_name_for_display (display, selection);
+
+  gdk_display_request_selection_notification (display, gdk_atom_intern (selection, FALSE));
+  gdk_x11_clipboard_request_targets (cb);
+
+  return GDK_CLIPBOARD (cb);
+}
+
+gboolean
+gdk_x11_clipboard_handle_event (GdkX11Clipboard *cb,
+                                XEvent          *xevent)
+{
+  return FALSE;
+}
+
+gboolean
+gdk_x11_clipboard_handle_selection_notify (GdkX11Clipboard *cb,
+                                           XEvent          *xevent)
+{
+#ifdef HAVE_XFIXES
+  GdkDisplay *display = gdk_clipboard_get_display (GDK_CLIPBOARD (cb));
+  XFixesSelectionNotifyEvent *event = (XFixesSelectionNotifyEvent *)xevent;
+
+  if (event->window != GDK_X11_DISPLAY (display)->leader_window ||
+      event->selection != cb->xselection)
+    return FALSE;
+
+  return TRUE;
+#else
+  return FALSE;
+#endif
+}
+
diff --git a/gdk/x11/gdkclipboard-x11.h b/gdk/x11/gdkclipboard-x11.h
new file mode 100644
index 0000000..fa8485d
--- /dev/null
+++ b/gdk/x11/gdkclipboard-x11.h
@@ -0,0 +1,46 @@
+/* GDK - The GIMP Drawing Kit
+ * 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 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/>.
+ */
+
+#ifndef __GDK_CLIPBOARD_X11_H__
+#define __GDK_CLIPBOARD_X11_H__
+
+#include "gdk/gdkclipboard.h"
+
+#include <X11/Xlib.h>
+
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_X11_CLIPBOARD    (gdk_x11_clipboard_get_type ())
+#define GDK_X11_CLIPBOARD(obj)    (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_X11_CLIPBOARD, 
GdkX11Clipboard))
+#define GDK_IS_X11_CLIPBOARD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_X11_CLIPBOARD))
+
+typedef struct _GdkX11Clipboard GdkX11Clipboard;
+
+GType                   gdk_x11_clipboard_get_type              (void) G_GNUC_CONST;
+
+GdkClipboard *          gdk_x11_clipboard_new                   (GdkDisplay             *display,
+                                                                 const gchar            *selection);
+
+gboolean                gdk_x11_clipboard_handle_event          (GdkX11Clipboard        *clipboard,
+                                                                 XEvent                 *xevent);
+gboolean                gdk_x11_clipboard_handle_selection_notify(GdkX11Clipboard       *clipboard,
+                                                                 XEvent                 *xevent);
+
+G_END_DECLS
+
+#endif /* __GDK_CLIPBOARD_X11_H__ */
diff --git a/gdk/x11/gdkdisplay-x11.c b/gdk/x11/gdkdisplay-x11.c
index 245df4a..5faa0de 100644
--- a/gdk/x11/gdkdisplay-x11.c
+++ b/gdk/x11/gdkdisplay-x11.c
@@ -24,6 +24,9 @@
 
 #define VK_USE_PLATFORM_XLIB_KHR
 
+#include "gdkdisplay-x11.h"
+#include "gdkdisplayprivate.h"
+
 #include "gdkasync.h"
 #include "gdkdisplay.h"
 #include "gdkeventsource.h"
@@ -35,13 +38,12 @@
 #include "gdkkeysprivate.h"
 #include "gdkdevicemanager.h"
 #include "xsettings-client.h"
-#include "gdkdisplay-x11.h"
+#include "gdkclipboard-x11.h"
 #include "gdkprivate-x11.h"
 #include "gdkscreen-x11.h"
 #include "gdkglcontext-x11.h"
 #include "gdkvulkancontext-x11.h"
 #include "gdk-private.h"
-#include "gdkdisplayprivate.h"
 
 #include <glib.h>
 #include <glib/gprintf.h>
@@ -1141,16 +1143,24 @@ gdk_x11_display_translate_event (GdkEventTranslator *translator,
               gdk_display_set_composited (display, composited);
             }
 
-         event->owner_change.type = GDK_OWNER_CHANGE;
-         event->owner_change.window = window;
-         event->owner_change.reason = selection_notify->subtype;
-         event->owner_change.selection =
-           gdk_x11_xatom_to_atom_for_display (display,
-                                              selection_notify->selection);
-         event->owner_change.time = selection_notify->timestamp;
-         event->owner_change.selection_time = selection_notify->selection_timestamp;
-
-         return_val = TRUE;
+          if (gdk_x11_clipboard_handle_selection_notify (GDK_X11_CLIPBOARD (display->clipboard), xevent) ||
+              gdk_x11_clipboard_handle_selection_notify (GDK_X11_CLIPBOARD (display->primary_clipboard), 
xevent))
+            {
+              return_val = FALSE;
+            }
+          else
+            {
+              event->owner_change.type = GDK_OWNER_CHANGE;
+              event->owner_change.window = window;
+              event->owner_change.reason = selection_notify->subtype;
+              event->owner_change.selection =
+                gdk_x11_xatom_to_atom_for_display (display,
+                                                   selection_notify->selection);
+              event->owner_change.time = selection_notify->timestamp;
+              event->owner_change.selection_time = selection_notify->selection_timestamp;
+
+             return_val = TRUE;
+            }
        }
       else
 #endif
@@ -1741,6 +1751,9 @@ gdk_x11_display_open (const gchar *display_name)
   }
 #endif
 
+  display->clipboard = gdk_x11_clipboard_new (display, "CLIPBOARD");
+  display->primary_clipboard = gdk_x11_clipboard_new (display, "PRIMARY");
+
   /*
    * It is important that we first request the selection
    * notification, and then setup the initial state of
@@ -2053,6 +2066,9 @@ gdk_x11_display_finalize (GObject *object)
   /* Empty the event queue */
   _gdk_x11_display_free_translate_queue (GDK_DISPLAY (display_x11));
 
+  /* Get rid of pending streams */
+  g_slist_free_full (display_x11->input_streams, g_object_unref);
+
   /* Atom Hashtable */
   g_hash_table_destroy (display_x11->atom_from_virtual);
   g_hash_table_destroy (display_x11->atom_to_virtual);
diff --git a/gdk/x11/gdkdisplay-x11.h b/gdk/x11/gdkdisplay-x11.h
index adee9f2..4c7a2e2 100644
--- a/gdk/x11/gdkdisplay-x11.h
+++ b/gdk/x11/gdkdisplay-x11.h
@@ -104,6 +104,9 @@ struct _GdkX11Display
   /* translation queue */
   GQueue *translate_queue;
 
+  /* streams reading selections */
+  GSList *input_streams;
+
   /* input GdkWindow list */
   GList *input_windows;
 
diff --git a/gdk/x11/gdkselectioninputstream-x11.c b/gdk/x11/gdkselectioninputstream-x11.c
new file mode 100644
index 0000000..9a4965c
--- /dev/null
+++ b/gdk/x11/gdkselectioninputstream-x11.c
@@ -0,0 +1,443 @@
+/* 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>
+ *         Christian Kellner <gicmo gnome org> 
+ */
+
+#include "config.h"
+
+#include "gdkselectioninputstream-x11.h"
+
+#include "gdkdisplay-x11.h"
+#include "gdkintl.h"
+#include "gdkx11display.h"
+#include "gdkx11property.h"
+#include "gdkx11window.h"
+
+typedef struct GdkX11SelectionInputStreamPrivate  GdkX11SelectionInputStreamPrivate;
+
+struct GdkX11SelectionInputStreamPrivate {
+  GdkDisplay *display;
+  GQueue chunks;
+  char *selection;
+  Atom xselection;
+  char *target;
+  Atom xtarget;
+  char *property;
+  Atom xproperty;
+
+  GTask *pending_task;
+  guchar *pending_data;
+  gsize pending_size;
+
+  guint complete : 1;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GdkX11SelectionInputStream, gdk_x11_selection_input_stream, G_TYPE_INPUT_STREAM);
+
+static GdkFilterReturn
+gdk_x11_selection_input_stream_filter_event (GdkXEvent *xevent,
+                                             GdkEvent  *gdkevent,
+                                             gpointer   data);
+
+static gboolean
+gdk_x11_selection_input_stream_has_data (GdkX11SelectionInputStream *stream)
+{
+  GdkX11SelectionInputStreamPrivate *priv = gdk_x11_selection_input_stream_get_instance_private (stream);
+
+  return !g_queue_is_empty (&priv->chunks) || priv->complete;
+}
+
+static gssize
+gdk_x11_selection_input_stream_fill_buffer (GdkX11SelectionInputStream *stream,
+                                            guchar                     *buffer,
+                                            gsize                       count)
+{
+  GdkX11SelectionInputStreamPrivate *priv = gdk_x11_selection_input_stream_get_instance_private (stream);
+  gssize result = 0;
+
+  while (!g_queue_is_empty (&priv->chunks) && count > 0)
+  {
+    GBytes *bytes = g_queue_pop_head (&priv->chunks);
+    gsize size = g_bytes_get_size (bytes);
+
+    if (g_bytes_get_size (bytes) > count)
+      {
+        GBytes *subbytes;
+        if (buffer)
+          memcpy (buffer, g_bytes_get_data (bytes, NULL), count);
+        subbytes = g_bytes_new_from_bytes (bytes, count, size - count);
+        g_queue_push_head (&priv->chunks, subbytes);
+        size = count;
+      }
+    else
+      {
+        if (buffer)
+          memcpy (buffer, g_bytes_get_data (bytes, NULL), size);
+      }
+
+    g_bytes_unref (bytes);
+    result += size;
+    if (buffer)
+      buffer += size;
+    count -= size;
+  }
+
+  return result;
+}
+
+static void
+gdk_x11_selection_input_stream_flush (GdkX11SelectionInputStream *stream)
+{
+  GdkX11SelectionInputStreamPrivate *priv = gdk_x11_selection_input_stream_get_instance_private (stream);
+  gssize written;
+
+  if (!gdk_x11_selection_input_stream_has_data (stream))
+    return;
+
+  if (priv->pending_task == NULL)
+    return;
+
+  written = gdk_x11_selection_input_stream_fill_buffer (stream, priv->pending_data, priv->pending_size);
+  g_task_return_int (priv->pending_task, written);
+
+  priv->pending_task = NULL;
+  priv->pending_data = NULL;
+  priv->pending_size = 0;
+}
+
+static void
+gdk_x11_selection_input_stream_complete (GdkX11SelectionInputStream *stream)
+{
+  GdkX11SelectionInputStreamPrivate *priv = gdk_x11_selection_input_stream_get_instance_private (stream);
+
+  if (priv->complete)
+    return;
+
+  priv->complete = TRUE;
+
+  gdk_x11_selection_input_stream_flush (stream);
+
+  GDK_X11_DISPLAY (priv->display)->input_streams = g_slist_remove (GDK_X11_DISPLAY 
(priv->display)->input_streams, stream);
+  gdk_window_remove_filter (GDK_X11_DISPLAY (priv->display)->leader_gdk_window, 
gdk_x11_selection_input_stream_filter_event, stream);
+
+  g_object_unref (stream);
+}
+
+static gssize
+gdk_x11_selection_input_stream_read (GInputStream  *stream,
+                                     void          *buffer,
+                                     gsize          count,
+                                     GCancellable  *cancellable,
+                                     GError       **error)
+{
+  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                       _("X11 Selections cannot be read synchronously"));
+
+  return -1;
+}
+
+static gboolean
+gdk_x11_selection_input_stream_close (GInputStream  *stream,
+                                      GCancellable  *cancellable,
+                                      GError       **error)
+{
+  return TRUE;
+}
+
+static void
+gdk_x11_selection_input_stream_read_async (GInputStream        *input_stream,
+                                           void                *buffer,
+                                           gsize                count,
+                                           int                  io_priority,
+                                           GCancellable        *cancellable,
+                                           GAsyncReadyCallback  callback,
+                                           gpointer             user_data)
+{
+  GdkX11SelectionInputStream *stream = GDK_X11_SELECTION_INPUT_STREAM (input_stream);
+  GdkX11SelectionInputStreamPrivate *priv = gdk_x11_selection_input_stream_get_instance_private (stream);
+  GTask *task;
+  
+  task = g_task_new (stream, cancellable, callback, user_data);
+  g_task_set_source_tag (task, gdk_x11_selection_input_stream_read_async);
+  g_task_set_priority (task, io_priority);
+
+  if (gdk_x11_selection_input_stream_has_data (stream))
+    {
+      gssize size;
+
+      size = gdk_x11_selection_input_stream_fill_buffer (stream, buffer, count);
+      g_task_return_int (task, size);
+    }
+  else
+    {
+      priv->pending_data = buffer;
+      priv->pending_size = count;
+      priv->pending_task = task;
+    }
+}
+
+static gssize
+gdk_x11_selection_input_stream_read_finish (GInputStream  *stream,
+                                            GAsyncResult  *result,
+                                            GError       **error)
+{
+  g_return_val_if_fail (g_task_is_valid (result, stream), -1);
+
+  return g_task_propagate_int (G_TASK (result), error);
+}
+
+static void
+gdk_x11_selection_input_stream_close_async (GInputStream        *stream,
+                                            int                  io_priority,
+                                            GCancellable        *cancellable,
+                                            GAsyncReadyCallback  callback,
+                                            gpointer             user_data)
+{
+  GTask *task;
+
+  task = g_task_new (stream, cancellable, callback, user_data);
+  g_task_set_source_tag (task, gdk_x11_selection_input_stream_close_async);
+  g_task_return_boolean (task, TRUE);
+  g_object_unref (task);
+}
+
+static gboolean
+gdk_x11_selection_input_stream_close_finish (GInputStream  *stream,
+                                             GAsyncResult  *result,
+                                             GError       **error)
+{
+  return TRUE;
+}
+
+static void
+gdk_x11_selection_input_stream_finalize (GObject *object)
+{
+  GdkX11SelectionInputStream *stream = GDK_X11_SELECTION_INPUT_STREAM (object);
+  GdkX11SelectionInputStreamPrivate *priv = gdk_x11_selection_input_stream_get_instance_private (stream);
+
+  gdk_x11_selection_input_stream_complete (stream);
+
+  g_queue_foreach (&priv->chunks, (GFunc) g_bytes_unref, NULL);
+  g_queue_clear (&priv->chunks);
+
+  g_free (priv->selection);
+  g_free (priv->target);
+  g_free (priv->property);
+
+  G_OBJECT_CLASS (gdk_x11_selection_input_stream_parent_class)->finalize (object);
+}
+
+static void
+gdk_x11_selection_input_stream_class_init (GdkX11SelectionInputStreamClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GInputStreamClass *istream_class = G_INPUT_STREAM_CLASS (klass);
+
+  object_class->finalize = gdk_x11_selection_input_stream_finalize;
+  
+  istream_class->read_fn = gdk_x11_selection_input_stream_read;
+  istream_class->close_fn = gdk_x11_selection_input_stream_close;
+
+  istream_class->read_async = gdk_x11_selection_input_stream_read_async;
+  istream_class->read_finish = gdk_x11_selection_input_stream_read_finish;
+  istream_class->close_async = gdk_x11_selection_input_stream_close_async;
+  istream_class->close_finish = gdk_x11_selection_input_stream_close_finish;
+}
+
+static void
+gdk_x11_selection_input_stream_init (GdkX11SelectionInputStream *stream)
+{
+}
+
+static void
+XFree_without_return_value (gpointer data)
+{
+  XFree (data);
+}
+
+static GBytes *
+get_selection_property (Display  *display,
+                        Window    owner,
+                        Atom      property,
+                        Atom     *ret_type,
+                        gint     *ret_format)
+{
+  gulong nitems;
+  gulong nbytes;
+  Atom prop_type;
+  gint prop_format;
+  guchar *data = NULL;
+
+  if (XGetWindowProperty (display, owner, property,
+                          0, 0x1FFFFFFF, False,
+                          AnyPropertyType, &prop_type, &prop_format,
+                          &nitems, &nbytes, &data) != Success)
+    goto err;
+
+  if (prop_type != None)
+    {
+      gsize length;
+
+      switch (prop_format)
+        {
+        case 8:
+          length = nitems;
+          break;
+        case 16:
+          length = sizeof (short) * nitems;
+          break;
+        case 32:
+          length = sizeof (long) * nitems;
+          break;
+        default:
+          g_warning ("Unknown XGetWindowProperty() format %u", prop_format);
+          goto err;
+        }
+
+      *ret_type = prop_type;
+      *ret_format = prop_format;
+
+      return g_bytes_new_with_free_func (data,
+                                         length,
+                                         XFree_without_return_value,
+                                         data);
+    }
+
+err:
+  if (data)
+    XFree (data);
+
+  *ret_type = None;
+  *ret_format = 0;
+
+  return NULL;
+}
+
+
+static GdkFilterReturn
+gdk_x11_selection_input_stream_filter_event (GdkXEvent *xev,
+                                             GdkEvent  *gdkevent,
+                                             gpointer   data)
+{
+  GdkX11SelectionInputStream *stream = GDK_X11_SELECTION_INPUT_STREAM (data);
+  GdkX11SelectionInputStreamPrivate *priv = gdk_x11_selection_input_stream_get_instance_private (stream);
+  XEvent *xevent = xev;
+  Display *xdisplay;
+  Window xwindow;
+
+#if 0
+  GList *tmp_list;
+  GtkRetrievalInfo *info = NULL;
+  GdkWindow *window;
+  GBytes *bytes;
+  gint length;
+  GdkAtom type;
+  GdkAtom selection;
+  GdkAtom property;
+  gint   format;
+  guint32 time;
+#endif
+
+  xdisplay = gdk_x11_display_get_xdisplay (priv->display);
+  xwindow = GDK_X11_DISPLAY (priv->display)->leader_window;
+
+  if (xevent->xany.window != xwindow)
+    return GDK_FILTER_CONTINUE;
+
+  switch (xevent->type)
+    {
+    case SelectionNotify:
+      {
+        GBytes *bytes;
+        Atom type;
+        gint format;
+
+        if (priv->xselection != xevent->xselection.selection ||
+            priv->xtarget != xevent->xselection.target)
+          return GDK_FILTER_CONTINUE;
+
+        GDK_NOTE(SELECTION, g_print ("%s:%s: got SelectionNotify\n", priv->selection, priv->target));
+
+        if (xevent->xselection.property != None)
+          bytes = get_selection_property (xdisplay, xwindow, xevent->xselection.property, &type, &format);
+        else
+          bytes = NULL;
+
+        if (bytes == NULL)
+          { 
+            gdk_x11_selection_input_stream_complete (stream);
+          }
+        else
+          {
+            g_queue_push_tail (&priv->chunks, bytes);
+
+            if (type == gdk_x11_get_xatom_by_name_for_display (priv->display, "INCR"))
+              {
+                /* The remainder of the selection will come through PropertyNotify
+                   events on xwindow */
+                GDK_NOTE(SELECTION, g_print ("%s:%s: initiating INCR transfer\n", priv->selection, 
priv->target));
+                gdk_x11_selection_input_stream_flush (stream);
+              }
+            else
+              {
+                gdk_x11_selection_input_stream_complete (stream);
+              }
+
+            XDeleteProperty (xdisplay, xwindow, xevent->xselection.property);
+          }
+        }
+      return GDK_FILTER_REMOVE;
+
+    default:
+      return GDK_FILTER_CONTINUE;
+    }
+}
+
+GInputStream *
+gdk_x11_selection_input_stream_new (GdkDisplay *display,
+                                    const char *selection,
+                                    const char *target)
+{
+  GdkX11SelectionInputStream *stream;
+  GdkX11SelectionInputStreamPrivate *priv;
+  
+  stream = g_object_new (GDK_TYPE_X11_SELECTION_INPUT_STREAM, NULL);
+  priv = gdk_x11_selection_input_stream_get_instance_private (stream);
+
+  priv->display = display;
+  GDK_X11_DISPLAY (display)->input_streams = g_slist_prepend (GDK_X11_DISPLAY (display)->input_streams, 
stream);
+  priv->selection = g_strdup (selection);
+  priv->xselection = gdk_x11_get_xatom_by_name_for_display (display, priv->selection);
+  priv->target = g_strdup (target);
+  priv->xtarget = gdk_x11_get_xatom_by_name_for_display (display, priv->target);
+  priv->property = g_strdup_printf ("GDK_SELECTION_%p", stream); 
+  priv->xproperty = gdk_x11_get_xatom_by_name_for_display (display, priv->property);
+
+  gdk_window_add_filter (NULL, gdk_x11_selection_input_stream_filter_event, stream);
+
+  XConvertSelection (GDK_DISPLAY_XDISPLAY (display),
+                     priv->xselection,
+                     priv->xtarget,
+                     priv->xproperty,
+                     GDK_X11_DISPLAY (display)->leader_window,
+                     CurrentTime);
+
+  return g_object_ref (stream);
+}
+
diff --git a/gdk/x11/gdkselectioninputstream-x11.h b/gdk/x11/gdkselectioninputstream-x11.h
new file mode 100644
index 0000000..9947329
--- /dev/null
+++ b/gdk/x11/gdkselectioninputstream-x11.h
@@ -0,0 +1,59 @@
+/* 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>
+ *         Christian Kellner <gicmo gnome org>
+ */
+
+#ifndef __GDK_X11_SELECTION_INPUT_STREAM_H__
+#define __GDK_X11_SELECTION_INPUT_STREAM_H__
+
+#include <gio/gio.h>
+#include "gdktypes.h"
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_X11_SELECTION_INPUT_STREAM         (gdk_x11_selection_input_stream_get_type ())
+#define GDK_X11_SELECTION_INPUT_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), 
GDK_TYPE_X11_SELECTION_INPUT_STREAM, GdkX11SelectionInputStream))
+#define GDK_X11_SELECTION_INPUT_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), 
GDK_TYPE_X11_SELECTION_INPUT_STREAM, GdkX11SelectionInputStreamClass))
+#define GDK_IS_X11_SELECTION_INPUT_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), 
GDK_TYPE_X11_SELECTION_INPUT_STREAM))
+#define GDK_IS_X11_SELECTION_INPUT_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), 
GDK_TYPE_X11_SELECTION_INPUT_STREAM))
+#define GDK_X11_SELECTION_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), 
GDK_TYPE_X11_SELECTION_INPUT_STREAM, GdkX11SelectionInputStreamClass))
+
+typedef struct GdkX11SelectionInputStream         GdkX11SelectionInputStream;
+typedef struct GdkX11SelectionInputStreamClass    GdkX11SelectionInputStreamClass;
+
+struct GdkX11SelectionInputStream
+{
+  GInputStream parent_instance;
+};
+
+struct GdkX11SelectionInputStreamClass
+{
+  GInputStreamClass parent_class;
+};
+
+
+GType          gdk_x11_selection_input_stream_get_type      (void) G_GNUC_CONST;
+
+GInputStream * gdk_x11_selection_input_stream_new           (GdkDisplay *display,
+                                                             const char *selection,
+                                                             const char *target);
+
+G_END_DECLS
+
+#endif /* __GDK_X11_SELECTION_INPUT_STREAM_H__ */
diff --git a/gdk/x11/meson.build b/gdk/x11/meson.build
index 526329f..57ed429 100644
--- a/gdk/x11/meson.build
+++ b/gdk/x11/meson.build
@@ -2,6 +2,7 @@
 gdk_x11_sources = files([
   'gdkapplaunchcontext-x11.c',
   'gdkasync.c',
+  'gdkclipboard-x11.c',
   'gdkcursor-x11.c',
   'gdkdevice-core-x11.c',
   'gdkdevice-xi2.c',
@@ -20,6 +21,7 @@ gdk_x11_sources = files([
   'gdkproperty-x11.c',
   'gdkscreen-x11.c',
   'gdkselection-x11.c',
+  'gdkselectioninputstream-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]