.. _indev:

=======================
Input Device (lv_indev)
=======================


.. _indev_creation:

Creating an Input Device
************************

To create an input device on the :ref:`default_display`:

.. code-block:: c

    /* Create and set up at least one display before you register any input devices. */
    lv_indev_t * indev = lv_indev_create();        /* Create input device connected to Default Display. */
    lv_indev_set_type(indev, LV_INDEV_TYPE_...);   /* Touch pad is a pointer-like device. */
    lv_indev_set_read_cb(indev, my_input_read);    /* Set driver function. */

If you have multiple displays, you will need to ensure the Default Display is set
to the display your input device is "connected to" before making the above calls.

The ``type`` member can be:

- :cpp:enumerator:`LV_INDEV_TYPE_POINTER`: touchpad or mouse
- :cpp:enumerator:`LV_INDEV_TYPE_KEYPAD`: keyboard or keypad
- :cpp:enumerator:`LV_INDEV_TYPE_ENCODER`: encoder with left/right turn and push options
- :cpp:enumerator:`LV_INDEV_TYPE_BUTTON`: external buttons virtually pressing the screen

``my_input_read`` is a function pointer which will be called periodically to
report the current state of an input device to LVGL.



Touchpad, Touch-Screen, Mouse or Any Pointer
--------------------------------------------

Input devices that can click points on the display belong to the POINTER
category.  Here is an example of a simple input-device Read Callback function:

.. code-block:: c

    lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
    ...

    void my_input_read(lv_indev_t * indev, lv_indev_data_t * data)
    {
        if(touchpad_pressed) {
            data->point.x = touchpad_x;
            data->point.y = touchpad_y;
            data->state = LV_INDEV_STATE_PRESSED;
        } else {
            data->state = LV_INDEV_STATE_RELEASED;
        }
    }


.. _indev_cursor:

Mouse Cursor
~~~~~~~~~~~~

Pointer input devices (like a mouse) can have a cursor.

.. code-block:: c

   ...
   lv_indev_t * mouse_indev = lv_indev_create();
   ...
   LV_IMAGE_DECLARE(mouse_cursor_icon);                          /* Declare the image source. */
   lv_obj_t * cursor_obj = lv_image_create(lv_screen_active());  /* Create image Widget for cursor. */
   lv_image_set_src(cursor_obj, &mouse_cursor_icon);             /* Set image source. */
   lv_indev_set_cursor(mouse_indev, cursor_obj);                 /* Connect image to Input Device. */

Note that the cursor object should have
:cpp:expr:`lv_obj_remove_flag(cursor_obj, LV_OBJ_FLAG_CLICKABLE)`.
For images, *clicking* is disabled by default.


.. _indev_gestures:

Gestures
~~~~~~~~

Pointer input devices can detect basic gestures.  By default, most Widgets send
gestures to their parents so they can be detected on the Screen Widget in the
form of an :cpp:enumerator:`LV_EVENT_GESTURE` event.  For example:

.. code-block:: c

    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 prevent passing the gesture event to the parent from a Widget, use
:cpp:expr:`lv_obj_remove_flag(widget, LV_OBJ_FLAG_GESTURE_BUBBLE)`.

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

If you did some action on a gesture you can call
:cpp:expr:`lv_indev_wait_release(lv_indev_active())` in the event handler to
prevent LVGL sending further input-device-related events.

.. _indev_crown:

Crown Behavior
~~~~~~~~~~~~~~

A "Crown" is a rotary device typically found on smart watches.

When the user clicks somewhere and after that turns the rotary
the last clicked widget will be either scrolled or it's value will be incremented/decremented
(e.g. in case of a slider).

As this behavior is tightly related to the last clicked widget, the crown support is
an extension of the pointer input device.  Just set ``data->diff`` to the number of
turned steps and LVGL will automatically send the :cpp:enumerator:`LV_EVENT_ROTARY`
event or scroll the widget based on the ``editable`` flag in the widget's class.
Non-editable widgets are scrolled and for editable widgets the event is sent.

To get the steps in an event callback use ``int32_t diff = lv_event_get_rotary_diff(e)``

The rotary sensitivity can be adjusted on 2 levels:

1.  in the input device by the ``indev->rotary_sensitivity`` element (1/256 unit), and
2.  by the ``rotary_sensitivity`` style property in the widget (1/256 unit).

The final diff is calculated like this:

``diff_final = diff_in * (indev_sensitivity / 256) +  (widget_sensitivity / 256);``


For example, if both the indev and widget sensitivity is set to 128 (0.5), the input
diff will be multiplied by 0.25.  The value of the Widget will be incremented by that
value or the Widget will be scrolled that amount of pixels.

Multi-touch gestures
====================

LVGL has the ability to recognize multi-touch gestures, when a gesture
is detected a ``LV_EVENT_GESTURE`` is passed to the object on which the
gesture occurred. Currently, 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.

Currently, the system sends the events if the gestures are in one of the following states:

- ``LV_INDEV_GESTURE_STATE_RECOGNIZED``: The gesture has been recognized and is now active.
- ``LV_INDEV_GESTURE_STATE_ENDED``: The gesture has ended.


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 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 be 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;

   /* An array that stores the collected touch events */
   static lv_indev_touch_data_t touches[10];

   /* A counter that needs to be incremented each time a touch event is received */
   static uint8_t touch_cnt;

   static void touch_read_callback(lv_indev_t * drv, lv_indev_data_t * data)
   {

        lv_indev_touch_data_t * touch;

        lv_indev_update_recognizers(drv, &touches[0], touch_cnt);

        touch_cnt = 0;

        /* Set the gesture information, before returning to LVGL */
        lv_indev_gesture_recognizers_set_data(drv, data);

   }

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``:

.. code-block:: c

  /**
  * @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;

      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``.


Keypad or Keyboard
------------------

Full keyboards with all the letters or simple keypads with a few navigation buttons
belong in the keypad category.

You can fully control the user interface without a touchpad or mouse by using a
keypad or encoder.  It works similar to the *TAB* key on the PC to select an element
in an application or web page.

To use a keyboard or keypad:

- Register a Read Callback function for your device and set its type to
  :cpp:enumerator:`LV_INDEV_TYPE_KEYPAD`.
- Create a Widget Group (``lv_group_t * g = lv_group_create()``) and add Widgets to
  it with :cpp:expr:`lv_group_add_obj(g, widget)`.
- Assign the group to an input device: :cpp:expr:`lv_indev_set_group(indev, g)`.
- Use ``LV_KEY_...`` to navigate among the Widgets in the group.  See
  ``lv_core/lv_group.h`` for the available keys.

.. code-block:: c

   lv_indev_set_type(indev, LV_INDEV_TYPE_KEYPAD);

   ...

   void keyboard_read(lv_indev_t * indev, lv_indev_data_t * data){
     data->key = last_key();            /* Get the last pressed or released key */

     if(key_pressed()) data->state = LV_INDEV_STATE_PRESSED;
     else data->state = LV_INDEV_STATE_RELEASED;
   }


Encoder
-------

A common example of an encoder is a device with a turning knob that tells the hosting
device *when* the knob is being turned, and *in which direction*.

With an encoder your application can receive events from the following:

1.  press of its button,
2.  long-press of its button,
3.  turn left, and
4.  turn right.

In short, the Encoder input devices work like this:

- By turning the encoder you can focus on the next/previous object.
- When you press the encoder on a simple object (like a button), it will be clicked.
- If you press the encoder on a complex object (like a list, message box, etc.)
  the Widget will go to edit mode whereby you can navigate inside the
  object by turning the encoder.
- To leave edit mode, long press the button.

To use an Encoder (similar to the *Keypads*) the Widgets should be added to a group.

.. code-block:: c

   lv_indev_set_type(indev, LV_INDEV_TYPE_ENCODER);

   ...

   void encoder_read(lv_indev_t * indev, lv_indev_data_t * data){
     data->enc_diff = enc_get_new_moves();

     if(enc_pressed()) data->state = LV_INDEV_STATE_PRESSED;
     else data->state = LV_INDEV_STATE_RELEASED;
   }


.. _indev_groups:

Widget Groups
-------------
When input focus needs to be managed among a set of Widgets (e.g. to capture user
input from a keypad or encoder), that set of Widgets is placed in a group which
thereafter manages how input focus moves from Widget to Widget.

In each group there is exactly one object with focus which receives the pressed keys
or the encoder actions.  For example, if a :ref:`Text Area <lv_textarea>` has focus
and you press some letter on a keyboard, the keys will be sent and inserted into the
text area.  Similarly, if a :ref:`Slider <lv_slider>` has focus and you press the
left or right arrows, the slider's value will be changed.

You need to associate an input device with a group.  An input device can
send key events to only one group but a group can receive data from more
than one input device.

To create a group use :cpp:expr:`lv_group_t * g = lv_group_create()` and to add
a Widget to the group use :cpp:expr:`lv_group_add_obj(g, widget)`.

Once a Widget has been added to a group, you can find out what group it is in
using :cpp:expr:`lv_obj_get_group(widget)`.

To find out what Widget in a group has focus, if any, call
:cpp:expr:`lv_group_get_focused(group)`.  If a Widget in that group has focus, it
will return a pointer to it, otherwise it will return NULL.

To associate a group with an input device use :cpp:expr:`lv_indev_set_group(indev, g)`.



.. _indev_keys:

Keys
~~~~

There are some predefined keys which have special meaning:

- :cpp:enumerator:`LV_KEY_NEXT`: Move focus to next object
- :cpp:enumerator:`LV_KEY_PREV`: Move focus to previous object
- :cpp:enumerator:`LV_KEY_ENTER`: Triggers :cpp:enumerator:`LV_EVENT_PRESSED`, :cpp:enumerator:`LV_EVENT_CLICKED`, or :cpp:enumerator:`LV_EVENT_LONG_PRESSED` etc. events
- :cpp:enumerator:`LV_KEY_UP`: Increase value or move up
- :cpp:enumerator:`LV_KEY_DOWN`: Decrease value or move down
- :cpp:enumerator:`LV_KEY_RIGHT`: Increase value or move to the right
- :cpp:enumerator:`LV_KEY_LEFT`: Decrease value or move to the left
- :cpp:enumerator:`LV_KEY_ESC`: Close or exit (e.g. close a :ref:`Drop-Down List <lv_dropdown>`)
- :cpp:enumerator:`LV_KEY_DEL`: Delete (e.g. a character on the right in a :ref:`Text Area <lv_textarea>`)
- :cpp:enumerator:`LV_KEY_BACKSPACE`: Delete (e.g. a character on the left in a :ref:`Text Area <lv_textarea>`)
- :cpp:enumerator:`LV_KEY_HOME`: Go to the beginning/top (e.g. in a :ref:`Text Area <lv_textarea>`)
- :cpp:enumerator:`LV_KEY_END`: Go to the end (e.g. in a :ref:`Text Area <lv_textarea>`)

The most important special keys in your :cpp:func:`read_cb` function are:

- :cpp:enumerator:`LV_KEY_NEXT`
- :cpp:enumerator:`LV_KEY_PREV`
- :cpp:enumerator:`LV_KEY_ENTER`
- :cpp:enumerator:`LV_KEY_UP`
- :cpp:enumerator:`LV_KEY_DOWN`
- :cpp:enumerator:`LV_KEY_LEFT`
- :cpp:enumerator:`LV_KEY_RIGHT`

You should translate some of your keys to these special keys to support navigation
in a group and interact with selected Widgets.

Usually, it's enough to use only :cpp:enumerator:`LV_KEY_LEFT` and :cpp:enumerator:`LV_KEY_RIGHT` because most
Widgets can be fully controlled with them.

With an encoder you should use only :cpp:enumerator:`LV_KEY_LEFT`, :cpp:enumerator:`LV_KEY_RIGHT`,
and :cpp:enumerator:`LV_KEY_ENTER`.

Edit and Navigate Mode
~~~~~~~~~~~~~~~~~~~~~~

Since a keypad has plenty of keys, it's easy to navigate between Widgets
and edit them using the keypad. But encoders have a limited number of
"keys" and hence it is difficult to navigate using the default options.
*Navigate* and *Edit* modes are used to avoid this problem with
encoders.

In *Navigate* mode, an encoder's :cpp:enumerator:`LV_KEY_LEFT` or :cpp:enumerator:`LV_KEY_RIGHT` is translated to
:cpp:enumerator:`LV_KEY_NEXT` or :cpp:enumerator:`LV_KEY_PREV`. Therefore, the next or previous object will be
selected by turning the encoder. Pressing :cpp:enumerator:`LV_KEY_ENTER` will change
to *Edit* mode.

In *Edit* mode, :cpp:enumerator:`LV_KEY_NEXT` and :cpp:enumerator:`LV_KEY_PREV` is usually used to modify an
object. Depending on the Widget's type, a short or long press of
:cpp:enumerator:`LV_KEY_ENTER` changes back to *Navigate* mode. Usually, a Widget
which cannot be pressed (like a :ref:`Slider <lv_slider>`) leaves
*Edit* mode upon a short click. But with Widgets where a short click has
meaning (e.g. :ref:`Button <lv_button>`), a long press is required.

Default Group
~~~~~~~~~~~~~

Interactive widgets (such as Buttons, Checkboxes, Sliders, etc.) can
be automatically added to a default group. Just create a group with
:cpp:expr:`lv_group_t * g = lv_group_create()` and set the default group with
:cpp:expr:`lv_group_set_default(g)`

Don't forget to assign one or more input devices to the default group
with :cpp:expr:`lv_indev_set_group(my_indev, g)`.

Styling
-------

When a Widget receives focus either by clicking it via touchpad or by navigating to
it with an encoder or keypad, it goes to the :cpp:enumerator:`LV_STATE_FOCUSED`
state.  Hence, focused styles will be applied to it.

If a Widget switches to edit mode it enters the
:cpp:expr:`LV_STATE_FOCUSED | LV_STATE_EDITED` states so any style properties
assigned to these states will be shown.

See :ref:`styles` for more details.




Using Buttons with Encoder Logic
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In addition to standard encoder behavior, you can also utilize its logic
to navigate (focus) and edit widgets using buttons. This is especially
handy if you have only few buttons available, or you want to use other
buttons in addition to an encoder wheel.

You need to have 3 buttons available:

- :cpp:enumerator:`LV_KEY_ENTER`: will simulate press or pushing of the encoder button.
- :cpp:enumerator:`LV_KEY_LEFT`: will simulate turning encoder left.
- :cpp:enumerator:`LV_KEY_RIGHT`: will simulate turning encoder right.
- Other keys will be passed to the focused widget.

If you hold the keys it will simulate an encoder advance with period
specified in ``indev_drv.long_press_repeat_time``.

.. code-block:: c


   lv_indev_set_type(indev, LV_INDEV_TYPE_ENCODER);

   ...

   void encoder_with_keys_read(lv_indev_t * indev, lv_indev_data_t * data){
     data->key = last_key();            /* Get the last pressed or released key */
                                        /* use LV_KEY_ENTER for encoder press */
     if(key_pressed()) data->state = LV_INDEV_STATE_PRESSED;
     else {
         data->state = LV_INDEV_STATE_RELEASED;
         /* Optionally you can also use enc_diff, if you have encoder */
         data->enc_diff = enc_get_new_moves();
     }
   }


Hardware Button
---------------

A *Hardware Button* here is an external button (switch) typically next to the screen
which is assigned to specific coordinates of the screen.  If a button is pressed it
will simulate the pressing on the assigned coordinate, similar to a touchpad.

To assign Hardware Buttons to coordinates use ``lv_indev_set_button_points(my_indev,
points_array)``. ``points_array`` should look like ``const lv_point_t points_array[]
= { {12,30}, {60,90}, ...}``

.. admonition::  Important:

    ``points_array`` cannot be allowed to go out of scope.  Either declare it as a
    global variable or as a static variable inside a function.

.. code-block:: c

   lv_indev_set_type(indev, LV_INDEV_TYPE_BUTTON);

   ...

   void button_read(lv_indev_t * indev, lv_indev_data_t * data){
       static uint32_t last_btn = 0;   /* Store the last pressed button */
       int btn_pr = my_btn_read();     /* Get the ID (0,1,2...) of the pressed button */
       if(btn_pr >= 0) {               /* Is there a button press? (E.g. -1 indicated no button was pressed) */
          last_btn = btn_pr;           /* Save the ID of the pressed button */
          data->state = LV_INDEV_STATE_PRESSED;  /* Set the pressed state */
       } else {
          data->state = LV_INDEV_STATE_RELEASED; /* Set the released state */
       }

       data->btn_id = last_btn;         /* Save the last button */
   }

When the ``button_read`` callback in the example above changes the ``data->btn_id`` to ``0``
a press/release action at the first index of the ``points_array`` will be performed (``{12,30}``).


.. _indev_other_features:

Other Features
**************

Parameters
----------

The default value of the following parameters can be changed in :cpp:type:`lv_indev_t`:

- ``scroll_limit`` Number of pixels to slide before actually scrolling the Widget
- ``scroll_throw`` Scroll throw (momentum) slow-down in [%]. Greater value means faster slow-down.
- ``long_press_time`` Press time to send :cpp:enumerator:`LV_EVENT_LONG_PRESSED` (in milliseconds)
- ``long_press_repeat_time`` Interval of sending :cpp:enumerator:`LV_EVENT_LONG_PRESSED_REPEAT` (in milliseconds)
- ``read_timer`` pointer to the ``lv_timer`` which reads the input device. Its parameters
  can be changed by calling ``lv_timer_...()`` functions. :c:macro:`LV_DEF_REFR_PERIOD`
  in ``lv_conf.h`` sets the default read period.

Buffered Reading
----------------

By default, LVGL calls ``read_cb`` periodically. Because of this
intermittent polling there is a chance that some user gestures are
missed.

To solve this you can write an event driven driver for your input device
that buffers measured data. In ``read_cb`` you can report the buffered
data instead of directly reading the input device. Setting the
``data->continue_reading`` flag will tell LVGL there is more data to
read and it should call ``read_cb`` again.

If the driver can provide precise timestamps for buffered events, it can
overwrite ``data->timestamp``. By default, this is initialized to
:cpp:func:`lv_tick_get()` just before invoking ``read_cb``.


.. _indev event mode:

Switching the Input Device to Event-Driven Mode
-----------------------------------------------

Normally an Input Device is read every :c:macro:`LV_DEF_REFR_PERIOD`
milliseconds (set in ``lv_conf.h``).  However, in some cases, you might
need more control over when to read the input device. For example, you
might need to read it by polling a file descriptor (fd).

You can do this by:

.. code-block:: c

   /* Update the input device's running mode to LV_INDEV_MODE_EVENT */
   lv_indev_set_mode(indev, LV_INDEV_MODE_EVENT);

   ...

   /* Call this anywhere you want to read the input device */
   lv_indev_read(indev);

.. note:: :cpp:func:`lv_indev_read`, :cpp:func:`lv_timer_handler` and :cpp:func:`_lv_display_refr_timer` cannot run at the same time.

.. note:: For devices in event-driven mode, `data->continue_reading` is ignored.


Pausing the Indev Timer
-----------------------

It's not always possible to take an indev reading directly inside
a raw interrupt handler. Typically a flag would be set inside the interrupt handler
which would be checked and reset inside the indev read callback where the reading
would actually be taken. This works fine, but the indev read callback is constantly
"polling" a flag which may go for long periods unset. We cannot use :ref:`indev event mode`
because :cpp:func:`lv_indev_read` should not be called in an interrupt handler.

For this situation you can use the timer-based indev read callback as usual but
pause the indev timer if there hasn't been an interrupt in a while.
Resuming a timer is typically safe in an interrupt handler.
Care must be taken to avoid race conditions.

.. code-block:: c

    volatile bool interrupt_occurred;
    lv_timer_t * volatile indev_timer;

    void interrupt_handler(void)
    {
        interrupt_occurred = true;
        if(indev_timer) lv_timer_resume(indev_timer);
    }

    uint32_t last_interrupt_tick;

    void my_input_read(lv_indev_t * indev, lv_indev_data_t * data)
    {
        uint32_t tick_now = lv_tick_get();

        /* if no interrupt has happened in the past 100 ms, pause the indev timer */
        if(lv_tick_diff(tick_now, last_interrupt_tick) > 100) {
            lv_timer_pause(indev_timer);
        }

        if(interrupt_occurred) {
            interrupt_occurred = false;
            last_interrupt_tick = tick_now;
            /* 
             * Ensure the timer is running in case an interrupt occurred
             * just after the timer was paused. Without this, a race condition
             * could leave the timer paused and input events would not be processed.
             */
            lv_timer_resume(indev_timer);
        }

        /* perform the reading */
        /* ... */
    }

.. code-block:: c

    /* in your setup code */
    indev_timer = lv_indev_get_read_timer(indev);


.. admonition::  Further Reading

    - `lv_port_indev_template.c <https://github.com/lvgl/lvgl/blob/master/examples/porting/lv_port_indev_template.c>`__
      for a template for your own Input-Device driver.



.. _indev_api:

API
***
