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