nitpicker: add capture service

Issue #3812
This commit is contained in:
Norman Feske 2020-06-26 22:10:17 +02:00
parent 537b317273
commit 22cb6dded7
9 changed files with 392 additions and 72 deletions

View File

@ -6,4 +6,5 @@ input_session
timer_session
framebuffer_session
gui_session
capture_session
report_session

View File

@ -60,11 +60,14 @@ class Nitpicker::Canvas : public Canvas_base, public Surface_base::Flusher
{
private:
Point const _offset;
Surface<PT> _surface;
public:
Canvas(PT *base, Area size) : _surface(base, size)
Canvas(PT *base, Point offset, Area size)
:
_offset(offset), _surface(base, size)
{
_surface.flusher(this);
}
@ -76,13 +79,20 @@ class Nitpicker::Canvas : public Canvas_base, public Surface_base::Flusher
Area size() const override { return _surface.size(); }
Rect clip() const override { return _surface.clip(); }
Rect clip() const override
{
Rect const clip_rect = _surface.clip();
return Rect(clip_rect.p1() + _offset, clip_rect.area());
}
void clip(Rect rect) override { _surface.clip(rect); }
void clip(Rect rect) override
{
_surface.clip(Rect(rect.p1() - _offset, rect.area()));
}
void draw_box(Rect rect, Color color) override
{
Box_painter::paint(_surface, rect, color);
Box_painter::paint(_surface, Rect(rect.p1() - _offset, rect.area()), color);
}
void draw_texture(Point pos, Texture_base const &texture_base,
@ -90,13 +100,14 @@ class Nitpicker::Canvas : public Canvas_base, public Surface_base::Flusher
bool allow_alpha) override
{
Texture<PT> const &texture = static_cast<Texture<PT> const &>(texture_base);
Texture_painter::paint(_surface, texture, mix_color, pos, mode,
Texture_painter::paint(_surface, texture, mix_color, pos - _offset, mode,
allow_alpha);
}
void draw_text(Point pos, Font const &font,
Color color, char const *string) override
{
pos = pos - _offset;
Text_painter::paint(_surface, Text_painter::Position(pos.x(), pos.y()),
font, color, string);
}

View File

@ -0,0 +1,163 @@
/*
* \brief Capture session component
* \author Norman Feske
* \date 2020-06-27
*/
/*
* Copyright (C) 2020 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _CAPTURE_SESSION_H_
#define _CAPTURE_SESSION_H_
/* Genode includes */
#include <base/session_object.h>
#include <capture_session/capture_session.h>
namespace Nitpicker { class Capture_session; }
class Nitpicker::Capture_session : public Session_object<Capture::Session>
{
public:
struct Handler : Interface
{
/**
* Prompt nitpicker to adjust the screen size depending on all
* present capture buffers.
*/
virtual void capture_buffer_size_changed() = 0;
};
private:
Env &_env;
Constrained_ram_allocator _ram;
Handler &_handler;
View_stack const &_view_stack;
Area _buffer_size { };
Constructible<Attached_ram_dataspace> _buffer { };
Signal_context_capability _screen_size_sigh { };
using Dirty_rect = Genode::Dirty_rect<Rect, Affected_rects::NUM_RECTS>;
Dirty_rect _dirty_rect { };
public:
Capture_session(Env &env,
Resources const &resources,
Label const &label,
Diag const &diag,
Handler &handler,
View_stack const &view_stack)
:
Session_object(env.ep(), resources, label, diag),
_env(env),
_ram(env.ram(), _ram_quota_guard(), _cap_quota_guard()),
_handler(handler),
_view_stack(view_stack)
{
_dirty_rect.mark_as_dirty(Rect(Point(0, 0), view_stack.size()));
}
~Capture_session() { }
/*****************************************
** Interface used by 'Nitpicker::Main' **
*****************************************/
Area buffer_size() const { return _buffer_size; }
void mark_as_damaged(Rect rect)
{
// XXX not called yet
_dirty_rect.mark_as_dirty(rect);
}
void screen_size_changed()
{
if (_screen_size_sigh.valid())
Signal_transmitter(_screen_size_sigh).submit();
}
/*******************************
** Capture session interface **
*******************************/
Area screen_size() const override { return _view_stack.size(); }
void screen_size_sigh(Signal_context_capability sigh) override
{
_screen_size_sigh = sigh;
}
void buffer(Area size) override
{
_buffer_size = Area { };
if (size.count() == 0) {
_buffer.destruct();
return;
}
try {
_buffer.construct(_ram, _env.rm(), buffer_bytes(size));
_buffer_size = size;
_handler.capture_buffer_size_changed();
} catch (...) {
_handler.capture_buffer_size_changed();
throw;
}
}
Dataspace_capability dataspace() override
{
if (_buffer.constructed())
return _buffer->cap();
return Dataspace_capability();
}
Affected_rects capture_at(Point pos) override
{
if (!_buffer.constructed())
return Affected_rects { };
using Pixel = Pixel_rgb888;
Canvas<Pixel> canvas = { _buffer->local_addr<Pixel>(), pos, _buffer_size };
Rect const buffer_rect(Point(0, 0), _buffer_size);
Affected_rects affected { };
unsigned i = 0;
_dirty_rect.flush([&] (Rect const &rect) {
_view_stack.draw(canvas, rect);
if (i < Affected_rects::NUM_RECTS) {
Rect const translated(rect.p1() - pos, rect.area());
Rect const clipped = Rect::intersect(translated, buffer_rect);
affected.rects[i++] = clipped;
}
});
return affected;
}
};
#endif /* _CAPTURE_SESSION_H_ */

View File

@ -411,7 +411,7 @@ void Gui_session::execute()
Framebuffer::Mode Gui_session::mode()
{
return Framebuffer::Mode { .area = screen_area(_screen_size) };
return Framebuffer::Mode { .area = screen_area(_view_stack.size()) };
}

View File

@ -93,8 +93,6 @@ class Nitpicker::Gui_session : public Session_object<Gui::Session>,
Sliced_heap _session_alloc;
Area const &_screen_size;
Framebuffer::Session_component _framebuffer_session_component;
bool const _input_session_accounted = (
@ -179,7 +177,6 @@ class Nitpicker::Gui_session : public Session_object<Gui::Session>,
Focus_updater &focus_updater,
View &pointer_origin,
View &builtin_background,
Area const &screen_size,
bool provides_default_bg,
Reporter &focus_reporter,
Visibility_controller &visibility_controller)
@ -188,7 +185,6 @@ class Nitpicker::Gui_session : public Session_object<Gui::Session>,
_env(env),
_ram(env.ram(), _ram_quota_guard(), _cap_quota_guard()),
_session_alloc(_ram, env.rm()),
_screen_size(screen_size),
_framebuffer_session_component(view_stack, *this, *this),
_view_stack(view_stack), _focus_updater(focus_updater),
_pointer_origin(pointer_origin),

View File

@ -31,9 +31,11 @@
#include "clip_guard.h"
#include "pointer_origin.h"
#include "domain_registry.h"
#include "capture_session.h"
namespace Nitpicker {
class Gui_root;
class Capture_root;
struct Main;
}
@ -75,7 +77,6 @@ class Nitpicker::Gui_root : public Root_component<Gui_session>,
User_state &_user_state;
View &_pointer_origin;
View &_builtin_background;
Area const &_screen_size;
Reporter &_focus_reporter;
Reporter &_hover_reporter;
Focus_updater &_focus_updater;
@ -93,8 +94,8 @@ class Nitpicker::Gui_root : public Root_component<Gui_session>,
session_resources_from_args(args), label,
session_diag_from_args(args),
_view_stack, _focus_updater, _pointer_origin,
_builtin_background, _screen_size,
provides_default_bg, _focus_reporter, *this);
_builtin_background, provides_default_bg,
_focus_reporter, *this);
session->apply_session_policy(_config.xml(), _domain_registry);
_session_list.insert(session);
@ -152,7 +153,6 @@ class Nitpicker::Gui_root : public Root_component<Gui_session>,
View &pointer_origin,
View &builtin_background,
Allocator &md_alloc,
Area const &screen_size,
Reporter &focus_reporter,
Reporter &hover_reporter,
Focus_updater &focus_updater)
@ -163,8 +163,8 @@ class Nitpicker::Gui_root : public Root_component<Gui_session>,
_view_stack(view_stack), _user_state(user_state),
_pointer_origin(pointer_origin),
_builtin_background(builtin_background),
_screen_size(screen_size), _focus_reporter(focus_reporter),
_hover_reporter(hover_reporter), _focus_updater(focus_updater)
_focus_reporter(focus_reporter), _hover_reporter(hover_reporter),
_focus_updater(focus_updater)
{ }
@ -196,7 +196,92 @@ class Nitpicker::Gui_root : public Root_component<Gui_session>,
};
struct Nitpicker::Main : Focus_updater, View_stack::Damage
/*******************************************
** Implementation of the capture service **
*******************************************/
class Nitpicker::Capture_root : public Root_component<Capture_session>
{
private:
using Sessions = Registry<Registered<Capture_session>>;
Env &_env;
Sessions _sessions { };
View_stack const &_view_stack;
Capture_session::Handler &_handler;
protected:
Capture_session *_create_session(const char *args) override
{
return new (md_alloc())
Registered<Capture_session>(_sessions, _env,
session_resources_from_args(args),
session_label_from_args(args),
session_diag_from_args(args),
_handler, _view_stack);
}
void _upgrade_session(Capture_session *s, const char *args) override
{
s->upgrade(ram_quota_from_args(args));
s->upgrade(cap_quota_from_args(args));
}
void _destroy_session(Capture_session *session) override
{
Genode::destroy(md_alloc(), session);
/* shrink screen according to the remaining output back ends */
_handler.capture_buffer_size_changed();
}
public:
/**
* Constructor
*/
Capture_root(Env &env,
Allocator &md_alloc,
View_stack const &view_stack,
Capture_session::Handler &handler)
:
Root_component<Capture_session>(&env.ep().rpc_ep(), &md_alloc),
_env(env), _view_stack(view_stack), _handler(handler)
{ }
/**
* Determine the size of the bounding box of all capture pixel buffers
*/
Area bounding_box() const
{
Area result { 0, 0 };
_sessions.for_each([&] (Capture_session const &session) {
result = max_area(result, session.buffer_size()); });
return result;
}
/**
* Notify all capture clients about the changed screen size
*/
void screen_size_changed()
{
_sessions.for_each([&] (Capture_session &session) {
session.screen_size_changed(); });
}
void mark_as_damaged(Rect rect)
{
_sessions.for_each([&] (Capture_session &session) {
session.mark_as_damaged(rect); });
}
};
struct Nitpicker::Main : Focus_updater,
View_stack::Damage,
Capture_session::Handler
{
Env &_env;
@ -222,7 +307,7 @@ struct Nitpicker::Main : Focus_updater, View_stack::Damage
Attached_dataspace fb_ds;
Canvas<PT> screen = { fb_ds.local_addr<PT>(), mode.area };
Canvas<PT> screen = { fb_ds.local_addr<PT>(), Point(0, 0), mode.area };
Area size = screen.size();
@ -241,23 +326,7 @@ struct Nitpicker::Main : Focus_updater, View_stack::Damage
}
};
Reconstructible<Framebuffer_screen> _fb_screen = { _env.rm(), _framebuffer };
/**
* View_stack::Damage interface
*/
void mark_as_damaged(Rect rect) override
{
if (_fb_screen.constructed()) {
_fb_screen->dirty_rect.mark_as_dirty(rect);
}
}
Point _initial_pointer_pos()
{
Area const scr_size = _fb_screen->screen.size();
return Point(scr_size.w()/2, scr_size.h()/2);
}
Constructible<Framebuffer_screen> _fb_screen { };
void _handle_fb_mode();
void _report_displays();
@ -285,8 +354,8 @@ struct Nitpicker::Main : Focus_updater, View_stack::Damage
Tff_font const _font { _binary_default_tff_start, _glyph_buffer };
Focus _focus { };
View_stack _view_stack { _fb_screen->screen.size(), _focus, _font, *this };
User_state _user_state { _focus, _global_keys, _view_stack, _initial_pointer_pos() };
View_stack _view_stack { _focus, _font, *this };
User_state _user_state { _focus, _global_keys, _view_stack };
View_owner _global_view_owner { };
@ -313,10 +382,60 @@ struct Nitpicker::Main : Focus_updater, View_stack::Damage
Constructible<Attached_rom_dataspace> _focus_rom { };
Gui_root _root { _env, _config_rom, _session_list, *_domain_registry,
_global_keys, _view_stack, _user_state, _pointer_origin,
_builtin_background, _sliced_heap, _fb_screen->size,
_focus_reporter, _hover_reporter, *this };
Gui_root _gui_root { _env, _config_rom, _session_list, *_domain_registry,
_global_keys, _view_stack, _user_state, _pointer_origin,
_builtin_background, _sliced_heap,
_focus_reporter, _hover_reporter, *this };
Capture_root _capture_root { _env, _sliced_heap, _view_stack, *this };
/**
* View_stack::Damage interface
*/
void mark_as_damaged(Rect rect) override
{
if (_fb_screen.constructed()) {
_fb_screen->dirty_rect.mark_as_dirty(rect);
}
_capture_root.mark_as_damaged(rect);
}
/**
* Capture_session::Handler interface
*/
void capture_buffer_size_changed() override
{
/*
* Determine the new screen size, which is the bounding box of all
* present output back ends.
*/
Area new_size { 0, 0 };
if (_fb_screen.constructed())
new_size = max_area(new_size, _fb_screen->size);
new_size = max_area(new_size, _capture_root.bounding_box());
bool const size_changed = (new_size != _view_stack.size());
if (size_changed) {
_view_stack.size(new_size);
_user_state.sanitize_pointer_position();
_update_pointer_position();
_capture_root.screen_size_changed();
/* redraw */
_view_stack.update_all_views();
/* notify clients about the change screen mode */
for (Gui_session *s = _session_list.first(); s; s = s->next())
s->notify_mode_change();
_report_displays();
}
}
/**
* Focus_updater interface
@ -380,20 +499,30 @@ struct Nitpicker::Main : Focus_updater, View_stack::Damage
*/
bool _motion_activity = false;
void _update_pointer_position()
{
_view_stack.geometry(_pointer_origin, Rect(_user_state.pointer_pos(), Area()));
}
Main(Env &env) : _env(env)
{
_view_stack.default_background(_builtin_background);
_view_stack.stack(_pointer_origin);
_view_stack.geometry(_pointer_origin, Rect(_user_state.pointer_pos(), Area()));
_view_stack.stack(_builtin_background);
_update_pointer_position();
_config_rom.sigh(_config_handler);
_handle_config();
_framebuffer.sync_sigh(_input_handler);
_handle_fb_mode();
_framebuffer.mode_sigh(_fb_mode_handler);
_env.parent().announce(_env.ep().manage(_root));
_env.parent().announce(_env.ep().manage(_gui_root));
if (_config_rom.xml().has_sub_node("capture"))
_env.parent().announce(_env.ep().manage(_capture_root));
/*
* Detect initial motion activity such that the first hover report
@ -478,20 +607,20 @@ void Nitpicker::Main::_handle_input()
/* update pointer position */
if (result.motion_activity)
_view_stack.geometry(_pointer_origin, Rect(_user_state.pointer_pos(), Area()));
_update_pointer_position();
/* perform redraw */
{
if (_fb_screen.constructed()) {
/* call 'Dirty_rect::flush' on a copy to preserve the state */
Dirty_rect dirty_rect = _fb_screen->dirty_rect;
dirty_rect.flush([&] (Rect const &rect) {
_view_stack.draw(_fb_screen->screen, rect); });
}
/* flush pixels to the framebuffer, reset dirty_rect */
_fb_screen->dirty_rect.flush([&] (Rect const &rect) {
_framebuffer.refresh(rect.x1(), rect.y1(),
/* flush pixels to the framebuffer, reset dirty_rect */
_fb_screen->dirty_rect.flush([&] (Rect const &rect) {
_framebuffer.refresh(rect.x1(), rect.y1(),
rect.w(), rect.h()); });
}
/* deliver framebuffer synchronization events */
for (Gui_session *s = _session_list.first(); s; s = s->next())
@ -611,10 +740,12 @@ void Nitpicker::Main::_report_displays()
return;
Reporter::Xml_generator xml(_displays_reporter, [&] () {
xml.node("display", [&] () {
xml.attribute("width", _fb_screen->size.w());
xml.attribute("height", _fb_screen->size.h());
});
if (_fb_screen.constructed()) {
xml.node("display", [&] () {
xml.attribute("width", _fb_screen->size.w());
xml.attribute("height", _fb_screen->size.h());
});
}
});
}
@ -624,17 +755,7 @@ void Nitpicker::Main::_handle_fb_mode()
/* reconstruct framebuffer screen and menu bar */
_fb_screen.construct(_env.rm(), _framebuffer);
/* let the view stack use the new size */
_view_stack.size(_fb_screen->mode.area);
/* redraw */
_view_stack.update_all_views();
/* notify clients about the change screen mode */
for (Gui_session *s = _session_list.first(); s; s = s->next())
s->notify_mode_change();
_report_displays();
capture_buffer_size_changed();
}

View File

@ -40,6 +40,11 @@ namespace Nitpicker {
class Gui_session;
class View_stack;
static inline Area max_area(Area a1, Area a2)
{
return Area(max(a1.w(), a2.w()), max(a1.h(), a2.h()));
}
}
#endif /* _TYPES_H_ */

View File

@ -75,10 +75,16 @@ class Nitpicker::User_state
*/
View_stack &_view_stack;
/**
* True once the initial screen size becomes known and used as the
* initial (centered) pointer position.
*/
bool _initial_pointer_position_defined = false;
/*
* Current pointer position
*/
Point _pointer_pos;
Point _pointer_pos { };
/*
* Currently pointed-at view owner
@ -194,13 +200,30 @@ class Nitpicker::User_state
* \param focus exported focus information, to be consumed by the
* view stack to tailor its view drawing operations
*/
User_state(Focus &focus, Global_keys &global_keys, View_stack &view_stack,
Point initial_pointer_pos)
User_state(Focus &focus, Global_keys &global_keys, View_stack &view_stack)
:
_focus(focus), _global_keys(global_keys), _view_stack(view_stack),
_pointer_pos(initial_pointer_pos)
_focus(focus), _global_keys(global_keys), _view_stack(view_stack)
{ }
/**
* Called whenever the view-stack size has changed
*/
void sanitize_pointer_position()
{
Area const screen_size = _view_stack.size();
/* center pointer initially */
if (!_initial_pointer_position_defined) {
_pointer_pos = Point(screen_size.w()/2, screen_size.h()/2);
_initial_pointer_position_defined = true;
}
/* ensure that pointer remains within screen boundaries */
if (screen_size.count() > 0)
_pointer_pos = Point(min((int)screen_size.w() - 1, _pointer_pos.x()),
min((int)screen_size.h() - 1, _pointer_pos.y()));
}
/****************************************
** Interface used by the main program **

View File

@ -32,7 +32,7 @@ class Nitpicker::View_stack
private:
Area _size;
Area _size { };
Focus &_focus;
Font const &_font;
List<View_stack_elem> _views { };
@ -91,9 +91,9 @@ class Nitpicker::View_stack
/**
* Constructor
*/
View_stack(Area size, Focus &focus, Font const &font, Damage &damage)
View_stack(Focus &focus, Font const &font, Damage &damage)
:
_size(size), _focus(focus), _font(font), _damage(damage)
_focus(focus), _font(font), _damage(damage)
{ }
/**