From 6f4d2de28a76def16728b12dcdab3d7440a9c030 Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Sun, 4 Jun 2023 20:28:25 +0100 Subject: [PATCH 01/13] display: fbdev: add force_refresh quirk This is needed specifically for Qualcomm devices using DRM fbdev emulation, it is probably useful for other platforms using fbdev emulation. --- display/fbdev.c | 13 +++++++++++-- display/fbdev.h | 5 +++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/display/fbdev.c b/display/fbdev.c index f649703..07992e6 100644 --- a/display/fbdev.c +++ b/display/fbdev.c @@ -75,6 +75,7 @@ static struct fb_fix_screeninfo finfo; static char *fbp = 0; static long int screensize = 0; static int fbfd = 0; +static bool force_refresh = false; /********************** * MACROS @@ -161,6 +162,10 @@ void fbdev_init(void) } +void fbdev_force_refresh(bool enabled) { + force_refresh = enabled; +} + void fbdev_exit(void) { close(fbfd); @@ -263,8 +268,12 @@ void fbdev_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color /*Not supported bit per pixel*/ } - //May be some direct update command is required - //ret = ioctl(state->fd, FBIO_UPDATE, (unsigned long)((uintptr_t)rect)); + if (force_refresh) { + vinfo.activate |= FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE; + if (ioctl(fbfd, FBIOPUT_VSCREENINFO, &vinfo) == -1) { + perror("Error setting var screen info"); + } + } lv_disp_flush_ready(drv); } diff --git a/display/fbdev.h b/display/fbdev.h index b7f2c81..a2890fa 100644 --- a/display/fbdev.h +++ b/display/fbdev.h @@ -51,6 +51,11 @@ void fbdev_get_sizes(uint32_t *width, uint32_t *height, uint32_t *dpi); */ void fbdev_set_offset(uint32_t xoffset, uint32_t yoffset); +/** + * Force the display to be refreshed on every change. + * Expected to be used with direct_mode or full_refresh. + */ +void fbdev_force_refresh(bool enabled); /********************** * MACROS From 2f8cc742a7fa27b806112db3d8e545f14527baaa Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Sun, 4 Jun 2023 21:08:33 +0100 Subject: [PATCH 02/13] indev: libinput: implement event queue Libinput doesn't buffer events, so hook up a dedicated thread per input device and a small circular queue to buffer input events. This fixes the dropped input issue so you can type much faster. If there are too many events, the oldest events in the queue will be dropped. --- indev/libinput.c | 230 +++++++++++++++++++++++++++++++------------ indev/libinput_drv.h | 21 +++- 2 files changed, 184 insertions(+), 67 deletions(-) diff --git a/indev/libinput.c b/indev/libinput.c index d4f8e2a..3739626 100644 --- a/indev/libinput.c +++ b/indev/libinput.c @@ -17,6 +17,7 @@ #include #include #include +#include #if USE_BSD_LIBINPUT #include @@ -42,6 +43,7 @@ struct input_device { static bool rescan_devices(void); static bool add_scanned_device(char *path, libinput_capability capabilities); static void reset_scanned_devices(void); +static void *libinput_poll_worker(void* data); static void read_pointer(libinput_drv_state_t *state, struct libinput_event *event); static void read_keypad(libinput_drv_state_t *state, struct libinput_event *event); @@ -55,9 +57,9 @@ static void close_restricted(int fd, void *user_data); static struct input_device *devices = NULL; static size_t num_devices = 0; -static libinput_drv_state_t default_state = { .most_recent_touch_point = { .x = 0, .y = 0 } }; +static libinput_drv_state_t default_state = { .event_lock = PTHREAD_MUTEX_INITIALIZER, }; -static const int timeout = 0; // do not block +static const int timeout = 100; // ms static const nfds_t nfds = 1; static const struct libinput_interface interface = { @@ -174,9 +176,6 @@ bool libinput_set_file_state(libinput_drv_state_t *state, char* dev_name) return false; } - state->button = LV_INDEV_STATE_REL; - state->key_val = 0; - return true; } @@ -186,6 +185,7 @@ bool libinput_set_file_state(libinput_drv_state_t *state, char* dev_name) */ void libinput_init(void) { + memset(&default_state, 0, sizeof(libinput_drv_state_t)); libinput_init_state(&default_state, LIBINPUT_NAME); } @@ -211,6 +211,8 @@ void libinput_init_state(libinput_drv_state_t *state, char* path) state->fds[0].events = POLLIN; state->fds[0].revents = 0; + pthread_create(&state->worker_thread, NULL, libinput_poll_worker, state); + #if USE_XKB xkb_init_state(&(state->xkb_state)); #endif @@ -223,6 +225,21 @@ void libinput_init_state(libinput_drv_state_t *state, char* path) */ void libinput_deinit_state(libinput_drv_state_t *state) { + if (state->fd) + state->deinit = true; + + /* Give worker thread a whole second to quit */ + for (int i = 0; i < 100; i++) { + if (!state->deinit) + break; + usleep(10000); + } + + if (state->deinit) { + fprintf(stderr, "libinput worker thread did not quit in time!\n"); + pthread_cancel(state->worker_thread); + } + if (state->libinput_device) { libinput_path_remove_device(state->libinput_device); libinput_device_unref(state->libinput_device); @@ -250,6 +267,83 @@ void libinput_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data) libinput_read_state(&default_state, indev_drv, data); } +libinput_lv_event_t *get_event(libinput_drv_state_t *state) +{ + if (state->start == state->end) { + return NULL; + } + + libinput_lv_event_t *evt = &state->points[state->start]; + + if (++state->start == MAX_EVENTS) + state->start = 0; + + return evt; +} + +bool event_pending(libinput_drv_state_t *state) +{ + return state->start != state->end; +} + +libinput_lv_event_t *new_event(libinput_drv_state_t *state) +{ + libinput_lv_event_t *evt = &state->points[state->end]; + + if (++state->end == MAX_EVENTS) + state->end = 0; + + /* We have overflowed the buffer, start overwriting + * old events. + */ + if (state->end == state->start) { + LV_LOG_INFO("libinput: overflowed event buffer!"); + if (++state->start == MAX_EVENTS) + state->start = 0; + } + + memset(evt, 0, sizeof(libinput_lv_event_t)); + + return evt; +} + +static void *libinput_poll_worker(void* data) +{ + libinput_drv_state_t * state = (libinput_drv_state_t *)data; + struct libinput_event *event; + int rc = 0; + + LV_LOG_INFO("libinput: poll worker started"); + + while (true) { + rc = poll(state->fds, nfds, timeout); + switch (rc){ + case -1: + perror(NULL); + __attribute__((fallthrough)); + case 0: + if (state->deinit) { + state->deinit = false; /* Signal that we're done */ + return NULL; + } + continue; + default: + break; + } + libinput_dispatch(state->libinput_context); + pthread_mutex_lock(&state->event_lock); + while((event = libinput_get_event(state->libinput_context)) != NULL) { + read_pointer(state, event); + read_keypad(state, event); + libinput_event_destroy(event); + } + pthread_mutex_unlock(&state->event_lock); + LV_LOG_INFO("libinput: event read"); + } + + return NULL; +} + /** * Read available input events via libinput using a specific driver state. Use this function if you want to * connect multiple devices. @@ -259,37 +353,26 @@ void libinput_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data) */ void libinput_read_state(libinput_drv_state_t * state, lv_indev_drv_t * indev_drv, lv_indev_data_t * data) { - struct libinput_event *event; - int rc = 0; + LV_UNUSED(indev_drv); - rc = poll(state->fds, nfds, timeout); - switch (rc){ - case -1: - perror(NULL); - case 0: - goto report_most_recent_state; - default: - break; - } - libinput_dispatch(state->libinput_context); - while((event = libinput_get_event(state->libinput_context)) != NULL) { - switch (indev_drv->type) { - case LV_INDEV_TYPE_POINTER: - read_pointer(state, event); - break; - case LV_INDEV_TYPE_KEYPAD: - read_keypad(state, event); - break; - default: - break; - } - libinput_event_destroy(event); - } -report_most_recent_state: - data->point.x = state->most_recent_touch_point.x; - data->point.y = state->most_recent_touch_point.y; - data->state = state->button; - data->key = state->key_val; + pthread_mutex_lock(&state->event_lock); + + libinput_lv_event_t *evt = get_event(state); + data->continue_reading = event_pending(state); + if (!evt) + evt = &state->last_event; + /* indev expects us to report the most recent state */ + else if (!data->continue_reading) + state->last_event = *evt; + + data->point = evt->point; + data->state = evt->pressed; + data->key = evt->key_val; + + pthread_mutex_unlock(&state->event_lock); + + if (evt) + LV_LOG_TRACE("libinput_read: %d//%d: (%04d,%04d): %d continue_reading? %d", state->start, state->end, data->point.x, data->point.y, data->state, data->continue_reading); } @@ -410,34 +493,51 @@ static void reset_scanned_devices(void) { static void read_pointer(libinput_drv_state_t *state, struct libinput_event *event) { struct libinput_event_touch *touch_event = NULL; struct libinput_event_pointer *pointer_event = NULL; + libinput_lv_event_t *evt = NULL; enum libinput_event_type type = libinput_event_get_type(event); + /* We only care about these events */ + if (type != LIBINPUT_EVENT_TOUCH_MOTION && + type != LIBINPUT_EVENT_TOUCH_DOWN && + type != LIBINPUT_EVENT_TOUCH_UP && + type != LIBINPUT_EVENT_POINTER_MOTION && + type != LIBINPUT_EVENT_POINTER_BUTTON && + type != LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE) + return; + /* We need to read unrotated display dimensions directly from the driver because libinput won't account * for any rotation inside of LVGL */ lv_disp_drv_t *drv = lv_disp_get_default()->driver; + /* ignore more than 2 fingers as it will only confuse LVGL */ + touch_event = libinput_event_get_touch_event(event); + if (touch_event && libinput_event_touch_get_slot(touch_event) > 1) + return; + + evt = new_event(state); + switch (type) { case LIBINPUT_EVENT_TOUCH_MOTION: - case LIBINPUT_EVENT_TOUCH_DOWN: - touch_event = libinput_event_get_touch_event(event); - lv_coord_t x_touch = libinput_event_touch_get_x_transformed(touch_event, drv->physical_hor_res > 0 ? drv->physical_hor_res : drv->hor_res) - drv->offset_x; - lv_coord_t y_touch = libinput_event_touch_get_y_transformed(touch_event, drv->physical_ver_res > 0 ? drv->physical_ver_res : drv->ver_res) - drv->offset_y; - if (x_touch < 0 || x_touch > drv->hor_res || y_touch < 0 || y_touch > drv->ver_res) { + case LIBINPUT_EVENT_TOUCH_DOWN: { + lv_coord_t x = libinput_event_touch_get_x_transformed(touch_event, drv->physical_hor_res > 0 ? drv->physical_hor_res : drv->hor_res) - drv->offset_x; + lv_coord_t y = libinput_event_touch_get_y_transformed(touch_event, drv->physical_ver_res > 0 ? drv->physical_ver_res : drv->ver_res) - drv->offset_y; + if (x < 0 || x > drv->hor_res || y < 0 || y > drv->ver_res) { break; /* ignore touches that are out of bounds */ } - state->most_recent_touch_point.x = x_touch; - state->most_recent_touch_point.y = y_touch; - state->button = LV_INDEV_STATE_PR; + evt->point.x = x; + evt->point.y = y; + evt->pressed = LV_INDEV_STATE_PR; break; + } case LIBINPUT_EVENT_TOUCH_UP: - state->button = LV_INDEV_STATE_REL; + evt->pressed = LV_INDEV_STATE_REL; break; case LIBINPUT_EVENT_POINTER_MOTION: pointer_event = libinput_event_get_pointer_event(event); - state->most_recent_touch_point.x += libinput_event_pointer_get_dx(pointer_event); - state->most_recent_touch_point.y += libinput_event_pointer_get_dy(pointer_event); - state->most_recent_touch_point.x = LV_CLAMP(0, state->most_recent_touch_point.x, drv->hor_res - 1); - state->most_recent_touch_point.y = LV_CLAMP(0, state->most_recent_touch_point.y, drv->ver_res - 1); + evt->point.x += libinput_event_pointer_get_dx(pointer_event); + evt->point.y += libinput_event_pointer_get_dy(pointer_event); + evt->point.x = LV_CLAMP(0, evt->point.x, drv->hor_res - 1); + evt->point.y = LV_CLAMP(0, evt->point.y, drv->ver_res - 1); break; case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: pointer_event = libinput_event_get_pointer_event(event); @@ -446,13 +546,13 @@ static void read_pointer(libinput_drv_state_t *state, struct libinput_event *eve if (x_pointer < 0 || x_pointer > drv->hor_res || y_pointer < 0 || y_pointer > drv->ver_res) { break; /* ignore pointer events that are out of bounds */ } - state->most_recent_touch_point.x = x_pointer; - state->most_recent_touch_point.y = y_pointer; + evt->point.x = x_pointer; + evt->point.y = y_pointer; break; case LIBINPUT_EVENT_POINTER_BUTTON: pointer_event = libinput_event_get_pointer_event(event); enum libinput_button_state button_state = libinput_event_pointer_get_button_state(pointer_event); - state->button = button_state == LIBINPUT_BUTTON_STATE_RELEASED ? LV_INDEV_STATE_REL : LV_INDEV_STATE_PR; + evt->pressed = button_state == LIBINPUT_BUTTON_STATE_RELEASED ? LV_INDEV_STATE_REL : LV_INDEV_STATE_PR; break; default: break; @@ -467,50 +567,52 @@ static void read_pointer(libinput_drv_state_t *state, struct libinput_event *eve static void read_keypad(libinput_drv_state_t *state, struct libinput_event *event) { struct libinput_event_keyboard *keyboard_event = NULL; enum libinput_event_type type = libinput_event_get_type(event); + libinput_lv_event_t *evt = NULL; switch (type) { case LIBINPUT_EVENT_KEYBOARD_KEY: + evt = new_event(state); 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 USE_XKB - state->key_val = xkb_process_key_state(&(state->xkb_state), code, key_state == LIBINPUT_KEY_STATE_PRESSED); + evt->key_val = xkb_process_key_state(&(state->xkb_state), code, key_state == LIBINPUT_KEY_STATE_PRESSED); #else switch(code) { case KEY_BACKSPACE: - state->key_val = LV_KEY_BACKSPACE; + evt->key_val = LV_KEY_BACKSPACE; break; case KEY_ENTER: - state->key_val = LV_KEY_ENTER; + evt->key_val = LV_KEY_ENTER; break; case KEY_PREVIOUS: - state->key_val = LV_KEY_PREV; + evt->key_val = LV_KEY_PREV; break; case KEY_NEXT: - state->key_val = LV_KEY_NEXT; + evt->key_val = LV_KEY_NEXT; break; case KEY_UP: - state->key_val = LV_KEY_UP; + evt->key_val = LV_KEY_UP; break; case KEY_LEFT: - state->key_val = LV_KEY_LEFT; + evt->key_val = LV_KEY_LEFT; break; case KEY_RIGHT: - state->key_val = LV_KEY_RIGHT; + evt->key_val = LV_KEY_RIGHT; break; case KEY_DOWN: - state->key_val = LV_KEY_DOWN; + evt->key_val = LV_KEY_DOWN; break; case KEY_TAB: - state->key_val = LV_KEY_NEXT; + evt->key_val = LV_KEY_NEXT; break; default: - state->key_val = 0; + evt->key_val = 0; break; } #endif /* USE_XKB */ - if (state->key_val != 0) { + if (evt->key_val != 0) { /* Only record button state when actual output is produced to prevent widgets from refreshing */ - state->button = (key_state == LIBINPUT_KEY_STATE_RELEASED) ? LV_INDEV_STATE_REL : LV_INDEV_STATE_PR; + evt->pressed = (key_state == LIBINPUT_KEY_STATE_RELEASED) ? LV_INDEV_STATE_REL : LV_INDEV_STATE_PR; } break; default: diff --git a/indev/libinput_drv.h b/indev/libinput_drv.h index 8dd8c43..c15028c 100644 --- a/indev/libinput_drv.h +++ b/indev/libinput_drv.h @@ -30,6 +30,7 @@ extern "C" { #endif #include +#include #if USE_XKB #include "xkb.h" @@ -49,13 +50,27 @@ typedef enum { LIBINPUT_CAPABILITY_TOUCH = 1U << 2 } libinput_capability; +typedef struct { + lv_indev_state_t pressed; + int key_val; + lv_point_t point; +} libinput_lv_event_t; + +#define MAX_EVENTS 32 typedef struct { int fd; struct pollfd fds[1]; - int button; - int key_val; - lv_point_t most_recent_touch_point; + /* The points array is implemented as a circular LIFO queue */ + libinput_lv_event_t points[MAX_EVENTS]; /* Event buffer */ + int start; /* Index of start of event queue */ + int end; /* Index of end of queue*/ + libinput_lv_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; From 4b61d7cd67cacc725b9f036e6010ea73289ada57 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Sat, 8 Jul 2023 20:39:20 +0200 Subject: [PATCH 03/13] Only call libinput_event_get_touch_event on touch events --- indev/libinput.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/indev/libinput.c b/indev/libinput.c index 3739626..181b5a8 100644 --- a/indev/libinput.c +++ b/indev/libinput.c @@ -496,21 +496,26 @@ static void read_pointer(libinput_drv_state_t *state, struct libinput_event *eve libinput_lv_event_t *evt = NULL; enum libinput_event_type type = libinput_event_get_type(event); - /* We only care about these events */ - if (type != LIBINPUT_EVENT_TOUCH_MOTION && - type != LIBINPUT_EVENT_TOUCH_DOWN && - type != LIBINPUT_EVENT_TOUCH_UP && - type != LIBINPUT_EVENT_POINTER_MOTION && - type != LIBINPUT_EVENT_POINTER_BUTTON && - type != LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE) - return; + 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_disp_drv_t *drv = lv_disp_get_default()->driver; /* ignore more than 2 fingers as it will only confuse LVGL */ - touch_event = libinput_event_get_touch_event(event); if (touch_event && libinput_event_touch_get_slot(touch_event) > 1) return; @@ -533,7 +538,6 @@ static void read_pointer(libinput_drv_state_t *state, struct libinput_event *eve evt->pressed = LV_INDEV_STATE_REL; break; case LIBINPUT_EVENT_POINTER_MOTION: - pointer_event = libinput_event_get_pointer_event(event); evt->point.x += libinput_event_pointer_get_dx(pointer_event); evt->point.y += libinput_event_pointer_get_dy(pointer_event); evt->point.x = LV_CLAMP(0, evt->point.x, drv->hor_res - 1); @@ -550,7 +554,6 @@ static void read_pointer(libinput_drv_state_t *state, struct libinput_event *eve evt->point.y = y_pointer; break; case LIBINPUT_EVENT_POINTER_BUTTON: - pointer_event = libinput_event_get_pointer_event(event); enum libinput_button_state button_state = libinput_event_pointer_get_button_state(pointer_event); evt->pressed = button_state == LIBINPUT_BUTTON_STATE_RELEASED ? LV_INDEV_STATE_REL : LV_INDEV_STATE_PR; break; From 30519f0fa2778652fdab0abcd32cdfee372ce165 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Sat, 8 Jul 2023 20:49:52 +0200 Subject: [PATCH 04/13] Initialise mutex in libinput_init_state --- indev/libinput.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/indev/libinput.c b/indev/libinput.c index 181b5a8..c484cb2 100644 --- a/indev/libinput.c +++ b/indev/libinput.c @@ -57,7 +57,7 @@ static void close_restricted(int fd, void *user_data); static struct input_device *devices = NULL; static size_t num_devices = 0; -static libinput_drv_state_t default_state = { .event_lock = PTHREAD_MUTEX_INITIALIZER, }; +static libinput_drv_state_t default_state; static const int timeout = 100; // ms static const nfds_t nfds = 1; @@ -185,7 +185,6 @@ bool libinput_set_file_state(libinput_drv_state_t *state, char* dev_name) */ void libinput_init(void) { - memset(&default_state, 0, sizeof(libinput_drv_state_t)); libinput_init_state(&default_state, LIBINPUT_NAME); } @@ -197,7 +196,9 @@ void libinput_init(void) */ void libinput_init_state(libinput_drv_state_t *state, char* path) { - state->libinput_device = NULL; + /* Clear the state */ + lv_memzero(state, sizeof(libinput_drv_state_t)); + state->libinput_context = libinput_path_create_context(&interface, NULL); if(path == NULL || !libinput_set_file_state(state, path)) { @@ -211,11 +212,13 @@ void libinput_init_state(libinput_drv_state_t *state, char* path) state->fds[0].events = POLLIN; state->fds[0].revents = 0; - pthread_create(&state->worker_thread, NULL, libinput_poll_worker, state); - #if USE_XKB xkb_init_state(&(state->xkb_state)); #endif + + /* Set up thread & lock */ + pthread_mutex_init(&state->event_lock, NULL); + pthread_create(&state->worker_thread, NULL, libinput_poll_worker, state); } /** From af1c04077f800fbaf817dac09133be97ba83118e Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Sat, 8 Jul 2023 21:20:21 +0200 Subject: [PATCH 05/13] Handle pointer click and motion events using delta logic --- indev/libinput.c | 22 ++++++++++++++++++---- indev/libinput_drv.h | 1 + 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/indev/libinput.c b/indev/libinput.c index c484cb2..3e699d5 100644 --- a/indev/libinput.c +++ b/indev/libinput.c @@ -363,15 +363,27 @@ void libinput_read_state(libinput_drv_state_t * state, lv_indev_drv_t * indev_dr libinput_lv_event_t *evt = get_event(state); data->continue_reading = event_pending(state); if (!evt) - evt = &state->last_event; - /* indev expects us to report the most recent state */ - else if (!data->continue_reading) - state->last_event = *evt; + evt = &state->last_event; /* indev expects us to report the most recent state */ + + /* Rewrite relative pointer click and motion events into absolute ones using the previous event */ + if (evt->is_relative) { + /* We need to read unrotated display dimensions directly from the driver because libinput won't account + * for any rotation inside of LVGL */ + lv_disp_drv_t *drv = lv_disp_get_default()->driver; + + evt->point.x = state->last_event.point.x + evt->point.x; + evt->point.y = state->last_event.point.y + evt->point.y; + evt->point.x = LV_CLAMP(0, evt->point.x, drv->hor_res - 1); + evt->point.y = LV_CLAMP(0, evt->point.y, drv->ver_res - 1); + evt->is_relative = false; + } data->point = evt->point; data->state = evt->pressed; data->key = evt->key_val; + state->last_event = *evt; /* Remember the last event for the next call */ + pthread_mutex_unlock(&state->event_lock); if (evt) @@ -545,6 +557,7 @@ static void read_pointer(libinput_drv_state_t *state, struct libinput_event *eve evt->point.y += libinput_event_pointer_get_dy(pointer_event); evt->point.x = LV_CLAMP(0, evt->point.x, drv->hor_res - 1); evt->point.y = LV_CLAMP(0, evt->point.y, drv->ver_res - 1); + evt->is_relative = true; break; case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: pointer_event = libinput_event_get_pointer_event(event); @@ -559,6 +572,7 @@ static void read_pointer(libinput_drv_state_t *state, struct libinput_event *eve case LIBINPUT_EVENT_POINTER_BUTTON: enum libinput_button_state button_state = libinput_event_pointer_get_button_state(pointer_event); evt->pressed = button_state == LIBINPUT_BUTTON_STATE_RELEASED ? LV_INDEV_STATE_REL : LV_INDEV_STATE_PR; + evt->is_relative = true; break; default: break; diff --git a/indev/libinput_drv.h b/indev/libinput_drv.h index c15028c..4f8776c 100644 --- a/indev/libinput_drv.h +++ b/indev/libinput_drv.h @@ -54,6 +54,7 @@ typedef struct { lv_indev_state_t pressed; int key_val; lv_point_t point; + bool is_relative; } libinput_lv_event_t; #define MAX_EVENTS 32 From 878388c5bf20e8fa50683c02f092fdd341035c9f Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Tue, 11 Jul 2023 15:24:52 +0100 Subject: [PATCH 06/13] libinput: insert dummy events to handle fast OSK typing When typing fast with thumbs, you might hit a key while your other thumb is already pressing a key, however this confuses indev quite a lot. For example, you'll press a key (say 'h') with your right thumb, then before releasing your thumb you'll press the 'e' key with your left thumb, and then finally release the 'h' key followed by the 'e' key. This should result in "he" being typed, but usually results in an "e". Indev assumes that a pointer release will always be for whatever the last position was. This is reasonable for mice but not for multitouch displays. This workaround detects this scenario and inserts two dummy events, first a "pressed" state with the coordinates of the first finger (the 'h' key), then a release event, triggering the keypress, then it sends another pressed events with the position of the second finger (the 'e' key) so that if the next event is that finger releasing, the position will be correct. --- indev/libinput.c | 63 +++++++++++++++++++++++++++++++++++++++----- indev/libinput_drv.h | 3 +++ 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/indev/libinput.c b/indev/libinput.c index 3e699d5..75ce382 100644 --- a/indev/libinput.c +++ b/indev/libinput.c @@ -284,6 +284,15 @@ libinput_lv_event_t *get_event(libinput_drv_state_t *state) return evt; } +libinput_lv_event_t *peek_event(libinput_drv_state_t *state) +{ + if (state->start == state->end) { + return NULL; + } + + return &state->points[state->start]; +} + bool event_pending(libinput_drv_state_t *state) { return state->start != state->end; @@ -360,8 +369,8 @@ void libinput_read_state(libinput_drv_state_t * state, lv_indev_drv_t * indev_dr pthread_mutex_lock(&state->event_lock); - libinput_lv_event_t *evt = get_event(state); - data->continue_reading = event_pending(state); + /* We may want to send this event twice so only peek it for now */ + libinput_lv_event_t *evt = peek_event(state); if (!evt) evt = &state->last_event; /* indev expects us to report the most recent state */ @@ -376,18 +385,51 @@ void libinput_read_state(libinput_drv_state_t * state, lv_indev_drv_t * indev_dr evt->point.x = LV_CLAMP(0, evt->point.x, drv->hor_res - 1); evt->point.y = LV_CLAMP(0, evt->point.y, drv->ver_res - 1); evt->is_relative = false; + } else if (evt->pressed == LV_INDEV_STATE_REL && evt->slot == 0 + && state->slots[1].pressed == LV_INDEV_STATE_PR && !state->doing_mtouch_dummy_event) { + /* + * 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. + * + * To work around this, detect the case where a finger is releasing while the other finger is + * still pressed and insert a dummy press event for the finger which is still pressed. + */ + + /* evt won't be consumed so it will be re-sent on the next call */ + evt->pressed = LV_INDEV_STATE_PR; + evt->point = state->slots[0].point; + state->doing_mtouch_dummy_event = 1; + + /* slot 1 will become slot 0 when slot 0 is released */ + state->slots[1].pressed = LV_INDEV_STATE_REL; + } else if (state->doing_mtouch_dummy_event == 1) { + /* Now that the position is definitely correct, send the release */ + evt->pressed = LV_INDEV_STATE_REL; + state->doing_mtouch_dummy_event++; + } else if (state->doing_mtouch_dummy_event == 2) { + /* Finally, update the position to the remaining finger and send a press */ + evt->pressed = LV_INDEV_STATE_PR; + evt->point = state->slots[1].point; /* Wherever slot 1 most recently pressed */ + state->doing_mtouch_dummy_event = 0; + } else { + /* Consume the event */ + get_event(state); } data->point = evt->point; data->state = evt->pressed; data->key = evt->key_val; + data->continue_reading = event_pending(state); state->last_event = *evt; /* Remember the last event for the next call */ pthread_mutex_unlock(&state->event_lock); if (evt) - LV_LOG_TRACE("libinput_read: %d//%d: (%04d,%04d): %d continue_reading? %d", state->start, state->end, data->point.x, data->point.y, data->state, data->continue_reading); + LV_LOG_TRACE("libinput_read: %d (%04d, %04d): %d continue_reading? %d", evt->slot, data->point.x, data->point.y, data->state, data->continue_reading); } @@ -510,6 +552,7 @@ static void read_pointer(libinput_drv_state_t *state, struct libinput_event *eve struct libinput_event_pointer *pointer_event = NULL; libinput_lv_event_t *evt = NULL; enum libinput_event_type type = libinput_event_get_type(event); + int slot; switch (type) { case LIBINPUT_EVENT_TOUCH_MOTION: @@ -531,7 +574,7 @@ static void read_pointer(libinput_drv_state_t *state, struct libinput_event *eve lv_disp_drv_t *drv = lv_disp_get_default()->driver; /* ignore more than 2 fingers as it will only confuse LVGL */ - if (touch_event && libinput_event_touch_get_slot(touch_event) > 1) + if (touch_event && (slot = libinput_event_touch_get_slot(touch_event)) > 1) return; evt = new_event(state); @@ -547,10 +590,15 @@ static void read_pointer(libinput_drv_state_t *state, struct libinput_event *eve evt->point.x = x; evt->point.y = y; evt->pressed = LV_INDEV_STATE_PR; + evt->slot = slot; + state->slots[slot].point = evt->point; + state->slots[slot].pressed = evt->pressed; break; } case LIBINPUT_EVENT_TOUCH_UP: evt->pressed = LV_INDEV_STATE_REL; + state->slots[slot].pressed = evt->pressed; + evt->slot = slot; break; case LIBINPUT_EVENT_POINTER_MOTION: evt->point.x += libinput_event_pointer_get_dx(pointer_event); @@ -559,8 +607,7 @@ static void read_pointer(libinput_drv_state_t *state, struct libinput_event *eve evt->point.y = LV_CLAMP(0, evt->point.y, drv->ver_res - 1); evt->is_relative = true; break; - case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: - pointer_event = libinput_event_get_pointer_event(event); + case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: { lv_coord_t x_pointer = libinput_event_pointer_get_absolute_x_transformed(pointer_event, drv->physical_hor_res > 0 ? drv->physical_hor_res : drv->hor_res) - drv->offset_x; lv_coord_t y_pointer = libinput_event_pointer_get_absolute_y_transformed(pointer_event, drv->physical_ver_res > 0 ? drv->physical_ver_res : drv->ver_res) - drv->offset_y; if (x_pointer < 0 || x_pointer > drv->hor_res || y_pointer < 0 || y_pointer > drv->ver_res) { @@ -569,11 +616,13 @@ static void read_pointer(libinput_drv_state_t *state, struct libinput_event *eve evt->point.x = x_pointer; evt->point.y = y_pointer; break; - case LIBINPUT_EVENT_POINTER_BUTTON: + } + case LIBINPUT_EVENT_POINTER_BUTTON: { enum libinput_button_state button_state = libinput_event_pointer_get_button_state(pointer_event); evt->pressed = button_state == LIBINPUT_BUTTON_STATE_RELEASED ? LV_INDEV_STATE_REL : LV_INDEV_STATE_PR; evt->is_relative = true; break; + } default: break; } diff --git a/indev/libinput_drv.h b/indev/libinput_drv.h index 4f8776c..e7ca916 100644 --- a/indev/libinput_drv.h +++ b/indev/libinput_drv.h @@ -55,6 +55,7 @@ typedef struct { int key_val; lv_point_t point; bool is_relative; + int slot : 4; } libinput_lv_event_t; #define MAX_EVENTS 32 @@ -64,6 +65,8 @@ typedef struct { /* The points array is implemented as a circular LIFO queue */ libinput_lv_event_t points[MAX_EVENTS]; /* Event buffer */ + libinput_lv_event_t slots[2]; /* Realtime state of up to 2 fingers to handle multitouch */ + int doing_mtouch_dummy_event; int start; /* Index of start of event queue */ int end; /* Index of end of queue*/ libinput_lv_event_t last_event; /* Report when no new events From 5a70147b8f2bf7cd29655002c596a2fcaf9a4826 Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Mon, 24 Jul 2023 21:28:41 +0100 Subject: [PATCH 07/13] libinput: fix pointer always releasing on motion event --- indev/libinput.c | 29 ++++++++++++++--------------- indev/libinput_drv.h | 1 + 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/indev/libinput.c b/indev/libinput.c index 75ce382..023b002 100644 --- a/indev/libinput.c +++ b/indev/libinput.c @@ -409,12 +409,13 @@ void libinput_read_state(libinput_drv_state_t * state, lv_indev_drv_t * indev_dr /* Now that the position is definitely correct, send the release */ evt->pressed = LV_INDEV_STATE_REL; state->doing_mtouch_dummy_event++; - } else if (state->doing_mtouch_dummy_event == 2) { - /* Finally, update the position to the remaining finger and send a press */ - evt->pressed = LV_INDEV_STATE_PR; - evt->point = state->slots[1].point; /* Wherever slot 1 most recently pressed */ - state->doing_mtouch_dummy_event = 0; } else { + if (state->doing_mtouch_dummy_event == 2) { + /* Finally, update the position to the remaining finger and send a press */ + evt->pressed = LV_INDEV_STATE_PR; + evt->point = state->slots[1].point; /* Wherever slot 1 most recently pressed */ + state->doing_mtouch_dummy_event = 0; + } /* Consume the event */ get_event(state); } @@ -600,11 +601,15 @@ static void read_pointer(libinput_drv_state_t *state, struct libinput_event *eve state->slots[slot].pressed = evt->pressed; evt->slot = slot; break; + case LIBINPUT_EVENT_POINTER_BUTTON: { + enum libinput_button_state button_state = libinput_event_pointer_get_button_state(pointer_event); + state->pointer_button_down = button_state == LIBINPUT_BUTTON_STATE_RELEASED ? LV_INDEV_STATE_REL : LV_INDEV_STATE_PR; + __attribute__((fallthrough)); + } case LIBINPUT_EVENT_POINTER_MOTION: - evt->point.x += libinput_event_pointer_get_dx(pointer_event); - evt->point.y += libinput_event_pointer_get_dy(pointer_event); - evt->point.x = LV_CLAMP(0, evt->point.x, drv->hor_res - 1); - evt->point.y = LV_CLAMP(0, evt->point.y, drv->ver_res - 1); + evt->point.x = libinput_event_pointer_get_dx(pointer_event); + evt->point.y = libinput_event_pointer_get_dy(pointer_event); + evt->pressed = state->pointer_button_down; evt->is_relative = true; break; case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: { @@ -617,12 +622,6 @@ static void read_pointer(libinput_drv_state_t *state, struct libinput_event *eve evt->point.y = y_pointer; break; } - case LIBINPUT_EVENT_POINTER_BUTTON: { - enum libinput_button_state button_state = libinput_event_pointer_get_button_state(pointer_event); - evt->pressed = button_state == LIBINPUT_BUTTON_STATE_RELEASED ? LV_INDEV_STATE_REL : LV_INDEV_STATE_PR; - evt->is_relative = true; - break; - } default: break; } diff --git a/indev/libinput_drv.h b/indev/libinput_drv.h index e7ca916..d9355ca 100644 --- a/indev/libinput_drv.h +++ b/indev/libinput_drv.h @@ -66,6 +66,7 @@ typedef struct { /* The points array is implemented as a circular LIFO queue */ libinput_lv_event_t points[MAX_EVENTS]; /* Event buffer */ libinput_lv_event_t slots[2]; /* Realtime state of up to 2 fingers to handle multitouch */ + bool pointer_button_down; int doing_mtouch_dummy_event; int start; /* Index of start of event queue */ int end; /* Index of end of queue*/ From 181f59304d6c3765a0be63e112d4e2c20eb2b988 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Wed, 26 Jul 2023 21:19:18 +0200 Subject: [PATCH 08/13] Eliminate is_relative in favor of storing last known position --- indev/libinput.c | 34 ++++++++++++++-------------------- indev/libinput_drv.h | 5 ++++- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/indev/libinput.c b/indev/libinput.c index 023b002..8429f89 100644 --- a/indev/libinput.c +++ b/indev/libinput.c @@ -374,18 +374,7 @@ void libinput_read_state(libinput_drv_state_t * state, lv_indev_drv_t * indev_dr if (!evt) evt = &state->last_event; /* indev expects us to report the most recent state */ - /* Rewrite relative pointer click and motion events into absolute ones using the previous event */ - if (evt->is_relative) { - /* We need to read unrotated display dimensions directly from the driver because libinput won't account - * for any rotation inside of LVGL */ - lv_disp_drv_t *drv = lv_disp_get_default()->driver; - - evt->point.x = state->last_event.point.x + evt->point.x; - evt->point.y = state->last_event.point.y + evt->point.y; - evt->point.x = LV_CLAMP(0, evt->point.x, drv->hor_res - 1); - evt->point.y = LV_CLAMP(0, evt->point.y, drv->ver_res - 1); - evt->is_relative = false; - } else if (evt->pressed == LV_INDEV_STATE_REL && evt->slot == 0 + if (evt->pressed == LV_INDEV_STATE_REL && evt->slot == 0 && state->slots[1].pressed == LV_INDEV_STATE_PR && !state->doing_mtouch_dummy_event) { /* * We don't support "multitouch", but libinput does. To make fast typing with two thumbs @@ -601,16 +590,14 @@ static void read_pointer(libinput_drv_state_t *state, struct libinput_event *eve state->slots[slot].pressed = evt->pressed; evt->slot = slot; break; - case LIBINPUT_EVENT_POINTER_BUTTON: { - enum libinput_button_state button_state = libinput_event_pointer_get_button_state(pointer_event); - state->pointer_button_down = button_state == LIBINPUT_BUTTON_STATE_RELEASED ? LV_INDEV_STATE_REL : LV_INDEV_STATE_PR; - __attribute__((fallthrough)); - } case LIBINPUT_EVENT_POINTER_MOTION: - evt->point.x = libinput_event_pointer_get_dx(pointer_event); - evt->point.y = libinput_event_pointer_get_dy(pointer_event); + state->pointer_position.x += libinput_event_pointer_get_dx(pointer_event); + state->pointer_position.y += libinput_event_pointer_get_dy(pointer_event); + state->pointer_position.x = LV_CLAMP(0, state->pointer_position.x, drv->hor_res - 1); + state->pointer_position.y = LV_CLAMP(0, state->pointer_position.y, drv->ver_res - 1); + evt->point.x = state->pointer_position.x; + evt->point.y = state->pointer_position.y; evt->pressed = state->pointer_button_down; - evt->is_relative = true; break; case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: { lv_coord_t x_pointer = libinput_event_pointer_get_absolute_x_transformed(pointer_event, drv->physical_hor_res > 0 ? drv->physical_hor_res : drv->hor_res) - drv->offset_x; @@ -622,6 +609,13 @@ static void read_pointer(libinput_drv_state_t *state, struct libinput_event *eve evt->point.y = y_pointer; break; } + case LIBINPUT_EVENT_POINTER_BUTTON: { + enum libinput_button_state button_state = libinput_event_pointer_get_button_state(pointer_event); + state->pointer_button_down = button_state == LIBINPUT_BUTTON_STATE_RELEASED ? LV_INDEV_STATE_REL : LV_INDEV_STATE_PR; + evt->point.x = state->pointer_position.x; + evt->point.y = state->pointer_position.y; + evt->pressed = state->pointer_button_down; + } default: break; } diff --git a/indev/libinput_drv.h b/indev/libinput_drv.h index d9355ca..ae04bbb 100644 --- a/indev/libinput_drv.h +++ b/indev/libinput_drv.h @@ -54,7 +54,6 @@ typedef struct { lv_indev_state_t pressed; int key_val; lv_point_t point; - bool is_relative; int slot : 4; } libinput_lv_event_t; @@ -66,7 +65,11 @@ typedef struct { /* The points array is implemented as a circular LIFO queue */ libinput_lv_event_t points[MAX_EVENTS]; /* Event buffer */ libinput_lv_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 doing_mtouch_dummy_event; int start; /* Index of start of event queue */ int end; /* Index of end of queue*/ From 953c66478ebb33140ad2a9d763d0f8c868658474 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Wed, 26 Jul 2023 21:22:26 +0200 Subject: [PATCH 09/13] Also set pressed on absolute pointer motion events --- indev/libinput.c | 1 + 1 file changed, 1 insertion(+) diff --git a/indev/libinput.c b/indev/libinput.c index 8429f89..c1efd0d 100644 --- a/indev/libinput.c +++ b/indev/libinput.c @@ -607,6 +607,7 @@ static void read_pointer(libinput_drv_state_t *state, struct libinput_event *eve } evt->point.x = x_pointer; evt->point.y = y_pointer; + evt->pressed = state->pointer_button_down; break; } case LIBINPUT_EVENT_POINTER_BUTTON: { From 1c4d998156426e80a190e03bf5c4a72532ba37c0 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Wed, 26 Jul 2023 22:00:00 +0200 Subject: [PATCH 10/13] Move dummy event logic for multi-touch into the libinput read loop to make it thread-safe --- indev/libinput.c | 88 +++++++++++++++++++------------------------- indev/libinput_drv.h | 2 - 2 files changed, 37 insertions(+), 53 deletions(-) diff --git a/indev/libinput.c b/indev/libinput.c index c1efd0d..aeea196 100644 --- a/indev/libinput.c +++ b/indev/libinput.c @@ -284,15 +284,6 @@ libinput_lv_event_t *get_event(libinput_drv_state_t *state) return evt; } -libinput_lv_event_t *peek_event(libinput_drv_state_t *state) -{ - if (state->start == state->end) { - return NULL; - } - - return &state->points[state->start]; -} - bool event_pending(libinput_drv_state_t *state) { return state->start != state->end; @@ -369,57 +360,22 @@ void libinput_read_state(libinput_drv_state_t * state, lv_indev_drv_t * indev_dr pthread_mutex_lock(&state->event_lock); - /* We may want to send this event twice so only peek it for now */ - libinput_lv_event_t *evt = peek_event(state); + libinput_lv_event_t *evt = get_event(state); + if (!evt) evt = &state->last_event; /* indev expects us to report the most recent state */ - if (evt->pressed == LV_INDEV_STATE_REL && evt->slot == 0 - && state->slots[1].pressed == LV_INDEV_STATE_PR && !state->doing_mtouch_dummy_event) { - /* - * 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. - * - * To work around this, detect the case where a finger is releasing while the other finger is - * still pressed and insert a dummy press event for the finger which is still pressed. - */ - - /* evt won't be consumed so it will be re-sent on the next call */ - evt->pressed = LV_INDEV_STATE_PR; - evt->point = state->slots[0].point; - state->doing_mtouch_dummy_event = 1; - - /* slot 1 will become slot 0 when slot 0 is released */ - state->slots[1].pressed = LV_INDEV_STATE_REL; - } else if (state->doing_mtouch_dummy_event == 1) { - /* Now that the position is definitely correct, send the release */ - evt->pressed = LV_INDEV_STATE_REL; - state->doing_mtouch_dummy_event++; - } else { - if (state->doing_mtouch_dummy_event == 2) { - /* Finally, update the position to the remaining finger and send a press */ - evt->pressed = LV_INDEV_STATE_PR; - evt->point = state->slots[1].point; /* Wherever slot 1 most recently pressed */ - state->doing_mtouch_dummy_event = 0; - } - /* Consume the event */ - get_event(state); - } - data->point = evt->point; data->state = evt->pressed; data->key = evt->key_val; - data->continue_reading = event_pending(state); + state->last_event = *evt; /* Remember the last event for the next call */ pthread_mutex_unlock(&state->event_lock); if (evt) - LV_LOG_TRACE("libinput_read: %d (%04d, %04d): %d continue_reading? %d", evt->slot, data->point.x, data->point.y, data->state, data->continue_reading); + LV_LOG_TRACE("libinput_read: (%04d, %04d): %d continue_reading? %d", data->point.x, data->point.y, data->state, data->continue_reading); } @@ -580,15 +536,45 @@ static void read_pointer(libinput_drv_state_t *state, struct libinput_event *eve evt->point.x = x; evt->point.y = y; evt->pressed = LV_INDEV_STATE_PR; - evt->slot = slot; state->slots[slot].point = evt->point; state->slots[slot].pressed = evt->pressed; break; } case LIBINPUT_EVENT_TOUCH_UP: - evt->pressed = LV_INDEV_STATE_REL; + if (slot == 0 && state->slots[1].pressed == LV_INDEV_STATE_PR) { + /* + * 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, detect the case where the first finger is releasing while the second is + * still pressed and insert dummy press events for the first and second finger around the + * release event for the first finger. + * + * In other words, P1 > P2 > R1 > R2 becomes P1 > P2 > (P1) > R1 > (P2) > R2 where P and R + * mean press and release, respectively, and the parentheses denote the dummy events. + */ + + /* Inject the dummy press event for the first finger */ + libinput_lv_event_t *synth_evt = evt; + synth_evt->pressed = LV_INDEV_STATE_PR; + synth_evt->point = state->slots[0].point; + + /* Append the real release event for the first finger */ + evt = new_event(state); + evt->pressed = LV_INDEV_STATE_REL; + + /* Inject the dummy press event for the second finger */ + synth_evt = new_event(state); + synth_evt->pressed = LV_INDEV_STATE_PR; + synth_evt->point = state->slots[1].point; + } else { + evt->pressed = LV_INDEV_STATE_REL; + } + state->slots[slot].pressed = evt->pressed; - evt->slot = slot; break; case LIBINPUT_EVENT_POINTER_MOTION: state->pointer_position.x += libinput_event_pointer_get_dx(pointer_event); diff --git a/indev/libinput_drv.h b/indev/libinput_drv.h index ae04bbb..473755a 100644 --- a/indev/libinput_drv.h +++ b/indev/libinput_drv.h @@ -54,7 +54,6 @@ typedef struct { lv_indev_state_t pressed; int key_val; lv_point_t point; - int slot : 4; } libinput_lv_event_t; #define MAX_EVENTS 32 @@ -70,7 +69,6 @@ typedef struct { lv_point_t pointer_position; bool pointer_button_down; - int doing_mtouch_dummy_event; int start; /* Index of start of event queue */ int end; /* Index of end of queue*/ libinput_lv_event_t last_event; /* Report when no new events From 805a7f5b876bd5fd990a21a3ec1a41c2c8b3e453 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Thu, 27 Jul 2023 21:12:59 +0200 Subject: [PATCH 11/13] Handle the case where the second finger is released before the first --- indev/libinput.c | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/indev/libinput.c b/indev/libinput.c index aeea196..ce3cb19 100644 --- a/indev/libinput.c +++ b/indev/libinput.c @@ -541,21 +541,21 @@ static void read_pointer(libinput_drv_state_t *state, struct libinput_event *eve 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 && state->slots[1].pressed == LV_INDEV_STATE_PR) { - /* - * 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, detect the case where the first finger is releasing while the second is - * still pressed and insert dummy press events for the first and second finger around the - * release event for the first finger. - * - * In other words, P1 > P2 > R1 > R2 becomes P1 > P2 > (P1) > R1 > (P2) > R2 where P and R - * mean press and release, respectively, and the parentheses denote the dummy events. - */ + /* 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 */ libinput_lv_event_t *synth_evt = evt; @@ -570,6 +570,18 @@ static void read_pointer(libinput_drv_state_t *state, struct libinput_event *eve synth_evt = new_event(state); synth_evt->pressed = LV_INDEV_STATE_PR; synth_evt->point = state->slots[1].point; + } else if (slot == 1 && state->slots[0].pressed == LV_INDEV_STATE_PR) { + /* 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_REL; + + /* Inject the dummy press event for the first finger */ + libinput_lv_event_t *synth_evt = new_event(state); + synth_evt->pressed = LV_INDEV_STATE_PR; + synth_evt->point = state->slots[0].point; } else { evt->pressed = LV_INDEV_STATE_REL; } From 7e3135bcae37cbd03c2f003fcc96278671bd8632 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Sat, 30 Sep 2023 20:26:50 +0200 Subject: [PATCH 12/13] Set point on touch release events --- indev/libinput.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/indev/libinput.c b/indev/libinput.c index ce3cb19..a78419f 100644 --- a/indev/libinput.c +++ b/indev/libinput.c @@ -565,6 +565,7 @@ static void read_pointer(libinput_drv_state_t *state, struct libinput_event *eve /* Append the real release event for the first finger */ evt = new_event(state); evt->pressed = LV_INDEV_STATE_REL; + evt->point = state->slots[0].point; /* Inject the dummy press event for the second finger */ synth_evt = new_event(state); @@ -577,6 +578,7 @@ static void read_pointer(libinput_drv_state_t *state, struct libinput_event *eve /* Append the real release event for the second finger */ evt->pressed = LV_INDEV_STATE_REL; + evt->point = state->slots[1].point; /* Inject the dummy press event for the first finger */ libinput_lv_event_t *synth_evt = new_event(state); @@ -584,6 +586,7 @@ static void read_pointer(libinput_drv_state_t *state, struct libinput_event *eve synth_evt->point = state->slots[0].point; } else { evt->pressed = LV_INDEV_STATE_REL; + evt->point = state->slots[slot].point; } state->slots[slot].pressed = evt->pressed; From 36d981a7d4dff6d288c4294cf0763d7c642a2bff Mon Sep 17 00:00:00 2001 From: jane400 Date: Sat, 16 Dec 2023 20:38:33 +0100 Subject: [PATCH 13/13] libinput: release keyboard keys in lvgl directly after pressing This enables fast typing on hardware keyboards as keys are no longer forgotten. --- indev/libinput.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/indev/libinput.c b/indev/libinput.c index a78419f..0f63a58 100644 --- a/indev/libinput.c +++ b/indev/libinput.c @@ -677,6 +677,19 @@ static void read_keypad(libinput_drv_state_t *state, struct libinput_event *even 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_REL : LV_INDEV_STATE_PR; + + // 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) { + libinput_lv_event_t *release_evt = new_event(state); + release_evt->pressed = LV_INDEV_STATE_REL; + release_evt->key_val = evt->key_val; + } } break; default: