use super::gesture_arena::{
self, DetailedReasonFloat, DetailedReasonUint, EndGestureEvent, ExamineEventResult, MouseEvent,
ProcessBufferedEventsResult, ProcessNewEventResult, Reason, RecognizedGesture, TouchpadEvent,
VerifyEventResult,
};
use super::utils::{movement_from_events, MovementDetail};
use crate::mouse_binding::{self, MouseButton};
use crate::utils::{euclidean_distance, Position};
use maplit::hashset;
use std::collections::HashSet;
#[derive(Debug)]
pub(super) struct InitialContender {
pub(super) spurious_to_intentional_motion_threshold_mm: f32,
pub(super) spurious_to_intentional_motion_threshold_button_change_mm: f32,
pub(super) button_change_state_timeout: zx::MonotonicDuration,
}
#[derive(Debug)]
struct FingerContactContender {
spurious_to_intentional_motion_threshold_mm: f32,
spurious_to_intentional_motion_threshold_button_change_mm: f32,
button_change_state_timeout: zx::MonotonicDuration,
initial_position: Position,
}
#[derive(Debug)]
struct MatchedContender {
spurious_to_intentional_motion_threshold_mm: f32,
spurious_to_intentional_motion_threshold_button_change_mm: f32,
button_change_state_timeout: zx::MonotonicDuration,
pressed_event: TouchpadEvent,
}
#[derive(Debug)]
struct ButtonDownWinner {
spurious_to_intentional_motion_threshold_mm: f32,
spurious_to_intentional_motion_threshold_button_change_mm: f32,
button_change_state_timeout: zx::MonotonicDuration,
pressed_event: TouchpadEvent,
}
#[derive(Debug)]
struct DragWinner {
spurious_to_intentional_motion_threshold_button_change_mm: f32,
button_change_state_timeout: zx::MonotonicDuration,
last_event: TouchpadEvent,
}
#[derive(Debug)]
struct ButtonUpWinner {
spurious_to_intentional_motion_threshold_button_change_mm: f32,
button_change_state_timeout: zx::MonotonicDuration,
button_up_event: TouchpadEvent,
}
impl InitialContender {
#[allow(clippy::boxed_local, reason = "mass allow for https://fxbug.dev/381896734")]
fn into_finger_contact_contender(
self: Box<Self>,
initial_position: Position,
) -> Box<dyn gesture_arena::Contender> {
Box::new(FingerContactContender {
spurious_to_intentional_motion_threshold_mm: self
.spurious_to_intentional_motion_threshold_mm,
spurious_to_intentional_motion_threshold_button_change_mm: self
.spurious_to_intentional_motion_threshold_button_change_mm,
button_change_state_timeout: self.button_change_state_timeout,
initial_position,
})
}
#[allow(clippy::boxed_local, reason = "mass allow for https://fxbug.dev/381896734")]
fn into_matched_contender(
self: Box<Self>,
pressed_event: TouchpadEvent,
) -> Box<dyn gesture_arena::MatchedContender> {
Box::new(MatchedContender {
spurious_to_intentional_motion_threshold_mm: self
.spurious_to_intentional_motion_threshold_mm,
spurious_to_intentional_motion_threshold_button_change_mm: self
.spurious_to_intentional_motion_threshold_button_change_mm,
button_change_state_timeout: self.button_change_state_timeout,
pressed_event,
})
}
}
impl gesture_arena::Contender for InitialContender {
fn examine_event(self: Box<Self>, event: &TouchpadEvent) -> ExamineEventResult {
let num_contacts = event.contacts.len();
if num_contacts != 1 {
return ExamineEventResult::Mismatch(Reason::DetailedUint(DetailedReasonUint {
criterion: "num_contacts",
min: Some(1),
max: Some(1),
actual: num_contacts,
}));
}
let num_pressed_buttons = event.pressed_buttons.len();
match num_pressed_buttons {
0 => ExamineEventResult::Contender(
self.into_finger_contact_contender(position_from_event(event)),
),
1 => ExamineEventResult::MatchedContender(self.into_matched_contender(event.clone())),
_ => ExamineEventResult::Mismatch(Reason::DetailedUint(DetailedReasonUint {
criterion: "num_pressed_buttons",
min: Some(0),
max: Some(1),
actual: num_pressed_buttons,
})),
}
}
}
impl FingerContactContender {
#[allow(clippy::boxed_local, reason = "mass allow for https://fxbug.dev/381896734")]
fn into_matched_contender(
self: Box<Self>,
pressed_event: TouchpadEvent,
) -> Box<dyn gesture_arena::MatchedContender> {
Box::new(MatchedContender {
spurious_to_intentional_motion_threshold_mm: self
.spurious_to_intentional_motion_threshold_mm,
spurious_to_intentional_motion_threshold_button_change_mm: self
.spurious_to_intentional_motion_threshold_button_change_mm,
button_change_state_timeout: self.button_change_state_timeout,
pressed_event,
})
}
}
impl gesture_arena::Contender for FingerContactContender {
fn examine_event(self: Box<Self>, event: &TouchpadEvent) -> ExamineEventResult {
let num_contacts = event.contacts.len();
if num_contacts != 1 {
return ExamineEventResult::Mismatch(Reason::DetailedUint(DetailedReasonUint {
criterion: "num_contacts",
min: Some(1),
max: Some(1),
actual: num_contacts,
}));
}
let displacement_mm = euclidean_distance(position_from_event(event), self.initial_position);
if displacement_mm >= self.spurious_to_intentional_motion_threshold_mm {
return ExamineEventResult::Mismatch(Reason::DetailedFloat(DetailedReasonFloat {
criterion: "displacement_mm",
min: None,
max: Some(self.spurious_to_intentional_motion_threshold_mm),
actual: displacement_mm,
}));
}
let num_pressed_buttons = event.pressed_buttons.len();
match num_pressed_buttons {
0 => ExamineEventResult::Contender(self),
1 => ExamineEventResult::MatchedContender(self.into_matched_contender(event.clone())),
_ => ExamineEventResult::Mismatch(Reason::DetailedUint(DetailedReasonUint {
criterion: "num_pressed_buttons",
min: Some(0),
max: Some(1),
actual: num_pressed_buttons,
})),
}
}
}
impl MatchedContender {
#[allow(clippy::boxed_local, reason = "mass allow for https://fxbug.dev/381896734")]
fn into_button_down_winner(self: Box<Self>) -> Box<dyn gesture_arena::Winner> {
Box::new(ButtonDownWinner {
spurious_to_intentional_motion_threshold_mm: self
.spurious_to_intentional_motion_threshold_mm,
spurious_to_intentional_motion_threshold_button_change_mm: self
.spurious_to_intentional_motion_threshold_button_change_mm,
button_change_state_timeout: self.button_change_state_timeout,
pressed_event: self.pressed_event,
})
}
}
impl gesture_arena::MatchedContender for MatchedContender {
fn verify_event(self: Box<Self>, _event: &TouchpadEvent) -> VerifyEventResult {
tracing::error!("Unexpected MatchedContender::verify_event() called");
VerifyEventResult::MatchedContender(self)
}
fn process_buffered_events(
self: Box<Self>,
_events: Vec<TouchpadEvent>,
) -> ProcessBufferedEventsResult {
ProcessBufferedEventsResult {
generated_events: vec![touchpad_event_to_mouse_down_event(&self.pressed_event)],
winner: Some(self.into_button_down_winner()),
recognized_gesture: RecognizedGesture::OneButtonDown,
}
}
}
impl ButtonDownWinner {
#[allow(clippy::boxed_local, reason = "mass allow for https://fxbug.dev/381896734")]
fn into_drag_winner(self: Box<Self>) -> Box<dyn gesture_arena::Winner> {
Box::new(DragWinner {
spurious_to_intentional_motion_threshold_button_change_mm: self
.spurious_to_intentional_motion_threshold_button_change_mm,
button_change_state_timeout: self.button_change_state_timeout,
last_event: self.pressed_event,
})
}
#[allow(clippy::boxed_local, reason = "mass allow for https://fxbug.dev/381896734")]
fn into_button_up(
self: Box<Self>,
button_up_event: TouchpadEvent,
) -> Box<dyn gesture_arena::Winner> {
Box::new(ButtonUpWinner {
spurious_to_intentional_motion_threshold_button_change_mm: self
.spurious_to_intentional_motion_threshold_button_change_mm,
button_change_state_timeout: self.button_change_state_timeout,
button_up_event,
})
}
}
impl gesture_arena::Winner for ButtonDownWinner {
fn process_new_event(self: Box<Self>, event: TouchpadEvent) -> ProcessNewEventResult {
let motion_threshold =
if event.timestamp - self.pressed_event.timestamp > self.button_change_state_timeout {
self.spurious_to_intentional_motion_threshold_mm
} else {
self.spurious_to_intentional_motion_threshold_button_change_mm
};
let num_pressed_buttons = event.pressed_buttons.len();
match num_pressed_buttons {
0 => ProcessNewEventResult::ContinueGesture(
Some(touchpad_event_to_mouse_up_event(&event)),
self.into_button_up(event),
),
1 => {
let MovementDetail { euclidean_distance, movement: _ } =
movement_from_events(&self.pressed_event, &event);
if euclidean_distance > motion_threshold {
let drag_winner = self.into_drag_winner();
return drag_winner.process_new_event(event);
}
ProcessNewEventResult::ContinueGesture(None, self)
}
_ => ProcessNewEventResult::ContinueGesture(None, self),
}
}
}
impl DragWinner {
#[allow(clippy::boxed_local, reason = "mass allow for https://fxbug.dev/381896734")]
fn into_drag_winner(
self: Box<Self>,
last_event: TouchpadEvent,
) -> Box<dyn gesture_arena::Winner> {
Box::new(DragWinner {
spurious_to_intentional_motion_threshold_button_change_mm: self
.spurious_to_intentional_motion_threshold_button_change_mm,
button_change_state_timeout: self.button_change_state_timeout,
last_event,
})
}
#[allow(clippy::boxed_local, reason = "mass allow for https://fxbug.dev/381896734")]
fn into_button_up(
self: Box<Self>,
button_up_event: TouchpadEvent,
) -> Box<dyn gesture_arena::Winner> {
Box::new(ButtonUpWinner {
spurious_to_intentional_motion_threshold_button_change_mm: self
.spurious_to_intentional_motion_threshold_button_change_mm,
button_change_state_timeout: self.button_change_state_timeout,
button_up_event,
})
}
}
impl gesture_arena::Winner for DragWinner {
fn process_new_event(self: Box<Self>, event: TouchpadEvent) -> ProcessNewEventResult {
let num_pressed_buttons = event.pressed_buttons.len();
match num_pressed_buttons {
0 => ProcessNewEventResult::ContinueGesture(
Some(touchpad_event_to_mouse_up_event(&event)),
self.into_button_up(event),
),
_ => {
ProcessNewEventResult::ContinueGesture(
Some(touchpad_event_to_mouse_drag_event(&self.last_event, &event)),
self.into_drag_winner(event),
)
}
}
}
}
impl gesture_arena::Winner for ButtonUpWinner {
fn process_new_event(self: Box<Self>, event: TouchpadEvent) -> ProcessNewEventResult {
let num_contacts = event.contacts.len();
if num_contacts != 1 {
return ProcessNewEventResult::EndGesture(
EndGestureEvent::UnconsumedEvent(event),
Reason::DetailedUint(DetailedReasonUint {
criterion: "num_contacts",
min: Some(1),
max: Some(1),
actual: num_contacts,
}),
);
}
let num_pressed_buttons = event.pressed_buttons.len();
if num_pressed_buttons != 0 {
return ProcessNewEventResult::EndGesture(
EndGestureEvent::UnconsumedEvent(event),
Reason::DetailedUint(DetailedReasonUint {
criterion: "num_buttons",
min: Some(0),
max: Some(0),
actual: num_pressed_buttons,
}),
);
}
if event.timestamp - self.button_up_event.timestamp > self.button_change_state_timeout {
return ProcessNewEventResult::EndGesture(
EndGestureEvent::UnconsumedEvent(event),
Reason::Basic("button_up_timeout"),
);
}
let MovementDetail { euclidean_distance, movement: _ } =
movement_from_events(&self.button_up_event, &event);
if euclidean_distance > self.spurious_to_intentional_motion_threshold_button_change_mm {
return ProcessNewEventResult::EndGesture(
EndGestureEvent::UnconsumedEvent(event),
Reason::DetailedFloat(DetailedReasonFloat {
criterion: "displacement_mm",
min: None,
max: Some(self.spurious_to_intentional_motion_threshold_button_change_mm),
actual: euclidean_distance,
}),
);
}
ProcessNewEventResult::ContinueGesture(None, self)
}
}
fn position_from_event(event: &TouchpadEvent) -> Position {
event.contacts[0].position
}
fn touchpad_event_to_mouse_down_event(event: &TouchpadEvent) -> MouseEvent {
make_mouse_event(
event.timestamp,
Position::zero(),
mouse_binding::MousePhase::Down,
hashset! {1},
hashset! {1},
)
}
fn touchpad_event_to_mouse_up_event(event: &TouchpadEvent) -> MouseEvent {
make_mouse_event(
event.timestamp,
Position::zero(),
mouse_binding::MousePhase::Up,
hashset! {1},
hashset! {},
)
}
fn touchpad_event_to_mouse_drag_event(
last_event: &TouchpadEvent,
event: &TouchpadEvent,
) -> MouseEvent {
let MovementDetail { movement, euclidean_distance: _ } =
movement_from_events(last_event, event);
make_mouse_event(
event.timestamp,
movement,
mouse_binding::MousePhase::Move,
hashset! {},
hashset! {1},
)
}
fn make_mouse_event(
timestamp: zx::MonotonicInstant,
movement_in_mm: Position,
phase: mouse_binding::MousePhase,
affected_buttons: HashSet<MouseButton>,
pressed_buttons: HashSet<MouseButton>,
) -> MouseEvent {
MouseEvent {
timestamp,
mouse_data: mouse_binding::MouseEvent::new(
mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
millimeters: movement_in_mm,
}),
None,
None,
phase,
affected_buttons,
pressed_buttons,
None,
),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::touch_binding;
use assert_matches::assert_matches;
use test_case::test_case;
fn make_touch_contact(id: u32, position: Position) -> touch_binding::TouchContact {
touch_binding::TouchContact { id, position, pressure: None, contact_size: None }
}
const SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM: f32 = 10.0;
const SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM: f32 = 20.0;
const BUTTON_CHANGE_STATE_TIMEOUT: zx::MonotonicDuration =
zx::MonotonicDuration::from_seconds(1);
#[test_case(TouchpadEvent{
timestamp: zx::MonotonicInstant::ZERO,
pressed_buttons: vec![],
contacts: vec![],
filtered_palm_contacts: vec![],
};"0 fingers")]
#[test_case(TouchpadEvent{
timestamp: zx::MonotonicInstant::ZERO,
pressed_buttons: vec![],
contacts: vec![
make_touch_contact(1, Position{x: 1.0, y: 1.0}),
make_touch_contact(2, Position{x: 5.0, y: 5.0}),
],
filtered_palm_contacts: vec![],
};"2 fingers")]
#[fuchsia::test]
fn initial_contender_examine_event_mismatch(event: TouchpadEvent) {
let contender: Box<dyn gesture_arena::Contender> = Box::new(InitialContender {
spurious_to_intentional_motion_threshold_mm:
SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
spurious_to_intentional_motion_threshold_button_change_mm:
SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
});
let got = contender.examine_event(&event);
assert_matches!(got, ExamineEventResult::Mismatch(_));
}
#[fuchsia::test]
fn initial_contender_examine_event_finger_contact_contender() {
let contender: Box<dyn gesture_arena::Contender> = Box::new(InitialContender {
spurious_to_intentional_motion_threshold_mm:
SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
spurious_to_intentional_motion_threshold_button_change_mm:
SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
});
let event = TouchpadEvent {
timestamp: zx::MonotonicInstant::ZERO,
pressed_buttons: vec![],
contacts: vec![make_touch_contact(1, Position { x: 1.0, y: 1.0 })],
filtered_palm_contacts: vec![],
};
let got = contender.examine_event(&event);
assert_matches!(got, ExamineEventResult::Contender(_));
}
#[fuchsia::test]
fn initial_contender_examine_event_matched_contender() {
let contender: Box<dyn gesture_arena::Contender> = Box::new(InitialContender {
spurious_to_intentional_motion_threshold_mm:
SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
spurious_to_intentional_motion_threshold_button_change_mm:
SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
});
let event = TouchpadEvent {
timestamp: zx::MonotonicInstant::ZERO,
pressed_buttons: vec![1],
contacts: vec![make_touch_contact(1, Position { x: 1.0, y: 1.0 })],
filtered_palm_contacts: vec![],
};
let got = contender.examine_event(&event);
assert_matches!(got, ExamineEventResult::MatchedContender(_));
}
#[test_case(TouchpadEvent{
timestamp: zx::MonotonicInstant::ZERO,
pressed_buttons: vec![],
contacts: vec![],
filtered_palm_contacts: vec![],
};"0 fingers")]
#[test_case(TouchpadEvent{
timestamp: zx::MonotonicInstant::ZERO,
pressed_buttons: vec![],
contacts: vec![
make_touch_contact(1, Position{x: 1.0, y: 1.0}),
make_touch_contact(2, Position{x: 5.0, y: 5.0}),
],
filtered_palm_contacts: vec![],
};"2 fingers")]
#[test_case(TouchpadEvent{
timestamp: zx::MonotonicInstant::ZERO,
pressed_buttons: vec![],
contacts: vec![
make_touch_contact(1, Position{x: 10.0, y: 1.0}),
],
filtered_palm_contacts: vec![],
};"1 fingers move more than threshold")]
#[fuchsia::test]
fn finger_contact_contender_examine_event_mismatch(event: TouchpadEvent) {
let contender: Box<dyn gesture_arena::Contender> = Box::new(FingerContactContender {
spurious_to_intentional_motion_threshold_mm:
SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
spurious_to_intentional_motion_threshold_button_change_mm:
SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
initial_position: Position { x: 0.0, y: 0.0 },
});
let got = contender.examine_event(&event);
assert_matches!(got, ExamineEventResult::Mismatch(_));
}
#[test_case(TouchpadEvent{
timestamp: zx::MonotonicInstant::ZERO,
pressed_buttons: vec![],
contacts: vec![
make_touch_contact(1, Position{x: 9.0, y: 1.0}),
],
filtered_palm_contacts: vec![],
};"1 fingers move less than threshold")]
#[test_case(TouchpadEvent{
timestamp: zx::MonotonicInstant::ZERO,
pressed_buttons: vec![],
contacts: vec![
make_touch_contact(1, Position{x: 0.0, y: 0.0}),
],
filtered_palm_contacts: vec![],
};"1 fingers stay")]
#[fuchsia::test]
fn finger_contact_contender_examine_event_finger_contact_contender(event: TouchpadEvent) {
let contender: Box<dyn gesture_arena::Contender> = Box::new(FingerContactContender {
spurious_to_intentional_motion_threshold_mm:
SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
spurious_to_intentional_motion_threshold_button_change_mm:
SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
initial_position: Position { x: 0.0, y: 0.0 },
});
let got = contender.examine_event(&event);
assert_matches!(got, ExamineEventResult::Contender(_));
}
#[fuchsia::test]
fn finger_contact_contender_examine_event_matched_contender() {
let contender: Box<dyn gesture_arena::Contender> = Box::new(FingerContactContender {
spurious_to_intentional_motion_threshold_mm:
SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
spurious_to_intentional_motion_threshold_button_change_mm:
SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
initial_position: Position { x: 0.0, y: 0.0 },
});
let event = TouchpadEvent {
timestamp: zx::MonotonicInstant::ZERO,
pressed_buttons: vec![1],
contacts: vec![make_touch_contact(1, Position { x: 1.0, y: 1.0 })],
filtered_palm_contacts: vec![],
};
let got = contender.examine_event(&event);
assert_matches!(got, ExamineEventResult::MatchedContender(_));
}
#[fuchsia::test]
fn matched_contender_process_buffered_events() {
let contender: Box<dyn gesture_arena::MatchedContender> = Box::new(MatchedContender {
spurious_to_intentional_motion_threshold_mm:
SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
spurious_to_intentional_motion_threshold_button_change_mm:
SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
pressed_event: TouchpadEvent {
timestamp: zx::MonotonicInstant::from_nanos(41),
pressed_buttons: vec![1],
contacts: vec![make_touch_contact(1, Position { x: 1.0, y: 1.0 })],
filtered_palm_contacts: vec![],
},
});
let events = vec![
TouchpadEvent {
timestamp: zx::MonotonicInstant::ZERO,
pressed_buttons: vec![],
contacts: vec![make_touch_contact(1, Position { x: 1.0, y: 1.0 })],
filtered_palm_contacts: vec![],
},
TouchpadEvent {
timestamp: zx::MonotonicInstant::from_nanos(41),
pressed_buttons: vec![1],
contacts: vec![make_touch_contact(1, Position { x: 1.0, y: 1.0 })],
filtered_palm_contacts: vec![],
},
];
let got = contender.process_buffered_events(events);
assert_matches!(got, ProcessBufferedEventsResult{
generated_events,
winner: Some(winner),
recognized_gesture: RecognizedGesture::OneButtonDown,
} => {
pretty_assertions::assert_eq!(generated_events, vec![
MouseEvent {
timestamp:zx::MonotonicInstant::from_nanos(41),
mouse_data: mouse_binding::MouseEvent::new(
mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
millimeters: Position { x: 0.0, y: 0.0 },
}),
None,
None,
mouse_binding::MousePhase::Down,
hashset!{1},
hashset!{1},
None,
),
}
]);
pretty_assertions::assert_eq!(winner.get_type_name(), "input_pipeline_lib_test::gestures::one_finger_button::ButtonDownWinner");
});
}
#[test_case(TouchpadEvent{
timestamp: zx::MonotonicInstant::from_nanos(41),
pressed_buttons: vec![],
contacts: vec![
make_touch_contact(1, Position{x: 1.0, y: 1.0}),
],
filtered_palm_contacts: vec![],
};"button release")]
#[test_case(TouchpadEvent{
timestamp: zx::MonotonicInstant::from_nanos(41),
pressed_buttons: vec![],
contacts: vec![
make_touch_contact(1, Position{x: 19.0, y: 1.0}),
],
filtered_palm_contacts: vec![],
};"move less than threshold in edge state")]
#[test_case(TouchpadEvent{
timestamp: zx::MonotonicInstant::ZERO + zx::MonotonicDuration::from_millis(1500),
pressed_buttons: vec![],
contacts: vec![
make_touch_contact(1, Position{x: 9.0, y: 1.0}),
],
filtered_palm_contacts: vec![],
};"move less than threshold out of edge state")]
#[test_case(TouchpadEvent{
timestamp: zx::MonotonicInstant::from_nanos(41),
pressed_buttons: vec![],
contacts: vec![
make_touch_contact(1, Position{x: 20.0, y: 1.0}),
],
filtered_palm_contacts: vec![],
};"move more than threshold in edge state and release button")]
#[test_case(TouchpadEvent{
timestamp: zx::MonotonicInstant::ZERO + zx::MonotonicDuration::from_millis(1500),
pressed_buttons: vec![],
contacts: vec![
make_touch_contact(1, Position{x: 10.0, y: 1.0}),
],
filtered_palm_contacts: vec![],
};"move more than threshold out of edge state and release button")]
#[fuchsia::test]
fn button_down_winner_button_up(event: TouchpadEvent) {
let winner: Box<dyn gesture_arena::Winner> = Box::new(ButtonDownWinner {
spurious_to_intentional_motion_threshold_mm:
SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
spurious_to_intentional_motion_threshold_button_change_mm:
SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
pressed_event: TouchpadEvent {
timestamp: zx::MonotonicInstant::ZERO,
pressed_buttons: vec![1],
contacts: vec![make_touch_contact(1, Position { x: 0.0, y: 0.0 })],
filtered_palm_contacts: vec![],
},
});
let got = winner.process_new_event(event);
assert_matches!(got, ProcessNewEventResult::ContinueGesture(Some(MouseEvent {mouse_data, ..}), got_winner) => {
pretty_assertions::assert_eq!(mouse_data, mouse_binding::MouseEvent::new(
mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
millimeters: Position { x: 0.0, y: 0.0 },
}),
None,
None,
mouse_binding::MousePhase::Up,
hashset!{1},
hashset!{},
None,
));
pretty_assertions::assert_eq!(got_winner.get_type_name(), "input_pipeline_lib_test::gestures::one_finger_button::ButtonUpWinner");
});
}
#[test_case(TouchpadEvent{
timestamp: zx::MonotonicInstant::from_nanos(41),
pressed_buttons: vec![1],
contacts: vec![
make_touch_contact(1, Position{x: 19.0, y: 1.0}),
],
filtered_palm_contacts: vec![],
};"move less than threshold in edge state")]
#[test_case(TouchpadEvent{
timestamp: zx::MonotonicInstant::ZERO + zx::MonotonicDuration::from_millis(1500),
pressed_buttons: vec![1],
contacts: vec![
make_touch_contact(1, Position{x: 9.0, y: 1.0}),
],
filtered_palm_contacts: vec![],
};"move less than threshold out of edge state")]
#[test_case(TouchpadEvent{
timestamp: zx::MonotonicInstant::from_nanos(41),
pressed_buttons: vec![1],
contacts: vec![
make_touch_contact(1, Position{x: 1.0, y: 1.0}),
make_touch_contact(2, Position{x: 1.0, y: 2.0}),
],
filtered_palm_contacts: vec![],
};"add more finger")]
#[fuchsia::test]
fn button_down_winner_continue(event: TouchpadEvent) {
let winner: Box<dyn gesture_arena::Winner> = Box::new(ButtonDownWinner {
spurious_to_intentional_motion_threshold_mm:
SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
spurious_to_intentional_motion_threshold_button_change_mm:
SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
pressed_event: TouchpadEvent {
timestamp: zx::MonotonicInstant::ZERO,
pressed_buttons: vec![1],
contacts: vec![make_touch_contact(1, Position { x: 0.0, y: 0.0 })],
filtered_palm_contacts: vec![],
},
});
let got = winner.process_new_event(event);
assert_matches!(got, ProcessNewEventResult::ContinueGesture(None, got_winner)=>{
pretty_assertions::assert_eq!(got_winner.get_type_name(), "input_pipeline_lib_test::gestures::one_finger_button::ButtonDownWinner");
});
}
#[test_case(TouchpadEvent{
timestamp: zx::MonotonicInstant::from_nanos(41),
pressed_buttons: vec![1],
contacts: vec![
make_touch_contact(1, Position{x: 20.0, y: 1.0}),
],
filtered_palm_contacts: vec![],
};"move more than threshold in edge state")]
#[test_case(TouchpadEvent{
timestamp: zx::MonotonicInstant::ZERO + zx::MonotonicDuration::from_millis(1500),
pressed_buttons: vec![1],
contacts: vec![
make_touch_contact(1, Position{x: 10.0, y: 1.0}),
],
filtered_palm_contacts: vec![],
};"move more than threshold out of edge state")]
#[fuchsia::test]
fn button_down_winner_drag_winner_continue(event: TouchpadEvent) {
let winner: Box<dyn gesture_arena::Winner> = Box::new(ButtonDownWinner {
spurious_to_intentional_motion_threshold_mm:
SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_MM,
spurious_to_intentional_motion_threshold_button_change_mm:
SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
pressed_event: TouchpadEvent {
timestamp: zx::MonotonicInstant::ZERO,
pressed_buttons: vec![1],
contacts: vec![make_touch_contact(1, Position { x: 0.0, y: 0.0 })],
filtered_palm_contacts: vec![],
},
});
let got = winner.process_new_event(event);
assert_matches!(got, ProcessNewEventResult::ContinueGesture(Some(MouseEvent {mouse_data, ..}), got_winner)=>{
pretty_assertions::assert_eq!(mouse_data.phase, mouse_binding::MousePhase::Move);
pretty_assertions::assert_eq!(got_winner.get_type_name(), "input_pipeline_lib_test::gestures::one_finger_button::DragWinner");
});
}
#[test_case(TouchpadEvent{
timestamp: zx::MonotonicInstant::from_nanos(41),
pressed_buttons: vec![],
contacts: vec![
make_touch_contact(1, Position{x: 1.0, y: 1.0}),
],
filtered_palm_contacts: vec![],
};"button release")]
#[test_case(TouchpadEvent{
timestamp: zx::MonotonicInstant::from_nanos(41),
pressed_buttons: vec![],
contacts: vec![
make_touch_contact(1, Position{x: 19.0, y: 1.0}),
],
filtered_palm_contacts: vec![],
};"move and button release")]
#[fuchsia::test]
fn drag_winner_button_up(event: TouchpadEvent) {
let winner: Box<dyn gesture_arena::Winner> = Box::new(DragWinner {
spurious_to_intentional_motion_threshold_button_change_mm:
SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
last_event: TouchpadEvent {
timestamp: zx::MonotonicInstant::ZERO,
pressed_buttons: vec![1],
contacts: vec![make_touch_contact(1, Position { x: 0.0, y: 0.0 })],
filtered_palm_contacts: vec![],
},
});
let got = winner.process_new_event(event);
assert_matches!(got, ProcessNewEventResult::ContinueGesture(Some(MouseEvent {mouse_data, ..}), got_winner) => {
pretty_assertions::assert_eq!(mouse_data, mouse_binding::MouseEvent::new(
mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
millimeters: Position { x: 0.0, y: 0.0 },
}),
None,
None,
mouse_binding::MousePhase::Up,
hashset!{1},
hashset!{},
None,
));
pretty_assertions::assert_eq!(got_winner.get_type_name(), "input_pipeline_lib_test::gestures::one_finger_button::ButtonUpWinner");
});
}
#[fuchsia::test]
fn drag_winner_continue() {
let winner: Box<dyn gesture_arena::Winner> = Box::new(DragWinner {
spurious_to_intentional_motion_threshold_button_change_mm:
SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
last_event: TouchpadEvent {
timestamp: zx::MonotonicInstant::ZERO,
pressed_buttons: vec![1],
contacts: vec![make_touch_contact(1, Position { x: 0.0, y: 0.0 })],
filtered_palm_contacts: vec![],
},
});
let event = TouchpadEvent {
timestamp: zx::MonotonicInstant::from_nanos(41),
pressed_buttons: vec![1],
contacts: vec![make_touch_contact(1, Position { x: 19.0, y: 1.0 })],
filtered_palm_contacts: vec![],
};
let got = winner.process_new_event(event);
assert_matches!(got, ProcessNewEventResult::ContinueGesture(Some(MouseEvent {mouse_data, ..}), got_winner)=>{
pretty_assertions::assert_eq!(mouse_data, mouse_binding::MouseEvent::new(
mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
millimeters: Position { x: 19.0, y: 1.0 },
}),
None,
None,
mouse_binding::MousePhase::Move,
hashset!{},
hashset!{1},
None,
));
pretty_assertions::assert_eq!(got_winner.get_type_name(), "input_pipeline_lib_test::gestures::one_finger_button::DragWinner");
});
}
#[fuchsia::test]
fn drag_winner_continue_2_finger() {
let mut winner: Box<dyn gesture_arena::Winner> = Box::new(DragWinner {
spurious_to_intentional_motion_threshold_button_change_mm:
SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
last_event: TouchpadEvent {
timestamp: zx::MonotonicInstant::ZERO,
pressed_buttons: vec![1],
contacts: vec![make_touch_contact(1, Position { x: 0.0, y: 0.0 })],
filtered_palm_contacts: vec![],
},
});
let event = TouchpadEvent {
timestamp: zx::MonotonicInstant::from_nanos(41),
pressed_buttons: vec![1],
contacts: vec![
make_touch_contact(1, Position { x: 0.0, y: 0.0 }),
make_touch_contact(2, Position { x: 0.0, y: 5.0 }),
],
filtered_palm_contacts: vec![],
};
let got = winner.process_new_event(event);
assert_matches!(got, ProcessNewEventResult::ContinueGesture(Some(MouseEvent {mouse_data, ..}), got_winner)=>{
pretty_assertions::assert_eq!(mouse_data, mouse_binding::MouseEvent::new(
mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
millimeters: Position { x: 0.0, y: 0.0 },
}),
None,
None,
mouse_binding::MousePhase::Move,
hashset!{},
hashset!{1},
None,
));
winner = got_winner;
pretty_assertions::assert_eq!(winner.get_type_name(), "input_pipeline_lib_test::gestures::one_finger_button::DragWinner");
});
let event = TouchpadEvent {
timestamp: zx::MonotonicInstant::from_nanos(41),
pressed_buttons: vec![1],
contacts: vec![
make_touch_contact(1, Position { x: 0.0, y: 0.0 }),
make_touch_contact(2, Position { x: 19.0, y: 5.0 }),
],
filtered_palm_contacts: vec![],
};
let got = winner.process_new_event(event);
assert_matches!(got, ProcessNewEventResult::ContinueGesture(Some(MouseEvent {mouse_data, ..}), got_winner)=>{
pretty_assertions::assert_eq!(mouse_data, mouse_binding::MouseEvent::new(
mouse_binding::MouseLocation::Relative(mouse_binding::RelativeLocation {
millimeters: Position { x: 19.0, y: 0.0 },
}),
None,
None,
mouse_binding::MousePhase::Move,
hashset!{},
hashset!{1},
None,
));
pretty_assertions::assert_eq!(got_winner.get_type_name(), "input_pipeline_lib_test::gestures::one_finger_button::DragWinner");
});
}
#[fuchsia::test]
fn button_up_winner_continue() {
let winner: Box<dyn gesture_arena::Winner> = Box::new(ButtonUpWinner {
spurious_to_intentional_motion_threshold_button_change_mm:
SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
button_up_event: TouchpadEvent {
timestamp: zx::MonotonicInstant::ZERO,
pressed_buttons: vec![],
contacts: vec![make_touch_contact(1, Position { x: 0.0, y: 0.0 })],
filtered_palm_contacts: vec![],
},
});
let event = TouchpadEvent {
timestamp: zx::MonotonicInstant::from_nanos(41),
pressed_buttons: vec![],
contacts: vec![make_touch_contact(1, Position { x: 10.0, y: 1.0 })],
filtered_palm_contacts: vec![],
};
let got = winner.process_new_event(event);
assert_matches!(got, ProcessNewEventResult::ContinueGesture(None, got_winner)=>{
pretty_assertions::assert_eq!(got_winner.get_type_name(), "input_pipeline_lib_test::gestures::one_finger_button::ButtonUpWinner");
});
}
#[test_case(TouchpadEvent{
timestamp: zx::MonotonicInstant::ZERO + zx::MonotonicDuration::from_millis(1_001),
pressed_buttons: vec![],
contacts: vec![
make_touch_contact(1, Position{x: 0.0, y: 0.0}),
],
filtered_palm_contacts: vec![],
};"timeout")]
#[test_case(TouchpadEvent{
timestamp: zx::MonotonicInstant::from_nanos(41),
pressed_buttons: vec![1],
contacts: vec![
make_touch_contact(1, Position{x: 0.0, y: 0.0}),
],
filtered_palm_contacts: vec![],
};"button down")]
#[test_case(TouchpadEvent{
timestamp: zx::MonotonicInstant::from_nanos(41),
pressed_buttons: vec![],
contacts: vec![
make_touch_contact(1, Position{x: 21.0, y: 0.0}),
],
filtered_palm_contacts: vec![],
};"move more than threshold")]
#[test_case(TouchpadEvent{
timestamp: zx::MonotonicInstant::from_nanos(41),
pressed_buttons: vec![],
contacts: vec![
make_touch_contact(1, Position{x: 0.0, y: 0.0}),
make_touch_contact(2, Position{x: 10.0, y: 10.0}),
],
filtered_palm_contacts: vec![],
};"more contacts")]
#[test_case(TouchpadEvent{
timestamp: zx::MonotonicInstant::from_nanos(41),
pressed_buttons: vec![],
contacts: vec![],
filtered_palm_contacts: vec![],
};"no contact")]
#[fuchsia::test]
fn button_up_winner_end(event: TouchpadEvent) {
let winner: Box<dyn gesture_arena::Winner> = Box::new(ButtonUpWinner {
spurious_to_intentional_motion_threshold_button_change_mm:
SPURIOUS_TO_INTENTIONAL_MOTION_THRESHOLD_BUTTON_CHANGE_MM,
button_change_state_timeout: BUTTON_CHANGE_STATE_TIMEOUT,
button_up_event: TouchpadEvent {
timestamp: zx::MonotonicInstant::ZERO,
pressed_buttons: vec![],
contacts: vec![make_touch_contact(1, Position { x: 0.0, y: 0.0 })],
filtered_palm_contacts: vec![],
},
});
let got = winner.process_new_event(event);
assert_matches!(
got,
ProcessNewEventResult::EndGesture(EndGestureEvent::UnconsumedEvent(_), _)
);
}
}