[Spice-devel] [spice-gtk Win32 PATCH 3/7] Windows: add win-usb-dev.[ch]: implement GUdevDevice & GUdevClient

Uri Lublin uril at redhat.com
Mon May 7 06:15:35 PDT 2012


From: Arnon Gilboa <agilboa at redhat.com>

With this patch usb-device-manager can work with little change
on windows too.

-add uevent signal based on WM_DEVICECHANGE
-add spice_usb_device_manager_set_window for setting winproc
   (which is needed for event registration)

Modified-by: Uri Lublin <uril at redhat.com>

---
 gtk/Makefile.am          |   21 +++
 gtk/map-file             |    1 +
 gtk/usb-device-manager.c |   13 ++
 gtk/usb-device-manager.h |    4 +
 gtk/win-usb-dev.c        |  341 ++++++++++++++++++++++++++++++++++++++++++++++
 gtk/win-usb-dev.h        |   95 +++++++++++++
 6 files changed, 475 insertions(+), 0 deletions(-)
 create mode 100644 gtk/win-usb-dev.c
 create mode 100644 gtk/win-usb-dev.h

diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 69cf0ef..d0e4bda 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -133,6 +133,23 @@ SPICE_GTK_SOURCES_COMMON +=		\
 	$(NULL)
 endif

+if OS_WIN32
+WIN_USB_SRCS =						\
+	win-usb-dev.c					\
+	$(NULL)
+
+WIN_USB_HDRS =						\
+	win-usb-dev.h					\
+	$(NULL)
+
+WIN_USB_LIBS = $(GTK_LIBS)
+else
+WIN_USB_SRCS =
+WIN_USB_HDRS =
+WIN_USB_LIBS =
+endif
+
+
 if HAVE_GTK_2
 libspice_client_gtk_2_0_la_LDFLAGS = $(SPICE_GTK_LDFLAGS_COMMON)
 libspice_client_gtk_2_0_la_LIBADD = $(SPICE_GTK_LIBADD_COMMON)
@@ -181,6 +198,7 @@ libspice_client_glib_2_0_la_LIBADD =					\
 	$(SMARTCARD_LIBS)						\
 	$(USBREDIR_LIBS)						\
 	$(GUDEV_LIBS)							\
+	$(WIN_USB_LIBS)							\
 	$(NULL)

 if WITH_POLKIT
@@ -232,6 +250,7 @@ libspice_client_glib_2_0_la_SOURCES =			\
 	smartcard-manager-priv.h			\
 	usb-device-manager.c				\
 	usb-device-manager-priv.h			\
+	$(WIN_USB_SRCS)					\
 	usbutil.c					\
 	usbutil.h					\
 	$(USB_ACL_HELPER_SRCS)				\
@@ -269,6 +288,7 @@ libspice_client_glibinclude_HEADERS =	\
 	channel-smartcard.h		\
 	channel-usbredir.h		\
 	usb-device-manager.h		\
+	$(WIN_USB_HDRS)			\
 	smartcard-manager.h		\
 	$(NULL)

@@ -566,6 +586,7 @@ glib_introspection_files =				\
 	channel-usbredir.c				\
 	smartcard-manager.c				\
 	usb-device-manager.c				\
+	$(WIN_USB_SRCS)					\
 	$(NULL)

 gtk_introspection_files =				\
diff --git a/gtk/map-file b/gtk/map-file
index 32ead37..66d6545 100644
--- a/gtk/map-file
+++ b/gtk/map-file
@@ -94,6 +94,7 @@ 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_manager_set_window;
 spice_usb_device_widget_get_type;
 spice_usb_device_widget_new;
 spice_usbredir_channel_get_type;
diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
index 14b60c9..509ed8c 100644
--- a/gtk/usb-device-manager.c
+++ b/gtk/usb-device-manager.c
@@ -28,7 +28,12 @@
 #ifdef USE_USBREDIR
 #include <errno.h>
 #include <libusb.h>
+#ifdef __linux__
 #include <gudev/gudev.h>
+#elif WIN32
+#include "win-usb-dev.h"
+#endif
+
 #include "channel-usbredir-priv.h"
 #include "usbredirhost.h"
 #include "usbutil.h"
@@ -1044,3 +1049,11 @@ gchar *spice_usb_device_get_description(SpiceUsbDevice *_device, const gchar *fo
     return NULL;
 #endif
 }
+
+
+void spice_usb_device_manager_set_window(SpiceUsbDeviceManager *manager, GdkWindow *win)
+{
+#ifdef WIN32
+    g_udev_client_win_init(manager->priv->udev, manager, win);
+#endif
+}
diff --git a/gtk/usb-device-manager.h b/gtk/usb-device-manager.h
index ba917da..dd18c6d 100644
--- a/gtk/usb-device-manager.h
+++ b/gtk/usb-device-manager.h
@@ -22,6 +22,7 @@
 #define __SPICE_USB_DEVICE_MANAGER_H__

 #include "spice-client.h"
+#include <gtk/gtk.h>
 #include <gio/gio.h>

 G_BEGIN_DECLS
@@ -114,6 +115,9 @@ spice_usb_device_manager_can_redirect_device(SpiceUsbDeviceManager  *self,
                                              SpiceUsbDevice         *device,
                                              GError                **err);

+void spice_usb_device_manager_set_window(SpiceUsbDeviceManager *manager,
+                                         GdkWindow *win);
+
 G_END_DECLS

 #endif /* __SPICE_USB_DEVICE_MANAGER_H__ */
diff --git a/gtk/win-usb-dev.c b/gtk/win-usb-dev.c
new file mode 100644
index 0000000..0a154f6
--- /dev/null
+++ b/gtk/win-usb-dev.c
@@ -0,0 +1,341 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2012 Red Hat, Inc.
+
+   Red Hat Authors:
+   Arnon Gilboa <agilboa 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/>.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <libusb.h>
+#include "win-usb-dev.h"
+#include "usb-device-manager.h"
+#include "spice-marshal.h"
+#include "spice-client.h"
+#include <windows.h>
+
+#define G_UDEV_CLIENT_GET_PRIVATE(obj) \
+    (G_TYPE_INSTANCE_GET_PRIVATE((obj), G_UDEV_TYPE_CLIENT, GUdevClientPrivate))
+
+struct _GUdevClientPrivate {
+    SpiceUsbDeviceManager *usb_dev_mgr;
+    libusb_device **dev_list;
+    ssize_t dev_list_size;
+    GList *udevice_list;
+    GdkWindow *gdk_win;
+};
+
+
+static void g_udev_client_initable_iface_init(GInitableIface  *iface);
+
+G_DEFINE_TYPE_WITH_CODE(GUdevClient, g_udev_client, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE, g_udev_client_initable_iface_init));
+
+
+typedef struct _GUdevDeviceInfo GUdevDeviceInfo;
+
+struct _GUdevDeviceInfo {
+    struct libusb_device_descriptor desc;
+    libusb_device *dev;
+    gchar sclass[4];
+    gchar sbus[4];
+    gchar saddr[4];
+};
+
+struct _GUdevDevicePrivate
+{
+    GUdevDeviceInfo *usbdev;
+};
+
+G_DEFINE_TYPE(GUdevDevice, g_udev_device, G_TYPE_OBJECT)
+
+enum
+{
+    UEVENT_SIGNAL,
+    LAST_SIGNAL,
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+static GUdevDevice *g_udev_device_new(GUdevDeviceInfo *usbdev);
+static GdkFilterReturn win_message_cb(GdkXEvent *wmevent, GdkEvent *event, gpointer data);
+gboolean get_usb_dev_info(libusb_device *dev, GUdevDeviceInfo *usbdev);
+
+GUdevClient *g_udev_client_new(const gchar* const *subsystems)
+{
+    return g_initable_new(G_UDEV_TYPE_CLIENT, NULL, NULL, NULL);
+}
+
+static gboolean g_udev_client_initable_init(GInitable *initable, GCancellable *cancellable,
+                                            GError **err)
+{
+    GUdevClient *self;
+    GUdevClientPrivate *priv;
+    GUdevDeviceInfo *usbdev;
+    GUdevDevice *udevice;
+    libusb_device **dev;
+    int rc;
+
+    g_return_val_if_fail(G_UDEV_IS_CLIENT(initable), FALSE);
+    g_return_val_if_fail(cancellable == NULL, FALSE);
+
+    self = G_UDEV_CLIENT(initable);
+    priv = self->priv;
+
+    /* FIXME: context needed? get it from manager? remove libusb_init & libusb_exit? */
+    rc = libusb_init(NULL);
+    g_return_val_if_fail(rc >= 0, FALSE);
+
+    priv->dev_list_size = libusb_get_device_list(NULL, &priv->dev_list);
+    g_return_val_if_fail(priv->dev_list_size >= 0, FALSE);
+    for (dev = priv->dev_list; *dev; dev++) {
+        usbdev = g_new(GUdevDeviceInfo, 1);
+        get_usb_dev_info(*dev, usbdev);
+        udevice = g_udev_device_new(usbdev);
+        priv->udevice_list = g_list_append(priv->udevice_list, udevice);
+    }
+    return TRUE;
+}
+
+static void g_udev_client_initable_iface_init(GInitableIface *iface)
+{
+    iface->init = g_udev_client_initable_init;
+}
+
+GList *g_udev_client_query_by_subsystem(GUdevClient *self, const gchar *subsystem)
+{
+    GList *l = g_list_copy(self->priv->udevice_list);
+    g_list_foreach(l, (GFunc)g_object_ref, NULL);
+    return l;
+}
+
+static GObject *g_udev_client_constructor(GType gtype, guint n_properties,
+                                          GObjectConstructParam *properties)
+{
+    GObject *obj = G_OBJECT_CLASS(g_udev_client_parent_class)->constructor(
+        gtype, n_properties, properties);
+    return obj;
+}
+
+static void g_udev_client_init(GUdevClient *self)
+{
+    GUdevClientPrivate *priv;
+
+    self->priv = priv = G_UDEV_CLIENT_GET_PRIVATE(self);
+    priv->usb_dev_mgr = NULL;
+    priv->dev_list = NULL;
+    priv->dev_list_size = 0;
+    priv->udevice_list = NULL;
+    priv->gdk_win = NULL;
+}
+
+static void g_udev_client_finalize(GObject *gobject)
+{
+    GUdevClient *self = G_UDEV_CLIENT(gobject);
+    GUdevClientPrivate *priv = self->priv;
+
+    if (priv->usb_dev_mgr || priv->gdk_win) {
+        gdk_window_remove_filter(priv->gdk_win, win_message_cb, priv->usb_dev_mgr);
+        g_list_free_full(priv->udevice_list, g_free);
+        libusb_free_device_list(priv->dev_list, 1);
+        libusb_exit(NULL);
+    }
+    /* Chain up to the parent class */
+    if (G_OBJECT_CLASS(g_udev_client_parent_class)->finalize)
+        G_OBJECT_CLASS(g_udev_client_parent_class)->finalize(gobject);
+}
+
+static void g_udev_client_class_init(GUdevClientClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+
+    gobject_class->constructor = g_udev_client_constructor;
+    gobject_class->finalize = g_udev_client_finalize;
+
+    signals[UEVENT_SIGNAL] =
+        g_signal_new("uevent",
+                     G_OBJECT_CLASS_TYPE(klass),
+                     G_SIGNAL_RUN_FIRST,
+                     G_STRUCT_OFFSET(GUdevClientClass, uevent),
+                     NULL, NULL,
+                     g_cclosure_user_marshal_VOID__BOXED_BOXED,
+                     G_TYPE_NONE,
+                     2,
+                     G_TYPE_STRING,
+                     G_UDEV_TYPE_DEVICE);
+
+    g_type_class_add_private(klass, sizeof(GUdevClientPrivate));
+}
+
+gboolean get_usb_dev_info(libusb_device *dev, GUdevDeviceInfo *usbdev)
+{
+    if (!usbdev) {
+        return FALSE;
+    }
+    if (libusb_get_device_descriptor(dev, &usbdev->desc) < 0) {
+        return FALSE;
+    }
+    usbdev->dev = libusb_ref_device(dev);
+    sprintf(usbdev->sclass, "%d", usbdev->desc.bDeviceClass);
+    sprintf(usbdev->sbus, "%d", libusb_get_bus_number(dev));
+    sprintf(usbdev->saddr, "%d", libusb_get_device_address(dev));
+    return TRUE;
+}
+
+static void handle_dev_change(GUdevClient *self)
+{
+    GUdevClientPrivate *priv = self->priv;
+    libusb_device *changed_dev = NULL;
+    libusb_device **devs, **dev, **d;
+    GUdevDeviceInfo *usbdev;
+    gboolean dev_found = FALSE;
+    GUdevDevice *udevice;
+    ssize_t dev_count;
+    int dev_change;
+
+    /* Assume each event stands for a single device change (at most) */
+    dev_count = libusb_get_device_list(NULL, &devs);
+    dev_change = dev_count - priv->dev_list_size;
+    if (dev_change == 0) {
+        libusb_free_device_list(devs, 1);
+        return;
+    }
+    for (dev = (dev_change > 0 ? devs : priv->dev_list); *dev && !changed_dev; dev++) {
+        for (d = (dev_change > 0 ? priv->dev_list : devs); *d && *d != *dev; d++);
+        if (!*d) {
+            changed_dev = *dev;
+        }
+    }
+    if (!changed_dev) {
+        goto leave;
+    }
+
+    if (dev_change > 0) {
+        usbdev = g_new(GUdevDeviceInfo, 1);
+        get_usb_dev_info(changed_dev, usbdev);
+        udevice = g_udev_device_new(usbdev);
+        priv->udevice_list = g_list_append(priv->udevice_list, udevice);
+        SPICE_DEBUG(">>> device inserted: %04x:%04x (bus %s, device %s)\n",
+                    usbdev->desc.idVendor, usbdev->desc.idProduct, usbdev->sbus, usbdev->saddr);
+        g_signal_emit(self, signals[UEVENT_SIGNAL], 0, "add", udevice);
+    } else {
+        dev_found = FALSE;
+        for (GList *it = g_list_first(priv->udevice_list); it != NULL && !dev_found; it = g_list_next(it)) {
+            udevice = (GUdevDevice*)it->data;
+            usbdev = udevice->priv->usbdev;
+            dev_found = (usbdev->dev == changed_dev);
+        }
+        if (!dev_found) {
+            goto leave;
+        }
+        SPICE_DEBUG("<<< device removed: %04x:%04x (bus %s, device %s)\n",
+                    usbdev->desc.idVendor, usbdev->desc.idProduct, usbdev->sbus, usbdev->saddr);
+        g_signal_emit(self, signals[UEVENT_SIGNAL], 0, "remove", udevice);
+        priv->udevice_list = g_list_remove(priv->udevice_list, udevice);
+        g_object_unref(udevice);
+    }
+
+leave:
+    libusb_free_device_list(priv->dev_list, 1);
+    priv->dev_list = devs;
+    priv->dev_list_size = dev_count;
+}
+
+static GdkFilterReturn win_message_cb(GdkXEvent *wmevent, GdkEvent *event, gpointer data)
+{
+    GUdevClient *self = G_UDEV_CLIENT(data);
+    MSG *msg = (MSG*)wmevent;
+
+    /* Only DBT_DEVNODES_CHANGED recieved */
+    if (msg->message == WM_DEVICECHANGE) {
+        handle_dev_change(self);
+    }
+    return GDK_FILTER_CONTINUE;
+}
+
+gboolean g_udev_client_win_init(GUdevClient *self, SpiceUsbDeviceManager *manager, GdkWindow *win)
+{
+    GUdevClientPrivate *priv = self->priv;
+
+    if (priv->usb_dev_mgr || priv->gdk_win || !manager || !win) {
+        return FALSE;
+    }
+    priv->usb_dev_mgr = manager;
+    priv->gdk_win = win;
+    gdk_window_add_filter(win, win_message_cb, self);
+    return TRUE;
+}
+
+/*** GUdevDevice ***/
+
+static void g_udev_device_finalize(GObject *object)
+{
+    GUdevDevice *device =  G_UDEV_DEVICE(object);
+
+    libusb_unref_device(device->priv->usbdev->dev);
+    g_free(device->priv->usbdev);
+    if (G_OBJECT_CLASS(g_udev_device_parent_class)->finalize != NULL)
+        (* G_OBJECT_CLASS(g_udev_device_parent_class)->finalize)(object);
+}
+
+static void g_udev_device_class_init(GUdevDeviceClass *klass)
+{
+    GObjectClass *gobject_class = (GObjectClass *) klass;
+
+    gobject_class->finalize = g_udev_device_finalize;
+    g_type_class_add_private (klass, sizeof(GUdevDevicePrivate));
+}
+
+static void g_udev_device_init(GUdevDevice *device)
+{
+    device->priv = G_TYPE_INSTANCE_GET_PRIVATE(device, G_UDEV_TYPE_DEVICE, GUdevDevicePrivate);
+}
+
+static GUdevDevice *g_udev_device_new(GUdevDeviceInfo *usbdev)
+{
+    GUdevDevice *device =  G_UDEV_DEVICE(g_object_new(G_UDEV_TYPE_DEVICE, NULL));
+
+    device->priv->usbdev = usbdev;
+    return device;
+}
+
+const gchar *g_udev_device_get_property(GUdevDevice *udev, const gchar *property)
+{
+    GUdevDeviceInfo* usbdev = udev->priv->usbdev;
+
+    if (strcmp(property, "BUSNUM") == 0) {
+        return usbdev->sbus;
+    } else if (strcmp(property, "DEVNUM") == 0) {
+        return usbdev->saddr;
+    } else if (strcmp(property, "DEVTYPE") == 0) {
+        return "usb_device";
+    }
+    return NULL;
+}
+
+const gchar *g_udev_device_get_sysfs_attr(GUdevDevice *udev, const gchar *attr)
+{
+    GUdevDeviceInfo* usbdev = udev->priv->usbdev;
+
+    if (strcmp(attr, "bDeviceClass") == 0) {
+        return usbdev->sclass;
+    }
+    return NULL;
+}
+
diff --git a/gtk/win-usb-dev.h b/gtk/win-usb-dev.h
new file mode 100644
index 0000000..25d4474
--- /dev/null
+++ b/gtk/win-usb-dev.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2012 Red Hat, Inc.
+
+   Red Hat Authors:
+   Arnon Gilboa <agilboa 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 __WIN_USB_DEV_H__
+#define __WIN_USB_DEV_H__
+
+#include <gtk/gtk.h>
+
+
+G_BEGIN_DECLS
+
+/* GUdevDevice */
+
+#define G_UDEV_TYPE_DEVICE         (g_udev_device_get_type())
+#define G_UDEV_DEVICE(o)           (G_TYPE_CHECK_INSTANCE_CAST((o), G_UDEV_TYPE_DEVICE, GUdevDevice))
+#define G_UDEV_DEVICE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_UDEV_TYPE_DEVICE, GUdevDeviceClass))
+#define G_UDEV_IS_DEVICE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_UDEV_TYPE_DEVICE))
+#define G_UDEV_IS_DEVICE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE((k), G_UDEV_TYPE_DEVICE))
+#define G_UDEV_DEVICE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), G_UDEV_TYPE_DEVICE, GUdevDeviceClass))
+
+typedef struct _GUdevDevice GUdevDevice;
+typedef struct _GUdevDeviceClass GUdevDeviceClass;
+typedef struct _GUdevDevicePrivate GUdevDevicePrivate;
+
+struct _GUdevDevice
+{
+  GObject parent;
+  GUdevDevicePrivate *priv;
+};
+
+struct _GUdevDeviceClass
+{
+  GObjectClass parent_class;
+};
+
+/* GUdevClient */
+
+#define G_UDEV_TYPE_CLIENT         (g_udev_client_get_type())
+#define G_UDEV_CLIENT(o)           (G_TYPE_CHECK_INSTANCE_CAST((o), G_UDEV_TYPE_CLIENT, GUdevClient))
+#define G_UDEV_CLIENT_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_UDEV_TYPE_CLIENT, GUdevClientClass))
+#define G_UDEV_IS_CLIENT(o)        (G_TYPE_CHECK_INSTANCE_TYPE((o), G_UDEV_TYPE_CLIENT))
+#define G_UDEV_IS_CLIENT_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE((k), G_UDEV_TYPE_CLIENT))
+#define G_UDEV_CLIENT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), G_UDEV_TYPE_CLIENT, GUdevClientClass))
+
+typedef struct _GUdevClient GUdevClient;
+typedef struct _GUdevClientClass GUdevClientClass;
+typedef struct _GUdevClientPrivate GUdevClientPrivate;
+
+struct _GUdevClient
+{
+    GObject parent;
+
+    GUdevClientPrivate *priv;
+};
+
+struct _GUdevClientClass
+{
+    GObjectClass parent_class;
+
+    /* signals */
+    void (*uevent)(GUdevClient *client, const gchar *action, GUdevDevice  *device);
+};
+
+typedef struct _SpiceUsbDeviceManager SpiceUsbDeviceManager;
+
+GType g_udev_client_get_type(void) G_GNUC_CONST;
+GUdevClient *g_udev_client_new(const gchar* const *subsystems);
+GList *g_udev_client_query_by_subsystem(GUdevClient *client, const gchar *subsystem);
+
+gboolean g_udev_client_win_init(GUdevClient *self, SpiceUsbDeviceManager *manager, GdkWindow *win);
+
+GType g_udev_device_get_type(void) G_GNUC_CONST;
+const gchar *g_udev_device_get_property(GUdevDevice *udev, const gchar *property);
+const gchar *g_udev_device_get_sysfs_attr(GUdevDevice *udev, const gchar *attr);
+
+G_END_DECLS
+
+#endif /* __WIN_USB_DEV_H__ */
-- 
1.7.7.6



More information about the Spice-devel mailing list