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
This commit is contained in:
Norman Feske 2018-12-12 09:49:20 +01:00
parent f7d33010e5
commit 56cb1885bb
15 changed files with 374 additions and 333 deletions

View File

@ -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;

View File

@ -82,6 +82,12 @@ class Decorator::Config
private:
/**
* Noncopyable
*/
Config(Config const &);
Config & operator = (Config const &);
Genode::Allocator &_alloc;
Reconstructible<Genode::Buffered_xml> _buffered_config;

View File

@ -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<Pixel_rgb565> 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<Pixel_rgb565>(),
Area(mode.width(), mode.height()),
env.ram(), env.rm())
{ }
};
Canvas<Pixel_rgb565> _canvas = { _fb_ds.local_addr<Pixel_rgb565>(),
Area(_mode.width(), _mode.height()),
_env.ram(), _env.rm() };
Reconstructible<Canvas> _canvas { _env, _nitpicker };
Signal_handler<Main> _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<char>(),
_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("<window_layout/>"),
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();

View File

@ -8,5 +8,3 @@ INC_DIR += $(PRG_DIR)
vpath %.tff $(TFF_DIR)
vpath %.rgba $(PRG_DIR)
CC_CXX_WARN_STRICT =

View File

@ -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)

View File

@ -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<Command::To_front>(_handle, neighbor);
}
void stack_back_most()
{
_nitpicker.enqueue<Command::To_back>(_handle, View_handle());
}
void place(Rect rect)
{
_nitpicker.enqueue<Command::Geometry>(_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;

View File

@ -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<int> _r, _g, _b;
Lazy_value<int> _r { }, _g { }, _b { };
bool _focused = false;
bool _highlighted = false;

View File

@ -56,9 +56,9 @@ struct Decorator::Main : Window_factory_base
Signal_handler<Main> _pointer_handler = {
_env.ep(), *this, &Main::_handle_pointer_update };
Constructible<Attached_rom_dataspace> _pointer;
Constructible<Attached_rom_dataspace> _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<char>(),
_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("<window_layout/>"),
flush_window_stack_changes);
}
_window_layout_update_needed = false;

View File

@ -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 =

View File

@ -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 */

View File

@ -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<int> 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<int> 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<Command::To_front>(_handle, neighbor);
}
void stack_back_most()
{
_nitpicker.enqueue<Command::To_back>(_handle, View_handle());
}
void place(Rect rect, Point offset)
{
_nitpicker.enqueue<Command::Geometry>(_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<int> _r, _g, _b;
Lazy_value<int> _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<Nitpicker_buffer> _buffer_top_bottom;
Genode::Constructible<Nitpicker_buffer> _buffer_top_bottom { };
/**
* Nitpicker session that contains the left and right window
* decorations.
*/
Nitpicker::Connection _nitpicker_left_right { _env };
Genode::Constructible<Nitpicker_buffer> _buffer_left_right;
Genode::Constructible<Nitpicker_buffer> _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();

View File

@ -21,6 +21,8 @@
#include <util/geometry.h>
#include <util/color.h>
#include <util/dirty_rect.h>
#include <util/list_model.h>
#include <util/interface.h>
#include <os/surface.h>
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_ */

View File

@ -15,9 +15,9 @@
#define _INCLUDE__DECORATOR__WINDOW_H_
/* Genode includes */
#include <util/list.h>
#include <util/string.h>
#include <util/xml_generator.h>
#include <util/list_model.h>
#include <nitpicker_session/client.h>
#include <base/snprintf.h>
@ -29,14 +29,18 @@
namespace Decorator {
class Canvas_base;
class Window_base;
typedef Genode::List<Window_base> Window_list;
typedef Genode::List<Genode::List_element<Window_base> > Abandoned_windows;
typedef Genode::List<Genode::List_element<Window_base> > Reversed_windows;
}
class Decorator::Window_base : public Window_list::Element
class Decorator::Window_base : private Genode::List_model<Window_base>::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<Window_base>;
friend class Genode::List<Window_base>;
/*
* 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<Window_base> _abandoned { this };
Genode::List_element<Window_base> _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<Window_base>::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() { }

View File

@ -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;

View File

@ -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<Window_base> _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 <typename FUNC>
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<Window_base> *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 <typename FN>
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<Window_base>::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<Window_base> *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<Window_base> *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<Window_base> *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_ */