[gtk+] wayland: Implement the (so far internal) primary selection protocol



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

    wayland: Implement the (so far internal) primary selection protocol
    
    Implement it using the internal copy of the protocol. Otherwise,
    we just deal with it the same than clipboard selection, just mapping
    it to the PRIMARY atom instead of the CLIPBOARD one.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=762561

 gdk/wayland/gdkdevice-wayland.c    |   62 ++++++++-
 gdk/wayland/gdkdisplay-wayland.c   |    6 +
 gdk/wayland/gdkdisplay-wayland.h   |    1 +
 gdk/wayland/gdkprivate-wayland.h   |   13 ++-
 gdk/wayland/gdkselection-wayland.c |  293 +++++++++++++++++++++++++++++-------
 5 files changed, 315 insertions(+), 60 deletions(-)
---
diff --git a/gdk/wayland/gdkdevice-wayland.c b/gdk/wayland/gdkdevice-wayland.c
index f95b65a..e093bc0 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 gtk_primary_selection_device *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 gtk_primary_selection_device *gtk_primary_selection_device,
+                              struct gtk_primary_selection_offer  *gtk_primary_offer)
+{
+  GdkWaylandDeviceData *device = (GdkWaylandDeviceData *) data;
+
+  GDK_NOTE (EVENTS,
+            g_message ("primary selection offer, device %p, data offer %p",
+                       gtk_primary_selection_device, gtk_primary_offer));
+
+  gdk_wayland_selection_ensure_primary_offer (device->display, gtk_primary_offer);
+}
+
+static void
+primary_selection_selection (void                                *data,
+                             struct gtk_primary_selection_device *gtk_primary_selection_device,
+                             struct gtk_primary_selection_offer  *gtk_primary_offer)
+{
+  GdkWaylandDeviceData *device = (GdkWaylandDeviceData *) data;
+  GdkAtom selection;
+
+  GDK_NOTE (EVENTS,
+            g_message ("primary selection selection, device %p, data offer %p",
+                       gtk_primary_selection_device, gtk_primary_offer));
+
+  selection = gdk_atom_intern_static_string ("PRIMARY");
+  gdk_wayland_selection_set_offer (device->display, selection, gtk_primary_offer);
+  emit_selection_owner_change (device->keyboard_focus, selection);
+}
+
+static const struct gtk_primary_selection_device_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 =
+    gtk_primary_selection_device_manager_get_device (display_wayland->primary_selection_manager,
+                                                     seat->wl_seat);
+  gtk_primary_selection_device_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);
@@ -3118,6 +3161,23 @@ gdk_wayland_device_set_selection (GdkDevice             *gdk_device,
                                 _gdk_wayland_display_get_serial (display_wayland));
 }
 
+void
+gdk_wayland_seat_set_primary (GdkSeat                             *seat,
+                              struct gtk_primary_selection_source *source)
+{
+  GdkWaylandSeat *wayland_seat = GDK_WAYLAND_SEAT (seat);
+  GdkWaylandDisplay *display_wayland;
+  guint32 serial;
+
+  if (source)
+    {
+      display_wayland = GDK_WAYLAND_DISPLAY (gdk_seat_get_display (seat));
+      serial = _gdk_wayland_display_get_serial (display_wayland);
+      gtk_primary_selection_device_set_selection (wayland_seat->primary_data_device,
+                                                  source, serial);
+    }
+}
+
 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..fb9f26f 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, "gtk_primary_selection_device_manager") == 0)
+    {
+      display_wayland->primary_selection_manager =
+        wl_registry_bind(display_wayland->wl_registry, id,
+                         &gtk_primary_selection_device_manager_interface, 1);
+    }
   else
     handled = FALSE;
 
diff --git a/gdk/wayland/gdkdisplay-wayland.h b/gdk/wayland/gdkdisplay-wayland.h
index 74b970f..bb10196 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 gtk_primary_selection_device_manager *primary_selection_manager;
 
   GList *async_roundtrips;
 
diff --git a/gdk/wayland/gdkprivate-wayland.h b/gdk/wayland/gdkprivate-wayland.h
index 95ca47e..a8df4dd 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 "gtk-primary-selection-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 gtk_primary_selection_source *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 gtk_primary_selection_offer *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..c5e0dc5 100644
--- a/gdk/wayland/gdkselection-wayland.c
+++ b/gdk/wayland/gdkselection-wayland.c
@@ -67,7 +67,8 @@ struct _DataSourceData
 
 struct _DataOfferData
 {
-  struct wl_data_offer *offer;
+  GDestroyNotify destroy_notify;
+  gpointer offer_data;
   GList *targets; /* List of GdkAtom */
 };
 
@@ -79,17 +80,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 +101,9 @@ struct _GdkWaylandSelection
   GArray *source_targets;
   GdkAtom requested_target;
 
+  struct gtk_primary_selection_source *primary_source;
+  GdkWindow *primary_owner;
+
   struct wl_data_source *clipboard_source;
   GdkWindow *clipboard_owner;
 
@@ -268,12 +274,14 @@ selection_buffer_read (SelectionBuffer *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->offer_data = offer;
+  info->destroy_notify = destroy_notify;
 
   return info;
 }
@@ -281,7 +289,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_data);
   g_list_free (info->targets);
   g_slice_free (DataOfferData, info);
 }
@@ -292,6 +300,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 +334,8 @@ gdk_wayland_selection_free (GdkWaylandSelection *selection)
   if (selection->stored_selection.fd > 0)
     close (selection->stored_selection.fd);
 
+  if (selection->primary_source)
+    gtk_primary_selection_source_destroy (selection->primary_source);
   if (selection->clipboard_source)
     wl_data_source_destroy (selection->clipboard_source);
   if (selection->dnd_source)
@@ -415,11 +426,34 @@ static const struct wl_data_offer_listener data_offer_listener = {
   data_offer_action
 };
 
+static void
+primary_offer_offer (void                               *data,
+                     struct gtk_primary_selection_offer *gtk_offer,
+                     const char                         *type)
+{
+  GdkWaylandSelection *selection = data;
+  DataOfferData *info;
+  GdkAtom atom = gdk_atom_intern (type, FALSE);
+
+  info = g_hash_table_lookup (selection->offers, gtk_offer);
+
+  if (!info || g_list_find (info->targets, atom))
+    return;
+
+  info->targets = g_list_prepend (info->targets, atom);
+}
+
+static const struct gtk_primary_selection_offer_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 +472,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 +482,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 gtk_primary_selection_offer *gtk_offer)
+{
+  GdkWaylandSelection *selection = gdk_wayland_display_get_selection (display);
+  DataOfferData *info;
+
+  info = g_hash_table_lookup (selection->offers, gtk_offer);
+
+  if (!info)
+    {
+      info = data_offer_data_new (gtk_offer,
+                                  (GDestroyNotify) gtk_primary_selection_offer_destroy);
+      g_hash_table_insert (selection->offers, gtk_offer, info);
+      gtk_primary_selection_offer_add_listener (gtk_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 +517,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 +528,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)
 {
@@ -481,7 +538,7 @@ gdk_wayland_selection_get_offer (GdkDisplay *display,
   info = selection_lookup_offer_by_atom (selection, selection_atom);
 
   if (info)
-    return info->offer;
+    return info->offer_data;
 
   return NULL;
 }
@@ -718,18 +775,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 +815,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 +824,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 +851,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 +863,15 @@ 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
     {
       close (fd);
@@ -818,6 +882,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);
@@ -919,15 +984,61 @@ static const struct wl_data_source_listener data_source_listener = {
   data_source_action,
 };
 
+static void
+primary_source_send (void                                *data,
+                     struct gtk_primary_selection_source *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 gtk_primary_selection_source *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 gtk_primary_selection_source_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 +1046,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)
+        {
+          gtk_primary_selection_source_destroy (wayland_selection->primary_source);
+          wayland_selection->primary_source = NULL;
+        }
+    }
   else if (selection == atoms[ATOM_CLIPBOARD])
     {
       if (wayland_selection->clipboard_source &&
@@ -946,8 +1069,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 +1078,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 = gtk_primary_selection_device_manager_create_source 
(display_wayland->primary_selection_manager);
+      gtk_primary_selection_source_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 +1123,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)
+        {
+          gtk_primary_selection_source_destroy (wayland_selection->primary_source);
+          wayland_selection->primary_source = NULL;
+        }
+    }
   else if (selection == atoms[ATOM_DND])
     {
       wayland_selection->dnd_source = NULL;
@@ -1004,6 +1149,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 +1171,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 +1240,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 +1269,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 +1278,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 +1323,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])
+            gtk_primary_selection_offer_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 +1413,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 +1442,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]