Skip to content

Commit 398be69

Browse files
committed
Skip confusing buttons from dom0 and disp template
- Dom0: hide shutdown and pause, technically, shutdown can be used by GUIVMs in the future if the action was modified, currently, qubesd logs a failure. - Disposables templates have the "Start qube" button in the "TEMPLATES" tab and the "Search" page, but not in the "APPS" tab. Fixes: QubesOS/qubes-issues#10288 For: QubesOS/qubes-issues#1512
1 parent 4d858cd commit 398be69

File tree

5 files changed

+130
-50
lines changed

5 files changed

+130
-50
lines changed

qubes_menu/app_widgets.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545

4646
logger = logging.getLogger("qubes-appmenu")
4747

48-
DISP_TEXT = "new Disposable Qube from "
48+
DISP_TEXT = "New disposable qube from "
4949

5050

5151
class AppEntry(Gtk.ListBoxRow):

qubes_menu/application_page.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,9 @@ def _selection_changed(self, _widget, row: Optional[VMRow]):
342342
self.selected_vm_entry = row
343343
self._set_right_visibility(True)
344344
self.network_indicator.set_network_state(row.vm_entry.has_network)
345-
self.control_list.update_visibility(row.vm_entry.power_state)
345+
self.control_list.update_visibility(
346+
row.vm_entry, self.toggle_buttons.apps_toggle.get_active()
347+
)
346348
self.control_list.unselect_all()
347349
self.app_list.ephemeral_vm = bool(self.selected_vm_entry.vm_entry.parent_vm)
348350
self.app_list.invalidate_filter()

qubes_menu/custom_widgets.py

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -269,8 +269,9 @@ def get_appinfo(self) -> ApplicationInfo:
269269
vm_entry.settings_desktop_file_name
270270
)
271271

272-
def update_state(self, state): # pylint: disable=unused-argument
272+
def update_state(self, vm_entry: VMEntry, apps_tab: bool = False):
273273
"""Update state: should be always visible."""
274+
# pylint: disable=unused-argument
274275
self.show_all()
275276

276277
def show_menu(self, _widget, event):
@@ -449,7 +450,7 @@ def __init__(self):
449450
self.show_all()
450451
self.command = None
451452

452-
def update_state(self, state):
453+
def update_state(self, vm_entry: VMEntry):
453454
"""
454455
Update own state (visibility/text/sensitivity) based on provided VM
455456
state.
@@ -497,35 +498,49 @@ def show_menu(self, _widget, event):
497498
if event.button == 3:
498499
self.menu.popup_at_pointer(None) # None means current event
499500

500-
def update_state(self, state):
501+
def update_state(self, vm_entry: VMEntry, apps_tab: bool = False):
501502
"""
502503
Update own state (visibility/text/sensitivity) based on provided VM
503504
state.
504505
"""
505-
self.state = state
506-
if state == "Running":
507-
self.row_label.set_label("Shutdown qube")
508-
self.command = "qvm-shutdown"
506+
vm_name = vm_entry.vm_name
507+
is_dispvm_template = vm_entry.is_dispvm_template
508+
self.state = vm_entry.power_state
509+
510+
if (
511+
vm_name == "dom0"
512+
or (apps_tab and is_dispvm_template and self.state != "Running")
513+
):
514+
self.row_label.set_label(" ")
515+
self.set_sensitive(False)
516+
self.command = None
517+
self.icon.hide()
518+
return
519+
520+
self.set_sensitive(True)
521+
self.icon.show()
522+
if self.state == "Halted" and not (is_dispvm_template and apps_tab):
523+
self.row_label.set_label("Start qube")
524+
self.command = "qvm-start"
509525
self.icon.set_from_pixbuf(
510-
load_icon("qappmenu-shutdown", size=None, pixel_size=15)
526+
load_icon("qappmenu-start", size=None, pixel_size=15)
511527
)
512528
return
513-
if state == "Transient":
529+
if self.state == "Transient":
514530
self.row_label.set_label("Kill qube")
515531
self.command = "qvm-kill"
516532
self.icon.set_from_pixbuf(
517533
load_icon("qappmenu-shutdown", size=None, pixel_size=15)
518534
)
519535
return
520-
if state == "Halted":
521-
self.row_label.set_label("Start qube")
522-
self.command = "qvm-start"
536+
if self.state == "Running":
537+
self.row_label.set_label("Shutdown qube")
538+
self.command = "qvm-shutdown"
523539
self.icon.set_from_pixbuf(
524-
load_icon("qappmenu-start", size=None, pixel_size=15)
540+
load_icon("qappmenu-shutdown", size=None, pixel_size=15)
525541
)
526-
527542
return
528-
if state == "Paused":
543+
if self.state == "Paused":
529544
self.row_label.set_label("Unpause qube")
530545
self.command = "qvm-unpause"
531546
self.icon.set_from_pixbuf(
@@ -544,13 +559,16 @@ def __init__(self):
544559
self.icon.set_from_pixbuf(load_icon("qappmenu-pause", size=None, pixel_size=15))
545560
self.state = None
546561

547-
def update_state(self, state):
562+
def update_state(self, vm_entry: VMEntry, apps_tab: bool = False):
548563
"""
549564
Update own state (visibility/text/sensitivity) based on provided VM
550565
state.
551566
"""
552-
self.state = state
553-
if state == "Running":
567+
# pylint: disable=unused-argument
568+
vm_name = vm_entry.vm_name
569+
self.state = vm_entry.power_state
570+
571+
if self.state == "Running" and vm_name != "dom0":
554572
self.row_label.set_label("Pause qube")
555573
self.set_sensitive(True)
556574
self.command = "qvm-pause"
@@ -581,12 +599,12 @@ def __init__(self, app_page):
581599
self.add(self.start_item)
582600
self.add(self.pause_item)
583601

584-
def update_visibility(self, state):
602+
def update_visibility(self, vm_entry: VMEntry, apps_tab: bool = False):
585603
"""
586604
Update children's state based on provided VM state.
587605
"""
588606
for row in self.get_children():
589-
row.update_state(state)
607+
row.update_state(vm_entry, apps_tab)
590608

591609

592610
class KeynavController:

qubes_menu/search_page.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,7 @@ def _selection_changed(self, _widget, row: Optional[SearchVMRow]):
511511
else:
512512
self.selected_vm_row = row
513513
self.control_list.show()
514-
self.control_list.update_visibility(row.vm_entry.power_state)
514+
self.control_list.update_visibility(row.vm_entry, apps_tab=False)
515515
self.control_list.unselect_all()
516516
self.app_list.invalidate_filter()
517517
self.app_list.select_row(None)

qubes_menu/tests/test_app_page.py

Lines changed: 87 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -30,43 +30,101 @@ def test_app_page_vm_state(test_desktop_file_path, test_qapp, test_builder):
3030
dispatcher = MockDispatcher(test_qapp)
3131
vm_manager = VMManager(test_qapp, dispatcher)
3232

33-
with mock.patch.object(DesktopFileManager, 'desktop_dirs',
34-
[test_desktop_file_path]):
33+
with mock.patch.object(
34+
DesktopFileManager, "desktop_dirs", [test_desktop_file_path]
35+
):
3536
desktop_file_manager = DesktopFileManager(test_qapp)
3637

3738
app_page = AppPage(vm_manager, test_builder, desktop_file_manager)
3839

39-
# select a turned off vm
40-
app_page.vm_list.select_row([row for row in app_page.vm_list.get_children()
41-
if row.vm_name == 'test-red'][0])
40+
# select dom0
41+
app_page.vm_list.select_row(
42+
[
43+
row
44+
for row in app_page.vm_list.get_children()
45+
if row.vm_name == "dom0"
46+
][0]
47+
)
48+
assert app_page.control_list.start_item.row_label.get_label() == " "
49+
assert app_page.control_list.pause_item.row_label.get_label() == " "
4250

43-
assert app_page.control_list.start_item.row_label.get_label() == \
44-
"Start qube"
45-
assert app_page.control_list.pause_item.row_label.get_label() == \
46-
" "
51+
# select a turned off vm
52+
app_page.vm_list.select_row(
53+
[
54+
row
55+
for row in app_page.vm_list.get_children()
56+
if row.vm_name == "test-red"
57+
][0]
58+
)
59+
60+
assert (
61+
app_page.control_list.start_item.row_label.get_label() == "Start qube"
62+
)
63+
assert app_page.control_list.pause_item.row_label.get_label() == " "
4764

4865
# select a turned on vm
49-
app_page.vm_list.select_row([row for row in app_page.vm_list.get_children()
50-
if row.vm_name == 'sys-usb'][0])
51-
52-
assert app_page.control_list.start_item.row_label.get_label() == \
53-
"Shutdown qube"
54-
assert app_page.control_list.pause_item.row_label.get_label() == \
55-
"Pause qube"
66+
app_page.vm_list.select_row(
67+
[
68+
row
69+
for row in app_page.vm_list.get_children()
70+
if row.vm_name == "sys-usb"
71+
][0]
72+
)
73+
74+
assert (
75+
app_page.control_list.start_item.row_label.get_label()
76+
== "Shutdown qube"
77+
)
78+
assert (
79+
app_page.control_list.pause_item.row_label.get_label() == "Pause qube"
80+
)
81+
82+
# select a turned off disposable template
83+
app_page.vm_list.select_row(
84+
[
85+
row
86+
for row in app_page.vm_list.get_children()
87+
if row.vm_name == "test-alt-dvm"
88+
][0]
89+
)
90+
assert app_page.control_list.start_item.row_label.get_label() == " "
91+
assert app_page.control_list.pause_item.row_label.get_label() == " "
92+
93+
# select a turned on disposable template
94+
app_page.vm_list.select_row(
95+
[
96+
row
97+
for row in app_page.vm_list.get_children()
98+
if row.vm_name == "test-alt-dvm-running"
99+
][0]
100+
)
101+
assert (
102+
app_page.control_list.start_item.row_label.get_label()
103+
== "Shutdown qube"
104+
)
105+
assert (
106+
app_page.control_list.pause_item.row_label.get_label() == "Pause qube"
107+
)
56108

57109

58110
def test_dispvm_parent_sorting(test_desktop_file_path, test_qapp, test_builder):
59111
# check if dispvm child is sorted after the parent
60-
test_qapp._qubes['disp1233'] = MockQube(
61-
name="disp1233", qapp=test_qapp, klass='DispVM',
62-
template_for_dispvms='True', template='default-dvm', auto_cleanup=True)
112+
test_qapp._qubes["disp1233"] = MockQube(
113+
name="disp1233",
114+
qapp=test_qapp,
115+
klass="DispVM",
116+
template_for_dispvms="True",
117+
template="default-dvm",
118+
auto_cleanup=True,
119+
)
63120
test_qapp.update_vm_calls()
64121

65122
dispatcher = MockDispatcher(test_qapp)
66123
vm_manager = VMManager(test_qapp, dispatcher)
67124

68-
with mock.patch.object(DesktopFileManager, 'desktop_dirs',
69-
[test_desktop_file_path]):
125+
with mock.patch.object(
126+
DesktopFileManager, "desktop_dirs", [test_desktop_file_path]
127+
):
70128
desktop_file_manager = DesktopFileManager(test_qapp)
71129

72130
app_page = AppPage(vm_manager, test_builder, desktop_file_manager)
@@ -75,11 +133,11 @@ def test_dispvm_parent_sorting(test_desktop_file_path, test_qapp, test_builder):
75133

76134
for row in app_page.vm_list.get_children():
77135
if found_dvm:
78-
if row.vm_name == 'disp1233' and row.vm_entry.parent_vm:
136+
if row.vm_name == "disp1233" and row.vm_entry.parent_vm:
79137
break
80138
found_dvm = False
81139
continue
82-
if row.vm_name == 'default-dvm' and row.vm_entry._is_dispvm_template:
140+
if row.vm_entry.is_dispvm_template:
83141
found_dvm = True
84142
continue
85143
found_dvm = False
@@ -92,12 +150,14 @@ def test_settings_app_page(test_desktop_file_path, test_qapp, test_builder):
92150
dispatcher = MockDispatcher(test_qapp)
93151
vm_manager = VMManager(test_qapp, dispatcher)
94152

95-
with mock.patch.object(DesktopFileManager, 'desktop_dirs',
96-
[test_desktop_file_path]):
153+
with mock.patch.object(
154+
DesktopFileManager, "desktop_dirs", [test_desktop_file_path]
155+
):
97156
desktop_file_manager = DesktopFileManager(test_qapp)
98157

99-
settings_page = SettingsPage(test_qapp, test_builder,
100-
desktop_file_manager, dispatcher)
158+
settings_page = SettingsPage(
159+
test_qapp, test_builder, desktop_file_manager, dispatcher
160+
)
101161

102162
for row in settings_page.app_list.get_children():
103163
assert not row.app_info.vm

0 commit comments

Comments
 (0)