While working on usb redirection support for spice-gtk I needed some
code to integrate libusb into glib's mainloop amongst other things. I ended
up borrowing code from colord for this. Richard (the colord author) and I
quickly agreed that doing generic glib bindings for libusb is a good idea,
akin to the gudev bindings for libudev we've called our WIP on this gusb:
https://gitorious.org/gusb

Since this very much is a WIP, the API is nowere near stable, so for now
we bundle a copy of this code with spice-gtk. When gusb has an official
release out the door with a stable API we should switch to that.

Signed-off-by: Hans de Goede <hdego...@redhat.com>
---
 configure.ac                    |   16 ++
 gtk/Makefile.am                 |   23 +++
 gtk/gusb/README.txt             |    5 +
 gtk/gusb/gusb-context-private.h |   35 ++++
 gtk/gusb/gusb-context.c         |  292 +++++++++++++++++++++++++++++++
 gtk/gusb/gusb-context.h         |   63 +++++++
 gtk/gusb/gusb-device-list.c     |  358 +++++++++++++++++++++++++++++++++++++++
 gtk/gusb/gusb-device-list.h     |   72 ++++++++
 gtk/gusb/gusb-device-private.h  |   34 ++++
 gtk/gusb/gusb-device.c          |  232 +++++++++++++++++++++++++
 gtk/gusb/gusb-device.h          |   71 ++++++++
 gtk/gusb/gusb-marshal.h         |    7 +
 gtk/gusb/gusb-source.c          |  309 +++++++++++++++++++++++++++++++++
 gtk/gusb/gusb-source.h          |   58 +++++++
 gtk/spice-marshal.txt           |    1 +
 15 files changed, 1576 insertions(+), 0 deletions(-)
 create mode 100644 gtk/gusb/README.txt
 create mode 100644 gtk/gusb/gusb-context-private.h
 create mode 100644 gtk/gusb/gusb-context.c
 create mode 100644 gtk/gusb/gusb-context.h
 create mode 100644 gtk/gusb/gusb-device-list.c
 create mode 100644 gtk/gusb/gusb-device-list.h
 create mode 100644 gtk/gusb/gusb-device-private.h
 create mode 100644 gtk/gusb/gusb-device.c
 create mode 100644 gtk/gusb/gusb-device.h
 create mode 100644 gtk/gusb/gusb-marshal.h
 create mode 100644 gtk/gusb/gusb-source.c
 create mode 100644 gtk/gusb/gusb-source.h

diff --git a/configure.ac b/configure.ac
index e60ed78..9fa2b20 100644
--- a/configure.ac
+++ b/configure.ac
@@ -310,6 +310,21 @@ else
   AM_CONDITIONAL(WITH_SMARTCARD, true)
 fi
 
+AC_ARG_ENABLE([usbredir],
+  AS_HELP_STRING([--enable-usbredir=@<:@yes/no@:>@],
+                 [Enable usbredir support @<:@default=yes@:>@]),
+  [],
+  [enable_usbredir="yes"])
+
+if test "x$enable_usbredir" = "xno"; then
+  AM_CONDITIONAL(WITH_USBREDIR, false)
+else
+  PKG_CHECK_MODULES(GUDEV, gudev-1.0)
+  PKG_CHECK_MODULES(LIBUSB, libusb-1.0 >= 1.0.9)
+  AC_DEFINE(USE_USBREDIR, [1], [Define if supporting usbredir proxying])
+  AM_CONDITIONAL(WITH_USBREDIR, true)
+fi
+
 AC_ARG_WITH([coroutine],
   AS_HELP_STRING([--with-coroutine=@<:@ucontext/gthread/winfiber/auto@:>@],
                  [use ucontext or GThread for coroutines 
@<:@default=auto@:>@]),
@@ -544,6 +559,7 @@ AC_MSG_NOTICE([
         Target:                   ${red_target}
         SASL support:             ${enable_sasl}
         Smartcard support:        ${enable_smartcard}
+        USB redirection support:  ${enable_usbredir}
         Gtk:                      $GTK_API_VERSION
 
         Now type 'make' to build $PACKAGE
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index cbcaa79..1b841e2 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -15,6 +15,7 @@ EXTRA_DIST =                                  \
        coroutine_winfibers.c                   \
        continuation.h continuation.c           \
        map-file                                \
+       gusb/README.txt                         \
        $(NULL)
 
 bin_PROGRAMS = spicy snappy spicy-stats
@@ -71,6 +72,8 @@ SPICE_COMMON_CPPFLAGS = \
        $(SASL_CFLAGS)                  \
        $(GST_CFLAGS)                   \
        $(SMARTCARD_CFLAGS)             \
+       $(GUDEV_CFLAGS)                 \
+       $(LIBUSB_CFLAGS)                \
        $(NULL)
 
 AM_CPPFLAGS = \
@@ -149,8 +152,27 @@ libspice_client_glib_2_0_la_LIBADD =       \
        $(GST_LIBS)                     \
        $(SASL_LIBS)                    \
        $(SMARTCARD_LIBS)               \
+       $(GUDEV_LIBS)                   \
+       $(LIBUSB_LIBS)                  \
        $(NULL)
 
+if WITH_USBREDIR
+GUSB_SRCS =                            \
+       gusb/gusb-context.c             \
+       gusb/gusb-context.h             \
+       gusb/gusb-context-private.h     \
+       gusb/gusb-device.c              \
+       gusb/gusb-device.h              \
+       gusb/gusb-device-private.h      \
+       gusb/gusb-device-list.c         \
+       gusb/gusb-device-list.h         \
+       gusb/gusb-marshal.h             \
+       gusb/gusb-source.c              \
+       gusb/gusb-source.h
+else
+GUSB_SRCS =
+endif
+
 libspice_client_glib_2_0_la_SOURCES =  \
        spice-audio.c                   \
        spice-common.h                  \
@@ -189,6 +211,7 @@ libspice_client_glib_2_0_la_SOURCES =       \
        smartcard-manager.c             \
        smartcard-manager.h             \
        smartcard-manager-priv.h        \
+       $(GUSB_SRCS)                    \
        \
        decode.h                        \
        decode-glz.c                    \
diff --git a/gtk/gusb/README.txt b/gtk/gusb/README.txt
new file mode 100644
index 0000000..8d76ed7
--- /dev/null
+++ b/gtk/gusb/README.txt
@@ -0,0 +1,5 @@
+HDG: this is a private copy of gusb, from:
+https://gitorious.org/gusb
+
+This is a temporary solution until gusb upstream has a stable release out
+the door.
diff --git a/gtk/gusb/gusb-context-private.h b/gtk/gusb/gusb-context-private.h
new file mode 100644
index 0000000..4069a4d
--- /dev/null
+++ b/gtk/gusb/gusb-context-private.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2011 Richard Hughes <rich...@hughsie.com>
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 
USA
+ */
+
+#ifndef __GUSB_CONTEXT_PRIVATE_H__
+#define __GUSB_CONTEXT_PRIVATE_H__
+
+#include <libusb-1.0/libusb.h>
+
+#include <gusb/gusb-context.h>
+
+G_BEGIN_DECLS
+
+libusb_context *_g_usb_context_get_context     (GUsbContext    *context);
+
+G_END_DECLS
+
+#endif /* __GUSB_CONTEXT_PRIVATE_H__ */
diff --git a/gtk/gusb/gusb-context.c b/gtk/gusb/gusb-context.c
new file mode 100644
index 0000000..339b821
--- /dev/null
+++ b/gtk/gusb/gusb-context.c
@@ -0,0 +1,292 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2011 Richard Hughes <rich...@hughsie.com>
+ * Copyright (C) 2011 Hans de Goede <hdego...@redhat.com>
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * 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/>.
+ */
+
+/**
+ * SECTION:gusb-context
+ * @short_description: Per-thread instance integration for libusb
+ *
+ * This object is used to get a context that is thread safe.
+ */
+
+#include "config.h"
+
+#include <libusb-1.0/libusb.h>
+
+#include "gusb-context.h"
+#include "gusb-context-private.h"
+
+/* libusb_strerror is awaiting merging upstream */
+#define libusb_strerror(error) "unknown"
+
+static void g_usb_context_finalize (GObject *object);
+
+#define G_USB_CONTEXT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), 
G_USB_TYPE_CONTEXT, GUsbContextPrivate))
+
+enum {
+       PROP_0,
+       PROP_LIBUSB_CONTEXT,
+       PROP_DEBUG_LEVEL,
+};
+
+/**
+ * GUsbContextPrivate:
+ *
+ * Private #GUsbContext data
+ **/
+struct _GUsbContextPrivate
+{
+       libusb_context          *context;
+       int                      debug_level;
+};
+
+G_DEFINE_TYPE (GUsbContext, g_usb_context, G_TYPE_OBJECT)
+
+/**
+ * usb_context_get_property:
+ **/
+static void
+g_usb_context_get_property (GObject            *object,
+                           guint                prop_id,
+                           GValue              *value,
+                           GParamSpec          *pspec)
+{
+       GUsbContext *context = G_USB_CONTEXT (object);
+       GUsbContextPrivate *priv = context->priv;
+
+       switch (prop_id) {
+       case PROP_LIBUSB_CONTEXT:
+               g_value_set_pointer (value, priv->context);
+               break;
+       case PROP_DEBUG_LEVEL:
+               g_value_set_int (value, priv->debug_level);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+/**
+ * usb_context_set_property:
+ **/
+static void
+g_usb_context_set_property (GObject            *object,
+                          guint                 prop_id,
+                          const GValue         *value,
+                          GParamSpec           *pspec)
+{
+       GUsbContext *context = G_USB_CONTEXT (object);
+       GUsbContextPrivate *priv = context->priv;
+
+       switch (prop_id) {
+       case PROP_LIBUSB_CONTEXT:
+               priv->context = g_value_get_pointer (value);
+               break;
+       case PROP_DEBUG_LEVEL:
+               priv->debug_level = g_value_get_int (value);
+               libusb_set_debug (priv->context, priv->debug_level);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static GObject *
+g_usb_context_constructor (GType                        gtype,
+                          guint                         n_properties,
+                          GObjectConstructParam        *properties)
+{
+       GObject *obj;
+       GUsbContext *context;
+       GUsbContextPrivate *priv;
+
+       {
+               /* Always chain up to the parent constructor */
+               GObjectClass *parent_class;
+               parent_class = G_OBJECT_CLASS (g_usb_context_parent_class);
+               obj = parent_class->constructor (gtype, n_properties,
+                                                properties);
+       }
+
+       context = G_USB_CONTEXT (obj);
+       priv = context->priv;
+
+       /*
+        * Yes you're reading this right the sole reason for this constructor
+        * is to check the context has been set (for now).
+        */
+       if (!priv->context)
+               g_error("constructed without a context");
+
+       return obj;
+}
+
+/**
+ * usb_context_class_init:
+ **/
+static void
+g_usb_context_class_init (GUsbContextClass *klass)
+{
+       GParamSpec *pspec;
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+       object_class->constructor       = g_usb_context_constructor;
+       object_class->finalize          = g_usb_context_finalize;
+       object_class->get_property      = g_usb_context_get_property;
+       object_class->set_property      = g_usb_context_set_property;
+
+       /**
+        * GUsbContext:libusb_context:
+        */
+       pspec = g_param_spec_pointer ("libusb_context", NULL, NULL,
+                                     G_PARAM_CONSTRUCT_ONLY|
+                                     G_PARAM_READWRITE);
+       g_object_class_install_property (object_class, PROP_LIBUSB_CONTEXT,
+                                        pspec);
+
+       /**
+        * GUsbContext:debug_level:
+        */
+       pspec = g_param_spec_int ("debug_level", NULL, NULL,
+                                 0, 3, 0,
+                                 G_PARAM_READWRITE);
+       g_object_class_install_property (object_class, PROP_DEBUG_LEVEL,
+                                        pspec);
+
+       g_type_class_add_private (klass, sizeof (GUsbContextPrivate));
+}
+
+/**
+ * g_usb_context_init:
+ **/
+static void
+g_usb_context_init (GUsbContext *context)
+{
+       context->priv = G_USB_CONTEXT_GET_PRIVATE (context);
+}
+
+/**
+ * g_usb_context_finalize:
+ **/
+static void
+g_usb_context_finalize (GObject *object)
+{
+       GUsbContext *context = G_USB_CONTEXT (object);
+       GUsbContextPrivate *priv = context->priv;
+
+       libusb_exit (priv->context);
+
+       G_OBJECT_CLASS (g_usb_context_parent_class)->finalize (object);
+}
+
+/**
+ * g_usb_context_error_quark:
+ *
+ * Return value: Our personal error quark.
+ *
+ * Since: 0.0.1
+ **/
+GQuark
+g_usb_context_error_quark (void)
+{
+       static GQuark quark = 0;
+       if (!quark)
+               quark = g_quark_from_static_string ("g_usb_context_error");
+       return quark;
+}
+
+/**
+ * _g_usb_context_get_context:
+ * @context: a #GUsbContext
+ *
+ * Gets the internal libusb_context.
+ *
+ * Return value: (transfer none): the libusb_context
+ *
+ * Since: 0.0.1
+ **/
+libusb_context *
+_g_usb_context_get_context (GUsbContext *context)
+{
+       return context->priv->context;
+}
+
+/**
+ * g_usb_context_set_debug:
+ * @context: a #GUsbContext
+ * @flags: a GLogLevelFlags such as %G_LOG_LEVEL_ERROR | %G_LOG_LEVEL_INFO, or 0
+ *
+ * Sets the debug flags which control what is logged to the console.
+ *
+ * Using %G_LOG_LEVEL_INFO will output to standard out, and everything
+ * else logs to standard error.
+ *
+ * Since: 0.0.1
+ **/
+void
+g_usb_context_set_debug (GUsbContext *context, GLogLevelFlags flags)
+{
+       GUsbContextPrivate *priv = context->priv;
+
+       if (flags & (G_LOG_LEVEL_DEBUG | G_LOG_LEVEL_INFO))
+               priv->debug_level = 3;
+       else if (flags & (G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_WARNING))
+               priv->debug_level = 2;
+       else if (flags & (G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_ERROR))
+               priv->debug_level = 1;
+       else
+               priv->debug_level = 0;
+
+       libusb_set_debug (priv->context, priv->debug_level);
+}
+
+/**
+ * g_usb_context_new:
+ * @error: a #GError, or %NULL
+ *
+ * Creates a new context for accessing USB devices.
+ *
+ * Return value: a new %GUsbContext object or %NULL on error.
+ *
+ * Since: 0.0.1
+ **/
+GUsbContext *
+g_usb_context_new (GError **error)
+{
+       gint rc;
+       GObject *obj;
+       libusb_context *context;
+
+       rc = libusb_init (&context);
+       if (rc < 0) {
+               g_set_error (error,
+                            G_USB_CONTEXT_ERROR,
+                            G_USB_CONTEXT_ERROR_INTERNAL,
+                            "failed to init libusb: %s [%i]",
+                            libusb_strerror (rc), rc);
+               return NULL;
+       }
+
+       obj = g_object_new (G_USB_TYPE_CONTEXT, "libusb_context", context,
+                           NULL);
+       return G_USB_CONTEXT (obj);
+}
diff --git a/gtk/gusb/gusb-context.h b/gtk/gusb/gusb-context.h
new file mode 100644
index 0000000..ca9d28c
--- /dev/null
+++ b/gtk/gusb/gusb-context.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2011 Richard Hughes <rich...@hughsie.com>
+ * Copyright (C) 2011 Hans de Goede <hdego...@redhat.com>
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * 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 __GUSB_CONTEXT_H__
+#define __GUSB_CONTEXT_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define G_USB_TYPE_CONTEXT             (g_usb_context_get_type ())
+#define G_USB_CONTEXT(o)               (G_TYPE_CHECK_INSTANCE_CAST ((o), 
G_USB_TYPE_CONTEXT, GUsbContext))
+#define G_USB_IS_CONTEXT(o)            (G_TYPE_CHECK_INSTANCE_TYPE ((o), 
G_USB_TYPE_CONTEXT))
+#define G_USB_CONTEXT_ERROR            (g_usb_context_error_quark ())
+
+typedef struct _GUsbContextPrivate     GUsbContextPrivate;
+typedef struct _GUsbContext            GUsbContext;
+typedef struct _GUsbContextClass       GUsbContextClass;
+
+struct _GUsbContext
+{
+        GObject                         parent;
+        GUsbContextPrivate             *priv;
+};
+
+struct _GUsbContextClass
+{
+       GObjectClass                     parent_class;
+};
+
+typedef enum {
+       G_USB_CONTEXT_ERROR_INTERNAL
+} GUsbContextError;
+
+GType           g_usb_context_get_type         (void);
+GQuark          g_usb_context_error_quark      (void);
+
+GUsbContext    *g_usb_context_new              (GError         **error);
+
+void            g_usb_context_set_debug        (GUsbContext    *context,
+                                                GLogLevelFlags  flags);
+
+G_END_DECLS
+
+#endif /* __GUSB_CONTEXT_H__ */
diff --git a/gtk/gusb/gusb-device-list.c b/gtk/gusb/gusb-device-list.c
new file mode 100644
index 0000000..dbe73f8
--- /dev/null
+++ b/gtk/gusb/gusb-device-list.c
@@ -0,0 +1,358 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2011 Hans de Goede <hdego...@redhat.com>
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <gudev/gudev.h>
+#include <libusb-1.0/libusb.h>
+
+#include "gusb-marshal.h"
+#include "gusb-context.h"
+#include "gusb-context-private.h"
+#include "gusb-device.h"
+#include "gusb-device-private.h"
+
+#include "gusb-device-list.h"
+
+#define G_USB_DEVICE_LIST_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE 
((obj), G_USB_TYPE_DEVICE_LIST, GUsbDeviceListPrivate))
+
+enum {
+       PROP_0,
+       PROP_CONTEXT,
+};
+
+enum
+{
+       DEVICE_ADDED_SIGNAL,
+       DEVICE_REMOVED_SIGNAL,
+       LAST_SIGNAL,
+};
+
+struct _GUsbDeviceListPrivate {
+       GUsbContext      *context;
+       GUdevClient      *udev;
+       GPtrArray        *devices;
+       libusb_device   **coldplug_list;
+};
+
+static void g_usb_device_list_uevent_cb (GUdevClient   *client,
+                                       const gchar     *action,
+                                       GUdevDevice     *udevice,
+                                       gpointer         user_data);
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (GUsbDeviceList, g_usb_device_list, G_TYPE_OBJECT);
+
+static void
+g_usb_device_list_finalize (GObject *object)
+{
+       GUsbDeviceList *list = G_USB_DEVICE_LIST (object);
+       GUsbDeviceListPrivate *priv = list->priv;
+
+       g_object_unref (priv->udev);
+       g_ptr_array_unref (priv->devices);
+}
+
+/**
+ * g_usb_device_list_get_property:
+ **/
+static void
+g_usb_device_list_get_property (GObject                *object,
+                               guint            prop_id,
+                               GValue          *value,
+                               GParamSpec      *pspec)
+{
+       GUsbDeviceList *list = G_USB_DEVICE_LIST (object);
+       GUsbDeviceListPrivate *priv = list->priv;
+
+       switch (prop_id) {
+       case PROP_CONTEXT:
+               g_value_set_object (value, priv->context);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+/**
+ * usb_device_list_set_property:
+ **/
+static void
+g_usb_device_list_set_property (GObject                *object,
+                               guint            prop_id,
+                               const GValue    *value,
+                               GParamSpec      *pspec)
+{
+       GUsbDeviceList *list = G_USB_DEVICE_LIST (object);
+       GUsbDeviceListPrivate *priv = list->priv;
+
+       switch (prop_id) {
+       case PROP_CONTEXT:
+               priv->context = g_value_get_object (value);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static GObject *
+g_usb_device_list_constructor (GType                    gtype,
+                              guint                     n_properties,
+                              GObjectConstructParam    *properties)
+{
+       GObject *obj;
+       GUsbDeviceList *list;
+       GUsbDeviceListPrivate *priv;
+       const gchar *const subsystems[] = {"usb", NULL};
+
+       {
+               /* Always chain up to the parent constructor */
+               GObjectClass *parent_class;
+               parent_class = G_OBJECT_CLASS (g_usb_device_list_parent_class);
+               obj = parent_class->constructor (gtype, n_properties,
+                                                properties);
+       }
+
+       list = G_USB_DEVICE_LIST (obj);
+       priv = list->priv;
+
+       if (!priv->context)
+               g_error("constructed without a context");
+
+       priv->udev = g_udev_client_new (subsystems);
+       g_signal_connect (G_OBJECT (priv->udev), "uevent",
+                         G_CALLBACK (g_usb_device_list_uevent_cb), list);
+
+       priv->devices = g_ptr_array_new_with_free_func ((GDestroyNotify)
+                                                       g_object_unref);
+
+       priv->coldplug_list = NULL;
+
+       return obj;
+}
+
+/**
+ * g_usb_device_list_class_init:
+ **/
+static void
+g_usb_device_list_class_init (GUsbDeviceListClass *klass)
+{
+       GParamSpec *pspec;
+       GObjectClass *object_class = (GObjectClass *) klass;
+
+       object_class->constructor       = g_usb_device_list_constructor;
+       object_class->finalize          = g_usb_device_list_finalize;
+       object_class->get_property      = g_usb_device_list_get_property;
+       object_class->set_property      = g_usb_device_list_set_property;
+
+       /**
+        * GUsbDeviceList:context:
+        */
+       pspec = g_param_spec_object ("context", NULL, NULL,
+                                    G_USB_TYPE_CONTEXT,
+                                    G_PARAM_CONSTRUCT_ONLY|
+                                    G_PARAM_READWRITE);
+       g_object_class_install_property (object_class, PROP_CONTEXT, pspec);
+
+       signals[DEVICE_ADDED_SIGNAL] = g_signal_new ("device_added",
+                       G_TYPE_FROM_CLASS (klass),
+                       G_SIGNAL_RUN_LAST,
+                       G_STRUCT_OFFSET (GUsbDeviceListClass, device_added),
+                       NULL,
+                       NULL,
+                       g_cclosure_user_marshal_VOID__OBJECT_OBJECT,
+                       G_TYPE_NONE,
+                       2,
+                       G_TYPE_OBJECT,
+                       G_TYPE_OBJECT);
+
+       signals[DEVICE_REMOVED_SIGNAL] = g_signal_new ("device_removed",
+                       G_TYPE_FROM_CLASS (klass),
+                       G_SIGNAL_RUN_LAST,
+                       G_STRUCT_OFFSET (GUsbDeviceListClass, device_removed),
+                       NULL,
+                       NULL,
+                       g_cclosure_user_marshal_VOID__OBJECT_OBJECT,
+                       G_TYPE_NONE,
+                       2,
+                       G_TYPE_OBJECT,
+                       G_TYPE_OBJECT);
+
+       g_type_class_add_private (klass, sizeof (GUsbDeviceListPrivate));
+}
+
+static void
+g_usb_device_list_init (GUsbDeviceList *list)
+{
+       list->priv = G_USB_DEVICE_LIST_GET_PRIVATE (list);
+}
+
+static gboolean
+g_usb_device_list_get_bus_n_address (GUdevDevice       *udev,
+                                    gint               *bus,
+                                    gint               *address)
+{
+       const gchar *bus_str, *address_str;
+
+       *bus = *address = 0;
+
+       bus_str = g_udev_device_get_property (udev, "BUSNUM");
+       address_str = g_udev_device_get_property (udev, "DEVNUM");
+       if (bus_str)
+               *bus = atoi(bus_str);
+       if (address_str)
+               *address = atoi(address_str);
+
+       return *bus && *address;
+}
+
+static void
+g_usb_device_list_add_dev (GUsbDeviceList *list, GUdevDevice *udev)
+{
+       GUsbDeviceListPrivate *priv = list->priv;
+       GUsbDevice *device = NULL;
+       libusb_device **dev_list = NULL;
+       const gchar *devtype, *devclass;
+       gint i, bus, address;
+       libusb_context *ctx = _g_usb_context_get_context (priv->context);
+
+       devtype = g_udev_device_get_property (udev, "DEVTYPE");
+       /* Check if this is a usb device (and not an interface) */
+       if (!devtype || strcmp(devtype, "usb_device"))
+               return;
+
+       /* Skip hubs */
+       devclass = g_udev_device_get_sysfs_attr(udev, "bDeviceClass");
+       if (!devclass || !strcmp(devclass, "09"))
+               return;
+
+       if (!g_usb_device_list_get_bus_n_address (udev, &bus, &address)) {
+               g_warning ("usb-device without bus number or device address");
+               return;
+       }
+
+       if (priv->coldplug_list)
+               dev_list = priv->coldplug_list;
+       else
+               libusb_get_device_list(ctx, &dev_list);
+
+       for (i = 0; dev_list && dev_list[i]; i++) {
+               if (libusb_get_bus_number (dev_list[i]) == bus &&
+                   libusb_get_device_address (dev_list[i]) == address) {
+                       device = _g_usb_device_new (dev_list[i]);
+                       break;
+               }
+       }
+
+       if (!priv->coldplug_list)
+               libusb_free_device_list (dev_list, 1);
+
+       if (!device) {
+               g_warning ("Could not find usb dev at busnum %d devaddr %d",
+                          bus, address);
+               return;
+       }
+
+       g_ptr_array_add (priv->devices, device);
+       g_signal_emit (list, signals[DEVICE_ADDED_SIGNAL], 0, device, udev);
+}
+
+static void
+g_usb_device_list_remove_dev (GUsbDeviceList *list, GUdevDevice *udev)
+{
+       GUsbDeviceListPrivate *priv = list->priv;
+       GUsbDevice *device;
+       gint bus, address;
+
+       if (!g_usb_device_list_get_bus_n_address (udev, &bus, &address))
+               return;
+
+       device = g_usb_device_list_get_dev_by_bus_n_address (list, bus,
+                                                            address);
+       if (!device)
+               return;
+
+       g_signal_emit (list, signals[DEVICE_REMOVED_SIGNAL], 0, device, udev);
+       g_ptr_array_remove (priv->devices, device);
+}
+
+static void
+g_usb_device_list_uevent_cb (GUdevClient               *client,
+                            const gchar                *action,
+                            GUdevDevice                *udevice,
+                            gpointer                    user_data)
+{
+       GUsbDeviceList *list = G_USB_DEVICE_LIST (user_data);
+
+       if (g_str_equal (action, "add"))
+               g_usb_device_list_add_dev (list, udevice);
+       else if (g_str_equal (action, "remove"))
+               g_usb_device_list_remove_dev (list, udevice);
+}
+
+void
+g_usb_device_list_coldplug (GUsbDeviceList *list)
+{
+       GUsbDeviceListPrivate *priv = list->priv;
+       GList *devices, *elem;
+       libusb_context *ctx = _g_usb_context_get_context (priv->context);
+
+       libusb_get_device_list(ctx, &priv->coldplug_list);
+       devices = g_udev_client_query_by_subsystem (priv->udev, "usb");
+       for (elem = g_list_first (devices); elem; elem = g_list_next (elem)) {
+               g_usb_device_list_add_dev (list, elem->data);
+               g_object_unref (elem->data);
+       }
+       g_list_free (devices);
+       libusb_free_device_list (priv->coldplug_list, 1);
+       priv->coldplug_list = NULL;
+}
+
+GUsbDevice *
+g_usb_device_list_get_dev_by_bus_n_address (GUsbDeviceList     *list,
+                                           guint8               bus,
+                                           guint8               address)
+{
+       GUsbDeviceListPrivate *priv = list->priv;
+       GUsbDevice *device = NULL;
+       guint i;
+
+       for (i = 0; i < priv->devices->len; i++) {
+               GUsbDevice *curr = g_ptr_array_index (priv->devices, i);
+               if (g_usb_device_get_bus (curr) == bus &&
+                   g_usb_device_get_address (curr) == address) {
+                       device = curr;
+                       break;
+                }
+       }
+
+       return device;
+}
+
+GUsbDeviceList *
+g_usb_device_list_new (GUsbContext *context)
+{
+       GObject *obj;
+       obj = g_object_new (G_USB_TYPE_DEVICE_LIST, "context", context, NULL);
+       return G_USB_DEVICE_LIST (obj);
+}
diff --git a/gtk/gusb/gusb-device-list.h b/gtk/gusb/gusb-device-list.h
new file mode 100644
index 0000000..430984f
--- /dev/null
+++ b/gtk/gusb/gusb-device-list.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2011 Hans de Goede <hdego...@redhat.com>
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * 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 __GUSB_DEVICE_LIST_H__
+#define __GUSB_DEVICE_LIST_H__
+
+#include <glib-object.h>
+#include <gudev/gudev.h>
+
+#include <gusb/gusb-context.h>
+#include <gusb/gusb-device.h>
+
+G_BEGIN_DECLS
+
+#define G_USB_TYPE_DEVICE_LIST         (g_usb_device_list_get_type ())
+#define G_USB_DEVICE_LIST(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), 
G_USB_TYPE_DEVICE_LIST, GUsbDeviceList))
+#define G_USB_IS_DEVICE_LIST(o)                (G_TYPE_CHECK_INSTANCE_TYPE 
((o), G_USB_TYPE_DEVICE_LIST))
+
+typedef struct _GUsbDeviceListPrivate  GUsbDeviceListPrivate;
+typedef struct _GUsbDeviceList         GUsbDeviceList;
+typedef struct _GUsbDeviceListClass    GUsbDeviceListClass;
+
+struct _GUsbDeviceList
+{
+        GObject                         parent;
+        GUsbDeviceListPrivate          *priv;
+};
+
+struct _GUsbDeviceListClass
+{
+       GObjectClass                     parent_class;
+       /* Signals */
+       void (*device_added)            (GUsbDeviceList         *list,
+                                        GUsbDevice             *device,
+                                        GUdevDevice            *udev);
+       void (*device_removed)          (GUsbDeviceList         *list,
+                                        GUsbDevice             *device,
+                                        GUdevDevice            *udev);
+};
+
+GType                   g_usb_device_list_get_type (void);
+
+GUsbDeviceList         *g_usb_device_list_new (GUsbContext *context);
+
+void                    g_usb_device_list_coldplug (GUsbDeviceList *list);
+
+GUsbDevice             *g_usb_device_list_get_dev_by_bus_n_address (
+                                       GUsbDeviceList  *list,
+                                       guint8           bus,
+                                       guint8           address);
+
+
+G_END_DECLS
+
+#endif /* __GUSB_DEVICE_LIST_H__ */
diff --git a/gtk/gusb/gusb-device-private.h b/gtk/gusb/gusb-device-private.h
new file mode 100644
index 0000000..c93b51a
--- /dev/null
+++ b/gtk/gusb/gusb-device-private.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2011 Hans de Goede <hdego...@redhat.com>
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * 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 __GUSB_DEVICE_PRIVATE_H__
+#define __GUSB_DEVICE_PRIVATE_H__
+
+#include <gusb/gusb-device.h>
+
+G_BEGIN_DECLS
+
+GUsbDevice     *_g_usb_device_new              (libusb_device  *device);
+
+libusb_device  *_g_usb_device_get_device       (GUsbDevice     *device);
+
+G_END_DECLS
+
+#endif /* __GUSB_DEVICE_PRIVATE_H__ */
diff --git a/gtk/gusb/gusb-device.c b/gtk/gusb/gusb-device.c
new file mode 100644
index 0000000..26a25f3
--- /dev/null
+++ b/gtk/gusb/gusb-device.c
@@ -0,0 +1,232 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010-2011 Richard Hughes <rich...@hughsie.com>
+ * Copyright (C) 2011 Hans de Goede <hdego...@redhat.com>
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * 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/>.
+ */
+
+/**
+ * SECTION:usb-device
+ * @short_description: GLib device integration for libusb
+ *
+ * This object is a thin glib wrapper around a libusb_device
+ */
+
+#include "config.h"
+
+#include <libusb-1.0/libusb.h>
+
+#include "gusb-device.h"
+#include "gusb-device-private.h"
+
+static void     g_usb_device_finalize  (GObject     *object);
+
+#define G_USB_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), 
G_USB_TYPE_DEVICE, GUsbDevicePrivate))
+
+/**
+ * GUsbDevicePrivate:
+ *
+ * Private #GUsbDevice data
+ **/
+struct _GUsbDevicePrivate
+{
+       libusb_device           *device;
+};
+
+enum {
+       PROP_0,
+       PROP_LIBUSB_DEVICE,
+};
+
+G_DEFINE_TYPE (GUsbDevice, g_usb_device, G_TYPE_OBJECT)
+
+
+/**
+ * g_usb_device_error_quark:
+ *
+ * Return value: Our personal error quark.
+ *
+ * Since: 0.0.1
+ **/
+GQuark
+g_usb_device_error_quark (void)
+{
+       static GQuark quark = 0;
+       if (!quark)
+               quark = g_quark_from_static_string ("g_usb_device_error");
+       return quark;
+}
+
+/**
+ * usb_device_get_property:
+ **/
+static void
+g_usb_device_get_property (GObject             *object,
+                          guint                 prop_id,
+                          GValue               *value,
+                          GParamSpec           *pspec)
+{
+       GUsbDevice *device = G_USB_DEVICE (object);
+       GUsbDevicePrivate *priv = device->priv;
+
+       switch (prop_id) {
+       case PROP_LIBUSB_DEVICE:
+               g_value_set_pointer (value, priv->device);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+/**
+ * usb_device_set_property:
+ **/
+static void
+g_usb_device_set_property (GObject             *object,
+                          guint                 prop_id,
+                          const GValue         *value,
+                          GParamSpec           *pspec)
+{
+       GUsbDevice *device = G_USB_DEVICE (object);
+       GUsbDevicePrivate *priv = device->priv;
+
+       switch (prop_id) {
+       case PROP_LIBUSB_DEVICE:
+               priv->device = g_value_get_pointer (value);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static GObject *
+g_usb_device_constructor (GType                         gtype,
+                         guint                  n_properties,
+                         GObjectConstructParam *properties)
+{
+       GObject *obj;
+       GUsbDevice *device;
+       GUsbDevicePrivate *priv;
+
+       {
+               /* Always chain up to the parent constructor */
+               GObjectClass *parent_class;
+               parent_class = G_OBJECT_CLASS (g_usb_device_parent_class);
+               obj = parent_class->constructor (gtype, n_properties,
+                                                properties);
+       }
+
+       device = G_USB_DEVICE (obj);
+       priv = device->priv;
+
+       if (!priv->device)
+               g_error("constructed without a libusb_device");
+
+       libusb_ref_device(priv->device);
+
+       return obj;
+}
+
+/**
+ * usb_device_class_init:
+ **/
+static void
+g_usb_device_class_init (GUsbDeviceClass *klass)
+{
+       GParamSpec *pspec;
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+       object_class->constructor       = g_usb_device_constructor;
+       object_class->finalize          = g_usb_device_finalize;
+       object_class->get_property      = g_usb_device_get_property;
+       object_class->set_property      = g_usb_device_set_property;
+
+       /**
+        * GUsbDevice:libusb_device:
+        */
+       pspec = g_param_spec_pointer ("libusb_device", NULL, NULL,
+                                    G_PARAM_CONSTRUCT_ONLY|
+                                    G_PARAM_READWRITE);
+       g_object_class_install_property (object_class, PROP_LIBUSB_DEVICE,
+                                        pspec);
+
+       g_type_class_add_private (klass, sizeof (GUsbDevicePrivate));
+}
+
+/**
+ * g_usb_device_init:
+ **/
+static void
+g_usb_device_init (GUsbDevice *device)
+{
+       device->priv = G_USB_DEVICE_GET_PRIVATE (device);
+}
+
+/**
+ * g_usb_device_finalize:
+ **/
+static void
+g_usb_device_finalize (GObject *object)
+{
+       GUsbDevice *device = G_USB_DEVICE (object);
+       GUsbDevicePrivate *priv = device->priv;
+
+       libusb_unref_device(priv->device);
+
+       G_OBJECT_CLASS (g_usb_device_parent_class)->finalize (object);
+}
+
+/**
+ * _g_usb_device_new:
+ *
+ * Return value: a new #GUsbDevice object.
+ **/
+GUsbDevice *
+_g_usb_device_new (libusb_device       *device)
+{
+       GObject *obj;
+       obj = g_object_new (G_USB_TYPE_DEVICE, "libusb_device", device, NULL);
+       return G_USB_DEVICE (obj);
+}
+
+/**
+ * _g_usb_device_get_device:
+ * @device: a #GUsbDevice instance
+ *
+ * Gets the low-level libusb_device
+ *
+ * Return value: The #libusb_device or %NULL. Do not unref this value.
+ **/
+libusb_device *
+_g_usb_device_get_device (GUsbDevice   *device)
+{
+       return device->priv->device;
+}
+
+guint8
+g_usb_device_get_bus (GUsbDevice       *device)
+{
+       return libusb_get_bus_number (device->priv->device);
+}
+
+guint8
+g_usb_device_get_address (GUsbDevice   *device)
+{
+       return libusb_get_device_address (device->priv->device);
+}
diff --git a/gtk/gusb/gusb-device.h b/gtk/gusb/gusb-device.h
new file mode 100644
index 0000000..a504182
--- /dev/null
+++ b/gtk/gusb/gusb-device.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010-2011 Richard Hughes <rich...@hughsie.com>
+ * Copyright (C) 2011 Hans de Goede <hdego...@redhat.com>
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * 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 __GUSB_DEVICE_H__
+#define __GUSB_DEVICE_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define G_USB_TYPE_DEVICE              (g_usb_device_get_type ())
+#define G_USB_DEVICE(o)                        (G_TYPE_CHECK_INSTANCE_CAST 
((o), G_USB_TYPE_DEVICE, GUsbDevice))
+#define G_USB_IS_DEVICE(o)             (G_TYPE_CHECK_INSTANCE_TYPE ((o), 
G_USB_TYPE_DEVICE))
+#define G_USB_DEVICE_ERROR             (g_usb_device_error_quark ())
+
+typedef struct _GUsbDevicePrivate      GUsbDevicePrivate;
+typedef struct _GUsbDevice             GUsbDevice;
+typedef struct _GUsbDeviceClass                GUsbDeviceClass;
+
+/**
+ * GUsbDeviceError:
+ *
+ * The error code.
+ **/
+typedef enum {
+       G_USB_DEVICE_ERROR_INTERNAL
+} GUsbDeviceError;
+
+struct _GUsbDevice
+{
+        GObject                         parent;
+        GUsbDevicePrivate              *priv;
+};
+
+struct _GUsbDeviceClass
+{
+       GObjectClass                     parent_class;
+};
+
+GType                   g_usb_device_get_type          (void);
+GQuark                  g_usb_device_error_quark       (void);
+
+guint8                  g_usb_device_get_bus           (GUsbDevice      
*device);
+guint8                  g_usb_device_get_address       (GUsbDevice      
*device);
+
+#if 0 /* TODO */
+GUsbDeviceHandle       *g_usb_device_get_device_handle (GUsbDevice      
*device,
+                                                        GError         **err);
+#endif
+
+G_END_DECLS
+
+#endif /* __GUSB_DEVICE_H__ */
diff --git a/gtk/gusb/gusb-marshal.h b/gtk/gusb/gusb-marshal.h
new file mode 100644
index 0000000..b00fd65
--- /dev/null
+++ b/gtk/gusb/gusb-marshal.h
@@ -0,0 +1,7 @@
+/*
+ * HDG: hack
+ * Important: Don't forget to remove VOID:OBJECT,OBJECT from spice-marshal.txt
+ * when we remove our bundled gusb.
+ */
+
+#include "spice-marshal.h"
diff --git a/gtk/gusb/gusb-source.c b/gtk/gusb/gusb-source.c
new file mode 100644
index 0000000..0324e68
--- /dev/null
+++ b/gtk/gusb/gusb-source.c
@@ -0,0 +1,309 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010-2011 Richard Hughes <rich...@hughsie.com>
+ * Copyright (C) 2011 Hans de Goede <hdego...@redhat.com>
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 
USA
+ */
+
+/**
+ * SECTION:gusb-source
+ * @short_description: GSource integration for libusb
+ *
+ * This object can be used to integrate libusb into the GLib main loop.
+ */
+
+#include "config.h"
+
+#include <libusb-1.0/libusb.h>
+#include <poll.h>
+#include <stdlib.h>
+
+#include "gusb-context.h"
+#include "gusb-context-private.h"
+#include "gusb-source.h"
+
+/**
+ * g_usb_source_error_quark:
+ *
+ * Return value: Our personal error quark.
+ *
+ * Since: 0.0.1
+ **/
+GQuark
+g_usb_source_error_quark (void)
+{
+       static GQuark quark = 0;
+       if (!quark)
+               quark = g_quark_from_static_string ("g_usb_source_error");
+       return quark;
+}
+
+/* libusb_strerror is awaiting merging upstream */
+#define libusb_strerror(error) "unknown"
+
+struct _GUsbSource {
+       GSource          source;
+       GSList          *pollfds;
+       GUsbContext     *usbcontext;
+       libusb_context  *ctx;
+};
+
+static void
+g_usb_source_pollfd_add (GUsbSource *source, int fd, short events)
+{
+       GPollFD *pollfd = g_slice_new(GPollFD);
+       pollfd->fd = fd;
+       pollfd->events = 0;
+       pollfd->revents = 0;
+       if (events & POLLIN)
+               pollfd->events |= G_IO_IN;
+       if (events & POLLOUT)
+               pollfd->events |= G_IO_OUT;
+
+       source->pollfds = g_slist_prepend(source->pollfds, pollfd);
+       g_source_add_poll((GSource *)source, pollfd);
+}
+
+static void
+g_usb_source_pollfd_added_cb (int fd, short events, void *user_data)
+{
+       GUsbSource *source = user_data;
+       g_usb_source_pollfd_add (source, fd, events);
+}
+
+static void
+g_usb_source_pollfd_remove (GUsbSource *source, int fd)
+{
+       GPollFD *pollfd;
+       GSList *elem = source->pollfds;
+
+       /* nothing to see here, move along */
+       if (elem == NULL) {
+               g_warning("cannot remove from list as list is empty?");
+               return;
+       }
+
+       /* find the pollfd in the list */
+       do {
+               pollfd = elem->data;
+               if (pollfd->fd != fd)
+                       continue;
+
+               g_source_remove_poll((GSource *)source, pollfd);
+               g_slice_free(GPollFD, pollfd);
+               source->pollfds = g_slist_delete_link(source->pollfds, elem);
+               return;
+       } while ((elem = g_slist_next(elem)));
+       g_warning ("couldn't find fd %d in list", fd);
+}
+
+static void
+g_usb_source_pollfd_removed_cb(int fd, void *user_data)
+{
+       GUsbSource *source = user_data;
+
+       g_usb_source_pollfd_remove (source, fd);
+}
+
+static void
+g_usb_source_pollfd_remove_all (GUsbSource *source)
+{
+       GPollFD *pollfd;
+       GSList *curr, *next;
+
+       next = source->pollfds;
+       while (next) {
+               curr = next;
+               next = g_slist_next(curr);
+               pollfd = curr->data;
+               g_source_remove_poll((GSource *)source, pollfd);
+               g_slice_free (GPollFD, pollfd);
+               source->pollfds = g_slist_delete_link(source->pollfds, curr);
+       }
+}
+
+/**
+ * g_usb_source_prepare:
+ *
+ * Called before all the file descriptors are polled.
+ * As we are a file descriptor source, the prepare function returns FALSE.
+ * It sets the returned timeout to -1 to indicate that it doesn't mind
+ * how long the poll() call blocks.
+ *
+ * No, we're not going to support FreeBSD.
+ **/
+static gboolean
+g_usb_source_prepare (GSource *source, gint *timeout)
+{
+       *timeout = -1;
+       return FALSE;
+}
+
+/**
+ * g_usb_source_check:
+ *
+ * In the check function, it tests the results of the poll() call to see
+ * if the required condition has been met, and returns TRUE if so.
+ **/
+static gboolean
+g_usb_source_check (GSource *source)
+{
+       GUsbSource *usb_source = (GUsbSource *)source;
+       GPollFD *pollfd;
+       GSList *elem = usb_source->pollfds;
+
+       /* no fds */
+       if (elem == NULL)
+               return FALSE;
+
+       /* check each pollfd */
+       do {
+               pollfd = elem->data;
+               if (pollfd->revents)
+                       return TRUE;
+       } while ((elem = g_slist_next(elem)));
+
+       return FALSE;
+}
+
+static gboolean
+g_usb_source_dispatch (GSource *source,
+                      GSourceFunc callback,
+                      gpointer user_data)
+{
+       GUsbSource *usb_source = (GUsbSource *)source;
+       struct timeval tv = { 0, 0 };
+       gint rc;
+
+       rc = libusb_handle_events_timeout (usb_source->ctx, &tv);
+       if (rc < 0) {
+               g_warning ("failed to handle event: %s [%i]",
+                          libusb_strerror (rc), rc);
+       }
+
+       if (callback)
+               callback(user_data);
+
+       return TRUE;
+}
+
+static void
+g_usb_source_finalize (GSource *source)
+{
+       GUsbSource *usb_source = (GUsbSource *)source;
+       g_object_unref (usb_source->usbcontext);
+       g_slist_free (usb_source->pollfds);
+}
+
+static GSourceFuncs usb_source_funcs = {
+       g_usb_source_prepare,
+       g_usb_source_check,
+       g_usb_source_dispatch,
+       g_usb_source_finalize,
+       NULL, NULL
+};
+
+/**
+ * g_usb_source_new:
+ * @main_ctx: a #GMainContext, or %NULL
+ * @gusb_ctx: a #GUsbContext
+ * @error: a #GError, or %NULL
+ *
+ * Creates a source for integration into libusb1.
+ *
+ * Return value: (transfer none): the #GUsbSource, or %NULL. Use 
g_usb_source_destroy() to unref.
+ *
+ * Since: 0.0.1
+ **/
+GUsbSource *
+g_usb_source_new (GMainContext *main_ctx,
+                 GUsbContext *gusb_ctx,
+                 GError **error)
+{
+       guint i;
+       const struct libusb_pollfd **pollfds;
+       GUsbSource *gusb_source;
+
+       gusb_source = (GUsbSource *)g_source_new (&usb_source_funcs,
+                                                 sizeof(GUsbSource));
+       gusb_source->pollfds = NULL;
+       gusb_source->usbcontext = g_object_ref (gusb_ctx);
+       gusb_source->ctx = _g_usb_context_get_context (gusb_ctx);
+
+       /* watch the fd's already created */
+       pollfds = libusb_get_pollfds (gusb_source->ctx);
+       if (pollfds == NULL) {
+               g_set_error_literal (error,
+                                    G_USB_SOURCE_ERROR,
+                                    G_USB_SOURCE_ERROR_INTERNAL,
+                                    "failed to allocate memory");
+               g_free (gusb_source);
+               gusb_source = NULL;
+               goto out;
+       }
+       for (i=0; pollfds[i] != NULL; i++)
+               g_usb_source_pollfd_add (gusb_source,
+                                        pollfds[i]->fd,
+                                        pollfds[i]->events);
+       free (pollfds);
+
+       /* watch for PollFD changes */
+       g_source_attach ((GSource *)gusb_source, main_ctx);
+       libusb_set_pollfd_notifiers (gusb_source->ctx,
+                                    g_usb_source_pollfd_added_cb,
+                                    g_usb_source_pollfd_removed_cb,
+                                    gusb_source);
+out:
+       return gusb_source;
+}
+
+/**
+ * g_usb_source_destroy:
+ * @source: a #GUsbSource
+ *
+ * Destroys a #GUsbSource
+ *
+ * Since: 0.0.1
+ **/
+void
+g_usb_source_destroy (GUsbSource *source)
+{
+       libusb_set_pollfd_notifiers (source->ctx, NULL, NULL, NULL);
+       g_usb_source_pollfd_remove_all (source);
+       g_source_destroy ((GSource *)source);
+}
+
+/**
+ * g_usb_source_set_callback:
+ * @source: a #GUsbSource
+ * @func: a function to call
+ * @data: data to pass to @func
+ * @notify: a #GDestroyNotify
+ *
+ * Set a callback to be called when the source is dispatched.
+ *
+ * Since: 0.0.1
+ **/
+void
+g_usb_source_set_callback (GUsbSource *source,
+                          GSourceFunc func,
+                          gpointer data,
+                          GDestroyNotify notify)
+{
+       g_source_set_callback ((GSource *)source, func, data, notify);
+}
diff --git a/gtk/gusb/gusb-source.h b/gtk/gusb/gusb-source.h
new file mode 100644
index 0000000..40f6880
--- /dev/null
+++ b/gtk/gusb/gusb-source.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010-2011 Richard Hughes <rich...@hughsie.com>
+ * Copyright (C) 2011 Hans de Goede <hdego...@redhat.com>
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 
USA
+ */
+
+#ifndef __GUSB_SOURCE_H__
+#define __GUSB_SOURCE_H__
+
+#include <glib.h>
+
+#include <gusb/gusb-context.h>
+
+G_BEGIN_DECLS
+
+#define G_USB_SOURCE_ERROR                     (g_usb_source_error_quark ())
+
+typedef struct _GUsbSource GUsbSource;
+
+/**
+ * GUsbSourceError:
+ *
+ * The error code.
+ **/
+typedef enum {
+       G_USB_SOURCE_ERROR_INTERNAL
+} GUsbSourceError;
+
+GQuark          g_usb_source_error_quark       (void);
+GUsbSource     *g_usb_source_new               (GMainContext   *main_ctx,
+                                                GUsbContext    *gusb_ctx,
+                                                GError         **error);
+void            g_usb_source_destroy           (GUsbSource     *source);
+
+void            g_usb_source_set_callback      (GUsbSource     *source,
+                                                GSourceFunc     func,
+                                                gpointer        data,
+                                                GDestroyNotify  notify);
+
+G_END_DECLS
+
+#endif /* __GUSB_SOURCE_H__ */
diff --git a/gtk/spice-marshal.txt b/gtk/spice-marshal.txt
index 8e56394..b9630eb 100644
--- a/gtk/spice-marshal.txt
+++ b/gtk/spice-marshal.txt
@@ -11,3 +11,4 @@ VOID:UINT,POINTER,UINT
 VOID:UINT,UINT,POINTER,UINT
 BOOLEAN:UINT,POINTER,UINT
 BOOLEAN:UINT,UINT
+VOID:OBJECT,OBJECT
-- 
1.7.5.1

_______________________________________________
Spice-devel mailing list
Spice-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/spice-devel

Reply via email to