[Spice-devel] [PATCH spice-gtk 2/2] Add a USB device selection widget.
Christophe Fergeau
cfergeau at redhat.com
Thu Jan 26 07:40:18 PST 2012
I had a quick glance through this, this looks good apart from a few nits.
I'll let elmarco comment on the widget part though since he is more
experienced than I am in this.
Christophe
On Thu, Jan 26, 2012 at 04:24:56PM +0100, Hans de Goede wrote:
> This patch adds a SpiceUsbDeviceWidget which apps can use to easily
> add an UI to select USB devices to redirect (or unredirect).
>
> See spicy for an example usage.
>
> Signed-off-by: Hans de Goede <hdegoede at redhat.com>
> ---
> doc/reference/spice-gtk-docs.xml | 1 +
> doc/reference/spice-gtk-sections.txt | 18 ++
> doc/reference/spice-gtk.types | 2 +
> gtk/Makefile.am | 3 +
> gtk/map-file | 2 +
> gtk/spicy.c | 60 +++++-
> gtk/usb-device-widget.c | 392 ++++++++++++++++++++++++++++++++++
> gtk/usb-device-widget.h | 83 +++++++
> 8 files changed, 551 insertions(+), 10 deletions(-)
> create mode 100644 gtk/usb-device-widget.c
> create mode 100644 gtk/usb-device-widget.h
>
> diff --git a/doc/reference/spice-gtk-docs.xml b/doc/reference/spice-gtk-docs.xml
> index 2b4336d..82cdce8 100644
> --- a/doc/reference/spice-gtk-docs.xml
> +++ b/doc/reference/spice-gtk-docs.xml
> @@ -42,6 +42,7 @@
> <title>GTK Widget, from spice-client-gtk</title>
> <xi:include href="xml/spice-gtk-session.xml"/>
> <xi:include href="xml/spice-widget.xml"/>
> + <xi:include href="xml/usb-device-widget.xml"/>
> </chapter>
>
> <chapter id="application-support">
> diff --git a/doc/reference/spice-gtk-sections.txt b/doc/reference/spice-gtk-sections.txt
> index 870352d..5e3af99 100644
> --- a/doc/reference/spice-gtk-sections.txt
> +++ b/doc/reference/spice-gtk-sections.txt
> @@ -342,6 +342,24 @@ SpiceDisplayPrivate
> </SECTION>
>
> <SECTION>
> +<FILE>usb-device-widget</FILE>
> +<TITLE>SpiceUsbDeviceWidget</TITLE>
> +SpiceUsbDeviceWidget
> +SpiceUsbDeviceWidgetClass
> +<SUBSECTION>
> +spice_usb_device_widget_new
> +<SUBSECTION Standard>
> +SPICE_USB_DEVICE_WIDGET
> +SPICE_IS_USB_DEVICE_WIDGET
> +spice_usb_device_widget_get_type
> +SPICE_USB_DEVICE_WIDGET_CLASS
> +SPICE_IS_USB_DEVICE_WIDGET_CLASS
> +SPICE_USB_DEVICE_WIDGET_GET_CLASS
> +<SUBSECTION Private>
> +SpiceUsbDeviceWidgetPrivate
> +</SECTION>
> +
> +<SECTION>
> <FILE>spice-util</FILE>
> spice_util_set_debug
> spice_util_get_version_string
> diff --git a/doc/reference/spice-gtk.types b/doc/reference/spice-gtk.types
> index a88ece1..ff80277 100644
> --- a/doc/reference/spice-gtk.types
> +++ b/doc/reference/spice-gtk.types
> @@ -18,6 +18,7 @@
> #include "spice-grabsequence.h"
> #include "smartcard-manager.h"
> #include "usb-device-manager.h"
> +#include "usb-device-widget.h"
>
> spice_audio_get_type
> spice_channel_event_get_type
> @@ -40,3 +41,4 @@ spice_session_verify_get_type
> spice_usbredir_channel_get_type
> spice_usb_device_get_type
> spice_usb_device_manager_get_type
> +spice_usb_device_widget_get_type
> diff --git a/gtk/Makefile.am b/gtk/Makefile.am
> index f919aab..404316c 100644
> --- a/gtk/Makefile.am
> +++ b/gtk/Makefile.am
> @@ -111,10 +111,12 @@ SPICE_GTK_SOURCES_COMMON = \
> vncdisplaykeymap.h \
> spice-grabsequence.c \
> spice-grabsequence.h \
> + usb-device-widget.c \
> $(NULL)
>
> nodist_SPICE_GTK_SOURCES_COMMON = \
> spice-widget-enums.c \
> + spice-marshal.c \
> $(NULL)
>
> if WITH_X11
> @@ -145,6 +147,7 @@ libspice_client_gtkinclude_HEADERS = \
> spice-gtk-session.h \
> spice-widget.h \
> spice-grabsequence.h \
> + usb-device-widget.h \
> $(NULL)
>
> nodist_libspice_client_gtkinclude_HEADERS = \
> diff --git a/gtk/map-file b/gtk/map-file
> index 4b470bb..38f7c26 100644
> --- a/gtk/map-file
> +++ b/gtk/map-file
> @@ -91,6 +91,8 @@ spice_usb_device_manager_get;
> spice_usb_device_manager_get_devices;
> spice_usb_device_manager_get_type;
> spice_usb_device_manager_is_device_connected;
> +spice_usb_device_widget_get_type;
> +spice_usb_device_widget_new;
> spice_usbredir_channel_get_type;
> spice_util_get_debug;
> spice_util_get_version_string;
> diff --git a/gtk/spicy.c b/gtk/spicy.c
> index e37ce82..1dfa6df 100644
> --- a/gtk/spicy.c
> +++ b/gtk/spicy.c
> @@ -1,4 +1,3 @@
> -/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> /*
> Copyright (C) 2010-2011 Red Hat, Inc.
>
> @@ -38,6 +37,7 @@
> #include "spice-common.h"
> #include "spice-cmdline.h"
> #include "spice-option.h"
> +#include "usb-device-widget.h"
>
> /* config */
> static gboolean fullscreen = false;
> @@ -98,10 +98,10 @@ static void connection_disconnect(spice_connection *conn);
> static void connection_destroy(spice_connection *conn);
> static void resolution_fullscreen(struct spice_window *win);
> static void resolution_restore(struct spice_window *win);
> -static void auto_connect_failed(SpiceUsbDeviceManager *manager,
> - SpiceUsbDevice *device,
> - GError *error,
> - gpointer data);
> +static void usb_connect_failed(GObject *object,
> + SpiceUsbDevice *device,
> + GError *error,
> + gpointer data);
> static gboolean is_gtk_session_property(const gchar *property);
>
> /* ------------------------------------------------------------------ */
> @@ -417,6 +417,35 @@ static void menu_cb_remove_smartcard(GtkAction *action, void *data)
> }
> #endif
>
> +#ifdef USE_USBREDIR
> +static void menu_cb_select_usb_devices(GtkAction *action, void *data)
> +{
> + GtkWidget *dialog, *area, *usb_device_widget;
> + struct spice_window *win = data;
> +
> + /* Create the widgets */
> + dialog = gtk_dialog_new_with_buttons(
> + _("Select USB Devices for redirection"),
The capitalization of 'Devices' is a bit odd here
> + GTK_WINDOW(win->toplevel),
> + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
> + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
> + NULL);
> + gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
> + area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
> +
> + usb_device_widget = spice_usb_device_widget_new(win->conn->session,
> + "%s %s");
> + g_signal_connect(usb_device_widget, "connect-failed",
> + G_CALLBACK(usb_connect_failed), NULL);
> + gtk_box_pack_start(GTK_BOX(area), usb_device_widget, TRUE, TRUE, 5);
> +
> + /* show and run */
> + gtk_widget_show_all(dialog);
> + gtk_dialog_run(GTK_DIALOG(dialog));
> + gtk_widget_destroy(dialog);
> +}
> +#endif
> +
> static void menu_cb_bool_prop(GtkToggleAction *action, gpointer data)
> {
> struct spice_window *win = data;
> @@ -708,6 +737,14 @@ static const GtkActionEntry entries[] = {
> },{
> #endif
>
> +#ifdef USE_USBREDIR
> + .name = "SelectUsbDevices",
> + .label = N_("_Select USB Devices for redirection"),
> + .callback = G_CALLBACK(menu_cb_select_usb_devices),
> + .accelerator = "<shift>F10",
> + },{
> +#endif
> +
> /* Help menu */
> .name = "About",
> .stock_id = GTK_STOCK_ABOUT,
> @@ -797,6 +834,9 @@ static char ui_xml[] =
> " <menuitem action='InsertSmartcard'/>\n"
> " <menuitem action='RemoveSmartcard'/>\n"
> #endif
> +#ifdef USE_USBREDIR
> +" <menuitem action='SelectUsbDevices'/>\n"
> +#endif
> " </menu>\n"
> " <menu action='OptionMenu'>\n"
> " <menuitem action='grab-keyboard'/>\n"
> @@ -1522,7 +1562,7 @@ static spice_connection *connection_new(void)
> manager = spice_usb_device_manager_get(conn->session, NULL);
> if (manager) {
> g_signal_connect(manager, "auto-connect-failed",
> - G_CALLBACK(auto_connect_failed), NULL);
> + G_CALLBACK(usb_connect_failed), NULL);
> }
>
> connections++;
> @@ -1611,10 +1651,10 @@ signal_handler(int signum)
> g_main_loop_quit(mainloop);
> }
>
> -static void auto_connect_failed(SpiceUsbDeviceManager *manager,
> - SpiceUsbDevice *device,
> - GError *error,
> - gpointer data)
> +static void usb_connect_failed(GObject *object,
> + SpiceUsbDevice *device,
> + GError *error,
> + gpointer data)
> {
> GtkWidget *dialog;
>
> diff --git a/gtk/usb-device-widget.c b/gtk/usb-device-widget.c
> new file mode 100644
> index 0000000..1c69c0a
> --- /dev/null
> +++ b/gtk/usb-device-widget.c
> @@ -0,0 +1,392 @@
> +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> +/*
> + Copyright (C) 2012 Red Hat, Inc.
> +
> + Red Hat Authors:
> + Hans de Goede <hdegoede at 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"
> +#include <glib/gi18n.h>
> +#include "spice-client.h"
> +#include "spice-marshal.h"
> +#include "usb-device-widget.h"
> +
> +/**
> + * SECTION:usb-device-widget
> + * @short_description: USB device selection widget
> + * @title: Spice USB device selection widget
> + * @section_id:
> + * @see_also:
> + * @stability: Stable
> + * @include: usb-device-widget.h
> + *
> + * #SpiceUsbDeviceWidget is a gtk widget which apps can use to easily
> + * add an UI to select USB devices to redirect (or unredirect).
> + */
> +
> +/* ------------------------------------------------------------------ */
> +/* Prototypes for callbacks */
> +static void device_added_cb(SpiceUsbDeviceManager *manager,
> + SpiceUsbDevice *device, gpointer user_data);
> +static void device_removed_cb(SpiceUsbDeviceManager *manager,
> + SpiceUsbDevice *device, gpointer user_data);
> +
> +/* ------------------------------------------------------------------ */
> +/* gobject glue */
> +
> +#define SPICE_USB_DEVICE_WIDGET_GET_PRIVATE(obj) \
> + (G_TYPE_INSTANCE_GET_PRIVATE((obj), SPICE_TYPE_USB_DEVICE_WIDGET, \
> + SpiceUsbDeviceWidgetPrivate))
> +
> +enum {
> + PROP_0,
> + PROP_SESSION,
> + PROP_DEVICE_FORMAT_STRING,
> +};
> +
> +enum {
> + CONNECT_FAILED,
> + LAST_SIGNAL,
> +};
> +
> +struct _SpiceUsbDeviceWidgetPrivate {
> + SpiceSession *session;
> + gchar *device_format_string;
> + SpiceUsbDeviceManager *manager;
> +};
> +
> +static guint signals[LAST_SIGNAL] = { 0, };
> +
> +G_DEFINE_TYPE(SpiceUsbDeviceWidget, spice_usb_device_widget, GTK_TYPE_VBOX);
> +
> +static void spice_usb_device_widget_get_property(GObject *gobject,
> + guint prop_id,
> + GValue *value,
> + GParamSpec *pspec)
> +{
> + SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(gobject);
> + SpiceUsbDeviceWidgetPrivate *priv = self->priv;
> +
> + switch (prop_id) {
> + case PROP_SESSION:
> + g_value_set_object(value, priv->session);
> + break;
> + case PROP_DEVICE_FORMAT_STRING:
> + g_value_set_string(value, priv->device_format_string);
> + break;
> + default:
> + G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, pspec);
> + break;
> + }
> +}
> +
> +static void spice_usb_device_widget_set_property(GObject *gobject,
> + guint prop_id,
> + const GValue *value,
> + GParamSpec *pspec)
> +{
> + SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(gobject);
> + SpiceUsbDeviceWidgetPrivate *priv = self->priv;
> +
> + switch (prop_id) {
> + case PROP_SESSION:
> + priv->session = g_object_ref(g_value_get_object(value));
> + break;
> + case PROP_DEVICE_FORMAT_STRING:
> + priv->device_format_string = g_strdup(g_value_get_string(value));
> + break;
There are g_value_dup_object and g_value_dup_string for that purpose
> + default:
> + G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, pspec);
> + break;
> + }
> +}
> +
> +static GObject *spice_usb_device_widget_constructor(
> + GType gtype, guint n_properties, GObjectConstructParam *properties)
> +{
> + GObject *obj;
> + SpiceUsbDeviceWidget *self;
> + SpiceUsbDeviceWidgetPrivate *priv;
> + const gchar *err_msg = NULL;
> + GPtrArray *devices = NULL;
> + GError *err = NULL;
> + GtkWidget *label;
> + gboolean enabled;
> + int i;
> +
> + {
> + /* Always chain up to the parent constructor */
> + GObjectClass *parent_class;
> + parent_class = G_OBJECT_CLASS(spice_usb_device_widget_parent_class);
> + obj = parent_class->constructor(gtype, n_properties, properties);
> + }
> +
> + self = SPICE_USB_DEVICE_WIDGET(obj);
> + priv = self->priv;
> + if (!priv->session)
> + g_error("SpiceUsbDeviceWidget constructed without a session");
> +
> + g_object_get(G_OBJECT(priv->session), "enable-usbredir", &enabled, NULL);
> + if (!enabled)
> + err_msg = _("USB redirection is disabled");
> +
> + if (!err_msg && !spice_session_has_channel_type(priv->session,
> + SPICE_CHANNEL_USBREDIR))
> + err_msg = _("The connected VM is not configured for USB redirection");
> +
> + if (!err_msg) {
> + priv->manager = spice_usb_device_manager_get(priv->session, &err);
> + if (!err) {
> + g_signal_connect(priv->manager, "device-added",
> + G_CALLBACK(device_added_cb), self);
> + g_signal_connect(priv->manager, "device-removed",
> + G_CALLBACK(device_removed_cb), self);
> + devices = spice_usb_device_manager_get_devices(priv->manager);
> + } else
> + err_msg = err->message;
> + }
> +
> + if (err_msg) {
> + label = gtk_label_new(err_msg);
> + gtk_box_pack_start(GTK_BOX(self), label, TRUE, TRUE, 5);
> + g_clear_error(&err);
> + return obj;
> + }
> +
> + label = gtk_label_new(_("Select USB devices to redirect"));
> + gtk_box_pack_start(GTK_BOX(self), label, TRUE, TRUE, 5);
> +
> + for (i = 0; i < devices->len; i++)
> + device_added_cb(NULL, g_ptr_array_index(devices, i), self);
> +
> + g_ptr_array_unref(devices);
> +
> + return obj;
> +}
> +
> +static void spice_usb_device_widget_finalize(GObject *object)
> +{
> + SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(object);
> + SpiceUsbDeviceWidgetPrivate *priv = self->priv;
> +
> + if (priv->manager) {
> + g_signal_handlers_disconnect_by_func(priv->manager,
> + device_added_cb, self);
> + g_signal_handlers_disconnect_by_func(priv->manager,
> + device_removed_cb, self);
> + }
> + g_object_unref(priv->session);
> + g_free(priv->device_format_string);
> +}
> +
> +static void spice_usb_device_widget_class_init(
> + SpiceUsbDeviceWidgetClass *klass)
> +{
> + GObjectClass *gobject_class = (GObjectClass *)klass;
> + GParamSpec *pspec;
> +
> + g_type_class_add_private (klass, sizeof (SpiceUsbDeviceWidgetPrivate));
> +
> + gobject_class->constructor = spice_usb_device_widget_constructor;
> + gobject_class->finalize = spice_usb_device_widget_finalize;
> + gobject_class->get_property = spice_usb_device_widget_get_property;
> + gobject_class->set_property = spice_usb_device_widget_set_property;
> +
> + /**
> + * SpiceUsbDeviceWidget:session:
> + *
> + * #SpiceSession this #SpiceUsbDeviceWidget is associated with
> + *
> + **/
> + pspec = g_param_spec_object("session",
> + "Session",
> + "SpiceSession",
> + SPICE_TYPE_SESSION,
> + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
> + G_PARAM_STATIC_STRINGS);
> + g_object_class_install_property(gobject_class, PROP_SESSION, pspec);
> +
> + /**
> + * SpiceUsbDeviceWidget:device-format-string:
> + *
> + * Format string to pass to spice_usb_device_get_description() for getting
> + * the device USB descriptions.
> + */
> + pspec = g_param_spec_string("device-format-string",
> + "Device format string",
> + "Format string for device description",
> + NULL,
> + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
> + G_PARAM_STATIC_STRINGS);
> + g_object_class_install_property(gobject_class, PROP_DEVICE_FORMAT_STRING,
> + pspec);
> +
> + /**
> + * SpiceUsbDeviceWidget::connect-failed:
> + * @widget: The #SpiceUsbDeviceWidget that emitted the signal
> + * @device: #SpiceUsbDevice boxed object corresponding to the added device
> + * @error: #GError describing the reason why the connect failed
> + *
> + * The #SpiceUsbDeviceWidget::connect-failed signal is emitted whenever
> + * the user has requested for a device to be redirected and this has
> + * failed.
> + **/
> + signals[CONNECT_FAILED] =
> + g_signal_new("connect-failed",
> + G_OBJECT_CLASS_TYPE(gobject_class),
> + G_SIGNAL_RUN_FIRST,
> + G_STRUCT_OFFSET(SpiceUsbDeviceWidgetClass, connect_failed),
> + NULL, NULL,
> + g_cclosure_user_marshal_VOID__BOXED_BOXED,
> + G_TYPE_NONE,
> + 2,
> + SPICE_TYPE_USB_DEVICE,
> + G_TYPE_ERROR);
> +}
> +
> +static void spice_usb_device_widget_init(SpiceUsbDeviceWidget *self)
> +{
> + self->priv = SPICE_USB_DEVICE_WIDGET_GET_PRIVATE(self);
> +}
> +
> +/* ------------------------------------------------------------------ */
> +/* public api */
> +
> +/**
> + * spice_usb_device_widget_new:
> + * @session: #SpiceSession for which to widget will control USB redirection
> + * @device_format_string: String passed to spice_usb_device_get_description()
> + *
> + * Returns: a new #SpiceUsbDeviceWidget instance
> + */
> +GtkWidget *spice_usb_device_widget_new(SpiceSession *session,
> + gchar *device_format_string)
device_format_string can be const.
> +{
> + return g_object_new(SPICE_TYPE_USB_DEVICE_WIDGET,
> + "session", session,
> + "device-format-string", device_format_string,
> + NULL);
> +}
> +
> +/* ------------------------------------------------------------------ */
> +/* callbacks */
> +typedef struct _connect_cb_data {
> + GtkWidget *check;
> + SpiceUsbDeviceWidget *self;
> +} connect_cb_data;
> +
> +static void connect_cb(GObject *gobject, GAsyncResult *res, gpointer user_data)
> +{
> + SpiceUsbDeviceManager *manager = SPICE_USB_DEVICE_MANAGER(gobject);
> + connect_cb_data *data = user_data;
> + SpiceUsbDeviceWidget *self = data->self;
> + SpiceUsbDeviceWidgetPrivate *priv = self->priv;
> + SpiceUsbDevice *device;
> + GError *err = NULL;
> + gchar *desc;
> +
> + spice_usb_device_manager_connect_device_finish(manager, res, &err);
> + if (err) {
> + device = g_object_get_data(G_OBJECT(data->check), "usb-device");
> + desc = spice_usb_device_get_description(device,
> + priv->device_format_string);
> + g_prefix_error(&err, "Could not redirect %s: ", desc);
> + g_free(desc);
> +
> + SPICE_DEBUG("%s", err->message);
> + g_signal_emit(self, signals[CONNECT_FAILED], 0, device, err);
> + g_error_free(err);
> +
> + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->check), FALSE);
> + }
> +
> + g_object_unref(data->check);
> + g_object_unref(data->self);
> + g_free(data);
> +}
> +
> +static void checkbox_clicked_cb(GtkWidget *check, gpointer user_data)
> +{
> + SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_data);
> + SpiceUsbDeviceWidgetPrivate *priv = self->priv;
> + SpiceUsbDevice *device;
> +
> + device = g_object_get_data(G_OBJECT(check), "usb-device");
> +
> + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check))) {
> + connect_cb_data *data = g_new(connect_cb_data, 1);
> + data->check = g_object_ref(check);
> + data->self = g_object_ref(self);
> + spice_usb_device_manager_connect_device_async(priv->manager,
> + device,
> + NULL,
> + connect_cb,
> + data);
> + } else {
> + spice_usb_device_manager_disconnect_device(priv->manager,
> + device);
> + }
> +}
> +
> +static void checkbox_usb_device_destroy_notify(gpointer data)
> +{
> + g_boxed_free(spice_usb_device_get_type(), data);
> +}
> +
> +static void device_added_cb(SpiceUsbDeviceManager *manager,
> + SpiceUsbDevice *device, gpointer user_data)
> +{
> + SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_data);
> + SpiceUsbDeviceWidgetPrivate *priv = self->priv;
> + GtkWidget *check;
> + gchar *desc;
> +
> + desc = spice_usb_device_get_description(device,
> + priv->device_format_string);
> +
> + check = gtk_check_button_new_with_label(desc);
> + g_object_set_data_full(
> + G_OBJECT(check), "usb-device",
> + g_boxed_copy(spice_usb_device_get_type(), device),
> + checkbox_usb_device_destroy_notify);
> + g_signal_connect(G_OBJECT(check), "clicked",
> + G_CALLBACK(checkbox_clicked_cb), self);
> +
> + if (spice_usb_device_manager_is_device_connected(priv->manager,
> + device))
> + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), TRUE);
> +
> + gtk_box_pack_start(GTK_BOX(self), check, TRUE, TRUE, 5);
> + gtk_widget_show(check);
> +
> + g_free(desc);
> +}
> +
> +static void destroy_widget_by_usb_device(GtkWidget *widget, gpointer user_data)
> +{
> + if (g_object_get_data(G_OBJECT(widget), "usb-device") == user_data)
> + gtk_widget_destroy(widget);
> +}
> +
> +static void device_removed_cb(SpiceUsbDeviceManager *manager,
> + SpiceUsbDevice *device, gpointer user_data)
> +{
> + SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_data);
> +
> + gtk_container_foreach(GTK_CONTAINER(self),
> + destroy_widget_by_usb_device, device);
> +}
> diff --git a/gtk/usb-device-widget.h b/gtk/usb-device-widget.h
> new file mode 100644
> index 0000000..1fb14d8
> --- /dev/null
> +++ b/gtk/usb-device-widget.h
> @@ -0,0 +1,83 @@
> +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> +/*
> + Copyright (C) 2012 Red Hat, Inc.
> +
> + Red Hat Authors:
> + Hans de Goede <hdegoede at 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_USB_DEVICE_WIDGET_H__
> +#define __SPICE_USB_DEVICE_WIDGET_H__
> +
> +#include <gtk/gtk.h>
> +#include "spice-client.h"
> +
> +G_BEGIN_DECLS
> +
> +#define SPICE_TYPE_USB_DEVICE_WIDGET (spice_usb_device_widget_get_type ())
> +#define SPICE_USB_DEVICE_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SPICE_TYPE_USB_DEVICE_WIDGET, SpiceUsbDeviceWidget))
> +#define SPICE_USB_DEVICE_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SPICE_TYPE_USB_DEVICE_WIDGET, SpiceUsbDeviceWidgetClass))
> +#define SPICE_IS_USB_DEVICE_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SPICE_TYPE_USB_DEVICE_WIDGET))
> +#define SPICE_IS_USB_DEVICE_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SPICE_TYPE_USB_DEVICE_WIDGET))
> +#define SPICE_USB_DEVICE_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SPICE_TYPE_USB_DEVICE_WIDGET, SpiceUsbDeviceWidgetClass))
> +
> +typedef struct _SpiceUsbDeviceWidget SpiceUsbDeviceWidget;
> +typedef struct _SpiceUsbDeviceWidgetClass SpiceUsbDeviceWidgetClass;
> +typedef struct _SpiceUsbDeviceWidgetPrivate SpiceUsbDeviceWidgetPrivate;
> +
> +/**
> + * SpiceUsbDeviceWidget:
> + * @parent: Parent instance.
> + *
> + * The #SpiceUsbDeviceWidget struct is opaque and should not be accessed directly.
> + */
> +struct _SpiceUsbDeviceWidget
> +{
> + GtkVBox parent;
> +
> + /*< private >*/
> + SpiceUsbDeviceWidgetPrivate *priv;
> + /* Do not add fields to this struct */
> +};
> +
> +/**
> + * SpiceUsbDeviceWidgetClass:
> + * @parent_class: Parent class.
> + * @connect_failed: Signal class handler for the #SpiceUsbDeviceWidget::connect-failed signal.
> + *
> + * Class structure for #SpiceUsbDeviceWidget.
> + */
> +struct _SpiceUsbDeviceWidgetClass
> +{
> + GtkVBoxClass parent_class;
> +
> + /* signals */
> + void (*connect_failed) (SpiceUsbDeviceWidget *widget,
> + SpiceUsbDevice *device, GError *error);
> + /*< private >*/
> + /*
> + * If adding fields to this struct, remove corresponding
> + * amount of padding to avoid changing overall struct size
> + */
> + gchar _spice_reserved[SPICE_RESERVED_PADDING];
> +};
> +
> +GType spice_usb_device_widget_get_type(void);
> +GtkWidget *spice_usb_device_widget_new(SpiceSession *session,
> + gchar *device_format_string);
> +
> +G_END_DECLS
> +
> +#endif /* __SPICE_USB_DEVICE_WIDGET_H__ */
> --
> 1.7.7.6
>
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/spice-devel
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://lists.freedesktop.org/archives/spice-devel/attachments/20120126/beece91e/attachment-0001.pgp>
More information about the Spice-devel
mailing list