nitpicker: Layers and client-side mouse cursor

This patch introduces a mandatory layer attribute to domains. The layer
ordering is superimposed on the stacking order of the views. The
top-most layer can be assigned to a pointer-managing client. An example
for such a pointer is located at os/src/app/pointer. It replaces the
formerly built-in nitpicker mouse cursor.

The new layering mechanism replaces the former "stay-top" session
argument. So the Nitpicker::Connection no longer takes the stay-top flag
as the first argument.
This commit is contained in:
Norman Feske 2014-07-02 22:00:15 +02:00
parent 6c4c4e5528
commit 1745453315
17 changed files with 233 additions and 168 deletions

View File

@ -34,7 +34,7 @@ class Nitpicker::Connection : public Genode::Connection<Session>,
/**
* Create session and return typed session capability
*/
Session_capability _connect(bool stay_top, char const *label)
Session_capability _connect(char const *label)
{
enum { ARGBUF_SIZE = 128 };
char argbuf[ARGBUF_SIZE];
@ -50,9 +50,6 @@ class Nitpicker::Connection : public Genode::Connection<Session>,
enum { SESSION_METADATA = 36*1024 };
Arg_string::set_arg(argbuf, sizeof(argbuf), "ram_quota", SESSION_METADATA);
if (stay_top)
Arg_string::set_arg(argbuf, sizeof(argbuf), "stay_top", "yes");
return session(argbuf);
}
@ -61,10 +58,10 @@ class Nitpicker::Connection : public Genode::Connection<Session>,
/**
* Constructor
*/
Connection(bool stay_top = false, char const *label = "")
Connection(char const *label = "")
:
/* establish nitpicker session */
Genode::Connection<Session>(_connect(stay_top, label)),
Genode::Connection<Session>(_connect(label)),
Session_client(cap()),
/* request frame-buffer and input sub sessions */

View File

@ -5,7 +5,8 @@
set build_components {
core init
drivers/timer
server/nitpicker server/liquid_framebuffer app/launchpad app/scout
server/nitpicker app/pointer
server/liquid_framebuffer app/launchpad app/scout
test/nitpicker server/nitlog
drivers/framebuffer drivers/pci drivers/input
}
@ -119,8 +120,11 @@ append config {
<resource name="RAM" quantum="1M"/>
<provides><service name="Nitpicker"/></provides>
<config>
<domain name="default" color="#ffffff"/>
<policy label="" domain="default"/>
<domain name="pointer" layer="1" xray="no" origin="pointer" />
<domain name="default" layer="2" />
<policy label="pointer" domain="pointer"/>
<policy label="" domain="default"/>
<global-key name="KEY_SCROLLLOCK" operation="xray" />
<global-key name="KEY_SYSRQ" operation="kill" />
@ -129,6 +133,9 @@ append config {
<global-key name="KEY_F12" operation="xray" />
</config>
</start>
<start name="pointer">
<resource name="RAM" quantum="1M"/>
</start>
<start name="launchpad">
<resource name="RAM" quantum="64M" />
<configfile name="launchpad.config" />
@ -163,7 +170,7 @@ close $launchpad_config_fd
set boot_modules {
core init
timer
nitpicker liquid_fb launchpad scout
nitpicker pointer liquid_fb launchpad scout
testnit nitlog
launchpad.config
}

View File

@ -0,0 +1,66 @@
/*
* \brief Minimalistic nitpicker pointer
* \author Norman Feske
* \date 2014-07-02
*/
/*
* Copyright (C) 2014 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <nitpicker_session/connection.h>
#include <os/surface.h>
#include <os/pixel_rgb565.h>
#include <os/attached_dataspace.h>
#include <base/sleep.h>
/* local includes */
#include "big_mouse.h"
template <typename PT>
void convert_cursor_data_to_pixels(PT *pixel, Nitpicker::Area size)
{
unsigned char *alpha = (unsigned char *)(pixel + size.count());
for (unsigned y = 0; y < size.h(); y++) {
for (unsigned x = 0; x < size.w(); x++) {
/* the source is known to be in RGB565 format */
Genode::Pixel_rgb565 src =
*(Genode::Pixel_rgb565 *)(&big_mouse.pixels[y][x]);
unsigned const i = y*size.w() + x;
pixel[i] = PT(src.r(), src.g(), src.b());
alpha[i] = src.r() ? 255 : 0;
}
}
}
int main(int, char **)
{
static Nitpicker::Connection nitpicker;
Nitpicker::Area const cursor_size(big_mouse.w, big_mouse.h);
Framebuffer::Mode const mode(cursor_size.w(), cursor_size.h(),
Framebuffer::Mode::RGB565);
nitpicker.buffer(mode, true);
static Genode::Attached_dataspace ds(nitpicker.framebuffer()->dataspace());
convert_cursor_data_to_pixels(ds.local_addr<Genode::Pixel_rgb565>(), cursor_size);
Nitpicker::Session::View_handle view = nitpicker.create_view();
Nitpicker::Rect geometry(Nitpicker::Point(0, 0), cursor_size);
nitpicker.enqueue<Nitpicker::Session::Command::Geometry>(view, geometry);
nitpicker.enqueue<Nitpicker::Session::Command::To_front>(view);
nitpicker.execute();
Genode::sleep_forever();
}

View File

@ -0,0 +1,3 @@
TARGET = pointer
SRC_CC = main.cc
LIBS += base

View File

@ -30,8 +30,8 @@ struct Background : private Texture_base, Session, View
*/
Background(Area size)
:
Texture_base(Area(0, 0)), Session(Genode::Session_label(""), 0, false),
View(*this, View::NOT_STAY_TOP, View::NOT_TRANSPARENT, View::BACKGROUND, 0),
Texture_base(Area(0, 0)), Session(Genode::Session_label(""), 0),
View(*this, View::NOT_TRANSPARENT, View::BACKGROUND, 0),
color(25, 37, 50)
{
View::geometry(Rect(Point(0, 0), size));

View File

@ -35,9 +35,8 @@ class Chunky_menubar : public Texture<PT>,
Chunky_menubar(PT *pixels, Area size)
:
Texture<PT>(pixels, 0, size),
Session(Genode::Session_label(""), 0, false),
View(*this, View::STAY_TOP, View::NOT_TRANSPARENT,
View::NOT_BACKGROUND, 0),
Session(Genode::Session_label(""), 0),
View(*this, View::NOT_TRANSPARENT, View::NOT_BACKGROUND, 0),
_canvas(pixels, size)
{
View::geometry(Rect(Point(0, 0), size));

View File

@ -44,16 +44,19 @@ class Domain_registry
private:
Name _name;
Color _color;
Xray _xray;
Origin _origin;
Name _name;
Color _color;
Xray _xray;
Origin _origin;
unsigned _layer;
friend class Domain_registry;
Entry(Name const &name, Color color, Xray xray, Origin origin)
Entry(Name const &name, Color color, Xray xray, Origin origin,
unsigned layer)
:
_name(name), _color(color), _xray(xray), _origin(origin)
_name(name), _color(color), _xray(xray), _origin(origin),
_layer(layer)
{ }
public:
@ -62,6 +65,8 @@ class Domain_registry
Color color() const { return _color; }
unsigned layer() const { return _layer; }
bool xray_opaque() const { return _xray == XRAY_OPAQUE; }
bool xray_no() const { return _xray == XRAY_NO; }
bool origin_pointer() const { return _origin == ORIGIN_POINTER; }
@ -126,10 +131,17 @@ class Domain_registry
return;
}
if (!domain.has_attribute("layer")) {
PERR("no layer specified for domain \"%s\"", name.string());
return;
}
unsigned const layer = domain.attribute_value("layer", ~0UL);
Entry::Color const color = domain.attribute_value("color", WHITE);
_entries.insert(new (_alloc) Entry(name, color, _xray(domain),
_origin(domain)));
_origin(domain), layer));
}
private:

View File

@ -34,10 +34,9 @@
/* local includes */
#include "input.h"
#include "big_mouse.h"
#include "background.h"
#include "clip_guard.h"
#include "mouse_cursor.h"
#include "pointer_origin.h"
#include "chunky_menubar.h"
#include "domain_registry.h"
@ -638,11 +637,10 @@ class Nitpicker::Session_component : public Genode::Rpc_object<Session>,
Framebuffer::Session &framebuffer,
int v_offset,
bool provides_default_bg,
bool stay_top,
Allocator &session_alloc,
size_t ram_quota)
:
::Session(label, v_offset, stay_top),
::Session(label, v_offset),
_session_alloc(&session_alloc, ram_quota),
_framebuffer(framebuffer),
_framebuffer_session_component(view_stack, *this, framebuffer, *this),
@ -729,7 +727,6 @@ class Nitpicker::Session_component : public Genode::Rpc_object<Session>,
view = new (_view_alloc)
View(*this,
stay_top() ? View::STAY_TOP : View::NOT_STAY_TOP,
View::NOT_TRANSPARENT, View::NOT_BACKGROUND,
&(*parent));
@ -748,7 +745,6 @@ class Nitpicker::Session_component : public Genode::Rpc_object<Session>,
try {
view = new (_view_alloc)
View(*this,
stay_top() ? View::STAY_TOP : View::NOT_STAY_TOP,
View::NOT_TRANSPARENT, View::NOT_BACKGROUND,
nullptr);
}
@ -921,8 +917,6 @@ class Nitpicker::Root : public Genode::Root_component<Session_component>
int const v_offset = _default_v_offset;
bool const stay_top = Arg_string::find_arg(args, "stay_top").bool_value(false);
size_t const required_quota = Input::Session_component::ev_ds_size()
+ Genode::align_addr(sizeof(Session::Command_buffer), 12);
@ -940,7 +934,7 @@ class Nitpicker::Root : public Genode::Root_component<Session_component>
Session_component *session = new (md_alloc())
Session_component(Session_label(args), _view_stack, _mode,
_pointer_origin, *ep(), _framebuffer,
v_offset, provides_default_bg, stay_top,
v_offset, provides_default_bg,
*md_alloc(), unused_quota);
session->apply_session_policy(_domain_registry);
@ -1068,9 +1062,7 @@ struct Nitpicker::Main
/*
* Create view stack with default elements
*/
Area mouse_size { big_mouse.w, big_mouse.h };
Mouse_cursor<PT const> mouse_cursor { (PT const *)&big_mouse.pixels[0][0],
mouse_size, user_state };
Pointer_origin pointer_origin;
Background background = { Area(99999, 99999) };
@ -1080,7 +1072,7 @@ struct Nitpicker::Main
Genode::Sliced_heap sliced_heap = { env()->ram_session(), env()->rm_session() };
Root<PT> np_root = { session_list, *domain_registry, global_keys,
ep.rpc_ep(), user_state, user_state, mouse_cursor,
ep.rpc_ep(), user_state, user_state, pointer_origin,
sliced_heap, framebuffer, Framebuffer_screen::MENUBAR_HEIGHT };
Genode::Reporter pointer_reporter = { "pointer" };
@ -1115,9 +1107,9 @@ struct Nitpicker::Main
fb_screen->menubar.state(Menubar_state(user_state, "", BLACK));
user_state.default_background(background);
user_state.stack(mouse_cursor);
user_state.stack(fb_screen->menubar);
user_state.stack(pointer_origin);
user_state.stack(background);
user_state.stack(fb_screen->menubar);
config()->sigh(config_dispatcher);
Signal_transmitter(config_dispatcher).submit();
@ -1144,19 +1136,19 @@ void Nitpicker::Main::handle_input(unsigned)
return;
do {
Point const old_mouse_pos = user_state.mouse_pos();
Point const old_pointer_pos = user_state.pointer_pos();
/* handle batch of pending events */
if (input.is_pending())
import_input_events(ev_buf, input.flush(), user_state);
Point const new_mouse_pos = user_state.mouse_pos();
Point const menubar_offset = Point(0, Framebuffer_screen::MENUBAR_HEIGHT);
Point const report_pos = new_mouse_pos - menubar_offset;
Point const new_pointer_pos = user_state.pointer_pos();
Point const menubar_offset = Point(0, Framebuffer_screen::MENUBAR_HEIGHT);
Point const report_pos = new_pointer_pos - menubar_offset;
/* report mouse-position updates */
if (pointer_reporter.is_enabled() && old_mouse_pos != new_mouse_pos
&& new_mouse_pos.y() >= 0)
if (pointer_reporter.is_enabled() && old_pointer_pos != new_pointer_pos
&& new_pointer_pos.y() >= 0)
Genode::Reporter::Xml_generator xml(pointer_reporter, [&] ()
{
@ -1165,8 +1157,8 @@ void Nitpicker::Main::handle_input(unsigned)
});
/* update mouse cursor */
if (old_mouse_pos != new_mouse_pos)
user_state.geometry(mouse_cursor, Rect(new_mouse_pos, mouse_size));
if (old_pointer_pos != new_pointer_pos)
user_state.geometry(pointer_origin, Rect(new_pointer_pos, Area()));
/* perform redraw and flush pixels to the framebuffer */
user_state.draw(fb_screen->screen).flush([&] (Rect const &rect) {
@ -1226,7 +1218,13 @@ void Nitpicker::Main::handle_config(unsigned)
for (::Session *s = session_list.first(); s; s = s->next())
s->apply_session_policy(*domain_registry);
user_state.apply_origin_policy(mouse_cursor);
user_state.apply_origin_policy(pointer_origin);
/*
* Domains may have changed their layering, resort the view stack with the
* new constrains.
*/
user_state.sort_views_by_layer();
/* redraw */
user_state.update_all_views();
@ -1251,7 +1249,7 @@ void Nitpicker::Main::handle_fb_mode(unsigned)
fb_screen->menubar.state(menubar_state);
/* re-insert menu bar behind mouse cursor */
user_state.stack(fb_screen->menubar, &mouse_cursor);
user_state.stack(fb_screen->menubar, &pointer_origin);
/* redraw */
user_state.update_all_views();

View File

@ -1,81 +0,0 @@
/*
* \brief Nitpicker mouse cursor
* \author Norman Feske
* \date 2006-08-18
*
* The Nitpicker mouse cursor is implemented as a transparent
* view that stays always in front of all other views.
*/
/*
* Copyright (C) 2006-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _MOUSE_CURSOR_H_
#define _MOUSE_CURSOR_H_
#include <nitpicker_gfx/texture_painter.h>
#include "view.h"
#include "session.h"
template <typename PT>
class Mouse_cursor : public Texture<PT>,
public Session, public View
{
private:
View_stack const &_view_stack;
public:
/**
* Constructor
*/
Mouse_cursor(PT const *pixels, Area size, View_stack const &view_stack)
:
Texture<PT>(pixels, 0, size),
Session(Genode::Session_label(""), 0, false),
View(*this, View::STAY_TOP, View::TRANSPARENT, View::NOT_BACKGROUND, 0),
_view_stack(view_stack)
{ }
/***********************
** Session interface **
***********************/
void submit_input_event(Input::Event) override { }
void submit_sync() override { }
/********************
** View interface **
********************/
/*
* The mouse cursor is always displayed without a surrounding frame.
*/
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
{
Rect const view_rect = abs_geometry();
Clip_guard clip_guard(canvas, view_rect);
/* draw area behind the mouse cursor */
_view_stack.draw_rec(canvas, view_stack_next(), view_rect);
/* draw mouse cursor */
canvas.draw_texture(view_rect.p1(), *this, Texture_painter::MASKED, BLACK, true);
}
};
#endif

View File

@ -0,0 +1,46 @@
/*
* \brief View that represents the origin of the pointer coordinate system
* \author Norman Feske
* \date 2014-07-02
*/
/*
* Copyright (C) 2014 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _POINTER_ORIGIN_H_
#define _POINTER_ORIGIN_H_
#include "view.h"
#include "session.h"
struct Pointer_origin : Session, View
{
Pointer_origin()
:
Session(Genode::Session_label(""), 0),
View(*this, View::TRANSPARENT, View::NOT_BACKGROUND, 0)
{ }
/***********************
** Session 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 { }
};
#endif /* _POINTER_ORIGIN_H_ */

View File

@ -42,7 +42,6 @@ class Session : public Session_list::Element
View *_background = 0;
int _v_offset;
unsigned char const *_input_mask = { 0 };
bool const _stay_top;
public:
@ -51,11 +50,10 @@ class Session : public Session_list::Element
*
* \param label session label
* \param v_offset vertical screen offset of session
* \param stay_top true for views that stay always in front
*/
Session(Genode::Session_label const &label, int v_offset, bool stay_top)
Session(Genode::Session_label const &label, int v_offset)
:
_label(label), _v_offset(v_offset), _stay_top(stay_top)
_label(label), _v_offset(v_offset)
{ }
virtual ~Session() { }
@ -72,6 +70,8 @@ class Session : public Session_list::Element
bool origin_pointer() const { return _domain && _domain->origin_pointer(); }
unsigned layer() const { return _domain ? _domain->layer() : ~0UL; }
Texture_base const *texture() const { return _texture; }
void texture(Texture_base const *texture, bool uses_alpha)
@ -100,8 +100,6 @@ class Session : public Session_list::Element
void background(View *background) { _background = background; }
bool stay_top() const { return _stay_top; }
/**
* Return true if session uses an alpha channel
*/

View File

@ -71,7 +71,7 @@ void User_state::handle_event(Input::Event ev)
/*
* Mangle incoming events
*/
int ax = _mouse_pos.x(), ay = _mouse_pos.y();
int ax = _pointer_pos.x(), ay = _pointer_pos.y();
int rx = 0, ry = 0; /* skip info about relative motion by default */
/* transparently handle absolute and relative motion events */
@ -94,13 +94,13 @@ void User_state::handle_event(Input::Event ev)
/* create the mangled event */
ev = Input::Event(type, keycode, ax, ay, rx, ry);
_mouse_pos = Point(ax, ay);
_pointer_pos = Point(ax, ay);
/* count keys */
if (type == Event::PRESS) Mode::inc_key_cnt();
if (type == Event::RELEASE && Mode::drag()) Mode::dec_key_cnt();
View const * const pointed_view = find_view(_mouse_pos);
View const * const pointed_view = find_view(_pointer_pos);
::Session * const pointed_session = pointed_view ? &pointed_view->session() : 0;
/*
@ -268,7 +268,7 @@ void User_state::forget(::Session const &session)
Mode::forget(session);
if (_pointed_session == &session) {
View * const pointed_view = find_view(_mouse_pos);
View * const pointed_view = find_view(_pointer_pos);
_pointed_session = pointed_view ? &pointed_view->session() : nullptr;
}

View File

@ -40,9 +40,9 @@ class User_state : public Mode, public View_stack
Menubar &_menubar;
/*
* Current mouse cursor position
* Current pointer position
*/
Point _mouse_pos;
Point _pointer_pos;
/*
* Currently pointed-at session
@ -80,7 +80,7 @@ class User_state : public Mode, public View_stack
/**
* Accessors
*/
Point mouse_pos() { return _mouse_pos; }
Point pointer_pos() { return _pointer_pos; }
/**
* (Re-)apply origin policy to all views

View File

@ -76,13 +76,11 @@ class View : public Same_buffer_list_elem,
enum { TITLE_LEN = 32 }; /* max.characters of a title */
enum Stay_top { NOT_STAY_TOP = 0, STAY_TOP = 1 };
enum Transparent { NOT_TRANSPARENT = 0, TRANSPARENT = 1 };
enum Background { NOT_BACKGROUND = 0, BACKGROUND = 1 };
private:
Stay_top const _stay_top; /* keep view always on top */
Transparent const _transparent; /* background is partly visible */
Background _background; /* view is a background view */
@ -119,10 +117,10 @@ class View : public Same_buffer_list_elem,
public:
View(Session &session, Stay_top stay_top, Transparent transparent,
View(Session &session, Transparent transparent,
Background bg, View *parent)
:
_stay_top(stay_top), _transparent(transparent), _background(bg),
_transparent(transparent), _background(bg),
_parent(parent), _session(session)
{ title(""); }
@ -250,7 +248,6 @@ class View : public Same_buffer_list_elem,
bool same_session_as(View const &other) const { return &_session == &other._session; }
bool top_level() const { return _parent == 0; }
bool stay_top() const { return _stay_top; }
bool transparent() const { return _transparent || _session.uses_alpha(); }
bool background() const { return _background; }
Rect label_rect() const { return _label_rect; }

View File

@ -15,24 +15,6 @@
#include "clip_guard.h"
/***************
** Utilities **
***************/
/**
* Get last view with the stay_top attribute
*
* \param view first view of the view stack
*/
static View const *last_stay_top_view(View const *view)
{
for (; view && view->view_stack_next() && view->view_stack_next()->stay_top(); )
view = view->view_stack_next();
return view;
}
/**************************
** View stack interface **
**************************/
@ -79,10 +61,9 @@ Rect View_stack::_outline(View const &view) const
View const *View_stack::_target_stack_position(View const *neighbor, bool behind)
{
/* find target position in view stack */
View const *cv = last_stay_top_view(_first_view());
View const *cv = _first_view();
for ( ; cv; cv = _next_view(*cv)) {
for (; cv; cv = _next_view(*cv)) {
/* bring view to front? */
if (behind && !neighbor)
@ -101,7 +82,7 @@ View const *View_stack::_target_stack_position(View const *neighbor, bool behind
break;
}
return cv ? cv : last_stay_top_view(_first_view());
return cv ? cv : _first_view();
}
@ -296,6 +277,9 @@ void View_stack::stack(View &view, View const *neighbor, bool behind)
_views.remove(&view);
_views.insert(&view, _target_stack_position(neighbor, behind));
/* enforce stacking constrains dictated by domain layers */
sort_views_by_layer();
_place_labels(view.abs_geometry());
_mark_view_as_dirty(view, _outline(view));
@ -336,3 +320,40 @@ void View_stack::remove_view(View const &view, bool redraw)
refresh(rect);
}
void View_stack::sort_views_by_layer()
{
Genode::List<View_stack_elem> sorted;
/* last element of the sorted list */
View_stack_elem *at = nullptr;
while (_views.first()) {
/* find view with the lowest layer */
unsigned lowest_layer = ~0UL;
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();
if (layer < lowest_layer) {
lowest_layer = layer;
lowest_view = v;
}
}
if (!lowest_view)
lowest_view = _views.first();
/*
* Move lowest view from unsorted list to the end of the sorted
* list.
*/
_views.remove(lowest_view);
sorted.insert(lowest_view, at);
at = lowest_view;
}
/* replace empty source list by newly sorted list */
_views = sorted;
}

View File

@ -262,6 +262,8 @@ class View_stack
for (View *v = _first_view(); v; v = v->view_stack_next())
v->apply_origin_policy(pointer_origin);
}
void sort_views_by_layer();
};
#endif