diff --git a/repos/os/src/server/nit_fb/README b/repos/os/src/server/nit_fb/README
index 6fa0d01b68..ef0f0d0f6b 100644
--- a/repos/os/src/server/nit_fb/README
+++ b/repos/os/src/server/nit_fb/README
@@ -5,7 +5,5 @@ virtual frame buffers on one physical screen. The size and screen position
of each 'nit_fb' instance can be defined via Genode's configuration mechansim
using the following attributes of the 'nit_fb' config node:
-!
+!
-If 'refresh_rate' isn't set the server will not trigger any refresh operations
-by itself.
diff --git a/repos/os/src/server/nit_fb/main.cc b/repos/os/src/server/nit_fb/main.cc
index 57406789d6..037339bbbe 100644
--- a/repos/os/src/server/nit_fb/main.cc
+++ b/repos/os/src/server/nit_fb/main.cc
@@ -5,112 +5,40 @@
*/
/*
- * Copyright (C) 2010-2013 Genode Labs GmbH
+ * Copyright (C) 2010-2014 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
-#include
#include
-#include
-#include
+#include
+#include
#include
#include
#include
+#include
+#include
#include
+namespace Nit_fb {
-namespace Input {
+ struct Main;
- /**
- * Input session applying a position offset to absolute motion events
- */
- class Session_component : public Genode::Rpc_object
- {
- private:
+ using Genode::Static_root;
+ using Genode::Signal_rpc_member;
- /**
- * Offset to be applied to absolute motion events
- */
- int _dx, _dy;
-
- /**
- * Input session, from which we fetch events
- */
- Input::Session *_from_input;
- Genode::Dataspace_capability _from_input_ds;
- Genode::size_t _from_ev_buf_size;
- Input::Event *_from_ev_buf;
-
- /**
- * Input session, to which to provide events
- */
- Genode::Dataspace_capability _to_input_ds;
- Input::Event *_to_ev_buf;
-
- /**
- * Shortcut for mapping an event buffer locally
- */
- Input::Event *_map_ev_buf(Genode::Dataspace_capability ds_cap) {
- return Genode::env()->rm_session()->attach(ds_cap); }
-
- public:
-
- /**
- * Constructor
- *
- * \param dx, dy offset to be added to absolute motion events
- * \param from_input input session from where to get input events
- */
- Session_component(int dx, int dy, Input::Session *from_input)
- :
- _dx(dx), _dy(dy),
- _from_input(from_input),
- _from_input_ds(from_input->dataspace()),
- _from_ev_buf_size(Genode::Dataspace_client(_from_input_ds).size()),
- _from_ev_buf(_map_ev_buf(_from_input_ds)),
- _to_input_ds(Genode::env()->ram_session()->alloc(_from_ev_buf_size)),
- _to_ev_buf(_map_ev_buf(_to_input_ds))
- { }
-
-
- /*****************************
- ** Input session interface **
- *****************************/
-
- Genode::Dataspace_capability dataspace() override { return _to_input_ds; }
-
- bool is_pending() const override { return _from_input->is_pending(); }
-
- int flush() override
- {
- /* flush events at input session */
- int num_events = _from_input->flush();
-
- /* copy events from input buffer to client buffer */
- for (int i = 0; i < num_events; i++) {
- Input::Event e = _from_ev_buf[i];
-
- /* apply view offset to absolute motion events */
- if (e.is_absolute_motion())
- e = Event(e.type(), e.code(),
- e.ax() + _dx, e.ay() + _dy, 0, 0);
- _to_ev_buf[i] = e;
- }
- return num_events;
- }
-
- void sigh(Genode::Signal_context_capability sigh) override
- {
- _from_input->sigh(sigh);
- }
- };
+ typedef Genode::Surface_base::Point Point;
+ typedef Genode::Surface_base::Area Area;
+ typedef Genode::Surface_base::Rect Rect;
}
+/***************
+ ** Utilities **
+ ***************/
+
/**
* Read integer value from config attribute
*/
@@ -123,139 +51,286 @@ long config_arg(const char *attr, long default_value)
}
-struct Config_args
+/**
+ * Translate input event
+ */
+static Input::Event translate_event(Input::Event const ev,
+ Nit_fb::Point const input_origin)
{
- long xpos, ypos, width, height;
- unsigned refresh_rate;
+ switch (ev.type()) {
- Config_args()
- :
- xpos(config_arg("xpos", 0)),
- ypos(config_arg("ypos", 0)),
- width(config_arg("width", 0)),
- height(config_arg("height", 0)),
- refresh_rate(config_arg("refresh_rate", 0))
- { }
+ case Input::Event::MOTION:
+ case Input::Event::PRESS:
+ case Input::Event::RELEASE:
+ case Input::Event::FOCUS:
+ case Input::Event::LEAVE:
+ {
+ Nit_fb::Point abs_pos = Nit_fb::Point(ev.ax(), ev.ay()) + input_origin;
+ return Input::Event(ev.type(), ev.code(),
+ abs_pos.x(), abs_pos.y(), 0, 0);
+ }
+
+ case Input::Event::INVALID:
+ case Input::Event::WHEEL:
+ return ev;
+ }
+ return Input::Event();
+}
+
+
+struct View_updater
+{
+ virtual void update_view() = 0;
};
-int main(int argc, char **argv)
+/*****************************
+ ** Virtualized framebuffer **
+ *****************************/
+
+namespace Framebuffer { struct Session_component; }
+
+
+struct Framebuffer::Session_component : Genode::Rpc_object
{
- using namespace Genode;
+ Nitpicker::Connection &_nitpicker;
+
+ Framebuffer::Session &_nit_fb = *_nitpicker.framebuffer();
+
+ Genode::Signal_context_capability _mode_sigh;
+
+ View_updater &_view_updater;
+
+ Framebuffer::Mode::Format _format = _nitpicker.mode().format();
/*
- * Read arguments from config
+ * Mode as requested by the configuration or by a mode change of our
+ * nitpicker session.
*/
- Config_args cfg;
- long view_x = cfg.xpos, view_y = cfg.ypos,
- view_w = cfg.width, view_h = cfg.height;
+ Framebuffer::Mode _next_mode;
/*
- * Open Nitpicker session
+ * Mode that was returned to the client at the last call of
+ * 'Framebuffer:mode'. The virtual framebuffer must correspond to this
+ * mode. The variable is mutable because it is changed as a side effect of
+ * calling the const 'mode' function.
*/
- static Nitpicker::Connection nitpicker;
+ Framebuffer::Mode mutable _active_mode = _next_mode;
- /*
- * If no config was provided, use screen size of Nitpicker
+ bool _dataspace_is_new = true;
+
+
+ /**
+ * Constructor
*/
- if (view_w == 0 || view_h == 0) {
- Framebuffer::Mode const mode = nitpicker.mode();
- view_w = mode.width(), view_h = mode.height();
+ Session_component(Nitpicker::Connection &nitpicker,
+ View_updater &view_updater,
+ Framebuffer::Mode initial_mode)
+ :
+ _nitpicker(nitpicker), _view_updater(view_updater),
+ _next_mode(initial_mode)
+ { }
+
+ void size(Nitpicker::Area size)
+ {
+ /* ignore calls that don't change the size */
+ if (Nitpicker::Area(_next_mode.width(), _next_mode.height()) == size)
+ return;
+
+ _next_mode = Framebuffer::Mode(size.w(), size.h(), _next_mode.format());
+
+ if (_mode_sigh.valid())
+ Genode::Signal_transmitter(_mode_sigh).submit();
}
- /*
- * Setup virtual framebuffer
- */
- Framebuffer::Mode const mode(view_w, view_h, Framebuffer::Mode::RGB565);
- nitpicker.buffer(mode, false);
+ Nitpicker::Area size() const
+ {
+ return Nitpicker::Area(_active_mode.width(), _active_mode.height());
+ }
- PINF("using xywh=(%ld,%ld,%ld,%ld) refresh_rate=%u",
- view_x, view_y, view_w, view_h, cfg.refresh_rate);
- /*
- * Create Nitpicker view and bring it to front
- */
+ /************************************
+ ** Framebuffer::Session interface **
+ ************************************/
+
+ Genode::Dataspace_capability dataspace() override
+ {
+ _nitpicker.buffer(_active_mode, false);
+
+ /*
+ * We defer the update of the view until the client calls refresh the
+ * next time. This avoid showing the empty buffer as an intermediate
+ * artifact.
+ */
+ _dataspace_is_new = true;
+
+ return _nit_fb.dataspace();
+ }
+
+ Mode mode() const override
+ {
+ _active_mode = _next_mode;
+ return _active_mode;
+ }
+
+ void mode_sigh(Genode::Signal_context_capability sigh) override
+ {
+ _mode_sigh = sigh;
+ }
+
+ void refresh(int x, int y, int w, int h) override
+ {
+ if (_dataspace_is_new) {
+ _view_updater.update_view();
+ _dataspace_is_new = false;
+ }
+
+ _nit_fb.refresh(x, y, w, h);
+ }
+
+ void sync_sigh(Genode::Signal_context_capability sigh) override
+ {
+ _nit_fb.sync_sigh(sigh);
+ }
+};
+
+
+/******************
+ ** Main program **
+ ******************/
+
+struct Nit_fb::Main : View_updater
+{
+ Server::Entrypoint &ep;
+
+ Nitpicker::Connection nitpicker;
+
+ Point position;
+
+ unsigned refresh_rate = 0;
+
Nitpicker::Session::View_handle view = nitpicker.create_view();
- typedef Nitpicker::Session::Command Command;
- Nitpicker::Rect geometry(Nitpicker::Point(view_x, view_y),
- Nitpicker::Area(view_w, view_h));
- nitpicker.enqueue(view, geometry);
- nitpicker.enqueue(view);
- nitpicker.execute();
- /*
- * Initialize server entry point
- */
- enum { STACK_SIZE = 4096 };
- static Cap_connection cap;
- static Rpc_entrypoint ep(&cap, STACK_SIZE, "nitfb_ep");
+ Genode::Attached_dataspace input_ds { nitpicker.input()->dataspace() };
- /*
- * Let the entry point serve the framebuffer and input root interfaces
- */
- static Static_root fb_root(nitpicker.framebuffer_session());
-
- /*
- * Pre-initialize single client input session
- */
- static Input::Session_client nit_input(nitpicker.input_session());
- static Input::Session_component input_session(-view_x, -view_y, &nit_input);
-
- /*
- * Attach input root interface to the entry point
- */
- static Static_root input_root(ep.manage(&input_session));
-
- /*
- * Announce services
- */
- env()->parent()->announce(ep.manage(&fb_root));
- env()->parent()->announce(ep.manage(&input_root));
-
- /*
- * Register signal handler for config changes
- */
- Signal_receiver sig_rec;
- Signal_context sig_ctx;
- config()->sigh(sig_rec.manage(&sig_ctx));
-
- for (;;) {
-
- bool reload_config = false;
-
- if (!cfg.refresh_rate) {
-
- sig_rec.wait_for_signal();
- reload_config = true;
-
- } else {
-
- static Timer::Connection timer;
- static Framebuffer::Session_client nit_fb(nitpicker.framebuffer_session());
-
- timer.msleep(cfg.refresh_rate);
- nit_fb.refresh(0, 0, view_w, view_h);
-
- if (sig_rec.pending()) {
- sig_rec.wait_for_signal();
- reload_config = true;
- }
- }
-
- if (reload_config) {
- try {
- config()->reload();
- cfg = Config_args();
-
- Nitpicker::Rect geometry(Nitpicker::Point(cfg.xpos, cfg.ypos),
- Nitpicker::Area(cfg.width, cfg.height));
- nitpicker.enqueue(view, geometry);
- nitpicker.execute();
- } catch (...) {
- PERR("Error while reloading config");
- }
- }
+ Framebuffer::Mode _initial_mode()
+ {
+ return Framebuffer::Mode(config_arg("width", nitpicker.mode().width()),
+ config_arg("height", nitpicker.mode().height()),
+ nitpicker.mode().format());
}
- return 0;
-}
+ /*
+ * Input and framebuffer sessions provided to our client
+ */
+ Input::Session_component input_session;
+ Framebuffer::Session_component fb_session { nitpicker, *this, _initial_mode() };
+
+ /*
+ * Attach root interfaces to the entry point
+ */
+ Static_root input_root { ep.manage(input_session) };
+ Static_root fb_root { ep.manage(fb_session) };
+
+ /**
+ * View_updater interface
+ */
+ void update_view() override
+ {
+ typedef Nitpicker::Session::Command Command;
+ nitpicker.enqueue(view, Rect(position,
+ fb_session.size()));
+ nitpicker.enqueue(view);
+ nitpicker.execute();
+ }
+
+ void handle_config_update(unsigned)
+ {
+ Genode::config()->reload();
+
+ Framebuffer::Mode const nit_mode = nitpicker.mode();
+
+ position = Point(config_arg("xpos", 0),
+ config_arg("ypos", 0));
+
+ fb_session.size(Area(config_arg("width", nit_mode.width()),
+ config_arg("height", nit_mode.height())));
+
+ /*
+ * Simulate a client call Framebuffer::Session::mode to make the
+ * initial mode the active mode.
+ */
+ fb_session.mode();
+
+ PINF("using xywh=(%d,%d,%d,%d)",
+ position.x(), position.x(),
+ fb_session.size().w(), fb_session.size().h());
+
+ update_view();
+ }
+
+ Signal_rpc_member config_update_dispatcher =
+ { ep, *this, &Main::handle_config_update};
+
+ void handle_mode_update(unsigned)
+ {
+ Framebuffer::Mode const nit_mode = nitpicker.mode();
+
+ fb_session.size(Area(nit_mode.width(), nit_mode.height()));
+ }
+
+ Signal_rpc_member mode_update_dispatcher =
+ { ep, *this, &Main::handle_mode_update};
+
+ void handle_input(unsigned)
+ {
+ Input::Event const * const events = input_ds.local_addr();
+
+ unsigned const num = nitpicker.input()->flush();
+ for (unsigned i = 0; i < num; i++)
+ input_session.submit(translate_event(events[i], position));
+ }
+
+ Signal_rpc_member input_dispatcher =
+ { ep, *this, &Main::handle_input};
+
+ /**
+ * Constructor
+ */
+ Main(Server::Entrypoint &ep) : ep(ep)
+ {
+ input_session.event_queue().enabled(true);
+
+ /*
+ * Announce services
+ */
+ Genode::env()->parent()->announce(ep.manage(fb_root));
+ Genode::env()->parent()->announce(ep.manage(input_root));
+
+ /*
+ * Apply initial configuration
+ */
+ handle_config_update(0);
+
+ /*
+ * Register signal handlers
+ */
+ Genode::config()->sigh(config_update_dispatcher);
+ nitpicker.mode_sigh(mode_update_dispatcher);
+ nitpicker.input()->sigh(input_dispatcher);
+ }
+};
+
+
+/************
+ ** Server **
+ ************/
+
+namespace Server {
+
+ char const *name() { return "nit_fb_ep"; }
+
+ size_t stack_size() { return 4*1024*sizeof(long); }
+
+ void construct(Entrypoint &ep) { static Nit_fb::Main inst(ep); }
+}
diff --git a/repos/os/src/server/nit_fb/target.mk b/repos/os/src/server/nit_fb/target.mk
index 182c61049d..50ce180018 100644
--- a/repos/os/src/server/nit_fb/target.mk
+++ b/repos/os/src/server/nit_fb/target.mk
@@ -1,3 +1,3 @@
TARGET = nit_fb
SRC_CC = main.cc
-LIBS = base config
+LIBS = base config server