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