/* * \brief Menu view * \author Norman Feske * \date 2009-09-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. */ /* local includes */ #include "widgets.h" /* Genode includes */ #include #include /* gems includes */ #include struct Menu_view::Main { Env &_env; Nitpicker::Connection _nitpicker { _env }; Constructible _buffer; Nitpicker::Session::View_handle _view_handle = _nitpicker.create_view(); Point _position; Rect _view_geometry; void _update_view() { if (_view_geometry.p1() == _position && _view_geometry.area() == _buffer->size()) return; /* display view behind all others */ typedef Nitpicker::Session::Command Command; _view_geometry = Rect(_position, _buffer->size()); _nitpicker.enqueue(_view_handle, _view_geometry); _nitpicker.enqueue(_view_handle); _nitpicker.execute(); } /** * Function called on config change or mode change */ void _handle_dialog_update(); Signal_handler
_dialog_update_handler = { _env.ep(), *this, &Main::_handle_dialog_update}; Heap _heap { _env.ram(), _env.rm() }; Style_database _styles { _env.ram(), _env.rm(), _heap }; Animator _animator; Widget_factory _widget_factory { _heap, _styles, _animator }; Root_widget _root_widget { _widget_factory, Xml_node(""), Widget::Unique_id() }; Attached_rom_dataspace _dialog_rom { _env, "dialog" }; Attached_dataspace _input_ds { _env.rm(), _nitpicker.input()->dataspace() }; Widget::Unique_id _hovered; Attached_rom_dataspace _config { _env, "config" }; void _handle_config(); Signal_handler
_config_handler = { _env.ep(), *this, &Main::_handle_config}; void _handle_input(); Signal_handler
_input_handler = { _env.ep(), *this, &Main::_handle_input}; /* * Timer used for animating widgets */ struct Frame_timer : Timer::Connection { enum { PERIOD = 10 }; unsigned curr_frame() const { return elapsed_ms() / PERIOD; } void schedule() { trigger_once(Frame_timer::PERIOD*1000); } Frame_timer(Env &env) : Timer::Connection(env) { } } _timer { _env }; void _handle_frame_timer(); Signal_handler
_frame_timer_handler = { _env.ep(), *this, &Main::_handle_frame_timer}; Genode::Reporter _hover_reporter = { _env, "hover" }; bool _schedule_redraw = false; /** * Frame of last call of 'handle_frame_timer' */ unsigned _last_frame = 0; /** * Number of frames between two redraws */ enum { REDRAW_PERIOD = 4 }; /** * Counter used for triggering redraws. Incremented in each frame-timer * period, wraps at 'REDRAW_PERIOD'. The redraw is performed when the * counter wraps. */ unsigned _frame_cnt = 0; Main(Env &env) : _env(env) { _dialog_rom.sigh(_dialog_update_handler); _config.sigh(_config_handler); _nitpicker.input()->sigh(_input_handler); _timer.sigh(_frame_timer_handler); /* apply initial configuration */ _handle_config(); } }; void Menu_view::Main::_handle_dialog_update() { try { _position = Decorator::point_attribute(_config.xml()); } catch (...) { } _dialog_rom.update(); try { Xml_node dialog_xml(_dialog_rom.local_addr()); _root_widget.update(dialog_xml); _root_widget.size(_root_widget.min_size()); } catch (...) { Genode::error("failed to construct widget tree"); } _schedule_redraw = true; /* * If we have not processed a period for at least one frame, perform the * processing immediately. This way, we avoid latencies when the dialog * model is updated sporadically. */ if (_timer.curr_frame() != _last_frame) _handle_frame_timer(); else _timer.schedule(); } void Menu_view::Main::_handle_config() { _config.update(); try { _hover_reporter.enabled(_config.xml().sub_node("report") .attribute_value("hover", false)); } catch (...) { _hover_reporter.enabled(false); } _handle_dialog_update(); } void Menu_view::Main::_handle_input() { _nitpicker.input()->for_each_event([&] (Input::Event const &ev) { if (ev.absolute_motion()) { Point const at = Point(ev.ax(), ev.ay()) - _position; Widget::Unique_id const new_hovered = _root_widget.hovered(at); if (_hovered != new_hovered) { if (_hover_reporter.enabled()) { Genode::Reporter::Xml_generator xml(_hover_reporter, [&] () { _root_widget.gen_hover_model(xml, at); }); } _hovered = new_hovered; } } /* * Reset hover model when losing the focus */ if ((ev.type() == Input::Event::FOCUS && ev.code() == 0) || (ev.type() == Input::Event::LEAVE)) { _hovered = Widget::Unique_id(); if (_hover_reporter.enabled()) { Genode::Reporter::Xml_generator xml(_hover_reporter, [&] () { }); } } }); } void Menu_view::Main::_handle_frame_timer() { _frame_cnt++; unsigned const curr_frame = _timer.curr_frame(); if (_animator.active()) { unsigned const passed_frames = curr_frame - _last_frame; if (passed_frames > 0) { for (unsigned i = 0; i < passed_frames; i++) _animator.animate(); _schedule_redraw = true; } } _last_frame = curr_frame; if (_schedule_redraw && _frame_cnt >= REDRAW_PERIOD) { _frame_cnt = 0; Area const old_size = _buffer.constructed() ? _buffer->size() : Area(); Area const size = _root_widget.min_size(); if (!_buffer.constructed() || size != old_size) _buffer.construct(_nitpicker, size, _env.ram(), _env.rm()); else _buffer->reset_surface(); _root_widget.size(size); _root_widget.position(Point(0, 0)); Surface pixel_surface = _buffer->pixel_surface(); Surface alpha_surface = _buffer->alpha_surface(); // XXX restrict redraw to dirty regions // don't perform a full dialog update _root_widget.draw(pixel_surface, alpha_surface, Point(0, 0)); _buffer->flush_surface(); _nitpicker.framebuffer()->refresh(0, 0, _buffer->size().w(), _buffer->size().h()); _update_view(); _schedule_redraw = false; } /* * Deactivate timer periods when idle, activate timer when an animation is * in progress or a redraw is pending. */ bool const redraw_pending = _schedule_redraw && _frame_cnt != 0; if (_animator.active() || redraw_pending) _timer.schedule(); } /* * Silence debug messages */ extern "C" void _sigprocmask() { } void Libc::Component::construct(Libc::Env &env) { static Menu_view::Main main(env); }