From: Benjamin Otte Date: Thu, 14 Jun 2018 00:47:56 +0000 (+0200) Subject: x11: Split drag and drop contexts X-Git-Tag: archive/raspbian/4.4.1+ds1-2+rpi1^2~18^2~22^2~84 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=dd30a288f2e92b342790e7b2c5aebfba743e8073;p=gtk4.git x11: Split drag and drop contexts While doing so, turn the drop context into a GdkDrop subclass and no longer pretend to be a GdkDragContext. --- diff --git a/gdk/x11/gdkdnd-x11.c b/gdk/x11/gdkdnd-x11.c index be9a90d298..7c743a3d4f 100644 --- a/gdk/x11/gdkdnd-x11.c +++ b/gdk/x11/gdkdnd-x11.c @@ -167,19 +167,6 @@ static GrabKey grab_keys[] = { static GdkSurfaceCache *gdk_surface_cache_ref (GdkSurfaceCache *cache); static void gdk_surface_cache_unref (GdkSurfaceCache *cache); -static gboolean xdnd_enter_filter (GdkSurface *surface, - const XEvent *xevent); -static gboolean xdnd_leave_filter (GdkSurface *surface, - const XEvent *xevent); -static gboolean xdnd_position_filter (GdkSurface *surface, - const XEvent *xevent); -static gboolean xdnd_drop_filter (GdkSurface *surface, - const XEvent *xevent); - -static void xdnd_manage_source_filter (GdkDrop *drop, - GdkSurface *surface, - gboolean add_filter); - gboolean gdk_x11_drag_context_handle_event (GdkDragContext *context, const GdkEvent *event); void gdk_x11_drag_context_action_changed (GdkDragContext *context, @@ -188,17 +175,6 @@ void gdk_x11_drag_context_action_changed (GdkDragContext *context, static GList *contexts; static GSList *window_caches; -static const struct { - const char *atom_name; - gboolean (* func) (GdkSurface *surface, const XEvent *event); -} xdnd_filters[] = { - { "XdndEnter", xdnd_enter_filter }, - { "XdndLeave", xdnd_leave_filter }, - { "XdndPosition", xdnd_position_filter }, - { "XdndDrop", xdnd_drop_filter }, -}; - - G_DEFINE_TYPE (GdkX11DragContext, gdk_x11_drag_context, GDK_TYPE_DRAG_CONTEXT) static void @@ -221,14 +197,10 @@ static gboolean gdk_x11_drag_context_drag_motion (GdkDragContext *context, GdkDragAction suggested_action, GdkDragAction possible_actions, guint32 time); -static void gdk_x11_drag_context_status (GdkDrop *drop, - GdkDragAction actions); static void gdk_x11_drag_context_drag_abort (GdkDragContext *context, guint32 time_); static void gdk_x11_drag_context_drag_drop (GdkDragContext *context, guint32 time_); -static void gdk_x11_drag_context_finish (GdkDrop *drop, - GdkDragAction action); static GdkSurface * gdk_x11_drag_context_get_drag_surface (GdkDragContext *context); static void gdk_x11_drag_context_set_hotspot (GdkDragContext *context, gint hot_x, @@ -242,149 +214,14 @@ static void gdk_x11_drag_context_cancel (GdkDragContext *con static void gdk_x11_drag_context_drop_performed (GdkDragContext *context, guint32 time); -static void -gdk_x11_drag_context_read_got_stream (GObject *source, - GAsyncResult *res, - gpointer data) -{ - GTask *task = data; - GError *error = NULL; - GInputStream *stream; - const char *type; - int format; - - stream = gdk_x11_selection_input_stream_new_finish (res, &type, &format, &error); - if (stream == NULL) - { - GSList *targets, *next; - - targets = g_task_get_task_data (task); - next = targets->next; - if (next) - { - GdkDrop *drop = GDK_DROP (g_task_get_source_object (task)); - - GDK_DISPLAY_NOTE (gdk_drop_get_display (drop), DND, g_printerr ("reading %s failed, trying %s next\n", - (char *) targets->data, (char *) next->data)); - targets->next = NULL; - g_task_set_task_data (task, next, (GDestroyNotify) g_slist_free); - gdk_x11_selection_input_stream_new_async (gdk_drop_get_display (drop), - "XdndSelection", - next->data, - CurrentTime, - g_task_get_priority (task), - g_task_get_cancellable (task), - gdk_x11_drag_context_read_got_stream, - task); - g_error_free (error); - return; - } - - g_task_return_error (task, error); - } - else - { - const char *mime_type = ((GSList *) g_task_get_task_data (task))->data; -#if 0 - gsize i; - - for (i = 0; i < G_N_ELEMENTS (special_targets); i++) - { - if (g_str_equal (mime_type, special_targets[i].x_target)) - { - g_assert (special_targets[i].mime_type != NULL); - - GDK_DISPLAY_NOTE (CLIPBOARD, g_printerr ("%s: reading with converter from %s to %s\n", - cb->selection, mime_type, special_targets[i].mime_type)); - mime_type = g_intern_string (special_targets[i].mime_type); - g_task_set_task_data (task, g_slist_prepend (NULL, (gpointer) mime_type), (GDestroyNotify) g_slist_free); - stream = special_targets[i].convert (cb, stream, type, format); - break; - } - } -#endif - - GDK_NOTE (DND, g_printerr ("reading DND as %s now\n", - mime_type)); - g_task_return_pointer (task, stream, g_object_unref); - } - - g_object_unref (task); -} - -static void -gdk_x11_drag_context_read_async (GdkDrop *drop, - GdkContentFormats *formats, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSList *targets; - GTask *task; - - task = g_task_new (drop, cancellable, callback, user_data); - g_task_set_priority (task, io_priority); - g_task_set_source_tag (task, gdk_x11_drag_context_read_async); - - targets = gdk_x11_clipboard_formats_to_targets (formats); - g_task_set_task_data (task, targets, (GDestroyNotify) g_slist_free); - if (targets == NULL) - { - g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - _("No compatible transfer format found")); - return; - } - - GDK_DISPLAY_NOTE (gdk_drop_get_display (drop), DND, g_printerr ("new read for %s (%u other options)\n", - (char *) targets->data, g_slist_length (targets->next))); - gdk_x11_selection_input_stream_new_async (gdk_drop_get_display (drop), - "XdndSelection", - targets->data, - CurrentTime, - io_priority, - cancellable, - gdk_x11_drag_context_read_got_stream, - task); -} - -static GInputStream * -gdk_x11_drag_context_read_finish (GdkDrop *drop, - const char **out_mime_type, - GAsyncResult *result, - GError **error) -{ - GTask *task; - - g_return_val_if_fail (g_task_is_valid (result, G_OBJECT (drop)), NULL); - task = G_TASK (result); - g_return_val_if_fail (g_task_get_source_tag (task) == gdk_x11_drag_context_read_async, NULL); - - if (out_mime_type) - { - GSList *targets; - - targets = g_task_get_task_data (task); - *out_mime_type = targets ? targets->data : NULL; - } - - return g_task_propagate_pointer (task, error); -} - static void gdk_x11_drag_context_class_init (GdkX11DragContextClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - GdkDropClass *drop_class = GDK_DROP_CLASS (klass); GdkDragContextClass *context_class = GDK_DRAG_CONTEXT_CLASS (klass); object_class->finalize = gdk_x11_drag_context_finalize; - drop_class->status = gdk_x11_drag_context_status; - drop_class->finish = gdk_x11_drag_context_finish; - drop_class->read_async = gdk_x11_drag_context_read_async; - drop_class->read_finish = gdk_x11_drag_context_read_finish; - context_class->drag_abort = gdk_x11_drag_context_drag_abort; context_class->drag_drop = gdk_x11_drag_context_drag_drop; context_class->get_drag_surface = gdk_x11_drag_context_get_drag_surface; @@ -404,12 +241,6 @@ gdk_x11_drag_context_finalize (GObject *object) GdkX11DragContext *x11_context = GDK_X11_DRAG_CONTEXT (object); GdkSurface *drag_surface, *ipc_surface; - if (context->source_surface) - { - if ((x11_context->protocol == GDK_DRAG_PROTO_XDND) && !context->is_source) - xdnd_manage_source_filter (GDK_DROP (context), context->source_surface, FALSE); - } - if (x11_context->cache) gdk_surface_cache_unref (x11_context->cache); @@ -428,10 +259,10 @@ gdk_x11_drag_context_finalize (GObject *object) /* Drag Contexts */ -static GdkDragContext * -gdk_drag_context_find (GdkDisplay *display, - Window source_xid, - Window dest_xid) +GdkDragContext * +gdk_x11_drag_context_find (GdkDisplay *display, + Window source_xid, + Window dest_xid) { GList *tmp_list; GdkDragContext *context; @@ -971,16 +802,6 @@ get_client_window_at_coords (GdkSurfaceCache *cache, return GDK_DISPLAY_XROOTWIN (display); } -#ifdef G_ENABLE_DEBUG -static void -print_target_list (GdkContentFormats *formats) -{ - gchar *name = gdk_content_formats_to_string (formats); - g_message ("DND formats: %s", name); - g_free (name); -} -#endif /* G_ENABLE_DEBUG */ - /************************************************************* ***************************** XDND ************************** *************************************************************/ @@ -1034,16 +855,16 @@ xdnd_action_to_atom (GdkDisplay *display, /* Source side */ -static gboolean -xdnd_status_filter (GdkDisplay *display, - const XEvent *xevent) +void +gdk_x11_drag_context_handle_status (GdkDisplay *display, + const XEvent *xevent) { guint32 dest_surface = xevent->xclient.data.l[0]; guint32 flags = xevent->xclient.data.l[1]; Atom action = xevent->xclient.data.l[4]; GdkDragContext *context; - context = gdk_drag_context_find (display, xevent->xclient.window, dest_surface); + context = gdk_x11_drag_context_find (display, xevent->xclient.window, dest_surface); GDK_DISPLAY_NOTE (display, DND, g_message ("XdndStatus: dest_surface: %#x action: %ld", @@ -1070,19 +891,17 @@ xdnd_status_filter (GdkDisplay *display, g_signal_emit_by_name (context, "action-changed", action); } } - - return TRUE; } -static gboolean -xdnd_finished_filter (GdkDisplay *display, - const XEvent *xevent) +void +gdk_x11_drag_context_handle_finished (GdkDisplay *display, + const XEvent *xevent) { guint32 dest_surface = xevent->xclient.data.l[0]; GdkDragContext *context; GdkX11DragContext *context_x11; - context = gdk_drag_context_find (display, xevent->xclient.window, dest_surface); + context = gdk_x11_drag_context_find (display, xevent->xclient.window, dest_surface); GDK_DISPLAY_NOTE (display, DND, g_message ("XdndFinished: dest_surface: %#x", dest_surface)); @@ -1100,8 +919,6 @@ xdnd_finished_filter (GdkDisplay *display, g_object_unref (context); } - - return TRUE; } static void @@ -1238,18 +1055,8 @@ xdnd_send_xevent (GdkX11DragContext *context_x11, /* We short-circuit messages to ourselves */ if (gdk_surface_get_surface_type (surface) != GDK_SURFACE_FOREIGN) { - gint i; - - for (i = 0; i < G_N_ELEMENTS (xdnd_filters); i++) - { - if (gdk_x11_get_xatom_by_name_for_display (display, xdnd_filters[i].atom_name) == - event_send->xclient.message_type) - { - (*xdnd_filters[i].func) (surface, event_send); - - return TRUE; - } - } + if (gdk_x11_drop_filter (surface, event_send)) + return TRUE; } xwindow = GDK_SURFACE_XID (surface); @@ -1483,146 +1290,6 @@ xdnd_check_dest (GdkDisplay *display, /* Target side */ -static void -xdnd_read_actions (GdkX11DragContext *context_x11) -{ - GdkDragContext *context = GDK_DRAG_CONTEXT (context_x11); - GdkDisplay *display = gdk_drag_context_get_display (context); - GdkDragAction actions = GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_ASK; - Atom type; - int format; - gulong nitems, after; - guchar *data; - Atom *atoms; - gint i; - - context_x11->xdnd_have_actions = FALSE; - - if (gdk_surface_get_surface_type (context->source_surface) == GDK_SURFACE_FOREIGN) - { - /* Get the XdndActionList, if set */ - - gdk_x11_display_error_trap_push (display); - if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), - GDK_SURFACE_XID (context->source_surface), - gdk_x11_get_xatom_by_name_for_display (display, "XdndActionList"), - 0, 65536, - False, XA_ATOM, &type, &format, &nitems, - &after, &data) == Success && - type == XA_ATOM) - { - actions = 0; - - atoms = (Atom *)data; - - for (i = 0; i < nitems; i++) - actions |= xdnd_action_from_atom (display, atoms[i]); - - context_x11->xdnd_have_actions = TRUE; - -#ifdef G_ENABLE_DEBUG - if (GDK_DISPLAY_DEBUG_CHECK (display, DND)) - { - GString *action_str = g_string_new (NULL); - GdkDragAction actions = gdk_drag_context_get_actions (context); - if (actions & GDK_ACTION_MOVE) - g_string_append(action_str, "MOVE "); - if (actions & GDK_ACTION_COPY) - g_string_append(action_str, "COPY "); - if (actions & GDK_ACTION_LINK) - g_string_append(action_str, "LINK "); - if (actions & GDK_ACTION_ASK) - g_string_append(action_str, "ASK "); - - g_message("Xdnd actions = %s", action_str->str); - g_string_free (action_str, TRUE); - } -#endif /* G_ENABLE_DEBUG */ - } - - if (data) - XFree (data); - - gdk_x11_display_error_trap_pop_ignored (display); - } - else - { - /* Local drag - */ - GdkDragContext *source_context; - - source_context = gdk_drag_context_find (display, - GDK_SURFACE_XID (context->source_surface), - GDK_SURFACE_XID (context->dest_surface)); - - if (source_context) - { - actions = gdk_drag_context_get_actions (source_context); - context_x11->xdnd_have_actions = TRUE; - } - } - - gdk_drag_context_set_actions (context, actions, gdk_drag_context_get_suggested_action (context)); -} - -/* We have to make sure that the XdndActionList we keep internally - * is up to date with the XdndActionList on the source window - * because we get no notification, because Xdnd wasn’t meant - * to continually send actions. So we select on PropertyChangeMask - * and add this filter. - */ -static gboolean -xdnd_source_surface_filter (GdkDisplay *display, - const XEvent *xevent, - gpointer data) -{ - GdkX11DragContext *context_x11 = data; - - context_x11 = data; - - if ((xevent->xany.type == PropertyNotify) && - (xevent->xany.window == GDK_SURFACE_XID (GDK_DRAG_CONTEXT (context_x11)->source_surface)) && - (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "XdndActionList"))) - { - xdnd_read_actions (context_x11); - } - - return FALSE; -} - -static void -xdnd_manage_source_filter (GdkDrop *drop, - GdkSurface *surface, - gboolean add_filter) -{ - if (!GDK_SURFACE_DESTROYED (surface) && - gdk_surface_get_surface_type (surface) == GDK_SURFACE_FOREIGN) - { - GdkDisplay *display = gdk_drop_get_display (drop); - - if (add_filter) - { - gdk_x11_display_error_trap_push (display); - gdk_surface_set_events (surface, - gdk_surface_get_events (surface) | - GDK_PROPERTY_CHANGE_MASK); - gdk_x11_display_error_trap_pop_ignored (display); - - g_signal_connect (display, "xevent", G_CALLBACK (xdnd_source_surface_filter), drop); - } - else - { - g_signal_handlers_disconnect_by_func (display, - xdnd_source_surface_filter, - drop); - /* Should we remove the GDK_PROPERTY_NOTIFY mask? - * but we might want it for other reasons. (Like - * INCR selection transactions). - */ - } - } -} - static void base_precache_atoms (GdkDisplay *display) { @@ -1674,279 +1341,6 @@ xdnd_precache_atoms (GdkDisplay *display) } } -static gboolean -xdnd_enter_filter (GdkSurface *surface, - const XEvent *xevent) -{ - GdkDisplay *display; - GdkX11Display *display_x11; - GdkDrop *drop; - GdkX11DragContext *context_x11; - GdkDragContext *drag; - GdkSeat *seat; - gint i; - Atom type; - int format; - gulong nitems, after; - guchar *data; - Atom *atoms; - GdkContentFormats *content_formats; - GPtrArray *formats; - guint32 source_surface; - gboolean get_types; - gint version; - - source_surface = xevent->xclient.data.l[0]; - get_types = ((xevent->xclient.data.l[1] & 1) != 0); - version = (xevent->xclient.data.l[1] & 0xff000000) >> 24; - - display = gdk_surface_get_display (surface); - display_x11 = GDK_X11_DISPLAY (display); - - xdnd_precache_atoms (display); - - GDK_DISPLAY_NOTE (display, DND, - g_message ("XdndEnter: source_surface: %#x, version: %#x", - source_surface, version)); - - if (version < 3) - { - /* Old source ignore */ - GDK_DISPLAY_NOTE (display, DND, g_message ("Ignored old XdndEnter message")); - return TRUE; - } - - g_clear_object (&display_x11->current_drop); - - seat = gdk_display_get_default_seat (display); - - formats = g_ptr_array_new (); - if (get_types) - { - gdk_x11_display_error_trap_push (display); - XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), - source_surface, - gdk_x11_get_xatom_by_name_for_display (display, "XdndTypeList"), - 0, 65536, - False, XA_ATOM, &type, &format, &nitems, - &after, &data); - - if (gdk_x11_display_error_trap_pop (display) || (format != 32) || (type != XA_ATOM)) - { - if (data) - XFree (data); - - return TRUE; - } - - atoms = (Atom *)data; - for (i = 0; i < nitems; i++) - g_ptr_array_add (formats, - (gpointer) gdk_x11_get_xatom_name_for_display (display, atoms[i])); - - XFree (atoms); - } - else - { - for (i = 0; i < 3; i++) - if (xevent->xclient.data.l[2 + i]) - g_ptr_array_add (formats, - (gpointer) gdk_x11_get_xatom_name_for_display (display, - xevent->xclient.data.l[2 + i])); - } - content_formats = gdk_content_formats_new ((const char **) formats->pdata, formats->len); - g_ptr_array_unref (formats); - -#ifdef G_ENABLE_DEBUG - if (GDK_DISPLAY_DEBUG_CHECK (display, DND)) - print_target_list (content_formats); -#endif /* G_ENABLE_DEBUG */ - - drag = gdk_drag_context_find (display, source_surface, GDK_SURFACE_XID (surface)); - - context_x11 = g_object_new (GDK_TYPE_X11_DRAG_CONTEXT, - "device", gdk_seat_get_pointer (seat), - "drag", drag, - "formats", content_formats, - "surface", surface, - NULL); - drop = GDK_DROP (context_x11); - - context_x11->protocol = GDK_DRAG_PROTO_XDND; - context_x11->version = version; - - /* FIXME: Should extend DnD protocol to have device info */ - - GDK_DRAG_CONTEXT (drop)->source_surface = gdk_x11_surface_foreign_new_for_display (display, source_surface); - if (!GDK_DRAG_CONTEXT (drop)->source_surface) - { - g_object_unref (drop); - return TRUE; - } - GDK_DRAG_CONTEXT (drop)->dest_surface = surface; - g_object_ref (GDK_DRAG_CONTEXT (drop)->dest_surface); - xdnd_manage_source_filter (drop, GDK_DRAG_CONTEXT (drop)->source_surface, TRUE); - xdnd_read_actions (context_x11); - - display_x11->current_drop = drop; - - gdk_drop_emit_enter_event (drop, FALSE, GDK_CURRENT_TIME); - - gdk_content_formats_unref (content_formats); - - return TRUE; -} - -static gboolean -xdnd_leave_filter (GdkSurface *surface, - const XEvent *xevent) -{ - guint32 source_surface = xevent->xclient.data.l[0]; - GdkDisplay *display; - GdkX11Display *display_x11; - - display = gdk_surface_get_display (surface); - display_x11 = GDK_X11_DISPLAY (display); - - GDK_DISPLAY_NOTE (display, DND, - g_message ("XdndLeave: source_surface: %#x", - source_surface)); - - xdnd_precache_atoms (display); - - if ((display_x11->current_drop != NULL) && - (GDK_SURFACE_XID (GDK_DRAG_CONTEXT (display_x11->current_drop)->source_surface) == source_surface)) - { - gdk_drop_emit_leave_event (display_x11->current_drop, FALSE, GDK_CURRENT_TIME); - - g_clear_object (&display_x11->current_drop); - } - - return TRUE; -} - -static gboolean -xdnd_position_filter (GdkSurface *surface, - const XEvent *xevent) -{ - GdkSurfaceImplX11 *impl; - guint32 source_surface = xevent->xclient.data.l[0]; - gint16 x_root = xevent->xclient.data.l[2] >> 16; - gint16 y_root = xevent->xclient.data.l[2] & 0xffff; - guint32 time = xevent->xclient.data.l[3]; - Atom action = xevent->xclient.data.l[4]; - GdkDisplay *display; - GdkX11Display *display_x11; - GdkDragContext *context; - GdkX11DragContext *context_x11; - GdkDragAction suggested_action; - - display = gdk_surface_get_display (surface); - display_x11 = GDK_X11_DISPLAY (display); - - GDK_DISPLAY_NOTE (display, DND, - g_message ("XdndPosition: source_surface: %#x position: (%d, %d) time: %d action: %ld", - source_surface, x_root, y_root, time, action)); - - xdnd_precache_atoms (display); - - context = GDK_DRAG_CONTEXT (display_x11->current_drop); - - if ((context != NULL) && - (GDK_SURFACE_XID (context->source_surface) == source_surface)) - { - impl = GDK_SURFACE_IMPL_X11 (gdk_drop_get_surface (GDK_DROP (context))->impl); - - context_x11 = GDK_X11_DRAG_CONTEXT (context); - - suggested_action = xdnd_action_from_atom (display, action); - if (context_x11->xdnd_have_actions) - gdk_drag_context_set_actions (context, - gdk_drag_context_get_actions (context), - suggested_action); - else - gdk_drag_context_set_actions (context, - suggested_action, - suggested_action); - - context_x11->last_x = x_root / impl->surface_scale; - context_x11->last_y = y_root / impl->surface_scale; - - gdk_drop_emit_motion_event (GDK_DROP (context), FALSE, context_x11->last_x, context_x11->last_y, time); - } - - return TRUE; -} - -static gboolean -xdnd_drop_filter (GdkSurface *surface, - const XEvent *xevent) -{ - guint32 source_surface = xevent->xclient.data.l[0]; - guint32 time = xevent->xclient.data.l[2]; - GdkDisplay *display; - GdkX11Display *display_x11; - GdkDragContext *context; - GdkX11DragContext *context_x11; - - display = gdk_surface_get_display (surface); - display_x11 = GDK_X11_DISPLAY (display); - - GDK_DISPLAY_NOTE (display, DND, - g_message ("XdndDrop: source_surface: %#x time: %d", - source_surface, time)); - - xdnd_precache_atoms (display); - - context = GDK_DRAG_CONTEXT (display_x11->current_drop); - - if ((context != NULL) && - (GDK_SURFACE_XID (context->source_surface) == source_surface)) - { - context_x11 = GDK_X11_DRAG_CONTEXT (context); - - gdk_x11_surface_set_user_time (gdk_drop_get_surface (GDK_DROP (context)), time); - - gdk_drop_emit_drop_event (GDK_DROP (context), FALSE, context_x11->last_x, context_x11->last_y, time); - } - - return TRUE; -} - -GdkFilterReturn -_gdk_x11_dnd_filter (const XEvent *xevent, - GdkEvent *event, - gpointer data) -{ - GdkDisplay *display; - int i; - - if (!GDK_IS_X11_SURFACE (event->any.surface)) - return GDK_FILTER_CONTINUE; - - if (xevent->type != ClientMessage) - return GDK_FILTER_CONTINUE; - - if (!event->any.surface || - gdk_surface_get_surface_type (event->any.surface) == GDK_SURFACE_FOREIGN) - return GDK_FILTER_CONTINUE; /* Not for us */ - - display = GDK_SURFACE_DISPLAY (event->any.surface); - - for (i = 0; i < G_N_ELEMENTS (xdnd_filters); i++) - { - if (xevent->xclient.message_type != gdk_x11_get_xatom_by_name_for_display (display, xdnd_filters[i].atom_name)) - continue; - - if (xdnd_filters[i].func (event->any.surface, xevent)) - return GDK_FILTER_REMOVE; - else - return GDK_FILTER_CONTINUE; - } - - return GDK_FILTER_CONTINUE; -} - /* Source side */ static void @@ -2171,22 +1565,13 @@ gdk_x11_drag_context_drag_motion (GdkDragContext *context, { if (dest_surface) { - if (gdk_surface_get_surface_type (dest_surface) == GDK_SURFACE_FOREIGN) - xdnd_set_actions (context_x11); - else if (context->dest_surface == dest_surface) - { - GdkDisplay *display = GDK_SURFACE_DISPLAY (dest_surface); - GdkDragContext *dest_context; - - dest_context = GDK_DRAG_CONTEXT (GDK_X11_DISPLAY (display)->current_drop); + GdkDisplay *display = GDK_SURFACE_DISPLAY (dest_surface); + GdkDrop *drop = GDK_X11_DISPLAY (display)->current_drop; - if (dest_context && - dest_context->dest_surface == dest_surface) - { - gdk_drag_context_set_actions (dest_context, possible_actions, suggested_action); - GDK_X11_DRAG_CONTEXT (dest_context)->xdnd_have_actions = TRUE; - } - } + if (drop && gdk_drop_get_surface (drop) == dest_surface) + gdk_x11_drop_read_actions (drop); + else + xdnd_set_actions (context_x11); } } @@ -2320,117 +1705,6 @@ gdk_x11_drag_context_drag_drop (GdkDragContext *context, /* Destination side */ -static void -gdk_x11_drop_do_nothing (Window window, - gboolean success, - gpointer data) -{ - GdkDisplay *display = data; - - if (!success) - { - GDK_DISPLAY_NOTE (display, DND, - g_message ("Send event to %lx failed", - window)); - } -} - -static void -gdk_x11_drag_context_status (GdkDrop *drop, - GdkDragAction actions) -{ - GdkDragContext *context = GDK_DRAG_CONTEXT (drop); - GdkDragAction possible_actions; - XEvent xev; - GdkDisplay *display; - - display = gdk_drag_context_get_display (context); - - context->action = actions; - possible_actions = actions & gdk_drop_get_actions (drop); - - xev.xclient.type = ClientMessage; - xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndStatus"); - xev.xclient.format = 32; - xev.xclient.window = GDK_SURFACE_XID (context->source_surface); - - xev.xclient.data.l[0] = GDK_SURFACE_XID (context->dest_surface); - xev.xclient.data.l[1] = (possible_actions != 0) ? (2 | 1) : 0; - xev.xclient.data.l[2] = 0; - xev.xclient.data.l[3] = 0; - xev.xclient.data.l[4] = xdnd_action_to_atom (display, possible_actions); - - if (gdk_drop_get_drag (drop)) - { - xdnd_status_filter (display, &xev); - } - else - { - _gdk_x11_send_client_message_async (display, - GDK_SURFACE_XID (context->source_surface), - FALSE, 0, - &xev.xclient, - gdk_x11_drop_do_nothing, - display); - } -} - -static void -gdk_x11_drag_context_finish (GdkDrop *drop, - GdkDragAction action) -{ - if (GDK_X11_DRAG_CONTEXT (drop)->protocol == GDK_DRAG_PROTO_XDND) - { - GdkDragContext *context = GDK_DRAG_CONTEXT (drop); - GdkDisplay *display = gdk_drop_get_display (drop); - XEvent xev; - - if (action == GDK_ACTION_MOVE) - { - XConvertSelection (GDK_DISPLAY_XDISPLAY (display), - gdk_x11_get_xatom_by_name_for_display (display, "XdndSelection"), - gdk_x11_get_xatom_by_name_for_display (display, "DELETE"), - gdk_x11_get_xatom_by_name_for_display (display, "GDK_SELECTION"), - GDK_SURFACE_XID (context->source_surface), - GDK_X11_DRAG_CONTEXT (drop)->timestamp); - /* XXX: Do we need to wait for a reply here before sending the next message? */ - } - - xev.xclient.type = ClientMessage; - xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndFinished"); - xev.xclient.format = 32; - xev.xclient.window = GDK_SURFACE_XID (context->source_surface); - - xev.xclient.data.l[0] = GDK_SURFACE_XID (context->dest_surface); - if (action != 0) - { - xev.xclient.data.l[1] = 1; - xev.xclient.data.l[2] = xdnd_action_to_atom (display, action); - } - else - { - xev.xclient.data.l[1] = 0; - xev.xclient.data.l[2] = None; - } - xev.xclient.data.l[3] = 0; - xev.xclient.data.l[4] = 0; - - if (gdk_drop_get_drag (drop)) - { - xdnd_finished_filter (display, &xev); - } - else - { - _gdk_x11_send_client_message_async (display, - GDK_SURFACE_XID (context->source_surface), - FALSE, 0, - &xev.xclient, - gdk_x11_drop_do_nothing, - display); - } - } -} - void _gdk_x11_surface_register_dnd (GdkSurface *surface) { @@ -2579,11 +1853,12 @@ gdk_x11_drag_context_xevent (GdkDisplay *display, case ClientMessage: if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "XdndStatus")) - return xdnd_status_filter (display, xevent); + gdk_x11_drag_context_handle_status (display, xevent); else if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "XdndFinished")) - return xdnd_finished_filter (display, xevent); + gdk_x11_drag_context_handle_finished (display, xevent); else return FALSE; + return TRUE; default: return FALSE; diff --git a/gdk/x11/gdkdrop-x11.c b/gdk/x11/gdkdrop-x11.c new file mode 100644 index 0000000000..6aabc9ef35 --- /dev/null +++ b/gdk/x11/gdkdrop-x11.c @@ -0,0 +1,877 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1999 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include "config.h" + +#include "gdkx11dnd.h" + +#include "gdk-private.h" +#include "gdkasync.h" +#include "gdkclipboardprivate.h" +#include "gdkclipboard-x11.h" +#include "gdkdeviceprivate.h" +#include "gdkdisplay-x11.h" +#include "gdkdndprivate.h" +#include "gdkinternals.h" +#include "gdkintl.h" +#include "gdkproperty.h" +#include "gdkprivate-x11.h" +#include "gdkscreen-x11.h" +#include "gdkselectioninputstream-x11.h" +#include "gdkselectionoutputstream-x11.h" + +#include +#include +#include +#include +#ifdef HAVE_XCOMPOSITE +#include +#endif + +#include + +#define GDK_TYPE_X11_DROP (gdk_x11_drop_get_type()) +G_DECLARE_FINAL_TYPE (GdkX11Drop, gdk_x11_drop, GDK, X11_DROP, GdkDrop) + +struct _GdkX11Drop +{ + GdkDrop parent_instance; + + GdkSurface *source_surface; + + guint16 last_x; /* Coordinates from last event */ + guint16 last_y; + gulong timestamp; /* Timestamp we claimed the DND selection with */ + guint version; /* Xdnd protocol version */ + + GdkDragAction xdnd_actions; /* What is currently set in XdndActionList */ + GdkDragAction suggested_action; + + guint xdnd_targets_set : 1; /* Whether we've already set XdndTypeList */ + guint xdnd_have_actions : 1; /* Whether an XdndActionList was provided */ +}; + +struct _GdkX11DropClass +{ + GdkDropClass parent_class; +}; + +/* Forward declarations */ + +static gboolean xdnd_enter_filter (GdkSurface *surface, + const XEvent *xevent); +static gboolean xdnd_leave_filter (GdkSurface *surface, + const XEvent *xevent); +static gboolean xdnd_position_filter (GdkSurface *surface, + const XEvent *xevent); +static gboolean xdnd_drop_filter (GdkSurface *surface, + const XEvent *xevent); + +static void xdnd_manage_source_filter (GdkDrop *drop, + GdkSurface *surface, + gboolean add_filter); + +static const struct { + const char *atom_name; + gboolean (* func) (GdkSurface *surface, const XEvent *event); +} xdnd_filters[] = { + { "XdndEnter", xdnd_enter_filter }, + { "XdndLeave", xdnd_leave_filter }, + { "XdndPosition", xdnd_position_filter }, + { "XdndDrop", xdnd_drop_filter }, +}; + +G_DEFINE_TYPE (GdkX11Drop, gdk_x11_drop, GDK_TYPE_DROP) + +static void +gdk_x11_drop_read_got_stream (GObject *source, + GAsyncResult *res, + gpointer data) +{ + GTask *task = data; + GError *error = NULL; + GInputStream *stream; + const char *type; + int format; + + stream = gdk_x11_selection_input_stream_new_finish (res, &type, &format, &error); + if (stream == NULL) + { + GSList *targets, *next; + + targets = g_task_get_task_data (task); + next = targets->next; + if (next) + { + GdkDrop *drop = GDK_DROP (g_task_get_source_object (task)); + + GDK_DISPLAY_NOTE (gdk_drop_get_display (drop), DND, g_printerr ("reading %s failed, trying %s next\n", + (char *) targets->data, (char *) next->data)); + targets->next = NULL; + g_task_set_task_data (task, next, (GDestroyNotify) g_slist_free); + gdk_x11_selection_input_stream_new_async (gdk_drop_get_display (drop), + "XdndSelection", + next->data, + CurrentTime, + g_task_get_priority (task), + g_task_get_cancellable (task), + gdk_x11_drop_read_got_stream, + task); + g_error_free (error); + return; + } + + g_task_return_error (task, error); + } + else + { + const char *mime_type = ((GSList *) g_task_get_task_data (task))->data; +#if 0 + gsize i; + + for (i = 0; i < G_N_ELEMENTS (special_targets); i++) + { + if (g_str_equal (mime_type, special_targets[i].x_target)) + { + g_assert (special_targets[i].mime_type != NULL); + + GDK_DISPLAY_NOTE (CLIPBOARD, g_printerr ("%s: reading with converter from %s to %s\n", + cb->selection, mime_type, special_targets[i].mime_type)); + mime_type = g_intern_string (special_targets[i].mime_type); + g_task_set_task_data (task, g_slist_prepend (NULL, (gpointer) mime_type), (GDestroyNotify) g_slist_free); + stream = special_targets[i].convert (cb, stream, type, format); + break; + } + } +#endif + + GDK_NOTE (DND, g_printerr ("reading DND as %s now\n", + mime_type)); + g_task_return_pointer (task, stream, g_object_unref); + } + + g_object_unref (task); +} + +static void +gdk_x11_drop_read_async (GdkDrop *drop, + GdkContentFormats *formats, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSList *targets; + GTask *task; + + task = g_task_new (drop, cancellable, callback, user_data); + g_task_set_priority (task, io_priority); + g_task_set_source_tag (task, gdk_x11_drop_read_async); + + targets = gdk_x11_clipboard_formats_to_targets (formats); + g_task_set_task_data (task, targets, (GDestroyNotify) g_slist_free); + if (targets == NULL) + { + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + _("No compatible transfer format found")); + return; + } + + GDK_DISPLAY_NOTE (gdk_drop_get_display (drop), DND, g_printerr ("new read for %s (%u other options)\n", + (char *) targets->data, g_slist_length (targets->next))); + gdk_x11_selection_input_stream_new_async (gdk_drop_get_display (drop), + "XdndSelection", + targets->data, + CurrentTime, + io_priority, + cancellable, + gdk_x11_drop_read_got_stream, + task); +} + +static GInputStream * +gdk_x11_drop_read_finish (GdkDrop *drop, + const char **out_mime_type, + GAsyncResult *result, + GError **error) +{ + GTask *task; + + g_return_val_if_fail (g_task_is_valid (result, G_OBJECT (drop)), NULL); + task = G_TASK (result); + g_return_val_if_fail (g_task_get_source_tag (task) == gdk_x11_drop_read_async, NULL); + + if (out_mime_type) + { + GSList *targets; + + targets = g_task_get_task_data (task); + *out_mime_type = targets ? targets->data : NULL; + } + + return g_task_propagate_pointer (task, error); +} + +static void +gdk_x11_drop_finalize (GObject *object) +{ + GdkX11Drop *drop_x11 = GDK_X11_DROP (object); + + if (drop_x11->source_surface) + { + xdnd_manage_source_filter (GDK_DROP (drop_x11), drop_x11->source_surface, FALSE); + } + + G_OBJECT_CLASS (gdk_x11_drop_parent_class)->finalize (object); +} + +/* Utility functions */ + +#ifdef G_ENABLE_DEBUG +static void +print_target_list (GdkContentFormats *formats) +{ + gchar *name = gdk_content_formats_to_string (formats); + g_message ("DND formats: %s", name); + g_free (name); +} +#endif /* G_ENABLE_DEBUG */ + +/************************************************************* + ***************************** XDND ************************** + *************************************************************/ + +/* Utility functions */ + +static struct { + const gchar *name; + GdkDragAction action; +} xdnd_actions_table[] = { + { "XdndActionCopy", GDK_ACTION_COPY }, + { "XdndActionMove", GDK_ACTION_MOVE }, + { "XdndActionLink", GDK_ACTION_LINK }, + { "XdndActionAsk", GDK_ACTION_ASK }, + { "XdndActionPrivate", GDK_ACTION_COPY }, + }; + +static const gint xdnd_n_actions = G_N_ELEMENTS (xdnd_actions_table); + +static GdkDragAction +xdnd_action_from_atom (GdkDisplay *display, + Atom xatom) +{ + const char *name; + gint i; + + if (xatom == None) + return 0; + + name = gdk_x11_get_xatom_name_for_display (display, xatom); + + for (i = 0; i < xdnd_n_actions; i++) + if (g_str_equal (name, xdnd_actions_table[i].name)) + return xdnd_actions_table[i].action; + + return 0; +} + +static Atom +xdnd_action_to_atom (GdkDisplay *display, + GdkDragAction action) +{ + gint i; + + for (i = 0; i < xdnd_n_actions; i++) + if (action == xdnd_actions_table[i].action) + return gdk_x11_get_xatom_by_name_for_display (display, xdnd_actions_table[i].name); + + return None; +} + +/* Target side */ + +static void +gdk_x11_drop_update_actions (GdkX11Drop *drop_x11) +{ + GdkDragAction actions; + + if (!drop_x11->xdnd_have_actions) + actions = drop_x11->suggested_action; + else if (drop_x11->suggested_action & GDK_ACTION_ASK) + actions = drop_x11->xdnd_actions & GDK_ACTION_ALL; + else + actions = drop_x11->suggested_action; + + gdk_drop_set_actions (GDK_DROP (drop_x11), actions); +} + +void +gdk_x11_drop_read_actions (GdkDrop *drop) +{ + GdkX11Drop *drop_x11 = GDK_X11_DROP (drop); + GdkDisplay *display = gdk_drop_get_display (drop); + GdkDragContext *drag; + GdkDragAction actions = GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_ASK; + Atom type; + int format; + gulong nitems, after; + guchar *data; + Atom *atoms; + gint i; + + drag = gdk_drop_get_drag (drop); + + drop_x11->xdnd_have_actions = FALSE; + + if (drag == NULL) + { + /* Get the XdndActionList, if set */ + + gdk_x11_display_error_trap_push (display); + if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), + GDK_SURFACE_XID (drop_x11->source_surface), + gdk_x11_get_xatom_by_name_for_display (display, "XdndActionList"), + 0, 65536, + False, XA_ATOM, &type, &format, &nitems, + &after, &data) == Success && + type == XA_ATOM) + { + actions = 0; + + atoms = (Atom *)data; + + for (i = 0; i < nitems; i++) + actions |= xdnd_action_from_atom (display, atoms[i]); + + drop_x11->xdnd_have_actions = TRUE; + +#ifdef G_ENABLE_DEBUG + if (GDK_DISPLAY_DEBUG_CHECK (display, DND)) + { + GString *action_str = g_string_new (NULL); + GdkDragAction actions = gdk_drop_get_actions (drop); + if (actions & GDK_ACTION_MOVE) + g_string_append(action_str, "MOVE "); + if (actions & GDK_ACTION_COPY) + g_string_append(action_str, "COPY "); + if (actions & GDK_ACTION_LINK) + g_string_append(action_str, "LINK "); + if (actions & GDK_ACTION_ASK) + g_string_append(action_str, "ASK "); + + g_message("Xdnd actions = %s", action_str->str); + g_string_free (action_str, TRUE); + } +#endif /* G_ENABLE_DEBUG */ + } + + if (data) + XFree (data); + + gdk_x11_display_error_trap_pop_ignored (display); + } + else + { + actions = gdk_drag_context_get_actions (drag); + drop_x11->xdnd_have_actions = TRUE; + } + + drop_x11->xdnd_actions = actions; + gdk_x11_drop_update_actions (drop_x11); +} + +/* We have to make sure that the XdndActionList we keep internally + * is up to date with the XdndActionList on the source window + * because we get no notification, because Xdnd wasn’t meant + * to continually send actions. So we select on PropertyChangeMask + * and add this filter. + */ +static gboolean +xdnd_source_surface_filter (GdkDisplay *display, + const XEvent *xevent, + gpointer data) +{ + GdkX11Drop *drop_x11 = data; + + if ((xevent->xany.type == PropertyNotify) && + (xevent->xany.window == GDK_SURFACE_XID (drop_x11->source_surface)) && + (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "XdndActionList"))) + { + gdk_x11_drop_read_actions (GDK_DROP (drop_x11)); + } + + return FALSE; +} + +static void +xdnd_manage_source_filter (GdkDrop *drop, + GdkSurface *surface, + gboolean add_filter) +{ + if (!GDK_SURFACE_DESTROYED (surface) && + gdk_surface_get_surface_type (surface) == GDK_SURFACE_FOREIGN) + { + GdkDisplay *display = gdk_drop_get_display (drop); + + if (add_filter) + { + gdk_x11_display_error_trap_push (display); + gdk_surface_set_events (surface, + gdk_surface_get_events (surface) | + GDK_PROPERTY_CHANGE_MASK); + gdk_x11_display_error_trap_pop_ignored (display); + + g_signal_connect (display, "xevent", G_CALLBACK (xdnd_source_surface_filter), drop); + } + else + { + g_signal_handlers_disconnect_by_func (display, + xdnd_source_surface_filter, + drop); + /* Should we remove the GDK_PROPERTY_NOTIFY mask? + * but we might want it for other reasons. (Like + * INCR selection transactions). + */ + } + } +} + +static void +xdnd_precache_atoms (GdkDisplay *display) +{ + GdkX11Display *display_x11 = GDK_X11_DISPLAY (display); + + if (!display_x11->xdnd_atoms_precached) + { + static const gchar *const precache_atoms[] = { + "XdndActionAsk", + "XdndActionCopy", + "XdndActionLink", + "XdndActionList", + "XdndActionMove", + "XdndActionPrivate", + "XdndDrop", + "XdndEnter", + "XdndFinished", + "XdndLeave", + "XdndPosition", + "XdndSelection", + "XdndStatus", + "XdndTypeList" + }; + + _gdk_x11_precache_atoms (display, + precache_atoms, G_N_ELEMENTS (precache_atoms)); + + display_x11->xdnd_atoms_precached = TRUE; + } +} + +static gboolean +xdnd_enter_filter (GdkSurface *surface, + const XEvent *xevent) +{ + GdkDisplay *display; + GdkX11Display *display_x11; + GdkDrop *drop; + GdkX11Drop *drop_x11; + GdkDragContext *drag; + GdkSeat *seat; + gint i; + Atom type; + int format; + gulong nitems, after; + guchar *data; + Atom *atoms; + GdkContentFormats *content_formats; + GPtrArray *formats; + guint32 source_surface; + gboolean get_types; + gint version; + + source_surface = xevent->xclient.data.l[0]; + get_types = ((xevent->xclient.data.l[1] & 1) != 0); + version = (xevent->xclient.data.l[1] & 0xff000000) >> 24; + + display = gdk_surface_get_display (surface); + display_x11 = GDK_X11_DISPLAY (display); + + xdnd_precache_atoms (display); + + GDK_DISPLAY_NOTE (display, DND, + g_message ("XdndEnter: source_surface: %#x, version: %#x", + source_surface, version)); + + if (version < 3) + { + /* Old source ignore */ + GDK_DISPLAY_NOTE (display, DND, g_message ("Ignored old XdndEnter message")); + return TRUE; + } + + g_clear_object (&display_x11->current_drop); + + seat = gdk_display_get_default_seat (display); + + formats = g_ptr_array_new (); + if (get_types) + { + gdk_x11_display_error_trap_push (display); + XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), + source_surface, + gdk_x11_get_xatom_by_name_for_display (display, "XdndTypeList"), + 0, 65536, + False, XA_ATOM, &type, &format, &nitems, + &after, &data); + + if (gdk_x11_display_error_trap_pop (display) || (format != 32) || (type != XA_ATOM)) + { + if (data) + XFree (data); + + return TRUE; + } + + atoms = (Atom *)data; + for (i = 0; i < nitems; i++) + g_ptr_array_add (formats, + (gpointer) gdk_x11_get_xatom_name_for_display (display, atoms[i])); + + XFree (atoms); + } + else + { + for (i = 0; i < 3; i++) + if (xevent->xclient.data.l[2 + i]) + g_ptr_array_add (formats, + (gpointer) gdk_x11_get_xatom_name_for_display (display, + xevent->xclient.data.l[2 + i])); + } + content_formats = gdk_content_formats_new ((const char **) formats->pdata, formats->len); + g_ptr_array_unref (formats); + +#ifdef G_ENABLE_DEBUG + if (GDK_DISPLAY_DEBUG_CHECK (display, DND)) + print_target_list (content_formats); +#endif /* G_ENABLE_DEBUG */ + + drag = gdk_x11_drag_context_find (display, source_surface, GDK_SURFACE_XID (surface)); + + drop_x11 = g_object_new (GDK_TYPE_X11_DROP, + "device", gdk_seat_get_pointer (seat), + "drag", drag, + "formats", content_formats, + "surface", surface, + NULL); + drop = GDK_DROP (drop_x11); + + drop_x11->version = version; + + /* FIXME: Should extend DnD protocol to have device info */ + + drop_x11->source_surface = gdk_x11_surface_foreign_new_for_display (display, source_surface); + if (!drop_x11->source_surface) + { + g_object_unref (drop); + return TRUE; + } + xdnd_manage_source_filter (drop, drop_x11->source_surface, TRUE); + gdk_x11_drop_read_actions (drop); + + display_x11->current_drop = drop; + + gdk_drop_emit_enter_event (drop, FALSE, GDK_CURRENT_TIME); + + gdk_content_formats_unref (content_formats); + + return TRUE; +} + +static gboolean +xdnd_leave_filter (GdkSurface *surface, + const XEvent *xevent) +{ + guint32 source_surface = xevent->xclient.data.l[0]; + GdkDisplay *display; + GdkX11Display *display_x11; + + display = gdk_surface_get_display (surface); + display_x11 = GDK_X11_DISPLAY (display); + + GDK_DISPLAY_NOTE (display, DND, + g_message ("XdndLeave: source_surface: %#x", + source_surface)); + + xdnd_precache_atoms (display); + + if ((display_x11->current_drop != NULL) && + (GDK_SURFACE_XID (GDK_X11_DROP (display_x11->current_drop)->source_surface) == source_surface)) + { + gdk_drop_emit_leave_event (display_x11->current_drop, FALSE, GDK_CURRENT_TIME); + + g_clear_object (&display_x11->current_drop); + } + + return TRUE; +} + +static gboolean +xdnd_position_filter (GdkSurface *surface, + const XEvent *xevent) +{ + GdkSurfaceImplX11 *impl; + guint32 source_surface = xevent->xclient.data.l[0]; + gint16 x_root = xevent->xclient.data.l[2] >> 16; + gint16 y_root = xevent->xclient.data.l[2] & 0xffff; + guint32 time = xevent->xclient.data.l[3]; + Atom action = xevent->xclient.data.l[4]; + GdkDisplay *display; + GdkX11Display *display_x11; + GdkDrop *drop; + GdkX11Drop *drop_x11; + + display = gdk_surface_get_display (surface); + display_x11 = GDK_X11_DISPLAY (display); + + GDK_DISPLAY_NOTE (display, DND, + g_message ("XdndPosition: source_surface: %#x position: (%d, %d) time: %d action: %ld", + source_surface, x_root, y_root, time, action)); + + xdnd_precache_atoms (display); + + drop = display_x11->current_drop; + drop_x11 = GDK_X11_DROP (drop); + + if ((drop != NULL) && + (GDK_SURFACE_XID (drop_x11->source_surface) == source_surface)) + { + impl = GDK_SURFACE_IMPL_X11 (gdk_drop_get_surface (drop)->impl); + + drop_x11->suggested_action = xdnd_action_from_atom (display, action); + gdk_x11_drop_update_actions (drop_x11); + + drop_x11->last_x = x_root / impl->surface_scale; + drop_x11->last_y = y_root / impl->surface_scale; + + gdk_drop_emit_motion_event (drop, FALSE, drop_x11->last_x, drop_x11->last_y, time); + } + + return TRUE; +} + +static gboolean +xdnd_drop_filter (GdkSurface *surface, + const XEvent *xevent) +{ + guint32 source_surface = xevent->xclient.data.l[0]; + guint32 time = xevent->xclient.data.l[2]; + GdkDisplay *display; + GdkX11Display *display_x11; + GdkDrop *drop; + GdkX11Drop *drop_x11; + + display = gdk_surface_get_display (surface); + display_x11 = GDK_X11_DISPLAY (display); + + GDK_DISPLAY_NOTE (display, DND, + g_message ("XdndDrop: source_surface: %#x time: %d", + source_surface, time)); + + xdnd_precache_atoms (display); + + drop = display_x11->current_drop; + drop_x11 = GDK_X11_DROP (drop); + + if ((drop != NULL) && + (GDK_SURFACE_XID (drop_x11->source_surface) == source_surface)) + { + gdk_x11_surface_set_user_time (gdk_drop_get_surface (drop), time); + + gdk_drop_emit_drop_event (drop, FALSE, drop_x11->last_x, drop_x11->last_y, time); + } + + return TRUE; +} + +gboolean +gdk_x11_drop_filter (GdkSurface *surface, + const XEvent *xevent) + +{ + GdkDisplay *display; + int i; + + if (!GDK_IS_X11_SURFACE (surface)) + return GDK_FILTER_CONTINUE; + + if (xevent->type != ClientMessage) + return GDK_FILTER_CONTINUE; + + if (gdk_surface_get_surface_type (surface) == GDK_SURFACE_FOREIGN) + return GDK_FILTER_CONTINUE; /* Not for us */ + + display = GDK_SURFACE_DISPLAY (surface); + + for (i = 0; i < G_N_ELEMENTS (xdnd_filters); i++) + { + if (xevent->xclient.message_type != gdk_x11_get_xatom_by_name_for_display (display, xdnd_filters[i].atom_name)) + continue; + + if (xdnd_filters[i].func (surface, xevent)) + return TRUE; + else + return FALSE; + } + + return FALSE; +} + +/* Destination side */ + +static void +gdk_x11_drop_do_nothing (Window window, + gboolean success, + gpointer data) +{ + GdkDisplay *display = data; + + if (!success) + { + GDK_DISPLAY_NOTE (display, DND, + g_message ("Send event to %lx failed", + window)); + } +} + +static void +gdk_x11_drop_status (GdkDrop *drop, + GdkDragAction actions) +{ + GdkX11Drop *drop_x11 = GDK_X11_DROP (drop); + GdkDragAction possible_actions; + XEvent xev; + GdkDisplay *display; + + display = gdk_drop_get_display (drop); + + possible_actions = actions & gdk_drop_get_actions (drop); + + xev.xclient.type = ClientMessage; + xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndStatus"); + xev.xclient.format = 32; + xev.xclient.window = GDK_SURFACE_XID (drop_x11->source_surface); + + xev.xclient.data.l[0] = GDK_SURFACE_XID (gdk_drop_get_surface (drop)); + xev.xclient.data.l[1] = (possible_actions != 0) ? (2 | 1) : 0; + xev.xclient.data.l[2] = 0; + xev.xclient.data.l[3] = 0; + xev.xclient.data.l[4] = xdnd_action_to_atom (display, possible_actions); + + if (gdk_drop_get_drag (drop)) + { + gdk_x11_drag_context_handle_status (display, &xev); + } + else + { + _gdk_x11_send_client_message_async (display, + GDK_SURFACE_XID (drop_x11->source_surface), + FALSE, 0, + &xev.xclient, + gdk_x11_drop_do_nothing, + display); + } +} + +static void +gdk_x11_drop_finish (GdkDrop *drop, + GdkDragAction action) +{ + GdkX11Drop *drop_x11 = GDK_X11_DROP (drop); + GdkDisplay *display = gdk_drop_get_display (drop); + XEvent xev; + + if (action == GDK_ACTION_MOVE) + { + XConvertSelection (GDK_DISPLAY_XDISPLAY (display), + gdk_x11_get_xatom_by_name_for_display (display, "XdndSelection"), + gdk_x11_get_xatom_by_name_for_display (display, "DELETE"), + gdk_x11_get_xatom_by_name_for_display (display, "GDK_SELECTION"), + GDK_SURFACE_XID (drop_x11->source_surface), + GDK_X11_DROP (drop)->timestamp); + /* XXX: Do we need to wait for a reply here before sending the next message? */ + } + + xev.xclient.type = ClientMessage; + xev.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "XdndFinished"); + xev.xclient.format = 32; + xev.xclient.window = GDK_SURFACE_XID (drop_x11->source_surface); + + xev.xclient.data.l[0] = GDK_SURFACE_XID (gdk_drop_get_surface (drop)); + if (action != 0) + { + xev.xclient.data.l[1] = 1; + xev.xclient.data.l[2] = xdnd_action_to_atom (display, action); + } + else + { + xev.xclient.data.l[1] = 0; + xev.xclient.data.l[2] = None; + } + xev.xclient.data.l[3] = 0; + xev.xclient.data.l[4] = 0; + + if (gdk_drop_get_drag (drop)) + { + gdk_x11_drag_context_handle_status (display, &xev); + } + else + { + _gdk_x11_send_client_message_async (display, + GDK_SURFACE_XID (drop_x11->source_surface), + FALSE, 0, + &xev.xclient, + gdk_x11_drop_do_nothing, + display); + } +} + +static void +gdk_x11_drop_class_init (GdkX11DropClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GdkDropClass *drop_class = GDK_DROP_CLASS (klass); + + object_class->finalize = gdk_x11_drop_finalize; + + drop_class->status = gdk_x11_drop_status; + drop_class->finish = gdk_x11_drop_finish; + drop_class->read_async = gdk_x11_drop_read_async; + drop_class->read_finish = gdk_x11_drop_read_finish; +} + +static void +gdk_x11_drop_init (GdkX11Drop *context) +{ +} + diff --git a/gdk/x11/gdkeventsource.c b/gdk/x11/gdkeventsource.c index f1c55bcbd5..5f85a0b7b0 100644 --- a/gdk/x11/gdkeventsource.c +++ b/gdk/x11/gdkeventsource.c @@ -265,8 +265,9 @@ gdk_event_source_translate_event (GdkX11Display *x11_display, if (result == GDK_FILTER_CONTINUE) result = _gdk_wm_protocols_filter (xevent, event, NULL); - if (result == GDK_FILTER_CONTINUE) - result = _gdk_x11_dnd_filter (xevent, event, NULL); + if (result == GDK_FILTER_CONTINUE && + gdk_x11_drop_filter (event->any.surface, xevent)) + result = GDK_FILTER_REMOVE; if (result != GDK_FILTER_CONTINUE) { diff --git a/gdk/x11/gdkprivate-x11.h b/gdk/x11/gdkprivate-x11.h index e6bfd2bd82..e9014ee8b1 100644 --- a/gdk/x11/gdkprivate-x11.h +++ b/gdk/x11/gdkprivate-x11.h @@ -197,10 +197,16 @@ Atom _gdk_x11_get_xatom_for_display_printf (GdkDisplay *display, const gchar *format, ...) G_GNUC_PRINTF (2, 3); -GdkFilterReturn -_gdk_x11_dnd_filter (const XEvent *xevent, - GdkEvent *event, - gpointer data); +GdkDragContext *gdk_x11_drag_context_find (GdkDisplay *display, + Window source_xid, + Window dest_xid); +void gdk_x11_drag_context_handle_status (GdkDisplay *display, + const XEvent *xevent); +void gdk_x11_drag_context_handle_finished (GdkDisplay *display, + const XEvent *xevent); +void gdk_x11_drop_read_actions (GdkDrop *drop); +gboolean gdk_x11_drop_filter (GdkSurface *surface, + const XEvent *xevent); typedef struct _GdkSurfaceCache GdkSurfaceCache; diff --git a/gdk/x11/meson.build b/gdk/x11/meson.build index 38df0746eb..69c4ab2b47 100644 --- a/gdk/x11/meson.build +++ b/gdk/x11/meson.build @@ -12,6 +12,7 @@ gdk_x11_sources = files([ 'gdkdevicemanager-xi2.c', 'gdkdisplay-x11.c', 'gdkdnd-x11.c', + 'gdkdrop-x11.c', 'gdkeventsource.c', 'gdkeventtranslator.c', 'gdkglcontext-x11.c',