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.
This commit is contained in:
@@ -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)
|
||||
GdkDisplay *display = GDK_SURFACE_DISPLAY (dest_surface);
|
||||
GdkDrop *drop = GDK_X11_DISPLAY (display)->current_drop;
|
||||
|
||||
if (drop && gdk_drop_get_surface (drop) == dest_surface)
|
||||
gdk_x11_drop_read_actions (drop);
|
||||
else
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
877
gdk/x11/gdkdrop-x11.c
Normal file
877
gdk/x11/gdkdrop-x11.c
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/extensions/shape.h>
|
||||
#ifdef HAVE_XCOMPOSITE
|
||||
#include <X11/extensions/Xcomposite.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#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)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user