Handle input devices as they are plugged in and out

This commit is contained in:
Johannes Marbach
2023-03-11 20:20:34 +00:00
parent 542e964951
commit aa3a4945e4
7 changed files with 526 additions and 117 deletions

View File

@@ -89,6 +89,7 @@ For an example configuration file, see [unl0kr.conf].
- [lv_drivers] (git submodule / linked statically)
- [squeek2lvgl] (git submodule / linked statically)
- [libinput]
- [libudev]
- [libxkbcommon]
- [libdrm] (optional, required for the DRM backend)
- evdev kernel module
@@ -223,6 +224,7 @@ The [FontAwesome] font is licensed under the Open Font License version 1.1.
[fix(examples) don't compile assets unless needed]: https://github.com/lvgl/lvgl/pull/2523
[inih]: https://github.com/benhoyt/inih
[libinput]: https://gitlab.freedesktop.org/libinput/libinput
[libudev]: https://github.com/systemd/systemd/tree/main/src/libudev
[libxkbcommon]: https://github.com/xkbcommon/libxkbcommon
[libdrm]: https://gitlab.freedesktop.org/mesa/drm
[lv_drivers]: https://github.com/lvgl/lv_drivers

560
indev.c
View File

@@ -25,13 +25,22 @@
#include "lv_drivers/indev/libinput_drv.h"
#include <libinput.h>
#include <libudev.h>
#include <limits.h>
#include <stdio.h>
#include <linux/input.h>
#include <sys/select.h>
/**
* Defines
*/
#define INPUT_DEVICE_NODE_PREFIX "/dev/input/event"
#define MAX_KEYBOARD_DEVS 4
#define MAX_POINTER_DEVS 4
#define MAX_TOUCHSCREEN_DEVS 1
@@ -41,23 +50,26 @@
* Static variables
*/
static int num_keyboard_devs = 0;
static char *keyboard_devs[MAX_KEYBOARD_DEVS];
static lv_indev_t *keyboard_indevs[MAX_KEYBOARD_DEVS];
static lv_indev_drv_t keyboard_indev_drvs[MAX_KEYBOARD_DEVS];
static libinput_drv_state_t keyboard_drv_states[MAX_KEYBOARD_DEVS];
static libinput_capability allowed_capability = LIBINPUT_CAPABILITY_NONE;
static int num_pointer_devs = 0;
static char *pointer_devs[MAX_POINTER_DEVS];
static lv_indev_t *pointer_indevs[MAX_POINTER_DEVS];
static lv_indev_drv_t pointer_indev_drvs[MAX_POINTER_DEVS];
static libinput_drv_state_t pointer_drv_states[MAX_POINTER_DEVS];
static struct udev *context = NULL;
static struct udev_monitor *monitor = NULL;
static int monitor_fd = -1;
static int num_touchscreen_devs = 0;
static char *touchscreen_devs[MAX_TOUCHSCREEN_DEVS];
static lv_indev_t *touchscreen_indevs[MAX_TOUCHSCREEN_DEVS];
static lv_indev_drv_t touchscreen_indev_drvs[MAX_TOUCHSCREEN_DEVS];
static libinput_drv_state_t touchscreen_drv_states[MAX_TOUCHSCREEN_DEVS];
struct input_device {
char *node;
libinput_capability capability;
libinput_drv_state_t drv_state;
lv_indev_drv_t indev_drv;
lv_indev_t *indev;
};
static struct input_device **devices = NULL;
static int num_devices = 0;
static int num_connected_devices = 0;
lv_group_t *keyboard_input_group = NULL;
lv_obj_t *cursor_obj = NULL;
/**
@@ -65,26 +77,56 @@ static libinput_drv_state_t touchscreen_drv_states[MAX_TOUCHSCREEN_DEVS];
*/
/**
* Auto-connect available input devices having a specific capability.
* Test whether a device can act as a keyboard device.
*
* @param capability capability to filter devices by
* @param max_num_devs maximum number of devices to connect
* @param num_devs pointer for writing the actual number of connected devices into
* @param devs array for storing device paths
* @param indevs array for storing LVGL indevs
* @param indev_drvs array for storing LVGL indev drivers
* @param drv_states array for storing LVGL libinput driver states
* @param device the device to test
* @return true if the device has the keyboard capability
*/
static void auto_connect(libinput_capability capability, int max_num_devs, int *num_devs, char *devs[], lv_indev_t *indevs[],
lv_indev_drv_t indev_drvs[], libinput_drv_state_t drv_states[]);
static bool is_keyboard_device(struct input_device *device);
/**
* Log a message announcing the connection of an input device.
* Test whether a device can act as a pointer device.
*
* @param capability the device's capability
* @param dev the device path
* @param device the device to test
* @return true if the device has the pointer capability
*/
static void log_connection(libinput_capability capability, char *dev);
static bool is_pointer_device(struct input_device *device);
/**
* Convert a device capability into a descriptive string.
*
* @param capability input device capability
* @return textual description
*/
static char *capability_to_str(libinput_capability capability);
/**
* Connect a specific input device using its udev device.
*
* @param device udev device
*/
static void connect_udev_device(struct udev_device *device);
/**
* Connect a specific input device using its device node.
*
* @param node device node path
*/
static void connect_devnode(const char *node);
/**
* Disconnect a specific input device using its device node.
*
* @param node device node path
*/
static void disconnect_devnode(const char *node);
/**
* Disconnect a specific input device using its index in the devices array.
*
* @param idx index in devices array
*/
static void disconnect_idx(int idx);
/**
* Perform an input read on a device using the libinput driver.
@@ -94,105 +136,433 @@ static void log_connection(libinput_capability capability, char *dev);
*/
static void libinput_read_cb(lv_indev_drv_t *indev_drv, lv_indev_data_t *data);
/**
* Set up the input group for a keyboard device.
*
* @param device the input device
*/
static void set_keyboard_input_group(struct input_device *device);
/**
* Set up the mouse cursor image for a pointer device.
*
* @param device the input device
*/
static void set_mouse_cursor(struct input_device *device);
/**
* Static functions
*/
static void auto_connect(libinput_capability capability, int max_num_devs, int *num_devs, char *devs[], lv_indev_t *indevs[],
lv_indev_drv_t indev_drvs[], libinput_drv_state_t drv_states[]) {
memset(devs, 0, max_num_devs * sizeof(char *));
memset(indevs, 0, max_num_devs * sizeof(lv_indev_t *));
memset(indev_drvs, 0, max_num_devs * sizeof(lv_indev_drv_t));
memset(drv_states, 0, max_num_devs * sizeof(libinput_drv_state_t));
*num_devs = libinput_find_devs(capability, devs, max_num_devs, false);
for (int i = 0; i < *num_devs; ++i) {
log_connection(capability, devs[i]);
libinput_init_state(&(drv_states[i]), devs[i]);
lv_indev_drv_init(&(indev_drvs[i]));
indev_drvs[i].read_cb = libinput_read_cb;
indev_drvs[i].user_data = &(drv_states[i]);
if (capability == LIBINPUT_CAPABILITY_KEYBOARD) {
indev_drvs[i].type = LV_INDEV_TYPE_KEYPAD;
} else {
indev_drvs[i].type = LV_INDEV_TYPE_POINTER;
indev_drvs[i].long_press_repeat_time = USHRT_MAX;
}
indevs[i] = lv_indev_drv_register(&(indev_drvs[i]));
}
static bool is_keyboard_device(struct input_device *device) {
return (device->capability & LIBINPUT_CAPABILITY_KEYBOARD) != LIBINPUT_CAPABILITY_NONE;
}
static void log_connection(libinput_capability capability, char *dev) {
switch (capability) {
static bool is_pointer_device(struct input_device *device) {
return (device->capability & LIBINPUT_CAPABILITY_POINTER) != LIBINPUT_CAPABILITY_NONE;
}
static char *capability_to_str(libinput_capability capability) {
if (capability == LIBINPUT_CAPABILITY_KEYBOARD) {
return "keyboard";
}
if (capability == LIBINPUT_CAPABILITY_POINTER) {
return "pointer";
}
if (capability == LIBINPUT_CAPABILITY_TOUCH) {
return "touch";
}
return "none";
}
static void connect_udev_device(struct udev_device *device) {
/* Obtain and verify device node */
const char *node = udev_device_get_devnode(device);
if (!node || strncmp(node, INPUT_DEVICE_NODE_PREFIX, strlen(INPUT_DEVICE_NODE_PREFIX)) != 0) {
ul_log(UL_LOG_LEVEL_VERBOSE, "Ignoring unsupported input device %s", udev_device_get_syspath(device));
return;
}
/* Connect device using its node */
connect_devnode(node);
}
static void connect_devnode(const char *node) {
/* Check if the device is already connected */
for (int i = 0; i < num_connected_devices; ++i) {
if (strcmp(devices[i]->node, node) == 0) {
ul_log(UL_LOG_LEVEL_WARNING, "Ignoring already connected input device %s", node);
return;
}
}
/* Double array size every time it's filled */
if (num_connected_devices == num_devices) {
/* Re-allocate array */
struct input_device **tmp = realloc(devices, (2 * num_devices + 1) * sizeof(struct input_device *));
if (!tmp) {
ul_log(UL_LOG_LEVEL_ERROR, "Could not reallocate memory for input device array");
return;
}
devices = tmp;
/* Update size counter */
num_devices = 2 * num_devices + 1;
/* Fill empty space with zeros */
lv_memzero(devices + num_connected_devices, (num_devices - num_connected_devices) * sizeof(struct input_device *));
}
/* Allocate memory for new input device and insert it */
struct input_device *device = malloc(sizeof(struct input_device));
lv_memzero(device, sizeof(struct input_device));
devices[num_connected_devices] = device;
/* Get pointers to driver state & indev driver */
libinput_drv_state_t *drv_state = &(device->drv_state);
lv_indev_drv_t *indev_drv = &(device->indev_drv);
/* Copy the node path so that it can be used beyond the caller's scope */
device->node = strdup(node);
/* Initialise the driver state and obtain the libinput device */
libinput_init_state(drv_state, device->node);
struct libinput_device *device_libinput = drv_state->libinput_device;
/* If libinput failed to connect the device, exit */
if (!device_libinput) {
ul_log(UL_LOG_LEVEL_WARNING, "Aborting connection of input device %s because libinput failed to connect it", node);
disconnect_idx(num_connected_devices);
return;
}
/* Obtain device capabilities */
device->capability = libinput_query_capability(device_libinput);
/* If the device doesn't have any supported capabilities, exit */
if ((device->capability & allowed_capability) == LIBINPUT_CAPABILITY_NONE) {
ul_log(UL_LOG_LEVEL_WARNING, "Aborting connection of input device %s because it has no allowed capabilities", node);
disconnect_idx(num_connected_devices);
return;
}
/* Initialise indev driver */
lv_indev_drv_init(indev_drv);
indev_drv->read_cb = libinput_read_cb;
indev_drv->user_data = drv_state;
/* Set up indev driver type and related properties */
switch (device->capability) {
case LIBINPUT_CAPABILITY_KEYBOARD:
ul_log(UL_LOG_LEVEL_VERBOSE, "Connecting keyboard device %s", dev);
indev_drv->type = LV_INDEV_TYPE_KEYPAD;
break;
case LIBINPUT_CAPABILITY_POINTER:
ul_log(UL_LOG_LEVEL_VERBOSE, "Connecting pointer device %s", dev);
indev_drv->type = LV_INDEV_TYPE_POINTER;
indev_drv->long_press_repeat_time = USHRT_MAX;
break;
case LIBINPUT_CAPABILITY_TOUCH:
ul_log(UL_LOG_LEVEL_VERBOSE, "Connecting touchscreen device %s", dev);
indev_drv->type = LV_INDEV_TYPE_POINTER;
indev_drv->long_press_repeat_time = USHRT_MAX;
break;
case LIBINPUT_CAPABILITY_NONE:
default:
break;
}
/* Register indev */
device->indev = lv_indev_drv_register(indev_drv);
/* Set the input group for keyboard devices */
if (is_keyboard_device(device)) {
set_keyboard_input_group(device);
}
/* Set the mouse cursor for pointer devices */
if (is_pointer_device(device)) {
set_mouse_cursor(device);
}
/* Increment connected device count */
num_connected_devices++;
ul_log(UL_LOG_LEVEL_VERBOSE, "Connected input device %s (%s)", node, capability_to_str(device->capability));
}
static void disconnect_udev_device(struct udev_device *device) {
/* Obtain and verify device node */
const char *node = udev_device_get_devnode(device);
if (!node || strncmp(node, INPUT_DEVICE_NODE_PREFIX, strlen(INPUT_DEVICE_NODE_PREFIX)) != 0) {
ul_log(UL_LOG_LEVEL_VERBOSE, "Ignoring unsupported input device %s", udev_device_get_syspath(device));
return;
}
/* Disconnect device using its node */
disconnect_devnode(node);
}
static void disconnect_devnode(const char *node) {
/* Find connected device matching the specified node */
int idx = -1;
for (int i = 0; i < num_connected_devices; ++i) {
if (strcmp(devices[i]->node, node) == 0) {
idx = i;
break;
}
}
/* If no matching device was found, exit */
if (idx < 0) {
ul_log(UL_LOG_LEVEL_WARNING, "Ignoring already disconnected input device %s", node);
return;
}
/* Disconnect device using its index */
disconnect_idx(idx);
/* Shift subsequent devices forward */
for (int i = idx + 1; i < num_connected_devices; ++i) {
devices[i - 1] = devices[i];
/* Zero out the last element after shifting it forward */
if (i == num_connected_devices - 1) {
lv_memzero(devices + i, sizeof(struct input_device *));
}
}
/* Decrement connected device count */
--num_connected_devices;
ul_log(UL_LOG_LEVEL_VERBOSE, "Disconnected input device %s", node);
}
static void disconnect_idx(int idx) {
/* Delete LVGL indev */
if (devices[idx]->indev) {
lv_indev_delete(devices[idx]->indev);
}
/* Free previously copied node path */
if (devices[idx]->node) {
free(devices[idx]->node);
}
/* De-initialise driver state */
libinput_deinit_state(&(devices[idx]->drv_state));
/* Deallocate memory and zero out freed array element */
free(devices[idx]);
lv_memzero(devices + idx, sizeof(struct input_device *));
}
static void libinput_read_cb(lv_indev_drv_t *indev_drv, lv_indev_data_t *data) {
libinput_read_state(indev_drv->user_data, indev_drv, data);
}
static void set_keyboard_input_group(struct input_device *device) {
/* Ignore non-keyboard devices */
if (!is_keyboard_device(device)) {
return;
}
/* Apply the input group */
lv_indev_set_group(device->indev, keyboard_input_group);
}
static void set_mouse_cursor(struct input_device *device) {
/* Ignore non-pointer devices */
if (!is_pointer_device(device)) {
return;
}
/* Initialise cursor image if needed */
if (!cursor_obj) {
cursor_obj = lv_img_create(lv_scr_act());
lv_img_set_src(cursor_obj, &ul_cursor_img_dsc);
}
/* Apply the cursor image */
lv_indev_set_cursor(device->indev, cursor_obj);
}
/**
* Public functions
*/
void ul_indev_auto_connect(bool keyboard, bool pointer, bool touchscreen) {
void ul_indev_set_allowed_device_capability(bool keyboard, bool pointer, bool touchscreen) {
allowed_capability = LIBINPUT_CAPABILITY_NONE;
if (keyboard) {
auto_connect(LIBINPUT_CAPABILITY_KEYBOARD, MAX_KEYBOARD_DEVS, &num_keyboard_devs, keyboard_devs, keyboard_indevs,
keyboard_indev_drvs, keyboard_drv_states);
allowed_capability |= LIBINPUT_CAPABILITY_KEYBOARD;
}
if (pointer) {
auto_connect(LIBINPUT_CAPABILITY_POINTER, MAX_POINTER_DEVS, &num_pointer_devs, pointer_devs, pointer_indevs,
pointer_indev_drvs, pointer_drv_states);
allowed_capability |= LIBINPUT_CAPABILITY_POINTER;
}
if (touchscreen) {
auto_connect(LIBINPUT_CAPABILITY_TOUCH, MAX_TOUCHSCREEN_DEVS, &num_touchscreen_devs, touchscreen_devs, touchscreen_indevs,
touchscreen_indev_drvs, touchscreen_drv_states);
allowed_capability |= LIBINPUT_CAPABILITY_TOUCH;
}
}
void ul_indev_set_keyboard_input_group(lv_group_t *group) {
/* Store the group */
keyboard_input_group = group;
/* Apply the group on all connected keyboard devices */
for (int i = 0; i < num_connected_devices; ++i) {
if (is_keyboard_device(devices[i])) {
set_keyboard_input_group(devices[i]);
}
}
}
void ul_indev_auto_connect() {
ul_log(UL_LOG_LEVEL_VERBOSE, "Auto-connecting supported input devices");
/* Make sure udev context is initialised */
if (!context) {
context = udev_new();
if (!context) {
ul_log(UL_LOG_LEVEL_WARNING, "Could not create udev context");
return;
}
}
/* Scan for available input devices */
struct udev_enumerate *enumerate = udev_enumerate_new(context);
udev_enumerate_add_match_subsystem(enumerate, "input");
udev_enumerate_scan_devices(enumerate);
/* Prepare for enumerating found devices */
struct udev_list_entry *first_entry = udev_enumerate_get_list_entry(enumerate);
struct udev_list_entry *entry;
udev_list_entry_foreach(entry, first_entry) {
/* Obtain system path */
const char *path = udev_list_entry_get_name(entry);
/* Create udev device */
struct udev_device *device = udev_device_new_from_syspath(context, path);
if (!device) {
ul_log(UL_LOG_LEVEL_WARNING, "Could not create udev device for %s", path);
continue;
}
/* Connect device */
connect_udev_device(device);
/* Unreference udev device */
udev_device_unref(device);
}
/* Unreference enumeration */
udev_enumerate_unref(enumerate);
}
void ul_indev_start_monitor() {
/* Make sure udev context is initialised */
if (!context) {
context = udev_new();
if (!context) {
ul_log(UL_LOG_LEVEL_WARNING, "Could not create udev context");
return;
}
}
/* Check if monitor is already running */
if (monitor) {
ul_log(UL_LOG_LEVEL_WARNING, "Not starting udev monitor because it is already running");
return;
}
/* Create new monitor */
monitor = udev_monitor_new_from_netlink(context, "udev");
if (!monitor) {
ul_log(UL_LOG_LEVEL_WARNING, "Could not create udev monitor");
ul_indev_stop_monitor();
return;
}
/* Apply input subsystem filter */
if (udev_monitor_filter_add_match_subsystem_devtype(monitor, "input", NULL) < 0) {
ul_log(UL_LOG_LEVEL_WARNING, "Could not add input subsystem filter for udev monitor");
}
/* Start monitor */
if (udev_monitor_enable_receiving(monitor) < 0) {
ul_log(UL_LOG_LEVEL_WARNING, "Could not enable udev monitor");
ul_indev_stop_monitor();
return;
}
/* Obtain monitor file descriptor */
if ((monitor_fd = udev_monitor_get_fd(monitor)) < 0) {
ul_log(UL_LOG_LEVEL_WARNING, "Could not acquire file descriptor for udev monitor");
ul_indev_stop_monitor();
return;
}
}
void ul_indev_stop_monitor() {
/* Unreference monitor */
if (monitor) {
udev_monitor_unref(monitor);
monitor = NULL;
}
/* Reset monitor file descriptor */
if (monitor_fd >= 0) {
monitor_fd = -1;
}
/* Unreference udev context */
if (context) {
udev_unref(context);
context = NULL;
}
}
void ul_indev_query_monitor() {
/* Make sure the monitor is running */
if (!monitor) {
ul_log(UL_LOG_LEVEL_ERROR, "Cannot query udev monitor because it is not running");
return;
}
/* Prepare file descriptor set for reading */
fd_set fds;
FD_ZERO(&fds);
FD_SET(monitor_fd, &fds);
/* Set up timeval to not block when no updates are available */
struct timeval tv = { .tv_sec = 0, .tv_usec = 0 };
/* Read and process all updates */
while (select(monitor_fd + 1, &fds, NULL, NULL, &tv) > 0 && FD_ISSET(monitor_fd, &fds)) {
/* Obtain udev device */
struct udev_device *device = udev_monitor_receive_device(monitor);
if (!device) {
continue;
}
/* Obtain action */
const char *action = udev_device_get_action(device);
/* Connect or disconnect the device */
if (strcmp(action, "add") == 0) {
connect_udev_device(device);
} else if (strcmp(action, "remove") == 0) {
disconnect_udev_device(device);
}
/* Unreference udev device */
udev_device_unref(device);
}
}
bool ul_indev_is_keyboard_connected() {
return num_keyboard_devs > 0;
}
void ul_indev_set_up_textarea_for_keyboard_input(lv_obj_t *textarea) {
if (!ul_indev_is_keyboard_connected()) {
return;
}
lv_group_t *group = lv_group_create();
lv_group_add_obj(group, textarea);
for (int i = 0; i < num_keyboard_devs; ++i) {
lv_indev_set_group(keyboard_indevs[i], group);
}
}
void ul_indev_set_up_mouse_cursor() {
if (num_pointer_devs == 0) {
return;
}
lv_obj_t *cursor_obj = lv_img_create(lv_scr_act());
lv_img_set_src(cursor_obj, &ul_cursor_img_dsc);
for (int i = 0; i < num_pointer_devs; ++i) {
lv_indev_set_cursor(pointer_indevs[i], cursor_obj);
}
for (int i = 0; i < num_connected_devices; ++i) {
if (is_keyboard_device(devices[i])) {
return true;
}
}
return false;
}

49
indev.h
View File

@@ -26,13 +26,40 @@
#include <stdbool.h>
/**
* Auto-connect currently available keyboard, pointer and touchscreen input devices.
* Set the required capabilities for input devices.
*
* @param keyboard if true, auto-connect keyboard devices
* @param pointer if true, auto-connect pointer devices
* @param touchscreen if true, auto-connect touchscreen devices
* @param keyboard if true, allow connection of keyboard devices
* @param pointer if true, allow connection of pointer devices
* @param touchscreen if true, allow connection of touchscreen devices
*/
void ul_indev_auto_connect(bool keyboard, bool pointer, bool touchscreen);
void ul_indev_set_allowed_device_capability(bool keyboard, bool pointer, bool touchscreen);
/**
* Set the group for receiving input from keyboard devices.
*
* @param group group that should receive input
*/
void ul_indev_set_keyboard_input_group(lv_group_t *group);
/**
* Auto-connect currently available keyboard, pointer and touchscreen input devices.
*/
void ul_indev_auto_connect();
/**
* Start the udev device monitor.
*/
void ul_indev_start_monitor();
/**
* Stop the udev device monitor.
*/
void ul_indev_stop_monitor();
/**
* Query the udev device monitor and (dis)connect added or removed devices
*/
void ul_indev_query_monitor();
/**
* Check if any keyboard devices are connected.
@@ -41,16 +68,4 @@ void ul_indev_auto_connect(bool keyboard, bool pointer, bool touchscreen);
*/
bool ul_indev_is_keyboard_connected();
/**
* Set up an LVGL text area to receive input from currently connected keyboard devices.
*
* @param textarea textarea widget
*/
void ul_indev_set_up_textarea_for_keyboard_input(lv_obj_t *textarea);
/**
* Set up the mouse cursor image for currently connected pointer devices.
*/
void ul_indev_set_up_mouse_cursor();
#endif /* UL_INDEV_H */

View File

@@ -67,6 +67,9 @@
/*Use the standard `memcpy` and `memset` instead of LVGL's own functions. (Might or might not be faster).*/
#define LV_MEMCPY_MEMSET_STD 0
/*TODO: Remove once lv_memzero becomes available in a release. Currently it is only available on master.*/
#define lv_memzero(dst, len) (lv_memset(dst, 0x00, len))
/*====================
HAL SETTINGS
*====================*/

26
main.c
View File

@@ -68,6 +68,13 @@ lv_obj_t *keyboard = NULL;
* Static prototypes
*/
/**
* Query the device monitor and handle updates.
*
* @param timer the timer object
*/
static void query_device_monitor(lv_timer_t *timer);
/**
* Handle LV_EVENT_CLICKED events from the theme toggle button.
*
@@ -199,6 +206,11 @@ static void sigaction_handler(int signum);
* Static functions
*/
static void query_device_monitor(lv_timer_t *timer) {
LV_UNUSED(timer);
ul_indev_query_monitor();
}
static void toggle_theme_btn_clicked_cb(lv_event_t *event) {
LV_UNUSED(event);
toggle_theme();
@@ -408,9 +420,15 @@ int main(int argc, char *argv[]) {
disp_drv.dpi = dpi;
lv_disp_drv_register(&disp_drv);
/* Connect input devices */
ul_indev_auto_connect(conf_opts.input.keyboard, conf_opts.input.pointer, conf_opts.input.touchscreen);
ul_indev_set_up_mouse_cursor();
/* Prepare for routing physical keyboard input into the textarea */
lv_group_t *keyboard_input_group = lv_group_create();
ul_indev_set_keyboard_input_group(keyboard_input_group);
/* Start input device monitor and auto-connect available devices */
ul_indev_set_allowed_device_capability(conf_opts.input.keyboard, conf_opts.input.pointer, conf_opts.input.touchscreen);
ul_indev_start_monitor();
lv_timer_create(query_device_monitor, 1000, NULL);
ul_indev_auto_connect();
/* Hide the on-screen keyboard by default if a physical keyboard is connected */
if (conf_opts.keyboard.autohide && ul_indev_is_keyboard_connected()) {
@@ -524,7 +542,7 @@ int main(int argc, char *argv[]) {
lv_obj_add_state(textarea, LV_STATE_FOCUSED);
/* Route physical keyboard input into textarea */
ul_indev_set_up_textarea_for_keyboard_input(textarea);
lv_group_add_obj(keyboard_input_group, textarea);
/* Reveal / obscure password button */
lv_obj_t *toggle_pw_btn = lv_btn_create(textarea_container);

View File

@@ -48,6 +48,7 @@ squeek2lvgl_sources = [
unl0kr_dependencies = [
dependency('inih'),
dependency('libinput'),
dependency('libudev'),
dependency('xkbcommon'),
]