diff --git a/data/org.virt-manager.virt-manager.gschema.xml b/data/org.virt-manager.virt-manager.gschema.xml
index 2566e1ac1..9a10b2056 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 8de463de6..3ccd2c233 100644
--- a/ui/details.ui
+++ b/ui/details.ui
@@ -301,6 +301,21 @@
+
+
+
+
+
+
diff --git a/ui/preferences.ui b/ui/preferences.ui
index cfb4b3ec5..e673fdf57 100644
--- a/ui/preferences.ui
+++ b/ui/preferences.ui
@@ -1,18 +1,13 @@
-
+
-
+
-
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
+
+
+
+
+
+ 0
+ 1
+ 1
+ 1
+
+
+
+
1
1
@@ -946,4 +972,9 @@ identical CPUs in order to migrate the VM.
+
diff --git a/virtManager/config.py b/virtManager/config.py
index c2d5e361f..880775a73 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 129bb046b..823089d54 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 7bb67d4d4..4dfd6d241 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 363c87e2a..fc916919f 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 2965ed417..5f4a48f1b 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 86f953cdc..a6d65d187 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