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

258 lines
5.7 KiB
C++

/*
* \brief Policy for activating the X-Ray mode
* \author Norman Feske
* \date 2015-10-03
*/
/*
* Copyright (C) 2015-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/attached_rom_dataspace.h>
#include <nitpicker_session/connection.h>
#include <os/reporter.h>
#include <input/event.h>
#include <input/keycodes.h>
#include <timer_session/connection.h>
namespace Xray_trigger {
struct Main;
using namespace Genode;
}
struct Xray_trigger::Main
{
Env &_env;
/**
* Configuration buffer
*/
Attached_rom_dataspace _config { _env, "config" };
/**
* Nitpicker connection to obtain user input
*/
Nitpicker::Connection _nitpicker { _env, "input" };
/**
* Input-event buffer
*/
Attached_dataspace _ev_ds { _env.rm(), _nitpicker.input()->dataspace() };
/**
* Number of pressed keys, used to distinguish primary keys from key
* sequences.
*/
unsigned _key_cnt = 0;
/**
* Hover model as reported by nitpicker
*/
Constructible<Attached_rom_dataspace> _hover_ds;
/**
* Reporter for posting the result of our policy decision
*/
Reporter _xray_reporter { _env, "xray" };
/**
* Timer to delay the xray report
*/
Timer::Connection _timer { _env };
/**
* X-Ray criterion depending on key events
*/
bool _key_xray = false;
bool _key_xray_initialized = false;
/**
* X-Ray criterion depending on hovered domain
*/
bool _hover_xray = false;
bool _xray() const { return _key_xray || _hover_xray; }
bool _evaluate_input(bool, unsigned, Input::Event const [], unsigned &) const;
bool _evaluate_hover(Xml_node) const;
/**
* Handler that is called on config changes, on hover-model changes, or on
* the arrival of user input
*/
void _handle_update();
Signal_handler<Main> _update_handler =
{ _env.ep(), *this, &Main::_handle_update };
void _report_xray()
{
Reporter::Xml_generator xml(_xray_reporter, [&] () {
xml.attribute("enabled", _xray() ? "yes" : "no");
});
}
/**
* Handler that is called after the xray report delay
*/
Signal_handler<Main> _timeout_handler =
{ _env.ep(), *this, &Main::_report_xray };
Main(Env &env) : _env(env)
{
_config.sigh(_update_handler);
_timer.sigh(_timeout_handler);
/* enable xray reporter and produce initial xray report */
_xray_reporter.enabled(true);
_report_xray();
_nitpicker.input()->sigh(_update_handler);
_handle_update();
}
};
bool Xray_trigger::Main::_evaluate_input(bool key_xray, unsigned num_ev,
Input::Event const events[],
unsigned &key_cnt) const
{
/* adjust '_key_xray' according to user key input */
for (unsigned i = 0; i < num_ev; i++) {
Input::Event const &ev = events[i];
if (ev.type() != Input::Event::PRESS
&& ev.type() != Input::Event::RELEASE)
continue;
if (ev.type() == Input::Event::PRESS) key_cnt++;
if (ev.type() == Input::Event::RELEASE) key_cnt--;
/* ignore key combinations */
if (key_cnt > 1) continue;
typedef String<32> Key_name;
Key_name const ev_key_name(Input::key_name(ev.keycode()));
typedef Xml_node Xml_node;
auto lambda = [&] (Xml_node node) {
if (!node.has_type("press") && !node.has_type("release"))
return;
if (node.has_type("press") && ev.type() != Input::Event::PRESS)
return;
if (node.has_type("release") && ev.type() != Input::Event::RELEASE)
return;
/*
* XML node applies for current event type, check if the key
* matches.
*/
Key_name const cfg_key_name =
node.attribute_value("name", Key_name());
if (cfg_key_name != ev_key_name)
return;
/*
* Manipulate X-Ray mode as instructed by the XML node.
*/
if (node.attribute("xray").has_value("on"))
key_xray = true;
if (node.attribute("xray").has_value("off"))
key_xray = false;
if (node.attribute("xray").has_value("toggle"))
key_xray = !_key_xray;
};
_config.xml().for_each_sub_node(lambda);
}
return key_xray;
}
bool Xray_trigger::Main::_evaluate_hover(Xml_node nitpicker_hover) const
{
bool hover_xray = false;
using namespace Genode;
_config.xml().for_each_sub_node("hover", [&] (Xml_node node) {
typedef String<160> Domain;
Domain nitpicker_domain = nitpicker_hover.attribute_value("domain", Domain());
Domain expected_domain = node.attribute_value("domain", Domain());
if (nitpicker_domain == expected_domain)
hover_xray = true;
});
return hover_xray;
}
void Xray_trigger::Main::_handle_update()
{
_config.update();
/* define initial state once */
if (!_key_xray_initialized) {
_key_xray = _config.xml().attribute_value("initial", false);
_key_xray_initialized = true;
_timer.trigger_once(10000);
}
/* remember X-Ray mode prior applying the changes */
bool const orig_xray = _xray();
while (unsigned const num_ev = _nitpicker.input()->flush())
_key_xray = _evaluate_input(_key_xray, num_ev,
_ev_ds.local_addr<Input::Event const>(),
_key_cnt);
/* obtain / update hover model if needed */
if (_config.xml().has_sub_node("hover")) {
if (!_hover_ds.constructed()) {
_hover_ds.construct(_env, "hover");
_hover_ds->sigh(_update_handler);
}
_hover_ds->update();
}
try {
_hover_xray = _evaluate_hover(Xml_node(_hover_ds->local_addr<char>(),
_hover_ds->size()));
} catch (...) { }
/* generate new X-Ray report if the X-Ray mode changed */
if (_xray() != orig_xray)
_timer.trigger_once(125000);
}
/***************
** Component **
***************/
void Component::construct(Genode::Env &env) {
static Xray_trigger::Main main(env); }