mirror of
https://github.com/lvgl/lvgl.git
synced 2025-01-28 07:03:00 +08:00
feat(drivers): add libinput/xkb driver (#5486)
This commit is contained in:
parent
c835e888b1
commit
b45ef5ccf9
2
.github/workflows/ccpp.yml
vendored
2
.github/workflows/ccpp.yml
vendored
@ -95,7 +95,7 @@ jobs:
|
|||||||
|
|
||||||
install: |
|
install: |
|
||||||
apt-get update -y
|
apt-get update -y
|
||||||
apt-get install build-essential ccache libgcc-10-dev python3 libpng-dev ruby-full gcovr cmake libjpeg62-turbo-dev libfreetype6-dev libasan6 pngquant python3-pip -q -y
|
apt-get install build-essential ccache libgcc-10-dev python3 libpng-dev ruby-full gcovr cmake libjpeg62-turbo-dev libfreetype6-dev libasan6 pngquant python3-pip libinput-dev libxkbcommon-dev pkg-config -q -y
|
||||||
pip install pypng lz4
|
pip install pypng lz4
|
||||||
/usr/sbin/update-ccache-symlinks
|
/usr/sbin/update-ccache-symlinks
|
||||||
echo 'export PATH="/usr/lib/ccache:$PATH"' | tee -a ~/.bashrc
|
echo 'export PATH="/usr/lib/ccache:$PATH"' | tee -a ~/.bashrc
|
||||||
|
14
Kconfig
14
Kconfig
@ -1489,6 +1489,20 @@ menu "LVGL configuration"
|
|||||||
bool "Use evdev input driver"
|
bool "Use evdev input driver"
|
||||||
default n
|
default n
|
||||||
|
|
||||||
|
config LV_USE_LIBINPUT
|
||||||
|
bool "Use libinput input driver"
|
||||||
|
default n
|
||||||
|
|
||||||
|
config LV_LIBINPUT_BSD
|
||||||
|
bool "Use the BSD variant of the libinput input driver"
|
||||||
|
depends on LV_USE_LIBINPUT
|
||||||
|
default n
|
||||||
|
|
||||||
|
config LV_LIBINPUT_XKB
|
||||||
|
bool "Enable full keyboard support via XKB"
|
||||||
|
depends on LV_USE_LIBINPUT
|
||||||
|
default n
|
||||||
|
|
||||||
config LV_USE_ST7735
|
config LV_USE_ST7735
|
||||||
bool "Use ST7735 LCD driver"
|
bool "Use ST7735 LCD driver"
|
||||||
default n
|
default n
|
||||||
|
@ -7,5 +7,6 @@ Drivers
|
|||||||
|
|
||||||
display/index
|
display/index
|
||||||
touchpad/index
|
touchpad/index
|
||||||
|
libinput
|
||||||
X11
|
X11
|
||||||
windows
|
windows
|
||||||
|
87
docs/integration/driver/libinput.rst
Normal file
87
docs/integration/driver/libinput.rst
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
===============
|
||||||
|
Libinput Driver
|
||||||
|
===============
|
||||||
|
|
||||||
|
Overview
|
||||||
|
--------
|
||||||
|
|
||||||
|
Libinput is an input stack for processes that need to provide events from commonly used input devices. That includes mice, keyboards, touchpads,
|
||||||
|
touchscreens and graphics tablets. Libinput handles device-specific quirks and provides an easy-to-use API to receive events from devices.
|
||||||
|
|
||||||
|
Prerequisites
|
||||||
|
-------------
|
||||||
|
|
||||||
|
You have the development version of libinput installed (usually ``libinput-dev``). If your input device requires quirks, make sure they are
|
||||||
|
installed as well (usually in ``/usr/share/libinput/*.quirks``). To test if your device is set up correctly for use with libinput, you can
|
||||||
|
run ``libinput list-devices``.
|
||||||
|
|
||||||
|
.. code:: console
|
||||||
|
|
||||||
|
$ sudo libinput list-devices
|
||||||
|
...
|
||||||
|
Device: ETPS/2 Elantech Touchpad
|
||||||
|
Kernel: /dev/input/event5
|
||||||
|
Group: 10
|
||||||
|
Seat: seat0, default
|
||||||
|
Size: 102x74mm
|
||||||
|
Capabilities: pointer gesture
|
||||||
|
Tap-to-click: disabled
|
||||||
|
Tap-and-drag: enabled
|
||||||
|
...
|
||||||
|
|
||||||
|
If your device doesn't show up, you may have to configure udev and the appropriate udev rules to connect it.
|
||||||
|
|
||||||
|
Additionally, if you want full keyboard support, including letters and modifiers, you'll need the development version of libxkbcommon
|
||||||
|
installed (usually ``libxkbcommon-dev``).
|
||||||
|
|
||||||
|
Configuring the driver
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Enable the libinput driver support in lv_conf.h, by cmake compiler define or by KConfig.
|
||||||
|
|
||||||
|
.. code:: c
|
||||||
|
|
||||||
|
#define LV_USE_LIBINPUT 1
|
||||||
|
|
||||||
|
Full keyboard support needs to be enabled separately.
|
||||||
|
|
||||||
|
.. code:: c
|
||||||
|
|
||||||
|
#define LV_LIBINPUT_XKB 1
|
||||||
|
#define LV_LIBINPUT_XKB_KEY_MAP { .rules = NULL, .model = "pc101", .layout = "us", .variant = NULL, .options = NULL }
|
||||||
|
|
||||||
|
To find the right key map values, you may use the ``setxkbmap -query`` command.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
To set up an input device via the libinput driver, all you need to do is call ``lv_libinput_create`` with the respective device type
|
||||||
|
(``LV_INDEV_TYPE_POINTER`` or ``LV_INDEV_TYPE_KEYPAD``) and device node path (e.g. ``/dev/input/event5``).
|
||||||
|
|
||||||
|
.. code:: c
|
||||||
|
|
||||||
|
lv_indev_t *indev = lv_libinput_create(LV_INDEV_TYPE_POINTER, "/dev/input/event5");
|
||||||
|
|
||||||
|
Note that touchscreens are treated as (absolute) pointer devices by the libinput driver and require ``LV_INDEV_TYPE_POINTER``.
|
||||||
|
|
||||||
|
Depending on your system, the device node paths might not be stable across reboots. If this is the case, you can use ``lv_libinput_find_dev``
|
||||||
|
to find the first device that has a specific capability.
|
||||||
|
|
||||||
|
.. code:: c
|
||||||
|
|
||||||
|
char *path = lv_libinput_find_dev(LV_LIBINPUT_CAPABILITY_TOUCH, true);
|
||||||
|
|
||||||
|
The second argument controls whether or not all devices are rescanned. If you have many devices connected this can get quite slow.
|
||||||
|
Therefore, you should only specify ``true`` on the first call when calling this method multiple times in a row. If you want to find
|
||||||
|
all devices that have a specific capability, use ``lv_libinput_find_devs``.
|
||||||
|
|
||||||
|
If you want to connect a keyboard device to a textarea, create a dedicated input group and set it on both the indev and textarea.
|
||||||
|
|
||||||
|
.. code:: c
|
||||||
|
|
||||||
|
lv_obj_t *textarea = lv_textarea_create(...);
|
||||||
|
...
|
||||||
|
lv_group_t *keyboard_input_group = lv_group_create();
|
||||||
|
lv_indev_set_group(indev, keyboard_input_group);
|
||||||
|
lv_group_add_obj(keyboard_input_group, textarea);
|
||||||
|
|
@ -886,6 +886,20 @@
|
|||||||
/*Driver for evdev input devices*/
|
/*Driver for evdev input devices*/
|
||||||
#define LV_USE_EVDEV 0
|
#define LV_USE_EVDEV 0
|
||||||
|
|
||||||
|
/*Driver for libinput input devices*/
|
||||||
|
#define LV_USE_LIBINPUT 0
|
||||||
|
|
||||||
|
#if LV_USE_LIBINPUT
|
||||||
|
#define LV_LIBINPUT_BSD 0
|
||||||
|
|
||||||
|
/*Full keyboard support*/
|
||||||
|
#define LV_LIBINPUT_XKB 0
|
||||||
|
#if LV_LIBINPUT_XKB
|
||||||
|
/*"setxkbmap -query" can help find the right values for your keyboard*/
|
||||||
|
#define LV_LIBINPUT_XKB_KEY_MAP { .rules = NULL, .model = "pc101", .layout = "us", .variant = NULL, .options = NULL }
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
/*Drivers for LCD devices connected via SPI/parallel port*/
|
/*Drivers for LCD devices connected via SPI/parallel port*/
|
||||||
#define LV_USE_ST7735 0
|
#define LV_USE_ST7735 0
|
||||||
#define LV_USE_ST7789 0
|
#define LV_USE_ST7789 0
|
||||||
|
@ -6,5 +6,5 @@
|
|||||||
#
|
#
|
||||||
# Note: This script is run by the CI workflows.
|
# Note: This script is run by the CI workflows.
|
||||||
sudo apt update
|
sudo apt update
|
||||||
sudo apt install gcc python3 libpng-dev ruby-full gcovr cmake libjpeg-turbo8-dev libfreetype6-dev pngquant
|
sudo apt install gcc python3 libpng-dev ruby-full gcovr cmake libjpeg-turbo8-dev libfreetype6-dev pngquant libinput-dev libxkbcommon-dev pkg-config
|
||||||
pip3 install pypng lz4
|
pip3 install pypng lz4
|
||||||
|
664
src/drivers/libinput/lv_libinput.c
Normal file
664
src/drivers/libinput/lv_libinput.c
Normal file
@ -0,0 +1,664 @@
|
|||||||
|
/**
|
||||||
|
* @file lv_libinput.c
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* INCLUDES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
#include "lv_libinput.h"
|
||||||
|
|
||||||
|
#if LV_USE_LIBINPUT
|
||||||
|
|
||||||
|
#include "../../display/lv_display_private.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <linux/limits.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <libinput.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#if LV_LIBINPUT_BSD
|
||||||
|
#include <dev/evdev/input.h>
|
||||||
|
#else
|
||||||
|
#include <linux/input.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* DEFINES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* TYPEDEFS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
struct lv_libinput_device {
|
||||||
|
lv_libinput_capability capabilities;
|
||||||
|
char * path;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* STATIC PROTOTYPES
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
static bool _rescan_devices(void);
|
||||||
|
static bool _add_scanned_device(char * path, lv_libinput_capability capabilities);
|
||||||
|
static void _reset_scanned_devices(void);
|
||||||
|
|
||||||
|
static void * _poll_thread(void * data);
|
||||||
|
|
||||||
|
lv_libinput_event_t * _get_event(lv_libinput_t * state);
|
||||||
|
bool _event_pending(lv_libinput_t * state);
|
||||||
|
lv_libinput_event_t * _create_event(lv_libinput_t * state);
|
||||||
|
|
||||||
|
static void _read(lv_indev_t * indev, lv_indev_data_t * data);
|
||||||
|
static void _read_pointer(lv_libinput_t * state, struct libinput_event * event);
|
||||||
|
static void _read_keypad(lv_libinput_t * state, struct libinput_event * event);
|
||||||
|
|
||||||
|
static int _open_restricted(const char * path, int flags, void * user_data);
|
||||||
|
static void _close_restricted(int fd, void * user_data);
|
||||||
|
|
||||||
|
static void _delete(lv_libinput_t * dsc);
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* STATIC VARIABLES
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
static struct lv_libinput_device * devices = NULL;
|
||||||
|
static size_t num_devices = 0;
|
||||||
|
|
||||||
|
static const int timeout = 100; // ms
|
||||||
|
static const nfds_t nfds = 1;
|
||||||
|
|
||||||
|
static const struct libinput_interface interface = {
|
||||||
|
.open_restricted = _open_restricted,
|
||||||
|
.close_restricted = _close_restricted,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* MACROS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* GLOBAL FUNCTIONS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
lv_libinput_capability lv_libinput_query_capability(struct libinput_device * device)
|
||||||
|
{
|
||||||
|
lv_libinput_capability capability = LV_LIBINPUT_CAPABILITY_NONE;
|
||||||
|
if(libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_KEYBOARD)
|
||||||
|
&& (libinput_device_keyboard_has_key(device, KEY_ENTER) || libinput_device_keyboard_has_key(device, KEY_KPENTER))) {
|
||||||
|
capability |= LV_LIBINPUT_CAPABILITY_KEYBOARD;
|
||||||
|
}
|
||||||
|
if(libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_POINTER)) {
|
||||||
|
capability |= LV_LIBINPUT_CAPABILITY_POINTER;
|
||||||
|
}
|
||||||
|
if(libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_TOUCH)) {
|
||||||
|
capability |= LV_LIBINPUT_CAPABILITY_TOUCH;
|
||||||
|
}
|
||||||
|
return capability;
|
||||||
|
}
|
||||||
|
|
||||||
|
char * lv_libinput_find_dev(lv_libinput_capability capabilities, bool force_rescan)
|
||||||
|
{
|
||||||
|
char * path = NULL;
|
||||||
|
lv_libinput_find_devs(capabilities, &path, 1, force_rescan);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t lv_libinput_find_devs(lv_libinput_capability capabilities, char ** found, size_t count, bool force_rescan)
|
||||||
|
{
|
||||||
|
if((!devices || force_rescan) && !_rescan_devices()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t num_found = 0;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < num_devices && num_found < count; ++i) {
|
||||||
|
if(devices[i].capabilities & capabilities) {
|
||||||
|
found[num_found] = devices[i].path;
|
||||||
|
num_found++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return num_found;
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_indev_t * lv_libinput_create(lv_indev_type_t indev_type, const char * dev_path)
|
||||||
|
{
|
||||||
|
lv_libinput_t * dsc = lv_malloc_zeroed(sizeof(lv_libinput_t));
|
||||||
|
LV_ASSERT_MALLOC(dsc);
|
||||||
|
if(dsc == NULL) return NULL;
|
||||||
|
|
||||||
|
dsc->libinput_context = libinput_path_create_context(&interface, NULL);
|
||||||
|
if(!dsc->libinput_context) {
|
||||||
|
LV_LOG_ERROR("libinput_path_create_context failed: %s", strerror(errno));
|
||||||
|
_delete(dsc);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsc->libinput_device = libinput_path_add_device(dsc->libinput_context, dev_path);
|
||||||
|
if(!dsc->libinput_device) {
|
||||||
|
_delete(dsc);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsc->libinput_device = libinput_device_ref(dsc->libinput_device);
|
||||||
|
if(!dsc->libinput_device) {
|
||||||
|
_delete(dsc);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsc->fd = libinput_get_fd(dsc->libinput_context);
|
||||||
|
|
||||||
|
/* Prepare poll */
|
||||||
|
dsc->fds[0].fd = dsc->fd;
|
||||||
|
dsc->fds[0].events = POLLIN;
|
||||||
|
dsc->fds[0].revents = 0;
|
||||||
|
|
||||||
|
#if LV_LIBINPUT_XKB
|
||||||
|
struct xkb_rule_names names = LV_LIBINPUT_XKB_KEY_MAP;
|
||||||
|
lv_xkb_init(&(dsc->xkb), names);
|
||||||
|
#endif /* LV_LIBINPUT_XKB */
|
||||||
|
|
||||||
|
/* Create indev */
|
||||||
|
lv_indev_t * indev = lv_indev_create();
|
||||||
|
if(!indev) {
|
||||||
|
_delete(dsc);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
lv_indev_set_type(indev, indev_type);
|
||||||
|
lv_indev_set_read_cb(indev, _read);
|
||||||
|
lv_indev_set_driver_data(indev, dsc);
|
||||||
|
|
||||||
|
/* Set up thread & lock */
|
||||||
|
pthread_mutex_init(&dsc->event_lock, NULL);
|
||||||
|
pthread_create(&dsc->worker_thread, NULL, _poll_thread, dsc);
|
||||||
|
|
||||||
|
return indev;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lv_libinput_delete(lv_indev_t * indev)
|
||||||
|
{
|
||||||
|
_delete(lv_indev_get_driver_data(indev));
|
||||||
|
lv_indev_delete(indev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* STATIC FUNCTIONS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rescan all attached evdev devices and store capable ones into the static devices array for quick later filtering
|
||||||
|
* @return true if the operation succeeded
|
||||||
|
*/
|
||||||
|
static bool _rescan_devices(void)
|
||||||
|
{
|
||||||
|
_reset_scanned_devices();
|
||||||
|
|
||||||
|
DIR * dir;
|
||||||
|
struct dirent * ent;
|
||||||
|
if(!(dir = opendir("/dev/input"))) {
|
||||||
|
perror("unable to open directory /dev/input");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct libinput * context = libinput_path_create_context(&interface, NULL);
|
||||||
|
|
||||||
|
while((ent = readdir(dir))) {
|
||||||
|
if(strncmp(ent->d_name, "event", 5) != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 11 characters for /dev/input/ + length of name + 1 NUL terminator */
|
||||||
|
char * path = malloc((11 + strlen(ent->d_name) + 1) * sizeof(char));
|
||||||
|
if(!path) {
|
||||||
|
perror("could not allocate memory for device node path");
|
||||||
|
libinput_unref(context);
|
||||||
|
_reset_scanned_devices();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
strcpy(path, "/dev/input/");
|
||||||
|
strcat(path, ent->d_name);
|
||||||
|
|
||||||
|
struct libinput_device * device = libinput_path_add_device(context, path);
|
||||||
|
if(!device) {
|
||||||
|
perror("unable to add device to libinput context");
|
||||||
|
free(path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The device pointer is guaranteed to be valid until the next libinput_dispatch. Since we're not dispatching events
|
||||||
|
* as part of this function, we don't have to increase its reference count to keep it alive.
|
||||||
|
* https://wayland.freedesktop.org/libinput/doc/latest/api/group__base.html#gaa797496f0150b482a4e01376bd33a47b */
|
||||||
|
|
||||||
|
lv_libinput_capability capabilities = lv_libinput_query_capability(device);
|
||||||
|
|
||||||
|
libinput_path_remove_device(device);
|
||||||
|
|
||||||
|
if(capabilities == LV_LIBINPUT_CAPABILITY_NONE) {
|
||||||
|
free(path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!_add_scanned_device(path, capabilities)) {
|
||||||
|
free(path);
|
||||||
|
libinput_unref(context);
|
||||||
|
_reset_scanned_devices();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
libinput_unref(context);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add a new scanned device to the static devices array, growing its size when necessary
|
||||||
|
* @param path device file path
|
||||||
|
* @param capabilities device input capabilities
|
||||||
|
* @return true if the operation succeeded
|
||||||
|
*/
|
||||||
|
static bool _add_scanned_device(char * path, lv_libinput_capability capabilities)
|
||||||
|
{
|
||||||
|
/* Double array size every 2^n elements */
|
||||||
|
if((num_devices & (num_devices + 1)) == 0) {
|
||||||
|
struct lv_libinput_device * tmp = realloc(devices, (2 * num_devices + 1) * sizeof(struct lv_libinput_device));
|
||||||
|
if(!tmp) {
|
||||||
|
perror("could not reallocate memory for devices array");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
devices = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
devices[num_devices].path = path;
|
||||||
|
devices[num_devices].capabilities = capabilities;
|
||||||
|
num_devices++;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* reset the array of scanned devices and free any dynamically allocated memory
|
||||||
|
*/
|
||||||
|
static void _reset_scanned_devices(void)
|
||||||
|
{
|
||||||
|
if(!devices) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t i = 0; i < num_devices; ++i) {
|
||||||
|
free(devices[i].path);
|
||||||
|
}
|
||||||
|
free(devices);
|
||||||
|
|
||||||
|
devices = NULL;
|
||||||
|
num_devices = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void * _poll_thread(void * data)
|
||||||
|
{
|
||||||
|
lv_libinput_t * dsc = (lv_libinput_t *)data;
|
||||||
|
struct libinput_event * event;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
LV_LOG_INFO("libinput: poll worker started");
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
rc = poll(dsc->fds, nfds, timeout);
|
||||||
|
switch(rc) {
|
||||||
|
case -1:
|
||||||
|
perror(NULL);
|
||||||
|
__attribute__((fallthrough));
|
||||||
|
case 0:
|
||||||
|
if(dsc->deinit) {
|
||||||
|
dsc->deinit = false; /* Signal that we're done */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
libinput_dispatch(dsc->libinput_context);
|
||||||
|
pthread_mutex_lock(&dsc->event_lock);
|
||||||
|
while((event = libinput_get_event(dsc->libinput_context)) != NULL) {
|
||||||
|
_read_pointer(dsc, event);
|
||||||
|
_read_keypad(dsc, event);
|
||||||
|
libinput_event_destroy(event);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&dsc->event_lock);
|
||||||
|
LV_LOG_INFO("libinput: event read");
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_libinput_event_t * _get_event(lv_libinput_t * dsc)
|
||||||
|
{
|
||||||
|
if(dsc->start == dsc->end) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_libinput_event_t * evt = &dsc->points[dsc->start];
|
||||||
|
|
||||||
|
if(++dsc->start == LV_LIBINPUT_MAX_EVENTS)
|
||||||
|
dsc->start = 0;
|
||||||
|
|
||||||
|
return evt;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _event_pending(lv_libinput_t * dsc)
|
||||||
|
{
|
||||||
|
return dsc->start != dsc->end;
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_libinput_event_t * _create_event(lv_libinput_t * dsc)
|
||||||
|
{
|
||||||
|
lv_libinput_event_t * evt = &dsc->points[dsc->end];
|
||||||
|
|
||||||
|
if(++dsc->end == LV_LIBINPUT_MAX_EVENTS)
|
||||||
|
dsc->end = 0;
|
||||||
|
|
||||||
|
/* We have overflowed the buffer, start overwriting
|
||||||
|
* old events.
|
||||||
|
*/
|
||||||
|
if(dsc->end == dsc->start) {
|
||||||
|
LV_LOG_INFO("libinput: overflowed event buffer!");
|
||||||
|
if(++dsc->start == LV_LIBINPUT_MAX_EVENTS)
|
||||||
|
dsc->start = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(evt, 0, sizeof(lv_libinput_event_t));
|
||||||
|
|
||||||
|
return evt;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _read(lv_indev_t * indev, lv_indev_data_t * data)
|
||||||
|
{
|
||||||
|
lv_libinput_t * dsc = lv_indev_get_driver_data(indev);
|
||||||
|
LV_ASSERT_NULL(dsc);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&dsc->event_lock);
|
||||||
|
|
||||||
|
lv_libinput_event_t * evt = _get_event(dsc);
|
||||||
|
|
||||||
|
if(!evt)
|
||||||
|
evt = &dsc->last_event; /* indev expects us to report the most recent state */
|
||||||
|
|
||||||
|
data->point = evt->point;
|
||||||
|
data->state = evt->pressed;
|
||||||
|
data->key = evt->key_val;
|
||||||
|
data->continue_reading = _event_pending(dsc);
|
||||||
|
|
||||||
|
dsc->last_event = *evt; /* Remember the last event for the next call */
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&dsc->event_lock);
|
||||||
|
|
||||||
|
if(evt)
|
||||||
|
LV_LOG_TRACE("libinput_read: (%04d, %04d): %d continue_reading? %d", data->point.x, data->point.y, data->state,
|
||||||
|
data->continue_reading);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _read_pointer(lv_libinput_t * dsc, struct libinput_event * event)
|
||||||
|
{
|
||||||
|
struct libinput_event_touch * touch_event = NULL;
|
||||||
|
struct libinput_event_pointer * pointer_event = NULL;
|
||||||
|
lv_libinput_event_t * evt = NULL;
|
||||||
|
enum libinput_event_type type = libinput_event_get_type(event);
|
||||||
|
int slot = 0;
|
||||||
|
|
||||||
|
switch(type) {
|
||||||
|
case LIBINPUT_EVENT_TOUCH_MOTION:
|
||||||
|
case LIBINPUT_EVENT_TOUCH_DOWN:
|
||||||
|
case LIBINPUT_EVENT_TOUCH_UP:
|
||||||
|
touch_event = libinput_event_get_touch_event(event);
|
||||||
|
break;
|
||||||
|
case LIBINPUT_EVENT_POINTER_MOTION:
|
||||||
|
case LIBINPUT_EVENT_POINTER_BUTTON:
|
||||||
|
case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
|
||||||
|
pointer_event = libinput_event_get_pointer_event(event);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return; /* We don't care about this events */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We need to read unrotated display dimensions directly from the driver because libinput won't account
|
||||||
|
* for any rotation inside of LVGL */
|
||||||
|
lv_display_t * disp = lv_display_get_default();
|
||||||
|
|
||||||
|
/* ignore more than 2 fingers as it will only confuse LVGL */
|
||||||
|
if(touch_event && (slot = libinput_event_touch_get_slot(touch_event)) > 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
evt = _create_event(dsc);
|
||||||
|
|
||||||
|
const int32_t hor_res = disp->physical_hor_res > 0 ? disp->physical_hor_res : disp->hor_res;
|
||||||
|
const int32_t ver_res = disp->physical_ver_res > 0 ? disp->physical_ver_res : disp->ver_res;
|
||||||
|
|
||||||
|
switch(type) {
|
||||||
|
case LIBINPUT_EVENT_TOUCH_MOTION:
|
||||||
|
case LIBINPUT_EVENT_TOUCH_DOWN: {
|
||||||
|
lv_point_t point;
|
||||||
|
point.x = (int32_t)LV_CLAMP(INT32_MIN, libinput_event_touch_get_x_transformed(touch_event, hor_res) - disp->offset_x,
|
||||||
|
INT32_MAX);
|
||||||
|
point.y = (int32_t)LV_CLAMP(INT32_MIN, libinput_event_touch_get_y_transformed(touch_event, ver_res) - disp->offset_y,
|
||||||
|
INT32_MAX);
|
||||||
|
if(point.x < 0 || point.x > disp->hor_res || point.y < 0 || point.y > disp->ver_res) {
|
||||||
|
break; /* ignore touches that are out of bounds */
|
||||||
|
}
|
||||||
|
evt->point = point;
|
||||||
|
evt->pressed = LV_INDEV_STATE_PRESSED;
|
||||||
|
dsc->slots[slot].point = evt->point;
|
||||||
|
dsc->slots[slot].pressed = evt->pressed;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LIBINPUT_EVENT_TOUCH_UP:
|
||||||
|
/*
|
||||||
|
* We don't support "multitouch", but libinput does. To make fast typing with two thumbs
|
||||||
|
* on a keyboard feel good, it's necessary to handle two fingers individually. The edge
|
||||||
|
* case here is if you press a key with one finger and then press a second key with another
|
||||||
|
* finger. No matter which finger you release, it will count as the second finger releasing
|
||||||
|
* and ignore the first because LVGL only stores a single (the latest) pressed state.
|
||||||
|
*
|
||||||
|
* To work around this, we detect the case where one finger is released while the other is
|
||||||
|
* still pressed and insert dummy events so that both release events trigger at the correct
|
||||||
|
* position.
|
||||||
|
*/
|
||||||
|
if(slot == 0 && dsc->slots[1].pressed == LV_INDEV_STATE_PRESSED) {
|
||||||
|
/* The first finger is released while the second finger is still pressed.
|
||||||
|
* We turn P1 > P2 > R1 > R2 into P1 > P2 > (P1) > R1 > (P2) > R2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Inject the dummy press event for the first finger */
|
||||||
|
lv_libinput_event_t * synth_evt = evt;
|
||||||
|
synth_evt->pressed = LV_INDEV_STATE_PRESSED;
|
||||||
|
synth_evt->point = dsc->slots[0].point;
|
||||||
|
|
||||||
|
/* Append the real release event for the first finger */
|
||||||
|
evt = _create_event(dsc);
|
||||||
|
evt->pressed = LV_INDEV_STATE_RELEASED;
|
||||||
|
evt->point = dsc->slots[0].point;
|
||||||
|
|
||||||
|
/* Inject the dummy press event for the second finger */
|
||||||
|
synth_evt = _create_event(dsc);
|
||||||
|
synth_evt->pressed = LV_INDEV_STATE_PRESSED;
|
||||||
|
synth_evt->point = dsc->slots[1].point;
|
||||||
|
}
|
||||||
|
else if(slot == 1 && dsc->slots[0].pressed == LV_INDEV_STATE_PRESSED) {
|
||||||
|
/* The second finger is released while the first finger is still pressed.
|
||||||
|
* We turn P1 > P2 > R2 > R1 into P1 > P2 > R2 > (P1) > R1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Append the real release event for the second finger */
|
||||||
|
evt->pressed = LV_INDEV_STATE_RELEASED;
|
||||||
|
evt->point = dsc->slots[1].point;
|
||||||
|
|
||||||
|
/* Inject the dummy press event for the first finger */
|
||||||
|
lv_libinput_event_t * synth_evt = _create_event(dsc);
|
||||||
|
synth_evt->pressed = LV_INDEV_STATE_PRESSED;
|
||||||
|
synth_evt->point = dsc->slots[0].point;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
evt->pressed = LV_INDEV_STATE_RELEASED;
|
||||||
|
evt->point = dsc->slots[slot].point;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsc->slots[slot].pressed = evt->pressed;
|
||||||
|
break;
|
||||||
|
case LIBINPUT_EVENT_POINTER_MOTION:
|
||||||
|
dsc->pointer_position.x = (int32_t)LV_CLAMP(0, dsc->pointer_position.x + libinput_event_pointer_get_dx(pointer_event),
|
||||||
|
disp->hor_res - 1);
|
||||||
|
dsc->pointer_position.y = (int32_t)LV_CLAMP(0, dsc->pointer_position.y + libinput_event_pointer_get_dy(pointer_event),
|
||||||
|
disp->ver_res - 1);
|
||||||
|
evt->point.x = dsc->pointer_position.x;
|
||||||
|
evt->point.y = dsc->pointer_position.y;
|
||||||
|
evt->pressed = dsc->pointer_button_down;
|
||||||
|
break;
|
||||||
|
case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: {
|
||||||
|
lv_point_t point;
|
||||||
|
point.x = (int32_t)LV_CLAMP(INT32_MIN, libinput_event_pointer_get_absolute_x_transformed(pointer_event,
|
||||||
|
hor_res) - disp->offset_x, INT32_MAX);
|
||||||
|
point.y = (int32_t)LV_CLAMP(INT32_MIN, libinput_event_pointer_get_absolute_y_transformed(pointer_event,
|
||||||
|
ver_res) - disp->offset_y, INT32_MAX);
|
||||||
|
if(point.x < 0 || point.x > disp->hor_res || point.y < 0 || point.y > disp->ver_res) {
|
||||||
|
break; /* ignore pointer events that are out of bounds */
|
||||||
|
}
|
||||||
|
evt->point = point;
|
||||||
|
evt->pressed = dsc->pointer_button_down;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LIBINPUT_EVENT_POINTER_BUTTON: {
|
||||||
|
enum libinput_button_state button_state = libinput_event_pointer_get_button_state(pointer_event);
|
||||||
|
dsc->pointer_button_down = button_state == LIBINPUT_BUTTON_STATE_RELEASED ? LV_INDEV_STATE_RELEASED :
|
||||||
|
LV_INDEV_STATE_PRESSED;
|
||||||
|
evt->point.x = dsc->pointer_position.x;
|
||||||
|
evt->point.y = dsc->pointer_position.y;
|
||||||
|
evt->pressed = dsc->pointer_button_down;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _read_keypad(lv_libinput_t * dsc, struct libinput_event * event)
|
||||||
|
{
|
||||||
|
struct libinput_event_keyboard * keyboard_event = NULL;
|
||||||
|
enum libinput_event_type type = libinput_event_get_type(event);
|
||||||
|
lv_libinput_event_t * evt = NULL;
|
||||||
|
switch(type) {
|
||||||
|
case LIBINPUT_EVENT_KEYBOARD_KEY:
|
||||||
|
evt = _create_event(dsc);
|
||||||
|
keyboard_event = libinput_event_get_keyboard_event(event);
|
||||||
|
enum libinput_key_state key_state = libinput_event_keyboard_get_key_state(keyboard_event);
|
||||||
|
uint32_t code = libinput_event_keyboard_get_key(keyboard_event);
|
||||||
|
#if LV_LIBINPUT_XKB
|
||||||
|
evt->key_val = lv_xkb_process_key(&(dsc->xkb), code, key_state == LIBINPUT_KEY_STATE_PRESSED);
|
||||||
|
#else
|
||||||
|
switch(code) {
|
||||||
|
case KEY_BACKSPACE:
|
||||||
|
evt->key_val = LV_KEY_BACKSPACE;
|
||||||
|
break;
|
||||||
|
case KEY_ENTER:
|
||||||
|
evt->key_val = LV_KEY_ENTER;
|
||||||
|
break;
|
||||||
|
case KEY_PREVIOUS:
|
||||||
|
evt->key_val = LV_KEY_PREV;
|
||||||
|
break;
|
||||||
|
case KEY_NEXT:
|
||||||
|
evt->key_val = LV_KEY_NEXT;
|
||||||
|
break;
|
||||||
|
case KEY_UP:
|
||||||
|
evt->key_val = LV_KEY_UP;
|
||||||
|
break;
|
||||||
|
case KEY_LEFT:
|
||||||
|
evt->key_val = LV_KEY_LEFT;
|
||||||
|
break;
|
||||||
|
case KEY_RIGHT:
|
||||||
|
evt->key_val = LV_KEY_RIGHT;
|
||||||
|
break;
|
||||||
|
case KEY_DOWN:
|
||||||
|
evt->key_val = LV_KEY_DOWN;
|
||||||
|
break;
|
||||||
|
case KEY_TAB:
|
||||||
|
evt->key_val = LV_KEY_NEXT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
evt->key_val = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif /* LV_LIBINPUT_XKB */
|
||||||
|
if(evt->key_val != 0) {
|
||||||
|
/* Only record button state when actual output is produced to prevent widgets from refreshing */
|
||||||
|
evt->pressed = (key_state == LIBINPUT_KEY_STATE_RELEASED) ? LV_INDEV_STATE_RELEASED : LV_INDEV_STATE_PRESSED;
|
||||||
|
|
||||||
|
// just release the key immediatly after it got pressed.
|
||||||
|
// but don't handle special keys where holding a key makes sense
|
||||||
|
if(evt->key_val != LV_KEY_BACKSPACE &&
|
||||||
|
evt->key_val != LV_KEY_UP &&
|
||||||
|
evt->key_val != LV_KEY_LEFT &&
|
||||||
|
evt->key_val != LV_KEY_RIGHT &&
|
||||||
|
evt->key_val != LV_KEY_DOWN &&
|
||||||
|
key_state == LIBINPUT_KEY_STATE_PRESSED) {
|
||||||
|
lv_libinput_event_t * release_evt = _create_event(dsc);
|
||||||
|
release_evt->pressed = LV_INDEV_STATE_RELEASED;
|
||||||
|
release_evt->key_val = evt->key_val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _open_restricted(const char * path, int flags, void * user_data)
|
||||||
|
{
|
||||||
|
LV_UNUSED(user_data);
|
||||||
|
int fd = open(path, flags);
|
||||||
|
return fd < 0 ? -errno : fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _close_restricted(int fd, void * user_data)
|
||||||
|
{
|
||||||
|
LV_UNUSED(user_data);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _delete(lv_libinput_t * dsc)
|
||||||
|
{
|
||||||
|
if(dsc->fd)
|
||||||
|
dsc->deinit = true;
|
||||||
|
|
||||||
|
/* Give worker thread a whole second to quit */
|
||||||
|
for(int i = 0; i < 100; i++) {
|
||||||
|
if(!dsc->deinit)
|
||||||
|
break;
|
||||||
|
usleep(10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dsc->deinit) {
|
||||||
|
LV_LOG_ERROR("libinput worker thread did not quit in time, cancelling it");
|
||||||
|
pthread_cancel(dsc->worker_thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dsc->libinput_device) {
|
||||||
|
libinput_path_remove_device(dsc->libinput_device);
|
||||||
|
libinput_device_unref(dsc->libinput_device);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dsc->libinput_context) {
|
||||||
|
libinput_unref(dsc->libinput_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LV_LIBINPUT_XKB
|
||||||
|
lv_xkb_deinit(&(dsc->xkb));
|
||||||
|
#endif /* LV_LIBINPUT_XKB */
|
||||||
|
|
||||||
|
lv_free(dsc);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* LV_USE_LIBINPUT */
|
134
src/drivers/libinput/lv_libinput.h
Normal file
134
src/drivers/libinput/lv_libinput.h
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
/**
|
||||||
|
* @file lv_libinput.h
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LV_LIBINPUT_H
|
||||||
|
#define LV_LIBINPUT_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* INCLUDES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
#include "../../indev/lv_indev.h"
|
||||||
|
|
||||||
|
#if LV_USE_LIBINPUT
|
||||||
|
|
||||||
|
#include <poll.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#if LV_LIBINPUT_XKB
|
||||||
|
#include "lv_xkb.h"
|
||||||
|
#endif /* LV_LIBINPUT_XKB */
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* DEFINES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* TYPEDEFS
|
||||||
|
**********************/
|
||||||
|
typedef enum {
|
||||||
|
LV_LIBINPUT_CAPABILITY_NONE = 0,
|
||||||
|
LV_LIBINPUT_CAPABILITY_KEYBOARD = 1U << 0,
|
||||||
|
LV_LIBINPUT_CAPABILITY_POINTER = 1U << 1,
|
||||||
|
LV_LIBINPUT_CAPABILITY_TOUCH = 1U << 2
|
||||||
|
} lv_libinput_capability;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
lv_indev_state_t pressed;
|
||||||
|
int key_val;
|
||||||
|
lv_point_t point;
|
||||||
|
} lv_libinput_event_t;
|
||||||
|
|
||||||
|
#define LV_LIBINPUT_MAX_EVENTS 32
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int fd;
|
||||||
|
struct pollfd fds[1];
|
||||||
|
|
||||||
|
/* The points array is implemented as a circular LIFO queue */
|
||||||
|
lv_libinput_event_t points[LV_LIBINPUT_MAX_EVENTS]; /* Event buffer */
|
||||||
|
lv_libinput_event_t slots[2]; /* Realtime state of up to 2 fingers to handle multitouch */
|
||||||
|
|
||||||
|
/* Pointer devices work a bit differently in libinput which requires us to store their last known state */
|
||||||
|
lv_point_t pointer_position;
|
||||||
|
bool pointer_button_down;
|
||||||
|
|
||||||
|
int start; /* Index of start of event queue */
|
||||||
|
int end; /* Index of end of queue*/
|
||||||
|
lv_libinput_event_t last_event; /* Report when no new events
|
||||||
|
* to keep indev state consistent
|
||||||
|
*/
|
||||||
|
bool deinit; /* Tell worker thread to quit */
|
||||||
|
pthread_mutex_t event_lock;
|
||||||
|
pthread_t worker_thread;
|
||||||
|
|
||||||
|
struct libinput * libinput_context;
|
||||||
|
struct libinput_device * libinput_device;
|
||||||
|
|
||||||
|
#if LV_LIBINPUT_XKB
|
||||||
|
lv_xkb_t xkb;
|
||||||
|
#endif /* LV_LIBINPUT_XKB */
|
||||||
|
} lv_libinput_t;
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* GLOBAL PROTOTYPES
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the capabilities of a specific libinput device.
|
||||||
|
* @param device the libinput device to query
|
||||||
|
* @return the supported input capabilities
|
||||||
|
*/
|
||||||
|
lv_libinput_capability lv_libinput_query_capability(struct libinput_device * device);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find connected input device with specific capabilities
|
||||||
|
* @param capabilities required device capabilities
|
||||||
|
* @param force_rescan erase the device cache (if any) and rescan the file system for available devices
|
||||||
|
* @return device node path (e.g. /dev/input/event0) for the first matching device or NULL if no device was found.
|
||||||
|
* The pointer is safe to use until the next forceful device search.
|
||||||
|
*/
|
||||||
|
char * lv_libinput_find_dev(lv_libinput_capability capabilities, bool force_rescan);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find connected input devices with specific capabilities
|
||||||
|
* @param capabilities required device capabilities
|
||||||
|
* @param devices pre-allocated array to store the found device node paths (e.g. /dev/input/event0). The pointers are
|
||||||
|
* safe to use until the next forceful device search.
|
||||||
|
* @param count maximum number of devices to find (the devices array should be at least this long)
|
||||||
|
* @param force_rescan erase the device cache (if any) and rescan the file system for available devices
|
||||||
|
* @return number of devices that were found
|
||||||
|
*/
|
||||||
|
size_t lv_libinput_find_devs(lv_libinput_capability capabilities, char ** found, size_t count, bool force_rescan);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new libinput input device
|
||||||
|
* @param type LV_INDEV_TYPE_POINTER or LV_INDEV_TYPE_KEYPAD
|
||||||
|
* @param dev_path device path, e.g. /dev/input/event0
|
||||||
|
* @return pointer to input device or NULL if opening failed
|
||||||
|
*/
|
||||||
|
lv_indev_t * lv_libinput_create(lv_indev_type_t indev_type, const char * dev_path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a libinput input devic
|
||||||
|
* @param indev pointer to input device
|
||||||
|
*/
|
||||||
|
void lv_libinput_delete(lv_indev_t * indev);
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* MACROS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
#endif /* LV_USE_LIBINPUT */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* LV_LIBINPUT_H */
|
172
src/drivers/libinput/lv_xkb.c
Normal file
172
src/drivers/libinput/lv_xkb.c
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
/**
|
||||||
|
* @file lv_xkb.c
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* INCLUDES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
#include "lv_xkb.h"
|
||||||
|
|
||||||
|
#if defined(LV_LIBINPUT_XKB) && LV_LIBINPUT_XKB
|
||||||
|
|
||||||
|
#include "../../core/lv_group.h"
|
||||||
|
#include "../../misc/lv_log.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* DEFINES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* TYPEDEFS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* STATIC PROTOTYPES
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
static bool _set_keymap(lv_xkb_t * dsc, struct xkb_rule_names names);
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* STATIC VARIABLES
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
static struct xkb_context * context = NULL;
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* MACROS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* GLOBAL FUNCTIONS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
bool lv_xkb_init(lv_xkb_t * dsc, struct xkb_rule_names names)
|
||||||
|
{
|
||||||
|
if(!context) {
|
||||||
|
context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||||
|
if(!context) {
|
||||||
|
LV_LOG_ERROR("xkb_context_new failed: %s", strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _set_keymap(dsc, names);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lv_xkb_deinit(lv_xkb_t * dsc)
|
||||||
|
{
|
||||||
|
if(dsc->state) {
|
||||||
|
xkb_state_unref(dsc->state);
|
||||||
|
dsc->state = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dsc->keymap) {
|
||||||
|
xkb_keymap_unref(dsc->keymap);
|
||||||
|
dsc->keymap = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t lv_xkb_process_key(lv_xkb_t * dsc, uint32_t scancode, bool down)
|
||||||
|
{
|
||||||
|
/* Offset the evdev scancode by 8, see https://xkbcommon.org/doc/current/xkbcommon_8h.html#ac29aee92124c08d1953910ab28ee1997 */
|
||||||
|
xkb_keycode_t keycode = scancode + 8;
|
||||||
|
|
||||||
|
uint32_t result = 0;
|
||||||
|
|
||||||
|
switch(xkb_state_key_get_one_sym(dsc->state, keycode)) {
|
||||||
|
case XKB_KEY_BackSpace:
|
||||||
|
result = LV_KEY_BACKSPACE;
|
||||||
|
break;
|
||||||
|
case XKB_KEY_Return:
|
||||||
|
case XKB_KEY_KP_Enter:
|
||||||
|
result = LV_KEY_ENTER;
|
||||||
|
break;
|
||||||
|
case XKB_KEY_Prior:
|
||||||
|
case XKB_KEY_KP_Prior:
|
||||||
|
result = LV_KEY_PREV;
|
||||||
|
break;
|
||||||
|
case XKB_KEY_Next:
|
||||||
|
case XKB_KEY_KP_Next:
|
||||||
|
result = LV_KEY_NEXT;
|
||||||
|
break;
|
||||||
|
case XKB_KEY_Up:
|
||||||
|
case XKB_KEY_KP_Up:
|
||||||
|
result = LV_KEY_UP;
|
||||||
|
break;
|
||||||
|
case XKB_KEY_Left:
|
||||||
|
case XKB_KEY_KP_Left:
|
||||||
|
result = LV_KEY_LEFT;
|
||||||
|
break;
|
||||||
|
case XKB_KEY_Right:
|
||||||
|
case XKB_KEY_KP_Right:
|
||||||
|
result = LV_KEY_RIGHT;
|
||||||
|
break;
|
||||||
|
case XKB_KEY_Down:
|
||||||
|
case XKB_KEY_KP_Down:
|
||||||
|
result = LV_KEY_DOWN;
|
||||||
|
break;
|
||||||
|
case XKB_KEY_Tab:
|
||||||
|
case XKB_KEY_KP_Tab:
|
||||||
|
result = LV_KEY_NEXT;
|
||||||
|
break;
|
||||||
|
case XKB_KEY_ISO_Left_Tab: /* Sent on SHIFT + TAB */
|
||||||
|
result = LV_KEY_PREV;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(result == 0) {
|
||||||
|
char buffer[4] = { 0, 0, 0, 0 };
|
||||||
|
int size = xkb_state_key_get_utf8(dsc->state, keycode, NULL, 0) + 1;
|
||||||
|
if(size > 1) {
|
||||||
|
xkb_state_key_get_utf8(dsc->state, keycode, buffer, size);
|
||||||
|
memcpy(&result, buffer, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xkb_state_update_key(dsc->state, keycode, down ? XKB_KEY_DOWN : XKB_KEY_UP);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* STATIC FUNCTIONS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
static bool _set_keymap(lv_xkb_t * dsc, struct xkb_rule_names names)
|
||||||
|
{
|
||||||
|
if(dsc->keymap) {
|
||||||
|
xkb_keymap_unref(dsc->keymap);
|
||||||
|
dsc->keymap = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsc->keymap = xkb_keymap_new_from_names(context, &names, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||||
|
if(!dsc->keymap) {
|
||||||
|
LV_LOG_ERROR("xkb_keymap_new_from_names failed: %s", strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dsc->state) {
|
||||||
|
xkb_state_unref(dsc->state);
|
||||||
|
dsc->state = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsc->state = xkb_state_new(dsc->keymap);
|
||||||
|
if(!dsc->state) {
|
||||||
|
LV_LOG_ERROR("xkb_state_new failed: %s", strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* defined(LV_LIBINPUT_XKB) && LV_LIBINPUT_XKB */
|
72
src/drivers/libinput/lv_xkb.h
Normal file
72
src/drivers/libinput/lv_xkb.h
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
* @file lv_xkb.h
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LV_XKB_H
|
||||||
|
#define LV_XKB_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* INCLUDES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
#include "../../lv_conf_internal.h"
|
||||||
|
|
||||||
|
#if defined(LV_LIBINPUT_XKB) && LV_LIBINPUT_XKB
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* DEFINES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* TYPEDEFS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
struct xkb_keymap * keymap;
|
||||||
|
struct xkb_state * state;
|
||||||
|
} lv_xkb_t;
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* GLOBAL PROTOTYPES
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise an XKB descriptor.
|
||||||
|
* @return true if the initialisation was successful
|
||||||
|
*/
|
||||||
|
bool lv_xkb_init(lv_xkb_t * dsc, struct xkb_rule_names names);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* De-initialise an XKB descriptor.
|
||||||
|
* @param dsc Pointer to descriptor
|
||||||
|
*/
|
||||||
|
void lv_xkb_deinit(lv_xkb_t * dsc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process an evdev scancode using a specific XKB descriptor.
|
||||||
|
* @param state XKB descriptor to use
|
||||||
|
* @param scancode evdev scancode to process
|
||||||
|
* @param down true if the key was pressed, false if it was releases
|
||||||
|
* @return the (first) UTF-8 character produced by the event or 0 if no output was produced
|
||||||
|
*/
|
||||||
|
uint32_t lv_xkb_process_key(lv_xkb_t * dsc, uint32_t scancode, bool down);
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* MACROS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
#endif /* LV_LIBINPUT_XKB */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* defined(LV_LIBINPUT_XKB) && LV_LIBINPUT_XKB */
|
@ -32,6 +32,7 @@ extern "C" {
|
|||||||
#include "nuttx/lv_nuttx_libuv.h"
|
#include "nuttx/lv_nuttx_libuv.h"
|
||||||
|
|
||||||
#include "evdev/lv_evdev.h"
|
#include "evdev/lv_evdev.h"
|
||||||
|
#include "libinput/lv_libinput.h"
|
||||||
|
|
||||||
#include "windows/lv_windows_input.h"
|
#include "windows/lv_windows_input.h"
|
||||||
#include "windows/lv_windows_display.h"
|
#include "windows/lv_windows_display.h"
|
||||||
|
@ -2906,6 +2906,44 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*Driver for libinput input devices*/
|
||||||
|
#ifndef LV_USE_LIBINPUT
|
||||||
|
#ifdef CONFIG_LV_USE_LIBINPUT
|
||||||
|
#define LV_USE_LIBINPUT CONFIG_LV_USE_LIBINPUT
|
||||||
|
#else
|
||||||
|
#define LV_USE_LIBINPUT 0
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LV_USE_LIBINPUT
|
||||||
|
#ifndef LV_LIBINPUT_BSD
|
||||||
|
#ifdef CONFIG_LV_LIBINPUT_BSD
|
||||||
|
#define LV_LIBINPUT_BSD CONFIG_LV_LIBINPUT_BSD
|
||||||
|
#else
|
||||||
|
#define LV_LIBINPUT_BSD 0
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*Full keyboard support*/
|
||||||
|
#ifndef LV_LIBINPUT_XKB
|
||||||
|
#ifdef CONFIG_LV_LIBINPUT_XKB
|
||||||
|
#define LV_LIBINPUT_XKB CONFIG_LV_LIBINPUT_XKB
|
||||||
|
#else
|
||||||
|
#define LV_LIBINPUT_XKB 0
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#if LV_LIBINPUT_XKB
|
||||||
|
/*"setxkbmap -query" can help find the right values for your keyboard*/
|
||||||
|
#ifndef LV_LIBINPUT_XKB_KEY_MAP
|
||||||
|
#ifdef CONFIG_LV_LIBINPUT_XKB_KEY_MAP
|
||||||
|
#define LV_LIBINPUT_XKB_KEY_MAP CONFIG_LV_LIBINPUT_XKB_KEY_MAP
|
||||||
|
#else
|
||||||
|
#define LV_LIBINPUT_XKB_KEY_MAP { .rules = NULL, .model = "pc101", .layout = "us", .variant = NULL, .options = NULL }
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
/*Drivers for LCD devices connected via SPI/parallel port*/
|
/*Drivers for LCD devices connected via SPI/parallel port*/
|
||||||
#ifndef LV_USE_ST7735
|
#ifndef LV_USE_ST7735
|
||||||
#ifdef CONFIG_LV_USE_ST7735
|
#ifdef CONFIG_LV_USE_ST7735
|
||||||
|
@ -253,6 +253,14 @@ include_directories(${PNG_INCLUDE_DIR})
|
|||||||
find_package(Freetype REQUIRED)
|
find_package(Freetype REQUIRED)
|
||||||
include_directories(${FREETYPE_INCLUDE_DIRS})
|
include_directories(${FREETYPE_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
# libinput is required for the libinput device driver test case
|
||||||
|
find_package(Libinput REQUIRED)
|
||||||
|
include_directories(${LIBINPUT_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
# libxkbcommon is required for the libinput device driver test case
|
||||||
|
find_package(PkgConfig)
|
||||||
|
pkg_check_modules(xkbcommon REQUIRED xkbcommon)
|
||||||
|
|
||||||
# disable test targets for build only tests
|
# disable test targets for build only tests
|
||||||
if (ENABLE_TESTS)
|
if (ENABLE_TESTS)
|
||||||
file( GLOB_RECURSE TEST_CASE_FILES src/test_cases/*.c )
|
file( GLOB_RECURSE TEST_CASE_FILES src/test_cases/*.c )
|
||||||
@ -288,6 +296,7 @@ foreach( test_case_fname ${TEST_CASE_FILES} )
|
|||||||
lvgl_thorvg
|
lvgl_thorvg
|
||||||
${PNG_LIBRARIES}
|
${PNG_LIBRARIES}
|
||||||
${FREETYPE_LIBRARIES}
|
${FREETYPE_LIBRARIES}
|
||||||
|
${LIBINPUT_LIBRARIES}
|
||||||
${JPEG_LIBRARIES}
|
${JPEG_LIBRARIES}
|
||||||
m
|
m
|
||||||
${TEST_LIBS})
|
${TEST_LIBS})
|
||||||
|
@ -106,6 +106,8 @@
|
|||||||
#define LV_USE_ST7735 1
|
#define LV_USE_ST7735 1
|
||||||
#define LV_USE_ST7789 1
|
#define LV_USE_ST7789 1
|
||||||
#define LV_USE_ST7796 1
|
#define LV_USE_ST7796 1
|
||||||
|
#define LV_USE_LIBINPUT 1
|
||||||
|
#define LV_LIBINPUT_XKB 1
|
||||||
|
|
||||||
#define LV_USE_FREETYPE 1
|
#define LV_USE_FREETYPE 1
|
||||||
#define LV_FREETYPE_CACHE_SIZE 768
|
#define LV_FREETYPE_CACHE_SIZE 768
|
||||||
|
Loading…
x
Reference in New Issue
Block a user