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); +} 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. + + 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); +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,) + + +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); +} + +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) +{ + 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); + 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 + "/%" 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); + 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); + 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); +} + +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); +} + +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,) + + +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); +} + +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"); + 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"); + /* 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; + + 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. + + 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