genode/repos/os/src/server/input_filter/remap_source.h

148 lines
3.5 KiB
C++

/*
* \brief Input-event source that remaps keys from another source
* \author Norman Feske
* \date 2017-02-01
*/
/*
* Copyright (C) 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.
*/
#ifndef _INPUT_FILTER__REMAP_SOURCE_H_
#define _INPUT_FILTER__REMAP_SOURCE_H_
/* Genode includes */
#include <input/keycodes.h>
/* local includes */
#include "include_accessor.h"
#include "source.h"
#include "key_code_by_name.h"
namespace Input_filter { class Remap_source; }
class Input_filter::Remap_source : public Source, Source::Sink
{
private:
Include_accessor &_include_accessor;
struct Key
{
Input::Keycode code = Input::KEY_UNKNOWN;
};
Key _keys[Input::KEY_MAX];
Owner _owner;
Source &_source;
Source::Sink &_destination;
/**
* Sink interface
*/
void submit_event(Input::Event const &event) override
{
using Input::Event;
/* forward events that are unrelated to the remapper */
if (!event.press() && !event.release()) {
_destination.submit_event(event);
return;
}
/*
* remap the key code for press and release events
*
* The range of the 'key' is checked by the 'Event' handle methods,
* so it is safe to use as array index.
*/
auto remap = [&] (Input::Keycode key) { return _keys[key].code; };
event.handle_press([&] (Input::Keycode key, Codepoint codepoint) {
_destination.submit_event(Input::Press_char{remap(key), codepoint}); });
event.handle_release([&] (Input::Keycode key) {
_destination.submit_event(Input::Release{remap(key)}); });
}
void _apply_config(Xml_node const config, unsigned const max_recursion = 4)
{
config.for_each_sub_node([&] (Xml_node node) {
_apply_sub_node(node, max_recursion); });
}
void _apply_sub_node(Xml_node const node, unsigned const max_recursion)
{
if (max_recursion == 0) {
warning("too deeply nested includes");
throw Invalid_config();
}
/*
* Handle includes
*/
if (node.type() == "include") {
try {
Include_accessor::Name const rom =
node.attribute_value("rom", Include_accessor::Name());
_include_accessor.apply_include(rom, name(), [&] (Xml_node inc) {
_apply_config(inc, max_recursion - 1); });
return;
}
catch (Include_accessor::Include_unavailable) {
throw Invalid_config(); }
}
/*
* Handle key nodes
*/
if (node.type() == "key") {
Key_name const key_name = node.attribute_value("name", Key_name());
try {
Input::Keycode const code = key_code_by_name(key_name);
if (node.has_attribute("to")) {
Key_name const to = node.attribute_value("to", Key_name());
try { _keys[code].code = key_code_by_name(to); }
catch (Unknown_key) { warning("ignoring remap rule ", node); }
}
}
catch (Unknown_key) {
warning("invalid key name ", key_name); }
return;
}
}
public:
static char const *name() { return "remap"; }
Remap_source(Owner &owner, Xml_node config, Source::Sink &destination,
Source::Factory &factory, Include_accessor &include_accessor)
:
Source(owner),
_include_accessor(include_accessor),
_owner(factory),
_source(factory.create_source(_owner, input_sub_node(config), *this)),
_destination(destination)
{
for (unsigned i = 0; i < Input::KEY_MAX; i++)
_keys[i].code = Input::Keycode(i);
_apply_config(config);
}
void generate() override { _source.generate(); }
};
#endif /* _INPUT_FILTER__REMAP_SOURCE_H_ */