diff --git a/repos/gems/run/nit_fader.run b/repos/gems/run/nit_fader.run
new file mode 100644
index 000000000..5434eb265
--- /dev/null
+++ b/repos/gems/run/nit_fader.run
@@ -0,0 +1,141 @@
+#
+# Build
+#
+if {![have_spec linux]} {
+ puts "Runs on Linux only"
+ exit 0
+}
+
+set build_components {
+ core init drivers/timer drivers/framebuffer/sdl
+ server/dynamic_rom server/nitpicker app/scout server/nit_fader
+ app/pointer
+}
+
+build $build_components
+
+create_boot_directory
+
+#
+# Generate config
+#
+
+append config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}
+
+install_config $config
+
+#
+# Boot modules
+#
+
+# generic modules
+set boot_modules {
+ core init timer dynamic_rom fb_sdl nitpicker nit_fader scout pointer
+}
+
+build_boot_image $boot_modules
+
+run_genode_until forever
diff --git a/repos/gems/src/server/nit_fader/alpha_dither_painter.h b/repos/gems/src/server/nit_fader/alpha_dither_painter.h
new file mode 100644
index 000000000..2b37fa0d9
--- /dev/null
+++ b/repos/gems/src/server/nit_fader/alpha_dither_painter.h
@@ -0,0 +1,107 @@
+/*
+ * \brief Functor for drawing dithered alpha values
+ * \author Norman Feske
+ * \date 2014-09-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.
+ */
+
+#ifndef _ALPHA_DITHER_PAINTER_H_
+#define _ALPHA_DITHER_PAINTER_H_
+
+#include
+#include
+#include
+
+
+struct Alpha_dither_painter
+{
+ typedef Genode::Pixel_alpha8 Pixel_alpha8;
+
+ typedef Genode::Surface_base::Rect Rect;
+ typedef Genode::Surface_base::Point Point;
+
+ /*
+ * \param fade fade value in 16.16 fixpoint format
+ */
+ static inline void paint(Genode::Surface &surface,
+ Rect rect, int fade)
+ {
+ Rect clipped = Rect::intersect(surface.clip(), rect);
+
+ if (!clipped.valid()) return;
+
+ Pixel_alpha8 *dst, *dst_line = surface.addr() + surface.size().w()*clipped.y1() + clipped.x1();
+
+ int y = clipped.y1();
+
+ /* scale fade value to range of alpha values */
+ fade *= 256;
+
+ for (int w, h = clipped.h() ; h--; dst_line += surface.size().w()) {
+
+ int x = clipped.x1();
+
+ for (dst = dst_line, w = clipped.w(); w--; dst++, x++) {
+
+ int const v = Genode::Dither_matrix::value(x, y) << 13;
+
+ dst->pixel = Genode::min(255, Genode::max(0, (fade - v) >> 16));
+ }
+
+ y++;
+ }
+ }
+
+ template
+ static inline void paint(Genode::Surface &surface,
+ Rect rect, int fade,
+ Genode::Texture const &texture)
+ {
+ /* clip against surface size */
+ Rect clipped = Rect::intersect(surface.clip(), rect);
+
+ /* clip against texture size */
+ clipped = Rect::intersect(Rect(Point(0, 0), texture.size()), clipped);
+
+ if (!clipped.valid()) return;
+
+ unsigned const src_line_w = texture.size().w(),
+ dst_line_w = surface.size().w();
+
+ unsigned const src_start = src_line_w*clipped.y1() + clipped.x1(),
+ dst_start = dst_line_w*clipped.y1() + clipped.x1();
+
+ Pixel_alpha8 *src, *src_line = (Pixel_alpha8 *)texture.alpha() + src_start;
+ Pixel_alpha8 *dst, *dst_line = surface.addr() + dst_start;
+
+ int y = clipped.y1();
+
+ for (int w, h = clipped.h() ; h--; dst_line += dst_line_w, src_line += src_line_w) {
+
+ int x = clipped.x1();
+
+ for (dst = dst_line, src = src_line, w = clipped.w(); w--; dst++, src++, x++) {
+
+ /*
+ * Multiply texture alpha value with fade value, dither the
+ * result.
+ */
+
+ int const a = (((int)src->pixel)*fade);
+ int const v = Genode::Dither_matrix::value(x, y) << 13;
+
+ dst->pixel = Genode::min(255, Genode::max(0, (a - v) >> 16));
+ }
+
+ y++;
+ }
+ }
+};
+
+#endif /* _ALPHA_DITHER_PAINTER_H_ */
diff --git a/repos/gems/src/server/nit_fader/main.cc b/repos/gems/src/server/nit_fader/main.cc
new file mode 100644
index 000000000..31763117f
--- /dev/null
+++ b/repos/gems/src/server/nit_fader/main.cc
@@ -0,0 +1,526 @@
+/*
+ * \brief Fader for a nitpicker client
+ * \author Norman Feske
+ * \date 2014-09-08
+ */
+
+/*
+ * 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.
+ */
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* local includes */
+#include
+
+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::env;
+ using Genode::Xml_node;
+ using Genode::Dataspace_capability;
+ using Genode::Attached_ram_dataspace;
+ using Genode::Texture;
+ using Genode::Surface;
+ using Genode::Volatile_object;
+ using Genode::Lazy_volatile_object;
+
+ 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 _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(Area size, bool use_alpha)
+ :
+ _use_alpha(use_alpha),
+ _ds(env()->ram_session(), _needed_bytes(size)),
+ _texture(_ds.local_addr(),
+ _ds.local_addr() + size.count()*sizeof(Pixel),
+ size)
+ { }
+
+ Dataspace_capability dataspace() { return _ds.cap(); }
+
+ Texture 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_surface { _ds.local_addr(), _size };
+
+ Surface _alpha_surface
+ {
+ _ds.local_addr() + _size.count()*sizeof(Pixel_rgb565),
+ _size
+ };
+
+ public:
+
+ Dst_buffer(Dataspace_capability ds_cap, Area size)
+ :
+ _ds(ds_cap), _size(size)
+ {
+ /* initialize input-mask buffer */
+ unsigned char *input_mask_buffer = _ds.local_addr()
+ + _size.count()*(1 + sizeof(Pixel_rgb565));
+
+ Genode::memset(input_mask_buffer, 0xff, _size.count());
+ }
+
+ Surface &pixel_surface() { return _pixel_surface; }
+ Surface &alpha_surface() { return _alpha_surface; }
+};
+
+
+class Nit_fader::Framebuffer_session_component
+:
+ public Genode::Rpc_object
+{
+ private:
+
+ Nitpicker::Connection &_nitpicker;
+ Src_buffer &_src_buffer;
+
+ Lazy_volatile_object _dst_buffer;
+
+
+ Lazy_value _fade;
+
+ public:
+
+ /**
+ * Constructor
+ */
+ Framebuffer_session_component(Nitpicker::Connection &nitpicker,
+ Src_buffer &src_buffer)
+ :
+ _nitpicker(nitpicker), _src_buffer(src_buffer)
+ { }
+
+ void dst_buffer(Dataspace_capability ds_cap, Area size)
+ {
+ _dst_buffer.construct(ds_cap, size);
+ }
+
+ void transfer_src_to_dst_pixel(Rect const rect)
+ {
+ if (!_dst_buffer.is_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.is_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.is_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
+{
+ private:
+
+ typedef Nitpicker::View_capability View_capability;
+ typedef Nitpicker::Session::View_handle View_handle;
+
+ Server::Entrypoint &_ep;
+
+ Volatile_object _src_buffer { Area(1, 1), false };
+
+ Nitpicker::Connection _nitpicker;
+
+ Genode::Attached_ram_dataspace _command_ds {
+ env()->ram_session(), sizeof(Nitpicker::Session::Command_buffer) };
+
+ Nitpicker::Session::Command_buffer &_commands =
+ *_command_ds.local_addr();
+
+ Framebuffer_session_component _fb_session { _nitpicker, *_src_buffer };
+
+ Framebuffer::Session_capability _fb_cap { _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(_view_handle, _view_geometry);
+ else
+ _nitpicker.enqueue(_view_handle, Rect());
+
+ _nitpicker.execute();
+
+ _view_visible = _fb_session.visible();
+ }
+
+ public:
+
+ /**
+ * Constructor
+ */
+ Nitpicker_session_component(Server::Entrypoint &ep) : _ep(ep)
+ { }
+
+ /**
+ * Destructor
+ */
+ ~Nitpicker_session_component()
+ {
+ _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);
+ 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(size, use_alpha);
+
+ _nitpicker.buffer(mode, true);
+
+ _fb_session.dst_buffer(_nitpicker.framebuffer()->dataspace(), size);
+ }
+
+ void focus(Genode::Capability focused) override
+ {
+ _nitpicker.focus(focused);
+ }
+};
+
+
+struct Nit_fader::Main
+{
+ Server::Entrypoint &ep;
+
+ Timer::Connection timer;
+
+ enum { PERIOD = 20 };
+
+ unsigned long alpha = 0;
+
+ unsigned long curr_frame() const { return timer.elapsed_ms() / PERIOD; }
+
+ unsigned long last_frame = 0;
+
+ void handle_config_update(unsigned);
+
+ Genode::Signal_rpc_member config_dispatcher
+ {
+ ep, *this, &Main::handle_config_update
+ };
+
+ Nitpicker_session_component nitpicker_session { ep };
+
+ Genode::Static_root nitpicker_root
+ {
+ ep.manage(nitpicker_session)
+ };
+
+ void handle_timer(unsigned)
+ {
+ unsigned long frame = curr_frame();
+ if (nitpicker_session.animate(frame - last_frame))
+ timer.trigger_once(PERIOD);
+
+ last_frame = frame;
+ }
+
+ Genode::Signal_rpc_member timer_dispatcher
+ {
+ ep, *this, &Main::handle_timer
+ };
+
+ Main(Server::Entrypoint &ep) : ep(ep)
+ {
+ Genode::config()->sigh(config_dispatcher);
+
+ timer.sigh(timer_dispatcher);
+
+ /* apply initial config */
+ handle_config_update(0);
+
+ env()->parent()->announce(ep.manage(nitpicker_root));
+ }
+};
+
+
+void Nit_fader::Main::handle_config_update(unsigned)
+{
+ Genode::config()->reload();
+
+ Genode::Xml_node config_xml = Genode::config()->xml_node();
+
+ 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);
+ }
+}
+
+
+/************
+ ** Server **
+ ************/
+
+namespace Server {
+
+ char const *name() { return "nit_fader_ep"; }
+
+ size_t stack_size() { return 4*1024*sizeof(long); }
+
+ void construct(Entrypoint &ep)
+ {
+ static Nit_fader::Main desktop(ep);
+ }
+}
diff --git a/repos/gems/src/server/nit_fader/target.mk b/repos/gems/src/server/nit_fader/target.mk
new file mode 100644
index 000000000..e7ca13565
--- /dev/null
+++ b/repos/gems/src/server/nit_fader/target.mk
@@ -0,0 +1,4 @@
+TARGET = nit_fader
+SRC_CC = main.cc
+LIBS = base server config blit
+INC_DIR += $(PRG_DIR)