/* * \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 #include #include #include #include #include #include #include #include #include #include #include /* local includes */ #include "util.h" #include "rom_registry.h" #include "big_mouse.h" namespace Pointer { class Main; } template 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 Pointer::String 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 _hover_ds; Genode::Constructible _xray_ds; Genode::Signal_handler
_hover_signal_handler { _env.ep(), *this, &Main::_handle_hover }; Genode::Signal_handler
_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(), 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 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 texture(_texture_pixel_ds.local_addr(), _texture_alpha_ds.local_addr(), 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_alpha8 *alpha = reinterpret_cast(pixel + shape_size.count()); Surface pixel_surface(pixel, shape_size); Surface 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(_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 && ((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()); 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()); 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(_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); }