[gtk+/wip/wayland-dnd-actions: 673/674] wayland: Implement DnD actions as per wl_data_device v3



commit 86c18a807acd3a9b5724017f7bf36f1b81f7060e
Author: Carlos Garnacho <carlosg gnome org>
Date:   Tue Apr 7 17:01:03 2015 +0200

    wayland: Implement DnD actions as per wl_data_device v3
    
    Instead of faking drag status all around, communicate these to the
    compositor (and other peer), and trigger the right events on both sides:
    
      - Changes in the selected action trigger GDK_DRAG_STATUS events on the
        drag source, which trigger grab (and grab cursor) updates.
      - In the case of move actions, the "DELETE" target request is completely
        emulated on the source side as well, that target will be filtered out
        from the offered mimetypes list as well.

 gdk/wayland/gdkdisplay-wayland.c   |    2 +-
 gdk/wayland/gdkdnd-wayland.c       |   38 ++++++++-
 gdk/wayland/gdkprivate-wayland.h   |    6 +-
 gdk/wayland/gdkselection-wayland.c |  161 +++++++++++++++++++++++++++++++----
 4 files changed, 181 insertions(+), 26 deletions(-)
---
diff --git a/gdk/wayland/gdkdisplay-wayland.c b/gdk/wayland/gdkdisplay-wayland.c
index a0a5d0c..dbe8bc7 100644
--- a/gdk/wayland/gdkdisplay-wayland.c
+++ b/gdk/wayland/gdkdisplay-wayland.c
@@ -350,7 +350,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 e96aeda..82e4265 100644
--- a/gdk/wayland/gdkdnd-wayland.c
+++ b/gdk/wayland/gdkdnd-wayland.c
@@ -143,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_set_actions (wayland_context->data_source,
+                              gdk_to_wl_actions (actions));
 }
 
 static gboolean
@@ -167,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,6 +215,9 @@ gdk_wayland_drop_context_set_status (GdkDragContext *context,
 
   context_wayland = GDK_WAYLAND_DRAG_CONTEXT (context);
 
+  if (!context->dest_window)
+    return;
+
   display = gdk_device_get_display (gdk_drag_context_get_device (context));
   wl_offer = gdk_wayland_selection_get_offer (display,
                                               gdk_drag_get_selection (context));
@@ -231,6 +253,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);
 }
 
diff --git a/gdk/wayland/gdkprivate-wayland.h b/gdk/wayland/gdkprivate-wayland.h
index b78f854..05e54c0 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);
@@ -256,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 9da66cb..c81afc9 100644
--- a/gdk/wayland/gdkselection-wayland.c
+++ b/gdk/wayland/gdkselection-wayland.c
@@ -350,8 +350,69 @@ data_offer_offer (void                 *data,
   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 *
@@ -406,7 +467,6 @@ gdk_wayland_selection_set_offer (GdkDisplay           *display,
   else if (selection_atom == atoms[ATOM_DND])
     selection->dnd_offer = info;
 
-  /* Clear all buffers */
   g_hash_table_remove_all (selection->selection_buffers);
 }
 
@@ -569,6 +629,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);
 
@@ -715,18 +778,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);
     }
@@ -748,7 +808,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",
@@ -760,8 +819,6 @@ data_source_send (void                  *data,
       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)
@@ -779,13 +836,6 @@ data_source_send (void                  *data,
                                              gdk_atom_intern (mime_type, FALSE),
                                              fd))
     gdk_wayland_selection_check_write (wayland_selection);
-
-  if (context)
-    {
-      gdk_wayland_device_unset_grab (gdk_drag_context_get_device (context));
-      _gdk_wayland_drag_context_emit_event (context, GDK_DROP_FINISHED,
-                                            GDK_CURRENT_TIME);
-    }
 }
 
 static void
@@ -814,10 +864,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_device_unset_grab (gdk_drag_context_get_device (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 *
@@ -1015,7 +1117,7 @@ _gdk_wayland_display_convert_selection (GdkDisplay *display,
   offer = gdk_wayland_selection_get_offer (display, selection);
   target_list = gdk_wayland_selection_get_targets (display, selection);
 
-  if (!offer)
+  if (!offer || target == gdk_atom_intern_static_string ("DELETE"))
     {
       GdkEvent *event;
 
@@ -1192,3 +1294,24 @@ gdk_wayland_selection_clear_targets (GdkDisplay *display,
   g_array_set_size (wayland_selection->source_targets, 0);
   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_set_actions (offer, all_actions, action);
+  return TRUE;
+}


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