/* * \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 #include #include #include #include #include #include #include #include #include #include /* gems includes */ #include #include #include #include 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 Surface surface() { return Surface(surface_ds.local_addr(), size()); } void flush_surface() { /* blit back to front buffer */ blit(surface_ds.local_addr(), surface_num_bytes(), fb_ds.local_addr(), surface_num_bytes(), surface_num_bytes(), 1); } }; Lazy_volatile_object 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(view_handle); Nitpicker::Rect rect(Nitpicker::Point(), buffer->size()); nitpicker.enqueue(view_handle, rect); nitpicker.enqueue(view_handle); nitpicker.execute(); } Signal_receiver &sig_rec; /** * Function called on config change or mode change */ void handle_config(unsigned); Signal_dispatcher
config_dispatcher = { sig_rec, *this, &Main::handle_config}; void handle_sync(unsigned); Signal_dispatcher
sync_dispatcher = { sig_rec, *this, &Main::handle_sync}; template void paint_texture(Surface &, Texture 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); } }; /** * 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 void Backdrop::Main::paint_texture(Surface &surface, Texture 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 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()); 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 *png_texture = png_image.texture(); /* create texture with the scaled image */ Chunky_texture 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 texture(*env()->ram_session(), scaled_size); convert_pixel_format(scaled_texture, texture, alpha); /* paint texture onto surface */ Surface surface = buffer->surface(); 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 surface = buffer->surface(); 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("")); /* 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(sig.context()); if (dispatcher) dispatcher->dispatch(sig.num()); } }