diff --git a/repos/gems/src/app/sculpt_manager/graph.h b/repos/gems/src/app/sculpt_manager/graph.h index 9add32d26f..fd5eb7b1fb 100644 --- a/repos/gems/src/app/sculpt_manager/graph.h +++ b/repos/gems/src/app/sculpt_manager/graph.h @@ -25,6 +25,7 @@ #include #include #include +#include namespace Sculpt { struct Graph; } @@ -33,7 +34,8 @@ struct Sculpt::Graph { Env &_env; - Runtime_state &_runtime_state; + Runtime_state &_runtime_state; + Runtime_config const &_runtime_config; Storage_target const &_sculpt_partition; @@ -43,18 +45,8 @@ struct Sculpt::Graph Expanding_reporter _graph_dialog_reporter { _env, "dialog", "runtime_view_dialog" }; - /* - * Even though the runtime configuration is generate by the sculpt - * manager, we still obtain it as a separate ROM session to keep both - * parts decoupled. - */ - Attached_rom_dataspace _runtime_config_rom { _env, "config -> managed/runtime" }; - Attached_rom_dataspace _hover_rom { _env, "runtime_view_hover" }; - Signal_handler _runtime_config_handler { - _env.ep(), *this, &Graph::_handle_runtime_config }; - Signal_handler _hover_handler { _env.ep(), *this, &Graph::_handle_hover }; @@ -69,51 +61,6 @@ struct Sculpt::Graph bool _hovered = false; - typedef Start_name Node_name; - - /** - * Return component name targeted by the first route of the start node - */ - static Node_name _primary_dependency(Xml_node const start) - { - if (!start.has_sub_node("route")) - return Node_name(); - - Xml_node const route = start.sub_node("route"); - - if (!route.has_sub_node("service")) - return Node_name(); - - Xml_node const service = route.sub_node("service"); - - if (service.has_sub_node("child")) { - Xml_node const child = service.sub_node("child"); - return child.attribute_value("name", Node_name()); - } - - return Node_name(); - } - - template - static void _for_each_secondary_dep(Xml_node const start, FN const &fn) - { - if (!start.has_sub_node("route")) - return; - - Xml_node const route = start.sub_node("route"); - - bool first_route = true; - route.for_each_sub_node("service", [&] (Xml_node service) { - - if (!first_route && service.has_sub_node("child")) { - Xml_node const child = service.sub_node("child"); - fn(child.attribute_value("name", Start_name())); - } - - first_route = false; - }); - } - void _gen_selected_node_content(Xml_generator &xml, Start_name const &name, Runtime_state::Info const &info) const { @@ -147,8 +94,6 @@ struct Sculpt::Graph void _gen_graph_dialog() { - Xml_node const config = _runtime_config_rom.xml(); - _graph_dialog_reporter.generate([&] (Xml_generator &xml) { xml.node("depgraph", [&] () { @@ -164,9 +109,11 @@ struct Sculpt::Graph xml.attribute("text", "+"); }); }); } - config.for_each_sub_node("start", [&] (Xml_node start) { + typedef Runtime_config::Component Component; - Start_name const name = start.attribute_value("name", Start_name()); + _runtime_config.for_each_component([&] (Component const &component) { + + Start_name const name = component.name; /* omit sculpt's helpers from the graph */ bool const blacklisted = (name == "runtime_view" @@ -179,7 +126,7 @@ struct Sculpt::Graph gen_named_node(xml, "frame", name, [&] () { - Node_name primary_dep = _primary_dependency(start); + Start_name primary_dep = component.primary_dependency; if (primary_dep == "default_fs_rw") primary_dep = _sculpt_partition.fs(); @@ -206,19 +153,19 @@ struct Sculpt::Graph }); }); - config.for_each_sub_node("start", [&] (Xml_node start) { + _runtime_config.for_each_component([&] (Component const &component) { - Start_name const name = start.attribute_value("name", Start_name()); + Start_name const name = component.name; Runtime_state::Info const info = _runtime_state.info(name); bool const show_details = info.selected; if (show_details) { - _for_each_secondary_dep(start, [&] (Start_name const &dep) { + component.for_each_secondary_dep([&] (Start_name const &dep) { xml.node("dep", [&] () { xml.attribute("node", name); - xml.attribute("on", dep); + xml.attribute("on", dep); }); }); } @@ -227,13 +174,6 @@ struct Sculpt::Graph }); } - void _handle_runtime_config() - { - _runtime_config_rom.update(); - - _gen_graph_dialog(); - } - void _handle_hover() { _hover_rom.update(); @@ -284,15 +224,15 @@ struct Sculpt::Graph Graph(Env &env, Runtime_state &runtime_state, + Runtime_config const &runtime_config, Storage_target const &sculpt_partition, Popup::State const &popup_state, Depot_deploy::Children const &deploy_children) : - _env(env), _runtime_state(runtime_state), + _env(env), _runtime_state(runtime_state), _runtime_config(runtime_config), _sculpt_partition(sculpt_partition), _popup_state(popup_state), _deploy_children(deploy_children) { - _runtime_config_rom.sigh(_runtime_config_handler); _hover_rom.sigh(_hover_handler); } @@ -339,6 +279,8 @@ struct Sculpt::Graph _gen_graph_dialog(); } + + void gen_dialog() { _gen_graph_dialog(); } }; #endif /* _GRAPH_H_ */ diff --git a/repos/gems/src/app/sculpt_manager/main.cc b/repos/gems/src/app/sculpt_manager/main.cc index 1af140d353..e5a023760b 100644 --- a/repos/gems/src/app/sculpt_manager/main.cc +++ b/repos/gems/src/app/sculpt_manager/main.cc @@ -364,6 +364,35 @@ struct Sculpt::Main : Input_event_handler, void _handle_runtime_state(); + + /**************************************** + ** Cached model of the runtime config ** + ****************************************/ + + /* + * Even though the runtime configuration is generated by the sculpt + * manager, we still obtain it as a separate ROM session to keep the GUI + * part decoupled from the lower-level runtime configuration generator. + */ + Attached_rom_dataspace _runtime_config_rom { _env, "config -> managed/runtime" }; + + Signal_handler
_runtime_config_handler { + _env.ep(), *this, &Main::_handle_runtime_config }; + + Runtime_config _cached_runtime_config { _heap }; + + void _handle_runtime_config() + { + _runtime_config_rom.update(); + _cached_runtime_config.update_from_xml(_runtime_config_rom.xml()); + _graph.gen_dialog(); + } + + + /**************************** + ** Interactive operations ** + ****************************/ + Keyboard_focus _keyboard_focus { _env, _network.dialog, _network.wpa_passphrase }; Hovered::Dialog _last_clicked { Hovered::NONE }; @@ -538,8 +567,8 @@ struct Sculpt::Main : Input_event_handler, Popup _popup { }; - Graph _graph { _env, _runtime_state, _storage._sculpt_partition, - _popup.state, _deploy._children }; + Graph _graph { _env, _runtime_state, _cached_runtime_config, + _storage._sculpt_partition, _popup.state, _deploy._children }; Child_state _runtime_view_state { "runtime_view", Ram_quota{8*1024*1024}, Cap_quota{200} }; @@ -549,6 +578,7 @@ struct Sculpt::Main : Input_event_handler, { _manual_deploy_rom.sigh(_manual_deploy_handler); _runtime_state_rom.sigh(_runtime_state_handler); + _runtime_config_rom.sigh(_runtime_config_handler); _nitpicker_displays.sigh(_nitpicker_displays_handler); /* @@ -573,6 +603,7 @@ struct Sculpt::Main : Input_event_handler, _storage.handle_storage_devices_update(); _deploy.handle_deploy(); _handle_pci_devices(); + _handle_runtime_config(); /* * Generate initial config/managed/deploy configuration diff --git a/repos/gems/src/app/sculpt_manager/model/runtime_config.h b/repos/gems/src/app/sculpt_manager/model/runtime_config.h new file mode 100644 index 0000000000..b445c7a14e --- /dev/null +++ b/repos/gems/src/app/sculpt_manager/model/runtime_config.h @@ -0,0 +1,175 @@ +/* + * \brief Cached information of the current runtime configuration + * \author Norman Feske + * \date 2019-02-22 + */ + +/* + * Copyright (C) 2019 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__RUNTIME_CONFIG_H_ +#define _MODEL__RUNTIME_CONFIG_H_ + +/* Genode includes */ +#include +#include + +/* local includes */ +#include + +namespace Sculpt { class Runtime_config; } + +class Sculpt::Runtime_config +{ + private: + + Allocator &_alloc; + + /** + * Return component name targeted by the first route of the start node + */ + static Start_name _primary_dependency(Xml_node const start) + { + if (!start.has_sub_node("route")) + return Start_name(); + + Xml_node const route = start.sub_node("route"); + + if (!route.has_sub_node("service")) + return Start_name(); + + Xml_node const service = route.sub_node("service"); + + if (service.has_sub_node("child")) { + Xml_node const child = service.sub_node("child"); + return child.attribute_value("name", Start_name()); + } + + return Start_name(); + } + + public: + + struct Component : List_model::Element + { + Start_name const name; + + Start_name primary_dependency { }; + + struct Child_dep : List_model::Element + { + Start_name const to_name; + + Child_dep(Start_name to_name) : to_name(to_name) { } + + struct Update_policy + { + typedef Child_dep Element; + + Allocator &_alloc; + + Update_policy(Allocator &alloc) : _alloc(alloc) { } + + void destroy_element(Child_dep &elem) { destroy(_alloc, &elem); } + + static Start_name _to_name(Xml_node node) + { + Start_name result { }; + node.with_sub_node("child", [&] (Xml_node child) { + result = child.attribute_value("name", Start_name()); }); + + return result; + } + + Child_dep &create_element(Xml_node node) + { + return *new (_alloc) Child_dep(_to_name(node)); + } + + void update_element(Child_dep &, Xml_node) { } + + static bool element_matches_xml_node(Child_dep const &elem, Xml_node node) + { + return _to_name(node) == elem.to_name; + } + + static bool node_is_element(Xml_node node) + { + return _to_name(node).valid(); + } + }; + }; + + /* dependencies on other child components */ + List_model child_deps { }; + + template + void for_each_secondary_dep(FN const &fn) const + { + child_deps.for_each([&] (Child_dep const &dep) { + if (dep.to_name != primary_dependency) + fn(dep.to_name); }); + } + + Component(Start_name const &name) : name(name) { } + + struct Update_policy + { + typedef Component Element; + + Allocator &_alloc; + + Update_policy(Allocator &alloc) : _alloc(alloc) { } + + void destroy_element(Component &elem) + { + destroy(_alloc, &elem); + } + + Component &create_element(Xml_node node) + { + return *new (_alloc) + Component(node.attribute_value("name", Start_name())); + } + + void update_element(Component &elem, Xml_node node) + { + elem.primary_dependency = _primary_dependency(node); + + Child_dep::Update_policy policy(_alloc); + node.with_sub_node("route", [&] (Xml_node route) { + elem.child_deps.update_from_xml(policy, route); }); + } + + static bool element_matches_xml_node(Component const &elem, Xml_node node) + { + return node.attribute_value("name", Start_name()) == elem.name; + } + + static bool node_is_element(Xml_node node) { return node.has_type("start"); } + }; + }; + + private: + + List_model _components { }; + + public: + + Runtime_config(Allocator &alloc) : _alloc(alloc) { } + + void update_from_xml(Xml_node config) + { + Component::Update_policy policy(_alloc); + _components.update_from_xml(policy, config); + } + + template + void for_each_component(FN const &fn) const { _components.for_each(fn); } +}; + +#endif /* _MODEL__RUNTIME_CONFIG_H_ */