vbox_pointer: policy-based shape selection

Fixes #1557
This commit is contained in:
Christian Helmuth 2015-06-04 16:59:45 +02:00
parent 178f2c0e88
commit 7897e52235
9 changed files with 1105 additions and 305 deletions

View File

@ -0,0 +1,342 @@
#
# Build
#
# for ldso debugging: <config ld_verbose="yes"/>
#
assert_spec linux
set build_components {
core init
drivers/timer
drivers/framebuffer/sdl
server/report_rom
server/dynamic_rom
server/nitpicker
app/vbox_pointer
test/vbox_pointer
test/nitpicker
}
build $build_components
create_boot_directory
#
# Generate config
#
set config {
<config>
<parent-provides>
<service name="ROM"/>
<service name="RAM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="CAP"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
<service name="SIGNAL"/>
</parent-provides>
<start name="timer">
<resource name="RAM" quantum="1M"/>
<provides> <service name="Timer"/> </provides>
<route>
<service name="RM"> <parent/> </service>
<service name="LOG"> <parent/> </service>
<service name="SIGNAL"> <parent/> </service>
<service name="CAP"> <parent/> </service>
<service name="ROM"> <parent/> </service>
</route>
</start>
<start name="fb_sdl">
<resource name="RAM" quantum="6M"/>
<provides>
<service name="Framebuffer"/>
<service name="Input"/>
</provides>
<config buffered="yes" width="1280" height="720" depth="16"/>
<!--<config buffered="yes" width="1440" height="900" depth="16"/>-->
<route>
<service name="RM"> <parent/> </service>
<service name="LOG"> <parent/> </service>
<service name="SIGNAL"> <parent/> </service>
<service name="CAP"> <parent/> </service>
</route>
</start>
<alias name="input_drv" child="fb_sdl"/>
<alias name="fb_drv" child="fb_sdl"/>
<start name="report_rom_nitpicker">
<binary name="report_rom"/>
<resource name="RAM" quantum="1M"/>
<provides> <service name="Report"/> <service name="ROM"/> </provides>
<config>
<rom>
<policy label="pointer -> hover" report="nitpicker -> hover"/>
<policy label="pointer -> xray" report="nitpicker -> xray"/>
<policy label="pointer -> focus" report="nitpicker -> focus"/>
</rom>
</config>
<route>
<service name="RM"> <parent/> </service>
<service name="LOG"> <parent/> </service>
<service name="SIGNAL"> <parent/> </service>
<service name="CAP"> <parent/> </service>
</route>
</start>
<start name="nitpicker">
<resource name="RAM" quantum="4M"/>
<provides><service name="Nitpicker"/></provides>
<config>
<report focus="yes" hover="yes" xray="yes"/>
<domain name="pointer" layer="1" xray="no" origin="pointer"/>
<domain name="smiley" layer="3"/>
<domain name="" layer="3"/>
<policy label="pointer" domain="pointer"/>
<policy label="test-domain-smiley" domain="smiley"/>
<policy label="" domain=""/>
<global-key name="KEY_F12" operation="xray"/>
<background color="#00426f"/> <!-- indigo -->
</config>
<route>
<service name="RM"> <parent/> </service>
<service name="LOG"> <parent/> </service>
<service name="SIGNAL"> <parent/> </service>
<service name="CAP"> <parent/> </service>
<service name="Timer"> <child name="timer"/> </service>
<service name="Framebuffer"> <child name="fb_drv"/> </service>
<service name="Input"> <child name="input_drv"/> </service>
<service name="Report"> <child name="report_rom_nitpicker"/> </service>
</route>
</start>
<start name="report_rom_shapes">
<binary name="report_rom"/>
<resource name="RAM" quantum="1M"/>
<provides> <service name="Report"/> <service name="ROM"/> </provides>
<config>
<rom>
<policy label="pointer -> arrow" report="shape-arrow -> shape"/>
<policy label="pointer -> blade" report="shape-blade -> shape"/>
<policy label="pointer -> bladex" report="shape-bladex -> shape"/>
<policy label="pointer -> smiley" report="shape-smiley -> shape"/>
</rom>
</config>
<route>
<service name="RM"> <parent/> </service>
<service name="LOG"> <parent/> </service>
<service name="SIGNAL"> <parent/> </service>
<service name="CAP"> <parent/> </service>
</route>
</start>
<start name="shape-arrow">
<binary name="test-vbox_pointer"/>
<resource name="RAM" quantum="2M"/>
<config shape="arrow"/>
<route>
<service name="LOG"> <parent/> </service>
<service name="SIGNAL"> <parent/> </service>
<service name="Report"> <child name="report_rom_shapes"/> </service>
</route>
</start>
<start name="shape-blade">
<binary name="test-vbox_pointer"/>
<resource name="RAM" quantum="2M"/>
<config shape="blade"/>
<route>
<service name="LOG"> <parent/> </service>
<service name="SIGNAL"> <parent/> </service>
<service name="Report"> <child name="report_rom_shapes"/> </service>
</route>
</start>
<start name="shape-bladex">
<binary name="test-vbox_pointer"/>
<resource name="RAM" quantum="2M"/>
<config shape="bladex"/>
<route>
<service name="LOG"> <parent/> </service>
<service name="SIGNAL"> <parent/> </service>
<service name="Report"> <child name="report_rom_shapes"/> </service>
</route>
</start>
<start name="shape-smiley-config">
<binary name="dynamic_rom"/>
<resource name="RAM" quantum="1M"/>
<provides> <service name="ROM"/> </provides>
<config>
<rom name="smiley.config">
<inline description="smiley">
<config shape="smiley"/>
</inline>
<sleep milliseconds="500" />
<inline description="yelims">
<config shape="yelims"/>
</inline>
<sleep milliseconds="500" />
</rom>
</config>
<route>
<service name="RM"> <parent/> </service>
<service name="LOG"> <parent/> </service>
<service name="SIGNAL"> <parent/> </service>
<service name="CAP"> <parent/> </service>
<service name="Timer"> <child name="timer"/> </service>
</route>
</start>
<start name="shape-smiley">
<binary name="test-vbox_pointer"/>
<resource name="RAM" quantum="2M"/>
<configfile name="smiley.config"/>
<route>
<service name="LOG"> <parent/> </service>
<service name="SIGNAL"> <parent/> </service>
<service name="ROM"> <if-args key="label" value="smiley.config"/>
<child name="shape-smiley-config"/> </service>
<service name="Report"> <child name="report_rom_shapes"/> </service>
</route>
</start>
<start name="pointer">
<binary name="vbox_pointer"/>
<resource name="RAM" quantum="1M"/>
<config>
<policy domain="smiley" rom="smiley"/>
<policy label="test-label-arrow" rom="arrow"/>
<policy label="test-label-blade" rom="blade"/>
<policy label="test-label-bladex" rom="bladex"/>
</config>
<route>
<service name="RM"> <parent/> </service>
<service name="LOG"> <parent/> </service>
<service name="SIGNAL"> <parent/> </service>
<service name="Nitpicker"> <child name="nitpicker"/> </service>
<service name="ROM"> <if-arg key="label" value="xray"/>
<child name="report_rom_nitpicker"/> </service>
<service name="ROM"> <if-arg key="label" value="hover"/>
<child name="report_rom_nitpicker"/> </service>
<service name="ROM"> <child name="report_rom_shapes"/> </service>
</route>
</start>
<start name="test-no-match">
<binary name="testnit"/>
<resource name="RAM" quantum="2M"/>
<route>
<service name="RM"> <parent/> </service>
<service name="LOG"> <parent/> </service>
<service name="SIGNAL"> <parent/> </service>
<service name="CAP"> <parent/> </service>
<service name="PD"> <parent/> </service>
<service name="CPU"> <parent/> </service>
<service name="RAM"> <parent/> </service>
<service name="Timer"> <child name="timer"/> </service>
<service name="Nitpicker"> <child name="nitpicker"/> </service>
</route>
</start>
<start name="test-domain-smiley">
<binary name="testnit"/>
<resource name="RAM" quantum="2M"/>
<route>
<service name="RM"> <parent/> </service>
<service name="LOG"> <parent/> </service>
<service name="SIGNAL"> <parent/> </service>
<service name="CAP"> <parent/> </service>
<service name="PD"> <parent/> </service>
<service name="CPU"> <parent/> </service>
<service name="RAM"> <parent/> </service>
<service name="Timer"> <child name="timer"/> </service>
<service name="Nitpicker"> <child name="nitpicker"/> </service>
</route>
</start>
<start name="test-label-arrow">
<binary name="testnit"/>
<resource name="RAM" quantum="2M"/>
<route>
<service name="RM"> <parent/> </service>
<service name="LOG"> <parent/> </service>
<service name="SIGNAL"> <parent/> </service>
<service name="CAP"> <parent/> </service>
<service name="PD"> <parent/> </service>
<service name="CPU"> <parent/> </service>
<service name="RAM"> <parent/> </service>
<service name="Timer"> <child name="timer"/> </service>
<service name="Nitpicker"> <child name="nitpicker"/> </service>
</route>
</start>
<start name="test-label-blade">
<binary name="testnit"/>
<resource name="RAM" quantum="2M"/>
<route>
<service name="RM"> <parent/> </service>
<service name="LOG"> <parent/> </service>
<service name="SIGNAL"> <parent/> </service>
<service name="CAP"> <parent/> </service>
<service name="PD"> <parent/> </service>
<service name="CPU"> <parent/> </service>
<service name="RAM"> <parent/> </service>
<service name="Timer"> <child name="timer"/> </service>
<service name="Nitpicker"> <child name="nitpicker"/> </service>
</route>
</start>
<start name="test-label-blade2">
<binary name="testnit"/>
<resource name="RAM" quantum="2M"/>
<route>
<service name="RM"> <parent/> </service>
<service name="LOG"> <parent/> </service>
<service name="SIGNAL"> <parent/> </service>
<service name="CAP"> <parent/> </service>
<service name="PD"> <parent/> </service>
<service name="CPU"> <parent/> </service>
<service name="RAM"> <parent/> </service>
<service name="Timer"> <child name="timer"/> </service>
<service name="Nitpicker"> <child name="nitpicker"/> </service>
</route>
</start>
<start name="test-label-bladex">
<binary name="testnit"/>
<resource name="RAM" quantum="2M"/>
<route>
<service name="RM"> <parent/> </service>
<service name="LOG"> <parent/> </service>
<service name="SIGNAL"> <parent/> </service>
<service name="CAP"> <parent/> </service>
<service name="PD"> <parent/> </service>
<service name="CPU"> <parent/> </service>
<service name="RAM"> <parent/> </service>
<service name="Timer"> <child name="timer"/> </service>
<service name="Nitpicker"> <child name="nitpicker"/> </service>
</route>
</start>
</config>}
install_config $config
#
# Boot modules
#
# generic modules
set boot_modules {
init timer
fb_sdl
report_rom dynamic_rom
nitpicker
vbox_pointer arrow.shape cross.shape smiley.shape
test-vbox_pointer
testnit
}
# "lsort -unique" removes duplicates but core must be first
build_boot_image "core [lsort -unique $boot_modules]"
run_genode_until forever
# vi: set ft=tcl :

View File

@ -0,0 +1,26 @@
Hover-sensitive pointer for Nitpicker with VirtualBox shape support
Per default the standard "big mouse" pointer is rendered on screen,
which is the behavior known from the classical app/pointer.
Additionally, VirtualBox pointer supports to render "pointer shapes"
when hovering configured Nitpicker sessions. The policies can be
defined for labels or domains of the sessions.
! <start name="vbox_pointer">
! <resource name="RAM" quantum="1M"/>
! <config>
! <policy domain="smiley" rom="smiley"/>
! <policy label="test-label-arrow" rom="arrow"/>
! <policy label="test-label-blade" rom="blade"/>
! </config>
! </start>
In the example above, which is from vbox_pointer.run, the domain
"smiley" gets the ROM "smiley" as pointer shape. The labels
"test-label-blade" and "test-label-arrow" will render the ROMs "arrow"
resp. "blade" as pointer shape. Note that label matching is done from
the start of the actual label until the defined label ends. So,
"test-label-blade2" will also match the policy defined above.
The most common use case for vbox_pointer is VirtualBox, which reports
the guest-pointer shapes if Guest Additions are installed.

View File

@ -14,29 +14,19 @@
*/
/* Genode includes */
#include <base/sleep.h>
#include <os/attached_dataspace.h>
#include <os/attached_ram_dataspace.h>
#include <os/attached_rom_dataspace.h>
#include <os/surface.h>
#include <os/pixel_alpha8.h>
#include <os/config.h>
#include <os/pixel_rgb565.h>
#include <os/pixel_rgb888.h>
#include <os/texture_rgb888.h>
#include <os/texture.h>
#include <util/xml_node.h>
#include <nitpicker_session/connection.h>
#include <vbox_pointer/dither_painter.h>
#include <vbox_pointer/shape_report.h>
/* local includes */
#include "util.h"
#include "policy.h"
#include "big_mouse.h"
/* exception */
struct Pointer_shape_too_large { };
template <typename PT>
void convert_default_cursor_data_to_pixels(PT *pixel, Nitpicker::Area size)
void convert_default_pointer_data_to_pixels(PT *pixel, Nitpicker::Area size)
{
unsigned char *alpha = (unsigned char *)(pixel + size.count());
@ -54,342 +44,232 @@ void convert_default_cursor_data_to_pixels(PT *pixel, Nitpicker::Area size)
}
}
template <typename PT>
void convert_vbox_cursor_data_to_pixels(PT *pixel, unsigned char *shape,
Nitpicker::Area size)
class Main : public Vbox_pointer::Pointer_updater
{
Genode::Attached_ram_dataspace texture_pixel_ds { Genode::env()->ram_session(),
size.count() *
sizeof(Genode::Pixel_rgb888) };
Genode::Attached_ram_dataspace texture_alpha_ds { Genode::env()->ram_session(),
size.count() };
Genode::Texture<Genode::Pixel_rgb888>
texture(texture_pixel_ds.local_addr<Genode::Pixel_rgb888>(),
texture_alpha_ds.local_addr<unsigned char>(),
size);
for (unsigned int y = 0; y < size.h(); y++) {
/* convert the shape data from BGRA encoding to RGBA encoding */
unsigned char *bgra_line = &shape[y * size.w() * 4];
unsigned char rgba_line[size.w() * 4];
for (unsigned int i = 0; i < size.w() * 4; i += 4) {
rgba_line[i + 0] = bgra_line[i + 2];
rgba_line[i + 1] = bgra_line[i + 1];
rgba_line[i + 2] = bgra_line[i + 0];
rgba_line[i + 3] = bgra_line[i + 3];
}
/* import the RGBA-encoded line into the texture */
texture.rgba(rgba_line, size.w(), y);
}
Genode::Pixel_alpha8 *alpha =
reinterpret_cast<Genode::Pixel_alpha8*>(pixel + size.count());
Genode::Surface<PT> pixel_surface(pixel, size);
Genode::Surface<Genode::Pixel_alpha8> alpha_surface(alpha, size);
Dither_painter::paint(pixel_surface, texture);
Dither_painter::paint(alpha_surface, texture);
}
//struct Log { Log(char const *msg) { PINF("Log: %s", msg); } };
typedef Genode::String<64> Domain_name;
class Domain : public Genode::List<Domain>::Element
{
public:
struct Name_too_long { };
private:
Domain_name _name;
typedef Vbox_pointer::String String;
typedef Vbox_pointer::Policy Policy;
typedef Vbox_pointer::Policy_registry Policy_registry;
Genode::Attached_rom_dataspace _hover_ds { "hover" };
Genode::Attached_rom_dataspace _xray_ds { "xray" };
Genode::Signal_receiver _sig_rec;
Genode::Signal_dispatcher<Main> _hover_signal_dispatcher {
_sig_rec, *this, &Main::_handle_hover };
Genode::Signal_dispatcher<Main> _xray_signal_dispatcher {
_sig_rec, *this, &Main::_handle_xray };
Nitpicker::Connection _nitpicker;
Nitpicker::Session::View_handle _view = _nitpicker.create_view();
Policy_registry _policy_registry { *this };
String _hovered_label;
String _hovered_domain;
bool _xray = false;
bool _default_pointer_visible = false;
Nitpicker::Area _current_pointer_size;
Genode::Dataspace_capability _pointer_ds;
void _resize_nitpicker_buffer_if_needed(Nitpicker::Area pointer_size);
void _show_default_pointer();
void _show_shape_pointer(Policy *p);
void _update_pointer();
void _handle_hover(unsigned num = 0);
void _handle_xray(unsigned num = 0);
public:
Domain(char const *name) : _name(name)
{
if (Genode::strlen(name) + 1 > _name.capacity())
throw Name_too_long();
}
Main();
Domain_name const & name() { return _name; }
/*******************************
** Pointer_updater interface **
*******************************/
void update_pointer(Policy *policy) override;
Genode::Signal_receiver & signal_receiver() override { return _sig_rec; }
};
struct Domain_list : private Genode::List<Domain>
void Main::_resize_nitpicker_buffer_if_needed(Nitpicker::Area pointer_size)
{
void add(char const *name)
{
Domain *d = new (Genode::env()->heap()) Domain(name);
insert(d);
}
if (pointer_size == _current_pointer_size)
return;
bool contains(Domain_name const &name)
{
for (Domain *d = first(); d; d = d->next())
if (d->name() == name)
return true;
Framebuffer::Mode const mode { (int)pointer_size.w(),
(int)pointer_size.h(),
Framebuffer::Mode::RGB565 };
return false;
}
};
_nitpicker.buffer(mode, true /* use alpha */);
_pointer_ds = _nitpicker.framebuffer()->dataspace();
struct Main
{
Genode::Attached_rom_dataspace hover_ds { "hover" };
Genode::Attached_rom_dataspace xray_ds { "xray" };
Genode::Attached_rom_dataspace shape_ds { "shape" };
Genode::Signal_receiver sig_rec;
void handle_hover(unsigned num = 0);
void handle_xray(unsigned num = 0);
void handle_shape(unsigned num = 0);
Genode::Signal_dispatcher<Main> hover_signal_dispatcher {
sig_rec, *this, &Main::handle_hover };
Genode::Signal_dispatcher<Main> xray_signal_dispatcher {
sig_rec, *this, &Main::handle_xray };
Genode::Signal_dispatcher<Main> shape_signal_dispatcher {
sig_rec, *this, &Main::handle_shape };
Nitpicker::Connection nitpicker;
Nitpicker::Session::View_handle view = nitpicker.create_view();
Domain_list vbox_domains;
Domain_name current_domain;
bool xray = false;
bool default_pointer_visible = false;
bool vbox_pointer_visible = false;
bool vbox_pointer_shape_changed = false;
Nitpicker::Area current_cursor_size;
Genode::Dataspace_capability pointer_ds;
void resize_nitpicker_buffer_if_needed(Nitpicker::Area cursor_size)
{
if (cursor_size != current_cursor_size) {
Framebuffer::Mode const mode { (int)cursor_size.w(), (int)cursor_size.h(),
Framebuffer::Mode::RGB565 };
nitpicker.buffer(mode, true /* use alpha */);
pointer_ds = nitpicker.framebuffer()->dataspace();
current_cursor_size = cursor_size;
}
}
void show_default_pointer()
{
if (!default_pointer_visible) {
Nitpicker::Area const cursor_size { big_mouse.w, big_mouse.h };
try {
resize_nitpicker_buffer_if_needed(cursor_size);
} catch (...) {
PERR("%s: could not resize the pointer buffer for %u x %u pixels",
__func__, cursor_size.w(), cursor_size.h());
return;
}
Genode::Attached_dataspace ds { pointer_ds };
convert_default_cursor_data_to_pixels(ds.local_addr<Genode::Pixel_rgb565>(),
cursor_size);
nitpicker.framebuffer()->refresh(0, 0, cursor_size.w(), cursor_size.h());
Nitpicker::Rect geometry(Nitpicker::Point(0, 0), cursor_size);
nitpicker.enqueue<Nitpicker::Session::Command::Geometry>(view, geometry);
nitpicker.execute();
default_pointer_visible = true;
vbox_pointer_visible = false;
}
}
void show_vbox_pointer()
{
if (!vbox_pointer_visible || vbox_pointer_shape_changed) {
try {
Vbox_pointer::Shape_report *shape_report =
shape_ds.local_addr<Vbox_pointer::Shape_report>();
if (shape_report->visible) {
if ((shape_report->width == 0) || (shape_report->height == 0))
return;
if ((shape_report->width > Vbox_pointer::MAX_WIDTH) ||
(shape_report->height > Vbox_pointer::MAX_HEIGHT))
throw Pointer_shape_too_large();
Nitpicker::Area const cursor_size { shape_report->width,
shape_report->height };
resize_nitpicker_buffer_if_needed(cursor_size);
Genode::Attached_dataspace ds { pointer_ds };
convert_vbox_cursor_data_to_pixels(ds.local_addr<Genode::Pixel_rgb565>(),
shape_report->shape,
cursor_size);
nitpicker.framebuffer()->refresh(0, 0, cursor_size.w(), cursor_size.h());
Nitpicker::Rect geometry(Nitpicker::Point(-shape_report->x_hot, -shape_report->y_hot), cursor_size);
nitpicker.enqueue<Nitpicker::Session::Command::Geometry>(view, geometry);
} else {
Nitpicker::Rect geometry(Nitpicker::Point(0, 0), Nitpicker::Area(0, 0));
nitpicker.enqueue<Nitpicker::Session::Command::Geometry>(view, geometry);
}
} catch (Genode::Volatile_object<Genode::Attached_dataspace>::Deref_unconstructed_object) {
/* no shape has been reported, yet */
Nitpicker::Rect geometry(Nitpicker::Point(0, 0), Nitpicker::Area(0, 0));
nitpicker.enqueue<Nitpicker::Session::Command::Geometry>(view, geometry);
}
nitpicker.execute();
vbox_pointer_visible = true;
vbox_pointer_shape_changed = false;
default_pointer_visible = false;
}
}
void update_pointer()
{
if (xray || !vbox_domains.contains(current_domain))
show_default_pointer();
else
try {
show_vbox_pointer();
} catch (Pointer_shape_too_large) {
PERR("%s: the pointer shape is larger than the maximum supported size of %u x %u",
__func__, Vbox_pointer::MAX_WIDTH, Vbox_pointer::MAX_HEIGHT);
show_default_pointer();
} catch (...) {
PERR("%s: an unhandled exception occurred while trying to show \
the VirtualBox pointer", __func__);
show_default_pointer();
}
}
Main()
{
/*
* Try to allocate the Nitpicker buffer for the maximum supported
* pointer size to let the user know right from the start if the
* RAM quota is too low.
*/
Framebuffer::Mode const mode { Vbox_pointer::MAX_WIDTH, Vbox_pointer::MAX_HEIGHT,
Framebuffer::Mode::RGB565 };
nitpicker.buffer(mode, true /* use alpha */);
/* TODO should be read from config */
vbox_domains.add("vbox");
/* register signal handlers */
hover_ds.sigh(hover_signal_dispatcher);
xray_ds.sigh(xray_signal_dispatcher);
shape_ds.sigh(shape_signal_dispatcher);
nitpicker.enqueue<Nitpicker::Session::Command::To_front>(view);
nitpicker.execute();
/* import initial state */
handle_hover();
handle_xray();
handle_shape();
}
};
static Domain_name read_string_attribute(Genode::Xml_node const &node,
char const *attr,
Domain_name const &default_value)
{
try {
char buf[Domain_name::capacity()];
node.attribute(attr).value(buf, sizeof(buf));
return Domain_name(buf);
}
catch (...) {
return default_value; }
_current_pointer_size = pointer_size;
}
void Main::handle_hover(unsigned)
void Main::_show_default_pointer()
{
hover_ds.update();
if (!hover_ds.is_valid())
/* only draw default pointer if not already drawn */
if (_default_pointer_visible)
return;
Nitpicker::Area const pointer_size { big_mouse.w, big_mouse.h };
try {
_resize_nitpicker_buffer_if_needed(pointer_size);
} catch (...) {
PERR("%s: could not resize the pointer buffer for %u x %u pixels",
__func__, pointer_size.w(), pointer_size.h());
return;
}
Genode::Attached_dataspace ds { _pointer_ds };
convert_default_pointer_data_to_pixels(ds.local_addr<Genode::Pixel_rgb565>(),
pointer_size);
_nitpicker.framebuffer()->refresh(0, 0, pointer_size.w(), pointer_size.h());
Nitpicker::Rect geometry(Nitpicker::Point(0, 0), pointer_size);
_nitpicker.enqueue<Nitpicker::Session::Command::Geometry>(_view, geometry);
_nitpicker.execute();
_default_pointer_visible = true;
}
void Main::_show_shape_pointer(Policy *p)
{
try {
_resize_nitpicker_buffer_if_needed(p->shape_size());
} catch (...) {
PERR("%s: could not resize the pointer buffer for %u x %u pixels",
__func__, p->shape_size().w(), p->shape_size().h());
throw;
}
Genode::Attached_dataspace ds { _pointer_ds };
p->draw_shape(ds.local_addr<Genode::Pixel_rgb565>());
_nitpicker.framebuffer()->refresh(0, 0, p->shape_size().w(), p->shape_size().h());
Nitpicker::Rect geometry(p->shape_hot(), p->shape_size());
_nitpicker.enqueue<Nitpicker::Session::Command::Geometry>(_view, geometry);
_nitpicker.execute();
_default_pointer_visible = false;
}
void Main::_update_pointer()
{
Policy *policy = nullptr;
if (_xray
|| !(policy = _policy_registry.lookup(_hovered_label, _hovered_domain))
|| !policy->shape_valid())
_show_default_pointer();
else
try {
_show_shape_pointer(policy);
} catch (...) { _show_default_pointer(); }
}
void Main::_handle_hover(unsigned)
{
using Vbox_pointer::read_string_attribute;
_hover_ds.update();
if (!_hover_ds.is_valid())
return;
/* read new hover information from nitpicker's hover report */
try {
Genode::Xml_node node(hover_ds.local_addr<char>());
Genode::Xml_node node(_hover_ds.local_addr<char>());
current_domain = read_string_attribute(node, "domain", Domain_name());
String hovered_label = read_string_attribute(node, "label", String());
String hovered_domain = read_string_attribute(node, "domain", String());
/* update pointer if hovered domain or label changed */
if (hovered_label != _hovered_label || hovered_domain != _hovered_domain) {
_hovered_label = hovered_label;
_hovered_domain = hovered_domain;
_update_pointer();
}
}
catch (...) {
PWRN("could not parse hover report");
}
update_pointer();
}
void Main::handle_xray(unsigned)
void Main::_handle_xray(unsigned)
{
xray_ds.update();
if (!xray_ds.is_valid())
_xray_ds.update();
if (!_xray_ds.is_valid())
return;
try {
Genode::Xml_node node(xray_ds.local_addr<char>());
Genode::Xml_node node(_xray_ds.local_addr<char>());
xray = node.has_attribute("enabled")
&& node.attribute("enabled").has_value("yes");
bool xray = node.has_attribute("enabled")
&& node.attribute("enabled").has_value("yes");
/* update pointer if xray status changed */
if (xray != _xray) {
_xray = xray;
_update_pointer();
}
}
catch (...) {
PWRN("could not parse xray report");
}
update_pointer();
}
void Main::handle_shape(unsigned)
void Main::update_pointer(Policy *policy)
{
shape_ds.update();
/* update pointer if shape-changing policy is hovered */
if (policy == _policy_registry.lookup(_hovered_label, _hovered_domain))
_update_pointer();
}
if (!shape_ds.is_valid())
return;
if (shape_ds.size() < sizeof(Vbox_pointer::Shape_report))
return;
Main::Main()
{
/*
* Try to allocate the Nitpicker buffer for the maximum supported
* pointer size to let the user know right from the start if the
* RAM quota is too low.
*/
Framebuffer::Mode const mode { Vbox_pointer::MAX_WIDTH, Vbox_pointer::MAX_HEIGHT,
Framebuffer::Mode::RGB565 };
vbox_pointer_shape_changed = true;
_nitpicker.buffer(mode, true /* use alpha */);
update_pointer();
_policy_registry.update(Genode::config()->xml_node());
/* register signal handlers */
_hover_ds.sigh(_hover_signal_dispatcher);
_xray_ds.sigh(_xray_signal_dispatcher);
_nitpicker.enqueue<Nitpicker::Session::Command::To_front>(_view);
_nitpicker.execute();
/* import initial state */
_handle_hover();
_handle_xray();
_update_pointer();
}
@ -400,7 +280,7 @@ int main()
/* dispatch signals */
for (;;) {
Genode::Signal sig = main.sig_rec.wait_for_signal();
Genode::Signal sig = main.signal_receiver().wait_for_signal();
Genode::Signal_dispatcher_base *dispatcher =
dynamic_cast<Genode::Signal_dispatcher_base *>(sig.context());

View File

@ -0,0 +1,229 @@
/*
* \brief VirtualBox pointer policies implementation
* \author Christian Prochaska
* \author Christian Helmuth
* \date 2015-06-08
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <os/attached_rom_dataspace.h>
#include <os/attached_ram_dataspace.h>
#include <os/pixel_alpha8.h>
#include <os/pixel_rgb888.h>
#include <os/surface.h>
#include <os/texture_rgb888.h>
#include <vbox_pointer/dither_painter.h>
/* local includes */
#include "policy.h"
/******************************
** Entry in policy registry **
******************************/
class Vbox_pointer::Policy_entry : public Vbox_pointer::Policy,
public Genode::List<Policy_entry>::Element
{
private:
String _label;
String _domain;
Pointer_updater &_updater;
Genode::Attached_ram_dataspace _texture_pixel_ds { Genode::env()->ram_session(),
Vbox_pointer::MAX_WIDTH *
Vbox_pointer::MAX_HEIGHT *
sizeof(Genode::Pixel_rgb888) };
Genode::Attached_ram_dataspace _texture_alpha_ds { Genode::env()->ram_session(),
Vbox_pointer::MAX_WIDTH *
Vbox_pointer::MAX_HEIGHT };
Genode::Attached_rom_dataspace _shape_ds;
Genode::Signal_dispatcher<Policy_entry> shape_signal_dispatcher {
_updater.signal_receiver(), *this, &Policy_entry::_import_shape };
Nitpicker::Area _shape_size;
Nitpicker::Point _shape_hot;
void _import_shape(unsigned = 0)
{
using namespace Genode;
_shape_ds.update();
if (!_shape_ds.is_valid())
return;
if (_shape_ds.size() < sizeof(Vbox_pointer::Shape_report))
return;
Vbox_pointer::Shape_report *shape_report =
_shape_ds.local_addr<Vbox_pointer::Shape_report>();
if (!shape_report->visible
|| shape_report->width == 0 || shape_report->height == 0
|| shape_report->width > Vbox_pointer::MAX_WIDTH
|| shape_report->height > Vbox_pointer::MAX_HEIGHT) {
_shape_size = Nitpicker::Area();
_shape_hot = Nitpicker::Point();
_updater.update_pointer(this);
}
_shape_size = Nitpicker::Area(shape_report->width, shape_report->height);
_shape_hot = Nitpicker::Point(-shape_report->x_hot, -shape_report->y_hot);
Texture<Pixel_rgb888>
texture(_texture_pixel_ds.local_addr<Pixel_rgb888>(),
_texture_alpha_ds.local_addr<unsigned char>(),
_shape_size);
for (unsigned int y = 0; y < _shape_size.h(); y++) {
/* convert the shape data from BGRA encoding to RGBA encoding */
unsigned char *shape = shape_report->shape;
unsigned char *bgra_line = &shape[y * _shape_size.w() * 4];
unsigned char rgba_line[_shape_size.w() * 4];
for (unsigned int i = 0; i < _shape_size.w() * 4; i += 4) {
rgba_line[i + 0] = bgra_line[i + 2];
rgba_line[i + 1] = bgra_line[i + 1];
rgba_line[i + 2] = bgra_line[i + 0];
rgba_line[i + 3] = bgra_line[i + 3];
}
/* import the RGBA-encoded line into the texture */
texture.rgba(rgba_line, _shape_size.w(), y);
}
_updater.update_pointer(this);
}
public:
Policy_entry(String const &label, String const &domain, String const &rom,
Pointer_updater &updater)
:
_label(label), _domain(domain), _updater(updater),
_shape_ds(rom.string())
{
_import_shape();
_shape_ds.sigh(shape_signal_dispatcher);
}
/**
* Return similarity of policy label and the passed label
*/
Genode::size_t match_label(String const &other) const
{
if (_label.length() > 1 && other.length() > 1) {
/* length of string w/o null byte */
Genode::size_t const len = _label.length() - 1;
if (Genode::strcmp(other.string(), _label.string(), len) == 0)
return len;
}
return 0;
}
/**
* Return if policy domain and passed domain match exactly
*/
bool match_domain(String const &other) const
{
return _domain.length() > 1 && _domain == other;
}
/**********************
** Policy interface **
**********************/
Nitpicker::Area shape_size() const override { return _shape_size; }
Nitpicker::Point shape_hot() const override { return _shape_hot; }
bool shape_valid() const override { return _shape_size.valid(); }
void draw_shape(Genode::Pixel_rgb565 *pixel) override
{
using namespace Genode;
if (!_shape_size.valid())
return;
Pixel_alpha8 *alpha =
reinterpret_cast<Pixel_alpha8 *>(pixel + _shape_size.count());
Surface<Pixel_rgb565> pixel_surface(pixel, _shape_size);
Surface<Pixel_alpha8> alpha_surface(alpha, _shape_size);
Texture<Pixel_rgb888>
texture(_texture_pixel_ds.local_addr<Pixel_rgb888>(),
_texture_alpha_ds.local_addr<unsigned char>(),
_shape_size);
Dither_painter::paint(pixel_surface, texture);
Dither_painter::paint(alpha_surface, texture);
}
};
/**************
** Registry **
**************/
void Vbox_pointer::Policy_registry::update(Genode::Xml_node config)
{
/* TODO real update should flush at least */
try {
for (Genode::Xml_node policy = config.sub_node("policy");
true; policy = policy.next("policy")) {
String label = read_string_attribute(policy, "label", String());
String domain = read_string_attribute(policy, "domain", String());
String rom = read_string_attribute(policy, "rom", String());
if (!label.valid() && !domain.valid())
PWRN("policy does not declare label/domain attribute");
else if (!rom.valid())
PWRN("policy does not declare shape rom");
else
insert(new (Genode::env()->heap())
Policy_entry(label, domain, rom, _updater));
}
} catch (...) { }
}
Vbox_pointer::Policy * Vbox_pointer::Policy_registry::lookup(String const &label,
String const &domain)
{
/* try label similarity matching first */
unsigned similarity = 0;
Policy_entry *match = nullptr;
for (Policy_entry *p = first(); p; p = p->next()) {
unsigned s = p->match_label(label);
if (s > similarity) {
similarity = s;
match = p;
}
}
if (match) return match;
/* then match domains */
for (Policy_entry *p = first(); p; p = p->next())
if (p->match_domain(domain))
return p;
return nullptr;
}

View File

@ -0,0 +1,68 @@
/*
* \brief VirtualBox pointer policies
* \author Christian Helmuth
* \date 2015-06-08
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _VBOX_POINTER_POLICY_H_
#define _VBOX_POINTER_POLICY_H_
/* Genode includes */
#include <os/pixel_rgb565.h>
#include <nitpicker_session/nitpicker_session.h>
#include <vbox_pointer/shape_report.h>
#include <util/list.h>
/* local includes */
#include "util.h"
namespace Vbox_pointer {
struct Pointer_updater;
struct Policy;
struct Policy_entry;
struct Policy_registry;
}
struct Vbox_pointer::Pointer_updater
{
virtual void update_pointer(Policy *initiator) = 0;
virtual Genode::Signal_receiver & signal_receiver() = 0;
};
struct Vbox_pointer::Policy
{
virtual Nitpicker::Area shape_size() const = 0;
virtual Nitpicker::Point shape_hot() const = 0;
virtual bool shape_valid() const = 0;
virtual void draw_shape(Genode::Pixel_rgb565 *pixel) = 0;
};
class Vbox_pointer::Policy_registry : private Genode::List<Policy_entry>
{
private:
Pointer_updater &_updater;
public:
Policy_registry(Pointer_updater &updater)
: _updater(updater) { }
void update(Genode::Xml_node config);
Policy * lookup(String const &label, String const &domain);
};
#endif /* _VBOX_POINTER_POLICY_H_ */

View File

@ -1,3 +1,3 @@
TARGET = vbox_pointer
SRC_CC = main.cc
LIBS += base
SRC_CC = main.cc policy.cc
LIBS += base config

View File

@ -0,0 +1,42 @@
/*
* \brief VirtualBox pointer utilities
* \author Christian Helmuth
* \date 2015-06-08
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _VBOX_POINTER_UTIL_H_
#define _VBOX_POINTER_UTIL_H_
/* Genode includes */
#include <util/xml_node.h>
#include <util/string.h>
namespace Vbox_pointer {
typedef Genode::String<64> String;
inline String read_string_attribute(Genode::Xml_node const &node,
char const *attr,
String const &default_value);
}
Vbox_pointer::String Vbox_pointer::read_string_attribute(Genode::Xml_node const &node,
char const *attr,
String const &default_value)
{
try {
char buf[String::capacity()];
node.attribute(attr).value(buf, sizeof(buf));
return String(buf);
} catch (...) { return default_value; }
}
#endif /* _VBOX_POINTER_UTIL_H_ */

View File

@ -0,0 +1,210 @@
/*
* \brief Pointer shape reporter test
* \author Christian Helmuth
* \date 2015-06-03
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#include <base/printf.h>
#include <util/string.h>
#include <os/reporter.h>
#include <os/config.h>
#include <vbox_pointer/shape_report.h>
static bool const verbose = false;
typedef Genode::String<16> String;
static String read_string_attribute(Genode::Xml_node const &node,
char const *attr,
String const &default_value)
{
try {
char buf[String::capacity()];
node.attribute(attr).value(buf, sizeof(buf));
return String(buf);
}
catch (...) {
return default_value; }
}
struct Shape
{
enum { WIDTH = 16, HEIGHT = 16 };
String const id;
unsigned const x_hot;
unsigned const y_hot;
unsigned char const map[WIDTH*HEIGHT];
};
struct Shape_report : Vbox_pointer::Shape_report
{
Genode::Reporter reporter { "shape", sizeof(Vbox_pointer::Shape_report) };
Shape_report()
:
Vbox_pointer::Shape_report{true, 0, 0, Shape::WIDTH, Shape::HEIGHT, { 0 }}
{
reporter.enabled(true);
}
void report(Shape const &s)
{
x_hot = s.x_hot;
y_hot = s.y_hot;
unsigned const w = Shape::WIDTH;
unsigned const h = Shape::HEIGHT;
for (unsigned y = 0; y < h; ++y) {
for (unsigned x = 0; x < w; ++x) {
shape[(y*w + x)*4 + 0] = 0xff;
shape[(y*w + x)*4 + 1] = 0xff;
shape[(y*w + x)*4 + 2] = 0xff;
shape[(y*w + x)*4 + 3] = s.map[y*w +x] ? 0xe0 : 0;
if (verbose)
Genode::printf("%c", s.map[y*w +x] ? 'X' : ' ');
}
if (verbose)
Genode::printf("\n");
}
if (verbose)
Genode::printf(".%s.%u.%u.\n", s.id.string(), s.x_hot, s.y_hot);
reporter.report(static_cast<Vbox_pointer::Shape_report *>(this),
sizeof(Vbox_pointer::Shape_report));
}
};
static Shape const shape[] = {
{ "arrow", 0, 0, {
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,
0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,
0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,
0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,
0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,
0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,1,0,1,1,1,1,0,0,
0,0,0,0,0,0,0,0,1,0,1,1,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,0,
0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } },
{ "blade", 0, 0, {
1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,1,0,1,0,1,1,0,0,
0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,
0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,
0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,
0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } },
{ "bladex", 8, 8, {
1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,
1,0,1,0,0,0,0,0,0,0,0,0,0,1,0,1,
0,1,0,1,0,0,0,0,0,0,0,0,1,0,1,0,
0,0,1,0,1,0,0,0,0,0,0,1,0,1,0,0,
0,0,0,1,0,1,0,0,0,0,1,0,1,0,0,0,
0,0,0,0,1,0,1,0,0,1,0,1,0,0,0,0,
0,0,0,0,0,1,0,1,1,0,1,0,0,0,0,0,
0,0,0,0,0,0,1,0,1,1,0,0,0,0,0,0,
0,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,
0,0,1,1,0,1,0,1,1,0,1,0,1,1,0,0,
0,0,1,1,1,1,1,0,0,1,1,1,1,1,0,0,
0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,
0,0,1,1,1,1,1,0,0,1,1,1,1,1,0,0,
0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,
0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } },
{ "smiley", 8, 8, {
0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,
0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,
0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,
0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,
0,1,0,0,0,1,1,0,0,1,1,0,0,0,1,0,
1,0,0,0,0,1,1,0,0,1,1,0,0,0,0,1,
1,0,0,0,0,1,1,0,0,1,1,0,0,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,
0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,
0,1,0,0,0,0,1,1,1,1,0,0,0,0,1,0,
0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,
0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,
0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0 } },
{ "yelims", 8, 8, {
0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,
0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,
0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,
0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,
0,1,0,0,0,1,1,0,0,1,1,0,0,0,1,0,
1,0,0,0,0,1,1,0,0,1,1,0,0,0,0,1,
1,0,0,0,0,1,1,0,0,1,1,0,0,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,0,0,0,1,1,1,1,0,0,0,0,0,1,
0,1,0,0,1,1,0,0,0,0,1,1,0,0,1,0,
0,1,0,1,0,0,0,0,0,0,0,0,1,0,1,0,
0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,
0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,
0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0 } }
};
static Shape const & select_shape()
{
String const id = read_string_attribute(Genode::config()->xml_node(),
"shape", String("arrow"));
for (Shape const &s : shape)
if (s.id == id)
return s;
/* not found -> use first as default */
return shape[0];
}
int main()
{
static Shape_report r;
/* register signal handler for config changes */
Genode::Signal_receiver sig_rec;
Genode::Signal_context sig_ctx;
Genode::config()->sigh(sig_rec.manage(&sig_ctx));
while (true) {
r.report(select_shape());
sig_rec.wait_for_signal();
Genode::config()->reload();
}
}

View File

@ -0,0 +1,3 @@
TARGET = test-vbox_pointer
SRC_CC = main.cc
LIBS = base config