genode/repos/os/src/app/pointer/main.cc

383 lines
9.8 KiB
C++

/*
* \brief Nitpicker pointer with support for VirtualBox-defined shapes
* \author Norman Feske
* \author Christian Helmuth
* \author Christian Prochaska
* \date 2014-07-02
*/
/*
* 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.
*/
/* Genode includes */
#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 "rom_registry.h"
#include "big_mouse.h"
namespace Pointer { class Main; }
template <typename PT>
void convert_default_pointer_data_to_pixels(PT *pixel, Nitpicker::Area size)
{
unsigned char *alpha = (unsigned char *)(pixel + size.count());
for (unsigned y = 0; y < size.h(); y++) {
for (unsigned x = 0; x < size.w(); x++) {
/* the source is known to be in RGB565 format */
Genode::Pixel_rgb565 src =
*(Genode::Pixel_rgb565 *)(&big_mouse.pixels[y][x]);
unsigned const i = y*size.w() + x;
pixel[i] = PT(src.r(), src.g(), src.b());
alpha[i] = src.r() ? 255 : 0;
}
}
}
class Pointer::Main : public Rom::Reader
{
private:
typedef Genode::String<128> String;
Genode::Env &_env;
Genode::Attached_rom_dataspace _config { _env, "config" };
bool _verbose = _config.xml().attribute_value("verbose", false);
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 };
Genode::Session_label _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;
Nitpicker::Point shape_hot;
if (shape_report.visible) {
shape_size = Nitpicker::Area(shape_report.width, shape_report.height);
shape_hot = Nitpicker::Point((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;
}
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 { 0, 0, 0, 0, 0, 0 };
shape_module.read_content(*this, (char*)&shape_report, sizeof(shape_report));
/* show default pointer on invisible/empty/invalid shape report */
if (!shape_report.visible ||
((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()
{
_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>());
Genode::Session_label hovered_label {
node.attribute_value("label", String()) };
hovered_label = hovered_label.prefix();
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.");
}
}
typedef Nitpicker::Session::View_handle View_handle;
_nitpicker.enqueue<Nitpicker::Session::Command::To_front>(_view, View_handle());
_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); }