Files
RedBear-OS/local/recipes/system/evdevd/source/src/gesture.rs
T
vasilito b9874d0941 feat: USB storage read/write proof + full Red Bear OS tree sync
Add redbear-usb-storage-check in-guest binary that validates USB mass
storage read and write I/O: discovers /scheme/disk/ devices, writes a
test pattern to sector 2048, reads it back, verifies match, restores
original content. Updates test-usb-storage-qemu.sh with write-proof
verification step.

Includes all accumulated Red Bear OS work: kernel patches, relibc
patches, driver infrastructure, DRM/GPU, KDE recipes, firmware,
validation tooling, build system hardening, and documentation.
2026-05-03 23:03:24 +01:00

300 lines
11 KiB
Rust

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<GestureEvent> {
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<GestureEvent> {
let active: Vec<usize> = 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<GestureEvent> {
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;
}
}