[gtk+] dnd: Introduce gdk_drop_read_async() and use it
- From: Benjamin Otte <otte src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+] dnd: Introduce gdk_drop_read_async() and use it
- Date: Sun, 10 Dec 2017 01:00:00 +0000 (UTC)
commit 803cbd576f6597f4b851c6f1b7ded98691525e5a
Author: Benjamin Otte <otte redhat com>
Date: Sun Dec 10 01:05:37 2017 +0100
dnd: Introduce gdk_drop_read_async() and use it
This is the replacement for selection usage.
Backend implementations for X11 (missing support for backwards compat
formats like COMPOUND_TEXT) and Wayland are included.
GTK code should be adapted to use gdk_drop_read_*() functions instead
of gtk_drag_get_data().
gdk/gdkdnd.c | 80 +++++++++++++++++++++++
gdk/gdkdnd.h | 13 ++++
gdk/gdkdndprivate.h | 10 +++
gdk/wayland/gdkdnd-wayland.c | 81 +++++++++++++++++++++++
gdk/x11/gdkclipboard-x11.c | 2 +-
gdk/x11/gdkclipboard-x11.h | 1 +
gdk/x11/gdkdnd-x11.c | 144 ++++++++++++++++++++++++++++++++++++++++--
gtk/gtkdnd.c | 137 +++++++++++++++++++++++++++++++++++-----
8 files changed, 447 insertions(+), 21 deletions(-)
---
diff --git a/gdk/gdkdnd.c b/gdk/gdkdnd.c
index ead7bdf..577ff73 100644
--- a/gdk/gdkdnd.c
+++ b/gdk/gdkdnd.c
@@ -310,6 +310,40 @@ gdk_drag_context_finalize (GObject *object)
}
static void
+gdk_drag_context_read_local_async (GdkDragContext *context,
+ GdkContentFormats *formats,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (context, cancellable, callback, user_data);
+ g_task_set_priority (task, io_priority);
+ g_task_set_source_tag (task, gdk_drag_context_read_local_async);
+
+ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("Reading not implemented."));
+ g_object_unref (task);
+}
+
+static GInputStream *
+gdk_drag_context_read_local_finish (GdkDragContext *context,
+ const char **out_mime_type,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (g_task_is_valid (result, context), NULL);
+ g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gdk_drag_context_read_local_async, NULL);
+
+ if (out_mime_type)
+ *out_mime_type = g_task_get_task_data (G_TASK (result));
+
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+static void
gdk_drag_context_class_init (GdkDragContextClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -637,6 +671,52 @@ gdk_drag_get_selection (GdkDragContext *context)
return GDK_DRAG_CONTEXT_GET_CLASS (context)->get_selection (context);
}
+void
+gdk_drop_read_async (GdkDragContext *context,
+ const char **mime_types,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GdkContentFormats *formats;
+
+ g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
+ g_return_if_fail (mime_types != NULL && mime_types[0] != NULL);
+ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+ g_return_if_fail (callback != NULL);
+
+ formats = gdk_content_formats_new (mime_types, g_strv_length ((char **) mime_types));
+
+ GDK_DRAG_CONTEXT_GET_CLASS (context)->read_async (context,
+ formats,
+ io_priority,
+ cancellable,
+ callback,
+ user_data);
+
+ gdk_content_formats_unref (formats);
+}
+
+GInputStream *
+gdk_drop_read_finish (GdkDragContext *context,
+ const char **out_mime_type,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ if (g_async_result_is_tagged (result, gdk_drag_context_read_local_async))
+ {
+ return gdk_drag_context_read_local_finish (context, out_mime_type, result, error);
+ }
+ else
+ {
+ return GDK_DRAG_CONTEXT_GET_CLASS (context)->read_finish (context, out_mime_type, result, error);
+ }
+}
+
/**
* gdk_drag_context_get_drag_window:
* @context: a #GdkDragContext
diff --git a/gdk/gdkdnd.h b/gdk/gdkdnd.h
index 85f8fa0..70c69e1 100644
--- a/gdk/gdkdnd.h
+++ b/gdk/gdkdnd.h
@@ -121,6 +121,19 @@ void gdk_drop_finish (GdkDragContext *context,
GDK_AVAILABLE_IN_ALL
GdkAtom gdk_drag_get_selection (GdkDragContext *context);
+GDK_AVAILABLE_IN_3_94
+void gdk_drop_read_async (GdkDragContext *context,
+ const char **mime_types,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GDK_AVAILABLE_IN_3_94
+GInputStream * gdk_drop_read_finish (GdkDragContext *context,
+ const char **out_mime_type,
+ GAsyncResult *result,
+ GError **error);
+
/* Source side */
GDK_AVAILABLE_IN_ALL
diff --git a/gdk/gdkdndprivate.h b/gdk/gdkdndprivate.h
index 78ae19b..e44e49b 100644
--- a/gdk/gdkdndprivate.h
+++ b/gdk/gdkdndprivate.h
@@ -87,6 +87,16 @@ struct _GdkDragContextClass {
void (*drop_finish) (GdkDragContext *context,
gboolean success,
guint32 time_);
+ void (* read_async) (GdkDragContext *context,
+ GdkContentFormats *formats,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ GInputStream * (* read_finish) (GdkDragContext *context,
+ const char **out_mime_type,
+ GAsyncResult *result,
+ GError **error);
gboolean (*drop_status) (GdkDragContext *context);
GdkWindow* (*get_drag_window) (GdkDragContext *context);
void (*set_hotspot) (GdkDragContext *context,
diff --git a/gdk/wayland/gdkdnd-wayland.c b/gdk/wayland/gdkdnd-wayland.c
index a7281b8..b64f487 100644
--- a/gdk/wayland/gdkdnd-wayland.c
+++ b/gdk/wayland/gdkdnd-wayland.c
@@ -24,10 +24,14 @@
#include "gdkprivate-wayland.h"
#include "gdkcontentformats.h"
#include "gdkdisplay-wayland.h"
+#include "gdkintl.h"
#include "gdkseat-wayland.h"
#include "gdkdeviceprivate.h"
+#include <glib-unix.h>
+#include <gio/gunixinputstream.h>
+#include <gio/gunixoutputstream.h>
#include <string.h>
#define GDK_TYPE_WAYLAND_DRAG_CONTEXT (gdk_wayland_drag_context_get_type ())
@@ -324,6 +328,80 @@ gdk_wayland_drag_context_drop_finish (GdkDragContext *context,
gdk_wayland_selection_set_offer (display, selection, NULL);
}
+static void
+gdk_wayland_drag_context_read_async (GdkDragContext *context,
+ GdkContentFormats *formats,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GdkDisplay *display;
+ GdkContentFormats *dnd_formats;
+ GInputStream *stream;
+ struct wl_data_offer *offer;
+ const char *mime_type;
+ int pipe_fd[2];
+ GError *error = NULL;
+ GTask *task;
+
+ display = gdk_drag_context_get_display (context),
+ task = g_task_new (context, cancellable, callback, user_data);
+ g_task_set_priority (task, io_priority);
+ g_task_set_source_tag (task, gdk_wayland_drag_context_read_async);
+
+ GDK_NOTE (DND, char *s = gdk_content_formats_to_string (formats);
+ g_printerr ("%p: read for %s\n", context, s);
+ g_free (s); );
+ dnd_formats = gdk_wayland_selection_get_targets (display,
+ gdk_drag_get_selection (context));
+ mime_type = gdk_content_formats_match_mime_type (formats, dnd_formats);
+ if (mime_type == NULL)
+ {
+ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("No compatible transfer format found"));
+ return;
+ }
+ /* offer formats should be empty if we have no offer */
+ offer = gdk_wayland_selection_get_offer (display,
+ gdk_drag_get_selection (context));
+ g_assert (offer);
+
+ g_task_set_task_data (task, (gpointer) mime_type, NULL);
+
+ if (!g_unix_open_pipe (pipe_fd, FD_CLOEXEC, &error))
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ wl_data_offer_accept (offer,
+ _gdk_wayland_display_get_serial (GDK_WAYLAND_DISPLAY (display)),
+ mime_type);
+ wl_data_offer_receive (offer, mime_type, pipe_fd[1]);
+ stream = g_unix_input_stream_new (pipe_fd[0], TRUE);
+ close (pipe_fd[1]);
+ g_task_return_pointer (task, stream, g_object_unref);
+}
+
+static GInputStream *
+gdk_wayland_drag_context_read_finish (GdkDragContext *context,
+ const char **out_mime_type,
+ GAsyncResult *result,
+ GError **error)
+{
+ GTask *task;
+
+ g_return_val_if_fail (g_task_is_valid (result, G_OBJECT (context)), NULL);
+ task = G_TASK (result);
+ g_return_val_if_fail (g_task_get_source_tag (task) == gdk_wayland_drag_context_read_async, NULL);
+
+ if (out_mime_type)
+ *out_mime_type = g_task_get_task_data (task);
+
+ return g_task_propagate_pointer (task, error);
+}
+
static gboolean
gdk_wayland_drag_context_drop_status (GdkDragContext *context)
{
@@ -471,6 +549,9 @@ gdk_wayland_drag_context_class_init (GdkWaylandDragContextClass *klass)
context_class->drag_drop = gdk_wayland_drag_context_drag_drop;
context_class->drop_reply = gdk_wayland_drag_context_drop_reply;
context_class->drop_finish = gdk_wayland_drag_context_drop_finish;
+ context_class->drop_finish = gdk_wayland_drag_context_drop_finish;
+ context_class->read_async = gdk_wayland_drag_context_read_async;
+ context_class->read_finish = gdk_wayland_drag_context_read_finish;
context_class->drop_status = gdk_wayland_drag_context_drop_status;
context_class->get_selection = gdk_wayland_drag_context_get_selection;
context_class->get_drag_window = gdk_wayland_drag_context_get_drag_window;
diff --git a/gdk/x11/gdkclipboard-x11.c b/gdk/x11/gdkclipboard-x11.c
index f6d7b8c..817792e 100644
--- a/gdk/x11/gdkclipboard-x11.c
+++ b/gdk/x11/gdkclipboard-x11.c
@@ -287,7 +287,7 @@ static const struct {
{ "SAVE_TARGETS", NULL, NULL, "NULL", 32, handle_save_targets
}
};
-static GSList *
+GSList *
gdk_x11_clipboard_formats_to_targets (GdkContentFormats *formats)
{
GSList *targets;
diff --git a/gdk/x11/gdkclipboard-x11.h b/gdk/x11/gdkclipboard-x11.h
index a4ed9d4..0c0322d 100644
--- a/gdk/x11/gdkclipboard-x11.h
+++ b/gdk/x11/gdkclipboard-x11.h
@@ -36,6 +36,7 @@ GType gdk_x11_clipboard_get_type (void) G_GNUC_CO
GdkClipboard * gdk_x11_clipboard_new (GdkDisplay *display,
const gchar *selection);
+GSList * gdk_x11_clipboard_formats_to_targets (GdkContentFormats *formats);
G_END_DECLS
diff --git a/gdk/x11/gdkdnd-x11.c b/gdk/x11/gdkdnd-x11.c
index 85a98c5..9b626e2 100644
--- a/gdk/x11/gdkdnd-x11.c
+++ b/gdk/x11/gdkdnd-x11.c
@@ -25,16 +25,19 @@
#include "config.h"
#include "gdkx11dnd.h"
-#include "gdkdndprivate.h"
-#include "gdkdeviceprivate.h"
-#include "gdkinternals.h"
#include "gdkasync.h"
-#include "gdkcontentformatsprivate.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 "gdkdisplay-x11.h"
+#include "gdkselectioninputstream-x11.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
@@ -243,6 +246,135 @@ 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)
+ {
+ GdkDragContext *context = GDK_DRAG_CONTEXT (g_task_get_source_object (task));
+
+ GDK_NOTE (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_drag_context_get_display (context),
+ gdk_drag_get_selection (context),
+ 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_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 (GdkDragContext *context,
+ GdkContentFormats *formats,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSList *targets;
+ GTask *task;
+
+ task = g_task_new (context, 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_NOTE(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_drag_context_get_display (context),
+ gdk_drag_get_selection (context),
+ targets->data,
+ CurrentTime,
+ io_priority,
+ cancellable,
+ gdk_x11_drag_context_read_got_stream,
+ task);
+}
+
+static GInputStream *
+gdk_x11_drag_context_read_finish (GdkDragContext *context,
+ const char **out_mime_type,
+ GAsyncResult *result,
+ GError **error)
+{
+ GTask *task;
+
+ g_return_val_if_fail (g_task_is_valid (result, G_OBJECT (context)), 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);
@@ -258,6 +390,8 @@ gdk_x11_drag_context_class_init (GdkX11DragContextClass *klass)
context_class->drop_reply = gdk_x11_drag_context_drop_reply;
context_class->drop_finish = gdk_x11_drag_context_drop_finish;
context_class->drop_status = gdk_x11_drag_context_drop_status;
+ context_class->read_async = gdk_x11_drag_context_read_async;
+ context_class->read_finish = gdk_x11_drag_context_read_finish;
context_class->get_selection = gdk_x11_drag_context_get_selection;
context_class->get_drag_window = gdk_x11_drag_context_get_drag_window;
context_class->set_hotspot = gdk_x11_drag_context_set_hotspot;
diff --git a/gtk/gtkdnd.c b/gtk/gtkdnd.c
index 1526a3f..fd28212 100644
--- a/gtk/gtkdnd.c
+++ b/gtk/gtkdnd.c
@@ -326,6 +326,115 @@ gtk_drag_get_event_actions (const GdkEvent *event,
* Destination side *
********************/
+typedef struct {
+ GdkDragContext *context;
+ GtkWidget *widget;
+ const char *mime_type;
+ guint time;
+} GtkDragGetData;
+
+static void
+gtk_drag_get_data_finish (GtkDragGetData *data,
+ guchar *bytes,
+ gsize size)
+{
+ GtkDragDestSite *site;
+ GtkSelectionData sdata;
+
+ site = g_object_get_data (G_OBJECT (data->widget), "gtk-drag-dest");
+
+ sdata.selection = gdk_drag_get_selection (data->context);
+ sdata.target = data->mime_type;
+ sdata.type = data->mime_type;
+ sdata.format = 8;
+ sdata.length = size;
+ sdata.data = bytes;
+ sdata.display = gtk_widget_get_display (data->widget);
+
+ if (site && site->target_list)
+ {
+ if (gdk_content_formats_contain_mime_type (site->target_list, data->mime_type))
+ {
+ if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
+ size >= 0)
+ g_signal_emit_by_name (data->widget,
+ "drag-data-received",
+ data->context,
+ &sdata,
+ data->time);
+ }
+ }
+ else
+ {
+ g_signal_emit_by_name (data->widget,
+ "drag-data-received",
+ data->context,
+ &sdata,
+ data->time);
+ }
+
+ if (site && site->flags & GTK_DEST_DEFAULT_DROP)
+ {
+
+ gtk_drag_finish (data->context,
+ size > 0,
+ (gdk_drag_context_get_selected_action (data->context) == GDK_ACTION_MOVE),
+ data->time);
+ }
+
+ g_object_unref (data->widget);
+ g_slice_free (GtkDragGetData, data);
+}
+
+static void
+gtk_drag_get_data_got_data (GObject *source,
+ GAsyncResult *result,
+ gpointer data)
+{
+ gssize written;
+
+ written = g_output_stream_splice_finish (G_OUTPUT_STREAM (source), result, NULL);
+ if (written < 0)
+ {
+ gtk_drag_get_data_finish (data, NULL, 0);
+ }
+ else
+ {
+ gtk_drag_get_data_finish (data,
+ g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (source)),
+ g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (source)));
+ }
+}
+
+static void
+gtk_drag_get_data_got_stream (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GtkDragGetData *data = user_data;
+ GInputStream *input_stream;
+ GOutputStream *output_stream;
+
+ input_stream = gdk_drop_read_finish (GDK_DRAG_CONTEXT (source), &data->mime_type, result, NULL);
+ if (input_stream == NULL)
+ {
+ gtk_drag_get_data_finish (data, NULL, 0);
+ return;
+ }
+
+ output_stream = g_memory_output_stream_new_resizable ();
+ g_output_stream_splice_async (output_stream,
+ input_stream,
+ G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
+ G_PRIORITY_DEFAULT,
+ NULL,
+ gtk_drag_get_data_got_data,
+ data);
+ g_object_unref (output_stream);
+ g_object_unref (input_stream);
+
+}
+
/**
* gtk_drag_get_data: (method)
* @widget: the widget that will receive the
@@ -351,25 +460,23 @@ gtk_drag_get_data (GtkWidget *widget,
GdkAtom target,
guint32 time_)
{
- GtkWidget *selection_widget;
+ GtkDragGetData *data;
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
- selection_widget = gtk_drag_get_ipc_widget (widget);
-
- g_object_ref (context);
- g_object_ref (widget);
-
- g_signal_connect (selection_widget, "selection-received",
- G_CALLBACK (gtk_drag_selection_received), widget);
-
- g_object_set_data (G_OBJECT (selection_widget), I_("drag-context"), context);
-
- gtk_selection_convert (selection_widget,
- gdk_drag_get_selection (context),
- target,
- time_);
+ data = g_slice_new0 (GtkDragGetData);
+ data->widget = g_object_ref (widget);
+ data->context = context;
+ data->mime_type = target;
+ data->time = time_;
+
+ gdk_drop_read_async (context,
+ (const gchar *[2]) { target, NULL },
+ G_PRIORITY_DEFAULT,
+ NULL,
+ gtk_drag_get_data_got_stream,
+ data);
}
/**
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]