[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