From 22cb6dded709f891450f30ca43c7031dbb0f14cd Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Fri, 26 Jun 2020 22:10:17 +0200 Subject: [PATCH] nitpicker: add capture service Issue #3812 --- repos/os/recipes/src/nitpicker/used_apis | 1 + repos/os/src/server/nitpicker/canvas.h | 21 +- .../os/src/server/nitpicker/capture_session.h | 163 +++++++++++++ repos/os/src/server/nitpicker/gui_session.cc | 2 +- repos/os/src/server/nitpicker/gui_session.h | 4 - repos/os/src/server/nitpicker/main.cc | 229 +++++++++++++----- repos/os/src/server/nitpicker/types.h | 5 + repos/os/src/server/nitpicker/user_state.h | 33 ++- repos/os/src/server/nitpicker/view_stack.h | 6 +- 9 files changed, 392 insertions(+), 72 deletions(-) create mode 100644 repos/os/src/server/nitpicker/capture_session.h diff --git a/repos/os/recipes/src/nitpicker/used_apis b/repos/os/recipes/src/nitpicker/used_apis index 28a4992ba9..a589901020 100644 --- a/repos/os/recipes/src/nitpicker/used_apis +++ b/repos/os/recipes/src/nitpicker/used_apis @@ -6,4 +6,5 @@ input_session timer_session framebuffer_session gui_session +capture_session report_session diff --git a/repos/os/src/server/nitpicker/canvas.h b/repos/os/src/server/nitpicker/canvas.h index 13bba57d8e..b5225f26c6 100644 --- a/repos/os/src/server/nitpicker/canvas.h +++ b/repos/os/src/server/nitpicker/canvas.h @@ -60,11 +60,14 @@ class Nitpicker::Canvas : public Canvas_base, public Surface_base::Flusher { private: + Point const _offset; Surface _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 const &texture = static_cast 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); } diff --git a/repos/os/src/server/nitpicker/capture_session.h b/repos/os/src/server/nitpicker/capture_session.h new file mode 100644 index 0000000000..b0435a0f69 --- /dev/null +++ b/repos/os/src/server/nitpicker/capture_session.h @@ -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 +#include + +namespace Nitpicker { class Capture_session; } + + +class Nitpicker::Capture_session : public Session_object +{ + 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 _buffer { }; + + Signal_context_capability _screen_size_sigh { }; + + using Dirty_rect = Genode::Dirty_rect; + + 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 canvas = { _buffer->local_addr(), 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_ */ diff --git a/repos/os/src/server/nitpicker/gui_session.cc b/repos/os/src/server/nitpicker/gui_session.cc index 24e9722e26..76806ac606 100644 --- a/repos/os/src/server/nitpicker/gui_session.cc +++ b/repos/os/src/server/nitpicker/gui_session.cc @@ -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()) }; } diff --git a/repos/os/src/server/nitpicker/gui_session.h b/repos/os/src/server/nitpicker/gui_session.h index e96e39b62a..b6b48e15fa 100644 --- a/repos/os/src/server/nitpicker/gui_session.h +++ b/repos/os/src/server/nitpicker/gui_session.h @@ -93,8 +93,6 @@ class Nitpicker::Gui_session : public Session_object, 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, 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, _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), diff --git a/repos/os/src/server/nitpicker/main.cc b/repos/os/src/server/nitpicker/main.cc index 2ce3d1dd10..53f93fe2e9 100644 --- a/repos/os/src/server/nitpicker/main.cc +++ b/repos/os/src/server/nitpicker/main.cc @@ -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, 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, 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, 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, _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, }; -struct Nitpicker::Main : Focus_updater, View_stack::Damage +/******************************************* + ** Implementation of the capture service ** + *******************************************/ + +class Nitpicker::Capture_root : public Root_component +{ + private: + + using Sessions = Registry>; + + 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(_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(&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 screen = { fb_ds.local_addr(), mode.area }; + Canvas screen = { fb_ds.local_addr(), Point(0, 0), mode.area }; Area size = screen.size(); @@ -241,23 +326,7 @@ struct Nitpicker::Main : Focus_updater, View_stack::Damage } }; - Reconstructible _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 _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 _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(); } diff --git a/repos/os/src/server/nitpicker/types.h b/repos/os/src/server/nitpicker/types.h index f6c805c928..92735e04a7 100644 --- a/repos/os/src/server/nitpicker/types.h +++ b/repos/os/src/server/nitpicker/types.h @@ -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_ */ diff --git a/repos/os/src/server/nitpicker/user_state.h b/repos/os/src/server/nitpicker/user_state.h index 35d93a5c8f..ad99fe2dd4 100644 --- a/repos/os/src/server/nitpicker/user_state.h +++ b/repos/os/src/server/nitpicker/user_state.h @@ -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 ** diff --git a/repos/os/src/server/nitpicker/view_stack.h b/repos/os/src/server/nitpicker/view_stack.h index 7921cd2476..1c54e730e8 100644 --- a/repos/os/src/server/nitpicker/view_stack.h +++ b/repos/os/src/server/nitpicker/view_stack.h @@ -32,7 +32,7 @@ class Nitpicker::View_stack private: - Area _size; + Area _size { }; Focus &_focus; Font const &_font; List _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) { } /**