diff --git a/src/meson.build b/src/meson.build index 0e5cd0aa0..787023dd3 100644 --- a/src/meson.build +++ b/src/meson.build @@ -557,8 +557,8 @@ if have_wayland 'wayland/meta-xwayland-grab-keyboard.h', 'wayland/meta-xwayland.h', 'wayland/meta-xwayland-private.h', - 'wayland/meta-xwayland-selection.c', - 'wayland/meta-xwayland-selection-private.h', + 'wayland/meta-xwayland-dnd.c', + 'wayland/meta-xwayland-dnd-private.h', ] endif diff --git a/src/wayland/meta-wayland-data-device.c b/src/wayland/meta-wayland-data-device.c index 2adc26b25..1291945de 100644 --- a/src/wayland/meta-wayland-data-device.c +++ b/src/wayland/meta-wayland-data-device.c @@ -610,8 +610,8 @@ destroy_primary_offer (struct wl_resource *resource) } static struct wl_resource * -meta_wayland_data_source_send_offer (MetaWaylandDataSource *source, - struct wl_resource *target) +create_and_send_dnd_offer (MetaWaylandDataSource *source, + struct wl_resource *target) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); @@ -872,7 +872,7 @@ meta_wayland_drag_grab_set_focus (MetaWaylandDragGrab *drag_grab, data_device_resource = wl_resource_find_for_client (&seat->data_device.resource_list, client); if (source && data_device_resource) - offer = meta_wayland_data_source_send_offer (source, data_device_resource); + offer = create_and_send_dnd_offer (source, data_device_resource); drag_grab->drag_focus = surface; drag_grab->drag_focus_destroy_handler_id = diff --git a/src/wayland/meta-wayland-private.h b/src/wayland/meta-wayland-private.h index 07a71f82b..31a7fc748 100644 --- a/src/wayland/meta-wayland-private.h +++ b/src/wayland/meta-wayland-private.h @@ -33,7 +33,7 @@ #include "wayland/meta-wayland-versions.h" #include "wayland/meta-wayland.h" -typedef struct _MetaXWaylandSelection MetaXWaylandSelection; +typedef struct _MetaXWaylandDnd MetaXWaylandDnd; typedef struct { @@ -56,7 +56,7 @@ typedef struct GSubprocess *proc; GMainLoop *init_loop; - MetaXWaylandSelection *selection_data; + MetaXWaylandDnd *dnd; } MetaXWaylandManager; struct _MetaWaylandCompositor diff --git a/src/wayland/meta-xwayland-selection-private.h b/src/wayland/meta-xwayland-dnd-private.h similarity index 100% rename from src/wayland/meta-xwayland-selection-private.h rename to src/wayland/meta-xwayland-dnd-private.h diff --git a/src/wayland/meta-xwayland-dnd.c b/src/wayland/meta-xwayland-dnd.c new file mode 100644 index 000000000..15f23fbda --- /dev/null +++ b/src/wayland/meta-xwayland-dnd.c @@ -0,0 +1,948 @@ +/* + * Copyright © 2012 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* The file is loosely based on xwayland/selection.c from Weston */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "meta/meta-x11-errors.h" +#include "wayland/meta-wayland-data-device.h" +#include "wayland/meta-xwayland-private.h" +#include "wayland/meta-xwayland-dnd-private.h" +#include "wayland/meta-xwayland.h" +#include "x11/meta-x11-display-private.h" + +#define INCR_CHUNK_SIZE (128 * 1024) +#define XDND_VERSION 5 + +struct _MetaWaylandDataSourceXWayland +{ + MetaWaylandDataSource parent; + MetaXWaylandDnd *dnd; + gboolean has_utf8_string_atom; +}; + +struct _MetaXWaylandDnd +{ + Window owner; + Time client_message_timestamp; + MetaWaylandDataSource *source; /* owned by MetaWaylandDataDevice */ + 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; +}; + +enum +{ + ATOM_DND_SELECTION, + ATOM_DND_AWARE, + ATOM_DND_STATUS, + ATOM_DND_POSITION, + ATOM_DND_ENTER, + ATOM_DND_LEAVE, + ATOM_DND_DROP, + ATOM_DND_FINISHED, + ATOM_DND_PROXY, + ATOM_DND_TYPE_LIST, + ATOM_DND_ACTION_MOVE, + ATOM_DND_ACTION_COPY, + ATOM_DND_ACTION_ASK, + ATOM_DND_ACTION_PRIVATE, + N_DND_ATOMS +}; + +/* Matches order in enum above */ +const gchar *atom_names[] = { + "XdndSelection", + "XdndAware", + "XdndStatus", + "XdndPosition", + "XdndEnter", + "XdndLeave", + "XdndDrop", + "XdndFinished", + "XdndProxy", + "XdndTypeList", + "XdndActionMove", + "XdndActionCopy", + "XdndActionAsk", + "XdndActionPrivate", + NULL +}; + +Atom xdnd_atoms[N_DND_ATOMS]; + +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 enum wl_data_device_manager_dnd_action +atom_to_action (Atom atom) +{ + if (atom == xdnd_atoms[ATOM_DND_ACTION_COPY] || + atom == xdnd_atoms[ATOM_DND_ACTION_PRIVATE]) + return WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + else if (atom == xdnd_atoms[ATOM_DND_ACTION_MOVE]) + return WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; + else if (atom == xdnd_atoms[ATOM_DND_ACTION_ASK]) + return WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK; + else + return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; +} + +static void +xdnd_send_enter (MetaXWaylandDnd *dnd, + Window dest) +{ + MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); + MetaX11Display *x11_display = meta_get_display ()->x11_display; + Display *xdisplay = x11_display->xdisplay; + MetaWaylandDataSource *data_source; + XEvent xev = { 0 }; + gchar **p; + struct wl_array *source_mime_types; + + data_source = compositor->seat->data_device.dnd_data_source; + xev.xclient.type = ClientMessage; + xev.xclient.message_type = xdnd_atoms[ATOM_DND_ENTER]; + xev.xclient.format = 32; + xev.xclient.window = dest; + + xev.xclient.data.l[0] = x11_display->selection.xwindow; + xev.xclient.data.l[1] = XDND_VERSION << 24; /* version */ + xev.xclient.data.l[2] = xev.xclient.data.l[3] = xev.xclient.data.l[4] = 0; + + source_mime_types = meta_wayland_data_source_get_mime_types (data_source); + if (source_mime_types->size <= 3) + { + /* The mimetype atoms fit in this same message */ + gint i = 2; + + wl_array_for_each (p, source_mime_types) + { + xev.xclient.data.l[i++] = gdk_x11_get_xatom_by_name (*p); + } + } + else + { + /* We have more than 3 mimetypes, we must set up + * the mimetype list as a XdndTypeList property. + */ + Atom *atomlist; + gint i = 0; + + xev.xclient.data.l[1] |= 1; + atomlist = g_new0 (Atom, source_mime_types->size); + + wl_array_for_each (p, source_mime_types) + { + atomlist[i++] = gdk_x11_get_xatom_by_name (*p); + } + + XChangeProperty (xdisplay, x11_display->selection.xwindow, + xdnd_atoms[ATOM_DND_TYPE_LIST], + XA_ATOM, 32, PropModeReplace, + (guchar *) atomlist, i); + } + + XSendEvent (xdisplay, dest, False, NoEventMask, &xev); +} + +static void +xdnd_send_leave (MetaXWaylandDnd *dnd, + Window dest) +{ + MetaX11Display *x11_display = meta_get_display ()->x11_display; + Display *xdisplay = x11_display->xdisplay; + XEvent xev = { 0 }; + + xev.xclient.type = ClientMessage; + xev.xclient.message_type = xdnd_atoms[ATOM_DND_LEAVE]; + xev.xclient.format = 32; + xev.xclient.window = dest; + xev.xclient.data.l[0] = x11_display->selection.xwindow; + + XSendEvent (xdisplay, dest, False, NoEventMask, &xev); +} + +static void +xdnd_send_position (MetaXWaylandDnd *dnd, + Window dest, + uint32_t time, + int x, + int y) +{ + MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); + MetaWaylandDataSource *source = compositor->seat->data_device.dnd_data_source; + MetaX11Display *x11_display = meta_get_display ()->x11_display; + Display *xdisplay = x11_display->xdisplay; + 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; + xev.xclient.window = dest; + + xev.xclient.data.l[0] = x11_display->selection.xwindow; + 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] = action_to_atom (action); + + XSendEvent (xdisplay, dest, False, NoEventMask, &xev); +} + +static void +xdnd_send_drop (MetaXWaylandDnd *dnd, + Window dest, + uint32_t time) +{ + MetaX11Display *x11_display = meta_get_display ()->x11_display; + Display *xdisplay = x11_display->xdisplay; + XEvent xev = { 0 }; + + xev.xclient.type = ClientMessage; + xev.xclient.message_type = xdnd_atoms[ATOM_DND_DROP]; + xev.xclient.format = 32; + xev.xclient.window = dest; + + xev.xclient.data.l[0] = x11_display->selection.xwindow; + xev.xclient.data.l[2] = time; + + XSendEvent (xdisplay, dest, False, NoEventMask, &xev); +} + +static void +xdnd_send_finished (MetaXWaylandDnd *dnd, + Window dest, + gboolean accepted) +{ + MetaX11Display *x11_display = meta_get_display ()->x11_display; + Display *xdisplay = x11_display->xdisplay; + MetaWaylandDataSource *source = dnd->source; + uint32_t action = 0; + 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] = dnd->dnd_window; + + if (accepted) + { + action = meta_wayland_data_source_get_current_action (source); + xev.xclient.data.l[1] = 1; /* Drop successful */ + xev.xclient.data.l[2] = action_to_atom (action); + } + + XSendEvent (xdisplay, dest, False, NoEventMask, &xev); +} + +static void +xdnd_send_status (MetaXWaylandDnd *dnd, + Window dest, + uint32_t action) +{ + 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] = dnd->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 (xev.xclient.data.l[4]) + xev.xclient.data.l[1] |= 1 << 0; /* Bit 1: dest accepts the drop */ + + XSendEvent (xdisplay, dest, False, NoEventMask, &xev); +} + +static void +meta_xwayland_end_dnd_grab (MetaWaylandDataDevice *data_device) +{ + Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); + MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); + MetaXWaylandManager *manager = &compositor->xwayland_manager; + MetaXWaylandDnd *dnd = manager->dnd; + + meta_wayland_data_device_end_drag (data_device); + + XMoveResizeWindow (xdisplay, dnd->dnd_window, -1, -1, 1, 1); + XUnmapWindow (xdisplay, dnd->dnd_window); +} + +static void +transfer_cb (MetaSelection *selection, + GAsyncResult *res, + GOutputStream *stream) +{ + GError *error = NULL; + + if (!meta_selection_transfer_finish (selection, res, &error)) + { + g_warning ("Could not transfer DnD selection: %s\n", error->message); + g_error_free (error); + } + + g_output_stream_close (stream, NULL, NULL); +} + +static void +meta_x11_source_send (MetaWaylandDataSource *source, + const gchar *mime_type, + gint fd) +{ + MetaDisplay *display = meta_get_display (); + GOutputStream *stream; + + stream = g_unix_output_stream_new (fd, TRUE); + meta_selection_transfer_async (meta_display_get_selection (display), + META_SELECTION_DND, + mime_type, + -1, + stream, + NULL, + (GAsyncReadyCallback) transfer_cb, + stream); +} + +static void +meta_x11_source_target (MetaWaylandDataSource *source, + const gchar *mime_type) +{ + MetaWaylandDataSourceXWayland *source_xwayland = + META_WAYLAND_DATA_SOURCE_XWAYLAND (source); + MetaXWaylandDnd *dnd = source_xwayland->dnd; + uint32_t action = 0; + + if (mime_type) + action = meta_wayland_data_source_get_current_action (source); + + xdnd_send_status (dnd, dnd->owner, action); +} + +static void +meta_x11_source_cancel (MetaWaylandDataSource *source) +{ + MetaWaylandDataSourceXWayland *source_xwayland = + META_WAYLAND_DATA_SOURCE_XWAYLAND (source); + MetaXWaylandDnd *dnd = source_xwayland->dnd; + + xdnd_send_finished (dnd, dnd->owner, FALSE); +} + +static void +meta_x11_source_action (MetaWaylandDataSource *source, + uint32_t action) +{ + MetaWaylandDataSourceXWayland *source_xwayland = + META_WAYLAND_DATA_SOURCE_XWAYLAND (source); + MetaXWaylandDnd *dnd = source_xwayland->dnd; + + if (!meta_wayland_data_source_has_target (source)) + action = 0; + + xdnd_send_status (dnd, dnd->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); + MetaXWaylandDnd *dnd = source_xwayland->dnd; + MetaX11Display *x11_display = meta_get_display ()->x11_display; + uint32_t action = meta_wayland_data_source_get_current_action (source); + + if (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, + xdnd_atoms[ATOM_DND_SELECTION], + gdk_x11_get_xatom_by_name ("DELETE"), + gdk_x11_get_xatom_by_name ("_META_SELECTION"), + x11_display->selection.xwindow, + META_CURRENT_TIME); + } + + xdnd_send_finished (dnd, dnd->owner, TRUE); +} + +static void +meta_wayland_data_source_xwayland_init (MetaWaylandDataSourceXWayland *source_xwayland) +{ +} + +static void +meta_wayland_data_source_xwayland_class_init (MetaWaylandDataSourceXWaylandClass *klass) +{ + MetaWaylandDataSourceClass *data_source_class = + META_WAYLAND_DATA_SOURCE_CLASS (klass); + + 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 * +meta_wayland_data_source_xwayland_new (MetaXWaylandDnd *dnd) +{ + MetaWaylandDataSourceXWayland *source_xwayland; + + source_xwayland = g_object_new (META_TYPE_WAYLAND_DATA_SOURCE_XWAYLAND, NULL); + source_xwayland->dnd = dnd; + + return META_WAYLAND_DATA_SOURCE (source_xwayland); +} + +static void +meta_x11_drag_dest_focus_in (MetaWaylandDataDevice *data_device, + MetaWaylandSurface *surface, + MetaWaylandDataOffer *offer) +{ + MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); + MetaXWaylandDnd *dnd = compositor->xwayland_manager.dnd; + + dnd->dnd_dest = surface->window->xwindow; + xdnd_send_enter (dnd, dnd->dnd_dest); +} + +static void +meta_x11_drag_dest_focus_out (MetaWaylandDataDevice *data_device, + MetaWaylandSurface *surface) +{ + MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); + MetaXWaylandDnd *dnd = compositor->xwayland_manager.dnd; + + xdnd_send_leave (dnd, dnd->dnd_dest); + dnd->dnd_dest = None; +} + +static void +meta_x11_drag_dest_motion (MetaWaylandDataDevice *data_device, + MetaWaylandSurface *surface, + const ClutterEvent *event) +{ + MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); + MetaXWaylandDnd *dnd = compositor->xwayland_manager.dnd; + guint32 time; + gfloat x, y; + + time = clutter_event_get_time (event); + clutter_event_get_coords (event, &x, &y); + xdnd_send_position (dnd, dnd->dnd_dest, time, x, y); +} + +static void +meta_x11_drag_dest_drop (MetaWaylandDataDevice *data_device, + MetaWaylandSurface *surface) +{ + MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); + MetaXWaylandDnd *dnd = compositor->xwayland_manager.dnd; + + xdnd_send_drop (dnd, dnd->dnd_dest, + 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 (); + MetaXWaylandDnd *dnd = compositor->xwayland_manager.dnd; + MetaWaylandSeat *seat = compositor->seat; + ClutterPoint pos; + + clutter_input_device_get_coords (seat->pointer->device, NULL, &pos); + xdnd_send_position (dnd, 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_update +}; + +const MetaWaylandDragDestFuncs * +meta_xwayland_selection_get_drag_dest_funcs (void) +{ + return &meta_x11_drag_dest_funcs; +} + +static gboolean +meta_xwayland_data_source_fetch_mimetype_list (MetaWaylandDataSource *source, + Window window, + Atom prop) +{ + MetaWaylandDataSourceXWayland *source_xwayland = + META_WAYLAND_DATA_SOURCE_XWAYLAND (source); + Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); + gulong nitems_ret, bytes_after_ret, i; + Atom *atoms, type_ret, utf8_string; + int format_ret; + struct wl_array *source_mime_types; + + source_mime_types = meta_wayland_data_source_get_mime_types (source); + if (source_mime_types->size != 0) + return TRUE; + + utf8_string = gdk_x11_get_xatom_by_name ("UTF8_STRING"); + XGetWindowProperty (xdisplay, window, prop, + 0, /* offset */ + 0x1fffffff, /* length */ + True, /* delete */ + AnyPropertyType, + &type_ret, + &format_ret, + &nitems_ret, + &bytes_after_ret, + (guchar **) &atoms); + + if (nitems_ret == 0 || type_ret != XA_ATOM) + { + XFree (atoms); + return FALSE; + } + + for (i = 0; i < nitems_ret; i++) + { + const gchar *mime_type; + + if (atoms[i] == utf8_string) + { + meta_wayland_data_source_add_mime_type (source, + "text/plain;charset=utf-8"); + source_xwayland->has_utf8_string_atom = TRUE; + } + + mime_type = gdk_x11_get_xatom_name (atoms[i]); + meta_wayland_data_source_add_mime_type (source, mime_type); + } + + XFree (atoms); + + 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->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 ()); + MetaXWaylandDnd *dnd = compositor->xwayland_manager.dnd; + MetaWaylandSurface *focus = NULL; + + focus = pick_drop_surface (compositor, event); + if (dnd->focus_surface == focus) + return; + + dnd->focus_surface = focus; + + if (focus && + focus->window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND) + { + XMapRaised (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 (); + MetaXWaylandDnd *dnd = compositor->xwayland_manager.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; + MetaWaylandDataSource *data_source; + + meta_wayland_pointer_send_button (seat->pointer, event); + data_source = compositor->seat->data_device.dnd_data_source; + + if (seat->pointer->button_count == 0 && + (!meta_wayland_drag_grab_get_focus ((MetaWaylandDragGrab *) grab) || + meta_wayland_data_source_get_current_action (data_source) == + WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE)) + meta_xwayland_end_dnd_grab (&seat->data_device); +} + +static const MetaWaylandPointerGrabInterface drag_xgrab_interface = { + drag_xgrab_focus, + drag_xgrab_motion, + drag_xgrab_button, +}; + +static gboolean +meta_xwayland_dnd_handle_client_message (MetaWaylandCompositor *compositor, + XEvent *xevent) +{ + XClientMessageEvent *event = (XClientMessageEvent *) xevent; + MetaXWaylandDnd *dnd = compositor->xwayland_manager.dnd; + MetaWaylandSeat *seat = compositor->seat; + MetaX11Display *x11_display = meta_get_display ()->x11_display; + + /* Source side messages */ + if (event->window == x11_display->selection.xwindow) + { + MetaWaylandDataSource *data_source; + uint32_t action = 0; + + data_source = compositor->seat->data_device.dnd_data_source; + + if (!data_source) + return FALSE; + + if (event->message_type == xdnd_atoms[ATOM_DND_STATUS]) + { + /* The first bit in data.l[1] is set if the drag was accepted */ + 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]) + action = atom_to_action ((Atom) event->data.l[4]); + + meta_wayland_data_source_set_current_action (data_source, action); + return TRUE; + } + else if (event->message_type == xdnd_atoms[ATOM_DND_FINISHED]) + { + /* Reject messages mid-grab */ + if (compositor->seat->data_device.current_grab) + return FALSE; + + meta_wayland_data_source_notify_finish (data_source); + return TRUE; + } + } + /* Dest side messages */ + else if (dnd->source && + compositor->seat->data_device.current_grab && + (Window) event->data.l[0] == dnd->owner) + { + MetaWaylandDragGrab *drag_grab = compositor->seat->data_device.current_grab; + MetaWaylandSurface *drag_focus = meta_wayland_drag_grab_get_focus (drag_grab); + + if (!drag_focus && + event->message_type != xdnd_atoms[ATOM_DND_ENTER]) + return FALSE; + + 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; + struct wl_array *source_mime_types; + + /* We only need to fetch once */ + source_mime_types = + meta_wayland_data_source_get_mime_types (dnd->source); + if (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->source, + mimetype); + } + } + } + else + { + /* Fetch mimetypes from type list */ + meta_xwayland_data_source_fetch_mimetype_list (dnd->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; + uint32_t action = 0; + + dnd->client_message_timestamp = event->data.l[3]; + + 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); + + action = atom_to_action ((Atom) event->data.l[4]); + meta_wayland_data_source_set_actions (dnd->source, action); + + meta_wayland_surface_drag_dest_motion (drag_focus, motion); + xdnd_send_status (dnd, (Window) event->data.l[0], + meta_wayland_data_source_get_current_action (dnd->source)); + + 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]) + { + dnd->client_message_timestamp = event->data.l[2]; + meta_wayland_surface_drag_dest_drop (drag_focus); + meta_xwayland_end_dnd_grab (&seat->data_device); + return TRUE; + } + } + + return FALSE; +} + +static gboolean +meta_xwayland_dnd_handle_xfixes_selection_notify (MetaWaylandCompositor *compositor, + XEvent *xevent) +{ + XFixesSelectionNotifyEvent *event = (XFixesSelectionNotifyEvent *) xevent; + MetaXWaylandDnd *dnd = compositor->xwayland_manager.dnd; + MetaWaylandDataDevice *data_device = &compositor->seat->data_device; + MetaX11Display *x11_display = meta_get_display ()->x11_display; + MetaWaylandSurface *focus; + + if (event->selection != xdnd_atoms[ATOM_DND_SELECTION]) + return FALSE; + + dnd->owner = event->owner; + focus = compositor->seat->pointer->focus_surface; + + if (event->owner != None && event->owner != x11_display->selection.xwindow && + focus && meta_xwayland_is_xwayland_surface (focus)) + { + dnd->source = meta_wayland_data_source_xwayland_new (dnd); + meta_wayland_data_device_set_dnd_source (&compositor->seat->data_device, + dnd->source); + + meta_wayland_data_device_start_drag (data_device, + wl_resource_get_client (focus->resource), + &drag_xgrab_interface, + focus, dnd->source, + NULL); + } + else if (event->owner == None) + { + meta_xwayland_end_dnd_grab (data_device); + } + + return FALSE; +} + +gboolean +meta_xwayland_dnd_handle_event (XEvent *xevent) +{ + MetaWaylandCompositor *compositor; + + compositor = meta_wayland_compositor_get_default (); + + if (!compositor->xwayland_manager.dnd) + return FALSE; + + switch (xevent->type) + { + case ClientMessage: + return meta_xwayland_dnd_handle_client_message (compositor, xevent); + default: + { + MetaX11Display *x11_display = meta_get_display ()->x11_display; + + if (xevent->type - x11_display->xfixes_event_base == XFixesSelectionNotify) + return meta_xwayland_dnd_handle_xfixes_selection_notify (compositor, xevent); + + return FALSE; + } + } +} + +void +meta_xwayland_init_dnd (void) +{ + MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); + MetaXWaylandManager *manager = &compositor->xwayland_manager; + Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); + MetaXWaylandDnd *dnd = manager->dnd; + XSetWindowAttributes attributes; + guint32 i, version = XDND_VERSION; + + g_assert (manager->dnd == NULL); + + manager->dnd = dnd = g_slice_new0 (MetaXWaylandDnd); + + 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); +} + +void +meta_xwayland_shutdown_dnd (void) +{ + MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); + MetaXWaylandManager *manager = &compositor->xwayland_manager; + MetaXWaylandDnd *dnd = manager->dnd; + + g_assert (dnd != NULL); + + XDestroyWindow (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), + dnd->dnd_window); + dnd->dnd_window = None; + + g_slice_free (MetaXWaylandDnd, dnd); + manager->dnd = NULL; +} diff --git a/src/wayland/meta-xwayland-private.h b/src/wayland/meta-xwayland-private.h index 38874eda3..9a109da66 100644 --- a/src/wayland/meta-xwayland-private.h +++ b/src/wayland/meta-xwayland-private.h @@ -35,9 +35,9 @@ void meta_xwayland_stop (MetaXWaylandManager *manager); /* wl_data_device/X11 selection interoperation */ -void meta_xwayland_init_selection (void); -void meta_xwayland_shutdown_selection (void); -gboolean meta_xwayland_selection_handle_event (XEvent *xevent); +void meta_xwayland_init_dnd (void); +void meta_xwayland_shutdown_dnd (void); +gboolean meta_xwayland_dnd_handle_event (XEvent *xevent); const MetaWaylandDragDestFuncs * meta_xwayland_selection_get_drag_dest_funcs (void); diff --git a/src/wayland/meta-xwayland-selection.c b/src/wayland/meta-xwayland-selection.c deleted file mode 100644 index 808f91333..000000000 --- a/src/wayland/meta-xwayland-selection.c +++ /dev/null @@ -1,1797 +0,0 @@ -/* - * Copyright © 2012 Intel Corporation - * - * Permission to use, copy, modify, distribute, and sell this software and - * its documentation for any purpose is hereby granted without fee, provided - * that the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation, and that the name of the copyright holders not be used in - * advertising or publicity pertaining to distribution of the software - * without specific, written prior permission. The copyright holders make - * no representations about the suitability of this software for any - * purpose. It is provided "as is" without express or implied warranty. - * - * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS - * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER - * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF - * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -/* The file is loosely based on xwayland/selection.c from Weston */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "meta/meta-x11-errors.h" -#include "wayland/meta-wayland-data-device.h" -#include "wayland/meta-xwayland-private.h" -#include "wayland/meta-xwayland-selection-private.h" -#include "wayland/meta-xwayland.h" -#include "x11/meta-x11-display-private.h" - -#define INCR_CHUNK_SIZE (128 * 1024) -#define XDND_VERSION 5 - -typedef struct { - MetaXWaylandSelection *selection_data; - GInputStream *stream; - GCancellable *cancellable; - MetaWindow *window; - XSelectionRequestEvent request_event; - guchar buffer[INCR_CHUNK_SIZE]; - gsize buffer_len; - guint incr : 1; -} WaylandSelectionData; - -typedef struct { - MetaXWaylandSelection *selection_data; - GOutputStream *stream; - GCancellable *cancellable; - gchar *mime_type; - guint incr : 1; -} X11SelectionData; - -typedef struct { - Atom selection_atom; - Window window; - Window owner; - Time timestamp; - Time client_message_timestamp; - MetaWaylandDataSource *source; /* owned by MetaWaylandDataDevice */ - WaylandSelectionData *wayland_selection; - X11SelectionData *x11_selection; - - struct wl_listener ownership_listener; -} MetaSelectionBridge; - -typedef struct { - MetaSelectionBridge selection; - 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 _MetaWaylandDataSourceXWayland -{ - MetaWaylandDataSource parent; - - MetaSelectionBridge *selection; - guint has_utf8_string_atom : 1; -}; - -struct _MetaXWaylandSelection { - MetaSelectionBridge clipboard; - MetaSelectionBridge primary; - MetaDndBridge dnd; -}; - -enum -{ - ATOM_DND_SELECTION, - ATOM_DND_AWARE, - ATOM_DND_STATUS, - ATOM_DND_POSITION, - ATOM_DND_ENTER, - ATOM_DND_LEAVE, - ATOM_DND_DROP, - ATOM_DND_FINISHED, - ATOM_DND_PROXY, - ATOM_DND_TYPE_LIST, - ATOM_DND_ACTION_MOVE, - ATOM_DND_ACTION_COPY, - ATOM_DND_ACTION_ASK, - ATOM_DND_ACTION_PRIVATE, - N_DND_ATOMS -}; - -/* Matches order in enum above */ -const gchar *atom_names[] = { - "XdndSelection", - "XdndAware", - "XdndStatus", - "XdndPosition", - "XdndEnter", - "XdndLeave", - "XdndDrop", - "XdndFinished", - "XdndProxy", - "XdndTypeList", - "XdndActionMove", - "XdndActionCopy", - "XdndActionAsk", - "XdndActionPrivate", - NULL -}; - -Atom xdnd_atoms[N_DND_ATOMS]; - -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 enum wl_data_device_manager_dnd_action -atom_to_action (Atom atom) -{ - if (atom == xdnd_atoms[ATOM_DND_ACTION_COPY] || - atom == xdnd_atoms[ATOM_DND_ACTION_PRIVATE]) - return WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; - else if (atom == xdnd_atoms[ATOM_DND_ACTION_MOVE]) - return WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; - else if (atom == xdnd_atoms[ATOM_DND_ACTION_ASK]) - return WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK; - else - return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; -} - -static void -xdnd_send_enter (MetaXWaylandSelection *selection_data, - Window dest) -{ - MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); - MetaSelectionBridge *selection = &selection_data->dnd.selection; - Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); - MetaWaylandDataSource *data_source; - XEvent xev = { 0 }; - gchar **p; - struct wl_array *source_mime_types; - - data_source = compositor->seat->data_device.dnd_data_source; - xev.xclient.type = ClientMessage; - xev.xclient.message_type = xdnd_atoms[ATOM_DND_ENTER]; - xev.xclient.format = 32; - xev.xclient.window = dest; - - xev.xclient.data.l[0] = selection->window; - xev.xclient.data.l[1] = XDND_VERSION << 24; /* version */ - xev.xclient.data.l[2] = xev.xclient.data.l[3] = xev.xclient.data.l[4] = 0; - - source_mime_types = meta_wayland_data_source_get_mime_types (data_source); - if (source_mime_types->size <= 3) - { - /* The mimetype atoms fit in this same message */ - gint i = 2; - - wl_array_for_each (p, source_mime_types) - { - xev.xclient.data.l[i++] = gdk_x11_get_xatom_by_name (*p); - } - } - else - { - /* We have more than 3 mimetypes, we must set up - * the mimetype list as a XdndTypeList property. - */ - Atom *atomlist; - gint i = 0; - - xev.xclient.data.l[1] |= 1; - atomlist = g_new0 (Atom, source_mime_types->size); - - wl_array_for_each (p, source_mime_types) - { - atomlist[i++] = gdk_x11_get_xatom_by_name (*p); - } - - XChangeProperty (xdisplay, selection->window, - xdnd_atoms[ATOM_DND_TYPE_LIST], - XA_ATOM, 32, PropModeReplace, - (guchar *) atomlist, i); - } - - XSendEvent (xdisplay, dest, False, NoEventMask, &xev); -} - -static void -xdnd_send_leave (MetaXWaylandSelection *selection_data, - Window dest) -{ - MetaSelectionBridge *selection = &selection_data->dnd.selection; - Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); - XEvent xev = { 0 }; - - xev.xclient.type = ClientMessage; - xev.xclient.message_type = xdnd_atoms[ATOM_DND_LEAVE]; - xev.xclient.format = 32; - xev.xclient.window = dest; - xev.xclient.data.l[0] = selection->window; - - XSendEvent (xdisplay, dest, False, NoEventMask, &xev); -} - -static void -xdnd_send_position (MetaXWaylandSelection *selection_data, - Window dest, - uint32_t time, - 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; - xev.xclient.window = dest; - - xev.xclient.data.l[0] = selection->window; - 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] = action_to_atom (action); - - XSendEvent (xdisplay, dest, False, NoEventMask, &xev); -} - -static void -xdnd_send_drop (MetaXWaylandSelection *selection_data, - Window dest, - uint32_t time) -{ - MetaSelectionBridge *selection = &selection_data->dnd.selection; - Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); - XEvent xev = { 0 }; - - xev.xclient.type = ClientMessage; - xev.xclient.message_type = xdnd_atoms[ATOM_DND_DROP]; - xev.xclient.format = 32; - xev.xclient.window = dest; - - xev.xclient.data.l[0] = selection->window; - xev.xclient.data.l[2] = time; - - XSendEvent (xdisplay, dest, False, NoEventMask, &xev); -} - -static void -xdnd_send_finished (MetaXWaylandSelection *selection_data, - Window dest, - gboolean accepted) -{ - 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; - 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) - { - action = meta_wayland_data_source_get_current_action (source); - xev.xclient.data.l[1] = 1; /* Drop successful */ - xev.xclient.data.l[2] = action_to_atom (action); - } - - XSendEvent (xdisplay, dest, False, NoEventMask, &xev); -} - -static void -xdnd_send_status (MetaXWaylandSelection *selection_data, - Window dest, - uint32_t action) -{ - 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 */ - xev.xclient.data.l[4] = action_to_atom (action); - - 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); -} - -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; -} - -static void -meta_xwayland_end_dnd_grab (MetaWaylandDataDevice *data_device) -{ - Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); - MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); - MetaXWaylandManager *manager = &compositor->xwayland_manager; - MetaDndBridge *dnd = &manager->selection_data->dnd; - - meta_wayland_data_device_end_drag (data_device); - - XMoveResizeWindow (xdisplay, dnd->dnd_window, -1, -1, 1, 1); - XUnmapWindow (xdisplay, dnd->dnd_window); -} - -/* X11/Wayland data bridges */ - -static MetaSelectionBridge * -atom_to_selection_bridge (MetaWaylandCompositor *compositor, - Atom selection_atom) -{ - MetaXWaylandSelection *selection_data = compositor->xwayland_manager.selection_data; - - if (selection_atom == selection_data->clipboard.selection_atom) - return &selection_data->clipboard; - else if (selection_atom == selection_data->primary.selection_atom) - return &selection_data->primary; - else if (selection_atom == selection_data->dnd.selection.selection_atom) - return &selection_data->dnd.selection; - else - return NULL; -} - -static X11SelectionData * -x11_selection_data_new (MetaXWaylandSelection *selection_data, - int fd, - const char *mime_type) -{ - X11SelectionData *data; - - data = g_slice_new0 (X11SelectionData); - data->selection_data = selection_data; - data->stream = g_unix_output_stream_new (fd, TRUE); - data->cancellable = g_cancellable_new (); - data->mime_type = g_strdup (mime_type); - - return data; -} - -static void -x11_selection_data_free (X11SelectionData *data) -{ - g_cancellable_cancel (data->cancellable); - g_object_unref (data->cancellable); - g_object_unref (data->stream); - g_free (data->mime_type); - 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, - META_CURRENT_TIME); - } - - xdnd_send_finished (selection->x11_selection->selection_data, - selection->owner, success); -} - -static void -x11_selection_data_finish (MetaSelectionBridge *selection, - gboolean success) -{ - if (!selection->x11_selection) - return; - - if (selection == &selection->x11_selection->selection_data->dnd.selection) - x11_selection_data_send_finished (selection, success); - - g_clear_pointer (&selection->x11_selection, - 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) -{ - MetaSelectionBridge *selection = user_data; - X11SelectionData *data = selection->x11_selection; - GError *error = NULL; - gboolean success = TRUE; - - g_output_stream_write_finish (G_OUTPUT_STREAM (object), res, &error); - - if (error) - { - if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - { - g_error_free (error); - return; - } - - g_warning ("Error writing from X11 selection: %s\n", error->message); - g_error_free (error); - success = FALSE; - } - - if (success && data->incr) - { - Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); - XDeleteProperty (xdisplay, selection->window, - gdk_x11_get_xatom_by_name ("_META_SELECTION")); - } - else - { - x11_selection_data_close (selection->x11_selection); - x11_selection_data_finish (selection, success); - } -} - -static void -x11_selection_data_write (MetaSelectionBridge *selection, - guchar *buffer, - gulong len) -{ - X11SelectionData *data = selection->x11_selection; - - g_output_stream_write_async (data->stream, buffer, len, - G_PRIORITY_DEFAULT, data->cancellable, - x11_data_write_cb, selection); -} - -static MetaWaylandDataSource * -data_device_get_active_source_for_atom (MetaWaylandDataDevice *data_device, - Atom selection_atom) -{ - if (selection_atom == gdk_x11_get_xatom_by_name ("CLIPBOARD")) - return data_device->selection_data_source; - else if (selection_atom == gdk_x11_get_xatom_by_name ("PRIMARY")) - return data_device->primary_data_source; - else if (selection_atom == xdnd_atoms[ATOM_DND_SELECTION]) - return data_device->dnd_data_source; - else - return NULL; -} - -static WaylandSelectionData * -wayland_selection_data_new (XSelectionRequestEvent *request_event, - MetaWaylandCompositor *compositor) -{ - MetaDisplay *display = meta_get_display (); - MetaX11Display *x11_display = display->x11_display; - MetaWaylandDataDevice *data_device; - MetaWaylandDataSource *wayland_source; - MetaSelectionBridge *selection; - WaylandSelectionData *data; - const gchar *mime_type; - GError *error = NULL; - int p[2]; - - selection = atom_to_selection_bridge (compositor, request_event->selection); - - if (!selection) - return NULL; - - if (!g_unix_open_pipe (p, FD_CLOEXEC, &error)) - { - g_critical ("Failed to open pipe: %s\n", error->message); - g_error_free (error); - return NULL; - } - - data_device = &compositor->seat->data_device; - mime_type = gdk_x11_get_xatom_name (request_event->target); - - if (!g_unix_set_fd_nonblocking (p[0], TRUE, &error) || - !g_unix_set_fd_nonblocking (p[1], TRUE, &error)) - { - if (error) - { - g_critical ("Failed to make fds non-blocking: %s\n", error->message); - g_error_free (error); - } - - close (p[0]); - close (p[1]); - return NULL; - } - - wayland_source = data_device_get_active_source_for_atom (data_device, - selection->selection_atom), - meta_wayland_data_source_send (wayland_source, mime_type, p[1]); - - data = g_slice_new0 (WaylandSelectionData); - data->request_event = *request_event; - data->cancellable = g_cancellable_new (); - data->stream = g_unix_input_stream_new (p[0], TRUE); - - data->window = meta_x11_display_lookup_x_window (x11_display, - data->request_event.requestor); - - /* Do *not* change the event mask on the root window, bugger! */ - if (!data->window && data->request_event.requestor != x11_display->xroot) - { - /* Not a managed window, set the PropertyChangeMask - * for INCR deletion notifications. - */ - XSelectInput (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), - data->request_event.requestor, PropertyChangeMask); - } - - return data; -} - -static void -reply_selection_request (XSelectionRequestEvent *request_event, - gboolean accepted) -{ - Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); - XSelectionEvent event; - - memset(&event, 0, sizeof (XSelectionEvent)); - event.type = SelectionNotify; - event.time = request_event->time; - event.requestor = request_event->requestor; - event.selection = request_event->selection; - event.target = request_event->target; - event.property = accepted ? request_event->property : None; - - XSendEvent (xdisplay, request_event->requestor, - False, NoEventMask, (XEvent *) &event); -} - -static void -wayland_selection_data_free (WaylandSelectionData *data) -{ - MetaDisplay *display = meta_get_display (); - MetaX11Display *x11_display = display->x11_display; - - /* Do *not* change the event mask on the root window, bugger! */ - if (!data->window && data->request_event.requestor != x11_display->xroot) - { - meta_x11_error_trap_push (x11_display); - XSelectInput (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), - data->request_event.requestor, NoEventMask); - meta_x11_error_trap_pop (x11_display); - } - - g_cancellable_cancel (data->cancellable); - g_object_unref (data->cancellable); - g_object_unref (data->stream); - g_slice_free (WaylandSelectionData, data); -} - -static void -wayland_selection_update_x11_property (WaylandSelectionData *data) -{ - Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); - - XChangeProperty (xdisplay, - data->request_event.requestor, - data->request_event.property, - data->request_event.target, - 8, PropModeReplace, - data->buffer, data->buffer_len); - data->buffer_len = 0; -} - -static void -wayland_data_read_cb (GObject *object, - GAsyncResult *res, - gpointer user_data) -{ - MetaSelectionBridge *selection = user_data; - WaylandSelectionData *data = selection->wayland_selection; - GError *error = NULL; - gsize bytes_read; - - if (!g_input_stream_read_all_finish (G_INPUT_STREAM (object), - res, &bytes_read, &error)) - { - if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - { - g_error_free (error); - return; - } - - g_warning ("Error transferring wayland clipboard to X11: %s\n", - error->message); - g_error_free (error); - - if (data && data->stream == G_INPUT_STREAM (object)) - { - reply_selection_request (&data->request_event, FALSE); - g_clear_pointer (&selection->wayland_selection, - wayland_selection_data_free); - } - - return; - } - - data->buffer_len = bytes_read; - - if (bytes_read == INCR_CHUNK_SIZE) - { - if (!data->incr) - { - Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); - guint32 incr_chunk_size = INCR_CHUNK_SIZE; - - /* Not yet in incr */ - data->incr = TRUE; - XChangeProperty (xdisplay, - data->request_event.requestor, - data->request_event.property, - gdk_x11_get_xatom_by_name ("INCR"), - 32, PropModeReplace, - (guchar *) &incr_chunk_size, 1); - reply_selection_request (&data->request_event, TRUE); - } - else - wayland_selection_update_x11_property (data); - } - else - { - if (!data->incr) - { - /* Non-incr transfer finished */ - wayland_selection_update_x11_property (data); - reply_selection_request (&data->request_event, TRUE); - } - else if (data->incr) - { - /* Incr transfer complete, setting a new property */ - wayland_selection_update_x11_property (data); - - if (bytes_read > 0) - return; - } - - g_clear_pointer (&selection->wayland_selection, - wayland_selection_data_free); - } -} - -static void -wayland_selection_data_read (MetaSelectionBridge *selection) -{ - WaylandSelectionData *data = selection->wayland_selection; - - g_input_stream_read_all_async (data->stream, data->buffer, - INCR_CHUNK_SIZE, G_PRIORITY_DEFAULT, - data->cancellable, - wayland_data_read_cb, selection); -} - -static void -meta_xwayland_selection_get_incr_chunk (MetaWaylandCompositor *compositor, - MetaSelectionBridge *selection) -{ - Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); - gulong nitems_ret, bytes_after_ret; - guchar *prop_ret; - int format_ret; - Atom type_ret; - - XGetWindowProperty (xdisplay, - selection->window, - gdk_x11_get_xatom_by_name ("_META_SELECTION"), - 0, /* offset */ - 0x1fffffff, /* length */ - False, /* delete */ - AnyPropertyType, - &type_ret, - &format_ret, - &nitems_ret, - &bytes_after_ret, - &prop_ret); - - if (nitems_ret > 0) - { - x11_selection_data_write (selection, prop_ret, nitems_ret); - } - else - { - /* Transfer has completed */ - x11_selection_data_close (selection->x11_selection); - x11_selection_data_finish (selection, TRUE); - } - - XFree (prop_ret); -} - -static void -meta_x11_source_send (MetaWaylandDataSource *source, - const gchar *mime_type, - gint fd) -{ - MetaWaylandDataSourceXWayland *source_xwayland = - META_WAYLAND_DATA_SOURCE_XWAYLAND (source); - MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); - Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); - MetaSelectionBridge *selection = source_xwayland->selection; - Atom type_atom; - - if (source_xwayland->has_utf8_string_atom && - strcmp (mime_type, "text/plain;charset=utf-8") == 0) - type_atom = gdk_x11_get_xatom_by_name ("UTF8_STRING"); - else - type_atom = gdk_x11_get_xatom_by_name (mime_type); - - /* Ensure we close previous transactions */ - x11_selection_data_finish (selection, FALSE); - - /* Takes ownership of fd */ - selection->x11_selection = - x11_selection_data_new (compositor->xwayland_manager.selection_data, - fd, gdk_x11_get_xatom_name (type_atom)); - - XConvertSelection (xdisplay, - selection->selection_atom, type_atom, - gdk_x11_get_xatom_by_name ("_META_SELECTION"), - selection->window, - selection->client_message_timestamp); - XFlush (xdisplay); -} - -static void -meta_x11_source_target (MetaWaylandDataSource *source, - const gchar *mime_type) -{ - MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); - 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, action); - } -} - -static void -meta_x11_source_cancel (MetaWaylandDataSource *source) -{ - MetaWaylandDataSourceXWayland *source_xwayland = - META_WAYLAND_DATA_SOURCE_XWAYLAND (source); - MetaSelectionBridge *selection = source_xwayland->selection; - - x11_selection_data_send_finished (selection, FALSE); - g_clear_pointer (&selection->x11_selection, - 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) -{ -} - -static void -meta_wayland_data_source_xwayland_class_init (MetaWaylandDataSourceXWaylandClass *klass) -{ - MetaWaylandDataSourceClass *data_source_class = - META_WAYLAND_DATA_SOURCE_CLASS (klass); - - 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 * -meta_wayland_data_source_xwayland_new (MetaSelectionBridge *selection) -{ - MetaWaylandDataSourceXWayland *source_xwayland; - - source_xwayland = g_object_new (META_TYPE_WAYLAND_DATA_SOURCE_XWAYLAND, NULL); - source_xwayland->selection = selection; - - return META_WAYLAND_DATA_SOURCE (source_xwayland); -} - -static void -meta_x11_drag_dest_focus_in (MetaWaylandDataDevice *data_device, - MetaWaylandSurface *surface, - MetaWaylandDataOffer *offer) -{ - MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); - - compositor->xwayland_manager.selection_data->dnd.dnd_dest = surface->window->xwindow; - xdnd_send_enter (compositor->xwayland_manager.selection_data, - compositor->xwayland_manager.selection_data->dnd.dnd_dest); -} - -static void -meta_x11_drag_dest_focus_out (MetaWaylandDataDevice *data_device, - MetaWaylandSurface *surface) -{ - MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); - - xdnd_send_leave (compositor->xwayland_manager.selection_data, - compositor->xwayland_manager.selection_data->dnd.dnd_dest); - compositor->xwayland_manager.selection_data->dnd.dnd_dest = None; -} - -static void -meta_x11_drag_dest_motion (MetaWaylandDataDevice *data_device, - MetaWaylandSurface *surface, - const ClutterEvent *event) -{ - MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); - guint32 time; - gfloat x, y; - - time = clutter_event_get_time (event); - clutter_event_get_coords (event, &x, &y); - xdnd_send_position (compositor->xwayland_manager.selection_data, - compositor->xwayland_manager.selection_data->dnd.dnd_dest, - time, x, y); -} - -static void -meta_x11_drag_dest_drop (MetaWaylandDataDevice *data_device, - MetaWaylandSurface *surface) -{ - MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); - - xdnd_send_drop (compositor->xwayland_manager.selection_data, - compositor->xwayland_manager.selection_data->dnd.dnd_dest, - 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_update -}; - -const MetaWaylandDragDestFuncs * -meta_xwayland_selection_get_drag_dest_funcs (void) -{ - return &meta_x11_drag_dest_funcs; -} - -static gboolean -meta_xwayland_data_source_fetch_mimetype_list (MetaWaylandDataSource *source, - Window window, - Atom prop) -{ - MetaWaylandDataSourceXWayland *source_xwayland = - META_WAYLAND_DATA_SOURCE_XWAYLAND (source); - Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); - gulong nitems_ret, bytes_after_ret, i; - Atom *atoms, type_ret, utf8_string; - int format_ret; - struct wl_array *source_mime_types; - - source_mime_types = meta_wayland_data_source_get_mime_types (source); - if (source_mime_types->size != 0) - return TRUE; - - utf8_string = gdk_x11_get_xatom_by_name ("UTF8_STRING"); - XGetWindowProperty (xdisplay, window, prop, - 0, /* offset */ - 0x1fffffff, /* length */ - True, /* delete */ - AnyPropertyType, - &type_ret, - &format_ret, - &nitems_ret, - &bytes_after_ret, - (guchar **) &atoms); - - if (nitems_ret == 0 || type_ret != XA_ATOM) - { - XFree (atoms); - return FALSE; - } - - for (i = 0; i < nitems_ret; i++) - { - const gchar *mime_type; - - if (atoms[i] == utf8_string) - { - meta_wayland_data_source_add_mime_type (source, - "text/plain;charset=utf-8"); - source_xwayland->has_utf8_string_atom = TRUE; - } - - mime_type = gdk_x11_get_xatom_name (atoms[i]); - meta_wayland_data_source_add_mime_type (source, mime_type); - } - - XFree (atoms); - - return TRUE; -} - -static void -meta_xwayland_selection_get_x11_targets (MetaWaylandCompositor *compositor, - MetaSelectionBridge *selection) -{ - MetaWaylandDataSource *data_source; - - data_source = meta_wayland_data_source_xwayland_new (selection); - - if (meta_xwayland_data_source_fetch_mimetype_list (data_source, - selection->window, - gdk_x11_get_xatom_by_name ("_META_SELECTION"))) - { - g_clear_object (&selection->source); - selection->source = data_source; - - if (selection->selection_atom == gdk_x11_get_xatom_by_name ("CLIPBOARD")) - { - meta_wayland_data_device_set_selection (&compositor->seat->data_device, data_source, - wl_display_next_serial (compositor->wayland_display)); - } - else if (selection->selection_atom == gdk_x11_get_xatom_by_name ("PRIMARY")) - { - meta_wayland_data_device_set_primary (&compositor->seat->data_device, data_source, - wl_display_next_serial (compositor->wayland_display)); - } - } - else - g_object_unref (data_source); -} - -static void -meta_xwayland_selection_get_x11_data (MetaWaylandCompositor *compositor, - MetaSelectionBridge *selection) -{ - Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); - gulong nitems_ret, bytes_after_ret; - guchar *prop_ret; - int format_ret; - Atom type_ret; - - if (!selection->x11_selection) - return; - - XGetWindowProperty (xdisplay, - selection->window, - gdk_x11_get_xatom_by_name ("_META_SELECTION"), - 0, /* offset */ - 0x1fffffff, /* length */ - True, /* delete */ - AnyPropertyType, - &type_ret, - &format_ret, - &nitems_ret, - &bytes_after_ret, - &prop_ret); - - selection->x11_selection->incr = (type_ret == gdk_x11_get_xatom_by_name ("INCR")); - - if (selection->x11_selection->incr) - return; - - if (type_ret == gdk_x11_get_xatom_by_name (selection->x11_selection->mime_type)) - x11_selection_data_write (selection, prop_ret, nitems_ret); - - XFree (prop_ret); -} - -static gboolean -meta_xwayland_selection_handle_selection_notify (MetaWaylandCompositor *compositor, - XEvent *xevent) -{ - XSelectionEvent *event = (XSelectionEvent *) xevent; - MetaSelectionBridge *selection; - - selection = atom_to_selection_bridge (compositor, event->selection); - - if (!selection) - return FALSE; - - if (selection->window != event->requestor) - return FALSE; - - /* convert selection failed */ - if (event->property == None) - { - g_clear_pointer (&selection->x11_selection, - x11_selection_data_free); - return FALSE; - } - - if (event->target == gdk_x11_get_xatom_by_name ("TARGETS")) - meta_xwayland_selection_get_x11_targets (compositor, selection); - else - meta_xwayland_selection_get_x11_data (compositor, selection); - - return TRUE; -} - -static void -meta_xwayland_selection_send_targets (MetaWaylandCompositor *compositor, - const MetaWaylandDataSource *data_source, - Window requestor, - Atom property) -{ - Atom *targets; - gchar **p; - int i = 0; - struct wl_array *source_mime_types; - - if (!data_source) - return; - - source_mime_types = meta_wayland_data_source_get_mime_types (data_source); - if (source_mime_types->size == 0) - return; - - /* Make extra room for TIMESTAMP/TARGETS */ - targets = g_new (Atom, source_mime_types->size + 2); - - wl_array_for_each (p, source_mime_types) - { - targets[i++] = gdk_x11_get_xatom_by_name (*p); - } - - targets[i++] = gdk_x11_get_xatom_by_name ("TIMESTAMP"); - targets[i++] = gdk_x11_get_xatom_by_name ("TARGETS"); - - XChangeProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), - requestor, property, - XA_ATOM, 32, PropModeReplace, - (guchar *) targets, i); - - g_free (targets); -} - -static void -meta_xwayland_selection_send_timestamp (MetaWaylandCompositor *compositor, - Window requestor, - Atom property, - Time timestamp) -{ - Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); - - XChangeProperty (xdisplay, requestor, property, - XA_INTEGER, 32, - PropModeReplace, - (guchar *) ×tamp, 1); -} - -static void -meta_xwayland_selection_send_incr_chunk (MetaWaylandCompositor *compositor, - MetaSelectionBridge *selection) -{ - if (!selection->wayland_selection) - return; - - if (selection->wayland_selection->buffer_len > 0) - wayland_selection_update_x11_property (selection->wayland_selection); - else - wayland_selection_data_read (selection); -} - -static gboolean -handle_incr_chunk (MetaWaylandCompositor *compositor, - MetaSelectionBridge *selection, - XPropertyEvent *event) -{ - if (selection->x11_selection && - selection->x11_selection->incr && - event->window == selection->window && - event->state == PropertyNewValue && - event->atom == gdk_x11_get_xatom_by_name ("_META_SELECTION")) - { - /* X11 to Wayland */ - meta_xwayland_selection_get_incr_chunk (compositor, selection); - return TRUE; - } - else if (selection->wayland_selection && - selection->wayland_selection->incr && - event->state == PropertyDelete && - event->window == selection->wayland_selection->request_event.requestor && - event->atom == selection->wayland_selection->request_event.property) - { - /* Wayland to X11 */ - meta_xwayland_selection_send_incr_chunk (compositor, selection); - return TRUE; - } - - return FALSE; -} - -static gboolean -meta_xwayland_selection_handle_property_notify (MetaWaylandCompositor *compositor, - XEvent *xevent) -{ - MetaXWaylandSelection *selection_data = compositor->xwayland_manager.selection_data; - XPropertyEvent *event = (XPropertyEvent *) xevent; - - return handle_incr_chunk (compositor, &selection_data->clipboard, event); -} - -static gboolean -meta_xwayland_selection_handle_selection_request (MetaWaylandCompositor *compositor, - XEvent *xevent) -{ - XSelectionRequestEvent *event = (XSelectionRequestEvent *) xevent; - MetaWaylandDataSource *data_source; - MetaSelectionBridge *selection; - - selection = atom_to_selection_bridge (compositor, event->selection); - - if (!selection) - return FALSE; - - if (selection->window != event->owner) - return FALSE; - - /* We must fetch from the currently active source, not the Xwayland one */ - data_source = data_device_get_active_source_for_atom (&compositor->seat->data_device, - selection->selection_atom); - if (!data_source) - return FALSE; - - g_clear_pointer (&selection->wayland_selection, - wayland_selection_data_free); - - if (event->target == gdk_x11_get_xatom_by_name ("TARGETS")) - { - meta_xwayland_selection_send_targets (compositor, - data_source, - event->requestor, - event->property); - reply_selection_request (event, TRUE); - } - else if (event->target == gdk_x11_get_xatom_by_name ("TIMESTAMP")) - { - meta_xwayland_selection_send_timestamp (compositor, - event->requestor, event->property, - selection->timestamp); - reply_selection_request (event, TRUE); - } - else if (data_source && event->target == gdk_x11_get_xatom_by_name ("DELETE")) - { - reply_selection_request (event, TRUE); - } - else - { - if (data_source && - meta_wayland_data_source_has_mime_type (data_source, - gdk_x11_get_xatom_name (event->target))) - { - selection->wayland_selection = wayland_selection_data_new (event, - compositor); - - if (selection->wayland_selection) - wayland_selection_data_read (selection); - } - - if (!selection->wayland_selection) - reply_selection_request (event, FALSE); - } - - 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->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); - if (dnd->focus_surface == focus) - return; - - dnd->focus_surface = focus; - - if (focus && - focus->window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND) - { - XMapRaised (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; - MetaWaylandDataSource *data_source; - - meta_wayland_pointer_send_button (seat->pointer, event); - data_source = compositor->seat->data_device.dnd_data_source; - - if (seat->pointer->button_count == 0 && - (!meta_wayland_drag_grab_get_focus ((MetaWaylandDragGrab *) grab) || - meta_wayland_data_source_get_current_action (data_source) == - WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE)) - meta_xwayland_end_dnd_grab (&seat->data_device); -} - -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; - MetaDndBridge *dnd = &compositor->xwayland_manager.selection_data->dnd; - MetaWaylandSeat *seat = compositor->seat; - - /* Source side messages */ - if (event->window == dnd->selection.window) - { - MetaWaylandDataSource *data_source; - uint32_t action = 0; - - data_source = compositor->seat->data_device.dnd_data_source; - - if (!data_source) - return FALSE; - - if (event->message_type == xdnd_atoms[ATOM_DND_STATUS]) - { - /* The first bit in data.l[1] is set if the drag was accepted */ - 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]) - action = atom_to_action ((Atom) event->data.l[4]); - - meta_wayland_data_source_set_current_action (data_source, action); - return TRUE; - } - else if (event->message_type == xdnd_atoms[ATOM_DND_FINISHED]) - { - /* Reject messages mid-grab */ - if (compositor->seat->data_device.current_grab) - return FALSE; - - meta_wayland_data_source_notify_finish (data_source); - 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 (!drag_focus && - event->message_type != xdnd_atoms[ATOM_DND_ENTER]) - return FALSE; - - 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; - struct wl_array *source_mime_types; - - /* We only need to fetch once */ - source_mime_types = - meta_wayland_data_source_get_mime_types (dnd->selection.source); - if (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; - uint32_t action = 0; - - dnd->selection.client_message_timestamp = event->data.l[3]; - - 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); - - action = atom_to_action ((Atom) event->data.l[4]); - 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_get_current_action (dnd->selection.source)); - - 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]) - { - dnd->selection.client_message_timestamp = event->data.l[2]; - meta_wayland_surface_drag_dest_drop (drag_focus); - meta_xwayland_end_dnd_grab (&seat->data_device); - return TRUE; - } - } - - return FALSE; -} - -static gboolean -meta_xwayland_selection_handle_xfixes_selection_notify (MetaWaylandCompositor *compositor, - XEvent *xevent) -{ - XFixesSelectionNotifyEvent *event = (XFixesSelectionNotifyEvent *) xevent; - Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); - MetaSelectionBridge *selection; - - selection = atom_to_selection_bridge (compositor, event->selection); - - if (!selection) - return FALSE; - - if (selection->selection_atom == gdk_x11_get_xatom_by_name ("CLIPBOARD") || - selection->selection_atom == gdk_x11_get_xatom_by_name ("PRIMARY")) - { - if (event->owner == None) - { - if (selection->source && selection->owner != selection->window) - { - /* An X client went away, clear the selection */ - g_clear_object (&selection->source); - } - - selection->owner = None; - } - else - { - selection->owner = event->owner; - - if (selection->owner == selection->window) - { - /* This our own selection window */ - selection->timestamp = event->timestamp; - return TRUE; - } - - g_clear_pointer (&selection->x11_selection, - x11_selection_data_free); - - XConvertSelection (xdisplay, - event->selection, - gdk_x11_get_xatom_by_name ("TARGETS"), - gdk_x11_get_xatom_by_name ("_META_SELECTION"), - selection->window, - META_CURRENT_TIME); - XFlush (xdisplay); - } - } - else if (selection->selection_atom == xdnd_atoms[ATOM_DND_SELECTION]) - { - MetaWaylandDataDevice *data_device = &compositor->seat->data_device; - MetaWaylandSurface *focus; - - selection->owner = event->owner; - focus = compositor->seat->pointer->focus_surface; - - if (event->owner != None && event->owner != selection->window && - focus && meta_xwayland_is_xwayland_surface (focus)) - { - selection->client_message_timestamp = META_CURRENT_TIME; - selection->source = meta_wayland_data_source_xwayland_new (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_xwayland_end_dnd_grab (data_device); - } - } - - return TRUE; -} - -gboolean -meta_xwayland_selection_handle_event (XEvent *xevent) -{ - MetaWaylandCompositor *compositor; - - compositor = meta_wayland_compositor_get_default (); - - if (!compositor->xwayland_manager.selection_data) - return FALSE; - - switch (xevent->type) - { - case SelectionNotify: - return meta_xwayland_selection_handle_selection_notify (compositor, xevent); - case PropertyNotify: - return meta_xwayland_selection_handle_property_notify (compositor, xevent); - case SelectionRequest: - return meta_xwayland_selection_handle_selection_request (compositor, xevent); - case ClientMessage: - return meta_xwayland_selection_handle_client_message (compositor, xevent); - default: - { - MetaX11Display *x11_display = meta_get_display ()->x11_display; - - if (xevent->type - x11_display->xfixes_event_base == XFixesSelectionNotify) - return meta_xwayland_selection_handle_xfixes_selection_notify (compositor, xevent); - - return FALSE; - } - } -} - -static void -meta_selection_bridge_ownership_notify (struct wl_listener *listener, - void *data) -{ - Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); - MetaSelectionBridge *selection = - wl_container_of (listener, selection, ownership_listener); - MetaWaylandDataSource *owner = data; - - if (!owner && selection->window == selection->owner) - { - XSetSelectionOwner (xdisplay, selection->selection_atom, - None, selection->timestamp); - } - else if (owner && selection->source != owner) - { - XSetSelectionOwner (xdisplay, - selection->selection_atom, - selection->window, - META_CURRENT_TIME); - } -} - -static void -init_selection_bridge (MetaSelectionBridge *selection, - Atom selection_atom, - struct wl_signal *signal) -{ - Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); - XSetWindowAttributes attributes; - guint mask; - - attributes.event_mask = PropertyChangeMask; - - selection->ownership_listener.notify = meta_selection_bridge_ownership_notify; - wl_signal_add (signal, &selection->ownership_listener); - - selection->selection_atom = selection_atom; - selection->window = - XCreateWindow (xdisplay, - gdk_x11_window_get_xid (gdk_get_default_root_window ()), - -1, -1, 1, 1, /* position */ - 0, /* border width */ - 0, /* depth */ - InputOnly, /* class */ - CopyFromParent, /* visual */ - CWEventMask, - &attributes); - - mask = XFixesSetSelectionOwnerNotifyMask | - XFixesSelectionWindowDestroyNotifyMask | - XFixesSelectionClientCloseNotifyMask; - - XFixesSelectSelectionInput (xdisplay, selection->window, - selection_atom, mask); -} - -static void -shutdown_selection_bridge (MetaSelectionBridge *selection) -{ - wl_list_remove (&selection->ownership_listener.link); - - XDestroyWindow (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), - selection->window); - g_clear_pointer (&selection->wayland_selection, - wayland_selection_data_free); - g_clear_pointer (&selection->x11_selection, - x11_selection_data_free); -} - -void -meta_xwayland_init_selection (void) -{ - MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); - MetaXWaylandManager *manager = &compositor->xwayland_manager; - - g_assert (manager->selection_data == NULL); - - manager->selection_data = g_slice_new0 (MetaXWaylandSelection); - - 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); - init_selection_bridge (&manager->selection_data->primary, - gdk_x11_get_xatom_by_name ("PRIMARY"), - &compositor->seat->data_device.primary_ownership_signal); - init_selection_bridge (&manager->selection_data->dnd.selection, - xdnd_atoms[ATOM_DND_SELECTION], - &compositor->seat->data_device.dnd_ownership_signal); -} - -void -meta_xwayland_shutdown_selection (void) -{ - MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); - MetaXWaylandManager *manager = &compositor->xwayland_manager; - MetaXWaylandSelection *selection = manager->selection_data; - - g_assert (selection != NULL); - - g_clear_object (&selection->clipboard.source); - - meta_xwayland_shutdown_dnd (manager); - shutdown_selection_bridge (&selection->clipboard); - shutdown_selection_bridge (&selection->primary); - shutdown_selection_bridge (&selection->dnd.selection); - - g_slice_free (MetaXWaylandSelection, selection); - manager->selection_data = NULL; -} diff --git a/src/wayland/meta-xwayland.c b/src/wayland/meta-xwayland.c index 15c85df69..90fdd222b 100644 --- a/src/wayland/meta-xwayland.c +++ b/src/wayland/meta-xwayland.c @@ -559,7 +559,7 @@ out: static void on_x11_display_closing (MetaDisplay *display) { - meta_xwayland_shutdown_selection (); + meta_xwayland_shutdown_dnd (); } /* To be called right after connecting */ @@ -575,7 +575,7 @@ meta_xwayland_complete_init (MetaDisplay *display) g_signal_connect (display, "x11-display-closing", G_CALLBACK (on_x11_display_closing), NULL); - meta_xwayland_init_selection (); + meta_xwayland_init_dnd (); } void diff --git a/src/x11/events.c b/src/x11/events.c index e925e482e..92b5d7015 100644 --- a/src/x11/events.c +++ b/src/x11/events.c @@ -1774,7 +1774,7 @@ meta_x11_display_handle_xevent (MetaX11Display *x11_display, #ifdef HAVE_WAYLAND if (meta_is_wayland_compositor () && - meta_xwayland_selection_handle_event (event)) + meta_xwayland_dnd_handle_event (event)) { bypass_gtk = bypass_compositor = TRUE; goto out;