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.
|
||||
- :dtcompatible:`zephyr,input-longpress`: listens for key events, emits events
|
||||
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-encoder-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_LONGPRESS input_longpress.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_INPUT_DOUBLE_TAP input_double_tap.c)
|
||||
|
|
|
@ -95,4 +95,11 @@ config INPUT_KEYMAP
|
|||
help
|
||||
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
|
||||
|
|
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>;
|
||||
};
|
||||
|
||||
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 {
|
||||
#address-cells = <1>;
|
||||
#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