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.dnd_drop_performed
  - wl_data_source.dnd_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.

The Xdnd bridge code is also modified to cope with actions, so mixed
wayland-x11 scenarios are able to convey that information.

https://bugzilla.gnome.org/show_bug.cgi?id=760805
This commit is contained in:
Carlos Garnacho
2015-04-07 16:05:46 +02:00
parent f053c09083
commit 9b26694bbc
4 changed files with 693 additions and 27 deletions

View File

@@ -37,17 +37,30 @@
#include "meta-wayland-private.h"
#include "meta-dnd-actor-private.h"
#define ALL_ACTIONS (WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | \
WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | \
WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)
struct _MetaWaylandDataOffer
{
struct wl_resource *resource;
MetaWaylandDataSource *source;
struct wl_listener source_destroy_listener;
uint32_t dnd_actions;
enum wl_data_device_manager_dnd_action preferred_dnd_action;
};
typedef struct _MetaWaylandDataSourcePrivate
{
MetaWaylandDataOffer *offer;
struct wl_array mime_types;
gboolean has_target;
uint32_t dnd_actions;
enum wl_data_device_manager_dnd_action user_dnd_action;
enum wl_data_device_manager_dnd_action current_dnd_action;
MetaWaylandSeat *seat;
guint actions_set : 1;
guint in_ask : 1;
} MetaWaylandDataSourcePrivate;
typedef struct _MetaWaylandDataSourceWayland
@@ -74,6 +87,75 @@ unbind_resource (struct wl_resource *resource)
wl_list_remove (wl_resource_get_link (resource));
}
static gboolean
meta_wayland_source_get_in_ask (MetaWaylandDataSource *source)
{
MetaWaylandDataSourcePrivate *priv =
meta_wayland_data_source_get_instance_private (source);
return priv->in_ask;
}
static void
meta_wayland_source_update_in_ask (MetaWaylandDataSource *source)
{
MetaWaylandDataSourcePrivate *priv =
meta_wayland_data_source_get_instance_private (source);
priv->in_ask =
priv->current_dnd_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK;
}
static enum wl_data_device_manager_dnd_action
data_offer_choose_action (MetaWaylandDataOffer *offer)
{
MetaWaylandDataSource *source = offer->source;
uint32_t actions, user_action, available_actions;
actions = meta_wayland_data_source_get_actions (source);
user_action = meta_wayland_data_source_get_user_action (source);
available_actions = actions & offer->dnd_actions;
if (!available_actions)
return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
/* If the user is forcing an action, go for it */
if ((user_action & available_actions) != 0)
return user_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_offer_update_action (MetaWaylandDataOffer *offer)
{
enum wl_data_device_manager_dnd_action current_action, action;
MetaWaylandDataSource *source;
if (!offer->source)
return;
source = offer->source;
current_action = meta_wayland_data_source_get_current_action (source);
action = data_offer_choose_action (offer);
if (current_action == action)
return;
meta_wayland_data_source_set_current_action (source, action);
if (!meta_wayland_source_get_in_ask (source) &&
wl_resource_get_version (offer->resource) >=
WL_DATA_OFFER_ACTION_SINCE_VERSION)
wl_data_offer_send_action (offer->resource, action);
}
static void
meta_wayland_data_source_target (MetaWaylandDataSource *source,
const char *mime_type)
@@ -98,6 +180,25 @@ meta_wayland_data_source_has_target (MetaWaylandDataSource *source)
return priv->has_target;
}
static void
meta_wayland_data_source_set_seat (MetaWaylandDataSource *source,
MetaWaylandSeat *seat)
{
MetaWaylandDataSourcePrivate *priv =
meta_wayland_data_source_get_instance_private (source);
priv->seat = seat;
}
static MetaWaylandSeat *
meta_wayland_data_source_get_seat (MetaWaylandDataSource *source)
{
MetaWaylandDataSourcePrivate *priv =
meta_wayland_data_source_get_instance_private (source);
return priv->seat;
}
void
meta_wayland_data_source_set_has_target (MetaWaylandDataSource *source,
gboolean has_target)
@@ -123,6 +224,100 @@ meta_wayland_data_source_cancel (MetaWaylandDataSource *source)
META_WAYLAND_DATA_SOURCE_GET_CLASS (source)->cancel (source);
}
uint32_t
meta_wayland_data_source_get_actions (MetaWaylandDataSource *source)
{
MetaWaylandDataSourcePrivate *priv =
meta_wayland_data_source_get_instance_private (source);
return priv->dnd_actions;
}
enum wl_data_device_manager_dnd_action
meta_wayland_data_source_get_user_action (MetaWaylandDataSource *source)
{
MetaWaylandDataSourcePrivate *priv =
meta_wayland_data_source_get_instance_private (source);
if (!priv->seat)
return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
return priv->user_dnd_action;
}
enum wl_data_device_manager_dnd_action
meta_wayland_data_source_get_current_action (MetaWaylandDataSource *source)
{
MetaWaylandDataSourcePrivate *priv =
meta_wayland_data_source_get_instance_private (source);
return priv->current_dnd_action;
}
static void
meta_wayland_data_source_set_current_offer (MetaWaylandDataSource *source,
MetaWaylandDataOffer *offer)
{
MetaWaylandDataSourcePrivate *priv =
meta_wayland_data_source_get_instance_private (source);
priv->offer = offer;
}
static MetaWaylandDataOffer *
meta_wayland_data_source_get_current_offer (MetaWaylandDataSource *source)
{
MetaWaylandDataSourcePrivate *priv =
meta_wayland_data_source_get_instance_private (source);
return priv->offer;
}
void
meta_wayland_data_source_set_current_action (MetaWaylandDataSource *source,
enum wl_data_device_manager_dnd_action action)
{
MetaWaylandDataSourcePrivate *priv =
meta_wayland_data_source_get_instance_private (source);
if (priv->current_dnd_action == action)
return;
priv->current_dnd_action = action;
if (!meta_wayland_source_get_in_ask (source))
META_WAYLAND_DATA_SOURCE_GET_CLASS (source)->action (source, action);
}
void
meta_wayland_data_source_set_actions (MetaWaylandDataSource *source,
uint32_t dnd_actions)
{
MetaWaylandDataSourcePrivate *priv =
meta_wayland_data_source_get_instance_private (source);
priv->dnd_actions = dnd_actions;
priv->actions_set = TRUE;
}
static void
meta_wayland_data_source_set_user_action (MetaWaylandDataSource *source,
enum wl_data_device_manager_dnd_action action)
{
MetaWaylandDataSourcePrivate *priv =
meta_wayland_data_source_get_instance_private (source);
MetaWaylandDataOffer *offer;
if (priv->user_dnd_action == action)
return;
priv->user_dnd_action = action;
offer = meta_wayland_data_source_get_current_offer (source);
if (offer)
data_offer_update_action (offer);
}
static void
data_offer_accept (struct wl_client *client,
struct wl_resource *resource,
@@ -161,21 +356,117 @@ data_offer_destroy (struct wl_client *client, struct wl_resource *resource)
wl_resource_destroy (resource);
}
static void
data_offer_finish (struct wl_client *client,
struct wl_resource *resource)
{
MetaWaylandDataOffer *offer = wl_resource_get_user_data (resource);
enum wl_data_device_manager_dnd_action current_action;
if (!offer->source ||
offer != meta_wayland_data_source_get_current_offer (offer->source))
return;
if (meta_wayland_data_source_get_seat (offer->source) ||
!meta_wayland_data_source_has_target (offer->source))
{
wl_resource_post_error (offer->resource,
WL_DATA_OFFER_ERROR_INVALID_FINISH,
"premature finish request");
return;
}
current_action = meta_wayland_data_source_get_current_action (offer->source);
if (current_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE ||
current_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)
{
wl_resource_post_error (offer->resource,
WL_DATA_OFFER_ERROR_INVALID_OFFER,
"offer finished with an invalid action");
return;
}
meta_wayland_data_source_notify_finish (offer->source);
}
static void
data_offer_set_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);
if (dnd_actions & ~(ALL_ACTIONS))
{
wl_resource_post_error (offer->resource,
WL_DATA_OFFER_ERROR_INVALID_ACTION_MASK,
"invalid actions mask %x", dnd_actions);
return;
}
if (preferred_action &&
(!(preferred_action & dnd_actions) ||
__builtin_popcount (preferred_action) > 1))
{
wl_resource_post_error (offer->resource,
WL_DATA_OFFER_ERROR_INVALID_ACTION,
"invalid action %x", preferred_action);
return;
}
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_finish,
data_offer_set_actions,
};
static void
meta_wayland_data_source_notify_drop_performed (MetaWaylandDataSource *source)
{
META_WAYLAND_DATA_SOURCE_GET_CLASS (source)->drop_performed (source);
}
void
meta_wayland_data_source_notify_finish (MetaWaylandDataSource *source)
{
META_WAYLAND_DATA_SOURCE_GET_CLASS (source)->drag_finished (source);
}
static void
destroy_data_offer (struct wl_resource *resource)
{
MetaWaylandDataOffer *offer = wl_resource_get_user_data (resource);
if (offer->source)
g_object_remove_weak_pointer (G_OBJECT (offer->source),
(gpointer *)&offer->source);
{
if (offer == meta_wayland_data_source_get_current_offer (offer->source))
{
if (wl_resource_get_version (offer->resource) <
WL_DATA_OFFER_ACTION_SINCE_VERSION)
meta_wayland_data_source_notify_finish (offer->source);
else
{
meta_wayland_data_source_cancel (offer->source);
meta_wayland_data_source_set_current_offer (offer->source, NULL);
}
}
g_object_remove_weak_pointer (G_OBJECT (offer->source),
(gpointer *)&offer->source);
offer->source = NULL;
}
meta_display_sync_wayland_input_focus (meta_get_display ());
g_slice_free (MetaWaylandDataOffer, offer);
}
@@ -203,6 +494,9 @@ meta_wayland_data_source_send_offer (MetaWaylandDataSource *source,
wl_array_for_each (p, &priv->mime_types)
wl_data_offer_send_offer (offer->resource, *p);
data_offer_update_action (offer);
meta_wayland_data_source_set_current_offer (source, offer);
return offer->resource;
}
@@ -222,14 +516,56 @@ data_source_destroy (struct wl_client *client, struct wl_resource *resource)
wl_resource_destroy (resource);
}
static void
data_source_set_actions (struct wl_client *client,
struct wl_resource *resource,
uint32_t dnd_actions)
{
MetaWaylandDataSource *source = wl_resource_get_user_data (resource);
MetaWaylandDataSourcePrivate *priv =
meta_wayland_data_source_get_instance_private (source);
MetaWaylandDataSourceWayland *source_wayland =
META_WAYLAND_DATA_SOURCE_WAYLAND (source);
if (priv->actions_set)
{
wl_resource_post_error (source_wayland->resource,
WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK,
"cannot set actions more than once");
return;
}
if (dnd_actions & ~(ALL_ACTIONS))
{
wl_resource_post_error (source_wayland->resource,
WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK,
"invalid actions mask %x", dnd_actions);
return;
}
if (meta_wayland_data_source_get_seat (source))
{
wl_resource_post_error (source_wayland->resource,
WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK,
"invalid action change after "
"wl_data_device.start_drag");
return;
}
meta_wayland_data_source_set_actions (source, dnd_actions);
}
static struct wl_data_source_interface data_source_interface = {
data_source_offer,
data_source_destroy
data_source_destroy,
data_source_set_actions
};
struct _MetaWaylandDragGrab {
MetaWaylandPointerGrab generic;
MetaWaylandKeyboardGrab keyboard_grab;
MetaWaylandSeat *seat;
struct wl_client *drag_client;
@@ -248,6 +584,7 @@ struct _MetaWaylandDragGrab {
struct wl_listener drag_origin_listener;
int drag_start_x, drag_start_y;
ClutterModifierType buttons;
};
static void
@@ -256,6 +593,7 @@ destroy_drag_focus (struct wl_listener *listener, void *data)
MetaWaylandDragGrab *grab = wl_container_of (listener, grab, drag_focus_listener);
grab->drag_focus_data_device = NULL;
grab->drag_focus = NULL;
}
static void
@@ -292,6 +630,9 @@ meta_wayland_drag_grab_set_focus (MetaWaylandDragGrab *drag_grab,
drag_grab->drag_focus = NULL;
}
if (drag_grab->drag_data_source)
meta_wayland_data_source_set_current_offer (drag_grab->drag_data_source, NULL);
if (!surface)
return;
@@ -329,6 +670,22 @@ drag_grab_focus (MetaWaylandPointerGrab *grab,
meta_wayland_drag_grab_set_focus (drag_grab, surface);
}
static void
data_source_update_user_dnd_action (MetaWaylandDataSource *source,
ClutterModifierType modifiers)
{
enum wl_data_device_manager_dnd_action 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;
meta_wayland_data_source_set_user_action (source, user_dnd_action);
}
static void
drag_grab_motion (MetaWaylandPointerGrab *grab,
const ClutterEvent *event)
@@ -370,7 +727,15 @@ data_device_end_drag_grab (MetaWaylandDragGrab *drag_grab)
drag_grab->seat->data_device.current_grab = NULL;
meta_wayland_pointer_end_grab (drag_grab->generic.pointer);
/* There might be other grabs created in result to DnD actions like popups
* on "ask" actions, we must not reset those, only our own.
*/
if (drag_grab->generic.pointer->grab == (MetaWaylandPointerGrab *) drag_grab)
{
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);
}
@@ -385,17 +750,33 @@ drag_grab_button (MetaWaylandPointerGrab *grab,
if (drag_grab->generic.pointer->grab_button == clutter_event_get_button (event) &&
event_type == CLUTTER_BUTTON_RELEASE)
{
gboolean success = FALSE;
MetaWaylandDataSource *source = drag_grab->drag_data_source;
gboolean success;
if (meta_wayland_data_source_has_target (drag_grab->drag_data_source))
if (drag_grab->drag_focus && source &&
meta_wayland_data_source_has_target (source) &&
meta_wayland_data_source_get_current_action (source))
{
/* Detach the data source from the grab, it's meant to live longer */
meta_wayland_drag_grab_set_source (drag_grab, NULL);
meta_wayland_data_source_set_seat (source, NULL);
meta_wayland_surface_drag_dest_drop (drag_grab->drag_focus);
meta_wayland_data_source_notify_drop_performed (source);
meta_wayland_source_update_in_ask (source);
success = TRUE;
}
else
{
meta_wayland_data_source_cancel (source);
meta_wayland_data_source_set_current_offer (source, NULL);
meta_wayland_data_device_set_dnd_source (&seat->data_device, NULL);
success= FALSE;
}
/* Finish drag and let actor self-destruct */
meta_dnd_actor_drag_finish (META_DND_ACTOR (drag_grab->feedback_actor),
success);
meta_dnd_actor_drag_finish (META_DND_ACTOR (drag_grab->feedback_actor), success);
drag_grab->feedback_actor = NULL;
}
@@ -410,6 +791,40 @@ 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);
if (drag_grab->drag_focus)
meta_wayland_surface_drag_dest_update (drag_grab->drag_focus);
}
}
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)
{
@@ -454,12 +869,16 @@ meta_wayland_data_device_start_drag (MetaWaylandDataDevice *data
MetaWaylandSeat *seat = wl_container_of (data_device, seat, data_device);
MetaWaylandDragGrab *drag_grab;
ClutterPoint pos, stage_pos;
ClutterModifierType modifiers;
data_device->current_grab = drag_grab = g_slice_new0 (MetaWaylandDragGrab);
drag_grab->generic.interface = funcs;
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;
@@ -474,9 +893,15 @@ meta_wayland_data_device_start_drag (MetaWaylandDataDevice *data
drag_grab->drag_start_x = stage_pos.x;
drag_grab->drag_start_y = stage_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);
meta_wayland_drag_grab_set_source (drag_grab, source);
meta_wayland_data_device_set_dnd_source (data_device,
drag_grab->drag_data_source);
data_source_update_user_dnd_action (source, modifiers);
if (icon_surface)
{
@@ -500,6 +925,7 @@ meta_wayland_data_device_start_drag (MetaWaylandDataDevice *data
}
meta_wayland_pointer_start_grab (&seat->pointer, (MetaWaylandPointerGrab*) drag_grab);
meta_wayland_data_source_set_seat (source, seat);
}
void
@@ -558,6 +984,10 @@ data_device_start_drag (struct wl_client *client,
meta_wayland_data_device_start_drag (data_device, client,
&drag_grab_interface,
surface, drag_source, icon_surface);
meta_wayland_keyboard_set_focus (&seat->keyboard, NULL);
meta_wayland_keyboard_start_grab (&seat->keyboard,
&seat->data_device.current_grab->keyboard_grab);
}
static void
@@ -610,6 +1040,47 @@ meta_wayland_source_cancel (MetaWaylandDataSource *source)
wl_data_source_send_cancelled (source_wayland->resource);
}
static void
meta_wayland_source_action (MetaWaylandDataSource *source,
enum wl_data_device_manager_dnd_action action)
{
MetaWaylandDataSourceWayland *source_wayland =
META_WAYLAND_DATA_SOURCE_WAYLAND (source);
if (wl_resource_get_version (source_wayland->resource) >=
WL_DATA_SOURCE_ACTION_SINCE_VERSION)
wl_data_source_send_action (source_wayland->resource, action);
}
static void
meta_wayland_source_drop_performed (MetaWaylandDataSource *source)
{
MetaWaylandDataSourceWayland *source_wayland =
META_WAYLAND_DATA_SOURCE_WAYLAND (source);
if (wl_resource_get_version (source_wayland->resource) >=
WL_DATA_SOURCE_DND_DROP_PERFORMED_SINCE_VERSION)
wl_data_source_send_dnd_drop_performed (source_wayland->resource);
}
static void
meta_wayland_source_drag_finished (MetaWaylandDataSource *source)
{
MetaWaylandDataSourceWayland *source_wayland =
META_WAYLAND_DATA_SOURCE_WAYLAND (source);
enum wl_data_device_manager_dnd_action action;
if (meta_wayland_source_get_in_ask (source))
{
action = meta_wayland_data_source_get_current_action (source);
meta_wayland_source_action (source, action);
}
if (wl_resource_get_version (source_wayland->resource) >=
WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION)
wl_data_source_send_dnd_finished (source_wayland->resource);
}
static void
meta_wayland_source_finalize (GObject *object)
{
@@ -633,6 +1104,9 @@ meta_wayland_data_source_wayland_class_init (MetaWaylandDataSourceWaylandClass *
data_source_class->send = meta_wayland_source_send;
data_source_class->target = meta_wayland_source_target;
data_source_class->cancel = meta_wayland_source_cancel;
data_source_class->action = meta_wayland_source_action;
data_source_class->drop_performed = meta_wayland_source_drop_performed;
data_source_class->drag_finished = meta_wayland_source_drag_finished;
}
static void
@@ -657,6 +1131,7 @@ meta_wayland_data_source_init (MetaWaylandDataSource *source)
meta_wayland_data_source_get_instance_private (source);
wl_array_init (&priv->mime_types);
priv->current_dnd_action = -1;
}
static void
@@ -675,6 +1150,7 @@ meta_wayland_drag_dest_focus_in (MetaWaylandDataDevice *data_device,
MetaWaylandDragGrab *grab = data_device->current_grab;
struct wl_display *display;
struct wl_client *client;
uint32_t source_actions;
wl_fixed_t sx, sy;
if (!grab->drag_focus_data_device)
@@ -687,6 +1163,13 @@ meta_wayland_drag_dest_focus_in (MetaWaylandDataDevice *data_device,
wl_resource_add_destroy_listener (grab->drag_focus_data_device,
&grab->drag_focus_listener);
if (wl_resource_get_version (offer->resource) >=
WL_DATA_OFFER_SOURCE_ACTIONS_SINCE_VERSION)
{
source_actions = meta_wayland_data_source_get_actions (offer->source);
wl_data_offer_send_source_actions (offer->resource, source_actions);
}
meta_wayland_pointer_get_relative_coordinates (grab->generic.pointer,
surface, &sx, &sy);
wl_data_device_send_enter (grab->drag_focus_data_device,
@@ -732,11 +1215,18 @@ meta_wayland_drag_dest_drop (MetaWaylandDataDevice *data_device,
wl_data_device_send_drop (grab->drag_focus_data_device);
}
static void
meta_wayland_drag_dest_update (MetaWaylandDataDevice *data_device,
MetaWaylandSurface *surface)
{
}
static const MetaWaylandDragDestFuncs meta_wayland_drag_dest_funcs = {
meta_wayland_drag_dest_focus_in,
meta_wayland_drag_dest_focus_out,
meta_wayland_drag_dest_motion,
meta_wayland_drag_dest_drop
meta_wayland_drag_dest_drop,
meta_wayland_drag_dest_update
};
const MetaWaylandDragDestFuncs *
@@ -825,6 +1315,7 @@ data_device_set_selection (struct wl_client *client,
guint32 serial)
{
MetaWaylandDataDevice *data_device = wl_resource_get_user_data (resource);
MetaWaylandDataSourcePrivate *priv;
MetaWaylandDataSource *source;
if (source_resource)
@@ -832,6 +1323,19 @@ data_device_set_selection (struct wl_client *client,
else
source = NULL;
if (source)
{
priv = meta_wayland_data_source_get_instance_private (source);
if (priv->actions_set)
{
wl_resource_post_error(source_resource,
WL_DATA_SOURCE_ERROR_INVALID_SOURCE,
"cannot set drag-and-drop source as selection");
return;
}
}
/* FIXME: Store serial and check against incoming serial here. */
meta_wayland_data_device_set_selection (data_device, source, serial);
}