[gtk+/wip/wayland-primary-selection: 249/249] WIP: Implement primary selection protocol



commit 2ffb19be7e6a2e0caacae3acbda0f0663f608c39
Author: Carlos Garnacho <carlosg gnome org>
Date:   Thu Feb 4 17:33:51 2016 +0100

    WIP: Implement primary selection protocol

 gdk/wayland/Makefile.am            |    2 +
 gdk/wayland/gdkdevice-wayland.c    |   56 +++++++-
 gdk/wayland/gdkdisplay-wayland.c   |    6 +
 gdk/wayland/gdkdisplay-wayland.h   |    1 +
 gdk/wayland/gdkprivate-wayland.h   |   13 ++-
 gdk/wayland/gdkselection-wayland.c |  311 +++++++++++++++++++++++++++++-------
 6 files changed, 325 insertions(+), 64 deletions(-)
---
diff --git a/gdk/wayland/Makefile.am b/gdk/wayland/Makefile.am
index a56c407..d155db2 100644
--- a/gdk/wayland/Makefile.am
+++ b/gdk/wayland/Makefile.am
@@ -24,6 +24,8 @@ BUILT_SOURCES =                               \
        pointer-gestures-unstable-v1-protocol.c                 \
        xdg-shell-unstable-v5-client-protocol.h                 \
        xdg-shell-unstable-v5-protocol.c                        \
+       primary-selection-unstable-v1-client-protocol.h         \
+       primary-selection-unstable-v1-protocol.c                \
        gtk-shell-client-protocol.h             \
        gtk-shell-protocol.c
 
diff --git a/gdk/wayland/gdkdevice-wayland.c b/gdk/wayland/gdkdevice-wayland.c
index b8f92fe..f69eb24 100644
--- a/gdk/wayland/gdkdevice-wayland.c
+++ b/gdk/wayland/gdkdevice-wayland.c
@@ -96,7 +96,6 @@ struct _GdkWaylandSeat
   GdkWindow *pointer_focus;
   GdkWindow *keyboard_focus;
   GdkAtom pending_selection;
-  struct wl_data_device *data_device;
   double surface_x, surface_y;
   uint32_t time;
   uint32_t enter_serial;
@@ -115,6 +114,8 @@ struct _GdkWaylandSeat
   guint cursor_image_index;
   guint cursor_image_delay;
 
+  struct zwp_primary_selection_device_v1 *primary_data_device;
+  struct wl_data_device *data_device;
   GdkDragContext *drop_context;
 
   struct wl_surface *pointer_surface;
@@ -926,6 +927,42 @@ static const struct wl_data_device_listener data_device_listener = {
   data_device_selection
 };
 
+static void
+primary_selection_data_offer (void                                   *data,
+                              struct zwp_primary_selection_device_v1 *wp_primary_selection_device,
+                              struct zwp_primary_selection_offer_v1  *wp_primary_offer)
+{
+  GdkWaylandDeviceData *device = (GdkWaylandDeviceData *) data;
+
+  GDK_NOTE (EVENTS,
+            g_message ("primary selection offer, device %p, data offer %p",
+                       wp_primary_selection_device, wp_primary_offer));
+
+  gdk_wayland_selection_ensure_primary_offer (device->display, wp_primary_offer);
+}
+
+static void
+primary_selection_selection (void                                   *data,
+                             struct zwp_primary_selection_device_v1 *wp_primary_selection_device,
+                             struct zwp_primary_selection_offer_v1  *wp_primary_offer)
+{
+  GdkWaylandDeviceData *device = (GdkWaylandDeviceData *) data;
+  GdkAtom selection;
+
+  GDK_NOTE (EVENTS,
+            g_message ("primary selection selection, device %p, data offer %p",
+                       wp_primary_selection_device, wp_primary_offer));
+
+  selection = gdk_atom_intern_static_string ("PRIMARY");
+  gdk_wayland_selection_set_offer (device->display, selection, wp_primary_offer);
+  emit_selection_owner_change (device->keyboard_focus, selection);
+}
+
+static const struct zwp_primary_selection_device_v1_listener primary_selection_device_listener = {
+  primary_selection_data_offer,
+  primary_selection_selection,
+};
+
 static GdkEvent *
 create_scroll_event (GdkWaylandSeat *seat,
                      gboolean        emulated)
@@ -2883,6 +2920,12 @@ _gdk_wayland_device_manager_add_seat (GdkDeviceManager *device_manager,
   wl_seat_add_listener (seat->wl_seat, &seat_listener, seat);
   wl_seat_set_user_data (seat->wl_seat, seat);
 
+  seat->primary_data_device =
+    zwp_primary_selection_device_manager_v1_get_device (display_wayland->primary_selection_manager,
+                                                        seat->wl_seat);
+  zwp_primary_selection_device_v1_add_listener (seat->primary_data_device,
+                                                &primary_selection_device_listener, seat);
+
   seat->data_device =
     wl_data_device_manager_get_data_device (display_wayland->data_device_manager,
                                             seat->wl_seat);
@@ -3116,6 +3159,17 @@ gdk_wayland_device_set_selection (GdkDevice             *gdk_device,
                                 _gdk_wayland_display_get_serial (display_wayland));
 }
 
+void
+gdk_wayland_seat_set_primary (GdkSeat                                *seat,
+                              struct zwp_primary_selection_source_v1 *source)
+{
+  GdkWaylandSeat *wayland_seat = GDK_WAYLAND_SEAT (seat);
+
+  if (source)
+    zwp_primary_selection_device_v1_set_selection (wayland_seat->primary_data_device,
+                                                   source);
+}
+
 struct wl_seat *
 gdk_wayland_seat_get_wl_seat (GdkSeat *seat)
 {
diff --git a/gdk/wayland/gdkdisplay-wayland.c b/gdk/wayland/gdkdisplay-wayland.c
index d708385..dc2d879 100644
--- a/gdk/wayland/gdkdisplay-wayland.c
+++ b/gdk/wayland/gdkdisplay-wayland.c
@@ -335,6 +335,12 @@ gdk_registry_handle_global (void               *data,
         wl_registry_bind (display_wayland->wl_registry,
                           id, &zwp_pointer_gestures_v1_interface, version);
     }
+  else if (strcmp (interface, "zwp_primary_selection_device_manager_v1") == 0)
+    {
+      display_wayland->primary_selection_manager =
+        wl_registry_bind(display_wayland->wl_registry, id,
+                         &zwp_primary_selection_device_manager_v1_interface, 1);
+    }
   else
     handled = FALSE;
 
diff --git a/gdk/wayland/gdkdisplay-wayland.h b/gdk/wayland/gdkdisplay-wayland.h
index 74b970f..c5eabff 100644
--- a/gdk/wayland/gdkdisplay-wayland.h
+++ b/gdk/wayland/gdkdisplay-wayland.h
@@ -74,6 +74,7 @@ struct _GdkWaylandDisplay
   struct wl_data_device_manager *data_device_manager;
   struct wl_subcompositor *subcompositor;
   struct zwp_pointer_gestures_v1 *pointer_gestures;
+  struct zwp_primary_selection_device_manager_v1 *primary_selection_manager;
 
   GList *async_roundtrips;
 
diff --git a/gdk/wayland/gdkprivate-wayland.h b/gdk/wayland/gdkprivate-wayland.h
index 549851c..9732359 100644
--- a/gdk/wayland/gdkprivate-wayland.h
+++ b/gdk/wayland/gdkprivate-wayland.h
@@ -37,6 +37,7 @@
 #include <xkbcommon/xkbcommon.h>
 
 #include "gdkinternals.h"
+#include "primary-selection-unstable-v1-client-protocol.h"
 
 #include "config.h"
 
@@ -190,6 +191,9 @@ struct wl_data_device * gdk_wayland_device_get_data_device (GdkDevice *gdk_devic
 void gdk_wayland_device_set_selection (GdkDevice             *gdk_device,
                                        struct wl_data_source *source);
 
+void gdk_wayland_seat_set_primary (GdkSeat                                *seat,
+                                   struct zwp_primary_selection_source_v1 *source);
+
 GdkDragContext * gdk_wayland_device_get_drop_context (GdkDevice *gdk_device);
 
 void gdk_wayland_device_unset_touch_grab (GdkDevice        *device,
@@ -242,11 +246,14 @@ void gdk_wayland_selection_free (GdkWaylandSelection *selection);
 
 void gdk_wayland_selection_ensure_offer (GdkDisplay           *display,
                                          struct wl_data_offer *wl_offer);
+void gdk_wayland_selection_ensure_primary_offer (GdkDisplay                            *display,
+                                                 struct zwp_primary_selection_offer_v1 *wp_offer);
+
 void gdk_wayland_selection_set_offer (GdkDisplay           *display,
                                       GdkAtom               selection,
-                                      struct wl_data_offer *wl_offer);
-struct wl_data_offer * gdk_wayland_selection_get_offer (GdkDisplay *display,
-                                                        GdkAtom     selection);
+                                      gpointer              offer);
+gpointer gdk_wayland_selection_get_offer (GdkDisplay *display,
+                                          GdkAtom     selection);
 GList * gdk_wayland_selection_get_targets (GdkDisplay *display,
                                            GdkAtom     selection);
 
diff --git a/gdk/wayland/gdkselection-wayland.c b/gdk/wayland/gdkselection-wayland.c
index 5ed13ae..50cda6e 100644
--- a/gdk/wayland/gdkselection-wayland.c
+++ b/gdk/wayland/gdkselection-wayland.c
@@ -33,6 +33,8 @@
 
 #include <string.h>
 
+#define READ_BUFFER_SIZE 1000
+
 typedef struct _SelectionBuffer SelectionBuffer;
 typedef struct _StoredSelection StoredSelection;
 typedef struct _AsyncWriteData AsyncWriteData;
@@ -67,7 +69,8 @@ struct _DataSourceData
 
 struct _DataOfferData
 {
-  struct wl_data_offer *offer;
+  GDestroyNotify destroy_notify;
+  gpointer offer;
   GList *targets; /* List of GdkAtom */
 };
 
@@ -79,17 +82,19 @@ struct _AsyncWriteData
 };
 
 enum {
+  ATOM_PRIMARY,
   ATOM_CLIPBOARD,
   ATOM_DND
 };
 
-static GdkAtom atoms[2] = { 0 };
+static GdkAtom atoms[3] = { 0 };
 
 struct _GdkWaylandSelection
 {
   /* Destination-side data */
   DataOfferData *dnd_offer;
   DataOfferData *clipboard_offer;
+  DataOfferData *primary_offer;
   GHashTable *offers; /* Currently alive offers, Hashtable of wl_data_offer->DataOfferData */
   GHashTable *selection_buffers; /* Hashtable of target_atom->SelectionBuffer */
 
@@ -98,6 +103,9 @@ struct _GdkWaylandSelection
   GArray *source_targets;
   GdkAtom requested_target;
 
+  struct zwp_primary_selection_source_v1 *primary_source;
+  GdkWindow *primary_owner;
+
   struct wl_data_source *clipboard_source;
   GdkWindow *clipboard_owner;
 
@@ -224,19 +232,23 @@ selection_buffer_read_cb (GObject      *object,
                           gpointer      user_data)
 {
   SelectionBuffer *buffer = user_data;
+  gboolean finished = TRUE;
   GError *error = NULL;
   GBytes *bytes;
 
   bytes = g_input_stream_read_bytes_finish (buffer->stream, result, &error);
 
-  if (bytes && g_bytes_get_size (bytes) > 0)
+  if (bytes)
     {
+      finished = g_bytes_get_size (bytes) < READ_BUFFER_SIZE;
       selection_buffer_append_data (buffer,
                                     g_bytes_get_data (bytes, NULL),
                                     g_bytes_get_size (bytes));
-      selection_buffer_read (buffer);
       g_bytes_unref (bytes);
     }
+
+  if (!finished)
+    selection_buffer_read (buffer);
   else
     {
       if (error)
@@ -250,9 +262,6 @@ selection_buffer_read_cb (GObject      *object,
       g_input_stream_close (buffer->stream, NULL, NULL);
       g_clear_object (&buffer->stream);
       g_clear_object (&buffer->cancellable);
-
-      if (bytes)
-        g_bytes_unref (bytes);
     }
 
   selection_buffer_unref (buffer);
@@ -262,18 +271,20 @@ static void
 selection_buffer_read (SelectionBuffer *buffer)
 {
   selection_buffer_ref (buffer);
-  g_input_stream_read_bytes_async (buffer->stream, 1000, G_PRIORITY_DEFAULT,
+  g_input_stream_read_bytes_async (buffer->stream, READ_BUFFER_SIZE, G_PRIORITY_DEFAULT,
                                    buffer->cancellable, selection_buffer_read_cb,
                                    buffer);
 }
 
 static DataOfferData *
-data_offer_data_new (struct wl_data_offer *offer)
+data_offer_data_new (gpointer       offer,
+                     GDestroyNotify destroy_notify)
 {
   DataOfferData *info;
 
   info = g_slice_new0 (DataOfferData);
   info->offer = offer;
+  info->destroy_notify = destroy_notify;
 
   return info;
 }
@@ -281,7 +292,7 @@ data_offer_data_new (struct wl_data_offer *offer)
 static void
 data_offer_data_free (DataOfferData *info)
 {
-  wl_data_offer_destroy (info->offer);
+  info->destroy_notify (info->offer);
   g_list_free (info->targets);
   g_slice_free (DataOfferData, info);
 }
@@ -292,6 +303,7 @@ gdk_wayland_selection_new (void)
   GdkWaylandSelection *selection;
 
   /* init atoms */
+  atoms[ATOM_PRIMARY] = gdk_atom_intern_static_string ("PRIMARY");
   atoms[ATOM_CLIPBOARD] = gdk_atom_intern_static_string ("CLIPBOARD");
   atoms[ATOM_DND] = gdk_atom_intern_static_string ("GdkWaylandSelection");
 
@@ -325,6 +337,8 @@ gdk_wayland_selection_free (GdkWaylandSelection *selection)
   if (selection->stored_selection.fd > 0)
     close (selection->stored_selection.fd);
 
+  if (selection->primary_source)
+    zwp_primary_selection_source_v1_destroy (selection->primary_source);
   if (selection->clipboard_source)
     wl_data_source_destroy (selection->clipboard_source);
   if (selection->dnd_source)
@@ -415,11 +429,34 @@ static const struct wl_data_offer_listener data_offer_listener = {
   data_offer_action
 };
 
+static void
+primary_offer_offer (void                                  *data,
+                     struct zwp_primary_selection_offer_v1 *wp_offer,
+                     const char                            *type)
+{
+  GdkWaylandSelection *selection = data;
+  DataOfferData *info;
+  GdkAtom atom = gdk_atom_intern (type, FALSE);
+
+  info = g_hash_table_lookup (selection->offers, wp_offer);
+
+  if (!info || g_list_find (info->targets, atom))
+    return;
+
+  info->targets = g_list_prepend (info->targets, atom);
+}
+
+static const struct zwp_primary_selection_offer_v1_listener primary_offer_listener = {
+  primary_offer_offer,
+};
+
 DataOfferData *
 selection_lookup_offer_by_atom (GdkWaylandSelection *selection,
                                 GdkAtom              selection_atom)
 {
-  if (selection_atom == atoms[ATOM_CLIPBOARD])
+  if (selection_atom == atoms[ATOM_PRIMARY])
+    return selection->primary_offer;
+  else if (selection_atom == atoms[ATOM_CLIPBOARD])
     return selection->clipboard_offer;
   else if (selection_atom == atoms[ATOM_DND])
     return selection->dnd_offer;
@@ -438,7 +475,8 @@ gdk_wayland_selection_ensure_offer (GdkDisplay           *display,
 
   if (!info)
     {
-      info = data_offer_data_new (wl_offer);
+      info = data_offer_data_new (wl_offer,
+                                  (GDestroyNotify) wl_data_offer_destroy);
       g_hash_table_insert (selection->offers, wl_offer, info);
       wl_data_offer_add_listener (wl_offer,
                                   &data_offer_listener,
@@ -447,9 +485,29 @@ gdk_wayland_selection_ensure_offer (GdkDisplay           *display,
 }
 
 void
-gdk_wayland_selection_set_offer (GdkDisplay           *display,
-                                 GdkAtom               selection_atom,
-                                 struct wl_data_offer *wl_offer)
+gdk_wayland_selection_ensure_primary_offer (GdkDisplay                            *display,
+                                            struct zwp_primary_selection_offer_v1 *wp_offer)
+{
+  GdkWaylandSelection *selection = gdk_wayland_display_get_selection (display);
+  DataOfferData *info;
+
+  info = g_hash_table_lookup (selection->offers, wp_offer);
+
+  if (!info)
+    {
+      info = data_offer_data_new (wp_offer,
+                                  (GDestroyNotify) zwp_primary_selection_offer_v1_destroy);
+      g_hash_table_insert (selection->offers, wp_offer, info);
+      zwp_primary_selection_offer_v1_add_listener (wp_offer,
+                                                   &primary_offer_listener,
+                                                   selection);
+    }
+}
+
+void
+gdk_wayland_selection_set_offer (GdkDisplay *display,
+                                 GdkAtom     selection_atom,
+                                 gpointer    wl_offer)
 {
   GdkWaylandSelection *selection = gdk_wayland_display_get_selection (display);
   struct wl_data_offer *prev_offer;
@@ -462,7 +520,9 @@ gdk_wayland_selection_set_offer (GdkDisplay           *display,
   if (prev_offer)
     g_hash_table_remove (selection->offers, prev_offer);
 
-  if (selection_atom == atoms[ATOM_CLIPBOARD])
+  if (selection_atom == atoms[ATOM_PRIMARY])
+    selection->primary_offer = info;
+  else if (selection_atom == atoms[ATOM_CLIPBOARD])
     selection->clipboard_offer = info;
   else if (selection_atom == atoms[ATOM_DND])
     selection->dnd_offer = info;
@@ -471,7 +531,7 @@ gdk_wayland_selection_set_offer (GdkDisplay           *display,
   g_hash_table_remove_all (selection->selection_buffers);
 }
 
-struct wl_data_offer *
+gpointer
 gdk_wayland_selection_get_offer (GdkDisplay *display,
                                  GdkAtom     selection_atom)
 {
@@ -718,18 +778,10 @@ gdk_wayland_selection_source_handles_target (GdkWaylandSelection *wayland_select
 static gboolean
 gdk_wayland_selection_request_target (GdkWaylandSelection *wayland_selection,
                                       GdkWindow           *window,
+                                      GdkAtom              selection,
                                       GdkAtom              target,
                                       gint                 fd)
 {
-  GdkAtom selection;
-
-  if (wayland_selection->clipboard_owner == window)
-    selection = atoms[ATOM_CLIPBOARD];
-  else if (wayland_selection->dnd_owner == window)
-    selection = atoms[ATOM_DND];
-  else
-    return FALSE;
-
   if (wayland_selection->stored_selection.fd == fd &&
       wayland_selection->requested_target == target)
     return FALSE;
@@ -766,6 +818,7 @@ data_source_target (void                  *data,
 {
   GdkWaylandSelection *wayland_selection = data;
   GdkWindow *window = NULL;
+  GdkAtom selection;
 
   g_debug (G_STRLOC ": %s source = %p, mime_type = %s",
            G_STRFUNC, source, mime_type);
@@ -774,14 +827,21 @@ data_source_target (void                  *data,
     return;
 
   if (source == wayland_selection->dnd_source)
-    window = wayland_selection->dnd_owner;
+    {
+      selection = atoms[ATOM_DND];
+      window = wayland_selection->dnd_owner;
+    }
   else if (source == wayland_selection->clipboard_source)
-    window = wayland_selection->clipboard_owner;
+    {
+      selection = atoms[ATOM_CLIPBOARD];
+      window = wayland_selection->clipboard_owner;
+    }
 
   if (!window)
     return;
 
   gdk_wayland_selection_request_target (wayland_selection, window,
+                                        selection,
                                         gdk_atom_intern (mime_type, FALSE),
                                         -1);
 }
@@ -794,6 +854,7 @@ data_source_send (void                  *data,
 {
   GdkWaylandSelection *wayland_selection = data;
   GdkWindow *window;
+  GdkAtom selection;
 
   g_debug (G_STRLOC ": %s source = %p, mime_type = %s, fd = %d",
            G_STRFUNC, source, mime_type, fd);
@@ -805,9 +866,20 @@ data_source_send (void                  *data,
     }
 
   if (source == wayland_selection->dnd_source)
-    window = wayland_selection->dnd_owner;
+    {
+      window = wayland_selection->dnd_owner;
+      selection = atoms[ATOM_DND];
+    }
   else if (source == wayland_selection->clipboard_source)
-    window = wayland_selection->clipboard_owner;
+    {
+      window = wayland_selection->clipboard_owner;
+      selection = atoms[ATOM_CLIPBOARD];
+    }
+  else if (source == (gpointer) wayland_selection->primary_source)
+    {
+      window = wayland_selection->primary_owner;
+      selection = atoms[ATOM_PRIMARY];
+    }
   else
     {
       close (fd);
@@ -818,6 +890,7 @@ data_source_send (void                  *data,
     return;
 
   if (!gdk_wayland_selection_request_target (wayland_selection, window,
+                                             selection,
                                              gdk_atom_intern (mime_type, FALSE),
                                              fd))
     gdk_wayland_selection_check_write (wayland_selection);
@@ -839,6 +912,8 @@ data_source_cancelled (void                  *data,
 
   if (source == wayland_selection->dnd_source)
     atom = atoms[ATOM_DND];
+  else if (source == (gpointer) wayland_selection->primary_source)
+    atom = atoms[ATOM_PRIMARY];
   else if (source == wayland_selection->clipboard_source)
     atom = atoms[ATOM_CLIPBOARD];
   else
@@ -919,15 +994,61 @@ static const struct wl_data_source_listener data_source_listener = {
   data_source_action,
 };
 
+static void
+primary_source_send (void                                   *data,
+                     struct zwp_primary_selection_source_v1 *source,
+                     const char                             *mime_type,
+                     int32_t                                 fd)
+{
+  GdkWaylandSelection *wayland_selection = data;
+
+  g_debug (G_STRLOC ": %s source = %p, mime_type = %s, fd = %d",
+           G_STRFUNC, source, mime_type, fd);
+
+  if (!mime_type || !wayland_selection->primary_owner)
+    {
+      close (fd);
+      return;
+    }
+
+  if (!gdk_wayland_selection_request_target (wayland_selection,
+                                             wayland_selection->primary_owner,
+                                             atoms[ATOM_PRIMARY],
+                                             gdk_atom_intern (mime_type, FALSE),
+                                             fd))
+    gdk_wayland_selection_check_write (wayland_selection);
+}
+
+static void
+primary_source_cancelled (void                                   *data,
+                          struct zwp_primary_selection_source_v1 *source)
+{
+  GdkDisplay *display;
+  GdkAtom atom;
+
+  g_debug (G_STRLOC ": %s source = %p",
+           G_STRFUNC, source);
+
+  display = gdk_display_get_default ();
+
+  atom = atoms[ATOM_PRIMARY];
+  gdk_selection_owner_set (NULL, atom, GDK_CURRENT_TIME, TRUE);
+  gdk_wayland_selection_unset_data_source (display, atom);
+}
+
+static const struct zwp_primary_selection_source_v1_listener primary_source_listener = {
+  primary_source_send,
+  primary_source_cancelled,
+};
+
 struct wl_data_source *
 gdk_wayland_selection_get_data_source (GdkWindow *owner,
                                        GdkAtom    selection)
 {
   GdkDisplay *display = gdk_window_get_display (owner);
   GdkWaylandSelection *wayland_selection = gdk_wayland_display_get_selection (display);
-  struct wl_data_source *source = NULL;
+  gpointer source = NULL;
   GdkWaylandDisplay *display_wayland;
-  gboolean is_clipboard = FALSE;
 
   if (selection == atoms[ATOM_DND])
     {
@@ -935,6 +1056,18 @@ gdk_wayland_selection_get_data_source (GdkWindow *owner,
           (!owner || owner == wayland_selection->dnd_owner))
         return wayland_selection->dnd_source;
     }
+  else if (selection == atoms[ATOM_PRIMARY])
+    {
+      if (wayland_selection->primary_source &&
+          (!owner || owner == wayland_selection->primary_owner))
+        return (gpointer) wayland_selection->primary_source;
+
+      if (wayland_selection->primary_source)
+        {
+          zwp_primary_selection_source_v1_destroy (wayland_selection->primary_source);
+          wayland_selection->primary_source = NULL;
+        }
+    }
   else if (selection == atoms[ATOM_CLIPBOARD])
     {
       if (wayland_selection->clipboard_source &&
@@ -946,8 +1079,6 @@ gdk_wayland_selection_get_data_source (GdkWindow *owner,
           wl_data_source_destroy (wayland_selection->clipboard_source);
           wayland_selection->clipboard_source = NULL;
         }
-
-      is_clipboard = TRUE;
     }
   else
     return NULL;
@@ -957,15 +1088,27 @@ gdk_wayland_selection_get_data_source (GdkWindow *owner,
 
   display_wayland = GDK_WAYLAND_DISPLAY (gdk_window_get_display (owner));
 
-  source = wl_data_device_manager_create_data_source (display_wayland->data_device_manager);
-  wl_data_source_add_listener (source,
-                               &data_source_listener,
-                               wayland_selection);
-
-  if (is_clipboard)
-    wayland_selection->clipboard_source = source;
+  if (selection == atoms[ATOM_PRIMARY])
+    {
+      source = zwp_primary_selection_device_manager_v1_create_source 
(display_wayland->primary_selection_manager);
+      zwp_primary_selection_source_v1_add_listener (source,
+                                                    &primary_source_listener,
+                                                    wayland_selection);
+    }
   else
+    {
+      source = wl_data_device_manager_create_data_source (display_wayland->data_device_manager);
+      wl_data_source_add_listener (source,
+                                   &data_source_listener,
+                                   wayland_selection);
+    }
+
+  if (selection == atoms[ATOM_DND])
     wayland_selection->dnd_source = source;
+  else if (selection == atoms[ATOM_PRIMARY])
+    wayland_selection->primary_source = source;
+  else if (selection == atoms[ATOM_CLIPBOARD])
+    wayland_selection->clipboard_source = source;
 
   return source;
 }
@@ -990,6 +1133,18 @@ gdk_wayland_selection_unset_data_source (GdkDisplay *display,
           wayland_selection->clipboard_source = NULL;
         }
     }
+  else if (selection == atoms[ATOM_PRIMARY])
+    {
+      GdkSeat *seat = gdk_display_get_default_seat (display);
+
+      gdk_wayland_seat_set_primary (seat, NULL);
+
+      if (wayland_selection->primary_source)
+        {
+          zwp_primary_selection_source_v1_destroy (wayland_selection->primary_source);
+          wayland_selection->primary_source = NULL;
+        }
+    }
   else if (selection == atoms[ATOM_DND])
     {
       wayland_selection->dnd_source = NULL;
@@ -1004,6 +1159,8 @@ _gdk_wayland_display_get_selection_owner (GdkDisplay *display,
 
   if (selection == atoms[ATOM_CLIPBOARD])
     return wayland_selection->clipboard_owner;
+  else if (selection == atoms[ATOM_PRIMARY])
+    return wayland_selection->primary_owner;
   else if (selection == atoms[ATOM_DND])
     return wayland_selection->dnd_owner;
 
@@ -1024,6 +1181,11 @@ _gdk_wayland_display_set_selection_owner (GdkDisplay *display,
       wayland_selection->clipboard_owner = owner;
       return TRUE;
     }
+  else if (selection == atoms[ATOM_PRIMARY])
+    {
+      wayland_selection->primary_owner = owner;
+      return TRUE;
+    }
   else if (selection == atoms[ATOM_DND])
     {
       wayland_selection->dnd_owner = owner;
@@ -1088,6 +1250,26 @@ _gdk_wayland_display_get_selection_property (GdkDisplay  *display,
   return len;
 }
 
+static void
+emit_empty_selection_notify (GdkWindow *requestor,
+                             GdkAtom    selection,
+                             GdkAtom    target)
+{
+  GdkEvent *event;
+
+  event = gdk_event_new (GDK_SELECTION_NOTIFY);
+  event->selection.window = g_object_ref (requestor);
+  event->selection.send_event = FALSE;
+  event->selection.selection = selection;
+  event->selection.target = target;
+  event->selection.property = GDK_NONE;
+  event->selection.time = GDK_CURRENT_TIME;
+  event->selection.requestor = g_object_ref (requestor);
+
+  gdk_event_put (event);
+  gdk_event_free (event);
+}
+
 void
 _gdk_wayland_display_convert_selection (GdkDisplay *display,
                                         GdkWindow  *requestor,
@@ -1097,7 +1279,7 @@ _gdk_wayland_display_convert_selection (GdkDisplay *display,
 {
   GdkWaylandSelection *wayland_selection = gdk_wayland_display_get_selection (display);
   SelectionBuffer *buffer_data;
-  struct wl_data_offer *offer;
+  gpointer offer;
   gchar *mimetype;
   GList *target_list;
 
@@ -1106,28 +1288,25 @@ _gdk_wayland_display_convert_selection (GdkDisplay *display,
 
   if (!offer || target == gdk_atom_intern_static_string ("DELETE"))
     {
-      GdkEvent *event;
-
-      event = gdk_event_new (GDK_SELECTION_NOTIFY);
-      event->selection.window = g_object_ref (requestor);
-      event->selection.send_event = FALSE;
-      event->selection.selection = selection;
-      event->selection.target = target;
-      event->selection.property = GDK_NONE;
-      event->selection.time = GDK_CURRENT_TIME;
-      event->selection.requestor = g_object_ref (requestor);
-
-      gdk_event_put (event);
-      gdk_event_free (event);
+      emit_empty_selection_notify (requestor, selection, target);
       return;
     }
 
   mimetype = gdk_atom_name (target);
 
   if (target != gdk_atom_intern_static_string ("TARGETS"))
-    wl_data_offer_accept (offer,
-                          _gdk_wayland_display_get_serial (GDK_WAYLAND_DISPLAY (display)),
-                          mimetype);
+    {
+      if (!g_list_find (target_list, GDK_ATOM_TO_POINTER (target)))
+        {
+          emit_empty_selection_notify (requestor, selection, target);
+          return;
+        }
+
+      if (selection != atoms[ATOM_PRIMARY])
+        wl_data_offer_accept (offer,
+                              _gdk_wayland_display_get_serial (GDK_WAYLAND_DISPLAY (display)),
+                              mimetype);
+    }
 
   buffer_data = g_hash_table_lookup (wayland_selection->selection_buffers,
                                      target);
@@ -1154,7 +1333,12 @@ _gdk_wayland_display_convert_selection (GdkDisplay *display,
       else
         {
           g_unix_open_pipe (pipe_fd, FD_CLOEXEC, NULL);
-          wl_data_offer_receive (offer, mimetype, pipe_fd[1]);
+
+          if (selection == atoms[ATOM_PRIMARY])
+            zwp_primary_selection_offer_v1_receive (offer, mimetype, pipe_fd[1]);
+          else
+            wl_data_offer_receive (offer, mimetype, pipe_fd[1]);
+
           stream = g_unix_input_stream_new (pipe_fd[0], TRUE);
           close (pipe_fd[1]);
         }
@@ -1239,7 +1423,7 @@ gdk_wayland_selection_add_targets (GdkWindow *window,
 {
   GdkDisplay *display = gdk_window_get_display (window);
   GdkWaylandSelection *wayland_selection = gdk_wayland_display_get_selection (display);
-  struct wl_data_source *data_source;
+  gpointer data_source;
   guint i;
 
   g_return_if_fail (GDK_IS_WINDOW (window));
@@ -1268,6 +1452,13 @@ gdk_wayland_selection_add_targets (GdkWindow *window,
       device = gdk_seat_get_pointer (gdk_display_get_default_seat (display));
       gdk_wayland_device_set_selection (device, data_source);
     }
+  else if (selection == atoms[ATOM_PRIMARY])
+    {
+      GdkSeat *seat;
+
+      seat = gdk_display_get_default_seat (display);
+      gdk_wayland_seat_set_primary (seat, data_source);
+    }
 }
 
 void


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