297 lines
6.4 KiB
C++
297 lines
6.4 KiB
C++
/*
|
|
* \brief Menu view
|
|
* \author Norman Feske
|
|
* \date 2009-09-11
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2014 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.
|
|
*/
|
|
|
|
/* local includes */
|
|
#include "widgets.h"
|
|
|
|
/* Genode includes */
|
|
#include <input/event.h>
|
|
#include <os/reporter.h>
|
|
|
|
/* gems includes */
|
|
#include <gems/nitpicker_buffer.h>
|
|
|
|
|
|
struct Menu_view::Main
|
|
{
|
|
Env &_env;
|
|
|
|
Nitpicker::Connection _nitpicker { _env };
|
|
|
|
Constructible<Nitpicker_buffer> _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<Command::Geometry>(_view_handle, _view_geometry);
|
|
_nitpicker.enqueue<Command::To_front>(_view_handle);
|
|
_nitpicker.execute();
|
|
}
|
|
|
|
/**
|
|
* Function called on config change or mode change
|
|
*/
|
|
void _handle_dialog_update();
|
|
|
|
Signal_handler<Main> _dialog_update_handler = {
|
|
_env.ep(), *this, &Main::_handle_dialog_update};
|
|
|
|
Style_database _styles;
|
|
|
|
Animator _animator;
|
|
|
|
Heap _heap { _env.ram(), _env.rm() };
|
|
|
|
Widget_factory _widget_factory { _heap, _styles, _animator };
|
|
|
|
Root_widget _root_widget { _widget_factory, Xml_node("<dialog/>"), Widget::Unique_id() };
|
|
|
|
Attached_rom_dataspace _dialog_rom { _env, "dialog" };
|
|
|
|
Attached_dataspace _input_ds { _nitpicker.input()->dataspace() };
|
|
|
|
Widget::Unique_id _hovered;
|
|
|
|
Attached_rom_dataspace _config { _env, "config" };
|
|
|
|
void _handle_config();
|
|
|
|
Signal_handler<Main> _config_handler = {
|
|
_env.ep(), *this, &Main::_handle_config};
|
|
|
|
void _handle_input();
|
|
|
|
Signal_handler<Main> _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<Main> _frame_timer_handler = {
|
|
_env.ep(), *this, &Main::_handle_frame_timer};
|
|
|
|
Genode::Reporter _hover_reporter = { "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<char>());
|
|
|
|
_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());
|
|
else
|
|
_buffer->reset_surface();
|
|
|
|
_root_widget.size(size);
|
|
_root_widget.position(Point(0, 0));
|
|
|
|
Surface<Pixel_rgb888> pixel_surface = _buffer->pixel_surface();
|
|
Surface<Pixel_alpha8> 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(Genode::Env &env) { static Menu_view::Main main(env); }
|
|
|