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:
parent
48bf5c6ab8
commit
43512302f7
|
@ -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"><b>The console is currently unavailable</b></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"><b>The console is currently unavailable</b></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>
|
281
ui/vmwindow.ui
281
ui/vmwindow.ui
|
@ -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,281 +493,11 @@
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkNotebook" id="console-pages">
|
||||
<object class="GtkAlignment" id="console-placeholder">
|
||||
<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"/>
|
||||
<property name="can_focus">False</property>
|
||||
<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"><b>The console is currently unavailable</b></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">
|
||||
<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>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
_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
|
||||
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()
|
||||
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())
|
||||
|
||||
|
||||
#########################
|
||||
|
@ -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()
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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,11 +227,10 @@ class vmmVMWindow(vmmGObjectUI):
|
|||
return
|
||||
|
||||
self.topwin.hide()
|
||||
if self.console.details_viewer_is_visible():
|
||||
try:
|
||||
self.console.details_close_viewer()
|
||||
except Exception: # pragma: no cover
|
||||
log.error("Failure when disconnecting from desktop server")
|
||||
try:
|
||||
self._console.vmwindow_close_viewer()
|
||||
except Exception: # pragma: no cover
|
||||
log.error("Failure when disconnecting from desktop server")
|
||||
|
||||
self.emit("closed")
|
||||
vmmEngine.get_instance().decrement_window_counter()
|
||||
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue