diff --git a/repos/os/run/input_filter.run b/repos/os/run/input_filter.run index cc8e042d9..1f38a15a0 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 fbf07aec6..f44f3affa 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 a256e141c..76869fbad 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 5f64b14e1..400db90de 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 0209f53c8..1377bc6cb 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 def67caf3..e3095a498 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 cf0502d16..3560d4d09 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 bdaf85629..24fd1e785 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(); } };