multitouch 0.3.0

Raw macOS multitouch, gesture recognition, device monitoring, and Force Touch haptics
Documentation
use multitouch::{
    Contact, ContactState, Finger, FingerCountChange, GestureEndReason, GestureEvent, GesturePhase,
    GestureRecognizer, GestureTypes, Hand, Point, Vector,
};

fn contact(x: f32, y: f32, id: i32, finger: Option<Finger>) -> Contact {
    Contact::new(
        0,
        0.0,
        id,
        ContactState::Touching,
        finger,
        Some(Hand::Right),
        Vector::new(Point::new(x, y), Point::ZERO),
        0.0,
        0.0,
        0.0,
        0.0,
        0.0,
        Vector::new(Point::ZERO, Point::ZERO),
        0.0,
    )
}

fn two(a: (f32, f32), b: (f32, f32)) -> [Contact; 2] {
    [
        contact(a.0, a.1, 1, Some(Finger::Index)),
        contact(b.0, b.1, 2, Some(Finger::Middle)),
    ]
}

#[test]
fn swipe_begins_and_changes() {
    let mut r = GestureRecognizer::new(2);
    assert_eq!(
        r.process(&two((0.3, 0.5), (0.4, 0.5))).unwrap().phase(),
        GesturePhase::Determining
    );

    let event = r.process(&two((0.4, 0.5), (0.5, 0.5))).unwrap();
    let GestureEvent::Swipe(event) = event else {
        panic!("expected swipe")
    };
    assert_eq!(event.phase, GesturePhase::Began);
    assert!(event.translation.x > 0.0);

    let event = r.process(&two((0.5, 0.5), (0.6, 0.5))).unwrap();
    let GestureEvent::Swipe(event) = event else {
        panic!("expected changed swipe")
    };
    assert_eq!(event.phase, GesturePhase::Changed);
}

#[test]
fn magnify_has_priority_over_swipe() {
    let mut r = GestureRecognizer::new(2);
    let _ = r.process(&two((0.45, 0.5), (0.55, 0.5)));
    let event = r.process(&two((0.35, 0.5), (0.75, 0.5))).unwrap();
    assert!(matches!(event, GestureEvent::Magnify(_)));
}

#[test]
fn rotation_is_detected_and_can_be_disabled() {
    let mut r = GestureRecognizer::new(2);
    let _ = r.process(&two((0.3, 0.5), (0.7, 0.5)));
    let event = r.process(&two((0.3268, 0.4), (0.6732, 0.6))).unwrap();
    assert!(matches!(event, GestureEvent::Rotation(_)));

    let mut r =
        GestureRecognizer::new(2).with_gesture_types(GestureTypes::SWIPE | GestureTypes::MAGNIFY);
    let _ = r.process(&two((0.3, 0.5), (0.7, 0.5)));
    let event = r.process(&two((0.3268, 0.4), (0.6732, 0.6))).unwrap();
    assert_eq!(event.phase(), GesturePhase::Determining);
}

#[test]
fn unresolved_lift_and_timeout_are_reported() {
    let mut r = GestureRecognizer::new(2);
    let _ = r.process(&two((0.5, 0.5), (0.6, 0.5)));
    assert!(matches!(
        r.process(&[]),
        Some(GestureEvent::UnresolvedEnded(GestureEndReason::Lifted))
    ));

    let mut r = GestureRecognizer::new(2);
    let _ = r.process(&two((0.5, 0.5), (0.6, 0.5)));
    assert_eq!(
        r.timeout(),
        Some(GestureEvent::UnresolvedEnded(GestureEndReason::TimedOut))
    );
}

#[test]
fn resolved_gesture_is_sticky_until_below_two_fingers() {
    let mut r = GestureRecognizer::new(2);
    let _ = r.process(&two((0.3, 0.5), (0.4, 0.5)));
    let _ = r.process(&two((0.4, 0.5), (0.5, 0.5)));

    let three = [
        contact(0.4, 0.5, 1, Some(Finger::Index)),
        contact(0.5, 0.5, 2, Some(Finger::Middle)),
        contact(0.6, 0.5, 3, Some(Finger::Ring)),
    ];
    let event = r.process(&three).unwrap();
    let GestureEvent::Swipe(event) = event else {
        panic!("expected swipe")
    };
    assert_eq!(event.phase, GesturePhase::Changed);
    assert_eq!(event.finger_count, 3);

    let one = [contact(0.5, 0.5, 1, Some(Finger::Index))];
    let GestureEvent::Swipe(event) = r.process(&one).unwrap() else {
        panic!("expected ended swipe")
    };
    assert_eq!(event.phase, GesturePhase::Ended(GestureEndReason::Lifted));
}

#[test]
fn exact_count_mode_reports_count_change() {
    let mut r = GestureRecognizer::new(3).with_exact_finger_count(true);
    let origin = [
        contact(0.3, 0.5, 1, Some(Finger::Index)),
        contact(0.4, 0.5, 2, Some(Finger::Middle)),
        contact(0.5, 0.5, 3, Some(Finger::Ring)),
    ];
    let moved = [
        contact(0.4, 0.5, 1, Some(Finger::Index)),
        contact(0.5, 0.5, 2, Some(Finger::Middle)),
        contact(0.6, 0.5, 3, Some(Finger::Ring)),
    ];
    let _ = r.process(&origin);
    assert_eq!(r.process(&moved).unwrap().phase(), GesturePhase::Began);

    let event = r.process(&two((0.4, 0.5), (0.5, 0.5))).unwrap();
    assert_eq!(
        event.phase(),
        GesturePhase::Ended(GestureEndReason::FingerCountChanged(
            FingerCountChange::Decreased
        ))
    );
}

#[test]
fn activation_after_finger_count_decrease_is_blocked() {
    let mut r = GestureRecognizer::new(2);
    let three = [
        contact(0.3, 0.5, 1, Some(Finger::Index)),
        contact(0.4, 0.5, 2, Some(Finger::Middle)),
        contact(0.5, 0.5, 3, Some(Finger::Ring)),
    ];
    let _ = r.process(&three);
    assert!(r.process(&two((0.4, 0.5), (0.5, 0.5))).is_none());
    assert!(r.process(&two((0.5, 0.5), (0.6, 0.5))).is_none());
}