From 9129db03c440b031d540154cb8656528a8caa79f Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Wed, 10 Sep 2014 16:00:50 +0200 Subject: [PATCH] gems: nit_fader --- repos/gems/run/nit_fader.run | 141 +++++ .../server/nit_fader/alpha_dither_painter.h | 107 ++++ repos/gems/src/server/nit_fader/main.cc | 526 ++++++++++++++++++ repos/gems/src/server/nit_fader/target.mk | 4 + 4 files changed, 778 insertions(+) create mode 100644 repos/gems/run/nit_fader.run create mode 100644 repos/gems/src/server/nit_fader/alpha_dither_painter.h create mode 100644 repos/gems/src/server/nit_fader/main.cc create mode 100644 repos/gems/src/server/nit_fader/target.mk 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)