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::{Position, euclidean_distance};
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 None,
762 ),
763 }
764}
765
766#[cfg(test)]
767mod tests {
768 use super::*;
769 use crate::touch_binding;
770 use assert_matches::assert_matches;
771
772 use test_case::test_case;
773
774 const MOTION_THRESHOLD_IN_MM: f32 = 5.0;
775 const MIN_MOVEMENT_IN_MM: f32 = 10.0;
776 const MAX_MOVEMENT_IN_MM: f32 = 20.0;
777 const LIMIT_TANGENT_FOR_DIRECTION: f32 = 0.2;
778
779 #[test_case(Position {x: 0.0, y: 1.0}, Some(ScrollDirection::Down); "scroll down")]
780 #[test_case(Position {x: 0.0, y: -1.0}, Some(ScrollDirection::Up); "scroll up")]
781 #[test_case(Position {x: 1.0, y: 0.0}, Some(ScrollDirection::Right); "scroll right")]
782 #[test_case(Position {x: -1.0, y: 0.0}, Some(ScrollDirection::Left); "scroll left")]
783 #[test_case(Position {x: 1.0, y: 1.0}, None; "scroll No 45°")]
784 #[test_case(
785 Position {x: 0.9, y: 5.0}, Some(ScrollDirection::Down);
786 "scroll down inside tolerated right")]
787 #[test_case(
788 Position {x: -0.9, y: 5.0}, Some(ScrollDirection::Down);
789 "scroll down inside tolerated left")]
790 #[test_case(Position {x: 1.0, y: 5.0}, None; "scroll No outside tolerated right")]
791 #[test_case(Position {x: -1.0, y: 5.0}, None; "scroll No outside tolerated left")]
792 #[fuchsia::test]
793 fn direction(to: Position, want: Option<ScrollDirection>) {
794 let movement = Movement { from: Position { x: 0.0, y: 0.0 }, to };
795 let got = movement.direction(LIMIT_TANGENT_FOR_DIRECTION);
796 pretty_assertions::assert_eq!(want, got);
797 }
798
799 fn make_touch_contact(id: u32, position: Position) -> touch_binding::TouchContact {
800 touch_binding::TouchContact { id, position, pressure: None, contact_size: None }
801 }
802
803 #[test_case(TouchpadEvent{
804 timestamp: zx::MonotonicInstant::ZERO,
805 pressed_buttons: vec![1],
806 contacts: vec![
807 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
808 make_touch_contact(2, Position{x: 5.0, y: 5.0})
809 ],
810 filtered_palm_contacts: vec![],
811 };"button down")]
812 #[test_case(TouchpadEvent{
813 timestamp: zx::MonotonicInstant::ZERO,
814 pressed_buttons: vec![],
815 contacts: vec![],
816 filtered_palm_contacts: vec![],
817 };"0 fingers")]
818 #[fuchsia::test]
819 fn initial_contender_examine_event_mismatch(event: TouchpadEvent) {
820 let contender: Box<dyn gesture_arena::Contender> = Box::new(InitialContender {
821 motion_threshold_in_mm: MOTION_THRESHOLD_IN_MM,
822 min_movement_in_mm: MIN_MOVEMENT_IN_MM,
823 max_movement_in_mm: MAX_MOVEMENT_IN_MM,
824 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
825 });
826
827 let got = contender.examine_event(&event);
828 assert_matches!(got, ExamineEventResult::Mismatch(_));
829 }
830
831 #[fuchsia::test]
832 fn initial_contender_examine_event_one_finger_contact_contender() {
833 let contender: Box<dyn gesture_arena::Contender> = Box::new(InitialContender {
834 motion_threshold_in_mm: MOTION_THRESHOLD_IN_MM,
835 min_movement_in_mm: MIN_MOVEMENT_IN_MM,
836 max_movement_in_mm: MAX_MOVEMENT_IN_MM,
837 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
838 });
839
840 let event = TouchpadEvent {
841 timestamp: zx::MonotonicInstant::ZERO,
842 pressed_buttons: vec![],
843 contacts: vec![make_touch_contact(1, Position { x: 1.0, y: 1.0 })],
844 filtered_palm_contacts: vec![],
845 };
846 let got = contender.examine_event(&event);
847 assert_matches!(got, ExamineEventResult::Contender(contender) => {
848 pretty_assertions::assert_eq!(contender.get_type_name(), "input_pipeline_lib_test::gestures::scroll::OneFingerContactContender");
849 });
850 }
851
852 #[fuchsia::test]
853 fn initial_contender_examine_event_two_finger_contact_contender() {
854 let contender: Box<dyn gesture_arena::Contender> = Box::new(InitialContender {
855 motion_threshold_in_mm: MOTION_THRESHOLD_IN_MM,
856 min_movement_in_mm: MIN_MOVEMENT_IN_MM,
857 max_movement_in_mm: MAX_MOVEMENT_IN_MM,
858 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
859 });
860
861 let event = TouchpadEvent {
862 timestamp: zx::MonotonicInstant::ZERO,
863 pressed_buttons: vec![],
864 contacts: vec![
865 make_touch_contact(1, Position { x: 1.0, y: 1.0 }),
866 make_touch_contact(2, Position { x: 5.0, y: 5.0 }),
867 ],
868 filtered_palm_contacts: vec![],
869 };
870 let got = contender.examine_event(&event);
871 assert_matches!(got, ExamineEventResult::Contender(contender) => {
872 pretty_assertions::assert_eq!(contender.get_type_name(), "input_pipeline_lib_test::gestures::scroll::TwoFingerContactContender");
873 });
874 }
875
876 #[test_case(TouchpadEvent{
877 timestamp: zx::MonotonicInstant::ZERO,
878 pressed_buttons: vec![1],
879 contacts: vec![
880 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
881 ],
882 filtered_palm_contacts: vec![],
883 };"button down")]
884 #[test_case(TouchpadEvent{
885 timestamp: zx::MonotonicInstant::ZERO,
886 pressed_buttons: vec![],
887 contacts: vec![],
888 filtered_palm_contacts: vec![],
889 };"0 fingers")]
890 #[test_case(TouchpadEvent{
891 timestamp: zx::MonotonicInstant::ZERO,
892 pressed_buttons: vec![],
893 contacts: vec![
894 make_touch_contact(1, Position{x: 1.0, y: 7.0}),
895 ],
896 filtered_palm_contacts: vec![],
897 };"> motion threshold")]
898 #[test_case(TouchpadEvent{
899 timestamp: zx::MonotonicInstant::ZERO,
900 pressed_buttons: vec![],
901 contacts: vec![
902 make_touch_contact(1, Position{x: 1.0, y: 7.0}),
903 make_touch_contact(2, Position{x: 1.0, y: 5.0}),
904 ],
905 filtered_palm_contacts: vec![],
906 };"2 finger and > motion threshold")]
907 #[test_case(TouchpadEvent{
908 timestamp: zx::MonotonicInstant::ZERO,
909 pressed_buttons: vec![],
910 contacts: vec![
911 make_touch_contact(2, Position{x: 1.0, y: 5.0}),
912 ],
913 filtered_palm_contacts: vec![],
914 };"inital finger lift")]
915 #[fuchsia::test]
916 fn one_finger_contact_contender_examine_event_mismatch(event: TouchpadEvent) {
917 let contender: Box<dyn gesture_arena::Contender> = Box::new(OneFingerContactContender {
918 motion_threshold_in_mm: MOTION_THRESHOLD_IN_MM,
919 min_movement_in_mm: MIN_MOVEMENT_IN_MM,
920 max_movement_in_mm: MAX_MOVEMENT_IN_MM,
921 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
922 initial_position: ContactPosition { id: 1, position: Position { x: 1.0, y: 1.0 } },
923 });
924
925 let got = contender.examine_event(&event);
926 assert_matches!(got, ExamineEventResult::Mismatch(_));
927 }
928
929 #[test_case(TouchpadEvent{
930 timestamp: zx::MonotonicInstant::ZERO,
931 pressed_buttons: vec![],
932 contacts: vec![
933 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
934 ],
935 filtered_palm_contacts: vec![],
936 };"hold")]
937 #[test_case(TouchpadEvent{
938 timestamp: zx::MonotonicInstant::ZERO,
939 pressed_buttons: vec![],
940 contacts: vec![
941 make_touch_contact(1, Position{x: 1.0, y: 2.0}),
942 ],
943 filtered_palm_contacts: vec![],
944 };"< motion threshold")]
945 #[fuchsia::test]
946 fn one_finger_contact_contender_examine_event_contender(event: TouchpadEvent) {
947 let contender: Box<dyn gesture_arena::Contender> = Box::new(OneFingerContactContender {
948 motion_threshold_in_mm: MOTION_THRESHOLD_IN_MM,
949 min_movement_in_mm: MIN_MOVEMENT_IN_MM,
950 max_movement_in_mm: MAX_MOVEMENT_IN_MM,
951 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
952 initial_position: ContactPosition { id: 1, position: Position { x: 1.0, y: 1.0 } },
953 });
954
955 let got = contender.examine_event(&event);
956 assert_matches!(got, ExamineEventResult::Contender(contender) => {
957 pretty_assertions::assert_eq!(contender.get_type_name(), "input_pipeline_lib_test::gestures::scroll::OneFingerContactContender");
958 });
959 }
960
961 #[fuchsia::test]
962 fn one_finger_contact_contender_examine_event_two_finger_contact_contender() {
963 let contender: Box<dyn gesture_arena::Contender> = Box::new(OneFingerContactContender {
964 motion_threshold_in_mm: MOTION_THRESHOLD_IN_MM,
965 min_movement_in_mm: MIN_MOVEMENT_IN_MM,
966 max_movement_in_mm: MAX_MOVEMENT_IN_MM,
967 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
968 initial_position: ContactPosition { id: 1, position: Position { x: 1.0, y: 1.0 } },
969 });
970
971 let event = TouchpadEvent {
972 timestamp: zx::MonotonicInstant::ZERO,
973 pressed_buttons: vec![],
974 contacts: vec![
975 make_touch_contact(1, Position { x: 1.0, y: 1.0 }),
976 make_touch_contact(2, Position { x: 1.0, y: 5.0 }),
977 ],
978 filtered_palm_contacts: vec![],
979 };
980 let got = contender.examine_event(&event);
981 assert_matches!(got, ExamineEventResult::Contender(contender) => {
982 pretty_assertions::assert_eq!(contender.get_type_name(), "input_pipeline_lib_test::gestures::scroll::TwoFingerContactContender");
983 });
984 }
985
986 #[test_case(TouchpadEvent{
987 timestamp: zx::MonotonicInstant::ZERO,
988 pressed_buttons: vec![1],
989 contacts: vec![
990 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
991 make_touch_contact(2, Position{x: 5.0, y: 5.0}),
992 ],
993 filtered_palm_contacts: vec![],
994 };"button down")]
995 #[test_case(TouchpadEvent{
996 timestamp: zx::MonotonicInstant::ZERO,
997 pressed_buttons: vec![],
998 contacts: vec![],
999 filtered_palm_contacts: vec![],
1000 };"0 fingers")]
1001 #[test_case(TouchpadEvent{
1002 timestamp: zx::MonotonicInstant::ZERO,
1003 pressed_buttons: vec![],
1004 contacts: vec![
1005 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
1006 ],
1007 filtered_palm_contacts: vec![],
1008 };"1 fingers")]
1009 #[test_case(TouchpadEvent{
1010 timestamp: zx::MonotonicInstant::ZERO,
1011 pressed_buttons: vec![],
1012 contacts: vec![
1013 make_touch_contact(1, Position{x: 20.0, y: 20.0}),
1014 make_touch_contact(2, Position{x: 25.0, y: 25.0}),
1015 ],
1016 filtered_palm_contacts: vec![],
1017 };"> max movement no direction")]
1018 #[test_case(TouchpadEvent{
1019 timestamp: zx::MonotonicInstant::ZERO,
1020 pressed_buttons: vec![],
1021 contacts: vec![
1022 make_touch_contact(1, Position{x: 1.0, y: 21.0}),
1023 make_touch_contact(2, Position{x: 5.0, y: -16.0}),
1024 ],
1025 filtered_palm_contacts: vec![],
1026 };"> max movement different direction")]
1027 #[fuchsia::test]
1028 fn two_finger_contact_contender_examine_event_mismatch(event: TouchpadEvent) {
1029 let contender: Box<dyn gesture_arena::Contender> = Box::new(TwoFingerContactContender {
1030 min_movement_in_mm: MIN_MOVEMENT_IN_MM,
1031 max_movement_in_mm: MAX_MOVEMENT_IN_MM,
1032 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
1033 initial_positions: ContactPositions {
1034 first_contact: ContactPosition { id: 1, position: Position { x: 1.0, y: 1.0 } },
1035 second_contact: ContactPosition { id: 2, position: Position { x: 5.0, y: 5.0 } },
1036 },
1037 });
1038
1039 let got = contender.examine_event(&event);
1040 assert_matches!(got, ExamineEventResult::Mismatch(_));
1041 }
1042
1043 #[test_case(TouchpadEvent{
1044 timestamp: zx::MonotonicInstant::ZERO,
1045 pressed_buttons: vec![],
1046 contacts: vec![
1047 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
1048 make_touch_contact(2, Position{x: 5.0, y: 5.0}),
1049 ],
1050 filtered_palm_contacts: vec![],
1051 };"finger hold")]
1052 #[test_case(TouchpadEvent{
1053 timestamp: zx::MonotonicInstant::ZERO,
1054 pressed_buttons: vec![],
1055 contacts: vec![
1056 make_touch_contact(1, Position{x: 2.0, y: 1.0}),
1057 make_touch_contact(2, Position{x: 6.0, y: 5.0}),
1058 ],
1059 filtered_palm_contacts: vec![],
1060 };"2 finger move less than threshold")]
1061 #[test_case(TouchpadEvent{
1062 timestamp: zx::MonotonicInstant::ZERO,
1063 pressed_buttons: vec![],
1064 contacts: vec![
1065 make_touch_contact(1, Position{x: 12.0, y: 1.0}),
1066 make_touch_contact(2, Position{x: 6.0, y: 5.0}),
1067 ],
1068 filtered_palm_contacts: vec![],
1069 };"both finger move, 1 finger move less than threshold")]
1070 #[test_case(TouchpadEvent{
1071 timestamp: zx::MonotonicInstant::ZERO,
1072 pressed_buttons: vec![],
1073 contacts: vec![
1074 make_touch_contact(1, Position{x: 10.0, y: 10.0}),
1075 make_touch_contact(2, Position{x: 15.0, y: 15.0}),
1076 ],
1077 filtered_palm_contacts: vec![],
1078 };"no direction")]
1079 #[test_case(TouchpadEvent{
1080 timestamp: zx::MonotonicInstant::ZERO,
1081 pressed_buttons: vec![],
1082 contacts: vec![
1083 make_touch_contact(1, Position{x: 1.0, y: 11.0}),
1084 make_touch_contact(2, Position{x: 5.0, y: -6.0}),
1085 ],
1086 filtered_palm_contacts: vec![],
1087 };"different direction")]
1088 #[fuchsia::test]
1089 fn two_finger_contact_contender_examine_event_finger_contact_contender(event: TouchpadEvent) {
1090 let contender: Box<dyn gesture_arena::Contender> = Box::new(TwoFingerContactContender {
1091 min_movement_in_mm: MIN_MOVEMENT_IN_MM,
1092 max_movement_in_mm: MAX_MOVEMENT_IN_MM,
1093 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
1094 initial_positions: ContactPositions {
1095 first_contact: ContactPosition { id: 1, position: Position { x: 1.0, y: 1.0 } },
1096 second_contact: ContactPosition { id: 2, position: Position { x: 5.0, y: 5.0 } },
1097 },
1098 });
1099
1100 let got = contender.examine_event(&event);
1101 assert_matches!(got, ExamineEventResult::Contender(contender) => {
1102 pretty_assertions::assert_eq!(contender.get_type_name(), "input_pipeline_lib_test::gestures::scroll::TwoFingerContactContender");
1103 });
1104 }
1105
1106 #[fuchsia::test]
1107 fn two_finger_contact_contender_examine_event_matched_contender() {
1108 let initial_positions = ContactPositions {
1109 first_contact: ContactPosition { id: 1, position: Position { x: 1.0, y: 1.0 } },
1110 second_contact: ContactPosition { id: 2, position: Position { x: 5.0, y: 5.0 } },
1111 };
1112 let contender: Box<dyn gesture_arena::Contender> = Box::new(TwoFingerContactContender {
1113 min_movement_in_mm: MIN_MOVEMENT_IN_MM,
1114 max_movement_in_mm: MAX_MOVEMENT_IN_MM,
1115 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
1116 initial_positions,
1117 });
1118
1119 let event = TouchpadEvent {
1120 timestamp: zx::MonotonicInstant::ZERO,
1121 pressed_buttons: vec![],
1122 contacts: vec![
1123 make_touch_contact(1, Position { x: 1.0, y: 11.0 }),
1124 make_touch_contact(2, Position { x: 5.0, y: 15.0 }),
1125 ],
1126 filtered_palm_contacts: vec![],
1127 };
1128 let got = contender.examine_event(&event);
1129 assert_matches!(got, ExamineEventResult::MatchedContender(_));
1130 }
1131
1132 #[test_case(
1133 TouchpadEvent{
1134 timestamp: zx::MonotonicInstant::ZERO,
1135 pressed_buttons: vec![1],
1136 contacts: vec![make_touch_contact(1, Position{x: 1.0, y: 1.0})],
1137 filtered_palm_contacts: vec![],
1138 };"button down")]
1139 #[test_case(
1140 TouchpadEvent{
1141 timestamp: zx::MonotonicInstant::ZERO,
1142 pressed_buttons: vec![],
1143 contacts: vec![],
1144 filtered_palm_contacts: vec![],
1145 };"0 fingers")]
1146 #[test_case(
1147 TouchpadEvent{
1148 timestamp: zx::MonotonicInstant::ZERO,
1149 pressed_buttons: vec![],
1150 contacts: vec![
1151 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
1152 ],
1153 filtered_palm_contacts: vec![],
1154 };"1 fingers")]
1155 #[test_case(
1156 TouchpadEvent{
1157 timestamp: zx::MonotonicInstant::ZERO,
1158 pressed_buttons: vec![],
1159 contacts: vec![
1160 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
1161 make_touch_contact(2, Position{x: 5.0, y: 5.0}),
1162 ],
1163 filtered_palm_contacts: vec![],
1164 };"finger hold")]
1165 #[test_case(
1166 TouchpadEvent{
1167 timestamp: zx::MonotonicInstant::ZERO,
1168 pressed_buttons: vec![],
1169 contacts: vec![
1170 make_touch_contact(1, Position{x: 10.0, y: 10.0}),
1171 make_touch_contact(2, Position{x: 15.0, y: 15.0}),
1172 ],
1173 filtered_palm_contacts: vec![],
1174 };"no direction")]
1175 #[test_case(
1176 TouchpadEvent{
1177 timestamp: zx::MonotonicInstant::ZERO,
1178 pressed_buttons: vec![],
1179 contacts: vec![
1180 make_touch_contact(1, Position{x: 1.0, y: 11.0}),
1181 make_touch_contact(2, Position{x: 5.0, y: -6.0}),
1182 ],
1183 filtered_palm_contacts: vec![],
1184 };"different direction")]
1185 #[test_case(
1186 TouchpadEvent{
1187 timestamp: zx::MonotonicInstant::ZERO,
1188 pressed_buttons: vec![],
1189 contacts: vec![
1190 make_touch_contact(1, Position{x: 1.0, y: 11.0}),
1191 make_touch_contact(2, Position{x: 5.0, y: 16.0}),
1192 ],
1193 filtered_palm_contacts: vec![],
1194 };"wrong direction")]
1195 #[fuchsia::test]
1196 fn matched_contender_verify_event_mismatch(event: TouchpadEvent) {
1197 let contender: Box<dyn gesture_arena::MatchedContender> = Box::new(MatchedContender {
1198 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
1199 initial_positions: ContactPositions {
1200 first_contact: ContactPosition { id: 1, position: Position { x: 1.0, y: 1.0 } },
1201 second_contact: ContactPosition { id: 2, position: Position { x: 5.0, y: 5.0 } },
1202 },
1203 direction: ScrollDirection::Up,
1204 });
1205
1206 let got = contender.verify_event(&event);
1207 assert_matches!(got, VerifyEventResult::Mismatch(_));
1208 }
1209
1210 #[test_case(TouchpadEvent{
1211 timestamp: zx::MonotonicInstant::ZERO,
1212 pressed_buttons: vec![],
1213 contacts: vec![
1214 make_touch_contact(1, Position{x: 1.0, y: -11.0}),
1215 make_touch_contact(2, Position{x: 5.0, y: -5.0}),
1216 ],
1217 filtered_palm_contacts: vec![],
1218 };"on direction")]
1219 #[fuchsia::test]
1220 fn matched_contender_verify_event_matched_contender(event: TouchpadEvent) {
1221 let contender: Box<dyn gesture_arena::MatchedContender> = Box::new(MatchedContender {
1222 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
1223 initial_positions: ContactPositions {
1224 first_contact: ContactPosition { id: 1, position: Position { x: 1.0, y: 1.0 } },
1225 second_contact: ContactPosition { id: 2, position: Position { x: 5.0, y: 5.0 } },
1226 },
1227 direction: ScrollDirection::Up,
1228 });
1229
1230 let got = contender.verify_event(&event);
1231 assert_matches!(got, VerifyEventResult::MatchedContender(_));
1232 }
1233
1234 #[fuchsia::test]
1235 fn matched_contender_process_buffered_events() {
1236 let contender: Box<dyn gesture_arena::MatchedContender> = Box::new(MatchedContender {
1237 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
1238 initial_positions: ContactPositions {
1239 first_contact: ContactPosition { id: 1, position: Position { x: 1.0, y: 1.0 } },
1240 second_contact: ContactPosition { id: 2, position: Position { x: 5.0, y: 5.0 } },
1241 },
1242 direction: ScrollDirection::Up,
1243 });
1244
1245 let got = contender.process_buffered_events(vec![
1246 TouchpadEvent {
1247 timestamp: zx::MonotonicInstant::from_nanos(1),
1248 pressed_buttons: vec![],
1249 contacts: vec![
1250 make_touch_contact(1, Position { x: 1.0, y: 1.0 }),
1251 make_touch_contact(2, Position { x: 5.0, y: 5.0 }),
1252 ],
1253 filtered_palm_contacts: vec![],
1254 },
1255 TouchpadEvent {
1256 timestamp: zx::MonotonicInstant::from_nanos(2),
1257 pressed_buttons: vec![],
1258 contacts: vec![
1259 make_touch_contact(1, Position { x: 1.0, y: 1.0 }),
1260 make_touch_contact(2, Position { x: 5.0, y: 5.0 }),
1261 ],
1262 filtered_palm_contacts: vec![],
1263 },
1264 TouchpadEvent {
1265 timestamp: zx::MonotonicInstant::from_nanos(3),
1266 pressed_buttons: vec![],
1267 contacts: vec![
1268 make_touch_contact(1, Position { x: 1.0, y: 2.0 }),
1269 make_touch_contact(2, Position { x: 5.0, y: 6.0 }),
1270 ],
1271 filtered_palm_contacts: vec![],
1272 },
1273 TouchpadEvent {
1274 timestamp: zx::MonotonicInstant::from_nanos(4),
1275 pressed_buttons: vec![],
1276 contacts: vec![
1277 make_touch_contact(1, Position { x: 1.0, y: -11.0 }),
1278 make_touch_contact(2, Position { x: 5.0, y: -5.0 }),
1279 ],
1280 filtered_palm_contacts: vec![],
1281 },
1282 ]);
1283
1284 pretty_assertions::assert_eq!(
1285 got.generated_events,
1286 vec![
1287 MouseEvent {
1289 timestamp: zx::MonotonicInstant::from_nanos(2),
1290 mouse_data: mouse_binding::MouseEvent::new(
1291 mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
1292 millimeters: Position { x: 0.0, y: 0.0 },
1293 }),
1294 wheel_delta_mm(0.0),
1295 None,
1296 mouse_binding::MousePhase::Wheel,
1297 hashset! {},
1298 hashset! {},
1299 Some(mouse_binding::PrecisionScroll::Yes),
1300 None,
1301 ),
1302 },
1303 MouseEvent {
1305 timestamp: zx::MonotonicInstant::from_nanos(3),
1306 mouse_data: mouse_binding::MouseEvent::new(
1307 mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
1308 millimeters: Position { x: 0.0, y: 0.0 },
1309 }),
1310 wheel_delta_mm(0.0),
1311 None,
1312 mouse_binding::MousePhase::Wheel,
1313 hashset! {},
1314 hashset! {},
1315 Some(mouse_binding::PrecisionScroll::Yes),
1316 None,
1317 ),
1318 },
1319 MouseEvent {
1320 timestamp: zx::MonotonicInstant::from_nanos(4),
1321 mouse_data: mouse_binding::MouseEvent::new(
1322 mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
1323 millimeters: Position { x: 0.0, y: 0.0 },
1324 }),
1325 wheel_delta_mm(-12.0),
1326 None,
1327 mouse_binding::MousePhase::Wheel,
1328 hashset! {},
1329 hashset! {},
1330 Some(mouse_binding::PrecisionScroll::Yes),
1331 None,
1332 ),
1333 },
1334 ]
1335 );
1336 pretty_assertions::assert_eq!(got.recognized_gesture, RecognizedGesture::Scroll);
1337 }
1338
1339 #[fuchsia::test]
1340 fn winner_process_new_event_end_gesture_none() {
1341 let winner: Box<dyn gesture_arena::Winner> = Box::new(Winner {
1342 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
1343 direction: ScrollDirection::Up,
1344 last_positions: ContactPositions {
1345 first_contact: ContactPosition { id: 1, position: Position { x: 1.0, y: 1.0 } },
1346 second_contact: ContactPosition { id: 2, position: Position { x: 5.0, y: 5.0 } },
1347 },
1348 });
1349 let event = TouchpadEvent {
1350 timestamp: zx::MonotonicInstant::ZERO,
1351 pressed_buttons: vec![],
1352 contacts: vec![],
1353 filtered_palm_contacts: vec![],
1354 };
1355 let got = winner.process_new_event(event);
1356
1357 assert_matches!(got, ProcessNewEventResult::EndGesture(EndGestureEvent::NoEvent, _reason));
1358 }
1359
1360 #[test_case(
1361 TouchpadEvent{
1362 timestamp: zx::MonotonicInstant::ZERO,
1363 pressed_buttons: vec![1],
1364 contacts: vec![make_touch_contact(1, Position{x: 1.0, y: 1.0})],
1365 filtered_palm_contacts: vec![],
1366 };"button down")]
1367 #[test_case(
1368 TouchpadEvent{
1369 timestamp: zx::MonotonicInstant::ZERO,
1370 pressed_buttons: vec![],
1371 contacts: vec![
1372 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
1373 ],
1374 filtered_palm_contacts: vec![],
1375 };"1 fingers")]
1376 #[test_case(
1377 TouchpadEvent{
1378 timestamp: zx::MonotonicInstant::ZERO,
1379 pressed_buttons: vec![],
1380 contacts: vec![
1381 make_touch_contact(1, Position{x: 1.0, y: 11.0}),
1382 make_touch_contact(2, Position{x: 5.0, y: -6.0}),
1383 ],
1384 filtered_palm_contacts: vec![],
1385 };"1 finger reverse direction")]
1386 #[test_case(
1387 TouchpadEvent{
1388 timestamp: zx::MonotonicInstant::ZERO,
1389 pressed_buttons: vec![],
1390 contacts: vec![
1391 make_touch_contact(1, Position{x: 1.0, y: 11.0}),
1392 make_touch_contact(2, Position{x: 5.0, y: 16.0}),
1393 ],
1394 filtered_palm_contacts: vec![],
1395 };"2 fingers reverse direction")]
1396 #[fuchsia::test]
1397 fn winner_process_new_event_end_gesture_some(event: TouchpadEvent) {
1398 let winner: Box<dyn gesture_arena::Winner> = Box::new(Winner {
1399 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
1400 direction: ScrollDirection::Up,
1401 last_positions: ContactPositions {
1402 first_contact: ContactPosition { id: 1, position: Position { x: 1.0, y: 1.0 } },
1403 second_contact: ContactPosition { id: 2, position: Position { x: 5.0, y: 5.0 } },
1404 },
1405 });
1406 let got = winner.process_new_event(event);
1407
1408 assert_matches!(
1409 got,
1410 ProcessNewEventResult::EndGesture(EndGestureEvent::UnconsumedEvent(_), _reason)
1411 );
1412 }
1413
1414 #[test_case(
1415 TouchpadEvent{
1416 timestamp: zx::MonotonicInstant::ZERO,
1417 pressed_buttons: vec![],
1418 contacts: vec![
1419 make_touch_contact(1, Position{x: 1.0, y: 1.0}),
1420 make_touch_contact(2, Position{x: 5.0, y: 5.0}),
1421 ],
1422 filtered_palm_contacts: vec![],
1423 } => MouseEvent {
1424 timestamp: zx::MonotonicInstant::from_nanos(0),
1425 mouse_data: mouse_binding::MouseEvent {
1426 location: mouse_binding::MouseLocation::Relative(
1427 mouse_binding::RelativeLocation {
1428 millimeters: Position { x: 0.0, y: 0.0 },
1429 }
1430 ),
1431 wheel_delta_v: wheel_delta_mm(0.0),
1432 wheel_delta_h: None,
1433 phase: mouse_binding::MousePhase::Wheel,
1434 affected_buttons: hashset! {},
1435 pressed_buttons: hashset! {},
1436 is_precision_scroll: Some(mouse_binding::PrecisionScroll::Yes),
1437 wake_lease: None.into(),
1438 },
1439 };"finger hold")]
1440 #[test_case(
1441 TouchpadEvent{
1442 timestamp: zx::MonotonicInstant::ZERO,
1443 pressed_buttons: vec![],
1444 contacts: vec![
1445 make_touch_contact(1, Position{x: 10.0, y: -10.0}),
1446 make_touch_contact(2, Position{x: 15.0, y: -15.0}),
1447 ],
1448 filtered_palm_contacts: vec![],
1449 } => MouseEvent {
1450 timestamp: zx::MonotonicInstant::from_nanos(0),
1451 mouse_data: mouse_binding::MouseEvent {
1452 location: mouse_binding::MouseLocation::Relative(
1453 mouse_binding::RelativeLocation {
1454 millimeters: Position { x: 0.0, y: 0.0 },
1455 }
1456 ),
1457 wheel_delta_v: wheel_delta_mm(-15.5),
1458 wheel_delta_h: None,
1459 phase: mouse_binding::MousePhase::Wheel,
1460 affected_buttons: hashset! {},
1461 pressed_buttons: hashset! {},
1462 is_precision_scroll: Some(mouse_binding::PrecisionScroll::Yes),
1463 wake_lease: None.into(),
1464 },
1465 };"direction contact1 only")]
1466 #[test_case(
1467 TouchpadEvent{
1468 timestamp: zx::MonotonicInstant::ZERO,
1469 pressed_buttons: vec![],
1470 contacts: vec![
1471 make_touch_contact(1, Position{x: 1.0, y: -11.0}),
1472 make_touch_contact(2, Position{x: 5.0, y: -5.0}),
1473 ],
1474 filtered_palm_contacts: vec![],
1475 } => MouseEvent {
1476 timestamp: zx::MonotonicInstant::from_nanos(0),
1477 mouse_data: mouse_binding::MouseEvent {
1478 location: mouse_binding::MouseLocation::Relative(
1479 mouse_binding::RelativeLocation {
1480 millimeters: Position { x: 0.0, y: 0.0 },
1481 }
1482 ),
1483 wheel_delta_v: wheel_delta_mm(-11.0),
1484 wheel_delta_h: None,
1485 phase: mouse_binding::MousePhase::Wheel,
1486 affected_buttons: hashset! {},
1487 pressed_buttons: hashset! {},
1488 is_precision_scroll: Some(mouse_binding::PrecisionScroll::Yes),
1489 wake_lease: None.into(),
1490 },
1491 };"on direction")]
1492 #[fuchsia::test]
1493 fn winner_process_new_event_continue_gesture(event: TouchpadEvent) -> MouseEvent {
1494 let winner: Box<dyn gesture_arena::Winner> = Box::new(Winner {
1495 limit_tangent_for_direction: LIMIT_TANGENT_FOR_DIRECTION,
1496 direction: ScrollDirection::Up,
1497 last_positions: ContactPositions {
1498 first_contact: ContactPosition { id: 1, position: Position { x: 1.0, y: 1.0 } },
1499 second_contact: ContactPosition { id: 2, position: Position { x: 5.0, y: 5.0 } },
1500 },
1501 });
1502 let got = winner.process_new_event(event);
1503
1504 match got {
1508 ProcessNewEventResult::EndGesture(..) => {
1509 panic!("Got {:?}, want ContinueGesture()", got)
1510 }
1511 ProcessNewEventResult::ContinueGesture(got_mouse_event, _) => got_mouse_event.unwrap(),
1512 }
1513 }
1514}