[gtk+/wip/otte/clipboard: 9/19] clipboard: Refactor gdk_clipboard_read() to be async
- From: Benjamin Otte <otte src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/wip/otte/clipboard: 9/19] clipboard: Refactor gdk_clipboard_read() to be async
- Date: Fri, 24 Nov 2017 10:36:43 +0000 (UTC)
commit 850e01e4b995dc873dde020d26f1a6b642bb2406
Author: Benjamin Otte <otte redhat com>
Date: Wed Nov 22 09:25:35 2017 +0100
clipboard: Refactor gdk_clipboard_read() to be async
This allows us not just to pass any mime type to the read function, but
it also makes it possible to pass multiple mime types and the clipboard
can then try them in order until it finds a supported one.
This is so far not implemented though.
gdk/gdkclipboard.c | 109 +++++++++++++++++++++++---
gdk/gdkclipboard.h | 13 +++-
gdk/gdkclipboardprivate.h | 12 +++-
gdk/x11/gdkclipboard-x11.c | 138 ++++++++++++++++++++++++++++-----
gdk/x11/gdkselectioninputstream-x11.c | 93 ++++++++++++++++------
gdk/x11/gdkselectioninputstream-x11.h | 15 +++-
tests/testclipboard2.c | 42 +++++++---
7 files changed, 349 insertions(+), 73 deletions(-)
---
diff --git a/gdk/gdkclipboard.c b/gdk/gdkclipboard.c
index eee9284..30b81ff 100644
--- a/gdk/gdkclipboard.c
+++ b/gdk/gdkclipboard.c
@@ -113,9 +113,30 @@ gdk_clipboard_finalize (GObject *object)
G_OBJECT_CLASS (gdk_clipboard_parent_class)->finalize (object);
}
+static void
+gdk_clipboard_real_read_async (GdkClipboard *clipboard,
+ GdkContentFormats *formats,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (clipboard, cancellable, callback, user_data);
+ g_task_set_priority (task, io_priority);
+ g_task_set_source_tag (task, gdk_clipboard_read_async);
+ g_task_set_task_data (task, gdk_content_formats_ref (formats), (GDestroyNotify) gdk_content_formats_unref);
+
+ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Reading local content not supported
yet.");
+ g_object_unref (task);
+}
+
static GInputStream *
-gdk_clipboard_real_read (GdkClipboard *clipboard,
- const char *mime_type)
+gdk_clipboard_real_read_finish (GdkClipboard *clipboard,
+ const char **out_mime_type,
+ GAsyncResult *result,
+ GError **error)
{
/* whoop whooop */
return g_memory_input_stream_new ();
@@ -130,7 +151,8 @@ gdk_clipboard_class_init (GdkClipboardClass *class)
object_class->set_property = gdk_clipboard_set_property;
object_class->finalize = gdk_clipboard_finalize;
- class->read = gdk_clipboard_real_read;
+ class->read_async = gdk_clipboard_real_read_async;
+ class->read_finish = gdk_clipboard_real_read_finish;
/**
* GdkClipboard:display:
@@ -237,17 +259,84 @@ gdk_clipboard_get_formats (GdkClipboard *clipboard)
return priv->formats;
}
-GInputStream *
-gdk_clipboard_read (GdkClipboard *clipboard,
- const char *mime_type)
+static void
+gdk_clipboard_read_internal (GdkClipboard *clipboard,
+ GdkContentFormats *formats,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GdkClipboardPrivate *priv = gdk_clipboard_get_instance_private (clipboard);
+ return GDK_CLIPBOARD_GET_CLASS (clipboard)->read_async (clipboard,
+ formats,
+ io_priority,
+ cancellable,
+ callback,
+ user_data);
+}
+
+/**
+ * gdk_clipboard_read_async:
+ * @clipboard: a #GdkClipboard
+ * @mime_types: a %NULL-terminated array of mime types to choose from
+ * @io_priority: the [I/O priority][io-priority]
+ * of the request.
+ * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore.
+ * @callback: (scope async): callback to call when the request is satisfied
+ * @user_data: (closure): the data to pass to callback function
+ *
+ * Asynchronously requests an input stream to read the @clipboard's
+ * contents from. When the operation is finished @callback will be called.
+ * You can then call gdk_clipboard_read_finish() to get the result of the
+ * operation.
+ *
+ * The clipboard will choose the most suitable mime type from the given list
+ * to fulfill the request, preferring the ones listed first.
+ **/
+void
+gdk_clipboard_read_async (GdkClipboard *clipboard,
+ const char **mime_types,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GdkContentFormats *formats;
+
+ g_return_if_fail (GDK_IS_CLIPBOARD (clipboard));
+ g_return_if_fail (mime_types != NULL && mime_types[0] != NULL);
+ g_return_if_fail (callback != NULL);
+
+ formats = gdk_content_formats_new (mime_types, g_strv_length ((char **) mime_types));
+
+ gdk_clipboard_read_internal (clipboard, formats, io_priority, cancellable, callback, user_data);
+
+ gdk_content_formats_unref (formats);
+}
+/**
+ * gdk_clipboard_read_finish:
+ * @clipboard: a #GdkClipboard
+ * @out_mime_type: (out) (allow-none) (transfer none): pointer to store
+ * the chosen mime type in or %NULL
+ * @result: a #GAsyncResult
+ * @error: a #GError location to store the error occurring, or %NULL to
+ * ignore.
+ *
+ * Finishes an asynchronous clipboard read started with gdk_clipboard_read_async().
+ *
+ * Returns: (transfer full): a #GInputStream or %NULL on error.
+ **/
+GInputStream *
+gdk_clipboard_read_finish (GdkClipboard *clipboard,
+ const char **out_mime_type,
+ GAsyncResult *result,
+ GError **error)
+{
g_return_val_if_fail (GDK_IS_CLIPBOARD (clipboard), NULL);
- g_return_val_if_fail (mime_type != NULL, NULL);
- g_return_val_if_fail (gdk_content_formats_contain_mime_type (priv->formats, mime_type), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
- return GDK_CLIPBOARD_GET_CLASS (clipboard)->read (clipboard, mime_type);
+ return GDK_CLIPBOARD_GET_CLASS (clipboard)->read_finish (clipboard, out_mime_type, result, error);
}
GdkClipboard *
diff --git a/gdk/gdkclipboard.h b/gdk/gdkclipboard.h
index 6c36c94..5721fd1 100644
--- a/gdk/gdkclipboard.h
+++ b/gdk/gdkclipboard.h
@@ -43,8 +43,17 @@ GDK_AVAILABLE_IN_3_94
GdkContentFormats * gdk_clipboard_get_formats (GdkClipboard *clipboard);
GDK_AVAILABLE_IN_3_94
-GInputStream * gdk_clipboard_read (GdkClipboard *clipboard,
- const char *mime_type);
+void gdk_clipboard_read_async (GdkClipboard *clipboard,
+ const char **mime_types,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GDK_AVAILABLE_IN_3_94
+GInputStream * gdk_clipboard_read_finish (GdkClipboard *clipboard,
+ const char **out_mime_type,
+ GAsyncResult *result,
+ GError **error);
G_END_DECLS
diff --git a/gdk/gdkclipboardprivate.h b/gdk/gdkclipboardprivate.h
index faf1ce6..b171068 100644
--- a/gdk/gdkclipboardprivate.h
+++ b/gdk/gdkclipboardprivate.h
@@ -41,8 +41,16 @@ struct _GdkClipboardClass
void (* changed) (GdkClipboard *clipboard);
/* vfuncs */
- GInputStream * (* read) (GdkClipboard *clipboard,
- const char *mime_type);
+ void (* read_async) (GdkClipboard *clipboard,
+ GdkContentFormats *formats,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ GInputStream * (* read_finish) (GdkClipboard *clipboard,
+ const char **out_mime_type,
+ GAsyncResult *result,
+ GError **error);
};
GdkClipboard * gdk_clipboard_new (GdkDisplay *display);
diff --git a/gdk/x11/gdkclipboard-x11.c b/gdk/x11/gdkclipboard-x11.c
index b692006..31b3bfe 100644
--- a/gdk/x11/gdkclipboard-x11.c
+++ b/gdk/x11/gdkclipboard-x11.c
@@ -73,10 +73,16 @@ gdk_x11_clipboard_request_targets_finish (GObject *source_object,
guint i, n_atoms;
bytes = g_input_stream_read_bytes_finish (stream, res, &error);
- if (bytes == NULL || g_bytes_get_size (bytes) == 0)
+ if (bytes == NULL)
{
- if (bytes)
- g_bytes_unref (bytes);
+ g_error_free (error);
+ g_object_unref (stream);
+ g_object_unref (cb);
+ return;
+ }
+ else if (g_bytes_get_size (bytes) == 0)
+ {
+ g_bytes_unref (bytes);
g_object_unref (stream);
g_object_unref (cb);
return;
@@ -108,24 +114,43 @@ gdk_x11_clipboard_request_targets_finish (GObject *source_object,
}
static void
-gdk_x11_clipboard_request_targets (GdkX11Clipboard *cb)
+gdk_x11_clipboard_request_targets_got_stream (GObject *source,
+ GAsyncResult *result,
+ gpointer data)
{
+ GdkX11Clipboard *cb = data;
GInputStream *stream;
GdkDisplay *display;
-
- display = gdk_clipboard_get_display (GDK_CLIPBOARD (cb));
+ GError *error = NULL;
+
+ stream = gdk_x11_selection_input_stream_new_finish (result, &error);
+ if (stream == NULL)
+ {
+ g_object_unref (cb);
+ return;
+ }
- stream = gdk_x11_selection_input_stream_new (gdk_clipboard_get_display (GDK_CLIPBOARD (cb)),
- cb->selection,
- "TARGETS",
- cb->timestamp);
+ display = gdk_clipboard_get_display (GDK_CLIPBOARD (cb));
g_input_stream_read_bytes_async (stream,
SELECTION_MAX_SIZE (display),
G_PRIORITY_DEFAULT,
NULL,
gdk_x11_clipboard_request_targets_finish,
- g_object_ref (cb));
+ cb);
+}
+
+static void
+gdk_x11_clipboard_request_targets (GdkX11Clipboard *cb)
+{
+ gdk_x11_selection_input_stream_new_async (gdk_clipboard_get_display (GDK_CLIPBOARD (cb)),
+ cb->selection,
+ "TARGETS",
+ cb->timestamp,
+ G_PRIORITY_DEFAULT,
+ NULL,
+ gdk_x11_clipboard_request_targets_got_stream,
+ g_object_ref (cb));
}
static GdkFilterReturn
@@ -180,16 +205,90 @@ gdk_x11_clipboard_finalize (GObject *object)
G_OBJECT_CLASS (gdk_x11_clipboard_parent_class)->finalize (object);
}
-static GInputStream *
-gdk_x11_clipboard_read (GdkClipboard *clipboard,
- const char *mime_type)
+static void
+gdk_x11_clipboard_read_got_stream (GObject *source,
+ GAsyncResult *res,
+ gpointer data)
+{
+ GTask *task = data;
+ GError *error = NULL;
+ GInputStream *stream;
+
+ stream = gdk_x11_selection_input_stream_new_finish (res, &error);
+ /* XXX: We could try more types here */
+ if (stream == NULL)
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, stream, g_object_unref);
+
+ g_object_unref (task);
+}
+
+static void
+gdk_x11_clipboard_read_async (GdkClipboard *clipboard,
+ GdkContentFormats *formats,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
GdkX11Clipboard *cb = GDK_X11_CLIPBOARD (clipboard);
+ const char * const *mime_types;
+ GTask *task;
+
+ task = g_task_new (clipboard, cancellable, callback, user_data);
+ g_task_set_priority (task, io_priority);
+ g_task_set_source_tag (task, gdk_clipboard_read_async);
+ g_task_set_task_data (task, gdk_content_formats_ref (formats), (GDestroyNotify) gdk_content_formats_unref);
+
+ /* XXX: Sort differently? */
+ mime_types = gdk_content_formats_get_mime_types (formats, NULL);
+
+ gdk_x11_selection_input_stream_new_async (gdk_clipboard_get_display (GDK_CLIPBOARD (cb)),
+ cb->selection,
+ mime_types[0],
+ cb->timestamp,
+ io_priority,
+ cancellable,
+ gdk_x11_clipboard_read_got_stream,
+ task);
+}
+
+static GInputStream *
+gdk_x11_clipboard_read_finish (GdkClipboard *clipboard,
+ const char **out_mime_type,
+ GAsyncResult *result,
+ GError **error)
+{
+ GInputStream *stream;
+ GTask *task;
+
+ g_return_val_if_fail (g_task_is_valid (result, G_OBJECT (clipboard)), NULL);
+ task = G_TASK (result);
+ g_return_val_if_fail (g_task_get_source_tag (task) == gdk_clipboard_read_async, NULL);
+
+ stream = g_task_propagate_pointer (task, error);
+
+ if (stream)
+ {
+ if (out_mime_type)
+ {
+ GdkContentFormats *formats;
+ const char * const *mime_types;
+
+ formats = g_task_get_task_data (task);
+ mime_types = gdk_content_formats_get_mime_types (formats, NULL);
+ *out_mime_type = mime_types[0];
+ }
+ g_object_ref (stream);
+ }
+ else
+ {
+ if (out_mime_type)
+ *out_mime_type = NULL;
+ }
- return gdk_x11_selection_input_stream_new (gdk_clipboard_get_display (GDK_CLIPBOARD (cb)),
- cb->selection,
- mime_type,
- cb->timestamp);
+ return stream;
}
static void
@@ -200,7 +299,8 @@ gdk_x11_clipboard_class_init (GdkX11ClipboardClass *class)
object_class->finalize = gdk_x11_clipboard_finalize;
- clipboard_class->read = gdk_x11_clipboard_read;
+ clipboard_class->read_async = gdk_x11_clipboard_read_async;
+ clipboard_class->read_finish = gdk_x11_clipboard_read_finish;
}
static void
diff --git a/gdk/x11/gdkselectioninputstream-x11.c b/gdk/x11/gdkselectioninputstream-x11.c
index 187b00e..e267793 100644
--- a/gdk/x11/gdkselectioninputstream-x11.c
+++ b/gdk/x11/gdkselectioninputstream-x11.c
@@ -404,42 +404,64 @@ gdk_x11_selection_input_stream_filter_event (GdkXEvent *xev,
case SelectionNotify:
{
+ GTask *task;
+ /* selection is not for us */
if (priv->xselection != xevent->xselection.selection ||
priv->xtarget != xevent->xselection.target)
return GDK_FILTER_CONTINUE;
+ /* We already received a selectionNotify before */
+ if (priv->pending_task == NULL ||
+ g_task_get_source_tag (priv->pending_task) != gdk_x11_selection_input_stream_new_async)
+ return GDK_FILTER_CONTINUE;
+
GDK_NOTE(SELECTION, g_print ("%s:%s: got SelectionNotify\n", priv->selection, priv->target));
- if (xevent->xselection.property != None)
- bytes = get_selection_property (xdisplay, xwindow, xevent->xselection.property, &type, &format);
- else
- bytes = NULL;
+ task = priv->pending_task;
+ priv->pending_task = NULL;
- if (bytes == NULL)
- {
+ if (xevent->xselection.property == None)
+ {
+ g_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_FOUND,
+ _("Cannot receive target %s"), priv->target);
gdk_x11_selection_input_stream_complete (stream);
}
else
{
- if (type == gdk_x11_get_xatom_by_name_for_display (priv->display, "INCR"))
- {
- /* The remainder of the selection will come through PropertyNotify
- events on xwindow */
- GDK_NOTE(SELECTION, g_print ("%s:%s: initiating INCR transfer\n", priv->selection,
priv->target));
- priv->incr = TRUE;
- gdk_x11_selection_input_stream_flush (stream);
+ bytes = get_selection_property (xdisplay, xwindow, xevent->xselection.property, &type, &format);
+
+ g_task_return_pointer (task, g_object_ref (stream), g_object_unref);
+
+ if (bytes == NULL)
+ {
+ gdk_x11_selection_input_stream_complete (stream);
}
else
{
- g_async_queue_push (priv->chunks, bytes);
-
- gdk_x11_selection_input_stream_complete (stream);
+ if (type == gdk_x11_get_xatom_by_name_for_display (priv->display, "INCR"))
+ {
+ /* The remainder of the selection will come through PropertyNotify
+ events on xwindow */
+ GDK_NOTE(SELECTION, g_print ("%s:%s: initiating INCR transfer\n", priv->selection,
priv->target));
+ priv->incr = TRUE;
+ gdk_x11_selection_input_stream_flush (stream);
+ }
+ else
+ {
+ g_async_queue_push (priv->chunks, bytes);
+
+ gdk_x11_selection_input_stream_complete (stream);
+ }
}
XDeleteProperty (xdisplay, xwindow, xevent->xselection.property);
}
- }
+
+ g_object_unref (task);
+ }
return GDK_FILTER_REMOVE;
default:
@@ -447,15 +469,19 @@ gdk_x11_selection_input_stream_filter_event (GdkXEvent *xev,
}
}
-GInputStream *
-gdk_x11_selection_input_stream_new (GdkDisplay *display,
- const char *selection,
- const char *target,
- guint32 timestamp)
+void
+gdk_x11_selection_input_stream_new_async (GdkDisplay *display,
+ const char *selection,
+ const char *target,
+ guint32 timestamp,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
GdkX11SelectionInputStream *stream;
GdkX11SelectionInputStreamPrivate *priv;
-
+
stream = g_object_new (GDK_TYPE_X11_SELECTION_INPUT_STREAM, NULL);
priv = gdk_x11_selection_input_stream_get_instance_private (stream);
@@ -477,6 +503,25 @@ gdk_x11_selection_input_stream_new (GdkDisplay *display,
GDK_X11_DISPLAY (display)->leader_window,
timestamp);
- return g_object_ref (stream);
+ priv->pending_task = g_task_new (NULL, cancellable, callback, user_data);
+ g_task_set_source_tag (priv->pending_task, gdk_x11_selection_input_stream_new_async);
+ g_task_set_priority (priv->pending_task, io_priority);
+}
+
+GInputStream *
+gdk_x11_selection_input_stream_new_finish (GAsyncResult *result,
+ GError **error)
+{
+ GInputStream *stream;
+ GTask *task;
+
+ g_return_val_if_fail (g_task_is_valid (result, NULL), NULL);
+ task = G_TASK (result);
+ g_return_val_if_fail (g_task_get_source_tag (task) == gdk_x11_selection_input_stream_new_async, NULL);
+
+ stream = g_task_propagate_pointer (task, error);
+ if (stream)
+ g_object_ref (stream);
+ return stream;
}
diff --git a/gdk/x11/gdkselectioninputstream-x11.h b/gdk/x11/gdkselectioninputstream-x11.h
index ad4364f..c8bd19d 100644
--- a/gdk/x11/gdkselectioninputstream-x11.h
+++ b/gdk/x11/gdkselectioninputstream-x11.h
@@ -50,10 +50,17 @@ struct GdkX11SelectionInputStreamClass
GType gdk_x11_selection_input_stream_get_type (void) G_GNUC_CONST;
-GInputStream * gdk_x11_selection_input_stream_new (GdkDisplay *display,
- const char *selection,
- const char *target,
- guint32 timestamp);
+void gdk_x11_selection_input_stream_new_async (GdkDisplay *display,
+ const char *selection,
+ const char *target,
+ guint32 timestamp,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GInputStream * gdk_x11_selection_input_stream_new_finish (GAsyncResult *result,
+ GError **error);
+
G_END_DECLS
diff --git a/tests/testclipboard2.c b/tests/testclipboard2.c
index 7cabd6a..e141ecf 100644
--- a/tests/testclipboard2.c
+++ b/tests/testclipboard2.c
@@ -50,6 +50,30 @@ pixbuf_loaded_cb (GObject *stream,
}
static void
+clipboard_read_pixbuf_cb (GObject *clipboard,
+ GAsyncResult *result,
+ gpointer image)
+{
+ GInputStream *stream;
+ GError *error = NULL;
+
+ stream = gdk_clipboard_read_finish (GDK_CLIPBOARD (clipboard),
+ NULL,
+ result,
+ &error);
+ if (stream)
+ {
+ gdk_pixbuf_new_from_stream_async (stream, NULL, pixbuf_loaded_cb, image);
+ g_object_unref (stream);
+ }
+ else
+ {
+ g_print ("%s\n", error->message);
+ g_error_free (error);
+ }
+}
+
+static void
visible_child_changed_cb (GtkWidget *stack,
GParamSpec *pspec,
GdkClipboard *clipboard)
@@ -63,19 +87,13 @@ visible_child_changed_cb (GtkWidget *stack,
else if (g_str_equal (visible_child, "image"))
{
GtkWidget *image = gtk_stack_get_child_by_name (GTK_STACK (stack), "image");
- GdkContentFormats *formats;
- GInputStream *stream;
-
- formats = gdk_clipboard_get_formats (clipboard);
- if (gdk_content_formats_contain_mime_type (formats, "image/png"))
- stream = gdk_clipboard_read (clipboard, "image/png");
- else
- stream = NULL;
- if (stream)
- {
- gdk_pixbuf_new_from_stream_async (stream, NULL, pixbuf_loaded_cb, image);
- g_object_unref (stream);
- }
+
+ gdk_clipboard_read_async (clipboard,
+ (const gchar*[2]) { "image/png", NULL },
+ G_PRIORITY_DEFAULT,
+ NULL,
+ clipboard_read_pixbuf_cb,
+ image);
}
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]