feat(drivers): add libinput/xkb driver (#5486)

This commit is contained in:
Johannes Marbach
2024-01-29 13:14:37 +01:00
committed by GitHub
parent c835e888b1
commit b45ef5ccf9
14 changed files with 1210 additions and 2 deletions
+1 -1
View File
@@ -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
View File
@@ -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
+1
View File
@@ -7,5 +7,6 @@ Drivers
display/index display/index
touchpad/index touchpad/index
libinput
X11 X11
windows windows
+87
View 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);
+14
View File
@@ -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
+1 -1
View File
@@ -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
File diff suppressed because it is too large Load Diff
+134
View 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
View 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
View 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 */
+1
View File
@@ -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"
+38
View File
@@ -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
+9
View File
@@ -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})
+2
View File
@@ -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