| @ -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 | |||||