diff --git a/repos/gems/src/app/themed_decorator/theme.h b/repos/gems/src/app/themed_decorator/theme.h index 432fceba26..32988982f6 100644 --- a/repos/gems/src/app/themed_decorator/theme.h +++ b/repos/gems/src/app/themed_decorator/theme.h @@ -15,6 +15,7 @@ #define _THEME_H_ /* Genode includes */ +#include #include #include #include diff --git a/repos/gems/src/server/gui_fader/main.cc b/repos/gems/src/server/gui_fader/main.cc index f839f7169b..61065725ca 100644 --- a/repos/gems/src/server/gui_fader/main.cc +++ b/repos/gems/src/server/gui_fader/main.cc @@ -278,22 +278,22 @@ class Gui_fader::Gui_session_component Framebuffer::Session_capability _fb_cap { _env.ep().manage(_fb_session) }; - Gui::Session::View_handle _view_handle { }; + Constructible _view_handle { }; bool _view_visible = false; Rect _view_geometry { }; void _update_view_visibility() { - if (!_view_handle.valid() || (_view_visible == _fb_session.visible())) + if (!_view_handle.constructed() || (_view_visible == _fb_session.visible())) return; using Command = Gui::Session::Command; if (_fb_session.visible()) - _gui.enqueue(_view_handle, _view_geometry); + _gui.enqueue(*_view_handle, _view_geometry); else - _gui.enqueue(_view_handle, Rect()); + _gui.enqueue(*_view_handle, Rect()); _gui.execute(); @@ -347,16 +347,16 @@ class Gui_fader::Gui_session_component Create_view_result create_view() override { - _view_handle = _gui.create_view(); + _view_handle.construct(_gui.create_view()); _update_view_visibility(); - return _view_handle; + return *_view_handle; } Create_child_view_result create_child_view(View_handle parent) override { - _view_handle = _gui.create_child_view(parent); + _view_handle.construct(_gui.create_child_view(parent)); _update_view_visibility(); - return _view_handle; + return *_view_handle; } void destroy_view(View_handle handle) override diff --git a/repos/gems/src/server/wm/gui.h b/repos/gems/src/server/wm/gui.h index d9205e161e..33d8c6ea82 100644 --- a/repos/gems/src/server/wm/gui.h +++ b/repos/gems/src/server/wm/gui.h @@ -98,9 +98,6 @@ struct Wm::Gui::Input_origin_changed_handler : Interface }; -struct Gui::View : Genode::Interface { GENODE_RPC_INTERFACE(); }; - - class Wm::Gui::View : private Genode::Weak_object, public Genode::Rpc_object< ::Gui::View> { @@ -115,15 +112,15 @@ class Wm::Gui::View : private Genode::Weak_object, using Command = Gui::Session::Command; using View_handle = Gui::Session::View_handle; - Session_label _session_label; - Real_gui &_real_gui; - View_handle _real_handle { }; - Title _title { }; - Rect _geometry { }; - Point _buffer_offset { }; - Weak_ptr _neighbor_ptr { }; - bool _neighbor_behind { }; - bool _has_alpha; + Session_label _session_label; + Real_gui &_real_gui; + Constructible _real_handle { }; + Title _title { }; + Rect _geometry { }; + Point _buffer_offset { }; + Weak_ptr _neighbor_ptr { }; + bool _neighbor_behind { }; + bool _has_alpha; View(Real_gui &real_gui, Session_label const &session_label, @@ -143,37 +140,37 @@ class Wm::Gui::View : private Genode::Weak_object, */ void _unsynchronized_apply_view_config(Locked_ptr &neighbor) { - if (!_real_handle.valid()) + if (!_real_handle.constructed()) return; _propagate_view_geometry(); - _real_gui.enqueue(_real_handle, _buffer_offset); - _real_gui.enqueue (_real_handle, _title.string()); + _real_gui.enqueue(*_real_handle, _buffer_offset); + _real_gui.enqueue (*_real_handle, _title.string()); - View_handle real_neighbor_handle; + Constructible real_neighbor_handle { }; if (neighbor.valid()) _real_gui.session.alloc_view_handle(neighbor->real_view_cap()).with_result( - [&] (View_handle handle) { real_neighbor_handle = handle; }, + [&] (View_handle handle) { real_neighbor_handle.construct(handle); }, [&] (auto) { warning("unable to obtain real_neighbor_handle"); } ); - if (real_neighbor_handle.valid()) { + if (real_neighbor_handle.constructed()) { if (_neighbor_behind) - _real_gui.enqueue(_real_handle, real_neighbor_handle); + _real_gui.enqueue(*_real_handle, *real_neighbor_handle); else - _real_gui.enqueue(_real_handle, real_neighbor_handle); + _real_gui.enqueue(*_real_handle, *real_neighbor_handle); } else { if (_neighbor_behind) - _real_gui.enqueue(_real_handle); + _real_gui.enqueue(*_real_handle); else - _real_gui.enqueue(_real_handle); + _real_gui.enqueue(*_real_handle); } _real_gui.execute(); - if (real_neighbor_handle.valid()) - _real_gui.session.release_view_handle(real_neighbor_handle); + if (real_neighbor_handle.constructed()) + _real_gui.session.release_view_handle(*real_neighbor_handle); } void _apply_view_config() @@ -186,8 +183,8 @@ class Wm::Gui::View : private Genode::Weak_object, ~View() { - if (_real_handle.valid()) - _real_gui.session.destroy_view(_real_handle); + if (_real_handle.constructed()) + _real_gui.session.destroy_view(*_real_handle); } using Genode::Weak_object::weak_ptr; @@ -204,7 +201,7 @@ class Wm::Gui::View : private Genode::Weak_object, /* * Propagate new size to real GUI view but */ - if (_real_handle.valid()) { + if (_real_handle.constructed()) { _propagate_view_geometry(); _real_gui.execute(); } @@ -214,8 +211,8 @@ class Wm::Gui::View : private Genode::Weak_object, { _title = Title(title); - if (_real_handle.valid()) { - _real_gui.enqueue(_real_handle, title); + if (_real_handle.constructed()) { + _real_gui.enqueue(*_real_handle, title); _real_gui.execute(); } } @@ -224,19 +221,19 @@ class Wm::Gui::View : private Genode::Weak_object, virtual void stack(Weak_ptr, bool) { } - View_handle real_handle() const { return _real_handle; } - View_capability real_view_cap() { - return _real_gui.session.view_capability(_real_handle); + return _real_handle.constructed() + ? _real_gui.session.view_capability(*_real_handle) + : View_capability(); } void buffer_offset(Point buffer_offset) { _buffer_offset = buffer_offset; - if (_real_handle.valid()) { - _real_gui.enqueue(_real_handle, _buffer_offset); + if (_real_handle.constructed()) { + _real_gui.enqueue(*_real_handle, _buffer_offset); _real_gui.execute(); } } @@ -294,6 +291,31 @@ class Wm::Gui::Top_level_view : public View, private List::Eleme using List::Element::next; + void init_real_gui_view() + { + if (_real_handle.constructed()) + return; + + /* + * Create and configure physical GUI view. + */ + _real_gui.session.create_view().with_result( + [&] (View_handle handle) { + _real_handle.construct(handle); + _real_gui.enqueue(handle, _buffer_offset); + _real_gui.enqueue (handle, _title.string()); + _real_gui.execute(); + }, + [&] (Gui::Session::Create_view_error) { + warning("init_real_view failed"); + } + ); + + if (!_real_handle.constructed()) { + warning("failed to created content view for ", _title); + } + } + void _propagate_view_geometry() override { } void geometry(Rect geometry) override @@ -352,27 +374,13 @@ class Wm::Gui::Top_level_view : public View, private List::Eleme View_capability content_view() { - if (!_real_handle.valid()) { + init_real_gui_view(); - /* - * Create and configure physical GUI view. - */ - _real_gui.session.create_view().with_result( - [&] (View_handle handle) { - _real_handle = handle; - _real_gui.enqueue(_real_handle, _buffer_offset); - _real_gui.enqueue (_real_handle, _title.string()); - _real_gui.execute(); - }, - [&] (Gui::Session::Create_view_error) { } - ); + if (_real_handle.constructed()) + return _real_gui.session.view_capability(*_real_handle); - if (!_real_handle.valid()) { - warning("failed to created content view for ", _title); - return { }; - } - } - return _real_gui.session.view_capability(_real_handle); + error("content_view was unable to obtain real view"); + return { }; } void hidden(bool hidden) { _window_registry.hidden(_win_id, hidden); } @@ -415,7 +423,8 @@ class Wm::Gui::Child_view : public View, private List::Element void _propagate_view_geometry() override { - _real_gui.enqueue(_real_handle, _geometry); + if (_real_handle.constructed()) + _real_gui.enqueue(*_real_handle, _geometry); } void stack(Weak_ptr neighbor_ptr, bool behind) override @@ -443,33 +452,35 @@ class Wm::Gui::Child_view : public View, private List::Element void try_to_init_real_view() { - if (_real_handle.valid()) + if (_real_handle.constructed()) return; Locked_ptr parent(_parent); if (!parent.valid()) return; - View_handle parent_handle { }; + Constructible parent_handle { }; _real_gui.session.alloc_view_handle(parent->real_view_cap()).with_result( - [&] (View_handle handle) { parent_handle = handle; }, - [&] (Gui::Session::Alloc_view_handle_error) { } + [&] (View_handle handle) { parent_handle.construct(handle); }, + [&] (Gui::Session::Alloc_view_handle_error e) { + warning("try_to_init_real_view could not alloc parent handle e=", (int)e); + } ); - if (!parent_handle.valid()) { + if (!parent_handle.constructed()) { warning("try_to_init_real_view failed to obtain parent handle"); return; } - _real_gui.session.create_child_view(parent_handle).with_result( - [&] (View_handle handle) { _real_handle = handle; }, + _real_gui.session.create_child_view(*parent_handle).with_result( + [&] (View_handle handle) { _real_handle.construct(handle); }, [&] (Gui::Session::Create_child_view_error) { } ); - if (!_real_handle.valid()) { + if (!_real_handle.constructed()) { warning("try_to_init_real_view failed to create child view"); return; } - _real_gui.session.release_view_handle(parent_handle); + _real_gui.session.release_view_handle(*parent_handle); if (_neighbor_ptr == _parent) _unsynchronized_apply_view_config(parent); @@ -484,11 +495,11 @@ class Wm::Gui::Child_view : public View, private List::Element void hide() { - if (!_real_handle.valid()) + if (!_real_handle.constructed()) return; - _real_gui.session.destroy_view(_real_handle); - _real_handle = { }; + _real_gui.session.destroy_view(*_real_handle); + _real_handle.destruct(); } }; @@ -503,6 +514,37 @@ class Wm::Gui::Session_component : public Rpc_object, using View_handle = Gui::Session::View_handle; + using View_ids = Id_space; + + struct View_ref : Gui::View_ref + { + Weak_ptr _weak_ptr; + + View_ids::Element id; + + View_ref(Weak_ptr view, View_ids &ids) + : _weak_ptr(view), id(*this, ids) { } + + View_ref(Weak_ptr view, View_ids &ids, View_handle id) + : _weak_ptr(view), id(*this, ids, id) { } + + auto with_view(auto const &fn, auto const &missing_fn) -> decltype(missing_fn()) + { + /* + * Release the lock before calling 'fn' to allow the nesting of + * 'with_view' calls. The locking aspect of the weak ptr is not + * needed here because the component is single-threaded. + */ + View *ptr = nullptr; + { + Locked_ptr view(_weak_ptr); + if (view.valid()) + ptr = view.operator->(); + } + return ptr ? fn(*ptr) : missing_fn(); + } + }; + Genode::Env &_env; Session_label _session_label; @@ -511,8 +553,10 @@ class Wm::Gui::Session_component : public Rpc_object, Window_registry &_window_registry; Tslab _top_level_view_alloc; Tslab _child_view_alloc; + Tslab _view_ref_alloc; List _top_level_views { }; List _child_views { }; + View_ids _view_ids { }; Input::Session_component _input_session { _env, _ram }; Input::Session_capability _input_session_cap; Click_handler &_click_handler; @@ -526,6 +570,18 @@ class Wm::Gui::Session_component : public Rpc_object, Point _virtual_pointer_pos { }; unsigned _key_cnt = 0; + auto _with_view(View_handle handle, auto const &fn, auto const &missing_fn) + -> decltype(missing_fn()) + { + return _view_ids.apply(handle, + [&] (View_ref &view_ref) { + return view_ref.with_view( + [&] (View &view) { return fn(view); }, + [&] /* view vanished */ { return missing_fn(); }); + }, + [&] /* ID does not exist */ { return missing_fn(); }); + } + /* * Command buffer */ @@ -535,13 +591,6 @@ class Wm::Gui::Session_component : public Rpc_object, Command_buffer &_command_buffer = *_command_ds.local_addr(); - /* - * View handle registry - */ - using View_handle_registry = Genode::Handle_registry; - - View_handle_registry _view_handle_registry; - /* * Input */ @@ -687,29 +736,6 @@ class Wm::Gui::Session_component : public Rpc_object, _input_session.submit(Input::Absolute_motion { pos.x, pos.y }); } - View &_create_view_object() - { - Top_level_view *view = new (_top_level_view_alloc) - Top_level_view(_real_gui, _has_alpha, - _window_registry, *this); - - view->resizeable(_mode_sigh.valid()); - - _top_level_views.insert(view); - return *view; - } - - View &_create_child_view_object(View_handle parent_handle) - { - Weak_ptr parent_ptr = _view_handle_registry.lookup(parent_handle); - - Child_view *view = new (_child_view_alloc) - Child_view(_real_gui, _has_alpha, parent_ptr); - - _child_views.insert(view); - return *view; - } - void _destroy_top_level_view(Top_level_view &view) { _top_level_views.remove(&view); @@ -724,62 +750,51 @@ class Wm::Gui::Session_component : public Rpc_object, Genode::destroy(&_child_view_alloc, &view); } - void _destroy_view_object(View &view) - { - if (Top_level_view *v = dynamic_cast(&view)) - _destroy_top_level_view(*v); - - if (Child_view *v = dynamic_cast(&view)) - _destroy_child_view(*v); - } - void _execute_command(Command const &command) { + auto with_this = [&] (auto const &args, auto const &fn) + { + Session_component::_with_view(args.view, + [&] (View &view) { fn(view, args); }, + [&] /* ignore operations on non-existing views */ { }); + }; + switch (command.opcode) { case Command::GEOMETRY: - { - Locked_ptr view(_view_handle_registry.lookup(command.geometry.view)); - if (view.valid()) - view->geometry(command.geometry.rect); - return; - } + + with_this(command.geometry, [&] (View &view, Command::Geometry const &args) { + view.geometry(args.rect); }); + return; case Command::OFFSET: - { - Locked_ptr view(_view_handle_registry.lookup(command.offset.view)); - if (view.valid()) - view->buffer_offset(command.offset.offset); - return; - } + with_this(command.offset, [&] (View &view, Command::Offset const &args) { + view.buffer_offset(args.offset); }); + return; case Command::FRONT: - { - Locked_ptr view(_view_handle_registry.lookup(command.front.view)); - if (view.valid()) - view->stack(Weak_ptr(), true); - return; - } + + with_this(command.front, [&] (View &view, auto const &) { + view.stack(Weak_ptr(), true); }); + return; case Command::FRONT_OF: - { - Locked_ptr view(_view_handle_registry.lookup(command.front_of.view)); - if (!view.valid()) - return; - if (!command.front_of.neighbor.valid()) - return; - - /* stack view relative to neighbor */ - view->stack(_view_handle_registry.lookup(command.front_of.neighbor), - true); - return; - } + with_this(command.front_of, [&] (View &view, Command::Front_of const &args) { + Session_component::_with_view(args.neighbor, + [&] (View &neighbor) { + if (&view != &neighbor) + view.stack(neighbor.weak_ptr(), true); }, + [&] { }); + }); + return; case Command::TITLE: - { - char sanitized_title[command.title.title.capacity()]; + + with_this(command.title, [&] (View &view, Command::Title const &args) { + + char sanitized_title[args.title.capacity()]; Genode::copy_cstring(sanitized_title, command.title.title.string(), sizeof(sanitized_title)); @@ -788,12 +803,9 @@ class Wm::Gui::Session_component : public Rpc_object, if (*c == '"') *c = '\''; - Locked_ptr view(_view_handle_registry.lookup(command.title.view)); - if (view.valid()) - view->title(sanitized_title); - - return; - } + view.title(sanitized_title); + }); + return; case Command::BACK: case Command::BEHIND_OF: @@ -824,10 +836,10 @@ class Wm::Gui::Session_component : public Rpc_object, _window_registry(window_registry), _top_level_view_alloc(&session_alloc), _child_view_alloc(&session_alloc), + _view_ref_alloc(&session_alloc), _input_session_cap(env.ep().manage(_input_session)), _click_handler(click_handler), - _pointer_state(pointer_tracker), - _view_handle_registry(session_alloc) + _pointer_state(pointer_tracker) { _gui_input.sigh(_input_handler); _real_gui.session.mode_sigh(_mode_handler); @@ -836,11 +848,14 @@ class Wm::Gui::Session_component : public Rpc_object, ~Session_component() { + while (_view_ids.apply_any([&] (View_ref &view_ref) { + destroy(_view_ref_alloc, &view_ref); })); + while (Top_level_view *view = _top_level_views.first()) - _destroy_view_object(*view); + _destroy_top_level_view(*view); while (Child_view *view = _child_views.first()) - _destroy_view_object(*view); + _destroy_child_view(*view); _env.ep().dissolve(_input_session); } @@ -958,92 +973,160 @@ class Wm::Gui::Session_component : public Rpc_object, return _input_session_cap; } - Create_view_result create_view() override + template + Create_view_result _create_view_with_id(auto &dealloc, auto const &create_fn) { + Create_view_error error { }; + + VIEW *view_ptr = nullptr; try { - View &view = _create_view_object(); - _env.ep().manage(view); - return _view_handle_registry.alloc(view); + view_ptr = create_fn(); } - catch (Out_of_ram) { return Create_view_error::OUT_OF_RAM; } - catch (Out_of_caps) { return Create_view_error::OUT_OF_CAPS; } + catch (Out_of_ram) { error = Create_view_error::OUT_OF_RAM; } + catch (Out_of_caps) { error = Create_view_error::OUT_OF_CAPS; } + if (!view_ptr) + return error; + + View_ref *view_ref_ptr = nullptr; + try { + view_ref_ptr = + new (_view_ref_alloc) View_ref(view_ptr->weak_ptr(), _view_ids); + } + catch (Out_of_ram) { error = Create_view_error::OUT_OF_RAM; } + catch (Out_of_caps) { error = Create_view_error::OUT_OF_CAPS; } + if (!view_ref_ptr) { + destroy(dealloc, view_ptr); + return error; + } + + _env.ep().manage(*view_ptr); + + return view_ref_ptr->id.id(); } - Create_child_view_result create_child_view(View_handle parent) override + Create_view_result create_view() override { - try { - View &view = _create_child_view_object(parent); - _env.ep().manage(view); - return _view_handle_registry.alloc(view); + Top_level_view *view_ptr = nullptr; + + Create_view_result const result = + _create_view_with_id(_top_level_view_alloc, + [&] { + view_ptr = new (_top_level_view_alloc) + Top_level_view(_real_gui, _has_alpha, + _window_registry, *this); + return view_ptr; + }); + + if (result.ok() && view_ptr) { + + view_ptr->init_real_gui_view(); + + view_ptr->resizeable(_mode_sigh.valid()); + _top_level_views.insert(view_ptr); } - catch (Out_of_ram) { return Create_child_view_error::OUT_OF_RAM; } - catch (Out_of_caps) { return Create_child_view_error::OUT_OF_CAPS; } - catch (View_handle_registry::Lookup_failed) { - return View_handle(); } + return result; + } + + Create_child_view_result create_child_view(View_handle const parent) override + { + using Error = Create_child_view_error; + return _with_view(parent, + [&] (View &parent) -> Create_child_view_result { + + Child_view *view_ptr = nullptr; + + Create_view_result const result = + _create_view_with_id(_child_view_alloc, + [&] { + view_ptr = new (_child_view_alloc) + Child_view(_real_gui, _has_alpha, parent.weak_ptr()); + return view_ptr; + }); + + return result.convert( + [&] (View_handle handle) { + if (view_ptr) + _child_views.insert(view_ptr); + return handle; + }, + [&] (Create_view_error e) { + switch (e) { + case Create_view_error::OUT_OF_RAM: return Error::OUT_OF_RAM; + case Create_view_error::OUT_OF_CAPS: return Error::OUT_OF_CAPS; + }; + return Error::INVALID; + } + ); + }, + [&] () -> Create_child_view_result { return Error::INVALID; }); } void destroy_view(View_handle handle) override { - try { - /* - * Lookup the view but release the lock to prevent the view - * destructor from taking the lock a second time. The locking - * aspect of the weak ptr is not needed within the wm because - * the component is single-threaded. - */ - View *view_ptr = nullptr; - { - Locked_ptr view(_view_handle_registry.lookup(handle)); - if (view.valid()) - view_ptr = view.operator->(); - } - - if (view_ptr) - _destroy_view_object(*view_ptr); - - _view_handle_registry.free(handle); - } catch (View_handle_registry::Lookup_failed) { } + _with_view(handle, + [&] (View &view) { + for (Child_view *v = _child_views.first(); v; v = v->next()) + if (&view == v) { + _destroy_child_view(*v); + return; + } + for (Top_level_view *v = _top_level_views.first(); v; v = v->next()) + if (&view == v) { + _destroy_top_level_view(*v); + return; + } + }, + [&] /* ID exists but view vanished */ { } + ); + release_view_handle(handle); } Alloc_view_handle_result alloc_view_handle(View_capability view_cap) override { - try { - return _env.ep().rpc_ep().apply(view_cap, [&] (View *view) { - return (view) ? _view_handle_registry.alloc(*view) - : View_handle(); }); - } - catch (Out_of_ram) { return Alloc_view_handle_error::OUT_OF_RAM; } - catch (Out_of_caps) { return Alloc_view_handle_error::OUT_OF_CAPS; } + return _env.ep().rpc_ep().apply(view_cap, + [&] (View *view_ptr) -> Alloc_view_handle_result { + if (!view_ptr) + return Alloc_view_handle_error::INVALID; + try { + View_ref &view_ref = *new (_view_ref_alloc) + View_ref(view_ptr->weak_ptr(), _view_ids); + return view_ref.id.id(); + } + catch (Out_of_ram) { return Alloc_view_handle_error::OUT_OF_RAM; } + catch (Out_of_caps) { return Alloc_view_handle_error::OUT_OF_CAPS; } + }); } View_handle_result view_handle(View_capability view_cap, View_handle handle) override { - try { - _env.ep().rpc_ep().apply(view_cap, [&] (View *view) { - if (view) - _view_handle_registry.alloc(*view, handle); }); - return View_handle_result::OK; - } - catch (Out_of_ram) { return View_handle_result::OUT_OF_RAM; } - catch (Out_of_caps) { return View_handle_result::OUT_OF_CAPS; } + /* prevent ID conflict in 'View_ids::Element' constructor */ + release_view_handle(handle); + + return _env.ep().rpc_ep().apply(view_cap, + [&] (View *view_ptr) -> View_handle_result { + if (!view_ptr) + return View_handle_result::INVALID; + try { + new (_view_ref_alloc) View_ref(view_ptr->weak_ptr(), _view_ids, handle); + return View_handle_result::OK; + } + catch (Out_of_ram) { return View_handle_result::OUT_OF_RAM; } + catch (Out_of_caps) { return View_handle_result::OUT_OF_CAPS; } + }); } View_capability view_capability(View_handle handle) override { - Locked_ptr view(_view_handle_registry.lookup(handle)); - - return view.valid() ? view->cap() : View_capability(); + return _with_view(handle, + [&] (View &view) { return view.cap(); }, + [&] /* view does not exist */ { return View_capability(); }); } void release_view_handle(View_handle handle) override { - try { - _view_handle_registry.free(handle); } - - catch (View_handle_registry::Lookup_failed) { - Genode::warning("view lookup failed while releasing view handle"); - return; - } + _view_ids.apply(handle, + [&] (View_ref &view_ref) { destroy(_view_ref_alloc, &view_ref); }, + [&] { }); } Genode::Dataspace_capability command_dataspace() override @@ -1053,12 +1136,8 @@ class Wm::Gui::Session_component : public Rpc_object, void execute() override { - for (unsigned i = 0; i < _command_buffer.num(); i++) { - try { - _execute_command(_command_buffer.get(i)); } - catch (View_handle_registry::Lookup_failed) { - Genode::warning("view lookup failed during command execution"); } - } + for (unsigned i = 0; i < _command_buffer.num(); i++) + _execute_command(_command_buffer.get(i)); /* propagate window-list changes to the layouter */ _window_registry.flush(); diff --git a/repos/os/include/decorator/window.h b/repos/os/include/decorator/window.h index fea4b34a33..e5e0dd886e 100644 --- a/repos/os/include/decorator/window.h +++ b/repos/os/include/decorator/window.h @@ -18,6 +18,7 @@ #include #include #include +#include #include /* decorator includes */ @@ -109,7 +110,7 @@ class Decorator::Window_base : private Genode::List_model::Element /* * View immediately behind the window */ - View_handle _neighbor { }; + Genode::Constructible _neighbor { }; Genode::List_element _abandoned { this }; @@ -138,18 +139,20 @@ class Decorator::Window_base : private Genode::List_model::Element void stacking_neighbor(View_handle neighbor) { - _neighbor = neighbor; + _neighbor.construct(neighbor); _stacked = true; } bool back_most() const { - return _stacked && !_neighbor.valid(); + return _stacked && !_neighbor.constructed(); } bool in_front_of(Window_base const &neighbor) const { - return _stacked && (_neighbor == neighbor.frontmost_view()); + return _stacked + && _neighbor.constructed() + && (*_neighbor == neighbor.frontmost_view()); } void geometry(Rect geometry) { _geometry = geometry; } diff --git a/repos/os/include/gui_session/connection.h b/repos/os/include/gui_session/connection.h index 3ef2514e9d..8b4c2a1928 100644 --- a/repos/os/include/gui_session/connection.h +++ b/repos/os/include/gui_session/connection.h @@ -50,7 +50,6 @@ class Gui::Connection : private Genode::Connection */ Input::Session_client input { _env.rm(), _client.input() }; - using View_handle = Session::View_handle; using Genode::Connection::cap; using Genode::Connection::upgrade; using Genode::Connection::upgrade_ram; diff --git a/repos/os/include/gui_session/gui_session.h b/repos/os/include/gui_session/gui_session.h index a298493f21..da32cd1cf7 100644 --- a/repos/os/include/gui_session/gui_session.h +++ b/repos/os/include/gui_session/gui_session.h @@ -14,9 +14,9 @@ #ifndef _INCLUDE__GUI_SESSION__GUI_SESSION_H_ #define _INCLUDE__GUI_SESSION__GUI_SESSION_H_ +#include #include #include -#include #include #include @@ -28,7 +28,16 @@ namespace Gui { struct View; struct Session; + /* + * View capabilities are used as tokens to share views between sessions. + * There is no RPC interface associated with a view. View operations refer + * to views via session-local IDs. + */ + struct View : Interface { GENODE_RPC_INTERFACE(); }; + struct View_ref : Interface { }; + using View_capability = Capability; + using View_handle = Id_space::Id; using Rect = Surface_base::Rect; using Point = Surface_base::Point; @@ -52,14 +61,7 @@ struct Gui::Session : Genode::Session static constexpr unsigned CAP_QUOTA = Framebuffer::Session::CAP_QUOTA + Input::Session::CAP_QUOTA + 3; - /** - * Session-local view handle - * - * When issuing commands to nitpicker via the 'execute' method, views - * are referenced by session-local handles. - */ - using View_handle = Handle; - + using View_handle = Gui::View_handle; struct Command { diff --git a/repos/os/src/server/nitpicker/gui_session.cc b/repos/os/src/server/nitpicker/gui_session.cc index 7843d3db94..ef0169968d 100644 --- a/repos/os/src/server/nitpicker/gui_session.cc +++ b/repos/os/src/server/nitpicker/gui_session.cc @@ -16,18 +16,6 @@ using namespace Nitpicker; -bool Gui_session::_views_are_equal(View_handle v1, View_handle v2) -{ - if (!v1.valid() || !v2.valid()) - return false; - - Weak_ptr v1_ptr = _view_handle_registry.lookup(v1); - Weak_ptr v2_ptr = _view_handle_registry.lookup(v2); - - return v1_ptr == v2_ptr; -} - - View_owner &Gui_session::forwarded_focus() { Gui_session *next_focus = this; @@ -60,102 +48,75 @@ View_owner &Gui_session::forwarded_focus() void Gui_session::_execute_command(Command const &command) { + auto with_this = [&] (auto const &args, auto const &fn) + { + Gui_session::_with_view(args.view, + [&] (View &view) { fn(view, args); }, + [&] /* ignore operations on non-existing views */ { }); + }; + switch (command.opcode) { case Command::GEOMETRY: - { - Command::Geometry const &cmd = command.geometry; - Locked_ptr view(_view_handle_registry.lookup(cmd.view)); - if (!view.valid()) - return; - Point pos = cmd.rect.p1(); + with_this(command.geometry, [&] (View &view, Command::Geometry const &args) { + Point pos = args.rect.p1(); /* transpose position of top-level views by vertical session offset */ - if (view->top_level()) + if (view.top_level()) pos = _phys_pos(pos, _view_stack.size()); - if (view.valid()) - _view_stack.geometry(*view, Rect(pos, cmd.rect.area)); - - return; - } + _view_stack.geometry(view, Rect(pos, args.rect.area)); + }); + return; case Command::OFFSET: - { - Command::Offset const &cmd = command.offset; - Locked_ptr view(_view_handle_registry.lookup(cmd.view)); - if (view.valid()) - _view_stack.buffer_offset(*view, cmd.offset); - - return; - } + with_this(command.offset, [&] (View &view, Command::Offset const &args) { + _view_stack.buffer_offset(view, args.offset); }); + return; case Command::FRONT: - { - Command::Front const &cmd = command.front; - Locked_ptr view(_view_handle_registry.lookup(cmd.view)); - if (view.valid()) - _view_stack.stack(*view, nullptr, true); - return; - } + with_this(command.front, [&] (View &view, auto const &) { + _view_stack.stack(view, nullptr, true); }); + return; case Command::BACK: - { - Command::Back const &cmd = command.back; - Locked_ptr view(_view_handle_registry.lookup(cmd.view)); - if (view.valid()) - _view_stack.stack(*view, nullptr, false); - return; - } + with_this(command.back, [&] (View &view, auto const &) { + _view_stack.stack(view, nullptr, false); }); + return; case Command::FRONT_OF: - { - Command::Front_of const &cmd = command.front_of; - if (_views_are_equal(cmd.view, cmd.neighbor)) - return; - Locked_ptr view(_view_handle_registry.lookup(cmd.view)); - if (!view.valid()) - return; - - /* stack view relative to neighbor */ - Locked_ptr neighbor(_view_handle_registry.lookup(cmd.neighbor)); - if (neighbor.valid()) - _view_stack.stack(*view, &(*neighbor), false); - return; - } + with_this(command.front_of, [&] (View &view, Command::Front_of const &args) { + Gui_session::_with_view(args.neighbor, + [&] (View &neighbor) { + if (&view != &neighbor) + _view_stack.stack(view, &neighbor, false); }, + [&] { }); + }); + return; case Command::BEHIND_OF: - { - Command::Behind_of const &cmd = command.behind_of; - if (_views_are_equal(cmd.view, cmd.neighbor)) - return; - Locked_ptr view(_view_handle_registry.lookup(cmd.view)); - if (!view.valid()) - return; - - /* stack view relative to neighbor */ - Locked_ptr neighbor(_view_handle_registry.lookup(cmd.neighbor)); - if (neighbor.valid()) - _view_stack.stack(*view, &(*neighbor), true); - return; - } + with_this(command.behind_of, [&] (View &view, Command::Behind_of const &args) { + Gui_session::_with_view(args.neighbor, + [&] (View &neighbor) { + if (&view != &neighbor) + _view_stack.stack(view, &neighbor, false); }, + [&] /* neighbor view does not exist */ { }); + }); + return; case Command::BACKGROUND: - { - Command::Background const &cmd = command.background; - if (_provides_default_bg) { - Locked_ptr view(_view_handle_registry.lookup(cmd.view)); - if (!view.valid()) - return; - view->background(true); - _view_stack.default_background(*view); + with_this(command.background, [&] (View &view, auto const &) { + + if (_provides_default_bg) { + view.background(true); + _view_stack.default_background(view); return; } @@ -164,29 +125,19 @@ void Gui_session::_execute_command(Command const &command) _background->background(false); /* assign session background */ - Locked_ptr view(_view_handle_registry.lookup(cmd.view)); - if (!view.valid()) - return; - - _background = &(*view); + _background = &view; /* switch background view to background mode */ if (background()) - view->background(true); - - return; - } + view.background(true); + }); + return; case Command::TITLE: - { - Command::Title const &cmd = command.title; - Locked_ptr view(_view_handle_registry.lookup(cmd.view)); - if (view.valid()) - _view_stack.title(*view, cmd.title.string()); - - return; - } + with_this(command.title, [&] (View &view, Command::Title const &args) { + _view_stack.title(view, args.title.string()); }); + return; case Command::NOP: return; @@ -247,47 +198,72 @@ void Gui_session::_adopt_new_view(View &view) } -Gui_session::Create_view_result Gui_session::create_view() +Gui_session::Create_view_result Gui_session::_create_view_with_id(auto const &create_fn) { + Create_view_error error { }; try { - View &view = *new (_view_alloc) - View(*this, _texture, { .transparent = false, .background = false }, nullptr); - - _adopt_new_view(view); - - return _view_handle_registry.alloc(view); + View &view = create_fn(); + try { + View_ref &view_ref = + *new (_view_ref_alloc) View_ref(view.weak_ptr(), _view_ids); + _adopt_new_view(view); + return view_ref.id.id(); + } + catch (Out_of_ram) { error = Create_view_error::OUT_OF_RAM; } + catch (Out_of_caps) { error = Create_view_error::OUT_OF_CAPS; } + destroy(_view_alloc, &view); } - catch (Out_of_ram) { return Create_view_error::OUT_OF_RAM; } - catch (Out_of_caps) { return Create_view_error::OUT_OF_CAPS; } + catch (Out_of_ram) { error = Create_view_error::OUT_OF_RAM; } + catch (Out_of_caps) { error = Create_view_error::OUT_OF_CAPS; } + return error; } -Gui_session::Create_child_view_result Gui_session::create_child_view(View_handle const parent_handle) +Gui_session::Create_view_result Gui_session::create_view() { - View *parent_view_ptr = nullptr; + return _create_view_with_id([&] () -> View & { + return *new (_view_alloc) + View(*this, _texture, + { .transparent = false, .background = false }, + nullptr); + }); +} - try { - Locked_ptr parent(_view_handle_registry.lookup(parent_handle)); - if (parent.valid()) - parent_view_ptr = &(*parent); - } - catch (View_handle_registry::Lookup_failed) { } - if (!parent_view_ptr) - return Create_child_view_error::INVALID; +Gui_session::Create_child_view_result Gui_session::create_child_view(View_handle const parent) +{ + using Error = Create_child_view_error; - try { - View &view = *new (_view_alloc) - View(*this, _texture, { .transparent = false, .background = false }, parent_view_ptr); + return _with_view(parent, + [&] (View &parent) -> Create_child_view_result { - parent_view_ptr->add_child(view); + View *view_ptr = nullptr; + Create_view_result const result = _create_view_with_id( + [&] () -> View & { + view_ptr = new (_view_alloc) + View(*this, _texture, + { .transparent = false, .background = false }, + &parent); + return *view_ptr; + }); - _adopt_new_view(view); - - return _view_handle_registry.alloc(view); - } - catch (Out_of_ram) { return Create_child_view_error::OUT_OF_RAM; } - catch (Out_of_caps) { return Create_child_view_error::OUT_OF_CAPS; } + return result.convert( + [&] (View_handle handle) { + if (view_ptr) + parent.add_child(*view_ptr); + return handle; + }, + [&] (Create_view_error e) { + switch (e) { + case Create_view_error::OUT_OF_RAM: return Error::OUT_OF_RAM; + case Create_view_error::OUT_OF_CAPS: return Error::OUT_OF_CAPS; + }; + return Error::INVALID; + }); + }, + [&] /* parent view does not exist */ () -> Create_child_view_result { + return Error::INVALID; } + ); } @@ -320,36 +296,22 @@ void Gui_session::apply_session_policy(Xml_node config, } -void Gui_session::destroy_view(View_handle handle) +void Gui_session::destroy_view(View_handle const handle) { /* - * Search view object given the handle - * - * We cannot look up the view directly from the - * '_view_handle_registry' because we would obtain a weak - * pointer to the view object. If we called the object's - * destructor from the corresponding locked pointer, the - * call of 'lock_for_destruction' in the view's destructor - * would attempt to take the lock again. + * Search among the session's own views the one with the given handle */ - for (Session_view_list_elem *v = _view_list.first(); v; v = v->next()) { - - auto handle_matches = [&] (View const &view) - { - try { return _view_handle_registry.has_handle(view, handle); } - - /* 'Handle_registry::has_handle' may throw */ - catch (...) { return false; }; - }; - - View &view = *static_cast(v); - - if (handle_matches(view)) { - _destroy_view(view); - _view_handle_registry.free(handle); - break; - } - } + _with_view(handle, + [&] (View &view) { + for (Session_view_list_elem *v = _view_list.first(); v; v = v->next()) + if (&view == v) { + _destroy_view(view); + break; + } + }, + [&] /* ID exists but view vanished */ { } + ); + release_view_handle(handle); _hover_updater.update_hover(); } @@ -358,65 +320,62 @@ void Gui_session::destroy_view(View_handle handle) Gui_session::Alloc_view_handle_result Gui_session::alloc_view_handle(View_capability view_cap) { - try { - return _env.ep().rpc_ep().apply(view_cap, [&] (View *view_ptr) -> Alloc_view_handle_result { + return _env.ep().rpc_ep().apply(view_cap, + [&] (View *view_ptr) -> Alloc_view_handle_result { if (!view_ptr) return Alloc_view_handle_error::INVALID; - return _view_handle_registry.alloc(*view_ptr); }); - } - catch (View_handle_registry::Out_of_memory) { return Alloc_view_handle_error::OUT_OF_RAM; } - catch (Out_of_ram) { return Alloc_view_handle_error::OUT_OF_RAM; } - catch (Out_of_caps) { return Alloc_view_handle_error::OUT_OF_RAM; } + try { + View_ref &view_ref = *new (_view_ref_alloc) + View_ref(view_ptr->weak_ptr(), _view_ids); + return view_ref.id.id(); + } + catch (Out_of_ram) { return Alloc_view_handle_error::OUT_OF_RAM; } + catch (Out_of_caps) { return Alloc_view_handle_error::OUT_OF_CAPS; } + }); } Gui_session::View_handle_result Gui_session::view_handle(View_capability view_cap, View_handle handle) { - try { - return _env.ep().rpc_ep().apply(view_cap, [&] (View *view_ptr) -> View_handle_result { + /* prevent ID conflict in 'View_ids::Element' constructor */ + release_view_handle(handle); + + return _env.ep().rpc_ep().apply(view_cap, + [&] (View *view_ptr) -> View_handle_result { if (!view_ptr) return View_handle_result::INVALID; - _view_handle_registry.alloc(*view_ptr, handle); - return View_handle_result::OK; + try { + new (_view_ref_alloc) View_ref(view_ptr->weak_ptr(), _view_ids, handle); + return View_handle_result::OK; + } + catch (Out_of_ram) { return View_handle_result::OUT_OF_RAM; } + catch (Out_of_caps) { return View_handle_result::OUT_OF_CAPS; } }); - } - catch (View_handle_registry::Out_of_memory) { return View_handle_result::OUT_OF_RAM; } - catch (Out_of_ram) { return View_handle_result::OUT_OF_RAM; } - catch (Out_of_caps) { return View_handle_result::OUT_OF_RAM; } } View_capability Gui_session::view_capability(View_handle handle) { - try { - Locked_ptr view(_view_handle_registry.lookup(handle)); - return view.valid() ? view->cap() : View_capability(); - } - catch (View_handle_registry::Lookup_failed) { return View_capability(); } + return _with_view(handle, + [&] (View &view) { return view.cap(); }, + [&] /* view does not exist */ { return View_capability(); }); } void Gui_session::release_view_handle(View_handle handle) { - try { - _view_handle_registry.free(handle); } - - catch (View_handle_registry::Lookup_failed) { - warning("view lookup failed while releasing view handle"); - return; - } + _view_ids.apply(handle, + [&] (View_ref &view_ref) { destroy(_view_ref_alloc, &view_ref); }, + [&] { }); } void Gui_session::execute() { - for (unsigned i = 0; i < _command_buffer.num(); i++) { - try { - _execute_command(_command_buffer.get(i)); } - catch (View_handle_registry::Lookup_failed) { - warning("view lookup failed during command execution"); } - } + for (unsigned i = 0; i < _command_buffer.num(); i++) + _execute_command(_command_buffer.get(i)); + _hover_updater.update_hover(); } diff --git a/repos/os/src/server/nitpicker/gui_session.h b/repos/os/src/server/nitpicker/gui_session.h index 8800127684..469acc5d8e 100644 --- a/repos/os/src/server/nitpicker/gui_session.h +++ b/repos/os/src/server/nitpicker/gui_session.h @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2006-2017 Genode Labs GmbH + * Copyright (C) 2006-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. @@ -48,6 +48,37 @@ class Nitpicker::Gui_session : public Session_object, { private: + using View_ids = Id_space; + + struct View_ref : Gui::View_ref + { + Weak_ptr _weak_ptr; + + View_ids::Element id; + + View_ref(Weak_ptr view, View_ids &ids) + : _weak_ptr(view), id(*this, ids) { } + + View_ref(Weak_ptr view, View_ids &ids, View_handle id) + : _weak_ptr(view), id(*this, ids, id) { } + + auto with_view(auto const &fn, auto const &missing_fn) -> decltype(missing_fn()) + { + /* + * Release the lock before calling 'fn' to allow the nesting of + * 'with_view' calls. The locking aspect of the weak ptr is not + * needed here because the component is single-threaded. + */ + View *ptr = nullptr; + { + Locked_ptr view(_weak_ptr); + if (view.valid()) + ptr = view.operator->(); + } + return ptr ? fn(*ptr) : missing_fn(); + } + }; + friend class List; using Gui::Session::Label; @@ -105,6 +136,8 @@ class Nitpicker::Gui_session : public Session_object, Tslab _view_alloc { &_session_alloc }; + Tslab _view_ref_alloc { &_session_alloc }; + /* capabilities for sub sessions */ Framebuffer::Session_capability _framebuffer_session_cap; Input::Session_capability _input_session_cap; @@ -122,9 +155,7 @@ class Nitpicker::Gui_session : public Session_object, Command_buffer &_command_buffer = *_command_ds.local_addr(); - using View_handle_registry = Handle_registry; - - View_handle_registry _view_handle_registry; + View_ids _view_ids { }; Reporter &_focus_reporter; @@ -156,6 +187,20 @@ class Nitpicker::Gui_session : public Session_object, void _adopt_new_view(View &); + Create_view_result _create_view_with_id(auto const &); + + auto _with_view(View_handle handle, auto const &fn, auto const &missing_fn) + -> decltype(missing_fn()) + { + return _view_ids.apply(handle, + [&] (View_ref &view_ref) { + return view_ref.with_view( + [&] (View &view) { return fn(view); }, + [&] /* view vanished */ { return missing_fn(); }); + }, + [&] /* ID does not exist */ { return missing_fn(); }); + } + public: Gui_session(Env &env, @@ -182,7 +227,6 @@ class Nitpicker::Gui_session : public Session_object, _framebuffer_session_cap(_env.ep().manage(_framebuffer_session_component)), _input_session_cap(_env.ep().manage(_input_session_component)), _provides_default_bg(provides_default_bg), - _view_handle_registry(_session_alloc), _focus_reporter(focus_reporter) { } @@ -191,6 +235,9 @@ class Nitpicker::Gui_session : public Session_object, _env.ep().dissolve(_framebuffer_session_component); _env.ep().dissolve(_input_session_component); + while (_view_ids.apply_any([&] (View_ref &view_ref) { + destroy(_view_ref_alloc, &view_ref); })); + destroy_all_views(); } diff --git a/repos/os/src/server/nitpicker/view.h b/repos/os/src/server/nitpicker/view.h index 03b2c09550..e7c7d9cecc 100644 --- a/repos/os/src/server/nitpicker/view.h +++ b/repos/os/src/server/nitpicker/view.h @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2006-2017 Genode Labs GmbH + * Copyright (C) 2006-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. @@ -19,6 +19,8 @@ #include #include #include +#include +#include /* local includes */ #include @@ -54,16 +56,6 @@ namespace Nitpicker { } -namespace Gui { - - /* - * We use view capabilities as mere tokens to pass views between sessions. - * There is no RPC interface associated with a view. - */ - struct View : Interface { GENODE_RPC_INTERFACE(); }; -} - - class Nitpicker::View : private Same_buffer_list_elem, private Session_view_list_elem, private View_stack_elem, @@ -76,7 +68,6 @@ class Nitpicker::View : private Same_buffer_list_elem, using Title = String<32>; using Weak_object::weak_ptr; - using Weak_object::weak_ptr_const; private: