diff --git a/src/drivers/evdev/lv_evdev.c b/src/drivers/evdev/lv_evdev.c index 20c5d7e4a0..1b8319e049 100644 --- a/src/drivers/evdev/lv_evdev.c +++ b/src/drivers/evdev/lv_evdev.c @@ -33,6 +33,7 @@ #include "../../display/lv_display.h" #include "../../display/lv_display_private.h" #include "../../widgets/image/lv_image.h" +#include "../../indev/lv_indev_gesture.h" /********************* * DEFINES @@ -43,6 +44,7 @@ #define EVDEV_DISCOVERY_PATH_BUF_SIZE 32 #define REL_XY_MASK ((1 << REL_X) | (1 << REL_Y)) #define ABS_XY_MASK ((1 << ABS_X) | (1 << ABS_Y)) +#define MAX_TOUCH_POINTS 5 /********************** * TYPEDEFS @@ -66,6 +68,13 @@ typedef struct { int key; lv_indev_state_t state; bool deleting; + /* Multi-touch support */ +#if LV_USE_GESTURE_RECOGNITION + lv_indev_touch_data_t touch_data[MAX_TOUCH_POINTS]; /* Array of touch points for gesture recognition */ + uint8_t touch_count; /* Number of valid touch points */ + uint8_t current_slot; /* Current touch point slot */ + bool touch_data_changed; /* Flag to indicate if touch data has changed since last SYN_REPORT */ +#endif } lv_evdev_t; #ifndef BSD @@ -161,12 +170,70 @@ static void _evdev_read(lv_indev_t * indev, lv_indev_data_t * data) else if(in.code == REL_Y) dsc->root_y += in.value; } else if(in.type == EV_ABS) { - if(in.code == ABS_X || in.code == ABS_MT_POSITION_X) dsc->root_x = in.value; - else if(in.code == ABS_Y || in.code == ABS_MT_POSITION_Y) dsc->root_y = in.value; - else if(in.code == ABS_MT_TRACKING_ID) { - if(in.value == -1) dsc->state = LV_INDEV_STATE_RELEASED; - else if(in.value == 0) dsc->state = LV_INDEV_STATE_PRESSED; +#if LV_USE_GESTURE_RECOGNITION + if(in.code == ABS_MT_SLOT) { + if(in.value >= MAX_TOUCH_POINTS) { + dsc->current_slot = MAX_TOUCH_POINTS - 1; + dsc->touch_count = MAX_TOUCH_POINTS; + LV_LOG_WARN("Touch point slot out of range, setting to max: %d", MAX_TOUCH_POINTS - 1); + } + else { + dsc->current_slot = in.value; + dsc->touch_count = LV_MAX(dsc->touch_count, dsc->current_slot + 1); + LV_LOG_TRACE("Slot changed to %d, touch_count=%d", dsc->current_slot, dsc->touch_count); + } } + else +#endif + if(in.code == ABS_X || in.code == ABS_MT_POSITION_X) { + dsc->root_x = in.value; +#if LV_USE_GESTURE_RECOGNITION + if(in.code == ABS_MT_POSITION_X && dsc->current_slot < MAX_TOUCH_POINTS) { + dsc->touch_data[dsc->current_slot].point.x = in.value; + dsc->touch_data_changed = true; + LV_LOG_TRACE("MT_X update: slot=%d, x=%d", dsc->current_slot, in.value); + } +#endif + } + else if(in.code == ABS_Y || in.code == ABS_MT_POSITION_Y) { + dsc->root_y = in.value; +#if LV_USE_GESTURE_RECOGNITION + if(in.code == ABS_MT_POSITION_Y && dsc->current_slot < MAX_TOUCH_POINTS) { + dsc->touch_data[dsc->current_slot].point.y = in.value; + dsc->touch_data_changed = true; + LV_LOG_TRACE("MT_Y update: slot=%d, y=%d", dsc->current_slot, in.value); + } +#endif + } + else if(in.code == ABS_MT_TRACKING_ID) { + if(in.value == -1) dsc->state = LV_INDEV_STATE_RELEASED; + else dsc->state = LV_INDEV_STATE_PRESSED; +#if LV_USE_GESTURE_RECOGNITION + if(in.value == -1) { + if(dsc->current_slot < MAX_TOUCH_POINTS) { + dsc->touch_data[dsc->current_slot].state = LV_INDEV_STATE_RELEASED; + dsc->touch_data_changed = true; + LV_LOG_TRACE("Touch slot %d released", dsc->current_slot); + + dsc->touch_count = 0; + for(int i = 0; i < MAX_TOUCH_POINTS; i++) { + if(dsc->touch_data[i].state == LV_INDEV_STATE_PRESSED) { + dsc->touch_count = i + 1; + } + } + } + } + else { + if(dsc->current_slot < MAX_TOUCH_POINTS) { + dsc->touch_data[dsc->current_slot].state = LV_INDEV_STATE_PRESSED; + dsc->touch_data[dsc->current_slot].id = dsc->current_slot; + dsc->touch_count = LV_MAX(dsc->touch_count, dsc->current_slot + 1); + dsc->touch_data_changed = true; + LV_LOG_TRACE("Touch slot %d pressed, touch_count=%d", dsc->current_slot, dsc->touch_count); + } + } +#endif + } } else if(in.type == EV_KEY) { if(in.code == BTN_MOUSE || in.code == BTN_TOUCH) { @@ -182,7 +249,64 @@ static void _evdev_read(lv_indev_t * indev, lv_indev_data_t * data) } } } +#if LV_USE_GESTURE_RECOGNITION + else if(in.type == EV_SYN && in.code == SYN_REPORT) { + /* Handle gesture recognition at sync event */ + if(dsc->touch_count > 0 && dsc->touch_data_changed) { + LV_LOG_TRACE("=== SYN_REPORT: touch_count=%d ===", dsc->touch_count); + for(int i = 0; i < MAX_TOUCH_POINTS; i++) { + if(dsc->touch_data[i].state == LV_INDEV_STATE_PRESSED || dsc->touch_data[i].state == LV_INDEV_STATE_RELEASED) { + LV_LOG_TRACE("Slot %d: state=%s, raw(%d, %d)", + i, + dsc->touch_data[i].state == LV_INDEV_STATE_PRESSED ? "PRESSED" : "RELEASED", + dsc->touch_data[i].point.x, dsc->touch_data[i].point.y); + } + } + + /* Create a temporary array with calibrated coordinates for gesture recognition */ + lv_indev_touch_data_t calibrated_touch_data[MAX_TOUCH_POINTS]; + + int active_touches = 0; + + for(int i = 0; i < MAX_TOUCH_POINTS; i++) { + if(dsc->touch_data[i].state == LV_INDEV_STATE_PRESSED || dsc->touch_data[i].state == LV_INDEV_STATE_RELEASED) { + calibrated_touch_data[active_touches] = dsc->touch_data[i]; + + lv_point_t calib_point = _evdev_process_pointer(indev, dsc->touch_data[i].point.x, dsc->touch_data[i].point.y); + calibrated_touch_data[active_touches].point = calib_point; + + LV_LOG_TRACE("Touch %d (slot %d): state=%s, raw(%d, %d) -> calib(%d, %d)", + active_touches, i, + dsc->touch_data[i].state == LV_INDEV_STATE_PRESSED ? "PRESSED" : "RELEASED", + dsc->touch_data[i].point.x, dsc->touch_data[i].point.y, + calib_point.x, calib_point.y); + active_touches++; + } + } + + LV_LOG_TRACE("Gesture recognition: %d touches detected", active_touches); + lv_indev_gesture_recognizers_update(indev, calibrated_touch_data, active_touches); + lv_indev_gesture_recognizers_set_data(indev, data); + + /* Clear RELEASED touch points after gesture recognition to prevent duplicate processing */ + for(int i = 0; i < MAX_TOUCH_POINTS; i++) { + if(dsc->touch_data[i].state == LV_INDEV_STATE_RELEASED) { + /* Mark touch point as invalid by zeroing out the data */ + dsc->touch_data[i].point.x = 0; + dsc->touch_data[i].point.y = 0; + dsc->touch_data[i].id = -1; /* Mark as invalid */ + /* Note: We keep the RELEASED state for this frame, it will be naturally + * cleared when new touch events come in or when all touches end */ + LV_LOG_TRACE("Cleared released touch point slot %d", i); + } + } + + dsc->touch_data_changed = false; + } + } +#endif } + if(!dsc->deleting && br == -1 && errno != EAGAIN) { if(errno == ENODEV) { LV_LOG_INFO("evdev device was removed"); @@ -201,8 +325,19 @@ static void _evdev_read(lv_indev_t * indev, lv_indev_data_t * data) data->key = dsc->key; break; case LV_INDEV_TYPE_POINTER: +#if LV_USE_GESTURE_RECOGNITION + if(dsc->touch_count > 0) { + data->state = dsc->touch_data[0].state; + data->point = _evdev_process_pointer(indev, dsc->touch_data[0].point.x, dsc->touch_data[0].point.y); + } + else { + data->state = dsc->state; + data->point = _evdev_process_pointer(indev, dsc->root_x, dsc->root_y); + } +#else data->state = dsc->state; data->point = _evdev_process_pointer(indev, dsc->root_x, dsc->root_y); +#endif break; default: break;