cadmus_core/
gesture.rs

1use crate::device::CURRENT_DEVICE;
2use crate::geom::{elbow, nearest_segment_point, Axis, DiagDir, Dir, Point, Vec2};
3use crate::input::{ButtonCode, ButtonStatus, DeviceEvent, FingerStatus};
4use crate::unit::mm_to_px;
5use crate::view::Event;
6use fxhash::FxHashMap;
7use std::f64;
8use std::fmt;
9use std::sync::mpsc::{self, Receiver, Sender};
10use std::sync::{Arc, Mutex};
11use std::thread;
12use std::time::Duration;
13
14pub const TAP_JITTER_MM: f32 = 6.0;
15pub const HOLD_JITTER_MM: f32 = 1.5;
16pub const HOLD_DELAY_SHORT: Duration = Duration::from_millis(666);
17pub const HOLD_DELAY_LONG: Duration = Duration::from_millis(1333);
18
19#[derive(Debug, Copy, Clone)]
20pub enum GestureEvent {
21    Tap(Point),
22    MultiTap([Point; 2]),
23    Swipe {
24        dir: Dir,
25        start: Point,
26        end: Point,
27    },
28    SlantedSwipe {
29        dir: DiagDir,
30        start: Point,
31        end: Point,
32    },
33    MultiSwipe {
34        dir: Dir,
35        starts: [Point; 2],
36        ends: [Point; 2],
37    },
38    Arrow {
39        dir: Dir,
40        start: Point,
41        end: Point,
42    },
43    MultiArrow {
44        dir: Dir,
45        starts: [Point; 2],
46        ends: [Point; 2],
47    },
48    Corner {
49        dir: DiagDir,
50        start: Point,
51        end: Point,
52    },
53    MultiCorner {
54        dir: DiagDir,
55        starts: [Point; 2],
56        ends: [Point; 2],
57    },
58    Pinch {
59        axis: Axis,
60        center: Point,
61        factor: f32,
62    },
63    Spread {
64        axis: Axis,
65        center: Point,
66        factor: f32,
67    },
68    Rotate {
69        center: Point,
70        quarter_turns: i8,
71        angle: f32,
72    },
73    Cross(Point),
74    Diamond(Point),
75    HoldFingerShort(Point, i32),
76    HoldFingerLong(Point, i32),
77    HoldButtonShort(ButtonCode),
78    HoldButtonLong(ButtonCode),
79}
80
81impl fmt::Display for GestureEvent {
82    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
83        match self {
84            GestureEvent::Tap(pt) => write!(f, "Tap {}", pt),
85            GestureEvent::MultiTap(pts) => write!(f, "Multitap {} {}", pts[0], pts[1]),
86            GestureEvent::Swipe { dir, .. } => write!(f, "Swipe {}", dir),
87            GestureEvent::SlantedSwipe { dir, .. } => write!(f, "SlantedSwipe {}", dir),
88            GestureEvent::MultiSwipe { dir, .. } => write!(f, "Multiswipe {}", dir),
89            GestureEvent::Arrow { dir, .. } => write!(f, "Arrow {}", dir),
90            GestureEvent::MultiArrow { dir, .. } => write!(f, "Multiarrow {}", dir),
91            GestureEvent::Corner { dir, .. } => write!(f, "Corner {}", dir),
92            GestureEvent::MultiCorner { dir, .. } => write!(f, "Multicorner {}", dir),
93            GestureEvent::Pinch {
94                axis,
95                center,
96                factor,
97                ..
98            } => write!(f, "Pinch {} {} {:.2}", axis, center, factor),
99            GestureEvent::Spread {
100                axis,
101                center,
102                factor,
103                ..
104            } => write!(f, "Spread {} {} {:.2}", axis, center, factor),
105            GestureEvent::Rotate {
106                center,
107                quarter_turns,
108                ..
109            } => write!(f, "Rotate {} {}", center, *quarter_turns as i32 * 90),
110            GestureEvent::Cross(pt) => write!(f, "Cross {}", pt),
111            GestureEvent::Diamond(pt) => write!(f, "Diamond {}", pt),
112            GestureEvent::HoldFingerShort(pt, id) => write!(f, "Short-held finger {} {}", id, pt),
113            GestureEvent::HoldFingerLong(pt, id) => write!(f, "Long-held finger {} {}", id, pt),
114            GestureEvent::HoldButtonShort(code) => write!(f, "Short-held button {:?}", code),
115            GestureEvent::HoldButtonLong(code) => write!(f, "Long-held button {:?}", code),
116        }
117    }
118}
119
120#[derive(Debug)]
121pub struct TouchState {
122    time: f64,
123    held: bool,
124    positions: Vec<Point>,
125}
126
127pub fn gesture_events(rx: Receiver<DeviceEvent>) -> Receiver<Event> {
128    let (ty, ry) = mpsc::channel();
129    thread::spawn(move || parse_gesture_events(&rx, &ty));
130    ry
131}
132
133pub fn parse_gesture_events(rx: &Receiver<DeviceEvent>, ty: &Sender<Event>) {
134    let contacts: Arc<Mutex<FxHashMap<i32, TouchState>>> =
135        Arc::new(Mutex::new(FxHashMap::default()));
136    let buttons: Arc<Mutex<FxHashMap<ButtonCode, f64>>> =
137        Arc::new(Mutex::new(FxHashMap::default()));
138    let segments: Arc<Mutex<Vec<Vec<Point>>>> = Arc::new(Mutex::new(Vec::new()));
139    let tap_jitter = mm_to_px(TAP_JITTER_MM, CURRENT_DEVICE.dpi);
140    let hold_jitter = mm_to_px(HOLD_JITTER_MM, CURRENT_DEVICE.dpi);
141
142    while let Ok(evt) = rx.recv() {
143        ty.send(Event::Device(evt)).ok();
144        match evt {
145            DeviceEvent::Finger {
146                status: FingerStatus::Down,
147                position,
148                id,
149                time,
150            } => {
151                let mut ct = contacts.lock().unwrap();
152                ct.insert(
153                    id,
154                    TouchState {
155                        time,
156                        held: false,
157                        positions: vec![position],
158                    },
159                );
160                let ty = ty.clone();
161                let contacts = contacts.clone();
162                let segments = segments.clone();
163                thread::spawn(move || {
164                    let mut held = false;
165                    thread::sleep(HOLD_DELAY_SHORT);
166                    {
167                        let mut ct = contacts.lock().unwrap();
168                        let sg = segments.lock().unwrap();
169                        if ct.len() > 1 || !sg.is_empty() {
170                            return;
171                        }
172                        if let Some(ts) = ct.get(&id) {
173                            let tp = &ts.positions;
174                            if (ts.time - time).abs() < f64::EPSILON
175                                && (tp[tp.len() - 1] - position).length() < hold_jitter
176                                && (tp[tp.len() / 2] - position).length() < hold_jitter
177                            {
178                                held = true;
179                                ty.send(Event::Gesture(GestureEvent::HoldFingerShort(
180                                    position, id,
181                                )))
182                                .ok();
183                            }
184                        }
185                        if held {
186                            if let Some(ts) = ct.get_mut(&id) {
187                                ts.held = true;
188                            }
189                        } else {
190                            return;
191                        }
192                    }
193                    thread::sleep(HOLD_DELAY_LONG - HOLD_DELAY_SHORT);
194                    {
195                        let mut ct = contacts.lock().unwrap();
196                        let sg = segments.lock().unwrap();
197                        if ct.len() > 1 || !sg.is_empty() {
198                            return;
199                        }
200                        if let Some(ts) = ct.get_mut(&id) {
201                            let tp = &ts.positions;
202                            if (ts.time - time).abs() < f64::EPSILON
203                                && (tp[tp.len() - 1] - position).length() < hold_jitter
204                                && (tp[tp.len() / 2] - position).length() < hold_jitter
205                            {
206                                ty.send(Event::Gesture(GestureEvent::HoldFingerLong(position, id)))
207                                    .ok();
208                            }
209                        }
210                    }
211                });
212            }
213            DeviceEvent::Finger {
214                status: FingerStatus::Motion,
215                position,
216                id,
217                ..
218            } => {
219                let mut ct = contacts.lock().unwrap();
220                if let Some(ref mut ts) = ct.get_mut(&id) {
221                    ts.positions.push(position);
222                }
223            }
224            DeviceEvent::Finger {
225                status: FingerStatus::Up,
226                position,
227                id,
228                ..
229            } => {
230                let mut ct = contacts.lock().unwrap();
231                let mut sg = segments.lock().unwrap();
232                if let Some(mut ts) = ct.remove(&id) {
233                    if !ts.held {
234                        ts.positions.push(position);
235                        sg.push(ts.positions);
236                    }
237                }
238                if ct.is_empty() && !sg.is_empty() {
239                    let len = sg.len();
240                    if len == 1 {
241                        ty.send(Event::Gesture(interpret_segment(
242                            &sg.pop().unwrap(),
243                            tap_jitter,
244                        )))
245                        .ok();
246                    } else if len == 2 {
247                        let ge1 = interpret_segment(&sg.pop().unwrap(), tap_jitter);
248                        let ge2 = interpret_segment(&sg.pop().unwrap(), tap_jitter);
249                        match (ge1, ge2) {
250                            (GestureEvent::Tap(c1), GestureEvent::Tap(c2)) => {
251                                ty.send(Event::Gesture(GestureEvent::MultiTap([c1, c2])))
252                                    .ok();
253                            }
254                            (
255                                GestureEvent::Swipe {
256                                    dir: d1,
257                                    start: s1,
258                                    end: e1,
259                                    ..
260                                },
261                                GestureEvent::Swipe {
262                                    dir: d2,
263                                    start: s2,
264                                    end: e2,
265                                    ..
266                                },
267                            ) if d1 == d2 => {
268                                ty.send(Event::Gesture(GestureEvent::MultiSwipe {
269                                    dir: d1,
270                                    starts: [s1, s2],
271                                    ends: [e1, e2],
272                                }))
273                                .ok();
274                            }
275                            (
276                                GestureEvent::Swipe {
277                                    dir: d1,
278                                    start: s1,
279                                    end: e1,
280                                    ..
281                                },
282                                GestureEvent::Swipe {
283                                    dir: d2,
284                                    start: s2,
285                                    end: e2,
286                                    ..
287                                },
288                            ) if d1 == d2.opposite() => {
289                                let center = (s1 + s2) / 2;
290                                let ds = (s2 - s1).length();
291                                let de = (e2 - e1).length();
292                                let factor = de / ds;
293                                if factor < 1.0 {
294                                    ty.send(Event::Gesture(GestureEvent::Pinch {
295                                        axis: d1.axis(),
296                                        center,
297                                        factor,
298                                    }))
299                                    .ok();
300                                } else {
301                                    ty.send(Event::Gesture(GestureEvent::Spread {
302                                        axis: d1.axis(),
303                                        center,
304                                        factor,
305                                    }))
306                                    .ok();
307                                }
308                            }
309                            (
310                                GestureEvent::SlantedSwipe {
311                                    dir: d1,
312                                    start: s1,
313                                    end: e1,
314                                    ..
315                                },
316                                GestureEvent::SlantedSwipe {
317                                    dir: d2,
318                                    start: s2,
319                                    end: e2,
320                                    ..
321                                },
322                            ) if d1 == d2.opposite() => {
323                                let center = (s1 + s2) / 2;
324                                let ds = (s2 - s1).length();
325                                let de = (e2 - e1).length();
326                                let factor = de / ds;
327                                if factor < 1.0 {
328                                    ty.send(Event::Gesture(GestureEvent::Pinch {
329                                        axis: Axis::Diagonal,
330                                        center,
331                                        factor,
332                                    }))
333                                    .ok();
334                                } else {
335                                    ty.send(Event::Gesture(GestureEvent::Spread {
336                                        axis: Axis::Diagonal,
337                                        center,
338                                        factor,
339                                    }))
340                                    .ok();
341                                }
342                            }
343                            (
344                                GestureEvent::Arrow {
345                                    dir: Dir::East,
346                                    start: s1,
347                                    end: e1,
348                                },
349                                GestureEvent::Arrow {
350                                    dir: Dir::West,
351                                    start: s2,
352                                    end: e2,
353                                },
354                            )
355                            | (
356                                GestureEvent::Arrow {
357                                    dir: Dir::West,
358                                    start: s2,
359                                    end: e2,
360                                },
361                                GestureEvent::Arrow {
362                                    dir: Dir::East,
363                                    start: s1,
364                                    end: e1,
365                                },
366                            ) if s1.x < s2.x => {
367                                ty.send(Event::Gesture(GestureEvent::Cross(
368                                    (s1 + e1 + s2 + e2) / 4,
369                                )))
370                                .ok();
371                            }
372                            (
373                                GestureEvent::Arrow {
374                                    dir: Dir::West,
375                                    start: s1,
376                                    end: e1,
377                                },
378                                GestureEvent::Arrow {
379                                    dir: Dir::East,
380                                    start: s2,
381                                    end: e2,
382                                },
383                            )
384                            | (
385                                GestureEvent::Arrow {
386                                    dir: Dir::East,
387                                    start: s2,
388                                    end: e2,
389                                },
390                                GestureEvent::Arrow {
391                                    dir: Dir::West,
392                                    start: s1,
393                                    end: e1,
394                                },
395                            ) if s1.x < s2.x => {
396                                ty.send(Event::Gesture(GestureEvent::Diamond(
397                                    (s1 + e1 + s2 + e2) / 4,
398                                )))
399                                .ok();
400                            }
401                            (
402                                GestureEvent::Arrow {
403                                    dir: d1,
404                                    start: s1,
405                                    end: e1,
406                                },
407                                GestureEvent::Arrow {
408                                    dir: d2,
409                                    start: s2,
410                                    end: e2,
411                                },
412                            ) if d1 == d2 => {
413                                ty.send(Event::Gesture(GestureEvent::MultiArrow {
414                                    dir: d1,
415                                    starts: [s1, s2],
416                                    ends: [e1, e2],
417                                }))
418                                .ok();
419                            }
420                            (
421                                GestureEvent::Corner {
422                                    dir: d1,
423                                    start: s1,
424                                    end: e1,
425                                },
426                                GestureEvent::Corner {
427                                    dir: d2,
428                                    start: s2,
429                                    end: e2,
430                                },
431                            ) if d1 == d2 => {
432                                ty.send(Event::Gesture(GestureEvent::MultiCorner {
433                                    dir: d1,
434                                    starts: [s1, s2],
435                                    ends: [e1, e2],
436                                }))
437                                .ok();
438                            }
439                            (
440                                GestureEvent::Tap(c),
441                                GestureEvent::Swipe {
442                                    start: s, end: e, ..
443                                },
444                            )
445                            | (
446                                GestureEvent::Swipe {
447                                    start: s, end: e, ..
448                                },
449                                GestureEvent::Tap(c),
450                            )
451                            | (
452                                GestureEvent::Tap(c),
453                                GestureEvent::Arrow {
454                                    start: s, end: e, ..
455                                },
456                            )
457                            | (
458                                GestureEvent::Arrow {
459                                    start: s, end: e, ..
460                                },
461                                GestureEvent::Tap(c),
462                            )
463                            | (
464                                GestureEvent::Tap(c),
465                                GestureEvent::Corner {
466                                    start: s, end: e, ..
467                                },
468                            )
469                            | (
470                                GestureEvent::Corner {
471                                    start: s, end: e, ..
472                                },
473                                GestureEvent::Tap(c),
474                            ) => {
475                                // Angle are positive in the counter clockwise direction.
476                                let angle = ((e - c).angle() - (s - c).angle()).to_degrees();
477                                let quarter_turns = (angle / 90.0).round() as i8;
478                                ty.send(Event::Gesture(GestureEvent::Rotate {
479                                    angle,
480                                    quarter_turns,
481                                    center: c,
482                                }))
483                                .ok();
484                            }
485                            _ => (),
486                        }
487                    } else {
488                        sg.clear();
489                    }
490                }
491            }
492            DeviceEvent::Button {
493                status: ButtonStatus::Pressed,
494                code,
495                time,
496            } => {
497                let mut bt = buttons.lock().unwrap();
498                bt.insert(code, time);
499                let ty = ty.clone();
500                let buttons = buttons.clone();
501                thread::spawn(move || {
502                    thread::sleep(HOLD_DELAY_SHORT);
503                    {
504                        let bt = buttons.lock().unwrap();
505                        if let Some(&initial_time) = bt.get(&code) {
506                            if (initial_time - time).abs() < f64::EPSILON {
507                                ty.send(Event::Gesture(GestureEvent::HoldButtonShort(code)))
508                                    .ok();
509                            }
510                        }
511                    }
512                    thread::sleep(HOLD_DELAY_LONG - HOLD_DELAY_SHORT);
513                    {
514                        let bt = buttons.lock().unwrap();
515                        if let Some(&initial_time) = bt.get(&code) {
516                            if (initial_time - time).abs() < f64::EPSILON {
517                                ty.send(Event::Gesture(GestureEvent::HoldButtonLong(code)))
518                                    .ok();
519                            }
520                        }
521                    }
522                });
523            }
524            DeviceEvent::Button {
525                status: ButtonStatus::Released,
526                code,
527                ..
528            } => {
529                let mut bt = buttons.lock().unwrap();
530                bt.remove(&code);
531            }
532            _ => (),
533        }
534    }
535}
536
537fn interpret_segment(sp: &[Point], tap_jitter: f32) -> GestureEvent {
538    let a = sp[0];
539    let b = sp[sp.len() - 1];
540    let ab = b - a;
541    let d = ab.length();
542    if d < tap_jitter {
543        GestureEvent::Tap(a)
544    } else {
545        let p = sp[elbow(sp)];
546        let (n, p) = {
547            let p: Vec2 = p.into();
548            let (n, _) = nearest_segment_point(p, a.into(), b.into());
549            (n, p)
550        };
551        let np = p - n;
552        let ds = np.length();
553        if ds > d / 5.0 {
554            let g = (np.x as f32 / np.y as f32).abs();
555            if g < 0.5 || g > 2.0 {
556                GestureEvent::Arrow {
557                    dir: np.dir(),
558                    start: a,
559                    end: b,
560                }
561            } else {
562                GestureEvent::Corner {
563                    dir: np.diag_dir(),
564                    start: a,
565                    end: b,
566                }
567            }
568        } else {
569            let g = (ab.x as f32 / ab.y as f32).abs();
570            if g < 0.5 || g > 2.0 {
571                GestureEvent::Swipe {
572                    start: a,
573                    end: b,
574                    dir: ab.dir(),
575                }
576            } else {
577                GestureEvent::SlantedSwipe {
578                    start: a,
579                    end: b,
580                    dir: ab.diag_dir(),
581                }
582            }
583        }
584    }
585}