From bff94f2365aba52e7566b80f5d4240ad7fcad343 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Fri, 31 Jan 2014 09:13:53 -0500 Subject: [PATCH] console: Support spice 'resize-guest' (bz 754559) This will auto change the guest resolution to match the window size. Off by default, can be enabled like scaling preferences. --- .../org.virt-manager.virt-manager.gschema.xml | 12 +++ ui/details.ui | 15 ++++ ui/preferences.ui | 51 ++++++++--- virtManager/config.py | 16 +++- virtManager/console.py | 85 +++++++++++++++++-- virtManager/details.py | 1 + virtManager/domain.py | 11 +++ virtManager/preferences.py | 23 +++++ virtManager/uiutil.py | 2 +- 9 files changed, 193 insertions(+), 23 deletions(-) diff --git a/data/org.virt-manager.virt-manager.gschema.xml b/data/org.virt-manager.virt-manager.gschema.xml index 2566e1ac..9a10b205 100644 --- a/data/org.virt-manager.virt-manager.gschema.xml +++ b/data/org.virt-manager.virt-manager.gschema.xml @@ -19,6 +19,12 @@ Username and secrets ID for graphical password Username and secrets ID for graphical password + + + -1 + Automatically resize guest when window size changes + Automatically change guest resolution along with virt-manager window. Only works with spice with a vdagent set up. -1 = global default, 0 = off, 1 = on. + When to scale the VM graphical console. 0 = never, 1 = only when in full screen mode, 2 = Always + + -1 + Automatically resize guest when window size changes + Automatically change guest resolution along with virt-manager window. Only works with spice with a vdagent set up. -1 = global default, 0 = off, 1 = on. + + '' Grab keyboard sequence for the graphical console diff --git a/ui/details.ui b/ui/details.ui index 8de463de..3ccd2c23 100644 --- a/ui/details.ui +++ b/ui/details.ui @@ -301,6 +301,21 @@ + + + True + False + + + + + True + False + Auto _resize VM with window + True + + + diff --git a/ui/preferences.ui b/ui/preferences.ui index cfb4b3ec..e673fdf5 100644 --- a/ui/preferences.ui +++ b/ui/preferences.ui @@ -1,18 +1,13 @@ - + - + 1 60 1 5 - - 300 - 5 - 5 - False 12 @@ -542,7 +537,7 @@ identical CPUs in order to migrate the VM. 0 - 1 + 2 1 1 @@ -572,7 +567,7 @@ identical CPUs in order to migrate the VM. 1 - 2 + 3 1 1 @@ -589,7 +584,7 @@ identical CPUs in order to migrate the VM. 0 - 2 + 3 1 1 @@ -604,6 +599,37 @@ identical CPUs in order to migrate the VM. False + + 1 + 2 + 1 + 1 + + + + + True + False + Change guest resolution when the guest window size is changed. Only works with properly configured guest using spice and the desktop agent. + 0 + _Resize guest with window: + True + prefs-console-resizeguest + 2 + + + 0 + 1 + 1 + 1 + + + + + True + False + + 1 1 @@ -946,4 +972,9 @@ identical CPUs in order to migrate the VM. + + 300 + 5 + 5 + diff --git a/virtManager/config.py b/virtManager/config.py index c2d5e361..880775a7 100644 --- a/virtManager/config.py +++ b/virtManager/config.py @@ -135,10 +135,6 @@ class vmmConfig(object): CONSOLE_SCALE_FULLSCREEN = 1 CONSOLE_SCALE_ALWAYS = 2 - _PEROBJ_FUNC_SET = 0 - _PEROBJ_FUNC_GET = 1 - _PEROBJ_FUNC_LISTEN = 2 - DEFAULT_XEN_IMAGE_DIR = "/var/lib/xen/images" DEFAULT_XEN_SAVE_DIR = "/var/lib/xen/dump" @@ -166,8 +162,10 @@ class vmmConfig(object): self.libvirt_packages = cliconfig.libvirt_packages self.askpass_package = cliconfig.askpass_package self.default_graphics_from_config = cliconfig.default_graphics + self.default_storage_format_from_config = "qcow2" self.cpu_default_from_config = "host-cpu-model" + self.default_console_resizeguest = 0 self._objects = [] @@ -381,6 +379,16 @@ class vmmConfig(object): def set_console_scaling(self, pref): self.conf.set("/console/scaling", pref) + def on_console_resizeguest_changed(self, cb): + return self.conf.notify_add("/console/resize-guest", cb) + def get_console_resizeguest(self): + val = self.conf.get("/console/resize-guest") + if val == -1: + val = self.default_console_resizeguest + return val + def set_console_resizeguest(self, pref): + self.conf.set("/console/resize-guest", pref) + def get_auto_redirection(self): return self.conf.get("/console/auto-redirect") def set_auto_redirection(self, state): diff --git a/virtManager/console.py b/virtManager/console.py index 129bb046..823089d5 100644 --- a/virtManager/console.py +++ b/virtManager/console.py @@ -371,9 +371,15 @@ class Viewer(vmmGObject): def has_usb_redirection(self): return False + def has_agent(self): + return False + def set_resizeguest(self, val): + ignore = val class VNCViewer(Viewer): + viewer_type = "vnc" + def __init__(self, console): Viewer.__init__(self, console) self.display = GtkVnc.Display.new() @@ -393,6 +399,7 @@ class VNCViewer(Viewer): self.display.set_force_size(False) self.console.sync_scaling_with_display() + self.console.refresh_resizeguest_from_settings() self.display.set_keyboard_grab(True) self.display.set_pointer_grab(True) @@ -549,17 +556,21 @@ class VNCViewer(Viewer): class SpiceViewer(Viewer): + viewer_type = "spice" + def __init__(self, console): Viewer.__init__(self, console) self.spice_session = None self.display = None self.audio = None + self.main_channel = None self.display_channel = None self.usbdev_manager = None def _init_widget(self): self.set_grab_keys() self.console.sync_scaling_with_display() + self.console.refresh_resizeguest_from_settings() self.display.realize() @@ -613,6 +624,7 @@ class SpiceViewer(Viewer): self.display.destroy() self.display = None self.display_channel = None + self.main_channel = None self.usbdev_manager = None def is_open(self): @@ -653,13 +665,17 @@ class SpiceViewer(Viewer): GObject.GObject.connect(channel, "open-fd", self._channel_open_fd_request) - if type(channel) == SpiceClientGLib.MainChannel: + if (type(channel) == SpiceClientGLib.MainChannel and + not self.main_channel): if self.console.tunnels: self.console.tunnels.unlock() - channel.connect_after("channel-event", self._main_channel_event_cb) - return + self.main_channel = channel + self.main_channel.connect_after("channel-event", + self._main_channel_event_cb) + self.main_channel.connect_after("notify::agent-connected", + self._agent_connected_cb) - if (type(channel) == SpiceClientGLib.DisplayChannel and + elif (type(channel) == SpiceClientGLib.DisplayChannel and not self.display): channel_id = channel.get_property("channel-id") @@ -673,13 +689,11 @@ class SpiceViewer(Viewer): self.console.widget("console-gfx-viewport").add(self.display) self._init_widget() self.console.connected() - return - if (type(channel) in [SpiceClientGLib.PlaybackChannel, - SpiceClientGLib.RecordChannel] and + elif (type(channel) in [SpiceClientGLib.PlaybackChannel, + SpiceClientGLib.RecordChannel] and not self.audio): self.audio = SpiceClientGLib.Audio.get(self.spice_session, None) - return def get_desktop_resolution(self): if (not self.display_channel or @@ -687,6 +701,16 @@ class SpiceViewer(Viewer): return None return self.display_channel.get_properties("width", "height") + def has_agent(self): + if (not self.main_channel or + not has_property(self.main_channel, "agent-connected")): + return False + ret = self.main_channel.get_property("agent-connected") + return ret + + def _agent_connected_cb(self, src, val): + self.console.refresh_resizeguest_from_settings() + def _create_spice_session(self): self.spice_session = SpiceClientGLib.Session() SpiceClientGLib.set_session_option(self.spice_session) @@ -742,6 +766,10 @@ class SpiceViewer(Viewer): return self.display.set_property("scaling", scaling) + def set_resizeguest(self, val): + if self.display: + self.display.set_property("resize-guest", val) + def _usbdev_redirect_error(self, spice_usbdev_widget, spice_usb_device, errstr): @@ -829,6 +857,10 @@ class vmmConsolePages(vmmGObjectUI): self.add_gconf_handle( self.vm.on_console_scaling_changed( self.refresh_scaling_from_settings)) + self.refresh_resizeguest_from_settings() + self.add_gconf_handle( + self.vm.on_console_resizeguest_changed( + self.refresh_resizeguest_from_settings)) scroll = self.widget("console-gfx-scroll") scroll.connect("size-allocate", self.scroll_size_allocate) @@ -1032,6 +1064,42 @@ class vmmConsolePages(vmmGObjectUI): # Make sure modifiers are up to date self.viewer_focus_changed() + def refresh_resizeguest_from_settings(self): + tooltip = "" + if self.viewer: + if self.viewer.viewer_type != "spice": + tooltip = ( + _("Graphics type '%s' does not support auto resize.") % + self.viewer.viewer_type) + elif not self.viewer.has_agent(): + tooltip = _("Guest agent is not available.") + + val = self.vm.get_console_resizeguest() + widget = self.widget("details-menu-view-resizeguest") + widget.set_tooltip_text(tooltip) + widget.set_sensitive(not bool(tooltip)) + if not tooltip: + self.widget("details-menu-view-resizeguest").set_active(bool(val)) + + self.sync_resizeguest_with_display() + + def resizeguest_ui_changed_cb(self, src): + # Called from details.py + if not src.get_active(): + return + + val = int(self.widget("details-menu-view-resizeguest").get_active()) + self.vm.set_console_resizeguest(val) + self.sync_resizeguest_with_display() + + def sync_resizeguest_with_display(self): + if not self.viewer: + return + + val = bool(self.vm.get_console_resizeguest()) + self.viewer.set_resizeguest(val) + self.widget("console-gfx-scroll").queue_resize() + def refresh_scaling_from_settings(self): scale_type = self.vm.get_console_scaling() self.widget("details-menu-view-scale-always").set_active( @@ -1287,6 +1355,7 @@ class vmmConsolePages(vmmGObjectUI): error += "\n\nError: %s" % errout self.activate_unavailable_page(error) + self.refresh_resizeguest_from_settings() def _set_viewer_connected(self, val): self._viewer_connected = val diff --git a/virtManager/details.py b/virtManager/details.py index 7bb67d4d..4dfd6d24 100644 --- a/virtManager/details.py +++ b/virtManager/details.py @@ -547,6 +547,7 @@ class vmmDetails(vmmGObjectUI): "on_details_menu_view_scale_always_toggled": self.console.scaling_ui_changed_cb, "on_details_menu_view_scale_fullscreen_toggled": self.console.scaling_ui_changed_cb, "on_details_menu_view_scale_never_toggled": self.console.scaling_ui_changed_cb, + "on_details_menu_view_resizeguest_toggled": self.console.resizeguest_ui_changed_cb, "on_console_pages_switch_page": self.console.page_changed, "on_console_auth_password_activate": self.console.auth_login, diff --git a/virtManager/domain.py b/virtManager/domain.py index 363c87e2..fc916919 100644 --- a/virtManager/domain.py +++ b/virtManager/domain.py @@ -1627,6 +1627,17 @@ class vmmDomain(vmmLibvirtObject): return self.config.get_console_scaling() return ret + def on_console_resizeguest_changed(self, *args, **kwargs): + return self.config.listen_pervm(self.uuid, "/resize-guest", + *args, **kwargs) + def set_console_resizeguest(self, value): + self.config.set_pervm(self.uuid, "/resize-guest", value) + def get_console_resizeguest(self): + ret = self.config.get_pervm(self.uuid, "/resize-guest") + if ret == -1: + return self.config.get_console_resizeguest() + return ret + def set_details_window_size(self, w, h): self.config.set_pervm(self.uuid, "/vm-window-size", (w, h)) def get_details_window_size(self): diff --git a/virtManager/preferences.py b/virtManager/preferences.py index 2965ed41..5f4a48f1 100644 --- a/virtManager/preferences.py +++ b/virtManager/preferences.py @@ -39,6 +39,7 @@ class vmmPreferences(vmmGObjectUI): self.refresh_update_interval() self.refresh_console_accels() self.refresh_console_scaling() + self.refresh_console_resizeguest() self.refresh_new_vm_sound() self.refresh_graphics_type() self.refresh_storage_format() @@ -60,6 +61,7 @@ class vmmPreferences(vmmGObjectUI): "on_prefs_stats_update_interval_changed": self.change_update_interval, "on_prefs_console_accels_toggled": self.change_console_accels, "on_prefs_console_scaling_changed": self.change_console_scaling, + "on_prefs_console_resizeguest_changed": self.change_console_resizeguest, "on_prefs_close_clicked": self.close, "on_vmm_preferences_delete_event": self.close, "on_prefs_new_vm_sound_toggled": self.change_new_vm_sound, @@ -104,6 +106,20 @@ class vmmPreferences(vmmGObjectUI): combo.set_model(model) uiutil.set_combo_text_column(combo, 1) + combo = self.widget("prefs-console-resizeguest") + # [gsettings value, string] + model = Gtk.ListStore(int, str) + vals = { + 0: _("Off"), + 1: _("On"), + } + model.append([-1, _("System default (%s)") % + vals[self.config.default_console_resizeguest]]) + for key, val in vals.items(): + model.append([key, val]) + combo.set_model(model) + uiutil.set_combo_text_column(combo, 1) + combo = self.widget("prefs-graphics-type") # [gsettings value, string] model = Gtk.ListStore(str, str) @@ -157,6 +173,10 @@ class vmmPreferences(vmmGObjectUI): combo = self.widget("prefs-console-scaling") val = self.config.get_console_scaling() uiutil.set_row_selection(combo, val) + def refresh_console_resizeguest(self): + combo = self.widget("prefs-console-resizeguest") + val = self.config.get_console_resizeguest() + uiutil.set_row_selection(combo, val) def refresh_new_vm_sound(self): self.widget("prefs-new-vm-sound").set_active( @@ -294,6 +314,9 @@ class vmmPreferences(vmmGObjectUI): self.config.set_console_accels(src.get_active()) def change_console_scaling(self, box): self.config.set_console_scaling(box.get_active()) + def change_console_resizeguest(self, box): + val = uiutil.get_list_selection(box, 0) + self.config.set_console_resizeguest(val) def change_new_vm_sound(self, src): self.config.set_new_vm_sound(src.get_active()) diff --git a/virtManager/uiutil.py b/virtManager/uiutil.py index 86f953cd..a6d65d18 100644 --- a/virtManager/uiutil.py +++ b/virtManager/uiutil.py @@ -103,7 +103,7 @@ def set_row_selection(listwidget, prevkey): """ model = listwidget.get_model() _iter = None - if prevkey: + if prevkey is not None: for row in model: if row[0] == prevkey: _iter = row.iter