data_device: Add dnd implementation to ds_data_offer 22/279822/1
authorSeunghun Lee <shiin.lee@samsung.com>
Thu, 11 Aug 2022 07:05:16 +0000 (16:05 +0900)
committerTizen Window System <tizen.windowsystem@gmail.com>
Thu, 18 Aug 2022 07:37:50 +0000 (16:37 +0900)
Change-Id: I5c38f00421dda52dcaa08bf90cca18b2bc73f939

src/data_device/data_device_private.h
src/data_device/data_offer.c

index 5cd1c4d..e766cd3 100644 (file)
@@ -6,9 +6,14 @@
 #include "data_device.h"
 #include "seat.h"
 
+#define DATA_DEVICE_ALL_ACTIONS (WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | \
+        WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | \
+        WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)
+
 enum ds_data_offer_type
 {
     DS_DATA_OFFER_SELECTION,
+    DS_DATA_OFFER_DRAG,
 };
 
 struct ds_data_device_manager
@@ -59,6 +64,7 @@ struct ds_data_offer
     enum ds_data_offer_type type;
 
     uint32_t actions;
+    enum wl_data_device_manager_dnd_action preferred_action;
 
     struct wl_list link; // ds_data_device.data_offers
 
index 4c1395b..23d71c1 100644 (file)
@@ -1,5 +1,8 @@
+#include <assert.h>
 #include <stdlib.h>
+#include <strings.h>
 
+#include "libds/log.h"
 #include "data_device_private.h"
 
 static const struct wl_data_offer_interface data_offer_iface;
@@ -7,6 +10,8 @@ static const struct wl_data_offer_interface data_offer_iface;
 static void data_offer_handle_resource_destroy(struct wl_resource *resource);
 static void data_offer_handle_source_destroy(struct wl_listener *listener,
         void *data);
+static uint32_t data_offer_choose_action(struct ds_data_offer *offer);
+static void data_offer_source_dnd_finish(struct ds_data_offer *offer);
 
 WL_EXPORT void
 ds_data_offer_send(struct ds_data_offer *offer, const char *mime_type,
@@ -55,6 +60,26 @@ create_data_offer(struct ds_data_device *data_device,
     return offer;
 }
 
+void
+data_offer_update_action(struct ds_data_offer *offer)
+{
+    uint32_t action;
+
+    assert(offer->type == DS_DATA_OFFER_DRAG);
+
+    action = data_offer_choose_action(offer);
+    if (offer->source->current_dnd_action == action)
+        return;
+
+    offer->source->current_dnd_action = action;
+
+    ds_data_source_dnd_action(offer->source, action);
+
+    if (wl_resource_get_version(offer->resource) >=
+            WL_DATA_OFFER_ACTION_SINCE_VERSION)
+        wl_data_offer_send_action(offer->resource, action);
+}
+
 static void
 data_offer_destroy(struct ds_data_offer *offer)
 {
@@ -96,7 +121,13 @@ data_offer_handle_accept(struct wl_client *client,
     if (!offer)
         return;
 
-    // TODO
+    if (offer->type != DS_DATA_OFFER_DRAG) {
+        ds_dbg("Ignoring wl_data_offer.accept request on a "
+                "non-drag-and-drop offer");
+        return;
+    }
+
+    ds_data_source_accept(offer->source, serial, mime_type);
 }
 
 static void
@@ -131,12 +162,38 @@ data_offer_handle_finish(struct wl_client *client,
         struct wl_resource *resource)
 {
     struct ds_data_offer *offer;
+    enum wl_data_device_manager_dnd_action action;
 
     offer = wl_resource_get_user_data(resource);
     if (!offer)
         return;
 
-    // TODO
+    // TODO: also fail while we have a drag-and-drop grab
+    if (offer->type != DS_DATA_OFFER_DRAG) {
+        wl_resource_post_error(offer->resource,
+                WL_DATA_OFFER_ERROR_INVALID_FINISH,
+                "Offer is not drag-and-drop");
+        return;
+    }
+
+    if (!offer->source->accepted) {
+        wl_resource_post_error(offer->resource,
+                WL_DATA_OFFER_ERROR_INVALID_FINISH,
+                "Premature finish request");
+        return;
+    }
+
+    action = offer->source->current_dnd_action;
+    if (action == WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE ||
+            action == WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) {
+        wl_resource_post_error(offer->resource,
+                WL_DATA_OFFER_ERROR_INVALID_FINISH,
+                "Offer finished with an invalid action");
+        return;
+    }
+
+    data_offer_source_dnd_finish(offer);
+    data_offer_destroy(offer);
 }
 
 static void
@@ -150,7 +207,30 @@ data_offer_handle_set_actions(struct wl_client *client,
     if (!offer)
         return;
 
-    // TODO
+    if (actions & ~DATA_DEVICE_ALL_ACTIONS) {
+        wl_resource_post_error(offer->resource,
+                WL_DATA_OFFER_ERROR_INVALID_ACTION_MASK,
+                "invalid action mask %x", actions);
+        return;
+    }
+
+    if (preferred_action && (!(preferred_action & actions) ||
+                __builtin_popcount(preferred_action) > 1)) {
+        wl_resource_post_error(offer->resource,
+                WL_DATA_OFFER_ERROR_INVALID_ACTION,
+                "invalid action %x", preferred_action);
+        return;
+    }
+
+    if (offer->type != DS_DATA_OFFER_DRAG) {
+        wl_resource_post_error(offer->resource,
+                WL_DATA_OFFER_ERROR_INVALID_OFFER,
+                "set_action can only be sent to drag-and-drop offers");
+        return;
+    }
+
+    offer->actions = actions;
+    offer->preferred_action = preferred_action;
 }
 
 static const struct wl_data_offer_interface data_offer_iface = {
@@ -160,3 +240,46 @@ static const struct wl_data_offer_interface data_offer_iface = {
     .finish = data_offer_handle_finish,
     .set_actions = data_offer_handle_set_actions,
 };
+
+static uint32_t
+data_offer_choose_action(struct ds_data_offer *offer)
+{
+    uint32_t offer_actions, preferred_action = 0;
+    uint32_t source_actions, available_actions;
+
+    if (wl_resource_get_version(offer->resource) >=
+            WL_DATA_OFFER_ACTION_SINCE_VERSION) {
+        offer_actions = offer->actions;
+        preferred_action = offer->preferred_action;
+    }
+    else {
+        offer_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
+    }
+
+    if (offer->source->actions >= 0)
+        source_actions = offer->source->actions;
+    else
+        source_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
+
+    available_actions = offer_actions & source_actions;
+    if (!available_actions)
+        return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
+
+    if ((preferred_action & available_actions) != 0)
+        return preferred_action;
+
+    // Use the first found action, in bit order
+    return 1 << (ffs(available_actions) - 1);
+}
+
+static void
+data_offer_source_dnd_finish(struct ds_data_offer *offer)
+{
+    struct ds_data_source *source;
+
+    source = offer->source;
+    if (source->actions < 0)
+        return;
+
+    ds_data_source_dnd_finish(source);
+}