feat(indev): add multi touch gestures (#7078)

This commit is contained in:
Erik Tagirov 2024-11-18 09:52:25 +01:00 committed by GitHub
parent c81e69ec74
commit f677c25abc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 1340 additions and 32 deletions

View File

@ -123,7 +123,6 @@ If you did some action on a gesture you can call
:cpp:expr:`lv_indev_wait_release(lv_indev_active())` in the event handler to :cpp:expr:`lv_indev_wait_release(lv_indev_active())` in the event handler to
prevent LVGL sending further input-device-related events. prevent LVGL sending further input-device-related events.
.. _indev_crown: .. _indev_crown:
Crown Behavior Crown Behavior
@ -157,6 +156,72 @@ For example, if both the indev and widget sensitivity is set to 128 (0.5), the i
diff will be multiplied by 0.25. The value of the Widget will be incremented by that diff will be multiplied by 0.25. The value of the Widget will be incremented by that
value or the Widget will be scrolled that amount of pixels. value or the Widget will be scrolled that amount of pixels.
Multi-touch gestures
====================
LVGL has the ability to recognize multi-touch gestures, when a gesture
is detected a ``LV_EVENT_GESTURE`` is passed to the object on which the
gesture occurred. Currently, only the pinch gesture is supported
more gesture types will be implemented soon.
To enable the multi-touch gesture recognition set the
``LV_USE_GESTURE_RECOGNITION`` option in the ``lv_conf.h`` file.
Touch event collection
~~~~~~~~~~~~~~~~~~~~~~
The driver or application collects touch events until the indev read callback
is called. It is the responsibility of the driver to call
the gesture recognition function of the appropriate type. For example
to recognise pinch gestures call ``lv_indev_gesture_detect_pinch``.
After calling the gesture detection function, it's necessary to call
the ``lv_indev_set_gesture_data`` function to set the ``gesture_data``
and ``gesture_type`` fields of the structure ``lv_indev_data_t``
.. code-block::
/* The recognizer keeps the state of the gesture */
static lv_indev_gesture_recognizer_t recognizer;
/* An array that stores the collected touch events */
static lv_indev_touch_data_t touches[10];
/* A counter that needs to be incremented each time a touch event is recieved */
static uint8_t touch_cnt;
static void touch_read_callback(lv_indev_t * drv, lv_indev_data_t * data)
{
lv_indev_touch_data_t * touch;
uint8_t i;
touch = &touches[0];
lv_indev_gesture_detect_pinch(recognizer, &touches[0],
touch_cnt);
touch_cnt = 0;
/* Set the gesture information, before returning to LVGL */
lv_indev_set_gesture_data(data, recognizer);
}
A touch event is represented by the ``lv_indev_touch_data_t`` structure, the fields
being 1:1 compatible with events emitted by the `libinput <https://wayland.freedesktop.org/libinput/doc/latest/>`_ library
Handling touch events
~~~~~~~~~~~~~~~~~~~~~
Touch events are handled like any other event. First, setup a listener for the ``LV_EVENT_GESTURE`` event type by defining and setting the callback function.
The state or scale of the pinch gesture can be retrieved by
calling the ``lv_event_get_pinch_scale`` and ``lv_indev_get_gesture_state`` from within the
callback.
An example of such an application is available in
the source tree ``examples/others/gestures/lv_example_gestures.c``
Keypad or Keyboard Keypad or Keyboard
------------------ ------------------

View File

@ -0,0 +1,200 @@
/*******************************************************************
*
* @file lv_example_gestures.c
*
* This is a simple example program that demonstrates how to use
* the gesture recognition API, please refer to lv_indev_gesture.h or the documentation
* for more details
*
* The application starts with a single rectangle that is scaled when a pinch gesture
* is detected. A single finger moves the rectangle around,
*
* Copyright (c) 2024 EDGEMTech Ltd
*
* Author: EDGEMTech Ltd, Erik Tagirov (erik.tagirov@edgemtech.ch)
*
******************************************************************/
/*********************
* INCLUDES
*********************/
#include "../../lv_examples.h"
#if LV_USE_GESTURE_RECOGNITION && \
LV_USE_FLOAT
/*********************
* DEFINES
*********************/
#define RECT_INIT_WIDTH 300.0
#define RECT_INIT_HEIGHT 300.0
#define RECT_COLOR 0xC1BCFF
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void label_scale(lv_event_t * gesture_event);
static void label_move(lv_event_t * event);
/**********************
* STATIC VARIABLES
**********************/
static lv_obj_t * label;
static lv_style_t label_style;
static float label_width;
static float label_height;
static float label_x;
static float label_y;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Entry point it creates the screen, and the label
* Set event callbacks on the label
*/
void lv_example_gestures(void)
{
lv_obj_t * rectangle;
lv_obj_t * root_view;
label_width = RECT_INIT_WIDTH;
label_height = RECT_INIT_HEIGHT;
label_y = label_x = 300;
root_view = lv_screen_active();
lv_obj_set_style_bg_color(root_view, lv_color_hex(0xFFFFFF), LV_PART_MAIN);
label = lv_label_create(root_view);
lv_obj_remove_flag(root_view, LV_OBJ_FLAG_SCROLLABLE);
lv_label_set_text(label, "Zoom or move");
lv_obj_add_flag(label, LV_OBJ_FLAG_CLICKABLE);
lv_style_init(&label_style);
lv_style_set_bg_color(&label_style, lv_color_hex(RECT_COLOR));
lv_style_set_bg_opa(&label_style, LV_OPA_COVER);
lv_style_set_width(&label_style, (int)label_width);
lv_style_set_height(&label_style, (int)label_height);
lv_style_set_x(&label_style, (int)label_x);
lv_style_set_y(&label_style, (int)label_y);
lv_obj_add_style(label, &label_style, LV_STATE_DEFAULT);
lv_obj_add_event_cb(label, label_scale, LV_EVENT_GESTURE, label);
lv_obj_add_event_cb(label, label_move, LV_EVENT_PRESSING, label);
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Called when a pinch event occurs - scales the label
* @param gesture_event point to a LV_EVENT_PINCH event
*/
static void label_scale(lv_event_t * gesture_event)
{
static int initial_w = -1;
static int initial_h = -1;
lv_indev_gesture_state_t state;
lv_point_t center_pnt;
float scale;
scale = lv_event_get_pinch_scale(gesture_event);
state = lv_event_get_gesture_state(gesture_event);
lv_indev_get_point(lv_indev_active(), &center_pnt);
if(state == LV_INDEV_GESTURE_STATE_ENDED) {
/* Pinch gesture has ended - reset the width/height for the next pinch gesture*/
initial_w = -1;
initial_h = -1;
LV_LOG_TRACE("label end scale: %g %d\n", scale, state);
return;
}
if(initial_h == -1 || initial_w == -1) {
LV_ASSERT(state == LV_INDEV_GESTURE_STATE_RECOGNIZED);
/* Pinch gesture has been recognized - this is the first event in a series of recognized events */
/* The scaling is applied relative to the original width/height of the rectangle */
initial_w = label_width;
initial_h = label_height;
LV_LOG_TRACE("label start scale: %g\n", scale);
}
/* The gesture has started or is on-going */
/* Avoids a situation where the rectangle becomes too small,
* do not perform the scaling - leave straight away */
if(scale < 0.4) {
return;
}
label_width = initial_w * scale;
label_height = initial_h * scale;
label_x = center_pnt.x - label_width / 2;
label_y = center_pnt.y - label_height / 2;
LV_LOG_TRACE("label scale: %g label x: %g label y: %g w: %g h: %g\n",
scale, label_x, label_y, label_width, label_height);
/* Update position and size */
lv_style_set_width(&label_style, (int)label_width);
lv_style_set_height(&label_style, (int)label_height);
lv_style_set_x(&label_style, (int)label_x);
lv_style_set_y(&label_style, (int)label_y);
lv_obj_add_style(label, &label_style, LV_STATE_DEFAULT);
}
/**
* Called when a LV_EVENT_PRESSING occurs on the rectangle - moves the label
* @param event pointer to the event
*/
static void label_move(lv_event_t * event)
{
lv_point_t pnt;
lv_indev_gesture_state_t state;
state = lv_event_get_gesture_state(event);
lv_indev_get_point(lv_indev_active(), &pnt);
/* Do not move and when a pinch gesture is ongoing */
if(state == LV_INDEV_GESTURE_STATE_RECOGNIZED) {
return;
}
LV_LOG_TRACE("label move %p x: %d y: %d\n", event, pnt.x, pnt.y);
label_x = pnt.x - label_width / 2;
label_y = pnt.y - label_height / 2;
/* Update position */
lv_style_set_x(&label_style, (int)label_x);
lv_style_set_y(&label_style, (int)label_y);
lv_obj_add_style(label, &label_style, LV_STATE_DEFAULT);
}
#endif /* LV_USE_GESTURE_RECOGNITION && LV_USE_FLOAT */

View File

@ -0,0 +1,44 @@
/*******************************************************************
* @file lv_example_gestures.h
*
* Copyright (c) 2024 EDGEMTech Ltd.
*
* Author: EDGEMTech Ltd, Erik Tagirov (erik.tagirov@edgemtech.ch)
*
******************************************************************/
#ifndef LV_EXAMPLE_GESTURES_H
#define LV_EXAMPLE_GESTURES_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/* Example entry point */
void lv_example_gestures(void);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_EXAMPLE_GESTURES_H*/

View File

@ -22,6 +22,7 @@ extern "C" {
#include "monkey/lv_example_monkey.h" #include "monkey/lv_example_monkey.h"
#include "observer/lv_example_observer.h" #include "observer/lv_example_observer.h"
#include "snapshot/lv_example_snapshot.h" #include "snapshot/lv_example_snapshot.h"
#include "gestures/lv_example_gestures.h"
/********************* /*********************
* DEFINES * DEFINES

View File

@ -475,6 +475,10 @@
#define LV_VG_LITE_THORVG_THREAD_RENDER 0 #define LV_VG_LITE_THORVG_THREAD_RENDER 0
#endif #endif
/* Enable the multi-touch gesture recognition feature */
/* Gesture recognition requires the use of floats */
#define LV_USE_GESTURE_RECOGNITION 0
/*===================== /*=====================
* COMPILER SETTINGS * COMPILER SETTINGS
*====================*/ *====================*/

2
lvgl.h
View File

@ -43,6 +43,7 @@ extern "C" {
#include "src/core/lv_obj.h" #include "src/core/lv_obj.h"
#include "src/core/lv_group.h" #include "src/core/lv_group.h"
#include "src/indev/lv_indev.h" #include "src/indev/lv_indev.h"
#include "src/indev/lv_indev_gesture.h"
#include "src/core/lv_refr.h" #include "src/core/lv_refr.h"
#include "src/display/lv_display.h" #include "src/display/lv_display.h"
@ -130,6 +131,7 @@ extern "C" {
#include "src/lvgl_private.h" #include "src/lvgl_private.h"
#endif #endif
/********************* /*********************
* DEFINES * DEFINES
*********************/ *********************/

View File

@ -1,7 +1,6 @@
/******************************************************************* /*******************************************************************
* *
* @file lv_wayland.c - The Wayland client for LVGL applications * @file lv_wayland.c - The Wayland client for LVGL applications
*
* Based on the original file from the repository. * Based on the original file from the repository.
* *
* Porting to LVGL 9.1 * Porting to LVGL 9.1
@ -43,7 +42,6 @@ typedef int dummy_t; /* Make GCC on windows happy, avoid empty translation un
#include "lvgl.h" #include "lvgl.h"
#if !LV_WAYLAND_WL_SHELL #if !LV_WAYLAND_WL_SHELL
#include "wayland_xdg_shell.h" #include "wayland_xdg_shell.h"
#define LV_WAYLAND_XDG_SHELL 1 #define LV_WAYLAND_XDG_SHELL 1
@ -101,6 +99,7 @@ enum object_type {
#define LAST_DECORATION (OBJECT_BORDER_RIGHT) #define LAST_DECORATION (OBJECT_BORDER_RIGHT)
#define NUM_DECORATIONS (LAST_DECORATION-FIRST_DECORATION+1) #define NUM_DECORATIONS (LAST_DECORATION-FIRST_DECORATION+1)
struct window; struct window;
struct input { struct input {
struct { struct {
@ -117,11 +116,12 @@ struct input {
lv_indev_state_t state; lv_indev_state_t state;
} keyboard; } keyboard;
struct { #if LV_USE_GESTURE_RECOGNITION
uint32_t x; lv_indev_touch_data_t touches[10];
uint32_t y; uint8_t touch_event_cnt;
lv_indev_state_t state; uint8_t primary_id;
} touch; lv_indev_gesture_recognizer_t recognizer;
#endif
}; };
struct seat { struct seat {
@ -878,11 +878,14 @@ static const struct wl_keyboard_listener keyboard_listener = {
.modifiers = keyboard_handle_modifiers, .modifiers = keyboard_handle_modifiers,
}; };
#if LV_USE_GESTURE_RECOGNITION
static void touch_handle_down(void * data, struct wl_touch * wl_touch, static void touch_handle_down(void * data, struct wl_touch * wl_touch,
uint32_t serial, uint32_t time, struct wl_surface * surface, uint32_t serial, uint32_t time, struct wl_surface * surface,
int32_t id, wl_fixed_t x_w, wl_fixed_t y_w) int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
{ {
struct application * app = data; struct application * app = data;
uint8_t i;
LV_UNUSED(id); LV_UNUSED(id);
LV_UNUSED(time); LV_UNUSED(time);
@ -894,11 +897,16 @@ static void touch_handle_down(void * data, struct wl_touch * wl_touch,
return; return;
} }
/* Create the touch down event */
app->touch_obj = wl_surface_get_user_data(surface); app->touch_obj = wl_surface_get_user_data(surface);
i = app->touch_obj->input.touch_event_cnt;
app->touch_obj->input.touch.x = wl_fixed_to_int(x_w); app->touch_obj->input.touches[i].point.x = wl_fixed_to_int(x_w);
app->touch_obj->input.touch.y = wl_fixed_to_int(y_w); app->touch_obj->input.touches[i].point.y = wl_fixed_to_int(y_w);
app->touch_obj->input.touch.state = LV_INDEV_STATE_PRESSED; app->touch_obj->input.touches[i].id = id;
app->touch_obj->input.touches[i].timestamp = time;
app->touch_obj->input.touches[i].state = LV_INDEV_STATE_PRESSED;
app->touch_obj->input.touch_event_cnt++;
#if LV_WAYLAND_WINDOW_DECORATIONS #if LV_WAYLAND_WINDOW_DECORATIONS
struct window * window = app->touch_obj->window; struct window * window = app->touch_obj->window;
@ -927,17 +935,25 @@ static void touch_handle_up(void * data, struct wl_touch * wl_touch,
uint32_t serial, uint32_t time, int32_t id) uint32_t serial, uint32_t time, int32_t id)
{ {
struct application * app = data; struct application * app = data;
uint8_t i;
LV_UNUSED(serial); LV_UNUSED(serial);
LV_UNUSED(time); LV_UNUSED(time);
LV_UNUSED(id); LV_UNUSED(id);
LV_UNUSED(wl_touch); LV_UNUSED(wl_touch);
if(!app->touch_obj) { #if LV_USE_GESTURE_RECOGNITION
return; /* Create a released event */
} i = app->touch_obj->input.touch_event_cnt;
app->touch_obj->input.touch.state = LV_INDEV_STATE_RELEASED; app->touch_obj->input.touches[i].point.x = 0;
app->touch_obj->input.touches[i].point.y = 0;
app->touch_obj->input.touches[i].id = id;
app->touch_obj->input.touches[i].timestamp = time;
app->touch_obj->input.touches[i].state = LV_INDEV_STATE_RELEASED;
app->touch_obj->input.touch_event_cnt++;
#endif
#if LV_WAYLAND_WINDOW_DECORATIONS #if LV_WAYLAND_WINDOW_DECORATIONS
struct window * window = app->touch_obj->window; struct window * window = app->touch_obj->window;
@ -962,30 +978,56 @@ static void touch_handle_up(void * data, struct wl_touch * wl_touch,
xdg_toplevel_set_minimized(window->xdg_toplevel); xdg_toplevel_set_minimized(window->xdg_toplevel);
window->flush_pending = true; window->flush_pending = true;
} }
#endif // LV_WAYLAND_XDG_SHELL #endif /* LV_WAYLAND_XDG_SHELL */
default: default:
break; break;
} }
#endif // LV_WAYLAND_WINDOW_DECORATIONS #endif /* LV_WAYLAND_WINDOW_DECORATIONS */
app->touch_obj = NULL;
} }
static void touch_handle_motion(void * data, struct wl_touch * wl_touch, static void touch_handle_motion(void * data, struct wl_touch * wl_touch,
uint32_t time, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w) uint32_t time, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
{ {
struct application * app = data; struct application * app = data;
lv_indev_touch_data_t * touch;
lv_indev_touch_data_t * cur;
uint8_t i;
LV_UNUSED(time); LV_UNUSED(time);
LV_UNUSED(id); LV_UNUSED(id);
LV_UNUSED(wl_touch); LV_UNUSED(wl_touch);
if(!app->touch_obj) { /* Update the contact point of the corresponding id with the latest coordinate */
return; touch = &app->touch_obj->input.touches[0];
cur = NULL;
for(i = 0; i < app->touch_obj->input.touch_event_cnt; i++) {
if(touch->id == id) {
cur = touch;
}
touch++;
}
if(cur == NULL) {
i = app->touch_obj->input.touch_event_cnt;
app->touch_obj->input.touches[i].point.x = wl_fixed_to_int(x_w);
app->touch_obj->input.touches[i].point.y = wl_fixed_to_int(y_w);
app->touch_obj->input.touches[i].id = id;
app->touch_obj->input.touches[i].timestamp = time;
app->touch_obj->input.touches[i].state = LV_INDEV_STATE_PRESSED;
app->touch_obj->input.touch_event_cnt++;
}
else {
cur->point.x = wl_fixed_to_int(x_w);
cur->point.y = wl_fixed_to_int(y_w);
cur->id = id;
cur->timestamp = time;
} }
app->touch_obj->input.touch.x = wl_fixed_to_int(x_w);
app->touch_obj->input.touch.y = wl_fixed_to_int(y_w);
} }
static void touch_handle_frame(void * data, struct wl_touch * wl_touch) static void touch_handle_frame(void * data, struct wl_touch * wl_touch)
@ -1009,6 +1051,8 @@ static const struct wl_touch_listener touch_listener = {
.cancel = touch_handle_cancel, .cancel = touch_handle_cancel,
}; };
#endif /* END LV_USE_GESTURE_RECOGNITION */
static void seat_handle_capabilities(void * data, struct wl_seat * wl_seat, enum wl_seat_capability caps) static void seat_handle_capabilities(void * data, struct wl_seat * wl_seat, enum wl_seat_capability caps)
{ {
struct application * app = data; struct application * app = data;
@ -1039,10 +1083,12 @@ static void seat_handle_capabilities(void * data, struct wl_seat * wl_seat, enum
seat->wl_keyboard = NULL; seat->wl_keyboard = NULL;
} }
#if LV_USE_GESTURE_RECOGNITION
if((caps & WL_SEAT_CAPABILITY_TOUCH) && !seat->wl_touch) { if((caps & WL_SEAT_CAPABILITY_TOUCH) && !seat->wl_touch) {
seat->wl_touch = wl_seat_get_touch(wl_seat); seat->wl_touch = wl_seat_get_touch(wl_seat);
wl_touch_add_listener(seat->wl_touch, &touch_listener, app); wl_touch_add_listener(seat->wl_touch, &touch_listener, app);
} }
#endif
else if(!(caps & WL_SEAT_CAPABILITY_TOUCH) && seat->wl_touch) { else if(!(caps & WL_SEAT_CAPABILITY_TOUCH) && seat->wl_touch) {
wl_touch_destroy(seat->wl_touch); wl_touch_destroy(seat->wl_touch);
seat->wl_touch = NULL; seat->wl_touch = NULL;
@ -2223,6 +2269,7 @@ skip:
static void _lv_wayland_handle_input(void) static void _lv_wayland_handle_input(void)
{ {
int prepare_read = wl_display_prepare_read(application.display); int prepare_read = wl_display_prepare_read(application.display);
while(prepare_read != 0) { while(prepare_read != 0) {
wl_display_dispatch_pending(application.display); wl_display_dispatch_pending(application.display);
} }
@ -2249,13 +2296,6 @@ static void _lv_wayland_handle_output(void)
window->shall_close = false; window->shall_close = false;
shall_flush = true; shall_flush = true;
window->body->input.touch.x = 0;
window->body->input.touch.y = 0;
window->body->input.touch.state = LV_INDEV_STATE_RELEASED;
if(window->application->touch_obj == window->body) {
window->application->touch_obj = NULL;
}
window->body->input.pointer.x = 0; window->body->input.pointer.x = 0;
window->body->input.pointer.y = 0; window->body->input.pointer.y = 0;
window->body->input.pointer.left_button = LV_INDEV_STATE_RELEASED; window->body->input.pointer.left_button = LV_INDEV_STATE_RELEASED;
@ -2331,18 +2371,40 @@ static void _lv_wayland_keyboard_read(lv_indev_t * drv, lv_indev_data_t * data)
data->state = window->body->input.keyboard.state; data->state = window->body->input.keyboard.state;
} }
#if LV_USE_GESTURE_RECOGNITION
static void _lv_wayland_touch_read(lv_indev_t * drv, lv_indev_data_t * data) static void _lv_wayland_touch_read(lv_indev_t * drv, lv_indev_data_t * data)
{ {
struct window * window = lv_display_get_user_data(lv_indev_get_display(drv)); struct window * window = lv_display_get_user_data(lv_indev_get_display(drv));
lv_indev_touch_data_t * touch;
bool is_active;
lv_indev_gesture_recognizer_t * recognizer;
uint8_t touch_cnt;
uint8_t i;
if(!window || window->closed) { if(!window || window->closed) {
return; return;
} }
data->point.x = window->body->input.touch.x; /* Collect touches if there are any - send them to the gesture recognizer */
data->point.y = window->body->input.touch.y; recognizer = &window->body->input.recognizer;
data->state = window->body->input.touch.state; touch = &window->body->input.touches[0];
LV_LOG_TRACE("collected touch events: %d", window->body->input.touch_event_cnt);
lv_indev_gesture_detect_pinch(recognizer, &window->body->input.touches[0],
window->body->input.touch_event_cnt);
window->body->input.touch_event_cnt = 0;
/* Set the gesture information, before returning to LVGL */
lv_indev_set_gesture_data(data, recognizer);
} }
#endif /* END LV_USE_GESTURE_RECOGNITION */
/********************** /**********************
* GLOBAL FUNCTIONS * GLOBAL FUNCTIONS
**********************/ **********************/
@ -2569,6 +2631,8 @@ lv_display_t * lv_wayland_window_create(uint32_t hor_res, uint32_t ver_res, char
LV_LOG_ERROR("failed to register pointeraxis indev"); LV_LOG_ERROR("failed to register pointeraxis indev");
} }
#if LV_USE_GESTURE_RECOGNITION
window->lv_indev_touch = lv_indev_create(); window->lv_indev_touch = lv_indev_create();
lv_indev_set_type(window->lv_indev_touch, LV_INDEV_TYPE_POINTER); lv_indev_set_type(window->lv_indev_touch, LV_INDEV_TYPE_POINTER);
lv_indev_set_read_cb(window->lv_indev_touch, _lv_wayland_touch_read); lv_indev_set_read_cb(window->lv_indev_touch, _lv_wayland_touch_read);
@ -2578,6 +2642,8 @@ lv_display_t * lv_wayland_window_create(uint32_t hor_res, uint32_t ver_res, char
LV_LOG_ERROR("failed to register touch indev"); LV_LOG_ERROR("failed to register touch indev");
} }
#endif /* END LV_USE_GESTURE_RECOGNITION */
window->lv_indev_keyboard = lv_indev_create(); window->lv_indev_keyboard = lv_indev_create();
lv_indev_set_type(window->lv_indev_keyboard, LV_INDEV_TYPE_KEYPAD); lv_indev_set_type(window->lv_indev_keyboard, LV_INDEV_TYPE_KEYPAD);
lv_indev_set_read_cb(window->lv_indev_keyboard, _lv_wayland_keyboard_read); lv_indev_set_read_cb(window->lv_indev_keyboard, _lv_wayland_keyboard_read);

View File

@ -27,6 +27,7 @@ extern "C" {
#include "../../display/lv_display.h" #include "../../display/lv_display.h"
#include "../../indev/lv_indev.h" #include "../../indev/lv_indev.h"
#include "../../indev/lv_indev_gesture.h"
#if LV_USE_WAYLAND #if LV_USE_WAYLAND

View File

@ -12,6 +12,7 @@
* INCLUDES * INCLUDES
********************/ ********************/
#include "lv_indev_scroll.h" #include "lv_indev_scroll.h"
#include "lv_indev_gesture.h"
#include "../display/lv_display_private.h" #include "../display/lv_display_private.h"
#include "../core/lv_global.h" #include "../core/lv_global.h"
#include "../core/lv_obj_private.h" #include "../core/lv_obj_private.h"
@ -728,6 +729,9 @@ static void indev_pointer_proc(lv_indev_t * i, lv_indev_data_t * data)
i->pointer.act_point.y = data->point.y; i->pointer.act_point.y = data->point.y;
i->pointer.diff = data->enc_diff; i->pointer.diff = data->enc_diff;
i->gesture_type = data->gesture_type;
i->gesture_data = data->gesture_data;
/*Process the diff first as scrolling will be processed in indev_proc_release*/ /*Process the diff first as scrolling will be processed in indev_proc_release*/
indev_proc_pointer_diff(i); indev_proc_pointer_diff(i);
@ -1277,13 +1281,24 @@ static void indev_proc_press(lv_indev_t * indev)
indev->pointer.press_moved = 1; indev->pointer.press_moved = 1;
} }
/* Send a gesture event to a potential indev cb callback, even if no object was found */
if(indev->gesture_type != LV_INDEV_GESTURE_NONE) {
lv_indev_send_event(indev, LV_EVENT_GESTURE, indev_act);
}
if(indev_obj_act) { if(indev_obj_act) {
const bool is_enabled = !lv_obj_has_state(indev_obj_act, LV_STATE_DISABLED); const bool is_enabled = !lv_obj_has_state(indev_obj_act, LV_STATE_DISABLED);
if(indev->gesture_type != LV_INDEV_GESTURE_NONE) {
/* NOTE: hardcoded to pinch for now */
if(send_event(LV_EVENT_GESTURE, indev_act) == LV_RESULT_INVALID) return;
}
if(is_enabled) { if(is_enabled) {
if(send_event(LV_EVENT_PRESSING, indev_act) == LV_RESULT_INVALID) return; if(send_event(LV_EVENT_PRESSING, indev_act) == LV_RESULT_INVALID) return;
} }
if(indev_act->wait_until_release) return; if(indev_act->wait_until_release) return;
lv_indev_scroll_handler(indev); lv_indev_scroll_handler(indev);
@ -1363,11 +1378,20 @@ static void indev_proc_release(lv_indev_t * indev)
lv_timer_pause(indev->read_timer); lv_timer_pause(indev->read_timer);
} }
/* Send a gesture event to a potential indev cb callback, even if no object was found */
if(indev->gesture_type != LV_INDEV_GESTURE_NONE) {
lv_indev_send_event(indev, LV_EVENT_GESTURE, indev_act);
}
if(indev_obj_act) { if(indev_obj_act) {
LV_LOG_INFO("released"); LV_LOG_INFO("released");
const bool is_enabled = !lv_obj_has_state(indev_obj_act, LV_STATE_DISABLED); const bool is_enabled = !lv_obj_has_state(indev_obj_act, LV_STATE_DISABLED);
if(is_enabled && indev->gesture_type != LV_INDEV_GESTURE_NONE) {
if(send_event(LV_EVENT_GESTURE, indev_act) == LV_RESULT_INVALID) return;
}
if(is_enabled) { if(is_enabled) {
if(send_event(LV_EVENT_RELEASED, indev_act) == LV_RESULT_INVALID) return; if(send_event(LV_EVENT_RELEASED, indev_act) == LV_RESULT_INVALID) return;
} }

View File

@ -47,6 +47,17 @@ typedef enum {
LV_INDEV_MODE_EVENT, LV_INDEV_MODE_EVENT,
} lv_indev_mode_t; } lv_indev_mode_t;
/* Supported types of gestures */
typedef enum {
LV_INDEV_GESTURE_NONE = 0,
LV_INDEV_GESTURE_PINCH,
LV_INDEV_GESTURE_SWIPE,
LV_INDEV_GESTURE_ROTATE,
LV_INDEV_GESTURE_SCROLL, /* Used with scrollwheels */
LV_INDEV_GESTURE_CNT, /* Total number of gestures types */
} lv_indev_gesture_type_t;
/** Data structure passed to an input driver to fill*/ /** Data structure passed to an input driver to fill*/
typedef struct { typedef struct {
lv_point_t point; /**< For LV_INDEV_TYPE_POINTER the currently pressed point*/ lv_point_t point; /**< For LV_INDEV_TYPE_POINTER the currently pressed point*/
@ -56,6 +67,10 @@ typedef struct {
lv_indev_state_t state; /**< LV_INDEV_STATE_RELEASED or LV_INDEV_STATE_PRESSED*/ lv_indev_state_t state; /**< LV_INDEV_STATE_RELEASED or LV_INDEV_STATE_PRESSED*/
bool continue_reading; /**< If set to true, the read callback is invoked again, unless the device is in event-driven mode*/ bool continue_reading; /**< If set to true, the read callback is invoked again, unless the device is in event-driven mode*/
lv_indev_gesture_type_t gesture_type;
void * gesture_data;
} lv_indev_data_t; } lv_indev_data_t;
typedef void (*lv_indev_read_cb_t)(lv_indev_t * indev, lv_indev_data_t * data); typedef void (*lv_indev_read_cb_t)(lv_indev_t * indev, lv_indev_data_t * data);

View File

@ -0,0 +1,615 @@
/******************************************************************
* @file lv_indev_gesture.c
*
* Recognize gestures that consist of multiple touch events
*
* Copyright (c) 2024 EDGEMTech Ltd
*
* Author EDGEMTech Ltd. (erik.tagirov@edgemtech.ch)
*
******************************************************************/
/********************
* INCLUDES
********************/
#include "lv_indev_private.h"
#include "../misc/lv_event_private.h"
#if LV_USE_GESTURE_RECOGNITION
#include <math.h>
#include "lv_indev_gesture.h"
#include "lv_indev_gesture_private.h"
/********************
* DEFINES
********************/
#define LV_GESTURE_PINCH_DOWN_THRESHOLD 0.75 /* Default value - start sending events when reached */
#define LV_GESTURE_PINCH_UP_THRESHOLD 1.5 /* Default value - start sending events when reached */
#define LV_GESTURE_PINCH_MAX_INITIAL_SCALE 2.5 /* Default value */
/********************
* TYPEDEFS
********************/
/********************
* STATIC PROTOTYPES
********************/
static lv_indev_gesture_t * init_gesture_info(void);
static void reset_gesture_info(lv_indev_gesture_t * info);
static lv_indev_gesture_motion_t * get_motion(uint8_t id, lv_indev_gesture_t * info);
static int8_t get_motion_idx(uint8_t id, lv_indev_gesture_t * info);
static void process_touch_event(lv_indev_touch_data_t * touch, lv_indev_gesture_t * info);
static void gesture_update_center_point(lv_indev_gesture_t * gesture, int touch_points_nb);
static void gesture_calculate_factors(lv_indev_gesture_t * gesture, int touch_points_nb);
static void reset_recognizer(lv_indev_gesture_recognizer_t * recognizer);
static lv_indev_gesture_recognizer_t * lv_indev_get_gesture_recognizer(lv_event_t * gesture_event);
/********************
* STATIC VARIABLES
********************/
/********************
* MACROS
********************/
/********************
* GLOBAL FUNCTIONS
********************/
void lv_indev_set_pinch_up_threshold(lv_indev_gesture_recognizer_t * recognizer, float threshold)
{
/* A up threshold MUST always be bigger than 1 */
LV_ASSERT(threshold > 1.0);
if(recognizer->config == NULL) {
recognizer->config = lv_malloc(sizeof(lv_indev_gesture_configuration_t));
LV_ASSERT(recognizer->config != NULL);
recognizer->config->pinch_down_threshold = LV_GESTURE_PINCH_DOWN_THRESHOLD;
}
recognizer->config->pinch_up_threshold = threshold;
}
void lv_indev_set_pinch_down_threshold(lv_indev_gesture_recognizer_t * recognizer, float threshold)
{
/* A down threshold MUST always be smaller than 1 */
LV_ASSERT(threshold < 1.0);
if(recognizer->config == NULL) {
recognizer->config = lv_malloc(sizeof(lv_indev_gesture_configuration_t));
LV_ASSERT(recognizer->config != NULL);
recognizer->config->pinch_up_threshold = LV_GESTURE_PINCH_UP_THRESHOLD;
}
recognizer->config->pinch_down_threshold = threshold;
}
void lv_indev_get_gesture_primary_point(lv_indev_gesture_recognizer_t * recognizer, lv_point_t * point)
{
if(recognizer->info->motions[0].finger != -1) {
point->x = recognizer->info->motions[0].point.x;
point->y = recognizer->info->motions[0].point.y;
return;
}
/* There are currently no active contact points */
point->x = 0;
point->y = 0;
}
bool lv_indev_recognizer_is_active(lv_indev_gesture_recognizer_t * recognizer)
{
if(recognizer->state == LV_INDEV_GESTURE_STATE_ENDED ||
recognizer->info->finger_cnt == 0) {
return false;
}
return true;
}
float lv_event_get_pinch_scale(lv_event_t * gesture_event)
{
lv_indev_gesture_recognizer_t * recognizer;
if((recognizer = lv_indev_get_gesture_recognizer(gesture_event)) == NULL) {
return 0.0f;
}
return recognizer->scale;
}
void lv_indev_get_gesture_center_point(lv_indev_gesture_recognizer_t * recognizer, lv_point_t * point)
{
if(lv_indev_recognizer_is_active(recognizer) == false) {
point->x = 0;
point->y = 0;
return;
}
point->x = recognizer->info->center.x;
point->y = recognizer->info->center.y;
}
lv_indev_gesture_state_t lv_event_get_gesture_state(lv_event_t * gesture_event)
{
lv_indev_gesture_recognizer_t * recognizer;
if((recognizer = lv_indev_get_gesture_recognizer(gesture_event)) == NULL) {
return LV_INDEV_GESTURE_STATE_NONE;
}
return recognizer->state;
}
void lv_indev_set_gesture_data(lv_indev_data_t * data, lv_indev_gesture_recognizer_t * recognizer)
{
bool is_active;
lv_point_t cur_pnt;
if(recognizer == NULL) return;
/* If there is a single contact point use its coords,
* when there are no contact points it's set to 0,0
*
* Note: If a gesture was detected, the primary point is overwritten below
*/
lv_indev_get_gesture_primary_point(recognizer, &cur_pnt);
data->point.x = cur_pnt.x;
data->point.y = cur_pnt.y;
data->gesture_type = LV_INDEV_GESTURE_NONE;
data->gesture_data = NULL;
/* The call below returns false if there are no active contact points */
/* - OR when the gesture has ended, false is considered as a RELEASED state */
is_active = lv_indev_recognizer_is_active(recognizer);
if(is_active == false) {
data->state = LV_INDEV_STATE_RELEASED;
}
else {
data->state = LV_INDEV_STATE_PRESSED;
}
switch(recognizer->state) {
case LV_INDEV_GESTURE_STATE_RECOGNIZED:
lv_indev_get_gesture_center_point(recognizer, &cur_pnt);
data->point.x = cur_pnt.x;
data->point.y = cur_pnt.y;
data->gesture_type = LV_INDEV_GESTURE_PINCH;
data->gesture_data = (void *) recognizer;
break;
case LV_INDEV_GESTURE_STATE_ENDED:
data->gesture_type = LV_INDEV_GESTURE_PINCH;
data->gesture_data = (void *) recognizer;
break;
}
}
void lv_indev_gesture_detect_pinch(lv_indev_gesture_recognizer_t * recognizer, lv_indev_touch_data_t * touches,
uint16_t touch_cnt)
{
lv_indev_touch_data_t * touch;
lv_indev_gesture_recognizer_t * r = recognizer;
uint8_t i;
if(r->info == NULL) {
LV_LOG_TRACE("init gesture info");
r->info = init_gesture_info();
}
if(r->config == NULL) {
LV_LOG_TRACE("init gesture configuration - set defaults");
r->config = lv_malloc(sizeof(lv_indev_gesture_configuration_t));
LV_ASSERT(r->config != NULL);
r->config->pinch_up_threshold = LV_GESTURE_PINCH_UP_THRESHOLD;
r->config->pinch_down_threshold = LV_GESTURE_PINCH_DOWN_THRESHOLD;
}
/* Process collected touch events */
for(i = 0; i < touch_cnt; i++) {
touch = touches;
process_touch_event(touch, r->info);
touches++;
LV_LOG_TRACE("processed touch ev: %d finger id: %d state: %d x: %d y: %d finger_cnt: %d",
i, touch->id, touch->state, touch->point.x, touch->point.y, r->info->finger_cnt);
}
LV_LOG_TRACE("Current finger count: %d state: %d", r->info->finger_cnt, r->state);
if(r->info->finger_cnt == 2) {
switch(r->state) {
case LV_INDEV_GESTURE_STATE_ENDED:
case LV_INDEV_GESTURE_STATE_CANCELED:
case LV_INDEV_GESTURE_STATE_NONE:
/* 2 fingers down - potential pinch or swipe */
reset_recognizer(recognizer);
gesture_update_center_point(r->info, 2);
r->state = LV_INDEV_GESTURE_STATE_ONGOING;
break;
case LV_INDEV_GESTURE_STATE_ONGOING:
case LV_INDEV_GESTURE_STATE_RECOGNIZED:
/* It's an ongoing pinch gesture - update the factors */
gesture_calculate_factors(r->info, 2);
if(r->info->scale > LV_GESTURE_PINCH_MAX_INITIAL_SCALE &&
r->state == LV_INDEV_GESTURE_STATE_ONGOING) {
r->state = LV_INDEV_GESTURE_STATE_CANCELED;
break;
}
LV_ASSERT(r->config != NULL);
if(r->info->scale > r->config->pinch_up_threshold ||
r->info->scale < r->config->pinch_down_threshold) {
if(r->info->scale > 1.0) {
r->scale = r->info->scale - (r->config->pinch_up_threshold - 1.0);
}
else if(r->info->scale < 1.0) {
r->scale = r->info->scale + (1.0 - r->config->pinch_down_threshold);
}
r->type = LV_INDEV_GESTURE_PINCH;
r->state = LV_INDEV_GESTURE_STATE_RECOGNIZED;
}
break;
default:
LV_ASSERT_MSG(true, "invalid gesture recognizer state");
}
}
else {
switch(r->state) {
case LV_INDEV_GESTURE_STATE_RECOGNIZED:
/* Gesture has ended */
r->state = LV_INDEV_GESTURE_STATE_ENDED;
r->type = LV_INDEV_GESTURE_PINCH;
break;
case LV_INDEV_GESTURE_STATE_ONGOING:
/* User lifted a finger before reaching threshold */
r->state = LV_INDEV_GESTURE_STATE_CANCELED;
reset_recognizer(r);
break;
case LV_INDEV_GESTURE_STATE_CANCELED:
case LV_INDEV_GESTURE_STATE_ENDED:
reset_recognizer(r);
break;
default:
LV_ASSERT_MSG(true, "invalid gesture recognizer state");
}
}
}
/********************
* STATIC FUNCTIONS
********************/
/**
* Get the gesture recognizer associated to the event
* @param gesture_event an LV_GESTURE_EVENT event
* @return A pointer to the gesture recognizer that emitted the event
*/
lv_indev_gesture_recognizer_t * lv_indev_get_gesture_recognizer(lv_event_t * gesture_event)
{
lv_indev_t * indev;
if(gesture_event == NULL || gesture_event->param == NULL) return NULL;
indev = (lv_indev_t *) gesture_event->param;
if(indev == NULL || indev->gesture_data == NULL) return NULL;
return (lv_indev_gesture_recognizer_t *) indev->gesture_data;
}
/**
* Resets a gesture recognizer, motion descriptors are preserved
* @param recognizer a pointer to the recognizer to reset
*/
static void reset_recognizer(lv_indev_gesture_recognizer_t * recognizer)
{
size_t motion_arr_sz;
lv_indev_gesture_t * info;
lv_indev_gesture_configuration_t * conf;
if(recognizer == NULL) return;
info = recognizer->info;
conf = recognizer->config;
/* Set everything to zero but preserve the motion descriptors,
* which are located at the start of the lv_indev_gesture_t struct */
motion_arr_sz = sizeof(lv_indev_gesture_motion_t) * LV_GESTURE_MAX_POINTS;
lv_memset(info + motion_arr_sz, 0, sizeof(lv_indev_gesture_t) - motion_arr_sz);
lv_memset(recognizer, 0, sizeof(lv_indev_gesture_recognizer_t));
recognizer->scale = info->scale = 1;
recognizer->info = info;
recognizer->config = conf;
}
/**
* Initializes a motion descriptors used with the recognizer(s)
* @return a pointer to gesture descriptor
*/
static lv_indev_gesture_t * init_gesture_info(void)
{
lv_indev_gesture_t * info;
uint8_t i;
info = lv_malloc(sizeof(lv_indev_gesture_t));
LV_ASSERT_NULL(info);
lv_memset(info, 0, sizeof(lv_indev_gesture_t));
info->scale = 1;
for(i = 0; i < LV_GESTURE_MAX_POINTS; i++) {
info->motions[i].finger = -1;
}
return info;
}
/**
* Obtains the contact point motion descriptor with id
* @param id the id of the contact point
* @param info a pointer to the gesture descriptor that stores the motion of each contact point
* @return a pointer to the motion descriptor or NULL if not found
*/
static lv_indev_gesture_motion_t * get_motion(uint8_t id, lv_indev_gesture_t * info)
{
uint8_t i;
for(i = 0; i < LV_GESTURE_MAX_POINTS; i++) {
if(info->motions[i].finger == id) {
return &info->motions[i];
}
}
return NULL;
}
/**
* Obtains the index of the contact point motion descriptor
* @param id the id of the contact point
* @param info a pointer to the gesture descriptor that stores the motion of each contact point
* @return the index of the motion descriptor or -1 if not found
*/
static int8_t get_motion_idx(uint8_t id, lv_indev_gesture_t * info)
{
uint8_t i;
for(i = 0; i < LV_GESTURE_MAX_POINTS; i++) {
if(info->motions[i].finger == id) {
return i;
}
}
return -1;
}
/**
* Update the motion descriptors of a gesture
* @param touch a pointer to a touch data structure
* @param info a pointer to a gesture descriptor
*/
static void process_touch_event(lv_indev_touch_data_t * touch, lv_indev_gesture_t * info)
{
lv_indev_gesture_t * g = info;
lv_indev_gesture_motion_t * motion;
int8_t motion_idx;
uint8_t len;
motion_idx = get_motion_idx(touch->id, g);
if(motion_idx == -1 && touch->state == LV_INDEV_STATE_PRESSED) {
if(g->finger_cnt == LV_GESTURE_MAX_POINTS) {
/* Skip touch */
return;
}
/* New touch point id */
motion = &g->motions[g->finger_cnt];
motion->start_point.x = touch->point.x;
motion->start_point.y = touch->point.y;
motion->point.x = touch->point.x;
motion->point.y = touch->point.y;
motion->finger = touch->id;
motion->state = touch->state;
g->finger_cnt++;
}
else if(motion_idx >= 0 && touch->state == LV_INDEV_STATE_RELEASED) {
if(motion_idx == g->finger_cnt - 1) {
/* Mark last item as un-used */
motion = get_motion(touch->id, g);
motion->finger = -1;
motion->state = touch->state;
}
else {
/* Move back by one */
len = (g->finger_cnt - 1) - motion_idx;
lv_memmove(g->motions + motion_idx,
g->motions + motion_idx + 1,
sizeof(lv_indev_gesture_motion_t) * len);
g->motions[g->finger_cnt - 1].finger = -1;
LV_ASSERT(g->motions[motion_idx + 1].finger == -1);
}
g->finger_cnt--;
}
else if(motion_idx >= 0) {
motion = get_motion(touch->id, g);
motion->point.x = touch->point.x;
motion->point.y = touch->point.y;
motion->state = touch->state;
}
else {
LV_LOG_TRACE("Ignore extra touch id: %d", touch->id);
}
}
/**
* Calculate the center point of a gesture, called when there
* is a probability for the gesture to occur
* @param touch a pointer to a touch data structure
* @param touch_points_nb The number of contact point to take into account
*/
static void gesture_update_center_point(lv_indev_gesture_t * gesture, int touch_points_nb)
{
lv_indev_gesture_motion_t * motion;
lv_indev_gesture_t * g = gesture;
int32_t x = 0;
int32_t y = 0;
uint8_t i;
float scale_factor = 0.0f;
float delta_x[LV_GESTURE_MAX_POINTS] = {0.0f};
float delta_y[LV_GESTURE_MAX_POINTS] = {0.0f};
uint8_t touch_cnt = 0;
x = y = 0;
g->p_scale = g->scale;
g->p_delta_x = g->delta_x;
g->p_delta_y = g->delta_y;
g->p_rotation = g->rotation;
for(i = 0; i < touch_points_nb; i++) {
motion = &g->motions[i];
if(motion->finger >= 0) {
x += motion->point.x;
y += motion->point.y;
touch_cnt++;
}
else {
break;
}
}
g->center.x = x / touch_cnt;
g->center.y = y / touch_cnt;
for(i = 0; i < touch_points_nb; i++) {
motion = &g->motions[i];
if(motion->finger >= 0) {
delta_x[i] = motion->point.x - g->center.x;
delta_y[i] = motion->point.y - g->center.y;
scale_factor += (delta_x[i] * delta_x[i]) + (delta_y[i] * delta_y[i]);
}
}
for(i = 0; i < touch_points_nb; i++) {
motion = &g->motions[i];
if(motion->finger >= 0) {
g->scale_factors_x[i] = delta_x[i] / scale_factor;
g->scale_factors_y[i] = delta_y[i] / scale_factor;
}
}
}
/**
* Calculate the scale, translation and rotation of a gesture, called when
* the gesture has been recognized
* @param gesture a pointer to the gesture descriptor
* @param touch_points_nb the number of contact points to take into account
*/
static void gesture_calculate_factors(lv_indev_gesture_t * gesture, int touch_points_nb)
{
lv_indev_gesture_motion_t * motion;
lv_indev_gesture_t * g = gesture;
float center_x = 0;
float center_y = 0;
float a = 0;
float b = 0;
float d_x;
float d_y;
int8_t i;
int8_t touch_cnt = 0;
for(i = 0; i < touch_points_nb; i++) {
motion = &g->motions[i];
if(motion->finger >= 0) {
center_x += motion->point.x;
center_y += motion->point.y;
touch_cnt++;
}
else {
break;
}
}
center_x = center_x / touch_cnt;
center_y = center_y / touch_cnt;
/* translation */
g->delta_x = g->p_delta_x + (center_x - g->center.x);
g->delta_y = g->p_delta_x + (center_y - g->center.y);
/* rotation & scaling */
for(i = 0; i < touch_points_nb; i++) {
motion = &g->motions[i];
if(motion->finger >= 0) {
d_x = (motion->point.x - center_x);
d_y = (motion->point.y - center_y);
a += g->scale_factors_x[i] * d_x + g->scale_factors_y[i] * d_y;
b += g->scale_factors_x[i] * d_y + g->scale_factors_y[i] * d_x;
}
}
g->rotation = g->p_rotation + atan2f(b, a);
g->scale = g->p_scale * sqrtf((a * a) + (b * b));
g->center.x = center_x;
g->center.y = center_y;
}
#endif /* LV_USE_GESTURE_RECOGNITION */

View File

@ -0,0 +1,165 @@
/*******************************************************************
*
* @file lv_indev_gesture.h
*
* Copyright (c) 2024 EDGEMTech Ltd.
*
* Author EDGEMTech Ltd, (erik.tagirov@edgemtech.ch)
*
******************************************************************/
#ifndef LV_INDEV_GESTURE_H
#define LV_INDEV_GESTURE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../core/lv_obj.h"
#if LV_USE_GESTURE_RECOGNITION
#if LV_USE_FLOAT == 0
#error "LV_USE_FLOAT is required for gesture detection."
#endif
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/* Opaque types defined in the private header */
struct lv_indev_gesture;
struct lv_indev_gesture_configuration;
typedef struct lv_indev_gesture lv_indev_gesture_t;
typedef struct lv_indev_gesture_configuration lv_indev_gesture_configuration_t;
/* The states of a gesture recognizer */
typedef enum {
LV_INDEV_GESTURE_STATE_NONE = 0, /* Beginning & end */
LV_INDEV_GESTURE_STATE_ONGOING, /* Set when there is a probability */
LV_INDEV_GESTURE_STATE_RECOGNIZED, /* Recognized, the event will contain touch info */
LV_INDEV_GESTURE_STATE_ENDED, /* A recognized gesture has ended */
LV_INDEV_GESTURE_STATE_CANCELED, /* Canceled - usually a finger is lifted */
} lv_indev_gesture_state_t;
/* Data structures for touch events - used to repsensent a libinput event */
/* Emitted by devices capable of tracking identifiable contacts (type B) */
typedef struct {
lv_point_t point; /* Coordinates of the touch */
lv_indev_state_t state; /* The state i.e PRESSED or RELEASED */
uint8_t id; /* Identification/slot of the contact point */
uint32_t timestamp; /* Timestamp in milliseconds */
} lv_indev_touch_data_t;
/* Gesture recognizer */
typedef struct {
lv_indev_gesture_type_t type; /* The detected gesture type */
lv_indev_gesture_state_t state; /* The gesture state ongoing, recognized */
lv_indev_gesture_t * info; /* Information on the motion of each touch point */
float scale; /* Relevant for the pinch gesture */
float rotation; /* Relevant for rotation */
float distance; /* Relevant for swipes */
float speed;
lv_indev_gesture_configuration_t * config;
} lv_indev_gesture_recognizer_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/* PINCH Gesture */
/**
* Detects a pinch gesture
* @param recognizer pointer to a gesture recognizer
* @param touches pointer to the first element of the collected touch events
* @param touch_cnt length of passed touch event array.
*/
void lv_indev_gesture_detect_pinch(lv_indev_gesture_recognizer_t * recognizer, lv_indev_touch_data_t * touches,
uint16_t touch_cnt);
/**
* Set the threshold for the pinch gesture scale up, when the scale factor of gesture
* reaches the threshold events get sent
* @param recognizer pointer to a gesture recognizer
* @param touches pointer to the first element of the collected touch events
* @param touch_cnt length of passed touch event array.
*/
void lv_indev_set_pinch_up_threshold(lv_indev_gesture_recognizer_t * recognizer, float threshold);
/**
* Set the threshold for the pinch gesture scale down, when the scale factor of gesture
* reaches the threshold events get sent
* @param recognizer pointer to a gesture recognizer
* @param touches pointer to the first element of the collected touch events
* @param touch_cnt length of passed touch event array.
*/
void lv_indev_set_pinch_down_threshold(lv_indev_gesture_recognizer_t * recognizer, float threshold);
/**
* Obtains the current scale of a pinch gesture
* @param gesture_event pointer to a gesture recognizer event
* @return the scale of the current gesture
*/
float lv_event_get_pinch_scale(lv_event_t * gesture_event);
/**
* Sets the state of the recognizer to a indev data structure,
* it is usually called from the indev read callback
* @param data the indev data
* @param recognizer pointer to a gesture recognizer
*/
void lv_indev_set_gesture_data(lv_indev_data_t * data, lv_indev_gesture_recognizer_t * recognizer);
/**
* Obtains the center point of a gesture
* @param gesture_event pointer to a gesture recognizer event
* @param point pointer to a point
*/
void lv_indev_get_gesture_center_point(lv_indev_gesture_recognizer_t * recognizer, lv_point_t * point);
/**
* Obtains the current state of the gesture recognizer attached to an event
* @param gesture_event pointer to a gesture recognizer event
* @return current state of the gesture recognizer
*/
lv_indev_gesture_state_t lv_event_get_gesture_state(lv_event_t * gesture_event);
/**
* Obtains the coordinates of the current primary point
* @param recognizer pointer to a gesture recognizer
* @param point pointer to a point
*/
void lv_indev_get_gesture_primary_point(lv_indev_gesture_recognizer_t * recognizer, lv_point_t * point);
/**
* Allows to determine if there is an are ongoing gesture
* @param recognizer pointer to a gesture recognizer
* @return false if there are no contact points, or the gesture has ended - true otherwise
*/
bool lv_indev_recognizer_is_active(lv_indev_gesture_recognizer_t * recognizer);
/**********************
* MACROS
**********************/
#endif /* END LV_USE_RECOGNITION */
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /* END LV_INDEV_GESTURE_H */

View File

@ -0,0 +1,93 @@
/*******************************************************************
*
* @file lv_indev_gesture_private.h
*
* Contains declarations and definition that are internal
* to the gesture detection logic
*
* Copyright (c) 2024 EDGEMTech Ltd.
*
* Author EDGEMTech Ltd, (erik.tagirov@edgemtech.ch)
*
******************************************************************/
#ifndef LV_INDEV_GESTURE_PRIVATE_H
#define LV_INDEV_GESTURE_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../core/lv_obj.h"
#if LV_USE_GESTURE_RECOGNITION
/*********************
* DEFINES
*********************/
#define LV_GESTURE_MAX_POINTS 2
/**********************
* TYPEDEFS
**********************/
/* Represent the motion of a finger */
struct lv_indev_gesture_motion {
int8_t finger; /* The ID of the tracked finger */
lv_point_t start_point; /* The coordinates where the DOWN event occured */
lv_point_t point; /* The current coordinates */
lv_indev_state_t state; /* DEBUG: The state i.e PRESSED or RELEASED */
};
typedef struct lv_indev_gesture_motion lv_indev_gesture_motion_t;
/* General descriptor for a gesture, used by recognizer state machines to track
* the scale, rotation, and translation NOTE: (this will likely become private) */
struct lv_indev_gesture {
/* Motion descriptor, stores the coordinates and velocity of a contact point */
lv_indev_gesture_motion_t motions[LV_GESTURE_MAX_POINTS];
lv_point_t center; /* Center point */
float scale; /* Scale factor & previous scale factor */
float p_scale;
float scale_factors_x[LV_GESTURE_MAX_POINTS]; /* Scale factor relative to center for each point */
float scale_factors_y[LV_GESTURE_MAX_POINTS];
float delta_x; /* Translation & previous translation */
float delta_y;
float p_delta_x;
float p_delta_y;
float rotation; /* Rotation & previous rotation*/
float p_rotation;
uint8_t finger_cnt; /* Current number of contact points */
};
struct lv_indev_gesture_configuration {
float pinch_up_threshold; /* When the gesture reaches the threshold - start sending events */
float pinch_down_threshold; /* When the gesture reaches the threshold - start sending events */
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#endif /* END LV_USE_RECOGNITION */
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /* END LV_INDEV_GESTURE_PRIVATE_H */

View File

@ -112,6 +112,9 @@ struct _lv_indev_t {
here by the buttons*/ here by the buttons*/
lv_event_list_t event_list; lv_event_list_t event_list;
lv_anim_t * scroll_throw_anim; lv_anim_t * scroll_throw_anim;
lv_indev_gesture_type_t gesture_type;
void * gesture_data;
}; };
/********************** /**********************

View File

@ -1367,6 +1367,16 @@
#endif #endif
#endif #endif
/* Enable the multi-touch gesture recognition feature */
/* Gesture recognition requires the use of floats */
#ifndef LV_USE_GESTURE_RECOGNITION
#ifdef CONFIG_LV_USE_GESTURE_RECOGNITION
#define LV_USE_GESTURE_RECOGNITION CONFIG_LV_USE_GESTURE_RECOGNITION
#else
#define LV_USE_GESTURE_RECOGNITION 0
#endif
#endif
/*===================== /*=====================
* COMPILER SETTINGS * COMPILER SETTINGS
*====================*/ *====================*/