1use super::gesture_arena::{
6 self, DetailedReasonFloat, DetailedReasonUint, EndGestureEvent, ExamineEventResult, MouseEvent,
7 ProcessBufferedEventsResult, ProcessNewEventResult, Reason, RecognizedGesture, TouchpadEvent,
8 VerifyEventResult,
9};
10use super::utils::{movement_from_events, MovementDetail};
11use crate::mouse_binding::{self, MouseButton};
12use crate::utils::{euclidean_distance, Position};
13
14use maplit::hashset;
15use std::collections::HashSet;
16
17#[derive(Debug)]
19pub(super) struct InitialContender {
20 pub(super) spurious_to_intentional_motion_threshold_mm: f32,
22
23 pub(super) spurious_to_intentional_motion_threshold_button_change_mm: f32,
26
27 pub(super) button_change_state_timeout: zx::MonotonicDuration,
30}
31
32#[derive(Debug)]
35struct FingerContactContender {
36 spurious_to_intentional_motion_threshold_mm: f32,
38
39 spurious_to_intentional_motion_threshold_button_change_mm: f32,
42
43 button_change_state_timeout: zx::MonotonicDuration,
46
47 initial_position: Position,
49}
50
51#[derive(Debug)]
54struct MatchedContender {
55 spurious_to_intentional_motion_threshold_mm: f32,
57
58 spurious_to_intentional_motion_threshold_button_change_mm: f32,
61
62 button_change_state_timeout: zx::MonotonicDuration,
65
66 pressed_event: TouchpadEvent,
68}
69
70#[derive(Debug)]
72struct ButtonDownWinner {
73 spurious_to_intentional_motion_threshold_mm: f32,
75
76 spurious_to_intentional_motion_threshold_button_change_mm: f32,
79
80 button_change_state_timeout: zx::MonotonicDuration,
83
84 pressed_event: TouchpadEvent,
86}
87
88#[derive(Debug)]
91struct DragWinner {
92 spurious_to_intentional_motion_threshold_button_change_mm: f32,
95
96 button_change_state_timeout: zx::MonotonicDuration,
99
100 last_event: TouchpadEvent,
102}
103
104#[derive(Debug)]
107struct ButtonUpWinner {
108 spurious_to_intentional_motion_threshold_button_change_mm: f32,
111
112 button_change_state_timeout: zx::MonotonicDuration,
115
116 button_up_event: TouchpadEvent,
118}
119
120impl InitialContender {
121 #[allow(clippy::boxed_local, reason = "mass allow for https://fxbug.dev/381896734")]
122 fn into_finger_contact_contender(
123 self: Box<Self>,
124 initial_position: Position,
125 ) -> Box<dyn gesture_arena::Contender> {
126 Box::new(FingerContactContender {
127 spurious_to_intentional_motion_threshold_mm: self
128 .spurious_to_intentional_motion_threshold_mm,
129 spurious_to_intentional_motion_threshold_button_change_mm: self
130 .spurious_to_intentional_motion_threshold_button_change_mm,
131 button_change_state_timeout: self.button_change_state_timeout,
132 initial_position,
133 })
134 }
135
136 #[allow(clippy::boxed_local, reason = "mass allow for https://fxbug.dev/381896734")]
137 fn into_matched_contender(
138 self: Box<Self>,
139 pressed_event: TouchpadEvent,
140 ) -> Box<dyn gesture_arena::MatchedContender> {
141 Box::new(MatchedContender {
142 spurious_to_intentional_motion_threshold_mm: self
143 .spurious_to_intentional_motion_threshold_mm,
144 spurious_to_intentional_motion_threshold_button_change_mm: self
145 .spurious_to_intentional_motion_threshold_button_change_mm,
146 button_change_state_timeout: self.button_change_state_timeout,
147 pressed_event,
148 })
149 }
150}
151
152impl gesture_arena::Contender for InitialContender {
153 fn examine_event(self: Box<Self>, event: &TouchpadEvent) -> ExamineEventResult {
154 let num_contacts = event.contacts.len();
155 if num_contacts != 1 {
156 return ExamineEventResult::Mismatch(Reason::DetailedUint(DetailedReasonUint {
157 criterion: "num_contacts",
158 min: Some(1),
159 max: Some(1),
160 actual: num_contacts,
161 }));
162 }
163
164 let num_pressed_buttons = event.pressed_buttons.len();
165 match num_pressed_buttons {
166 0 => ExamineEventResult::Contender(
167 self.into_finger_contact_contender(position_from_event(event)),
168 ),
169 1 => ExamineEventResult::MatchedContender(self.into_matched_contender(event.clone())),
170 _ => ExamineEventResult::Mismatch(Reason::DetailedUint(DetailedReasonUint {
174 criterion: "num_pressed_buttons",
175 min: Some(0),
176 max: Some(1),
177 actual: num_pressed_buttons,
178 })),
179 }
180 }
181}
182
183impl FingerContactContender {
184 #[allow(clippy::boxed_local, reason = "mass allow for https://fxbug.dev/381896734")]
185 fn into_matched_contender(
186 self: Box<Self>,
187 pressed_event: TouchpadEvent,
188 ) -> Box<dyn gesture_arena::MatchedContender> {
189 Box::new(MatchedContender {
190 spurious_to_intentional_motion_threshold_mm: self
191 .spurious_to_intentional_motion_threshold_mm,
192 spurious_to_intentional_motion_threshold_button_change_mm: self
193 .spurious_to_intentional_motion_threshold_button_change_mm,
194 button_change_state_timeout: self.button_change_state_timeout,
195 pressed_event,
196 })
197 }
198}
199
200impl gesture_arena::Contender for FingerContactContender {
201 fn examine_event(self: Box<Self>, event: &TouchpadEvent) -> ExamineEventResult {
202 let num_contacts = event.contacts.len();
203 if num_contacts != 1 {
204 return ExamineEventResult::Mismatch(Reason::DetailedUint(DetailedReasonUint {
205 criterion: "num_contacts",
206 min: Some(1),
207 max: Some(1),
208 actual: num_contacts,
209 }));
210 }
211
212 let displacement_mm = euclidean_distance(position_from_event(event), self.initial_position);
213 if displacement_mm >= self.spurious_to_intentional_motion_threshold_mm {
214 return ExamineEventResult::Mismatch(Reason::DetailedFloat(DetailedReasonFloat {
215 criterion: "displacement_mm",
216 min: None,
217 max: Some(self.spurious_to_intentional_motion_threshold_mm),
218 actual: displacement_mm,
219 }));
220 }
221
222 let num_pressed_buttons = event.pressed_buttons.len();
223 match num_pressed_buttons {
224 0 => ExamineEventResult::Contender(self),
225 1 => ExamineEventResult::MatchedContender(self.into_matched_contender(event.clone())),
226 _ => ExamineEventResult::Mismatch(Reason::DetailedUint(DetailedReasonUint {
227 criterion: "num_pressed_buttons",
228 min: Some(0),
229 max: Some(1),
230 actual: num_pressed_buttons,
231 })),
232 }
233 }
234}
235
236impl MatchedContender {
237 #[allow(clippy::boxed_local, reason = "mass allow for https://fxbug.dev/381896734")]
238 fn into_button_down_winner(self: Box<Self>) -> Box<dyn gesture_arena::Winner> {
239 Box::new(ButtonDownWinner {
240 spurious_to_intentional_motion_threshold_mm: self
241 .spurious_to_intentional_motion_threshold_mm,
242 spurious_to_intentional_motion_threshold_button_change_mm: self
243 .spurious_to_intentional_motion_threshold_button_change_mm,
244 button_change_state_timeout: self.button_change_state_timeout,
245 pressed_event: self.pressed_event,
246 })
247 }
248}
249
250impl gesture_arena::MatchedContender for MatchedContender {
251 fn verify_event(self: Box<Self>, _event: &TouchpadEvent) -> VerifyEventResult {
252 log::error!("Unexpected MatchedContender::verify_event() called");
256
257 VerifyEventResult::MatchedContender(self)
258 }
259
260 fn process_buffered_events(
261 self: Box<Self>,
262 _events: Vec<TouchpadEvent>,
263 ) -> ProcessBufferedEventsResult {
264 ProcessBufferedEventsResult {
266 generated_events: vec![touchpad_event_to_mouse_down_event(&self.pressed_event)],
267 winner: Some(self.into_button_down_winner()),
268 recognized_gesture: RecognizedGesture::OneButtonDown,
269 }
270 }
271}
272
273impl ButtonDownWinner {
274 #[allow(clippy::boxed_local, reason = "mass allow for https://fxbug.dev/381896734")]
275 fn into_drag_winner(self: Box<Self>) -> Box<dyn gesture_arena::Winner> {
276 Box::new(DragWinner {
277 spurious_to_intentional_motion_threshold_button_change_mm: self
278 .spurious_to_intentional_motion_threshold_button_change_mm,
279 button_change_state_timeout: self.button_change_state_timeout,
280 last_event: self.pressed_event,
281 })
282 }
283
284 #[allow(clippy::boxed_local, reason = "mass allow for https://fxbug.dev/381896734")]
285 fn into_button_up(
286 self: Box<Self>,
287 button_up_event: TouchpadEvent,
288 ) -> Box<dyn gesture_arena::Winner> {
289 Box::new(ButtonUpWinner {
290 spurious_to_intentional_motion_threshold_button_change_mm: self
291 .spurious_to_intentional_motion_threshold_button_change_mm,
292 button_change_state_timeout: self.button_change_state_timeout,
293 button_up_event,
294 })
295 }
296}
297
298impl gesture_arena::Winner for ButtonDownWinner {
299 fn process_new_event(self: Box<Self>, event: TouchpadEvent) -> ProcessNewEventResult {
300 let motion_threshold =
301 if event.timestamp - self.pressed_event.timestamp > self.button_change_state_timeout {
302 self.spurious_to_intentional_motion_threshold_mm
303 } else {
304 self.spurious_to_intentional_motion_threshold_button_change_mm
305 };
306
307 let num_pressed_buttons = event.pressed_buttons.len();
308 match num_pressed_buttons {
309 0 => ProcessNewEventResult::ContinueGesture(
312 Some(touchpad_event_to_mouse_up_event(&event)),
313 self.into_button_up(event),
314 ),
315 1 => {
316 let MovementDetail { euclidean_distance, movement: _ } =
318 movement_from_events(&self.pressed_event, &event);
319
320 if euclidean_distance > motion_threshold {
321 let drag_winner = self.into_drag_winner();
322 return drag_winner.process_new_event(event);
323 }
324 ProcessNewEventResult::ContinueGesture(None, self)
325 }
326 _ => ProcessNewEventResult::ContinueGesture(None, self),
329 }
330 }
331}
332
333impl DragWinner {
334 #[allow(clippy::boxed_local, reason = "mass allow for https://fxbug.dev/381896734")]
335 fn into_drag_winner(
336 self: Box<Self>,
337 last_event: TouchpadEvent,
338 ) -> Box<dyn gesture_arena::Winner> {
339 Box::new(DragWinner {
340 spurious_to_intentional_motion_threshold_button_change_mm: self
341 .spurious_to_intentional_motion_threshold_button_change_mm,
342 button_change_state_timeout: self.button_change_state_timeout,
343 last_event,
344 })
345 }
346
347 #[allow(clippy::boxed_local, reason = "mass allow for https://fxbug.dev/381896734")]
348 fn into_button_up(
349 self: Box<Self>,
350 button_up_event: TouchpadEvent,
351 ) -> Box<dyn gesture_arena::Winner> {
352 Box::new(ButtonUpWinner {
353 spurious_to_intentional_motion_threshold_button_change_mm: self
354 .spurious_to_intentional_motion_threshold_button_change_mm,
355 button_change_state_timeout: self.button_change_state_timeout,
356 button_up_event,
357 })
358 }
359}
360
361impl gesture_arena::Winner for DragWinner {
362 fn process_new_event(self: Box<Self>, event: TouchpadEvent) -> ProcessNewEventResult {
363 let num_pressed_buttons = event.pressed_buttons.len();
364 match num_pressed_buttons {
365 0 => ProcessNewEventResult::ContinueGesture(
368 Some(touchpad_event_to_mouse_up_event(&event)),
369 self.into_button_up(event),
370 ),
371 _ => {
372 ProcessNewEventResult::ContinueGesture(
375 Some(touchpad_event_to_mouse_drag_event(&self.last_event, &event)),
376 self.into_drag_winner(event),
377 )
378 }
379 }
380 }
381}
382
383impl gesture_arena::Winner for ButtonUpWinner {
384 fn process_new_event(self: Box<Self>, event: TouchpadEvent) -> ProcessNewEventResult {
385 let num_contacts = event.contacts.len();
387 if num_contacts != 1 {
388 return ProcessNewEventResult::EndGesture(
389 EndGestureEvent::UnconsumedEvent(event),
390 Reason::DetailedUint(DetailedReasonUint {
391 criterion: "num_contacts",
392 min: Some(1),
393 max: Some(1),
394 actual: num_contacts,
395 }),
396 );
397 }
398
399 let num_pressed_buttons = event.pressed_buttons.len();
401 if num_pressed_buttons != 0 {
402 return ProcessNewEventResult::EndGesture(
403 EndGestureEvent::UnconsumedEvent(event),
404 Reason::DetailedUint(DetailedReasonUint {
405 criterion: "num_buttons",
406 min: Some(0),
407 max: Some(0),
408 actual: num_pressed_buttons,
409 }),
410 );
411 }
412
413 if event.timestamp - self.button_up_event.timestamp > self.button_change_state_timeout {
415 return ProcessNewEventResult::EndGesture(
416 EndGestureEvent::UnconsumedEvent(event),
417 Reason::Basic("button_up_timeout"),
418 );
419 }
420
421 let MovementDetail { euclidean_distance, movement: _ } =
423 movement_from_events(&self.button_up_event, &event);
424 if euclidean_distance > self.spurious_to_intentional_motion_threshold_button_change_mm {
425 return ProcessNewEventResult::EndGesture(
426 EndGestureEvent::UnconsumedEvent(event),
427 Reason::DetailedFloat(DetailedReasonFloat {
428 criterion: "displacement_mm",
429 min: None,
430 max: Some(self.spurious_to_intentional_motion_threshold_button_change_mm),
431 actual: euclidean_distance,
432 }),
433 );
434 }
435
436 ProcessNewEventResult::ContinueGesture(None, self)
438 }
439}
440
441fn position_from_event(event: &TouchpadEvent) -> Position {
444 event.contacts[0].position
445}
446
447fn touchpad_event_to_mouse_down_event(event: &TouchpadEvent) -> MouseEvent {
448 make_mouse_event(
449 event.timestamp,
450 Position::zero(),
451 mouse_binding::MousePhase::Down,
452 hashset! {1},
453 hashset! {1},
454 )
455}
456
457fn touchpad_event_to_mouse_up_event(event: &TouchpadEvent) -> MouseEvent {
458 make_mouse_event(
459 event.timestamp,
460 Position::zero(),
461 mouse_binding::MousePhase::Up,
462 hashset! {1},
463 hashset! {},
464 )
465}
466
467fn touchpad_event_to_mouse_drag_event(
468 last_event: &TouchpadEvent,
469 event: &TouchpadEvent,
470) -> MouseEvent {
471 let MovementDetail { movement, euclidean_distance: _ } =
472 movement_from_events(last_event, event);
473 make_mouse_event(
474 event.timestamp,
475 movement,
476 mouse_binding::MousePhase::Move,
477 hashset! {},
478 hashset! {1},
479 )
480}
481
482fn make_mouse_event(
483 timestamp: zx::MonotonicInstant,
484 movement_in_mm: Position,
485 phase: mouse_binding::MousePhase,
486 affected_buttons: HashSet<MouseButton>,
487 pressed_buttons: HashSet<MouseButton>,
488) -> MouseEvent {
489 MouseEvent {
490 timestamp,
491 mouse_data: mouse_binding::MouseEvent::new(
492 mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
493 millimeters: movement_in_mm,
494 }),
495 None,
496 None,
497 phase,
498 affected_buttons,
499 pressed_buttons,
500 None,
501 ),
502 }
503}
504
505#[cfg(test)]
506mod tests {
507 use super::*;
508 use crate::touch_binding;
509 use assert_matches::assert_matches;
510 use test_case::test_case;
511
512 fn make_touch_contact(id: u32, position: Position) -> touch_binding::TouchContact {
513 touch_binding::TouchContact { id, position, pressure: None, contact_size: None }
514 }
515
516 const SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM: f32 = 10.0;
517 const SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM: f32 = 20.0;
518 const BUTTON_CHANGE_STATE_TIMEOUT: zx::MonotonicDuration =
519 zx::MonotonicDuration::from_seconds(1);
520
521 #[test_case(TouchpadEvent{
522 timestamp: zx::MonotonicInstant::ZERO,
523 pressed_buttons: vec![],
524 contacts: vec![],
525 filtered_palm_contacts: vec![],
526 };"0 fingers")]
527 #[test_case(TouchpadEvent{
528 timestamp: zx::MonotonicInstant::ZERO,
529 pressed_buttons: vec![],
530 contacts: vec![
531 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
532 make_touch_contact(2, Position{x: 5.0, y: 5.0}),
533 ],
534 filtered_palm_contacts: vec![],
535 };"2 fingers")]
536 #[fuchsia::test]
537 fn initial_contender_examine_event_mismatch(event: TouchpadEvent) {
538 let contender: Box<dyn gesture_arena::Contender> = Box::new(InitialContender {
539 spurious_to_intentional_motion_threshold_mm:
540 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
541 spurious_to_intentional_motion_threshold_button_change_mm:
542 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
543 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
544 });
545
546 let got = contender.examine_event(&event);
547 assert_matches!(got, ExamineEventResult::Mismatch(_));
548 }
549
550 #[fuchsia::test]
551 fn initial_contender_examine_event_finger_contact_contender() {
552 let contender: Box<dyn gesture_arena::Contender> = Box::new(InitialContender {
553 spurious_to_intentional_motion_threshold_mm:
554 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
555 spurious_to_intentional_motion_threshold_button_change_mm:
556 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
557 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
558 });
559 let event = TouchpadEvent {
560 timestamp: zx::MonotonicInstant::ZERO,
561 pressed_buttons: vec![],
562 contacts: vec![make_touch_contact(1, Position { x: 1.0, y: 1.0 })],
563 filtered_palm_contacts: vec![],
564 };
565
566 let got = contender.examine_event(&event);
567 assert_matches!(got, ExamineEventResult::Contender(_));
568 }
569
570 #[fuchsia::test]
571 fn initial_contender_examine_event_matched_contender() {
572 let contender: Box<dyn gesture_arena::Contender> = Box::new(InitialContender {
573 spurious_to_intentional_motion_threshold_mm:
574 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
575 spurious_to_intentional_motion_threshold_button_change_mm:
576 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
577 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
578 });
579 let event = TouchpadEvent {
580 timestamp: zx::MonotonicInstant::ZERO,
581 pressed_buttons: vec![1],
582 contacts: vec![make_touch_contact(1, Position { x: 1.0, y: 1.0 })],
583 filtered_palm_contacts: vec![],
584 };
585
586 let got = contender.examine_event(&event);
587 assert_matches!(got, ExamineEventResult::MatchedContender(_));
588 }
589
590 #[test_case(TouchpadEvent{
591 timestamp: zx::MonotonicInstant::ZERO,
592 pressed_buttons: vec![],
593 contacts: vec![],
594 filtered_palm_contacts: vec![],
595 };"0 fingers")]
596 #[test_case(TouchpadEvent{
597 timestamp: zx::MonotonicInstant::ZERO,
598 pressed_buttons: vec![],
599 contacts: vec![
600 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
601 make_touch_contact(2, Position{x: 5.0, y: 5.0}),
602 ],
603 filtered_palm_contacts: vec![],
604 };"2 fingers")]
605 #[test_case(TouchpadEvent{
606 timestamp: zx::MonotonicInstant::ZERO,
607 pressed_buttons: vec![],
608 contacts: vec![
609 make_touch_contact(1, Position{x: 10.0, y: 1.0}),
610 ],
611 filtered_palm_contacts: vec![],
612 };"1 fingers move more than threshold")]
613 #[fuchsia::test]
614 fn finger_contact_contender_examine_event_mismatch(event: TouchpadEvent) {
615 let contender: Box<dyn gesture_arena::Contender> = Box::new(FingerContactContender {
616 spurious_to_intentional_motion_threshold_mm:
617 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
618 spurious_to_intentional_motion_threshold_button_change_mm:
619 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
620 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
621 initial_position: Position { x: 0.0, y: 0.0 },
622 });
623
624 let got = contender.examine_event(&event);
625 assert_matches!(got, ExamineEventResult::Mismatch(_));
626 }
627
628 #[test_case(TouchpadEvent{
629 timestamp: zx::MonotonicInstant::ZERO,
630 pressed_buttons: vec![],
631 contacts: vec![
632 make_touch_contact(1, Position{x: 9.0, y: 1.0}),
633 ],
634 filtered_palm_contacts: vec![],
635 };"1 fingers move less than threshold")]
636 #[test_case(TouchpadEvent{
637 timestamp: zx::MonotonicInstant::ZERO,
638 pressed_buttons: vec![],
639 contacts: vec![
640 make_touch_contact(1, Position{x: 0.0, y: 0.0}),
641 ],
642 filtered_palm_contacts: vec![],
643 };"1 fingers stay")]
644 #[fuchsia::test]
645 fn finger_contact_contender_examine_event_finger_contact_contender(event: TouchpadEvent) {
646 let contender: Box<dyn gesture_arena::Contender> = Box::new(FingerContactContender {
647 spurious_to_intentional_motion_threshold_mm:
648 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
649 spurious_to_intentional_motion_threshold_button_change_mm:
650 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
651 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
652 initial_position: Position { x: 0.0, y: 0.0 },
653 });
654
655 let got = contender.examine_event(&event);
656 assert_matches!(got, ExamineEventResult::Contender(_));
657 }
658
659 #[fuchsia::test]
660 fn finger_contact_contender_examine_event_matched_contender() {
661 let contender: Box<dyn gesture_arena::Contender> = Box::new(FingerContactContender {
662 spurious_to_intentional_motion_threshold_mm:
663 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
664 spurious_to_intentional_motion_threshold_button_change_mm:
665 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
666 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
667 initial_position: Position { x: 0.0, y: 0.0 },
668 });
669 let event = TouchpadEvent {
670 timestamp: zx::MonotonicInstant::ZERO,
671 pressed_buttons: vec![1],
672 contacts: vec![make_touch_contact(1, Position { x: 1.0, y: 1.0 })],
673 filtered_palm_contacts: vec![],
674 };
675
676 let got = contender.examine_event(&event);
677 assert_matches!(got, ExamineEventResult::MatchedContender(_));
678 }
679
680 #[fuchsia::test]
681 fn matched_contender_process_buffered_events() {
682 let contender: Box<dyn gesture_arena::MatchedContender> = Box::new(MatchedContender {
683 spurious_to_intentional_motion_threshold_mm:
684 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
685 spurious_to_intentional_motion_threshold_button_change_mm:
686 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
687 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
688 pressed_event: TouchpadEvent {
689 timestamp: zx::MonotonicInstant::from_nanos(41),
690 pressed_buttons: vec![1],
691 contacts: vec![make_touch_contact(1, Position { x: 1.0, y: 1.0 })],
692 filtered_palm_contacts: vec![],
693 },
694 });
695 let events = vec![
696 TouchpadEvent {
697 timestamp: zx::MonotonicInstant::ZERO,
698 pressed_buttons: vec![],
699 contacts: vec![make_touch_contact(1, Position { x: 1.0, y: 1.0 })],
700 filtered_palm_contacts: vec![],
701 },
702 TouchpadEvent {
703 timestamp: zx::MonotonicInstant::from_nanos(41),
704 pressed_buttons: vec![1],
705 contacts: vec![make_touch_contact(1, Position { x: 1.0, y: 1.0 })],
706 filtered_palm_contacts: vec![],
707 },
708 ];
709
710 let got = contender.process_buffered_events(events);
711
712 assert_matches!(got, ProcessBufferedEventsResult{
713 generated_events,
714 winner: Some(winner),
715 recognized_gesture: RecognizedGesture::OneButtonDown,
716 } => {
717 pretty_assertions::assert_eq!(generated_events, vec![
718 MouseEvent {
719 timestamp:zx::MonotonicInstant::from_nanos(41),
720 mouse_data: mouse_binding::MouseEvent::new(
721 mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
722 millimeters: Position { x: 0.0, y: 0.0 },
723 }),
724 None,
725 None,
726 mouse_binding::MousePhase::Down,
727 hashset!{1},
728 hashset!{1},
729 None,
730 ),
731 }
732 ]);
733 pretty_assertions::assert_eq!(winner.get_type_name(), "input_pipeline_lib_test::gestures::one_finger_button::ButtonDownWinner");
734 });
735 }
736
737 #[test_case(TouchpadEvent{
738 timestamp: zx::MonotonicInstant::from_nanos(41),
739 pressed_buttons: vec![],
740 contacts: vec![
741 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
742 ],
743 filtered_palm_contacts: vec![],
744 };"button release")]
745 #[test_case(TouchpadEvent{
746 timestamp: zx::MonotonicInstant::from_nanos(41),
747 pressed_buttons: vec![],
748 contacts: vec![
749 make_touch_contact(1, Position{x: 19.0, y: 1.0}),
750 ],
751 filtered_palm_contacts: vec![],
752 };"move less than threshold in edge state")]
753 #[test_case(TouchpadEvent{
754 timestamp: zx::MonotonicInstant::ZERO + zx::MonotonicDuration::from_millis(1500),
755 pressed_buttons: vec![],
756 contacts: vec![
757 make_touch_contact(1, Position{x: 9.0, y: 1.0}),
758 ],
759 filtered_palm_contacts: vec![],
760 };"move less than threshold out of edge state")]
761 #[test_case(TouchpadEvent{
762 timestamp: zx::MonotonicInstant::from_nanos(41),
763 pressed_buttons: vec![],
764 contacts: vec![
765 make_touch_contact(1, Position{x: 20.0, y: 1.0}),
766 ],
767 filtered_palm_contacts: vec![],
768 };"move more than threshold in edge state and release button")]
769 #[test_case(TouchpadEvent{
770 timestamp: zx::MonotonicInstant::ZERO + zx::MonotonicDuration::from_millis(1500),
771 pressed_buttons: vec![],
772 contacts: vec![
773 make_touch_contact(1, Position{x: 10.0, y: 1.0}),
774 ],
775 filtered_palm_contacts: vec![],
776 };"move more than threshold out of edge state and release button")]
777 #[fuchsia::test]
778 fn button_down_winner_button_up(event: TouchpadEvent) {
779 let winner: Box<dyn gesture_arena::Winner> = Box::new(ButtonDownWinner {
780 spurious_to_intentional_motion_threshold_mm:
781 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
782 spurious_to_intentional_motion_threshold_button_change_mm:
783 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
784 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
785 pressed_event: TouchpadEvent {
786 timestamp: zx::MonotonicInstant::ZERO,
787 pressed_buttons: vec![1],
788 contacts: vec![make_touch_contact(1, Position { x: 0.0, y: 0.0 })],
789 filtered_palm_contacts: vec![],
790 },
791 });
792
793 let got = winner.process_new_event(event);
794 assert_matches!(got, ProcessNewEventResult::ContinueGesture(Some(MouseEvent {mouse_data, ..}), got_winner) => {
795 pretty_assertions::assert_eq!(mouse_data, mouse_binding::MouseEvent::new(
796 mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
797 millimeters: Position { x: 0.0, y: 0.0 },
798 }),
799 None,
800 None,
801 mouse_binding::MousePhase::Up,
802 hashset!{1},
803 hashset!{},
804 None,
805 ));
806 pretty_assertions::assert_eq!(got_winner.get_type_name(), "input_pipeline_lib_test::gestures::one_finger_button::ButtonUpWinner");
807 });
808 }
809
810 #[test_case(TouchpadEvent{
811 timestamp: zx::MonotonicInstant::from_nanos(41),
812 pressed_buttons: vec![1],
813 contacts: vec![
814 make_touch_contact(1, Position{x: 19.0, y: 1.0}),
815 ],
816 filtered_palm_contacts: vec![],
817 };"move less than threshold in edge state")]
818 #[test_case(TouchpadEvent{
819 timestamp: zx::MonotonicInstant::ZERO + zx::MonotonicDuration::from_millis(1500),
820 pressed_buttons: vec![1],
821 contacts: vec![
822 make_touch_contact(1, Position{x: 9.0, y: 1.0}),
823 ],
824 filtered_palm_contacts: vec![],
825 };"move less than threshold out of edge state")]
826 #[test_case(TouchpadEvent{
827 timestamp: zx::MonotonicInstant::from_nanos(41),
828 pressed_buttons: vec![1],
829 contacts: vec![
830 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
831 make_touch_contact(2, Position{x: 1.0, y: 2.0}),
832 ],
833 filtered_palm_contacts: vec![],
834 };"add more finger")]
835 #[fuchsia::test]
836 fn button_down_winner_continue(event: TouchpadEvent) {
837 let winner: Box<dyn gesture_arena::Winner> = Box::new(ButtonDownWinner {
838 spurious_to_intentional_motion_threshold_mm:
839 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
840 spurious_to_intentional_motion_threshold_button_change_mm:
841 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
842 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
843 pressed_event: TouchpadEvent {
844 timestamp: zx::MonotonicInstant::ZERO,
845 pressed_buttons: vec![1],
846 contacts: vec![make_touch_contact(1, Position { x: 0.0, y: 0.0 })],
847 filtered_palm_contacts: vec![],
848 },
849 });
850
851 let got = winner.process_new_event(event);
852 assert_matches!(got, ProcessNewEventResult::ContinueGesture(None, got_winner)=>{
853 pretty_assertions::assert_eq!(got_winner.get_type_name(), "input_pipeline_lib_test::gestures::one_finger_button::ButtonDownWinner");
854 });
855 }
856
857 #[test_case(TouchpadEvent{
858 timestamp: zx::MonotonicInstant::from_nanos(41),
859 pressed_buttons: vec![1],
860 contacts: vec![
861 make_touch_contact(1, Position{x: 20.0, y: 1.0}),
862 ],
863 filtered_palm_contacts: vec![],
864 };"move more than threshold in edge state")]
865 #[test_case(TouchpadEvent{
866 timestamp: zx::MonotonicInstant::ZERO + zx::MonotonicDuration::from_millis(1500),
867 pressed_buttons: vec![1],
868 contacts: vec![
869 make_touch_contact(1, Position{x: 10.0, y: 1.0}),
870 ],
871 filtered_palm_contacts: vec![],
872 };"move more than threshold out of edge state")]
873 #[fuchsia::test]
874 fn button_down_winner_drag_winner_continue(event: TouchpadEvent) {
875 let winner: Box<dyn gesture_arena::Winner> = Box::new(ButtonDownWinner {
876 spurious_to_intentional_motion_threshold_mm:
877 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
878 spurious_to_intentional_motion_threshold_button_change_mm:
879 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
880 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
881 pressed_event: TouchpadEvent {
882 timestamp: zx::MonotonicInstant::ZERO,
883 pressed_buttons: vec![1],
884 contacts: vec![make_touch_contact(1, Position { x: 0.0, y: 0.0 })],
885 filtered_palm_contacts: vec![],
886 },
887 });
888
889 let got = winner.process_new_event(event);
890 assert_matches!(got, ProcessNewEventResult::ContinueGesture(Some(MouseEvent {mouse_data, ..}), got_winner)=>{
891 pretty_assertions::assert_eq!(mouse_data.phase, mouse_binding::MousePhase::Move);
892 pretty_assertions::assert_eq!(got_winner.get_type_name(), "input_pipeline_lib_test::gestures::one_finger_button::DragWinner");
893 });
894 }
895
896 #[test_case(TouchpadEvent{
897 timestamp: zx::MonotonicInstant::from_nanos(41),
898 pressed_buttons: vec![],
899 contacts: vec![
900 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
901 ],
902 filtered_palm_contacts: vec![],
903 };"button release")]
904 #[test_case(TouchpadEvent{
905 timestamp: zx::MonotonicInstant::from_nanos(41),
906 pressed_buttons: vec![],
907 contacts: vec![
908 make_touch_contact(1, Position{x: 19.0, y: 1.0}),
909 ],
910 filtered_palm_contacts: vec![],
911 };"move and button release")]
912 #[fuchsia::test]
913 fn drag_winner_button_up(event: TouchpadEvent) {
914 let winner: Box<dyn gesture_arena::Winner> = Box::new(DragWinner {
915 spurious_to_intentional_motion_threshold_button_change_mm:
916 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
917 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
918 last_event: TouchpadEvent {
919 timestamp: zx::MonotonicInstant::ZERO,
920 pressed_buttons: vec![1],
921 contacts: vec![make_touch_contact(1, Position { x: 0.0, y: 0.0 })],
922 filtered_palm_contacts: vec![],
923 },
924 });
925
926 let got = winner.process_new_event(event);
927 assert_matches!(got, ProcessNewEventResult::ContinueGesture(Some(MouseEvent {mouse_data, ..}), got_winner) => {
928 pretty_assertions::assert_eq!(mouse_data, mouse_binding::MouseEvent::new(
929 mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
930 millimeters: Position { x: 0.0, y: 0.0 },
931 }),
932 None,
933 None,
934 mouse_binding::MousePhase::Up,
935 hashset!{1},
936 hashset!{},
937 None,
938 ));
939 pretty_assertions::assert_eq!(got_winner.get_type_name(), "input_pipeline_lib_test::gestures::one_finger_button::ButtonUpWinner");
940 });
941 }
942
943 #[fuchsia::test]
944 fn drag_winner_continue() {
945 let winner: Box<dyn gesture_arena::Winner> = Box::new(DragWinner {
946 spurious_to_intentional_motion_threshold_button_change_mm:
947 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
948 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
949 last_event: TouchpadEvent {
950 timestamp: zx::MonotonicInstant::ZERO,
951 pressed_buttons: vec![1],
952 contacts: vec![make_touch_contact(1, Position { x: 0.0, y: 0.0 })],
953 filtered_palm_contacts: vec![],
954 },
955 });
956
957 let event = TouchpadEvent {
958 timestamp: zx::MonotonicInstant::from_nanos(41),
959 pressed_buttons: vec![1],
960 contacts: vec![make_touch_contact(1, Position { x: 19.0, y: 1.0 })],
961 filtered_palm_contacts: vec![],
962 };
963
964 let got = winner.process_new_event(event);
965 assert_matches!(got, ProcessNewEventResult::ContinueGesture(Some(MouseEvent {mouse_data, ..}), got_winner)=>{
966 pretty_assertions::assert_eq!(mouse_data, mouse_binding::MouseEvent::new(
967 mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
968 millimeters: Position { x: 19.0, y: 1.0 },
969 }),
970 None,
971 None,
972 mouse_binding::MousePhase::Move,
973 hashset!{},
974 hashset!{1},
975 None,
976 ));
977 pretty_assertions::assert_eq!(got_winner.get_type_name(), "input_pipeline_lib_test::gestures::one_finger_button::DragWinner");
978 });
979 }
980
981 #[fuchsia::test]
982 fn drag_winner_continue_2_finger() {
983 let mut winner: Box<dyn gesture_arena::Winner> = Box::new(DragWinner {
984 spurious_to_intentional_motion_threshold_button_change_mm:
985 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
986 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
987 last_event: TouchpadEvent {
988 timestamp: zx::MonotonicInstant::ZERO,
989 pressed_buttons: vec![1],
990 contacts: vec![make_touch_contact(1, Position { x: 0.0, y: 0.0 })],
991 filtered_palm_contacts: vec![],
992 },
993 });
994
995 let event = TouchpadEvent {
996 timestamp: zx::MonotonicInstant::from_nanos(41),
997 pressed_buttons: vec![1],
998 contacts: vec![
999 make_touch_contact(1, Position { x: 0.0, y: 0.0 }),
1000 make_touch_contact(2, Position { x: 0.0, y: 5.0 }),
1001 ],
1002 filtered_palm_contacts: vec![],
1003 };
1004
1005 let got = winner.process_new_event(event);
1006 assert_matches!(got, ProcessNewEventResult::ContinueGesture(Some(MouseEvent {mouse_data, ..}), got_winner)=>{
1007 pretty_assertions::assert_eq!(mouse_data, mouse_binding::MouseEvent::new(
1008 mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
1009 millimeters: Position { x: 0.0, y: 0.0 },
1010 }),
1011 None,
1012 None,
1013 mouse_binding::MousePhase::Move,
1014 hashset!{},
1015 hashset!{1},
1016 None,
1017 ));
1018 winner = got_winner;
1019 pretty_assertions::assert_eq!(winner.get_type_name(), "input_pipeline_lib_test::gestures::one_finger_button::DragWinner");
1020 });
1021
1022 let event = TouchpadEvent {
1023 timestamp: zx::MonotonicInstant::from_nanos(41),
1024 pressed_buttons: vec![1],
1025 contacts: vec![
1026 make_touch_contact(1, Position { x: 0.0, y: 0.0 }),
1027 make_touch_contact(2, Position { x: 19.0, y: 5.0 }),
1028 ],
1029 filtered_palm_contacts: vec![],
1030 };
1031
1032 let got = winner.process_new_event(event);
1033 assert_matches!(got, ProcessNewEventResult::ContinueGesture(Some(MouseEvent {mouse_data, ..}), got_winner)=>{
1034 pretty_assertions::assert_eq!(mouse_data, mouse_binding::MouseEvent::new(
1035 mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
1036 millimeters: Position { x: 19.0, y: 0.0 },
1037 }),
1038 None,
1039 None,
1040 mouse_binding::MousePhase::Move,
1041 hashset!{},
1042 hashset!{1},
1043 None,
1044 ));
1045 pretty_assertions::assert_eq!(got_winner.get_type_name(), "input_pipeline_lib_test::gestures::one_finger_button::DragWinner");
1046 });
1047 }
1048
1049 #[fuchsia::test]
1050 fn button_up_winner_continue() {
1051 let winner: Box<dyn gesture_arena::Winner> = Box::new(ButtonUpWinner {
1052 spurious_to_intentional_motion_threshold_button_change_mm:
1053 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
1054 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
1055 button_up_event: TouchpadEvent {
1056 timestamp: zx::MonotonicInstant::ZERO,
1057 pressed_buttons: vec![],
1058 contacts: vec![make_touch_contact(1, Position { x: 0.0, y: 0.0 })],
1059 filtered_palm_contacts: vec![],
1060 },
1061 });
1062
1063 let event = TouchpadEvent {
1064 timestamp: zx::MonotonicInstant::from_nanos(41),
1065 pressed_buttons: vec![],
1066 contacts: vec![make_touch_contact(1, Position { x: 10.0, y: 1.0 })],
1067 filtered_palm_contacts: vec![],
1068 };
1069
1070 let got = winner.process_new_event(event);
1071 assert_matches!(got, ProcessNewEventResult::ContinueGesture(None, got_winner)=>{
1072 pretty_assertions::assert_eq!(got_winner.get_type_name(), "input_pipeline_lib_test::gestures::one_finger_button::ButtonUpWinner");
1073 });
1074 }
1075
1076 #[test_case(TouchpadEvent{
1077 timestamp: zx::MonotonicInstant::ZERO + zx::MonotonicDuration::from_millis(1_001),
1078 pressed_buttons: vec![],
1079 contacts: vec![
1080 make_touch_contact(1, Position{x: 0.0, y: 0.0}),
1081 ],
1082 filtered_palm_contacts: vec![],
1083 };"timeout")]
1084 #[test_case(TouchpadEvent{
1085 timestamp: zx::MonotonicInstant::from_nanos(41),
1086 pressed_buttons: vec![1],
1087 contacts: vec![
1088 make_touch_contact(1, Position{x: 0.0, y: 0.0}),
1089 ],
1090 filtered_palm_contacts: vec![],
1091 };"button down")]
1092 #[test_case(TouchpadEvent{
1093 timestamp: zx::MonotonicInstant::from_nanos(41),
1094 pressed_buttons: vec![],
1095 contacts: vec![
1096 make_touch_contact(1, Position{x: 21.0, y: 0.0}),
1097 ],
1098 filtered_palm_contacts: vec![],
1099 };"move more than threshold")]
1100 #[test_case(TouchpadEvent{
1101 timestamp: zx::MonotonicInstant::from_nanos(41),
1102 pressed_buttons: vec![],
1103 contacts: vec![
1104 make_touch_contact(1, Position{x: 0.0, y: 0.0}),
1105 make_touch_contact(2, Position{x: 10.0, y: 10.0}),
1106 ],
1107 filtered_palm_contacts: vec![],
1108 };"more contacts")]
1109 #[test_case(TouchpadEvent{
1110 timestamp: zx::MonotonicInstant::from_nanos(41),
1111 pressed_buttons: vec![],
1112 contacts: vec![],
1113 filtered_palm_contacts: vec![],
1114 };"no contact")]
1115 #[fuchsia::test]
1116 fn button_up_winner_end(event: TouchpadEvent) {
1117 let winner: Box<dyn gesture_arena::Winner> = Box::new(ButtonUpWinner {
1118 spurious_to_intentional_motion_threshold_button_change_mm:
1119 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
1120 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
1121 button_up_event: TouchpadEvent {
1122 timestamp: zx::MonotonicInstant::ZERO,
1123 pressed_buttons: vec![],
1124 contacts: vec![make_touch_contact(1, Position { x: 0.0, y: 0.0 })],
1125 filtered_palm_contacts: vec![],
1126 },
1127 });
1128
1129 let got = winner.process_new_event(event);
1130 assert_matches!(
1131 got,
1132 ProcessNewEventResult::EndGesture(EndGestureEvent::UnconsumedEvent(_), _)
1133 );
1134 }
1135}