nitpicker: re-organize implementation

This patch revises the implementation of nitpicker in the following
respects:

- Split the implementation into smaller files,
- Consistently use the 'Nitpicker' namespace,
- Avoid the use of format strings,
- Retire old (and hackish) debug mode,
- Removal of unused timer connection,
- Merging 'Session' into 'Session_component',
- Merging 'Mode' into 'User_state',
- Adding the notions of 'View_owner' and 'Focus' as interfaces,
- Untangle 'User_state' and 'View_stack'
This commit is contained in:
Norman Feske 2017-11-16 17:07:52 +01:00 committed by Christian Helmuth
parent 40c9226bb9
commit 82e2900aa7
31 changed files with 2739 additions and 2468 deletions

View File

@ -16,56 +16,45 @@
#include <nitpicker_gfx/box_painter.h>
#include "view.h"
#include "session_component.h"
#include "clip_guard.h"
struct Background : private Texture_base, Session, View
namespace Nitpicker { struct Background; }
struct Nitpicker::Background : private Texture_base, View_component
{
Color color;
static Color default_color() { return Color(25, 37, 50); }
Color color = default_color();
/*
* The background uses no texture. Therefore
* we can pass a null pointer as texture argument
* to the Session constructor.
*/
Background(Area size)
Background(View_owner &owner, Area size)
:
Texture_base(Area(0, 0)), Session(Genode::Session_label()),
View(*this, View::NOT_TRANSPARENT, View::BACKGROUND, 0),
color(25, 37, 50)
Texture_base(Area(0, 0)),
View_component(owner, View_component::NOT_TRANSPARENT,
View_component::BACKGROUND, 0)
{
View::geometry(Rect(Point(0, 0), size));
View_component::geometry(Rect(Point(0, 0), size));
}
/***********************
** Session interface **
***********************/
/******************************
** View_component interface **
******************************/
void submit_input_event(Input::Event) override { }
void submit_sync() override { }
int frame_size(Focus const &) const override { return 0; }
void frame(Canvas_base &canvas, Focus const &) const override { }
/********************
** View interface **
********************/
int frame_size(Mode const &mode) const override { return 0; }
void frame(Canvas_base &canvas, Mode const &mode) const override { }
void draw(Canvas_base &canvas, Mode const &mode) const override
void draw(Canvas_base &canvas, Focus const &) const override
{
Rect const view_rect = abs_geometry();
Clip_guard clip_guard(canvas, view_rect);
if (tmp_fb) {
for (unsigned i = 0; i < 7; i++) {
canvas.draw_box(view_rect, Color(i*2,i*6,i*16*2));
tmp_fb->refresh(0,0,1024,768);
}
}
canvas.draw_box(view_rect, color);
}
};

View File

@ -0,0 +1,75 @@
/*
* \brief Nitpicker buffer
* \author Norman Feske
* \date 2017-11-16
*/
/*
* Copyright (C) 2006-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _BUFFER_H_
#define _BUFFER_H_
/* Genode includes */
#include <base/attached_ram_dataspace.h>
#include <framebuffer_session/framebuffer_session.h>
/* local includes */
#include "types.h"
namespace Nitpicker { class Buffer; }
class Nitpicker::Buffer
{
private:
Area _size;
Framebuffer::Mode::Format _format;
Attached_ram_dataspace _ram_ds;
public:
/**
* Constructor - allocate and map dataspace for virtual frame buffer
*
* \throw Out_of_ram
* \throw Out_of_caps
* \throw Region_map::Region_conflict
*/
Buffer(Ram_session &ram, Region_map &rm,
Area size, Framebuffer::Mode::Format format, size_t bytes)
:
_size(size), _format(format), _ram_ds(ram, rm, bytes)
{ }
/**
* Accessors
*/
Ram_dataspace_capability ds_cap() const { return _ram_ds.cap(); }
Area size() const { return _size; }
Framebuffer::Mode::Format format() const { return _format; }
void *local_addr() const { return _ram_ds.local_addr<void>(); }
};
namespace Nitpicker { struct Buffer_provider; }
/**
* Interface for triggering the re-allocation of a virtual framebuffer
*
* Used by 'Framebuffer::Session_component',
* implemented by 'Nitpicker::Session_component'
*/
struct Nitpicker::Buffer_provider
{
virtual Buffer *realloc_buffer(Framebuffer::Mode mode, bool use_alpha) = 0;
};
#endif /* _BUFFER_H_ */

View File

@ -18,19 +18,19 @@
#include <nitpicker_gfx/text_painter.h>
#include <nitpicker_gfx/texture_painter.h>
typedef Genode::Surface_base::Area Area;
typedef Genode::Surface_base::Point Point;
typedef Genode::Surface_base::Rect Rect;
typedef Genode::Color Color;
/* local includes */
#include "types.h"
using Genode::Texture_base;
using Genode::Texture;
namespace Nitpicker {
struct Canvas_base;
template <typename PT> class Canvas;
}
/**
* Pixel-type-independent interface of nitpicker's graphics backend
*/
struct Canvas_base
struct Nitpicker::Canvas_base
{
virtual Area size() const = 0;
@ -54,11 +54,11 @@ struct Canvas_base
* Pixel-type-specific implementation of nitpicker's graphics backend
*/
template <typename PT>
class Canvas : public Canvas_base, public Genode::Surface_base::Flusher
class Nitpicker::Canvas : public Canvas_base, public Surface_base::Flusher
{
private:
Genode::Surface<PT> _surface;
Surface<PT> _surface;
public:

View File

@ -0,0 +1,77 @@
/*
* \brief Texture allocated as RAM dataspace
* \author Norman Feske
* \date 2017-11-16
*/
/*
* Copyright (C) 2006-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _CHUNKY_TEXTURE_H_
#define _CHUNKY_TEXTURE_H_
/* local includes */
#include "buffer.h"
namespace Nitpicker { template <typename> class Chunky_texture; }
template <typename PT>
class Nitpicker::Chunky_texture : public Buffer, public Texture<PT>
{
private:
Framebuffer::Mode::Format _format() {
return Framebuffer::Mode::RGB565; }
/**
* Return base address of alpha channel or 0 if no alpha channel exists
*/
unsigned char *_alpha_base(Area size, bool use_alpha)
{
if (!use_alpha) return 0;
/* alpha values come right after the pixel values */
return (unsigned char *)local_addr() + calc_num_bytes(size, false);
}
public:
/**
* Constructor
*/
Chunky_texture(Ram_session &ram, Region_map &rm, Area size, bool use_alpha)
:
Buffer(ram, rm, size, _format(), calc_num_bytes(size, use_alpha)),
Texture<PT>((PT *)local_addr(),
_alpha_base(size, use_alpha), size) { }
static size_t calc_num_bytes(Area size, bool use_alpha)
{
/*
* If using an alpha channel, the alpha buffer follows the
* pixel buffer. The alpha buffer is followed by an input
* mask buffer. Hence, we have to account one byte per
* alpha value and one byte for the input mask value.
*/
size_t bytes_per_pixel = sizeof(PT) + (use_alpha ? 2 : 0);
return bytes_per_pixel*size.w()*size.h();
}
unsigned char *input_mask_buffer()
{
if (!Texture<PT>::alpha()) return 0;
Area const size = Texture<PT>::size();
/* input-mask values come right after the alpha values */
return (unsigned char *)local_addr() + calc_num_bytes(size, false)
+ size.count();
}
};
#endif /* _CHUNKY_TEXTURE_H_ */

View File

@ -30,7 +30,10 @@
#include "canvas.h"
class Clip_guard
namespace Nitpicker { class Clip_guard; }
class Nitpicker::Clip_guard
{
private:

View File

@ -1,27 +0,0 @@
/*
* \brief Color definitions used by nitpicker
* \date 2006-08-04
* \author Norman Feske
*/
/*
* Copyright (C) 2006-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _COLOR_H_
#define _COLOR_H_
#include <util/color.h>
/*
* Symbolic names for some important colors
*/
static const Genode::Color BLACK(0, 0, 0);
static const Genode::Color WHITE(255, 255, 255);
static const Genode::Color FRAME_COLOR(255, 200, 127);
static const Genode::Color KILL_COLOR(255, 0, 0);
#endif /* _COLOR_H_ */

View File

@ -14,21 +14,20 @@
#ifndef _DOMAIN_REGISTRY_
#define _DOMAIN_REGISTRY_
#include <base/allocator.h>
#include <base/log.h>
#include <util/xml_node.h>
#include <util/color.h>
#include "types.h"
class Domain_registry
namespace Nitpicker { class Domain_registry; }
class Nitpicker::Domain_registry
{
public:
class Entry : public Genode::List<Entry>::Element
class Entry : public List<Entry>::Element
{
public:
typedef Genode::String<64> Name;
typedef Genode::Color Color;
typedef String<64> Name;
enum Label { LABEL_NO, LABEL_YES };
enum Content { CONTENT_CLIENT, CONTENT_TINTED };
@ -109,31 +108,31 @@ class Domain_registry
{
int const w = _area.x() > 0
? _area.x()
: Genode::max(0, (int)phys_screen_area.w() + _area.x());
: max(0, (int)phys_screen_area.w() + _area.x());
int const h = _area.y() > 0
? _area.y()
: Genode::max(0, (int)phys_screen_area.h() + _area.y());
: max(0, (int)phys_screen_area.h() + _area.y());
return Area(w, h);
}
};
static Entry::Label _label(Genode::Xml_node domain)
static Entry::Label _label(Xml_node domain)
{
typedef Genode::String<32> Value;
typedef String<32> Value;
Value const value = domain.attribute_value("label", Value("yes"));
if (value == "no") return Entry::LABEL_NO;
if (value == "yes") return Entry::LABEL_YES;
Genode::warning("invalid value of label attribute in <domain>");
warning("invalid value of label attribute in <domain>");
return Entry::LABEL_YES;
}
static Entry::Content _content(Genode::Xml_node domain)
static Entry::Content _content(Xml_node domain)
{
typedef Genode::String<32> Value;
typedef String<32> Value;
Value const value = domain.attribute_value("content", Value("tinted"));
if (value == "client") return Entry::CONTENT_CLIENT;
@ -142,34 +141,34 @@ class Domain_registry
return Entry::CONTENT_TINTED;
}
static Entry::Hover _hover(Genode::Xml_node domain)
static Entry::Hover _hover(Xml_node domain)
{
typedef Genode::String<32> Value;
typedef String<32> Value;
Value const value = domain.attribute_value("hover", Value("focused"));
if (value == "focused") return Entry::HOVER_FOCUSED;
if (value == "always") return Entry::HOVER_ALWAYS;
Genode::warning("invalid value of hover attribute in <domain>");
warning("invalid value of hover attribute in <domain>");
return Entry::HOVER_FOCUSED;
}
static Entry::Focus _focus(Genode::Xml_node domain)
static Entry::Focus _focus(Xml_node domain)
{
typedef Genode::String<32> Value;
typedef String<32> Value;
Value const value = domain.attribute_value("focus", Value("none"));
if (value == "none") return Entry::FOCUS_NONE;
if (value == "click") return Entry::FOCUS_CLICK;
if (value == "transient") return Entry::FOCUS_TRANSIENT;
Genode::warning("invalid value of focus attribute in <domain>");
warning("invalid value of focus attribute in <domain>");
return Entry::FOCUS_NONE;
}
static Entry::Origin _origin(Genode::Xml_node domain)
static Entry::Origin _origin(Xml_node domain)
{
typedef Genode::String<32> Value;
typedef String<32> Value;
Value const value = domain.attribute_value("origin", Value("top_left"));
if (value == "top_left") return Entry::ORIGIN_TOP_LEFT;
@ -178,11 +177,11 @@ class Domain_registry
if (value == "bottom_right") return Entry::ORIGIN_BOTTOM_RIGHT;
if (value == "pointer") return Entry::ORIGIN_POINTER;
Genode::warning("invalid value of origin attribute in <domain>");
warning("invalid value of origin attribute in <domain>");
return Entry::ORIGIN_BOTTOM_LEFT;
}
void _insert(Genode::Xml_node domain)
void _insert(Xml_node domain)
{
char buf[sizeof(Entry::Name)];
buf[0] = 0;
@ -193,19 +192,19 @@ class Domain_registry
} catch (...) { }
if (!name_defined) {
Genode::error("no valid domain name specified");
error("no valid domain name specified");
return;
}
Entry::Name const name(buf);
if (lookup(name)) {
Genode::error("domain name \"", name, "\" is not unique");
error("domain name \"", name, "\" is not unique");
return;
}
if (!domain.has_attribute("layer")) {
Genode::error("no layer specified for domain \"", name, "\"");
error("no layer specified for domain \"", name, "\"");
return;
}
@ -217,7 +216,7 @@ class Domain_registry
Point const area(domain.attribute_value("width", 0L),
domain.attribute_value("height", 0L));
Entry::Color const color = domain.attribute_value("color", WHITE);
Color const color = domain.attribute_value("color", white());
_entries.insert(new (_alloc) Entry(name, color, _label(domain),
_content(domain), _hover(domain),
@ -227,12 +226,12 @@ class Domain_registry
private:
Genode::List<Entry> _entries;
Genode::Allocator &_alloc;
List<Entry> _entries;
Allocator &_alloc;
public:
Domain_registry(Genode::Allocator &alloc, Genode::Xml_node config)
Domain_registry(Allocator &alloc, Xml_node config)
:
_alloc(alloc)
{
@ -241,7 +240,7 @@ class Domain_registry
if (!config.has_sub_node(type))
return;
Genode::Xml_node domain = config.sub_node(type);
Xml_node domain = config.sub_node(type);
for (;; domain = domain.next(type)) {
@ -256,7 +255,7 @@ class Domain_registry
{
while (Entry *e = _entries.first()) {
_entries.remove(e);
Genode::destroy(_alloc, e);
destroy(_alloc, e);
}
}

View File

@ -15,58 +15,63 @@
#define _DRAW_LABEL_H_
#include "canvas.h"
extern Text_painter::Font default_font;
/*
* Gap between session label and view title in pixels
*/
enum { LABEL_GAP = 5 };
/**
* Draw black outline of string
*/
inline void draw_string_outline(Canvas_base &canvas, Point pos, char const *s)
{
for (int j = -1; j <= 1; j++)
for (int i = -1; i <= 1; i++)
if (i || j)
canvas.draw_text(pos + Point(i, j), default_font, BLACK, s);
}
#include "types.h"
/**
* Return bounding box of composed label displayed with the default font
*
* \param sl session label string
* \param vt view title string
*/
inline Area label_size(const char *sl, const char *vt) {
return Area(default_font.str_w(sl) + LABEL_GAP + default_font.str_w(vt) + 2,
default_font.str_h(sl) + 2); }
namespace Nitpicker {
extern Text_painter::Font default_font;
/*
* Gap between session label and view title in pixels
*/
enum { LABEL_GAP = 5 };
/**
* Draw black outline of string
*/
inline void draw_string_outline(Canvas_base &canvas, Point pos, char const *s)
{
for (int j = -1; j <= 1; j++)
for (int i = -1; i <= 1; i++)
if (i || j)
canvas.draw_text(pos + Point(i, j), default_font, black(), s);
}
/**
* Draw outlined view label
*
* View labels are composed of two parts: the session label and the view title.
* The unforgeable session label is defined on session creation by system
* policy. In contrast, the view title can individually be defined by the
* application.
*/
static inline void draw_label(Canvas_base &canvas, Point pos,
char const *session_label, Color session_label_color,
char const *view_title, Color view_title_color)
{
pos = pos + Point(1, 1);
/**
* Return bounding box of composed label displayed with the default font
*
* \param sl session label string
* \param vt view title string
*/
inline Area label_size(const char *sl, const char *vt) {
return Area(default_font.str_w(sl) + LABEL_GAP + default_font.str_w(vt) + 2,
default_font.str_h(sl) + 2); }
draw_string_outline(canvas, pos, session_label);
canvas.draw_text(pos, default_font, session_label_color, session_label);
pos = pos + Point(default_font.str_w(session_label) + LABEL_GAP, 0);
/**
* Draw outlined view label
*
* View labels are composed of two parts: the session label and the view title.
* The unforgeable session label is defined on session creation by system
* policy. In contrast, the view title can individually be defined by the
* application.
*/
static inline void draw_label(Canvas_base &canvas, Point pos,
char const *session_label, Color session_label_color,
char const *view_title, Color view_title_color)
{
pos = pos + Point(1, 1);
draw_string_outline(canvas, pos, view_title);
canvas.draw_text(pos, default_font, view_title_color, view_title);
draw_string_outline(canvas, pos, session_label);
canvas.draw_text(pos, default_font, session_label_color, session_label);
pos = pos + Point(default_font.str_w(session_label) + LABEL_GAP, 0);
draw_string_outline(canvas, pos, view_title);
canvas.draw_text(pos, default_font, view_title_color, view_title);
}
}
#endif /* _DRAW_LABEL_H_ */

View File

@ -0,0 +1,86 @@
/*
* \brief Interfaces for requesting and controlling the focus
* \author Norman Feske
* \date 2017-11-17
*/
/*
* Copyright (C) 2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _FOCUS_H_
#define _FOCUS_H_
#include "types.h"
#include "view_component.h"
namespace Nitpicker {
struct Focus;
struct Focus_controller;
}
/**
* Interface used by the view stack
*/
class Nitpicker::Focus : Noncopyable
{
private:
View_owner const *_focused = nullptr;
public:
/**
* Return true if specified view owner has the input focus
*/
bool focused(View_owner const &owner) const
{
return &owner == _focused;
}
/**
* Return true if the specified view owner belongs to the same domain as
* the currently focused view owner
*/
bool same_domain_as_focused(View_owner const &owner) const
{
return owner.has_same_domain(_focused);
}
/**
* Return true if the specified view is the background view as defined for
* the currentlu focused view owner.
*/
bool focused_background(View_component const &view) const
{
return _focused && (_focused->background() == &view);
}
/**
* Set the input focus to the specified view owner
*/
void assign(View_owner const &focused) { _focused = &focused; }
void forget(View_owner const &owner)
{
if (_focused == &owner)
_focused = nullptr;
}
};
/**
* Interface used by a nitpicker client to assign the focus to a session of
* one of its child components (according to the session labels)
*/
struct Nitpicker::Focus_controller
{
virtual void focus_view_owner(View_owner const &caller,
View_owner &next_focused) = 0;
};
#endif /* _FOCUS_H_ */

View File

@ -0,0 +1,115 @@
/*
* \brief Framebuffer sub session as part of the nitpicker session
* \author Norman Feske
* \date 2017-11-16
*/
/*
* Copyright (C) 2006-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _FRAMEBUFFER_SESSION_COMPONENT_H_
#define _FRAMEBUFFER_SESSION_COMPONENT_H_
/* local includes */
#include "buffer.h"
namespace Framebuffer {
using namespace Nitpicker;
using Nitpicker::Rect;
using Nitpicker::Point;
using Nitpicker::Area;
class Session_component;
}
class Framebuffer::Session_component : public Rpc_object<Session>
{
private:
Buffer *_buffer = 0;
View_stack &_view_stack;
Nitpicker::Session_component &_session;
Framebuffer::Session &_framebuffer;
Buffer_provider &_buffer_provider;
Signal_context_capability _mode_sigh;
Signal_context_capability _sync_sigh;
Framebuffer::Mode _mode;
bool _alpha = false;
public:
/**
* Constructor
*/
Session_component(View_stack &view_stack,
Nitpicker::Session_component &session,
Framebuffer::Session &framebuffer,
Buffer_provider &buffer_provider)
:
_view_stack(view_stack),
_session(session),
_framebuffer(framebuffer),
_buffer_provider(buffer_provider)
{ }
/**
* Change virtual framebuffer mode
*
* Called by Nitpicker::Session_component when re-dimensioning the
* buffer.
*
* The new mode does not immediately become active. The client can
* keep using an already obtained framebuffer dataspace. However,
* we inform the client about the mode change via a signal. If the
* client calls 'dataspace' the next time, the new mode becomes
* effective.
*/
void notify_mode_change(Framebuffer::Mode mode, bool alpha)
{
_mode = mode;
_alpha = alpha;
if (_mode_sigh.valid())
Signal_transmitter(_mode_sigh).submit();
}
void submit_sync()
{
if (_sync_sigh.valid())
Signal_transmitter(_sync_sigh).submit();
}
/************************************
** Framebuffer::Session interface **
************************************/
Dataspace_capability dataspace() override
{
_buffer = _buffer_provider.realloc_buffer(_mode, _alpha);
return _buffer ? _buffer->ds_cap() : Ram_dataspace_capability();
}
Mode mode() const override { return _mode; }
void mode_sigh(Signal_context_capability sigh) override
{
_mode_sigh = sigh;
}
void sync_sigh(Signal_context_capability sigh) override
{
_sync_sigh = sigh;
}
void refresh(int x, int y, int w, int h) override;
};
#endif /* _FRAMEBUFFER_SESSION_COMPONENT_H_ */

View File

@ -13,12 +13,15 @@
/* local includes */
#include "global_keys.h"
#include "session_component.h"
using namespace Nitpicker;
Global_keys::Policy *Global_keys::_lookup_policy(char const *key_name)
{
for (unsigned i = 0; i < NUM_POLICIES; i++)
if (Genode::strcmp(key_name, Input::key_name((Input::Keycode)i)) == 0)
if (strcmp(key_name, Input::key_name((Input::Keycode)i)) == 0)
return &_policies[i];
return 0;
@ -38,15 +41,15 @@ void Global_keys::apply_config(Xml_node config, Session_list &session_list)
for (; ; node = node.next(node_type)) {
if (!node.has_attribute("name")) {
Genode::warning("attribute 'name' missing in <global-key> config node");
warning("attribute 'name' missing in <global-key> config node");
continue;
}
typedef Genode::String<32> Name;
typedef String<32> Name;
Name name = node.attribute_value("name", Name());
Policy * policy = _lookup_policy(name.string());
if (!policy) {
Genode::warning("invalid key name \"", name, "\"");
warning("invalid key name \"", name, "\"");
continue;
}
@ -55,12 +58,12 @@ void Global_keys::apply_config(Xml_node config, Session_list &session_list)
continue;
if (!node.has_attribute("label")) {
Genode::warning("missing 'label' attribute for key ", name);
warning("missing 'label' attribute for key ", name);
continue;
}
/* assign policy to matching client session */
for (Session *s = session_list.first(); s; s = s->next())
for (Session_component *s = session_list.first(); s; s = s->next())
if (node.attribute("label").has_value(s->label().string()))
policy->client(s);
}

View File

@ -16,24 +16,24 @@
/* Genode includes */
#include <input/keycodes.h>
#include <util/xml_node.h>
/* local includes */
#include "session.h"
#include "session_component.h"
class Global_keys
namespace Nitpicker { class Global_keys; }
class Nitpicker::Global_keys
{
private:
typedef Genode::Xml_node Xml_node;
struct Policy
{
Session *_session = nullptr;
Session_component *_session = nullptr;
bool defined() const { return _session != nullptr; }
void client(Session *s) { _session = s; }
void client(Session_component *s) { _session = s; }
};
enum { NUM_POLICIES = Input::KEY_MAX + 1 };
@ -50,7 +50,7 @@ class Global_keys
public:
Session *global_receiver(Input::Keycode key) {
Session_component *global_receiver(Input::Keycode key) {
return _valid(key) ? _policies[key]._session : 0; }
void apply_config(Xml_node config, Session_list &session_list);

View File

@ -1,130 +0,0 @@
/*
* \brief Input handling utilities
* \author Norman Feske
* \date 2013-09-07
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INPUT_H_
#define _INPUT_H_
/* Genode includes */
#include <input/event.h>
/* local includes */
#include "user_state.h"
/**
* Determine number of events that can be merged into one
*
* \param ev pointer to first event array element to check
* \param max size of the event array
* \return number of events subjected to merge
*/
static unsigned num_consecutive_events(Input::Event const *ev, unsigned max)
{
if (max < 1) return 0;
if (ev->type() != Input::Event::MOTION) return 1;
bool const first_absolute = ev->absolute_motion();
/* iterate until we get a different event type, start at second */
unsigned cnt = 1;
for (ev++ ; cnt < max; cnt++, ev++) {
if (ev->type() != Input::Event::MOTION) break;
if (first_absolute != ev->absolute_motion()) break;
}
return cnt;
}
/**
* Merge consecutive motion events
*
* \param ev event array to merge
* \param n number of events to merge
* \return merged motion event
*/
static Input::Event merge_motion_events(Input::Event const *ev, unsigned n)
{
Input::Event res;
for (unsigned i = 0; i < n; i++, ev++)
res = Input::Event(Input::Event::MOTION, 0, ev->ax(), ev->ay(),
res.rx() + ev->rx(), res.ry() + ev->ry());
return res;
}
/**
* Feed input event to the user state
*
* \return true if user has been active. A user is active as long as at
* least one key/button is pressed (during drag operations)
* and when a key/button changes it state.
*/
static bool import_input_events(Input::Event *ev_buf, unsigned num_ev,
User_state &user_state)
{
bool user_active = false;
if (num_ev > 0) {
/*
* Take events from input event buffer, merge consecutive motion
* events, and pass result to the user state.
*/
for (unsigned src_ev_cnt = 0; src_ev_cnt < num_ev; src_ev_cnt++) {
Input::Event *e = &ev_buf[src_ev_cnt];
Input::Event curr = *e;
if (e->type() == Input::Event::MOTION) {
unsigned n = num_consecutive_events(e, num_ev - src_ev_cnt);
curr = merge_motion_events(e, n);
/* skip merged events */
src_ev_cnt += n - 1;
}
/*
* If subsequential relative motion events are merged to
* a zero-motion event, drop it. Otherwise, it would be
* misinterpreted as absolute event pointing to (0, 0).
*/
if (e->relative_motion() && curr.rx() == 0 && curr.ry() == 0)
continue;
/*
* If we detect a pressed key sometime during the event processing,
* we regard the user as active. This check captures the presence
* of press-release combinations within one batch of input events.
*/
user_active |= user_state.key_pressed();
/* pass event to user state */
user_state.handle_event(curr);
}
} else {
/*
* Besides handling input events, 'user_state.handle_event()' also
* updates the pointed session, which might have changed by other
* means, for example view movement.
*/
user_state.handle_event(Input::Event());
}
/*
* If at least one key is kept pressed, we regard the user as active.
*/
user_active |= user_state.key_pressed();
return user_active;
}
#endif /* _INPUT_H_ */

View File

@ -0,0 +1,113 @@
/*
* \brief Input sub session as part of the nitpicker session
* \author Norman Feske
* \date 2017-11-16
*/
/*
* Copyright (C) 2006-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INPUT_SESSION_COMPONENT_H_
#define _INPUT_SESSION_COMPONENT_H_
/* Genode includes */
#include <base/rpc_server.h>
#include <input_session/input_session.h>
#include <input/event.h>
/* local incudes */
#include "types.h"
namespace Input {
using namespace Nitpicker;
class Session_component;
}
class Input::Session_component : public Rpc_object<Session>
{
public:
enum { MAX_EVENTS = 200 };
static size_t ev_ds_size() {
return align_addr(MAX_EVENTS*sizeof(Event), 12); }
private:
/*
* Exported event buffer dataspace
*/
Attached_ram_dataspace _ev_ram_ds;
/*
* Local event buffer that is copied
* to the exported event buffer when
* flush() gets called.
*/
Event _ev_buf[MAX_EVENTS];
unsigned _num_ev = 0;
Signal_context_capability _sigh;
public:
Session_component(Env &env)
:
_ev_ram_ds(env.ram(), env.rm(), ev_ds_size())
{ }
/**
* Wake up client
*/
void submit_signal()
{
if (_sigh.valid())
Signal_transmitter(_sigh).submit();
}
/**
* Enqueue event into local event buffer of the input session
*/
void submit(const Event *ev)
{
/* drop event when event buffer is full */
if (_num_ev >= MAX_EVENTS) return;
/* insert event into local event buffer */
_ev_buf[_num_ev++] = *ev;
submit_signal();
}
/*****************************
** Input session interface **
*****************************/
Dataspace_capability dataspace() override { return _ev_ram_ds.cap(); }
bool pending() const override { return _num_ev > 0; }
int flush() override
{
unsigned ev_cnt;
/* copy events from local event buffer to exported buffer */
Event *ev_ds_buf = _ev_ram_ds.local_addr<Event>();
for (ev_cnt = 0; ev_cnt < _num_ev; ev_cnt++)
ev_ds_buf[ev_cnt] = _ev_buf[ev_cnt];
_num_ev = 0;
return ev_cnt;
}
void sigh(Signal_context_capability sigh) override { _sigh = sigh; }
};
#endif /* _INPUT_SESSION_COMPONENT_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -1,97 +0,0 @@
/*
* \brief Nitpicker mode
* \author Norman Feske
* \date 2006-08-22
*/
/*
* Copyright (C) 2006-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _MODE_H_
#define _MODE_H_
class Session;
class Mode
{
private:
/*
* Number of currently pressed keys. This counter is used to determine
* if the user is dragging an item.
*/
unsigned _key_cnt = 0;
Session *_focused_session = nullptr;
Session *_next_focused_session = nullptr;
protected:
/*
* True while a global key sequence is processed
*/
bool _global_key_sequence = false;
public:
virtual ~Mode() { }
/**
* Accessors
*/
bool drag() const { return _key_cnt > 0; }
void inc_key_cnt() { _key_cnt++; }
void dec_key_cnt() { _key_cnt--; }
unsigned key_cnt() const { return _key_cnt; }
bool key_pressed() const { return _key_cnt > 0; }
Session *focused_session() { return _focused_session; }
Session const *focused_session() const { return _focused_session; }
virtual void focused_session(Session *session)
{
_focused_session = session;
_next_focused_session = session;
}
bool focused(Session const &session) const { return &session == _focused_session; }
void next_focused_session(Session *session) { _next_focused_session = session; }
/**
* Apply pending focus-change request that was issued during drag state
*/
void apply_pending_focus_change()
{
/*
* Defer focus changes to a point where no drag operation is in
* flight because otherwise, the involved sessions would obtain
* inconsistent press and release events. However, focus changes
* during global key sequences are fine.
*/
if (key_pressed() && !_global_key_sequence)
return;
if (_focused_session != _next_focused_session)
_focused_session = _next_focused_session;
}
/**
* Discard all references to specified view
*/
virtual void forget(Session const &session)
{
if (&session == _focused_session) _focused_session = nullptr;
if (&session == _next_focused_session) _next_focused_session = nullptr;
}
};
#endif

View File

@ -14,33 +14,28 @@
#ifndef _POINTER_ORIGIN_H_
#define _POINTER_ORIGIN_H_
#include "view.h"
#include "session.h"
#include "view_component.h"
#include "session_component.h"
struct Pointer_origin : Session, View
namespace Nitpicker { struct Pointer_origin; }
struct Nitpicker::Pointer_origin : View_component
{
Pointer_origin()
Pointer_origin(View_owner &owner)
:
Session(Genode::Session_label()),
View(*this, View::TRANSPARENT, View::NOT_BACKGROUND, 0)
View_component(owner, View_component::TRANSPARENT,
View_component::NOT_BACKGROUND, 0)
{ }
/***********************
** Session interface **
***********************/
/******************************
** View_component interface **
******************************/
void submit_input_event(Input::Event) override { }
void submit_sync() override { }
/********************
** View interface **
********************/
int frame_size(Mode const &) const override { return 0; }
void frame(Canvas_base &, Mode const &) const override { }
void draw(Canvas_base &, Mode const &) const override { }
int frame_size(Focus const &) const override { return 0; }
void frame(Canvas_base &, Focus const &) const override { }
void draw(Canvas_base &, Focus const &) const override { }
};
#endif /* _POINTER_ORIGIN_H_ */

View File

@ -1,232 +0,0 @@
/*
* \brief Nitpicker session interface
* \author Norman Feske
* \date 2006-08-09
*/
/*
* Copyright (C) 2006-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _SESSION_H_
#define _SESSION_H_
/* Genode includes */
#include <util/list.h>
#include <util/string.h>
#include <os/session_policy.h>
/* local includes */
#include "color.h"
#include "canvas.h"
#include "domain_registry.h"
class View;
class Session;
namespace Input { class Event; }
typedef Genode::List<Session> Session_list;
class Session : public Session_list::Element
{
private:
Genode::Session_label const _label;
Domain_registry::Entry const *_domain;
Texture_base const *_texture = { 0 };
bool _uses_alpha = { false };
bool _visible = true;
View *_background = 0;
unsigned char const *_input_mask = { 0 };
public:
/**
* Constructor
*
* \param label session label
*/
explicit Session(Genode::Session_label const &label) : _label(label) { }
virtual ~Session() { }
virtual void submit_input_event(Input::Event ev) = 0;
virtual void submit_sync() = 0;
Genode::Session_label const &label() const { return _label; }
/**
* Return true if session label starts with specified 'selector'
*/
bool matches_session_label(char const *selector) const
{
/*
* Append label separator to match selectors with a trailing
* separator.
*/
char label[Genode::Session_label::capacity() + 4];
Genode::snprintf(label, sizeof(label), "%s ->", _label.string());
return Genode::strcmp(label, selector,
Genode::strlen(selector)) == 0;
}
/**
* Accessors to the domain configuration used in conditions
*/
bool label_visible() const { return !_domain || _domain->label_visible(); }
bool content_client() const { return _domain && _domain->content_client(); }
bool hover_focused() const { return !_domain || _domain->hover_focused(); }
bool hover_always() const { return _domain && _domain->hover_always(); }
bool origin_pointer() const { return _domain && _domain->origin_pointer(); }
unsigned layer() const { return _domain ? _domain->layer() : ~0UL; }
bool visible() const { return _visible; }
void visible(bool visible) { _visible = visible; }
Domain_registry::Entry::Name domain_name() const
{
return _domain ? _domain->name() : Domain_registry::Entry::Name();
}
Texture_base const *texture() const { return _texture; }
void texture(Texture_base const *texture, bool uses_alpha)
{
_texture = texture;
_uses_alpha = uses_alpha;
}
/**
* Set input mask buffer
*
* \param mask input mask buffer containing a byte value per texture
* pixel, which describes the policy of handling user
* input referring to the pixel. If set to zero, the input
* is passed through the view such that it can be handled
* by one of the subsequent views in the view stack. If
* set to one, the input is consumed by the view. If
* 'input_mask' is a null pointer, user input is
* unconditionally consumed by the view.
*/
void input_mask(unsigned char const *mask) { _input_mask = mask; }
Color color() const { return _domain ? _domain->color() : WHITE; }
View *background() const { return _background; }
void background(View *background) { _background = background; }
/**
* Return true if session uses an alpha channel
*/
bool uses_alpha() const { return _texture && _uses_alpha; }
/**
* Calculate session-local coordinate to physical screen position
*
* \param pos coordinate in session-local coordinate system
* \param screen_area session-local screen size
*/
Point phys_pos(Point pos, Area screen_area) const
{
return _domain ? _domain->phys_pos(pos, screen_area) : Point(0, 0);
}
/**
* Return session-local screen area
*
* \param phys_pos size of physical screen
*/
Area screen_area(Area phys_area) const
{
return _domain ? _domain->screen_area(phys_area) : Area(0, 0);
}
/**
* Return input mask value at specified buffer position
*/
unsigned char input_mask_at(Point p) const
{
if (!_input_mask || !_texture) return 0;
/* check boundaries */
if ((unsigned)p.x() >= _texture->size().w()
|| (unsigned)p.y() >= _texture->size().h())
return 0;
return _input_mask[p.y()*_texture->size().w() + p.x()];
}
bool has_same_domain(Session const *s) const
{
return s && (s->_domain == _domain);
}
bool has_focusable_domain()
{
return has_valid_domain()
&& (_domain->focus_click() || _domain->focus_transient());
}
bool has_transient_focusable_domain()
{
return has_valid_domain() && _domain->focus_transient();
}
bool has_valid_domain() const
{
return _domain;
}
void reset_domain()
{
_domain = nullptr;
}
/**
* Set session domain according to the list of configured policies
*
* Select the policy that matches the label. If multiple policies
* match, select the one with the largest number of characters.
*/
void apply_session_policy(Genode::Xml_node config,
Domain_registry const &domain_registry)
{
reset_domain();
try {
Genode::Session_policy policy(_label, config);
/* read domain attribute */
if (!policy.has_attribute("domain")) {
Genode::error("policy for label \"", _label, "\" lacks domain declaration");
return;
}
typedef Domain_registry::Entry::Name Domain_name;
char buf[sizeof(Domain_name)];
buf[0] = 0;
try {
policy.attribute("domain").value(buf, sizeof(buf)); }
catch (...) { }
Domain_name name(buf);
_domain = domain_registry.lookup(name);
if (!_domain)
Genode::error("policy for label \"", _label,
"\" specifies nonexistent domain \"", name, "\"");
} catch (...) {
Genode::error("no policy matching label \"", _label, "\""); }
}
};
#endif

View File

@ -0,0 +1,484 @@
/*
* \brief Nitpicker session component
* \author Norman Feske
* \date 2017-11-16
*/
/*
* Copyright (C) 2006-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include "view_stack.h"
using namespace Nitpicker;
void Session_component::_release_buffer()
{
if (!_texture)
return;
typedef Pixel_rgb565 PT;
Chunky_texture<PT> const *cdt = static_cast<Chunky_texture<PT> const *>(_texture);
_texture = nullptr;
_uses_alpha = false;
_input_mask = nullptr;
destroy(&_session_alloc, const_cast<Chunky_texture<PT> *>(cdt));
_session_alloc.upgrade(_buffer_size);
_buffer_size = 0;
}
bool Session_component::_views_are_equal(View_handle v1, View_handle v2)
{
if (!v1.valid() || !v2.valid())
return false;
Weak_ptr<View_component> v1_ptr = _view_handle_registry.lookup(v1);
Weak_ptr<View_component> v2_ptr = _view_handle_registry.lookup(v2);
return v1_ptr == v2_ptr;
}
void Session_component::_execute_command(Command const &command)
{
switch (command.opcode) {
case Command::OP_GEOMETRY:
{
Command::Geometry const &cmd = command.geometry;
Locked_ptr<View_component> view(_view_handle_registry.lookup(cmd.view));
if (!view.valid())
return;
Point pos = cmd.rect.p1();
/* transpose position of top-level views by vertical session offset */
if (view->top_level())
pos = _phys_pos(pos, _view_stack.size());
if (view.valid())
_view_stack.geometry(*view, Rect(pos, cmd.rect.area()));
return;
}
case Command::OP_OFFSET:
{
Command::Offset const &cmd = command.offset;
Locked_ptr<View_component> view(_view_handle_registry.lookup(cmd.view));
if (view.valid())
_view_stack.buffer_offset(*view, cmd.offset);
return;
}
case Command::OP_TO_FRONT:
{
Command::To_front const &cmd = command.to_front;
if (_views_are_equal(cmd.view, cmd.neighbor))
return;
Locked_ptr<View_component> view(_view_handle_registry.lookup(cmd.view));
if (!view.valid())
return;
/* bring to front if no neighbor is specified */
if (!cmd.neighbor.valid()) {
_view_stack.stack(*view, nullptr, true);
return;
}
/* stack view relative to neighbor */
Locked_ptr<View_component> neighbor(_view_handle_registry.lookup(cmd.neighbor));
if (neighbor.valid())
_view_stack.stack(*view, &(*neighbor), false);
return;
}
case Command::OP_TO_BACK:
{
Command::To_back const &cmd = command.to_back;
if (_views_are_equal(cmd.view, cmd.neighbor))
return;
Locked_ptr<View_component> view(_view_handle_registry.lookup(cmd.view));
if (!view.valid())
return;
/* bring to front if no neighbor is specified */
if (!cmd.neighbor.valid()) {
_view_stack.stack(*view, nullptr, false);
return;
}
/* stack view relative to neighbor */
Locked_ptr<View_component> neighbor(_view_handle_registry.lookup(cmd.neighbor));
if (neighbor.valid())
_view_stack.stack(*view, &(*neighbor), true);
return;
}
case Command::OP_BACKGROUND:
{
Command::Background const &cmd = command.background;
if (_provides_default_bg) {
Locked_ptr<View_component> view(_view_handle_registry.lookup(cmd.view));
if (!view.valid())
return;
view->background(true);
_view_stack.default_background(*view);
return;
}
/* revert old background view to normal mode */
if (_background)
_background->background(false);
/* assign session background */
Locked_ptr<View_component> view(_view_handle_registry.lookup(cmd.view));
if (!view.valid())
return;
_background = &(*view);
/* switch background view to background mode */
if (background())
view->background(true);
return;
}
case Command::OP_TITLE:
{
Command::Title const &cmd = command.title;
Locked_ptr<View_component> view(_view_handle_registry.lookup(cmd.view));
if (view.valid())
_view_stack.title(*view, cmd.title.string());
return;
}
case Command::OP_NOP:
return;
}
}
void Session_component::_destroy_view(View_component &view)
{
if (_background == &view)
_background = nullptr;
/* reset background if view was used as default background */
if (_view_stack.is_default_background(view))
_view_stack.default_background(_builtin_background);
_view_stack.remove_view(view);
_env.ep().dissolve(view);
_view_list.remove(&view);
destroy(_view_alloc, &view);
}
void Session_component::destroy_all_views()
{
while (Session_view_list_elem *v = _view_list.first())
_destroy_view(*static_cast<View_component *>(v));
}
void Session_component::submit_input_event(Input::Event e)
{
using namespace Input;
Point const origin_offset = _phys_pos(Point(0, 0), _view_stack.size());
/*
* Transpose absolute coordinates by session-specific vertical
* offset.
*/
if (e.ax() || e.ay())
e = Event(e.type(), e.code(),
max(0, e.ax() - origin_offset.x()),
max(0, e.ay() - origin_offset.y()),
e.rx(), e.ry());
_input_session_component.submit(&e);
}
Session_component::View_handle Session_component::create_view(View_handle parent_handle)
{
View_component *view = nullptr;
/*
* Create child view
*/
if (parent_handle.valid()) {
try {
Locked_ptr<View_component> parent(_view_handle_registry.lookup(parent_handle));
if (!parent.valid())
return View_handle();
view = new (_view_alloc)
View_component(*this,
View_component::NOT_TRANSPARENT, View_component::NOT_BACKGROUND,
&(*parent));
parent->add_child(*view);
}
catch (View_handle_registry::Lookup_failed) { return View_handle(); }
catch (View_handle_registry::Out_of_memory) { throw Out_of_ram(); }
}
/*
* Create top-level view
*/
else {
try {
view = new (_view_alloc)
View_component(*this,
View_component::NOT_TRANSPARENT, View_component::NOT_BACKGROUND,
nullptr);
}
catch (Allocator::Out_of_memory) { throw Out_of_ram(); }
}
view->apply_origin_policy(_pointer_origin);
_view_list.insert(view);
_env.ep().manage(*view);
try {
return _view_handle_registry.alloc(*view);
}
catch (View_handle_registry::Out_of_memory) { throw Out_of_ram(); }
}
void Session_component::apply_session_policy(Xml_node config,
Domain_registry const &domain_registry)
{
reset_domain();
try {
Session_policy policy(_label, config);
/* read domain attribute */
if (!policy.has_attribute("domain")) {
error("policy for label \"", _label, "\" lacks domain declaration");
return;
}
typedef Domain_registry::Entry::Name Domain_name;
char buf[sizeof(Domain_name)];
buf[0] = 0;
try {
policy.attribute("domain").value(buf, sizeof(buf)); }
catch (...) { }
Domain_name name(buf);
_domain = domain_registry.lookup(name);
if (!_domain)
error("policy for label \"", _label,
"\" specifies nonexistent domain \"", name, "\"");
} catch (...) {
error("no policy matching label \"", _label, "\""); }
}
void Session_component::destroy_view(View_handle handle)
{
/*
* Search view object given the handle
*
* We cannot look up the view directly from the
* '_view_handle_registry' because we would obtain a weak
* pointer to the view object. If we called the object's
* destructor from the corresponding locked pointer, the
* call of 'lock_for_destruction' in the view's destructor
* would attempt to take the lock again.
*/
for (Session_view_list_elem *v = _view_list.first(); v; v = v->next()) {
try {
View_component &view = *static_cast<View_component *>(v);
if (_view_handle_registry.has_handle(view, handle)) {
_destroy_view(view);
break;
}
} catch (View_handle_registry::Lookup_failed) { }
}
_view_handle_registry.free(handle);
}
Session_component::View_handle
Session_component::view_handle(View_capability view_cap, View_handle handle)
{
auto lambda = [&] (View_component *view)
{
return (view) ? _view_handle_registry.alloc(*view, handle)
: View_handle();
};
try {
return _env.ep().rpc_ep().apply(view_cap, lambda);
}
catch (View_handle_registry::Out_of_memory) { throw Out_of_ram(); }
}
View_capability Session_component::view_capability(View_handle handle)
{
try {
Locked_ptr<View_component> view(_view_handle_registry.lookup(handle));
return view.valid() ? view->cap() : View_capability();
}
catch (View_handle_registry::Lookup_failed) { return View_capability(); }
}
void Session_component::release_view_handle(View_handle handle)
{
try {
_view_handle_registry.free(handle); }
catch (View_handle_registry::Lookup_failed) {
warning("view lookup failed while releasing view handle");
return;
}
}
void Session_component::execute()
{
for (unsigned i = 0; i < _command_buffer.num(); i++) {
try {
_execute_command(_command_buffer.get(i)); }
catch (View_handle_registry::Lookup_failed) {
warning("view lookup failed during command execution"); }
}
}
Framebuffer::Mode Session_component::mode()
{
Area const phys_area(_framebuffer.mode().width(),
_framebuffer.mode().height());
Area const session_area = screen_area(phys_area);
return Framebuffer::Mode(session_area.w(), session_area.h(),
_framebuffer.mode().format());
}
void Session_component::buffer(Framebuffer::Mode mode, bool use_alpha)
{
/* check if the session quota suffices for the specified mode */
if (_session_alloc.quota() < ram_quota(mode, use_alpha))
throw Out_of_ram();
_framebuffer_session_component.notify_mode_change(mode, use_alpha);
}
void Session_component::focus(Capability<Nitpicker::Session> session_cap)
{
if (this->cap() == session_cap)
return;
Session_component const &caller = *this;
_env.ep().rpc_ep().apply(session_cap, [&] (Session_component *session) {
if (session)
_focus_controller.focus_view_owner(caller, *session); });
}
void Session_component::session_control(Label suffix, Session_control control)
{
switch (control) {
case SESSION_CONTROL_HIDE:
_visibility_controller.hide_matching_sessions(label(), suffix);
break;
case SESSION_CONTROL_SHOW:
_visibility_controller.show_matching_sessions(label(), suffix);
break;
case SESSION_CONTROL_TO_FRONT:
_view_stack.to_front(Label(label(), suffix).string());
break;
}
}
Buffer *Session_component::realloc_buffer(Framebuffer::Mode mode, bool use_alpha)
{
typedef Pixel_rgb565 PT;
Area const size(mode.width(), mode.height());
_buffer_size = Chunky_texture<PT>::calc_num_bytes(size, use_alpha);
/*
* Preserve the content of the original buffer if nitpicker has
* enough lack memory to temporarily keep the original pixels.
*/
Texture<PT> const *src_texture = nullptr;
if (texture()) {
enum { PRESERVED_RAM = 128*1024 };
if (_env.ram().avail_ram().value > _buffer_size + PRESERVED_RAM) {
src_texture = static_cast<Texture<PT> const *>(texture());
} else {
warning("not enough RAM to preserve buffer content during resize");
_release_buffer();
}
}
Chunky_texture<PT> * const texture = new (&_session_alloc)
Chunky_texture<PT>(_env.ram(), _env.rm(), size, use_alpha);
/* copy old buffer content into new buffer and release old buffer */
if (src_texture) {
Surface<PT> surface(texture->pixel(),
texture->Texture_base::size());
Texture_painter::paint(surface, *src_texture, Color(), Point(0, 0),
Texture_painter::SOLID, false);
_release_buffer();
}
if (!_session_alloc.withdraw(_buffer_size)) {
destroy(&_session_alloc, texture);
_buffer_size = 0;
return nullptr;
}
_texture = texture;
_uses_alpha = use_alpha;
_input_mask = texture->input_mask_buffer();
return texture;
}

View File

@ -0,0 +1,379 @@
/*
* \brief Nitpicker session component
* \author Norman Feske
* \date 2017-11-16
*/
/*
* Copyright (C) 2006-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _SESSION_COMPONENT_H_
#define _SESSION_COMPONENT_H_
/* Genode includes */
#include <util/list.h>
#include <base/rpc_server.h>
#include <os/session_policy.h>
#include <os/reporter.h>
#include <os/pixel_rgb565.h>
#include <nitpicker_session/nitpicker_session.h>
#include <base/allocator_guard.h>
/* local includes */
#include "canvas.h"
#include "domain_registry.h"
#include "framebuffer_session.h"
#include "input_session.h"
#include "focus.h"
#include "chunky_texture.h"
#include "view_component.h"
namespace Nitpicker {
class Visibility_controller;
class Session_component;
class View_component;
typedef List<Session_component> Session_list;
}
struct Nitpicker::Visibility_controller
{
typedef Session::Label Suffix;
virtual void hide_matching_sessions(Session_label const &, Suffix const &) = 0;
virtual void show_matching_sessions(Session_label const &, Suffix const &) = 0;
};
class Nitpicker::Session_component : public Rpc_object<Session>,
public View_owner,
public Buffer_provider,
public Session_list::Element
{
private:
Env &_env;
Session_label const _label;
Domain_registry::Entry const *_domain = nullptr;
Texture_base const *_texture = nullptr;
View_component *_background = nullptr;
/*
* The input mask buffer containing a byte value per texture pixel,
* which describes the policy of handling user input referring to the
* pixel. If set to zero, the input is passed through the view such
* that it can be handled by one of the subsequent views in the view
* stack. If set to one, the input is consumed by the view. If
* 'input_mask' is a null pointer, user input is unconditionally
* consumed by the view.
*/
unsigned char const *_input_mask = nullptr;
bool _uses_alpha = false;
bool _visible = true;
Allocator_guard _session_alloc;
Framebuffer::Session &_framebuffer;
Framebuffer::Session_component _framebuffer_session_component;
Input::Session_component _input_session_component { _env };
View_stack &_view_stack;
Focus_controller &_focus_controller;
Signal_context_capability _mode_sigh;
View_component &_pointer_origin;
View_component &_builtin_background;
List<Session_view_list_elem> _view_list;
Tslab<View_component, 4000> _view_alloc { &_session_alloc };
/* capabilities for sub sessions */
Framebuffer::Session_capability _framebuffer_session_cap;
Input::Session_capability _input_session_cap;
bool const _provides_default_bg;
/* size of currently allocated virtual framebuffer, in bytes */
size_t _buffer_size = 0;
Attached_ram_dataspace _command_ds { _env.ram(), _env.rm(),
sizeof(Command_buffer) };
Command_buffer &_command_buffer = *_command_ds.local_addr<Command_buffer>();
typedef Handle_registry<View_handle, View_component> View_handle_registry;
View_handle_registry _view_handle_registry;
Reporter &_focus_reporter;
Visibility_controller &_visibility_controller;
/**
* Calculate session-local coordinate to physical screen position
*
* \param pos coordinate in session-local coordinate system
* \param screen_area session-local screen size
*/
Point _phys_pos(Point pos, Area screen_area) const
{
return _domain ? _domain->phys_pos(pos, screen_area) : Point(0, 0);
}
void _release_buffer();
/**
* Helper for performing sanity checks in OP_TO_FRONT and OP_TO_BACK
*
* We have to check for the equality of both the specified view and
* neighbor. If both arguments refer to the same view, the creation of
* locked pointers for both views would result in a deadlock.
*/
bool _views_are_equal(View_handle, View_handle);
bool _focus_change_permitted() const;
void _execute_command(Command const &);
void _destroy_view(View_component &);
public:
Session_component(Env &env,
Session_label const &label,
View_stack &view_stack,
Focus_controller &focus_controller,
View_component &pointer_origin,
View_component &builtin_background,
Framebuffer::Session &framebuffer,
bool provides_default_bg,
Allocator &session_alloc,
size_t ram_quota,
Reporter &focus_reporter,
Visibility_controller &visibility_controller)
:
_env(env),
_label(label),
_session_alloc(&session_alloc, ram_quota),
_framebuffer(framebuffer),
_framebuffer_session_component(view_stack, *this, framebuffer, *this),
_view_stack(view_stack), _focus_controller(focus_controller),
_pointer_origin(pointer_origin),
_builtin_background(builtin_background),
_framebuffer_session_cap(_env.ep().manage(_framebuffer_session_component)),
_input_session_cap(_env.ep().manage(_input_session_component)),
_provides_default_bg(provides_default_bg),
_view_handle_registry(_session_alloc),
_focus_reporter(focus_reporter),
_visibility_controller(visibility_controller)
{
_session_alloc.upgrade(ram_quota);
}
~Session_component()
{
_env.ep().dissolve(_framebuffer_session_component);
_env.ep().dissolve(_input_session_component);
destroy_all_views();
_release_buffer();
}
/**************************
** View_owner interface **
**************************/
Session::Label label() const override { return _label; }
/**
* Return true if session label starts with specified 'selector'
*/
bool matches_session_label(Session::Label const &selector) const override
{
/*
* Append label separator to match selectors with a trailing
* separator.
*/
String<Session_label::capacity() + 4> const label(_label, " ->");
return strcmp(label.string(), selector.string(),
strlen(selector.string())) == 0;
}
bool visible() const override { return _visible; }
bool label_visible() const override
{
return !_domain || _domain->label_visible();
}
bool has_same_domain(View_owner const *owner) const override
{
if (!owner) return false;
return static_cast<Session_component const &>(*owner)._domain == _domain;
}
bool has_focusable_domain() const override
{
return _domain && (_domain->focus_click() || _domain->focus_transient());
}
bool has_transient_focusable_domain() const override
{
return _domain && _domain->focus_transient();
}
Color color() const override { return _domain ? _domain->color() : white(); }
bool content_client() const override { return _domain && _domain->content_client(); }
bool hover_always() const override { return _domain && _domain->hover_always(); }
View const *background() const override { return _background; }
Texture_base const *texture() const override { return _texture; }
bool uses_alpha() const override { return _texture && _uses_alpha; }
unsigned layer() const override { return _domain ? _domain->layer() : ~0UL; }
bool origin_pointer() const override { return _domain && _domain->origin_pointer(); }
/**
* Return input mask value at specified buffer position
*/
unsigned char input_mask_at(Point p) const override
{
if (!_input_mask || !_texture) return 0;
/* check boundaries */
if ((unsigned)p.x() >= _texture->size().w()
|| (unsigned)p.y() >= _texture->size().h())
return 0;
return _input_mask[p.y()*_texture->size().w() + p.x()];
}
void submit_input_event(Input::Event e) override;
void report(Xml_generator &xml) const override
{
xml.attribute("label", _label);
xml.attribute("color", String<32>(color()));
if (_domain)
xml.attribute("domain", _domain->name());
}
/****************************************
** Interface used by the main program **
****************************************/
/**
* Set the visibility of the views owned by the session
*/
void visible(bool visible) { _visible = visible; }
/**
* Return session-local screen area
*
* \param phys_pos size of physical screen
*/
Area screen_area(Area phys_area) const
{
return _domain ? _domain->screen_area(phys_area) : Area(0, 0);
}
void reset_domain() { _domain = nullptr; }
/**
* Set session domain according to the list of configured policies
*
* Select the policy that matches the label. If multiple policies
* match, select the one with the largest number of characters.
*/
void apply_session_policy(Xml_node config, Domain_registry const &);
void destroy_all_views();
/**
* Deliver mode-change signal to client
*/
void notify_mode_change()
{
if (_mode_sigh.valid())
Signal_transmitter(_mode_sigh).submit();
}
void upgrade_ram_quota(size_t ram_quota) { _session_alloc.upgrade(ram_quota); }
/**
* Deliver sync signal to the client's virtual frame buffer
*/
void submit_sync()
{
_framebuffer_session_component.submit_sync();
}
/*********************************
** Nitpicker session interface **
*********************************/
Framebuffer::Session_capability framebuffer_session() override {
return _framebuffer_session_cap; }
Input::Session_capability input_session() override {
return _input_session_cap; }
View_handle create_view(View_handle parent_handle) override;
void destroy_view(View_handle handle) override;
View_handle view_handle(View_capability view_cap, View_handle handle) override;
View_capability view_capability(View_handle handle) override;
void release_view_handle(View_handle handle) override;
Dataspace_capability command_dataspace() override { return _command_ds.cap(); }
void execute() override;
Framebuffer::Mode mode() override;
void mode_sigh(Signal_context_capability sigh) override { _mode_sigh = sigh; }
void buffer(Framebuffer::Mode mode, bool use_alpha) override;
void focus(Capability<Nitpicker::Session> session_cap) override;
void session_control(Label suffix, Session_control control) override;
/*******************************
** Buffer_provider interface **
*******************************/
Buffer *realloc_buffer(Framebuffer::Mode mode, bool use_alpha) override;
};
#endif /* _SESSION_COMPONENT_H_ */

View File

@ -1,9 +1,4 @@
TARGET = nitpicker
LIBS = base blit
SRC_CC = main.cc \
view_stack.cc \
view.cc \
user_state.cc \
global_keys.cc
SRC_CC = $(notdir $(wildcard $(PRG_DIR)/*.cc))
SRC_BIN = default.tff

View File

@ -0,0 +1,42 @@
/*
* \brief Common types used within nitpicker
* \date 2017-11-166
* \author Norman Feske
*/
/*
* Copyright (C) 2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _TYPES_H_
#define _TYPES_H_
/* Genode includes */
#include <util/xml_node.h>
#include <util/color.h>
#include <base/allocator.h>
#include <base/log.h>
#include <os/surface.h>
namespace Nitpicker {
using namespace Genode;
typedef Surface_base::Point Point;
typedef Surface_base::Area Area;
typedef Surface_base::Rect Rect;
/*
* Symbolic names for some important colors
*/
static inline Color black() { return Color(0, 0, 0); }
static inline Color white() { return Color(255, 255, 255); }
class Session_component;
class View_stack;
}
#endif /* _TYPES_H_ */

View File

@ -27,23 +27,52 @@ static inline bool _mouse_button(Keycode keycode) {
return keycode >= BTN_LEFT && keycode <= BTN_MIDDLE; }
/**
* Determine number of events that can be merged into one
*
* \param ev pointer to first event array element to check
* \param max size of the event array
* \return number of events subjected to merge
*/
static unsigned num_consecutive_events(Input::Event const *ev, unsigned max)
{
if (max < 1) return 0;
if (ev->type() != Input::Event::MOTION) return 1;
bool const first_absolute = ev->absolute_motion();
/* iterate until we get a different event type, start at second */
unsigned cnt = 1;
for (ev++ ; cnt < max; cnt++, ev++) {
if (ev->type() != Input::Event::MOTION) break;
if (first_absolute != ev->absolute_motion()) break;
}
return cnt;
}
/**
* Merge consecutive motion events
*
* \param ev event array to merge
* \param n number of events to merge
* \return merged motion event
*/
static Input::Event merge_motion_events(Input::Event const *ev, unsigned n)
{
Input::Event res;
for (unsigned i = 0; i < n; i++, ev++)
res = Input::Event(Input::Event::MOTION, 0, ev->ax(), ev->ay(),
res.rx() + ev->rx(), res.ry() + ev->ry());
return res;
}
/**************************
** User state interface **
**************************/
User_state::User_state(Global_keys &global_keys, Area view_stack_size)
:
View_stack(view_stack_size, *this), _global_keys(global_keys)
{ }
void User_state::_update_all()
{
update_all_views();
}
void User_state::handle_event(Input::Event ev)
void User_state::_handle_input_event(Input::Event ev)
{
Input::Keycode const keycode = ev.keycode();
Input::Event::Type const type = ev.type();
@ -57,8 +86,8 @@ void User_state::handle_event(Input::Event ev)
/* transparently handle absolute and relative motion events */
if (type == Event::MOTION) {
if ((ev.rx() || ev.ry()) && ev.ax() == 0 && ev.ay() == 0) {
ax = Genode::max(0, Genode::min((int)size().w() - 1, ax + ev.rx()));
ay = Genode::max(0, Genode::min((int)size().h() - 1, ay + ev.ry()));
ax = max(0, min((int)_view_stack.size().w() - 1, ax + ev.rx()));
ay = max(0, min((int)_view_stack.size().h() - 1, ay + ev.ry()));
} else {
ax = ev.ax();
ay = ev.ay();
@ -83,9 +112,11 @@ void User_state::handle_event(Input::Event ev)
_pointer_pos = Point(ax, ay);
bool const drag = _key_cnt > 0;
/* count keys */
if (type == Event::PRESS) Mode::inc_key_cnt();
if (type == Event::RELEASE && Mode::drag()) Mode::dec_key_cnt();
if (type == Event::PRESS) _key_cnt++;
if (type == Event::RELEASE && drag) _key_cnt--;
/* track key states */
if (type == Event::PRESS) {
@ -100,72 +131,52 @@ void User_state::handle_event(Input::Event ev)
_key_array.pressed(keycode, false);
}
View const * const pointed_view = find_view(_pointer_pos);
::Session * const pointed_session = pointed_view ? &pointed_view->session() : 0;
View_component const * const pointed_view = _view_stack.find_view(_pointer_pos);
View_owner * const hovered = pointed_view ? &pointed_view->owner() : 0;
/*
* Deliver a leave event if pointed-to session changed
*/
if (_pointed_session && (pointed_session != _pointed_session)) {
if (_hovered && (hovered != _hovered)) {
Input::Event leave_ev(Input::Event::LEAVE, 0, ax, ay, 0, 0);
_pointed_session->submit_input_event(leave_ev);
_hovered->submit_input_event(leave_ev);
}
_pointed_session = pointed_session;
/**
* Guard that, when 'enabled' is set to true, performs a whole-screen
* update when leaving the scope
*/
struct Update_all_guard
{
User_state &user_state;
bool update = false;
Update_all_guard(User_state &user_state)
: user_state(user_state) { }
~Update_all_guard()
{
if (update)
user_state._update_all();
}
} update_all_guard(*this);
_hovered = hovered;
/*
* Handle start of a key sequence
*/
if (type == Event::PRESS && (Mode::key_cnt() == 1)) {
if (type == Event::PRESS && (_key_cnt == 1)) {
::Session *global_receiver = nullptr;
View_owner *global_receiver = nullptr;
/* update focused session */
if (_mouse_button(keycode)
&& _pointed_session
&& (_pointed_session != Mode::focused_session())
&& (_pointed_session->has_focusable_domain()
|| _pointed_session->has_same_domain(Mode::focused_session()))) {
update_all_guard.update = true;
&& _hovered
&& (_hovered != _focused)
&& (_hovered->has_focusable_domain()
|| _hovered->has_same_domain(_focused))) {
/*
* Notify both the old focused session and the new one.
*/
if (Mode::focused_session()) {
if (_focused) {
Input::Event unfocus_ev(Input::Event::FOCUS, 0, ax, ay, 0, 0);
Mode::focused_session()->submit_input_event(unfocus_ev);
_focused->submit_input_event(unfocus_ev);
}
if (_pointed_session) {
if (_hovered) {
Input::Event focus_ev(Input::Event::FOCUS, 1, ax, ay, 0, 0);
_pointed_session->submit_input_event(focus_ev);
_hovered->submit_input_event(focus_ev);
}
if (_pointed_session->has_transient_focusable_domain())
global_receiver = _pointed_session;
if (_hovered->has_transient_focusable_domain())
global_receiver = _hovered;
else
focused_session(_pointed_session);
_focus_view_owner_via_click(*_hovered);
}
/*
@ -182,18 +193,16 @@ void User_state::handle_event(Input::Event ev)
global_receiver = _global_keys.global_receiver(keycode);
if (global_receiver) {
_global_key_sequence = true;
_input_receiver = global_receiver;
update_all_guard.update = true;
_global_key_sequence = true;
_input_receiver = global_receiver;
}
/*
* No global rule matched, so the input stream gets directed to the
* focused session or refers to a built-in operation.
*/
if (!global_receiver) {
_input_receiver = Mode::focused_session();
}
if (!global_receiver)
_input_receiver = _focused;
}
/*
@ -201,18 +210,18 @@ void User_state::handle_event(Input::Event ev)
*/
if (type == Event::MOTION || type == Event::WHEEL || type == Event::TOUCH) {
if (Mode::key_cnt() == 0) {
if (_key_cnt == 0) {
if (_pointed_session) {
if (_hovered) {
/*
* Unless the domain of the pointed session is configured to
* always receive hover events, we deliver motion events only
* to the focused domain.
*/
if (_pointed_session->hover_always()
|| _pointed_session->has_same_domain(Mode::focused_session()))
_pointed_session->submit_input_event(ev);
if (_hovered->hover_always()
|| _hovered->has_same_domain(_focused))
_hovered->submit_input_event(ev);
}
} else if (_input_receiver)
@ -225,9 +234,9 @@ void User_state::handle_event(Input::Event ev)
*/
if ((type == Event::PRESS) && _input_receiver) {
if (!_mouse_button(ev.keycode())
|| (_pointed_session
&& (_pointed_session->has_focusable_domain()
|| _pointed_session->has_same_domain(Mode::focused_session()))))
|| (_hovered
&& (_hovered->has_focusable_domain()
|| _hovered->has_same_domain(_focused))))
_input_receiver->submit_input_event(ev);
else
_input_receiver = nullptr;
@ -245,46 +254,182 @@ void User_state::handle_event(Input::Event ev)
/*
* Detect end of global key sequence
*/
if (ev.type() == Event::RELEASE && (Mode::key_cnt() == 0) && _global_key_sequence) {
_input_receiver = Mode::focused_session();
update_all_guard.update = true;
if (ev.type() == Event::RELEASE && (_key_cnt == 0) && _global_key_sequence) {
_input_receiver = _focused;
_global_key_sequence = false;
}
}
void User_state::report_keystate(Genode::Xml_generator &xml)
User_state::Handle_input_result
User_state::handle_input_events(Input::Event const * const ev_buf,
unsigned const num_ev)
{
xml.attribute("count", Mode::key_cnt());
Point const old_pointer_pos = _pointer_pos;
View_owner * const old_hovered = _hovered;
View_owner const * const old_focused = _focused;
View_owner const * const old_input_receiver = _input_receiver;
bool user_active = false;
if (num_ev > 0) {
/*
* Take events from input event buffer, merge consecutive motion
* events, and pass result to the user state.
*/
for (unsigned src_ev_cnt = 0; src_ev_cnt < num_ev; src_ev_cnt++) {
Input::Event const *e = &ev_buf[src_ev_cnt];
Input::Event curr = *e;
if (e->type() == Input::Event::MOTION) {
unsigned n = num_consecutive_events(e, num_ev - src_ev_cnt);
curr = merge_motion_events(e, n);
/* skip merged events */
src_ev_cnt += n - 1;
}
/*
* If subsequential relative motion events are merged to
* a zero-motion event, drop it. Otherwise, it would be
* misinterpreted as absolute event pointing to (0, 0).
*/
if (e->relative_motion() && curr.rx() == 0 && curr.ry() == 0)
continue;
/*
* If we detect a pressed key sometime during the event processing,
* we regard the user as active. This check captures the presence
* of press-release combinations within one batch of input events.
*/
user_active |= _key_pressed();
/* pass event to user state */
_handle_input_event(curr);
}
} else {
/*
* Besides handling input events, 'user_state.handle_event()' also
* updates the pointed session, which might have changed by other
* means, for example view movement.
*/
_handle_input_event(Input::Event());
}
/*
* If at least one key is kept pressed, we regard the user as active.
*/
user_active |= _key_pressed();
bool key_state_affected = false;
for (unsigned i = 0; i < num_ev; i++)
key_state_affected |= (ev_buf[i].type() == Input::Event::PRESS) ||
(ev_buf[i].type() == Input::Event::RELEASE);
_apply_pending_focus_change();
return {
.pointer_position_changed = _pointer_pos != old_pointer_pos,
.hover_changed = _hovered != old_hovered,
.focus_changed = (_focused != old_focused) ||
(_input_receiver != old_input_receiver),
.key_state_affected = key_state_affected,
.user_active = user_active,
.key_pressed = _key_pressed()
};
}
void User_state::report_keystate(Xml_generator &xml) const
{
xml.attribute("count", _key_cnt);
_key_array.report_state(xml);
}
/********************
** Mode interface **
********************/
void User_state::forget(::Session const &session)
void User_state::report_pointer_position(Xml_generator &xml) const
{
Mode::forget(session);
xml.attribute("xpos", _pointer_pos.x());
xml.attribute("ypos", _pointer_pos.y());
}
if (_pointed_session == &session) {
View * const pointed_view = find_view(_pointer_pos);
_pointed_session = pointed_view ? &pointed_view->session() : nullptr;
void User_state::report_hovered_view_owner(Xml_generator &xml) const
{
if (_hovered)
_hovered->report(xml);
}
void User_state::report_focused_view_owner(Xml_generator &xml, bool active) const
{
if (_focused) {
_focused->report(xml);
if (active) xml.attribute("active", "yes");
}
}
void User_state::forget(View_owner const &owner)
{
_focus.forget(owner);
if (&owner == _focused) _focused = nullptr;
if (&owner == _next_focused) _next_focused = nullptr;
if (_hovered == &owner) {
View_component * const pointed_view = _view_stack.find_view(_pointer_pos);
_hovered = pointed_view ? &pointed_view->owner() : nullptr;
}
if (_input_receiver == &session)
if (_input_receiver == &owner)
_input_receiver = nullptr;
}
void User_state::focused_session(::Session *session)
bool User_state::_focus_change_permitted(View_owner const &caller) const
{
Mode::focused_session(session);
/*
* If no session is focused, we allow any client to assign it. This
* is useful for programs such as an initial login window that
* should receive input events without prior manual selection via
* the mouse.
*
* In principle, a client could steal the focus during time between
* a currently focused session gets closed and before the user
* manually picks a new session. However, in practice, the focus
* policy during application startup and exit is managed by a
* window manager that sits between nitpicker and the application.
*/
if (!_focused)
return true;
/*
* Check if the currently focused session label belongs to a
* session subordinated to the caller, i.e., it originated from
* a child of the caller or from the same process. This is the
* case if the first part of the focused session label is
* identical to the caller's label.
*/
char const * const focused_label = _focused->label().string();
char const * const caller_label = caller.label().string();
return strcmp(focused_label, caller_label, strlen(caller_label)) == 0;
}
void User_state::_focus_view_owner_via_click(View_owner &owner)
{
_focus.assign(owner);
_focused = &owner;
_next_focused = &owner;
_focused = &owner;
_focus.assign(owner);
if (!_global_key_sequence)
_input_receiver = session;
_input_receiver = &owner;
}

View File

@ -20,35 +20,61 @@
#include <util/xml_generator.h>
#include "mode.h"
#include "focus.h"
#include "view_stack.h"
#include "global_keys.h"
class User_state : public Mode, public View_stack
namespace Nitpicker { class User_state; }
class Nitpicker::User_state : public Focus_controller
{
private:
/*
* Number of currently pressed keys. This counter is used to determine
* if the user is dragging an item.
*/
unsigned _key_cnt = 0;
View_owner *_focused = nullptr;
View_owner *_next_focused = nullptr;
/*
* True while a global key sequence is processed
*/
bool _global_key_sequence = false;
/**
* Input-focus information propagated to the view stack
*/
Focus &_focus;
/**
* Policy for the routing of global keys
*/
Global_keys &_global_keys;
/**
* View stack, used to determine the hovered view and pointer boundary
*/
View_stack &_view_stack;
/*
* Current pointer position
*/
Point _pointer_pos;
/*
* Currently pointed-at session
* Currently pointed-at view owner
*/
Session *_pointed_session = nullptr;
View_owner *_hovered = nullptr;
/*
* Session that receives the current stream of input events
*/
Session *_input_receiver = nullptr;
void _update_all();
View_owner *_input_receiver = nullptr;
/**
* Array for tracking the state of each key
@ -80,43 +106,98 @@ class User_state : public Mode, public View_stack
} _key_array;
bool _focus_change_permitted(View_owner const &caller) const;
void _focus_view_owner_via_click(View_owner &);
void _handle_input_event(Input::Event);
bool _key_pressed() const { return _key_cnt > 0; }
/**
* Apply pending focus-change request that was issued during drag state
*/
void _apply_pending_focus_change()
{
/*
* Defer focus changes to a point where no drag operation is in
* flight because otherwise, the involved sessions would obtain
* inconsistent press and release events. However, focus changes
* during global key sequences are fine.
*/
if (_key_pressed() && !_global_key_sequence)
return;
if (_focused != _next_focused)
_focused = _next_focused;
}
public:
/**
* Constructor
*/
User_state(Global_keys &, Area view_stack_size);
/**
* Handle input event
*
* This function controls the Nitpicker mode and the user state
* variables.
* \param focus exported focus information, to be consumed by the
* view stack to tailor its view drawing operations
*/
void handle_event(Input::Event ev);
void report_keystate(Genode::Xml_generator &);
/**
* Accessors
*/
Point pointer_pos() { return _pointer_pos; }
Session *pointed_session() { return _pointed_session; }
User_state(Focus &focus, Global_keys &global_keys, View_stack &view_stack)
:
_focus(focus), _global_keys(global_keys), _view_stack(view_stack)
{ }
/**
* (Re-)apply origin policy to all views
*/
void apply_origin_policy(View &pointer_origin)
/********************************
** Focus_controller interface **
********************************/
void focus_view_owner(View_owner const &caller,
View_owner &next_focused) override
{
View_stack::apply_origin_policy(pointer_origin);
/* check permission by comparing session labels */
if (!_focus_change_permitted(caller)) {
warning("unauthorized focus change requesed by ", caller.label());
return;
}
/*
* To avoid changing the focus in the middle of a drag operation,
* we cannot perform the focus change immediately. Instead, it
* comes into effect via the 'apply_pending_focus_change()' method
* called the next time when the user input is handled and no drag
* operation is in flight.
*/
_next_focused = &next_focused;
}
/****************************************
** Interface used by the main program **
****************************************/
struct Handle_input_result
{
bool const pointer_position_changed;
bool const hover_changed;
bool const focus_changed;
bool const key_state_affected;
bool const user_active;
bool const key_pressed;
};
Handle_input_result handle_input_events(Input::Event const *ev_buf,
unsigned num_ev);
/**
* Mode interface
* Discard all references to specified view owner
*/
void forget(Session const &) override;
void focused_session(Session *) override;
void forget(View_owner const &);
void report_keystate(Xml_generator &) const;
void report_pointer_position(Xml_generator &) const;
void report_hovered_view_owner(Xml_generator &) const;
void report_focused_view_owner(Xml_generator &, bool user_active) const;
Point pointer_pos() { return _pointer_pos; }
};
#endif
#endif /* _USER_STATE_H_ */

View File

@ -1,146 +0,0 @@
/*
* \brief Nitpicker view implementation
* \author Norman Feske
* \date 2006-08-09
*/
/*
* Copyright (C) 2006-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include <os/pixel_rgb565.h>
#include <nitpicker_gfx/texture_painter.h>
#include <nitpicker_gfx/box_painter.h>
#include "view.h"
#include "clip_guard.h"
#include "session.h"
#include "draw_label.h"
/***************
** Utilities **
***************/
/**
* Draw rectangle
*/
static void draw_rect(Canvas_base &canvas, int x, int y, int w, int h,
Color color)
{
canvas.draw_box(Rect(Point(x, y), Area(w, 1)), color);
canvas.draw_box(Rect(Point(x, y), Area(1, h)), color);
canvas.draw_box(Rect(Point(x + w - 1, y), Area(1, h)), color);
canvas.draw_box(Rect(Point(x, y + h - 1), Area(w, 1)), color);
}
/**
* Draw outlined frame with black outline color
*/
static void draw_frame(Canvas_base &canvas, Rect r, Color color,
int frame_size)
{
/* draw frame around the view */
int d = frame_size;
draw_rect(canvas, r.x1() - d, r.y1() - d, r.w() + 2*d, r.h() + 2*d, BLACK);
while (--d > 1)
draw_rect(canvas, r.x1() - d, r.y1() - d, r.w() + 2*d, r.h() + 2*d, color);
draw_rect(canvas, r.x1() - d, r.y1() - d, r.w() + 2*d, r.h() + 2*d, BLACK);
}
/**********
** View **
**********/
void View::title(const char *title)
{
Genode::strncpy(_title, title, TITLE_LEN);
/* calculate label size, the position is defined by the view stack */
_label_rect = Rect(Point(0, 0), label_size(_session.label().string(), _title));
}
void View::frame(Canvas_base &canvas, Mode const &mode) const
{
if (!_session.label_visible())
return;
Rect const geometry = abs_geometry();
draw_frame(canvas, geometry, _session.color(), frame_size(mode));
}
/**
* Return texture painter mode depending on nitpicker state and session policy
*/
static Texture_painter::Mode
texture_painter_mode(Mode const &mode, Session const &session)
{
/*
* Tint view unless it belongs to a domain that is explicitly configured to
* display the raw client content or if belongs to the focused domain.
*/
if (session.content_client() || session.has_same_domain(mode.focused_session()))
return Texture_painter::SOLID;
return Texture_painter::MIXED;
}
void View::draw(Canvas_base &canvas, Mode const &mode) const
{
Texture_painter::Mode const op = texture_painter_mode(mode, _session);
Rect const view_rect = abs_geometry();
/*
* The view content and label should never overdraw the
* frame of the view in non-flat Nitpicker modes. The frame
* is located outside the view area. By shrinking the
* clipping area to the view area, we protect the frame.
*/
Clip_guard clip_guard(canvas, view_rect);
/*
* If the clipping area shrinked to zero, we do not process drawing
* operations.
*/
if (!canvas.clip().valid() || !&_session) return;
if (tmp_fb) {
for (unsigned i = 0; i < 2; i++) {
canvas.draw_box(view_rect, Color(i*8,i*24,i*16*8));
tmp_fb->refresh(0,0,1024,768);
}
}
/* allow alpha blending only if the raw client content is enabled */
bool allow_alpha = _session.content_client();
/* draw view content */
Color const mix_color = Color(_session.color().r >> 1,
_session.color().g >> 1,
_session.color().b >> 1);
if (_session.texture()) {
canvas.draw_texture(_buffer_off + view_rect.p1(), *_session.texture(),
op, mix_color, allow_alpha);
} else {
canvas.draw_box(view_rect, BLACK);
}
if (!_session.label_visible()) return;
/* draw label */
Color const frame_color = _session.color();
draw_label(canvas, _label_rect.p1(), _session.label().string(), WHITE,
_title, frame_color);
}

View File

@ -1,302 +0,0 @@
/*
* \brief Nitpicker view interface
* \author Norman Feske
* \date 2006-08-08
*/
/*
* Copyright (C) 2006-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _VIEW_H_
#define _VIEW_H_
/* Genode includes */
#include <util/string.h>
#include <util/list.h>
#include <util/dirty_rect.h>
#include <base/weak_ptr.h>
#include <base/rpc_server.h>
/* local includes */
#include "mode.h"
#include "session.h"
class Buffer;
#include <framebuffer_session/framebuffer_session.h>
extern Framebuffer::Session *tmp_fb;
typedef Genode::Dirty_rect<Rect, 3> Dirty_rect;
/*
* For each buffer, there is a list of views that belong to this buffer.
*/
struct Same_buffer_list_elem : Genode::List<Same_buffer_list_elem>::Element { };
/*
* The view stack holds a list of all visible view in stacking order.
*/
struct View_stack_elem : Genode::List<View_stack_elem>::Element { };
/*
* Each session maintains a list of views owned by the session.
*/
struct Session_view_list_elem : Genode::List<Session_view_list_elem>::Element { };
/*
* If a view has a parent, it is a list element of its parent view
*/
struct View_parent_elem : Genode::List<View_parent_elem>::Element { };
namespace Nitpicker { class View; }
/*
* We use view capabilities as mere tokens to pass views between sessions.
* There is no RPC interface associated with a view.
*/
struct Nitpicker::View { GENODE_RPC_INTERFACE(); };
class View : public Same_buffer_list_elem,
public Session_view_list_elem,
public View_stack_elem,
public View_parent_elem,
public Genode::Weak_object<View>,
public Genode::Rpc_object<Nitpicker::View>
{
public:
enum { TITLE_LEN = 32 }; /* max.characters of a title */
enum Transparent { NOT_TRANSPARENT = 0, TRANSPARENT = 1 };
enum Background { NOT_BACKGROUND = 0, BACKGROUND = 1 };
private:
Transparent const _transparent; /* background is partly visible */
Background _background; /* view is a background view */
View *_parent; /* parent view */
Rect _geometry; /* position and size relative to parent */
Rect _label_rect; /* position and size of label */
Point _buffer_off; /* offset to the visible buffer area */
Session &_session; /* session that created the view */
char _title[TITLE_LEN];
Dirty_rect _dirty_rect;
Genode::List<View_parent_elem> _children;
/**
* Assign new parent
*
* Normally, the parent of a view is defined at the construction time
* of the view. However, if the domain origin changes at runtime, we
* need to dynamically re-assign the pointer origin as the parent.
*/
void _assign_parent(View *parent)
{
if (_parent == parent)
return;
if (_parent)
_parent->remove_child(*this);
_parent = parent;
if (_parent)
_parent->add_child(*this);
}
public:
View(Session &session, Transparent transparent,
Background bg, View *parent)
:
_transparent(transparent), _background(bg),
_parent(parent), _session(session)
{ title(""); }
virtual ~View()
{
/* invalidate weak pointers to this object */
lock_for_destruction();
/* break link to our parent */
if (_parent)
_parent->remove_child(*this);
/* break links to our children */
while (View_parent_elem *e = _children.first()) {
static_cast<View *>(e)->dissolve_from_parent();
_children.remove(e);
}
}
/**
* Return absolute view position
*/
Point abs_position() const
{
return _parent ? _geometry.p1() + _parent->abs_position()
: _geometry.p1();
}
/**
* Return absolute view geometry
*/
Rect abs_geometry() const
{
return Rect(abs_position(), _geometry.area());
}
/**
* Break the relationship of a child view from its parent
*
* This function is called when a parent view gets destroyed.
*/
void dissolve_from_parent()
{
_parent = 0;
_geometry = Rect();
}
bool has_parent(View const &parent) const { return &parent == _parent; }
void apply_origin_policy(View &pointer_origin)
{
if (session().origin_pointer() && !has_parent(pointer_origin))
_assign_parent(&pointer_origin);
if (!session().origin_pointer() && has_parent(pointer_origin))
_assign_parent(0);
}
Rect geometry() const { return _geometry; }
void geometry(Rect geometry) { _geometry = geometry; }
void add_child(View const &child) { _children.insert(&child); }
void remove_child(View const &child) { _children.remove(&child); }
template <typename FN>
void for_each_child(FN const &fn) {
for (View_parent_elem *e = _children.first(); e; e = e->next())
fn(*static_cast<View *>(e));
}
template <typename FN>
void for_each_const_child(FN const &fn) const {
for (View_parent_elem const *e = _children.first(); e; e = e->next())
fn(*static_cast<View const *>(e));
}
/**
* Return thickness of frame that surrounds the view
*/
virtual int frame_size(Mode const &mode) const
{
if (!_session.label_visible()) return 0;
return mode.focused(_session) ? 5 : 3;
}
/**
* Draw view-surrounding frame on canvas
*/
virtual void frame(Canvas_base &canvas, Mode const &mode) const;
/**
* Draw view on canvas
*/
virtual void draw(Canvas_base &canvas, Mode const &mode) const;
/**
* Set view title
*/
void title(const char *title);
/**
* Return successor in view stack
*/
View const *view_stack_next() const {
return static_cast<View const *>(View_stack_elem::next()); }
View *view_stack_next() {
return static_cast<View *>(View_stack_elem::next()); }
/**
* Set view as background
*
* \param bg true if view is background
*/
void background(bool bg) {
_background = bg ? BACKGROUND : NOT_BACKGROUND; }
/**
* Accessors
*/
Session &session() const { return _session; }
bool belongs_to(Session const &session) const { return &session == &_session; }
bool same_session_as(View const &other) const { return &_session == &other._session; }
bool top_level() const { return _parent == 0; }
bool transparent() const { return _transparent || _session.uses_alpha(); }
bool background() const { return _background; }
Rect label_rect() const { return _label_rect; }
bool uses_alpha() const { return _session.uses_alpha(); }
Point buffer_off() const { return _buffer_off; }
char const *title() const { return _title; }
void buffer_off(Point buffer_off) { _buffer_off = buffer_off; }
void label_pos(Point pos) { _label_rect = Rect(pos, _label_rect.area()); }
/**
* Return true if input at screen position 'p' refers to the view
*/
bool input_response_at(Point p, Mode const &mode) const
{
Rect const view_rect = abs_geometry();
/* check if point lies outside view geometry */
if ((p.x() < view_rect.x1()) || (p.x() > view_rect.x2())
|| (p.y() < view_rect.y1()) || (p.y() > view_rect.y2()))
return false;
/* if view uses an alpha channel, check the input mask */
if (_session.content_client() && session().uses_alpha())
return session().input_mask_at(p - view_rect.p1() - _buffer_off);
return true;
}
/**
* Mark part of view as dirty
*
* \param rect dirty rectangle in absolute coordinates
*/
void mark_as_dirty(Rect rect) { _dirty_rect.mark_as_dirty(rect); }
/**
* Return dirty-rectangle information
*/
Dirty_rect dirty_rect() const { return _dirty_rect; }
/**
* Reset dirty rectangle
*/
void mark_as_clean() { _dirty_rect = Dirty_rect(); }
};
#endif /* _VIEW_H_ */

View File

@ -0,0 +1,190 @@
/*
* \brief Nitpicker view implementation
* \author Norman Feske
* \date 2006-08-09
*/
/*
* Copyright (C) 2006-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include <os/pixel_rgb565.h>
#include <nitpicker_gfx/texture_painter.h>
#include <nitpicker_gfx/box_painter.h>
#include "view_component.h"
#include "clip_guard.h"
#include "session_component.h"
#include "draw_label.h"
/***************
** Utilities **
***************/
namespace Nitpicker {
/**
* Draw rectangle
*/
static void draw_rect(Canvas_base &canvas, int x, int y, int w, int h,
Color color)
{
canvas.draw_box(Rect(Point(x, y), Area(w, 1)), color);
canvas.draw_box(Rect(Point(x, y), Area(1, h)), color);
canvas.draw_box(Rect(Point(x + w - 1, y), Area(1, h)), color);
canvas.draw_box(Rect(Point(x, y + h - 1), Area(w, 1)), color);
}
/**
* Draw outlined frame with black outline color
*/
static void draw_frame(Canvas_base &canvas, Rect r, Color color,
int frame_size)
{
/* draw frame around the view */
int d = frame_size;
draw_rect(canvas, r.x1() - d, r.y1() - d, r.w() + 2*d, r.h() + 2*d, black());
while (--d > 1)
draw_rect(canvas, r.x1() - d, r.y1() - d, r.w() + 2*d, r.h() + 2*d, color);
draw_rect(canvas, r.x1() - d, r.y1() - d, r.w() + 2*d, r.h() + 2*d, black());
}
/**
* Return texture painter mode depending on nitpicker state and session policy
*/
static Texture_painter::Mode
texture_painter_mode(Focus const &focus, View_owner const &owner)
{
/*
* Tint view unless it belongs to a domain that is explicitly configured to
* display the raw client content or if belongs to the focused domain.
*/
if (owner.content_client() || focus.same_domain_as_focused(owner))
return Texture_painter::SOLID;
return Texture_painter::MIXED;
}
}
using namespace Nitpicker;
void View_component::title(Title const &title)
{
_title = title;
/* calculate label size, the position is defined by the view stack */
_label_rect = Rect(Point(0, 0), label_size(_owner.label().string(),
_title.string()));
}
void View_component::frame(Canvas_base &canvas, Focus const &focus) const
{
if (!_owner.label_visible())
return;
Rect const geometry = abs_geometry();
draw_frame(canvas, geometry, _owner.color(), frame_size(focus));
}
void View_component::draw(Canvas_base &canvas, Focus const &focus) const
{
Texture_painter::Mode const op = texture_painter_mode(focus, _owner);
Rect const view_rect = abs_geometry();
/*
* The view content and label should never overdraw the
* frame of the view in non-flat Nitpicker modes. The frame
* is located outside the view area. By shrinking the
* clipping area to the view area, we protect the frame.
*/
Clip_guard clip_guard(canvas, view_rect);
/*
* If the clipping area shrinked to zero, we do not process drawing
* operations.
*/
if (!canvas.clip().valid() || !&_owner) return;
/* allow alpha blending only if the raw client content is enabled */
bool allow_alpha = _owner.content_client();
/* draw view content */
Color const owner_color = _owner.color();
Color const mix_color = Color(owner_color.r >> 1,
owner_color.g >> 1,
owner_color.b >> 1);
Texture_base const *texture = _owner.texture();
if (texture) {
canvas.draw_texture(_buffer_off + view_rect.p1(), *texture, op,
mix_color, allow_alpha);
} else {
canvas.draw_box(view_rect, black());
}
if (!_owner.label_visible()) return;
/* draw label */
Color const frame_color = owner_color;
draw_label(canvas, _label_rect.p1(), _owner.label().string(), white(),
_title.string(), frame_color);
}
void View_component::apply_origin_policy(View_component &pointer_origin)
{
if (owner().origin_pointer() && !has_parent(pointer_origin))
_assign_parent(&pointer_origin);
if (!owner().origin_pointer() && has_parent(pointer_origin))
_assign_parent(0);
}
bool View_component::input_response_at(Point p) const
{
Rect const view_rect = abs_geometry();
/* check if point lies outside view geometry */
if ((p.x() < view_rect.x1()) || (p.x() > view_rect.x2())
|| (p.y() < view_rect.y1()) || (p.y() > view_rect.y2()))
return false;
/* if view uses an alpha channel, check the input mask */
if (_owner.content_client() && _owner.uses_alpha())
return _owner.input_mask_at(p - view_rect.p1() - _buffer_off);
return true;
}
int View_component::frame_size(Focus const &focus) const
{
if (!_owner.label_visible()) return 0;
return focus.focused(_owner) ? 5 : 3;
}
bool View_component::transparent() const
{
return _transparent || _owner.uses_alpha();
}
bool View_component::uses_alpha() const
{
return _owner.uses_alpha();
}

View File

@ -0,0 +1,283 @@
/*
* \brief Nitpicker view interface
* \author Norman Feske
* \date 2006-08-08
*/
/*
* Copyright (C) 2006-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _VIEW_H_
#define _VIEW_H_
/* Genode includes */
#include <util/string.h>
#include <util/list.h>
#include <util/dirty_rect.h>
#include <base/weak_ptr.h>
#include <base/rpc_server.h>
/* local includes */
#include "canvas.h"
#include "view_owner.h"
namespace Nitpicker {
class Buffer;
class Focus;
typedef Dirty_rect<Rect, 3> Dirty_rect;
/*
* For each buffer, there is a list of views that belong to this buffer.
*/
struct Same_buffer_list_elem : List<Same_buffer_list_elem>::Element { };
/*
* The view stack holds a list of all visible view in stacking order.
*/
struct View_stack_elem : List<View_stack_elem>::Element { };
/*
* If a view has a parent, it is a list element of its parent view
*/
struct View_parent_elem : List<View_parent_elem>::Element { };
/*
* Each session maintains a list of views owned by the session.
*/
struct Session_view_list_elem : List<Session_view_list_elem>::Element { };
/*
* We use view capabilities as mere tokens to pass views between sessions.
* There is no RPC interface associated with a view.
*/
struct View { GENODE_RPC_INTERFACE(); };
class View_component;
}
class Nitpicker::View_component : public Same_buffer_list_elem,
public Session_view_list_elem,
public View_stack_elem,
public View_parent_elem,
public Weak_object<View_component>,
public Rpc_object<View>
{
public:
typedef String<32> Title;
enum Transparent { NOT_TRANSPARENT = 0, TRANSPARENT = 1 };
enum Background { NOT_BACKGROUND = 0, BACKGROUND = 1 };
private:
Transparent const _transparent; /* background is partly visible */
Background _background; /* view is a background view */
View_component *_parent; /* parent view */
Rect _geometry; /* position and size relative to parent */
Rect _label_rect; /* position and size of label */
Point _buffer_off; /* offset to the visible buffer area */
View_owner &_owner;
Title _title;
Dirty_rect _dirty_rect;
List<View_parent_elem> _children;
/**
* Assign new parent
*
* Normally, the parent of a view is defined at the construction time
* of the view. However, if the domain origin changes at runtime, we
* need to dynamically re-assign the pointer origin as the parent.
*/
void _assign_parent(View_component *parent)
{
if (_parent == parent)
return;
if (_parent)
_parent->remove_child(*this);
_parent = parent;
if (_parent)
_parent->add_child(*this);
}
public:
View_component(View_owner &owner, Transparent transparent,
Background bg, View_component *parent)
:
_transparent(transparent), _background(bg), _parent(parent),
_owner(owner)
{
title(""); /* initialize '_label_rect' */
}
virtual ~View_component()
{
/* invalidate weak pointers to this object */
lock_for_destruction();
/* break link to our parent */
if (_parent)
_parent->remove_child(*this);
/* break links to our children */
while (View_parent_elem *e = _children.first()) {
static_cast<View_component *>(e)->dissolve_from_parent();
_children.remove(e);
}
}
/**
* Return absolute view position
*/
Point abs_position() const
{
return _parent ? _geometry.p1() + _parent->abs_position()
: _geometry.p1();
}
/**
* Return absolute view geometry
*/
Rect abs_geometry() const
{
return Rect(abs_position(), _geometry.area());
}
/**
* Break the relationship of a child view from its parent
*
* This function is called when a parent view gets destroyed.
*/
void dissolve_from_parent()
{
_parent = 0;
_geometry = Rect();
}
bool has_parent(View_component const &parent) const { return &parent == _parent; }
void apply_origin_policy(View_component &pointer_origin);
Rect geometry() const { return _geometry; }
void geometry(Rect geometry) { _geometry = geometry; }
void add_child(View_component const &child) { _children.insert(&child); }
void remove_child(View_component const &child) { _children.remove(&child); }
template <typename FN>
void for_each_child(FN const &fn) {
for (View_parent_elem *e = _children.first(); e; e = e->next())
fn(*static_cast<View_component *>(e));
}
template <typename FN>
void for_each_const_child(FN const &fn) const {
for (View_parent_elem const *e = _children.first(); e; e = e->next())
fn(*static_cast<View_component const *>(e));
}
/**
* Return thickness of frame that surrounds the view
*/
virtual int frame_size(Focus const &) const;
/**
* Draw view-surrounding frame on canvas
*/
virtual void frame(Canvas_base &canvas, Focus const &) const;
/**
* Draw view on canvas
*/
virtual void draw(Canvas_base &canvas, Focus const &) const;
/**
* Set view title
*/
void title(Title const &title);
/**
* Return successor in view stack
*/
View_component const *view_stack_next() const {
return static_cast<View_component const *>(View_stack_elem::next()); }
View_component *view_stack_next() {
return static_cast<View_component *>(View_stack_elem::next()); }
/**
* Set view as background
*
* \param bg true if view is background
*/
void background(bool bg) {
_background = bg ? BACKGROUND : NOT_BACKGROUND; }
/**
* Accessors
*/
View_owner &owner() const { return _owner; }
bool owned_by(View_owner const &owner) const
{
return &owner == &_owner;
}
bool same_owner_as(View_component const &other) const
{
return &_owner == &other._owner;
}
bool top_level() const { return _parent == 0; }
bool transparent() const;
bool background() const { return _background; }
Rect label_rect() const { return _label_rect; }
bool uses_alpha() const;
Point buffer_off() const { return _buffer_off; }
template <typename FN>
void with_title(FN const &fn) const { fn(_title); }
void buffer_off(Point buffer_off) { _buffer_off = buffer_off; }
void label_pos(Point pos) { _label_rect = Rect(pos, _label_rect.area()); }
/**
* Return true if input at screen position 'p' refers to the view
*/
bool input_response_at(Point p) const;
/**
* Mark part of view as dirty
*
* \param rect dirty rectangle in absolute coordinates
*/
void mark_as_dirty(Rect rect) { _dirty_rect.mark_as_dirty(rect); }
/**
* Return dirty-rectangle information
*/
Dirty_rect dirty_rect() const { return _dirty_rect; }
/**
* Reset dirty rectangle
*/
void mark_as_clean() { _dirty_rect = Dirty_rect(); }
};
#endif /* _VIEW_H_ */

View File

@ -0,0 +1,98 @@
/*
* \brief Representation of a view owner
* \author Norman Feske
* \date 2017-11-16
*
* The view owner defines the policy when drawing or interacting with a
* view. Except for the background and pointer-origin views that are
* owned by nitpicker, the view owner corresponds to the session that
* created the view.
*/
/*
* Copyright (C) 2006-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _VIEW_OWNER_H_
#define _VIEW_OWNER_H_
/* Genode includes */
#include <util/xml_generator.h>
#include <input/event.h>
#include <nitpicker_session/nitpicker_session.h>
#include <os/texture.h>
/* local includes */
#include "types.h"
namespace Nitpicker { class View_owner; }
struct Nitpicker::View_owner
{
/**
* Return the owner's session label
*/
virtual Session::Label label() const { return Session::Label(""); }
virtual bool matches_session_label(Session::Label const &) const { return false; }
virtual bool visible() const { return true; }
virtual bool label_visible() const { return false; }
virtual bool has_same_domain(View_owner const *) const { return false; }
virtual bool has_focusable_domain() const { return false; }
virtual bool has_transient_focusable_domain() const { return false; }
virtual Color color() const { return black(); }
virtual bool content_client() const { return true; }
virtual bool hover_always() const { return false; }
/**
* Return true if owner uses an alpha channel
*/
virtual bool uses_alpha() const { return false; }
/**
* Return layer assigned to the owner's domain
*/
virtual unsigned layer() const { return ~0U; }
/**
* Return true if the owner uses the pointer as coordinate origin
*/
virtual bool origin_pointer() const { return false; }
/**
* Return the owner's designated background view
*/
virtual View const *background() const { return nullptr; }
/**
* Teturn texture containing the owners virtual frame buffer
*/
virtual Texture_base const *texture() const { return nullptr; }
/**
* Return input-mask value at given position
*/
virtual unsigned char input_mask_at(Point p) const { return 0; }
virtual void submit_input_event(Input::Event ev) { }
/**
* Produce report with the owner's information
*/
virtual void report(Xml_generator &xml) const { }
};
#endif /* _VIEW_OWNER_H_ */

View File

@ -14,19 +14,12 @@
#include "view_stack.h"
#include "clip_guard.h"
using namespace Nitpicker;
/**************************
** View stack interface **
**************************/
template <typename VIEW>
VIEW *View_stack::_next_view(VIEW &view) const
{
Session * const focused_session = _mode.focused_session();
View * const active_background = focused_session ?
focused_session->background() : 0;
for (VIEW *next_view = &view; ;) {
next_view = next_view->view_stack_next();
@ -34,11 +27,11 @@ VIEW *View_stack::_next_view(VIEW &view) const
/* check if we hit the bottom of the view stack */
if (!next_view) return 0;
if (!next_view->session().visible()) continue;
if (!next_view->owner().visible()) continue;
if (!next_view->background()) return next_view;
if (is_default_background(*next_view) || next_view == active_background)
if (is_default_background(*next_view) || _focus.focused_background(*next_view))
return next_view;
/* view is a background view belonging to a non-focused session */
@ -47,19 +40,19 @@ VIEW *View_stack::_next_view(VIEW &view) const
}
Rect View_stack::_outline(View const &view) const
Nitpicker::Rect View_stack::_outline(View_component const &view) const
{
Rect const rect = view.abs_geometry();
/* request thickness of view frame */
int const frame_size = view.frame_size(_mode);
int const frame_size = view.frame_size(_focus);
return Rect(Point(rect.x1() - frame_size, rect.y1() - frame_size),
Point(rect.x2() + frame_size, rect.y2() + frame_size));
}
View const *View_stack::_target_stack_position(View const *neighbor, bool behind)
View_component const *View_stack::_target_stack_position(View_component const *neighbor, bool behind)
{
if (behind) {
@ -67,7 +60,7 @@ View const *View_stack::_target_stack_position(View const *neighbor, bool behind
return nullptr;
/* find target position behind neighbor */
for (View const *cv = _first_view(); cv; cv = _next_view(*cv))
for (View_component const *cv = _first_view(); cv; cv = _next_view(*cv))
if (cv == neighbor)
return cv;
@ -77,7 +70,7 @@ View const *View_stack::_target_stack_position(View const *neighbor, bool behind
return nullptr;
/* find target position in front of neighbor */
for (View const *cv = _first_view(), *next = nullptr; cv; cv = next) {
for (View_component const *cv = _first_view(), *next = nullptr; cv; cv = next) {
next = _next_view(*cv);
if (!next || next == neighbor || next->background())
@ -90,7 +83,8 @@ View const *View_stack::_target_stack_position(View const *neighbor, bool behind
}
void View_stack::_optimize_label_rec(View const *cv, View const *lv, Rect rect, Rect *optimal)
void View_stack::_optimize_label_rec(View_component const *cv, View_component const *lv,
Rect rect, Rect *optimal)
{
/* if label already fits in optimized rectangle, we are happy */
if (optimal->fits(lv->label_rect().area()))
@ -142,8 +136,8 @@ void View_stack::_place_labels(Rect rect)
*/
/* ignore mouse cursor */
View const *start = _next_view(*_first_view());
View *view = _next_view(*_first_view());
View_component const *start = _next_view(*_first_view());
View_component *view = _next_view(*_first_view());
for (; view && _next_view(*view); view = _next_view(*view)) {
@ -174,7 +168,7 @@ void View_stack::_place_labels(Rect rect)
}
void View_stack::draw_rec(Canvas_base &canvas, View const *view, Rect rect) const
void View_stack::draw_rec(Canvas_base &canvas, View_component const *view, Rect rect) const
{
Rect clipped;
@ -188,7 +182,7 @@ void View_stack::draw_rec(Canvas_base &canvas, View const *view, Rect rect) cons
Rect top, left, right, bottom;
rect.cut(clipped, &top, &left, &right, &bottom);
View const *next = _next_view(*view);
View_component const *next = _next_view(*view);
/* draw areas at the top/left of the current view */
if (next && top.valid()) draw_rec(canvas, next, top);
@ -203,8 +197,8 @@ void View_stack::draw_rec(Canvas_base &canvas, View const *view, Rect rect) cons
if (view->uses_alpha())
draw_rec(canvas, _next_view(*view), clipped);
view->frame(canvas, _mode);
view->draw(canvas, _mode);
view->frame(canvas, _focus);
view->draw(canvas, _focus);
});
/* draw areas at the bottom/right of the current view */
@ -213,12 +207,12 @@ void View_stack::draw_rec(Canvas_base &canvas, View const *view, Rect rect) cons
}
void View_stack::refresh_view(View &view, Rect const rect)
void View_stack::refresh_view(View_component &view, Rect const rect)
{
/* rectangle constrained to view geometry */
Rect const view_rect = Rect::intersect(rect, _outline(view));
for (View *v = _first_view(); v; v = v->view_stack_next()) {
for (View_component *v = _first_view(); v; v = v->view_stack_next()) {
Rect const intersection = Rect::intersect(view_rect, _outline(*v));
@ -226,13 +220,13 @@ void View_stack::refresh_view(View &view, Rect const rect)
_mark_view_as_dirty(*v, intersection);
}
view.for_each_child([&] (View &child) { refresh_view(child, rect); });
view.for_each_child([&] (View_component &child) { refresh_view(child, rect); });
}
void View_stack::refresh(Rect const rect)
{
for (View *v = _first_view(); v; v = v->view_stack_next()) {
for (View_component *v = _first_view(); v; v = v->view_stack_next()) {
Rect const intersection = Rect::intersect(rect, _outline(*v));
@ -242,7 +236,7 @@ void View_stack::refresh(Rect const rect)
}
void View_stack::geometry(View &view, Rect const rect)
void View_stack::geometry(View_component &view, Rect const rect)
{
Rect const old_outline = _outline(view);
@ -269,7 +263,7 @@ void View_stack::geometry(View &view, Rect const rect)
}
void View_stack::buffer_offset(View &view, Point const buffer_off)
void View_stack::buffer_offset(View_component &view, Point const buffer_off)
{
view.buffer_off(buffer_off);
@ -277,7 +271,7 @@ void View_stack::buffer_offset(View &view, Point const buffer_off)
}
void View_stack::stack(View &view, View const *neighbor, bool behind)
void View_stack::stack(View_component &view, View_component const *neighbor, bool behind)
{
_views.remove(&view);
_views.insert(&view, _target_stack_position(neighbor, behind));
@ -291,7 +285,7 @@ void View_stack::stack(View &view, View const *neighbor, bool behind)
}
void View_stack::title(View &view, const char *title)
void View_stack::title(View_component &view, const char *title)
{
view.title(title);
_place_labels(view.abs_geometry());
@ -300,21 +294,21 @@ void View_stack::title(View &view, const char *title)
}
View *View_stack::find_view(Point p)
View_component *View_stack::find_view(Point p)
{
View *view = _first_view();
View_component *view = _first_view();
for ( ; view; view = _next_view(*view))
if (view->input_response_at(p, _mode))
if (view->input_response_at(p))
return view;
return 0;
return nullptr;
}
void View_stack::remove_view(View const &view, bool redraw)
void View_stack::remove_view(View_component const &view, bool redraw)
{
view.for_each_const_child([&] (View const &child) { remove_view(child); });
view.for_each_const_child([&] (View_component const &child) { remove_view(child); });
/* remember geometry of view to remove */
Rect rect = _outline(view);
@ -339,7 +333,7 @@ void View_stack::sort_views_by_layer()
unsigned lowest_layer = ~0U;
View_stack_elem *lowest_view = nullptr;
for (View_stack_elem *v = _views.first(); v; v = v->next()) {
unsigned const layer = static_cast<View *>(v)->session().layer();
unsigned const layer = static_cast<View_component *>(v)->owner().layer();
if (layer < lowest_layer) {
lowest_layer = layer;
lowest_view = v;
@ -361,3 +355,37 @@ void View_stack::sort_views_by_layer()
/* replace empty source list by newly sorted list */
_views = sorted;
}
void View_stack::to_front(char const *selector)
{
/*
* Move all views that match the selector to the front while
* maintaining their ordering.
*/
View_component *at = nullptr;
for (View_component *v = _first_view(); v; v = v->view_stack_next()) {
if (!v->owner().matches_session_label(selector))
continue;
if (v->background())
continue;
/*
* Move view to behind the previous view that we moved to
* front. If 'v' is the first view that matches the selector,
* move it to the front ('at' argument of 'insert' is 0).
*/
_views.remove(v);
_views.insert(v, at);
at = v;
/* mark view geometry as to be redrawn */
refresh(_outline(*v));
}
/* reestablish domain layering */
sort_views_by_layer();
}

View File

@ -14,21 +14,22 @@
#ifndef _VIEW_STACK_H_
#define _VIEW_STACK_H_
#include "view.h"
#include "view_component.h"
#include "session_component.h"
#include "canvas.h"
class Session;
namespace Nitpicker { class View_stack; }
class View_stack
class Nitpicker::View_stack
{
private:
Area _size;
Mode &_mode;
Genode::List<View_stack_elem> _views;
View *_default_background = nullptr;
Dirty_rect mutable _dirty_rect;
Area _size;
Focus &_focus;
List<View_stack_elem> _views;
View_component *_default_background = nullptr;
Dirty_rect mutable _dirty_rect;
/**
* Return outline geometry of a view
@ -37,24 +38,31 @@ class View_stack
* Nitpicker mode. In non-flat modes, we incorporate the surrounding
* frame.
*/
Rect _outline(View const &view) const;
Rect _outline(View_component const &view) const;
/**
* Return top-most view of the view stack
*/
View const *_first_view_const() const { return static_cast<View const *>(_views.first()); }
View_component const *_first_view() const
{
return static_cast<View_component const *>(_views.first());
}
View *_first_view() { return static_cast<View *>(_views.first()); }
View_component *_first_view()
{
return static_cast<View_component *>(_views.first());
}
/**
* Find position in view stack for inserting a view
*/
View const *_target_stack_position(View const *neighbor, bool behind);
View_component const *_target_stack_position(View_component const *neighbor, bool behind);
/**
* Find best visible label position
*/
void _optimize_label_rec(View const *cv, View const *lv, Rect rect, Rect *optimal);
void _optimize_label_rec(View_component const *cv, View_component const *lv,
Rect rect, Rect *optimal);
/**
* Position labels that are affected by specified area
@ -73,7 +81,7 @@ class View_stack
/**
* Schedule 'rect' to be redrawn
*/
void _mark_view_as_dirty(View &view, Rect rect)
void _mark_view_as_dirty(View_component &view, Rect rect)
{
_dirty_rect.mark_as_dirty(rect);
@ -85,7 +93,7 @@ class View_stack
/**
* Constructor
*/
View_stack(Area size, Mode &mode) : _size(size), _mode(mode)
View_stack(Area size, Focus &focus) : _size(size), _focus(focus)
{
_dirty_rect.mark_as_dirty(Rect(Point(0, 0), _size));
}
@ -107,7 +115,7 @@ class View_stack
*
* \param view current view in view stack
*/
void draw_rec(Canvas_base &, View const *view, Rect) const;
void draw_rec(Canvas_base &, View_component const *view, Rect) const;
/**
* Draw dirty areas
@ -117,7 +125,7 @@ class View_stack
Dirty_rect result = _dirty_rect;
_dirty_rect.flush([&] (Rect const &rect) {
draw_rec(canvas, _first_view_const(), rect); });
draw_rec(canvas, _first_view(), rect); });
return result;
}
@ -132,9 +140,8 @@ class View_stack
_place_labels(whole_screen);
_dirty_rect.mark_as_dirty(whole_screen);
for (View *view = _first_view(); view; view = view->view_stack_next())
for (View_component *view = _first_view(); view; view = view->view_stack_next())
view->mark_as_dirty(_outline(*view));
}
/**
@ -142,7 +149,7 @@ class View_stack
*/
void mark_all_views_as_clean()
{
for (View *view = _first_view(); view; view = view->view_stack_next())
for (View_component *view = _first_view(); view; view = view->view_stack_next())
view->mark_as_clean();
}
@ -152,11 +159,11 @@ class View_stack
* \param Session Session that created the view
* \param Rect Buffer area to update
*/
void mark_session_views_as_dirty(Session const &session, Rect rect)
void mark_session_views_as_dirty(Session_component const &session, Rect rect)
{
for (View *view = _first_view(); view; view = view->view_stack_next()) {
for (View_component *view = _first_view(); view; view = view->view_stack_next()) {
if (!view->belongs_to(session))
if (!view->owned_by(session))
continue;
/*
@ -177,12 +184,12 @@ class View_stack
*
* \param view view that should be updated on screen
*/
void refresh_view(View &view, Rect);
void refresh_view(View_component &view, Rect);
/**
* Refresh entire view
*/
void refresh_view(View &view) { refresh_view(view, _outline(view)); }
void refresh_view(View_component &view) { refresh_view(view, _outline(view)); }
/**
* Refresh area
@ -194,14 +201,14 @@ class View_stack
*
* \param rect new geometry of view on screen
*/
void geometry(View &view, Rect rect);
void geometry(View_component &view, Rect rect);
/**
* Define buffer offset of view
*
* \param buffer_off view offset of displayed buffer
*/
void buffer_offset(View &view, Point buffer_off);
void buffer_offset(View_component &view, Point buffer_off);
/**
* Insert view at specified position in view stack
@ -214,94 +221,49 @@ class View_stack
* bottom of the view stack, specify neighbor = 0 and
* behind = false.
*/
void stack(View &view, View const *neighbor = 0, bool behind = true);
void stack(View_component &view, View_component const *neighbor = 0,
bool behind = true);
/**
* Set view title
*/
void title(View &view, char const *title);
void title(View_component &view, char const *title);
/**
* Find view at specified position
*/
View *find_view(Point);
View_component *find_view(Point);
/**
* Remove view from view stack
*/
void remove_view(View const &, bool redraw = true);
void remove_view(View_component const &, bool redraw = true);
/**
* Define default background
*/
void default_background(View &view) { _default_background = &view; }
void default_background(View_component &view) { _default_background = &view; }
/**
* Return true if view is the default background
*/
bool is_default_background(View const &view) const { return &view == _default_background; }
void apply_origin_policy(View &pointer_origin)
bool is_default_background(View_component const &view) const
{
for (View *v = _first_view(); v; v = v->view_stack_next())
return &view == _default_background;
}
void apply_origin_policy(View_component &pointer_origin)
{
for (View_component *v = _first_view(); v; v = v->view_stack_next())
v->apply_origin_policy(pointer_origin);
}
void sort_views_by_layer();
/**
* Set visibility of views that match the specified label selector
*/
void visible(char const *selector, bool visible)
{
for (View *v = _first_view(); v; v = v->view_stack_next()) {
if (!v->session().matches_session_label(selector))
continue;
/* mark view geometry as to be redrawn */
refresh(_outline(*v));
/* let the view stack omit the views of the session */
v->session().visible(visible);
}
}
/**
* Bring views that match the specified label selector to the front
*/
void to_front(char const *selector)
{
/*
* Move all views that match the selector to the front while
* maintaining their ordering.
*/
View *at = nullptr;
for (View *v = _first_view(); v; v = v->view_stack_next()) {
if (!v->session().matches_session_label(selector))
continue;
if (v->background())
continue;
/*
* Move view to behind the previous view that we moved to
* front. If 'v' is the first view that matches the selector,
* move it to the front ('at' argument of 'insert' is 0).
*/
_views.remove(v);
_views.insert(v, at);
at = v;
/* mark view geometry as to be redrawn */
refresh(_outline(*v));
}
/* reestablish domain layering */
sort_views_by_layer();
}
void to_front(char const *selector);
};
#endif
#endif /* _VIEW_STACK_H_ */