# Gestures (/main-modules/indev/gestures)



Overview [#overview]

Pointer input devices can detect simple (up, down, left, right) and multi-touch
(swipe, pinch, scroll) gestures.

By default, most widgets send gestures to their parents so they can be detected on the
Screen widget in the form of an <ApiLink name="LV_EVENT_GESTURE" /> event. For example:

To prevent passing the gesture event to the parent from a widget, use
<ApiLink name="lv_obj_remove_flag" display="lv_obj_remove_flag(widget, LV_OBJ_FLAG_GESTURE_BUBBLE)" />.

Note that gestures are not triggered if a widget is being scrolled.

If you performed some action on a gesture, you can call
<ApiLink name="lv_indev_wait_release" display="lv_indev_wait_release(lv_indev_active())" /> in the event handler to prevent
LVGL from sending further input-device-related events.

Simple Gestures [#simple-gestures]

Simple gestures are always enabled and very easy to use:

```c title=" " lineNumbers=1
void my_event(lv_event_t * e)
{
    lv_obj_t * screen = lv_event_get_current_target(e);
    lv_dir_t dir = lv_indev_get_gesture_dir(lv_indev_active());
    switch(dir) {
        case LV_DIR_LEFT:
            ...
            break;
        case LV_DIR_RIGHT:
            ...
            break;
        case LV_DIR_TOP:
            ...
            break;
        case LV_DIR_BOTTOM:
            ...
            break;
    }
}

...

lv_obj_add_event_cb(screen1, my_event, LV_EVENT_GESTURE, NULL);
```

To trigger a gesture, two things need to happen. The movement needs to be:

1. fast enough: the difference between the current and the previous point must be
   greater than Indev's `gesture_min_velocity`
2. large enough: the difference between the first and the current point must be
   greater than Indev's `gesture_min_distance`

These limits can be set via <ApiLink name="lv_indev_set_gesture_min_velocity" /> and
<ApiLink name="lv_indev_set_gesture_min_distance" /> accordingly.

Multi-touch Gestures [#multi-touch-gestures]

LVGL has the ability to recognize multi-touch gestures. Currently, these multi-touch
gestures are supported:

* Two fingers pinch (up and down)
* Two fingers rotation
* Two fingers swipe (infinite)

To enable multi-touch gesture recognition, set the <ApiLink name="LV_USE_GESTURE_RECOGNITION" />
option in the `lv_conf.h` file.

Usage [#usage]

The recognizers can be updated to recognize gestures by calling
<ApiLink name="lv_indev_gesture_recognizers_update" display="lv_indev_gesture_recognizers_update(indev, touches, touch_cnt)" />. 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. Currently, only one
multi-touch gesture can be recognized or ended at a time.

Once the recognizers are updated, calling
<ApiLink name="lv_indev_gesture_recognizers_set_data" display="lv_indev_gesture_recognizers_set_data(indev, data)" /> will update the
<ApiLink name="lv_indev_data_t" /> structure. This should also be done in the indev
`read_cb`.

The way the touch points are collected varies based on the hardware and drivers used.

Here is a generic example of the `read_cb`:

```c title=" " lineNumbers=1
static void touch_read_callback(lv_indev_t * indev, lv_indev_data_t * data)
{
    /* Stores the collected touch events */
    lv_indev_touch_data_t touches[10];

    /* Store the current touch (finger) count */
    int32_t touch_cnt;

    /* Get the touch points */
    touch_cnt = my_read_touch_points(touches);

    lv_indev_gesture_recognizers_update(indev, touches, touch_cnt);
    lv_indev_gesture_recognizers_set_data(indev, data);

    /* Also process normal touch */
    if(touch_cnt > 0) {
        data->point.x = touches[0].point.x;
        data->point.y = touches[0].point.y;
        data->state = LV_INDEV_STATE_PRESSED;
    } else {
        data->state = LV_INDEV_STATE_RELEASED;
    }
}
```

LVGL sends events if the gestures are in one of the following states:

* <ApiLink name="LV_INDEV_GESTURE_STATE_RECOGNIZED" />: The gesture has been recognized and is now
  active.
* <ApiLink name="LV_INDEV_GESTURE_STATE_ENDED" />: The gesture has ended.

Events [#events]

Once a gesture is recognized or ended, a <ApiLink name="LV_EVENT_GESTURE" /> is sent. The user can
use these functions to gather more information about the gesture:

* <ApiLink name="lv_event_get_gesture_type" display="lv_event_get_gesture_type(lv_event_t * e)" />: Get the type of the gesture.
  Use this to check which multi-touch gesture is currently reported.
* <ApiLink name="lv_event_get_gesture_state" display="lv_event_get_gesture_state(lv_event_t * e, lv_indev_gesture_type_t type)" />:
  Get the state of the gesture as <ApiLink name="lv_indev_gesture_state_t" />. It can be one
  of:

  * <ApiLink name="LV_INDEV_GESTURE_STATE_NONE" />: The gesture is not active.
  * <ApiLink name="LV_INDEV_GESTURE_STATE_RECOGNIZED" />: The gesture is recognized and
    can be used.
  * <ApiLink name="LV_INDEV_GESTURE_STATE_ENDED" />: The gesture ended.

The user can then request the gesture values with the following functions:

* <ApiLink name="lv_event_get_pinch_scale" display="lv_event_get_pinch_scale(lv_event_t * e)" />: Get the pinch scale. Only
  relevant for pinch gestures.
* <ApiLink name="lv_event_get_rotation" display="lv_event_get_rotation(lv_event_t * e)" />: Get the rotation in radians. Only
  relevant for rotation gestures.
* <ApiLink name="lv_event_get_two_fingers_swipe_distance" display="lv_event_get_two_fingers_swipe_distance(lv_event_t * e)" />: Get the
  distance in pixels from the gesture starting center. Only relevant for two-finger
  swipe gestures.
* <ApiLink name="lv_event_get_two_fingers_swipe_dir" display="lv_event_get_two_fingers_swipe_dir(lv_event_t * e)" />: Get the direction
  from the starting center. Only relevant for two-finger swipe gestures.

This allows the user to react to the gestures and use the gesture values. An example
of such an application is available in the source tree:
`examples/others/gestures/lv_example_gestures.c`.

Thresholds [#thresholds]

The gesture recognizers can be configured to modify the gesture thresholds:

* <ApiLink name="lv_indev_set_pinch_up_threshold" display="lv_indev_set_pinch_up_threshold(lv_indev_t * indev, float threshold)" />:
  Set the pinch-up (zoom in) threshold in pixels.
* <ApiLink name="lv_indev_set_pinch_down_threshold" display="lv_indev_set_pinch_down_threshold(lv_indev_t * indev, float threshold)" />:
  Set the pinch-down (zoom out) threshold in pixels.
* <ApiLink name="lv_indev_set_rotation_rad_threshold" display="lv_indev_set_rotation_rad_threshold(lv_indev_t * indev, float threshold)" />:
  Set the rotation angle threshold in radians.

libinput Example [#libinput-example]

In the case of `libinput`, touch events are received asynchronously. To handle it,
the touch array and touch count need to be global variables:

```c title=" " lineNumbers=1
/* Stores the collected touch events */
static lv_indev_touch_data_t touches[10];

/* Store the current touch (finger) count SINCE THE LAST READ */
static int32_t touch_cnt;
```

so the flow is:

1. Save the touch events asynchronously in the `touches` array (one event per finger)
2. Process the touch data in the `read_cb`
3. Forget the touches and wait for new touch events by setting `touch_cnt = 0` at
   the end of the `read_cb`

```c title=" " lineNumbers=1
/**
 * @brief Convert the libinput event to LVGL's representation of a touch event
 * @param ev a pointer to the libinput event
 */
static void touch_process(struct libinput_event *ev)
{
   int type = libinput_event_get_type(ev);
   struct libinput_event_touch *touch_ev = libinput_event_get_touch_event(ev);
   int id = libinput_event_touch_get_slot(touch_ev);
   uint32_t time = libinput_event_touch_get_time(touch_ev);

   /* Get the last event for the contact point */
   lv_indev_touch_data_t *cur = NULL;

   /* Find if the touch is already stored in the array by its ID.
    * If there are 2 active touches it's enough to check those. */
   for (int i = 0; i < touch_cnt; i++) {
       if (touches[i].id == id) {
           cur = &touches[i];
           break;
       }
   }

   if (cur && cur->timestamp == time) {
       /* Previous event has the same timestamp - ignore duplicate event */
       return;
   }

   /* Save the new touch */
   if (cur == NULL ||
           type == LIBINPUT_EVENT_TOUCH_UP ||
           type == LIBINPUT_EVENT_TOUCH_DOWN) {

       cur = &touches[touch_cnt];
       cur->id = id; /* It will be needed to identify the touches in the next events */
       touch_cnt++;
   }

   /* Process the event */
   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;
}

/**
 * @brief Filter out libinput events that are not related to touches
 * @param ev a pointer to the libinput event
 */
static void process_libinput_event(struct libinput_event *ev)
{
   int 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_process(ev);
           break;
       default:
           /* Skip unrelated libinput events */
           return;
   }
}
```
