[Spice-devel] [PATCH spice-gtk 5/6] usbredir: Add a spice_usb_device_manager_can_redirect_device function

Hans de Goede hdegoede at redhat.com
Tue Feb 21 06:12:24 PST 2012


Add a spice_usb_device_manager_can_redirect_device function and use this
in SpiceUsbDeviceWidget to check if redirection is available.

Signed-off-by: Hans de Goede <hdegoede at redhat.com>
---
 gtk/map-file             |    1 +
 gtk/usb-device-manager.c |   71 +++++++++++++++++++++++++++++---
 gtk/usb-device-manager.h |    5 ++
 gtk/usb-device-widget.c  |  101 ++++++++++++++++++++++++++++++++++------------
 4 files changed, 146 insertions(+), 32 deletions(-)

diff --git a/gtk/map-file b/gtk/map-file
index 320dae3..81cd682 100644
--- a/gtk/map-file
+++ b/gtk/map-file
@@ -85,6 +85,7 @@ spice_smartcard_reader_get_type;
 spice_smartcard_reader_is_software;
 spice_usb_device_get_description;
 spice_usb_device_get_type;
+spice_usb_device_manager_can_redirect_device;
 spice_usb_device_manager_connect_device_async;
 spice_usb_device_manager_connect_device_finish;
 spice_usb_device_manager_disconnect_device;
diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
index cc67131..c07ac7a 100644
--- a/gtk/usb-device-manager.c
+++ b/gtk/usb-device-manager.c
@@ -212,7 +212,7 @@ static gboolean spice_usb_device_manager_initable_init(GInitable  *initable,
     return TRUE;
 #else
     g_set_error_literal(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
-                        "USB redirection support not compiled in");
+                        _("USB redirection support not compiled in"));
     return FALSE;
 #endif
 }
@@ -592,10 +592,17 @@ static void spice_usb_device_manager_add_dev(SpiceUsbDeviceManager  *self,
     g_ptr_array_add(priv->devices, device);
 
     if (priv->auto_connect) {
-        if (usbredirhost_check_device_filter(
-                priv->auto_conn_filter_rules,
-                priv->auto_conn_filter_rules_count,
-                device, 0) == 0)
+        gboolean can_redirect, auto_ok;
+
+        can_redirect = spice_usb_device_manager_can_redirect_device(
+                                        self, (SpiceUsbDevice *)device, NULL);
+
+        auto_ok = usbredirhost_check_device_filter(
+                            priv->auto_conn_filter_rules,
+                            priv->auto_conn_filter_rules_count,
+                            device, 0) == 0;
+
+        if (can_redirect && auto_ok)
             spice_usb_device_manager_connect_device_async(self,
                                    (SpiceUsbDevice *)device, NULL,
                                    spice_usb_device_manager_auto_connect_cb,
@@ -869,7 +876,7 @@ void spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
 
     g_simple_async_result_set_error(result,
                             SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
-                            "No free USB channel");
+                            _("No free USB channel"));
 #ifdef USE_USBREDIR
 done:
 #endif
@@ -916,6 +923,58 @@ void spice_usb_device_manager_disconnect_device(SpiceUsbDeviceManager *self,
 #endif
 }
 
+gboolean
+spice_usb_device_manager_can_redirect_device(SpiceUsbDeviceManager  *self,
+                                             SpiceUsbDevice         *device,
+                                             GError                **err)
+{
+#ifdef USE_USBREDIR
+    SpiceUsbDeviceManagerPrivate *priv = self->priv;
+    int i;
+    gboolean enabled;
+
+    g_return_val_if_fail(SPICE_IS_USB_DEVICE_MANAGER(self), FALSE);
+    g_return_val_if_fail(device != NULL, FALSE);
+    g_return_val_if_fail(err == NULL || *err == NULL, FALSE);
+
+    g_object_get(G_OBJECT(priv->session), "enable-usbredir", &enabled, NULL);
+    if (!enabled) {
+        g_set_error_literal(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
+                            _("USB redirection is disabled"));
+        return FALSE;
+    }
+
+    if (!priv->channels->len) {
+        g_set_error_literal(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
+                            _("The connected VM is not configured for USB redirection"));
+        return FALSE;
+    }
+
+    /* Skip the other checks for already connected devices */
+    if (spice_usb_device_manager_is_device_connected(self, device))
+        return TRUE;
+
+    /* Check if there are free channels */
+    for (i = 0; i < priv->channels->len; i++) {
+        SpiceUsbredirChannel *channel = g_ptr_array_index(priv->channels, i);
+
+        if (!spice_usbredir_channel_get_device(channel))
+            break;
+    }
+    if (i == priv->channels->len) {
+        g_set_error_literal(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
+                            _("There are no free USB channels"));
+        return FALSE;
+    }
+
+    return TRUE;
+#else
+    g_set_error_literal(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
+                        _("USB redirection support not compiled in"));
+    return FALSE;
+#endif
+}
+
 /**
  * spice_usb_device_get_description:
  * @device: #SpiceUsbDevice to get the description of
diff --git a/gtk/usb-device-manager.h b/gtk/usb-device-manager.h
index eabfd00..de0e2ba 100644
--- a/gtk/usb-device-manager.h
+++ b/gtk/usb-device-manager.h
@@ -110,6 +110,11 @@ gboolean spice_usb_device_manager_connect_device_finish(
 void spice_usb_device_manager_disconnect_device(SpiceUsbDeviceManager *manager,
                                                 SpiceUsbDevice *device);
 
+gboolean
+spice_usb_device_manager_can_redirect_device(SpiceUsbDeviceManager  *self,
+                                             SpiceUsbDevice         *device,
+                                             GError                **err);
+
 G_END_DECLS
 
 #endif /* __SPICE_USB_DEVICE_MANAGER_H__ */
diff --git a/gtk/usb-device-widget.c b/gtk/usb-device-widget.c
index cfece81..79258bd 100644
--- a/gtk/usb-device-widget.c
+++ b/gtk/usb-device-widget.c
@@ -71,6 +71,7 @@ struct _SpiceUsbDeviceWidgetPrivate {
     gchar *device_format_string;
     SpiceUsbDeviceManager *manager;
     GtkWidget *info_bar;
+    gchar *err_msg;
 };
 
 static guint signals[LAST_SIGNAL] = { 0, };
@@ -167,11 +168,9 @@ static GObject *spice_usb_device_widget_constructor(
     GObject *obj;
     SpiceUsbDeviceWidget *self;
     SpiceUsbDeviceWidgetPrivate *priv;
-    const gchar *err_msg = NULL;
     GPtrArray *devices = NULL;
     GError *err = NULL;
     GtkWidget *label;
-    gboolean enabled;
     gchar *str;
     int i;
 
@@ -187,28 +186,6 @@ static GObject *spice_usb_device_widget_constructor(
     if (!priv->session)
         g_error("SpiceUsbDeviceWidget constructed without a session");
 
-    g_object_get(G_OBJECT(priv->session), "enable-usbredir", &enabled, NULL);
-    if (!enabled)
-        err_msg = _("USB redirection is disabled");
-
-    if (!err_msg && !spice_session_has_channel_type(priv->session,
-                                                    SPICE_CHANNEL_USBREDIR))
-        err_msg = _("The connected VM is not configured for USB redirection");
-
-    if (!err_msg) {
-        priv->manager = spice_usb_device_manager_get(priv->session, &err);
-        if (!err) {
-            g_signal_connect(priv->manager, "device-added",
-                             G_CALLBACK(device_added_cb), self);
-            g_signal_connect(priv->manager, "device-removed",
-                             G_CALLBACK(device_removed_cb), self);
-            g_signal_connect(priv->manager, "device-error",
-                             G_CALLBACK(device_error_cb), self);
-            devices = spice_usb_device_manager_get_devices(priv->manager);
-        } else
-            err_msg = err->message;
-    }
-
     label = gtk_label_new(NULL);
     str = g_strdup_printf("<b>%s</b>", _("Select USB devices to redirect"));
     gtk_label_set_markup(GTK_LABEL (label), str);
@@ -216,14 +193,24 @@ static GObject *spice_usb_device_widget_constructor(
     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
     gtk_box_pack_start(GTK_BOX(self), label, FALSE, FALSE, 0);
 
-    if (err_msg) {
-        spice_usb_device_widget_show_info_bar(self, err_msg,
+    priv->manager = spice_usb_device_manager_get(priv->session, &err);
+    if (err) {
+        spice_usb_device_widget_show_info_bar(self, err->message,
                                               GTK_MESSAGE_WARNING,
                                               GTK_STOCK_DIALOG_WARNING);
         g_clear_error(&err);
         return obj;
     }
 
+    g_signal_connect(priv->manager, "device-added",
+                     G_CALLBACK(device_added_cb), self);
+    g_signal_connect(priv->manager, "device-removed",
+                     G_CALLBACK(device_removed_cb), self);
+    g_signal_connect(priv->manager, "device-error",
+                     G_CALLBACK(device_error_cb), self);
+
+    devices = spice_usb_device_manager_get_devices(priv->manager);
+
     for (i = 0; i < devices->len; i++)
         device_added_cb(NULL, g_ptr_array_index(devices, i), self);
 
@@ -351,6 +338,60 @@ static SpiceUsbDevice *get_usb_device(GtkWidget *widget)
     return g_object_get_data(G_OBJECT(widget), "usb-device");
 }
 
+static void check_can_redirect(GtkWidget *widget, gpointer user_data)
+{
+    SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_data);
+    SpiceUsbDeviceWidgetPrivate *priv = self->priv;
+    SpiceUsbDevice *device;
+    gboolean can_redirect;
+    GError *err = NULL;
+
+    device = get_usb_device(widget);
+    if (!device)
+        return; /* Non device widget, ie the info_bar */
+
+    can_redirect = spice_usb_device_manager_can_redirect_device(priv->manager,
+                                                                device, &err);
+    gtk_widget_set_sensitive(widget, can_redirect);
+
+    /* If we can not redirect this device, append the error message to
+       err_msg, but only if it is *not* already there! */
+    if (!can_redirect) {
+        if (priv->err_msg) {
+            if (!strstr(priv->err_msg, err->message)) {
+                gchar *old_err_msg = priv->err_msg;
+
+                priv->err_msg = g_strdup_printf("%s\n%s", priv->err_msg,
+                                                err->message);
+                g_free(old_err_msg);
+            }
+        } else {
+            priv->err_msg = g_strdup(err->message);
+        }
+    }
+
+    g_clear_error(&err);
+}
+
+static gboolean spice_usb_device_widget_update_status(gpointer user_data)
+{
+    SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_data);
+    SpiceUsbDeviceWidgetPrivate *priv = self->priv;
+
+    gtk_container_foreach(GTK_CONTAINER(self), check_can_redirect, self);
+
+    if (priv->err_msg) {
+        spice_usb_device_widget_show_info_bar(self, priv->err_msg,
+                                              GTK_MESSAGE_INFO,
+                                              GTK_STOCK_DIALOG_WARNING);
+        g_free(priv->err_msg);
+        priv->err_msg = NULL;
+    } else {
+        spice_usb_device_widget_hide_info_bar(self);
+    }
+    return FALSE;
+}
+
 typedef struct _connect_cb_data {
     GtkWidget *check;
     SpiceUsbDeviceWidget *self;
@@ -379,6 +420,7 @@ static void connect_cb(GObject *gobject, GAsyncResult *res, gpointer user_data)
         g_error_free(err);
 
         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->check), FALSE);
+        spice_usb_device_widget_update_status(self);
     }
 
     g_object_unref(data->check);
@@ -407,6 +449,7 @@ static void checkbox_clicked_cb(GtkWidget *check, gpointer user_data)
         spice_usb_device_manager_disconnect_device(priv->manager,
                                                    device);
     }
+    spice_usb_device_widget_update_status(self);
 }
 
 static void checkbox_usb_device_destroy_notify(gpointer data)
@@ -459,6 +502,8 @@ static void device_removed_cb(SpiceUsbDeviceManager *manager,
 
     gtk_container_foreach(GTK_CONTAINER(self),
                           destroy_widget_by_usb_device, device);
+
+    spice_usb_device_widget_update_status(self);
 }
 
 static void set_inactive_by_usb_device(GtkWidget *widget, gpointer user_data)
@@ -474,4 +519,8 @@ static void device_error_cb(SpiceUsbDeviceManager *manager,
 
     gtk_container_foreach(GTK_CONTAINER(self),
                           set_inactive_by_usb_device, device);
+
+    /* When we run the channel is still "in use", it only becomes free
+       after we run, so we update our status in idle. */
+    g_idle_add(spice_usb_device_widget_update_status, self);
 }
-- 
1.7.7.6



More information about the Spice-devel mailing list