[mutter/wip/carlosg/clipboard-manager: 16/17] wayland: Reduce MetaXWaylandSelection to just DnD
- From: Carlos Garnacho <carlosg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [mutter/wip/carlosg/clipboard-manager: 16/17] wayland: Reduce MetaXWaylandSelection to just DnD
- Date: Thu, 2 May 2019 14:34:48 +0000 (UTC)
commit 17d00d49d49d49bdac0fafcf8047ad997f801cd6
Author: Carlos Garnacho <carlosg gnome org>
Date: Thu Nov 22 01:33:13 2018 +0100
wayland: Reduce MetaXWaylandSelection to just DnD
All the actual selection management functionality is superseded by
MetaSelection. Reduce it to just handling the XDND messaging and leave
selections to MetaSelection.
https://gitlab.gnome.org/GNOME/mutter/merge_requests/320
src/meson.build | 4 +-
src/wayland/meta-wayland-data-device.c | 6 +-
src/wayland/meta-wayland-private.h | 4 +-
...ction-private.h => meta-xwayland-dnd-private.h} | 0
src/wayland/meta-xwayland-dnd.c | 948 +++++++++++
src/wayland/meta-xwayland-private.h | 6 +-
src/wayland/meta-xwayland-selection.c | 1797 --------------------
src/wayland/meta-xwayland.c | 4 +-
src/x11/events.c | 2 +-
9 files changed, 961 insertions(+), 1810 deletions(-)
---
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 <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <glib-unix.h>
+#include <gio/gunixoutputstream.h>
+#include <gio/gunixinputstream.h>
+#include <gdk/gdkx.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/Xfixes.h>
+
+#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.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;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]