1use super::gesture_arena::{
6 self, DetailedReasonFloat, DetailedReasonUint, EndGestureEvent, ExamineEventResult, MouseEvent,
7 ProcessBufferedEventsResult, ProcessNewEventResult, Reason, RecognizedGesture, TouchpadEvent,
8 VerifyEventResult,
9};
10use crate::mouse_binding;
11use crate::utils::{euclidean_distance, Position};
12use maplit::hashset;
13
14#[derive(Debug)]
16pub(super) struct InitialContender {
17 pub(super) motion_threshold_in_mm: f32,
19
20 pub(super) min_movement_in_mm: f32,
22
23 pub(super) max_movement_in_mm: f32,
27
28 pub(super) limit_tangent_for_direction: f32,
31}
32
33#[derive(Debug)]
35struct OneFingerContactContender {
36 motion_threshold_in_mm: f32,
38
39 min_movement_in_mm: f32,
41
42 max_movement_in_mm: f32,
46
47 limit_tangent_for_direction: f32,
49
50 initial_position: ContactPosition,
52}
53
54#[derive(Debug)]
56struct TwoFingerContactContender {
57 min_movement_in_mm: f32,
59
60 max_movement_in_mm: f32,
64
65 limit_tangent_for_direction: f32,
67
68 initial_positions: ContactPositions,
70}
71
72#[derive(Debug, PartialEq, Clone, Copy)]
73enum ScrollDirection {
74 Left,
75 Right,
76 Up,
77 Down,
78}
79
80#[derive(Debug, PartialEq)]
83struct MatchedContender {
84 limit_tangent_for_direction: f32,
86
87 initial_positions: ContactPositions,
89
90 direction: ScrollDirection,
92}
93
94#[derive(Debug)]
96struct Winner {
97 limit_tangent_for_direction: f32,
99
100 direction: ScrollDirection,
102
103 last_positions: ContactPositions,
105}
106
107enum Error {
108 FingerNotMatch,
109 MustBe2Finger,
110}
111
112#[derive(Debug, PartialEq, Clone, Copy)]
113struct ContactPosition {
114 id: u32,
115 position: Position,
116}
117
118#[derive(Debug, PartialEq)]
119struct ContactPositions {
120 first_contact: ContactPosition,
121 second_contact: ContactPosition,
122}
123
124impl ContactPositions {
125 fn from(event: &TouchpadEvent) -> Result<Self, Error> {
126 let mut contact_positions: Vec<ContactPosition> = Vec::new();
127 for c in &event.contacts {
128 contact_positions.push(ContactPosition { id: c.id, position: c.position.clone() });
129 }
130
131 if contact_positions.len() != 2 {
132 return Err(Error::MustBe2Finger);
133 }
134 contact_positions.sort_by_key(|a| a.id);
135 Ok(ContactPositions {
136 first_contact: contact_positions[0],
137 second_contact: contact_positions[1],
138 })
139 }
140
141 fn get_movements(&self, other: &Self) -> Result<Vec<Movement>, Error> {
142 if self.first_contact.id != other.first_contact.id
143 || self.second_contact.id != other.second_contact.id
144 {
145 return Err(Error::FingerNotMatch);
146 }
147
148 Ok(vec![
149 Movement { from: self.first_contact.position, to: other.first_contact.position },
150 Movement { from: self.second_contact.position, to: other.second_contact.position },
151 ])
152 }
153}
154
155struct Movement {
157 from: Position,
159 to: Position,
161}
162
163impl Movement {
164 fn is_in_direction(
165 &self,
166 direction: ScrollDirection,
167 limit_tangent_for_direction: f32,
168 ) -> bool {
169 let dx = self.to.x - self.from.x;
170 let dy = self.to.y - self.from.y;
171
172 match direction {
174 ScrollDirection::Left => {
175 if dx >= 0.0 {
176 return false;
177 }
178 }
179 ScrollDirection::Right => {
180 if dx <= 0.0 {
181 return false;
182 }
183 }
184 ScrollDirection::Up => {
185 if dy >= 0.0 {
186 return false;
187 }
188 }
189 ScrollDirection::Down => {
190 if dy <= 0.0 {
191 return false;
192 }
193 }
194 }
195
196 let (long, short) = match direction {
197 ScrollDirection::Left => (dx.abs(), dy.abs()),
198 ScrollDirection::Right => (dx.abs(), dy.abs()),
199 ScrollDirection::Up => (dy.abs(), dx.abs()),
200 ScrollDirection::Down => (dy.abs(), dx.abs()),
201 };
202
203 short < (long * limit_tangent_for_direction)
204 }
205
206 fn direction(&self, limit_tangent_for_direction: f32) -> Option<ScrollDirection> {
207 let directions = [
208 ScrollDirection::Left,
209 ScrollDirection::Right,
210 ScrollDirection::Up,
211 ScrollDirection::Down,
212 ];
213 for d in directions {
214 if self.is_in_direction(d, limit_tangent_for_direction) {
215 return Some(d);
216 }
217 }
218
219 None
220 }
221
222 fn has_delta_on_reverse_direction(&self, want_direction: ScrollDirection) -> bool {
223 let dx = self.to.x - self.from.x;
224 let dy = self.to.y - self.from.y;
225 match want_direction {
226 ScrollDirection::Up => dy > 0.0,
227 ScrollDirection::Down => dy < 0.0,
228 ScrollDirection::Left => dx > 0.0,
229 ScrollDirection::Right => dx < 0.0,
230 }
231 }
232}
233
234impl InitialContender {
235 #[allow(clippy::boxed_local, reason = "mass allow for https://fxbug.dev/381896734")]
236 fn into_one_finger_contact_contender(
237 self: Box<Self>,
238 initial_position: ContactPosition,
239 ) -> Box<dyn gesture_arena::Contender> {
240 Box::new(OneFingerContactContender {
241 motion_threshold_in_mm: self.motion_threshold_in_mm,
242 min_movement_in_mm: self.min_movement_in_mm,
243 max_movement_in_mm: self.max_movement_in_mm,
244 limit_tangent_for_direction: self.limit_tangent_for_direction,
245 initial_position,
246 })
247 }
248
249 #[allow(clippy::boxed_local, reason = "mass allow for https://fxbug.dev/381896734")]
250 fn into_two_finger_contact_contender(
251 self: Box<Self>,
252 initial_positions: ContactPositions,
253 ) -> Box<dyn gesture_arena::Contender> {
254 Box::new(TwoFingerContactContender {
255 min_movement_in_mm: self.min_movement_in_mm,
256 max_movement_in_mm: self.max_movement_in_mm,
257 limit_tangent_for_direction: self.limit_tangent_for_direction,
258 initial_positions,
259 })
260 }
261}
262
263impl gesture_arena::Contender for InitialContender {
264 fn examine_event(self: Box<Self>, event: &TouchpadEvent) -> ExamineEventResult {
265 let num_pressed_buttons = event.pressed_buttons.len();
266 if num_pressed_buttons > 0 {
267 return ExamineEventResult::Mismatch(Reason::DetailedUint(DetailedReasonUint {
268 criterion: "num_pressed_buttons",
269 min: None,
270 max: Some(0),
271 actual: num_pressed_buttons,
272 }));
273 }
274
275 let num_contacts = event.contacts.len();
276 match num_contacts {
277 1 => ExamineEventResult::Contender(self.into_one_finger_contact_contender(
278 ContactPosition { position: event.contacts[0].position, id: event.contacts[0].id },
279 )),
280 2 => {
281 match ContactPositions::from(event) {
282 Ok(positions) => {
283 return ExamineEventResult::Contender(
284 self.into_two_finger_contact_contender(positions),
285 )
286 }
287 Err(_) => {
288 log::error!("failed to parse positions");
289 return ExamineEventResult::Mismatch(Reason::Basic(
290 "failed to parse positions",
291 ));
292 }
293 };
294 }
295 _ => ExamineEventResult::Mismatch(Reason::DetailedUint(DetailedReasonUint {
296 criterion: "num_contacts",
297 min: Some(1),
298 max: Some(2),
299 actual: num_contacts,
300 })),
301 }
302 }
303}
304
305impl OneFingerContactContender {
306 #[allow(clippy::boxed_local, reason = "mass allow for https://fxbug.dev/381896734")]
307 fn into_two_finger_contact_contender(
308 self: Box<Self>,
309 initial_positions: ContactPositions,
310 ) -> Box<dyn gesture_arena::Contender> {
311 Box::new(TwoFingerContactContender {
312 min_movement_in_mm: self.min_movement_in_mm,
313 max_movement_in_mm: self.max_movement_in_mm,
314 limit_tangent_for_direction: self.limit_tangent_for_direction,
315 initial_positions,
316 })
317 }
318}
319
320impl gesture_arena::Contender for OneFingerContactContender {
321 fn examine_event(self: Box<Self>, event: &TouchpadEvent) -> ExamineEventResult {
322 let num_pressed_buttons = event.pressed_buttons.len();
323 if num_pressed_buttons > 0 {
324 return ExamineEventResult::Mismatch(Reason::DetailedUint(DetailedReasonUint {
325 criterion: "num_pressed_buttons",
326 min: None,
327 max: Some(0),
328 actual: num_pressed_buttons,
329 }));
330 }
331
332 let num_contacts = event.contacts.len();
333 if num_contacts > 0 {
335 match event.contacts.iter().find(|&c| c.id == self.initial_position.id) {
336 Some(contact) => {
337 let displacement_mm =
338 euclidean_distance(contact.position, self.initial_position.position);
339 if displacement_mm >= self.motion_threshold_in_mm {
340 return ExamineEventResult::Mismatch(Reason::DetailedFloat(
341 DetailedReasonFloat {
342 criterion: "displacement_mm",
343 min: None,
344 max: Some(self.motion_threshold_in_mm),
345 actual: displacement_mm,
346 },
347 ));
348 }
349 }
350 None => {
351 return ExamineEventResult::Mismatch(Reason::Basic("initial contact lift"));
352 }
353 }
354 }
355 match num_contacts {
356 1 => ExamineEventResult::Contender(self),
357 2 => {
358 let current_positions = match ContactPositions::from(event) {
359 Ok(positions) => positions,
360 Err(_) => {
361 log::error!("failed to parse positions");
362 return ExamineEventResult::Mismatch(Reason::Basic(
363 "failed to parse positions",
364 ));
365 }
366 };
367
368 ExamineEventResult::Contender(
369 self.into_two_finger_contact_contender(current_positions),
370 )
371 }
372 _ => ExamineEventResult::Mismatch(Reason::DetailedUint(DetailedReasonUint {
373 criterion: "num_contacts",
374 min: Some(1),
375 max: Some(2),
376 actual: num_contacts,
377 })),
378 }
379 }
380}
381
382impl TwoFingerContactContender {
383 #[allow(clippy::boxed_local, reason = "mass allow for https://fxbug.dev/381896734")]
384 fn into_matched_contender(
385 self: Box<Self>,
386 direction: ScrollDirection,
387 ) -> Box<dyn gesture_arena::MatchedContender> {
388 Box::new(MatchedContender {
389 limit_tangent_for_direction: self.limit_tangent_for_direction,
390 initial_positions: self.initial_positions,
391 direction,
392 })
393 }
394}
395
396impl gesture_arena::Contender for TwoFingerContactContender {
397 fn examine_event(self: Box<Self>, event: &TouchpadEvent) -> ExamineEventResult {
398 let num_contacts = event.contacts.len();
399 if num_contacts != 2 {
400 return ExamineEventResult::Mismatch(Reason::DetailedUint(DetailedReasonUint {
401 criterion: "num_contacts",
402 min: Some(2),
403 max: Some(2),
404 actual: num_contacts,
405 }));
406 }
407
408 let num_pressed_buttons = event.pressed_buttons.len();
409 if num_pressed_buttons > 0 {
410 return ExamineEventResult::Mismatch(Reason::DetailedUint(DetailedReasonUint {
411 criterion: "num_pressed_buttons",
412 min: None,
413 max: Some(0),
414 actual: num_pressed_buttons,
415 }));
416 }
417
418 let current_positions = match ContactPositions::from(event) {
419 Ok(positions) => positions,
420 Err(_) => {
421 log::error!("failed to parse positions");
422 return ExamineEventResult::Mismatch(Reason::Basic("failed to parse positions"));
423 }
424 };
425
426 let movements = match self.initial_positions.get_movements(¤t_positions) {
427 Err(_) => {
430 log::error!("new event contact id not match old event");
431 return ExamineEventResult::Mismatch(Reason::Basic(
432 "contact ids changed since last event",
433 ));
434 }
435 Ok(m) => m,
436 };
437
438 if movements.iter().any(|movement| {
440 euclidean_distance(movement.to, movement.from) < self.min_movement_in_mm
441 }) {
442 return ExamineEventResult::Contender(self);
443 }
444
445 let directions: Vec<Option<ScrollDirection>> =
446 movements.iter().map(|m| m.direction(self.limit_tangent_for_direction)).collect();
447 if let Some(first_direction) = directions[0] {
448 if directions.iter().all(|&d| d == directions[0]) {
449 return ExamineEventResult::MatchedContender(
450 self.into_matched_contender(first_direction),
451 );
452 }
453 }
454
455 if movements.iter().any(|movement| {
458 euclidean_distance(movement.to, movement.from) > self.max_movement_in_mm
459 }) {
460 return ExamineEventResult::Mismatch(Reason::Basic(
461 "too much motion without clear direction",
462 ));
463 }
464
465 ExamineEventResult::Contender(self)
466 }
467}
468
469impl MatchedContender {
470 #[allow(clippy::boxed_local, reason = "mass allow for https://fxbug.dev/381896734")]
471 fn into_winner(
472 self: Box<Self>,
473 last_positions: ContactPositions,
474 ) -> Box<dyn gesture_arena::Winner> {
475 Box::new(Winner {
476 limit_tangent_for_direction: self.limit_tangent_for_direction,
477 direction: self.direction,
478 last_positions,
479 })
480 }
481}
482
483impl gesture_arena::MatchedContender for MatchedContender {
484 fn verify_event(self: Box<Self>, event: &TouchpadEvent) -> VerifyEventResult {
485 let num_contacts = event.contacts.len();
486 if num_contacts != 2 {
487 return VerifyEventResult::Mismatch(Reason::DetailedUint(DetailedReasonUint {
488 criterion: "num_contacts",
489 min: Some(2),
490 max: Some(2),
491 actual: num_contacts,
492 }));
493 }
494
495 let num_pressed_buttons = event.pressed_buttons.len();
496 if num_pressed_buttons > 0 {
497 return VerifyEventResult::Mismatch(Reason::DetailedUint(DetailedReasonUint {
498 criterion: "num_pressed_buttons",
499 min: None,
500 max: Some(0),
501 actual: num_pressed_buttons,
502 }));
503 }
504
505 let current_positions = match ContactPositions::from(event) {
506 Ok(positions) => positions,
507 Err(_) => {
508 log::error!("failed to parse positions");
509 return VerifyEventResult::Mismatch(Reason::Basic("failed to parse positions"));
510 }
511 };
512 let movements = match self.initial_positions.get_movements(¤t_positions) {
513 Err(_) => {
516 log::error!("new event contact id not match old event");
517 return VerifyEventResult::Mismatch(Reason::Basic(
518 "contact ids changed since last event",
519 ));
520 }
521 Ok(m) => m,
522 };
523
524 let directions: Vec<Option<ScrollDirection>> =
525 movements.into_iter().map(|m| m.direction(self.limit_tangent_for_direction)).collect();
526
527 if directions.iter().all(|&d| d == Some(self.direction)) {
528 return VerifyEventResult::MatchedContender(self);
529 }
530
531 VerifyEventResult::Mismatch(Reason::Basic("contacts moved in different directions"))
532 }
533
534 fn process_buffered_events(
535 self: Box<Self>,
536 events: Vec<TouchpadEvent>,
537 ) -> ProcessBufferedEventsResult {
538 let mut mouse_events: Vec<MouseEvent> = Vec::new();
539
540 for pair in events.windows(2) {
541 if pair[0].contacts.len() != 2 {
546 continue;
547 }
548 assert_eq!(pair[1].contacts.len(), 2);
549 let old_positions = match ContactPositions::from(&pair[0]) {
550 Ok(positions) => positions,
551 Err(_) => ContactPositions {
552 first_contact: ContactPosition { id: 0, position: Position { x: 0.0, y: 0.0 } },
555 second_contact: ContactPosition {
556 id: 0,
557 position: Position { x: 0.0, y: 0.0 },
558 },
559 },
560 };
561 mouse_events.push(touchpad_event_to_mouse_scroll_event(
562 self.direction,
563 old_positions,
564 &pair[1],
565 ));
566 }
567
568 let last_positions = match ContactPositions::from(&events[events.len() - 1]) {
569 Ok(positions) => positions,
570 Err(_) => ContactPositions {
571 first_contact: ContactPosition { id: 0, position: Position { x: 0.0, y: 0.0 } },
574 second_contact: ContactPosition { id: 0, position: Position { x: 0.0, y: 0.0 } },
575 },
576 };
577
578 ProcessBufferedEventsResult {
579 generated_events: mouse_events,
580 winner: Some(self.into_winner(last_positions)),
581 recognized_gesture: RecognizedGesture::Scroll,
582 }
583 }
584}
585
586impl gesture_arena::Winner for Winner {
587 fn process_new_event(self: Box<Self>, event: TouchpadEvent) -> ProcessNewEventResult {
588 match u8::try_from(event.contacts.len()).unwrap_or(u8::MAX) {
589 0 => ProcessNewEventResult::EndGesture(
590 EndGestureEvent::NoEvent,
591 Reason::DetailedUint(DetailedReasonUint {
592 criterion: "num_contacts",
593 min: Some(2),
594 max: Some(2),
595 actual: 0,
596 }),
597 ),
598 2 => {
599 let num_pressed_buttons = event.pressed_buttons.len();
600 if num_pressed_buttons > 0 {
601 return ProcessNewEventResult::EndGesture(
602 EndGestureEvent::UnconsumedEvent(event),
603 Reason::DetailedUint(DetailedReasonUint {
604 criterion: "num_pressed_buttons",
605 min: None,
606 max: Some(0),
607 actual: num_pressed_buttons,
608 }),
609 );
610 }
611
612 let positions = match ContactPositions::from(&event) {
613 Ok(positions) => positions,
614 Err(_) => {
615 log::error!("failed to parse positions");
616 return ProcessNewEventResult::EndGesture(
617 EndGestureEvent::UnconsumedEvent(event),
618 Reason::Basic("failed to parse positions"),
619 );
620 }
621 };
622
623 let movements = match self.last_positions.get_movements(&positions) {
624 Err(_) => {
627 log::error!("new event contact id not match old event");
628 return ProcessNewEventResult::EndGesture(
629 EndGestureEvent::UnconsumedEvent(event),
630 Reason::Basic("contact ids changed since last event"),
631 );
632 }
633 Ok(m) => m,
634 };
635
636 if movements.iter().any(|m| m.has_delta_on_reverse_direction(self.direction)) {
637 return ProcessNewEventResult::EndGesture(
638 EndGestureEvent::UnconsumedEvent(event),
639 Reason::Basic("inconsistent direction"),
640 );
641 }
642
643 ProcessNewEventResult::ContinueGesture(
644 Some(touchpad_event_to_mouse_scroll_event(
645 self.direction,
646 self.last_positions,
647 &event,
648 )),
649 Box::new(Winner {
650 limit_tangent_for_direction: self.limit_tangent_for_direction,
651 direction: self.direction,
652 last_positions: positions,
653 }),
654 )
655 }
656 1 => ProcessNewEventResult::EndGesture(
657 EndGestureEvent::UnconsumedEvent(event),
658 Reason::DetailedUint(DetailedReasonUint {
659 criterion: "num_contacts",
660 min: Some(2),
661 max: Some(2),
662 actual: 1,
663 }),
664 ),
665 num_contacts @ 3.. => ProcessNewEventResult::EndGesture(
666 EndGestureEvent::UnconsumedEvent(event),
667 Reason::DetailedUint(DetailedReasonUint {
668 criterion: "num_contacts",
669 min: Some(2),
670 max: Some(2),
671 actual: usize::from(num_contacts),
672 }),
673 ),
674 }
675 }
676}
677
678fn wheel_delta_mm(delta: f32) -> Option<mouse_binding::WheelDelta> {
679 Some(mouse_binding::WheelDelta {
680 raw_data: mouse_binding::RawWheelDelta::Millimeters(delta),
681 physical_pixel: None,
682 })
683}
684
685fn filter_off_direction_movement(
687 direction: ScrollDirection,
688 offset_v: f32,
689 offset_h: f32,
690) -> (Option<mouse_binding::WheelDelta>, Option<mouse_binding::WheelDelta>) {
691 match direction {
692 ScrollDirection::Left => {
693 if offset_h > 0.0 {
694 (None, wheel_delta_mm(0.0))
695 } else {
696 (None, wheel_delta_mm(offset_h))
697 }
698 }
699 ScrollDirection::Right => {
700 if offset_h < 0.0 {
701 (None, wheel_delta_mm(0.0))
702 } else {
703 (None, wheel_delta_mm(offset_h))
704 }
705 }
706 ScrollDirection::Up => {
707 if offset_v > 0.0 {
708 (wheel_delta_mm(0.0), None)
709 } else {
710 (wheel_delta_mm(offset_v), None)
711 }
712 }
713 ScrollDirection::Down => {
714 if offset_v < 0.0 {
715 (wheel_delta_mm(0.0), None)
716 } else {
717 (wheel_delta_mm(offset_v), None)
718 }
719 }
720 }
721}
722
723fn touchpad_event_to_mouse_scroll_event(
724 direction: ScrollDirection,
725 old_positions: ContactPositions,
726 new_event: &TouchpadEvent,
727) -> MouseEvent {
728 let offset_v = (new_event.contacts[0].position.y + new_event.contacts[1].position.y
737 - old_positions.first_contact.position.y
738 - old_positions.second_contact.position.y)
739 / 2.0;
740 let offset_h = (new_event.contacts[0].position.x + new_event.contacts[1].position.x
741 - old_positions.first_contact.position.x
742 - old_positions.second_contact.position.x)
743 / 2.0;
744
745 let (wheel_delta_v, wheel_delta_h) =
748 filter_off_direction_movement(direction, offset_v, offset_h);
749 MouseEvent {
750 timestamp: new_event.timestamp,
751 mouse_data: mouse_binding::MouseEvent::new(
752 mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
753 millimeters: Position { x: 0.0, y: 0.0 },
754 }),
755 wheel_delta_v,
756 wheel_delta_h,
757 mouse_binding::MousePhase::Wheel,
758 hashset! {},
759 hashset! {},
760 Some(mouse_binding::PrecisionScroll::Yes),
761 ),
762 }
763}
764
765#[cfg(test)]
766mod tests {
767 use super::*;
768 use crate::touch_binding;
769 use assert_matches::assert_matches;
770
771 use test_case::test_case;
772
773 const MOTION_THRESHOLD_IN_MM: f32 = 5.0;
774 const MIN_MOVEMENT_IN_MM: f32 = 10.0;
775 const MAX_MOVEMENT_IN_MM: f32 = 20.0;
776 const LIMIT_TANGENT_FOR_DIRECTION: f32 = 0.2;
777
778 #[test_case(Position {x: 0.0, y: 1.0}, Some(ScrollDirection::Down); "scroll down")]
779 #[test_case(Position {x: 0.0, y: -1.0}, Some(ScrollDirection::Up); "scroll up")]
780 #[test_case(Position {x: 1.0, y: 0.0}, Some(ScrollDirection::Right); "scroll right")]
781 #[test_case(Position {x: -1.0, y: 0.0}, Some(ScrollDirection::Left); "scroll left")]
782 #[test_case(Position {x: 1.0, y: 1.0}, None; "scroll No 45°")]
783 #[test_case(
784 Position {x: 0.9, y: 5.0}, Some(ScrollDirection::Down);
785 "scroll down inside tolerated right")]
786 #[test_case(
787 Position {x: -0.9, y: 5.0}, Some(ScrollDirection::Down);
788 "scroll down inside tolerated left")]
789 #[test_case(Position {x: 1.0, y: 5.0}, None; "scroll No outside tolerated right")]
790 #[test_case(Position {x: -1.0, y: 5.0}, None; "scroll No outside tolerated left")]
791 #[fuchsia::test]
792 fn direction(to: Position, want: Option<ScrollDirection>) {
793 let movement = Movement { from: Position { x: 0.0, y: 0.0 }, to };
794 let got = movement.direction(LIMIT_TANGENT_FOR_DIRECTION);
795 pretty_assertions::assert_eq!(want, got);
796 }
797
798 fn make_touch_contact(id: u32, position: Position) -> touch_binding::TouchContact {
799 touch_binding::TouchContact { id, position, pressure: None, contact_size: None }
800 }
801
802 #[test_case(TouchpadEvent{
803 timestamp: zx::MonotonicInstant::ZERO,
804 pressed_buttons: vec![1],
805 contacts: vec![
806 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
807 make_touch_contact(2, Position{x: 5.0, y: 5.0})
808 ],
809 filtered_palm_contacts: vec![],
810 };"button down")]
811 #[test_case(TouchpadEvent{
812 timestamp: zx::MonotonicInstant::ZERO,
813 pressed_buttons: vec![],
814 contacts: vec![],
815 filtered_palm_contacts: vec![],
816 };"0 fingers")]
817 #[fuchsia::test]
818 fn initial_contender_examine_event_mismatch(event: TouchpadEvent) {
819 let contender: Box<dyn gesture_arena::Contender> = Box::new(InitialContender {
820 motion_threshold_in_mm: MOTION_THRESHOLD_IN_MM,
821 min_movement_in_mm: MIN_MOVEMENT_IN_MM,
822 max_movement_in_mm: MAX_MOVEMENT_IN_MM,
823 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
824 });
825
826 let got = contender.examine_event(&event);
827 assert_matches!(got, ExamineEventResult::Mismatch(_));
828 }
829
830 #[fuchsia::test]
831 fn initial_contender_examine_event_one_finger_contact_contender() {
832 let contender: Box<dyn gesture_arena::Contender> = Box::new(InitialContender {
833 motion_threshold_in_mm: MOTION_THRESHOLD_IN_MM,
834 min_movement_in_mm: MIN_MOVEMENT_IN_MM,
835 max_movement_in_mm: MAX_MOVEMENT_IN_MM,
836 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
837 });
838
839 let event = TouchpadEvent {
840 timestamp: zx::MonotonicInstant::ZERO,
841 pressed_buttons: vec![],
842 contacts: vec![make_touch_contact(1, Position { x: 1.0, y: 1.0 })],
843 filtered_palm_contacts: vec![],
844 };
845 let got = contender.examine_event(&event);
846 assert_matches!(got, ExamineEventResult::Contender(contender) => {
847 pretty_assertions::assert_eq!(contender.get_type_name(), "input_pipeline_lib_test::gestures::scroll::OneFingerContactContender");
848 });
849 }
850
851 #[fuchsia::test]
852 fn initial_contender_examine_event_two_finger_contact_contender() {
853 let contender: Box<dyn gesture_arena::Contender> = Box::new(InitialContender {
854 motion_threshold_in_mm: MOTION_THRESHOLD_IN_MM,
855 min_movement_in_mm: MIN_MOVEMENT_IN_MM,
856 max_movement_in_mm: MAX_MOVEMENT_IN_MM,
857 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
858 });
859
860 let event = TouchpadEvent {
861 timestamp: zx::MonotonicInstant::ZERO,
862 pressed_buttons: vec![],
863 contacts: vec![
864 make_touch_contact(1, Position { x: 1.0, y: 1.0 }),
865 make_touch_contact(2, Position { x: 5.0, y: 5.0 }),
866 ],
867 filtered_palm_contacts: vec![],
868 };
869 let got = contender.examine_event(&event);
870 assert_matches!(got, ExamineEventResult::Contender(contender) => {
871 pretty_assertions::assert_eq!(contender.get_type_name(), "input_pipeline_lib_test::gestures::scroll::TwoFingerContactContender");
872 });
873 }
874
875 #[test_case(TouchpadEvent{
876 timestamp: zx::MonotonicInstant::ZERO,
877 pressed_buttons: vec![1],
878 contacts: vec![
879 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
880 ],
881 filtered_palm_contacts: vec![],
882 };"button down")]
883 #[test_case(TouchpadEvent{
884 timestamp: zx::MonotonicInstant::ZERO,
885 pressed_buttons: vec![],
886 contacts: vec![],
887 filtered_palm_contacts: vec![],
888 };"0 fingers")]
889 #[test_case(TouchpadEvent{
890 timestamp: zx::MonotonicInstant::ZERO,
891 pressed_buttons: vec![],
892 contacts: vec![
893 make_touch_contact(1, Position{x: 1.0, y: 7.0}),
894 ],
895 filtered_palm_contacts: vec![],
896 };"> motion threshold")]
897 #[test_case(TouchpadEvent{
898 timestamp: zx::MonotonicInstant::ZERO,
899 pressed_buttons: vec![],
900 contacts: vec![
901 make_touch_contact(1, Position{x: 1.0, y: 7.0}),
902 make_touch_contact(2, Position{x: 1.0, y: 5.0}),
903 ],
904 filtered_palm_contacts: vec![],
905 };"2 finger and > motion threshold")]
906 #[test_case(TouchpadEvent{
907 timestamp: zx::MonotonicInstant::ZERO,
908 pressed_buttons: vec![],
909 contacts: vec![
910 make_touch_contact(2, Position{x: 1.0, y: 5.0}),
911 ],
912 filtered_palm_contacts: vec![],
913 };"inital finger lift")]
914 #[fuchsia::test]
915 fn one_finger_contact_contender_examine_event_mismatch(event: TouchpadEvent) {
916 let contender: Box<dyn gesture_arena::Contender> = Box::new(OneFingerContactContender {
917 motion_threshold_in_mm: MOTION_THRESHOLD_IN_MM,
918 min_movement_in_mm: MIN_MOVEMENT_IN_MM,
919 max_movement_in_mm: MAX_MOVEMENT_IN_MM,
920 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
921 initial_position: ContactPosition { id: 1, position: Position { x: 1.0, y: 1.0 } },
922 });
923
924 let got = contender.examine_event(&event);
925 assert_matches!(got, ExamineEventResult::Mismatch(_));
926 }
927
928 #[test_case(TouchpadEvent{
929 timestamp: zx::MonotonicInstant::ZERO,
930 pressed_buttons: vec![],
931 contacts: vec![
932 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
933 ],
934 filtered_palm_contacts: vec![],
935 };"hold")]
936 #[test_case(TouchpadEvent{
937 timestamp: zx::MonotonicInstant::ZERO,
938 pressed_buttons: vec![],
939 contacts: vec![
940 make_touch_contact(1, Position{x: 1.0, y: 2.0}),
941 ],
942 filtered_palm_contacts: vec![],
943 };"< motion threshold")]
944 #[fuchsia::test]
945 fn one_finger_contact_contender_examine_event_contender(event: TouchpadEvent) {
946 let contender: Box<dyn gesture_arena::Contender> = Box::new(OneFingerContactContender {
947 motion_threshold_in_mm: MOTION_THRESHOLD_IN_MM,
948 min_movement_in_mm: MIN_MOVEMENT_IN_MM,
949 max_movement_in_mm: MAX_MOVEMENT_IN_MM,
950 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
951 initial_position: ContactPosition { id: 1, position: Position { x: 1.0, y: 1.0 } },
952 });
953
954 let got = contender.examine_event(&event);
955 assert_matches!(got, ExamineEventResult::Contender(contender) => {
956 pretty_assertions::assert_eq!(contender.get_type_name(), "input_pipeline_lib_test::gestures::scroll::OneFingerContactContender");
957 });
958 }
959
960 #[fuchsia::test]
961 fn one_finger_contact_contender_examine_event_two_finger_contact_contender() {
962 let contender: Box<dyn gesture_arena::Contender> = Box::new(OneFingerContactContender {
963 motion_threshold_in_mm: MOTION_THRESHOLD_IN_MM,
964 min_movement_in_mm: MIN_MOVEMENT_IN_MM,
965 max_movement_in_mm: MAX_MOVEMENT_IN_MM,
966 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
967 initial_position: ContactPosition { id: 1, position: Position { x: 1.0, y: 1.0 } },
968 });
969
970 let event = TouchpadEvent {
971 timestamp: zx::MonotonicInstant::ZERO,
972 pressed_buttons: vec![],
973 contacts: vec![
974 make_touch_contact(1, Position { x: 1.0, y: 1.0 }),
975 make_touch_contact(2, Position { x: 1.0, y: 5.0 }),
976 ],
977 filtered_palm_contacts: vec![],
978 };
979 let got = contender.examine_event(&event);
980 assert_matches!(got, ExamineEventResult::Contender(contender) => {
981 pretty_assertions::assert_eq!(contender.get_type_name(), "input_pipeline_lib_test::gestures::scroll::TwoFingerContactContender");
982 });
983 }
984
985 #[test_case(TouchpadEvent{
986 timestamp: zx::MonotonicInstant::ZERO,
987 pressed_buttons: vec![1],
988 contacts: vec![
989 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
990 make_touch_contact(2, Position{x: 5.0, y: 5.0}),
991 ],
992 filtered_palm_contacts: vec![],
993 };"button down")]
994 #[test_case(TouchpadEvent{
995 timestamp: zx::MonotonicInstant::ZERO,
996 pressed_buttons: vec![],
997 contacts: vec![],
998 filtered_palm_contacts: vec![],
999 };"0 fingers")]
1000 #[test_case(TouchpadEvent{
1001 timestamp: zx::MonotonicInstant::ZERO,
1002 pressed_buttons: vec![],
1003 contacts: vec![
1004 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
1005 ],
1006 filtered_palm_contacts: vec![],
1007 };"1 fingers")]
1008 #[test_case(TouchpadEvent{
1009 timestamp: zx::MonotonicInstant::ZERO,
1010 pressed_buttons: vec![],
1011 contacts: vec![
1012 make_touch_contact(1, Position{x: 20.0, y: 20.0}),
1013 make_touch_contact(2, Position{x: 25.0, y: 25.0}),
1014 ],
1015 filtered_palm_contacts: vec![],
1016 };"> max movement no direction")]
1017 #[test_case(TouchpadEvent{
1018 timestamp: zx::MonotonicInstant::ZERO,
1019 pressed_buttons: vec![],
1020 contacts: vec![
1021 make_touch_contact(1, Position{x: 1.0, y: 21.0}),
1022 make_touch_contact(2, Position{x: 5.0, y: -16.0}),
1023 ],
1024 filtered_palm_contacts: vec![],
1025 };"> max movement different direction")]
1026 #[fuchsia::test]
1027 fn two_finger_contact_contender_examine_event_mismatch(event: TouchpadEvent) {
1028 let contender: Box<dyn gesture_arena::Contender> = Box::new(TwoFingerContactContender {
1029 min_movement_in_mm: MIN_MOVEMENT_IN_MM,
1030 max_movement_in_mm: MAX_MOVEMENT_IN_MM,
1031 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
1032 initial_positions: ContactPositions {
1033 first_contact: ContactPosition { id: 1, position: Position { x: 1.0, y: 1.0 } },
1034 second_contact: ContactPosition { id: 2, position: Position { x: 5.0, y: 5.0 } },
1035 },
1036 });
1037
1038 let got = contender.examine_event(&event);
1039 assert_matches!(got, ExamineEventResult::Mismatch(_));
1040 }
1041
1042 #[test_case(TouchpadEvent{
1043 timestamp: zx::MonotonicInstant::ZERO,
1044 pressed_buttons: vec![],
1045 contacts: vec![
1046 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
1047 make_touch_contact(2, Position{x: 5.0, y: 5.0}),
1048 ],
1049 filtered_palm_contacts: vec![],
1050 };"finger hold")]
1051 #[test_case(TouchpadEvent{
1052 timestamp: zx::MonotonicInstant::ZERO,
1053 pressed_buttons: vec![],
1054 contacts: vec![
1055 make_touch_contact(1, Position{x: 2.0, y: 1.0}),
1056 make_touch_contact(2, Position{x: 6.0, y: 5.0}),
1057 ],
1058 filtered_palm_contacts: vec![],
1059 };"2 finger move less than threshold")]
1060 #[test_case(TouchpadEvent{
1061 timestamp: zx::MonotonicInstant::ZERO,
1062 pressed_buttons: vec![],
1063 contacts: vec![
1064 make_touch_contact(1, Position{x: 12.0, y: 1.0}),
1065 make_touch_contact(2, Position{x: 6.0, y: 5.0}),
1066 ],
1067 filtered_palm_contacts: vec![],
1068 };"both finger move, 1 finger move less than threshold")]
1069 #[test_case(TouchpadEvent{
1070 timestamp: zx::MonotonicInstant::ZERO,
1071 pressed_buttons: vec![],
1072 contacts: vec![
1073 make_touch_contact(1, Position{x: 10.0, y: 10.0}),
1074 make_touch_contact(2, Position{x: 15.0, y: 15.0}),
1075 ],
1076 filtered_palm_contacts: vec![],
1077 };"no direction")]
1078 #[test_case(TouchpadEvent{
1079 timestamp: zx::MonotonicInstant::ZERO,
1080 pressed_buttons: vec![],
1081 contacts: vec![
1082 make_touch_contact(1, Position{x: 1.0, y: 11.0}),
1083 make_touch_contact(2, Position{x: 5.0, y: -6.0}),
1084 ],
1085 filtered_palm_contacts: vec![],
1086 };"different direction")]
1087 #[fuchsia::test]
1088 fn two_finger_contact_contender_examine_event_finger_contact_contender(event: TouchpadEvent) {
1089 let contender: Box<dyn gesture_arena::Contender> = Box::new(TwoFingerContactContender {
1090 min_movement_in_mm: MIN_MOVEMENT_IN_MM,
1091 max_movement_in_mm: MAX_MOVEMENT_IN_MM,
1092 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
1093 initial_positions: ContactPositions {
1094 first_contact: ContactPosition { id: 1, position: Position { x: 1.0, y: 1.0 } },
1095 second_contact: ContactPosition { id: 2, position: Position { x: 5.0, y: 5.0 } },
1096 },
1097 });
1098
1099 let got = contender.examine_event(&event);
1100 assert_matches!(got, ExamineEventResult::Contender(contender) => {
1101 pretty_assertions::assert_eq!(contender.get_type_name(), "input_pipeline_lib_test::gestures::scroll::TwoFingerContactContender");
1102 });
1103 }
1104
1105 #[fuchsia::test]
1106 fn two_finger_contact_contender_examine_event_matched_contender() {
1107 let initial_positions = ContactPositions {
1108 first_contact: ContactPosition { id: 1, position: Position { x: 1.0, y: 1.0 } },
1109 second_contact: ContactPosition { id: 2, position: Position { x: 5.0, y: 5.0 } },
1110 };
1111 let contender: Box<dyn gesture_arena::Contender> = Box::new(TwoFingerContactContender {
1112 min_movement_in_mm: MIN_MOVEMENT_IN_MM,
1113 max_movement_in_mm: MAX_MOVEMENT_IN_MM,
1114 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
1115 initial_positions,
1116 });
1117
1118 let event = TouchpadEvent {
1119 timestamp: zx::MonotonicInstant::ZERO,
1120 pressed_buttons: vec![],
1121 contacts: vec![
1122 make_touch_contact(1, Position { x: 1.0, y: 11.0 }),
1123 make_touch_contact(2, Position { x: 5.0, y: 15.0 }),
1124 ],
1125 filtered_palm_contacts: vec![],
1126 };
1127 let got = contender.examine_event(&event);
1128 assert_matches!(got, ExamineEventResult::MatchedContender(_));
1129 }
1130
1131 #[test_case(
1132 TouchpadEvent{
1133 timestamp: zx::MonotonicInstant::ZERO,
1134 pressed_buttons: vec![1],
1135 contacts: vec![make_touch_contact(1, Position{x: 1.0, y: 1.0})],
1136 filtered_palm_contacts: vec![],
1137 };"button down")]
1138 #[test_case(
1139 TouchpadEvent{
1140 timestamp: zx::MonotonicInstant::ZERO,
1141 pressed_buttons: vec![],
1142 contacts: vec![],
1143 filtered_palm_contacts: vec![],
1144 };"0 fingers")]
1145 #[test_case(
1146 TouchpadEvent{
1147 timestamp: zx::MonotonicInstant::ZERO,
1148 pressed_buttons: vec![],
1149 contacts: vec![
1150 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
1151 ],
1152 filtered_palm_contacts: vec![],
1153 };"1 fingers")]
1154 #[test_case(
1155 TouchpadEvent{
1156 timestamp: zx::MonotonicInstant::ZERO,
1157 pressed_buttons: vec![],
1158 contacts: vec![
1159 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
1160 make_touch_contact(2, Position{x: 5.0, y: 5.0}),
1161 ],
1162 filtered_palm_contacts: vec![],
1163 };"finger hold")]
1164 #[test_case(
1165 TouchpadEvent{
1166 timestamp: zx::MonotonicInstant::ZERO,
1167 pressed_buttons: vec![],
1168 contacts: vec![
1169 make_touch_contact(1, Position{x: 10.0, y: 10.0}),
1170 make_touch_contact(2, Position{x: 15.0, y: 15.0}),
1171 ],
1172 filtered_palm_contacts: vec![],
1173 };"no direction")]
1174 #[test_case(
1175 TouchpadEvent{
1176 timestamp: zx::MonotonicInstant::ZERO,
1177 pressed_buttons: vec![],
1178 contacts: vec![
1179 make_touch_contact(1, Position{x: 1.0, y: 11.0}),
1180 make_touch_contact(2, Position{x: 5.0, y: -6.0}),
1181 ],
1182 filtered_palm_contacts: vec![],
1183 };"different direction")]
1184 #[test_case(
1185 TouchpadEvent{
1186 timestamp: zx::MonotonicInstant::ZERO,
1187 pressed_buttons: vec![],
1188 contacts: vec![
1189 make_touch_contact(1, Position{x: 1.0, y: 11.0}),
1190 make_touch_contact(2, Position{x: 5.0, y: 16.0}),
1191 ],
1192 filtered_palm_contacts: vec![],
1193 };"wrong direction")]
1194 #[fuchsia::test]
1195 fn matched_contender_verify_event_mismatch(event: TouchpadEvent) {
1196 let contender: Box<dyn gesture_arena::MatchedContender> = Box::new(MatchedContender {
1197 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
1198 initial_positions: ContactPositions {
1199 first_contact: ContactPosition { id: 1, position: Position { x: 1.0, y: 1.0 } },
1200 second_contact: ContactPosition { id: 2, position: Position { x: 5.0, y: 5.0 } },
1201 },
1202 direction: ScrollDirection::Up,
1203 });
1204
1205 let got = contender.verify_event(&event);
1206 assert_matches!(got, VerifyEventResult::Mismatch(_));
1207 }
1208
1209 #[test_case(TouchpadEvent{
1210 timestamp: zx::MonotonicInstant::ZERO,
1211 pressed_buttons: vec![],
1212 contacts: vec![
1213 make_touch_contact(1, Position{x: 1.0, y: -11.0}),
1214 make_touch_contact(2, Position{x: 5.0, y: -5.0}),
1215 ],
1216 filtered_palm_contacts: vec![],
1217 };"on direction")]
1218 #[fuchsia::test]
1219 fn matched_contender_verify_event_matched_contender(event: TouchpadEvent) {
1220 let contender: Box<dyn gesture_arena::MatchedContender> = Box::new(MatchedContender {
1221 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
1222 initial_positions: ContactPositions {
1223 first_contact: ContactPosition { id: 1, position: Position { x: 1.0, y: 1.0 } },
1224 second_contact: ContactPosition { id: 2, position: Position { x: 5.0, y: 5.0 } },
1225 },
1226 direction: ScrollDirection::Up,
1227 });
1228
1229 let got = contender.verify_event(&event);
1230 assert_matches!(got, VerifyEventResult::MatchedContender(_));
1231 }
1232
1233 #[fuchsia::test]
1234 fn matched_contender_process_buffered_events() {
1235 let contender: Box<dyn gesture_arena::MatchedContender> = Box::new(MatchedContender {
1236 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
1237 initial_positions: ContactPositions {
1238 first_contact: ContactPosition { id: 1, position: Position { x: 1.0, y: 1.0 } },
1239 second_contact: ContactPosition { id: 2, position: Position { x: 5.0, y: 5.0 } },
1240 },
1241 direction: ScrollDirection::Up,
1242 });
1243
1244 let got = contender.process_buffered_events(vec![
1245 TouchpadEvent {
1246 timestamp: zx::MonotonicInstant::from_nanos(1),
1247 pressed_buttons: vec![],
1248 contacts: vec![
1249 make_touch_contact(1, Position { x: 1.0, y: 1.0 }),
1250 make_touch_contact(2, Position { x: 5.0, y: 5.0 }),
1251 ],
1252 filtered_palm_contacts: vec![],
1253 },
1254 TouchpadEvent {
1255 timestamp: zx::MonotonicInstant::from_nanos(2),
1256 pressed_buttons: vec![],
1257 contacts: vec![
1258 make_touch_contact(1, Position { x: 1.0, y: 1.0 }),
1259 make_touch_contact(2, Position { x: 5.0, y: 5.0 }),
1260 ],
1261 filtered_palm_contacts: vec![],
1262 },
1263 TouchpadEvent {
1264 timestamp: zx::MonotonicInstant::from_nanos(3),
1265 pressed_buttons: vec![],
1266 contacts: vec![
1267 make_touch_contact(1, Position { x: 1.0, y: 2.0 }),
1268 make_touch_contact(2, Position { x: 5.0, y: 6.0 }),
1269 ],
1270 filtered_palm_contacts: vec![],
1271 },
1272 TouchpadEvent {
1273 timestamp: zx::MonotonicInstant::from_nanos(4),
1274 pressed_buttons: vec![],
1275 contacts: vec![
1276 make_touch_contact(1, Position { x: 1.0, y: -11.0 }),
1277 make_touch_contact(2, Position { x: 5.0, y: -5.0 }),
1278 ],
1279 filtered_palm_contacts: vec![],
1280 },
1281 ]);
1282
1283 pretty_assertions::assert_eq!(
1284 got.generated_events,
1285 vec![
1286 MouseEvent {
1288 timestamp: zx::MonotonicInstant::from_nanos(2),
1289 mouse_data: mouse_binding::MouseEvent::new(
1290 mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
1291 millimeters: Position { x: 0.0, y: 0.0 },
1292 }),
1293 wheel_delta_mm(0.0),
1294 None,
1295 mouse_binding::MousePhase::Wheel,
1296 hashset! {},
1297 hashset! {},
1298 Some(mouse_binding::PrecisionScroll::Yes),
1299 ),
1300 },
1301 MouseEvent {
1303 timestamp: zx::MonotonicInstant::from_nanos(3),
1304 mouse_data: mouse_binding::MouseEvent::new(
1305 mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
1306 millimeters: Position { x: 0.0, y: 0.0 },
1307 }),
1308 wheel_delta_mm(0.0),
1309 None,
1310 mouse_binding::MousePhase::Wheel,
1311 hashset! {},
1312 hashset! {},
1313 Some(mouse_binding::PrecisionScroll::Yes),
1314 ),
1315 },
1316 MouseEvent {
1317 timestamp: zx::MonotonicInstant::from_nanos(4),
1318 mouse_data: mouse_binding::MouseEvent::new(
1319 mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
1320 millimeters: Position { x: 0.0, y: 0.0 },
1321 }),
1322 wheel_delta_mm(-12.0),
1323 None,
1324 mouse_binding::MousePhase::Wheel,
1325 hashset! {},
1326 hashset! {},
1327 Some(mouse_binding::PrecisionScroll::Yes),
1328 ),
1329 },
1330 ]
1331 );
1332 pretty_assertions::assert_eq!(got.recognized_gesture, RecognizedGesture::Scroll);
1333 }
1334
1335 #[fuchsia::test]
1336 fn winner_process_new_event_end_gesture_none() {
1337 let winner: Box<dyn gesture_arena::Winner> = Box::new(Winner {
1338 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
1339 direction: ScrollDirection::Up,
1340 last_positions: ContactPositions {
1341 first_contact: ContactPosition { id: 1, position: Position { x: 1.0, y: 1.0 } },
1342 second_contact: ContactPosition { id: 2, position: Position { x: 5.0, y: 5.0 } },
1343 },
1344 });
1345 let event = TouchpadEvent {
1346 timestamp: zx::MonotonicInstant::ZERO,
1347 pressed_buttons: vec![],
1348 contacts: vec![],
1349 filtered_palm_contacts: vec![],
1350 };
1351 let got = winner.process_new_event(event);
1352
1353 assert_matches!(got, ProcessNewEventResult::EndGesture(EndGestureEvent::NoEvent, _reason));
1354 }
1355
1356 #[test_case(
1357 TouchpadEvent{
1358 timestamp: zx::MonotonicInstant::ZERO,
1359 pressed_buttons: vec![1],
1360 contacts: vec![make_touch_contact(1, Position{x: 1.0, y: 1.0})],
1361 filtered_palm_contacts: vec![],
1362 };"button down")]
1363 #[test_case(
1364 TouchpadEvent{
1365 timestamp: zx::MonotonicInstant::ZERO,
1366 pressed_buttons: vec![],
1367 contacts: vec![
1368 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
1369 ],
1370 filtered_palm_contacts: vec![],
1371 };"1 fingers")]
1372 #[test_case(
1373 TouchpadEvent{
1374 timestamp: zx::MonotonicInstant::ZERO,
1375 pressed_buttons: vec![],
1376 contacts: vec![
1377 make_touch_contact(1, Position{x: 1.0, y: 11.0}),
1378 make_touch_contact(2, Position{x: 5.0, y: -6.0}),
1379 ],
1380 filtered_palm_contacts: vec![],
1381 };"1 finger reverse direction")]
1382 #[test_case(
1383 TouchpadEvent{
1384 timestamp: zx::MonotonicInstant::ZERO,
1385 pressed_buttons: vec![],
1386 contacts: vec![
1387 make_touch_contact(1, Position{x: 1.0, y: 11.0}),
1388 make_touch_contact(2, Position{x: 5.0, y: 16.0}),
1389 ],
1390 filtered_palm_contacts: vec![],
1391 };"2 fingers reverse direction")]
1392 #[fuchsia::test]
1393 fn winner_process_new_event_end_gesture_some(event: TouchpadEvent) {
1394 let winner: Box<dyn gesture_arena::Winner> = Box::new(Winner {
1395 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
1396 direction: ScrollDirection::Up,
1397 last_positions: ContactPositions {
1398 first_contact: ContactPosition { id: 1, position: Position { x: 1.0, y: 1.0 } },
1399 second_contact: ContactPosition { id: 2, position: Position { x: 5.0, y: 5.0 } },
1400 },
1401 });
1402 let got = winner.process_new_event(event);
1403
1404 assert_matches!(
1405 got,
1406 ProcessNewEventResult::EndGesture(EndGestureEvent::UnconsumedEvent(_), _reason)
1407 );
1408 }
1409
1410 #[test_case(
1411 TouchpadEvent{
1412 timestamp: zx::MonotonicInstant::ZERO,
1413 pressed_buttons: vec![],
1414 contacts: vec![
1415 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
1416 make_touch_contact(2, Position{x: 5.0, y: 5.0}),
1417 ],
1418 filtered_palm_contacts: vec![],
1419 } => MouseEvent {
1420 timestamp: zx::MonotonicInstant::from_nanos(0),
1421 mouse_data: mouse_binding::MouseEvent {
1422 location: mouse_binding::MouseLocation::Relative(
1423 mouse_binding::RelativeLocation {
1424 millimeters: Position { x: 0.0, y: 0.0 },
1425 }
1426 ),
1427 wheel_delta_v: wheel_delta_mm(0.0),
1428 wheel_delta_h: None,
1429 phase: mouse_binding::MousePhase::Wheel,
1430 affected_buttons: hashset! {},
1431 pressed_buttons: hashset! {},
1432 is_precision_scroll: Some(mouse_binding::PrecisionScroll::Yes),
1433 },
1434 };"finger hold")]
1435 #[test_case(
1436 TouchpadEvent{
1437 timestamp: zx::MonotonicInstant::ZERO,
1438 pressed_buttons: vec![],
1439 contacts: vec![
1440 make_touch_contact(1, Position{x: 10.0, y: -10.0}),
1441 make_touch_contact(2, Position{x: 15.0, y: -15.0}),
1442 ],
1443 filtered_palm_contacts: vec![],
1444 } => MouseEvent {
1445 timestamp: zx::MonotonicInstant::from_nanos(0),
1446 mouse_data: mouse_binding::MouseEvent {
1447 location: mouse_binding::MouseLocation::Relative(
1448 mouse_binding::RelativeLocation {
1449 millimeters: Position { x: 0.0, y: 0.0 },
1450 }
1451 ),
1452 wheel_delta_v: wheel_delta_mm(-15.5),
1453 wheel_delta_h: None,
1454 phase: mouse_binding::MousePhase::Wheel,
1455 affected_buttons: hashset! {},
1456 pressed_buttons: hashset! {},
1457 is_precision_scroll: Some(mouse_binding::PrecisionScroll::Yes),
1458 },
1459 };"direction contact1 only")]
1460 #[test_case(
1461 TouchpadEvent{
1462 timestamp: zx::MonotonicInstant::ZERO,
1463 pressed_buttons: vec![],
1464 contacts: vec![
1465 make_touch_contact(1, Position{x: 1.0, y: -11.0}),
1466 make_touch_contact(2, Position{x: 5.0, y: -5.0}),
1467 ],
1468 filtered_palm_contacts: vec![],
1469 } => MouseEvent {
1470 timestamp: zx::MonotonicInstant::from_nanos(0),
1471 mouse_data: mouse_binding::MouseEvent {
1472 location: mouse_binding::MouseLocation::Relative(
1473 mouse_binding::RelativeLocation {
1474 millimeters: Position { x: 0.0, y: 0.0 },
1475 }
1476 ),
1477 wheel_delta_v: wheel_delta_mm(-11.0),
1478 wheel_delta_h: None,
1479 phase: mouse_binding::MousePhase::Wheel,
1480 affected_buttons: hashset! {},
1481 pressed_buttons: hashset! {},
1482 is_precision_scroll: Some(mouse_binding::PrecisionScroll::Yes),
1483 },
1484 };"on direction")]
1485 #[fuchsia::test]
1486 fn winner_process_new_event_continue_gesture(event: TouchpadEvent) -> MouseEvent {
1487 let winner: Box<dyn gesture_arena::Winner> = Box::new(Winner {
1488 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
1489 direction: ScrollDirection::Up,
1490 last_positions: ContactPositions {
1491 first_contact: ContactPosition { id: 1, position: Position { x: 1.0, y: 1.0 } },
1492 second_contact: ContactPosition { id: 2, position: Position { x: 5.0, y: 5.0 } },
1493 },
1494 });
1495 let got = winner.process_new_event(event);
1496
1497 match got {
1501 ProcessNewEventResult::EndGesture(..) => {
1502 panic!("Got {:?}, want ContinueGesture()", got)
1503 }
1504 ProcessNewEventResult::ContinueGesture(got_mouse_event, _) => got_mouse_event.unwrap(),
1505 }
1506 }
1507}