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>