diff --git a/repos/gems/run/decorator.run b/repos/gems/run/decorator.run index 0366a904d7..6f3025733e 100644 --- a/repos/gems/run/decorator.run +++ b/repos/gems/run/decorator.run @@ -77,79 +77,93 @@ install_config { - + + + - - + + + + - - - + + + + + - - - + + + + + - - - + + + + + - - - + + + + + - - - + + + + + @@ -160,7 +174,7 @@ install_config { - + diff --git a/repos/gems/sculpt/leitzentrale/default b/repos/gems/sculpt/leitzentrale/default index 39d841370c..e4be530b53 100644 --- a/repos/gems/sculpt/leitzentrale/default +++ b/repos/gems/sculpt/leitzentrale/default @@ -114,7 +114,7 @@ - + diff --git a/repos/gems/src/app/decorator/main.cc b/repos/gems/src/app/decorator/main.cc index 7ec8beca58..84fe8a3a16 100644 --- a/repos/gems/src/app/decorator/main.cc +++ b/repos/gems/src/app/decorator/main.cc @@ -40,6 +40,8 @@ struct Decorator::Main : Window_factory_base { Env &_env; + Heap _heap { _env.ram(), _env.rm() }; + Timer::Connection _timer { _env }; /* @@ -120,7 +122,9 @@ struct Decorator::Main : Window_factory_base _back_to_front(dirty); } - Window_stack _window_stack = { *this }; + Window_stack _window_stack { *this, _heap }; + + Windows _windows { }; /** * Handler for responding to window-layout changes @@ -172,8 +176,6 @@ struct Decorator::Main : Window_factory_base } } - Heap _heap { _env.ram(), _env.rm() }; - Attached_rom_dataspace _config { _env, "config" }; void _handle_config(); @@ -222,19 +224,34 @@ struct Decorator::Main : Window_factory_base /** * Window_factory_base interface */ - Window_base *create(Xml_node window_node) override + Window_base::Ref &create_ref(Xml_node const &window_node) override { - return new (_heap) - Window(window_node.attribute_value("id", 0U), - _gui, _animator, _decorator_config); + Windows::Id const id { window_node.attribute_value("id", 0U) }; + + Window_base *window_ptr = nullptr; + _windows.apply(id, + [&] (Window_base &window) { window_ptr = &window; }, + [&] /* missing */ { + window_ptr = new (_heap) + Window(_windows, id, _gui, _animator, _decorator_config); }); + + return *new (_heap) Window_base::Ref(*window_ptr); } /** * Window_factory_base interface */ - void destroy(Window_base *window) override + void destroy_ref(Window_base::Ref &ref) override { - Genode::destroy(_heap, static_cast(window)); + destroy(_heap, &ref); + } + + /** + * Window_factory_base interface + */ + void destroy_window(Window_base &window) override + { + destroy(_heap, &window); } }; @@ -280,11 +297,11 @@ static void update_hover_report(Genode::Xml_node pointer_node, Genode::Reporter::Xml_generator xml(hover_reporter, [&] () { - if (hover.window_id > 0) { + if (hover.window_id.value > 0) { xml.node("window", [&] () { - xml.attribute("id", hover.window_id); + xml.attribute("id", hover.window_id.value); if (hover.left_sizer) xml.node("left_sizer"); if (hover.right_sizer) xml.node("right_sizer"); diff --git a/repos/gems/src/app/decorator/window.cc b/repos/gems/src/app/decorator/window.cc index c133b8b7d3..d2b1e8db3b 100644 --- a/repos/gems/src/app/decorator/window.cc +++ b/repos/gems/src/app/decorator/window.cc @@ -15,9 +15,8 @@ #include "window.h" -void Decorator::Window::draw(Decorator::Canvas_base &canvas, - Decorator::Rect clip, - Draw_behind_fn const &draw_behind_fn) const +void Decorator::Window::draw(Decorator::Canvas_base &canvas, Ref const &win_ref, + Decorator::Rect clip, Draw_behind_fn const &draw_behind_fn) const { Clip_guard clip_guard(canvas, clip); @@ -28,7 +27,7 @@ void Decorator::Window::draw(Decorator::Canvas_base &canvas, Point p2 = rect.p2(); if (_has_alpha) - draw_behind_fn.draw_behind(canvas, *this, canvas.clip()); + draw_behind_fn.draw_behind(canvas, win_ref, canvas.clip()); _draw_corner(canvas, Rect(p1, corner), _border_size, true, true, _window_elem_attr(Element::TOP_LEFT)); diff --git a/repos/gems/src/app/decorator/window.h b/repos/gems/src/app/decorator/window.h index 393489d565..5fd2753779 100644 --- a/repos/gems/src/app/decorator/window.h +++ b/repos/gems/src/app/decorator/window.h @@ -38,6 +38,8 @@ class Decorator::Window : public Window_base */ bool _gui_views_up_to_date = false; + Rect _clip { }; /* most recently used clipping rectangle */ + struct Gui_view : Genode::Noncopyable { Gui::Connection &_gui; @@ -74,11 +76,18 @@ class Decorator::Window : public Window_base void stack_back_most() { _gui.enqueue(id()); } - void place(Rect rect) + void place_as_decor(Clip const &clip, Rect rect) { - _gui.enqueue(id(), rect); - Point offset = Point(0, 0) - rect.at; - _gui.enqueue(id(), offset); + Rect const intersection = Rect::intersect(clip, rect); + _gui.enqueue(id(), intersection); + _gui.enqueue(id(), Point() - intersection.at); + } + + void place_as_content(Clip const &clip, Rect rect) + { + Rect const intersection = Rect::intersect(clip, rect); + _gui.enqueue(id(), intersection); + _gui.enqueue(id(), rect.at - intersection.at); } }; @@ -87,7 +96,7 @@ class Decorator::Window : public Window_base _left_view { _gui }, _top_view { _gui }; - Gui_view _content_view { _gui, (unsigned)id() }; + Gui_view _content_view { _gui, unsigned(id().value) }; static Border _init_border() { return Border(_border_size + _title_height, @@ -419,10 +428,10 @@ class Decorator::Window : public Window_base public: - Window(unsigned id, Gui::Connection &gui, + Window(Windows &windows, Windows::Id id, Gui::Connection &gui, Animator &animator, Config const &config) : - Window_base(id), + Window_base(windows, id), _gui(gui), _animator(animator), _config(config) { } @@ -465,18 +474,18 @@ class Decorator::Window : public Window_base geometry().p2() + Point(_border.right, _border.bottom)); } - void update_gui_views() override + void update_gui_views(Clip const &clip) override { - if (!_gui_views_up_to_date) { + if (!_gui_views_up_to_date || (clip != _clip)) { /* update view positions */ auto const border = outer_geometry().cut(geometry()); - _content_view.place(geometry()); - _top_view .place(border.top); - _left_view .place(border.left); - _right_view .place(border.right); - _bottom_view .place(border.bottom); + _content_view.place_as_content(clip, geometry()); + _top_view .place_as_decor (clip, border.top); + _left_view .place_as_decor (clip, border.left); + _right_view .place_as_decor (clip, border.right); + _bottom_view .place_as_decor (clip, border.bottom); _gui_views_up_to_date = true; } @@ -487,7 +496,7 @@ class Decorator::Window : public Window_base _base_color = _config.base_color(_title); } - void draw(Canvas_base &canvas, Rect clip, Draw_behind_fn const &) const override; + void draw(Canvas_base &canvas, Ref const &, Rect clip, Draw_behind_fn const &) const override; bool update(Xml_node) override; diff --git a/repos/gems/src/app/sculpt_manager/main.cc b/repos/gems/src/app/sculpt_manager/main.cc index 3d2f0d25b5..c0ff8b8154 100644 --- a/repos/gems/src/app/sculpt_manager/main.cc +++ b/repos/gems/src/app/sculpt_manager/main.cc @@ -1621,8 +1621,9 @@ struct Sculpt::Main : Input_event_handler, Rom_handler
_decorator_margins { _env, "decorator_margins", *this, &Main::_handle_window_layout_or_decorator_margins }; - Expanding_reporter _wm_focus { _env, "focus", "wm_focus" }; - Expanding_reporter _window_layout { _env, "window_layout", "window_layout" }; + Expanding_reporter _wm_focus { _env, "focus", "wm_focus" }; + Expanding_reporter _window_layout { _env, "window_layout", "window_layout" }; + Expanding_reporter _resize_request { _env, "resize_request", "resize_request" }; template void _with_window(Xml_node window_list, String const &match, auto const &fn) @@ -1906,10 +1907,20 @@ void Sculpt::Main::_update_window_layout(Xml_node const &decorator_margins, Point const inspect_p2(avail.x2() - margins.right - 1, avail.y2() - margins.bottom - 1); - _window_layout.generate([&] (Xml_generator &xml) { + auto generate_within_screen_boundary = [&] (auto const &fn) + { + _resize_request.generate([&] (Xml_generator &resize_xml) { + _window_layout.generate([&] (Xml_generator &xml) { + xml.node("boundary", [&] { + xml.attribute("width", _screen_size.w); + xml.attribute("height", _screen_size.h); + fn(xml, resize_xml); }); }); }); + }; + + generate_within_screen_boundary([&] (Xml_generator &xml, Xml_generator &resize_xml) { auto gen_window = [&] (Xml_node const &win, Rect rect) { - if (rect.valid()) { + if (rect.valid()) xml.node("window", [&] { xml.attribute("id", win.attribute_value("id", 0UL)); xml.attribute("xpos", rect.x1()); @@ -1918,7 +1929,15 @@ void Sculpt::Main::_update_window_layout(Xml_node const &decorator_margins, xml.attribute("height", rect.h()); xml.attribute("title", win.attribute_value("label", Label())); }); - } + }; + + auto gen_resize = [&] (Xml_node const &win, Area area) { + if (area.valid()) + resize_xml.node("window", [&] { + resize_xml.attribute("id", win.attribute_value("id", 0UL)); + resize_xml.attribute("width", area.w); + resize_xml.attribute("height", area.h); + }); }; /* window size limited to space unobstructed by the menu and log */ @@ -1935,7 +1954,10 @@ void Sculpt::Main::_update_window_layout(Xml_node const &decorator_margins, gen_window(win, panel); }); _with_window(window_list, Label("log"), [&] (Xml_node const &win) { - gen_window(win, Rect::compound(log_p1, log_p2)); }); + Rect const rect = Rect::compound(log_p1, log_p2); + gen_window(win, rect); + gen_resize(win, rect.area); + }); int system_right_xpos = 0; if (system_available()) { @@ -2058,8 +2080,12 @@ void Sculpt::Main::_update_window_layout(Xml_node const &decorator_margins, } _with_window(window_list, inspect_label, [&] (Xml_node const &win) { - if (_selected_tab == Panel_dialog::Tab::INSPECT) - gen_window(win, Rect::compound(inspect_p1, inspect_p2)); }); + if (_selected_tab == Panel_dialog::Tab::INSPECT) { + Rect const rect = Rect::compound(inspect_p1, inspect_p2); + gen_window(win, rect); + gen_resize(win, rect.area); + } + }); /* * Position runtime view centered within the inspect area, but allow diff --git a/repos/gems/src/app/themed_decorator/main.cc b/repos/gems/src/app/themed_decorator/main.cc index edf44d3bca..57c84d650c 100644 --- a/repos/gems/src/app/themed_decorator/main.cc +++ b/repos/gems/src/app/themed_decorator/main.cc @@ -37,6 +37,8 @@ struct Decorator::Main : Window_factory_base { Env &_env; + Heap _heap { _env.ram(), _env.rm() }; + Timer::Connection _timer { _env }; /* @@ -49,7 +51,9 @@ struct Decorator::Main : Window_factory_base return { .cs = _timer.curr_time().trunc_to_plain_ms().value / 10 }; } - Window_stack _window_stack = { *this }; + Window_stack _window_stack = { *this, _heap }; + + Windows _windows { }; /** * Handler for responding to window-layout changes @@ -84,8 +88,6 @@ struct Decorator::Main : Window_factory_base Animator _animator { }; - Heap _heap { _env.ram(), _env.rm() }; - Theme _theme { _env.ram(), _env.rm(), _heap }; Reporter _decorator_margins_reporter = { _env, "decorator_margins" }; @@ -177,19 +179,35 @@ struct Decorator::Main : Window_factory_base /** * Window_factory_base interface */ - Window_base *create(Xml_node window_node) override + Window_base::Ref &create_ref(Xml_node const &window_node) override { - return new (_heap) - Window(_env, window_node.attribute_value("id", 0U), - _gui, _animator, _theme, _decorator_config); + Windows::Id const id { window_node.attribute_value("id", 0U) }; + + Window_base *window_ptr = nullptr; + _windows.apply(id, + [&] (Window_base &window) { window_ptr = &window; }, + [&] /* missing */ { + window_ptr = new (_heap) + Window(_env, _windows, id, _gui, _animator, _theme, + _decorator_config); }); + + return *new (_heap) Window_base::Ref(*window_ptr); } /** * Window_factory_base interface */ - void destroy(Window_base *window) override + void destroy_ref(Window_base::Ref &ref) override { - Genode::destroy(_heap, static_cast(window)); + destroy(_heap, &ref); + } + + /** + * Window_factory_base interface + */ + void destroy_window(Window_base &window) override + { + destroy(_heap, &window); } }; @@ -233,11 +251,11 @@ static void update_hover_report(Genode::Xml_node pointer_node, Genode::Reporter::Xml_generator xml(hover_reporter, [&] () { - if (hover.window_id > 0) { + if (hover.window_id.value > 0) { xml.node("window", [&] () { - xml.attribute("id", hover.window_id); + xml.attribute("id", hover.window_id.value); if (hover.left_sizer) xml.node("left_sizer"); if (hover.right_sizer) xml.node("right_sizer"); diff --git a/repos/gems/src/app/themed_decorator/theme.cc b/repos/gems/src/app/themed_decorator/theme.cc index 3570c4efbd..807e1940cc 100644 --- a/repos/gems/src/app/themed_decorator/theme.cc +++ b/repos/gems/src/app/themed_decorator/theme.cc @@ -161,7 +161,9 @@ element_geometry(Genode::Ram_allocator &ram, Genode::Region_map &rm, Genode::Allocator &alloc, char const *sub_node_type, Texture_id texture_id) { - using namespace Decorator; + using Rect = Decorator::Rect; + using Point = Decorator::Point; + using Area = Decorator::Area; static Genode::Xml_node const node = metadata(alloc); diff --git a/repos/gems/src/app/themed_decorator/theme.h b/repos/gems/src/app/themed_decorator/theme.h index 32988982f6..600ea1c0cc 100644 --- a/repos/gems/src/app/themed_decorator/theme.h +++ b/repos/gems/src/app/themed_decorator/theme.h @@ -19,6 +19,7 @@ #include #include #include +#include namespace Decorator { @@ -29,10 +30,6 @@ namespace Decorator { using Pixel_surface = Genode::Surface; using Alpha_surface = Genode::Surface; - - using Area = Genode::Surface_base::Area; - using Point = Genode::Surface_base::Point; - using Rect = Genode::Surface_base::Rect; } diff --git a/repos/gems/src/app/themed_decorator/window.h b/repos/gems/src/app/themed_decorator/window.h index 7abdf8fb02..b82cb70e40 100644 --- a/repos/gems/src/app/themed_decorator/window.h +++ b/repos/gems/src/app/themed_decorator/window.h @@ -52,6 +52,8 @@ class Decorator::Window : public Window_base, public Animator::Item */ bool _gui_views_up_to_date = false; + Rect _clip { }; /* most recently used clipping rectangle */ + unsigned _topped_cnt = 0; Window_title _title { }; @@ -162,10 +164,11 @@ class Decorator::Window : public Window_base, public Animator::Item void stack_back_most() { _gui.enqueue(id()); } - void place(Rect rect, Point offset) + void place(Clip const &clip, Rect rect, Point offset) { - _gui.enqueue(id(), rect); - _gui.enqueue(id(), offset); + Rect const intersection = Rect::intersect(clip, rect); + _gui.enqueue(id(), intersection); + _gui.enqueue(id(), offset + rect.at - intersection.at); } }; @@ -263,7 +266,7 @@ class Decorator::Window : public Window_base, public Animator::Item _left_view { _gui, _gui_left_right }, _top_view { _gui, _gui_top_bottom }; - Content_view _content_view { _gui, (unsigned)id() }; + Content_view _content_view { _gui, unsigned(id().value) }; void _repaint_decorations(Gui_buffer &buffer, Area area) { @@ -352,10 +355,11 @@ class Decorator::Window : public Window_base, public Animator::Item public: - Window(Genode::Env &env, unsigned id, Gui::Connection &gui, - Animator &animator, Theme const &theme, Config const &config) + Window(Genode::Env &env, Windows &windows, Windows::Id id, + Gui::Connection &gui, Animator &animator, Theme const &theme, + Config const &config) : - Window_base(id), + Window_base(windows, id), Animator::Item(animator), _env(env), _theme(theme), _animator(animator), _gui(gui), _config(config) @@ -433,11 +437,10 @@ class Decorator::Window : public Window_base, public Animator::Item return _outer_from_inner_geometry(geometry()); } - void update_gui_views() override + void update_gui_views(Clip const &clip) override { - bool const gui_view_rect_up_to_date = - _gui_view_rect.p1() == geometry().p1() && - _gui_view_rect.p2() == geometry().p2(); + bool const gui_view_rect_up_to_date = (_gui_view_rect == geometry()) + && (_clip == clip); if (!_gui_views_up_to_date || !gui_view_rect_up_to_date) { @@ -448,20 +451,21 @@ class Decorator::Window : public Window_base, public Animator::Item /* update view positions */ Rect::Cut_remainder const r = outer.cut(inner); - _content_view.place(inner, Point(0, 0)); - _top_view .place(r.top, Point(0, 0)); - _left_view .place(r.left, Point(0, -r.top.h())); - _right_view .place(r.right, Point(-r.right.w(), -r.top.h())); - _bottom_view .place(r.bottom, Point(0, -theme_size.h + r.bottom.h())); + _content_view.place(clip, inner, Point(0, 0)); + _top_view .place(clip, r.top, Point(0, 0)); + _left_view .place(clip, r.left, Point(0, -r.top.h())); + _right_view .place(clip, r.right, Point(-r.right.w(), -r.top.h())); + _bottom_view .place(clip, r.bottom, Point(0, -theme_size.h + r.bottom.h())); _gui.execute(); _gui_view_rect = inner; _gui_views_up_to_date = true; + _clip = clip; } } - void draw(Canvas_base &, Rect, Draw_behind_fn const &) const override { } + void draw(Canvas_base &, Ref const &, Rect, Draw_behind_fn const &) const override { } void adapt_to_changed_config() { diff --git a/repos/gems/src/app/window_layouter/assign_list.h b/repos/gems/src/app/window_layouter/assign_list.h index 8d4ed67dbb..7ad88647f0 100644 --- a/repos/gems/src/app/window_layouter/assign_list.h +++ b/repos/gems/src/app/window_layouter/assign_list.h @@ -90,11 +90,24 @@ class Window_layouter::Assign_list : Noncopyable return result; } - template - void for_each(FN const &fn) { _assignments.for_each(fn); } + void for_each(auto const &fn) { _assignments.for_each(fn); } + void for_each(auto const &fn) const { _assignments.for_each(fn); } - template - void for_each(FN const &fn) const { _assignments.for_each(fn); } + void for_each_visible(auto const &target_name, auto const &fn) const + { + for_each([&] (Assign const &assign) { + if (assign.visible() && target_name == assign.target_name()) + fn(assign); }); + } + + bool target_empty(auto const &target_name) const + { + bool result = true; + for_each_visible(target_name, [&] (Assign const &assign) { + assign.for_each_member([&] (Assign::Member const &) { + result = false; }); }); + return result; + } }; #endif /* _ASSIGN_LIST_H_ */ diff --git a/repos/gems/src/app/window_layouter/target_list.h b/repos/gems/src/app/window_layouter/target_list.h index 69a5b72893..b65fa2d476 100644 --- a/repos/gems/src/app/window_layouter/target_list.h +++ b/repos/gems/src/app/window_layouter/target_list.h @@ -146,29 +146,28 @@ class Window_layouter::Target_list if (target.layer() >= min_layer && target.layer() <= layer) layer = target.layer(); }); - /* visit all windows on the layer */ - assignments.for_each([&] (Assign const &assign) { + /* search target by name */ + _targets.for_each([&] (Target const &target) { - if (!assign.visible()) + if (target.layer() != layer) return; - Target::Name const target_name = assign.target_name(); + if (!target.visible()) + return; - /* search target by name */ - _targets.for_each([&] (Target const &target) { + if (assignments.target_empty(target.name())) + return; - if (target.name() != target_name) - return; + Rect const boundary = target.geometry(); + xml.node("boundary", [&] { + xml.attribute("name", target.name()); + generate(xml, boundary); - if (target.layer() != layer) - return; - - if (!target.visible()) - return; - - /* found target area, iterate though all assigned windows */ - assign.for_each_member([&] (Assign::Member const &member) { - member.window.generate(xml, target.geometry()); }); + /* visit all windows on the layer */ + assignments.for_each_visible(target.name(), [&] (Assign const &assign) { + assign.for_each_member([&] (Assign::Member const &member) { + member.window.generate(xml, boundary); }); + }); }); }); diff --git a/repos/gems/src/app/window_layouter/types.h b/repos/gems/src/app/window_layouter/types.h index 43728ac1e0..d647949928 100644 --- a/repos/gems/src/app/window_layouter/types.h +++ b/repos/gems/src/app/window_layouter/types.h @@ -78,6 +78,14 @@ namespace Window_layouter { from.for_each_sub_node([&] (Xml_node const &sub_node) { copy_node(xml, sub_node, { max_depth.value - 1 }); }); }); } + + static void generate(Xml_generator &xml, Rect const &rect) + { + xml.attribute("xpos", rect.x1()); + xml.attribute("ypos", rect.y1()); + xml.attribute("width", rect.w()); + xml.attribute("height", rect.h()); + } } #endif /* _TYPES_H_ */ diff --git a/repos/gems/src/server/wm/decorator_gui.h b/repos/gems/src/server/wm/decorator_gui.h index 067606c2dc..e59ef3688f 100644 --- a/repos/gems/src/server/wm/decorator_gui.h +++ b/repos/gems/src/server/wm/decorator_gui.h @@ -64,6 +64,11 @@ struct Wm::Decorator_gui_session : Session_object, Window_registry::Id win_id; + Rect geometry { }; + Point offset { }; + + Rect content_geometry() const { return { geometry.p1() + offset, geometry.area }; } + Content_view_ref(Window_registry::Id win_id, Gui::View_ids &ids, View_id id) : id(*this, ids, id), win_id(win_id) { } }; @@ -152,35 +157,33 @@ struct Wm::Decorator_gui_session : Session_object, void _execute_command(Command const &cmd) { + /* + * If the content view changes position, propagate the new position to + * the GUI service to properly transform absolute input coordinates. + */ + auto with_content_view_ref = [&] (View_id id, auto const &fn) + { + _content_view_ids.apply(id, + [&] (Content_view_ref &ref) { + Rect const orig = ref.content_geometry(); + fn(ref); + if (orig != ref.content_geometry()) + _content_callback.content_geometry(ref.win_id, + ref.content_geometry()); }, + [&] { }); + }; + switch (cmd.opcode) { case Command::GEOMETRY: - /* - * If the content view changes position, propagate the new position - * to the GUI service to properly transform absolute input - * coordinates. - */ - _content_view_ids.apply(cmd.geometry.view, - [&] (Content_view_ref const &view_ref) { - _content_callback.content_geometry(view_ref.win_id, cmd.geometry.rect); }, - [&] { }); + with_content_view_ref(cmd.geometry.view, [&] (Content_view_ref &view_ref) { + view_ref.geometry = cmd.geometry.rect; }); /* forward command */ _real_gui.enqueue(cmd); return; - case Command::OFFSET: - - /* - * If non-content views change their offset (if the lookup - * fails), propagate the event - */ - _content_view_ids.apply(cmd.geometry.view, - [&] (Content_view_ref const &) { }, - [&] { _real_gui.enqueue(cmd); }); - return; - case Command::FRONT: case Command::BACK: case Command::FRONT_OF: @@ -192,7 +195,14 @@ struct Wm::Decorator_gui_session : Session_object, _real_gui.execute(); _content_callback.update_content_child_views(view_ref.win_id); }, [&] { }); + return; + case Command::OFFSET: + + with_content_view_ref(cmd.offset.view, [&] (Content_view_ref &view_ref) { + view_ref.offset = cmd.offset.offset; }); + + _real_gui.enqueue(cmd); return; case Command::TITLE: diff --git a/repos/gems/src/test/decorator_stress/main.cc b/repos/gems/src/test/decorator_stress/main.cc index 3f129d0b0a..289a0a70c3 100644 --- a/repos/gems/src/test/decorator_stress/main.cc +++ b/repos/gems/src/test/decorator_stress/main.cc @@ -45,30 +45,34 @@ struct Param }; -void report_window_layout(Param param, Genode::Reporter &reporter) +void report_window_layout(Param param, Genode::Expanding_reporter &reporter) { float w = 1024; float h = 768; - Genode::Reporter::Xml_generator xml(reporter, [&] () - { - for (unsigned i = 1; i <= 10; i++) { + reporter.generate([&] (Genode::Xml_generator &xml) { - xml.node("window", [&] () - { - xml.attribute("id", i); - xml.attribute("xpos", (long)(w * (0.25 + sin(param.angle[0])/5))); - xml.attribute("ypos", (long)(h * (0.25 + sin(param.angle[1])/5))); - xml.attribute("width", (long)(w * (0.25 + sin(param.angle[2])/5))); - xml.attribute("height", (long)(h * (0.25 + sin(param.angle[3])/5))); + xml.node("boundary", [&] { + xml.attribute("width", unsigned(w)); + xml.attribute("height", unsigned(h)); - if (i == 2) - xml.attribute("focused", "yes"); - }); + for (unsigned i = 1; i <= 10; i++) { - param = param + Param(2.2, 3.3, 4.4, 5.5); - } + xml.node("window", [&] { + xml.attribute("id", i); + xml.attribute("xpos", (long)(w * (0.25 + sin(param.angle[0])/5))); + xml.attribute("ypos", (long)(h * (0.25 + sin(param.angle[1])/5))); + xml.attribute("width", (long)(w * (0.25 + sin(param.angle[2])/5))); + xml.attribute("height", (long)(h * (0.25 + sin(param.angle[3])/5))); + + if (i == 2) + xml.attribute("focused", "yes"); + }); + + param = param + Param(2.2, 3.3, 4.4, 5.5); + } + }); }); } @@ -79,7 +83,8 @@ struct Main Param _param { 0, 1, 2, 3 }; - Genode::Reporter _window_layout_reporter { _env, "window_layout", "window_layout", 10*4096 }; + Genode::Expanding_reporter _window_layout_reporter { + _env, "window_layout", "window_layout" }; Timer::Connection _timer { _env }; @@ -95,7 +100,6 @@ struct Main Main(Genode::Env &env) : _env(env) { - _window_layout_reporter.enabled(true); _timer.sigh(_timer_handler); _timer.trigger_periodic(10*1000); } diff --git a/repos/os/include/decorator/types.h b/repos/os/include/decorator/types.h index cd5c0c6a34..ad9df150fc 100644 --- a/repos/os/include/decorator/types.h +++ b/repos/os/include/decorator/types.h @@ -27,16 +27,12 @@ namespace Decorator { - using Point = Genode::Surface_base::Point; - using Area = Genode::Surface_base::Area; - using Rect = Genode::Surface_base::Rect; - using Dirty_rect = Genode::Dirty_rect; + using namespace Genode; - using Genode::size_t; - using Genode::Color; - using Genode::Xml_node; - using Genode::List_model; - using Genode::Interface; + using Point = Surface_base::Point; + using Area = Surface_base::Area; + using Rect = Surface_base::Rect; + using Dirty_rect = Genode::Dirty_rect; } #endif /* _INCLUDE__DECORATOR__TYPES_H_ */ diff --git a/repos/os/include/decorator/window.h b/repos/os/include/decorator/window.h index bc097c9de8..9746163574 100644 --- a/repos/os/include/decorator/window.h +++ b/repos/os/include/decorator/window.h @@ -20,6 +20,8 @@ #include #include #include +#include +#include /* decorator includes */ #include @@ -30,15 +32,49 @@ namespace Decorator { class Canvas_base; class Window_base; - using Abandoned_windows = Genode::List >; + using Windows = Id_space; + using Abandoned_windows = Registry; using Reversed_windows = Genode::List >; } -class Decorator::Window_base : private Genode::List_model::Element +class Decorator::Window_base : private Windows::Element { public: + using Windows::Element::id; + + struct Ref; + + using Refs = List_model; + + /** + * Reference to a window + * + * The 'Ref' type decouples the lifetime of window objects from + * the lifetimes of their surrounding boundaries. If a window + * moves from one boundary to another, the old 'Ref' vanishes and + * a new 'Ref' is created but the window object stays intact. + */ + struct Ref : Refs::Element + { + Window_base &window; + + Registry::Element _registered; + + inline Ref(Window_base &); + + /** + * List_model::Element + */ + inline bool matches(Xml_node const &) const; + + /** + * List_model::Element + */ + static bool type_matches(Xml_node const &) { return true; } + }; + struct Border { unsigned top, left, right, bottom; @@ -59,7 +95,7 @@ class Decorator::Window_base : private Genode::List_model::Element maximizer = false, unmaximizer = false; - unsigned window_id = 0; + Windows::Id window_id { }; bool operator != (Hover const &other) const { @@ -84,7 +120,7 @@ class Decorator::Window_base : private Genode::List_model::Element */ struct Draw_behind_fn : Interface { - virtual void draw_behind(Canvas_base &, Window_base const &, Rect) const = 0; + virtual void draw_behind(Canvas_base &, Ref const &, Rect) const = 0; }; private: @@ -93,16 +129,13 @@ class Decorator::Window_base : private Genode::List_model::Element friend class Genode::List_model; friend class Genode::List; + Registry _refs { }; + /* * Geometry of content */ Rect _geometry { }; - /* - * Unique window ID - */ - unsigned const _id; - bool _stacked = false; /* @@ -110,30 +143,42 @@ class Decorator::Window_base : private Genode::List_model::Element */ Genode::Constructible _neighbor { }; - Genode::List_element _abandoned { this }; + Constructible _abandoned { }; Genode::List_element _reversed { this }; public: - Window_base(unsigned id) : _id(id) { } + Window_base(Windows &windows, Windows::Id id) + : + Windows::Element(*this, windows, id) + { } virtual ~Window_base() { } - void abandon(Abandoned_windows &abandoned_windows) + bool referenced() const { - abandoned_windows.insert(&_abandoned); + bool result = false; + _refs.for_each([&] (Ref const &) { result = true; }); + return result; } + void consider_as_abandoned(Abandoned_windows ®istry) + { + _abandoned.construct(registry, *this); + } + + /** + * Revert 'consider_as_abandoned' after window was temporarily not referenced + */ + void dont_abandon() { _abandoned.destruct(); } + void prepend_to_reverse_list(Reversed_windows &window_list) { window_list.insert(&_reversed); } - using List_model::Element::next; - - unsigned id() const { return _id; } - Rect geometry() const { return _geometry; } + Rect geometry() const { return _geometry; } void stacking_neighbor(Gui::View_id neighbor) { @@ -169,11 +214,8 @@ class Decorator::Window_base : private Genode::List_model::Element /** * Draw window elements - * - * \param canvas graphics back end - * \param clip clipping area to apply */ - virtual void draw(Canvas_base &canvas, Rect clip, Draw_behind_fn const &) const = 0; + virtual void draw(Canvas_base &, Ref const &, Rect clip, Draw_behind_fn const &) const = 0; /** * Update internal window representation from XML model @@ -187,7 +229,9 @@ class Decorator::Window_base : private Genode::List_model::Element */ virtual bool update(Xml_node window_node) = 0; - virtual void update_gui_views() { } + struct Clip : Rect { }; + + virtual void update_gui_views(Clip const &) { } /** * Report information about element at specified position @@ -207,7 +251,7 @@ class Decorator::Window_base : private Genode::List_model::Element */ bool matches(Xml_node const &node) const { - return _id == node.attribute_value("id", ~0UL); + return id() == Windows::Id { node.attribute_value("id", ~0UL) }; } /** @@ -216,4 +260,16 @@ class Decorator::Window_base : private Genode::List_model::Element static bool type_matches(Xml_node const &) { return true; } }; + +Decorator::Window_base::Ref::Ref(Window_base &window) +: + window(window), _registered(window._refs, *this) +{ } + + +bool Decorator::Window_base::Ref::matches(Xml_node const &node) const +{ + return window.id() == Windows::Id { node.attribute_value("id", ~0UL) }; +} + #endif /* _INCLUDE__DECORATOR__WINDOW_H_ */ diff --git a/repos/os/include/decorator/window_factory.h b/repos/os/include/decorator/window_factory.h index 967bba4003..a64ef45672 100644 --- a/repos/os/include/decorator/window_factory.h +++ b/repos/os/include/decorator/window_factory.h @@ -24,8 +24,13 @@ namespace Decorator { struct Decorator::Window_factory_base : Interface { - virtual Window_base *create (Xml_node) = 0; - virtual void destroy (Window_base *) = 0; + using Win_ref = Window_base::Ref; + + virtual Win_ref &create_ref(Xml_node const &) = 0; + + virtual void destroy_ref(Win_ref &) = 0; + + virtual void destroy_window(Window_base &) = 0; }; #endif /* _INCLUDE__DECORATOR__WINDOW_FACTORY_H_ */ diff --git a/repos/os/include/decorator/window_stack.h b/repos/os/include/decorator/window_stack.h index e305a84d49..99c0a68301 100644 --- a/repos/os/include/decorator/window_stack.h +++ b/repos/os/include/decorator/window_stack.h @@ -17,6 +17,7 @@ /* Genode includes */ #include #include +#include /* local includes */ #include @@ -31,13 +32,102 @@ class Decorator::Window_stack : public Window_base::Draw_behind_fn { private: - List_model _windows { }; - Window_factory_base &_window_factory; - Dirty_rect mutable _dirty_rect { }; + struct Boundary; + using Boundaries = List_model; - unsigned long _front_most_id = ~0UL; + using Abandoned_boundaries = Registry; - inline void _draw_rec(Canvas_base &canvas, Window_base const *win, + using Win_ref = Window_base::Ref; + + struct Boundary : Boundaries::Element + { + using Name = Genode::String<64>; + + Name const _name; + + Constructible::Element> _abandoned { }; + + Rect rect { }; + + List_model win_refs { }; + + Boundary(Name const &name) : _name(name) { } + + void update(Window_factory_base &factory, + Abandoned_windows &abandoned_windows, + Dirty_rect &dirty_rect, + Xml_node const &boundary) + { + rect = Rect::from_xml(boundary); + + win_refs.update_from_xml(boundary, + + [&] (Xml_node const &node) -> Win_ref & { + return factory.create_ref(node); }, + + [&] (Win_ref &ref) { + Window_base &window = ref.window; + factory.destroy_ref(ref); + if (!window.referenced()) + window.consider_as_abandoned(abandoned_windows); + }, + + [&] (Win_ref &ref, Xml_node const &node) { + Rect const orig_geometry = ref.window.outer_geometry(); + if (ref.window.update(node)) { + dirty_rect.mark_as_dirty(orig_geometry); + dirty_rect.mark_as_dirty(ref.window.outer_geometry()); + } + } + ); + } + + void abandon(Registry ®istry) + { + _abandoned.construct(registry, *this); + } + + static Name name(Xml_node const &node) + { + return node.attribute_value("name", Name()); + } + + /** + * List_model::Element + */ + bool matches(Xml_node const &node) const + { + return _name == name(node); + } + + /** + * List_model::Element + */ + static bool type_matches(Xml_node const &node) + { + return node.has_type("boundary"); + } + + /** + * Generate window list in reverse order + */ + Reversed_windows reversed_window_list() + { + Reversed_windows reversed { }; + win_refs.for_each([&] (Win_ref &ref) { + ref.window.prepend_to_reverse_list(reversed); }); + return reversed; + } + }; + + Boundaries _boundaries { }; + Window_factory_base &_window_factory; + Allocator &_alloc; + Dirty_rect mutable _dirty_rect { }; + + Windows::Id _front_most_id { ~0UL }; + + inline void _draw_rec(Canvas_base &canvas, Win_ref const *, Rect rect) const; static inline @@ -54,33 +144,38 @@ class Decorator::Window_stack : public Window_base::Draw_behind_fn throw Xml_node::Nonexistent_sub_node(); } - /** - * Generate window list in reverse order - */ - Reversed_windows _reversed_window_list() + void _for_each_window_const(auto const &fn) const { - Reversed_windows reversed { }; - _windows.for_each([&] (Window_base &window) { - window.prepend_to_reverse_list(reversed); }); - return reversed; + _boundaries.for_each([&] (Boundary const &boundary) { + boundary.win_refs.for_each([&] (Win_ref const &ref) { + fn(ref.window); }); }); } public: - Window_stack(Window_factory_base &window_factory) + Window_stack(Window_factory_base &window_factory, Allocator &alloc) : - _window_factory(window_factory) + _window_factory(window_factory), _alloc(alloc) { } void mark_as_dirty(Rect rect) { _dirty_rect.mark_as_dirty(rect); } + void for_each_window(auto const &fn) + { + _boundaries.for_each([&] (Boundary &boundary) { + boundary.win_refs.for_each([&] (Win_ref &ref) { + fn(ref.window); }); }); + } + Dirty_rect draw(Canvas_base &canvas) const { Dirty_rect result = _dirty_rect; _dirty_rect.flush([&] (Rect const &rect) { - _windows.with_first([&] (Window_base const &first) { - _draw_rec(canvas, &first, rect); }); }); + _boundaries.for_each([&] (Boundary const &boundary) { + Rect const clipped = Rect::intersect(rect, boundary.rect); + boundary.win_refs.with_first([&] (Win_ref const &first) { + _draw_rec(canvas, &first, clipped); }); }); }); return result; } @@ -91,8 +186,7 @@ class Decorator::Window_stack : public Window_base::Draw_behind_fn { bool redraw_needed = false; - _windows.for_each([&] (Window_base const &win) { - + _for_each_window_const([&] (Window_base const &win) { if (win.animated()) { _dirty_rect.mark_as_dirty(win.outer_geometry()); redraw_needed = true; @@ -101,41 +195,37 @@ class Decorator::Window_stack : public Window_base::Draw_behind_fn return redraw_needed; } - /** - * Apply functor to each window - * - * The functor is called with 'Window_base &' as argument. - */ - void for_each_window(auto const &fn) { _windows.for_each(fn); } - void update_gui_views() { - /* - * Update GUI views in reverse order (back-most first). The - * reverse order is important because the stacking position of a - * view is propagated by referring to the neighbor the view is in - * front of. By starting with the back-most view, we make sure that - * each view is always at its final stacking position when - * specified as neighbor of another view. - */ - Reversed_windows reversed = _reversed_window_list(); + _boundaries.for_each([&] (Boundary &boundary) { - while (Genode::List_element *win = reversed.first()) { - win->object()->update_gui_views(); - reversed.remove(win); - } + /* + * Update GUI views in reverse order (back-most first). The + * reverse order is important because the stacking position of + * a view is propagated by referring to the neighbor the view + * is in front of. By starting with the back-most view, we make + * sure that each view is always at its final stacking position + * when specified as neighbor of another view. + */ + Reversed_windows reversed = boundary.reversed_window_list(); + + while (Genode::List_element *win = reversed.first()) { + win->object()->update_gui_views({ boundary.rect }); + reversed.remove(win); + } + }); } Window_base::Hover hover(Point pos) const { Window_base::Hover result { }; - _windows.for_each([&] (Window_base const &win) { + _for_each_window_const([&] (Window_base const &win) { - if (!result.window_id && win.outer_geometry().contains(pos)) { + if (!result.window_id.value && win.outer_geometry().contains(pos)) { Window_base::Hover const hover = win.hover(pos); - if (hover.window_id != 0) + if (hover.window_id.value != 0) result = hover; } }); @@ -148,28 +238,27 @@ class Decorator::Window_stack : public Window_base::Draw_behind_fn ** Window::Draw_behind_fn interface ** **************************************/ - void draw_behind(Canvas_base &canvas, Window_base const &window, Rect clip) const override + void draw_behind(Canvas_base &canvas, Win_ref const &ref, Rect clip) const override { - _draw_rec(canvas, window.next(), clip); + _draw_rec(canvas, ref.next(), clip); } }; -void Decorator::Window_stack::_draw_rec(Decorator::Canvas_base &canvas, - Decorator::Window_base const *win, - Decorator::Rect rect) const +void Decorator::Window_stack::_draw_rec(Canvas_base &canvas, + Win_ref const *ref, Rect rect) const { Rect clipped; /* find next window that intersects with the rectangle */ - for ( ; win && !(clipped = Rect::intersect(win->outer_geometry(), rect)).valid(); ) - win = win->next(); + for ( ; ref && !(clipped = Rect::intersect(ref->window.outer_geometry(), rect)).valid(); ) + ref = ref->next(); /* check if we hit the bottom of the window stack */ - if (!win) return; + if (!ref) return; /* draw areas around the current window */ - if (Window_base const * const next = win->next()) { + if (Window_base::Ref const * const next = ref->next()) { Rect::Cut_remainder const r = rect.cut(clipped); @@ -180,50 +269,41 @@ void Decorator::Window_stack::_draw_rec(Decorator::Canvas_base &canvas, } /* draw current window */ - win->draw(canvas, clipped, *this); + ref->window.draw(canvas, *ref, clipped, *this); } void Decorator::Window_stack::update_model(Genode::Xml_node root_node, auto const &flush_window_stack_changes_fn) { - Abandoned_windows _abandoned_windows { }; + Abandoned_boundaries abandoned_boundaries { }; + Abandoned_windows abandoned_windows { }; - _windows.update_from_xml(root_node, + _boundaries.update_from_xml(root_node, - [&] (Xml_node const &node) -> Window_base & { - return *_window_factory.create(node); }, + [&] (Xml_node const &node) -> Boundary & { + return *new (_alloc) Boundary(Boundary::name(node)); }, - [&] (Window_base &window) { window.abandon(_abandoned_windows); }, + [&] (Boundary &boundary) { + boundary.update(_window_factory, abandoned_windows, _dirty_rect, + Xml_node("")); + boundary.abandon(abandoned_boundaries); + }, - [&] (Window_base &window, Xml_node const &node) - { - Rect const orig_geometry = window.outer_geometry(); - - if (window.update(node)) { - _dirty_rect.mark_as_dirty(orig_geometry); - _dirty_rect.mark_as_dirty(window.outer_geometry()); - } - } + [&] (Boundary &boundary, Xml_node const &node) { + boundary.update(_window_factory, abandoned_windows, _dirty_rect, node); } ); - unsigned long new_front_most_id = ~0UL; - if (root_node.has_sub_node("window")) - new_front_most_id = root_node.sub_node("window").attribute_value("id", ~0UL); + Windows::Id new_front_most_id { ~0UL }; - /* - * Propagate changed stacking order to the GUI server - * - * First, we reverse the window list. The 'reversed' list starts with - * the back-most window. We then go throuh each window back to front - * and check if its neighbor is consistent with its position in the - * window list. - */ - Reversed_windows reversed = _reversed_window_list(); + _boundaries.with_first([&] (Boundary const &boundary) { + boundary.win_refs.with_first([&] (Window_base::Ref const &ref) { + new_front_most_id = ref.window.id(); }); }); /* return true if window just came to front */ auto new_front_most_window = [&] (Window_base const &win) { - return (new_front_most_id != _front_most_id) && (win.id() == new_front_most_id); }; + return (new_front_most_id.value != _front_most_id.value) + && (win.id() == new_front_most_id); }; auto stack_back_most_window = [&] (Window_base &window) { @@ -251,27 +331,40 @@ void Decorator::Window_stack::update_model(Genode::Xml_node root_node, _dirty_rect.mark_as_dirty(window.outer_geometry()); }; - if (Genode::List_element *back_most = reversed.first()) { + _boundaries.for_each([&] (Boundary &boundary) { - /* handle back-most window */ - reversed.remove(back_most); - Window_base &window = *back_most->object(); - stack_back_most_window(window); - window.forget_neighbor(); + /* + * Propagate changed stacking order to the GUI server + * + * First, we reverse the window list. The 'reversed' list starts with + * the back-most window. We then go throuh each window back to front + * and check if its neighbor is consistent with its position in the + * window list. + */ + Reversed_windows reversed = boundary.reversed_window_list(); - Window_base *neighbor = &window; + if (Genode::List_element *back_most = reversed.first()) { - /* check consistency between window list order and view stacking */ - while (Genode::List_element *elem = reversed.first()) { + /* handle back-most window */ + reversed.remove(back_most); + Window_base &window = *back_most->object(); + stack_back_most_window(window); + window.forget_neighbor(); - reversed.remove(elem); + Window_base *neighbor = &window; - Window_base &window = *elem->object(); - stack_window(window, *neighbor); - window.stacking_neighbor(neighbor->frontmost_view()); - neighbor = &window; + /* check consistency between window list order and view stacking */ + while (Genode::List_element *elem = reversed.first()) { + + reversed.remove(elem); + + Window_base &window = *elem->object(); + stack_window(window, *neighbor); + window.stacking_neighbor(neighbor->frontmost_view()); + neighbor = &window; + } } - } + }); /* * Apply window-creation operations before destroying windows to prevent @@ -280,7 +373,7 @@ void Decorator::Window_stack::update_model(Genode::Xml_node root_node, flush_window_stack_changes_fn(); /* - * Destroy abandoned window objects + * Destroy abandoned window and boundary objects * * This is done after all other operations to avoid flickering whenever one * window is replaced by another one. If we first destroyed the original @@ -289,12 +382,19 @@ void Decorator::Window_stack::update_model(Genode::Xml_node root_node, * point when the new one already exists, one of both windows is visible at * all times. */ - Genode::List_element *elem = _abandoned_windows.first(), *next = nullptr; - for (; elem; elem = next) { - next = elem->next(); - _dirty_rect.mark_as_dirty(elem->object()->outer_geometry()); - _window_factory.destroy(elem->object()); - } + + abandoned_boundaries.for_each([&] (Boundary &boundary) { + destroy(_alloc, &boundary); }); + + abandoned_windows.for_each([&] (Window_base &window) { + if (window.referenced()) { + window.dont_abandon(); + } else { + Rect const rect = window.outer_geometry(); + _window_factory.destroy_window(window); + mark_as_dirty(rect); + } + }); _front_most_id = new_front_most_id; }