mirror of
https://github.com/lvgl/lvgl.git
synced 2024-11-23 01:33:59 +08:00
feat(indev): add multi touch gestures (#7078)
This commit is contained in:
parent
c81e69ec74
commit
f677c25abc
@ -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
|
||||
prevent LVGL sending further input-device-related events.
|
||||
|
||||
|
||||
.. _indev_crown:
|
||||
|
||||
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
|
||||
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
|
||||
------------------
|
||||
|
200
examples/others/gestures/lv_example_gestures.c
Normal file
200
examples/others/gestures/lv_example_gestures.c
Normal 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(), ¢er_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 */
|
44
examples/others/gestures/lv_example_gestures.h
Normal file
44
examples/others/gestures/lv_example_gestures.h
Normal 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*/
|
@ -22,6 +22,7 @@ extern "C" {
|
||||
#include "monkey/lv_example_monkey.h"
|
||||
#include "observer/lv_example_observer.h"
|
||||
#include "snapshot/lv_example_snapshot.h"
|
||||
#include "gestures/lv_example_gestures.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
|
@ -475,6 +475,10 @@
|
||||
#define LV_VG_LITE_THORVG_THREAD_RENDER 0
|
||||
#endif
|
||||
|
||||
/* Enable the multi-touch gesture recognition feature */
|
||||
/* Gesture recognition requires the use of floats */
|
||||
#define LV_USE_GESTURE_RECOGNITION 0
|
||||
|
||||
/*=====================
|
||||
* COMPILER SETTINGS
|
||||
*====================*/
|
||||
|
2
lvgl.h
2
lvgl.h
@ -43,6 +43,7 @@ extern "C" {
|
||||
#include "src/core/lv_obj.h"
|
||||
#include "src/core/lv_group.h"
|
||||
#include "src/indev/lv_indev.h"
|
||||
#include "src/indev/lv_indev_gesture.h"
|
||||
#include "src/core/lv_refr.h"
|
||||
#include "src/display/lv_display.h"
|
||||
|
||||
@ -130,6 +131,7 @@ extern "C" {
|
||||
#include "src/lvgl_private.h"
|
||||
#endif
|
||||
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
@ -1,7 +1,6 @@
|
||||
/*******************************************************************
|
||||
*
|
||||
* @file lv_wayland.c - The Wayland client for LVGL applications
|
||||
*
|
||||
* Based on the original file from the repository.
|
||||
*
|
||||
* 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"
|
||||
|
||||
|
||||
#if !LV_WAYLAND_WL_SHELL
|
||||
#include "wayland_xdg_shell.h"
|
||||
#define LV_WAYLAND_XDG_SHELL 1
|
||||
@ -101,6 +99,7 @@ enum object_type {
|
||||
#define LAST_DECORATION (OBJECT_BORDER_RIGHT)
|
||||
#define NUM_DECORATIONS (LAST_DECORATION-FIRST_DECORATION+1)
|
||||
|
||||
|
||||
struct window;
|
||||
struct input {
|
||||
struct {
|
||||
@ -117,11 +116,12 @@ struct input {
|
||||
lv_indev_state_t state;
|
||||
} keyboard;
|
||||
|
||||
struct {
|
||||
uint32_t x;
|
||||
uint32_t y;
|
||||
lv_indev_state_t state;
|
||||
} touch;
|
||||
#if LV_USE_GESTURE_RECOGNITION
|
||||
lv_indev_touch_data_t touches[10];
|
||||
uint8_t touch_event_cnt;
|
||||
uint8_t primary_id;
|
||||
lv_indev_gesture_recognizer_t recognizer;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct seat {
|
||||
@ -878,11 +878,14 @@ static const struct wl_keyboard_listener keyboard_listener = {
|
||||
.modifiers = keyboard_handle_modifiers,
|
||||
};
|
||||
|
||||
#if LV_USE_GESTURE_RECOGNITION
|
||||
|
||||
static void touch_handle_down(void * data, struct wl_touch * wl_touch,
|
||||
uint32_t serial, uint32_t time, struct wl_surface * surface,
|
||||
int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
|
||||
{
|
||||
struct application * app = data;
|
||||
uint8_t i;
|
||||
|
||||
LV_UNUSED(id);
|
||||
LV_UNUSED(time);
|
||||
@ -894,11 +897,16 @@ static void touch_handle_down(void * data, struct wl_touch * wl_touch,
|
||||
return;
|
||||
}
|
||||
|
||||
/* Create the touch down event */
|
||||
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.touch.y = wl_fixed_to_int(y_w);
|
||||
app->touch_obj->input.touch.state = LV_INDEV_STATE_PRESSED;
|
||||
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++;
|
||||
|
||||
#if LV_WAYLAND_WINDOW_DECORATIONS
|
||||
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)
|
||||
{
|
||||
struct application * app = data;
|
||||
uint8_t i;
|
||||
|
||||
LV_UNUSED(serial);
|
||||
LV_UNUSED(time);
|
||||
LV_UNUSED(id);
|
||||
LV_UNUSED(wl_touch);
|
||||
|
||||
if(!app->touch_obj) {
|
||||
return;
|
||||
}
|
||||
#if LV_USE_GESTURE_RECOGNITION
|
||||
/* 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
|
||||
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);
|
||||
window->flush_pending = true;
|
||||
}
|
||||
#endif // LV_WAYLAND_XDG_SHELL
|
||||
#endif /* LV_WAYLAND_XDG_SHELL */
|
||||
default:
|
||||
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,
|
||||
uint32_t time, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
|
||||
{
|
||||
struct application * app = data;
|
||||
lv_indev_touch_data_t * touch;
|
||||
lv_indev_touch_data_t * cur;
|
||||
uint8_t i;
|
||||
|
||||
LV_UNUSED(time);
|
||||
LV_UNUSED(id);
|
||||
LV_UNUSED(wl_touch);
|
||||
|
||||
if(!app->touch_obj) {
|
||||
return;
|
||||
/* Update the contact point of the corresponding id with the latest coordinate */
|
||||
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)
|
||||
@ -1009,6 +1051,8 @@ static const struct wl_touch_listener touch_listener = {
|
||||
.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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
#if LV_USE_GESTURE_RECOGNITION
|
||||
if((caps & WL_SEAT_CAPABILITY_TOUCH) && !seat->wl_touch) {
|
||||
seat->wl_touch = wl_seat_get_touch(wl_seat);
|
||||
wl_touch_add_listener(seat->wl_touch, &touch_listener, app);
|
||||
}
|
||||
#endif
|
||||
else if(!(caps & WL_SEAT_CAPABILITY_TOUCH) && seat->wl_touch) {
|
||||
wl_touch_destroy(seat->wl_touch);
|
||||
seat->wl_touch = NULL;
|
||||
@ -2223,6 +2269,7 @@ skip:
|
||||
static void _lv_wayland_handle_input(void)
|
||||
{
|
||||
int prepare_read = wl_display_prepare_read(application.display);
|
||||
|
||||
while(prepare_read != 0) {
|
||||
wl_display_dispatch_pending(application.display);
|
||||
}
|
||||
@ -2249,13 +2296,6 @@ static void _lv_wayland_handle_output(void)
|
||||
window->shall_close = false;
|
||||
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.y = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
#if LV_USE_GESTURE_RECOGNITION
|
||||
|
||||
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));
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
||||
data->point.x = window->body->input.touch.x;
|
||||
data->point.y = window->body->input.touch.y;
|
||||
data->state = window->body->input.touch.state;
|
||||
/* Collect touches if there are any - send them to the gesture recognizer */
|
||||
recognizer = &window->body->input.recognizer;
|
||||
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
|
||||
**********************/
|
||||
@ -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");
|
||||
}
|
||||
|
||||
#if LV_USE_GESTURE_RECOGNITION
|
||||
|
||||
window->lv_indev_touch = lv_indev_create();
|
||||
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);
|
||||
@ -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");
|
||||
}
|
||||
|
||||
#endif /* END LV_USE_GESTURE_RECOGNITION */
|
||||
|
||||
window->lv_indev_keyboard = lv_indev_create();
|
||||
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);
|
||||
|
@ -27,6 +27,7 @@ extern "C" {
|
||||
|
||||
#include "../../display/lv_display.h"
|
||||
#include "../../indev/lv_indev.h"
|
||||
#include "../../indev/lv_indev_gesture.h"
|
||||
|
||||
#if LV_USE_WAYLAND
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
* INCLUDES
|
||||
********************/
|
||||
#include "lv_indev_scroll.h"
|
||||
#include "lv_indev_gesture.h"
|
||||
#include "../display/lv_display_private.h"
|
||||
#include "../core/lv_global.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.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*/
|
||||
indev_proc_pointer_diff(i);
|
||||
|
||||
@ -1277,13 +1281,24 @@ static void indev_proc_press(lv_indev_t * indev)
|
||||
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) {
|
||||
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(send_event(LV_EVENT_PRESSING, indev_act) == LV_RESULT_INVALID) return;
|
||||
}
|
||||
|
||||
|
||||
if(indev_act->wait_until_release) return;
|
||||
|
||||
lv_indev_scroll_handler(indev);
|
||||
@ -1363,11 +1378,20 @@ static void indev_proc_release(lv_indev_t * indev)
|
||||
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) {
|
||||
LV_LOG_INFO("released");
|
||||
|
||||
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(send_event(LV_EVENT_RELEASED, indev_act) == LV_RESULT_INVALID) return;
|
||||
}
|
||||
|
@ -47,6 +47,17 @@ typedef enum {
|
||||
LV_INDEV_MODE_EVENT,
|
||||
} 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*/
|
||||
typedef struct {
|
||||
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*/
|
||||
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;
|
||||
|
||||
typedef void (*lv_indev_read_cb_t)(lv_indev_t * indev, lv_indev_data_t * data);
|
||||
|
615
src/indev/lv_indev_gesture.c
Normal file
615
src/indev/lv_indev_gesture.c
Normal 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 */
|
165
src/indev/lv_indev_gesture.h
Normal file
165
src/indev/lv_indev_gesture.h
Normal 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 */
|
93
src/indev/lv_indev_gesture_private.h
Normal file
93
src/indev/lv_indev_gesture_private.h
Normal 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 */
|
@ -112,6 +112,9 @@ struct _lv_indev_t {
|
||||
here by the buttons*/
|
||||
lv_event_list_t event_list;
|
||||
lv_anim_t * scroll_throw_anim;
|
||||
|
||||
lv_indev_gesture_type_t gesture_type;
|
||||
void * gesture_data;
|
||||
};
|
||||
|
||||
/**********************
|
||||
|
@ -1367,6 +1367,16 @@
|
||||
#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
|
||||
*====================*/
|
||||
|
Loading…
Reference in New Issue
Block a user