[gtk+/wip/wayland-dnd-actions] WIP: Wayland DnD actions



commit 061a3dcde287dda2ea28038d71e97bc34230e72c
Author: Carlos Garnacho <carlosg gnome org>
Date:   Wed Feb 18 13:27:54 2015 +0100

    WIP: Wayland DnD actions

 gdk/wayland/gdkdevice-wayland.c    |   64 ++++++-
 gdk/wayland/gdkdisplay-wayland.c   |    2 +-
 gdk/wayland/gdkdnd-wayland.c       |   62 ++++++--
 gdk/wayland/gdkprivate-wayland.h   |   16 ++-
 gdk/wayland/gdkselection-wayland.c |  325 +++++++++++++++++++++++++++++-------
 5 files changed, 380 insertions(+), 89 deletions(-)
---
diff --git a/gdk/wayland/gdkdevice-wayland.c b/gdk/wayland/gdkdevice-wayland.c
index a22351b..34495c7 100644
--- a/gdk/wayland/gdkdevice-wayland.c
+++ b/gdk/wayland/gdkdevice-wayland.c
@@ -95,6 +95,9 @@ struct _GdkWaylandDeviceData
   struct wl_surface *pointer_surface;
   guint current_output_scale;
   GSList *pointer_surface_outputs;
+
+  /* Source/dest for non-local dnd */
+  GdkWindow *foreign_dnd_window;
 };
 
 struct _GdkWaylandDevice
@@ -589,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
@@ -604,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);
 
@@ -621,10 +624,13 @@ 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;
 
-  if (dnd_owner)
-    _gdk_wayland_drag_context_set_source_window (device->drop_context, dnd_owner);
+  _gdk_wayland_drag_context_set_source_window (device->drop_context, dnd_owner);
 
   _gdk_wayland_drag_context_set_dest_window (device->drop_context,
                                              dest_window, serial);
@@ -633,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
@@ -695,6 +701,9 @@ data_device_drop (void                  *data,
   GDK_NOTE (EVENTS,
             g_message ("data device drop, data device %p", data_device));
 
+  if (!gdk_drag_context_get_dest_window (device->drop_context))
+    return;
+
   local_dnd_owner = gdk_selection_owner_get_for_display (device->display, gdk_drag_get_selection 
(device->drop_context));
 
   if (local_dnd_owner)
@@ -715,13 +724,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 = {
@@ -1838,6 +1849,26 @@ static const struct wl_surface_listener pointer_surface_listener = {
   pointer_surface_leave
 };
 
+static GdkWindow *
+create_foreign_dnd_window (GdkDisplay *display)
+{
+  GdkWindowAttr attrs;
+  GdkScreen *screen;
+  guint mask;
+
+  screen = gdk_display_get_default_screen (display);
+
+  attrs.x = attrs.y = 0;
+  attrs.width = attrs.height = 1;
+  attrs.wclass = GDK_INPUT_OUTPUT;
+  attrs.window_type = GDK_WINDOW_TEMP;
+  attrs.visual = gdk_screen_get_system_visual (screen);
+
+  mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
+
+  return gdk_window_new (gdk_screen_get_root_window (screen), &attrs, mask);
+}
+
 void
 _gdk_wayland_device_manager_add_seat (GdkDeviceManager *device_manager,
                                       guint32           id,
@@ -1857,6 +1888,7 @@ _gdk_wayland_device_manager_add_seat (GdkDeviceManager *device_manager,
   device->device_manager = device_manager;
   device->touches = g_hash_table_new_full (NULL, NULL, NULL,
                                            (GDestroyNotify) g_free);
+  device->foreign_dnd_window = create_foreign_dnd_window (display);
   device->wl_seat = wl_seat;
 
   wl_seat_add_listener (device->wl_seat, &seat_listener, device);
@@ -1898,6 +1930,7 @@ _gdk_wayland_device_manager_remove_seat (GdkDeviceManager *manager,
           /* FIXME: destroy data_device */
           g_clear_object (&device->keyboard_settings);
           g_hash_table_destroy (device->touches);
+          gdk_window_destroy (device->foreign_dnd_window);
           g_free (device);
 
           break;
@@ -2072,3 +2105,14 @@ gdk_wayland_device_set_selection (GdkDevice             *gdk_device,
   wl_data_device_set_selection (device->data_device, source,
                                 _gdk_wayland_display_get_serial (display_wayland));
 }
+
+GdkDragContext *
+gdk_wayland_device_get_drop_context (GdkDevice *gdk_device)
+{
+  GdkWaylandDeviceData *device;
+
+  g_return_if_fail (GDK_IS_WAYLAND_DEVICE (gdk_device));
+  device = GDK_WAYLAND_DEVICE (gdk_device)->device;
+
+  return device->drop_context;
+}
diff --git a/gdk/wayland/gdkdisplay-wayland.c b/gdk/wayland/gdkdisplay-wayland.c
index 1b26e5b..4aeb526 100644
--- a/gdk/wayland/gdkdisplay-wayland.c
+++ b/gdk/wayland/gdkdisplay-wayland.c
@@ -202,7 +202,7 @@ gdk_registry_handle_global (void               *data,
   else if (strcmp (interface, "wl_data_device_manager") == 0)
     {
       display_wayland->data_device_manager =
-        wl_registry_bind (display_wayland->wl_registry, id, &wl_data_device_manager_interface, 1);
+        wl_registry_bind (display_wayland->wl_registry, id, &wl_data_device_manager_interface, 3);
     }
   else if (strcmp (interface, "wl_subcompositor") == 0)
     {
diff --git a/gdk/wayland/gdkdnd-wayland.c b/gdk/wayland/gdkdnd-wayland.c
index f558bb5..30a7df2 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;
@@ -144,11 +143,30 @@ gdk_wayland_drag_context_find_window (GdkDragContext  *context,
   return NULL;
 }
 
+static inline uint32_t
+gdk_to_wl_actions (GdkDragAction action)
+{
+  uint32_t dnd_actions = 0;
+
+  if (action & (GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_PRIVATE))
+    dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
+  if (action & GDK_ACTION_MOVE)
+    dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
+  if (action & GDK_ACTION_ASK)
+    dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK;
+
+  return dnd_actions;
+}
+
 void
-gdk_wayland_drag_context_set_action (GdkDragContext *context,
-                                     GdkDragAction   action)
+gdk_wayland_drag_context_set_actions (GdkDragContext *context,
+                                      GdkDragAction   actions)
 {
-  context->suggested_action = context->action = action;
+  GdkWaylandDragContext *wayland_context = GDK_WAYLAND_DRAG_CONTEXT (context);
+
+  context->actions = actions;
+  wl_data_source_notify_actions (wayland_context->data_source,
+                                 gdk_to_wl_actions (actions));
 }
 
 static gboolean
@@ -168,7 +186,7 @@ gdk_wayland_drag_context_drag_motion (GdkDragContext *context,
       _gdk_wayland_drag_context_emit_event (context, GDK_DRAG_STATUS, time);
     }
 
-  gdk_wayland_drag_context_set_action (context, suggested_action);
+  gdk_wayland_drag_context_set_actions (context, possible_actions);
 
   return context->dest_window != NULL;
 }
@@ -196,8 +214,13 @@ 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);
+
+  if (!context->dest_window)
+    return;
+
+  display = gdk_window_get_display (context->dest_window);
+  wl_offer = gdk_wayland_selection_get_offer (display,
+                                              gdk_drag_get_selection (context));
 
   if (!wl_offer)
     return;
@@ -228,6 +251,14 @@ gdk_wayland_drag_context_drag_status (GdkDragContext *context,
                                      GdkDragAction   action,
                                      guint32         time_)
 {
+  GdkDisplay *display;
+  uint32_t dnd_actions;
+
+  display = gdk_device_get_display (gdk_drag_context_get_device (context));
+
+  dnd_actions = gdk_to_wl_actions (action);
+  gdk_wayland_selection_set_current_offer_actions (display, dnd_actions);
+
   gdk_wayland_drop_context_set_status (context, action != 0);
 }
 
@@ -244,10 +275,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 +424,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
@@ -418,6 +455,9 @@ _gdk_wayland_drag_context_set_dest_window (GdkDragContext *context,
                                            GdkWindow      *dest_window,
                                            uint32_t        serial)
 {
+  if (context->dest_window)
+    g_object_unref (context->dest_window);
+
   context->dest_window = dest_window ? g_object_ref (dest_window) : NULL;
   GDK_WAYLAND_DRAG_CONTEXT (context)->serial = serial;
   gdk_wayland_drop_context_update_targets (context);
diff --git a/gdk/wayland/gdkprivate-wayland.h b/gdk/wayland/gdkprivate-wayland.h
index a8211fd..6931e24 100644
--- a/gdk/wayland/gdkprivate-wayland.h
+++ b/gdk/wayland/gdkprivate-wayland.h
@@ -118,8 +118,8 @@ void _gdk_wayland_drag_context_set_coords (GdkDragContext *context,
                                            gdouble         x,
                                            gdouble         y);
 
-void gdk_wayland_drag_context_set_action (GdkDragContext *context,
-                                          GdkDragAction   action);
+void gdk_wayland_drag_context_set_actions (GdkDragContext *context,
+                                           GdkDragAction   actions);
 
 GdkDragContext * gdk_wayland_drag_context_lookup_by_data_source   (struct wl_data_source *source);
 GdkDragContext * gdk_wayland_drag_context_lookup_by_source_window (GdkWindow *window);
@@ -186,6 +186,7 @@ uint32_t _gdk_wayland_device_get_last_implicit_grab_serial (GdkWaylandDevice  *d
 struct wl_data_device * gdk_wayland_device_get_data_device (GdkDevice *gdk_device);
 void gdk_wayland_device_set_selection (GdkDevice             *gdk_device,
                                        struct wl_data_source *source);
+GdkDragContext * gdk_wayland_device_get_drop_context (GdkDevice *gdk_device);
 
 void gdk_wayland_device_unset_touch_grab (GdkDevice        *device,
                                           GdkEventSequence *sequence);
@@ -237,10 +238,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,
@@ -250,6 +256,8 @@ void     gdk_wayland_selection_store   (GdkWindow    *window,
 struct wl_data_source * gdk_wayland_selection_get_data_source (GdkWindow *owner,
                                                                GdkAtom    selection);
 void gdk_wayland_selection_unset_data_source (GdkDisplay *display, GdkAtom selection);
+gboolean gdk_wayland_selection_set_current_offer_actions (GdkDisplay *display,
+                                                          uint32_t    actions);
 
 EGLSurface gdk_wayland_window_get_egl_surface (GdkWindow *window,
                                                EGLConfig config);
diff --git a/gdk/wayland/gdkselection-wayland.c b/gdk/wayland/gdkselection-wayland.c
index 12a2d30..f7cd7ea 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,165 @@ 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 inline GdkDragAction
+_wl_to_gdk_actions (uint32_t dnd_actions)
+{
+  GdkDragAction actions = 0;
+
+  if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY)
+    actions |= GDK_ACTION_COPY;
+  if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE)
+    actions |= GDK_ACTION_MOVE;
+  if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)
+    actions |= GDK_ACTION_ASK;
+
+  return actions;
+}
+
+static void
+data_offer_source_actions (void                 *data,
+                           struct wl_data_offer *wl_data_offer,
+                           uint32_t              source_actions)
+{
+  GdkDeviceManager *device_manager;
+  GdkDragContext *drop_context;
+  GdkDisplay *display;
+  GdkDevice *device;
+
+  display = gdk_display_get_default ();
+  device_manager = gdk_display_get_device_manager (display);
+  device = gdk_device_manager_get_client_pointer (device_manager);
+  drop_context = gdk_wayland_device_get_drop_context (device);
+
+  drop_context->actions = _wl_to_gdk_actions (source_actions);
+
+  if (gdk_drag_context_get_dest_window (drop_context))
+    _gdk_wayland_drag_context_emit_event (drop_context, GDK_DRAG_MOTION,
+                                          GDK_CURRENT_TIME);
+}
+
+static void
+data_offer_action (void                 *data,
+                   struct wl_data_offer *wl_data_offer,
+                   uint32_t              action)
+{
+  GdkDeviceManager *device_manager;
+  GdkDragContext *drop_context;
+  GdkDisplay *display;
+  GdkDevice *device;
+
+  display = gdk_display_get_default ();
+  device_manager = gdk_display_get_device_manager (display);
+  device = gdk_device_manager_get_client_pointer (device_manager);
+  drop_context = gdk_wayland_device_get_drop_context (device);
+
+  drop_context->action = _wl_to_gdk_actions (action);
+
+  if (gdk_drag_context_get_dest_window (drop_context))
+    _gdk_wayland_drag_context_emit_event (drop_context, GDK_DRAG_MOTION,
+                                          GDK_CURRENT_TIME);
 }
 
 static const struct wl_data_offer_listener data_offer_listener = {
   data_offer_offer,
+  data_offer_source_actions,
+  data_offer_action
 };
 
+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;
 
-  return selection->targets;
+  info = selection_lookup_offer_by_atom (selection, selection_atom);
+
+  if (info)
+    return info->targets;
+
+  return NULL;
 }
 
 static void
@@ -399,6 +531,8 @@ async_write_data_new (GdkWaylandSelection *selection)
 static void
 async_write_data_free (AsyncWriteData *write_data)
 {
+  close (write_data->selection->stored_selection.fd);
+  write_data->selection->stored_selection.fd = -1;
   g_object_unref (write_data->stream);
   g_slice_free (AsyncWriteData, write_data);
 }
@@ -461,7 +595,7 @@ gdk_wayland_selection_check_write (GdkWaylandSelection *selection)
 {
   AsyncWriteData *write_data;
 
-  if (selection->stored_selection.fd < 0 ||
+  if (selection->stored_selection.fd <= 0 ||
       selection->stored_selection.data_len == 0)
     return FALSE;
 
@@ -483,6 +617,9 @@ gdk_wayland_selection_store (GdkWindow    *window,
   GdkWaylandSelection *selection = gdk_wayland_display_get_selection (display);
   GArray *array;
 
+  if (type == gdk_atom_intern_static_string ("NULL"))
+    return;
+
   array = g_array_new (TRUE, FALSE, sizeof (guchar));
   g_array_append_vals (array, data, len);
 
@@ -551,6 +688,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 +698,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
@@ -594,18 +730,15 @@ data_source_target (void                  *data,
   if (!mime_type)
     {
       if (context)
-        {
-          gdk_wayland_drag_context_set_action (context, 0);
-          _gdk_wayland_drag_context_emit_event (context, GDK_DRAG_STATUS,
-                                                GDK_CURRENT_TIME);
-        }
+        _gdk_wayland_drag_context_emit_event (context, GDK_DRAG_STATUS,
+                                              GDK_CURRENT_TIME);
+
       return;
     }
 
   if (source == wayland_selection->dnd_source)
     {
       window = wayland_selection->dnd_owner;
-      gdk_wayland_drag_context_set_action (context, GDK_ACTION_COPY);
       _gdk_wayland_drag_context_emit_event (context, GDK_DRAG_STATUS,
                                             GDK_CURRENT_TIME);
     }
@@ -626,7 +759,6 @@ data_source_send (void                  *data,
                   int32_t                fd)
 {
   GdkWaylandSelection *wayland_selection = data;
-  GdkDragContext *context;
   GdkWindow *window;
 
   g_debug (G_STRLOC ": %s source = %p, mime_type = %s, fd = %d",
@@ -635,8 +767,6 @@ data_source_send (void                  *data,
   if (!mime_type)
     return;
 
-  context = gdk_wayland_drag_context_lookup_by_data_source (source);
-
   if (source == wayland_selection->dnd_source)
     window = wayland_selection->dnd_owner;
   else if (source == wayland_selection->clipboard_source)
@@ -648,15 +778,6 @@ data_source_send (void                  *data,
                                              gdk_atom_intern (mime_type, FALSE),
                                              fd))
     gdk_wayland_selection_check_write (wayland_selection);
-
-  if (context)
-    {
-      gdk_wayland_drag_context_undo_grab (context);
-      _gdk_wayland_drag_context_emit_event (context, GDK_DROP_FINISHED,
-                                            GDK_CURRENT_TIME);
-    }
-
-  wayland_selection->source_requested_target = GDK_NONE;
 }
 
 static void
@@ -685,10 +806,62 @@ data_source_cancelled (void                  *data,
     gdk_wayland_selection_unset_data_source (display, atoms[ATOM_CLIPBOARD]);
 }
 
+static void
+data_source_action (void                  *data,
+                    struct wl_data_source *source,
+                    uint32_t               action)
+{
+  GdkDragContext *context;
+
+  g_debug (G_STRLOC ": %s source = %p action=%x",
+           G_STRFUNC, source, action);
+
+  context = gdk_wayland_drag_context_lookup_by_data_source (source);
+
+  if (context)
+    {
+      context->action = _wl_to_gdk_actions (action);
+      _gdk_wayland_drag_context_emit_event (context, GDK_DRAG_STATUS,
+                                            GDK_CURRENT_TIME);
+    }
+}
+
+static void
+data_source_drop_performed (void                  *data,
+                            struct wl_data_source *source)
+{
+}
+
+static void
+data_source_drag_finished (void                  *data,
+                           struct wl_data_source *source)
+{
+  GdkDragContext *context;
+
+  context = gdk_wayland_drag_context_lookup_by_data_source (source);
+
+  if (!context)
+    return;
+
+  if (context->action == GDK_ACTION_MOVE)
+    {
+      gdk_wayland_selection_emit_request (context->source_window,
+                                          atoms[ATOM_DND],
+                                          gdk_atom_intern_static_string ("DELETE"));
+    }
+
+  gdk_wayland_drag_context_undo_grab (context);
+  _gdk_wayland_drag_context_emit_event (context, GDK_DROP_FINISHED,
+                                        GDK_CURRENT_TIME);
+}
+
 static const struct wl_data_source_listener data_source_listener = {
   data_source_target,
   data_source_send,
-  data_source_cancelled
+  data_source_cancelled,
+  data_source_action,
+  data_source_drop_performed,
+  data_source_drag_finished
 };
 
 struct wl_data_source *
@@ -879,8 +1052,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;
 
-  if (!wayland_selection->offer)
+  offer = gdk_wayland_selection_get_offer (display, selection);
+  targets = gdk_wayland_selection_get_targets (display, selection);
+
+  if (!offer || target == gdk_atom_intern_static_string ("DELETE"))
     {
       GdkEvent *event;
 
@@ -899,7 +1077,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 +1097,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);
@@ -1043,3 +1221,24 @@ gdk_wayland_selection_clear_targets (GdkDisplay *display,
 {
   gdk_wayland_selection_unset_data_source (display, selection);
 }
+
+gboolean
+gdk_wayland_selection_set_current_offer_actions (GdkDisplay *display,
+                                                 uint32_t    action)
+{
+  struct wl_data_offer *offer;
+  uint32_t all_actions = 0;
+
+  offer = gdk_wayland_selection_get_offer (display, atoms[ATOM_DND]);
+
+  if (!offer)
+    return FALSE;
+
+  if (action != 0)
+    all_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY |
+      WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE |
+      WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK;
+
+  wl_data_offer_notify_actions (offer, all_actions, action);
+  return TRUE;
+}


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