diff --git a/gdk/x11/gdkdnd-x11.c b/gdk/x11/gdkdnd-x11.c index c36c10bf41..618630af49 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 #include @@ -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) { @@ -2667,6 +2773,24 @@ gdk_drag_anim_timeout (gpointer data) return G_SOURCE_CONTINUE; } +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 6ebd0e8eb1..431ca7ddc8 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 dd2a0034e0..9fbcd7529f 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);