// 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 #include #include // 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" #include "include/vector.h" #ifndef GESTURES_IMMEDIATE_INTERPRETER_H_ #define GESTURES_IMMEDIATE_INTERPRETER_H_ namespace gestures { typedef std::set 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& added, const std::set& removed, const std::set& 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 touched_; std::set released_; // At least one finger must meet the minimum pressure requirement during a // tap. This set contains the fingers that have. std::set min_tap_pressure_met_; // All fingers must meet the cotap pressure, which is half of the min tap // pressure. std::set 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, stime_t timestamp, stime_t prev_timestamp); 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 buf_; size_t max_size_; size_t size_; size_t head_; stime_t last_scroll_timestamp_; 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( const_cast(this)->Get(idx)); } private: std::unique_ptr 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 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, GetGesturingFingersWithEmptyStateTest); 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, TapToClickKeyboardTest); FRIEND_TEST(ImmediateInterpreterTest, TapToClickLowPressureBeginOrEndTest); FRIEND_TEST(ImmediateInterpreterTest, ThumbRetainReevaluateTest); FRIEND_TEST(ImmediateInterpreterTest, ThumbRetainTest); FRIEND_TEST(ImmediateInterpreterTest, WarpedFingersTappingTest); FRIEND_TEST(ImmediateInterpreterTest, ZeroClickInitializationTest); FRIEND_TEST(ImmediateInterpreterTtcEnableTest, TapToClickEnableTest); friend class TapRecord; friend class TapToClickStateMachineTest; 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_; } bool device_reports_pressure() const { return hwprops_->reports_pressure; } stime_t finger_origin_timestamp(short tracking_id) const { return metrics_->GetFinger(tracking_id)->origin_time(); } 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* 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 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_; // Total distance travelled by a finger since its origin timestamp. std::map 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_deadline_; // 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 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 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 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 four_finger_swipe_start_positions_; // We keep track of where each finger started when they touched. // Map: Finger ID -> (x, y) coordinate. std::map origin_positions_; // tracking ids of known fingers that are not palms, nor thumbs. std::set pointing_; // tracking ids of known non-palms. But might be thumbs. std::set fingers_; // contacts believed to be thumbs, and when they were inserted into the map std::map thumb_; // Timer of the evaluation period for contacts believed to be thumbs. std::map 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 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_; // Speed [mm/s] a finger must move to lock on to that finger, when another // finger is already locked. DoubleProperty move_change_lock_speed_; // How much faster a finger must move than the currently locked finger to // switch the lock. DoubleProperty move_change_lock_ratio_; // 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_