diff --git a/repos/os/run/event_filter.run b/repos/os/run/event_filter.run index a860b3278b..08872ccfdc 100644 --- a/repos/os/run/event_filter.run +++ b/repos/os/run/event_filter.run @@ -88,6 +88,7 @@ append config { + @@ -483,6 +484,7 @@ append config { + @@ -502,6 +504,7 @@ append config { + @@ -524,6 +527,7 @@ append config { + @@ -545,6 +549,29 @@ append config { + + + + + + + + + + + + + + + + + + + + + + + diff --git a/repos/os/src/server/event_filter/README b/repos/os/src/server/event_filter/README index 134c95d1af..f01d745511 100644 --- a/repos/os/src/server/event_filter/README +++ b/repos/os/src/server/event_filter/README @@ -86,6 +86,14 @@ one of the following filters: touch input. The original touch events are preserved, enabling touch-aware applications to interpet touch gestures. +:: + + Triggers an artificial key tap (a press event followed by a release event) + when touching a preconfigured area on a touch screen. The filter node can + host any number of '' sub nodes. Each sub node must define a + rectangular area - using the attributes 'xpos', 'ypos', 'width', and + 'height' - and the name of the tapped key as 'key' attribute. + Character generator rules ------------------------- diff --git a/repos/os/src/server/event_filter/main.cc b/repos/os/src/server/event_filter/main.cc index e2fbcfda79..c9fae9fa7c 100644 --- a/repos/os/src/server/event_filter/main.cc +++ b/repos/os/src/server/event_filter/main.cc @@ -26,6 +26,7 @@ #include #include #include +#include #include namespace Event_filter { struct Main; } @@ -254,6 +255,9 @@ struct Event_filter::Main : Source::Factory, Source::Trigger if (node.type() == Touch_click_source::name()) return *new (_heap) Touch_click_source(owner, node, *this); + if (node.type() == Touch_key_source::name()) + return *new (_heap) Touch_key_source(owner, node, *this, _heap); + warning("unknown <", node.type(), "> input-source node type"); throw Source::Invalid_config(); } diff --git a/repos/os/src/server/event_filter/source.h b/repos/os/src/server/event_filter/source.h index 629ff0cc89..4ecda4c7b7 100644 --- a/repos/os/src/server/event_filter/source.h +++ b/repos/os/src/server/event_filter/source.h @@ -48,7 +48,8 @@ class Event_filter::Source || node.type() == "button-scroll" || node.type() == "accelerate" || node.type() == "log" - || node.type() == "touch-click"; + || node.type() == "touch-click" + || node.type() == "touch-key"; return false; } diff --git a/repos/os/src/server/event_filter/touch_key_source.h b/repos/os/src/server/event_filter/touch_key_source.h new file mode 100644 index 0000000000..cefca4c6df --- /dev/null +++ b/repos/os/src/server/event_filter/touch_key_source.h @@ -0,0 +1,128 @@ +/* + * \brief Input-event source that generates press/release from touch events + * \author Norman Feske + * \date 2022-08-22 + * + * This filter generates artificial key press/release event pairs when touching + * pre-defined areas on a touch screen. All events occurring while such a + * special area is touched are suppressed. + */ + +/* + * Copyright (C) 2022 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 _EVENT_FILTER__TOUCH_KEY_SOURCE_H_ +#define _EVENT_FILTER__TOUCH_KEY_SOURCE_H_ + +/* Genode includes */ +#include +#include +#include + +/* local includes */ +#include + +namespace Event_filter { class Touch_key_source; } + + +class Event_filter::Touch_key_source : public Source, Source::Filter +{ + private: + + using Rect = Genode::Rect<>; + + Owner _owner; + + Source &_source; + + Allocator &_alloc; + + bool _pressed = false; /* true during touch sequence */ + + struct Tap : Interface + { + Rect const rect; + + Input::Keycode const code; + + static Input::Keycode code_from_xml(Xml_node const &node) + { + try { + return key_code_by_name(node.attribute_value("key", Key_name())); + } + catch (Unknown_key) { } + warning("ignoring tap rule ", node); + return Input::KEY_UNKNOWN; + } + + Tap(Xml_node const &node) + : rect(Rect::from_xml(node)), code(code_from_xml(node)) { } + }; + + Registry> _tap_rules { }; + + /** + * Filter interface + */ + void filter_event(Sink &destination, Input::Event const &event) override + { + Input::Event ev = event; + + ev.handle_touch([&] (Input::Touch_id id, float x, float y) { + + /* respond to initial touch of first finger only */ + if (id.value != 0 || _pressed) + return; + + _tap_rules.for_each([&] (Tap const &tap) { + if (tap.rect.contains(Point((int)(x), (int)(y)))) { + destination.submit(Input::Press { tap.code }); + destination.submit(Input::Release { tap.code }); + _pressed = true; + } + }); + }); + + /* filter out all events during the touch sequence */ + if (!_pressed) + destination.submit(ev); + + ev.handle_touch_release([&] (Input::Touch_id id) { + if (id.value == 0) + _pressed = false; + }); + } + + public: + + static char const *name() { return "touch-key"; } + + Touch_key_source(Owner &owner, Xml_node config, + Source::Factory &factory, Allocator &alloc) + : + Source(owner), + _owner(factory), + _source(factory.create_source(_owner, input_sub_node(config))), + _alloc(alloc) + { + config.for_each_sub_node("tap", [&] (Xml_node const &node) { + new (_alloc) Registered(_tap_rules, node); }); + } + + ~Touch_key_source() + { + _tap_rules.for_each([&] (Registered &tap) { + destroy(_alloc, &tap); }); + } + + void generate(Sink &destination) override + { + Source::Filter::apply(destination, *this, _source); + } +}; + +#endif /* _EVENT_FILTER__TOUCH_KEY_SOURCE_H_*/ diff --git a/repos/os/src/test/event_filter/main.cc b/repos/os/src/test/event_filter/main.cc index 86787787a3..c6e46caeff 100644 --- a/repos/os/src/test/event_filter/main.cc +++ b/repos/os/src/test/event_filter/main.cc @@ -259,6 +259,13 @@ class Test::Input_to_filter if (motion && rel) batch.submit(Input::Relative_motion{(int)node.attribute_value("rx", 0L), (int)node.attribute_value("ry", 0L)}); + + if (node.has_type("touch")) + batch.submit(Input::Touch{ { 0 }, (float)node.attribute_value("x", 0.0), + (float)node.attribute_value("y", 0.0)}); + + if (node.has_type("touch-release")) + batch.submit(Input::Touch_release { { 0 } } ); }); }); } @@ -337,6 +344,8 @@ struct Test::Main : Input_from_filter::Event_handler step.type() == "expect_release" || step.type() == "not_expect_press" || step.type() == "not_expect_release" || + step.type() == "expect_touch" || + step.type() == "expect_touch_release" || step.type() == "expect_char" || step.type() == "expect_motion" || step.type() == "expect_wheel"); @@ -399,6 +408,7 @@ struct Test::Main : Input_from_filter::Event_handler if (step.type() == "expect_press" || step.type() == "expect_release" || step.type() == "not_expect_press" || step.type() == "not_expect_release" + || step.type() == "expect_touch" || step.type() == "expect_touch_release" || step.type() == "expect_char" || step.type() == "expect_motion" || step.type() == "expect_wheel") return; @@ -485,6 +495,18 @@ struct Test::Main : Input_from_filter::Event_handler && (!step.has_attribute("ay") || step.attribute_value("ay", 0L) == y)) step_succeeded = true; }); + ev.handle_touch([&] (Input::Touch_id, float x, float y) { + if (step.type() == "expect_touch" + && ((float)step.attribute_value("x", 0.0) == x) + && ((float)step.attribute_value("y", 0.0) == y)) + step_succeeded = true; + }); + + ev.handle_touch_release([&] (Input::Touch_id) { + if (step.type() == "expect_touch_release") + step_succeeded = true; + }); + if (step_failed) { error("got unexpected event: ", step); throw Exception();