mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-19 13:47:56 +00:00
Minimalistic decorator for window system
This commit is contained in:
parent
20090000d2
commit
09ec663d2d
190
repos/gems/run/decorator.run
Normal file
190
repos/gems/run/decorator.run
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
#
|
||||||
|
# Build
|
||||||
|
#
|
||||||
|
if {![have_spec linux]} {
|
||||||
|
puts "Runs on Linux only"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
set build_components {
|
||||||
|
core init drivers/timer drivers/framebuffer/sdl
|
||||||
|
server/dynamic_rom server/report_rom server/nitpicker app/decorator
|
||||||
|
}
|
||||||
|
|
||||||
|
build $build_components
|
||||||
|
|
||||||
|
create_boot_directory
|
||||||
|
|
||||||
|
#
|
||||||
|
# Generate config
|
||||||
|
#
|
||||||
|
|
||||||
|
append config {
|
||||||
|
<config>
|
||||||
|
<parent-provides>
|
||||||
|
<service name="ROM"/>
|
||||||
|
<service name="RAM"/>
|
||||||
|
<service name="RM"/>
|
||||||
|
<service name="LOG"/>
|
||||||
|
<service name="IRQ"/>
|
||||||
|
<service name="IO_MEM"/>
|
||||||
|
<service name="IO_PORT"/>
|
||||||
|
<service name="CAP"/>
|
||||||
|
<service name="SIGNAL"/>
|
||||||
|
</parent-provides>
|
||||||
|
<default-route>
|
||||||
|
<any-service> <parent/> <any-child/> </any-service>
|
||||||
|
</default-route>
|
||||||
|
<start name="fb_sdl">
|
||||||
|
<resource name="RAM" quantum="4M"/>
|
||||||
|
<provides>
|
||||||
|
<service name="Input"/>
|
||||||
|
<service name="Framebuffer"/>
|
||||||
|
</provides>
|
||||||
|
</start>
|
||||||
|
<start name="timer">
|
||||||
|
<resource name="RAM" quantum="1M"/>
|
||||||
|
<provides><service name="Timer"/></provides>
|
||||||
|
</start>
|
||||||
|
<start name="report_rom">
|
||||||
|
<resource name="RAM" quantum="2M"/>
|
||||||
|
<provides> <service name="ROM" />
|
||||||
|
<service name="Report" /> </provides>
|
||||||
|
<config>
|
||||||
|
<rom>
|
||||||
|
<policy label="decorator -> pointer" report="nitpicker -> pointer"/>
|
||||||
|
</rom>
|
||||||
|
</config>
|
||||||
|
</start>
|
||||||
|
<start name="nitpicker">
|
||||||
|
<resource name="RAM" quantum="1M"/>
|
||||||
|
<provides><service name="Nitpicker"/></provides>
|
||||||
|
<config>
|
||||||
|
<report pointer="yes" />
|
||||||
|
<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="dynamic_rom">
|
||||||
|
<resource name="RAM" quantum="4M"/>
|
||||||
|
<provides><service name="ROM"/></provides>
|
||||||
|
<config verbose="yes">
|
||||||
|
<rom name="window_layout">
|
||||||
|
<inline description="initial state" />
|
||||||
|
<sleep milliseconds="500" />
|
||||||
|
<inline description="open window 1">
|
||||||
|
<window_layout>
|
||||||
|
<window id="1" title="Genode Toolchain"
|
||||||
|
xpos="100" ypos="50" width="200" height="200"
|
||||||
|
focused="yes" />
|
||||||
|
</window_layout>
|
||||||
|
</inline>
|
||||||
|
<sleep milliseconds="1000" />
|
||||||
|
<inline description="open window 2 behind window 1">
|
||||||
|
<window_layout>
|
||||||
|
<window id="1" title="Genode Toolchain"
|
||||||
|
xpos="100" ypos="50" width="200" height="200"
|
||||||
|
focused="yes" />
|
||||||
|
<window id="2" title="Arora (2)"
|
||||||
|
xpos="170" ypos="150" width="300" height="200" />
|
||||||
|
</window_layout>
|
||||||
|
</inline>
|
||||||
|
<sleep milliseconds="1000" />
|
||||||
|
<inline description="open window 3 in front">
|
||||||
|
<window_layout>
|
||||||
|
<window id="3" title="Launchpad"
|
||||||
|
xpos="210" ypos="250" width="400" height="200" />
|
||||||
|
<window id="1" title="Genode Toolchain"
|
||||||
|
xpos="100" ypos="50" width="200" height="200"
|
||||||
|
focused="yes" />
|
||||||
|
<window id="2" title="Arora (2)"
|
||||||
|
xpos="170" ypos="150" width="300" height="200" />
|
||||||
|
</window_layout>
|
||||||
|
</inline>
|
||||||
|
<sleep milliseconds="1000" />
|
||||||
|
<inline description="bring window 1 to front">
|
||||||
|
<window_layout>
|
||||||
|
<window id="1" title="Genode Toolchain"
|
||||||
|
xpos="100" ypos="50" width="200" height="200"
|
||||||
|
focused="yes" />
|
||||||
|
<window id="3" title="Launchpad"
|
||||||
|
xpos="210" ypos="250" width="400" height="200" />
|
||||||
|
<window id="2" title="Arora (2)"
|
||||||
|
xpos="170" ypos="150" width="300" height="200" />
|
||||||
|
</window_layout>
|
||||||
|
</inline>
|
||||||
|
<sleep milliseconds="1000" />
|
||||||
|
<inline description="change title of window 1">
|
||||||
|
<window_layout>
|
||||||
|
<window id="1" title="Genode Toolchain (running)"
|
||||||
|
xpos="100" ypos="50" width="200" height="200"
|
||||||
|
focused="yes" />
|
||||||
|
<window id="3" title="Launchpad"
|
||||||
|
xpos="210" ypos="250" width="400" height="200" />
|
||||||
|
<window id="2" title="Arora (2)"
|
||||||
|
xpos="170" ypos="150" width="300" height="200" />
|
||||||
|
</window_layout>
|
||||||
|
</inline>
|
||||||
|
<sleep milliseconds="1000" />
|
||||||
|
<inline description="change focus to window 3">
|
||||||
|
<window_layout>
|
||||||
|
<window id="1" title="Genode Toolchain (running)"
|
||||||
|
xpos="100" ypos="50" width="200" height="200" />
|
||||||
|
<window id="3" title="Launchpad"
|
||||||
|
xpos="210" ypos="250" width="400" height="200"
|
||||||
|
focused="yes" />
|
||||||
|
<window id="2" title="Arora (2)"
|
||||||
|
xpos="170" ypos="150" width="300" height="200" />
|
||||||
|
</window_layout>
|
||||||
|
</inline>
|
||||||
|
<sleep milliseconds="1000" />
|
||||||
|
<inline description="move window 3">
|
||||||
|
<window_layout>
|
||||||
|
<window id="1" title="Genode Toolchain"
|
||||||
|
xpos="100" ypos="50" width="200" height="200" />
|
||||||
|
<window id="3" title="Launchpad"
|
||||||
|
xpos="310" ypos="300" width="500" height="300"
|
||||||
|
focused="yes" />
|
||||||
|
<window id="2" title="Arora (2)"
|
||||||
|
xpos="170" ypos="150" width="300" height="200" />
|
||||||
|
</window_layout>
|
||||||
|
</inline>
|
||||||
|
<sleep milliseconds="1000" />
|
||||||
|
<empty />
|
||||||
|
<sleep milliseconds="1000" />
|
||||||
|
</rom>
|
||||||
|
</config>
|
||||||
|
</start>
|
||||||
|
<start name="decorator">
|
||||||
|
<resource name="RAM" quantum="4M"/>
|
||||||
|
<route>
|
||||||
|
<service name="ROM">
|
||||||
|
<if-arg key="label" value="pointer" />
|
||||||
|
<child name="report_rom" />
|
||||||
|
</service>
|
||||||
|
<service name="ROM">
|
||||||
|
<if-arg key="label" value="window_layout" />
|
||||||
|
<child name="dynamic_rom" />
|
||||||
|
</service>
|
||||||
|
<any-service> <parent/> <any-child/> </any-service>
|
||||||
|
</route>
|
||||||
|
</start>
|
||||||
|
</config>}
|
||||||
|
|
||||||
|
install_config $config
|
||||||
|
|
||||||
|
#
|
||||||
|
# Boot modules
|
||||||
|
#
|
||||||
|
|
||||||
|
# generic modules
|
||||||
|
set boot_modules {
|
||||||
|
core init timer dynamic_rom report_rom fb_sdl nitpicker decorator
|
||||||
|
}
|
||||||
|
|
||||||
|
build_boot_image $boot_modules
|
||||||
|
|
||||||
|
run_genode_until forever
|
76
repos/gems/src/app/decorator/animator.h
Normal file
76
repos/gems/src/app/decorator/animator.h
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* \brief Utility for implementing animated objects
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2014-06-24
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 _ANIMATOR_H_
|
||||||
|
#define _ANIMATOR_H_
|
||||||
|
|
||||||
|
class Animator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
class Item;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
friend class Item;
|
||||||
|
|
||||||
|
Genode::List<Item> _items;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
inline void animate();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to be implemented by animated objects
|
||||||
|
*/
|
||||||
|
class Animator::Item : public Genode::List<Item>::Element
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
Animator &_animator;
|
||||||
|
bool _animated = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Item(Animator &animator) : _animator(animator) { }
|
||||||
|
|
||||||
|
virtual ~Item() { animated(false); }
|
||||||
|
|
||||||
|
virtual void animate() = 0;
|
||||||
|
|
||||||
|
void animated(bool animated)
|
||||||
|
{
|
||||||
|
if (animated == _animated)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (animated)
|
||||||
|
_animator._items.insert(this);
|
||||||
|
else
|
||||||
|
_animator._items.remove(this);
|
||||||
|
|
||||||
|
_animated = animated;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool animated() const { return _animated; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
inline void Animator::animate()
|
||||||
|
{
|
||||||
|
for (Item *item = _items.first(); item; item = item->next())
|
||||||
|
item->animate();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _ANIMATOR_H_ */
|
101
repos/gems/src/app/decorator/canvas.h
Normal file
101
repos/gems/src/app/decorator/canvas.h
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* \brief Graphics back end for example window decorator
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2014-01-10
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 _CANVAS_H_
|
||||||
|
#define _CANVAS_H_
|
||||||
|
|
||||||
|
#include <decorator/types.h>
|
||||||
|
|
||||||
|
namespace Decorator {
|
||||||
|
typedef Text_painter::Font Font;
|
||||||
|
Font &default_font();
|
||||||
|
template <typename PT> class Canvas;
|
||||||
|
class Clip_guard;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define FONT_START_SYMBOL _binary_droidsansb10_tff_start
|
||||||
|
extern char FONT_START_SYMBOL;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return default font
|
||||||
|
*/
|
||||||
|
Decorator::Font &Decorator::default_font()
|
||||||
|
{
|
||||||
|
static Font font(&FONT_START_SYMBOL);
|
||||||
|
return font;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract interface of graphics back end
|
||||||
|
*/
|
||||||
|
struct Decorator::Canvas_base
|
||||||
|
{
|
||||||
|
virtual Rect clip() const = 0;
|
||||||
|
virtual void clip(Rect) = 0;
|
||||||
|
virtual void draw_box(Rect, Color) = 0;
|
||||||
|
virtual void draw_text(Point, Font const &, Color, char const *) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename PT>
|
||||||
|
class Decorator::Canvas : public Decorator::Canvas_base
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
Genode::Surface<PT> _surface;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Canvas(PT *base, Area size) : _surface(base, size) { }
|
||||||
|
|
||||||
|
Rect clip() const override { return _surface.clip(); }
|
||||||
|
|
||||||
|
void clip(Rect rect) override { _surface.clip(rect); }
|
||||||
|
|
||||||
|
void draw_box(Rect rect, Color color) override
|
||||||
|
{
|
||||||
|
Box_painter::paint(_surface, rect, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_text(Point pos, Font const &font,
|
||||||
|
Color color, char const *string) override
|
||||||
|
{
|
||||||
|
Text_painter::paint(_surface, pos, font, color, string);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Decorator::Clip_guard : Rect
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
Canvas_base &_canvas;
|
||||||
|
Rect const _orig_rect;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Clip_guard(Canvas_base &canvas, Rect clip_rect)
|
||||||
|
:
|
||||||
|
_canvas(canvas), _orig_rect(canvas.clip())
|
||||||
|
{
|
||||||
|
_canvas.clip(Rect::intersect(_orig_rect, clip_rect));
|
||||||
|
}
|
||||||
|
|
||||||
|
~Clip_guard() { _canvas.clip(_orig_rect); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _CANVAS_H_ */
|
||||||
|
|
243
repos/gems/src/app/decorator/main.cc
Normal file
243
repos/gems/src/app/decorator/main.cc
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
/*
|
||||||
|
* \brief Example window decorator that mimics the Motif look
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2013-01-04
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 <nitpicker_session/connection.h>
|
||||||
|
#include <os/pixel_rgb565.h>
|
||||||
|
#include <os/attached_rom_dataspace.h>
|
||||||
|
#include <os/reporter.h>
|
||||||
|
|
||||||
|
/* Nitpicker graphics backend */
|
||||||
|
#include <nitpicker_gfx/text_painter.h>
|
||||||
|
#include <nitpicker_gfx/box_painter.h>
|
||||||
|
|
||||||
|
/* decorator includes */
|
||||||
|
#include <decorator/window_stack.h>
|
||||||
|
#include <decorator/xml_utils.h>
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include "canvas.h"
|
||||||
|
#include "window.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace Decorator {
|
||||||
|
using namespace Genode;
|
||||||
|
struct Main;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct Decorator::Main : Window_factory_base
|
||||||
|
{
|
||||||
|
Signal_receiver &sig_rec;
|
||||||
|
|
||||||
|
Nitpicker::Connection nitpicker;
|
||||||
|
|
||||||
|
Framebuffer::Mode mode = { nitpicker.mode() };
|
||||||
|
|
||||||
|
Attached_dataspace fb_ds = { (nitpicker.buffer(mode, false),
|
||||||
|
nitpicker.framebuffer()->dataspace()) };
|
||||||
|
|
||||||
|
Canvas<Pixel_rgb565> canvas = { fb_ds.local_addr<Pixel_rgb565>(),
|
||||||
|
Area(mode.width(), mode.height()) };
|
||||||
|
|
||||||
|
Window_stack window_stack = { *this };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Install handler for responding to window-layout changes
|
||||||
|
*/
|
||||||
|
void handle_window_layout_update(unsigned);
|
||||||
|
|
||||||
|
Signal_dispatcher<Main> window_layout_dispatcher = {
|
||||||
|
sig_rec, *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_dispatcher<Main> pointer_dispatcher = {
|
||||||
|
sig_rec, *this, &Main::handle_pointer_update };
|
||||||
|
|
||||||
|
Attached_rom_dataspace pointer { "pointer" };
|
||||||
|
|
||||||
|
Window_base::Hover hover;
|
||||||
|
|
||||||
|
Reporter hover_reporter = { "hover" };
|
||||||
|
|
||||||
|
bool window_layout_update_needed = false;
|
||||||
|
|
||||||
|
Animator animator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Install handler for responding to nitpicker sync events
|
||||||
|
*/
|
||||||
|
void handle_nitpicker_sync(unsigned);
|
||||||
|
|
||||||
|
Signal_dispatcher<Main> nitpicker_sync_dispatcher = {
|
||||||
|
sig_rec, *this, &Main::handle_nitpicker_sync };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
Main(Signal_receiver &sig_rec) : sig_rec(sig_rec)
|
||||||
|
{
|
||||||
|
window_layout.sigh(window_layout_dispatcher);
|
||||||
|
pointer.sigh(pointer_dispatcher);
|
||||||
|
|
||||||
|
nitpicker.framebuffer()->sync_sigh(nitpicker_sync_dispatcher);
|
||||||
|
|
||||||
|
hover_reporter.enabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Window_factory_base interface
|
||||||
|
*/
|
||||||
|
Window_base *create(Xml_node window_node) override
|
||||||
|
{
|
||||||
|
return new (env()->heap())
|
||||||
|
Window(attribute(window_node, "id", 0UL), nitpicker, animator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Window_factory_base interface
|
||||||
|
*/
|
||||||
|
void destroy(Window_base *window) override
|
||||||
|
{
|
||||||
|
Genode::destroy(env()->heap(), static_cast<Window *>(window));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void Decorator::Main::handle_window_layout_update(unsigned)
|
||||||
|
{
|
||||||
|
window_layout.update();
|
||||||
|
|
||||||
|
window_layout_update_needed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Decorator::Main::handle_nitpicker_sync(unsigned)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
} 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();
|
||||||
|
|
||||||
|
animator.animate();
|
||||||
|
|
||||||
|
if (!model_updated && !windows_animated)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Dirty_rect dirty = window_stack.draw(canvas);
|
||||||
|
|
||||||
|
window_stack.update_nitpicker_views();
|
||||||
|
|
||||||
|
nitpicker.execute();
|
||||||
|
|
||||||
|
dirty.flush([&] (Rect const &r) {
|
||||||
|
nitpicker.framebuffer()->refresh(r.x1(), r.y1(), r.w(), r.h()); });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Decorator::Main::handle_pointer_update(unsigned)
|
||||||
|
{
|
||||||
|
pointer.update();
|
||||||
|
|
||||||
|
if (!pointer.is_valid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Xml_node const pointer_node(pointer.local_addr<char>());
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
static Genode::Signal_receiver sig_rec;
|
||||||
|
|
||||||
|
static Decorator::Main application(sig_rec);
|
||||||
|
|
||||||
|
/* import initial state */
|
||||||
|
application.handle_pointer_update(0);
|
||||||
|
application.handle_window_layout_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());
|
||||||
|
}
|
||||||
|
}
|
8
repos/gems/src/app/decorator/target.mk
Normal file
8
repos/gems/src/app/decorator/target.mk
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
TARGET = decorator
|
||||||
|
SRC_CC = main.cc
|
||||||
|
LIBS = base config
|
||||||
|
TFF_DIR = $(call select_from_repositories,src/app/scout/data)
|
||||||
|
SRC_BIN = droidsansb10.tff
|
||||||
|
INC_DIR += $(PRG_DIR)
|
||||||
|
|
||||||
|
vpath %.tff $(TFF_DIR)
|
472
repos/gems/src/app/decorator/window.h
Normal file
472
repos/gems/src/app/decorator/window.h
Normal file
@ -0,0 +1,472 @@
|
|||||||
|
/*
|
||||||
|
* \brief Example window decorator that mimics the Motif look
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2014-01-10
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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_H_
|
||||||
|
#define _WINDOW_H_
|
||||||
|
|
||||||
|
#include <util/lazy_value.h>
|
||||||
|
#include <decorator/window.h>
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <animator.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace Decorator { class Window; }
|
||||||
|
|
||||||
|
|
||||||
|
class Decorator::Window : public Window_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
typedef Genode::String<200> Title;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
Title _title;
|
||||||
|
|
||||||
|
bool _focused = false;
|
||||||
|
|
||||||
|
Animator &_animator;
|
||||||
|
|
||||||
|
static unsigned const _corner_size = 16;
|
||||||
|
static unsigned const _border_size = 4;
|
||||||
|
static unsigned const _title_height = 16;
|
||||||
|
|
||||||
|
static Border _border() {
|
||||||
|
return Border(_border_size + _title_height,
|
||||||
|
_border_size, _border_size, _border_size); }
|
||||||
|
|
||||||
|
Color _bright = { 255, 255, 255, 64 };
|
||||||
|
Color _dark = { 0, 0, 0, 127 };
|
||||||
|
|
||||||
|
Color _base_color() const { return Color(45, 49, 65); }
|
||||||
|
|
||||||
|
class Element : public Animator::Item
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum Type { TITLE, LEFT, RIGHT, TOP, BOTTOM,
|
||||||
|
TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT,
|
||||||
|
UNDEFINED };
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
static Color _add(Color c1, Color c2)
|
||||||
|
{
|
||||||
|
return Color(Genode::min(c1.r + c2.r, 255),
|
||||||
|
Genode::min(c1.g + c2.g, 255),
|
||||||
|
Genode::min(c1.b + c2.b, 255));
|
||||||
|
}
|
||||||
|
|
||||||
|
Type _type;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Color value in 8.4 fixpoint format. We use four bits to
|
||||||
|
* represent the fractional part to enable smooth
|
||||||
|
* interpolation between the color values.
|
||||||
|
*/
|
||||||
|
Lazy_value<unsigned> _r, _g, _b;
|
||||||
|
|
||||||
|
bool _focused = false;
|
||||||
|
bool _highlighted = false;
|
||||||
|
|
||||||
|
static Color _dst_color(bool focused, bool highlighted, Color base)
|
||||||
|
{
|
||||||
|
Color result = base;
|
||||||
|
|
||||||
|
if (focused)
|
||||||
|
result = _add(result, Color(70, 70, 70));
|
||||||
|
|
||||||
|
if (highlighted)
|
||||||
|
result = _add(result, Color(65, 60, 55));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned _anim_steps(bool focused, bool highlighted) const
|
||||||
|
{
|
||||||
|
/* quick fade-in when gaining the focus or hover highlight */
|
||||||
|
if ((!_focused && focused) || (!_highlighted && highlighted))
|
||||||
|
return 20;
|
||||||
|
|
||||||
|
/* slow fade-out when leaving focus or hover highlight */
|
||||||
|
return 180;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _apply_state(bool focused, bool highlighted, Color base_color)
|
||||||
|
{
|
||||||
|
Color const dst_color = _dst_color(focused, highlighted, base_color);
|
||||||
|
unsigned const steps = _anim_steps(focused, highlighted);
|
||||||
|
|
||||||
|
_r.dst(dst_color.r << 4, steps);
|
||||||
|
_g.dst(dst_color.g << 4, steps);
|
||||||
|
_b.dst(dst_color.b << 4, steps);
|
||||||
|
|
||||||
|
/* schedule animation */
|
||||||
|
animate();
|
||||||
|
|
||||||
|
_focused = focused;
|
||||||
|
_highlighted = highlighted;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Element(Type type, Animator &animator, Color base_color)
|
||||||
|
:
|
||||||
|
Animator::Item(animator),
|
||||||
|
_type(type)
|
||||||
|
{
|
||||||
|
_apply_state(false, false, base_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
Type type() const { return _type; }
|
||||||
|
|
||||||
|
char const *type_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 "";
|
||||||
|
}
|
||||||
|
|
||||||
|
Color color() const { return Color(_r >> 4, _g >> 4, _b >> 4); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return true if state has changed
|
||||||
|
*/
|
||||||
|
bool apply_state(bool focused, bool highlighted, Color base_color)
|
||||||
|
{
|
||||||
|
if (_focused == focused && _highlighted == highlighted)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return _apply_state(focused, highlighted, base_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animator::Item interface
|
||||||
|
*/
|
||||||
|
void animate() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The element order must correspond to the order of enum values
|
||||||
|
* because the type is used as index into the '_elements' array.
|
||||||
|
*/
|
||||||
|
Element _elements[9] { { Element::TITLE, _animator, _base_color() },
|
||||||
|
{ Element::LEFT, _animator, _base_color() },
|
||||||
|
{ Element::RIGHT, _animator, _base_color() },
|
||||||
|
{ Element::TOP, _animator, _base_color() },
|
||||||
|
{ Element::BOTTOM, _animator, _base_color() },
|
||||||
|
{ Element::TOP_LEFT, _animator, _base_color() },
|
||||||
|
{ Element::TOP_RIGHT, _animator, _base_color() },
|
||||||
|
{ Element::BOTTOM_LEFT, _animator, _base_color() },
|
||||||
|
{ Element::BOTTOM_RIGHT, _animator, _base_color() } };
|
||||||
|
|
||||||
|
Element &element(Element::Type type)
|
||||||
|
{
|
||||||
|
return _elements[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
Element const &element(Element::Type type) const
|
||||||
|
{
|
||||||
|
return _elements[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned num_elements() const { return sizeof(_elements)/sizeof(Element); }
|
||||||
|
|
||||||
|
void _draw_hline(Canvas_base &canvas, Point pos, unsigned w,
|
||||||
|
bool at_left, bool at_right,
|
||||||
|
unsigned border, Color color) const
|
||||||
|
{
|
||||||
|
int const x1 = at_left ? (pos.x()) : (pos.x() + w - border);
|
||||||
|
int const x2 = at_right ? (pos.x() + w - 1) : (pos.x() + border - 1);
|
||||||
|
|
||||||
|
canvas.draw_box(Rect(Point(x1, pos.y()),
|
||||||
|
Point(x2, pos.y())), color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _draw_vline(Canvas_base &canvas, Point pos, unsigned h,
|
||||||
|
bool at_top, bool at_bottom,
|
||||||
|
unsigned border, Color color) const
|
||||||
|
{
|
||||||
|
int const y1 = at_top ? (pos.y()) : (pos.y() + h - border);
|
||||||
|
int const y2 = at_bottom ? (pos.y() + h - 1) : (pos.y() + border - 1);
|
||||||
|
|
||||||
|
canvas.draw_box(Rect(Point(pos.x(), y1),
|
||||||
|
Point(pos.x(), y2)), color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _draw_raised_frame(Canvas_base &canvas, Rect rect) const
|
||||||
|
{
|
||||||
|
_draw_hline(canvas, rect.p1(), rect.w(), true, true, 0, _bright);
|
||||||
|
_draw_vline(canvas, rect.p1(), rect.h(), true, true, 0, _bright);
|
||||||
|
_draw_hline(canvas, Point(rect.p1().x(), rect.p2().y()), rect.w(),
|
||||||
|
true, true, 0, _dark);
|
||||||
|
_draw_vline(canvas, Point(rect.p2().x(), rect.p1().y()), rect.h(),
|
||||||
|
true, true, 0, _dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _draw_raised_box(Canvas_base &canvas, Rect rect, Color color) const
|
||||||
|
{
|
||||||
|
canvas.draw_box(rect, color);
|
||||||
|
_draw_raised_frame(canvas, rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _draw_title_box(Canvas_base &canvas, Rect rect, Color color) const
|
||||||
|
{
|
||||||
|
canvas.draw_box(rect, color);
|
||||||
|
for (unsigned i = 0; i < rect.h(); i++)
|
||||||
|
canvas.draw_box(Rect(rect.p1() + Point(0, i),
|
||||||
|
Area(rect.w(), 1)),
|
||||||
|
Color(255,255,255, 30 + (rect.h() - i)*4));
|
||||||
|
|
||||||
|
_draw_raised_frame(canvas, rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _draw_corner(Canvas_base &canvas, Rect const rect,
|
||||||
|
unsigned const border,
|
||||||
|
bool const left, bool const top,
|
||||||
|
Color color) const
|
||||||
|
{
|
||||||
|
bool const bottom = !top;
|
||||||
|
bool const right = !left;
|
||||||
|
|
||||||
|
int const x1 = rect.p1().x();
|
||||||
|
int const y1 = rect.p1().y();
|
||||||
|
int const x2 = rect.p2().x();
|
||||||
|
int const y2 = rect.p2().y();
|
||||||
|
int const w = rect.w();
|
||||||
|
int const h = rect.h();
|
||||||
|
|
||||||
|
canvas.draw_box(Rect(Point(x1, top ? y1 : y2 - border + 1),
|
||||||
|
Area(w, border)), color);
|
||||||
|
|
||||||
|
canvas.draw_box(Rect(Point(left ? x1 : x2 - border + 1,
|
||||||
|
top ? y1 + border : y1),
|
||||||
|
Area(border, h - border)), color);
|
||||||
|
|
||||||
|
/* top bright line */
|
||||||
|
_draw_hline(canvas, rect.p1(), w,
|
||||||
|
top || left, top || right, border, _bright);
|
||||||
|
|
||||||
|
/* inner horizontal line */
|
||||||
|
int y = top ? y1 + border - 1 : y2 - border + 1;
|
||||||
|
_draw_hline(canvas, Point(x1, y), w, right, left, w - border,
|
||||||
|
top ? _dark : _bright);
|
||||||
|
|
||||||
|
/* bottom line */
|
||||||
|
_draw_hline(canvas, Point(x1, y2), w,
|
||||||
|
bottom || left, bottom || right, border, _dark);
|
||||||
|
|
||||||
|
/* left bright line */
|
||||||
|
_draw_vline(canvas, rect.p1(), h,
|
||||||
|
left || top, left || bottom, border, _bright);
|
||||||
|
|
||||||
|
/* inner vertical line */
|
||||||
|
int x = left ? x1 + border - 1 : x2 - border + 1;
|
||||||
|
_draw_vline(canvas, Point(x, y1), h, bottom, top, h - border + 1,
|
||||||
|
left ? _dark : _bright);
|
||||||
|
|
||||||
|
/* right line */
|
||||||
|
_draw_vline(canvas, Point(x2, y1), h,
|
||||||
|
right || top, right || bottom, border, _dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _apply_state(Window::Element::Type type, bool focused, bool highlighted)
|
||||||
|
{
|
||||||
|
return element(type).apply_state(_focused, highlighted, _base_color());
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Window(unsigned id, Nitpicker::Session_client &nitpicker, Animator &animator)
|
||||||
|
:
|
||||||
|
Window_base(id, nitpicker, _border()),
|
||||||
|
_animator(animator)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
void draw(Canvas_base &canvas, Rect clip) const override;
|
||||||
|
|
||||||
|
bool update(Xml_node window_node) override;
|
||||||
|
|
||||||
|
Hover hover(Point) const override;
|
||||||
|
|
||||||
|
bool animated() const override
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < num_elements(); i++)
|
||||||
|
if (_elements[i].animated())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void Decorator::Window::draw(Decorator::Canvas_base &canvas,
|
||||||
|
Decorator::Rect clip) const
|
||||||
|
{
|
||||||
|
Clip_guard clip_guard(canvas, clip);
|
||||||
|
|
||||||
|
Rect rect = outer_geometry();
|
||||||
|
Area corner(_corner_size, _corner_size);
|
||||||
|
|
||||||
|
Point p1 = rect.p1();
|
||||||
|
Point p2 = rect.p2();
|
||||||
|
|
||||||
|
bool const draw_content = false;
|
||||||
|
|
||||||
|
if (draw_content)
|
||||||
|
canvas.draw_box(geometry(), Color(10, 20, 40));
|
||||||
|
|
||||||
|
_draw_corner(canvas, Rect(p1, corner), _border_size, true, true,
|
||||||
|
element(Element::TOP_LEFT).color());
|
||||||
|
|
||||||
|
_draw_corner(canvas, Rect(Point(p1.x(), p2.y() - _corner_size + 1), corner),
|
||||||
|
_border_size, true, false,
|
||||||
|
element(Element::BOTTOM_LEFT).color());
|
||||||
|
|
||||||
|
_draw_corner(canvas, Rect(Point(p2.x() - _corner_size + 1, p1.y()), corner),
|
||||||
|
_border_size, false, true,
|
||||||
|
element(Element::TOP_RIGHT).color());
|
||||||
|
|
||||||
|
_draw_corner(canvas, Rect(Point(p2.x() - _corner_size + 1, p2.y() - _corner_size + 1), corner),
|
||||||
|
_border_size, false, false,
|
||||||
|
element(Element::BOTTOM_RIGHT).color());
|
||||||
|
|
||||||
|
_draw_raised_box(canvas, Rect(Point(p1.x() + _corner_size, p1.y()),
|
||||||
|
Area(rect.w() - 2*_corner_size, _border_size)),
|
||||||
|
element(Element::TOP).color());
|
||||||
|
|
||||||
|
_draw_raised_box(canvas, Rect(Point(p1.x() + _corner_size, p2.y() - _border_size + 1),
|
||||||
|
Area(rect.w() - 2*_corner_size, _border_size)),
|
||||||
|
element(Element::BOTTOM).color());
|
||||||
|
|
||||||
|
_draw_raised_box(canvas, Rect(Point(p1.x(), p1.y() + _corner_size),
|
||||||
|
Area(_border_size, rect.h() - 2*_corner_size)),
|
||||||
|
element(Element::LEFT).color());
|
||||||
|
|
||||||
|
_draw_raised_box(canvas, Rect(Point(p2.x() - _border_size + 1, p1.y() + _corner_size),
|
||||||
|
Area(_border_size, rect.h() - 2*_corner_size)),
|
||||||
|
element(Element::RIGHT).color());
|
||||||
|
|
||||||
|
Rect title_rect(Point(p1.x() + _border_size, p1.y() + _border_size),
|
||||||
|
Area(rect.w() - 2*_border_size, _title_height));
|
||||||
|
|
||||||
|
_draw_title_box(canvas, title_rect, element(Element::TITLE).color());
|
||||||
|
|
||||||
|
char const * const text = _title.string();;
|
||||||
|
|
||||||
|
Area const label_area(default_font().str_w(text),
|
||||||
|
default_font().str_h(text));
|
||||||
|
|
||||||
|
Point text_pos = title_rect.center(label_area) - Point(0, 1);
|
||||||
|
|
||||||
|
{
|
||||||
|
Clip_guard clip_guard(canvas, title_rect);
|
||||||
|
|
||||||
|
canvas.draw_text(text_pos + Point(1, 1), default_font(),
|
||||||
|
Color(0, 0, 0, 128), text);
|
||||||
|
|
||||||
|
Color title_color = element(Element::TITLE).color();
|
||||||
|
|
||||||
|
canvas.draw_text(text_pos, default_font(),
|
||||||
|
Color(255, 255, 255, (2*255 + title_color.r) / 3), text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Decorator::Window::update(Genode::Xml_node window_node)
|
||||||
|
{
|
||||||
|
bool updated = Window_base::update(window_node);
|
||||||
|
|
||||||
|
_focused = window_node.has_attribute("focused")
|
||||||
|
&& window_node.attribute("focused").has_value("yes");
|
||||||
|
|
||||||
|
try {
|
||||||
|
Xml_node highlight = window_node.sub_node("highlight");
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < num_elements(); i++)
|
||||||
|
updated |= _apply_state(_elements[i].type(), _focused,
|
||||||
|
highlight.has_sub_node(_elements[i].type_name()));
|
||||||
|
} catch (...) {
|
||||||
|
|
||||||
|
/* window node has no "highlight" sub node, reset highlighting */
|
||||||
|
for (unsigned i = 0; i < num_elements(); i++)
|
||||||
|
updated |= _apply_state(_elements[i].type(), _focused, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Title title = Decorator::string_attribute(window_node, "title", Title("<untitled>"));
|
||||||
|
updated |= !(title == _title);
|
||||||
|
_title = title;
|
||||||
|
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Decorator::Window_base::Hover Decorator::Window::hover(Point abs_pos) const
|
||||||
|
{
|
||||||
|
Hover hover;
|
||||||
|
|
||||||
|
if (!outer_geometry().contains(abs_pos))
|
||||||
|
return hover;
|
||||||
|
|
||||||
|
hover.window_id = id();
|
||||||
|
|
||||||
|
unsigned const x = abs_pos.x() - outer_geometry().x1(),
|
||||||
|
y = abs_pos.y() - outer_geometry().y1();
|
||||||
|
|
||||||
|
Area const area = outer_geometry().area();
|
||||||
|
|
||||||
|
bool const at_border = x < _border_size
|
||||||
|
|| x >= area.w() - _border_size
|
||||||
|
|| y < _border_size
|
||||||
|
|| y >= area.h() - _border_size;
|
||||||
|
|
||||||
|
if (at_border) {
|
||||||
|
|
||||||
|
hover.left_sizer = (x < _corner_size);
|
||||||
|
hover.top_sizer = (y < _corner_size);
|
||||||
|
hover.right_sizer = (x >= area.w() - _corner_size);
|
||||||
|
hover.bottom_sizer = (y >= area.h() - _corner_size);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
hover.title = (y < _border_size + _title_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hover;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Decorator::Window::Element::animate()
|
||||||
|
{
|
||||||
|
_r.animate();
|
||||||
|
_g.animate();
|
||||||
|
_b.animate();
|
||||||
|
|
||||||
|
/* keep animation running until the destination values are reached */
|
||||||
|
Animator::Item::animated(_r != _r.dst() || _g != _g.dst() || _b != _b.dst());
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _WINDOW_H_ */
|
38
repos/os/include/decorator/types.h
Normal file
38
repos/os/include/decorator/types.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* \brief Basic types used by decorator
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2014-01-09
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 _INCLUDE__DECORATOR__TYPES_H_
|
||||||
|
#define _INCLUDE__DECORATOR__TYPES_H_
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <nitpicker_session/nitpicker_session.h>
|
||||||
|
#include <util/xml_node.h>
|
||||||
|
#include <util/color.h>
|
||||||
|
#include <util/geometry.h>
|
||||||
|
#include <util/color.h>
|
||||||
|
#include <util/dirty_rect.h>
|
||||||
|
#include <os/surface.h>
|
||||||
|
|
||||||
|
namespace Decorator {
|
||||||
|
typedef Genode::Surface_base::Point Point;
|
||||||
|
typedef Genode::Surface_base::Area Area;
|
||||||
|
typedef Genode::Surface_base::Rect Rect;
|
||||||
|
|
||||||
|
typedef Genode::Dirty_rect<Rect, 3> Dirty_rect;
|
||||||
|
|
||||||
|
using Genode::size_t;
|
||||||
|
using Genode::Color;
|
||||||
|
using Genode::Xml_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _INCLUDE__DECORATOR__TYPES_H_ */
|
298
repos/os/include/decorator/window.h
Normal file
298
repos/os/include/decorator/window.h
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
/*
|
||||||
|
* \brief Window representation for decorator
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2014-01-09
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 _INCLUDE__DECORATOR__WINDOW_H_
|
||||||
|
#define _INCLUDE__DECORATOR__WINDOW_H_
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <util/list.h>
|
||||||
|
#include <util/string.h>
|
||||||
|
#include <util/xml_generator.h>
|
||||||
|
#include <nitpicker_session/client.h>
|
||||||
|
#include <base/snprintf.h>
|
||||||
|
|
||||||
|
/* decorator includes */
|
||||||
|
#include <decorator/types.h>
|
||||||
|
#include <decorator/xml_utils.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace Decorator {
|
||||||
|
class Canvas_base;
|
||||||
|
class Window_base;
|
||||||
|
typedef Genode::List<Window_base> Window_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Decorator::Window_base : public Window_list::Element
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
struct Border
|
||||||
|
{
|
||||||
|
unsigned top, left, right, bottom;
|
||||||
|
|
||||||
|
Border(unsigned top, unsigned left, unsigned right, unsigned bottom)
|
||||||
|
: top(top), left(left), right(right), bottom(bottom) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Hover
|
||||||
|
{
|
||||||
|
bool left_sizer = false,
|
||||||
|
right_sizer = false,
|
||||||
|
top_sizer = false,
|
||||||
|
bottom_sizer = false,
|
||||||
|
title = false;
|
||||||
|
|
||||||
|
unsigned window_id = 0;
|
||||||
|
|
||||||
|
bool operator != (Hover const &other) const
|
||||||
|
{
|
||||||
|
return other.left_sizer != left_sizer
|
||||||
|
|| other.right_sizer != right_sizer
|
||||||
|
|| other.top_sizer != top_sizer
|
||||||
|
|| other.bottom_sizer != bottom_sizer
|
||||||
|
|| other.title != title
|
||||||
|
|| other.window_id != window_id;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
Nitpicker::Session_client &_nitpicker;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Geometry of content
|
||||||
|
*/
|
||||||
|
Rect _geometry;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unique window ID
|
||||||
|
*/
|
||||||
|
unsigned const _id;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Flag indicating that the current window position has been propagated
|
||||||
|
* to the window's corresponding nitpicker views.
|
||||||
|
*/
|
||||||
|
bool _nitpicker_views_up_to_date = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Flag indicating that the stacking position of the window within the
|
||||||
|
* window stack has changed. The new stacking position must be
|
||||||
|
* propagated to nitpicker.
|
||||||
|
*/
|
||||||
|
bool _nitpicker_stacking_up_to_date = false;
|
||||||
|
|
||||||
|
unsigned _topped_cnt = 0;
|
||||||
|
|
||||||
|
bool _global_to_front = false;
|
||||||
|
|
||||||
|
Nitpicker::Session::View_handle _neighbor;
|
||||||
|
|
||||||
|
Border const _border;
|
||||||
|
|
||||||
|
struct Nitpicker_view
|
||||||
|
{
|
||||||
|
Nitpicker::Session_client &_nitpicker;
|
||||||
|
Nitpicker::Session::View_handle _handle { _nitpicker.create_view() };
|
||||||
|
|
||||||
|
typedef Nitpicker::Session::Command Command;
|
||||||
|
|
||||||
|
Nitpicker_view(Nitpicker::Session_client &nitpicker, unsigned id = 0)
|
||||||
|
:
|
||||||
|
_nitpicker(nitpicker)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We supply the window ID as label for the anchor view.
|
||||||
|
*/
|
||||||
|
if (id) {
|
||||||
|
char buf[128];
|
||||||
|
Genode::snprintf(buf, sizeof(buf), "%d", id);
|
||||||
|
|
||||||
|
_nitpicker.enqueue<Command::Title>(_handle, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~Nitpicker_view()
|
||||||
|
{
|
||||||
|
_nitpicker.destroy_view(_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
Nitpicker::Session::View_handle handle() const { return _handle; }
|
||||||
|
|
||||||
|
void stack(Nitpicker::Session::View_handle neighbor)
|
||||||
|
{
|
||||||
|
_nitpicker.enqueue<Command::To_front>(_handle, neighbor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void place(Rect rect)
|
||||||
|
{
|
||||||
|
_nitpicker.enqueue<Command::Geometry>(_handle, rect);
|
||||||
|
Point offset = Point(0, 0) - rect.p1();
|
||||||
|
_nitpicker.enqueue<Command::Offset>(_handle, offset);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Nitpicker_view _bottom_view, _right_view, _left_view, _top_view;
|
||||||
|
Nitpicker_view _content_view;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Window_base(unsigned id, Nitpicker::Session_client &nitpicker,
|
||||||
|
Border border)
|
||||||
|
:
|
||||||
|
_nitpicker(nitpicker), _id(id), _border(border),
|
||||||
|
_bottom_view(nitpicker),
|
||||||
|
_right_view(nitpicker),
|
||||||
|
_left_view(nitpicker),
|
||||||
|
_top_view(nitpicker),
|
||||||
|
_content_view(nitpicker, _id)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
void stack(Nitpicker::Session::View_handle neighbor)
|
||||||
|
{
|
||||||
|
_neighbor = neighbor;
|
||||||
|
_nitpicker_stacking_up_to_date = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Nitpicker::Session::View_handle frontmost_view() const
|
||||||
|
{
|
||||||
|
return _bottom_view.handle();
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect outer_geometry() const
|
||||||
|
{
|
||||||
|
return Rect(_geometry.p1() - Point(_border.left, _border.top),
|
||||||
|
_geometry.p2() + Point(_border.right, _border.bottom));
|
||||||
|
}
|
||||||
|
|
||||||
|
void border_rects(Rect *top, Rect *left, Rect *right, Rect *bottom) const
|
||||||
|
{
|
||||||
|
outer_geometry().cut(_geometry, top, left, right, bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long id() const { return _id; }
|
||||||
|
Rect geometry() const { return _geometry; }
|
||||||
|
|
||||||
|
bool is_in_front_of(Window_base const &neighbor) const
|
||||||
|
{
|
||||||
|
return _neighbor == neighbor.frontmost_view();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw window elements
|
||||||
|
*
|
||||||
|
* \param canvas graphics back end
|
||||||
|
* \param clip clipping area to apply
|
||||||
|
*/
|
||||||
|
virtual void draw(Canvas_base &canvas, Rect clip) const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update internal window representation from XML model
|
||||||
|
*
|
||||||
|
* \return true if window changed
|
||||||
|
*
|
||||||
|
* We do not immediately update the views as part of the update
|
||||||
|
* function because at the time when updating the model, the
|
||||||
|
* decorations haven't been redrawn already. If we updated the
|
||||||
|
* nitpicker views at this point, we would reveal not-yet-drawn pixels.
|
||||||
|
*/
|
||||||
|
virtual bool update(Xml_node window_node)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Detect the need to bring the window to the top of the global
|
||||||
|
* view stack.
|
||||||
|
*/
|
||||||
|
unsigned const topped_cnt = attribute(window_node, "topped", 0UL);
|
||||||
|
if (topped_cnt != _topped_cnt) {
|
||||||
|
|
||||||
|
_global_to_front = true;
|
||||||
|
_topped_cnt = topped_cnt;
|
||||||
|
_nitpicker_stacking_up_to_date = false;
|
||||||
|
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Detect geometry changes
|
||||||
|
*/
|
||||||
|
Rect new_geometry = rect_attribute(window_node);
|
||||||
|
if (new_geometry.p1() != _geometry.p1()
|
||||||
|
|| new_geometry.p2() != _geometry.p2()) {
|
||||||
|
|
||||||
|
_geometry = new_geometry;
|
||||||
|
|
||||||
|
_nitpicker_views_up_to_date = false;
|
||||||
|
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void update_nitpicker_views()
|
||||||
|
{
|
||||||
|
if (!_nitpicker_views_up_to_date) {
|
||||||
|
|
||||||
|
/* update view positions */
|
||||||
|
Rect top, left, right, bottom;
|
||||||
|
border_rects(&top, &left, &right, &bottom);
|
||||||
|
|
||||||
|
_content_view.place(_geometry);
|
||||||
|
_top_view .place(top);
|
||||||
|
_left_view .place(left);
|
||||||
|
_right_view .place(right);
|
||||||
|
_bottom_view .place(bottom);
|
||||||
|
|
||||||
|
_nitpicker_views_up_to_date = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_nitpicker_stacking_up_to_date) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bring the view to the global top of the view stack if the
|
||||||
|
* 'topped' counter changed. Otherwise, we refer to a
|
||||||
|
* session-local neighbor for the restacking operation.
|
||||||
|
*/
|
||||||
|
Nitpicker::Session::View_handle neighbor = _neighbor;
|
||||||
|
if (_global_to_front) {
|
||||||
|
neighbor = Nitpicker::Session::View_handle();
|
||||||
|
_global_to_front = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_content_view.stack(neighbor);
|
||||||
|
_top_view.stack(_content_view.handle());
|
||||||
|
_left_view.stack(_top_view.handle());
|
||||||
|
_right_view.stack(_left_view.handle());
|
||||||
|
_bottom_view.stack(_right_view.handle());
|
||||||
|
|
||||||
|
_nitpicker_stacking_up_to_date = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Report information about element at specified position
|
||||||
|
*
|
||||||
|
* \param position screen position
|
||||||
|
*/
|
||||||
|
virtual Hover hover(Point position) const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if window needs to be redrawn event if the window layout
|
||||||
|
* model has not changed
|
||||||
|
*/
|
||||||
|
virtual bool animated() const { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _INCLUDE__DECORATOR__WINDOW_H_ */
|
31
repos/os/include/decorator/window_factory.h
Normal file
31
repos/os/include/decorator/window_factory.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* \brief Interface for creating and destroying windows
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2014-01-09
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 _INCLUDE__DECORATOR__WINDOW_FACTORY_H_
|
||||||
|
#define _INCLUDE__DECORATOR__WINDOW_FACTORY_H_
|
||||||
|
|
||||||
|
#include <decorator/types.h>
|
||||||
|
|
||||||
|
namespace Decorator {
|
||||||
|
struct Window_base;
|
||||||
|
struct Window_factory_base;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct Decorator::Window_factory_base
|
||||||
|
{
|
||||||
|
virtual Window_base *create (Xml_node) = 0;
|
||||||
|
virtual void destroy (Window_base *) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _INCLUDE__DECORATOR__WINDOW_FACTORY_H_ */
|
314
repos/os/include/decorator/window_stack.h
Normal file
314
repos/os/include/decorator/window_stack.h
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
/*
|
||||||
|
* \brief Window-stack handling for decorator
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2014-01-09
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 _INCLUDE__DECORATOR__WINDOW_STACK_H_
|
||||||
|
#define _INCLUDE__DECORATOR__WINDOW_STACK_H_
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <nitpicker_session/nitpicker_session.h>
|
||||||
|
#include <base/printf.h>
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include <decorator/types.h>
|
||||||
|
#include <decorator/window.h>
|
||||||
|
#include <decorator/window_factory.h>
|
||||||
|
#include <decorator/xml_utils.h>
|
||||||
|
|
||||||
|
namespace Decorator { class Window_stack; }
|
||||||
|
|
||||||
|
|
||||||
|
class Decorator::Window_stack
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
Window_list _windows;
|
||||||
|
Window_factory_base &_window_factory;
|
||||||
|
Dirty_rect mutable _dirty_rect;
|
||||||
|
|
||||||
|
inline void _draw_rec(Canvas_base &canvas, Window_base const *win,
|
||||||
|
Rect rect) const;
|
||||||
|
|
||||||
|
Window_base *_lookup_by_id(unsigned const id)
|
||||||
|
{
|
||||||
|
for (Window_base *win = _windows.first(); win; win = win->next())
|
||||||
|
if (win->id() == id)
|
||||||
|
return win;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
Xml_node _xml_node_by_window_id(Genode::Xml_node node, unsigned id)
|
||||||
|
{
|
||||||
|
for (node = node.sub_node("window"); ; node = node.next()) {
|
||||||
|
|
||||||
|
if (node.has_type("window") && attribute(node, "id", 0UL) == id)
|
||||||
|
return node;
|
||||||
|
|
||||||
|
if (node.is_last()) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Xml_node::Nonexistent_sub_node();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _destroy(Window_base &window)
|
||||||
|
{
|
||||||
|
_windows.remove(&window);
|
||||||
|
_window_factory.destroy(&window);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate window list in reverse order
|
||||||
|
*
|
||||||
|
* After calling this function, the '_windows' list is empty.
|
||||||
|
*/
|
||||||
|
Window_list _reversed_window_list()
|
||||||
|
{
|
||||||
|
Window_list reversed;
|
||||||
|
while (Window_base *w = _windows.first()) {
|
||||||
|
_windows.remove(w);
|
||||||
|
reversed.insert(w);
|
||||||
|
}
|
||||||
|
return reversed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Window_stack(Window_factory_base &window_factory)
|
||||||
|
:
|
||||||
|
_window_factory(window_factory)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
Dirty_rect draw(Canvas_base &canvas) const
|
||||||
|
{
|
||||||
|
Dirty_rect result = _dirty_rect;
|
||||||
|
|
||||||
|
_dirty_rect.flush([&] (Rect const &rect) {
|
||||||
|
_draw_rec(canvas, _windows.first(), rect); });
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void update_model(Xml_node root_node);
|
||||||
|
|
||||||
|
bool schedule_animated_windows()
|
||||||
|
{
|
||||||
|
bool redraw_needed = false;
|
||||||
|
|
||||||
|
for (Window_base *win = _windows.first(); win; win = win->next()) {
|
||||||
|
if (win->animated()) {
|
||||||
|
_dirty_rect.mark_as_dirty(win->outer_geometry());
|
||||||
|
redraw_needed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return redraw_needed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_nitpicker_views()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Update nitpicker views in reverse order (back-most first). The
|
||||||
|
* reverse order is important because the stacking position of a
|
||||||
|
* view is propagated by referring to the neighbor the view is in
|
||||||
|
* front of. By starting with the back-most view, we make sure that
|
||||||
|
* each view is always at its final stacking position when
|
||||||
|
* specified as neighbor of another view.
|
||||||
|
*/
|
||||||
|
Window_list reversed = _reversed_window_list();
|
||||||
|
|
||||||
|
while (Window_base *win = reversed.first()) {
|
||||||
|
win->update_nitpicker_views();
|
||||||
|
reversed.remove(win);
|
||||||
|
_windows.insert(win);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush()
|
||||||
|
{
|
||||||
|
while (Window_base *window = _windows.first())
|
||||||
|
_destroy(*window);
|
||||||
|
}
|
||||||
|
|
||||||
|
Window_base::Hover hover(Point pos) const
|
||||||
|
{
|
||||||
|
for (Window_base const *win = _windows.first(); win; win = win->next())
|
||||||
|
if (win->outer_geometry().contains(pos))
|
||||||
|
return win->hover(pos);
|
||||||
|
|
||||||
|
return Window_base::Hover();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void Decorator::Window_stack::_draw_rec(Decorator::Canvas_base &canvas,
|
||||||
|
Decorator::Window_base const *win,
|
||||||
|
Decorator::Rect rect) const
|
||||||
|
{
|
||||||
|
Rect clipped;
|
||||||
|
|
||||||
|
/* find next window that intersects with the rectangle */
|
||||||
|
for ( ; win && !(clipped = Rect::intersect(win->outer_geometry(), rect)).valid(); )
|
||||||
|
win = win->next();;
|
||||||
|
|
||||||
|
/* check if we hit the bottom of the window stack */
|
||||||
|
if (!win) return;
|
||||||
|
|
||||||
|
/* draw areas around the current window */
|
||||||
|
if (Window_base const * const next = win->next()) {
|
||||||
|
Rect top, left, right, bottom;
|
||||||
|
rect.cut(clipped, &top, &left, &right, &bottom);
|
||||||
|
|
||||||
|
if (top.valid()) _draw_rec(canvas, next, top);
|
||||||
|
if (left.valid()) _draw_rec(canvas, next, left);
|
||||||
|
if (right.valid()) _draw_rec(canvas, next, right);
|
||||||
|
if (bottom.valid()) _draw_rec(canvas, next, bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* draw current window */
|
||||||
|
win->draw(canvas, clipped);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Decorator::Window_stack::update_model(Genode::Xml_node root_node)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Step 1: Remove windows that are no longer present.
|
||||||
|
*/
|
||||||
|
for (Window_base *window = _windows.first(), *next = 0; window; window = next) {
|
||||||
|
next = window->next();
|
||||||
|
try {
|
||||||
|
_xml_node_by_window_id(root_node, window->id()); }
|
||||||
|
|
||||||
|
catch (Xml_node::Nonexistent_sub_node) {
|
||||||
|
_destroy(*window); };
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Step 2: Update window properties of already present windows.
|
||||||
|
*/
|
||||||
|
for (Window_base *window = _windows.first(); window; window = window->next()) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* After step 1, a Xml_node::Nonexistent_sub_node exception can no
|
||||||
|
* longer occur. All windows remaining in the window stack are present
|
||||||
|
* in the XML model.
|
||||||
|
*/
|
||||||
|
try {
|
||||||
|
Rect const orig_geometry = window->outer_geometry();
|
||||||
|
if (window->update(_xml_node_by_window_id(root_node, window->id()))) {
|
||||||
|
_dirty_rect.mark_as_dirty(orig_geometry);
|
||||||
|
_dirty_rect.mark_as_dirty(window->outer_geometry());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Xml_node::Nonexistent_sub_node) {
|
||||||
|
PERR("could not look up window %ld in XML model", window->id()); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Step 3: Add new appearing windows to the window stack.
|
||||||
|
*/
|
||||||
|
for_each_sub_node(root_node, "window", [&] (Xml_node window_node) {
|
||||||
|
|
||||||
|
unsigned long const id = attribute(window_node, "id", 0UL);
|
||||||
|
|
||||||
|
if (!_lookup_by_id(id)) {
|
||||||
|
|
||||||
|
Window_base *new_window = _window_factory.create(window_node);
|
||||||
|
|
||||||
|
if (new_window) {
|
||||||
|
|
||||||
|
new_window->update(window_node);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Insert new window in front of all other windows.
|
||||||
|
*
|
||||||
|
* Immediately propagate the new stacking position of the new
|
||||||
|
* window to nitpicker ('update_nitpicker_views'). Otherwise,
|
||||||
|
*/
|
||||||
|
new_window->stack(_windows.first()
|
||||||
|
? _windows.first()->frontmost_view()
|
||||||
|
: Nitpicker::Session::View_handle());
|
||||||
|
|
||||||
|
_windows.insert(new_window);
|
||||||
|
|
||||||
|
_dirty_rect.mark_as_dirty(new_window->outer_geometry());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Step 4: Adjust window order.
|
||||||
|
*/
|
||||||
|
Window_base *previous_window = 0;
|
||||||
|
Window_base *window = _windows.first();
|
||||||
|
|
||||||
|
for_each_sub_node(root_node, "window", [&] (Xml_node window_node) {
|
||||||
|
|
||||||
|
if (!window) {
|
||||||
|
PERR("unexpected end of window list during re-ordering");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long const id = attribute(window_node, "id", 0UL);
|
||||||
|
|
||||||
|
if (window->id() != id) {
|
||||||
|
window = _lookup_by_id(id);
|
||||||
|
if (!window) {
|
||||||
|
PERR("window lookup unexpectedly failed during re-ordering");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_windows.remove(window);
|
||||||
|
_windows.insert(window, previous_window);
|
||||||
|
|
||||||
|
_dirty_rect.mark_as_dirty(window->outer_geometry());
|
||||||
|
}
|
||||||
|
|
||||||
|
previous_window = window;
|
||||||
|
window = window->next();
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Propagate changed stacking order to nitpicker
|
||||||
|
*
|
||||||
|
* First, we reverse the window list. The 'reversed' list starts with
|
||||||
|
* the back-most window. We then go throuh each window back to front
|
||||||
|
* and check if its neighbor is consistent with its position in the
|
||||||
|
* window list.
|
||||||
|
*/
|
||||||
|
Window_list reversed = _reversed_window_list();
|
||||||
|
|
||||||
|
if (Window_base * const back_most = reversed.first()) {
|
||||||
|
|
||||||
|
/* keep back-most window as is */
|
||||||
|
reversed.remove(back_most);
|
||||||
|
_windows.insert(back_most);
|
||||||
|
|
||||||
|
/* check consistency between window list order and view stacking */
|
||||||
|
while (Window_base *w = reversed.first()) {
|
||||||
|
|
||||||
|
Window_base * const neighbor = _windows.first();
|
||||||
|
|
||||||
|
reversed.remove(w);
|
||||||
|
_windows.insert(w);
|
||||||
|
|
||||||
|
/* propagate change stacking order to nitpicker */
|
||||||
|
if (w->is_in_front_of(*neighbor))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
w->stack(neighbor->frontmost_view());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _INCLUDE__DECORATOR__WINDOW_STACK_H_ */
|
134
repos/os/include/decorator/xml_utils.h
Normal file
134
repos/os/include/decorator/xml_utils.h
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* \brief Utilities for XML parsing
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2014-01-09
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 _INCLUDE__DECORATOR__XML_UTILS_H_
|
||||||
|
#define _INCLUDE__DECORATOR__XML_UTILS_H_
|
||||||
|
|
||||||
|
#include <decorator/types.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace Decorator {
|
||||||
|
template <typename T>
|
||||||
|
static T attribute(Xml_node const &, char const *, T);
|
||||||
|
|
||||||
|
template <size_t CAPACITY>
|
||||||
|
Genode::String<CAPACITY> string_attribute(Xml_node const &, char const *,
|
||||||
|
Genode::String<CAPACITY> const &);
|
||||||
|
|
||||||
|
static Point point_attribute(Xml_node const &);
|
||||||
|
|
||||||
|
static Area area_attribute(Xml_node const &);
|
||||||
|
|
||||||
|
static Rect rect_attribute(Xml_node const &);
|
||||||
|
|
||||||
|
template <typename FUNC>
|
||||||
|
static void for_each_sub_node(Xml_node, char const *, FUNC const &);
|
||||||
|
|
||||||
|
static Color color(Xml_node const &);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read attribute value from XML node
|
||||||
|
*
|
||||||
|
* \param node XML node
|
||||||
|
* \param name attribute name
|
||||||
|
* \param default_value value returned if no such attribute exists
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
static T
|
||||||
|
Decorator::attribute(Xml_node const &node, char const *name, T default_value)
|
||||||
|
{
|
||||||
|
T result = default_value;
|
||||||
|
if (node.has_attribute(name))
|
||||||
|
node.attribute(name).value(&result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read string from XML node
|
||||||
|
*/
|
||||||
|
template <Genode::size_t CAPACITY>
|
||||||
|
Genode::String<CAPACITY>
|
||||||
|
Decorator::string_attribute(Xml_node const &node, char const *attr,
|
||||||
|
Genode::String<CAPACITY> const &default_value)
|
||||||
|
{
|
||||||
|
if (!node.has_attribute(attr))
|
||||||
|
return default_value;
|
||||||
|
|
||||||
|
char buf[CAPACITY];
|
||||||
|
node.attribute(attr).value(buf, sizeof(buf));
|
||||||
|
return Genode::String<CAPACITY>(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read point position from XML node
|
||||||
|
*/
|
||||||
|
static inline Decorator::Point Decorator::point_attribute(Genode::Xml_node const &point)
|
||||||
|
{
|
||||||
|
return Point(attribute(point, "xpos", 0L),
|
||||||
|
attribute(point, "ypos", 0L)); }
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read area size from XML node
|
||||||
|
*/
|
||||||
|
static inline Decorator::Area Decorator::area_attribute(Genode::Xml_node const &area)
|
||||||
|
{
|
||||||
|
return Area(attribute(area, "width", 0UL),
|
||||||
|
attribute(area, "height", 0UL));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read rectangle coordinates from XML node
|
||||||
|
*/
|
||||||
|
static inline Decorator::Rect Decorator::rect_attribute(Genode::Xml_node const &rect)
|
||||||
|
{
|
||||||
|
return Rect(point_attribute(rect), area_attribute(rect));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply functor 'func' to all XML sub nodes of given type
|
||||||
|
*/
|
||||||
|
template <typename FUNC>
|
||||||
|
static void
|
||||||
|
Decorator::for_each_sub_node(Genode::Xml_node node, char const *type,
|
||||||
|
FUNC const &func)
|
||||||
|
{
|
||||||
|
if (!node.has_sub_node(type))
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (node = node.sub_node(type); ; node = node.next()) {
|
||||||
|
|
||||||
|
if (node.has_type(type))
|
||||||
|
func(node);
|
||||||
|
|
||||||
|
if (node.is_last()) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read color attribute from XML node
|
||||||
|
*/
|
||||||
|
static inline Genode::Color Decorator::color(Genode::Xml_node const &color)
|
||||||
|
{
|
||||||
|
return attribute(color, "color", Color(0, 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _INCLUDE__DECORATOR__XML_UTILS_H_ */
|
Loading…
Reference in New Issue
Block a user