260 lines
6.9 KiB
C++
260 lines
6.9 KiB
C++
/*
|
|
* \brief Minimalistic status bar for nitpicker
|
|
* \author Norman Feske
|
|
* \date 2014-07-08
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2014-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 <util/reconstructible.h>
|
|
#include <util/xml_node.h>
|
|
#include <util/color.h>
|
|
#include <base/component.h>
|
|
#include <base/attached_rom_dataspace.h>
|
|
#include <os/pixel_rgb565.h>
|
|
#include <nitpicker_session/connection.h>
|
|
#include <nitpicker_gfx/box_painter.h>
|
|
#include <nitpicker_gfx/tff_font.h>
|
|
|
|
namespace Status_bar {
|
|
|
|
using namespace Genode;
|
|
|
|
typedef String<128> Domain_name;
|
|
typedef String<128> Label;
|
|
typedef Surface_base::Area Area;
|
|
typedef Surface_base::Point Point;
|
|
typedef Surface_base::Rect Rect;
|
|
|
|
struct Buffer;
|
|
struct Main;
|
|
}
|
|
|
|
extern char _binary_default_tff_start;
|
|
|
|
|
|
struct Status_bar::Buffer
|
|
{
|
|
enum { HEIGHT = 18, LABEL_GAP = 15 };
|
|
|
|
Nitpicker::Connection &_nitpicker;
|
|
|
|
Framebuffer::Mode const _nit_mode { _nitpicker.mode() };
|
|
|
|
/*
|
|
* Dimension nitpicker buffer depending on nitpicker's screen size.
|
|
* The status bar is as wide as nitpicker's screen and has a fixed
|
|
* height.
|
|
*/
|
|
Framebuffer::Mode const _mode { _nit_mode.width(), HEIGHT, _nit_mode.format() };
|
|
|
|
Dataspace_capability _init_buffer()
|
|
{
|
|
_nitpicker.buffer(_mode, false);
|
|
return _nitpicker.framebuffer()->dataspace();
|
|
}
|
|
|
|
Attached_dataspace _fb_ds;
|
|
|
|
Tff_font::Static_glyph_buffer<4096> _glyph_buffer { };
|
|
Tff_font _font { &_binary_default_tff_start, _glyph_buffer };
|
|
|
|
Buffer(Region_map &rm, Nitpicker::Connection &nitpicker)
|
|
:
|
|
_nitpicker(nitpicker), _fb_ds(rm, _init_buffer())
|
|
{ }
|
|
|
|
template <typename PT>
|
|
void _draw_outline(Surface<PT> &surface, Point pos, char const *s)
|
|
{
|
|
for (int j = -1; j <= 1; j++)
|
|
for (int i = -1; i <= 1; i++)
|
|
if (i || j)
|
|
Text_painter::paint(surface,
|
|
Text_painter::Position(pos.x() + i, pos.y() + j),
|
|
_font, Color(0, 0, 0), s);
|
|
}
|
|
|
|
template <typename PT>
|
|
void _draw_label(Surface<PT> &surface, Point pos,
|
|
Domain_name const &domain_name, Label const &label,
|
|
Color color)
|
|
{
|
|
Color const label_text_color((color.r + 255)/2,
|
|
(color.g + 255)/2,
|
|
(color.b + 255)/2);
|
|
Color const domain_text_color(255, 255, 255);
|
|
|
|
pos = pos + Point(1, 1);
|
|
|
|
_draw_outline(surface, pos, domain_name.string());
|
|
Text_painter::paint(surface, Text_painter::Position(pos.x(), pos.y()),
|
|
_font, domain_text_color, domain_name.string());
|
|
|
|
pos = pos + Point(_font.string_width(domain_name.string()).decimal() + LABEL_GAP, 0);
|
|
|
|
_draw_outline(surface, pos, label.string());
|
|
Text_painter::paint(surface, Text_painter::Position(pos.x(), pos.y()),
|
|
_font, label_text_color, label.string());
|
|
}
|
|
|
|
Area _label_size(Domain_name const &domain_name, Label const &label) const
|
|
{
|
|
return Area(_font.string_width(domain_name.string()).decimal() + LABEL_GAP
|
|
+ _font.string_width(label.string()).decimal() + 2,
|
|
_font.bounding_box().h() + 2);
|
|
}
|
|
|
|
void draw(Domain_name const &, Label const &, Color);
|
|
|
|
Framebuffer::Mode mode() const { return _mode; }
|
|
};
|
|
|
|
|
|
void Status_bar::Buffer::draw(Domain_name const &domain_name,
|
|
Label const &label,
|
|
Color color)
|
|
{
|
|
if (_mode.format() != Framebuffer::Mode::RGB565) {
|
|
error("pixel format not supported");
|
|
return;
|
|
}
|
|
|
|
typedef Pixel_rgb565 PT;
|
|
|
|
Area const area(_mode.width(), _mode.height());
|
|
|
|
Surface<PT> surface(_fb_ds.local_addr<PT>(), area);
|
|
|
|
Rect const view_rect(Point(0, 0), area);
|
|
|
|
int r = color.r, g = color.g, b = color.b;
|
|
|
|
/* dim session color a bit to improve the contrast of the label */
|
|
r = (r + 100)/2, g = (g + 100)/2, b = (b + 100)/2;
|
|
|
|
/* highlight first line with slightly brighter color */
|
|
Box_painter::paint(surface,
|
|
Rect(Point(0, 0), Area(view_rect.w(), 1)),
|
|
Color(r + (r / 2), g + (g / 2), b + (b / 2)));
|
|
|
|
/* draw slightly shaded background */
|
|
for (unsigned i = 1; i < area.h() - 1; i++) {
|
|
r -= r > 3 ? 4 : 0;
|
|
g -= g > 3 ? 4 : 0;
|
|
b -= b > 4 ? 4 : 0;
|
|
|
|
Box_painter::paint(surface,
|
|
Rect(Point(0, i), Area(view_rect.w(), 1)),
|
|
Color(r, g, b));
|
|
}
|
|
|
|
/* draw last line darker */
|
|
Box_painter::paint(surface,
|
|
Rect(Point(0, view_rect.h() - 1), Area(view_rect.w(), 1)),
|
|
Color(r / 4, g / 4, b / 4));
|
|
|
|
_draw_label(surface, view_rect.center(_label_size(domain_name, label)),
|
|
domain_name, label, color);
|
|
|
|
_nitpicker.framebuffer()->refresh(0, 0, area.w(), area.h());
|
|
}
|
|
|
|
|
|
struct Status_bar::Main
|
|
{
|
|
Env &_env;
|
|
|
|
Attached_rom_dataspace _focus_ds { _env, "focus" };
|
|
|
|
void _handle_focus();
|
|
|
|
Signal_handler<Main> _focus_handler {
|
|
_env.ep(), *this, &Main::_handle_focus };
|
|
|
|
void _handle_mode();
|
|
|
|
Signal_handler<Main> _mode_handler {
|
|
_env.ep(), *this, &Main::_handle_mode };
|
|
|
|
Nitpicker::Connection _nitpicker { _env, "status_bar" };
|
|
|
|
/* status-bar attributes */
|
|
Domain_name _domain_name { };
|
|
Label _label { };
|
|
Color _color { };
|
|
|
|
Reconstructible<Buffer> _buffer { _env.rm(), _nitpicker };
|
|
|
|
Nitpicker::Session::View_handle const _view { _nitpicker.create_view() };
|
|
|
|
void _draw_status_bar()
|
|
{
|
|
_buffer->draw(_domain_name, _label, _color);
|
|
}
|
|
|
|
Main(Env &env) : _env(env)
|
|
{
|
|
/* register signal handlers */
|
|
_focus_ds.sigh(_focus_handler);
|
|
_nitpicker.mode_sigh(_mode_handler);
|
|
|
|
/* schedule initial view-stacking command, needed only once */
|
|
typedef Nitpicker::Session::View_handle View_handle;
|
|
_nitpicker.enqueue<Nitpicker::Session::Command::To_front>(_view, View_handle());
|
|
|
|
/* import initial state */
|
|
_handle_mode();
|
|
_handle_focus();
|
|
}
|
|
};
|
|
|
|
|
|
void Status_bar::Main::_handle_focus()
|
|
{
|
|
/* fetch new content of the focus ROM module */
|
|
_focus_ds.update();
|
|
if (!_focus_ds.valid())
|
|
return;
|
|
|
|
/* reset status-bar properties */
|
|
_label = Label();
|
|
_domain_name = Domain_name();
|
|
_color = Color(0, 0, 0);
|
|
|
|
/* read new focus information from nitpicker's focus report */
|
|
try {
|
|
Xml_node node(_focus_ds.local_addr<char>());
|
|
|
|
_label = node.attribute_value("label", Label());
|
|
_domain_name = node.attribute_value("domain", Domain_name());
|
|
_color = node.attribute_value("color", Color(0, 0, 0));
|
|
}
|
|
catch (...) {
|
|
warning("could not parse focus report"); }
|
|
|
|
_draw_status_bar();
|
|
}
|
|
|
|
|
|
void Status_bar::Main::_handle_mode()
|
|
{
|
|
_buffer.construct(_env.rm(), _nitpicker);
|
|
|
|
_draw_status_bar();
|
|
|
|
Rect const geometry(Point(0, 0), Area(_buffer->mode().width(),
|
|
_buffer->mode().height()));
|
|
|
|
_nitpicker.enqueue<Nitpicker::Session::Command::Geometry>(_view, geometry);
|
|
_nitpicker.execute();
|
|
}
|
|
|
|
|
|
void Component::construct(Genode::Env &env) { static Status_bar::Main main { env }; }
|