[mutter/wip/carlosg/clipboard-manager: 9/10] wayland: Reduce MetaXWaylandSelection to just DnD



commit 2f9a49efa1cdf6279ff10c5e6b4219b827eaf0f1
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.

 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                    |  946 +++++++++++
 src/wayland/meta-xwayland-private.h                |    6 +-
 src/wayland/meta-xwayland-selection.c              | 1796 --------------------
 src/wayland/meta-xwayland.c                        |    4 +-
 src/x11/events.c                                   |    2 +-
 9 files changed, 959 insertions(+), 1809 deletions(-)
---
diff --git a/src/meson.build b/src/meson.build
index 27e5ede93..a211b84b0 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -558,8 +558,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 002d3df69..64fcd7c1a 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_dnd_source_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_dnd_source_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..d2e21fcbb
--- /dev/null
+++ b/src/wayland/meta-xwayland-dnd.c
@@ -0,0 +1,946 @@
+/*
+ * 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.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, x11_display->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 (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.window;
+
+  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.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 (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.window;
+  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.window,
+                         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.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->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.window &&
+      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 f0b95351d..249c533f0 100644
--- a/src/wayland/meta-xwayland.c
+++ b/src/wayland/meta-xwayland.c
@@ -558,7 +558,7 @@ out:
 static void
 on_x11_display_closing (MetaDisplay *display)
 {
-  meta_xwayland_shutdown_selection ();
+  meta_xwayland_shutdown_dnd ();
 }
 
 /* To be called right after connecting */
@@ -574,7 +574,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]