summaryrefslogtreecommitdiff
path: root/src/wayland/meta-xwayland-selection.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/wayland/meta-xwayland-selection.c')
-rw-r--r--src/wayland/meta-xwayland-selection.c177
1 files changed, 161 insertions, 16 deletions
diff --git a/src/wayland/meta-xwayland-selection.c b/src/wayland/meta-xwayland-selection.c
index 081a24439..5140d0481 100644
--- a/src/wayland/meta-xwayland-selection.c
+++ b/src/wayland/meta-xwayland-selection.c
@@ -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);
}
@@ -388,6 +415,32 @@ x11_selection_data_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,14 +448,19 @@ 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,
gpointer user_data)
@@ -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,11 +820,47 @@ 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;