genode/repos/gems/src/app/backdrop/main.cc

378 lines
9.7 KiB
C++

/*
* \brief Backdrop for Nitpicker
* \author Norman Feske
* \date 2009-08-28
*/
/*
* Copyright (C) 2009-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.
*/
/* Genode includes */
#include <nitpicker_session/connection.h>
#include <base/printf.h>
#include <util/misc_math.h>
#include <os/config.h>
#include <decorator/xml_utils.h>
#include <nitpicker_gfx/box_painter.h>
#include <nitpicker_gfx/texture_painter.h>
#include <os/attached_dataspace.h>
#include <util/volatile_object.h>
#include <os/texture_rgb565.h>
#include <os/texture_rgb888.h>
/* gems includes */
#include <gems/png_image.h>
#include <gems/file.h>
#include <gems/xml_anchor.h>
#include <gems/texture_utils.h>
using namespace Genode;
namespace Backdrop { struct Main; }
struct Backdrop::Main
{
Nitpicker::Connection nitpicker;
struct Buffer
{
Nitpicker::Connection &nitpicker;
/* physical screen size */
Framebuffer::Mode const mode = nitpicker.mode();
/**
* Return dataspace capability for virtual framebuffer
*/
Dataspace_capability _ds_cap(Nitpicker::Connection &nitpicker)
{
/* setup virtual framebuffer mode */
nitpicker.buffer(mode, false);
if (mode.format() != Framebuffer::Mode::RGB565) {
PWRN("Color mode %d not supported\n", (int)mode.format());
return Dataspace_capability();
}
return nitpicker.framebuffer()->dataspace();
}
Attached_dataspace fb_ds { _ds_cap(nitpicker) };
size_t surface_num_bytes() const
{
return size().count()*mode.bytes_per_pixel();
}
Attached_ram_dataspace surface_ds { env()->ram_session(), surface_num_bytes() };
/**
* Constructor
*/
Buffer(Nitpicker::Connection &nitpicker) : nitpicker(nitpicker) { }
/**
* Return size of virtual framebuffer
*/
Surface_base::Area size() const
{
return Surface_base::Area(mode.width(), mode.height());
}
/**
* Return back buffer as painting surface
*/
template <typename PT>
Surface<PT> surface()
{
return Surface<PT>(surface_ds.local_addr<PT>(), size());
}
void flush_surface()
{
/* blit back to front buffer */
blit(surface_ds.local_addr<void>(), surface_num_bytes(),
fb_ds.local_addr<void>(), surface_num_bytes(), surface_num_bytes(), 1);
}
};
Lazy_volatile_object<Buffer> buffer;
Nitpicker::Session::View_handle view_handle = nitpicker.create_view();
void _update_view()
{
/* display view behind all others */
typedef Nitpicker::Session::Command Command;
nitpicker.enqueue<Command::Background>(view_handle);
Nitpicker::Rect rect(Nitpicker::Point(), buffer->size());
nitpicker.enqueue<Command::Geometry>(view_handle, rect);
nitpicker.enqueue<Command::To_back>(view_handle);
nitpicker.execute();
}
Signal_receiver &sig_rec;
/**
* Function called on config change or mode change
*/
void handle_config(unsigned);
Signal_dispatcher<Main> config_dispatcher = {
sig_rec, *this, &Main::handle_config};
void handle_sync(unsigned);
Signal_dispatcher<Main> sync_dispatcher = {
sig_rec, *this, &Main::handle_sync};
template <typename PT>
void paint_texture(Surface<PT> &, Texture<PT> const &, Surface_base::Point, bool);
void apply_image(Xml_node);
void apply_fill(Xml_node);
Main(Signal_receiver &sig_rec) : sig_rec(sig_rec)
{
/* trigger application of initial config */
Signal_transmitter(config_dispatcher).submit();
nitpicker.mode_sigh(config_dispatcher);
config()->sigh(config_dispatcher);
}
};
/**
* Calculate designated image size with proportional scaling applied
*/
static Surface_base::Area calc_scaled_size(Xml_node operation,
Surface_base::Area image_size,
Surface_base::Area mode_size)
{
char const *attr = "scale";
if (!operation.has_attribute(attr))
return image_size;
/* prevent division by zero, below */
if (image_size.count() == 0)
return image_size;
/*
* Determine scale ratio (in 16.16 fixpoint format)
*/
unsigned const ratio =
operation.attribute(attr).has_value("fit") ?
min((mode_size.w() << 16) / image_size.w(),
(mode_size.h() << 16) / image_size.h()) :
operation.attribute(attr).has_value("zoom") ?
max((mode_size.w() << 16) / image_size.w(),
(mode_size.h() << 16) / image_size.h()) :
1 << 16;
/*
* We add 0.5 (1 << 15) to round instead of truncating the fractional
* part when converting the fixpoint numbers to integers.
*/
return Surface_base::Area((image_size.w()*ratio + (1 << 15)) >> 16,
(image_size.h()*ratio + (1 << 15)) >> 16);
}
template <typename PT>
void Backdrop::Main::paint_texture(Surface<PT> &surface, Texture<PT> const &texture,
Surface_base::Point pos, bool tiled)
{
/* prevent division by zero */
if (texture.size().count() == 0)
return;
if (tiled) {
/* shortcuts */
int const w = texture.size().w(), surface_w = surface.size().w();
int const h = texture.size().h(), surface_h = surface.size().h();
/* draw tiles across the whole surface */
for (int y = (pos.y() % h) - h; y < surface_h + h; y += h)
for (int x = (pos.x() % w) - w; x < surface_w + w; x += w)
Texture_painter::paint(surface, texture, Color(),
Texture_painter::Point(x, y),
Texture_painter::SOLID,
true);
} else {
Texture_painter::paint(surface, texture, Color(), pos,
Texture_painter::SOLID, true);
}
}
void Backdrop::Main::apply_image(Xml_node operation)
{
typedef Surface_base::Point Point;
typedef Surface_base::Area Area;
if (!operation.has_attribute("png")) {
PWRN("missing 'png' attribute in <image> node");
return;
}
char png_file_name[256];
png_file_name[0] = 0;
operation.attribute("png").value(png_file_name, sizeof(png_file_name));
File file(png_file_name, *env()->heap());
Anchor anchor(operation);
Png_image png_image(file.data<void>());
Area const scaled_size = calc_scaled_size(operation, png_image.size(),
Area(buffer->mode.width(),
buffer->mode.height()));
/*
* Determine parameters of graphics operation
*/
int const h_gap = (int)buffer->mode.width() - scaled_size.w(),
v_gap = (int)buffer->mode.height() - scaled_size.h();
int const anchored_xpos = anchor.horizontal == Anchor::LOW ? 0
: anchor.horizontal == Anchor::CENTER ? h_gap/2
: anchor.horizontal == Anchor::HIGH ? h_gap
: 0;
int const anchored_ypos = anchor.vertical == Anchor::LOW ? 0
: anchor.vertical == Anchor::CENTER ? v_gap/2
: anchor.vertical == Anchor::HIGH ? v_gap
: 0;
Point const offset = Decorator::point_attribute(operation);
Point const pos = Point(anchored_xpos, anchored_ypos) + offset;
bool const tiled = operation.has_attribute("tiled")
&& operation.attribute("tiled").has_value("yes");
unsigned alpha = Decorator::attribute(operation, "alpha", 256U);
/* obtain texture containing the pixels of the PNG image */
Texture<Pixel_rgb888> *png_texture = png_image.texture<Pixel_rgb888>();
/* create texture with the scaled image */
Chunky_texture<Pixel_rgb888> scaled_texture(*env()->ram_session(), scaled_size);
scale(*png_texture, scaled_texture);
png_image.release_texture(png_texture);
/*
* Code specific for the screen mode's pixel format
*/
/* create texture with down-sampled scaled image */
typedef Pixel_rgb565 PT;
Chunky_texture<PT> texture(*env()->ram_session(), scaled_size);
convert_pixel_format(scaled_texture, texture, alpha);
/* paint texture onto surface */
Surface<PT> surface = buffer->surface<PT>();
paint_texture(surface, texture, pos, tiled);
}
void Backdrop::Main::apply_fill(Xml_node operation)
{
/*
* Code specific for the screen mode's pixel format
*/
/* create texture with down-sampled scaled image */
typedef Pixel_rgb565 PT;
Surface<PT> surface = buffer->surface<PT>();
Color const color = Decorator::attribute(operation, "color", Color(0, 0, 0));
Box_painter::paint(surface, Surface_base::Rect(Surface_base::Point(0, 0),
buffer->size()), color);
}
void Backdrop::Main::handle_config(unsigned)
{
config()->reload();
buffer.construct(nitpicker);
/* clear surface */
apply_fill(Xml_node("<fill color=\"#000000\"/>"));
/* apply graphics primitives defined in the config */
try {
for (unsigned i = 0; i < config()->xml_node().num_sub_nodes(); i++) {
try {
Xml_node operation = config()->xml_node().sub_node(i);
if (operation.has_type("image"))
apply_image(operation);
if (operation.has_type("fill"))
apply_fill(operation);
}
catch (...) {
/*
* Ignore failure of individual operation, i.e., non-existing
* files or malformed PNG data.
*/
}
}
} catch (...) { /* ignore failure to obtain config */ }
/* schedule buffer refresh */
nitpicker.framebuffer()->sync_sigh(sync_dispatcher);
}
void Backdrop::Main::handle_sync(unsigned)
{
buffer->flush_surface();
_update_view();
/* disable sync signal until the next call of 'handle_config' */
nitpicker.framebuffer()->sync_sigh(Signal_context_capability());
}
/*
* Silence debug messages
*/
extern "C" void _sigprocmask() { }
int main(int argc, char **argv)
{
static Signal_receiver sig_rec;
static Backdrop::Main application(sig_rec);
/* process incoming signals */
for (;;) {
using namespace Genode;
Signal sig = sig_rec.wait_for_signal();
Signal_dispatcher_base *dispatcher =
dynamic_cast<Signal_dispatcher_base *>(sig.context());
if (dispatcher)
dispatcher->dispatch(sig.num());
}
}