aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMarc Palau <sucdepressec@gmail.com>2015-04-22 17:34:42 +0200
committerMarc Palau <sucdepressec@gmail.com>2015-04-22 17:34:42 +0200
commitceb73257272e34a739b6225e1f30e4e377fdab77 (patch)
tree08521b1830316d124739f2fa1553f7b9949295f1 /src
parent7d2318c1677fd6e81b8cc63b040289148ca8adfc (diff)
downloadraylib-ceb73257272e34a739b6225e1f30e4e377fdab77.tar.gz
raylib-ceb73257272e34a739b6225e1f30e4e377fdab77.zip
Added Gestures System for Android and Web
Diffstat (limited to 'src')
-rw-r--r--src/core.c316
-rw-r--r--src/gestures.c636
-rw-r--r--src/raylib.h24
3 files changed, 702 insertions, 274 deletions
diff --git a/src/core.c b/src/core.c
index fbc4838a..7196fbb0 100644
--- a/src/core.c
+++ b/src/core.c
@@ -158,22 +158,6 @@ static struct android_poll_source *source; // Android events polling source
static int ident, events;
static bool windowReady = false; // Used to detect display initialization
-// Gestures detection variables
-static float tapTouchX, tapTouchY;
-static int64_t lastTapTime = 0;
-static float lastTapX = 0, lastTapY = 0;
-static bool touchTap = false;
-static bool doubleTap = false;
-static bool drag = false;
-static int stdVector[MAX_TOUCH_POINTS];
-static int indexPosition = 0;
-const AInputEvent* eventDrag;
-static int32_t touchId;
-const int32_t DOUBLE_TAP_TIMEOUT = 300*1000000;
-const int32_t DOUBLE_TAP_SLOP = 100;
-const int32_t TAP_TIMEOUT = 180*1000000;
-const int32_t TOUCH_SLOP = 8;
-
#elif defined(PLATFORM_RPI)
static EGL_DISPMANX_WINDOW_T nativeWindow; // Native window (graphic device)
@@ -241,11 +225,6 @@ static int exitKey = KEY_ESCAPE; // Default exit key (ESC)
static int lastKeyPressed = -1;
#endif
-#if defined(PLATFORM_ANDROID)
-static float touchX; // Touch position X
-static float touchY; // Touch position Y
-#endif
-
static double currentTime, previousTime; // Used to track timmings
static double updateTime, drawTime; // Time measures for update and draw
static double frameTime; // Time measure for one frame
@@ -276,6 +255,17 @@ extern void UnloadDefaultFont(void); // [Module: text] Unloads defaul
extern void UpdateMusicStream(void); // [Module: audio] Updates buffers for music streaming
+extern Vector2 GetRawPosition(void);
+extern void ResetGestures(void);
+
+#if defined(PLATFORM_ANDROID)
+extern void InitAndroidGestures(struct android_app *app);
+#endif
+
+#if defined(PLATFORM_WEB)
+extern void InitWebGestures(void);
+#endif
+
//----------------------------------------------------------------------------------
// Module specific Functions Declaration
//----------------------------------------------------------------------------------
@@ -313,8 +303,7 @@ static void TakeScreenshot(void);
#endif
#if defined(PLATFORM_ANDROID)
-static int32_t InputCallback(struct android_app *app, AInputEvent *event); // Process Android activity input events
-static void CommandCallback(struct android_app *app, int32_t cmd); // Process Android activity lifecycle commands
+static void AndroidCommandCallback(struct android_app *app, int32_t cmd); // Process Android activity lifecycle commands
#endif
static void ProcessCamera(Camera *camera, Vector3 *playerPosition);
@@ -350,6 +339,11 @@ void InitWindow(int width, int height, const char *title)
InitKeyboard(); // Keyboard init
InitGamepad(); // Gamepad init
#endif
+
+#if defined(PLATFORM_WEB)
+ InitWebGestures(); // Init touch input events for web
+#endif
+
mousePosition.x = screenWidth/2;
mousePosition.y = screenHeight/2;
@@ -401,8 +395,9 @@ void InitWindow(int width, int height, struct android_app *state)
//AConfiguration_getScreenLong(app->config);
//state->userData = &engine;
- app->onAppCmd = CommandCallback;
- app->onInputEvent = InputCallback;
+ app->onAppCmd = AndroidCommandCallback;
+
+ InitAndroidGestures(app);
InitAssetManager(app->activity->assetManager);
@@ -557,6 +552,11 @@ void EndDrawing(void)
if (enabledPostpro) rlglDrawPostpro(); // Draw postprocessing effect (shader)
SwapBuffers(); // Copy back buffer to front buffer
+
+#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB)
+ ResetGestures();
+#endif
+
PollInputEvents(); // Poll user events
UpdateMusicStream(); // NOTE: Function checks if music is enabled
@@ -1053,63 +1053,37 @@ bool IsGamepadButtonUp(int gamepad, int button)
#endif
#if defined(PLATFORM_ANDROID)
-bool IsScreenTouched(void)
-{
- return touchTap;
-}
-
-bool IsDoubleTap(void)
-{
- if (doubleTap) TraceLog(INFO, "DOUBLE TAP gesture detected");
-
- return doubleTap;
-}
-
-bool IsDragGesture(void)
-{
- return drag;
-}
-
// Returns touch position X
int GetTouchX(void)
{
- return (int)touchX;
+ return (int)GetRawPosition().x;
}
// Returns touch position Y
int GetTouchY(void)
{
- return (int)touchY;
+ return (int)GetRawPosition().y;
}
// Returns touch position XY
Vector2 GetTouchPosition(void)
{
- Vector2 position = { touchX, touchY };
+ Vector2 position = GetRawPosition();
+
+ if ((screenWidth > displayWidth) || (screenHeight > displayHeight))
+ {
+ // TODO: Seems to work ok but... review!
+ position.x = position.x*((float)screenWidth / (float)(displayWidth - renderOffsetX)) - renderOffsetX/2;
+ position.y = position.y*((float)screenHeight / (float)(displayHeight - renderOffsetY)) - renderOffsetY/2;
+ }
+ else
+ {
+ position.x = position.x*((float)renderWidth / (float)displayWidth) - renderOffsetX/2;
+ position.y = position.y*((float)renderHeight / (float)displayHeight) - renderOffsetY/2;
+ }
return position;
}
-
-/*bool GetPointer(Vector2 *dragPositions)
-{
- //static int stdVector[MAX_TOUCH_POINTS];
- //static int indexPosition = 0;
- //if (indexPosition == 0) return false;
- Vector2 vec_pointers_[];
-
- //eventDrag
- int32_t iIndex = FindIndex( eventDrag, vec_pointers_[0] );
-
- if (iIndex == -1) return false;
-
- float x = AMotionEvent_getX(eventDrag, iIndex);
- float y = AMotionEvent_getY(eventDrag, iIndex);
-
- *dragPositions = Vector2( x, y );
-
-
- return true;
-}*/
#endif
// Set postprocessing shader
@@ -1499,209 +1473,8 @@ static void WindowIconifyCallback(GLFWwindow* window, int iconified)
#endif
#if defined(PLATFORM_ANDROID)
-// Android: Process activity input events
-static int32_t InputCallback(struct android_app *app, AInputEvent *event)
-{
- int type = AInputEvent_getType(event);
- //int32_t key = 0;
-
- if (type == AINPUT_EVENT_TYPE_MOTION)
- {
- // Detect TOUCH position
- if ((screenWidth > displayWidth) || (screenHeight > displayHeight))
- {
- // TODO: Seems to work ok but... review!
- touchX = AMotionEvent_getX(event, 0) * ((float)screenWidth / (float)(displayWidth - renderOffsetX)) - renderOffsetX/2;
- touchY = AMotionEvent_getY(event, 0) * ((float)screenHeight / (float)(displayHeight - renderOffsetY)) - renderOffsetY/2;
- }
- else
- {
- touchX = AMotionEvent_getX(event, 0) * ((float)renderWidth / (float)displayWidth) - renderOffsetX/2;
- touchY = AMotionEvent_getY(event, 0) * ((float)renderHeight / (float)displayHeight) - renderOffsetY/2;
- }
-
- // Detect TAP event
-/*
- if (AMotionEvent_getPointerCount(event) > 1 )
- {
- // Only support single touch
- return false;
- }
-*/
- int32_t action = AMotionEvent_getAction(event);
- unsigned int flags = action & AMOTION_EVENT_ACTION_MASK;
-
- switch (flags)
- {
- case AMOTION_EVENT_ACTION_DOWN:
- {
- touchId = AMotionEvent_getPointerId(event, 0);
- tapTouchX = AMotionEvent_getX(event, 0);
- tapTouchY = AMotionEvent_getY(event, 0);
-
- } break;
- case AMOTION_EVENT_ACTION_UP:
- {
- int64_t eventTime = AMotionEvent_getEventTime(event);
- int64_t downTime = AMotionEvent_getDownTime(event);
-
- if (eventTime - downTime <= TAP_TIMEOUT)
- {
- if (touchId == AMotionEvent_getPointerId(event, 0))
- {
- float x = AMotionEvent_getX(event, 0) - tapTouchX;
- float y = AMotionEvent_getY(event, 0) - tapTouchY;
-
- float densityFactor = 1.0f;
-
- if ( x*x + y*y < TOUCH_SLOP*TOUCH_SLOP * densityFactor)
- {
- // TAP Detected
- touchTap = true;
- }
- }
- }
- break;
- }
- }
-
- //float AMotionEvent_getX(event, size_t pointer_index);
- //int32_t AMotionEvent_getButtonState(event); // Pressed buttons
- //int32_t AMotionEvent_getPointerId(event, size_t pointer_index);
- //size_t pointerCount = AMotionEvent_getPointerCount(event);
- //float AMotionEvent_getPressure(const AInputEvent *motion_event, size_t pointer_index); // 0 to 1
- //float AMotionEvent_getSize(const AInputEvent *motion_event, size_t pointer_index); // Pressed area
-
- // Detect DOUBLE TAP event
- bool tapDetected = touchTap;
-
- switch (flags)
- {
- case AMOTION_EVENT_ACTION_DOWN:
- {
- int64_t eventTime = AMotionEvent_getEventTime(event);
-
- if (eventTime - lastTapTime <= DOUBLE_TAP_TIMEOUT)
- {
- float x = AMotionEvent_getX(event, 0) - lastTapX;
- float y = AMotionEvent_getY(event, 0) - lastTapY;
-
- float densityFactor = 1.0f;
-
- if ((x*x + y*y) < (DOUBLE_TAP_SLOP*DOUBLE_TAP_SLOP*densityFactor))
- {
- // Doubletap detected
- doubleTap = true;
-
- }
- }
- } break;
- case AMOTION_EVENT_ACTION_UP:
- {
- if (tapDetected)
- {
- lastTapTime = AMotionEvent_getEventTime(event);
- lastTapX = AMotionEvent_getX(event, 0);
- lastTapY = AMotionEvent_getY(event, 0);
-
- }
- } break;
- }
-
-
- // Detect DRAG event
- //int32_t action = AMotionEvent_getAction(event);
-
- int32_t index = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
- //uint32_t flags = action & AMOTION_EVENT_ACTION_MASK;
- //event_ = event;
-
- int32_t count = AMotionEvent_getPointerCount(event);
-
- switch (flags)
- {
- case AMOTION_EVENT_ACTION_DOWN:
- {
- stdVector[indexPosition] = AMotionEvent_getPointerId(event, 0);
- indexPosition++;
- TraceLog(INFO, "ACTION_DOWN");
-
- //ret = GESTURE_STATE_START;
- } break;
- case AMOTION_EVENT_ACTION_POINTER_DOWN:
- {
- stdVector[indexPosition] = AMotionEvent_getPointerId(event, index);
- indexPosition++;
- TraceLog(INFO, "ACTION_POINTER_DOWN");
-
- } break;
- case AMOTION_EVENT_ACTION_UP:
- {
- //int value = stdVector[indexPosition];
- indexPosition--;
- //ret = GESTURE_STATE_END;
- TraceLog(INFO, "ACTION_UP");
-
- } break;
- case AMOTION_EVENT_ACTION_POINTER_UP:
- {
- int32_t releasedPointerId = AMotionEvent_getPointerId(event, index);
-
- int i = 0;
- for (i = 0; i < MAX_TOUCH_POINTS; i++)
- {
- if (stdVector[i] == releasedPointerId)
- {
- for (int k = i; k < indexPosition - 1; k++)
- {
- stdVector[k] = stdVector[k + 1];
- }
-
- //indexPosition--;
- indexPosition = 0;
- break;
- }
- }
-
- if (i <= 1)
- {
- // Reset pinch or drag
- //if (count == 2) //ret = GESTURE_STATE_START;
- }
- TraceLog(INFO, "ACTION_POINTER_UP");
-
- } break;
- case AMOTION_EVENT_ACTION_MOVE:
- {
- if (count == 1)
- {
- //TraceLog(INFO, "DRAG gesture detected");
-
- drag = true; //ret = GESTURE_STATE_MOVE;
- }
- else break;
- TraceLog(INFO, "ACTION_MOVE");
-
- } break;
- case AMOTION_EVENT_ACTION_CANCEL: break;
- default: break;
- }
-
- //--------------------------------------------------------------------
-
- return 1;
- }
- else if (type == AINPUT_EVENT_TYPE_KEY)
- {
- //key = AKeyEvent_getKeyCode(event);
- //int32_t AKeyEvent_getMetaState(event);
- }
-
- return 0;
-}
-
// Android: Process activity lifecycle commands
-static void CommandCallback(struct android_app *app, int32_t cmd)
+static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
{
switch (cmd)
{
@@ -1905,11 +1678,6 @@ static void PollInputEvents(void)
// TODO: Check virtual keyboard (?)
- // Reset touch events
- touchTap = false;
- doubleTap = false;
- drag = false;
-
// Poll Events (registered events)
// TODO: Enable/disable activityMinimized to block activity if minimized
//while ((ident = ALooper_pollAll(activityMinimized ? 0 : -1, NULL, &events,(void**)&source)) >= 0)
@@ -2397,6 +2165,7 @@ static void LogoAnimation(void)
// Process desired camera mode and controls
static void ProcessCamera(Camera *camera, Vector3 *playerPosition)
{
+#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) || defined(PLATFORM_RPI)
// Mouse movement detection
if (cameraMode != CAMERA_FREE)
{
@@ -2647,4 +2416,5 @@ static void ProcessCamera(Camera *camera, Vector3 *playerPosition)
} break;
default: break;
}
+#endif
} \ No newline at end of file
diff --git a/src/gestures.c b/src/gestures.c
new file mode 100644
index 00000000..594c3cd5
--- /dev/null
+++ b/src/gestures.c
@@ -0,0 +1,636 @@
+/**********************************************************************************************
+*
+* raylib.gestures
+*
+* Gestures Detection and Usage Functions Definitions
+*
+* Copyright (c) 2015 Marc Palau and Ramon Santamaria
+*
+* This software is provided "as-is", without any express or implied warranty. In no event
+* will the authors be held liable for any damages arising from the use of this software.
+*
+* Permission is granted to anyone to use this software for any purpose, including commercial
+* applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+* 1. The origin of this software must not be misrepresented; you must not claim that you
+* wrote the original software. If you use this software in a product, an acknowledgment
+* in the product documentation would be appreciated but is not required.
+*
+* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
+* as being the original software.
+*
+* 3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+#include "raylib.h"
+#include "raymath.h"
+#include "utils.h"
+
+#include <stdlib.h> // malloc(), free()
+#include <stdio.h> // printf(), fprintf()
+#include <math.h> // Used for ...
+#include <time.h>
+
+#if defined(PLATFORM_ANDROID)
+ #include <jni.h> // Java native interface
+ #include <android/sensor.h> // Android sensors functions
+ #include <android/window.h> // Defines AWINDOW_FLAG_FULLSCREEN and others
+#endif
+
+#if defined(PLATFORM_WEB)
+ #include <emscripten/emscripten.h>
+ #include <emscripten/emscripten/html5.h>
+#endif
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+#define FORCE_TO_SWIPE 20
+#define TAP_TIMEOUT 300
+
+#define MAX_TOUCH_POINTS 4
+
+typedef enum {
+ TYPE_MOTIONLESS,
+ TYPE_DRAG,
+ TYPE_DUAL_INPUT
+} GestureType;
+
+typedef enum {
+ UP,
+ DOWN,
+ MOVE
+} ActionType;
+
+typedef struct {
+ ActionType action;
+ int pointCount;
+ int pointerId[MAX_TOUCH_POINTS];
+ Vector2 position[MAX_TOUCH_POINTS];
+} GestureEvent;
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+
+// typedef
+static GestureType gestureType = TYPE_MOTIONLESS;
+
+// Gestures detection variables
+static int32_t touchId;
+
+// Event
+static int64_t eventTime = 0;
+
+// Tap
+// Our initial press position on tap
+static Vector2 initialTapPosition = { 0, 0 };
+
+// Double tap
+// If we are double tapping or not
+static bool doubleTapping = false;
+// If we recently made a tap
+static bool untap = false;
+
+// Drag
+// Our initial press position on drag
+static Vector2 initialDragPosition = { 0, 0 };
+// Position that will compare itself with the mouse one
+static Vector2 endDragPosition = { 0, 0 };
+// Position of the last event detection
+static Vector2 lastDragPosition = { 0, 0 };
+// The total drag vector
+static Vector2 dragVector = { 0, 0 };
+// The distance traveled dragging
+static float magnitude = 0;
+// The angle direction of the drag
+static float angle = 0;
+// A magnitude to calculate how fast we did the drag ( pixels per frame )
+static float intensity = 0;
+// Time that have passed while dragging
+static int draggingTimeCounter = 0;
+
+// Pinch
+// First initial pinch position
+static Vector2 firstInitialPinchPosition = { 0, 0 };
+// Second initial pinch position
+static Vector2 secondInitialPinchPosition = { 0, 0 };
+// First end pinch position
+static Vector2 firstEndPinchPosition = { 0, 0 };
+// Second end pinch position
+static Vector2 secondEndPinchPosition = { 0, 0 };
+// Delta Displacement
+static float pinchDelta = 0;
+
+// Detected gesture
+static int currentGesture = GESTURE_NONE;
+
+static float touchX, touchY;
+
+//----------------------------------------------------------------------------------
+// Module specific Functions Declaration
+//----------------------------------------------------------------------------------
+extern void ProcessMotionEvent(GestureEvent event);
+extern void ResetGestures(void);
+extern Vector2 GetRawPosition(void);
+
+static float CalculateAngle(Vector2 initialPosition, Vector2 actualPosition, float magnitude);
+static float OnPinch();
+static void SetDualInput(GestureEvent event);
+static float Distance(Vector2 v1, Vector2 v2);
+static float DotProduct(Vector2 v1, Vector2 v2);
+static int GetCurrentTime();
+
+#if defined(PLATFORM_WEB)
+static EM_BOOL EmscriptenInputCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData);
+#endif
+
+#if defined(PLATFORM_ANDROID)
+static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event);
+#endif
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+
+// Returns tap position XY
+extern Vector2 GetRawPosition(void)
+{
+ Vector2 position = { touchX, touchY };
+
+ return position;
+}
+
+// Check if a gesture have been detected
+bool IsGestureDetected(void)
+{
+ if (currentGesture == GESTURE_DRAG) TraceLog(INFO, "DRAG");
+ else if (currentGesture == GESTURE_TAP) TraceLog(INFO, "TAP");
+ else if (currentGesture == GESTURE_DOUBLETAP) TraceLog(INFO, "DOUBLE");
+ else if (currentGesture == GESTURE_HOLD) TraceLog(INFO, "HOLD");
+ else if (currentGesture == GESTURE_SWIPE_RIGHT) TraceLog(INFO, "RIGHT");
+ else if (currentGesture == GESTURE_SWIPE_UP) TraceLog(INFO, "UP");
+ else if (currentGesture == GESTURE_SWIPE_LEFT) TraceLog(INFO, "LEFT");
+ else if (currentGesture == GESTURE_SWIPE_DOWN) TraceLog(INFO, "DOWN");
+ else if (currentGesture == GESTURE_PINCH_IN) TraceLog(INFO, "PINCH IN");
+ else if (currentGesture == GESTURE_PINCH_OUT) TraceLog(INFO, "PINCH OUT");
+
+ if (currentGesture != GESTURE_NONE) return false;
+ else return true;
+}
+
+// Check gesture type
+int GetGestureType(void)
+{
+ return currentGesture;
+}
+
+// Get drag intensity (pixels per frame)
+float GetDragIntensity(void)
+{
+ return intensity;
+}
+
+// Get drag angle
+// NOTE: Angle in degrees, horizontal-right is 0, counterclock-wise
+float GetDragAngle(void)
+{
+ return angle;
+}
+
+// Get drag vector (between initial and final position)
+Vector2 GetDragVector(void)
+{
+ return dragVector;
+}
+
+// Hold time measured in frames
+int GetHoldDuration(void)
+{
+ return 0;
+}
+
+// Get magnitude between two pinch points
+float GetPinchDelta(void)
+{
+ return pinchDelta;
+}
+
+// Get angle beween two pinch points
+// NOTE: Angle in degrees, horizontal-right is 0, counterclock-wise
+float GetPinchAngle(void)
+{
+ return 0;
+}
+
+extern void ResetGestures(void)
+{
+ if (currentGesture != GESTURE_HOLD) currentGesture = GESTURE_NONE;
+}
+
+#if defined(PLATFORM_WEB)
+extern void InitWebGestures(void)
+{
+ /*
+ emscripten_set_touchstart_callback("#canvas", data, 0, Emscripten_HandleTouch);
+ emscripten_set_touchend_callback("#canvas", data, 0, Emscripten_HandleTouch);
+ emscripten_set_touchmove_callback("#canvas", data, 0, Emscripten_HandleTouch);
+ emscripten_set_touchcancel_callback("#canvas", data, 0, Emscripten_HandleTouch);
+ */
+
+ //emscripten_set_touchstart_callback(0, NULL, 1, Emscripten_HandleTouch);
+
+ emscripten_set_touchstart_callback("#canvas", NULL, 1, EmscriptenInputCallback);
+ emscripten_set_touchend_callback("#canvas", NULL, 1, EmscriptenInputCallback);
+ emscripten_set_touchmove_callback("#canvas", NULL, 1, EmscriptenInputCallback);
+ emscripten_set_touchcancel_callback("#canvas", NULL, 1, EmscriptenInputCallback);
+}
+#endif
+
+#if defined(PLATFORM_ANDROID)
+extern void InitAndroidGestures(struct android_app *app)
+{
+ app->onInputEvent = AndroidInputCallback;
+
+ // TODO: Receive frameBuffer data: displayWidth/displayHeight, renderWidth/renderHeight, screenWidth/screenHeight
+}
+#endif
+
+//----------------------------------------------------------------------------------
+// Module specific Functions Definition
+//----------------------------------------------------------------------------------
+extern void ProcessMotionEvent(GestureEvent event)
+{
+ // Resets
+ dragVector = (Vector2){ 0, 0 };
+ pinchDelta = 0;
+
+ switch (gestureType)
+ {
+ case TYPE_MOTIONLESS: // Detect TAP, DOUBLE_TAP and HOLD events
+ {
+ if (event.action == DOWN)
+ {
+ if (event.pointCount > 1) SetDualInput(event);
+ else
+ {
+ // Set the press position
+ initialTapPosition = event.position[0];
+
+ // If too much time have passed, we reset the double tap
+ if (GetCurrentTime() - eventTime > TAP_TIMEOUT) untap = false;
+
+ // If we are in time, we detect the double tap
+ if (untap) doubleTapping = true;
+
+ // Update our event time
+ eventTime = GetCurrentTime();
+
+ // Set hold
+ currentGesture = GESTURE_HOLD;
+ }
+ }
+ else if (event.action == UP)
+ {
+ // Detect that we are tapping instead of holding
+ if (GetCurrentTime() - eventTime < TAP_TIMEOUT)
+ {
+ if (doubleTapping)
+ {
+ // If we tapped before we define it as double tap
+ currentGesture = GESTURE_DOUBLETAP;
+ untap = false;
+ }
+ else
+ {
+ // Simple tap
+ currentGesture = GESTURE_TAP;
+ untap = true;
+ }
+ }
+ else currentGesture = GESTURE_NONE;
+
+ // Tap finished
+ doubleTapping = false;
+ // Update our event time
+ eventTime = GetCurrentTime();
+ }
+ // Begin dragging
+ else if (event.action == MOVE)
+ {
+ if (event.pointCount > 1) SetDualInput(event);
+ else
+ {
+ // Set the drag starting position
+ initialDragPosition = initialTapPosition;
+ endDragPosition = initialDragPosition;
+
+ // Initialize drag
+ draggingTimeCounter = 0;
+ gestureType = TYPE_DRAG;
+ currentGesture = GESTURE_NONE;
+ }
+ }
+ } break;
+ case TYPE_DRAG: // Detect DRAG and SWIPE events
+ {
+ // end of the drag
+ if (event.action == UP)
+ {
+ // Return Swipe if we have enough sensitivity
+ if (intensity > FORCE_TO_SWIPE)
+ {
+ if (angle < 30 || angle > 330) currentGesture = GESTURE_SWIPE_RIGHT; // Right
+ else if (angle > 60 && angle < 120) currentGesture = GESTURE_SWIPE_UP; // Up
+ else if (angle > 150 && angle < 210) currentGesture = GESTURE_SWIPE_LEFT; // Left
+ else if (angle > 240 && angle < 300) currentGesture = GESTURE_SWIPE_DOWN; // Down
+ }
+
+ magnitude = 0;
+ angle = 0;
+ intensity = 0;
+
+ gestureType = TYPE_MOTIONLESS;
+ }
+ // Update while we are dragging
+ else if (event.action == MOVE)
+ {
+ if (event.pointCount > 1) SetDualInput(event);
+ else
+ {
+ lastDragPosition = endDragPosition;
+
+ endDragPosition = GetRawPosition();
+
+ //endDragPosition.x = AMotionEvent_getX(event, 0);
+ //endDragPosition.y = AMotionEvent_getY(event, 0);
+
+ // Calculate attributes
+ dragVector = (Vector2){ endDragPosition.x - lastDragPosition.x, endDragPosition.y - lastDragPosition.y };
+ magnitude = sqrt(pow(endDragPosition.x - initialDragPosition.x, 2) + pow(endDragPosition.y - initialDragPosition.y, 2));
+ angle = CalculateAngle(initialDragPosition, endDragPosition, magnitude);
+ intensity = magnitude / (float)draggingTimeCounter;
+
+ currentGesture = GESTURE_DRAG;
+ draggingTimeCounter++;
+ }
+ }
+ } break;
+ case TYPE_DUAL_INPUT:
+ {
+ if (event.action == UP)
+ {
+ if (event.pointCount == 1)
+ {
+ // Set the drag starting position
+ initialTapPosition = event.position[0];
+ }
+ gestureType = TYPE_MOTIONLESS;
+ }
+ else if (event.action == MOVE)
+ {
+ // Adapt the ending position of the inputs
+ firstEndPinchPosition = event.position[0];
+ secondEndPinchPosition = event.position[1];
+
+ // If there is no more than two inputs
+ if (event.pointCount == 2)
+ {
+ // Detect pinch delta
+ pinchDelta = OnPinch();
+
+ // Pinch gesture resolution
+ if (pinchDelta != 0)
+ {
+ if (pinchDelta > 0) currentGesture = GESTURE_PINCH_IN;
+ else currentGesture = GESTURE_PINCH_OUT;
+ }
+ }
+ else
+ {
+ // Set the drag starting position
+ initialTapPosition = event.position[0];
+
+ gestureType = TYPE_MOTIONLESS;
+ }
+
+ // Readapt the initial position of the inputs
+ firstInitialPinchPosition = firstEndPinchPosition;
+ secondInitialPinchPosition = secondEndPinchPosition;
+ }
+ } break;
+ }
+ //--------------------------------------------------------------------
+}
+
+
+static float CalculateAngle(Vector2 initialPosition, Vector2 actualPosition, float magnitude)
+{
+ float angle;
+
+ // Calculate arcsinus of the movement ( Our sinus is (actualPosition.y - initialPosition.y) / magnitude)
+ angle = asin((actualPosition.y - initialPosition.y) / magnitude);
+ angle *= RAD2DEG;
+
+ // Calculate angle depending on the sector
+ if (actualPosition.x - initialPosition.x >= 0)
+ {
+ // Sector 4
+ if (actualPosition.y - initialPosition.y >= 0)
+ {
+ angle *= -1;
+ angle += 360;
+ }
+ // Sector 1
+ else
+ {
+ angle *= -1;
+ }
+ }
+ else
+ {
+ // Sector 3
+ if (actualPosition.y - initialPosition.y >= 0)
+ {
+ angle += 180;
+ }
+ // Sector 2
+ else
+ {
+ angle *= -1;
+ angle = 180 - angle;
+ }
+ }
+
+ return angle;
+}
+
+static float OnPinch()
+{
+ // Calculate distances
+ float initialDistance = Distance(firstInitialPinchPosition, secondInitialPinchPosition);
+ float endDistance = Distance(firstEndPinchPosition, secondEndPinchPosition);
+
+ // Calculate Vectors
+ Vector2 firstTouchVector = { firstEndPinchPosition.x - firstInitialPinchPosition.x, firstEndPinchPosition.y - firstInitialPinchPosition.y };
+ Vector2 secondTouchVector = { secondEndPinchPosition.x - secondInitialPinchPosition.x, secondEndPinchPosition.y - secondInitialPinchPosition.y };
+
+ // Detect the pinch gesture
+ // Calculate Distances
+ if (DotProduct(firstTouchVector, secondTouchVector) < -0.5) return initialDistance - endDistance;
+ else return 0;
+}
+
+static void SetDualInput(GestureEvent event)
+{
+ initialDragPosition = (Vector2){ 0, 0 };
+ endDragPosition = (Vector2){ 0, 0 };
+ lastDragPosition = (Vector2){ 0, 0 };
+
+ // Initialize positions
+ firstInitialPinchPosition = event.position[0];
+ secondInitialPinchPosition = event.position[1];
+
+ firstEndPinchPosition = firstInitialPinchPosition;
+ secondEndPinchPosition = secondInitialPinchPosition;
+
+ // Resets
+ magnitude = 0;
+ angle = 0;
+ intensity = 0;
+
+ gestureType = TYPE_DUAL_INPUT;
+}
+
+static float Distance(Vector2 v1, Vector2 v2)
+{
+ float result;
+
+ float dx = v2.x - v1.x;
+ float dy = v2.y - v1.y;
+
+ result = sqrt(dx*dx + dy*dy);
+
+ return result;
+}
+
+static float DotProduct(Vector2 v1, Vector2 v2)
+{
+ float result;
+
+ float v1Module = sqrt(v1.x*v1.x + v1.y*v1.y);
+ float v2Module = sqrt(v2.x*v2.x + v2.y*v2.y);
+
+ Vector2 v1Normalized = { v1.x / v1Module, v1.y / v1Module };
+ Vector2 v2Normalized = { v2.x / v2Module, v2.y / v2Module };
+
+ result = v1Normalized.x*v2Normalized.x + v1Normalized.y*v2Normalized.y;
+
+ return result;
+}
+
+static int GetCurrentTime()
+{
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ uint64_t nowTime = (uint64_t)now.tv_sec*1000000000LLU + (uint64_t)now.tv_nsec; // Time provided in nanoseconds
+
+ return nowTime / 1000000; // Return time in miliseconds
+}
+
+#if defined(PLATFORM_ANDROID)
+// Android: Process activity input events
+static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
+{
+ int type = AInputEvent_getType(event);
+ //int32_t key = 0;
+
+ if (type == AINPUT_EVENT_TYPE_MOTION)
+ {
+ touchX = AMotionEvent_getX(event, 0);
+ touchY = AMotionEvent_getY(event, 0);
+ }
+ else if (type == AINPUT_EVENT_TYPE_KEY)
+ {
+ //key = AKeyEvent_getKeyCode(event);
+ //int32_t AKeyEvent_getMetaState(event);
+ }
+
+ int32_t action = AMotionEvent_getAction(event);
+ unsigned int flags = action & AMOTION_EVENT_ACTION_MASK;
+
+
+ GestureEvent gestureEvent;
+
+ // Action
+ if (flags == AMOTION_EVENT_ACTION_DOWN) gestureEvent.action = DOWN;
+ else if (flags == AMOTION_EVENT_ACTION_UP) gestureEvent.action = UP;
+ else if (flags == AMOTION_EVENT_ACTION_MOVE) gestureEvent.action = MOVE;
+
+ // Points
+ gestureEvent.pointCount = AMotionEvent_getPointerCount(event);
+
+ // Position
+ gestureEvent.position[0] = (Vector2){ AMotionEvent_getX(event, 0), AMotionEvent_getY(event, 0) };
+ gestureEvent.position[1] = (Vector2){ AMotionEvent_getX(event, 1), AMotionEvent_getY(event, 1) };
+
+ ProcessMotionEvent(gestureEvent);
+
+ return 0;
+}
+#endif
+
+#if defined(PLATFORM_WEB)
+static EM_BOOL EmscriptenInputCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)
+{
+ /*
+ for (int i = 0; i < touchEvent->numTouches; i++)
+ {
+ long x, y, id;
+
+ if (!touchEvent->touches[i].isChanged) continue;
+
+ id = touchEvent->touches[i].identifier;
+ x = touchEvent->touches[i].canvasX;
+ y = touchEvent->touches[i].canvasY;
+ }
+
+ printf("%s, numTouches: %d %s%s%s%s\n", emscripten_event_type_to_string(eventType), event->numTouches,
+ event->ctrlKey ? " CTRL" : "", event->shiftKey ? " SHIFT" : "", event->altKey ? " ALT" : "", event->metaKey ? " META" : "");
+
+ for(int i = 0; i < event->numTouches; ++i)
+ {
+ const EmscriptenTouchPoint *t = &event->touches[i];
+
+ printf(" %ld: screen: (%ld,%ld), client: (%ld,%ld), page: (%ld,%ld), isChanged: %d, onTarget: %d, canvas: (%ld, %ld)\n",
+ t->identifier, t->screenX, t->screenY, t->clientX, t->clientY, t->pageX, t->pageY, t->isChanged, t->onTarget, t->canvasX, t->canvasY);
+ }
+ */
+
+ GestureEvent gestureEvent;
+
+ // Action
+ if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) gestureEvent.action = DOWN;
+ else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) gestureEvent.action = UP;
+ else if (eventType == EMSCRIPTEN_EVENT_TOUCHMOVE) gestureEvent.action = MOVE;
+
+ // Points
+ gestureEvent.pointCount = event->numTouches;
+
+ // Position
+ gestureEvent.position[0] = (Vector2){ touchEvent->touches[0].canvasX, touchEvent->touches[0].canvasY };
+ gestureEvent.position[1] = (Vector2){ touchEvent->touches[1].canvasX, touchEvent->touches[1].canvasY };
+
+
+ ProcessMotionEvent(gestureEvent);
+
+ return 1;
+}
+#endif
+
+
+
+
+
+
diff --git a/src/raylib.h b/src/raylib.h
index 603cc47d..962376a9 100644
--- a/src/raylib.h
+++ b/src/raylib.h
@@ -183,6 +183,20 @@
typedef enum { false, true } bool;
#endif
+typedef enum {
+ GESTURE_NONE = 0,
+ GESTURE_TAP,
+ GESTURE_DOUBLETAP,
+ GESTURE_HOLD,
+ GESTURE_DRAG,
+ GESTURE_SWIPE_RIGHT,
+ GESTURE_SWIPE_LEFT,
+ GESTURE_SWIPE_UP,
+ GESTURE_SWIPE_DOWN,
+ GESTURE_PINCH_IN,
+ GESTURE_PINCH_OUT
+} Gestures;
+
// byte type
typedef unsigned char byte;
@@ -433,10 +447,18 @@ bool IsGamepadButtonUp(int gamepad, int button); // Detect if a gamepad b
#endif
#if defined(PLATFORM_ANDROID)
-bool IsScreenTouched(void); // Detect screen touch event
int GetTouchX(void); // Returns touch position X
int GetTouchY(void); // Returns touch position Y
Vector2 GetTouchPosition(void); // Returns touch position XY
+
+bool IsGestureDetected(void);
+int GetGestureType(void);
+float GetDragIntensity(void);
+float GetDragAngle(void);
+Vector2 GetDragVector(void);
+int GetHoldDuration(void); // Hold time in frames
+float GetPinchDelta(void);
+float GetPinchAngle(void);
#endif
//------------------------------------------------------------------------------------