use crate::types::*; #[derive(Clone, Copy, Debug, PartialEq)] pub enum GestureType { None, ScrollBegin, ScrollUpdate { dx: f64, dy: f64 }, ScrollEnd, PinchBegin, PinchUpdate { scale: f64 }, PinchEnd, SwipeBegin { finger_count: u32 }, SwipeUpdate { dx: f64, dy: f64, finger_count: u32 }, SwipeEnd, Tap { finger_count: u32 }, } #[derive(Clone, Debug)] pub struct GestureEvent { pub gesture_type: GestureType, pub timestamp_us: u64, } #[derive(Clone, Copy, Default)] struct FingerState { x: i32, y: i32, touching: bool, start_x: i32, start_y: i32, } const MAX_FINGERS: usize = 5; const TAP_THRESHOLD_PX: i32 = 20; const TAP_MAX_DURATION_US: u64 = 300_000; const SWIPE_THRESHOLD_PX: i32 = 50; pub struct GestureRecognizer { fingers: [FingerState; MAX_FINGERS], prev_fingers: [FingerState; MAX_FINGERS], gesture_active: GestureType, finger_count_at_start: u32, start_timestamp: u64, pinch_initial_dist: f64, } impl GestureRecognizer { pub fn new() -> Self { GestureRecognizer { fingers: [FingerState::default(); MAX_FINGERS], prev_fingers: [FingerState::default(); MAX_FINGERS], gesture_active: GestureType::None, finger_count_at_start: 0, start_timestamp: 0, pinch_initial_dist: 0.0, } } pub fn update_slot( &mut self, slot: usize, x: i32, y: i32, touching: bool, timestamp_us: u64, ) -> Vec { if slot >= MAX_FINGERS { return Vec::new(); } let was_touching = self.fingers[slot].touching; self.prev_fingers[slot] = self.fingers[slot]; self.fingers[slot].x = x; self.fingers[slot].y = y; self.fingers[slot].touching = touching; if touching && !was_touching { self.fingers[slot].start_x = x; self.fingers[slot].start_y = y; self.fingers[slot].x = x; self.fingers[slot].y = y; let count = self.active_finger_count(); if count == 1 { self.finger_count_at_start = 1; self.start_timestamp = timestamp_us; } else if count >= 2 { self.finger_count_at_start = count; self.start_timestamp = timestamp_us; } } if !touching && was_touching { let duration = timestamp_us.saturating_sub(self.start_timestamp); let dx = self.fingers[slot].x - self.fingers[slot].start_x; let dy = self.fingers[slot].y - self.fingers[slot].start_y; let distance = ((dx * dx + dy * dy) as f64).sqrt(); if distance < TAP_THRESHOLD_PX as f64 && duration < TAP_MAX_DURATION_US && self.gesture_active == GestureType::None { let remaining = self.active_finger_count(); if remaining == 0 { let fc = self.finger_count_at_start.max(1); self.finger_count_at_start = 0; return vec![GestureEvent { gesture_type: GestureType::Tap { finger_count: fc }, timestamp_us, }]; } } if self.gesture_active != GestureType::None { let end = self.end_gesture(timestamp_us); self.finger_count_at_start = 0; return end; } self.finger_count_at_start = 0; } if touching && self.active_finger_count() >= 2 { return self.detect_multi_finger_update(timestamp_us); } Vec::new() } fn active_finger_count(&self) -> u32 { self.fingers.iter().filter(|f| f.touching).count() as u32 } fn detect_multi_finger_update(&mut self, timestamp_us: u64) -> Vec { let active: Vec = self .fingers .iter() .enumerate() .filter(|(_, f)| f.touching) .map(|(i, _)| i) .collect(); if active.len() < 2 { return Vec::new(); } match self.gesture_active { GestureType::None => { let count = active.len() as u32; if count >= 3 { self.gesture_active = GestureType::SwipeBegin { finger_count: count }; self.start_timestamp = timestamp_us; self.pinch_initial_dist = self.finger_pair_distance(active[0], active[1]); return vec![GestureEvent { gesture_type: GestureType::SwipeBegin { finger_count: count }, timestamp_us, }]; } if count == 2 { let dx0 = self.fingers[active[0]].x - self.prev_fingers[active[0]].x; let dy0 = self.fingers[active[0]].y - self.prev_fingers[active[0]].y; let dx1 = self.fingers[active[1]].x - self.prev_fingers[active[1]].x; let dy1 = self.fingers[active[1]].y - self.prev_fingers[active[1]].y; let avg_dx = (dx0 + dx1) as f64 / 2.0; let avg_dy = (dy0 + dy1) as f64 / 2.0; let movement = (avg_dx * avg_dx + avg_dy * avg_dy).sqrt(); let current_dist = self.finger_pair_distance(active[0], active[1]); let dist_change = if self.pinch_initial_dist > 0.0 { ((current_dist - self.pinch_initial_dist) / self.pinch_initial_dist).abs() } else { 0.0 }; if dist_change > 0.05 { self.gesture_active = GestureType::PinchBegin; self.start_timestamp = timestamp_us; self.pinch_initial_dist = current_dist; return vec![GestureEvent { gesture_type: GestureType::PinchBegin, timestamp_us, }]; } if movement > 2.0 { self.gesture_active = GestureType::ScrollBegin; self.start_timestamp = timestamp_us; return vec![GestureEvent { gesture_type: GestureType::ScrollBegin, timestamp_us, }]; } } } GestureType::ScrollBegin | GestureType::ScrollUpdate { .. } => { if active.len() >= 2 { let dx0 = self.fingers[active[0]].x - self.prev_fingers[active[0]].x; let dy0 = self.fingers[active[0]].y - self.prev_fingers[active[0]].y; let dx1 = self.fingers[active[1]].x - self.prev_fingers[active[1]].x; let dy1 = self.fingers[active[1]].y - self.prev_fingers[active[1]].y; let avg_dx = (dx0 + dx1) as f64 / 2.0; let avg_dy = (dy0 + dy1) as f64 / 2.0; self.gesture_active = GestureType::ScrollUpdate { dx: avg_dx, dy: avg_dy, }; return vec![GestureEvent { gesture_type: GestureType::ScrollUpdate { dx: avg_dx, dy: avg_dy, }, timestamp_us, }]; } } GestureType::PinchBegin | GestureType::PinchUpdate { .. } => { if active.len() >= 2 { let current_dist = self.finger_pair_distance(active[0], active[1]); let scale = if self.pinch_initial_dist > 0.0 { current_dist / self.pinch_initial_dist } else { 1.0 }; self.gesture_active = GestureType::PinchUpdate { scale }; return vec![GestureEvent { gesture_type: GestureType::PinchUpdate { scale }, timestamp_us, }]; } } GestureType::SwipeBegin { finger_count } | GestureType::SwipeUpdate { finger_count, .. } => { if !active.is_empty() { let first = active[0]; let dx = self.fingers[first].x - self.prev_fingers[first].x; let dy = self.fingers[first].y - self.prev_fingers[first].y; let fc = match self.gesture_active { GestureType::SwipeBegin { finger_count: fc } | GestureType::SwipeUpdate { finger_count: fc, .. } => fc, _ => finger_count, }; self.gesture_active = GestureType::SwipeUpdate { dx: dx as f64, dy: dy as f64, finger_count: fc, }; return vec![GestureEvent { gesture_type: GestureType::SwipeUpdate { dx: dx as f64, dy: dy as f64, finger_count: fc, }, timestamp_us, }]; } } _ => {} } Vec::new() } fn end_gesture(&mut self, timestamp_us: u64) -> Vec { let end_type = match self.gesture_active { GestureType::ScrollBegin | GestureType::ScrollUpdate { .. } => GestureType::ScrollEnd, GestureType::PinchBegin | GestureType::PinchUpdate { .. } => GestureType::PinchEnd, GestureType::SwipeBegin { .. } | GestureType::SwipeUpdate { .. } => GestureType::SwipeEnd, _ => GestureType::None, }; self.gesture_active = GestureType::None; if end_type == GestureType::None { Vec::new() } else { vec![GestureEvent { gesture_type: end_type, timestamp_us, }] } } fn finger_pair_distance(&self, a: usize, b: usize) -> f64 { let dx = self.fingers[a].x - self.fingers[b].x; let dy = self.fingers[a].y - self.fingers[b].y; ((dx * dx + dy * dy) as f64).sqrt() } pub fn reset(&mut self) { self.fingers = [FingerState::default(); MAX_FINGERS]; self.prev_fingers = [FingerState::default(); MAX_FINGERS]; self.gesture_active = GestureType::None; self.finger_count_at_start = 0; self.start_timestamp = 0; self.pinch_initial_dist = 0.0; } }