On Wed, Feb 5, 2014 at 5:01 PM, Christophe Fergeau <cfergeau@xxxxxxxxxx> wrote: > On Wed, Jan 22, 2014 at 07:26:50PM +0100, Marc-André Lureau wrote: >> From: Marc-André Lureau <marcandre.lureau@xxxxxxxxxx> >> >> This allows to use conveniently GIOStream APIs without caring about >> coroutine and Spice messages details. >> --- >> gtk/Makefile.am | 2 + >> gtk/channel-base.c | 48 +++++ >> gtk/channel-port.c | 33 +-- >> gtk/spice-channel-priv.h | 8 + >> gtk/vmcstream.c | 532 +++++++++++++++++++++++++++++++++++++++++++++++ >> gtk/vmcstream.h | 81 ++++++++ >> 6 files changed, 674 insertions(+), 30 deletions(-) >> create mode 100644 gtk/vmcstream.c >> create mode 100644 gtk/vmcstream.h >> >> diff --git a/gtk/Makefile.am b/gtk/Makefile.am >> index 62afd36..7ceb22f 100644 >> --- a/gtk/Makefile.am >> +++ b/gtk/Makefile.am >> @@ -255,6 +255,8 @@ libspice_client_glib_2_0_la_SOURCES = \ >> usbutil.c \ >> usbutil.h \ >> $(USB_ACL_HELPER_SRCS) \ >> + vmcstream.c \ >> + vmcstream.h \ >> \ >> decode.h \ >> decode-glz.c \ >> diff --git a/gtk/channel-base.c b/gtk/channel-base.c >> index 646042d..363dda5 100644 >> --- a/gtk/channel-base.c >> +++ b/gtk/channel-base.c >> @@ -232,3 +232,51 @@ void spice_channel_set_handlers(SpiceChannelClass *klass, >> spice_channel_add_base_handlers(klass); >> set_handlers(klass, handlers, n); >> } >> + >> +static void >> +vmc_write_free_cb(uint8_t *data, void *user_data) >> +{ >> + GSimpleAsyncResult *result = user_data; >> + >> + g_simple_async_result_complete_in_idle(result); >> + g_object_unref(result); >> +} >> + >> +G_GNUC_INTERNAL >> +void spice_vmc_write_async(SpiceChannel *self, >> + const void *buffer, gsize count, >> + GCancellable *cancellable, >> + GAsyncReadyCallback callback, >> + gpointer user_data) >> +{ >> + SpiceMsgOut *msg; >> + GSimpleAsyncResult *simple; >> + >> + simple = g_simple_async_result_new(G_OBJECT(self), callback, user_data, >> + spice_port_write_async); >> + g_simple_async_result_set_op_res_gssize(simple, count); >> + >> + msg = spice_msg_out_new(SPICE_CHANNEL(self), SPICE_MSGC_SPICEVMC_DATA); >> + spice_marshaller_add_ref_full(msg->marshaller, (uint8_t*)buffer, count, >> + vmc_write_free_cb, simple); >> + spice_msg_out_send(msg); >> +} >> + >> +G_GNUC_INTERNAL >> +gssize spice_vmc_write_finish(SpiceChannel *self, >> + GAsyncResult *result, GError **error) >> +{ >> + GSimpleAsyncResult *simple; >> + >> + g_return_val_if_fail(result != NULL, -1); >> + >> + simple = (GSimpleAsyncResult *)result; >> + >> + if (g_simple_async_result_propagate_error(simple, error)) >> + return -1; >> + >> + g_return_val_if_fail(g_simple_async_result_is_valid(result, G_OBJECT(self), >> + spice_port_write_async), -1); >> + >> + return g_simple_async_result_get_op_res_gssize(simple); >> +} > > I think Hans asked you to split these helpers in a separate commit in his > review. > I don't see any good reason to do that. >> diff --git a/gtk/channel-port.c b/gtk/channel-port.c >> index 0a8b37f..5512713 100644 >> --- a/gtk/channel-port.c >> +++ b/gtk/channel-port.c >> @@ -289,14 +289,6 @@ static void port_handle_msg(SpiceChannel *channel, SpiceMsgIn *in) >> emit_main_context(channel, SPICE_PORT_DATA, buf, size); >> } >> >> -static void port_write_free_cb(uint8_t *data, void *user_data) >> -{ >> - GSimpleAsyncResult *result = user_data; >> - >> - g_simple_async_result_complete(result); >> - g_object_unref(result); >> -} >> - >> /** >> * spice_port_write_async: >> * @port: A #SpicePortChannel >> @@ -320,9 +312,7 @@ void spice_port_write_async(SpicePortChannel *self, >> GAsyncReadyCallback callback, >> gpointer user_data) >> { >> - GSimpleAsyncResult *simple; >> SpicePortChannelPrivate *c; >> - SpiceMsgOut *msg; >> >> g_return_if_fail(SPICE_IS_PORT_CHANNEL(self)); >> g_return_if_fail(buffer != NULL); >> @@ -335,14 +325,8 @@ void spice_port_write_async(SpicePortChannel *self, >> return; >> } >> >> - simple = g_simple_async_result_new(G_OBJECT(self), callback, user_data, >> - spice_port_write_async); >> - g_simple_async_result_set_op_res_gssize(simple, count); >> - >> - msg = spice_msg_out_new(SPICE_CHANNEL(self), SPICE_MSGC_SPICEVMC_DATA); >> - spice_marshaller_add_ref_full(msg->marshaller, (uint8_t*)buffer, count, >> - port_write_free_cb, simple); >> - spice_msg_out_send(msg); >> + spice_vmc_write_async(SPICE_CHANNEL(self), buffer, count, >> + cancellable, callback, user_data); >> } >> >> /** >> @@ -360,20 +344,9 @@ void spice_port_write_async(SpicePortChannel *self, >> gssize spice_port_write_finish(SpicePortChannel *self, >> GAsyncResult *result, GError **error) >> { >> - GSimpleAsyncResult *simple; >> - >> g_return_val_if_fail(SPICE_IS_PORT_CHANNEL(self), -1); >> - g_return_val_if_fail(result != NULL, -1); >> - >> - simple = (GSimpleAsyncResult *)result; >> - >> - if (g_simple_async_result_propagate_error(simple, error)) >> - return -1; >> - >> - g_return_val_if_fail(g_simple_async_result_is_valid(result, G_OBJECT(self), >> - spice_port_write_async), -1); >> >> - return g_simple_async_result_get_op_res_gssize(simple); >> + return spice_vmc_write_finish(SPICE_CHANNEL(self), result, error); >> } >> >> /** >> diff --git a/gtk/spice-channel-priv.h b/gtk/spice-channel-priv.h >> index 0816061..35704ea 100644 >> --- a/gtk/spice-channel-priv.h >> +++ b/gtk/spice-channel-priv.h >> @@ -196,6 +196,14 @@ void spice_caps_set(GArray *caps, guint32 cap, const gchar *desc); >> >> gchar *spice_channel_supported_string(void); >> >> +void spice_vmc_write_async(SpiceChannel *self, >> + const void *buffer, gsize count, >> + GCancellable *cancellable, >> + GAsyncReadyCallback callback, >> + gpointer user_data); >> +gssize spice_vmc_write_finish(SpiceChannel *self, >> + GAsyncResult *result, GError **error); >> + >> G_END_DECLS >> >> #endif /* __SPICE_CLIENT_CHANNEL_PRIV_H__ */ >> diff --git a/gtk/vmcstream.c b/gtk/vmcstream.c >> new file mode 100644 >> index 0000000..724ee58 >> --- /dev/null >> +++ b/gtk/vmcstream.c >> @@ -0,0 +1,532 @@ >> +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ >> +/* >> + Copyright (C) 2013 Red Hat, Inc. > > 2014 > Thanks for being extra picky, but how is this important? It was mostly written in 2013. >> + >> + 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.1 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/>. >> +*/ >> +#include <string.h> >> + >> +#include "vmcstream.h" >> +#include "spice-channel-priv.h" >> +#include "gio-coroutine.h" >> + >> +struct _SpiceVmcInputStream >> +{ >> + GInputStream parent_instance; >> + GSimpleAsyncResult *result; >> + struct coroutine *coroutine; >> + >> + SpiceChannel *channel; >> + gboolean all; >> + guint8 *buffer; >> + gsize count; >> + gsize pos; >> + >> + GCancellable *cancellable; >> + gulong cancel_id; >> +}; >> + >> +struct _SpiceVmcInputStreamClass >> +{ >> + GInputStreamClass parent_class; >> +}; >> + >> +static gssize spice_vmc_input_stream_read (GInputStream *stream, >> + void *buffer, >> + gsize count, >> + GCancellable *cancellable, >> + GError **error); > > Tabs here, probably better to get rid of them for consistency. ok >> +static void spice_vmc_input_stream_read_async (GInputStream *stream, >> + void *buffer, >> + gsize count, >> + int io_priority, >> + GCancellable *cancellable, >> + GAsyncReadyCallback callback, >> + gpointer user_data); >> +static gssize spice_vmc_input_stream_read_finish (GInputStream *stream, >> + GAsyncResult *result, >> + GError **error); >> +static gssize spice_vmc_input_stream_skip (GInputStream *stream, >> + gsize count, >> + GCancellable *cancellable, >> + GError **error); >> +static gboolean spice_vmc_input_stream_close (GInputStream *stream, >> + GCancellable *cancellable, >> + GError **error); >> +static void spice_vmc_input_stream_finalize (GObject *object); >> + >> +G_DEFINE_TYPE_WITH_CODE(SpiceVmcInputStream, spice_vmc_input_stream, G_TYPE_INPUT_STREAM,) > > G_DEFINE_TYPE() to get rid of the trailing comma? > ok >> + >> + >> +static void >> +spice_vmc_input_stream_class_init(SpiceVmcInputStreamClass *klass) >> +{ >> + GObjectClass *object_class; >> + GInputStreamClass *istream_class; >> + >> + object_class = G_OBJECT_CLASS(klass); >> + object_class->finalize = spice_vmc_input_stream_finalize; >> + >> + istream_class = G_INPUT_STREAM_CLASS(klass); >> + istream_class->read_fn = spice_vmc_input_stream_read; >> + istream_class->read_async = spice_vmc_input_stream_read_async; >> + istream_class->read_finish = spice_vmc_input_stream_read_finish; >> + istream_class->skip = spice_vmc_input_stream_skip; >> + istream_class->close_fn = spice_vmc_input_stream_close; >> +} >> + >> +static void >> +spice_vmc_input_stream_finalize(GObject *object) >> +{ >> + G_OBJECT_CLASS (spice_vmc_input_stream_parent_class)->finalize(object); >> +} > > Not strictly needed ok > >> + >> +static void >> +spice_vmc_input_stream_init(SpiceVmcInputStream *self) >> +{ >> +} >> + >> +static SpiceVmcInputStream * >> +spice_vmc_input_stream_new(void) >> +{ >> + SpiceVmcInputStream *self; >> + >> + self = g_object_new(SPICE_TYPE_VMC_INPUT_STREAM, NULL); >> + >> + return self; >> +} >> + >> +/* coroutine */ >> +G_GNUC_INTERNAL void >> +spice_vmc_input_stream_co_data(SpiceVmcInputStream *self, >> + const gpointer d, gsize size) > > This function is not used in this patch, is unusual in a stream > implementation, ... it's probably better to add it with the webdav > patch or in a separate commit, and to have a comment explaining what > it is. It is necessary to feed the stream from a coroutine. Without it, you won't get any data from input stream. > >> +{ >> + guint8 *data = d; >> + >> + g_return_if_fail(SPICE_IS_VMC_INPUT_STREAM(self)); >> + g_return_if_fail(self->coroutine == NULL); >> + >> + self->coroutine = coroutine_self(); >> + >> + while (size > 0) { >> + SPICE_DEBUG("webdav co_data %p", self->result); > > s/webdav/spicevmc > >> + if (!self->result) >> + coroutine_yield(NULL); >> + >> + g_return_if_fail(self->result != NULL); >> + >> + gsize min = MIN(self->count, size); >> + memcpy(self->buffer, data, min); >> + >> + size -= min; >> + data += min; >> + >> + SPICE_DEBUG("webdav co_data complete: %" G_GSIZE_FORMAT > > s/webdav/spicevmc ok > >> + "/%" G_GSIZE_FORMAT, min, self->count); >> + >> + self->pos += min; >> + self->buffer += min; >> + >> + if (self->all && min > 0 && self->pos != self->count) >> + continue; >> + >> + g_simple_async_result_set_op_res_gssize(self->result, self->pos); >> + g_simple_async_result_complete_in_idle(self->result); >> + g_clear_object(&self->result); >> + } >> + >> + self->coroutine = NULL; >> +} >> + >> +static void >> +read_cancelled(GCancellable *cancellable, >> + gpointer user_data) >> +{ >> + SpiceVmcInputStream *self = SPICE_VMC_INPUT_STREAM(user_data); >> + >> + SPICE_DEBUG("read cancelled, %p", self->result); >> + g_simple_async_result_set_error(self->result, >> + G_IO_ERROR, G_IO_ERROR_CANCELLED, >> + "read cancelled"); >> + g_simple_async_result_complete_in_idle(self->result); >> + g_clear_object(&self->result); >> +} >> + >> +void >> +spice_vmc_input_stream_read_all_async(GInputStream *stream, >> + void *buffer, >> + gsize count, >> + int io_priority, >> + GCancellable *cancellable, >> + GAsyncReadyCallback callback, >> + gpointer user_data) >> +{ >> + SpiceVmcInputStream *self = SPICE_VMC_INPUT_STREAM(stream); >> + GSimpleAsyncResult *result; >> + >> + /* no concurrent read permitted by ginputstream */ >> + g_return_if_fail(self->result == NULL); >> + g_return_if_fail(self->cancellable == NULL); > > Isn't it what g_input_stream_set_pending() is meant for? > It is set before implementation (see g_output_stream_write_async for example). Those are just extra checks. >> + self->all = TRUE; >> + self->buffer = buffer; >> + self->count = count; >> + self->pos = 0; >> + result = g_simple_async_result_new(G_OBJECT(self), >> + callback, >> + user_data, >> + spice_vmc_input_stream_read_async); > > I think it's missing > g_simple_async_result_set_op_res_gssize(self->result, count); > it's done when we know what to return, in spice_vmc_input_stream_co_data() >> + self->result = result; > > Calling _finish() is not supposed to be mandatory after calling an _async() > function. Things will not work quite well here if _finish() is not called, > it would be nice to at least document it. > You don't "have" to. and when data comes, it's calling g_simple_async_result_complete_in_idle() so things works the way I needed to. >> + self->cancellable = g_object_ref(cancellable); >> + if (cancellable) >> + self->cancel_id = >> + g_cancellable_connect(cancellable, G_CALLBACK(read_cancelled), self, NULL); >> + >> + if (self->coroutine) >> + coroutine_yieldto(self->coroutine, NULL); >> +} >> + >> +gssize >> +spice_vmc_input_stream_read_all_finish(GInputStream *stream, >> + GAsyncResult *result, >> + GError **error) >> +{ >> + GSimpleAsyncResult *simple; >> + SpiceVmcInputStream *self = SPICE_VMC_INPUT_STREAM(stream); >> + >> + g_return_val_if_fail(g_simple_async_result_is_valid(result, >> + G_OBJECT(self), >> + spice_vmc_input_stream_read_async), >> + -1); >> + >> + if (self->cancellable) { >> + g_cancellable_disconnect(self->cancellable, self->cancel_id); >> + g_clear_object(&self->cancellable); >> + } >> + >> + simple = (GSimpleAsyncResult *)result; >> + >> + if (g_simple_async_result_propagate_error(simple, error)) >> + return -1; >> + >> + return g_simple_async_result_get_op_res_gssize(simple); >> +} >> + >> +static void >> +spice_vmc_input_stream_read_async(GInputStream *stream, >> + void *buffer, >> + gsize count, >> + int io_priority, >> + GCancellable *cancellable, >> + GAsyncReadyCallback callback, >> + gpointer user_data) >> +{ >> + SpiceVmcInputStream *self = SPICE_VMC_INPUT_STREAM(stream); >> + GSimpleAsyncResult *result; >> + >> + /* no concurrent read permitted by ginputstream */ >> + g_return_if_fail(self->result == NULL); >> + g_return_if_fail(self->cancellable == NULL); >> + self->all = FALSE; >> + self->buffer = buffer; >> + self->count = count; >> + self->pos = 0; >> + result = g_simple_async_result_new(G_OBJECT(self), >> + callback, >> + user_data, >> + spice_vmc_input_stream_read_async); >> + self->result = result; >> + self->cancellable = g_object_ref(cancellable); >> + if (cancellable) >> + self->cancel_id = >> + g_cancellable_connect(cancellable, G_CALLBACK(read_cancelled), self, NULL); >> + >> + if (self->coroutine) >> + coroutine_yieldto(self->coroutine, NULL); > > Also missing a call to g_simple_async_result_set_op_res_gssize(self->result, count); > You don't have to do that, it is chaining async call to spice_vmc_write_async/finish and returning result in spice_vmc_output_stream_write_finish(). >> +} >> + >> +static gssize >> +spice_vmc_input_stream_read_finish(GInputStream *stream, >> + GAsyncResult *result, >> + GError **error) >> +{ >> + GSimpleAsyncResult *simple; >> + SpiceVmcInputStream *self = SPICE_VMC_INPUT_STREAM(stream); >> + >> + g_return_val_if_fail(g_simple_async_result_is_valid(result, >> + G_OBJECT(self), >> + spice_vmc_input_stream_read_async), >> + -1); >> + >> + if (self->cancellable) { >> + g_cancellable_disconnect(self->cancellable, self->cancel_id); >> + g_clear_object(&self->cancellable); >> + } >> + >> + simple = (GSimpleAsyncResult *)result; >> + >> + if (g_simple_async_result_propagate_error(simple, error)) >> + return -1; >> + >> + return g_simple_async_result_get_op_res_gssize(simple); >> +} >> + >> +static gssize >> +spice_vmc_input_stream_read(GInputStream *stream, >> + void *buffer, >> + gsize count, >> + GCancellable *cancellable, >> + GError **error) >> +{ >> + g_return_val_if_reached(-1); >> +} >> + >> +static gssize >> +spice_vmc_input_stream_skip(GInputStream *stream, >> + gsize count, >> + GCancellable *cancellable, >> + GError **error) >> +{ >> + g_return_val_if_reached(-1); >> +} >> + >> +static gboolean >> +spice_vmc_input_stream_close(GInputStream *stream, >> + GCancellable *cancellable, >> + GError **error) >> +{ >> + SPICE_DEBUG("fake close"); >> + return TRUE; >> +} >> + >> +/* OUTPUT */ >> + >> +struct _SpiceVmcOutputStream >> +{ >> + GOutputStream parent_instance; >> + >> + SpiceChannel *channel; /* weak */ >> +}; >> + >> +struct _SpiceVmcOutputStreamClass >> +{ >> + GOutputStreamClass parent_class; >> +}; >> + >> +static void spice_vmc_output_stream_finalize (GObject *object); >> +static gssize spice_vmc_output_stream_write_fn (GOutputStream *stream, >> + const void *buffer, >> + gsize count, >> + GCancellable *cancellable, >> + GError **error); >> +static gssize spice_vmc_output_stream_write_finish (GOutputStream *stream, >> + GAsyncResult *result, >> + GError **error); >> +static void spice_vmc_output_stream_write_async (GOutputStream *stream, >> + const void *buffer, >> + gsize count, >> + int io_priority, >> + GCancellable *cancellable, >> + GAsyncReadyCallback callback, >> + gpointer user_data); >> + >> +G_DEFINE_TYPE_WITH_CODE(SpiceVmcOutputStream, spice_vmc_output_stream, G_TYPE_OUTPUT_STREAM,) > > G_DEFINE_TYPE ? ok > >> + >> + >> +static void >> +spice_vmc_output_stream_class_init(SpiceVmcOutputStreamClass *klass) >> +{ >> + GObjectClass *object_class; >> + GOutputStreamClass *ostream_class; >> + >> + object_class = G_OBJECT_CLASS(klass); >> + object_class->finalize = spice_vmc_output_stream_finalize; >> + >> + ostream_class = G_OUTPUT_STREAM_CLASS(klass); >> + ostream_class->write_fn = spice_vmc_output_stream_write_fn; >> + ostream_class->write_async = spice_vmc_output_stream_write_async; >> + ostream_class->write_finish = spice_vmc_output_stream_write_finish; >> +} >> + >> +static void >> +spice_vmc_output_stream_finalize(GObject *object) >> +{ >> + G_OBJECT_CLASS(spice_vmc_output_stream_parent_class)->finalize(object); >> +} > > Not needed. ok >> + >> +static void >> +spice_vmc_output_stream_init(SpiceVmcOutputStream *self) >> +{ >> +} >> + >> +static SpiceVmcOutputStream * >> +spice_vmc_output_stream_new(SpiceChannel *channel) >> +{ >> + SpiceVmcOutputStream *self; >> + >> + self = g_object_new(SPICE_TYPE_VMC_OUTPUT_STREAM, NULL); >> + self->channel = channel; >> + >> + return self; >> +} >> + >> +static gssize >> +spice_vmc_output_stream_write_fn(GOutputStream *stream, >> + const void *buffer, >> + gsize count, >> + GCancellable *cancellable, >> + GError **error) >> +{ >> + SpiceVmcOutputStream *self = SPICE_VMC_OUTPUT_STREAM(stream); >> + SpiceMsgOut *msg_out; >> + >> + msg_out = spice_msg_out_new(SPICE_CHANNEL(self->channel), >> + SPICE_MSGC_SPICEVMC_DATA); >> + spice_marshaller_add(msg_out->marshaller, buffer, count); >> + spice_msg_out_send(msg_out); >> + >> + return count; >> +} >> + >> +static gssize >> +spice_vmc_output_stream_write_finish(GOutputStream *stream, >> + GAsyncResult *simple, >> + GError **error) >> +{ >> + SpiceVmcOutputStream *self = SPICE_VMC_OUTPUT_STREAM(stream); >> + GSimpleAsyncResult *res = >> + g_simple_async_result_get_op_res_gpointer(G_SIMPLE_ASYNC_RESULT(simple)); >> + >> + SPICE_DEBUG("webdav write finish"); > > s/webdav/spicevmc > ok >> + return spice_vmc_write_finish(self->channel, G_ASYNC_RESULT(res), error); >> +} >> + >> +static void >> +write_cb(GObject *source_object, >> + GAsyncResult *res, >> + gpointer user_data) >> +{ >> + GSimpleAsyncResult *simple = user_data; >> + >> + g_simple_async_result_set_op_res_gpointer(simple, res, NULL); >> + >> + g_simple_async_result_complete(simple); >> + g_object_unref(simple); >> +} >> + >> +static void >> +spice_vmc_output_stream_write_async(GOutputStream *stream, >> + const void *buffer, >> + gsize count, >> + int io_priority, >> + GCancellable *cancellable, >> + GAsyncReadyCallback callback, >> + gpointer user_data) >> +{ >> + SpiceVmcOutputStream *self = SPICE_VMC_OUTPUT_STREAM(stream); >> + GSimpleAsyncResult *simple; >> + >> + SPICE_DEBUG("webdav write async"); > > s/webdav/spicevmc > >> + /* an AsyncResult to forward async op to channel */ >> + simple = g_simple_async_result_new(G_OBJECT(self), callback, user_data, >> + spice_vmc_output_stream_write_async); >> + >> + spice_vmc_write_async(self->channel, buffer, count, >> + cancellable, write_cb, >> + simple); >> +} >> + >> +/* STREAM */ >> + >> +struct _SpiceVmcStream >> +{ >> + GIOStream parent_instance; >> + >> + SpiceChannel *channel; /* weak */ >> + SpiceVmcInputStream *in; >> + SpiceVmcOutputStream *out; >> +}; >> + >> +struct _SpiceVmcStreamClass >> +{ >> + GIOStreamClass parent_class; >> +}; >> + >> +static void spice_vmc_stream_finalize (GObject *object); >> +static GInputStream * spice_vmc_stream_get_input_stream (GIOStream *stream); >> +static GOutputStream * spice_vmc_stream_get_output_stream (GIOStream *stream); >> + >> +G_DEFINE_TYPE_WITH_CODE(SpiceVmcStream, spice_vmc_stream, G_TYPE_IO_STREAM,) >> + >> +static void >> +spice_vmc_stream_class_init(SpiceVmcStreamClass *klass) >> +{ >> + GObjectClass *object_class; >> + GIOStreamClass *iostream_class; >> + >> + object_class = G_OBJECT_CLASS(klass); >> + object_class->finalize = spice_vmc_stream_finalize; >> + >> + iostream_class = G_IO_STREAM_CLASS(klass); >> + iostream_class->get_input_stream = spice_vmc_stream_get_input_stream; >> + iostream_class->get_output_stream = spice_vmc_stream_get_output_stream; >> +} >> + >> +static void >> +spice_vmc_stream_finalize(GObject *object) >> +{ >> + SpiceVmcStream *self = SPICE_VMC_STREAM(object); >> + >> + g_clear_object(&self->in); >> + g_clear_object(&self->out); >> + >> + G_OBJECT_CLASS(spice_vmc_stream_parent_class)->finalize(object); >> +} >> + >> +static void >> +spice_vmc_stream_init(SpiceVmcStream *self) >> +{ >> +} >> + >> +SpiceVmcStream * >> +spice_vmc_stream_new(SpiceChannel *channel) >> +{ >> + SpiceVmcStream *self; >> + >> + self = g_object_new(SPICE_TYPE_VMC_STREAM, NULL); >> + self->channel = channel; > > Can't we create the output/input channels here to save the need for > SpiceVmcStream::channel? > That's not the way it is usually done in GIO code. >> + >> + return self; >> +} >> + >> +static GInputStream * >> +spice_vmc_stream_get_input_stream(GIOStream *stream) >> +{ >> + SpiceVmcStream *self = SPICE_VMC_STREAM(stream); >> + >> + if (!self->in) >> + self->in = spice_vmc_input_stream_new(); >> + >> + return G_INPUT_STREAM(self->in); >> +} >> + >> +static GOutputStream * >> +spice_vmc_stream_get_output_stream(GIOStream *stream) >> +{ >> + SpiceVmcStream *self = SPICE_VMC_STREAM(stream); >> + >> + if (!self->out) >> + self->out = spice_vmc_output_stream_new(self->channel); >> + >> + return G_OUTPUT_STREAM(self->out); >> +} >> diff --git a/gtk/vmcstream.h b/gtk/vmcstream.h >> new file mode 100644 >> index 0000000..1316b77 >> --- /dev/null >> +++ b/gtk/vmcstream.h >> @@ -0,0 +1,81 @@ >> +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ >> +/* >> + Copyright (C) 2013 Red Hat, Inc. > > 2014 > >> + >> + 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.1 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/>. >> +*/ >> +#ifndef __SPICE_VMC_STREAM_H__ >> +#define __SPICE_VMC_STREAM_H__ >> + >> +#include <gio/gio.h> >> + >> +#include "spice-types.h" >> + >> +G_BEGIN_DECLS >> + >> +#define SPICE_TYPE_VMC_INPUT_STREAM (spice_vmc_input_stream_get_type ()) >> +#define SPICE_VMC_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), SPICE_TYPE_VMC_INPUT_STREAM, SpiceVmcInputStream)) >> +#define SPICE_VMC_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), SPICE_TYPE_VMC_INPUT_STREAM, SpiceVmcInputStreamClass)) >> +#define SPICE_IS_VMC_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), SPICE_TYPE_VMC_INPUT_STREAM)) >> +#define SPICE_IS_VMC_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), SPICE_TYPE_VMC_INPUT_STREAM)) >> +#define SPICE_VMC_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), SPICE_TYPE_VMC_INPUT_STREAM, SpiceVmcInputStreamClass)) >> + >> +typedef struct _SpiceVmcInputStreamClass SpiceVmcInputStreamClass; >> +typedef struct _SpiceVmcInputStream SpiceVmcInputStream; >> + >> +GType spice_vmc_input_stream_get_type (void) G_GNUC_CONST; >> +void spice_vmc_input_stream_co_data (SpiceVmcInputStream *input, >> + const gpointer data, >> + gsize size); >> + >> +void spice_vmc_input_stream_read_all_async(GInputStream *stream, >> + void *buffer, >> + gsize count, >> + int io_priority, >> + GCancellable *cancellable, >> + GAsyncReadyCallback callback, >> + gpointer user_data); >> +gssize spice_vmc_input_stream_read_all_finish(GInputStream *stream, >> + GAsyncResult *result, >> + GError **error); >> + >> + >> +#define SPICE_TYPE_VMC_OUTPUT_STREAM (spice_vmc_output_stream_get_type ()) >> +#define SPICE_VMC_OUTPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), SPICE_TYPE_VMC_OUTPUT_STREAM, SpiceVmcOutputStream)) >> +#define SPICE_VMC_OUTPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), SPICE_TYPE_VMC_OUTPUT_STREAM, SpiceVmcOutputStreamClass)) >> +#define SPICE_IS_VMC_OUTPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), SPICE_TYPE_VMC_OUTPUT_STREAM)) >> +#define SPICE_IS_VMC_OUTPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), SPICE_TYPE_VMC_OUTPUT_STREAM)) >> +#define SPICE_VMC_OUTPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), SPICE_TYPE_VMC_OUTPUT_STREAM, SpiceVmcOutputStreamClass)) >> + >> +typedef struct _SpiceVmcOutputStreamClass SpiceVmcOutputStreamClass; >> +typedef struct _SpiceVmcOutputStream SpiceVmcOutputStream; >> + >> +GType spice_vmc_output_stream_get_type (void) G_GNUC_CONST; >> + >> +#define SPICE_TYPE_VMC_STREAM (spice_vmc_stream_get_type ()) >> +#define SPICE_VMC_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), SPICE_TYPE_VMC_STREAM, SpiceVmcStream)) >> +#define SPICE_VMC_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), SPICE_TYPE_VMC_STREAM, SpiceVmcStreamClass)) >> +#define SPICE_IS_VMC_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), SPICE_TYPE_VMC_STREAM)) >> +#define SPICE_IS_VMC_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), SPICE_TYPE_VMC_STREAM)) >> +#define SPICE_VMC_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), SPICE_TYPE_VMC_STREAM, SpiceVmcStreamClass)) >> + >> +typedef struct _SpiceVmcStreamClass SpiceVmcStreamClass; >> +typedef struct _SpiceVmcStream SpiceVmcStream; >> + >> +GType spice_vmc_stream_get_type (void) G_GNUC_CONST; >> +SpiceVmcStream* spice_vmc_stream_new (SpiceChannel *channel); >> + >> +G_END_DECLS >> + >> +#endif /* __SPICE_VMC_STREAM_H__ */ >> -- >> 1.8.4.2 >> >> _______________________________________________ >> Spice-devel mailing list >> Spice-devel@xxxxxxxxxxxxxxxxxxxxx >> http://lists.freedesktop.org/mailman/listinfo/spice-devel -- Marc-André Lureau _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel