Signed-off-by: Hans de Goede <hdego...@redhat.com> --- configure.ac | 1 + gtk/Makefile.am | 6 + gtk/channel-usbredir-priv.h | 43 ++++++ gtk/channel-usbredir.c | 353 +++++++++++++++++++++++++++++++++++++++++++ gtk/channel-usbredir.h | 60 ++++++++ gtk/map-file | 1 + gtk/spice-channel.c | 15 ++- gtk/spice-client-gtk.defs | 16 ++ gtk/spice-client.h | 1 + gtk/spice-option.c | 5 + gtk/spice-session.c | 29 ++++ 11 files changed, 529 insertions(+), 1 deletions(-) create mode 100644 gtk/channel-usbredir-priv.h create mode 100644 gtk/channel-usbredir.c create mode 100644 gtk/channel-usbredir.h
diff --git a/configure.ac b/configure.ac index 9fa2b20..98b91de 100644 --- a/configure.ac +++ b/configure.ac @@ -321,6 +321,7 @@ if test "x$enable_usbredir" = "xno"; then else PKG_CHECK_MODULES(GUDEV, gudev-1.0) PKG_CHECK_MODULES(LIBUSB, libusb-1.0 >= 1.0.9) + PKG_CHECK_MODULES(LIBUSBREDIRHOST, libusbredirhost >= 0.3.1) AC_DEFINE(USE_USBREDIR, [1], [Define if supporting usbredir proxying]) AM_CONDITIONAL(WITH_USBREDIR, true) fi diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 1b841e2..f11576c 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -74,6 +74,7 @@ SPICE_COMMON_CPPFLAGS = \ $(SMARTCARD_CFLAGS) \ $(GUDEV_CFLAGS) \ $(LIBUSB_CFLAGS) \ + $(LIBUSBREDIRHOST_CFLAGS) \ $(NULL) AM_CPPFLAGS = \ @@ -154,6 +155,7 @@ libspice_client_glib_2_0_la_LIBADD = \ $(SMARTCARD_LIBS) \ $(GUDEV_LIBS) \ $(LIBUSB_LIBS) \ + $(LIBUSBREDIRHOST_LIBS) \ $(NULL) if WITH_USBREDIR @@ -208,6 +210,8 @@ libspice_client_glib_2_0_la_SOURCES = \ channel-playback.c \ channel-record.c \ channel-smartcard.c \ + channel-usbredir.c \ + channel-usbredir-priv.h \ smartcard-manager.c \ smartcard-manager.h \ smartcard-manager-priv.h \ @@ -259,6 +263,7 @@ libspice_client_glibinclude_HEADERS = \ channel-playback.h \ channel-record.h \ channel-smartcard.h \ + channel-usbredir.h \ $(NULL) # file for API compatibility, but we don't want warning during our compilation @@ -518,6 +523,7 @@ glib_introspection_files = \ channel-playback.c \ channel-record.c \ channel-smartcard.c \ + channel-usbredir.c \ $(NULL) gtk_introspection_files = \ diff --git a/gtk/channel-usbredir-priv.h b/gtk/channel-usbredir-priv.h new file mode 100644 index 0000000..05988e1 --- /dev/null +++ b/gtk/channel-usbredir-priv.h @@ -0,0 +1,43 @@ +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + Copyright (C) 2011 Red Hat, Inc. + + Red Hat Authors: + Hans de Goede <hdego...@redhat.com> + + 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_CLIENT_USBREDIR_CHANNEL_PRIV_H__ +#define __SPICE_CLIENT_USBREDIR_CHANNEL_PRIV_H__ + +#include <gusb/gusb-context.h> +#include <gusb/gusb-device.h> + +#include "spice-client.h" + +G_BEGIN_DECLS + +gboolean spice_usbredir_channel_connect(SpiceUsbredirChannel *channel, + GUsbContext *context, + GUsbDevice *device, + GError **err); +void spice_usbredir_channel_disconnect(SpiceUsbredirChannel *channel); + +GUsbDevice *spice_usbredir_channel_get_device(SpiceUsbredirChannel *channel); + +void spice_usbredir_channel_do_write(SpiceUsbredirChannel *channel); + +G_END_DECLS + +#endif /* __SPICE_CLIENT_USBREDIR_CHANNEL_PRIV_H__ */ diff --git a/gtk/channel-usbredir.c b/gtk/channel-usbredir.c new file mode 100644 index 0000000..2baca5f --- /dev/null +++ b/gtk/channel-usbredir.c @@ -0,0 +1,353 @@ +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + Copyright 2010-2011 Red Hat, Inc. + + Red Hat Authors: + Hans de Goede <hdego...@redhat.com> + Richard Hughes <rhug...@redhat.com> + + 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 "config.h" + +#ifdef USE_USBREDIR +#include <usbredirhost.h> +#include <gusb/gusb-context-private.h> +#include <gusb/gusb-device-private.h> +#include "channel-usbredir-priv.h" +#endif + +#include "spice-client.h" +#include "spice-common.h" + +#include "spice-channel-priv.h" + +/* libusb_strerror is awaiting merging upstream */ +#define libusb_strerror(error) "unknown" + +/** + * SECTION:channel-usbredir + * @short_description: usb redirection + * @title: USB Redirection Channel + * @section_id: + * @stability: API Stable (channel in development) + * @include: channel-usbredir.h + * + * The Spice protocol defines a set of messages to redirect USB devices + * from the Spice client to the VM. This channel handles these messages. + */ + +#define SPICE_USBREDIR_CHANNEL_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE((obj), SPICE_TYPE_USBREDIR_CHANNEL, SpiceUsbredirChannelPrivate)) + +struct _SpiceUsbredirChannelPrivate { + int verbose; +#ifdef USE_USBREDIR + GUsbContext *context; + GUsbDevice *device; + struct usbredirhost *host; + /* To catch usbredirhost error messages and report them as a GError */ + GError **catch_error; + /* Data passed from channel handle msg to the usbredirhost read cb */ + const uint8_t *read_buf; + int read_buf_size; + SpiceMsgOut *msg_out; + gboolean up; +#endif +}; + +G_DEFINE_TYPE(SpiceUsbredirChannel, spice_usbredir_channel, SPICE_TYPE_CHANNEL) + +enum { + + SPICE_USBREDIR_LAST_SIGNAL, +}; + +static void spice_usbredir_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg); +static void spice_usbredir_channel_up(SpiceChannel *channel); +static void usbredir_handle_msg(SpiceChannel *channel, SpiceMsgIn *in); + +#ifdef USE_USBREDIR +static void usbredir_log(void *user_data, int level, const char *msg); +static int usbredir_read_callback(void *user_data, uint8_t *data, int count); +static int usbredir_write_callback(void *user_data, uint8_t *data, int count); +#endif + +/* ------------------------------------------------------------------ */ + +static void spice_usbredir_channel_init(SpiceUsbredirChannel *channel) +{ + SpiceUsbredirChannelPrivate *priv; + + channel->priv = SPICE_USBREDIR_CHANNEL_GET_PRIVATE(channel); + priv = channel->priv; + + memset(priv, 0, sizeof(SpiceUsbredirChannelPrivate)); +#ifdef USE_USBREDIR + if (spice_util_get_debug()) { + priv->verbose = usbredirparser_debug; + } else { + priv->verbose = usbredirparser_warning; + } +#endif +} + +static void spice_usbredir_channel_finalize(GObject *obj) +{ +#ifdef USE_USBREDIR + SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(obj); + + spice_usbredir_channel_disconnect(channel); +#endif + + if (G_OBJECT_CLASS(spice_usbredir_channel_parent_class)->finalize) + G_OBJECT_CLASS(spice_usbredir_channel_parent_class)->finalize(obj); +} + +static void spice_usbredir_channel_class_init(SpiceUsbredirChannelClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + SpiceChannelClass *channel_class = SPICE_CHANNEL_CLASS(klass); + + gobject_class->finalize = spice_usbredir_channel_finalize; + channel_class->handle_msg = spice_usbredir_handle_msg; + channel_class->channel_up = spice_usbredir_channel_up; + + g_type_class_add_private(klass, sizeof(SpiceUsbredirChannelPrivate)); +} + +static const spice_msg_handler usbredir_handlers[] = { + [ SPICE_MSG_SPICEVMC_DATA ] = usbredir_handle_msg, +}; + +GQuark spice_usbredir_channel_error_quark(void) +{ + static GQuark quark = 0; + if (!quark) + quark = g_quark_from_static_string ("spice-usbredir-channel-error-quark"); + return quark; +} + +#ifdef USE_USBREDIR + +/* ------------------------------------------------------------------ */ +/* private api */ + +gboolean spice_usbredir_channel_connect(SpiceUsbredirChannel *channel, + GUsbContext *context, + GUsbDevice *device, + GError **err) +{ + SpiceUsbredirChannelPrivate *priv = channel->priv; + libusb_device_handle *handle = NULL; + int rc; + + g_return_val_if_fail (err == NULL || *err == NULL, FALSE); + + spice_usbredir_channel_disconnect(channel); + + rc = libusb_open(_g_usb_device_get_device(device), &handle); + if (rc != 0) { + g_set_error(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED, + "Could not open usb device: %s [%i]", + libusb_strerror(rc), rc); + return FALSE; + } + + priv->catch_error = err; + priv->host = usbredirhost_open(_g_usb_context_get_context(context), + handle, usbredir_log, + usbredir_read_callback, + usbredir_write_callback, + channel, PACKAGE_STRING, priv->verbose, + usbredirhost_fl_write_cb_owns_buffer); + priv->catch_error = NULL; + if (!priv->host) { + return FALSE; + } + + priv->context = g_object_ref(context); + priv->device = g_object_ref(device); + + spice_channel_connect(SPICE_CHANNEL(channel)); + + return TRUE; +} + +void spice_usbredir_channel_disconnect(SpiceUsbredirChannel *channel) +{ + SpiceUsbredirChannelPrivate *priv = channel->priv; + + spice_channel_disconnect(SPICE_CHANNEL(channel), SPICE_CHANNEL_NONE); + priv->up = FALSE; + + if (priv->host) { + /* This also closes the libusb handle we passed to its _open */ + usbredirhost_close(priv->host); + priv->host = NULL; + g_clear_object(&priv->device); + g_clear_object(&priv->context); + } +} + +GUsbDevice *spice_usbredir_channel_get_device(SpiceUsbredirChannel *channel) +{ + return channel->priv->device; +} + +void spice_usbredir_channel_do_write(SpiceUsbredirChannel *channel) +{ + SpiceUsbredirChannelPrivate *priv = channel->priv; + + /* No recursion allowed! */ + g_return_if_fail(priv->msg_out == NULL); + + if (!priv->up || !usbredirhost_has_data_to_write(priv->host)) + return; + + priv->msg_out = spice_msg_out_new(SPICE_CHANNEL(channel), + SPICE_MSGC_SPICEVMC_DATA); + + /* Collect all pending writes in priv->msg_out->marshaller */ + usbredirhost_write_guest_data(priv->host); + + spice_msg_out_send(priv->msg_out); + spice_msg_out_unref(priv->msg_out); + priv->msg_out = NULL; +} + +/* ------------------------------------------------------------------ */ +/* callbacks (any context) */ + +static void usbredir_log(void *user_data, int level, const char *msg) +{ + SpiceUsbredirChannel *channel = user_data; + SpiceUsbredirChannelPrivate *priv = channel->priv; + + if (priv->catch_error && level == usbredirparser_error) { + g_set_error_literal(priv->catch_error, SPICE_CLIENT_ERROR, + SPICE_CLIENT_ERROR_FAILED, msg); + return; + } + + if (level > priv->verbose) { + return; + } + + switch(level) { + case usbredirparser_error: + g_critical(G_STRLOC " " "%s", msg); break; + case usbredirparser_warning: + g_warning(G_STRLOC " " "%s", msg); break; + case usbredirparser_info: + g_message(G_STRLOC " " "%s", msg); break; + default: + g_debug(G_STRLOC " " "%s", msg); break; + } +} + +static int usbredir_read_callback(void *user_data, uint8_t *data, int count) +{ + SpiceUsbredirChannel *channel = user_data; + SpiceUsbredirChannelPrivate *priv = channel->priv; + + if (priv->read_buf_size < count) { + count = priv->read_buf_size; + } + + memcpy(data, priv->read_buf, count); + + priv->read_buf_size -= count; + if (priv->read_buf_size) { + priv->read_buf += count; + } else { + priv->read_buf = NULL; + } + + return count; +} + +static void usbredir_free_write_cb_data(uint8_t *data, void *user_data) +{ + SpiceUsbredirChannel *channel = user_data; + SpiceUsbredirChannelPrivate *priv = channel->priv; + + usbredirhost_free_write_buffer(priv->host, data); +} + +static int usbredir_write_callback(void *user_data, uint8_t *data, int count) +{ + SpiceUsbredirChannel *channel = user_data; + SpiceUsbredirChannelPrivate *priv = channel->priv; + + spice_marshaller_add_ref_full(priv->msg_out->marshaller, data, count, + usbredir_free_write_cb_data, channel); + return count; +} + +#endif /* USE_USBREDIR */ + +/* --------------------------------------------------------------------- */ +/* coroutine context */ +static void spice_usbredir_handle_msg(SpiceChannel *c, SpiceMsgIn *msg) +{ + int type = spice_msg_in_type(msg); + SpiceChannelClass *parent_class; + + g_return_if_fail(type < SPICE_N_ELEMENTS(usbredir_handlers)); + + parent_class = SPICE_CHANNEL_CLASS(spice_usbredir_channel_parent_class); + + if (usbredir_handlers[type] != NULL) + usbredir_handlers[type](c, msg); + else if (parent_class->handle_msg) + parent_class->handle_msg(c, msg); + else + g_return_if_reached(); +} + +static void spice_usbredir_channel_up(SpiceChannel *c) +{ +#ifdef USE_USBREDIR + SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(c); + SpiceUsbredirChannelPrivate *priv = channel->priv; + + priv->up = TRUE; + /* Flush any pending writes */ + spice_usbredir_channel_do_write(channel); +#endif +} + +static void usbredir_handle_msg(SpiceChannel *c, SpiceMsgIn *in) +{ +#ifdef USE_USBREDIR + SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(c); + SpiceUsbredirChannelPrivate *priv = channel->priv; + int size; + uint8_t *buf; + + g_return_if_fail(priv->host != NULL); + + /* No recursion allowed! */ + g_return_if_fail(priv->read_buf == NULL); + + buf = spice_msg_in_raw(in, &size); + priv->read_buf = buf; + priv->read_buf_size = size; + + usbredirhost_read_guest_data(priv->host); + /* Send any acks, etc. which may be queued now */ + spice_usbredir_channel_do_write(channel); +#endif +} diff --git a/gtk/channel-usbredir.h b/gtk/channel-usbredir.h new file mode 100644 index 0000000..ca0b535 --- /dev/null +++ b/gtk/channel-usbredir.h @@ -0,0 +1,60 @@ +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + Copyright (C) 2011 Red Hat, Inc. + + Red Hat Authors: + Hans de Goede <hdego...@redhat.com> + + 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_CLIENT_USBREDIR_CHANNEL_H__ +#define __SPICE_CLIENT_USBREDIR_CHANNEL_H__ + +#include "spice-client.h" + +G_BEGIN_DECLS + +#define SPICE_TYPE_USBREDIR_CHANNEL (spice_usbredir_channel_get_type()) +#define SPICE_USBREDIR_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SPICE_TYPE_USBREDIR_CHANNEL, SpiceUsbredirChannel)) +#define SPICE_USBREDIR_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SPICE_TYPE_USBREDIR_CHANNEL, SpiceUsbredirChannelClass)) +#define SPICE_IS_USBREDIR_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SPICE_TYPE_USBREDIR_CHANNEL)) +#define SPICE_IS_USBREDIR_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SPICE_TYPE_USBREDIR_CHANNEL)) +#define SPICE_USBREDIR_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), SPICE_TYPE_USBREDIR_CHANNEL, SpiceUsbredirChannelClass)) + +typedef struct _SpiceUsbredirChannel SpiceUsbredirChannel; +typedef struct _SpiceUsbredirChannelClass SpiceUsbredirChannelClass; +typedef struct _SpiceUsbredirChannelPrivate SpiceUsbredirChannelPrivate; + +struct _SpiceUsbredirChannel { + SpiceChannel parent; + + /*< private >*/ + SpiceUsbredirChannelPrivate *priv; + /* Do not add fields to this struct */ +}; + +struct _SpiceUsbredirChannelClass { + SpiceChannelClass parent_class; + + /* signals */ + + /*< private >*/ + /* Do not add fields to this struct */ +}; + +GType spice_usbredir_channel_get_type(void); + +G_END_DECLS + +#endif /* __SPICE_CLIENT_USBREDIR_CHANNEL_H__ */ diff --git a/gtk/map-file b/gtk/map-file index f358066..669b5dd 100644 --- a/gtk/map-file +++ b/gtk/map-file @@ -74,6 +74,7 @@ spice_smartcard_manager_insert_card; spice_smartcard_manager_remove_card; spice_smartcard_reader_get_type; spice_smartcard_reader_is_software; +spice_usbredir_channel_get_type; spice_util_get_debug; spice_util_get_version_string; spice_util_set_debug; diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c index a89b75c..3a17204 100644 --- a/gtk/spice-channel.c +++ b/gtk/spice-channel.c @@ -1658,7 +1658,8 @@ const gchar* spice_channel_type_to_string(gint type) [ SPICE_CHANNEL_PLAYBACK ] = "playback", [ SPICE_CHANNEL_RECORD ] = "record", [ SPICE_CHANNEL_TUNNEL ] = "tunnel", - [ SPICE_CHANNEL_SMARTCARD ] = "smartcard" + [ SPICE_CHANNEL_SMARTCARD ] = "smartcard", + [ SPICE_CHANNEL_USBREDIR ] = "usbredir", }; const char *str = NULL; @@ -1717,6 +1718,18 @@ SpiceChannel *spice_channel_new(SpiceSession *s, int type, int id) break; } #endif +#ifdef USE_USBREDIR + case SPICE_CHANNEL_USBREDIR: { + gboolean enabled; + g_object_get(G_OBJECT(s), "enable-usbredir", &enabled, NULL); + if (!enabled) { + g_debug("usbredir channel is disabled, not creating it"); + return NULL; + } + gtype = SPICE_TYPE_USBREDIR_CHANNEL; + break; + } +#endif default: g_debug("unsupported channel kind: %s: %d", spice_channel_type_to_string(type), type); diff --git a/gtk/spice-client-gtk.defs b/gtk/spice-client-gtk.defs index f718c8c..513815e 100644 --- a/gtk/spice-client-gtk.defs +++ b/gtk/spice-client-gtk.defs @@ -77,6 +77,13 @@ (gtype-id "SPICE_TYPE_SMARTCARD_CHANNEL") ) +(define-object UsbredirChannel + (in-module "Spice") + (parent "SpiceChannel") + (c-name "SpiceUsbredirChannel") + (gtype-id "SPICE_TYPE_USBREDIR_CHANNEL") +) + ;; Enumerations and flags ... (define-enum DisplayKeyEvent @@ -649,3 +656,12 @@ ) + +;; From channel-usbredir.h + +(define-function spice_usbredir_channel_get_type + (c-name "spice_usbredir_channel_get_type") + (return-type "GType") +) + + diff --git a/gtk/spice-client.h b/gtk/spice-client.h index 885d81c..54284ce 100644 --- a/gtk/spice-client.h +++ b/gtk/spice-client.h @@ -39,6 +39,7 @@ #include "channel-playback.h" #include "channel-record.h" #include "channel-smartcard.h" +#include "channel-usbredir.h" #define SPICE_CLIENT_ERROR spice_client_error_quark() diff --git a/gtk/spice-option.c b/gtk/spice-option.c index 6c4e50c..4a2ba90 100644 --- a/gtk/spice-option.c +++ b/gtk/spice-option.c @@ -32,6 +32,7 @@ static char *host_subject = NULL; static char *smartcard_db = NULL; static char *smartcard_certificates = NULL; static gboolean smartcard = FALSE; +static gboolean disable_usbredir = FALSE; static void option_version(void) { @@ -69,6 +70,8 @@ GOptionGroup* spice_get_option_group(void) N_("Certificates to use for software smartcards (field=values separated by commas)"), N_("<certificates>") }, { "spice-smartcard-db", '\0', 0, G_OPTION_ARG_STRING, &smartcard_db, N_("Path to the local certificate database to use for software smartcard certificates"), N_("<certificate-db>") }, + { "spice-disable-usbredir", '\0', 0, G_OPTION_ARG_NONE, &disable_usbredir, + N_("Disable USB redirection support"), NULL }, { "spice-debug", '\0', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, option_debug, N_("Enable Spice-GTK debugging"), NULL }, @@ -127,4 +130,6 @@ void spice_set_session_option(SpiceSession *session) if (smartcard_db) g_object_set(session, "smartcard-db", smartcard_db, NULL); } + if (disable_usbredir) + g_object_set(session, "enable-usbredir", FALSE, NULL); } diff --git a/gtk/spice-session.c b/gtk/spice-session.c index d6100f6..b0bbb14 100644 --- a/gtk/spice-session.c +++ b/gtk/spice-session.c @@ -59,6 +59,10 @@ struct _SpiceSessionPrivate { * fallback to using a default database. */ char * smartcard_db; + + /* whether to enable USB redirection */ + gboolean usbredir; + GStrv disable_effects; gint color_depth; @@ -139,6 +143,7 @@ enum { PROP_SMARTCARD, PROP_SMARTCARD_CERTIFICATES, PROP_SMARTCARD_DB, + PROP_USBREDIR, PROP_DISABLE_EFFECTS, PROP_COLOR_DEPTH, }; @@ -160,6 +165,7 @@ static void spice_session_init(SpiceSession *session) SPICE_DEBUG("New session (compiled from package " PACKAGE_STRING ")"); s = session->priv = SPICE_SESSION_GET_PRIVATE(session); memset(s, 0, sizeof(*s)); + s->usbredir = TRUE; ring_init(&s->channels); cache_init(&s->images, "image"); @@ -393,6 +399,9 @@ static void spice_session_get_property(GObject *gobject, case PROP_SMARTCARD_DB: g_value_set_string(value, s->smartcard_db); break; + case PROP_USBREDIR: + g_value_set_boolean(value, s->usbredir); + break; case PROP_DISABLE_EFFECTS: g_value_set_boxed(value, s->disable_effects); break; @@ -479,6 +488,9 @@ static void spice_session_set_property(GObject *gobject, g_free(s->smartcard_db); s->smartcard_db = g_value_dup_string(value); break; + case PROP_USBREDIR: + s->usbredir = g_value_get_boolean(value); + break; case PROP_DISABLE_EFFECTS: g_strfreev(s->disable_effects); s->disable_effects = g_value_dup_boxed(value); @@ -788,6 +800,23 @@ static void spice_session_class_init(SpiceSessionClass *klass) G_PARAM_STATIC_STRINGS)); /** + * SpiceSession:enable-usbredir: + * + * If set to TRUE, the usbredir channel will be enabled and USB devices + * can be redirected to the guest + * + * Since: 0.7 + **/ + g_object_class_install_property + (gobject_class, PROP_USBREDIR, + g_param_spec_boolean("enable-usbredir", + "Enable USB device redirection", + "Forward USB devices to the SPICE server", + TRUE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** * SpiceSession::channel-new: * @session: the session that emitted the signal * @channel: the new #SpiceChannel -- 1.7.5.1 _______________________________________________ Spice-devel mailing list Spice-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/spice-devel