[gtk+] x11: Move selection handling to GDK



commit 65eab87238191ac55d3a7f40f4e35795e1d67b53
Author: Benjamin Otte <otte redhat com>
Date:   Wed Dec 13 18:22:21 2017 +0100

    x11: Move selection handling to GDK
    
    Instead of claiming the selection in GTK, claim it in the X11 dnd code.
    Also handle SelectionRequest and SelectionClear X events there.

 gdk/x11/gdkdnd-x11.c     |  168 +++++++++++++++++++++++++++++++++++++++++++---
 gdk/x11/gdkprivate-x11.h |    4 -
 gtk/gtkdnd.c             |   18 -----
 3 files changed, 159 insertions(+), 31 deletions(-)
---
diff --git a/gdk/x11/gdkdnd-x11.c b/gdk/x11/gdkdnd-x11.c
index c36c10b..618630a 100644
--- a/gdk/x11/gdkdnd-x11.c
+++ b/gdk/x11/gdkdnd-x11.c
@@ -26,6 +26,7 @@
 
 #include "gdkx11dnd.h"
 
+#include "gdk-private.h"
 #include "gdkasync.h"
 #include "gdkclipboardprivate.h"
 #include "gdkclipboard-x11.h"
@@ -38,6 +39,7 @@
 #include "gdkprivate-x11.h"
 #include "gdkscreen-x11.h"
 #include "gdkselectioninputstream-x11.h"
+#include "gdkselectionoutputstream-x11.h"
 
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
@@ -82,6 +84,7 @@ struct _GdkX11DragContext
   gint start_y;
   guint16 last_x;              /* Coordinates from last event */
   guint16 last_y;
+  gulong timestamp;            /* Timestamp we claimed the DND selection with */
   GdkDragAction old_action;    /* The last action we sent to the source */
   GdkDragAction old_actions;   /* The last actions we sent to the source */
   GdkDragAction xdnd_actions;  /* What is currently set in XdndActionList */
@@ -2091,7 +2094,7 @@ create_drag_window (GdkDisplay *display)
   return window;
 }
 
-Window
+static Window
 _gdk_x11_display_get_drag_protocol (GdkDisplay      *display,
                                     Window           xid,
                                     GdkDragProtocol *protocol,
@@ -2612,6 +2615,109 @@ gdk_x11_drag_context_set_hotspot (GdkDragContext *context,
     }
 }
 
+static void
+gdk_x11_drag_context_default_output_done (GObject      *context,
+                                          GAsyncResult *result,
+                                          gpointer      user_data)
+{
+  GError *error = NULL;
+
+  if (!gdk_drag_context_write_finish (GDK_DRAG_CONTEXT (context), result, &error))
+    {
+      GDK_NOTE(DND, g_printerr ("failed to write stream: %s\n", error->message));
+      g_error_free (error);
+    }
+}
+
+static void
+gdk_x11_drag_context_default_output_handler (GOutputStream   *stream,
+                                             const char      *mime_type,
+                                             gpointer         user_data)
+{
+  gdk_drag_context_write_async (GDK_DRAG_CONTEXT (user_data),
+                                mime_type,
+                                stream,
+                                G_PRIORITY_DEFAULT,
+                                NULL,
+                                gdk_x11_drag_context_default_output_done,
+                                NULL);
+  g_object_unref (stream);
+}
+
+static gboolean
+gdk_x11_drag_context_xevent (GdkDisplay   *display,
+                             const XEvent *xevent,
+                             gpointer      data)
+{
+  GdkDragContext *context = GDK_DRAG_CONTEXT (data);
+  GdkX11DragContext *x11_context = GDK_X11_DRAG_CONTEXT (context);
+  Window xwindow;
+  Atom xselection;
+
+  xwindow = GDK_WINDOW_XID (x11_context->ipc_window);
+  xselection = gdk_x11_get_xatom_by_name_for_display (display, "XdndSelection");
+
+  if (xevent->xany.window != xwindow)
+    return FALSE;
+
+  switch (xevent->type)
+  {
+    case SelectionClear:
+      if (xevent->xselectionclear.selection != xselection)
+        return FALSE;
+
+      if (xevent->xselectionclear.time < x11_context->timestamp)
+        {
+          GDK_NOTE(CLIPBOARD, g_printerr ("ignoring SelectionClear with too old timestamp (%lu vs %lu)\n",
+                                          xevent->xselectionclear.time, x11_context->timestamp));
+          return FALSE;
+        }
+
+      GDK_NOTE(CLIPBOARD, g_printerr ("got SelectionClear, aborting DND\n"));
+      gdk_drag_context_cancel (context, GDK_DRAG_CANCEL_ERROR);
+      return TRUE;
+
+    case SelectionRequest:
+      {
+        const char *target, *property;
+
+        if (xevent->xselectionrequest.selection != xselection)
+          return FALSE;
+
+        target = gdk_x11_get_xatom_name_for_display (display, xevent->xselectionrequest.target);
+        if (xevent->xselectionrequest.property == None)
+          property = target;
+        else
+          property = gdk_x11_get_xatom_name_for_display (display, xevent->xselectionrequest.property);
+
+        if (xevent->xselectionrequest.requestor == None)
+          {
+            GDK_NOTE(CLIPBOARD, g_printerr ("got SelectionRequest for %s @ %s with NULL window, ignoring\n",
+                                            target, property));
+            return TRUE;
+          }
+        
+        GDK_NOTE(CLIPBOARD, g_printerr ("got SelectionRequest for %s @ %s\n",
+                                        target, property));
+
+        gdk_x11_selection_output_streams_create (display,
+                                                 gdk_drag_context_get_formats (context),
+                                                 xevent->xselectionrequest.requestor,
+                                                 xevent->xselectionrequest.selection,
+                                                 xevent->xselectionrequest.target,
+                                                 xevent->xselectionrequest.property ? 
xevent->xselectionrequest.property
+                                                                                    : 
xevent->xselectionrequest.target,
+                                                 xevent->xselectionrequest.time,
+                                                 gdk_x11_drag_context_default_output_handler,
+                                                 context);
+        return TRUE;
+      }
+
+    default:
+      return FALSE;
+  }
+}
+
 static double
 ease_out_cubic (double t)
 {
@@ -2668,6 +2774,24 @@ gdk_drag_anim_timeout (gpointer data)
 }
 
 static void
+gdk_x11_drag_context_release_selection (GdkDragContext *context)
+{
+  GdkX11DragContext *x11_context = GDK_X11_DRAG_CONTEXT (context);
+  GdkDisplay *display;
+  Display *xdisplay;
+  Window xwindow;
+  Atom xselection;
+
+  display = gdk_drag_context_get_display (context);
+  xdisplay = GDK_DISPLAY_XDISPLAY (display);
+  xselection = gdk_x11_get_xatom_by_name_for_display (display, "XdndSelection");
+  xwindow = GDK_WINDOW_XID (x11_context->ipc_window);
+
+  if (XGetSelectionOwner (xdisplay, xselection) == xwindow)
+    XSetSelectionOwner (xdisplay, xselection, None, CurrentTime);
+}
+
+static void
 gdk_x11_drag_context_drop_done (GdkDragContext *context,
                                 gboolean        success)
 {
@@ -2678,6 +2802,11 @@ gdk_x11_drag_context_drop_done (GdkDragContext *context,
   cairo_t *cr;
   guint id;
 
+  gdk_x11_drag_context_release_selection (context);
+
+  g_signal_handlers_disconnect_by_func (gdk_drag_context_get_display (context),
+                                        gdk_x11_drag_context_xevent,
+                                        context);
   if (success)
     {
       gdk_window_hide (x11_context->drag_window);
@@ -2864,18 +2993,26 @@ _gdk_x11_window_drag_begin (GdkWindow          *window,
                             gint                dx,
                             gint                dy)
 {
+  GdkX11DragContext *x11_context;
   GdkDragContext *context;
+  GdkDisplay *display;
   int x_root, y_root;
+  Atom xselection;
+
+  display = gdk_window_get_display (window);
 
   context = (GdkDragContext *) g_object_new (GDK_TYPE_X11_DRAG_CONTEXT,
-                                             "display", gdk_window_get_display (window),
+                                             "display", display,
                                              "content", content,
                                              NULL);
+  x11_context = GDK_X11_DRAG_CONTEXT (context);
 
   context->is_source = TRUE;
   context->source_window = window;
   g_object_ref (window);
 
+  g_signal_connect (display, "xevent", G_CALLBACK (gdk_x11_drag_context_xevent), context);
+
   precache_target_list (context);
 
   gdk_drag_context_set_device (context, device);
@@ -2883,16 +3020,16 @@ _gdk_x11_window_drag_begin (GdkWindow          *window,
   x_root += dx;
   y_root += dy;
 
-  GDK_X11_DRAG_CONTEXT (context)->start_x = x_root;
-  GDK_X11_DRAG_CONTEXT (context)->start_y = y_root;
-  GDK_X11_DRAG_CONTEXT (context)->last_x = x_root;
-  GDK_X11_DRAG_CONTEXT (context)->last_y = y_root;
+  x11_context->start_x = x_root;
+  x11_context->start_y = y_root;
+  x11_context->last_x = x_root;
+  x11_context->last_y = y_root;
 
   context->protocol = GDK_DRAG_PROTO_XDND;
-  GDK_X11_DRAG_CONTEXT (context)->actions = actions;
-  GDK_X11_DRAG_CONTEXT (context)->ipc_window = g_object_ref (window);
+  x11_context->actions = actions;
+  x11_context->ipc_window = g_object_ref (window);
 
-  GDK_X11_DRAG_CONTEXT (context)->drag_window = create_drag_window (gdk_window_get_display(window));
+  x11_context->drag_window = create_drag_window (display);
 
   if (!drag_context_grab (context))
     {
@@ -2902,6 +3039,19 @@ _gdk_x11_window_drag_begin (GdkWindow          *window,
   
   move_drag_window (context, x_root, y_root);
 
+  x11_context->timestamp = gdk_display_get_last_seen_time (display);
+  xselection = gdk_x11_get_xatom_by_name_for_display (display, "XdndSelection");
+  XSetSelectionOwner (GDK_DISPLAY_XDISPLAY (display),
+                      xselection,
+                      GDK_WINDOW_XID (window),
+                      x11_context->timestamp);
+  if (XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display), xselection) != GDK_WINDOW_XID (window))
+    {
+      GDK_NOTE(DND, g_printerr ("failed XSetSelectionOwner() on \"XdndSelection\", aborting DND\n"));
+      g_object_unref (context);
+      return NULL;
+    }
+
   return context;
 }
 
diff --git a/gdk/x11/gdkprivate-x11.h b/gdk/x11/gdkprivate-x11.h
index 6ebd0e8..431ca7d 100644
--- a/gdk/x11/gdkprivate-x11.h
+++ b/gdk/x11/gdkprivate-x11.h
@@ -147,10 +147,6 @@ void _gdk_x11_display_queue_events            (GdkDisplay *display);
 
 
 GdkAppLaunchContext *_gdk_x11_display_get_app_launch_context (GdkDisplay *display);
-Window      _gdk_x11_display_get_drag_protocol     (GdkDisplay      *display,
-                                                    Window           xid,
-                                                    GdkDragProtocol *protocol,
-                                                    guint           *version);
 
 gboolean    _gdk_x11_display_set_selection_owner   (GdkDisplay *display,
                                                     GdkWindow  *owner,
diff --git a/gtk/gtkdnd.c b/gtk/gtkdnd.c
index dd2a003..9fbcd75 100644
--- a/gtk/gtkdnd.c
+++ b/gtk/gtkdnd.c
@@ -1122,7 +1122,6 @@ gtk_drag_begin_internal (GtkWidget          *widget,
   GtkWidget *ipc_widget;
   GdkWindow *ipc_window;
   int dx, dy;
-  GdkAtom selection;
   GtkDragContent *content;
   guint32 time;
 
@@ -1205,23 +1204,6 @@ gtk_drag_begin_internal (GtkWidget          *widget,
   g_signal_connect (context, "cancel",
                     G_CALLBACK (gtk_drag_context_cancel_cb), info);
 
-  selection = gdk_drag_get_selection (context);
-  if (selection)
-    {
-      gtk_selection_owner_set_for_display (gtk_widget_get_display (info->widget),
-                                           info->ipc_widget,
-                                           selection,
-                                           time);
-
-      gtk_selection_add_targets (info->ipc_widget,
-                                 selection,
-                                 info->target_list);
-
-      gtk_selection_add_target (info->ipc_widget,
-                                selection,
-                                gdk_atom_intern_static_string ("DELETE"));
-    }
-
   g_signal_connect (info->ipc_widget, "selection-get",
                     G_CALLBACK (gtk_drag_selection_get), info);
 


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