2015-11-11 16:54:08 +01:00
|
|
|
/*
|
|
|
|
* \brief Window decorator that can be styled
|
|
|
|
* \author Norman Feske
|
|
|
|
* \date 2015-11-11
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (C) 2015 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 <os/server.h>
|
|
|
|
|
|
|
|
/* decorator includes */
|
|
|
|
#include <decorator/window_stack.h>
|
|
|
|
#include <decorator/xml_utils.h>
|
|
|
|
|
|
|
|
/* local includes */
|
|
|
|
#include "window.h"
|
|
|
|
|
|
|
|
|
|
|
|
namespace Decorator {
|
|
|
|
using namespace Genode;
|
|
|
|
struct Main;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct Decorator::Main : Window_factory_base
|
|
|
|
{
|
|
|
|
Server::Entrypoint &ep;
|
|
|
|
|
|
|
|
Window_stack window_stack = { *this };
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Install handler for responding to window-layout changes
|
|
|
|
*/
|
|
|
|
void handle_window_layout_update(unsigned);
|
|
|
|
|
|
|
|
Signal_rpc_member<Main> window_layout_dispatcher = {
|
|
|
|
ep, *this, &Main::handle_window_layout_update };
|
|
|
|
|
|
|
|
Attached_rom_dataspace window_layout { "window_layout" };
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Install handler for responding to pointer-position updates
|
|
|
|
*/
|
|
|
|
void handle_pointer_update(unsigned);
|
|
|
|
|
|
|
|
Signal_rpc_member<Main> pointer_dispatcher = {
|
|
|
|
ep, *this, &Main::handle_pointer_update };
|
|
|
|
|
2015-12-04 16:18:33 +01:00
|
|
|
Lazy_volatile_object<Attached_rom_dataspace> pointer;
|
2015-11-11 16:54:08 +01:00
|
|
|
|
|
|
|
Window_base::Hover hover;
|
|
|
|
|
|
|
|
Reporter hover_reporter = { "hover" };
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Nitpicker connection used to sync animations
|
|
|
|
*/
|
|
|
|
Nitpicker::Connection nitpicker;
|
|
|
|
|
|
|
|
bool window_layout_update_needed = false;
|
|
|
|
|
|
|
|
Animator animator;
|
|
|
|
|
|
|
|
Theme theme { *Genode::env()->heap() };
|
|
|
|
|
2016-02-01 12:40:47 +01:00
|
|
|
Reporter decorator_margins_reporter = { "decorator_margins" };
|
|
|
|
|
2015-11-11 16:54:08 +01:00
|
|
|
/**
|
|
|
|
* Process the update every 'frame_period' nitpicker sync signals. The
|
|
|
|
* 'frame_cnt' holds the counter of the nitpicker sync signals.
|
|
|
|
*
|
|
|
|
* A lower 'frame_period' value makes the decorations more responsive
|
|
|
|
* but it also puts more load on the system.
|
|
|
|
*
|
|
|
|
* If the nitpicker sync signal fires every 10 milliseconds, a
|
|
|
|
* 'frame_period' of 2 results in an update rate of 1000/20 = 50 frames per
|
|
|
|
* second.
|
|
|
|
*/
|
|
|
|
unsigned frame_cnt = 0;
|
|
|
|
unsigned frame_period = 2;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Install handler for responding to nitpicker sync events
|
|
|
|
*/
|
|
|
|
void handle_nitpicker_sync(unsigned);
|
|
|
|
|
|
|
|
Signal_rpc_member<Main> nitpicker_sync_dispatcher = {
|
|
|
|
ep, *this, &Main::handle_nitpicker_sync };
|
|
|
|
|
|
|
|
Config config;
|
|
|
|
|
|
|
|
void handle_config(unsigned);
|
|
|
|
|
|
|
|
Signal_rpc_member<Main> config_dispatcher = {
|
|
|
|
ep, *this, &Main::handle_config};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*/
|
|
|
|
Main(Server::Entrypoint &ep) : ep(ep)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Eagerly upgrade the session quota in order to be able to create a
|
|
|
|
* high amount of view handles.
|
|
|
|
*
|
|
|
|
* XXX Consider upgrading the session quota on demand by responding
|
|
|
|
* to Out_of_metadata exceptions raised by the create_view
|
|
|
|
* and view_handle operations. Currently, these exceptions will
|
|
|
|
* abort the decorator.
|
|
|
|
*/
|
|
|
|
Genode::env()->parent()->upgrade(nitpicker, "ram_quota=256K");
|
|
|
|
|
|
|
|
Genode::config()->sigh(config_dispatcher);
|
|
|
|
handle_config(0);
|
|
|
|
|
|
|
|
window_layout.sigh(window_layout_dispatcher);
|
2015-12-04 16:18:33 +01:00
|
|
|
|
|
|
|
try {
|
|
|
|
pointer.construct("pointer");
|
|
|
|
pointer->sigh(pointer_dispatcher);
|
|
|
|
} catch (Genode::Rom_connection::Rom_connection_failed) {
|
|
|
|
PINF("pointer information unavailable");
|
|
|
|
|
|
|
|
PDBG("is_constructed=%d", pointer.is_constructed());
|
|
|
|
}
|
2015-11-11 16:54:08 +01:00
|
|
|
|
|
|
|
nitpicker.framebuffer()->sync_sigh(nitpicker_sync_dispatcher);
|
|
|
|
|
|
|
|
hover_reporter.enabled(true);
|
|
|
|
|
2016-02-01 12:40:47 +01:00
|
|
|
decorator_margins_reporter.enabled(true);
|
|
|
|
|
|
|
|
Genode::Reporter::Xml_generator xml(decorator_margins_reporter, [&] ()
|
|
|
|
{
|
|
|
|
xml.node("floating", [&] () {
|
|
|
|
|
|
|
|
Theme::Margins const margins = theme.decor_margins();
|
|
|
|
|
|
|
|
xml.attribute("top", margins.top);
|
|
|
|
xml.attribute("bottom", margins.bottom);
|
|
|
|
xml.attribute("left", margins.left);
|
|
|
|
xml.attribute("right", margins.right);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2015-11-11 16:54:08 +01:00
|
|
|
/* import initial state */
|
|
|
|
handle_pointer_update(0);
|
|
|
|
handle_window_layout_update(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Window_factory_base interface
|
|
|
|
*/
|
|
|
|
Window_base *create(Xml_node window_node) override
|
|
|
|
{
|
|
|
|
return new (env()->heap())
|
|
|
|
Window(attribute(window_node, "id", 0UL), nitpicker, animator,
|
|
|
|
*env()->ram_session(), theme, config);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Window_factory_base interface
|
|
|
|
*/
|
|
|
|
void destroy(Window_base *window) override
|
|
|
|
{
|
|
|
|
Genode::destroy(env()->heap(), static_cast<Window *>(window));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
void Decorator::Main::handle_config(unsigned)
|
|
|
|
{
|
|
|
|
Genode::config()->reload();
|
|
|
|
|
|
|
|
/* notify all windows to consider the updated policy */
|
|
|
|
window_stack.for_each_window([&] (Window_base &window) {
|
|
|
|
static_cast<Window &>(window).adapt_to_changed_config(); });
|
|
|
|
|
|
|
|
/* trigger redraw of the window stack */
|
|
|
|
handle_window_layout_update(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static Decorator::Window_base::Hover
|
|
|
|
find_hover(Genode::Xml_node pointer_node, Decorator::Window_stack &window_stack)
|
|
|
|
{
|
|
|
|
if (!pointer_node.has_attribute("xpos")
|
|
|
|
|| !pointer_node.has_attribute("ypos"))
|
|
|
|
return Decorator::Window_base::Hover();
|
|
|
|
|
|
|
|
return window_stack.hover(Decorator::point_attribute(pointer_node));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void update_hover_report(Genode::Xml_node pointer_node,
|
|
|
|
Decorator::Window_stack &window_stack,
|
|
|
|
Decorator::Window_base::Hover &hover,
|
|
|
|
Genode::Reporter &hover_reporter)
|
|
|
|
{
|
|
|
|
Decorator::Window_base::Hover const new_hover =
|
|
|
|
find_hover(pointer_node, window_stack);
|
|
|
|
|
|
|
|
/* produce report only if hover state changed */
|
|
|
|
if (new_hover != hover) {
|
|
|
|
|
|
|
|
hover = new_hover;
|
|
|
|
|
|
|
|
Genode::Reporter::Xml_generator xml(hover_reporter, [&] ()
|
|
|
|
{
|
|
|
|
if (hover.window_id > 0) {
|
|
|
|
|
|
|
|
xml.node("window", [&] () {
|
|
|
|
|
|
|
|
xml.attribute("id", hover.window_id);
|
|
|
|
|
|
|
|
if (hover.left_sizer) xml.node("left_sizer");
|
|
|
|
if (hover.right_sizer) xml.node("right_sizer");
|
|
|
|
if (hover.top_sizer) xml.node("top_sizer");
|
|
|
|
if (hover.bottom_sizer) xml.node("bottom_sizer");
|
|
|
|
if (hover.title) xml.node("title");
|
|
|
|
if (hover.closer) xml.node("closer");
|
|
|
|
if (hover.minimizer) xml.node("minimizer");
|
|
|
|
if (hover.maximizer) xml.node("maximizer");
|
|
|
|
if (hover.unmaximizer) xml.node("unmaximizer");
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Decorator::Main::handle_window_layout_update(unsigned)
|
|
|
|
{
|
|
|
|
window_layout.update();
|
|
|
|
|
|
|
|
window_layout_update_needed = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Decorator::Main::handle_nitpicker_sync(unsigned)
|
|
|
|
{
|
|
|
|
if (frame_cnt++ < frame_period)
|
|
|
|
return;
|
|
|
|
|
|
|
|
frame_cnt = 0;
|
|
|
|
|
|
|
|
bool model_updated = false;
|
|
|
|
|
|
|
|
if (window_layout_update_needed && window_layout.is_valid()) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
Xml_node xml(window_layout.local_addr<char>(),
|
|
|
|
window_layout.size());
|
|
|
|
window_stack.update_model(xml);
|
|
|
|
|
|
|
|
model_updated = true;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A decorator element might have appeared or disappeared under
|
|
|
|
* the pointer.
|
|
|
|
*/
|
2015-12-04 16:18:33 +01:00
|
|
|
if (pointer.is_constructed() && pointer->is_valid())
|
|
|
|
update_hover_report(Xml_node(pointer->local_addr<char>()),
|
2015-11-11 16:54:08 +01:00
|
|
|
window_stack, hover, hover_reporter);
|
|
|
|
|
|
|
|
} catch (Xml_node::Invalid_syntax) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* An error occured with processing the XML model. Flush the
|
|
|
|
* internal representation.
|
|
|
|
*/
|
|
|
|
window_stack.flush();
|
|
|
|
}
|
|
|
|
|
|
|
|
window_layout_update_needed = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool const windows_animated = window_stack.schedule_animated_windows();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* To make the perceived animation speed independent from the setting of
|
|
|
|
* 'frame_period', we update the animation as often as the nitpicker
|
|
|
|
* sync signal occurs.
|
|
|
|
*/
|
|
|
|
for (unsigned i = 0; i < frame_period; i++)
|
|
|
|
animator.animate();
|
|
|
|
|
|
|
|
if (!model_updated && !windows_animated)
|
|
|
|
return;
|
|
|
|
|
|
|
|
window_stack.update_nitpicker_views();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Decorator::Main::handle_pointer_update(unsigned)
|
|
|
|
{
|
2015-12-04 16:18:33 +01:00
|
|
|
if (!pointer.is_constructed())
|
|
|
|
return;
|
|
|
|
|
|
|
|
pointer->update();
|
2015-11-11 16:54:08 +01:00
|
|
|
|
2015-12-04 16:18:33 +01:00
|
|
|
if (pointer->is_valid())
|
|
|
|
update_hover_report(Xml_node(pointer->local_addr<char>()),
|
2015-11-11 16:54:08 +01:00
|
|
|
window_stack, hover, hover_reporter);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/************
|
|
|
|
** Server **
|
|
|
|
************/
|
|
|
|
|
|
|
|
namespace Server {
|
|
|
|
|
|
|
|
char const *name() { return "decorator_ep"; }
|
|
|
|
|
|
|
|
size_t stack_size() { return 8*1024*sizeof(long); }
|
|
|
|
|
|
|
|
void construct(Entrypoint &ep)
|
|
|
|
{
|
|
|
|
static Decorator::Main main(ep);
|
|
|
|
}
|
|
|
|
}
|