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 diff --git a/indev/libinput.c b/indev/libinput.c index 4702caf..3ef8209 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,38 @@ 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; - - 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); + LV_UNUSED(indev_drv); + + 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 */ + + /* 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; } -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; + + 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) + 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 +505,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->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); - 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; + evt->is_relative = true; break; default: break; @@ -457,50 +568,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..4f8776c 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,28 @@ typedef enum { LIBINPUT_CAPABILITY_TOUCH = 1U << 2 } libinput_capability; +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 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;