[gtk+/wip/wayland-dnd-actions: 20/23] wayland: Split handling of clipboard/DnD data offers



commit 94795e1be8434ede272ccaaa12ffdaafeb666251
Author: Carlos Garnacho <carlosg gnome org>
Date:   Tue Apr 7 16:40:57 2015 +0200

    wayland: Split handling of clipboard/DnD data offers
    
    We currently only hold the last offer received, which is wrong, as both
    are independent and have different life cycles.

 gdk/wayland/gdkdevice-wayland.c    |   19 +++--
 gdk/wayland/gdkdnd-wayland.c       |   21 +++--
 gdk/wayland/gdkprivate-wayland.h   |    9 ++-
 gdk/wayland/gdkselection-wayland.c |  162 ++++++++++++++++++++++++++----------
 4 files changed, 150 insertions(+), 61 deletions(-)
---
diff --git a/gdk/wayland/gdkdevice-wayland.c b/gdk/wayland/gdkdevice-wayland.c
index 0708372..14ba5ea 100644
--- a/gdk/wayland/gdkdevice-wayland.c
+++ b/gdk/wayland/gdkdevice-wayland.c
@@ -592,8 +592,7 @@ data_device_data_offer (void                  *data,
             g_message ("data device data offer, data device %p, offer %p",
                        data_device, offer));
 
-  gdk_wayland_selection_set_offer (device->display, offer);
-  emit_selection_owner_change_forall (gdk_atom_intern_static_string ("GdkWaylandSelection"));
+  gdk_wayland_selection_ensure_offer (device->display, offer);
 }
 
 static void
@@ -607,6 +606,7 @@ data_device_enter (void                  *data,
 {
   GdkWaylandDeviceData *device = (GdkWaylandDeviceData *)data;
   GdkWindow *dest_window, *dnd_owner;
+  GdkAtom selection;
 
   dest_window = wl_surface_get_user_data (surface);
 
@@ -624,7 +624,8 @@ data_device_enter (void                  *data,
 
   gdk_wayland_drop_context_update_targets (device->drop_context);
 
-  dnd_owner = gdk_selection_owner_get_for_display (device->display, gdk_drag_get_selection 
(device->drop_context));
+  selection = gdk_drag_get_selection (device->drop_context);
+  dnd_owner = gdk_selection_owner_get_for_display (device->display, selection);
 
   if (!dnd_owner)
     dnd_owner = device->foreign_dnd_window;
@@ -638,9 +639,9 @@ data_device_enter (void                  *data,
                                         wl_fixed_to_double (y));
   _gdk_wayland_drag_context_emit_event (device->drop_context, GDK_DRAG_ENTER,
                                         GDK_CURRENT_TIME);
-  gdk_wayland_selection_set_offer (device->display, offer);
-  emit_selection_owner_change (dest_window,
-                               gdk_atom_intern_static_string ("GdkWaylandSelection"));
+
+  gdk_wayland_selection_set_offer (device->display, selection, offer);
+  emit_selection_owner_change_forall (selection);
 }
 
 static void
@@ -720,13 +721,15 @@ data_device_selection (void                  *data,
                        struct wl_data_offer  *offer)
 {
   GdkWaylandDeviceData *device = (GdkWaylandDeviceData *) data;
+  GdkAtom selection;
 
   GDK_NOTE (EVENTS,
             g_message ("data device selection, data device %p, data offer %p",
                        wl_data_device, offer));
 
-  gdk_wayland_selection_set_offer (device->display, offer);
-  emit_selection_owner_change_forall (gdk_atom_intern_static_string ("CLIPBOARD"));
+  selection = gdk_atom_intern_static_string ("CLIPBOARD");
+  gdk_wayland_selection_set_offer (device->display, selection, offer);
+  emit_selection_owner_change_forall (selection);
 }
 
 static const struct wl_data_device_listener data_device_listener = {
diff --git a/gdk/wayland/gdkdnd-wayland.c b/gdk/wayland/gdkdnd-wayland.c
index f558bb5..fd50c01 100644
--- a/gdk/wayland/gdkdnd-wayland.c
+++ b/gdk/wayland/gdkdnd-wayland.c
@@ -43,7 +43,6 @@ struct _GdkWaylandDragContext
   GdkWindow *dnd_window;
   struct wl_surface *dnd_surface;
   struct wl_data_source *data_source;
-  struct wl_data_offer *offer;
   uint32_t serial;
   gdouble x;
   gdouble y;
@@ -196,8 +195,10 @@ gdk_wayland_drop_context_set_status (GdkDragContext *context,
   struct wl_data_offer *wl_offer;
 
   context_wayland = GDK_WAYLAND_DRAG_CONTEXT (context);
-  display = gdk_window_get_display (context->source_window);
-  wl_offer = gdk_wayland_selection_get_offer (display);
+
+  display = gdk_device_get_display (gdk_drag_context_get_device (context));
+  wl_offer = gdk_wayland_selection_get_offer (display,
+                                              gdk_drag_get_selection (context));
 
   if (!wl_offer)
     return;
@@ -244,10 +245,15 @@ gdk_wayland_drag_context_drop_finish (GdkDragContext *context,
                                      gboolean        success,
                                      guint32         time)
 {
-  GdkDisplay *display = gdk_window_get_display (context->source_window);
+  GdkDisplay *display = gdk_device_get_display (gdk_drag_context_get_device (context));
+  GdkAtom selection;
 
-  if (gdk_selection_owner_get_for_display (display, gdk_drag_get_selection (context)))
-    gdk_wayland_selection_unset_data_source (display, gdk_drag_get_selection (context));
+  selection = gdk_drag_get_selection (context);
+
+  if (gdk_selection_owner_get_for_display (display, selection))
+    gdk_wayland_selection_unset_data_source (display, selection);
+
+  gdk_wayland_selection_set_offer (display, selection, NULL);
 }
 
 static gboolean
@@ -388,7 +394,8 @@ gdk_wayland_drop_context_update_targets (GdkDragContext *context)
   device = gdk_drag_context_get_device (context);
   display = gdk_device_get_display (device);
   g_list_free (context->targets);
-  context->targets = g_list_copy (gdk_wayland_selection_get_targets (display));
+  context->targets = g_list_copy (gdk_wayland_selection_get_targets (display,
+                                                                     gdk_drag_get_selection (context)));
 }
 
 void
diff --git a/gdk/wayland/gdkprivate-wayland.h b/gdk/wayland/gdkprivate-wayland.h
index a8211fd..01038ba 100644
--- a/gdk/wayland/gdkprivate-wayland.h
+++ b/gdk/wayland/gdkprivate-wayland.h
@@ -237,10 +237,15 @@ GdkWaylandSelection * gdk_wayland_display_get_selection (GdkDisplay *display);
 GdkWaylandSelection * gdk_wayland_selection_new (void);
 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_set_offer (GdkDisplay           *display,
+                                      GdkAtom               selection,
                                       struct wl_data_offer *wl_offer);
-struct wl_data_offer * gdk_wayland_selection_get_offer (GdkDisplay *display);
-GList * gdk_wayland_selection_get_targets (GdkDisplay *display);
+struct wl_data_offer * gdk_wayland_selection_get_offer (GdkDisplay *display,
+                                                        GdkAtom     selection);
+GList * gdk_wayland_selection_get_targets (GdkDisplay *display,
+                                           GdkAtom     selection);
 
 void     gdk_wayland_selection_store   (GdkWindow    *window,
                                         GdkAtom       type,
diff --git a/gdk/wayland/gdkselection-wayland.c b/gdk/wayland/gdkselection-wayland.c
index 12a2d30..528724a 100644
--- a/gdk/wayland/gdkselection-wayland.c
+++ b/gdk/wayland/gdkselection-wayland.c
@@ -36,6 +36,7 @@
 typedef struct _SelectionBuffer SelectionBuffer;
 typedef struct _StoredSelection StoredSelection;
 typedef struct _AsyncWriteData AsyncWriteData;
+typedef struct _DataOfferData DataOfferData;
 
 struct _SelectionBuffer
 {
@@ -64,6 +65,13 @@ struct _DataSourceData
   GdkAtom selection;
 };
 
+struct _DataOfferData
+{
+  struct wl_data_offer *offer;
+  GList *targets; /* List of GdkAtom */
+  GdkAtom requested_target;
+};
+
 struct _AsyncWriteData
 {
   GOutputStream *stream;
@@ -81,11 +89,10 @@ static GdkAtom atoms[2] = { 0 };
 struct _GdkWaylandSelection
 {
   /* Destination-side data */
-  struct wl_data_offer *offer;
-  GdkAtom source_requested_target;
-
+  DataOfferData *dnd_offer;
+  DataOfferData *clipboard_offer;
+  GHashTable *offers; /* Currently alive offers, Hashtable of wl_data_offer->DataOfferData */
   GHashTable *selection_buffers; /* Hashtable of target_atom->SelectionBuffer */
-  GList *targets; /* List of GdkAtom */
 
   /* Source-side data */
   StoredSelection stored_selection;
@@ -259,6 +266,25 @@ selection_buffer_read (SelectionBuffer *buffer)
                                    buffer);
 }
 
+static DataOfferData *
+data_offer_data_new (struct wl_data_offer *offer)
+{
+  DataOfferData *info;
+
+  info = g_slice_new0 (DataOfferData);
+  info->offer = offer;
+
+  return info;
+}
+
+static void
+data_offer_data_free (DataOfferData *info)
+{
+  wl_data_offer_destroy (info->offer);
+  g_list_free (info->targets);
+  g_slice_free (DataOfferData, info);
+}
+
 GdkWaylandSelection *
 gdk_wayland_selection_new (void)
 {
@@ -270,8 +296,11 @@ gdk_wayland_selection_new (void)
 
   selection = g_new0 (GdkWaylandSelection, 1);
   selection->selection_buffers =
-      g_hash_table_new_full (NULL, NULL, NULL,
-                             (GDestroyNotify) selection_buffer_cancel_and_unref);
+    g_hash_table_new_full (NULL, NULL, NULL,
+                           (GDestroyNotify) selection_buffer_cancel_and_unref);
+  selection->offers =
+    g_hash_table_new_full (NULL, NULL, NULL,
+                           (GDestroyNotify) data_offer_data_free);
   return selection;
 }
 
@@ -279,10 +308,7 @@ void
 gdk_wayland_selection_free (GdkWaylandSelection *selection)
 {
   g_hash_table_destroy (selection->selection_buffers);
-
-  if (selection->targets)
-    g_list_free (selection->targets);
-
+  g_hash_table_destroy (selection->offers);
   g_free (selection->stored_selection.data);
 
   if (selection->stored_selection.cancellable)
@@ -294,8 +320,6 @@ gdk_wayland_selection_free (GdkWaylandSelection *selection)
   if (selection->stored_selection.fd > 0)
     close (selection->stored_selection.fd);
 
-  if (selection->offer)
-    wl_data_offer_destroy (selection->offer);
   if (selection->clipboard_source)
     wl_data_source_destroy (selection->clipboard_source);
   if (selection->dnd_source)
@@ -310,57 +334,105 @@ data_offer_offer (void                 *data,
                   const char           *type)
 {
   GdkWaylandSelection *selection = data;
+  DataOfferData *info;
   GdkAtom atom = gdk_atom_intern (type, FALSE);
 
-  if (g_list_find (selection->targets, atom))
+  info = g_hash_table_lookup (selection->offers, wl_data_offer);
+
+  if (!info || g_list_find (info->targets, atom))
     return;
 
-  selection->targets = g_list_prepend (selection->targets, atom);
+  info->targets = g_list_prepend (info->targets, atom);
 }
 
 static const struct wl_data_offer_listener data_offer_listener = {
   data_offer_offer,
 };
 
+DataOfferData *
+selection_lookup_offer_by_atom (GdkWaylandSelection *selection,
+                                GdkAtom              selection_atom)
+{
+  if (selection_atom == atoms[ATOM_CLIPBOARD])
+    return selection->clipboard_offer;
+  else if (selection_atom == atoms[ATOM_DND])
+    return selection->dnd_offer;
+  else
+    return NULL;
+}
+
+void
+gdk_wayland_selection_ensure_offer (GdkDisplay           *display,
+                                    struct wl_data_offer *wl_offer)
+{
+  GdkWaylandSelection *selection = gdk_wayland_display_get_selection (display);
+  DataOfferData *info;
+
+  info = g_hash_table_lookup (selection->offers, wl_offer);
+
+  if (!info)
+    {
+      info = data_offer_data_new (wl_offer);
+      g_hash_table_insert (selection->offers, wl_offer, info);
+      wl_data_offer_add_listener (wl_offer,
+                                  &data_offer_listener,
+                                  selection);
+    }
+}
+
 void
 gdk_wayland_selection_set_offer (GdkDisplay           *display,
+                                 GdkAtom               selection_atom,
                                  struct wl_data_offer *wl_offer)
 {
   GdkWaylandSelection *selection = gdk_wayland_display_get_selection (display);
+  struct wl_data_offer *prev_offer;
+  DataOfferData *info;
 
-  if (selection->offer == wl_offer)
-    return;
+  info = g_hash_table_lookup (selection->offers, wl_offer);
 
-  if (selection->offer)
-    wl_data_offer_destroy (selection->offer);
+  prev_offer = gdk_wayland_selection_get_offer (display, selection_atom);
 
-  selection->offer = wl_offer;
+  if (prev_offer)
+    g_hash_table_remove (selection->offers, prev_offer);
 
-  if (wl_offer)
-    wl_data_offer_add_listener (wl_offer,
-                                &data_offer_listener,
-                                selection);
+  if (selection_atom == atoms[ATOM_CLIPBOARD])
+    selection->clipboard_offer = info;
+  else if (selection_atom == atoms[ATOM_DND])
+    selection->dnd_offer = info;
 
   /* Clear all buffers */
   g_hash_table_remove_all (selection->selection_buffers);
-  g_list_free (selection->targets);
-  selection->targets = NULL;
 }
 
 struct wl_data_offer *
-gdk_wayland_selection_get_offer (GdkDisplay *display)
+gdk_wayland_selection_get_offer (GdkDisplay *display,
+                                 GdkAtom     selection_atom)
 {
   GdkWaylandSelection *selection = gdk_wayland_display_get_selection (display);
+  const DataOfferData *info;
+
+  info = selection_lookup_offer_by_atom (selection, selection_atom);
 
-  return selection->offer;
+  if (info)
+    return info->offer;
+
+  return NULL;
 }
 
 GList *
-gdk_wayland_selection_get_targets (GdkDisplay *display)
+gdk_wayland_selection_get_targets (GdkDisplay *display,
+                                   GdkAtom     selection_atom)
 {
   GdkWaylandSelection *selection = gdk_wayland_display_get_selection (display);
+  const DataOfferData *info;
+
+  info = selection_lookup_offer_by_atom (selection, selection_atom);
 
-  return selection->targets;
+  if (info)
+    return info->targets;
+
+  return NULL;
 }
 
 static void
@@ -551,6 +623,7 @@ gdk_wayland_selection_request_target (GdkWaylandSelection *wayland_selection,
                                       GdkAtom              target,
                                       gint                 fd)
 {
+  DataOfferData *offer;
   GdkAtom selection;
 
   if (wayland_selection->clipboard_owner == window)
@@ -560,21 +633,19 @@ gdk_wayland_selection_request_target (GdkWaylandSelection *wayland_selection,
   else
     return FALSE;
 
+  offer = selection_lookup_offer_by_atom (wayland_selection, selection);
+
   if (wayland_selection->stored_selection.fd == fd &&
-      wayland_selection->source_requested_target == target)
+      offer->requested_target == target)
     return FALSE;
 
   wayland_selection->stored_selection.fd = fd;
-
-  wayland_selection->source_requested_target = target;
+  offer->requested_target = target;
 
   if (window && target != GDK_NONE)
-    {
-      gdk_wayland_selection_emit_request (window, selection, target);
-      return TRUE;
-    }
+    gdk_wayland_selection_emit_request (window, selection, target);
 
-  return FALSE;
+  return TRUE;
 }
 
 static void
@@ -655,8 +726,6 @@ data_source_send (void                  *data,
       _gdk_wayland_drag_context_emit_event (context, GDK_DROP_FINISHED,
                                             GDK_CURRENT_TIME);
     }
-
-  wayland_selection->source_requested_target = GDK_NONE;
 }
 
 static void
@@ -879,8 +948,13 @@ _gdk_wayland_display_convert_selection (GdkDisplay *display,
 {
   GdkWaylandSelection *wayland_selection = gdk_wayland_display_get_selection (display);
   SelectionBuffer *buffer_data;
+  struct wl_data_offer *offer;
+  GList *targets;
+
+  offer = gdk_wayland_selection_get_offer (display, selection);
+  targets = gdk_wayland_selection_get_targets (display, selection);
 
-  if (!wayland_selection->offer)
+  if (!offer)
     {
       GdkEvent *event;
 
@@ -899,7 +973,7 @@ _gdk_wayland_display_convert_selection (GdkDisplay *display,
     }
 
   if (target != gdk_atom_intern_static_string ("TARGETS"))
-    wl_data_offer_accept (wayland_selection->offer,
+    wl_data_offer_accept (offer,
                           _gdk_wayland_display_get_serial (GDK_WAYLAND_DISPLAY (display)),
                           gdk_atom_name (target));
 
@@ -919,16 +993,16 @@ _gdk_wayland_display_convert_selection (GdkDisplay *display,
           gint i = 0;
           GList *l;
 
-          natoms = g_list_length (wayland_selection->targets);
+          natoms = g_list_length (targets);
           atoms = g_new0 (GdkAtom, natoms);
 
-          for (l = wayland_selection->targets; l; l = l->next)
+          for (l = targets; l; l = l->next)
             atoms[i++] = l->data;
         }
       else
         {
           g_unix_open_pipe (pipe_fd, FD_CLOEXEC, NULL);
-          wl_data_offer_receive (wayland_selection->offer,
+          wl_data_offer_receive (offer,
                                  gdk_atom_name (target),
                                  pipe_fd[1]);
           stream = g_unix_input_stream_new (pipe_fd[0], TRUE);


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