[mutter/wip/dnd-actions: 2/3] wayland: Implement DnD actions as per wl_data_device v3 changes



commit 8d13525b5fd8ee8a78d5700983bfe6fe578be0d6
Author: Carlos Garnacho <carlosg gnome org>
Date:   Tue Apr 7 16:05:46 2015 +0200

    wayland: Implement DnD actions as per wl_data_device v3 changes
    
    We now additionally send:
      - wl_data_offer.source_actions
      - wl_data_source.action
      - wl_data_offer.action
      - wl_data_source.drop_performed
      - wl_data_source.drag_finished
    
    The protocol changes allow for compositors to implement different policies
    when chosing the action, mutter uses this to reimplement the same behavior
    that GTK+ traditionally had:
    
      - Alt/Control/Shift modifiers change the chosen action to
        ask/copy/move respectively
      - Drags with middle button start out as "ask" by default
    
    As mutter now also grabs the keyboard and unsets the window focus for these
    purposes, the window focus is restored after the drag operation has
    finished.

 src/wayland/meta-wayland-data-device.c |  184 +++++++++++++++++++++++++++++++-
 src/wayland/meta-wayland-versions.h    |    2 +-
 2 files changed, 182 insertions(+), 4 deletions(-)
---
diff --git a/src/wayland/meta-wayland-data-device.c b/src/wayland/meta-wayland-data-device.c
index 16f626d..d0689fb 100644
--- a/src/wayland/meta-wayland-data-device.c
+++ b/src/wayland/meta-wayland-data-device.c
@@ -41,6 +41,8 @@ typedef struct
   struct wl_resource *resource;
   MetaWaylandDataSource *source;
   struct wl_listener source_destroy_listener;
+  uint32_t dnd_actions;
+  uint32_t preferred_dnd_action;
 } MetaWaylandDataOffer;
 
 struct _MetaWaylandDataSource
@@ -48,6 +50,10 @@ struct _MetaWaylandDataSource
   struct wl_resource *resource;
   struct wl_array mime_types;
   gboolean has_target;
+  uint32_t dnd_actions;
+  uint32_t user_dnd_action;
+  uint32_t current_dnd_action;
+  MetaWaylandDataOffer *offer;
 };
 
 static void
@@ -56,6 +62,60 @@ unbind_resource (struct wl_resource *resource)
   wl_list_remove (wl_resource_get_link (resource));
 }
 
+static uint32_t
+data_offer_choose_action (MetaWaylandDataOffer *offer)
+{
+  MetaWaylandDataSource *source = offer->source;
+  uint32_t available_actions;
+
+  available_actions = source->dnd_actions & offer->dnd_actions;
+
+  if (!available_actions)
+    return 0;
+
+  /* If the user is forcing an action, go for it */
+  if ((source->user_dnd_action & available_actions) != 0)
+    return source->user_dnd_action;
+
+  /* If the dest side has a preferred DnD action, use it */
+  if ((offer->preferred_dnd_action & available_actions) != 0)
+    return offer->preferred_dnd_action;
+
+  /* Use the first found action, in bit order */
+  return 1 << (ffs (available_actions) - 1);
+}
+
+static void
+data_source_update_actions (MetaWaylandDataSource *source,
+                            uint32_t               dnd_actions)
+{
+  source->dnd_actions = dnd_actions;
+
+  if (source->offer)
+    {
+      wl_data_offer_send_source_actions (source->offer->resource,
+                                         source->dnd_actions);
+    }
+}
+
+static void
+data_offer_update_action (MetaWaylandDataOffer *offer)
+{
+  uint32_t action;
+
+  if (!offer->source)
+    return;
+
+  action = data_offer_choose_action (offer);
+
+  if (offer->source->current_dnd_action == action)
+    return;
+
+  offer->source->current_dnd_action = action;
+  wl_data_source_send_action (offer->source->resource, action);
+  wl_data_offer_send_action (offer->resource, action);
+}
+
 static void
 data_offer_accept (struct wl_client *client,
                    struct wl_resource *resource,
@@ -93,10 +153,25 @@ data_offer_destroy (struct wl_client *client, struct wl_resource *resource)
   wl_resource_destroy (resource);
 }
 
+static void
+data_offer_notify_actions (struct wl_client   *client,
+                           struct wl_resource *resource,
+                           uint32_t            dnd_actions,
+                           uint32_t            preferred_action)
+{
+  MetaWaylandDataOffer *offer = wl_resource_get_user_data (resource);
+
+  offer->dnd_actions = dnd_actions;
+  offer->preferred_dnd_action = preferred_action;
+
+  data_offer_update_action (offer);
+}
+
 static const struct wl_data_offer_interface data_offer_interface = {
   data_offer_accept,
   data_offer_receive,
   data_offer_destroy,
+  data_offer_notify_actions
 };
 
 static void
@@ -105,8 +180,17 @@ destroy_data_offer (struct wl_resource *resource)
   MetaWaylandDataOffer *offer = wl_resource_get_user_data (resource);
 
   if (offer->source)
-    wl_list_remove (&offer->source_destroy_listener.link);
+    {
+      if (offer == offer->source->offer)
+        {
+          wl_data_source_send_drag_finished (offer->source->resource);
+          offer->source->offer = NULL;
+        }
+
+      wl_list_remove (&offer->source_destroy_listener.link);
+    }
 
+  meta_display_sync_wayland_input_focus (meta_get_display ());
   g_slice_free (MetaWaylandDataOffer, offer);
 }
 
@@ -139,6 +223,9 @@ meta_wayland_data_source_send_offer (MetaWaylandDataSource *source,
   wl_array_for_each (p, &source->mime_types)
     wl_data_offer_send_offer (offer->resource, *p);
 
+  data_offer_update_action (offer);
+  source->offer = offer;
+
   return offer->resource;
 }
 
@@ -162,14 +249,33 @@ data_source_destroy (struct wl_client *client, struct wl_resource *resource)
   wl_resource_destroy (resource);
 }
 
+static void
+data_source_notify_actions (struct wl_client   *client,
+                            struct wl_resource *resource,
+                            uint32_t            dnd_actions)
+{
+  MetaWaylandDataSource *source = wl_resource_get_user_data (resource);
+
+  if (source->dnd_actions == dnd_actions)
+    return;
+
+  data_source_update_actions (source, dnd_actions);
+
+  if (source->offer)
+    data_offer_update_action (source->offer);
+}
+
 static struct wl_data_source_interface data_source_interface = {
   data_source_offer,
-  data_source_destroy
+  data_source_destroy,
+  data_source_notify_actions
 };
 
 struct _MetaWaylandDragGrab {
   MetaWaylandPointerGrab  generic;
 
+  MetaWaylandKeyboardGrab keyboard_grab;
+
   MetaWaylandSeat        *seat;
   struct wl_client       *drag_client;
 
@@ -189,6 +295,7 @@ struct _MetaWaylandDragGrab {
   struct wl_listener      drag_origin_listener;
 
   int                     drag_start_x, drag_start_y;
+  ClutterModifierType     buttons;
 };
 
 static void
@@ -254,6 +361,28 @@ drag_grab_focus (MetaWaylandPointerGrab *grab,
 }
 
 static void
+data_source_update_user_dnd_action (MetaWaylandDataSource *source,
+                                    ClutterModifierType    modifiers)
+{
+  uint32_t user_dnd_action = 0;
+
+  if (modifiers & CLUTTER_SHIFT_MASK)
+    user_dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
+  else if (modifiers & CLUTTER_CONTROL_MASK)
+    user_dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
+  else if (modifiers & (CLUTTER_MOD1_MASK | CLUTTER_BUTTON2_MASK))
+    user_dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK;
+
+  if (source->user_dnd_action == user_dnd_action)
+    return;
+
+  source->user_dnd_action = user_dnd_action;
+
+  if (source->offer)
+    data_offer_update_action (source->offer);
+}
+
+static void
 drag_grab_motion (MetaWaylandPointerGrab *grab,
                  const ClutterEvent     *event)
 {
@@ -307,6 +436,7 @@ data_device_end_drag_grab (MetaWaylandDragGrab *drag_grab)
   drag_grab_focus (&drag_grab->generic, NULL);
 
   meta_wayland_pointer_end_grab (drag_grab->generic.pointer);
+  meta_wayland_keyboard_end_grab (drag_grab->keyboard_grab.keyboard);
   g_slice_free (MetaWaylandDragGrab, drag_grab);
 }
 
@@ -324,11 +454,17 @@ drag_grab_button (MetaWaylandPointerGrab *grab,
       gboolean success = FALSE;
 
       if (drag_grab->drag_focus_data_device &&
-          drag_grab->drag_data_source->has_target)
+          drag_grab->drag_data_source->has_target &&
+          drag_grab->drag_data_source->current_dnd_action)
         {
           wl_data_device_send_drop (drag_grab->drag_focus_data_device);
+          wl_data_source_send_drop_performed (drag_grab->drag_data_source->resource);
           success = TRUE;
         }
+      else
+        {
+          wl_data_source_send_cancelled (drag_grab->drag_data_source->resource);
+        }
 
       /* Finish drag and let actor self-destruct */
       meta_dnd_actor_drag_finish (META_DND_ACTOR (drag_grab->feedback_actor),
@@ -347,6 +483,35 @@ static const MetaWaylandPointerGrabInterface drag_grab_interface = {
   drag_grab_button,
 };
 
+static gboolean
+keyboard_drag_grab_key (MetaWaylandKeyboardGrab *grab,
+                        const ClutterEvent      *event)
+{
+  return FALSE;
+}
+
+static void
+keyboard_drag_grab_modifiers (MetaWaylandKeyboardGrab *grab,
+                              ClutterModifierType      modifiers)
+{
+  MetaWaylandDragGrab *drag_grab;
+
+  drag_grab = wl_container_of (grab, drag_grab, keyboard_grab);
+
+  /* The modifiers here just contain keyboard modifiers, mix it with the
+   * mouse button modifiers we got when starting the drag operation.
+   */
+  modifiers |= drag_grab->buttons;
+
+  if (drag_grab->drag_data_source)
+    data_source_update_user_dnd_action (drag_grab->drag_data_source, modifiers);
+}
+
+static const MetaWaylandKeyboardGrabInterface keyboard_drag_grab_interface = {
+  keyboard_drag_grab_key,
+  keyboard_drag_grab_modifiers
+};
+
 static void
 destroy_data_device_origin (struct wl_listener *listener, void *data)
 {
@@ -390,6 +555,7 @@ data_device_start_drag (struct wl_client *client,
   MetaWaylandSeat *seat = wl_container_of (data_device, seat, data_device);
   MetaWaylandSurface *surface = NULL;
   MetaWaylandDragGrab *drag_grab;
+  ClutterModifierType modifiers;
   ClutterPoint pos;
 
   if (origin_resource)
@@ -422,6 +588,9 @@ data_device_start_drag (struct wl_client *client,
   drag_grab->generic.interface = &drag_grab_interface;
   drag_grab->generic.pointer = &seat->pointer;
 
+  drag_grab->keyboard_grab.interface = &keyboard_drag_grab_interface;
+  drag_grab->keyboard_grab.keyboard = &seat->keyboard;
+
   drag_grab->drag_client = client;
   drag_grab->seat = seat;
 
@@ -436,12 +605,18 @@ data_device_start_drag (struct wl_client *client,
   drag_grab->drag_start_x = pos.x;
   drag_grab->drag_start_y = pos.y;
 
+  modifiers = clutter_input_device_get_modifier_state (seat->pointer.device);
+  drag_grab->buttons = modifiers &
+    (CLUTTER_BUTTON1_MASK | CLUTTER_BUTTON2_MASK | CLUTTER_BUTTON3_MASK |
+     CLUTTER_BUTTON4_MASK | CLUTTER_BUTTON5_MASK);
+
   if (source_resource)
     {
       drag_grab->drag_data_source = wl_resource_get_user_data (source_resource);
       drag_grab->drag_data_source_listener.notify = destroy_data_device_source;
       wl_resource_add_destroy_listener (source_resource,
                                         &drag_grab->drag_data_source_listener);
+      data_source_update_user_dnd_action (drag_grab->drag_data_source, modifiers);
     }
 
   if (icon_resource)
@@ -467,6 +642,9 @@ data_device_start_drag (struct wl_client *client,
 
   meta_wayland_pointer_set_focus (&seat->pointer, NULL);
   meta_wayland_pointer_start_grab (&seat->pointer, (MetaWaylandPointerGrab*)drag_grab);
+
+  meta_wayland_keyboard_set_focus (&seat->keyboard, NULL);
+  meta_wayland_keyboard_start_grab (&seat->keyboard, &drag_grab->keyboard_grab);
 }
 
 static void
diff --git a/src/wayland/meta-wayland-versions.h b/src/wayland/meta-wayland-versions.h
index 3944956..d601148 100644
--- a/src/wayland/meta-wayland-versions.h
+++ b/src/wayland/meta-wayland-versions.h
@@ -36,7 +36,7 @@
 
 /* Global/master objects (version exported by wl_registry and negotiated through bind) */
 #define META_WL_COMPOSITOR_VERSION          3
-#define META_WL_DATA_DEVICE_MANAGER_VERSION 2
+#define META_WL_DATA_DEVICE_MANAGER_VERSION 3
 #define META_XDG_SHELL_VERSION              1
 #define META_WL_SHELL_VERSION               1
 #define META_WL_SEAT_VERSION                4


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