[mutter] xwayland: Implement wayland-to-X11 DnD



commit ccb7833e992055d381b3e403928b05c37e3d10a8
Author: Carlos Garnacho <carlosg gnome org>
Date:   Wed Apr 29 17:44:05 2015 +0200

    xwayland: Implement wayland-to-X11 DnD
    
    X11 client windows now hook a X11-specific MetaWaylandDragDestFuncs
    that converts these into Xdnd* messages, and an additional selection
    bridge has been added to take care of XdndSelection, and the data
    transfers done through it.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=738312

 src/wayland/meta-wayland-surface.c    |    7 +-
 src/wayland/meta-xwayland-private.h   |    2 +
 src/wayland/meta-xwayland-selection.c |  288 +++++++++++++++++++++++++++++++++
 3 files changed, 296 insertions(+), 1 deletions(-)
---
diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c
index 405164b..cfd112d 100644
--- a/src/wayland/meta-wayland-surface.c
+++ b/src/wayland/meta-wayland-surface.c
@@ -52,6 +52,7 @@
 
 #include "meta-surface-actor.h"
 #include "meta-surface-actor-wayland.h"
+#include "meta-xwayland-private.h"
 
 typedef enum
 {
@@ -751,7 +752,11 @@ sync_reactive (MetaWaylandSurface *surface)
 static void
 sync_drag_dest_funcs (MetaWaylandSurface *surface)
 {
-  surface->dnd.funcs = meta_wayland_data_device_get_drag_dest_funcs ();
+  if (surface->window &&
+      surface->window->client_type == META_WINDOW_CLIENT_TYPE_X11)
+    surface->dnd.funcs = meta_xwayland_selection_get_drag_dest_funcs ();
+  else
+    surface->dnd.funcs = meta_wayland_data_device_get_drag_dest_funcs ();
 }
 
 void
diff --git a/src/wayland/meta-xwayland-private.h b/src/wayland/meta-xwayland-private.h
index 594ab1c..ad2a2f6 100644
--- a/src/wayland/meta-xwayland-private.h
+++ b/src/wayland/meta-xwayland-private.h
@@ -39,4 +39,6 @@ void     meta_xwayland_init_selection         (void);
 void     meta_xwayland_shutdown_selection     (void);
 gboolean meta_xwayland_selection_handle_event (XEvent *xevent);
 
+const MetaWaylandDragDestFuncs * meta_xwayland_selection_get_drag_dest_funcs (void);
+
 #endif /* META_XWAYLAND_PRIVATE_H */
diff --git a/src/wayland/meta-xwayland-selection.c b/src/wayland/meta-xwayland-selection.c
index a3d68a7..39051a4 100644
--- a/src/wayland/meta-xwayland-selection.c
+++ b/src/wayland/meta-xwayland-selection.c
@@ -38,6 +38,7 @@
 #include "meta-wayland-data-device.h"
 
 #define INCR_CHUNK_SIZE (128 * 1024)
+#define XDND_VERSION 5
 
 typedef struct {
   MetaXWaylandSelection *selection_data;
@@ -70,10 +71,184 @@ typedef struct {
   struct wl_listener ownership_listener;
 } MetaSelectionBridge;
 
+typedef struct {
+  MetaSelectionBridge selection;
+  Window dnd_dest;
+} MetaDndBridge;
+
 struct _MetaXWaylandSelection {
   MetaSelectionBridge clipboard;
+  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,
+  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",
+  NULL
+};
+
+Atom xdnd_atoms[N_DND_ATOMS];
+
+/* XDND helpers */
+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;
+
+  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;
+
+  if (data_source->mime_types.size <= 3)
+    {
+      /* The mimetype atoms fit in this same message */
+      gchar **p;
+      gint i = 2;
+
+      wl_array_for_each (p, &data_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, data_source->mime_types.size);
+
+      wl_array_for_each (p, &data_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)
+{
+  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_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] = xdnd_atoms[ATOM_DND_ACTION_COPY];
+
+  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
+meta_xwayland_init_dnd (void)
+{
+  guint i;
+
+  for (i = 0; i < N_DND_ATOMS; i++)
+    xdnd_atoms[i] = gdk_x11_get_xatom_by_name (atom_names[i]);
+}
+
+/* X11/Wayland data bridges */
+
 static MetaSelectionBridge *
 atom_to_selection_bridge (MetaWaylandCompositor *compositor,
                           Atom                   selection_atom)
@@ -82,6 +257,8 @@ atom_to_selection_bridge (MetaWaylandCompositor *compositor,
 
   if (selection_atom == selection_data->clipboard.selection_atom)
     return &selection_data->clipboard;
+  else if (selection_atom == selection_data->dnd.selection.selection_atom)
+    return &selection_data->dnd.selection;
   else
     return NULL;
 }
@@ -164,6 +341,8 @@ data_device_get_active_source_for_atom (MetaWaylandDataDevice *data_device,
 {
   if (selection_atom == gdk_x11_get_xatom_by_name ("CLIPBOARD"))
     return data_device->selection_data_source;
+  else if (selection_atom == xdnd_atoms[ATOM_DND_SELECTION])
+    return data_device->dnd_data_source;
   else
     return NULL;
 }
@@ -457,6 +636,69 @@ static const MetaWaylandDataSourceFuncs meta_x11_source_funcs = {
   meta_x11_source_cancel
 };
 
+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 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
+};
+
+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,
@@ -753,6 +995,45 @@ meta_xwayland_selection_handle_selection_request (MetaWaylandCompositor *composi
 }
 
 static gboolean
+meta_xwayland_selection_handle_client_message (MetaWaylandCompositor *compositor,
+                                               XEvent                *xevent)
+{
+  XClientMessageEvent *event = (XClientMessageEvent *) xevent;
+  MetaSelectionBridge *selection = &compositor->xwayland_manager.selection_data->dnd.selection;
+
+  /* Source side messages */
+  if (event->window == selection->window)
+    {
+      MetaWaylandDataSource *data_source;
+
+      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 */
+          data_source->has_target = (event->data.l[1] & 1) != 0;
+
+          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_device_set_dnd_source (&compositor->seat->data_device,
+                                                   NULL);
+          return TRUE;
+        }
+    }
+
+  return FALSE;
+}
+
+static gboolean
 meta_xwayland_selection_handle_xfixes_selection_notify (MetaWaylandCompositor *compositor,
                                                         XEvent                *xevent)
 {
@@ -824,6 +1105,8 @@ meta_xwayland_selection_handle_event (XEvent *xevent)
       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:
       {
         MetaDisplay *display = meta_get_display ();
@@ -916,9 +1199,13 @@ meta_xwayland_init_selection (void)
 
   manager->selection_data = g_slice_new0 (MetaXWaylandSelection);
 
+  meta_xwayland_init_dnd ();
   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->dnd.selection,
+                         xdnd_atoms[ATOM_DND_SELECTION],
+                         &compositor->seat->data_device.dnd_ownership_signal);
 }
 
 void
@@ -937,6 +1224,7 @@ meta_xwayland_shutdown_selection (void)
     }
 
   shutdown_selection_bridge (&selection->clipboard);
+  shutdown_selection_bridge (&selection->dnd.selection);
 
   g_slice_free (MetaXWaylandSelection, selection);
   manager->selection_data = NULL;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]