[Spice-devel] [PATCH spice-gtk 2/3] Add a USB device selection widget.

Marc-André Lureau marcandre.lureau at gmail.com
Mon Jan 30 10:00:13 PST 2012


Nice! tested & worked as expected, code looks fine.
ack

On Mon, Jan 30, 2012 at 2:33 PM, Hans de Goede <hdegoede at redhat.com> 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/spice-client-gtk.override        |    2 +
>  gtk/spicy.c                          |   60 +++++-
>  gtk/usb-device-widget.c              |  393 ++++++++++++++++++++++++++++++++++
>  gtk/usb-device-widget.h              |   83 +++++++
>  po/POTFILES.in                       |    1 +
>  10 files changed, 555 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 2cc0163..a5b6e29 100644
> --- a/gtk/Makefile.am
> +++ b/gtk/Makefile.am
> @@ -109,10 +109,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
> @@ -143,6 +145,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/spice-client-gtk.override b/gtk/spice-client-gtk.override
> index e393037..31e4f9e 100644
> --- a/gtk/spice-client-gtk.override
> +++ b/gtk/spice-client-gtk.override
> @@ -6,12 +6,14 @@ headers
>  #include "spice-widget.h"
>  #include "spice-gtk-session.h"
>  #include "spice-audio.h"
> +#include "usb-device-widget.h"
>  %%
>  modulename spice_client_gtk
>  %%
>  import gobject.GObject as PyGObject_Type
>  import gtk.DrawingArea as PyGtkDrawingArea_Type
>  import gtk.Widget as PyGtkWidget_Type
> +import gtk.VBox as PyGtkVBox_Type
>  %%
>  ignore-glob
>   *_get_type
> diff --git a/gtk/spicy.c b/gtk/spicy.c
> index e37ce82..c002a5e 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"),
> +                    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..7a57e27
> --- /dev/null
> +++ b/gtk/usb-device-widget.c
> @@ -0,0 +1,393 @@
> +/* -*- 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_value_dup_object(value);
> +        break;
> +    case PROP_DEVICE_FORMAT_STRING:
> +        priv->device_format_string = g_value_dup_string(value);
> +        break;
> +    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,
> +                                       const gchar     *device_format_string)
> +{
> +    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);
> +
> +    if (spice_usb_device_manager_is_device_connected(priv->manager,
> +                                                     device))
> +        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), TRUE);
> +
> +    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);
> +
> +    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..27ec795
> --- /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,
> +                                       const gchar     *device_format_string);
> +
> +G_END_DECLS
> +
> +#endif /* __SPICE_USB_DEVICE_WIDGET_H__ */
> diff --git a/po/POTFILES.in b/po/POTFILES.in
> index b008d10..3c87ed1 100644
> --- a/po/POTFILES.in
> +++ b/po/POTFILES.in
> @@ -11,3 +11,4 @@ gtk/spice-option.c
>  gtk/spicy-stats.c
>  gtk/spicy.c
>  gtk/usb-device-manager.c
> +gtk/usb-device-widget.c
> --
> 1.7.7.6
>
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/spice-devel



-- 
Marc-André Lureau


More information about the Spice-devel mailing list