init Files
This commit is contained in:
303
libraries/lvgl/docs/porting/indev.rst
Normal file
303
libraries/lvgl/docs/porting/indev.rst
Normal file
@@ -0,0 +1,303 @@
|
||||
.. _porting_indev:
|
||||
|
||||
======================
|
||||
Input device interface
|
||||
======================
|
||||
|
||||
Types of input devices
|
||||
**********************
|
||||
|
||||
To create an input device use
|
||||
|
||||
.. code:: c
|
||||
|
||||
/*Register at least one display before you register any input devices*/
|
||||
lv_indev_t * indev = lv_indev_create();
|
||||
lv_indev_set_type(indev, LV_INDEV_TYPE_...); /*See below.*/
|
||||
lv_indev_set_read_cb(indev, read_cb); /*See below.*/
|
||||
|
||||
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
|
||||
|
||||
``read_cb`` is a function pointer which will be called periodically to
|
||||
report the current state of an input device.
|
||||
|
||||
Visit :ref:`Input devices <indev>` to learn more about input
|
||||
devices in general.
|
||||
|
||||
Touchpad, mouse or any pointer
|
||||
------------------------------
|
||||
|
||||
Input devices that can click points on the screen belong to this
|
||||
category.
|
||||
|
||||
.. code:: 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Mouse cursor
|
||||
~~~~~~~~~~~~
|
||||
|
||||
To set a mouse cursor use :cpp:expr:`lv_indev_set_cursor(indev, &img_cursor)`.
|
||||
|
||||
Crown behavior
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
The "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 :cpp:enum:`LV_EVENT_ROTARY` 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 :cpp:func:`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)
|
||||
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 (divided by 4). The value of the widget will be incremented by this value or
|
||||
the widget will be scrolled this amount of pixels.
|
||||
|
||||
Keypad or keyboard
|
||||
------------------
|
||||
|
||||
Full keyboards with all the letters or simple keypads with a few
|
||||
navigation buttons belong here.
|
||||
|
||||
To use a keyboard/keypad:
|
||||
|
||||
- Register a ``read_cb`` function and use :cpp:enumerator:`LV_INDEV_TYPE_KEYPAD` type.
|
||||
- An object group has to be created: ``lv_group_t * g = lv_group_create()`` and objects have to be added to
|
||||
it with :cpp:expr:`lv_group_add_obj(g, obj)`
|
||||
- The created group has to be assigned to an input device: :cpp:expr:`lv_indev_set_group(indev, g)`
|
||||
- Use ``LV_KEY_...`` to navigate among the objects in the group. See
|
||||
``lv_core/lv_group.h`` for the available keys.
|
||||
|
||||
.. code:: 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
|
||||
-------
|
||||
|
||||
With an encoder you can do the following:
|
||||
|
||||
1. Press its button
|
||||
2. Long-press its button
|
||||
3. Turn left
|
||||
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 object 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* (similarly to the *Keypads*) the objects should be
|
||||
added to groups.
|
||||
|
||||
.. code:: 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;
|
||||
}
|
||||
|
||||
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 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:: 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();
|
||||
}
|
||||
}
|
||||
|
||||
Button
|
||||
------
|
||||
|
||||
*Buttons* mean external "hardware" buttons next to the screen which are
|
||||
assigned to specific coordinates of the screen. If a button is pressed
|
||||
it will simulate the pressing on the assigned coordinate. (Similarly to a touchpad)
|
||||
|
||||
To assign 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}, ...}``
|
||||
|
||||
:important: The points_array can't go out of scope. Either declare it as a global variable
|
||||
or as a static variable inside a function.`
|
||||
|
||||
.. code:: 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}``).
|
||||
|
||||
.. _porting_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 object.
|
||||
- ``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 ``lv_timer_...()`` functions. :c:macro:`LV_DEF_REFR_PERIOD`
|
||||
in ``lv_conf.h`` sets the default read period.
|
||||
|
||||
Feedback
|
||||
--------
|
||||
|
||||
Besides ``read_cb`` a ``feedback_cb`` callback can be also specified in
|
||||
:cpp:type:`lv_indev_t`. ``feedback_cb`` is called when any type of event is sent
|
||||
by the input devices (independently of its type). This allows generating
|
||||
feedback for the user, e.g. to play a sound on :cpp:enumerator:`LV_EVENT_CLICKED`.
|
||||
|
||||
Associating with a display
|
||||
--------------------------
|
||||
|
||||
Every input device is associated with a display. By default, a new input
|
||||
device is added to the last display created or explicitly selected
|
||||
(using :cpp:func:`lv_display_set_default`). The associated display is stored and
|
||||
can be changed in ``disp`` field of the driver.
|
||||
|
||||
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.
|
||||
|
||||
Switching the input device to event-driven mode
|
||||
-----------------------------------------------
|
||||
|
||||
Normally the input event 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 file descriptor (fd).
|
||||
|
||||
You can do this in the following way:
|
||||
|
||||
.. code:: 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:: that :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.
|
||||
|
||||
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 driver.
|
||||
- `INdev features <indev>` to learn more about higher level input device features.
|
||||
|
||||
API
|
||||
***
|
||||
Reference in New Issue
Block a user