xwayland: Implement X11-to-wayland DnD

When DnD is started from an X11 client, mutter now sets up an special
grab that 1) Ensures the drag source keeps receiving events, and 2)
Moves an internal X Window over wayland clients as soon as the pointer
enters over these.

That window will act as the X-side peer for the currently focused
wayland client, and will transform XdndEnter/Position/Leave/Drop
messages into wayland actions. If DnD happens between X11 clients,
the window will be moved away and unmapped, to let these operate as
usual.

https://bugzilla.gnome.org/show_bug.cgi?id=738312
This commit is contained in:
Carlos Garnacho 2015-05-12 19:35:28 +02:00
parent ccb7833e99
commit 4a968c3b4e
3 changed files with 441 additions and 77 deletions

View File

@ -190,11 +190,10 @@ destroy_drag_focus (struct wl_listener *listener, void *data)
grab->drag_focus_data_device = NULL;
}
static void
drag_grab_focus (MetaWaylandPointerGrab *grab,
void
meta_wayland_drag_grab_set_focus (MetaWaylandDragGrab *drag_grab,
MetaWaylandSurface *surface)
{
MetaWaylandDragGrab *drag_grab = (MetaWaylandDragGrab*) grab;
MetaWaylandSeat *seat = drag_grab->seat;
struct wl_client *client;
struct wl_resource *data_device_resource, *offer = NULL;
@ -230,6 +229,21 @@ drag_grab_focus (MetaWaylandPointerGrab *grab,
offer ? wl_resource_get_user_data (offer) : NULL);
}
MetaWaylandSurface *
meta_wayland_drag_grab_get_focus (MetaWaylandDragGrab *drag_grab)
{
return drag_grab->drag_focus;
}
static void
drag_grab_focus (MetaWaylandPointerGrab *grab,
MetaWaylandSurface *surface)
{
MetaWaylandDragGrab *drag_grab = (MetaWaylandDragGrab*) grab;
meta_wayland_drag_grab_set_focus (drag_grab, surface);
}
static void
drag_grab_motion (MetaWaylandPointerGrab *grab,
const ClutterEvent *event)
@ -247,7 +261,7 @@ drag_grab_motion (MetaWaylandPointerGrab *grab,
static void
data_device_end_drag_grab (MetaWaylandDragGrab *drag_grab)
{
drag_grab_focus (&drag_grab->generic, NULL);
meta_wayland_drag_grab_set_focus (drag_grab, NULL);
if (drag_grab->drag_origin)
{
@ -261,7 +275,8 @@ data_device_end_drag_grab (MetaWaylandDragGrab *drag_grab)
wl_list_remove (&drag_grab->drag_icon_listener.link);
}
if (drag_grab->drag_data_source)
if (drag_grab->drag_data_source &&
drag_grab->drag_data_source->resource)
wl_list_remove (&drag_grab->drag_data_source_listener.link);
if (drag_grab->feedback_actor)
@ -347,6 +362,82 @@ destroy_data_device_icon (struct wl_listener *listener, void *data)
clutter_actor_remove_all_children (drag_grab->feedback_actor);
}
void
meta_wayland_data_device_start_drag (MetaWaylandDataDevice *data_device,
struct wl_client *client,
const MetaWaylandPointerGrabInterface *funcs,
MetaWaylandSurface *surface,
MetaWaylandDataSource *source,
MetaWaylandSurface *icon_surface)
{
MetaWaylandSeat *seat = wl_container_of (data_device, seat, data_device);
MetaWaylandDragGrab *drag_grab;
ClutterPoint pos, stage_pos;
data_device->current_grab = drag_grab = g_slice_new0 (MetaWaylandDragGrab);
drag_grab->generic.interface = funcs;
drag_grab->generic.pointer = &seat->pointer;
drag_grab->drag_client = client;
drag_grab->seat = seat;
drag_grab->drag_origin = surface;
drag_grab->drag_origin_listener.notify = destroy_data_device_origin;
wl_resource_add_destroy_listener (surface->resource,
&drag_grab->drag_origin_listener);
clutter_input_device_get_coords (seat->pointer.device, NULL, &pos);
clutter_actor_transform_stage_point (CLUTTER_ACTOR (meta_surface_actor_get_texture (surface->surface_actor)),
pos.x, pos.y, &stage_pos.x, &stage_pos.y);
drag_grab->drag_start_x = stage_pos.x;
drag_grab->drag_start_y = stage_pos.y;
if (source)
{
if (source->resource)
{
drag_grab->drag_data_source_listener.notify = destroy_data_device_source;
wl_resource_add_destroy_listener (source->resource,
&drag_grab->drag_data_source_listener);
}
drag_grab->drag_data_source = source;
meta_wayland_data_device_set_dnd_source (data_device,
drag_grab->drag_data_source);
}
if (icon_surface)
{
drag_grab->drag_surface = icon_surface;
drag_grab->drag_icon_listener.notify = destroy_data_device_icon;
wl_resource_add_destroy_listener (icon_surface->resource,
&drag_grab->drag_icon_listener);
drag_grab->feedback_actor = meta_dnd_actor_new (CLUTTER_ACTOR (drag_grab->drag_origin->surface_actor),
drag_grab->drag_start_x,
drag_grab->drag_start_y);
meta_feedback_actor_set_anchor (META_FEEDBACK_ACTOR (drag_grab->feedback_actor),
-drag_grab->drag_surface->offset_x,
-drag_grab->drag_surface->offset_y);
clutter_actor_add_child (drag_grab->feedback_actor,
CLUTTER_ACTOR (drag_grab->drag_surface->surface_actor));
meta_feedback_actor_set_position (META_FEEDBACK_ACTOR (drag_grab->feedback_actor),
pos.x, pos.y);
}
meta_wayland_pointer_start_grab (&seat->pointer, (MetaWaylandPointerGrab*) drag_grab);
}
void
meta_wayland_data_device_end_drag (MetaWaylandDataDevice *data_device)
{
if (data_device->current_grab)
data_device_end_drag_grab (data_device->current_grab);
}
static void
data_device_start_drag (struct wl_client *client,
struct wl_resource *resource,
@ -356,9 +447,8 @@ data_device_start_drag (struct wl_client *client,
{
MetaWaylandDataDevice *data_device = wl_resource_get_user_data (resource);
MetaWaylandSeat *seat = wl_container_of (data_device, seat, data_device);
MetaWaylandSurface *surface = NULL;
MetaWaylandDragGrab *drag_grab;
ClutterPoint pos;
MetaWaylandSurface *surface = NULL, *icon_surface = NULL;
MetaWaylandDataSource *drag_source = NULL;
if (origin_resource)
surface = wl_resource_get_user_data (origin_resource);
@ -378,66 +468,22 @@ data_device_start_drag (struct wl_client *client,
seat->pointer.grab != &seat->pointer.default_grab)
return;
if (icon_resource)
icon_surface = wl_resource_get_user_data (icon_resource);
if (source_resource)
drag_source = wl_resource_get_user_data (source_resource);
if (icon_resource &&
meta_wayland_surface_set_role (wl_resource_get_user_data (icon_resource),
meta_wayland_surface_set_role (icon_surface,
META_WAYLAND_SURFACE_ROLE_DND,
resource,
WL_DATA_DEVICE_ERROR_ROLE) != 0)
return;
data_device->current_grab = drag_grab = g_slice_new0 (MetaWaylandDragGrab);
drag_grab->generic.interface = &drag_grab_interface;
drag_grab->generic.pointer = &seat->pointer;
drag_grab->drag_client = client;
drag_grab->seat = seat;
drag_grab->drag_origin = surface;
drag_grab->drag_origin_listener.notify = destroy_data_device_origin;
wl_resource_add_destroy_listener (origin_resource,
&drag_grab->drag_origin_listener);
clutter_input_device_get_coords (seat->pointer.device, NULL, &pos);
clutter_actor_transform_stage_point (CLUTTER_ACTOR (meta_surface_actor_get_texture (surface->surface_actor)),
pos.x, pos.y, &pos.x, &pos.y);
drag_grab->drag_start_x = pos.x;
drag_grab->drag_start_y = pos.y;
if (source_resource)
{
drag_grab->drag_data_source = wl_resource_get_user_data (source_resource);
drag_grab->drag_data_source_listener.notify = destroy_data_device_source;
wl_resource_add_destroy_listener (source_resource,
&drag_grab->drag_data_source_listener);
meta_wayland_data_device_set_dnd_source (data_device,
drag_grab->drag_data_source);
}
if (icon_resource)
{
drag_grab->drag_surface = wl_resource_get_user_data (icon_resource);
drag_grab->drag_icon_listener.notify = destroy_data_device_icon;
wl_resource_add_destroy_listener (icon_resource,
&drag_grab->drag_icon_listener);
drag_grab->feedback_actor = meta_dnd_actor_new (CLUTTER_ACTOR (drag_grab->drag_origin->surface_actor),
drag_grab->drag_start_x,
drag_grab->drag_start_y);
meta_feedback_actor_set_anchor (META_FEEDBACK_ACTOR (drag_grab->feedback_actor),
-drag_grab->drag_surface->offset_x,
-drag_grab->drag_surface->offset_y);
clutter_actor_add_child (drag_grab->feedback_actor,
CLUTTER_ACTOR (drag_grab->drag_surface->surface_actor));
clutter_input_device_get_coords (seat->pointer.device, NULL, &pos);
meta_feedback_actor_set_position (META_FEEDBACK_ACTOR (drag_grab->feedback_actor),
pos.x, pos.y);
}
meta_wayland_pointer_set_focus (&seat->pointer, NULL);
meta_wayland_pointer_start_grab (&seat->pointer, (MetaWaylandPointerGrab*)drag_grab);
meta_wayland_data_device_start_drag (data_device, client,
&drag_grab_interface,
surface, drag_source, icon_surface);
}
static void

View File

@ -96,4 +96,18 @@ void meta_wayland_data_source_send (MetaWaylandDataSource *source,
const MetaWaylandDragDestFuncs *
meta_wayland_data_device_get_drag_dest_funcs (void);
void meta_wayland_data_device_start_drag (MetaWaylandDataDevice *data_device,
struct wl_client *client,
const MetaWaylandPointerGrabInterface *funcs,
MetaWaylandSurface *surface,
MetaWaylandDataSource *source,
MetaWaylandSurface *icon_surface);
void meta_wayland_data_device_end_drag (MetaWaylandDataDevice *data_device);
void meta_wayland_drag_grab_set_focus (MetaWaylandDragGrab *drag_grab,
MetaWaylandSurface *surface);
MetaWaylandSurface *
meta_wayland_drag_grab_get_focus (MetaWaylandDragGrab *drag_grab);
#endif /* META_WAYLAND_DATA_DEVICE_H */

View File

@ -64,7 +64,7 @@ typedef struct {
Window window;
Window owner;
Time timestamp;
const MetaWaylandDataSource *source;
MetaWaylandDataSource *source; /* owned by MetaWaylandDataDevice */
WaylandSelectionData *wayland_selection;
X11SelectionData *x11_selection;
@ -73,7 +73,10 @@ typedef struct {
typedef struct {
MetaSelectionBridge selection;
Window dnd_dest;
MetaWaylandSurface *focus_surface;
Window dnd_window; /* Mutter-internal window, acts as peer on wayland drop sites */
Window dnd_dest; /* X11 drag dest window */
guint32 last_motion_time;
} MetaDndBridge;
struct _MetaXWaylandSelection {
@ -239,12 +242,93 @@ xdnd_send_drop (MetaXWaylandSelection *selection_data,
}
static void
meta_xwayland_init_dnd (void)
xdnd_send_finished (MetaXWaylandSelection *selection_data,
Window dest,
gboolean accepted)
{
guint i;
MetaDndBridge *selection = &selection_data->dnd;
Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
XEvent xev = { 0 };
xev.xclient.type = ClientMessage;
xev.xclient.message_type = xdnd_atoms[ATOM_DND_FINISHED];
xev.xclient.format = 32;
xev.xclient.window = dest;
xev.xclient.data.l[0] = selection->dnd_window;
if (accepted)
{
xev.xclient.data.l[1] = 1; /* Drop successful */
xev.xclient.data.l[2] = xdnd_atoms[ATOM_DND_ACTION_COPY];
}
XSendEvent (xdisplay, dest, False, NoEventMask, &xev);
}
static void
xdnd_send_status (MetaXWaylandSelection *selection_data,
Window dest,
gboolean accepted)
{
MetaDndBridge *selection = &selection_data->dnd;
Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
XEvent xev = { 0 };
xev.xclient.type = ClientMessage;
xev.xclient.message_type = xdnd_atoms[ATOM_DND_STATUS];
xev.xclient.format = 32;
xev.xclient.window = dest;
xev.xclient.data.l[0] = selection->dnd_window;
xev.xclient.data.l[1] = 1 << 1; /* Bit 2: dest wants XdndPosition messages */
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];
}
XSendEvent (xdisplay, dest, False, NoEventMask, &xev);
}
static void
meta_xwayland_init_dnd (MetaXWaylandManager *manager)
{
Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
MetaDndBridge *dnd = &manager->selection_data->dnd;
XSetWindowAttributes attributes;
guint32 i, version = XDND_VERSION;
for (i = 0; i < N_DND_ATOMS; i++)
xdnd_atoms[i] = gdk_x11_get_xatom_by_name (atom_names[i]);
attributes.event_mask = PropertyChangeMask | SubstructureNotifyMask;
attributes.override_redirect = True;
dnd->dnd_window = XCreateWindow (xdisplay,
gdk_x11_window_get_xid (gdk_get_default_root_window ()),
-1, -1, 1, 1,
0, /* border width */
0, /* depth */
InputOnly, /* class */
CopyFromParent, /* visual */
CWEventMask | CWOverrideRedirect,
&attributes);
XChangeProperty (xdisplay, dnd->dnd_window,
xdnd_atoms[ATOM_DND_AWARE],
XA_ATOM, 32, PropModeReplace,
(guchar*) &version, 1);
}
static void
meta_xwayland_shutdown_dnd (MetaXWaylandManager *manager)
{
MetaDndBridge *dnd = &manager->selection_data->dnd;
XDestroyWindow (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
dnd->dnd_window);
dnd->dnd_window = None;
}
/* X11/Wayland data bridges */
@ -289,6 +373,21 @@ x11_selection_data_free (X11SelectionData *data)
g_slice_free (X11SelectionData, data);
}
static void
x11_selection_data_finish (MetaSelectionBridge *selection,
gboolean success)
{
if (!selection->x11_selection)
return;
if (selection == &selection->x11_selection->selection_data->dnd.selection)
xdnd_send_finished (selection->x11_selection->selection_data,
selection->owner, success);
g_clear_pointer (&selection->x11_selection,
(GDestroyNotify) x11_selection_data_free);
}
static void
x11_data_write_cb (GObject *object,
GAsyncResult *res,
@ -317,10 +416,7 @@ x11_data_write_cb (GObject *object,
}
if (!data->incr)
{
g_clear_pointer (&selection->x11_selection,
(GDestroyNotify) x11_selection_data_free);
}
x11_selection_data_finish (selection, TRUE);
}
static void
@ -577,8 +673,7 @@ meta_xwayland_selection_get_incr_chunk (MetaWaylandCompositor *compositor,
else
{
/* Transfer has completed */
g_clear_pointer (&selection->x11_selection,
(GDestroyNotify) x11_selection_data_free);
x11_selection_data_finish (selection, TRUE);
}
XFree (prop_ret);
@ -599,8 +694,8 @@ meta_x11_source_send (MetaWaylandDataSource *source,
else
type_atom = gdk_x11_get_xatom_by_name (mime_type);
g_clear_pointer (&selection->x11_selection,
(GDestroyNotify) x11_selection_data_free);
/* Ensure we close previous transactions */
x11_selection_data_finish (selection, FALSE);
/* Takes ownership of fd */
selection->x11_selection =
@ -619,6 +714,14 @@ static void
meta_x11_source_target (MetaWaylandDataSource *source,
const gchar *mime_type)
{
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
MetaSelectionBridge *selection = source->user_data;
if (selection->selection_atom == xdnd_atoms[ATOM_DND_SELECTION])
{
xdnd_send_status (compositor->xwayland_manager.selection_data,
selection->owner, mime_type != NULL);
}
}
static void
@ -709,6 +812,9 @@ meta_xwayland_data_source_fetch_mimetype_list (MetaWaylandDataSource *source,
Atom *atoms, type_ret, utf8_string;
int format_ret;
if (source->mime_types.size != 0)
return TRUE;
utf8_string = gdk_x11_get_xatom_by_name ("UTF8_STRING");
XGetWindowProperty (xdisplay, window, prop,
0, /* offset */
@ -994,15 +1100,106 @@ meta_xwayland_selection_handle_selection_request (MetaWaylandCompositor *composi
return TRUE;
}
static MetaWaylandSurface *
pick_drop_surface (MetaWaylandCompositor *compositor,
const ClutterEvent *event)
{
MetaDisplay *display = meta_get_display ();
MetaWindow *focus_window = NULL;
ClutterPoint pos;
clutter_event_get_coords (event, &pos.x, &pos.y);
focus_window = meta_stack_get_default_focus_window_at_point (display->screen->stack,
NULL, NULL,
pos.x, pos.y);
return focus_window ? focus_window->surface : NULL;
}
static void
repick_drop_surface (MetaWaylandCompositor *compositor,
MetaWaylandDragGrab *drag_grab,
const ClutterEvent *event)
{
Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
MetaDndBridge *dnd = &compositor->xwayland_manager.selection_data->dnd;
MetaWaylandSurface *focus = NULL;
focus = pick_drop_surface (compositor, event);
dnd->focus_surface = focus;
if (meta_wayland_drag_grab_get_focus (drag_grab) == focus)
return;
if (focus &&
focus->window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND)
{
XMapWindow (xdisplay, dnd->dnd_window);
XMoveResizeWindow (xdisplay, dnd->dnd_window,
focus->window->rect.x,
focus->window->rect.y,
focus->window->rect.width,
focus->window->rect.height);
}
else
{
XMoveResizeWindow (xdisplay, dnd->dnd_window, -1, -1, 1, 1);
XUnmapWindow (xdisplay, dnd->dnd_window);
}
}
static void
drag_xgrab_focus (MetaWaylandPointerGrab *grab,
MetaWaylandSurface *surface)
{
/* Do not update the focus here. First, the surface may perfectly
* be the X11 source DnD icon window's, so we can only be fooled
* here. Second, delaying focus handling to XdndEnter/Leave
* makes us do the negotiation orderly on the X11 side.
*/
}
static void
drag_xgrab_motion (MetaWaylandPointerGrab *grab,
const ClutterEvent *event)
{
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
MetaDndBridge *dnd = &compositor->xwayland_manager.selection_data->dnd;
MetaWaylandSeat *seat = compositor->seat;
repick_drop_surface (compositor,
(MetaWaylandDragGrab *) grab,
event);
dnd->last_motion_time = clutter_event_get_time (event);
meta_wayland_pointer_send_motion (&seat->pointer, event);
}
static void
drag_xgrab_button (MetaWaylandPointerGrab *grab,
const ClutterEvent *event)
{
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
MetaWaylandSeat *seat = compositor->seat;
meta_wayland_pointer_send_button (&seat->pointer, event);
}
static const MetaWaylandPointerGrabInterface drag_xgrab_interface = {
drag_xgrab_focus,
drag_xgrab_motion,
drag_xgrab_button,
};
static gboolean
meta_xwayland_selection_handle_client_message (MetaWaylandCompositor *compositor,
XEvent *xevent)
{
XClientMessageEvent *event = (XClientMessageEvent *) xevent;
MetaSelectionBridge *selection = &compositor->xwayland_manager.selection_data->dnd.selection;
MetaDndBridge *dnd = &compositor->xwayland_manager.selection_data->dnd;
MetaWaylandSeat *seat = compositor->seat;
/* Source side messages */
if (event->window == selection->window)
if (event->window == dnd->selection.window)
{
MetaWaylandDataSource *data_source;
@ -1029,6 +1226,83 @@ meta_xwayland_selection_handle_client_message (MetaWaylandCompositor *compositor
return TRUE;
}
}
/* Dest side messages */
else if (dnd->selection.source &&
compositor->seat->data_device.current_grab &&
(Window) event->data.l[0] == dnd->selection.owner)
{
MetaWaylandDragGrab *drag_grab = compositor->seat->data_device.current_grab;
MetaWaylandSurface *drag_focus = meta_wayland_drag_grab_get_focus (drag_grab);
if (event->message_type == xdnd_atoms[ATOM_DND_ENTER])
{
/* Bit 1 in data.l[1] determines whether there's 3 or less mimetype
* atoms (and are thus contained in this same message), or whether
* there's more than 3 and we need to check the XdndTypeList property
* for the full list.
*/
if (!(event->data.l[1] & 1))
{
/* Mimetypes are contained in this message */
const gchar *mimetype;
gint i;
/* We only need to fetch once */
if (dnd->selection.source->mime_types.size == 0)
{
for (i = 2; i <= 4; i++)
{
if (event->data.l[i] == None)
break;
mimetype = gdk_x11_get_xatom_name (event->data.l[i]);
meta_wayland_data_source_add_mime_type (dnd->selection.source,
mimetype);
}
}
}
else
{
/* Fetch mimetypes from type list */
meta_xwayland_data_source_fetch_mimetype_list (dnd->selection.source,
event->data.l[0],
xdnd_atoms[ATOM_DND_TYPE_LIST]);
}
meta_wayland_drag_grab_set_focus (drag_grab, dnd->focus_surface);
return TRUE;
}
else if (event->message_type == xdnd_atoms[ATOM_DND_POSITION])
{
ClutterEvent *motion;
ClutterPoint pos;
motion = clutter_event_new (CLUTTER_MOTION);
clutter_input_device_get_coords (seat->pointer.device, NULL, &pos);
clutter_event_set_coords (motion, pos.x, pos.y);
clutter_event_set_device (motion, seat->pointer.device);
clutter_event_set_source_device (motion, seat->pointer.device);
clutter_event_set_time (motion, dnd->last_motion_time);
meta_wayland_surface_drag_dest_motion (drag_focus, motion);
xdnd_send_status (compositor->xwayland_manager.selection_data,
(Window) event->data.l[0],
dnd->selection.source->has_target);
clutter_event_free (motion);
return TRUE;
}
else if (event->message_type == xdnd_atoms[ATOM_DND_LEAVE])
{
meta_wayland_drag_grab_set_focus (drag_grab, NULL);
return TRUE;
}
else if (event->message_type == xdnd_atoms[ATOM_DND_DROP])
{
meta_wayland_surface_drag_dest_drop (drag_focus);
return TRUE;
}
}
return FALSE;
}
@ -1083,6 +1357,35 @@ meta_xwayland_selection_handle_xfixes_selection_notify (MetaWaylandCompositor *c
XFlush (xdisplay);
}
}
else if (selection->selection_atom == xdnd_atoms[ATOM_DND_SELECTION])
{
MetaWaylandDataDevice *data_device = &compositor->seat->data_device;
MetaXWaylandSelection *selection_data = compositor->xwayland_manager.selection_data;
selection->owner = event->owner;
if (event->owner != None && event->owner != selection->window)
{
MetaWaylandSurface *focus;
focus = compositor->seat->pointer.focus_surface;
selection->source = meta_wayland_data_source_new (&meta_x11_source_funcs,
NULL, selection);
meta_wayland_data_device_set_dnd_source (&compositor->seat->data_device,
selection->source);
meta_wayland_data_device_start_drag (data_device,
wl_resource_get_client (focus->resource),
&drag_xgrab_interface,
focus, selection->source,
NULL);
}
else if (event->owner == None)
{
meta_wayland_data_device_end_drag (data_device);
XUnmapWindow (xdisplay, selection_data->dnd.dnd_window);
}
}
return TRUE;
}
@ -1199,7 +1502,7 @@ meta_xwayland_init_selection (void)
manager->selection_data = g_slice_new0 (MetaXWaylandSelection);
meta_xwayland_init_dnd ();
meta_xwayland_init_dnd (manager);
init_selection_bridge (&manager->selection_data->clipboard,
gdk_x11_get_xatom_by_name ("CLIPBOARD"),
&compositor->seat->data_device.selection_ownership_signal);
@ -1223,6 +1526,7 @@ meta_xwayland_shutdown_selection (void)
wl_display_next_serial (compositor->wayland_display));
}
meta_xwayland_shutdown_dnd (manager);
shutdown_selection_bridge (&selection->clipboard);
shutdown_selection_bridge (&selection->dnd.selection);