@@ -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/16x16/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 ):
406+ super ().do_show_all (self )
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,32 @@ 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 ("domain-feature-set:gui" , self .debug_change )
771+ self .dispatcher .add_handler (
772+ "domain-feature-delete:gui" , self .debug_change
773+ )
774+ self .dispatcher .add_handler (
775+ "domain-feature-set:wizard-mode" , self .debug_change
776+ )
777+ self .dispatcher .add_handler (
778+ "domain-feature-delete:wizard-mode" , self .debug_change
779+ )
780+
781+ def debug_change (self , vm , * _args , ** _kwargs ):
782+ if vm == self .qapp .local_name :
783+ self .wizard_mode = self .qapp .domains [
784+ self .qapp .local_name
785+ ].features .get ("wizard-mode" , False )
786+ vms = self .menu_items
787+ else :
788+ vms = {vm }
789+ for menu in vms :
790+ submenu = self .menu_items [menu ].get_submenu ()
791+ if isinstance (submenu , StartedMenu ):
792+ submenu .debug_console_update ()
793+
720794 def show_menu (self , _unused , event ):
721795 self .tray_menu .popup_at_pointer (event ) # None means current event
722796
@@ -1069,6 +1143,21 @@ def _disconnect_signals(self, _event):
10691143
10701144 self .stats_dispatcher .remove_handler ("vm-stats" , self .update_stats )
10711145
1146+ self .dispatcher .remove_handler ("property-set:debug" , self .debug_change )
1147+ self .dispatcher .remove_handler ("property-set:guivm" , self .debug_change )
1148+ self .dispatcher .remove_handler (
1149+ "domain-feature-set:gui" , self .debug_change
1150+ )
1151+ self .dispatcher .remove_handler (
1152+ "domain-feature-delete:gui" , self .debug_change
1153+ )
1154+ self .dispatcher .remove_handler (
1155+ "domain-feature-set:wizard-mode" , self .debug_change
1156+ )
1157+ self .dispatcher .remove_handler (
1158+ "domain-feature-delete:wizard-mode" , self .debug_change
1159+ )
1160+
10721161
10731162def main ():
10741163 """main function"""
0 commit comments