fix(surface): a better tap / double-tap detection approach with the latest trackpad surface
Some checks failed
Build firmware / build-firmware (default) (push) Failing after 4s
Build firmware / build-firmware (oryx) (push) Failing after 2s

This commit is contained in:
Florian Didron
2025-11-24 12:08:36 +07:00
parent 96c8423509
commit 178bb9b426
2 changed files with 160 additions and 52 deletions

View File

@@ -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;
}

View File

@@ -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)