genode/repos/gems/src/app/menu_view/main.cc

313 lines
6.8 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
{
Nitpicker::Connection nitpicker;
Lazy_volatile_object<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();
}
Signal_receiver &sig_rec;
/**
* Function called on config change or mode change
*/
void handle_dialog_update(unsigned);
Signal_dispatcher<Main> dialog_update_dispatcher = {
sig_rec, *this, &Main::handle_dialog_update};
Style_database styles;
Animator animator;
Widget_factory widget_factory { *env()->heap(), styles, animator };
Root_widget root_widget { widget_factory, Xml_node("<dialog/>"), Widget::Unique_id() };
Attached_rom_dataspace dialog_rom { "dialog" };
Attached_dataspace input_ds { nitpicker.input()->dataspace() };
Widget::Unique_id hovered;
void handle_config(unsigned);
Signal_dispatcher<Main> config_dispatcher = {
sig_rec, *this, &Main::handle_config};
void handle_input(unsigned);
Signal_dispatcher<Main> input_dispatcher = {
sig_rec, *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); }
} timer;
void handle_frame_timer(unsigned);
Signal_dispatcher<Main> frame_timer_dispatcher = {
sig_rec, *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(Signal_receiver &sig_rec) : sig_rec(sig_rec)
{
dialog_rom.sigh(dialog_update_dispatcher);
config()->sigh(config_dispatcher);
nitpicker.input()->sigh(input_dispatcher);
timer.sigh(frame_timer_dispatcher);
/* apply initial configuration */
handle_config(0);
}
};
void Menu_view::Main::handle_dialog_update(unsigned)
{
try {
position = Decorator::point_attribute(config()->xml_node());
} 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 (...) {
PERR("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(0);
else
timer.schedule();
}
void Menu_view::Main::handle_config(unsigned)
{
config()->reload();
try {
hover_reporter.enabled(config()->xml_node().sub_node("report")
.attribute("hover")
.has_value("yes"));
} catch (...) {
hover_reporter.enabled(false);
}
handle_dialog_update(0);
}
void Menu_view::Main::handle_input(unsigned)
{
Input::Event const *ev_buf = input_ds.local_addr<Input::Event>();
unsigned const num_events = nitpicker.input()->flush();
for (unsigned i = 0; i < num_events; i++) {
Input::Event ev = ev_buf[i];
if (ev.is_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.is_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.is_enabled()) {
Genode::Reporter::Xml_generator xml(hover_reporter, [&] () { });
}
}
}
}
void Menu_view::Main::handle_frame_timer(unsigned)
{
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.is_constructed() ? buffer->size() : Area();
Area const size = root_widget.min_size();
if (!buffer.is_constructed() || size != old_size)
buffer.construct(nitpicker, size, *env()->ram_session());
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() { }
int main(int argc, char **argv)
{
static Genode::Signal_receiver sig_rec;
static Menu_view::Main application(sig_rec);
/* process incoming signals */
for (;;) {
using namespace Genode;
Signal sig = sig_rec.wait_for_signal();
Signal_dispatcher_base *dispatcher =
dynamic_cast<Signal_dispatcher_base *>(sig.context());
if (dispatcher)
dispatcher->dispatch(sig.num());
}
}