genode/repos/gems/src/server/nit_fader/main.cc

523 lines
12 KiB
C++

/*
* \brief Fader for a nitpicker client
* \author Norman Feske
* \date 2014-09-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.
*/
/* Genode includes */
#include <base/component.h>
#include <nitpicker_session/connection.h>
#include <base/attached_rom_dataspace.h>
#include <base/attached_ram_dataspace.h>
#include <os/texture.h>
#include <os/surface.h>
#include <os/pixel_rgb565.h>
#include <os/pixel_alpha8.h>
#include <os/static_root.h>
#include <util/reconstructible.h>
#include <nitpicker_gfx/texture_painter.h>
#include <util/lazy_value.h>
#include <timer_session/connection.h>
/* local includes */
#include <alpha_dither_painter.h>
namespace Nit_fader {
class Main;
class Src_buffer;
class Dst_buffer;
class Framebuffer_session_component;
class Nitpicker_session_component;
typedef Genode::Surface_base::Area Area;
typedef Genode::Surface_base::Point Point;
typedef Genode::Surface_base::Rect Rect;
using Genode::size_t;
using Genode::Xml_node;
using Genode::Dataspace_capability;
using Genode::Attached_ram_dataspace;
using Genode::Texture;
using Genode::Surface;
using Genode::Reconstructible;
using Genode::Constructible;
typedef Genode::Pixel_rgb565 Pixel_rgb565;
typedef Genode::Pixel_alpha8 Pixel_alpha8;
}
/**
* Buffer handed out to our client as virtual framebuffer
*/
class Nit_fader::Src_buffer
{
private:
typedef Pixel_rgb565 Pixel;
bool const _use_alpha;
Attached_ram_dataspace _ds;
Texture<Pixel> _texture;
static size_t _needed_bytes(Area size)
{
/* account for alpha channel, input mask, and pixels */
return size.count() * (1 + 1 + sizeof(Pixel));
}
public:
/**
* Constructor
*/
Src_buffer(Genode::Env &env, Area size, bool use_alpha)
:
_use_alpha(use_alpha),
_ds(env.ram(), env.rm(), _needed_bytes(size)),
_texture(_ds.local_addr<Pixel>(),
_ds.local_addr<unsigned char>() + size.count()*sizeof(Pixel),
size)
{ }
Dataspace_capability dataspace() { return _ds.cap(); }
Texture<Pixel> const &texture() const { return _texture; }
bool use_alpha() const { return _use_alpha; }
};
class Nit_fader::Dst_buffer
{
private:
Genode::Attached_dataspace _ds;
Area _size;
Surface<Pixel_rgb565> _pixel_surface { _ds.local_addr<Pixel_rgb565>(), _size };
Surface<Pixel_alpha8> _alpha_surface
{
_ds.local_addr<Pixel_alpha8>() + _size.count()*sizeof(Pixel_rgb565),
_size
};
public:
Dst_buffer(Genode::Env &env, Dataspace_capability ds_cap, Area size)
:
_ds(env.rm(), ds_cap), _size(size)
{
/* initialize input-mask buffer */
unsigned char *input_mask_buffer = _ds.local_addr<unsigned char>()
+ _size.count()*(1 + sizeof(Pixel_rgb565));
Genode::memset(input_mask_buffer, 0xff, _size.count());
}
Surface<Pixel_rgb565> &pixel_surface() { return _pixel_surface; }
Surface<Pixel_alpha8> &alpha_surface() { return _alpha_surface; }
};
class Nit_fader::Framebuffer_session_component
:
public Genode::Rpc_object<Framebuffer::Session>
{
private:
Genode::Env &_env;
Nitpicker::Connection &_nitpicker;
Src_buffer &_src_buffer;
Constructible<Dst_buffer> _dst_buffer { };
Lazy_value<int> _fade { };
public:
/**
* Constructor
*/
Framebuffer_session_component(Genode::Env &env,
Nitpicker::Connection &nitpicker,
Src_buffer &src_buffer)
:
_env(env), _nitpicker(nitpicker), _src_buffer(src_buffer)
{ }
void dst_buffer(Dataspace_capability ds_cap, Area size)
{
_dst_buffer.construct(_env, ds_cap, size);
}
void transfer_src_to_dst_pixel(Rect const rect)
{
if (!_dst_buffer.constructed())
return;
_dst_buffer->pixel_surface().clip(rect);
Texture_painter::paint(_dst_buffer->pixel_surface(),
_src_buffer.texture(),
Genode::Color(0, 0, 0),
Point(0, 0),
Texture_painter::SOLID,
false);
}
void transfer_src_to_dst_alpha(Rect const rect)
{
if (!_dst_buffer.constructed())
return;
_dst_buffer->alpha_surface().clip(rect);
if (_src_buffer.use_alpha()) {
Alpha_dither_painter::paint(_dst_buffer->alpha_surface(), rect, _fade,
_src_buffer.texture());
} else {
Alpha_dither_painter::paint(_dst_buffer->alpha_surface(), rect, _fade);
}
}
Area size()
{
return _dst_buffer.constructed() ? _dst_buffer->pixel_surface().size()
: Area();
}
bool animate(unsigned num_frames)
{
for (unsigned i = 0; i < num_frames; i++)
_fade.animate();
Rect const rect(Point(0, 0), size());
transfer_src_to_dst_alpha(rect);
_nitpicker.framebuffer()->refresh(rect.x1(), rect.y1(), rect.w(), rect.h());
/* keep animating as long as the destination value is not reached */
return _fade != _fade.dst();
}
void fade(int fade_value, int steps) { _fade.dst(fade_value, steps); }
bool visible() const { return _fade != 0; }
/************************************
** Framebuffer::Session interface **
************************************/
Dataspace_capability dataspace() override
{
return _src_buffer.dataspace();
}
Framebuffer::Mode mode() const override
{
return _nitpicker.framebuffer()->mode();
}
void mode_sigh(Genode::Signal_context_capability sigh) override
{
_nitpicker.framebuffer()->mode_sigh(sigh);
}
void refresh(int x, int y, int w, int h) override
{
transfer_src_to_dst_pixel(Rect(Point(x, y), Area(w, h)));
transfer_src_to_dst_alpha(Rect(Point(x, y), Area(w, h)));
_nitpicker.framebuffer()->refresh(x, y, w, h);
}
void sync_sigh(Genode::Signal_context_capability sigh) override
{
_nitpicker.framebuffer()->sync_sigh(sigh);
}
};
class Nit_fader::Nitpicker_session_component
:
public Genode::Rpc_object<Nitpicker::Session>
{
private:
typedef Nitpicker::View_capability View_capability;
typedef Nitpicker::Session::View_handle View_handle;
Genode::Env &_env;
Reconstructible<Src_buffer> _src_buffer { _env, Area(1, 1), false };
Nitpicker::Connection _nitpicker { _env };
Genode::Attached_ram_dataspace _command_ds {
_env.ram(), _env.rm(), sizeof(Nitpicker::Session::Command_buffer) };
Nitpicker::Session::Command_buffer &_commands =
*_command_ds.local_addr<Nitpicker::Session::Command_buffer>();
Framebuffer_session_component _fb_session { _env, _nitpicker, *_src_buffer };
Framebuffer::Session_capability _fb_cap { _env.ep().manage(_fb_session) };
Nitpicker::Session::View_handle _view_handle { };
bool _view_visible = false;
Rect _view_geometry { };
void _update_view_visibility()
{
if (!_view_handle.valid() || (_view_visible == _fb_session.visible()))
return;
typedef Nitpicker::Session::Command Command;
if (_fb_session.visible())
_nitpicker.enqueue<Command::Geometry>(_view_handle, _view_geometry);
else
_nitpicker.enqueue<Command::Geometry>(_view_handle, Rect());
_nitpicker.execute();
_view_visible = _fb_session.visible();
}
public:
/**
* Constructor
*/
Nitpicker_session_component(Genode::Env &env) : _env(env)
{ }
/**
* Destructor
*/
~Nitpicker_session_component()
{
_env.ep().dissolve(_fb_session);
}
bool animate(unsigned num_frames)
{
bool const keep_animating = _fb_session.animate(num_frames);
_update_view_visibility();
return keep_animating;
}
void fade(int fade_value, int steps)
{
_fb_session.fade(fade_value, steps);
}
/**********************************
** Nitpicker::Session interface **
**********************************/
Framebuffer::Session_capability framebuffer_session() override
{
return _fb_cap;
}
Input::Session_capability input_session() override
{
return _nitpicker.input_session();
}
View_handle create_view(View_handle parent) override
{
_view_handle = _nitpicker.create_view(parent);
_update_view_visibility();
return _view_handle;
}
void destroy_view(View_handle handle) override
{
return _nitpicker.destroy_view(handle);
}
View_handle view_handle(View_capability view_cap,
View_handle handle) override
{
return _nitpicker.view_handle(view_cap, handle);
}
View_capability view_capability(View_handle handle) override
{
return _nitpicker.view_capability(handle);
}
void release_view_handle(View_handle handle) override
{
_nitpicker.release_view_handle(handle);
}
Dataspace_capability command_dataspace() override
{
return _command_ds.cap();
}
void execute() override
{
for (unsigned i = 0; i < _commands.num(); i++) {
Nitpicker::Session::Command command = _commands.get(i);
bool forward_command = true;
if (command.opcode == Nitpicker::Session::Command::OP_GEOMETRY) {
/* remember view geometry as defined by the client */
_view_geometry = command.geometry.rect;
if (!_view_visible)
forward_command = false;
}
if (forward_command)
_nitpicker.enqueue(command);
}
_fb_session.transfer_src_to_dst_pixel(Rect(Point(0, 0), _fb_session.size()));
_fb_session.transfer_src_to_dst_alpha(Rect(Point(0, 0), _fb_session.size()));
return _nitpicker.execute();
}
Framebuffer::Mode mode() override
{
return _nitpicker.mode();
}
void mode_sigh(Genode::Signal_context_capability sigh) override
{
_nitpicker.mode_sigh(sigh);
}
void buffer(Framebuffer::Mode mode, bool use_alpha) override
{
Area const size(mode.width(), mode.height());
_src_buffer.construct(_env, size, use_alpha);
_nitpicker.buffer(mode, true);
_fb_session.dst_buffer(_nitpicker.framebuffer()->dataspace(), size);
}
void focus(Genode::Capability<Session> focused) override
{
_nitpicker.focus(focused);
}
};
struct Nit_fader::Main
{
Genode::Env &env;
Genode::Attached_rom_dataspace config { env, "config" };
Timer::Connection timer { env };
enum { PERIOD = 20 };
unsigned long alpha = 0;
Genode::uint64_t curr_frame() const { return timer.elapsed_ms() / PERIOD; }
Genode::uint64_t last_frame = 0;
void handle_config_update();
Genode::Signal_handler<Main> config_handler
{
env.ep(), *this, &Main::handle_config_update
};
Nitpicker_session_component nitpicker_session { env };
Genode::Static_root<Nitpicker::Session> nitpicker_root
{
env.ep().manage(nitpicker_session)
};
void handle_timer()
{
Genode::uint64_t frame = curr_frame();
if (nitpicker_session.animate(frame - last_frame))
timer.trigger_once(PERIOD);
last_frame = frame;
}
Genode::Signal_handler<Main> timer_handler
{
env.ep(), *this, &Main::handle_timer
};
Main(Genode::Env &env) : env(env)
{
config.sigh(config_handler);
timer.sigh(timer_handler);
/* apply initial config */
handle_config_update();
env.parent().announce(env.ep().manage(nitpicker_root));
}
};
void Nit_fader::Main::handle_config_update()
{
config.update();
Genode::Xml_node config_xml = config.xml();
unsigned long new_alpha = alpha;
if (config_xml.has_attribute("alpha"))
config_xml.attribute("alpha").value(new_alpha);
/* respond to state change */
if (new_alpha != alpha) {
int const steps = (new_alpha > alpha) ? 20 : 50;
nitpicker_session.fade(280*new_alpha, steps);
alpha = new_alpha;
/* schedule animation */
last_frame = curr_frame();
timer.trigger_once(PERIOD);
}
}
/***************
** Component **
***************/
void Component::construct(Genode::Env &env) {
static Nit_fader::Main desktop(env); }