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.
This commit is contained in:
Cole Robinson 2014-01-31 09:13:53 -05:00
parent 382543e15f
commit bff94f2365
9 changed files with 193 additions and 23 deletions

View File

@ -19,6 +19,12 @@
<summary>Username and secrets ID for graphical password</summary> <summary>Username and secrets ID for graphical password</summary>
<description>Username and secrets ID for graphical password</description> <description>Username and secrets ID for graphical password</description>
</key> </key>
<key name="resize-guest" type="i">
<default>-1</default>
<summary>Automatically resize guest when window size changes</summary>
<description>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.</description>
</key>
</schema> </schema>
<schema id="org.virt-manager.virt-manager" <schema id="org.virt-manager.virt-manager"
@ -166,6 +172,12 @@
<description>When to scale the VM graphical console. 0 = never, 1 = only when in full screen mode, 2 = Always</description> <description>When to scale the VM graphical console. 0 = never, 1 = only when in full screen mode, 2 = Always</description>
</key> </key>
<key name="resize-guest" type="i">
<default>-1</default>
<summary>Automatically resize guest when window size changes</summary>
<description>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.</description>
</key>
<key name="grab-keys" type="s"> <key name="grab-keys" type="s">
<default>''</default> <default>''</default>
<summary>Grab keyboard sequence for the graphical console</summary> <summary>Grab keyboard sequence for the graphical console</summary>

View File

@ -301,6 +301,21 @@
<signal name="toggled" handler="on_details_menu_view_scale_never_toggled" swapped="no"/> <signal name="toggled" handler="on_details_menu_view_scale_never_toggled" swapped="no"/>
</object> </object>
</child> </child>
<child>
<object class="GtkSeparatorMenuItem" id="menuitem3">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkCheckMenuItem" id="details-menu-view-resizeguest">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Auto _resize VM with window</property>
<property name="use_underline">True</property>
<signal name="toggled" handler="on_details_menu_view_resizeguest_toggled" swapped="no"/>
</object>
</child>
</object> </object>
</child> </child>
</object> </object>

View File

@ -1,18 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.16.0 on Fri Jan 17 17:40:39 2014 --> <!-- Generated with glade 3.16.1 -->
<interface> <interface>
<!-- interface-requires gtk+ 3.0 --> <requires lib="gtk+" version="3.0"/>
<object class="GtkAdjustment" id="adjustment1"> <object class="GtkAdjustment" id="adjustment1">
<property name="lower">1</property> <property name="lower">1</property>
<property name="upper">60</property> <property name="upper">60</property>
<property name="step_increment">1</property> <property name="step_increment">1</property>
<property name="page_increment">5</property> <property name="page_increment">5</property>
</object> </object>
<object class="GtkAdjustment" id="adjustment2">
<property name="upper">300</property>
<property name="step_increment">5</property>
<property name="page_increment">5</property>
</object>
<object class="GtkWindow" id="vmm-preferences"> <object class="GtkWindow" id="vmm-preferences">
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="border_width">12</property> <property name="border_width">12</property>
@ -542,7 +537,7 @@ identical CPUs in order to migrate the VM.</property>
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
<property name="top_attach">1</property> <property name="top_attach">2</property>
<property name="width">1</property> <property name="width">1</property>
<property name="height">1</property> <property name="height">1</property>
</packing> </packing>
@ -572,7 +567,7 @@ identical CPUs in order to migrate the VM.</property>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="top_attach">2</property> <property name="top_attach">3</property>
<property name="width">1</property> <property name="width">1</property>
<property name="height">1</property> <property name="height">1</property>
</packing> </packing>
@ -589,7 +584,7 @@ identical CPUs in order to migrate the VM.</property>
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
<property name="top_attach">2</property> <property name="top_attach">3</property>
<property name="width">1</property> <property name="width">1</property>
<property name="height">1</property> <property name="height">1</property>
</packing> </packing>
@ -604,6 +599,37 @@ identical CPUs in order to migrate the VM.</property>
<property name="hexpand">False</property> <property name="hexpand">False</property>
<signal name="clicked" handler="on_prefs_btn_keys_define_clicked" swapped="no"/> <signal name="clicked" handler="on_prefs_btn_keys_define_clicked" swapped="no"/>
</object> </object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label26">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Change guest resolution when the guest window size is changed. Only works with properly configured guest using spice and the desktop agent.</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Resize guest with window:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">prefs-console-resizeguest</property>
<property name="lines">2</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="prefs-console-resizeguest">
<property name="visible">True</property>
<property name="can_focus">False</property>
<signal name="changed" handler="on_prefs_console_resizeguest_changed" swapped="no"/>
</object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="top_attach">1</property> <property name="top_attach">1</property>
@ -946,4 +972,9 @@ identical CPUs in order to migrate the VM.</property>
</object> </object>
</child> </child>
</object> </object>
<object class="GtkAdjustment" id="adjustment2">
<property name="upper">300</property>
<property name="step_increment">5</property>
<property name="page_increment">5</property>
</object>
</interface> </interface>

View File

@ -135,10 +135,6 @@ class vmmConfig(object):
CONSOLE_SCALE_FULLSCREEN = 1 CONSOLE_SCALE_FULLSCREEN = 1
CONSOLE_SCALE_ALWAYS = 2 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_IMAGE_DIR = "/var/lib/xen/images"
DEFAULT_XEN_SAVE_DIR = "/var/lib/xen/dump" DEFAULT_XEN_SAVE_DIR = "/var/lib/xen/dump"
@ -166,8 +162,10 @@ class vmmConfig(object):
self.libvirt_packages = cliconfig.libvirt_packages self.libvirt_packages = cliconfig.libvirt_packages
self.askpass_package = cliconfig.askpass_package self.askpass_package = cliconfig.askpass_package
self.default_graphics_from_config = cliconfig.default_graphics self.default_graphics_from_config = cliconfig.default_graphics
self.default_storage_format_from_config = "qcow2" self.default_storage_format_from_config = "qcow2"
self.cpu_default_from_config = "host-cpu-model" self.cpu_default_from_config = "host-cpu-model"
self.default_console_resizeguest = 0
self._objects = [] self._objects = []
@ -381,6 +379,16 @@ class vmmConfig(object):
def set_console_scaling(self, pref): def set_console_scaling(self, pref):
self.conf.set("/console/scaling", 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): def get_auto_redirection(self):
return self.conf.get("/console/auto-redirect") return self.conf.get("/console/auto-redirect")
def set_auto_redirection(self, state): def set_auto_redirection(self, state):

View File

@ -371,9 +371,15 @@ class Viewer(vmmGObject):
def has_usb_redirection(self): def has_usb_redirection(self):
return False return False
def has_agent(self):
return False
def set_resizeguest(self, val):
ignore = val
class VNCViewer(Viewer): class VNCViewer(Viewer):
viewer_type = "vnc"
def __init__(self, console): def __init__(self, console):
Viewer.__init__(self, console) Viewer.__init__(self, console)
self.display = GtkVnc.Display.new() self.display = GtkVnc.Display.new()
@ -393,6 +399,7 @@ class VNCViewer(Viewer):
self.display.set_force_size(False) self.display.set_force_size(False)
self.console.sync_scaling_with_display() self.console.sync_scaling_with_display()
self.console.refresh_resizeguest_from_settings()
self.display.set_keyboard_grab(True) self.display.set_keyboard_grab(True)
self.display.set_pointer_grab(True) self.display.set_pointer_grab(True)
@ -549,17 +556,21 @@ class VNCViewer(Viewer):
class SpiceViewer(Viewer): class SpiceViewer(Viewer):
viewer_type = "spice"
def __init__(self, console): def __init__(self, console):
Viewer.__init__(self, console) Viewer.__init__(self, console)
self.spice_session = None self.spice_session = None
self.display = None self.display = None
self.audio = None self.audio = None
self.main_channel = None
self.display_channel = None self.display_channel = None
self.usbdev_manager = None self.usbdev_manager = None
def _init_widget(self): def _init_widget(self):
self.set_grab_keys() self.set_grab_keys()
self.console.sync_scaling_with_display() self.console.sync_scaling_with_display()
self.console.refresh_resizeguest_from_settings()
self.display.realize() self.display.realize()
@ -613,6 +624,7 @@ class SpiceViewer(Viewer):
self.display.destroy() self.display.destroy()
self.display = None self.display = None
self.display_channel = None self.display_channel = None
self.main_channel = None
self.usbdev_manager = None self.usbdev_manager = None
def is_open(self): def is_open(self):
@ -653,13 +665,17 @@ class SpiceViewer(Viewer):
GObject.GObject.connect(channel, "open-fd", GObject.GObject.connect(channel, "open-fd",
self._channel_open_fd_request) self._channel_open_fd_request)
if type(channel) == SpiceClientGLib.MainChannel: if (type(channel) == SpiceClientGLib.MainChannel and
not self.main_channel):
if self.console.tunnels: if self.console.tunnels:
self.console.tunnels.unlock() self.console.tunnels.unlock()
channel.connect_after("channel-event", self._main_channel_event_cb) self.main_channel = channel
return 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): not self.display):
channel_id = channel.get_property("channel-id") channel_id = channel.get_property("channel-id")
@ -673,13 +689,11 @@ class SpiceViewer(Viewer):
self.console.widget("console-gfx-viewport").add(self.display) self.console.widget("console-gfx-viewport").add(self.display)
self._init_widget() self._init_widget()
self.console.connected() self.console.connected()
return
if (type(channel) in [SpiceClientGLib.PlaybackChannel, elif (type(channel) in [SpiceClientGLib.PlaybackChannel,
SpiceClientGLib.RecordChannel] and SpiceClientGLib.RecordChannel] and
not self.audio): not self.audio):
self.audio = SpiceClientGLib.Audio.get(self.spice_session, None) self.audio = SpiceClientGLib.Audio.get(self.spice_session, None)
return
def get_desktop_resolution(self): def get_desktop_resolution(self):
if (not self.display_channel or if (not self.display_channel or
@ -687,6 +701,16 @@ class SpiceViewer(Viewer):
return None return None
return self.display_channel.get_properties("width", "height") 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): def _create_spice_session(self):
self.spice_session = SpiceClientGLib.Session() self.spice_session = SpiceClientGLib.Session()
SpiceClientGLib.set_session_option(self.spice_session) SpiceClientGLib.set_session_option(self.spice_session)
@ -742,6 +766,10 @@ class SpiceViewer(Viewer):
return return
self.display.set_property("scaling", scaling) 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, def _usbdev_redirect_error(self,
spice_usbdev_widget, spice_usb_device, spice_usbdev_widget, spice_usb_device,
errstr): errstr):
@ -829,6 +857,10 @@ class vmmConsolePages(vmmGObjectUI):
self.add_gconf_handle( self.add_gconf_handle(
self.vm.on_console_scaling_changed( self.vm.on_console_scaling_changed(
self.refresh_scaling_from_settings)) 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 = self.widget("console-gfx-scroll")
scroll.connect("size-allocate", self.scroll_size_allocate) scroll.connect("size-allocate", self.scroll_size_allocate)
@ -1032,6 +1064,42 @@ class vmmConsolePages(vmmGObjectUI):
# Make sure modifiers are up to date # Make sure modifiers are up to date
self.viewer_focus_changed() 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): def refresh_scaling_from_settings(self):
scale_type = self.vm.get_console_scaling() scale_type = self.vm.get_console_scaling()
self.widget("details-menu-view-scale-always").set_active( self.widget("details-menu-view-scale-always").set_active(
@ -1287,6 +1355,7 @@ class vmmConsolePages(vmmGObjectUI):
error += "\n\nError: %s" % errout error += "\n\nError: %s" % errout
self.activate_unavailable_page(error) self.activate_unavailable_page(error)
self.refresh_resizeguest_from_settings()
def _set_viewer_connected(self, val): def _set_viewer_connected(self, val):
self._viewer_connected = val self._viewer_connected = val

View File

@ -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_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_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_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_pages_switch_page": self.console.page_changed,
"on_console_auth_password_activate": self.console.auth_login, "on_console_auth_password_activate": self.console.auth_login,

View File

@ -1627,6 +1627,17 @@ class vmmDomain(vmmLibvirtObject):
return self.config.get_console_scaling() return self.config.get_console_scaling()
return ret 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): def set_details_window_size(self, w, h):
self.config.set_pervm(self.uuid, "/vm-window-size", (w, h)) self.config.set_pervm(self.uuid, "/vm-window-size", (w, h))
def get_details_window_size(self): def get_details_window_size(self):

View File

@ -39,6 +39,7 @@ class vmmPreferences(vmmGObjectUI):
self.refresh_update_interval() self.refresh_update_interval()
self.refresh_console_accels() self.refresh_console_accels()
self.refresh_console_scaling() self.refresh_console_scaling()
self.refresh_console_resizeguest()
self.refresh_new_vm_sound() self.refresh_new_vm_sound()
self.refresh_graphics_type() self.refresh_graphics_type()
self.refresh_storage_format() self.refresh_storage_format()
@ -60,6 +61,7 @@ class vmmPreferences(vmmGObjectUI):
"on_prefs_stats_update_interval_changed": self.change_update_interval, "on_prefs_stats_update_interval_changed": self.change_update_interval,
"on_prefs_console_accels_toggled": self.change_console_accels, "on_prefs_console_accels_toggled": self.change_console_accels,
"on_prefs_console_scaling_changed": self.change_console_scaling, "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_prefs_close_clicked": self.close,
"on_vmm_preferences_delete_event": self.close, "on_vmm_preferences_delete_event": self.close,
"on_prefs_new_vm_sound_toggled": self.change_new_vm_sound, "on_prefs_new_vm_sound_toggled": self.change_new_vm_sound,
@ -104,6 +106,20 @@ class vmmPreferences(vmmGObjectUI):
combo.set_model(model) combo.set_model(model)
uiutil.set_combo_text_column(combo, 1) 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") combo = self.widget("prefs-graphics-type")
# [gsettings value, string] # [gsettings value, string]
model = Gtk.ListStore(str, str) model = Gtk.ListStore(str, str)
@ -157,6 +173,10 @@ class vmmPreferences(vmmGObjectUI):
combo = self.widget("prefs-console-scaling") combo = self.widget("prefs-console-scaling")
val = self.config.get_console_scaling() val = self.config.get_console_scaling()
uiutil.set_row_selection(combo, val) 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): def refresh_new_vm_sound(self):
self.widget("prefs-new-vm-sound").set_active( self.widget("prefs-new-vm-sound").set_active(
@ -294,6 +314,9 @@ class vmmPreferences(vmmGObjectUI):
self.config.set_console_accels(src.get_active()) self.config.set_console_accels(src.get_active())
def change_console_scaling(self, box): def change_console_scaling(self, box):
self.config.set_console_scaling(box.get_active()) 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): def change_new_vm_sound(self, src):
self.config.set_new_vm_sound(src.get_active()) self.config.set_new_vm_sound(src.get_active())

View File

@ -103,7 +103,7 @@ def set_row_selection(listwidget, prevkey):
""" """
model = listwidget.get_model() model = listwidget.get_model()
_iter = None _iter = None
if prevkey: if prevkey is not None:
for row in model: for row in model:
if row[0] == prevkey: if row[0] == prevkey:
_iter = row.iter _iter = row.iter