mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-19 05:37:54 +00:00
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
This commit is contained in:
parent
29e21bff7f
commit
f34bf0d19e
@ -1,4 +1,4 @@
|
||||
<config>
|
||||
<config system="yes">
|
||||
<device vendor="0x8086" device="0x1606" generation="8" platform="broadwell" description="HD Graphics (BDW GT1 ULT)"/>
|
||||
<device vendor="0x8086" device="0x1616" generation="8" platform="broadwell" description="HD Graphics 5500 (BDW GT2 ULT)"/>
|
||||
<device vendor="0x8086" device="0x1622" generation="8" platform="broadwell" description="Iris Pro Graphics 6200 (BDW GT3e)"/>
|
||||
|
@ -46,6 +46,7 @@ struct Sculpt::Ahci_driver : private Noncopyable
|
||||
gen_named_node(xml, "binary", "ahci_drv");
|
||||
gen_provides<Block::Session>(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", [&] {
|
||||
|
@ -52,6 +52,7 @@ struct Sculpt::Fb_driver : private Noncopyable
|
||||
gen_parent_route<Platform::Session>(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<Rm_session>(xml);
|
||||
gen_common_routes(xml);
|
||||
});
|
||||
@ -110,10 +111,13 @@ struct Sculpt::Fb_driver : private Noncopyable
|
||||
void update(Registry<Child_state> ®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,
|
||||
|
@ -47,8 +47,11 @@ struct Sculpt::Nic_driver : private Noncopyable
|
||||
|
||||
void update(Registry<Child_state> ®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 });
|
||||
}
|
||||
};
|
||||
|
@ -46,6 +46,7 @@ struct Sculpt::Nvme_driver : private Noncopyable
|
||||
gen_named_node(xml, "binary", "nvme_drv");
|
||||
gen_provides<Block::Session>(xml);
|
||||
xml.node("config", [&] {
|
||||
xml.attribute("system", "yes");
|
||||
xml.node("report", [&] { xml.attribute("namespaces", "yes"); });
|
||||
xml.node("policy", [&] {
|
||||
xml.attribute("label", 1);
|
||||
|
@ -53,8 +53,11 @@ struct Sculpt::Ps2_driver : private Noncopyable
|
||||
|
||||
void update(Registry<Child_state> ®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 });
|
||||
}
|
||||
};
|
||||
|
@ -161,15 +161,17 @@ struct Sculpt::Usb_driver : private Noncopyable
|
||||
|
||||
void update(Registry<Child_state> ®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 });
|
||||
|
||||
|
@ -92,8 +92,11 @@ struct Sculpt::Wifi_driver : private Noncopyable
|
||||
|
||||
void update(Registry<Child_state> ®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 });
|
||||
}
|
||||
};
|
||||
|
@ -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(); };
|
||||
|
@ -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_ */
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include <model/settings.h>
|
||||
#include <model/presets.h>
|
||||
#include <model/screensaver.h>
|
||||
#include <model/system_state.h>
|
||||
#include <view/download_status_widget.h>
|
||||
#include <view/popup_dialog.h>
|
||||
#include <view/panel_dialog.h>
|
||||
@ -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<Main> _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<Main> _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<Main> _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<Main> _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> _panel_dialog { _dialog_runtime, *this, *this };
|
||||
|
||||
Dialog_view<System_dialog> _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> _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();
|
||||
|
||||
|
@ -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_ */
|
||||
|
94
repos/gems/src/app/sculpt_manager/model/system_state.h
Normal file
94
repos/gems/src/app/sculpt_manager/model/system_state.h
Normal file
@ -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 <types.h>
|
||||
|
||||
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_ */
|
@ -15,6 +15,7 @@
|
||||
#define _VIEW__SYSTEM_DIALOG_H_
|
||||
|
||||
#include <view/dialog.h>
|
||||
#include <view/system_power_widget.h>
|
||||
#include <view/software_presets_widget.h>
|
||||
#include <view/software_update_widget.h>
|
||||
#include <view/software_version_widget.h>
|
||||
@ -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<Frame, Vbox, System_power_widget> _power_widget { Id { "power" } };
|
||||
Hosted<Frame, Vbox, Software_presets_widget> _presets_widget { Id { "presets" } };
|
||||
Hosted<Frame, Vbox, Software_update_widget> _update_widget;
|
||||
Hosted<Frame, Vbox, Software_version_widget> _version_widget { Id { "version" } };
|
||||
|
||||
Hosted<Frame, Vbox, Hbox, Select_button<Tab>>
|
||||
_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<Hbox>([&] (Scope<Frame, Vbox, Hbox> &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)
|
||||
|
162
repos/gems/src/app/sculpt_manager/view/system_power_widget.h
Normal file
162
repos/gems/src/app/sculpt_manager/view/system_power_widget.h
Normal file
@ -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 <util/formatted_output.h>
|
||||
#include <view/dialog.h>
|
||||
|
||||
namespace Sculpt { struct System_power_widget; }
|
||||
|
||||
|
||||
struct Sculpt::System_power_widget : Widget<Vbox>
|
||||
{
|
||||
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<Right_floating_hbox>
|
||||
{
|
||||
Hosted<Right_floating_hbox, Deferred_action_button> _button { Id { } };
|
||||
|
||||
void view(Scope<Right_floating_hbox> &s, bool condition) const
|
||||
{
|
||||
s.widget(_button, [&] (Scope<Button> &s) {
|
||||
|
||||
if (!condition)
|
||||
s.attribute("style", "invisible");
|
||||
|
||||
s.sub_scope<Label>("Confirm", [&] (auto &s) {
|
||||
if (!condition) s.attribute("style", "invisible"); });
|
||||
});
|
||||
}
|
||||
|
||||
void click(Clicked_at const &at) { _button.propagate(at); }
|
||||
|
||||
void clack(Clacked_at const &at, auto const &confirmed_fn)
|
||||
{
|
||||
_button.propagate(at, confirmed_fn);
|
||||
}
|
||||
};
|
||||
|
||||
struct Power_options : Widget<Float>
|
||||
{
|
||||
struct Entry : Widget<Hbox>
|
||||
{
|
||||
struct Attr { bool need_confirm; };
|
||||
|
||||
Hosted<Hbox, Radio_select_button<Option>> _radio;
|
||||
Hosted<Hbox, Conditional_confirm> _confirm { Id { "confirm" } };
|
||||
|
||||
Entry(Option const option) : _radio(Id { "radio" }, option) { }
|
||||
|
||||
void view(Scope<Hbox> &s, Option const &selected, Attr attr) const
|
||||
{
|
||||
s.widget(_radio, selected, s.id.value);
|
||||
s.widget(_confirm, attr.need_confirm && (selected == _radio.value));
|
||||
}
|
||||
|
||||
void click(Clicked_at const &at, Option const &selected, auto const &fn)
|
||||
{
|
||||
_radio.propagate(at, fn);
|
||||
|
||||
if (selected == _radio.value)
|
||||
_confirm.propagate(at);
|
||||
}
|
||||
|
||||
void clack(Clacked_at const &at, auto const &fn)
|
||||
{
|
||||
_confirm.propagate(at, [&] { fn(_radio.value); });
|
||||
}
|
||||
};
|
||||
|
||||
Hosted<Float, Frame, Vbox, Entry>
|
||||
_suspend { Id { "Suspend" }, Option::SUSPEND },
|
||||
_reboot { Id { "Hard reboot" }, Option::REBOOT },
|
||||
_off { Id { "Hard power down" }, Option::OFF };
|
||||
|
||||
void view(Scope<Float> &s, Option const &selected, Supported const supported) const
|
||||
{
|
||||
s.sub_scope<Frame>([&] (Scope<Float, Frame> &s) {
|
||||
s.sub_scope<Vbox>([&] (Scope<Float, Frame, Vbox> &s) {
|
||||
Entry::Attr const attr { .need_confirm = true };
|
||||
if (supported.suspend) s.widget(_suspend, selected, attr);
|
||||
if (supported.reset) s.widget(_reboot, selected, attr);
|
||||
if (supported.poweroff) s.widget(_off, selected, attr);
|
||||
s.sub_scope<Min_ex>(35);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void click(Clicked_at const &at, Option const &selected, auto const &fn)
|
||||
{
|
||||
_suspend.propagate(at, selected, fn);
|
||||
_reboot .propagate(at, selected, fn);
|
||||
_off .propagate(at, selected, fn);
|
||||
}
|
||||
|
||||
void clack(Clacked_at const &at, auto const &fn)
|
||||
{
|
||||
_suspend.propagate(at, fn);
|
||||
_reboot .propagate(at, fn);
|
||||
_off .propagate(at, fn);
|
||||
}
|
||||
};
|
||||
|
||||
Hosted<Vbox, Power_options> _power_options { Id { "options" } };
|
||||
|
||||
void view(Scope<Vbox> &s, Supported const supported) const
|
||||
{
|
||||
s.widget(_power_options, _selected_option, supported);
|
||||
}
|
||||
|
||||
struct Action : Interface
|
||||
{
|
||||
virtual void trigger_suspend() = 0;
|
||||
virtual void trigger_reboot() = 0;
|
||||
virtual void trigger_power_off() = 0;
|
||||
};
|
||||
|
||||
void click(Clicked_at const &at)
|
||||
{
|
||||
_power_options.propagate(at, _selected_option, [&] (Option const selected) {
|
||||
_selected_option = selected; });
|
||||
}
|
||||
|
||||
void clack(Clacked_at const &at, Action &action)
|
||||
{
|
||||
_power_options.propagate(at, [&] (Option const confirmed) {
|
||||
|
||||
if (confirmed == Option::SUSPEND) action.trigger_suspend();
|
||||
if (confirmed == Option::REBOOT) action.trigger_reboot();
|
||||
if (confirmed == Option::OFF) action.trigger_power_off();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _VIEW__SYSTEM_POWER_WIDGET_H_ */
|
Loading…
Reference in New Issue
Block a user