2011-12-22 16:19:25 +01:00
|
|
|
/*
|
|
|
|
* \brief Implementation of Framebuffer and Input services
|
|
|
|
* \author Norman Feske
|
|
|
|
* \date 2006-09-22
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2013-01-10 21:44:47 +01:00
|
|
|
* Copyright (C) 2006-2013 Genode Labs GmbH
|
2011-12-22 16:19:25 +01:00
|
|
|
*
|
|
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
|
|
* under the terms of the GNU General Public License version 2.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <base/env.h>
|
|
|
|
#include <base/semaphore.h>
|
|
|
|
#include <base/rpc_server.h>
|
|
|
|
#include <framebuffer_session/framebuffer_session.h>
|
|
|
|
#include <input/component.h>
|
2013-12-30 01:21:53 +01:00
|
|
|
#include <nitpicker_gfx/texture_painter.h>
|
|
|
|
#include <os/pixel_rgb565.h>
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
#include "services.h"
|
|
|
|
|
|
|
|
|
2013-12-30 01:21:53 +01:00
|
|
|
typedef Genode::Texture<Genode::Pixel_rgb565> Texture_rgb565;
|
|
|
|
|
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
/*****************
|
|
|
|
** Event queue **
|
|
|
|
*****************/
|
|
|
|
|
|
|
|
class Event_queue
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
|
|
|
|
enum { QUEUE_SIZE = 1024 };
|
|
|
|
|
|
|
|
Input::Event _queue[QUEUE_SIZE];
|
|
|
|
int _head;
|
|
|
|
int _tail;
|
|
|
|
Genode::Semaphore _sem;
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*/
|
|
|
|
Event_queue(): _head(0), _tail(0)
|
|
|
|
{
|
2013-12-30 01:21:53 +01:00
|
|
|
Scout::memset(_queue, 0, sizeof(_queue));
|
2011-12-22 16:19:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void post(Input::Event ev)
|
|
|
|
{
|
|
|
|
if ((_head + 1)%QUEUE_SIZE != _tail) {
|
|
|
|
_queue[_head] = ev;
|
|
|
|
_head = (_head + 1)%QUEUE_SIZE;
|
|
|
|
_sem.up();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Input::Event get()
|
|
|
|
{
|
|
|
|
_sem.down();
|
|
|
|
Input::Event dst_ev = _queue[_tail];
|
|
|
|
_tail = (_tail + 1)%QUEUE_SIZE;
|
|
|
|
return dst_ev;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pending() { return _head != _tail; }
|
|
|
|
|
|
|
|
} _ev_queue;
|
|
|
|
|
|
|
|
|
|
|
|
/***************************
|
|
|
|
** Input service backend **
|
|
|
|
***************************/
|
|
|
|
|
|
|
|
namespace Input {
|
|
|
|
|
|
|
|
void event_handling(bool enable) { }
|
|
|
|
bool event_pending() { return _ev_queue.pending(); }
|
|
|
|
Event get_event() { return _ev_queue.get(); }
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-12-30 01:21:53 +01:00
|
|
|
class Window_content : public Scout::Element
|
2011-12-22 16:19:25 +01:00
|
|
|
{
|
|
|
|
private:
|
|
|
|
|
2013-12-30 01:21:53 +01:00
|
|
|
class Content_event_handler : public Scout::Event_handler
|
2011-12-22 16:19:25 +01:00
|
|
|
{
|
|
|
|
private:
|
|
|
|
|
2013-12-30 01:21:53 +01:00
|
|
|
Event_queue *_ev_queue;
|
|
|
|
Scout::Point _old_mouse_position;
|
|
|
|
Element *_element;
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
public:
|
|
|
|
|
2013-12-30 01:21:53 +01:00
|
|
|
Content_event_handler(Event_queue *ev_queue,
|
|
|
|
Scout::Element *element)
|
2013-05-22 19:24:20 +02:00
|
|
|
:
|
2013-05-22 19:21:29 +02:00
|
|
|
_ev_queue(ev_queue), _element(element) { }
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2013-12-30 01:21:53 +01:00
|
|
|
void handle(Scout::Event &ev)
|
2011-12-22 16:19:25 +01:00
|
|
|
{
|
2013-12-30 01:21:53 +01:00
|
|
|
using namespace Scout;
|
|
|
|
|
|
|
|
Point mouse_position = ev.mouse_position - _element->abs_position();
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
int code = 0;
|
|
|
|
|
|
|
|
if (ev.type == Event::PRESS || ev.type == Event::RELEASE)
|
|
|
|
code = ev.code;
|
|
|
|
|
|
|
|
Input::Event::Type type;
|
|
|
|
|
|
|
|
type = (ev.type == Event::MOTION) ? Input::Event::MOTION
|
|
|
|
: (ev.type == Event::PRESS) ? Input::Event::PRESS
|
|
|
|
: (ev.type == Event::RELEASE) ? Input::Event::RELEASE
|
|
|
|
: Input::Event::INVALID;
|
|
|
|
|
|
|
|
if (type != Input::Event::INVALID)
|
2013-12-30 01:21:53 +01:00
|
|
|
_ev_queue->post(Input::Event(type, code, mouse_position.x(),
|
|
|
|
mouse_position.y(),
|
|
|
|
mouse_position.x() - _old_mouse_position.x(),
|
|
|
|
mouse_position.y() - _old_mouse_position.y()));
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2013-12-30 01:21:53 +01:00
|
|
|
_old_mouse_position = mouse_position;
|
2011-12-22 16:19:25 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-05-22 19:24:20 +02:00
|
|
|
struct Fb_texture
|
|
|
|
{
|
2013-12-30 01:21:53 +01:00
|
|
|
unsigned w, h;
|
|
|
|
Genode::Attached_ram_dataspace ds;
|
|
|
|
Genode::Pixel_rgb565 *pixel;
|
|
|
|
unsigned char *alpha;
|
|
|
|
Genode::Texture<Genode::Pixel_rgb565> texture;
|
2013-05-22 19:24:20 +02:00
|
|
|
|
|
|
|
Fb_texture(unsigned w, unsigned h, bool config_alpha)
|
|
|
|
:
|
|
|
|
w(w), h(h),
|
2013-12-30 01:21:53 +01:00
|
|
|
ds(Genode::env()->ram_session(), w*h*sizeof(Genode::Pixel_rgb565)),
|
|
|
|
pixel(ds.local_addr<Genode::Pixel_rgb565>()),
|
2013-05-22 19:24:20 +02:00
|
|
|
alpha((unsigned char *)Genode::env()->heap()->alloc(w*h)),
|
2013-12-30 01:21:53 +01:00
|
|
|
texture(pixel, alpha, Scout::Area(w, h))
|
2013-05-22 19:24:20 +02:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
2013-12-30 01:21:53 +01:00
|
|
|
a += (Genode::Dither_matrix::value(x, y) - 127) >> 4;
|
2013-05-22 19:24:20 +02:00
|
|
|
|
|
|
|
alpha[y*w + x] = Genode::max(alpha_min, Genode::min(a, 255));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
~Fb_texture()
|
|
|
|
{
|
|
|
|
Genode::env()->heap()->free(alpha, w*h);
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
bool _config_alpha;
|
|
|
|
Content_event_handler _ev_handler;
|
|
|
|
Fb_texture *_fb;
|
|
|
|
unsigned _new_w, _new_h;
|
|
|
|
Genode::Signal_context_capability _mode_sigh;
|
|
|
|
bool _wait_for_refresh;
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
Window_content(unsigned fb_w, unsigned fb_h, Event_queue *ev_queue,
|
|
|
|
bool config_alpha)
|
|
|
|
:
|
2013-05-22 19:24:20 +02:00
|
|
|
_config_alpha(config_alpha),
|
2013-05-22 19:21:29 +02:00
|
|
|
_ev_handler(ev_queue, this),
|
2013-05-22 19:24:20 +02:00
|
|
|
_fb(new (Genode::env()->heap()) Fb_texture(fb_w, fb_h, _config_alpha)),
|
|
|
|
_new_w(fb_w), _new_h(fb_h),
|
|
|
|
_wait_for_refresh(false)
|
2011-12-22 16:19:25 +01:00
|
|
|
{
|
2013-12-30 01:21:53 +01:00
|
|
|
_min_size = Scout::Area(_fb->w, _fb->h);
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2013-05-22 19:24:20 +02:00
|
|
|
event_handler(&_ev_handler);
|
|
|
|
}
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2013-05-22 19:24:20 +02:00
|
|
|
Genode::Dataspace_capability fb_ds_cap() {
|
|
|
|
return _fb->ds.cap();
|
|
|
|
}
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2013-05-22 19:24:20 +02:00
|
|
|
unsigned fb_w() {
|
|
|
|
return _fb->w;
|
|
|
|
}
|
|
|
|
unsigned fb_h() {
|
|
|
|
return _fb->h;
|
|
|
|
}
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2013-05-22 19:24:20 +02:00
|
|
|
void mode_sigh(Genode::Signal_context_capability sigh)
|
|
|
|
{
|
|
|
|
_mode_sigh = sigh;
|
|
|
|
}
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2013-05-22 19:24:20 +02:00
|
|
|
void realloc_framebuffer()
|
|
|
|
{
|
|
|
|
/* skip reallocation if size has not changed */
|
|
|
|
if (_new_w == _fb->w && _new_h == _fb->h)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Genode::destroy(Genode::env()->heap(), _fb);
|
|
|
|
|
|
|
|
_fb = new (Genode::env()->heap())
|
|
|
|
Fb_texture(_new_w, _new_h, _config_alpha);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Suppress drawing of the texture until we received the next
|
|
|
|
* refresh call from the client. This way, we avoid flickering
|
|
|
|
* artifacts while continuously resizing the window.
|
|
|
|
*/
|
|
|
|
_wait_for_refresh = true;
|
2011-12-22 16:19:25 +01:00
|
|
|
}
|
|
|
|
|
2013-05-22 19:24:20 +02:00
|
|
|
void client_called_refresh() { _wait_for_refresh = false; }
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Element interface
|
|
|
|
*/
|
2013-12-30 01:21:53 +01:00
|
|
|
void draw(Scout::Canvas_base &canvas, Scout::Point abs_position)
|
2013-05-22 19:24:20 +02:00
|
|
|
{
|
|
|
|
if (!_wait_for_refresh)
|
2013-12-30 01:21:53 +01:00
|
|
|
canvas.draw_texture(abs_position + _position, _fb->texture);
|
2013-05-22 19:24:20 +02:00
|
|
|
}
|
|
|
|
|
2013-12-30 01:21:53 +01:00
|
|
|
void format_fixed_size(Scout::Area size)
|
2013-05-22 19:24:20 +02:00
|
|
|
{
|
2013-12-30 01:21:53 +01:00
|
|
|
_new_w = size.w(), _new_h = size.h();
|
2013-05-22 19:24:20 +02:00
|
|
|
|
|
|
|
/* notify framebuffer client about mode change */
|
|
|
|
if (_mode_sigh.valid())
|
|
|
|
Genode::Signal_transmitter(_mode_sigh).submit();
|
|
|
|
}
|
2011-12-22 16:19:25 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static Window_content *_window_content;
|
|
|
|
|
2013-12-30 01:21:53 +01:00
|
|
|
Scout::Element *window_content() { return _window_content; }
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
|
|
|
|
/***********************************************
|
|
|
|
** Implementation of the framebuffer service **
|
|
|
|
***********************************************/
|
|
|
|
|
|
|
|
namespace Framebuffer
|
|
|
|
{
|
|
|
|
class Session_component : public Genode::Rpc_object<Session>
|
|
|
|
{
|
2013-05-22 19:24:20 +02:00
|
|
|
private:
|
|
|
|
|
|
|
|
Window_content &_window_content;
|
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
public:
|
|
|
|
|
2013-05-22 19:24:20 +02:00
|
|
|
Session_component(Window_content &window_content)
|
|
|
|
: _window_content(window_content) { }
|
|
|
|
|
2012-01-20 21:34:01 +01:00
|
|
|
Genode::Dataspace_capability dataspace() {
|
2013-05-22 19:24:20 +02:00
|
|
|
return _window_content.fb_ds_cap(); }
|
2012-01-20 21:34:01 +01:00
|
|
|
|
2013-05-22 19:24:20 +02:00
|
|
|
void release() {
|
|
|
|
_window_content.realloc_framebuffer(); }
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2012-01-25 23:04:50 +01:00
|
|
|
Mode mode() const
|
2011-12-22 16:19:25 +01:00
|
|
|
{
|
2013-05-22 19:24:20 +02:00
|
|
|
return Mode(_window_content.fb_w(), _window_content.fb_h(),
|
2012-01-18 14:57:08 +01:00
|
|
|
Mode::RGB565);
|
2011-12-22 16:19:25 +01:00
|
|
|
}
|
|
|
|
|
2013-05-22 19:24:20 +02:00
|
|
|
void mode_sigh(Genode::Signal_context_capability sigh) {
|
|
|
|
_window_content.mode_sigh(sigh); }
|
2012-01-20 21:34:01 +01:00
|
|
|
|
2013-05-22 19:24:20 +02:00
|
|
|
void refresh(int x, int y, int w, int h)
|
|
|
|
{
|
|
|
|
_window_content.client_called_refresh();
|
|
|
|
_window_content.redraw_area(x, y, w, h);
|
|
|
|
}
|
2011-12-22 16:19:25 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class Root : public Genode::Root_component<Session_component>
|
|
|
|
{
|
2013-05-22 19:24:20 +02:00
|
|
|
private:
|
|
|
|
|
|
|
|
Window_content &_window_content;
|
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
protected:
|
|
|
|
|
|
|
|
Session_component *_create_session(const char *args) {
|
2013-05-22 19:24:20 +02:00
|
|
|
return new (md_alloc()) Session_component(_window_content); }
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
Root(Genode::Rpc_entrypoint *session_ep,
|
2013-05-22 19:24:20 +02:00
|
|
|
Genode::Allocator *md_alloc,
|
|
|
|
Window_content &window_content)
|
|
|
|
:
|
|
|
|
Genode::Root_component<Session_component>(session_ep, md_alloc),
|
|
|
|
_window_content(window_content)
|
|
|
|
{ }
|
2011-12-22 16:19:25 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-05-22 19:21:29 +02:00
|
|
|
void init_window_content(unsigned fb_w, unsigned fb_h, bool config_alpha)
|
2011-12-22 16:19:25 +01:00
|
|
|
{
|
|
|
|
static Window_content content(fb_w, fb_h, &_ev_queue, config_alpha);
|
|
|
|
_window_content = &content;
|
2013-05-22 19:21:29 +02:00
|
|
|
}
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2013-05-22 19:21:29 +02:00
|
|
|
|
|
|
|
void init_services(Genode::Rpc_entrypoint &ep)
|
|
|
|
{
|
|
|
|
using namespace Genode;
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Let the entry point serve the framebuffer and input root interfaces
|
|
|
|
*/
|
2013-05-22 19:21:29 +02:00
|
|
|
static Framebuffer::Root fb_root(&ep, env()->heap(), *_window_content);
|
2011-12-22 16:19:25 +01:00
|
|
|
static Input::Root input_root(&ep, env()->heap());
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now, the root interfaces are ready to accept requests.
|
|
|
|
* This is the right time to tell mummy about our services.
|
|
|
|
*/
|
|
|
|
env()->parent()->announce(ep.manage(&fb_root));
|
|
|
|
env()->parent()->announce(ep.manage(&input_root));
|
|
|
|
}
|