]> git.sesse.net Git - pistorm/blob - raylib_pi4_test/gestures.h
Add Meson build files.
[pistorm] / raylib_pi4_test / gestures.h
1 /**********************************************************************************************
2 *
3 *   raylib.gestures - Gestures system, gestures processing based on input events (touch/mouse)
4 *
5 *   NOTE: Memory footprint of this library is aproximately 128 bytes (global variables)
6 *
7 *   CONFIGURATION:
8 *
9 *   #define GESTURES_IMPLEMENTATION
10 *       Generates the implementation of the library into the included file.
11 *       If not defined, the library is in header only mode and can be included in other headers
12 *       or source files without problems. But only ONE file should hold the implementation.
13 *
14 *   #define GESTURES_STANDALONE
15 *       If defined, the library can be used as standalone to process gesture events with
16 *       no external dependencies.
17 *
18 *   CONTRIBUTORS:
19 *       Marc Palau:         Initial implementation (2014)
20 *       Albert Martos:      Complete redesign and testing (2015)
21 *       Ian Eito:           Complete redesign and testing (2015)
22 *       Ramon Santamaria:   Supervision, review, update and maintenance
23 *
24 *
25 *   LICENSE: zlib/libpng
26 *
27 *   Copyright (c) 2014-2021 Ramon Santamaria (@raysan5)
28 *
29 *   This software is provided "as-is", without any express or implied warranty. In no event
30 *   will the authors be held liable for any damages arising from the use of this software.
31 *
32 *   Permission is granted to anyone to use this software for any purpose, including commercial
33 *   applications, and to alter it and redistribute it freely, subject to the following restrictions:
34 *
35 *     1. The origin of this software must not be misrepresented; you must not claim that you
36 *     wrote the original software. If you use this software in a product, an acknowledgment
37 *     in the product documentation would be appreciated but is not required.
38 *
39 *     2. Altered source versions must be plainly marked as such, and must not be misrepresented
40 *     as being the original software.
41 *
42 *     3. This notice may not be removed or altered from any source distribution.
43 *
44 **********************************************************************************************/
45
46 #ifndef GESTURES_H
47 #define GESTURES_H
48
49 #ifndef PI
50     #define PI 3.14159265358979323846
51 #endif
52
53 //----------------------------------------------------------------------------------
54 // Defines and Macros
55 //----------------------------------------------------------------------------------
56 //...
57
58 //----------------------------------------------------------------------------------
59 // Types and Structures Definition
60 // NOTE: Below types are required for GESTURES_STANDALONE usage
61 //----------------------------------------------------------------------------------
62 #if defined(GESTURES_STANDALONE)
63     #ifndef __cplusplus
64         // Boolean type
65         typedef enum { false, true } bool;
66     #endif
67
68     // Vector2 type
69     typedef struct Vector2 {
70         float x;
71         float y;
72     } Vector2;
73
74     // Gestures type
75     // NOTE: It could be used as flags to enable only some gestures
76     typedef enum {
77         GESTURE_NONE        = 0,
78         GESTURE_TAP         = 1,
79         GESTURE_DOUBLETAP   = 2,
80         GESTURE_HOLD        = 4,
81         GESTURE_DRAG        = 8,
82         GESTURE_SWIPE_RIGHT = 16,
83         GESTURE_SWIPE_LEFT  = 32,
84         GESTURE_SWIPE_UP    = 64,
85         GESTURE_SWIPE_DOWN  = 128,
86         GESTURE_PINCH_IN    = 256,
87         GESTURE_PINCH_OUT   = 512
88     } Gestures;
89 #endif
90
91 typedef enum { TOUCH_UP, TOUCH_DOWN, TOUCH_MOVE } TouchAction;
92
93 // Gesture event
94 // NOTE: MAX_TOUCH_POINTS fixed to 4
95 typedef struct {
96     int touchAction;
97     int pointCount;
98     int pointerId[4];
99     Vector2 position[4];
100 } GestureEvent;
101
102 #ifdef __cplusplus
103 extern "C" {            // Prevents name mangling of functions
104 #endif
105
106 //----------------------------------------------------------------------------------
107 // Global Variables Definition
108 //----------------------------------------------------------------------------------
109 //...
110
111 //----------------------------------------------------------------------------------
112 // Module Functions Declaration
113 //----------------------------------------------------------------------------------
114 void ProcessGestureEvent(GestureEvent event);           // Process gesture event and translate it into gestures
115 void UpdateGestures(void);                              // Update gestures detected (must be called every frame)
116
117 #if defined(GESTURES_STANDALONE)
118 void SetGesturesEnabled(unsigned int flags);     // Enable a set of gestures using flags
119 bool IsGestureDetected(int gesture);                    // Check if a gesture have been detected
120 int GetGestureDetected(void);                           // Get latest detected gesture
121 int GetTouchPointsCount(void);                          // Get touch points count
122 float GetGestureHoldDuration(void);                     // Get gesture hold time in milliseconds
123 Vector2 GetGestureDragVector(void);                     // Get gesture drag vector
124 float GetGestureDragAngle(void);                        // Get gesture drag angle
125 Vector2 GetGesturePinchVector(void);                    // Get gesture pinch delta
126 float GetGesturePinchAngle(void);                       // Get gesture pinch angle
127 #endif
128
129 #ifdef __cplusplus
130 }
131 #endif
132
133 #endif // GESTURES_H
134
135 /***********************************************************************************
136 *
137 *   GESTURES IMPLEMENTATION
138 *
139 ************************************************************************************/
140
141 #if defined(GESTURES_IMPLEMENTATION)
142
143 #if defined(_WIN32)
144     // Functions required to query time on Windows
145     int __stdcall QueryPerformanceCounter(unsigned long long int *lpPerformanceCount);
146     int __stdcall QueryPerformanceFrequency(unsigned long long int *lpFrequency);
147 #elif defined(__linux__)
148     #if _POSIX_C_SOURCE < 199309L
149         #undef _POSIX_C_SOURCE
150         #define _POSIX_C_SOURCE 199309L // Required for CLOCK_MONOTONIC if compiled with c99 without gnu ext.
151     #endif
152     #include <sys/time.h>               // Required for: timespec
153     #include <time.h>                   // Required for: clock_gettime()
154
155     #include <math.h>                   // Required for: sqrtf(), atan2f()
156 #endif
157 #if defined(__APPLE__)                  // macOS also defines __MACH__
158     #include <mach/clock.h>             // Required for: clock_get_time()
159     #include <mach/mach.h>              // Required for: mach_timespec_t
160 #endif
161
162 //----------------------------------------------------------------------------------
163 // Defines and Macros
164 //----------------------------------------------------------------------------------
165 #define FORCE_TO_SWIPE      0.0005f     // Swipe force, measured in normalized screen units/time
166 #define MINIMUM_DRAG        0.015f      // Drag minimum force, measured in normalized screen units (0.0f to 1.0f)
167 #define MINIMUM_PINCH       0.005f      // Pinch minimum force, measured in normalized screen units (0.0f to 1.0f)
168 #define TAP_TIMEOUT         300         // Tap minimum time, measured in milliseconds
169 #define PINCH_TIMEOUT       300         // Pinch minimum time, measured in milliseconds
170 #define DOUBLETAP_RANGE     0.03f       // DoubleTap range, measured in normalized screen units (0.0f to 1.0f)
171
172 //----------------------------------------------------------------------------------
173 // Types and Structures Definition
174 //----------------------------------------------------------------------------------
175
176 // Gestures module state context [136 bytes]
177 typedef struct {
178     unsigned int current;               // Current detected gesture
179     unsigned int enabledFlags;          // Enabled gestures flags
180     struct {
181         int firstId;                    // Touch id for first touch point
182         int pointCount;                 // Touch points counter
183         double eventTime;               // Time stamp when an event happened
184         Vector2 upPosition;             // Touch up position
185         Vector2 downPositionA;          // First touch down position
186         Vector2 downPositionB;          // Second touch down position
187         Vector2 downDragPosition;       // Touch drag position
188         Vector2 moveDownPositionA;      // First touch down position on move
189         Vector2 moveDownPositionB;      // Second touch down position on move
190         int tapCounter;                 // TAP counter (one tap implies TOUCH_DOWN and TOUCH_UP actions)
191     } Touch;
192     struct {
193         bool resetRequired;             // HOLD reset to get first touch point again
194         double timeDuration;            // HOLD duration in milliseconds
195     } Hold;
196     struct {
197         Vector2 vector;                 // DRAG vector (between initial and current position)
198         float angle;                    // DRAG angle (relative to x-axis)
199         float distance;                 // DRAG distance (from initial touch point to final) (normalized [0..1])
200         float intensity;                // DRAG intensity, how far why did the DRAG (pixels per frame)
201     } Drag;
202     struct {
203         bool start;                     // SWIPE used to define when start measuring GESTURES.Swipe.timeDuration
204         double timeDuration;            // SWIPE time to calculate drag intensity
205     } Swipe;
206     struct {
207         Vector2 vector;                 // PINCH vector (between first and second touch points)
208         float angle;                    // PINCH angle (relative to x-axis)
209         float distance;                 // PINCH displacement distance (normalized [0..1])
210     } Pinch;
211 } GesturesData;
212
213 //----------------------------------------------------------------------------------
214 // Global Variables Definition
215 //----------------------------------------------------------------------------------
216 static GesturesData GESTURES = {
217     .Touch.firstId = -1,
218     .current = GESTURE_NONE,            // No current gesture detected
219     .enabledFlags = 0b0000001111111111  // All gestures supported by default
220 };
221
222 //----------------------------------------------------------------------------------
223 // Module specific Functions Declaration
224 //----------------------------------------------------------------------------------
225 #if defined(GESTURES_STANDALONE)
226 // Some required math functions provided by raymath.h
227 static float Vector2Angle(Vector2 initialPosition, Vector2 finalPosition);
228 static float Vector2Distance(Vector2 v1, Vector2 v2);
229 #endif
230 static double GetCurrentTime(void);
231
232 //----------------------------------------------------------------------------------
233 // Module Functions Definition
234 //----------------------------------------------------------------------------------
235
236 // Enable only desired getures to be detected
237 void SetGesturesEnabled(unsigned int flags)
238 {
239     GESTURES.enabledFlags = flags;
240 }
241
242 // Check if a gesture have been detected
243 bool IsGestureDetected(int gesture)
244 {
245     if ((GESTURES.enabledFlags & GESTURES.current) == gesture) return true;
246     else return false;
247 }
248
249 // Process gesture event and translate it into gestures
250 void ProcessGestureEvent(GestureEvent event)
251 {
252     // Reset required variables
253     GESTURES.Touch.pointCount = event.pointCount;      // Required on UpdateGestures()
254
255     if (GESTURES.Touch.pointCount < 2)
256     {
257         if (event.touchAction == TOUCH_DOWN)
258         {
259             GESTURES.Touch.tapCounter++;    // Tap counter
260
261             // Detect GESTURE_DOUBLE_TAP
262             if ((GESTURES.current == GESTURE_NONE) && (GESTURES.Touch.tapCounter >= 2) && ((GetCurrentTime() - GESTURES.Touch.eventTime) < TAP_TIMEOUT) && (Vector2Distance(GESTURES.Touch.downPositionA, event.position[0]) < DOUBLETAP_RANGE))
263             {
264                 GESTURES.current = GESTURE_DOUBLETAP;
265                 GESTURES.Touch.tapCounter = 0;
266             }
267             else    // Detect GESTURE_TAP
268             {
269                 GESTURES.Touch.tapCounter = 1;
270                 GESTURES.current = GESTURE_TAP;
271             }
272
273             GESTURES.Touch.downPositionA = event.position[0];
274             GESTURES.Touch.downDragPosition = event.position[0];
275
276             GESTURES.Touch.upPosition = GESTURES.Touch.downPositionA;
277             GESTURES.Touch.eventTime = GetCurrentTime();
278
279             GESTURES.Touch.firstId = event.pointerId[0];
280
281             GESTURES.Drag.vector = (Vector2){ 0.0f, 0.0f };
282         }
283         else if (event.touchAction == TOUCH_UP)
284         {
285             if (GESTURES.current == GESTURE_DRAG) GESTURES.Touch.upPosition = event.position[0];
286
287             // NOTE: GESTURES.Drag.intensity dependend on the resolution of the screen
288             GESTURES.Drag.distance = Vector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.upPosition);
289             GESTURES.Drag.intensity = GESTURES.Drag.distance/(float)((GetCurrentTime() - GESTURES.Swipe.timeDuration));
290
291             GESTURES.Swipe.start = false;
292
293             // Detect GESTURE_SWIPE
294             if ((GESTURES.Drag.intensity > FORCE_TO_SWIPE) && (GESTURES.Touch.firstId == event.pointerId[0]))
295             {
296                 // NOTE: Angle should be inverted in Y
297                 GESTURES.Drag.angle = 360.0f - Vector2Angle(GESTURES.Touch.downPositionA, GESTURES.Touch.upPosition);
298
299                 if ((GESTURES.Drag.angle < 30) || (GESTURES.Drag.angle > 330)) GESTURES.current = GESTURE_SWIPE_RIGHT;        // Right
300                 else if ((GESTURES.Drag.angle > 30) && (GESTURES.Drag.angle < 120)) GESTURES.current = GESTURE_SWIPE_UP;      // Up
301                 else if ((GESTURES.Drag.angle > 120) && (GESTURES.Drag.angle < 210)) GESTURES.current = GESTURE_SWIPE_LEFT;   // Left
302                 else if ((GESTURES.Drag.angle > 210) && (GESTURES.Drag.angle < 300)) GESTURES.current = GESTURE_SWIPE_DOWN;   // Down
303                 else GESTURES.current = GESTURE_NONE;
304             }
305             else
306             {
307                 GESTURES.Drag.distance = 0.0f;
308                 GESTURES.Drag.intensity = 0.0f;
309                 GESTURES.Drag.angle = 0.0f;
310
311                 GESTURES.current = GESTURE_NONE;
312             }
313
314             GESTURES.Touch.downDragPosition = (Vector2){ 0.0f, 0.0f };
315             GESTURES.Touch.pointCount = 0;
316         }
317         else if (event.touchAction == TOUCH_MOVE)
318         {
319             if (GESTURES.current == GESTURE_DRAG) GESTURES.Touch.eventTime = GetCurrentTime();
320
321             if (!GESTURES.Swipe.start)
322             {
323                 GESTURES.Swipe.timeDuration = GetCurrentTime();
324                 GESTURES.Swipe.start = true;
325             }
326
327             GESTURES.Touch.moveDownPositionA = event.position[0];
328
329             if (GESTURES.current == GESTURE_HOLD)
330             {
331                 if (GESTURES.Hold.resetRequired) GESTURES.Touch.downPositionA = event.position[0];
332
333                 GESTURES.Hold.resetRequired = false;
334
335                 // Detect GESTURE_DRAG
336                 if (Vector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.moveDownPositionA) >= MINIMUM_DRAG)
337                 {
338                     GESTURES.Touch.eventTime = GetCurrentTime();
339                     GESTURES.current = GESTURE_DRAG;
340                 }
341             }
342
343             GESTURES.Drag.vector.x = GESTURES.Touch.moveDownPositionA.x - GESTURES.Touch.downDragPosition.x;
344             GESTURES.Drag.vector.y = GESTURES.Touch.moveDownPositionA.y - GESTURES.Touch.downDragPosition.y;
345         }
346     }
347     else    // Two touch points
348     {
349         if (event.touchAction == TOUCH_DOWN)
350         {
351             GESTURES.Touch.downPositionA = event.position[0];
352             GESTURES.Touch.downPositionB = event.position[1];
353
354             //GESTURES.Pinch.distance = Vector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.downPositionB);
355
356             GESTURES.Pinch.vector.x = GESTURES.Touch.downPositionB.x - GESTURES.Touch.downPositionA.x;
357             GESTURES.Pinch.vector.y = GESTURES.Touch.downPositionB.y - GESTURES.Touch.downPositionA.y;
358
359             GESTURES.current = GESTURE_HOLD;
360             GESTURES.Hold.timeDuration = GetCurrentTime();
361         }
362         else if (event.touchAction == TOUCH_MOVE)
363         {
364             GESTURES.Pinch.distance = Vector2Distance(GESTURES.Touch.moveDownPositionA, GESTURES.Touch.moveDownPositionB);
365
366             GESTURES.Touch.downPositionA = GESTURES.Touch.moveDownPositionA;
367             GESTURES.Touch.downPositionB = GESTURES.Touch.moveDownPositionB;
368
369             GESTURES.Touch.moveDownPositionA = event.position[0];
370             GESTURES.Touch.moveDownPositionB = event.position[1];
371
372             GESTURES.Pinch.vector.x = GESTURES.Touch.moveDownPositionB.x - GESTURES.Touch.moveDownPositionA.x;
373             GESTURES.Pinch.vector.y = GESTURES.Touch.moveDownPositionB.y - GESTURES.Touch.moveDownPositionA.y;
374
375             if ((Vector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.moveDownPositionA) >= MINIMUM_PINCH) || (Vector2Distance(GESTURES.Touch.downPositionB, GESTURES.Touch.moveDownPositionB) >= MINIMUM_PINCH))
376             {
377                 if ((Vector2Distance(GESTURES.Touch.moveDownPositionA, GESTURES.Touch.moveDownPositionB) - GESTURES.Pinch.distance) < 0) GESTURES.current = GESTURE_PINCH_IN;
378                 else GESTURES.current = GESTURE_PINCH_OUT;
379             }
380             else
381             {
382                 GESTURES.current = GESTURE_HOLD;
383                 GESTURES.Hold.timeDuration = GetCurrentTime();
384             }
385
386             // NOTE: Angle should be inverted in Y
387             GESTURES.Pinch.angle = 360.0f - Vector2Angle(GESTURES.Touch.moveDownPositionA, GESTURES.Touch.moveDownPositionB);
388         }
389         else if (event.touchAction == TOUCH_UP)
390         {
391             GESTURES.Pinch.distance = 0.0f;
392             GESTURES.Pinch.angle = 0.0f;
393             GESTURES.Pinch.vector = (Vector2){ 0.0f, 0.0f };
394             GESTURES.Touch.pointCount = 0;
395
396             GESTURES.current = GESTURE_NONE;
397         }
398     }
399 }
400
401 // Update gestures detected (must be called every frame)
402 void UpdateGestures(void)
403 {
404     // NOTE: Gestures are processed through system callbacks on touch events
405
406     // Detect GESTURE_HOLD
407     if (((GESTURES.current == GESTURE_TAP) || (GESTURES.current == GESTURE_DOUBLETAP)) && (GESTURES.Touch.pointCount < 2))
408     {
409         GESTURES.current = GESTURE_HOLD;
410         GESTURES.Hold.timeDuration = GetCurrentTime();
411     }
412
413     if (((GetCurrentTime() - GESTURES.Touch.eventTime) > TAP_TIMEOUT) && (GESTURES.current == GESTURE_DRAG) && (GESTURES.Touch.pointCount < 2))
414     {
415         GESTURES.current = GESTURE_HOLD;
416         GESTURES.Hold.timeDuration = GetCurrentTime();
417         GESTURES.Hold.resetRequired = true;
418     }
419
420     // Detect GESTURE_NONE
421     if ((GESTURES.current == GESTURE_SWIPE_RIGHT) || (GESTURES.current == GESTURE_SWIPE_UP) || (GESTURES.current == GESTURE_SWIPE_LEFT) || (GESTURES.current == GESTURE_SWIPE_DOWN))
422     {
423         GESTURES.current = GESTURE_NONE;
424     }
425 }
426
427 // Get number of touch points
428 int GetTouchPointsCount(void)
429 {
430     // NOTE: point count is calculated when ProcessGestureEvent(GestureEvent event) is called
431
432     return GESTURES.Touch.pointCount;
433 }
434
435 // Get latest detected gesture
436 int GetGestureDetected(void)
437 {
438     // Get current gesture only if enabled
439     return (GESTURES.enabledFlags & GESTURES.current);
440 }
441
442 // Hold time measured in ms
443 float GetGestureHoldDuration(void)
444 {
445     // NOTE: time is calculated on current gesture HOLD
446
447     double time = 0.0;
448
449     if (GESTURES.current == GESTURE_HOLD) time = GetCurrentTime() - GESTURES.Hold.timeDuration;
450
451     return (float)time;
452 }
453
454 // Get drag vector (between initial touch point to current)
455 Vector2 GetGestureDragVector(void)
456 {
457     // NOTE: drag vector is calculated on one touch points TOUCH_MOVE
458
459     return GESTURES.Drag.vector;
460 }
461
462 // Get drag angle
463 // NOTE: Angle in degrees, horizontal-right is 0, counterclock-wise
464 float GetGestureDragAngle(void)
465 {
466     // NOTE: drag angle is calculated on one touch points TOUCH_UP
467
468     return GESTURES.Drag.angle;
469 }
470
471 // Get distance between two pinch points
472 Vector2 GetGesturePinchVector(void)
473 {
474     // NOTE: The position values used for GESTURES.Pinch.distance are not modified like the position values of [core.c]-->GetTouchPosition(int index)
475     // NOTE: pinch distance is calculated on two touch points TOUCH_MOVE
476
477     return GESTURES.Pinch.vector;
478 }
479
480 // Get angle beween two pinch points
481 // NOTE: Angle in degrees, horizontal-right is 0, counterclock-wise
482 float GetGesturePinchAngle(void)
483 {
484     // NOTE: pinch angle is calculated on two touch points TOUCH_MOVE
485
486     return GESTURES.Pinch.angle;
487 }
488
489 //----------------------------------------------------------------------------------
490 // Module specific Functions Definition
491 //----------------------------------------------------------------------------------
492 #if defined(GESTURES_STANDALONE)
493 // Returns angle from two-points vector with X-axis
494 static float Vector2Angle(Vector2 v1, Vector2 v2)
495 {
496     float angle = atan2f(v2.y - v1.y, v2.x - v1.x)*(180.0f/PI);
497
498     if (angle < 0) angle += 360.0f;
499
500     return angle;
501 }
502
503 // Calculate distance between two Vector2
504 static float Vector2Distance(Vector2 v1, Vector2 v2)
505 {
506     float result;
507
508     float dx = v2.x - v1.x;
509     float dy = v2.y - v1.y;
510
511     result = (float)sqrt(dx*dx + dy*dy);
512
513     return result;
514 }
515 #endif
516
517 // Time measure returned are milliseconds
518 static double GetCurrentTime(void)
519 {
520     double time = 0;
521
522 #if defined(_WIN32)
523     unsigned long long int clockFrequency, currentTime;
524
525     QueryPerformanceFrequency(&clockFrequency);     // BE CAREFUL: Costly operation!
526     QueryPerformanceCounter(&currentTime);
527
528     time = (double)currentTime/clockFrequency*1000.0f;  // Time in miliseconds
529 #endif
530
531 #if defined(__linux__)
532     // NOTE: Only for Linux-based systems
533     struct timespec now;
534     clock_gettime(CLOCK_MONOTONIC, &now);
535     unsigned long long int nowTime = (unsigned long long int)now.tv_sec*1000000000LLU + (unsigned long long int)now.tv_nsec;     // Time in nanoseconds
536
537     time = ((double)nowTime/1000000.0);     // Time in miliseconds
538 #endif
539
540 #if defined(__APPLE__)
541     //#define CLOCK_REALTIME  CALENDAR_CLOCK    // returns UTC time since 1970-01-01
542     //#define CLOCK_MONOTONIC SYSTEM_CLOCK      // returns the time since boot time
543
544     clock_serv_t cclock;
545     mach_timespec_t now;
546     host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
547
548     // NOTE: OS X does not have clock_gettime(), using clock_get_time()
549     clock_get_time(cclock, &now);
550     mach_port_deallocate(mach_task_self(), cclock);
551     unsigned long long int nowTime = (unsigned long long int)now.tv_sec*1000000000LLU + (unsigned long long int)now.tv_nsec;     // Time in nanoseconds
552
553     time = ((double)nowTime/1000000.0);     // Time in miliseconds
554 #endif
555
556     return time;
557 }
558
559 #endif // GESTURES_IMPLEMENTATION