mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-08 20:05:54 +00:00
sculpt_manager: touch-screen compatibility
This patch makes Sculpt's leitzentrale GUI able to respond to touch events. It formerly assumed that click/clack events are always preceded by hover reports that identify the clicked-on widgets. For touch events, however, the most up-to-date hover information referred to the previous click because there is no motion without touching. So the GUI tended to identify the wrong widgets as click targets. The patch solved this problem by testing the freshness of the hover information at the time of the click. If the hover information is older than the click, the action is deferred until up-to-date hover information becomes available. Fixes #4398
This commit is contained in:
parent
cdbb929125
commit
8e9cabf819
@ -91,6 +91,7 @@
|
||||
<default-policy domain="default"/>
|
||||
<capture/>
|
||||
<event/>
|
||||
<report clicked="yes"/>
|
||||
</config>
|
||||
<route>
|
||||
<service name="Input"> <child name="gui_fb"/> </service>
|
||||
@ -114,6 +115,7 @@
|
||||
<policy label="decorator -> pointer" report="wm -> pointer"/>
|
||||
<policy label="manager -> window_list" report="wm -> window_list"/>
|
||||
<policy label="manager -> decorator_margins" report="decorator -> decorator_margins"/>
|
||||
<policy label="manager -> clicked" report="nitpicker -> clicked"/>
|
||||
<policy label="nitpicker -> focus" report="manager -> focus"/>
|
||||
<policy label="runtime -> leitzentrale -> menu_view -> dialog"
|
||||
report="manager -> menu_dialog"/>
|
||||
@ -240,9 +242,10 @@
|
||||
<child name="config_fs_report" label="managed -> depot_query"/> </service>
|
||||
<service name="Report"> <child name="report_rom"/> </service>
|
||||
<service name="ROM" label_prefix="report ->"> <parent/> </service>
|
||||
<service name="ROM" label_suffix="_hover"> <child name="report_rom"/> </service>
|
||||
<service name="ROM" label="window_list"> <child name="report_rom"/> </service>
|
||||
<service name="ROM" label_suffix="_hover"> <child name="report_rom"/> </service>
|
||||
<service name="ROM" label="window_list"> <child name="report_rom"/> </service>
|
||||
<service name="ROM" label="decorator_margins"> <child name="report_rom"/> </service>
|
||||
<service name="ROM" label="clicked"> <child name="report_rom"/> </service>
|
||||
<service name="Gui"> <parent/> </service>
|
||||
<any-service> <parent/> </any-service>
|
||||
</route>
|
||||
|
@ -24,6 +24,8 @@ struct Gui::Session_component : Rpc_object<Gui::Session>
|
||||
|
||||
Input_event_handler &_event_handler;
|
||||
|
||||
Input::Seq_number &_global_input_seq_number;
|
||||
|
||||
Gui::Connection _connection;
|
||||
|
||||
Input::Session_component _input_component { _env, _env.ram() };
|
||||
@ -35,16 +37,28 @@ struct Gui::Session_component : Rpc_object<Gui::Session>
|
||||
{
|
||||
_connection.input()->for_each_event([&] (Input::Event ev) {
|
||||
|
||||
/* handle event locally within the sculpt manager */
|
||||
/*
|
||||
* Augment input stream with sequence numbers to correlate
|
||||
* clicks with hover reports.
|
||||
*/
|
||||
if (ev.key_press(Input::BTN_LEFT) || ev.key_release(Input::BTN_LEFT)) {
|
||||
_global_input_seq_number.value++;
|
||||
_input_component.submit(_global_input_seq_number);
|
||||
}
|
||||
|
||||
/* handle event locally within the sculpt manager */
|
||||
_event_handler.handle_input_event(ev);
|
||||
|
||||
_input_component.submit(ev);
|
||||
});
|
||||
}
|
||||
|
||||
Session_component(Env &env, char const *args, Input_event_handler &event_handler)
|
||||
Session_component(Env &env, char const *args,
|
||||
Input_event_handler &event_handler,
|
||||
Input::Seq_number &global_input_seq_number)
|
||||
:
|
||||
_env(env), _event_handler(event_handler),
|
||||
_global_input_seq_number(global_input_seq_number),
|
||||
_connection(env, session_label_from_args(args).string())
|
||||
{
|
||||
_connection.input()->sigh(_input_handler);
|
||||
@ -108,7 +122,8 @@ struct Gui::Session_component : Rpc_object<Gui::Session>
|
||||
|
||||
Gui::Session_component *Gui::Root::_create_session(const char *args)
|
||||
{
|
||||
return new (md_alloc()) Session_component(_env, args, _event_handler);
|
||||
return new (md_alloc()) Session_component(_env, args, _event_handler,
|
||||
_global_input_seq_number);
|
||||
}
|
||||
|
||||
|
||||
@ -124,10 +139,13 @@ void Gui::Root::_destroy_session(Session_component *session)
|
||||
}
|
||||
|
||||
|
||||
Gui::Root::Root(Env &env, Allocator &md_alloc, Input_event_handler &event_handler)
|
||||
Gui::Root::Root(Env &env, Allocator &md_alloc,
|
||||
Input_event_handler &event_handler,
|
||||
Input::Seq_number &global_input_seq_number)
|
||||
:
|
||||
Root_component<Session_component>(env.ep(), md_alloc),
|
||||
_env(env), _event_handler(event_handler)
|
||||
_env(env), _event_handler(event_handler),
|
||||
_global_input_seq_number(global_input_seq_number)
|
||||
{
|
||||
env.parent().announce(env.ep().manage(*this));
|
||||
}
|
||||
|
@ -36,12 +36,13 @@ struct Gui::Root : Genode::Root_component<Session_component>
|
||||
{
|
||||
Env &_env;
|
||||
Input_event_handler &_event_handler;
|
||||
Input::Seq_number &_global_input_seq_number;
|
||||
|
||||
Session_component *_create_session(const char *) override;
|
||||
void _upgrade_session(Session_component *, const char *) override;
|
||||
void _destroy_session(Session_component *) override;
|
||||
|
||||
Root(Env &, Allocator &, Input_event_handler &);
|
||||
Root(Env &, Allocator &, Input_event_handler &, Input::Seq_number &);
|
||||
|
||||
~Root();
|
||||
};
|
||||
|
@ -62,7 +62,8 @@ struct Sculpt::Main : Input_event_handler,
|
||||
Depot_query,
|
||||
Panel_dialog::State,
|
||||
Dialog,
|
||||
Popup_dialog::Refresh
|
||||
Popup_dialog::Refresh,
|
||||
Menu_view::Hover_update_handler
|
||||
{
|
||||
Env &_env;
|
||||
|
||||
@ -72,11 +73,13 @@ struct Sculpt::Main : Input_event_handler,
|
||||
|
||||
Registry<Child_state> _child_states { };
|
||||
|
||||
Input::Seq_number _global_input_seq_number { };
|
||||
|
||||
Gui::Connection _gui { _env, "input" };
|
||||
|
||||
bool _gui_mode_ready = false; /* becomes true once the graphics driver is up */
|
||||
|
||||
Gui::Root _gui_root { _env, _heap, *this };
|
||||
Gui::Root _gui_root { _env, _heap, *this, _global_input_seq_number };
|
||||
|
||||
Signal_handler<Main> _input_handler {
|
||||
_env.ep(), *this, &Main::_handle_input };
|
||||
@ -209,7 +212,7 @@ struct Sculpt::Main : Input_event_handler,
|
||||
}
|
||||
|
||||
|
||||
Network _network { _env, _heap, _child_states, *this, _runtime_state, _pci_info };
|
||||
Network _network { _env, _heap, _child_states, *this, *this, _runtime_state, _pci_info };
|
||||
|
||||
|
||||
/************
|
||||
@ -546,8 +549,137 @@ struct Sculpt::Main : Input_event_handler,
|
||||
** Interactive operations **
|
||||
****************************/
|
||||
|
||||
/*
|
||||
* Track nitpicker's "clicked" report to reliably detect clicks outside
|
||||
* any menu view (closing the popup window).
|
||||
*/
|
||||
|
||||
Attached_rom_dataspace _clicked_rom { _env, "clicked" };
|
||||
|
||||
Signal_handler<Main> _clicked_handler {
|
||||
_env.ep(), *this, &Main::_handle_clicked };
|
||||
|
||||
void _handle_clicked()
|
||||
{
|
||||
_clicked_rom.update();
|
||||
_try_handle_click();
|
||||
}
|
||||
|
||||
Keyboard_focus _keyboard_focus { _env, _network.dialog, _network.wpa_passphrase, *this };
|
||||
|
||||
Constructible<Input::Seq_number> _clicked_seq_number { };
|
||||
Constructible<Input::Seq_number> _clacked_seq_number { };
|
||||
|
||||
void _try_handle_click()
|
||||
{
|
||||
if (!_clicked_seq_number.constructed())
|
||||
return;
|
||||
|
||||
Input::Seq_number const seq = *_clicked_seq_number;
|
||||
|
||||
auto click_outside_popup = [&] ()
|
||||
{
|
||||
Xml_node const clicked = _clicked_rom.xml();
|
||||
|
||||
if (!clicked.has_attribute("seq"))
|
||||
return false;
|
||||
|
||||
if (clicked.attribute_value("seq", 0u) != seq.value)
|
||||
return false;
|
||||
|
||||
Label const popup_label { "wm -> runtime -> leitzentrale -> popup_view" };
|
||||
|
||||
if (clicked.attribute_value("label", Label()) == popup_label)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/* remove popup dialog when clicking somewhere outside */
|
||||
if (click_outside_popup() && _popup.state == Popup::VISIBLE
|
||||
&& !_graph.add_button_hovered()) {
|
||||
|
||||
_popup.state = Popup::OFF;
|
||||
_popup_dialog.reset();
|
||||
discard_construction();
|
||||
|
||||
/* de-select '+' button */
|
||||
_graph_menu_view.generate();
|
||||
|
||||
/* remove popup window from window layout */
|
||||
_handle_window_layout();
|
||||
}
|
||||
|
||||
if (_main_menu_view.hovered(seq)) {
|
||||
_main_menu_view.generate();
|
||||
_clicked_seq_number.destruct();
|
||||
}
|
||||
else if (_graph_menu_view.hovered(seq)) {
|
||||
_graph.click(*this);
|
||||
_graph_menu_view.generate();
|
||||
_clicked_seq_number.destruct();
|
||||
}
|
||||
else if (_popup_menu_view.hovered(seq)) {
|
||||
_popup_dialog.click(*this);
|
||||
_popup_menu_view.generate();
|
||||
_clicked_seq_number.destruct();
|
||||
}
|
||||
else if (_panel_menu_view.hovered(seq)) {
|
||||
_panel_dialog.click(*this);
|
||||
_clicked_seq_number.destruct();
|
||||
}
|
||||
else if (_settings_menu_view.hovered(seq)) {
|
||||
_settings_dialog.click(*this);
|
||||
_settings_menu_view.generate();
|
||||
_clicked_seq_number.destruct();
|
||||
}
|
||||
else if (_network.dialog_hovered(seq)) {
|
||||
_network.dialog.click(_network);
|
||||
_network.update_view();
|
||||
_clicked_seq_number.destruct();
|
||||
}
|
||||
else if (_file_browser_menu_view.hovered(seq)) {
|
||||
_file_browser_dialog.click(*this);
|
||||
_file_browser_menu_view.generate();
|
||||
_clicked_seq_number.destruct();
|
||||
}
|
||||
}
|
||||
|
||||
void _try_handle_clack()
|
||||
{
|
||||
if (!_clacked_seq_number.constructed())
|
||||
return;
|
||||
|
||||
Input::Seq_number const seq = *_clacked_seq_number;
|
||||
|
||||
if (_main_menu_view.hovered(seq)) {
|
||||
_storage.dialog.clack(_storage);
|
||||
_main_menu_view.generate();
|
||||
_clacked_seq_number.destruct();
|
||||
}
|
||||
else if (_graph_menu_view.hovered(seq)) {
|
||||
_graph.clack(*this, _storage);
|
||||
_graph_menu_view.generate();
|
||||
_clacked_seq_number.destruct();
|
||||
}
|
||||
else if (_popup_menu_view.hovered(seq)) {
|
||||
_popup_dialog.clack(*this);
|
||||
_clacked_seq_number.destruct();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu_view::Hover_update_handler interface
|
||||
*/
|
||||
void menu_view_hover_updated() override
|
||||
{
|
||||
if (_clicked_seq_number.constructed())
|
||||
_try_handle_click();
|
||||
|
||||
if (_clacked_seq_number.constructed())
|
||||
_try_handle_clack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Input_event_handler interface
|
||||
*/
|
||||
@ -556,60 +688,13 @@ struct Sculpt::Main : Input_event_handler,
|
||||
bool need_generate_dialog = false;
|
||||
|
||||
if (ev.key_press(Input::BTN_LEFT)) {
|
||||
|
||||
/* remove popup dialog when clicking somewhere outside */
|
||||
if (!_popup_menu_view.hovered() && _popup.state == Popup::VISIBLE
|
||||
&& !_graph.add_button_hovered()) {
|
||||
|
||||
_popup.state = Popup::OFF;
|
||||
_popup_dialog.reset();
|
||||
discard_construction();
|
||||
|
||||
/* de-select '+' button */
|
||||
_graph_menu_view.generate();
|
||||
|
||||
/* remove popup window from window layout */
|
||||
_handle_window_layout();
|
||||
}
|
||||
|
||||
if (_main_menu_view.hovered()) {
|
||||
_main_menu_view.generate();
|
||||
}
|
||||
else if (_graph_menu_view.hovered()) {
|
||||
_graph.click(*this);
|
||||
_graph_menu_view.generate();
|
||||
}
|
||||
else if (_popup_menu_view.hovered()) {
|
||||
_popup_dialog.click(*this);
|
||||
_popup_menu_view.generate();
|
||||
}
|
||||
else if (_panel_menu_view.hovered()) {
|
||||
_panel_dialog.click(*this);
|
||||
}
|
||||
else if (_settings_menu_view.hovered()) {
|
||||
_settings_dialog.click(*this);
|
||||
_settings_menu_view.generate();
|
||||
}
|
||||
else if (_network.dialog_hovered()) {
|
||||
_network.dialog.click(_network);
|
||||
_network.update_view();
|
||||
}
|
||||
else if (_file_browser_menu_view.hovered()) {
|
||||
_file_browser_dialog.click(*this);
|
||||
_file_browser_menu_view.generate();
|
||||
}
|
||||
_clicked_seq_number.construct(_global_input_seq_number);
|
||||
_try_handle_click();
|
||||
}
|
||||
|
||||
if (ev.key_release(Input::BTN_LEFT)) {
|
||||
if (_main_menu_view.hovered()) {
|
||||
_storage.dialog.clack(_storage);
|
||||
_main_menu_view.generate();
|
||||
}
|
||||
else if (_graph_menu_view.hovered()) {
|
||||
_graph.clack(*this, _storage);
|
||||
_graph_menu_view.generate();
|
||||
}
|
||||
else if (_popup_menu_view.hovered()) _popup_dialog.clack(*this);
|
||||
_clacked_seq_number.construct(_global_input_seq_number);
|
||||
_try_handle_clack();
|
||||
}
|
||||
|
||||
if (_keyboard_focus.target == Keyboard_focus::WPA_PASSPHRASE)
|
||||
@ -1021,17 +1106,17 @@ struct Sculpt::Main : Input_event_handler,
|
||||
|
||||
Menu_view _panel_menu_view { _env, _child_states, _panel_dialog, "panel_view",
|
||||
Ram_quota{4*1024*1024}, Cap_quota{150},
|
||||
"panel_dialog", "panel_view_hover" };
|
||||
"panel_dialog", "panel_view_hover", *this };
|
||||
|
||||
Settings_dialog _settings_dialog { _settings };
|
||||
|
||||
Menu_view _settings_menu_view { _env, _child_states, _settings_dialog, "settings_view",
|
||||
Ram_quota{4*1024*1024}, Cap_quota{150},
|
||||
"settings_dialog", "settings_view_hover" };
|
||||
"settings_dialog", "settings_view_hover", *this };
|
||||
|
||||
Menu_view _main_menu_view { _env, _child_states, *this, "menu_view",
|
||||
Ram_quota{4*1024*1024}, Cap_quota{150},
|
||||
"menu_dialog", "menu_view_hover" };
|
||||
"menu_dialog", "menu_view_hover", *this };
|
||||
|
||||
Popup_dialog _popup_dialog { _env, *this, _launchers,
|
||||
_network._nic_state, _network._nic_target,
|
||||
@ -1040,13 +1125,13 @@ struct Sculpt::Main : Input_event_handler,
|
||||
|
||||
Menu_view _popup_menu_view { _env, _child_states, _popup_dialog, "popup_view",
|
||||
Ram_quota{4*1024*1024}, Cap_quota{150},
|
||||
"popup_dialog", "popup_view_hover" };
|
||||
"popup_dialog", "popup_view_hover", *this };
|
||||
|
||||
File_browser_dialog _file_browser_dialog { _cached_runtime_config, _file_browser_state };
|
||||
|
||||
Menu_view _file_browser_menu_view { _env, _child_states, _file_browser_dialog, "file_browser_view",
|
||||
Ram_quota{8*1024*1024}, Cap_quota{150},
|
||||
"file_browser_dialog", "file_browser_view_hover" };
|
||||
"file_browser_dialog", "file_browser_view_hover", *this };
|
||||
|
||||
/**
|
||||
* Popup_dialog::Refresh interface
|
||||
@ -1098,7 +1183,7 @@ struct Sculpt::Main : Input_event_handler,
|
||||
|
||||
Menu_view _graph_menu_view { _env, _child_states, _graph, "runtime_view",
|
||||
Ram_quota{8*1024*1024}, Cap_quota{200},
|
||||
"runtime_dialog", "runtime_view_hover" };
|
||||
"runtime_dialog", "runtime_view_hover", *this };
|
||||
Main(Env &env) : _env(env)
|
||||
{
|
||||
_manual_deploy_rom.sigh(_manual_deploy_handler);
|
||||
@ -1117,6 +1202,7 @@ struct Sculpt::Main : Input_event_handler,
|
||||
_launcher_listing_rom.sigh(_launcher_listing_handler);
|
||||
_blueprint_rom .sigh(_blueprint_handler);
|
||||
_editor_saved_rom .sigh(_editor_saved_handler);
|
||||
_clicked_rom .sigh(_clicked_handler);
|
||||
|
||||
/*
|
||||
* Generate initial configurations
|
||||
@ -1132,6 +1218,7 @@ struct Sculpt::Main : Input_event_handler,
|
||||
_deploy.handle_deploy();
|
||||
_handle_pci_devices();
|
||||
_handle_runtime_config();
|
||||
_handle_clicked();
|
||||
|
||||
/*
|
||||
* Read static platform information
|
||||
@ -1849,6 +1936,9 @@ void Sculpt::Main::_generate_event_filter_config(Xml_generator &xml)
|
||||
});
|
||||
});
|
||||
|
||||
xml.node("touch-click", [&] () {
|
||||
gen_input("touch"); });
|
||||
|
||||
gen_input("usb");
|
||||
gen_input("touch");
|
||||
gen_input("sdl");
|
||||
|
@ -28,7 +28,14 @@ void Menu_view::_handle_hover()
|
||||
|
||||
Hover_result hover_result = Hover_result::UNMODIFIED;
|
||||
|
||||
_hover_rom.xml().with_sub_node("dialog", [&] (Xml_node hover) {
|
||||
Xml_node hover = _hover_rom.xml();
|
||||
|
||||
if (hover.has_attribute("seq_number")) {
|
||||
Input::Seq_number const seq { hover.attribute_value("seq_number", 0U) };
|
||||
_seq_number.construct(seq);
|
||||
}
|
||||
|
||||
hover.with_sub_node("dialog", [&] (Xml_node hover) {
|
||||
_hovered = true;
|
||||
hover_result = _dialog.hover(hover);
|
||||
});
|
||||
@ -41,6 +48,9 @@ void Menu_view::_handle_hover()
|
||||
|
||||
if (dialog_hover_changed || widget_hover_changed)
|
||||
generate();
|
||||
|
||||
/* trigger handling of pending click/clack actions */
|
||||
_hover_update_handler.menu_view_hover_updated();
|
||||
}
|
||||
|
||||
|
||||
@ -48,9 +58,11 @@ Menu_view::Menu_view(Env &env, Registry<Child_state> ®istry,
|
||||
Dialog &dialog, Start_name const &name,
|
||||
Ram_quota ram_quota, Cap_quota cap_quota,
|
||||
Session_label const &dialog_report_name,
|
||||
Session_label const &hover_rom_name)
|
||||
Session_label const &hover_rom_name,
|
||||
Hover_update_handler &hover_update_handler)
|
||||
:
|
||||
_dialog(dialog),
|
||||
_hover_update_handler(hover_update_handler),
|
||||
_child_state(registry, name, Priority::LEITZENTRALE, ram_quota, cap_quota),
|
||||
_dialog_reporter(env, "dialog", dialog_report_name.string()),
|
||||
_hover_rom(env, hover_rom_name.string()),
|
||||
|
@ -29,8 +29,15 @@ namespace Sculpt { struct Menu_view; }
|
||||
|
||||
struct Sculpt::Menu_view : Noncopyable
|
||||
{
|
||||
struct Hover_update_handler : Interface, Noncopyable
|
||||
{
|
||||
virtual void menu_view_hover_updated() = 0;
|
||||
};
|
||||
|
||||
Dialog &_dialog;
|
||||
|
||||
Hover_update_handler &_hover_update_handler;
|
||||
|
||||
Child_state _child_state;
|
||||
|
||||
Expanding_reporter _dialog_reporter;
|
||||
@ -41,6 +48,8 @@ struct Sculpt::Menu_view : Noncopyable
|
||||
|
||||
bool _hovered = false;
|
||||
|
||||
Constructible<Input::Seq_number> _seq_number { };
|
||||
|
||||
unsigned min_width = 0;
|
||||
unsigned min_height = 0;
|
||||
|
||||
@ -51,11 +60,18 @@ struct Sculpt::Menu_view : Noncopyable
|
||||
Menu_view(Env &, Registry<Child_state> ®istry,
|
||||
Dialog &, Start_name const &, Ram_quota, Cap_quota,
|
||||
Session_label const &dialog_report_name,
|
||||
Session_label const &hover_rom_name);
|
||||
Session_label const &hover_rom_name,
|
||||
Hover_update_handler &);
|
||||
|
||||
void generate();
|
||||
|
||||
bool hovered() const { return _hovered; }
|
||||
bool hovered(Input::Seq_number const &seq_number) const
|
||||
{
|
||||
if (!_seq_number.constructed() || !_hovered)
|
||||
return false;
|
||||
|
||||
return (_seq_number->value >= seq_number.value);
|
||||
}
|
||||
|
||||
void reset();
|
||||
|
||||
|
@ -37,6 +37,8 @@ struct Sculpt::Network : Network_dialog::Action
|
||||
|
||||
Registry<Child_state> &_child_states;
|
||||
|
||||
Menu_view::Hover_update_handler &_hover_update_handler;
|
||||
|
||||
Runtime_config_generator &_runtime_config_generator;
|
||||
|
||||
Runtime_info const &_runtime_info;
|
||||
@ -103,11 +105,12 @@ struct Sculpt::Network : Network_dialog::Action
|
||||
|
||||
Menu_view _menu_view { _env, _child_states, dialog, "network_view",
|
||||
Ram_quota{4*1024*1024}, Cap_quota{150},
|
||||
"network_dialog", "network_view_hover" };
|
||||
"network_dialog", "network_view_hover",
|
||||
_hover_update_handler };
|
||||
|
||||
void min_dialog_width(unsigned value) { _menu_view.min_width = value; }
|
||||
|
||||
bool dialog_hovered() const { return _menu_view.hovered(); }
|
||||
bool dialog_hovered(Input::Seq_number seq) const { return _menu_view.hovered(seq); }
|
||||
|
||||
void update_view() { _menu_view.generate(); }
|
||||
|
||||
@ -223,10 +226,12 @@ struct Sculpt::Network : Network_dialog::Action
|
||||
}
|
||||
|
||||
Network(Env &env, Allocator &alloc, Registry<Child_state> &child_states,
|
||||
Menu_view::Hover_update_handler &hover_update_handler,
|
||||
Runtime_config_generator &runtime_config_generator,
|
||||
Runtime_info const &runtime_info, Pci_info const &pci_info)
|
||||
:
|
||||
_env(env), _alloc(alloc), _child_states(child_states),
|
||||
_hover_update_handler(hover_update_handler),
|
||||
_runtime_config_generator(runtime_config_generator),
|
||||
_runtime_info(runtime_info), _pci_info(pci_info)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user