diff --git a/repos/gems/include/dialog/sandboxed_runtime.h b/repos/gems/include/dialog/sandboxed_runtime.h index abe02ccb75..33f10fd029 100644 --- a/repos/gems/include/dialog/sandboxed_runtime.h +++ b/repos/gems/include/dialog/sandboxed_runtime.h @@ -38,6 +38,8 @@ class Dialog::Sandboxed_runtime : Noncopyable template class Event_handler; + using Start_name = String<128>; + private: Env &_env; @@ -96,219 +98,15 @@ class Dialog::Sandboxed_runtime : Noncopyable Rom_service _rom_service; Report_service _report_service; - public: - - Sandboxed_runtime(Env &, Allocator &, Sandbox &); - - /** - * Respond to sandbox state changes - * - * \return true if the sandbox configuration needs to be updated - */ - bool apply_sandbox_state(Xml_node const &); - - void gen_start_nodes(Xml_generator &) const; -}; - - -class Dialog::Sandboxed_runtime::Report_session : public Session_object -{ - public: - - struct Handler : Interface, Genode::Noncopyable - { - virtual void handle_report() = 0; - }; - - private: - - Attached_ram_dataspace _client_ds; - Attached_ram_dataspace _local_ds; - - Constructible _xml { }; /* points inside _local_ds */ - - Handler &_handler; - - - /******************************* - ** Report::Session interface ** - *******************************/ - - Dataspace_capability dataspace() override { return _client_ds.cap(); } - - void submit(size_t length) override - { - size_t const num_bytes = min(_client_ds.size(), length); - - memcpy(_local_ds.local_addr(), _client_ds.local_addr(), - num_bytes); - - _xml.destruct(); - - try { _xml.construct(_local_ds.local_addr(), num_bytes); } - catch (...) { } - - _handler.handle_report(); - } - - void response_sigh(Signal_context_capability) override { } - - size_t obtain_response() override { return 0; } - - public: - - template - Report_session(Env &env, Handler &handler, - Entrypoint &ep, Resources const &resources, - ARGS &&... args) - : - Session_object(ep, resources, args...), - _client_ds(env.ram(), env.rm(), resources.ram_quota.value/2), - _local_ds (env.ram(), env.rm(), resources.ram_quota.value/2), - _handler(handler) - { } - - template - void with_xml(FN const &fn) const - { - if (_xml.constructed()) - fn(*_xml); - else - fn(Xml_node("")); - } -}; - - -class Dialog::Sandboxed_runtime::View : private Views::Element -{ - private: - - /* needed for privately inheriting 'Views::Element' */ - friend class Dictionary; - friend class Avl_node; - friend class Avl_tree; - friend class Sandboxed_runtime; - - Env &_env; - - Allocator &_alloc; - - Event::Seq_number &_global_seq_number; - - Optional_event_handler &_optional_event_handler; - - Top_level_dialog &_dialog; - - bool _dialog_hovered = false; /* used to cut hover feedback loop */ - - /* sequence numbers to correlate hover info with click/clack events */ - Event::Seq_number _hover_seq_number { }; - Constructible _click_seq_number { }; - Constructible _clack_seq_number { }; - - bool _click_delivered = false; /* used to deliver each click only once */ - - bool _dragged() const - { - return _click_seq_number.constructed() - && *_click_seq_number == _global_seq_number - && _click_delivered; - } - - bool _hover_observable_without_click = false; - - struct Rom_producer : Dynamic_rom_session::Xml_producer - { - View const &_view; - - Rom_producer(View const &view) - : - Dynamic_rom_session::Xml_producer("dialog"), - _view(view) - { } - - void produce_xml(Xml_generator &xml) override - { - _view._with_dialog_hover([&] (Xml_node const &hover) { - - Event::Dragged const dragged { _view._dragged() }; - - bool const supply_hover = _view._hover_observable_without_click - || dragged.value; - - static Xml_node omitted_hover(""); - - At const at { _view._global_seq_number, - supply_hover ? hover : omitted_hover }; - - Scope<> top_level_scope(xml, at, dragged, { _view._dialog.name }); - - _view._dialog.view(top_level_scope); - }); - } - } _dialog_producer { *this }; - - Dynamic_rom_session _dialog_rom_session { - _env.ep(), _env.ram(), _env.rm(), _dialog_producer }; - - template - struct Hover_handler : Report_session::Handler - { - T &_obj; - void (T::*_member) (); - - Hover_handler(T &obj, void (T::*member)()) - : _obj(obj), _member(member) { } - - void handle_report() override - { - (_obj.*_member)(); - } - }; - - Constructible _hover_report_session { }; - - template - void _with_dialog_hover(FN const &fn) const - { - bool done = false; - - if (_hover_report_session.constructed()) - _hover_report_session->with_xml([&] (Xml_node const &hover) { - hover.with_optional_sub_node("dialog", [&] (Xml_node const &dialog) { - fn(dialog); - done = true; }); }); - - if (!done) - fn(Xml_node("")); - } - - void _handle_input_event(Input::Event const &); - - void _handle_hover(); - - Hover_handler _hover_handler { *this, &View::_handle_hover }; - - void _try_handle_click_and_clack(); - struct Menu_view_state { - using Start_name = String<128>; - Start_name const name; - Ram_quota const initial_ram; - Cap_quota const initial_caps; + Ram_quota const initial_ram { 4*1024*1024 }; + Cap_quota const initial_caps { 100 }; Ram_quota ram = initial_ram; Cap_quota caps = initial_caps; - int xpos = 0, ypos = 0; - - unsigned min_width = 0, min_height = 0; - - bool opaque = false; - Color background { }; - unsigned version = 0; void trigger_restart() @@ -348,45 +146,226 @@ class Dialog::Sandboxed_runtime::View : private Views::Element return result; } - void gen_start_node(Xml_generator &) const; + void gen_start_node(Xml_generator &, Views const &) const; } _menu_view_state; + class Report_session : public Session_object + { + public: + + struct Handler : Interface, Genode::Noncopyable + { + virtual void handle_report() = 0; + }; + + private: + + Attached_ram_dataspace _client_ds; + Attached_ram_dataspace _local_ds; + + Constructible _xml { }; /* points inside _local_ds */ + + Handler &_handler; + + + /******************************* + ** Report::Session interface ** + *******************************/ + + Dataspace_capability dataspace() override { return _client_ds.cap(); } + + void submit(size_t length) override + { + size_t const num_bytes = min(_client_ds.size(), length); + + memcpy(_local_ds.local_addr(), _client_ds.local_addr(), + num_bytes); + + _xml.destruct(); + + try { _xml.construct(_local_ds.local_addr(), num_bytes); } + catch (...) { } + + _handler.handle_report(); + } + + void response_sigh(Signal_context_capability) override { } + + size_t obtain_response() override { return 0; } + + public: + + template + Report_session(Env &env, Handler &handler, + Entrypoint &ep, Resources const &resources, + ARGS &&... args) + : + Session_object(ep, resources, args...), + _client_ds(env.ram(), env.rm(), resources.ram_quota.value/2), + _local_ds (env.ram(), env.rm(), resources.ram_quota.value/2), + _handler(handler) + { } + + template + void with_xml(FN const &fn) const + { + if (_xml.constructed()) + fn(*_xml); + else + fn(Xml_node("")); + } + }; + + template + struct Hover_handler : Report_session::Handler + { + T &_obj; + void (T::*_member) (); + + Hover_handler(T &obj, void (T::*member)()) + : _obj(obj), _member(member) { } + + void handle_report() override + { + (_obj.*_member)(); + } + }; + + Top_level_dialog::Name _hovered_dialog { }; + + Hover_handler _hover_handler { + *this, &Sandboxed_runtime::_handle_hover }; + + void _handle_hover(); + + Constructible _hover_report_session { }; + + Event::Seq_number _hover_seq_number { }; + + public: + + struct Attr { Start_name name; }; + + Sandboxed_runtime(Env &, Allocator &, Sandbox &, + Attr const &attr = { "view" }); + + /** + * Respond to sandbox state changes + * + * \return true if the sandbox configuration needs to be updated + */ + bool apply_sandbox_state(Xml_node const &); + + void gen_start_nodes(Xml_generator &) const; +}; + + +class Dialog::Sandboxed_runtime::View : private Views::Element +{ + private: + + /* needed for privately inheriting 'Views::Element' */ + friend class Dictionary; + friend class Avl_node; + friend class Avl_tree; + friend class Sandboxed_runtime; + + Env &_env; + + Sandboxed_runtime &_runtime; + + Top_level_dialog &_dialog; + + bool _dialog_hovered = false; + + /* sequence numbers to correlate hover info with click/clack events */ + Constructible _click_seq_number { }; + Constructible _clack_seq_number { }; + + bool _click_delivered = false; /* used to deliver each click only once */ + + bool _dragged() const + { + return _click_seq_number.constructed() + && *_click_seq_number == _runtime._global_seq_number + && _click_delivered; + } + + bool _hover_observable_without_click = false; + + void _with_dialog_hover(auto const &fn) const + { + bool done = false; + + if (_runtime._hover_report_session.constructed()) + _runtime._hover_report_session->with_xml([&] (Xml_node const &hover) { + hover.with_optional_sub_node("dialog", [&] (Xml_node const &dialog) { + fn(dialog); + done = true; }); }); + + if (!done) + fn(Xml_node("")); + } + + struct Rom_producer : Dynamic_rom_session::Xml_producer + { + View const &_view; + + Rom_producer(View const &view) + : + Dynamic_rom_session::Xml_producer("dialog"), + _view(view) + { } + + void produce_xml(Xml_generator &xml) override + { + _view._with_dialog_hover([&] (Xml_node const &hover) { + + Event::Dragged const dragged { _view._dragged() }; + + bool const supply_hover = _view._hover_observable_without_click + || dragged.value; + + static Xml_node omitted_hover(""); + + At const at { _view._runtime._global_seq_number, + supply_hover ? hover : omitted_hover }; + + Scope<> top_level_scope(xml, at, dragged, { _view._dialog.name }); + + _view._dialog.view(top_level_scope); + }); + } + } _dialog_producer { *this }; + + Dynamic_rom_session _dialog_rom_session { + _env.ep(), _env.ram(), _env.rm(), _dialog_producer }; + + void _gen_menu_view_dialog(Xml_generator &) const; + void _gen_menu_view_routes(Xml_generator &) const; + + void _handle_input_event(Input::Event const &); + + void _handle_hover(); + void _leave(); + void _try_handle_click_and_clack(); + Registry _gui_sessions { }; public: - int &xpos = _menu_view_state.xpos; - int &ypos = _menu_view_state.ypos; - unsigned &min_width = _menu_view_state.min_width; - unsigned &min_height = _menu_view_state.min_height; - bool &opaque = _menu_view_state.opaque; - Color &background = _menu_view_state.background; - - struct Attr - { - bool opaque; - Ram_quota initial_ram; - }; - - View(Sandboxed_runtime &runtime, Top_level_dialog &dialog, Attr const attr) - : - Views::Element(runtime._views, dialog.name), - _env(runtime._env), _alloc(runtime._alloc), - _global_seq_number(runtime._global_seq_number), - _optional_event_handler(runtime._optional_event_handler), - _dialog(dialog), - _menu_view_state({ - .name = dialog.name, - .initial_ram = attr.initial_ram, - .initial_caps = Cap_quota { 200 } - }) - { } + int xpos { }; + int ypos { }; + unsigned min_width { }; + unsigned min_height { }; + bool opaque { }; + Color background { }; View(Sandboxed_runtime &runtime, Top_level_dialog &dialog) : - View(runtime, dialog, Attr { .opaque = false, - .initial_ram = { 4*1024*1024 } }) + Views::Element(runtime._views, dialog.name), + _env(runtime._env), _runtime(runtime), _dialog(dialog) { } ~View(); diff --git a/repos/gems/src/app/touch_keyboard/main.cc b/repos/gems/src/app/touch_keyboard/main.cc index 5ff2f4ee97..bf139601f6 100644 --- a/repos/gems/src/app/touch_keyboard/main.cc +++ b/repos/gems/src/app/touch_keyboard/main.cc @@ -44,10 +44,7 @@ struct Touch_keyboard::Main : Top_level_dialog Runtime _runtime { _env, _heap }; - Runtime::View _view { _runtime, *this, Runtime::View::Attr { - .opaque = true, - .initial_ram = Ram_quota { 4*1024*1024 } - } }; + Runtime::View _view { _runtime, *this }; /* * Top_level_dialog interface diff --git a/repos/gems/src/lib/dialog/sandboxed_runtime.cc b/repos/gems/src/lib/dialog/sandboxed_runtime.cc index ef76727cd5..072da517ca 100644 --- a/repos/gems/src/lib/dialog/sandboxed_runtime.cc +++ b/repos/gems/src/lib/dialog/sandboxed_runtime.cc @@ -82,8 +82,9 @@ struct Sandboxed_runtime::Gui_session : Session_object if (clack(ev)) _clicked = false; if (orig_clicked != _clicked) { - _view._global_seq_number.value++; - _input_component.submit(Input::Seq_number { _view._global_seq_number.value }); + Event::Seq_number &global_seq = _view._runtime._global_seq_number; + global_seq.value++; + _input_component.submit(Input::Seq_number { global_seq.value }); } /* local event (click/clack) handling */ @@ -161,12 +162,14 @@ struct Sandboxed_runtime::Gui_session : Session_object }; -Sandboxed_runtime::Sandboxed_runtime(Env &env, Allocator &alloc, Sandbox &sandbox) +Sandboxed_runtime::Sandboxed_runtime(Env &env, Allocator &alloc, Sandbox &sandbox, + Attr const &attr) : _env(env), _alloc(alloc), _sandbox(sandbox), _gui_service (_sandbox, _gui_handler), _rom_service (_sandbox, _rom_handler), - _report_service(_sandbox, _report_handler) + _report_service(_sandbox, _report_handler), + _menu_view_state { .name = attr.name } { } @@ -175,14 +178,8 @@ bool Sandboxed_runtime::apply_sandbox_state(Xml_node const &state) bool reconfiguration_needed = false; state.for_each_sub_node("child", [&] (Xml_node const &child) { - using Name = Top_level_dialog::Name; - Name const name = child.attribute_value("name", Name()); - _views.with_element(name, - [&] (View &view) { - if (view._menu_view_state.apply_child_state_report(child)) - reconfiguration_needed = true; }, - [&] /* no view named after this child */ { }); - }); + if (_menu_view_state.apply_child_state_report(child)) + reconfiguration_needed = true; }); return reconfiguration_needed; } @@ -191,12 +188,10 @@ bool Sandboxed_runtime::apply_sandbox_state(Xml_node const &state) void Sandboxed_runtime::_handle_rom_service() { _rom_service.for_each_requested_session([&] (Rom_service::Request &request) { - if (request.label.last_element() == "dialog") { - _views.with_element(request.label.prefix(), - [&] (View &view) { - request.deliver_session(view._dialog_rom_session); }, - [&] /* no view named after this child */ { }); - } + _views.with_element(request.label.last_element(), + [&] (View &view) { + request.deliver_session(view._dialog_rom_session); }, + [&] { }); }); _rom_service.for_each_session_to_close([&] (Dynamic_rom_session &) { @@ -209,14 +204,10 @@ void Sandboxed_runtime::_handle_rom_service() void Sandboxed_runtime::_handle_report_service() { _report_service.for_each_requested_session([&] (Report_service::Request &request) { - if (request.label.last_element() == "hover") { - _views.with_element(request.label.prefix(), - [&] (View &view) { - view._hover_report_session.construct(_env, view._hover_handler, _env.ep(), - request.resources, "", request.diag); - request.deliver_session(*view._hover_report_session); - }, - [&] /* no view named after this child */ { }); + if (request.label == Start_name { _menu_view_state.name, " -> hover" }) { + _hover_report_session.construct(_env, _hover_handler, _env.ep(), + request.resources, "", request.diag); + request.deliver_session(*_hover_report_session); } }); @@ -230,16 +221,16 @@ void Sandboxed_runtime::_handle_report_service() void Sandboxed_runtime::_handle_gui_service() { _gui_service.for_each_requested_session([&] (Gui_service::Request &request) { - _views.with_element(request.label.prefix(), + _views.with_element(request.label.last_element(), [&] (View &view) { Gui_session &session = *new (_alloc) Gui_session(_env, view, _env.ep(), request.resources, "", request.diag); request.deliver_session(session); }, - [&] { - warning("unexpected GUI-sesssion request, label=", request.label); - }); + [&] { + warning("unexpected GUI-sesssion request, label=", request.label); + }); }); _gui_service.for_each_upgraded_session([&] (Gui_session &session, @@ -257,12 +248,11 @@ void Sandboxed_runtime::_handle_gui_service() void Sandboxed_runtime::gen_start_nodes(Xml_generator &xml) const { - _views.for_each([&] (View const &view) { - view._menu_view_state.gen_start_node(xml); }); + _menu_view_state.gen_start_node(xml, _views); } -void Sandboxed_runtime::View::Menu_view_state::gen_start_node(Xml_generator &xml) const +void Sandboxed_runtime::Menu_view_state::gen_start_node(Xml_generator &xml, Views const &views) const { xml.node("start", [&] () { @@ -280,14 +270,6 @@ void Sandboxed_runtime::View::Menu_view_state::gen_start_node(Xml_generator &xml xml.node("config", [&] () { - if (xpos) xml.attribute("xpos", xpos); - if (ypos) xml.attribute("ypos", ypos); - if (min_width) xml.attribute("width", min_width); - if (min_height) xml.attribute("height", min_height); - if (opaque) xml.attribute("opaque", "yes"); - - xml.attribute("background", String<20>(background)); - xml.node("report", [&] () { xml.attribute("hover", "yes"); }); @@ -308,15 +290,15 @@ void Sandboxed_runtime::View::Menu_view_state::gen_start_node(Xml_generator &xml }); }); }); + + views.for_each([&] (View const &view) { + view._gen_menu_view_dialog(xml); }); }); xml.node("route", [&] () { - xml.node("service", [&] () { - xml.attribute("name", "ROM"); - xml.attribute("label", "dialog"); - xml.node("local", [&] () { }); - }); + views.for_each([&] (View const &view) { + view._gen_menu_view_routes(xml); }); xml.node("service", [&] () { xml.attribute("name", "Report"); @@ -336,44 +318,118 @@ void Sandboxed_runtime::View::Menu_view_state::gen_start_node(Xml_generator &xml xml.attribute("label", "fonts"); }); }); - xml.node("any-service", [&] () { - xml.node("parent", [&] () { }); }); + auto parent_route = [&] (auto const &service) + { + xml.node("service", [&] { + xml.attribute("name", service); + xml.node("parent", [&] { }); }); + }; + + parent_route("PD"); + parent_route("CPU"); + parent_route("LOG"); + parent_route("Timer"); + + auto parent_rom_route = [&] (auto const &name) + { + xml.node("service", [&] () { + xml.attribute("name", "ROM"); + xml.attribute("label_last", name); + xml.node("parent", [&] { }); }); + }; + + parent_rom_route("menu_view"); + parent_rom_route("ld.lib.so"); + parent_rom_route("libc.lib.so"); + parent_rom_route("libm.lib.so"); + parent_rom_route("libpng.lib.so"); + parent_rom_route("zlib.lib.so"); + parent_rom_route("vfs.lib.so"); + parent_rom_route("menu_view_styles.tar"); }); }); } +void Sandboxed_runtime::View::_gen_menu_view_dialog(Xml_generator &xml) const +{ + xml.node("dialog", [&] { + xml.attribute("name", name); + + if (xpos) xml.attribute("xpos", xpos); + if (ypos) xml.attribute("ypos", ypos); + if (min_width) xml.attribute("width", min_width); + if (min_height) xml.attribute("height", min_height); + if (opaque) xml.attribute("opaque", "yes"); + + xml.attribute("background", String<20>(background)); + }); +} + + +void Sandboxed_runtime::View::_gen_menu_view_routes(Xml_generator &xml) const +{ + xml.node("service", [&] { + xml.attribute("name", "ROM"); + xml.attribute("label", name); + xml.node("local", [&] { }); + }); +} + + void Sandboxed_runtime::View::_handle_input_event(Input::Event const &event) { if (event.absolute_motion()) _hover_observable_without_click = true; if (event.touch()) _hover_observable_without_click = false; + Event::Seq_number const global_seq = _runtime._global_seq_number; + if (click(event) && !_click_seq_number.constructed()) { - _click_seq_number.construct(_global_seq_number); + _click_seq_number.construct(global_seq); _click_delivered = false; } if (clack(event)) - _clack_seq_number.construct(_global_seq_number); + _clack_seq_number.construct(global_seq); _try_handle_click_and_clack(); - _optional_event_handler.handle_event(Event { _global_seq_number, event }); + _runtime._optional_event_handler.handle_event(Event { global_seq, event }); +} + + +void Sandboxed_runtime::_handle_hover() +{ + if (!_hover_report_session.constructed()) + return; + + using Name = Top_level_dialog::Name; + Name const orig_hovered_dialog = _hovered_dialog; + + _hover_report_session->with_xml([&] (Xml_node const &hover) { + _hover_seq_number = { hover.attribute_value("seq_number", 0U) }; + + hover.with_sub_node("dialog", + [&] (Xml_node const &dialog) { + _hovered_dialog = dialog.attribute_value("name", Name()); }, + [&] { _hovered_dialog = { }; }); + }); + + if (orig_hovered_dialog.valid() && orig_hovered_dialog != _hovered_dialog) + _views.with_element(orig_hovered_dialog, + [&] (View &view) { view._leave(); }, + [&] { }); + + if (_hovered_dialog.valid()) + _views.with_element(_hovered_dialog, + [&] (View &view) { view._handle_hover(); }, + [&] { }); } void Sandboxed_runtime::View::_handle_hover() { - bool const orig_dialog_hovered = _dialog_hovered; - - if (_hover_report_session.constructed()) - _hover_report_session->with_xml([&] (Xml_node const &hover) { - _hover_seq_number = { hover.attribute_value("seq_number", 0U) }; - _dialog_hovered = (hover.num_sub_nodes() > 0); - }); - - if (orig_dialog_hovered != _dialog_hovered || _dialog_hovered) - _dialog_rom_session.trigger_update(); + _dialog_hovered = true; if (_click_delivered && _click_seq_number.constructed()) { _with_dialog_hover([&] (Xml_node const &hover) { @@ -382,16 +438,24 @@ void Sandboxed_runtime::View::_handle_hover() }); } + _dialog_rom_session.trigger_update(); _try_handle_click_and_clack(); } +void Sandboxed_runtime::View::_leave() +{ + _dialog_hovered = false; + _dialog_rom_session.trigger_update(); +} + + void Sandboxed_runtime::View::_try_handle_click_and_clack() { Constructible &click = _click_seq_number, &clack = _clack_seq_number; - if (!_click_delivered && click.constructed() && *click == _hover_seq_number) { + if (!_click_delivered && click.constructed() && *click == _runtime._hover_seq_number) { _with_dialog_hover([&] (Xml_node const &hover) { Clicked_at at(*click, hover); _dialog.click(at); @@ -399,7 +463,7 @@ void Sandboxed_runtime::View::_try_handle_click_and_clack() }); } - if (click.constructed() && clack.constructed() && *clack== _hover_seq_number) { + if (click.constructed() && clack.constructed() && *clack == _runtime._hover_seq_number) { _with_dialog_hover([&] (Xml_node const &hover) { /* use click seq number for to associate clack with click */ Clacked_at at(*click, hover); @@ -415,5 +479,5 @@ void Sandboxed_runtime::View::_try_handle_click_and_clack() Sandboxed_runtime::View::~View() { _gui_sessions.for_each([&] (Gui_session &session) { - destroy(_alloc, &session); }); + destroy(_runtime._alloc, &session); }); }