/* * \brief Main program of Genode version of Scout * \author Norman Feske * \date 2006-08-28 */ /* * Copyright (C) 2006-2012 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 #include #include #include #include #include #include #include #include #include #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.keycode()); _evqueue.add(&ev); } if (e.type() == Input::Event::MOTION) { _mx = e.ax(); _my = e.ay(); ev.assign(Event::MOTION, e.ax(), e.ay(), e.keycode()); _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); }