genode/demo/src/app/scout/genode/platform_genode.cc

386 lines
8.7 KiB
C++

/*
* \brief Main program of Genode version of Scout
* \author Norman Feske
* \date 2006-08-28
*/
/*
* Copyright (C) 2006-2013 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.
*/
#include <base/env.h>
#include <base/thread.h>
#include <base/printf.h>
#include <base/lock.h>
#include <nitpicker_session/connection.h>
#include <timer_session/connection.h>
#include <nitpicker_view/client.h>
#include <input/event.h>
#include <base/semaphore.h>
#include <blit/blit.h>
#include "platform.h"
#include "config.h"
static int _scr_w;
static int _scr_h;
static Framebuffer::Mode::Format _scr_format;
static Input::Event *_ev_buf;
static char *_scr_adr;
static char *_buf_adr;
static int _mx, _my;
static int _flip_state; /* visible buffer (0..first, 1..second) */
static Nitpicker::Connection *_nitpicker;
static Timer::Session *_timer;
static unsigned long _timer_tick;
static int _init_flag;
static bool _view_initialized;
static int _vx, _vy, _vw, _vh; /* view geometry */
/**
* Create view and bring it to front
*
* This function is executed once during the construction of the static
* 'View_client' object in the 'view' function.
*/
static Nitpicker::View_capability create_and_top_view()
{
Nitpicker::View_capability cap = _nitpicker->create_view();
Nitpicker::View_client(cap).stack(Nitpicker::View_capability(), true, true);
Nitpicker::View_client(cap).viewport(_vx, _vy, _vw, _vh, 0, _flip_state ? -_scr_h : 0, true);
return cap;
}
/**
* Return Nitpicker view for the application
*
* On the first call of this function, the view gets created as static object.
* All subsequent calls just return the pointer to this object.
*/
static Nitpicker::View *view()
{
static Nitpicker::View_client view(create_and_top_view());
_view_initialized = true;
return &view;
}
using namespace Genode;
void *operator new(size_t size)
{
void *addr = env()->heap()->alloc(size);
if (!addr) {
PERR("env()->heap() has consumed %zd", env()->heap()->consumed());
PERR("env()->ram_session()->quota = %zd", env()->ram_session()->quota());
throw Genode::Allocator::Out_of_memory();
}
return addr;
}
/*****************
** Event queue **
*****************/
class Eventqueue
{
private:
static const int queue_size = 1024;
int _head;
int _tail;
Semaphore _sem;
Genode::Lock _head_lock; /* synchronize add */
Event _queue[queue_size];
public:
/**
* Constructor
*/
Eventqueue(): _head(0), _tail(0)
{
memset(_queue, 0, sizeof(_queue));
}
void add(Event *ev)
{
Lock::Guard lock_guard(_head_lock);
if ((_head + 1)%queue_size != _tail) {
_queue[_head] = *ev;
_head = (_head + 1)%queue_size;
_sem.up();
}
}
void get(Event *dst_ev)
{
_sem.down();
*dst_ev = _queue[_tail];
_tail = (_tail + 1)%queue_size;
}
int pending() { return _head != _tail; }
} _evqueue;
/******************
** Timer thread **
******************/
class Timer_thread : public Thread<4096>
{
private:
void _import_events()
{
if (_nitpicker->input()->is_pending() == false) return;
for (int i = 0, num = _nitpicker->input()->flush(); i < num; i++)
{
Event ev;
Input::Event e = _ev_buf[i];
if (e.type() == Input::Event::RELEASE
|| e.type() == Input::Event::PRESS) {
_mx = e.ax();
_my = e.ay();
ev.assign(e.type() == Input::Event::PRESS ? Event::PRESS : Event::RELEASE,
e.ax(), e.ay(), e.code());
_evqueue.add(&ev);
}
if (e.type() == Input::Event::MOTION) {
_mx = e.ax();
_my = e.ay();
ev.assign(Event::MOTION, e.ax(), e.ay(), e.code());
_evqueue.add(&ev);
}
}
}
public:
/**
* Constructor
*
* Start thread immediately on construction.
*/
Timer_thread() { start(); }
void entry()
{
while (1) {
Event ev;
ev.assign(Event::TIMER, _mx, _my, 0);
_evqueue.add(&ev);
_import_events();
_timer->msleep(10);
_timer_tick += 10;
}
}
};
/************************
** Platform interface **
************************/
/**
* Initialization
*/
Platform::Platform(unsigned vx, unsigned vy, unsigned vw, unsigned vh,
unsigned max_vw, unsigned max_vh)
: _max_vw(max_vw), _max_vh(max_vh)
{
_vx = vx, _vy = vy, _vw = vw, _vh = vh;
Config::mouse_cursor = 0;
Config::browser_attr = 7;
/*
* Create temporary nitpicker session just to determine the screen size
*
* NOTE: This approach has the disadvantage creating the nitpicker session
* is not an atomic operation. In theory, both session requests may be
* propagated to different nitpicker instances.
*/
_nitpicker = new (env()->heap()) Nitpicker::Connection();
Framebuffer::Mode const query_mode = _nitpicker->framebuffer()->mode();
_scr_w = query_mode.width();
_scr_h = query_mode.height();
_scr_format = query_mode.format();
destroy(env()->heap(), _nitpicker);
if (_max_vw) _scr_w = min(_max_vw, _scr_w);
if (_max_vh) _scr_h = min(_max_vh, _scr_h);
/*
* Allocate a nitpicker buffer double as high as the physical screen to
* use the upper/lower halves for double-buffering.
*/
_nitpicker = new (env()->heap())
Nitpicker::Connection(_scr_w, _scr_h*2, false, _scr_format);
static Timer::Connection timer;
_timer = &timer;
Framebuffer::Mode const used_mode = _nitpicker->framebuffer()->mode();
/*
* We use the upper half the allocated nitpicker buffer for '_scr_adr'
* and the lower half for '_buf_adr'.
*/
_scr_adr = env()->rm_session()->attach(_nitpicker->framebuffer()->dataspace());
_buf_adr = (char *)_scr_adr + _scr_w*_scr_h*used_mode.bytes_per_pixel();
_ev_buf = env()->rm_session()->attach(_nitpicker->input()->dataspace());
new (env()->heap()) Timer_thread();
/* mark platform as successfully initialized */
_init_flag = 1;
}
/**
* Platform information
*/
int Platform::initialized() { return _init_flag; }
void *Platform::scr_adr() { return _scr_adr; }
void *Platform::buf_adr() { return _buf_adr; }
int Platform::scr_w() { return _scr_w; }
int Platform::scr_h() { return _scr_h; }
/**
* Return pixel format used by the platform
*/
Platform::pixel_format Platform::scr_pixel_format() { return RGB565; }
/**
* Exchange foreground and back buffers
*/
void Platform::flip_buf_scr()
{
char *tmp = _buf_adr;
_buf_adr = _scr_adr;
_scr_adr = tmp;
_flip_state ^= 1;
/* enable new foreground buffer by configuring the view port */
view_geometry(_vx, _vy, _vw, _vh);
}
/**
* Copy background buffer pixels to foreground buffer
*/
void Platform::copy_buf_to_scr(int x, int y, int w, int h)
{
Genode::size_t bpp = Framebuffer::Mode::bytes_per_pixel(_scr_format);
/* copy background buffer to foreground buffer */
int len = w*bpp;
int linelen = _scr_w*bpp;
char *src = _buf_adr + (y*_scr_w + x)*bpp;
char *dst = _scr_adr + (y*_scr_w + x)*bpp;
blit(src, linelen, dst, linelen, len, h);
// for (int j = 0; j < h; j++, dst += linelen, src += linelen)
// memcpy(dst, src, len);
}
/**
* Flush pixels of specified area
*/
void Platform::scr_update(int x, int y, int w, int h)
{
if (w <= 0 || h <= 0) return;
if (_flip_state) y += _scr_h;
/*
* Initialize Nitpicker view
*
* We defer the initialization of the Nitpicker view to the occurrence of
* the first refresh to avoid artifacts during the startup of the program.
* Previous version used to create the view some time before calling
* 'refresh' for the first time. During that time, moving the mouse over
* the designated view area resulted in parts of the buffer to become
* visible.
*/
view();
/* refresh part of the buffer */
_nitpicker->framebuffer()->refresh(x, y, w, h);
}
void Platform::top_view()
{
if (_view_initialized)
view()->stack(Nitpicker::View_capability(), true, true);
}
/**
* Report view geometry changes to Nitpicker.
*/
void Platform::view_geometry(int x, int y, int w, int h, int do_redraw)
{
_vx = x; _vy = y; _vw = w; _vh = h;
if (_view_initialized)
view()->viewport(_vx, _vy, _vw, _vh, 0, _flip_state ? -_scr_h : 0, do_redraw);
}
int Platform::vx() { return _vx; }
int Platform::vy() { return _vy; }
int Platform::vw() { return _vw; }
int Platform::vh() { return _vh; }
/**
* Provide timer tick information
*/
unsigned long Platform::timer_ticks()
{
return _timer_tick;
}
/**
* Check if an event is pending
*/
int Platform::event_pending()
{
return _evqueue.pending();
}
/**
* Wait for an event, Zzz...zz..
*/
void Platform::get_event(Event *out_e)
{
_evqueue.get(out_e);
}