1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_layers_GestureEventListener_h 8 #define mozilla_layers_GestureEventListener_h 9 10 #include "InputData.h" // for MultiTouchInput, etc 11 #include "Units.h" 12 #include "mozilla/EventForwards.h" // for nsEventStatus 13 #include "mozilla/RefPtr.h" // for RefPtr 14 #include "nsISupportsImpl.h" 15 #include "nsTArray.h" // for nsTArray 16 17 namespace mozilla { 18 19 class CancelableRunnable; 20 21 namespace layers { 22 23 class AsyncPanZoomController; 24 25 /** 26 * Platform-non-specific, generalized gesture event listener. This class 27 * intercepts all touches events on their way to AsyncPanZoomController and 28 * determines whether or not they are part of a gesture. 29 * 30 * For example, seeing that two fingers are on the screen means that the user 31 * wants to do a pinch gesture, so we don't forward the touches along to 32 * AsyncPanZoomController since it will think that they are just trying to pan 33 * the screen. Instead, we generate a PinchGestureInput and send that. If the 34 * touch event is not part of a gesture, we just return nsEventStatus_eIgnore 35 * and AsyncPanZoomController is expected to handle it. 36 */ 37 class GestureEventListener final { 38 public: 39 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GestureEventListener) 40 41 explicit GestureEventListener( 42 AsyncPanZoomController* aAsyncPanZoomController); 43 44 // -------------------------------------------------------------------------- 45 // These methods must only be called on the controller/UI thread. 46 // 47 48 /** 49 * General input handler for a touch event. If the touch event is not a part 50 * of a gesture, then we pass it along to AsyncPanZoomController. Otherwise, 51 * it gets consumed here and never forwarded along. 52 */ 53 nsEventStatus HandleInputEvent(const MultiTouchInput& aEvent); 54 55 /** 56 * Returns the identifier of the touch in the last touch event processed by 57 * this GestureEventListener. This should only be called when the last touch 58 * event contained only one touch. 59 */ 60 int32_t GetLastTouchIdentifier() const; 61 62 /** 63 * Function used to disable long tap gestures. 64 * 65 * On slow running tests, drags and touch events can be misinterpreted 66 * as a long tap. This allows tests to disable long tap gesture detection. 67 */ 68 static void SetLongTapEnabled(bool aLongTapEnabled); 69 static bool IsLongTapEnabled(); 70 71 private: 72 // Private destructor, to discourage deletion outside of Release(): 73 ~GestureEventListener(); 74 75 /** 76 * States of GEL finite-state machine. 77 */ 78 enum GestureState { 79 // This is the initial and final state of any gesture. 80 // In this state there's no gesture going on, and we don't think we're 81 // about to enter one. 82 // Allowed next states: GESTURE_FIRST_SINGLE_TOUCH_DOWN, 83 // GESTURE_MULTI_TOUCH_DOWN. 84 GESTURE_NONE, 85 86 // A touch start with a single touch point has just happened. 87 // After having gotten into this state we start timers for MAX_TAP_TIME and 88 // StaticPrefs::ui_click_hold_context_menus_delay(). 89 // Allowed next states: GESTURE_MULTI_TOUCH_DOWN, GESTURE_NONE, 90 // GESTURE_FIRST_SINGLE_TOUCH_UP, 91 // GESTURE_LONG_TOUCH_DOWN, 92 // GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN. 93 GESTURE_FIRST_SINGLE_TOUCH_DOWN, 94 95 // While in GESTURE_FIRST_SINGLE_TOUCH_DOWN state a MAX_TAP_TIME timer got 96 // triggered. Now we'll trigger either a single tap if a user lifts her 97 // finger or a long tap if StaticPrefs::ui_click_hold_context_menus_delay() 98 // happens first. 99 // Allowed next states: GESTURE_MULTI_TOUCH_DOWN, GESTURE_NONE, 100 // GESTURE_LONG_TOUCH_DOWN. 101 GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN, 102 103 // A user put her finger down and lifted it up quickly enough. 104 // After having gotten into this state we clear the timer for MAX_TAP_TIME. 105 // Allowed next states: GESTURE_SECOND_SINGLE_TOUCH_DOWN, GESTURE_NONE, 106 // GESTURE_MULTI_TOUCH_DOWN. 107 GESTURE_FIRST_SINGLE_TOUCH_UP, 108 109 // A user put down her finger again right after a single tap thus the 110 // gesture can't be a single tap, but rather a double tap. But we're 111 // still not sure about that until the user lifts her finger again. 112 // Allowed next states: GESTURE_MULTI_TOUCH_DOWN, GESTURE_ONE_TOUCH_PINCH, 113 // GESTURE_NONE. 114 GESTURE_SECOND_SINGLE_TOUCH_DOWN, 115 116 // A long touch has happened, but the user still keeps her finger down. 117 // We'll trigger a "long tap up" event when the finger is up. 118 // Allowed next states: GESTURE_NONE, GESTURE_MULTI_TOUCH_DOWN. 119 GESTURE_LONG_TOUCH_DOWN, 120 121 // We have detected that two or more fingers are on the screen, but there 122 // hasn't been enough movement yet to make us start actually zooming the 123 // screen. 124 // Allowed next states: GESTURE_PINCH, GESTURE_NONE 125 GESTURE_MULTI_TOUCH_DOWN, 126 127 // There are two or more fingers on the screen, and the user has already 128 // pinched enough for us to start zooming the screen. 129 // Allowed next states: GESTURE_NONE 130 GESTURE_PINCH, 131 132 // The user has double tapped, but not lifted her finger, and moved her 133 // finger more than PINCH_START_THRESHOLD. 134 // Allowed next states: GESTURE_NONE. 135 GESTURE_ONE_TOUCH_PINCH 136 }; 137 138 /** 139 * These HandleInput* functions comprise input alphabet of the GEL 140 * finite-state machine triggering state transitions. 141 */ 142 nsEventStatus HandleInputTouchSingleStart(); 143 nsEventStatus HandleInputTouchMultiStart(); 144 nsEventStatus HandleInputTouchEnd(); 145 nsEventStatus HandleInputTouchMove(); 146 nsEventStatus HandleInputTouchCancel(); 147 void HandleInputTimeoutLongTap(); 148 void HandleInputTimeoutMaxTap(bool aDuringFastFling); 149 150 void EnterFirstSingleTouchDown(); 151 152 void TriggerSingleTapConfirmedEvent(); 153 154 bool MoveDistanceExceeds(ScreenCoord aThreshold) const; 155 bool MoveDistanceIsLarge() const; 156 bool SecondTapIsFar() const; 157 158 /** 159 * Returns current vertical span, counting from the where the gesture first 160 * began (after a brief delay detecting the gesture from first touch). 161 */ 162 ScreenCoord GetYSpanFromGestureStartPoint(); 163 164 /** 165 * Do actual state transition and reset substates. 166 */ 167 void SetState(GestureState aState); 168 169 RefPtr<AsyncPanZoomController> mAsyncPanZoomController; 170 171 /** 172 * Array containing all active touches. When a touch happens it, gets added to 173 * this array, even if we choose not to handle it. When it ends, we remove it. 174 * We need to maintain this array in order to detect the end of the 175 * "multitouch" states because touch start events contain all current touches, 176 * but touch end events contain only those touches that have gone. 177 */ 178 nsTArray<SingleTouchData> mTouches; 179 180 /** 181 * Current state we're dealing with. 182 */ 183 GestureState mState; 184 185 /** 186 * Total change in span since we detected a pinch gesture. Only used when we 187 * are in the |GESTURE_WAITING_PINCH| state and need to know how far zoomed 188 * out we are compared to our original pinch span. Note that this does _not_ 189 * continue to be updated once we jump into the |GESTURE_PINCH| state. 190 */ 191 ScreenCoord mSpanChange; 192 193 /** 194 * Previous span calculated for the purposes of setting inside a 195 * PinchGestureInput. 196 */ 197 ScreenCoord mPreviousSpan; 198 199 /* Properties similar to mSpanChange and mPreviousSpan, but for the focus */ 200 ScreenCoord mFocusChange; 201 ScreenPoint mPreviousFocus; 202 203 /** 204 * Cached copy of the last touch input. 205 */ 206 MultiTouchInput mLastTouchInput; 207 208 /** 209 * Cached copy of the last tap gesture input. 210 * In the situation when we have a tap followed by a pinch we lose info 211 * about tap since we keep only last input and to dispatch it correctly 212 * we save last tap copy into this variable. 213 * For more info see bug 947892. 214 */ 215 MultiTouchInput mLastTapInput; 216 217 /** 218 * Position of the last touch that exceeds the GetTouchStartTolerance when 219 * performing a one-touch-pinch gesture; using the mTouchStartPosition is 220 * slightly inaccurate because by the time the touch position has passed 221 * the threshold for the gesture, there is already a span that the zoom 222 * is calculated from, instead of starting at 1.0 when the threshold gets 223 * passed. 224 */ 225 ScreenPoint mOneTouchPinchStartPosition; 226 227 /** 228 * Position of the last touch starting. This is only valid during an attempt 229 * to determine if a touch is a tap. If a touch point moves away from 230 * mTouchStartPosition to the distance greater than 231 * AsyncPanZoomController::GetTouchStartTolerance() while in 232 * GESTURE_FIRST_SINGLE_TOUCH_DOWN, GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN 233 * or GESTURE_SECOND_SINGLE_TOUCH_DOWN then we're certain the gesture is 234 * not tap. 235 */ 236 ScreenPoint mTouchStartPosition; 237 238 /** 239 * We store the window/GeckoView's display offset as well, so we can 240 * track the user's physical touch movements in absolute display coordinates. 241 */ 242 ExternalPoint mTouchStartOffset; 243 244 /** 245 * Task used to timeout a long tap. This gets posted to the UI thread such 246 * that it runs a time when a single tap happens. We cache it so that 247 * we can cancel it if any other touch event happens. 248 * 249 * The task is supposed to be non-null if in GESTURE_FIRST_SINGLE_TOUCH_DOWN 250 * and GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN states. 251 * 252 * CancelLongTapTimeoutTask: Cancel the mLongTapTimeoutTask and also set 253 * it to null. 254 */ 255 RefPtr<CancelableRunnable> mLongTapTimeoutTask; 256 void CancelLongTapTimeoutTask(); 257 void CreateLongTapTimeoutTask(); 258 259 /** 260 * Task used to timeout a single tap or a double tap. 261 * 262 * The task is supposed to be non-null if in GESTURE_FIRST_SINGLE_TOUCH_DOWN, 263 * GESTURE_FIRST_SINGLE_TOUCH_UP and GESTURE_SECOND_SINGLE_TOUCH_DOWN states. 264 * 265 * CancelMaxTapTimeoutTask: Cancel the mMaxTapTimeoutTask and also set 266 * it to null. 267 */ 268 RefPtr<CancelableRunnable> mMaxTapTimeoutTask; 269 void CancelMaxTapTimeoutTask(); 270 void CreateMaxTapTimeoutTask(); 271 272 /** 273 * Tracks whether the single-tap event was already sent to content. This is 274 * needed because it affects how the double-tap gesture, if detected, is 275 * handled. The value is only valid in states GESTURE_FIRST_SINGLE_TOUCH_UP 276 * and GESTURE_SECOND_SINGLE_TOUCH_DOWN; to more easily catch violations it is 277 * stored in a Maybe which is set to Nothing() at all other times. 278 */ 279 Maybe<bool> mSingleTapSent; 280 }; 281 282 } // namespace layers 283 } // namespace mozilla 284 285 #endif 286