From 12ca641ff5f901f72cd44bee5f9075b675fbc3e1 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Fri, 24 Nov 2017 10:13:23 +0100 Subject: [PATCH] clipboard: Implement local fallback clipboard transfers This requires implementing a "pipe" so we can have 2 streams running: contentprovider => serializer => outputstream inputstream => deserializer => reader And the pipe shoves the data from the outputstream into the inputstream. --- gdk/gdkclipboard.c | 130 +++++++++- gdk/gdkclipboardprivate.h | 10 + gdk/gdkpipeiostream.c | 476 +++++++++++++++++++++++++++++++++++ gdk/gdkpipeiostreamprivate.h | 33 +++ gdk/meson.build | 1 + tests/testclipboard2.c | 11 +- 6 files changed, 655 insertions(+), 6 deletions(-) create mode 100644 gdk/gdkpipeiostream.c create mode 100644 gdk/gdkpipeiostreamprivate.h diff --git a/gdk/gdkclipboard.c b/gdk/gdkclipboard.c index 7ba9632991..b19b0904aa 100644 --- a/gdk/gdkclipboard.c +++ b/gdk/gdkclipboard.c @@ -26,6 +26,7 @@ #include "gdkcontentproviderprivate.h" #include "gdkdisplay.h" #include "gdkintl.h" +#include "gdkpipeiostreamprivate.h" typedef struct _GdkClipboardPrivate GdkClipboardPrivate; @@ -123,6 +124,20 @@ gdk_clipboard_finalize (GObject *object) G_OBJECT_CLASS (gdk_clipboard_parent_class)->finalize (object); } +static void +gdk_clipboard_read_local_write_done (GObject *clipboard, + GAsyncResult *result, + gpointer stream) +{ + /* we don't care about the error, we just want to clean up */ + gdk_clipboard_write_finish (GDK_CLIPBOARD (clipboard), result, NULL); + + /* XXX: Do we need to close_async() here? */ + g_output_stream_close (stream, NULL, NULL); + + g_object_unref (stream); +} + static void gdk_clipboard_read_local_async (GdkClipboard *clipboard, GdkContentFormats *formats, @@ -132,24 +147,51 @@ gdk_clipboard_read_local_async (GdkClipboard *clipboard, gpointer user_data) { GdkClipboardPrivate *priv = gdk_clipboard_get_instance_private (clipboard); + GdkContentFormats *content_formats; + const char *mime_type; 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_local_async); - g_task_set_task_data (task, gdk_content_formats_ref (formats), (GDestroyNotify) gdk_content_formats_unref); if (priv->content == NULL) { g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("Cannot read from empty clipboard.")); + g_object_unref (task); + return; + } + + content_formats = gdk_content_provider_ref_formats (priv->content); + + if (!gdk_content_formats_match (content_formats, formats, NULL, &mime_type) + || mime_type == NULL) + { + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + _("No compatible formats to transfer clipboard contents.")); } else { - g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - _("Reading local content via streams not supported yet.")); + GOutputStream *output_stream; + GIOStream *stream; + + stream = gdk_pipe_io_stream_new (); + output_stream = g_io_stream_get_output_stream (stream); + gdk_clipboard_write_async (clipboard, + mime_type, + output_stream, + io_priority, + cancellable, + gdk_clipboard_read_local_write_done, + g_object_ref (output_stream)); + g_task_set_task_data (task, (gpointer) mime_type, NULL); + g_task_return_pointer (task, g_object_ref (g_io_stream_get_input_stream (stream)), g_object_unref); + + g_object_unref (stream); } + gdk_content_formats_unref (content_formats); g_object_unref (task); } @@ -163,7 +205,7 @@ gdk_clipboard_read_local_finish (GdkClipboard *clipboard, g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gdk_clipboard_read_local_async, NULL); if (out_mime_type) - *out_mime_type = NULL; + *out_mime_type = g_task_get_task_data (G_TASK (result)); return g_task_propagate_pointer (G_TASK (result), error); } @@ -769,6 +811,86 @@ gdk_clipboard_new (GdkDisplay *display) NULL); } +static void +gdk_clipboard_write_done (GObject *content, + GAsyncResult *result, + gpointer task) +{ + GError *error = NULL; + + if (gdk_content_provider_write_mime_type_finish (GDK_CONTENT_PROVIDER (content), result, &error)) + g_task_return_boolean (task, TRUE); + else + g_task_return_error (task, error); + + g_object_unref (task); +} + +void +gdk_clipboard_write_async (GdkClipboard *clipboard, + const char *mime_type, + GOutputStream *stream, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GdkClipboardPrivate *priv = gdk_clipboard_get_instance_private (clipboard); + GdkContentFormats *formats; + GTask *task; + + g_return_if_fail (GDK_IS_CLIPBOARD (clipboard)); + g_return_if_fail (priv->local); + g_return_if_fail (mime_type != NULL); + g_return_if_fail (mime_type == g_intern_string (mime_type)); + g_return_if_fail (G_IS_OUTPUT_STREAM (stream)); + g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); + g_return_if_fail (callback != NULL); + + task = g_task_new (clipboard, cancellable, callback, user_data); + g_task_set_priority (task, io_priority); + g_task_set_source_tag (task, gdk_clipboard_write_async); + + if (priv->content == NULL) + { + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + _("Cannot read from empty clipboard.")); + g_object_unref (task); + return; + } + + formats = gdk_content_provider_ref_formats (priv->content); + if (gdk_content_formats_contain_mime_type (formats, mime_type)) + { + gdk_content_provider_write_mime_type_async (priv->content, + mime_type, + stream, + io_priority, + cancellable, + gdk_clipboard_write_done, + task); + gdk_content_formats_unref (formats); + return; + } + + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + _("FIXME: Implement serializing.")); + gdk_content_formats_unref (formats); + g_object_unref (task); + return; +} + +gboolean +gdk_clipboard_write_finish (GdkClipboard *clipboard, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_task_is_valid (result, clipboard), FALSE); + g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gdk_clipboard_write_async, FALSE); + + return g_task_propagate_boolean (G_TASK (result), error); +} + static void gdk_clipboard_content_changed_cb (GdkContentProvider *provider, GdkClipboard *clipboard); diff --git a/gdk/gdkclipboardprivate.h b/gdk/gdkclipboardprivate.h index b171068a5f..73b198eacf 100644 --- a/gdk/gdkclipboardprivate.h +++ b/gdk/gdkclipboardprivate.h @@ -57,6 +57,16 @@ GdkClipboard * gdk_clipboard_new (GdkDisplay void gdk_clipboard_claim_remote (GdkClipboard *clipboard, GdkContentFormats *formats); +void gdk_clipboard_write_async (GdkClipboard *clipboard, + const char *mime_type, + GOutputStream *stream, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean gdk_clipboard_write_finish (GdkClipboard *clipboard, + GAsyncResult *result, + GError **error); G_END_DECLS diff --git a/gdk/gdkpipeiostream.c b/gdk/gdkpipeiostream.c new file mode 100644 index 0000000000..c7e0b6c32e --- /dev/null +++ b/gdk/gdkpipeiostream.c @@ -0,0 +1,476 @@ +/* GDK - The GIMP Drawing Kit + * + * Copyright (C) 2017 Benjamin Otte + * + * 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 . + */ + +#include "config.h" + +#include "gdkpipeiostreamprivate.h" + +#include + +/* PIPE */ + +typedef enum { + GDK_IO_PIPE_EMPTY, + GDK_IO_PIPE_INPUT_BUFFER, + GDK_IO_PIPE_OUTPUT_BUFFER +} GdkIOPipeState; + +typedef struct _GdkIOPipe GdkIOPipe; + +struct _GdkIOPipe +{ + gint ref_count; + + GMutex mutex; + GCond cond; + guchar *buffer; + gsize size; + GdkIOPipeState state : 2; + guint input_closed : 1; + guint output_closed : 1; +}; + +static GdkIOPipe * +gdk_io_pipe_new (void) +{ + GdkIOPipe *pipe; + + pipe = g_slice_new0 (GdkIOPipe); + pipe->ref_count = 1; + + g_mutex_init (&pipe->mutex); + g_cond_init (&pipe->cond); + + return pipe; +} + +static GdkIOPipe * +gdk_io_pipe_ref (GdkIOPipe *pipe) +{ + g_atomic_int_inc (&pipe->ref_count); + + return pipe; +} + +static void +gdk_io_pipe_unref (GdkIOPipe *pipe) +{ + if (!g_atomic_int_dec_and_test (&pipe->ref_count)) + return; + + g_cond_clear (&pipe->cond); + g_mutex_clear (&pipe->mutex); +} + +static void +gdk_io_pipe_lock (GdkIOPipe *pipe) +{ + g_mutex_lock (&pipe->mutex); +} + +static void +gdk_io_pipe_unlock (GdkIOPipe *pipe) +{ + g_mutex_unlock (&pipe->mutex); +} + +/* INPUT STREAM */ + +#define GDK_TYPE_PIPE_INPUT_STREAM (gdk_pipe_input_stream_get_type ()) +#define GDK_PIPE_INPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_PIPE_INPUT_STREAM, GdkPipeInputStream)) +#define GDK_IS_PIPE_INPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_PIPE_INPUT_STREAM)) +#define GDK_PIPE_INPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_PIPE_INPUT_STREAM, GdkPipeInputStreamClass)) +#define GDK_IS_PIPE_INPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_PIPE_INPUT_STREAM)) +#define GDK_PIPE_INPUT_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_PIPE_INPUT_STREAM, GdkPipeInputStreamClass)) + +typedef struct _GdkPipeInputStream GdkPipeInputStream; +typedef struct _GdkPipeInputStreamClass GdkPipeInputStreamClass; + +struct _GdkPipeInputStream +{ + GInputStream parent; + + GdkIOPipe *pipe; +}; + +struct _GdkPipeInputStreamClass +{ + GInputStreamClass parent_class; +}; + +GType gdk_pipe_input_stream_get_type (void) G_GNUC_CONST; + +G_DEFINE_TYPE (GdkPipeInputStream, gdk_pipe_input_stream, G_TYPE_INPUT_STREAM) + +static void +gdk_pipe_input_stream_finalize (GObject *object) +{ + GdkPipeInputStream *pipe = GDK_PIPE_INPUT_STREAM (object); + + g_clear_pointer (&pipe->pipe, gdk_io_pipe_unref); + + G_OBJECT_CLASS (gdk_pipe_input_stream_parent_class)->finalize (object); +} + +static gssize +gdk_pipe_input_stream_read (GInputStream *stream, + void *buffer, + gsize count, + GCancellable *cancellable, + GError **error) +{ + GdkPipeInputStream *pipe_stream = GDK_PIPE_INPUT_STREAM (stream); + GdkIOPipe *pipe = pipe_stream->pipe; + gsize amount; + + gdk_io_pipe_lock (pipe); + + switch (pipe->state) + { + case GDK_IO_PIPE_EMPTY: + if (pipe->output_closed) + { + amount = 0; + break; + } + pipe->buffer = buffer; + pipe->size = count; + pipe->state = GDK_IO_PIPE_INPUT_BUFFER; + do + g_cond_wait (&pipe->cond, &pipe->mutex); + while (pipe->size == count && + pipe->state == GDK_IO_PIPE_INPUT_BUFFER && + !pipe->output_closed); + if (pipe->state == GDK_IO_PIPE_INPUT_BUFFER) + { + amount = count - pipe->size; + pipe->state = GDK_IO_PIPE_EMPTY; + pipe->size = 0; + } + else + { + amount = count; + } + break; + + case GDK_IO_PIPE_OUTPUT_BUFFER: + amount = MIN (count, pipe->size); + + memcpy (buffer, pipe->buffer, amount); + count -= amount; + pipe->size -= amount; + + if (pipe->size == 0) + pipe->state = GDK_IO_PIPE_EMPTY; + else + pipe->buffer += amount; + break; + + case GDK_IO_PIPE_INPUT_BUFFER: + default: + g_assert_not_reached (); + amount = 0; + break; + } + + g_cond_broadcast (&pipe->cond); + gdk_io_pipe_unlock (pipe); + + return amount; +} + +static gboolean +gdk_pipe_input_stream_close (GInputStream *stream, + GCancellable *cancellable, + GError **error) +{ + GdkPipeInputStream *pipe_stream = GDK_PIPE_INPUT_STREAM (stream); + GdkIOPipe *pipe = pipe_stream->pipe; + + gdk_io_pipe_lock (pipe); + + pipe->input_closed = TRUE; + g_cond_broadcast (&pipe->cond); + + gdk_io_pipe_unlock (pipe); + + return TRUE; +} + +static void +gdk_pipe_input_stream_class_init (GdkPipeInputStreamClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + GInputStreamClass *input_stream_class = G_INPUT_STREAM_CLASS (class); + + object_class->finalize = gdk_pipe_input_stream_finalize; + + input_stream_class->read_fn = gdk_pipe_input_stream_read; + input_stream_class->close_fn = gdk_pipe_input_stream_close; +} + +static void +gdk_pipe_input_stream_init (GdkPipeInputStream *pipe) +{ +} + +/* OUTPUT STREAM */ + +#define GDK_TYPE_PIPE_OUTPUT_STREAM (gdk_pipe_output_stream_get_type ()) +#define GDK_PIPE_OUTPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_PIPE_OUTPUT_STREAM, GdkPipeOutputStream)) +#define GDK_IS_PIPE_OUTPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_PIPE_OUTPUT_STREAM)) +#define GDK_PIPE_OUTPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_PIPE_OUTPUT_STREAM, GdkPipeOutputStreamClass)) +#define GDK_IS_PIPE_OUTPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_PIPE_OUTPUT_STREAM)) +#define GDK_PIPE_OUTPUT_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_PIPE_OUTPUT_STREAM, GdkPipeOutputStreamClass)) + +typedef struct _GdkPipeOutputStream GdkPipeOutputStream; +typedef struct _GdkPipeOutputStreamClass GdkPipeOutputStreamClass; + +struct _GdkPipeOutputStream +{ + GOutputStream parent; + + GdkIOPipe *pipe; +}; + +struct _GdkPipeOutputStreamClass +{ + GOutputStreamClass parent_class; +}; + +GType gdk_pipe_output_stream_get_type (void) G_GNUC_CONST; + +G_DEFINE_TYPE (GdkPipeOutputStream, gdk_pipe_output_stream, G_TYPE_OUTPUT_STREAM) + +static void +gdk_pipe_output_stream_finalize (GObject *object) +{ + GdkPipeOutputStream *pipe = GDK_PIPE_OUTPUT_STREAM (object); + + g_clear_pointer (&pipe->pipe, gdk_io_pipe_unref); + + G_OBJECT_CLASS (gdk_pipe_output_stream_parent_class)->finalize (object); +} + +static gssize +gdk_pipe_output_stream_write (GOutputStream *stream, + const void *buffer, + gsize count, + GCancellable *cancellable, + GError **error) +{ + GdkPipeOutputStream *pipe_stream = GDK_PIPE_OUTPUT_STREAM (stream); + GdkIOPipe *pipe = pipe_stream->pipe; + gsize amount; + + gdk_io_pipe_lock (pipe); + + switch (pipe->state) + { + case GDK_IO_PIPE_EMPTY: + pipe->buffer = (void *) buffer; + pipe->size = count; + pipe->state = GDK_IO_PIPE_OUTPUT_BUFFER; + while (pipe->size == count && + pipe->state == GDK_IO_PIPE_OUTPUT_BUFFER && + !pipe->input_closed) + g_cond_wait (&pipe->cond, &pipe->mutex); + if (pipe->state == GDK_IO_PIPE_OUTPUT_BUFFER) + { + amount = count - pipe->size; + pipe->state = GDK_IO_PIPE_EMPTY; + pipe->size = 0; + if (pipe->input_closed && amount == 0) + amount = count; + } + else + { + amount = count; + } + break; + + case GDK_IO_PIPE_INPUT_BUFFER: + amount = MIN (count, pipe->size); + + memcpy (pipe->buffer, buffer, amount); + count -= amount; + pipe->size -= amount; + + if (pipe->size == 0) + pipe->state = GDK_IO_PIPE_EMPTY; + else + pipe->buffer += amount; + break; + + case GDK_IO_PIPE_OUTPUT_BUFFER: + default: + g_assert_not_reached (); + amount = 0; + break; + } + + g_cond_broadcast (&pipe->cond); + gdk_io_pipe_unlock (pipe); + + return amount; +} + +static gboolean +gdk_pipe_output_stream_close (GOutputStream *stream, + GCancellable *cancellable, + GError **error) +{ + GdkPipeOutputStream *pipe_stream = GDK_PIPE_OUTPUT_STREAM (stream); + GdkIOPipe *pipe = pipe_stream->pipe; + + gdk_io_pipe_lock (pipe); + + pipe->output_closed = TRUE; + + g_cond_broadcast (&pipe->cond); + gdk_io_pipe_unlock (pipe); + + return TRUE; +} + +static void +gdk_pipe_output_stream_class_init (GdkPipeOutputStreamClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + GOutputStreamClass *output_stream_class = G_OUTPUT_STREAM_CLASS (class); + + object_class->finalize = gdk_pipe_output_stream_finalize; + + output_stream_class->write_fn = gdk_pipe_output_stream_write; + output_stream_class->close_fn = gdk_pipe_output_stream_close; +} + +static void +gdk_pipe_output_stream_init (GdkPipeOutputStream *pipe) +{ +} + +/* IOSTREAM */ + +#define GDK_TYPE_PIPE_IO_STREAM (gdk_pipe_io_stream_get_type ()) +#define GDK_PIPE_IO_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_PIPE_IO_STREAM, GdkPipeIOStream)) +#define GDK_IS_PIPE_IO_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_PIPE_IO_STREAM)) +#define GDK_PIPE_IO_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_PIPE_IO_STREAM, GdkPipeIOStreamClass)) +#define GDK_IS_PIPE_IO_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_PIPE_IO_STREAM)) +#define GDK_PIPE_IO_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_PIPE_IO_STREAM, GdkPipeIOStreamClass)) + +typedef struct _GdkPipeIOStream GdkPipeIOStream; +typedef struct _GdkPipeIOStreamClass GdkPipeIOStreamClass; + +struct _GdkPipeIOStream +{ + GIOStream parent; + + GInputStream *input_stream; + GOutputStream *output_stream; + GdkIOPipe *pipe; +}; + +struct _GdkPipeIOStreamClass +{ + GIOStreamClass parent_class; +}; + +GType gdk_pipe_io_stream_get_type (void) G_GNUC_CONST; + +G_DEFINE_TYPE (GdkPipeIOStream, gdk_pipe_io_stream, G_TYPE_IO_STREAM) + +static void +gdk_pipe_io_stream_finalize (GObject *object) +{ + GdkPipeIOStream *pipe = GDK_PIPE_IO_STREAM (object); + + g_clear_object (&pipe->input_stream); + g_clear_object (&pipe->output_stream); + g_clear_pointer (&pipe->pipe, gdk_io_pipe_unref); + + G_OBJECT_CLASS (gdk_pipe_io_stream_parent_class)->finalize (object); +} + +static GInputStream * +gdk_pipe_io_stream_get_input_stream (GIOStream *stream) +{ + GdkPipeIOStream *pipe = GDK_PIPE_IO_STREAM (stream); + + return pipe->input_stream; +} + +static GOutputStream * +gdk_pipe_io_stream_get_output_stream (GIOStream *stream) +{ + GdkPipeIOStream *pipe = GDK_PIPE_IO_STREAM (stream); + + return pipe->output_stream; +} + +static gboolean +gdk_pipe_io_stream_close (GIOStream *stream, + GCancellable *cancellable, + GError **error) +{ + /* overwrite so we don't close the 2 streams */ + return TRUE; +} + +static void +gdk_pipe_io_stream_class_init (GdkPipeIOStreamClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + GIOStreamClass *io_class = G_IO_STREAM_CLASS (class); + + object_class->finalize = gdk_pipe_io_stream_finalize; + + io_class->get_input_stream = gdk_pipe_io_stream_get_input_stream; + io_class->get_output_stream = gdk_pipe_io_stream_get_output_stream; + io_class->close_fn = gdk_pipe_io_stream_close; +} + +static void +gdk_pipe_io_stream_init (GdkPipeIOStream *pipe) +{ + pipe->pipe = gdk_io_pipe_new (); + + pipe->input_stream = g_object_new (GDK_TYPE_PIPE_INPUT_STREAM, NULL); + GDK_PIPE_INPUT_STREAM (pipe->input_stream)->pipe = gdk_io_pipe_ref (pipe->pipe); + + pipe->output_stream = g_object_new (GDK_TYPE_PIPE_OUTPUT_STREAM, NULL); + GDK_PIPE_OUTPUT_STREAM (pipe->output_stream)->pipe = gdk_io_pipe_ref (pipe->pipe); +} + +/** + * gdk_pipe_io_stream_new: + * + * Creates a #GIOStream whose input- and output-stream behave like a pipe. + * Data written into the output stream becomes available for reading on + * the input stream. + * + * Note that this is data transfer in the opposite direction to + * g_output_stream_splice(). + * + * Returns: a new #GIOStream + **/ +GIOStream * +gdk_pipe_io_stream_new (void) +{ + return g_object_new (GDK_TYPE_PIPE_IO_STREAM, NULL); +} diff --git a/gdk/gdkpipeiostreamprivate.h b/gdk/gdkpipeiostreamprivate.h new file mode 100644 index 0000000000..50af59defe --- /dev/null +++ b/gdk/gdkpipeiostreamprivate.h @@ -0,0 +1,33 @@ +/* GDK - The GIMP Drawing Kit + * + * Copyright (C) 2017 Benjamin Otte + * + * 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 . + */ + +#ifndef __GDK_PIPE_IO_STREAM_H__ +#define __GDK_PIPE_IO_STREAM_H__ + +#include +#include + +G_BEGIN_DECLS + + +GIOStream * gdk_pipe_io_stream_new (void); + + +G_END_DECLS + +#endif /* __GDK_PIPE_IO_STREAM_H__ */ diff --git a/gdk/meson.build b/gdk/meson.build index 968dd5d14f..b13e9c79da 100644 --- a/gdk/meson.build +++ b/gdk/meson.build @@ -28,6 +28,7 @@ gdk_public_sources = files([ 'gdkmonitor.c', 'gdkpango.c', 'gdkpixbuf-drawable.c', + 'gdkpipeiostream.c', 'gdkproperty.c', 'gdkrectangle.c', 'gdkrgba.c', diff --git a/tests/testclipboard2.c b/tests/testclipboard2.c index 4ea833dbe2..c333a0ff38 100644 --- a/tests/testclipboard2.c +++ b/tests/testclipboard2.c @@ -182,7 +182,7 @@ get_button_list (GdkClipboard *clipboard) add_provider_button (box, gdk_content_provider_new_for_value (&value), clipboard, - "Icon"); + "GdkPixbuf"); g_value_unset (&value); g_value_init (&value, G_TYPE_STRING); @@ -190,9 +190,16 @@ get_button_list (GdkClipboard *clipboard) add_provider_button (box, gdk_content_provider_new_for_value (&value), clipboard, - "Text"); + "gchararry"); g_value_unset (&value); + add_provider_button (box, + gdk_content_provider_new_for_bytes ("text/plain;charset=utf-8", + g_bytes_new_static ("𝕳𝖊𝖑𝖑𝖔 𝖀𝖓𝖎𝖈𝖔𝖉𝖊", + strlen ("𝕳𝖊𝖑𝖑𝖔 𝖀𝖓𝖎𝖈𝖔𝖉𝖊") + 1)), + clipboard, + "text/plain"); + return box; }