diff --git a/repos/ports/run/vbox_pointer.run b/repos/ports/run/vbox_pointer.run
new file mode 100644
index 0000000000..187aa2999c
--- /dev/null
+++ b/repos/ports/run/vbox_pointer.run
@@ -0,0 +1,342 @@
+#
+# Build
+#
+# for ldso debugging:
+#
+
+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 {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}
+
+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 :
diff --git a/repos/ports/src/app/vbox_pointer/README b/repos/ports/src/app/vbox_pointer/README
new file mode 100644
index 0000000000..886f880f24
--- /dev/null
+++ b/repos/ports/src/app/vbox_pointer/README
@@ -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.
+
+!
+!
+!
+!
+!
+!
+!
+!
+
+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.
diff --git a/repos/ports/src/app/vbox_pointer/main.cc b/repos/ports/src/app/vbox_pointer/main.cc
index 59aef95e6b..86e5299466 100644
--- a/repos/ports/src/app/vbox_pointer/main.cc
+++ b/repos/ports/src/app/vbox_pointer/main.cc
@@ -14,29 +14,19 @@
*/
/* Genode includes */
-#include
-#include
-#include
#include
-#include
-#include
+#include
#include
-#include
-#include
-#include
-#include
#include
-#include
-#include
/* local includes */
+#include "util.h"
+#include "policy.h"
#include "big_mouse.h"
-/* exception */
-struct Pointer_shape_too_large { };
template
-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
-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
- texture(texture_pixel_ds.local_addr(),
- texture_alpha_ds.local_addr(),
- 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(pixel + size.count());
-
- Genode::Surface pixel_surface(pixel, size);
- Genode::Surface 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::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 _hover_signal_dispatcher {
+ _sig_rec, *this, &Main::_handle_hover };
+ Genode::Signal_dispatcher _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
+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 hover_signal_dispatcher {
- sig_rec, *this, &Main::handle_hover };
- Genode::Signal_dispatcher xray_signal_dispatcher {
- sig_rec, *this, &Main::handle_xray };
- Genode::Signal_dispatcher 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(),
- cursor_size);
- nitpicker.framebuffer()->refresh(0, 0, cursor_size.w(), cursor_size.h());
-
- Nitpicker::Rect geometry(Nitpicker::Point(0, 0), cursor_size);
- nitpicker.enqueue(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();
-
- 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(),
- 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(view, geometry);
-
- } else {
-
- Nitpicker::Rect geometry(Nitpicker::Point(0, 0), Nitpicker::Area(0, 0));
- nitpicker.enqueue(view, geometry);
-
- }
-
- } catch (Genode::Volatile_object::Deref_unconstructed_object) {
- /* no shape has been reported, yet */
- Nitpicker::Rect geometry(Nitpicker::Point(0, 0), Nitpicker::Area(0, 0));
- nitpicker.enqueue(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(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(),
+ pointer_size);
+ _nitpicker.framebuffer()->refresh(0, 0, pointer_size.w(), pointer_size.h());
+
+ Nitpicker::Rect geometry(Nitpicker::Point(0, 0), pointer_size);
+ _nitpicker.enqueue(_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());
+
+ _nitpicker.framebuffer()->refresh(0, 0, p->shape_size().w(), p->shape_size().h());
+
+ Nitpicker::Rect geometry(p->shape_hot(), p->shape_size());
+ _nitpicker.enqueue(_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());
+ Genode::Xml_node node(_hover_ds.local_addr());
- 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());
+ Genode::Xml_node node(_xray_ds.local_addr());
- 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(_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(sig.context());
diff --git a/repos/ports/src/app/vbox_pointer/policy.cc b/repos/ports/src/app/vbox_pointer/policy.cc
new file mode 100644
index 0000000000..6342decc27
--- /dev/null
+++ b/repos/ports/src/app/vbox_pointer/policy.cc
@@ -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
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* local includes */
+#include "policy.h"
+
+
+/******************************
+ ** Entry in policy registry **
+ ******************************/
+
+class Vbox_pointer::Policy_entry : public Vbox_pointer::Policy,
+ public Genode::List::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 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();
+
+ 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
+ texture(_texture_pixel_ds.local_addr(),
+ _texture_alpha_ds.local_addr(),
+ _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 + _shape_size.count());
+
+ Surface pixel_surface(pixel, _shape_size);
+ Surface alpha_surface(alpha, _shape_size);
+
+ Texture
+ texture(_texture_pixel_ds.local_addr(),
+ _texture_alpha_ds.local_addr(),
+ _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;
+}
diff --git a/repos/ports/src/app/vbox_pointer/policy.h b/repos/ports/src/app/vbox_pointer/policy.h
new file mode 100644
index 0000000000..64cccbea3e
--- /dev/null
+++ b/repos/ports/src/app/vbox_pointer/policy.h
@@ -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
+#include
+#include
+#include
+
+/* 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
+{
+ 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_ */
diff --git a/repos/ports/src/app/vbox_pointer/target.mk b/repos/ports/src/app/vbox_pointer/target.mk
index 2280cd9f77..6bf2ff5083 100644
--- a/repos/ports/src/app/vbox_pointer/target.mk
+++ b/repos/ports/src/app/vbox_pointer/target.mk
@@ -1,3 +1,3 @@
TARGET = vbox_pointer
-SRC_CC = main.cc
-LIBS += base
+SRC_CC = main.cc policy.cc
+LIBS += base config
diff --git a/repos/ports/src/app/vbox_pointer/util.h b/repos/ports/src/app/vbox_pointer/util.h
new file mode 100644
index 0000000000..2732a9d329
--- /dev/null
+++ b/repos/ports/src/app/vbox_pointer/util.h
@@ -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
+#include
+
+
+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_ */
diff --git a/repos/ports/src/test/vbox_pointer/main.cc b/repos/ports/src/test/vbox_pointer/main.cc
new file mode 100644
index 0000000000..bed46deafb
--- /dev/null
+++ b/repos/ports/src/test/vbox_pointer/main.cc
@@ -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
+#include
+#include
+#include
+#include
+
+
+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(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();
+ }
+}
diff --git a/repos/ports/src/test/vbox_pointer/target.mk b/repos/ports/src/test/vbox_pointer/target.mk
new file mode 100644
index 0000000000..5c587b4ded
--- /dev/null
+++ b/repos/ports/src/test/vbox_pointer/target.mk
@@ -0,0 +1,3 @@
+TARGET = test-vbox_pointer
+SRC_CC = main.cc
+LIBS = base config