diff --git a/gdk/gdkclipboard.c b/gdk/gdkclipboard.c index eee9284a9a..30b81fff3e 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 6c36c940f9..5721fd10ff 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 faf1ce61fc..b171068a5f 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 b692006793..a88c86d1ce 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 (gdk_clipboard_get_display (GDK_CLIPBOARD (cb)), - cb->selection, - "TARGETS", - cb->timestamp); + stream = gdk_x11_selection_input_stream_new_finish (result, &error); + if (stream == NULL) + { + g_object_unref (cb); + return; + } + + 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; - return gdk_x11_selection_input_stream_new (gdk_clipboard_get_display (GDK_CLIPBOARD (cb)), - cb->selection, - mime_type, - cb->timestamp); + task = g_task_new (clipboard, cancellable, callback, user_data); + g_task_set_priority (task, io_priority); + g_task_set_source_tag (task, gdk_x11_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_x11_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 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 187b00ea84..d4d698c7d8 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, + _("Format %s not supported"), 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); + if (priv->xtype == 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); + 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 ad4364f25c..c8bd19d55e 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 7cabd6a0ea..e141ecfcdc 100644 --- a/tests/testclipboard2.c +++ b/tests/testclipboard2.c @@ -49,6 +49,30 @@ pixbuf_loaded_cb (GObject *stream, g_object_unref (pixbuf); } +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, @@ -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); } }