+#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;
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,
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)
{
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
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
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 = {
.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);
+}