Skip to main content

kobo_core/device/
touch.rs

1//! Touch->display transform + gesture predicates.
2//!
3//! The transform uses per-device flags from `DeviceConfig`:
4//! - `touch_switch_xy`: swap X and Y axes
5//! - `touch_mirrored_x`: mirror the horizontal axis
6//! - `touch_mirrored_y`: mirror the vertical axis
7
8pub const SWIPE_MIN_DX: f32 = 80.0;
9pub const SWIPE_MAX_MS: u128 = 500;
10pub const DOUBLE_TAP_MAX_PRESS_MS: u128 = 300;
11pub const DOUBLE_TAP_WINDOW_MS: u128 = 400;
12
13/// Per-device touch configuration.
14#[derive(Clone, Copy)]
15pub struct TouchConfig {
16    pub switch_xy: bool,
17    pub mirrored_x: bool,
18    pub mirrored_y: bool,
19    pub raw_x_max: i32,
20    pub raw_y_max: i32,
21    pub screen_w: i32,
22    pub screen_h: i32,
23}
24
25/// Raw evdev -> display-space transform.
26pub fn to_display(rx: i32, ry: i32, cfg: &TouchConfig) -> (f32, f32) {
27    let (mut x, mut y) = if cfg.switch_xy { (ry, rx) } else { (rx, ry) };
28
29    if cfg.mirrored_x {
30        let max = if cfg.switch_xy {
31            cfg.raw_y_max
32        } else {
33            cfg.raw_x_max
34        };
35        x = max - x;
36    }
37    if cfg.mirrored_y {
38        let max = if cfg.switch_xy {
39            cfg.raw_x_max
40        } else {
41            cfg.raw_y_max
42        };
43        y = max - y;
44    }
45
46    (x as f32, y as f32)
47}
48
49#[allow(dead_code)]
50pub fn is_swipe(swipe_dx: f32, swipe_dy: f32, dt_ms: u128) -> bool {
51    swipe_dx.abs() > SWIPE_MIN_DX && dt_ms < SWIPE_MAX_MS && swipe_dx.abs() > swipe_dy.abs()
52}
53
54pub fn is_double_tap(dt_ms: u128, since_prev_tap_ms: u128) -> bool {
55    dt_ms < DOUBLE_TAP_MAX_PRESS_MS && since_prev_tap_ms < DOUBLE_TAP_WINDOW_MS
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    // Libra Colour cyttsp5_mt: switch_xy=true, mirrored_x=true
63    fn libra_cfg() -> TouchConfig {
64        TouchConfig {
65            switch_xy: true,
66            mirrored_x: true,
67            mirrored_y: false,
68            raw_x_max: 1447,
69            raw_y_max: 1071,
70            screen_w: 1072,
71            screen_h: 1448,
72        }
73    }
74
75    // Standard touch panel: no swap, no mirror
76    fn plain_cfg() -> TouchConfig {
77        TouchConfig {
78            switch_xy: false,
79            mirrored_x: false,
80            mirrored_y: false,
81            raw_x_max: 1071,
82            raw_y_max: 1447,
83            screen_w: 1072,
84            screen_h: 1448,
85        }
86    }
87
88    #[test]
89    fn libra_left_edge_maps_small_x() {
90        let cfg = libra_cfg();
91        let (dx, _) = to_display(736, 1035, &cfg);
92        assert!(
93            dx < 100.0,
94            "left-edge tap must map to small display_x, got {dx}"
95        );
96    }
97
98    #[test]
99    fn libra_right_edge_maps_large_x() {
100        let cfg = libra_cfg();
101        let (dx, _) = to_display(693, 36, &cfg);
102        assert!(
103            dx > 970.0,
104            "right-edge tap must map to large display_x, got {dx}"
105        );
106    }
107
108    #[test]
109    fn plain_passes_through() {
110        let cfg = plain_cfg();
111        let (dx, dy) = to_display(100, 200, &cfg);
112        assert_eq!(dx, 100.0);
113        assert_eq!(dy, 200.0);
114    }
115
116    #[test]
117    fn plain_mirrored_x() {
118        let mut cfg = plain_cfg();
119        cfg.mirrored_x = true;
120        let (dx, _) = to_display(0, 0, &cfg);
121        assert_eq!(dx, cfg.raw_x_max as f32);
122    }
123
124    #[test]
125    fn is_swipe_detects_fast_horizontal_flick() {
126        assert!(is_swipe(200.0, 5.0, 120));
127        assert!(is_swipe(-250.0, 40.0, 200));
128    }
129
130    #[test]
131    fn is_swipe_rejects_small_or_slow_or_vertical() {
132        assert!(!is_swipe(20.0, 5.0, 300));
133        assert!(!is_swipe(200.0, 5.0, 600));
134        assert!(!is_swipe(100.0, 300.0, 100));
135    }
136
137    #[test]
138    fn is_double_tap_within_window() {
139        assert!(is_double_tap(150, 200));
140        assert!(!is_double_tap(400, 200));
141        assert!(!is_double_tap(150, 500));
142    }
143}