386 lines
8.7 KiB
C++
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);
|
|
}
|