Window decorator that can be styled

This commit is contained in:
Norman Feske 2015-11-11 16:54:08 +01:00 committed by Christian Helmuth
parent 2dde77f62c
commit 4b9e1f1060
13 changed files with 1413 additions and 0 deletions

View File

@ -0,0 +1,53 @@
/*
* \brief Decorator configuration handling
* \author Norman Feske
* \date 2015-09-17
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _CONFIG_H_
#define _CONFIG_H_
/* Genode includes */
#include <os/session_policy.h>
#include <util/color.h>
/* decorator includes */
#include <decorator/types.h>
namespace Decorator {
class Config;
typedef Genode::String<200> Window_title;
}
class Decorator::Config
{
public:
/**
* Return the base color of the window with the specified title
*/
Color base_color(Window_title const &title) const
{
Color result(0, 0, 0);
try {
Genode::Session_policy policy(title);
result = policy.attribute_value("color", result);
} catch (Genode::Session_policy::No_policy_defined) { }
return result;
}
};
#endif /* _CONFIG_H_ */

View File

@ -0,0 +1,304 @@
/*
* \brief Window decorator that can be styled
* \author Norman Feske
* \date 2015-11-11
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <base/printf.h>
#include <base/signal.h>
#include <os/attached_rom_dataspace.h>
#include <os/reporter.h>
#include <os/server.h>
/* decorator includes */
#include <decorator/window_stack.h>
#include <decorator/xml_utils.h>
/* local includes */
#include "window.h"
namespace Decorator {
using namespace Genode;
struct Main;
}
struct Decorator::Main : Window_factory_base
{
Server::Entrypoint &ep;
Window_stack window_stack = { *this };
/**
* Install handler for responding to window-layout changes
*/
void handle_window_layout_update(unsigned);
Signal_rpc_member<Main> window_layout_dispatcher = {
ep, *this, &Main::handle_window_layout_update };
Attached_rom_dataspace window_layout { "window_layout" };
/**
* Install handler for responding to pointer-position updates
*/
void handle_pointer_update(unsigned);
Signal_rpc_member<Main> pointer_dispatcher = {
ep, *this, &Main::handle_pointer_update };
Attached_rom_dataspace pointer { "pointer" };
Window_base::Hover hover;
Reporter hover_reporter = { "hover" };
/**
* Nitpicker connection used to sync animations
*/
Nitpicker::Connection nitpicker;
bool window_layout_update_needed = false;
Animator animator;
Theme theme { *Genode::env()->heap() };
/**
* Process the update every 'frame_period' nitpicker sync signals. The
* 'frame_cnt' holds the counter of the nitpicker sync signals.
*
* A lower 'frame_period' value makes the decorations more responsive
* but it also puts more load on the system.
*
* If the nitpicker sync signal fires every 10 milliseconds, a
* 'frame_period' of 2 results in an update rate of 1000/20 = 50 frames per
* second.
*/
unsigned frame_cnt = 0;
unsigned frame_period = 2;
/**
* Install handler for responding to nitpicker sync events
*/
void handle_nitpicker_sync(unsigned);
Signal_rpc_member<Main> nitpicker_sync_dispatcher = {
ep, *this, &Main::handle_nitpicker_sync };
Config config;
void handle_config(unsigned);
Signal_rpc_member<Main> config_dispatcher = {
ep, *this, &Main::handle_config};
/**
* Constructor
*/
Main(Server::Entrypoint &ep) : ep(ep)
{
/*
* Eagerly upgrade the session quota in order to be able to create a
* high amount of view handles.
*
* XXX Consider upgrading the session quota on demand by responding
* to Out_of_metadata exceptions raised by the create_view
* and view_handle operations. Currently, these exceptions will
* abort the decorator.
*/
Genode::env()->parent()->upgrade(nitpicker, "ram_quota=256K");
Genode::config()->sigh(config_dispatcher);
handle_config(0);
window_layout.sigh(window_layout_dispatcher);
pointer.sigh(pointer_dispatcher);
nitpicker.framebuffer()->sync_sigh(nitpicker_sync_dispatcher);
hover_reporter.enabled(true);
/* import initial state */
handle_pointer_update(0);
handle_window_layout_update(0);
}
/**
* Window_factory_base interface
*/
Window_base *create(Xml_node window_node) override
{
return new (env()->heap())
Window(attribute(window_node, "id", 0UL), nitpicker, animator,
*env()->ram_session(), theme, config);
}
/**
* Window_factory_base interface
*/
void destroy(Window_base *window) override
{
Genode::destroy(env()->heap(), static_cast<Window *>(window));
}
};
void Decorator::Main::handle_config(unsigned)
{
Genode::config()->reload();
/* notify all windows to consider the updated policy */
window_stack.for_each_window([&] (Window_base &window) {
static_cast<Window &>(window).adapt_to_changed_config(); });
/* trigger redraw of the window stack */
handle_window_layout_update(0);
}
static Decorator::Window_base::Hover
find_hover(Genode::Xml_node pointer_node, Decorator::Window_stack &window_stack)
{
if (!pointer_node.has_attribute("xpos")
|| !pointer_node.has_attribute("ypos"))
return Decorator::Window_base::Hover();
return window_stack.hover(Decorator::point_attribute(pointer_node));
}
static void update_hover_report(Genode::Xml_node pointer_node,
Decorator::Window_stack &window_stack,
Decorator::Window_base::Hover &hover,
Genode::Reporter &hover_reporter)
{
Decorator::Window_base::Hover const new_hover =
find_hover(pointer_node, window_stack);
/* produce report only if hover state changed */
if (new_hover != hover) {
hover = new_hover;
Genode::Reporter::Xml_generator xml(hover_reporter, [&] ()
{
if (hover.window_id > 0) {
xml.node("window", [&] () {
xml.attribute("id", hover.window_id);
if (hover.left_sizer) xml.node("left_sizer");
if (hover.right_sizer) xml.node("right_sizer");
if (hover.top_sizer) xml.node("top_sizer");
if (hover.bottom_sizer) xml.node("bottom_sizer");
if (hover.title) xml.node("title");
if (hover.closer) xml.node("closer");
if (hover.minimizer) xml.node("minimizer");
if (hover.maximizer) xml.node("maximizer");
if (hover.unmaximizer) xml.node("unmaximizer");
});
}
});
}
}
void Decorator::Main::handle_window_layout_update(unsigned)
{
window_layout.update();
window_layout_update_needed = true;
}
void Decorator::Main::handle_nitpicker_sync(unsigned)
{
if (frame_cnt++ < frame_period)
return;
frame_cnt = 0;
bool model_updated = false;
if (window_layout_update_needed && window_layout.is_valid()) {
try {
Xml_node xml(window_layout.local_addr<char>(),
window_layout.size());
window_stack.update_model(xml);
model_updated = true;
/*
* A decorator element might have appeared or disappeared under
* the pointer.
*/
if (pointer.is_valid())
update_hover_report(Xml_node(pointer.local_addr<char>()),
window_stack, hover, hover_reporter);
} catch (Xml_node::Invalid_syntax) {
/*
* An error occured with processing the XML model. Flush the
* internal representation.
*/
window_stack.flush();
}
window_layout_update_needed = false;
}
bool const windows_animated = window_stack.schedule_animated_windows();
/*
* To make the perceived animation speed independent from the setting of
* 'frame_period', we update the animation as often as the nitpicker
* sync signal occurs.
*/
for (unsigned i = 0; i < frame_period; i++)
animator.animate();
if (!model_updated && !windows_animated)
return;
window_stack.update_nitpicker_views();
}
void Decorator::Main::handle_pointer_update(unsigned)
{
pointer.update();
if (pointer.is_valid())
update_hover_report(Xml_node(pointer.local_addr<char>()),
window_stack, hover, hover_reporter);
}
/************
** Server **
************/
namespace Server {
char const *name() { return "decorator_ep"; }
size_t stack_size() { return 8*1024*sizeof(long); }
void construct(Entrypoint &ep)
{
static Decorator::Main main(ep);
}
}

View File

@ -0,0 +1,10 @@
TARGET = themed_decorator
SRC_CC = main.cc theme.cc window.cc
LIBS = base config server libc libpng zlib blit file
INC_DIR += $(PRG_DIR)
.PHONY: plain_decorator_theme.tar
$(TARGET): plain_decorator_theme.tar
plain_decorator_theme.tar:
$(VERBOSE)cd $(PRG_DIR); tar cf $(PWD)/bin/$@ theme

View File

@ -0,0 +1,282 @@
/*
* \brief Window decorator that can be styled - theme handling
* \author Norman Feske
* \date 2015-11-12
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <os/texture_rgb888.h>
#include <nitpicker_gfx/text_painter.h>
#include <util/xml_node.h>
#include <decorator/xml_utils.h>
/* gems includes */
#include <gems/file.h>
#include <gems/png_image.h>
/* demo includes */
#include <scout_gfx/icon_painter.h>
/* local includes */
#include "theme.h"
enum Texture_id { TEXTURE_ID_DEFAULT, TEXTURE_ID_CLOSER, TEXTURE_ID_MAXIMIZER };
struct Texture_from_png_file
{
typedef Genode::Texture<Genode::Pixel_rgb888> Texture;
File png_file;
Png_image png_image { png_file.data<void>() };
Texture &texture { *png_image.texture<Genode::Pixel_rgb888>() };
Texture_from_png_file(char const *path, Genode::Allocator &alloc)
:
png_file(path, alloc)
{ }
};
static Genode::Texture<Genode::Pixel_rgb888> const &
texture_by_id(Texture_id texture_id, Genode::Allocator &alloc)
{
if (texture_id == TEXTURE_ID_DEFAULT) {
static Texture_from_png_file texture("theme/default.png", alloc);
return texture.texture;
}
if (texture_id == TEXTURE_ID_CLOSER) {
static Texture_from_png_file texture("theme/closer.png", alloc);
return texture.texture;
}
if (texture_id == TEXTURE_ID_MAXIMIZER) {
static Texture_from_png_file texture("theme/maximizer.png", alloc);
return texture.texture;
}
struct Invalid_texture_id { };
throw Invalid_texture_id();
}
static Genode::Texture<Genode::Pixel_rgb888> const &
texture_by_element_type(Decorator::Theme::Element_type type, Genode::Allocator &alloc)
{
switch (type) {
case Decorator::Theme::ELEMENT_TYPE_CLOSER:
return texture_by_id(TEXTURE_ID_CLOSER, alloc);
case Decorator::Theme::ELEMENT_TYPE_MAXIMIZER:
return texture_by_id(TEXTURE_ID_MAXIMIZER, alloc);
}
struct Invalid_element_type { };
throw Invalid_element_type();
};
static Text_painter::Font const &title_font(Genode::Allocator &alloc)
{
static File tff_file("theme/font.tff", alloc);
static Text_painter::Font font(tff_file.data<char>());
return font;
}
static Genode::Xml_node metadata(Genode::Allocator &alloc)
{
static File file("theme/metadata", alloc);
return Genode::Xml_node(file.data<char>(), file.size());
}
Decorator::Area Decorator::Theme::background_size() const
{
Genode::Texture<Pixel_rgb888> const &texture = texture_by_id(TEXTURE_ID_DEFAULT, _alloc);
return texture.size();
}
struct Margins_from_metadata : Decorator::Theme::Margins
{
Margins_from_metadata(char const *sub_node, Genode::Allocator &alloc)
{
Genode::Xml_node aura = metadata(alloc).sub_node(sub_node);
top = aura.attribute_value("top", 0UL);
bottom = aura.attribute_value("bottom", 0UL);
left = aura.attribute_value("left", 0UL);
right = aura.attribute_value("right", 0UL);
}
};
Decorator::Theme::Margins Decorator::Theme::aura_margins() const
{
static Margins_from_metadata aura("aura", _alloc);
return aura;
}
Decorator::Theme::Margins Decorator::Theme::decor_margins() const
{
static Margins_from_metadata decor("decor", _alloc);
return decor;
}
Decorator::Rect Decorator::Theme::title_geometry() const
{
static Rect rect = rect_attribute(metadata(_alloc).sub_node("title"));
return rect;
}
Decorator::Rect Decorator::Theme::element_geometry(Element_type type) const
{
if (type == ELEMENT_TYPE_CLOSER) {
static Rect rect(point_attribute(metadata(_alloc).sub_node("closer")),
texture_by_id(TEXTURE_ID_CLOSER, _alloc).size());
return rect;
}
if (type == ELEMENT_TYPE_MAXIMIZER) {
static Rect rect(point_attribute(metadata(_alloc).sub_node("maximizer")),
texture_by_id(TEXTURE_ID_MAXIMIZER, _alloc).size());
return rect;
}
struct Invalid_element_type { };
throw Invalid_element_type();
}
void Decorator::Theme::draw_background(Decorator::Pixel_surface pixel_surface,
Decorator::Alpha_surface alpha_surface,
unsigned alpha) const
{
Genode::Texture<Pixel_rgb888> const &texture = texture_by_id(TEXTURE_ID_DEFAULT, _alloc);
typedef Genode::Surface_base::Point Point;
typedef Genode::Surface_base::Rect Rect;
unsigned const left = aura_margins().left + decor_margins().left;
unsigned const right = aura_margins().right + decor_margins().right;
unsigned const middle = left + right < pixel_surface.size().w()
? pixel_surface.size().w() - left - right
: 0;
Rect const orig_clip = pixel_surface.clip();
/* left */
if (left) {
Rect curr_clip = Rect(Point(0, 0), Area(left, pixel_surface.size().h()));
pixel_surface.clip(curr_clip);
alpha_surface.clip(curr_clip);
Rect const rect(Point(0, 0), pixel_surface.size());
Icon_painter::paint(pixel_surface, rect, texture, alpha);
Icon_painter::paint(alpha_surface, rect, texture, alpha);
}
/* middle */
if (middle) {
Rect curr_clip = Rect(Point(left, 0), Area(middle, pixel_surface.size().h()));
pixel_surface.clip(curr_clip);
alpha_surface.clip(curr_clip);
Rect const rect(Point(0, 0), pixel_surface.size());
Icon_painter::paint(pixel_surface, rect, texture, alpha);
Icon_painter::paint(alpha_surface, rect, texture, alpha);
}
/* right */
if (right) {
Rect curr_clip = Rect(Point(left + middle, 0), Area(right, pixel_surface.size().h()));
pixel_surface.clip(curr_clip);
alpha_surface.clip(curr_clip);
Point at(0, 0);
Area size = pixel_surface.size();
if (texture.size().w() > pixel_surface.size().w()) {
at = Point((int)pixel_surface.size().w() - (int)texture.size().w(), 0);
size = Area(texture.size().w(), size.h());
}
Icon_painter::paint(pixel_surface, Rect(at, size), texture, alpha);
Icon_painter::paint(alpha_surface, Rect(at, size), texture, alpha);
}
pixel_surface.clip(orig_clip);
}
void Decorator::Theme::draw_title(Decorator::Pixel_surface pixel_surface,
Decorator::Alpha_surface alpha_surface,
char const *title) const
{
Text_painter::Font const &font = title_font(_alloc);
Area const label_area(font.str_w(title), font.str_h(title));
Rect const surface_rect(Point(0, 0), pixel_surface.size());
Rect const title_rect = absolute(title_geometry(), surface_rect);
Point const centered_text_pos = title_rect.center(label_area) - Point(0, 1);
Text_painter::paint(pixel_surface, centered_text_pos, font,
Genode::Color(0, 0, 0), title);
}
void Decorator::Theme::draw_element(Decorator::Pixel_surface pixel_surface,
Decorator::Alpha_surface alpha_surface,
Element_type element_type,
unsigned alpha) const
{
Genode::Texture<Pixel_rgb888> const &texture =
texture_by_element_type(element_type, _alloc);
Rect const surface_rect(Point(0, 0), pixel_surface.size());
Rect const element_rect = element_geometry(element_type);
Point const pos = absolute(element_rect.p1(), surface_rect);
Rect const rect(pos, element_rect.area());
Icon_painter::paint(pixel_surface, rect, texture, alpha);
Icon_painter::paint(alpha_surface, rect, texture, alpha);
}
Decorator::Point Decorator::Theme::absolute(Decorator::Point pos,
Decorator::Rect win_rect) const
{
Area const theme_size = background_size();
int x = pos.x();
int y = pos.y();
if (x > (int)theme_size.w()/2) x = win_rect.w() - theme_size.w() + x;
if (y > (int)theme_size.h()/2) y = win_rect.h() - theme_size.h() + y;
return win_rect.p1() + Point(x, y);
}
Decorator::Rect Decorator::Theme::absolute(Decorator::Rect rect,
Decorator::Rect win_rect) const
{
return Rect(absolute(rect.p1(), win_rect), absolute(rect.p2(), win_rect));
}

View File

@ -0,0 +1,85 @@
/*
* \brief Window decorator that can be styled - theme handling
* \author Norman Feske
* \date 2015-11-12
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _THEME_H_
#define _THEME_H_
/* Genode includes */
#include <os/texture.h>
#include <os/pixel_rgb565.h>
#include <os/pixel_alpha8.h>
#include <os/pixel_rgb888.h>
namespace Decorator {
class Theme;
typedef Genode::Pixel_rgb888 Pixel_rgb888;
typedef Genode::Pixel_rgb565 Pixel_rgb565;
typedef Genode::Pixel_alpha8 Pixel_alpha8;
typedef Genode::Surface<Pixel_rgb888> Pixel_surface;
typedef Genode::Surface<Pixel_alpha8> Alpha_surface;
typedef Genode::Surface_base::Area Area;
typedef Genode::Surface_base::Point Point;
typedef Genode::Surface_base::Rect Rect;
}
class Decorator::Theme
{
private:
Genode::Allocator &_alloc;
public:
struct Margins
{
unsigned top, bottom, left, right;
};
enum Element_type { ELEMENT_TYPE_CLOSER, ELEMENT_TYPE_MAXIMIZER };
Theme(Genode::Allocator &alloc) : _alloc(alloc) { }
Area background_size() const;
Margins aura_margins() const;
Margins decor_margins() const;
void draw_background(Pixel_surface, Alpha_surface, unsigned alpha) const;
void draw_title(Pixel_surface, Alpha_surface, char const *title) const;
void draw_element(Pixel_surface, Alpha_surface, Element_type,
unsigned alpha) const;
/**
* Return geometry of theme elements in the theme coordinate space
*/
Rect title_geometry() const;
Rect element_geometry(Element_type) const;
/**
* Calculate screen-absolute coordinate for a position within the theme
* coordinate space
*/
Point absolute(Point theme_pos, Rect win_outer_geometry) const;
Rect absolute(Rect theme_rect, Rect win_outer_geometry) const;
};
#endif /* _THEME_H_ */

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 B

View File

@ -0,0 +1,7 @@
<theme>
<aura top="8" bottom="8" left="8" right="8"/>
<decor top="20" bottom="8" left="1" right="1"/>
<title xpos="16" ypos="9" width="32" height="20"/>
<closer xpos="36" ypos="10"/>
<maximizer xpos="10" ypos="10"/>
</theme>

View File

@ -0,0 +1,73 @@
/*
* \brief Functor for tinting a surface with a color
* \author Norman Feske
* \date 2015-11-13
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _TINT_PAINTER_H_
#define _TINT_PAINTER_H_
#include <os/surface.h>
#include <polygon_gfx/interpolate_rgba.h>
struct Tint_painter
{
typedef Genode::Surface_base::Rect Rect;
/**
* Tint box with specified color
*
* \param rect position and size of box to tint
* \param color tinting color
*/
template <typename PT>
static inline void paint(Genode::Surface<PT> surface,
Rect rect,
Genode::Color color)
{
Rect clipped = Rect::intersect(surface.clip(), rect);
if (!clipped.valid()) return;
/*
* Generate lookup table (LUT) for mapping brightness values to colors.
* The specified color is used as a fixed point within the LUT. The
* other values are interpolated from black over the color to white.
*/
enum { LUT_SIZE = 256*3 };
PT pixel_lut[LUT_SIZE];
unsigned char alpha_lut[LUT_SIZE];
unsigned const lut_idx = color.r + color.g + color.b;
Polygon::interpolate_rgba(Polygon::Color(0, 0, 0), color,
pixel_lut, alpha_lut,
lut_idx + 1, 0, 0);
Polygon::interpolate_rgba(color, Polygon::Color(255, 255, 255),
pixel_lut + lut_idx, alpha_lut + lut_idx,
LUT_SIZE - lut_idx, 0, 0);
PT pix(color.r, color.g, color.b);
PT *dst, *dst_line = surface.addr() + surface.size().w()*clipped.y1() + clipped.x1();
for (int w, h = clipped.h() ; h--; dst_line += surface.size().w())
for (dst = dst_line, w = clipped.w(); w--; dst++) {
PT const pixel = *dst;
*dst = pixel_lut[pixel.r() + pixel.g() + pixel.b()];
}
surface.flush_pixels(clipped);
}
};
#endif /* _INCLUDE__NITPICKER_GFX__BOX_PAINTER_H_ */

View File

@ -0,0 +1,60 @@
/*
* \brief Example window decorator that mimics the Motif look
* \author Norman Feske
* \date 2014-01-10
*/
/*
* Copyright (C) 2014 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* local includes */
#include "window.h"
Decorator::Window_base::Hover Decorator::Window::hover(Point abs_pos) const
{
Hover hover;
if (!_decor_geometry().contains(abs_pos))
return hover;
hover.window_id = id();
Rect const closer_geometry =
_theme.absolute(_theme.element_geometry(Theme::ELEMENT_TYPE_CLOSER),
outer_geometry());
if (closer_geometry.contains(abs_pos)) {
hover.closer = true;
return hover;
}
Rect const maximizer_geometry =
_theme.absolute(_theme.element_geometry(Theme::ELEMENT_TYPE_MAXIMIZER),
outer_geometry());
if (maximizer_geometry.contains(abs_pos)) {
hover.maximizer = true;
return hover;
}
Rect const title_geometry = _theme.absolute(_theme.title_geometry(),
outer_geometry());
if (title_geometry.contains(abs_pos)) {
hover.title = true;
return hover;
}
int const x = abs_pos.x();
int const y = abs_pos.y();
Area const theme_size = _theme.background_size();
hover.left_sizer = x < outer_geometry().x1() + (int)theme_size.w()/2;
hover.right_sizer = x > outer_geometry().x2() - (int)theme_size.w()/2;
hover.top_sizer = y < outer_geometry().y1() + (int)theme_size.h()/2;
hover.bottom_sizer = y > outer_geometry().y2() - (int)theme_size.h()/2;
return hover;
}

View File

@ -0,0 +1,539 @@
/*
* \brief Window decorator that can be styled
* \author Norman Feske
* \date 2015-11-11
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _WINDOW_H_
#define _WINDOW_H_
/* Genode includes */
#include <ram_session/ram_session.h>
#include <decorator/window.h>
#include <nitpicker_session/connection.h>
#include <os/attached_dataspace.h>
#include <util/volatile_object.h>
/* demo includes */
#include <util/lazy_value.h>
/* gems includes */
#include <gems/animator.h>
#include <gems/nitpicker_buffer.h>
/* local includes */
#include "theme.h"
#include "config.h"
#include "tint_painter.h"
namespace Decorator {
class Window;
typedef Genode::String<200> Window_title;
typedef Genode::Attached_dataspace Attached_dataspace;
}
class Decorator::Window : public Window_base, public Animator::Item
{
private:
Genode::Ram_session &_ram;
Theme const &_theme;
/*
* Flag indicating that the current window position has been propagated
* to the window's corresponding nitpicker views.
*/
bool _nitpicker_views_up_to_date = false;
Nitpicker::Session::View_handle _neighbor;
unsigned _topped_cnt = 0;
Window_title _title;
bool _focused = false;
Lazy_value<int> _alpha = 0;
Animator &_animator;
struct Element : Animator::Item
{
Theme::Element_type const type;
char const * const attr;
bool _highlighted = false;
bool _present = false;
Lazy_value<int> alpha = 0;
int _alpha_dst() const
{
if (!_present)
return 0;
return _highlighted ? 255 : 150;
}
void _update_alpha_dst()
{
if ((int)alpha == _alpha_dst())
return;
alpha.dst(_alpha_dst(), 20);
animate();
}
void highlighted(bool highlighted)
{
_highlighted = highlighted;
_update_alpha_dst();
}
bool highlighted() const { return _highlighted; }
void present(bool present)
{
_present = present;
_update_alpha_dst();
}
bool present() const { return _present; }
void animate() override
{
alpha.animate();
animated((int)alpha != alpha.dst());
}
Element(Animator &animator, Theme::Element_type type, char const *attr)
:
Animator::Item(animator),
type(type), attr(attr)
{
_update_alpha_dst();
}
};
Element _closer { _animator, Theme::ELEMENT_TYPE_CLOSER, "closer" };
Element _maximizer { _animator, Theme::ELEMENT_TYPE_MAXIMIZER, "maximizer" };
template <typename FN>
void _for_each_element(FN const &func)
{
func(_closer);
func(_maximizer);
}
struct Nitpicker_view
{
typedef Nitpicker::Session::Command Command;
typedef Nitpicker::Session::View_handle View_handle;
bool const _view_is_remote;
Nitpicker::Session_client &_nitpicker;
View_handle _handle;
Nitpicker_view(Nitpicker::Session_client &nitpicker,
unsigned id = 0)
:
_view_is_remote(false),
_nitpicker(nitpicker),
_handle(_nitpicker.create_view())
{
/*
* We supply the window ID as label for the anchor view.
*/
if (id) {
char buf[128];
Genode::snprintf(buf, sizeof(buf), "%d", id);
_nitpicker.enqueue<Command::Title>(_handle, buf);
}
}
View_handle _create_remote_view(Nitpicker::Session_client &remote_nitpicker)
{
/* create view at the remote nitpicker session */
View_handle handle = remote_nitpicker.create_view();
Nitpicker::View_capability view_cap = remote_nitpicker.view_capability(handle);
/* import remote view into local nitpicker session */
return _nitpicker.view_handle(view_cap);
}
/**
* Constructor called for creating a view that refers to a buffer
* of another nitpicker session
*/
Nitpicker_view(Nitpicker::Session_client &nitpicker,
Nitpicker::Session_client &remote_nitpicker)
:
_view_is_remote(true),
_nitpicker(nitpicker),
_handle(_create_remote_view(remote_nitpicker))
{ }
~Nitpicker_view()
{
if (_view_is_remote)
_nitpicker.release_view_handle(_handle);
else
_nitpicker.destroy_view(_handle);
}
View_handle handle() const { return _handle; }
void stack(View_handle neighbor)
{
_nitpicker.enqueue<Command::To_front>(_handle, neighbor);
}
void place(Rect rect, Point offset)
{
_nitpicker.enqueue<Command::Geometry>(_handle, rect);
_nitpicker.enqueue<Command::Offset>(_handle, offset);
}
};
/**
* Nitpicker used as a global namespace of view handles
*/
Nitpicker::Session_client &_nitpicker;
Config const &_config;
Color _base_color = _config.base_color(_title);
/*
* Color value in 8.4 fixpoint format. We use four bits to
* represent the fractional part to enable smooth
* interpolation between the color values.
*/
Lazy_value<int> _r, _g, _b;
Color _color() const { return Color(_r >> 4, _g >> 4, _b >> 4); }
/**
* Nitpicker session that contains the upper and lower window
* decorations.
*/
Nitpicker::Connection _nitpicker_top_bottom;
Genode::Lazy_volatile_object<Nitpicker_buffer> _buffer_top_bottom;
/**
* Nitpicker session that contains the left and right window
* decorations.
*/
Nitpicker::Connection _nitpicker_left_right;
Genode::Lazy_volatile_object<Nitpicker_buffer> _buffer_left_right;
Nitpicker_view _bottom_view { _nitpicker, _nitpicker_top_bottom },
_right_view { _nitpicker, _nitpicker_left_right },
_left_view { _nitpicker, _nitpicker_left_right },
_top_view { _nitpicker, _nitpicker_top_bottom };
Nitpicker_view _content_view { _nitpicker, (unsigned)id() };
void _reallocate_nitpicker_buffers()
{
Area const theme_size = _theme.background_size();
bool const use_alpha = true;
Area const inner_size = geometry().area();
Area const outer_size = outer_geometry().area();
Area const size_top_bottom(outer_size.w(),
theme_size.h());
_nitpicker_top_bottom.buffer(Framebuffer::Mode(size_top_bottom.w(),
size_top_bottom.h(),
Framebuffer::Mode::RGB565),
use_alpha);
_buffer_top_bottom.construct(_nitpicker_top_bottom, size_top_bottom, _ram);
Area const size_left_right(outer_size.w() - inner_size.w(),
outer_size.h());
_nitpicker_left_right.buffer(Framebuffer::Mode(size_left_right.w(),
size_left_right.h(),
Framebuffer::Mode::RGB565),
use_alpha);
_buffer_left_right.construct(_nitpicker_left_right, size_left_right, _ram);
}
void _repaint_decorations(Nitpicker_buffer &buffer)
{
buffer.reset_surface();
_theme.draw_background(buffer.pixel_surface(),
buffer.alpha_surface(),
(int)_alpha);
_theme.draw_title(buffer.pixel_surface(), buffer.alpha_surface(),
_title.string());
_for_each_element([&] (Element const &element) {
_theme.draw_element(buffer.pixel_surface(), buffer.alpha_surface(),
element.type, element.alpha); });
Color const tint_color = _color();
if (tint_color != Color(0, 0, 0))
Tint_painter::paint(buffer.pixel_surface(),
Rect(Point(0, 0), buffer.pixel_surface().size()),
tint_color);
buffer.flush_surface();
buffer.nitpicker.framebuffer()->refresh(0, 0, buffer.size().w(), buffer.size().h());
}
void _assign_color(Color color)
{
_base_color = color;
_r.dst(_base_color.r << 4, 20);
_g.dst(_base_color.g << 4, 20);
_b.dst(_base_color.b << 4, 20);
}
public:
Window(unsigned id, Nitpicker::Session_client &nitpicker,
Animator &animator, Genode::Ram_session &ram,
Theme const &theme, Config const &config)
:
Window_base(id),
Animator::Item(animator),
_ram(ram), _theme(theme), _animator(animator),
_nitpicker(nitpicker), _config(config)
{
_reallocate_nitpicker_buffers();
_alpha.dst(_focused ? 256 : 200, 20);
animate();
}
void stack(Nitpicker::Session::View_handle neighbor) override
{
_neighbor = neighbor;
_top_view.stack(neighbor);
_left_view.stack(_top_view.handle());
_right_view.stack(_left_view.handle());
_bottom_view.stack(_right_view.handle());
_content_view.stack(_bottom_view.handle());
}
Nitpicker::Session::View_handle frontmost_view() const override
{
return _content_view.handle();
}
Rect _decor_geometry() const
{
Theme::Margins const decor = _theme.decor_margins();
return Rect(geometry().p1() - Point(decor.left, decor.top),
geometry().p2() + Point(decor.right, decor.bottom));
}
Rect outer_geometry() const override
{
Theme::Margins const aura = _theme.aura_margins();
Theme::Margins const decor = _theme.decor_margins();
unsigned const left = aura.left + decor.left;
unsigned const right = aura.right + decor.right;
unsigned const top = aura.top + decor.top;
unsigned const bottom = aura.bottom + decor.bottom;
return Rect(geometry().p1() - Point(left, top),
geometry().p2() + Point(right, bottom));
}
void border_rects(Rect *top, Rect *left, Rect *right, Rect *bottom) const
{
outer_geometry().cut(geometry(), top, left, right, bottom);
}
bool is_in_front_of(Window_base const &neighbor) const override
{
return _neighbor == neighbor.frontmost_view();
}
void update_nitpicker_views() override
{
if (!_nitpicker_views_up_to_date) {
Area const theme_size = _theme.background_size();
/* update view positions */
Rect top, left, right, bottom;
border_rects(&top, &left, &right, &bottom);
_content_view.place(geometry(), Point(0, 0));
_top_view .place(top, Point(0, 0));
_left_view .place(left, Point(0, -top.h()));
_right_view .place(right, Point(-right.w(), -top.h()));
_bottom_view .place(bottom, Point(0, -theme_size.h() + bottom.h()));
_nitpicker_views_up_to_date = true;
}
_nitpicker.execute();
}
void draw(Canvas_base &, Rect, Draw_behind_fn const &) const override { }
void adapt_to_changed_config()
{
_assign_color(_config.base_color(_title));
animate();
}
bool update(Xml_node window_node) override
{
bool updated = false;
/*
* Detect the need to bring the window to the top of the global
* view stack.
*/
unsigned const topped_cnt = attribute(window_node, "topped", 0UL);
if (topped_cnt != _topped_cnt) {
_topped_cnt = topped_cnt;
stack(Nitpicker::Session::View_handle());
updated = true;
}
bool trigger_animation = false;
Rect const old_geometry = geometry();
geometry(rect_attribute(window_node));
/*
* Detect position changes
*/
if (geometry().p1() != old_geometry.p1()
|| geometry().p2() != old_geometry.p2()) {
_nitpicker_views_up_to_date = false;
updated = true;
}
/*
* Detect size changes
*/
if (geometry().w() != old_geometry.w()
|| geometry().h() != old_geometry.h()) {
_reallocate_nitpicker_buffers();
/* triggering the animation has the side effect of repainting */
trigger_animation = true;
}
bool focused = window_node.attribute_value("focused", false);
if (_focused != focused) {
_focused = focused;
_alpha.dst(_focused ? 256 : 200, 20);
trigger_animation = true;
}
Window_title title = Decorator::string_attribute(window_node, "title",
Window_title("<untitled>"));
if (_title != title) {
_title = title;
trigger_animation = true;
}
/* update color on title change as the title is used as policy selector */
Color const base_color = _config.base_color(_title);
if (_base_color != base_color) {
_assign_color(base_color);
trigger_animation = true;
}
_for_each_element([&] (Element &element) {
bool const present = window_node.attribute_value(element.attr, false);
if (present != element.present()) {
element.present(present);
trigger_animation = true;
}
});
Xml_node const highlight = window_node.has_sub_node("highlight")
? window_node.sub_node("highlight")
: Xml_node("<highlight/>");
_for_each_element([&] (Element &element) {
bool const highlighted = highlight.has_sub_node(element.attr);
if (highlighted != element.highlighted()) {
element.highlighted(highlighted);
trigger_animation = true;
}
});
if (trigger_animation) {
updated = true;
/* schedule animation */
animate();
}
return updated;
}
Hover hover(Point) const override;
/**
* Window_base interface
*/
bool animated() const override
{
return (_alpha.dst() != (int)_alpha)
|| _r != _r.dst() || _g != _g.dst() || _b != _b.dst()
|| _closer.animated() || _maximizer.animated();
}
/**
* Animator::Item interface
*/
void animate() override
{
_alpha.animate();
_r.animate();
_g.animate();
_b.animate();
_for_each_element([&] (Element &element) { element.animate(); });
Animator::Item::animated(animated());
_repaint_decorations(*_buffer_top_bottom);
_repaint_decorations(*_buffer_left_right);
}
};
#endif /* _WINDOW_H_ */