diff --git a/docs/src/details/main-modules/indev.rst b/docs/src/details/main-modules/indev.rst
index d07fa33da5..975ec6e72e 100644
--- a/docs/src/details/main-modules/indev.rst
+++ b/docs/src/details/main-modules/indev.rst
@@ -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 `_ 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 `_ 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
------------------
diff --git a/examples/others/gestures/lv_example_gestures.c b/examples/others/gestures/lv_example_gestures.c
index 517142bacf..f035cd91b5 100644
--- a/examples/others/gestures/lv_example_gestures.c
+++ b/examples/others/gestures/lv_example_gestures.c
@@ -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(), ¢er_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 */
diff --git a/src/drivers/wayland/lv_wayland.c b/src/drivers/wayland/lv_wayland.c
index 8b85ae5a04..b4afbd64f7 100644
--- a/src/drivers/wayland/lv_wayland.c
+++ b/src/drivers/wayland/lv_wayland.c
@@ -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);
}
diff --git a/src/indev/lv_indev.c b/src/indev/lv_indev.c
index a9b09dad12..84ac23f748 100644
--- a/src/indev/lv_indev.c
+++ b/src/indev/lv_indev.c
@@ -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
+}
diff --git a/src/indev/lv_indev.h b/src/indev/lv_indev.h
index 6bdf9b1c5a..d6c60c4049 100644
--- a/src/indev/lv_indev.h
+++ b/src/indev/lv_indev.h
@@ -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;
diff --git a/src/indev/lv_indev_gesture.c b/src/indev/lv_indev_gesture.c
index 3ea34ca8b6..d278873145 100644
--- a/src/indev/lv_indev_gesture.c
+++ b/src/indev/lv_indev_gesture.c
@@ -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 */
diff --git a/src/indev/lv_indev_gesture.h b/src/indev/lv_indev_gesture.h
index 9b57689995..08318b9157 100644
--- a/src/indev/lv_indev_gesture.h
+++ b/src/indev/lv_indev_gesture.h
@@ -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
diff --git a/src/indev/lv_indev_gesture_private.h b/src/indev/lv_indev_gesture_private.h
index 35dd4ed28f..752672dfcc 100644
--- a/src/indev/lv_indev_gesture_private.h
+++ b/src/indev/lv_indev_gesture_private.h
@@ -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 */
};
diff --git a/src/indev/lv_indev_private.h b/src/indev/lv_indev_private.h
index 1ea590e6af..83b65c77b8 100644
--- a/src/indev/lv_indev_private.h
+++ b/src/indev/lv_indev_private.h
@@ -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
**********************/