input: double tap
Added input double tap psuedo device Signed-off-by: Helmut Lord <kellyhlord@gmail.com>
This commit is contained in:
parent
d3815dae43
commit
15c4df0aaa
12 changed files with 322 additions and 0 deletions
|
@ -100,6 +100,8 @@ General Purpose Drivers
|
||||||
matrix to key events.
|
matrix to key events.
|
||||||
- :dtcompatible:`zephyr,input-longpress`: listens for key events, emits events
|
- :dtcompatible:`zephyr,input-longpress`: listens for key events, emits events
|
||||||
for short and long press.
|
for short and long press.
|
||||||
|
- :dtcompatible:`zephyr,input-double-tap`: listens for key events, emits events
|
||||||
|
for input double taps
|
||||||
- :dtcompatible:`zephyr,lvgl-button-input`
|
- :dtcompatible:`zephyr,lvgl-button-input`
|
||||||
:dtcompatible:`zephyr,lvgl-encoder-input`
|
:dtcompatible:`zephyr,lvgl-encoder-input`
|
||||||
:dtcompatible:`zephyr,lvgl-keypad-input`
|
:dtcompatible:`zephyr,lvgl-keypad-input`
|
||||||
|
|
48
dts/bindings/input/zephyr,input-double-tap.yaml
Normal file
48
dts/bindings/input/zephyr,input-double-tap.yaml
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
# Copyright 2024 Kelly Helmut Lord
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
description: |
|
||||||
|
Input double tap pseudo-device
|
||||||
|
|
||||||
|
Listens for key events as an input and produces key events as output
|
||||||
|
corresponding to double taps.
|
||||||
|
|
||||||
|
Can be optionally be associated to a specific device to listen for events
|
||||||
|
only from that device.
|
||||||
|
|
||||||
|
Example configuration:
|
||||||
|
|
||||||
|
#include <zephyr/dt-bindings/input/input-event-codes.h>
|
||||||
|
|
||||||
|
double_tap {
|
||||||
|
input = <&buttons>;
|
||||||
|
compatible = "zephyr,input-double-tap";
|
||||||
|
input-codes = <INPUT_KEY_0>, <INPUT_KEY_1>;
|
||||||
|
double-tap-codes = <INPUT_KEY_A>, <INPUT_KEY_B>;
|
||||||
|
double-tap-delay-ms = <300>;
|
||||||
|
};
|
||||||
|
|
||||||
|
compatible: "zephyr,input-double-tap"
|
||||||
|
|
||||||
|
properties:
|
||||||
|
input:
|
||||||
|
type: phandle
|
||||||
|
description: |
|
||||||
|
Input device phandle, if not specified listen for input from all devices.
|
||||||
|
|
||||||
|
input-codes:
|
||||||
|
type: array
|
||||||
|
required: true
|
||||||
|
description: |
|
||||||
|
Array of input event key codes (INPUT_KEY_* or INPUT_BTN_*).
|
||||||
|
|
||||||
|
double-tap-codes:
|
||||||
|
type: array
|
||||||
|
required: true
|
||||||
|
description: |
|
||||||
|
Array of key codes to be generated for double taps (INPUT_KEY_* or INPUT_BTN_*).
|
||||||
|
|
||||||
|
double-tap-delay-ms:
|
||||||
|
type: int
|
||||||
|
required: true
|
||||||
|
description: Maximum time delay between taps to register a double tap, in milliseconds.
|
|
@ -8,3 +8,4 @@ zephyr_library_sources(input_utils.c)
|
||||||
|
|
||||||
zephyr_library_sources_ifdef(CONFIG_INPUT_KEYMAP input_keymap.c)
|
zephyr_library_sources_ifdef(CONFIG_INPUT_KEYMAP input_keymap.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_INPUT_LONGPRESS input_longpress.c)
|
zephyr_library_sources_ifdef(CONFIG_INPUT_LONGPRESS input_longpress.c)
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_INPUT_DOUBLE_TAP input_double_tap.c)
|
||||||
|
|
|
@ -95,4 +95,11 @@ config INPUT_KEYMAP
|
||||||
help
|
help
|
||||||
Enable the input keymap driver.
|
Enable the input keymap driver.
|
||||||
|
|
||||||
|
config INPUT_DOUBLE_TAP
|
||||||
|
bool "Input double tap"
|
||||||
|
default y
|
||||||
|
depends on DT_HAS_ZEPHYR_INPUT_DOUBLE_TAP_ENABLED
|
||||||
|
help
|
||||||
|
Enable the input double tap driver.
|
||||||
|
|
||||||
endif # INPUT
|
endif # INPUT
|
||||||
|
|
125
subsys/input/input_double_tap.c
Normal file
125
subsys/input/input_double_tap.c
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Kelly Helmut Lord
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define DT_DRV_COMPAT zephyr_input_double_tap
|
||||||
|
|
||||||
|
#include <zephyr/device.h>
|
||||||
|
#include <zephyr/input/input.h>
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(input_double_tap, CONFIG_INPUT_LOG_LEVEL);
|
||||||
|
|
||||||
|
struct double_tap_config {
|
||||||
|
const struct device *input_dev;
|
||||||
|
struct double_tap_data_entry *entries;
|
||||||
|
const uint16_t *input_codes;
|
||||||
|
const uint16_t *double_tap_codes;
|
||||||
|
uint32_t double_tap_delay_ms;
|
||||||
|
uint8_t num_codes;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct double_tap_data_entry {
|
||||||
|
const struct device *dev;
|
||||||
|
struct k_work_delayable work;
|
||||||
|
uint8_t index;
|
||||||
|
bool first_tap;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void double_tap_deferred(struct k_work *work)
|
||||||
|
{
|
||||||
|
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
|
||||||
|
struct double_tap_data_entry *entry =
|
||||||
|
CONTAINER_OF(dwork, struct double_tap_data_entry, work);
|
||||||
|
|
||||||
|
entry->first_tap = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void double_tap_cb(struct input_event *evt, void *user_data)
|
||||||
|
{
|
||||||
|
const struct device *dev = user_data;
|
||||||
|
const struct double_tap_config *cfg = dev->config;
|
||||||
|
struct double_tap_data_entry *entry;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (evt->type != INPUT_EV_KEY) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < cfg->num_codes; i++) {
|
||||||
|
if (evt->code == cfg->input_codes[i]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i == cfg->num_codes) {
|
||||||
|
LOG_DBG("ignored code %d", evt->code);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = &cfg->entries[i];
|
||||||
|
|
||||||
|
if (evt->value) {
|
||||||
|
if (entry->first_tap) {
|
||||||
|
k_work_cancel_delayable(&entry->work);
|
||||||
|
input_report_key(dev, cfg->double_tap_codes[i], 1, true, K_FOREVER);
|
||||||
|
input_report_key(dev, cfg->double_tap_codes[i], 0, true, K_FOREVER);
|
||||||
|
entry->first_tap = false;
|
||||||
|
} else {
|
||||||
|
entry->first_tap = true;
|
||||||
|
k_work_schedule(&entry->work, K_MSEC(cfg->double_tap_delay_ms));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int double_tap_init(const struct device *dev)
|
||||||
|
{
|
||||||
|
const struct double_tap_config *cfg = dev->config;
|
||||||
|
|
||||||
|
if (cfg->input_dev && !device_is_ready(cfg->input_dev)) {
|
||||||
|
LOG_ERR("input device not ready");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < cfg->num_codes; i++) {
|
||||||
|
struct double_tap_data_entry *entry = &cfg->entries[i];
|
||||||
|
|
||||||
|
entry->dev = dev;
|
||||||
|
entry->index = i;
|
||||||
|
entry->first_tap = false;
|
||||||
|
k_work_init_delayable(&entry->work, double_tap_deferred);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define INPUT_DOUBLE_TAP_DEFINE(inst) \
|
||||||
|
BUILD_ASSERT(DT_INST_PROP_LEN(inst, input_codes) == \
|
||||||
|
DT_INST_PROP_LEN(inst, double_tap_codes)); \
|
||||||
|
\
|
||||||
|
INPUT_CALLBACK_DEFINE_NAMED(DEVICE_DT_GET_OR_NULL(DT_INST_PHANDLE(inst, input)), \
|
||||||
|
double_tap_cb, (void *)DEVICE_DT_INST_GET(inst), \
|
||||||
|
double_tap_cb_##inst); \
|
||||||
|
\
|
||||||
|
static const uint16_t double_tap_input_codes_##inst[] = DT_INST_PROP(inst, input_codes); \
|
||||||
|
\
|
||||||
|
static const uint16_t double_tap_codes_##inst[] = DT_INST_PROP(inst, double_tap_codes); \
|
||||||
|
\
|
||||||
|
static struct double_tap_data_entry \
|
||||||
|
double_tap_data_entries_##inst[DT_INST_PROP_LEN(inst, input_codes)]; \
|
||||||
|
\
|
||||||
|
static const struct double_tap_config double_tap_config_##inst = { \
|
||||||
|
.input_dev = DEVICE_DT_GET_OR_NULL(DT_INST_PHANDLE(inst, input)), \
|
||||||
|
.entries = double_tap_data_entries_##inst, \
|
||||||
|
.input_codes = double_tap_input_codes_##inst, \
|
||||||
|
.double_tap_codes = double_tap_codes_##inst, \
|
||||||
|
.num_codes = DT_INST_PROP_LEN(inst, input_codes), \
|
||||||
|
.double_tap_delay_ms = DT_INST_PROP(inst, double_tap_delay_ms), \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
DEVICE_DT_INST_DEFINE(inst, double_tap_init, NULL, NULL, &double_tap_config_##inst, \
|
||||||
|
POST_KERNEL, CONFIG_INPUT_INIT_PRIORITY, NULL);
|
||||||
|
|
||||||
|
DT_INST_FOREACH_STATUS_OKAY(INPUT_DOUBLE_TAP_DEFINE)
|
|
@ -154,6 +154,14 @@
|
||||||
long-delay-ms = <100>;
|
long-delay-ms = <100>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
double_tap: doubletap {
|
||||||
|
input = <&double_tap>;
|
||||||
|
compatible = "zephyr,input-double-tap";
|
||||||
|
input-codes = <0>;
|
||||||
|
double-tap-codes = <0>;
|
||||||
|
double-tap-delay-ms = <0>;
|
||||||
|
};
|
||||||
|
|
||||||
i2c@1 {
|
i2c@1 {
|
||||||
#address-cells = <1>;
|
#address-cells = <1>;
|
||||||
#size-cells = <0>;
|
#size-cells = <0>;
|
||||||
|
|
9
tests/subsys/input/double_tap/CMakeLists.txt
Normal file
9
tests/subsys/input/double_tap/CMakeLists.txt
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.20.0)
|
||||||
|
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||||
|
|
||||||
|
project(input_double_tap)
|
||||||
|
|
||||||
|
FILE(GLOB app_sources src/*.c)
|
||||||
|
target_sources(app PRIVATE ${app_sources})
|
21
tests/subsys/input/double_tap/boards/native_sim.overlay
Normal file
21
tests/subsys/input/double_tap/boards/native_sim.overlay
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Kelly Helmut Lord
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/dt-bindings/input/input-event-codes.h>
|
||||||
|
|
||||||
|
/ {
|
||||||
|
fake_input_device: fake-device {
|
||||||
|
compatible = "vnd,input-device";
|
||||||
|
};
|
||||||
|
|
||||||
|
double_tap: doubletap {
|
||||||
|
input = <&fake_input_device>;
|
||||||
|
compatible = "zephyr,input-double-tap";
|
||||||
|
input-codes = <INPUT_KEY_0>, <INPUT_KEY_1>;
|
||||||
|
double-tap-codes = <INPUT_KEY_X>, <INPUT_KEY_Y>;
|
||||||
|
double-tap-delay-ms = <300>;
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,6 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#include "native_sim.overlay"
|
5
tests/subsys/input/double_tap/prj.conf
Normal file
5
tests/subsys/input/double_tap/prj.conf
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
CONFIG_ZTEST=y
|
||||||
|
CONFIG_INPUT=y
|
||||||
|
CONFIG_INPUT_MODE_SYNCHRONOUS=y
|
78
tests/subsys/input/double_tap/src/main.c
Normal file
78
tests/subsys/input/double_tap/src/main.c
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Kelly Helmut Lord
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/device.h>
|
||||||
|
#include <zephyr/input/input.h>
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/ztest.h>
|
||||||
|
|
||||||
|
static const struct device *const fake_dev = DEVICE_DT_GET(
|
||||||
|
DT_NODELABEL(fake_input_device));
|
||||||
|
static const struct device *const double_tap_dev = DEVICE_DT_GET(
|
||||||
|
DT_NODELABEL(double_tap));
|
||||||
|
|
||||||
|
DEVICE_DT_DEFINE(DT_INST(0, vnd_input_device), NULL, NULL, NULL, NULL,
|
||||||
|
PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, NULL);
|
||||||
|
|
||||||
|
static int event_count;
|
||||||
|
static struct input_event last_events[2];
|
||||||
|
|
||||||
|
static void test_cb(struct input_event *evt, void *user_data)
|
||||||
|
{
|
||||||
|
TC_PRINT("%s: %d %x %d\n", __func__, event_count, evt->code, evt->value);
|
||||||
|
|
||||||
|
event_count++;
|
||||||
|
memcpy(&last_events[1], &last_events[0], sizeof(struct input_event));
|
||||||
|
memcpy(&last_events[0], evt, sizeof(struct input_event));
|
||||||
|
}
|
||||||
|
INPUT_CALLBACK_DEFINE(double_tap_dev, test_cb, NULL);
|
||||||
|
|
||||||
|
ZTEST(double_tap, test_double_tap_test)
|
||||||
|
{
|
||||||
|
zassert_equal(event_count, 0);
|
||||||
|
|
||||||
|
/* ignored */
|
||||||
|
input_report_key(fake_dev, INPUT_KEY_3, 1, true, K_FOREVER);
|
||||||
|
input_report_key(fake_dev, INPUT_KEY_3, 0, true, K_FOREVER);
|
||||||
|
zassert_equal(event_count, 0);
|
||||||
|
input_report_abs(fake_dev, INPUT_KEY_0, 1, true, K_FOREVER);
|
||||||
|
input_report_abs(fake_dev, INPUT_KEY_0, 0, true, K_FOREVER);
|
||||||
|
zassert_equal(event_count, 0);
|
||||||
|
|
||||||
|
/* double tap*/
|
||||||
|
input_report_key(fake_dev, INPUT_KEY_0, 1, true, K_FOREVER);
|
||||||
|
k_sleep(K_MSEC(50));
|
||||||
|
input_report_key(fake_dev, INPUT_KEY_0, 0, true, K_FOREVER);
|
||||||
|
k_sleep(K_MSEC(50));
|
||||||
|
input_report_key(fake_dev, INPUT_KEY_0, 1, true, K_FOREVER);
|
||||||
|
k_sleep(K_MSEC(50));
|
||||||
|
input_report_key(fake_dev, INPUT_KEY_0, 0, true, K_FOREVER);
|
||||||
|
zassert_equal(event_count, 2);
|
||||||
|
zassert_equal(last_events[1].type, INPUT_EV_KEY);
|
||||||
|
zassert_equal(last_events[1].code, INPUT_KEY_X);
|
||||||
|
zassert_equal(last_events[1].value, 1);
|
||||||
|
zassert_equal(last_events[0].type, INPUT_EV_KEY);
|
||||||
|
zassert_equal(last_events[0].code, INPUT_KEY_X);
|
||||||
|
zassert_equal(last_events[0].value, 0);
|
||||||
|
|
||||||
|
/* double tap - other key */
|
||||||
|
input_report_key(fake_dev, INPUT_KEY_1, 1, true, K_FOREVER);
|
||||||
|
k_sleep(K_MSEC(50));
|
||||||
|
input_report_key(fake_dev, INPUT_KEY_1, 0, true, K_FOREVER);
|
||||||
|
k_sleep(K_MSEC(50));
|
||||||
|
input_report_key(fake_dev, INPUT_KEY_1, 1, true, K_FOREVER);
|
||||||
|
k_sleep(K_MSEC(50));
|
||||||
|
input_report_key(fake_dev, INPUT_KEY_1, 0, true, K_FOREVER);
|
||||||
|
zassert_equal(event_count, 4);
|
||||||
|
zassert_equal(last_events[1].type, INPUT_EV_KEY);
|
||||||
|
zassert_equal(last_events[1].code, INPUT_KEY_Y);
|
||||||
|
zassert_equal(last_events[1].value, 1);
|
||||||
|
zassert_equal(last_events[0].type, INPUT_EV_KEY);
|
||||||
|
zassert_equal(last_events[0].code, INPUT_KEY_Y);
|
||||||
|
zassert_equal(last_events[0].value, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZTEST_SUITE(double_tap, NULL, NULL, NULL, NULL, NULL);
|
12
tests/subsys/input/double_tap/testcase.yaml
Normal file
12
tests/subsys/input/double_tap/testcase.yaml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
tests:
|
||||||
|
input.input_double_tap:
|
||||||
|
platform_allow:
|
||||||
|
- native_sim
|
||||||
|
- native_sim/native/64
|
||||||
|
tags:
|
||||||
|
- drivers
|
||||||
|
- input
|
||||||
|
integration_platforms:
|
||||||
|
- native_sim
|
Loading…
Add table
Add a link
Reference in a new issue