Window manager

The window manager provides a nitpicker session interface. In contrast
to the nitpicker server, which leaves the view layout up to the client,
the window manager organizes the views on screen according to a policy
provided by a window layouter. Furthermore, it equips views with window
decorations as provided by a window decorator. Both layouter and
decorator are independent programs.
This commit is contained in:
Norman Feske 2014-01-06 19:15:14 +01:00
parent 59555c540e
commit acd9599890
13 changed files with 3297 additions and 0 deletions

224
repos/gems/run/wm.run Normal file
View File

@ -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 {
<config>
<parent-provides>
<service name="ROM"/>
<service name="RAM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="CAP"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
<service name="SIGNAL"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>}
append_if [have_spec sdl] config {
<start name="fb_sdl">
<resource name="RAM" quantum="4M"/>
<provides>
<service name="Input"/>
<service name="Framebuffer"/>
</provides>
</start>}
append_if [have_spec pci] config {
<start name="pci_drv">
<resource name="RAM" quantum="1M"/>
<provides><service name="PCI"/></provides>
</start>}
append_if [have_spec framebuffer] config {
<start name="fb_drv">
<resource name="RAM" quantum="4M"/>
<provides><service name="Framebuffer"/></provides>
</start>}
append_if [have_spec gpio] config {
<start name="gpio_drv">
<resource name="RAM" quantum="4M"/>
<provides><service name="Gpio"/></provides>
<config/>
</start>}
append_if [have_spec exynos5] config {
<start name="platform_drv">
<resource name="RAM" quantum="1M"/>
<provides><service name="Regulator"/></provides>
<config/>
</start>}
append_if [have_spec platform_rpi] config {
<start name="platform_drv">
<resource name="RAM" quantum="1M"/>
<provides><service name="Platform"/></provides>
<config/>
</start>}
append_if [have_spec imx53] config {
<start name="platform_drv">
<resource name="RAM" quantum="1M"/>
<provides><service name="Platform"/></provides>
<config/>
</start>
<start name="input_drv">
<resource name="RAM" quantum="1M"/>
<provides><service name="Input"/></provides>
<config/>
</start> }
append_if [have_spec ps2] config {
<start name="ps2_drv">
<resource name="RAM" quantum="1M"/>
<provides><service name="Input"/></provides>
</start>}
append_if [expr ![have_spec ps2] && [have_spec usb]] config {
<start name="usb_drv">
<resource name="RAM" quantum="12M"/>
<provides><service name="Input"/></provides>
<config ehci="yes" uhci="yes" xhci="no"> <hid/> </config>
</start> }
append config {
<start name="timer">
<resource name="RAM" quantum="1M"/>
<provides><service name="Timer"/></provides>
</start>
<start name="nitpicker">
<resource name="RAM" quantum="1M"/>
<provides><service name="Nitpicker"/></provides>
<config>
<domain name="pointer" layer="1" xray="no" origin="pointer" />
<domain name="decorator" layer="2" xray="opaque" />
<domain name="default" layer="2" />
<policy label="pointer" domain="pointer"/>
<policy label="" domain="default"/>
<policy label="wm -> decorator" domain="decorator"/>
<global-key name="KEY_SCROLLLOCK" operation="xray" />
<global-key name="KEY_SYSRQ" operation="kill" />
<global-key name="KEY_PRINT" operation="kill" />
<global-key name="KEY_F11" operation="kill" />
<global-key name="KEY_F12" operation="xray" />
</config>
</start>
<start name="pointer">
<resource name="RAM" quantum="1M"/>
<route>
<service name="Nitpicker"> <child name="nitpicker"/> </service>
<any-service> <parent/> <any-child/> </any-service>
</route>
</start>
<start name="wm">
<resource name="RAM" quantum="32M"/>
<provides><service name="Nitpicker"/></provides>
<route>
<any-service>
<child name="nitpicker"/> <parent/> <any-child/>
</any-service>
</route>
</start>
<start name="testnit">
<resource name="RAM" quantum="1M"/>
<route>
<any-service> <child name="wm"/> <parent/> <any-child/>
</any-service>
</route>
</start>
<start name="direct_testnit">
<binary name="testnit" />
<resource name="RAM" quantum="1M"/>
<route>
<any-service> <child name="nitpicker"/> <parent/> <any-child/>
</any-service>
</route>
</start>
<start name="launchpad">
<resource name="RAM" quantum="20M"/>
<config>
<launcher name="testnit" ram_quota="768K" />
</config>
<route>
<any-service> <child name="wm"/> <parent/> <any-child/>
</any-service>
</route>
</start>
<start name="nit_fb">
<resource name="RAM" quantum="20M"/>
<config width="400" height="300" />
<route>
<any-service> <child name="wm"/> <parent/> <any-child/>
</any-service>
</route>
</start>
</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

View File

@ -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 <base/printf.h>
#include <base/signal.h>
#include <os/attached_rom_dataspace.h>
#include <os/reporter.h>
#include <input_session/connection.h>
#include <input/event.h>
#include <input/keycodes.h>
#include <rom_session/connection.h>
#include <decorator/xml_utils.h>
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<Window>::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<Window> 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<Main> 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<Main> 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<Main> 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<char>())); }
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<char>());
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<Input::Event>();
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<Signal_dispatcher_base *>(sig.context());
if (dispatcher)
dispatcher->dispatch(sig.num());
}
}

View File

@ -0,0 +1,4 @@
TARGET = floating_window_layouter
SRC_CC = main.cc
LIBS = base cxx

View File

@ -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 <util/string.h>
#include <os/server.h>
#include <os/attached_dataspace.h>
#include <nitpicker_session/connection.h>
#include <input_session/client.h>
#include <input/event.h>
#include <input/component.h>
/* local includes */
#include <window_registry.h>
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<Entry>::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<Entry> _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<Entry *>(&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<Nitpicker::Session>
{
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<Command_buffer>();
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<Decorator_nitpicker_session>
_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<Input::Event>();
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<Nitpicker::Session>) { }
};
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<Decorator_nitpicker_session>::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_ */

View File

@ -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<Report::Session> 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<Report::Session> 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_ */

View File

@ -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 <os/attached_dataspace.h>
#include <util/xml_generator.h>
#include <report_session/client.h>
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<Report::Session> session_cap)
:
_session(session_cap), _ds(_session.dataspace()), _name(name)
{ }
struct Xml_generator : public Genode::Xml_generator
{
template <typename FUNC>
Xml_generator(Local_reporter &reporter, FUNC const &func)
:
Genode::Xml_generator(reporter._ds.local_addr<char>(),
reporter._ds.size(),
reporter._name,
func)
{
reporter._session.submit(used());
}
};
};
#endif /* _LOCAL_REPORTER_H_ */

View File

@ -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 <os/server.h>
#include <nitpicker_session/client.h>
#include <framebuffer_session/client.h>
#include <cap_session/connection.h>
#include <os/attached_dataspace.h>
#include <util/volatile_object.h>
#include <util/xml_node.h>
/* local includes */
#include <single_session_service.h>
#include <report_rom_slave.h>
#include <decorator_nitpicker.h>
#include <local_reporter.h>
#include <decorator_slave.h>
#include <window_layouter_slave.h>
#include <nitpicker.h>
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<Report::Session> pointer_report = report_rom_slave.report_session("pointer");
Local_reporter pointer_reporter = { "pointer", pointer_report };
/* hovered element reported by decorator */
Capability<Report::Session> hover_report = report_rom_slave.report_session("hover");
Capability<Report::Session> window_list_report = report_rom_slave.report_session("window_list");
Local_reporter window_list_reporter = { "window_list", window_list_report };
Capability<Report::Session> window_layout_report = report_rom_slave.report_session("window_layout");
Capability<Report::Session> resize_request_report = report_rom_slave.report_session("resize_request");
Capability<Report::Session> 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<Attached_dataspace> 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<char>()).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<Main> focus_dispatcher = { ep, *this, &Main::handle_focus_update };
Genode::Lazy_volatile_object<Attached_dataspace> 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<char>()).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<Main> 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);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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 <base/lock.h>
#include <os/slave.h>
#include <report_session/report_session.h>
#include <rom_session/capability.h>
#include <root/client.h>
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("<config> <rom>"
" <policy label=\"window_list\" report=\"window_list\"/>"
" <policy label=\"window_layout\" report=\"window_layout\"/>"
" <policy label=\"resize_request\" report=\"resize_request\"/>"
" <policy label=\"pointer\" report=\"pointer\"/>"
" <policy label=\"hover\" report=\"hover\"/>"
" <policy label=\"focus\" report=\"focus\"/>"
"</rom> </config>");
}
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<Genode::Rom_session>(session_cap);
}
Genode::Capability<Report::Session> 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<Report::Session>(session_cap);
}
};
#endif /* _REPORT_ROM_SLAVE_H_ */

View File

@ -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 <base/service.h>
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_ */

View File

@ -0,0 +1,4 @@
TARGET = wm
SRC_CC = main.cc
LIBS = base server config
INC_DIR += $(PRG_DIR)

View File

@ -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<Report::Session> window_layout_report,
Capability<Report::Session> resize_request_report,
Capability<Report::Session> 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<Report::Session> window_layout_report,
Capability<Report::Session> resize_request_report,
Capability<Report::Session> 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_ */

View File

@ -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 <util/bit_allocator.h>
#include <util/list.h>
#include <util/xml_generator.h>
#include <base/allocator.h>
#include <os/surface.h>
/* local includes */
#include <local_reporter.h>
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<Window>::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<MAX_WINDOWS> _window_ids;
List<Window> _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 <typename ATTR>
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_ */