From f34bf0d19e2116e592ebae86a25fe3395b43c467 Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Tue, 16 Apr 2024 17:25:01 +0200 Subject: [PATCH] sculpt: power options (suspend, reset, power off) This patch enhances the sculpt manager to drive the system state and manage the lifecycle of driver components during suspend- resume cycles. The new Power options can be found in the System menu. The suspend and power-off controls are presented only when the acpi-support option is activated. Note that the USB controller is hard restarted when resuming from suspend. Hence, all components that depend on USB are restarted implicitely. Issue #5180 Issue #5174 --- repos/gems/sculpt/gpu_drv/intel | 2 +- .../gems/src/app/sculpt_manager/driver/ahci.h | 1 + repos/gems/src/app/sculpt_manager/driver/fb.h | 10 +- .../gems/src/app/sculpt_manager/driver/nic.h | 7 +- .../gems/src/app/sculpt_manager/driver/nvme.h | 1 + .../gems/src/app/sculpt_manager/driver/ps2.h | 7 +- .../gems/src/app/sculpt_manager/driver/usb.h | 8 +- .../gems/src/app/sculpt_manager/driver/wifi.h | 7 +- repos/gems/src/app/sculpt_manager/drivers.cc | 13 ++ repos/gems/src/app/sculpt_manager/drivers.h | 6 + repos/gems/src/app/sculpt_manager/main.cc | 110 +++++++++++- .../src/app/sculpt_manager/model/board_info.h | 126 ++++++++++---- .../app/sculpt_manager/model/system_state.h | 94 ++++++++++ .../app/sculpt_manager/view/system_dialog.h | 59 ++++--- .../sculpt_manager/view/system_power_widget.h | 162 ++++++++++++++++++ 15 files changed, 537 insertions(+), 76 deletions(-) create mode 100644 repos/gems/src/app/sculpt_manager/model/system_state.h create mode 100644 repos/gems/src/app/sculpt_manager/view/system_power_widget.h diff --git a/repos/gems/sculpt/gpu_drv/intel b/repos/gems/sculpt/gpu_drv/intel index c928a31511..27068b043f 100644 --- a/repos/gems/sculpt/gpu_drv/intel +++ b/repos/gems/sculpt/gpu_drv/intel @@ -1,4 +1,4 @@ - + diff --git a/repos/gems/src/app/sculpt_manager/driver/ahci.h b/repos/gems/src/app/sculpt_manager/driver/ahci.h index 47859a5e4a..b709b2ad99 100644 --- a/repos/gems/src/app/sculpt_manager/driver/ahci.h +++ b/repos/gems/src/app/sculpt_manager/driver/ahci.h @@ -46,6 +46,7 @@ struct Sculpt::Ahci_driver : private Noncopyable gen_named_node(xml, "binary", "ahci_drv"); gen_provides(xml); xml.node("config", [&] { + xml.attribute("system", "yes"); xml.node("report", [&] { xml.attribute("ports", "yes"); }); for (unsigned i = 0; i < 6; i++) xml.node("policy", [&] { diff --git a/repos/gems/src/app/sculpt_manager/driver/fb.h b/repos/gems/src/app/sculpt_manager/driver/fb.h index 9c1287d4f9..02454c2ae7 100644 --- a/repos/gems/src/app/sculpt_manager/driver/fb.h +++ b/repos/gems/src/app/sculpt_manager/driver/fb.h @@ -52,6 +52,7 @@ struct Sculpt::Fb_driver : private Noncopyable gen_parent_route(xml); gen_parent_rom_route(xml, "intel_gpu_drv"); gen_parent_rom_route(xml, "config", "config -> gpu_drv"); + gen_parent_rom_route(xml, "system", "config -> managed/system"); gen_parent_route(xml); gen_common_routes(xml); }); @@ -110,10 +111,13 @@ struct Sculpt::Fb_driver : private Noncopyable void update(Registry ®istry, Board_info const &board_info, Xml_node const &platform) { + bool const suspending = board_info.options.suspending; + bool const use_intel = board_info.detected.intel_gfx - && !board_info.options.suppress.intel_gpu; - bool const use_boot_fb = !use_intel && board_info.detected.boot_fb; - bool const use_vesa = !use_boot_fb && !use_intel && board_info.detected.vga; + && !board_info.options.suppress.intel_gpu + && !suspending; + bool const use_boot_fb = !use_intel && !suspending && board_info.detected.boot_fb; + bool const use_vesa = !use_boot_fb && !use_intel && !suspending && board_info.detected.vga; _intel_gpu.conditional(use_intel, registry, "intel_gpu", Priority::MULTIMEDIA, diff --git a/repos/gems/src/app/sculpt_manager/driver/nic.h b/repos/gems/src/app/sculpt_manager/driver/nic.h index 8fe60f7a55..59e96a83f5 100644 --- a/repos/gems/src/app/sculpt_manager/driver/nic.h +++ b/repos/gems/src/app/sculpt_manager/driver/nic.h @@ -47,8 +47,11 @@ struct Sculpt::Nic_driver : private Noncopyable void update(Registry ®istry, Board_info const &board_info) { - _nic.conditional(board_info.detected.nic && board_info.options.nic, - registry, "nic", Priority::DEFAULT, + bool const use_nic = board_info.detected.nic + && board_info.options.nic + && !board_info.options.suspending; + + _nic.conditional(use_nic, registry, "nic", Priority::DEFAULT, Ram_quota { 20*1024*1024 }, Cap_quota { 300 }); } }; diff --git a/repos/gems/src/app/sculpt_manager/driver/nvme.h b/repos/gems/src/app/sculpt_manager/driver/nvme.h index e83b67564b..0675686214 100644 --- a/repos/gems/src/app/sculpt_manager/driver/nvme.h +++ b/repos/gems/src/app/sculpt_manager/driver/nvme.h @@ -46,6 +46,7 @@ struct Sculpt::Nvme_driver : private Noncopyable gen_named_node(xml, "binary", "nvme_drv"); gen_provides(xml); xml.node("config", [&] { + xml.attribute("system", "yes"); xml.node("report", [&] { xml.attribute("namespaces", "yes"); }); xml.node("policy", [&] { xml.attribute("label", 1); diff --git a/repos/gems/src/app/sculpt_manager/driver/ps2.h b/repos/gems/src/app/sculpt_manager/driver/ps2.h index 0b83518f53..7e530e0b70 100644 --- a/repos/gems/src/app/sculpt_manager/driver/ps2.h +++ b/repos/gems/src/app/sculpt_manager/driver/ps2.h @@ -53,8 +53,11 @@ struct Sculpt::Ps2_driver : private Noncopyable void update(Registry ®istry, Board_info const &board_info) { - _ps2.conditional(board_info.detected.ps2 && !board_info.options.suppress.ps2, - registry, "ps2", Priority::MULTIMEDIA, + bool const use_ps2 = board_info.detected.ps2 + && !board_info.options.suppress.ps2 + && !board_info.options.suspending; + + _ps2.conditional(use_ps2, registry, "ps2", Priority::MULTIMEDIA, Ram_quota { 1*1024*1024 }, Cap_quota { 100 }); } }; diff --git a/repos/gems/src/app/sculpt_manager/driver/usb.h b/repos/gems/src/app/sculpt_manager/driver/usb.h index bf16193fb7..5a0a9fc984 100644 --- a/repos/gems/src/app/sculpt_manager/driver/usb.h +++ b/repos/gems/src/app/sculpt_manager/driver/usb.h @@ -161,15 +161,17 @@ struct Sculpt::Usb_driver : private Noncopyable void update(Registry ®istry, Board_info const &board_info) { - _hcd.conditional(board_info.usb_avail(), + bool const suspending = board_info.options.suspending; + + _hcd.conditional(board_info.usb_avail() && !suspending, registry, "usb", Priority::MULTIMEDIA, Ram_quota { 16*1024*1024 }, Cap_quota { 200 }); - _hid.conditional(board_info.usb_avail() && _detected.hid, + _hid.conditional(board_info.usb_avail() && _detected.hid && !suspending, registry, "usb_hid", Priority::MULTIMEDIA, Ram_quota { 11*1024*1024 }, Cap_quota { 180 }); - _net.conditional(board_info.usb_avail() && _detected.net && board_info.options.usb_net, + _net.conditional(board_info.usb_avail() && _detected.net && !suspending && board_info.options.usb_net, registry, "usb_net", Priority::DEFAULT, Ram_quota { 20*1024*1024 }, Cap_quota { 200 }); diff --git a/repos/gems/src/app/sculpt_manager/driver/wifi.h b/repos/gems/src/app/sculpt_manager/driver/wifi.h index 37cfc311c6..c6cbf1c0e4 100644 --- a/repos/gems/src/app/sculpt_manager/driver/wifi.h +++ b/repos/gems/src/app/sculpt_manager/driver/wifi.h @@ -92,8 +92,11 @@ struct Sculpt::Wifi_driver : private Noncopyable void update(Registry ®istry, Board_info const &board_info) { - _wifi.conditional(board_info.wifi_avail() && board_info.options.wifi, - registry, "wifi", Priority::DEFAULT, + bool const use_wifi = board_info.wifi_avail() + && board_info.options.wifi + && !board_info.options.suspending; + + _wifi.conditional(use_wifi, registry, "wifi", Priority::DEFAULT, Ram_quota { 16*1024*1024 }, Cap_quota { 250 }); } }; diff --git a/repos/gems/src/app/sculpt_manager/drivers.cc b/repos/gems/src/app/sculpt_manager/drivers.cc index 0a568159ae..ac68c58cb4 100644 --- a/repos/gems/src/app/sculpt_manager/drivers.cc +++ b/repos/gems/src/app/sculpt_manager/drivers.cc @@ -52,6 +52,7 @@ class Sculpt::Drivers::Instance : Noncopyable, Action &_action; Board_info _board_info { }; + Resumed _resumed { }; Attached_rom_dataspace const _platform { _env, "platform_info" }; @@ -62,6 +63,10 @@ class Sculpt::Drivers::Instance : Noncopyable, { _board_info.detected = Board_info::Detected::from_xml(devices, _platform.xml()); + _board_info.used = Board_info::Used::from_xml(devices); + + _resumed = { devices.attribute_value("resumed", 0u) }; + /* * The decision which fb driver to start depends on information * about available devices from both the devices ROM and the @@ -159,6 +164,10 @@ class Sculpt::Drivers::Instance : Noncopyable, void with(With_board_info::Callback const &fn) const { fn(_board_info); } void with(With_platform_info::Callback const &fn) const { fn(_platform.xml()); } + + bool ready_for_suspend() const { return !_board_info.used.any(); } + + Resumed resumed() const { return _resumed; } }; @@ -191,3 +200,7 @@ void Drivers::update_soc (Board_info::Soc soc) { _instance.update_soc(soc void Drivers::update_options(Board_info::Options opt) { _instance.update_options(opt); } void Drivers::gen_start_nodes(Xml_generator &xml) const { _instance.gen_start_nodes(xml); } + +bool Drivers::ready_for_suspend() const { return _instance.ready_for_suspend(); }; + +Drivers::Resumed Drivers::resumed() const { return _instance.resumed(); }; diff --git a/repos/gems/src/app/sculpt_manager/drivers.h b/repos/gems/src/app/sculpt_manager/drivers.h index 091b926d1e..750bcfce01 100644 --- a/repos/gems/src/app/sculpt_manager/drivers.h +++ b/repos/gems/src/app/sculpt_manager/drivers.h @@ -72,6 +72,12 @@ class Sculpt::Drivers : Noncopyable void with_storage_devices(auto const &fn) const { _with(With_storage_devices::Fn { fn }); } void with_board_info (auto const &fn) const { _with(With_board_info::Fn { fn }); } void with_platform_info (auto const &fn) const { _with(With_platform_info::Fn { fn }); } + + bool ready_for_suspend() const; + + struct Resumed { unsigned count; }; + + Resumed resumed() const; }; #endif /* _DRIVERS_H_ */ diff --git a/repos/gems/src/app/sculpt_manager/main.cc b/repos/gems/src/app/sculpt_manager/main.cc index 478cb57851..4e82f20b5d 100644 --- a/repos/gems/src/app/sculpt_manager/main.cc +++ b/repos/gems/src/app/sculpt_manager/main.cc @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -66,8 +67,7 @@ struct Sculpt::Main : Input_event_handler, Panel_dialog::Action, Network_widget::Action, Settings_widget::Action, - Software_presets_widget::Action, - Software_update_widget::Action, + System_dialog::Action, File_browser_dialog::Action, Popup_dialog::Action, Component::Construction_info, @@ -114,12 +114,27 @@ struct Sculpt::Main : Input_event_handler, handle_input_event(ev); }); } + System_state _system_state { }; + + using Power_features = System_power_widget::Supported; + + Power_features _power_features { .suspend = false, + .reset = (_build_info.board == "pc"), + .poweroff = false }; + Managed_config
_system_config { _env, "system", "system", *this, &Main::_handle_system_config }; - void _handle_system_config(Xml_node const &) + void _broadcast_system_state() { - _system_config.try_generate_manually_managed(); + _system_config.generate([&] (Xml_generator &xml) { + _system_state.generate(xml); }); + } + + void _handle_system_config(Xml_node const &state) + { + if (_system_state.apply_config(state).progress) + _broadcast_system_state(); } Signal_handler
_gui_mode_handler { @@ -152,6 +167,38 @@ struct Sculpt::Main : Input_event_handler, /* hook for driving the lifetime of the display driver */ } + /** + * System_power_widget::Action + */ + void trigger_suspend() override + { + _system_state.state = System_state::DRIVERS_STOPPING; + _broadcast_system_state(); + + _driver_options.suspending = true; + _drivers.update_options(_driver_options); + + generate_runtime_config(); + } + + /** + * System_power_widget::Action + */ + void trigger_reboot() override + { + _system_state.state = System_state::RESET; + _broadcast_system_state(); + } + + /** + * System_power_widget::Action + */ + void trigger_power_off() override + { + _system_state.state = System_state::POWERED_OFF; + _broadcast_system_state(); + } + Managed_config
_fonts_config { _env, "config", "fonts", *this, &Main::_handle_fonts_config }; @@ -232,6 +279,8 @@ struct Sculpt::Main : Input_event_handler, Drivers _drivers { _env, _child_states, *this, *this }; + Drivers::Resumed _resumed = _drivers.resumed(); + Board_info::Options _driver_options { }; /** @@ -239,11 +288,49 @@ struct Sculpt::Main : Input_event_handler, */ void handle_device_plug_unplug() override { + /* drive suspend/resume */ + { + auto const orig_state = _system_state.state; + + Drivers::Resumed const orig_resumed = _resumed; + _resumed = _drivers.resumed(); + + if (orig_resumed.count != _resumed.count) + _system_state.state = System_state::ACPI_RESUMING; + + if (_system_state.drivers_stopping() && _drivers.ready_for_suspend()) + _system_state.state = System_state::ACPI_SUSPENDING; + + if (orig_state != _system_state.state) + _broadcast_system_state(); + } + _handle_storage_devices(); network_config_changed(); generate_runtime_config(); } + Rom_handler
_acpi_sleep_states { + _env, "report -> runtime/acpi_support/sleep_states", + *this, &Main::_handle_acpi_sleep_states }; + + void _handle_acpi_sleep_states(Xml_node const &node) + { + auto const orig_state = _system_state.state; + + if (_system_state.ready_for_suspended(node)) + _system_state.state = System_state::SUSPENDED; + + if (_system_state.ready_for_restarting_drivers(node)) { + _system_state.state = System_state::RUNNING; + _driver_options.suspending = false; + _drivers.update_options(_driver_options); + } + + if (orig_state != _system_state.state) + _broadcast_system_state(); + } + /** * Drivers::Info */ @@ -1433,10 +1520,11 @@ struct Sculpt::Main : Input_event_handler, Dialog_view _panel_dialog { _dialog_runtime, *this, *this }; Dialog_view _system_dialog { _dialog_runtime, - _presets, _build_info, _network._nic_state, + _presets, _build_info, _power_features, + _network._nic_state, _download_queue, _index_update_queue, _file_operation_queue, _scan_rom, - _image_index_rom, *this, *this }; + _image_index_rom, *this }; Dialog_view _diag_dialog { _dialog_runtime, *this, _heap }; @@ -2111,6 +2199,16 @@ void Sculpt::Main::_handle_runtime_state(Xml_node const &state) if (_dialog_runtime.apply_runtime_state(state)) reconfigure_runtime = true; + /* power-management features depend on optional acpi_support subsystem */ + { + bool const acpi_support = _runtime_state.present_in_runtime("acpi_support"); + Power_features const orig_power_features = _power_features; + _power_features.poweroff = acpi_support; + _power_features.suspend = acpi_support; + if (orig_power_features != _power_features) + _system_dialog.refresh(); + } + if (refresh_storage) _handle_storage_devices(); diff --git a/repos/gems/src/app/sculpt_manager/model/board_info.h b/repos/gems/src/app/sculpt_manager/model/board_info.h index 0e267deecb..6f27f7e373 100644 --- a/repos/gems/src/app/sculpt_manager/model/board_info.h +++ b/repos/gems/src/app/sculpt_manager/model/board_info.h @@ -20,6 +20,38 @@ namespace Sculpt { struct Board_info; } struct Sculpt::Board_info { + enum class Pci_class : unsigned { + WIFI = 0x28000, + NIC = 0x20000, + VGA = 0x30000, + AHCI = 0x10601, + NVME = 0x10802, + UHCI = 0xc0300, OHCI = 0xc0310, EHCI = 0xc0320, XHCI = 0xc0330, + }; + + enum class Pci_vendor : unsigned { INTEL = 0x8086U, }; + + static bool _matches_class(Xml_node const &pci, Pci_class value) + { + return pci.attribute_value("class", 0U) == unsigned(value); + }; + + static bool _matches_vendor(Xml_node const &pci, Pci_vendor value) + { + return pci.attribute_value("vendor_id", 0U) == unsigned(value); + }; + + static bool _matches_usb(Xml_node const &pci) + { + return _matches_class(pci, Pci_class::UHCI) || _matches_class(pci, Pci_class::OHCI) + || _matches_class(pci, Pci_class::EHCI) || _matches_class(pci, Pci_class::XHCI); + } + + static bool _matches_ahci(Xml_node const &pci) + { + return _matches_class(pci, Pci_class::AHCI) && _matches_vendor(pci, Pci_vendor::INTEL); + } + /** * Runtime-detected features */ @@ -39,6 +71,23 @@ struct Sculpt::Board_info } detected; + struct Used + { + bool wifi, nic, intel_gfx, nvme, ahci, usb; + + void print(Output &out) const + { + Genode::print(out, "wifi=", wifi, " nic=", nic, + " intel_gfx=", intel_gfx, " nvme=", nvme, + " ahci=", ahci, " usb=", usb); + } + + static inline Used from_xml(Xml_node const &devices); + + bool any() const { return wifi || nic || intel_gfx || nvme || usb; } + + } used; + /** * Statically-known or configured features */ @@ -72,11 +121,13 @@ struct Sculpt::Board_info } suppress; + bool suspending; + bool operator != (Options const &other) const { - return display != other.display || usb_net != other.usb_net - || nic != other.nic || wifi != other.wifi - || suppress != other.suppress; + return display != other.display || usb_net != other.usb_net + || nic != other.nic || wifi != other.wifi + || suppress != other.suppress || suspending != other.suspending; } } options; @@ -101,41 +152,15 @@ Sculpt::Board_info::Detected::from_xml(Xml_node const &devices, Xml_node const & device.with_optional_sub_node("pci-config", [&] (Xml_node const &pci) { - enum class Pci_class : unsigned { - WIFI = 0x28000, - NIC = 0x20000, - VGA = 0x30000, - AHCI = 0x10601, - NVME = 0x10802, - UHCI = 0xc0300, OHCI = 0xc0310, EHCI = 0xc0320, XHCI = 0xc0330, - }; + if (_matches_class(pci, Pci_class::WIFI)) detected.wifi = true; + if (_matches_class(pci, Pci_class::NIC)) detected.nic = true; + if (_matches_class(pci, Pci_class::NVME)) detected.nvme = true; + if (_matches_usb(pci)) detected.usb = true; + if (_matches_ahci(pci)) detected.ahci = true; - enum class Pci_vendor : unsigned { INTEL = 0x8086U, }; - - auto matches_class = [&] (Pci_class value) - { - return pci.attribute_value("class", 0U) == unsigned(value); - }; - - auto matches_vendor = [&] (Pci_vendor value) - { - return pci.attribute_value("vendor_id", 0U) == unsigned(value); - }; - - if (matches_class(Pci_class::WIFI)) detected.wifi = true; - if (matches_class(Pci_class::NIC)) detected.nic = true; - if (matches_class(Pci_class::NVME)) detected.nvme = true; - - if (matches_class(Pci_class::UHCI) || matches_class(Pci_class::OHCI) - || matches_class(Pci_class::EHCI) || matches_class(Pci_class::XHCI)) - detected.usb = true; - - if (matches_class(Pci_class::AHCI) && matches_vendor(Pci_vendor::INTEL)) - detected.ahci = true; - - if (matches_class(Pci_class::VGA)) { + if (_matches_class(pci, Pci_class::VGA)) { detected.vga = true; - if (matches_vendor(Pci_vendor::INTEL)) + if (_matches_vendor(pci, Pci_vendor::INTEL)) detected.intel_gfx = true; } }); @@ -144,4 +169,33 @@ Sculpt::Board_info::Detected::from_xml(Xml_node const &devices, Xml_node const & return detected; } + +Sculpt::Board_info::Used +Sculpt::Board_info::Used::from_xml(Xml_node const &devices) +{ + Used used { }; + + devices.for_each_sub_node("device", [&] (Xml_node const &device) { + + if (!device.attribute_value("used", false)) + return; + + device.with_optional_sub_node("pci-config", [&] (Xml_node const &pci) { + + if (_matches_class(pci, Pci_class::WIFI)) used.wifi = true; + if (_matches_class(pci, Pci_class::NIC)) used.nic = true; + if (_matches_class(pci, Pci_class::NVME)) used.nvme = true; + if (_matches_usb(pci)) used.usb = true; + if (_matches_ahci(pci)) used.ahci = true; + + if (_matches_class(pci, Pci_class::VGA)) { + if (_matches_vendor(pci, Pci_vendor::INTEL)) + used.intel_gfx = true; + } + }); + }); + + return used; +} + #endif /* _MODEL__BOARD_INFO_H_ */ diff --git a/repos/gems/src/app/sculpt_manager/model/system_state.h b/repos/gems/src/app/sculpt_manager/model/system_state.h new file mode 100644 index 0000000000..1005e0ccb2 --- /dev/null +++ b/repos/gems/src/app/sculpt_manager/model/system_state.h @@ -0,0 +1,94 @@ +/* + * \brief Global system state for suspend/resume support + * \author Norman Feske + * \date 2024-04-16 + */ + +/* + * Copyright (C) 2024 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _MODEL__SYSTEM_STATE_H_ +#define _MODEL__SYSTEM_STATE_H_ + +#include + +namespace Sculpt { struct System_state; } + + +struct Sculpt::System_state : private Noncopyable +{ + enum State { + RUNNING, + DRIVERS_STOPPING, ACPI_SUSPENDING, SUSPENDED, ACPI_RESUMING, + POWERED_OFF, RESET + }; + + State state { RUNNING }; + + static State _state_from_xml(Xml_node const &node) + { + auto value = node.attribute_value("state", String<64>()); + + if (value == "driver_stop") return State::DRIVERS_STOPPING; + if (value == "s3_prepare") return State::ACPI_RESUMING; + if (value == "suspend") return State::SUSPENDED; + if (value == "s3_resume") return State::ACPI_RESUMING; + if (value == "poweroff") return State::POWERED_OFF; + if (value == "reset") return State::RESET; + + return State::RUNNING; + } + + static auto _state_name(State const state) + { + switch (state) { + case RUNNING: break; + case DRIVERS_STOPPING: return "driver_stop"; + case ACPI_SUSPENDING: return "s3_prepare"; + case SUSPENDED: return "suspend"; + case ACPI_RESUMING: return "s3_resume"; + case POWERED_OFF: return "poweroff"; + case RESET: return "reset"; + }; + return ""; + } + + Progress apply_config(Xml_node const &node) + { + State const orig = state; + state = _state_from_xml(node); + return { orig != state }; + } + + void generate(Xml_generator &xml) const + { + xml.attribute("state", _state_name(state)); + } + + bool drivers_stopping() const { return state == State::DRIVERS_STOPPING; } + bool acpi_suspending() const { return state == State::ACPI_SUSPENDING; } + bool acpi_resuming() const { return state == State::ACPI_RESUMING; } + + bool _acpi_completed(State const expected, Xml_node const &sleep_states) const + { + auto const complete = sleep_states.attribute_value("complete", String<16>()); + + return (state == expected) && (complete == _state_name(expected)); + } + + bool ready_for_suspended(Xml_node const &acpi_sleep_states) const + { + return _acpi_completed(ACPI_SUSPENDING, acpi_sleep_states); + } + + bool ready_for_restarting_drivers(Xml_node const &acpi_sleep_states) const + { + return _acpi_completed(ACPI_RESUMING, acpi_sleep_states); + } +}; + +#endif /* _MODEL__SYSTEM_STATE_H_ */ diff --git a/repos/gems/src/app/sculpt_manager/view/system_dialog.h b/repos/gems/src/app/sculpt_manager/view/system_dialog.h index d8874122b4..bfa8cdfc87 100644 --- a/repos/gems/src/app/sculpt_manager/view/system_dialog.h +++ b/repos/gems/src/app/sculpt_manager/view/system_dialog.h @@ -15,6 +15,7 @@ #define _VIEW__SYSTEM_DIALOG_H_ #include +#include #include #include #include @@ -25,23 +26,30 @@ namespace Sculpt { struct System_dialog; } struct Sculpt::System_dialog : Top_level_dialog { - using Depot_users = Depot_users_widget::Depot_users; - using Image_index = Rom_data; + using Depot_users = Depot_users_widget::Depot_users; + using Image_index = Rom_data; + using Power_features = System_power_widget::Supported; - Presets const &_presets; - Image_index const &_image_index; - Build_info const &_build_info; + Presets const &_presets; + Image_index const &_image_index; + Build_info const &_build_info; + Power_features const &_power_features; - Software_presets_widget::Action &_presets_action; - Software_update_widget::Action &_update_action; + struct Action : virtual System_power_widget::Action, + virtual Software_presets_widget::Action, + virtual Software_update_widget::Action { }; - enum Tab { PRESET, UPDATE } _selected_tab = Tab::PRESET; + Action &_action; + enum Tab { POWER, PRESET, UPDATE } _selected_tab = Tab::PRESET; + + Hosted _power_widget { Id { "power" } }; Hosted _presets_widget { Id { "presets" } }; Hosted _update_widget; Hosted _version_widget { Id { "version" } }; Hosted> + _power_tab { Id { " Power " }, Tab::POWER }, _preset_tab { Id { " Presets " }, Tab::PRESET }, _update_tab { Id { " Update " }, Tab::UPDATE }; @@ -52,6 +60,8 @@ struct Sculpt::System_dialog : Top_level_dialog /* tabs */ s.sub_scope([&] (Scope &s) { + if (_power_features.any_support()) + s.widget(_power_tab, _selected_tab); s.widget(_preset_tab, _selected_tab, [&] (auto &s) { if (!_presets.available()) s.attribute("style", "unimportant"); @@ -61,6 +71,10 @@ struct Sculpt::System_dialog : Top_level_dialog }); switch (_selected_tab) { + case Tab::POWER: + if (_power_features.any_support()) + s.widget(_power_widget, _power_features); + break; case Tab::PRESET: s.widget(_presets_widget, _presets); break; @@ -76,36 +90,39 @@ struct Sculpt::System_dialog : Top_level_dialog void click(Clicked_at const &at) override { + _power_tab .propagate(at, [&] (Tab t) { _selected_tab = t; }); _preset_tab.propagate(at, [&] (Tab t) { _selected_tab = t; }); _update_tab.propagate(at, [&] (Tab t) { _selected_tab = t; }); + _power_widget.propagate(at); _presets_widget.propagate(at, _presets); if (_selected_tab == Tab::UPDATE) - _update_widget.propagate(at, _update_action); + _update_widget.propagate(at, _action); } void clack(Clacked_at const &at) override { - _presets_widget.propagate(at, _presets, _presets_action); + _power_widget.propagate(at, _action); + _presets_widget.propagate(at, _presets, _action); } void drag(Dragged_at const &) override { } - System_dialog(Presets const &presets, - Build_info const &build_info, - Nic_state const &nic_state, - Download_queue const &download_queue, - Index_update_queue const &index_update_queue, - File_operation_queue const &file_operation_queue, - Depot_users const &depot_users, - Image_index const &image_index, - Software_presets_widget::Action &presets_action, - Software_update_widget::Action &update_action) + System_dialog(Presets const &presets, + Build_info const &build_info, + Power_features const &power_features, + Nic_state const &nic_state, + Download_queue const &download_queue, + Index_update_queue const &index_update_queue, + File_operation_queue const &file_operation_queue, + Depot_users const &depot_users, + Image_index const &image_index, + Action &action) : Top_level_dialog("system"), _presets(presets), _image_index(image_index), _build_info(build_info), - _presets_action(presets_action), _update_action(update_action), + _power_features(power_features), _action(action), _update_widget(Id { "update" }, build_info, nic_state, download_queue, index_update_queue, file_operation_queue, depot_users) diff --git a/repos/gems/src/app/sculpt_manager/view/system_power_widget.h b/repos/gems/src/app/sculpt_manager/view/system_power_widget.h new file mode 100644 index 0000000000..bc6d628d73 --- /dev/null +++ b/repos/gems/src/app/sculpt_manager/view/system_power_widget.h @@ -0,0 +1,162 @@ +/* + * \brief System power-control widget + * \author Norman Feske + * \date 2024-04-16 + */ + +/* + * Copyright (C) 2024 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _VIEW__SYSTEM_POWER_WIDGET_H_ +#define _VIEW__SYSTEM_POWER_WIDGET_H_ + +#include +#include + +namespace Sculpt { struct System_power_widget; } + + +struct Sculpt::System_power_widget : Widget +{ + struct Supported + { + bool suspend, reset, poweroff; + + bool any_support() const { return suspend || reset || poweroff; } + + bool operator != (Supported const &other) const + { + return suspend != other.suspend + || reset != other.reset + || poweroff != other.poweroff; + } + }; + + enum class Option { UNKNOWN, SUSPEND, REBOOT, OFF }; + + Option _selected_option { Option::UNKNOWN }; + + struct Conditional_confirm : Widget + { + Hosted _button { Id { } }; + + void view(Scope &s, bool condition) const + { + s.widget(_button, [&] (Scope