diff --git a/repos/os/run/input_filter.run b/repos/os/run/input_filter.run
index cc8e042d97..1f38a15a0a 100644
--- a/repos/os/run/input_filter.run
+++ b/repos/os/run/input_filter.run
@@ -57,6 +57,8 @@ append config {
report="test-input_filter -> chargen_include"/>
+
@@ -68,6 +70,7 @@ append config {
+
@@ -228,6 +231,7 @@ append_if [test_char_repeat] config {
}
append config {
+
@@ -236,26 +240,31 @@ append config {
-
-
+
+
-
-
-
-
-
-
-
+
+
+
-
+
+
+
-
+
+
+
diff --git a/repos/os/src/server/input_filter/README b/repos/os/src/server/input_filter/README
index fbf07aec6a..f44f3affa9 100644
--- a/repos/os/src/server/input_filter/README
+++ b/repos/os/src/server/input_filter/README
@@ -25,11 +25,7 @@ one of the following filters:
It may contain any number of '' nodes. Each of those key nodes has
the key name as 'name' attribute, may feature an optional 'to' attribute
- with the name of the key that should be reported instead of 'name', and
- an optional 'sticky' attribute. If the latter is set to "yes", the key
- behaves like a sticky key. That means, only press events are evaluated
- and every second press event is reported as a release event. This is
- useful for special keys like capslock.
+ with the name of the key that should be reported instead of 'name'.
::
@@ -57,8 +53,14 @@ sub nodes:
!
!
+ !
!
+ The '' node incorporates the content of the ROM module of the
+ specified name into the modifier state. If the ROM module contains a
+ top-level node with the attribute 'enabled' set to "yes", the modifier
+ is enabled. This is useful for handling a system-global capslock state.
+
::
A '' node contains a list of keys that emit a specified character when
diff --git a/repos/os/src/server/input_filter/chargen_source.h b/repos/os/src/server/input_filter/chargen_source.h
index a256e141cc..76869fbadf 100644
--- a/repos/os/src/server/input_filter/chargen_source.h
+++ b/repos/os/src/server/input_filter/chargen_source.h
@@ -71,6 +71,39 @@ class Input_filter::Chargen_source : public Source, Source::Sink
Registry _modifiers;
+ struct Modifier_rom
+ {
+ typedef String<32> Name;
+
+ Registry::Element _element;
+
+ Modifier::Id const _id;
+
+ bool _enabled = false;
+
+ Modifier_rom(Registry ®istry,
+ Modifier::Id id,
+ Include_accessor &include_accessor,
+ Name const &name)
+ :
+ _element(registry, *this), _id(id)
+ {
+ try {
+ include_accessor.apply_include(name, "capslock", [&] (Xml_node node) {
+ _enabled = node.attribute_value("enabled", false); }); }
+
+ catch (Include_accessor::Include_unavailable) {
+ warning("failed to obtain modifier state from "
+ "\"", name, "\" ROM module"); }
+ }
+
+ Modifier::Id id() const { return _id; }
+
+ bool enabled() const { return _enabled; }
+ };
+
+ Registry _modifier_roms;
+
/*
* Key rules for generating characters
*/
@@ -322,6 +355,11 @@ class Input_filter::Chargen_source : public Source, Source::Sink
_modifiers.for_each([&] (Modifier const &mod) {
_mod_map.states[mod.id()].enabled |=
_key_map.key(mod.code()).state; });
+
+ /* supplement modifier state provided by ROM modules */
+ _modifier_roms.for_each([&] (Modifier_rom const &mod_rom) {
+ _mod_map.states[mod_rom.id()].enabled |=
+ mod_rom.enabled(); });
}
Owner _owner;
@@ -432,7 +470,7 @@ class Input_filter::Chargen_source : public Source, Source::Sink
void _apply_sub_node(Xml_node const node, unsigned const max_recursion)
{
if (max_recursion == 0) {
- error("too deeply nested includes");
+ warning("too deeply nested includes");
throw Invalid_config();
}
@@ -483,6 +521,16 @@ class Input_filter::Chargen_source : public Source, Source::Sink
new (_alloc) Modifier(_modifiers, id, key);
});
+
+ node.for_each_sub_node("rom", [&] (Xml_node rom_node) {
+
+ typedef Modifier_rom::Name Rom_name;
+ Rom_name const rom_name = rom_node.attribute_value("name", Rom_name());
+
+ new (_alloc) Modifier_rom(_modifier_roms, id, _include_accessor, rom_name);
+ });
+
+ _update_modifier_state();
}
public:
@@ -515,7 +563,11 @@ class Input_filter::Chargen_source : public Source, Source::Sink
~Chargen_source()
{
- _modifiers.for_each([&] (Modifier &mod) { destroy(_alloc, &mod); });
+ _modifiers.for_each([&] (Modifier &mod) {
+ destroy(_alloc, &mod); });
+
+ _modifier_roms.for_each([&] (Modifier_rom &mod_rom) {
+ destroy(_alloc, &mod_rom); });
}
void generate() override { _source.generate(); }
diff --git a/repos/os/src/server/input_filter/key_code_by_name.h b/repos/os/src/server/input_filter/key_code_by_name.h
index 5f64b14e15..400db90def 100644
--- a/repos/os/src/server/input_filter/key_code_by_name.h
+++ b/repos/os/src/server/input_filter/key_code_by_name.h
@@ -34,8 +34,6 @@ namespace Input_filter {
if (name == Input::key_name(code))
return code;
}
-
- error("unknown key: ", name);
throw Unknown_key();
}
}
diff --git a/repos/os/src/server/input_filter/main.cc b/repos/os/src/server/input_filter/main.cc
index 0209f53c8d..1377bc6cb0 100644
--- a/repos/os/src/server/input_filter/main.cc
+++ b/repos/os/src/server/input_filter/main.cc
@@ -107,9 +107,6 @@ struct Input_filter::Main : Input_connection::Avail_handler,
_dataspace(env, name.string()), _reconfig_sigh(reconfig_sigh),
_rom_update_handler(env.ep(), *this, &Rom::_handle_rom_update)
{
- /* validate top-level node type */
- xml(type);
-
/* respond to ROM updates */
_dataspace.sigh(_rom_update_handler);
}
@@ -127,8 +124,8 @@ struct Input_filter::Main : Input_connection::Avail_handler,
if (node.type() == type)
return node;
- error("unexpected <", node.type(), "> node " "in included "
- "ROM \"", _name, "\", expected, <", type, "> node");
+ warning("unexpected <", node.type(), "> node " "in included "
+ "ROM \"", _name, "\", expected, <", type, "> node");
throw Include_unavailable();
}
};
@@ -170,11 +167,13 @@ struct Input_filter::Main : Input_connection::Avail_handler,
{
/* populate registry on demand */
if (!_exists(name)) {
- try { new (_alloc) Rom(_registry, _env, name, type, _sigh); }
- catch (...) {
- error("include \"", name, "\" unavailable");
- throw Include_unavailable();
+ try {
+ Rom &rom = *new (_alloc) Rom(_registry, _env, name, type, _sigh);
+
+ /* \throw Include_unavailable on mismatching top-level node type */
+ rom.xml(type);
}
+ catch (...) { throw Include_unavailable(); }
}
/* call 'fn' with the XML content of the named include */
@@ -214,7 +213,7 @@ struct Input_filter::Main : Input_connection::Avail_handler,
Nesting_level_guard(unsigned &level) : level(level)
{
if (level == 0) {
- error("too many nested input sources");
+ warning("too many nested input sources");
throw Source::Invalid_config();
}
level--;
@@ -236,7 +235,7 @@ struct Input_filter::Main : Input_connection::Avail_handler,
if (match)
return *new (_heap) Input_source(owner, *match, sink);
- error("input named '", label, "' does not exist");
+ warning("input named '", label, "' does not exist");
throw Source::Invalid_config();
}
@@ -251,7 +250,7 @@ struct Input_filter::Main : Input_connection::Avail_handler,
return *new (_heap) Chargen_source(owner, node, sink, *this, _heap,
_timer_accessor, _include_accessor);
- error("unknown <", node.type(), "> input-source node type");
+ warning("unknown <", node.type(), "> input-source node type");
throw Source::Invalid_config();
}
@@ -384,23 +383,23 @@ struct Input_filter::Main : Input_connection::Avail_handler,
new (_heap)
Registered(_input_connections, _env,
label, *this, _heap);
-
- } catch (Genode::Service_denied) {
- error("parent denied input source '", label, "'");
}
- } catch (Xml_node::Nonexistent_attribute) {
- error("ignoring invalid input node '", input_node);
+ catch (Genode::Service_denied) {
+ warning("parent denied input source '", label, "'"); }
}
+ catch (Xml_node::Nonexistent_attribute) {
+ warning("ignoring invalid input node '", input_node); }
});
try {
if (_config.xml().has_sub_node("output"))
_output.construct(_config.xml().sub_node("output"),
_final_sink, *this);
+ }
+ catch (Source::Invalid_config) {
+ warning("invalid configuration"); }
- } catch (Source::Invalid_config) {
- error("invalid configuration");
- } catch (Allocator::Out_of_memory) {
+ catch (Allocator::Out_of_memory) {
error("out of memory while constructing filter chain"); }
_config_update_pending = false;
diff --git a/repos/os/src/server/input_filter/remap_source.h b/repos/os/src/server/input_filter/remap_source.h
index def67caf3f..e3095a4983 100644
--- a/repos/os/src/server/input_filter/remap_source.h
+++ b/repos/os/src/server/input_filter/remap_source.h
@@ -31,14 +31,6 @@ class Input_filter::Remap_source : public Source, Source::Sink
struct Key
{
Input::Keycode code = Input::KEY_UNKNOWN;
- bool sticky = false;
-
- enum State { RELEASED, PRESSED } state = RELEASED;
-
- void toggle()
- {
- state = (state == PRESSED) ? RELEASED : PRESSED;
- }
};
Key _keys[Input::KEY_MAX];
@@ -68,27 +60,10 @@ class Input_filter::Remap_source : public Source, Source::Sink
return;
}
+ /* remap the key code */
Key &key = _keys[event.keycode()];
- Key::State const old_state = key.state;
-
- /* update key state, depending on the stickyness of the key */
- if (key.sticky) {
- if (event.type() == Event::PRESS)
- key.toggle();
- } else {
- key.state = (event.type() == Event::PRESS) ? Key::PRESSED
- : Key::RELEASED;
- }
-
- /* drop release events of sticky keys */
- if (key.state == old_state)
- return;
-
- Event::Type const type =
- key.state == Key::PRESSED ? Event::PRESS : Event::RELEASE;
-
- _destination.submit_event(Event(type, key.code, 0, 0, 0, 0));
+ _destination.submit_event(Event(event.type(), key.code, 0, 0, 0, 0));
}
public:
@@ -118,8 +93,6 @@ class Input_filter::Remap_source : public Source, Source::Sink
try { _keys[code].code = key_code_by_name(to); }
catch (Unknown_key) { warning("ignoring remap rule ", node); }
}
-
- _keys[code].sticky = node.attribute_value("sticky", false);
}
catch (Unknown_key) {
warning("invalid key name ", key_name); }
diff --git a/repos/os/src/server/input_filter/source.h b/repos/os/src/server/input_filter/source.h
index cf0502d164..3560d4d095 100644
--- a/repos/os/src/server/input_filter/source.h
+++ b/repos/os/src/server/input_filter/source.h
@@ -57,7 +57,7 @@ class Input_filter::Source
if (result.type() != "none")
return result;
- error("missing // sub node in ", node);
+ warning("missing input sub node in ", node);
throw Invalid_config { };
}
diff --git a/repos/os/src/test/input_filter/main.cc b/repos/os/src/test/input_filter/main.cc
index bdaf856290..24fd1e785c 100644
--- a/repos/os/src/test/input_filter/main.cc
+++ b/repos/os/src/test/input_filter/main.cc
@@ -235,9 +235,10 @@ struct Test::Main : Input_from_filter::Event_handler
Input_to_filter _input_to_filter { _env };
- Reporter _input_filter_config_reporter { _env, "config", "input_filter.config" };
- Reporter _chargen_include_reporter { _env, "chargen", "chargen_include" };
- Reporter _remap_include_reporter { _env, "remap", "remap_include" };
+ Reporter _input_filter_config_reporter { _env, "config", "input_filter.config" };
+ Reporter _chargen_include_reporter { _env, "chargen", "chargen_include" };
+ Reporter _remap_include_reporter { _env, "remap", "remap_include" };
+ Reporter _capslock_reporter { _env, "capslock", "capslock" };
Attached_rom_dataspace _config { _env, "config" };
@@ -319,6 +320,13 @@ struct Test::Main : Input_from_filter::Event_handler
continue;
}
+ if (step.type() == "capslock") {
+ Reporter::Xml_generator xml(_capslock_reporter, [&] () {
+ xml.attribute("enabled", step.attribute_value("enabled", false)); });
+ _advance_step();
+ continue;
+ }
+
if (step.type() == "usb" || step.type() == "ps2") {
_input_to_filter.submit_events(step);
_advance_step();
@@ -428,6 +436,7 @@ struct Test::Main : Input_from_filter::Event_handler
_input_filter_config_reporter.enabled(true);
_chargen_include_reporter.enabled(true);
_remap_include_reporter.enabled(true);
+ _capslock_reporter.enabled(true);
_execute_curr_step();
}
};