diff --git a/repos/gems/run/wm.run b/repos/gems/run/wm.run
new file mode 100644
index 0000000000..7bc8fdc0e1
--- /dev/null
+++ b/repos/gems/run/wm.run
@@ -0,0 +1,224 @@
+#
+# Build
+#
+
+set build_components {
+ core init
+ drivers/timer
+ server/wm app/decorator app/floating_window_layouter
+ server/nitpicker app/pointer server/report_rom
+ drivers/framebuffer drivers/pci drivers/input
+ test/nitpicker
+ app/launchpad
+ server/nit_fb
+}
+
+lappend_if [have_spec usb] build_components drivers/usb
+lappend_if [have_spec gpio] build_components drivers/gpio
+lappend_if [have_spec imx53] build_components drivers/platform
+lappend_if [have_spec exynos5] build_components drivers/platform
+lappend_if [have_spec platform_rpi] build_components drivers/platform
+
+build $build_components
+
+create_boot_directory
+
+#
+# Generate config
+#
+
+append config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+
+append_if [have_spec sdl] config {
+
+
+
+
+
+
+ }
+
+append_if [have_spec pci] config {
+
+
+
+ }
+
+append_if [have_spec framebuffer] config {
+
+
+
+ }
+
+append_if [have_spec gpio] config {
+
+
+
+
+ }
+
+append_if [have_spec exynos5] config {
+
+
+
+
+ }
+
+append_if [have_spec platform_rpi] config {
+
+
+
+
+ }
+
+append_if [have_spec imx53] config {
+
+
+
+
+
+
+
+
+
+ }
+
+append_if [have_spec ps2] config {
+
+
+
+ }
+
+append_if [expr ![have_spec ps2] && [have_spec usb]] config {
+
+
+
+
+ }
+
+append config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}
+
+install_config $config
+
+
+#
+# Boot modules
+#
+
+# generic modules
+set boot_modules {
+ core init
+ timer
+ wm decorator
+ floating_window_layouter
+ nitpicker pointer report_rom
+ testnit launchpad nit_fb
+}
+
+# platform-specific modules
+lappend_if [have_spec linux] boot_modules fb_sdl
+lappend_if [have_spec pci] boot_modules pci_drv
+lappend_if [have_spec ps2] boot_modules ps2_drv
+lappend_if [have_spec framebuffer] boot_modules fb_drv
+lappend_if [have_spec usb] boot_modules usb_drv
+lappend_if [have_spec gpio] boot_modules gpio_drv
+lappend_if [have_spec imx53] boot_modules platform_drv
+lappend_if [have_spec exynos5] boot_modules platform_drv
+lappend_if [have_spec platform_rpi] boot_modules platform_drv
+lappend_if [have_spec imx53] boot_modules input_drv
+
+build_boot_image $boot_modules
+
+append qemu_args " -m 256 "
+
+run_genode_until forever
diff --git a/repos/gems/src/app/floating_window_layouter/main.cc b/repos/gems/src/app/floating_window_layouter/main.cc
new file mode 100644
index 0000000000..81bb0cf9d2
--- /dev/null
+++ b/repos/gems/src/app/floating_window_layouter/main.cc
@@ -0,0 +1,683 @@
+/*
+ * \brief Floating window layouter
+ * \author Norman Feske
+ * \date 2013-02-14
+ */
+
+/*
+ * Copyright (C) 2013-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
+
+
+namespace Floating_window_layouter {
+
+ using namespace Genode;
+ struct Main;
+ class Window;
+
+ typedef Decorator::Point Point;
+ typedef Decorator::Area Area;
+ typedef Decorator::Rect Rect;
+
+ using Decorator::attribute;
+ using Decorator::string_attribute;
+ using Decorator::area_attribute;
+
+
+ static Xml_node xml_lookup_window_by_id(Xml_node node, unsigned const id)
+ {
+ char const *tag = "window";
+ char const *id_attr = "id";
+
+ for (node = node.sub_node(tag); ; node = node.next(tag))
+ if (attribute(node, id_attr, 0UL) == id)
+ return node;
+
+ throw Xml_node::Nonexistent_sub_node();
+ }
+
+
+ /**
+ * Return true if compound XML node contains a sub node with ID
+ */
+ static bool xml_contains_window_node_with_id(Xml_node node,
+ unsigned const id)
+ {
+ try { xml_lookup_window_by_id(node, id); return true; }
+ catch (Xml_node::Nonexistent_sub_node) { return false; }
+ }
+}
+
+
+class Floating_window_layouter::Window : public List::Element
+{
+ public:
+
+ typedef String<256> Title;
+
+ struct Element
+ {
+ enum Type { UNDEFINED, TITLE, LEFT, RIGHT, TOP, BOTTOM,
+ TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT };
+
+ Type type;
+
+ char const *name() const
+ {
+ switch (type) {
+ case UNDEFINED: return "";
+ case TITLE: return "title";
+ case LEFT: return "left";
+ case RIGHT: return "right";
+ case TOP: return "top";
+ case BOTTOM: return "bottom";
+ case TOP_LEFT: return "top_left";
+ case TOP_RIGHT: return "top_right";
+ case BOTTOM_LEFT: return "bottom_left";
+ case BOTTOM_RIGHT: return "bottom_right";
+ }
+ return "";
+ }
+
+ Element(Type type) : type(type) { }
+
+ bool operator != (Element const &other) const { return other.type != type; }
+ };
+
+ private:
+
+ unsigned const _id = 0;
+
+ Title _title;
+
+ Rect _geometry;
+
+ /**
+ * Window geometry at the start of the current drag operation
+ */
+ Rect _orig_geometry;
+
+ /**
+ * Size as desired by the user during resize drag operations
+ */
+ Area _requested_size;
+
+ /*
+ * Number of times the window has been topped. This value is used by
+ * the decorator to detect the need for bringing the window to the
+ * front of nitpicker's global view stack even if the stacking order
+ * stays the same within the decorator instance. This is important in
+ * the presence of more than a single decorator.
+ */
+ unsigned _topped_cnt = 0;
+
+ bool _drag_left_border = false;
+ bool _drag_right_border = false;
+ bool _drag_top_border = false;
+ bool _drag_bottom_border = false;
+
+ public:
+
+ Window(unsigned id) : _id(id) { }
+
+ bool has_id(unsigned id) const { return id == _id; }
+
+ unsigned id() const { return _id; }
+
+ void title(Title const &title) { _title = title; }
+
+ void geometry(Rect geometry) { _geometry = geometry; }
+
+ Point position() const { return _geometry.p1(); }
+
+ void position(Point pos) { _geometry = Rect(pos, _geometry.area()); }
+
+ /**
+ * Return true if user drags a window border
+ */
+ bool _drag_border() const
+ {
+ return _drag_left_border || _drag_right_border
+ || _drag_top_border || _drag_bottom_border;
+ }
+
+ /**
+ * Define window size
+ *
+ * This function is called when the window-list model changes.
+ */
+ void size(Area size)
+ {
+ if (!_drag_border()) {
+ _geometry = Rect(_geometry.p1(), size);
+ return;
+ }
+
+ Point p1 = _geometry.p1(), p2 = _geometry.p2();
+
+ if (_drag_left_border)
+ p1 = Point(p2.x() - size.w() + 1, p1.y());
+
+ if (_drag_right_border)
+ p2 = Point(p1.x() + size.w() - 1, p2.y());
+
+ if (_drag_top_border)
+ p1 = Point(p1.x(), p2.y() - size.h() + 1);
+
+ if (_drag_bottom_border)
+ p2 = Point(p2.x(), p1.y() + size.h() - 1);
+
+ _geometry = Rect(p1, p2);
+ }
+
+ Area size() const { return _geometry.area(); }
+
+ Area requested_size() const { return _requested_size; }
+
+ void serialize(Xml_generator &xml, bool focused, Element highlight)
+ {
+ xml.node("window", [&]() {
+ xml.attribute("id", _id);
+ xml.attribute("title", _title.string());
+ xml.attribute("xpos", _geometry.x1());
+ xml.attribute("ypos", _geometry.y1());
+ xml.attribute("width", _geometry.w());
+ xml.attribute("height", _geometry.h());
+ xml.attribute("topped", _topped_cnt);
+
+ if (focused)
+ xml.attribute("focused", "yes");
+
+ if (highlight.type != Element::UNDEFINED) {
+ xml.node("highlight", [&] () {
+ xml.node(highlight.name());
+ });
+ }
+ });
+ }
+
+ /**
+ * Called when the user starts dragging a window element
+ */
+ void initiate_drag_operation(Window::Element element)
+ {
+ _drag_left_border = (element.type == Window::Element::LEFT)
+ || (element.type == Window::Element::TOP_LEFT)
+ || (element.type == Window::Element::BOTTOM_LEFT);
+
+ _drag_right_border = (element.type == Window::Element::RIGHT)
+ || (element.type == Window::Element::TOP_RIGHT)
+ || (element.type == Window::Element::BOTTOM_RIGHT);
+
+ _drag_top_border = (element.type == Window::Element::TOP)
+ || (element.type == Window::Element::TOP_LEFT)
+ || (element.type == Window::Element::TOP_RIGHT);
+
+ _drag_bottom_border = (element.type == Window::Element::BOTTOM)
+ || (element.type == Window::Element::BOTTOM_LEFT)
+ || (element.type == Window::Element::BOTTOM_RIGHT);
+
+ _orig_geometry = _geometry;
+
+ _requested_size = _geometry.area();
+ }
+
+ void apply_drag_operation(Point offset)
+ {
+ if (!_drag_border())
+ position(_orig_geometry.p1() + offset);
+
+ int requested_w = _orig_geometry.w(),
+ requested_h = _orig_geometry.h();
+
+ if (_drag_left_border) requested_w -= offset.x();
+ if (_drag_right_border) requested_w += offset.x();
+ if (_drag_top_border) requested_h -= offset.y();
+ if (_drag_bottom_border) requested_h += offset.y();
+
+ _requested_size = Area(max(1, requested_w), max(1, requested_h));
+ }
+
+ void finalize_drag_operation()
+ {
+ _requested_size = _geometry.area();
+ }
+
+ void topped() { _topped_cnt++; }
+};
+
+
+struct Floating_window_layouter::Main
+{
+ Signal_receiver &sig_rec;
+
+ List windows;
+
+ unsigned focused_window_id = 0;
+ unsigned key_cnt = 0;
+
+ Window::Element hovered_element = Window::Element::UNDEFINED;
+
+ bool drag_state = false;
+
+ Window *lookup_window_by_id(unsigned id)
+ {
+ for (Window *w = windows.first(); w; w = w->next())
+ if (w->has_id(id))
+ return w;
+
+ return nullptr;
+ }
+
+
+ /**
+ * Install handler for responding to window-list changes
+ */
+ void handle_window_list_update(unsigned);
+
+ Signal_dispatcher window_list_dispatcher = {
+ sig_rec, *this, &Main::handle_window_list_update };
+
+ Attached_rom_dataspace window_list { "window_list" };
+
+
+ /**
+ * Install handler for responding to hover changes
+ */
+ void handle_hover_update(unsigned);
+
+ Signal_dispatcher hover_dispatcher = {
+ sig_rec, *this, &Main::handle_hover_update };
+
+ Attached_rom_dataspace hover { "hover" };
+
+
+ /**
+ * Install handler for responding to user input
+ */
+ void handle_input(unsigned);
+
+ Signal_dispatcher input_dispatcher = {
+ sig_rec, *this, &Main::handle_input };
+
+ Input::Connection input;
+
+ Attached_dataspace input_ds { input.dataspace() };
+
+ Reporter window_layout_reporter = { "window_layout" };
+ Reporter resize_request_reporter = { "resize_request" };
+ Reporter focus_reporter = { "focus" };
+
+ unsigned dragged_window_id = 0;
+
+ Point pointer_clicked;
+ Point pointer_last;
+ Point pointer_curr;
+
+ void import_window_list(Xml_node);
+ void generate_window_layout_model();
+ void generate_resize_request_model();
+ void generate_focus_model();
+ void initiate_window_drag(Window &window);
+
+ /**
+ * Constructor
+ */
+ Main(Signal_receiver &sig_rec) : sig_rec(sig_rec)
+ {
+ window_list.sigh(window_list_dispatcher);
+
+ hover.sigh(hover_dispatcher);
+
+ input.sigh(input_dispatcher);
+
+ window_layout_reporter.enabled(true);
+ resize_request_reporter.enabled(true);
+ focus_reporter.enabled(true);
+ }
+};
+
+
+void Floating_window_layouter::Main::import_window_list(Xml_node window_list_xml)
+{
+ char const *tag = "window";
+
+ /*
+ * Remove windows from layout that are no longer in the window list
+ */
+ for (Window *win = windows.first(), *next = 0; win; win = next) {
+ next = win->next();
+ if (!xml_contains_window_node_with_id(window_list_xml, win->id())) {
+ windows.remove(win);
+ destroy(env()->heap(), win);
+ }
+ }
+
+ /*
+ * Update window attributes, add new windows to the layout
+ */
+ try {
+ for (Xml_node node = window_list_xml.sub_node(tag); ; node = node.next(tag)) {
+
+ unsigned long id = 0;
+ node.attribute("id").value(&id);
+
+ Window *win = lookup_window_by_id(id);
+ if (!win) {
+ win = new (env()->heap()) Window(id);
+ windows.insert(win);
+
+ /*
+ * Define initial window position
+ */
+ win->position(Point(150*id % 800, 30 + (100*id % 500)));
+ }
+
+ win->size(area_attribute(node));
+ win->title(string_attribute(node, "title", Window::Title("untitled")));
+ }
+ } catch (...) { }
+}
+
+
+void Floating_window_layouter::Main::generate_window_layout_model()
+{
+ Reporter::Xml_generator xml(window_layout_reporter, [&] ()
+ {
+ for (Window *w = windows.first(); w; w = w->next()) {
+
+ bool const is_focused = w->has_id(focused_window_id);
+
+ Window::Element const highlight =
+ is_focused ? hovered_element : Window::Element::UNDEFINED;
+
+ w->serialize(xml, is_focused, highlight);
+ }
+ });
+}
+
+
+void Floating_window_layouter::Main::generate_resize_request_model()
+{
+ Reporter::Xml_generator xml(resize_request_reporter, [&] ()
+ {
+ Window const *dragged_window = lookup_window_by_id(dragged_window_id);
+ if (dragged_window) {
+
+ Area const requested_size = dragged_window->requested_size();
+ if (requested_size != dragged_window->size()) {
+ xml.node("window", [&] () {
+ xml.attribute("id", dragged_window_id);
+ xml.attribute("width", requested_size.w());
+ xml.attribute("height", requested_size.h());
+ });
+ }
+ }
+ });
+}
+
+
+void Floating_window_layouter::Main::generate_focus_model()
+{
+ Reporter::Xml_generator xml(focus_reporter, [&] ()
+ {
+ xml.node("window", [&] () {
+ xml.attribute("id", focused_window_id);
+ });
+ });
+}
+
+
+/**
+ * Determine window element that corresponds to hover model
+ */
+static Floating_window_layouter::Window::Element
+element_from_hover_model(Genode::Xml_node hover_window_xml)
+{
+ typedef Floating_window_layouter::Window::Element::Type Type;
+
+ bool const left_sizer = hover_window_xml.has_sub_node("left_sizer"),
+ right_sizer = hover_window_xml.has_sub_node("right_sizer"),
+ top_sizer = hover_window_xml.has_sub_node("top_sizer"),
+ bottom_sizer = hover_window_xml.has_sub_node("bottom_sizer");
+
+ if (left_sizer && top_sizer) return Type::TOP_LEFT;
+ if (left_sizer && bottom_sizer) return Type::BOTTOM_LEFT;
+ if (left_sizer) return Type::LEFT;
+
+ if (right_sizer && top_sizer) return Type::TOP_RIGHT;
+ if (right_sizer && bottom_sizer) return Type::BOTTOM_RIGHT;
+ if (right_sizer) return Type::RIGHT;
+
+ if (top_sizer) return Type::TOP;
+ if (bottom_sizer) return Type::BOTTOM;
+
+ if (hover_window_xml.has_sub_node("title")) return Type::TITLE;
+
+ return Type::UNDEFINED;
+}
+
+
+void Floating_window_layouter::Main::initiate_window_drag(Window &window)
+{
+ window.initiate_drag_operation(hovered_element);
+
+ /* bring focused window to front */
+ if (&window != windows.first()) {
+ windows.remove(&window);
+ windows.insert(&window);
+ }
+
+ window.topped();
+}
+
+
+void Floating_window_layouter::Main::handle_window_list_update(unsigned)
+{
+ window_list.update();
+
+ try {
+ import_window_list(Xml_node(window_list.local_addr())); }
+ catch (...) {
+ PERR("Error while importing window list"); }
+
+ generate_window_layout_model();
+}
+
+
+void Floating_window_layouter::Main::handle_hover_update(unsigned)
+{
+ hover.update();
+
+ try {
+ Xml_node const hover_xml(hover.local_addr());
+
+ Xml_node const hover_window_xml = hover_xml.sub_node("window");
+
+ unsigned const id = attribute(hover_window_xml, "id", 0UL);
+
+ Window::Element hovered = element_from_hover_model(hover_window_xml);
+
+ /*
+ * Check if we have just received an update while already being in
+ * dragged state.
+ *
+ * This can happen when the user selects a new nitpicker domain by
+ * clicking on a window decoration. Prior the click, the new session is
+ * not aware of the current mouse position. So the hover model is not
+ * up to date. As soon as nitpicker assigns the focus to the new
+ * session and delivers the corresponding press event, we enter the
+ * drag state (in the 'handle_input' function. But we don't know which
+ * window is dragged until the decorator updates the hover model. Now,
+ * when the model is updated and we are still in dragged state, we can
+ * finally initiate the window-drag operation for the now-known window.
+ */
+ if (id && drag_state && dragged_window_id == 0)
+ {
+ dragged_window_id = id;
+ focused_window_id = id;
+
+ Window *window = lookup_window_by_id(id);
+ if (window) {
+ initiate_window_drag(*window);
+ generate_window_layout_model();
+ generate_focus_model();
+ }
+ }
+
+ if (!drag_state && (id != focused_window_id || hovered != hovered_element)) {
+
+ focused_window_id = id;
+ hovered_element = hovered;
+ generate_window_layout_model();
+ generate_focus_model();
+ }
+ } catch (...) {
+
+ /* reset focused window if pointer does not hover over any window */
+ if (!drag_state) {
+ hovered_element = Window::Element::UNDEFINED;
+ generate_window_layout_model();
+ generate_focus_model();
+ }
+ }
+}
+
+
+void Floating_window_layouter::Main::handle_input(unsigned)
+{
+ bool need_regenerate_window_layout_model = false;
+ bool need_regenerate_resize_request_model = false;
+
+ Window *focused_window = lookup_window_by_id(focused_window_id);
+
+ while (input.is_pending()) {
+
+ size_t const num_events = input.flush();
+
+ Input::Event const * const ev = input_ds.local_addr();
+
+ for (size_t i = 0; i < num_events; i++) {
+
+ Input::Event e = ev[i];
+
+ if (e.type() == Input::Event::MOTION
+ || e.type() == Input::Event::FOCUS)
+ pointer_curr = Point(e.ax(), e.ay());
+
+ /* track number of pressed buttons/keys */
+ if (e.type() == Input::Event::PRESS) key_cnt++;
+ if (e.type() == Input::Event::RELEASE) key_cnt--;
+
+ if (e.type() == Input::Event::PRESS
+ && e.keycode() == Input::BTN_LEFT) {
+
+ drag_state = true;
+ dragged_window_id = focused_window_id;
+ pointer_clicked = pointer_curr;
+ pointer_last = pointer_clicked;
+
+ /*
+ * If the focused window is known at the time of the press
+ * event, we can initiate the drag operation immediately.
+ * Otherwise, we the initiation is deferred to the next
+ * update of the hover model.
+ */
+ if (focused_window) {
+ initiate_window_drag(*focused_window);
+ need_regenerate_window_layout_model = true;
+ }
+ }
+
+ /* detect end of drag operation */
+ if (e.type() == Input::Event::RELEASE) {
+ if (key_cnt == 0) {
+ drag_state = false;
+ generate_focus_model();
+
+ Window *dragged_window = lookup_window_by_id(dragged_window_id);
+ if (dragged_window) {
+
+ Area const last_requested_size = dragged_window->requested_size();
+ dragged_window->finalize_drag_operation();
+
+ if (last_requested_size != dragged_window->requested_size())
+ need_regenerate_resize_request_model = true;
+
+ /*
+ * Update window layout because highlighting may have
+ * changed after the drag operation. E.g., if the
+ * window has not kept up with the dragging of a
+ * resize handle, the resize handle is no longer
+ * hovered.
+ */
+ handle_hover_update(0);
+ }
+ }
+ }
+ }
+ }
+
+ if (drag_state && (pointer_curr != pointer_last)) {
+
+ pointer_last = pointer_curr;
+
+ Window *dragged_window = lookup_window_by_id(dragged_window_id);
+ if (dragged_window) {
+
+ Point const last_pos = dragged_window->position();
+ Area const last_requested_size = dragged_window->requested_size();
+
+ dragged_window->apply_drag_operation(pointer_curr - pointer_clicked);
+
+ if (last_pos != dragged_window->position())
+ need_regenerate_window_layout_model = true;
+
+ if (last_requested_size != dragged_window->requested_size())
+ need_regenerate_resize_request_model = true;
+ }
+ }
+
+ if (need_regenerate_window_layout_model)
+ generate_window_layout_model();
+
+ if (need_regenerate_resize_request_model)
+ generate_resize_request_model();
+}
+
+
+int main(int argc, char **argv)
+{
+ static Genode::Signal_receiver sig_rec;
+
+ static Floating_window_layouter::Main application(sig_rec);
+
+ /* import initial state */
+ application.handle_window_list_update(0);
+
+ /* process incoming signals */
+ for (;;) {
+ using namespace Genode;
+
+ Signal sig = sig_rec.wait_for_signal();
+ Signal_dispatcher_base *dispatcher =
+ dynamic_cast(sig.context());
+
+ if (dispatcher)
+ dispatcher->dispatch(sig.num());
+ }
+}
diff --git a/repos/gems/src/app/floating_window_layouter/target.mk b/repos/gems/src/app/floating_window_layouter/target.mk
new file mode 100644
index 0000000000..af9ad4b9cf
--- /dev/null
+++ b/repos/gems/src/app/floating_window_layouter/target.mk
@@ -0,0 +1,4 @@
+TARGET = floating_window_layouter
+SRC_CC = main.cc
+LIBS = base cxx
+
diff --git a/repos/gems/src/server/wm/decorator_nitpicker.h b/repos/gems/src/server/wm/decorator_nitpicker.h
new file mode 100644
index 0000000000..03b9d2a879
--- /dev/null
+++ b/repos/gems/src/server/wm/decorator_nitpicker.h
@@ -0,0 +1,473 @@
+/*
+ * \brief Local nitpicker service provided to decorator
+ * \author Norman Feske
+ * \date 2014-02-14
+ */
+
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef _DECORATOR_NITPICKER_H_
+#define _DECORATOR_NITPICKER_H_
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* local includes */
+#include
+
+namespace Wm { class Main;
+ using Genode::size_t;
+ using Genode::Allocator;
+ using Server::Entrypoint;
+ using Genode::Ram_session_client;
+ using Genode::Ram_session_capability;
+ using Genode::Arg_string;
+ using Genode::Object_pool;
+ using Genode::Attached_dataspace;
+ using Genode::Attached_ram_dataspace;
+ using Genode::Signal_rpc_member;
+}
+
+
+namespace Wm {
+
+ struct Decorator_nitpicker_session;
+ struct Decorator_nitpicker_service;
+ struct Decorator_content_callback;
+ struct Decorator_content_registry;
+}
+
+
+struct Wm::Decorator_content_callback
+{
+ virtual void content_geometry(Window_registry::Id win_id, Rect rect) = 0;
+
+ virtual Nitpicker::View_capability content_view(Window_registry::Id win_id) = 0;
+
+ virtual void update_content_child_views(Window_registry::Id win_id) = 0;
+};
+
+
+class Wm::Decorator_content_registry
+{
+ public:
+
+ /**
+ * Exception type
+ */
+ struct Lookup_failed { };
+
+ private:
+
+ struct Entry : List::Element
+ {
+ Nitpicker::Session::View_handle const decorator_view_handle;
+ Window_registry::Id const win_id;
+
+ Entry(Nitpicker::Session::View_handle decorator_view_handle,
+ Window_registry::Id win_id)
+ :
+ decorator_view_handle(decorator_view_handle),
+ win_id(win_id)
+ { }
+ };
+
+ List _list;
+ Allocator &_entry_alloc;
+
+ Entry const &_lookup(Nitpicker::Session::View_handle view_handle) const
+ {
+ for (Entry const *e = _list.first(); e; e = e->next()) {
+ if (e->decorator_view_handle == view_handle)
+ return *e;
+ }
+
+ throw Lookup_failed();
+ }
+
+ void _remove(Entry const &e)
+ {
+ _list.remove(&e);
+ destroy(_entry_alloc, const_cast(&e));
+ }
+
+ public:
+
+ Decorator_content_registry(Allocator &entry_alloc)
+ :
+ _entry_alloc(entry_alloc)
+ { }
+
+ ~Decorator_content_registry()
+ {
+ while (Entry *e = _list.first())
+ _remove(*e);
+ }
+
+ void insert(Nitpicker::Session::View_handle decorator_view_handle,
+ Window_registry::Id win_id)
+ {
+ Entry *e = new (_entry_alloc) Entry(decorator_view_handle, win_id);
+ _list.insert(e);
+ }
+
+ /**
+ * Lookup window ID for a given decorator content view
+ *
+ * \throw Lookup_failed
+ */
+ Window_registry::Id lookup(Nitpicker::Session::View_handle view_handle) const
+ {
+ return _lookup(view_handle).win_id;
+ }
+
+ /**
+ * Remove entry
+ *
+ * \throw Lookup_failed
+ */
+ void remove(Nitpicker::Session::View_handle view_handle)
+ {
+ _remove(_lookup(view_handle));
+ }
+};
+
+
+struct Wm::Decorator_nitpicker_session : Genode::Rpc_object
+{
+ typedef Nitpicker::View_capability View_capability;
+ typedef Nitpicker::Session::View_handle View_handle;
+
+ Ram_session_client _ram;
+
+ Nitpicker::Connection _nitpicker_session { "decorator" };
+
+ typedef Nitpicker::Session::Command_buffer Command_buffer;
+
+ Attached_ram_dataspace _command_ds { &_ram, sizeof(Command_buffer) };
+
+ Command_buffer &_command_buffer = *_command_ds.local_addr();
+
+ Input::Session_client _nitpicker_input { _nitpicker_session.input_session() };
+
+ Attached_dataspace _nitpicker_input_ds { _nitpicker_input.dataspace() };
+
+ Local_reporter &_pointer_reporter;
+
+ Input::Session_component &_window_layouter_input;
+
+ Decorator_content_callback &_content_callback;
+
+ /* XXX don't allocate content-registry entries from heap */
+ Decorator_content_registry _content_registry { *Genode::env()->heap() };
+
+ Entrypoint &_ep;
+
+ Allocator &_md_alloc;
+
+ Signal_rpc_member
+ _input_dispatcher { _ep, *this, &Decorator_nitpicker_session::_input_handler };
+
+ /**
+ * Constructor
+ *
+ * \param ep entrypoint used for dispatching signals
+ */
+ Decorator_nitpicker_session(Ram_session_capability ram,
+ Entrypoint &ep, Allocator &md_alloc,
+ Local_reporter &pointer_reporter,
+ Input::Session_component &window_layouter_input,
+ Decorator_content_callback &content_callback)
+ :
+ _ram(ram),
+ _pointer_reporter(pointer_reporter),
+ _window_layouter_input(window_layouter_input),
+ _content_callback(content_callback),
+ _ep(ep), _md_alloc(md_alloc)
+ {
+ _nitpicker_input.sigh(_input_dispatcher);
+ }
+
+ void _input_handler(unsigned)
+ {
+ Input::Event const * const events =
+ _nitpicker_input_ds.local_addr();
+
+ while (_nitpicker_input.is_pending()) {
+
+ size_t const num_events = _nitpicker_input.flush();
+
+ /* we trust nitpicker to return a valid number of events */
+
+ for (size_t i = 0; i < num_events; i++) {
+
+ Input::Event const &ev = events[i];
+
+ if (ev.type() == Input::Event::MOTION) {
+ Local_reporter::Xml_generator xml(_pointer_reporter, [&] ()
+ {
+ xml.attribute("xpos", ev.ax());
+ xml.attribute("ypos", ev.ay());
+ });
+ }
+
+ if (ev.type() == Input::Event::LEAVE) {
+
+ Local_reporter::Xml_generator xml(_pointer_reporter, [&] ()
+ {
+ /* report empty pointer model */
+ });
+ }
+
+ _window_layouter_input.submit(ev);
+ }
+ }
+ }
+
+ void _execute_command(Command const &cmd)
+ {
+ switch (cmd.opcode) {
+
+ case Command::OP_TITLE:
+ {
+ unsigned long id = 0;
+ Genode::ascii_to(cmd.title.title.string(), &id);
+
+ if (id > 0)
+ _content_registry.insert(cmd.title.view,
+ Window_registry::Id(id));
+ return;
+ }
+
+ case Command::OP_TO_FRONT:
+
+ try {
+
+ /*
+ * If the content view is re-stacked, replace it by the real
+ * window content.
+ *
+ * The lookup fails with an exception for non-content views.
+ * In this case, forward the command.
+ */
+ Window_registry::Id win_id = _content_registry.lookup(cmd.to_front.view);
+
+ /*
+ * Replace content view originally created by the decorator
+ * by view that shows the real window content.
+ */
+ Nitpicker::View_capability view_cap =
+ _content_callback.content_view(win_id);
+
+ _nitpicker_session.view_handle(view_cap,
+ cmd.to_front.view);
+
+ _nitpicker_session.enqueue(cmd);
+ _nitpicker_session.execute();
+
+ /*
+ * Now that the physical content view exists, it is time
+ * to revisit the child views.
+ */
+ _content_callback.update_content_child_views(win_id);
+
+ } catch (Decorator_content_registry::Lookup_failed) {
+
+ _nitpicker_session.enqueue(cmd);
+ }
+
+ return;
+
+ case Command::OP_GEOMETRY:
+
+ try {
+
+ /*
+ * If the content view changes position, propagate the new
+ * position to the nitpicker service to properly transform
+ * absolute input coordinates.
+ */
+ Window_registry::Id win_id = _content_registry.lookup(cmd.geometry.view);
+
+ _content_callback.content_geometry(win_id, cmd.geometry.rect);
+ }
+ catch (Decorator_content_registry::Lookup_failed) { }
+
+ /* forward command */
+ _nitpicker_session.enqueue(cmd);
+ return;
+
+ case Command::OP_OFFSET:
+
+ try {
+
+ /*
+ * If non-content views change their offset (if the lookup
+ * fails), propagate the event
+ */
+ _content_registry.lookup(cmd.geometry.view);
+ }
+ catch (Decorator_content_registry::Lookup_failed) {
+ _nitpicker_session.enqueue(cmd);
+ }
+ return;
+
+ case Command::OP_TO_BACK:
+ case Command::OP_BACKGROUND:
+ case Command::OP_NOP:
+
+ _nitpicker_session.enqueue(cmd);
+ return;
+ }
+ }
+
+ void upgrade(const char *args)
+ {
+ Genode::env()->parent()->upgrade(_nitpicker_session, args);
+ }
+
+
+ /*********************************
+ ** Nitpicker session interface **
+ *********************************/
+
+ Framebuffer::Session_capability framebuffer_session() override
+ {
+ return _nitpicker_session.framebuffer_session();
+ }
+
+ Input::Session_capability input_session() override
+ {
+ /*
+ * Deny input to the decorator. User input referring to the
+ * window decorations is routed to the window manager.
+ */
+ return Input::Session_capability();
+ }
+
+ View_handle create_view(View_handle parent) override
+ {
+ return _nitpicker_session.create_view(parent);
+ }
+
+ void destroy_view(View_handle view) override
+ {
+ _nitpicker_session.destroy_view(view);
+ }
+
+ View_handle view_handle(View_capability view_cap, View_handle handle) override
+ {
+ return _nitpicker_session.view_handle(view_cap, handle);
+ }
+
+ View_capability view_capability(View_handle view) override
+ {
+ return _nitpicker_session.view_capability(view);
+ }
+
+ void release_view_handle(View_handle view) override
+ {
+ /* XXX dealloc View_ptr */
+ _nitpicker_session.release_view_handle(view);
+ }
+
+ Genode::Dataspace_capability command_dataspace() override
+ {
+ return _command_ds.cap();
+ }
+
+ void execute() override
+ {
+ for (unsigned i = 0; i < _command_buffer.num(); i++) {
+ try {
+ _execute_command(_command_buffer.get(i));
+ }
+ catch (...) {
+ PWRN("unhandled exception while processing command from decorator");
+ }
+ }
+ _nitpicker_session.execute();
+ }
+
+ Framebuffer::Mode mode() override
+ {
+ return _nitpicker_session.mode();
+ }
+
+ void mode_sigh(Genode::Signal_context_capability sigh) override
+ {
+ _nitpicker_session.mode_sigh(sigh);
+ }
+
+ void buffer(Framebuffer::Mode mode, bool use_alpha) override
+ {
+ _nitpicker_session.buffer(mode, use_alpha);
+ }
+
+ void focus(Genode::Capability) { }
+};
+
+
+struct Wm::Decorator_nitpicker_service : Genode::Service, Genode::Noncopyable
+{
+ private:
+
+ Entrypoint &_ep;
+ Allocator &_md_alloc;
+ Ram_session_capability _ram;
+ Local_reporter &_pointer_reporter;
+ Input::Session_component &_window_layouter_input;
+ Decorator_content_callback &_content_callback;
+
+
+ public:
+
+ Decorator_nitpicker_service(Entrypoint &ep, Allocator &md_alloc,
+ Ram_session_capability ram,
+ Local_reporter &pointer_reporter,
+ Input::Session_component &window_layouter_input,
+ Decorator_content_callback &content_callback)
+ :
+ Service("Nitpicker"),
+ _ep(ep), _md_alloc(md_alloc),
+ _ram(ram), _pointer_reporter(pointer_reporter),
+ _window_layouter_input(window_layouter_input),
+ _content_callback(content_callback)
+ { }
+
+ Genode::Session_capability
+ session(const char *, Genode::Affinity const &) override
+ {
+ Decorator_nitpicker_session *s = new (_md_alloc)
+ Decorator_nitpicker_session(_ram, _ep, _md_alloc,
+ _pointer_reporter,
+ _window_layouter_input,
+ _content_callback);
+
+ return _ep.manage(*s);
+ }
+
+ void upgrade(Genode::Session_capability session, const char *args) override
+ {
+ typedef typename Object_pool::Guard Object_guard;
+ Object_guard np_session(_ep.rpc_ep().lookup_and_lock(session));
+
+ if (np_session)
+ np_session->upgrade(args);
+ }
+
+ void close(Genode::Session_capability) { }
+};
+
+#endif /* _DECORATOR_NITPICKER_H_ */
diff --git a/repos/gems/src/server/wm/decorator_slave.h b/repos/gems/src/server/wm/decorator_slave.h
new file mode 100644
index 0000000000..f8dbb55902
--- /dev/null
+++ b/repos/gems/src/server/wm/decorator_slave.h
@@ -0,0 +1,128 @@
+/*
+ * \brief Slave for drawing window decorations
+ * \author Norman Feske
+ * \date 2014-02-14
+ */
+
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef _DECORATOR_SLAVE_H_
+#define _DECORATOR_SLAVE_H_
+
+namespace Wm {
+
+ class Decorator_slave;
+
+ using Genode::Rom_session_capability;
+ using Genode::Capability;
+}
+
+
+class Wm::Decorator_slave
+{
+ private:
+
+ Genode::Ram_session &_ram;
+
+ class Policy : public Genode::Slave_policy
+ {
+ private:
+
+ Genode::Service &_nitpicker_service;
+
+ Single_session_service _window_layout_rom_service;
+ Single_session_service _pointer_rom_service;
+ Single_session_service _hover_report_service;
+
+ protected:
+
+ char const **_permitted_services() const
+ {
+ static char const *permitted_services[] = {
+ "CAP", "LOG", "SIGNAL", "RM", 0 };
+
+ return permitted_services;
+ };
+
+ public:
+
+ Policy(Genode::Rpc_entrypoint &entrypoint,
+ Genode::Ram_session &ram,
+ Genode::Service &nitpicker_service,
+ Rom_session_capability window_layout_rom,
+ Rom_session_capability pointer_rom,
+ Genode::Capability hover_report)
+ :
+ Slave_policy("decorator", entrypoint, &ram),
+ _nitpicker_service(nitpicker_service),
+ _window_layout_rom_service("ROM", window_layout_rom),
+ _pointer_rom_service("ROM", pointer_rom),
+ _hover_report_service("Report", hover_report)
+
+ { }
+
+ Genode::Service *resolve_session_request(const char *service_name,
+ const char *args) override
+ {
+ using Genode::strcmp;
+
+ if (strcmp(service_name, "Nitpicker") == 0)
+ return &_nitpicker_service;
+
+ char label[128];
+ Arg_string::find_arg(args, "label").string(label, sizeof(label), "");
+
+ if (strcmp(service_name, "ROM") == 0) {
+
+ if (strcmp(label, "decorator -> window_layout") == 0)
+ return &_window_layout_rom_service;
+
+ if (strcmp(label, "decorator -> pointer") == 0)
+ return &_pointer_rom_service;
+ }
+
+ if (strcmp(service_name, "Report") == 0) {
+
+ if (strcmp(label, "decorator -> hover") == 0)
+ return &_hover_report_service;
+ }
+
+ return Genode::Slave_policy::resolve_session_request(service_name, args);
+ }
+ };
+
+ Genode::size_t const _ep_stack_size = 4*1024*sizeof(Genode::addr_t);
+ Genode::Rpc_entrypoint _ep;
+ Policy _policy;
+ Genode::size_t const _quota = 4*1024*1024;
+ Genode::Slave _slave;
+
+ public:
+
+ /**
+ * Constructor
+ *
+ * \param ram RAM session for paying nitpicker sessions created
+ * by the decorator
+ */
+ Decorator_slave(Genode::Cap_session &cap,
+ Genode::Service &nitpicker_service,
+ Genode::Ram_session &ram,
+ Rom_session_capability window_layout_rom,
+ Rom_session_capability pointer_rom,
+ Genode::Capability hover_report)
+ :
+ _ram(ram),
+ _ep(&cap, _ep_stack_size, "decorator"),
+ _policy(_ep, ram, nitpicker_service, window_layout_rom,
+ pointer_rom, hover_report),
+ _slave(_ep, _policy, _quota)
+ { }
+};
+
+#endif /* _DECORATOR_SLAVE_H_ */
diff --git a/repos/gems/src/server/wm/local_reporter.h b/repos/gems/src/server/wm/local_reporter.h
new file mode 100644
index 0000000000..1dade34c0a
--- /dev/null
+++ b/repos/gems/src/server/wm/local_reporter.h
@@ -0,0 +1,52 @@
+/*
+ * \brief Utility for producing reports to a report session
+ * \author Norman Feske
+ * \date 2014-02-14
+ */
+
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef _LOCAL_REPORTER_H_
+#define _LOCAL_REPORTER_H_
+
+#include
+#include
+#include
+
+
+namespace Wm { struct Local_reporter; }
+
+struct Wm::Local_reporter
+{
+ Report::Session_client _session;
+
+ Genode::Attached_dataspace _ds;
+
+ char const *_name;
+
+ Local_reporter(char const *name, Genode::Capability session_cap)
+ :
+ _session(session_cap), _ds(_session.dataspace()), _name(name)
+ { }
+
+ struct Xml_generator : public Genode::Xml_generator
+ {
+ template
+ Xml_generator(Local_reporter &reporter, FUNC const &func)
+ :
+ Genode::Xml_generator(reporter._ds.local_addr(),
+ reporter._ds.size(),
+ reporter._name,
+ func)
+ {
+ reporter._session.submit(used());
+ }
+ };
+};
+
+#endif /* _LOCAL_REPORTER_H_ */
diff --git a/repos/gems/src/server/wm/main.cc b/repos/gems/src/server/wm/main.cc
new file mode 100644
index 0000000000..c0bd2ca28b
--- /dev/null
+++ b/repos/gems/src/server/wm/main.cc
@@ -0,0 +1,185 @@
+/*
+ * \brief Window manager
+ * \author Norman Feske
+ * \date 2014-01-06
+ */
+
+/*
+ * Copyright (C) 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
+
+/* local includes */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace Wm {
+
+ class Main;
+
+ using Genode::size_t;
+ using Genode::env;
+ using Genode::Rom_session_client;
+ using Genode::Rom_connection;
+ using Genode::Xml_node;
+}
+
+
+struct Wm::Main
+{
+ Server::Entrypoint ep;
+
+ Genode::Cap_connection cap;
+
+ Report_rom_slave report_rom_slave = { cap, *env()->ram_session() };
+
+ Rom_session_capability window_list_rom = report_rom_slave.rom_session("window_list");
+ Rom_session_capability window_layout_rom = report_rom_slave.rom_session("window_layout");
+ Rom_session_capability pointer_rom = report_rom_slave.rom_session("pointer");
+ Rom_session_capability hover_rom = report_rom_slave.rom_session("hover");
+
+ Rom_session_client focus_rom { report_rom_slave.rom_session("focus") };
+ Rom_session_client resize_request_rom { report_rom_slave.rom_session("resize_request") };
+
+ /* pointer position reported by nitpicker */
+ Capability pointer_report = report_rom_slave.report_session("pointer");
+ Local_reporter pointer_reporter = { "pointer", pointer_report };
+
+ /* hovered element reported by decorator */
+ Capability hover_report = report_rom_slave.report_session("hover");
+
+ Capability window_list_report = report_rom_slave.report_session("window_list");
+ Local_reporter window_list_reporter = { "window_list", window_list_report };
+
+ Capability window_layout_report = report_rom_slave.report_session("window_layout");
+ Capability resize_request_report = report_rom_slave.report_session("resize_request");
+ Capability focus_report = report_rom_slave.report_session("focus");
+
+ Input::Session_component window_layouter_input;
+
+ Window_registry window_registry { *env()->heap(), window_list_reporter };
+
+ Nitpicker::Root nitpicker_root { ep, window_registry,
+ *env()->heap(), env()->ram_session_cap() };
+
+ Decorator_nitpicker_service decorator_nitpicker_service {
+ ep, *env()->heap(), env()->ram_session_cap(), pointer_reporter,
+ window_layouter_input, nitpicker_root };
+
+ Window_layouter_slave window_layouter_slave = {
+ cap, *env()->ram_session(), window_list_rom, hover_rom,
+ ep.manage(window_layouter_input), window_layout_report,
+ resize_request_report, focus_report };
+
+ Decorator_slave decorator_slave = {
+ cap, decorator_nitpicker_service, *env()->ram_session(),
+ window_layout_rom, pointer_rom, hover_report };
+
+ Genode::Lazy_volatile_object focus_ds;
+
+ Nitpicker::Connection focus_nitpicker_session;
+
+ void handle_focus_update(unsigned)
+ {
+ try {
+ if (!focus_ds.is_constructed() || focus_rom.update() == false)
+ focus_ds.construct(focus_rom.dataspace());
+
+ unsigned long win_id = 0;
+
+ Xml_node(focus_ds->local_addr()).sub_node("window")
+ .attribute("id").value(&win_id);
+
+ if (win_id) {
+ Nitpicker::Session_capability session_cap =
+ nitpicker_root.lookup_nitpicker_session(win_id);
+
+ focus_nitpicker_session.focus(session_cap);
+ }
+
+ } catch (...) {
+ PWRN("no focus model available");
+ }
+ }
+
+ Genode::Signal_rpc_member focus_dispatcher = { ep, *this, &Main::handle_focus_update };
+
+ Genode::Lazy_volatile_object resize_request_ds;
+
+ void handle_resize_request_update(unsigned)
+ {
+ try {
+ if (!resize_request_ds.is_constructed()
+ || resize_request_rom.update() == false)
+ resize_request_ds.construct(resize_request_rom.dataspace());
+
+ char const * const node_type = "window";
+
+ Xml_node window =
+ Xml_node(resize_request_ds->local_addr()).sub_node(node_type);
+
+ for (;;) {
+ unsigned long win_id = 0, width = 0, height = 0;
+
+ window.attribute("id") .value(&win_id);
+ window.attribute("width") .value(&width);
+ window.attribute("height").value(&height);
+
+ nitpicker_root.request_resize(win_id, Area(width, height));
+
+ if (window.is_last(node_type))
+ break;
+
+ window = window.next(node_type);
+ }
+
+ } catch (...) { /* no resize-request model available */ }
+ }
+
+ Genode::Signal_rpc_member resize_request_dispatcher =
+ { ep, *this, &Main::handle_resize_request_update };
+
+ Main(Server::Entrypoint &ep) : ep(ep)
+ {
+ window_layouter_input.event_queue().enabled(true);
+
+ /* initially report an empty window list */
+ Local_reporter::Xml_generator xml(window_list_reporter, [&] () { });
+
+ focus_rom.sigh(focus_dispatcher);
+ resize_request_rom.sigh(resize_request_dispatcher);
+ }
+};
+
+
+/************
+ ** Server **
+ ************/
+
+namespace Server {
+
+ char const *name() { return "desktop_ep"; }
+
+ size_t stack_size() { return 4*1024*sizeof(long); }
+
+ void construct(Entrypoint &ep)
+ {
+ static Wm::Main desktop(ep);
+ }
+}
diff --git a/repos/gems/src/server/wm/nitpicker.h b/repos/gems/src/server/wm/nitpicker.h
new file mode 100644
index 0000000000..65c9616dd0
--- /dev/null
+++ b/repos/gems/src/server/wm/nitpicker.h
@@ -0,0 +1,1005 @@
+/*
+ * \brief Virtualized nitpicker service announced to the outside world
+ * \author Norman Feske
+ * \date 2014-02-14
+ */
+
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef _NITPICKER_H_
+#define _NITPICKER_H_
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* local includes */
+#include
+
+
+namespace Wm {
+
+ using Genode::Rpc_object;
+ using Genode::List;
+ using Genode::Allocator;
+ using Genode::Affinity;
+ using Genode::static_cap_cast;
+ using Genode::Signal_rpc_member;
+ using Genode::Ram_session_capability;
+ using Genode::Weak_ptr;
+ using Genode::Locked_ptr;
+ using Genode::Tslab;
+ using Genode::Attached_ram_dataspace;
+ using Genode::Signal_context_capability;
+ using Genode::Signal_transmitter;
+}
+
+namespace Wm { namespace Nitpicker {
+
+ using namespace ::Nitpicker;
+
+ class View_handle_ctx;
+ class View;
+ class Top_level_view;
+ class Child_view;
+ class Direct_view;
+ class Session_component;
+ class Root;
+
+ typedef Genode::Surface_base::Rect Rect;
+ typedef Genode::Surface_base::Point Point;
+ typedef Genode::Session_label Session_label;
+} }
+
+
+struct Nitpicker::View { GENODE_RPC_INTERFACE(); };
+
+
+class Wm::Nitpicker::View : public Genode::Weak_object,
+ public Genode::Rpc_object< ::Nitpicker::View>
+{
+ protected:
+
+ typedef Genode::String<100> Title;
+ typedef Nitpicker::Session::Command Command;
+ typedef Nitpicker::Session::View_handle View_handle;
+
+ Session_label _session_label;
+ Nitpicker::Session_client &_real_nitpicker;
+ View_handle _real_handle;
+ Title _title;
+ Rect _geometry;
+ Point _buffer_offset;
+ Weak_ptr _neighbor_ptr;
+ bool _neighbor_behind;
+
+ View(Nitpicker::Session_client &real_nitpicker,
+ Session_label const &session_label)
+ :
+ _session_label(session_label), _real_nitpicker(real_nitpicker)
+ { }
+
+ /**
+ * Propagate cached view geometry to the physical nitpicker view
+ */
+ virtual void _propagate_view_geometry() = 0;
+
+ /**
+ * Apply cached view state to the physical nitpicker view
+ */
+ void _apply_view_config()
+ {
+ if (!_real_handle.valid())
+ return;
+
+ _propagate_view_geometry();
+ _real_nitpicker.enqueue(_real_handle, _buffer_offset);
+ _real_nitpicker.enqueue (_real_handle, _title.string());
+
+ View_handle real_neighbor_handle;
+
+ Locked_ptr neighbor(_neighbor_ptr);
+ if (neighbor.is_valid())
+ real_neighbor_handle = _real_nitpicker.view_handle(neighbor->real_view_cap());
+
+ if (_neighbor_behind)
+ _real_nitpicker.enqueue(_real_handle, real_neighbor_handle);
+ else
+ _real_nitpicker.enqueue(_real_handle, real_neighbor_handle);
+
+ _real_nitpicker.execute();
+
+ if (real_neighbor_handle.valid())
+ _real_nitpicker.release_view_handle(real_neighbor_handle);
+ }
+
+ public:
+
+ ~View()
+ {
+ if (_real_handle.valid())
+ _real_nitpicker.destroy_view(_real_handle);
+ }
+
+ Point virtual_position() const { return _geometry.p1(); }
+
+ virtual bool belongs_to_win_id(Window_registry::Id id) const = 0;
+
+ virtual void geometry(Rect geometry)
+ {
+ _geometry = geometry;
+
+ /*
+ * Propagate new size to real nitpicker view but
+ */
+ if (_real_handle.valid()) {
+ _propagate_view_geometry();
+ _real_nitpicker.execute();
+ }
+ }
+
+ virtual void title(char const *title)
+ {
+ _title = Title(title);
+
+ if (_real_handle.valid()) {
+ _real_nitpicker.enqueue(_real_handle, title);
+ _real_nitpicker.execute();
+ }
+ }
+
+ virtual Point input_anchor_position() const = 0;
+
+ virtual void stack(Weak_ptr neighbor_ptr, bool behind) { }
+
+ View_handle real_handle() const { return _real_handle; }
+
+ View_capability real_view_cap()
+ {
+ return _real_nitpicker.view_capability(_real_handle);
+ }
+
+ void buffer_offset(Point buffer_offset)
+ {
+ _buffer_offset = buffer_offset;
+
+ if (_real_handle.valid()) {
+ _real_nitpicker.enqueue(_real_handle, _buffer_offset);
+ _real_nitpicker.execute();
+ }
+ }
+};
+
+
+class Wm::Nitpicker::Top_level_view : public View,
+ public List::Element
+{
+ private:
+
+ Window_registry::Id _win_id;
+
+ Window_registry &_window_registry;
+
+ /*
+ * Geometry of window-content view, which corresponds to the location
+ * of the window content as known by the decorator.
+ */
+ Rect _content_geometry;
+
+ /*
+ * The window title is the concatenation of the session label with
+ * view title.
+ */
+ struct Window_title : Title
+ {
+ Window_title(Session_label const session_label, Title const &title)
+ {
+ bool const has_title = Genode::strlen(title.string()) > 0;
+ char buf[256];
+ Genode::snprintf(buf, sizeof(buf), "%s%s%s",
+ session_label.string(),
+ has_title ? " " : "", title.string());
+
+ *static_cast(this) = Title(buf);
+ }
+ } _window_title;
+
+ typedef Nitpicker::Session::Command Command;
+
+ public:
+
+ Top_level_view(Nitpicker::Session_client &real_nitpicker,
+ Session_label const &session_label,
+ Window_registry &window_registry)
+ :
+ View(real_nitpicker, session_label),
+ _window_registry(window_registry),
+ _window_title(session_label, "")
+ { }
+
+ ~Top_level_view()
+ {
+ if (_win_id.valid())
+ _window_registry.destroy(_win_id);
+ }
+
+ void _propagate_view_geometry() override { }
+
+ void geometry(Rect geometry) override
+ {
+ /*
+ * Add window to the window-list model on the first call. We
+ * defer the creation of the window ID until the time when the
+ * initial geometry is known.
+ */
+ if (!_win_id.valid()) {
+ _win_id = _window_registry.create();
+ _window_registry.title(_win_id, _window_title.string());
+ }
+
+ _window_registry.size(_win_id, geometry.area());
+
+ View::geometry(geometry);
+ }
+
+ Area size() const { return _geometry.area(); }
+
+ void title(char const *title) override
+ {
+ View::title(title);
+
+ _window_title = Window_title(_session_label, title);
+
+ if (_win_id.valid())
+ _window_registry.title(_win_id, _window_title.string());
+ }
+
+ bool has_win_id(Window_registry::Id id) const { return id == _win_id; }
+
+ bool belongs_to_win_id(Window_registry::Id id) const override
+ {
+ return has_win_id(id);
+ }
+
+ Point input_anchor_position() const override
+ {
+ return _content_geometry.p1();
+ }
+
+ void content_geometry(Rect rect) { _content_geometry = rect; }
+
+ View_capability content_view()
+ {
+ if (!_real_handle.valid()) {
+
+ /*
+ * Create and configure physical nitpicker view.
+ */
+ _real_handle = _real_nitpicker.create_view();
+
+ _real_nitpicker.enqueue(_real_handle, _buffer_offset);
+ _real_nitpicker.enqueue (_real_handle, _title.string());
+ _real_nitpicker.execute();
+ }
+
+ return _real_nitpicker.view_capability(_real_handle);
+ }
+};
+
+
+class Wm::Nitpicker::Child_view : public View,
+ public List::Element
+{
+ private:
+
+ Weak_ptr mutable _parent;
+
+ public:
+
+ Child_view(Nitpicker::Session_client &real_nitpicker,
+ Session_label const &session_label,
+ Weak_ptr parent)
+ :
+ View(real_nitpicker, session_label), _parent(parent)
+ {
+ try_to_init_real_view();
+ }
+
+ void _propagate_view_geometry() override
+ {
+ _real_nitpicker.enqueue(_real_handle, _geometry);
+ }
+
+ void stack(Weak_ptr neighbor_ptr, bool behind) override
+ {
+ _neighbor_ptr = neighbor_ptr;
+ _neighbor_behind = behind;
+
+ _apply_view_config();
+ }
+
+ bool belongs_to_win_id(Window_registry::Id id) const override
+ {
+ Locked_ptr parent(_parent);
+ return parent.is_valid() && parent->belongs_to_win_id(id);
+ }
+
+ Point input_anchor_position() const override
+ {
+ Locked_ptr parent(_parent);
+ if (parent.is_valid())
+ return parent->input_anchor_position();
+
+ return Point();
+ }
+
+ void try_to_init_real_view()
+ {
+ if (_real_handle.valid())
+ return;
+
+ Locked_ptr parent(_parent);
+ if (!parent.is_valid())
+ return;
+
+ View_handle parent_handle = _real_nitpicker.view_handle(parent->real_view_cap());
+ if (!parent_handle.valid())
+ return;
+
+ _real_handle = _real_nitpicker.create_view(parent_handle);
+
+ _real_nitpicker.release_view_handle(parent_handle);
+
+ _apply_view_config();
+ }
+
+ void update_child_stacking()
+ {
+ _apply_view_config();
+ }
+};
+
+
+class Wm::Nitpicker::Direct_view : public View
+{
+ public:
+
+ Direct_view(Nitpicker::Session_client &real_nitpicker,
+ Session_label const &session_label,
+ bool const direct)
+ :
+ View(real_nitpicker, session_label)
+ {
+ if (!direct)
+ return;
+
+ typedef Nitpicker::Session::Command Command;
+ _real_handle = _real_nitpicker.create_view();
+ _real_nitpicker.enqueue(_real_handle,
+ Rect(Point(0, 0), Area(0, 0)));
+ _real_nitpicker.enqueue(_real_handle);
+ _real_nitpicker.execute();
+ }
+
+ bool belongs_to_win_id(Window_registry::Id id) const override { return false; }
+
+ void _propagate_view_geometry() override { }
+
+ Point input_anchor_position() const override { return Point(); }
+};
+
+
+class Wm::Nitpicker::Session_component : public Genode::Rpc_object,
+ public List::Element
+{
+ private:
+
+ typedef Nitpicker::Session::View_handle View_handle;
+
+ Session_label _session_label;
+ Ram_session_client _ram;
+ Nitpicker::Connection _session { _session_label.string() };
+
+ Direct_view _direct_view;
+ Window_registry &_window_registry;
+ Entrypoint &_ep;
+ Tslab _top_level_view_alloc;
+ Tslab _child_view_alloc;
+ List _top_level_views;
+ List _child_views;
+ Input::Session_component _input_session;
+ Input::Session_capability _input_session_cap;
+ Signal_context_capability _mode_sigh;
+ Area _requested_size;
+
+ /*
+ * Command buffer
+ */
+ typedef Nitpicker::Session::Command_buffer Command_buffer;
+
+ Attached_ram_dataspace _command_ds { &_ram, sizeof(Command_buffer) };
+
+ Command_buffer &_command_buffer = *_command_ds.local_addr();
+
+ /*
+ * View handle registry
+ */
+ typedef Genode::Handle_registry
+ View_handle_registry;
+
+ View_handle_registry _view_handle_registry;
+
+ /*
+ * Input
+ */
+ Input::Session_client _nitpicker_input { _session.input_session() };
+ Attached_dataspace _nitpicker_input_ds { _nitpicker_input.dataspace() };
+
+ Signal_rpc_member _input_dispatcher {
+ _ep, *this, &Session_component::_input_handler };
+
+ Point _input_origin() const
+ {
+ if (Top_level_view const *v = _top_level_views.first())
+ return v->virtual_position() - v->input_anchor_position();
+
+ if (Child_view const *v = _child_views.first())
+ return Point(0, 0) - v->input_anchor_position();
+
+ return Point();
+ }
+
+ /**
+ * Translate input event to the client's coordinate system
+ */
+ Input::Event _translate_event(Input::Event const ev, Point const input_origin)
+ {
+ switch (ev.type()) {
+
+ case Input::Event::MOTION:
+ case Input::Event::PRESS:
+ case Input::Event::RELEASE:
+ case Input::Event::FOCUS:
+ case Input::Event::LEAVE:
+ {
+ Point abs_pos = 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();
+ }
+
+ void _input_handler(unsigned)
+ {
+ Point const input_origin = _input_origin();
+
+ Input::Event const * const events =
+ _nitpicker_input_ds.local_addr();
+
+ try {
+ while (_nitpicker_input.is_pending()) {
+
+ size_t const num_events = _nitpicker_input.flush();
+
+ /* we trust nitpicker to return a valid number of events */
+
+ for (size_t i = 0; i < num_events; i++)
+ _input_session.submit(_translate_event(events[i], input_origin));
+ }
+ } catch (Input::Event_queue::Overflow) {
+
+ PWRN("client \"%s\" does not respond to user input",
+ _session_label.string());
+
+ _input_session.event_queue().reset();
+ }
+ }
+
+ View &_create_view_object(View_handle parent_handle)
+ {
+ /*
+ * If the session operates in direct mode, we subordinate all
+ * top-level views of the session to the 'direct_parent' pseudo
+ * view, which is located at the screen origin.
+ */
+ if (!parent_handle.valid() && _direct_view.real_handle().valid()) {
+
+ Child_view *view = new (_child_view_alloc)
+ Child_view(_session, _session_label, _direct_view.weak_ptr());
+
+ _child_views.insert(view);
+ return *view;
+ }
+
+ /*
+ * Create child view
+ */
+ if (parent_handle.valid()) {
+
+ Weak_ptr parent_ptr = _view_handle_registry.lookup(parent_handle);
+
+ Child_view *view = new (_child_view_alloc)
+ Child_view(_session, _session_label, parent_ptr);
+
+ _child_views.insert(view);
+ return *view;
+ }
+
+ /*
+ * Create top-level view
+ */
+ else {
+ Top_level_view *view = new (_top_level_view_alloc)
+ Top_level_view(_session, _session_label, _window_registry);
+
+ _top_level_views.insert(view);
+ return *view;
+ }
+ }
+
+ void _destroy_top_level_view(Top_level_view &view)
+ {
+ _top_level_views.remove(&view);
+ _ep.dissolve(view);
+ Genode::destroy(&_top_level_view_alloc, &view);
+ }
+
+ void _destroy_child_view(Child_view &view)
+ {
+ _child_views.remove(&view);
+ _ep.dissolve(view);
+ Genode::destroy(&_child_view_alloc, &view);
+ }
+
+ void _destroy_view_object(View &view)
+ {
+ if (Top_level_view *v = dynamic_cast(&view))
+ _destroy_top_level_view(*v);
+
+ if (Child_view *v = dynamic_cast(&view))
+ _destroy_child_view(*v);
+ }
+
+ void _execute_command(Command const &command)
+ {
+ switch (command.opcode) {
+
+ case Command::OP_GEOMETRY:
+ {
+ Locked_ptr view(_view_handle_registry.lookup(command.geometry.view));
+ if (view.is_valid())
+ view->geometry(command.geometry.rect);
+ return;
+ }
+
+ case Command::OP_OFFSET:
+ {
+ Locked_ptr view(_view_handle_registry.lookup(command.offset.view));
+ if (view.is_valid())
+ view->buffer_offset(command.offset.offset);
+
+ return;
+ }
+
+ case Command::OP_TO_FRONT:
+ {
+ Locked_ptr view(_view_handle_registry.lookup(command.to_front.view));
+ if (!view.is_valid())
+ return;
+
+ /* bring to front if no neighbor is specified */
+ if (!command.to_front.neighbor.valid()) {
+ view->stack(Weak_ptr(), true);
+ return;
+ }
+
+ /* stack view relative to neighbor */
+ view->stack(_view_handle_registry.lookup(command.to_front.neighbor),
+ true);
+ return;
+ }
+
+ case Command::OP_TO_BACK:
+ {
+ PDBG("OP_TO_BACK not implemented");
+ return;
+ }
+
+ case Command::OP_BACKGROUND:
+ {
+ PDBG("OP_BACKGROUND not implemented");
+ return;
+ }
+
+ case Command::OP_TITLE:
+ {
+ Locked_ptr view(_view_handle_registry.lookup(command.title.view));
+ if (view.is_valid())
+ view->title(command.title.title.string());
+
+ return;
+ }
+
+ case Command::OP_NOP:
+ return;
+ }
+ }
+
+ public:
+
+ /**
+ * Constructor
+ *
+ * \param nitpicker real nitpicker service
+ * \param ep entrypoint used for managing the views
+ */
+ Session_component(Ram_session_capability ram,
+ Window_registry &window_registry,
+ Entrypoint &ep,
+ Allocator &session_alloc,
+ Session_label const &session_label,
+ bool const direct)
+ :
+ _session_label(session_label),
+ _ram(ram),
+ _direct_view(_session, session_label, direct),
+ _window_registry(window_registry),
+ _ep(ep),
+ _top_level_view_alloc(&session_alloc),
+ _child_view_alloc(&session_alloc),
+ _input_session_cap(_ep.manage(_input_session)),
+ _view_handle_registry(session_alloc)
+ {
+ _nitpicker_input.sigh(_input_dispatcher);
+ _input_session.event_queue().enabled(true);
+ }
+
+ ~Session_component()
+ {
+ while (Top_level_view *view = _top_level_views.first())
+ _destroy_view_object(*view);
+
+ while (Child_view *view = _child_views.first())
+ _destroy_view_object(*view);
+
+ _ep.dissolve(_input_session);
+ }
+
+ void upgrade(char const *args)
+ {
+ Genode::env()->parent()->upgrade(_session, args);
+ }
+
+ void try_to_init_real_child_views()
+ {
+ for (Child_view *v = _child_views.first(); v; v = v->next())
+ v->try_to_init_real_view();
+ }
+
+ void update_stacking_order_of_children(Window_registry::Id id)
+ {
+ for (Child_view *v = _child_views.first(); v; v = v->next())
+ if (v->belongs_to_win_id(id))
+ v->update_child_stacking();
+ }
+
+ void content_geometry(Window_registry::Id id, Rect rect)
+ {
+ for (Top_level_view *v = _top_level_views.first(); v; v = v->next()) {
+ if (!v->has_win_id(id))
+ continue;
+
+ v->content_geometry(rect);
+ break;
+ }
+ }
+
+ View_capability content_view(Window_registry::Id id)
+ {
+ for (Top_level_view *v = _top_level_views.first(); v; v = v->next())
+ if (v->has_win_id(id.value))
+ return v->content_view();
+
+ return View_capability();
+ }
+
+ bool has_win_id(unsigned id) const
+ {
+ for (Top_level_view const *v = _top_level_views.first(); v; v = v->next())
+ if (v->has_win_id(id))
+ return true;
+
+ return false;
+ }
+
+ void request_resize(Area size)
+ {
+ _requested_size = size;
+
+ /* notify client */
+ if (_mode_sigh.valid())
+ Signal_transmitter(_mode_sigh).submit();
+ }
+
+ /**
+ * Return session capability to real nitpicker session
+ */
+ Capability session() { return _session; }
+
+
+ /*********************************
+ ** Nitpicker session interface **
+ *********************************/
+
+ Framebuffer::Session_capability framebuffer_session() override
+ {
+ return _session.framebuffer_session();
+ }
+
+ Input::Session_capability input_session() override
+ {
+ return _input_session_cap;
+ }
+
+ View_handle create_view(View_handle parent) override
+ {
+ try {
+ View &view = _create_view_object(parent);
+ _ep.manage(view);
+ return _view_handle_registry.alloc(view);
+ }
+ catch (View_handle_registry::Lookup_failed) {
+ return View_handle(); }
+ }
+
+ void destroy_view(View_handle handle) override
+ {
+ try {
+ Locked_ptr view(_view_handle_registry.lookup(handle));
+ if (view.is_valid())
+ _destroy_view_object(*view);
+ }
+ catch (View_handle_registry::Lookup_failed) { }
+
+ _view_handle_registry.free(handle);
+ }
+
+ View_handle view_handle(View_capability view_cap, View_handle handle) override
+ {
+ View *view = dynamic_cast(_ep.rpc_ep().lookup_and_lock(view_cap));
+ if (!view) return View_handle();
+
+ Object_pool::Guard guard(view);
+
+ return _view_handle_registry.alloc(*view, handle);
+ }
+
+ View_capability view_capability(View_handle handle) override
+ {
+ Locked_ptr view(_view_handle_registry.lookup(handle));
+
+ return view.is_valid() ? view->cap() : View_capability();
+ }
+
+ void release_view_handle(View_handle handle) override
+ {
+ try {
+ _view_handle_registry.free(handle); }
+
+ catch (View_handle_registry::Lookup_failed) {
+ PWRN("view lookup failed while releasing view handle");
+ return;
+ }
+ }
+
+ Genode::Dataspace_capability command_dataspace() override
+ {
+ return _command_ds.cap();
+ }
+
+ void execute() override
+ {
+ for (unsigned i = 0; i < _command_buffer.num(); i++) {
+ try {
+ _execute_command(_command_buffer.get(i)); }
+ catch (View_handle_registry::Lookup_failed) {
+ PWRN("view lookup failed during command execution"); }
+ }
+ }
+
+ Framebuffer::Mode mode() override
+ {
+ Framebuffer::Mode const real_mode = _session.mode();
+
+ /*
+ * While resizing the window, return requested window size as
+ * mode
+ */
+ if (_requested_size.valid())
+ return Framebuffer::Mode(_requested_size.w(),
+ _requested_size.h(),
+ real_mode.format());
+
+ /*
+ * If the first top-level view has a defined size, use it
+ * as the size of the virtualized nitpicker session.
+ */
+ if (Top_level_view const *v = _top_level_views.first())
+ if (v->size().valid())
+ return Framebuffer::Mode(v->size().w(),
+ v->size().h(),
+ real_mode.format());
+
+ /*
+ * If top-level view has yet been defined, return the real mode.
+ */
+ return real_mode;
+ }
+
+ void mode_sigh(Genode::Signal_context_capability sigh) override
+ {
+ _mode_sigh = sigh;
+ }
+
+ void buffer(Framebuffer::Mode mode, bool use_alpha) override
+ {
+ _session.buffer(mode, use_alpha);
+ }
+
+ void focus(Genode::Capability) { }
+};
+
+
+class Wm::Nitpicker::Root : public Genode::Root_component,
+ public Decorator_content_callback
+{
+ private:
+
+ Entrypoint &_ep;
+
+ Ram_session_capability _ram;
+
+ enum { STACK_SIZE = 1024*sizeof(long) };
+
+ Window_registry &_window_registry;
+
+ List _sessions;
+
+ protected:
+
+ Session_component *_create_session(const char *args) override
+ {
+ bool direct = false;
+
+ Session_label session_label(args);
+
+ /*
+ * Determine session policy
+ */
+ try {
+ Genode::Xml_node policy = Genode::Session_policy(session_label);
+ direct = policy.attribute("direct").has_value("yes");
+ }
+ catch (...) { }
+
+ Session_component *session = new (md_alloc())
+ Session_component(_ram, _window_registry,
+ _ep, *md_alloc(), session_label, direct);
+
+ _sessions.insert(session);
+
+ return session;
+ }
+
+ void _destroy_session(Session_component *session) override
+ {
+ _sessions.remove(session);
+ Root_component::_destroy_session(session);
+ }
+
+ void _upgrade_session(Session_component *s, const char *args) override
+ {
+ s->upgrade(args);
+ }
+
+ public:
+
+ /**
+ * Constructor
+ */
+ Root(Entrypoint &ep,
+ Window_registry &window_registry, Allocator &md_alloc,
+ Ram_session_capability ram)
+ :
+ Root_component(&ep.rpc_ep(), &md_alloc),
+ _ep(ep), _ram(ram), _window_registry(window_registry)
+ {
+ Genode::env()->parent()->announce(_ep.manage(*this));
+ }
+
+
+ /******************************************
+ ** Decorator_content_callback interface **
+ ******************************************/
+
+ /*
+ * This function is called once the decorator has produced the content
+ * view for a new window, or when a window is brought to the front.
+ */
+ View_capability content_view(Window_registry::Id id) override
+ {
+ /*
+ * Propagate the request to the sessions. It will be picked up
+ * by the session to which the specified window ID belongs.
+ * The real content view will be created as a side effect of
+ * calling 's->content_view'.
+ */
+ for (Session_component *s = _sessions.first(); s; s = s->next())
+ if (s->has_win_id(id.value))
+ return s->content_view(id.value);
+
+ return View_capability();
+ }
+
+ void update_content_child_views(Window_registry::Id id) override
+ {
+ /*
+ * Try to create physical views for its child views.
+ */
+ for (Session_component *s = _sessions.first(); s; s = s->next())
+ s->try_to_init_real_child_views();
+
+ /*
+ * Apply the stacking order to the child views that belong to the
+ * given window ID. I.e., when the window was brought to the front,
+ * we need to restack its child views such that they end up in
+ * front of the top-level view. Otherwise, the top-level view
+ * will obstruct the child views.
+ */
+ for (Session_component *s = _sessions.first(); s; s = s->next())
+ s->update_stacking_order_of_children(id);
+ }
+
+ void content_geometry(Window_registry::Id id, Rect rect) override
+ {
+ for (Session_component *s = _sessions.first(); s; s = s->next())
+ s->content_geometry(id, rect);
+ }
+
+ Capability lookup_nitpicker_session(unsigned win_id)
+ {
+ for (Session_component *s = _sessions.first(); s; s = s->next())
+ if (s->has_win_id(win_id))
+ return s->session();
+
+ return Capability();
+ }
+
+ void request_resize(unsigned win_id, Area size)
+ {
+ for (Session_component *s = _sessions.first(); s; s = s->next())
+ if (s->has_win_id(win_id))
+ return s->request_resize(size);
+ }
+};
+
+#endif /* _NITPICKER_H_ */
diff --git a/repos/gems/src/server/wm/report_rom_slave.h b/repos/gems/src/server/wm/report_rom_slave.h
new file mode 100644
index 0000000000..fd75ab323a
--- /dev/null
+++ b/repos/gems/src/server/wm/report_rom_slave.h
@@ -0,0 +1,173 @@
+/*
+ * \brief Report-ROM slave
+ * \author Norman Feske
+ * \date 2014-02-14
+ */
+
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef _REPORT_ROM_SLAVE_H_
+#define _REPORT_ROM_SLAVE_H_
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+#include
+
+
+class Report_rom_slave : public Genode::Noncopyable
+{
+ private:
+
+ class Policy : public Genode::Slave_policy
+ {
+ private:
+
+ Genode::Root_capability _report_root_cap;
+ Genode::Root_capability _rom_root_cap;
+ bool _announced;
+ Genode::Lock mutable _lock; /* used to wait for announcement */
+
+ protected:
+
+ char const **_permitted_services() const
+ {
+ static char const *permitted_services[] = {
+ "CAP", "LOG", "SIGNAL", "RM", 0 };
+
+ return permitted_services;
+ };
+
+ public:
+
+ Policy(Genode::Rpc_entrypoint &entrypoint,
+ Genode::Ram_session &ram)
+ :
+ Slave_policy("report_rom", entrypoint, &ram),
+ _lock(Genode::Lock::LOCKED)
+ {
+ configure(" "
+ " "
+ " "
+ " "
+ " "
+ " "
+ " "
+ " ");
+ }
+
+ bool announce_service(const char *service_name,
+ Genode::Root_capability root,
+ Genode::Allocator *,
+ Genode::Server *)
+ {
+ if (Genode::strcmp(service_name, "ROM") == 0)
+ _rom_root_cap = root;
+ else if (Genode::strcmp(service_name, "Report") == 0)
+ _report_root_cap = root;
+ else
+ return false;
+
+ if (_rom_root_cap.valid() && _report_root_cap.valid())
+ _lock.unlock();
+
+ return true;
+ }
+
+ Genode::Root_capability report_root() const
+ {
+ Genode::Lock::Guard guard(_lock);
+ return _report_root_cap;
+ }
+
+ Genode::Root_capability rom_root() const
+ {
+ Genode::Lock::Guard guard(_lock);
+ return _rom_root_cap;
+ }
+ };
+
+ Genode::size_t const _ep_stack_size = 4*1024*sizeof(Genode::addr_t);
+ Genode::Rpc_entrypoint _ep;
+ Policy _policy;
+ Genode::size_t const _quota = 1024*1024;
+ Genode::Slave _slave;
+ Genode::Root_client _rom_root;
+ Genode::Root_client _report_root;
+
+ public:
+
+ /**
+ * Constructor
+ *
+ * \param ep entrypoint used for nitpicker child thread
+ * \param ram RAM session used to allocate the configuration
+ * dataspace
+ */
+ Report_rom_slave(Genode::Cap_session &cap, Genode::Ram_session &ram)
+ :
+ _ep(&cap, _ep_stack_size, "report_rom"),
+ _policy(_ep, ram),
+ _slave(_ep, _policy, _quota),
+ _rom_root(_policy.rom_root()),
+ _report_root(_policy.report_root())
+ { }
+
+ Genode::Rom_session_capability rom_session(char const *label)
+ {
+ using namespace Genode;
+
+ enum { ARGBUF_SIZE = 128 };
+ char argbuf[ARGBUF_SIZE];
+ argbuf[0] = 0;
+
+ /*
+ * Declare ram-quota donation
+ */
+ enum { SESSION_METADATA = 4*1024 };
+ Arg_string::set_arg(argbuf, sizeof(argbuf), "ram_quota", SESSION_METADATA);
+
+ /*
+ * Set session label
+ */
+ Arg_string::set_arg(argbuf, sizeof(argbuf), "label", label);
+
+ Session_capability session_cap = _rom_root.session(argbuf, Affinity());
+
+ return static_cap_cast(session_cap);
+ }
+
+ Genode::Capability report_session(char const *label)
+ {
+ using namespace Genode;
+
+ enum { ARGBUF_SIZE = 128 };
+ char argbuf[ARGBUF_SIZE];
+ argbuf[0] = 0;
+
+ /*
+ * Declare ram-quota donation
+ */
+ enum { BUFFER_SIZE = 4096, SESSION_METADATA = BUFFER_SIZE + 8*1024 };
+ Arg_string::set_arg(argbuf, sizeof(argbuf), "ram_quota", SESSION_METADATA);
+ Arg_string::set_arg(argbuf, sizeof(argbuf), "buffer_size", BUFFER_SIZE);
+
+ /*
+ * Set session label
+ */
+ Arg_string::set_arg(argbuf, sizeof(argbuf), "label", label);
+
+ Session_capability session_cap = _report_root.session(argbuf, Affinity());
+
+ return static_cap_cast(session_cap);
+ }
+};
+
+#endif /* _REPORT_ROM_SLAVE_H_ */
diff --git a/repos/gems/src/server/wm/single_session_service.h b/repos/gems/src/server/wm/single_session_service.h
new file mode 100644
index 0000000000..f2aad6f6e4
--- /dev/null
+++ b/repos/gems/src/server/wm/single_session_service.h
@@ -0,0 +1,41 @@
+/*
+ * \brief Utility for implementing a local service with a single session
+ * \author Norman Feske
+ * \date 2014-02-14
+ */
+
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef _SINGLE_SESSION_SERVICE_H_
+#define _SINGLE_SESSION_SERVICE_H_
+
+#include
+
+namespace Wm { class Single_session_service; }
+
+struct Wm::Single_session_service : Genode::Service
+{
+ Genode::Session_capability session_cap;
+
+ Single_session_service(char const *service_name,
+ Genode::Session_capability session_cap)
+ :
+ Service(service_name), session_cap(session_cap)
+ { }
+
+ Genode::Session_capability
+ session(const char *, Genode::Affinity const &) override
+ {
+ return session_cap;
+ }
+
+ void upgrade(Genode::Session_capability, const char *) override { }
+ void close(Genode::Session_capability) override { }
+};
+
+#endif /* _SINGLE_SESSION_SERVICE_H_ */
diff --git a/repos/gems/src/server/wm/target.mk b/repos/gems/src/server/wm/target.mk
new file mode 100644
index 0000000000..1d59dfe223
--- /dev/null
+++ b/repos/gems/src/server/wm/target.mk
@@ -0,0 +1,4 @@
+TARGET = wm
+SRC_CC = main.cc
+LIBS = base server config
+INC_DIR += $(PRG_DIR)
diff --git a/repos/gems/src/server/wm/window_layouter_slave.h b/repos/gems/src/server/wm/window_layouter_slave.h
new file mode 100644
index 0000000000..63de576a34
--- /dev/null
+++ b/repos/gems/src/server/wm/window_layouter_slave.h
@@ -0,0 +1,137 @@
+/*
+ * \brief Slave for managing the window layout
+ * \author Norman Feske
+ * \date 2014-02-14
+ */
+
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef _WINDOW_LAYOUTER_SLAVE_H_
+#define _WINDOW_LAYOUTER_SLAVE_H_
+
+namespace Wm {
+
+ class Window_layouter_slave;
+
+ using Genode::Rom_session_capability;
+ using Genode::Capability;
+}
+
+
+class Wm::Window_layouter_slave
+{
+ private:
+
+ Genode::Ram_session &_ram;
+
+ class Policy : public Genode::Slave_policy
+ {
+ private:
+
+ Single_session_service _window_list_rom_service;
+ Single_session_service _hover_rom_service;
+ Single_session_service _input_service;
+ Single_session_service _window_layout_report_service;
+ Single_session_service _resize_request_report_service;
+ Single_session_service _focus_report_service;
+
+ protected:
+
+ char const **_permitted_services() const
+ {
+ static char const *permitted_services[] = {
+ "CAP", "LOG", "SIGNAL", "RM", "Timer", 0 };
+
+ return permitted_services;
+ };
+
+ public:
+
+ Policy(Genode::Rpc_entrypoint &entrypoint,
+ Genode::Ram_session &ram,
+ Rom_session_capability window_list_rom,
+ Rom_session_capability hover_rom,
+ Input::Session_capability input,
+ Capability window_layout_report,
+ Capability resize_request_report,
+ Capability focus_report)
+ :
+ Slave_policy("floating_window_layouter", entrypoint, &ram),
+ _window_list_rom_service("ROM", window_list_rom),
+ _hover_rom_service("ROM", hover_rom),
+ _input_service("Input", input),
+ _window_layout_report_service("Report", window_layout_report),
+ _resize_request_report_service("Report", resize_request_report),
+ _focus_report_service("Report", focus_report)
+ { }
+
+ Genode::Service *resolve_session_request(const char *service_name,
+ const char *args) override
+ {
+ using Genode::strcmp;
+
+ char label[128];
+ Arg_string::find_arg(args, "label").string(label, sizeof(label), "");
+
+ if (strcmp(service_name, "ROM") == 0) {
+
+ if (strcmp(label, "floating_window_layouter -> window_list") == 0)
+ return &_window_list_rom_service;
+
+ if (strcmp(label, "floating_window_layouter -> hover") == 0)
+ return &_hover_rom_service;
+ }
+
+ if (strcmp(service_name, "Report") == 0) {
+
+ if (strcmp(label, "floating_window_layouter -> window_layout") == 0)
+ return &_window_layout_report_service;
+
+ if (strcmp(label, "floating_window_layouter -> resize_request") == 0)
+ return &_resize_request_report_service;
+
+ if (strcmp(label, "floating_window_layouter -> focus") == 0)
+ return &_focus_report_service;
+ }
+
+ if (strcmp(service_name, "Input") == 0)
+ return &_input_service;
+
+ return Genode::Slave_policy::resolve_session_request(service_name, args);
+ }
+ };
+
+ Genode::size_t const _ep_stack_size = 4*1024*sizeof(Genode::addr_t);
+ Genode::Rpc_entrypoint _ep;
+ Policy _policy;
+ Genode::size_t const _quota = 1*1024*1024;
+ Genode::Slave _slave;
+
+ public:
+
+ /**
+ * Constructor
+ */
+ Window_layouter_slave(Genode::Cap_session &cap,
+ Genode::Ram_session &ram,
+ Rom_session_capability window_list_rom,
+ Rom_session_capability hover_rom,
+ Input::Session_capability input,
+ Capability window_layout_report,
+ Capability resize_request_report,
+ Capability focus_report)
+ :
+ _ram(ram),
+ _ep(&cap, _ep_stack_size, "floating_window_layouter"),
+ _policy(_ep, ram, window_list_rom, hover_rom, input,
+ window_layout_report, resize_request_report, focus_report),
+ _slave(_ep, _policy, _quota)
+ { }
+};
+
+#endif /* _WINDOW_LAYOUTER_SLAVE_H_ */
diff --git a/repos/gems/src/server/wm/window_registry.h b/repos/gems/src/server/wm/window_registry.h
new file mode 100644
index 0000000000..d416fe8f5f
--- /dev/null
+++ b/repos/gems/src/server/wm/window_registry.h
@@ -0,0 +1,188 @@
+/*
+ * \brief Window registry
+ * \author Norman Feske
+ * \date 2014-05-02
+ */
+
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef _WINDOW_REGISTRY_H_
+#define _WINDOW_REGISTRY_H_
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+#include
+
+/* local includes */
+#include
+
+
+namespace Wm { class Window_registry; }
+
+
+namespace Wm {
+ using Genode::Allocator;
+ using Genode::List;
+ using Genode::Xml_generator;
+
+ typedef Genode::Surface_base::Area Area;
+ typedef Genode::Surface_base::Point Point;
+ typedef Genode::Surface_base::Rect Rect;
+}
+
+
+class Wm::Window_registry
+{
+ public:
+
+ struct Id
+ {
+ unsigned value;
+
+ Id(unsigned value) : value(value) { }
+
+ Id() /* invalid */ : value(0) { }
+
+ bool operator == (Id const &other) const { return value == other.value; }
+
+ bool valid() const { return value != 0; }
+ };
+
+ class Window : public List::Element
+ {
+ public:
+
+ typedef Genode::String<200> Title;
+
+ private:
+
+ Id const _id;
+
+ Title _title;
+
+ Area _size;
+
+ friend class Window_registry;
+
+ Window(Id id) : _id(id) { }
+
+ public:
+
+ Id id() const { return _id; }
+
+ /*
+ * Accessors for setting attributes
+ */
+ void attr(Title const &title) { _title = title; }
+ void attr(Area size) { _size = size; }
+
+ void generate_window_list_entry_xml(Xml_generator &xml) const
+ {
+ xml.node("window", [&] () {
+ xml.attribute("id", _id.value);
+ xml.attribute("title", _title.string());
+ xml.attribute("width", _size.w());
+ xml.attribute("height", _size.h());
+ });
+ }
+ };
+
+ private:
+
+ Allocator &_alloc;
+ Local_reporter &_window_list_reporter;
+
+ enum { MAX_WINDOWS = 1024 };
+
+ Genode::Bit_allocator _window_ids;
+
+ List _windows;
+
+ Window *_lookup(Id id)
+ {
+ for (Window *w = _windows.first(); w; w = w->next())
+ if (w->id() == id)
+ return w;
+
+ return 0;
+ }
+
+ void _report_updated_window_list_model() const
+ {
+ Local_reporter::Xml_generator xml(_window_list_reporter, [&] ()
+ {
+ for (Window const *w = _windows.first(); w; w = w->next())
+ w->generate_window_list_entry_xml(xml);
+ });
+ }
+
+ template
+ void _set_attr(Id const id, ATTR const &value)
+ {
+ Window * const win = _lookup(id);
+
+ if (!win) {
+ PWRN("lookup for window ID %d failed", id.value);
+ return;
+ }
+
+ win->attr(value);
+
+ _report_updated_window_list_model();
+ }
+
+ public:
+
+ Window_registry(Allocator &alloc, Local_reporter &window_list_reporter)
+ :
+ _alloc(alloc), _window_list_reporter(window_list_reporter)
+ {
+ /* preserve ID 0 to represent an invalid ID */
+ _window_ids.alloc();
+ }
+
+ Id create()
+ {
+ Window * const win = new (_alloc) Window(_window_ids.alloc());
+
+ _windows.insert(win);
+
+ /*
+ * Even though we change the window-list model by adding a
+ * window, we don't call '_report_updated_window_list_model' here
+ * because the window does not have any useful properties before
+ * the 'size' function has been called.
+ *
+ * XXX should we pass the initial size as argument to this function?
+ */
+
+ return win->id();
+ }
+
+ void destroy(Id id)
+ {
+ Window * const win = _lookup(id);
+ if (!win)
+ return;
+
+ _windows.remove(win);
+
+ Genode::destroy(&_alloc, win);
+
+ _report_updated_window_list_model();
+ }
+
+ void size(Id id, Area size) { _set_attr(id, size); }
+
+ void title(Id id, Window::Title title) { _set_attr(id, title); }
+};
+
+#endif /* _WINDOW_REGISTRY_H_ */