@@ -338,6 +338,34 @@ async def perform_action(self):
338338 )
339339
340340
341+ class RunDebugConsoleItem (VMActionMenuItem ):
342+ """Run Debug Console menu Item. When activated runs a qvm-console-dispvm."""
343+
344+ def __init__ (self , vm , icon_cache ):
345+ img = Gtk .Image .new_from_file (
346+ "/usr/share/icons/HighContrast/16x16/apps/logviewer.png"
347+ )
348+ super ().__init__ (
349+ vm ,
350+ label = _ ("Debug Console" ),
351+ img = img ,
352+ )
353+ self .visible = False
354+ self .connect ("show" , self .on_show_event )
355+
356+ def on_show_event (self , widget ):
357+ if self .visible :
358+ widget .show ()
359+ else :
360+ widget .hide ()
361+
362+ async def perform_action (self ):
363+ # pylint: disable=consider-using-with
364+ await asyncio .create_subprocess_exec (
365+ "qvm-console-dispvm" , self .vm .name , stderr = subprocess .PIPE
366+ )
367+
368+
341369class OpenFileManagerItem (VMActionMenuItem ):
342370 """Attempts to open a file manager in the VM. If fails, displays an
343371 error message."""
@@ -392,15 +420,37 @@ def __init__(self, vm, app, icon_cache):
392420 self .add (
393421 RunTerminalItem (self .vm , icon_cache , as_root = app .terminal_as_root )
394422 )
423+
424+ # Debug console for developers, troubleshooting, headless qubes
425+ self .debug_console = RunDebugConsoleItem (self .vm , icon_cache )
426+ self .add (self .debug_console )
427+
395428 self .add (PreferencesItem (self .vm , icon_cache ))
396429 self .add (PauseItem (self .vm , icon_cache ))
397430 self .add (ShutdownItem (self .vm , icon_cache ))
398431 if self .vm .klass != "DispVM" or not self .vm .auto_cleanup :
399432 self .add (RestartItem (self .vm , icon_cache ))
400433
401434 self .set_reserve_toggle_size (False )
435+ self .debug_console_update ()
402436 self .show_all ()
403437
438+ def debug_console_update (self , * _args , ** _kwargs ):
439+ # Debug console is shown only if debug property is set, no GUIVM is set
440+ # ... or with `wizard-mode` feature per qube or per entire GUIVM.
441+ if (
442+ self .app .wizard_mode
443+ or getattr (self .vm , "debug" )
444+ or not getattr (self .vm , "guivm" )
445+ or not self .vm .features .check_with_template ("gui" , False )
446+ or self .vm .features .get ("wizard-mode" , False )
447+ ):
448+ self .debug_console .visible = True
449+ self .debug_console .show ()
450+ else :
451+ self .debug_console .visible = False
452+ self .debug_console .hide ()
453+
404454
405455class PausedMenu (Gtk .Menu ):
406456 """The sub-menu for a paused domain"""
@@ -696,6 +746,11 @@ def __init__(self, app_name, qapp, dispatcher, stats_dispatcher):
696746 self .set_application_id (app_name )
697747 self .register () # register Gtk Application
698748
749+ # to display debug console for all qubes
750+ self .wizard_mode = self .qapp .domains [self .qapp .local_name ].features .get (
751+ "wizard-mode" , False
752+ )
753+
699754 def register_events (self ):
700755 self .dispatcher .add_handler ("connection-established" , self .refresh_all )
701756 self .dispatcher .add_handler ("domain-pre-start" , self .update_domain_item )
@@ -743,8 +798,34 @@ def register_events(self):
743798 self .dispatcher .add_handler ("property-set:netvm" , self .property_change )
744799 self .dispatcher .add_handler ("property-set:label" , self .property_change )
745800
801+ self .dispatcher .add_handler ("property-set:debug" , self .debug_change )
802+ self .dispatcher .add_handler ("property-set:guivm" , self .debug_change )
803+ self .dispatcher .add_handler ("domain-feature-set:gui" , self .debug_change )
804+ self .dispatcher .add_handler (
805+ "domain-feature-delete:gui" , self .debug_change
806+ )
807+ self .dispatcher .add_handler (
808+ "domain-feature-set:wizard-mode" , self .debug_change
809+ )
810+ self .dispatcher .add_handler (
811+ "domain-feature-delete:wizard-mode" , self .debug_change
812+ )
813+
746814 self .stats_dispatcher .add_handler ("vm-stats" , self .update_stats )
747815
816+ def debug_change (self , vm , * _args , ** _kwargs ):
817+ if vm == self .qapp .local_name :
818+ self .wizard_mode = self .qapp .domains [
819+ self .qapp .local_name
820+ ].features .get ("wizard-mode" , False )
821+ vms = self .menu_items
822+ else :
823+ vms = {vm }
824+ for menu in vms :
825+ submenu = self .menu_items [menu ].get_submenu ()
826+ if isinstance (submenu , StartedMenu ):
827+ submenu .debug_console_update ()
828+
748829 def show_menu (self , _unused , event ):
749830 self .terminal_as_root = False
750831 self .tray_menu .popup_at_pointer (event ) # None means current event
@@ -1096,6 +1177,21 @@ def _disconnect_signals(self, _event):
10961177 "property-set:label" , self .property_change
10971178 )
10981179
1180+ self .dispatcher .remove_handler ("property-set:debug" , self .debug_change )
1181+ self .dispatcher .remove_handler ("property-set:guivm" , self .debug_change )
1182+ self .dispatcher .remove_handler (
1183+ "domain-feature-set:gui" , self .debug_change
1184+ )
1185+ self .dispatcher .remove_handler (
1186+ "domain-feature-delete:gui" , self .debug_change
1187+ )
1188+ self .dispatcher .remove_handler (
1189+ "domain-feature-set:wizard-mode" , self .debug_change
1190+ )
1191+ self .dispatcher .remove_handler (
1192+ "domain-feature-delete:wizard-mode" , self .debug_change
1193+ )
1194+
10991195 self .stats_dispatcher .remove_handler ("vm-stats" , self .update_stats )
11001196
11011197 @property
0 commit comments