mirror of
https://github.com/genodelabs/genode.git
synced 2025-06-18 15:18:20 +00:00
pointer: custom pointer shape support
Make the revised 'vbox_pointer' component the new 'pointer' component. Fixes #2585
This commit is contained in:
committed by
Christian Helmuth
parent
18993b5ede
commit
dd98bd67a0
39
repos/os/src/app/pointer/README
Normal file
39
repos/os/src/app/pointer/README
Normal file
@ -0,0 +1,39 @@
|
||||
Pointer for Nitpicker with optional hover-sensitive shape support
|
||||
|
||||
Per default the standard "big mouse" pointer is rendered on screen.
|
||||
|
||||
Additionally, 'pointer' supports to render "pointer shapes" when hovering
|
||||
Nitpicker sessions for which a shape was reported. This feature must be
|
||||
enabled with the '<config shapes="yes"/>' option. When enabled, the
|
||||
'pointer' component announces a 'Report' service for custom pointer shapes
|
||||
and requests ROM sessions for Nitpicker's 'hover' and 'xray' reports. The
|
||||
mapping between hovered Nitpicker sessions and applications can be achieved
|
||||
with report session label rewriting:
|
||||
|
||||
! <start name="shape-arrow">
|
||||
! <binary name="test-pointer"/>
|
||||
! <resource name="RAM" quantum="2M"/>
|
||||
! <config shape="arrow"/>
|
||||
! <route>
|
||||
! <service name="PD"> <parent/> </service>
|
||||
! <service name="ROM"> <parent/> </service>
|
||||
! <service name="CPU"> <parent/> </service>
|
||||
! <service name="LOG"> <parent/> </service>
|
||||
! <service name="Report" label="shape">
|
||||
! <child name="pointer" label="test-label-arrow -> testnit"/>
|
||||
! </service>
|
||||
! </route>
|
||||
! </start>
|
||||
|
||||
In the example above, which is from 'pointer.run', the 'shape-arrow' component
|
||||
reports an arrow shape with the label "shape". By rewriting the label of the
|
||||
report, the shape will be drawn for the 'test-label-arrow' component, which
|
||||
is reported by Nitpicker with the label 'test-label-arror -> testnit' when
|
||||
hovered.
|
||||
|
||||
When configured with '<config shapes="yes" verbose="yes"/>', the 'pointer'
|
||||
component prints the labels of hovered Nitpicker sessions and received shape
|
||||
reports to the 'LOG' service.
|
||||
|
||||
The most common use cases for pointer shapes are VirtualBox, which reports
|
||||
the guest-pointer shapes if Guest Additions are installed, and Qt applications.
|
@ -1,6 +1,8 @@
|
||||
/*
|
||||
* \brief Minimalistic nitpicker pointer
|
||||
* \brief Nitpicker pointer with support for VirtualBox-defined shapes
|
||||
* \author Norman Feske
|
||||
* \author Christian Helmuth
|
||||
* \author Christian Prochaska
|
||||
* \date 2014-07-02
|
||||
*/
|
||||
|
||||
@ -12,18 +14,29 @@
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <nitpicker_session/connection.h>
|
||||
#include <os/surface.h>
|
||||
#include <os/pixel_rgb565.h>
|
||||
#include <base/attached_dataspace.h>
|
||||
#include <base/component.h>
|
||||
#include <base/heap.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <os/pixel_alpha8.h>
|
||||
#include <os/pixel_rgb565.h>
|
||||
#include <os/pixel_rgb888.h>
|
||||
#include <os/surface.h>
|
||||
#include <os/texture_rgb888.h>
|
||||
#include <nitpicker_session/connection.h>
|
||||
#include <report_rom/report_service.h>
|
||||
#include <pointer/dither_painter.h>
|
||||
#include <pointer/shape_report.h>
|
||||
|
||||
/* local includes */
|
||||
#include "util.h"
|
||||
#include "rom_registry.h"
|
||||
#include "big_mouse.h"
|
||||
|
||||
namespace Pointer { class Main; }
|
||||
|
||||
|
||||
template <typename PT>
|
||||
void convert_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());
|
||||
|
||||
@ -42,25 +55,324 @@ void convert_cursor_data_to_pixels(PT *pixel, Nitpicker::Area size)
|
||||
}
|
||||
|
||||
|
||||
void Component::construct(Genode::Env &env)
|
||||
class Pointer::Main : public Rom::Reader
|
||||
{
|
||||
static Nitpicker::Connection nitpicker { env, "cursor" };
|
||||
private:
|
||||
|
||||
Nitpicker::Area const cursor_size(big_mouse.w, big_mouse.h);
|
||||
typedef Pointer::String String;
|
||||
|
||||
Framebuffer::Mode const mode(cursor_size.w(), cursor_size.h(),
|
||||
Framebuffer::Mode::RGB565);
|
||||
nitpicker.buffer(mode, true);
|
||||
Genode::Env &_env;
|
||||
|
||||
Genode::Attached_dataspace ds(
|
||||
env.rm(), nitpicker.framebuffer()->dataspace());
|
||||
Genode::Attached_rom_dataspace _config { _env, "config" };
|
||||
|
||||
convert_cursor_data_to_pixels(
|
||||
ds.local_addr<Genode::Pixel_rgb565>(), cursor_size);
|
||||
bool _verbose = _config.xml().attribute_value("verbose", false);
|
||||
|
||||
Nitpicker::Session::View_handle view = nitpicker.create_view();
|
||||
Nitpicker::Rect geometry(Nitpicker::Point(0, 0), cursor_size);
|
||||
nitpicker.enqueue<Nitpicker::Session::Command::Geometry>(view, geometry);
|
||||
nitpicker.enqueue<Nitpicker::Session::Command::To_front>(view);
|
||||
nitpicker.execute();
|
||||
Nitpicker::Connection _nitpicker { _env };
|
||||
|
||||
Nitpicker::Session::View_handle _view = _nitpicker.create_view();
|
||||
|
||||
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 _update_pointer();
|
||||
|
||||
/* custom shape support */
|
||||
|
||||
bool _shapes_enabled = _config.xml().attribute_value("shapes", false);
|
||||
|
||||
bool _xray = false;
|
||||
|
||||
Genode::Constructible<Genode::Attached_rom_dataspace> _hover_ds;
|
||||
Genode::Constructible<Genode::Attached_rom_dataspace> _xray_ds;
|
||||
|
||||
Genode::Signal_handler<Main> _hover_signal_handler {
|
||||
_env.ep(), *this, &Main::_handle_hover };
|
||||
Genode::Signal_handler<Main> _xray_signal_handler {
|
||||
_env.ep(), *this, &Main::_handle_xray };
|
||||
|
||||
Genode::Sliced_heap _sliced_heap { _env.ram(), _env.rm() };
|
||||
|
||||
Rom::Registry _rom_registry { _sliced_heap, _env.ram(), _env.rm(), *this };
|
||||
|
||||
Report::Root _report_root { _env, _sliced_heap, _rom_registry, _verbose };
|
||||
|
||||
String _hovered_label;
|
||||
|
||||
Genode::Attached_ram_dataspace _texture_pixel_ds { _env.ram(), _env.rm(),
|
||||
Pointer::MAX_WIDTH *
|
||||
Pointer::MAX_HEIGHT *
|
||||
sizeof(Genode::Pixel_rgb888) };
|
||||
|
||||
Genode::Attached_ram_dataspace _texture_alpha_ds { _env.ram(), _env.rm(),
|
||||
Pointer::MAX_WIDTH *
|
||||
Pointer::MAX_HEIGHT };
|
||||
|
||||
void _show_shape_pointer(Shape_report &shape_report);
|
||||
void _handle_hover();
|
||||
void _handle_xray();
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Reader interface
|
||||
*/
|
||||
void notify_module_changed() override
|
||||
{
|
||||
_update_pointer();
|
||||
}
|
||||
|
||||
void notify_module_invalidated() override
|
||||
{
|
||||
_update_pointer();
|
||||
}
|
||||
|
||||
Main(Genode::Env &);
|
||||
};
|
||||
|
||||
|
||||
void Pointer::Main::_resize_nitpicker_buffer_if_needed(Nitpicker::Area pointer_size)
|
||||
{
|
||||
if (pointer_size == _current_pointer_size)
|
||||
return;
|
||||
|
||||
Framebuffer::Mode const mode { (int)pointer_size.w(),
|
||||
(int)pointer_size.h(),
|
||||
Framebuffer::Mode::RGB565 };
|
||||
|
||||
_nitpicker.buffer(mode, true /* use alpha */);
|
||||
|
||||
_pointer_ds = _nitpicker.framebuffer()->dataspace();
|
||||
|
||||
_current_pointer_size = pointer_size;
|
||||
}
|
||||
|
||||
|
||||
void Pointer::Main::_show_default_pointer()
|
||||
{
|
||||
/* 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 (...) {
|
||||
Genode::error(__func__, ": could not resize the pointer buffer "
|
||||
"for ", pointer_size.w(), "x", pointer_size.h(), " pixels");
|
||||
return;
|
||||
}
|
||||
|
||||
Genode::Attached_dataspace ds { _env.rm(), _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 Pointer::Main::_show_shape_pointer(Shape_report &shape_report)
|
||||
{
|
||||
Nitpicker::Area shape_size { shape_report.width, shape_report.height };
|
||||
Nitpicker::Point shape_hot { (int)-shape_report.x_hot, (int)-shape_report.y_hot };
|
||||
|
||||
try {
|
||||
_resize_nitpicker_buffer_if_needed(shape_size);
|
||||
} catch (...) {
|
||||
error(__func__, ": could not resize the pointer buffer "
|
||||
"for ", shape_size, " pixels");
|
||||
throw;
|
||||
}
|
||||
|
||||
if (shape_report.visible) {
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
/* import shape into texture */
|
||||
|
||||
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++) {
|
||||
|
||||
/* import the RGBA-encoded line into the texture */
|
||||
unsigned char *shape = shape_report.shape;
|
||||
unsigned char *line = &shape[y * shape_size.w() * 4];
|
||||
texture.rgba(line, shape_size.w(), y);
|
||||
}
|
||||
|
||||
/* draw texture */
|
||||
|
||||
Attached_dataspace ds { _env.rm(), _pointer_ds };
|
||||
|
||||
Pixel_rgb565 *pixel = ds.local_addr<Pixel_rgb565>();
|
||||
|
||||
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);
|
||||
|
||||
Dither_painter::paint(pixel_surface, texture);
|
||||
Dither_painter::paint(alpha_surface, texture);
|
||||
}
|
||||
|
||||
_nitpicker.framebuffer()->refresh(0, 0, shape_size.w(), shape_size.h());
|
||||
|
||||
Nitpicker::Rect geometry(shape_hot, shape_size);
|
||||
_nitpicker.enqueue<Nitpicker::Session::Command::Geometry>(_view, geometry);
|
||||
_nitpicker.execute();
|
||||
|
||||
_default_pointer_visible = false;
|
||||
}
|
||||
|
||||
|
||||
void Pointer::Main::_update_pointer()
|
||||
{
|
||||
if (!_shapes_enabled || _xray) {
|
||||
_show_default_pointer();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
Rom::Readable_module &shape_module =
|
||||
_rom_registry.lookup(*this, _hovered_label);
|
||||
|
||||
try {
|
||||
Shape_report shape_report;
|
||||
|
||||
shape_module.read_content(*this, (char*)&shape_report, sizeof(shape_report));
|
||||
|
||||
if (shape_report.visible) {
|
||||
|
||||
if ((shape_report.width == 0) ||
|
||||
(shape_report.height == 0) ||
|
||||
(shape_report.width > MAX_WIDTH) ||
|
||||
(shape_report.height > MAX_HEIGHT))
|
||||
throw Genode::Exception();
|
||||
|
||||
_show_shape_pointer(shape_report);
|
||||
}
|
||||
|
||||
} catch (...) {
|
||||
_rom_registry.release(*this, shape_module);
|
||||
throw;
|
||||
}
|
||||
|
||||
_rom_registry.release(*this, shape_module);
|
||||
|
||||
} catch (...) {
|
||||
_show_default_pointer();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Pointer::Main::_handle_hover()
|
||||
{
|
||||
using Pointer::read_string_attribute;
|
||||
|
||||
_hover_ds->update();
|
||||
if (!_hover_ds->valid())
|
||||
return;
|
||||
|
||||
/* read new hover information from nitpicker's hover report */
|
||||
try {
|
||||
Genode::Xml_node node(_hover_ds->local_addr<char>());
|
||||
|
||||
String hovered_label = read_string_attribute(node, "label", String());
|
||||
|
||||
if (_verbose)
|
||||
Genode::log("hovered_label: ", hovered_label);
|
||||
|
||||
/* update pointer if hovered label changed */
|
||||
if (hovered_label != _hovered_label) {
|
||||
_hovered_label = hovered_label;
|
||||
_update_pointer();
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
Genode::warning("could not parse hover report");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Pointer::Main::_handle_xray()
|
||||
{
|
||||
_xray_ds->update();
|
||||
if (!_xray_ds->valid())
|
||||
return;
|
||||
|
||||
try {
|
||||
Genode::Xml_node node(_xray_ds->local_addr<char>());
|
||||
|
||||
bool xray = node.attribute_value("enabled", false);
|
||||
|
||||
/* update pointer if xray status changed */
|
||||
if (xray != _xray) {
|
||||
_xray = xray;
|
||||
_update_pointer();
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
Genode::warning("could not parse xray report");
|
||||
}
|
||||
}
|
||||
|
||||
Pointer::Main::Main(Genode::Env &env) : _env(env)
|
||||
{
|
||||
/*
|
||||
* 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 { Pointer::MAX_WIDTH, Pointer::MAX_HEIGHT,
|
||||
Framebuffer::Mode::RGB565 };
|
||||
|
||||
_nitpicker.buffer(mode, true /* use alpha */);
|
||||
|
||||
if (_shapes_enabled) {
|
||||
try {
|
||||
_hover_ds.construct(_env, "hover");
|
||||
_hover_ds->sigh(_hover_signal_handler);
|
||||
_handle_hover();
|
||||
} catch (Genode::Rom_connection::Rom_connection_failed) {
|
||||
Genode::warning("Could not open ROM session for \"hover\".",
|
||||
" This ROM is used for custom pointer shape support.");
|
||||
}
|
||||
|
||||
try {
|
||||
_xray_ds.construct(_env, "xray");
|
||||
_xray_ds->sigh(_xray_signal_handler);
|
||||
_handle_xray();
|
||||
} catch (Genode::Rom_connection::Rom_connection_failed) {
|
||||
Genode::warning("Could not open ROM session for \"xray\".",
|
||||
" This ROM is used for custom pointer shape support.");
|
||||
}
|
||||
}
|
||||
|
||||
_nitpicker.enqueue<Nitpicker::Session::Command::To_front>(_view);
|
||||
_nitpicker.execute();
|
||||
|
||||
_update_pointer();
|
||||
|
||||
if (_shapes_enabled) {
|
||||
/* announce 'Report' service */
|
||||
env.parent().announce(env.ep().manage(_report_root));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Component::construct(Genode::Env &env) { static Pointer::Main main(env); }
|
||||
|
137
repos/os/src/app/pointer/rom_registry.h
Normal file
137
repos/os/src/app/pointer/rom_registry.h
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* \brief Registry of ROM modules
|
||||
* \author Norman Feske
|
||||
* \date 2014-01-11
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2014-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _ROM_REGISTRY_H_
|
||||
#define _ROM_REGISTRY_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <report_rom/rom_registry.h>
|
||||
#include <os/session_policy.h>
|
||||
|
||||
namespace Rom { struct Registry; }
|
||||
|
||||
|
||||
struct Rom::Registry : Registry_for_reader, Registry_for_writer, Genode::Noncopyable
|
||||
{
|
||||
private:
|
||||
|
||||
Genode::Allocator &_md_alloc;
|
||||
Genode::Ram_session &_ram;
|
||||
Genode::Region_map &_rm;
|
||||
Reader &_reader;
|
||||
|
||||
Module_list _modules;
|
||||
|
||||
struct Read_write_policy : Module::Read_policy, Module::Write_policy
|
||||
{
|
||||
bool read_permitted(Module const &,
|
||||
Writer const &,
|
||||
Reader const &) const override
|
||||
{
|
||||
/*
|
||||
* The pointer application is always allowed to read the ROM content.
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
bool write_permitted(Module const &, Writer const &) const override
|
||||
{
|
||||
/*
|
||||
* Because the report-session label is used as the module name
|
||||
* for the writer, each report session refers to a distinct
|
||||
* module. Report client can write to their respective modules
|
||||
* at any time.
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
} _read_write_policy;
|
||||
|
||||
Module &_lookup(Module::Name const name, bool create_if_not_found)
|
||||
{
|
||||
for (Module *m = _modules.first(); m; m = m->next())
|
||||
if (m->_has_name(name))
|
||||
return *m;
|
||||
|
||||
if (!create_if_not_found)
|
||||
throw Genode::Service_denied();
|
||||
|
||||
/* module does not exist yet, create one */
|
||||
|
||||
/* XXX proper accounting for the used memory is missing */
|
||||
/* XXX if we run out of memory, the server will abort */
|
||||
|
||||
Module * const module = new (&_md_alloc)
|
||||
Module(_ram, _rm, name, _read_write_policy, _read_write_policy);
|
||||
|
||||
_modules.insert(module);
|
||||
return *module;
|
||||
}
|
||||
|
||||
void _try_to_destroy(Module const &module)
|
||||
{
|
||||
if (module._in_use())
|
||||
return;
|
||||
|
||||
_modules.remove(&module);
|
||||
Genode::destroy(&_md_alloc, const_cast<Module *>(&module));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Registry(Genode::Allocator &md_alloc,
|
||||
Genode::Ram_session &ram, Genode::Region_map &rm,
|
||||
Reader &reader)
|
||||
:
|
||||
_md_alloc(md_alloc), _ram(ram), _rm(rm), _reader(reader)
|
||||
{ }
|
||||
|
||||
Module &lookup(Writer &writer, Module::Name const &name) override
|
||||
{
|
||||
Module &module = _lookup(name, true);
|
||||
|
||||
module._register(writer);
|
||||
|
||||
/*
|
||||
* Enforce invariant that each module can have only one writer at a
|
||||
* time.
|
||||
*/
|
||||
if (module._num_writers() > 1) {
|
||||
release(writer, module);
|
||||
throw Genode::Service_denied();
|
||||
}
|
||||
|
||||
module._register(_reader);
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
void release(Writer &writer, Module &module) override
|
||||
{
|
||||
module._unregister(_reader);
|
||||
module._unregister(writer);
|
||||
_try_to_destroy(module);
|
||||
}
|
||||
|
||||
Readable_module &lookup(Reader &reader, Module::Name const &rom_label) override
|
||||
{
|
||||
return _lookup(rom_label, false);
|
||||
}
|
||||
|
||||
void release(Reader &reader, Readable_module &module) override
|
||||
{
|
||||
_try_to_destroy(static_cast<Module&>(module));
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _ROM_REGISTRY_H_ */
|
42
repos/os/src/app/pointer/util.h
Normal file
42
repos/os/src/app/pointer/util.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* \brief VirtualBox pointer utilities
|
||||
* \author Christian Helmuth
|
||||
* \date 2015-06-08
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _POINTER_UTIL_H_
|
||||
#define _POINTER_UTIL_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/xml_node.h>
|
||||
#include <util/string.h>
|
||||
|
||||
|
||||
namespace Pointer {
|
||||
typedef Genode::String<64> String;
|
||||
|
||||
inline String read_string_attribute(Genode::Xml_node const &node,
|
||||
char const *attr,
|
||||
String const &default_value);
|
||||
}
|
||||
|
||||
|
||||
Pointer::String 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(Genode::Cstring(buf));
|
||||
} catch (...) { return default_value; }
|
||||
}
|
||||
|
||||
#endif /* _POINTER_UTIL_H_ */
|
194
repos/os/src/test/pointer/main.cc
Normal file
194
repos/os/src/test/pointer/main.cc
Normal file
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* \brief Pointer shape reporter test
|
||||
* \author Christian Helmuth
|
||||
* \date 2015-06-03
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#include <base/component.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <util/string.h>
|
||||
#include <os/reporter.h>
|
||||
#include <pointer/shape_report.h>
|
||||
|
||||
struct Shape
|
||||
{
|
||||
enum { WIDTH = 16, HEIGHT = 16 };
|
||||
|
||||
typedef Genode::String<16> Id;
|
||||
|
||||
Id const id;
|
||||
unsigned const x_hot;
|
||||
unsigned const y_hot;
|
||||
unsigned char const map[WIDTH*HEIGHT];
|
||||
|
||||
void print(Genode::Output &output) const
|
||||
{
|
||||
Genode::print(output, ".", id, ".", x_hot, ".", y_hot, ".");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
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(Genode::Xml_node config)
|
||||
{
|
||||
Shape::Id const id = config.attribute_value("shape", Shape::Id("arrow"));
|
||||
|
||||
for (Shape const &s : shape)
|
||||
if (s.id == id)
|
||||
return s;
|
||||
|
||||
/* not found -> use first as default */
|
||||
return shape[0];
|
||||
}
|
||||
|
||||
|
||||
struct Main
|
||||
{
|
||||
Genode::Env &_env;
|
||||
|
||||
Pointer::Shape_report _shape_report {
|
||||
true, 0, 0, Shape::WIDTH, Shape::HEIGHT, { 0 } };
|
||||
|
||||
Genode::Reporter _reporter {
|
||||
_env, "shape", "shape", sizeof(Pointer::Shape_report) };
|
||||
|
||||
Genode::Signal_handler<Main> _config_handler {
|
||||
_env.ep(), *this, &Main::_handle_config };
|
||||
|
||||
Genode::Attached_rom_dataspace _config { _env, "config" };
|
||||
|
||||
void _handle_config()
|
||||
{
|
||||
_config.update();
|
||||
|
||||
Shape const &shape = select_shape(_config.xml());
|
||||
|
||||
_shape_report.x_hot = shape.x_hot;
|
||||
_shape_report.y_hot = shape.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_report.shape[(y*w + x)*4 + 0] = 0xff;
|
||||
_shape_report.shape[(y*w + x)*4 + 1] = 0xff;
|
||||
_shape_report.shape[(y*w + x)*4 + 2] = 0xff;
|
||||
_shape_report.shape[(y*w + x)*4 + 3] = shape.map[y*w +x] ? 0xe0 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
_reporter.report(&_shape_report, sizeof(Pointer::Shape_report));
|
||||
}
|
||||
|
||||
Main(Genode::Env &env) : _env(env)
|
||||
{
|
||||
_reporter.enabled(true);
|
||||
_config.sigh(_config_handler);
|
||||
_handle_config();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void Component::construct(Genode::Env &env)
|
||||
{
|
||||
/* XXX execute constructors of global statics */
|
||||
env.exec_static_constructors();
|
||||
|
||||
static Main main(env);
|
||||
}
|
3
repos/os/src/test/pointer/target.mk
Normal file
3
repos/os/src/test/pointer/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = test-pointer
|
||||
SRC_CC = main.cc
|
||||
LIBS = base
|
Reference in New Issue
Block a user