mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-18 10:46:25 +00:00
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:
parent
40c9226bb9
commit
82e2900aa7
@ -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);
|
||||
}
|
||||
};
|
||||
|
75
repos/os/src/server/nitpicker/buffer.h
Normal file
75
repos/os/src/server/nitpicker/buffer.h
Normal 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_ */
|
@ -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:
|
||||
|
||||
|
77
repos/os/src/server/nitpicker/chunky_texture.h
Normal file
77
repos/os/src/server/nitpicker/chunky_texture.h
Normal 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_ */
|
@ -30,7 +30,10 @@
|
||||
|
||||
#include "canvas.h"
|
||||
|
||||
class Clip_guard
|
||||
namespace Nitpicker { class Clip_guard; }
|
||||
|
||||
|
||||
class Nitpicker::Clip_guard
|
||||
{
|
||||
private:
|
||||
|
||||
|
@ -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_ */
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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_ */
|
||||
|
86
repos/os/src/server/nitpicker/focus.h
Normal file
86
repos/os/src/server/nitpicker/focus.h
Normal 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_ */
|
115
repos/os/src/server/nitpicker/framebuffer_session.h
Normal file
115
repos/os/src/server/nitpicker/framebuffer_session.h
Normal 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_ */
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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_ */
|
113
repos/os/src/server/nitpicker/input_session.h
Normal file
113
repos/os/src/server/nitpicker/input_session.h
Normal 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
@ -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
|
@ -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_ */
|
||||
|
@ -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
|
484
repos/os/src/server/nitpicker/session_component.cc
Normal file
484
repos/os/src/server/nitpicker/session_component.cc
Normal 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;
|
||||
}
|
379
repos/os/src/server/nitpicker/session_component.h
Normal file
379
repos/os/src/server/nitpicker/session_component.h
Normal 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_ */
|
@ -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
|
||||
|
42
repos/os/src/server/nitpicker/types.h
Normal file
42
repos/os/src/server/nitpicker/types.h
Normal 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_ */
|
@ -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;
|
||||
}
|
||||
|
@ -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_ */
|
||||
|
@ -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);
|
||||
}
|
@ -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_ */
|
190
repos/os/src/server/nitpicker/view_component.cc
Normal file
190
repos/os/src/server/nitpicker/view_component.cc
Normal 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();
|
||||
}
|
283
repos/os/src/server/nitpicker/view_component.h
Normal file
283
repos/os/src/server/nitpicker/view_component.h
Normal 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_ */
|
98
repos/os/src/server/nitpicker/view_owner.h
Normal file
98
repos/os/src/server/nitpicker/view_owner.h
Normal 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_ */
|
@ -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();
|
||||
}
|
||||
|
@ -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_ */
|
||||
|
Loading…
Reference in New Issue
Block a user