mirror of
https://github.com/brl/mutter.git
synced 2024-11-21 15:40:41 -05:00
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.
This commit is contained in:
parent
ce78db31b7
commit
319d0bb679
@ -42,12 +42,18 @@ struct _MetaWaylandDataOffer
|
||||
struct wl_resource *resource;
|
||||
MetaWaylandDataSource *source;
|
||||
struct wl_listener source_destroy_listener;
|
||||
uint32_t dnd_actions;
|
||||
uint32_t preferred_dnd_action;
|
||||
};
|
||||
|
||||
typedef struct _MetaWaylandDataSourcePrivate
|
||||
{
|
||||
MetaWaylandDataOffer *offer;
|
||||
struct wl_array mime_types;
|
||||
gboolean has_target;
|
||||
uint32_t dnd_actions;
|
||||
uint32_t user_dnd_action;
|
||||
uint32_t current_dnd_action;
|
||||
} MetaWaylandDataSourcePrivate;
|
||||
|
||||
typedef struct _MetaWaylandDataSourceWayland
|
||||
@ -74,6 +80,50 @@ 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 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 0;
|
||||
|
||||
/* 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)
|
||||
{
|
||||
uint32_t current_action, action;
|
||||
|
||||
if (!offer->source)
|
||||
return;
|
||||
|
||||
current_action = meta_wayland_data_source_get_current_action (offer->source);
|
||||
action = data_offer_choose_action (offer);
|
||||
|
||||
if (current_action == action)
|
||||
return;
|
||||
|
||||
meta_wayland_data_source_set_current_action (offer->source, action);
|
||||
wl_data_offer_send_action (offer->resource, action);
|
||||
}
|
||||
|
||||
static void
|
||||
meta_wayland_data_source_target (MetaWaylandDataSource *source,
|
||||
const char *mime_type)
|
||||
@ -123,6 +173,106 @@ 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;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
meta_wayland_data_source_get_user_action (MetaWaylandDataSource *source)
|
||||
{
|
||||
MetaWaylandDataSourcePrivate *priv =
|
||||
meta_wayland_data_source_get_instance_private (source);
|
||||
|
||||
return priv->user_dnd_action;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
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,
|
||||
uint32_t action)
|
||||
{
|
||||
MetaWaylandDataSourcePrivate *priv =
|
||||
meta_wayland_data_source_get_instance_private (source);
|
||||
|
||||
if (priv->current_dnd_action == action)
|
||||
return;
|
||||
|
||||
priv->current_dnd_action = action;
|
||||
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);
|
||||
MetaWaylandDataOffer *offer;
|
||||
|
||||
if (priv->dnd_actions == dnd_actions)
|
||||
return;
|
||||
|
||||
priv->dnd_actions = dnd_actions;
|
||||
offer = meta_wayland_data_source_get_current_offer (source);
|
||||
|
||||
if (offer)
|
||||
{
|
||||
wl_data_offer_send_source_actions (offer->resource,
|
||||
priv->dnd_actions);
|
||||
data_offer_update_action (offer);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
meta_wayland_data_source_set_user_action (MetaWaylandDataSource *source,
|
||||
uint32_t 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 +311,59 @@ data_offer_destroy (struct wl_client *client, struct wl_resource *resource)
|
||||
wl_resource_destroy (resource);
|
||||
}
|
||||
|
||||
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 (offer->dnd_actions == dnd_actions &&
|
||||
offer->preferred_dnd_action == 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_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))
|
||||
meta_wayland_data_source_notify_finish (offer->source);
|
||||
|
||||
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 +391,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 +413,27 @@ 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);
|
||||
|
||||
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 +452,7 @@ struct _MetaWaylandDragGrab {
|
||||
struct wl_listener drag_origin_listener;
|
||||
|
||||
int drag_start_x, drag_start_y;
|
||||
ClutterModifierType buttons;
|
||||
};
|
||||
|
||||
static void
|
||||
@ -256,6 +461,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;
|
||||
}
|
||||
|
||||
void
|
||||
@ -285,6 +491,7 @@ meta_wayland_drag_grab_set_focus (MetaWaylandDragGrab *drag_grab,
|
||||
client = wl_resource_get_client (surface->resource);
|
||||
|
||||
data_device_resource = wl_resource_find_for_client (&seat->data_device.resource_list, client);
|
||||
meta_wayland_data_source_set_current_offer (drag_grab->drag_data_source, NULL);
|
||||
|
||||
if (drag_grab->drag_data_source && data_device_resource)
|
||||
offer = meta_wayland_data_source_send_offer (drag_grab->drag_data_source,
|
||||
@ -312,6 +519,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)
|
||||
{
|
||||
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;
|
||||
|
||||
meta_wayland_data_source_set_user_action (source, user_dnd_action);
|
||||
}
|
||||
|
||||
static void
|
||||
drag_grab_motion (MetaWaylandPointerGrab *grab,
|
||||
const ClutterEvent *event)
|
||||
@ -356,7 +579,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);
|
||||
}
|
||||
|
||||
@ -371,13 +602,23 @@ drag_grab_button (MetaWaylandPointerGrab *grab,
|
||||
if (drag_grab->generic.pointer->grab_button == clutter_event_get_button (event) &&
|
||||
event_type == CLUTTER_BUTTON_RELEASE)
|
||||
{
|
||||
MetaWaylandDataSource *data_source = drag_grab->drag_data_source;
|
||||
gboolean success = FALSE;
|
||||
|
||||
if (meta_wayland_data_source_has_target (drag_grab->drag_data_source))
|
||||
if (drag_grab->drag_focus &&
|
||||
meta_wayland_data_source_get_current_action (drag_grab->drag_data_source) &&
|
||||
meta_wayland_data_source_has_target (drag_grab->drag_data_source))
|
||||
{
|
||||
meta_wayland_surface_drag_dest_drop (drag_grab->drag_focus);
|
||||
meta_wayland_data_source_notify_drop_performed (data_source);
|
||||
success = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
meta_wayland_data_source_cancel (data_source);
|
||||
meta_wayland_data_source_set_current_offer (data_source, NULL);
|
||||
meta_wayland_data_device_set_dnd_source (&drag_grab->seat->data_device, NULL);
|
||||
}
|
||||
|
||||
/* Finish drag and let actor self-destruct */
|
||||
meta_dnd_actor_drag_finish (META_DND_ACTOR (drag_grab->feedback_actor),
|
||||
@ -396,6 +637,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)
|
||||
{
|
||||
@ -440,12 +715,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;
|
||||
|
||||
@ -460,6 +739,11 @@ 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);
|
||||
|
||||
g_object_weak_ref (G_OBJECT (source),
|
||||
drag_grab_data_source_destroyed,
|
||||
drag_grab);
|
||||
@ -467,6 +751,7 @@ meta_wayland_data_device_start_drag (MetaWaylandDataDevice *data
|
||||
drag_grab->drag_data_source = 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)
|
||||
{
|
||||
@ -548,6 +833,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
|
||||
@ -600,6 +889,34 @@ meta_wayland_source_cancel (MetaWaylandDataSource *source)
|
||||
wl_data_source_send_cancelled (source_wayland->resource);
|
||||
}
|
||||
|
||||
static void
|
||||
meta_wayland_source_action (MetaWaylandDataSource *source,
|
||||
uint32_t action)
|
||||
{
|
||||
MetaWaylandDataSourceWayland *source_wayland =
|
||||
META_WAYLAND_DATA_SOURCE_WAYLAND (source);
|
||||
|
||||
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);
|
||||
|
||||
wl_data_source_send_drop_performed (source_wayland->resource);
|
||||
}
|
||||
|
||||
static void
|
||||
meta_wayland_source_drag_finished (MetaWaylandDataSource *source)
|
||||
{
|
||||
MetaWaylandDataSourceWayland *source_wayland =
|
||||
META_WAYLAND_DATA_SOURCE_WAYLAND (source);
|
||||
|
||||
wl_data_source_send_drag_finished (source_wayland->resource);
|
||||
}
|
||||
|
||||
static void
|
||||
meta_wayland_source_finalize (GObject *object)
|
||||
{
|
||||
@ -623,6 +940,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
|
||||
@ -647,6 +967,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
|
||||
@ -722,11 +1043,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 *
|
||||
|
@ -45,6 +45,11 @@ struct _MetaWaylandDataSourceClass
|
||||
void (* target) (MetaWaylandDataSource *source,
|
||||
const gchar *mime_type);
|
||||
void (* cancel) (MetaWaylandDataSource *source);
|
||||
|
||||
void (* action) (MetaWaylandDataSource *source,
|
||||
uint32_t action);
|
||||
void (* drop_performed) (MetaWaylandDataSource *source);
|
||||
void (* drag_finished) (MetaWaylandDataSource *source);
|
||||
};
|
||||
|
||||
struct _MetaWaylandDataDevice
|
||||
@ -95,6 +100,17 @@ void meta_wayland_data_source_send (MetaWaylandDataSource *source,
|
||||
const gchar *mime_type,
|
||||
gint fd);
|
||||
|
||||
void meta_wayland_data_source_notify_finish (MetaWaylandDataSource *source);
|
||||
|
||||
uint32_t meta_wayland_data_source_get_actions (MetaWaylandDataSource *source);
|
||||
uint32_t meta_wayland_data_source_get_user_action (MetaWaylandDataSource *source);
|
||||
uint32_t meta_wayland_data_source_get_current_action (MetaWaylandDataSource *source);
|
||||
|
||||
void meta_wayland_data_source_set_actions (MetaWaylandDataSource *source,
|
||||
uint32_t dnd_actions);
|
||||
void meta_wayland_data_source_set_current_action (MetaWaylandDataSource *source,
|
||||
uint32_t action);
|
||||
|
||||
const MetaWaylandDragDestFuncs *
|
||||
meta_wayland_data_device_get_drag_dest_funcs (void);
|
||||
|
||||
|
@ -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
|
||||
|
@ -108,6 +108,7 @@ enum {
|
||||
ATOM_DND_ACTION_MOVE,
|
||||
ATOM_DND_ACTION_COPY,
|
||||
ATOM_DND_ACTION_ASK,
|
||||
ATOM_DND_ACTION_PRIVATE,
|
||||
N_DND_ATOMS
|
||||
};
|
||||
|
||||
@ -126,6 +127,7 @@ const gchar *atom_names[] = {
|
||||
"XdndActionMove",
|
||||
"XdndActionCopy",
|
||||
"XdndActionAsk",
|
||||
"XdndActionPrivate",
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -135,6 +137,19 @@ G_DEFINE_TYPE (MetaWaylandDataSourceXWayland, meta_wayland_data_source_xwayland,
|
||||
META_TYPE_WAYLAND_DATA_SOURCE);
|
||||
|
||||
/* XDND helpers */
|
||||
static Atom
|
||||
action_to_atom (uint32_t action)
|
||||
{
|
||||
if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY)
|
||||
return xdnd_atoms[ATOM_DND_ACTION_COPY];
|
||||
else if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE)
|
||||
return xdnd_atoms[ATOM_DND_ACTION_MOVE];
|
||||
else if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)
|
||||
return xdnd_atoms[ATOM_DND_ACTION_ASK];
|
||||
else
|
||||
return None;
|
||||
}
|
||||
|
||||
static void
|
||||
xdnd_send_enter (MetaXWaylandSelection *selection_data,
|
||||
Window dest)
|
||||
@ -217,10 +232,21 @@ xdnd_send_position (MetaXWaylandSelection *selection_data,
|
||||
int x,
|
||||
int y)
|
||||
{
|
||||
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
|
||||
MetaSelectionBridge *selection = &selection_data->dnd.selection;
|
||||
MetaWaylandDataSource *source = compositor->seat->data_device.dnd_data_source;
|
||||
Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
|
||||
uint32_t action = 0, user_action, actions;
|
||||
XEvent xev = { 0 };
|
||||
|
||||
user_action = meta_wayland_data_source_get_user_action (source);
|
||||
actions = meta_wayland_data_source_get_actions (source);
|
||||
|
||||
if (user_action & actions)
|
||||
action = user_action;
|
||||
if (!action)
|
||||
action = actions;
|
||||
|
||||
xev.xclient.type = ClientMessage;
|
||||
xev.xclient.message_type = xdnd_atoms[ATOM_DND_POSITION];
|
||||
xev.xclient.format = 32;
|
||||
@ -230,7 +256,7 @@ xdnd_send_position (MetaXWaylandSelection *selection_data,
|
||||
xev.xclient.data.l[1] = 0;
|
||||
xev.xclient.data.l[2] = (x << 16) | y;
|
||||
xev.xclient.data.l[3] = time;
|
||||
xev.xclient.data.l[4] = xdnd_atoms[ATOM_DND_ACTION_COPY];
|
||||
xev.xclient.data.l[4] = action_to_atom (action);
|
||||
|
||||
XSendEvent (xdisplay, dest, False, NoEventMask, &xev);
|
||||
}
|
||||
@ -262,6 +288,8 @@ xdnd_send_finished (MetaXWaylandSelection *selection_data,
|
||||
{
|
||||
MetaDndBridge *selection = &selection_data->dnd;
|
||||
Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
|
||||
MetaWaylandDataSource *source = selection_data->dnd.selection.source;
|
||||
uint32_t action = 0;
|
||||
XEvent xev = { 0 };
|
||||
|
||||
xev.xclient.type = ClientMessage;
|
||||
@ -273,8 +301,9 @@ xdnd_send_finished (MetaXWaylandSelection *selection_data,
|
||||
|
||||
if (accepted)
|
||||
{
|
||||
action = meta_wayland_data_source_get_current_action (source);
|
||||
xev.xclient.data.l[1] = 1; /* Drop successful */
|
||||
xev.xclient.data.l[2] = xdnd_atoms[ATOM_DND_ACTION_COPY];
|
||||
xev.xclient.data.l[2] = action_to_atom (action);
|
||||
}
|
||||
|
||||
XSendEvent (xdisplay, dest, False, NoEventMask, &xev);
|
||||
@ -283,7 +312,7 @@ xdnd_send_finished (MetaXWaylandSelection *selection_data,
|
||||
static void
|
||||
xdnd_send_status (MetaXWaylandSelection *selection_data,
|
||||
Window dest,
|
||||
gboolean accepted)
|
||||
uint32_t action)
|
||||
{
|
||||
MetaDndBridge *selection = &selection_data->dnd;
|
||||
Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
|
||||
@ -296,12 +325,10 @@ xdnd_send_status (MetaXWaylandSelection *selection_data,
|
||||
|
||||
xev.xclient.data.l[0] = selection->dnd_window;
|
||||
xev.xclient.data.l[1] = 1 << 1; /* Bit 2: dest wants XdndPosition messages */
|
||||
xev.xclient.data.l[4] = action_to_atom (action);
|
||||
|
||||
if (accepted)
|
||||
{
|
||||
xev.xclient.data.l[1] |= 1 << 0; /* Bit 1: dest accepts the drop */
|
||||
xev.xclient.data.l[4] = xdnd_atoms[ATOM_DND_ACTION_COPY];
|
||||
}
|
||||
if (xev.xclient.data.l[4])
|
||||
xev.xclient.data.l[1] |= 1 << 0; /* Bit 1: dest accepts the drop */
|
||||
|
||||
XSendEvent (xdisplay, dest, False, NoEventMask, &xev);
|
||||
}
|
||||
@ -387,6 +414,32 @@ x11_selection_data_free (X11SelectionData *data)
|
||||
g_slice_free (X11SelectionData, data);
|
||||
}
|
||||
|
||||
static void
|
||||
x11_selection_data_send_finished (MetaSelectionBridge *selection,
|
||||
gboolean success)
|
||||
{
|
||||
uint32_t action = meta_wayland_data_source_get_current_action (selection->source);
|
||||
|
||||
if (!selection->x11_selection)
|
||||
return;
|
||||
|
||||
if (success && action == WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE)
|
||||
{
|
||||
Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
|
||||
|
||||
/* Request data deletion on the drag source */
|
||||
XConvertSelection (xdisplay,
|
||||
selection->selection_atom,
|
||||
gdk_x11_get_xatom_by_name ("DELETE"),
|
||||
gdk_x11_get_xatom_by_name ("_META_SELECTION"),
|
||||
selection->window,
|
||||
CurrentTime);
|
||||
}
|
||||
|
||||
xdnd_send_finished (selection->x11_selection->selection_data,
|
||||
selection->owner, success);
|
||||
}
|
||||
|
||||
static void
|
||||
x11_selection_data_finish (MetaSelectionBridge *selection,
|
||||
gboolean success)
|
||||
@ -395,13 +448,18 @@ x11_selection_data_finish (MetaSelectionBridge *selection,
|
||||
return;
|
||||
|
||||
if (selection == &selection->x11_selection->selection_data->dnd.selection)
|
||||
xdnd_send_finished (selection->x11_selection->selection_data,
|
||||
selection->owner, success);
|
||||
x11_selection_data_send_finished (selection, success);
|
||||
|
||||
g_clear_pointer (&selection->x11_selection,
|
||||
(GDestroyNotify) x11_selection_data_free);
|
||||
}
|
||||
|
||||
static void
|
||||
x11_selection_data_close (X11SelectionData *data)
|
||||
{
|
||||
g_output_stream_close (data->stream, data->cancellable, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
x11_data_write_cb (GObject *object,
|
||||
GAsyncResult *res,
|
||||
@ -435,6 +493,7 @@ x11_data_write_cb (GObject *object,
|
||||
}
|
||||
else
|
||||
{
|
||||
x11_selection_data_close (selection->x11_selection);
|
||||
x11_selection_data_finish (selection, success);
|
||||
}
|
||||
}
|
||||
@ -693,6 +752,7 @@ meta_xwayland_selection_get_incr_chunk (MetaWaylandCompositor *compositor,
|
||||
else
|
||||
{
|
||||
/* Transfer has completed */
|
||||
x11_selection_data_close (selection->x11_selection);
|
||||
x11_selection_data_finish (selection, TRUE);
|
||||
}
|
||||
|
||||
@ -741,11 +801,15 @@ meta_x11_source_target (MetaWaylandDataSource *source,
|
||||
MetaWaylandDataSourceXWayland *source_xwayland =
|
||||
META_WAYLAND_DATA_SOURCE_XWAYLAND (source);
|
||||
MetaSelectionBridge *selection = source_xwayland->selection;
|
||||
uint32_t action = 0;
|
||||
|
||||
if (selection->selection_atom == xdnd_atoms[ATOM_DND_SELECTION])
|
||||
{
|
||||
if (mime_type)
|
||||
action = meta_wayland_data_source_get_current_action (source);
|
||||
|
||||
xdnd_send_status (compositor->xwayland_manager.selection_data,
|
||||
selection->owner, mime_type != NULL);
|
||||
selection->owner, action);
|
||||
}
|
||||
}
|
||||
|
||||
@ -756,10 +820,46 @@ meta_x11_source_cancel (MetaWaylandDataSource *source)
|
||||
META_WAYLAND_DATA_SOURCE_XWAYLAND (source);
|
||||
MetaSelectionBridge *selection = source_xwayland->selection;
|
||||
|
||||
x11_selection_data_send_finished (selection, FALSE);
|
||||
g_clear_pointer (&selection->x11_selection,
|
||||
(GDestroyNotify) x11_selection_data_free);
|
||||
}
|
||||
|
||||
static void
|
||||
meta_x11_source_action (MetaWaylandDataSource *source,
|
||||
uint32_t action)
|
||||
{
|
||||
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
|
||||
MetaWaylandDataSourceXWayland *source_xwayland =
|
||||
META_WAYLAND_DATA_SOURCE_XWAYLAND (source);
|
||||
MetaSelectionBridge *selection = source_xwayland->selection;
|
||||
|
||||
if (selection->selection_atom == xdnd_atoms[ATOM_DND_SELECTION])
|
||||
{
|
||||
if (!meta_wayland_data_source_has_target (source))
|
||||
action = 0;
|
||||
|
||||
xdnd_send_status (compositor->xwayland_manager.selection_data,
|
||||
selection->owner, action);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
meta_x11_source_drop_performed (MetaWaylandDataSource *source)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
meta_x11_source_drag_finished (MetaWaylandDataSource *source)
|
||||
{
|
||||
MetaWaylandDataSourceXWayland *source_xwayland =
|
||||
META_WAYLAND_DATA_SOURCE_XWAYLAND (source);
|
||||
MetaSelectionBridge *selection = source_xwayland->selection;
|
||||
|
||||
if (selection->x11_selection)
|
||||
x11_selection_data_send_finished (selection, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
meta_wayland_data_source_xwayland_init (MetaWaylandDataSourceXWayland *source_xwayland)
|
||||
{
|
||||
@ -774,6 +874,9 @@ meta_wayland_data_source_xwayland_class_init (MetaWaylandDataSourceXWaylandClass
|
||||
data_source_class->send = meta_x11_source_send;
|
||||
data_source_class->target = meta_x11_source_target;
|
||||
data_source_class->cancel = meta_x11_source_cancel;
|
||||
data_source_class->action = meta_x11_source_action;
|
||||
data_source_class->drop_performed = meta_x11_source_drop_performed;
|
||||
data_source_class->drag_finished = meta_x11_source_drag_finished;
|
||||
}
|
||||
|
||||
static MetaWaylandDataSource *
|
||||
@ -837,11 +940,27 @@ meta_x11_drag_dest_drop (MetaWaylandDataDevice *data_device,
|
||||
meta_display_get_current_time_roundtrip (meta_get_display ()));
|
||||
}
|
||||
|
||||
static void
|
||||
meta_x11_drag_dest_update (MetaWaylandDataDevice *data_device,
|
||||
MetaWaylandSurface *surface)
|
||||
{
|
||||
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
|
||||
MetaWaylandSeat *seat = compositor->seat;
|
||||
ClutterPoint pos;
|
||||
|
||||
clutter_input_device_get_coords (seat->pointer.device, NULL, &pos);
|
||||
xdnd_send_position (compositor->xwayland_manager.selection_data,
|
||||
compositor->xwayland_manager.selection_data->dnd.dnd_dest,
|
||||
clutter_get_current_event_time (),
|
||||
pos.x, pos.y);
|
||||
}
|
||||
|
||||
static const MetaWaylandDragDestFuncs meta_x11_drag_dest_funcs = {
|
||||
meta_x11_drag_dest_focus_in,
|
||||
meta_x11_drag_dest_focus_out,
|
||||
meta_x11_drag_dest_motion,
|
||||
meta_x11_drag_dest_drop
|
||||
meta_x11_drag_dest_drop,
|
||||
meta_x11_drag_dest_update
|
||||
};
|
||||
|
||||
const MetaWaylandDragDestFuncs *
|
||||
@ -1135,6 +1254,10 @@ meta_xwayland_selection_handle_selection_request (MetaWaylandCompositor *composi
|
||||
selection->timestamp);
|
||||
reply_selection_request (event, TRUE);
|
||||
}
|
||||
else if (data_source && event->target == gdk_x11_get_xatom_by_name ("DELETE"))
|
||||
{
|
||||
meta_wayland_data_source_notify_finish (data_source);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (data_source &&
|
||||
@ -1257,6 +1380,7 @@ meta_xwayland_selection_handle_client_message (MetaWaylandCompositor *compositor
|
||||
if (event->window == dnd->selection.window)
|
||||
{
|
||||
MetaWaylandDataSource *data_source;
|
||||
uint32_t action = 0;
|
||||
|
||||
data_source = compositor->seat->data_device.dnd_data_source;
|
||||
|
||||
@ -1269,6 +1393,19 @@ meta_xwayland_selection_handle_client_message (MetaWaylandCompositor *compositor
|
||||
meta_wayland_data_source_set_has_target (data_source,
|
||||
(event->data.l[1] & 1) != 0);
|
||||
|
||||
/* data.l[4] contains the action atom */
|
||||
if (event->data.l[4])
|
||||
{
|
||||
if (((Atom) event->data.l[4]) == xdnd_atoms[ATOM_DND_ACTION_COPY] ||
|
||||
((Atom) event->data.l[4]) == xdnd_atoms[ATOM_DND_ACTION_PRIVATE])
|
||||
action = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
|
||||
else if (((Atom) event->data.l[4]) == xdnd_atoms[ATOM_DND_ACTION_MOVE])
|
||||
action = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
|
||||
else if (((Atom) event->data.l[4]) == xdnd_atoms[ATOM_DND_ACTION_ASK])
|
||||
action = WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK;
|
||||
}
|
||||
|
||||
meta_wayland_data_source_set_current_action (data_source, action);
|
||||
return TRUE;
|
||||
}
|
||||
else if (event->message_type == xdnd_atoms[ATOM_DND_FINISHED])
|
||||
@ -1277,8 +1414,7 @@ meta_xwayland_selection_handle_client_message (MetaWaylandCompositor *compositor
|
||||
if (compositor->seat->data_device.current_grab)
|
||||
return FALSE;
|
||||
|
||||
meta_wayland_data_device_set_dnd_source (&compositor->seat->data_device,
|
||||
NULL);
|
||||
meta_wayland_data_source_notify_finish (data_source);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
@ -1338,6 +1474,7 @@ meta_xwayland_selection_handle_client_message (MetaWaylandCompositor *compositor
|
||||
{
|
||||
ClutterEvent *motion;
|
||||
ClutterPoint pos;
|
||||
uint32_t action = 0;
|
||||
|
||||
motion = clutter_event_new (CLUTTER_MOTION);
|
||||
clutter_input_device_get_coords (seat->pointer.device, NULL, &pos);
|
||||
@ -1346,11 +1483,19 @@ meta_xwayland_selection_handle_client_message (MetaWaylandCompositor *compositor
|
||||
clutter_event_set_source_device (motion, seat->pointer.device);
|
||||
clutter_event_set_time (motion, dnd->last_motion_time);
|
||||
|
||||
if ((Atom) event->data.l[4] == xdnd_atoms[ATOM_DND_ACTION_COPY])
|
||||
action = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
|
||||
else if ((Atom) event->data.l[4] == xdnd_atoms[ATOM_DND_ACTION_MOVE])
|
||||
action = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
|
||||
else if ((Atom) event->data.l[4] == xdnd_atoms[ATOM_DND_ACTION_ASK])
|
||||
action = WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK;
|
||||
|
||||
meta_wayland_data_source_set_actions (dnd->selection.source, action);
|
||||
|
||||
meta_wayland_surface_drag_dest_motion (drag_focus, motion);
|
||||
xdnd_send_status (compositor->xwayland_manager.selection_data,
|
||||
(Window) event->data.l[0],
|
||||
meta_wayland_data_source_has_target (
|
||||
dnd->selection.source));
|
||||
meta_wayland_data_source_get_current_action (dnd->selection.source));
|
||||
|
||||
clutter_event_free (motion);
|
||||
return TRUE;
|
||||
|
Loading…
Reference in New Issue
Block a user