/* * \brief Implementation of Framebuffer and Input services * \author Norman Feske * \date 2006-09-22 */ /* * Copyright (C) 2006-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 include */ #include #include #include #include #include #include #include #include #include /* local includes */ #include "services.h" typedef Genode::Texture Texture_rgb565; class Window_content : public Scout::Element { private: class Content_event_handler : public Scout::Event_handler { private: /* * Noncopyable */ Content_event_handler(Content_event_handler const &); Content_event_handler &operator = (Content_event_handler const &); Input::Session_component &_input_session; Scout::Point _old_mouse_position { }; Element *_element; public: Content_event_handler(Input::Session_component &input_session, Scout::Element *element) : _input_session(input_session), _element(element) { } void handle_event(Scout::Event const &ev) override { using namespace Scout; Point mouse_position = ev.mouse_position - _element->abs_position(); auto motion = [&] (Point p) { return Input::Absolute_motion{p.x(), p.y()}; }; if (ev.type == Event::MOTION) _input_session.submit(motion(mouse_position)); if (ev.type == Event::PRESS) _input_session.submit(Input::Press{Input::Keycode(ev.code)}); if (ev.type == Event::RELEASE) _input_session.submit(Input::Release{Input::Keycode(ev.code)}); _old_mouse_position = mouse_position; } }; struct Fb_texture { Genode::Allocator &alloc; unsigned w, h; Genode::Attached_ram_dataspace ds; Genode::Pixel_rgb565 *pixel; unsigned char *alpha; Genode::Texture texture; Fb_texture(Genode::Ram_allocator &ram, Genode::Region_map &local_rm, Genode::Allocator &alloc, unsigned w, unsigned h, bool config_alpha) : alloc(alloc), w(w), h(h), ds(ram, local_rm, w*h*sizeof(Genode::Pixel_rgb565)), pixel(ds.local_addr()), alpha((unsigned char *)alloc.alloc(w*h)), texture(pixel, alpha, Scout::Area(w, h)) { int alpha_min = config_alpha ? 0 : 255; /* init alpha channel */ for (unsigned y = 0; y < h; y++) for (unsigned x = 0; x < w; x++) { int v = (x * y + (w*h)/4) / w; v = v + (x + y)/2; int a = v & 0xff; if (v & 0x100) a = 255 - a; a += (Genode::Dither_matrix::value(x, y) - 127) >> 4; alpha[y*w + x] = Genode::max(alpha_min, Genode::min(a, 255)); } } ~Fb_texture() { alloc.free(alpha, w*h); } private: /* * Noncopyable */ Fb_texture(Fb_texture const &); Fb_texture &operator = (Fb_texture const &); }; Genode::Ram_allocator &_ram; Genode::Region_map &_rm; Genode::Allocator &_alloc; bool _config_alpha; Content_event_handler _ev_handler; Genode::Reconstructible _fb; /** * Size of the framebuffer handed out by the next call of 'dataspace' */ Scout::Area _next_size; /** * Most current designated size of the framebuffer as defined by the * user. * * The '_designated_size' may be updated any time when the user drags * the resize handle of the window. It is propagated to '_next_size' * not before the framebuffer client requests the current mode. Once * the mode information is passed to the client, it is locked until * the client requests the mode again. */ Scout::Area _designated_size; Genode::Signal_context_capability _mode_sigh { }; public: Window_content(Genode::Ram_allocator &ram, Genode::Region_map &rm, Genode::Allocator &alloc, unsigned fb_w, unsigned fb_h, Input::Session_component &input_session, bool config_alpha) : _ram(ram), _rm(rm), _alloc(alloc), _config_alpha(config_alpha), _ev_handler(input_session, this), _fb(_ram, _rm, _alloc, fb_w, fb_h, _config_alpha), _next_size(fb_w, fb_h), _designated_size(_next_size) { _min_size = Scout::Area(_fb->w, _fb->h); event_handler(&_ev_handler); } Genode::Dataspace_capability fb_ds_cap() { return _fb->ds.cap(); } unsigned fb_w() { return _fb->w; } unsigned fb_h() { return _fb->h; } Scout::Area mode_size() { _next_size = _designated_size; return _next_size; } void mode_sigh(Genode::Signal_context_capability sigh) { _mode_sigh = sigh; } void realloc_framebuffer() { /* skip reallocation if size has not changed */ if (_next_size.w() == _fb->w && _next_size.h() == _fb->h) return; _fb.construct(_ram, _rm, _alloc, _next_size.w(), _next_size.h(), _config_alpha); } /** * Element interface */ void draw(Scout::Canvas_base &canvas, Scout::Point abs_position) { canvas.draw_texture(abs_position + _position, _fb->texture); } void format_fixed_size(Scout::Area size) { _designated_size = size; /* notify framebuffer client about mode change */ if (_mode_sigh.valid()) Genode::Signal_transmitter(_mode_sigh).submit(); } }; static Window_content *_window_content; Scout::Element *window_content() { return _window_content; } /*********************************************** ** Implementation of the framebuffer service ** ***********************************************/ namespace Framebuffer { class Session_component; } class Framebuffer::Session_component : public Genode::Rpc_object { private: Timer::Connection _timer; Window_content &_window_content; public: Session_component(Genode::Env &env, Window_content &window_content) : _timer(env), _window_content(window_content) { } Genode::Dataspace_capability dataspace() override { _window_content.realloc_framebuffer(); return _window_content.fb_ds_cap(); } Mode mode() const override { return Mode(_window_content.mode_size().w(), _window_content.mode_size().h(), Mode::RGB565); } void mode_sigh(Genode::Signal_context_capability sigh) override { _window_content.mode_sigh(sigh); } void sync_sigh(Genode::Signal_context_capability sigh) override { _timer.sigh(sigh); _timer.trigger_periodic(10*1000); } void refresh(int x, int y, int w, int h) override { _window_content.redraw_area(x, y, w, h); } }; void init_window_content(Genode::Ram_allocator &ram, Genode::Region_map &rm, Genode::Allocator &alloc, Input::Session_component &input_component, unsigned fb_w, unsigned fb_h, bool config_alpha) { static Window_content content(ram, rm, alloc, fb_w, fb_h, input_component, config_alpha); _window_content = &content; } void init_services(Genode::Env &env, Input::Session_component &input_component) { using namespace Genode; static Framebuffer::Session_component fb_session(env, *_window_content); static Static_root fb_root(env.ep().manage(fb_session)); static Input::Root_component input_root(env.ep().rpc_ep(), input_component); /* * Now, the root interfaces are ready to accept requests. * This is the right time to tell mummy about our services. */ env.parent().announce(env.ep().manage(fb_root)); env.parent().announce(env.ep().manage(input_root)); }