From aa3a4945e46efaa0b63fc07c9e2126c21341aba3 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Sat, 11 Mar 2023 20:20:34 +0000 Subject: [PATCH] Handle input devices as they are plugged in and out --- README.md | 2 + indev.c | 560 +++++++++++++++++++++++++++++++++++++++++++--------- indev.h | 49 +++-- lv_conf.h | 3 + lv_drivers | 2 +- main.c | 26 ++- meson.build | 1 + 7 files changed, 526 insertions(+), 117 deletions(-) diff --git a/README.md b/README.md index 901ee79..97bc81f 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/indev.c b/indev.c index 363d71f..df82527 100644 --- a/indev.c +++ b/indev.c @@ -25,13 +25,22 @@ #include "lv_drivers/indev/libinput_drv.h" +#include +#include #include +#include + +#include + +#include /** * 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; } diff --git a/indev.h b/indev.h index d06d8c4..8e2a1fe 100644 --- a/indev.h +++ b/indev.h @@ -26,13 +26,40 @@ #include /** - * 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 */ diff --git a/lv_conf.h b/lv_conf.h index fa98b23..8e7eb89 100644 --- a/lv_conf.h +++ b/lv_conf.h @@ -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 *====================*/ diff --git a/lv_drivers b/lv_drivers index 2ed01fe..ec0ad82 160000 --- a/lv_drivers +++ b/lv_drivers @@ -1 +1 @@ -Subproject commit 2ed01feab02940c117fa4cd3f83cd4649b8b9f0e +Subproject commit ec0ad829619a1ebe5ca25903aac1a62016829b84 diff --git a/main.c b/main.c index 4f50614..de2d26f 100644 --- a/main.c +++ b/main.c @@ -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); diff --git a/meson.build b/meson.build index fdca3f9..7b49cb1 100644 --- a/meson.build +++ b/meson.build @@ -48,6 +48,7 @@ squeek2lvgl_sources = [ unl0kr_dependencies = [ dependency('inih'), dependency('libinput'), + dependency('libudev'), dependency('xkbcommon'), ]