nitpicker: New signal handling, bg color config

This patch overhauls the signal handling of nitpicker to clear the way
towards dynamic reconfiguration. Furthermore, it moves the
implementation of the global-keys handling and input utilities to
separate files.
This commit is contained in:
Norman Feske
2013-09-07 23:48:40 +02:00
parent 430582c060
commit 602e36394d
7 changed files with 628 additions and 563 deletions

View File

@ -65,3 +65,10 @@ the "launchpad -> testnit" program is running. As soon as testnit gets started
by launchpad, testnit will receive the events. If the order was reversed, by launchpad, testnit will receive the events. If the order was reversed,
launchpad would always receive the events. launchpad would always receive the events.
Background color
----------------
The background color can be defined via the '<background>' config node:
! <background color="#112233" />

View File

@ -0,0 +1,90 @@
/*
* \brief Global keys handling
* \author Norman Feske
* \date 2013-09-07
*/
/*
* Copyright (C) 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.
*/
/* Genode includes */
#include <os/config.h>
/* local includes */
#include "global_keys.h"
Global_keys::Policy *Global_keys::_lookup_policy(char const *key_name)
{
for (unsigned i = 0; i < NUM_POLICIES; i++)
if (Genode::strcmp(key_name, Input::key_name((Input::Keycode)i)) == 0)
return &_policies[i];
return 0;
}
void Global_keys::apply_config(Session_list &session_list)
{
for (unsigned i = 0; i < NUM_POLICIES; i++)
_policies[i].undefine();
using Genode::Xml_node;
try {
Xml_node node = Genode::config()->xml_node().sub_node("global-keys").sub_node("key");
for (; ; node = node.next("key")) {
if (!node.has_attribute("name")) {
PWRN("attribute 'name' missing in <key> config node");
continue;
}
char name[32]; name[0] = 0;
node.attribute("name").value(name, sizeof(name));
Policy * policy = _lookup_policy(name);
if (!policy) {
PWRN("invalid key name \"%s\"", name);
continue;
}
/* if two policies match, give precedence to policy defined first */
if (policy->defined())
continue;
if (node.has_attribute("operation")) {
Xml_node::Attribute operation = node.attribute("operation");
if (operation.has_value("kill")) {
policy->operation_kill();
continue;
} else if (operation.has_value("xray")) {
policy->operation_xray();
continue;
} else {
char buf[32]; buf[0] = 0;
operation.value(buf, sizeof(buf));
PWRN("unknown operation \"%s\" for key %s", buf, name);
}
continue;
}
if (!node.has_attribute("label")) {
PWRN("missing 'label' attribute for key %s", name);
continue;
}
/* assign policy to matching client session */
for (Session *s = session_list.first(); s; s = s->next())
if (node.attribute("label").has_value(s->label()))
policy->client(s);
}
} catch (Xml_node::Nonexistent_sub_node) { }
}

View File

@ -31,15 +31,24 @@
#include <nitpicker_gfx/pixel_rgb565.h> #include <nitpicker_gfx/pixel_rgb565.h>
#include <nitpicker_gfx/string.h> #include <nitpicker_gfx/string.h>
#include <os/session_policy.h> #include <os/session_policy.h>
#include <os/signal_rpc_dispatcher.h>
/* local includes */ /* local includes */
#include "input.h"
#include "big_mouse.h" #include "big_mouse.h"
#include "background.h" #include "background.h"
#include "user_state.h"
#include "clip_guard.h" #include "clip_guard.h"
#include "mouse_cursor.h" #include "mouse_cursor.h"
#include "chunky_menubar.h" #include "chunky_menubar.h"
namespace Input { using namespace Genode; }
namespace Input { class Session_component; }
namespace Framebuffer { using namespace Genode; }
namespace Framebuffer { class Session_component; }
namespace Nitpicker { using namespace Genode; }
namespace Nitpicker { class Session_component; }
namespace Nitpicker { template <typename> class Root; }
/*************** /***************
** Utilities ** ** Utilities **
@ -53,16 +62,16 @@
*/ */
static Color session_color(char const *session_args) static Color session_color(char const *session_args)
{ {
using namespace Genode;
/* use white by default */ /* use white by default */
Color color = WHITE; Color color = WHITE;
try { try {
Genode::Session_policy policy(session_args); Session_policy policy(session_args);
/* read color attribute */ /* read color attribute */
char color_buf[8]; policy.attribute("color").value(&color);
policy.attribute("color").value(color_buf, sizeof(color_buf));
Genode::ascii_to(color_buf, &color);
} catch (...) { } } catch (...) { }
return color; return color;
@ -207,10 +216,8 @@ class Chunky_dataspace_texture : public Buffer, public Chunky_texture<PT>
** Input sub session ** ** Input sub session **
***********************/ ***********************/
namespace Input { class Input::Session_component : public Genode::Rpc_object<Session>
{
class Session_component : public Genode::Rpc_object<Session>
{
public: public:
enum { MAX_EVENTS = 200 }; enum { MAX_EVENTS = 200 };
@ -220,7 +227,7 @@ namespace Input {
/* /*
* Exported event buffer dataspace * Exported event buffer dataspace
*/ */
Genode::Attached_ram_dataspace _ev_ram_ds; Attached_ram_dataspace _ev_ram_ds;
/* /*
* Local event buffer that is copied * Local event buffer that is copied
@ -232,14 +239,14 @@ namespace Input {
public: public:
static Genode::size_t ev_ds_size() { static size_t ev_ds_size() {
return Genode::align_addr(MAX_EVENTS*sizeof(Event), 12); } return align_addr(MAX_EVENTS*sizeof(Event), 12); }
/** /**
* Constructor * Constructor
*/ */
Session_component(): Session_component():
_ev_ram_ds(Genode::env()->ram_session(), ev_ds_size()), _ev_ram_ds(env()->ram_session(), ev_ds_size()),
_num_ev(0) { } _num_ev(0) { }
/** /**
@ -259,7 +266,7 @@ namespace Input {
** Input session interface ** ** Input session interface **
*****************************/ *****************************/
Genode::Dataspace_capability dataspace() { return _ev_ram_ds.cap(); } Dataspace_capability dataspace() { return _ev_ram_ds.cap(); }
bool is_pending() const { return _num_ev > 0; } bool is_pending() const { return _num_ev > 0; }
@ -275,18 +282,15 @@ namespace Input {
_num_ev = 0; _num_ev = 0;
return ev_cnt; return ev_cnt;
} }
}; };
}
/***************************** /*****************************
** Framebuffer sub session ** ** Framebuffer sub session **
*****************************/ *****************************/
namespace Framebuffer { class Framebuffer::Session_component : public Genode::Rpc_object<Session>
{
class Session_component : public Genode::Rpc_object<Session>
{
private: private:
::Buffer &_buffer; ::Buffer &_buffer;
@ -309,7 +313,7 @@ namespace Framebuffer {
_buffer(buffer), _view_stack(view_stack), _session(session), _buffer(buffer), _view_stack(view_stack), _session(session),
_flush_merger(flush_merger), _framebuffer(framebuffer) { } _flush_merger(flush_merger), _framebuffer(framebuffer) { }
Genode::Dataspace_capability dataspace() { return _buffer.ds_cap(); } Dataspace_capability dataspace() { return _buffer.ds_cap(); }
void release() { } void release() { }
@ -319,7 +323,7 @@ namespace Framebuffer {
_buffer.format()); _buffer.format());
} }
void mode_sigh(Genode::Signal_context_capability) { } void mode_sigh(Signal_context_capability) { }
void refresh(int x, int y, int w, int h) void refresh(int x, int y, int w, int h)
{ {
@ -334,8 +338,7 @@ namespace Framebuffer {
} }
_flush_merger.defer = true; _flush_merger.defer = true;
} }
}; };
}
class View_component : public Genode::List<View_component>::Element, class View_component : public Genode::List<View_component>::Element,
@ -343,9 +346,11 @@ class View_component : public Genode::List<View_component>::Element,
{ {
private: private:
typedef Genode::Rpc_entrypoint Rpc_entrypoint;
View_stack &_view_stack; View_stack &_view_stack;
::View _view; ::View _view;
Genode::Rpc_entrypoint &_ep; Rpc_entrypoint &_ep;
public: public:
@ -353,7 +358,7 @@ class View_component : public Genode::List<View_component>::Element,
* Constructor * Constructor
*/ */
View_component(::Session &session, View_stack &view_stack, View_component(::Session &session, View_stack &view_stack,
Genode::Rpc_entrypoint &ep): Rpc_entrypoint &ep):
_view_stack(view_stack), _view_stack(view_stack),
_view(session, _view(session,
session.stay_top() ? ::View::STAY_TOP : ::View::NOT_STAY_TOP, session.stay_top() ? ::View::STAY_TOP : ::View::NOT_STAY_TOP,
@ -380,7 +385,9 @@ class View_component : public Genode::List<View_component>::Element,
int stack(Nitpicker::View_capability neighbor_cap, bool behind, bool redraw) int stack(Nitpicker::View_capability neighbor_cap, bool behind, bool redraw)
{ {
Genode::Object_pool<View_component>::Guard nvc(_ep.lookup_and_lock(neighbor_cap)); using namespace Genode;
Object_pool<View_component>::Guard nvc(_ep.lookup_and_lock(neighbor_cap));
::View *neighbor_view = nvc ? &nvc->view() : 0; ::View *neighbor_view = nvc ? &nvc->view() : 0;
@ -400,10 +407,9 @@ class View_component : public Genode::List<View_component>::Element,
** Implementation of Nitpicker service ** ** Implementation of Nitpicker service **
*****************************************/ *****************************************/
namespace Nitpicker { class Nitpicker::Session_component : public Genode::Rpc_object<Session>,
public ::Session
class Session_component : public Genode::Rpc_object<Session>, public ::Session {
{
private: private:
/* Framebuffer_session_component */ /* Framebuffer_session_component */
@ -416,11 +422,11 @@ namespace Nitpicker {
* Entrypoint that is used for the views, input session, * Entrypoint that is used for the views, input session,
* and framebuffer session. * and framebuffer session.
*/ */
Genode::Rpc_entrypoint &_ep; Rpc_entrypoint &_ep;
View_stack &_view_stack; View_stack &_view_stack;
Genode::List<View_component> _view_list; List<View_component> _view_list;
/* capabilities for sub sessions */ /* capabilities for sub sessions */
Framebuffer::Session_capability _framebuffer_session_cap; Framebuffer::Session_capability _framebuffer_session_cap;
@ -437,7 +443,7 @@ namespace Nitpicker {
::Buffer &buffer, ::Buffer &buffer,
Texture const &texture, Texture const &texture,
View_stack &view_stack, View_stack &view_stack,
Genode::Rpc_entrypoint &ep, Rpc_entrypoint &ep,
Flush_merger &flush_merger, Flush_merger &flush_merger,
Framebuffer::Session &framebuffer, Framebuffer::Session &framebuffer,
int v_offset, int v_offset,
@ -503,7 +509,7 @@ namespace Nitpicker {
* FIXME: Do not allocate View meta data from Heap! * FIXME: Do not allocate View meta data from Heap!
* Use a heap partition! * Use a heap partition!
*/ */
View_component *view = new (Genode::env()->heap()) View_component *view = new (env()->heap())
View_component(*this, _view_stack, _ep); View_component(*this, _view_stack, _ep);
_view_list.insert(view); _view_list.insert(view);
@ -518,13 +524,13 @@ namespace Nitpicker {
_view_stack.remove_view(vc->view()); _view_stack.remove_view(vc->view());
_ep.dissolve(vc); _ep.dissolve(vc);
_view_list.remove(vc); _view_list.remove(vc);
destroy(Genode::env()->heap(), vc); destroy(env()->heap(), vc);
} }
int background(View_capability view_cap) int background(View_capability view_cap)
{ {
if (_provides_default_bg) { if (_provides_default_bg) {
Genode::Object_pool<View_component>::Guard vc(_ep.lookup_and_lock(view_cap)); Object_pool<View_component>::Guard vc(_ep.lookup_and_lock(view_cap));
vc->view().background(true); vc->view().background(true);
_view_stack.default_background(vc->view()); _view_stack.default_background(vc->view());
return 0; return 0;
@ -534,7 +540,7 @@ namespace Nitpicker {
if (::Session::background()) ::Session::background()->background(false); if (::Session::background()) ::Session::background()->background(false);
/* assign session background */ /* assign session background */
Genode::Object_pool<View_component>::Guard vc(_ep.lookup_and_lock(view_cap)); Object_pool<View_component>::Guard vc(_ep.lookup_and_lock(view_cap));
::Session::background(&vc->view()); ::Session::background(&vc->view());
/* switch background view to background mode */ /* switch background view to background mode */
@ -542,12 +548,12 @@ namespace Nitpicker {
return 0; return 0;
} }
}; };
template <typename PT> template <typename PT>
class Root : public Genode::Root_component<Session_component> class Nitpicker::Root : public Genode::Root_component<Session_component>
{ {
private: private:
Session_list &_session_list; Session_list &_session_list;
@ -563,36 +569,36 @@ namespace Nitpicker {
Session_component *_create_session(const char *args) Session_component *_create_session(const char *args)
{ {
PINF("create session with args: %s\n", args); PINF("create session with args: %s\n", args);
Genode::size_t ram_quota = Genode::Arg_string::find_arg(args, "ram_quota").ulong_value(0); size_t ram_quota = Arg_string::find_arg(args, "ram_quota").ulong_value(0);
int v_offset = _default_v_offset; int v_offset = _default_v_offset;
/* determine buffer size of the session */ /* determine buffer size of the session */
Area size(Genode::Arg_string::find_arg(args, "fb_width" ).long_value(_scr_size.w()), Area size(Arg_string::find_arg(args, "fb_width" ).long_value(_scr_size.w()),
Genode::Arg_string::find_arg(args, "fb_height").long_value(_scr_size.h() - v_offset)); Arg_string::find_arg(args, "fb_height").long_value(_scr_size.h() - v_offset));
char label_buf[::Session::LABEL_LEN]; char label_buf[::Session::LABEL_LEN];
Genode::Arg_string::find_arg(args, "label").string(label_buf, sizeof(label_buf), "<unlabeled>"); Arg_string::find_arg(args, "label").string(label_buf, sizeof(label_buf), "<unlabeled>");
bool use_alpha = Genode::Arg_string::find_arg(args, "alpha").bool_value(false); bool use_alpha = Arg_string::find_arg(args, "alpha").bool_value(false);
bool stay_top = Genode::Arg_string::find_arg(args, "stay_top").bool_value(false); bool stay_top = Arg_string::find_arg(args, "stay_top").bool_value(false);
Genode::size_t texture_num_bytes = Chunky_dataspace_texture<PT>::calc_num_bytes(size, use_alpha); size_t texture_num_bytes = Chunky_dataspace_texture<PT>::calc_num_bytes(size, use_alpha);
Genode::size_t required_quota = texture_num_bytes size_t required_quota = texture_num_bytes
+ Input::Session_component::ev_ds_size(); + Input::Session_component::ev_ds_size();
if (ram_quota < required_quota) { if (ram_quota < required_quota) {
PWRN("Insufficient dontated ram_quota (%zd bytes), require %zd bytes", PWRN("Insufficient dontated ram_quota (%zd bytes), require %zd bytes",
ram_quota, required_quota); ram_quota, required_quota);
throw Genode::Root::Quota_exceeded(); throw Root::Quota_exceeded();
} }
/* allocate texture */ /* allocate texture */
Chunky_dataspace_texture<PT> *cdt; Chunky_dataspace_texture<PT> *cdt;
cdt = new (md_alloc()) Chunky_dataspace_texture<PT>(size, use_alpha); cdt = new (md_alloc()) Chunky_dataspace_texture<PT>(size, use_alpha);
bool provides_default_bg = (Genode::strcmp(label_buf, "backdrop") == 0); bool provides_default_bg = (strcmp(label_buf, "backdrop") == 0);
Session_component *session = new (md_alloc()) Session_component *session = new (md_alloc())
Session_component(label_buf, *cdt, *cdt, _view_stack, *ep(), Session_component(label_buf, *cdt, *cdt, _view_stack, *ep(),
@ -628,251 +634,43 @@ namespace Nitpicker {
* Constructor * Constructor
*/ */
Root(Session_list &session_list, Global_keys &global_keys, Root(Session_list &session_list, Global_keys &global_keys,
Genode::Rpc_entrypoint &session_ep, Area scr_size, Rpc_entrypoint &session_ep, Area scr_size,
View_stack &view_stack, Genode::Allocator &md_alloc, View_stack &view_stack, Allocator &md_alloc,
Flush_merger &flush_merger, Flush_merger &flush_merger,
Framebuffer::Session &framebuffer, int default_v_offset) Framebuffer::Session &framebuffer, int default_v_offset)
: :
Genode::Root_component<Session_component>(&session_ep, &md_alloc), Root_component<Session_component>(&session_ep, &md_alloc),
_session_list(session_list), _global_keys(global_keys), _session_list(session_list), _global_keys(global_keys),
_scr_size(scr_size), _view_stack(view_stack), _flush_merger(flush_merger), _scr_size(scr_size), _view_stack(view_stack), _flush_merger(flush_merger),
_framebuffer(framebuffer), _default_v_offset(default_v_offset) { } _framebuffer(framebuffer), _default_v_offset(default_v_offset) { }
};
}
/*******************
** Input handler **
*******************/
struct Input_handler
{
GENODE_RPC(Rpc_do_input_handling, void, do_input_handling);
GENODE_RPC_INTERFACE(Rpc_do_input_handling);
}; };
class Input_handler_component : public Genode::Rpc_object<Input_handler, void wait_and_dispatch_one_signal(Genode::Signal_receiver &sig_rec)
Input_handler_component>
{ {
private: using namespace Genode;
User_state &_user_state;
View &_mouse_cursor;
Area const _mouse_size;
Flush_merger &_flush_merger;
Input::Session &_input;
Input::Event *_ev_buf;
Framebuffer::Session &_framebuffer;
Genode::Signal_receiver &_sig_rec;
public:
/**
* Constructor
*/
Input_handler_component(User_state &user_state, View &mouse_cursor,
Area mouse_size, Flush_merger &flush_merger,
Input::Session &input,
Framebuffer::Session &framebuffer,
Genode::Signal_receiver &sig_rec)
:
_user_state(user_state), _mouse_cursor(mouse_cursor),
_mouse_size(mouse_size), _flush_merger(flush_merger),
_input(input),
_ev_buf(Genode::env()->rm_session()->attach(_input.dataspace())),
_framebuffer(framebuffer), _sig_rec(sig_rec)
{ }
/**
* Determine number of events that can be merged into one
*
* \param ev pointer to first event array element to check
* \param max size of the event array
* \return number of events subjected to merge
*/
unsigned _num_consecutive_events(Input::Event const *ev, unsigned max)
{
if (max < 1) return 0;
if (ev->type() != Input::Event::MOTION) return 1;
bool first_is_absolute = ev->is_absolute_motion();
/* iterate until we get a different event type, start at second */
unsigned cnt = 1;
for (ev++ ; cnt < max; cnt++, ev++) {
if (ev->type() != Input::Event::MOTION) break;
if (first_is_absolute != ev->is_absolute_motion()) break;
}
return cnt;
}
/**
* Merge consecutive motion events
*
* \param ev event array to merge
* \param n number of events to merge
* \return merged motion event
*/
static Input::Event _merge_motion_events(Input::Event const *ev, unsigned n)
{
Input::Event res;
for (unsigned i = 0; i < n; i++, ev++)
res = Input::Event(Input::Event::MOTION, 0, ev->ax(), ev->ay(),
res.rx() + ev->rx(), res.ry() + ev->ry());
return res;
}
void _import_input_events(unsigned num_ev)
{
/*
* Take events from input event buffer, merge consecutive motion
* events, and pass result to the user state.
*/
for (unsigned src_ev_cnt = 0; src_ev_cnt < num_ev; src_ev_cnt++) {
Input::Event *e = &_ev_buf[src_ev_cnt];
Input::Event curr = *e;
if (e->type() == Input::Event::MOTION) {
unsigned n = _num_consecutive_events(e, num_ev - src_ev_cnt);
curr = _merge_motion_events(e, n);
/* skip merged events */
src_ev_cnt += n - 1;
}
/* /*
* If subsequential relative motion events are merged to * We call the signal dispatcher outside of the scope of 'Signal'
* a zero-motion event, drop it. Otherwise, it would be * object because we block the RPC interface in the input handler
* misinterpreted as absolute event pointing to (0, 0). * when the kill mode gets actived. While kill mode is active, we
* do not serve incoming RPC requests but we need to stay responsive
* to user input. Hence, we wait for signals in the input dispatcher
* in this case. An already existing 'Signal' object would lock the
* signal receiver and thereby prevent this nested way of signal
* handling.
*/ */
if (e->is_relative_motion() && curr.rx() == 0 && curr.ry() == 0) Signal_dispatcher_base *dispatcher = 0;
continue; unsigned num = 0;
/* pass event to user state */
_user_state.handle_event(curr);
}
}
/**
* This function is called periodically from the timer loop
*/
void do_input_handling()
{ {
do { Signal sig = sig_rec.wait_for_signal();
Point old_mouse_pos = _user_state.mouse_pos(); dispatcher = dynamic_cast<Signal_dispatcher_base *>(sig.context());
num = sig.num();
/* handle batch of pending events */
if (_input.is_pending())
_import_input_events(_input.flush());
Point new_mouse_pos = _user_state.mouse_pos();
/* update mouse cursor */
if (old_mouse_pos != new_mouse_pos)
_user_state.viewport(_mouse_cursor,
Rect(new_mouse_pos, _mouse_size),
Point(), true);
/* flush dirty pixels to physical frame buffer */
if (_flush_merger.defer == false) {
Rect r = _flush_merger.to_be_flushed();
if (r.valid())
_framebuffer.refresh(r.x1(), r.y1(), r.w(), r.h());
_flush_merger.reset();
}
_flush_merger.defer = false;
/*
* In kill mode, we never leave the dispatch function to block
* RPC calls from Nitpicker clients. We block for signals here
* to make the spinning for the end of the kill mode less
* painful for all non-blocked processes.
*/
if (_user_state.kill())
_sig_rec.wait_for_signal();
} while (_user_state.kill());
}
};
typedef Pixel_rgb565 PT; /* physical pixel type */
/*****************************
** Handling of global keys **
*****************************/
Global_keys::Policy *Global_keys::_lookup_policy(char const *key_name)
{
for (unsigned i = 0; i < NUM_POLICIES; i++)
if (Genode::strcmp(key_name, Input::key_name((Input::Keycode)i)) == 0)
return &_policies[i];
return 0;
}
void Global_keys::apply_config(Session_list &session_list)
{
for (unsigned i = 0; i < NUM_POLICIES; i++)
_policies[i].undefine();
using Genode::Xml_node;
try {
Xml_node node = Genode::config()->xml_node().sub_node("global-keys").sub_node("key");
for (; ; node = node.next("key")) {
if (!node.has_attribute("name")) {
PWRN("attribute 'name' missing in <key> config node");
continue;
} }
char name[32]; name[0] = 0; if (dispatcher)
node.attribute("name").value(name, sizeof(name)); dispatcher->dispatch(num);
Policy * policy = _lookup_policy(name);
if (!policy) {
PWRN("invalid key name \"%s\"", name);
continue;
}
/* if two policies match, give precedence to policy defined first */
if (policy->defined())
continue;
if (node.has_attribute("operation")) {
Xml_node::Attribute operation = node.attribute("operation");
if (operation.has_value("kill")) {
policy->operation_kill();
continue;
} else if (operation.has_value("xray")) {
policy->operation_xray();
continue;
} else {
char buf[32]; buf[0] = 0;
operation.value(buf, sizeof(buf));
PWRN("unknown operation \"%s\" for key %s", buf, name);
}
continue;
}
if (!node.has_attribute("label")) {
PWRN("missing 'label' attribute for key %s", name);
continue;
}
/* assign policy to matching client session */
for (Session *s = session_list.first(); s; s = s->next())
if (node.attribute("label").has_value(s->label()))
policy->client(s);
}
} catch (Xml_node::Nonexistent_sub_node) { }
} }
@ -880,6 +678,9 @@ void Global_keys::apply_config(Session_list &session_list)
** Main program ** ** Main program **
******************/ ******************/
typedef Pixel_rgb565 PT; /* physical pixel type */
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
using namespace Genode; using namespace Genode;
@ -891,6 +692,9 @@ int main(int argc, char **argv)
static Input::Connection input; static Input::Connection input;
static Cap_connection cap; static Cap_connection cap;
static Input::Event * const ev_buf =
env()->rm_session()->attach(input.dataspace());
/* /*
* Initialize server entry point * Initialize server entry point
*/ */
@ -922,13 +726,6 @@ int main(int argc, char **argv)
static Session_list session_list; static Session_list session_list;
/*
* Apply initial global-key policy to turn X-ray and kill keys into
* effect. The policy will be updated each time the config changes,
* or when a session appears or disappears.
*/
global_keys.apply_config(session_list);
static User_state user_state(global_keys, screen, menubar); static User_state user_state(global_keys, screen, menubar);
/* /*
@ -958,35 +755,98 @@ int main(int argc, char **argv)
screen, framebuffer, screen, framebuffer,
MENUBAR_HEIGHT); MENUBAR_HEIGHT);
env()->parent()->announce(ep.manage(&np_root)); static Signal_receiver sig_rec;
/* /*
* Schedule periodic timer signals every 10 milliseconds * Configuration-update dispatcher, executed in the context of the RPC
* entrypoint.
*
* In addition to installing the signal dispatcher, we trigger first signal
* manually to turn the initial configuration into effect.
*/
static auto config_func = [&](unsigned)
{
config()->reload();
global_keys.apply_config(session_list);
try {
config()->xml_node().sub_node("background")
.attribute("color").value(&background.color);
} catch (...) { }
user_state.update_all_views();
};
auto config_dispatcher = signal_rpc_dispatcher(config_func);
Signal_context_capability config_sigh = config_dispatcher.manage(sig_rec, ep);
config()->sigh(config_sigh);
Signal_transmitter(config_sigh).submit();
/*
* Input dispatcher, executed in the contect of the RPC entrypoint
*/
static auto input_func = [&](unsigned num)
{
/*
* If kill mode is already active, we got recursively called from
* within this 'input_func' (via 'wait_and_dispatch_one_signal').
* In this case, return immediately. New input events will be
* processed in the local 'do' loop.
*/
if (user_state.kill())
return;
do {
Point const old_mouse_pos = user_state.mouse_pos();
/* handle batch of pending events */
if (input.is_pending())
import_input_events(ev_buf, input.flush(), user_state);
Point const new_mouse_pos = user_state.mouse_pos();
/* update mouse cursor */
if (old_mouse_pos != new_mouse_pos)
user_state.viewport(mouse_cursor,
Rect(new_mouse_pos, mouse_size),
Point(), true);
/* flush dirty pixels to physical frame buffer */
if (screen.defer == false) {
Rect const r = screen.to_be_flushed();
if (r.valid())
framebuffer.refresh(r.x1(), r.y1(), r.w(), r.h());
screen.reset();
}
screen.defer = false;
/*
* In kill mode, we do not leave the dispatch function in order to
* block RPC calls from Nitpicker clients. We block for signals
* here to stay responsive to user input and configuration changes.
* Nested calls of 'input_func' are prevented by the condition
* check for 'user_state.kill()' at the beginning of the handler.
*/
if (user_state.kill())
wait_and_dispatch_one_signal(sig_rec);
} while (user_state.kill());
};
auto input_dispatcher = signal_rpc_dispatcher(input_func);
/*
* Dispatch input on periodic timer signals every 10 milliseconds
*/ */
static Timer::Connection timer; static Timer::Connection timer;
static Signal_receiver sig_rec; timer.sigh(input_dispatcher.manage(sig_rec, ep));
Signal_context timer_sig_ctx;
timer.sigh(sig_rec.manage(&timer_sig_ctx));
timer.trigger_periodic(10*1000); timer.trigger_periodic(10*1000);
/* env()->parent()->announce(ep.manage(&np_root));
* Initialize input handling
*
* We serialize the input handling with the client interfaces via
* Nitpicker's entry point. For this, we implement the input handling
* as a 'Rpc_object' and perform RPC calls to this local object in a
* periodic fashion.
*/
static Input_handler_component
input_handler(user_state, mouse_cursor, mouse_size,
screen, input, framebuffer, sig_rec);
Capability<Input_handler> input_handler_cap = ep.manage(&input_handler); for (;;)
wait_and_dispatch_one_signal(sig_rec);
for (;;) {
sig_rec.wait_for_signal();
input_handler_cap.call<Input_handler::Rpc_do_input_handling>();
}
return 0; return 0;
} }

View File

@ -3,9 +3,13 @@ LIBS = base blit config
SRC_CC = main.cc \ SRC_CC = main.cc \
view_stack.cc \ view_stack.cc \
view.cc \ view.cc \
user_state.cc user_state.cc \
global_keys.cc
SRC_BIN = default.tff SRC_BIN = default.tff
# enable C++11 support
CC_CXX_OPT += -std=gnu++11
INC_DIR = $(PRG_DIR)/../include \ INC_DIR = $(PRG_DIR)/../include \
$(PRG_DIR)/../data $(PRG_DIR)/../data

View File

@ -19,6 +19,8 @@
struct Background : private Texture, Session, View struct Background : private Texture, Session, View
{ {
Color color;
/* /*
* The background uses no texture. Therefore * The background uses no texture. Therefore
* we can pass a null pointer as texture argument * we can pass a null pointer as texture argument
@ -28,7 +30,8 @@ struct Background : private Texture, Session, View
: :
Texture(Area(0, 0)), Session("", *this, 0, BLACK), Texture(Area(0, 0)), Session("", *this, 0, BLACK),
View(*this, View::NOT_STAY_TOP, View::NOT_TRANSPARENT, View(*this, View::NOT_STAY_TOP, View::NOT_TRANSPARENT,
View::BACKGROUND, Rect(Point(0, 0), size)) View::BACKGROUND, Rect(Point(0, 0), size)),
color(25, 37, 50)
{ } { }
@ -49,7 +52,7 @@ struct Background : private Texture, Session, View
void draw(Canvas &canvas, Mode const &mode) const void draw(Canvas &canvas, Mode const &mode) const
{ {
Clip_guard clip_guard(canvas, *this); Clip_guard clip_guard(canvas, *this);
canvas.draw_box(*this, Color(25, 37, 50)); canvas.draw_box(*this, color);
} }
}; };

View File

@ -0,0 +1,98 @@
/*
* \brief Input handling utilities
* \author Norman Feske
* \date 2013-09-07
*/
/*
* Copyright (C) 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.
*/
#ifndef _INPUT_H_
#define _INPUT_H_
/* Genode includes */
#include <input/event.h>
/* local includes */
#include "user_state.h"
/**
* Determine number of events that can be merged into one
*
* \param ev pointer to first event array element to check
* \param max size of the event array
* \return number of events subjected to merge
*/
static unsigned num_consecutive_events(Input::Event const *ev, unsigned max)
{
if (max < 1) return 0;
if (ev->type() != Input::Event::MOTION) return 1;
bool first_is_absolute = ev->is_absolute_motion();
/* iterate until we get a different event type, start at second */
unsigned cnt = 1;
for (ev++ ; cnt < max; cnt++, ev++) {
if (ev->type() != Input::Event::MOTION) break;
if (first_is_absolute != ev->is_absolute_motion()) break;
}
return cnt;
}
/**
* Merge consecutive motion events
*
* \param ev event array to merge
* \param n number of events to merge
* \return merged motion event
*/
static Input::Event merge_motion_events(Input::Event const *ev, unsigned n)
{
Input::Event res;
for (unsigned i = 0; i < n; i++, ev++)
res = Input::Event(Input::Event::MOTION, 0, ev->ax(), ev->ay(),
res.rx() + ev->rx(), res.ry() + ev->ry());
return res;
}
static void import_input_events(Input::Event *ev_buf, unsigned num_ev,
User_state &user_state)
{
/*
* Take events from input event buffer, merge consecutive motion
* events, and pass result to the user state.
*/
for (unsigned src_ev_cnt = 0; src_ev_cnt < num_ev; src_ev_cnt++) {
Input::Event *e = &ev_buf[src_ev_cnt];
Input::Event curr = *e;
if (e->type() == Input::Event::MOTION) {
unsigned n = num_consecutive_events(e, num_ev - src_ev_cnt);
curr = merge_motion_events(e, n);
/* skip merged events */
src_ev_cnt += n - 1;
}
/*
* If subsequential relative motion events are merged to
* a zero-motion event, drop it. Otherwise, it would be
* misinterpreted as absolute event pointing to (0, 0).
*/
if (e->is_relative_motion() && curr.rx() == 0 && curr.ry() == 0)
continue;
/* pass event to user state */
user_state.handle_event(curr);
}
}
#endif /* _INPUT_H_ */

View File

@ -17,6 +17,9 @@
/* Genode includes */ /* Genode includes */
#include <util/list.h> #include <util/list.h>
#include <util/string.h> #include <util/string.h>
#include <nitpicker_gfx/color.h>
#include <nitpicker_gfx/geometry.h>
#include <nitpicker_gfx/canvas.h>
class Texture; class Texture;
class View; class View;