Skip to content

Commit 9c1fd41

Browse files
committed
fix(trackpad): improve mousee movements for ptp fallback
1 parent 1829d47 commit 9c1fd41

File tree

1 file changed

+107
-63
lines changed

1 file changed

+107
-63
lines changed

drivers/sensors/navigator_trackpad_ptp.c

Lines changed: 107 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
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
9097
static 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
102123
static 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
114131
static 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

Comments
 (0)