feat(indev): Add rotation and two fingers swipe gestures support (#7865)

This commit is contained in:
David Truan
2025-03-12 16:38:43 +01:00
committed by GitHub
parent 8ae5ee254a
commit 176b500005
9 changed files with 918 additions and 196 deletions

View File

@@ -161,25 +161,44 @@ 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.
gesture occurred. Currently, these multi-touch gestures are supported:
- Two fingers pinch (up and down)
- Two fingers rotation
- Two fingers swipe (infinite)
To enable the multi-touch gesture recognition set the
``LV_USE_GESTURE_RECOGNITION`` option in the ``lv_conf.h`` file.
Touch event collection
~~~~~~~~~~~~~~~~~~~~~~
Currently, the system sends the events if the gestures are in one of the following states:
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``.
- ``LV_INDEV_GESTURE_STATE_RECOGNIZED``: The gesture has been recognized and is now active.
- ``LV_INDEV_GESTURE_STATE_ENDED``: The gesture has ended.
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::
Multi-touch gestures overview
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To recognize multi touch gestures, recognizers are used. The structure ``lv_indev_t`` contains
an array of recognizers, one per gesture type. These recognizers are initialized internally by ``lv_indev_create`` by calling
``lv_indev_gesture_init_recognizers`` after the indev device is created. The the recognizers can then be configured to
modify the gestures thresholds. These thresholds are used to be able to recognize the gesture only after the threshold
have been reached. They can be set-up like this:
- ``lv_indev_set_pinch_up_threshold(lv_indev_t * indev, float threshold)``: Set the pinch up (zoom in) threshold in pixels.
- ``lv_indev_set_pinch_down_threshold(lv_indev_t * indev, float threshold)``: Set the pinch down (zoom out) threshold in pixels.
- ``lv_indev_set_rotation_rad_threshold(lv_indev_t * indev, float threshold)``: Set the rotation angle threshold in radians.
The recognizers can then be updated to recognize the gestures by calling ``lv_indev_gesture_recognizers_update``.
This must be done in the user defined indev ``read_cb``. This will iterate over the recognizers and stop once it detects a
recognized or ended gesture. For now only one multi-touch gesture can be recognized/ended at a time.
Once the recognizers are updated, calling ``lv_indev_gesture_recognizers_set_data`` will update the ``lv_indev_data_t`` structure.
It is meant to be done in the indev ``read_cb``. This allows the future ``lv_event_t`` to eb filled with multi-touch gesture info.
Here is an example of the ``read_cb``:
.. code-block:: c
/* The recognizer keeps the state of the gesture */
static lv_indev_gesture_recognizer_t recognizer;
@@ -194,34 +213,146 @@ and ``gesture_type`` fields of the structure ``lv_indev_data_t``
{
lv_indev_touch_data_t * touch;
uint8_t i;
touch = &touches[0];
lv_indev_gesture_detect_pinch(recognizer, &touches[0],
touch_cnt);
lv_indev_update_recognizers(drv, &touches[0], touch_cnt);
touch_cnt = 0;
/* Set the gesture information, before returning to LVGL */
lv_indev_set_gesture_data(data, recognizer);
lv_indev_gesture_recognizers_set_data(drv, data);
}
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
The user is in charge of collecting the necessary touches events from the driver until the indev ``read_cb`` is called.
It must then convert the specific driver input to ``lv_indev_touch_data_t`` to be processed by the ``read_cb`` at a later point.
Here is an example using ``libinput``:
Handling touch events
~~~~~~~~~~~~~~~~~~~~~
.. code-block:: c
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.
/**
* @brief Convert the libinput to lvgl's representation of touch event
* @param ev a pointer to the lib input event
*/
static void touch_event_queue_add(struct libinput_event *ev)
{
struct libinput_event_touch *touch_ev;
lv_indev_touch_data_t *cur;
lv_indev_touch_data_t *t;
uint32_t time;
int i;
int id;
int type;
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.
type = libinput_event_get_type(ev);
touch_ev = libinput_event_get_touch_event(ev);
id = libinput_event_touch_get_slot(touch_ev);
time = libinput_event_touch_get_time(touch_ev);
/* Get the last event for contact point */
t = &touches[0];
cur = NULL;
for (i = 0; i < touch_cnt; i++) {
if (t->id == id) {
cur = t;
}
t++;
}
if (cur != NULL && cur->timestamp == time) {
/* Previous event has the same timestamp - ignore duplicate event */
return;
}
if (cur == NULL ||
type == LIBINPUT_EVENT_TOUCH_UP ||
type == LIBINPUT_EVENT_TOUCH_DOWN) {
/* create new event */
cur = &touches[touch_cnt];
touch_cnt++;
}
switch (type) {
case LIBINPUT_EVENT_TOUCH_DOWN:
case LIBINPUT_EVENT_TOUCH_MOTION:
cur->point.x = (int) libinput_event_touch_get_x_transformed(touch_ev, SCREEN_WIDTH);
cur->point.y = (int) libinput_event_touch_get_y_transformed(touch_ev, SCREEN_HEIGHT);
cur->state = LV_INDEV_STATE_PRESSED;
break;
case LIBINPUT_EVENT_TOUCH_UP:
cur->state = LV_INDEV_STATE_RELEASED;
cur->point.x = 0;
cur->point.y = 0;
break;
}
cur->timestamp = time;
cur->id = id;
}
/**
* @brief Filter out libinput events that are not related to touches
* @param ev a pointer to the lib input event
*/
static void process_libinput_event(struct libinput_event *ev)
{
int type;
type = libinput_event_get_type(ev);
switch (type) {
case LIBINPUT_EVENT_TOUCH_MOTION:
case LIBINPUT_EVENT_TOUCH_DOWN:
case LIBINPUT_EVENT_TOUCH_UP:
/* Filter only touch events */
touch_event_queue_add(ev);
break;
default:
/* Skip an unrelated libinput event */
return;
}
}
From this setup, the user can now register events callbacks to react to ``LV_EVENT_GESTURE``.
.. note::
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 multi-touch gesture events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Once a gesture is recognized or ended, a ``LV_EVENT_GESTURE`` is sent. The user can the use these functions to
gather more information about the gesture:
- ``lv_event_get_gesture_type(lv_event_t * gesture_event)``: Get the type of the gesture. To be
used to check which multi-touch gesture is currently reported.
- ``lv_indev_gesture_state_t lv_event_get_gesture_state(lv_event_t * gesture_event, lv_indev_gesture_type_t type)``: Get the
state of the gesture. It can be one of those:
- ``LV_INDEV_GESTURE_STATE_NONE``: The gesture is not active.
- ``LV_INDEV_GESTURE_STATE_RECOGNIZED``: The gesture is recognized and can be used.
- ``LV_INDEV_GESTURE_STATE_ENDED``: The gesture ended.
These functions allow the user to confirm the gesture is the expected one and that it is in a usable state.
The user can then request the gestures values with the following functions:
- ``lv_event_get_pinch_scale(lv_event_t * gesture_event)``: Get the pinch scale. Only relevant for pinch gesture.
- ``lv_event_get_rotation(lv_event_t * gesture_event)``: Get the rotation in radians. Only relevant for rotation gesture.
- ``lv_event_get_two_fingers_swipe_distance(lv_event_t * gesture_event)``: Get the distance in pixels from the gesture staring center.
Only relevant for two fingers swipe gesture.
- ``lv_event_get_two_fingers_swipe_dir(lv_event_t * gesture_event)``: Get the direction from the starting center. Only relevant for
two fingers swipe gesture.
This allow the user to react to the gestures and to use the gestures values. An example of such an application is available in
the source tree ``examples/others/gestures/lv_example_gestures.c``.
An example of such an application is available in
the source tree ``examples/others/gestures/lv_example_gestures.c``
Keypad or Keyboard
------------------

View File

@@ -6,8 +6,10 @@
* 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,
* The application starts with a single rectangle. The user can then
* - Pinch the rectangle to scale it
* - Rotate the rectangle to rotate it
* - Two fingers swipe the rectangle to see the direction and the distance of the swipe
*
* Copyright (c) 2024 EDGEMTech Ltd
*
@@ -26,10 +28,14 @@
* DEFINES
*********************/
#define RECT_INIT_WIDTH 300.0
#define RECT_INIT_HEIGHT 300.0
#define RECT_INIT_WIDTH 300
#define RECT_INIT_HEIGHT 300
#define RECT_COLOR 0xC1BCFF
#ifndef M_PI
#define M_PI 3.1415926f
#endif
/**********************
* TYPEDEFS
**********************/
@@ -39,7 +45,8 @@
**********************/
static void label_scale(lv_event_t * gesture_event);
static void label_move(lv_event_t * event);
static void label_rotate(lv_event_t * gesture_event);
static void label_swipe(lv_event_t * gesture_event);
/**********************
* STATIC VARIABLES
@@ -47,10 +54,10 @@ static void label_move(lv_event_t * event);
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;
static uint32_t label_width;
static uint32_t label_height;
static uint32_t label_x;
static uint32_t label_y;
/**********************
* MACROS
@@ -60,6 +67,7 @@ static float label_y;
* GLOBAL FUNCTIONS
**********************/
/**
* Entry point it creates the screen, and the label
* Set event callbacks on the label
@@ -70,15 +78,16 @@ void lv_example_gestures(void)
label_width = RECT_INIT_WIDTH;
label_height = RECT_INIT_HEIGHT;
label_y = label_x = 300;
label_x = LV_HOR_RES / 2 - (label_width / 2);
label_y = LV_VER_RES / 2 - (label_height / 2);
root_view = lv_screen_active();
lv_obj_set_style_bg_color(root_view, lv_color_hex(0xFFFFFF), LV_PART_MAIN);
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_label_set_text(label, "Zoom, rotate or move");
lv_obj_add_flag(label, LV_OBJ_FLAG_CLICKABLE);
lv_style_init(&label_style);
@@ -93,9 +102,9 @@ void lv_example_gestures(void)
lv_obj_add_style(label, &label_style, LV_STATE_DEFAULT);
lv_obj_add_event_cb(label, label_rotate, LV_EVENT_GESTURE, label);
lv_obj_add_event_cb(label, label_scale, LV_EVENT_GESTURE, label);
lv_obj_add_event_cb(label, label_move, LV_EVENT_PRESSING, label);
lv_obj_add_event_cb(label, label_swipe, LV_EVENT_GESTURE, label);
}
/**********************
@@ -103,8 +112,56 @@ void lv_example_gestures(void)
**********************/
/**
* Called when a pinch event occurs - scales the label
* @param gesture_event point to a LV_EVENT_PINCH event
* Called when a LV_EVENT_GESTURE event occurs - update the label if the gesture is a swipe
* @param gesture_event pointer to a LV_EVENT_GESTURE event
*/
static void label_swipe(lv_event_t * gesture_event)
{
lv_dir_t dir;
lv_indev_gesture_state_t state;
char * text;
if(lv_event_get_gesture_type(gesture_event) != LV_INDEV_GESTURE_TWO_FINGERS_SWIPE) {
return;
}
state = lv_event_get_gesture_state(gesture_event, LV_INDEV_GESTURE_TWO_FINGERS_SWIPE);
dir = lv_event_get_two_fingers_swipe_dir(gesture_event);
if(state == LV_INDEV_GESTURE_STATE_ENDED) {
text = "NONE";
lv_label_set_text(label, text);
}
else if(state == LV_INDEV_GESTURE_STATE_RECOGNIZED) {
switch(dir) {
case LV_DIR_LEFT:
text = "LEFT";
break;
case LV_DIR_RIGHT:
text = "RIGHT";
break;
case LV_DIR_TOP:
text = "TOP";
break;
case LV_DIR_BOTTOM:
text = "BOTTOM";
break;
default:
text = "???";
break;
}
lv_label_set_text_fmt(label, "%s - %f", text, lv_event_get_two_fingers_swipe_distance(gesture_event));
}
}
/**
* Called when a LV_EVENT_GESTURE event occurs - scales the label if the gesture is a pinch
* @param gesture_event pointer to a LV_EVENT_GESTURE event
*/
static void label_scale(lv_event_t * gesture_event)
{
@@ -112,87 +169,115 @@ 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;
static lv_point_t center_pnt;
static float base_scale = 1.0;
float scale;
float label_width_float;
float label_height_float;
scale = lv_event_get_pinch_scale(gesture_event);
state = lv_event_get_gesture_state(gesture_event);
/* Ensure the gesture is a pinch */
if(lv_event_get_gesture_type(gesture_event) != LV_INDEV_GESTURE_PINCH) {
return;
}
lv_indev_get_point(lv_indev_active(), &center_pnt);
state = lv_event_get_gesture_state(gesture_event, LV_INDEV_GESTURE_PINCH);
scale = base_scale * lv_event_get_pinch_scale(gesture_event);
/* Reset state when the gesture ended */
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_USER("label end scale: %g, state: %d", scale, state);
base_scale = scale;
return;
}
if(initial_h == -1 || initial_w == -1) {
/* The first time the gesture is recognized, save its center */
if(state == LV_INDEV_GESTURE_STATE_RECOGNIZED) {
LV_ASSERT(state == LV_INDEV_GESTURE_STATE_RECOGNIZED);
if((initial_h == -1 || initial_w == -1)) {
/* 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 = (int)label_width;
initial_h = (int)label_height;
/* 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;
center_pnt.x = lv_obj_get_x(label) + label_width / 2;
center_pnt.y = lv_obj_get_y(label) + label_height / 2;
}
LV_LOG_USER("label start scale: %g", scale);
/* The gesture is recognized, we can now use the scale */
/* Avoids a situation where the rectangle becomes too small or too big,
* adding limits */
if(scale < 0.4f) {
scale = 0.4f;
}
else if(scale > 2.0f) {
scale = 2.0f;
}
label_x = center_pnt.x - label_width / 2;
label_y = center_pnt.y - label_height / 2;
label_width_float = (float)RECT_INIT_WIDTH * scale;
label_height_float = (float)RECT_INIT_HEIGHT * scale;
/* Update position and size */
lv_style_set_width(&label_style, (int)label_width_float);
lv_style_set_height(&label_style, (int)label_height_float);
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);
label_width = (int)label_width_float;
label_height = (int)label_height_float;
}
/* 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_USER("label scale: %g label x: %g label y: %g w: %g h: %g",
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
* Called when a LV_EVENT_GESTURE event occurs - rotate the label if the gesture is a rotation
* @param gesture_event pointer to a LV_EVENT_GESTURE event
*/
static void label_move(lv_event_t * event)
static void label_rotate(lv_event_t * gesture_event)
{
lv_point_t pnt;
float angle_degrees = 0.f;
static float start_angle = 0.f;
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) {
if(lv_event_get_gesture_type(gesture_event) != LV_INDEV_GESTURE_ROTATE) {
return;
}
LV_LOG_USER("label move x: %" LV_PRId32 ", y: %" LV_PRId32, pnt.x, pnt.y);
state = lv_event_get_gesture_state(gesture_event, LV_INDEV_GESTURE_ROTATE);
label_x = pnt.x - label_width / 2;
label_y = pnt.y - label_height / 2;
/* Calculate new angle. The x10 is due to lv_obj_set_style_transform_rotation using x10 angle in parameter */
angle_degrees = start_angle + 10.0f * (lv_event_get_rotation(gesture_event) * 180.0f / M_PI);
/* Update position */
lv_style_set_x(&label_style, (int)label_x);
lv_style_set_y(&label_style, (int)label_y);
/* Once the gesture ends, save the current angle to the start_angle */
if(state == LV_INDEV_GESTURE_STATE_ENDED) {
lv_obj_add_style(label, &label_style, LV_STATE_DEFAULT);
start_angle = angle_degrees;
}
/* If the gesture is recognized, rotate the label */
if(state == LV_INDEV_GESTURE_STATE_RECOGNIZED) {
/* Need to set the pivot to the center of the widget to not rotate
around the top-left corner */
lv_obj_set_style_transform_pivot_x(label, lv_obj_get_width(label) / 2, 0);
lv_obj_set_style_transform_pivot_y(label, lv_obj_get_height(label) / 2, 0);
lv_obj_set_style_transform_rotation(label, (int) angle_degrees, 0);
}
return;
}
#endif /* LV_USE_GESTURE_RECOGNITION && LV_USE_FLOAT */

View File

@@ -120,7 +120,6 @@ struct input {
lv_indev_touch_data_t touches[10];
uint8_t touch_event_cnt;
uint8_t primary_id;
lv_indev_gesture_recognizer_t recognizer;
#endif
};
@@ -2382,24 +2381,21 @@ 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_gesture_recognizer_t * recognizer;
if(!window || window->closed) {
return;
}
/* Collect touches if there are any - send them to the gesture recognizer */
recognizer = &window->body->input.recognizer;
lv_indev_gesture_recognizers_update(drv, &window->body->input.touches[0],
window->body->input.touch_event_cnt);
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);
lv_indev_gesture_recognizers_set_data(drv, data);
}

View File

@@ -81,6 +81,7 @@ static void indev_gesture(lv_indev_t * indev);
static bool indev_reset_check(lv_indev_t * indev);
static void indev_read_core(lv_indev_t * indev, lv_indev_data_t * data);
static void indev_reset_core(lv_indev_t * indev, lv_obj_t * obj);
static void indev_init_gesture_recognizers(lv_indev_t * indev);
static lv_result_t send_event(lv_event_code_t code, void * param);
static void indev_scroll_throw_anim_start(lv_indev_t * indev);
@@ -141,6 +142,9 @@ lv_indev_t * lv_indev_create(void)
indev->gesture_limit = LV_INDEV_DEF_GESTURE_LIMIT;
indev->gesture_min_velocity = LV_INDEV_DEF_GESTURE_MIN_VELOCITY;
indev->rotary_sensitivity = LV_INDEV_DEF_ROTARY_SENSITIVITY;
indev_init_gesture_recognizers(indev);
return indev;
}
@@ -729,8 +733,12 @@ 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;
#if LV_USE_GESTURE_RECOGNITION
for(int gest = 0; gest < LV_INDEV_GESTURE_CNT; gest++) {
i->gesture_type[gest] = data->gesture_type[gest];
i->gesture_data[gest] = data->gesture_data[gest];
}
#endif
/*Process the diff first as scrolling will be processed in indev_proc_release*/
indev_proc_pointer_diff(i);
@@ -1296,19 +1304,30 @@ 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 LV_USE_GESTURE_RECOGNITION
for(int i = 0; i < LV_INDEV_GESTURE_CNT; i++) {
/* Send a gesture event to a potential indev cb callback, even if no object was found */
if(indev->gesture_type[i] != LV_INDEV_GESTURE_NONE) {
indev->cur_gesture = (lv_indev_gesture_type_t) i;
lv_indev_send_event(indev, LV_EVENT_GESTURE, indev_act);
break;
}
}
#endif
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 LV_USE_GESTURE_RECOGNITION
for(int i = 0; i < LV_INDEV_GESTURE_CNT; i++) {
if(indev->gesture_type[i] != LV_INDEV_GESTURE_NONE) {
indev->cur_gesture = (lv_indev_gesture_type_t) i;
if(send_event(LV_EVENT_GESTURE, indev_act) == LV_RESULT_INVALID) return;
break;
}
}
#endif
if(is_enabled) {
if(send_event(LV_EVENT_PRESSING, indev_act) == LV_RESULT_INVALID) return;
}
@@ -1400,20 +1419,32 @@ static void indev_proc_release(lv_indev_t * indev)
lv_timer_pause(indev->read_timer);
}
#if LV_USE_GESTURE_RECOGNITION
/* 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);
for(int i = 0; i < LV_INDEV_GESTURE_CNT; i++) {
if(indev->gesture_type[i] != LV_INDEV_GESTURE_NONE) {
indev_act->cur_gesture = (lv_indev_gesture_type_t) i;
lv_indev_send_event(indev, LV_EVENT_GESTURE, indev_act);
break;
}
}
#endif
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 LV_USE_GESTURE_RECOGNITION
for(int i = 0; i < LV_INDEV_GESTURE_CNT; i++) {
if(is_enabled && indev->gesture_type[i] != LV_INDEV_GESTURE_NONE) {
indev_act->cur_gesture = (lv_indev_gesture_type_t) i;
if(send_event(LV_EVENT_GESTURE, indev_act) == LV_RESULT_INVALID) return;
break;
}
}
#endif
if(is_enabled) {
if(send_event(LV_EVENT_RELEASED, indev_act) == LV_RESULT_INVALID) return;
}
@@ -1851,3 +1882,21 @@ static void indev_scroll_throw_anim_start(lv_indev_t * indev)
indev->scroll_throw_anim = lv_anim_start(&a);
}
/**
* Initialize this indev's recognizers. It specify their recognizer function
* @param indev pointer to the indev containing the recognizers to initialize
*/
static void indev_init_gesture_recognizers(lv_indev_t * indev)
{
#if LV_USE_GESTURE_RECOGNITION
indev->recognizers[LV_INDEV_GESTURE_NONE].recog_fn = NULL;
indev->recognizers[LV_INDEV_GESTURE_PINCH].recog_fn = lv_indev_gesture_detect_pinch;
indev->recognizers[LV_INDEV_GESTURE_ROTATE].recog_fn = lv_indev_gesture_detect_rotation;
indev->recognizers[LV_INDEV_GESTURE_TWO_FINGERS_SWIPE].recog_fn = lv_indev_gesture_detect_two_fingers_swipe;
indev->recognizers[LV_INDEV_GESTURE_SCROLL].recog_fn = NULL;
indev->recognizers[LV_INDEV_GESTURE_SWIPE].recog_fn = NULL;
#else
LV_UNUSED(indev);
#endif
}

View File

@@ -54,6 +54,7 @@ typedef enum {
LV_INDEV_GESTURE_PINCH,
LV_INDEV_GESTURE_SWIPE,
LV_INDEV_GESTURE_ROTATE,
LV_INDEV_GESTURE_TWO_FINGERS_SWIPE,
LV_INDEV_GESTURE_SCROLL, /* Used with scrollwheels */
LV_INDEV_GESTURE_CNT, /* Total number of gestures types */
} lv_indev_gesture_type_t;
@@ -68,8 +69,8 @@ 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_gesture_type_t gesture_type[LV_INDEV_GESTURE_CNT]; /* Current gesture types, per gesture */
void * gesture_data[LV_INDEV_GESTURE_CNT]; /* Used to store data per gesture */
} lv_indev_data_t;

View File

@@ -29,6 +29,7 @@
#define LV_GESTURE_PINCH_DOWN_THRESHOLD 0.75f /* Default value - start sending events when reached */
#define LV_GESTURE_PINCH_UP_THRESHOLD 1.5f /* Default value - start sending events when reached */
#define LV_GESTURE_PINCH_MAX_INITIAL_SCALE 2.5f /* Default value */
#define LV_GESTURE_ROTATION_ANGLE_RAD_THRESHOLD 0.2f /* Default value - start sending events when reached */
/********************
@@ -46,7 +47,10 @@ static void process_touch_event(lv_indev_touch_data_t * touch, lv_indev_gesture_
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 lv_indev_gesture_recognizer_t * lv_indev_get_gesture_recognizer(lv_event_t * gesture_event,
lv_indev_gesture_type_t type);
static lv_dir_t calculate_swipe_dir(lv_indev_gesture_recognizer_t * recognizer);
static lv_indev_gesture_type_t get_first_recognized_or_ended_gesture(lv_indev_t * indev);
/********************
* STATIC VARIABLES
@@ -55,16 +59,20 @@ static lv_indev_gesture_recognizer_t * lv_indev_get_gesture_recognizer(lv_event_
/********************
* MACROS
********************/
#define SQUARE(x) ((x) * (x))
#define SQUARE_SUM(x, y) (SQUARE(x) + SQUARE(y))
/********************
* GLOBAL FUNCTIONS
********************/
void lv_indev_set_pinch_up_threshold(lv_indev_gesture_recognizer_t * recognizer, float threshold)
void lv_indev_set_pinch_up_threshold(lv_indev_t * indev, float threshold)
{
/* A up threshold MUST always be bigger than 1 */
LV_ASSERT(threshold > 1.0f);
lv_indev_gesture_recognizer_t * recognizer = &indev->recognizers[LV_INDEV_GESTURE_PINCH];
if(recognizer->config == NULL) {
recognizer->config = lv_malloc_zeroed(sizeof(lv_indev_gesture_configuration_t));
LV_ASSERT_MALLOC(recognizer->config);
@@ -74,11 +82,13 @@ void lv_indev_set_pinch_up_threshold(lv_indev_gesture_recognizer_t * recognizer,
recognizer->config->pinch_up_threshold = threshold;
}
void lv_indev_set_pinch_down_threshold(lv_indev_gesture_recognizer_t * recognizer, float threshold)
void lv_indev_set_pinch_down_threshold(lv_indev_t * indev, float threshold)
{
/* A down threshold MUST always be smaller than 1 */
LV_ASSERT(threshold < 1.0f);
lv_indev_gesture_recognizer_t * recognizer = &indev->recognizers[LV_INDEV_GESTURE_PINCH];
if(recognizer->config == NULL) {
recognizer->config = lv_malloc_zeroed(sizeof(lv_indev_gesture_configuration_t));
LV_ASSERT_MALLOC(recognizer->config);
@@ -88,6 +98,23 @@ void lv_indev_set_pinch_down_threshold(lv_indev_gesture_recognizer_t * recognize
recognizer->config->pinch_down_threshold = threshold;
}
void lv_indev_set_rotation_rad_threshold(lv_indev_t * indev, float threshold)
{
/* A rotation threshold MUST always be a positive number */
LV_ASSERT(threshold > 0.0f);
lv_indev_gesture_recognizer_t * recognizer = &indev->recognizers[LV_INDEV_GESTURE_ROTATE];
if(recognizer->config == NULL) {
recognizer->config = lv_malloc_zeroed(sizeof(lv_indev_gesture_configuration_t));
LV_ASSERT(recognizer->config != NULL);
recognizer->config->rotation_angle_rad_threshold = LV_GESTURE_ROTATION_ANGLE_RAD_THRESHOLD;
}
recognizer->config->rotation_angle_rad_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) {
@@ -114,13 +141,48 @@ bool lv_indev_recognizer_is_active(lv_indev_gesture_recognizer_t * recognizer)
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) {
if((recognizer = lv_indev_get_gesture_recognizer(gesture_event, LV_INDEV_GESTURE_PINCH)) == NULL) {
return 0.0f;
}
return recognizer->scale;
}
float lv_event_get_rotation(lv_event_t * gesture_event)
{
lv_indev_gesture_recognizer_t * recognizer;
if((recognizer = lv_indev_get_gesture_recognizer(gesture_event, LV_INDEV_GESTURE_ROTATE)) == NULL) {
return 0.0f;
}
return recognizer->rotation;
}
float lv_event_get_two_fingers_swipe_distance(lv_event_t * gesture_event)
{
lv_indev_gesture_recognizer_t * recognizer;
if((recognizer = lv_indev_get_gesture_recognizer(gesture_event, LV_INDEV_GESTURE_TWO_FINGERS_SWIPE)) == NULL) {
return 0.0f;
}
return recognizer->distance;
}
lv_dir_t lv_event_get_two_fingers_swipe_dir(lv_event_t * gesture_event)
{
lv_indev_gesture_recognizer_t * recognizer;
if((recognizer = lv_indev_get_gesture_recognizer(gesture_event, LV_INDEV_GESTURE_TWO_FINGERS_SWIPE)) == NULL) {
return LV_DIR_NONE;
}
return recognizer->two_fingers_swipe_dir;
}
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) {
@@ -133,18 +195,30 @@ void lv_indev_get_gesture_center_point(lv_indev_gesture_recognizer_t * recognize
point->y = recognizer->info->center.y;
}
lv_indev_gesture_state_t lv_event_get_gesture_state(lv_event_t * gesture_event)
lv_indev_gesture_state_t lv_event_get_gesture_state(lv_event_t * gesture_event, lv_indev_gesture_type_t type)
{
lv_indev_gesture_recognizer_t * recognizer;
if((recognizer = lv_indev_get_gesture_recognizer(gesture_event)) == NULL) {
if((recognizer = lv_indev_get_gesture_recognizer(gesture_event, type)) == NULL) {
return LV_INDEV_GESTURE_STATE_NONE;
}
return recognizer->state;
}
lv_indev_gesture_type_t lv_event_get_gesture_type(lv_event_t * gesture_event)
{
lv_indev_t * indev = (lv_indev_t *) gesture_event->param;
void lv_indev_set_gesture_data(lv_indev_data_t * data, lv_indev_gesture_recognizer_t * recognizer)
if(indev == NULL) {
return LV_INDEV_GESTURE_NONE;
}
return indev->cur_gesture;
}
void lv_indev_set_gesture_data(lv_indev_data_t * data, lv_indev_gesture_recognizer_t * recognizer,
lv_indev_gesture_type_t type)
{
bool is_active;
lv_point_t cur_pnt;
@@ -161,8 +235,8 @@ void lv_indev_set_gesture_data(lv_indev_data_t * data, lv_indev_gesture_recogniz
data->point.x = cur_pnt.x;
data->point.y = cur_pnt.y;
data->gesture_type = LV_INDEV_GESTURE_NONE;
data->gesture_data = NULL;
data->gesture_type[type] = LV_INDEV_GESTURE_NONE;
data->gesture_data[type] = 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 */
@@ -180,13 +254,13 @@ void lv_indev_set_gesture_data(lv_indev_data_t * data, lv_indev_gesture_recogniz
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;
data->gesture_type[type] = type;
data->gesture_data[type] = (void *) recognizer;
break;
case LV_INDEV_GESTURE_STATE_ENDED:
data->gesture_type = LV_INDEV_GESTURE_PINCH;
data->gesture_data = (void *) recognizer;
data->gesture_type[type] = type;
data->gesture_data[type] = (void *) recognizer;
break;
default:
@@ -222,7 +296,7 @@ void lv_indev_gesture_detect_pinch(lv_indev_gesture_recognizer_t * recognizer, l
process_touch_event(touch, r->info);
touches++;
LV_LOG_TRACE("processed touch ev: %d finger id: %d state: %d x: %" LV_PRId32 " y: %" LV_PRId32 " finger_cnt: %d",
LV_LOG_TRACE("processed touch ev: %d finger id: %d state: %d x: %" LV_PRId32 " y: %" LV_PRId32 " finger_cnt: %d",
i, touch->id, touch->state, touch->point.x, touch->point.y, r->info->finger_cnt);
}
@@ -230,8 +304,6 @@ void lv_indev_gesture_detect_pinch(lv_indev_gesture_recognizer_t * recognizer, l
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 */
@@ -239,38 +311,28 @@ void lv_indev_gesture_detect_pinch(lv_indev_gesture_recognizer_t * recognizer, l
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) {
if(r->info->scale > LV_GESTURE_PINCH_MAX_INITIAL_SCALE) {
r->state = LV_INDEV_GESTURE_STATE_CANCELED;
break;
}
LV_ASSERT_NULL(r->config);
if(r->info->scale > r->config->pinch_up_threshold ||
r->info->scale < r->config->pinch_down_threshold) {
if(r->info->scale > 1.0f) {
r->scale = r->info->scale - (r->config->pinch_up_threshold - 1.0f);
}
else if(r->info->scale < 1.0f) {
r->scale = r->info->scale + (1.0f - r->config->pinch_down_threshold);
}
r->type = LV_INDEV_GESTURE_PINCH;
r->state = LV_INDEV_GESTURE_STATE_RECOGNIZED;
}
break;
case LV_INDEV_GESTURE_STATE_RECOGNIZED:
/* It's an ongoing pinch gesture - update the factors */
gesture_calculate_factors(r->info, 2);
LV_ASSERT(r->info != NULL);
r->scale = r->info->scale;
r->type = LV_INDEV_GESTURE_PINCH;
break;
case LV_INDEV_GESTURE_STATE_ENDED:
case LV_INDEV_GESTURE_STATE_CANCELED:
reset_recognizer(r);
break;
default:
LV_ASSERT_MSG(true, "invalid gesture recognizer state");
}
@@ -280,17 +342,13 @@ void lv_indev_gesture_detect_pinch(lv_indev_gesture_recognizer_t * recognizer, l
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:
case LV_INDEV_GESTURE_STATE_CANCELED:
reset_recognizer(r);
break;
@@ -300,16 +358,296 @@ void lv_indev_gesture_detect_pinch(lv_indev_gesture_recognizer_t * recognizer, l
}
}
void lv_indev_gesture_detect_rotation(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_zeroed(sizeof(lv_indev_gesture_configuration_t));
LV_ASSERT(r->config != NULL);
}
/* 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: %" LV_PRId32 " y: %" LV_PRId32 " 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_NONE:
/* 2 fingers down - potential rotation 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:
/* Update the rotation from the inputs */
gesture_calculate_factors(r->info, 2);
if(fabs(r->info->rotation - r->info->p_rotation) > r->config->rotation_angle_rad_threshold) {
gesture_update_center_point(r->info, 2);
r->state = LV_INDEV_GESTURE_STATE_RECOGNIZED;
}
break;
case LV_INDEV_GESTURE_STATE_RECOGNIZED:
/* It's a recognized rotation gesture - update the factors */
gesture_calculate_factors(r->info, 2);
r->type = LV_INDEV_GESTURE_ROTATE;
r->rotation = r->info->rotation;
break;
case LV_INDEV_GESTURE_STATE_ENDED:
case LV_INDEV_GESTURE_STATE_CANCELED:
reset_recognizer(r);
r->type = LV_INDEV_GESTURE_NONE;
r->state = LV_INDEV_GESTURE_STATE_CANCELED;
break;
default:
LV_ASSERT_MSG(true, "invalid gesture recognizer state");
}
}
else {
switch(r->state) {
case LV_INDEV_GESTURE_STATE_RECOGNIZED:
/* Gesture has ended */
r->type = LV_INDEV_GESTURE_ROTATE;
r->state = LV_INDEV_GESTURE_STATE_ENDED;
break;
case LV_INDEV_GESTURE_STATE_ONGOING:
/* User lifted a finger before reaching threshold */
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");
}
}
}
void lv_indev_gesture_detect_two_fingers_swipe(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;
float dist;
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_zeroed(sizeof(lv_indev_gesture_configuration_t));
LV_ASSERT(r->config != NULL);
}
/* 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: %" LV_PRId32 " y: %" LV_PRId32 " 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_NONE:
/* 2 fingers down - potential rotation 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:
/* The gesture is ongoing, now wait for the distance from the center
to be higher than the threshold to pass it as recognized */
gesture_calculate_factors(r->info, 2);
dist = SQUARE_SUM(r->info->delta_x, r->info->delta_y);
if(dist > SQUARE(lv_indev_active()->gesture_limit)) {
r->state = LV_INDEV_GESTURE_STATE_RECOGNIZED;
}
break;
case LV_INDEV_GESTURE_STATE_RECOGNIZED:
/* The gesture is now recognized, and will stay recognized
until a finger is lifted */
gesture_calculate_factors(r->info, 2);
r->distance = (float) sqrt(SQUARE_SUM(r->info->delta_x, r->info->delta_y));
r->two_fingers_swipe_dir = calculate_swipe_dir(r);
r->type = LV_INDEV_GESTURE_TWO_FINGERS_SWIPE;
break;
case LV_INDEV_GESTURE_STATE_ENDED:
case LV_INDEV_GESTURE_STATE_CANCELED:
reset_recognizer(r);
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_TWO_FINGERS_SWIPE;
break;
case LV_INDEV_GESTURE_STATE_ONGOING:
/* User lifted a finger before reaching threshold */
reset_recognizer(r);
r->state = LV_INDEV_GESTURE_STATE_ENDED;
break;
case LV_INDEV_GESTURE_STATE_CANCELED:
case LV_INDEV_GESTURE_STATE_ENDED:
reset_recognizer(r);
r->state = LV_INDEV_GESTURE_STATE_NONE;
break;
default:
LV_ASSERT_MSG(true, "invalid gesture recognizer state");
}
}
}
void lv_indev_gesture_recognizers_update(lv_indev_t * indev, lv_indev_touch_data_t * touches, uint16_t touch_cnt)
{
lv_indev_gesture_type_t type;
/* First check if a recognizer state is RECOGNIZED or ENDED. *
* In that case, call its recongizer function and reset the other*/
type = get_first_recognized_or_ended_gesture(indev);
if(type != LV_INDEV_GESTURE_NONE) {
for(int i = 0; i < LV_INDEV_GESTURE_CNT; i++) {
if(indev->recognizers[i].recog_fn != NULL) {
/* Update all recognizers to let them process input */
indev->recognizers[i].recog_fn(&indev->recognizers[i], &touches[0], touch_cnt);
/* Then reset the recognizers which did not repport RECONIZED or ENDED */
if(((lv_indev_gesture_type_t)i) != type) {
reset_recognizer(&indev->recognizers[i]);
}
}
}
}
else {
/* Otherwise call all recognizer functions, and stop as soon as one recognizer *
* reports the state RECOGNIZED or ENDED */
for(int i = 0; i < LV_INDEV_GESTURE_CNT; i++) {
if(indev->recognizers[i].recog_fn != NULL) {
indev->recognizers[i].recog_fn(&indev->recognizers[i], &touches[0], touch_cnt);
/* If the new state is RECOGNIZED or ENDED */
if(indev->recognizers[i].state == LV_INDEV_GESTURE_STATE_RECOGNIZED ||
indev->recognizers[i].state == LV_INDEV_GESTURE_STATE_ENDED) {
/* Reset the others registered recognizers */
for(int j = 0; j < LV_INDEV_GESTURE_CNT; j++) {
if(j != i && indev->recognizers[j].recog_fn != NULL) {
reset_recognizer(&indev->recognizers[j]);
}
}
break;
}
}
}
}
}
void lv_indev_gesture_recognizers_set_data(lv_indev_t * indev, lv_indev_data_t * data)
{
lv_indev_gesture_type_t type;
type = get_first_recognized_or_ended_gesture(indev);
/* If a gesture is RECOGNIZED or ENDED, set only its data */
if(type != LV_INDEV_GESTURE_NONE) {
lv_indev_set_gesture_data(data, &indev->recognizers[type], type);
}
else {
/* Otherwise, set data from all initialized recognizer */
for(int i = 0; i < LV_INDEV_GESTURE_CNT; i++) {
if(indev->recognizers[i].recog_fn != NULL) {
lv_indev_set_gesture_data(data, &indev->recognizers[i], i);
}
}
}
}
/********************
* 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
* Caluclate the direction from the starting center of a two fingers swipe gesture
* @param recognizer pointer to the recognizer handling the two fingers
* swipe gesture
* @return the direction of the swipe, from the starting center
*/
lv_indev_gesture_recognizer_t * lv_indev_get_gesture_recognizer(lv_event_t * gesture_event)
static lv_dir_t calculate_swipe_dir(lv_indev_gesture_recognizer_t * recognizer)
{
float abs_x = LV_ABS(recognizer->info->delta_x);
float abs_y = LV_ABS(recognizer->info->delta_y);
if(abs_x > abs_y) {
return recognizer->info->delta_x > 0 ? LV_DIR_RIGHT : LV_DIR_LEFT;
}
else {
return recognizer->info->delta_y > 0 ? LV_DIR_BOTTOM : LV_DIR_TOP;
}
}
/**
* Get the gesture recognizer associated to the event
* @param gesture_event an LV_GESTURE_EVENT event
* @param type the type of the recognizer we want to get
* @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_gesture_type_t type)
{
lv_indev_t * indev;
@@ -317,9 +655,9 @@ lv_indev_gesture_recognizer_t * lv_indev_get_gesture_recognizer(lv_event_t * ges
indev = (lv_indev_t *) gesture_event->param;
if(indev == NULL || indev->gesture_data == NULL) return NULL;
if(indev == NULL || indev->gesture_data[type] == NULL) return NULL;
return (lv_indev_gesture_recognizer_t *) indev->gesture_data;
return (lv_indev_gesture_recognizer_t *) indev->gesture_data[type];
}
/**
@@ -331,22 +669,36 @@ static void reset_recognizer(lv_indev_gesture_recognizer_t * recognizer)
uint8_t finger_cnt;
lv_indev_gesture_t * info;
lv_indev_gesture_configuration_t * conf;
lv_recognizer_func_t recog_fn;
if(recognizer == NULL) return;
finger_cnt = recognizer->info->finger_cnt;
info = recognizer->info;
conf = recognizer->config;
recog_fn = recognizer->recog_fn;
/* Set everything to zero but preserve the motion descriptors,
* which are located at the start of the lv_indev_gesture_t struct */
lv_memzero((uint8_t *)info + sizeof(info->motions), sizeof(lv_indev_gesture_t) - sizeof(info->motions));
lv_memzero(recognizer, sizeof(lv_indev_gesture_recognizer_t));
info->finger_cnt = finger_cnt;
recognizer->scale = info->scale = 1;
recognizer->info = info;
recognizer->config = conf;
recognizer->recog_fn = recog_fn;
recognizer->scale = 1;
recognizer->rotation = 0.0;
recognizer->info->rotation = 0.0;
recognizer->info->p_rotation = 0.0;
recognizer->info->scale = 1.0;
recognizer->info->delta_x = 0.0;
recognizer->info->delta_y = 0.0;
recognizer->info->finger_cnt = finger_cnt;
recognizer->state = LV_INDEV_GESTURE_STATE_NONE;
recognizer->type = LV_INDEV_GESTURE_NONE;
}
/**
@@ -362,6 +714,7 @@ static lv_indev_gesture_t * init_gesture_info(void)
LV_ASSERT_MALLOC(info);
info->scale = 1;
info->rotation = 0.0;
for(i = 0; i < LV_GESTURE_MAX_POINTS; i++) {
info->motions[i].finger = -1;
@@ -587,15 +940,33 @@ static void gesture_calculate_factors(lv_indev_gesture_t * gesture, int touch_po
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;
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 = (int32_t)center_x;
g->center.y = (int32_t)center_y;
}
/**
* Get the type of the first gesture which reports either a LV_INDEV_GESTURE_STATE_RECOGNIZED
* or LV_INDEV_GESTURE_STATE_ENDED state
* @param indev pointer to the indev device from which we want to check the gestures states
* @return the type of the gesture having the state LV_INDEV_GESTURE_STATE_RECOGNIZED or
* LV_INDEV_GESTURE_STATE_ENDED, if found
* LV_INDEV_GESTURE_NONE otherwise
*/
static lv_indev_gesture_type_t get_first_recognized_or_ended_gesture(lv_indev_t * indev)
{
for(int i = 0; i < LV_INDEV_GESTURE_CNT; i++) {
if(indev->recognizers[i].state == LV_INDEV_GESTURE_STATE_RECOGNIZED ||
indev->recognizers[i].state == LV_INDEV_GESTURE_STATE_ENDED)
return (lv_indev_gesture_type_t) i;
}
return LV_INDEV_GESTURE_NONE;
}
#endif /* LV_USE_GESTURE_RECOGNITION */

View File

@@ -38,9 +38,14 @@ extern "C" {
struct lv_indev_gesture;
struct lv_indev_gesture_configuration;
typedef struct lv_indev_gesture_recognizer lv_indev_gesture_recognizer_t;
typedef struct lv_indev_touch_data lv_indev_touch_data_t;
typedef struct lv_indev_gesture lv_indev_gesture_t;
typedef struct lv_indev_gesture_configuration lv_indev_gesture_configuration_t;
typedef void (*lv_recognizer_func_t)(lv_indev_gesture_recognizer_t *, lv_indev_touch_data_t *, uint16_t);
/* The states of a gesture recognizer */
typedef enum {
LV_INDEV_GESTURE_STATE_NONE = 0, /* Beginning & end */
@@ -52,15 +57,15 @@ typedef enum {
/* Data structures for touch events - used to repsensent a libinput event */
/* Emitted by devices capable of tracking identifiable contacts (type B) */
typedef struct {
struct lv_indev_touch_data {
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 {
struct lv_indev_gesture_recognizer {
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 */
@@ -68,10 +73,12 @@ typedef struct {
float rotation; /* Relevant for rotation */
float distance; /* Relevant for swipes */
float speed;
lv_dir_t two_fingers_swipe_dir; /* Relevant for swipes */
lv_indev_gesture_configuration_t * config;
} lv_indev_gesture_recognizer_t;
lv_indev_gesture_configuration_t * config; /* The recognizer config, containing the gestures
thresholds */
lv_recognizer_func_t recog_fn; /* The recognizer function that this recongnizer must execute */
};
/**********************
* GLOBAL PROTOTYPES
@@ -81,7 +88,8 @@ typedef struct {
/* PINCH Gesture */
/**
* Detects a pinch gesture
* Pinch gesture recognizer function
* Will update the recognizer data
* @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.
@@ -89,39 +97,86 @@ typedef struct {
void lv_indev_gesture_detect_pinch(lv_indev_gesture_recognizer_t * recognizer, lv_indev_touch_data_t * touches,
uint16_t touch_cnt);
/**
* Rotation gesture recognizer function
* Will update the recognizer data
* @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_rotation(lv_indev_gesture_recognizer_t * recognizer, lv_indev_touch_data_t * touches,
uint16_t touch_cnt);
/**
* Two finger swipe gesture recognizer function
* Will update the recognizer data
* @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_two_fingers_swipe(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.
* @param indev pointer to the indev device containing the pinch recognizer
* @param threshold threshold for a pinch up gesture to be recognized
*/
void lv_indev_set_pinch_up_threshold(lv_indev_gesture_recognizer_t * recognizer, float threshold);
void lv_indev_set_pinch_up_threshold(lv_indev_t * indev, 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.
* @param indev pointer to the indev device containing the pinch recognizer
* @param threshold threshold for a pinch down gesture to be recognized
*/
void lv_indev_set_pinch_down_threshold(lv_indev_gesture_recognizer_t * recognizer, float threshold);
void lv_indev_set_pinch_down_threshold(lv_indev_t * indev, float threshold);
/**
* Set the rotation threshold in radian for the rotation gesture
* @param indev pointer to the indev device containing the rotation recognizer
* @param threshold threshold in radian for a rotation gesture to be recognized
*/
void lv_indev_set_rotation_rad_threshold(lv_indev_t * indev, float threshold);
/**
* Obtains the current scale of a pinch gesture
* @param gesture_event pointer to a gesture recognizer event
* @param gesture_event pointer to a gesture event
* @return the scale of the current gesture
*/
float lv_event_get_pinch_scale(lv_event_t * gesture_event);
/**
* Obtains the current angle in radian of a rotation gesture
* @param gesture_event pointer to a gesture event
* @return the rotation angle in radian of the current gesture
*/
float lv_event_get_rotation(lv_event_t * gesture_event);
/**
* Obtains the current distance in pixels of a two fingers swipe gesture, from the starting center
* @param gesture_event pointer to a gesture event
* @return the distance from the center, in pixels, of the current gesture
*/
float lv_event_get_two_fingers_swipe_distance(lv_event_t * gesture_event);
/**
* Obtains the current direction from the center of a two finger swipe
* @param gesture_event pointer to a gesture event
* @return the rotation angle in radian of the current gesture
*/
lv_dir_t lv_event_get_two_fingers_swipe_dir(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);
void lv_indev_set_gesture_data(lv_indev_data_t * data, lv_indev_gesture_recognizer_t * recognizer,
lv_indev_gesture_type_t type);
/**
* Obtains the center point of a gesture
@@ -135,7 +190,14 @@ void lv_indev_get_gesture_center_point(lv_indev_gesture_recognizer_t * recognize
* @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);
lv_indev_gesture_state_t lv_event_get_gesture_state(lv_event_t * gesture_event, lv_indev_gesture_type_t type);
/**
* Obtains the current event type of the gesture recognizer attached to an event
* @param gesture_event pointer to a gesture recognizer event
* @return current event type of the gesture recognizer
*/
lv_indev_gesture_type_t lv_event_get_gesture_type(lv_event_t * gesture_event);
/**
* Obtains the coordinates of the current primary point
@@ -151,6 +213,23 @@ void lv_indev_get_gesture_primary_point(lv_indev_gesture_recognizer_t * recogniz
*/
bool lv_indev_recognizer_is_active(lv_indev_gesture_recognizer_t * recognizer);
/**
* Update the recognizers. It execute the recognizers functions and checks for
* LV_GESTURE_STATE_RECOGNIZED or LV_GESTURE_STATE_ENDED gestures.
* To be called in the indev read_cb.
* @param indev pointer to the indev containing from which the reconizer need an update
* @param touches indev touch data array, containing the last touch data from indev
* since the last recognizers update
* @param touch_cnt number of indev touch data in touches
*/
void lv_indev_gesture_recognizers_update(lv_indev_t * indev, lv_indev_touch_data_t * touches, uint16_t touch_cnt);
/**
* Set the lv_indev_data_t struct from the recognizer data.
* To be called in the indev read_cb.
*/
void lv_indev_gesture_recognizers_set_data(lv_indev_t * indev, lv_indev_data_t * data);
/**********************
* MACROS

View File

@@ -69,10 +69,14 @@ struct lv_indev_gesture {
};
/* Recognizer configuration. It stores the thresholds needed to detect the gestures and
* consider them as recognized. Once recognized, indev start sending LV_GESTURE event
*/
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 */
float pinch_up_threshold; /* Threshold for the pinch up gesture to be recognized - in pixels */
float pinch_down_threshold; /* Threshold for the pinch down gesture to be recognized - in pixels */
float rotation_angle_rad_threshold; /* Threshold for the rotation gesture to be recognized - in radians */
};

View File

@@ -16,6 +16,7 @@ extern "C" {
#include "lv_indev.h"
#include "../misc/lv_anim.h"
#include "lv_indev_scroll.h"
#include "lv_indev_gesture.h"
/*********************
* DEFINES
@@ -117,8 +118,12 @@ struct _lv_indev_t {
lv_event_list_t event_list;
lv_anim_t * scroll_throw_anim;
lv_indev_gesture_type_t gesture_type;
void * gesture_data;
#if LV_USE_GESTURE_RECOGNITION
lv_indev_gesture_recognizer_t recognizers[LV_INDEV_GESTURE_CNT];
lv_indev_gesture_type_t cur_gesture;
void * gesture_data[LV_INDEV_GESTURE_CNT];
lv_indev_gesture_type_t gesture_type[LV_INDEV_GESTURE_CNT];
#endif
};
/**********************
@@ -133,6 +138,7 @@ struct _lv_indev_t {
*/
lv_obj_t * lv_indev_find_scroll_obj(lv_indev_t * indev);
/**********************
* MACROS
**********************/