1use super::gesture_arena::{
6 self, DetailedReasonFloat, DetailedReasonUint, EndGestureEvent, ExamineEventResult, MouseEvent,
7 ProcessBufferedEventsResult, ProcessNewEventResult, Reason, RecognizedGesture, TouchpadEvent,
8 VerifyEventResult,
9};
10use super::utils::{MovementDetail, movement_from_events};
11use crate::mouse_binding::{self, MouseButton};
12use crate::utils::{Position, euclidean_distance};
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 None,
502 ),
503 }
504}
505
506#[cfg(test)]
507mod tests {
508 use super::*;
509 use crate::touch_binding;
510 use assert_matches::assert_matches;
511 use test_case::test_case;
512
513 fn make_touch_contact(id: u32, position: Position) -> touch_binding::TouchContact {
514 touch_binding::TouchContact { id, position, pressure: None, contact_size: None }
515 }
516
517 const SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM: f32 = 10.0;
518 const SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM: f32 = 20.0;
519 const BUTTON_CHANGE_STATE_TIMEOUT: zx::MonotonicDuration =
520 zx::MonotonicDuration::from_seconds(1);
521
522 #[test_case(TouchpadEvent{
523 timestamp: zx::MonotonicInstant::ZERO,
524 pressed_buttons: vec![],
525 contacts: vec![],
526 filtered_palm_contacts: vec![],
527 };"0 fingers")]
528 #[test_case(TouchpadEvent{
529 timestamp: zx::MonotonicInstant::ZERO,
530 pressed_buttons: vec![],
531 contacts: vec![
532 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
533 make_touch_contact(2, Position{x: 5.0, y: 5.0}),
534 ],
535 filtered_palm_contacts: vec![],
536 };"2 fingers")]
537 #[fuchsia::test]
538 fn initial_contender_examine_event_mismatch(event: TouchpadEvent) {
539 let contender: Box<dyn gesture_arena::Contender> = Box::new(InitialContender {
540 spurious_to_intentional_motion_threshold_mm:
541 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
542 spurious_to_intentional_motion_threshold_button_change_mm:
543 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
544 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
545 });
546
547 let got = contender.examine_event(&event);
548 assert_matches!(got, ExamineEventResult::Mismatch(_));
549 }
550
551 #[fuchsia::test]
552 fn initial_contender_examine_event_finger_contact_contender() {
553 let contender: Box<dyn gesture_arena::Contender> = Box::new(InitialContender {
554 spurious_to_intentional_motion_threshold_mm:
555 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
556 spurious_to_intentional_motion_threshold_button_change_mm:
557 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
558 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
559 });
560 let event = TouchpadEvent {
561 timestamp: zx::MonotonicInstant::ZERO,
562 pressed_buttons: vec![],
563 contacts: vec![make_touch_contact(1, Position { x: 1.0, y: 1.0 })],
564 filtered_palm_contacts: vec![],
565 };
566
567 let got = contender.examine_event(&event);
568 assert_matches!(got, ExamineEventResult::Contender(_));
569 }
570
571 #[fuchsia::test]
572 fn initial_contender_examine_event_matched_contender() {
573 let contender: Box<dyn gesture_arena::Contender> = Box::new(InitialContender {
574 spurious_to_intentional_motion_threshold_mm:
575 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
576 spurious_to_intentional_motion_threshold_button_change_mm:
577 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
578 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
579 });
580 let event = TouchpadEvent {
581 timestamp: zx::MonotonicInstant::ZERO,
582 pressed_buttons: vec![1],
583 contacts: vec![make_touch_contact(1, Position { x: 1.0, y: 1.0 })],
584 filtered_palm_contacts: vec![],
585 };
586
587 let got = contender.examine_event(&event);
588 assert_matches!(got, ExamineEventResult::MatchedContender(_));
589 }
590
591 #[test_case(TouchpadEvent{
592 timestamp: zx::MonotonicInstant::ZERO,
593 pressed_buttons: vec![],
594 contacts: vec![],
595 filtered_palm_contacts: vec![],
596 };"0 fingers")]
597 #[test_case(TouchpadEvent{
598 timestamp: zx::MonotonicInstant::ZERO,
599 pressed_buttons: vec![],
600 contacts: vec![
601 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
602 make_touch_contact(2, Position{x: 5.0, y: 5.0}),
603 ],
604 filtered_palm_contacts: vec![],
605 };"2 fingers")]
606 #[test_case(TouchpadEvent{
607 timestamp: zx::MonotonicInstant::ZERO,
608 pressed_buttons: vec![],
609 contacts: vec![
610 make_touch_contact(1, Position{x: 10.0, y: 1.0}),
611 ],
612 filtered_palm_contacts: vec![],
613 };"1 fingers move more than threshold")]
614 #[fuchsia::test]
615 fn finger_contact_contender_examine_event_mismatch(event: TouchpadEvent) {
616 let contender: Box<dyn gesture_arena::Contender> = Box::new(FingerContactContender {
617 spurious_to_intentional_motion_threshold_mm:
618 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
619 spurious_to_intentional_motion_threshold_button_change_mm:
620 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
621 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
622 initial_position: Position { x: 0.0, y: 0.0 },
623 });
624
625 let got = contender.examine_event(&event);
626 assert_matches!(got, ExamineEventResult::Mismatch(_));
627 }
628
629 #[test_case(TouchpadEvent{
630 timestamp: zx::MonotonicInstant::ZERO,
631 pressed_buttons: vec![],
632 contacts: vec![
633 make_touch_contact(1, Position{x: 9.0, y: 1.0}),
634 ],
635 filtered_palm_contacts: vec![],
636 };"1 fingers move less than threshold")]
637 #[test_case(TouchpadEvent{
638 timestamp: zx::MonotonicInstant::ZERO,
639 pressed_buttons: vec![],
640 contacts: vec![
641 make_touch_contact(1, Position{x: 0.0, y: 0.0}),
642 ],
643 filtered_palm_contacts: vec![],
644 };"1 fingers stay")]
645 #[fuchsia::test]
646 fn finger_contact_contender_examine_event_finger_contact_contender(event: TouchpadEvent) {
647 let contender: Box<dyn gesture_arena::Contender> = Box::new(FingerContactContender {
648 spurious_to_intentional_motion_threshold_mm:
649 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
650 spurious_to_intentional_motion_threshold_button_change_mm:
651 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
652 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
653 initial_position: Position { x: 0.0, y: 0.0 },
654 });
655
656 let got = contender.examine_event(&event);
657 assert_matches!(got, ExamineEventResult::Contender(_));
658 }
659
660 #[fuchsia::test]
661 fn finger_contact_contender_examine_event_matched_contender() {
662 let contender: Box<dyn gesture_arena::Contender> = Box::new(FingerContactContender {
663 spurious_to_intentional_motion_threshold_mm:
664 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
665 spurious_to_intentional_motion_threshold_button_change_mm:
666 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
667 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
668 initial_position: Position { x: 0.0, y: 0.0 },
669 });
670 let event = TouchpadEvent {
671 timestamp: zx::MonotonicInstant::ZERO,
672 pressed_buttons: vec![1],
673 contacts: vec![make_touch_contact(1, Position { x: 1.0, y: 1.0 })],
674 filtered_palm_contacts: vec![],
675 };
676
677 let got = contender.examine_event(&event);
678 assert_matches!(got, ExamineEventResult::MatchedContender(_));
679 }
680
681 #[fuchsia::test]
682 fn matched_contender_process_buffered_events() {
683 let contender: Box<dyn gesture_arena::MatchedContender> = Box::new(MatchedContender {
684 spurious_to_intentional_motion_threshold_mm:
685 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
686 spurious_to_intentional_motion_threshold_button_change_mm:
687 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
688 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
689 pressed_event: TouchpadEvent {
690 timestamp: zx::MonotonicInstant::from_nanos(41),
691 pressed_buttons: vec![1],
692 contacts: vec![make_touch_contact(1, Position { x: 1.0, y: 1.0 })],
693 filtered_palm_contacts: vec![],
694 },
695 });
696 let events = vec![
697 TouchpadEvent {
698 timestamp: zx::MonotonicInstant::ZERO,
699 pressed_buttons: vec![],
700 contacts: vec![make_touch_contact(1, Position { x: 1.0, y: 1.0 })],
701 filtered_palm_contacts: vec![],
702 },
703 TouchpadEvent {
704 timestamp: zx::MonotonicInstant::from_nanos(41),
705 pressed_buttons: vec![1],
706 contacts: vec![make_touch_contact(1, Position { x: 1.0, y: 1.0 })],
707 filtered_palm_contacts: vec![],
708 },
709 ];
710
711 let got = contender.process_buffered_events(events);
712
713 assert_matches!(got, ProcessBufferedEventsResult{
714 generated_events,
715 winner: Some(winner),
716 recognized_gesture: RecognizedGesture::OneButtonDown,
717 } => {
718 pretty_assertions::assert_eq!(generated_events, vec![
719 MouseEvent {
720 timestamp:zx::MonotonicInstant::from_nanos(41),
721 mouse_data: mouse_binding::MouseEvent::new(
722 mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
723 millimeters: Position { x: 0.0, y: 0.0 },
724 }),
725 None,
726 None,
727 mouse_binding::MousePhase::Down,
728 hashset!{1},
729 hashset!{1},
730 None,
731 None,
732 ),
733 }
734 ]);
735 pretty_assertions::assert_eq!(winner.get_type_name(), "input_pipeline_lib_test::gestures::one_finger_button::ButtonDownWinner");
736 });
737 }
738
739 #[test_case(TouchpadEvent{
740 timestamp: zx::MonotonicInstant::from_nanos(41),
741 pressed_buttons: vec![],
742 contacts: vec![
743 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
744 ],
745 filtered_palm_contacts: vec![],
746 };"button release")]
747 #[test_case(TouchpadEvent{
748 timestamp: zx::MonotonicInstant::from_nanos(41),
749 pressed_buttons: vec![],
750 contacts: vec![
751 make_touch_contact(1, Position{x: 19.0, y: 1.0}),
752 ],
753 filtered_palm_contacts: vec![],
754 };"move less than threshold in edge state")]
755 #[test_case(TouchpadEvent{
756 timestamp: zx::MonotonicInstant::ZERO + zx::MonotonicDuration::from_millis(1500),
757 pressed_buttons: vec![],
758 contacts: vec![
759 make_touch_contact(1, Position{x: 9.0, y: 1.0}),
760 ],
761 filtered_palm_contacts: vec![],
762 };"move less than threshold out of edge state")]
763 #[test_case(TouchpadEvent{
764 timestamp: zx::MonotonicInstant::from_nanos(41),
765 pressed_buttons: vec![],
766 contacts: vec![
767 make_touch_contact(1, Position{x: 20.0, y: 1.0}),
768 ],
769 filtered_palm_contacts: vec![],
770 };"move more than threshold in edge state and release button")]
771 #[test_case(TouchpadEvent{
772 timestamp: zx::MonotonicInstant::ZERO + zx::MonotonicDuration::from_millis(1500),
773 pressed_buttons: vec![],
774 contacts: vec![
775 make_touch_contact(1, Position{x: 10.0, y: 1.0}),
776 ],
777 filtered_palm_contacts: vec![],
778 };"move more than threshold out of edge state and release button")]
779 #[fuchsia::test]
780 fn button_down_winner_button_up(event: TouchpadEvent) {
781 let winner: Box<dyn gesture_arena::Winner> = Box::new(ButtonDownWinner {
782 spurious_to_intentional_motion_threshold_mm:
783 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
784 spurious_to_intentional_motion_threshold_button_change_mm:
785 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
786 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
787 pressed_event: TouchpadEvent {
788 timestamp: zx::MonotonicInstant::ZERO,
789 pressed_buttons: vec![1],
790 contacts: vec![make_touch_contact(1, Position { x: 0.0, y: 0.0 })],
791 filtered_palm_contacts: vec![],
792 },
793 });
794
795 let got = winner.process_new_event(event);
796 assert_matches!(got, ProcessNewEventResult::ContinueGesture(Some(MouseEvent {mouse_data, ..}), got_winner) => {
797 pretty_assertions::assert_eq!(mouse_data, mouse_binding::MouseEvent::new(
798 mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
799 millimeters: Position { x: 0.0, y: 0.0 },
800 }),
801 None,
802 None,
803 mouse_binding::MousePhase::Up,
804 hashset!{1},
805 hashset!{},
806 None,
807 None,
808 ));
809 pretty_assertions::assert_eq!(got_winner.get_type_name(), "input_pipeline_lib_test::gestures::one_finger_button::ButtonUpWinner");
810 });
811 }
812
813 #[test_case(TouchpadEvent{
814 timestamp: zx::MonotonicInstant::from_nanos(41),
815 pressed_buttons: vec![1],
816 contacts: vec![
817 make_touch_contact(1, Position{x: 19.0, y: 1.0}),
818 ],
819 filtered_palm_contacts: vec![],
820 };"move less than threshold in edge state")]
821 #[test_case(TouchpadEvent{
822 timestamp: zx::MonotonicInstant::ZERO + zx::MonotonicDuration::from_millis(1500),
823 pressed_buttons: vec![1],
824 contacts: vec![
825 make_touch_contact(1, Position{x: 9.0, y: 1.0}),
826 ],
827 filtered_palm_contacts: vec![],
828 };"move less than threshold out of edge state")]
829 #[test_case(TouchpadEvent{
830 timestamp: zx::MonotonicInstant::from_nanos(41),
831 pressed_buttons: vec![1],
832 contacts: vec![
833 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
834 make_touch_contact(2, Position{x: 1.0, y: 2.0}),
835 ],
836 filtered_palm_contacts: vec![],
837 };"add more finger")]
838 #[fuchsia::test]
839 fn button_down_winner_continue(event: TouchpadEvent) {
840 let winner: Box<dyn gesture_arena::Winner> = Box::new(ButtonDownWinner {
841 spurious_to_intentional_motion_threshold_mm:
842 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
843 spurious_to_intentional_motion_threshold_button_change_mm:
844 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
845 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
846 pressed_event: TouchpadEvent {
847 timestamp: zx::MonotonicInstant::ZERO,
848 pressed_buttons: vec![1],
849 contacts: vec![make_touch_contact(1, Position { x: 0.0, y: 0.0 })],
850 filtered_palm_contacts: vec![],
851 },
852 });
853
854 let got = winner.process_new_event(event);
855 assert_matches!(got, ProcessNewEventResult::ContinueGesture(None, got_winner)=>{
856 pretty_assertions::assert_eq!(got_winner.get_type_name(), "input_pipeline_lib_test::gestures::one_finger_button::ButtonDownWinner");
857 });
858 }
859
860 #[test_case(TouchpadEvent{
861 timestamp: zx::MonotonicInstant::from_nanos(41),
862 pressed_buttons: vec![1],
863 contacts: vec![
864 make_touch_contact(1, Position{x: 20.0, y: 1.0}),
865 ],
866 filtered_palm_contacts: vec![],
867 };"move more than threshold in edge state")]
868 #[test_case(TouchpadEvent{
869 timestamp: zx::MonotonicInstant::ZERO + zx::MonotonicDuration::from_millis(1500),
870 pressed_buttons: vec![1],
871 contacts: vec![
872 make_touch_contact(1, Position{x: 10.0, y: 1.0}),
873 ],
874 filtered_palm_contacts: vec![],
875 };"move more than threshold out of edge state")]
876 #[fuchsia::test]
877 fn button_down_winner_drag_winner_continue(event: TouchpadEvent) {
878 let winner: Box<dyn gesture_arena::Winner> = Box::new(ButtonDownWinner {
879 spurious_to_intentional_motion_threshold_mm:
880 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
881 spurious_to_intentional_motion_threshold_button_change_mm:
882 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
883 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
884 pressed_event: TouchpadEvent {
885 timestamp: zx::MonotonicInstant::ZERO,
886 pressed_buttons: vec![1],
887 contacts: vec![make_touch_contact(1, Position { x: 0.0, y: 0.0 })],
888 filtered_palm_contacts: vec![],
889 },
890 });
891
892 let got = winner.process_new_event(event);
893 assert_matches!(got, ProcessNewEventResult::ContinueGesture(Some(MouseEvent {mouse_data, ..}), got_winner)=>{
894 pretty_assertions::assert_eq!(mouse_data.phase, mouse_binding::MousePhase::Move);
895 pretty_assertions::assert_eq!(got_winner.get_type_name(), "input_pipeline_lib_test::gestures::one_finger_button::DragWinner");
896 });
897 }
898
899 #[test_case(TouchpadEvent{
900 timestamp: zx::MonotonicInstant::from_nanos(41),
901 pressed_buttons: vec![],
902 contacts: vec![
903 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
904 ],
905 filtered_palm_contacts: vec![],
906 };"button release")]
907 #[test_case(TouchpadEvent{
908 timestamp: zx::MonotonicInstant::from_nanos(41),
909 pressed_buttons: vec![],
910 contacts: vec![
911 make_touch_contact(1, Position{x: 19.0, y: 1.0}),
912 ],
913 filtered_palm_contacts: vec![],
914 };"move and button release")]
915 #[fuchsia::test]
916 fn drag_winner_button_up(event: TouchpadEvent) {
917 let winner: Box<dyn gesture_arena::Winner> = Box::new(DragWinner {
918 spurious_to_intentional_motion_threshold_button_change_mm:
919 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
920 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
921 last_event: TouchpadEvent {
922 timestamp: zx::MonotonicInstant::ZERO,
923 pressed_buttons: vec![1],
924 contacts: vec![make_touch_contact(1, Position { x: 0.0, y: 0.0 })],
925 filtered_palm_contacts: vec![],
926 },
927 });
928
929 let got = winner.process_new_event(event);
930 assert_matches!(got, ProcessNewEventResult::ContinueGesture(Some(MouseEvent {mouse_data, ..}), got_winner) => {
931 pretty_assertions::assert_eq!(mouse_data, mouse_binding::MouseEvent::new(
932 mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
933 millimeters: Position { x: 0.0, y: 0.0 },
934 }),
935 None,
936 None,
937 mouse_binding::MousePhase::Up,
938 hashset!{1},
939 hashset!{},
940 None,
941 None,
942 ));
943 pretty_assertions::assert_eq!(got_winner.get_type_name(), "input_pipeline_lib_test::gestures::one_finger_button::ButtonUpWinner");
944 });
945 }
946
947 #[fuchsia::test]
948 fn drag_winner_continue() {
949 let winner: Box<dyn gesture_arena::Winner> = Box::new(DragWinner {
950 spurious_to_intentional_motion_threshold_button_change_mm:
951 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
952 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
953 last_event: TouchpadEvent {
954 timestamp: zx::MonotonicInstant::ZERO,
955 pressed_buttons: vec![1],
956 contacts: vec![make_touch_contact(1, Position { x: 0.0, y: 0.0 })],
957 filtered_palm_contacts: vec![],
958 },
959 });
960
961 let event = TouchpadEvent {
962 timestamp: zx::MonotonicInstant::from_nanos(41),
963 pressed_buttons: vec![1],
964 contacts: vec![make_touch_contact(1, Position { x: 19.0, y: 1.0 })],
965 filtered_palm_contacts: vec![],
966 };
967
968 let got = winner.process_new_event(event);
969 assert_matches!(got, ProcessNewEventResult::ContinueGesture(Some(MouseEvent {mouse_data, ..}), got_winner)=>{
970 pretty_assertions::assert_eq!(mouse_data, mouse_binding::MouseEvent::new(
971 mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
972 millimeters: Position { x: 19.0, y: 1.0 },
973 }),
974 None,
975 None,
976 mouse_binding::MousePhase::Move,
977 hashset!{},
978 hashset!{1},
979 None,
980 None,
981 ));
982 pretty_assertions::assert_eq!(got_winner.get_type_name(), "input_pipeline_lib_test::gestures::one_finger_button::DragWinner");
983 });
984 }
985
986 #[fuchsia::test]
987 fn drag_winner_continue_2_finger() {
988 let mut winner: Box<dyn gesture_arena::Winner> = Box::new(DragWinner {
989 spurious_to_intentional_motion_threshold_button_change_mm:
990 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
991 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
992 last_event: TouchpadEvent {
993 timestamp: zx::MonotonicInstant::ZERO,
994 pressed_buttons: vec![1],
995 contacts: vec![make_touch_contact(1, Position { x: 0.0, y: 0.0 })],
996 filtered_palm_contacts: vec![],
997 },
998 });
999
1000 let event = TouchpadEvent {
1001 timestamp: zx::MonotonicInstant::from_nanos(41),
1002 pressed_buttons: vec![1],
1003 contacts: vec![
1004 make_touch_contact(1, Position { x: 0.0, y: 0.0 }),
1005 make_touch_contact(2, Position { x: 0.0, y: 5.0 }),
1006 ],
1007 filtered_palm_contacts: vec![],
1008 };
1009
1010 let got = winner.process_new_event(event);
1011 assert_matches!(got, ProcessNewEventResult::ContinueGesture(Some(MouseEvent {mouse_data, ..}), got_winner)=>{
1012 pretty_assertions::assert_eq!(mouse_data, mouse_binding::MouseEvent::new(
1013 mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
1014 millimeters: Position { x: 0.0, y: 0.0 },
1015 }),
1016 None,
1017 None,
1018 mouse_binding::MousePhase::Move,
1019 hashset!{},
1020 hashset!{1},
1021 None,
1022 None,
1023 ));
1024 winner = got_winner;
1025 pretty_assertions::assert_eq!(winner.get_type_name(), "input_pipeline_lib_test::gestures::one_finger_button::DragWinner");
1026 });
1027
1028 let event = TouchpadEvent {
1029 timestamp: zx::MonotonicInstant::from_nanos(41),
1030 pressed_buttons: vec![1],
1031 contacts: vec![
1032 make_touch_contact(1, Position { x: 0.0, y: 0.0 }),
1033 make_touch_contact(2, Position { x: 19.0, y: 5.0 }),
1034 ],
1035 filtered_palm_contacts: vec![],
1036 };
1037
1038 let got = winner.process_new_event(event);
1039 assert_matches!(got, ProcessNewEventResult::ContinueGesture(Some(MouseEvent {mouse_data, ..}), got_winner)=>{
1040 pretty_assertions::assert_eq!(mouse_data, mouse_binding::MouseEvent::new(
1041 mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
1042 millimeters: Position { x: 19.0, y: 0.0 },
1043 }),
1044 None,
1045 None,
1046 mouse_binding::MousePhase::Move,
1047 hashset!{},
1048 hashset!{1},
1049 None,
1050 None,
1051 ));
1052 pretty_assertions::assert_eq!(got_winner.get_type_name(), "input_pipeline_lib_test::gestures::one_finger_button::DragWinner");
1053 });
1054 }
1055
1056 #[fuchsia::test]
1057 fn button_up_winner_continue() {
1058 let winner: Box<dyn gesture_arena::Winner> = Box::new(ButtonUpWinner {
1059 spurious_to_intentional_motion_threshold_button_change_mm:
1060 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
1061 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
1062 button_up_event: TouchpadEvent {
1063 timestamp: zx::MonotonicInstant::ZERO,
1064 pressed_buttons: vec![],
1065 contacts: vec![make_touch_contact(1, Position { x: 0.0, y: 0.0 })],
1066 filtered_palm_contacts: vec![],
1067 },
1068 });
1069
1070 let event = TouchpadEvent {
1071 timestamp: zx::MonotonicInstant::from_nanos(41),
1072 pressed_buttons: vec![],
1073 contacts: vec![make_touch_contact(1, Position { x: 10.0, y: 1.0 })],
1074 filtered_palm_contacts: vec![],
1075 };
1076
1077 let got = winner.process_new_event(event);
1078 assert_matches!(got, ProcessNewEventResult::ContinueGesture(None, got_winner)=>{
1079 pretty_assertions::assert_eq!(got_winner.get_type_name(), "input_pipeline_lib_test::gestures::one_finger_button::ButtonUpWinner");
1080 });
1081 }
1082
1083 #[test_case(TouchpadEvent{
1084 timestamp: zx::MonotonicInstant::ZERO + zx::MonotonicDuration::from_millis(1_001),
1085 pressed_buttons: vec![],
1086 contacts: vec![
1087 make_touch_contact(1, Position{x: 0.0, y: 0.0}),
1088 ],
1089 filtered_palm_contacts: vec![],
1090 };"timeout")]
1091 #[test_case(TouchpadEvent{
1092 timestamp: zx::MonotonicInstant::from_nanos(41),
1093 pressed_buttons: vec![1],
1094 contacts: vec![
1095 make_touch_contact(1, Position{x: 0.0, y: 0.0}),
1096 ],
1097 filtered_palm_contacts: vec![],
1098 };"button down")]
1099 #[test_case(TouchpadEvent{
1100 timestamp: zx::MonotonicInstant::from_nanos(41),
1101 pressed_buttons: vec![],
1102 contacts: vec![
1103 make_touch_contact(1, Position{x: 21.0, y: 0.0}),
1104 ],
1105 filtered_palm_contacts: vec![],
1106 };"move more than threshold")]
1107 #[test_case(TouchpadEvent{
1108 timestamp: zx::MonotonicInstant::from_nanos(41),
1109 pressed_buttons: vec![],
1110 contacts: vec![
1111 make_touch_contact(1, Position{x: 0.0, y: 0.0}),
1112 make_touch_contact(2, Position{x: 10.0, y: 10.0}),
1113 ],
1114 filtered_palm_contacts: vec![],
1115 };"more contacts")]
1116 #[test_case(TouchpadEvent{
1117 timestamp: zx::MonotonicInstant::from_nanos(41),
1118 pressed_buttons: vec![],
1119 contacts: vec![],
1120 filtered_palm_contacts: vec![],
1121 };"no contact")]
1122 #[fuchsia::test]
1123 fn button_up_winner_end(event: TouchpadEvent) {
1124 let winner: Box<dyn gesture_arena::Winner> = Box::new(ButtonUpWinner {
1125 spurious_to_intentional_motion_threshold_button_change_mm:
1126 SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
1127 button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
1128 button_up_event: TouchpadEvent {
1129 timestamp: zx::MonotonicInstant::ZERO,
1130 pressed_buttons: vec![],
1131 contacts: vec![make_touch_contact(1, Position { x: 0.0, y: 0.0 })],
1132 filtered_palm_contacts: vec![],
1133 },
1134 });
1135
1136 let got = winner.process_new_event(event);
1137 assert_matches!(
1138 got,
1139 ProcessNewEventResult::EndGesture(EndGestureEvent::UnconsumedEvent(_), _)
1140 );
1141 }
1142}