1 #pragma once 2 3 /** 4 * Touchscreen gesture library, designed for use in Wayfire (and elsewhere). 5 * Goal is to process touch events and detect various configurable gestures. 6 * 7 * High-level design: 8 * A gesture consists of one or more consecutive actions. 9 * 10 * An action is usually a simple part of the gesture which can be processed 11 * separately, for ex. touch down with 3 fingers, swipe in a direction, etc. 12 * 13 * When processing events, the gesture starts with its first action. Once it is 14 * completed, the processing continues with the next action, and so on, until 15 * either all actions are completed or an action cancels the gesture. 16 */ 17 #include <glm/vec2.hpp> 18 #include <vector> 19 #include <map> 20 #include <memory> 21 #include <functional> 22 23 namespace wf 24 { 25 namespace touch 26 { 27 using point_t = glm::dvec2; 28 29 30 /** 31 * Movement direction. 32 */ 33 enum move_direction_t 34 { 35 MOVE_DIRECTION_LEFT = (1 << 0), 36 MOVE_DIRECTION_RIGHT = (1 << 1), 37 MOVE_DIRECTION_UP = (1 << 2), 38 MOVE_DIRECTION_DOWN = (1 << 3), 39 }; 40 41 struct finger_t 42 { 43 point_t origin; 44 point_t current; 45 46 /** Get movement vector */ 47 point_t delta() const; 48 49 /** Find direction of movement, a bitmask of move_direction_t */ 50 uint32_t get_direction() const; 51 52 /** Find drag distance in the given direction */ 53 double get_drag_distance(uint32_t direction) const; 54 55 /** Find drag distance in opposite and perpendicular directions */ 56 double get_incorrect_drag_distance(uint32_t direction) const; 57 }; 58 59 enum gesture_event_type_t 60 { 61 /** Finger touched down the screen */ 62 EVENT_TYPE_TOUCH_DOWN, 63 /** Finger was lifted off the screen */ 64 EVENT_TYPE_TOUCH_UP, 65 /** Finger moved across the screen */ 66 EVENT_TYPE_MOTION, 67 }; 68 69 /** 70 * Represents a single update on the touch state. 71 */ 72 struct gesture_event_t 73 { 74 /** type of the event */ 75 gesture_event_type_t type; 76 /** timestamp of the event in milliseconds */ 77 uint32_t time; 78 /** finger id which the event is about */ 79 int32_t finger; 80 81 /** coordinates of the finger */ 82 point_t pos; 83 }; 84 85 /** 86 * Contains all fingers. 87 */ 88 struct gesture_state_t 89 { 90 public: 91 // finger_id -> finger_t 92 std::map<int, finger_t> fingers; 93 94 /** Update fingers based on the event */ 95 void update(const gesture_event_t& event); 96 97 /** Reset finger origin to current positions */ 98 void reset_origin(); 99 100 /** Find the center points of the fingers. */ 101 finger_t get_center() const; 102 103 /** Get the pinch scale of current touch points. */ 104 double get_pinch_scale() const; 105 106 /** 107 * Get the rotation angle in radians of current touch points. 108 * NB: Works only for rotation < 180 degrees. 109 */ 110 double get_rotation_angle() const; 111 }; 112 113 /** 114 * Represents the status of an action after it is updated 115 */ 116 enum action_status_t 117 { 118 /** Action is done after this event. */ 119 ACTION_STATUS_COMPLETED, 120 /** Action was completed before this event (for example, hold action). */ 121 ACTION_STATUS_ALREADY_COMPLETED, 122 /** Action is still running after this event. */ 123 ACTION_STATUS_RUNNING, 124 /** The whole gesture should be cancelled. */ 125 ACTION_STATUS_CANCELLED, 126 }; 127 128 /** 129 * Represents a part of the gesture. 130 */ 131 class gesture_action_t 132 { 133 public: 134 /** 135 * Set the move tolerance. 136 * This is the maximum amount the fingers may move in unwanted directions. 137 */ 138 void set_move_tolerance(double tolerance); 139 140 /** @return The move tolerance. */ 141 double get_move_tolerance() const; 142 143 /** 144 * Set the duration of the action in milliseconds. 145 * This is the maximal time needed for this action to be happening to 146 * consider it complete. 147 */ 148 void set_duration(uint32_t duration); 149 150 /** @return The duration of the gesture action. */ 151 uint32_t get_duration() const; 152 153 /** 154 * Update the action's state according to the new state. 155 * 156 * NOTE: The actual implementation should update the @start_time field. 157 * 158 * @param state The gesture state since the last reset of the gesture. 159 * @param event The event causing this update. 160 * @return The new action status. 161 */ 162 virtual action_status_t update_state(const gesture_state_t& state, 163 const gesture_event_t& event) = 0; 164 165 /** 166 * Reset the action. 167 * Called whenever the action is started again. 168 */ 169 virtual void reset(uint32_t time); 170 ~gesture_action_t()171 virtual ~gesture_action_t() {} 172 173 protected: gesture_action_t()174 gesture_action_t() {} 175 176 /** Time of the first event. */ 177 int64_t start_time; 178 179 /** 180 * Calculate the correct action status. It is determined as follows: 181 * 1. action has timed out(i.e start_time + duration > timestamp) => CANCELLED 182 * 1. finger movement exceeds move tolerance => CANCELLED 183 * 2. @running is false and gesture has not timed out => COMPLETED 184 * 3. @running is true and gesture has not timed out => RUNNING 185 */ 186 action_status_t calculate_next_status(const gesture_state_t& state, 187 const gesture_event_t& last_event, bool running); 188 189 /** 190 * Calculate whether movement exceeds tolerance. 191 * By default, tolerance is ignored, so actions should override this function. 192 */ 193 virtual bool exceeds_tolerance(const gesture_state_t& state); 194 195 private: 196 double tolerance = 1e18; // very big 197 uint32_t duration = -1; // maximal duration 198 }; 199 200 /** 201 * Represents a target area where the touch event takes place. 202 */ 203 struct touch_target_t 204 { 205 double x; 206 double y; 207 double width; 208 double height; 209 210 bool contains(const point_t& point) const; 211 }; 212 213 /** 214 * Represents the action of touching down with several fingers. 215 */ 216 class touch_action_t : public gesture_action_t 217 { 218 public: 219 /** 220 * Create a new touch down or up action. 221 * 222 * @param cnt_fingers The number of fingers that need to be touched down 223 * or released to consider the action completed. 224 * @param touch_down Whether the action is touch down or touch up. 225 */ 226 touch_action_t(int cnt_fingers, bool touch_down); 227 228 /** 229 * Set the target area of this gesture. 230 */ 231 void set_target(const touch_target_t& target); 232 233 /** 234 * Mark the action as completed iff state has the right amount of fingers 235 * and if the event is a touch down. 236 */ 237 action_status_t update_state(const gesture_state_t& state, 238 const gesture_event_t& event) override; 239 240 void reset(uint32_t time) override; 241 242 protected: 243 /** @return True if the fingers have moved too much. */ 244 bool exceeds_tolerance(const gesture_state_t& state) override; 245 246 private: 247 int cnt_fingers; 248 int released_fingers; 249 gesture_event_type_t type; 250 251 touch_target_t target; 252 }; 253 254 /** 255 * Represents the action of holding the fingers still for a certain amount 256 * of time. 257 */ 258 class hold_action_t : public gesture_action_t 259 { 260 public: 261 /** 262 * Create a new hold action. 263 * 264 * @param threshold The time is milliseconds needed to consider the gesture 265 * complete. 266 */ 267 hold_action_t(int32_t threshold); 268 269 /** 270 * The action is already completed iff no fingers have been added or 271 * released and the given amount of time has passed without much movement. 272 */ 273 action_status_t update_state(const gesture_state_t& state, 274 const gesture_event_t& event) override; 275 276 protected: 277 /** @return True if the fingers have moved too much. */ 278 bool exceeds_tolerance(const gesture_state_t& state) override; 279 280 private: 281 int32_t threshold; 282 }; 283 284 /** 285 * Represents the action of dragging the fingers in a particular direction 286 * over a particular distance. 287 */ 288 class drag_action_t : public gesture_action_t 289 { 290 public: 291 /** 292 * Create a new drag action. 293 * 294 * @param direction The direction of the drag action. 295 * @param threshold The distance that needs to be covered. 296 */ 297 drag_action_t(uint32_t direction, double threshold); 298 299 /** 300 * The action is already completed iff no fingers have been added or 301 * released and the given amount of time has passed without much movement. 302 */ 303 action_status_t update_state(const gesture_state_t& state, 304 const gesture_event_t& event) override; 305 306 protected: 307 /** 308 * @return True if any finger has moved more than the threshold in an 309 * incorrect direction. 310 */ 311 bool exceeds_tolerance(const gesture_state_t& state) override; 312 313 private: 314 double threshold; 315 uint32_t direction; 316 }; 317 318 /** 319 * Represents a pinch action. 320 */ 321 class pinch_action_t : public gesture_action_t 322 { 323 public: 324 /** 325 * Create a new pinch action. 326 * 327 * @param threshold The threshold to be exceeded. 328 * If threshold is less/more than 1, then the action is complete when 329 * the actual pinch scale is respectively less/more than threshold. 330 */ 331 pinch_action_t(double threshold); 332 333 /** 334 * The action is already completed iff no fingers have been added or 335 * released and the pinch threshold has been reached without much movement. 336 */ 337 action_status_t update_state(const gesture_state_t& state, 338 const gesture_event_t& event) override; 339 340 protected: 341 /** 342 * @return True if gesture center has moved more than tolerance. 343 */ 344 bool exceeds_tolerance(const gesture_state_t& state) override; 345 346 private: 347 double threshold; 348 }; 349 350 /** 351 * Represents a rotate action. 352 */ 353 class rotate_action_t : public gesture_action_t 354 { 355 public: 356 /** 357 * Create a new rotate action. 358 * 359 * @param threshold The threshold to be exceeded. 360 * If threshold is less/more than 0, then the action is complete when 361 * the actual rotation angle is respectively less/more than threshold. 362 */ 363 rotate_action_t(double threshold); 364 365 /** 366 * The action is already completed iff no fingers have been added or 367 * released and the rotation threshold has been reached without much movement. 368 */ 369 action_status_t update_state(const gesture_state_t& state, 370 const gesture_event_t& event) override; 371 372 protected: 373 /** 374 * @return True if gesture center has moved more than tolerance. 375 */ 376 bool exceeds_tolerance(const gesture_state_t& state) override; 377 378 private: 379 double threshold; 380 }; 381 382 using gesture_callback_t = std::function<void()>; 383 384 /** 385 * Represents a series of actions forming a gesture together. 386 */ 387 class gesture_t 388 { 389 public: 390 /** 391 * Create a new gesture consisting of the given actions. 392 * 393 * @param actions The actions the gesture consists of. 394 * @param completed The callback to execute each time the gesture is 395 * completed. 396 * @param cancelled The callback to execute each time the gesture is 397 * cancelled. 398 */ 399 gesture_t(std::vector<std::unique_ptr<gesture_action_t>> actions, __anon6a983b800102()400 gesture_callback_t completed, gesture_callback_t cancelled = [](){}); 401 ~gesture_t(); 402 403 /** @return What percentage of the actions are complete. */ 404 double get_progress() const; 405 406 /** 407 * Update the gesture state. 408 * 409 * @param event The next event. 410 */ 411 void update_state(const gesture_event_t& event); 412 413 /** 414 * Reset the gesture state. 415 * 416 * @param time The time of the event causing the start of gesture 417 * recognition, this is typically the first touch event. 418 */ 419 void reset(uint32_t time); 420 421 private: 422 class impl; 423 std::unique_ptr<impl> priv; 424 }; 425 } 426 427 } 428