Browse Source

Merge pull request #1298 from Pixelized/gesture_events

New gestures and gestures support for Android and iOS
Sean Taylor 12 years ago
parent
commit
54124c9c40

+ 12 - 0
gameplay/src/Game.cpp

@@ -508,6 +508,18 @@ void Game::gestureTapEvent(int x, int y)
 {
 }
 
+void Game::gestureLongTapEvent(int x, int y, float duration)
+{
+}
+
+void Game::gestureDragEvent(int x, int y)
+{
+}
+
+void Game::gestureDropEvent(int x, int y)
+{
+}
+
 void Game::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad)
 {
 }

+ 41 - 16
gameplay/src/Game.h

@@ -26,7 +26,7 @@ class ScriptController;
 class Game
 {
     friend class Platform;
-	friend class ShutdownListener;
+    friend class ShutdownListener;
 
 public:
     
@@ -429,6 +429,15 @@ public:
      */
     virtual void gesturePinchEvent(int x, int y, float scale);
 
+    /**
+     * Gesture callback on Gesture::LONG_TAP events.
+     *
+     * @param x The x-coordinate of the long tap.
+     * @param y The y-coordinate of the long tap.
+     * @param duration The duration of the long tap in ms.
+     */
+    virtual void gestureLongTapEvent(int x, int y, float duration);
+
     /**
      * Gesture callback on Gesture::TAP events.
      *
@@ -437,6 +446,22 @@ public:
      */
     virtual void gestureTapEvent(int x, int y);
 
+    /**
+     * Gesture callback on Gesture::DRAG events.
+     *
+     * @param x The x-coordinate of the start of the drag event.
+     * @param y The y-coordinate of the start of the drag event.
+     */
+    virtual void gestureDragEvent(int x, int y);
+
+    /**
+     * Gesture callback on Gesture::DROP events.
+     *
+     * @param x The x-coordinate of the drop event.
+     * @param y The y-coordinate of the drop event.
+     */
+    virtual void gestureDropEvent(int x, int y);
+
     /**
      * Gamepad callback on gamepad events.  Override to receive Gamepad::CONNECTED_EVENT 
      * and Gamepad::DISCONNECTED_EVENT, and store the Gamepad* in order to poll it from update().
@@ -471,18 +496,18 @@ public:
     inline Gamepad* getGamepad(unsigned int index, bool preferPhysical = true) const;
 
     /**
-	 * Sets whether multi-sampling is to be enabled/disabled. Default is disabled.
-	 *
-	 * @param enabled true sets multi-sampling to be enabled, false to be disabled.
-	 */
-	inline void setMultiSampling(bool enabled);
+     * Sets whether multi-sampling is to be enabled/disabled. Default is disabled.
+     *
+     * @param enabled true sets multi-sampling to be enabled, false to be disabled.
+     */
+    inline void setMultiSampling(bool enabled);
 
-	/*
-	 * Is multi-sampling enabled.
-	 *
-	 * @return true if multi-sampling is enabled.
-	 */
-	inline bool isMultiSampling() const;
+    /*
+     * Is multi-sampling enabled.
+     *
+     * @return true if multi-sampling is enabled.
+     */
+    inline bool isMultiSampling() const;
 
     /**
      * Sets multi-touch is to be enabled/disabled. Default is disabled.
@@ -660,10 +685,10 @@ private:
         std::string function;
     };
 
-	struct ShutdownListener : public TimeListener
-	{
-		void timeEvent(long timeDiff, void* cookie);
-	};
+    struct ShutdownListener : public TimeListener
+    {
+        void timeEvent(long timeDiff, void* cookie);
+    };
 
     /**
      * TimeEvent represents the event that is sent to TimeListeners as a result of calling Game::schedule().

+ 3 - 0
gameplay/src/Gesture.h

@@ -19,6 +19,9 @@ public:
         GESTURE_TAP = 0,
         GESTURE_SWIPE,
         GESTURE_PINCH,
+        GESTURE_LONG_TAP,
+        GESTURE_DRAG,
+        GESTURE_DROP,
         GESTURE_ANY_SUPPORTED = -1,
     };
 

+ 21 - 0
gameplay/src/Platform.cpp

@@ -63,6 +63,27 @@ void Platform::gestureTapEventInternal(int x, int y)
     Game::getInstance()->getScriptController()->gestureTapEvent(x, y);
 }
 
+void Platform::gestureLongTapEventInternal(int x, int y, float duration)
+{
+    // TODO: Add support to Form for gestures
+	Game::getInstance()->gestureLongTapEvent(x, y, duration);
+	Game::getInstance()->getScriptController()->gestureLongTapEvent(x, y, duration);
+}
+
+void Platform::gestureDragEventInternal(int x, int y)
+{
+    // TODO: Add support to Form for gestures
+	Game::getInstance()->gestureDragEvent(x, y);
+	Game::getInstance()->getScriptController()->gestureDragEvent(x, y);
+}
+
+void Platform::gestureDropEventInternal(int x, int y)
+{
+    // TODO: Add support to Form for gestures
+	Game::getInstance()->gestureDropEvent(x, y);
+	Game::getInstance()->getScriptController()->gestureDropEvent(x, y);
+}
+
 void Platform::resizeEventInternal(unsigned int width, unsigned int height)
 {
     // Update the width and height of the game

+ 21 - 0
gameplay/src/Platform.h

@@ -336,6 +336,27 @@ public:
      */
     static void gestureTapEventInternal(int x, int y);
 
+    /**
+     * Internal method used only from static code in various platform implementation.
+     *
+     * @script{ignore}
+     */
+	static void gestureLongTapEventInternal(int x, int y, float duration);
+
+    /**
+     * Internal method used only from static code in various platform implementation.
+     *
+     * @script{ignore}
+     */
+	static void gestureDragEventInternal(int x, int y);
+
+    /**
+     * Internal method used only from static code in various platform implementation.
+     *
+     * @script{ignore}
+     */
+	static void gestureDropEventInternal(int x, int y);
+
     /**
      * Internal method used only from static code in various platform implementation.
      *

+ 183 - 34
gameplay/src/PlatformAndroid.cpp

@@ -51,11 +51,22 @@ PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays = NULL;
 PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays = NULL;
 PFNGLISVERTEXARRAYOESPROC glIsVertexArray = NULL;
 
-#define GESTURE_TAP_DURATION_MAX    200
-#define GESTURE_SWIPE_DURATION_MAX  400
-#define GESTURE_SWIPE_DISTANCE_MIN  50
-
-static std::bitset<3> __gestureEventsProcessed;
+#define GESTURE_TAP_DURATION_MAX			200
+#define GESTURE_LONG_TAP_DURATION_MIN   	GESTURE_TAP_DURATION_MAX
+#define GESTURE_DRAG_START_DURATION_MIN		GESTURE_LONG_TAP_DURATION_MIN
+#define GESTURE_DRAG_DISTANCE_MIN    		30
+#define GESTURE_SWIPE_DURATION_MAX      	400
+#define GESTURE_SWIPE_DISTANCE_MIN      	50
+#define GESTURE_PINCH_DISTANCE_MIN    		GESTURE_DRAG_DISTANCE_MIN
+
+static bool __gestureDraging = false;
+static bool __gesturePinching = false;
+static std::pair<int, int> __gesturePointer0LastPosition, __gesturePointer1LastPosition;
+static std::pair<int, int> __gesturePointer0CurrentPosition, __gesturePointer1CurrentPosition;
+static std::pair<int, int> __gesturePinchCentroid;
+static int __gesturePointer0Delta, __gesturePointer1Delta;
+
+static std::bitset<6> __gestureEventsProcessed;
 
 struct TouchPointerData
 {
@@ -72,6 +83,7 @@ TouchPointerData __pointer1;
 namespace gameplay
 {
 
+
 static double timespec2millis(struct timespec *a)
 {
     GP_ASSERT(a);
@@ -232,7 +244,7 @@ static bool initEGL()
 
             if (samples)
             {
-                // Try lowering the MSAA sample size until we find a supported config
+                // Try lowering the MSAA sample size until we find a  config
                 int sampleCount = samples;
                 while (sampleCount)
                 {
@@ -555,6 +567,7 @@ static Keyboard::Key getKey(int keycode, int metastate)
             return Keyboard::KEY_MENU;
         case AKEYCODE_SEARCH:
             return Keyboard::KEY_SEARCH;
+    
         case AKEYCODE_BACK:
             return Keyboard::KEY_ESCAPE;
         default:
@@ -705,13 +718,18 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event)
 
                     // Gesture handling
                     if ( __gestureEventsProcessed.test(Gesture::GESTURE_TAP) ||
-                         __gestureEventsProcessed.test(Gesture::GESTURE_SWIPE) )
+                         __gestureEventsProcessed.test(Gesture::GESTURE_SWIPE) ||
+                         __gestureEventsProcessed.test(Gesture::GESTURE_DRAG) ||
+                         __gestureEventsProcessed.test(Gesture::GESTURE_DROP) ||
+                         __gestureEventsProcessed.test(Gesture::GESTURE_PINCH) ||
+                         __gestureEventsProcessed.test(Gesture::GESTURE_LONG_TAP))
                     {
                         __pointer0.pressed = true;
                         __pointer0.time = Game::getInstance()->getAbsoluteTime();
                         __pointer0.pointerId = pointerId;
                         __pointer0.x = x;
                         __pointer0.y = y;
+						__gesturePointer0CurrentPosition = __gesturePointer0LastPosition = std::pair<int, int>(x, y);
                     }
 
                     // Primary pointer down.
@@ -728,13 +746,28 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event)
                     
                     // Gestures
                     bool gestureDetected = false;
-                    if ( __pointer0.pressed &&  __pointer0.pointerId == pointerId)
+					if (__pointer0.pressed &&  __pointer0.pointerId == pointerId)
                     {
                         int deltaX = x - __pointer0.x;
                         int deltaY = y - __pointer0.y;
-
+						
+                        // Test for drop
+                      	if (__gesturePinching)
+						{
+							__gesturePinching = false;
+							gestureDetected = true;
+						}
+						else if (__gestureDraging)
+                        {
+                            if (__gestureEventsProcessed.test(Gesture::GESTURE_DROP))
+                            {
+                                gameplay::Platform::gestureDropEventInternal(x, y);
+                                gestureDetected = true;
+                            }
+                            __gestureDraging = false;
+                        }
                         // Test for swipe
-                        if (__gestureEventsProcessed.test(Gesture::GESTURE_SWIPE) &&
+                        else if (__gestureEventsProcessed.test(Gesture::GESTURE_SWIPE) &&
                             gameplay::Game::getInstance()->getAbsoluteTime() - __pointer0.time < GESTURE_SWIPE_DURATION_MAX && 
                             (abs(deltaX) > GESTURE_SWIPE_DISTANCE_MIN || abs(deltaY) > GESTURE_SWIPE_DISTANCE_MIN) )
                         {
@@ -754,17 +787,24 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event)
                                     direction = gameplay::Gesture::SWIPE_DIRECTION_UP;
                             }
                             gameplay::Platform::gestureSwipeEventInternal(x, y, direction);
-                            __pointer0.pressed = false;
                             gestureDetected = true;
                         }
+                        // Test for tap
                         else if(__gestureEventsProcessed.test(Gesture::GESTURE_TAP) &&
                                gameplay::Game::getInstance()->getAbsoluteTime() - __pointer0.time < GESTURE_TAP_DURATION_MAX)
                         {
                             gameplay::Platform::gestureTapEventInternal(x, y);
-                            __pointer0.pressed = false;
                             gestureDetected = true;
                         }
+                        // Test for long tap
+                        else if(__gestureEventsProcessed.test(Gesture::GESTURE_LONG_TAP) &&
+                               gameplay::Game::getInstance()->getAbsoluteTime() - __pointer0.time >= GESTURE_LONG_TAP_DURATION_MIN)
+                        {
+                            gameplay::Platform::gestureLongTapEventInternal(x, y, gameplay::Game::getInstance()->getAbsoluteTime() - __pointer0.time);
+                            gestureDetected = true;
+                        }    
                     }
+					__pointer0.pressed = false;
 
                     if (!gestureDetected && (__multiTouch || __primaryTouchId == pointerId) )
                     {
@@ -776,26 +816,30 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event)
 
             case AMOTION_EVENT_ACTION_POINTER_DOWN:
                 {
-                    pointerId = AMotionEvent_getPointerId(event, 0);
-                    x = AMotionEvent_getX(event, 0);
-                    y = AMotionEvent_getY(event, 0);
+                    pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+                    pointerId = AMotionEvent_getPointerId(event, pointerIndex);
+                    x = AMotionEvent_getX(event, pointerIndex);
+                    y = AMotionEvent_getY(event, pointerIndex);
 
                     // Gesture handling
                     if ( __gestureEventsProcessed.test(Gesture::GESTURE_TAP) ||
-                         __gestureEventsProcessed.test(Gesture::GESTURE_SWIPE) )
+                         __gestureEventsProcessed.test(Gesture::GESTURE_SWIPE) ||
+                         __gestureEventsProcessed.test(Gesture::GESTURE_DRAG) ||
+                         __gestureEventsProcessed.test(Gesture::GESTURE_DROP) ||
+                         __gestureEventsProcessed.test(Gesture::GESTURE_PINCH) ||
+                         __gestureEventsProcessed.test(Gesture::GESTURE_LONG_TAP))
                     {
                         __pointer1.pressed = true;
                         __pointer1.time = Game::getInstance()->getAbsoluteTime();
                         __pointer1.pointerId = pointerId;
                         __pointer1.x = x;
                         __pointer1.y = y;
-                    }
+                    	__gesturePointer1CurrentPosition = __gesturePointer1LastPosition = std::pair<int, int>(x, y);
+					}
 
                     // Non-primary pointer down.
                     if (__multiTouch)
                     {
-                        pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
-                        pointerId = AMotionEvent_getPointerId(event, pointerIndex);
                         gameplay::Platform::touchEventInternal(Touch::TOUCH_PRESS, AMotionEvent_getX(event, pointerIndex), AMotionEvent_getY(event, pointerIndex), pointerId);
                     }
                 }
@@ -805,17 +849,22 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event)
                 {
                     pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
                     pointerId = AMotionEvent_getPointerId(event, pointerIndex);
-                    x = AMotionEvent_getX(event, 0);
-                    y = AMotionEvent_getY(event, 0);
+                    x = AMotionEvent_getX(event, pointerIndex);
+                    y = AMotionEvent_getY(event, pointerIndex);
 
                     bool gestureDetected = false;
-                    if ( __pointer1.pressed &&  __pointer1.pointerId == pointerId)
+                    if (__pointer1.pressed &&  __pointer1.pointerId == pointerId)
                     {
                         int deltaX = x - __pointer1.x;
                         int deltaY = y - __pointer1.y;
-
-                        // Test for swipe
-                        if (__gestureEventsProcessed.test(Gesture::GESTURE_SWIPE) &&
+						
+						if (__gesturePinching)
+						{
+							__gesturePinching = false;
+							gestureDetected = true;
+						}
+						// Test for swipe
+						else if (__gestureEventsProcessed.test(Gesture::GESTURE_SWIPE) &&
                             gameplay::Game::getInstance()->getAbsoluteTime() - __pointer1.time < GESTURE_SWIPE_DURATION_MAX && 
                             (abs(deltaX) > GESTURE_SWIPE_DISTANCE_MIN || abs(deltaY) > GESTURE_SWIPE_DISTANCE_MIN) )
                         {
@@ -831,17 +880,22 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event)
                                 direction |= gameplay::Gesture::SWIPE_DIRECTION_UP;
 
                             gameplay::Platform::gestureSwipeEventInternal(x, y, direction);
-                            __pointer1.pressed = false;
                             gestureDetected = true;
                         }
                         else if(__gestureEventsProcessed.test(Gesture::GESTURE_TAP) &&
                                gameplay::Game::getInstance()->getAbsoluteTime() - __pointer1.time < GESTURE_TAP_DURATION_MAX)
                         {
                             gameplay::Platform::gestureTapEventInternal(x, y);
-                            __pointer1.pressed = false;
                             gestureDetected = true;
                         }
+                        else if(__gestureEventsProcessed.test(Gesture::GESTURE_LONG_TAP) &&
+                               gameplay::Game::getInstance()->getAbsoluteTime() - __pointer1.time >= GESTURE_LONG_TAP_DURATION_MIN)
+                        {
+                            gameplay::Platform::gestureLongTapEventInternal(x, y, gameplay::Game::getInstance()->getAbsoluteTime() - __pointer1.time);
+                            gestureDetected = true;
+                        }    
                     }
+					__pointer1.pressed = false;
 
                     if (!gestureDetected && (__multiTouch || __primaryTouchId == pointerId) )
                     {
@@ -859,7 +913,94 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event)
                     for (size_t i = 0; i < pointerCount; ++i)
                     {
                         pointerId = AMotionEvent_getPointerId(event, i);
-                        if (__multiTouch || __primaryTouchId == pointerId)
+                        x = AMotionEvent_getX(event, i);
+                        y = AMotionEvent_getY(event, i);
+                        
+                        bool gestureDetected = false;
+						if (__pointer0.pressed)
+						{
+							//The two pointers are pressed and the event was done by one of it
+							if (__pointer1.pressed && (pointerId == __pointer0.pointerId || pointerId == __pointer1.pointerId))
+							{
+								if (__pointer0.pointerId == __pointer1.pointerId)
+								{
+									__gesturePinching = false;
+									break;
+								}
+								//Test for pinch
+								if (__gestureEventsProcessed.test(Gesture::GESTURE_PINCH))
+								{
+									int pointer0Distance, pointer1Distance;
+
+									if (__pointer0.pointerId == pointerId)
+									{
+										__gesturePointer0LastPosition = __gesturePointer0CurrentPosition;
+										__gesturePointer0CurrentPosition = std::pair<int, int>(x, y);
+										__gesturePointer0Delta = sqrt(pow(static_cast<float>(x - __pointer0.x), 2) +
+																	pow(static_cast<float>(y - __pointer0.y), 2));
+									}
+									else
+									{
+										__gesturePointer1LastPosition = __gesturePointer1CurrentPosition;
+										__gesturePointer1CurrentPosition = std::pair<int, int>(x, y);
+										__gesturePointer1Delta = sqrt(pow(static_cast<float>(x - __pointer1.x), 2) +
+																	pow(static_cast<float>(y - __pointer1.y), 2));
+									}
+									if (!__gesturePinching &&
+										__gesturePointer0Delta >= GESTURE_PINCH_DISTANCE_MIN &&
+										__gesturePointer1Delta >= GESTURE_PINCH_DISTANCE_MIN)
+									{
+										__gesturePinching = true;
+										__gesturePinchCentroid = std::pair<int, int>((__pointer0.x + __pointer1.x) / 2,
+																					(__pointer0.y + __pointer1.y) / 2);
+									}
+									if (__gesturePinching)
+									{
+										int currentDistancePointer0, currentDistancePointer1;
+										int lastDistancePointer0, lastDistancePointer1;
+										float scale;
+
+										currentDistancePointer0 = sqrt(pow(static_cast<float>(__gesturePinchCentroid.first - __gesturePointer0CurrentPosition.first), 2) + pow(static_cast<float>(__gesturePinchCentroid.second - __gesturePointer0CurrentPosition.second), 2));
+										lastDistancePointer0 = sqrt(pow(static_cast<float>(__gesturePinchCentroid.first - __gesturePointer0LastPosition.first), 2) + pow(static_cast<float>(__gesturePinchCentroid.second - __gesturePointer0LastPosition.second), 2));
+										currentDistancePointer1 = sqrt(pow(static_cast<float>(__gesturePinchCentroid.first - __gesturePointer1CurrentPosition.first), 2) + pow(static_cast<float>(__gesturePinchCentroid.second - __gesturePointer1CurrentPosition.second), 2));
+										lastDistancePointer1 = sqrt(pow(static_cast<float>(__gesturePinchCentroid.first - __gesturePointer1LastPosition.first), 2) + pow(static_cast<float>(__gesturePinchCentroid.second - __gesturePointer1LastPosition.second), 2));
+										if (pointerId == __pointer0.pointerId)
+											scale = ((float) currentDistancePointer0) / ((float) lastDistancePointer0);
+										else
+											scale = ((float) currentDistancePointer1) / ((float) lastDistancePointer1);
+										if (currentDistancePointer0 >= lastDistancePointer0 && currentDistancePointer1 >= lastDistancePointer1 ||
+											currentDistancePointer0 <= lastDistancePointer0 && currentDistancePointer1 <= lastDistancePointer1)
+										{
+											gameplay::Platform::gesturePinchEventInternal(__gesturePinchCentroid.first, __gesturePinchCentroid.second, scale);	
+											gestureDetected = true;
+										}
+										else
+											__gesturePinching = false;
+									}
+								}
+							}
+							//Only the primary pointer is done and the event was done by it
+							else if (!gestureDetected && pointerId == __pointer0.pointerId)
+							{
+								//Test for drag
+								if (__gestureEventsProcessed.test(Gesture::GESTURE_DRAG))
+								{
+                            		int delta = sqrt(pow(static_cast<float>(x - __pointer0.x), 2) +
+													pow(static_cast<float>(y - __pointer0.y), 2));
+                            
+                            		if (__gestureDraging || __gestureEventsProcessed.test(Gesture::GESTURE_DRAG) && 
+                                 		gameplay::Game::getInstance()->getAbsoluteTime() - __pointer0.time >= GESTURE_DRAG_START_DURATION_MIN &&
+                                		delta >= GESTURE_DRAG_DISTANCE_MIN)
+                            		{
+                                		gameplay::Platform::gestureDragEventInternal(x, y);
+                                		__gestureDraging = true;
+                                		gestureDetected = true;
+                            		}
+								}
+							}
+						}
+
+                        if (!gestureDetected && (__multiTouch || __primaryTouchId == pointerId))
                         {
                             gameplay::Platform::touchEventInternal(Touch::TOUCH_MOVE, AMotionEvent_getX(event, i), AMotionEvent_getY(event, i), pointerId);
                         }
@@ -1300,29 +1441,29 @@ void Platform::getArguments(int* argc, char*** argv)
 
 bool Platform::hasMouse()
 {
-    // not supported
+    // not 
     return false;
 }
 
 void Platform::setMouseCaptured(bool captured)
 {
-    // not supported
+    // not 
 }
 
 bool Platform::isMouseCaptured()
 {
-    // not supported
+    // not 
     return false;
 }
 
 void Platform::setCursorVisible(bool visible)
 {
-    // not supported
+    // not 
 }
 
 bool Platform::isCursorVisible()
 {
-    // not supported
+    // not 
     return false;
 }
 
@@ -1342,7 +1483,8 @@ void Platform::shutdownInternal()
 bool Platform::isGestureSupported(Gesture::GestureEvent evt)
 {
     // Pinch currently not implemented
-    return evt == gameplay::Gesture::GESTURE_SWIPE || evt == gameplay::Gesture::GESTURE_TAP;
+    return evt == gameplay::Gesture::GESTURE_SWIPE || evt == gameplay::Gesture::GESTURE_TAP || evt == gameplay::Gesture::GESTURE_LONG_TAP ||
+        evt == gameplay::Gesture::GESTURE_DRAG || evt == gameplay::Gesture::GESTURE_DROP || evt == gameplay::Gesture::GESTURE_PINCH;
 }
 
 void Platform::registerGesture(Gesture::GestureEvent evt)
@@ -1355,6 +1497,10 @@ void Platform::registerGesture(Gesture::GestureEvent evt)
 
     case Gesture::GESTURE_TAP:
     case Gesture::GESTURE_SWIPE:
+    case Gesture::GESTURE_LONG_TAP:
+    case Gesture::GESTURE_DRAG:
+    case Gesture::GESTURE_DROP:
+    case Gesture::GESTURE_PINCH:
         __gestureEventsProcessed.set(evt);
         break;
 
@@ -1373,6 +1519,9 @@ void Platform::unregisterGesture(Gesture::GestureEvent evt)
 
     case Gesture::GESTURE_TAP:
     case Gesture::GESTURE_SWIPE:
+    case Gesture::GESTURE_LONG_TAP:
+    case Gesture::GESTURE_DRAG:
+    case Gesture::GESTURE_DROP:
         __gestureEventsProcessed.set(evt, 0);
         break;
 

+ 131 - 1
gameplay/src/PlatformiOS.mm

@@ -7,6 +7,7 @@
 #include "Form.h"
 #include "ScriptController.h"
 #include <unistd.h>
+#include <sys/time.h>
 #import <UIKit/UIKit.h>
 #import <GameKit/GameKit.h>
 #import <QuartzCore/QuartzCore.h>
@@ -59,6 +60,15 @@ public:
     }
 };
 
+// gestures
+
+#define GESTURE_LONG_PRESS_DURATION_MIN 0.2
+#define GESTURE_LONG_PRESS_DISTANCE_MIN 10
+
+static CGPoint  __gestureLongPressStartPosition;
+static long __gestureLongTapStartTimestamp = 0;
+static bool __gestureDraging = false;
+
 // more than we'd ever need, to be safe
 #define TOUCH_POINTS_MAX (10)
 static TouchPoint __touchPoints[TOUCH_POINTS_MAX];
@@ -69,7 +79,6 @@ static bool __vsync = WINDOW_VSYNC;
 static float __pitch;
 static float __roll;
 
-
 double getMachTimeInMilliseconds();
 
 int getKey(unichar keyCode);
@@ -96,6 +105,9 @@ int getUnicode(int key);
     UITapGestureRecognizer *_tapRecognizer;
     UIPinchGestureRecognizer *_pinchRecognizer;
     UISwipeGestureRecognizer *_swipeRecognizer;
+    UILongPressGestureRecognizer *_longPressRecognizer;
+    UILongPressGestureRecognizer *_longTapRecognizer;
+    UILongPressGestureRecognizer *_dragAndDropRecognizer;
 }
 
 @property (readonly, nonatomic, getter=isUpdating) BOOL updating;
@@ -643,6 +655,11 @@ int getUnicode(int key);
             return (_pinchRecognizer != NULL);
         case Gesture::GESTURE_TAP:
             return (_tapRecognizer != NULL);
+        case Gesture::GESTURE_LONG_TAP:
+            return (_longTapRecognizer != NULL);
+        case Gesture::GESTURE_DRAG:
+        case Gesture::GESTURE_DROP:
+            return (_dragAndDropRecognizer != NULL);
         default:
             break;
     }
@@ -685,6 +702,28 @@ int getUnicode(int key);
         _tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
         [self addGestureRecognizer:_tapRecognizer];
     }
+    if ((evt & Gesture::GESTURE_LONG_TAP) == Gesture::GESTURE_LONG_TAP && _longTapRecognizer == NULL)
+    {
+        if (_longPressRecognizer == NULL)
+        {
+            _longPressRecognizer =[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPressGestures:)];
+            _longPressRecognizer.minimumPressDuration = GESTURE_LONG_PRESS_DURATION_MIN;
+            _longPressRecognizer.allowableMovement = CGFLOAT_MAX;
+            [self addGestureRecognizer:_longPressRecognizer];
+        }
+        _longTapRecognizer = _longPressRecognizer;
+    }
+    if (((evt & Gesture::GESTURE_DRAG) == Gesture::GESTURE_DRAG || (evt & Gesture::GESTURE_DROP) == Gesture::GESTURE_DROP) && _dragAndDropRecognizer == NULL)
+    {
+        if (_longPressRecognizer == NULL)
+        {
+            _longPressRecognizer =[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPressGestures:)];
+            _longPressRecognizer.minimumPressDuration = GESTURE_LONG_PRESS_DURATION_MIN;
+            _longPressRecognizer.allowableMovement = CGFLOAT_MAX;
+            [self addGestureRecognizer:_longPressRecognizer];
+        }
+        _dragAndDropRecognizer = _longPressRecognizer;
+    }
 }
 
 - (void)unregisterGesture: (Gesture::GestureEvent) evt
@@ -707,6 +746,25 @@ int getUnicode(int key);
         [_tapRecognizer release];
         _tapRecognizer = NULL;
     }
+    if((evt & Gesture::GESTURE_LONG_TAP) == Gesture::GESTURE_LONG_TAP && _longTapRecognizer != NULL)
+    {
+        if (_dragAndDropRecognizer == NULL)
+        {
+            [self removeGestureRecognizer:_longPressRecognizer];
+            [_longPressRecognizer release];
+        }
+        _longTapRecognizer = NULL;
+    }
+    if (((evt & Gesture::GESTURE_DRAG) == Gesture::GESTURE_DRAG || (evt & Gesture::GESTURE_DROP) == Gesture::GESTURE_DROP) && _dragAndDropRecognizer != NULL)
+    {
+        
+        if (_longTapRecognizer == NULL)
+        {
+            [self removeGestureRecognizer:_longPressRecognizer];
+            [_longPressRecognizer release];
+        }
+        _dragAndDropRecognizer = NULL;
+    }
 }
 
 - (void)handleTapGesture:(UITapGestureRecognizer*)sender
@@ -715,6 +773,27 @@ int getUnicode(int key);
     gameplay::Platform::gestureTapEventInternal(location.x, location.y);
 }
 
+- (void)handleLongTapGesture:(UILongPressGestureRecognizer*)sender
+{
+    if (sender.state == UIGestureRecognizerStateBegan)
+    {
+        struct timeval time;
+        
+        gettimeofday(&time, NULL);
+        __gestureLongTapStartTimestamp = (time.tv_sec * 1000) + (time.tv_usec / 1000);
+    }
+    else if (sender.state == UIGestureRecognizerStateEnded)
+    {
+        CGPoint location = [sender locationInView:self];
+        struct timeval time;
+        long currentTimeStamp;
+        
+        gettimeofday(&time, NULL);
+        currentTimeStamp = (time.tv_sec * 1000) + (time.tv_usec / 1000);
+        gameplay::Platform::gestureLongTapEventInternal(location.x, location.y, currentTimeStamp - __gestureLongTapStartTimestamp);
+    }
+}
+
 - (void)handlePinchGesture:(UIPinchGestureRecognizer*)sender
 {
     CGFloat factor = [sender scale];
@@ -744,6 +823,57 @@ int getUnicode(int key);
     gameplay::Platform::gestureSwipeEventInternal(location.x, location.y, gameplayDirection);
 }
 
+- (void)handleLongPressGestures:(UILongPressGestureRecognizer*)sender
+{
+    CGPoint location = [sender locationInView:self];
+    
+    if (sender.state == UIGestureRecognizerStateBegan)
+    {
+        struct timeval time;
+        
+        gettimeofday(&time, NULL);
+        __gestureLongTapStartTimestamp = (time.tv_sec * 1000) + (time.tv_usec / 1000);
+        __gestureLongPressStartPosition = location;
+    }
+    if (sender.state == UIGestureRecognizerStateChanged)
+    {
+        if (__gestureDraging)
+            gameplay::Platform::gestureDragEventInternal(location.x, location.y);
+        else
+        {
+            float delta = sqrt(pow(__gestureLongPressStartPosition.x - location.x, 2) + pow(__gestureLongPressStartPosition.y - location.y, 2));
+        
+            if (delta >= GESTURE_LONG_PRESS_DISTANCE_MIN)
+            {
+                __gestureDraging = true;
+                gameplay::Platform::gestureDragEventInternal(__gestureLongPressStartPosition.x, __gestureLongPressStartPosition.y);
+            }
+        }
+    }
+    if (sender.state == UIGestureRecognizerStateEnded)
+    {
+        if (__gestureDraging)
+        {
+            gameplay::Platform::gestureDropEventInternal(location.x, location.y);
+            __gestureDraging = false;
+        }
+        else
+        {
+            struct timeval time;
+            long currentTimeStamp;
+        
+            gettimeofday(&time, NULL);
+            currentTimeStamp = (time.tv_sec * 1000) + (time.tv_usec / 1000);
+            gameplay::Platform::gestureLongTapEventInternal(location.x, location.y, currentTimeStamp - __gestureLongTapStartTimestamp);
+        }
+    }
+    if ((sender.state == UIGestureRecognizerStateCancelled || sender.state == UIGestureRecognizerStateFailed) && __gestureDraging)
+    {
+        gameplay::Platform::gestureDropEventInternal(location.x, location.y);
+        __gestureDraging = false;
+    }
+}
+
 @end
 
 

+ 12 - 0
gameplay/src/ScriptController.cpp

@@ -873,6 +873,18 @@ void ScriptController::gestureTapEvent(int x, int y)
         executeFunction<void>(list[i].c_str(), "ii", x, y);
 }
 
+void ScriptController::gestureLongTapEvent(int x, int y, float duration)
+{
+}
+
+void ScriptController::gestureDragEvent(int x, int y)
+{
+}
+
+void ScriptController::gestureDropEvent(int x, int y)
+{
+}
+
 void ScriptController::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
 {
     std::vector<std::string>& list = _callbacks[GAMEPAD_EVENT];

+ 25 - 0
gameplay/src/ScriptController.h

@@ -901,6 +901,31 @@ private:
      */
     void gestureTapEvent(int x, int y);
 
+	/**
+	 * Gesture callback on Gesture::LONG_TAP events.
+	 *
+	 * @param x The x-coordinate of the long tap.
+	 * @param y The y-coordinate of the long tap.
+	 * @param duration The duration of the long tap in ms.
+	 */
+	void gestureLongTapEvent(int x, int y, float duration);
+
+	/**
+	 * Gesture callback on Gesture::DRAG events.
+	 *
+	 * @param x The x-coordinate of the start of the drag event.
+	 * @param y The y-coordinate of the start of the drag event.
+	 */
+	void gestureDragEvent(int x, int y);
+
+	/**
+	 * Gesture callback on Gesture::DROP events.
+	 *
+	 * @param x The x-coordinate of the drop event.
+	 * @param y The y-coordinate of the drop event.
+	 */
+	void gestureDropEvent(int x, int y);
+
     /**
      * Script gamepad callback on gamepad events.
      *

+ 58 - 1
samples/browser/src/GestureSample.cpp

@@ -42,6 +42,24 @@ void GestureSample::initialize()
         registerGesture(Gesture::GESTURE_PINCH);
         GP_ASSERT(isGestureRegistered(Gesture::GESTURE_PINCH));
     }
+	if (isGestureSupported(Gesture::GESTURE_LONG_TAP))
+	{
+        anySupported = true;
+        registerGesture(Gesture::GESTURE_LONG_TAP);
+        GP_ASSERT(isGestureRegistered(Gesture::GESTURE_LONG_TAP));
+	}
+	if (isGestureSupported(Gesture::GESTURE_DRAG))
+	{
+        anySupported = true;
+        registerGesture(Gesture::GESTURE_DRAG);
+        GP_ASSERT(isGestureRegistered(Gesture::GESTURE_DRAG));
+	}
+	if (isGestureSupported(Gesture::GESTURE_DROP))
+	{
+        anySupported = true;
+        registerGesture(Gesture::GESTURE_DROP);
+        GP_ASSERT(isGestureRegistered(Gesture::GESTURE_DROP));
+	}
     GP_ASSERT(anySupported == isGestureSupported(Gesture::GESTURE_ANY_SUPPORTED));
 }
 
@@ -52,6 +70,9 @@ void GestureSample::finalize()
     unregisterGesture(Gesture::GESTURE_TAP);
     unregisterGesture(Gesture::GESTURE_SWIPE);
     unregisterGesture(Gesture::GESTURE_PINCH);
+	unregisterGesture(Gesture::GESTURE_LONG_TAP);
+	unregisterGesture(Gesture::GESTURE_DRAG);
+	unregisterGesture(Gesture::GESTURE_DROP);
 }
 
 void GestureSample::update(float elapsedTime)
@@ -83,7 +104,7 @@ void GestureSample::render(float elapsedTime)
     }
     
     int x = getWidth() - 200;
-    y = getHeight() - _font->getSize() * 3;
+    y = getHeight() - _font->getSize() * 6;
 
     if (isGestureSupported(Gesture::GESTURE_TAP))
     {
@@ -100,6 +121,21 @@ void GestureSample::render(float elapsedTime)
         _font->drawText("Pinch supported", x, y, fontColor, _font->getSize());
         y += _font->getSize();
     }
+    if (isGestureSupported(Gesture::GESTURE_LONG_TAP))
+    {
+        _font->drawText("Long tap supported", x, y, fontColor, _font->getSize());
+        y += _font->getSize();
+    }
+    if (isGestureSupported(Gesture::GESTURE_DRAG))
+    {
+        _font->drawText("Drag supported", x, y, fontColor, _font->getSize());
+        y += _font->getSize();
+    }
+    if (isGestureSupported(Gesture::GESTURE_DROP))
+    {
+        _font->drawText("Drop supported", x, y, fontColor, _font->getSize());
+        y += _font->getSize();
+    }
 
     _font->finish();
 }
@@ -160,3 +196,24 @@ void GestureSample::gestureTapEvent(int x, int y)
     _eventLog.push_front(convert.str());
 }
 
+void GestureSample::gestureLongTapEvent(int x, int y, float duration)
+{
+    std::ostringstream convert;
+    convert << "Long tap " << x << ", " << y << " (" << duration << "ms)";
+    _eventLog.push_front(convert.str());
+}
+
+void GestureSample::gestureDragEvent(int x, int y)
+{
+    std::ostringstream convert;
+    convert << "Drag " << x << ", " << y;
+    _eventLog.push_front(convert.str());
+}
+
+void GestureSample::gestureDropEvent(int x, int y)
+{
+    std::ostringstream convert;
+    convert << "Drop " << x << ", " << y;
+    _eventLog.push_front(convert.str());
+}
+

+ 6 - 0
samples/browser/src/GestureSample.h

@@ -23,6 +23,12 @@ public:
     
     void gestureTapEvent(int x, int y);
 
+	void gestureLongTapEvent(int x, int y, float duration);
+
+	void gestureDragEvent(int x, int y);
+	
+	void gestureDropEvent(int x, int y);
+
 protected:
 
     void initialize();

+ 12 - 0
samples/browser/src/Sample.cpp

@@ -237,6 +237,18 @@ void Sample::gestureTapEvent(int x, int y)
 {
 }
 
+void Sample::gestureLongTapEvent(int x, int y, float duration)
+{
+}
+
+void Sample::gestureDragEvent(int x, int y)
+{
+}
+
+void Sample::gestureDropEvent(int x, int y)
+{
+}
+
 void Sample::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad)
 {
 }

+ 4 - 1
samples/browser/src/Sample.h

@@ -72,7 +72,10 @@ public:
     virtual void gestureSwipeEvent(int x, int y, int direction);
     virtual void gesturePinchEvent(int x, int y, float scale);
     virtual void gestureTapEvent(int x, int y);
-    virtual void gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad);
+    virtual void gestureLongTapEvent(int x, int y, float duration);
+    virtual void gestureDragEvent(int x, int y);
+	virtual void gestureDropEvent(int x, int y);
+	virtual void gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad);
     unsigned int getGamepadCount() const;
     Gamepad* getGamepad(unsigned int index, bool preferPhysical = true) const;
 

+ 18 - 0
samples/browser/src/SamplesGame.cpp

@@ -205,6 +205,24 @@ void SamplesGame::gestureTapEvent(int x, int y)
         _activeSample->gestureTapEvent(x, y);
 }
 
+void SamplesGame::gestureLongTapEvent(int x, int y, float duration)
+{
+	if (_activeSample)
+		_activeSample->gestureLongTapEvent(x, y, duration);
+}
+
+void SamplesGame::gestureDragEvent(int x, int y)
+{
+	if (_activeSample)
+		_activeSample->gestureDragEvent(x, y);
+}
+
+void SamplesGame::gestureDropEvent(int x, int y)
+{
+	if (_activeSample)
+		_activeSample->gestureDropEvent(x, y);
+}
+
 void SamplesGame::controlEvent(Control* control, EventType evt)
 {
     const size_t size = _samples->size();

+ 7 - 1
samples/browser/src/SamplesGame.h

@@ -49,7 +49,13 @@ public:
     
     void gestureTapEvent(int x, int y);
 
-    void controlEvent(Control* control, EventType evt);
+	void gestureLongTapEvent(int x, int y, float duration);
+
+	void gestureDragEvent(int x, int y);
+
+	void gestureDropEvent(int x, int y);
+
+	void controlEvent(Control* control, EventType evt);
 
     void gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex = 0);