From 82e2900aa720ce01a9c337fb065b97579ac322d1 Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Thu, 16 Nov 2017 17:07:52 +0100 Subject: [PATCH] nitpicker: re-organize implementation This patch revises the implementation of nitpicker in the following respects: - Split the implementation into smaller files, - Consistently use the 'Nitpicker' namespace, - Avoid the use of format strings, - Retire old (and hackish) debug mode, - Removal of unused timer connection, - Merging 'Session' into 'Session_component', - Merging 'Mode' into 'User_state', - Adding the notions of 'View_owner' and 'Focus' as interfaces, - Untangle 'User_state' and 'View_stack' --- repos/os/src/server/nitpicker/background.h | 49 +- repos/os/src/server/nitpicker/buffer.h | 75 + repos/os/src/server/nitpicker/canvas.h | 18 +- .../os/src/server/nitpicker/chunky_texture.h | 77 + repos/os/src/server/nitpicker/clip_guard.h | 5 +- repos/os/src/server/nitpicker/color.h | 27 - .../os/src/server/nitpicker/domain_registry.h | 67 +- repos/os/src/server/nitpicker/draw_label.h | 95 +- repos/os/src/server/nitpicker/focus.h | 86 ++ .../server/nitpicker/framebuffer_session.h | 115 ++ repos/os/src/server/nitpicker/global_keys.cc | 15 +- repos/os/src/server/nitpicker/global_keys.h | 16 +- repos/os/src/server/nitpicker/input.h | 130 -- repos/os/src/server/nitpicker/input_session.h | 113 ++ repos/os/src/server/nitpicker/main.cc | 1314 +++-------------- repos/os/src/server/nitpicker/mode.h | 97 -- .../os/src/server/nitpicker/pointer_origin.h | 35 +- repos/os/src/server/nitpicker/session.h | 232 --- .../src/server/nitpicker/session_component.cc | 484 ++++++ .../src/server/nitpicker/session_component.h | 379 +++++ repos/os/src/server/nitpicker/target.mk | 7 +- repos/os/src/server/nitpicker/types.h | 42 + repos/os/src/server/nitpicker/user_state.cc | 327 ++-- repos/os/src/server/nitpicker/user_state.h | 145 +- repos/os/src/server/nitpicker/view.cc | 146 -- repos/os/src/server/nitpicker/view.h | 302 ---- .../os/src/server/nitpicker/view_component.cc | 190 +++ .../os/src/server/nitpicker/view_component.h | 283 ++++ repos/os/src/server/nitpicker/view_owner.h | 98 ++ repos/os/src/server/nitpicker/view_stack.cc | 102 +- repos/os/src/server/nitpicker/view_stack.h | 136 +- 31 files changed, 2739 insertions(+), 2468 deletions(-) create mode 100644 repos/os/src/server/nitpicker/buffer.h create mode 100644 repos/os/src/server/nitpicker/chunky_texture.h delete mode 100644 repos/os/src/server/nitpicker/color.h create mode 100644 repos/os/src/server/nitpicker/focus.h create mode 100644 repos/os/src/server/nitpicker/framebuffer_session.h delete mode 100644 repos/os/src/server/nitpicker/input.h create mode 100644 repos/os/src/server/nitpicker/input_session.h delete mode 100644 repos/os/src/server/nitpicker/mode.h delete mode 100644 repos/os/src/server/nitpicker/session.h create mode 100644 repos/os/src/server/nitpicker/session_component.cc create mode 100644 repos/os/src/server/nitpicker/session_component.h create mode 100644 repos/os/src/server/nitpicker/types.h delete mode 100644 repos/os/src/server/nitpicker/view.cc delete mode 100644 repos/os/src/server/nitpicker/view.h create mode 100644 repos/os/src/server/nitpicker/view_component.cc create mode 100644 repos/os/src/server/nitpicker/view_component.h create mode 100644 repos/os/src/server/nitpicker/view_owner.h diff --git a/repos/os/src/server/nitpicker/background.h b/repos/os/src/server/nitpicker/background.h index be17e758c5..14bba5e942 100644 --- a/repos/os/src/server/nitpicker/background.h +++ b/repos/os/src/server/nitpicker/background.h @@ -16,56 +16,45 @@ #include -#include "view.h" +#include "session_component.h" #include "clip_guard.h" -struct Background : private Texture_base, Session, View +namespace Nitpicker { struct Background; } + + +struct Nitpicker::Background : private Texture_base, View_component { - Color color; + static Color default_color() { return Color(25, 37, 50); } + + Color color = default_color(); /* * The background uses no texture. Therefore * we can pass a null pointer as texture argument * to the Session constructor. */ - Background(Area size) + Background(View_owner &owner, Area size) : - Texture_base(Area(0, 0)), Session(Genode::Session_label()), - View(*this, View::NOT_TRANSPARENT, View::BACKGROUND, 0), - color(25, 37, 50) + Texture_base(Area(0, 0)), + View_component(owner, View_component::NOT_TRANSPARENT, + View_component::BACKGROUND, 0) { - View::geometry(Rect(Point(0, 0), size)); + View_component::geometry(Rect(Point(0, 0), size)); } - /*********************** - ** Session interface ** - ***********************/ + /****************************** + ** View_component interface ** + ******************************/ - void submit_input_event(Input::Event) override { } - void submit_sync() override { } + int frame_size(Focus const &) const override { return 0; } + void frame(Canvas_base &canvas, Focus const &) const override { } - - /******************** - ** View interface ** - ********************/ - - 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 + void draw(Canvas_base &canvas, Focus const &) 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); } }; diff --git a/repos/os/src/server/nitpicker/buffer.h b/repos/os/src/server/nitpicker/buffer.h new file mode 100644 index 0000000000..3c8fb603ad --- /dev/null +++ b/repos/os/src/server/nitpicker/buffer.h @@ -0,0 +1,75 @@ +/* + * \brief Nitpicker buffer + * \author Norman Feske + * \date 2017-11-16 + */ + +/* + * Copyright (C) 2006-2017 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 _BUFFER_H_ +#define _BUFFER_H_ + +/* Genode includes */ +#include +#include + +/* local includes */ +#include "types.h" + + +namespace Nitpicker { class Buffer; } + + +class Nitpicker::Buffer +{ + private: + + Area _size; + Framebuffer::Mode::Format _format; + Attached_ram_dataspace _ram_ds; + + public: + + /** + * Constructor - allocate and map dataspace for virtual frame buffer + * + * \throw Out_of_ram + * \throw Out_of_caps + * \throw Region_map::Region_conflict + */ + Buffer(Ram_session &ram, Region_map &rm, + Area size, Framebuffer::Mode::Format format, size_t bytes) + : + _size(size), _format(format), _ram_ds(ram, rm, bytes) + { } + + /** + * Accessors + */ + Ram_dataspace_capability ds_cap() const { return _ram_ds.cap(); } + Area size() const { return _size; } + Framebuffer::Mode::Format format() const { return _format; } + void *local_addr() const { return _ram_ds.local_addr(); } +}; + + +namespace Nitpicker { struct Buffer_provider; } + + +/** + * Interface for triggering the re-allocation of a virtual framebuffer + * + * Used by 'Framebuffer::Session_component', + * implemented by 'Nitpicker::Session_component' + */ +struct Nitpicker::Buffer_provider +{ + virtual Buffer *realloc_buffer(Framebuffer::Mode mode, bool use_alpha) = 0; +}; + +#endif /* _BUFFER_H_ */ diff --git a/repos/os/src/server/nitpicker/canvas.h b/repos/os/src/server/nitpicker/canvas.h index 7c760151bd..ba7d580f8d 100644 --- a/repos/os/src/server/nitpicker/canvas.h +++ b/repos/os/src/server/nitpicker/canvas.h @@ -18,19 +18,19 @@ #include #include -typedef Genode::Surface_base::Area Area; -typedef Genode::Surface_base::Point Point; -typedef Genode::Surface_base::Rect Rect; -typedef Genode::Color Color; +/* local includes */ +#include "types.h" -using Genode::Texture_base; -using Genode::Texture; +namespace Nitpicker { + struct Canvas_base; + template class Canvas; +} /** * Pixel-type-independent interface of nitpicker's graphics backend */ -struct Canvas_base +struct Nitpicker::Canvas_base { virtual Area size() const = 0; @@ -54,11 +54,11 @@ struct Canvas_base * Pixel-type-specific implementation of nitpicker's graphics backend */ template -class Canvas : public Canvas_base, public Genode::Surface_base::Flusher +class Nitpicker::Canvas : public Canvas_base, public Surface_base::Flusher { private: - Genode::Surface _surface; + Surface _surface; public: diff --git a/repos/os/src/server/nitpicker/chunky_texture.h b/repos/os/src/server/nitpicker/chunky_texture.h new file mode 100644 index 0000000000..e085c0809f --- /dev/null +++ b/repos/os/src/server/nitpicker/chunky_texture.h @@ -0,0 +1,77 @@ +/* + * \brief Texture allocated as RAM dataspace + * \author Norman Feske + * \date 2017-11-16 + */ + +/* + * Copyright (C) 2006-2017 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 _CHUNKY_TEXTURE_H_ +#define _CHUNKY_TEXTURE_H_ + +/* local includes */ +#include "buffer.h" + +namespace Nitpicker { template class Chunky_texture; } + + +template +class Nitpicker::Chunky_texture : public Buffer, public Texture +{ + private: + + Framebuffer::Mode::Format _format() { + return Framebuffer::Mode::RGB565; } + + /** + * Return base address of alpha channel or 0 if no alpha channel exists + */ + unsigned char *_alpha_base(Area size, bool use_alpha) + { + if (!use_alpha) return 0; + + /* alpha values come right after the pixel values */ + return (unsigned char *)local_addr() + calc_num_bytes(size, false); + } + + public: + + /** + * Constructor + */ + Chunky_texture(Ram_session &ram, Region_map &rm, Area size, bool use_alpha) + : + Buffer(ram, rm, size, _format(), calc_num_bytes(size, use_alpha)), + Texture((PT *)local_addr(), + _alpha_base(size, use_alpha), size) { } + + static size_t calc_num_bytes(Area size, bool use_alpha) + { + /* + * If using an alpha channel, the alpha buffer follows the + * pixel buffer. The alpha buffer is followed by an input + * mask buffer. Hence, we have to account one byte per + * alpha value and one byte for the input mask value. + */ + size_t bytes_per_pixel = sizeof(PT) + (use_alpha ? 2 : 0); + return bytes_per_pixel*size.w()*size.h(); + } + + unsigned char *input_mask_buffer() + { + if (!Texture::alpha()) return 0; + + Area const size = Texture::size(); + + /* input-mask values come right after the alpha values */ + return (unsigned char *)local_addr() + calc_num_bytes(size, false) + + size.count(); + } +}; + +#endif /* _CHUNKY_TEXTURE_H_ */ diff --git a/repos/os/src/server/nitpicker/clip_guard.h b/repos/os/src/server/nitpicker/clip_guard.h index 9ba48fe9d0..27c4b541f6 100644 --- a/repos/os/src/server/nitpicker/clip_guard.h +++ b/repos/os/src/server/nitpicker/clip_guard.h @@ -30,7 +30,10 @@ #include "canvas.h" -class Clip_guard +namespace Nitpicker { class Clip_guard; } + + +class Nitpicker::Clip_guard { private: diff --git a/repos/os/src/server/nitpicker/color.h b/repos/os/src/server/nitpicker/color.h deleted file mode 100644 index f7f71bd7a6..0000000000 --- a/repos/os/src/server/nitpicker/color.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * \brief Color definitions used by nitpicker - * \date 2006-08-04 - * \author Norman Feske - */ - -/* - * Copyright (C) 2006-2017 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 _COLOR_H_ -#define _COLOR_H_ - -#include - -/* - * Symbolic names for some important colors - */ -static const Genode::Color BLACK(0, 0, 0); -static const Genode::Color WHITE(255, 255, 255); -static const Genode::Color FRAME_COLOR(255, 200, 127); -static const Genode::Color KILL_COLOR(255, 0, 0); - -#endif /* _COLOR_H_ */ diff --git a/repos/os/src/server/nitpicker/domain_registry.h b/repos/os/src/server/nitpicker/domain_registry.h index af595c6bfb..d750c4bb11 100644 --- a/repos/os/src/server/nitpicker/domain_registry.h +++ b/repos/os/src/server/nitpicker/domain_registry.h @@ -14,21 +14,20 @@ #ifndef _DOMAIN_REGISTRY_ #define _DOMAIN_REGISTRY_ -#include -#include -#include -#include +#include "types.h" -class Domain_registry +namespace Nitpicker { class Domain_registry; } + + +class Nitpicker::Domain_registry { public: - class Entry : public Genode::List::Element + class Entry : public List::Element { public: - typedef Genode::String<64> Name; - typedef Genode::Color Color; + typedef String<64> Name; enum Label { LABEL_NO, LABEL_YES }; enum Content { CONTENT_CLIENT, CONTENT_TINTED }; @@ -109,31 +108,31 @@ class Domain_registry { int const w = _area.x() > 0 ? _area.x() - : Genode::max(0, (int)phys_screen_area.w() + _area.x()); + : max(0, (int)phys_screen_area.w() + _area.x()); int const h = _area.y() > 0 ? _area.y() - : Genode::max(0, (int)phys_screen_area.h() + _area.y()); + : max(0, (int)phys_screen_area.h() + _area.y()); return Area(w, h); } }; - static Entry::Label _label(Genode::Xml_node domain) + static Entry::Label _label(Xml_node domain) { - typedef Genode::String<32> Value; + typedef String<32> Value; Value const value = domain.attribute_value("label", Value("yes")); if (value == "no") return Entry::LABEL_NO; if (value == "yes") return Entry::LABEL_YES; - Genode::warning("invalid value of label attribute in "); + warning("invalid value of label attribute in "); return Entry::LABEL_YES; } - static Entry::Content _content(Genode::Xml_node domain) + static Entry::Content _content(Xml_node domain) { - typedef Genode::String<32> Value; + typedef String<32> Value; Value const value = domain.attribute_value("content", Value("tinted")); if (value == "client") return Entry::CONTENT_CLIENT; @@ -142,34 +141,34 @@ class Domain_registry return Entry::CONTENT_TINTED; } - static Entry::Hover _hover(Genode::Xml_node domain) + static Entry::Hover _hover(Xml_node domain) { - typedef Genode::String<32> Value; + typedef String<32> Value; Value const value = domain.attribute_value("hover", Value("focused")); if (value == "focused") return Entry::HOVER_FOCUSED; if (value == "always") return Entry::HOVER_ALWAYS; - Genode::warning("invalid value of hover attribute in "); + warning("invalid value of hover attribute in "); return Entry::HOVER_FOCUSED; } - static Entry::Focus _focus(Genode::Xml_node domain) + static Entry::Focus _focus(Xml_node domain) { - typedef Genode::String<32> Value; + typedef String<32> Value; Value const value = domain.attribute_value("focus", Value("none")); if (value == "none") return Entry::FOCUS_NONE; if (value == "click") return Entry::FOCUS_CLICK; if (value == "transient") return Entry::FOCUS_TRANSIENT; - Genode::warning("invalid value of focus attribute in "); + warning("invalid value of focus attribute in "); return Entry::FOCUS_NONE; } - static Entry::Origin _origin(Genode::Xml_node domain) + static Entry::Origin _origin(Xml_node domain) { - typedef Genode::String<32> Value; + typedef String<32> Value; Value const value = domain.attribute_value("origin", Value("top_left")); if (value == "top_left") return Entry::ORIGIN_TOP_LEFT; @@ -178,11 +177,11 @@ class Domain_registry if (value == "bottom_right") return Entry::ORIGIN_BOTTOM_RIGHT; if (value == "pointer") return Entry::ORIGIN_POINTER; - Genode::warning("invalid value of origin attribute in "); + warning("invalid value of origin attribute in "); return Entry::ORIGIN_BOTTOM_LEFT; } - void _insert(Genode::Xml_node domain) + void _insert(Xml_node domain) { char buf[sizeof(Entry::Name)]; buf[0] = 0; @@ -193,19 +192,19 @@ class Domain_registry } catch (...) { } if (!name_defined) { - Genode::error("no valid domain name specified"); + error("no valid domain name specified"); return; } Entry::Name const name(buf); if (lookup(name)) { - Genode::error("domain name \"", name, "\" is not unique"); + error("domain name \"", name, "\" is not unique"); return; } if (!domain.has_attribute("layer")) { - Genode::error("no layer specified for domain \"", name, "\""); + error("no layer specified for domain \"", name, "\""); return; } @@ -217,7 +216,7 @@ class Domain_registry Point const area(domain.attribute_value("width", 0L), domain.attribute_value("height", 0L)); - Entry::Color const color = domain.attribute_value("color", WHITE); + Color const color = domain.attribute_value("color", white()); _entries.insert(new (_alloc) Entry(name, color, _label(domain), _content(domain), _hover(domain), @@ -227,12 +226,12 @@ class Domain_registry private: - Genode::List _entries; - Genode::Allocator &_alloc; + List _entries; + Allocator &_alloc; public: - Domain_registry(Genode::Allocator &alloc, Genode::Xml_node config) + Domain_registry(Allocator &alloc, Xml_node config) : _alloc(alloc) { @@ -241,7 +240,7 @@ class Domain_registry if (!config.has_sub_node(type)) return; - Genode::Xml_node domain = config.sub_node(type); + Xml_node domain = config.sub_node(type); for (;; domain = domain.next(type)) { @@ -256,7 +255,7 @@ class Domain_registry { while (Entry *e = _entries.first()) { _entries.remove(e); - Genode::destroy(_alloc, e); + destroy(_alloc, e); } } diff --git a/repos/os/src/server/nitpicker/draw_label.h b/repos/os/src/server/nitpicker/draw_label.h index 8b79272565..a2f23a3888 100644 --- a/repos/os/src/server/nitpicker/draw_label.h +++ b/repos/os/src/server/nitpicker/draw_label.h @@ -15,58 +15,63 @@ #define _DRAW_LABEL_H_ #include "canvas.h" - -extern Text_painter::Font default_font; - -/* - * Gap between session label and view title in pixels - */ -enum { LABEL_GAP = 5 }; - -/** - * Draw black outline of string - */ -inline void draw_string_outline(Canvas_base &canvas, Point pos, char const *s) -{ - for (int j = -1; j <= 1; j++) - for (int i = -1; i <= 1; i++) - if (i || j) - canvas.draw_text(pos + Point(i, j), default_font, BLACK, s); -} +#include "types.h" -/** - * Return bounding box of composed label displayed with the default font - * - * \param sl session label string - * \param vt view title string - */ -inline Area label_size(const char *sl, const char *vt) { - return Area(default_font.str_w(sl) + LABEL_GAP + default_font.str_w(vt) + 2, - default_font.str_h(sl) + 2); } +namespace Nitpicker { + + extern Text_painter::Font default_font; + + /* + * Gap between session label and view title in pixels + */ + enum { LABEL_GAP = 5 }; + + /** + * Draw black outline of string + */ + inline void draw_string_outline(Canvas_base &canvas, Point pos, char const *s) + { + for (int j = -1; j <= 1; j++) + for (int i = -1; i <= 1; i++) + if (i || j) + canvas.draw_text(pos + Point(i, j), default_font, black(), s); + } -/** - * Draw outlined view label - * - * View labels are composed of two parts: the session label and the view title. - * The unforgeable session label is defined on session creation by system - * policy. In contrast, the view title can individually be defined by the - * application. - */ -static inline void draw_label(Canvas_base &canvas, Point pos, - char const *session_label, Color session_label_color, - char const *view_title, Color view_title_color) -{ - pos = pos + Point(1, 1); + /** + * Return bounding box of composed label displayed with the default font + * + * \param sl session label string + * \param vt view title string + */ + inline Area label_size(const char *sl, const char *vt) { + return Area(default_font.str_w(sl) + LABEL_GAP + default_font.str_w(vt) + 2, + default_font.str_h(sl) + 2); } - draw_string_outline(canvas, pos, session_label); - canvas.draw_text(pos, default_font, session_label_color, session_label); - pos = pos + Point(default_font.str_w(session_label) + LABEL_GAP, 0); + /** + * Draw outlined view label + * + * View labels are composed of two parts: the session label and the view title. + * The unforgeable session label is defined on session creation by system + * policy. In contrast, the view title can individually be defined by the + * application. + */ + static inline void draw_label(Canvas_base &canvas, Point pos, + char const *session_label, Color session_label_color, + char const *view_title, Color view_title_color) + { + pos = pos + Point(1, 1); - draw_string_outline(canvas, pos, view_title); - canvas.draw_text(pos, default_font, view_title_color, view_title); + draw_string_outline(canvas, pos, session_label); + canvas.draw_text(pos, default_font, session_label_color, session_label); + + pos = pos + Point(default_font.str_w(session_label) + LABEL_GAP, 0); + + draw_string_outline(canvas, pos, view_title); + canvas.draw_text(pos, default_font, view_title_color, view_title); + } } #endif /* _DRAW_LABEL_H_ */ diff --git a/repos/os/src/server/nitpicker/focus.h b/repos/os/src/server/nitpicker/focus.h new file mode 100644 index 0000000000..d0e2a7ee96 --- /dev/null +++ b/repos/os/src/server/nitpicker/focus.h @@ -0,0 +1,86 @@ +/* + * \brief Interfaces for requesting and controlling the focus + * \author Norman Feske + * \date 2017-11-17 + */ + +/* + * Copyright (C) 2017 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 _FOCUS_H_ +#define _FOCUS_H_ + +#include "types.h" +#include "view_component.h" + +namespace Nitpicker { + struct Focus; + struct Focus_controller; +} + + +/** + * Interface used by the view stack + */ +class Nitpicker::Focus : Noncopyable +{ + private: + + View_owner const *_focused = nullptr; + + public: + + /** + * Return true if specified view owner has the input focus + */ + bool focused(View_owner const &owner) const + { + return &owner == _focused; + } + + /** + * Return true if the specified view owner belongs to the same domain as + * the currently focused view owner + */ + bool same_domain_as_focused(View_owner const &owner) const + { + return owner.has_same_domain(_focused); + } + + /** + * Return true if the specified view is the background view as defined for + * the currentlu focused view owner. + */ + bool focused_background(View_component const &view) const + { + return _focused && (_focused->background() == &view); + } + + /** + * Set the input focus to the specified view owner + */ + void assign(View_owner const &focused) { _focused = &focused; } + + void forget(View_owner const &owner) + { + if (_focused == &owner) + _focused = nullptr; + } +}; + + +/** + * Interface used by a nitpicker client to assign the focus to a session of + * one of its child components (according to the session labels) + */ +struct Nitpicker::Focus_controller +{ + virtual void focus_view_owner(View_owner const &caller, + View_owner &next_focused) = 0; +}; + +#endif /* _FOCUS_H_ */ diff --git a/repos/os/src/server/nitpicker/framebuffer_session.h b/repos/os/src/server/nitpicker/framebuffer_session.h new file mode 100644 index 0000000000..3810a3a28c --- /dev/null +++ b/repos/os/src/server/nitpicker/framebuffer_session.h @@ -0,0 +1,115 @@ +/* + * \brief Framebuffer sub session as part of the nitpicker session + * \author Norman Feske + * \date 2017-11-16 + */ + +/* + * Copyright (C) 2006-2017 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 _FRAMEBUFFER_SESSION_COMPONENT_H_ +#define _FRAMEBUFFER_SESSION_COMPONENT_H_ + +/* local includes */ +#include "buffer.h" + +namespace Framebuffer { + + using namespace Nitpicker; + using Nitpicker::Rect; + using Nitpicker::Point; + using Nitpicker::Area; + + class Session_component; +} + + +class Framebuffer::Session_component : public Rpc_object +{ + private: + + Buffer *_buffer = 0; + View_stack &_view_stack; + Nitpicker::Session_component &_session; + Framebuffer::Session &_framebuffer; + Buffer_provider &_buffer_provider; + Signal_context_capability _mode_sigh; + Signal_context_capability _sync_sigh; + Framebuffer::Mode _mode; + bool _alpha = false; + + public: + + /** + * Constructor + */ + Session_component(View_stack &view_stack, + Nitpicker::Session_component &session, + Framebuffer::Session &framebuffer, + Buffer_provider &buffer_provider) + : + _view_stack(view_stack), + _session(session), + _framebuffer(framebuffer), + _buffer_provider(buffer_provider) + { } + + /** + * Change virtual framebuffer mode + * + * Called by Nitpicker::Session_component when re-dimensioning the + * buffer. + * + * The new mode does not immediately become active. The client can + * keep using an already obtained framebuffer dataspace. However, + * we inform the client about the mode change via a signal. If the + * client calls 'dataspace' the next time, the new mode becomes + * effective. + */ + void notify_mode_change(Framebuffer::Mode mode, bool alpha) + { + _mode = mode; + _alpha = alpha; + + if (_mode_sigh.valid()) + Signal_transmitter(_mode_sigh).submit(); + } + + void submit_sync() + { + if (_sync_sigh.valid()) + Signal_transmitter(_sync_sigh).submit(); + } + + + /************************************ + ** Framebuffer::Session interface ** + ************************************/ + + Dataspace_capability dataspace() override + { + _buffer = _buffer_provider.realloc_buffer(_mode, _alpha); + + return _buffer ? _buffer->ds_cap() : Ram_dataspace_capability(); + } + + Mode mode() const override { return _mode; } + + void mode_sigh(Signal_context_capability sigh) override + { + _mode_sigh = sigh; + } + + void sync_sigh(Signal_context_capability sigh) override + { + _sync_sigh = sigh; + } + + void refresh(int x, int y, int w, int h) override; +}; + +#endif /* _FRAMEBUFFER_SESSION_COMPONENT_H_ */ diff --git a/repos/os/src/server/nitpicker/global_keys.cc b/repos/os/src/server/nitpicker/global_keys.cc index 3c38f79636..8f0b864964 100644 --- a/repos/os/src/server/nitpicker/global_keys.cc +++ b/repos/os/src/server/nitpicker/global_keys.cc @@ -13,12 +13,15 @@ /* local includes */ #include "global_keys.h" +#include "session_component.h" + +using namespace Nitpicker; Global_keys::Policy *Global_keys::_lookup_policy(char const *key_name) { for (unsigned i = 0; i < NUM_POLICIES; i++) - if (Genode::strcmp(key_name, Input::key_name((Input::Keycode)i)) == 0) + if (strcmp(key_name, Input::key_name((Input::Keycode)i)) == 0) return &_policies[i]; return 0; @@ -38,15 +41,15 @@ void Global_keys::apply_config(Xml_node config, Session_list &session_list) for (; ; node = node.next(node_type)) { if (!node.has_attribute("name")) { - Genode::warning("attribute 'name' missing in config node"); + warning("attribute 'name' missing in config node"); continue; } - typedef Genode::String<32> Name; + typedef String<32> Name; Name name = node.attribute_value("name", Name()); Policy * policy = _lookup_policy(name.string()); if (!policy) { - Genode::warning("invalid key name \"", name, "\""); + warning("invalid key name \"", name, "\""); continue; } @@ -55,12 +58,12 @@ void Global_keys::apply_config(Xml_node config, Session_list &session_list) continue; if (!node.has_attribute("label")) { - Genode::warning("missing 'label' attribute for key ", name); + warning("missing 'label' attribute for key ", name); continue; } /* assign policy to matching client session */ - for (Session *s = session_list.first(); s; s = s->next()) + for (Session_component *s = session_list.first(); s; s = s->next()) if (node.attribute("label").has_value(s->label().string())) policy->client(s); } diff --git a/repos/os/src/server/nitpicker/global_keys.h b/repos/os/src/server/nitpicker/global_keys.h index 6f3adf0559..7eafa53784 100644 --- a/repos/os/src/server/nitpicker/global_keys.h +++ b/repos/os/src/server/nitpicker/global_keys.h @@ -16,24 +16,24 @@ /* Genode includes */ #include -#include /* local includes */ -#include "session.h" +#include "session_component.h" -class Global_keys +namespace Nitpicker { class Global_keys; } + + +class Nitpicker::Global_keys { private: - typedef Genode::Xml_node Xml_node; - struct Policy { - Session *_session = nullptr; + Session_component *_session = nullptr; bool defined() const { return _session != nullptr; } - void client(Session *s) { _session = s; } + void client(Session_component *s) { _session = s; } }; enum { NUM_POLICIES = Input::KEY_MAX + 1 }; @@ -50,7 +50,7 @@ class Global_keys public: - Session *global_receiver(Input::Keycode key) { + Session_component *global_receiver(Input::Keycode key) { return _valid(key) ? _policies[key]._session : 0; } void apply_config(Xml_node config, Session_list &session_list); diff --git a/repos/os/src/server/nitpicker/input.h b/repos/os/src/server/nitpicker/input.h deleted file mode 100644 index 0d9f7d2a97..0000000000 --- a/repos/os/src/server/nitpicker/input.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * \brief Input handling utilities - * \author Norman Feske - * \date 2013-09-07 - */ - -/* - * Copyright (C) 2013-2017 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 _INPUT_H_ -#define _INPUT_H_ - -/* Genode includes */ -#include - -/* local includes */ -#include "user_state.h" - - -/** - * Determine number of events that can be merged into one - * - * \param ev pointer to first event array element to check - * \param max size of the event array - * \return number of events subjected to merge - */ -static unsigned num_consecutive_events(Input::Event const *ev, unsigned max) -{ - if (max < 1) return 0; - if (ev->type() != Input::Event::MOTION) return 1; - - bool const first_absolute = ev->absolute_motion(); - - /* iterate until we get a different event type, start at second */ - unsigned cnt = 1; - for (ev++ ; cnt < max; cnt++, ev++) { - if (ev->type() != Input::Event::MOTION) break; - if (first_absolute != ev->absolute_motion()) break; - } - return cnt; -} - - -/** - * Merge consecutive motion events - * - * \param ev event array to merge - * \param n number of events to merge - * \return merged motion event - */ -static Input::Event merge_motion_events(Input::Event const *ev, unsigned n) -{ - Input::Event res; - for (unsigned i = 0; i < n; i++, ev++) - res = Input::Event(Input::Event::MOTION, 0, ev->ax(), ev->ay(), - res.rx() + ev->rx(), res.ry() + ev->ry()); - return res; -} - - -/** - * Feed input event to the user state - * - * \return true if user has been active. A user is active as long as at - * least one key/button is pressed (during drag operations) - * and when a key/button changes it state. - */ -static bool import_input_events(Input::Event *ev_buf, unsigned num_ev, - User_state &user_state) -{ - bool user_active = false; - - if (num_ev > 0) { - /* - * Take events from input event buffer, merge consecutive motion - * events, and pass result to the user state. - */ - for (unsigned src_ev_cnt = 0; src_ev_cnt < num_ev; src_ev_cnt++) { - - Input::Event *e = &ev_buf[src_ev_cnt]; - Input::Event curr = *e; - - if (e->type() == Input::Event::MOTION) { - unsigned n = num_consecutive_events(e, num_ev - src_ev_cnt); - curr = merge_motion_events(e, n); - - /* skip merged events */ - src_ev_cnt += n - 1; - } - - /* - * If subsequential relative motion events are merged to - * a zero-motion event, drop it. Otherwise, it would be - * misinterpreted as absolute event pointing to (0, 0). - */ - if (e->relative_motion() && curr.rx() == 0 && curr.ry() == 0) - continue; - - /* - * If we detect a pressed key sometime during the event processing, - * we regard the user as active. This check captures the presence - * of press-release combinations within one batch of input events. - */ - user_active |= user_state.key_pressed(); - - /* pass event to user state */ - user_state.handle_event(curr); - } - } else { - /* - * Besides handling input events, 'user_state.handle_event()' also - * updates the pointed session, which might have changed by other - * means, for example view movement. - */ - user_state.handle_event(Input::Event()); - } - - /* - * If at least one key is kept pressed, we regard the user as active. - */ - user_active |= user_state.key_pressed(); - - return user_active; -} - -#endif /* _INPUT_H_ */ diff --git a/repos/os/src/server/nitpicker/input_session.h b/repos/os/src/server/nitpicker/input_session.h new file mode 100644 index 0000000000..630f14570a --- /dev/null +++ b/repos/os/src/server/nitpicker/input_session.h @@ -0,0 +1,113 @@ +/* + * \brief Input sub session as part of the nitpicker session + * \author Norman Feske + * \date 2017-11-16 + */ + +/* + * Copyright (C) 2006-2017 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 _INPUT_SESSION_COMPONENT_H_ +#define _INPUT_SESSION_COMPONENT_H_ + +/* Genode includes */ +#include +#include +#include + +/* local incudes */ +#include "types.h" + +namespace Input { + using namespace Nitpicker; + class Session_component; +} + + +class Input::Session_component : public Rpc_object +{ + public: + + enum { MAX_EVENTS = 200 }; + + static size_t ev_ds_size() { + return align_addr(MAX_EVENTS*sizeof(Event), 12); } + + private: + + /* + * Exported event buffer dataspace + */ + Attached_ram_dataspace _ev_ram_ds; + + /* + * Local event buffer that is copied + * to the exported event buffer when + * flush() gets called. + */ + Event _ev_buf[MAX_EVENTS]; + unsigned _num_ev = 0; + + Signal_context_capability _sigh; + + public: + + Session_component(Env &env) + : + _ev_ram_ds(env.ram(), env.rm(), ev_ds_size()) + { } + + /** + * Wake up client + */ + void submit_signal() + { + if (_sigh.valid()) + Signal_transmitter(_sigh).submit(); + } + + /** + * Enqueue event into local event buffer of the input session + */ + void submit(const Event *ev) + { + /* drop event when event buffer is full */ + if (_num_ev >= MAX_EVENTS) return; + + /* insert event into local event buffer */ + _ev_buf[_num_ev++] = *ev; + + submit_signal(); + } + + + /***************************** + ** Input session interface ** + *****************************/ + + Dataspace_capability dataspace() override { return _ev_ram_ds.cap(); } + + bool pending() const override { return _num_ev > 0; } + + int flush() override + { + unsigned ev_cnt; + + /* copy events from local event buffer to exported buffer */ + Event *ev_ds_buf = _ev_ram_ds.local_addr(); + for (ev_cnt = 0; ev_cnt < _num_ev; ev_cnt++) + ev_ds_buf[ev_cnt] = _ev_buf[ev_cnt]; + + _num_ev = 0; + return ev_cnt; + } + + void sigh(Signal_context_capability sigh) override { _sigh = sigh; } +}; + +#endif /* _INPUT_SESSION_COMPONENT_H_ */ + diff --git a/repos/os/src/server/nitpicker/main.cc b/repos/os/src/server/nitpicker/main.cc index e2cf65d0b0..7ca6be9de7 100644 --- a/repos/os/src/server/nitpicker/main.cc +++ b/repos/os/src/server/nitpicker/main.cc @@ -15,1010 +15,59 @@ #include #include #include -#include #include -#include #include -#include #include #include -#include -#include #include -#include -#include #include -#include -#include #include -#include /* local includes */ -#include "input.h" +#include "types.h" +#include "user_state.h" #include "background.h" #include "clip_guard.h" #include "pointer_origin.h" #include "domain_registry.h" -namespace Input { class Session_component; } -namespace Framebuffer { class Session_component; } - namespace Nitpicker { - class Session_component; template class Root; struct Main; } -using Genode::size_t; -using Genode::Allocator; -using Genode::Entrypoint; -using Genode::List; -using Genode::Pixel_rgb565; -using Genode::strcmp; -using Genode::Env; -using Genode::Arg_string; -using Genode::Object_pool; -using Genode::Dataspace_capability; -using Genode::Session_label; -using Genode::Signal_transmitter; -using Genode::Signal_context_capability; -using Genode::Signal_handler; -using Genode::Attached_ram_dataspace; -using Genode::Attached_rom_dataspace; -using Genode::Attached_dataspace; -using Genode::Weak_ptr; -using Genode::Locked_ptr; +/************************* + ** Font initialization ** + *************************/ -Framebuffer::Session *tmp_fb; +extern char _binary_default_tff_start; +namespace Nitpicker { -/*************** - ** Utilities ** - ***************/ - -static void report_session(Genode::Reporter &reporter, Session *session, - bool active = false) -{ - if (!reporter.enabled()) - return; - - Genode::Reporter::Xml_generator xml(reporter, [&] () - { - if (session) { - xml.attribute("label", session->label().string()); - xml.attribute("domain", session->domain_name().string()); - - Color const color = session->color(); - char buf[32]; - Genode::snprintf(buf, sizeof(buf), "#%02x%02x%02x", - color.r, color.g, color.b); - xml.attribute("color", buf); - - if (active) xml.attribute("active", "yes"); - } - }); + Text_painter::Font default_font(&_binary_default_tff_start); } -/* - * Font initialization - */ -extern char _binary_default_tff_start; +/************************************ + ** Framebuffer::Session_component ** + ************************************/ -Text_painter::Font default_font(&_binary_default_tff_start); - - -template -struct Screen : public Canvas +void Framebuffer::Session_component::refresh(int x, int y, int w, int h) { - Screen(PT *base, Area size) : Canvas(base, size) { } -}; + Rect const rect(Point(x, y), Area(w, h)); - -class Buffer -{ - private: - - Area _size; - Framebuffer::Mode::Format _format; - Genode::Attached_ram_dataspace _ram_ds; - - public: - - /** - * Constructor - allocate and map dataspace for virtual frame buffer - * - * \throw Out_of_ram - * \throw Out_of_caps - * \throw Region_map::Region_conflict - */ - Buffer(Genode::Ram_session &ram, Genode::Region_map &rm, - Area size, Framebuffer::Mode::Format format, Genode::size_t bytes) - : - _size(size), _format(format), _ram_ds(ram, rm, bytes) - { } - - /** - * Accessors - */ - Genode::Ram_dataspace_capability ds_cap() const { return _ram_ds.cap(); } - Area size() const { return _size; } - Framebuffer::Mode::Format format() const { return _format; } - void *local_addr() const { return _ram_ds.local_addr(); } -}; - - -/** - * Interface for triggering the re-allocation of a virtual framebuffer - * - * Used by 'Framebuffer::Session_component', - * implemented by 'Nitpicker::Session_component' - */ -struct Buffer_provider -{ - virtual Buffer *realloc_buffer(Framebuffer::Mode mode, bool use_alpha) = 0; -}; - - -template -class Chunky_dataspace_texture : public Buffer, - public Texture -{ - private: - - Framebuffer::Mode::Format _format() { - return Framebuffer::Mode::RGB565; } - - /** - * Return base address of alpha channel or 0 if no alpha channel exists - */ - unsigned char *_alpha_base(Area size, bool use_alpha) - { - if (!use_alpha) return 0; - - /* alpha values come right after the pixel values */ - return (unsigned char *)local_addr() + calc_num_bytes(size, false); - } - - public: - - /** - * Constructor - */ - Chunky_dataspace_texture(Genode::Ram_session &ram, Genode::Region_map &rm, - Area size, bool use_alpha) - : - Buffer(ram, rm, size, _format(), calc_num_bytes(size, use_alpha)), - Texture((PT *)local_addr(), - _alpha_base(size, use_alpha), size) { } - - static Genode::size_t calc_num_bytes(Area size, bool use_alpha) - { - /* - * If using an alpha channel, the alpha buffer follows the - * pixel buffer. The alpha buffer is followed by an input - * mask buffer. Hence, we have to account one byte per - * alpha value and one byte for the input mask value. - */ - Genode::size_t bytes_per_pixel = sizeof(PT) + (use_alpha ? 2 : 0); - return bytes_per_pixel*size.w()*size.h(); - } - - unsigned char *input_mask_buffer() - { - if (!Texture::alpha()) return 0; - - Area const size = Texture::size(); - - /* input-mask values come right after the alpha values */ - return (unsigned char *)local_addr() + calc_num_bytes(size, false) - + size.count(); - } -}; - - -/*********************** - ** Input sub session ** - ***********************/ - -class Input::Session_component : public Genode::Rpc_object -{ - public: - - enum { MAX_EVENTS = 200 }; - - static size_t ev_ds_size() { - return Genode::align_addr(MAX_EVENTS*sizeof(Event), 12); } - - private: - - /* - * Exported event buffer dataspace - */ - Attached_ram_dataspace _ev_ram_ds; - - /* - * Local event buffer that is copied - * to the exported event buffer when - * flush() gets called. - */ - Event _ev_buf[MAX_EVENTS]; - unsigned _num_ev = 0; - - Signal_context_capability _sigh; - - public: - - Session_component(Genode::Env &env) - : - _ev_ram_ds(env.ram(), env.rm(), ev_ds_size()) - { } - - /** - * Wake up client - */ - void submit_signal() - { - if (_sigh.valid()) - Signal_transmitter(_sigh).submit(); - } - - /** - * Enqueue event into local event buffer of the input session - */ - void submit(const Event *ev) - { - /* drop event when event buffer is full */ - if (_num_ev >= MAX_EVENTS) return; - - /* insert event into local event buffer */ - _ev_buf[_num_ev++] = *ev; - - submit_signal(); - } - - - /***************************** - ** Input session interface ** - *****************************/ - - Dataspace_capability dataspace() override { return _ev_ram_ds.cap(); } - - bool pending() const override { return _num_ev > 0; } - - int flush() override - { - unsigned ev_cnt; - - /* copy events from local event buffer to exported buffer */ - Event *ev_ds_buf = _ev_ram_ds.local_addr(); - for (ev_cnt = 0; ev_cnt < _num_ev; ev_cnt++) - ev_ds_buf[ev_cnt] = _ev_buf[ev_cnt]; - - _num_ev = 0; - return ev_cnt; - } - - void sigh(Genode::Signal_context_capability sigh) override { _sigh = sigh; } -}; - - -/***************************** - ** Framebuffer sub session ** - *****************************/ - -class Framebuffer::Session_component : public Genode::Rpc_object -{ - private: - - ::Buffer *_buffer = 0; - View_stack &_view_stack; - ::Session &_session; - Framebuffer::Session &_framebuffer; - Buffer_provider &_buffer_provider; - Signal_context_capability _mode_sigh; - Signal_context_capability _sync_sigh; - Framebuffer::Mode _mode; - bool _alpha = false; - - public: - - /** - * Constructor - */ - Session_component(View_stack &view_stack, - ::Session &session, - Framebuffer::Session &framebuffer, - Buffer_provider &buffer_provider) - : - _view_stack(view_stack), - _session(session), - _framebuffer(framebuffer), - _buffer_provider(buffer_provider) - { } - - /** - * Change virtual framebuffer mode - * - * Called by Nitpicker::Session_component when re-dimensioning the - * buffer. - * - * The new mode does not immediately become active. The client can - * keep using an already obtained framebuffer dataspace. However, - * we inform the client about the mode change via a signal. If the - * client calls 'dataspace' the next time, the new mode becomes - * effective. - */ - void notify_mode_change(Framebuffer::Mode mode, bool alpha) - { - _mode = mode; - _alpha = alpha; - - if (_mode_sigh.valid()) - Signal_transmitter(_mode_sigh).submit(); - } - - void submit_sync() - { - if (_sync_sigh.valid()) - Signal_transmitter(_sync_sigh).submit(); - } - - - /************************************ - ** Framebuffer::Session interface ** - ************************************/ - - Dataspace_capability dataspace() override - { - _buffer = _buffer_provider.realloc_buffer(_mode, _alpha); - - return _buffer ? _buffer->ds_cap() : Genode::Ram_dataspace_capability(); - } - - Mode mode() const override { return _mode; } - - void mode_sigh(Signal_context_capability sigh) override - { - _mode_sigh = sigh; - } - - void sync_sigh(Signal_context_capability sigh) override - { - _sync_sigh = sigh; - } - - void refresh(int x, int y, int w, int h) override - { - Rect const rect(Point(x, y), Area(w, h)); - - _view_stack.mark_session_views_as_dirty(_session, rect); - } -}; + _view_stack.mark_session_views_as_dirty(_session, rect); +} /***************************************** ** Implementation of Nitpicker service ** *****************************************/ -class Nitpicker::Session_component : public Genode::Rpc_object, - public ::Session, - public Buffer_provider -{ - private: - - typedef ::View View; - - Env &_env; - - Genode::Allocator_guard _session_alloc; - - Framebuffer::Session &_framebuffer; - - /* Framebuffer_session_component */ - Framebuffer::Session_component _framebuffer_session_component; - - /* Input_session_component */ - Input::Session_component _input_session_component { _env }; - - View_stack &_view_stack; - - Mode &_mode; - - Signal_context_capability _mode_sigh; - - View &_pointer_origin; - - View &_builtin_background; - - List _view_list; - - Genode::Tslab _view_alloc { &_session_alloc }; - - /* capabilities for sub sessions */ - Framebuffer::Session_capability _framebuffer_session_cap; - Input::Session_capability _input_session_cap; - - bool const _provides_default_bg; - - /* size of currently allocated virtual framebuffer, in bytes */ - size_t _buffer_size = 0; - - Attached_ram_dataspace _command_ds { _env.ram(), _env.rm(), - sizeof(Command_buffer) }; - - Command_buffer &_command_buffer = *_command_ds.local_addr(); - - typedef Genode::Handle_registry View_handle_registry; - - View_handle_registry _view_handle_registry; - - Genode::Reporter &_focus_reporter; - - void _release_buffer() - { - if (!::Session::texture()) - return; - - typedef Pixel_rgb565 PT; - - /* retrieve pointer to texture from session */ - Chunky_dataspace_texture const *cdt = - static_cast const *>(::Session::texture()); - - ::Session::texture(0, false); - ::Session::input_mask(0); - - destroy(&_session_alloc, const_cast *>(cdt)); - - _session_alloc.upgrade(_buffer_size); - _buffer_size = 0; - } - - /** - * Helper for performing sanity checks in OP_TO_FRONT and OP_TO_BACK - * - * We have to check for the equality of both the specified view and - * neighbor. If both arguments refer to the same view, the creation of - * locked pointers for both views would result in a deadlock. - */ - bool _views_are_equal(View_handle v1, View_handle v2) - { - if (!v1.valid() || !v2.valid()) - return false; - - Weak_ptr v1_ptr = _view_handle_registry.lookup(v1); - Weak_ptr v2_ptr = _view_handle_registry.lookup(v2); - - return v1_ptr == v2_ptr; - } - - bool _focus_change_permitted() const - { - ::Session * const focused_session = _mode.focused_session(); - - /* - * If no session is focused, we allow any client to assign it. This - * is useful for programs such as an initial login window that - * should receive input events without prior manual selection via - * the mouse. - * - * In principle, a client could steal the focus during time between - * a currently focused session gets closed and before the user - * manually picks a new session. However, in practice, the focus - * policy during application startup and exit is managed by a - * window manager that sits between nitpicker and the application. - */ - if (!focused_session) - return true; - - /* - * Check if the currently focused session label belongs to a - * session subordinated to the caller, i.e., it originated from - * a child of the caller or from the same process. This is the - * case if the first part of the focused session label is - * identical to the caller's label. - */ - char const * const focused_label = focused_session->label().string(); - char const * const caller_label = label().string(); - - return strcmp(focused_label, caller_label, Genode::strlen(caller_label)) == 0; - } - - void _execute_command(Command const &command) - { - switch (command.opcode) { - - case Command::OP_GEOMETRY: - { - Command::Geometry const &cmd = command.geometry; - Locked_ptr view(_view_handle_registry.lookup(cmd.view)); - if (!view.valid()) - return; - - Point pos = cmd.rect.p1(); - - /* transpose position of top-level views by vertical session offset */ - if (view->top_level()) - pos = ::Session::phys_pos(pos, _view_stack.size()); - - if (view.valid()) - _view_stack.geometry(*view, Rect(pos, cmd.rect.area())); - - return; - } - - case Command::OP_OFFSET: - { - Command::Offset const &cmd = command.offset; - Locked_ptr view(_view_handle_registry.lookup(cmd.view)); - - if (view.valid()) - _view_stack.buffer_offset(*view, cmd.offset); - - return; - } - - case Command::OP_TO_FRONT: - { - Command::To_front const &cmd = command.to_front; - if (_views_are_equal(cmd.view, cmd.neighbor)) - return; - - Locked_ptr view(_view_handle_registry.lookup(cmd.view)); - if (!view.valid()) - return; - - /* bring to front if no neighbor is specified */ - if (!cmd.neighbor.valid()) { - _view_stack.stack(*view, nullptr, true); - return; - } - - /* stack view relative to neighbor */ - Locked_ptr neighbor(_view_handle_registry.lookup(cmd.neighbor)); - if (neighbor.valid()) - _view_stack.stack(*view, &(*neighbor), false); - - return; - } - - case Command::OP_TO_BACK: - { - Command::To_back const &cmd = command.to_back; - if (_views_are_equal(cmd.view, cmd.neighbor)) - return; - - Locked_ptr view(_view_handle_registry.lookup(cmd.view)); - if (!view.valid()) - return; - - /* bring to front if no neighbor is specified */ - if (!cmd.neighbor.valid()) { - _view_stack.stack(*view, nullptr, false); - return; - } - - /* stack view relative to neighbor */ - Locked_ptr neighbor(_view_handle_registry.lookup(cmd.neighbor)); - if (neighbor.valid()) - _view_stack.stack(*view, &(*neighbor), true); - - return; - } - - case Command::OP_BACKGROUND: - { - Command::Background const &cmd = command.background; - if (_provides_default_bg) { - Locked_ptr view(_view_handle_registry.lookup(cmd.view)); - if (!view.valid()) - return; - - view->background(true); - _view_stack.default_background(*view); - return; - } - - /* revert old background view to normal mode */ - if (::Session::background()) - ::Session::background()->background(false); - - /* assign session background */ - Locked_ptr view(_view_handle_registry.lookup(cmd.view)); - if (!view.valid()) - return; - - ::Session::background(&(*view)); - - /* switch background view to background mode */ - if (::Session::background()) - view->background(true); - - return; - } - - case Command::OP_TITLE: - { - Command::Title const &cmd = command.title; - Locked_ptr view(_view_handle_registry.lookup(cmd.view)); - - if (view.valid()) - _view_stack.title(*view, cmd.title.string()); - - return; - } - - case Command::OP_NOP: - return; - } - } - - void _destroy_view(View &view) - { - /* reset background if view was used as default background */ - if (_view_stack.is_default_background(view)) - _view_stack.default_background(_builtin_background); - - _view_stack.remove_view(view); - _env.ep().dissolve(view); - _view_list.remove(&view); - destroy(_view_alloc, &view); - } - - public: - - /** - * Constructor - */ - Session_component(Env &env, - Session_label const &label, - View_stack &view_stack, - Mode &mode, - View &pointer_origin, - View &builtin_background, - Framebuffer::Session &framebuffer, - bool provides_default_bg, - Allocator &session_alloc, - size_t ram_quota, - Genode::Reporter &focus_reporter) - : - ::Session(label), - _env(env), - _session_alloc(&session_alloc, ram_quota), - _framebuffer(framebuffer), - _framebuffer_session_component(view_stack, *this, framebuffer, *this), - _view_stack(view_stack), _mode(mode), - _pointer_origin(pointer_origin), - _builtin_background(builtin_background), - _framebuffer_session_cap(_env.ep().manage(_framebuffer_session_component)), - _input_session_cap(_env.ep().manage(_input_session_component)), - _provides_default_bg(provides_default_bg), - _view_handle_registry(_session_alloc), - _focus_reporter(focus_reporter) - { - _session_alloc.upgrade(ram_quota); - } - - /** - * Destructor - */ - ~Session_component() - { - _env.ep().dissolve(_framebuffer_session_component); - _env.ep().dissolve(_input_session_component); - - destroy_all_views(); - - _release_buffer(); - } - - void destroy_all_views() - { - while (Session_view_list_elem *v = _view_list.first()) - _destroy_view(*static_cast(v)); - } - - /** - * Deliver mode-change signal to client - */ - void notify_mode_change() - { - if (_mode_sigh.valid()) - Signal_transmitter(_mode_sigh).submit(); - } - - void upgrade_ram_quota(size_t ram_quota) { _session_alloc.upgrade(ram_quota); } - - - /********************************** - ** Nitpicker-internal interface ** - **********************************/ - - void submit_input_event(Input::Event e) - { - using namespace Input; - - Point const origin_offset = - ::Session::phys_pos(Point(0, 0), _view_stack.size()); - - /* - * Transpose absolute coordinates by session-specific vertical - * offset. - */ - if (e.ax() || e.ay()) - e = Event(e.type(), e.code(), - Genode::max(0, e.ax() - origin_offset.x()), - Genode::max(0, e.ay() - origin_offset.y()), - e.rx(), e.ry()); - - _input_session_component.submit(&e); - } - - void submit_sync() override - { - _framebuffer_session_component.submit_sync(); - } - - - /********************************* - ** Nitpicker session interface ** - *********************************/ - - Framebuffer::Session_capability framebuffer_session() override { - return _framebuffer_session_cap; } - - Input::Session_capability input_session() override { - return _input_session_cap; } - - View_handle create_view(View_handle parent_handle) override - { - View *view = nullptr; - - /* - * Create child view - */ - if (parent_handle.valid()) { - - try { - Locked_ptr parent(_view_handle_registry.lookup(parent_handle)); - if (!parent.valid()) - return View_handle(); - - view = new (_view_alloc) - View(*this, - View::NOT_TRANSPARENT, View::NOT_BACKGROUND, - &(*parent)); - - parent->add_child(*view); - } - catch (View_handle_registry::Lookup_failed) { - return View_handle(); } - catch (View_handle_registry::Out_of_memory) { - throw Genode::Out_of_ram(); } - } - - /* - * Create top-level view - */ - else { - try { - view = new (_view_alloc) - View(*this, - View::NOT_TRANSPARENT, View::NOT_BACKGROUND, - nullptr); - } - catch (Genode::Allocator::Out_of_memory) { - throw Genode::Out_of_ram(); } - } - - view->apply_origin_policy(_pointer_origin); - - _view_list.insert(view); - _env.ep().manage(*view); - - try { return _view_handle_registry.alloc(*view); } - catch (View_handle_registry::Out_of_memory) { - throw Genode::Out_of_ram(); } - } - - void destroy_view(View_handle handle) override - { - /* - * Search view object given the handle - * - * We cannot look up the view directly from the - * '_view_handle_registry' because we would obtain a weak - * pointer to the view object. If we called the object's - * destructor from the corresponding locked pointer, the - * call of 'lock_for_destruction' in the view's destructor - * would attempt to take the lock again. - */ - for (Session_view_list_elem *v = _view_list.first(); v; v = v->next()) { - - try { - View &view = *static_cast(v); - if (_view_handle_registry.has_handle(view, handle)) { - _destroy_view(view); - break; - } - } catch (View_handle_registry::Lookup_failed) { } - } - - _view_handle_registry.free(handle); - } - - View_handle view_handle(View_capability view_cap, View_handle handle) override - { - auto lambda = [&] (View *view) - { - return (view) ? _view_handle_registry.alloc(*view, handle) - : View_handle(); - }; - - try { return _env.ep().rpc_ep().apply(view_cap, lambda); } - catch (View_handle_registry::Out_of_memory) { - throw Genode::Out_of_ram(); } - } - - View_capability view_capability(View_handle handle) override - { - try { - Locked_ptr view(_view_handle_registry.lookup(handle)); - return view.valid() ? view->cap() : View_capability(); - } - catch (View_handle_registry::Lookup_failed) { - return View_capability(); - } - } - - void release_view_handle(View_handle handle) override - { - try { - _view_handle_registry.free(handle); } - - catch (View_handle_registry::Lookup_failed) { - Genode::warning("view lookup failed while releasing view handle"); - return; - } - } - - Genode::Dataspace_capability command_dataspace() override - { - return _command_ds.cap(); - } - - void execute() override - { - for (unsigned i = 0; i < _command_buffer.num(); i++) { - try { - _execute_command(_command_buffer.get(i)); } - catch (View_handle_registry::Lookup_failed) { - Genode::warning("view lookup failed during command execution"); } - } - } - - Framebuffer::Mode mode() override - { - Area const phys_area(_framebuffer.mode().width(), - _framebuffer.mode().height()); - - Area const session_area = ::Session::screen_area(phys_area); - - return Framebuffer::Mode(session_area.w(), session_area.h(), - _framebuffer.mode().format()); - } - - void mode_sigh(Signal_context_capability sigh) override - { - _mode_sigh = sigh; - } - - void buffer(Framebuffer::Mode mode, bool use_alpha) override - { - /* check if the session quota suffices for the specified mode */ - if (_session_alloc.quota() < ram_quota(mode, use_alpha)) - throw Genode::Out_of_ram(); - - _framebuffer_session_component.notify_mode_change(mode, use_alpha); - } - - void focus(Genode::Capability session_cap) override - { - /* check permission by comparing session labels */ - if (!_focus_change_permitted()) { - Genode::warning("unauthorized focus change requesed by ", label().string()); - return; - } - - /* lookup targeted session object */ - auto lambda = [this] (Session_component *session) - { - _mode.next_focused_session(session); - }; - _env.ep().rpc_ep().apply(session_cap, lambda); - - /* - * To avoid changing the focus in the middle of a drag operation, - * we cannot perform the focus change immediately. Instead, it - * comes into effect via the 'Mode::apply_pending_focus_change()' - * function called the next time when the user input is handled and - * no drag operation is in flight. - */ - } - - void session_control(Label suffix, Session_control control) override - { - Session_label const selector(label(), suffix); - - switch (control) { - case SESSION_CONTROL_HIDE: _view_stack.visible(selector.string(), false); break; - case SESSION_CONTROL_SHOW: _view_stack.visible(selector.string(), true); break; - case SESSION_CONTROL_TO_FRONT: _view_stack.to_front(selector.string()); break; - } - } - - - /******************************* - ** Buffer_provider interface ** - *******************************/ - - Buffer *realloc_buffer(Framebuffer::Mode mode, bool use_alpha) - { - typedef Pixel_rgb565 PT; - - Area const size(mode.width(), mode.height()); - - _buffer_size = - Chunky_dataspace_texture::calc_num_bytes(size, use_alpha); - - /* - * Preserve the content of the original buffer if nitpicker has - * enough lack memory to temporarily keep the original pixels. - */ - Texture const *src_texture = nullptr; - if (::Session::texture()) { - - enum { PRESERVED_RAM = 128*1024 }; - if (_env.ram().avail_ram().value > _buffer_size + PRESERVED_RAM) { - src_texture = static_cast const *>(::Session::texture()); - } else { - Genode::warning("not enough RAM to preserve buffer content during resize"); - _release_buffer(); - } - } - - Chunky_dataspace_texture * const texture = new (&_session_alloc) - Chunky_dataspace_texture(_env.ram(), _env.rm(), size, use_alpha); - - /* copy old buffer content into new buffer and release old buffer */ - if (src_texture) { - - Genode::Surface surface(texture->pixel(), - texture->Texture_base::size()); - - Texture_painter::paint(surface, *src_texture, Color(), Point(0, 0), - Texture_painter::SOLID, false); - _release_buffer(); - } - - if (!_session_alloc.withdraw(_buffer_size)) { - destroy(&_session_alloc, texture); - _buffer_size = 0; - return 0; - } - - ::Session::texture(texture, use_alpha); - ::Session::input_mask(texture->input_mask_buffer()); - - return texture; - } -}; - - template -class Nitpicker::Root : public Genode::Root_component +class Nitpicker::Root : public Root_component, + public Visibility_controller { private: @@ -1029,11 +78,11 @@ class Nitpicker::Root : public Genode::Root_component Global_keys &_global_keys; Framebuffer::Mode _scr_mode; View_stack &_view_stack; - Mode &_mode; - ::View &_pointer_origin; - ::View &_builtin_background; + User_state &_user_state; + View_component &_pointer_origin; + View_component &_builtin_background; Framebuffer::Session &_framebuffer; - Genode::Reporter &_focus_reporter; + Reporter &_focus_reporter; protected: @@ -1042,24 +91,24 @@ class Nitpicker::Root : public Genode::Root_component size_t const ram_quota = Arg_string::find_arg(args, "ram_quota").ulong_value(0); size_t const required_quota = Input::Session_component::ev_ds_size() - + Genode::align_addr(sizeof(Session::Command_buffer), 12); + + align_addr(sizeof(Session::Command_buffer), 12); if (ram_quota < required_quota) { - Genode::warning("Insufficient dontated ram_quota (", ram_quota, - " bytes), require ", required_quota, " bytes"); - throw Genode::Insufficient_ram_quota(); + warning("Insufficient dontated ram_quota (", ram_quota, + " bytes), require ", required_quota, " bytes"); + throw Insufficient_ram_quota(); } size_t const unused_quota = ram_quota - required_quota; - Genode::Session_label const label = Genode::label_from_args(args); + Session_label const label = label_from_args(args); bool const provides_default_bg = (label == "backdrop"); Session_component *session = new (md_alloc()) - Session_component(_env, label, _view_stack, _mode, + Session_component(_env, label, _view_stack, _user_state, _pointer_origin, _builtin_background, _framebuffer, provides_default_bg, *md_alloc(), unused_quota, - _focus_reporter); + _focus_reporter, *this); session->apply_session_policy(_config.xml(), _domain_registry); _session_list.insert(session); @@ -1080,7 +129,7 @@ class Nitpicker::Root : public Genode::Root_component _global_keys.apply_config(_config.xml(), _session_list); session->destroy_all_views(); - _mode.forget(*session); + _user_state.forget(*session); Genode::destroy(md_alloc(), session); } @@ -1092,32 +141,59 @@ class Nitpicker::Root : public Genode::Root_component */ Root(Env &env, Attached_rom_dataspace const &config, Session_list &session_list, Domain_registry const &domain_registry, - Global_keys &global_keys, View_stack &view_stack, Mode &mode, - ::View &pointer_origin, ::View &builtin_background, Allocator &md_alloc, - Framebuffer::Session &framebuffer, Genode::Reporter &focus_reporter) + Global_keys &global_keys, View_stack &view_stack, + User_state &user_state, View_component &pointer_origin, + View_component &builtin_background, Allocator &md_alloc, + Framebuffer::Session &framebuffer, Reporter &focus_reporter) : Root_component(&env.ep().rpc_ep(), &md_alloc), - _env(env), _config(config), - _session_list(session_list), _domain_registry(domain_registry), - _global_keys(global_keys), _view_stack(view_stack), _mode(mode), - _pointer_origin(pointer_origin), _builtin_background(builtin_background), + _env(env), _config(config), _session_list(session_list), + _domain_registry(domain_registry), _global_keys(global_keys), + _view_stack(view_stack), _user_state(user_state), + _pointer_origin(pointer_origin), + _builtin_background(builtin_background), _framebuffer(framebuffer), _focus_reporter(focus_reporter) { } + + + /************************************* + ** Visibility_controller interface ** + *************************************/ + + void _session_visibility(Session_label const &label, Suffix const &suffix, + bool visible) + { + Nitpicker::Session::Label const selector(label, suffix); + + for (Session_component *s = _session_list.first(); s; s = s->next()) + if (s->matches_session_label(selector)) + s->visible(visible); + + _view_stack.update_all_views(); + } + + void hide_matching_sessions(Session_label const &label, Suffix const &suffix) override + { + _session_visibility(label, suffix, false); + } + + void show_matching_sessions(Session_label const &label, Suffix const &suffix) override + { + _session_visibility(label, suffix, true); + } }; struct Nitpicker::Main { - Env &env; + Env &_env; - /* - * Sessions to the required external services - */ - Framebuffer::Connection framebuffer { env, Framebuffer::Mode() }; - Input::Connection input { env }; + Framebuffer::Connection _framebuffer { _env, Framebuffer::Mode() }; - Input::Event * const ev_buf = env.rm().attach(input.dataspace()); + Input::Connection _input { _env }; + + Attached_dataspace _ev_ds { _env.rm(), _input.dataspace() }; typedef Pixel_rgb565 PT; /* physical pixel type */ @@ -1135,60 +211,68 @@ struct Nitpicker::Main Attached_dataspace fb_ds; - Screen screen = { fb_ds.local_addr(), Area(mode.width(), mode.height()) }; + Canvas screen = { fb_ds.local_addr(), Area(mode.width(), mode.height()) }; + + Area size = screen.size(); /** * Constructor */ - Framebuffer_screen(Genode::Region_map &rm, Framebuffer::Session &fb) + Framebuffer_screen(Region_map &rm, Framebuffer::Session &fb) : framebuffer(fb), fb_ds(rm, framebuffer.dataspace()) { } }; - Genode::Reconstructible fb_screen = { env.rm(), framebuffer }; + Reconstructible _fb_screen = { _env.rm(), _framebuffer }; - void handle_fb_mode(); + void _handle_fb_mode(); - Signal_handler
fb_mode_handler = { env.ep(), *this, &Main::handle_fb_mode }; + Signal_handler
_fb_mode_handler = { _env.ep(), *this, &Main::_handle_fb_mode }; /* * User-input policy */ - Global_keys global_keys; + Global_keys _global_keys; - Session_list session_list; + Session_list _session_list; /* * Construct empty domain registry. The initial version will be replaced * on the first call of 'handle_config'. */ - Genode::Heap domain_registry_heap { env.ram(), env.rm() }; - Genode::Reconstructible domain_registry { - domain_registry_heap, Genode::Xml_node("") }; + Heap _domain_registry_heap { _env.ram(), _env.rm() }; - User_state user_state = { global_keys, fb_screen->screen.size() }; + Reconstructible _domain_registry { + _domain_registry_heap, Xml_node("") }; + + Focus _focus; + View_stack _view_stack { _fb_screen->screen.size(), _focus }; + User_state _user_state { _focus, _global_keys, _view_stack }; + + View_owner _global_view_owner; /* * Create view stack with default elements */ - Pointer_origin pointer_origin; + Pointer_origin _pointer_origin { _global_view_owner }; - Background builtin_background = { Area(99999, 99999) }; + Background _builtin_background = { _global_view_owner, Area(99999, 99999) }; /* * Initialize Nitpicker root interface */ - Genode::Sliced_heap sliced_heap { env.ram(), env.rm() }; + Sliced_heap _sliced_heap { _env.ram(), _env.rm() }; - Genode::Reporter pointer_reporter = { env, "pointer" }; - Genode::Reporter hover_reporter = { env, "hover" }; - Genode::Reporter focus_reporter = { env, "focus" }; - Genode::Reporter keystate_reporter = { env, "keystate" }; + Reporter _pointer_reporter = { _env, "pointer" }; + Reporter _hover_reporter = { _env, "hover" }; + Reporter _focus_reporter = { _env, "focus" }; + Reporter _keystate_reporter = { _env, "keystate" }; - Genode::Attached_rom_dataspace config { env, "config" }; + Attached_rom_dataspace _config { _env, "config" }; - Root np_root = { env, config, session_list, *domain_registry, - global_keys, user_state, user_state, pointer_origin, - builtin_background, sliced_heap, framebuffer, focus_reporter }; + Root _root = { _env, _config, _session_list, *_domain_registry, + _global_keys, _view_stack, _user_state, _pointer_origin, + _builtin_background, _sliced_heap, _framebuffer, + _focus_reporter }; /* * Configuration-update handler, executed in the context of the RPC @@ -1197,37 +281,32 @@ struct Nitpicker::Main * In addition to installing the signal handler, we trigger first signal * manually to turn the initial configuration into effect. */ - void handle_config(); + void _handle_config(); - Signal_handler
config_handler = { env.ep(), *this, &Main::handle_config}; + Signal_handler
_config_handler = { _env.ep(), *this, &Main::_handle_config}; /** * Signal handler invoked on the reception of user input */ - void handle_input(); + void _handle_input(); - Signal_handler
input_handler = { env.ep(), *this, &Main::handle_input }; - - /* - * Dispatch input and redraw periodically - */ - Timer::Connection timer { env }; + Signal_handler
_input_handler = { _env.ep(), *this, &Main::_handle_input }; /** * Counter that is incremented periodically */ - unsigned period_cnt = 0; + unsigned _period_cnt = 0; /** * Period counter when the user was active the last time */ - unsigned last_active_period = 0; + unsigned _last_active_period = 0; /** * Number of periods after the last user activity when we regard the user * as becoming inactive */ - unsigned activity_threshold = 50; + unsigned _activity_threshold = 50; /** * True if the user was recently active @@ -1235,113 +314,98 @@ struct Nitpicker::Main * This state is reported as part of focus reports to allow the clipboard * to dynamically adjust its information-flow policy to the user activity. */ - bool user_active = false; + bool _user_active = false; /** * Perform redraw and flush pixels to the framebuffer */ - void draw_and_flush() + void _draw_and_flush() { - user_state.draw(fb_screen->screen).flush([&] (Rect const &rect) { - framebuffer.refresh(rect.x1(), rect.y1(), - rect.w(), rect.h()); }); + _view_stack.draw(_fb_screen->screen).flush([&] (Rect const &rect) { + _framebuffer.refresh(rect.x1(), rect.y1(), + rect.w(), rect.h()); }); } - Main(Env &env) : env(env) + Main(Env &env) : _env(env) { - user_state.default_background(builtin_background); - user_state.stack(pointer_origin); - user_state.stack(builtin_background); + _view_stack.default_background(_builtin_background); + _view_stack.stack(_pointer_origin); + _view_stack.stack(_builtin_background); - config.sigh(config_handler); - handle_config(); + _config.sigh(_config_handler); + _handle_config(); - framebuffer.sync_sigh(input_handler); - framebuffer.mode_sigh(fb_mode_handler); + _framebuffer.sync_sigh(_input_handler); + _framebuffer.mode_sigh(_fb_mode_handler); - env.parent().announce(env.ep().manage(np_root)); + _env.parent().announce(_env.ep().manage(_root)); } }; -void Nitpicker::Main::handle_input() +void Nitpicker::Main::_handle_input() { - period_cnt++; + _period_cnt++; - Point const old_pointer_pos = user_state.pointer_pos(); - ::Session * const old_pointed_session = user_state.pointed_session(); - ::Session * const old_focused_session = user_state.Mode::focused_session(); - bool const old_user_active = user_active; + bool const old_user_active = _user_active; /* handle batch of pending events */ - unsigned const num_events = input.flush(); - if (import_input_events(ev_buf, num_events, user_state)) { - last_active_period = period_cnt; - user_active = true; + User_state::Handle_input_result const result = + _user_state.handle_input_events(_ev_ds.local_addr(), + _input.flush()); + + if (result.user_active) { + _last_active_period = _period_cnt; + _user_active = true; } /* * Report information about currently pressed keys whenever the key state * is affected by the incoming events. */ - if (keystate_reporter.enabled()) { - - bool key_state_affected = false; - for (unsigned i = 0; i < num_events; i++) - key_state_affected |= (ev_buf[i].type() == Input::Event::PRESS) || - (ev_buf[i].type() == Input::Event::RELEASE); - - if (key_state_affected) - Genode::Reporter::Xml_generator xml(keystate_reporter, [&] () { - user_state.report_keystate(xml); }); + if (_keystate_reporter.enabled() && result.key_state_affected) { + Reporter::Xml_generator xml(_keystate_reporter, [&] () { + _user_state.report_keystate(xml); }); } - user_state.Mode::apply_pending_focus_change(); - - Point const new_pointer_pos = user_state.pointer_pos(); - ::Session * const new_pointed_session = user_state.pointed_session(); - ::Session * const new_focused_session = user_state.Mode::focused_session(); - - if (old_focused_session != new_focused_session) - user_state.update_all_views(); + if (result.focus_changed) + _view_stack.update_all_views(); /* flag user as inactive after activity threshold is reached */ - if (period_cnt == last_active_period + activity_threshold) - user_active = false; + if (_period_cnt == _last_active_period + _activity_threshold) + _user_active = false; /* report mouse-position updates */ - if (pointer_reporter.enabled() && old_pointer_pos != new_pointer_pos) { - Genode::Reporter::Xml_generator xml(pointer_reporter, [&] () - { - xml.attribute("xpos", new_pointer_pos.x()); - xml.attribute("ypos", new_pointer_pos.y()); - }); + if (_pointer_reporter.enabled() && result.pointer_position_changed) { + Reporter::Xml_generator xml(_pointer_reporter, [&] () { + _user_state.report_pointer_position(xml); }); } /* report hover changes */ - if (!user_state.Mode::key_pressed() - && old_pointed_session != new_pointed_session) { - report_session(hover_reporter, new_pointed_session); + if (_hover_reporter.enabled() && !result.key_pressed && result.hover_changed) { + Reporter::Xml_generator xml(_hover_reporter, [&] () { + _user_state.report_hovered_view_owner(xml); }); } /* report focus changes */ - if (old_focused_session != new_focused_session - || old_user_active != user_active) - report_session(focus_reporter, new_focused_session, user_active); + if (result.focus_changed || (old_user_active != _user_active)) { + Reporter::Xml_generator xml(_focus_reporter, [&] () { + _user_state.report_focused_view_owner(xml, _user_active); }); + } - /* update mouse cursor */ - if (old_pointer_pos != new_pointer_pos) - user_state.geometry(pointer_origin, Rect(new_pointer_pos, Area())); + /* update pointer position */ + if (result.pointer_position_changed) + _view_stack.geometry(_pointer_origin, Rect(_user_state.pointer_pos(), Area())); /* 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()); }); + _view_stack.draw(_fb_screen->screen).flush([&] (Rect const &rect) { + _framebuffer.refresh(rect.x1(), rect.y1(), + rect.w(), rect.h()); }); - user_state.mark_all_views_as_clean(); + _view_stack.mark_all_views_as_clean(); /* deliver framebuffer synchronization events */ - for (::Session *s = session_list.first(); s; s = s->next()) + for (Session_component *s = _session_list.first(); s; s = s->next()) s->submit_sync(); } @@ -1354,79 +418,73 @@ static void configure_reporter(Genode::Xml_node config, Genode::Reporter &report try { reporter.enabled(config.sub_node("report") .attribute_value(reporter.name().string(), false)); - } catch (...) { - reporter.enabled(false); } + catch (...) { reporter.enabled(false); } } -void Nitpicker::Main::handle_config() +void Nitpicker::Main::_handle_config() { - config.update(); + _config.update(); + + Xml_node const config = _config.xml(); /* update global keys policy */ - global_keys.apply_config(config.xml(), session_list); + _global_keys.apply_config(config, _session_list); /* update background color */ - try { - config.xml().sub_node("background") - .attribute("color").value(&builtin_background.color); - } catch (...) { } + _builtin_background.color = Background::default_color(); + if (config.has_sub_node("background")) + _builtin_background.color = + config.sub_node("background") + .attribute_value("color", Background::default_color()); - /* enable or disable redraw debug mode */ - tmp_fb = config.xml().attribute_value("flash", false) - ? &framebuffer - : nullptr; - - configure_reporter(config.xml(), pointer_reporter); - configure_reporter(config.xml(), hover_reporter); - configure_reporter(config.xml(), focus_reporter); - configure_reporter(config.xml(), keystate_reporter); + configure_reporter(config, _pointer_reporter); + configure_reporter(config, _hover_reporter); + configure_reporter(config, _focus_reporter); + configure_reporter(config, _keystate_reporter); /* update domain registry and session policies */ - for (::Session *s = session_list.first(); s; s = s->next()) + for (Session_component *s = _session_list.first(); s; s = s->next()) s->reset_domain(); - try { - domain_registry.construct(domain_registry_heap, config.xml()); } + try { _domain_registry.construct(_domain_registry_heap, config); } catch (...) { } - for (::Session *s = session_list.first(); s; s = s->next()) - s->apply_session_policy(config.xml(), *domain_registry); + for (Session_component *s = _session_list.first(); s; s = s->next()) + s->apply_session_policy(config, *_domain_registry); - user_state.apply_origin_policy(pointer_origin); + _view_stack.apply_origin_policy(_pointer_origin); /* * Domains may have changed their layering, resort the view stack with the * new constrains. */ - user_state.sort_views_by_layer(); + _view_stack.sort_views_by_layer(); /* redraw */ - user_state.update_all_views(); + _view_stack.update_all_views(); /* update focus report since the domain colors might have changed */ - report_session(focus_reporter, user_state.Mode::focused_session(), user_active); + Reporter::Xml_generator xml(_focus_reporter, [&] () { + _user_state.report_focused_view_owner(xml, _user_active); }); } -void Nitpicker::Main::handle_fb_mode() +void Nitpicker::Main::_handle_fb_mode() { /* reconstruct framebuffer screen and menu bar */ - fb_screen.construct(env.rm(), framebuffer); + _fb_screen.construct(_env.rm(), _framebuffer); /* let the view stack use the new size */ - user_state.size(Area(fb_screen->mode.width(), fb_screen->mode.height())); + _view_stack.size(Area(_fb_screen->mode.width(), _fb_screen->mode.height())); /* redraw */ - user_state.update_all_views(); + _view_stack.update_all_views(); /* notify clients about the change screen mode */ - for (::Session *s = session_list.first(); s; s = s->next()) { - Session_component *sc = dynamic_cast(s); - if (sc) - sc->notify_mode_change(); - } + for (Session_component *s = _session_list.first(); s; s = s->next()) + s->notify_mode_change(); } diff --git a/repos/os/src/server/nitpicker/mode.h b/repos/os/src/server/nitpicker/mode.h deleted file mode 100644 index 1418301586..0000000000 --- a/repos/os/src/server/nitpicker/mode.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * \brief Nitpicker mode - * \author Norman Feske - * \date 2006-08-22 - */ - -/* - * Copyright (C) 2006-2017 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 _MODE_H_ -#define _MODE_H_ - -class Session; - -class Mode -{ - private: - - /* - * Number of currently pressed keys. This counter is used to determine - * if the user is dragging an item. - */ - unsigned _key_cnt = 0; - - Session *_focused_session = nullptr; - - Session *_next_focused_session = nullptr; - - protected: - - /* - * True while a global key sequence is processed - */ - bool _global_key_sequence = false; - - public: - - virtual ~Mode() { } - - /** - * Accessors - */ - bool drag() const { return _key_cnt > 0; } - - void inc_key_cnt() { _key_cnt++; } - void dec_key_cnt() { _key_cnt--; } - - unsigned key_cnt() const { return _key_cnt; } - - bool key_pressed() const { return _key_cnt > 0; } - - Session *focused_session() { return _focused_session; } - Session const *focused_session() const { return _focused_session; } - - virtual void focused_session(Session *session) - { - _focused_session = session; - _next_focused_session = session; - } - - bool focused(Session const &session) const { return &session == _focused_session; } - - void next_focused_session(Session *session) { _next_focused_session = session; } - - /** - * Apply pending focus-change request that was issued during drag state - */ - void apply_pending_focus_change() - { - /* - * Defer focus changes to a point where no drag operation is in - * flight because otherwise, the involved sessions would obtain - * inconsistent press and release events. However, focus changes - * during global key sequences are fine. - */ - if (key_pressed() && !_global_key_sequence) - return; - - if (_focused_session != _next_focused_session) - _focused_session = _next_focused_session; - } - - /** - * Discard all references to specified view - */ - virtual void forget(Session const &session) - { - if (&session == _focused_session) _focused_session = nullptr; - if (&session == _next_focused_session) _next_focused_session = nullptr; - } -}; - -#endif diff --git a/repos/os/src/server/nitpicker/pointer_origin.h b/repos/os/src/server/nitpicker/pointer_origin.h index 5d974d1daf..efca9a67f4 100644 --- a/repos/os/src/server/nitpicker/pointer_origin.h +++ b/repos/os/src/server/nitpicker/pointer_origin.h @@ -14,33 +14,28 @@ #ifndef _POINTER_ORIGIN_H_ #define _POINTER_ORIGIN_H_ -#include "view.h" -#include "session.h" +#include "view_component.h" +#include "session_component.h" -struct Pointer_origin : Session, View +namespace Nitpicker { struct Pointer_origin; } + + +struct Nitpicker::Pointer_origin : View_component { - Pointer_origin() + Pointer_origin(View_owner &owner) : - Session(Genode::Session_label()), - View(*this, View::TRANSPARENT, View::NOT_BACKGROUND, 0) + View_component(owner, View_component::TRANSPARENT, + View_component::NOT_BACKGROUND, 0) { } - /*********************** - ** Session interface ** - ***********************/ + /****************************** + ** View_component interface ** + ******************************/ - void submit_input_event(Input::Event) override { } - void submit_sync() override { } - - - /******************** - ** View interface ** - ********************/ - - int frame_size(Mode const &) const override { return 0; } - void frame(Canvas_base &, Mode const &) const override { } - void draw(Canvas_base &, Mode const &) const override { } + int frame_size(Focus const &) const override { return 0; } + void frame(Canvas_base &, Focus const &) const override { } + void draw(Canvas_base &, Focus const &) const override { } }; #endif /* _POINTER_ORIGIN_H_ */ diff --git a/repos/os/src/server/nitpicker/session.h b/repos/os/src/server/nitpicker/session.h deleted file mode 100644 index afe8fac75e..0000000000 --- a/repos/os/src/server/nitpicker/session.h +++ /dev/null @@ -1,232 +0,0 @@ -/* - * \brief Nitpicker session interface - * \author Norman Feske - * \date 2006-08-09 - */ - -/* - * Copyright (C) 2006-2017 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 _SESSION_H_ -#define _SESSION_H_ - -/* Genode includes */ -#include -#include -#include - -/* local includes */ -#include "color.h" -#include "canvas.h" -#include "domain_registry.h" - -class View; -class Session; - -namespace Input { class Event; } - -typedef Genode::List Session_list; - -class Session : public Session_list::Element -{ - private: - - Genode::Session_label const _label; - Domain_registry::Entry const *_domain; - Texture_base const *_texture = { 0 }; - bool _uses_alpha = { false }; - bool _visible = true; - View *_background = 0; - unsigned char const *_input_mask = { 0 }; - - public: - - /** - * Constructor - * - * \param label session label - */ - explicit Session(Genode::Session_label const &label) : _label(label) { } - - virtual ~Session() { } - - virtual void submit_input_event(Input::Event ev) = 0; - - virtual void submit_sync() = 0; - - Genode::Session_label const &label() const { return _label; } - - /** - * Return true if session label starts with specified 'selector' - */ - bool matches_session_label(char const *selector) const - { - /* - * Append label separator to match selectors with a trailing - * separator. - */ - char label[Genode::Session_label::capacity() + 4]; - Genode::snprintf(label, sizeof(label), "%s ->", _label.string()); - return Genode::strcmp(label, selector, - Genode::strlen(selector)) == 0; - } - - /** - * Accessors to the domain configuration used in conditions - */ - bool label_visible() const { return !_domain || _domain->label_visible(); } - bool content_client() const { return _domain && _domain->content_client(); } - bool hover_focused() const { return !_domain || _domain->hover_focused(); } - bool hover_always() const { return _domain && _domain->hover_always(); } - bool origin_pointer() const { return _domain && _domain->origin_pointer(); } - - unsigned layer() const { return _domain ? _domain->layer() : ~0UL; } - - bool visible() const { return _visible; } - - void visible(bool visible) { _visible = visible; } - - Domain_registry::Entry::Name domain_name() const - { - return _domain ? _domain->name() : Domain_registry::Entry::Name(); - } - - Texture_base const *texture() const { return _texture; } - - void texture(Texture_base const *texture, bool uses_alpha) - { - _texture = texture; - _uses_alpha = uses_alpha; - } - - /** - * Set input mask buffer - * - * \param mask input mask buffer containing a byte value per texture - * pixel, which describes the policy of handling user - * input referring to the pixel. If set to zero, the input - * is passed through the view such that it can be handled - * by one of the subsequent views in the view stack. If - * set to one, the input is consumed by the view. If - * 'input_mask' is a null pointer, user input is - * unconditionally consumed by the view. - */ - void input_mask(unsigned char const *mask) { _input_mask = mask; } - - Color color() const { return _domain ? _domain->color() : WHITE; } - - View *background() const { return _background; } - - void background(View *background) { _background = background; } - - /** - * Return true if session uses an alpha channel - */ - bool uses_alpha() const { return _texture && _uses_alpha; } - - /** - * Calculate session-local coordinate to physical screen position - * - * \param pos coordinate in session-local coordinate system - * \param screen_area session-local screen size - */ - Point phys_pos(Point pos, Area screen_area) const - { - return _domain ? _domain->phys_pos(pos, screen_area) : Point(0, 0); - } - - /** - * Return session-local screen area - * - * \param phys_pos size of physical screen - */ - Area screen_area(Area phys_area) const - { - return _domain ? _domain->screen_area(phys_area) : Area(0, 0); - } - - /** - * Return input mask value at specified buffer position - */ - unsigned char input_mask_at(Point p) const - { - if (!_input_mask || !_texture) return 0; - - /* check boundaries */ - if ((unsigned)p.x() >= _texture->size().w() - || (unsigned)p.y() >= _texture->size().h()) - return 0; - - return _input_mask[p.y()*_texture->size().w() + p.x()]; - } - - bool has_same_domain(Session const *s) const - { - return s && (s->_domain == _domain); - } - - bool has_focusable_domain() - { - return has_valid_domain() - && (_domain->focus_click() || _domain->focus_transient()); - } - - bool has_transient_focusable_domain() - { - return has_valid_domain() && _domain->focus_transient(); - } - - bool has_valid_domain() const - { - return _domain; - } - - void reset_domain() - { - _domain = nullptr; - } - - /** - * Set session domain according to the list of configured policies - * - * Select the policy that matches the label. If multiple policies - * match, select the one with the largest number of characters. - */ - void apply_session_policy(Genode::Xml_node config, - Domain_registry const &domain_registry) - { - reset_domain(); - - try { - Genode::Session_policy policy(_label, config); - - /* read domain attribute */ - if (!policy.has_attribute("domain")) { - Genode::error("policy for label \"", _label, "\" lacks domain declaration"); - return; - } - - typedef Domain_registry::Entry::Name Domain_name; - char buf[sizeof(Domain_name)]; - buf[0] = 0; - try { - policy.attribute("domain").value(buf, sizeof(buf)); } - catch (...) { } - - Domain_name name(buf); - _domain = domain_registry.lookup(name); - - if (!_domain) - Genode::error("policy for label \"", _label, - "\" specifies nonexistent domain \"", name, "\""); - - } catch (...) { - Genode::error("no policy matching label \"", _label, "\""); } - } -}; - -#endif diff --git a/repos/os/src/server/nitpicker/session_component.cc b/repos/os/src/server/nitpicker/session_component.cc new file mode 100644 index 0000000000..947298cb1a --- /dev/null +++ b/repos/os/src/server/nitpicker/session_component.cc @@ -0,0 +1,484 @@ +/* + * \brief Nitpicker session component + * \author Norman Feske + * \date 2017-11-16 + */ + +/* + * Copyright (C) 2006-2017 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. + */ + +#include "view_stack.h" + +using namespace Nitpicker; + + +void Session_component::_release_buffer() +{ + if (!_texture) + return; + + typedef Pixel_rgb565 PT; + + Chunky_texture const *cdt = static_cast const *>(_texture); + + _texture = nullptr; + _uses_alpha = false; + _input_mask = nullptr; + + destroy(&_session_alloc, const_cast *>(cdt)); + + _session_alloc.upgrade(_buffer_size); + _buffer_size = 0; +} + + +bool Session_component::_views_are_equal(View_handle v1, View_handle v2) +{ + if (!v1.valid() || !v2.valid()) + return false; + + Weak_ptr v1_ptr = _view_handle_registry.lookup(v1); + Weak_ptr v2_ptr = _view_handle_registry.lookup(v2); + + return v1_ptr == v2_ptr; +} + + +void Session_component::_execute_command(Command const &command) +{ + switch (command.opcode) { + + case Command::OP_GEOMETRY: + { + Command::Geometry const &cmd = command.geometry; + Locked_ptr view(_view_handle_registry.lookup(cmd.view)); + if (!view.valid()) + return; + + Point pos = cmd.rect.p1(); + + /* transpose position of top-level views by vertical session offset */ + if (view->top_level()) + pos = _phys_pos(pos, _view_stack.size()); + + if (view.valid()) + _view_stack.geometry(*view, Rect(pos, cmd.rect.area())); + + return; + } + + case Command::OP_OFFSET: + { + Command::Offset const &cmd = command.offset; + Locked_ptr view(_view_handle_registry.lookup(cmd.view)); + + if (view.valid()) + _view_stack.buffer_offset(*view, cmd.offset); + + return; + } + + case Command::OP_TO_FRONT: + { + Command::To_front const &cmd = command.to_front; + if (_views_are_equal(cmd.view, cmd.neighbor)) + return; + + Locked_ptr view(_view_handle_registry.lookup(cmd.view)); + if (!view.valid()) + return; + + /* bring to front if no neighbor is specified */ + if (!cmd.neighbor.valid()) { + _view_stack.stack(*view, nullptr, true); + return; + } + + /* stack view relative to neighbor */ + Locked_ptr neighbor(_view_handle_registry.lookup(cmd.neighbor)); + if (neighbor.valid()) + _view_stack.stack(*view, &(*neighbor), false); + + return; + } + + case Command::OP_TO_BACK: + { + Command::To_back const &cmd = command.to_back; + if (_views_are_equal(cmd.view, cmd.neighbor)) + return; + + Locked_ptr view(_view_handle_registry.lookup(cmd.view)); + if (!view.valid()) + return; + + /* bring to front if no neighbor is specified */ + if (!cmd.neighbor.valid()) { + _view_stack.stack(*view, nullptr, false); + return; + } + + /* stack view relative to neighbor */ + Locked_ptr neighbor(_view_handle_registry.lookup(cmd.neighbor)); + if (neighbor.valid()) + _view_stack.stack(*view, &(*neighbor), true); + + return; + } + + case Command::OP_BACKGROUND: + { + Command::Background const &cmd = command.background; + if (_provides_default_bg) { + Locked_ptr view(_view_handle_registry.lookup(cmd.view)); + if (!view.valid()) + return; + + view->background(true); + _view_stack.default_background(*view); + return; + } + + /* revert old background view to normal mode */ + if (_background) + _background->background(false); + + /* assign session background */ + Locked_ptr view(_view_handle_registry.lookup(cmd.view)); + if (!view.valid()) + return; + + _background = &(*view); + + /* switch background view to background mode */ + if (background()) + view->background(true); + + return; + } + + case Command::OP_TITLE: + { + Command::Title const &cmd = command.title; + Locked_ptr view(_view_handle_registry.lookup(cmd.view)); + + if (view.valid()) + _view_stack.title(*view, cmd.title.string()); + + return; + } + + case Command::OP_NOP: + return; + } +} + + +void Session_component::_destroy_view(View_component &view) +{ + if (_background == &view) + _background = nullptr; + + /* reset background if view was used as default background */ + if (_view_stack.is_default_background(view)) + _view_stack.default_background(_builtin_background); + + _view_stack.remove_view(view); + _env.ep().dissolve(view); + _view_list.remove(&view); + destroy(_view_alloc, &view); +} + + +void Session_component::destroy_all_views() +{ + while (Session_view_list_elem *v = _view_list.first()) + _destroy_view(*static_cast(v)); +} + + +void Session_component::submit_input_event(Input::Event e) +{ + using namespace Input; + + Point const origin_offset = _phys_pos(Point(0, 0), _view_stack.size()); + + /* + * Transpose absolute coordinates by session-specific vertical + * offset. + */ + if (e.ax() || e.ay()) + e = Event(e.type(), e.code(), + max(0, e.ax() - origin_offset.x()), + max(0, e.ay() - origin_offset.y()), + e.rx(), e.ry()); + + _input_session_component.submit(&e); +} + + +Session_component::View_handle Session_component::create_view(View_handle parent_handle) +{ + View_component *view = nullptr; + + /* + * Create child view + */ + if (parent_handle.valid()) { + + try { + Locked_ptr parent(_view_handle_registry.lookup(parent_handle)); + if (!parent.valid()) + return View_handle(); + + view = new (_view_alloc) + View_component(*this, + View_component::NOT_TRANSPARENT, View_component::NOT_BACKGROUND, + &(*parent)); + + parent->add_child(*view); + } + catch (View_handle_registry::Lookup_failed) { return View_handle(); } + catch (View_handle_registry::Out_of_memory) { throw Out_of_ram(); } + } + + /* + * Create top-level view + */ + else { + try { + view = new (_view_alloc) + View_component(*this, + View_component::NOT_TRANSPARENT, View_component::NOT_BACKGROUND, + nullptr); + } + catch (Allocator::Out_of_memory) { throw Out_of_ram(); } + } + + view->apply_origin_policy(_pointer_origin); + + _view_list.insert(view); + _env.ep().manage(*view); + + try { + return _view_handle_registry.alloc(*view); + } + catch (View_handle_registry::Out_of_memory) { throw Out_of_ram(); } +} + + +void Session_component::apply_session_policy(Xml_node config, + Domain_registry const &domain_registry) +{ + reset_domain(); + + try { + Session_policy policy(_label, config); + + /* read domain attribute */ + if (!policy.has_attribute("domain")) { + error("policy for label \"", _label, "\" lacks domain declaration"); + return; + } + + typedef Domain_registry::Entry::Name Domain_name; + char buf[sizeof(Domain_name)]; + buf[0] = 0; + try { + policy.attribute("domain").value(buf, sizeof(buf)); } + catch (...) { } + + Domain_name name(buf); + _domain = domain_registry.lookup(name); + + if (!_domain) + error("policy for label \"", _label, + "\" specifies nonexistent domain \"", name, "\""); + + } catch (...) { + error("no policy matching label \"", _label, "\""); } +} + + +void Session_component::destroy_view(View_handle handle) +{ + /* + * Search view object given the handle + * + * We cannot look up the view directly from the + * '_view_handle_registry' because we would obtain a weak + * pointer to the view object. If we called the object's + * destructor from the corresponding locked pointer, the + * call of 'lock_for_destruction' in the view's destructor + * would attempt to take the lock again. + */ + for (Session_view_list_elem *v = _view_list.first(); v; v = v->next()) { + + try { + View_component &view = *static_cast(v); + if (_view_handle_registry.has_handle(view, handle)) { + _destroy_view(view); + break; + } + } catch (View_handle_registry::Lookup_failed) { } + } + + _view_handle_registry.free(handle); +} + + +Session_component::View_handle +Session_component::view_handle(View_capability view_cap, View_handle handle) +{ + auto lambda = [&] (View_component *view) + { + return (view) ? _view_handle_registry.alloc(*view, handle) + : View_handle(); + }; + + try { + return _env.ep().rpc_ep().apply(view_cap, lambda); + } + catch (View_handle_registry::Out_of_memory) { throw Out_of_ram(); } +} + + +View_capability Session_component::view_capability(View_handle handle) +{ + try { + Locked_ptr view(_view_handle_registry.lookup(handle)); + return view.valid() ? view->cap() : View_capability(); + } + catch (View_handle_registry::Lookup_failed) { return View_capability(); } +} + + +void Session_component::release_view_handle(View_handle handle) +{ + try { + _view_handle_registry.free(handle); } + + catch (View_handle_registry::Lookup_failed) { + warning("view lookup failed while releasing view handle"); + return; + } +} + + +void Session_component::execute() +{ + for (unsigned i = 0; i < _command_buffer.num(); i++) { + try { + _execute_command(_command_buffer.get(i)); } + catch (View_handle_registry::Lookup_failed) { + warning("view lookup failed during command execution"); } + } +} + +Framebuffer::Mode Session_component::mode() +{ + Area const phys_area(_framebuffer.mode().width(), + _framebuffer.mode().height()); + + Area const session_area = screen_area(phys_area); + + return Framebuffer::Mode(session_area.w(), session_area.h(), + _framebuffer.mode().format()); +} + + +void Session_component::buffer(Framebuffer::Mode mode, bool use_alpha) +{ + /* check if the session quota suffices for the specified mode */ + if (_session_alloc.quota() < ram_quota(mode, use_alpha)) + throw Out_of_ram(); + + _framebuffer_session_component.notify_mode_change(mode, use_alpha); +} + + +void Session_component::focus(Capability session_cap) +{ + if (this->cap() == session_cap) + return; + + Session_component const &caller = *this; + + _env.ep().rpc_ep().apply(session_cap, [&] (Session_component *session) { + if (session) + _focus_controller.focus_view_owner(caller, *session); }); +} + + +void Session_component::session_control(Label suffix, Session_control control) +{ + switch (control) { + case SESSION_CONTROL_HIDE: + _visibility_controller.hide_matching_sessions(label(), suffix); + break; + + case SESSION_CONTROL_SHOW: + _visibility_controller.show_matching_sessions(label(), suffix); + break; + + case SESSION_CONTROL_TO_FRONT: + _view_stack.to_front(Label(label(), suffix).string()); + break; + } +} + + +Buffer *Session_component::realloc_buffer(Framebuffer::Mode mode, bool use_alpha) +{ + typedef Pixel_rgb565 PT; + + Area const size(mode.width(), mode.height()); + + _buffer_size = Chunky_texture::calc_num_bytes(size, use_alpha); + + /* + * Preserve the content of the original buffer if nitpicker has + * enough lack memory to temporarily keep the original pixels. + */ + Texture const *src_texture = nullptr; + if (texture()) { + + enum { PRESERVED_RAM = 128*1024 }; + if (_env.ram().avail_ram().value > _buffer_size + PRESERVED_RAM) { + src_texture = static_cast const *>(texture()); + } else { + warning("not enough RAM to preserve buffer content during resize"); + _release_buffer(); + } + } + + Chunky_texture * const texture = new (&_session_alloc) + Chunky_texture(_env.ram(), _env.rm(), size, use_alpha); + + /* copy old buffer content into new buffer and release old buffer */ + if (src_texture) { + + Surface surface(texture->pixel(), + texture->Texture_base::size()); + + Texture_painter::paint(surface, *src_texture, Color(), Point(0, 0), + Texture_painter::SOLID, false); + _release_buffer(); + } + + if (!_session_alloc.withdraw(_buffer_size)) { + destroy(&_session_alloc, texture); + _buffer_size = 0; + return nullptr; + } + + _texture = texture; + _uses_alpha = use_alpha; + _input_mask = texture->input_mask_buffer(); + + return texture; +} diff --git a/repos/os/src/server/nitpicker/session_component.h b/repos/os/src/server/nitpicker/session_component.h new file mode 100644 index 0000000000..fa18e032a6 --- /dev/null +++ b/repos/os/src/server/nitpicker/session_component.h @@ -0,0 +1,379 @@ +/* + * \brief Nitpicker session component + * \author Norman Feske + * \date 2017-11-16 + */ + +/* + * Copyright (C) 2006-2017 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 _SESSION_COMPONENT_H_ +#define _SESSION_COMPONENT_H_ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include +#include + +/* local includes */ +#include "canvas.h" +#include "domain_registry.h" +#include "framebuffer_session.h" +#include "input_session.h" +#include "focus.h" +#include "chunky_texture.h" +#include "view_component.h" + +namespace Nitpicker { + + class Visibility_controller; + class Session_component; + class View_component; + + typedef List Session_list; +} + + +struct Nitpicker::Visibility_controller +{ + typedef Session::Label Suffix; + + virtual void hide_matching_sessions(Session_label const &, Suffix const &) = 0; + + virtual void show_matching_sessions(Session_label const &, Suffix const &) = 0; +}; + + +class Nitpicker::Session_component : public Rpc_object, + public View_owner, + public Buffer_provider, + public Session_list::Element +{ + private: + + Env &_env; + + Session_label const _label; + + Domain_registry::Entry const *_domain = nullptr; + Texture_base const *_texture = nullptr; + View_component *_background = nullptr; + + /* + * The input mask buffer containing a byte value per texture pixel, + * which describes the policy of handling user input referring to the + * pixel. If set to zero, the input is passed through the view such + * that it can be handled by one of the subsequent views in the view + * stack. If set to one, the input is consumed by the view. If + * 'input_mask' is a null pointer, user input is unconditionally + * consumed by the view. + */ + unsigned char const *_input_mask = nullptr; + + bool _uses_alpha = false; + bool _visible = true; + + Allocator_guard _session_alloc; + + Framebuffer::Session &_framebuffer; + + Framebuffer::Session_component _framebuffer_session_component; + + Input::Session_component _input_session_component { _env }; + + View_stack &_view_stack; + + Focus_controller &_focus_controller; + + Signal_context_capability _mode_sigh; + + View_component &_pointer_origin; + + View_component &_builtin_background; + + List _view_list; + + Tslab _view_alloc { &_session_alloc }; + + /* capabilities for sub sessions */ + Framebuffer::Session_capability _framebuffer_session_cap; + Input::Session_capability _input_session_cap; + + bool const _provides_default_bg; + + /* size of currently allocated virtual framebuffer, in bytes */ + size_t _buffer_size = 0; + + Attached_ram_dataspace _command_ds { _env.ram(), _env.rm(), + sizeof(Command_buffer) }; + + Command_buffer &_command_buffer = *_command_ds.local_addr(); + + typedef Handle_registry View_handle_registry; + + View_handle_registry _view_handle_registry; + + Reporter &_focus_reporter; + + Visibility_controller &_visibility_controller; + + /** + * Calculate session-local coordinate to physical screen position + * + * \param pos coordinate in session-local coordinate system + * \param screen_area session-local screen size + */ + Point _phys_pos(Point pos, Area screen_area) const + { + return _domain ? _domain->phys_pos(pos, screen_area) : Point(0, 0); + } + + void _release_buffer(); + + /** + * Helper for performing sanity checks in OP_TO_FRONT and OP_TO_BACK + * + * We have to check for the equality of both the specified view and + * neighbor. If both arguments refer to the same view, the creation of + * locked pointers for both views would result in a deadlock. + */ + bool _views_are_equal(View_handle, View_handle); + + bool _focus_change_permitted() const; + + void _execute_command(Command const &); + + void _destroy_view(View_component &); + + public: + + Session_component(Env &env, + Session_label const &label, + View_stack &view_stack, + Focus_controller &focus_controller, + View_component &pointer_origin, + View_component &builtin_background, + Framebuffer::Session &framebuffer, + bool provides_default_bg, + Allocator &session_alloc, + size_t ram_quota, + Reporter &focus_reporter, + Visibility_controller &visibility_controller) + : + _env(env), + _label(label), + _session_alloc(&session_alloc, ram_quota), + _framebuffer(framebuffer), + _framebuffer_session_component(view_stack, *this, framebuffer, *this), + _view_stack(view_stack), _focus_controller(focus_controller), + _pointer_origin(pointer_origin), + _builtin_background(builtin_background), + _framebuffer_session_cap(_env.ep().manage(_framebuffer_session_component)), + _input_session_cap(_env.ep().manage(_input_session_component)), + _provides_default_bg(provides_default_bg), + _view_handle_registry(_session_alloc), + _focus_reporter(focus_reporter), + _visibility_controller(visibility_controller) + { + _session_alloc.upgrade(ram_quota); + } + + ~Session_component() + { + _env.ep().dissolve(_framebuffer_session_component); + _env.ep().dissolve(_input_session_component); + + destroy_all_views(); + + _release_buffer(); + } + + + /************************** + ** View_owner interface ** + **************************/ + + Session::Label label() const override { return _label; } + + /** + * Return true if session label starts with specified 'selector' + */ + bool matches_session_label(Session::Label const &selector) const override + { + /* + * Append label separator to match selectors with a trailing + * separator. + */ + String const label(_label, " ->"); + return strcmp(label.string(), selector.string(), + strlen(selector.string())) == 0; + } + + bool visible() const override { return _visible; } + + bool label_visible() const override + { + return !_domain || _domain->label_visible(); + } + + bool has_same_domain(View_owner const *owner) const override + { + if (!owner) return false; + return static_cast(*owner)._domain == _domain; + } + + bool has_focusable_domain() const override + { + return _domain && (_domain->focus_click() || _domain->focus_transient()); + } + + bool has_transient_focusable_domain() const override + { + return _domain && _domain->focus_transient(); + } + + Color color() const override { return _domain ? _domain->color() : white(); } + + bool content_client() const override { return _domain && _domain->content_client(); } + + bool hover_always() const override { return _domain && _domain->hover_always(); } + + View const *background() const override { return _background; } + + Texture_base const *texture() const override { return _texture; } + + bool uses_alpha() const override { return _texture && _uses_alpha; } + + unsigned layer() const override { return _domain ? _domain->layer() : ~0UL; } + + bool origin_pointer() const override { return _domain && _domain->origin_pointer(); } + + /** + * Return input mask value at specified buffer position + */ + unsigned char input_mask_at(Point p) const override + { + if (!_input_mask || !_texture) return 0; + + /* check boundaries */ + if ((unsigned)p.x() >= _texture->size().w() + || (unsigned)p.y() >= _texture->size().h()) + return 0; + + return _input_mask[p.y()*_texture->size().w() + p.x()]; + } + + void submit_input_event(Input::Event e) override; + + void report(Xml_generator &xml) const override + { + xml.attribute("label", _label); + xml.attribute("color", String<32>(color())); + + if (_domain) + xml.attribute("domain", _domain->name()); + } + + + /**************************************** + ** Interface used by the main program ** + ****************************************/ + + /** + * Set the visibility of the views owned by the session + */ + void visible(bool visible) { _visible = visible; } + + /** + * Return session-local screen area + * + * \param phys_pos size of physical screen + */ + Area screen_area(Area phys_area) const + { + return _domain ? _domain->screen_area(phys_area) : Area(0, 0); + } + + void reset_domain() { _domain = nullptr; } + + /** + * Set session domain according to the list of configured policies + * + * Select the policy that matches the label. If multiple policies + * match, select the one with the largest number of characters. + */ + void apply_session_policy(Xml_node config, Domain_registry const &); + + void destroy_all_views(); + + /** + * Deliver mode-change signal to client + */ + void notify_mode_change() + { + if (_mode_sigh.valid()) + Signal_transmitter(_mode_sigh).submit(); + } + + void upgrade_ram_quota(size_t ram_quota) { _session_alloc.upgrade(ram_quota); } + + /** + * Deliver sync signal to the client's virtual frame buffer + */ + void submit_sync() + { + _framebuffer_session_component.submit_sync(); + } + + + /********************************* + ** Nitpicker session interface ** + *********************************/ + + Framebuffer::Session_capability framebuffer_session() override { + return _framebuffer_session_cap; } + + Input::Session_capability input_session() override { + return _input_session_cap; } + + View_handle create_view(View_handle parent_handle) override; + + void destroy_view(View_handle handle) override; + + View_handle view_handle(View_capability view_cap, View_handle handle) override; + + View_capability view_capability(View_handle handle) override; + + void release_view_handle(View_handle handle) override; + + Dataspace_capability command_dataspace() override { return _command_ds.cap(); } + + void execute() override; + + Framebuffer::Mode mode() override; + + void mode_sigh(Signal_context_capability sigh) override { _mode_sigh = sigh; } + + void buffer(Framebuffer::Mode mode, bool use_alpha) override; + + void focus(Capability session_cap) override; + + void session_control(Label suffix, Session_control control) override; + + + /******************************* + ** Buffer_provider interface ** + *******************************/ + + Buffer *realloc_buffer(Framebuffer::Mode mode, bool use_alpha) override; +}; + +#endif /* _SESSION_COMPONENT_H_ */ diff --git a/repos/os/src/server/nitpicker/target.mk b/repos/os/src/server/nitpicker/target.mk index 143f79f00e..0cb3be8128 100644 --- a/repos/os/src/server/nitpicker/target.mk +++ b/repos/os/src/server/nitpicker/target.mk @@ -1,9 +1,4 @@ TARGET = nitpicker LIBS = base blit -SRC_CC = main.cc \ - view_stack.cc \ - view.cc \ - user_state.cc \ - global_keys.cc - +SRC_CC = $(notdir $(wildcard $(PRG_DIR)/*.cc)) SRC_BIN = default.tff diff --git a/repos/os/src/server/nitpicker/types.h b/repos/os/src/server/nitpicker/types.h new file mode 100644 index 0000000000..d612ae57f8 --- /dev/null +++ b/repos/os/src/server/nitpicker/types.h @@ -0,0 +1,42 @@ +/* + * \brief Common types used within nitpicker + * \date 2017-11-166 + * \author Norman Feske + */ + +/* + * Copyright (C) 2017 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 _TYPES_H_ +#define _TYPES_H_ + +/* Genode includes */ +#include +#include +#include +#include +#include + +namespace Nitpicker { + + using namespace Genode; + + typedef Surface_base::Point Point; + typedef Surface_base::Area Area; + typedef Surface_base::Rect Rect; + + /* + * Symbolic names for some important colors + */ + static inline Color black() { return Color(0, 0, 0); } + static inline Color white() { return Color(255, 255, 255); } + + class Session_component; + class View_stack; +} + +#endif /* _TYPES_H_ */ diff --git a/repos/os/src/server/nitpicker/user_state.cc b/repos/os/src/server/nitpicker/user_state.cc index cbe4c634f2..5c0c85239c 100644 --- a/repos/os/src/server/nitpicker/user_state.cc +++ b/repos/os/src/server/nitpicker/user_state.cc @@ -27,23 +27,52 @@ static inline bool _mouse_button(Keycode keycode) { return keycode >= BTN_LEFT && keycode <= BTN_MIDDLE; } +/** + * Determine number of events that can be merged into one + * + * \param ev pointer to first event array element to check + * \param max size of the event array + * \return number of events subjected to merge + */ +static unsigned num_consecutive_events(Input::Event const *ev, unsigned max) +{ + if (max < 1) return 0; + if (ev->type() != Input::Event::MOTION) return 1; + + bool const first_absolute = ev->absolute_motion(); + + /* iterate until we get a different event type, start at second */ + unsigned cnt = 1; + for (ev++ ; cnt < max; cnt++, ev++) { + if (ev->type() != Input::Event::MOTION) break; + if (first_absolute != ev->absolute_motion()) break; + } + return cnt; +} + + +/** + * Merge consecutive motion events + * + * \param ev event array to merge + * \param n number of events to merge + * \return merged motion event + */ +static Input::Event merge_motion_events(Input::Event const *ev, unsigned n) +{ + Input::Event res; + for (unsigned i = 0; i < n; i++, ev++) + res = Input::Event(Input::Event::MOTION, 0, ev->ax(), ev->ay(), + res.rx() + ev->rx(), res.ry() + ev->ry()); + return res; +} + + /************************** ** User state interface ** **************************/ -User_state::User_state(Global_keys &global_keys, Area view_stack_size) -: - View_stack(view_stack_size, *this), _global_keys(global_keys) -{ } - - -void User_state::_update_all() -{ - update_all_views(); -} - - -void User_state::handle_event(Input::Event ev) +void User_state::_handle_input_event(Input::Event ev) { Input::Keycode const keycode = ev.keycode(); Input::Event::Type const type = ev.type(); @@ -57,8 +86,8 @@ void User_state::handle_event(Input::Event ev) /* transparently handle absolute and relative motion events */ if (type == Event::MOTION) { if ((ev.rx() || ev.ry()) && ev.ax() == 0 && ev.ay() == 0) { - ax = Genode::max(0, Genode::min((int)size().w() - 1, ax + ev.rx())); - ay = Genode::max(0, Genode::min((int)size().h() - 1, ay + ev.ry())); + ax = max(0, min((int)_view_stack.size().w() - 1, ax + ev.rx())); + ay = max(0, min((int)_view_stack.size().h() - 1, ay + ev.ry())); } else { ax = ev.ax(); ay = ev.ay(); @@ -83,9 +112,11 @@ void User_state::handle_event(Input::Event ev) _pointer_pos = Point(ax, ay); + bool const drag = _key_cnt > 0; + /* count keys */ - if (type == Event::PRESS) Mode::inc_key_cnt(); - if (type == Event::RELEASE && Mode::drag()) Mode::dec_key_cnt(); + if (type == Event::PRESS) _key_cnt++; + if (type == Event::RELEASE && drag) _key_cnt--; /* track key states */ if (type == Event::PRESS) { @@ -100,72 +131,52 @@ void User_state::handle_event(Input::Event ev) _key_array.pressed(keycode, false); } - View const * const pointed_view = find_view(_pointer_pos); - ::Session * const pointed_session = pointed_view ? &pointed_view->session() : 0; + View_component const * const pointed_view = _view_stack.find_view(_pointer_pos); + + View_owner * const hovered = pointed_view ? &pointed_view->owner() : 0; /* * Deliver a leave event if pointed-to session changed */ - if (_pointed_session && (pointed_session != _pointed_session)) { + if (_hovered && (hovered != _hovered)) { Input::Event leave_ev(Input::Event::LEAVE, 0, ax, ay, 0, 0); - _pointed_session->submit_input_event(leave_ev); + _hovered->submit_input_event(leave_ev); } - _pointed_session = pointed_session; - - /** - * Guard that, when 'enabled' is set to true, performs a whole-screen - * update when leaving the scope - */ - struct Update_all_guard - { - User_state &user_state; - bool update = false; - - Update_all_guard(User_state &user_state) - : user_state(user_state) { } - - ~Update_all_guard() - { - if (update) - user_state._update_all(); - } - } update_all_guard(*this); + _hovered = hovered; /* * Handle start of a key sequence */ - if (type == Event::PRESS && (Mode::key_cnt() == 1)) { + if (type == Event::PRESS && (_key_cnt == 1)) { - ::Session *global_receiver = nullptr; + View_owner *global_receiver = nullptr; /* update focused session */ if (_mouse_button(keycode) - && _pointed_session - && (_pointed_session != Mode::focused_session()) - && (_pointed_session->has_focusable_domain() - || _pointed_session->has_same_domain(Mode::focused_session()))) { - - update_all_guard.update = true; + && _hovered + && (_hovered != _focused) + && (_hovered->has_focusable_domain() + || _hovered->has_same_domain(_focused))) { /* * Notify both the old focused session and the new one. */ - if (Mode::focused_session()) { + if (_focused) { Input::Event unfocus_ev(Input::Event::FOCUS, 0, ax, ay, 0, 0); - Mode::focused_session()->submit_input_event(unfocus_ev); + _focused->submit_input_event(unfocus_ev); } - if (_pointed_session) { + if (_hovered) { Input::Event focus_ev(Input::Event::FOCUS, 1, ax, ay, 0, 0); - _pointed_session->submit_input_event(focus_ev); + _hovered->submit_input_event(focus_ev); } - if (_pointed_session->has_transient_focusable_domain()) - global_receiver = _pointed_session; + if (_hovered->has_transient_focusable_domain()) + global_receiver = _hovered; else - focused_session(_pointed_session); + _focus_view_owner_via_click(*_hovered); } /* @@ -182,18 +193,16 @@ void User_state::handle_event(Input::Event ev) global_receiver = _global_keys.global_receiver(keycode); if (global_receiver) { - _global_key_sequence = true; - _input_receiver = global_receiver; - update_all_guard.update = true; + _global_key_sequence = true; + _input_receiver = global_receiver; } /* * No global rule matched, so the input stream gets directed to the * focused session or refers to a built-in operation. */ - if (!global_receiver) { - _input_receiver = Mode::focused_session(); - } + if (!global_receiver) + _input_receiver = _focused; } /* @@ -201,18 +210,18 @@ void User_state::handle_event(Input::Event ev) */ if (type == Event::MOTION || type == Event::WHEEL || type == Event::TOUCH) { - if (Mode::key_cnt() == 0) { + if (_key_cnt == 0) { - if (_pointed_session) { + if (_hovered) { /* * Unless the domain of the pointed session is configured to * always receive hover events, we deliver motion events only * to the focused domain. */ - if (_pointed_session->hover_always() - || _pointed_session->has_same_domain(Mode::focused_session())) - _pointed_session->submit_input_event(ev); + if (_hovered->hover_always() + || _hovered->has_same_domain(_focused)) + _hovered->submit_input_event(ev); } } else if (_input_receiver) @@ -225,9 +234,9 @@ void User_state::handle_event(Input::Event ev) */ if ((type == Event::PRESS) && _input_receiver) { if (!_mouse_button(ev.keycode()) - || (_pointed_session - && (_pointed_session->has_focusable_domain() - || _pointed_session->has_same_domain(Mode::focused_session())))) + || (_hovered + && (_hovered->has_focusable_domain() + || _hovered->has_same_domain(_focused)))) _input_receiver->submit_input_event(ev); else _input_receiver = nullptr; @@ -245,46 +254,182 @@ void User_state::handle_event(Input::Event ev) /* * Detect end of global key sequence */ - if (ev.type() == Event::RELEASE && (Mode::key_cnt() == 0) && _global_key_sequence) { - - _input_receiver = Mode::focused_session(); - - update_all_guard.update = true; - + if (ev.type() == Event::RELEASE && (_key_cnt == 0) && _global_key_sequence) { + _input_receiver = _focused; _global_key_sequence = false; } } -void User_state::report_keystate(Genode::Xml_generator &xml) +User_state::Handle_input_result +User_state::handle_input_events(Input::Event const * const ev_buf, + unsigned const num_ev) { - xml.attribute("count", Mode::key_cnt()); + Point const old_pointer_pos = _pointer_pos; + View_owner * const old_hovered = _hovered; + View_owner const * const old_focused = _focused; + View_owner const * const old_input_receiver = _input_receiver; + + bool user_active = false; + + if (num_ev > 0) { + /* + * Take events from input event buffer, merge consecutive motion + * events, and pass result to the user state. + */ + for (unsigned src_ev_cnt = 0; src_ev_cnt < num_ev; src_ev_cnt++) { + + Input::Event const *e = &ev_buf[src_ev_cnt]; + Input::Event curr = *e; + + if (e->type() == Input::Event::MOTION) { + unsigned n = num_consecutive_events(e, num_ev - src_ev_cnt); + curr = merge_motion_events(e, n); + + /* skip merged events */ + src_ev_cnt += n - 1; + } + + /* + * If subsequential relative motion events are merged to + * a zero-motion event, drop it. Otherwise, it would be + * misinterpreted as absolute event pointing to (0, 0). + */ + if (e->relative_motion() && curr.rx() == 0 && curr.ry() == 0) + continue; + + /* + * If we detect a pressed key sometime during the event processing, + * we regard the user as active. This check captures the presence + * of press-release combinations within one batch of input events. + */ + user_active |= _key_pressed(); + + /* pass event to user state */ + _handle_input_event(curr); + } + } else { + /* + * Besides handling input events, 'user_state.handle_event()' also + * updates the pointed session, which might have changed by other + * means, for example view movement. + */ + _handle_input_event(Input::Event()); + } + + /* + * If at least one key is kept pressed, we regard the user as active. + */ + user_active |= _key_pressed(); + + bool key_state_affected = false; + for (unsigned i = 0; i < num_ev; i++) + key_state_affected |= (ev_buf[i].type() == Input::Event::PRESS) || + (ev_buf[i].type() == Input::Event::RELEASE); + + _apply_pending_focus_change(); + + return { + .pointer_position_changed = _pointer_pos != old_pointer_pos, + .hover_changed = _hovered != old_hovered, + .focus_changed = (_focused != old_focused) || + (_input_receiver != old_input_receiver), + .key_state_affected = key_state_affected, + .user_active = user_active, + .key_pressed = _key_pressed() + }; +} + + +void User_state::report_keystate(Xml_generator &xml) const +{ + xml.attribute("count", _key_cnt); _key_array.report_state(xml); } -/******************** - ** Mode interface ** - ********************/ - -void User_state::forget(::Session const &session) +void User_state::report_pointer_position(Xml_generator &xml) const { - Mode::forget(session); + xml.attribute("xpos", _pointer_pos.x()); + xml.attribute("ypos", _pointer_pos.y()); +} - if (_pointed_session == &session) { - View * const pointed_view = find_view(_pointer_pos); - _pointed_session = pointed_view ? &pointed_view->session() : nullptr; + +void User_state::report_hovered_view_owner(Xml_generator &xml) const +{ + if (_hovered) + _hovered->report(xml); +} + + +void User_state::report_focused_view_owner(Xml_generator &xml, bool active) const +{ + if (_focused) { + _focused->report(xml); + + if (active) xml.attribute("active", "yes"); + } +} + + +void User_state::forget(View_owner const &owner) +{ + _focus.forget(owner); + + if (&owner == _focused) _focused = nullptr; + if (&owner == _next_focused) _next_focused = nullptr; + + if (_hovered == &owner) { + View_component * const pointed_view = _view_stack.find_view(_pointer_pos); + _hovered = pointed_view ? &pointed_view->owner() : nullptr; } - if (_input_receiver == &session) + if (_input_receiver == &owner) _input_receiver = nullptr; } -void User_state::focused_session(::Session *session) +bool User_state::_focus_change_permitted(View_owner const &caller) const { - Mode::focused_session(session); + /* + * If no session is focused, we allow any client to assign it. This + * is useful for programs such as an initial login window that + * should receive input events without prior manual selection via + * the mouse. + * + * In principle, a client could steal the focus during time between + * a currently focused session gets closed and before the user + * manually picks a new session. However, in practice, the focus + * policy during application startup and exit is managed by a + * window manager that sits between nitpicker and the application. + */ + if (!_focused) + return true; + + /* + * Check if the currently focused session label belongs to a + * session subordinated to the caller, i.e., it originated from + * a child of the caller or from the same process. This is the + * case if the first part of the focused session label is + * identical to the caller's label. + */ + char const * const focused_label = _focused->label().string(); + char const * const caller_label = caller.label().string(); + + return strcmp(focused_label, caller_label, strlen(caller_label)) == 0; +} + + +void User_state::_focus_view_owner_via_click(View_owner &owner) +{ + _focus.assign(owner); + + _focused = &owner; + _next_focused = &owner; + _focused = &owner; + + _focus.assign(owner); if (!_global_key_sequence) - _input_receiver = session; + _input_receiver = &owner; } diff --git a/repos/os/src/server/nitpicker/user_state.h b/repos/os/src/server/nitpicker/user_state.h index 4a59edc5ce..35979c5383 100644 --- a/repos/os/src/server/nitpicker/user_state.h +++ b/repos/os/src/server/nitpicker/user_state.h @@ -20,35 +20,61 @@ #include -#include "mode.h" +#include "focus.h" #include "view_stack.h" #include "global_keys.h" -class User_state : public Mode, public View_stack +namespace Nitpicker { class User_state; } + + +class Nitpicker::User_state : public Focus_controller { private: /* + * Number of currently pressed keys. This counter is used to determine + * if the user is dragging an item. + */ + unsigned _key_cnt = 0; + + View_owner *_focused = nullptr; + + View_owner *_next_focused = nullptr; + + /* + * True while a global key sequence is processed + */ + bool _global_key_sequence = false; + + /** + * Input-focus information propagated to the view stack + */ + Focus &_focus; + + /** * Policy for the routing of global keys */ Global_keys &_global_keys; + /** + * View stack, used to determine the hovered view and pointer boundary + */ + View_stack &_view_stack; + /* * Current pointer position */ Point _pointer_pos; /* - * Currently pointed-at session + * Currently pointed-at view owner */ - Session *_pointed_session = nullptr; + View_owner *_hovered = nullptr; /* * Session that receives the current stream of input events */ - Session *_input_receiver = nullptr; - - void _update_all(); + View_owner *_input_receiver = nullptr; /** * Array for tracking the state of each key @@ -80,43 +106,98 @@ class User_state : public Mode, public View_stack } _key_array; + bool _focus_change_permitted(View_owner const &caller) const; + + void _focus_view_owner_via_click(View_owner &); + + void _handle_input_event(Input::Event); + + bool _key_pressed() const { return _key_cnt > 0; } + + /** + * Apply pending focus-change request that was issued during drag state + */ + void _apply_pending_focus_change() + { + /* + * Defer focus changes to a point where no drag operation is in + * flight because otherwise, the involved sessions would obtain + * inconsistent press and release events. However, focus changes + * during global key sequences are fine. + */ + if (_key_pressed() && !_global_key_sequence) + return; + + if (_focused != _next_focused) + _focused = _next_focused; + } + public: /** * Constructor - */ - User_state(Global_keys &, Area view_stack_size); - - /** - * Handle input event * - * This function controls the Nitpicker mode and the user state - * variables. + * \param focus exported focus information, to be consumed by the + * view stack to tailor its view drawing operations */ - void handle_event(Input::Event ev); - - void report_keystate(Genode::Xml_generator &); - - /** - * Accessors - */ - Point pointer_pos() { return _pointer_pos; } - Session *pointed_session() { return _pointed_session; } + User_state(Focus &focus, Global_keys &global_keys, View_stack &view_stack) + : + _focus(focus), _global_keys(global_keys), _view_stack(view_stack) + { } - /** - * (Re-)apply origin policy to all views - */ - void apply_origin_policy(View &pointer_origin) + /******************************** + ** Focus_controller interface ** + ********************************/ + + void focus_view_owner(View_owner const &caller, + View_owner &next_focused) override { - View_stack::apply_origin_policy(pointer_origin); + /* check permission by comparing session labels */ + if (!_focus_change_permitted(caller)) { + warning("unauthorized focus change requesed by ", caller.label()); + return; + } + + /* + * To avoid changing the focus in the middle of a drag operation, + * we cannot perform the focus change immediately. Instead, it + * comes into effect via the 'apply_pending_focus_change()' method + * called the next time when the user input is handled and no drag + * operation is in flight. + */ + _next_focused = &next_focused; } + + /**************************************** + ** Interface used by the main program ** + ****************************************/ + + struct Handle_input_result + { + bool const pointer_position_changed; + bool const hover_changed; + bool const focus_changed; + bool const key_state_affected; + bool const user_active; + bool const key_pressed; + }; + + Handle_input_result handle_input_events(Input::Event const *ev_buf, + unsigned num_ev); + /** - * Mode interface + * Discard all references to specified view owner */ - void forget(Session const &) override; - void focused_session(Session *) override; + void forget(View_owner const &); + + void report_keystate(Xml_generator &) const; + void report_pointer_position(Xml_generator &) const; + void report_hovered_view_owner(Xml_generator &) const; + void report_focused_view_owner(Xml_generator &, bool user_active) const; + + Point pointer_pos() { return _pointer_pos; } }; -#endif +#endif /* _USER_STATE_H_ */ diff --git a/repos/os/src/server/nitpicker/view.cc b/repos/os/src/server/nitpicker/view.cc deleted file mode 100644 index 612bf78492..0000000000 --- a/repos/os/src/server/nitpicker/view.cc +++ /dev/null @@ -1,146 +0,0 @@ -/* - * \brief Nitpicker view implementation - * \author Norman Feske - * \date 2006-08-09 - */ - -/* - * Copyright (C) 2006-2017 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. - */ - -#include - -#include -#include - -#include "view.h" -#include "clip_guard.h" -#include "session.h" -#include "draw_label.h" - - -/*************** - ** Utilities ** - ***************/ - -/** - * Draw rectangle - */ -static void draw_rect(Canvas_base &canvas, int x, int y, int w, int h, - Color color) -{ - canvas.draw_box(Rect(Point(x, y), Area(w, 1)), color); - canvas.draw_box(Rect(Point(x, y), Area(1, h)), color); - canvas.draw_box(Rect(Point(x + w - 1, y), Area(1, h)), color); - canvas.draw_box(Rect(Point(x, y + h - 1), Area(w, 1)), color); -} - - -/** - * Draw outlined frame with black outline color - */ -static void draw_frame(Canvas_base &canvas, Rect r, Color color, - int frame_size) -{ - /* draw frame around the view */ - int d = frame_size; - draw_rect(canvas, r.x1() - d, r.y1() - d, r.w() + 2*d, r.h() + 2*d, BLACK); - while (--d > 1) - draw_rect(canvas, r.x1() - d, r.y1() - d, r.w() + 2*d, r.h() + 2*d, color); - draw_rect(canvas, r.x1() - d, r.y1() - d, r.w() + 2*d, r.h() + 2*d, BLACK); -} - - -/********** - ** View ** - **********/ - -void View::title(const char *title) -{ - Genode::strncpy(_title, title, TITLE_LEN); - - /* calculate label size, the position is defined by the view stack */ - _label_rect = Rect(Point(0, 0), label_size(_session.label().string(), _title)); -} - - -void View::frame(Canvas_base &canvas, Mode const &mode) const -{ - if (!_session.label_visible()) - return; - - Rect const geometry = abs_geometry(); - - draw_frame(canvas, geometry, _session.color(), frame_size(mode)); -} - - -/** - * Return texture painter mode depending on nitpicker state and session policy - */ -static Texture_painter::Mode -texture_painter_mode(Mode const &mode, Session const &session) -{ - /* - * Tint view unless it belongs to a domain that is explicitly configured to - * display the raw client content or if belongs to the focused domain. - */ - if (session.content_client() || session.has_same_domain(mode.focused_session())) - return Texture_painter::SOLID; - - return Texture_painter::MIXED; -} - - -void View::draw(Canvas_base &canvas, Mode const &mode) const -{ - Texture_painter::Mode const op = texture_painter_mode(mode, _session); - - Rect const view_rect = abs_geometry(); - - /* - * The view content and label should never overdraw the - * frame of the view in non-flat Nitpicker modes. The frame - * is located outside the view area. By shrinking the - * clipping area to the view area, we protect the frame. - */ - Clip_guard clip_guard(canvas, view_rect); - - /* - * If the clipping area shrinked to zero, we do not process drawing - * operations. - */ - if (!canvas.clip().valid() || !&_session) return; - - if (tmp_fb) { - for (unsigned i = 0; i < 2; i++) { - canvas.draw_box(view_rect, Color(i*8,i*24,i*16*8)); - tmp_fb->refresh(0,0,1024,768); - } - } - - /* allow alpha blending only if the raw client content is enabled */ - bool allow_alpha = _session.content_client(); - - /* draw view content */ - Color const mix_color = Color(_session.color().r >> 1, - _session.color().g >> 1, - _session.color().b >> 1); - - if (_session.texture()) { - canvas.draw_texture(_buffer_off + view_rect.p1(), *_session.texture(), - op, mix_color, allow_alpha); - } else { - canvas.draw_box(view_rect, BLACK); - } - - if (!_session.label_visible()) return; - - /* draw label */ - Color const frame_color = _session.color(); - draw_label(canvas, _label_rect.p1(), _session.label().string(), WHITE, - _title, frame_color); -} diff --git a/repos/os/src/server/nitpicker/view.h b/repos/os/src/server/nitpicker/view.h deleted file mode 100644 index 7cbc6f41b1..0000000000 --- a/repos/os/src/server/nitpicker/view.h +++ /dev/null @@ -1,302 +0,0 @@ -/* - * \brief Nitpicker view interface - * \author Norman Feske - * \date 2006-08-08 - */ - -/* - * Copyright (C) 2006-2017 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 _VIEW_H_ -#define _VIEW_H_ - -/* Genode includes */ -#include -#include -#include -#include -#include - -/* local includes */ -#include "mode.h" -#include "session.h" - -class Buffer; - -#include -extern Framebuffer::Session *tmp_fb; - - -typedef Genode::Dirty_rect Dirty_rect; - - -/* - * For each buffer, there is a list of views that belong to this buffer. - */ -struct Same_buffer_list_elem : Genode::List::Element { }; - -/* - * The view stack holds a list of all visible view in stacking order. - */ -struct View_stack_elem : Genode::List::Element { }; - -/* - * Each session maintains a list of views owned by the session. - */ -struct Session_view_list_elem : Genode::List::Element { }; - -/* - * If a view has a parent, it is a list element of its parent view - */ -struct View_parent_elem : Genode::List::Element { }; - - -namespace Nitpicker { class View; } - - -/* - * We use view capabilities as mere tokens to pass views between sessions. - * There is no RPC interface associated with a view. - */ -struct Nitpicker::View { GENODE_RPC_INTERFACE(); }; - - -class View : public Same_buffer_list_elem, - public Session_view_list_elem, - public View_stack_elem, - public View_parent_elem, - public Genode::Weak_object, - public Genode::Rpc_object -{ - public: - - enum { TITLE_LEN = 32 }; /* max.characters of a title */ - - enum Transparent { NOT_TRANSPARENT = 0, TRANSPARENT = 1 }; - enum Background { NOT_BACKGROUND = 0, BACKGROUND = 1 }; - - private: - - 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]; - Dirty_rect _dirty_rect; - - Genode::List _children; - - /** - * Assign new parent - * - * Normally, the parent of a view is defined at the construction time - * of the view. However, if the domain origin changes at runtime, we - * need to dynamically re-assign the pointer origin as the parent. - */ - void _assign_parent(View *parent) - { - if (_parent == parent) - return; - - if (_parent) - _parent->remove_child(*this); - - _parent = parent; - - if (_parent) - _parent->add_child(*this); - } - - public: - - View(Session &session, Transparent transparent, - Background bg, View *parent) - : - _transparent(transparent), _background(bg), - _parent(parent), _session(session) - { title(""); } - - virtual ~View() - { - /* invalidate weak pointers to this object */ - lock_for_destruction(); - - /* break link to our parent */ - if (_parent) - _parent->remove_child(*this); - - /* break links to our children */ - while (View_parent_elem *e = _children.first()) { - static_cast(e)->dissolve_from_parent(); - _children.remove(e); - } - } - - /** - * Return absolute view position - */ - Point abs_position() const - { - return _parent ? _geometry.p1() + _parent->abs_position() - : _geometry.p1(); - } - - /** - * Return absolute view geometry - */ - Rect abs_geometry() const - { - return Rect(abs_position(), _geometry.area()); - } - - /** - * Break the relationship of a child view from its parent - * - * This function is called when a parent view gets destroyed. - */ - void dissolve_from_parent() - { - _parent = 0; - _geometry = Rect(); - } - - bool has_parent(View const &parent) const { return &parent == _parent; } - - void apply_origin_policy(View &pointer_origin) - { - if (session().origin_pointer() && !has_parent(pointer_origin)) - _assign_parent(&pointer_origin); - - if (!session().origin_pointer() && has_parent(pointer_origin)) - _assign_parent(0); - } - - Rect geometry() const { return _geometry; } - - void geometry(Rect geometry) { _geometry = geometry; } - - void add_child(View const &child) { _children.insert(&child); } - - void remove_child(View const &child) { _children.remove(&child); } - - template - void for_each_child(FN const &fn) { - for (View_parent_elem *e = _children.first(); e; e = e->next()) - fn(*static_cast(e)); - } - - template - void for_each_const_child(FN const &fn) const { - for (View_parent_elem const *e = _children.first(); e; e = e->next()) - fn(*static_cast(e)); - } - - /** - * Return thickness of frame that surrounds the view - */ - virtual int frame_size(Mode const &mode) const - { - if (!_session.label_visible()) return 0; - - return mode.focused(_session) ? 5 : 3; - } - - /** - * Draw view-surrounding frame on canvas - */ - virtual void frame(Canvas_base &canvas, Mode const &mode) const; - - /** - * Draw view on canvas - */ - virtual void draw(Canvas_base &canvas, Mode const &mode) const; - - /** - * Set view title - */ - void title(const char *title); - - /** - * Return successor in view stack - */ - View const *view_stack_next() const { - return static_cast(View_stack_elem::next()); } - - View *view_stack_next() { - return static_cast(View_stack_elem::next()); } - - /** - * Set view as background - * - * \param bg true if view is background - */ - void background(bool bg) { - _background = bg ? BACKGROUND : NOT_BACKGROUND; } - - /** - * Accessors - */ - Session &session() const { return _session; } - - bool belongs_to(Session const &session) const { return &session == &_session; } - bool same_session_as(View const &other) const { return &_session == &other._session; } - - bool top_level() const { return _parent == 0; } - bool transparent() const { return _transparent || _session.uses_alpha(); } - bool background() const { return _background; } - Rect label_rect() const { return _label_rect; } - bool uses_alpha() const { return _session.uses_alpha(); } - Point buffer_off() const { return _buffer_off; } - - char const *title() const { return _title; } - - void buffer_off(Point buffer_off) { _buffer_off = buffer_off; } - - void label_pos(Point pos) { _label_rect = Rect(pos, _label_rect.area()); } - - /** - * Return true if input at screen position 'p' refers to the view - */ - bool input_response_at(Point p, Mode const &mode) const - { - Rect const view_rect = abs_geometry(); - - /* check if point lies outside view geometry */ - if ((p.x() < view_rect.x1()) || (p.x() > view_rect.x2()) - || (p.y() < view_rect.y1()) || (p.y() > view_rect.y2())) - return false; - - /* if view uses an alpha channel, check the input mask */ - if (_session.content_client() && session().uses_alpha()) - return session().input_mask_at(p - view_rect.p1() - _buffer_off); - - return true; - } - - /** - * Mark part of view as dirty - * - * \param rect dirty rectangle in absolute coordinates - */ - void mark_as_dirty(Rect rect) { _dirty_rect.mark_as_dirty(rect); } - - /** - * Return dirty-rectangle information - */ - Dirty_rect dirty_rect() const { return _dirty_rect; } - - /** - * Reset dirty rectangle - */ - void mark_as_clean() { _dirty_rect = Dirty_rect(); } -}; - -#endif /* _VIEW_H_ */ diff --git a/repos/os/src/server/nitpicker/view_component.cc b/repos/os/src/server/nitpicker/view_component.cc new file mode 100644 index 0000000000..ab5bcd5572 --- /dev/null +++ b/repos/os/src/server/nitpicker/view_component.cc @@ -0,0 +1,190 @@ +/* + * \brief Nitpicker view implementation + * \author Norman Feske + * \date 2006-08-09 + */ + +/* + * Copyright (C) 2006-2017 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. + */ + +#include + +#include +#include + +#include "view_component.h" +#include "clip_guard.h" +#include "session_component.h" +#include "draw_label.h" + + +/*************** + ** Utilities ** + ***************/ + +namespace Nitpicker { + + /** + * Draw rectangle + */ + static void draw_rect(Canvas_base &canvas, int x, int y, int w, int h, + Color color) + { + canvas.draw_box(Rect(Point(x, y), Area(w, 1)), color); + canvas.draw_box(Rect(Point(x, y), Area(1, h)), color); + canvas.draw_box(Rect(Point(x + w - 1, y), Area(1, h)), color); + canvas.draw_box(Rect(Point(x, y + h - 1), Area(w, 1)), color); + } + + + /** + * Draw outlined frame with black outline color + */ + static void draw_frame(Canvas_base &canvas, Rect r, Color color, + int frame_size) + { + /* draw frame around the view */ + int d = frame_size; + draw_rect(canvas, r.x1() - d, r.y1() - d, r.w() + 2*d, r.h() + 2*d, black()); + while (--d > 1) + draw_rect(canvas, r.x1() - d, r.y1() - d, r.w() + 2*d, r.h() + 2*d, color); + draw_rect(canvas, r.x1() - d, r.y1() - d, r.w() + 2*d, r.h() + 2*d, black()); + } + + + /** + * Return texture painter mode depending on nitpicker state and session policy + */ + static Texture_painter::Mode + texture_painter_mode(Focus const &focus, View_owner const &owner) + { + /* + * Tint view unless it belongs to a domain that is explicitly configured to + * display the raw client content or if belongs to the focused domain. + */ + if (owner.content_client() || focus.same_domain_as_focused(owner)) + return Texture_painter::SOLID; + + return Texture_painter::MIXED; + } +} + +using namespace Nitpicker; + + +void View_component::title(Title const &title) +{ + _title = title; + + /* calculate label size, the position is defined by the view stack */ + _label_rect = Rect(Point(0, 0), label_size(_owner.label().string(), + _title.string())); +} + + +void View_component::frame(Canvas_base &canvas, Focus const &focus) const +{ + if (!_owner.label_visible()) + return; + + Rect const geometry = abs_geometry(); + + draw_frame(canvas, geometry, _owner.color(), frame_size(focus)); +} + + +void View_component::draw(Canvas_base &canvas, Focus const &focus) const +{ + Texture_painter::Mode const op = texture_painter_mode(focus, _owner); + + Rect const view_rect = abs_geometry(); + + /* + * The view content and label should never overdraw the + * frame of the view in non-flat Nitpicker modes. The frame + * is located outside the view area. By shrinking the + * clipping area to the view area, we protect the frame. + */ + Clip_guard clip_guard(canvas, view_rect); + + /* + * If the clipping area shrinked to zero, we do not process drawing + * operations. + */ + if (!canvas.clip().valid() || !&_owner) return; + + /* allow alpha blending only if the raw client content is enabled */ + bool allow_alpha = _owner.content_client(); + + /* draw view content */ + Color const owner_color = _owner.color(); + Color const mix_color = Color(owner_color.r >> 1, + owner_color.g >> 1, + owner_color.b >> 1); + + Texture_base const *texture = _owner.texture(); + if (texture) { + canvas.draw_texture(_buffer_off + view_rect.p1(), *texture, op, + mix_color, allow_alpha); + } else { + canvas.draw_box(view_rect, black()); + } + + if (!_owner.label_visible()) return; + + /* draw label */ + Color const frame_color = owner_color; + draw_label(canvas, _label_rect.p1(), _owner.label().string(), white(), + _title.string(), frame_color); +} + + +void View_component::apply_origin_policy(View_component &pointer_origin) +{ + if (owner().origin_pointer() && !has_parent(pointer_origin)) + _assign_parent(&pointer_origin); + + if (!owner().origin_pointer() && has_parent(pointer_origin)) + _assign_parent(0); +} + + +bool View_component::input_response_at(Point p) const +{ + Rect const view_rect = abs_geometry(); + + /* check if point lies outside view geometry */ + if ((p.x() < view_rect.x1()) || (p.x() > view_rect.x2()) + || (p.y() < view_rect.y1()) || (p.y() > view_rect.y2())) + return false; + + /* if view uses an alpha channel, check the input mask */ + if (_owner.content_client() && _owner.uses_alpha()) + return _owner.input_mask_at(p - view_rect.p1() - _buffer_off); + + return true; +} + + +int View_component::frame_size(Focus const &focus) const +{ + if (!_owner.label_visible()) return 0; + + return focus.focused(_owner) ? 5 : 3; +} + + +bool View_component::transparent() const +{ + return _transparent || _owner.uses_alpha(); +} + + +bool View_component::uses_alpha() const +{ + return _owner.uses_alpha(); +} diff --git a/repos/os/src/server/nitpicker/view_component.h b/repos/os/src/server/nitpicker/view_component.h new file mode 100644 index 0000000000..698383562f --- /dev/null +++ b/repos/os/src/server/nitpicker/view_component.h @@ -0,0 +1,283 @@ +/* + * \brief Nitpicker view interface + * \author Norman Feske + * \date 2006-08-08 + */ + +/* + * Copyright (C) 2006-2017 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 _VIEW_H_ +#define _VIEW_H_ + +/* Genode includes */ +#include +#include +#include +#include +#include + +/* local includes */ +#include "canvas.h" +#include "view_owner.h" + +namespace Nitpicker { + + class Buffer; + class Focus; + + typedef Dirty_rect Dirty_rect; + + /* + * For each buffer, there is a list of views that belong to this buffer. + */ + struct Same_buffer_list_elem : List::Element { }; + + /* + * The view stack holds a list of all visible view in stacking order. + */ + struct View_stack_elem : List::Element { }; + + /* + * If a view has a parent, it is a list element of its parent view + */ + struct View_parent_elem : List::Element { }; + + /* + * Each session maintains a list of views owned by the session. + */ + struct Session_view_list_elem : List::Element { }; + + /* + * We use view capabilities as mere tokens to pass views between sessions. + * There is no RPC interface associated with a view. + */ + struct View { GENODE_RPC_INTERFACE(); }; + + class View_component; +} + + +class Nitpicker::View_component : public Same_buffer_list_elem, + public Session_view_list_elem, + public View_stack_elem, + public View_parent_elem, + public Weak_object, + public Rpc_object +{ + public: + + typedef String<32> Title; + + enum Transparent { NOT_TRANSPARENT = 0, TRANSPARENT = 1 }; + enum Background { NOT_BACKGROUND = 0, BACKGROUND = 1 }; + + private: + + Transparent const _transparent; /* background is partly visible */ + Background _background; /* view is a background view */ + + View_component *_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 */ + View_owner &_owner; + Title _title; + Dirty_rect _dirty_rect; + + List _children; + + /** + * Assign new parent + * + * Normally, the parent of a view is defined at the construction time + * of the view. However, if the domain origin changes at runtime, we + * need to dynamically re-assign the pointer origin as the parent. + */ + void _assign_parent(View_component *parent) + { + if (_parent == parent) + return; + + if (_parent) + _parent->remove_child(*this); + + _parent = parent; + + if (_parent) + _parent->add_child(*this); + } + + public: + + View_component(View_owner &owner, Transparent transparent, + Background bg, View_component *parent) + : + _transparent(transparent), _background(bg), _parent(parent), + _owner(owner) + { + title(""); /* initialize '_label_rect' */ + } + + virtual ~View_component() + { + /* invalidate weak pointers to this object */ + lock_for_destruction(); + + /* break link to our parent */ + if (_parent) + _parent->remove_child(*this); + + /* break links to our children */ + while (View_parent_elem *e = _children.first()) { + static_cast(e)->dissolve_from_parent(); + _children.remove(e); + } + } + + /** + * Return absolute view position + */ + Point abs_position() const + { + return _parent ? _geometry.p1() + _parent->abs_position() + : _geometry.p1(); + } + + /** + * Return absolute view geometry + */ + Rect abs_geometry() const + { + return Rect(abs_position(), _geometry.area()); + } + + /** + * Break the relationship of a child view from its parent + * + * This function is called when a parent view gets destroyed. + */ + void dissolve_from_parent() + { + _parent = 0; + _geometry = Rect(); + } + + bool has_parent(View_component const &parent) const { return &parent == _parent; } + + void apply_origin_policy(View_component &pointer_origin); + + Rect geometry() const { return _geometry; } + + void geometry(Rect geometry) { _geometry = geometry; } + + void add_child(View_component const &child) { _children.insert(&child); } + + void remove_child(View_component const &child) { _children.remove(&child); } + + template + void for_each_child(FN const &fn) { + for (View_parent_elem *e = _children.first(); e; e = e->next()) + fn(*static_cast(e)); + } + + template + void for_each_const_child(FN const &fn) const { + for (View_parent_elem const *e = _children.first(); e; e = e->next()) + fn(*static_cast(e)); + } + + /** + * Return thickness of frame that surrounds the view + */ + virtual int frame_size(Focus const &) const; + + /** + * Draw view-surrounding frame on canvas + */ + virtual void frame(Canvas_base &canvas, Focus const &) const; + + /** + * Draw view on canvas + */ + virtual void draw(Canvas_base &canvas, Focus const &) const; + + /** + * Set view title + */ + void title(Title const &title); + + /** + * Return successor in view stack + */ + View_component const *view_stack_next() const { + return static_cast(View_stack_elem::next()); } + + View_component *view_stack_next() { + return static_cast(View_stack_elem::next()); } + + /** + * Set view as background + * + * \param bg true if view is background + */ + void background(bool bg) { + _background = bg ? BACKGROUND : NOT_BACKGROUND; } + + /** + * Accessors + */ + View_owner &owner() const { return _owner; } + + bool owned_by(View_owner const &owner) const + { + return &owner == &_owner; + } + + bool same_owner_as(View_component const &other) const + { + return &_owner == &other._owner; + } + + bool top_level() const { return _parent == 0; } + bool transparent() const; + bool background() const { return _background; } + Rect label_rect() const { return _label_rect; } + bool uses_alpha() const; + Point buffer_off() const { return _buffer_off; } + + template + void with_title(FN const &fn) const { fn(_title); } + + void buffer_off(Point buffer_off) { _buffer_off = buffer_off; } + + void label_pos(Point pos) { _label_rect = Rect(pos, _label_rect.area()); } + + /** + * Return true if input at screen position 'p' refers to the view + */ + bool input_response_at(Point p) const; + + /** + * Mark part of view as dirty + * + * \param rect dirty rectangle in absolute coordinates + */ + void mark_as_dirty(Rect rect) { _dirty_rect.mark_as_dirty(rect); } + + /** + * Return dirty-rectangle information + */ + Dirty_rect dirty_rect() const { return _dirty_rect; } + + /** + * Reset dirty rectangle + */ + void mark_as_clean() { _dirty_rect = Dirty_rect(); } +}; + +#endif /* _VIEW_H_ */ diff --git a/repos/os/src/server/nitpicker/view_owner.h b/repos/os/src/server/nitpicker/view_owner.h new file mode 100644 index 0000000000..017f853f0b --- /dev/null +++ b/repos/os/src/server/nitpicker/view_owner.h @@ -0,0 +1,98 @@ +/* + * \brief Representation of a view owner + * \author Norman Feske + * \date 2017-11-16 + * + * The view owner defines the policy when drawing or interacting with a + * view. Except for the background and pointer-origin views that are + * owned by nitpicker, the view owner corresponds to the session that + * created the view. + */ + +/* + * Copyright (C) 2006-2017 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 _VIEW_OWNER_H_ +#define _VIEW_OWNER_H_ + +/* Genode includes */ +#include +#include +#include +#include + +/* local includes */ +#include "types.h" + + +namespace Nitpicker { class View_owner; } + + +struct Nitpicker::View_owner +{ + /** + * Return the owner's session label + */ + virtual Session::Label label() const { return Session::Label(""); } + + virtual bool matches_session_label(Session::Label const &) const { return false; } + + virtual bool visible() const { return true; } + + virtual bool label_visible() const { return false; } + + virtual bool has_same_domain(View_owner const *) const { return false; } + + virtual bool has_focusable_domain() const { return false; } + + virtual bool has_transient_focusable_domain() const { return false; } + + virtual Color color() const { return black(); } + + virtual bool content_client() const { return true; } + + virtual bool hover_always() const { return false; } + + /** + * Return true if owner uses an alpha channel + */ + virtual bool uses_alpha() const { return false; } + + /** + * Return layer assigned to the owner's domain + */ + virtual unsigned layer() const { return ~0U; } + + /** + * Return true if the owner uses the pointer as coordinate origin + */ + virtual bool origin_pointer() const { return false; } + + /** + * Return the owner's designated background view + */ + virtual View const *background() const { return nullptr; } + + /** + * Teturn texture containing the owners virtual frame buffer + */ + virtual Texture_base const *texture() const { return nullptr; } + + /** + * Return input-mask value at given position + */ + virtual unsigned char input_mask_at(Point p) const { return 0; } + + virtual void submit_input_event(Input::Event ev) { } + + /** + * Produce report with the owner's information + */ + virtual void report(Xml_generator &xml) const { } +}; + +#endif /* _VIEW_OWNER_H_ */ diff --git a/repos/os/src/server/nitpicker/view_stack.cc b/repos/os/src/server/nitpicker/view_stack.cc index 03d49dffbe..0eaedcb7be 100644 --- a/repos/os/src/server/nitpicker/view_stack.cc +++ b/repos/os/src/server/nitpicker/view_stack.cc @@ -14,19 +14,12 @@ #include "view_stack.h" #include "clip_guard.h" +using namespace Nitpicker; -/************************** - ** View stack interface ** - **************************/ template VIEW *View_stack::_next_view(VIEW &view) const { - Session * const focused_session = _mode.focused_session(); - - View * const active_background = focused_session ? - focused_session->background() : 0; - for (VIEW *next_view = &view; ;) { next_view = next_view->view_stack_next(); @@ -34,11 +27,11 @@ VIEW *View_stack::_next_view(VIEW &view) const /* check if we hit the bottom of the view stack */ if (!next_view) return 0; - if (!next_view->session().visible()) continue; + if (!next_view->owner().visible()) continue; if (!next_view->background()) return next_view; - if (is_default_background(*next_view) || next_view == active_background) + if (is_default_background(*next_view) || _focus.focused_background(*next_view)) return next_view; /* view is a background view belonging to a non-focused session */ @@ -47,19 +40,19 @@ VIEW *View_stack::_next_view(VIEW &view) const } -Rect View_stack::_outline(View const &view) const +Nitpicker::Rect View_stack::_outline(View_component const &view) const { Rect const rect = view.abs_geometry(); /* request thickness of view frame */ - int const frame_size = view.frame_size(_mode); + int const frame_size = view.frame_size(_focus); return Rect(Point(rect.x1() - frame_size, rect.y1() - frame_size), Point(rect.x2() + frame_size, rect.y2() + frame_size)); } -View const *View_stack::_target_stack_position(View const *neighbor, bool behind) +View_component const *View_stack::_target_stack_position(View_component const *neighbor, bool behind) { if (behind) { @@ -67,7 +60,7 @@ View const *View_stack::_target_stack_position(View const *neighbor, bool behind return nullptr; /* find target position behind neighbor */ - for (View const *cv = _first_view(); cv; cv = _next_view(*cv)) + for (View_component const *cv = _first_view(); cv; cv = _next_view(*cv)) if (cv == neighbor) return cv; @@ -77,7 +70,7 @@ View const *View_stack::_target_stack_position(View const *neighbor, bool behind return nullptr; /* find target position in front of neighbor */ - for (View const *cv = _first_view(), *next = nullptr; cv; cv = next) { + for (View_component const *cv = _first_view(), *next = nullptr; cv; cv = next) { next = _next_view(*cv); if (!next || next == neighbor || next->background()) @@ -90,7 +83,8 @@ View const *View_stack::_target_stack_position(View const *neighbor, bool behind } -void View_stack::_optimize_label_rec(View const *cv, View const *lv, Rect rect, Rect *optimal) +void View_stack::_optimize_label_rec(View_component const *cv, View_component const *lv, + Rect rect, Rect *optimal) { /* if label already fits in optimized rectangle, we are happy */ if (optimal->fits(lv->label_rect().area())) @@ -142,8 +136,8 @@ void View_stack::_place_labels(Rect rect) */ /* ignore mouse cursor */ - View const *start = _next_view(*_first_view()); - View *view = _next_view(*_first_view()); + View_component const *start = _next_view(*_first_view()); + View_component *view = _next_view(*_first_view()); for (; view && _next_view(*view); view = _next_view(*view)) { @@ -174,7 +168,7 @@ void View_stack::_place_labels(Rect rect) } -void View_stack::draw_rec(Canvas_base &canvas, View const *view, Rect rect) const +void View_stack::draw_rec(Canvas_base &canvas, View_component const *view, Rect rect) const { Rect clipped; @@ -188,7 +182,7 @@ void View_stack::draw_rec(Canvas_base &canvas, View const *view, Rect rect) cons Rect top, left, right, bottom; rect.cut(clipped, &top, &left, &right, &bottom); - View const *next = _next_view(*view); + View_component const *next = _next_view(*view); /* draw areas at the top/left of the current view */ if (next && top.valid()) draw_rec(canvas, next, top); @@ -203,8 +197,8 @@ void View_stack::draw_rec(Canvas_base &canvas, View const *view, Rect rect) cons if (view->uses_alpha()) draw_rec(canvas, _next_view(*view), clipped); - view->frame(canvas, _mode); - view->draw(canvas, _mode); + view->frame(canvas, _focus); + view->draw(canvas, _focus); }); /* draw areas at the bottom/right of the current view */ @@ -213,12 +207,12 @@ void View_stack::draw_rec(Canvas_base &canvas, View const *view, Rect rect) cons } -void View_stack::refresh_view(View &view, Rect const rect) +void View_stack::refresh_view(View_component &view, Rect const rect) { /* rectangle constrained to view geometry */ Rect const view_rect = Rect::intersect(rect, _outline(view)); - for (View *v = _first_view(); v; v = v->view_stack_next()) { + for (View_component *v = _first_view(); v; v = v->view_stack_next()) { Rect const intersection = Rect::intersect(view_rect, _outline(*v)); @@ -226,13 +220,13 @@ void View_stack::refresh_view(View &view, Rect const rect) _mark_view_as_dirty(*v, intersection); } - view.for_each_child([&] (View &child) { refresh_view(child, rect); }); + view.for_each_child([&] (View_component &child) { refresh_view(child, rect); }); } void View_stack::refresh(Rect const rect) { - for (View *v = _first_view(); v; v = v->view_stack_next()) { + for (View_component *v = _first_view(); v; v = v->view_stack_next()) { Rect const intersection = Rect::intersect(rect, _outline(*v)); @@ -242,7 +236,7 @@ void View_stack::refresh(Rect const rect) } -void View_stack::geometry(View &view, Rect const rect) +void View_stack::geometry(View_component &view, Rect const rect) { Rect const old_outline = _outline(view); @@ -269,7 +263,7 @@ void View_stack::geometry(View &view, Rect const rect) } -void View_stack::buffer_offset(View &view, Point const buffer_off) +void View_stack::buffer_offset(View_component &view, Point const buffer_off) { view.buffer_off(buffer_off); @@ -277,7 +271,7 @@ void View_stack::buffer_offset(View &view, Point const buffer_off) } -void View_stack::stack(View &view, View const *neighbor, bool behind) +void View_stack::stack(View_component &view, View_component const *neighbor, bool behind) { _views.remove(&view); _views.insert(&view, _target_stack_position(neighbor, behind)); @@ -291,7 +285,7 @@ void View_stack::stack(View &view, View const *neighbor, bool behind) } -void View_stack::title(View &view, const char *title) +void View_stack::title(View_component &view, const char *title) { view.title(title); _place_labels(view.abs_geometry()); @@ -300,21 +294,21 @@ void View_stack::title(View &view, const char *title) } -View *View_stack::find_view(Point p) +View_component *View_stack::find_view(Point p) { - View *view = _first_view(); + View_component *view = _first_view(); for ( ; view; view = _next_view(*view)) - if (view->input_response_at(p, _mode)) + if (view->input_response_at(p)) return view; - return 0; + return nullptr; } -void View_stack::remove_view(View const &view, bool redraw) +void View_stack::remove_view(View_component const &view, bool redraw) { - view.for_each_const_child([&] (View const &child) { remove_view(child); }); + view.for_each_const_child([&] (View_component const &child) { remove_view(child); }); /* remember geometry of view to remove */ Rect rect = _outline(view); @@ -339,7 +333,7 @@ void View_stack::sort_views_by_layer() unsigned lowest_layer = ~0U; View_stack_elem *lowest_view = nullptr; for (View_stack_elem *v = _views.first(); v; v = v->next()) { - unsigned const layer = static_cast(v)->session().layer(); + unsigned const layer = static_cast(v)->owner().layer(); if (layer < lowest_layer) { lowest_layer = layer; lowest_view = v; @@ -361,3 +355,37 @@ void View_stack::sort_views_by_layer() /* replace empty source list by newly sorted list */ _views = sorted; } + + +void View_stack::to_front(char const *selector) +{ + /* + * Move all views that match the selector to the front while + * maintaining their ordering. + */ + View_component *at = nullptr; + for (View_component *v = _first_view(); v; v = v->view_stack_next()) { + + if (!v->owner().matches_session_label(selector)) + continue; + + if (v->background()) + continue; + + /* + * Move view to behind the previous view that we moved to + * front. If 'v' is the first view that matches the selector, + * move it to the front ('at' argument of 'insert' is 0). + */ + _views.remove(v); + _views.insert(v, at); + + at = v; + + /* mark view geometry as to be redrawn */ + refresh(_outline(*v)); + } + + /* reestablish domain layering */ + sort_views_by_layer(); +} diff --git a/repos/os/src/server/nitpicker/view_stack.h b/repos/os/src/server/nitpicker/view_stack.h index 52b0c3b208..2cde3bfe92 100644 --- a/repos/os/src/server/nitpicker/view_stack.h +++ b/repos/os/src/server/nitpicker/view_stack.h @@ -14,21 +14,22 @@ #ifndef _VIEW_STACK_H_ #define _VIEW_STACK_H_ -#include "view.h" +#include "view_component.h" +#include "session_component.h" #include "canvas.h" -class Session; +namespace Nitpicker { class View_stack; } -class View_stack +class Nitpicker::View_stack { private: - Area _size; - Mode &_mode; - Genode::List _views; - View *_default_background = nullptr; - Dirty_rect mutable _dirty_rect; + Area _size; + Focus &_focus; + List _views; + View_component *_default_background = nullptr; + Dirty_rect mutable _dirty_rect; /** * Return outline geometry of a view @@ -37,24 +38,31 @@ class View_stack * Nitpicker mode. In non-flat modes, we incorporate the surrounding * frame. */ - Rect _outline(View const &view) const; + Rect _outline(View_component const &view) const; /** * Return top-most view of the view stack */ - View const *_first_view_const() const { return static_cast(_views.first()); } + View_component const *_first_view() const + { + return static_cast(_views.first()); + } - View *_first_view() { return static_cast(_views.first()); } + View_component *_first_view() + { + return static_cast(_views.first()); + } /** * Find position in view stack for inserting a view */ - View const *_target_stack_position(View const *neighbor, bool behind); + View_component const *_target_stack_position(View_component const *neighbor, bool behind); /** * Find best visible label position */ - void _optimize_label_rec(View const *cv, View const *lv, Rect rect, Rect *optimal); + void _optimize_label_rec(View_component const *cv, View_component const *lv, + Rect rect, Rect *optimal); /** * Position labels that are affected by specified area @@ -73,7 +81,7 @@ class View_stack /** * Schedule 'rect' to be redrawn */ - void _mark_view_as_dirty(View &view, Rect rect) + void _mark_view_as_dirty(View_component &view, Rect rect) { _dirty_rect.mark_as_dirty(rect); @@ -85,7 +93,7 @@ class View_stack /** * Constructor */ - View_stack(Area size, Mode &mode) : _size(size), _mode(mode) + View_stack(Area size, Focus &focus) : _size(size), _focus(focus) { _dirty_rect.mark_as_dirty(Rect(Point(0, 0), _size)); } @@ -107,7 +115,7 @@ class View_stack * * \param view current view in view stack */ - void draw_rec(Canvas_base &, View const *view, Rect) const; + void draw_rec(Canvas_base &, View_component const *view, Rect) const; /** * Draw dirty areas @@ -117,7 +125,7 @@ class View_stack Dirty_rect result = _dirty_rect; _dirty_rect.flush([&] (Rect const &rect) { - draw_rec(canvas, _first_view_const(), rect); }); + draw_rec(canvas, _first_view(), rect); }); return result; } @@ -132,9 +140,8 @@ class View_stack _place_labels(whole_screen); _dirty_rect.mark_as_dirty(whole_screen); - for (View *view = _first_view(); view; view = view->view_stack_next()) + for (View_component *view = _first_view(); view; view = view->view_stack_next()) view->mark_as_dirty(_outline(*view)); - } /** @@ -142,7 +149,7 @@ class View_stack */ void mark_all_views_as_clean() { - for (View *view = _first_view(); view; view = view->view_stack_next()) + for (View_component *view = _first_view(); view; view = view->view_stack_next()) view->mark_as_clean(); } @@ -152,11 +159,11 @@ class View_stack * \param Session Session that created the view * \param Rect Buffer area to update */ - void mark_session_views_as_dirty(Session const &session, Rect rect) + void mark_session_views_as_dirty(Session_component const &session, Rect rect) { - for (View *view = _first_view(); view; view = view->view_stack_next()) { + for (View_component *view = _first_view(); view; view = view->view_stack_next()) { - if (!view->belongs_to(session)) + if (!view->owned_by(session)) continue; /* @@ -177,12 +184,12 @@ class View_stack * * \param view view that should be updated on screen */ - void refresh_view(View &view, Rect); + void refresh_view(View_component &view, Rect); /** * Refresh entire view */ - void refresh_view(View &view) { refresh_view(view, _outline(view)); } + void refresh_view(View_component &view) { refresh_view(view, _outline(view)); } /** * Refresh area @@ -194,14 +201,14 @@ class View_stack * * \param rect new geometry of view on screen */ - void geometry(View &view, Rect rect); + void geometry(View_component &view, Rect rect); /** * Define buffer offset of view * * \param buffer_off view offset of displayed buffer */ - void buffer_offset(View &view, Point buffer_off); + void buffer_offset(View_component &view, Point buffer_off); /** * Insert view at specified position in view stack @@ -214,94 +221,49 @@ class View_stack * bottom of the view stack, specify neighbor = 0 and * behind = false. */ - void stack(View &view, View const *neighbor = 0, bool behind = true); + void stack(View_component &view, View_component const *neighbor = 0, + bool behind = true); /** * Set view title */ - void title(View &view, char const *title); + void title(View_component &view, char const *title); /** * Find view at specified position */ - View *find_view(Point); + View_component *find_view(Point); /** * Remove view from view stack */ - void remove_view(View const &, bool redraw = true); + void remove_view(View_component const &, bool redraw = true); /** * Define default background */ - void default_background(View &view) { _default_background = &view; } + void default_background(View_component &view) { _default_background = &view; } /** * Return true if view is the default background */ - bool is_default_background(View const &view) const { return &view == _default_background; } - - void apply_origin_policy(View &pointer_origin) + bool is_default_background(View_component const &view) const { - for (View *v = _first_view(); v; v = v->view_stack_next()) + return &view == _default_background; + } + + void apply_origin_policy(View_component &pointer_origin) + { + for (View_component *v = _first_view(); v; v = v->view_stack_next()) v->apply_origin_policy(pointer_origin); } void sort_views_by_layer(); - /** - * Set visibility of views that match the specified label selector - */ - void visible(char const *selector, bool visible) - { - for (View *v = _first_view(); v; v = v->view_stack_next()) { - - if (!v->session().matches_session_label(selector)) - continue; - - /* mark view geometry as to be redrawn */ - refresh(_outline(*v)); - - /* let the view stack omit the views of the session */ - v->session().visible(visible); - } - } - /** * Bring views that match the specified label selector to the front */ - void to_front(char const *selector) - { - /* - * Move all views that match the selector to the front while - * maintaining their ordering. - */ - View *at = nullptr; - for (View *v = _first_view(); v; v = v->view_stack_next()) { - - if (!v->session().matches_session_label(selector)) - continue; - - if (v->background()) - continue; - - /* - * Move view to behind the previous view that we moved to - * front. If 'v' is the first view that matches the selector, - * move it to the front ('at' argument of 'insert' is 0). - */ - _views.remove(v); - _views.insert(v, at); - - at = v; - - /* mark view geometry as to be redrawn */ - refresh(_outline(*v)); - } - - /* reestablish domain layering */ - sort_views_by_layer(); - } + void to_front(char const *selector); }; -#endif +#endif /* _VIEW_STACK_H_ */