diff --git a/drivers/sensors/navigator_trackpad.c b/drivers/sensors/navigator_trackpad.c index 94a100e45e..ef01e696f8 100644 --- a/drivers/sensors/navigator_trackpad.c +++ b/drivers/sensors/navigator_trackpad.c @@ -24,16 +24,11 @@ uint16_t current_cpi = DEFAULT_CPI_TICK; uint32_t gpio_offset_addr; uint8_t has_motion = 0; extern bool set_scrolling; -bool in_motion; bool trackpad_init; #if defined(NAVIGATOR_TRACKPAD_PTP_MODE) -cgen6_report_t ptp_report; -bool prev_ptp_flag, prev_tap_clear = false; -uint8_t last_finger_count = 0; -uint16_t prev_ptp_x, prev_ptp_y; -uint16_t tap_timer = 0; -int16_t ptp_delta_x, ptp_delta_y; +cgen6_report_t ptp_report; +trackpad_gesture_t gesture = {0}; #endif // Helper functions to parse ptp reports @@ -301,7 +296,6 @@ void dump_ptp_report(void) { printf(" Contact Count: %d\n", ptp_report.contact_count); printf(" Buttons: %d\n", ptp_report.buttons); printf(" Fingers: %d\n", finger_count(&ptp_report)); - printf(" Last Finger: %d\n", last_finger_count); #endif } @@ -384,13 +378,19 @@ void navigator_trackpad_device_init(void) { } report_mouse_t navigator_trackpad_get_report(report_mouse_t mouse_report) { - if (!has_motion || !trackpad_init) { - if (prev_tap_clear) { - prev_tap_clear = false; - mouse_report.buttons = 0; - } +#if defined(NAVIGATOR_TRACKPAD_PTP_MODE) + // Handle pending click release from previous cycle + if (gesture.pending_click) { + gesture.pending_click = false; + mouse_report.buttons = 0; return mouse_report; } +#endif + + if (!has_motion || !trackpad_init) { + return mouse_report; + } + #if defined(NAVIGATOR_TRACKPAD_RELATIVE_MODE) mouse_report.x = ptp_report.xDelta; mouse_report.y = ptp_report.yDelta; @@ -398,56 +398,130 @@ report_mouse_t navigator_trackpad_get_report(report_mouse_t mouse_report) { mouse_report.h = ptp_report.panDelta; mouse_report.buttons = ptp_report.buttons; #endif + #if defined(NAVIGATOR_TRACKPAD_PTP_MODE) - if (!prev_ptp_flag && ptp_report.fingers[0].tip) { // Beginning of a motion - prev_ptp_x = ptp_report.fingers[0].x; - prev_ptp_y = ptp_report.fingers[0].y; - prev_ptp_flag = true; - tap_timer = timer_read(); - in_motion = false; - } else if (!ptp_report.fingers[0].tip) { // End of a motion - prev_ptp_flag = false; - if (in_motion == false) { // Register a tap or double tap - if (last_finger_count == 2) { + uint8_t fingers = finger_count(&ptp_report); + bool is_touching = ptp_report.fingers[0].tip; + bool was_idle = (gesture.state == TP_IDLE); + + // Handle finger down - record start position (regardless of current state) + if (is_touching && was_idle) { + gesture.touch_start_time = timer_read(); + gesture.prev_x = ptp_report.fingers[0].x; + gesture.prev_y = ptp_report.fingers[0].y; + gesture.settled_x = 0; // Will be set after settle time + gesture.settled_y = 0; + gesture.settled = false; + gesture.max_finger_count = fingers; + gesture.state = TP_MOVING; + } + + // Handle finger up - evaluate tap at lift time (libinput style) + if (!is_touching && !was_idle) { + uint16_t duration = timer_elapsed(gesture.touch_start_time); + + // Calculate distance from settled position (or treat as no movement if never settled) + int32_t dist_sq = 0; + if (gesture.settled) { + int16_t dx = gesture.prev_x - gesture.settled_x; + int16_t dy = gesture.prev_y - gesture.settled_y; + dist_sq = (int32_t)dx * dx + (int32_t)dy * dy; + } + +# ifdef NAVIGATOR_TRACKPAD_GESTURE_DEBUG + printf("LIFT: state=%d, fingers=%d, dist_sq=%ld, duration=%dms, settled=%d\n", + gesture.state, gesture.max_finger_count, (long)dist_sq, duration, gesture.settled); +# endif + + // Check tap conditions: short duration AND small movement from settled position + bool is_tap = (duration <= NAVIGATOR_TRACKPAD_TAP_TIMEOUT) && + (dist_sq <= NAVIGATOR_TRACKPAD_TAP_MOVE_THRESHOLD); + + if (is_tap) { # ifdef NAVIGATOR_TRACKPAD_ENABLE_DOUBLE_TAP - mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, true, POINTING_DEVICE_BUTTON2); + if (gesture.max_finger_count >= 2) { +# ifdef NAVIGATOR_TRACKPAD_GESTURE_DEBUG + printf(" -> RIGHT CLICK\n"); +# endif + mouse_report.x = 0; + mouse_report.y = 0; + mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, true, POINTING_DEVICE_BUTTON2); + gesture.pending_click = true; + } else # endif - } else if (last_finger_count == 1) { # ifdef NAVIGATOR_TRACKPAD_ENABLE_TAP - mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, true, POINTING_DEVICE_BUTTON1); + if (gesture.max_finger_count == 1) { +# ifdef NAVIGATOR_TRACKPAD_GESTURE_DEBUG + printf(" -> LEFT CLICK\n"); +# endif + mouse_report.x = 0; + mouse_report.y = 0; + mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, true, POINTING_DEVICE_BUTTON1); + gesture.pending_click = true; + } # endif - } - prev_tap_clear = true; } +# ifdef NAVIGATOR_TRACKPAD_GESTURE_DEBUG + else { + printf(" -> NO TAP (duration=%d/%d, dist=%ld/%d)\n", + duration, NAVIGATOR_TRACKPAD_TAP_TIMEOUT, + (long)dist_sq, NAVIGATOR_TRACKPAD_TAP_MOVE_THRESHOLD); + } +# endif + + gesture.state = TP_IDLE; set_scrolling = false; - } else { - ptp_delta_x = ptp_report.fingers[0].x - prev_ptp_x; - ptp_delta_y = ptp_report.fingers[0].y - prev_ptp_y; + } - if (timer_elapsed(tap_timer) >= NAVIGATOR_TRACKPAD_TAP_DEBOUNCE) { - if (finger_count(&ptp_report) > 1) { // More than one finger, return scroll motions - set_scrolling = true; - } - - if (ptp_delta_x < 0) { - mouse_report.x = -powf(-ptp_delta_x, 1.2); - } else { - mouse_report.x = powf(ptp_delta_x, 1.2); - } - if (ptp_delta_y < 0) { - mouse_report.y = -powf(-ptp_delta_y, 1.2); - } else { - mouse_report.y = powf(ptp_delta_y, 1.2); - } - - prev_ptp_x = ptp_report.fingers[0].x; - prev_ptp_y = ptp_report.fingers[0].y; - - in_motion = true; + // Handle ongoing touch - movement and scrolling + if (is_touching && !was_idle) { + // Track max fingers during gesture + if (fingers > gesture.max_finger_count) { + gesture.max_finger_count = fingers; } - last_finger_count = ptp_report.contact_count; + + // Determine mode based on finger count + if (fingers >= 2 && gesture.state != TP_SCROLLING) { + gesture.state = TP_SCROLLING; + set_scrolling = true; + } + + uint16_t duration = timer_elapsed(gesture.touch_start_time); + + // Record settled position once settle time elapses + if (!gesture.settled && duration >= NAVIGATOR_TRACKPAD_TAP_SETTLE_TIME) { + gesture.settled = true; + gesture.settled_x = ptp_report.fingers[0].x; + gesture.settled_y = ptp_report.fingers[0].y; + } + + // Check if we should suppress movement (might still be a tap) + int32_t dist_sq = 0; + if (gesture.settled) { + int16_t dx = ptp_report.fingers[0].x - gesture.settled_x; + int16_t dy = ptp_report.fingers[0].y - gesture.settled_y; + dist_sq = (int32_t)dx * dx + (int32_t)dy * dy; + } + + // Only report movement if: timeout exceeded OR moved beyond tap threshold (after settling) + bool should_move = (duration > NAVIGATOR_TRACKPAD_TAP_TIMEOUT) || + (gesture.settled && dist_sq > NAVIGATOR_TRACKPAD_TAP_MOVE_THRESHOLD); + + if (should_move) { + int16_t delta_x = ptp_report.fingers[0].x - gesture.prev_x; + int16_t delta_y = ptp_report.fingers[0].y - gesture.prev_y; + + if (delta_x != 0 || delta_y != 0) { + mouse_report.x = (delta_x < 0) ? -powf(-delta_x, 1.2) : powf(delta_x, 1.2); + mouse_report.y = (delta_y < 0) ? -powf(-delta_y, 1.2) : powf(delta_y, 1.2); + } + } + + gesture.prev_x = ptp_report.fingers[0].x; + gesture.prev_y = ptp_report.fingers[0].y; } #endif + has_motion = 0; return mouse_report; } diff --git a/drivers/sensors/navigator_trackpad.h b/drivers/sensors/navigator_trackpad.h index f4d499bbff..171636a47d 100644 --- a/drivers/sensors/navigator_trackpad.h +++ b/drivers/sensors/navigator_trackpad.h @@ -31,6 +31,21 @@ #define NAVIGATOR_TRACKPAD_TAPPING_TERM 100 #define NAVIGATOR_TRACKPAD_TAP_DEBOUNCE 100 +#ifndef NAVIGATOR_TRACKPAD_TAP_MOVE_THRESHOLD +# define NAVIGATOR_TRACKPAD_TAP_MOVE_THRESHOLD 100 // Max movement (squared) before tap becomes a drag +#endif + +#ifndef NAVIGATOR_TRACKPAD_TAP_TIMEOUT +# define NAVIGATOR_TRACKPAD_TAP_TIMEOUT 200 // Max duration (ms) for a tap +#endif + +#ifndef NAVIGATOR_TRACKPAD_TAP_SETTLE_TIME +# define NAVIGATOR_TRACKPAD_TAP_SETTLE_TIME 30 // Ignore movement during initial contact (ms) +#endif + +// Uncomment to enable gesture debug output +// #define NAVIGATOR_TRACKPAD_GESTURE_DEBUG + #ifndef NAVIGATOR_TRACKPAD_ADDRESS # define NAVIGATOR_TRACKPAD_ADDRESS 0x58 #endif @@ -103,6 +118,25 @@ typedef struct { uint8_t contact_count; uint8_t buttons; } cgen6_report_t; + +// Trackpad gesture state machine +typedef enum { + TP_IDLE, // No fingers touching + TP_MOVING, // One finger movement = mouse cursor + TP_SCROLLING, // Two finger movement = scroll +} trackpad_state_t; + +typedef struct { + trackpad_state_t state; + uint16_t touch_start_time; // When finger first touched + uint16_t settled_x; // Position after settle time (for tap threshold) + uint16_t settled_y; + uint16_t prev_x; // Previous position (for delta calculation) + uint16_t prev_y; + uint8_t max_finger_count; // Max fingers seen during this gesture + bool settled; // Has the settle time elapsed? + bool pending_click; // Need to send a click release next cycle +} trackpad_gesture_t; #endif #if defined(NAVIGATOR_TRACKPAD_ABSOLUTE_MODE)