GCC Code Coverage Report


Directory: ./
File: submodules/raylib/src/rgestures.h
Date: 2023-09-29 04:53:15
Exec Total Coverage
Lines: 0 101 0.0%
Branches: 0 76 0.0%

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