55// Converts Cirque Gen 6 sensor data to Windows Precision Touchpad HID reports
66// Also provides a fallback mouse collection for systems that don't support PTP
77
8+ #include <math.h>
89#include "navigator_trackpad_ptp.h"
910#include "navigator_trackpad_common.h"
1011#include "precision_trackpad_drivers.h"
@@ -43,16 +44,19 @@ extern uint8_t get_trackpad_input_mode(void);
4344
4445// Tap-to-click configuration
4546#ifndef TRACKPAD_TAP_TERM_MS
46- # define TRACKPAD_TAP_TERM_MS 150 // Maximum duration for a tap (ms)
47+ # define TRACKPAD_TAP_TERM_MS 200 // Maximum duration for a tap (ms)
4748#endif
4849
49- #ifndef TRACKPAD_TAP_MOVE_THRESHOLD
50- # define TRACKPAD_TAP_MOVE_THRESHOLD 30 // Maximum movement during tap (in sensor units, ~1.5% of range)
50+ #ifndef TRACKPAD_TAP_MOVE_THRESHOLD_SQ
51+ # define TRACKPAD_TAP_MOVE_THRESHOLD_SQ 100 // Max movement squared before tap becomes drag
5152#endif
5253
53- // Per-frame movement threshold squared for tap invalidation (~4 sensor units)
54- #ifndef TRACKPAD_TAP_FRAME_MOVE_THRESHOLD_SQ
55- # define TRACKPAD_TAP_FRAME_MOVE_THRESHOLD_SQ 16
54+ #ifndef TRACKPAD_TAP_SETTLE_TIME_MS
55+ # define TRACKPAD_TAP_SETTLE_TIME_MS 30 // Ignore movement during initial contact (ms)
56+ #endif
57+
58+ #ifndef TRACKPAD_MAX_DELTA
59+ # define TRACKPAD_MAX_DELTA 250 // Max allowed delta per frame to prevent jumps
5660#endif
5761
5862#if defined(NAVIGATOR_TRACKPAD_PTP_MODE )
@@ -74,18 +78,21 @@ static struct {
7478 bool tracking ;
7579 uint16_t last_x ;
7680 uint16_t last_y ;
77- // Tap detection
78- bool tap_pending ;
81+ // Tap detection - uses settled position like mouse mode
7982 uint32_t touch_start_time ;
80- uint16_t touch_start_x ;
81- uint16_t touch_start_y ;
82- // Click state
83- bool click_active ;
84- uint32_t click_release_time ;
83+ uint16_t settled_x ;
84+ uint16_t settled_y ;
85+ bool settled ;
86+ bool is_drag ; // Set when movement exceeds tap threshold - enables cursor movement
87+ // Click state - pending_release triggers button release on next cycle
88+ bool pending_release ;
8589 // Previous state for change detection
8690 uint8_t prev_buttons ;
8791} mouse_state = {0 };
8892
93+ // Track input mode to detect changes
94+ static uint8_t prev_input_mode = TRACKPAD_INPUT_MODE_PTP ;
95+
8996// Send fallback mouse report
9097static void send_mouse_report (int8_t dx , int8_t dy , uint8_t buttons ) {
9198 report_trackpad_mouse_t report = {
@@ -98,25 +105,42 @@ static void send_mouse_report(int8_t dx, int8_t dy, uint8_t buttons) {
98105 send_trackpad ((report_digitizer_t * )& report );
99106}
100107
108+ // Reset mouse state when mode changes to avoid stale timers/state
109+ static void reset_mouse_state (void ) {
110+ // Send release if button was pressed
111+ if (mouse_state .prev_buttons != 0 || mouse_state .pending_release ) {
112+ send_mouse_report (0 , 0 , 0 );
113+ }
114+ mouse_state .tracking = false;
115+ mouse_state .touch_start_time = 0 ;
116+ mouse_state .settled = false;
117+ mouse_state .is_drag = false;
118+ mouse_state .pending_release = false;
119+ mouse_state .prev_buttons = 0 ;
120+ }
121+
101122// Clamp value to int8_t range
102123static inline int8_t clamp_to_int8 (int32_t value ) {
103124 if (value > 127 ) return 127 ;
104125 if (value < -127 ) return -127 ;
105126 return (int8_t )value ;
106127}
107128
108- // Minimum time to hold tap-click before releasing (ms)
109- #ifndef TRACKPAD_TAP_CLICK_HOLD_MS
110- # define TRACKPAD_TAP_CLICK_HOLD_MS 20
111- #endif
112-
113129// Process fallback mouse movement and tap-to-click
130+ // Uses settle time and movement suppression like navigator_trackpad_mouse for smooth operation
114131static void process_fallback_mouse (cgen6_report_t * sensor_report , bool finger_down , bool prev_finger_down ) {
115- uint32_t now = timer_read32 ();
116132 int8_t dx = 0 ;
117133 int8_t dy = 0 ;
118134 uint8_t buttons = 0 ;
119135
136+ // Handle pending click release from previous cycle (like mouse mode)
137+ if (mouse_state .pending_release ) {
138+ mouse_state .pending_release = false;
139+ send_mouse_report (0 , 0 , 0 );
140+ mouse_state .prev_buttons = 0 ;
141+ // Continue processing - don't return early so we handle any new movement
142+ }
143+
120144 // Handle physical button (from sensor)
121145 if (sensor_report -> buttons & BUTTON_PRIMARY ) {
122146 buttons |= BUTTON_PRIMARY ;
@@ -127,40 +151,61 @@ static void process_fallback_mouse(cgen6_report_t *sensor_report, bool finger_do
127151 mouse_state .tracking = true;
128152 mouse_state .last_x = sensor_report -> fingers [0 ].x ;
129153 mouse_state .last_y = sensor_report -> fingers [0 ].y ;
130- // Start tap detection
131- mouse_state .tap_pending = true;
132- mouse_state .touch_start_time = now ;
133- mouse_state .touch_start_x = sensor_report -> fingers [0 ].x ;
134- mouse_state .touch_start_y = sensor_report -> fingers [0 ].y ;
154+ // Start tap detection with settle time approach
155+ mouse_state .touch_start_time = timer_read32 ();
156+ mouse_state .settled = false;
157+ mouse_state .settled_x = 0 ;
158+ mouse_state .settled_y = 0 ;
159+ mouse_state .is_drag = false;
135160 }
136161
137- // Handle finger movement (compute delta)
162+ // Handle finger movement
138163 if (finger_down && mouse_state .tracking ) {
139- int32_t raw_dx = (int32_t )sensor_report -> fingers [0 ].x - (int32_t )mouse_state .last_x ;
140- int32_t raw_dy = (int32_t )sensor_report -> fingers [0 ].y - (int32_t )mouse_state .last_y ;
141-
142- // Check if movement exceeded tap threshold BEFORE applying sensitivity
143- // This uses distance from initial touch point
144- if (mouse_state .tap_pending ) {
145- int32_t move_x = (int32_t )sensor_report -> fingers [0 ].x - (int32_t )mouse_state .touch_start_x ;
146- int32_t move_y = (int32_t )sensor_report -> fingers [0 ].y - (int32_t )mouse_state .touch_start_y ;
147- int32_t move_dist_sq = move_x * move_x + move_y * move_y ;
148- if (move_dist_sq > TRACKPAD_TAP_MOVE_THRESHOLD * TRACKPAD_TAP_MOVE_THRESHOLD ) {
149- mouse_state .tap_pending = false;
150- }
151- // Also invalidate if any single-frame movement is significant
152- if (raw_dx * raw_dx + raw_dy * raw_dy > TRACKPAD_TAP_FRAME_MOVE_THRESHOLD_SQ ) {
153- mouse_state .tap_pending = false;
164+ uint32_t duration = timer_elapsed32 (mouse_state .touch_start_time );
165+
166+ // Record settled position once settle time elapses (for tap detection)
167+ if (!mouse_state .settled && duration >= TRACKPAD_TAP_SETTLE_TIME_MS ) {
168+ mouse_state .settled = true;
169+ mouse_state .settled_x = sensor_report -> fingers [0 ].x ;
170+ mouse_state .settled_y = sensor_report -> fingers [0 ].y ;
171+ }
172+
173+ // Check if movement from settled position exceeds tap threshold → becomes a drag
174+ if (mouse_state .settled && !mouse_state .is_drag ) {
175+ int16_t move_x = (int16_t )sensor_report -> fingers [0 ].x - (int16_t )mouse_state .settled_x ;
176+ int16_t move_y = (int16_t )sensor_report -> fingers [0 ].y - (int16_t )mouse_state .settled_y ;
177+ int32_t dist_sq = (int32_t )move_x * move_x + (int32_t )move_y * move_y ;
178+ if (dist_sq > TRACKPAD_TAP_MOVE_THRESHOLD_SQ ) {
179+ mouse_state .is_drag = true;
154180 }
155181 }
156182
157- // Apply sensitivity scaling
158- raw_dx = (int32_t )(raw_dx * TRACKPAD_MOUSE_SENSITIVITY );
159- raw_dy = (int32_t )(raw_dy * TRACKPAD_MOUSE_SENSITIVITY );
183+ // Only report movement once we've determined this is a drag (not a tap)
184+ if (mouse_state .is_drag ) {
185+ int16_t raw_dx = (int16_t )sensor_report -> fingers [0 ].x - (int16_t )mouse_state .last_x ;
186+ int16_t raw_dy = (int16_t )sensor_report -> fingers [0 ].y - (int16_t )mouse_state .last_y ;
187+
188+ // Clamp deltas to prevent jumps from bad sensor data
189+ if (raw_dx > TRACKPAD_MAX_DELTA ) raw_dx = TRACKPAD_MAX_DELTA ;
190+ if (raw_dx < - TRACKPAD_MAX_DELTA ) raw_dx = - TRACKPAD_MAX_DELTA ;
191+ if (raw_dy > TRACKPAD_MAX_DELTA ) raw_dy = TRACKPAD_MAX_DELTA ;
192+ if (raw_dy < - TRACKPAD_MAX_DELTA ) raw_dy = - TRACKPAD_MAX_DELTA ;
160193
161- dx = clamp_to_int8 (raw_dx );
162- dy = clamp_to_int8 (raw_dy );
194+ if (raw_dx != 0 || raw_dy != 0 ) {
195+ // Apply exponential acceleration for smooth cursor feel (like mouse mode)
196+ float acc_dx = (raw_dx < 0 ) ? - powf (- raw_dx , 1.2f ) : powf (raw_dx , 1.2f );
197+ float acc_dy = (raw_dy < 0 ) ? - powf (- raw_dy , 1.2f ) : powf (raw_dy , 1.2f );
163198
199+ // Apply sensitivity scaling
200+ acc_dx *= TRACKPAD_MOUSE_SENSITIVITY ;
201+ acc_dy *= TRACKPAD_MOUSE_SENSITIVITY ;
202+
203+ dx = clamp_to_int8 ((int32_t )acc_dx );
204+ dy = clamp_to_int8 ((int32_t )acc_dy );
205+ }
206+ }
207+
208+ // Always update last position for delta calculation
164209 mouse_state .last_x = sensor_report -> fingers [0 ].x ;
165210 mouse_state .last_y = sensor_report -> fingers [0 ].y ;
166211 }
@@ -169,25 +214,19 @@ static void process_fallback_mouse(cgen6_report_t *sensor_report, bool finger_do
169214 if (!finger_down && prev_finger_down ) {
170215 mouse_state .tracking = false;
171216
172- // Check if this was a tap
173- if (mouse_state .tap_pending ) {
174- uint32_t touch_duration = timer_elapsed32 (mouse_state .touch_start_time );
175- if (touch_duration <= TRACKPAD_TAP_TERM_MS ) {
176- // Valid tap - generate click
177- mouse_state .click_active = true;
178- mouse_state .click_release_time = now ;
179- }
180- }
181- mouse_state .tap_pending = false;
182- }
217+ uint32_t touch_duration = timer_elapsed32 (mouse_state .touch_start_time );
183218
184- // Handle tap-generated click (button press then release after hold time)
185- if (mouse_state .click_active ) {
186- buttons |= BUTTON_PRIMARY ;
187- // Release after holding for TRACKPAD_TAP_CLICK_HOLD_MS
188- if (timer_elapsed32 (mouse_state .click_release_time ) >= TRACKPAD_TAP_CLICK_HOLD_MS ) {
189- mouse_state .click_active = false;
219+ // Tap conditions: not a drag AND short duration
220+ bool is_tap = !mouse_state .is_drag && (touch_duration <= TRACKPAD_TAP_TERM_MS );
221+
222+ if (is_tap ) {
223+ // Valid tap - send click press, release will happen next cycle
224+ send_mouse_report (0 , 0 , BUTTON_PRIMARY );
225+ mouse_state .prev_buttons = BUTTON_PRIMARY ;
226+ mouse_state .pending_release = true;
190227 }
228+
229+ mouse_state .settled = false;
191230 }
192231
193232 // Only send report if there's actual movement or button state changed
@@ -292,8 +331,13 @@ static bool navigator_trackpad_ptp_task(void) {
292331 // Contact count (bits 0-3) + buttons (bits 4-6)
293332 report [PTP_COUNT_BUTTONS_OFFSET ] = (contact_count & 0x0F ) | ((buttons & BUTTON_PRIMARY ) << 4 );
294333
295- // Get current input mode
334+ // Get current input mode and handle mode changes
296335 uint8_t input_mode = get_trackpad_input_mode ();
336+ if (input_mode != prev_input_mode ) {
337+ // Mode changed - reset mouse state to avoid stale timers/state
338+ reset_mouse_state ();
339+ prev_input_mode = input_mode ;
340+ }
297341
298342 // Send PTP report only in PTP mode (mode 3)
299343 if (input_mode == TRACKPAD_INPUT_MODE_PTP ) {
0 commit comments