972 lines
40 KiB
C++
972 lines
40 KiB
C++
// Copyright 2012 The ChromiumOS Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include <map>
|
|
#include <set>
|
|
|
|
#include <gtest/gtest.h> // for FRIEND_TEST
|
|
|
|
#include "include/finger_metrics.h"
|
|
#include "include/gestures.h"
|
|
#include "include/interpreter.h"
|
|
#include "include/macros.h"
|
|
#include "include/prop_registry.h"
|
|
#include "include/tracer.h"
|
|
|
|
#ifndef GESTURES_IMMEDIATE_INTERPRETER_H_
|
|
#define GESTURES_IMMEDIATE_INTERPRETER_H_
|
|
|
|
namespace gestures {
|
|
|
|
typedef std::set<short> FingerMap;
|
|
|
|
// This interpreter keeps some memory of the past and, for each incoming
|
|
// frame of hardware state, immediately determines the gestures to the best
|
|
// of its abilities.
|
|
|
|
class ImmediateInterpreter;
|
|
class MultitouchMouseInterpreter;
|
|
|
|
class TapRecord {
|
|
FRIEND_TEST(ImmediateInterpreterTest, TapRecordTest);
|
|
public:
|
|
explicit TapRecord(const ImmediateInterpreter* immediate_interpreter)
|
|
: immediate_interpreter_(immediate_interpreter),
|
|
t5r2_(false),
|
|
t5r2_touched_size_(0),
|
|
t5r2_released_size_(0),
|
|
fingers_below_max_age_(true) {}
|
|
void Update(const HardwareState& hwstate,
|
|
const HardwareState& prev_hwstate,
|
|
const std::set<short>& added,
|
|
const std::set<short>& removed,
|
|
const std::set<short>& dead);
|
|
void Clear();
|
|
|
|
// if any gesturing fingers are moving
|
|
bool Moving(const HardwareState& hwstate, const float dist_max) const;
|
|
bool Motionless(const HardwareState& hwstate,
|
|
const HardwareState& prev_hwstate,
|
|
const float max_speed) const;
|
|
|
|
bool TapBegan() const; // if a tap has begun
|
|
bool TapComplete() const; // is a completed tap
|
|
// return GESTURES_BUTTON_* value or 0, if tap was too light
|
|
int TapType() const;
|
|
// If any contact has met the minimum pressure threshold
|
|
bool MinTapPressureMet() const;
|
|
bool FingersBelowMaxAge() const;
|
|
private:
|
|
void NoteTouch(short the_id, const FingerState& fs); // Adds to touched_
|
|
void NoteRelease(short the_id); // Adds to released_
|
|
void Remove(short the_id); // Removes from touched_ and released_
|
|
|
|
float CotapMinPressure() const;
|
|
|
|
std::map<short, FingerState> touched_;
|
|
std::set<short> released_;
|
|
// At least one finger must meet the minimum pressure requirement during a
|
|
// tap. This set contains the fingers that have.
|
|
std::set<short> min_tap_pressure_met_;
|
|
// All fingers must meet the cotap pressure, which is half of the min tap
|
|
// pressure.
|
|
std::set<short> min_cotap_pressure_met_;
|
|
// Used to fetch properties
|
|
const ImmediateInterpreter* immediate_interpreter_;
|
|
// T5R2: For these pads, we try to track individual IDs, but if we get an
|
|
// input event with insufficient data, we switch into T5R2 mode, where we
|
|
// just track the number of contacts. We still maintain the non-T5R2 records
|
|
// which are useful for tracking if contacts move a lot.
|
|
// The following are for T5R2 mode:
|
|
bool t5r2_; // if set, use T5R2 hacks
|
|
unsigned short t5r2_touched_size_; // number of contacts that have arrived
|
|
unsigned short t5r2_released_size_; // number of contacts that have left
|
|
// Whether all the fingers have age less than "Tap Maximum Finger Age".
|
|
bool fingers_below_max_age_;
|
|
};
|
|
|
|
struct ScrollEvent {
|
|
float dx, dy, dt;
|
|
static ScrollEvent Add(const ScrollEvent& evt_a, const ScrollEvent& evt_b);
|
|
};
|
|
class ScrollEventBuffer {
|
|
public:
|
|
explicit ScrollEventBuffer(size_t size)
|
|
: buf_(new ScrollEvent[size]), max_size_(size), size_(0), head_(0) {}
|
|
void Insert(float dx, float dy, float dt);
|
|
void Clear();
|
|
size_t Size() const { return size_; }
|
|
// 0 is newest, 1 is next newest, ..., size_ - 1 is oldest.
|
|
const ScrollEvent& Get(size_t offset) const;
|
|
// For efficiency, returns dist_sq and time of the last num_events events in
|
|
// the buffer, from which speed can be computed.
|
|
void GetSpeedSq(size_t num_events, float* dist_sq, float* dt) const;
|
|
|
|
private:
|
|
std::unique_ptr<ScrollEvent[]> buf_;
|
|
size_t max_size_;
|
|
size_t size_;
|
|
size_t head_;
|
|
DISALLOW_COPY_AND_ASSIGN(ScrollEventBuffer);
|
|
};
|
|
|
|
// Circular buffer for storing a rolling backlog of events for analysis
|
|
// as well as accessor functions for using the buffer's contents.
|
|
class HardwareStateBuffer {
|
|
public:
|
|
explicit HardwareStateBuffer(size_t size);
|
|
~HardwareStateBuffer();
|
|
|
|
size_t Size() const { return size_; }
|
|
|
|
void Reset(size_t max_finger_cnt);
|
|
|
|
// Does a deep copy of state into states_
|
|
void PushState(const HardwareState& state);
|
|
// Pops most recently pushed state
|
|
void PopState();
|
|
|
|
const HardwareState* Get(size_t idx) const {
|
|
return &states_[(idx + newest_index_) % size_];
|
|
}
|
|
|
|
HardwareState* Get(size_t idx) {
|
|
return const_cast<HardwareState*>(
|
|
const_cast<const HardwareStateBuffer*>(this)->Get(idx));
|
|
}
|
|
|
|
private:
|
|
std::unique_ptr<HardwareState[]> states_;
|
|
size_t newest_index_;
|
|
size_t size_;
|
|
size_t max_finger_cnt_;
|
|
DISALLOW_COPY_AND_ASSIGN(HardwareStateBuffer);
|
|
};
|
|
|
|
struct Point {
|
|
Point() : x_(0.0), y_(0.0) {}
|
|
Point(float x, float y) : x_(x), y_(y) {}
|
|
bool operator==(const Point& that) const {
|
|
return x_ == that.x_ && y_ == that.y_;
|
|
}
|
|
bool operator!=(const Point& that) const { return !((*this) == that); }
|
|
float x_, y_;
|
|
};
|
|
|
|
// Helper class for compute scroll and fling.
|
|
class ScrollManager {
|
|
FRIEND_TEST(ImmediateInterpreterTest, FlingDepthTest);
|
|
FRIEND_TEST(ImmediateInterpreterTest, ScrollManagerTest);
|
|
FRIEND_TEST(MultitouchMouseInterpreterTest, SimpleTest);
|
|
|
|
public:
|
|
explicit ScrollManager(PropRegistry* prop_reg);
|
|
~ScrollManager() {}
|
|
|
|
// Returns true if a finger's movement should be suppressed based on
|
|
// max_stationary_move_* properties below.
|
|
bool SuppressStationaryFingerMovement(const FingerState& fs,
|
|
const FingerState& prev,
|
|
stime_t dt);
|
|
|
|
// Looking at this finger and the previous ones within a small window
|
|
// and returns true iff this finger is stationary and the pressure is
|
|
// changing so quickly that we expect it's arriving on the pad or
|
|
// departing.
|
|
bool StationaryFingerPressureChangingSignificantly(
|
|
const HardwareStateBuffer& state_buffer,
|
|
const FingerState& current) const;
|
|
|
|
// Compute a scroll and fill result. Return false when something goes wrong.
|
|
bool FillResultScroll(const HardwareStateBuffer& state_buffer,
|
|
const FingerMap& prev_gs_fingers,
|
|
const FingerMap& gs_fingers,
|
|
GestureType prev_gesture_type,
|
|
const Gesture& prev_result,
|
|
Gesture* result,
|
|
ScrollEventBuffer* scroll_buffer);
|
|
|
|
// Compute a fling and fill result.
|
|
void FillResultFling(const HardwareStateBuffer& state_buffer,
|
|
const ScrollEventBuffer& scroll_buffer,
|
|
Gesture* result);
|
|
|
|
// Update ScrollEventBuffer when the current gesture type is not scroll.
|
|
void UpdateScrollEventBuffer(GestureType gesture_type,
|
|
ScrollEventBuffer* scroll_buffer) const;
|
|
|
|
void ResetSameFingerState() {
|
|
stationary_start_positions_.clear();
|
|
}
|
|
|
|
// Set to true when a scroll or move is blocked b/c of high pressure
|
|
// change or small movement. Cleared when a normal scroll or move
|
|
// goes through.
|
|
bool prev_result_suppress_finger_movement_;
|
|
|
|
private:
|
|
// Set to true when generating a non-zero scroll gesture. Reset to false
|
|
// when a fling is generated.
|
|
bool did_generate_scroll_;
|
|
|
|
// Returns the number of most recent event events in the scroll_buffer_ that
|
|
// should be considered for fling. If it returns 0, there should be no fling.
|
|
size_t ScrollEventsForFlingCount(const ScrollEventBuffer& scroll_buffer)
|
|
const;
|
|
|
|
// Returns a ScrollEvent that contains velocity estimates for x and y based
|
|
// on an N-point linear regression.
|
|
void RegressScrollVelocity(const ScrollEventBuffer& scroll_buffer,
|
|
int count, ScrollEvent* out) const;
|
|
|
|
std::map<short, Point> stationary_start_positions_;
|
|
|
|
// In addition to checking for large pressure changes when moving
|
|
// slow, we can suppress all motion under a certain speed, unless
|
|
// the total distance exceeds a threshold.
|
|
DoubleProperty max_stationary_move_speed_;
|
|
DoubleProperty max_stationary_move_speed_hysteresis_;
|
|
DoubleProperty max_stationary_move_suppress_distance_;
|
|
|
|
// A finger must change in pressure by less than this per second to trigger
|
|
// motion.
|
|
DoubleProperty max_pressure_change_;
|
|
// If a contact crosses max_pressure_change_, motion continues to be blocked
|
|
// until the pressure change per second goes below
|
|
// max_pressure_change_hysteresis_.
|
|
DoubleProperty max_pressure_change_hysteresis_;
|
|
// Try to look over a period up to this length of time when looking for large
|
|
// pressure change.
|
|
DoubleProperty max_pressure_change_duration_;
|
|
// A fast-swiping finger may generate rapidly changing pressure and we should
|
|
// not report a high pressure change in this case. This is the maximum
|
|
// speed [mm/s] for which we may consider a finger stationary.
|
|
DoubleProperty max_stationary_speed_;
|
|
|
|
// y| V /
|
|
// | / D _-
|
|
// | / _-'
|
|
// | / _-'
|
|
// |/_-' H
|
|
// |'____________x
|
|
// The above quadrant of a cartesian plane shows the angles where we snap
|
|
// scrolling to vertical or horizontal. Very Vertical or Horizontal scrolls
|
|
// are snapped, while Diagonal scrolls are not. The two properties below
|
|
// are the slopes for the two lines.
|
|
DoubleProperty vertical_scroll_snap_slope_;
|
|
DoubleProperty horizontal_scroll_snap_slope_;
|
|
|
|
// Depth of recent scroll event buffer used to compute Fling velocity.
|
|
// For most systems this will be 3. However, for systems that use 2x
|
|
// interpolation, this should be 6, to ensure that the scroll events for 3
|
|
// actual hardware states are used.
|
|
IntProperty fling_buffer_depth_;
|
|
// Some platforms report fingers as perfectly stationary for a few frames
|
|
// before they report lift off. We don't include these non-movement
|
|
// frames in the scroll buffer, because that would suppress fling.
|
|
// Platforms with this property should set
|
|
// fling_buffer_suppress_zero_length_scrolls_ to non-zero.
|
|
BoolProperty fling_buffer_suppress_zero_length_scrolls_;
|
|
// When computing a fling, if the fling buffer has an average speed under
|
|
// this threshold, we do not perform a fling. Units are mm/sec.
|
|
DoubleProperty fling_buffer_min_avg_speed_;
|
|
};
|
|
|
|
// Helper class for computing the button type of multi-finger clicks.
|
|
class FingerButtonClick {
|
|
public:
|
|
// Describes the three classes of fingers we deal with while determining
|
|
// the type of physical button clicks.
|
|
enum FingerClickStatus {
|
|
// A 'recent' finger has recently touched down on the touchpad.
|
|
STATUS_RECENT,
|
|
// A 'cold' finger has already been on the touchpad for a while,
|
|
// but has not been moved.
|
|
STATUS_COLD,
|
|
// A 'hot' finger has been moved since it touched down.
|
|
STATUS_HOT
|
|
};
|
|
|
|
explicit FingerButtonClick(const ImmediateInterpreter* interpreter);
|
|
~FingerButtonClick() {};
|
|
|
|
// Processes the HardwareState finger data. Categorizes fingers into one of
|
|
// the FingerClickStatus and sort them according to their original timestamps.
|
|
// Returns true if further analysis is needed. Returns false in trivial cases
|
|
// where one is safe to use the HardwareState button data directly.
|
|
bool Update(const HardwareState& hwstate, stime_t button_down_time);
|
|
|
|
// Returns which button type corresponds to which touch count (e.g. 2f = right
|
|
// click, 3f = middle click).
|
|
int GetButtonTypeForTouchCount(int touch_count) const;
|
|
|
|
// All these following button type evaluation functions are guaranteed to
|
|
// return a button but the caller must ensure that the requirements are met.
|
|
//
|
|
// Evaluates the button type for the 2f case. Needs at least 2 fingers.
|
|
int EvaluateTwoFingerButtonType();
|
|
|
|
// Evaluates the button type for >=3f cases. Needs at least 3 fingers.
|
|
int EvaluateThreeOrMoreFingerButtonType();
|
|
|
|
// Evaluates the button type using finger locations.
|
|
int EvaluateButtonTypeUsingFigureLocation();
|
|
|
|
int num_fingers() const { return num_fingers_; }
|
|
int num_recent() const { return num_recent_; }
|
|
int num_cold() const { return num_cold_; }
|
|
int num_hot() const { return num_hot_; }
|
|
|
|
private:
|
|
// Used to fetch properties and other finger status.
|
|
const ImmediateInterpreter* interpreter_;
|
|
|
|
// Fingers that we are considering for determining the button type.
|
|
FingerState const * fingers_[4];
|
|
|
|
// FingerClickStatus of each finger.
|
|
FingerClickStatus fingers_status_[4];
|
|
|
|
// Number of fingers we are considering.
|
|
int num_fingers_;
|
|
|
|
// Number of fingers of each kind.
|
|
int num_recent_;
|
|
int num_cold_;
|
|
int num_hot_;
|
|
};
|
|
|
|
class ImmediateInterpreter : public Interpreter, public PropertyDelegate {
|
|
FRIEND_TEST(ImmediateInterpreterTest, AmbiguousPalmCoScrollTest);
|
|
FRIEND_TEST(ImmediateInterpreterTest, AvoidAccidentalPinchTest);
|
|
FRIEND_TEST(ImmediateInterpreterTest, ChangeTimeoutTest);
|
|
FRIEND_TEST(ImmediateInterpreterTest, ClickTest);
|
|
FRIEND_TEST(ImmediateInterpreterTest, FlingDepthTest);
|
|
FRIEND_TEST(ImmediateInterpreterTest, GetGesturingFingersTest);
|
|
FRIEND_TEST(ImmediateInterpreterTest, PalmAtEdgeTest);
|
|
FRIEND_TEST(ImmediateInterpreterTest, PalmReevaluateTest);
|
|
FRIEND_TEST(ImmediateInterpreterTest, PalmTest);
|
|
FRIEND_TEST(ImmediateInterpreterTest, PinchTests);
|
|
FRIEND_TEST(ImmediateInterpreterTest, ScrollResetTapTest);
|
|
FRIEND_TEST(ImmediateInterpreterTest, ScrollThenFalseTapTest);
|
|
FRIEND_TEST(ImmediateInterpreterTest, SemiMtActiveAreaTest);
|
|
FRIEND_TEST(ImmediateInterpreterTest, SemiMtNoPinchTest);
|
|
FRIEND_TEST(ImmediateInterpreterTest, StationaryPalmTest);
|
|
FRIEND_TEST(ImmediateInterpreterTest, SwipeTest);
|
|
FRIEND_TEST(ImmediateInterpreterTest, TapRecordTest);
|
|
FRIEND_TEST(ImmediateInterpreterTest, TapToClickEnableTest);
|
|
FRIEND_TEST(ImmediateInterpreterTest, TapToClickKeyboardTest);
|
|
FRIEND_TEST(ImmediateInterpreterTest, TapToClickLowPressureBeginOrEndTest);
|
|
FRIEND_TEST(ImmediateInterpreterTest, TapToClickStateMachineTest);
|
|
FRIEND_TEST(ImmediateInterpreterTest, ThumbRetainReevaluateTest);
|
|
FRIEND_TEST(ImmediateInterpreterTest, ThumbRetainTest);
|
|
FRIEND_TEST(ImmediateInterpreterTest, WarpedFingersTappingTest);
|
|
FRIEND_TEST(ImmediateInterpreterTest, ZeroClickInitializationTest);
|
|
friend class TapRecord;
|
|
friend class FingerButtonClick;
|
|
|
|
public:
|
|
enum TapToClickState {
|
|
kTtcIdle,
|
|
kTtcFirstTapBegan,
|
|
kTtcTapComplete,
|
|
kTtcSubsequentTapBegan,
|
|
kTtcDrag,
|
|
kTtcDragRelease,
|
|
kTtcDragRetouch
|
|
};
|
|
|
|
ImmediateInterpreter(PropRegistry* prop_reg, Tracer* tracer);
|
|
virtual ~ImmediateInterpreter() {}
|
|
|
|
protected:
|
|
virtual void SyncInterpretImpl(HardwareState* hwstate, stime_t* timeout);
|
|
|
|
virtual void HandleTimerImpl(stime_t now, stime_t* timeout);
|
|
|
|
virtual void Initialize(const HardwareProperties* hwprops,
|
|
Metrics* metrics, MetricsProperties* mprops,
|
|
GestureConsumer* consumer);
|
|
|
|
public:
|
|
TapToClickState tap_to_click_state() const { return tap_to_click_state_; }
|
|
|
|
float tap_min_pressure() const { return tap_min_pressure_.val_; }
|
|
|
|
stime_t tap_max_finger_age() const { return tap_max_finger_age_.val_; }
|
|
|
|
stime_t finger_origin_timestamp(short finger_id) const {
|
|
return origin_timestamps_.at(finger_id);
|
|
}
|
|
|
|
private:
|
|
// Reset the member variables corresponding to same-finger state and
|
|
// updates changed_time_ to |now|.
|
|
void ResetSameFingersState(const HardwareState& hwstate);
|
|
|
|
// Reset the member variables which track old timestamps. Called when the
|
|
// clock changes backward.
|
|
void ResetTime();
|
|
|
|
// Sets pointing_.
|
|
void UpdatePointingFingers(const HardwareState& hwstate);
|
|
|
|
// Gets the hardware button type (RIGHT, LEFT) based on the
|
|
// first finger's position.
|
|
int GetButtonTypeFromPosition(const HardwareState& hwstate);
|
|
|
|
// Returns the square of the distance that this contact has travelled since
|
|
// fingers changed (origin=false) or since they touched down (origin=true).
|
|
// If permit_warp is true, we ignore the GESTURES_FINGER_WARP_X/Y flags
|
|
// unless the more strict GESTURES_FINGER_WARP_TELEPORTATION flag is set.
|
|
float DistanceTravelledSq(const FingerState& fs,
|
|
bool origin,
|
|
bool permit_warp = false) const;
|
|
|
|
// Returns the vector that this finger has travelled since
|
|
// fingers changed (origin=false) or since they touched down (origin=true).
|
|
// If permit_warp is true, we ignore the GESTURES_FINGER_WARP_X/Y flags
|
|
// unless the more strict GESTURES_FINGER_WARP_TELEPORTATION flag is set.
|
|
Point FingerTraveledVector(const FingerState& fs,
|
|
bool origin,
|
|
bool permit_warp = false) const;
|
|
|
|
// Returns true if there is a potential for pinch zoom but still it's too
|
|
// early to decide. In this case, there shouldn't be any move or scroll
|
|
// event.
|
|
bool EarlyZoomPotential(const HardwareState& hwstate) const;
|
|
|
|
// Returns true if there are two fingers moving in opposite directions.
|
|
// Moreover, this function makes sure that fingers moving direction hasn't
|
|
// changed recently.
|
|
bool ZoomFingersAreConsistent(const HardwareStateBuffer& state_buffer) const;
|
|
|
|
// Returns true if the given finger is moving sufficiently upwards to be
|
|
// considered the bottom finger of an inward pinch.
|
|
bool InwardPinch(const HardwareStateBuffer& state_buffer,
|
|
const FingerState& fs) const;
|
|
|
|
// Returns Cos(A) where A is the angle between the move vector of two fingers
|
|
float FingersAngle(const FingerState* before1, const FingerState* before2,
|
|
const FingerState* curr1, const FingerState* curr2) const;
|
|
|
|
// Returns true if fingers are not moving in opposite directions.
|
|
bool ScrollAngle(const FingerState& finger1, const FingerState& finger2);
|
|
|
|
// Returns the square of distance between two fingers.
|
|
// Returns -1 if not exactly two fingers are present.
|
|
float TwoFingerDistanceSq(const HardwareState& hwstate) const;
|
|
|
|
// Returns the square of distance between two given fingers.
|
|
// Returns -1 if fingers don't present in the hwstate.
|
|
float TwoSpecificFingerDistanceSq(const HardwareState& hwstate,
|
|
const FingerMap& fingers) const;
|
|
|
|
// Updates thumb_ below.
|
|
void UpdateThumbState(const HardwareState& hwstate);
|
|
|
|
// Returns true iff the keyboard has been recently used.
|
|
bool KeyboardRecentlyUsed(stime_t now) const;
|
|
|
|
// Updates non_gs_fingers based on a new hardware state. Removes missing and
|
|
// newly moving fingers from non_gs_fingers.
|
|
void UpdateNonGsFingers(const HardwareState& hwstate);
|
|
|
|
// Gets the finger or fingers we should consider for gestures.
|
|
// Currently, it fetches the (up to) two fingers closest to the keyboard
|
|
// that are not palms. There is one exception: for t5r2 pads with > 2
|
|
// fingers present, we return all fingers.
|
|
FingerMap GetGesturingFingers(const HardwareState& hwstate) const;
|
|
|
|
// Updates current_gesture_type_ based on passed-in hwstate and
|
|
// considering the passed in fingers as gesturing.
|
|
// Returns the finger(s) that are performing the gesture in
|
|
// active_gs_fingers.
|
|
void UpdateCurrentGestureType(const HardwareState& hwstate,
|
|
const FingerMap& gs_fingers,
|
|
FingerMap* active_gs_fingers);
|
|
|
|
// Checks if gesture_type is one of kGestureTypeScroll, kGestureTypeSwipe, or
|
|
// kGestureTypeFourFingerSwipe
|
|
bool IsScrollOrSwipe(const GestureType gesture_type);
|
|
|
|
// Checks if a scroll or swipe has ended, and replaces current_gesture_type_
|
|
// with the appropriate finger lift gesture.
|
|
void GenerateFingerLiftGesture();
|
|
|
|
// Sorts the fingers referred to in finger_ids (whose details are in hwstate)
|
|
// according to prodimity and places the sorted range into out_sorted_ids.
|
|
// The sort first finds the two closes points and includes them first.
|
|
// Then, it finds the point closest to any included point, repeating until
|
|
// all points are included.
|
|
static void SortFingersByProximity(
|
|
const FingerMap& finger_ids,
|
|
const HardwareState& hwstate,
|
|
vector<short, kMaxGesturingFingers>* out_sorted_ids);
|
|
|
|
// If the finger is likely to be a palm and that its contact size/pressure
|
|
// is diminishing/increasing, we suppress the cursor movement. A real
|
|
// intentional 1f cursor move near the touchpad boundary usually has a
|
|
// stationary finger contact size/pressure.
|
|
bool PalmIsArrivingOrDeparting(const FingerState& finger) const;
|
|
|
|
// Check if a finger is close to any known thumb. Can be used to detect some
|
|
// thumb split cases.
|
|
bool IsTooCloseToThumb(const FingerState& finger) const;
|
|
|
|
// If the fingers are near each other in location and pressure and might
|
|
// to be part of a 2-finger action, returns true. The function can also
|
|
// be used to check whether the gesture is a left or a right button click
|
|
// with the parameter checking_button_type.
|
|
bool TwoFingersGesturing(const FingerState& finger1,
|
|
const FingerState& finger2,
|
|
bool check_button_type) const;
|
|
|
|
// Given that TwoFingersGesturing returns true for 2 fingers,
|
|
// This will further look to see if it's really 2 finger scroll or not.
|
|
// Returns the current state (move or scroll) or kGestureTypeNull if
|
|
// unknown.
|
|
GestureType GetTwoFingerGestureType(const FingerState& finger1,
|
|
const FingerState& finger2);
|
|
|
|
// Check for a pinch gesture and update the state machine for detection.
|
|
// If a pinch was detected it will return true. False otherwise.
|
|
// To reset the state machine call with reset=true
|
|
bool UpdatePinchState(const HardwareState& hwstate, bool reset,
|
|
const FingerMap& gs_fingers);
|
|
|
|
// Returns a gesture assuming that at least one of the fingers performing
|
|
// current_gesture_type has left
|
|
GestureType GetFingerLiftGesture(GestureType current_gesture_type);
|
|
|
|
// Returns the current multi-finger gesture, or kGestureTypeNull if no gesture
|
|
// should be produced. num_fingers can be 3 or 4.
|
|
GestureType GetMultiFingerGestureType(const FingerState* const fingers[],
|
|
const int num_fingers);
|
|
|
|
const char* TapToClickStateName(TapToClickState state);
|
|
|
|
stime_t TimeoutForTtcState(TapToClickState state);
|
|
|
|
void SetTapToClickState(TapToClickState state,
|
|
stime_t now);
|
|
|
|
void UpdateTapGesture(const HardwareState* hwstate,
|
|
const FingerMap& gs_fingers,
|
|
const bool same_fingers,
|
|
stime_t now,
|
|
stime_t* timeout);
|
|
|
|
void UpdateTapState(const HardwareState* hwstate,
|
|
const FingerMap& gs_fingers,
|
|
const bool same_fingers,
|
|
stime_t now,
|
|
unsigned* buttons_down,
|
|
unsigned* buttons_up,
|
|
stime_t* timeout);
|
|
|
|
// Returns true iff the given finger is too close to any other finger to
|
|
// realistically be doing a tap gesture.
|
|
bool FingerTooCloseToTap(const HardwareState& hwstate, const FingerState& fs);
|
|
|
|
// Returns true iff finger is in the bottom, dampened zone of the pad
|
|
bool FingerInDampenedZone(const FingerState& finger) const;
|
|
|
|
// Called when fingers have changed to fill start_positions_
|
|
// and origin_positions_.
|
|
void FillStartPositions(const HardwareState& hwstate);
|
|
|
|
// Fills the origin_* member variables.
|
|
void FillOriginInfo(const HardwareState& hwstate);
|
|
|
|
// Fills moving_ with any moving fingers.
|
|
FingerMap UpdateMovingFingers(const HardwareState& hwstate);
|
|
|
|
// Update started_moving_time_ to now if any gesturing fingers started moving.
|
|
void UpdateStartedMovingTime(stime_t now,
|
|
const FingerMap& gs_fingers,
|
|
const FingerMap& newly_moving_fingers);
|
|
|
|
// Updates the internal button state based on the passed in |hwstate|.
|
|
// Can optionally request a timeout by setting *timeout.
|
|
void UpdateButtons(const HardwareState& hwstate, stime_t* timeout);
|
|
|
|
// Called when the timeout is fired for UpdateButtons.
|
|
void UpdateButtonsTimeout(stime_t now);
|
|
|
|
// By looking at |hwstate| and internal state, determins if a button down
|
|
// at this time would correspond to a left/middle/right click. Returns
|
|
// GESTURES_BUTTON_{LEFT,MIDDLE,RIGHT}.
|
|
int EvaluateButtonType(const HardwareState& hwstate,
|
|
stime_t button_down_time);
|
|
|
|
// Precondition: current_mode_ is set to the mode based on |hwstate|.
|
|
// Computes the resulting gesture, storing it in result_.
|
|
void FillResultGesture(const HardwareState& hwstate,
|
|
const FingerMap& fingers);
|
|
|
|
virtual void IntWasWritten(IntProperty* prop);
|
|
|
|
// Fingers which are prohibited from ever tapping.
|
|
std::set<short> tap_dead_fingers_;
|
|
|
|
// Active gs fingers are the subset of gs_fingers that are actually performing
|
|
// a gesture
|
|
FingerMap prev_active_gs_fingers_;
|
|
|
|
// Fingers that would be considered as possibly gesturing, but others fingers
|
|
// did the gesturing.
|
|
FingerMap non_gs_fingers_;
|
|
|
|
FingerMap prev_gs_fingers_;
|
|
FingerMap prev_tap_gs_fingers_;
|
|
HardwareProperties hw_props_;
|
|
Gesture result_;
|
|
Gesture prev_result_;
|
|
|
|
// Time when a contact arrived. Persists even when fingers change.
|
|
std::map<short, stime_t> origin_timestamps_;
|
|
|
|
// Total distance travelled by a finger since the origin_timestamps_.
|
|
std::map<short, float> distance_walked_;
|
|
|
|
// Button data
|
|
// Which button we are going to send/have sent for the physical btn press
|
|
int button_type_; // left, middle, or right
|
|
|
|
FingerButtonClick finger_button_click_;
|
|
|
|
// If we have sent button down for the currently down button
|
|
bool sent_button_down_;
|
|
|
|
// If we haven't sent a button down by this time, send one
|
|
stime_t button_down_timeout_;
|
|
|
|
// When fingers change, we record the time
|
|
stime_t changed_time_;
|
|
|
|
// When gesturing fingers move after change, we record the time.
|
|
stime_t started_moving_time_;
|
|
// Record which fingers have started moving already.
|
|
std::set<short> moving_;
|
|
|
|
// When different fingers are gesturing, we record the time
|
|
stime_t gs_changed_time_;
|
|
|
|
// When fingers leave, we record the time
|
|
stime_t finger_leave_time_;
|
|
|
|
// When fingers change, we keep track of where they started.
|
|
// Map: Finger ID -> (x, y) coordinate
|
|
std::map<short, Point> start_positions_;
|
|
|
|
// Keep track of finger position from when three fingers began moving in the
|
|
// same direction.
|
|
// Map: Finger ID -> (x, y) coordinate
|
|
std::map<short, Point> three_finger_swipe_start_positions_;
|
|
|
|
// Keep track of finger position from when four fingers began moving in the
|
|
// same direction.
|
|
// Map: Finger ID -> (x, y) coordinate
|
|
std::map<short, Point> four_finger_swipe_start_positions_;
|
|
|
|
// We keep track of where each finger started when they touched.
|
|
// Map: Finger ID -> (x, y) coordinate.
|
|
std::map<short, Point> origin_positions_;
|
|
|
|
// tracking ids of known fingers that are not palms, nor thumbs.
|
|
std::set<short> pointing_;
|
|
// tracking ids of known non-palms. But might be thumbs.
|
|
std::set<short> fingers_;
|
|
// contacts believed to be thumbs, and when they were inserted into the map
|
|
std::map<short, stime_t> thumb_;
|
|
// Timer of the evaluation period for contacts believed to be thumbs.
|
|
std::map<short, stime_t> thumb_eval_timer_;
|
|
|
|
// once a moving finger is determined lock onto this one for cursor movement.
|
|
short moving_finger_id_;
|
|
|
|
// Tap-to-click
|
|
// The current state:
|
|
TapToClickState tap_to_click_state_;
|
|
|
|
// When we entered the state:
|
|
stime_t tap_to_click_state_entered_;
|
|
|
|
TapRecord tap_record_;
|
|
|
|
// Record time when the finger showed motion (uses different motion detection
|
|
// than last_movement_timestamp_)
|
|
stime_t tap_drag_last_motion_time_;
|
|
|
|
// True when the finger was stationary for a while during tap to drag
|
|
bool tap_drag_finger_was_stationary_;
|
|
|
|
// Time when the last motion (scroll, movement) occurred
|
|
stime_t last_movement_timestamp_;
|
|
|
|
bool swipe_is_vertical_;
|
|
|
|
// If we are currently pointing, scrolling, etc.
|
|
GestureType current_gesture_type_;
|
|
// Previous value of current_gesture_type_
|
|
GestureType prev_gesture_type_;
|
|
|
|
// Cache for distance between fingers at previous pinch gesture event, or
|
|
// start of pinch detection
|
|
float pinch_prev_distance_sq_;
|
|
|
|
HardwareStateBuffer state_buffer_;
|
|
ScrollEventBuffer scroll_buffer_;
|
|
|
|
FingerMetrics* finger_metrics_;
|
|
std::unique_ptr<FingerMetrics> test_finger_metrics_;
|
|
|
|
// There are three pinch guess states before locking:
|
|
// pinch_guess_start_ == -1: No definite guess made about pinch
|
|
// pinch_guess_start_ > 0:
|
|
// pinch_guess_ == true: Guess there is a pinch
|
|
// pinch_guess_ == false: Guess there is no pinch
|
|
|
|
// When guessing a pinch gesture. Do we guess pinch (true) or no-pinch?
|
|
bool pinch_guess_;
|
|
// Time when pinch guess was made. -1 if no guess has been made yet.
|
|
stime_t pinch_guess_start_;
|
|
// True when the pinch decision has been locked.
|
|
bool pinch_locked_;
|
|
// Pinch status: GESTURES_ZOOM_START, _UPDATE, or _END
|
|
unsigned pinch_status_;
|
|
// Direction of previous pinch update:
|
|
// 0: No previous update
|
|
// 1: Outward
|
|
// -1: Inward
|
|
int pinch_prev_direction_;
|
|
// Timestamp of previous pinch update
|
|
float pinch_prev_time_;
|
|
|
|
// Keeps track of if there was a finger seen during a physical click
|
|
bool finger_seen_shortly_after_button_down_;
|
|
|
|
bool is_haptic_pad_;
|
|
|
|
// See keyboard_touched_* properties
|
|
stime_t keyboard_touched_;
|
|
|
|
ScrollManager scroll_manager_;
|
|
|
|
// Properties
|
|
|
|
// Is Tap-To-Click enabled
|
|
BoolProperty tap_enable_;
|
|
// Allows Tap-To-Click to be paused
|
|
BoolProperty tap_paused_;
|
|
// General time limit [s] for tap gestures
|
|
DoubleProperty tap_timeout_;
|
|
// General time limit [s] for time between taps.
|
|
DoubleProperty inter_tap_timeout_;
|
|
// Time [s] before a tap gets recognized as a drag.
|
|
DoubleProperty tap_drag_delay_;
|
|
// Time [s] it takes to stop dragging when you let go of the touchpad
|
|
DoubleProperty tap_drag_timeout_;
|
|
// True if tap dragging is enabled. With it disbled we can respond quickly
|
|
// to tap clicks.
|
|
BoolProperty tap_drag_enable_;
|
|
// True if drag lock is enabled
|
|
BoolProperty drag_lock_enable_;
|
|
// Time [s] the finger has to be stationary to be considered dragging
|
|
DoubleProperty tap_drag_stationary_time_;
|
|
// Distance [mm] a finger can move and still register a tap
|
|
DoubleProperty tap_move_dist_;
|
|
// Minimum pressure a finger must have for it to click when tap to click is on
|
|
DoubleProperty tap_min_pressure_;
|
|
// Maximum distance [mm] per frame that a finger can move and still be
|
|
// considered stationary.
|
|
DoubleProperty tap_max_movement_;
|
|
// Maximum finger age for a finger to trigger tap.
|
|
DoubleProperty tap_max_finger_age_;
|
|
// If three finger click should be enabled. This is a temporary flag so that
|
|
// we can deploy this feature behind a file while we work out the bugs.
|
|
BoolProperty three_finger_click_enable_;
|
|
// If zero finger click should be enabled. On some platforms, bending the
|
|
// case may accidentally cause a physical click. This supresses all clicks
|
|
// that do not have at least 1 finger detected on the touchpad.
|
|
BoolProperty zero_finger_click_enable_;
|
|
// If T5R2 should support three-finger click/tap, which can in some situations
|
|
// be unreliable.
|
|
BoolProperty t5r2_three_finger_click_enable_;
|
|
// Distance [mm] a finger must move after fingers change to count as real
|
|
// motion
|
|
DoubleProperty change_move_distance_;
|
|
// Speed [mm/s] a finger must move to lock on to that finger
|
|
DoubleProperty move_lock_speed_;
|
|
// Distance [mm] a finger must move to report that movement
|
|
DoubleProperty move_report_distance_;
|
|
// Time [s] to block movement after number or identify of fingers change
|
|
DoubleProperty change_timeout_;
|
|
// Time [s] to wait before locking on to a gesture
|
|
DoubleProperty evaluation_timeout_;
|
|
// Time [s] to wait before deciding if the pinch zoom is happening.
|
|
DoubleProperty pinch_evaluation_timeout_;
|
|
// Time [s] to wait before decide if a thumb is doing a pinch
|
|
DoubleProperty thumb_pinch_evaluation_timeout_;
|
|
// Minimum movement that a thumb should have to be a gesturing finger.
|
|
DoubleProperty thumb_pinch_min_movement_;
|
|
// If the ratio of gesturing fingers movement to thumb movement is greater
|
|
// than this number, then we can't have pinch with thumb.
|
|
DoubleProperty thumb_pinch_movement_ratio_;
|
|
// Ratio of Distance_sq * Time * Time for two fingers. This measure is used
|
|
// to compare the slow movement of two fingers.
|
|
DoubleProperty thumb_slow_pinch_similarity_ratio_;
|
|
// If a thumb arrives at the same time as the other fingers, the
|
|
// thumb_pinch_evaluation_timeout_ is multiplied by this factor
|
|
DoubleProperty thumb_pinch_delay_factor_;
|
|
// Minimum movement that fingers must have before we consider their
|
|
// relative direction. If the movement is smaller than this number, it
|
|
// will considered as noise.
|
|
DoubleProperty minimum_movement_direction_detection_;
|
|
// A finger in the damp zone must move at least this much as much as
|
|
// the other finger to count toward a gesture. Should be between 0 and 1.
|
|
DoubleProperty damp_scroll_min_movement_factor_;
|
|
// If two fingers have a pressure difference greater than diff thresh and
|
|
// the larger is more than diff factor times the smaller, we assume the
|
|
// larger is a thumb.
|
|
DoubleProperty two_finger_pressure_diff_thresh_;
|
|
DoubleProperty two_finger_pressure_diff_factor_;
|
|
// Click-and-drags are sometimes wrongly classified as right-clicks if the
|
|
// physical-clicking finger arrives at the pad later than or at roughly the
|
|
// same time of the other finger. To distinguish between the two cases, we
|
|
// use the pressure difference and the fingers' relative positions.
|
|
DoubleProperty click_drag_pressure_diff_thresh_;
|
|
DoubleProperty click_drag_pressure_diff_factor_;
|
|
// Mininum slope of the line connecting two fingers that can qualify a click-
|
|
// and-drag gesture.
|
|
DoubleProperty click_drag_min_slope_;
|
|
// If a large contact moves more than this much times the lowest-pressure
|
|
// contact, consider it not to be a thumb.
|
|
DoubleProperty thumb_movement_factor_;
|
|
// If a large contact moves faster than this much times the lowest-pressure
|
|
// contact, consider it not to be a thumb.
|
|
DoubleProperty thumb_speed_factor_;
|
|
// This much time after fingers change, stop allowing contacts classified
|
|
// as thumb to be classified as non-thumb.
|
|
DoubleProperty thumb_eval_timeout_;
|
|
// If thumb is doing an inward pinch, the thresholds for distance and speed
|
|
// that thumb needs to move to be a gesturing finger are multiplied by this
|
|
// factor
|
|
DoubleProperty thumb_pinch_threshold_ratio_;
|
|
// If a finger is recognized as thumb, it has only this much time to change
|
|
// its status and perform a click
|
|
DoubleProperty thumb_click_prevention_timeout_;
|
|
// Consider scroll vs pointing if finger moves at least this distance [mm]
|
|
DoubleProperty two_finger_scroll_distance_thresh_;
|
|
// Consider move if there is no scroll and one finger moves at least this
|
|
// distance [mm]
|
|
DoubleProperty two_finger_move_distance_thresh_;
|
|
// Minimum distance [mm] one of the three fingers must move to perform a
|
|
// swipe gesture.
|
|
DoubleProperty three_finger_swipe_distance_thresh_;
|
|
// Minimum distance [mm] one of the four fingers must move to perform a
|
|
// four finger swipe gesture.
|
|
DoubleProperty four_finger_swipe_distance_thresh_;
|
|
// Minimum ratio between least and most moving finger to perform a
|
|
// three finger swipe gesture.
|
|
DoubleProperty three_finger_swipe_distance_ratio_;
|
|
// Minimum ratio between least and most moving finger to perform a
|
|
// four finger swipe gesture.
|
|
DoubleProperty four_finger_swipe_distance_ratio_;
|
|
// If three-finger swipe should be enabled
|
|
BoolProperty three_finger_swipe_enable_;
|
|
// Height [mm] of the bottom zone
|
|
DoubleProperty bottom_zone_size_;
|
|
// Time [s] to after button down to evaluate number of fingers for a click
|
|
DoubleProperty button_evaluation_timeout_;
|
|
// Time [s] to evaluate number of fingers for a click after a new touch has
|
|
// been registered
|
|
DoubleProperty button_finger_timeout_;
|
|
// Distance [mm] a finger can move to still be considered for a button click
|
|
DoubleProperty button_move_dist_;
|
|
// Distance [mm] a finger can be away from it's expected location to be
|
|
// considered part of the same finger group
|
|
DoubleProperty button_max_dist_from_expected_;
|
|
// Flag to enable the right click on the right side of the hardware button
|
|
BoolProperty button_right_click_zone_enable_;
|
|
// The size of the right click zone on the right side of the hardware button
|
|
DoubleProperty button_right_click_zone_size_;
|
|
// Timeval of time when keyboard was last touched. After the low one is set,
|
|
// the two are converted into an stime_t and stored in keyboard_touched_.
|
|
IntProperty keyboard_touched_timeval_high_; // seconds
|
|
IntProperty keyboard_touched_timeval_low_; // microseconds
|
|
// During this timeout, which is time [s] since the keyboard has been used,
|
|
// we are extra aggressive in palm detection. If this time is > 10s apart
|
|
// from now (either before or after), it's disregarded. We disregard old
|
|
// values b/c they no longer apply. Because of delays in other interpreters
|
|
// (LooaheadInterpreter), it's possible to get "future" keyboard used times.
|
|
// We wouldn't want a single bad future value to stop all tap-to-click, so
|
|
// we sanity check.
|
|
DoubleProperty keyboard_palm_prevent_timeout_;
|
|
// Motion (pointer movement, scroll) must halt for this length of time [s]
|
|
// before a tap can generate a click.
|
|
DoubleProperty motion_tap_prevent_timeout_;
|
|
// A finger must be at least this far from other fingers when it taps [mm].
|
|
DoubleProperty tapping_finger_min_separation_;
|
|
|
|
// Sum of squares of movement [mm] that is considered as noise during pinch
|
|
// detection
|
|
DoubleProperty pinch_noise_level_sq_;
|
|
// Minimal distance [mm] fingers have to move to indicate a pinch gesture.
|
|
DoubleProperty pinch_guess_min_movement_;
|
|
// Minimal distance [mm] a thumb have to move to do a pinch gesture.
|
|
DoubleProperty pinch_thumb_min_movement_;
|
|
// Minimal distance [mm] fingers have to move to lock a pinch gesture.
|
|
DoubleProperty pinch_certain_min_movement_;
|
|
// Minimum Cos(A) that is acceptable for an inward pinch zoom, where A
|
|
// is the angle between the lower finger and a vertical vector directed
|
|
// from top to bottom.
|
|
DoubleProperty inward_pinch_min_angle_;
|
|
// Maximum Cos(A) to perform a pinch zoom, where A is the angle between
|
|
// two fingers.
|
|
DoubleProperty pinch_zoom_max_angle_;
|
|
// Minimum Cos(A) to perform a scroll gesture when pinch is enabled,
|
|
// where A is the angle between two fingers.
|
|
DoubleProperty scroll_min_angle_;
|
|
// Minimum movement ratio between fingers before we call it a consistent move
|
|
// for a pinch.
|
|
DoubleProperty pinch_guess_consistent_mov_ratio_;
|
|
// Minimum number of touch events needed to start a pinch zoom
|
|
IntProperty pinch_zoom_min_events_;
|
|
// If a pinch is determined quickly we use the original landing position to
|
|
// determing original pinch width. But if they landed too long ago we use the
|
|
// pinch width at detection. Inverse of time in seconds.
|
|
DoubleProperty pinch_initial_scale_time_inv_;
|
|
// Resolution of pinch events: minimum change in squared pinch scale required
|
|
// to send a pinch update.
|
|
DoubleProperty pinch_res_;
|
|
// Change in squared pinch scale required to send a pinch update after fingers
|
|
// stay stationary.
|
|
DoubleProperty pinch_stationary_res_;
|
|
// Time fingers should remain motionless before being treated as stationary.
|
|
DoubleProperty pinch_stationary_time_;
|
|
// Change in squared pinch scale required to send a pinch update after fingers
|
|
// change direction.
|
|
DoubleProperty pinch_hysteresis_res_;
|
|
// Temporary flag to turn pinch on/off while we tune it.
|
|
BoolProperty pinch_enable_;
|
|
|
|
// Short start time diff of fingers for a two-finger click that indicates
|
|
// a right click
|
|
DoubleProperty right_click_start_time_diff_;
|
|
// Second finger comes down for a while then button clicks down that indicates
|
|
// a right click
|
|
DoubleProperty right_click_second_finger_age_;
|
|
// Suppress moves with a speed more than this much times the previous speed.
|
|
DoubleProperty quick_acceleration_factor_;
|
|
};
|
|
|
|
bool AnyGesturingFingerLeft(const HardwareState& state,
|
|
const FingerMap& prev_gs_fingers);
|
|
|
|
} // namespace gestures
|
|
|
|
#endif // GESTURES_IMMEDIATE_INTERPRETER_H_
|