vmwindow: Cleanup the interaction with vmmConsolePages

- Add ui/console.ui for console-pages and below
- Add move auth and graphics unavailable pages to a new subnotebook
- Move all the menubar handling up into vmwindow
- Clarify the control flow as much as I can come up with

Signed-off-by: Cole Robinson <crobinso@redhat.com>
This commit is contained in:
Cole Robinson 2020-09-09 15:14:05 -04:00
parent 48bf5c6ab8
commit 43512302f7
6 changed files with 623 additions and 545 deletions

312
ui/console.ui Normal file
View File

@ -0,0 +1,312 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.36.0 -->
<interface>
<requires lib="gtk+" version="3.22"/>
<object class="GtkNotebook" id="console-pages">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="show_border">False</property>
<signal name="switch-page" handler="on_console_pages_switch_page" after="yes" swapped="no"/>
<child>
<object class="GtkLabel" id="console-unavailable">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="label" translatable="yes">&lt;b&gt;The console is currently unavailable&lt;/b&gt;</property>
<property name="use_markup">True</property>
<property name="justify">center</property>
<property name="wrap">True</property>
<property name="selectable">True</property>
<property name="width_chars">60</property>
<property name="max_width_chars">60</property>
</object>
<packing>
<property name="tab_expand">True</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="label434">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label">Unavailable</property>
</object>
<packing>
<property name="tab_fill">False</property>
</packing>
</child>
<child>
<object class="GtkNotebook" id="serial-pages">
<property name="visible">True</property>
<property name="can_focus">True</property>
<child internal-child="accessible">
<object class="AtkObject" id="serial-pages-atkobject">
<property name="AtkObject::accessible-name">serial-pages</property>
</object>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="label82">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Serial</property>
</object>
<packing>
<property name="position">1</property>
<property name="tab_fill">False</property>
</packing>
</child>
<child>
<object class="GtkNotebook" id="console-gfx-pages">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="show_border">False</property>
<child>
<object class="GtkOverlay" id="console-overlay">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkScrolledWindow" id="console-gfx-scroll">
<property name="visible">True</property>
<property name="can_focus">True</property>
<child>
<object class="GtkViewport" id="console-gfx-viewport">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="resize_mode">queue</property>
<property name="shadow_type">none</property>
<child>
<placeholder/>
</child>
<child internal-child="accessible">
<object class="AtkObject" id="console-gfx-viewport-atkobject">
<property name="AtkObject::accessible-name">console-gfx-viewport</property>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="index">-1</property>
</packing>
</child>
</object>
</child>
<child type="tab">
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label">Display</property>
</object>
<packing>
<property name="tab_fill">False</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="console-auth">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="hexpand">True</property>
<property name="border_width">6</property>
<property name="row_spacing">6</property>
<property name="column_spacing">6</property>
<child>
<object class="GtkLabel" id="label-auth-password">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">_Password:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">console-auth-password</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label-auth-username">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">_Username:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">console-auth-username</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="console-auth-username">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<signal name="activate" handler="on_console_auth_password_activate" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="console-auth-password">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="visibility">False</property>
<signal name="activate" handler="on_console_auth_password_activate" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="console-auth-login">
<property name="label" translatable="yes">_Login</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="image">image70</property>
<property name="use_underline">True</property>
<signal name="clicked" handler="on_console_auth_login_clicked" swapped="no"/>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="console-auth-remember">
<property name="label" translatable="yes">_Save this password in your keyring</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Check to save password, uncheck to forget password.</property>
<property name="halign">start</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkAlignment">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkAlignment">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkAlignment">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label">Auth</property>
</object>
<packing>
<property name="position">1</property>
<property name="tab_fill">False</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="console-gfx-unavailable">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="label" translatable="yes">&lt;b&gt;The console is currently unavailable&lt;/b&gt;</property>
<property name="use_markup">True</property>
<property name="justify">center</property>
<property name="wrap">True</property>
<property name="selectable">True</property>
<property name="width_chars">60</property>
<property name="max_width_chars">60</property>
</object>
<packing>
<property name="position">2</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label">Unavailable</property>
</object>
<packing>
<property name="position">2</property>
<property name="tab_fill">False</property>
</packing>
</child>
</object>
<packing>
<property name="position">2</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="label439">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label">Graphics</property>
</object>
<packing>
<property name="position">2</property>
<property name="tab_fill">False</property>
</packing>
</child>
<child internal-child="accessible">
<object class="AtkObject" id="console-pages-atkobject">
<property name="AtkObject::accessible-name">console-pages</property>
</object>
</child>
</object>
<object class="GtkImage" id="image70">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-apply</property>
</object>
</interface>

View File

@ -3,11 +3,6 @@
<interface>
<requires lib="gtk+" version="3.22"/>
<object class="GtkAccelGroup" id="accelgroup1"/>
<object class="GtkImage" id="image70">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-apply</property>
</object>
<object class="GtkWindow" id="vmm-vmwindow">
<property name="can_focus">False</property>
<property name="title" translatable="yes">Virtual Machine</property>
@ -498,283 +493,13 @@
</packing>
</child>
<child>
<object class="GtkNotebook" id="console-pages">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="show_border">False</property>
<signal name="switch-page" handler="on_console_pages_switch_page" after="yes" swapped="no"/>
<child>
<object class="GtkLabel" id="console-unavailable">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="label" translatable="yes">&lt;b&gt;The console is currently unavailable&lt;/b&gt;</property>
<property name="use_markup">True</property>
<property name="justify">center</property>
<property name="wrap">True</property>
<property name="selectable">True</property>
<property name="width_chars">60</property>
<property name="max_width_chars">60</property>
</object>
<packing>
<property name="tab_expand">True</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="label434">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label">Unavailable</property>
</object>
<packing>
<property name="tab_fill">False</property>
</packing>
</child>
<child>
<object class="GtkTable" id="console-auth">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">3</property>
<property name="n_rows">3</property>
<property name="n_columns">3</property>
<property name="column_spacing">3</property>
<property name="row_spacing">3</property>
<child>
<object class="GtkLabel" id="label-auth-password">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">_Password:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">console-auth-password</property>
</object>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkEntry" id="console-auth-password">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="visibility">False</property>
<signal name="activate" handler="on_console_auth_password_activate" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="console-auth-remember">
<property name="label" translatable="yes">_Save this password in your keyring</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Check to save password, uncheck to forget password.</property>
<property name="halign">start</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label-auth-username">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">_Username:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">console-auth-username</property>
</object>
<packing>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkEntry" id="console-auth-username">
<property name="visible">True</property>
<property name="can_focus">True</property>
<signal name="activate" handler="on_console_auth_password_activate" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkButton" id="console-auth-login">
<property name="label" translatable="yes">_Login</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="image">image70</property>
<property name="use_underline">True</property>
<signal name="clicked" handler="on_console_auth_login_clicked" swapped="no"/>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment17">
<object class="GtkAlignment" id="console-placeholder">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment18">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment19">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="label438">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label">Auth</property>
</object>
<packing>
<property name="position">1</property>
<property name="tab_fill">False</property>
</packing>
</child>
<child>
<object class="GtkNotebook" id="serial-pages">
<property name="visible">True</property>
<property name="can_focus">True</property>
<child internal-child="accessible">
<object class="AtkObject" id="serial-pages-atkobject">
<property name="AtkObject::accessible-name">serial-pages</property>
</object>
</child>
</object>
<packing>
<property name="position">2</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="label82">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Serial</property>
</object>
<packing>
<property name="position">2</property>
<property name="tab_fill">False</property>
</packing>
</child>
<child>
<object class="GtkOverlay" id="console-overlay">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkScrolledWindow" id="console-gfx-scroll">
<property name="visible">True</property>
<property name="can_focus">True</property>
<child>
<object class="GtkViewport" id="console-gfx-viewport">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="resize_mode">queue</property>
<property name="shadow_type">none</property>
<child>
<placeholder/>
</child>
<child internal-child="accessible">
<object class="AtkObject" id="console-gfx-viewport-atkobject">
<property name="AtkObject::accessible-name">console-gfx-viewport</property>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="index">-1</property>
</packing>
</child>
</object>
<packing>
<property name="position">3</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="label439">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label">Graphics</property>
</object>
<packing>
<property name="position">3</property>
<property name="tab_fill">False</property>
</packing>
</child>
<child internal-child="accessible">
<object class="AtkObject" id="console-pages-atkobject">
<property name="AtkObject::accessible-name">console-pages</property>
</object>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>

View File

@ -16,6 +16,7 @@ from gi.repository import GObject
from gi.repository import Gtk
from virtinst import log
from virtinst import xmlutil
from . import config
@ -305,7 +306,10 @@ class vmmGObjectUI(vmmGObject):
err = property(_get_err)
def widget(self, name):
return self.builder.get_object(name)
ret = self.builder.get_object(name)
if not ret:
raise xmlutil.DevError("Did not find widget name=%s" % name)
return ret
def cleanup(self):
if self.__cleaned_up:

View File

@ -15,14 +15,17 @@ from .sshtunnels import ConnectionInfo
from .viewers import SpiceViewer, VNCViewer, have_spice_gtk
from ..baseclass import vmmGObject, vmmGObjectUI
from ..lib.keyring import vmmKeyring
from ..vmwindow import DETAILS_PAGE_CONSOLE
# console-pages IDs
(_CONSOLE_PAGE_UNAVAILABLE,
_CONSOLE_PAGE_AUTHENTICATE,
_CONSOLE_PAGE_SERIAL,
_CONSOLE_PAGE_VIEWER) = range(4)
_CONSOLE_PAGE_GRAPHICS) = range(3)
# console-gfx-pages IDs
(_GFX_PAGE_VIEWER,
_GFX_PAGE_AUTH,
_GFX_PAGE_UNAVAILABLE) = range(3)
class _TimedRevealer(vmmGObject):
@ -89,7 +92,7 @@ class _TimedRevealer(vmmGObject):
self._timeout_id = self.timeout_add(timeout, cb)
def _unregister_timeout(self):
if self._timeout_id:
if self._timeout_id: # pragma: no cover
self.remove_gobject_timeout(self._timeout_id)
self._timeout_id = None
@ -181,9 +184,6 @@ class vmmOverlayToolbar:
self.timed_revealer.cleanup()
self.timed_revealer = None
def set_sensitive(self, can_sendkey):
self._send_key_button.set_sensitive(can_sendkey)
class _ConsoleMenu:
"""
@ -287,17 +287,25 @@ class _ConsoleMenu:
for child in menu.get_children():
if child.get_sensitive() and hasattr(child, "toggled"):
child.toggled()
break
return True
return False
class vmmConsolePages(vmmGObjectUI):
"""
Handles all the complex UI handling dictated by the spice/vnc widgets
"""
__gsignals__ = {
"page-changed": (vmmGObjectUI.RUN_FIRST, None, []),
"leave-fullscreen": (vmmGObjectUI.RUN_FIRST, None, []),
}
def __init__(self, vm, builder, topwin):
vmmGObjectUI.__init__(self, None, None, builder=builder, topwin=topwin)
vmmGObjectUI.__init__(self, "console.ui",
None, builder=builder, topwin=topwin)
self.vm = vm
self.top_box = self.widget("console-pages")
self._pointer_is_grabbed = False
self._change_title()
self.vm.connect("state-changed", self._change_title)
@ -309,9 +317,13 @@ class vmmConsolePages(vmmGObjectUI):
# Initialize display widget
self._viewer = None
self._in_fullscreen = False
# Fullscreen toolbar
self._keycombo_menu = build_keycombo_menu(self._do_send_key)
self._console_list_menu = Gtk.Menu()
self._console_list_menu.connect("show",
self._populate_console_list_menu)
self._overlay_toolbar_fullscreen = vmmOverlayToolbar(
on_leave_fn=self._leave_fullscreen,
@ -326,34 +338,31 @@ class vmmConsolePages(vmmGObjectUI):
self.widget("console-pages").set_show_tabs(False)
self.widget("serial-pages").set_show_tabs(False)
self.widget("console-gfx-pages").set_show_tabs(False)
self._consolemenu = _ConsoleMenu()
self._serial_consoles = []
self._init_menus()
# Signals are added by vmmVMWindow. Don't use connect_signals here
# or it changes will be overwritten
self.builder.connect_signals({
"on_console_pages_switch_page": self._page_changed_cb,
"on_console_auth_password_activate": self._auth_login_cb,
"on_console_auth_login_clicked": self._auth_login_cb,
})
self.widget("console-gfx-scroll").connect("size-allocate",
self._scroll_size_allocate)
self._refresh_widget_states()
self._refresh_scaling_from_settings()
self.add_gsettings_handle(
self.vm.on_console_scaling_changed(
self._refresh_scaling_from_settings))
self._refresh_resizeguest_from_settings()
self.add_gsettings_handle(
self.vm.on_console_resizeguest_changed(
self._refresh_resizeguest_from_settings))
self.widget("console-gfx-pages").connect("switch-page",
self._page_changed_cb)
def _cleanup(self):
self.vm = None
if self._viewer:
self._viewer.cleanup()
self._viewer.cleanup() # pragma: no cover
self._viewer = None
self._overlay_toolbar_fullscreen.cleanup()
@ -368,15 +377,6 @@ class vmmConsolePages(vmmGObjectUI):
##########################
def _init_menus(self):
# Serial list menu
smenu = Gtk.Menu()
smenu.connect("show", self._populate_serial_menu)
self.widget("details-menu-view-console-list").set_submenu(smenu)
# Keycombo menu (ctrl+alt+del etc.)
self.widget("details-menu-send-key").set_submenu(self._keycombo_menu)
#################
# Internal APIs #
@ -398,7 +398,7 @@ class vmmConsolePages(vmmGObjectUI):
def _disable_modifiers(self):
if self._gtk_settings_accel is not None:
return
return # pragma: no cover
for g in self._accel_groups:
self.topwin.remove_accel_group(g)
@ -502,7 +502,7 @@ class vmmConsolePages(vmmGObjectUI):
viewer_alloc.height = desktop_h
self._viewer.console_size_allocate(viewer_alloc)
def _refresh_resizeguest_from_settings(self):
def _viewer_get_resizeguest_tooltip(self):
tooltip = ""
if self._viewer:
if self._viewer.viewer_type != "spice":
@ -511,15 +511,7 @@ class vmmConsolePages(vmmGObjectUI):
self._viewer.viewer_type)
elif not self._viewer.console_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()
return tooltip
def _sync_resizeguest_with_display(self):
if not self._viewer:
@ -529,15 +521,7 @@ class vmmConsolePages(vmmGObjectUI):
self._viewer.console_set_resizeguest(val)
self.widget("console-gfx-scroll").queue_resize()
def _resizeguest_ui_changed_cb(self, src):
if not src.get_sensitive():
return # pragma: no cover
val = int(self.widget("details-menu-view-resizeguest").get_active())
self.vm.set_console_resizeguest(val)
self._sync_resizeguest_with_display()
def _do_size_to_vm(self, src_ignore):
def _set_size_to_vm(self):
# Resize the console to best fit the VM resolution
if not self._viewer:
return # pragma: no cover
@ -558,39 +542,12 @@ class vmmConsolePages(vmmGObjectUI):
# Scaling APIs #
################
def _refresh_scaling_from_settings(self):
scale_type = self.vm.get_console_scaling()
self.widget("details-menu-view-scale-always").set_active(
scale_type == self.config.CONSOLE_SCALE_ALWAYS)
self.widget("details-menu-view-scale-never").set_active(
scale_type == self.config.CONSOLE_SCALE_NEVER)
self.widget("details-menu-view-scale-fullscreen").set_active(
scale_type == self.config.CONSOLE_SCALE_FULLSCREEN)
self._sync_scaling_with_display()
def _scaling_ui_changed_cb(self, src):
# Called from details.py
if not src.get_active():
return
scale_type = 0
if src == self.widget("details-menu-view-scale-always"):
scale_type = self.config.CONSOLE_SCALE_ALWAYS
elif src == self.widget("details-menu-view-scale-fullscreen"):
scale_type = self.config.CONSOLE_SCALE_FULLSCREEN
elif src == self.widget("details-menu-view-scale-never"):
scale_type = self.config.CONSOLE_SCALE_NEVER
self.vm.set_console_scaling(scale_type)
self._sync_scaling_with_display()
def _sync_scaling_with_display(self):
if not self._viewer:
return
fs = self._in_fullscreen
curscale = self._viewer.console_get_scaling()
fs = self.widget("control-fullscreen").get_active()
scale_type = self.vm.get_console_scaling()
if (scale_type == self.config.CONSOLE_SCALE_NEVER and
@ -611,37 +568,19 @@ class vmmConsolePages(vmmGObjectUI):
# Fullscreen APIs #
###################
def _refresh_can_fullscreen(self):
cpage = self.widget("console-pages").get_current_page()
dpage = self.widget("details-pages").get_current_page()
allow_fullscreen = bool(dpage == DETAILS_PAGE_CONSOLE and
cpage == _CONSOLE_PAGE_VIEWER and
self._viewer and self._viewer.console_is_open())
self.widget("control-fullscreen").set_sensitive(allow_fullscreen)
self.widget("details-menu-view-fullscreen").set_sensitive(
allow_fullscreen)
def _leave_fullscreen(self, ignore=None):
self._change_fullscreen(False)
self.emit("leave-fullscreen")
def _change_fullscreen(self, do_fullscreen):
self.widget("control-fullscreen").set_active(do_fullscreen)
if do_fullscreen:
self._in_fullscreen = True
self.topwin.fullscreen()
self._overlay_toolbar_fullscreen.timed_revealer.force_reveal(True)
self.widget("toolbar-box").hide()
self.widget("details-menubar").hide()
else:
self._in_fullscreen = False
self._overlay_toolbar_fullscreen.timed_revealer.force_reveal(False)
self.topwin.unfullscreen()
if self.widget("details-menu-view-toolbar").get_active():
self.widget("toolbar-box").show()
self.widget("details-menubar").show()
self._sync_scaling_with_display()
@ -651,9 +590,9 @@ class vmmConsolePages(vmmGObjectUI):
def _show_vm_status_unavailable(self):
if self.vm.is_crashed(): # pragma: no cover
self._activate_unavailable_page(_("Guest has crashed."))
self._activate_vm_unavailable_page(_("Guest has crashed."))
else:
self._activate_unavailable_page(_("Guest is not running."))
self._activate_vm_unavailable_page(_("Guest is not running."))
def _close_viewer(self):
self._leave_fullscreen()
@ -668,37 +607,46 @@ class vmmConsolePages(vmmGObjectUI):
self._viewer.cleanup()
self._viewer = None
def _update_vm_widget_states(self):
page = self.widget("console-pages").get_current_page()
def _refresh_vm_state(self):
cpage = self.widget("console-pages").get_current_page()
if self.vm.is_runable():
self._show_vm_status_unavailable()
return
elif (page == _CONSOLE_PAGE_UNAVAILABLE or
page == _CONSOLE_PAGE_VIEWER):
if self._viewer and self._viewer.console_is_open():
self._activate_viewer_page()
else:
self._init_viewer()
# Update other state
self._refresh_widget_states()
viewer_initialized = (self._viewer and self._viewer.console_is_open())
if cpage == _CONSOLE_PAGE_UNAVAILABLE and not viewer_initialized:
# If we are in this condition it should mean the VM was
# just started, so connect to the default page
self._activate_default_console_page()
###################
# Page Navigation #
###################
###########################
# console page navigation #
###########################
def _activate_unavailable_page(self, msg):
def _activate_gfx_unavailable_page(self, msg):
self._close_viewer()
self.widget("console-gfx-pages").set_current_page(
_GFX_PAGE_UNAVAILABLE)
if msg:
self.widget("console-gfx-unavailable").set_label(
"<b>" + msg + "</b>")
def _activate_vm_unavailable_page(self, msg):
"""
This function is passed to serialcon.py at least, so change
with care
This is the top level error page. We should only set it for very
specific error cases, because when it is set and the VM is running
we take that to mean we should attempt to connect to the default
console.
"""
self._close_viewer()
self.widget("console-pages").set_current_page(
_CONSOLE_PAGE_UNAVAILABLE)
if msg:
self.widget("console-unavailable").set_label("<b>" + msg + "</b>")
self.widget("console-unavailable").set_label(
"<b>" + msg + "</b>")
self._activate_gfx_unavailable_page(msg)
def _activate_auth_page(self, withPassword, withUsername):
(pw, username) = vmmKeyring.get_instance().get_console_password(self.vm)
@ -718,55 +666,34 @@ class vmmConsolePages(vmmGObjectUI):
self.widget("console-auth-remember").set_sensitive(has_keyring)
self.widget("console-auth-remember").set_active(remember)
self.widget("console-pages").set_current_page(
_CONSOLE_PAGE_AUTHENTICATE)
self.widget("console-gfx-pages").set_current_page(_GFX_PAGE_AUTH)
if withUsername:
self.widget("console-auth-username").grab_focus()
else:
self.widget("console-auth-password").grab_focus()
def _activate_viewer_page(self):
self.widget("console-pages").set_current_page(_CONSOLE_PAGE_VIEWER)
def _activate_gfx_viewer_page(self):
self.widget("console-pages").set_current_page(_CONSOLE_PAGE_GRAPHICS)
self.widget("console-gfx-pages").set_current_page(_GFX_PAGE_VIEWER)
if self._viewer:
self._viewer.console_grab_focus()
def _page_changed(self, src, origpage, newpage):
ignore = src
ignore = origpage
def _viewer_is_visible(self):
is_visible = self.widget("console-pages").is_visible()
cpage = self.widget("console-pages").get_current_page()
gpage = self.widget("console-gfx-pages").get_current_page()
# Hide the contents of all other pages, so they don't screw
# up window sizing
for i in range(self.widget("console-pages").get_n_pages()):
self.widget("console-pages").get_nth_page(i).set_visible(
i == newpage)
# Dispatch the next bit in idle_add, so the UI size can change
self.idle_add(self._refresh_widget_states)
def _refresh_widget_states(self):
if not self.vm:
# This is triggered via cleanup + idle_add, so vm might
# disappear and spam the logs
return # pragma: no cover
pagenum = self.widget("console-pages").get_current_page()
paused = self.vm.is_paused()
is_viewer = bool(pagenum == _CONSOLE_PAGE_VIEWER and
return bool(
is_visible and
cpage == _CONSOLE_PAGE_GRAPHICS and
gpage == _GFX_PAGE_VIEWER and
self._viewer and self._viewer.console_is_open())
self.widget("details-menu-vm-screenshot").set_sensitive(is_viewer)
self.widget("details-menu-usb-redirection").set_sensitive(
bool(is_viewer and self._viewer and
def _viewer_can_usb_redirect(self):
return (self._viewer_is_visible() and
self._viewer.console_has_usb_redirection() and
self.vm.has_spicevmc_type_redirdev()))
can_sendkey = (is_viewer and not paused)
for c in self._keycombo_menu.get_children():
c.set_sensitive(can_sendkey)
self._overlay_toolbar_fullscreen.set_sensitive(can_sendkey)
self._refresh_can_fullscreen()
self.vm.has_spicevmc_type_redirdev())
#########################
@ -792,7 +719,7 @@ class vmmConsolePages(vmmGObjectUI):
if ginfo is None:
log.debug("No graphics configured for guest")
self._activate_unavailable_page(
self._activate_gfx_unavailable_page(
_("Graphical console not configured for guest"))
return
@ -803,10 +730,10 @@ class vmmConsolePages(vmmGObjectUI):
msg = (_("Cannot display graphical console type '%s'")
% ginfo.gtype)
self._activate_unavailable_page(msg)
self._activate_gfx_unavailable_page(msg)
return
self._activate_unavailable_page(
self._activate_gfx_unavailable_page(
_("Connecting to graphical console for guest"))
log.debug("Starting connect process for %s", ginfo.logstring())
@ -825,7 +752,7 @@ class vmmConsolePages(vmmGObjectUI):
self._viewer.console_open()
except Exception as e:
log.exception("Error connection to graphical console")
self._activate_unavailable_page(
self._activate_gfx_unavailable_page(
_("Error connecting to graphical console:\n%s") % e)
def _set_credentials(self, src_ignore=None):
@ -853,7 +780,7 @@ class vmmConsolePages(vmmGObjectUI):
# Sync initial settings
self._sync_scaling_with_display()
self._refresh_resizeguest_from_settings()
self._sync_resizeguest_with_display()
def _pointer_grabbed(self, ignore):
self._pointer_is_grabbed = True
@ -889,16 +816,19 @@ class vmmConsolePages(vmmGObjectUI):
if viewer_will_disconnect:
# GtkVNC will disconnect after an auth error, so lets do it for
# them and re-init the viewer (which will be triggered by
# update_vm_widget_states if needed)
self._activate_unavailable_page(errmsg)
# _refresh_vm_state if needed)
self._activate_vm_unavailable_page(errmsg)
self._update_vm_widget_states()
self._refresh_vm_state()
def _viewer_need_auth(self, ignore, withPassword, withUsername):
self._activate_auth_page(withPassword, withUsername)
def _viewer_agent_connected(self, ignore):
self._refresh_resizeguest_from_settings() # pragma: no cover
# Tell the vmwindow to trigger a state refresh, since
# resizeguest setting depends on the agent value
if self.widget("console-pages").is_visible(): # pragma: no cover
self.emit("page-changed")
def _viewer_usb_redirect_error(self, ignore, errstr):
self.err.show_err(
@ -919,21 +849,20 @@ class vmmConsolePages(vmmGObjectUI):
msg += "\n\n"
msg += _("SSH tunnel error output: %s") % ssherr
self._activate_unavailable_page(msg)
self._activate_gfx_unavailable_page(msg)
def _viewer_disconnected(self, ignore, errdetails, ssherr):
self._activate_unavailable_page(_("Viewer disconnected."))
self._activate_gfx_unavailable_page(_("Viewer disconnected."))
log.debug("Viewer disconnected")
# Make sure modifiers are set correctly
self._viewer_sync_modifiers()
self._viewer_disconnected_set_page(errdetails, ssherr)
self._refresh_resizeguest_from_settings()
def _viewer_connected(self, ignore):
log.debug("Viewer connected")
self._activate_viewer_page()
self._activate_gfx_viewer_page()
# Make sure modifiers are set correctly
self._viewer_sync_modifiers()
@ -964,15 +893,18 @@ class vmmConsolePages(vmmGObjectUI):
"""
# We iterate through the 'console' menu and activate the first
# valid entry... hacky but it works
self._populate_serial_menu()
menu = self.widget("details-menu-view-console-list").get_submenu()
self._consolemenu.activate_default(menu)
self._populate_console_list_menu()
found = self._consolemenu.activate_default(self._console_list_menu)
if not found:
# Calling this with dev=None will trigger _init_viewer
# which shows some meaningful errors
self._console_list_menu_toggled(None, None)
def _console_menu_toggled(self, src, dev):
self.widget("details-pages").set_current_page(DETAILS_PAGE_CONSOLE)
if dev and dev.DEVICE_TYPE == "graphics":
self.widget("console-pages").set_current_page(_CONSOLE_PAGE_VIEWER)
def _console_list_menu_toggled(self, src, dev):
if not dev or dev.DEVICE_TYPE == "graphics":
self.widget("console-pages").set_current_page(
_CONSOLE_PAGE_GRAPHICS)
self.idle_add(self._init_viewer)
return
target_port = dev.get_xml_idx()
@ -997,51 +929,67 @@ class vmmConsolePages(vmmGObjectUI):
self.widget("console-pages").set_current_page(_CONSOLE_PAGE_SERIAL)
self.widget("serial-pages").set_current_page(page_idx)
def _populate_serial_menu(self, ignore=None):
submenu = self.widget("details-menu-view-console-list").get_submenu()
def _populate_console_list_menu(self, ignore=None):
self._consolemenu.rebuild_menu(
self.vm, submenu, self._console_menu_toggled)
self.vm, self._console_list_menu,
self._console_list_menu_toggled)
################
# UI listeners #
################
def _auth_login_cb(self, src):
self._set_credentials()
def _page_changed_cb(self, src, origpage, newpage):
# Hide the contents of all other pages, so they don't screw
# up window sizing
for i in range(src.get_n_pages()):
src.get_nth_page(i).set_visible(i == newpage)
# Dispatch the next bit in idle_add, so the UI size can change
self.idle_emit("page-changed")
###########################
# API used by vmmVMWindow #
###########################
def details_viewer_is_visible(self):
return bool(self._viewer and self._viewer.console_get_visible())
def details_viewer_has_usb_redirection(self):
def vmwindow_viewer_has_usb_redirection(self):
return bool(self._viewer and
self._viewer.console_has_usb_redirection())
def details_viewer_get_usb_widget(self):
def vmwindow_viewer_get_usb_widget(self):
return self._viewer.console_get_usb_widget()
def details_viewer_get_pixbuf(self):
def vmwindow_viewer_get_pixbuf(self):
return self._viewer.console_get_pixbuf()
def details_close_viewer(self):
return self._activate_unavailable_page(_("Viewer disconnected."))
def vmwindow_close_viewer(self):
return self._activate_vm_unavailable_page(
_("Viewer disconnected."))
def details_activate_default_console_page(self):
def vmwindow_activate_default_console_page(self):
return self._activate_default_console_page()
def vmwindow_refresh_vm_state(self):
return self._refresh_vm_state()
def details_update_widget_states(self):
return self._update_vm_widget_states()
def details_refresh_can_fullscreen(self):
return self._refresh_can_fullscreen()
def details_resizeguest_ui_changed_cb(self, *args, **kwargs):
return self._resizeguest_ui_changed_cb(*args, **kwargs)
def details_page_changed(self, *args, **kwargs):
return self._page_changed(*args, **kwargs)
def details_scaling_ui_changed_cb(self, *args, **kwargs):
return self._scaling_ui_changed_cb(*args, **kwargs)
def details_size_to_vm(self, *args, **kwargs):
return self._do_size_to_vm(*args, **kwargs)
def details_toggle_fullscreen(self, src):
do_fullscreen = src.get_active()
def vmwindow_set_size_to_vm(self):
return self._set_size_to_vm()
def vmwindow_set_fullscreen(self, do_fullscreen):
self._change_fullscreen(do_fullscreen)
def details_auth_login(self, ignore):
self._set_credentials()
def vmwindow_get_keycombo_menu(self):
return self._keycombo_menu
def vmwindow_get_console_list_menu(self):
return self._console_list_menu
def vmwindow_get_viewer_is_visible(self):
return self._viewer_is_visible()
def vmwindow_get_can_usb_redirect(self):
return self._viewer_can_usb_redirect()
def vmwindow_get_resizeguest_tooltip(self):
return self._viewer_get_resizeguest_tooltip()
def vmwindow_sync_scaling_with_display(self):
return self._sync_scaling_with_display()
def vmwindow_sync_resizeguest_with_display(self):
return self._sync_resizeguest_with_display()

View File

@ -101,14 +101,10 @@ class Viewer(vmmGObject):
def _grab_focus(self):
if self._display:
self._display.grab_focus()
def _has_focus(self):
return self._display and self._display.get_property("has-focus")
def _set_size_request(self, *args, **kwargs):
return self._display.set_size_request(*args, **kwargs)
def _size_allocate(self, *args, **kwargs):
return self._display.size_allocate(*args, **kwargs)
def _get_visible(self):
return self._display and self._display.get_visible()
def _get_pixbuf(self):
return self._display.get_pixbuf()
@ -205,16 +201,12 @@ class Viewer(vmmGObject):
def console_grab_focus(self):
return self._grab_focus()
def console_has_focus(self):
return self._has_focus()
def console_has_keyboard_grab(self):
return bool(self._display and self._keyboard_grab)
def console_set_size_request(self, *args, **kwargs):
return self._set_size_request(*args, **kwargs)
def console_size_allocate(self, *args, **kwargs):
return self._size_allocate(*args, **kwargs)
def console_get_visible(self):
return self._get_visible()
def console_get_pixbuf(self):
return self._get_pixbuf()
@ -236,7 +228,7 @@ class Viewer(vmmGObject):
def console_get_desktop_resolution(self):
ret = self._get_desktop_resolution()
if not ret:
return ret
return ret # pragma: no cover
# Don't pass on bogus resolutions
if (ret[0] == 0) or (ret[1] == 0):
@ -684,7 +676,7 @@ class SpiceViewer(Viewer):
def _get_desktop_resolution(self):
if not self._display_channel:
return None
return None # pragma: no cover
return self._display_channel.get_properties("width", "height")
def _has_agent(self):

View File

@ -12,6 +12,7 @@ from virtinst import log
from . import vmmenu
from .baseclass import vmmGObjectUI
from .engine import vmmEngine
from .details.console import vmmConsolePages
from .details.details import vmmDetails
from .details.snapshots import vmmSnapshotPage
@ -65,14 +66,17 @@ class vmmVMWindow(vmmGObjectUI):
else:
self.conn.connect("vm-removed", self._vm_removed_cb)
self._mediacombo = None
self.ignoreDetails = False
from .details.console import vmmConsolePages
self.console = vmmConsolePages(self.vm, self.builder, self.topwin)
self._console = vmmConsolePages(self.vm, self.builder, self.topwin)
self.widget("console-placeholder").add(self._console.top_box)
self._console.connect("page-changed", self._console_page_changed_cb)
self._console.connect("leave-fullscreen",
self._console_leave_fullscreen_cb)
self.snapshots = vmmSnapshotPage(self.vm, self.builder, self.topwin)
self.widget("snapshot-placeholder").add(self.snapshots.top_box)
self._details = vmmDetails(self.vm, self.builder, self.topwin,
self.is_customize_dialog)
self.widget("details-placeholder").add(self._details.top_box)
@ -119,32 +123,31 @@ class vmmVMWindow(vmmGObjectUI):
"on_details_pages_switch_page": self.switch_page,
# Listeners stored in vmmConsolePages
"on_details_menu_view_fullscreen_activate": (
self.console.details_toggle_fullscreen),
"on_details_menu_view_size_to_vm_activate": (
self.console.details_size_to_vm),
"on_details_menu_view_scale_always_toggled": (
self.console.details_scaling_ui_changed_cb),
"on_details_menu_view_scale_fullscreen_toggled": (
self.console.details_scaling_ui_changed_cb),
"on_details_menu_view_scale_never_toggled": (
self.console.details_scaling_ui_changed_cb),
"on_details_menu_view_resizeguest_toggled": (
self.console.details_resizeguest_ui_changed_cb),
"on_console_pages_switch_page": (
self.console.details_page_changed),
"on_console_auth_password_activate": (
self.console.details_auth_login),
"on_console_auth_login_clicked": (
self.console.details_auth_login),
"on_details_menu_view_fullscreen_activate": self._fullscreen_changed_cb,
"on_details_menu_view_size_to_vm_activate": self._size_to_vm_cb,
"on_details_menu_view_scale_always_toggled": self._scaling_ui_changed_cb,
"on_details_menu_view_scale_fullscreen_toggled": self._scaling_ui_changed_cb,
"on_details_menu_view_scale_never_toggled": self._scaling_ui_changed_cb,
"on_details_menu_view_resizeguest_toggled": self._resizeguest_ui_changed_cb,
})
# Deliberately keep all this after signal connection
self.vm.connect("state-changed", self.refresh_vm_state)
self.vm.connect("resources-sampled", self.refresh_resources)
self._console_page_changed_cb(None)
self._console_refresh_scaling_from_settings()
self.add_gsettings_handle(
self.vm.on_console_scaling_changed(
self._console_refresh_scaling_from_settings))
self._console_refresh_resizeguest_from_settings()
self.add_gsettings_handle(
self.vm.on_console_resizeguest_changed(
self._console_refresh_resizeguest_from_settings))
self.refresh_vm_state()
self.activate_default_page()
@ -154,8 +157,8 @@ class vmmVMWindow(vmmGObjectUI):
return self.vm.conn
def _cleanup(self):
self.console.cleanup()
self.console = None
self._console.cleanup()
self._console = None
self.snapshots.cleanup()
self.snapshots = None
self._details.cleanup()
@ -224,9 +227,8 @@ class vmmVMWindow(vmmGObjectUI):
return
self.topwin.hide()
if self.console.details_viewer_is_visible():
try:
self.console.details_close_viewer()
self._console.vmwindow_close_viewer()
except Exception: # pragma: no cover
log.error("Failure when disconnecting from desktop server")
@ -259,6 +261,14 @@ class vmmVMWindow(vmmGObjectUI):
self.widget("details-menu-view-toolbar").set_active(
self.config.get_details_show_toolbar())
# Keycombo menu (ctrl+alt+del etc.)
self.widget("details-menu-send-key").set_submenu(
self._console.vmwindow_get_keycombo_menu())
# Serial list menu
self.widget("details-menu-view-console-list").set_submenu(
self._console.vmwindow_get_console_list_menu())
##########################
# Window state listeners #
@ -342,7 +352,7 @@ class vmmVMWindow(vmmGObjectUI):
self.page_refresh(newpage)
self.sync_details_console_view(newpage)
self.console.details_refresh_can_fullscreen()
self._console_page_changed_cb(None)
def change_run_text(self, can_restore):
if can_restore:
@ -395,9 +405,7 @@ class vmmVMWindow(vmmGObjectUI):
self.page_refresh(details.get_current_page())
self._details.vmwindow_refresh_vm_state()
self.console.details_update_widget_states()
if not run:
self.activate_default_console_page()
self._console.vmwindow_refresh_vm_state()
#############################
@ -412,15 +420,7 @@ class vmmVMWindow(vmmGObjectUI):
vmmEngine.get_instance().exit_app()
def activate_default_console_page(self):
pages = self.widget("details-pages")
# console.activate_default_console_page() will as a side effect
# switch to DETAILS_PAGE_CONSOLE. However this code path is triggered
# when the user runs a VM while they are focused on the details page,
# and we don't want to switch pages out from under them.
origpage = pages.get_current_page()
self.console.details_activate_default_console_page()
pages.set_current_page(origpage)
self._console.vmwindow_activate_default_console_page()
# activate_* are called from engine.py via CLI options
def activate_default_page(self):
@ -463,7 +463,7 @@ class vmmVMWindow(vmmGObjectUI):
def control_vm_menu(self, src_ignore):
can_usb = bool(self.vm.has_spicevmc_type_redirdev() and
self.console.details_viewer_has_usb_redirection())
self._console.vmwindow_viewer_has_usb_redirection())
self.widget("details-menu-usb-redirection").set_sensitive(can_usb)
def control_vm_run(self, src_ignore):
@ -485,7 +485,7 @@ class vmmVMWindow(vmmGObjectUI):
ignore = src
spice_usbdev_dialog = self.err
spice_usbdev_widget = self.console.details_viewer_get_usb_widget()
spice_usbdev_widget = self._console.vmwindow_viewer_get_usb_widget()
if not spice_usbdev_widget: # pragma: no cover
self.err.show_err(_("Error initializing spice USB device widget"))
return
@ -496,7 +496,7 @@ class vmmVMWindow(vmmGObjectUI):
buttons=Gtk.ButtonsType.CLOSE)
def _take_screenshot(self):
image = self.console.details_viewer_get_pixbuf()
image = self._console.vmwindow_viewer_get_pixbuf()
metadata = {
'tEXt::Hypervisor URI': self.vm.conn.get_uri(),
@ -563,3 +563,100 @@ class vmmVMWindow(vmmGObjectUI):
def page_refresh(self, page):
if page == DETAILS_PAGE_DETAILS:
self._details.vmwindow_page_refresh()
#########################
# Console page handling #
#########################
def _console_page_changed_cb(self, src):
if not self.vm:
# This is triggered via cleanup + idle_add, so vm might
# disappear and spam the logs
return # pragma: no cover
paused = self.vm.is_paused()
is_viewer = self._console.vmwindow_get_viewer_is_visible()
can_usb = self._console.vmwindow_get_can_usb_redirect()
self.widget("details-menu-vm-screenshot").set_sensitive(is_viewer)
self.widget("details-menu-usb-redirection").set_sensitive(can_usb)
keycombo_menu = self._console.vmwindow_get_keycombo_menu()
can_sendkey = (is_viewer and not paused)
for c in keycombo_menu.get_children():
c.set_sensitive(can_sendkey)
self._console_refresh_can_fullscreen()
self._console_refresh_resizeguest_from_settings()
def _console_refresh_can_fullscreen(self):
allow_fullscreen = self._console.vmwindow_get_viewer_is_visible()
self.widget("control-fullscreen").set_sensitive(allow_fullscreen)
self.widget("details-menu-view-fullscreen").set_sensitive(
allow_fullscreen)
def _console_refresh_scaling_from_settings(self):
scale_type = self.vm.get_console_scaling()
self.widget("details-menu-view-scale-always").set_active(
scale_type == self.config.CONSOLE_SCALE_ALWAYS)
self.widget("details-menu-view-scale-never").set_active(
scale_type == self.config.CONSOLE_SCALE_NEVER)
self.widget("details-menu-view-scale-fullscreen").set_active(
scale_type == self.config.CONSOLE_SCALE_FULLSCREEN)
self._console.vmwindow_sync_scaling_with_display()
def _scaling_ui_changed_cb(self, src):
# Called from details.py
if not src.get_active():
return
scale_type = 0
if src == self.widget("details-menu-view-scale-always"):
scale_type = self.config.CONSOLE_SCALE_ALWAYS
elif src == self.widget("details-menu-view-scale-fullscreen"):
scale_type = self.config.CONSOLE_SCALE_FULLSCREEN
elif src == self.widget("details-menu-view-scale-never"):
scale_type = self.config.CONSOLE_SCALE_NEVER
self.vm.set_console_scaling(scale_type)
def _fullscreen_changed_cb(self, src):
do_fullscreen = src.get_active()
self.widget("control-fullscreen").set_active(do_fullscreen)
self._console.vmwindow_set_fullscreen(do_fullscreen)
self.widget("details-menubar").set_visible(not do_fullscreen)
show_toolbar = not do_fullscreen
if not self.widget("details-menu-view-toolbar").get_active():
show_toolbar = False # pragma: no cover
self.widget("toolbar-box").set_visible(show_toolbar)
def _resizeguest_ui_changed_cb(self, src):
if not src.get_sensitive():
return # pragma: no cover
val = int(self.widget("details-menu-view-resizeguest").get_active())
self.vm.set_console_resizeguest(val)
self._console.vmwindow_sync_resizeguest_with_display()
def _console_refresh_resizeguest_from_settings(self):
tooltip = self._console.vmwindow_get_resizeguest_tooltip()
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._console.vmwindow_sync_resizeguest_with_display()
def _size_to_vm_cb(self, src):
self._console.vmwindow_set_size_to_vm()
def _console_leave_fullscreen_cb(self, src):
# This will trigger de-fullscreening in a roundabout way
self.widget("control-fullscreen").set_active(False)