diff --git a/ui/vmm-details.ui b/ui/vmm-details.ui
index ea4b53ee7..8da5b3263 100644
--- a/ui/vmm-details.ui
+++ b/ui/vmm-details.ui
@@ -337,6 +337,21 @@
+
+
+
+
+
+
diff --git a/virtManager/console.py b/virtManager/console.py
index 001318e30..a2fc4f8ff 100644
--- a/virtManager/console.py
+++ b/virtManager/console.py
@@ -286,6 +286,7 @@ class VNCViewer(Viewer):
Viewer.__init__(self, console)
self.display = GtkVnc.Display.new()
self.sockfd = None
+ self.type = "vnc"
# Last noticed desktop resolution
self.desktop_resolution = None
@@ -446,6 +447,7 @@ class SpiceViewer(Viewer):
self.display = None
self.audio = None
self.display_channel = None
+ self.type = "spice"
def _init_widget(self):
self.set_grab_keys()
@@ -594,6 +596,22 @@ class SpiceViewer(Viewer):
return
self.display.set_property("scaling", scaling)
+ def get_usb_widget(self):
+
+ # The @format positional parameters are the following:
+ # 1 '%s' manufacturer
+ # 2 '%s' product
+ # 3 '%s' descriptor (a [vendor_id:product_id] string)
+ # 4 '%d' bus
+ # 5 '%d' address
+
+ usb_device_description_fmt = _("%s %s %s at %d-%d")
+
+ if self.spice_session:
+ return SpiceClientGtk.UsbDeviceWidget.new(self.spice_session,
+ usb_device_description_fmt)
+ return
+
class vmmConsolePages(vmmGObjectUI):
def __init__(self, vm, builder, topwin):
@@ -955,11 +973,13 @@ class vmmConsolePages(vmmGObjectUI):
self.close_viewer()
self.widget("console-pages").set_current_page(PAGE_UNAVAILABLE)
self.widget("details-menu-vm-screenshot").set_sensitive(False)
+ self.widget("details-menu-usb-redirection").set_sensitive(False)
self.widget("console-unavailable").set_label("" + msg + "")
def activate_auth_page(self, withPassword=True, withUsername=False):
(pw, username) = self.config.get_console_password(self.vm)
self.widget("details-menu-vm-screenshot").set_sensitive(False)
+ self.widget("details-menu-usb-redirection").set_sensitive(False)
if withPassword:
self.widget("console-auth-password").show()
@@ -999,6 +1019,9 @@ class vmmConsolePages(vmmGObjectUI):
if self.viewer and self.viewer.display:
self.viewer.display.grab_focus()
+ if self.viewer.type == "spice":
+ self.widget("details-menu-usb-redirection").set_sensitive(True)
+
def page_changed(self, ignore1=None, ignore2=None, ignore3=None):
self.set_allow_fullscreen()
diff --git a/virtManager/details.py b/virtManager/details.py
index b2d496e52..9a688198a 100644
--- a/virtManager/details.py
+++ b/virtManager/details.py
@@ -415,6 +415,7 @@ class vmmDetails(vmmGObjectUI):
"on_details_menu_migrate_activate": self.control_vm_migrate,
"on_details_menu_delete_activate": self.control_vm_delete,
"on_details_menu_screenshot_activate": self.control_vm_screenshot,
+ "on_details_menu_usb_redirection": self.control_vm_usb_redirection,
"on_details_menu_view_toolbar_activate": self.toggle_toolbar,
"on_details_menu_view_manager_activate": self.view_manager,
"on_details_menu_view_details_toggled": self.details_console_changed,
@@ -1593,6 +1594,31 @@ class vmmDetails(vmmGObjectUI):
except Exception, e:
self.err.show_err(_("Error taking screenshot: %s") % str(e))
+ def spice_usbdev_rediret_error(self,
+ spice_usbdev_widget, spice_usb_device,
+ errstr):
+ ignore_widget = spice_usbdev_widget
+ ignore_device = spice_usb_device
+
+ self.err.show_err(_("USB redirection error"),
+ text2=str(errstr),
+ async=False)
+
+ def control_vm_usb_redirection(self, src):
+ ignore = src
+ spice_usbdev_dialog = self.err
+
+ spice_usbdev_widget = self.console.viewer.get_usb_widget()
+ if not spice_usbdev_widget:
+ self.err.show_err(_("Error initialize spice USB device widget"))
+ return
+
+ spice_usbdev_widget.connect("connect-failed",
+ self.spice_usbdev_rediret_error)
+ spice_usbdev_widget.show()
+ spice_usbdev_dialog.show_info_with_widget(_("Select USB devices for redirection"),
+ widget=spice_usbdev_widget)
+
def _take_screenshot(self):
image = self.console.viewer.get_pixbuf()