From: Arnon Gilboa <agilboa@xxxxxxxxxx> 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@xxxxxxxxxx> --- 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@xxxxxxxxxx> + + 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@xxxxxxxxxx> + + 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 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel