input_filter: prepare for refactorization

This patch brings the input filter into a shape that is easier to
re-mold into an event filter, reversing the client/server roles of
the component.

* The 'Sink &destination' is no longer passed as constructor argument
  to the individual filters but passed as argument to the 'generate'
  method. This way, the final destination does not need to exist at
  the construction time of the filter chain but can be created on
  the fly (clearing the way for using 'Event::Client::with_batch').

* A new 'Source::Filter' interface with the 'apply' method aids the
  cascading of filters during 'generate'. The modules now implement
  the 'Source::Filter::filter_event' interface instead of the
  'Source::Sink::submit_event' interface.

* Since the 'Sink &destination' is no longer a member of the filter
  modules, character-repeat events can no longer be emitted in an
  ad-hoc way. Instead, the character-repeat mechanism now invokes
  a new 'Trigger::trigger_generate' hook that prompts the execution
  of the regular 'generate' mechanism by the main program.

This patch is supposed to leave the semantics of the input filter
unchanged (validated by the input_filter.run script).

Issue #3827
This commit is contained in:
Norman Feske 2020-07-15 19:50:42 +02:00
parent 6c4ce86a34
commit c502e1d095
8 changed files with 153 additions and 92 deletions

View File

@ -25,7 +25,7 @@
namespace Input_filter { class Accelerate_source; }
class Input_filter::Accelerate_source : public Source, Source::Sink
class Input_filter::Accelerate_source : public Source, Source::Filter
{
private:
@ -33,8 +33,6 @@ class Input_filter::Accelerate_source : public Source, Source::Sink
Source &_source;
Source::Sink &_destination;
/**
* Look-up table used for the non-linear acceleration of motion values
*/
@ -84,9 +82,9 @@ class Input_filter::Accelerate_source : public Source, Source::Sink
}
/**
* Sink interface
* Filter interface
*/
void submit_event(Input::Event const &event) override
void filter_event(Sink &destination, Input::Event const &event) override
{
using namespace Input;
@ -95,26 +93,27 @@ class Input_filter::Accelerate_source : public Source, Source::Sink
ev.handle_relative_motion([&] (int x, int y) {
ev = Relative_motion{_apply_acceleration(x),
_apply_acceleration(y)}; });
_destination.submit_event(ev);
destination.submit_event(ev);
}
public:
static char const *name() { return "accelerate"; }
Accelerate_source(Owner &owner, Xml_node config, Source::Sink &destination,
Source::Factory &factory)
Accelerate_source(Owner &owner, Xml_node config, Source::Factory &factory)
:
Source(owner),
_owner(factory),
_source(factory.create_source(_owner, input_sub_node(config), *this)),
_destination(destination),
_source(factory.create_source(_owner, input_sub_node(config))),
_lut (config.attribute_value("curve", 127L)),
_sensitivity_percent(config.attribute_value("sensitivity_percent", 100L)),
_max (config.attribute_value("max", 20L))
{ }
void generate() override { _source.generate(); }
void generate(Sink &destination) override
{
Source::Filter::apply(destination, *this, _source);
}
};
#endif /* _INPUT_FILTER__ACCELERATE_SOURCE_H_ */

View File

@ -24,7 +24,7 @@
namespace Input_filter { class Button_scroll_source; }
class Input_filter::Button_scroll_source : public Source, Source::Sink
class Input_filter::Button_scroll_source : public Source, Source::Filter
{
private:
@ -147,12 +147,10 @@ class Input_filter::Button_scroll_source : public Source, Source::Sink
Source &_source;
Source::Sink &_destination;
/**
* Sink interface
* Filter interface
*/
void submit_event(Input::Event const &event) override
void filter_event(Sink &destination, Input::Event const &event) override
{
using Input::Event;
@ -169,7 +167,7 @@ class Input_filter::Button_scroll_source : public Source, Source::Sink
wheel_y = _vertical_wheel .pending_motion();
if (wheel_x || wheel_y)
_destination.submit_event(Input::Wheel{wheel_x, wheel_y});
destination.submit_event(Input::Wheel{wheel_x, wheel_y});
/*
* Submit both press event and release event of magic button at
@ -187,8 +185,8 @@ class Input_filter::Button_scroll_source : public Source, Source::Sink
if (_vertical_wheel .handle_deactivation(event)
| _horizontal_wheel.handle_deactivation(event)) {
_destination.submit_event(Input::Press{key});
_destination.submit_event(Input::Release{key});
destination.submit_event(Input::Press{key});
destination.submit_event(Input::Release{key});
}
});
return;
@ -198,7 +196,7 @@ class Input_filter::Button_scroll_source : public Source, Source::Sink
if (_vertical_wheel .suppressed(event)) return;
if (_horizontal_wheel.suppressed(event)) return;
_destination.submit_event(event);
destination.submit_event(event);
}
static Xml_node _sub_node(Xml_node node, char const *type)
@ -211,18 +209,19 @@ class Input_filter::Button_scroll_source : public Source, Source::Sink
static char const *name() { return "button-scroll"; }
Button_scroll_source(Owner &owner, Xml_node config, Source::Sink &destination,
Source::Factory &factory)
Button_scroll_source(Owner &owner, Xml_node config, Source::Factory &factory)
:
Source(owner),
_vertical_wheel (_sub_node(config, "vertical")),
_horizontal_wheel(_sub_node(config, "horizontal")),
_owner(factory),
_source(factory.create_source(_owner, input_sub_node(config), *this)),
_destination(destination)
_source(factory.create_source(_owner, input_sub_node(config)))
{ }
void generate() override { _source.generate(); }
void generate(Sink &destination) override
{
Source::Filter::apply(destination, *this, _source);
}
};
#endif /* _INPUT_FILTER__BUTTON_SCROLL_SOURCE_H_ */

View File

@ -25,7 +25,7 @@
namespace Input_filter { class Chargen_source; }
class Input_filter::Chargen_source : public Source, Source::Sink
class Input_filter::Chargen_source : public Source, Source::Filter
{
private:
@ -514,19 +514,19 @@ class Input_filter::Chargen_source : public Source, Source::Sink
Owner _owner;
Source::Sink &_destination;
/**
* Mechanism for periodically repeating the last character
*/
struct Char_repeater
{
Source::Sink &_destination;
Timer::Connection &_timer;
Source::Trigger &_trigger;
Microseconds const _delay;
Microseconds const _rate;
unsigned _pending_event_count = 0;
Codepoint _curr_character { Codepoint::INVALID };
enum State { IDLE, REPEAT } _state { IDLE };
@ -534,45 +534,57 @@ class Input_filter::Chargen_source : public Source, Source::Sink
void _handle_timeout(Duration)
{
if (_state == REPEAT) {
_destination.submit_event(Input::Press_char{Input::KEY_UNKNOWN,
_curr_character});
_destination.submit_event(Input::Release{Input::KEY_UNKNOWN});
_pending_event_count++;
_timeout.schedule(_rate);
}
_trigger.trigger_generate();
}
void emit_events(Source::Sink &destination)
{
for (unsigned i = 0; i < _pending_event_count; i++) {
destination.submit_event(Input::Press_char{Input::KEY_UNKNOWN,
_curr_character});
destination.submit_event(Input::Release{Input::KEY_UNKNOWN});
}
_pending_event_count = 0;
}
Timer::One_shot_timeout<Char_repeater> _timeout {
_timer, *this, &Char_repeater::_handle_timeout };
Char_repeater(Source::Sink &destination, Timer::Connection &timer,
Xml_node node)
Char_repeater(Timer::Connection &timer, Xml_node node, Source::Trigger &trigger)
:
_destination(destination), _timer(timer),
_timer(timer), _trigger(trigger),
_delay(node.attribute_value("delay_ms", (uint64_t)0)*1000),
_rate (node.attribute_value("rate_ms", (uint64_t)0)*1000)
{ }
void schedule_repeat(Codepoint character)
{
_curr_character = character;
_state = REPEAT;
_curr_character = character;
_state = REPEAT;
_pending_event_count = 0;
_timeout.schedule(_delay);
}
void cancel()
{
_curr_character = Codepoint { Codepoint::INVALID };
_state = IDLE;
_curr_character = Codepoint { Codepoint::INVALID };
_state = IDLE;
_pending_event_count = 0;
}
};
Constructible<Char_repeater> _char_repeater { };
/**
* Sink interface (called from our child node)
* Filter interface
*/
void submit_event(Event const &event) override
void filter_event(Sink &destination, Event const &event) override
{
Event ev = event;
@ -607,11 +619,13 @@ class Input_filter::Chargen_source : public Source, Source::Sink
});
/* forward filtered event */
_destination.submit_event(ev);
destination.submit_event(ev);
}
Source &_source;
Source::Trigger &_trigger;
void _apply_config(Xml_node const config, unsigned const max_recursion = 4)
{
config.for_each_sub_node([&] (Xml_node node) {
@ -669,8 +683,7 @@ class Input_filter::Chargen_source : public Source, Source::Sink
* Instantiate character repeater on demand
*/
if (node.type() == "repeat") {
_char_repeater.construct(_destination,
_timer_accessor.timer(), node);
_char_repeater.construct(_timer_accessor.timer(), node, _trigger);
return;
}
@ -706,11 +719,11 @@ class Input_filter::Chargen_source : public Source, Source::Sink
Chargen_source(Owner &owner,
Xml_node config,
Source::Sink &destination,
Source::Factory &factory,
Allocator &alloc,
Timer_accessor &timer_accessor,
Include_accessor &include_accessor)
Include_accessor &include_accessor,
Source::Trigger &trigger)
:
Source(owner),
_alloc(alloc),
@ -719,8 +732,8 @@ class Input_filter::Chargen_source : public Source, Source::Sink
_key_map(_alloc),
_sequencer(_alloc),
_owner(factory),
_destination(destination),
_source(factory.create_source(_owner, input_sub_node(config), *this))
_source(factory.create_source(_owner, input_sub_node(config))),
_trigger(trigger)
{
_apply_config(config);
@ -738,7 +751,14 @@ class Input_filter::Chargen_source : public Source, Source::Sink
destroy(_alloc, &mod_rom); });
}
void generate() override { _source.generate(); }
void generate(Sink &destination) override
{
if (_char_repeater.constructed())
_char_repeater->emit_events(destination);
Source::Filter::apply(destination, *this, _source);
}
};
#endif /* _INPUT_FILTER__CHARGEN_SOURCE_H_ */

View File

@ -26,21 +26,20 @@ class Input_filter::Input_source : public Source
private:
Input_connection &_connection;
Sink &_destination;
public:
static char const *name() { return "input"; }
Input_source(Owner &owner, Input_connection &connection, Sink &destination)
Input_source(Owner &owner, Input_connection &connection)
:
Source(owner), _connection(connection), _destination(destination)
Source(owner), _connection(connection)
{ }
void generate() override
void generate(Sink &sink) override
{
_connection.for_each_event([&] (Input::Event const &event) {
_destination.submit_event(event); });
sink.submit_event(event); });
}
};

View File

@ -31,7 +31,7 @@ namespace Input_filter { struct Main; }
struct Input_filter::Main : Input_connection::Avail_handler,
Source::Factory
Source::Factory, Source::Trigger
{
Env &_env;
@ -201,7 +201,7 @@ struct Input_filter::Main : Input_connection::Avail_handler,
*
* \throw Source::Invalid_config
*/
Source &create_source(Source::Owner &owner, Xml_node node, Source::Sink &sink) override
Source &create_source(Source::Owner &owner, Xml_node node) override
{
/*
* Guard for the protection against too deep recursions while
@ -234,7 +234,7 @@ struct Input_filter::Main : Input_connection::Avail_handler,
match = &connection; });
if (match)
return *new (_heap) Input_source(owner, *match, sink);
return *new (_heap) Input_source(owner, *match);
warning("input named '", label, "' does not exist");
throw Source::Invalid_config();
@ -242,21 +242,22 @@ struct Input_filter::Main : Input_connection::Avail_handler,
/* create regular filter */
if (node.type() == Remap_source::name())
return *new (_heap) Remap_source(owner, node, sink, *this,
return *new (_heap) Remap_source(owner, node, *this,
_include_accessor);
if (node.type() == Merge_source::name())
return *new (_heap) Merge_source(owner, node, sink, *this);
return *new (_heap) Merge_source(owner, node, *this);
if (node.type() == Chargen_source::name())
return *new (_heap) Chargen_source(owner, node, sink, *this, _heap,
_timer_accessor, _include_accessor);
return *new (_heap) Chargen_source(owner, node, *this, _heap,
_timer_accessor, _include_accessor,
*this);
if (node.type() == Button_scroll_source::name())
return *new (_heap) Button_scroll_source(owner, node, sink, *this);
return *new (_heap) Button_scroll_source(owner, node, *this);
if (node.type() == Accelerate_source::name())
return *new (_heap) Accelerate_source(owner, node, sink, *this);
return *new (_heap) Accelerate_source(owner, node, *this);
warning("unknown <", node.type(), "> input-source node type");
throw Source::Invalid_config();
@ -298,13 +299,13 @@ struct Input_filter::Main : Input_connection::Avail_handler,
* \throw Source::Invalid_config
* \throw Genode::Out_of_memory
*/
Output(Xml_node output, Source::Sink &sink, Source::Factory &factory)
Output(Xml_node output, Source::Factory &factory)
:
_owner(factory),
_top_level(factory.create_source(_owner, Source::input_sub_node(output), sink))
_top_level(factory.create_source(_owner, Source::input_sub_node(output)))
{ }
void generate() { _top_level.generate(); }
void generate(Source::Sink &destination) { _top_level.generate(destination); }
};
Constructible<Output> _output { };
@ -344,7 +345,7 @@ struct Input_filter::Main : Input_connection::Avail_handler,
pending |= connection.pending(); });
if (pending && _output.constructed())
_output->generate();
_output->generate(_final_sink);
if (_config_update_pending && _input_connections_idle())
Signal_transmitter(_config_handler).submit();
@ -421,8 +422,7 @@ struct Input_filter::Main : Input_connection::Avail_handler,
try {
if (_config.xml().has_sub_node("output"))
_output.construct(_config.xml().sub_node("output"),
_final_sink, *this);
_output.construct(_config.xml().sub_node("output"), *this);
}
catch (Source::Invalid_config) {
warning("invalid <output> configuration"); }
@ -438,6 +438,17 @@ struct Input_filter::Main : Input_connection::Avail_handler,
Include_accessor _include_accessor { _env, _heap, _config_handler };
/**
* Source::Trigger interface
*
* Trigger emission of character-repeat events.
*/
void trigger_generate() override
{
if (_output.constructed())
_output->generate(_final_sink);
}
/**
* Constructor
*/

View File

@ -30,19 +30,19 @@ class Input_filter::Merge_source : public Source
static char const *name() { return "merge"; }
Merge_source(Owner &owner, Xml_node config, Sink &destination,
Source::Factory &factory)
Merge_source(Owner &owner, Xml_node config, Source::Factory &factory)
:
Source(owner), _owner(factory)
{
config.for_each_sub_node([&] (Xml_node node) {
if (input_node(node))
factory.create_source(_owner, node, destination); });
factory.create_source(_owner, node); });
}
void generate() override
void generate(Sink &destination) override
{
_owner.for_each([&] (Source &source) { source.generate(); });
_owner.for_each([&] (Source &source) {
source.generate(destination); });
}
};

View File

@ -25,7 +25,7 @@
namespace Input_filter { class Remap_source; }
class Input_filter::Remap_source : public Source, Source::Sink
class Input_filter::Remap_source : public Source, Source::Filter
{
private:
@ -42,18 +42,16 @@ class Input_filter::Remap_source : public Source, Source::Sink
Source &_source;
Source::Sink &_destination;
/**
* Sink interface
* Filter interface
*/
void submit_event(Input::Event const &event) override
void filter_event(Source::Sink &destination, 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);
destination.submit_event(event);
return;
}
@ -66,10 +64,10 @@ class Input_filter::Remap_source : public Source, Source::Sink
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}); });
destination.submit_event(Input::Press_char{remap(key), codepoint}); });
event.handle_release([&] (Input::Keycode key) {
_destination.submit_event(Input::Release{remap(key)}); });
destination.submit_event(Input::Release{remap(key)}); });
}
void _apply_config(Xml_node const config, unsigned const max_recursion = 4)
@ -126,14 +124,12 @@ class Input_filter::Remap_source : public Source, Source::Sink
static char const *name() { return "remap"; }
Remap_source(Owner &owner, Xml_node config, Source::Sink &destination,
Source::Factory &factory, Include_accessor &include_accessor)
Remap_source(Owner &owner, Xml_node config, 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)
_include_accessor(include_accessor), _owner(factory),
_source(factory.create_source(_owner, input_sub_node(config)))
{
for (unsigned i = 0; i < Input::KEY_MAX; i++)
_keys[i].code = Input::Keycode(i);
@ -141,7 +137,10 @@ class Input_filter::Remap_source : public Source, Source::Sink
_apply_config(config);
}
void generate() override { _source.generate(); }
void generate(Source::Sink &destination) override
{
Source::Filter::apply(destination, *this, _source);
}
};
#endif /* _INPUT_FILTER__REMAP_SOURCE_H_ */

View File

@ -65,8 +65,6 @@ class Input_filter::Source
throw Invalid_config { };
}
virtual void generate() = 0;
struct Owner;
struct Sink : Interface
@ -74,12 +72,48 @@ class Input_filter::Source
virtual void submit_event(Input::Event const &) = 0;
};
virtual void generate(Sink &) = 0;
/**
* Interface to time-trigger the generate mechanism independently from
* incoming events. This is used for emitting character repeat events.
*/
struct Trigger : Interface
{
virtual void trigger_generate() = 0;
};
struct Filter : Interface
{
virtual void filter_event(Sink &, Input::Event const &) = 0;
static void apply(Sink &destination, Filter &filter, Source &source)
{
struct Intermediate_sink : Sink
{
Sink &_destination;
Filter &_filter;
void submit_event(Input::Event const &event) override
{
_filter.filter_event(_destination, event);
}
Intermediate_sink(Sink &destination, Filter &filter)
: _destination(destination), _filter(filter) { }
} intermediate_sink { destination, filter };
source.generate(intermediate_sink);
}
};
struct Factory : Interface
{
/*
* \throw Invalid_config
*/
virtual Source &create_source(Owner &, Xml_node, Sink &) = 0;
virtual Source &create_source(Owner &, Xml_node) = 0;
virtual void destroy_source(Source &) = 0;
};