input_pipeline/gestures/
primary_tap.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use super::gesture_arena::{
6    self, DetailedReasonFloat, DetailedReasonInt, DetailedReasonUint, ExamineEventResult,
7    PRIMARY_BUTTON, ProcessBufferedEventsResult, Reason, RecognizedGesture, TouchpadEvent,
8    VerifyEventResult,
9};
10use crate::mouse_binding::{MouseEvent, MouseLocation, MousePhase, RelativeLocation};
11use crate::utils::{Position, euclidean_distance};
12use fuchsia_sync::Mutex;
13
14use maplit::hashset;
15
16/// The initial state of this recognizer, before a tap has been detected.
17#[derive(Debug)]
18pub(super) struct InitialContender {
19    /// The maximum displacement that a detected finger can withstand to still
20    /// be considered a tap. Measured in millimeters.
21    pub(super) max_finger_displacement_in_mm: f32,
22
23    /// The maximum time that can elapse between a finger down and finger up
24    /// to be considered a tap gesture.
25    pub(super) max_time_elapsed: zx::MonotonicDuration,
26}
27
28impl InitialContender {
29    #[allow(clippy::boxed_local, reason = "mass allow for https://fxbug.dev/381896734")]
30    fn into_finger_contact_contender(
31        self: Box<Self>,
32        finger_down_event: TouchpadEvent,
33    ) -> Box<dyn gesture_arena::Contender> {
34        Box::new(FingerContactContender {
35            max_finger_displacement_in_mm: self.max_finger_displacement_in_mm,
36            max_time_elapsed: self.max_time_elapsed,
37            finger_down_event,
38        })
39    }
40}
41
42impl gesture_arena::Contender for InitialContender {
43    fn examine_event(self: Box<Self>, event: &TouchpadEvent) -> ExamineEventResult {
44        let num_contacts = event.contacts.len();
45        if num_contacts != 1 {
46            return ExamineEventResult::Mismatch(Reason::DetailedUint(DetailedReasonUint {
47                criterion: "num_contacts",
48                min: Some(1),
49                max: Some(1),
50                actual: num_contacts,
51            }));
52        }
53
54        let num_pressed_buttons = event.pressed_buttons.len();
55        match num_pressed_buttons {
56            0 => ExamineEventResult::Contender(self.into_finger_contact_contender(event.clone())),
57            _ => ExamineEventResult::Mismatch(Reason::DetailedUint(DetailedReasonUint {
58                criterion: "num_pressed_buttons",
59                min: Some(0),
60                max: Some(0),
61                actual: num_pressed_buttons,
62            })),
63        }
64    }
65
66    fn start_from_idle(&self) -> bool {
67        true
68    }
69}
70
71/// The state when this recognizer has detected a single finger in contact with the touchpad.
72#[derive(Debug)]
73struct FingerContactContender {
74    /// The maximum displacement that a detected finger can withstand to still
75    /// be considered a tap. Measured in millimeters.
76    max_finger_displacement_in_mm: f32,
77
78    /// The maximum time that can elapse between a finger down and finger up
79    /// to be considered a tap gesture.
80    max_time_elapsed: zx::MonotonicDuration,
81
82    /// The TouchpadEvent when a finger down was first detected.
83    finger_down_event: TouchpadEvent,
84}
85
86impl FingerContactContender {
87    #[allow(clippy::boxed_local, reason = "mass allow for https://fxbug.dev/381896734")]
88    fn into_matched_contender(
89        self: Box<Self>,
90        finger_up_event: TouchpadEvent,
91    ) -> Box<dyn gesture_arena::MatchedContender> {
92        Box::new(MatchedContender {
93            finger_down_event: self.finger_down_event,
94            finger_up_event,
95            max_time_elapsed: self.max_time_elapsed,
96        })
97    }
98}
99
100impl gesture_arena::Contender for FingerContactContender {
101    fn examine_event(self: Box<Self>, event: &TouchpadEvent) -> ExamineEventResult {
102        let elapsed_time = event.timestamp - self.finger_down_event.timestamp;
103        if elapsed_time >= self.max_time_elapsed {
104            return ExamineEventResult::Mismatch(Reason::DetailedInt(DetailedReasonInt {
105                criterion: "elapsed_time_micros",
106                min: None,
107                max: Some(self.max_time_elapsed.into_micros()),
108                actual: elapsed_time.into_micros(),
109            }));
110        }
111
112        let num_pressed_buttons = event.pressed_buttons.len();
113        if num_pressed_buttons != 0 {
114            return ExamineEventResult::Mismatch(Reason::DetailedUint(DetailedReasonUint {
115                criterion: "num_pressed_buttons",
116                min: Some(0),
117                max: Some(0),
118                actual: num_pressed_buttons,
119            }));
120        }
121
122        let num_contacts = event.contacts.len();
123        match num_contacts {
124            0 => ExamineEventResult::MatchedContender(self.into_matched_contender(event.clone())),
125            1 => {
126                let displacement_mm = euclidean_distance(
127                    position_from_event(event),
128                    position_from_event(&self.finger_down_event),
129                );
130                if displacement_mm >= self.max_finger_displacement_in_mm {
131                    return ExamineEventResult::Mismatch(Reason::DetailedFloat(
132                        DetailedReasonFloat {
133                            criterion: "displacement_mm",
134                            min: None,
135                            max: Some(self.max_finger_displacement_in_mm),
136                            actual: displacement_mm,
137                        },
138                    ));
139                }
140                ExamineEventResult::Contender(self)
141            }
142            _ => ExamineEventResult::Mismatch(Reason::DetailedUint(DetailedReasonUint {
143                criterion: "num_contacts",
144                min: Some(0),
145                max: Some(1),
146                actual: num_contacts,
147            })),
148        }
149    }
150}
151
152/// The state when this recognizer has detected a tap, but the gesture arena
153/// has not declared this recognizer the winner.
154#[derive(Debug)]
155struct MatchedContender {
156    /// The TouchpadEvent when a finger down was first detected.
157    finger_down_event: TouchpadEvent,
158
159    /// The TouchpadEvent when a finger up was first detected.
160    finger_up_event: TouchpadEvent,
161
162    /// The maximum time that can elapse between a finger down and finger up
163    /// to be considered a tap gesture.
164    max_time_elapsed: zx::MonotonicDuration,
165}
166
167impl gesture_arena::MatchedContender for MatchedContender {
168    fn verify_event(self: Box<Self>, event: &TouchpadEvent) -> VerifyEventResult {
169        let elapsed_time = event.timestamp - self.finger_down_event.timestamp;
170        if elapsed_time >= self.max_time_elapsed {
171            return VerifyEventResult::Mismatch(Reason::DetailedInt(DetailedReasonInt {
172                criterion: "elapsed_time_micros",
173                min: None,
174                max: Some(self.max_time_elapsed.into_micros()),
175                actual: elapsed_time.into_micros(),
176            }));
177        }
178
179        let num_contacts = event.contacts.len();
180        if num_contacts != 0 {
181            return VerifyEventResult::Mismatch(Reason::DetailedUint(DetailedReasonUint {
182                criterion: "num_contacts",
183                min: Some(0),
184                max: Some(0),
185                actual: num_contacts,
186            }));
187        }
188
189        let num_pressed_buttons = event.pressed_buttons.len();
190        if num_pressed_buttons != 0 {
191            return VerifyEventResult::Mismatch(Reason::DetailedUint(DetailedReasonUint {
192                criterion: "num_pressed_buttons",
193                min: Some(0),
194                max: Some(0),
195                actual: num_pressed_buttons,
196            }));
197        }
198
199        VerifyEventResult::MatchedContender(self)
200    }
201
202    fn process_buffered_events(
203        self: Box<Self>,
204        _events: Vec<TouchpadEvent>,
205    ) -> ProcessBufferedEventsResult {
206        ProcessBufferedEventsResult {
207            generated_events: vec![
208                gesture_arena::MouseEvent {
209                    timestamp: self.finger_down_event.timestamp,
210                    mouse_data: MouseEvent {
211                        location: MouseLocation::Relative(RelativeLocation {
212                            millimeters: Position::zero(),
213                        }),
214                        wheel_delta_v: None,
215                        wheel_delta_h: None,
216                        phase: MousePhase::Down,
217                        affected_buttons: hashset! {PRIMARY_BUTTON},
218                        pressed_buttons: hashset! {PRIMARY_BUTTON},
219                        is_precision_scroll: None,
220                        wake_lease: Mutex::new(None),
221                    },
222                },
223                gesture_arena::MouseEvent {
224                    timestamp: self.finger_up_event.timestamp,
225                    mouse_data: MouseEvent {
226                        location: MouseLocation::Relative(RelativeLocation {
227                            millimeters: Position::zero(),
228                        }),
229                        wheel_delta_v: None,
230                        wheel_delta_h: None,
231                        phase: MousePhase::Up,
232                        affected_buttons: hashset! {PRIMARY_BUTTON},
233                        pressed_buttons: hashset! {},
234                        is_precision_scroll: None,
235                        wake_lease: Mutex::new(None),
236                    },
237                },
238            ],
239            winner: None,
240            recognized_gesture: RecognizedGesture::PrimaryTap,
241        }
242    }
243}
244
245/// This function returns the position associated with a TouchpadEvent that is
246/// assumed to have a single associated TouchContact.
247fn position_from_event(event: &TouchpadEvent) -> Position {
248    event.contacts[0].position
249}
250
251#[cfg(test)]
252mod tests {
253    use super::*;
254    use crate::gestures::gesture_arena::{Contender, MatchedContender as _};
255    use crate::testing_utilities::create_touch_contact;
256    use assert_matches::assert_matches;
257    use std::any::TypeId;
258
259    const MAX_TIME_ELAPSED: zx::MonotonicDuration = zx::MonotonicDuration::from_nanos(10000);
260    const MAX_FINGER_DISPLACEMENT_IN_MM: f32 = 10.0;
261    const HALF_MOTION: f32 = MAX_FINGER_DISPLACEMENT_IN_MM / 2.0;
262
263    fn assert_finger_down_contender(result: ExamineEventResult) {
264        match result {
265            ExamineEventResult::Contender(boxed) => {
266                assert_eq!((&*boxed).as_any().type_id(), TypeId::of::<FingerContactContender>());
267            }
268            other => panic!("Expected a Contender but found {:?}", other),
269        }
270    }
271
272    fn assert_examined_matched_contender(result: ExamineEventResult) {
273        match result {
274            ExamineEventResult::MatchedContender(boxed) => {
275                assert_eq!((&*boxed).as_any().type_id(), TypeId::of::<MatchedContender>());
276            }
277            other => panic!("Expected a MatchedContender but found {:?}", other),
278        }
279    }
280
281    fn assert_verified_matched_contender(result: VerifyEventResult) {
282        match result {
283            VerifyEventResult::MatchedContender(boxed) => {
284                assert_eq!((&*boxed).as_any().type_id(), TypeId::of::<MatchedContender>());
285            }
286            other => panic!("Expected a MatchedContender but found {:?}", other),
287        }
288    }
289
290    /// Tests that an InitialContender with zero touch contacts yields a
291    /// Mismatch.
292    #[fuchsia::test]
293    fn contender_no_touch_contacts() {
294        assert_matches!(
295            Box::new(InitialContender {
296                max_finger_displacement_in_mm: MAX_FINGER_DISPLACEMENT_IN_MM,
297                max_time_elapsed: MAX_TIME_ELAPSED
298            })
299            .examine_event(&TouchpadEvent {
300                contacts: vec![],
301                timestamp: zx::MonotonicInstant::from_nanos(0),
302                pressed_buttons: vec![],
303                filtered_palm_contacts: vec![],
304            }),
305            ExamineEventResult::Mismatch(_)
306        );
307    }
308
309    /// Tests that an InitialContender with multiple touch contacts yields a
310    /// Mismatch.
311    #[fuchsia::test]
312    fn contender_many_touch_contacts() {
313        assert_matches!(
314            Box::new(InitialContender {
315                max_finger_displacement_in_mm: MAX_FINGER_DISPLACEMENT_IN_MM,
316                max_time_elapsed: MAX_TIME_ELAPSED
317            })
318            .examine_event(&TouchpadEvent {
319                contacts: vec![
320                    create_touch_contact(0, Position::zero()),
321                    create_touch_contact(1, Position::zero())
322                ],
323                timestamp: zx::MonotonicInstant::from_nanos(0),
324                pressed_buttons: vec![],
325                filtered_palm_contacts: vec![],
326            }),
327            ExamineEventResult::Mismatch(_)
328        );
329    }
330
331    /// Tests that an InitialContender with a single touch contact and one
332    /// pressed button yields a Mismatch.
333    #[fuchsia::test]
334    fn contender_single_button() {
335        assert_matches!(
336            Box::new(InitialContender {
337                max_finger_displacement_in_mm: MAX_FINGER_DISPLACEMENT_IN_MM,
338                max_time_elapsed: MAX_TIME_ELAPSED
339            })
340            .examine_event(&TouchpadEvent {
341                contacts: vec![create_touch_contact(0, Position::zero())],
342                timestamp: zx::MonotonicInstant::from_nanos(0),
343                pressed_buttons: vec![0],
344                filtered_palm_contacts: vec![],
345            },),
346            ExamineEventResult::Mismatch(_)
347        );
348    }
349
350    /// Tests that an InitialContender with a single touch contact and multiple
351    /// pressed button yields a Mismatch.
352    #[fuchsia::test]
353    fn contender_many_buttons() {
354        assert_matches!(
355            Box::new(InitialContender {
356                max_finger_displacement_in_mm: MAX_FINGER_DISPLACEMENT_IN_MM,
357                max_time_elapsed: MAX_TIME_ELAPSED
358            })
359            .examine_event(&TouchpadEvent {
360                contacts: vec![create_touch_contact(0, Position::zero())],
361                timestamp: zx::MonotonicInstant::from_nanos(0),
362                pressed_buttons: vec![0, 1],
363                filtered_palm_contacts: vec![],
364            },),
365            ExamineEventResult::Mismatch(_)
366        );
367    }
368
369    /// Tests that an InitialContender with a single touch contact and no
370    /// pressed buttons yields a FingerContactContender.
371    #[fuchsia::test]
372    fn contender_no_buttons() {
373        assert_finger_down_contender(
374            Box::new(InitialContender {
375                max_finger_displacement_in_mm: MAX_FINGER_DISPLACEMENT_IN_MM,
376                max_time_elapsed: MAX_TIME_ELAPSED,
377            })
378            .examine_event(&TouchpadEvent {
379                contacts: vec![create_touch_contact(0, Position::zero())],
380                timestamp: zx::MonotonicInstant::from_nanos(0),
381                pressed_buttons: vec![],
382                filtered_palm_contacts: vec![],
383            }),
384        );
385    }
386
387    /// Tests that a FingerContactContender with an event whose timestamp exceeds
388    /// the elapsed threshold yields a Mismatch.
389    #[fuchsia::test]
390    fn finger_down_contender_too_long() {
391        assert_matches!(
392            Box::new(FingerContactContender {
393                max_finger_displacement_in_mm: MAX_FINGER_DISPLACEMENT_IN_MM,
394                max_time_elapsed: MAX_TIME_ELAPSED,
395                finger_down_event: TouchpadEvent {
396                    contacts: vec![create_touch_contact(0, Position::zero())],
397                    timestamp: zx::MonotonicInstant::from_nanos(0),
398                    pressed_buttons: vec![],
399                    filtered_palm_contacts: vec![],
400                },
401            })
402            .examine_event(&TouchpadEvent {
403                contacts: vec![],
404                timestamp: MAX_TIME_ELAPSED + zx::MonotonicInstant::from_nanos(1),
405                pressed_buttons: vec![],
406                filtered_palm_contacts: vec![],
407            }),
408            ExamineEventResult::Mismatch(_)
409        );
410    }
411
412    /// Tests that a FingerContactContender with zero touch contacts yields a
413    /// MatchedContender.
414    #[fuchsia::test]
415    fn finger_down_contender_no_touch_contacts() {
416        assert_examined_matched_contender(
417            Box::new(FingerContactContender {
418                max_finger_displacement_in_mm: MAX_FINGER_DISPLACEMENT_IN_MM,
419                max_time_elapsed: MAX_TIME_ELAPSED,
420                finger_down_event: TouchpadEvent {
421                    contacts: vec![create_touch_contact(0, Position::zero())],
422                    timestamp: zx::MonotonicInstant::from_nanos(0),
423                    pressed_buttons: vec![],
424                    filtered_palm_contacts: vec![],
425                },
426            })
427            .examine_event(&TouchpadEvent {
428                contacts: vec![],
429                timestamp: zx::MonotonicInstant::from_nanos(0),
430                pressed_buttons: vec![],
431                filtered_palm_contacts: vec![],
432            }),
433        );
434    }
435
436    /// Tests that a FingerContactContender with multiple touch contacts yields a
437    /// Mismatch.
438    #[fuchsia::test]
439    fn finger_down_contender_many_touch_contacts() {
440        assert_matches!(
441            Box::new(FingerContactContender {
442                max_finger_displacement_in_mm: MAX_FINGER_DISPLACEMENT_IN_MM,
443                max_time_elapsed: MAX_TIME_ELAPSED,
444                finger_down_event: TouchpadEvent {
445                    contacts: vec![create_touch_contact(0, Position::zero())],
446                    timestamp: zx::MonotonicInstant::from_nanos(0),
447                    pressed_buttons: vec![],
448                    filtered_palm_contacts: vec![],
449                },
450            })
451            .examine_event(&TouchpadEvent {
452                contacts: vec![
453                    create_touch_contact(0, Position::zero()),
454                    create_touch_contact(1, Position::zero())
455                ],
456                timestamp: zx::MonotonicInstant::from_nanos(0),
457                pressed_buttons: vec![],
458                filtered_palm_contacts: vec![],
459            }),
460            ExamineEventResult::Mismatch(_)
461        );
462    }
463
464    /// Tests that a FingerContactContender with a single touch contact and
465    /// too much displacement yields a Mismatch.
466    #[fuchsia::test]
467    fn finger_down_contender_large_displacement() {
468        assert_matches!(
469            Box::new(FingerContactContender {
470                max_finger_displacement_in_mm: MAX_FINGER_DISPLACEMENT_IN_MM,
471                max_time_elapsed: MAX_TIME_ELAPSED,
472                finger_down_event: TouchpadEvent {
473                    contacts: vec![create_touch_contact(0, Position::zero())],
474                    timestamp: zx::MonotonicInstant::from_nanos(0),
475                    pressed_buttons: vec![],
476                    filtered_palm_contacts: vec![],
477                },
478            })
479            .examine_event(&TouchpadEvent {
480                contacts: vec![create_touch_contact(
481                    0,
482                    Position { x: MAX_FINGER_DISPLACEMENT_IN_MM, y: MAX_FINGER_DISPLACEMENT_IN_MM }
483                )],
484                timestamp: zx::MonotonicInstant::from_nanos(0),
485                pressed_buttons: vec![],
486                filtered_palm_contacts: vec![],
487            }),
488            ExamineEventResult::Mismatch(_)
489        );
490    }
491
492    /// Tests that a FingerContactContender with a single touch contact,
493    /// no displacement, and no pressed buttons yields a
494    /// FingerContactContender.
495    #[fuchsia::test]
496    fn finger_down_contender_no_buttons_no_displacement() {
497        assert_finger_down_contender(
498            Box::new(FingerContactContender {
499                max_finger_displacement_in_mm: MAX_FINGER_DISPLACEMENT_IN_MM,
500                max_time_elapsed: MAX_TIME_ELAPSED,
501                finger_down_event: TouchpadEvent {
502                    contacts: vec![create_touch_contact(0, Position::zero())],
503                    timestamp: zx::MonotonicInstant::from_nanos(0),
504                    pressed_buttons: vec![],
505                    filtered_palm_contacts: vec![],
506                },
507            })
508            .examine_event(&TouchpadEvent {
509                contacts: vec![create_touch_contact(0, Position::zero())],
510                timestamp: zx::MonotonicInstant::from_nanos(0),
511                pressed_buttons: vec![],
512                filtered_palm_contacts: vec![],
513            }),
514        );
515    }
516
517    /// Tests that a FingerContactContender with a single touch contact,
518    /// acceptable displacement, and no pressed buttons yields a
519    /// FingerContactContender.
520    #[fuchsia::test]
521    fn finger_down_contender_no_buttons_some_displacement() {
522        assert_finger_down_contender(
523            Box::new(FingerContactContender {
524                max_finger_displacement_in_mm: MAX_FINGER_DISPLACEMENT_IN_MM,
525                max_time_elapsed: MAX_TIME_ELAPSED,
526                finger_down_event: TouchpadEvent {
527                    contacts: vec![create_touch_contact(0, Position::zero())],
528                    timestamp: zx::MonotonicInstant::from_nanos(0),
529                    pressed_buttons: vec![],
530                    filtered_palm_contacts: vec![],
531                },
532            })
533            .examine_event(&TouchpadEvent {
534                contacts: vec![create_touch_contact(
535                    0,
536                    Position { x: HALF_MOTION, y: HALF_MOTION },
537                )],
538                timestamp: zx::MonotonicInstant::from_nanos(0),
539                pressed_buttons: vec![],
540                filtered_palm_contacts: vec![],
541            }),
542        );
543    }
544
545    /// Tests that a FingerContactContender with a single touch contact,
546    /// acceptable displacement, and one pressed button yields a
547    /// Mismatch.
548    #[fuchsia::test]
549    fn finger_down_contender_single_button() {
550        assert_matches!(
551            Box::new(FingerContactContender {
552                max_finger_displacement_in_mm: MAX_FINGER_DISPLACEMENT_IN_MM,
553                max_time_elapsed: MAX_TIME_ELAPSED,
554                finger_down_event: TouchpadEvent {
555                    contacts: vec![create_touch_contact(0, Position::zero())],
556                    timestamp: zx::MonotonicInstant::from_nanos(0),
557                    pressed_buttons: vec![],
558                    filtered_palm_contacts: vec![],
559                },
560            })
561            .examine_event(&TouchpadEvent {
562                contacts: vec![create_touch_contact(
563                    0,
564                    Position { x: HALF_MOTION, y: HALF_MOTION }
565                )],
566                timestamp: zx::MonotonicInstant::from_nanos(0),
567                pressed_buttons: vec![0],
568                filtered_palm_contacts: vec![],
569            }),
570            ExamineEventResult::Mismatch(_)
571        );
572    }
573
574    /// Tests that a FingerContactContender with a single touch contact,
575    /// acceptable displacement, and multiple pressed buttons yields a
576    /// Mismatch.
577    #[fuchsia::test]
578    fn finger_down_contender_many_buttons() {
579        assert_matches!(
580            Box::new(FingerContactContender {
581                max_finger_displacement_in_mm: MAX_FINGER_DISPLACEMENT_IN_MM,
582                max_time_elapsed: MAX_TIME_ELAPSED,
583                finger_down_event: TouchpadEvent {
584                    contacts: vec![create_touch_contact(0, Position::zero())],
585                    timestamp: zx::MonotonicInstant::from_nanos(0),
586                    pressed_buttons: vec![],
587                    filtered_palm_contacts: vec![],
588                },
589            })
590            .examine_event(&TouchpadEvent {
591                contacts: vec![create_touch_contact(
592                    0,
593                    Position { x: HALF_MOTION, y: HALF_MOTION }
594                )],
595                timestamp: zx::MonotonicInstant::from_nanos(0),
596                pressed_buttons: vec![0, 1],
597                filtered_palm_contacts: vec![],
598            }),
599            ExamineEventResult::Mismatch(_)
600        );
601    }
602
603    /// Tests that a MatchedContender with one button pressed yields a Mismatch.
604    #[fuchsia::test]
605    fn matched_contender_one_button() {
606        assert_matches!(
607            Box::new(MatchedContender {
608                finger_down_event: TouchpadEvent {
609                    contacts: vec![create_touch_contact(0, Position::zero())],
610                    timestamp: zx::MonotonicInstant::from_nanos(0),
611                    pressed_buttons: vec![],
612                    filtered_palm_contacts: vec![],
613                },
614                finger_up_event: TouchpadEvent {
615                    contacts: vec![],
616                    timestamp: zx::MonotonicInstant::from_nanos(0),
617                    pressed_buttons: vec![],
618                    filtered_palm_contacts: vec![],
619                },
620                max_time_elapsed: MAX_TIME_ELAPSED,
621            })
622            .verify_event(&TouchpadEvent {
623                contacts: vec![],
624                timestamp: zx::MonotonicInstant::from_nanos(0),
625                pressed_buttons: vec![0],
626                filtered_palm_contacts: vec![],
627            }),
628            VerifyEventResult::Mismatch(_)
629        );
630    }
631
632    /// Tests that a MatchedContender with multiple buttons pressed yields a
633    /// Mismatch.
634    #[fuchsia::test]
635    fn matched_contender_many_buttons() {
636        assert_matches!(
637            Box::new(MatchedContender {
638                finger_down_event: TouchpadEvent {
639                    contacts: vec![create_touch_contact(0, Position::zero())],
640                    timestamp: zx::MonotonicInstant::from_nanos(0),
641                    pressed_buttons: vec![],
642                    filtered_palm_contacts: vec![],
643                },
644                finger_up_event: TouchpadEvent {
645                    contacts: vec![],
646                    timestamp: zx::MonotonicInstant::from_nanos(0),
647                    pressed_buttons: vec![],
648                    filtered_palm_contacts: vec![],
649                },
650                max_time_elapsed: MAX_TIME_ELAPSED,
651            })
652            .verify_event(&TouchpadEvent {
653                contacts: vec![],
654                timestamp: zx::MonotonicInstant::from_nanos(0),
655                pressed_buttons: vec![0, 1],
656                filtered_palm_contacts: vec![],
657            }),
658            VerifyEventResult::Mismatch(_)
659        );
660    }
661
662    /// Tests that a MatchedContender with one touch contact yields a Mismatch.
663    #[fuchsia::test]
664    fn matched_contender_single_touch_contact() {
665        assert_matches!(
666            Box::new(MatchedContender {
667                finger_down_event: TouchpadEvent {
668                    contacts: vec![create_touch_contact(0, Position::zero())],
669                    timestamp: zx::MonotonicInstant::from_nanos(0),
670                    pressed_buttons: vec![],
671                    filtered_palm_contacts: vec![],
672                },
673                finger_up_event: TouchpadEvent {
674                    contacts: vec![],
675                    timestamp: zx::MonotonicInstant::from_nanos(0),
676                    pressed_buttons: vec![],
677                    filtered_palm_contacts: vec![],
678                },
679                max_time_elapsed: MAX_TIME_ELAPSED,
680            })
681            .verify_event(&TouchpadEvent {
682                contacts: vec![create_touch_contact(0, Position::zero())],
683                timestamp: zx::MonotonicInstant::from_nanos(0),
684                pressed_buttons: vec![],
685                filtered_palm_contacts: vec![],
686            }),
687            VerifyEventResult::Mismatch(_)
688        );
689    }
690
691    /// Tests that a MatchedContender with multiple touch contacts yields a
692    /// Mismatch.
693    #[fuchsia::test]
694    fn matched_contender_many_touch_contacts() {
695        assert_matches!(
696            Box::new(MatchedContender {
697                finger_down_event: TouchpadEvent {
698                    contacts: vec![create_touch_contact(0, Position::zero())],
699                    timestamp: zx::MonotonicInstant::from_nanos(0),
700                    pressed_buttons: vec![0],
701                    filtered_palm_contacts: vec![],
702                },
703                finger_up_event: TouchpadEvent {
704                    contacts: vec![],
705                    timestamp: zx::MonotonicInstant::from_nanos(0),
706                    pressed_buttons: vec![],
707                    filtered_palm_contacts: vec![],
708                },
709                max_time_elapsed: MAX_TIME_ELAPSED,
710            })
711            .verify_event(&TouchpadEvent {
712                contacts: vec![
713                    create_touch_contact(0, Position::zero()),
714                    create_touch_contact(1, Position::zero())
715                ],
716                timestamp: zx::MonotonicInstant::from_nanos(0),
717                pressed_buttons: vec![],
718                filtered_palm_contacts: vec![],
719            }),
720            VerifyEventResult::Mismatch(_)
721        );
722    }
723
724    /// Tests that a MatchedContender with an event whose timestamp exceeds
725    /// the elapsed threshold yields a Mismatch.
726    #[fuchsia::test]
727    fn matched_contender_no_contacts_no_buttons_too_long() {
728        assert_matches!(
729            Box::new(MatchedContender {
730                finger_down_event: TouchpadEvent {
731                    contacts: vec![create_touch_contact(0, Position::zero())],
732                    timestamp: zx::MonotonicInstant::from_nanos(0),
733                    pressed_buttons: vec![],
734                    filtered_palm_contacts: vec![],
735                },
736                finger_up_event: TouchpadEvent {
737                    contacts: vec![],
738                    timestamp: zx::MonotonicInstant::from_nanos(0),
739                    pressed_buttons: vec![],
740                    filtered_palm_contacts: vec![],
741                },
742                max_time_elapsed: MAX_TIME_ELAPSED,
743            })
744            .verify_event(&TouchpadEvent {
745                contacts: vec![],
746                timestamp: MAX_TIME_ELAPSED + zx::MonotonicInstant::from_nanos(1),
747                pressed_buttons: vec![],
748                filtered_palm_contacts: vec![],
749            }),
750            VerifyEventResult::Mismatch(_)
751        );
752    }
753
754    /// Tests that a MatchedContender with no buttons or touch contacts
755    /// yields a MatchedContender.
756    #[fuchsia::test]
757    fn matched_contender_no_contacts_no_buttons() {
758        assert_verified_matched_contender(
759            Box::new(MatchedContender {
760                finger_down_event: TouchpadEvent {
761                    contacts: vec![create_touch_contact(0, Position::zero())],
762                    timestamp: zx::MonotonicInstant::from_nanos(0),
763                    pressed_buttons: vec![],
764                    filtered_palm_contacts: vec![],
765                },
766                finger_up_event: TouchpadEvent {
767                    contacts: vec![],
768                    timestamp: zx::MonotonicInstant::from_nanos(0),
769                    pressed_buttons: vec![],
770                    filtered_palm_contacts: vec![],
771                },
772                max_time_elapsed: MAX_TIME_ELAPSED,
773            })
774            .verify_event(&TouchpadEvent {
775                contacts: vec![],
776                timestamp: zx::MonotonicInstant::from_nanos(0),
777                pressed_buttons: vec![],
778                filtered_palm_contacts: vec![],
779            }),
780        );
781    }
782
783    /// Tests that a MatchedContender processes buffered events by
784    /// returning mouse down and mouse up events.
785    #[fuchsia::test]
786    fn matched_contender_process_buffered_events() {
787        let timestamp = zx::MonotonicInstant::from_nanos(0);
788        let ProcessBufferedEventsResult { generated_events, winner, recognized_gesture } =
789            Box::new(MatchedContender {
790                finger_down_event: TouchpadEvent {
791                    contacts: vec![create_touch_contact(0, Position::zero())],
792                    timestamp,
793                    pressed_buttons: vec![],
794                    filtered_palm_contacts: vec![],
795                },
796                finger_up_event: TouchpadEvent {
797                    contacts: vec![],
798                    timestamp,
799                    pressed_buttons: vec![],
800                    filtered_palm_contacts: vec![],
801                },
802                max_time_elapsed: MAX_TIME_ELAPSED,
803            })
804            .process_buffered_events(vec![]);
805
806        assert_eq!(
807            generated_events,
808            [
809                gesture_arena::MouseEvent {
810                    timestamp: timestamp,
811                    mouse_data: MouseEvent {
812                        location: MouseLocation::Relative(RelativeLocation {
813                            millimeters: Position { x: 0.0, y: 0.0 }
814                        }),
815                        wheel_delta_v: None,
816                        wheel_delta_h: None,
817                        phase: MousePhase::Down,
818                        affected_buttons: hashset! {PRIMARY_BUTTON},
819                        pressed_buttons: hashset! {PRIMARY_BUTTON},
820                        is_precision_scroll: None,
821                        wake_lease: Mutex::new(None),
822                    },
823                },
824                gesture_arena::MouseEvent {
825                    timestamp: timestamp,
826                    mouse_data: MouseEvent {
827                        location: MouseLocation::Relative(RelativeLocation {
828                            millimeters: Position { x: 0.0, y: 0.0 }
829                        }),
830                        wheel_delta_v: None,
831                        wheel_delta_h: None,
832                        phase: MousePhase::Up,
833                        affected_buttons: hashset! {PRIMARY_BUTTON},
834                        pressed_buttons: hashset! {},
835                        is_precision_scroll: None,
836                        wake_lease: Mutex::new(None),
837                    },
838                }
839            ]
840        );
841        assert_matches!(winner, None);
842        assert_eq!(recognized_gesture, RecognizedGesture::PrimaryTap);
843    }
844}