2015-04-04 03:54:38 +03:00
# Copyright (C) 2006-2008, 2015 Red Hat, Inc.
2009-10-30 20:25:27 +03:00
# Copyright (C) 2006 Daniel P. Berrange <berrange@redhat.com>
2015-03-23 22:56:55 +03:00
# Copyright (C) 2010 Marc-Andre Lureau <marcandre.lureau@redhat.com>
2009-10-30 20:25:27 +03:00
#
2018-04-04 16:35:41 +03:00
# This work is licensed under the GNU GPLv2 or later.
2018-03-20 22:00:02 +03:00
# See the COPYING file in the top-level directory.
2009-10-30 20:25:27 +03:00
2012-05-14 17:24:56 +04:00
from gi . repository import Gtk
from gi . repository import Gdk
2010-02-11 17:32:05 +03:00
2019-06-17 04:12:39 +03:00
from virtinst import log
2014-09-21 21:36:28 +04:00
from . serialcon import vmmSerialConsole
2015-04-04 03:54:38 +03:00
from . sshtunnels import ConnectionInfo
2022-02-27 22:23:09 +03:00
from . viewers import SpiceViewer , VNCViewer , SPICE_GTK_IMPORT_ERROR
2019-06-17 04:56:34 +03:00
from . . baseclass import vmmGObject , vmmGObjectUI
2020-08-26 16:03:09 +03:00
from . . lib . keyring import vmmKeyring
2015-04-04 03:54:38 +03:00
2009-10-30 20:25:27 +03:00
2015-09-18 18:55:15 +03:00
# console-pages IDs
( _CONSOLE_PAGE_UNAVAILABLE ,
_CONSOLE_PAGE_SERIAL ,
2020-09-09 22:14:05 +03:00
_CONSOLE_PAGE_GRAPHICS ) = range ( 3 )
# console-gfx-pages IDs
( _GFX_PAGE_VIEWER ,
_GFX_PAGE_AUTH ,
2020-09-10 02:04:44 +03:00
_GFX_PAGE_UNAVAILABLE ,
_GFX_PAGE_CONNECT ) = range ( 4 )
2015-09-18 18:55:15 +03:00
2016-05-17 19:14:32 +03:00
class _TimedRevealer ( vmmGObject ) :
"""
Revealer for the fullscreen toolbar , with a bit of extra logic to
hide / show based on mouse over
"""
def __init__ ( self , toolbar ) :
vmmGObject . __init__ ( self )
self . _in_fullscreen = False
self . _timeout_id = None
self . _revealer = Gtk . Revealer ( )
self . _revealer . add ( toolbar )
# Adding the revealer to the eventbox seems to ensure the
# eventbox always has 1 invisible pixel showing at the top of the
# screen, which we can use to grab the pointer event to show
# the hidden toolbar.
self . _ebox = Gtk . EventBox ( )
self . _ebox . add ( self . _revealer )
self . _ebox . set_halign ( Gtk . Align . CENTER )
self . _ebox . set_valign ( Gtk . Align . START )
self . _ebox . show_all ( )
self . _ebox . connect ( " enter-notify-event " , self . _enter_notify )
self . _ebox . connect ( " leave-notify-event " , self . _enter_notify )
def _cleanup ( self ) :
self . _ebox . destroy ( )
self . _ebox = None
self . _revealer . destroy ( )
self . _revealer = None
self . _timeout_id = None
def _enter_notify ( self , ignore1 , ignore2 ) :
x , y = self . _ebox . get_pointer ( )
alloc = self . _ebox . get_allocation ( )
entered = bool ( x > = 0 and y > = 0 and
x < alloc . width and y < alloc . height )
if not self . _in_fullscreen :
return
# Pointer exited the toolbar, and toolbar is revealed. Schedule
# a timeout to close it, if one isn't already scheduled
if not entered and self . _revealer . get_reveal_child ( ) :
self . _schedule_unreveal_timeout ( 1000 )
return
self . _unregister_timeout ( )
if entered and not self . _revealer . get_reveal_child ( ) :
self . _revealer . set_reveal_child ( True )
def _schedule_unreveal_timeout ( self , timeout ) :
if self . _timeout_id :
2020-08-28 18:55:36 +03:00
return # pragma: no cover
2016-05-17 19:14:32 +03:00
def cb ( ) :
self . _revealer . set_reveal_child ( False )
self . _timeout_id = None
self . _timeout_id = self . timeout_add ( timeout , cb )
def _unregister_timeout ( self ) :
2020-09-09 22:14:05 +03:00
if self . _timeout_id : # pragma: no cover
2016-05-17 19:14:32 +03:00
self . remove_gobject_timeout ( self . _timeout_id )
self . _timeout_id = None
def force_reveal ( self , val ) :
self . _unregister_timeout ( )
self . _in_fullscreen = val
self . _revealer . set_reveal_child ( val )
self . _schedule_unreveal_timeout ( 2000 )
def get_overlay_widget ( self ) :
return self . _ebox
2019-01-02 03:28:04 +03:00
def build_keycombo_menu ( on_send_key_fn ) :
menu = Gtk . Menu ( )
2020-07-14 10:41:55 +03:00
def make_item ( accel , combo ) :
name = Gtk . accelerator_get_label ( * Gtk . accelerator_parse ( accel ) )
item = Gtk . MenuItem ( name )
2019-01-02 03:28:04 +03:00
item . connect ( " activate " , on_send_key_fn , combo )
menu . add ( item )
2020-07-14 10:41:55 +03:00
make_item ( " <Control><Alt>BackSpace " , [ " Control_L " , " Alt_L " , " BackSpace " ] )
make_item ( " <Control><Alt>Delete " , [ " Control_L " , " Alt_L " , " Delete " ] )
2019-01-02 03:28:04 +03:00
menu . add ( Gtk . SeparatorMenuItem ( ) )
for i in range ( 1 , 13 ) :
2020-07-14 10:41:55 +03:00
make_item ( " <Control><Alt>F %d " % i , [ " Control_L " , " Alt_L " , " F %d " % i ] )
2019-01-02 03:28:04 +03:00
menu . add ( Gtk . SeparatorMenuItem ( ) )
2020-07-14 10:41:55 +03:00
make_item ( " Print " , [ " Print " ] )
2019-01-02 03:28:04 +03:00
menu . show_all ( )
return menu
class vmmOverlayToolbar :
2019-04-03 00:12:20 +03:00
def __init__ ( self , on_leave_fn , on_send_key_fn ) :
self . _send_key_button = None
self . _keycombo_menu = None
self . _toolbar = None
2019-01-02 03:28:04 +03:00
self . timed_revealer = None
2019-04-03 00:12:20 +03:00
self . _init_ui ( on_leave_fn , on_send_key_fn )
2019-01-02 03:28:04 +03:00
2019-04-03 00:12:20 +03:00
def _init_ui ( self , on_leave_fn , on_send_key_fn ) :
self . _keycombo_menu = build_keycombo_menu ( on_send_key_fn )
2019-01-02 03:28:04 +03:00
2019-04-03 00:12:20 +03:00
self . _toolbar = Gtk . Toolbar ( )
self . _toolbar . set_show_arrow ( False )
self . _toolbar . set_style ( Gtk . ToolbarStyle . BOTH_HORIZ )
self . _toolbar . get_accessible ( ) . set_name ( " Fullscreen Toolbar " )
2019-01-02 03:28:04 +03:00
2019-04-03 00:12:20 +03:00
# Exit button
2022-02-19 20:01:55 +03:00
button = Gtk . ToolButton ( )
button . set_label ( _ ( " Leave Fullscreen " ) )
button . set_icon_name ( " view-restore " )
2019-04-03 00:12:20 +03:00
button . set_tooltip_text ( _ ( " Leave fullscreen " ) )
2019-01-02 03:28:04 +03:00
button . show ( )
2019-04-03 00:12:20 +03:00
button . get_accessible ( ) . set_name ( " Fullscreen Exit " )
self . _toolbar . add ( button )
2019-01-02 03:28:04 +03:00
button . connect ( " clicked " , on_leave_fn )
2019-04-03 00:12:20 +03:00
self . _send_key_button = Gtk . ToolButton ( )
self . _send_key_button . set_icon_name (
2019-01-02 03:28:04 +03:00
" preferences-desktop-keyboard-shortcuts " )
2019-04-03 00:12:20 +03:00
self . _send_key_button . set_tooltip_text ( _ ( " Send key combination " ) )
self . _send_key_button . show_all ( )
2019-04-03 00:15:08 +03:00
self . _send_key_button . connect ( " clicked " ,
self . _on_send_key_button_clicked_cb )
2019-04-03 00:12:20 +03:00
self . _send_key_button . get_accessible ( ) . set_name ( " Fullscreen Send Key " )
self . _toolbar . add ( self . _send_key_button )
2019-01-02 03:28:04 +03:00
2019-04-03 00:12:20 +03:00
self . timed_revealer = _TimedRevealer ( self . _toolbar )
2019-01-02 03:28:04 +03:00
2019-04-03 00:15:08 +03:00
def _on_send_key_button_clicked_cb ( self , src ) :
2019-04-03 00:22:37 +03:00
event = Gtk . get_current_event ( )
win = self . _toolbar . get_window ( )
rect = Gdk . Rectangle ( )
rect . y = win . get_height ( )
self . _keycombo_menu . popup_at_rect ( win , rect ,
Gdk . Gravity . NORTH_WEST , Gdk . Gravity . NORTH_WEST , event )
2019-04-03 00:15:08 +03:00
2019-01-02 03:28:04 +03:00
def cleanup ( self ) :
2019-04-03 00:12:20 +03:00
self . _keycombo_menu . destroy ( )
self . _keycombo_menu = None
self . _toolbar . destroy ( )
self . _toolbar = None
2019-01-02 03:28:04 +03:00
self . timed_revealer . cleanup ( )
self . timed_revealer = None
2024-03-03 00:22:53 +03:00
def _cant_embed_graphics ( ginfo ) :
if ginfo . gtype in [ " vnc " , " spice " ] :
return
msg = _ ( " Cannot display graphical console type ' %s ' " ) % ginfo . gtype
return msg
class _ConsoleMenu ( vmmGObject ) :
2020-09-09 14:44:17 +03:00
"""
Helper class for building the text / graphical console menu list
"""
2024-03-03 00:22:53 +03:00
def __init__ ( self , show_cb , toggled_cb ) :
vmmGObject . __init__ ( self )
self . _menu = Gtk . Menu ( )
self . _menu . connect ( " show " , show_cb )
self . _toggled_cb = toggled_cb
def _cleanup ( self ) :
self . _menu . destroy ( )
self . _menu = None
self . _toggled_cb = None
2020-09-09 14:44:17 +03:00
################
# Internal API #
################
def _build_serial_menu_items ( self , vm ) :
ret = [ ]
2024-03-03 00:22:53 +03:00
for dev in vmmSerialConsole . get_serialcon_devices ( vm ) :
2020-09-09 14:44:17 +03:00
if dev . DEVICE_TYPE == " console " :
label = _ ( " Text Console %d " ) % ( dev . get_xml_idx ( ) + 1 )
else :
label = _ ( " Serial %d " ) % ( dev . get_xml_idx ( ) + 1 )
tooltip = vmmSerialConsole . can_connect ( vm , dev )
ret . append ( [ label , dev , tooltip ] )
2024-03-03 00:22:53 +03:00
if not ret :
ret = [ [ _ ( " No text console available " ) , None , None ] ]
2020-09-09 14:44:17 +03:00
return ret
def _build_graphical_menu_items ( self , vm ) :
from . . device . gfxdetails import vmmGraphicsDetails
ret = [ ]
2024-03-03 00:22:53 +03:00
found_default = False
for gdev in vm . xmlobj . devices . graphics :
idx = gdev . get_xml_idx ( )
ginfo = ConnectionInfo ( vm . conn , gdev )
2020-09-09 14:44:17 +03:00
2024-03-03 00:22:53 +03:00
label = ( _ ( " Graphical Console " ) + " " +
vmmGraphicsDetails . graphics_pretty_type_simple ( gdev . type ) )
2020-09-09 14:44:17 +03:00
if idx > 0 :
label + = " %s " % ( idx + 1 )
2023-01-30 10:49:24 +03:00
2024-03-03 00:22:53 +03:00
tooltip = _cant_embed_graphics ( ginfo )
if not tooltip :
if not found_default :
found_default = True
else :
tooltip = _ ( " virt-manager does not support more "
" than one graphical console " )
2020-09-09 14:44:17 +03:00
2024-03-03 00:22:53 +03:00
ret . append ( [ label , ginfo , tooltip ] )
if not ret :
ret = [ [ _ ( " Graphical console not configured for guest " ) ,
None , None ] ]
2020-09-09 14:44:17 +03:00
return ret
2024-03-03 00:22:53 +03:00
def _get_selected_menu_item ( self ) :
for child in self . _menu . get_children ( ) :
if hasattr ( child , ' get_active ' ) and child . get_active ( ) :
return child
2020-09-09 14:44:17 +03:00
##############
# Public API #
##############
2024-03-03 00:22:53 +03:00
def rebuild_menu ( self , vm ) :
olditem = self . _get_selected_menu_item ( )
oldlabel = olditem and olditem . get_label ( ) or None
# Clear menu
for child in self . _menu . get_children ( ) :
self . _menu . remove ( child )
2020-09-09 14:44:17 +03:00
graphics = self . _build_graphical_menu_items ( vm )
serials = self . _build_serial_menu_items ( vm )
# Use label == None to tell the loop to add a separator
items = graphics + [ [ None , None , None ] ] + serials
last_item = None
for ( label , dev , tooltip ) in items :
if label is None :
2024-03-03 00:22:53 +03:00
self . _menu . add ( Gtk . SeparatorMenuItem ( ) )
2020-09-09 14:44:17 +03:00
continue
2021-02-05 14:15:46 +03:00
sensitive = bool ( dev and not tooltip )
2024-03-03 00:22:53 +03:00
if not sensitive and not tooltip :
tooltip = label
2020-09-09 14:44:17 +03:00
active = False
if oldlabel is None and sensitive :
# Select the first selectable option
oldlabel = label
if label == oldlabel :
active = True
item = Gtk . RadioMenuItem ( )
if last_item is None :
last_item = item
else :
item . join_group ( last_item )
item . set_label ( label )
item . set_active ( active and sensitive )
item . set_sensitive ( sensitive )
item . set_tooltip_text ( tooltip or None )
2024-03-03 00:22:53 +03:00
item . vmm_data = dev
if sensitive :
item . connect ( " toggled " , self . _toggled_cb )
self . _menu . add ( item )
2020-09-09 14:44:17 +03:00
2024-03-03 00:22:53 +03:00
self . _menu . show_all ( )
2020-09-09 14:44:17 +03:00
2024-03-03 00:22:53 +03:00
def activate_default ( self ) :
for child in self . _menu . get_children ( ) :
2020-09-09 14:44:17 +03:00
if child . get_sensitive ( ) and hasattr ( child , " toggled " ) :
child . toggled ( )
2020-09-09 22:14:05 +03:00
return True
return False
2020-09-09 14:44:17 +03:00
2024-03-03 00:22:53 +03:00
def get_selected ( self ) :
row = self . _get_selected_menu_item ( )
if not row :
row = self . _menu . get_children ( ) [ 0 ]
return row . get_label ( ) , row . vmm_data , row . get_tooltip_text ( )
def get_menu ( self ) :
return self . _menu
2023-01-30 10:23:37 +03:00
2020-09-09 14:44:17 +03:00
2015-04-04 03:54:38 +03:00
class vmmConsolePages ( vmmGObjectUI ) :
2015-04-04 01:03:58 +03:00
"""
2015-04-04 03:54:38 +03:00
Handles all the complex UI handling dictated by the spice / vnc widgets
2015-04-04 01:03:58 +03:00
"""
2020-09-09 22:14:05 +03:00
__gsignals__ = {
" page-changed " : ( vmmGObjectUI . RUN_FIRST , None , [ ] ) ,
" leave-fullscreen " : ( vmmGObjectUI . RUN_FIRST , None , [ ] ) ,
2020-09-21 05:53:30 +03:00
" change-title " : ( vmmGObjectUI . RUN_FIRST , None , [ ] ) ,
2020-09-09 22:14:05 +03:00
}
2013-02-16 22:31:46 +04:00
def __init__ ( self , vm , builder , topwin ) :
2020-09-09 22:14:05 +03:00
vmmGObjectUI . __init__ ( self , " console.ui " ,
None , builder = builder , topwin = topwin )
2009-10-30 20:25:27 +03:00
self . vm = vm
2020-09-09 22:14:05 +03:00
self . top_box = self . widget ( " console-pages " )
2015-04-04 01:03:58 +03:00
self . _pointer_is_grabbed = False
2009-10-30 20:25:27 +03:00
# State for disabling modifiers when keyboard is grabbed
2015-04-04 04:34:03 +03:00
self . _accel_groups = Gtk . accel_groups_from_object ( self . topwin )
self . _gtk_settings_accel = None
self . _gtk_settings_mnemonic = None
2009-10-30 20:25:27 +03:00
# Initialize display widget
2015-04-04 01:03:58 +03:00
self . _viewer = None
2020-09-10 02:04:44 +03:00
self . _viewer_connect_clicked = False
2020-09-09 22:14:05 +03:00
self . _in_fullscreen = False
2009-10-30 20:25:27 +03:00
2011-05-19 01:22:07 +04:00
# Fullscreen toolbar
2019-01-02 03:28:04 +03:00
self . _keycombo_menu = build_keycombo_menu ( self . _do_send_key )
2019-04-03 00:12:20 +03:00
self . _overlay_toolbar_fullscreen = vmmOverlayToolbar (
2019-01-02 03:28:04 +03:00
on_leave_fn = self . _leave_fullscreen ,
on_send_key_fn = self . _do_send_key )
2019-04-03 00:12:20 +03:00
self . widget ( " console-overlay " ) . add_overlay (
self . _overlay_toolbar_fullscreen . timed_revealer . get_overlay_widget ( ) )
2011-05-19 01:22:07 +04:00
2022-01-27 18:57:06 +03:00
# When the gtk-vnc and spice-gtk widgets are in non-scaling mode, we
# make them fill the whole window, and they paint the non-VM areas of
# the viewer black. But when scaling is enabled, the viewer widget is
# constrained. This change makes sure the non-VM portions in that case
# are also colored black, rather than the default theme window color.
self . widget ( " console-gfx-viewport " ) . modify_bg (
Gtk . StateType . NORMAL , Gdk . Color ( 0 , 0 , 0 ) )
2015-09-18 18:55:15 +03:00
self . widget ( " console-pages " ) . set_show_tabs ( False )
self . widget ( " serial-pages " ) . set_show_tabs ( False )
2020-09-09 22:14:05 +03:00
self . widget ( " console-gfx-pages " ) . set_show_tabs ( False )
2015-09-18 18:55:15 +03:00
2024-03-03 00:22:53 +03:00
self . _consolemenu = _ConsoleMenu (
self . _on_console_menu_show_cb ,
self . _on_console_menu_toggled_cb )
2015-09-18 18:55:15 +03:00
self . _serial_consoles = [ ]
2013-09-02 01:40:38 +04:00
2019-05-05 23:25:35 +03:00
# Signals are added by vmmVMWindow. Don't use connect_signals here
2009-10-30 20:25:27 +03:00
# or it changes will be overwritten
2013-04-18 01:39:25 +04:00
2020-09-09 22:14:05 +03:00
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 ,
2020-09-10 02:04:44 +03:00
" on_console_connect_button_clicked " : self . _connect_button_clicked_cb ,
2020-09-09 22:14:05 +03:00
} )
2015-09-18 20:05:41 +03:00
self . widget ( " console-gfx-scroll " ) . connect ( " size-allocate " ,
self . _scroll_size_allocate )
2020-09-09 22:14:05 +03:00
self . widget ( " console-gfx-pages " ) . connect ( " switch-page " ,
self . _page_changed_cb )
2009-10-30 20:25:27 +03:00
2014-01-27 02:09:07 +04:00
2011-07-24 05:16:54 +04:00
def _cleanup ( self ) :
2011-04-12 02:35:21 +04:00
self . vm = None
2011-05-19 01:36:08 +04:00
2015-04-04 01:03:58 +03:00
if self . _viewer :
2020-09-09 22:14:05 +03:00
self . _viewer . cleanup ( ) # pragma: no cover
2015-04-04 01:03:58 +03:00
self . _viewer = None
2011-04-12 02:35:21 +04:00
2019-01-02 03:28:04 +03:00
self . _overlay_toolbar_fullscreen . cleanup ( )
2011-05-19 01:22:07 +04:00
2015-09-18 18:55:15 +03:00
for serial in self . _serial_consoles :
2013-09-02 01:40:38 +04:00
serial . cleanup ( )
2015-09-18 18:55:15 +03:00
self . _serial_consoles = [ ]
2013-09-02 01:40:38 +04:00
2024-03-03 00:22:53 +03:00
self . _consolemenu . cleanup ( )
self . _consolemenu = None
2013-09-02 01:40:38 +04:00
2015-04-04 04:34:03 +03:00
#################
# Internal APIs #
#################
2010-04-21 20:59:25 +04:00
def _disable_modifiers ( self ) :
2015-04-04 04:34:03 +03:00
if self . _gtk_settings_accel is not None :
2020-09-09 22:14:05 +03:00
return # pragma: no cover
2009-11-02 00:36:44 +03:00
2015-04-04 04:34:03 +03:00
for g in self . _accel_groups :
2009-10-30 20:25:27 +03:00
self . topwin . remove_accel_group ( g )
2009-10-30 21:36:17 +03:00
2012-05-14 17:24:56 +04:00
settings = Gtk . Settings . get_default ( )
2015-04-04 04:34:03 +03:00
self . _gtk_settings_accel = settings . get_property ( ' gtk-menu-bar-accel ' )
2009-10-30 20:25:27 +03:00
settings . set_property ( ' gtk-menu-bar-accel ' , None )
2015-04-04 04:34:03 +03:00
self . _gtk_settings_mnemonic = settings . get_property (
2015-03-26 20:10:38 +03:00
" gtk-enable-mnemonics " )
settings . set_property ( " gtk-enable-mnemonics " , False )
2009-10-30 21:36:17 +03:00
2010-04-21 20:59:25 +04:00
def _enable_modifiers ( self ) :
2015-04-04 04:34:03 +03:00
if self . _gtk_settings_accel is None :
2009-10-30 20:25:27 +03:00
return
2009-11-02 00:36:44 +03:00
2012-05-14 17:24:56 +04:00
settings = Gtk . Settings . get_default ( )
2015-04-04 04:34:03 +03:00
settings . set_property ( ' gtk-menu-bar-accel ' , self . _gtk_settings_accel )
self . _gtk_settings_accel = None
2009-10-30 21:36:17 +03:00
2015-04-04 04:34:03 +03:00
if self . _gtk_settings_mnemonic is not None :
2009-10-30 21:36:17 +03:00
settings . set_property ( " gtk-enable-mnemonics " ,
2015-04-04 04:34:03 +03:00
self . _gtk_settings_mnemonic )
2009-10-30 21:36:17 +03:00
2015-04-04 04:34:03 +03:00
for g in self . _accel_groups :
2009-10-30 20:25:27 +03:00
self . topwin . add_accel_group ( g )
2015-04-04 04:34:03 +03:00
def _do_send_key ( self , src , keys ) :
ignore = src
if keys is not None :
self . _viewer . console_send_keys ( keys )
###########################
# Resize and scaling APIs #
###########################
def _scroll_size_allocate ( self , src_ignore , req ) :
2016-05-20 01:27:35 +03:00
if not self . _viewer :
return
2020-08-29 20:32:37 +03:00
res = self . _viewer . console_get_desktop_resolution ( )
if res is None :
if not self . config . CLITestOptions . fake_console_resolution :
return
res = ( 800 , 600 )
2015-04-04 04:34:03 +03:00
scroll = self . widget ( " console-gfx-scroll " )
is_scale = self . _viewer . console_get_scaling ( )
is_resizeguest = self . _viewer . console_get_resizeguest ( )
dx = 0
dy = 0
align_ratio = float ( req . width ) / float ( req . height )
# pylint: disable=unpacking-non-sequence
2020-08-29 20:32:37 +03:00
desktop_w , desktop_h = res
2015-04-04 04:34:03 +03:00
desktop_ratio = float ( desktop_w ) / float ( desktop_h )
2016-05-20 01:27:35 +03:00
if is_scale :
2015-04-04 04:34:03 +03:00
# Make sure we never show scrollbars when scaling
scroll . set_policy ( Gtk . PolicyType . NEVER , Gtk . PolicyType . NEVER )
else :
scroll . set_policy ( Gtk . PolicyType . AUTOMATIC ,
Gtk . PolicyType . AUTOMATIC )
2016-05-20 01:27:35 +03:00
if is_resizeguest :
2015-04-04 04:34:03 +03:00
# With resize guest, we don't want to maintain aspect ratio,
# since the guest can resize to arbitrary resolutions.
2019-10-09 12:18:53 +03:00
viewer_alloc = Gdk . Rectangle ( )
viewer_alloc . width = req . width
viewer_alloc . height = req . height
self . _viewer . console_size_allocate ( viewer_alloc )
2015-04-04 04:34:03 +03:00
return
2016-05-20 01:27:35 +03:00
if not is_scale :
2015-04-04 04:34:03 +03:00
# Scaling disabled is easy, just force the VNC widget size. Since
# we are inside a scrollwindow, it shouldn't cause issues.
self . _viewer . console_set_size_request ( desktop_w , desktop_h )
return
# Make sure there is no hard size requirement so we can scale down
self . _viewer . console_set_size_request ( - 1 , - 1 )
# Make sure desktop aspect ratio is maintained
if align_ratio > desktop_ratio :
desktop_w = int ( req . height * desktop_ratio )
desktop_h = req . height
2017-10-11 14:35:55 +03:00
dx = ( req . width - desktop_w ) / / 2
2015-04-04 04:34:03 +03:00
else :
desktop_w = req . width
2017-10-11 14:35:55 +03:00
desktop_h = int ( req . width / / desktop_ratio )
dy = ( req . height - desktop_h ) / / 2
2015-04-04 04:34:03 +03:00
viewer_alloc = Gdk . Rectangle ( )
viewer_alloc . x = dx
viewer_alloc . y = dy
viewer_alloc . width = desktop_w
viewer_alloc . height = desktop_h
self . _viewer . console_size_allocate ( viewer_alloc )
2020-09-09 22:14:05 +03:00
def _viewer_get_resizeguest_tooltip ( self ) :
2014-01-31 18:13:53 +04:00
tooltip = " "
2015-04-04 01:03:58 +03:00
if self . _viewer :
2021-05-22 20:04:15 +03:00
tooltip = self . _viewer . console_get_resizeguest_warning ( )
return tooltip or " "
2014-01-31 18:13:53 +04:00
2015-04-04 04:34:03 +03:00
def _sync_resizeguest_with_display ( self ) :
if not self . _viewer :
return
val = bool ( self . vm . get_console_resizeguest ( ) )
self . _viewer . console_set_resizeguest ( val )
self . widget ( " console-gfx-scroll " ) . queue_resize ( )
2020-09-09 22:14:05 +03:00
def _set_size_to_vm ( self ) :
2015-04-04 04:34:03 +03:00
# Resize the console to best fit the VM resolution
2015-04-04 01:03:58 +03:00
if not self . _viewer :
2020-08-28 18:55:36 +03:00
return # pragma: no cover
2015-04-04 04:34:03 +03:00
if not self . _viewer . console_get_desktop_resolution ( ) :
2020-08-28 18:55:36 +03:00
return # pragma: no cover
2014-01-31 18:13:53 +04:00
2016-12-13 21:31:17 +03:00
top_w , top_h = self . topwin . get_size ( )
2016-05-20 01:27:35 +03:00
viewer_alloc = self . widget ( " console-gfx-scroll " ) . get_allocation ( )
desktop_w , desktop_h = self . _viewer . console_get_desktop_resolution ( )
2015-04-04 04:34:03 +03:00
self . topwin . unmaximize ( )
2016-05-20 01:27:35 +03:00
self . topwin . resize (
2016-12-13 21:31:17 +03:00
desktop_w + ( top_w - viewer_alloc . width ) ,
desktop_h + ( top_h - viewer_alloc . height ) )
2014-01-31 18:13:53 +04:00
2015-04-04 04:34:03 +03:00
################
# Scaling APIs #
################
2015-04-04 01:03:58 +03:00
def _sync_scaling_with_display ( self ) :
if not self . _viewer :
2010-12-22 04:13:11 +03:00
return
2020-09-09 22:14:05 +03:00
fs = self . _in_fullscreen
2015-04-04 01:03:58 +03:00
curscale = self . _viewer . console_get_scaling ( )
2014-01-31 04:15:02 +04:00
scale_type = self . vm . get_console_scaling ( )
2009-10-30 20:25:27 +03:00
2015-07-10 15:07:02 +03:00
if ( scale_type == self . config . CONSOLE_SCALE_NEVER and
curscale is True ) :
2015-04-04 01:03:58 +03:00
self . _viewer . console_set_scaling ( False )
2015-07-10 15:07:02 +03:00
elif ( scale_type == self . config . CONSOLE_SCALE_ALWAYS and
curscale is False ) :
2015-04-04 01:03:58 +03:00
self . _viewer . console_set_scaling ( True )
2015-07-10 15:07:02 +03:00
elif ( scale_type == self . config . CONSOLE_SCALE_FULLSCREEN and
curscale != fs ) :
2015-04-04 01:03:58 +03:00
self . _viewer . console_set_scaling ( fs )
2009-10-30 20:25:27 +03:00
2009-11-29 02:48:56 +03:00
# Refresh viewer size
2014-01-30 01:58:34 +04:00
self . widget ( " console-gfx-scroll " ) . queue_resize ( )
2009-11-29 02:48:56 +03:00
2009-10-30 20:25:27 +03:00
2015-04-04 04:34:03 +03:00
###################
# Fullscreen APIs #
###################
def _leave_fullscreen ( self , ignore = None ) :
2020-09-09 22:14:05 +03:00
self . emit ( " leave-fullscreen " )
2009-11-23 00:39:38 +03:00
2011-05-19 01:22:07 +04:00
def _change_fullscreen ( self , do_fullscreen ) :
2009-11-23 00:39:38 +03:00
if do_fullscreen :
2020-09-09 22:14:05 +03:00
self . _in_fullscreen = True
2009-10-30 20:25:27 +03:00
self . topwin . fullscreen ( )
2019-01-02 03:28:04 +03:00
self . _overlay_toolbar_fullscreen . timed_revealer . force_reveal ( True )
2009-10-30 20:25:27 +03:00
else :
2020-09-09 22:14:05 +03:00
self . _in_fullscreen = False
2019-01-02 03:28:04 +03:00
self . _overlay_toolbar_fullscreen . timed_revealer . force_reveal ( False )
2009-10-30 20:25:27 +03:00
self . topwin . unfullscreen ( )
2015-04-04 01:03:58 +03:00
self . _sync_scaling_with_display ( )
2014-01-30 01:58:34 +04:00
2009-10-30 20:25:27 +03:00
##########################
# State tracking methods #
##########################
2015-04-12 17:24:35 +03:00
def _show_vm_status_unavailable ( self ) :
2020-08-29 20:32:37 +03:00
if self . vm . is_crashed ( ) : # pragma: no cover
2020-09-09 22:14:05 +03:00
self . _activate_vm_unavailable_page ( _ ( " Guest has crashed. " ) )
2009-10-30 20:25:27 +03:00
else :
2020-09-09 22:14:05 +03:00
self . _activate_vm_unavailable_page ( _ ( " Guest is not running. " ) )
2009-10-30 20:25:27 +03:00
2015-04-04 04:34:03 +03:00
def _close_viewer ( self ) :
2020-08-28 17:25:38 +03:00
self . _leave_fullscreen ( )
2020-09-10 02:04:44 +03:00
self . _viewer_connect_clicked = False
2020-08-28 17:25:38 +03:00
for serial in self . _serial_consoles :
serial . close ( )
2015-04-04 01:03:58 +03:00
if self . _viewer is None :
2011-01-14 19:34:51 +03:00
return
2015-04-12 17:33:41 +03:00
self . _viewer . console_remove_display_from_widget (
self . widget ( " console-gfx-viewport " ) )
self . _viewer . cleanup ( )
2015-04-04 01:03:58 +03:00
self . _viewer = None
2022-01-21 18:44:27 +03:00
log . debug ( " Viewer object cleaned up " )
2011-01-14 19:34:51 +03:00
2020-09-09 22:14:05 +03:00
def _refresh_vm_state ( self ) :
2020-09-11 18:32:49 +03:00
self . _activate_default_console_page ( )
2015-09-12 19:07:56 +03:00
2009-10-30 20:25:27 +03:00
2020-09-09 22:14:05 +03:00
###########################
# console page navigation #
###########################
2013-09-02 01:40:38 +04:00
2020-09-09 22:14:05 +03:00
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> " )
2009-10-30 20:25:27 +03:00
2020-09-09 22:14:05 +03:00
def _activate_vm_unavailable_page ( self , msg ) :
2011-06-21 03:09:31 +04:00
"""
2020-09-09 22:14:05 +03:00
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 .
2011-06-21 03:09:31 +04:00
"""
2015-04-04 04:34:03 +03:00
self . _close_viewer ( )
self . widget ( " console-pages " ) . set_current_page (
2020-09-09 22:14:05 +03:00
_CONSOLE_PAGE_UNAVAILABLE )
2016-08-17 19:31:54 +03:00
if msg :
2020-09-09 22:14:05 +03:00
self . widget ( " console-unavailable " ) . set_label (
" <b> " + msg + " </b> " )
self . _activate_gfx_unavailable_page ( msg )
2009-10-30 20:25:27 +03:00
2015-04-04 04:34:03 +03:00
def _activate_auth_page ( self , withPassword , withUsername ) :
2020-08-26 16:03:09 +03:00
( pw , username ) = vmmKeyring . get_instance ( ) . get_console_password ( self . vm )
2009-10-30 20:25:27 +03:00
2013-09-02 01:40:38 +04:00
self . widget ( " console-auth-password " ) . set_visible ( withPassword )
self . widget ( " label-auth-password " ) . set_visible ( withPassword )
self . widget ( " console-auth-username " ) . set_visible ( withUsername )
self . widget ( " label-auth-username " ) . set_visible ( withUsername )
2009-10-30 20:25:27 +03:00
2011-07-14 21:13:13 +04:00
self . widget ( " console-auth-username " ) . set_text ( username )
self . widget ( " console-auth-password " ) . set_text ( pw )
2009-10-30 20:25:27 +03:00
2020-08-26 16:03:09 +03:00
has_keyring = vmmKeyring . get_instance ( ) . is_available ( )
remember = bool ( withPassword and pw ) or ( withUsername and username )
remember = has_keyring and remember
self . widget ( " console-auth-remember " ) . set_sensitive ( has_keyring )
self . widget ( " console-auth-remember " ) . set_active ( remember )
2013-09-02 01:40:38 +04:00
2020-09-09 22:14:05 +03:00
self . widget ( " console-gfx-pages " ) . set_current_page ( _GFX_PAGE_AUTH )
2009-10-30 20:25:27 +03:00
2015-04-12 19:43:29 +03:00
if withUsername :
self . widget ( " console-auth-username " ) . grab_focus ( )
else :
self . widget ( " console-auth-password " ) . grab_focus ( )
2020-09-09 22:14:05 +03:00
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 )
2015-04-04 01:03:58 +03:00
if self . _viewer :
self . _viewer . console_grab_focus ( )
2009-10-30 20:25:27 +03:00
2020-09-10 02:04:44 +03:00
def _activate_gfx_connect_page ( self ) :
self . widget ( " console-gfx-pages " ) . set_current_page ( _GFX_PAGE_CONNECT )
2020-09-09 22:14:05 +03:00
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 ( )
2018-03-15 14:43:56 +03:00
2020-09-09 22:14:05 +03:00
return bool (
is_visible and
cpage == _CONSOLE_PAGE_GRAPHICS and
gpage == _GFX_PAGE_VIEWER and
2015-09-18 20:05:41 +03:00
self . _viewer and self . _viewer . console_is_open ( ) )
2015-09-06 01:23:47 +03:00
2020-09-09 22:14:05 +03:00
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 ( ) )
2011-06-01 20:22:05 +04:00
2015-04-04 04:34:03 +03:00
#########################
# Viewer login attempts #
#########################
2011-06-01 20:22:05 +04:00
2024-03-03 00:22:53 +03:00
def _init_viewer ( self , ginfo , errmsg ) :
2015-04-12 17:24:35 +03:00
if self . _viewer or not self . is_visible ( ) :
2009-10-30 20:25:27 +03:00
return
2024-03-03 00:22:53 +03:00
if errmsg :
log . debug ( " No acceptable graphics to connect to " )
self . _activate_gfx_unavailable_page ( errmsg )
2009-10-30 20:25:27 +03:00
return
2020-09-10 02:04:44 +03:00
if ( not self . vm . get_console_autoconnect ( ) and
not self . _viewer_connect_clicked ) :
self . _activate_gfx_connect_page ( )
return
2020-09-09 22:14:05 +03:00
self . _activate_gfx_unavailable_page (
2015-04-04 04:34:03 +03:00
_ ( " Connecting to graphical console for guest " ) )
2010-02-11 18:43:44 +03:00
2019-06-17 04:12:39 +03:00
log . debug ( " Starting connect process for %s " , ginfo . logstring ( ) )
2009-10-30 20:25:27 +03:00
try :
2012-03-13 23:27:23 +04:00
if ginfo . gtype == " vnc " :
2015-04-04 01:03:58 +03:00
viewer_class = VNCViewer
2012-03-13 23:27:23 +04:00
elif ginfo . gtype == " spice " :
2024-03-03 00:22:53 +03:00
# We do this here and not in the embed check, since user
# is probably expecting their spice console to work, so we
# should show an explicit failure
2022-02-27 22:23:09 +03:00
if SPICE_GTK_IMPORT_ERROR :
raise RuntimeError (
" Error opening SPICE console: %s " %
SPICE_GTK_IMPORT_ERROR )
2020-08-28 18:55:36 +03:00
viewer_class = SpiceViewer
2011-01-14 22:17:35 +03:00
2016-05-08 01:08:25 +03:00
self . _viewer = viewer_class ( self . vm , ginfo )
2015-04-04 01:03:58 +03:00
self . _connect_viewer_signals ( )
2011-01-14 22:17:35 +03:00
2015-04-12 04:39:24 +03:00
self . _viewer . console_open ( )
2017-05-05 19:47:21 +03:00
except Exception as e :
2020-09-11 18:32:49 +03:00
log . exception ( " Error connecting to graphical console " )
2020-09-09 22:14:05 +03:00
self . _activate_gfx_unavailable_page (
2020-07-14 10:41:49 +03:00
_ ( " Error connecting to graphical console: \n %s " ) % e )
2009-10-30 20:25:27 +03:00
2015-04-04 04:34:03 +03:00
def _set_credentials ( self , src_ignore = None ) :
2011-07-14 21:13:13 +04:00
passwd = self . widget ( " console-auth-password " )
username = self . widget ( " console-auth-username " )
2015-04-04 01:03:58 +03:00
if passwd . get_visible ( ) :
self . _viewer . console_set_password ( passwd . get_text ( ) )
2012-05-14 17:24:56 +04:00
if username . get_visible ( ) :
2015-04-04 01:03:58 +03:00
self . _viewer . console_set_username ( username . get_text ( ) )
2009-10-30 20:25:27 +03:00
2011-07-14 21:13:13 +04:00
if self . widget ( " console-auth-remember " ) . get_active ( ) :
2020-08-26 16:03:09 +03:00
vmmKeyring . get_instance ( ) . set_console_password (
self . vm , passwd . get_text ( ) , username . get_text ( ) )
2016-06-07 16:25:55 +03:00
else :
2020-08-26 16:03:09 +03:00
vmmKeyring . get_instance ( ) . del_console_password ( self . vm )
2009-10-30 20:25:27 +03:00
2015-04-04 01:03:58 +03:00
##########################
# Viewer signal handling #
##########################
2022-01-21 18:37:24 +03:00
def _viewer_add_display_cb ( self , _src , display ) :
2015-04-04 01:03:58 +03:00
self . widget ( " console-gfx-viewport " ) . add ( display )
# Sync initial settings
self . _sync_scaling_with_display ( )
2020-09-09 22:14:05 +03:00
self . _sync_resizeguest_with_display ( )
2015-04-04 01:03:58 +03:00
2022-01-21 18:37:24 +03:00
def _pointer_grabbed_cb ( self , _src ) :
2015-04-04 01:03:58 +03:00
self . _pointer_is_grabbed = True
2020-09-21 05:53:30 +03:00
self . emit ( " change-title " )
2015-04-04 01:03:58 +03:00
2022-01-21 18:37:24 +03:00
def _pointer_ungrabbed_cb ( self , _src ) :
2015-04-04 01:03:58 +03:00
self . _pointer_is_grabbed = False
2020-09-21 05:53:30 +03:00
self . emit ( " change-title " )
2015-04-04 01:03:58 +03:00
def _viewer_allocate_cb ( self , src , ignore ) :
self . widget ( " console-gfx-scroll " ) . queue_resize ( )
2020-09-09 16:33:58 +03:00
def _viewer_keyboard_grab_cb ( self , src ) :
self . _viewer_sync_modifiers ( )
def _serial_focus_changed_cb ( self , src , event ) :
self . _viewer_sync_modifiers ( )
def _viewer_sync_modifiers ( self ) :
serial_has_focus = any ( [ s . has_focus ( ) for s in self . _serial_consoles ] )
viewer_keyboard_grab = ( self . _viewer and
self . _viewer . console_has_keyboard_grab ( ) )
if serial_has_focus or viewer_keyboard_grab :
2015-04-04 01:03:58 +03:00
self . _disable_modifiers ( )
else :
self . _enable_modifiers ( )
2022-01-21 18:37:24 +03:00
def _viewer_auth_error_cb ( self , _src , errmsg , viewer_will_disconnect ) :
2015-04-12 20:04:32 +03:00
errmsg = _ ( " Viewer authentication error: %s " ) % errmsg
self . err . val_err ( errmsg )
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
2020-09-09 22:14:05 +03:00
# _refresh_vm_state if needed)
self . _activate_vm_unavailable_page ( errmsg )
2015-04-12 20:04:32 +03:00
2020-09-09 22:14:05 +03:00
self . _refresh_vm_state ( )
2015-04-12 20:04:32 +03:00
2022-01-21 18:37:24 +03:00
def _viewer_need_auth_cb ( self , _src , withPassword , withUsername ) :
2015-04-04 04:34:03 +03:00
self . _activate_auth_page ( withPassword , withUsername )
2015-04-04 01:03:58 +03:00
2022-01-21 18:37:24 +03:00
def _viewer_agent_connected_cb ( self , _src ) :
2020-09-09 22:14:05 +03:00
# 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 " )
2015-04-04 01:03:58 +03:00
2022-01-21 18:37:24 +03:00
def _viewer_usb_redirect_error_cb ( self , _src , errstr ) :
2020-08-29 20:32:37 +03:00
self . err . show_err (
_ ( " USB redirection error " ) ,
text2 = str ( errstr ) , modal = True ) # pragma: no cover
2015-04-04 01:03:58 +03:00
2016-05-16 23:27:23 +03:00
def _viewer_disconnected_set_page ( self , errdetails , ssherr ) :
2020-08-29 20:32:37 +03:00
if self . vm . is_runable ( ) : # pragma: no cover
2016-05-16 23:16:13 +03:00
# Exit was probably for legitimate reasons
self . _show_vm_status_unavailable ( )
return
msg = _ ( " Viewer was disconnected. " )
2022-01-20 22:14:54 +03:00
errmsg = " "
2016-05-16 23:27:23 +03:00
if errdetails :
2022-01-20 22:14:54 +03:00
errmsg + = " \n " + errdetails
2016-05-16 23:16:13 +03:00
if ssherr :
2019-06-17 04:12:39 +03:00
log . debug ( " SSH tunnel error output: %s " , ssherr )
2022-01-20 22:14:54 +03:00
errmsg + = " \n \n "
errmsg + = _ ( " SSH tunnel error output: %s " ) % ssherr
2015-04-04 01:03:58 +03:00
2022-01-20 22:14:54 +03:00
if errmsg :
self . _activate_gfx_unavailable_page ( msg + errmsg )
return
# If no error message was reported, this isn't a clear graphics
# error that should block reconnecting. So use the top level
# 'VM unavailable' page which makes it easier for the user to
# reconnect.
self . _activate_vm_unavailable_page ( msg )
2016-05-16 23:16:13 +03:00
2022-01-21 18:37:24 +03:00
def _viewer_disconnected_cb ( self , _src , errdetails , ssherr ) :
2022-01-21 18:44:27 +03:00
self . _activate_gfx_unavailable_page ( _ ( " Viewer is disconnecting. " ) )
log . debug ( " Viewer disconnected cb " )
2015-04-04 01:03:58 +03:00
# Make sure modifiers are set correctly
2020-09-09 16:33:58 +03:00
self . _viewer_sync_modifiers ( )
2015-04-04 01:03:58 +03:00
2016-05-16 23:27:23 +03:00
self . _viewer_disconnected_set_page ( errdetails , ssherr )
2015-04-04 01:03:58 +03:00
2022-01-21 18:37:24 +03:00
def _viewer_connected_cb ( self , _src ) :
2022-01-21 18:44:27 +03:00
log . debug ( " Viewer connected cb " )
2020-09-09 22:14:05 +03:00
self . _activate_gfx_viewer_page ( )
2015-04-04 01:03:58 +03:00
# Make sure modifiers are set correctly
2020-09-09 16:33:58 +03:00
self . _viewer_sync_modifiers ( )
2015-04-04 01:03:58 +03:00
def _connect_viewer_signals ( self ) :
2022-01-21 18:37:24 +03:00
self . _viewer . connect ( " add-display-widget " , self . _viewer_add_display_cb )
self . _viewer . connect ( " pointer-grab " , self . _pointer_grabbed_cb )
self . _viewer . connect ( " pointer-ungrab " , self . _pointer_ungrabbed_cb )
2015-04-04 01:03:58 +03:00
self . _viewer . connect ( " size-allocate " , self . _viewer_allocate_cb )
2020-09-09 16:33:58 +03:00
self . _viewer . connect ( " keyboard-grab " , self . _viewer_keyboard_grab_cb )
self . _viewer . connect ( " keyboard-ungrab " , self . _viewer_keyboard_grab_cb )
2022-01-21 18:37:24 +03:00
self . _viewer . connect ( " connected " , self . _viewer_connected_cb )
self . _viewer . connect ( " disconnected " , self . _viewer_disconnected_cb )
self . _viewer . connect ( " auth-error " , self . _viewer_auth_error_cb )
self . _viewer . connect ( " need-auth " , self . _viewer_need_auth_cb )
self . _viewer . connect ( " agent-connected " ,
self . _viewer_agent_connected_cb )
2015-04-04 01:03:58 +03:00
self . _viewer . connect ( " usb-redirect-error " ,
2022-01-21 18:37:24 +03:00
self . _viewer_usb_redirect_error_cb )
2013-09-02 01:40:38 +04:00
2020-09-11 18:32:49 +03:00
##############################
# Console list menu handling #
##############################
2020-09-09 22:14:05 +03:00
2024-03-03 00:22:53 +03:00
def _console_menu_view_selected ( self ) :
name , dev , errmsg = self . _consolemenu . get_selected ( )
is_graphics = hasattr ( dev , " gtype " )
if errmsg or not dev or is_graphics :
2020-09-09 22:14:05 +03:00
self . widget ( " console-pages " ) . set_current_page (
_CONSOLE_PAGE_GRAPHICS )
2024-03-03 00:22:53 +03:00
self . idle_add ( self . _init_viewer , dev , errmsg )
2014-12-03 20:14:32 +03:00
return
2013-09-02 01:40:38 +04:00
2018-03-21 21:42:50 +03:00
target_port = dev . get_xml_idx ( )
2013-09-02 01:40:38 +04:00
serial = None
2015-09-18 18:55:15 +03:00
for s in self . _serial_consoles :
2013-09-02 01:40:38 +04:00
if s . name == name :
serial = s
break
if not serial :
serial = vmmSerialConsole ( self . vm , target_port , name )
2020-09-09 16:33:58 +03:00
serial . set_focus_callbacks ( self . _serial_focus_changed_cb ,
self . _serial_focus_changed_cb )
2013-09-02 01:40:38 +04:00
title = Gtk . Label ( label = name )
2020-08-28 18:14:10 +03:00
self . widget ( " serial-pages " ) . append_page ( serial . get_box ( ) , title )
2015-09-18 18:55:15 +03:00
self . _serial_consoles . append ( serial )
2013-09-02 01:40:38 +04:00
2013-11-10 02:54:47 +04:00
serial . open_console ( )
2015-09-18 18:55:15 +03:00
page_idx = self . _serial_consoles . index ( serial )
self . widget ( " console-pages " ) . set_current_page ( _CONSOLE_PAGE_SERIAL )
self . widget ( " serial-pages " ) . set_current_page ( page_idx )
2013-09-02 01:40:38 +04:00
2024-03-03 00:22:53 +03:00
def _populate_console_menu ( self ) :
self . _consolemenu . rebuild_menu ( self . vm )
2020-09-09 22:14:05 +03:00
2020-09-11 18:32:49 +03:00
def _toggle_first_console_menu_item ( self ) :
# We iterate through the 'console' menu and activate the first
# valid entry... hacky but it works
2024-03-03 00:22:53 +03:00
self . _populate_console_menu ( )
found = self . _consolemenu . activate_default ( )
2020-09-11 18:32:49 +03:00
if not found :
# Calling this with dev=None will trigger _init_viewer
# which shows some meaningful errors
2024-03-03 00:22:53 +03:00
self . _console_menu_view_selected ( )
2020-09-11 18:32:49 +03:00
def _activate_default_console_page ( self ) :
if self . vm . is_runable ( ) :
self . _show_vm_status_unavailable ( )
return
viewer_initialized = ( self . _viewer and self . _viewer . console_is_open ( ) )
if viewer_initialized :
return
cpage = self . widget ( " console-pages " ) . get_current_page ( )
if cpage != _CONSOLE_PAGE_UNAVAILABLE :
return
# If we are in this condition it should mean the VM was
# just started, so connect to the default page
self . _toggle_first_console_menu_item ( )
2024-03-03 00:22:53 +03:00
def _on_console_menu_toggled_cb ( self , src ) :
self . _console_menu_view_selected ( )
def _on_console_menu_show_cb ( self , src ) :
self . _populate_console_menu ( )
2020-09-09 22:14:05 +03:00
################
# UI listeners #
################
def _auth_login_cb ( self , src ) :
self . _set_credentials ( )
2013-09-02 01:40:38 +04:00
2020-09-10 02:04:44 +03:00
def _connect_button_clicked_cb ( self , src ) :
self . _viewer_connect_clicked = True
2024-03-03 00:22:53 +03:00
self . _console_menu_view_selected ( )
2020-09-10 02:04:44 +03:00
2020-09-09 22:14:05 +03:00
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 " )
2015-04-04 04:34:03 +03:00
2019-05-05 23:25:35 +03:00
###########################
# API used by vmmVMWindow #
###########################
2015-04-04 04:34:03 +03:00
2020-09-09 22:14:05 +03:00
def vmwindow_viewer_has_usb_redirection ( self ) :
2015-04-04 04:34:03 +03:00
return bool ( self . _viewer and
self . _viewer . console_has_usb_redirection ( ) )
2020-09-09 22:14:05 +03:00
def vmwindow_viewer_get_usb_widget ( self ) :
2015-04-04 04:34:03 +03:00
return self . _viewer . console_get_usb_widget ( )
2020-09-09 22:14:05 +03:00
def vmwindow_viewer_get_pixbuf ( self ) :
2015-04-04 04:34:03 +03:00
return self . _viewer . console_get_pixbuf ( )
2020-09-21 04:55:27 +03:00
def vmwindow_close ( self ) :
2020-09-09 22:14:05 +03:00
return self . _activate_vm_unavailable_page (
2022-01-21 18:44:27 +03:00
_ ( " Viewer window closed. " ) )
2020-09-21 05:53:30 +03:00
def vmwindow_get_title_message ( self ) :
if self . _pointer_is_grabbed and self . _viewer :
keystr = self . _viewer . console_get_grab_keys ( )
return _ ( " Press %s to release pointer. " ) % keystr
2015-04-04 04:34:03 +03:00
2020-09-09 22:14:05 +03:00
def vmwindow_activate_default_console_page ( self ) :
2015-04-04 04:34:03 +03:00
return self . _activate_default_console_page ( )
2020-09-09 22:14:05 +03:00
def vmwindow_refresh_vm_state ( self ) :
return self . _refresh_vm_state ( )
2015-04-04 04:34:03 +03:00
2020-09-09 22:14:05 +03:00
def vmwindow_set_size_to_vm ( self ) :
return self . _set_size_to_vm ( )
def vmwindow_set_fullscreen ( self , do_fullscreen ) :
2015-04-04 04:34:03 +03:00
self . _change_fullscreen ( do_fullscreen )
2020-09-09 22:14:05 +03:00
def vmwindow_get_keycombo_menu ( self ) :
return self . _keycombo_menu
def vmwindow_get_console_list_menu ( self ) :
2024-03-03 00:22:53 +03:00
return self . _consolemenu . get_menu ( )
2020-09-09 22:14:05 +03:00
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 ( )