[gtk+/wayland-selections: 9/14] wayland: Implement drag sources



commit 174c661a3d409bf54b6bc7d94ffb6704b4447a09
Author: Carlos Garnacho <carlosg gnome org>
Date:   Thu Aug 21 21:30:22 2014 +0200

    wayland: Implement drag sources
    
    The wl_data_source is retrieved from the selection object for the DnD
    selection, and used to initiate a drag. When the drag is finished, a
    button release or touch end event is synthesized to finish the DnD
    operation after the compositor grab is gone.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=697855

 gdk/wayland/gdkdevice-wayland.c  |   19 +++++-
 gdk/wayland/gdkdnd-wayland.c     |  147 +++++++++++++++++++++++++++++++++++++-
 gdk/wayland/gdkprivate-wayland.h |    6 ++
 3 files changed, 169 insertions(+), 3 deletions(-)
---
diff --git a/gdk/wayland/gdkdevice-wayland.c b/gdk/wayland/gdkdevice-wayland.c
index 74142f3..4e77317 100644
--- a/gdk/wayland/gdkdevice-wayland.c
+++ b/gdk/wayland/gdkdevice-wayland.c
@@ -523,7 +523,7 @@ data_device_enter (void                  *data,
                    struct wl_data_offer  *offer)
 {
   GdkWaylandDeviceData *device = (GdkWaylandDeviceData *)data;
-  GdkWindow *dest_window;
+  GdkWindow *dest_window, *dnd_owner;
 
   dest_window = wl_surface_get_user_data (surface);
 
@@ -539,6 +539,12 @@ data_device_enter (void                  *data,
   device->surface_y = wl_fixed_to_double (y);
 
   gdk_wayland_drop_context_update_targets (device->drop_context);
+
+  dnd_owner = gdk_selection_owner_get (gdk_drag_get_selection (device->drop_context));
+
+  if (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);
   _gdk_wayland_drag_context_set_coords (device->drop_context,
@@ -601,10 +607,21 @@ data_device_drop (void                  *data,
                   struct wl_data_device *data_device)
 {
   GdkWaylandDeviceData *device = (GdkWaylandDeviceData *) data;
+  GdkWindow *local_dnd_owner;
 
   g_debug (G_STRLOC ": %s data_device = %p",
            G_STRFUNC, data_device);
 
+  local_dnd_owner = gdk_selection_owner_get (gdk_drag_get_selection (device->drop_context));
+
+  if (local_dnd_owner)
+    {
+      GdkDragContext *source_context;
+
+      source_context = gdk_wayland_drag_context_lookup_by_source_window (local_dnd_owner);
+      gdk_wayland_drag_context_undo_grab (source_context);
+    }
+
   _gdk_wayland_drag_context_emit_event (device->drop_context,
                                         GDK_DROP_START, GDK_CURRENT_TIME);
 }
diff --git a/gdk/wayland/gdkdnd-wayland.c b/gdk/wayland/gdkdnd-wayland.c
index c6df483..5e04e26 100644
--- a/gdk/wayland/gdkdnd-wayland.c
+++ b/gdk/wayland/gdkdnd-wayland.c
@@ -40,6 +40,9 @@ typedef struct _GdkWaylandDragContextClass GdkWaylandDragContextClass;
 struct _GdkWaylandDragContext
 {
   GdkDragContext context;
+  GdkWindow *dnd_window;
+  struct wl_surface *dnd_surface;
+  struct wl_data_source *data_source;
   struct wl_data_offer *offer;
   uint32_t serial;
   gdouble x;
@@ -63,6 +66,10 @@ gdk_wayland_drag_context_finalize (GObject *object)
 
   contexts = g_list_remove (contexts, context);
 
+  if (context->is_source &&
+      gdk_selection_owner_get (gdk_drag_get_selection (context)) == context->source_window)
+    gdk_wayland_selection_unset_data_source (gdk_drag_get_selection (context));
+
   if (wayland_context->data_source)
     wl_data_source_destroy (wayland_context->data_source);
 
@@ -209,6 +216,8 @@ gdk_wayland_drag_context_drop_finish (GdkDragContext *context,
                                      gboolean        success,
                                      guint32         time)
 {
+  if (gdk_selection_owner_get (gdk_drag_get_selection (context)))
+    gdk_wayland_selection_unset_data_source (gdk_drag_get_selection (context));
 }
 
 static gboolean
@@ -266,18 +275,63 @@ _gdk_wayland_window_register_dnd (GdkWindow *window)
 {
 }
 
+static GdkWindow *
+create_dnd_window (void)
+{
+  GdkWindowAttr attrs;
+  GdkScreen *screen;
+  guint mask;
+
+  screen = gdk_display_get_default_screen (gdk_display_get_default ());
+
+  attrs.x = attrs.y = 0;
+  attrs.width = attrs.height = 100;
+  attrs.wclass = GDK_INPUT_OUTPUT;
+  attrs.window_type = GDK_WINDOW_TEMP;
+  attrs.type_hint = GDK_WINDOW_TYPE_HINT_DND;
+  attrs.visual = gdk_screen_get_system_visual (screen);
+
+  mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_TYPE_HINT;
+
+  return gdk_window_new (gdk_screen_get_root_window (screen), &attrs, mask);
+}
+
 GdkDragContext *
 _gdk_wayland_window_drag_begin (GdkWindow *window,
                                GdkDevice *device,
                                GList     *targets)
 {
+  GdkWaylandDragContext *context_wayland;
+  GdkWaylandDisplay *display_wayland;
   GdkDragContext *context;
+  GdkWindow *toplevel;
+  GList *l;
+
+  toplevel = gdk_device_get_window_at_position (device, NULL, NULL);
 
-  context = (GdkDragContext *) g_object_new (GDK_TYPE_WAYLAND_DRAG_CONTEXT, NULL);
-  context->source_window = window;
+  context_wayland = g_object_new (GDK_TYPE_WAYLAND_DRAG_CONTEXT, NULL);
+  context = GDK_DRAG_CONTEXT (context_wayland);
+  context->source_window = g_object_ref (window);
   context->is_source = TRUE;
+  context->targets = g_list_copy (targets);
 
   gdk_drag_context_set_device (context, device);
+  display_wayland = GDK_WAYLAND_DISPLAY (gdk_device_get_display (device));
+
+  context_wayland->dnd_window = create_dnd_window ();
+  context_wayland->dnd_surface = gdk_wayland_window_get_wl_surface (context_wayland->dnd_window);
+  context_wayland->data_source =
+    gdk_wayland_selection_get_data_source (window,
+                                           gdk_wayland_drag_context_get_selection (context));
+
+  for (l = context->targets; l; l = l->next)
+    wl_data_source_offer (context_wayland->data_source, gdk_atom_name (l->data));
+
+  wl_data_device_start_drag (gdk_wayland_device_get_data_device (device),
+                             context_wayland->data_source,
+                             gdk_wayland_window_get_wl_surface (toplevel),
+                            context_wayland->dnd_surface,
+                             _gdk_wayland_display_get_serial (display_wayland));
 
   return context;
 }
@@ -338,3 +392,92 @@ _gdk_wayland_drag_context_set_dest_window (GdkDragContext *context,
   context->actions = GDK_ACTION_COPY | GDK_ACTION_MOVE;
   gdk_wayland_drop_context_update_targets (context);
 }
+
+GdkDragContext *
+gdk_wayland_drag_context_lookup_by_data_source (struct wl_data_source *source)
+{
+  GList *l;
+
+  for (l = contexts; l; l = l->next)
+    {
+      GdkWaylandDragContext *wayland_context = l->data;
+
+      if (wayland_context->data_source == source)
+        return l->data;
+    }
+
+  return NULL;
+}
+
+GdkDragContext *
+gdk_wayland_drag_context_lookup_by_source_window (GdkWindow *window)
+{
+  GList *l;
+
+  for (l = contexts; l; l = l->next)
+    {
+      if (window == gdk_drag_context_get_source_window (l->data))
+        return l->data;
+    }
+
+  return NULL;
+}
+
+struct wl_data_source *
+gdk_wayland_drag_context_get_data_source (GdkDragContext *context)
+{
+  return GDK_WAYLAND_DRAG_CONTEXT (context)->data_source;
+}
+
+void
+gdk_wayland_drag_context_undo_grab (GdkDragContext *context)
+{
+  GdkEventSequence *sequence;
+  GdkModifierType state;
+  GdkDevice *device;
+  GdkEvent *event;
+  guint button;
+  gdouble x, y;
+
+  device = gdk_drag_context_get_device (context);
+  _gdk_wayland_device_get_last_implicit_grab_serial (GDK_WAYLAND_DEVICE (device), &sequence);
+  gdk_window_get_device_position_double (gdk_drag_context_get_source_window (context),
+                                         device, &x, &y, &state);
+
+  if (sequence)
+    {
+      event = gdk_event_new (GDK_TOUCH_END);
+      event->touch.window = g_object_ref (gdk_drag_context_get_source_window (context));
+      event->touch.send_event = TRUE;
+      event->touch.sequence = sequence;
+      event->touch.time = GDK_CURRENT_TIME;
+      event->touch.x = event->touch.x_root = x;
+      event->touch.y = event->touch.y_root = y;
+    }
+  else if (state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK))
+    {
+      if (state & GDK_BUTTON1_MASK)
+        button = 1;
+      else if (state & GDK_BUTTON2_MASK)
+        button = 2;
+      else if (state & GDK_BUTTON3_MASK)
+        button = 3;
+      else
+        return;
+
+      event = gdk_event_new (GDK_BUTTON_RELEASE);
+      event->button.window = g_object_ref (gdk_drag_context_get_source_window (context));
+      event->button.send_event = TRUE;
+      event->button.button = button;
+      event->button.time = GDK_CURRENT_TIME;
+      event->button.x = event->button.x_root = x;
+      event->button.y = event->button.x_root = y;
+    }
+  else
+    return;
+
+  gdk_event_set_device (event, device);
+  gdk_event_set_source_device (event, device);
+
+  _gdk_wayland_display_deliver_event (gdk_device_get_display (device), event);
+}
diff --git a/gdk/wayland/gdkprivate-wayland.h b/gdk/wayland/gdkprivate-wayland.h
index 99acee5..23510ad 100644
--- a/gdk/wayland/gdkprivate-wayland.h
+++ b/gdk/wayland/gdkprivate-wayland.h
@@ -106,6 +106,12 @@ void _gdk_wayland_drag_context_set_coords (GdkDragContext *context,
                                            gdouble         x,
                                            gdouble         y);
 
+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);
+struct wl_data_source * gdk_wayland_drag_context_get_data_source  (GdkDragContext *context);
+
+void gdk_wayland_drag_context_undo_grab (GdkDragContext *context);
+
 void gdk_wayland_drop_context_update_targets (GdkDragContext *context);
 
 void _gdk_wayland_display_create_window_impl (GdkDisplay    *display,


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