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();