From 56cb1885bb71dcc94d0f003fed3d74b4307ee97d Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Wed, 12 Dec 2018 09:49:20 +0100 Subject: [PATCH] decorator: make window-layout updates more robust This patch improves the window decorators in the following respects: * Strict warnings are enabled now. * The use of the 'List_model' makes the application of window- layout changes more robust. This is particularly the case for the restacking of windows. * Display-mode changes are now supported by both decorators. Issue #3094 --- repos/gems/src/app/decorator/canvas.h | 2 +- repos/gems/src/app/decorator/config.h | 6 + repos/gems/src/app/decorator/main.cc | 56 +++- repos/gems/src/app/decorator/target.mk | 2 - repos/gems/src/app/decorator/window.cc | 23 +- repos/gems/src/app/decorator/window.h | 63 ++-- repos/gems/src/app/decorator/window_element.h | 6 +- repos/gems/src/app/themed_decorator/main.cc | 15 +- repos/gems/src/app/themed_decorator/target.mk | 2 - repos/gems/src/app/themed_decorator/theme.cc | 4 +- repos/gems/src/app/themed_decorator/window.h | 166 +++++----- repos/os/include/decorator/types.h | 4 + repos/os/include/decorator/window.h | 70 ++++- repos/os/include/decorator/window_factory.h | 2 +- repos/os/include/decorator/window_stack.h | 286 ++++++++---------- 15 files changed, 374 insertions(+), 333 deletions(-) diff --git a/repos/gems/src/app/decorator/canvas.h b/repos/gems/src/app/decorator/canvas.h index 3bc594ac47..18355a83f7 100644 --- a/repos/gems/src/app/decorator/canvas.h +++ b/repos/gems/src/app/decorator/canvas.h @@ -47,7 +47,7 @@ namespace Decorator { /** * Abstract interface of graphics back end */ -struct Decorator::Canvas_base +struct Decorator::Canvas_base : Interface { virtual Rect clip() const = 0; virtual void clip(Rect) = 0; diff --git a/repos/gems/src/app/decorator/config.h b/repos/gems/src/app/decorator/config.h index 0d7be2b15c..f6412302c8 100644 --- a/repos/gems/src/app/decorator/config.h +++ b/repos/gems/src/app/decorator/config.h @@ -82,6 +82,12 @@ class Decorator::Config private: + /** + * Noncopyable + */ + Config(Config const &); + Config & operator = (Config const &); + Genode::Allocator &_alloc; Reconstructible _buffered_config; diff --git a/repos/gems/src/app/decorator/main.cc b/repos/gems/src/app/decorator/main.cc index 2fc231844c..6c9f9e410c 100644 --- a/repos/gems/src/app/decorator/main.cc +++ b/repos/gems/src/app/decorator/main.cc @@ -41,15 +41,40 @@ struct Decorator::Main : Window_factory_base Nitpicker::Connection _nitpicker { _env }; - Framebuffer::Mode _mode = { _nitpicker.mode() }; + struct Canvas + { + Framebuffer::Mode const mode; + Attached_dataspace fb_ds; + Decorator::Canvas canvas; - Attached_dataspace _fb_ds = { _env.rm(), - (_nitpicker.buffer(_mode, false), - _nitpicker.framebuffer()->dataspace()) }; + Canvas(Env &env, Nitpicker::Connection &nitpicker) + : + mode(nitpicker.mode()), + fb_ds(env.rm(), + (nitpicker.buffer(mode, false), nitpicker.framebuffer()->dataspace())), + canvas(fb_ds.local_addr(), + Area(mode.width(), mode.height()), + env.ram(), env.rm()) + { } + }; - Canvas _canvas = { _fb_ds.local_addr(), - Area(_mode.width(), _mode.height()), - _env.ram(), _env.rm() }; + Reconstructible _canvas { _env, _nitpicker }; + + Signal_handler
_mode_handler { _env.ep(), *this, &Main::_handle_mode }; + + void _handle_mode() + { + _canvas.construct(_env, _nitpicker); + + _window_stack.mark_as_dirty(Rect(Point(0, 0), + Area(_canvas->mode.width(), + _canvas->mode.height()))); + + Dirty_rect dirty = _window_stack.draw(_canvas->canvas); + + dirty.flush([&] (Rect const &r) { + _nitpicker.framebuffer()->refresh(r.x1(), r.y1(), r.w(), r.h()); }); + } Window_stack _window_stack = { *this }; @@ -73,7 +98,7 @@ struct Decorator::Main : Window_factory_base Attached_rom_dataspace _pointer { _env, "pointer" }; - Window_base::Hover _hover; + Window_base::Hover _hover { }; Reporter _hover_reporter = { _env, "hover" }; @@ -81,7 +106,7 @@ struct Decorator::Main : Window_factory_base Reporter _decorator_margins_reporter = { _env, "decorator_margins" }; - Animator _animator; + Animator _animator { }; /** * Process the update every 'frame_period' nitpicker sync signals. The @@ -124,6 +149,8 @@ struct Decorator::Main : Window_factory_base _config.sigh(_config_handler); _handle_config(); + _nitpicker.mode_sigh(_mode_handler); + _window_layout.sigh(_window_layout_handler); _pointer.sigh(_pointer_handler); @@ -264,14 +291,14 @@ void Decorator::Main::_handle_nitpicker_sync() bool model_updated = false; + auto flush_window_stack_changes = [&] () { }; + if (_window_layout_update_needed && _window_layout.valid()) { try { Xml_node xml(_window_layout.local_addr(), _window_layout.size()); - auto flush_window_stack_changes = [&] () { }; - _window_stack.update_model(xml, flush_window_stack_changes); model_updated = true; @@ -288,9 +315,10 @@ void Decorator::Main::_handle_nitpicker_sync() /* * An error occured with processing the XML model. Flush the - * internal representation. + * internal representation with an empty window layout. */ - _window_stack.flush(); + _window_stack.update_model(Xml_node(""), + flush_window_stack_changes); } _window_layout_update_needed = false; @@ -309,7 +337,7 @@ void Decorator::Main::_handle_nitpicker_sync() if (!model_updated && !windows_animated) return; - Dirty_rect dirty = _window_stack.draw(_canvas); + Dirty_rect dirty = _window_stack.draw(_canvas->canvas); _window_stack.update_nitpicker_views(); diff --git a/repos/gems/src/app/decorator/target.mk b/repos/gems/src/app/decorator/target.mk index cafddb4d2b..9d8e3a139c 100644 --- a/repos/gems/src/app/decorator/target.mk +++ b/repos/gems/src/app/decorator/target.mk @@ -8,5 +8,3 @@ INC_DIR += $(PRG_DIR) vpath %.tff $(TFF_DIR) vpath %.rgba $(PRG_DIR) - -CC_CXX_WARN_STRICT = diff --git a/repos/gems/src/app/decorator/window.cc b/repos/gems/src/app/decorator/window.cc index b4709eabae..2ee1a85eaf 100644 --- a/repos/gems/src/app/decorator/window.cc +++ b/repos/gems/src/app/decorator/window.cc @@ -94,7 +94,7 @@ void Decorator::Window::draw(Decorator::Canvas_base &canvas, Point right_pos = controls_rect.p1() + Point(controls_rect.w() - _icon_size.w(), 0); if (_controls.num() > 0) { - for (unsigned i = _controls.num() - 1; i >= 0; i--) { + for (int i = _controls.num() - 1; i >= 0; i--) { Control control = _controls.control(i); @@ -193,23 +193,10 @@ void Decorator::Window::draw(Decorator::Canvas_base &canvas, } -bool Decorator::Window::update(Genode::Xml_node window_node, bool new_top_most) +bool Decorator::Window::update(Genode::Xml_node window_node) { bool updated = false; - /* - * Detect the need to bring the window to the top of the global - * view stack. - */ - unsigned const topped_cnt = attribute(window_node, "topped", 0UL); - if (topped_cnt != _topped_cnt || new_top_most) { - - _topped_cnt = topped_cnt; - - stack(Nitpicker::Session::View_handle()); - updated |= true; - } - /* * Detect geometry changes */ @@ -276,13 +263,13 @@ bool Decorator::Window::update(Genode::Xml_node window_node, bool new_top_most) Xml_node highlight = window_node.sub_node("highlight"); for (unsigned i = 0; i < num_elements(); i++) - updated |= _apply_state(_elements[i].type(), _focused, + updated |= _apply_state(_elements[i].type(), highlight.has_sub_node(_elements[i].type_name())); } catch (...) { /* window node has no "highlight" sub node, reset highlighting */ for (unsigned i = 0; i < num_elements(); i++) - updated |= _apply_state(_elements[i].type(), _focused, false); + updated |= _apply_state(_elements[i].type(), false); } return updated; @@ -354,7 +341,7 @@ Decorator::Window_base::Hover Decorator::Window::hover(Point abs_pos) const Point pos = titlbar_pos + Point(area.w() - _border_size - _icon_size.w(), 0); - for (unsigned i = _controls.num() - 1; i >= 0; i--) { + for (int i = _controls.num() - 1; i >= 0; i--) { /* controls end when we reach the title */ if (_controls.control(i).type() == Control::TYPE_TITLE) diff --git a/repos/gems/src/app/decorator/window.h b/repos/gems/src/app/decorator/window.h index d6902681e2..991b254405 100644 --- a/repos/gems/src/app/decorator/window.h +++ b/repos/gems/src/app/decorator/window.h @@ -37,12 +37,11 @@ class Decorator::Window : public Window_base */ bool _nitpicker_views_up_to_date = false; - Nitpicker::Session::View_handle _neighbor; - struct Nitpicker_view { - Nitpicker::Session_client &_nitpicker; - Nitpicker::Session::View_handle _handle { _nitpicker.create_view() }; + Nitpicker::Session_client &_nitpicker; + + View_handle _handle { _nitpicker.create_view() }; typedef Nitpicker::Session::Command Command; @@ -66,13 +65,18 @@ class Decorator::Window : public Window_base _nitpicker.destroy_view(_handle); } - Nitpicker::Session::View_handle handle() const { return _handle; } + View_handle handle() const { return _handle; } - void stack(Nitpicker::Session::View_handle neighbor) + void stack(View_handle neighbor) { _nitpicker.enqueue(_handle, neighbor); } + void stack_back_most() + { + _nitpicker.enqueue(_handle, View_handle()); + } + void place(Rect rect) { _nitpicker.enqueue(_handle, rect); @@ -96,7 +100,7 @@ class Decorator::Window : public Window_base unsigned _topped_cnt = 0; - Window_title _title; + Window_title _title { }; bool _focused = false; @@ -157,7 +161,7 @@ class Decorator::Window : public Window_base unsigned num_elements() const { return sizeof(_elements)/sizeof(Element); } - bool _apply_state(Window::Element::Type type, bool focused, bool highlighted) + bool _apply_state(Window::Element::Type type, bool highlighted) { return element(type).apply_state(_focused, highlighted, _base_color); } @@ -172,7 +176,7 @@ class Decorator::Window : public Window_base private: - Control _controls[MAX_CONTROLS]; + Control _controls[MAX_CONTROLS] { }; unsigned _num = 0; @@ -214,7 +218,7 @@ class Decorator::Window : public Window_base } }; - Controls _controls; + Controls _controls { }; /*********************** @@ -387,6 +391,14 @@ class Decorator::Window : public Window_base _window_control_texture(control)); } + void _stack_decoration_views() + { + _top_view.stack(_content_view.handle()); + _left_view.stack(_top_view.handle()); + _right_view.stack(_left_view.handle()); + _bottom_view.stack(_right_view.handle()); + } + public: Window(unsigned id, Nitpicker::Session_client &nitpicker, @@ -404,21 +416,27 @@ class Decorator::Window : public Window_base { return Border(_border_size + _title_height, _border_size, _border_size, _border_size); - } - void stack(Nitpicker::Session::View_handle neighbor) override + void stack(View_handle neighbor) override { - _neighbor = neighbor; - _content_view.stack(neighbor); - _top_view.stack(_content_view.handle()); - _left_view.stack(_top_view.handle()); - _right_view.stack(_left_view.handle()); - _bottom_view.stack(_right_view.handle()); + _stack_decoration_views(); } - Nitpicker::Session::View_handle frontmost_view() const override + void stack_front_most() override + { + _content_view.stack(View_handle()); + _stack_decoration_views(); + } + + void stack_back_most() override + { + _content_view.stack_back_most(); + _stack_decoration_views(); + } + + View_handle frontmost_view() const override { return _bottom_view.handle(); } @@ -434,11 +452,6 @@ class Decorator::Window : public Window_base outer_geometry().cut(geometry(), top, left, right, bottom); } - bool in_front_of(Window_base const &neighbor) const override - { - return _neighbor == neighbor.frontmost_view(); - } - void update_nitpicker_views() override { if (!_nitpicker_views_up_to_date) { @@ -464,7 +477,7 @@ class Decorator::Window : public Window_base void draw(Canvas_base &canvas, Rect clip, Draw_behind_fn const &) const override; - bool update(Xml_node, bool) override; + bool update(Xml_node) override; Hover hover(Point) const override; diff --git a/repos/gems/src/app/decorator/window_element.h b/repos/gems/src/app/decorator/window_element.h index 5644a7cd38..8a6ab0b716 100644 --- a/repos/gems/src/app/decorator/window_element.h +++ b/repos/gems/src/app/decorator/window_element.h @@ -44,19 +44,19 @@ class Decorator::Window_element : public Animator::Item Genode::min(c1.b + c2.b, 255)); } - Type _type; + Type const _type; /* * Rememeber base color to detect when it changes */ - Color _base_color; + Color _base_color { }; /* * Color value in 8.4 fixpoint format. We use four bits to * represent the fractional part to enable smooth * interpolation between the color values. */ - Lazy_value _r, _g, _b; + Lazy_value _r { }, _g { }, _b { }; bool _focused = false; bool _highlighted = false; diff --git a/repos/gems/src/app/themed_decorator/main.cc b/repos/gems/src/app/themed_decorator/main.cc index 9379ce097a..00a6d7ac75 100644 --- a/repos/gems/src/app/themed_decorator/main.cc +++ b/repos/gems/src/app/themed_decorator/main.cc @@ -56,9 +56,9 @@ struct Decorator::Main : Window_factory_base Signal_handler
_pointer_handler = { _env.ep(), *this, &Main::_handle_pointer_update }; - Constructible _pointer; + Constructible _pointer { }; - Window_base::Hover _hover; + Window_base::Hover _hover { }; Reporter _hover_reporter = { _env, "hover" }; @@ -69,7 +69,7 @@ struct Decorator::Main : Window_factory_base bool _window_layout_update_needed = false; - Animator _animator; + Animator _animator { }; Heap _heap { _env.ram(), _env.rm() }; @@ -258,15 +258,15 @@ void Decorator::Main::_handle_nitpicker_sync() bool model_updated = false; + auto flush_window_stack_changes = [&] () { + _window_stack.update_nitpicker_views(); }; + if (_window_layout_update_needed && _window_layout.valid()) { try { Xml_node xml(_window_layout.local_addr(), _window_layout.size()); - auto flush_window_stack_changes = [&] () { - _window_stack.update_nitpicker_views(); }; - _window_stack.update_model(xml, flush_window_stack_changes); model_updated = true; @@ -285,7 +285,8 @@ void Decorator::Main::_handle_nitpicker_sync() * An error occured with processing the XML model. Flush the * internal representation. */ - _window_stack.flush(); + _window_stack.update_model(Xml_node(""), + flush_window_stack_changes); } _window_layout_update_needed = false; diff --git a/repos/gems/src/app/themed_decorator/target.mk b/repos/gems/src/app/themed_decorator/target.mk index 29f3a6518c..e5a0470828 100644 --- a/repos/gems/src/app/themed_decorator/target.mk +++ b/repos/gems/src/app/themed_decorator/target.mk @@ -8,5 +8,3 @@ INC_DIR += $(PRG_DIR) $(TARGET): plain_decorator_theme.tar plain_decorator_theme.tar: $(VERBOSE)cd $(PRG_DIR); tar cf $(PWD)/bin/$@ theme - -CC_CXX_WARN_STRICT = diff --git a/repos/gems/src/app/themed_decorator/theme.cc b/repos/gems/src/app/themed_decorator/theme.cc index 3203f0389e..6dde647fd1 100644 --- a/repos/gems/src/app/themed_decorator/theme.cc +++ b/repos/gems/src/app/themed_decorator/theme.cc @@ -120,6 +120,8 @@ Decorator::Area Decorator::Theme::background_size() const struct Margins_from_metadata : Decorator::Theme::Margins { Margins_from_metadata(char const *sub_node, Genode::Allocator &alloc) + : + Decorator::Theme::Margins() { Genode::Xml_node aura = metadata(alloc).sub_node(sub_node); top = aura.attribute_value("top", 0UL); @@ -259,7 +261,7 @@ void Decorator::Theme::draw_background(Decorator::Pixel_surface &pixel_surface, void Decorator::Theme::draw_title(Decorator::Pixel_surface &pixel_surface, - Decorator::Alpha_surface &alpha_surface, + Decorator::Alpha_surface &, char const *title) const { /* skip title drawing if the metadata lacks a title declaration */ diff --git a/repos/gems/src/app/themed_decorator/window.h b/repos/gems/src/app/themed_decorator/window.h index ffece1f39c..32f6fcd0bf 100644 --- a/repos/gems/src/app/themed_decorator/window.h +++ b/repos/gems/src/app/themed_decorator/window.h @@ -55,11 +55,9 @@ class Decorator::Window : public Window_base, public Animator::Item */ bool _nitpicker_views_up_to_date = false; - Nitpicker::Session::View_handle _neighbor; - unsigned _topped_cnt = 0; - Window_title _title; + Window_title _title { }; bool _focused = false; @@ -67,63 +65,73 @@ class Decorator::Window : public Window_base, public Animator::Item Animator &_animator; - struct Element : Animator::Item + class Element : public Animator::Item { - Theme::Element_type const type; + private: - char const * const attr; + /* + * Noncopyable + */ + Element(Element const &); + Element & operator = (Element const &); - bool _highlighted = false; - bool _present = false; + bool _highlighted = false; + bool _present = false; - Lazy_value alpha = 0; + int _alpha_dst() const + { + if (!_present) + return 0; - int _alpha_dst() const - { - if (!_present) - return 0; + return _highlighted ? 255 : 150; + } - return _highlighted ? 255 : 150; - } + void _update_alpha_dst() + { + if ((int)alpha == _alpha_dst()) + return; - void _update_alpha_dst() - { - if ((int)alpha == _alpha_dst()) - return; + alpha.dst(_alpha_dst(), 20); + animate(); + } - alpha.dst(_alpha_dst(), 20); - animate(); - } + public: - void highlighted(bool highlighted) - { - _highlighted = highlighted; - _update_alpha_dst(); - } + Theme::Element_type const type; - bool highlighted() const { return _highlighted; } + char const * const attr; - void present(bool present) - { - _present = present; - _update_alpha_dst(); - } + Lazy_value alpha = 0; - bool present() const { return _present; } + void highlighted(bool highlighted) + { + _highlighted = highlighted; + _update_alpha_dst(); + } - void animate() override - { - alpha.animate(); - animated((int)alpha != alpha.dst()); - } + bool highlighted() const { return _highlighted; } - Element(Animator &animator, Theme::Element_type type, char const *attr) - : - Animator::Item(animator), - type(type), attr(attr) - { - _update_alpha_dst(); - } + void present(bool present) + { + _present = present; + _update_alpha_dst(); + } + + bool present() const { return _present; } + + void animate() override + { + alpha.animate(); + animated((int)alpha != alpha.dst()); + } + + Element(Animator &animator, Theme::Element_type type, char const *attr) + : + Animator::Item(animator), + type(type), attr(attr) + { + _update_alpha_dst(); + } }; Element _closer { _animator, Theme::ELEMENT_TYPE_CLOSER, "closer" }; @@ -139,7 +147,6 @@ class Decorator::Window : public Window_base, public Animator::Item struct Nitpicker_view { typedef Nitpicker::Session::Command Command; - typedef Nitpicker::Session::View_handle View_handle; bool const _view_is_remote; @@ -202,6 +209,11 @@ class Decorator::Window : public Window_base, public Animator::Item _nitpicker.enqueue(_handle, neighbor); } + void stack_back_most() + { + _nitpicker.enqueue(_handle, View_handle()); + } + void place(Rect rect, Point offset) { _nitpicker.enqueue(_handle, rect); @@ -223,7 +235,7 @@ class Decorator::Window : public Window_base, public Animator::Item * represent the fractional part to enable smooth * interpolation between the color values. */ - Lazy_value _r, _g, _b; + Lazy_value _r { }, _g { }, _b { }; Color _color() const { return Color(_r >> 4, _g >> 4, _b >> 4); } @@ -232,14 +244,14 @@ class Decorator::Window : public Window_base, public Animator::Item * decorations. */ Nitpicker::Connection _nitpicker_top_bottom { _env }; - Genode::Constructible _buffer_top_bottom; + Genode::Constructible _buffer_top_bottom { }; /** * Nitpicker session that contains the left and right window * decorations. */ Nitpicker::Connection _nitpicker_left_right { _env }; - Genode::Constructible _buffer_left_right; + Genode::Constructible _buffer_left_right { }; Nitpicker_view _bottom_view { _nitpicker, _nitpicker_top_bottom }, _right_view { _nitpicker, _nitpicker_left_right }, @@ -313,6 +325,14 @@ class Decorator::Window : public Window_base, public Animator::Item _b.dst(_base_color.b << 4, 20); } + void _stack_decoration_views() + { + _top_view.stack(_content_view.handle()); + _left_view.stack(_top_view.handle()); + _right_view.stack(_left_view.handle()); + _bottom_view.stack(_right_view.handle()); + } + public: Window(Genode::Env &env, unsigned id, Nitpicker::Session_client &nitpicker, @@ -328,20 +348,27 @@ class Decorator::Window : public Window_base, public Animator::Item animate(); } - void stack(Nitpicker::Session::View_handle neighbor) override + void stack(View_handle neighbor) override { - _neighbor = neighbor; + _content_view.stack(neighbor); + _stack_decoration_views(); - _top_view.stack(neighbor); - _left_view.stack(_top_view.handle()); - _right_view.stack(_left_view.handle()); - _bottom_view.stack(_right_view.handle()); - _content_view.stack(_bottom_view.handle()); + } + void stack_front_most() override + { + _content_view.stack(View_handle()); + _stack_decoration_views(); } - Nitpicker::Session::View_handle frontmost_view() const override + void stack_back_most() override { - return _content_view.handle(); + _content_view.stack_back_most(); + _stack_decoration_views(); + } + + View_handle frontmost_view() const override + { + return _bottom_view.handle(); } Rect _decor_geometry() const @@ -371,11 +398,6 @@ class Decorator::Window : public Window_base, public Animator::Item outer_geometry().cut(geometry(), top, left, right, bottom); } - bool in_front_of(Window_base const &neighbor) const override - { - return _neighbor == neighbor.frontmost_view(); - } - void update_nitpicker_views() override { if (!_nitpicker_views_up_to_date) { @@ -405,24 +427,10 @@ class Decorator::Window : public Window_base, public Animator::Item animate(); } - bool update(Xml_node window_node, bool new_top_most) override + bool update(Xml_node window_node) override { bool updated = false; - /* - * Detect the need to bring the window to the top of the global - * view stack. - */ - unsigned const topped_cnt = attribute(window_node, "topped", 0UL); - if (topped_cnt != _topped_cnt || new_top_most) { - - _topped_cnt = topped_cnt; - - stack(Nitpicker::Session::View_handle()); - - updated = true; - } - bool trigger_animation = false; Rect const old_geometry = geometry(); diff --git a/repos/os/include/decorator/types.h b/repos/os/include/decorator/types.h index 1fa5d1e64b..d3bb7b0175 100644 --- a/repos/os/include/decorator/types.h +++ b/repos/os/include/decorator/types.h @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include namespace Decorator { @@ -34,6 +36,8 @@ namespace Decorator { using Genode::size_t; using Genode::Color; using Genode::Xml_node; + using Genode::List_model; + using Genode::Interface; } #endif /* _INCLUDE__DECORATOR__TYPES_H_ */ diff --git a/repos/os/include/decorator/window.h b/repos/os/include/decorator/window.h index 2ff74f394d..8c5f6262aa 100644 --- a/repos/os/include/decorator/window.h +++ b/repos/os/include/decorator/window.h @@ -15,9 +15,9 @@ #define _INCLUDE__DECORATOR__WINDOW_H_ /* Genode includes */ -#include #include #include +#include #include #include @@ -29,14 +29,18 @@ namespace Decorator { class Canvas_base; class Window_base; - typedef Genode::List Window_list; + + typedef Genode::List > Abandoned_windows; + typedef Genode::List > Reversed_windows; } -class Decorator::Window_base : public Window_list::Element +class Decorator::Window_base : private Genode::List_model::Element { public: + typedef Nitpicker::Session::View_handle View_handle; + struct Border { unsigned top, left, right, bottom; @@ -80,39 +84,83 @@ class Decorator::Window_base : public Window_list::Element * This functor is used for drawing the decorations of partially * transparent windows. It is implemented by the window stack. */ - struct Draw_behind_fn + struct Draw_behind_fn : Interface { virtual void draw_behind(Canvas_base &, Window_base const &, Rect) const = 0; }; private: + /* allow 'List_model' to access 'List_model::Element' */ + friend class Genode::List_model; + friend class Genode::List; + /* * Geometry of content */ - Rect _geometry; + Rect _geometry { }; /* * Unique window ID */ unsigned const _id; + bool _stacked = false; + + /* + * View immediately behind the window + */ + View_handle _neighbor { }; + + Genode::List_element _abandoned { this }; + + Genode::List_element _reversed { this }; + public: Window_base(unsigned id) : _id(id) { } + virtual ~Window_base() { } + + void abandon(Abandoned_windows &abandoned_windows) + { + abandoned_windows.insert(&_abandoned); + } + + void prepend_to_reverse_list(Reversed_windows &window_list) + { + window_list.insert(&_reversed); + } + + using List_model::Element::next; + unsigned long id() const { return _id; } Rect geometry() const { return _geometry; } + void stacking_neighbor(View_handle neighbor) + { + _neighbor = neighbor; + _stacked = true; + } + + bool stacked() const { return _stacked; } + + bool in_front_of(Window_base const &neighbor) const + { + return _neighbor == neighbor.frontmost_view(); + } + void geometry(Rect geometry) { _geometry = geometry; } virtual Rect outer_geometry() const = 0; - virtual void stack(Nitpicker::Session::View_handle neighbor) = 0; + virtual void stack(View_handle neighbor) = 0; - virtual Nitpicker::Session::View_handle frontmost_view() const = 0; + virtual void stack_front_most() = 0; - virtual bool in_front_of(Window_base const &neighbor) const = 0; + virtual void stack_back_most() = 0; + + virtual View_handle frontmost_view() const = 0; /** * Draw window elements @@ -125,10 +173,6 @@ class Decorator::Window_base : public Window_list::Element /** * Update internal window representation from XML model * - * \param new_top_most true if window became the new top-most - * window, which should prompt a corresponding - * nitpicker stacking operation. - * * \return true if window changed * * We do not immediately update the views as part of the update @@ -136,7 +180,7 @@ class Decorator::Window_base : public Window_list::Element * decorations haven't been redrawn already. If we updated the * nitpicker views at this point, we would reveal not-yet-drawn pixels. */ - virtual bool update(Xml_node window_node, bool new_top_most) = 0; + virtual bool update(Xml_node window_node) = 0; virtual void update_nitpicker_views() { } diff --git a/repos/os/include/decorator/window_factory.h b/repos/os/include/decorator/window_factory.h index f66a1f4a3b..967bba4003 100644 --- a/repos/os/include/decorator/window_factory.h +++ b/repos/os/include/decorator/window_factory.h @@ -22,7 +22,7 @@ namespace Decorator { } -struct Decorator::Window_factory_base +struct Decorator::Window_factory_base : Interface { virtual Window_base *create (Xml_node) = 0; virtual void destroy (Window_base *) = 0; diff --git a/repos/os/include/decorator/window_stack.h b/repos/os/include/decorator/window_stack.h index b2cdd94679..cec1d9b3ca 100644 --- a/repos/os/include/decorator/window_stack.h +++ b/repos/os/include/decorator/window_stack.h @@ -31,24 +31,15 @@ class Decorator::Window_stack : public Window_base::Draw_behind_fn { private: - Window_list _windows; - Window_factory_base &_window_factory; - Dirty_rect mutable _dirty_rect; + List_model _windows { }; + Window_factory_base &_window_factory; + Dirty_rect mutable _dirty_rect { }; - unsigned long _top_most_id = ~0UL; + unsigned long _front_most_id = ~0UL; inline void _draw_rec(Canvas_base &canvas, Window_base const *win, Rect rect) const; - Window_base *_lookup_by_id(unsigned const id) - { - for (Window_base *win = _windows.first(); win; win = win->next()) - if (win->id() == id) - return win; - - return nullptr; - } - static inline Xml_node _xml_node_by_window_id(Genode::Xml_node node, unsigned id) { @@ -63,24 +54,14 @@ class Decorator::Window_stack : public Window_base::Draw_behind_fn throw Xml_node::Nonexistent_sub_node(); } - void _destroy(Window_base &window) - { - _windows.remove(&window); - _window_factory.destroy(&window); - } - /** * Generate window list in reverse order - * - * After calling this method, the '_windows' list is empty. */ - Window_list _reversed_window_list() + Reversed_windows _reversed_window_list() { - Window_list reversed; - while (Window_base *w = _windows.first()) { - _windows.remove(w); - reversed.insert(w); - } + Reversed_windows reversed { }; + _windows.for_each([&] (Window_base &window) { + window.prepend_to_reverse_list(reversed); }); return reversed; } @@ -91,12 +72,15 @@ class Decorator::Window_stack : public Window_base::Draw_behind_fn _window_factory(window_factory) { } + void mark_as_dirty(Rect rect) { _dirty_rect.mark_as_dirty(rect); } + Dirty_rect draw(Canvas_base &canvas) const { Dirty_rect result = _dirty_rect; _dirty_rect.flush([&] (Rect const &rect) { - _draw_rec(canvas, _windows.first(), rect); }); + _windows.apply_first([&] (Window_base const &first) { + _draw_rec(canvas, &first, rect); }); }); return result; } @@ -108,12 +92,13 @@ class Decorator::Window_stack : public Window_base::Draw_behind_fn { bool redraw_needed = false; - for (Window_base *win = _windows.first(); win; win = win->next()) { - if (win->animated()) { - _dirty_rect.mark_as_dirty(win->outer_geometry()); + _windows.for_each([&] (Window_base const &win) { + + if (win.animated()) { + _dirty_rect.mark_as_dirty(win.outer_geometry()); redraw_needed = true; } - } + }); return redraw_needed; } @@ -123,11 +108,7 @@ class Decorator::Window_stack : public Window_base::Draw_behind_fn * The functor is called with 'Window_base &' as argument. */ template - void for_each_window(FUNC const &func) - { - for (Window_base *win = _windows.first(); win; win = win->next()) - func(*win); - } + void for_each_window(FUNC const &func) { _windows.for_each(func); } void update_nitpicker_views() { @@ -139,32 +120,29 @@ class Decorator::Window_stack : public Window_base::Draw_behind_fn * each view is always at its final stacking position when * specified as neighbor of another view. */ - Window_list reversed = _reversed_window_list(); + Reversed_windows reversed = _reversed_window_list(); - while (Window_base *win = reversed.first()) { - win->update_nitpicker_views(); + while (Genode::List_element *win = reversed.first()) { + win->object()->update_nitpicker_views(); reversed.remove(win); - _windows.insert(win); } } - void flush() - { - while (Window_base *window = _windows.first()) - _destroy(*window); - } - Window_base::Hover hover(Point pos) const { - for (Window_base const *win = _windows.first(); win; win = win->next()) - if (win->outer_geometry().contains(pos)) { + Window_base::Hover result { }; - Window_base::Hover const hover = win->hover(pos); + _windows.for_each([&] (Window_base const &win) { + + if (!result.window_id && win.outer_geometry().contains(pos)) { + + Window_base::Hover const hover = win.hover(pos); if (hover.window_id != 0) - return hover; + result = hover; } + }); - return Window_base::Hover(); + return result; } @@ -187,7 +165,7 @@ void Decorator::Window_stack::_draw_rec(Decorator::Canvas_base &canvas, /* find next window that intersects with the rectangle */ for ( ; win && !(clipped = Rect::intersect(win->outer_geometry(), rect)).valid(); ) - win = win->next();; + win = win->next(); /* check if we hit the bottom of the window stack */ if (!win) return; @@ -212,116 +190,58 @@ template void Decorator::Window_stack::update_model(Genode::Xml_node root_node, FN const &flush_window_stack_changes) { - Window_list _destroyed_windows { }; + Abandoned_windows _abandoned_windows { }; - unsigned long new_top_most_id = ~0UL; - if (root_node.has_sub_node("window")) - new_top_most_id = root_node.sub_node("window").attribute_value("id", ~0UL); + struct Update_policy : List_model::Update_policy + { + Abandoned_windows &_abandoned_windows; + Window_factory_base &_window_factory; + Dirty_rect &_dirty_rect; - /* - * Step 1: Remove windows that are no longer present. - */ - for (Window_base *window = _windows.first(), *next = nullptr; window; window = next) { - next = window->next(); - try { - _xml_node_by_window_id(root_node, window->id()); + Update_policy(Abandoned_windows &abandoned_windows, + Window_factory_base &window_factory, + Dirty_rect &dirty_rect) + : + _abandoned_windows(abandoned_windows), + _window_factory(window_factory), + _dirty_rect(dirty_rect) + { } + + void destroy_element(Window_base &window) + { + window.abandon(_abandoned_windows); } - catch (Xml_node::Nonexistent_sub_node) { - _dirty_rect.mark_as_dirty(window->outer_geometry()); - _windows.remove(window); - _destroyed_windows.insert(window); - }; - } - /** - * Return true if window has came to front - */ - auto window_is_new_top_most = [&] (Window_base const &window) { - return (_top_most_id != new_top_most_id) && (window.id() == new_top_most_id); }; + Window_base &create_element(Xml_node node) + { + return *_window_factory.create(node); + } - /* - * Step 2: Update window properties of already present windows. - */ - for (Window_base *window = _windows.first(); window; window = window->next()) { + void update_element(Window_base &window, Xml_node node) + { + Rect const orig_geometry = window.outer_geometry(); - /* - * After step 1, a Xml_node::Nonexistent_sub_node exception can no - * longer occur. All windows remaining in the window stack are present - * in the XML model. - */ - try { - Rect const orig_geometry = window->outer_geometry(); - if (window->update(_xml_node_by_window_id(root_node, window->id()), - window_is_new_top_most(*window))) { + if (window.update(node)) { _dirty_rect.mark_as_dirty(orig_geometry); - _dirty_rect.mark_as_dirty(window->outer_geometry()); + _dirty_rect.mark_as_dirty(window.outer_geometry()); } } - catch (Xml_node::Nonexistent_sub_node) { - Genode::error("could not look up window ", window->id(), " in XML model"); } - } - /* - * Step 3: Add new appearing windows to the window stack. - */ - for_each_sub_node(root_node, "window", [&] (Xml_node window_node) { - - unsigned long const id = attribute(window_node, "id", 0UL); - - if (!_lookup_by_id(id)) { - - Window_base *new_window = _window_factory.create(window_node); - - if (new_window) { - - new_window->update(window_node, window_is_new_top_most(*new_window)); - - /* - * Insert new window in front of all other windows. - * - * Immediately propagate the new stacking position of the new - * window to nitpicker ('update_nitpicker_views'). Otherwise, - */ - new_window->stack(Nitpicker::Session::View_handle()); - - _windows.insert(new_window); - - _dirty_rect.mark_as_dirty(new_window->outer_geometry()); - } - } - }); - - /* - * Step 4: Adjust window order. - */ - Window_base *previous_window = nullptr; - Window_base *window = _windows.first(); - - for_each_sub_node(root_node, "window", [&] (Xml_node window_node) { - - if (!window) { - Genode::error("unexpected end of window list during re-ordering"); - return; + static bool element_matches_xml_node(Window_base const &elem, Xml_node node) + { + return elem.id() == node.attribute_value("id", ~0UL); } - unsigned long const id = attribute(window_node, "id", 0UL); + static bool node_is_element(Xml_node) { return true; } + }; - if (window->id() != id) { - window = _lookup_by_id(id); - if (!window) { - Genode::error("window lookup unexpectedly failed during re-ordering"); - return; - } + Update_policy policy { _abandoned_windows, _window_factory, _dirty_rect }; - _windows.remove(window); - _windows.insert(window, previous_window); + _windows.update_from_xml(policy, root_node); - _dirty_rect.mark_as_dirty(window->outer_geometry()); - } - - previous_window = window; - window = window->next(); - }); + 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); /* * Propagate changed stacking order to nitpicker @@ -331,27 +251,57 @@ void Decorator::Window_stack::update_model(Genode::Xml_node root_node, * and check if its neighbor is consistent with its position in the * window list. */ - Window_list reversed = _reversed_window_list(); + Reversed_windows reversed = _reversed_window_list(); - if (Window_base * const back_most = reversed.first()) { + /* 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); }; - /* keep back-most window as is */ + auto stack_back_most_window = [&] (Window_base &window) { + + if (window.stacked()) + return; + + if (new_front_most_window(window)) + window.stack_front_most(); + else + window.stack_back_most(); + + _dirty_rect.mark_as_dirty(window.outer_geometry()); + }; + + auto stack_window = [&] (Window_base &window, Window_base &neighbor) { + + if (window.stacked() && window.in_front_of(neighbor)) + return; + + if (new_front_most_window(window)) + window.stack_front_most(); + else + window.stack(neighbor.frontmost_view()); + + _dirty_rect.mark_as_dirty(window.outer_geometry()); + }; + + if (Genode::List_element *back_most = reversed.first()) { + + /* handle back-most window */ reversed.remove(back_most); - _windows.insert(back_most); + Window_base &window = *back_most->object(); + stack_back_most_window(window); + window.stacking_neighbor(Window_base::View_handle()); + + Window_base *neighbor = &window; /* check consistency between window list order and view stacking */ - while (Window_base *w = reversed.first()) { + while (Genode::List_element *elem = reversed.first()) { - Window_base * const neighbor = _windows.first(); + reversed.remove(elem); - reversed.remove(w); - _windows.insert(w); - - /* propagate change stacking order to nitpicker */ - if (w->in_front_of(*neighbor)) - continue; - - w->stack(neighbor->frontmost_view()); + Window_base &window = *elem->object(); + stack_window(window, *neighbor); + window.stacking_neighbor(neighbor->frontmost_view()); + neighbor = &window; } } @@ -362,7 +312,7 @@ void Decorator::Window_stack::update_model(Genode::Xml_node root_node, flush_window_stack_changes(); /* - * Destroy window objects. + * Destroy abandoned window 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 @@ -371,12 +321,14 @@ 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. */ - for (Window_base *window = _destroyed_windows.first(), *next = nullptr; window; window = next) { - next = window->next(); - _destroy(*window); + 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()); } - _top_most_id = new_top_most_id; + _front_most_id = new_front_most_id; } #endif /* _INCLUDE__DECORATOR__WINDOW_STACK_H_ */