From 0b7f03e3f1d1dc7cea6ea0f6b994532a48afa581 Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Sun, 4 Jun 2023 20:28:25 +0100 Subject: [PATCH 1/3] 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 f0f053a00b37a5794e4504102c86cb571489f45a Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Sun, 4 Jun 2023 21:08:33 +0100 Subject: [PATCH 2/3] 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 | 219 +++++++++++++++++++++++++++++++------------ indev/libinput_drv.h | 21 ++++- 2 files changed, 178 insertions(+), 62 deletions(-) diff --git a/indev/libinput.c b/indev/libinput.c index 4702caf..8d6fc3b 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,39 +493,55 @@ 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) + 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); + 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; - state->most_recent_touch_point.y = y; - 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_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; @@ -457,50 +556,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 19a03ead9c1c1f4c37adee446505bb6994f6b204 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Sat, 8 Jul 2023 21:20:21 +0200 Subject: [PATCH 3/3] Handle pointer click and motion events using delta logic --- indev/libinput.c | 28 ++++++++++++++++++++-------- indev/libinput_drv.h | 1 + 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/indev/libinput.c b/indev/libinput.c index 8d6fc3b..3ef8209 100644 --- a/indev/libinput.c +++ b/indev/libinput.c @@ -360,15 +360,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) @@ -533,15 +545,15 @@ static void read_pointer(libinput_drv_state_t *state, struct libinput_event *eve 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); - 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->is_relative = true; 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; + 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