@@ -281,7 +281,7 @@ async def perform_action(self):
281281class LogItem (ActionMenuItem ):
282282 def __init__ (self , name , path ):
283283 img = Gtk .Image .new_from_file (
284- "/usr/share/icons/HighContrast/16x16 /apps/logviewer.png"
284+ "/usr/share/icons/HighContrast/48x48 /apps/logviewer.png"
285285 )
286286 super ().__init__ (label = name , img = img )
287287 self .path = path
@@ -316,6 +316,26 @@ async def perform_action(self):
316316 )
317317
318318
319+ class RunDebugConsoleItem (VMActionMenuItem ):
320+ """Run Debug Console menu Item. When activated runs a qvm-console-dispvm."""
321+
322+ def __init__ (self , vm , icon_cache ):
323+ img = Gtk .Image .new_from_file (
324+ "/usr/share/icons/HighContrast/48x48/apps/logviewer.png"
325+ )
326+ super ().__init__ (
327+ vm ,
328+ label = _ ("Debug Console" ),
329+ img = img ,
330+ )
331+
332+ async def perform_action (self ):
333+ # pylint: disable=consider-using-with
334+ await asyncio .create_subprocess_exec (
335+ "qvm-console-dispvm" , self .vm .name , stderr = subprocess .PIPE
336+ )
337+
338+
319339class OpenFileManagerItem (VMActionMenuItem ):
320340 """Attempts to open a file manager in the VM. If fails, displays an
321341 error message."""
@@ -368,6 +388,11 @@ def __init__(self, vm, app, icon_cache):
368388
369389 self .add (OpenFileManagerItem (self .vm , icon_cache ))
370390 self .add (RunTerminalItem (self .vm , icon_cache ))
391+
392+ # Debug console for developers, troubleshooting, headless qubes
393+ self .debug_console = RunDebugConsoleItem (self .vm , icon_cache )
394+ self .add (self .debug_console )
395+
371396 self .add (PreferencesItem (self .vm , icon_cache ))
372397 self .add (PauseItem (self .vm , icon_cache ))
373398 self .add (ShutdownItem (self .vm , icon_cache ))
@@ -377,6 +402,24 @@ def __init__(self, vm, app, icon_cache):
377402 self .set_reserve_toggle_size (False )
378403 self .show_all ()
379404
405+ def show_all (self , * _args , ** _kwargs ):
406+ super ().show_all (self , * _args , ** _kwargs )
407+ self .debug_console_update ()
408+
409+ def debug_console_update (self , * _args , ** _kwargs ):
410+ # Debug console is shown only if debug property is set, no GUIVM is set
411+ # ... or with `wizard-mode` feature per qube or per entire GUIVM.
412+ if (
413+ self .app .wizard_mode
414+ or getattr (self .vm , "debug" )
415+ or not getattr (self .vm , "guivm" )
416+ or bool (self .vm .features .get ("wizard-mode" , False ))
417+ or bool (self .vm .features .get ("gui" , False ))
418+ ):
419+ self .debug_console .show ()
420+ else :
421+ self .debug_console .hide ()
422+
380423
381424class PausedMenu (Gtk .Menu ):
382425 """The sub-menu for a paused domain"""
@@ -668,6 +711,11 @@ def __init__(self, app_name, qapp, dispatcher, stats_dispatcher):
668711 self .set_application_id (app_name )
669712 self .register () # register Gtk Application
670713
714+ # to display debug console for all qubes
715+ self .wizard_mode = self .qapp .domains [self .qapp .local_name ].features .get (
716+ "wizard-mode" , False
717+ )
718+
671719 def register_events (self ):
672720 self .dispatcher .add_handler ("connection-established" , self .refresh_all )
673721 self .dispatcher .add_handler ("domain-pre-start" , self .update_domain_item )
@@ -717,6 +765,34 @@ def register_events(self):
717765
718766 self .stats_dispatcher .add_handler ("vm-stats" , self .update_stats )
719767
768+ self .dispatcher .add_handler ("property-set:debug" , self .debug_change )
769+ self .dispatcher .add_handler ("property-set:guivm" , self .debug_change )
770+ self .dispatcher .add_handler (
771+ "domain-feature-set:gui" , self .debug_change
772+ )
773+ self .dispatcher .add_handler (
774+ "domain-feature-delete:gui" , self .debug_change
775+ )
776+ self .dispatcher .add_handler (
777+ "domain-feature-set:wizard-mode" , self .debug_change
778+ )
779+ self .dispatcher .add_handler (
780+ "domain-feature-delete:wizard-mode" , self .debug_change
781+ )
782+
783+ def debug_change (self , vm , * _args , ** _kwargs ):
784+ if vm == self .qapp .local_name :
785+ self .wizard_mode = self .qapp .domains [
786+ self .qapp .local_name
787+ ].features .get ("wizard-mode" , False )
788+ vms = self .menu_items
789+ else :
790+ vms = {vm }
791+ for menu in vms :
792+ submenu = self .menu_items [menu ].get_submenu ()
793+ if isinstance (submenu , StartedMenu ):
794+ submenu .debug_console_update ()
795+
720796 def show_menu (self , _unused , event ):
721797 self .tray_menu .popup_at_pointer (event ) # None means current event
722798
@@ -1069,6 +1145,21 @@ def _disconnect_signals(self, _event):
10691145
10701146 self .stats_dispatcher .remove_handler ("vm-stats" , self .update_stats )
10711147
1148+ self .dispatcher .remove_handler ("property-set:debug" , self .debug_change )
1149+ self .dispatcher .remove_handler ("property-set:guivm" , self .debug_change )
1150+ self .dispatcher .remove_handler (
1151+ "domain-feature-set:gui" , self .debug_change
1152+ )
1153+ self .dispatcher .remove_handler (
1154+ "domain-feature-delete:gui" , self .debug_change
1155+ )
1156+ self .dispatcher .remove_handler (
1157+ "domain-feature-set:wizard-mode" , self .debug_change
1158+ )
1159+ self .dispatcher .remove_handler (
1160+ "domain-feature-delete:wizard-mode" , self .debug_change
1161+ )
1162+
10721163
10731164def main ():
10741165 """main function"""
0 commit comments