nitpicker: Perform redraw asynchronously

This patch changes nitpicker's way of redrawing. Originally, redraw
operations were triggered immediately by the RPC functions invoked by
clients. In the presence of clients that invoked a large number of those
functions, the server could become overloaded with processing redraw
operations. The new version performs redraw operations out of band with
the RPC functions. Similar to the design of the DOpE GUI server, redraw
operations are processed periodically. The RPC functions merely modify
meta data and track the dirty areas that need to be updated.
Consequently, nitpicker's RPC functions become light-weight operations.

As a nice collateral effect of this patch, nitpicker's internal
structure could be simplified because the drawing backend is no longer
needed by the code that dispatches the RPC interface.
This commit is contained in:
Norman Feske 2014-04-30 13:51:45 +02:00
parent d5f57992ac
commit d22cddd1e8
13 changed files with 242 additions and 292 deletions

View File

@ -42,20 +42,30 @@ struct Background : private Texture_base, Session, View
** Session interface **
***********************/
void submit_input_event(Input::Event) { }
void submit_input_event(Input::Event) override { }
void submit_sync() override { }
/********************
** View interface **
********************/
int frame_size(Mode const &mode) const { return 0; }
void frame(Canvas_base &canvas, Mode const &mode) { }
int frame_size(Mode const &mode) const override { return 0; }
void frame(Canvas_base &canvas, Mode const &mode) const override { }
void draw(Canvas_base &canvas, Mode const &mode) const
void draw(Canvas_base &canvas, Mode const &mode) const override
{
Rect const view_rect = abs_geometry();
Clip_guard clip_guard(canvas, view_rect);
if (tmp_fb) {
for (unsigned i = 0; i < 7; i++) {
canvas.draw_box(view_rect, Color(i*2,i*6,i*16*2));
tmp_fb->refresh(0,0,1024,768);
}
}
canvas.draw_box(view_rect, color);
}
};

View File

@ -49,18 +49,19 @@ class Chunky_menubar : public Texture<PT>,
** Session interface **
***********************/
void submit_input_event(Input::Event) { }
void submit_input_event(Input::Event) override { }
void submit_sync() override { }
/********************
** View interface **
********************/
int frame_size(Mode const &mode) const { return 0; }
void frame(Canvas_base &canvas, Mode const &mode) { }
void draw(Canvas_base &canvas, Mode const &mode)
int frame_size(Mode const &mode) const override { return 0; }
void frame(Canvas_base &canvas, Mode const &mode) const override { }
void draw(Canvas_base &canvas, Mode const &mode) const override
{
Clip_guard clip_guard(canvas, *this);
Clip_guard clip_guard(canvas, geometry());
/* draw menubar content */
canvas.draw_texture(abs_position(), *this, Texture_painter::SOLID, BLACK, false);
@ -71,7 +72,7 @@ class Chunky_menubar : public Texture<PT>,
** Menubar interface **
***********************/
void state(Menubar_state const state)
void state(Menubar_state const state) override
{
*static_cast<Menubar_state *>(this) = state;

View File

@ -63,7 +63,7 @@ static Input::Event merge_motion_events(Input::Event const *ev, unsigned n)
static void import_input_events(Input::Event *ev_buf, unsigned num_ev,
User_state &user_state, Canvas_base &canvas)
User_state &user_state)
{
/*
* Take events from input event buffer, merge consecutive motion
@ -91,7 +91,7 @@ static void import_input_events(Input::Event *ev_buf, unsigned num_ev,
continue;
/* pass event to user state */
user_state.handle_event(curr, canvas);
user_state.handle_event(curr);
}
}

View File

@ -49,6 +49,7 @@ namespace Nitpicker {
struct Main;
}
using Genode::size_t;
using Genode::Allocator;
using Genode::Rpc_entrypoint;
@ -68,6 +69,9 @@ using Genode::Attached_ram_dataspace;
using Genode::Attached_dataspace;
Framebuffer::Session *tmp_fb;
/***************
** Utilities **
***************/
@ -80,46 +84,10 @@ extern char _binary_default_tff_start;
Text_painter::Font default_font(&_binary_default_tff_start);
class Flush_merger
{
private:
Rect _to_be_flushed = { Point(), Area() };
public:
bool defer = false;
Rect to_be_flushed() const { return _to_be_flushed; }
void merge(Rect rect)
{
if (_to_be_flushed.valid())
_to_be_flushed = Rect::compound(_to_be_flushed, rect);
else
_to_be_flushed = rect;
}
void reset() { _to_be_flushed = Rect(Point(), Area()); }
};
template <typename PT>
class Screen : public Canvas<PT>, public Flush_merger
struct Screen : public Canvas<PT>
{
protected:
/**
* Surface_base::Flusher interface
*/
void flush_pixels(Rect rect) { merge(rect); }
public:
/**
* Constructor
*/
Screen(PT *base, Area size) : Canvas<PT>(base, size) { }
Screen(PT *base, Area size) : Canvas<PT>(base, size) { }
};
@ -167,12 +135,6 @@ struct Buffer_provider
};
struct Canvas_accessor
{
virtual Canvas_base &canvas() = 0;
};
template <typename PT>
class Chunky_dataspace_texture : public Buffer,
public Texture<PT>
@ -321,17 +283,13 @@ class Framebuffer::Session_component : public Genode::Rpc_object<Session>
::Buffer *_buffer = 0;
View_stack &_view_stack;
::Session &_session;
Flush_merger &_flush_merger;
Framebuffer::Session &_framebuffer;
Canvas_accessor &_canvas_accessor;
Buffer_provider &_buffer_provider;
Signal_context_capability _mode_sigh;
Signal_context_capability _sync_sigh;
Framebuffer::Mode _mode;
bool _alpha = false;
Canvas_base &_canvas() { return _canvas_accessor.canvas(); }
public:
/**
@ -339,16 +297,12 @@ class Framebuffer::Session_component : public Genode::Rpc_object<Session>
*/
Session_component(View_stack &view_stack,
::Session &session,
Flush_merger &flush_merger,
Framebuffer::Session &framebuffer,
Canvas_accessor &canvas_accessor,
Buffer_provider &buffer_provider)
:
_view_stack(view_stack),
_session(session),
_flush_merger(flush_merger),
_framebuffer(framebuffer),
_canvas_accessor(canvas_accessor),
_buffer_provider(buffer_provider)
{ }
@ -373,6 +327,12 @@ class Framebuffer::Session_component : public Genode::Rpc_object<Session>
Signal_transmitter(_mode_sigh).submit();
}
void submit_sync()
{
if (_sync_sigh.valid())
Signal_transmitter(_sync_sigh).submit();
}
/************************************
** Framebuffer::Session interface **
@ -399,19 +359,9 @@ class Framebuffer::Session_component : public Genode::Rpc_object<Session>
void refresh(int x, int y, int w, int h) override
{
_view_stack.update_session_views(_canvas(), _session,
Rect(Point(x, y), Area(w, h)));
Rect const rect(Point(x, y), Area(w, h));
/* flush dirty pixels to physical frame buffer */
if (_flush_merger.defer == false) {
Rect r = _flush_merger.to_be_flushed();
_framebuffer.refresh(r.x1(), r.y1(), r.w(), r.h());
_flush_merger.reset();
}
_flush_merger.defer = true;
if (_sync_sigh.valid())
Signal_transmitter(_sync_sigh).submit();
_view_stack.mark_session_views_as_dirty(_session, rect);
}
};
@ -425,24 +375,19 @@ class View_component : public Genode::List<View_component>::Element,
View_stack &_view_stack;
::View _view;
Canvas_accessor &_canvas_accessor;
Rpc_entrypoint &_ep;
Canvas_base &_canvas() { return _canvas_accessor.canvas(); }
public:
/**
* Constructor
*/
View_component(::Session &session, View_stack &view_stack,
Canvas_accessor &canvas_accessor,
Rpc_entrypoint &ep, ::View *parent_view):
_view_stack(view_stack),
_view(session,
session.stay_top() ? ::View::STAY_TOP : ::View::NOT_STAY_TOP,
::View::NOT_TRANSPARENT, ::View::NOT_BACKGROUND, parent_view),
_canvas_accessor(canvas_accessor),
_ep(ep)
{ }
@ -454,31 +399,30 @@ class View_component : public Genode::List<View_component>::Element,
******************************/
int viewport(int x, int y, int w, int h,
int buf_x, int buf_y, bool redraw)
int buf_x, int buf_y, bool) override
{
/* transpose y position of top-level views by vertical session offset */
if (_view.top_level())
y += _view.session().v_offset();
_view_stack.viewport(_canvas(), _view,
Rect(Point(x, y), Area(w, h)),
Point(buf_x, buf_y), redraw);
_view_stack.viewport(_view, Rect(Point(x, y), Area(w, h)),
Point(buf_x, buf_y));
return 0;
}
int stack(Nitpicker::View_capability neighbor_cap, bool behind, bool redraw)
int stack(Nitpicker::View_capability neighbor_cap, bool behind, bool) override
{
Object_pool<View_component>::Guard nvc(_ep.lookup_and_lock(neighbor_cap));
::View *neighbor_view = nvc ? &nvc->view() : 0;
_view_stack.stack(_canvas(), _view, neighbor_view, behind, redraw);
_view_stack.stack(_view, neighbor_view, behind);
return 0;
}
int title(Title const &title)
int title(Title const &title) override
{
_view_stack.title(_canvas(), _view, title.string());
_view_stack.title(_view, title.string());
return 0;
}
};
@ -501,8 +445,6 @@ class Nitpicker::Session_component : public Genode::Rpc_object<Session>,
/* Framebuffer_session_component */
Framebuffer::Session_component _framebuffer_session_component;
Canvas_accessor &_canvas_accessor;
/* Input_session_component */
Input::Session_component _input_session_component;
@ -545,8 +487,6 @@ class Nitpicker::Session_component : public Genode::Rpc_object<Session>,
_buffer_size = 0;
}
Canvas_base &_canvas() { return _canvas_accessor.canvas(); }
public:
/**
@ -555,9 +495,7 @@ class Nitpicker::Session_component : public Genode::Rpc_object<Session>,
Session_component(Session_label const &label,
View_stack &view_stack,
Rpc_entrypoint &ep,
Flush_merger &flush_merger,
Framebuffer::Session &framebuffer,
Canvas_accessor &canvas_accessor,
int v_offset,
bool provides_default_bg,
bool stay_top,
@ -567,9 +505,7 @@ class Nitpicker::Session_component : public Genode::Rpc_object<Session>,
::Session(label, v_offset, stay_top),
_buffer_alloc(&buffer_alloc, ram_quota),
_framebuffer(framebuffer),
_framebuffer_session_component(view_stack, *this, flush_merger,
framebuffer, canvas_accessor, *this),
_canvas_accessor(canvas_accessor),
_framebuffer_session_component(view_stack, *this, framebuffer, *this),
_ep(ep), _view_stack(view_stack),
_framebuffer_session_cap(_ep.manage(&_framebuffer_session_component)),
_input_session_cap(_ep.manage(&_input_session_component)),
@ -595,9 +531,9 @@ class Nitpicker::Session_component : public Genode::Rpc_object<Session>,
void upgrade_ram_quota(size_t ram_quota) { _buffer_alloc.upgrade(ram_quota); }
/******************************************
** Nitpicker-internal session interface **
******************************************/
/**********************************
** Nitpicker-internal interface **
**********************************/
void submit_input_event(Input::Event e)
{
@ -614,6 +550,11 @@ class Nitpicker::Session_component : public Genode::Rpc_object<Session>,
_input_session_component.submit(&e);
}
void submit_sync() override
{
_framebuffer_session_component.submit_sync();
}
/*********************************
** Nitpicker session interface **
@ -636,7 +577,7 @@ class Nitpicker::Session_component : public Genode::Rpc_object<Session>,
* Use a heap partition!
*/
View_component *view = new (env()->heap())
View_component(*this, _view_stack, _canvas_accessor, _ep,
View_component(*this, _view_stack, _ep,
parent_view ? &parent_view->view() : 0);
if (parent_view)
@ -654,7 +595,7 @@ class Nitpicker::Session_component : public Genode::Rpc_object<Session>,
View_component *vc = dynamic_cast<View_component *>(_ep.lookup_and_lock(view_cap));
if (!vc) return;
_view_stack.remove_view(_canvas(), vc->view());
_view_stack.remove_view(vc->view());
_ep.dissolve(vc);
_view_list.remove(vc);
destroy(env()->heap(), vc);
@ -743,9 +684,7 @@ class Nitpicker::Root : public Genode::Root_component<Session_component>
Global_keys &_global_keys;
Framebuffer::Mode _scr_mode;
View_stack &_view_stack;
Flush_merger &_flush_merger;
Framebuffer::Session &_framebuffer;
Canvas_accessor &_canvas_accessor;
int _default_v_offset;
protected:
@ -774,9 +713,8 @@ class Nitpicker::Root : public Genode::Root_component<Session_component>
Session_component *session = new (md_alloc())
Session_component(Session_label(args), _view_stack, *ep(),
_flush_merger, _framebuffer, _canvas_accessor,
v_offset, provides_default_bg, stay_top,
*md_alloc(), unused_quota);
_framebuffer, v_offset, provides_default_bg,
stay_top, *md_alloc(), unused_quota);
session->apply_session_color();
_session_list.insert(session);
@ -806,14 +744,14 @@ class Nitpicker::Root : public Genode::Root_component<Session_component>
*/
Root(Session_list &session_list, Global_keys &global_keys,
Rpc_entrypoint &session_ep, View_stack &view_stack,
Allocator &md_alloc, Flush_merger &flush_merger,
Framebuffer::Session &framebuffer, Canvas_accessor &canvas_accessor,
Allocator &md_alloc,
Framebuffer::Session &framebuffer,
int default_v_offset)
:
Root_component<Session_component>(&session_ep, &md_alloc),
_session_list(session_list), _global_keys(global_keys),
_view_stack(view_stack), _flush_merger(flush_merger),
_framebuffer(framebuffer), _canvas_accessor(canvas_accessor),
_view_stack(view_stack),
_framebuffer(framebuffer),
_default_v_offset(default_v_offset)
{ }
};
@ -877,17 +815,6 @@ struct Nitpicker::Main
Genode::Volatile_object<Framebuffer_screen> fb_screen = { framebuffer };
struct Canvas_accessor : ::Canvas_accessor
{
Genode::Volatile_object<Framebuffer_screen> &fb_screen;
Canvas_accessor(Genode::Volatile_object<Framebuffer_screen> &fb_screen)
: fb_screen(fb_screen) { }
Canvas_base &canvas() override { return fb_screen->screen; }
} canvas_accessor = { fb_screen };
void handle_fb_mode(unsigned);
Signal_rpc_member<Main> fb_mode_dispatcher = { ep, *this, &Main::handle_fb_mode };
@ -915,10 +842,8 @@ struct Nitpicker::Main
*/
Genode::Sliced_heap sliced_heap = { env()->ram_session(), env()->rm_session() };
Root<PT> np_root = { session_list, global_keys, ep.rpc_ep(),
user_state, sliced_heap, fb_screen->screen,
framebuffer, canvas_accessor,
Framebuffer_screen::MENUBAR_HEIGHT };
Root<PT> np_root = { session_list, global_keys, ep.rpc_ep(), user_state,
sliced_heap, framebuffer, Framebuffer_screen::MENUBAR_HEIGHT };
Genode::Reporter pointer_reporter = { "pointer" };
@ -941,18 +866,20 @@ struct Nitpicker::Main
Signal_rpc_member<Main> input_dispatcher = { ep, *this, &Main::handle_input };
/*
* Dispatch input on periodic timer signals every 10 milliseconds
* Dispatch input and redraw periodically
*/
Timer::Connection timer;
Main(Server::Entrypoint &ep) : ep(ep)
{
// tmp_fb = &framebuffer;
fb_screen->menubar.state(Menubar_state(user_state, "", "", BLACK));
user_state.default_background(background);
user_state.stack(canvas_accessor.canvas(), mouse_cursor);
user_state.stack(canvas_accessor.canvas(), fb_screen->menubar);
user_state.stack(canvas_accessor.canvas(), background);
user_state.stack(mouse_cursor);
user_state.stack(fb_screen->menubar);
user_state.stack(background);
config()->sigh(config_dispatcher);
Signal_transmitter(config_dispatcher).submit();
@ -983,8 +910,7 @@ void Nitpicker::Main::handle_input(unsigned)
/* handle batch of pending events */
if (input.is_pending())
import_input_events(ev_buf, input.flush(), user_state,
canvas_accessor.canvas());
import_input_events(ev_buf, input.flush(), user_state);
Point const new_mouse_pos = user_state.mouse_pos();
Point const menubar_offset = Point(0, Framebuffer_screen::MENUBAR_HEIGHT);
@ -1002,18 +928,19 @@ void Nitpicker::Main::handle_input(unsigned)
/* update mouse cursor */
if (old_mouse_pos != new_mouse_pos)
user_state.viewport(canvas_accessor.canvas(), mouse_cursor,
Rect(new_mouse_pos, mouse_size),
Point(), true);
user_state.viewport(mouse_cursor,
Rect(new_mouse_pos, mouse_size), Point());
/* flush dirty pixels to physical frame buffer */
if (fb_screen->screen.defer == false) {
Rect const r = fb_screen->screen.to_be_flushed();
if (r.valid())
framebuffer.refresh(r.x1(), r.y1(), r.w(), r.h());
fb_screen->screen.reset();
/* perform redraw and flush pixels to the framebuffer */
user_state.draw(fb_screen->screen).flush([&] (Rect const &rect) {
framebuffer.refresh(rect.x1(), rect.y1(),
rect.w(), rect.h()); });
/* deliver framebuffer synchronization events */
if (!user_state.kill()) {
for (::Session *s = session_list.first(); s; s = s->next())
s->submit_sync();
}
fb_screen->screen.defer = false;
/*
* In kill mode, we do not leave the dispatch function in order to
@ -1054,7 +981,7 @@ void Nitpicker::Main::handle_config(unsigned)
s->apply_session_color();
/* redraw */
user_state.update_all_views(canvas_accessor.canvas());
user_state.update_all_views();
}
@ -1064,7 +991,7 @@ void Nitpicker::Main::handle_fb_mode(unsigned)
Menubar_state menubar_state = fb_screen->menubar.state();
/* remove old version of menu bar from view stack */
user_state.remove_view(canvas_accessor.canvas(), fb_screen->menubar, false);
user_state.remove_view(fb_screen->menubar, false);
/* reconstruct framebuffer screen and menu bar */
fb_screen.construct(framebuffer);
@ -1076,10 +1003,10 @@ void Nitpicker::Main::handle_fb_mode(unsigned)
fb_screen->menubar.state(menubar_state);
/* re-insert menu bar behind mouse cursor */
user_state.stack(canvas_accessor.canvas(), fb_screen->menubar, &mouse_cursor);
user_state.stack(fb_screen->menubar, &mouse_cursor);
/* redraw */
user_state.update_all_views(canvas_accessor.canvas());
user_state.update_all_views();
}

View File

@ -54,7 +54,7 @@ class Mode
/**
* Discard all references to specified view
*/
virtual void forget(Canvas_base &, View const &v) {
virtual void forget(View const &v) {
if (&v == _focused_view) _focused_view = 0; }
};

View File

@ -48,7 +48,8 @@ class Mouse_cursor : public Texture<PT>,
** Session interface **
***********************/
void submit_input_event(Input::Event) { }
void submit_input_event(Input::Event) override { }
void submit_sync() override { }
/********************
@ -59,18 +60,18 @@ class Mouse_cursor : public Texture<PT>,
* The mouse cursor is always displayed without a surrounding frame.
*/
int frame_size(Mode const &mode) const { return 0; }
int frame_size(Mode const &mode) const override { return 0; }
void frame(Canvas_base &canvas, Mode const &mode) const { }
void frame(Canvas_base &canvas, Mode const &mode) const override { }
void draw(Canvas_base &canvas, Mode const &mode) const
void draw(Canvas_base &canvas, Mode const &mode) const override
{
Rect const view_rect = abs_geometry();
Clip_guard clip_guard(canvas, view_rect);
/* draw area behind the mouse cursor */
_view_stack.draw_rec(canvas, view_stack_next(), 0, 0, view_rect);
_view_stack.draw_rec(canvas, view_stack_next(), view_rect);
/* draw mouse cursor */
canvas.draw_texture(view_rect.p1(), *this, Texture_painter::MASKED, BLACK, true);

View File

@ -61,6 +61,8 @@ class Session : public Session_list::Element
virtual void submit_input_event(Input::Event ev) = 0;
virtual void submit_sync() = 0;
Genode::Session_label const &label() const { return _label; }
Texture_base const *texture() const { return _texture; }

View File

@ -43,7 +43,7 @@ User_state::User_state(Global_keys &global_keys, Area view_stack_size, Menubar &
{ }
void User_state::handle_event(Input::Event ev, Canvas_base &canvas)
void User_state::handle_event(Input::Event ev)
{
Input::Keycode const keycode = ev.keycode();
Input::Event::Type const type = ev.type();
@ -101,13 +101,12 @@ void User_state::handle_event(Input::Event ev, Canvas_base &canvas)
struct Update_all_guard
{
User_state &user_state;
Canvas_base &canvas;
bool update_menubar = false;
bool update_views = false;
char const *menu_title = "";
Update_all_guard(User_state &user_state, Canvas_base &canvas)
: user_state(user_state), canvas(canvas) { }
Update_all_guard(User_state &user_state)
: user_state(user_state) { }
~Update_all_guard()
{
@ -123,9 +122,9 @@ void User_state::handle_event(Input::Event ev, Canvas_base &canvas)
user_state._menubar.state(state);
if (update_menubar || update_views)
user_state.update_all_views(canvas);
user_state.update_all_views();
}
} update_all_guard(*this, canvas);
} update_all_guard(*this);
/*
* Handle start of a key sequence
@ -138,7 +137,7 @@ void User_state::handle_event(Input::Event ev, Canvas_base &canvas)
*/
if (kill() && keycode == Input::BTN_LEFT) {
if (pointed_view)
lock_out_session(canvas, pointed_view->session());
lock_out_session(pointed_view->session());
/* leave kill mode */
update_all_guard.update_menubar = true;
@ -278,12 +277,12 @@ void User_state::handle_event(Input::Event ev, Canvas_base &canvas)
** Mode interface **
********************/
void User_state::forget(Canvas_base &canvas, View const &view)
void User_state::forget(View const &view)
{
if (focused_view() == &view) {
Mode::forget(canvas, view);
Mode::forget(view);
_menubar.state(Menubar_state(*this, "", "", BLACK));
update_all_views(canvas);
update_all_views();
}
if (_input_receiver && view.belongs_to(*_input_receiver))
_input_receiver = 0;

View File

@ -76,10 +76,10 @@ class User_state : public Mode, public View_stack
/**
* Handle input event
*
* This function controls the Nitpicker mode and the
* user state variables.
* This function controls the Nitpicker mode and the user state
* variables.
*/
void handle_event(Input::Event ev, Canvas_base &);
void handle_event(Input::Event ev);
/**
* Accessors
@ -89,7 +89,7 @@ class User_state : public Mode, public View_stack
/**
* Mode interface
*/
void forget(Canvas_base &, View const &) override;
void forget(View const &) override;
};
#endif

View File

@ -108,6 +108,14 @@ void View::draw(Canvas_base &canvas, Mode const &mode) const
*/
if (!canvas.clip().valid() || !&_session) return;
if (tmp_fb) {
for (unsigned i = 0; i < 7; i++) {
canvas.draw_box(view_rect, Color(i*2,i*6,i*16*2));
tmp_fb->refresh(0,0,1024,768);
}
}
/* allow alpha blending only in flat mode */
bool allow_alpha = mode.flat();

View File

@ -16,12 +16,19 @@
#include <util/string.h>
#include <util/list.h>
#include <util/dirty_rect.h>
#include "mode.h"
#include "session.h"
class Buffer;
#include <framebuffer_session/framebuffer_session.h>
extern Framebuffer::Session *tmp_fb;
typedef Genode::Dirty_rect<Rect, 3> Dirty_rect;
/*
* For each buffer, there is a list of views that belong to
@ -57,12 +64,12 @@ class View : public Same_buffer_list_elem,
Transparent const _transparent; /* background is partly visible */
Background _background; /* view is a background view */
View *_parent; /* parent view */
Rect _geometry; /* position and size relative to parent */
Rect _label_rect; /* position and size of label */
Point _buffer_off; /* offset to the visible buffer area */
Session &_session; /* session that created the view */
char _title[TITLE_LEN];
View *_parent; /* parent view */
Rect _geometry; /* position and size relative to parent */
Rect _label_rect; /* position and size of label */
Point _buffer_off; /* offset to the visible buffer area */
Session &_session; /* session that created the view */
char _title[TITLE_LEN];
Genode::List<View_parent_elem> _children;

View File

@ -38,7 +38,7 @@ static View const *last_stay_top_view(View const *view)
**************************/
template <typename VIEW>
VIEW *View_stack::_next_view(VIEW *view) const
VIEW *View_stack::_next_view(VIEW &view) const
{
Session * const active_session = _mode.focused_view() ?
&_mode.focused_view()->session() : 0;
@ -46,16 +46,17 @@ VIEW *View_stack::_next_view(VIEW *view) const
View * const active_background = active_session ?
active_session->background() : 0;
for (;;) {
view = view ? view->view_stack_next() : 0;
for (VIEW *next_view = &view; ;) {
next_view = next_view->view_stack_next();
/* check if we hit the bottom of the view stack */
if (!view) return 0;
if (!next_view) return 0;
if (!view->background()) return view;
if (!next_view->background()) return next_view;
if (is_default_background(view) || view == active_background)
return view;
if (is_default_background(*next_view) || next_view == active_background)
return next_view;
/* view is a background view belonging to a non-focused session */
}
@ -82,7 +83,7 @@ View const *View_stack::_target_stack_position(View const *neighbor, bool behind
/* find target position in view stack */
View const *cv = last_stay_top_view(_first_view());
for ( ; cv; cv = _next_view(cv)) {
for ( ; cv; cv = _next_view(*cv)) {
/* bring view to front? */
if (behind && !neighbor)
@ -93,11 +94,11 @@ View const *View_stack::_target_stack_position(View const *neighbor, bool behind
break;
/* insert view in front of cv? */
if (!behind && (_next_view(cv) == neighbor))
if (!behind && (_next_view(*cv) == neighbor))
break;
/* insert view in front of the background? */
if (!behind && !neighbor && _next_view(cv)->background())
if (!behind && !neighbor && _next_view(*cv)->background())
break;
}
@ -114,18 +115,18 @@ void View_stack::_optimize_label_rec(View const *cv, View const *lv, Rect rect,
/* find next view that intersects with the rectangle or the target view */
Rect clipped;
while (cv && cv != lv && !(clipped = Rect::intersect(_outline(*cv), rect)).valid())
cv = _next_view(cv);
cv = _next_view(*cv);
/* reached end of view stack */
if (!cv) return;
if (cv != lv && _next_view(cv)) {
if (cv != lv && _next_view(*cv)) {
/* cut current view from rectangle and go into sub rectangles */
Rect r[4];
rect.cut(clipped, &r[0], &r[1], &r[2], &r[3]);
for (int i = 0; i < 4; i++)
_optimize_label_rec(_next_view(cv), lv, r[i], optimal);
_optimize_label_rec(_next_view(*cv), lv, r[i], optimal);
return;
}
@ -150,16 +151,16 @@ void View_stack::_optimize_label_rec(View const *cv, View const *lv, Rect rect,
}
void View_stack::_place_labels(Canvas_base &canvas, Rect rect)
void View_stack::_place_labels(Rect rect)
{
/* do not calculate label positions in flat mode */
if (_mode.flat()) return;
/* ignore mouse cursor */
View const *start = _next_view(_first_view());
View *view = _next_view(_first_view());
View const *start = _next_view(*_first_view());
View *view = _next_view(*_first_view());
for (; view && _next_view(view); view = _next_view(view)) {
for (; view && _next_view(*view); view = _next_view(*view)) {
Rect const view_rect = view->abs_geometry();
if (Rect::intersect(view_rect, rect).valid()) {
@ -167,7 +168,7 @@ void View_stack::_place_labels(Canvas_base &canvas, Rect rect)
Rect old = view->label_rect(), best;
/* calculate best visible label position */
Rect rect = Rect::intersect(Rect(Point(), canvas.size()), view_rect);
Rect rect = Rect::intersect(Rect(Point(), _size), view_rect);
if (start) _optimize_label_rec(start, view, rect, &best);
/*
@ -181,21 +182,20 @@ void View_stack::_place_labels(Canvas_base &canvas, Rect rect)
view->label_pos(Point(x, best.y1()));
/* refresh old and new label positions */
refresh_view(canvas, *view, view, old);
refresh_view(canvas, *view, view, view->label_rect());
refresh_view(*view, old);
refresh_view(*view, view->label_rect());
}
}
}
void View_stack::draw_rec(Canvas_base &canvas, View const *view, View const *dst_view,
Session const *exclude, Rect rect) const
void View_stack::draw_rec(Canvas_base &canvas, View const *view, Rect rect) const
{
Rect clipped;
/* find next view that intersects with the current clipping rectangle */
for ( ; view && !(clipped = Rect::intersect(_outline(*view), rect)).valid(); )
view = _next_view(view);
view = _next_view(*view);
/* check if we hit the bottom of the view stack */
if (!view) return;
@ -203,94 +203,86 @@ void View_stack::draw_rec(Canvas_base &canvas, View const *view, View const *dst
Rect top, left, right, bottom;
rect.cut(clipped, &top, &left, &right, &bottom);
View const *next = _next_view(view);
View const *next = _next_view(*view);
/* draw areas at the top/left of the current view */
if (next && top.valid()) draw_rec(canvas, next, dst_view, exclude, top);
if (next && left.valid()) draw_rec(canvas, next, dst_view, exclude, left);
if (next && top.valid()) draw_rec(canvas, next, top);
if (next && left.valid()) draw_rec(canvas, next, left);
/* draw current view */
if (!dst_view || (dst_view == view) || view->transparent()) {
{
Clip_guard clip_guard(canvas, clipped);
/* draw background if view is transparent */
if (view->uses_alpha())
draw_rec(canvas, _next_view(view), 0, 0, clipped);
draw_rec(canvas, _next_view(*view), clipped);
view->frame(canvas, _mode);
if (!exclude || !view->belongs_to(*exclude))
view->draw(canvas, _mode);
view->draw(canvas, _mode);
}
/* draw areas at the bottom/right of the current view */
if (next && right.valid()) draw_rec(canvas, next, dst_view, exclude, right);
if (next && bottom.valid()) draw_rec(canvas, next, dst_view, exclude, bottom);
if (next && right.valid()) draw_rec(canvas, next, right);
if (next && bottom.valid()) draw_rec(canvas, next, bottom);
}
void View_stack::refresh_view(Canvas_base &canvas, View const &view,
View const *dst_view, Rect rect)
void View_stack::refresh_view(View const &view, Rect const rect)
{
/* clip argument agains view outline */
rect = Rect::intersect(rect, _outline(view));
Rect const intersection = Rect::intersect(rect, _outline(view));
draw_rec(canvas, _first_view(), dst_view, 0, rect);
_dirty_rect.mark_as_dirty(intersection);
view.for_each_child([&] (View const &child) { refresh_view(child, rect); });
}
void View_stack::viewport(Canvas_base &canvas, View &view, Rect rect,
Point buffer_off, bool do_redraw)
void View_stack::viewport(View &view, Rect const rect, Point const buffer_off)
{
Rect const old_compound = _compound_outline(view);
Rect const old_outline = _outline(view);
refresh_view(view, Rect(Point(), _size));
view.geometry(Rect(rect));
view.buffer_off(buffer_off);
Rect const new_compound = _compound_outline(view);
refresh_view(view, Rect(Point(), _size));
Rect const compound = Rect::compound(old_compound, new_compound);
Rect const compound = Rect::compound(old_outline, _outline(view));
/* update labels (except when moving the mouse cursor) */
if (&view != _first_view())
_place_labels(canvas, compound);
if (!_mode.flat())
do_redraw = true;
/* update area on screen */
draw_rec(canvas, _first_view(), 0, do_redraw ? 0 : &view.session(), compound);
_place_labels(compound);
}
void View_stack::stack(Canvas_base &canvas, View const &view,
View const *neighbor, bool behind, bool do_redraw)
void View_stack::stack(View const &view, View const *neighbor, bool behind)
{
_views.remove(&view);
_views.insert(&view, _target_stack_position(neighbor, behind));
_place_labels(canvas, view.abs_geometry());
_place_labels(view.abs_geometry());
/* refresh affected screen area */
refresh_view(canvas, view, 0, _outline(view));
_dirty_rect.mark_as_dirty(_outline(view));
}
void View_stack::title(Canvas_base &canvas, View &view, const char *title)
void View_stack::title(View &view, const char *title)
{
view.title(title);
_place_labels(canvas, view.abs_geometry());
refresh_view(canvas, view, 0, _outline(view));
_place_labels(view.abs_geometry());
_dirty_rect.mark_as_dirty(_outline(view));
}
View *View_stack::find_view(Point p)
{
/* skip mouse cursor */
View *view = _next_view(_first_view());
View *view = _next_view(*_first_view());
for ( ; view; view = _next_view(view))
for ( ; view; view = _next_view(*view))
if (view->input_response_at(p, _mode))
return view;
@ -298,7 +290,7 @@ View *View_stack::find_view(Point p)
}
void View_stack::remove_view(Canvas_base &canvas, View const &view, bool redraw)
void View_stack::remove_view(View const &view, bool redraw)
{
/* remember geometry of view to remove */
Rect rect = _outline(view);
@ -315,9 +307,7 @@ void View_stack::remove_view(Canvas_base &canvas, View const &view, bool redraw)
* the current one, resulting in a dangling pointer right after the view
* gets destructed by the caller of 'removed_view'.
*/
_mode.forget(canvas, view);
_mode.forget(view);
/* redraw area where the view was visible */
if (redraw)
draw_rec(canvas, _first_view(), 0, 0, rect);
_dirty_rect.mark_as_dirty(rect);
}

View File

@ -19,6 +19,7 @@
class Session;
class View_stack
{
private:
@ -26,20 +27,23 @@ class View_stack
Area _size;
Mode &_mode;
Genode::List<View_stack_elem> _views;
View *_default_background;
View *_default_background = nullptr;
Dirty_rect mutable _dirty_rect;
/**
* Return outline geometry of a view
*
* This geometry depends on the view geometry and the currently
* active Nitpicker mode. In non-flat modes, we incorporate the
* surrounding frame.
* This geometry depends on the view geometry and the currently active
* Nitpicker mode. In non-flat modes, we incorporate the surrounding
* frame.
*/
Rect _outline(View const &view) const;
/**
* Return top-most view of the view stack
*/
View const *_first_view_const() const { return static_cast<View const *>(_views.first()); }
View *_first_view() { return static_cast<View *>(_views.first()); }
/**
@ -55,20 +59,7 @@ class View_stack
/**
* Position labels that are affected by specified area
*/
void _place_labels(Canvas_base &, Rect);
/**
* Return compound rectangle covering the view and all of its children
*/
Rect _compound_outline(View const &view)
{
Rect rect = _outline(view);
view.for_each_child([&] (View const &child) {
rect = Rect::compound(_outline(child), rect); });
return rect;
}
void _place_labels(Rect);
/**
* Return view following the specified view in the view stack
@ -77,57 +68,75 @@ class View_stack
* usage.
*/
template <typename VIEW>
VIEW *_next_view(VIEW *view) const;
VIEW *_next_view(VIEW &view) const;
/**
* Schedule 'rect' to be redrawn
*/
void _mark_view_as_dirty(View &view, Rect rect)
{
_dirty_rect.mark_as_dirty(rect);
}
public:
/**
* Constructor
*/
View_stack(Area size, Mode &mode) :
_size(size), _mode(mode), _default_background(0) { }
View_stack(Area size, Mode &mode) : _size(size), _mode(mode)
{
_dirty_rect.mark_as_dirty(Rect(Point(0, 0), _size));
}
/**
* Return size
*/
Area size() const { return _size; }
void size(Area size) { _size = size; }
void size(Area size)
{
_size = size;
_dirty_rect.mark_as_dirty(Rect(Point(0, 0), _size));
}
/**
* Draw views in specified area (recursivly)
*
* \param view current view in view stack
* \param dst_view desired view to draw or NULL
* if all views should be drawn
* \param exclude do not draw views of this session
* \param view current view in view stack
*/
void draw_rec(Canvas_base &, View const *view, View const *dst_view, Session const *exclude, Rect) const;
void draw_rec(Canvas_base &, View const *view, Rect) const;
/**
* Draw whole view stack
* Draw dirty areas
*/
void update_all_views(Canvas_base &canvas)
Dirty_rect draw(Canvas_base &canvas) const
{
_place_labels(canvas, Rect(Point(), _size));
draw_rec(canvas, _first_view(), 0, 0, Rect(Point(), _size));
Dirty_rect result = _dirty_rect;
_dirty_rect.flush([&] (Rect const &rect) {
draw_rec(canvas, _first_view_const(), rect); });
return result;
}
/**
* Update all views belonging to the specified session
* Trigger redraw of the whole view stack
*/
void update_all_views()
{
_place_labels(Rect(Point(), _size));
_dirty_rect.mark_as_dirty(Rect(Point(), _size));
}
/**
* Mark all views belonging to the specified session as dirty
*
* \param Session Session that created the view
* \param Rect Buffer area to update
*
* Note: For now, we perform an independent view-stack traversal
* for each view when calling 'refresh_view'. This becomes
* a potentially high overhead with many views. Having
* a tailored 'draw_rec_session' function would overcome
* this problem.
*/
void update_session_views(Canvas_base &canvas, Session const &session, Rect rect)
void mark_session_views_as_dirty(Session const &session, Rect rect)
{
for (View const *view = _first_view(); view; view = view->view_stack_next()) {
for (View *view = _first_view(); view; view = view->view_stack_next()) {
if (!view->belongs_to(session))
continue;
@ -140,7 +149,8 @@ class View_stack
Rect const r = Rect::intersect(Rect(rect.p1() + offset,
rect.p2() + offset),
view->abs_geometry());
refresh_view(canvas, *view, view, r);
_mark_view_as_dirty(*view, r);
}
}
@ -148,20 +158,16 @@ class View_stack
* Refresh area within a view
*
* \param view view that should be updated on screen
* \param dst NULL if all views in the specified area should be
* refreshed or 'view' if the refresh should be limited to
* the specified view.
*/
void refresh_view(Canvas_base &, View const &view, View const *dst, Rect);
void refresh_view(View const &view, Rect);
/**
* Define position and viewport
*
* \param pos position of view on screen
* \param buffer_off view offset of displayed buffer
* \param do_redraw perform screen update immediately
*/
void viewport(Canvas_base &, View &view, Rect pos, Point buffer_off, bool do_redraw);
void viewport(View &view, Rect pos, Point buffer_off);
/**
* Insert view at specified position in view stack
@ -174,13 +180,12 @@ class View_stack
* bottom of the view stack, specify neighbor = 0 and
* behind = false.
*/
void stack(Canvas_base &, View const &view, View const *neighbor = 0,
bool behind = true, bool do_redraw = true);
void stack(View const &view, View const *neighbor = 0, bool behind = true);
/**
* Set view title
*/
void title(Canvas_base &, View &view, char const *title);
void title(View &view, char const *title);
/**
* Find view at specified position
@ -190,7 +195,7 @@ class View_stack
/**
* Remove view from view stack
*/
void remove_view(Canvas_base &, View const &, bool redraw = true);
void remove_view(View const &, bool redraw = true);
/**
* Define default background
@ -200,7 +205,7 @@ class View_stack
/**
* Return true if view is the default background
*/
bool is_default_background(View const *view) const { return view == _default_background; }
bool is_default_background(View const &view) const { return &view == _default_background; }
/**
* Remove all views of specified session from view stack
@ -208,11 +213,11 @@ class View_stack
* Rather than removing the views from the view stack, this function moves
* the session views out of the visible screen area.
*/
void lock_out_session(Canvas_base &canvas, Session const &session)
void lock_out_session(Session const &session)
{
View const *view = _first_view(), *next_view = view->view_stack_next();
while (view) {
if (view->belongs_to(session)) remove_view(canvas, *view);
if (view->belongs_to(session)) remove_view(*view);
view = next_view;
next_view = view ? view->view_stack_next() : 0;
}