@@ -316,6 +316,34 @@ 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+ self .visible = False
332+ self .connect ("show" , self .on_show_event )
333+
334+ def on_show_event (self , widget ):
335+ if self .visible :
336+ widget .show ()
337+ else :
338+ widget .hide ()
339+
340+ async def perform_action (self ):
341+ # pylint: disable=consider-using-with
342+ await asyncio .create_subprocess_exec (
343+ "qvm-console-dispvm" , self .vm .name , stderr = subprocess .PIPE
344+ )
345+
346+
319347class OpenFileManagerItem (VMActionMenuItem ):
320348 """Attempts to open a file manager in the VM. If fails, displays an
321349 error message."""
@@ -368,15 +396,35 @@ def __init__(self, vm, app, icon_cache):
368396
369397 self .add (OpenFileManagerItem (self .vm , icon_cache ))
370398 self .add (RunTerminalItem (self .vm , icon_cache ))
399+
400+ # Debug console for developers, troubleshooting, headless qubes
401+ self .debug_console = RunDebugConsoleItem (self .vm , icon_cache )
402+ self .add (self .debug_console )
403+
371404 self .add (PreferencesItem (self .vm , icon_cache ))
372405 self .add (PauseItem (self .vm , icon_cache ))
373406 self .add (ShutdownItem (self .vm , icon_cache ))
374407 if self .vm .klass != "DispVM" or not self .vm .auto_cleanup :
375408 self .add (RestartItem (self .vm , icon_cache ))
376409
377410 self .set_reserve_toggle_size (False )
411+ self .debug_console_update ()
378412 self .show_all ()
379413
414+ def debug_console_update (self , * _args , ** _kwargs ):
415+ # Debug console is shown only if debug property is set, no GUIVM is set
416+ # ... or with `wizard-mode` feature per qube or per entire GUIVM.
417+ if (
418+ self .app .wizard_mode
419+ or getattr (self .vm , "debug" )
420+ or not getattr (self .vm , "guivm" )
421+ or bool (self .vm .features .get ("wizard-mode" , False ))
422+ or bool (self .vm .features .get ("gui" , False ))
423+ ):
424+ self .debug_console .visible = True
425+ else :
426+ self .debug_console .visible = False
427+
380428
381429class PausedMenu (Gtk .Menu ):
382430 """The sub-menu for a paused domain"""
@@ -668,6 +716,11 @@ def __init__(self, app_name, qapp, dispatcher, stats_dispatcher):
668716 self .set_application_id (app_name )
669717 self .register () # register Gtk Application
670718
719+ # to display debug console for all qubes
720+ self .wizard_mode = self .qapp .domains [self .qapp .local_name ].features .get (
721+ "wizard-mode" , False
722+ )
723+
671724 def register_events (self ):
672725 self .dispatcher .add_handler ("connection-established" , self .refresh_all )
673726 self .dispatcher .add_handler ("domain-pre-start" , self .update_domain_item )
@@ -717,6 +770,32 @@ def register_events(self):
717770
718771 self .stats_dispatcher .add_handler ("vm-stats" , self .update_stats )
719772
773+ self .dispatcher .add_handler ("property-set:debug" , self .debug_change )
774+ self .dispatcher .add_handler ("property-set:guivm" , self .debug_change )
775+ self .dispatcher .add_handler ("domain-feature-set:gui" , self .debug_change )
776+ self .dispatcher .add_handler (
777+ "domain-feature-delete:gui" , self .debug_change
778+ )
779+ self .dispatcher .add_handler (
780+ "domain-feature-set:wizard-mode" , self .debug_change
781+ )
782+ self .dispatcher .add_handler (
783+ "domain-feature-delete:wizard-mode" , self .debug_change
784+ )
785+
786+ def debug_change (self , vm , * _args , ** _kwargs ):
787+ if vm == self .qapp .local_name :
788+ self .wizard_mode = self .qapp .domains [
789+ self .qapp .local_name
790+ ].features .get ("wizard-mode" , False )
791+ vms = self .menu_items
792+ else :
793+ vms = {vm }
794+ for menu in vms :
795+ submenu = self .menu_items [menu ].get_submenu ()
796+ if isinstance (submenu , StartedMenu ):
797+ submenu .debug_console_update ()
798+
720799 def show_menu (self , _unused , event ):
721800 self .tray_menu .popup_at_pointer (event ) # None means current event
722801
@@ -1069,6 +1148,21 @@ def _disconnect_signals(self, _event):
10691148
10701149 self .stats_dispatcher .remove_handler ("vm-stats" , self .update_stats )
10711150
1151+ self .dispatcher .remove_handler ("property-set:debug" , self .debug_change )
1152+ self .dispatcher .remove_handler ("property-set:guivm" , self .debug_change )
1153+ self .dispatcher .remove_handler (
1154+ "domain-feature-set:gui" , self .debug_change
1155+ )
1156+ self .dispatcher .remove_handler (
1157+ "domain-feature-delete:gui" , self .debug_change
1158+ )
1159+ self .dispatcher .remove_handler (
1160+ "domain-feature-set:wizard-mode" , self .debug_change
1161+ )
1162+ self .dispatcher .remove_handler (
1163+ "domain-feature-delete:wizard-mode" , self .debug_change
1164+ )
1165+
10721166
10731167def main ():
10741168 """main function"""
0 commit comments