liquid_fb: dynamic reconfiguration support

With this patch, 'liquid_framebuffer' can be reconfigured at runtime.
The configuration arguments are now provided as XML attributes, matching
those for 'nit_fb'. Furthermore, two new configuration options are added:

<config ...
  resize_handle="off" - show/hide a resize handle widget in the lower
                        right window corner
  decoration="on"     - show/hide window decoration
                        (title bar and borders)
/>

Fixes #740
Fixes #14
This commit is contained in:
Christian Prochaska 2013-05-22 19:21:29 +02:00 committed by Norman Feske
parent cf9eedca47
commit 314d5c0975
11 changed files with 313 additions and 134 deletions

View File

@ -39,7 +39,7 @@ 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 */
static int _vx, _vy, _vw, _vh, _vbx, _vby; /* view geometry */
/**
@ -52,7 +52,8 @@ 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);
Nitpicker::View_client(cap).viewport(_vx - _vbx, _vy - _vby, _vw, _vh,
_vbx, _flip_state ? _vby - _scr_h : _vby, true);
return cap;
}
@ -208,7 +209,7 @@ 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;
_vx = vx, _vy = vy, _vw = vw, _vh = vh, _vbx = 0, _vby = 0;
Config::mouse_cursor = 0;
Config::browser_attr = 7;
@ -284,7 +285,7 @@ void Platform::flip_buf_scr()
_flip_state ^= 1;
/* enable new foreground buffer by configuring the view port */
view_geometry(_vx, _vy, _vw, _vh);
view_geometry(_vx, _vy, _vw, _vh, false, _vbx, _vby);
}
@ -344,18 +345,23 @@ void Platform::top_view()
/**
* Report view geometry changes to Nitpicker.
*/
void Platform::view_geometry(int x, int y, int w, int h, int do_redraw)
void Platform::view_geometry(int x, int y, int w, int h, int do_redraw,
int buf_x, int buf_y)
{
_vx = x; _vy = y; _vw = w; _vh = h;
_vx = x; _vy = y; _vw = w; _vh = h; _vbx = buf_x, _vby = buf_y;
if (_view_initialized)
view()->viewport(_vx, _vy, _vw, _vh, 0, _flip_state ? -_scr_h : 0, do_redraw);
view()->viewport(_vx - _vbx, _vy - _vby, _vw, _vh,
_vbx,
_flip_state ? _vby - _scr_h : _vby, do_redraw);
}
int Platform::vx() { return _vx; }
int Platform::vy() { return _vy; }
int Platform::vw() { return _vw; }
int Platform::vh() { return _vh; }
int Platform::vx() { return _vx; }
int Platform::vy() { return _vy; }
int Platform::vw() { return _vw; }
int Platform::vh() { return _vh; }
int Platform::vbx() { return _vbx; }
int Platform::vby() { return _vby; }
/**

View File

@ -109,7 +109,8 @@ class Platform : public Screen_update
* The specified area is relative to the screen
* of the platform.
*/
void view_geometry(int x, int y, int w, int h, int do_redraw = 0);
void view_geometry(int x, int y, int w, int h, int do_redraw = 0,
int buf_x = 0, int buf_y = 0);
/**
* Bring Scouts view ontop
@ -123,6 +124,8 @@ class Platform : public Screen_update
int vy();
int vw();
int vh();
int vbx();
int vby();
/**
* Get timer ticks in miilliseconds

View File

@ -84,6 +84,15 @@ class User_state : public Parent_element
int vx() { return _vx; }
int vy() { return _vy; }
/**
* Update the current view offset
*/
void update_view_offset()
{
_vx = _window->view_x();
_vy = _window->view_y();
}
/**
* Apply input event to mouse focus state
*/
@ -112,8 +121,7 @@ class User_state : public Parent_element
_active = e;
_active->handle_event(ev);
_vx = _window->view_x();
_vy = _window->view_y();
update_view_offset();
_assign_mfocus(_root->find(ev.mx, ev.my), 1);
@ -122,8 +130,7 @@ class User_state : public Parent_element
case Event::RELEASE:
if (_key_cnt == 0) {
_vx = _window->view_x();
_vy = _window->view_y();
update_view_offset();
_active = 0;
_assign_mfocus(e);
}

View File

@ -71,7 +71,7 @@ class Window : public Parent_element
* Move window to new position
*/
virtual void vpos(int x, int y) {
_pf->view_geometry(x, y, _pf->vw(), _pf->vh(), 1); }
_pf->view_geometry(x, y, _pf->vw(), _pf->vh(), 1, _pf->vbx(), _pf->vby()); }
/**
* Define vertical scroll offset

View File

@ -3,23 +3,31 @@ running as a client of the Nitpicker GUI server. It supports the
following configuration options. The example shows the default
values.
! <config>
! <config
!
! <!-- enable the animated background,
! valid values or 'on' and 'off' -->
! <animate>on</animate>
! valid values are 'on' and 'off' -->
! animate="on"
!
! <!-- the initial window position and
! size of the virtual frame buffer -->
! <x>400</x>
! <y>270</y>
! <width>500</width>
! <height>400</height>
! xpos="400"
! ypos="270"
! width="500"
! height="400"
!
! <!-- set the window title -->
! <title>Liquid Framebuffer</title>
! title="Liquid Framebuffer"
!
! </config>
! <!-- show a resize handle,
! valid values are 'on' and 'off' -->
! resize_handle="off"
!
! <!-- show window decoration (title bar and borders),
! valid values are 'on' and 'off' -->
! decoration="on"
!
! />
Because Liquid frame buffer creates the virtual frame-buffer window at
start time, not at session-creation time, sufficient memory resources must

View File

@ -45,6 +45,8 @@ class Framebuffer_window : public Window
Fade_icon<PT, 32, 32> _sizer;
Element *_content;
bool _config_alpha;
bool _config_resize_handle;
bool _config_decoration;
public:
@ -55,10 +57,14 @@ class Framebuffer_window : public Window
Redraw_manager *redraw,
Element *content,
const char *name,
bool config_alpha)
bool config_alpha,
bool config_resize_handle,
bool config_decoration)
:
Window(pf, redraw, content->min_w() + 2, content->min_h() + 1 + _TH),
_bg_offset(0), _content(content)
_bg_offset(0), _content(content), _config_alpha(config_alpha),
_config_resize_handle(config_resize_handle),
_config_decoration(config_decoration)
{
/* titlebar */
_titlebar.rgba(TITLEBAR_RGBA);
@ -70,12 +76,71 @@ class Framebuffer_window : public Window
_sizer.event_handler(new Sizer_event_handler(this));
_sizer.alpha(100);
append(&_titlebar);
append(_content);
append(&_sizer);
if (config_decoration)
append(&_titlebar);
_min_w = max_w();
_min_h = max_h();
append(_content);
if (config_resize_handle)
append(&_sizer);
_min_w = 1 + 32 + 1; /* left border + resize handle + right border */
_min_h = _TH + 32 + 1; /* title bar + resize handle + bottom border */
}
/**
* Set the window title
*/
void name(const char *name)
{
_titlebar.text(name);
}
/**
* Set the alpha config option
*/
void config_alpha(bool alpha)
{
_config_alpha = alpha;
}
/**
* Set the resize_handle config option
*/
void config_resize_handle(bool resize_handle)
{
if (!_config_resize_handle && resize_handle)
append(&_sizer);
else if (_config_resize_handle && !resize_handle)
remove(&_sizer);
_config_resize_handle = resize_handle;
}
/**
* Set the decoration config option
*/
void config_decoration(bool decoration)
{
_config_decoration = decoration;
}
/**
* Move window to new position
*/
void vpos(int x, int y)
{
Window::vpos(x, y);
format(_w, _h);
}
/**
* Resize the window according to the new content size
*/
void content_geometry(int x, int y, int w, int h)
{
Window::vpos(x, y);
format(w + 2, h + 1 + _TH);
}
/**
@ -83,6 +148,9 @@ class Framebuffer_window : public Window
*/
void format(int w, int h)
{
/* limit window size to valid values */
w = (w < _min_w) ? _min_w : w;
h = (h < _min_h) ? _min_h : h;
w = (w > max_w()) ? max_w() : w;
h = (h > max_h()) ? max_h() : h;
_w = w;
@ -101,7 +169,13 @@ class Framebuffer_window : public Window
_sizer.geometry(_w - 32, _h - 32, 32, 32);
pf()->view_geometry(pf()->vx(), pf()->vy(), _w, _h);
pf()->top_view();
if (_config_decoration)
pf()->view_geometry(pf()->vx(), pf()->vy(), _w, _h);
else
pf()->view_geometry(pf()->vx(), pf()->vy(),
_w - 2, _h - 1 - _TH, false, -1, -_TH);
redraw()->size(_w, _h);
refresh();
}

View File

@ -11,6 +11,9 @@
* under the terms of the GNU General Public License version 2.
*/
#include <base/rpc_server.h>
#include <base/signal.h>
#include <cap_session/connection.h>
#include <os/config.h>
#include "framebuffer_window.h"
@ -18,6 +21,8 @@
#include "user_state.h"
#include "services.h"
using namespace Genode;
/**
* Runtime configuration
*/
@ -85,6 +90,15 @@ static long config_fb_y = 260;
*/
static const char *config_title = "Liquid Framebuffer";
/**
* Resize handle
*/
static bool config_resize_handle = false;
/**
* Window decoration
*/
static bool config_decoration = true;
/**
* Parse configuration
@ -97,37 +111,150 @@ static void read_config()
try {
char buf[16];
config_node.sub_node("animate").value(buf, sizeof(buf));
config_node.attribute("animate").value(buf, sizeof(buf));
if (!strcmp("off", buf)) config_animate = false;
else if (!strcmp("on", buf)) config_animate = true;
else
Genode::printf("Warning: invalid value for animate declaration,\n"
" valid values are 'on', 'off.\n'");
} catch (Xml_node::Nonexistent_sub_node) { }
} catch (Xml_node::Nonexistent_attribute) { }
config_alpha = config_animate;
try { config_node.sub_node("x").value(&config_fb_x); }
catch (Xml_node::Nonexistent_sub_node) { }
try { config_node.attribute("xpos").value(&config_fb_x); }
catch (Xml_node::Nonexistent_attribute) { }
try { config_node.sub_node("y").value(&config_fb_y); }
catch (Xml_node::Nonexistent_sub_node) { }
try { config_node.attribute("ypos").value(&config_fb_y); }
catch (Xml_node::Nonexistent_attribute) { }
try { config_node.sub_node("width").value(&config_fb_width); }
catch (Xml_node::Nonexistent_sub_node) { }
try { config_node.attribute("width").value(&config_fb_width); }
catch (Xml_node::Nonexistent_attribute) { }
try { config_node.sub_node("height").value(&config_fb_height); }
catch (Xml_node::Nonexistent_sub_node) { }
try { config_node.attribute("height").value(&config_fb_height); }
catch (Xml_node::Nonexistent_attribute) { }
try {
static char buf[64];
config_node.sub_node("title").value(buf, sizeof(buf));
config_node.attribute("title").value(buf, sizeof(buf));
config_title = buf;
} catch (Xml_node::Nonexistent_sub_node) { }
} catch (Xml_node::Nonexistent_attribute) { }
try {
char buf[16];
config_node.attribute("decoration").value(buf, sizeof(buf));
if (!strcmp("off", buf)) config_decoration = false;
else if (!strcmp("on", buf)) config_decoration = true;
else
Genode::printf("Warning: invalid value for decoration declaration,\n"
" valid values are 'on', 'off.\n'");
} catch (Xml_node::Nonexistent_attribute) { }
try {
char buf[16];
config_node.attribute("resize_handle").value(buf, sizeof(buf));
if (!strcmp("off", buf)) config_resize_handle = false;
else if (!strcmp("on", buf)) config_resize_handle = true;
else
Genode::printf("Warning: invalid value for resize_handle declaration,\n"
" valid values are 'on', 'off.\n'");
} catch (Xml_node::Nonexistent_attribute) { }
}
/*******************
** Input handler **
*******************/
struct Input_handler
{
GENODE_RPC(Rpc_handle_input, void, handle, Event&);
GENODE_RPC_INTERFACE(Rpc_handle_input);
};
class Input_handler_component : public Genode::Rpc_object<Input_handler,
Input_handler_component>
{
private:
Platform &_pf;
User_state &_user_state;
Framebuffer_window<Pixel_rgb565> &_fb_win;
Redraw_manager &_redraw;
Signal_receiver &_sig_rec;
unsigned long _curr_time, _old_time;
public:
Input_handler_component(Platform &pf,
User_state &user_state,
Framebuffer_window<Pixel_rgb565> &fb_win,
Redraw_manager &redraw,
Signal_receiver &sig_rec)
: _pf(pf),
_user_state(user_state),
_fb_win(fb_win),
_redraw(redraw),
_sig_rec(sig_rec)
{
_curr_time = _old_time = _pf.timer_ticks();
}
void handle(Event &ev)
{
if (ev.type != Event::WHEEL) {
ev.mx -= _user_state.vx();
ev.my -= _user_state.vy();
}
/* direct all keyboard events to the window content */
if ((ev.type == Event::PRESS || ev.type == Event::RELEASE)
&& (ev.code != Event::BTN_LEFT))
window_content()->handle_event(ev);
else
_user_state.handle_event(ev);
if (ev.type == Event::REFRESH)
_pf.scr_update(0, 0, _pf.scr_w(), _pf.scr_h());
if (ev.type == Event::TIMER) {
Tick::handle(_pf.timer_ticks());
/* check for configuration changes */
if (_sig_rec.pending()) {
_sig_rec.wait_for_signal();
config()->reload();
/* keep the current values by default */
config_fb_x = _fb_win.view_x();
config_fb_y = _fb_win.view_y();
config_fb_width = _fb_win.view_w();
config_fb_height = _fb_win.view_h();
try { read_config(); } catch (...) { }
_fb_win.name(config_title);
_fb_win.config_alpha(config_alpha);
_fb_win.config_resize_handle(config_resize_handle);
_fb_win.config_decoration(config_decoration);
/* must get called after 'config_decoration()' */
_fb_win.content_geometry(config_fb_x, config_fb_y,
config_fb_width, config_fb_height);
_user_state.update_view_offset();
}
}
/* perform periodic redraw */
_curr_time = _pf.timer_ticks();
if (!_pf.event_pending() && ((_curr_time - _old_time > 20) || (_curr_time < _old_time))) {
_old_time = _curr_time;
_redraw.process();
}
}
};
/**
* Main program
*/
@ -137,6 +264,14 @@ int main(int argc, char **argv)
try { read_config(); } catch (...) { }
/*
* Register signal handler for config changes
*/
static Signal_receiver sig_rec;
static Signal_context sig_ctx;
try { config()->sigh(sig_rec.manage(&sig_ctx)); } catch (...) { }
/* heuristic for allocating the double-buffer backing store */
enum { WINBORDER_WIDTH = 10, WINBORDER_HEIGHT = 40 };
@ -147,8 +282,8 @@ int main(int argc, char **argv)
config_fb_width + WINBORDER_WIDTH,
config_fb_height + WINBORDER_HEIGHT);
/* initialize our services and window content */
init_services(config_fb_width, config_fb_height, config_alpha);
/* initialize our window content */
init_window_content(config_fb_width, config_fb_height, config_alpha);
/* init canvas */
static Chunky_canvas<Pixel_rgb565> canvas;
@ -162,7 +297,8 @@ int main(int argc, char **argv)
/* create instance of browser window */
static Framebuffer_window<Pixel_rgb565>
fb_win(&pf, &redraw, window_content(), config_title, config_alpha);
fb_win(&pf, &redraw, window_content(), config_title, config_alpha,
config_resize_handle, config_decoration);
if (config_animate) {
static Background_animator fb_win_bg_anim(&fb_win);
@ -177,40 +313,24 @@ int main(int argc, char **argv)
fb_win.parent(&user_state);
fb_win.format(fb_win.min_w(), fb_win.min_h());
/* initialize server entry point */
enum { STACK_SIZE = 2*1024*sizeof(addr_t) };
static Cap_connection cap;
static Rpc_entrypoint ep(&cap, STACK_SIZE, "liquid_fb_ep");
/* initialize public services */
init_services(ep);
/* create local input handler service */
static Input_handler_component input_handler(pf, user_state, fb_win,
redraw, sig_rec);
Capability<Input_handler> input_handler_cap = ep.manage(&input_handler);
/* enter main loop */
Event ev;
unsigned long curr_time, old_time;
curr_time = old_time = pf.timer_ticks();
lock_window_content();
do {
unlock_window_content();
pf.get_event(&ev);
lock_window_content();
if (ev.type != Event::WHEEL) {
ev.mx -= user_state.vx();
ev.my -= user_state.vy();
}
/* direct all keyboard events to the window content */
if ((ev.type == Event::PRESS || ev.type == Event::RELEASE)
&& (ev.code != Event::BTN_LEFT))
window_content()->handle_event(ev);
else
user_state.handle_event(ev);
if (ev.type == Event::REFRESH)
pf.scr_update(0, 0, pf.scr_w(), pf.scr_h());
if (ev.type == Event::TIMER)
Tick::handle(pf.timer_ticks());
/* perform periodic redraw */
curr_time = pf.timer_ticks();
if (!pf.event_pending() && ((curr_time - old_time > 20) || (curr_time < old_time))) {
old_time = curr_time;
redraw.process();
}
input_handler_cap.call<Input_handler::Rpc_handle_input>(ev);
} while (ev.type != Event::QUIT);
return 0;

View File

@ -14,7 +14,6 @@
#include <base/env.h>
#include <base/semaphore.h>
#include <base/rpc_server.h>
#include <cap_session/connection.h>
#include <framebuffer_session/framebuffer_session.h>
#include <input/component.h>
@ -90,21 +89,16 @@ class Window_content : public Element
{
private:
Genode::Lock &_lock;
Event_queue *_ev_queue;
int _omx, _omy;
Element *_element;
public:
Content_event_handler(Event_queue *ev_queue, Element *element,
Genode::Lock &lock)
Content_event_handler(Event_queue *ev_queue, Element *element)
:
_lock(lock), _ev_queue(ev_queue), _element(element) { }
_ev_queue(ev_queue), _element(element) { }
/*
* Called from main program with taken lock for window content
*/
void handle(Event &ev)
{
int mx = ev.mx - _element->abs_x();
@ -171,7 +165,6 @@ class Window_content : public Element
};
Genode::Lock _lock;
bool _config_alpha;
Content_event_handler _ev_handler;
Fb_texture *_fb;
@ -185,7 +178,7 @@ class Window_content : public Element
bool config_alpha)
:
_config_alpha(config_alpha),
_ev_handler(ev_queue, this, _lock),
_ev_handler(ev_queue, this),
_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)
@ -196,35 +189,24 @@ class Window_content : public Element
event_handler(&_ev_handler);
}
/*
* Accessors, called by the RPC entrypoint. Hence, the need for
* locking.
*/
Genode::Dataspace_capability fb_ds_cap() {
Genode::Lock::Guard guard(_lock);
return _fb->ds.cap();
}
unsigned fb_w() {
Genode::Lock::Guard guard(_lock);
return _fb->w;
}
unsigned fb_h() {
Genode::Lock::Guard guard(_lock);
return _fb->h;
}
void mode_sigh(Genode::Signal_context_capability sigh)
{
Genode::Lock::Guard guard(_lock);
_mode_sigh = sigh;
}
void realloc_framebuffer()
{
Genode::Lock::Guard guard(_lock);
/* skip reallocation if size has not changed */
if (_new_w == _fb->w && _new_h == _fb->h)
return;
@ -246,9 +228,6 @@ class Window_content : public Element
/**
* Element interface
*
* Called indirectly by the Content_event_handler thread, which has
* already taken the lock.
*/
void draw(Canvas *c, int x, int y)
{
@ -264,9 +243,6 @@ class Window_content : public Element
if (_mode_sigh.valid())
Genode::Signal_transmitter(_mode_sigh).submit();
}
void lock() { _lock.lock(); }
void unlock() { _lock.unlock(); }
};
@ -274,9 +250,6 @@ static Window_content *_window_content;
Element *window_content() { return _window_content; }
void lock_window_content() { _window_content->lock(); }
void unlock_window_content() { _window_content->unlock(); }
/***********************************************
** Implementation of the framebuffer service **
@ -342,24 +315,21 @@ namespace Framebuffer
}
void init_services(unsigned fb_w, unsigned fb_h, bool config_alpha)
void init_window_content(unsigned fb_w, unsigned fb_h, bool config_alpha)
{
using namespace Genode;
static Window_content content(fb_w, fb_h, &_ev_queue, config_alpha);
_window_content = &content;
}
/*
* Initialize server entry point
*/
enum { STACK_SIZE = 4096 };
static Cap_connection cap;
static Rpc_entrypoint ep(&cap, STACK_SIZE, "liquid_fb_ep");
void init_services(Genode::Rpc_entrypoint &ep)
{
using namespace Genode;
/*
* Let the entry point serve the framebuffer and input root interfaces
*/
static Framebuffer::Root fb_root(&ep, env()->heap(), content);
static Framebuffer::Root fb_root(&ep, env()->heap(), *_window_content);
static Input::Root input_root(&ep, env()->heap());
/*

View File

@ -14,11 +14,14 @@
#ifndef _SERVICES_H_
#define _SERVICES_H_
#include <base/rpc_server.h>
#include "canvas.h"
#include "elements.h"
extern Element *window_content();
extern void init_services(unsigned fb_w, unsigned fb_h, bool config_alpha);
extern void init_window_content(unsigned fb_w, unsigned fb_h, bool config_alpha);
extern void init_services(Genode::Rpc_entrypoint &ep);
extern void lock_window_content();
extern void unlock_window_content();

View File

@ -106,14 +106,7 @@ append config {
</default-route>
<start name="liquid_fb">
<resource name="RAM" quantum="6M"/>
<config>
<animate>off</animate>
<x>400</x>
<y>270</y>
<width>300</width>
<height>200</height>
<title>Linux</title>
</config>
<config xpos="400" ypos="270" width="300" height="200" animate="off" title="Linux"/>
<provides>
<service name="Input"/>
<service name="Framebuffer"/>

View File

@ -114,12 +114,7 @@ puts $launchpad_cfg_fd {<config>
<route>
<any-service><parent/></any-service>
</route>
<config>
<animate>off</animate>
<x>10</x><y>10</y>
<width>800</width>
<height>600</height>
</config>
<config xpos="10" ypos="10" width="800" height="600" animate="off"/>
</start>
<start name="vancouver">