Jelajahi Sumber

Merge branch 'next' of https://github.com/blackberry/GamePlay into next

Conflicts:
	gameplay/src/PlatformLinux.cpp
sgrenier 13 tahun lalu
induk
melakukan
04aa4e68c0

+ 1 - 1
gameplay-template/gameplay-template.vcxproj

@@ -315,7 +315,7 @@
     </None>
     <None Include="game.config" />
     <None Include="icon.png" />
-    <None Include="res\box.dae" />
+    <None Include="res\box.fbx" />
     <None Include="res\box.gpb" />
     <None Include="res\box.material" />
     <None Include="res\colored.frag" />

+ 1 - 1
gameplay-template/gameplay-template.vcxproj.filters

@@ -10,7 +10,7 @@
 	<None Include="game.config" />
     <None Include="icon.png" />
     <None Include="bar-descriptor.xml" />
-    <None Include="res\box.dae">
+    <None Include="res\box.fbx">
       <Filter>res</Filter>
     </None>
     <None Include="res\box.gpb">

+ 0 - 183
gameplay-template/res/box.dae

@@ -1,183 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.1">
-  <asset>
-    <contributor>
-      <author>sgrenier</author>
-      <authoring_tool>OpenCOLLADA2011 x64</authoring_tool>
-      <comments>
-			ColladaMaya export options: 
-			bakeTransforms=1;relativePaths=0;copyTextures=0;exportTriangles=1;exportCgfxFileReferences=0;
-			isSampling=0;curveConstrainSampling=0;removeStaticCurves=1;exportPolygonMeshes=1;exportLights=1;
-			exportCameras=1;exportJointsAndSkin=1;exportAnimations=1;exportInvisibleNodes=0;exportDefaultCameras=0;
-			exportTexCoords=1;exportNormals=1;exportNormalsPerVertex=1;exportVertexColors=0;exportVertexColorsPerVertex=0;
-			exportTexTangents=0;exportTangents=0;exportReferencedMaterials=0;exportMaterialsOnly=0;
-			exportXRefs=1;dereferenceXRefs=1;exportCameraAsLookat=0;cameraXFov=0;cameraYFov=1;doublePrecision=0
-		</comments>
-      <source_data>file:///C:/Users/sgrenier/Documents/maya/projects/default/untitled</source_data>
-    </contributor>
-    <created>2011-12-05T20:23:32</created>
-    <modified>2011-12-05T20:23:32</modified>
-    <unit name="centimeter" meter="0.01"/>
-    <up_axis>Y_UP</up_axis>
-  </asset>
-  <library_lights>
-    <light id="directionalLightShape" name="directionalLightShape">
-      <technique_common>
-        <directional>
-          <color>1 1 1</color>
-        </directional>
-      </technique_common>
-      <extra>
-        <technique profile="OpenCOLLADAMaya">
-          <originalMayaNodeId>directionalLightShape</originalMayaNodeId>
-        </technique>
-      </extra>
-    </light>
-  </library_lights>
-  <library_cameras>
-    <camera id="cameraShape" name="cameraShape">
-      <optics>
-        <technique_common>
-          <perspective>
-            <yfov>27.38717</yfov>
-            <aspect_ratio>1.7</aspect_ratio>
-            <znear>0.25</znear>
-            <zfar>100</zfar>
-          </perspective>
-        </technique_common>
-      </optics>
-      <extra>
-        <technique profile="OpenCOLLADAMaya">
-          <film_fit>0</film_fit>
-          <film_fit_offset>0</film_fit_offset>
-          <film_offsetX>0</film_offsetX>
-          <film_offsetY>0</film_offsetY>
-          <horizontal_aperture>4.079992</horizontal_aperture>
-          <lens_squeeze>1</lens_squeeze>
-          <originalMayaNodeId>cameraShape</originalMayaNodeId>
-          <vertical_aperture>2.399995</vertical_aperture>
-        </technique>
-      </extra>
-    </camera>
-  </library_cameras>
-  <library_materials>
-    <material id="lambert1" name="lambert1">
-      <instance_effect url="#lambert1-fx"/>
-    </material>
-  </library_materials>
-  <library_effects>
-    <effect id="lambert1-fx">
-      <profile_COMMON>
-        <technique sid="common">
-          <lambert>
-            <emission>
-              <color>0 0 0 1</color>
-            </emission>
-            <ambient>
-              <color>0 0 0 1</color>
-            </ambient>
-            <diffuse>
-              <color>0.4 0.4 0.4 1</color>
-            </diffuse>
-            <transparent opaque="RGB_ZERO">
-              <color>0 0 0 1</color>
-            </transparent>
-            <transparency>
-              <float>1</float>
-            </transparency>
-          </lambert>
-        </technique>
-      </profile_COMMON>
-    </effect>
-  </library_effects>
-  <library_geometries>
-    <geometry id="boxShape" name="boxShape">
-      <mesh>
-        <source id="boxShape-positions" name="boxShape-positions">
-          <float_array id="boxShape-positions-array" count="24">-0.5 -0.5 0.5 0.5 -0.5 0.5 -0.5 0.5 0.5 0.5 0.5 0.5 -0.5 0.5 -0.5 0.5 0.5 -0.5 -0.5 -0.5 -0.5 0.5 -0.5 -0.5</float_array>
-          <technique_common>
-            <accessor source="#boxShape-positions-array" count="8" stride="3">
-              <param name="X" type="float"/>
-              <param name="Y" type="float"/>
-              <param name="Z" type="float"/>
-            </accessor>
-          </technique_common>
-        </source>
-        <source id="boxShape-normals" name="boxShape-normals">
-          <float_array id="boxShape-normals-array" count="72">0 0 1 0 0 1 0 0 1 0 0 1 0 1 0 0 1 0 0 1 0 0 1 0 0 0 -1 0 0 -1 0 0 -1 0 0 -1 0 -1 0 0 -1 0 0 -1 0 0 -1 0 1 0 0 1 0 0 1 0 0 1 0 0 -1 0 0 -1 0 0 -1 0 0 -1 0 0</float_array>
-          <technique_common>
-            <accessor source="#boxShape-normals-array" count="24" stride="3">
-              <param name="X" type="float"/>
-              <param name="Y" type="float"/>
-              <param name="Z" type="float"/>
-            </accessor>
-          </technique_common>
-        </source>
-        <source id="boxShape-map1" name="boxShape-map1">
-          <float_array id="boxShape-map1-array" count="28">0.375 0 0.625 0 0.375 0.25 0.625 0.25 0.375 0.5 0.625 0.5 0.375 0.75 0.625 0.75 0.375 1 0.625 1 0.875 0 0.875 0.25 0.125 0 0.125 0.25</float_array>
-          <technique_common>
-            <accessor source="#boxShape-map1-array" count="14" stride="2">
-              <param name="S" type="float"/>
-              <param name="T" type="float"/>
-            </accessor>
-          </technique_common>
-        </source>
-        <vertices id="boxShape-vertices" name="boxShape-vertices">
-          <input semantic="POSITION" source="#boxShape-positions"/>
-        </vertices>
-        <triangles material="initialShadingGroup" count="12">
-          <input semantic="VERTEX" source="#boxShape-vertices" offset="0"/>
-          <input semantic="NORMAL" source="#boxShape-normals" offset="1"/>
-          <input semantic="TEXCOORD" source="#boxShape-map1" offset="2" set="0"/>
-          <p>0 0 0 1 1 1 2 3 2 2 3 2 1 1 1 3 2 3 2 4 2 3 5 3 4 7 4 4 7 4 3 5 3 5 6 5 4 8 4 5 9 5 6 11 6 6 11 6 5 9 5 7 10 7 6 12 6 7 13 7 0 15 8 0 15 8 7 13 7 1 14 9 1 16 1 7 17 10 3 19 3 3 19 3 7 17 10 5 18 11 6 20 12 0 21 0 4 23 13 4 23 13 0 21 0 2 22 2</p>
-        </triangles>
-      </mesh>
-      <extra>
-        <technique profile="OpenCOLLADAMaya">
-          <originalMayaNodeId>boxShape</originalMayaNodeId>
-          <double_sided>1</double_sided>
-        </technique>
-      </extra>
-    </geometry>
-  </library_geometries>
-  <library_visual_scenes>
-    <visual_scene id="VisualSceneNode" name="untitled">
-      <node id="box" name="box" type="NODE">
-        <matrix sid="transform">1 0 0 0 0 1 0 0.5 0 0 1 0 0 0 0 1</matrix>
-        <instance_geometry url="#boxShape">
-          <bind_material>
-            <technique_common>
-              <instance_material symbol="initialShadingGroup" target="#lambert1"/>
-            </technique_common>
-          </bind_material>
-        </instance_geometry>
-        <extra>
-          <technique profile="OpenCOLLADAMaya">
-            <originalMayaNodeId>box</originalMayaNodeId>
-          </technique>
-        </extra>
-      </node>
-      <node id="camera" name="camera" type="NODE">
-        <matrix sid="transform">0.9753993 -0.08127667 0.2049154 1.554299 0.0276844 0.9673549 0.251909 2.301022 -0.2187002 -0.2400389 0.9458073 7.279555 0 0 0 1</matrix>
-        <instance_camera url="#cameraShape"/>
-        <extra>
-          <technique profile="OpenCOLLADAMaya">
-            <originalMayaNodeId>camera</originalMayaNodeId>
-          </technique>
-        </extra>
-      </node>
-      <node id="directionalLight" name="directionalLight" type="NODE">
-        <matrix sid="transform">0.9282893 -0.14183 0.3437488 4.077966 -0.03090286 0.8917856 0.4514016 1.976955 -0.3705726 -0.4296541 0.8234521 6.724438 0 0 0 1</matrix>
-        <instance_light url="#directionalLightShape"/>
-        <extra>
-          <technique profile="OpenCOLLADAMaya">
-            <originalMayaNodeId>directionalLight</originalMayaNodeId>
-          </technique>
-        </extra>
-      </node>
-    </visual_scene>
-  </library_visual_scenes>
-  <scene>
-    <instance_visual_scene url="#VisualSceneNode"/>
-  </scene>
-</COLLADA>

TEMPAT SAMPAH
gameplay-template/res/box.fbx


TEMPAT SAMPAH
gameplay-template/res/box.gpb


+ 2 - 4
gameplay-template/src/TemplateGame.cpp

@@ -11,12 +11,10 @@ TemplateGame::TemplateGame()
 void TemplateGame::initialize()
 {
     // Load game scene from file
-    Bundle* bundle = Bundle::create("res/box.gpb");
-    _scene = bundle->loadScene();
-    SAFE_RELEASE(bundle);
+    _scene = Scene::load("res/box.gpb");
 
     // Set the aspect ratio for the scene's camera to match the current resolution
-    _scene->getActiveCamera()->setAspectRatio((float)getWidth() / (float)getHeight());
+    _scene->getActiveCamera()->setAspectRatio(getAspectRatio());
     
     // Get light node
     Node* lightNode = _scene->findNode("directionalLight");

+ 1 - 1
gameplay/res/shaders/colored.vert

@@ -15,11 +15,11 @@ varying vec3 v_color;										// Output Vertex Color
 // Uniforms
 uniform mat4 u_worldViewProjectionMatrix;					// Matrix to transform a position to clip space.
 uniform mat4 u_inverseTransposeWorldViewMatrix;				// Matrix to transform a normal to view space
+uniform mat4 u_worldViewMatrix;								// Matrix to tranform a position to view space.
 #if defined(SKINNING)
 uniform vec4 u_matrixPalette[SKINNING_JOINT_COUNT * 3];		// Array of 4x3 matrices
 #endif
 #if defined(SPECULAR)
-uniform mat4 u_worldViewMatrix;								// Matrix to tranform a position to view space.
 uniform vec3 u_cameraPosition;                 				// Position of the camera in view space.
 #endif
 #if defined(POINT_LIGHT)

+ 5 - 0
gameplay/src/Game.cpp

@@ -509,6 +509,11 @@ void Game::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad)
 {
 }
 
+void Game::getArguments(int* argc, char*** argv)
+{
+    Platform::getArguments(argc, argv);
+}
+
 void Game::schedule(float timeOffset, TimeListener* timeListener, void* cookie)
 {
     GP_ASSERT(_timeEvents);

+ 8 - 0
gameplay/src/Game.h

@@ -518,6 +518,14 @@ public:
      */
     inline void getAccelerometerValues(float* pitch, float* roll);
 
+    /**
+     * Gets the command line arguments.
+     * 
+     * @param argc The number of command line arguments.
+     * @param argv The array of command line arguments.
+     */
+    void getArguments(int* argc, char*** argv);
+
     /**
      * Schedules a time event to be sent to the given TimeListener a given number of game milliseconds from now.
      * Game time stops while the game is paused. A time offset of zero will fire the time event in the next frame.

+ 8 - 0
gameplay/src/Platform.h

@@ -207,6 +207,14 @@ private:
      * @param roll The accelerometer roll.
      */
     static void getAccelerometerValues(float* pitch, float* roll);
+
+    /**
+     * Gets the command line arguments.
+     * 
+     * @param argc The number of command line arguments.
+     * @param argv The array of command line arguments.
+     */
+    static void getArguments(int* argc, char*** argv);
     
     /**
      * Shows or hides the virtual keyboard (if supported).

+ 1404 - 1396
gameplay/src/PlatformAndroid.cpp

@@ -1,1396 +1,1404 @@
-#ifdef __ANDROID__
-
-#include "Base.h"
-#include "Platform.h"
-#include "FileSystem.h"
-#include "Game.h"
-#include "Form.h"
-#include "ScriptController.h"
-#include <unistd.h>
-#include <android/sensor.h>
-#include <android_native_app_glue.h>
-#include <android/log.h>
-
-// Externally referenced global variables.
-struct android_app* __state;
-AAssetManager* __assetManager;
-
-static bool __initialized;
-static bool __suspended;
-static EGLDisplay __eglDisplay = EGL_NO_DISPLAY;
-static EGLContext __eglContext = EGL_NO_CONTEXT;
-static EGLSurface __eglSurface = EGL_NO_SURFACE;
-static EGLConfig __eglConfig = 0;
-static int __width;
-static int __height;
-static struct timespec __timespec;
-static double __timeStart;
-static double __timeAbsolute;
-static bool __vsync = WINDOW_VSYNC;
-static ASensorManager* __sensorManager;
-static ASensorEventQueue* __sensorEventQueue;
-static ASensorEvent __sensorEvent;
-static const ASensor* __accelerometerSensor;
-static int __orientationAngle = 90;
-static bool __multiSampling = false;
-static bool __multiTouch = false;
-static int __primaryTouchId = -1;
-static bool __displayKeyboard = false;
-
-// OpenGL VAO functions.
-static const char* __glExtensions;
-PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray = NULL;
-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;
-
-struct TouchPointerData
-{
-    size_t pointerId;
-    bool pressed;
-    double time;
-    int x;
-    int y;
-};
-
-TouchPointerData __pointer0;
-TouchPointerData __pointer1;
-
-namespace gameplay
-{
-
-static double timespec2millis(struct timespec *a)
-{
-    GP_ASSERT(a);
-    return (1000.0 * a->tv_sec) + (0.000001 * a->tv_nsec);
-}
-
-extern void print(const char* format, ...)
-{
-    GP_ASSERT(format);
-    va_list argptr;
-    va_start(argptr, format);
-    __android_log_vprint(ANDROID_LOG_INFO, "gameplay-native-activity", format, argptr);
-    va_end(argptr);
-}
-
-static EGLenum checkErrorEGL(const char* msg)
-{
-    GP_ASSERT(msg);
-    static const char* errmsg[] =
-    {
-        "EGL function succeeded",
-        "EGL is not initialized, or could not be initialized, for the specified display",
-        "EGL cannot access a requested resource",
-        "EGL failed to allocate resources for the requested operation",
-        "EGL fail to access an unrecognized attribute or attribute value was passed in an attribute list",
-        "EGLConfig argument does not name a valid EGLConfig",
-        "EGLContext argument does not name a valid EGLContext",
-        "EGL current surface of the calling thread is no longer valid",
-        "EGLDisplay argument does not name a valid EGLDisplay",
-        "EGL arguments are inconsistent",
-        "EGLNativePixmapType argument does not refer to a valid native pixmap",
-        "EGLNativeWindowType argument does not refer to a valid native window",
-        "EGL one or more argument values are invalid",
-        "EGLSurface argument does not name a valid surface configured for rendering",
-        "EGL power management event has occurred",
-    };
-    EGLenum error = eglGetError();
-    print("%s: %s.", msg, errmsg[error - EGL_SUCCESS]);
-    return error;
-}
-
-static int getRotation()
-{
-    jint rotation;
-
-    // Get the android application's activity.
-    ANativeActivity* activity = __state->activity;
-    JavaVM* jvm = __state->activity->vm;
-    JNIEnv* env = NULL;
-    jvm->GetEnv((void **)&env, JNI_VERSION_1_6);
-    jint res = jvm->AttachCurrentThread(&env, NULL);
-    if (res == JNI_ERR)
-    {
-        GP_ERROR("Failed to retrieve JVM environment when entering message pump.");
-        return -1; 
-    }
-    GP_ASSERT(env);
-
-    jclass clsContext = env->FindClass("android/content/Context");
-    GP_ASSERT(clsContext != NULL);
-    jmethodID getSystemService = env->GetMethodID(clsContext, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
-    GP_ASSERT(getSystemService != NULL);
-    jfieldID WINDOW_SERVICE_ID = env->GetStaticFieldID(clsContext, "WINDOW_SERVICE", "Ljava/lang/String;");
-    GP_ASSERT(WINDOW_SERVICE_ID != NULL);
-    jstring WINDOW_SERVICE = (jstring) env->GetStaticObjectField(clsContext, WINDOW_SERVICE_ID);
-    GP_ASSERT(WINDOW_SERVICE != NULL);
-    jobject windowManager = env->CallObjectMethod(activity->clazz, getSystemService, WINDOW_SERVICE);
-    GP_ASSERT(windowManager != NULL);
-    jclass clsWindowManager = env->FindClass("android/view/WindowManager");
-    GP_ASSERT(clsWindowManager != NULL);
-    jmethodID getDefaultDisplay = env->GetMethodID(clsWindowManager, "getDefaultDisplay", "()Landroid/view/Display;");
-    GP_ASSERT(getDefaultDisplay != NULL);
-    jobject defaultDisplay = env->CallObjectMethod(windowManager, getDefaultDisplay);
-    GP_ASSERT(defaultDisplay != NULL);
-    jclass clsDisplay = env->FindClass("android/view/Display");
-    GP_ASSERT(clsDisplay != NULL);
-    jmethodID getRotation = env->GetMethodID(clsDisplay, "getRotation", "()I");
-    GP_ASSERT(getRotation != NULL)
-    rotation =  env->CallIntMethod(defaultDisplay, getRotation);
-
-    return rotation;
-}
-
-
-// Initialized EGL resources.
-static bool initEGL()
-{
-    int samples = 0;
-    Properties* config = Game::getInstance()->getConfig()->getNamespace("window", true);
-    if (config)
-    {
-        samples = std::max(config->getInt("samples"), 0);
-    }
-
-    // Hard-coded to 32-bit/OpenGL ES 2.0.
-    // NOTE: EGL_SAMPLE_BUFFERS, EGL_SAMPLES and EGL_DEPTH_SIZE MUST remain at the beginning of the attribute list
-    // since they are expected to be at indices 0-5 in config fallback code later.
-    // EGL_DEPTH_SIZE is also expected to
-    EGLint eglConfigAttrs[] =
-    {
-        EGL_SAMPLE_BUFFERS,     samples > 0 ? 1 : 0,
-        EGL_SAMPLES,            samples,
-        EGL_DEPTH_SIZE,         24,
-        EGL_RED_SIZE,           8,
-        EGL_GREEN_SIZE,         8,
-        EGL_BLUE_SIZE,          8,
-        EGL_ALPHA_SIZE,         8,
-        EGL_STENCIL_SIZE,       8,
-        EGL_SURFACE_TYPE,       EGL_WINDOW_BIT,
-        EGL_RENDERABLE_TYPE,    EGL_OPENGL_ES2_BIT,
-        EGL_NONE
-    };
-    __multiSampling = samples > 0;
-    
-    EGLint eglConfigCount;
-    const EGLint eglContextAttrs[] =
-    {
-        EGL_CONTEXT_CLIENT_VERSION,    2,
-        EGL_NONE
-    };
-
-    const EGLint eglSurfaceAttrs[] =
-    {
-        EGL_RENDER_BUFFER,    EGL_BACK_BUFFER,
-        EGL_NONE
-    };
-
-    if (__eglDisplay == EGL_NO_DISPLAY && __eglContext == EGL_NO_CONTEXT)
-    {
-        // Get the EGL display and initialize.
-        __eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-        if (__eglDisplay == EGL_NO_DISPLAY)
-        {
-            checkErrorEGL("eglGetDisplay");
-            goto error;
-        }
-
-        if (eglInitialize(__eglDisplay, NULL, NULL) != EGL_TRUE)
-        {
-            checkErrorEGL("eglInitialize");
-            goto error;
-        }
-
-        // Try both 24 and 16-bit depth sizes since some hardware (i.e. Tegra) does not support 24-bit depth
-        bool validConfig = false;
-        EGLint depthSizes[] = { 24, 16 };
-        for (unsigned int i = 0; i < 2; ++i)
-        {
-            eglConfigAttrs[1] = samples > 0 ? 1 : 0;
-            eglConfigAttrs[3] = samples;
-            eglConfigAttrs[5] = depthSizes[i];
-
-            if (eglChooseConfig(__eglDisplay, eglConfigAttrs, &__eglConfig, 1, &eglConfigCount) == EGL_TRUE && eglConfigCount > 0)
-            {
-                validConfig = true;
-                break;
-            }
-
-            if (samples)
-            {
-                // Try lowering the MSAA sample size until we find a supported config
-                int sampleCount = samples;
-                while (sampleCount)
-                {
-                    GP_WARN("No EGL config found for depth_size=%d and samples=%d. Trying samples=%d instead.", depthSizes[i], sampleCount, sampleCount / 2);
-                    sampleCount /= 2;
-                    eglConfigAttrs[1] = sampleCount > 0 ? 1 : 0;
-                    eglConfigAttrs[3] = sampleCount;
-                    if (eglChooseConfig(__eglDisplay, eglConfigAttrs, &__eglConfig, 1, &eglConfigCount) == EGL_TRUE && eglConfigCount > 0)
-                    {
-                        validConfig = true;
-                        break;
-                    }
-                }
-
-                __multiSampling = sampleCount > 0;
-
-                if (validConfig)
-                    break;
-            }
-            else
-            {
-                GP_WARN("No EGL config found for depth_size=%d.", depthSizes[i]);
-            }
-        }
-
-        if (!validConfig)
-        {
-            checkErrorEGL("eglChooseConfig");
-            goto error;
-        }
-
-        __eglContext = eglCreateContext(__eglDisplay, __eglConfig, EGL_NO_CONTEXT, eglContextAttrs);
-        if (__eglContext == EGL_NO_CONTEXT)
-        {
-            checkErrorEGL("eglCreateContext");
-            goto error;
-        }
-    }
-    
-    // EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is
-    // guaranteed to be accepted by ANativeWindow_setBuffersGeometry().
-    // As soon as we picked a EGLConfig, we can safely reconfigure the
-    // ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID.
-    EGLint format;
-    eglGetConfigAttrib(__eglDisplay, __eglConfig, EGL_NATIVE_VISUAL_ID, &format);
-    ANativeWindow_setBuffersGeometry(__state->window, 0, 0, format);
-    
-    __eglSurface = eglCreateWindowSurface(__eglDisplay, __eglConfig, __state->window, eglSurfaceAttrs);
-    if (__eglSurface == EGL_NO_SURFACE)
-    {
-        checkErrorEGL("eglCreateWindowSurface");
-        goto error;
-    }
-    
-    if (eglMakeCurrent(__eglDisplay, __eglSurface, __eglSurface, __eglContext) != EGL_TRUE)
-    {
-        checkErrorEGL("eglMakeCurrent");
-        goto error;
-    }
-    
-    eglQuerySurface(__eglDisplay, __eglSurface, EGL_WIDTH, &__width);
-    eglQuerySurface(__eglDisplay, __eglSurface, EGL_HEIGHT, &__height);
-
-    __orientationAngle = getRotation() * 90;
-    
-    // Set vsync.
-    eglSwapInterval(__eglDisplay, WINDOW_VSYNC ? 1 : 0);
-    
-    // Initialize OpenGL ES extensions.
-    __glExtensions = (const char*)glGetString(GL_EXTENSIONS);
-    
-    if (strstr(__glExtensions, "GL_OES_vertex_array_object") || strstr(__glExtensions, "GL_ARB_vertex_array_object"))
-    {
-        // Disable VAO extension for now.
-        glBindVertexArray = (PFNGLBINDVERTEXARRAYOESPROC)eglGetProcAddress("glBindVertexArrayOES");
-        glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSOESPROC)eglGetProcAddress("glDeleteVertexArrays");
-        glGenVertexArrays = (PFNGLGENVERTEXARRAYSOESPROC)eglGetProcAddress("glGenVertexArraysOES");
-        glIsVertexArray = (PFNGLISVERTEXARRAYOESPROC)eglGetProcAddress("glIsVertexArrayOES");
-    }
-    
-    return true;
-    
-error:
-    return false;
-}
-
-static void destroyEGLSurface()
-{
-    if (__eglDisplay != EGL_NO_DISPLAY)
-    {
-        eglMakeCurrent(__eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-    }
-
-    if (__eglSurface != EGL_NO_SURFACE)
-    {
-        eglDestroySurface(__eglDisplay, __eglSurface);
-        __eglSurface = EGL_NO_SURFACE;
-    }
-}
-
-static void destroyEGLMain()
-{
-    destroyEGLSurface();
-
-    if (__eglContext != EGL_NO_CONTEXT)
-    {
-        eglDestroyContext(__eglDisplay, __eglContext);
-        __eglContext = EGL_NO_CONTEXT;
-    }
-
-    if (__eglDisplay != EGL_NO_DISPLAY)
-    {
-        eglTerminate(__eglDisplay);
-        __eglDisplay = EGL_NO_DISPLAY;
-    }
-}
-
-// Display the android virtual keyboard.
-static void displayKeyboard(android_app* state, bool show)
-{ 
-    // The following functions is supposed to show / hide functins from a native activity.. but currently do not work. 
-    // ANativeActivity_showSoftInput(state->activity, ANATIVEACTIVITY_SHOW_SOFT_INPUT_IMPLICIT);
-    // ANativeActivity_hideSoftInput(state->activity, ANATIVEACTIVITY_HIDE_SOFT_INPUT_IMPLICIT_ONLY);
-    
-    GP_ASSERT(state && state->activity && state->activity->vm);
-
-    // Show or hide the keyboard by calling the appropriate Java method through JNI instead.
-    jint flags = 0;
-    JavaVM* jvm = state->activity->vm;
-    JNIEnv* env = NULL;
-    jvm->GetEnv((void **)&env, JNI_VERSION_1_6);
-    jint result = jvm->AttachCurrentThread(&env, NULL);
-    if (result == JNI_ERR)
-    {
-        GP_ERROR("Failed to retrieve JVM environment to display keyboard.");
-        return; 
-    }
-    GP_ASSERT(env);
-
-    // Retrieves NativeActivity. 
-    jobject lNativeActivity = state->activity->clazz;
-    jclass ClassNativeActivity = env->GetObjectClass(lNativeActivity);
-
-    // Retrieves Context.INPUT_METHOD_SERVICE.
-    jclass ClassContext = env->FindClass("android/content/Context");
-    jfieldID FieldINPUT_METHOD_SERVICE = env->GetStaticFieldID(ClassContext, "INPUT_METHOD_SERVICE", "Ljava/lang/String;");
-    jobject INPUT_METHOD_SERVICE = env->GetStaticObjectField(ClassContext, FieldINPUT_METHOD_SERVICE);
-    
-    // Runs getSystemService(Context.INPUT_METHOD_SERVICE).
-    jclass ClassInputMethodManager = env->FindClass("android/view/inputmethod/InputMethodManager");
-    jmethodID MethodGetSystemService = env->GetMethodID(ClassNativeActivity, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
-    jobject lInputMethodManager = env->CallObjectMethod(lNativeActivity, MethodGetSystemService, INPUT_METHOD_SERVICE);
-    
-    // Runs getWindow().getDecorView().
-    jmethodID MethodGetWindow = env->GetMethodID(ClassNativeActivity, "getWindow", "()Landroid/view/Window;");
-    jobject lWindow = env->CallObjectMethod(lNativeActivity, MethodGetWindow);
-    jclass ClassWindow = env->FindClass("android/view/Window");
-    jmethodID MethodGetDecorView = env->GetMethodID(ClassWindow, "getDecorView", "()Landroid/view/View;");
-    jobject lDecorView = env->CallObjectMethod(lWindow, MethodGetDecorView);
-    if (show)
-    {
-        // Runs lInputMethodManager.showSoftInput(...).
-        jmethodID MethodShowSoftInput = env->GetMethodID( ClassInputMethodManager, "showSoftInput", "(Landroid/view/View;I)Z");
-        jboolean result = env->CallBooleanMethod(lInputMethodManager, MethodShowSoftInput, lDecorView, flags); 
-    } 
-    else 
-    { 
-        // Runs lWindow.getViewToken() 
-        jclass ClassView = env->FindClass("android/view/View");
-        jmethodID MethodGetWindowToken = env->GetMethodID(ClassView, "getWindowToken", "()Landroid/os/IBinder;");
-        jobject lBinder = env->CallObjectMethod(lDecorView, MethodGetWindowToken); 
-        
-        // lInputMethodManager.hideSoftInput(...). 
-        jmethodID MethodHideSoftInput = env->GetMethodID(ClassInputMethodManager, "hideSoftInputFromWindow", "(Landroid/os/IBinder;I)Z"); 
-        jboolean lRes = env->CallBooleanMethod( lInputMethodManager, MethodHideSoftInput, lBinder, flags); 
-    }
-    
-    // Finished with the JVM.
-    jvm->DetachCurrentThread(); 
-}
-
-// Gets the Keyboard::Key enumeration constant that corresponds to the given Android key code.
-static Keyboard::Key getKey(int keycode, int metastate)
-{
-    bool shiftOn = (metastate == AMETA_SHIFT_ON);
-    
-    switch(keycode)
-    {
-        case AKEYCODE_HOME:
-            return Keyboard::KEY_HOME;
-        case AKEYCODE_0:
-            return Keyboard::KEY_ZERO;
-        case AKEYCODE_1:
-            return Keyboard::KEY_ONE;
-        case AKEYCODE_2:
-            return Keyboard::KEY_TWO;
-        case AKEYCODE_3:
-            return Keyboard::KEY_THREE;
-        case AKEYCODE_4:
-            return Keyboard::KEY_FOUR;
-        case AKEYCODE_5:
-            return Keyboard::KEY_FIVE;
-        case AKEYCODE_6:
-            return Keyboard::KEY_SIX;
-        case AKEYCODE_7:
-            return Keyboard::KEY_SEVEN;
-        case AKEYCODE_8:
-            return Keyboard::KEY_EIGHT;
-        case AKEYCODE_9:
-            return Keyboard::KEY_NINE;
-        case AKEYCODE_STAR:
-            return Keyboard::KEY_ASTERISK;
-        case AKEYCODE_POUND:
-            return Keyboard::KEY_NUMBER;
-        case AKEYCODE_DPAD_UP:
-            return Keyboard::KEY_UP_ARROW;
-        case AKEYCODE_DPAD_DOWN:
-            return Keyboard::KEY_DOWN_ARROW;
-        case AKEYCODE_DPAD_LEFT:
-            return Keyboard::KEY_LEFT_ARROW;
-        case AKEYCODE_DPAD_RIGHT:
-            return Keyboard::KEY_RIGHT_ARROW;
-        case AKEYCODE_A:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_A : Keyboard::KEY_A;
-        case AKEYCODE_B:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_B : Keyboard::KEY_B;
-       case AKEYCODE_C:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_C : Keyboard::KEY_C;
-        case AKEYCODE_D:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_D : Keyboard::KEY_D;
-        case AKEYCODE_E:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_E : Keyboard::KEY_E;
-        case AKEYCODE_F:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_F : Keyboard::KEY_F;
-        case AKEYCODE_G:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_G : Keyboard::KEY_G;
-        case AKEYCODE_H:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_H : Keyboard::KEY_H;
-        case AKEYCODE_I:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_I : Keyboard::KEY_I;
-        case AKEYCODE_J:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_J : Keyboard::KEY_J;
-        case AKEYCODE_K:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_K : Keyboard::KEY_K;
-        case AKEYCODE_L:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_L : Keyboard::KEY_L;
-        case AKEYCODE_M:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_M : Keyboard::KEY_M;
-        case AKEYCODE_N:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_N : Keyboard::KEY_N;
-        case AKEYCODE_O:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_O : Keyboard::KEY_O;
-        case AKEYCODE_P:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_P : Keyboard::KEY_P;
-        case AKEYCODE_Q:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_Q : Keyboard::KEY_Q;
-        case AKEYCODE_R:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_R : Keyboard::KEY_R;
-        case AKEYCODE_S:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_S : Keyboard::KEY_S;
-        case AKEYCODE_T:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_T : Keyboard::KEY_T;
-        case AKEYCODE_U:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_U : Keyboard::KEY_U;
-        case AKEYCODE_V:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_V : Keyboard::KEY_V;
-        case AKEYCODE_W:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_W : Keyboard::KEY_W;
-        case AKEYCODE_X:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_X : Keyboard::KEY_X;
-        case AKEYCODE_Y:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_Y : Keyboard::KEY_Y;
-        case AKEYCODE_Z:
-            return (shiftOn) ? Keyboard::KEY_CAPITAL_Y : Keyboard::KEY_Y;
-        case AKEYCODE_COMMA:
-            return Keyboard::KEY_COMMA;
-        case AKEYCODE_PERIOD:
-            return Keyboard::KEY_PERIOD;
-        case AKEYCODE_ALT_LEFT:
-        case AKEYCODE_ALT_RIGHT:
-            return Keyboard::KEY_ALT;
-        case AKEYCODE_SHIFT_LEFT:
-        case AKEYCODE_SHIFT_RIGHT:
-            return Keyboard::KEY_SHIFT;
-        case AKEYCODE_TAB:
-            return Keyboard::KEY_TAB;
-        case AKEYCODE_SPACE:
-            return Keyboard::KEY_SPACE;
-        case AKEYCODE_ENTER:
-            return Keyboard::KEY_RETURN;
-        case AKEYCODE_DEL:
-            return Keyboard::KEY_DELETE;
-        case AKEYCODE_GRAVE:
-            return Keyboard::KEY_GRAVE;
-        case AKEYCODE_MINUS:
-            return Keyboard::KEY_MINUS;
-        case AKEYCODE_EQUALS:
-            return Keyboard::KEY_EQUAL;
-        case AKEYCODE_LEFT_BRACKET:
-            return Keyboard::KEY_LEFT_BRACKET;
-        case AKEYCODE_RIGHT_BRACKET:
-            return Keyboard::KEY_RIGHT_BRACKET;
-        case AKEYCODE_BACKSLASH:
-            return Keyboard::KEY_BACK_SLASH;
-        case AKEYCODE_SEMICOLON:
-            return Keyboard::KEY_SEMICOLON;
-        case AKEYCODE_APOSTROPHE:
-            return Keyboard::KEY_APOSTROPHE;
-        case AKEYCODE_SLASH:
-            return Keyboard::KEY_SLASH;
-        case AKEYCODE_AT:
-            return Keyboard::KEY_AT;
-        case AKEYCODE_PLUS:
-            return Keyboard::KEY_PLUS;
-        case AKEYCODE_PAGE_UP:
-            return Keyboard::KEY_PG_UP;
-        case AKEYCODE_PAGE_DOWN:
-            return Keyboard::KEY_PG_DOWN;
-        case AKEYCODE_MENU:
-            return Keyboard::KEY_MENU;
-        case AKEYCODE_SEARCH:
-            return Keyboard::KEY_SEARCH;
-        default:
-            return Keyboard::KEY_NONE;
-    }
-}
-
-/**
- * Returns the unicode value for the given keycode or zero if the key is not a valid printable character.
- */
-static int getUnicode(int keycode, int metastate)
-{
-    if (keycode == AKEYCODE_DEL)
-        return 0x0008;
-    // TODO: Doesn't support unicode currently.
-    Keyboard::Key key = getKey(keycode, metastate);
-    switch (key)
-    {
-    case Keyboard::KEY_BACKSPACE:
-        return 0x0008;
-    case Keyboard::KEY_TAB:
-        return 0x0009;
-    case Keyboard::KEY_RETURN:
-    case Keyboard::KEY_KP_ENTER:
-        return 0x000A;
-    case Keyboard::KEY_ESCAPE:
-        return 0x001B;
-    case Keyboard::KEY_SPACE:
-    case Keyboard::KEY_EXCLAM:
-    case Keyboard::KEY_QUOTE:
-    case Keyboard::KEY_NUMBER:
-    case Keyboard::KEY_DOLLAR:
-    case Keyboard::KEY_PERCENT:
-    case Keyboard::KEY_CIRCUMFLEX:
-    case Keyboard::KEY_AMPERSAND:
-    case Keyboard::KEY_APOSTROPHE:
-    case Keyboard::KEY_LEFT_PARENTHESIS:
-    case Keyboard::KEY_RIGHT_PARENTHESIS:
-    case Keyboard::KEY_ASTERISK:
-    case Keyboard::KEY_PLUS:
-    case Keyboard::KEY_COMMA:
-    case Keyboard::KEY_MINUS:
-    case Keyboard::KEY_PERIOD:
-    case Keyboard::KEY_SLASH:
-    case Keyboard::KEY_ZERO:
-    case Keyboard::KEY_ONE:
-    case Keyboard::KEY_TWO:
-    case Keyboard::KEY_THREE:
-    case Keyboard::KEY_FOUR:
-    case Keyboard::KEY_FIVE:
-    case Keyboard::KEY_SIX:
-    case Keyboard::KEY_SEVEN:
-    case Keyboard::KEY_EIGHT:
-    case Keyboard::KEY_NINE:
-    case Keyboard::KEY_COLON:
-    case Keyboard::KEY_SEMICOLON:
-    case Keyboard::KEY_LESS_THAN:
-    case Keyboard::KEY_EQUAL:
-    case Keyboard::KEY_GREATER_THAN:
-    case Keyboard::KEY_QUESTION:
-    case Keyboard::KEY_AT:
-    case Keyboard::KEY_CAPITAL_A:
-    case Keyboard::KEY_CAPITAL_B:
-    case Keyboard::KEY_CAPITAL_C:
-    case Keyboard::KEY_CAPITAL_D:
-    case Keyboard::KEY_CAPITAL_E:
-    case Keyboard::KEY_CAPITAL_F:
-    case Keyboard::KEY_CAPITAL_G:
-    case Keyboard::KEY_CAPITAL_H:
-    case Keyboard::KEY_CAPITAL_I:
-    case Keyboard::KEY_CAPITAL_J:
-    case Keyboard::KEY_CAPITAL_K:
-    case Keyboard::KEY_CAPITAL_L:
-    case Keyboard::KEY_CAPITAL_M:
-    case Keyboard::KEY_CAPITAL_N:
-    case Keyboard::KEY_CAPITAL_O:
-    case Keyboard::KEY_CAPITAL_P:
-    case Keyboard::KEY_CAPITAL_Q:
-    case Keyboard::KEY_CAPITAL_R:
-    case Keyboard::KEY_CAPITAL_S:
-    case Keyboard::KEY_CAPITAL_T:
-    case Keyboard::KEY_CAPITAL_U:
-    case Keyboard::KEY_CAPITAL_V:
-    case Keyboard::KEY_CAPITAL_W:
-    case Keyboard::KEY_CAPITAL_X:
-    case Keyboard::KEY_CAPITAL_Y:
-    case Keyboard::KEY_CAPITAL_Z:
-    case Keyboard::KEY_LEFT_BRACKET:
-    case Keyboard::KEY_BACK_SLASH:
-    case Keyboard::KEY_RIGHT_BRACKET:
-    case Keyboard::KEY_UNDERSCORE:
-    case Keyboard::KEY_GRAVE:
-    case Keyboard::KEY_A:
-    case Keyboard::KEY_B:
-    case Keyboard::KEY_C:
-    case Keyboard::KEY_D:
-    case Keyboard::KEY_E:
-    case Keyboard::KEY_F:
-    case Keyboard::KEY_G:
-    case Keyboard::KEY_H:
-    case Keyboard::KEY_I:
-    case Keyboard::KEY_J:
-    case Keyboard::KEY_K:
-    case Keyboard::KEY_L:
-    case Keyboard::KEY_M:
-    case Keyboard::KEY_N:
-    case Keyboard::KEY_O:
-    case Keyboard::KEY_P:
-    case Keyboard::KEY_Q:
-    case Keyboard::KEY_R:
-    case Keyboard::KEY_S:
-    case Keyboard::KEY_T:
-    case Keyboard::KEY_U:
-    case Keyboard::KEY_V:
-    case Keyboard::KEY_W:
-    case Keyboard::KEY_X:
-    case Keyboard::KEY_Y:
-    case Keyboard::KEY_Z:
-    case Keyboard::KEY_LEFT_BRACE:
-    case Keyboard::KEY_BAR:
-    case Keyboard::KEY_RIGHT_BRACE:
-    case Keyboard::KEY_TILDE:
-        return key;
-    default:
-        return 0;
-    }
-}
-
-// Process the next input event.
-static int32_t engine_handle_input(struct android_app* app, AInputEvent* event)
-{
-    if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION)
-    {
-        int32_t action = AMotionEvent_getAction(event);
-        size_t pointerIndex;
-        size_t pointerId;
-        size_t pointerCount;
-        int x;
-        int y;
-        
-        switch (action & AMOTION_EVENT_ACTION_MASK)
-        {
-            case AMOTION_EVENT_ACTION_DOWN:
-                {
-                    pointerId = AMotionEvent_getPointerId(event, 0);
-                    x = AMotionEvent_getX(event, 0);
-                    y = AMotionEvent_getY(event, 0);
-
-                    // Gesture handling
-                    if ( __gestureEventsProcessed.test(Gesture::GESTURE_TAP) ||
-                         __gestureEventsProcessed.test(Gesture::GESTURE_SWIPE) )
-                    {
-                        __pointer0.pressed = true;
-                        __pointer0.time = Game::getInstance()->getAbsoluteTime();
-                        __pointer0.pointerId = pointerId;
-                        __pointer0.x = x;
-                        __pointer0.y = y;
-                    }
-
-                    // Primary pointer down.
-                    gameplay::Platform::touchEventInternal(Touch::TOUCH_PRESS, x, y, pointerId);
-                    __primaryTouchId = pointerId;
-                }
-                break;
-
-            case AMOTION_EVENT_ACTION_UP:
-                {
-                    pointerId = AMotionEvent_getPointerId(event, 0);
-                    x = AMotionEvent_getX(event, 0);
-                    y = AMotionEvent_getY(event, 0);
-                    
-                    // Gestures
-                    bool gestureDetected = false;
-                    if ( __pointer0.pressed &&  __pointer0.pointerId == pointerId)
-                    {
-                        int deltaX = x - __pointer0.x;
-                        int deltaY = y - __pointer0.y;
-
-                        // Test for swipe
-                        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) )
-                        {
-                            int direction = 0;
-                            if ( abs(deltaX) > abs(deltaY) )
-                            {
-                                if (deltaX > 0)
-                                    direction = gameplay::Gesture::SWIPE_DIRECTION_RIGHT;
-                                else if (deltaX < 0)
-                                    direction = gameplay::Gesture::SWIPE_DIRECTION_LEFT;
-                            }
-                            else
-                            {
-                                if (deltaY > 0)
-                                    direction = gameplay::Gesture::SWIPE_DIRECTION_DOWN;
-                                else if (deltaY < 0)
-                                    direction = gameplay::Gesture::SWIPE_DIRECTION_UP;
-                            }
-                            gameplay::Game::getInstance()->gestureSwipeEvent(x, y, direction);
-                            __pointer0.pressed = false;
-                            gestureDetected = true;
-                        }
-                        else if(__gestureEventsProcessed.test(Gesture::GESTURE_TAP) &&
-                               gameplay::Game::getInstance()->getAbsoluteTime() - __pointer0.time < GESTURE_TAP_DURATION_MAX)
-                        {
-                            gameplay::Game::getInstance()->gestureTapEvent(x, y);
-                            __pointer0.pressed = false;
-                            gestureDetected = true;
-                        }
-                    }
-
-                    if (!gestureDetected && (__multiTouch || __primaryTouchId == pointerId) )
-                    {
-                        gameplay::Platform::touchEventInternal(Touch::TOUCH_RELEASE, x, y, pointerId);
-                    }
-                    __primaryTouchId = -1;
-                }
-                break;
-
-            case AMOTION_EVENT_ACTION_POINTER_DOWN:
-                {
-                    pointerId = AMotionEvent_getPointerId(event, 0);
-                    x = AMotionEvent_getX(event, 0);
-                    y = AMotionEvent_getY(event, 0);
-
-                    // Gesture handling
-                    if ( __gestureEventsProcessed.test(Gesture::GESTURE_TAP) ||
-                         __gestureEventsProcessed.test(Gesture::GESTURE_SWIPE) )
-                    {
-                        __pointer1.pressed = true;
-                        __pointer1.time = Game::getInstance()->getAbsoluteTime();
-                        __pointer1.pointerId = pointerId;
-                        __pointer1.x = x;
-                        __pointer1.y = 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);
-                    }
-                }
-                break;
-
-            case AMOTION_EVENT_ACTION_POINTER_UP:
-                {
-                    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);
-
-                    bool gestureDetected = false;
-                    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) &&
-                            gameplay::Game::getInstance()->getAbsoluteTime() - __pointer1.time < GESTURE_SWIPE_DURATION_MAX && 
-                            (abs(deltaX) > GESTURE_SWIPE_DISTANCE_MIN || abs(deltaY) > GESTURE_SWIPE_DISTANCE_MIN) )
-                        {
-                            int direction;
-                            if (deltaX > 0)
-                                direction |= gameplay::Gesture::SWIPE_DIRECTION_RIGHT;
-                            else if (deltaX < 0)
-                                direction |= gameplay::Gesture::SWIPE_DIRECTION_LEFT;
-                            
-                            if (deltaY > 0)
-                                direction |= gameplay::Gesture::SWIPE_DIRECTION_DOWN;
-                            else if (deltaY < 0)
-                                direction |= gameplay::Gesture::SWIPE_DIRECTION_UP;
-
-                            gameplay::Game::getInstance()->gestureSwipeEvent(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::Game::getInstance()->gestureTapEvent(x, y);
-                            __pointer1.pressed = false;
-                            gestureDetected = true;
-                        }
-                    }
-
-                    if (!gestureDetected && (__multiTouch || __primaryTouchId == pointerId) )
-                    {
-                        gameplay::Platform::touchEventInternal(Touch::TOUCH_RELEASE, AMotionEvent_getX(event, pointerIndex), AMotionEvent_getY(event, pointerIndex), pointerId);
-                    }
-                    if (__primaryTouchId == pointerId)
-                        __primaryTouchId = -1;
-                }
-                break;
-
-            case AMOTION_EVENT_ACTION_MOVE:
-                {
-                    // ACTION_MOVE events are batched, unlike the other events.
-                    pointerCount = AMotionEvent_getPointerCount(event);
-                    for (size_t i = 0; i < pointerCount; ++i)
-                    {
-                        pointerId = AMotionEvent_getPointerId(event, i);
-                        if (__multiTouch || __primaryTouchId == pointerId)
-                        {
-                            gameplay::Platform::touchEventInternal(Touch::TOUCH_MOVE, AMotionEvent_getX(event, i), AMotionEvent_getY(event, i), pointerId);
-                        }
-                    }
-                }
-                break;
-        }
-        return 1;
-    } 
-    else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY)
-    {
-        int32_t action = AKeyEvent_getAction(event);
-        int32_t keycode = AKeyEvent_getKeyCode(event);
-        int32_t metastate = AKeyEvent_getMetaState(event); 
-        
-        switch(action)
-        {
-            case AKEY_EVENT_ACTION_DOWN:
-                Game::getInstance()->keyEvent(Keyboard::KEY_PRESS, getKey(keycode, metastate));
-                if (int character = getUnicode(keycode, metastate))
-                    gameplay::Platform::keyEventInternal(Keyboard::KEY_CHAR, character);
-                break;
-                    
-            case AKEY_EVENT_ACTION_UP:
-                gameplay::Platform::keyEventInternal(Keyboard::KEY_RELEASE, getKey(keycode, metastate));
-                break;
-        }
-    }
-    return 0;
-}
-
-// Process the next main command.
-static void engine_handle_cmd(struct android_app* app, int32_t cmd)
-{
-    switch (cmd) 
-    {
-        case APP_CMD_INIT_WINDOW:
-            // The window is being shown, get it ready.
-            if (app->window != NULL)
-            {
-                initEGL();
-                __initialized = true;
-            }
-            break;
-        case APP_CMD_TERM_WINDOW:
-            destroyEGLSurface();
-            __initialized = false;
-            break;
-        case APP_CMD_DESTROY:
-            Game::getInstance()->exit();
-            destroyEGLMain();
-            __initialized = false;
-            break;
-        case APP_CMD_GAINED_FOCUS:
-            // When our app gains focus, we start monitoring the accelerometer.
-            if (__accelerometerSensor != NULL) 
-            {
-                ASensorEventQueue_enableSensor(__sensorEventQueue, __accelerometerSensor);
-                // We'd like to get 60 events per second (in microseconds).
-                ASensorEventQueue_setEventRate(__sensorEventQueue, __accelerometerSensor, (1000L/60)*1000);
-            }
-
-            if (Game::getInstance()->getState() == Game::UNINITIALIZED)
-            {
-                Game::getInstance()->run();
-            }
-            else
-            {
-                Game::getInstance()->resume();
-            }
-            break;
-        case APP_CMD_RESUME:
-            if (__initialized)
-            {
-                Game::getInstance()->resume();
-            }
-            __suspended = false;
-            break;
-        case APP_CMD_PAUSE:
-            Game::getInstance()->pause();
-            __suspended = true;
-            break;
-        case APP_CMD_LOST_FOCUS:
-            // When our app loses focus, we stop monitoring the accelerometer.
-            // This is to avoid consuming battery while not being used.
-            if (__accelerometerSensor != NULL) 
-            {
-                ASensorEventQueue_disableSensor(__sensorEventQueue, __accelerometerSensor);
-            }
-            break;
-    }
-}
-
-Platform::Platform(Game* game)
-    : _game(game)
-{
-}
-
-Platform::~Platform()
-{
-}
-
-Platform* Platform::create(Game* game, void* attachToWindow)
-{
-    Platform* platform = new Platform(game);
-    return platform;
-}
-
-int Platform::enterMessagePump()
-{
-    GP_ASSERT(__state && __state->activity && __state->activity->vm);
-
-    __initialized = false;
-    __suspended = false;
-
-    // Get the android application's activity.
-    ANativeActivity* activity = __state->activity;
-    JavaVM* jvm = __state->activity->vm;
-    JNIEnv* env = NULL;
-    jvm->GetEnv((void **)&env, JNI_VERSION_1_6);
-    jint res = jvm->AttachCurrentThread(&env, NULL);
-    if (res == JNI_ERR)
-    {
-        GP_ERROR("Failed to retrieve JVM environment when entering message pump.");
-        return -1; 
-    }
-    GP_ASSERT(env);
-
-    /* Get external files directory on Android; this will result in a directory where all app files
-     * should be stored, like /mnt/sdcard/android/<package-name>/files/
-     */
-    jboolean isCopy;
-
-    jclass clazz = env->GetObjectClass(activity->clazz);
-    jmethodID methodGetExternalStorage = env->GetMethodID(clazz, "getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;");
-
-    jclass clazzFile = env->FindClass("java/io/File");
-    jmethodID methodGetPath = env->GetMethodID(clazzFile, "getPath", "()Ljava/lang/String;");
-
-    // Now has java.io.File object pointing to directory
-    jobject objectFile  = env->CallObjectMethod(activity->clazz, methodGetExternalStorage, NULL);
-    
-    // Now has String object containing path to directory
-    jstring stringExternalPath = static_cast<jstring>(env->CallObjectMethod(objectFile, methodGetPath));
-    const char* externalPath = env->GetStringUTFChars(stringExternalPath, &isCopy);
-
-    // Set the default path to store the resources.
-    std::string assetsPath(externalPath);
-    if (externalPath[strlen(externalPath)-1] != '/')
-        assetsPath += "/";
-
-    FileSystem::setResourcePath(assetsPath.c_str());    
-
-    // Release string data
-    env->ReleaseStringUTFChars(stringExternalPath, externalPath);
-    jvm->DetachCurrentThread();
-    
-    // Get the asset manager to get the resources from the .apk file.
-    __assetManager = activity->assetManager; 
-    
-    // Set the event call back functions.
-    __state->onAppCmd = engine_handle_cmd;
-    __state->onInputEvent = engine_handle_input;
-    
-    // Prepare to monitor accelerometer.
-    __sensorManager = ASensorManager_getInstance();
-    __accelerometerSensor = ASensorManager_getDefaultSensor(__sensorManager, ASENSOR_TYPE_ACCELEROMETER);
-    __sensorEventQueue = ASensorManager_createEventQueue(__sensorManager, __state->looper, LOOPER_ID_USER, NULL, NULL);
-    
-    // Get the initial time.
-    clock_gettime(CLOCK_REALTIME, &__timespec);
-    __timeStart = timespec2millis(&__timespec);
-    __timeAbsolute = 0L;
-    
-    while (true)
-    {
-        // Read all pending events.
-        int ident;
-        int events;
-        struct android_poll_source* source;
-        
-        while ((ident=ALooper_pollAll(!__suspended ? 0 : -1, NULL, &events, (void**)&source)) >= 0) 
-        {
-            // Process this event.
-            if (source != NULL)
-                source->process(__state, source);
-            
-            // If a sensor has data, process it now.
-            if (ident == LOOPER_ID_USER && __accelerometerSensor != NULL)
-                ASensorEventQueue_getEvents(__sensorEventQueue, &__sensorEvent, 1);
-            
-            if (__state->destroyRequested != 0)
-            {
-                return 0;
-            }
-        }
-        
-        // Idle time (no events left to process) is spent rendering.
-        // We skip rendering when the app is paused.
-        if (__initialized && !__suspended)
-        {
-            _game->frame();
-
-            // Post the new frame to the display.
-            // Note that there are a couple cases where eglSwapBuffers could fail
-            // with an error code that requires a certain level of re-initialization:
-            //
-            // 1) EGL_BAD_NATIVE_WINDOW - Called when the surface we're currently using
-            //    is invalidated. This would require us to destroy our EGL surface,
-            //    close our OpenKODE window, and start again.
-            //
-            // 2) EGL_CONTEXT_LOST - Power management event that led to our EGL context
-            //    being lost. Requires us to re-create and re-initalize our EGL context
-            //    and all OpenGL ES state.
-            //
-            // For now, if we get these, we'll simply exit.
-            int rc = eglSwapBuffers(__eglDisplay, __eglSurface);
-            if (rc != EGL_TRUE)
-            {
-                EGLint error = eglGetError();
-                if (error == EGL_BAD_NATIVE_WINDOW)
-                {
-                    if (__state->window != NULL)
-                    {
-                        destroyEGLSurface();
-                        initEGL();
-                    }
-                    __initialized = true;
-                }
-                else
-                {
-                    perror("eglSwapBuffers");
-                    break;
-                }
-            }
-        }
-            
-        // Display the keyboard.
-        gameplay::displayKeyboard(__state, __displayKeyboard);
-    }
-}
-
-void Platform::signalShutdown() 
-{
-    // nothing to do  
-}
-
-bool Platform::canExit()
-{
-    return true;
-}
-   
-unsigned int Platform::getDisplayWidth()
-{
-    return __width;
-}
-    
-unsigned int Platform::getDisplayHeight()
-{
-    return __height;
-}
-    
-double Platform::getAbsoluteTime()
-{
-    clock_gettime(CLOCK_REALTIME, &__timespec);
-    double now = timespec2millis(&__timespec);
-    __timeAbsolute = now - __timeStart;
-
-    return __timeAbsolute;
-}
-
-void Platform::setAbsoluteTime(double time)
-{
-    __timeAbsolute = time;
-}
-
-bool Platform::isVsync()
-{
-    return __vsync;
-}
-
-void Platform::setVsync(bool enable)
-{
-    eglSwapInterval(__eglDisplay, enable ? 1 : 0);
-    __vsync = enable;
-}
-
-
-void Platform::swapBuffers()
-{
-    if (__eglDisplay && __eglSurface)
-        eglSwapBuffers(__eglDisplay, __eglSurface);
-}
-
-void Platform::sleep(long ms)
-{
-    usleep(ms * 1000);
-}
-
-void Platform::setMultiSampling(bool enabled)
-{
-    if (enabled == __multiSampling)
-    {
-        return;
-    }
-
-    //todo
-
-    __multiSampling = enabled;
-}
-
-bool Platform::isMultiSampling()
-{
-    return __multiSampling;
-}
-
-void Platform::setMultiTouch(bool enabled)
-{
-    __multiTouch = enabled;
-}
-
-bool Platform::isMultiTouch()
-{
-    return __multiTouch;
-}
-
-void Platform::getAccelerometerValues(float* pitch, float* roll)
-{
-    double tx, ty, tz;
-    ASensorEvent event;
-    
-    // By default, android accelerometer values are oriented to the portrait mode.
-    // flipping the x and y to get the desired landscape mode values.
-    switch (__orientationAngle)
-    {
-    case 90:
-        tx = -__sensorEvent.acceleration.y;
-        ty = __sensorEvent.acceleration.x;
-        break;
-    case 180:
-        tx = -__sensorEvent.acceleration.x;
-        ty = -__sensorEvent.acceleration.y;
-        break;
-    case 270:
-        tx = __sensorEvent.acceleration.y;
-        ty = -__sensorEvent.acceleration.x;
-        break;
-    default:
-        tx = __sensorEvent.acceleration.x;
-        ty = __sensorEvent.acceleration.y;
-        break;
-    }
-    tz = __sensorEvent.acceleration.z;
-
-    if (pitch != NULL)
-    {
-        GP_ASSERT(tx * tx + tz * tz);
-        *pitch = -atan(ty / sqrt(tx * tx + tz * tz)) * 180.0f * M_1_PI;
-    }
-    if (roll != NULL)
-    {
-        GP_ASSERT(ty * ty + tz * tz);
-        *roll = -atan(tx / sqrt(ty * ty + tz * tz)) * 180.0f * M_1_PI;
-    }
-}
-
-bool Platform::hasMouse()
-{
-    // not supported
-    return false;
-}
-
-void Platform::setMouseCaptured(bool captured)
-{
-    // not supported
-}
-
-bool Platform::isMouseCaptured()
-{
-    // not supported
-    return false;
-}
-
-void Platform::setCursorVisible(bool visible)
-{
-    // not supported
-}
-
-bool Platform::isCursorVisible()
-{
-    // not supported
-    return false;
-}
-
-void Platform::displayKeyboard(bool display)
-{
-    if (display)
-        __displayKeyboard = true;
-    else
-        __displayKeyboard = false;
-}
-
-void Platform::shutdownInternal()
-{
-    Game::getInstance()->shutdown();
-}
-
-bool Platform::isGestureSupported(Gesture::GestureEvent evt)
-{
-    // Pinch currently not implemented
-    return evt == gameplay::Gesture::GESTURE_SWIPE || evt == gameplay::Gesture::GESTURE_TAP;
-}
-
-void Platform::registerGesture(Gesture::GestureEvent evt)
-{
-    switch(evt)
-    {
-    case Gesture::GESTURE_ANY_SUPPORTED:
-        __gestureEventsProcessed.set();
-        break;
-
-    case Gesture::GESTURE_TAP:
-    case Gesture::GESTURE_SWIPE:
-        __gestureEventsProcessed.set(evt);
-        break;
-
-    default:
-        break;
-    }
-}
-
-void Platform::unregisterGesture(Gesture::GestureEvent evt)
-{
-    switch(evt)
-    {
-    case Gesture::GESTURE_ANY_SUPPORTED:
-        __gestureEventsProcessed.reset();
-        break;
-
-    case Gesture::GESTURE_TAP:
-    case Gesture::GESTURE_SWIPE:
-        __gestureEventsProcessed.set(evt, 0);
-        break;
-
-    default:
-        break;
-    }
-}
-    
-bool Platform::isGestureRegistered(Gesture::GestureEvent evt)
-{
-    return __gestureEventsProcessed.test(evt);
-}
-
-void Platform::pollGamepadState(Gamepad* gamepad)
-{
-}
-
-bool Platform::launchURL(const char *url)
-{
-    if (url == NULL || *url == '\0')
-        return false;
-
-    bool result = true;
-
-    android_app* state = __state;
-    GP_ASSERT(state && state->activity && state->activity->vm);
-    JavaVM* jvm = state->activity->vm;
-    JNIEnv* env = NULL;
-    jvm->GetEnv((void **)&env, JNI_VERSION_1_6);
-    jint r = jvm->AttachCurrentThread(&env, NULL);
-    if (r == JNI_ERR)
-    {
-        GP_ERROR("Failed to retrieve JVM environment to display keyboard.");
-        return false;
-    }
-    GP_ASSERT(env);
-
-    jclass classActivity = env->FindClass("android/app/NativeActivity");
-    jclass classIntent = env->FindClass("android/content/Intent");
-    jclass classUri = env->FindClass("android/net/Uri");
-
-    GP_ASSERT(classActivity && classIntent && classUri);
-
-    // Get static field ID Intent.ACTION_VIEW
-    jfieldID fieldActionView = env->GetStaticFieldID(classIntent, "ACTION_VIEW", "Ljava/lang/String;");
-    GP_ASSERT(fieldActionView);
-
-    // Get string value of Intent.ACTION_VIEW, we'll need that to pass to Intent's constructor later on
-    jstring paramActionView = (jstring)env->GetStaticObjectField(classIntent, fieldActionView);
-    GP_ASSERT(paramActionView);
-
-    // Get method ID Uri.parse, will be needed to parse the url given into Uri object
-    jmethodID methodUriParse = env->GetStaticMethodID(classUri, "parse","(Ljava/lang/String;)Landroid/net/Uri;");
-    GP_ASSERT(methodUriParse);
-
-    // Get method ID Activity.startActivity, so we can start the appropriate activity for the View action of our Uri
-    jmethodID methodActivityStartActivity = env->GetMethodID(classActivity, "startActivity","(Landroid/content/Intent;)V");
-    GP_ASSERT(methodActivityStartActivity);
-
-    // Get method ID Intent constructor, the one that takes action and uri (String;Uri)
-    jmethodID methodIntentInit = env->GetMethodID(classIntent, "<init>","(Ljava/lang/String;Landroid/net/Uri;)V");
-    GP_ASSERT(methodIntentInit);
-
-    // Convert our url to Java's string and parse it to Uri
-    jstring paramUrlString = env->NewStringUTF(url);
-    jobject paramUri = env->CallStaticObjectMethod(classUri, methodUriParse, paramUrlString);
-    GP_ASSERT(paramUri);
-
-    // Create Intent with Intent.ACTION_VIEW and parsed Uri arguments
-    jobject paramIntent = env->NewObject(classIntent, methodIntentInit, paramActionView, paramUri);
-    GP_ASSERT(paramIntent);
-
-    // Launch NativeActivity.startActivity with our intent to view the url! state->activity->clazz holds
-    // our NativeActivity object
-    env->CallVoidMethod(state->activity->clazz, methodActivityStartActivity, paramIntent);
-
-    /* startActivity may throw a ActivitNotFoundException if, well, activity is not found.
-       Example: http://<url> is passed to the intent but there is no browser installed in the system
-       we need to handle it. */
-    jobject exception = env->ExceptionOccurred();
-
-    // We're not lucky here
-    if (exception)
-    {
-        // Print out the exception data to logcat
-        env->ExceptionDescribe();
-
-        // Exception needs to be cleared
-        env->ExceptionClear();
-
-        // Launching the url failed
-        result = false;
-    }
-
-    // See you Space Cowboy
-    jvm->DetachCurrentThread();
-    return result;
-}
-
-}
-
-#endif
+#ifdef __ANDROID__
+
+#include "Base.h"
+#include "Platform.h"
+#include "FileSystem.h"
+#include "Game.h"
+#include "Form.h"
+#include "ScriptController.h"
+#include <unistd.h>
+#include <android/sensor.h>
+#include <android_native_app_glue.h>
+#include <android/log.h>
+
+// Externally referenced global variables.
+struct android_app* __state;
+AAssetManager* __assetManager;
+
+static bool __initialized;
+static bool __suspended;
+static EGLDisplay __eglDisplay = EGL_NO_DISPLAY;
+static EGLContext __eglContext = EGL_NO_CONTEXT;
+static EGLSurface __eglSurface = EGL_NO_SURFACE;
+static EGLConfig __eglConfig = 0;
+static int __width;
+static int __height;
+static struct timespec __timespec;
+static double __timeStart;
+static double __timeAbsolute;
+static bool __vsync = WINDOW_VSYNC;
+static ASensorManager* __sensorManager;
+static ASensorEventQueue* __sensorEventQueue;
+static ASensorEvent __sensorEvent;
+static const ASensor* __accelerometerSensor;
+static int __orientationAngle = 90;
+static bool __multiSampling = false;
+static bool __multiTouch = false;
+static int __primaryTouchId = -1;
+static bool __displayKeyboard = false;
+
+// OpenGL VAO functions.
+static const char* __glExtensions;
+PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray = NULL;
+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;
+
+struct TouchPointerData
+{
+    size_t pointerId;
+    bool pressed;
+    double time;
+    int x;
+    int y;
+};
+
+TouchPointerData __pointer0;
+TouchPointerData __pointer1;
+
+namespace gameplay
+{
+
+static double timespec2millis(struct timespec *a)
+{
+    GP_ASSERT(a);
+    return (1000.0 * a->tv_sec) + (0.000001 * a->tv_nsec);
+}
+
+extern void print(const char* format, ...)
+{
+    GP_ASSERT(format);
+    va_list argptr;
+    va_start(argptr, format);
+    __android_log_vprint(ANDROID_LOG_INFO, "gameplay-native-activity", format, argptr);
+    va_end(argptr);
+}
+
+static EGLenum checkErrorEGL(const char* msg)
+{
+    GP_ASSERT(msg);
+    static const char* errmsg[] =
+    {
+        "EGL function succeeded",
+        "EGL is not initialized, or could not be initialized, for the specified display",
+        "EGL cannot access a requested resource",
+        "EGL failed to allocate resources for the requested operation",
+        "EGL fail to access an unrecognized attribute or attribute value was passed in an attribute list",
+        "EGLConfig argument does not name a valid EGLConfig",
+        "EGLContext argument does not name a valid EGLContext",
+        "EGL current surface of the calling thread is no longer valid",
+        "EGLDisplay argument does not name a valid EGLDisplay",
+        "EGL arguments are inconsistent",
+        "EGLNativePixmapType argument does not refer to a valid native pixmap",
+        "EGLNativeWindowType argument does not refer to a valid native window",
+        "EGL one or more argument values are invalid",
+        "EGLSurface argument does not name a valid surface configured for rendering",
+        "EGL power management event has occurred",
+    };
+    EGLenum error = eglGetError();
+    print("%s: %s.", msg, errmsg[error - EGL_SUCCESS]);
+    return error;
+}
+
+static int getRotation()
+{
+    jint rotation;
+
+    // Get the android application's activity.
+    ANativeActivity* activity = __state->activity;
+    JavaVM* jvm = __state->activity->vm;
+    JNIEnv* env = NULL;
+    jvm->GetEnv((void **)&env, JNI_VERSION_1_6);
+    jint res = jvm->AttachCurrentThread(&env, NULL);
+    if (res == JNI_ERR)
+    {
+        GP_ERROR("Failed to retrieve JVM environment when entering message pump.");
+        return -1; 
+    }
+    GP_ASSERT(env);
+
+    jclass clsContext = env->FindClass("android/content/Context");
+    GP_ASSERT(clsContext != NULL);
+    jmethodID getSystemService = env->GetMethodID(clsContext, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
+    GP_ASSERT(getSystemService != NULL);
+    jfieldID WINDOW_SERVICE_ID = env->GetStaticFieldID(clsContext, "WINDOW_SERVICE", "Ljava/lang/String;");
+    GP_ASSERT(WINDOW_SERVICE_ID != NULL);
+    jstring WINDOW_SERVICE = (jstring) env->GetStaticObjectField(clsContext, WINDOW_SERVICE_ID);
+    GP_ASSERT(WINDOW_SERVICE != NULL);
+    jobject windowManager = env->CallObjectMethod(activity->clazz, getSystemService, WINDOW_SERVICE);
+    GP_ASSERT(windowManager != NULL);
+    jclass clsWindowManager = env->FindClass("android/view/WindowManager");
+    GP_ASSERT(clsWindowManager != NULL);
+    jmethodID getDefaultDisplay = env->GetMethodID(clsWindowManager, "getDefaultDisplay", "()Landroid/view/Display;");
+    GP_ASSERT(getDefaultDisplay != NULL);
+    jobject defaultDisplay = env->CallObjectMethod(windowManager, getDefaultDisplay);
+    GP_ASSERT(defaultDisplay != NULL);
+    jclass clsDisplay = env->FindClass("android/view/Display");
+    GP_ASSERT(clsDisplay != NULL);
+    jmethodID getRotation = env->GetMethodID(clsDisplay, "getRotation", "()I");
+    GP_ASSERT(getRotation != NULL)
+    rotation =  env->CallIntMethod(defaultDisplay, getRotation);
+
+    return rotation;
+}
+
+
+// Initialized EGL resources.
+static bool initEGL()
+{
+    int samples = 0;
+    Properties* config = Game::getInstance()->getConfig()->getNamespace("window", true);
+    if (config)
+    {
+        samples = std::max(config->getInt("samples"), 0);
+    }
+
+    // Hard-coded to 32-bit/OpenGL ES 2.0.
+    // NOTE: EGL_SAMPLE_BUFFERS, EGL_SAMPLES and EGL_DEPTH_SIZE MUST remain at the beginning of the attribute list
+    // since they are expected to be at indices 0-5 in config fallback code later.
+    // EGL_DEPTH_SIZE is also expected to
+    EGLint eglConfigAttrs[] =
+    {
+        EGL_SAMPLE_BUFFERS,     samples > 0 ? 1 : 0,
+        EGL_SAMPLES,            samples,
+        EGL_DEPTH_SIZE,         24,
+        EGL_RED_SIZE,           8,
+        EGL_GREEN_SIZE,         8,
+        EGL_BLUE_SIZE,          8,
+        EGL_ALPHA_SIZE,         8,
+        EGL_STENCIL_SIZE,       8,
+        EGL_SURFACE_TYPE,       EGL_WINDOW_BIT,
+        EGL_RENDERABLE_TYPE,    EGL_OPENGL_ES2_BIT,
+        EGL_NONE
+    };
+    __multiSampling = samples > 0;
+    
+    EGLint eglConfigCount;
+    const EGLint eglContextAttrs[] =
+    {
+        EGL_CONTEXT_CLIENT_VERSION,    2,
+        EGL_NONE
+    };
+
+    const EGLint eglSurfaceAttrs[] =
+    {
+        EGL_RENDER_BUFFER,    EGL_BACK_BUFFER,
+        EGL_NONE
+    };
+
+    if (__eglDisplay == EGL_NO_DISPLAY && __eglContext == EGL_NO_CONTEXT)
+    {
+        // Get the EGL display and initialize.
+        __eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+        if (__eglDisplay == EGL_NO_DISPLAY)
+        {
+            checkErrorEGL("eglGetDisplay");
+            goto error;
+        }
+
+        if (eglInitialize(__eglDisplay, NULL, NULL) != EGL_TRUE)
+        {
+            checkErrorEGL("eglInitialize");
+            goto error;
+        }
+
+        // Try both 24 and 16-bit depth sizes since some hardware (i.e. Tegra) does not support 24-bit depth
+        bool validConfig = false;
+        EGLint depthSizes[] = { 24, 16 };
+        for (unsigned int i = 0; i < 2; ++i)
+        {
+            eglConfigAttrs[1] = samples > 0 ? 1 : 0;
+            eglConfigAttrs[3] = samples;
+            eglConfigAttrs[5] = depthSizes[i];
+
+            if (eglChooseConfig(__eglDisplay, eglConfigAttrs, &__eglConfig, 1, &eglConfigCount) == EGL_TRUE && eglConfigCount > 0)
+            {
+                validConfig = true;
+                break;
+            }
+
+            if (samples)
+            {
+                // Try lowering the MSAA sample size until we find a supported config
+                int sampleCount = samples;
+                while (sampleCount)
+                {
+                    GP_WARN("No EGL config found for depth_size=%d and samples=%d. Trying samples=%d instead.", depthSizes[i], sampleCount, sampleCount / 2);
+                    sampleCount /= 2;
+                    eglConfigAttrs[1] = sampleCount > 0 ? 1 : 0;
+                    eglConfigAttrs[3] = sampleCount;
+                    if (eglChooseConfig(__eglDisplay, eglConfigAttrs, &__eglConfig, 1, &eglConfigCount) == EGL_TRUE && eglConfigCount > 0)
+                    {
+                        validConfig = true;
+                        break;
+                    }
+                }
+
+                __multiSampling = sampleCount > 0;
+
+                if (validConfig)
+                    break;
+            }
+            else
+            {
+                GP_WARN("No EGL config found for depth_size=%d.", depthSizes[i]);
+            }
+        }
+
+        if (!validConfig)
+        {
+            checkErrorEGL("eglChooseConfig");
+            goto error;
+        }
+
+        __eglContext = eglCreateContext(__eglDisplay, __eglConfig, EGL_NO_CONTEXT, eglContextAttrs);
+        if (__eglContext == EGL_NO_CONTEXT)
+        {
+            checkErrorEGL("eglCreateContext");
+            goto error;
+        }
+    }
+    
+    // EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is
+    // guaranteed to be accepted by ANativeWindow_setBuffersGeometry().
+    // As soon as we picked a EGLConfig, we can safely reconfigure the
+    // ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID.
+    EGLint format;
+    eglGetConfigAttrib(__eglDisplay, __eglConfig, EGL_NATIVE_VISUAL_ID, &format);
+    ANativeWindow_setBuffersGeometry(__state->window, 0, 0, format);
+    
+    __eglSurface = eglCreateWindowSurface(__eglDisplay, __eglConfig, __state->window, eglSurfaceAttrs);
+    if (__eglSurface == EGL_NO_SURFACE)
+    {
+        checkErrorEGL("eglCreateWindowSurface");
+        goto error;
+    }
+    
+    if (eglMakeCurrent(__eglDisplay, __eglSurface, __eglSurface, __eglContext) != EGL_TRUE)
+    {
+        checkErrorEGL("eglMakeCurrent");
+        goto error;
+    }
+    
+    eglQuerySurface(__eglDisplay, __eglSurface, EGL_WIDTH, &__width);
+    eglQuerySurface(__eglDisplay, __eglSurface, EGL_HEIGHT, &__height);
+
+    __orientationAngle = getRotation() * 90;
+    
+    // Set vsync.
+    eglSwapInterval(__eglDisplay, WINDOW_VSYNC ? 1 : 0);
+    
+    // Initialize OpenGL ES extensions.
+    __glExtensions = (const char*)glGetString(GL_EXTENSIONS);
+    
+    if (strstr(__glExtensions, "GL_OES_vertex_array_object") || strstr(__glExtensions, "GL_ARB_vertex_array_object"))
+    {
+        // Disable VAO extension for now.
+        glBindVertexArray = (PFNGLBINDVERTEXARRAYOESPROC)eglGetProcAddress("glBindVertexArrayOES");
+        glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSOESPROC)eglGetProcAddress("glDeleteVertexArrays");
+        glGenVertexArrays = (PFNGLGENVERTEXARRAYSOESPROC)eglGetProcAddress("glGenVertexArraysOES");
+        glIsVertexArray = (PFNGLISVERTEXARRAYOESPROC)eglGetProcAddress("glIsVertexArrayOES");
+    }
+    
+    return true;
+    
+error:
+    return false;
+}
+
+static void destroyEGLSurface()
+{
+    if (__eglDisplay != EGL_NO_DISPLAY)
+    {
+        eglMakeCurrent(__eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+    }
+
+    if (__eglSurface != EGL_NO_SURFACE)
+    {
+        eglDestroySurface(__eglDisplay, __eglSurface);
+        __eglSurface = EGL_NO_SURFACE;
+    }
+}
+
+static void destroyEGLMain()
+{
+    destroyEGLSurface();
+
+    if (__eglContext != EGL_NO_CONTEXT)
+    {
+        eglDestroyContext(__eglDisplay, __eglContext);
+        __eglContext = EGL_NO_CONTEXT;
+    }
+
+    if (__eglDisplay != EGL_NO_DISPLAY)
+    {
+        eglTerminate(__eglDisplay);
+        __eglDisplay = EGL_NO_DISPLAY;
+    }
+}
+
+// Display the android virtual keyboard.
+static void displayKeyboard(android_app* state, bool show)
+{ 
+    // The following functions is supposed to show / hide functins from a native activity.. but currently do not work. 
+    // ANativeActivity_showSoftInput(state->activity, ANATIVEACTIVITY_SHOW_SOFT_INPUT_IMPLICIT);
+    // ANativeActivity_hideSoftInput(state->activity, ANATIVEACTIVITY_HIDE_SOFT_INPUT_IMPLICIT_ONLY);
+    
+    GP_ASSERT(state && state->activity && state->activity->vm);
+
+    // Show or hide the keyboard by calling the appropriate Java method through JNI instead.
+    jint flags = 0;
+    JavaVM* jvm = state->activity->vm;
+    JNIEnv* env = NULL;
+    jvm->GetEnv((void **)&env, JNI_VERSION_1_6);
+    jint result = jvm->AttachCurrentThread(&env, NULL);
+    if (result == JNI_ERR)
+    {
+        GP_ERROR("Failed to retrieve JVM environment to display keyboard.");
+        return; 
+    }
+    GP_ASSERT(env);
+
+    // Retrieves NativeActivity. 
+    jobject lNativeActivity = state->activity->clazz;
+    jclass ClassNativeActivity = env->GetObjectClass(lNativeActivity);
+
+    // Retrieves Context.INPUT_METHOD_SERVICE.
+    jclass ClassContext = env->FindClass("android/content/Context");
+    jfieldID FieldINPUT_METHOD_SERVICE = env->GetStaticFieldID(ClassContext, "INPUT_METHOD_SERVICE", "Ljava/lang/String;");
+    jobject INPUT_METHOD_SERVICE = env->GetStaticObjectField(ClassContext, FieldINPUT_METHOD_SERVICE);
+    
+    // Runs getSystemService(Context.INPUT_METHOD_SERVICE).
+    jclass ClassInputMethodManager = env->FindClass("android/view/inputmethod/InputMethodManager");
+    jmethodID MethodGetSystemService = env->GetMethodID(ClassNativeActivity, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
+    jobject lInputMethodManager = env->CallObjectMethod(lNativeActivity, MethodGetSystemService, INPUT_METHOD_SERVICE);
+    
+    // Runs getWindow().getDecorView().
+    jmethodID MethodGetWindow = env->GetMethodID(ClassNativeActivity, "getWindow", "()Landroid/view/Window;");
+    jobject lWindow = env->CallObjectMethod(lNativeActivity, MethodGetWindow);
+    jclass ClassWindow = env->FindClass("android/view/Window");
+    jmethodID MethodGetDecorView = env->GetMethodID(ClassWindow, "getDecorView", "()Landroid/view/View;");
+    jobject lDecorView = env->CallObjectMethod(lWindow, MethodGetDecorView);
+    if (show)
+    {
+        // Runs lInputMethodManager.showSoftInput(...).
+        jmethodID MethodShowSoftInput = env->GetMethodID( ClassInputMethodManager, "showSoftInput", "(Landroid/view/View;I)Z");
+        jboolean result = env->CallBooleanMethod(lInputMethodManager, MethodShowSoftInput, lDecorView, flags); 
+    } 
+    else 
+    { 
+        // Runs lWindow.getViewToken() 
+        jclass ClassView = env->FindClass("android/view/View");
+        jmethodID MethodGetWindowToken = env->GetMethodID(ClassView, "getWindowToken", "()Landroid/os/IBinder;");
+        jobject lBinder = env->CallObjectMethod(lDecorView, MethodGetWindowToken); 
+        
+        // lInputMethodManager.hideSoftInput(...). 
+        jmethodID MethodHideSoftInput = env->GetMethodID(ClassInputMethodManager, "hideSoftInputFromWindow", "(Landroid/os/IBinder;I)Z"); 
+        jboolean lRes = env->CallBooleanMethod( lInputMethodManager, MethodHideSoftInput, lBinder, flags); 
+    }
+    
+    // Finished with the JVM.
+    jvm->DetachCurrentThread(); 
+}
+
+// Gets the Keyboard::Key enumeration constant that corresponds to the given Android key code.
+static Keyboard::Key getKey(int keycode, int metastate)
+{
+    bool shiftOn = (metastate == AMETA_SHIFT_ON);
+    
+    switch(keycode)
+    {
+        case AKEYCODE_HOME:
+            return Keyboard::KEY_HOME;
+        case AKEYCODE_0:
+            return Keyboard::KEY_ZERO;
+        case AKEYCODE_1:
+            return Keyboard::KEY_ONE;
+        case AKEYCODE_2:
+            return Keyboard::KEY_TWO;
+        case AKEYCODE_3:
+            return Keyboard::KEY_THREE;
+        case AKEYCODE_4:
+            return Keyboard::KEY_FOUR;
+        case AKEYCODE_5:
+            return Keyboard::KEY_FIVE;
+        case AKEYCODE_6:
+            return Keyboard::KEY_SIX;
+        case AKEYCODE_7:
+            return Keyboard::KEY_SEVEN;
+        case AKEYCODE_8:
+            return Keyboard::KEY_EIGHT;
+        case AKEYCODE_9:
+            return Keyboard::KEY_NINE;
+        case AKEYCODE_STAR:
+            return Keyboard::KEY_ASTERISK;
+        case AKEYCODE_POUND:
+            return Keyboard::KEY_NUMBER;
+        case AKEYCODE_DPAD_UP:
+            return Keyboard::KEY_UP_ARROW;
+        case AKEYCODE_DPAD_DOWN:
+            return Keyboard::KEY_DOWN_ARROW;
+        case AKEYCODE_DPAD_LEFT:
+            return Keyboard::KEY_LEFT_ARROW;
+        case AKEYCODE_DPAD_RIGHT:
+            return Keyboard::KEY_RIGHT_ARROW;
+        case AKEYCODE_A:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_A : Keyboard::KEY_A;
+        case AKEYCODE_B:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_B : Keyboard::KEY_B;
+       case AKEYCODE_C:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_C : Keyboard::KEY_C;
+        case AKEYCODE_D:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_D : Keyboard::KEY_D;
+        case AKEYCODE_E:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_E : Keyboard::KEY_E;
+        case AKEYCODE_F:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_F : Keyboard::KEY_F;
+        case AKEYCODE_G:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_G : Keyboard::KEY_G;
+        case AKEYCODE_H:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_H : Keyboard::KEY_H;
+        case AKEYCODE_I:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_I : Keyboard::KEY_I;
+        case AKEYCODE_J:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_J : Keyboard::KEY_J;
+        case AKEYCODE_K:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_K : Keyboard::KEY_K;
+        case AKEYCODE_L:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_L : Keyboard::KEY_L;
+        case AKEYCODE_M:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_M : Keyboard::KEY_M;
+        case AKEYCODE_N:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_N : Keyboard::KEY_N;
+        case AKEYCODE_O:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_O : Keyboard::KEY_O;
+        case AKEYCODE_P:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_P : Keyboard::KEY_P;
+        case AKEYCODE_Q:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_Q : Keyboard::KEY_Q;
+        case AKEYCODE_R:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_R : Keyboard::KEY_R;
+        case AKEYCODE_S:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_S : Keyboard::KEY_S;
+        case AKEYCODE_T:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_T : Keyboard::KEY_T;
+        case AKEYCODE_U:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_U : Keyboard::KEY_U;
+        case AKEYCODE_V:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_V : Keyboard::KEY_V;
+        case AKEYCODE_W:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_W : Keyboard::KEY_W;
+        case AKEYCODE_X:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_X : Keyboard::KEY_X;
+        case AKEYCODE_Y:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_Y : Keyboard::KEY_Y;
+        case AKEYCODE_Z:
+            return (shiftOn) ? Keyboard::KEY_CAPITAL_Y : Keyboard::KEY_Y;
+        case AKEYCODE_COMMA:
+            return Keyboard::KEY_COMMA;
+        case AKEYCODE_PERIOD:
+            return Keyboard::KEY_PERIOD;
+        case AKEYCODE_ALT_LEFT:
+        case AKEYCODE_ALT_RIGHT:
+            return Keyboard::KEY_ALT;
+        case AKEYCODE_SHIFT_LEFT:
+        case AKEYCODE_SHIFT_RIGHT:
+            return Keyboard::KEY_SHIFT;
+        case AKEYCODE_TAB:
+            return Keyboard::KEY_TAB;
+        case AKEYCODE_SPACE:
+            return Keyboard::KEY_SPACE;
+        case AKEYCODE_ENTER:
+            return Keyboard::KEY_RETURN;
+        case AKEYCODE_DEL:
+            return Keyboard::KEY_DELETE;
+        case AKEYCODE_GRAVE:
+            return Keyboard::KEY_GRAVE;
+        case AKEYCODE_MINUS:
+            return Keyboard::KEY_MINUS;
+        case AKEYCODE_EQUALS:
+            return Keyboard::KEY_EQUAL;
+        case AKEYCODE_LEFT_BRACKET:
+            return Keyboard::KEY_LEFT_BRACKET;
+        case AKEYCODE_RIGHT_BRACKET:
+            return Keyboard::KEY_RIGHT_BRACKET;
+        case AKEYCODE_BACKSLASH:
+            return Keyboard::KEY_BACK_SLASH;
+        case AKEYCODE_SEMICOLON:
+            return Keyboard::KEY_SEMICOLON;
+        case AKEYCODE_APOSTROPHE:
+            return Keyboard::KEY_APOSTROPHE;
+        case AKEYCODE_SLASH:
+            return Keyboard::KEY_SLASH;
+        case AKEYCODE_AT:
+            return Keyboard::KEY_AT;
+        case AKEYCODE_PLUS:
+            return Keyboard::KEY_PLUS;
+        case AKEYCODE_PAGE_UP:
+            return Keyboard::KEY_PG_UP;
+        case AKEYCODE_PAGE_DOWN:
+            return Keyboard::KEY_PG_DOWN;
+        case AKEYCODE_MENU:
+            return Keyboard::KEY_MENU;
+        case AKEYCODE_SEARCH:
+            return Keyboard::KEY_SEARCH;
+        default:
+            return Keyboard::KEY_NONE;
+    }
+}
+
+/**
+ * Returns the unicode value for the given keycode or zero if the key is not a valid printable character.
+ */
+static int getUnicode(int keycode, int metastate)
+{
+    if (keycode == AKEYCODE_DEL)
+        return 0x0008;
+    // TODO: Doesn't support unicode currently.
+    Keyboard::Key key = getKey(keycode, metastate);
+    switch (key)
+    {
+    case Keyboard::KEY_BACKSPACE:
+        return 0x0008;
+    case Keyboard::KEY_TAB:
+        return 0x0009;
+    case Keyboard::KEY_RETURN:
+    case Keyboard::KEY_KP_ENTER:
+        return 0x000A;
+    case Keyboard::KEY_ESCAPE:
+        return 0x001B;
+    case Keyboard::KEY_SPACE:
+    case Keyboard::KEY_EXCLAM:
+    case Keyboard::KEY_QUOTE:
+    case Keyboard::KEY_NUMBER:
+    case Keyboard::KEY_DOLLAR:
+    case Keyboard::KEY_PERCENT:
+    case Keyboard::KEY_CIRCUMFLEX:
+    case Keyboard::KEY_AMPERSAND:
+    case Keyboard::KEY_APOSTROPHE:
+    case Keyboard::KEY_LEFT_PARENTHESIS:
+    case Keyboard::KEY_RIGHT_PARENTHESIS:
+    case Keyboard::KEY_ASTERISK:
+    case Keyboard::KEY_PLUS:
+    case Keyboard::KEY_COMMA:
+    case Keyboard::KEY_MINUS:
+    case Keyboard::KEY_PERIOD:
+    case Keyboard::KEY_SLASH:
+    case Keyboard::KEY_ZERO:
+    case Keyboard::KEY_ONE:
+    case Keyboard::KEY_TWO:
+    case Keyboard::KEY_THREE:
+    case Keyboard::KEY_FOUR:
+    case Keyboard::KEY_FIVE:
+    case Keyboard::KEY_SIX:
+    case Keyboard::KEY_SEVEN:
+    case Keyboard::KEY_EIGHT:
+    case Keyboard::KEY_NINE:
+    case Keyboard::KEY_COLON:
+    case Keyboard::KEY_SEMICOLON:
+    case Keyboard::KEY_LESS_THAN:
+    case Keyboard::KEY_EQUAL:
+    case Keyboard::KEY_GREATER_THAN:
+    case Keyboard::KEY_QUESTION:
+    case Keyboard::KEY_AT:
+    case Keyboard::KEY_CAPITAL_A:
+    case Keyboard::KEY_CAPITAL_B:
+    case Keyboard::KEY_CAPITAL_C:
+    case Keyboard::KEY_CAPITAL_D:
+    case Keyboard::KEY_CAPITAL_E:
+    case Keyboard::KEY_CAPITAL_F:
+    case Keyboard::KEY_CAPITAL_G:
+    case Keyboard::KEY_CAPITAL_H:
+    case Keyboard::KEY_CAPITAL_I:
+    case Keyboard::KEY_CAPITAL_J:
+    case Keyboard::KEY_CAPITAL_K:
+    case Keyboard::KEY_CAPITAL_L:
+    case Keyboard::KEY_CAPITAL_M:
+    case Keyboard::KEY_CAPITAL_N:
+    case Keyboard::KEY_CAPITAL_O:
+    case Keyboard::KEY_CAPITAL_P:
+    case Keyboard::KEY_CAPITAL_Q:
+    case Keyboard::KEY_CAPITAL_R:
+    case Keyboard::KEY_CAPITAL_S:
+    case Keyboard::KEY_CAPITAL_T:
+    case Keyboard::KEY_CAPITAL_U:
+    case Keyboard::KEY_CAPITAL_V:
+    case Keyboard::KEY_CAPITAL_W:
+    case Keyboard::KEY_CAPITAL_X:
+    case Keyboard::KEY_CAPITAL_Y:
+    case Keyboard::KEY_CAPITAL_Z:
+    case Keyboard::KEY_LEFT_BRACKET:
+    case Keyboard::KEY_BACK_SLASH:
+    case Keyboard::KEY_RIGHT_BRACKET:
+    case Keyboard::KEY_UNDERSCORE:
+    case Keyboard::KEY_GRAVE:
+    case Keyboard::KEY_A:
+    case Keyboard::KEY_B:
+    case Keyboard::KEY_C:
+    case Keyboard::KEY_D:
+    case Keyboard::KEY_E:
+    case Keyboard::KEY_F:
+    case Keyboard::KEY_G:
+    case Keyboard::KEY_H:
+    case Keyboard::KEY_I:
+    case Keyboard::KEY_J:
+    case Keyboard::KEY_K:
+    case Keyboard::KEY_L:
+    case Keyboard::KEY_M:
+    case Keyboard::KEY_N:
+    case Keyboard::KEY_O:
+    case Keyboard::KEY_P:
+    case Keyboard::KEY_Q:
+    case Keyboard::KEY_R:
+    case Keyboard::KEY_S:
+    case Keyboard::KEY_T:
+    case Keyboard::KEY_U:
+    case Keyboard::KEY_V:
+    case Keyboard::KEY_W:
+    case Keyboard::KEY_X:
+    case Keyboard::KEY_Y:
+    case Keyboard::KEY_Z:
+    case Keyboard::KEY_LEFT_BRACE:
+    case Keyboard::KEY_BAR:
+    case Keyboard::KEY_RIGHT_BRACE:
+    case Keyboard::KEY_TILDE:
+        return key;
+    default:
+        return 0;
+    }
+}
+
+// Process the next input event.
+static int32_t engine_handle_input(struct android_app* app, AInputEvent* event)
+{
+    if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION)
+    {
+        int32_t action = AMotionEvent_getAction(event);
+        size_t pointerIndex;
+        size_t pointerId;
+        size_t pointerCount;
+        int x;
+        int y;
+        
+        switch (action & AMOTION_EVENT_ACTION_MASK)
+        {
+            case AMOTION_EVENT_ACTION_DOWN:
+                {
+                    pointerId = AMotionEvent_getPointerId(event, 0);
+                    x = AMotionEvent_getX(event, 0);
+                    y = AMotionEvent_getY(event, 0);
+
+                    // Gesture handling
+                    if ( __gestureEventsProcessed.test(Gesture::GESTURE_TAP) ||
+                         __gestureEventsProcessed.test(Gesture::GESTURE_SWIPE) )
+                    {
+                        __pointer0.pressed = true;
+                        __pointer0.time = Game::getInstance()->getAbsoluteTime();
+                        __pointer0.pointerId = pointerId;
+                        __pointer0.x = x;
+                        __pointer0.y = y;
+                    }
+
+                    // Primary pointer down.
+                    gameplay::Platform::touchEventInternal(Touch::TOUCH_PRESS, x, y, pointerId);
+                    __primaryTouchId = pointerId;
+                }
+                break;
+
+            case AMOTION_EVENT_ACTION_UP:
+                {
+                    pointerId = AMotionEvent_getPointerId(event, 0);
+                    x = AMotionEvent_getX(event, 0);
+                    y = AMotionEvent_getY(event, 0);
+                    
+                    // Gestures
+                    bool gestureDetected = false;
+                    if ( __pointer0.pressed &&  __pointer0.pointerId == pointerId)
+                    {
+                        int deltaX = x - __pointer0.x;
+                        int deltaY = y - __pointer0.y;
+
+                        // Test for swipe
+                        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) )
+                        {
+                            int direction = 0;
+                            if ( abs(deltaX) > abs(deltaY) )
+                            {
+                                if (deltaX > 0)
+                                    direction = gameplay::Gesture::SWIPE_DIRECTION_RIGHT;
+                                else if (deltaX < 0)
+                                    direction = gameplay::Gesture::SWIPE_DIRECTION_LEFT;
+                            }
+                            else
+                            {
+                                if (deltaY > 0)
+                                    direction = gameplay::Gesture::SWIPE_DIRECTION_DOWN;
+                                else if (deltaY < 0)
+                                    direction = gameplay::Gesture::SWIPE_DIRECTION_UP;
+                            }
+                            gameplay::Game::getInstance()->gestureSwipeEvent(x, y, direction);
+                            __pointer0.pressed = false;
+                            gestureDetected = true;
+                        }
+                        else if(__gestureEventsProcessed.test(Gesture::GESTURE_TAP) &&
+                               gameplay::Game::getInstance()->getAbsoluteTime() - __pointer0.time < GESTURE_TAP_DURATION_MAX)
+                        {
+                            gameplay::Game::getInstance()->gestureTapEvent(x, y);
+                            __pointer0.pressed = false;
+                            gestureDetected = true;
+                        }
+                    }
+
+                    if (!gestureDetected && (__multiTouch || __primaryTouchId == pointerId) )
+                    {
+                        gameplay::Platform::touchEventInternal(Touch::TOUCH_RELEASE, x, y, pointerId);
+                    }
+                    __primaryTouchId = -1;
+                }
+                break;
+
+            case AMOTION_EVENT_ACTION_POINTER_DOWN:
+                {
+                    pointerId = AMotionEvent_getPointerId(event, 0);
+                    x = AMotionEvent_getX(event, 0);
+                    y = AMotionEvent_getY(event, 0);
+
+                    // Gesture handling
+                    if ( __gestureEventsProcessed.test(Gesture::GESTURE_TAP) ||
+                         __gestureEventsProcessed.test(Gesture::GESTURE_SWIPE) )
+                    {
+                        __pointer1.pressed = true;
+                        __pointer1.time = Game::getInstance()->getAbsoluteTime();
+                        __pointer1.pointerId = pointerId;
+                        __pointer1.x = x;
+                        __pointer1.y = 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);
+                    }
+                }
+                break;
+
+            case AMOTION_EVENT_ACTION_POINTER_UP:
+                {
+                    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);
+
+                    bool gestureDetected = false;
+                    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) &&
+                            gameplay::Game::getInstance()->getAbsoluteTime() - __pointer1.time < GESTURE_SWIPE_DURATION_MAX && 
+                            (abs(deltaX) > GESTURE_SWIPE_DISTANCE_MIN || abs(deltaY) > GESTURE_SWIPE_DISTANCE_MIN) )
+                        {
+                            int direction;
+                            if (deltaX > 0)
+                                direction |= gameplay::Gesture::SWIPE_DIRECTION_RIGHT;
+                            else if (deltaX < 0)
+                                direction |= gameplay::Gesture::SWIPE_DIRECTION_LEFT;
+                            
+                            if (deltaY > 0)
+                                direction |= gameplay::Gesture::SWIPE_DIRECTION_DOWN;
+                            else if (deltaY < 0)
+                                direction |= gameplay::Gesture::SWIPE_DIRECTION_UP;
+
+                            gameplay::Game::getInstance()->gestureSwipeEvent(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::Game::getInstance()->gestureTapEvent(x, y);
+                            __pointer1.pressed = false;
+                            gestureDetected = true;
+                        }
+                    }
+
+                    if (!gestureDetected && (__multiTouch || __primaryTouchId == pointerId) )
+                    {
+                        gameplay::Platform::touchEventInternal(Touch::TOUCH_RELEASE, AMotionEvent_getX(event, pointerIndex), AMotionEvent_getY(event, pointerIndex), pointerId);
+                    }
+                    if (__primaryTouchId == pointerId)
+                        __primaryTouchId = -1;
+                }
+                break;
+
+            case AMOTION_EVENT_ACTION_MOVE:
+                {
+                    // ACTION_MOVE events are batched, unlike the other events.
+                    pointerCount = AMotionEvent_getPointerCount(event);
+                    for (size_t i = 0; i < pointerCount; ++i)
+                    {
+                        pointerId = AMotionEvent_getPointerId(event, i);
+                        if (__multiTouch || __primaryTouchId == pointerId)
+                        {
+                            gameplay::Platform::touchEventInternal(Touch::TOUCH_MOVE, AMotionEvent_getX(event, i), AMotionEvent_getY(event, i), pointerId);
+                        }
+                    }
+                }
+                break;
+        }
+        return 1;
+    } 
+    else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY)
+    {
+        int32_t action = AKeyEvent_getAction(event);
+        int32_t keycode = AKeyEvent_getKeyCode(event);
+        int32_t metastate = AKeyEvent_getMetaState(event); 
+        
+        switch(action)
+        {
+            case AKEY_EVENT_ACTION_DOWN:
+                Game::getInstance()->keyEvent(Keyboard::KEY_PRESS, getKey(keycode, metastate));
+                if (int character = getUnicode(keycode, metastate))
+                    gameplay::Platform::keyEventInternal(Keyboard::KEY_CHAR, character);
+                break;
+                    
+            case AKEY_EVENT_ACTION_UP:
+                gameplay::Platform::keyEventInternal(Keyboard::KEY_RELEASE, getKey(keycode, metastate));
+                break;
+        }
+    }
+    return 0;
+}
+
+// Process the next main command.
+static void engine_handle_cmd(struct android_app* app, int32_t cmd)
+{
+    switch (cmd) 
+    {
+        case APP_CMD_INIT_WINDOW:
+            // The window is being shown, get it ready.
+            if (app->window != NULL)
+            {
+                initEGL();
+                __initialized = true;
+            }
+            break;
+        case APP_CMD_TERM_WINDOW:
+            destroyEGLSurface();
+            __initialized = false;
+            break;
+        case APP_CMD_DESTROY:
+            Game::getInstance()->exit();
+            destroyEGLMain();
+            __initialized = false;
+            break;
+        case APP_CMD_GAINED_FOCUS:
+            // When our app gains focus, we start monitoring the accelerometer.
+            if (__accelerometerSensor != NULL) 
+            {
+                ASensorEventQueue_enableSensor(__sensorEventQueue, __accelerometerSensor);
+                // We'd like to get 60 events per second (in microseconds).
+                ASensorEventQueue_setEventRate(__sensorEventQueue, __accelerometerSensor, (1000L/60)*1000);
+            }
+
+            if (Game::getInstance()->getState() == Game::UNINITIALIZED)
+            {
+                Game::getInstance()->run();
+            }
+            else
+            {
+                Game::getInstance()->resume();
+            }
+            break;
+        case APP_CMD_RESUME:
+            if (__initialized)
+            {
+                Game::getInstance()->resume();
+            }
+            __suspended = false;
+            break;
+        case APP_CMD_PAUSE:
+            Game::getInstance()->pause();
+            __suspended = true;
+            break;
+        case APP_CMD_LOST_FOCUS:
+            // When our app loses focus, we stop monitoring the accelerometer.
+            // This is to avoid consuming battery while not being used.
+            if (__accelerometerSensor != NULL) 
+            {
+                ASensorEventQueue_disableSensor(__sensorEventQueue, __accelerometerSensor);
+            }
+            break;
+    }
+}
+
+Platform::Platform(Game* game)
+    : _game(game)
+{
+}
+
+Platform::~Platform()
+{
+}
+
+Platform* Platform::create(Game* game, void* attachToWindow)
+{
+    Platform* platform = new Platform(game);
+    return platform;
+}
+
+int Platform::enterMessagePump()
+{
+    GP_ASSERT(__state && __state->activity && __state->activity->vm);
+
+    __initialized = false;
+    __suspended = false;
+
+    // Get the android application's activity.
+    ANativeActivity* activity = __state->activity;
+    JavaVM* jvm = __state->activity->vm;
+    JNIEnv* env = NULL;
+    jvm->GetEnv((void **)&env, JNI_VERSION_1_6);
+    jint res = jvm->AttachCurrentThread(&env, NULL);
+    if (res == JNI_ERR)
+    {
+        GP_ERROR("Failed to retrieve JVM environment when entering message pump.");
+        return -1; 
+    }
+    GP_ASSERT(env);
+
+    /* Get external files directory on Android; this will result in a directory where all app files
+     * should be stored, like /mnt/sdcard/android/<package-name>/files/
+     */
+    jboolean isCopy;
+
+    jclass clazz = env->GetObjectClass(activity->clazz);
+    jmethodID methodGetExternalStorage = env->GetMethodID(clazz, "getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;");
+
+    jclass clazzFile = env->FindClass("java/io/File");
+    jmethodID methodGetPath = env->GetMethodID(clazzFile, "getPath", "()Ljava/lang/String;");
+
+    // Now has java.io.File object pointing to directory
+    jobject objectFile  = env->CallObjectMethod(activity->clazz, methodGetExternalStorage, NULL);
+    
+    // Now has String object containing path to directory
+    jstring stringExternalPath = static_cast<jstring>(env->CallObjectMethod(objectFile, methodGetPath));
+    const char* externalPath = env->GetStringUTFChars(stringExternalPath, &isCopy);
+
+    // Set the default path to store the resources.
+    std::string assetsPath(externalPath);
+    if (externalPath[strlen(externalPath)-1] != '/')
+        assetsPath += "/";
+
+    FileSystem::setResourcePath(assetsPath.c_str());    
+
+    // Release string data
+    env->ReleaseStringUTFChars(stringExternalPath, externalPath);
+    jvm->DetachCurrentThread();
+    
+    // Get the asset manager to get the resources from the .apk file.
+    __assetManager = activity->assetManager; 
+    
+    // Set the event call back functions.
+    __state->onAppCmd = engine_handle_cmd;
+    __state->onInputEvent = engine_handle_input;
+    
+    // Prepare to monitor accelerometer.
+    __sensorManager = ASensorManager_getInstance();
+    __accelerometerSensor = ASensorManager_getDefaultSensor(__sensorManager, ASENSOR_TYPE_ACCELEROMETER);
+    __sensorEventQueue = ASensorManager_createEventQueue(__sensorManager, __state->looper, LOOPER_ID_USER, NULL, NULL);
+    
+    // Get the initial time.
+    clock_gettime(CLOCK_REALTIME, &__timespec);
+    __timeStart = timespec2millis(&__timespec);
+    __timeAbsolute = 0L;
+    
+    while (true)
+    {
+        // Read all pending events.
+        int ident;
+        int events;
+        struct android_poll_source* source;
+        
+        while ((ident=ALooper_pollAll(!__suspended ? 0 : -1, NULL, &events, (void**)&source)) >= 0) 
+        {
+            // Process this event.
+            if (source != NULL)
+                source->process(__state, source);
+            
+            // If a sensor has data, process it now.
+            if (ident == LOOPER_ID_USER && __accelerometerSensor != NULL)
+                ASensorEventQueue_getEvents(__sensorEventQueue, &__sensorEvent, 1);
+            
+            if (__state->destroyRequested != 0)
+            {
+                return 0;
+            }
+        }
+        
+        // Idle time (no events left to process) is spent rendering.
+        // We skip rendering when the app is paused.
+        if (__initialized && !__suspended)
+        {
+            _game->frame();
+
+            // Post the new frame to the display.
+            // Note that there are a couple cases where eglSwapBuffers could fail
+            // with an error code that requires a certain level of re-initialization:
+            //
+            // 1) EGL_BAD_NATIVE_WINDOW - Called when the surface we're currently using
+            //    is invalidated. This would require us to destroy our EGL surface,
+            //    close our OpenKODE window, and start again.
+            //
+            // 2) EGL_CONTEXT_LOST - Power management event that led to our EGL context
+            //    being lost. Requires us to re-create and re-initalize our EGL context
+            //    and all OpenGL ES state.
+            //
+            // For now, if we get these, we'll simply exit.
+            int rc = eglSwapBuffers(__eglDisplay, __eglSurface);
+            if (rc != EGL_TRUE)
+            {
+                EGLint error = eglGetError();
+                if (error == EGL_BAD_NATIVE_WINDOW)
+                {
+                    if (__state->window != NULL)
+                    {
+                        destroyEGLSurface();
+                        initEGL();
+                    }
+                    __initialized = true;
+                }
+                else
+                {
+                    perror("eglSwapBuffers");
+                    break;
+                }
+            }
+        }
+            
+        // Display the keyboard.
+        gameplay::displayKeyboard(__state, __displayKeyboard);
+    }
+}
+
+void Platform::signalShutdown() 
+{
+    // nothing to do  
+}
+
+bool Platform::canExit()
+{
+    return true;
+}
+   
+unsigned int Platform::getDisplayWidth()
+{
+    return __width;
+}
+    
+unsigned int Platform::getDisplayHeight()
+{
+    return __height;
+}
+    
+double Platform::getAbsoluteTime()
+{
+    clock_gettime(CLOCK_REALTIME, &__timespec);
+    double now = timespec2millis(&__timespec);
+    __timeAbsolute = now - __timeStart;
+
+    return __timeAbsolute;
+}
+
+void Platform::setAbsoluteTime(double time)
+{
+    __timeAbsolute = time;
+}
+
+bool Platform::isVsync()
+{
+    return __vsync;
+}
+
+void Platform::setVsync(bool enable)
+{
+    eglSwapInterval(__eglDisplay, enable ? 1 : 0);
+    __vsync = enable;
+}
+
+
+void Platform::swapBuffers()
+{
+    if (__eglDisplay && __eglSurface)
+        eglSwapBuffers(__eglDisplay, __eglSurface);
+}
+
+void Platform::sleep(long ms)
+{
+    usleep(ms * 1000);
+}
+
+void Platform::setMultiSampling(bool enabled)
+{
+    if (enabled == __multiSampling)
+    {
+        return;
+    }
+
+    //todo
+
+    __multiSampling = enabled;
+}
+
+bool Platform::isMultiSampling()
+{
+    return __multiSampling;
+}
+
+void Platform::setMultiTouch(bool enabled)
+{
+    __multiTouch = enabled;
+}
+
+bool Platform::isMultiTouch()
+{
+    return __multiTouch;
+}
+
+void Platform::getAccelerometerValues(float* pitch, float* roll)
+{
+    double tx, ty, tz;
+    ASensorEvent event;
+    
+    // By default, android accelerometer values are oriented to the portrait mode.
+    // flipping the x and y to get the desired landscape mode values.
+    switch (__orientationAngle)
+    {
+    case 90:
+        tx = -__sensorEvent.acceleration.y;
+        ty = __sensorEvent.acceleration.x;
+        break;
+    case 180:
+        tx = -__sensorEvent.acceleration.x;
+        ty = -__sensorEvent.acceleration.y;
+        break;
+    case 270:
+        tx = __sensorEvent.acceleration.y;
+        ty = -__sensorEvent.acceleration.x;
+        break;
+    default:
+        tx = __sensorEvent.acceleration.x;
+        ty = __sensorEvent.acceleration.y;
+        break;
+    }
+    tz = __sensorEvent.acceleration.z;
+
+    if (pitch != NULL)
+    {
+        GP_ASSERT(tx * tx + tz * tz);
+        *pitch = -atan(ty / sqrt(tx * tx + tz * tz)) * 180.0f * M_1_PI;
+    }
+    if (roll != NULL)
+    {
+        GP_ASSERT(ty * ty + tz * tz);
+        *roll = -atan(tx / sqrt(ty * ty + tz * tz)) * 180.0f * M_1_PI;
+    }
+}
+
+void Platform::getArguments(int* argc, char*** argv)
+{
+    if (argc)
+        *argc = 0;
+    if (argv)
+        *argv = 0;
+}
+
+bool Platform::hasMouse()
+{
+    // not supported
+    return false;
+}
+
+void Platform::setMouseCaptured(bool captured)
+{
+    // not supported
+}
+
+bool Platform::isMouseCaptured()
+{
+    // not supported
+    return false;
+}
+
+void Platform::setCursorVisible(bool visible)
+{
+    // not supported
+}
+
+bool Platform::isCursorVisible()
+{
+    // not supported
+    return false;
+}
+
+void Platform::displayKeyboard(bool display)
+{
+    if (display)
+        __displayKeyboard = true;
+    else
+        __displayKeyboard = false;
+}
+
+void Platform::shutdownInternal()
+{
+    Game::getInstance()->shutdown();
+}
+
+bool Platform::isGestureSupported(Gesture::GestureEvent evt)
+{
+    // Pinch currently not implemented
+    return evt == gameplay::Gesture::GESTURE_SWIPE || evt == gameplay::Gesture::GESTURE_TAP;
+}
+
+void Platform::registerGesture(Gesture::GestureEvent evt)
+{
+    switch(evt)
+    {
+    case Gesture::GESTURE_ANY_SUPPORTED:
+        __gestureEventsProcessed.set();
+        break;
+
+    case Gesture::GESTURE_TAP:
+    case Gesture::GESTURE_SWIPE:
+        __gestureEventsProcessed.set(evt);
+        break;
+
+    default:
+        break;
+    }
+}
+
+void Platform::unregisterGesture(Gesture::GestureEvent evt)
+{
+    switch(evt)
+    {
+    case Gesture::GESTURE_ANY_SUPPORTED:
+        __gestureEventsProcessed.reset();
+        break;
+
+    case Gesture::GESTURE_TAP:
+    case Gesture::GESTURE_SWIPE:
+        __gestureEventsProcessed.set(evt, 0);
+        break;
+
+    default:
+        break;
+    }
+}
+    
+bool Platform::isGestureRegistered(Gesture::GestureEvent evt)
+{
+    return __gestureEventsProcessed.test(evt);
+}
+
+void Platform::pollGamepadState(Gamepad* gamepad)
+{
+}
+
+bool Platform::launchURL(const char *url)
+{
+    if (url == NULL || *url == '\0')
+        return false;
+
+    bool result = true;
+
+    android_app* state = __state;
+    GP_ASSERT(state && state->activity && state->activity->vm);
+    JavaVM* jvm = state->activity->vm;
+    JNIEnv* env = NULL;
+    jvm->GetEnv((void **)&env, JNI_VERSION_1_6);
+    jint r = jvm->AttachCurrentThread(&env, NULL);
+    if (r == JNI_ERR)
+    {
+        GP_ERROR("Failed to retrieve JVM environment to display keyboard.");
+        return false;
+    }
+    GP_ASSERT(env);
+
+    jclass classActivity = env->FindClass("android/app/NativeActivity");
+    jclass classIntent = env->FindClass("android/content/Intent");
+    jclass classUri = env->FindClass("android/net/Uri");
+
+    GP_ASSERT(classActivity && classIntent && classUri);
+
+    // Get static field ID Intent.ACTION_VIEW
+    jfieldID fieldActionView = env->GetStaticFieldID(classIntent, "ACTION_VIEW", "Ljava/lang/String;");
+    GP_ASSERT(fieldActionView);
+
+    // Get string value of Intent.ACTION_VIEW, we'll need that to pass to Intent's constructor later on
+    jstring paramActionView = (jstring)env->GetStaticObjectField(classIntent, fieldActionView);
+    GP_ASSERT(paramActionView);
+
+    // Get method ID Uri.parse, will be needed to parse the url given into Uri object
+    jmethodID methodUriParse = env->GetStaticMethodID(classUri, "parse","(Ljava/lang/String;)Landroid/net/Uri;");
+    GP_ASSERT(methodUriParse);
+
+    // Get method ID Activity.startActivity, so we can start the appropriate activity for the View action of our Uri
+    jmethodID methodActivityStartActivity = env->GetMethodID(classActivity, "startActivity","(Landroid/content/Intent;)V");
+    GP_ASSERT(methodActivityStartActivity);
+
+    // Get method ID Intent constructor, the one that takes action and uri (String;Uri)
+    jmethodID methodIntentInit = env->GetMethodID(classIntent, "<init>","(Ljava/lang/String;Landroid/net/Uri;)V");
+    GP_ASSERT(methodIntentInit);
+
+    // Convert our url to Java's string and parse it to Uri
+    jstring paramUrlString = env->NewStringUTF(url);
+    jobject paramUri = env->CallStaticObjectMethod(classUri, methodUriParse, paramUrlString);
+    GP_ASSERT(paramUri);
+
+    // Create Intent with Intent.ACTION_VIEW and parsed Uri arguments
+    jobject paramIntent = env->NewObject(classIntent, methodIntentInit, paramActionView, paramUri);
+    GP_ASSERT(paramIntent);
+
+    // Launch NativeActivity.startActivity with our intent to view the url! state->activity->clazz holds
+    // our NativeActivity object
+    env->CallVoidMethod(state->activity->clazz, methodActivityStartActivity, paramIntent);
+
+    /* startActivity may throw a ActivitNotFoundException if, well, activity is not found.
+       Example: http://<url> is passed to the intent but there is no browser installed in the system
+       we need to handle it. */
+    jobject exception = env->ExceptionOccurred();
+
+    // We're not lucky here
+    if (exception)
+    {
+        // Print out the exception data to logcat
+        env->ExceptionDescribe();
+
+        // Exception needs to be cleared
+        env->ExceptionClear();
+
+        // Launching the url failed
+        result = false;
+    }
+
+    // See you Space Cowboy
+    jvm->DetachCurrentThread();
+    return result;
+}
+
+}
+
+#endif

+ 1585 - 1575
gameplay/src/PlatformBlackBerry.cpp

@@ -1,1575 +1,1585 @@
-#ifdef __QNX__
-
-#include "Base.h"
-#include "Platform.h"
-#include "FileSystem.h"
-#include "Game.h"
-#include "Form.h"
-#include "ScriptController.h"
-#include <unistd.h>
-#include <sys/keycodes.h>
-#include <screen/screen.h>
-#include <input/screen_helpers.h>
-#include <gestures/set.h>
-#include <gestures/swipe.h>
-#include <gestures/pinch.h>
-#include <gestures/tap.h>
-#include <bps/bps.h>
-#include <bps/event.h>
-#include <bps/screen.h>
-#include <bps/navigator.h>
-#include <bps/sensor.h>
-#include <bps/orientation.h>
-#include <bps/virtualkeyboard.h>
-
-#define TOUCH_COUNT_MAX     4
-
-using namespace std;
-
-struct timespec __timespec;
-static double __timeStart;
-static double __timeAbsolute;
-static bool __vsync = WINDOW_VSYNC;
-static screen_context_t __screenContext;
-static screen_window_t __screenWindow;
-static screen_event_t __screenEvent;
-static int __screenWindowSize[2];
-static bool __screenFullscreen = false;
-static EGLDisplay __eglDisplay = EGL_NO_DISPLAY;
-static EGLContext __eglContext = EGL_NO_CONTEXT;
-static EGLSurface __eglSurface = EGL_NO_SURFACE;
-static EGLConfig __eglConfig = 0;
-static int __orientationAngle;
-static bool __multiTouch = false;
-static bool __multiSampling = false;
-static float __pitch;
-static float __roll;
-static const char* __glExtensions;
-static struct gestures_set * __gestureSet;
-static bitset<3> __gestureEventsProcessed;
-static bool __gestureSwipeRecognized = false;
-PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray = NULL;
-PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays = NULL;
-PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays = NULL;
-PFNGLISVERTEXARRAYOESPROC glIsVertexArray = NULL;
-
-namespace gameplay
-{
-
-// Gets the Keyboard::Key enumeration constant that corresponds to the given QNX key code.
-static Keyboard::Key getKey(int qnxKeycode)
-{
-    switch (qnxKeycode)
-    {
-    case KEYCODE_SYSREQ:
-        return Keyboard::KEY_SYSREQ;
-    case KEYCODE_BREAK:
-        return Keyboard::KEY_BREAK;
-    case KEYCODE_MENU:
-        return Keyboard::KEY_MENU;
-    case KEYCODE_KP_ENTER:
-        return Keyboard::KEY_KP_ENTER;
-    case KEYCODE_PAUSE:
-        return Keyboard::KEY_PAUSE;
-    case KEYCODE_SCROLL_LOCK:
-        return Keyboard::KEY_SCROLL_LOCK;
-    case KEYCODE_PRINT:
-        return Keyboard::KEY_PRINT;
-    case KEYCODE_ESCAPE:
-        return Keyboard::KEY_ESCAPE;
-    case KEYCODE_BACKSPACE:
-        return Keyboard::KEY_BACKSPACE;
-    case KEYCODE_BACK_TAB:
-        return Keyboard::KEY_BACK_TAB;
-    case KEYCODE_TAB:
-        return Keyboard::KEY_TAB;
-    case KEYCODE_RETURN:
-        return Keyboard::KEY_RETURN;
-    case KEYCODE_CAPS_LOCK:
-        return Keyboard::KEY_CAPS_LOCK;
-    case KEYCODE_LEFT_SHIFT:
-    case KEYCODE_RIGHT_SHIFT:
-        return Keyboard::KEY_SHIFT;
-    case KEYCODE_LEFT_CTRL:
-    case KEYCODE_RIGHT_CTRL:
-        return Keyboard::KEY_CTRL;
-    case KEYCODE_LEFT_ALT:
-    case KEYCODE_RIGHT_ALT:
-        return Keyboard::KEY_ALT;
-    case KEYCODE_LEFT_HYPER:
-    case KEYCODE_RIGHT_HYPER:
-        return Keyboard::KEY_HYPER;
-    case KEYCODE_INSERT:
-        return Keyboard::KEY_INSERT;
-    case KEYCODE_HOME:
-        return Keyboard::KEY_HOME;
-    case KEYCODE_PG_UP:
-        return Keyboard::KEY_PG_UP;
-    case KEYCODE_DELETE:
-        return Keyboard::KEY_DELETE;
-    case KEYCODE_END:
-        return Keyboard::KEY_END;
-    case KEYCODE_PG_DOWN:
-        return Keyboard::KEY_PG_DOWN;
-    case KEYCODE_LEFT:
-        return Keyboard::KEY_LEFT_ARROW;
-    case KEYCODE_RIGHT:
-        return Keyboard::KEY_RIGHT_ARROW;
-    case KEYCODE_UP:
-        return Keyboard::KEY_UP_ARROW;
-    case KEYCODE_DOWN:
-        return Keyboard::KEY_DOWN_ARROW;
-    case KEYCODE_NUM_LOCK:
-        return Keyboard::KEY_NUM_LOCK;
-    case KEYCODE_KP_PLUS:
-        return Keyboard::KEY_KP_PLUS;
-    case KEYCODE_KP_MINUS:
-        return Keyboard::KEY_KP_MINUS;
-    case KEYCODE_KP_MULTIPLY:
-        return Keyboard::KEY_KP_MULTIPLY;
-    case KEYCODE_KP_DIVIDE:
-        return Keyboard::KEY_KP_DIVIDE;
-    case KEYCODE_KP_HOME:
-        return Keyboard::KEY_KP_HOME;
-    case KEYCODE_KP_UP:
-        return Keyboard::KEY_KP_UP;
-    case KEYCODE_KP_PG_UP:
-        return Keyboard::KEY_KP_PG_UP;
-    case KEYCODE_KP_LEFT:
-        return Keyboard::KEY_KP_LEFT;
-    case KEYCODE_KP_FIVE:
-        return Keyboard::KEY_KP_FIVE;
-    case KEYCODE_KP_RIGHT:
-        return Keyboard::KEY_KP_RIGHT;
-    case KEYCODE_KP_END:
-        return Keyboard::KEY_KP_END;
-    case KEYCODE_KP_DOWN:
-        return Keyboard::KEY_KP_DOWN;
-    case KEYCODE_KP_PG_DOWN:
-        return Keyboard::KEY_KP_PG_DOWN;
-    case KEYCODE_KP_INSERT:
-        return Keyboard::KEY_KP_INSERT;
-    case KEYCODE_KP_DELETE:
-        return Keyboard::KEY_KP_DELETE;
-    case KEYCODE_F1:
-        return Keyboard::KEY_F1;
-    case KEYCODE_F2:
-        return Keyboard::KEY_F2;
-    case KEYCODE_F3:
-        return Keyboard::KEY_F3;
-    case KEYCODE_F4:
-        return Keyboard::KEY_F4;
-    case KEYCODE_F5:
-        return Keyboard::KEY_F5;
-    case KEYCODE_F6:
-        return Keyboard::KEY_F6;
-    case KEYCODE_F7:
-        return Keyboard::KEY_F7;
-    case KEYCODE_F8:
-        return Keyboard::KEY_F8;
-    case KEYCODE_F9:
-        return Keyboard::KEY_F9;
-    case KEYCODE_F10:
-        return Keyboard::KEY_F10;
-    case KEYCODE_F11:
-        return Keyboard::KEY_F11;
-    case KEYCODE_F12:
-        return Keyboard::KEY_F12;
-    case KEYCODE_SPACE:
-        return Keyboard::KEY_SPACE;
-    case KEYCODE_RIGHT_PAREN:
-        return Keyboard::KEY_RIGHT_PARENTHESIS;
-    case KEYCODE_ZERO:
-        return Keyboard::KEY_ZERO;
-    case KEYCODE_EXCLAM:
-        return Keyboard::KEY_EXCLAM;
-    case KEYCODE_ONE:
-        return Keyboard::KEY_ONE;
-    case KEYCODE_AT:
-        return Keyboard::KEY_AT;
-    case KEYCODE_TWO:
-        return Keyboard::KEY_TWO;
-    case KEYCODE_NUMBER:
-        return Keyboard::KEY_NUMBER;
-    case KEYCODE_THREE:
-        return Keyboard::KEY_THREE;
-    case KEYCODE_DOLLAR:
-        return Keyboard::KEY_DOLLAR;
-    case KEYCODE_FOUR:
-        return Keyboard::KEY_FOUR;
-    case KEYCODE_PERCENT:
-        return Keyboard::KEY_PERCENT;
-    case KEYCODE_FIVE:
-        return Keyboard::KEY_FIVE;
-    case KEYCODE_CIRCUMFLEX:
-        return Keyboard::KEY_CIRCUMFLEX;
-    case KEYCODE_SIX:
-        return Keyboard::KEY_SIX;
-    case KEYCODE_AMPERSAND:
-        return Keyboard::KEY_AMPERSAND;
-    case KEYCODE_SEVEN:
-        return Keyboard::KEY_SEVEN;
-    case KEYCODE_ASTERISK:
-        return Keyboard::KEY_ASTERISK;
-    case KEYCODE_EIGHT:
-        return Keyboard::KEY_EIGHT;
-    case KEYCODE_LEFT_PAREN:
-        return Keyboard::KEY_LEFT_PARENTHESIS;
-    case KEYCODE_NINE:
-        return Keyboard::KEY_NINE;
-    case KEYCODE_EQUAL:
-        return Keyboard::KEY_EQUAL;
-    case KEYCODE_PLUS:
-        return Keyboard::KEY_PLUS;
-    case KEYCODE_LESS_THAN:
-        return Keyboard::KEY_LESS_THAN;
-    case KEYCODE_COMMA:
-        return Keyboard::KEY_COMMA;
-    case KEYCODE_UNDERSCORE:
-        return Keyboard::KEY_UNDERSCORE;
-    case KEYCODE_MINUS:
-        return Keyboard::KEY_MINUS;
-    case KEYCODE_GREATER_THAN:
-        return Keyboard::KEY_GREATER_THAN;
-    case KEYCODE_PERIOD:
-        return Keyboard::KEY_PERIOD;
-    case KEYCODE_COLON:
-        return Keyboard::KEY_COLON;
-    case KEYCODE_SEMICOLON:
-        return Keyboard::KEY_SEMICOLON;
-    case KEYCODE_QUESTION:
-        return Keyboard::KEY_QUESTION;
-    case KEYCODE_SLASH:
-        return Keyboard::KEY_SLASH;
-    case KEYCODE_GRAVE:
-        return Keyboard::KEY_GRAVE;
-    case KEYCODE_TILDE:
-        return Keyboard::KEY_TILDE;
-    case KEYCODE_LEFT_BRACE:
-        return Keyboard::KEY_LEFT_BRACE;
-    case KEYCODE_LEFT_BRACKET:
-        return Keyboard::KEY_LEFT_BRACKET;
-    case KEYCODE_BAR:
-        return Keyboard::KEY_BAR;
-    case KEYCODE_BACK_SLASH:
-        return Keyboard::KEY_BACK_SLASH;
-    case KEYCODE_RIGHT_BRACE:
-        return Keyboard::KEY_RIGHT_BRACE;
-    case KEYCODE_RIGHT_BRACKET:
-        return Keyboard::KEY_RIGHT_BRACKET;
-    case KEYCODE_QUOTE:
-        return Keyboard::KEY_QUOTE;
-    case KEYCODE_APOSTROPHE:
-        return Keyboard::KEY_APOSTROPHE;
-    case 0x20AC:
-        return Keyboard::KEY_EURO;
-    case KEYCODE_POUND_SIGN:
-        return Keyboard::KEY_POUND;
-    case KEYCODE_YEN_SIGN:
-        return Keyboard::KEY_YEN;
-    case KEYCODE_MIDDLE_DOT:
-        return Keyboard::KEY_MIDDLE_DOT;
-    case KEYCODE_CAPITAL_A:
-        return Keyboard::KEY_CAPITAL_A;
-    case KEYCODE_A:
-        return Keyboard::KEY_A;
-    case KEYCODE_CAPITAL_B:
-        return Keyboard::KEY_CAPITAL_B;
-    case KEYCODE_B:
-        return Keyboard::KEY_B;
-    case KEYCODE_CAPITAL_C:
-        return Keyboard::KEY_CAPITAL_C;
-    case KEYCODE_C:
-        return Keyboard::KEY_C;
-    case KEYCODE_CAPITAL_D:
-        return Keyboard::KEY_CAPITAL_D;
-    case KEYCODE_D:
-        return Keyboard::KEY_D;
-    case KEYCODE_CAPITAL_E:
-        return Keyboard::KEY_CAPITAL_E;
-    case KEYCODE_E:
-        return Keyboard::KEY_E;
-    case KEYCODE_CAPITAL_F:
-        return Keyboard::KEY_CAPITAL_F;
-    case KEYCODE_F:
-        return Keyboard::KEY_F;
-    case KEYCODE_CAPITAL_G:
-        return Keyboard::KEY_CAPITAL_G;
-    case KEYCODE_G:
-        return Keyboard::KEY_G;
-    case KEYCODE_CAPITAL_H:
-        return Keyboard::KEY_CAPITAL_H;
-    case KEYCODE_H:
-        return Keyboard::KEY_H;
-    case KEYCODE_CAPITAL_I:
-        return Keyboard::KEY_CAPITAL_I;
-    case KEYCODE_I:
-        return Keyboard::KEY_I;
-    case KEYCODE_CAPITAL_J:
-        return Keyboard::KEY_CAPITAL_J;
-    case KEYCODE_J:
-        return Keyboard::KEY_J;
-    case KEYCODE_CAPITAL_K:
-        return Keyboard::KEY_CAPITAL_K;
-    case KEYCODE_K:
-        return Keyboard::KEY_K;
-    case KEYCODE_CAPITAL_L:
-        return Keyboard::KEY_CAPITAL_L;
-    case KEYCODE_L:
-        return Keyboard::KEY_L;
-    case KEYCODE_CAPITAL_M:
-        return Keyboard::KEY_CAPITAL_M;
-    case KEYCODE_M:
-        return Keyboard::KEY_M;
-    case KEYCODE_CAPITAL_N:
-        return Keyboard::KEY_CAPITAL_N;
-    case KEYCODE_N:
-        return Keyboard::KEY_N;
-    case KEYCODE_CAPITAL_O:
-        return Keyboard::KEY_CAPITAL_O;
-    case KEYCODE_O:
-        return Keyboard::KEY_O;
-    case KEYCODE_CAPITAL_P:
-        return Keyboard::KEY_CAPITAL_P;
-    case KEYCODE_P:
-        return Keyboard::KEY_P;
-    case KEYCODE_CAPITAL_Q:
-        return Keyboard::KEY_CAPITAL_Q;
-    case KEYCODE_Q:
-        return Keyboard::KEY_Q;
-    case KEYCODE_CAPITAL_R:
-        return Keyboard::KEY_CAPITAL_R;
-    case KEYCODE_R:
-        return Keyboard::KEY_R;
-    case KEYCODE_CAPITAL_S:
-        return Keyboard::KEY_CAPITAL_S;
-    case KEYCODE_S:
-        return Keyboard::KEY_S;
-    case KEYCODE_CAPITAL_T:
-        return Keyboard::KEY_CAPITAL_T;
-    case KEYCODE_T:
-        return Keyboard::KEY_T;
-    case KEYCODE_CAPITAL_U:
-        return Keyboard::KEY_CAPITAL_U;
-    case KEYCODE_U:
-        return Keyboard::KEY_U;
-    case KEYCODE_CAPITAL_V:
-        return Keyboard::KEY_CAPITAL_V;
-    case KEYCODE_V:
-        return Keyboard::KEY_V;
-    case KEYCODE_CAPITAL_W:
-        return Keyboard::KEY_CAPITAL_W;
-    case KEYCODE_W:
-        return Keyboard::KEY_W;
-    case KEYCODE_CAPITAL_X:
-        return Keyboard::KEY_CAPITAL_X;
-    case KEYCODE_X:
-        return Keyboard::KEY_X;
-    case KEYCODE_CAPITAL_Y:
-        return Keyboard::KEY_CAPITAL_Y;
-    case KEYCODE_Y:
-        return Keyboard::KEY_Y;
-    case KEYCODE_CAPITAL_Z:
-        return Keyboard::KEY_CAPITAL_Z;
-    case KEYCODE_Z:
-        return Keyboard::KEY_Z;
-    default:
-        return Keyboard::KEY_NONE;
-    }
-}
-
-/**
- * Returns the unicode value from the given QNX key code value.
- * Some non-printable characters also have corresponding unicode values, such as backspace.
- *
- * @param qnxKeyCode The keyboard key code.
- *
- * @return The unicode value or 0 if the keycode did not represent a unicode key.
- */
-static int getUnicode(int qnxKeyCode)
-{
-    if (qnxKeyCode >= KEYCODE_PC_KEYS && qnxKeyCode <= UNICODE_PRIVATE_USE_AREA_LAST)
-    {
-        switch (qnxKeyCode)
-        {
-        case KEYCODE_BACKSPACE:
-            return 0x0008;
-        case KEYCODE_TAB:
-            return 0x0009;
-        case KEYCODE_KP_ENTER:
-        case KEYCODE_RETURN:
-            return 0x000A;
-        case KEYCODE_ESCAPE:
-            return 0x001B;
-        // Win32 doesn't consider delete to be a key char.
-        default:
-            return 0;
-        }
-    }
-    return qnxKeyCode;
-}
-
-extern void print(const char* format, ...)
-{
-    GP_ASSERT(format);
-    va_list argptr;
-    va_start(argptr, format);
-    vfprintf(stderr, format, argptr);
-    va_end(argptr);
-}
-
-EGLenum checkErrorEGL(const char* msg)
-{
-    GP_ASSERT(msg);
-    static const char* errmsg[] =
-    {
-        "EGL function failed",
-        "EGL is not initialized, or could not be initialized, for the specified display",
-        "EGL cannot access a requested resource",
-        "EGL failed to allocate resources for the requested operation",
-        "EGL fail to access an unrecognized attribute or attribute value was passed in an attribute list",
-        "EGLConfig argument does not name a valid EGLConfig",
-        "EGLContext argument does not name a valid EGLContext",
-        "EGL current surface of the calling thread is no longer valid",
-        "EGLDisplay argument does not name a valid EGLDisplay",
-        "EGL arguments are inconsistent",
-        "EGLNativePixmapType argument does not refer to a valid native pixmap",
-        "EGLNativeWindowType argument does not refer to a valid native window",
-        "EGL one or more argument values are invalid",
-        "EGLSurface argument does not name a valid surface configured for rendering",
-        "EGL power management event has occurred",
-    };
-    EGLenum error = eglGetError();
-    fprintf(stderr, "%s: %s\n", msg, errmsg[error - EGL_SUCCESS]);
-    return error;
-}
-
-void gesture_callback(gesture_base_t* gesture, mtouch_event_t* event, void* param, int async)
-{
-    switch (gesture->type)
-    {
-    case GESTURE_SWIPE:
-        {
-            if ( __gestureEventsProcessed.test(Gesture::GESTURE_SWIPE) )
-            {
-                gesture_swipe_t* swipe = (gesture_swipe_t*)gesture;
-                if (!__gestureSwipeRecognized)
-                {
-                    Game::getInstance()->gestureSwipeEvent(swipe->coords.x, swipe->coords.y, swipe->direction);
-                    __gestureSwipeRecognized = true;
-                }
-
-            }
-            break;
-        }
-
-    case GESTURE_PINCH:
-        {
-            if ( __gestureEventsProcessed.test(Gesture::GESTURE_PINCH) )
-            {
-                gesture_pinch_t* pinch = (gesture_pinch_t*)gesture;
-                float dist_x = (float)pinch->last_distance.x - (float)pinch->distance.x;
-                float dist_y = (float)pinch->last_distance.y - (float)pinch->distance.y;
-                float scale = sqrt( (dist_x * dist_x) + (dist_y * dist_y) );
-                Game::getInstance()->gesturePinchEvent(pinch->centroid.x, pinch->centroid.y, scale);
-            }
-            break;
-        }
-
-    case GESTURE_TAP:
-        {
-            if ( __gestureEventsProcessed.test(Gesture::GESTURE_TAP) )
-            {
-                gesture_tap_t* tap = (gesture_tap_t*)gesture;
-                Game::getInstance()->gestureTapEvent(tap->touch_coords.x, tap->touch_coords.y);
-            }
-            break;
-        }
-
-    default:
-        break;
-
-    }
-}
-
-#ifdef USE_BLACKBERRY_GAMEPAD
-
-static const char* __vendorStrings[] =
-{
-    "SteelSeries",
-    "Nintendo",
-};
-
-static const char* __productStrings[] =
-{
-    "FREE",
-    "Wii Remote",
-};
-
-static const int __VIDs[] = {
-    0x1038,
-    0x057e,
-};
-
-static const int __PIDs[] = {
-    0x1412,
-    0x0306,
-};
-
-static const unsigned int __knownGamepads = 2;
-
-void queryGamepad(GamepadHandle handle, int* buttonCount, int* joystickCount, int* productId, int* vendorId, char* productString, char* vendorString)
-{
-    char id[128];
-    screen_get_device_property_iv(handle, SCREEN_PROPERTY_BUTTON_COUNT, buttonCount);
-    screen_get_device_property_cv(handle, SCREEN_PROPERTY_ID_STRING, 128, id);
-    screen_get_device_property_cv(handle, SCREEN_PROPERTY_PRODUCT, 64, productString);
-    screen_get_device_property_cv(handle, SCREEN_PROPERTY_VENDOR, 64, vendorString);
-
-    // Check for the existence of analog sticks.
-    int analogs[3];
-    if (!screen_get_device_property_iv(handle, SCREEN_PROPERTY_ANALOG0, analogs))
-    {
-    	++(*joystickCount);
-    }
-
-    if (!screen_get_device_property_iv(handle, SCREEN_PROPERTY_ANALOG1, analogs))
-    {
-    	++(*joystickCount);
-    }
-
-    // ID string format: A-BBBB-CCCC-D.D
-    // A is the device's index
-    // BBBB is the device's Vendor ID (in hexadecimal)
-    // CCCC is the device's Product ID (also in hexadecimal)
-    // D.D is the device's version number
-    char* token = strtok(id, "-");
-    token = strtok(NULL, "-");
-    if (token)
-    {
-	    *vendorId = strtol(token, NULL, 16);
-    }
-
-    token = strtok(NULL, "-");
-    if (token)
-    {
-        *productId = strtol(token, NULL, 16);
-    }
-
-    // For gamepads unknown to BB10,
-    // check VID and PID against gamepads known to gameplay.
-    if (strlen(productString) == 0 || strlen(vendorString) == 0)
-    {
-        for (unsigned int i = 0; i < __knownGamepads; ++i)
-        {
-            if (__VIDs[i] == *vendorId && __PIDs[i] == *productId)
-            {
-            	strcpy(vendorString, __vendorStrings[i]);
-                strcpy(productString, __productStrings[i]);
-            }
-        }
-    }
-}
-
-void Platform::pollGamepadState(Gamepad* gamepad)
-{
-    screen_get_device_property_iv(gamepad->_handle, SCREEN_PROPERTY_BUTTONS, (int*)&gamepad->_buttons);
-
-    unsigned int i;
-    for (i = 0; i < gamepad->_joystickCount; ++i)
-    {
-        GP_ASSERT(i < 2);
-
-        int analog[3];
-        switch (i)
-        {
-        case 0:
-            screen_get_device_property_iv(gamepad->_handle, SCREEN_PROPERTY_ANALOG0, analog);
-            break;
-        case 1:
-            screen_get_device_property_iv(gamepad->_handle, SCREEN_PROPERTY_ANALOG1, analog);
-            break;
-        }
-        
-        // So far we've tested two gamepads with analog sticks on BlackBerry:
-        // the SteelSeries FREE, and the iControlPad.
-        // Both return values between -128 and +127, with the y axis starting from
-        // the top at -128.
-        // 1 / 128 == 0.0078125f
-        // 1 / 127 == 0.0078740157480315f
-        float x = (float)analog[0];
-        float y = -(float)analog[1];
-        x *= (x < 0) ? 0.0078125f : 0.0078740157480315f;
-        y *= (y > 0) ? 0.0078125f : 0.0078740157480315f;
-
-        gamepad->_joysticks[i].set(x, y);        
-    }
-
-    for (i = 0; i < gamepad->_triggerCount; ++i)
-    {
-        GP_ASSERT(i < 2);
-
-        int analog[3];
-        switch (i)
-        {
-        case 0:
-            screen_get_device_property_iv(gamepad->_handle, SCREEN_PROPERTY_ANALOG0, analog);
-            break;
-        case 1:
-            screen_get_device_property_iv(gamepad->_handle, SCREEN_PROPERTY_ANALOG1, analog);
-            break;
-        }
-
-        float value = (float)analog[2] * 0.0078125f;
-        gamepad->_triggers[i] = value;
-    }
-}
-#else
-void Platform::pollGamepadState(Gamepad* gamepad)
-{
-}
-#endif
-
-Platform::Platform(Game* game)
-    : _game(game)
-{
-}
-
-Platform::~Platform()
-{
-    if (__eglDisplay != EGL_NO_DISPLAY)
-    {
-        eglMakeCurrent(__eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-    }
-
-    if (__eglSurface != EGL_NO_SURFACE)
-    {
-        eglDestroySurface(__eglDisplay, __eglSurface);
-        __eglSurface = EGL_NO_SURFACE;
-    }
-
-    if (__eglContext != EGL_NO_CONTEXT)
-    {
-        eglDestroyContext(__eglDisplay, __eglContext);
-        __eglContext = EGL_NO_CONTEXT;
-    }
-
-    if (__eglDisplay != EGL_NO_DISPLAY)
-    {
-        eglTerminate(__eglDisplay);
-        __eglDisplay = EGL_NO_DISPLAY;
-    }
-
-    if (__screenWindow)
-    {
-        screen_destroy_window(__screenWindow);
-        __screenWindow = NULL;
-    }
-
-    if (__screenEvent)
-    {
-        screen_destroy_event(__screenEvent);
-        __screenEvent = NULL;
-    }
-
-    if (__screenContext)
-    {
-        screen_destroy_context(__screenContext);
-        __screenContext = NULL;
-    }
-}
-
-Platform* Platform::create(Game* game, void* attachToWindow)
-{
-    FileSystem::setResourcePath("./app/native/");
-    Platform* platform = new Platform(game);
-
-    // Query game config
-    int samples = 0;
-    Properties* config = Game::getInstance()->getConfig()->getNamespace("window", true);
-    if (config)
-    {
-        samples = std::max(config->getInt("samples"), 0);
-    }
-
-    __gestureSet = gestures_set_alloc();
-    swipe_gesture_alloc(NULL, gesture_callback, __gestureSet);
-    pinch_gesture_alloc(NULL, gesture_callback, __gestureSet);
-    tap_gesture_alloc(NULL, gesture_callback, __gestureSet);
-
-    bps_initialize();
-
-    // Initialize navigator and orientation
-    static const int SENSOR_RATE = 25000;
-    sensor_set_rate(SENSOR_TYPE_AZIMUTH_PITCH_ROLL, SENSOR_RATE);
-    sensor_set_skip_duplicates(SENSOR_TYPE_AZIMUTH_PITCH_ROLL, true);
-    sensor_request_events(SENSOR_TYPE_AZIMUTH_PITCH_ROLL);
-    navigator_request_events(0);
-    navigator_rotation_lock(true);
-    __orientationAngle = atoi(getenv("ORIENTATION"));
-
-    int rc = 0;
-    int screenFormat = SCREEN_FORMAT_RGBA8888;
-#ifdef __X86__
-    int screenUsage = SCREEN_USAGE_OPENGL_ES2;
-#else
-    int screenUsage = SCREEN_USAGE_DISPLAY|SCREEN_USAGE_OPENGL_ES2; // Physical device copy directly into physical display
-#endif
-    int screenSwapInterval = WINDOW_VSYNC ? 1 : 0;
-    int screenTransparency = SCREEN_TRANSPARENCY_NONE;
-
-    char *width_str = getenv("WIDTH");
-    char *height_str = getenv("HEIGHT");
-
-    // Hard-coded to (0,0).
-    int windowPosition[] =
-    {
-        0, 0
-    };
-
-    EGLint eglConfigCount;
-
-    // Hard-coded to 32-bit/OpenGL ES 2.0.
-    // NOTE: EGL_SAMPLE_BUFFERS and EGL_SAMPLES MUST remain at the beginning of the attribute list
-    // since they are expected to be at indices 0-3 in config fallback code later.
-    EGLint eglConfigAttrs[] =
-    {
-        EGL_SAMPLE_BUFFERS,     samples > 0 ? 1 : 0,
-        EGL_SAMPLES,            samples,
-        EGL_RED_SIZE,           8,
-        EGL_GREEN_SIZE,         8,
-        EGL_BLUE_SIZE,          8,
-        EGL_ALPHA_SIZE,         8,
-        EGL_DEPTH_SIZE,         24,
-        EGL_STENCIL_SIZE,       8,
-        EGL_SURFACE_TYPE,       EGL_WINDOW_BIT,
-        EGL_RENDERABLE_TYPE,    EGL_OPENGL_ES2_BIT,
-        EGL_NONE
-    };
-    __multiSampling = samples > 0;
-
-    const EGLint eglContextAttrs[] =
-    {
-        EGL_CONTEXT_CLIENT_VERSION,    2,
-        EGL_NONE
-    };
-
-    const EGLint eglSurfaceAttrs[] =
-    {
-        EGL_RENDER_BUFFER,    EGL_BACK_BUFFER,
-        EGL_NONE
-    };
-
-    // Create the screen context.
-    rc = screen_create_context(&__screenContext, 0);
-    if (rc)
-    {
-        perror("screen_create_context");
-        goto error;
-    }
-
-    // Create the screen window.
-    rc = screen_create_window(&__screenWindow, __screenContext);
-    if (rc)
-    {
-        perror("screen_create_window");
-        goto error;
-    }
-
-    // Set/get any window properties.
-    rc = screen_set_window_property_iv(__screenWindow, SCREEN_PROPERTY_FORMAT, &screenFormat);
-    if (rc)
-    {
-        perror("screen_set_window_property_iv(SCREEN_PROPERTY_FORMAT)");
-        goto error;
-    }
-
-    rc = screen_set_window_property_iv(__screenWindow, SCREEN_PROPERTY_USAGE, &screenUsage);
-    if (rc)
-    {
-        perror("screen_set_window_property_iv(SCREEN_PROPERTY_USAGE)");
-        goto error;
-    }
-
-    if (width_str && height_str)
-    {
-        __screenWindowSize[0] = atoi(width_str);
-        __screenWindowSize[1] = atoi(height_str);
-    }
-    else
-    {
-        screen_display_t screen_display;
-        rc = screen_get_window_property_pv(__screenWindow, SCREEN_PROPERTY_DISPLAY, (void **)&screen_display);
-        if (rc)
-        {
-            perror("screen_get_window_property_pv(SCREEN_PROPERTY_DISPLAY)");
-            goto error;
-        }
-
-        screen_display_mode_t screen_mode;
-        rc = screen_get_display_property_pv(screen_display, SCREEN_PROPERTY_MODE, (void**)&screen_mode);
-        if (rc)
-        {
-            perror("screen_get_display_property_pv(SCREEN_PROPERTY_MODE)");
-            goto error;
-        }
-
-        int size[2];
-        rc = screen_get_window_property_iv(__screenWindow, SCREEN_PROPERTY_BUFFER_SIZE, size);
-        if (rc)
-        {
-            perror("screen_get_window_property_iv(SCREEN_PROPERTY_BUFFER_SIZE)");
-            goto error;
-        }
-
-        __screenWindowSize[0] = size[0];
-        __screenWindowSize[1] = size[1];
-
-        if ((__orientationAngle == 0) || (__orientationAngle == 180))
-        {
-            if (((screen_mode.width > screen_mode.height) && (size[0] < size[1])) ||
-                ((screen_mode.width < screen_mode.height) && (size[0] > size[1])))
-            {
-                __screenWindowSize[1] = size[0];
-                __screenWindowSize[0] = size[1];
-            }
-        }
-        else if ((__orientationAngle == 90) || (__orientationAngle == 270))
-        {
-            if (((screen_mode.width > screen_mode.height) && (size[0] > size[1])) ||
-                ((screen_mode.width < screen_mode.height) && (size[0] < size[1])))
-            {
-                __screenWindowSize[1] = size[0];
-                __screenWindowSize[0] = size[1];
-            }
-        }
-        else
-        {
-            perror("Navigator returned an unexpected orientation angle.");
-            goto error;
-        }
-
-
-        rc = screen_set_window_property_iv(__screenWindow, SCREEN_PROPERTY_ROTATION, &__orientationAngle);
-        if (rc)
-        {
-            perror("screen_set_window_property_iv(SCREEN_PROPERTY_ROTATION)");
-            goto error;
-        }
-    }
-
-    rc = screen_set_window_property_iv(__screenWindow, SCREEN_PROPERTY_BUFFER_SIZE, __screenWindowSize);
-    if (rc)
-    {
-        perror("screen_set_window_property_iv(SCREEN_PROPERTY_BUFFER_SIZE)");
-        goto error;
-    }
-
-    if (windowPosition[0] != 0 || windowPosition[1] != 0)
-    {
-        rc = screen_set_window_property_iv(__screenWindow, SCREEN_PROPERTY_POSITION, windowPosition);
-        if (rc)
-        {
-            perror("screen_set_window_property_iv(SCREEN_PROPERTY_POSITION)");
-            goto error;
-        }
-    }
-
-    rc = screen_set_window_property_iv(__screenWindow, SCREEN_PROPERTY_TRANSPARENCY, &screenTransparency);
-    if (rc)
-    {
-        perror("screen_set_window_property_iv(SCREEN_PROPERTY_TRANSPARENCY)");
-        goto error;
-    }
-
-    // Double buffered.
-    rc = screen_create_window_buffers(__screenWindow, 2);
-    if (rc)
-    {
-        perror("screen_create_window_buffers");
-        goto error;
-    }
-
-    // Create screen event object.
-    rc = screen_create_event(&__screenEvent);
-    if (rc)
-    {
-        perror("screen_create_event");
-        goto error;
-    }
-
-    // Request screen events.
-    screen_request_events(__screenContext);
-
-    // Get the EGL display and initialize.
-    __eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-    if (__eglDisplay == EGL_NO_DISPLAY)
-    {
-        perror("eglGetDisplay");
-        goto error;
-    }
-    if (eglInitialize(__eglDisplay, NULL, NULL) != EGL_TRUE)
-    {
-        perror("eglInitialize");
-        goto error;
-    }
-
-    if (eglChooseConfig(__eglDisplay, eglConfigAttrs, &__eglConfig, 1, &eglConfigCount) != EGL_TRUE || eglConfigCount == 0)
-    {
-        bool success = false;
-        while (samples)
-        {
-            // Try lowering the MSAA sample count until we find a supported config
-            GP_WARN("Failed to find a valid EGL configuration with EGL samples=%d. Trying samples=%d instead.", samples, samples/2);
-            samples /= 2;
-            eglConfigAttrs[1] = samples > 0 ? 1 : 0;
-            eglConfigAttrs[3] = samples;
-            if (eglChooseConfig(__eglDisplay, eglConfigAttrs, &__eglConfig, 1, &eglConfigCount) == EGL_TRUE && eglConfigCount > 0)
-            {
-                success = true;
-                break;
-            }
-        }
-
-        __multiSampling = samples > 0;
-
-        if (!success)
-        {
-            checkErrorEGL("eglChooseConfig");
-            goto error;
-        }
-    }
-
-    __eglContext = eglCreateContext(__eglDisplay, __eglConfig, EGL_NO_CONTEXT, eglContextAttrs);
-    if (__eglContext == EGL_NO_CONTEXT)
-    {
-        checkErrorEGL("eglCreateContext");
-        goto error;
-    }
-
-    __eglSurface = eglCreateWindowSurface(__eglDisplay, __eglConfig, __screenWindow, eglSurfaceAttrs);
-    if (__eglSurface == EGL_NO_SURFACE)
-    {
-        checkErrorEGL("eglCreateWindowSurface");
-        goto error;
-    }
-
-    if (eglMakeCurrent(__eglDisplay, __eglSurface, __eglSurface, __eglContext) != EGL_TRUE)
-    {
-        checkErrorEGL("eglMakeCurrent");
-        goto error;
-    }
-
-    // Set vsync.
-    eglSwapInterval(__eglDisplay, screenSwapInterval);
-
-    // Initialize OpenGL ES extensions.
-    __glExtensions = (const char*)glGetString(GL_EXTENSIONS);
-
-    if (strstr(__glExtensions, "GL_OES_vertex_array_object") || strstr(__glExtensions, "GL_ARB_vertex_array_object"))
-    {
-        glBindVertexArray = (PFNGLBINDVERTEXARRAYOESPROC)eglGetProcAddress("glBindVertexArrayOES");
-        glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSOESPROC)eglGetProcAddress("glDeleteVertexArraysOES");
-        glGenVertexArrays = (PFNGLGENVERTEXARRAYSOESPROC)eglGetProcAddress("glGenVertexArraysOES");
-        glIsVertexArray = (PFNGLISVERTEXARRAYOESPROC)eglGetProcAddress("glIsVertexArrayOES");
-    }
-
- #ifdef USE_BLACKBERRY_GAMEPAD
-
-    screen_device_t* screenDevs;
-
-    // Discover initial gamepad devices.
-    int count;
-    screen_get_context_property_iv(__screenContext, SCREEN_PROPERTY_DEVICE_COUNT, &count);
-    screenDevs = (screen_device_t*)calloc(count, sizeof(screen_device_t));
-    screen_get_context_property_pv(__screenContext, SCREEN_PROPERTY_DEVICES, (void**)screenDevs);
-
-	for (int i = 0; i < count; i++) 
-    {
-	    int type;
-        screen_get_device_property_iv(screenDevs[i], SCREEN_PROPERTY_TYPE, &type);
-
-        if (type == SCREEN_EVENT_GAMEPAD || type == SCREEN_EVENT_JOYSTICK)
-        {
-            int buttonCount = 0;
-            int joystickCount = 0;
-            int productId;
-            int vendorId;
-            char productString[64];
-            char vendorString[64];
-            queryGamepad(screenDevs[i], &buttonCount, &joystickCount, &productId, &vendorId, productString, vendorString);
-            Platform::gamepadEventConnectedInternal(screenDevs[i], buttonCount, joystickCount, 0, vendorId, productId, vendorString, productString);
-        }
-	}
-	free(screenDevs);
-#endif
-
-    return platform;
-
-error:
-
-    return NULL;
-}
-
-/**
- * Convert the timespec into milliseconds.
- */
-double timespec2millis(struct timespec *a)
-{
-    GP_ASSERT(a);
-    return (1000.0 * a->tv_sec) + (0.000001 * a->tv_nsec);
-}
-
-/**
- * Fires a mouse event or a touch event on the game.
- * If the mouse event is not consumed, a touch event is fired instead.
- *
- * @param mouseEvent The mouse event to fire.
- * @param touchEvent The touch event to fire.
- * @param x The x position of the touch in pixels.
- * @param y The y position of the touch in pixels.
- */
-void mouseOrTouchEvent(Mouse::MouseEvent mouseEvent, Touch::TouchEvent touchEvent, int x, int y)
-{
-    if (!gameplay::Platform::mouseEventInternal(mouseEvent, x, y, 0))
-    {
-        Platform::touchEventInternal(touchEvent, x, y, 0);
-    }
-}
-
-int Platform::enterMessagePump()
-{
-    GP_ASSERT(_game);
-
-    int rc;
-    int eventType;
-    int flags;
-    int value;
-    int position[2];
-    int domain;
-    mtouch_event_t touchEvent;
-    bool suspended = false;
-
-    // Get the initial time.
-    clock_gettime(CLOCK_REALTIME, &__timespec);
-    __timeStart = timespec2millis(&__timespec);
-    __timeAbsolute = 0L;
-
-    _game->run();
-
-    // Message loop.
-    while (true)
-    {
-        bps_event_t* event = NULL;
-        
-        while (true)
-        {
-            rc = bps_get_event(&event, 1);
-            GP_ASSERT(rc == BPS_SUCCESS);
-
-            if (event == NULL)
-                break;
-
-            domain = bps_event_get_domain(event);
-
-            if (domain == screen_get_domain())
-            {
-                __screenEvent = screen_event_get_event(event);
-                screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_TYPE, &eventType);
-                switch (eventType)
-                {
-                    case SCREEN_EVENT_MTOUCH_TOUCH:
-                    {
-                        screen_get_mtouch_event(__screenEvent, &touchEvent, 0);
-                        if (__gestureEventsProcessed.any())
-                            rc = gestures_set_process_event(__gestureSet, &touchEvent, NULL);
-
-                        if ( !rc && (__multiTouch || touchEvent.contact_id == 0) )
-                        {
-                            gameplay::Platform::touchEventInternal(Touch::TOUCH_PRESS, touchEvent.x, touchEvent.y, touchEvent.contact_id);
-                        }
-                        break;
-                    }
-
-                    case SCREEN_EVENT_MTOUCH_RELEASE:
-                    {
-                        screen_get_mtouch_event(__screenEvent, &touchEvent, 0);
-                        if (__gestureEventsProcessed.any())
-                            rc = gestures_set_process_event(__gestureSet, &touchEvent, NULL);
-
-                        if ( !rc && (__multiTouch || touchEvent.contact_id == 0) )
-                        {
-                            gameplay::Platform::touchEventInternal(Touch::TOUCH_RELEASE, touchEvent.x, touchEvent.y, touchEvent.contact_id);
-                        }
-                        if (__gestureSwipeRecognized)
-                        {
-                            __gestureSwipeRecognized = false;
-                        }
-                        break;
-                    }
-
-                    case SCREEN_EVENT_MTOUCH_MOVE:
-                    {
-                        screen_get_mtouch_event(__screenEvent, &touchEvent, 0);
-                        if (__gestureEventsProcessed.any())
-                            rc = gestures_set_process_event(__gestureSet, &touchEvent, NULL);
-
-                        if ( !rc && (__multiTouch || touchEvent.contact_id == 0) )
-                        {
-                            gameplay::Platform::touchEventInternal(Touch::TOUCH_MOVE, touchEvent.x, touchEvent.y, touchEvent.contact_id);
-                        }
-                        break;
-                    }
-
-                    case SCREEN_EVENT_POINTER:
-                    {
-                        static int mouse_pressed = 0;
-                        int buttons;
-                        int wheel;
-                        // A move event will be fired unless a button state changed.
-                        bool move = true;
-                        bool left_move = false;
-                        // This is a mouse move event, it is applicable to a device with a usb mouse or simulator.
-                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_BUTTONS, &buttons);
-                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_SOURCE_POSITION, position);
-                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_MOUSE_WHEEL, &wheel);
-
-                        // Handle left mouse. Interpret as touch if the left mouse event is not consumed.
-                        if (buttons & SCREEN_LEFT_MOUSE_BUTTON)
-                        {
-                            if (mouse_pressed & SCREEN_LEFT_MOUSE_BUTTON)
-                            {
-                                left_move = true;
-                            }
-                            else
-                            {
-                                move = false;
-                                mouse_pressed |= SCREEN_LEFT_MOUSE_BUTTON;
-                                mouseOrTouchEvent(Mouse::MOUSE_PRESS_LEFT_BUTTON, Touch::TOUCH_PRESS, position[0], position[1]);
-                            }
-                        }
-                        else if (mouse_pressed & SCREEN_LEFT_MOUSE_BUTTON)
-                        {
-                            move = false;
-                            mouse_pressed &= ~SCREEN_LEFT_MOUSE_BUTTON;
-                            mouseOrTouchEvent(Mouse::MOUSE_RELEASE_LEFT_BUTTON, Touch::TOUCH_RELEASE, position[0], position[1]);
-                        }
-
-                        // Handle right mouse.
-                        if (buttons & SCREEN_RIGHT_MOUSE_BUTTON)
-                        {
-                            if ((mouse_pressed & SCREEN_RIGHT_MOUSE_BUTTON) == 0)
-                            {
-                                move = false;
-                                mouse_pressed |= SCREEN_RIGHT_MOUSE_BUTTON;
-                                gameplay::Platform::mouseEventInternal(Mouse::MOUSE_PRESS_RIGHT_BUTTON, position[0], position[1], 0);
-                            }
-                        }
-                        else if (mouse_pressed & SCREEN_RIGHT_MOUSE_BUTTON)
-                        {
-                            move = false;
-                            mouse_pressed &= ~SCREEN_RIGHT_MOUSE_BUTTON;
-                            gameplay::Platform::mouseEventInternal(Mouse::MOUSE_RELEASE_RIGHT_BUTTON, position[0], position[1], 0);
-                        }
-
-                        // Handle middle mouse.
-                        if (buttons & SCREEN_MIDDLE_MOUSE_BUTTON)
-                        {
-                            if ((mouse_pressed & SCREEN_MIDDLE_MOUSE_BUTTON) == 0)
-                            {
-                                move = false;
-                                mouse_pressed |= SCREEN_MIDDLE_MOUSE_BUTTON;
-                                gameplay::Platform::mouseEventInternal(Mouse::MOUSE_PRESS_MIDDLE_BUTTON, position[0], position[1], 0);
-                            }
-                        }
-                        else if (mouse_pressed & SCREEN_MIDDLE_MOUSE_BUTTON)
-                        {
-                            move = false;
-                            mouse_pressed &= ~SCREEN_MIDDLE_MOUSE_BUTTON;
-                            gameplay::Platform::mouseEventInternal(Mouse::MOUSE_RELEASE_MIDDLE_BUTTON, position[0], position[1], 0);
-                        }
-
-                        // Fire a move event if none of the buttons changed.
-                        if (left_move)
-                        {
-                            mouseOrTouchEvent(Mouse::MOUSE_MOVE, Touch::TOUCH_MOVE, position[0], position[1]);
-                        }
-                        else if (move)
-                        {
-                            gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, position[0], position[1], 0);
-                        }
-
-                        // Handle mouse wheel events.
-                        if (wheel)
-                        {
-                            gameplay::Platform::mouseEventInternal(Mouse::MOUSE_WHEEL, position[0], position[1], -wheel);
-                        }
-                        break;
-                    }
-
-                    case SCREEN_EVENT_KEYBOARD:
-                    {
-                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_KEY_FLAGS, &flags);
-                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_KEY_SYM, &value);
-                        gameplay::Keyboard::KeyEvent evt = (flags & KEY_DOWN) ? gameplay::Keyboard::KEY_PRESS :  gameplay::Keyboard::KEY_RELEASE;
-                        // Suppress key repeats.
-                        if ((flags & KEY_REPEAT) == 0)
-                        {
-                            keyEventInternal(evt, getKey(value));
-                            if (evt == gameplay::Keyboard::KEY_PRESS && (flags & KEY_SYM_VALID))
-                            {
-                                int unicode = getUnicode(value);
-                                if (unicode)
-                                    keyEventInternal(gameplay::Keyboard::KEY_CHAR, unicode);
-                            }
-                        }
-                        break;
-                    }
-#ifdef USE_BLACKBERRY_GAMEPAD
-                    case SCREEN_EVENT_DEVICE:
-                    {
-                        // A device was attached or removed.
-                        screen_device_t device;
-                        int attached;
-
-                        screen_get_event_property_pv(__screenEvent, SCREEN_PROPERTY_DEVICE, (void**)&device);
-                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_ATTACHED, &attached);
-
-                        if (attached)
-                        {
-                            int type;
-                            screen_get_device_property_iv(device, SCREEN_PROPERTY_TYPE, &type);
-                            if (type == SCREEN_EVENT_GAMEPAD || type == SCREEN_EVENT_JOYSTICK)
-                            {
-                                int buttonCount = 0;
-                                int joystickCount = 0;
-                                int productId;
-                                int vendorId;
-                                char productString[64];
-                                char vendorString[64];
-                                queryGamepad(device, &buttonCount, &joystickCount, &productId, &vendorId, productString, vendorString);
-                                Platform::gamepadEventConnectedInternal(device, buttonCount, joystickCount, 0, vendorId, productId, vendorString, productString);
-                            }
-                        }
-                        else
-                        {
-                            Platform::gamepadEventDisconnectedInternal(device);
-                        }
-
-                        break;
-                    }
-#endif
-                    default:
-                        break;
-                }
-            }
-            else if (domain == navigator_get_domain())
-            {
-                switch (bps_event_get_code(event))
-                {
-                case NAVIGATOR_SWIPE_DOWN:
-                    _game->menuEvent();
-                    break;
-                case NAVIGATOR_WINDOW_STATE:
-                {
-                    navigator_window_state_t state = navigator_event_get_window_state(event);
-                    switch (state)
-                    {
-                    case NAVIGATOR_WINDOW_FULLSCREEN:
-                        if (!__screenFullscreen)
-                            __screenFullscreen = true;
-                        _game->resume();
-                        suspended = false;
-                        break;
-                    case NAVIGATOR_WINDOW_THUMBNAIL:
-                    case NAVIGATOR_WINDOW_INVISIBLE:
-                        if (__screenFullscreen && !suspended)
-                        {
-                            _game->pause();
-                            suspended = true;
-                        }
-                        break;
-                    }
-                    break;
-                }
-                case NAVIGATOR_EXIT:
-                	// Call Game::shutdown directly, instead of Game::exit.
-                	// We need to do this since exit() queues a request to shutdown for the
-                	// next frame, which will never get executed because we are suspended.
-                    _game->shutdown();
-                    break;
-                }
-            }
-            else if (domain == sensor_get_domain())
-            {
-                if (bps_event_get_code(event) == SENSOR_AZIMUTH_PITCH_ROLL_READING)
-                {
-                    float azimuth;
-                    sensor_event_get_apr(event, &azimuth, &__pitch, &__roll);
-                }
-            }
-        }
-
-        // If we are done, then exit.
-        if (_game->getState() == Game::UNINITIALIZED)
-            break;
-
-        if (!suspended)
-        {
-            _game->frame();
-
-            // Post the new frame to the display.
-            // Note that there are a couple cases where eglSwapBuffers could fail
-            // with an error code that requires a certain level of re-initialization:
-            //
-            // 1) EGL_BAD_NATIVE_WINDOW - Called when the surface we're currently using
-            //    is invalidated. This would require us to destroy our EGL surface,
-            //    close our OpenKODE window, and start again.
-            //
-            // 2) EGL_CONTEXT_LOST - Power management event that led to our EGL context
-            //    being lost. Requires us to re-create and re-initalize our EGL context
-            //    and all OpenGL ES state.
-            //
-            // For now, if we get these, we'll simply exit.
-            rc = eglSwapBuffers(__eglDisplay, __eglSurface);
-            if (rc != EGL_TRUE)
-            {
-                _game->shutdown();
-                perror("eglSwapBuffers");
-                break;
-            }
-        }
-    }
-
-    screen_stop_events(__screenContext);
-    bps_shutdown();
-    screen_destroy_context(__screenContext);
-
-    return 0;
-}
-    
-void Platform::signalShutdown() 
-{
-    // nothing to do  
-}
-
-bool Platform::canExit()
-{
-    return true;
-}
-
-unsigned int Platform::getDisplayWidth()
-{
-    return __screenWindowSize[0];
-}
-
-unsigned int Platform::getDisplayHeight()
-{
-    return __screenWindowSize[1];
-}
-
-double Platform::getAbsoluteTime()
-{
-    clock_gettime(CLOCK_REALTIME, &__timespec);
-    double now = timespec2millis(&__timespec);
-    __timeAbsolute = now - __timeStart;
-
-    return __timeAbsolute;
-}
-
-void Platform::setAbsoluteTime(double time)
-{
-    __timeAbsolute = time;
-}
-
-bool Platform::isVsync()
-{
-    return __vsync;
-}
-
-void Platform::setVsync(bool enable)
-{
-    eglSwapInterval(__eglDisplay, enable ? 1 : 0);
-    __vsync = enable;
-}
-
-void Platform::swapBuffers()
-{
-    if (__eglDisplay && __eglSurface)
-        eglSwapBuffers(__eglDisplay, __eglSurface);
-}
-
-void Platform::sleep(long ms)
-{
-    usleep(ms * 1000);
-}
-
-void Platform::setMultiSampling(bool enabled)
-{
-    if (enabled == __multiSampling)
-    {
-        return;
-    }
-
-    //todo
-
-    __multiSampling = enabled;
-}
-
-bool Platform::isMultiSampling()
-{
-    return __multiSampling;
-}
-
-void Platform::setMultiTouch(bool enabled)
-{
-    __multiTouch = enabled;
-}
-
-bool Platform::isMultiTouch()
-{
-    return __multiTouch;
-}
-
-void Platform::getAccelerometerValues(float* pitch, float* roll)
-{
-    GP_ASSERT(pitch);
-    GP_ASSERT(roll);
-
-    switch(__orientationAngle)
-    {
-    // Landscape based device adjusting for landscape game mode
-    case 0:
-        if (pitch)
-            *pitch = __pitch;
-        if (roll)
-            *roll = -__roll;
-        break;
-    case 180:
-        if (pitch)
-            *pitch = -__pitch;
-        if (roll)
-            *roll = __roll;
-        break;
-
-    // Portrait based device adjusting for landscape game mode
-    case 90:
-        if (pitch)
-            *pitch = -__roll;
-        if (roll)
-            *roll = -__pitch;
-        break;
-
-    case  270:
-        if (pitch)
-            *pitch = __roll;
-        if (roll)
-            *roll = __pitch;
-        break;
-
-    default:
-        break;
-    }
-}
-
-bool Platform::hasMouse()
-{
-    // not supported
-    return false;
-}
-
-void Platform::setMouseCaptured(bool captured)
-{
-    // not supported
-}
-
-bool Platform::isMouseCaptured()
-{
-    // not supported
-    return false;
-}
-
-void Platform::setCursorVisible(bool visible)
-{
-    // not supported
-}
-
-bool Platform::isCursorVisible()
-{
-    // not supported
-    return false;
-}
-
-void Platform::displayKeyboard(bool display)
-{
-    if (display)
-        virtualkeyboard_show();
-    else
-        virtualkeyboard_hide();
-}
-
-void Platform::shutdownInternal()
-{
-    Game::getInstance()->shutdown();
-}
-
-bool Platform::isGestureSupported(Gesture::GestureEvent evt)
-{
-    // All are supported no need to test the bitset
-    return true;
-}
-
-void Platform::registerGesture(Gesture::GestureEvent evt)
-{
-    switch(evt)
-    {
-    case Gesture::GESTURE_ANY_SUPPORTED:
-        __gestureEventsProcessed.set();
-        break;
-
-    case Gesture::GESTURE_SWIPE:
-    case Gesture::GESTURE_PINCH:
-    case Gesture::GESTURE_TAP:
-        __gestureEventsProcessed.set(evt);
-        break;
-
-    default:
-        break;
-    }
-}
-
-void Platform::unregisterGesture(Gesture::GestureEvent evt)
-{
-    switch(evt)
-    {
-    case Gesture::GESTURE_ANY_SUPPORTED:
-        __gestureEventsProcessed.reset();
-        break;
-
-    case Gesture::GESTURE_SWIPE:
-    case Gesture::GESTURE_PINCH:
-    case Gesture::GESTURE_TAP:
-        __gestureEventsProcessed.set(evt, 0);
-        break;
-
-    default:
-        break;
-    }
-}
-    
-bool Platform::isGestureRegistered(Gesture::GestureEvent evt)
-{
-    return __gestureEventsProcessed.test(evt);
-}
-
-bool Platform::launchURL(const char* url)
-{
-    if (url == NULL || *url == '\0')
-        return false;
-
-    return navigator_invoke(url, NULL) == BPS_SUCCESS;
-}
-
-}
-
-#endif
+#ifdef __QNX__
+
+#include "Base.h"
+#include "Platform.h"
+#include "FileSystem.h"
+#include "Game.h"
+#include "Form.h"
+#include "ScriptController.h"
+#include <unistd.h>
+#include <sys/keycodes.h>
+#include <screen/screen.h>
+#include <input/screen_helpers.h>
+#include <gestures/set.h>
+#include <gestures/swipe.h>
+#include <gestures/pinch.h>
+#include <gestures/tap.h>
+#include <bps/bps.h>
+#include <bps/event.h>
+#include <bps/screen.h>
+#include <bps/navigator.h>
+#include <bps/sensor.h>
+#include <bps/orientation.h>
+#include <bps/virtualkeyboard.h>
+
+#define TOUCH_COUNT_MAX     4
+
+using namespace std;
+
+int __argc = 0;
+char** __argv = 0;
+struct timespec __timespec;
+static double __timeStart;
+static double __timeAbsolute;
+static bool __vsync = WINDOW_VSYNC;
+static screen_context_t __screenContext;
+static screen_window_t __screenWindow;
+static screen_event_t __screenEvent;
+static int __screenWindowSize[2];
+static bool __screenFullscreen = false;
+static EGLDisplay __eglDisplay = EGL_NO_DISPLAY;
+static EGLContext __eglContext = EGL_NO_CONTEXT;
+static EGLSurface __eglSurface = EGL_NO_SURFACE;
+static EGLConfig __eglConfig = 0;
+static int __orientationAngle;
+static bool __multiTouch = false;
+static bool __multiSampling = false;
+static float __pitch;
+static float __roll;
+static const char* __glExtensions;
+static struct gestures_set * __gestureSet;
+static bitset<3> __gestureEventsProcessed;
+static bool __gestureSwipeRecognized = false;
+PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray = NULL;
+PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays = NULL;
+PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays = NULL;
+PFNGLISVERTEXARRAYOESPROC glIsVertexArray = NULL;
+
+namespace gameplay
+{
+
+// Gets the Keyboard::Key enumeration constant that corresponds to the given QNX key code.
+static Keyboard::Key getKey(int qnxKeycode)
+{
+    switch (qnxKeycode)
+    {
+    case KEYCODE_SYSREQ:
+        return Keyboard::KEY_SYSREQ;
+    case KEYCODE_BREAK:
+        return Keyboard::KEY_BREAK;
+    case KEYCODE_MENU:
+        return Keyboard::KEY_MENU;
+    case KEYCODE_KP_ENTER:
+        return Keyboard::KEY_KP_ENTER;
+    case KEYCODE_PAUSE:
+        return Keyboard::KEY_PAUSE;
+    case KEYCODE_SCROLL_LOCK:
+        return Keyboard::KEY_SCROLL_LOCK;
+    case KEYCODE_PRINT:
+        return Keyboard::KEY_PRINT;
+    case KEYCODE_ESCAPE:
+        return Keyboard::KEY_ESCAPE;
+    case KEYCODE_BACKSPACE:
+        return Keyboard::KEY_BACKSPACE;
+    case KEYCODE_BACK_TAB:
+        return Keyboard::KEY_BACK_TAB;
+    case KEYCODE_TAB:
+        return Keyboard::KEY_TAB;
+    case KEYCODE_RETURN:
+        return Keyboard::KEY_RETURN;
+    case KEYCODE_CAPS_LOCK:
+        return Keyboard::KEY_CAPS_LOCK;
+    case KEYCODE_LEFT_SHIFT:
+    case KEYCODE_RIGHT_SHIFT:
+        return Keyboard::KEY_SHIFT;
+    case KEYCODE_LEFT_CTRL:
+    case KEYCODE_RIGHT_CTRL:
+        return Keyboard::KEY_CTRL;
+    case KEYCODE_LEFT_ALT:
+    case KEYCODE_RIGHT_ALT:
+        return Keyboard::KEY_ALT;
+    case KEYCODE_LEFT_HYPER:
+    case KEYCODE_RIGHT_HYPER:
+        return Keyboard::KEY_HYPER;
+    case KEYCODE_INSERT:
+        return Keyboard::KEY_INSERT;
+    case KEYCODE_HOME:
+        return Keyboard::KEY_HOME;
+    case KEYCODE_PG_UP:
+        return Keyboard::KEY_PG_UP;
+    case KEYCODE_DELETE:
+        return Keyboard::KEY_DELETE;
+    case KEYCODE_END:
+        return Keyboard::KEY_END;
+    case KEYCODE_PG_DOWN:
+        return Keyboard::KEY_PG_DOWN;
+    case KEYCODE_LEFT:
+        return Keyboard::KEY_LEFT_ARROW;
+    case KEYCODE_RIGHT:
+        return Keyboard::KEY_RIGHT_ARROW;
+    case KEYCODE_UP:
+        return Keyboard::KEY_UP_ARROW;
+    case KEYCODE_DOWN:
+        return Keyboard::KEY_DOWN_ARROW;
+    case KEYCODE_NUM_LOCK:
+        return Keyboard::KEY_NUM_LOCK;
+    case KEYCODE_KP_PLUS:
+        return Keyboard::KEY_KP_PLUS;
+    case KEYCODE_KP_MINUS:
+        return Keyboard::KEY_KP_MINUS;
+    case KEYCODE_KP_MULTIPLY:
+        return Keyboard::KEY_KP_MULTIPLY;
+    case KEYCODE_KP_DIVIDE:
+        return Keyboard::KEY_KP_DIVIDE;
+    case KEYCODE_KP_HOME:
+        return Keyboard::KEY_KP_HOME;
+    case KEYCODE_KP_UP:
+        return Keyboard::KEY_KP_UP;
+    case KEYCODE_KP_PG_UP:
+        return Keyboard::KEY_KP_PG_UP;
+    case KEYCODE_KP_LEFT:
+        return Keyboard::KEY_KP_LEFT;
+    case KEYCODE_KP_FIVE:
+        return Keyboard::KEY_KP_FIVE;
+    case KEYCODE_KP_RIGHT:
+        return Keyboard::KEY_KP_RIGHT;
+    case KEYCODE_KP_END:
+        return Keyboard::KEY_KP_END;
+    case KEYCODE_KP_DOWN:
+        return Keyboard::KEY_KP_DOWN;
+    case KEYCODE_KP_PG_DOWN:
+        return Keyboard::KEY_KP_PG_DOWN;
+    case KEYCODE_KP_INSERT:
+        return Keyboard::KEY_KP_INSERT;
+    case KEYCODE_KP_DELETE:
+        return Keyboard::KEY_KP_DELETE;
+    case KEYCODE_F1:
+        return Keyboard::KEY_F1;
+    case KEYCODE_F2:
+        return Keyboard::KEY_F2;
+    case KEYCODE_F3:
+        return Keyboard::KEY_F3;
+    case KEYCODE_F4:
+        return Keyboard::KEY_F4;
+    case KEYCODE_F5:
+        return Keyboard::KEY_F5;
+    case KEYCODE_F6:
+        return Keyboard::KEY_F6;
+    case KEYCODE_F7:
+        return Keyboard::KEY_F7;
+    case KEYCODE_F8:
+        return Keyboard::KEY_F8;
+    case KEYCODE_F9:
+        return Keyboard::KEY_F9;
+    case KEYCODE_F10:
+        return Keyboard::KEY_F10;
+    case KEYCODE_F11:
+        return Keyboard::KEY_F11;
+    case KEYCODE_F12:
+        return Keyboard::KEY_F12;
+    case KEYCODE_SPACE:
+        return Keyboard::KEY_SPACE;
+    case KEYCODE_RIGHT_PAREN:
+        return Keyboard::KEY_RIGHT_PARENTHESIS;
+    case KEYCODE_ZERO:
+        return Keyboard::KEY_ZERO;
+    case KEYCODE_EXCLAM:
+        return Keyboard::KEY_EXCLAM;
+    case KEYCODE_ONE:
+        return Keyboard::KEY_ONE;
+    case KEYCODE_AT:
+        return Keyboard::KEY_AT;
+    case KEYCODE_TWO:
+        return Keyboard::KEY_TWO;
+    case KEYCODE_NUMBER:
+        return Keyboard::KEY_NUMBER;
+    case KEYCODE_THREE:
+        return Keyboard::KEY_THREE;
+    case KEYCODE_DOLLAR:
+        return Keyboard::KEY_DOLLAR;
+    case KEYCODE_FOUR:
+        return Keyboard::KEY_FOUR;
+    case KEYCODE_PERCENT:
+        return Keyboard::KEY_PERCENT;
+    case KEYCODE_FIVE:
+        return Keyboard::KEY_FIVE;
+    case KEYCODE_CIRCUMFLEX:
+        return Keyboard::KEY_CIRCUMFLEX;
+    case KEYCODE_SIX:
+        return Keyboard::KEY_SIX;
+    case KEYCODE_AMPERSAND:
+        return Keyboard::KEY_AMPERSAND;
+    case KEYCODE_SEVEN:
+        return Keyboard::KEY_SEVEN;
+    case KEYCODE_ASTERISK:
+        return Keyboard::KEY_ASTERISK;
+    case KEYCODE_EIGHT:
+        return Keyboard::KEY_EIGHT;
+    case KEYCODE_LEFT_PAREN:
+        return Keyboard::KEY_LEFT_PARENTHESIS;
+    case KEYCODE_NINE:
+        return Keyboard::KEY_NINE;
+    case KEYCODE_EQUAL:
+        return Keyboard::KEY_EQUAL;
+    case KEYCODE_PLUS:
+        return Keyboard::KEY_PLUS;
+    case KEYCODE_LESS_THAN:
+        return Keyboard::KEY_LESS_THAN;
+    case KEYCODE_COMMA:
+        return Keyboard::KEY_COMMA;
+    case KEYCODE_UNDERSCORE:
+        return Keyboard::KEY_UNDERSCORE;
+    case KEYCODE_MINUS:
+        return Keyboard::KEY_MINUS;
+    case KEYCODE_GREATER_THAN:
+        return Keyboard::KEY_GREATER_THAN;
+    case KEYCODE_PERIOD:
+        return Keyboard::KEY_PERIOD;
+    case KEYCODE_COLON:
+        return Keyboard::KEY_COLON;
+    case KEYCODE_SEMICOLON:
+        return Keyboard::KEY_SEMICOLON;
+    case KEYCODE_QUESTION:
+        return Keyboard::KEY_QUESTION;
+    case KEYCODE_SLASH:
+        return Keyboard::KEY_SLASH;
+    case KEYCODE_GRAVE:
+        return Keyboard::KEY_GRAVE;
+    case KEYCODE_TILDE:
+        return Keyboard::KEY_TILDE;
+    case KEYCODE_LEFT_BRACE:
+        return Keyboard::KEY_LEFT_BRACE;
+    case KEYCODE_LEFT_BRACKET:
+        return Keyboard::KEY_LEFT_BRACKET;
+    case KEYCODE_BAR:
+        return Keyboard::KEY_BAR;
+    case KEYCODE_BACK_SLASH:
+        return Keyboard::KEY_BACK_SLASH;
+    case KEYCODE_RIGHT_BRACE:
+        return Keyboard::KEY_RIGHT_BRACE;
+    case KEYCODE_RIGHT_BRACKET:
+        return Keyboard::KEY_RIGHT_BRACKET;
+    case KEYCODE_QUOTE:
+        return Keyboard::KEY_QUOTE;
+    case KEYCODE_APOSTROPHE:
+        return Keyboard::KEY_APOSTROPHE;
+    case 0x20AC:
+        return Keyboard::KEY_EURO;
+    case KEYCODE_POUND_SIGN:
+        return Keyboard::KEY_POUND;
+    case KEYCODE_YEN_SIGN:
+        return Keyboard::KEY_YEN;
+    case KEYCODE_MIDDLE_DOT:
+        return Keyboard::KEY_MIDDLE_DOT;
+    case KEYCODE_CAPITAL_A:
+        return Keyboard::KEY_CAPITAL_A;
+    case KEYCODE_A:
+        return Keyboard::KEY_A;
+    case KEYCODE_CAPITAL_B:
+        return Keyboard::KEY_CAPITAL_B;
+    case KEYCODE_B:
+        return Keyboard::KEY_B;
+    case KEYCODE_CAPITAL_C:
+        return Keyboard::KEY_CAPITAL_C;
+    case KEYCODE_C:
+        return Keyboard::KEY_C;
+    case KEYCODE_CAPITAL_D:
+        return Keyboard::KEY_CAPITAL_D;
+    case KEYCODE_D:
+        return Keyboard::KEY_D;
+    case KEYCODE_CAPITAL_E:
+        return Keyboard::KEY_CAPITAL_E;
+    case KEYCODE_E:
+        return Keyboard::KEY_E;
+    case KEYCODE_CAPITAL_F:
+        return Keyboard::KEY_CAPITAL_F;
+    case KEYCODE_F:
+        return Keyboard::KEY_F;
+    case KEYCODE_CAPITAL_G:
+        return Keyboard::KEY_CAPITAL_G;
+    case KEYCODE_G:
+        return Keyboard::KEY_G;
+    case KEYCODE_CAPITAL_H:
+        return Keyboard::KEY_CAPITAL_H;
+    case KEYCODE_H:
+        return Keyboard::KEY_H;
+    case KEYCODE_CAPITAL_I:
+        return Keyboard::KEY_CAPITAL_I;
+    case KEYCODE_I:
+        return Keyboard::KEY_I;
+    case KEYCODE_CAPITAL_J:
+        return Keyboard::KEY_CAPITAL_J;
+    case KEYCODE_J:
+        return Keyboard::KEY_J;
+    case KEYCODE_CAPITAL_K:
+        return Keyboard::KEY_CAPITAL_K;
+    case KEYCODE_K:
+        return Keyboard::KEY_K;
+    case KEYCODE_CAPITAL_L:
+        return Keyboard::KEY_CAPITAL_L;
+    case KEYCODE_L:
+        return Keyboard::KEY_L;
+    case KEYCODE_CAPITAL_M:
+        return Keyboard::KEY_CAPITAL_M;
+    case KEYCODE_M:
+        return Keyboard::KEY_M;
+    case KEYCODE_CAPITAL_N:
+        return Keyboard::KEY_CAPITAL_N;
+    case KEYCODE_N:
+        return Keyboard::KEY_N;
+    case KEYCODE_CAPITAL_O:
+        return Keyboard::KEY_CAPITAL_O;
+    case KEYCODE_O:
+        return Keyboard::KEY_O;
+    case KEYCODE_CAPITAL_P:
+        return Keyboard::KEY_CAPITAL_P;
+    case KEYCODE_P:
+        return Keyboard::KEY_P;
+    case KEYCODE_CAPITAL_Q:
+        return Keyboard::KEY_CAPITAL_Q;
+    case KEYCODE_Q:
+        return Keyboard::KEY_Q;
+    case KEYCODE_CAPITAL_R:
+        return Keyboard::KEY_CAPITAL_R;
+    case KEYCODE_R:
+        return Keyboard::KEY_R;
+    case KEYCODE_CAPITAL_S:
+        return Keyboard::KEY_CAPITAL_S;
+    case KEYCODE_S:
+        return Keyboard::KEY_S;
+    case KEYCODE_CAPITAL_T:
+        return Keyboard::KEY_CAPITAL_T;
+    case KEYCODE_T:
+        return Keyboard::KEY_T;
+    case KEYCODE_CAPITAL_U:
+        return Keyboard::KEY_CAPITAL_U;
+    case KEYCODE_U:
+        return Keyboard::KEY_U;
+    case KEYCODE_CAPITAL_V:
+        return Keyboard::KEY_CAPITAL_V;
+    case KEYCODE_V:
+        return Keyboard::KEY_V;
+    case KEYCODE_CAPITAL_W:
+        return Keyboard::KEY_CAPITAL_W;
+    case KEYCODE_W:
+        return Keyboard::KEY_W;
+    case KEYCODE_CAPITAL_X:
+        return Keyboard::KEY_CAPITAL_X;
+    case KEYCODE_X:
+        return Keyboard::KEY_X;
+    case KEYCODE_CAPITAL_Y:
+        return Keyboard::KEY_CAPITAL_Y;
+    case KEYCODE_Y:
+        return Keyboard::KEY_Y;
+    case KEYCODE_CAPITAL_Z:
+        return Keyboard::KEY_CAPITAL_Z;
+    case KEYCODE_Z:
+        return Keyboard::KEY_Z;
+    default:
+        return Keyboard::KEY_NONE;
+    }
+}
+
+/**
+ * Returns the unicode value from the given QNX key code value.
+ * Some non-printable characters also have corresponding unicode values, such as backspace.
+ *
+ * @param qnxKeyCode The keyboard key code.
+ *
+ * @return The unicode value or 0 if the keycode did not represent a unicode key.
+ */
+static int getUnicode(int qnxKeyCode)
+{
+    if (qnxKeyCode >= KEYCODE_PC_KEYS && qnxKeyCode <= UNICODE_PRIVATE_USE_AREA_LAST)
+    {
+        switch (qnxKeyCode)
+        {
+        case KEYCODE_BACKSPACE:
+            return 0x0008;
+        case KEYCODE_TAB:
+            return 0x0009;
+        case KEYCODE_KP_ENTER:
+        case KEYCODE_RETURN:
+            return 0x000A;
+        case KEYCODE_ESCAPE:
+            return 0x001B;
+        // Win32 doesn't consider delete to be a key char.
+        default:
+            return 0;
+        }
+    }
+    return qnxKeyCode;
+}
+
+extern void print(const char* format, ...)
+{
+    GP_ASSERT(format);
+    va_list argptr;
+    va_start(argptr, format);
+    vfprintf(stderr, format, argptr);
+    va_end(argptr);
+}
+
+EGLenum checkErrorEGL(const char* msg)
+{
+    GP_ASSERT(msg);
+    static const char* errmsg[] =
+    {
+        "EGL function failed",
+        "EGL is not initialized, or could not be initialized, for the specified display",
+        "EGL cannot access a requested resource",
+        "EGL failed to allocate resources for the requested operation",
+        "EGL fail to access an unrecognized attribute or attribute value was passed in an attribute list",
+        "EGLConfig argument does not name a valid EGLConfig",
+        "EGLContext argument does not name a valid EGLContext",
+        "EGL current surface of the calling thread is no longer valid",
+        "EGLDisplay argument does not name a valid EGLDisplay",
+        "EGL arguments are inconsistent",
+        "EGLNativePixmapType argument does not refer to a valid native pixmap",
+        "EGLNativeWindowType argument does not refer to a valid native window",
+        "EGL one or more argument values are invalid",
+        "EGLSurface argument does not name a valid surface configured for rendering",
+        "EGL power management event has occurred",
+    };
+    EGLenum error = eglGetError();
+    fprintf(stderr, "%s: %s\n", msg, errmsg[error - EGL_SUCCESS]);
+    return error;
+}
+
+void gesture_callback(gesture_base_t* gesture, mtouch_event_t* event, void* param, int async)
+{
+    switch (gesture->type)
+    {
+    case GESTURE_SWIPE:
+        {
+            if ( __gestureEventsProcessed.test(Gesture::GESTURE_SWIPE) )
+            {
+                gesture_swipe_t* swipe = (gesture_swipe_t*)gesture;
+                if (!__gestureSwipeRecognized)
+                {
+                    Game::getInstance()->gestureSwipeEvent(swipe->coords.x, swipe->coords.y, swipe->direction);
+                    __gestureSwipeRecognized = true;
+                }
+
+            }
+            break;
+        }
+
+    case GESTURE_PINCH:
+        {
+            if ( __gestureEventsProcessed.test(Gesture::GESTURE_PINCH) )
+            {
+                gesture_pinch_t* pinch = (gesture_pinch_t*)gesture;
+                float dist_x = (float)pinch->last_distance.x - (float)pinch->distance.x;
+                float dist_y = (float)pinch->last_distance.y - (float)pinch->distance.y;
+                float scale = sqrt( (dist_x * dist_x) + (dist_y * dist_y) );
+                Game::getInstance()->gesturePinchEvent(pinch->centroid.x, pinch->centroid.y, scale);
+            }
+            break;
+        }
+
+    case GESTURE_TAP:
+        {
+            if ( __gestureEventsProcessed.test(Gesture::GESTURE_TAP) )
+            {
+                gesture_tap_t* tap = (gesture_tap_t*)gesture;
+                Game::getInstance()->gestureTapEvent(tap->touch_coords.x, tap->touch_coords.y);
+            }
+            break;
+        }
+
+    default:
+        break;
+
+    }
+}
+
+#ifdef USE_BLACKBERRY_GAMEPAD
+
+static const char* __vendorStrings[] =
+{
+    "SteelSeries",
+    "Nintendo",
+};
+
+static const char* __productStrings[] =
+{
+    "FREE",
+    "Wii Remote",
+};
+
+static const int __VIDs[] = {
+    0x1038,
+    0x057e,
+};
+
+static const int __PIDs[] = {
+    0x1412,
+    0x0306,
+};
+
+static const unsigned int __knownGamepads = 2;
+
+void queryGamepad(GamepadHandle handle, int* buttonCount, int* joystickCount, int* productId, int* vendorId, char* productString, char* vendorString)
+{
+    char id[128];
+    screen_get_device_property_iv(handle, SCREEN_PROPERTY_BUTTON_COUNT, buttonCount);
+    screen_get_device_property_cv(handle, SCREEN_PROPERTY_ID_STRING, 128, id);
+    screen_get_device_property_cv(handle, SCREEN_PROPERTY_PRODUCT, 64, productString);
+    screen_get_device_property_cv(handle, SCREEN_PROPERTY_VENDOR, 64, vendorString);
+
+    // Check for the existence of analog sticks.
+    int analogs[3];
+    if (!screen_get_device_property_iv(handle, SCREEN_PROPERTY_ANALOG0, analogs))
+    {
+    	++(*joystickCount);
+    }
+
+    if (!screen_get_device_property_iv(handle, SCREEN_PROPERTY_ANALOG1, analogs))
+    {
+    	++(*joystickCount);
+    }
+
+    // ID string format: A-BBBB-CCCC-D.D
+    // A is the device's index
+    // BBBB is the device's Vendor ID (in hexadecimal)
+    // CCCC is the device's Product ID (also in hexadecimal)
+    // D.D is the device's version number
+    char* token = strtok(id, "-");
+    token = strtok(NULL, "-");
+    if (token)
+    {
+	    *vendorId = strtol(token, NULL, 16);
+    }
+
+    token = strtok(NULL, "-");
+    if (token)
+    {
+        *productId = strtol(token, NULL, 16);
+    }
+
+    // For gamepads unknown to BB10,
+    // check VID and PID against gamepads known to gameplay.
+    if (strlen(productString) == 0 || strlen(vendorString) == 0)
+    {
+        for (unsigned int i = 0; i < __knownGamepads; ++i)
+        {
+            if (__VIDs[i] == *vendorId && __PIDs[i] == *productId)
+            {
+            	strcpy(vendorString, __vendorStrings[i]);
+                strcpy(productString, __productStrings[i]);
+            }
+        }
+    }
+}
+
+void Platform::pollGamepadState(Gamepad* gamepad)
+{
+    screen_get_device_property_iv(gamepad->_handle, SCREEN_PROPERTY_BUTTONS, (int*)&gamepad->_buttons);
+
+    unsigned int i;
+    for (i = 0; i < gamepad->_joystickCount; ++i)
+    {
+        GP_ASSERT(i < 2);
+
+        int analog[3];
+        switch (i)
+        {
+        case 0:
+            screen_get_device_property_iv(gamepad->_handle, SCREEN_PROPERTY_ANALOG0, analog);
+            break;
+        case 1:
+            screen_get_device_property_iv(gamepad->_handle, SCREEN_PROPERTY_ANALOG1, analog);
+            break;
+        }
+        
+        // So far we've tested two gamepads with analog sticks on BlackBerry:
+        // the SteelSeries FREE, and the iControlPad.
+        // Both return values between -128 and +127, with the y axis starting from
+        // the top at -128.
+        // 1 / 128 == 0.0078125f
+        // 1 / 127 == 0.0078740157480315f
+        float x = (float)analog[0];
+        float y = -(float)analog[1];
+        x *= (x < 0) ? 0.0078125f : 0.0078740157480315f;
+        y *= (y > 0) ? 0.0078125f : 0.0078740157480315f;
+
+        gamepad->_joysticks[i].set(x, y);        
+    }
+
+    for (i = 0; i < gamepad->_triggerCount; ++i)
+    {
+        GP_ASSERT(i < 2);
+
+        int analog[3];
+        switch (i)
+        {
+        case 0:
+            screen_get_device_property_iv(gamepad->_handle, SCREEN_PROPERTY_ANALOG0, analog);
+            break;
+        case 1:
+            screen_get_device_property_iv(gamepad->_handle, SCREEN_PROPERTY_ANALOG1, analog);
+            break;
+        }
+
+        float value = (float)analog[2] * 0.0078125f;
+        gamepad->_triggers[i] = value;
+    }
+}
+#else
+void Platform::pollGamepadState(Gamepad* gamepad)
+{
+}
+#endif
+
+Platform::Platform(Game* game)
+    : _game(game)
+{
+}
+
+Platform::~Platform()
+{
+    if (__eglDisplay != EGL_NO_DISPLAY)
+    {
+        eglMakeCurrent(__eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+    }
+
+    if (__eglSurface != EGL_NO_SURFACE)
+    {
+        eglDestroySurface(__eglDisplay, __eglSurface);
+        __eglSurface = EGL_NO_SURFACE;
+    }
+
+    if (__eglContext != EGL_NO_CONTEXT)
+    {
+        eglDestroyContext(__eglDisplay, __eglContext);
+        __eglContext = EGL_NO_CONTEXT;
+    }
+
+    if (__eglDisplay != EGL_NO_DISPLAY)
+    {
+        eglTerminate(__eglDisplay);
+        __eglDisplay = EGL_NO_DISPLAY;
+    }
+
+    if (__screenWindow)
+    {
+        screen_destroy_window(__screenWindow);
+        __screenWindow = NULL;
+    }
+
+    if (__screenEvent)
+    {
+        screen_destroy_event(__screenEvent);
+        __screenEvent = NULL;
+    }
+
+    if (__screenContext)
+    {
+        screen_destroy_context(__screenContext);
+        __screenContext = NULL;
+    }
+}
+
+Platform* Platform::create(Game* game, void* attachToWindow)
+{
+    FileSystem::setResourcePath("./app/native/");
+    Platform* platform = new Platform(game);
+
+    // Query game config
+    int samples = 0;
+    Properties* config = Game::getInstance()->getConfig()->getNamespace("window", true);
+    if (config)
+    {
+        samples = std::max(config->getInt("samples"), 0);
+    }
+
+    __gestureSet = gestures_set_alloc();
+    swipe_gesture_alloc(NULL, gesture_callback, __gestureSet);
+    pinch_gesture_alloc(NULL, gesture_callback, __gestureSet);
+    tap_gesture_alloc(NULL, gesture_callback, __gestureSet);
+
+    bps_initialize();
+
+    // Initialize navigator and orientation
+    static const int SENSOR_RATE = 25000;
+    sensor_set_rate(SENSOR_TYPE_AZIMUTH_PITCH_ROLL, SENSOR_RATE);
+    sensor_set_skip_duplicates(SENSOR_TYPE_AZIMUTH_PITCH_ROLL, true);
+    sensor_request_events(SENSOR_TYPE_AZIMUTH_PITCH_ROLL);
+    navigator_request_events(0);
+    navigator_rotation_lock(true);
+    __orientationAngle = atoi(getenv("ORIENTATION"));
+
+    int rc = 0;
+    int screenFormat = SCREEN_FORMAT_RGBA8888;
+#ifdef __X86__
+    int screenUsage = SCREEN_USAGE_OPENGL_ES2;
+#else
+    int screenUsage = SCREEN_USAGE_DISPLAY|SCREEN_USAGE_OPENGL_ES2; // Physical device copy directly into physical display
+#endif
+    int screenSwapInterval = WINDOW_VSYNC ? 1 : 0;
+    int screenTransparency = SCREEN_TRANSPARENCY_NONE;
+
+    char *width_str = getenv("WIDTH");
+    char *height_str = getenv("HEIGHT");
+
+    // Hard-coded to (0,0).
+    int windowPosition[] =
+    {
+        0, 0
+    };
+
+    EGLint eglConfigCount;
+
+    // Hard-coded to 32-bit/OpenGL ES 2.0.
+    // NOTE: EGL_SAMPLE_BUFFERS and EGL_SAMPLES MUST remain at the beginning of the attribute list
+    // since they are expected to be at indices 0-3 in config fallback code later.
+    EGLint eglConfigAttrs[] =
+    {
+        EGL_SAMPLE_BUFFERS,     samples > 0 ? 1 : 0,
+        EGL_SAMPLES,            samples,
+        EGL_RED_SIZE,           8,
+        EGL_GREEN_SIZE,         8,
+        EGL_BLUE_SIZE,          8,
+        EGL_ALPHA_SIZE,         8,
+        EGL_DEPTH_SIZE,         24,
+        EGL_STENCIL_SIZE,       8,
+        EGL_SURFACE_TYPE,       EGL_WINDOW_BIT,
+        EGL_RENDERABLE_TYPE,    EGL_OPENGL_ES2_BIT,
+        EGL_NONE
+    };
+    __multiSampling = samples > 0;
+
+    const EGLint eglContextAttrs[] =
+    {
+        EGL_CONTEXT_CLIENT_VERSION,    2,
+        EGL_NONE
+    };
+
+    const EGLint eglSurfaceAttrs[] =
+    {
+        EGL_RENDER_BUFFER,    EGL_BACK_BUFFER,
+        EGL_NONE
+    };
+
+    // Create the screen context.
+    rc = screen_create_context(&__screenContext, 0);
+    if (rc)
+    {
+        perror("screen_create_context");
+        goto error;
+    }
+
+    // Create the screen window.
+    rc = screen_create_window(&__screenWindow, __screenContext);
+    if (rc)
+    {
+        perror("screen_create_window");
+        goto error;
+    }
+
+    // Set/get any window properties.
+    rc = screen_set_window_property_iv(__screenWindow, SCREEN_PROPERTY_FORMAT, &screenFormat);
+    if (rc)
+    {
+        perror("screen_set_window_property_iv(SCREEN_PROPERTY_FORMAT)");
+        goto error;
+    }
+
+    rc = screen_set_window_property_iv(__screenWindow, SCREEN_PROPERTY_USAGE, &screenUsage);
+    if (rc)
+    {
+        perror("screen_set_window_property_iv(SCREEN_PROPERTY_USAGE)");
+        goto error;
+    }
+
+    if (width_str && height_str)
+    {
+        __screenWindowSize[0] = atoi(width_str);
+        __screenWindowSize[1] = atoi(height_str);
+    }
+    else
+    {
+        screen_display_t screen_display;
+        rc = screen_get_window_property_pv(__screenWindow, SCREEN_PROPERTY_DISPLAY, (void **)&screen_display);
+        if (rc)
+        {
+            perror("screen_get_window_property_pv(SCREEN_PROPERTY_DISPLAY)");
+            goto error;
+        }
+
+        screen_display_mode_t screen_mode;
+        rc = screen_get_display_property_pv(screen_display, SCREEN_PROPERTY_MODE, (void**)&screen_mode);
+        if (rc)
+        {
+            perror("screen_get_display_property_pv(SCREEN_PROPERTY_MODE)");
+            goto error;
+        }
+
+        int size[2];
+        rc = screen_get_window_property_iv(__screenWindow, SCREEN_PROPERTY_BUFFER_SIZE, size);
+        if (rc)
+        {
+            perror("screen_get_window_property_iv(SCREEN_PROPERTY_BUFFER_SIZE)");
+            goto error;
+        }
+
+        __screenWindowSize[0] = size[0];
+        __screenWindowSize[1] = size[1];
+
+        if ((__orientationAngle == 0) || (__orientationAngle == 180))
+        {
+            if (((screen_mode.width > screen_mode.height) && (size[0] < size[1])) ||
+                ((screen_mode.width < screen_mode.height) && (size[0] > size[1])))
+            {
+                __screenWindowSize[1] = size[0];
+                __screenWindowSize[0] = size[1];
+            }
+        }
+        else if ((__orientationAngle == 90) || (__orientationAngle == 270))
+        {
+            if (((screen_mode.width > screen_mode.height) && (size[0] > size[1])) ||
+                ((screen_mode.width < screen_mode.height) && (size[0] < size[1])))
+            {
+                __screenWindowSize[1] = size[0];
+                __screenWindowSize[0] = size[1];
+            }
+        }
+        else
+        {
+            perror("Navigator returned an unexpected orientation angle.");
+            goto error;
+        }
+
+
+        rc = screen_set_window_property_iv(__screenWindow, SCREEN_PROPERTY_ROTATION, &__orientationAngle);
+        if (rc)
+        {
+            perror("screen_set_window_property_iv(SCREEN_PROPERTY_ROTATION)");
+            goto error;
+        }
+    }
+
+    rc = screen_set_window_property_iv(__screenWindow, SCREEN_PROPERTY_BUFFER_SIZE, __screenWindowSize);
+    if (rc)
+    {
+        perror("screen_set_window_property_iv(SCREEN_PROPERTY_BUFFER_SIZE)");
+        goto error;
+    }
+
+    if (windowPosition[0] != 0 || windowPosition[1] != 0)
+    {
+        rc = screen_set_window_property_iv(__screenWindow, SCREEN_PROPERTY_POSITION, windowPosition);
+        if (rc)
+        {
+            perror("screen_set_window_property_iv(SCREEN_PROPERTY_POSITION)");
+            goto error;
+        }
+    }
+
+    rc = screen_set_window_property_iv(__screenWindow, SCREEN_PROPERTY_TRANSPARENCY, &screenTransparency);
+    if (rc)
+    {
+        perror("screen_set_window_property_iv(SCREEN_PROPERTY_TRANSPARENCY)");
+        goto error;
+    }
+
+    // Double buffered.
+    rc = screen_create_window_buffers(__screenWindow, 2);
+    if (rc)
+    {
+        perror("screen_create_window_buffers");
+        goto error;
+    }
+
+    // Create screen event object.
+    rc = screen_create_event(&__screenEvent);
+    if (rc)
+    {
+        perror("screen_create_event");
+        goto error;
+    }
+
+    // Request screen events.
+    screen_request_events(__screenContext);
+
+    // Get the EGL display and initialize.
+    __eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    if (__eglDisplay == EGL_NO_DISPLAY)
+    {
+        perror("eglGetDisplay");
+        goto error;
+    }
+    if (eglInitialize(__eglDisplay, NULL, NULL) != EGL_TRUE)
+    {
+        perror("eglInitialize");
+        goto error;
+    }
+
+    if (eglChooseConfig(__eglDisplay, eglConfigAttrs, &__eglConfig, 1, &eglConfigCount) != EGL_TRUE || eglConfigCount == 0)
+    {
+        bool success = false;
+        while (samples)
+        {
+            // Try lowering the MSAA sample count until we find a supported config
+            GP_WARN("Failed to find a valid EGL configuration with EGL samples=%d. Trying samples=%d instead.", samples, samples/2);
+            samples /= 2;
+            eglConfigAttrs[1] = samples > 0 ? 1 : 0;
+            eglConfigAttrs[3] = samples;
+            if (eglChooseConfig(__eglDisplay, eglConfigAttrs, &__eglConfig, 1, &eglConfigCount) == EGL_TRUE && eglConfigCount > 0)
+            {
+                success = true;
+                break;
+            }
+        }
+
+        __multiSampling = samples > 0;
+
+        if (!success)
+        {
+            checkErrorEGL("eglChooseConfig");
+            goto error;
+        }
+    }
+
+    __eglContext = eglCreateContext(__eglDisplay, __eglConfig, EGL_NO_CONTEXT, eglContextAttrs);
+    if (__eglContext == EGL_NO_CONTEXT)
+    {
+        checkErrorEGL("eglCreateContext");
+        goto error;
+    }
+
+    __eglSurface = eglCreateWindowSurface(__eglDisplay, __eglConfig, __screenWindow, eglSurfaceAttrs);
+    if (__eglSurface == EGL_NO_SURFACE)
+    {
+        checkErrorEGL("eglCreateWindowSurface");
+        goto error;
+    }
+
+    if (eglMakeCurrent(__eglDisplay, __eglSurface, __eglSurface, __eglContext) != EGL_TRUE)
+    {
+        checkErrorEGL("eglMakeCurrent");
+        goto error;
+    }
+
+    // Set vsync.
+    eglSwapInterval(__eglDisplay, screenSwapInterval);
+
+    // Initialize OpenGL ES extensions.
+    __glExtensions = (const char*)glGetString(GL_EXTENSIONS);
+
+    if (strstr(__glExtensions, "GL_OES_vertex_array_object") || strstr(__glExtensions, "GL_ARB_vertex_array_object"))
+    {
+        glBindVertexArray = (PFNGLBINDVERTEXARRAYOESPROC)eglGetProcAddress("glBindVertexArrayOES");
+        glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSOESPROC)eglGetProcAddress("glDeleteVertexArraysOES");
+        glGenVertexArrays = (PFNGLGENVERTEXARRAYSOESPROC)eglGetProcAddress("glGenVertexArraysOES");
+        glIsVertexArray = (PFNGLISVERTEXARRAYOESPROC)eglGetProcAddress("glIsVertexArrayOES");
+    }
+
+ #ifdef USE_BLACKBERRY_GAMEPAD
+
+    screen_device_t* screenDevs;
+
+    // Discover initial gamepad devices.
+    int count;
+    screen_get_context_property_iv(__screenContext, SCREEN_PROPERTY_DEVICE_COUNT, &count);
+    screenDevs = (screen_device_t*)calloc(count, sizeof(screen_device_t));
+    screen_get_context_property_pv(__screenContext, SCREEN_PROPERTY_DEVICES, (void**)screenDevs);
+
+	for (int i = 0; i < count; i++) 
+    {
+	    int type;
+        screen_get_device_property_iv(screenDevs[i], SCREEN_PROPERTY_TYPE, &type);
+
+        if (type == SCREEN_EVENT_GAMEPAD || type == SCREEN_EVENT_JOYSTICK)
+        {
+            int buttonCount = 0;
+            int joystickCount = 0;
+            int productId;
+            int vendorId;
+            char productString[64];
+            char vendorString[64];
+            queryGamepad(screenDevs[i], &buttonCount, &joystickCount, &productId, &vendorId, productString, vendorString);
+            Platform::gamepadEventConnectedInternal(screenDevs[i], buttonCount, joystickCount, 0, vendorId, productId, vendorString, productString);
+        }
+	}
+	free(screenDevs);
+#endif
+
+    return platform;
+
+error:
+
+    return NULL;
+}
+
+/**
+ * Convert the timespec into milliseconds.
+ */
+double timespec2millis(struct timespec *a)
+{
+    GP_ASSERT(a);
+    return (1000.0 * a->tv_sec) + (0.000001 * a->tv_nsec);
+}
+
+/**
+ * Fires a mouse event or a touch event on the game.
+ * If the mouse event is not consumed, a touch event is fired instead.
+ *
+ * @param mouseEvent The mouse event to fire.
+ * @param touchEvent The touch event to fire.
+ * @param x The x position of the touch in pixels.
+ * @param y The y position of the touch in pixels.
+ */
+void mouseOrTouchEvent(Mouse::MouseEvent mouseEvent, Touch::TouchEvent touchEvent, int x, int y)
+{
+    if (!gameplay::Platform::mouseEventInternal(mouseEvent, x, y, 0))
+    {
+        Platform::touchEventInternal(touchEvent, x, y, 0);
+    }
+}
+
+int Platform::enterMessagePump()
+{
+    GP_ASSERT(_game);
+
+    int rc;
+    int eventType;
+    int flags;
+    int value;
+    int position[2];
+    int domain;
+    mtouch_event_t touchEvent;
+    bool suspended = false;
+
+    // Get the initial time.
+    clock_gettime(CLOCK_REALTIME, &__timespec);
+    __timeStart = timespec2millis(&__timespec);
+    __timeAbsolute = 0L;
+
+    _game->run();
+
+    // Message loop.
+    while (true)
+    {
+        bps_event_t* event = NULL;
+        
+        while (true)
+        {
+            rc = bps_get_event(&event, 1);
+            GP_ASSERT(rc == BPS_SUCCESS);
+
+            if (event == NULL)
+                break;
+
+            domain = bps_event_get_domain(event);
+
+            if (domain == screen_get_domain())
+            {
+                __screenEvent = screen_event_get_event(event);
+                screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_TYPE, &eventType);
+                switch (eventType)
+                {
+                    case SCREEN_EVENT_MTOUCH_TOUCH:
+                    {
+                        screen_get_mtouch_event(__screenEvent, &touchEvent, 0);
+                        if (__gestureEventsProcessed.any())
+                            rc = gestures_set_process_event(__gestureSet, &touchEvent, NULL);
+
+                        if ( !rc && (__multiTouch || touchEvent.contact_id == 0) )
+                        {
+                            gameplay::Platform::touchEventInternal(Touch::TOUCH_PRESS, touchEvent.x, touchEvent.y, touchEvent.contact_id);
+                        }
+                        break;
+                    }
+
+                    case SCREEN_EVENT_MTOUCH_RELEASE:
+                    {
+                        screen_get_mtouch_event(__screenEvent, &touchEvent, 0);
+                        if (__gestureEventsProcessed.any())
+                            rc = gestures_set_process_event(__gestureSet, &touchEvent, NULL);
+
+                        if ( !rc && (__multiTouch || touchEvent.contact_id == 0) )
+                        {
+                            gameplay::Platform::touchEventInternal(Touch::TOUCH_RELEASE, touchEvent.x, touchEvent.y, touchEvent.contact_id);
+                        }
+                        if (__gestureSwipeRecognized)
+                        {
+                            __gestureSwipeRecognized = false;
+                        }
+                        break;
+                    }
+
+                    case SCREEN_EVENT_MTOUCH_MOVE:
+                    {
+                        screen_get_mtouch_event(__screenEvent, &touchEvent, 0);
+                        if (__gestureEventsProcessed.any())
+                            rc = gestures_set_process_event(__gestureSet, &touchEvent, NULL);
+
+                        if ( !rc && (__multiTouch || touchEvent.contact_id == 0) )
+                        {
+                            gameplay::Platform::touchEventInternal(Touch::TOUCH_MOVE, touchEvent.x, touchEvent.y, touchEvent.contact_id);
+                        }
+                        break;
+                    }
+
+                    case SCREEN_EVENT_POINTER:
+                    {
+                        static int mouse_pressed = 0;
+                        int buttons;
+                        int wheel;
+                        // A move event will be fired unless a button state changed.
+                        bool move = true;
+                        bool left_move = false;
+                        // This is a mouse move event, it is applicable to a device with a usb mouse or simulator.
+                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_BUTTONS, &buttons);
+                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_SOURCE_POSITION, position);
+                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_MOUSE_WHEEL, &wheel);
+
+                        // Handle left mouse. Interpret as touch if the left mouse event is not consumed.
+                        if (buttons & SCREEN_LEFT_MOUSE_BUTTON)
+                        {
+                            if (mouse_pressed & SCREEN_LEFT_MOUSE_BUTTON)
+                            {
+                                left_move = true;
+                            }
+                            else
+                            {
+                                move = false;
+                                mouse_pressed |= SCREEN_LEFT_MOUSE_BUTTON;
+                                mouseOrTouchEvent(Mouse::MOUSE_PRESS_LEFT_BUTTON, Touch::TOUCH_PRESS, position[0], position[1]);
+                            }
+                        }
+                        else if (mouse_pressed & SCREEN_LEFT_MOUSE_BUTTON)
+                        {
+                            move = false;
+                            mouse_pressed &= ~SCREEN_LEFT_MOUSE_BUTTON;
+                            mouseOrTouchEvent(Mouse::MOUSE_RELEASE_LEFT_BUTTON, Touch::TOUCH_RELEASE, position[0], position[1]);
+                        }
+
+                        // Handle right mouse.
+                        if (buttons & SCREEN_RIGHT_MOUSE_BUTTON)
+                        {
+                            if ((mouse_pressed & SCREEN_RIGHT_MOUSE_BUTTON) == 0)
+                            {
+                                move = false;
+                                mouse_pressed |= SCREEN_RIGHT_MOUSE_BUTTON;
+                                gameplay::Platform::mouseEventInternal(Mouse::MOUSE_PRESS_RIGHT_BUTTON, position[0], position[1], 0);
+                            }
+                        }
+                        else if (mouse_pressed & SCREEN_RIGHT_MOUSE_BUTTON)
+                        {
+                            move = false;
+                            mouse_pressed &= ~SCREEN_RIGHT_MOUSE_BUTTON;
+                            gameplay::Platform::mouseEventInternal(Mouse::MOUSE_RELEASE_RIGHT_BUTTON, position[0], position[1], 0);
+                        }
+
+                        // Handle middle mouse.
+                        if (buttons & SCREEN_MIDDLE_MOUSE_BUTTON)
+                        {
+                            if ((mouse_pressed & SCREEN_MIDDLE_MOUSE_BUTTON) == 0)
+                            {
+                                move = false;
+                                mouse_pressed |= SCREEN_MIDDLE_MOUSE_BUTTON;
+                                gameplay::Platform::mouseEventInternal(Mouse::MOUSE_PRESS_MIDDLE_BUTTON, position[0], position[1], 0);
+                            }
+                        }
+                        else if (mouse_pressed & SCREEN_MIDDLE_MOUSE_BUTTON)
+                        {
+                            move = false;
+                            mouse_pressed &= ~SCREEN_MIDDLE_MOUSE_BUTTON;
+                            gameplay::Platform::mouseEventInternal(Mouse::MOUSE_RELEASE_MIDDLE_BUTTON, position[0], position[1], 0);
+                        }
+
+                        // Fire a move event if none of the buttons changed.
+                        if (left_move)
+                        {
+                            mouseOrTouchEvent(Mouse::MOUSE_MOVE, Touch::TOUCH_MOVE, position[0], position[1]);
+                        }
+                        else if (move)
+                        {
+                            gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, position[0], position[1], 0);
+                        }
+
+                        // Handle mouse wheel events.
+                        if (wheel)
+                        {
+                            gameplay::Platform::mouseEventInternal(Mouse::MOUSE_WHEEL, position[0], position[1], -wheel);
+                        }
+                        break;
+                    }
+
+                    case SCREEN_EVENT_KEYBOARD:
+                    {
+                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_KEY_FLAGS, &flags);
+                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_KEY_SYM, &value);
+                        gameplay::Keyboard::KeyEvent evt = (flags & KEY_DOWN) ? gameplay::Keyboard::KEY_PRESS :  gameplay::Keyboard::KEY_RELEASE;
+                        // Suppress key repeats.
+                        if ((flags & KEY_REPEAT) == 0)
+                        {
+                            keyEventInternal(evt, getKey(value));
+                            if (evt == gameplay::Keyboard::KEY_PRESS && (flags & KEY_SYM_VALID))
+                            {
+                                int unicode = getUnicode(value);
+                                if (unicode)
+                                    keyEventInternal(gameplay::Keyboard::KEY_CHAR, unicode);
+                            }
+                        }
+                        break;
+                    }
+#ifdef USE_BLACKBERRY_GAMEPAD
+                    case SCREEN_EVENT_DEVICE:
+                    {
+                        // A device was attached or removed.
+                        screen_device_t device;
+                        int attached;
+
+                        screen_get_event_property_pv(__screenEvent, SCREEN_PROPERTY_DEVICE, (void**)&device);
+                        screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_ATTACHED, &attached);
+
+                        if (attached)
+                        {
+                            int type;
+                            screen_get_device_property_iv(device, SCREEN_PROPERTY_TYPE, &type);
+                            if (type == SCREEN_EVENT_GAMEPAD || type == SCREEN_EVENT_JOYSTICK)
+                            {
+                                int buttonCount = 0;
+                                int joystickCount = 0;
+                                int productId;
+                                int vendorId;
+                                char productString[64];
+                                char vendorString[64];
+                                queryGamepad(device, &buttonCount, &joystickCount, &productId, &vendorId, productString, vendorString);
+                                Platform::gamepadEventConnectedInternal(device, buttonCount, joystickCount, 0, vendorId, productId, vendorString, productString);
+                            }
+                        }
+                        else
+                        {
+                            Platform::gamepadEventDisconnectedInternal(device);
+                        }
+
+                        break;
+                    }
+#endif
+                    default:
+                        break;
+                }
+            }
+            else if (domain == navigator_get_domain())
+            {
+                switch (bps_event_get_code(event))
+                {
+                case NAVIGATOR_SWIPE_DOWN:
+                    _game->menuEvent();
+                    break;
+                case NAVIGATOR_WINDOW_STATE:
+                {
+                    navigator_window_state_t state = navigator_event_get_window_state(event);
+                    switch (state)
+                    {
+                    case NAVIGATOR_WINDOW_FULLSCREEN:
+                        if (!__screenFullscreen)
+                            __screenFullscreen = true;
+                        _game->resume();
+                        suspended = false;
+                        break;
+                    case NAVIGATOR_WINDOW_THUMBNAIL:
+                    case NAVIGATOR_WINDOW_INVISIBLE:
+                        if (__screenFullscreen && !suspended)
+                        {
+                            _game->pause();
+                            suspended = true;
+                        }
+                        break;
+                    }
+                    break;
+                }
+                case NAVIGATOR_EXIT:
+                	// Call Game::shutdown directly, instead of Game::exit.
+                	// We need to do this since exit() queues a request to shutdown for the
+                	// next frame, which will never get executed because we are suspended.
+                    _game->shutdown();
+                    break;
+                }
+            }
+            else if (domain == sensor_get_domain())
+            {
+                if (bps_event_get_code(event) == SENSOR_AZIMUTH_PITCH_ROLL_READING)
+                {
+                    float azimuth;
+                    sensor_event_get_apr(event, &azimuth, &__pitch, &__roll);
+                }
+            }
+        }
+
+        // If we are done, then exit.
+        if (_game->getState() == Game::UNINITIALIZED)
+            break;
+
+        if (!suspended)
+        {
+            _game->frame();
+
+            // Post the new frame to the display.
+            // Note that there are a couple cases where eglSwapBuffers could fail
+            // with an error code that requires a certain level of re-initialization:
+            //
+            // 1) EGL_BAD_NATIVE_WINDOW - Called when the surface we're currently using
+            //    is invalidated. This would require us to destroy our EGL surface,
+            //    close our OpenKODE window, and start again.
+            //
+            // 2) EGL_CONTEXT_LOST - Power management event that led to our EGL context
+            //    being lost. Requires us to re-create and re-initalize our EGL context
+            //    and all OpenGL ES state.
+            //
+            // For now, if we get these, we'll simply exit.
+            rc = eglSwapBuffers(__eglDisplay, __eglSurface);
+            if (rc != EGL_TRUE)
+            {
+                _game->shutdown();
+                perror("eglSwapBuffers");
+                break;
+            }
+        }
+    }
+
+    screen_stop_events(__screenContext);
+    bps_shutdown();
+    screen_destroy_context(__screenContext);
+
+    return 0;
+}
+    
+void Platform::signalShutdown() 
+{
+    // nothing to do  
+}
+
+bool Platform::canExit()
+{
+    return true;
+}
+
+unsigned int Platform::getDisplayWidth()
+{
+    return __screenWindowSize[0];
+}
+
+unsigned int Platform::getDisplayHeight()
+{
+    return __screenWindowSize[1];
+}
+
+double Platform::getAbsoluteTime()
+{
+    clock_gettime(CLOCK_REALTIME, &__timespec);
+    double now = timespec2millis(&__timespec);
+    __timeAbsolute = now - __timeStart;
+
+    return __timeAbsolute;
+}
+
+void Platform::setAbsoluteTime(double time)
+{
+    __timeAbsolute = time;
+}
+
+bool Platform::isVsync()
+{
+    return __vsync;
+}
+
+void Platform::setVsync(bool enable)
+{
+    eglSwapInterval(__eglDisplay, enable ? 1 : 0);
+    __vsync = enable;
+}
+
+void Platform::swapBuffers()
+{
+    if (__eglDisplay && __eglSurface)
+        eglSwapBuffers(__eglDisplay, __eglSurface);
+}
+
+void Platform::sleep(long ms)
+{
+    usleep(ms * 1000);
+}
+
+void Platform::setMultiSampling(bool enabled)
+{
+    if (enabled == __multiSampling)
+    {
+        return;
+    }
+
+    //todo
+
+    __multiSampling = enabled;
+}
+
+bool Platform::isMultiSampling()
+{
+    return __multiSampling;
+}
+
+void Platform::setMultiTouch(bool enabled)
+{
+    __multiTouch = enabled;
+}
+
+bool Platform::isMultiTouch()
+{
+    return __multiTouch;
+}
+
+void Platform::getAccelerometerValues(float* pitch, float* roll)
+{
+    GP_ASSERT(pitch);
+    GP_ASSERT(roll);
+
+    switch(__orientationAngle)
+    {
+    // Landscape based device adjusting for landscape game mode
+    case 0:
+        if (pitch)
+            *pitch = __pitch;
+        if (roll)
+            *roll = -__roll;
+        break;
+    case 180:
+        if (pitch)
+            *pitch = -__pitch;
+        if (roll)
+            *roll = __roll;
+        break;
+
+    // Portrait based device adjusting for landscape game mode
+    case 90:
+        if (pitch)
+            *pitch = -__roll;
+        if (roll)
+            *roll = -__pitch;
+        break;
+
+    case  270:
+        if (pitch)
+            *pitch = __roll;
+        if (roll)
+            *roll = __pitch;
+        break;
+
+    default:
+        break;
+    }
+}
+
+void Platform::getArguments(int* argc, char*** argv)
+{
+    if (argc)
+        *argc = __argc;
+    if (argv)
+        *argv = __argv;
+}
+
+bool Platform::hasMouse()
+{
+    // not supported
+    return false;
+}
+
+void Platform::setMouseCaptured(bool captured)
+{
+    // not supported
+}
+
+bool Platform::isMouseCaptured()
+{
+    // not supported
+    return false;
+}
+
+void Platform::setCursorVisible(bool visible)
+{
+    // not supported
+}
+
+bool Platform::isCursorVisible()
+{
+    // not supported
+    return false;
+}
+
+void Platform::displayKeyboard(bool display)
+{
+    if (display)
+        virtualkeyboard_show();
+    else
+        virtualkeyboard_hide();
+}
+
+void Platform::shutdownInternal()
+{
+    Game::getInstance()->shutdown();
+}
+
+bool Platform::isGestureSupported(Gesture::GestureEvent evt)
+{
+    // All are supported no need to test the bitset
+    return true;
+}
+
+void Platform::registerGesture(Gesture::GestureEvent evt)
+{
+    switch(evt)
+    {
+    case Gesture::GESTURE_ANY_SUPPORTED:
+        __gestureEventsProcessed.set();
+        break;
+
+    case Gesture::GESTURE_SWIPE:
+    case Gesture::GESTURE_PINCH:
+    case Gesture::GESTURE_TAP:
+        __gestureEventsProcessed.set(evt);
+        break;
+
+    default:
+        break;
+    }
+}
+
+void Platform::unregisterGesture(Gesture::GestureEvent evt)
+{
+    switch(evt)
+    {
+    case Gesture::GESTURE_ANY_SUPPORTED:
+        __gestureEventsProcessed.reset();
+        break;
+
+    case Gesture::GESTURE_SWIPE:
+    case Gesture::GESTURE_PINCH:
+    case Gesture::GESTURE_TAP:
+        __gestureEventsProcessed.set(evt, 0);
+        break;
+
+    default:
+        break;
+    }
+}
+    
+bool Platform::isGestureRegistered(Gesture::GestureEvent evt)
+{
+    return __gestureEventsProcessed.test(evt);
+}
+
+bool Platform::launchURL(const char* url)
+{
+    if (url == NULL || *url == '\0')
+        return false;
+
+    return navigator_invoke(url, NULL) == BPS_SUCCESS;
+}
+
+}
+
+#endif

+ 29 - 18
gameplay/src/PlatformLinux.cpp

@@ -25,6 +25,9 @@
 
 using namespace std;
 
+int __argc = 0;
+char** __argv = 0;
+
 enum GamepadAxisInfoFlags
 {
     GP_AXIS_SKIP = 0x1,
@@ -1398,21 +1401,21 @@ namespace gameplay
         usleep(ms * 1000);
     }
 
-    void Platform::setMultiSampling(bool enabled)
-    {
-        if (enabled == __multiSampling)
-        {
-            return;
-        }
-        
-            //todo
-            
-            __multiSampling = enabled;
-    }
-    
-        bool Platform::isMultiSampling()
-        {
-            return __multiSampling;
+    void Platform::setMultiSampling(bool enabled)
+    {
+        if (enabled == __multiSampling)
+        {
+            return;
+        }
+        
+            //todo
+            
+            __multiSampling = enabled;
+    }
+    
+        bool Platform::isMultiSampling()
+        {
+            return __multiSampling;
         }
 
     void Platform::setMultiTouch(bool enabled)
@@ -1434,6 +1437,14 @@ namespace gameplay
         *roll = __roll;
     }
 
+    void Platform::getArguments(int* argc, char*** argv)
+    {
+        if (argc)
+            *argc = __argc;
+        if (argv)
+            *argv = __argv;
+    }
+
     bool Platform::hasMouse()
     {
         return true;
@@ -1505,10 +1516,10 @@ namespace gameplay
         // not supported
     }
 
-    void Platform::shutdownInternal()
-    {
+    void Platform::shutdownInternal()
+    {
         closeAllGamepads();
-        Game::getInstance()->shutdown();
+        Game::getInstance()->shutdown();
     }
 
     bool Platform::isGestureSupported(Gesture::GestureEvent evt)

+ 2285 - 2274
gameplay/src/PlatformMacOSX.mm

@@ -1,2274 +1,2285 @@
-#ifdef __APPLE__
-
-#include "Base.h"
-#include "Platform.h"
-#include "FileSystem.h"
-#include "Game.h"
-#include "Form.h"
-#include "ScriptController.h"
-#include <unistd.h>
-#include <IOKit/hid/IOHIDLib.h>
-#import <Cocoa/Cocoa.h>
-#import <QuartzCore/CVDisplayLink.h>
-#import <OpenGL/OpenGL.h>
-#import <mach/mach_time.h>
-#import <Foundation/Foundation.h>
-
-// These should probably be moved to a platform common file
-#define SONY_USB_VENDOR_ID              0x054c
-#define SONY_USB_PS3_PRODUCT_ID         0x0268
-#define MICROSOFT_VENDOR_ID             0x045e
-#define MICROSOFT_XBOX360_PRODUCT_ID    0x028e
-#define STEELSERIES_VENDOR_ID           0x1038
-#define STEELSERIES_FREE_PRODUCT_ID     0x1412
-#define FRUCTEL_VENDOR_ID               0x25B6
-#define FRUCTEL_GAMETEL_PRODUCT_ID      0x0001
-
-using namespace std;
-using namespace gameplay;
-
-@class View;
-@class HIDGamepad;
-
-// Default to 720p
-static int __width = 1280;
-static int __height = 720;
-
-static float ACCELEROMETER_FACTOR_X = 90.0f / __width;
-static float ACCELEROMETER_FACTOR_Y = 90.0f / __height;
-
-static double __timeStart;
-static double __timeAbsolute;
-static bool __vsync = WINDOW_VSYNC;
-static float __pitch;
-static float __roll;
-static int __lx;
-static int __ly;
-static bool __hasMouse = false;
-static bool __leftMouseDown = false;
-static bool __rightMouseDown = false;
-static bool __otherMouseDown = false;
-static bool __shiftDown = false;
-static char* __title = NULL;
-static bool __fullscreen = false;
-static bool __resizable = false;
-static void* __attachToWindow = NULL;
-static bool __mouseCaptured = false;
-static bool __mouseCapturedFirstPass = false;
-static CGPoint __mouseCapturePoint;
-static bool __multiSampling = false;
-static bool __cursorVisible = true;
-static View* __view = NULL;
-
-static NSMutableDictionary *__activeGamepads = NULL;
-static NSMutableArray *__gamepads = NULL;
-static IOHIDManagerRef __hidManagerRef = NULL;
-
-// Gamepad Helper Function
-HIDGamepad *gamepadForLocationID(NSNumber *locationID);
-HIDGamepad *gamepadForLocationIDValue(unsigned int locationIDValue);
-HIDGamepad *gamepadForGameHandle(int gameHandle);
-
-
-// IOHid Helper Functions
-CFMutableDictionaryRef IOHIDCreateDeviceMatchingDictionary(UInt32 inUsagePage, UInt32 inUsage);
-CFStringRef IOHIDDeviceGetStringProperty(IOHIDDeviceRef deviceRef, CFStringRef key);
-int IOHIDDeviceGetIntProperty(IOHIDDeviceRef deviceRef, CFStringRef key);
-
-// IOHid Callbacks
-static void hidDeviceDiscoveredCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef);
-static void hidDeviceRemovalCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef);
-static void hidDeviceValueAvailableCallback(void *inContext, IOReturn inResult,  void *inSender);
-
-double getMachTimeInMilliseconds()
-{
-    static const double kOneMillion = 1000 * 1000;
-    static mach_timebase_info_data_t s_timebase_info;
-    
-    if (s_timebase_info.denom == 0) 
-        (void) mach_timebase_info(&s_timebase_info);
-    
-    // mach_absolute_time() returns billionth of seconds, so divide by one million to get milliseconds
-    GP_ASSERT(s_timebase_info.denom);
-    return ((double)mach_absolute_time() * (double)s_timebase_info.numer) / (kOneMillion * (double)s_timebase_info.denom);
-}
-
-@interface HIDGamepadAxis : NSObject
-{
-    IOHIDElementRef e;
-    CFIndex v;
-    CFIndex logMin;
-    CFIndex logMax;
-}
-
-+ gamepadAxisWithAxisElement:(IOHIDElementRef)element;
-- initWithAxisElement:(IOHIDElementRef)element;
-- (IOHIDElementRef)element;
-- (IOHIDElementCookie)cookie;
-- (uint32_t)usage;
-- (uint32_t)usagePage;
-- (CFIndex)logicalMinimum;
-- (CFIndex)logicalMaximum;
-
-- (float)calibratedValue;
-- (CFIndex)value;
-- (void)setValue:(CFIndex)value;
-@end
-
-@implementation HIDGamepadAxis
-+ gamepadAxisWithAxisElement:(IOHIDElementRef)element
-{
-    return [[[[self class] alloc] initWithAxisElement:element] autorelease];
-}
-
-- initWithAxisElement:(IOHIDElementRef)element
-{
-    if((self = [super init]))
-    {
-        e = (IOHIDElementRef)CFRetain(element);
-    }
-    return self;
-}
-
-- (void)dealloc
-{
-    CFRelease(e);
-    [super dealloc];
-}
-
-- (IOHIDElementRef)element
-{
-    return e;
-}
-
-- (IOHIDElementCookie)cookie
-{
-    return IOHIDElementGetCookie(e);
-}
-
-- (bool)isHatSwitch {
-    return (IOHIDElementGetUsage(e) == kHIDUsage_GD_Hatswitch);
-}
-
-- (uint32_t)usage
-{
-    return IOHIDElementGetUsage(e);
-}
-
-- (uint32_t)usagePage
-{
-    return IOHIDElementGetUsagePage(e);
-}
-
-- (CFIndex)logicalMinimum
-{
-    return IOHIDElementGetLogicalMin(e);    
-}
-
-- (CFIndex)logicalMaximum
-{
-    return IOHIDElementGetLogicalMax(e);
-}
-
-- (float)calibratedValue
-{
-    float cmax = 2.0f;
-    float cmin = 0.0f;
-    return ((((v - [self logicalMinimum]) * (cmax - cmin)) / ([self logicalMaximum] - [self logicalMinimum])) + cmin - 1.0f);    
-}
-
-- (CFIndex)value
-{
-    return v;
-}
-
-- (void)setValue:(CFIndex)value
-{
-    v = value;
-}
-@end
-
-@interface HIDGamepadButton : NSObject
-{
-    IOHIDElementRef e;
-    IOHIDElementRef te;
-    bool state;
-    int triggerValue;
-}
-
-+ gamepadButtonWithButtonElement:(IOHIDElementRef)element;
-- initWithButtonElement:(IOHIDElementRef)element;
-- (void)setTriggerElement:(IOHIDElementRef)element;
-- (IOHIDElementRef)element;
-- (IOHIDElementCookie)cookie;
-- (IOHIDElementRef)triggerElement;
-- (IOHIDElementCookie)triggerCookie;
-
-- (bool)isTriggerButton;
-- (uint32_t)usage;
-- (uint32_t)usagePage;
-- (int)stateValue;
-- (float)calibratedStateValue;
-- (void)setStateValue:(int)value;
-- (bool)state;
-- (void)setState:(bool)state;
-@end
-
-@implementation HIDGamepadButton
-+ gamepadButtonWithButtonElement:(IOHIDElementRef)element
-{
-    return [[[[self class] alloc] initWithButtonElement:element] autorelease];
-}
-
-- initWithButtonElement:(IOHIDElementRef)element
-{
-    if((self = [super init]))
-    {
-        e = (IOHIDElementRef)CFRetain(element);
-        te = NULL;
-        state = false;
-    }
-    return self;
-}
-
-- (void)dealloc
-{
-    CFRelease(e);
-    if(te != NULL) CFRelease(te);
-    [super dealloc];
-}
-
-- (void)setTriggerElement:(IOHIDElementRef)element {
-    if(te)
-    {
-        CFRelease(te);
-        te = NULL;
-    }
-    if(element)
-    {
-        te = (IOHIDElementRef)CFRetain(element);
-    }
-}
-
-- (IOHIDElementRef)element
-{
-    return e;
-}
-
-- (IOHIDElementCookie)cookie
-{
-    return IOHIDElementGetCookie(e);
-}
-
-- (IOHIDElementRef)triggerElement
-{
-    return te;
-}
-
-- (IOHIDElementCookie)triggerCookie
-{
-    return IOHIDElementGetCookie(te);
-}
-
-- (bool)isTriggerButton
-{
-    return (te != NULL);
-}
-
-- (uint32_t)usage
-{
-    return IOHIDElementGetUsage(e);
-}
-
-- (uint32_t)usagePage
-{
-    return IOHIDElementGetUsagePage(e);
-}
-
-- (void)setStateValue:(int)value {
-    triggerValue = value;
-}
-
-- (int)stateValue
-{
-    return triggerValue;
-}
-
-- (float)calibratedStateValue
-{
-    return (float)triggerValue / 255.0f;
-}
-
-- (bool)state
-{
-    return state;
-}
-
-- (void)setState:(bool)s
-{
-    state = s;
-}
-@end
-
-@interface HIDGamepad : NSObject
-{
-    IOHIDDeviceRef hidDeviceRef;
-    IOHIDQueueRef queueRef;
-    NSMutableArray* buttons;
-    NSMutableArray* triggerButtons;
-    NSMutableArray* axes;
-    HIDGamepadAxis* hatSwitch;
-}
-@property (assign) IOHIDDeviceRef hidDeviceRef;
-@property (assign) IOHIDQueueRef queueRef;
-@property (retain) NSMutableArray* buttons;
-@property (retain) NSMutableArray* triggerButtons;
-@property (retain) NSMutableArray* axes;
-@property (retain) HIDGamepadAxis* hatSwitch;
-
-- initWithDevice:(IOHIDDeviceRef)rawDevice;
-- (IOHIDDeviceRef)rawDevice;
-- (NSNumber*)locationID;
-
-- (void)initializeGamepadElements;
-- (HIDGamepadButton*)buttonWithCookie:(IOHIDElementCookie)cookie;
-
-- (bool)startListening;
-- (void)stopListening;
-
-- (NSString*)identifierName;
-- (NSString*)productName;
-- (NSString*)manufacturerName;
-- (NSString*)serialNumber;
-- (int)versionNumber;
-- (int)vendorID;
-- (int)productID;
-
-- (NSUInteger)numberOfAxes;
-- (NSUInteger)numberOfSticks;
-- (NSUInteger)numberOfButtons;
-- (NSUInteger)numberOfTriggerButtons;
-- (HIDGamepadAxis*)axisAtIndex:(NSUInteger)index;
-- (HIDGamepadButton*)buttonAtIndex:(NSUInteger)index;
-- (HIDGamepadButton*)triggerButtonAtIndex:(NSUInteger)index;
-- (HIDGamepadAxis*)getHatSwitch;
-@end
-
-@implementation HIDGamepad
-
-@synthesize hidDeviceRef;
-@synthesize queueRef;
-@synthesize buttons;
-@synthesize triggerButtons;
-@synthesize axes;
-@synthesize hatSwitch;
-
-- initWithDevice:(IOHIDDeviceRef)rawDevice
-{
-    if((self = [super init]))
-    {
-        [self setButtons:[NSMutableArray array]];
-        [self setTriggerButtons:[NSMutableArray array]];
-        [self setAxes:[NSMutableArray array]];
-        hatSwitch = NULL;
-
-        CFRetain(rawDevice);
-        IOHIDQueueRef queue = IOHIDQueueCreate(CFAllocatorGetDefault(), rawDevice, 10, kIOHIDOptionsTypeNone);
-        [self setHidDeviceRef:rawDevice];
-        [self setQueueRef:queue];
-        
-        [self initializeGamepadElements];
-        [self startListening];
-    }
-    return self;
-}
-
-- (void)dealloc
-{
-    [self stopListening];
-    
-    CFRelease([self rawDevice]);
-    CFRelease([self queueRef]);
-    [self setQueueRef:NULL];
-    [self setHidDeviceRef:NULL];
-    
-    [self setButtons:NULL];
-    [self setTriggerButtons:NULL];
-    [self setAxes:NULL];
-    if (hatSwitch != NULL)
-    {
-        [hatSwitch dealloc];
-    }
-    
-    [super dealloc];
-}
-
-- (IOHIDDeviceRef)rawDevice
-{
-    return [self hidDeviceRef];
-}
-
-- (NSNumber*)locationID
-{
-    return (NSNumber*)IOHIDDeviceGetProperty([self rawDevice], CFSTR(kIOHIDLocationIDKey));
-}
-
-- (void)initializeGamepadElements
-{
-    uint32_t vendorID = [self vendorID];
-    uint32_t productID = [self productID];
-    
-    CFArrayRef elements = IOHIDDeviceCopyMatchingElements([self rawDevice], NULL, kIOHIDOptionsTypeNone);
-    for(int i = 0; i < CFArrayGetCount(elements); i++)
-    {
-        IOHIDElementRef hidElement = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
-        IOHIDElementType type = IOHIDElementGetType(hidElement);
-
-        if (type == kIOHIDElementTypeInput_Misc || type == kIOHIDElementTypeInput_Axis)
-        {
-            uint32_t pageUsage = IOHIDElementGetUsage(hidElement);
-            IOHIDElementCookie cookie = IOHIDElementGetCookie(hidElement);
-
-            switch(pageUsage)
-            {
-                case kHIDUsage_GD_X:
-                case kHIDUsage_GD_Y:
-                case kHIDUsage_GD_Rx:
-                case kHIDUsage_GD_Ry:
-                case kHIDUsage_GD_Z:
-                case kHIDUsage_GD_Rz:
-                {
-                    if (vendorID == MICROSOFT_VENDOR_ID &&
-                        productID == MICROSOFT_XBOX360_PRODUCT_ID &&
-                        (pageUsage == kHIDUsage_GD_Z || pageUsage == kHIDUsage_GD_Rz))
-                    {
-                        HIDGamepadButton* triggerButton = [HIDGamepadButton gamepadButtonWithButtonElement:hidElement];
-                        [triggerButton setTriggerElement:hidElement];
-                        [[self triggerButtons] addObject:triggerButton];
-                    }
-                    else
-                    {
-                        HIDGamepadAxis* axis = [HIDGamepadAxis gamepadAxisWithAxisElement:hidElement];
-                        [[self axes] addObject:axis];
-                    }
-                    break;
-                }
-                case kHIDUsage_GD_Hatswitch:
-                {
-                    HIDGamepadAxis* hat = [[HIDGamepadAxis alloc] initWithAxisElement:hidElement];
-                    [hat setValue: -1];
-                    hatSwitch = hat;
-                }
-                default:
-                    // Ignore the pointers
-                    // Note: Some of the pointers are for the 6-axis accelerometer in a PS3 controller
-                    // Note: L2/R2 triggers are at cookie 39 and 40 base 10 tied to 9 and 10 button elements
-                    break;
-            }
-
-        }
-        if(type == kIOHIDElementTypeInput_Button)
-        {
-            HIDGamepadButton *button = [HIDGamepadButton gamepadButtonWithButtonElement:hidElement];
-            [[self buttons] addObject:button];
-        }
-    }
-    // Go back and get proprietary information (e.g. triggers) and associate with appropriate values
-    // Example for other trigger buttons
-    for(int i = 0; i < CFArrayGetCount(elements); i++)
-    {
-        IOHIDElementRef hidElement = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
-        IOHIDElementType type = IOHIDElementGetType(hidElement);
-        IOHIDElementCookie cookie = IOHIDElementGetCookie(hidElement);
-        
-        // Gamepad specific code
-        if(vendorID == SONY_USB_VENDOR_ID && productID == SONY_USB_PS3_PRODUCT_ID)
-        {
-            if((unsigned long)cookie == 39)
-            {
-                HIDGamepadButton *leftTriggerButton = [self buttonWithCookie:(IOHIDElementCookie)9];
-                if(leftTriggerButton)
-                {
-                    [leftTriggerButton setTriggerElement:hidElement];
-                    [[self triggerButtons] addObject:leftTriggerButton];
-                }
-            }
-            if((unsigned long)cookie == 40)
-            {
-                HIDGamepadButton *rightTriggerButton = [self buttonWithCookie:(IOHIDElementCookie)10];
-                if(rightTriggerButton)
-                {
-                    [rightTriggerButton setTriggerElement:hidElement];
-                    [[self triggerButtons] addObject:rightTriggerButton];
-                }
-            }
-        }
-    }
-}
-
-- (HIDGamepadButton*)buttonWithCookie:(IOHIDElementCookie)cookie {
-    for(HIDGamepadButton *b in [self buttons]) {
-        if([b cookie] == cookie)
-            return b;
-    }
-    return NULL;
-}
-
-- (bool)startListening
-{
-    IOReturn kr = IOHIDDeviceOpen([self hidDeviceRef], kIOHIDOptionsTypeNone);
-    if(kr != 0) {
-        return false;
-    }
-    IOHIDDeviceScheduleWithRunLoop([self hidDeviceRef], CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
-    
-    IOHIDQueueStart([self queueRef]);
-    IOHIDQueueRegisterValueAvailableCallback([self queueRef], hidDeviceValueAvailableCallback, self);
-    
-    CFArrayRef elements = (CFArrayRef)[self watchedElements];
-    for(int i = 0; i < CFArrayGetCount(elements); i++)
-    {
-        IOHIDElementRef hidElement = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
-        IOHIDQueueAddElement([self queueRef], hidElement);
-    }
-    
-    IOHIDQueueScheduleWithRunLoop([self queueRef], CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
-    
-    return true;
-}
-
-- (void)stopListening
-{
-    IOHIDQueueUnscheduleFromRunLoop([self queueRef], CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
-    IOHIDQueueStop([self queueRef]);
-    
-    IOHIDDeviceUnscheduleFromRunLoop([self hidDeviceRef], CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
-    IOHIDDeviceClose([self hidDeviceRef], kIOHIDOptionsTypeNone);
-}
-
-- (NSString*)identifierName
-{
-    NSString* idName = NULL;
-    if(idName == NULL) idName = [self productName];
-    if(idName == NULL) idName = [self manufacturerName];
-    if(idName == NULL) idName = [self serialNumber];
-    if(idName == NULL) idName = [NSString stringWithFormat:@"%d-%d", [self vendorID], [self productID]];
-    return idName;
-}
-
-- (NSString*)productName
-{
-    CFStringRef productName = (CFStringRef)IOHIDDeviceGetProperty([self rawDevice], CFSTR(kIOHIDProductKey));
-    if(productName == NULL || CFGetTypeID(productName) != CFStringGetTypeID())
-    {
-        return NULL;
-    }
-    return (NSString*)productName;
-}
-
-- (NSString*)manufacturerName
-{
-    CFStringRef manufacturerName = (CFStringRef)IOHIDDeviceGetProperty([self rawDevice], CFSTR(kIOHIDManufacturerKey));
-    if(manufacturerName == NULL || CFGetTypeID(manufacturerName) != CFStringGetTypeID())
-    {
-        return NULL;
-    }
-    return (NSString*)manufacturerName;
-}
-
-- (NSString*)serialNumber
-{
-    CFStringRef serialNumber = (CFStringRef)IOHIDDeviceGetProperty([self rawDevice], CFSTR(kIOHIDSerialNumberKey));
-    if(serialNumber == NULL || CFGetTypeID(serialNumber) != CFStringGetTypeID())
-    {
-        return NULL;
-    }
-    return (NSString*)serialNumber;
-}
-
-- (int)versionNumber
-{
-    return IOHIDDeviceGetIntProperty([self rawDevice], CFSTR(kIOHIDVersionNumberKey));
-}
-
-- (int)vendorID
-{
-    return IOHIDDeviceGetIntProperty([self rawDevice], CFSTR(kIOHIDVendorIDKey));
-}
-
-- (int)productID
-{
-    return IOHIDDeviceGetIntProperty([self rawDevice], CFSTR(kIOHIDProductIDKey));
-}
-
-- (NSUInteger)numberOfAxes
-{
-    return [[self axes] count];
-}
-
-- (NSUInteger)numberOfSticks
-{
-    return ([[self axes] count] / 2);
-}
-
-- (NSUInteger)numberOfButtons
-{
-    return [[self buttons] count];
-}
-
-- (NSUInteger)numberOfTriggerButtons
-{
-    return [[self triggerButtons] count];
-}
-
-- (HIDGamepadButton*)triggerButtonAtIndex:(NSUInteger)index
-{
-    HIDGamepadButton *b = NULL;
-    if(index < [[self triggerButtons] count])
-    {
-        b = [[self triggerButtons] objectAtIndex:index];
-    }
-    return b;
-}
-
-- (HIDGamepadAxis*)axisAtIndex:(NSUInteger)index
-{
-    HIDGamepadAxis *a = NULL;
-    if(index < [[self axes] count])
-    {
-        a = [[self axes] objectAtIndex:index];
-    }
-    return a;
-}
-
-- (HIDGamepadButton*)buttonAtIndex:(NSUInteger)index
-{
-    HIDGamepadButton *b = NULL;
-    if(index < [[self buttons] count])
-    {
-        b = [[self buttons] objectAtIndex:index];
-    }
-    return b;
-}
-
-- (HIDGamepadAxis*)getHatSwitch
-{
-    if (hatSwitch != NULL)
-    {
-        return hatSwitch;
-    }
-    return NULL;
-}
-
-- (NSArray*)watchedElements
-{
-    NSMutableArray *r = [NSMutableArray array];
-    for(HIDGamepadButton *b in [self buttons])
-    {
-        [r addObject:(id)[b element]];
-    }
-    for(HIDGamepadAxis *a in [self axes])
-    {
-        [r addObject:(id)[a element]];
-    }
-    for(HIDGamepadButton* t in [self triggerButtons])
-    {
-        [r addObject:(id)[t triggerElement]];
-    }
-    if (hatSwitch)
-    {
-        [r addObject:(id)[hatSwitch element]];
-    }
-    return [NSArray arrayWithArray:r];
-}
-
-- (void)hidValueAvailable:(IOHIDValueRef)value
-{
-    IOHIDElementRef element = IOHIDValueGetElement(value);
-    IOHIDElementCookie cookie = IOHIDElementGetCookie(element);
-    
-    if(IOHIDValueGetLength(value) > 4) return; // saftey precaution for PS3 cotroller
-    CFIndex integerValue = IOHIDValueGetIntegerValue(value);
-    
-    for(HIDGamepadAxis *a in [self axes])
-    {
-        if([a cookie] == cookie)
-        {
-            [a setValue:integerValue];
-        }
-    }
-    
-    for(HIDGamepadButton *b in [self buttons])
-    {
-        if([b cookie] == cookie)
-        {
-            [b setState:(bool)integerValue];
-            break;
-        }
-    }
-    
-    for(HIDGamepadButton *b in [self triggerButtons])
-    {
-        if([b triggerCookie] == cookie)
-        {
-            [b setStateValue:integerValue];
-            break;
-        }
-    }
-
-    if (hatSwitch && [hatSwitch cookie] == cookie)
-    {
-        [hatSwitch setValue:integerValue];
-    }
-}
-@end
-
-
-@interface View : NSOpenGLView <NSWindowDelegate>
-{
-@public
-    CVDisplayLinkRef displayLink;
-    NSRecursiveLock* gameLock;
-
-@protected
-    Game* _game;
-    unsigned int _gestureEvents;
-}    
-- (void) detectGamepads: (Game*) game;
-
-@end
-
-
-@implementation View
-
--(void)windowWillClose:(NSNotification*)note 
-{
-    [gameLock lock];
-    _game->exit();
-    [gameLock unlock];
-    [[NSApplication sharedApplication] terminate:self];
-}
-
-- (void)windowDidResize:(NSNotification*)notification
-{
-    [gameLock lock];
-    NSSize size = [ [ _window contentView ] frame ].size;
-    gameplay::Platform::resizeEventInternal((unsigned int)size.width, (unsigned int)size.height);
-    [gameLock unlock];
-}
-
-- (CVReturn) getFrameForTime:(const CVTimeStamp*)outputTime
-{
-    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
-    
-    [self update];
-    
-    [pool release];
-    
-    return kCVReturnSuccess;
-}
-
-- (void) detectGamepads: (Game*) game
-{
-    // Locate any newly connected devices
-    for(HIDGamepad* gamepad in __gamepads)
-    {
-        NSNumber* locationID = [gamepad locationID];
-        if([__activeGamepads objectForKey:locationID] == NULL)
-        {            
-            // Gameplay::add is friended to Platform, but we're not in Platform right now.
-            Platform::gamepadEventConnectedInternal((unsigned int)[locationID intValue],
-                                                    [gamepad numberOfButtons],
-                                                    [gamepad numberOfSticks],
-                                                    [gamepad numberOfTriggerButtons],
-                                                    [gamepad vendorID],
-                                                    [gamepad productID],
-                                                    [[gamepad manufacturerName] cStringUsingEncoding:NSASCIIStringEncoding],
-                                                    [[gamepad productName] cStringUsingEncoding:NSASCIIStringEncoding]);
-
-            [__activeGamepads setObject:locationID forKey:locationID];
-        }
-    }
-    
-    // Detect any disconnected gamepads
-    NSMutableArray* deadGamepads = [NSMutableArray array];
-    for(NSNumber* locationID in __activeGamepads)
-    {
-        HIDGamepad* gamepad = gamepadForLocationID(locationID);
-        if(gamepad == NULL)
-        {
-            NSNumber* gameHandle = [__activeGamepads objectForKey:locationID];
-            Platform::gamepadEventDisconnectedInternal((unsigned int)[locationID intValue]);
-            [deadGamepads addObject:locationID];
-        }
-    }
-    [__activeGamepads removeObjectsForKeys:deadGamepads];
-}
-
--(void) update
-{       
-    [gameLock lock];
-
-    [[self openGLContext] makeCurrentContext];
-    CGLLockContext((CGLContextObj)[[self openGLContext] CGLContextObj]);
-    if (_game)
-    {
-        [self detectGamepads: _game];
-        
-        _game->frame();
-    }
-    CGLFlushDrawable((CGLContextObj)[[self openGLContext] CGLContextObj]);
-    CGLUnlockContext((CGLContextObj)[[self openGLContext] CGLContextObj]);  
-
-    [gameLock unlock];
-}
-
-static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, 
-                                      CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
-{
-    CVReturn result = [(View*)displayLinkContext getFrameForTime:outputTime];
-    return result;
-}
-
-- (id) initWithFrame: (NSRect) frame
-{
-    _game = Game::getInstance();
-    
-    Properties* config = _game->getConfig()->getNamespace("window", true);
-    int samples = config ? config->getInt("samples") : 0;
-    if (samples < 0)
-        samples = 0;
-    
-    // Note: Keep multisampling attributes at the start of the attribute lists since code below
-    // assumes they are array elements 0 through 4.
-    NSOpenGLPixelFormatAttribute windowedAttrs[] = 
-    {
-        NSOpenGLPFAMultisample,
-        NSOpenGLPFASampleBuffers, samples ? 1 : 0,
-        NSOpenGLPFASamples, samples,
-        NSOpenGLPFAAccelerated,
-        NSOpenGLPFADoubleBuffer,
-        NSOpenGLPFAColorSize, 32,
-        NSOpenGLPFADepthSize, 24,
-        NSOpenGLPFAAlphaSize, 8,
-        NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersionLegacy,
-        0
-    };
-    NSOpenGLPixelFormatAttribute fullscreenAttrs[] = 
-    {
-        NSOpenGLPFAMultisample,
-        NSOpenGLPFASampleBuffers, samples ? 1 : 0,
-        NSOpenGLPFASamples, samples,
-        NSOpenGLPFADoubleBuffer,
-        NSOpenGLPFAScreenMask, (NSOpenGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(CGMainDisplayID()),
-        NSOpenGLPFAFullScreen,
-        NSOpenGLPFAColorSize, 32,
-        NSOpenGLPFADepthSize, 24,
-        NSOpenGLPFAAlphaSize, 8,
-        NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersionLegacy,
-        0
-    };
-    NSOpenGLPixelFormatAttribute* attrs = __fullscreen ? fullscreenAttrs : windowedAttrs;
-    
-    __multiSampling = samples > 0;
-
-    // Try to choose a supported pixel format
-    NSOpenGLPixelFormat* pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
-    if (!pf)
-    {
-        bool valid = false;
-        while (!pf && samples > 0)
-        {
-            samples /= 2;
-            attrs[2] = samples ? 1 : 0;
-            attrs[4] = samples;
-            pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
-            if (pf)
-            {
-                valid = true;
-                break;
-            }
-        }
-
-        __multiSampling = samples > 0;
-        
-        if (!valid)
-        {
-            NSLog(@"OpenGL pixel format not supported.");
-            GP_ERROR("Failed to create a valid OpenGL pixel format.");
-            return nil;
-        }
-    }
-    
-    if ((self = [super initWithFrame:frame pixelFormat:[pf autorelease]])) 
-    {
-        gameLock = [[NSRecursiveLock alloc] init];
-        __timeStart = getMachTimeInMilliseconds();
-    }
-    
-    return self;
-}
-
-- (void) prepareOpenGL
-{
-    [super prepareOpenGL];
-    
-    _game->run();
-    
-    if (__fullscreen)
-    {
-        [[self window] setLevel: NSMainMenuWindowLevel+1];
-        [[self window] setHidesOnDeactivate:YES]; 
-    }
-    else
-    {
-        [[self window] setLevel: NSNormalWindowLevel];
-    }
-    [[self window] makeKeyAndOrderFront: self];
-    [[self window] setTitle: [NSString stringWithUTF8String: __title ? __title : ""]];
-    
-    // Make all the OpenGL calls to setup rendering and build the necessary rendering objects
-    [[self openGLContext] makeCurrentContext];
-    // Synchronize buffer swaps with vertical refresh rate
-    GLint swapInt = __vsync ? 1 : 0;
-    [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
-    
-    // Create a display link capable of being used with all active displays
-    CVDisplayLinkCreateWithActiveCGDisplays(&displayLink);
-    
-    // Set the renderer output callback function
-    CVDisplayLinkSetOutputCallback(displayLink, &MyDisplayLinkCallback, self);
-    
-    CGLContextObj cglContext = (CGLContextObj)[[self openGLContext] CGLContextObj];
-    CGLPixelFormatObj cglPixelFormat = (CGLPixelFormatObj)[[self pixelFormat] CGLPixelFormatObj];
-    CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat);
-    
-    GLint dim[2] = {__width, __height};
-    CGLSetParameter(cglContext, kCGLCPSurfaceBackingSize, dim);
-    CGLEnable(cglContext, kCGLCESurfaceBackingSize);
-    
-    // Activate the display link
-    CVDisplayLinkStart(displayLink);
-}
-
-- (void) dealloc
-{   
-    [gameLock lock];
-    
-    // Release the display link
-    CVDisplayLinkStop(displayLink);
-    CVDisplayLinkRelease(displayLink);
-    _game->exit();
-    
-    [gameLock unlock];
-
-    [super dealloc];
-}
-
-- (void)resumeDisplayRenderer 
-{
-    [gameLock lock];
-    CVDisplayLinkStop(displayLink);
-    [gameLock unlock]; 
-}
-
-- (void)haltDisplayRenderer 
-{
-    [gameLock lock];
-    CVDisplayLinkStop(displayLink);
-    [gameLock unlock];
-}
-
-- (void) mouse: (Mouse::MouseEvent) mouseEvent orTouchEvent: (Touch::TouchEvent) touchEvent x: (float) x y: (float) y s: (int) s 
-{
-    [__view->gameLock lock];
-    if (!gameplay::Platform::mouseEventInternal(mouseEvent, x, y, s))
-    {
-        gameplay::Platform::touchEventInternal(touchEvent, x, y, 0);
-    }
-    [__view->gameLock unlock];
-}
-
-- (void) mouseDown: (NSEvent*) event
-{
-    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    __leftMouseDown = true;
-    [self mouse: Mouse::MOUSE_PRESS_LEFT_BUTTON orTouchEvent: Touch::TOUCH_PRESS x: point.x y: __height - point.y s: 0];
-}
-
-- (void) mouseUp: (NSEvent*) event
-{
-    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    __leftMouseDown = false;
-    [self mouse: Mouse::MOUSE_RELEASE_LEFT_BUTTON orTouchEvent: Touch::TOUCH_RELEASE x: point.x y: __height - point.y s: 0];
-
-}
-
-- (void)mouseMoved:(NSEvent*) event 
-{
-    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    
-    float y;
-    if (__mouseCaptured)
-    {
-        if (__mouseCapturedFirstPass)
-        {
-            // Discard the first mouseMoved event following transition into capture
-            // since it contains bogus x,y data.
-            __mouseCapturedFirstPass = false;
-            return;
-        }
-
-        point.x = [event deltaX];
-        point.y = [event deltaY];
-
-        NSWindow* window = __view.window;
-        NSRect rect = window.frame;
-        CGPoint centerPoint;
-        centerPoint.x = rect.origin.x + (rect.size.width / 2);
-        centerPoint.y = rect.origin.y + (rect.size.height / 2);
-        CGDisplayMoveCursorToPoint(CGDisplayPrimaryDisplay(NULL), centerPoint);
-        y = point.y;
-    }
-    else
-    {
-        y = __height - point.y;
-    }
-    
-    [__view->gameLock lock];
-    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, point.x, y, 0);
-    [__view->gameLock unlock];
-}
-
-- (void) mouseDragged: (NSEvent*) event
-{
-    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    if (__leftMouseDown && !__mouseCaptured)
-    {
-        [self mouse: Mouse::MOUSE_MOVE orTouchEvent: Touch::TOUCH_MOVE x: point.x y: __height - point.y s: 0];
-    }
-}
-
-- (void) rightMouseDown: (NSEvent*) event
-{
-    __rightMouseDown = true;
-     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    __lx = point.x;
-    __ly = __height - point.y;
-    
-    [__view->gameLock lock];
-    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_PRESS_RIGHT_BUTTON, point.x, __height - point.y, 0);
-    [__view->gameLock unlock];
-}
-
-- (void) rightMouseUp: (NSEvent*) event
-{
-    __rightMouseDown = false;
-    NSPoint point = [event locationInWindow];
-    
-    [__view->gameLock lock];
-    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_RELEASE_RIGHT_BUTTON, point.x, __height - point.y, 0);
-    [__view->gameLock unlock];
-}
-
-- (void) rightMouseDragged: (NSEvent*) event
-{
-    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    if (__rightMouseDown)
-    {
-        // Update the pitch and roll by adding the scaled deltas.
-        __roll += (float)(point.x - __lx) * ACCELEROMETER_FACTOR_X;
-        __pitch -= -(float)(point.y - (__height - __ly)) * ACCELEROMETER_FACTOR_Y;
-    
-        // Clamp the values to the valid range.
-        __roll = max(min(__roll, 90.0f), -90.0f);
-        __pitch = max(min(__pitch, 90.0f), -90.0f);
-    
-        // Update the last X/Y values.
-        __lx = point.x;
-        __ly = (__height - point.y);
-    }
-    
-    // In right-mouse case, whether __rightMouseDown is true or false
-    // this should not matter, mouse move is still occuring
-    [__view->gameLock lock];
-    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, point.x, __height - point.y, 0);
-    [__view->gameLock unlock];
-}
-
-- (void)otherMouseDown: (NSEvent*) event 
-{
-    __otherMouseDown = true;
-    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-
-    [__view->gameLock lock];
-    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_PRESS_MIDDLE_BUTTON, point.x, __height - point.y, 0);
-    [__view->gameLock unlock];
-}
-
-- (void)otherMouseUp: (NSEvent*) event 
-{
-    __otherMouseDown = false;
-    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-
-    [__view->gameLock lock];
-    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_RELEASE_MIDDLE_BUTTON, point.x, __height - point.y, 0);
-    [__view->gameLock unlock];
-}
-
-- (void)otherMouseDragged: (NSEvent*) event 
-{
-    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-
-    [__view->gameLock lock];
-    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, point.x, __height - point.y, 0);
-    [__view->gameLock unlock];
-}
-
-- (void) mouseEntered: (NSEvent*)event
-{
-    __hasMouse = true;
-}
-
-- (void)scrollWheel: (NSEvent*) event 
-{
-    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-
-    [__view->gameLock lock];
-    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_WHEEL, point.x, __height - point.y, (int)([event deltaY] * 10.0f));
-    [__view->gameLock unlock];
-}
-
-- (void) mouseExited: (NSEvent*)event
-{
-    __leftMouseDown = false;
-    __rightMouseDown = false;
-    __otherMouseDown = false;
-    __hasMouse = false;
-}
-
-- (BOOL)acceptsFirstResponder
-{
-    return YES;
-}
-
-int getKey(unsigned short keyCode, unsigned int modifierFlags)
-{
-    __shiftDown = (modifierFlags & NSShiftKeyMask);
-    unsigned int caps = (__shiftDown ? 1 : 0) ^ ((modifierFlags & NSAlphaShiftKeyMask) ? 1 : 0);
-    switch(keyCode)
-    {
-        case 0x69:
-            return Keyboard::KEY_PRINT;
-        case 0x35:
-            return Keyboard::KEY_ESCAPE;
-        case 0x33:
-            return Keyboard::KEY_BACKSPACE;
-        case 0x30:
-            return Keyboard::KEY_TAB;
-        case 0x24:
-            return Keyboard::KEY_RETURN;
-        case 0x72:
-            return Keyboard::KEY_INSERT;
-        case 0x73:
-            return Keyboard::KEY_HOME;
-        case 0x74:
-            return Keyboard::KEY_PG_UP;
-        case 0x79:
-            return Keyboard::KEY_PG_DOWN;
-        case 0x75:
-            return Keyboard::KEY_DELETE;
-        case 0x77:
-            return Keyboard::KEY_END;
-        case 0x7B:
-            return Keyboard::KEY_LEFT_ARROW;
-        case 0x7C:
-            return Keyboard::KEY_RIGHT_ARROW;
-        case 0x7E:
-            return Keyboard::KEY_UP_ARROW;
-        case 0x7D:
-            return Keyboard::KEY_DOWN_ARROW;
-        case 0x47:
-            return Keyboard::KEY_NUM_LOCK;
-        case 0x45:
-            return Keyboard::KEY_KP_PLUS;
-        case 0x4E:
-            return Keyboard::KEY_KP_MINUS;
-        case 0x43:
-            return Keyboard::KEY_KP_MULTIPLY;
-        case 0x4B:
-            return Keyboard::KEY_KP_DIVIDE;
-        case 0x59:
-            return Keyboard::KEY_KP_HOME;
-        case 0x5B:
-            return Keyboard::KEY_KP_UP;
-        case 0x5C:
-            return Keyboard::KEY_KP_PG_UP;
-        case 0x56:
-            return Keyboard::KEY_KP_LEFT;
-        case 0x57:
-            return Keyboard::KEY_KP_FIVE;
-        case 0x58:
-            return Keyboard::KEY_KP_RIGHT;
-        case 0x53:
-            return Keyboard::KEY_KP_END;
-        case 0x54:
-            return Keyboard::KEY_KP_DOWN;
-        case 0x55:
-            return Keyboard::KEY_KP_PG_DOWN;
-        case 0x52:
-            return Keyboard::KEY_KP_INSERT;
-        case 0x41:
-            return Keyboard::KEY_KP_DELETE;
-        case 0x7A:
-            return Keyboard::KEY_F1;
-        case 0x78:
-            return Keyboard::KEY_F2;
-        case 0x63:
-            return Keyboard::KEY_F3;
-        case 0x76:
-            return Keyboard::KEY_F4;
-        case 0x60:
-            return Keyboard::KEY_F5;
-        case 0x61:
-            return Keyboard::KEY_F6;
-        case 0x62:
-            return Keyboard::KEY_F7;
-        case 0x64:
-            return Keyboard::KEY_F8;
-        case 0x65:
-            return Keyboard::KEY_F9;
-        case 0x6D:
-            return Keyboard::KEY_F10;
-        
-        // MACOS reserved:
-        // return Keyboard::KEY_F11;
-        // return Keyboard::KEY_F12;
-        // return Keyboard::KEY_PAUSE;
-        // return Keyboard::KEY_SCROLL_LOCK;
-            
-        case 0x31:
-            return Keyboard::KEY_SPACE;
-        case 0x1D:
-            return __shiftDown ? Keyboard::KEY_RIGHT_PARENTHESIS : Keyboard::KEY_ZERO;
-        case 0x12:
-            return __shiftDown ? Keyboard::KEY_EXCLAM : Keyboard::KEY_ONE;
-        case 0x13:
-            return __shiftDown ? Keyboard::KEY_AT : Keyboard::KEY_TWO;
-        case 0x14:
-            return __shiftDown ? Keyboard::KEY_NUMBER : Keyboard::KEY_THREE;
-        case 0x15:
-            return __shiftDown ? Keyboard::KEY_DOLLAR : Keyboard::KEY_FOUR;
-        case 0x17:
-            return __shiftDown ? Keyboard::KEY_PERCENT : Keyboard::KEY_FIVE;
-        case 0x16:
-            return __shiftDown ? Keyboard::KEY_CIRCUMFLEX : Keyboard::KEY_SIX;
-        case 0x1A:
-            return __shiftDown ? Keyboard::KEY_AMPERSAND : Keyboard::KEY_SEVEN;
-        case 0x1C:
-            return __shiftDown ? Keyboard::KEY_ASTERISK : Keyboard::KEY_EIGHT;
-        case 0x19:
-            return __shiftDown ? Keyboard::KEY_LEFT_PARENTHESIS : Keyboard::KEY_NINE;
-        case 0x18:
-            return __shiftDown ? Keyboard::KEY_EQUAL : Keyboard::KEY_PLUS;
-        case 0x2B:
-            return __shiftDown ? Keyboard::KEY_LESS_THAN : Keyboard::KEY_COMMA;
-        case 0x1B:
-            return __shiftDown ? Keyboard::KEY_UNDERSCORE : Keyboard::KEY_MINUS;
-        case 0x2F:
-            return __shiftDown ? Keyboard::KEY_GREATER_THAN : Keyboard::KEY_PERIOD;
-        case 0x29:
-            return __shiftDown ? Keyboard::KEY_COLON : Keyboard::KEY_SEMICOLON;
-        case 0x2C:
-            return __shiftDown ? Keyboard::KEY_QUESTION : Keyboard::KEY_SLASH;
-        case 0x32:
-            return __shiftDown ? Keyboard::KEY_GRAVE : Keyboard::KEY_TILDE;
-        case 0x21:
-            return __shiftDown ? Keyboard::KEY_LEFT_BRACE : Keyboard::KEY_LEFT_BRACKET;
-        case 0x2A:
-            return __shiftDown ? Keyboard::KEY_BAR : Keyboard::KEY_BACK_SLASH;
-        case 0x1E:
-            return __shiftDown ? Keyboard::KEY_RIGHT_BRACE : Keyboard::KEY_RIGHT_BRACKET;
-        case 0x27:
-            return __shiftDown ? Keyboard::KEY_QUOTE : Keyboard::KEY_APOSTROPHE;
-            
-        case 0x00:
-            return caps ? Keyboard::KEY_CAPITAL_A : Keyboard::KEY_A;
-        case 0x0B:
-            return caps ? Keyboard::KEY_CAPITAL_B : Keyboard::KEY_B;
-        case 0x08:
-            return caps ? Keyboard::KEY_CAPITAL_C : Keyboard::KEY_C;
-        case 0x02:
-            return caps ? Keyboard::KEY_CAPITAL_D : Keyboard::KEY_D;
-        case 0x0E:
-            return caps ? Keyboard::KEY_CAPITAL_E : Keyboard::KEY_E;
-        case 0x03:
-            return caps ? Keyboard::KEY_CAPITAL_F : Keyboard::KEY_F;
-        case 0x05:
-            return caps ? Keyboard::KEY_CAPITAL_G : Keyboard::KEY_G;
-        case 0x04:
-            return caps ? Keyboard::KEY_CAPITAL_H : Keyboard::KEY_H;
-        case 0x22:
-            return caps ? Keyboard::KEY_CAPITAL_I : Keyboard::KEY_I;
-        case 0x26:
-            return caps ? Keyboard::KEY_CAPITAL_J : Keyboard::KEY_J;
-        case 0x28:
-            return caps ? Keyboard::KEY_CAPITAL_K : Keyboard::KEY_K;
-        case 0x25:
-            return caps ? Keyboard::KEY_CAPITAL_L : Keyboard::KEY_L;
-        case 0x2E:
-            return caps ? Keyboard::KEY_CAPITAL_M : Keyboard::KEY_M;
-        case 0x2D:
-            return caps ? Keyboard::KEY_CAPITAL_N : Keyboard::KEY_N;
-        case 0x1F:
-            return caps ? Keyboard::KEY_CAPITAL_O : Keyboard::KEY_O;
-        case 0x23:
-            return caps ? Keyboard::KEY_CAPITAL_P : Keyboard::KEY_P;
-        case 0x0C:
-            return caps ? Keyboard::KEY_CAPITAL_Q : Keyboard::KEY_Q;
-        case 0x0F:
-            return caps ? Keyboard::KEY_CAPITAL_R : Keyboard::KEY_R;
-        case 0x01:
-            return caps ? Keyboard::KEY_CAPITAL_S : Keyboard::KEY_S;
-        case 0x11:
-            return caps ? Keyboard::KEY_CAPITAL_T : Keyboard::KEY_T;
-        case 0x20:
-            return caps ? Keyboard::KEY_CAPITAL_U : Keyboard::KEY_U;
-        case 0x09:
-            return caps ? Keyboard::KEY_CAPITAL_V : Keyboard::KEY_V;
-        case 0x0D:
-            return caps ? Keyboard::KEY_CAPITAL_W : Keyboard::KEY_W;
-        case 0x07:
-            return caps ? Keyboard::KEY_CAPITAL_X : Keyboard::KEY_X;
-        case 0x10:
-            return caps ? Keyboard::KEY_CAPITAL_Y : Keyboard::KEY_Y;
-        case 0x06:
-            return caps ? Keyboard::KEY_CAPITAL_Z : Keyboard::KEY_Z;
-
-        default:
-            return Keyboard::KEY_NONE;
-    }
-}
-
-/**
- * Returns the unicode value for the given keycode or zero if the key is not a valid printable character.
- */
-int getUnicode(int key)
-{
-    
-    switch (key)
-    {
-        case Keyboard::KEY_BACKSPACE:
-            return 0x0008;
-        case Keyboard::KEY_TAB:
-            return 0x0009;
-        case Keyboard::KEY_RETURN:
-        case Keyboard::KEY_KP_ENTER:
-            return 0x000A;
-        case Keyboard::KEY_ESCAPE:
-            return 0x001B;
-        case Keyboard::KEY_SPACE:
-        case Keyboard::KEY_EXCLAM:
-        case Keyboard::KEY_QUOTE:
-        case Keyboard::KEY_NUMBER:
-        case Keyboard::KEY_DOLLAR:
-        case Keyboard::KEY_PERCENT:
-        case Keyboard::KEY_CIRCUMFLEX:
-        case Keyboard::KEY_AMPERSAND:
-        case Keyboard::KEY_APOSTROPHE:
-        case Keyboard::KEY_LEFT_PARENTHESIS:
-        case Keyboard::KEY_RIGHT_PARENTHESIS:
-        case Keyboard::KEY_ASTERISK:
-        case Keyboard::KEY_PLUS:
-        case Keyboard::KEY_COMMA:
-        case Keyboard::KEY_MINUS:
-        case Keyboard::KEY_PERIOD:
-        case Keyboard::KEY_SLASH:
-        case Keyboard::KEY_ZERO:
-        case Keyboard::KEY_ONE:
-        case Keyboard::KEY_TWO:
-        case Keyboard::KEY_THREE:
-        case Keyboard::KEY_FOUR:
-        case Keyboard::KEY_FIVE:
-        case Keyboard::KEY_SIX:
-        case Keyboard::KEY_SEVEN:
-        case Keyboard::KEY_EIGHT:
-        case Keyboard::KEY_NINE:
-        case Keyboard::KEY_COLON:
-        case Keyboard::KEY_SEMICOLON:
-        case Keyboard::KEY_LESS_THAN:
-        case Keyboard::KEY_EQUAL:
-        case Keyboard::KEY_GREATER_THAN:
-        case Keyboard::KEY_QUESTION:
-        case Keyboard::KEY_AT:
-        case Keyboard::KEY_CAPITAL_A:
-        case Keyboard::KEY_CAPITAL_B:
-        case Keyboard::KEY_CAPITAL_C:
-        case Keyboard::KEY_CAPITAL_D:
-        case Keyboard::KEY_CAPITAL_E:
-        case Keyboard::KEY_CAPITAL_F:
-        case Keyboard::KEY_CAPITAL_G:
-        case Keyboard::KEY_CAPITAL_H:
-        case Keyboard::KEY_CAPITAL_I:
-        case Keyboard::KEY_CAPITAL_J:
-        case Keyboard::KEY_CAPITAL_K:
-        case Keyboard::KEY_CAPITAL_L:
-        case Keyboard::KEY_CAPITAL_M:
-        case Keyboard::KEY_CAPITAL_N:
-        case Keyboard::KEY_CAPITAL_O:
-        case Keyboard::KEY_CAPITAL_P:
-        case Keyboard::KEY_CAPITAL_Q:
-        case Keyboard::KEY_CAPITAL_R:
-        case Keyboard::KEY_CAPITAL_S:
-        case Keyboard::KEY_CAPITAL_T:
-        case Keyboard::KEY_CAPITAL_U:
-        case Keyboard::KEY_CAPITAL_V:
-        case Keyboard::KEY_CAPITAL_W:
-        case Keyboard::KEY_CAPITAL_X:
-        case Keyboard::KEY_CAPITAL_Y:
-        case Keyboard::KEY_CAPITAL_Z:
-        case Keyboard::KEY_LEFT_BRACKET:
-        case Keyboard::KEY_BACK_SLASH:
-        case Keyboard::KEY_RIGHT_BRACKET:
-        case Keyboard::KEY_UNDERSCORE:
-        case Keyboard::KEY_GRAVE:
-        case Keyboard::KEY_A:
-        case Keyboard::KEY_B:
-        case Keyboard::KEY_C:
-        case Keyboard::KEY_D:
-        case Keyboard::KEY_E:
-        case Keyboard::KEY_F:
-        case Keyboard::KEY_G:
-        case Keyboard::KEY_H:
-        case Keyboard::KEY_I:
-        case Keyboard::KEY_J:
-        case Keyboard::KEY_K:
-        case Keyboard::KEY_L:
-        case Keyboard::KEY_M:
-        case Keyboard::KEY_N:
-        case Keyboard::KEY_O:
-        case Keyboard::KEY_P:
-        case Keyboard::KEY_Q:
-        case Keyboard::KEY_R:
-        case Keyboard::KEY_S:
-        case Keyboard::KEY_T:
-        case Keyboard::KEY_U:
-        case Keyboard::KEY_V:
-        case Keyboard::KEY_W:
-        case Keyboard::KEY_X:
-        case Keyboard::KEY_Y:
-        case Keyboard::KEY_Z:
-        case Keyboard::KEY_LEFT_BRACE:
-        case Keyboard::KEY_BAR:
-        case Keyboard::KEY_RIGHT_BRACE:
-        case Keyboard::KEY_TILDE:
-            return key;
-        default:
-            return 0;
-    }
-}
-
-- (void)flagsChanged: (NSEvent*)event
-{
-    unsigned int keyCode = [event keyCode];
-    unsigned int flags = [event modifierFlags];
-
-    [__view->gameLock lock];
-    switch (keyCode) 
-    {
-        case 0x39:
-            gameplay::Platform::keyEventInternal((flags & NSAlphaShiftKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_CAPS_LOCK);
-            break;
-        case 0x38:
-            gameplay::Platform::keyEventInternal((flags & NSShiftKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_SHIFT);
-            break;
-        case 0x3C:
-            gameplay::Platform::keyEventInternal((flags & NSShiftKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_SHIFT);
-            break;
-        case 0x3A:
-            gameplay::Platform::keyEventInternal((flags & NSAlternateKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_ALT);
-            break;
-        case 0x3D:
-            gameplay::Platform::keyEventInternal((flags & NSAlternateKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_ALT);
-            break;
-        case 0x3B:
-            gameplay::Platform::keyEventInternal((flags & NSControlKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_CTRL);
-            break;
-        case 0x3E:
-            gameplay::Platform::keyEventInternal((flags & NSControlKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_CTRL);
-            break;
-        case 0x37:
-            gameplay::Platform::keyEventInternal((flags & NSCommandKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_HYPER);
-            break;
-        case 0x36:
-            gameplay::Platform::keyEventInternal((flags & NSCommandKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_HYPER);
-            break;
-    }
-    [__view->gameLock unlock];
-}
-
-- (void) keyDown: (NSEvent*) event
-{
-    if ([event isARepeat] == NO)
-    {
-        int key = getKey([event keyCode], [event modifierFlags]);
-
-        [__view->gameLock lock];
-        gameplay::Platform::keyEventInternal(Keyboard::KEY_PRESS, key);
-        
-        int character = getUnicode(key);
-        if (character)
-        {
-            gameplay::Platform::keyEventInternal(Keyboard::KEY_CHAR, character);
-        }
-
-        [__view->gameLock unlock];
-    }
-}
-
-- (void) keyUp: (NSEvent*) event
-{
-    [__view->gameLock lock];
-    gameplay::Platform::keyEventInternal(Keyboard::KEY_RELEASE, getKey([event keyCode], [event modifierFlags]));
-    [__view->gameLock unlock];
-}
-
-// Gesture support for Mac OS X Trackpads
-- (bool)isGestureRegistered: (Gesture::GestureEvent) evt
-{
-    return ((_gestureEvents & evt) == evt);
-}
-
-- (void)registerGesture: (Gesture::GestureEvent) evt
-{
-    _gestureEvents |= evt;
-}
-
-- (void)unregisterGesture: (Gesture::GestureEvent) evt
-{
-    _gestureEvents &= (~evt);
-}
-
-- (void)magnifyWithEvent:(NSEvent *)event
-{
-    if([self isGestureRegistered:Gesture::GESTURE_PINCH] == false) return;
-    
-    NSSet *touches = [event touchesMatchingPhase:NSTouchPhaseAny  inView:nil];
-    // Approximate the center by adding and averageing for now
-    // Note this is centroid on the physical device be used for touching, not the display
-    float xavg = 0.0f;
-    float yavg = 0.0f;
-    for(NSTouch *t in touches) {
-        xavg += [t normalizedPosition].x;
-        yavg += [t normalizedPosition].y;
-    }
-    xavg /= [touches count];
-    yavg /= [touches count];
-    
-    [gameLock lock];
-    _game->gesturePinchEvent((int)xavg, (int)yavg, [event magnification]);
-    [gameLock unlock];
-}
-
-
-@end
-
-@interface FullscreenWindow : NSWindow
-{ 
-}
-@end
-
-@implementation FullscreenWindow
-- (BOOL)canBecomeKeyWindow
-{
-    return YES;
-}
-@end
-
-
-namespace gameplay
-{
-
-extern void print(const char* format, ...)
-{
-    GP_ASSERT(format);
-    va_list argptr;
-    va_start(argptr, format);
-    vfprintf(stderr, format, argptr);
-    va_end(argptr);
-}
-    
-Platform::Platform(Game* game)
-: _game(game)
-{
-    __activeGamepads = [[NSMutableDictionary alloc] init];
-    __gamepads = [[NSMutableArray alloc] init];
-    __hidManagerRef = IOHIDManagerCreate(CFAllocatorGetDefault(), kIOHIDOptionsTypeNone);
-    IOHIDManagerRegisterDeviceMatchingCallback(__hidManagerRef, hidDeviceDiscoveredCallback, NULL);
-    IOHIDManagerRegisterDeviceRemovalCallback(__hidManagerRef, hidDeviceRemovalCallback, NULL);
-    
-    CFMutableArrayRef matching = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
-    if (matching)
-    {
-        CFDictionaryRef matchingJoystick = IOHIDCreateDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick);
-        CFDictionaryRef matchingGamepad = IOHIDCreateDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad);
-        
-        if (matchingJoystick && matchingGamepad)
-        {
-            CFArrayAppendValue(matching, matchingJoystick);
-            CFRelease(matchingJoystick);
-            CFArrayAppendValue(matching, matchingGamepad);
-            CFRelease(matchingGamepad);
-            IOHIDManagerSetDeviceMatchingMultiple(__hidManagerRef, matching);
-        }
-    }
-    
-    IOHIDManagerScheduleWithRunLoop(__hidManagerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
-    IOReturn kr = IOHIDManagerOpen(__hidManagerRef, kIOHIDOptionsTypeNone);
-    assert(kr == 0);
-}
-
-    
-Platform::~Platform()
-{
-    IOHIDManagerUnscheduleFromRunLoop(__hidManagerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
-    IOHIDManagerClose(__hidManagerRef, kIOHIDOptionsTypeNone);
-    
-    CFRelease(__hidManagerRef);
-    __hidManagerRef = NULL;
-    [__activeGamepads release];
-    __activeGamepads = NULL;
-    [__gamepads release];
-    __gamepads = NULL;
-}
-
-    
-Platform* Platform::create(Game* game, void* attachToWindow)
-{
-    __attachToWindow = attachToWindow;
-    Platform* platform = new Platform(game);
-    
-    return platform;
-}
-
-int Platform::enterMessagePump()
-{
-    NSString* bundlePath = [[NSBundle mainBundle] bundlePath];
-    NSString* path = [bundlePath stringByAppendingString:@"/Contents/Resources/"];
-    FileSystem::setResourcePath([path cStringUsingEncoding:NSASCIIStringEncoding]);
-    
-    // Read window settings from config.
-    if (_game->getConfig())
-    {
-        Properties* config = _game->getConfig()->getNamespace("window", true);
-        if (config)
-        {
-            // Read window title.
-            __title = const_cast<char *>(config->getString("title"));
-
-            // Read window size.
-            int width = config->getInt("width");
-            if (width != 0)
-                __width = width;
-            int height = config->getInt("height");
-            if (height != 0)
-                __height = height;
-
-            // Read fullscreen state.
-            __fullscreen = config->getBool("fullscreen");
-            if (__fullscreen && width == 0 && height == 0)
-            {
-                CGRect mainMonitor = CGDisplayBounds(CGMainDisplayID());
-                __width = CGRectGetWidth(mainMonitor);
-                __height = CGRectGetHeight(mainMonitor);
-            }
-            
-            // Read resizable state.
-            __resizable = config->getBool("resizable");
-        }
-    }
-
-    // Set the scale factors for the mouse movement used to simulate the accelerometer.
-    ACCELEROMETER_FACTOR_X = 90.0f / __width;
-    ACCELEROMETER_FACTOR_Y = 90.0f / __height;
-
-    NSAutoreleasePool* pool = [NSAutoreleasePool new];
-    NSApplication* app = [NSApplication sharedApplication];
-    NSRect screenBounds = [[NSScreen mainScreen] frame];
-    NSRect viewBounds = NSMakeRect(0, 0, __width, __height);
-    
-    __view = [[View alloc] initWithFrame:viewBounds];
-    if (__view == NULL)
-    {
-        GP_ERROR("Failed to create view: exiting.");
-        return EXIT_FAILURE;
-    }
-    
-    NSRect centered = NSMakeRect(NSMidX(screenBounds) - NSMidX(viewBounds),
-                                 NSMidY(screenBounds) - NSMidY(viewBounds),
-                                 viewBounds.size.width, 
-                                 viewBounds.size.height);
-    
-    NSWindow* window = NULL;
-    if (__fullscreen)
-    {
-        window = [[FullscreenWindow alloc]
-                   initWithContentRect:screenBounds
-                   styleMask:NSBorderlessWindowMask
-                   backing:NSBackingStoreBuffered
-                   defer:NO];
-    }
-    else
-    {
-        window = [[NSWindow alloc]
-                   initWithContentRect:centered
-                  styleMask:NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask
-                   backing:NSBackingStoreBuffered
-                   defer:NO];
-    }
-    
-    [window setAcceptsMouseMovedEvents:YES];
-    [window setContentView:__view];
-    [window setDelegate:__view];
-    [__view release];
-    
-    [app run];
-    
-    [pool release];
-    return EXIT_SUCCESS;
-}
-
-void Platform::signalShutdown() 
-{
-    [__view haltDisplayRenderer];
-
-    // Don't perform terminate right away, enqueue to give game object
-    // a chance to cleanup
-    NSApplication* app = [NSApplication sharedApplication];
-    [app performSelectorOnMainThread:@selector(terminate:) withObject:nil waitUntilDone:NO];
-}
-
-bool Platform::canExit()
-{
-    return true;
-}
-    
-unsigned int Platform::getDisplayWidth()
-{
-    return __width;
-}
-
-unsigned int Platform::getDisplayHeight()
-{
-    return __height;
-}
-
-double Platform::getAbsoluteTime()
-{
-    __timeAbsolute = getMachTimeInMilliseconds();
-    return __timeAbsolute;
-}
-
-void Platform::setAbsoluteTime(double time)
-{
-    __timeAbsolute = time;
-}
-
-bool Platform::isVsync()
-{
-    return __vsync;
-}
-
-void Platform::setVsync(bool enable)
-{
-    __vsync = enable;
-    GLint swapInt = enable ? 1 : 0;
-    [[__view openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
-}
-
-void Platform::swapBuffers()
-{
-    if (__view)
-        CGLFlushDrawable((CGLContextObj)[[__view openGLContext] CGLContextObj]);
-}
-
-void Platform::sleep(long ms)
-{
-    usleep(ms * 1000);
-}
-
-void Platform::setMultiSampling(bool enabled)
-{
-    if (enabled == __multiSampling)
-    {
-        return;
-    }
-
-    //todo
-
-    __multiSampling = enabled;
-}
-
-bool Platform::isMultiSampling()
-{
-    return __multiSampling;
-}
-
-void Platform::setMultiTouch(bool enabled)
-{
-}
-    
-bool Platform::isMultiTouch()
-{
-    return true;
-}
-    
-void Platform::getAccelerometerValues(float* pitch, float* roll)
-{
-    GP_ASSERT(pitch);
-    GP_ASSERT(roll);
-
-    *pitch = __pitch;
-    *roll = __roll;
-}
-
-bool Platform::hasMouse()
-{
-    return true;
-}
-
-void Platform::setMouseCaptured(bool captured)
-{
-    if (captured != __mouseCaptured)
-    {
-        if (captured)
-        {
-            [NSCursor hide];
-            __mouseCapturedFirstPass = true;
-        }
-        else
-        {   
-            [NSCursor unhide];
-        }
-        NSWindow* window = __view.window;
-        NSRect rect = window.frame;
-        CGPoint centerPoint;
-        centerPoint.x = rect.origin.x + (rect.size.width / 2);
-        centerPoint.y = rect.origin.y + (rect.size.height / 2);
-        CGDisplayMoveCursorToPoint(CGDisplayPrimaryDisplay(NULL), centerPoint);
-        __mouseCaptured = captured;
-    }
-}
-
-bool Platform::isMouseCaptured()
-{
-    return __mouseCaptured;
-}
-
-void Platform::setCursorVisible(bool visible)
-{
-    if (visible != __cursorVisible)
-    {
-        if (visible)
-        {
-             [NSCursor unhide];
-        }
-        else 
-        {
-             [NSCursor hide];
-        }
-        __cursorVisible = visible;
-    }
-}
-
-bool Platform::isCursorVisible()
-{
-    return __cursorVisible;
-}
-
-void Platform::displayKeyboard(bool display)
-{
-    // Do nothing.
-}
-
-void Platform::shutdownInternal()
-{
-    Game::getInstance()->shutdown();
-}
-
-bool Platform::isGestureSupported(Gesture::GestureEvent evt)
-{
-    // Swipe unsupported as it is considered moving mouse cursor
-    // Two fingers is scrolling
-    // Three fingers is swipe, but is not always enabled on users system
-    // Tap not supported as it is considered a mouse click/button click
-    // on some systems making it difficult to differentiate 
-    switch(evt)
-    {
-        case Gesture::GESTURE_PINCH:
-            return true;
-        default:
-            break;
-    }
-    return false;
-}
-
-void Platform::registerGesture(Gesture::GestureEvent evt)
-{
-    [__view registerGesture:evt];   
-}
-
-void Platform::unregisterGesture(Gesture::GestureEvent evt)
-{
-    [__view unregisterGesture:evt];        
-}
-  
-bool Platform::isGestureRegistered(Gesture::GestureEvent evt)
-{
-     return [__view isGestureRegistered:evt];
-}
-
-void Platform::pollGamepadState(Gamepad* gamepad)
-{
-    HIDGamepad* gp = gamepadForGameHandle(gamepad->_handle);
-    if (gp)
-    {
-        // Haven't figured out how to have the triggers not also show up in the buttons array.
-        // So for now a value of -1 means "Don't map this button."
-        static const int PS3Mapping[17] = {
-            Gamepad::BUTTON_MENU1,  // 0x0001
-            Gamepad::BUTTON_L3,     // 0x0002
-            Gamepad::BUTTON_R3,     // 0x0004
-            Gamepad::BUTTON_MENU2,  // 0x0008
-            Gamepad::BUTTON_UP,     // 0x0010
-            Gamepad::BUTTON_RIGHT,  // 0x0020
-            Gamepad::BUTTON_DOWN,   // 0x0040
-            Gamepad::BUTTON_LEFT,   // 0x0080
-            -1,                     // Gamepad::BUTTON_L2,     // 0x0100
-            -1,                     // Gamepad::BUTTON_R2,     // 0x0200
-            Gamepad::BUTTON_L1,     // 0x0400
-            Gamepad::BUTTON_R1,     // 0x0800
-            Gamepad::BUTTON_Y,      // 0x1000
-            Gamepad::BUTTON_B,      // 0x2000
-            Gamepad::BUTTON_A,      // 0x4000
-            Gamepad::BUTTON_X,      // 0x8000
-            Gamepad::BUTTON_MENU3   // 0x10000
-        };
-        
-        static const int XBox360Mapping[20] = {
-            -1, -1, -1, -1, -1,
-            Gamepad::BUTTON_UP,
-            Gamepad::BUTTON_DOWN,
-            Gamepad::BUTTON_LEFT,
-            Gamepad::BUTTON_RIGHT,
-            Gamepad::BUTTON_MENU2,
-            Gamepad::BUTTON_MENU1,
-            Gamepad::BUTTON_L3,
-            Gamepad::BUTTON_R3,
-            Gamepad::BUTTON_L1,
-            Gamepad::BUTTON_R1,
-            Gamepad::BUTTON_MENU3,
-            Gamepad::BUTTON_A,
-            Gamepad::BUTTON_B,
-            Gamepad::BUTTON_X,
-            Gamepad::BUTTON_Y
-        };
-        
-        static const int SteelSeriesFreeMapping[13] = {
-            Gamepad::BUTTON_A,
-            Gamepad::BUTTON_B,
-            -1,
-            Gamepad::BUTTON_X,
-            Gamepad::BUTTON_Y,
-            -1,
-            Gamepad::BUTTON_L1,
-            Gamepad::BUTTON_R1,
-            -1, -1, -1,
-            Gamepad::BUTTON_MENU2,
-            Gamepad::BUTTON_MENU1
-        };
-        
-        static const int GametelMapping103[12] = {
-            Gamepad::BUTTON_B,
-            Gamepad::BUTTON_X,
-            Gamepad::BUTTON_Y,
-            Gamepad::BUTTON_A,
-            Gamepad::BUTTON_L1,
-            Gamepad::BUTTON_R1,
-            Gamepad::BUTTON_MENU1,
-            Gamepad::BUTTON_MENU2,
-            Gamepad::BUTTON_RIGHT,
-            Gamepad::BUTTON_LEFT,
-            Gamepad::BUTTON_DOWN,
-            Gamepad::BUTTON_UP
-        };
-        
-        const int* mapping = NULL;
-        float axisDeadZone = 0.0f;
-        if (gamepad->_vendorId == SONY_USB_VENDOR_ID &&
-            gamepad->_productId == SONY_USB_PS3_PRODUCT_ID)
-        {
-            mapping = PS3Mapping;
-            axisDeadZone = 0.07f;
-        }
-        else if (gamepad->_vendorId == MICROSOFT_VENDOR_ID &&
-                 gamepad->_productId == MICROSOFT_XBOX360_PRODUCT_ID)
-        {
-            mapping = XBox360Mapping;
-            axisDeadZone = 0.2f;
-        }
-        else if (gamepad->_vendorId == STEELSERIES_VENDOR_ID &&
-                 gamepad->_productId == STEELSERIES_FREE_PRODUCT_ID)
-        {
-            mapping = SteelSeriesFreeMapping;
-            axisDeadZone = 0.005f;
-        }
-        else if (gamepad->_vendorId == FRUCTEL_VENDOR_ID &&
-                 gamepad->_productId == FRUCTEL_GAMETEL_PRODUCT_ID)
-        {
-            int ver = [gp versionNumber];
-            int major = ver >> 8;
-            int minor = ver & 0x00ff;
-            if (major >= 1 && minor > 1)
-            {
-                mapping = GametelMapping103;
-            }
-        }
-        
-        gamepad->_buttons = 0;
-        for (int i = 0; i < [gp numberOfButtons]; ++i)
-        {
-            HIDGamepadButton* b = [gp buttonAtIndex: i];
-            if ([b state])
-            {
-                // This button is down.
-                if (mapping)
-                {
-                    if (mapping[i] >= 0)
-                        gamepad->_buttons |= (1 << mapping[i]);
-                }
-                else
-                {
-                    gamepad->_buttons |= (1 << i);
-                }
-            }
-        }
-        
-        HIDGamepadAxis* hatSwitch = [gp getHatSwitch];
-        if (hatSwitch != NULL)
-        {
-            CFIndex v = [hatSwitch value];
-            switch (v)
-            {
-                case -1:
-                    break;
-                case 0:
-                    gamepad->_buttons |= (1 << Gamepad::BUTTON_UP);
-                    break;
-                case 1:
-                    gamepad->_buttons |= (1 << Gamepad::BUTTON_UP) | (1 << Gamepad::BUTTON_RIGHT);
-                    break;
-                case 2:
-                    gamepad->_buttons |= (1 << Gamepad::BUTTON_RIGHT);
-                    break;
-                case 3:
-                    gamepad->_buttons |= (1 << Gamepad::BUTTON_RIGHT) | (1 << Gamepad::BUTTON_DOWN);
-                    break;
-                case 4:
-                    gamepad->_buttons |= (1 << Gamepad::BUTTON_DOWN);
-                    break;
-                case 5:
-                    gamepad->_buttons |= (1 << Gamepad::BUTTON_DOWN) | (1 << Gamepad::BUTTON_LEFT);
-                    break;
-                case 6:
-                    gamepad->_buttons |= (1 << Gamepad::BUTTON_LEFT);
-                    break;
-                case 7:
-                    gamepad->_buttons |= (1 << Gamepad::BUTTON_LEFT) | (1 << Gamepad::BUTTON_UP);
-                    break;
-            }
-        }
-        
-        for (unsigned int i = 0; i < [gp numberOfSticks]; ++i)
-        {
-            float rawX = [[gp axisAtIndex: i*2] calibratedValue];
-            float rawY = -[[gp axisAtIndex: i*2 + 1] calibratedValue];
-            if (std::fabs(rawX) <= axisDeadZone)
-                rawX = 0;
-            if (std::fabs(rawY) <= axisDeadZone)
-                rawY = 0;
-            
-            gamepad->_joysticks[i].x = rawX;
-            gamepad->_joysticks[i].y = rawY;
-        }
-        
-        for (unsigned int i = 0; i < [gp numberOfTriggerButtons]; ++i)
-        {
-            gamepad->_triggers[i] = [[gp triggerButtonAtIndex: i] calibratedStateValue];
-        }
-    }
-}
-
-}
-
-HIDGamepad* gamepadForLocationID(NSNumber* locationID)
-{
-    HIDGamepad* fgamepad = NULL;
-    for(HIDGamepad* gamepad in __gamepads)
-    {
-        if([[gamepad locationID] isEqual:locationID])
-        {
-            fgamepad = gamepad;
-            break;
-        }
-    }
-    return fgamepad;
-}
-
-HIDGamepad* gamepadForLocationIDValue(unsigned int locationIDValue)
-{
-    return gamepadForLocationID([NSNumber numberWithUnsignedInt:locationIDValue]);
-}
-
-HIDGamepad* gamepadForGameHandle(int gameHandle)
-{
-    HIDGamepad* gamepad = NULL;
-    for(NSNumber* locationID in __activeGamepads)
-    {
-        NSNumber* handleID = [__activeGamepads objectForKey:locationID];
-        if([handleID integerValue] == gameHandle)
-        {
-            gamepad = gamepadForLocationID(locationID);
-            break;
-        }
-    }
-    return gamepad;
-}
-
-CFMutableDictionaryRef IOHIDCreateDeviceMatchingDictionary(UInt32 inUsagePage, UInt32 inUsage) 
-{
-    // create a dictionary to add usage page/usages to
-    CFMutableDictionaryRef result = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
-    if (result) {
-        if (inUsagePage) 
-        {
-            // Add key for device type to refine the matching dictionary.
-            CFNumberRef pageCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsagePage);
-            if (pageCFNumberRef) 
-            {
-                CFDictionarySetValue(result, CFSTR( kIOHIDDeviceUsagePageKey ), pageCFNumberRef);
-                CFRelease(pageCFNumberRef);
-                
-                // note: the usage is only valid if the usage page is also defined
-                if (inUsage) 
-                {
-                    CFNumberRef usageCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsage);
-                    if (usageCFNumberRef) 
-                    {
-                        CFDictionarySetValue(result, CFSTR(kIOHIDDeviceUsageKey), usageCFNumberRef);
-                        CFRelease(usageCFNumberRef);
-                    } 
-                    else 
-                    {
-                        fprintf(stderr, "%s: CFNumberCreate( usage ) failed.", __PRETTY_FUNCTION__);
-                    }
-                }
-            } 
-            else 
-            {
-                fprintf( stderr, "%s: CFNumberCreate( usage page ) failed.", __PRETTY_FUNCTION__);
-            }
-        }
-    } 
-    else 
-    {
-        fprintf( stderr, "%s: CFDictionaryCreateMutable failed.", __PRETTY_FUNCTION__);
-    }
-    return result;
-}
-
-CFStringRef IOHIDDeviceGetStringProperty(IOHIDDeviceRef deviceRef, CFStringRef key) 
-{
-    CFTypeRef typeRef = IOHIDDeviceGetProperty(deviceRef, key);
-    if (typeRef == NULL || CFGetTypeID(typeRef) != CFNumberGetTypeID()) 
-    {
-        return NULL;
-    }
-    return (CFStringRef)typeRef;
-}
-
-int IOHIDDeviceGetIntProperty(IOHIDDeviceRef deviceRef, CFStringRef key) 
-{
-    CFTypeRef typeRef = IOHIDDeviceGetProperty(deviceRef, key);
-    if (typeRef == NULL || CFGetTypeID(typeRef) != CFNumberGetTypeID()) 
-    {
-        return 0;
-    }
-    
-    int value;
-    CFNumberGetValue((CFNumberRef) typeRef, kCFNumberSInt32Type, &value);
-    return value;
-}
-
-static void hidDeviceDiscoveredCallback(void* inContext, IOReturn inResult, void* inSender, IOHIDDeviceRef device)
-{
-    CFNumberRef locID = (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDLocationIDKey));
-    if(locID)
-    {
-        HIDGamepad* gamepad = [[HIDGamepad alloc] initWithDevice: device];
-        [__gamepads addObject:gamepad];
-    }
-}
-
-static void hidDeviceRemovalCallback(void* inContext, IOReturn inResult, void* inSender, IOHIDDeviceRef device)
-{
-    int removeIndex = -1;
-    NSNumber *locID = (NSNumber*)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDLocationIDKey));
-    if(locID)
-    {
-        for(int i = 0; i < [__gamepads count]; i++)
-        {
-            HIDGamepad* gamepad = [__gamepads objectAtIndex:i];
-            if([[gamepad locationID] isEqual:locID])
-            {
-                removeIndex = i;
-                break;
-            }
-        }
-    }
-    if(removeIndex >= 0)
-    {
-        [__gamepads removeObjectAtIndex:removeIndex];
-    }
-}
-
-static void hidDeviceValueAvailableCallback(void* inContext, IOReturn inResult,  void* inSender)
-{
-    HIDGamepad* d = (HIDGamepad*)inContext;
-    do
-    {
-        IOHIDValueRef valueRef = IOHIDQueueCopyNextValueWithTimeout( ( IOHIDQueueRef ) inSender, 0. );
-        if (!valueRef) break;
-        [d hidValueAvailable:valueRef];
-        CFRelease(valueRef); // Don't forget to release our HID value reference
-    } while (1);
-}
-
-bool Platform::launchURL(const char *url)
-{
-    if (url == NULL || *url == '\0')
-        return false;
-
-    CFURLRef urlRef = CFURLCreateWithBytes(
-        NULL,
-        (UInt8*)url,
-        strlen(url),
-        kCFStringEncodingASCII,
-        NULL
-    );
-    const OSStatus err = LSOpenCFURLRef(urlRef, 0);
-    CFRelease(urlRef);
-    return (err == noErr);
-}
-
-#endif
+#ifdef __APPLE__
+
+#include "Base.h"
+#include "Platform.h"
+#include "FileSystem.h"
+#include "Game.h"
+#include "Form.h"
+#include "ScriptController.h"
+#include <unistd.h>
+#include <IOKit/hid/IOHIDLib.h>
+#import <Cocoa/Cocoa.h>
+#import <QuartzCore/CVDisplayLink.h>
+#import <OpenGL/OpenGL.h>
+#import <mach/mach_time.h>
+#import <Foundation/Foundation.h>
+
+// These should probably be moved to a platform common file
+#define SONY_USB_VENDOR_ID              0x054c
+#define SONY_USB_PS3_PRODUCT_ID         0x0268
+#define MICROSOFT_VENDOR_ID             0x045e
+#define MICROSOFT_XBOX360_PRODUCT_ID    0x028e
+#define STEELSERIES_VENDOR_ID           0x1038
+#define STEELSERIES_FREE_PRODUCT_ID     0x1412
+#define FRUCTEL_VENDOR_ID               0x25B6
+#define FRUCTEL_GAMETEL_PRODUCT_ID      0x0001
+
+using namespace std;
+using namespace gameplay;
+
+@class View;
+@class HIDGamepad;
+
+int __argc = 0;
+char** __argv = 0;
+
+// Default to 720p
+static int __width = 1280;
+static int __height = 720;
+
+static float ACCELEROMETER_FACTOR_X = 90.0f / __width;
+static float ACCELEROMETER_FACTOR_Y = 90.0f / __height;
+
+static double __timeStart;
+static double __timeAbsolute;
+static bool __vsync = WINDOW_VSYNC;
+static float __pitch;
+static float __roll;
+static int __lx;
+static int __ly;
+static bool __hasMouse = false;
+static bool __leftMouseDown = false;
+static bool __rightMouseDown = false;
+static bool __otherMouseDown = false;
+static bool __shiftDown = false;
+static char* __title = NULL;
+static bool __fullscreen = false;
+static bool __resizable = false;
+static void* __attachToWindow = NULL;
+static bool __mouseCaptured = false;
+static bool __mouseCapturedFirstPass = false;
+static CGPoint __mouseCapturePoint;
+static bool __multiSampling = false;
+static bool __cursorVisible = true;
+static View* __view = NULL;
+
+static NSMutableDictionary *__activeGamepads = NULL;
+static NSMutableArray *__gamepads = NULL;
+static IOHIDManagerRef __hidManagerRef = NULL;
+
+// Gamepad Helper Function
+HIDGamepad *gamepadForLocationID(NSNumber *locationID);
+HIDGamepad *gamepadForLocationIDValue(unsigned int locationIDValue);
+HIDGamepad *gamepadForGameHandle(int gameHandle);
+
+
+// IOHid Helper Functions
+CFMutableDictionaryRef IOHIDCreateDeviceMatchingDictionary(UInt32 inUsagePage, UInt32 inUsage);
+CFStringRef IOHIDDeviceGetStringProperty(IOHIDDeviceRef deviceRef, CFStringRef key);
+int IOHIDDeviceGetIntProperty(IOHIDDeviceRef deviceRef, CFStringRef key);
+
+// IOHid Callbacks
+static void hidDeviceDiscoveredCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef);
+static void hidDeviceRemovalCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef);
+static void hidDeviceValueAvailableCallback(void *inContext, IOReturn inResult,  void *inSender);
+
+double getMachTimeInMilliseconds()
+{
+    static const double kOneMillion = 1000 * 1000;
+    static mach_timebase_info_data_t s_timebase_info;
+    
+    if (s_timebase_info.denom == 0) 
+        (void) mach_timebase_info(&s_timebase_info);
+    
+    // mach_absolute_time() returns billionth of seconds, so divide by one million to get milliseconds
+    GP_ASSERT(s_timebase_info.denom);
+    return ((double)mach_absolute_time() * (double)s_timebase_info.numer) / (kOneMillion * (double)s_timebase_info.denom);
+}
+
+@interface HIDGamepadAxis : NSObject
+{
+    IOHIDElementRef e;
+    CFIndex v;
+    CFIndex logMin;
+    CFIndex logMax;
+}
+
++ gamepadAxisWithAxisElement:(IOHIDElementRef)element;
+- initWithAxisElement:(IOHIDElementRef)element;
+- (IOHIDElementRef)element;
+- (IOHIDElementCookie)cookie;
+- (uint32_t)usage;
+- (uint32_t)usagePage;
+- (CFIndex)logicalMinimum;
+- (CFIndex)logicalMaximum;
+
+- (float)calibratedValue;
+- (CFIndex)value;
+- (void)setValue:(CFIndex)value;
+@end
+
+@implementation HIDGamepadAxis
++ gamepadAxisWithAxisElement:(IOHIDElementRef)element
+{
+    return [[[[self class] alloc] initWithAxisElement:element] autorelease];
+}
+
+- initWithAxisElement:(IOHIDElementRef)element
+{
+    if((self = [super init]))
+    {
+        e = (IOHIDElementRef)CFRetain(element);
+    }
+    return self;
+}
+
+- (void)dealloc
+{
+    CFRelease(e);
+    [super dealloc];
+}
+
+- (IOHIDElementRef)element
+{
+    return e;
+}
+
+- (IOHIDElementCookie)cookie
+{
+    return IOHIDElementGetCookie(e);
+}
+
+- (bool)isHatSwitch {
+    return (IOHIDElementGetUsage(e) == kHIDUsage_GD_Hatswitch);
+}
+
+- (uint32_t)usage
+{
+    return IOHIDElementGetUsage(e);
+}
+
+- (uint32_t)usagePage
+{
+    return IOHIDElementGetUsagePage(e);
+}
+
+- (CFIndex)logicalMinimum
+{
+    return IOHIDElementGetLogicalMin(e);    
+}
+
+- (CFIndex)logicalMaximum
+{
+    return IOHIDElementGetLogicalMax(e);
+}
+
+- (float)calibratedValue
+{
+    float cmax = 2.0f;
+    float cmin = 0.0f;
+    return ((((v - [self logicalMinimum]) * (cmax - cmin)) / ([self logicalMaximum] - [self logicalMinimum])) + cmin - 1.0f);    
+}
+
+- (CFIndex)value
+{
+    return v;
+}
+
+- (void)setValue:(CFIndex)value
+{
+    v = value;
+}
+@end
+
+@interface HIDGamepadButton : NSObject
+{
+    IOHIDElementRef e;
+    IOHIDElementRef te;
+    bool state;
+    int triggerValue;
+}
+
++ gamepadButtonWithButtonElement:(IOHIDElementRef)element;
+- initWithButtonElement:(IOHIDElementRef)element;
+- (void)setTriggerElement:(IOHIDElementRef)element;
+- (IOHIDElementRef)element;
+- (IOHIDElementCookie)cookie;
+- (IOHIDElementRef)triggerElement;
+- (IOHIDElementCookie)triggerCookie;
+
+- (bool)isTriggerButton;
+- (uint32_t)usage;
+- (uint32_t)usagePage;
+- (int)stateValue;
+- (float)calibratedStateValue;
+- (void)setStateValue:(int)value;
+- (bool)state;
+- (void)setState:(bool)state;
+@end
+
+@implementation HIDGamepadButton
++ gamepadButtonWithButtonElement:(IOHIDElementRef)element
+{
+    return [[[[self class] alloc] initWithButtonElement:element] autorelease];
+}
+
+- initWithButtonElement:(IOHIDElementRef)element
+{
+    if((self = [super init]))
+    {
+        e = (IOHIDElementRef)CFRetain(element);
+        te = NULL;
+        state = false;
+    }
+    return self;
+}
+
+- (void)dealloc
+{
+    CFRelease(e);
+    if(te != NULL) CFRelease(te);
+    [super dealloc];
+}
+
+- (void)setTriggerElement:(IOHIDElementRef)element {
+    if(te)
+    {
+        CFRelease(te);
+        te = NULL;
+    }
+    if(element)
+    {
+        te = (IOHIDElementRef)CFRetain(element);
+    }
+}
+
+- (IOHIDElementRef)element
+{
+    return e;
+}
+
+- (IOHIDElementCookie)cookie
+{
+    return IOHIDElementGetCookie(e);
+}
+
+- (IOHIDElementRef)triggerElement
+{
+    return te;
+}
+
+- (IOHIDElementCookie)triggerCookie
+{
+    return IOHIDElementGetCookie(te);
+}
+
+- (bool)isTriggerButton
+{
+    return (te != NULL);
+}
+
+- (uint32_t)usage
+{
+    return IOHIDElementGetUsage(e);
+}
+
+- (uint32_t)usagePage
+{
+    return IOHIDElementGetUsagePage(e);
+}
+
+- (void)setStateValue:(int)value {
+    triggerValue = value;
+}
+
+- (int)stateValue
+{
+    return triggerValue;
+}
+
+- (float)calibratedStateValue
+{
+    return (float)triggerValue / 255.0f;
+}
+
+- (bool)state
+{
+    return state;
+}
+
+- (void)setState:(bool)s
+{
+    state = s;
+}
+@end
+
+@interface HIDGamepad : NSObject
+{
+    IOHIDDeviceRef hidDeviceRef;
+    IOHIDQueueRef queueRef;
+    NSMutableArray* buttons;
+    NSMutableArray* triggerButtons;
+    NSMutableArray* axes;
+    HIDGamepadAxis* hatSwitch;
+}
+@property (assign) IOHIDDeviceRef hidDeviceRef;
+@property (assign) IOHIDQueueRef queueRef;
+@property (retain) NSMutableArray* buttons;
+@property (retain) NSMutableArray* triggerButtons;
+@property (retain) NSMutableArray* axes;
+@property (retain) HIDGamepadAxis* hatSwitch;
+
+- initWithDevice:(IOHIDDeviceRef)rawDevice;
+- (IOHIDDeviceRef)rawDevice;
+- (NSNumber*)locationID;
+
+- (void)initializeGamepadElements;
+- (HIDGamepadButton*)buttonWithCookie:(IOHIDElementCookie)cookie;
+
+- (bool)startListening;
+- (void)stopListening;
+
+- (NSString*)identifierName;
+- (NSString*)productName;
+- (NSString*)manufacturerName;
+- (NSString*)serialNumber;
+- (int)versionNumber;
+- (int)vendorID;
+- (int)productID;
+
+- (NSUInteger)numberOfAxes;
+- (NSUInteger)numberOfSticks;
+- (NSUInteger)numberOfButtons;
+- (NSUInteger)numberOfTriggerButtons;
+- (HIDGamepadAxis*)axisAtIndex:(NSUInteger)index;
+- (HIDGamepadButton*)buttonAtIndex:(NSUInteger)index;
+- (HIDGamepadButton*)triggerButtonAtIndex:(NSUInteger)index;
+- (HIDGamepadAxis*)getHatSwitch;
+@end
+
+@implementation HIDGamepad
+
+@synthesize hidDeviceRef;
+@synthesize queueRef;
+@synthesize buttons;
+@synthesize triggerButtons;
+@synthesize axes;
+@synthesize hatSwitch;
+
+- initWithDevice:(IOHIDDeviceRef)rawDevice
+{
+    if((self = [super init]))
+    {
+        [self setButtons:[NSMutableArray array]];
+        [self setTriggerButtons:[NSMutableArray array]];
+        [self setAxes:[NSMutableArray array]];
+        hatSwitch = NULL;
+
+        CFRetain(rawDevice);
+        IOHIDQueueRef queue = IOHIDQueueCreate(CFAllocatorGetDefault(), rawDevice, 10, kIOHIDOptionsTypeNone);
+        [self setHidDeviceRef:rawDevice];
+        [self setQueueRef:queue];
+        
+        [self initializeGamepadElements];
+        [self startListening];
+    }
+    return self;
+}
+
+- (void)dealloc
+{
+    [self stopListening];
+    
+    CFRelease([self rawDevice]);
+    CFRelease([self queueRef]);
+    [self setQueueRef:NULL];
+    [self setHidDeviceRef:NULL];
+    
+    [self setButtons:NULL];
+    [self setTriggerButtons:NULL];
+    [self setAxes:NULL];
+    if (hatSwitch != NULL)
+    {
+        [hatSwitch dealloc];
+    }
+    
+    [super dealloc];
+}
+
+- (IOHIDDeviceRef)rawDevice
+{
+    return [self hidDeviceRef];
+}
+
+- (NSNumber*)locationID
+{
+    return (NSNumber*)IOHIDDeviceGetProperty([self rawDevice], CFSTR(kIOHIDLocationIDKey));
+}
+
+- (void)initializeGamepadElements
+{
+    uint32_t vendorID = [self vendorID];
+    uint32_t productID = [self productID];
+    
+    CFArrayRef elements = IOHIDDeviceCopyMatchingElements([self rawDevice], NULL, kIOHIDOptionsTypeNone);
+    for(int i = 0; i < CFArrayGetCount(elements); i++)
+    {
+        IOHIDElementRef hidElement = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
+        IOHIDElementType type = IOHIDElementGetType(hidElement);
+
+        if (type == kIOHIDElementTypeInput_Misc || type == kIOHIDElementTypeInput_Axis)
+        {
+            uint32_t pageUsage = IOHIDElementGetUsage(hidElement);
+            IOHIDElementCookie cookie = IOHIDElementGetCookie(hidElement);
+
+            switch(pageUsage)
+            {
+                case kHIDUsage_GD_X:
+                case kHIDUsage_GD_Y:
+                case kHIDUsage_GD_Rx:
+                case kHIDUsage_GD_Ry:
+                case kHIDUsage_GD_Z:
+                case kHIDUsage_GD_Rz:
+                {
+                    if (vendorID == MICROSOFT_VENDOR_ID &&
+                        productID == MICROSOFT_XBOX360_PRODUCT_ID &&
+                        (pageUsage == kHIDUsage_GD_Z || pageUsage == kHIDUsage_GD_Rz))
+                    {
+                        HIDGamepadButton* triggerButton = [HIDGamepadButton gamepadButtonWithButtonElement:hidElement];
+                        [triggerButton setTriggerElement:hidElement];
+                        [[self triggerButtons] addObject:triggerButton];
+                    }
+                    else
+                    {
+                        HIDGamepadAxis* axis = [HIDGamepadAxis gamepadAxisWithAxisElement:hidElement];
+                        [[self axes] addObject:axis];
+                    }
+                    break;
+                }
+                case kHIDUsage_GD_Hatswitch:
+                {
+                    HIDGamepadAxis* hat = [[HIDGamepadAxis alloc] initWithAxisElement:hidElement];
+                    [hat setValue: -1];
+                    hatSwitch = hat;
+                }
+                default:
+                    // Ignore the pointers
+                    // Note: Some of the pointers are for the 6-axis accelerometer in a PS3 controller
+                    // Note: L2/R2 triggers are at cookie 39 and 40 base 10 tied to 9 and 10 button elements
+                    break;
+            }
+
+        }
+        if(type == kIOHIDElementTypeInput_Button)
+        {
+            HIDGamepadButton *button = [HIDGamepadButton gamepadButtonWithButtonElement:hidElement];
+            [[self buttons] addObject:button];
+        }
+    }
+    // Go back and get proprietary information (e.g. triggers) and associate with appropriate values
+    // Example for other trigger buttons
+    for(int i = 0; i < CFArrayGetCount(elements); i++)
+    {
+        IOHIDElementRef hidElement = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
+        IOHIDElementType type = IOHIDElementGetType(hidElement);
+        IOHIDElementCookie cookie = IOHIDElementGetCookie(hidElement);
+        
+        // Gamepad specific code
+        if(vendorID == SONY_USB_VENDOR_ID && productID == SONY_USB_PS3_PRODUCT_ID)
+        {
+            if((unsigned long)cookie == 39)
+            {
+                HIDGamepadButton *leftTriggerButton = [self buttonWithCookie:(IOHIDElementCookie)9];
+                if(leftTriggerButton)
+                {
+                    [leftTriggerButton setTriggerElement:hidElement];
+                    [[self triggerButtons] addObject:leftTriggerButton];
+                }
+            }
+            if((unsigned long)cookie == 40)
+            {
+                HIDGamepadButton *rightTriggerButton = [self buttonWithCookie:(IOHIDElementCookie)10];
+                if(rightTriggerButton)
+                {
+                    [rightTriggerButton setTriggerElement:hidElement];
+                    [[self triggerButtons] addObject:rightTriggerButton];
+                }
+            }
+        }
+    }
+}
+
+- (HIDGamepadButton*)buttonWithCookie:(IOHIDElementCookie)cookie {
+    for(HIDGamepadButton *b in [self buttons]) {
+        if([b cookie] == cookie)
+            return b;
+    }
+    return NULL;
+}
+
+- (bool)startListening
+{
+    IOReturn kr = IOHIDDeviceOpen([self hidDeviceRef], kIOHIDOptionsTypeNone);
+    if(kr != 0) {
+        return false;
+    }
+    IOHIDDeviceScheduleWithRunLoop([self hidDeviceRef], CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+    
+    IOHIDQueueStart([self queueRef]);
+    IOHIDQueueRegisterValueAvailableCallback([self queueRef], hidDeviceValueAvailableCallback, self);
+    
+    CFArrayRef elements = (CFArrayRef)[self watchedElements];
+    for(int i = 0; i < CFArrayGetCount(elements); i++)
+    {
+        IOHIDElementRef hidElement = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
+        IOHIDQueueAddElement([self queueRef], hidElement);
+    }
+    
+    IOHIDQueueScheduleWithRunLoop([self queueRef], CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+    
+    return true;
+}
+
+- (void)stopListening
+{
+    IOHIDQueueUnscheduleFromRunLoop([self queueRef], CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+    IOHIDQueueStop([self queueRef]);
+    
+    IOHIDDeviceUnscheduleFromRunLoop([self hidDeviceRef], CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+    IOHIDDeviceClose([self hidDeviceRef], kIOHIDOptionsTypeNone);
+}
+
+- (NSString*)identifierName
+{
+    NSString* idName = NULL;
+    if(idName == NULL) idName = [self productName];
+    if(idName == NULL) idName = [self manufacturerName];
+    if(idName == NULL) idName = [self serialNumber];
+    if(idName == NULL) idName = [NSString stringWithFormat:@"%d-%d", [self vendorID], [self productID]];
+    return idName;
+}
+
+- (NSString*)productName
+{
+    CFStringRef productName = (CFStringRef)IOHIDDeviceGetProperty([self rawDevice], CFSTR(kIOHIDProductKey));
+    if(productName == NULL || CFGetTypeID(productName) != CFStringGetTypeID())
+    {
+        return NULL;
+    }
+    return (NSString*)productName;
+}
+
+- (NSString*)manufacturerName
+{
+    CFStringRef manufacturerName = (CFStringRef)IOHIDDeviceGetProperty([self rawDevice], CFSTR(kIOHIDManufacturerKey));
+    if(manufacturerName == NULL || CFGetTypeID(manufacturerName) != CFStringGetTypeID())
+    {
+        return NULL;
+    }
+    return (NSString*)manufacturerName;
+}
+
+- (NSString*)serialNumber
+{
+    CFStringRef serialNumber = (CFStringRef)IOHIDDeviceGetProperty([self rawDevice], CFSTR(kIOHIDSerialNumberKey));
+    if(serialNumber == NULL || CFGetTypeID(serialNumber) != CFStringGetTypeID())
+    {
+        return NULL;
+    }
+    return (NSString*)serialNumber;
+}
+
+- (int)versionNumber
+{
+    return IOHIDDeviceGetIntProperty([self rawDevice], CFSTR(kIOHIDVersionNumberKey));
+}
+
+- (int)vendorID
+{
+    return IOHIDDeviceGetIntProperty([self rawDevice], CFSTR(kIOHIDVendorIDKey));
+}
+
+- (int)productID
+{
+    return IOHIDDeviceGetIntProperty([self rawDevice], CFSTR(kIOHIDProductIDKey));
+}
+
+- (NSUInteger)numberOfAxes
+{
+    return [[self axes] count];
+}
+
+- (NSUInteger)numberOfSticks
+{
+    return ([[self axes] count] / 2);
+}
+
+- (NSUInteger)numberOfButtons
+{
+    return [[self buttons] count];
+}
+
+- (NSUInteger)numberOfTriggerButtons
+{
+    return [[self triggerButtons] count];
+}
+
+- (HIDGamepadButton*)triggerButtonAtIndex:(NSUInteger)index
+{
+    HIDGamepadButton *b = NULL;
+    if(index < [[self triggerButtons] count])
+    {
+        b = [[self triggerButtons] objectAtIndex:index];
+    }
+    return b;
+}
+
+- (HIDGamepadAxis*)axisAtIndex:(NSUInteger)index
+{
+    HIDGamepadAxis *a = NULL;
+    if(index < [[self axes] count])
+    {
+        a = [[self axes] objectAtIndex:index];
+    }
+    return a;
+}
+
+- (HIDGamepadButton*)buttonAtIndex:(NSUInteger)index
+{
+    HIDGamepadButton *b = NULL;
+    if(index < [[self buttons] count])
+    {
+        b = [[self buttons] objectAtIndex:index];
+    }
+    return b;
+}
+
+- (HIDGamepadAxis*)getHatSwitch
+{
+    if (hatSwitch != NULL)
+    {
+        return hatSwitch;
+    }
+    return NULL;
+}
+
+- (NSArray*)watchedElements
+{
+    NSMutableArray *r = [NSMutableArray array];
+    for(HIDGamepadButton *b in [self buttons])
+    {
+        [r addObject:(id)[b element]];
+    }
+    for(HIDGamepadAxis *a in [self axes])
+    {
+        [r addObject:(id)[a element]];
+    }
+    for(HIDGamepadButton* t in [self triggerButtons])
+    {
+        [r addObject:(id)[t triggerElement]];
+    }
+    if (hatSwitch)
+    {
+        [r addObject:(id)[hatSwitch element]];
+    }
+    return [NSArray arrayWithArray:r];
+}
+
+- (void)hidValueAvailable:(IOHIDValueRef)value
+{
+    IOHIDElementRef element = IOHIDValueGetElement(value);
+    IOHIDElementCookie cookie = IOHIDElementGetCookie(element);
+    
+    if(IOHIDValueGetLength(value) > 4) return; // saftey precaution for PS3 cotroller
+    CFIndex integerValue = IOHIDValueGetIntegerValue(value);
+    
+    for(HIDGamepadAxis *a in [self axes])
+    {
+        if([a cookie] == cookie)
+        {
+            [a setValue:integerValue];
+        }
+    }
+    
+    for(HIDGamepadButton *b in [self buttons])
+    {
+        if([b cookie] == cookie)
+        {
+            [b setState:(bool)integerValue];
+            break;
+        }
+    }
+    
+    for(HIDGamepadButton *b in [self triggerButtons])
+    {
+        if([b triggerCookie] == cookie)
+        {
+            [b setStateValue:integerValue];
+            break;
+        }
+    }
+
+    if (hatSwitch && [hatSwitch cookie] == cookie)
+    {
+        [hatSwitch setValue:integerValue];
+    }
+}
+@end
+
+
+@interface View : NSOpenGLView <NSWindowDelegate>
+{
+@public
+    CVDisplayLinkRef displayLink;
+    NSRecursiveLock* gameLock;
+
+@protected
+    Game* _game;
+    unsigned int _gestureEvents;
+}    
+- (void) detectGamepads: (Game*) game;
+
+@end
+
+
+@implementation View
+
+-(void)windowWillClose:(NSNotification*)note 
+{
+    [gameLock lock];
+    _game->exit();
+    [gameLock unlock];
+    [[NSApplication sharedApplication] terminate:self];
+}
+
+- (void)windowDidResize:(NSNotification*)notification
+{
+    [gameLock lock];
+    NSSize size = [ [ _window contentView ] frame ].size;
+    gameplay::Platform::resizeEventInternal((unsigned int)size.width, (unsigned int)size.height);
+    [gameLock unlock];
+}
+
+- (CVReturn) getFrameForTime:(const CVTimeStamp*)outputTime
+{
+    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+    
+    [self update];
+    
+    [pool release];
+    
+    return kCVReturnSuccess;
+}
+
+- (void) detectGamepads: (Game*) game
+{
+    // Locate any newly connected devices
+    for(HIDGamepad* gamepad in __gamepads)
+    {
+        NSNumber* locationID = [gamepad locationID];
+        if([__activeGamepads objectForKey:locationID] == NULL)
+        {            
+            // Gameplay::add is friended to Platform, but we're not in Platform right now.
+            Platform::gamepadEventConnectedInternal((unsigned int)[locationID intValue],
+                                                    [gamepad numberOfButtons],
+                                                    [gamepad numberOfSticks],
+                                                    [gamepad numberOfTriggerButtons],
+                                                    [gamepad vendorID],
+                                                    [gamepad productID],
+                                                    [[gamepad manufacturerName] cStringUsingEncoding:NSASCIIStringEncoding],
+                                                    [[gamepad productName] cStringUsingEncoding:NSASCIIStringEncoding]);
+
+            [__activeGamepads setObject:locationID forKey:locationID];
+        }
+    }
+    
+    // Detect any disconnected gamepads
+    NSMutableArray* deadGamepads = [NSMutableArray array];
+    for(NSNumber* locationID in __activeGamepads)
+    {
+        HIDGamepad* gamepad = gamepadForLocationID(locationID);
+        if(gamepad == NULL)
+        {
+            NSNumber* gameHandle = [__activeGamepads objectForKey:locationID];
+            Platform::gamepadEventDisconnectedInternal((unsigned int)[locationID intValue]);
+            [deadGamepads addObject:locationID];
+        }
+    }
+    [__activeGamepads removeObjectsForKeys:deadGamepads];
+}
+
+-(void) update
+{       
+    [gameLock lock];
+
+    [[self openGLContext] makeCurrentContext];
+    CGLLockContext((CGLContextObj)[[self openGLContext] CGLContextObj]);
+    if (_game)
+    {
+        [self detectGamepads: _game];
+        
+        _game->frame();
+    }
+    CGLFlushDrawable((CGLContextObj)[[self openGLContext] CGLContextObj]);
+    CGLUnlockContext((CGLContextObj)[[self openGLContext] CGLContextObj]);  
+
+    [gameLock unlock];
+}
+
+static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, 
+                                      CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
+{
+    CVReturn result = [(View*)displayLinkContext getFrameForTime:outputTime];
+    return result;
+}
+
+- (id) initWithFrame: (NSRect) frame
+{
+    _game = Game::getInstance();
+    
+    Properties* config = _game->getConfig()->getNamespace("window", true);
+    int samples = config ? config->getInt("samples") : 0;
+    if (samples < 0)
+        samples = 0;
+    
+    // Note: Keep multisampling attributes at the start of the attribute lists since code below
+    // assumes they are array elements 0 through 4.
+    NSOpenGLPixelFormatAttribute windowedAttrs[] = 
+    {
+        NSOpenGLPFAMultisample,
+        NSOpenGLPFASampleBuffers, samples ? 1 : 0,
+        NSOpenGLPFASamples, samples,
+        NSOpenGLPFAAccelerated,
+        NSOpenGLPFADoubleBuffer,
+        NSOpenGLPFAColorSize, 32,
+        NSOpenGLPFADepthSize, 24,
+        NSOpenGLPFAAlphaSize, 8,
+        NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersionLegacy,
+        0
+    };
+    NSOpenGLPixelFormatAttribute fullscreenAttrs[] = 
+    {
+        NSOpenGLPFAMultisample,
+        NSOpenGLPFASampleBuffers, samples ? 1 : 0,
+        NSOpenGLPFASamples, samples,
+        NSOpenGLPFADoubleBuffer,
+        NSOpenGLPFAScreenMask, (NSOpenGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(CGMainDisplayID()),
+        NSOpenGLPFAFullScreen,
+        NSOpenGLPFAColorSize, 32,
+        NSOpenGLPFADepthSize, 24,
+        NSOpenGLPFAAlphaSize, 8,
+        NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersionLegacy,
+        0
+    };
+    NSOpenGLPixelFormatAttribute* attrs = __fullscreen ? fullscreenAttrs : windowedAttrs;
+    
+    __multiSampling = samples > 0;
+
+    // Try to choose a supported pixel format
+    NSOpenGLPixelFormat* pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
+    if (!pf)
+    {
+        bool valid = false;
+        while (!pf && samples > 0)
+        {
+            samples /= 2;
+            attrs[2] = samples ? 1 : 0;
+            attrs[4] = samples;
+            pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
+            if (pf)
+            {
+                valid = true;
+                break;
+            }
+        }
+
+        __multiSampling = samples > 0;
+        
+        if (!valid)
+        {
+            NSLog(@"OpenGL pixel format not supported.");
+            GP_ERROR("Failed to create a valid OpenGL pixel format.");
+            return nil;
+        }
+    }
+    
+    if ((self = [super initWithFrame:frame pixelFormat:[pf autorelease]])) 
+    {
+        gameLock = [[NSRecursiveLock alloc] init];
+        __timeStart = getMachTimeInMilliseconds();
+    }
+    
+    return self;
+}
+
+- (void) prepareOpenGL
+{
+    [super prepareOpenGL];
+    
+    _game->run();
+    
+    if (__fullscreen)
+    {
+        [[self window] setLevel: NSMainMenuWindowLevel+1];
+        [[self window] setHidesOnDeactivate:YES]; 
+    }
+    else
+    {
+        [[self window] setLevel: NSNormalWindowLevel];
+    }
+    [[self window] makeKeyAndOrderFront: self];
+    [[self window] setTitle: [NSString stringWithUTF8String: __title ? __title : ""]];
+    
+    // Make all the OpenGL calls to setup rendering and build the necessary rendering objects
+    [[self openGLContext] makeCurrentContext];
+    // Synchronize buffer swaps with vertical refresh rate
+    GLint swapInt = __vsync ? 1 : 0;
+    [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
+    
+    // Create a display link capable of being used with all active displays
+    CVDisplayLinkCreateWithActiveCGDisplays(&displayLink);
+    
+    // Set the renderer output callback function
+    CVDisplayLinkSetOutputCallback(displayLink, &MyDisplayLinkCallback, self);
+    
+    CGLContextObj cglContext = (CGLContextObj)[[self openGLContext] CGLContextObj];
+    CGLPixelFormatObj cglPixelFormat = (CGLPixelFormatObj)[[self pixelFormat] CGLPixelFormatObj];
+    CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat);
+    
+    GLint dim[2] = {__width, __height};
+    CGLSetParameter(cglContext, kCGLCPSurfaceBackingSize, dim);
+    CGLEnable(cglContext, kCGLCESurfaceBackingSize);
+    
+    // Activate the display link
+    CVDisplayLinkStart(displayLink);
+}
+
+- (void) dealloc
+{   
+    [gameLock lock];
+    
+    // Release the display link
+    CVDisplayLinkStop(displayLink);
+    CVDisplayLinkRelease(displayLink);
+    _game->exit();
+    
+    [gameLock unlock];
+
+    [super dealloc];
+}
+
+- (void)resumeDisplayRenderer 
+{
+    [gameLock lock];
+    CVDisplayLinkStop(displayLink);
+    [gameLock unlock]; 
+}
+
+- (void)haltDisplayRenderer 
+{
+    [gameLock lock];
+    CVDisplayLinkStop(displayLink);
+    [gameLock unlock];
+}
+
+- (void) mouse: (Mouse::MouseEvent) mouseEvent orTouchEvent: (Touch::TouchEvent) touchEvent x: (float) x y: (float) y s: (int) s 
+{
+    [__view->gameLock lock];
+    if (!gameplay::Platform::mouseEventInternal(mouseEvent, x, y, s))
+    {
+        gameplay::Platform::touchEventInternal(touchEvent, x, y, 0);
+    }
+    [__view->gameLock unlock];
+}
+
+- (void) mouseDown: (NSEvent*) event
+{
+    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
+    __leftMouseDown = true;
+    [self mouse: Mouse::MOUSE_PRESS_LEFT_BUTTON orTouchEvent: Touch::TOUCH_PRESS x: point.x y: __height - point.y s: 0];
+}
+
+- (void) mouseUp: (NSEvent*) event
+{
+    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
+    __leftMouseDown = false;
+    [self mouse: Mouse::MOUSE_RELEASE_LEFT_BUTTON orTouchEvent: Touch::TOUCH_RELEASE x: point.x y: __height - point.y s: 0];
+
+}
+
+- (void)mouseMoved:(NSEvent*) event 
+{
+    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
+    
+    float y;
+    if (__mouseCaptured)
+    {
+        if (__mouseCapturedFirstPass)
+        {
+            // Discard the first mouseMoved event following transition into capture
+            // since it contains bogus x,y data.
+            __mouseCapturedFirstPass = false;
+            return;
+        }
+
+        point.x = [event deltaX];
+        point.y = [event deltaY];
+
+        NSWindow* window = __view.window;
+        NSRect rect = window.frame;
+        CGPoint centerPoint;
+        centerPoint.x = rect.origin.x + (rect.size.width / 2);
+        centerPoint.y = rect.origin.y + (rect.size.height / 2);
+        CGDisplayMoveCursorToPoint(CGDisplayPrimaryDisplay(NULL), centerPoint);
+        y = point.y;
+    }
+    else
+    {
+        y = __height - point.y;
+    }
+    
+    [__view->gameLock lock];
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, point.x, y, 0);
+    [__view->gameLock unlock];
+}
+
+- (void) mouseDragged: (NSEvent*) event
+{
+    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
+    if (__leftMouseDown && !__mouseCaptured)
+    {
+        [self mouse: Mouse::MOUSE_MOVE orTouchEvent: Touch::TOUCH_MOVE x: point.x y: __height - point.y s: 0];
+    }
+}
+
+- (void) rightMouseDown: (NSEvent*) event
+{
+    __rightMouseDown = true;
+     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
+    __lx = point.x;
+    __ly = __height - point.y;
+    
+    [__view->gameLock lock];
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_PRESS_RIGHT_BUTTON, point.x, __height - point.y, 0);
+    [__view->gameLock unlock];
+}
+
+- (void) rightMouseUp: (NSEvent*) event
+{
+    __rightMouseDown = false;
+    NSPoint point = [event locationInWindow];
+    
+    [__view->gameLock lock];
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_RELEASE_RIGHT_BUTTON, point.x, __height - point.y, 0);
+    [__view->gameLock unlock];
+}
+
+- (void) rightMouseDragged: (NSEvent*) event
+{
+    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
+    if (__rightMouseDown)
+    {
+        // Update the pitch and roll by adding the scaled deltas.
+        __roll += (float)(point.x - __lx) * ACCELEROMETER_FACTOR_X;
+        __pitch -= -(float)(point.y - (__height - __ly)) * ACCELEROMETER_FACTOR_Y;
+    
+        // Clamp the values to the valid range.
+        __roll = max(min(__roll, 90.0f), -90.0f);
+        __pitch = max(min(__pitch, 90.0f), -90.0f);
+    
+        // Update the last X/Y values.
+        __lx = point.x;
+        __ly = (__height - point.y);
+    }
+    
+    // In right-mouse case, whether __rightMouseDown is true or false
+    // this should not matter, mouse move is still occuring
+    [__view->gameLock lock];
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, point.x, __height - point.y, 0);
+    [__view->gameLock unlock];
+}
+
+- (void)otherMouseDown: (NSEvent*) event 
+{
+    __otherMouseDown = true;
+    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
+
+    [__view->gameLock lock];
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_PRESS_MIDDLE_BUTTON, point.x, __height - point.y, 0);
+    [__view->gameLock unlock];
+}
+
+- (void)otherMouseUp: (NSEvent*) event 
+{
+    __otherMouseDown = false;
+    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
+
+    [__view->gameLock lock];
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_RELEASE_MIDDLE_BUTTON, point.x, __height - point.y, 0);
+    [__view->gameLock unlock];
+}
+
+- (void)otherMouseDragged: (NSEvent*) event 
+{
+    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
+
+    [__view->gameLock lock];
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, point.x, __height - point.y, 0);
+    [__view->gameLock unlock];
+}
+
+- (void) mouseEntered: (NSEvent*)event
+{
+    __hasMouse = true;
+}
+
+- (void)scrollWheel: (NSEvent*) event 
+{
+    NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
+
+    [__view->gameLock lock];
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_WHEEL, point.x, __height - point.y, (int)([event deltaY] * 10.0f));
+    [__view->gameLock unlock];
+}
+
+- (void) mouseExited: (NSEvent*)event
+{
+    __leftMouseDown = false;
+    __rightMouseDown = false;
+    __otherMouseDown = false;
+    __hasMouse = false;
+}
+
+- (BOOL)acceptsFirstResponder
+{
+    return YES;
+}
+
+int getKey(unsigned short keyCode, unsigned int modifierFlags)
+{
+    __shiftDown = (modifierFlags & NSShiftKeyMask);
+    unsigned int caps = (__shiftDown ? 1 : 0) ^ ((modifierFlags & NSAlphaShiftKeyMask) ? 1 : 0);
+    switch(keyCode)
+    {
+        case 0x69:
+            return Keyboard::KEY_PRINT;
+        case 0x35:
+            return Keyboard::KEY_ESCAPE;
+        case 0x33:
+            return Keyboard::KEY_BACKSPACE;
+        case 0x30:
+            return Keyboard::KEY_TAB;
+        case 0x24:
+            return Keyboard::KEY_RETURN;
+        case 0x72:
+            return Keyboard::KEY_INSERT;
+        case 0x73:
+            return Keyboard::KEY_HOME;
+        case 0x74:
+            return Keyboard::KEY_PG_UP;
+        case 0x79:
+            return Keyboard::KEY_PG_DOWN;
+        case 0x75:
+            return Keyboard::KEY_DELETE;
+        case 0x77:
+            return Keyboard::KEY_END;
+        case 0x7B:
+            return Keyboard::KEY_LEFT_ARROW;
+        case 0x7C:
+            return Keyboard::KEY_RIGHT_ARROW;
+        case 0x7E:
+            return Keyboard::KEY_UP_ARROW;
+        case 0x7D:
+            return Keyboard::KEY_DOWN_ARROW;
+        case 0x47:
+            return Keyboard::KEY_NUM_LOCK;
+        case 0x45:
+            return Keyboard::KEY_KP_PLUS;
+        case 0x4E:
+            return Keyboard::KEY_KP_MINUS;
+        case 0x43:
+            return Keyboard::KEY_KP_MULTIPLY;
+        case 0x4B:
+            return Keyboard::KEY_KP_DIVIDE;
+        case 0x59:
+            return Keyboard::KEY_KP_HOME;
+        case 0x5B:
+            return Keyboard::KEY_KP_UP;
+        case 0x5C:
+            return Keyboard::KEY_KP_PG_UP;
+        case 0x56:
+            return Keyboard::KEY_KP_LEFT;
+        case 0x57:
+            return Keyboard::KEY_KP_FIVE;
+        case 0x58:
+            return Keyboard::KEY_KP_RIGHT;
+        case 0x53:
+            return Keyboard::KEY_KP_END;
+        case 0x54:
+            return Keyboard::KEY_KP_DOWN;
+        case 0x55:
+            return Keyboard::KEY_KP_PG_DOWN;
+        case 0x52:
+            return Keyboard::KEY_KP_INSERT;
+        case 0x41:
+            return Keyboard::KEY_KP_DELETE;
+        case 0x7A:
+            return Keyboard::KEY_F1;
+        case 0x78:
+            return Keyboard::KEY_F2;
+        case 0x63:
+            return Keyboard::KEY_F3;
+        case 0x76:
+            return Keyboard::KEY_F4;
+        case 0x60:
+            return Keyboard::KEY_F5;
+        case 0x61:
+            return Keyboard::KEY_F6;
+        case 0x62:
+            return Keyboard::KEY_F7;
+        case 0x64:
+            return Keyboard::KEY_F8;
+        case 0x65:
+            return Keyboard::KEY_F9;
+        case 0x6D:
+            return Keyboard::KEY_F10;
+        
+        // MACOS reserved:
+        // return Keyboard::KEY_F11;
+        // return Keyboard::KEY_F12;
+        // return Keyboard::KEY_PAUSE;
+        // return Keyboard::KEY_SCROLL_LOCK;
+            
+        case 0x31:
+            return Keyboard::KEY_SPACE;
+        case 0x1D:
+            return __shiftDown ? Keyboard::KEY_RIGHT_PARENTHESIS : Keyboard::KEY_ZERO;
+        case 0x12:
+            return __shiftDown ? Keyboard::KEY_EXCLAM : Keyboard::KEY_ONE;
+        case 0x13:
+            return __shiftDown ? Keyboard::KEY_AT : Keyboard::KEY_TWO;
+        case 0x14:
+            return __shiftDown ? Keyboard::KEY_NUMBER : Keyboard::KEY_THREE;
+        case 0x15:
+            return __shiftDown ? Keyboard::KEY_DOLLAR : Keyboard::KEY_FOUR;
+        case 0x17:
+            return __shiftDown ? Keyboard::KEY_PERCENT : Keyboard::KEY_FIVE;
+        case 0x16:
+            return __shiftDown ? Keyboard::KEY_CIRCUMFLEX : Keyboard::KEY_SIX;
+        case 0x1A:
+            return __shiftDown ? Keyboard::KEY_AMPERSAND : Keyboard::KEY_SEVEN;
+        case 0x1C:
+            return __shiftDown ? Keyboard::KEY_ASTERISK : Keyboard::KEY_EIGHT;
+        case 0x19:
+            return __shiftDown ? Keyboard::KEY_LEFT_PARENTHESIS : Keyboard::KEY_NINE;
+        case 0x18:
+            return __shiftDown ? Keyboard::KEY_EQUAL : Keyboard::KEY_PLUS;
+        case 0x2B:
+            return __shiftDown ? Keyboard::KEY_LESS_THAN : Keyboard::KEY_COMMA;
+        case 0x1B:
+            return __shiftDown ? Keyboard::KEY_UNDERSCORE : Keyboard::KEY_MINUS;
+        case 0x2F:
+            return __shiftDown ? Keyboard::KEY_GREATER_THAN : Keyboard::KEY_PERIOD;
+        case 0x29:
+            return __shiftDown ? Keyboard::KEY_COLON : Keyboard::KEY_SEMICOLON;
+        case 0x2C:
+            return __shiftDown ? Keyboard::KEY_QUESTION : Keyboard::KEY_SLASH;
+        case 0x32:
+            return __shiftDown ? Keyboard::KEY_GRAVE : Keyboard::KEY_TILDE;
+        case 0x21:
+            return __shiftDown ? Keyboard::KEY_LEFT_BRACE : Keyboard::KEY_LEFT_BRACKET;
+        case 0x2A:
+            return __shiftDown ? Keyboard::KEY_BAR : Keyboard::KEY_BACK_SLASH;
+        case 0x1E:
+            return __shiftDown ? Keyboard::KEY_RIGHT_BRACE : Keyboard::KEY_RIGHT_BRACKET;
+        case 0x27:
+            return __shiftDown ? Keyboard::KEY_QUOTE : Keyboard::KEY_APOSTROPHE;
+            
+        case 0x00:
+            return caps ? Keyboard::KEY_CAPITAL_A : Keyboard::KEY_A;
+        case 0x0B:
+            return caps ? Keyboard::KEY_CAPITAL_B : Keyboard::KEY_B;
+        case 0x08:
+            return caps ? Keyboard::KEY_CAPITAL_C : Keyboard::KEY_C;
+        case 0x02:
+            return caps ? Keyboard::KEY_CAPITAL_D : Keyboard::KEY_D;
+        case 0x0E:
+            return caps ? Keyboard::KEY_CAPITAL_E : Keyboard::KEY_E;
+        case 0x03:
+            return caps ? Keyboard::KEY_CAPITAL_F : Keyboard::KEY_F;
+        case 0x05:
+            return caps ? Keyboard::KEY_CAPITAL_G : Keyboard::KEY_G;
+        case 0x04:
+            return caps ? Keyboard::KEY_CAPITAL_H : Keyboard::KEY_H;
+        case 0x22:
+            return caps ? Keyboard::KEY_CAPITAL_I : Keyboard::KEY_I;
+        case 0x26:
+            return caps ? Keyboard::KEY_CAPITAL_J : Keyboard::KEY_J;
+        case 0x28:
+            return caps ? Keyboard::KEY_CAPITAL_K : Keyboard::KEY_K;
+        case 0x25:
+            return caps ? Keyboard::KEY_CAPITAL_L : Keyboard::KEY_L;
+        case 0x2E:
+            return caps ? Keyboard::KEY_CAPITAL_M : Keyboard::KEY_M;
+        case 0x2D:
+            return caps ? Keyboard::KEY_CAPITAL_N : Keyboard::KEY_N;
+        case 0x1F:
+            return caps ? Keyboard::KEY_CAPITAL_O : Keyboard::KEY_O;
+        case 0x23:
+            return caps ? Keyboard::KEY_CAPITAL_P : Keyboard::KEY_P;
+        case 0x0C:
+            return caps ? Keyboard::KEY_CAPITAL_Q : Keyboard::KEY_Q;
+        case 0x0F:
+            return caps ? Keyboard::KEY_CAPITAL_R : Keyboard::KEY_R;
+        case 0x01:
+            return caps ? Keyboard::KEY_CAPITAL_S : Keyboard::KEY_S;
+        case 0x11:
+            return caps ? Keyboard::KEY_CAPITAL_T : Keyboard::KEY_T;
+        case 0x20:
+            return caps ? Keyboard::KEY_CAPITAL_U : Keyboard::KEY_U;
+        case 0x09:
+            return caps ? Keyboard::KEY_CAPITAL_V : Keyboard::KEY_V;
+        case 0x0D:
+            return caps ? Keyboard::KEY_CAPITAL_W : Keyboard::KEY_W;
+        case 0x07:
+            return caps ? Keyboard::KEY_CAPITAL_X : Keyboard::KEY_X;
+        case 0x10:
+            return caps ? Keyboard::KEY_CAPITAL_Y : Keyboard::KEY_Y;
+        case 0x06:
+            return caps ? Keyboard::KEY_CAPITAL_Z : Keyboard::KEY_Z;
+
+        default:
+            return Keyboard::KEY_NONE;
+    }
+}
+
+/**
+ * Returns the unicode value for the given keycode or zero if the key is not a valid printable character.
+ */
+int getUnicode(int key)
+{
+    
+    switch (key)
+    {
+        case Keyboard::KEY_BACKSPACE:
+            return 0x0008;
+        case Keyboard::KEY_TAB:
+            return 0x0009;
+        case Keyboard::KEY_RETURN:
+        case Keyboard::KEY_KP_ENTER:
+            return 0x000A;
+        case Keyboard::KEY_ESCAPE:
+            return 0x001B;
+        case Keyboard::KEY_SPACE:
+        case Keyboard::KEY_EXCLAM:
+        case Keyboard::KEY_QUOTE:
+        case Keyboard::KEY_NUMBER:
+        case Keyboard::KEY_DOLLAR:
+        case Keyboard::KEY_PERCENT:
+        case Keyboard::KEY_CIRCUMFLEX:
+        case Keyboard::KEY_AMPERSAND:
+        case Keyboard::KEY_APOSTROPHE:
+        case Keyboard::KEY_LEFT_PARENTHESIS:
+        case Keyboard::KEY_RIGHT_PARENTHESIS:
+        case Keyboard::KEY_ASTERISK:
+        case Keyboard::KEY_PLUS:
+        case Keyboard::KEY_COMMA:
+        case Keyboard::KEY_MINUS:
+        case Keyboard::KEY_PERIOD:
+        case Keyboard::KEY_SLASH:
+        case Keyboard::KEY_ZERO:
+        case Keyboard::KEY_ONE:
+        case Keyboard::KEY_TWO:
+        case Keyboard::KEY_THREE:
+        case Keyboard::KEY_FOUR:
+        case Keyboard::KEY_FIVE:
+        case Keyboard::KEY_SIX:
+        case Keyboard::KEY_SEVEN:
+        case Keyboard::KEY_EIGHT:
+        case Keyboard::KEY_NINE:
+        case Keyboard::KEY_COLON:
+        case Keyboard::KEY_SEMICOLON:
+        case Keyboard::KEY_LESS_THAN:
+        case Keyboard::KEY_EQUAL:
+        case Keyboard::KEY_GREATER_THAN:
+        case Keyboard::KEY_QUESTION:
+        case Keyboard::KEY_AT:
+        case Keyboard::KEY_CAPITAL_A:
+        case Keyboard::KEY_CAPITAL_B:
+        case Keyboard::KEY_CAPITAL_C:
+        case Keyboard::KEY_CAPITAL_D:
+        case Keyboard::KEY_CAPITAL_E:
+        case Keyboard::KEY_CAPITAL_F:
+        case Keyboard::KEY_CAPITAL_G:
+        case Keyboard::KEY_CAPITAL_H:
+        case Keyboard::KEY_CAPITAL_I:
+        case Keyboard::KEY_CAPITAL_J:
+        case Keyboard::KEY_CAPITAL_K:
+        case Keyboard::KEY_CAPITAL_L:
+        case Keyboard::KEY_CAPITAL_M:
+        case Keyboard::KEY_CAPITAL_N:
+        case Keyboard::KEY_CAPITAL_O:
+        case Keyboard::KEY_CAPITAL_P:
+        case Keyboard::KEY_CAPITAL_Q:
+        case Keyboard::KEY_CAPITAL_R:
+        case Keyboard::KEY_CAPITAL_S:
+        case Keyboard::KEY_CAPITAL_T:
+        case Keyboard::KEY_CAPITAL_U:
+        case Keyboard::KEY_CAPITAL_V:
+        case Keyboard::KEY_CAPITAL_W:
+        case Keyboard::KEY_CAPITAL_X:
+        case Keyboard::KEY_CAPITAL_Y:
+        case Keyboard::KEY_CAPITAL_Z:
+        case Keyboard::KEY_LEFT_BRACKET:
+        case Keyboard::KEY_BACK_SLASH:
+        case Keyboard::KEY_RIGHT_BRACKET:
+        case Keyboard::KEY_UNDERSCORE:
+        case Keyboard::KEY_GRAVE:
+        case Keyboard::KEY_A:
+        case Keyboard::KEY_B:
+        case Keyboard::KEY_C:
+        case Keyboard::KEY_D:
+        case Keyboard::KEY_E:
+        case Keyboard::KEY_F:
+        case Keyboard::KEY_G:
+        case Keyboard::KEY_H:
+        case Keyboard::KEY_I:
+        case Keyboard::KEY_J:
+        case Keyboard::KEY_K:
+        case Keyboard::KEY_L:
+        case Keyboard::KEY_M:
+        case Keyboard::KEY_N:
+        case Keyboard::KEY_O:
+        case Keyboard::KEY_P:
+        case Keyboard::KEY_Q:
+        case Keyboard::KEY_R:
+        case Keyboard::KEY_S:
+        case Keyboard::KEY_T:
+        case Keyboard::KEY_U:
+        case Keyboard::KEY_V:
+        case Keyboard::KEY_W:
+        case Keyboard::KEY_X:
+        case Keyboard::KEY_Y:
+        case Keyboard::KEY_Z:
+        case Keyboard::KEY_LEFT_BRACE:
+        case Keyboard::KEY_BAR:
+        case Keyboard::KEY_RIGHT_BRACE:
+        case Keyboard::KEY_TILDE:
+            return key;
+        default:
+            return 0;
+    }
+}
+
+- (void)flagsChanged: (NSEvent*)event
+{
+    unsigned int keyCode = [event keyCode];
+    unsigned int flags = [event modifierFlags];
+
+    [__view->gameLock lock];
+    switch (keyCode) 
+    {
+        case 0x39:
+            gameplay::Platform::keyEventInternal((flags & NSAlphaShiftKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_CAPS_LOCK);
+            break;
+        case 0x38:
+            gameplay::Platform::keyEventInternal((flags & NSShiftKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_SHIFT);
+            break;
+        case 0x3C:
+            gameplay::Platform::keyEventInternal((flags & NSShiftKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_SHIFT);
+            break;
+        case 0x3A:
+            gameplay::Platform::keyEventInternal((flags & NSAlternateKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_ALT);
+            break;
+        case 0x3D:
+            gameplay::Platform::keyEventInternal((flags & NSAlternateKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_ALT);
+            break;
+        case 0x3B:
+            gameplay::Platform::keyEventInternal((flags & NSControlKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_CTRL);
+            break;
+        case 0x3E:
+            gameplay::Platform::keyEventInternal((flags & NSControlKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_CTRL);
+            break;
+        case 0x37:
+            gameplay::Platform::keyEventInternal((flags & NSCommandKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_HYPER);
+            break;
+        case 0x36:
+            gameplay::Platform::keyEventInternal((flags & NSCommandKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_HYPER);
+            break;
+    }
+    [__view->gameLock unlock];
+}
+
+- (void) keyDown: (NSEvent*) event
+{
+    if ([event isARepeat] == NO)
+    {
+        int key = getKey([event keyCode], [event modifierFlags]);
+
+        [__view->gameLock lock];
+        gameplay::Platform::keyEventInternal(Keyboard::KEY_PRESS, key);
+        
+        int character = getUnicode(key);
+        if (character)
+        {
+            gameplay::Platform::keyEventInternal(Keyboard::KEY_CHAR, character);
+        }
+
+        [__view->gameLock unlock];
+    }
+}
+
+- (void) keyUp: (NSEvent*) event
+{
+    [__view->gameLock lock];
+    gameplay::Platform::keyEventInternal(Keyboard::KEY_RELEASE, getKey([event keyCode], [event modifierFlags]));
+    [__view->gameLock unlock];
+}
+
+// Gesture support for Mac OS X Trackpads
+- (bool)isGestureRegistered: (Gesture::GestureEvent) evt
+{
+    return ((_gestureEvents & evt) == evt);
+}
+
+- (void)registerGesture: (Gesture::GestureEvent) evt
+{
+    _gestureEvents |= evt;
+}
+
+- (void)unregisterGesture: (Gesture::GestureEvent) evt
+{
+    _gestureEvents &= (~evt);
+}
+
+- (void)magnifyWithEvent:(NSEvent *)event
+{
+    if([self isGestureRegistered:Gesture::GESTURE_PINCH] == false) return;
+    
+    NSSet *touches = [event touchesMatchingPhase:NSTouchPhaseAny  inView:nil];
+    // Approximate the center by adding and averageing for now
+    // Note this is centroid on the physical device be used for touching, not the display
+    float xavg = 0.0f;
+    float yavg = 0.0f;
+    for(NSTouch *t in touches) {
+        xavg += [t normalizedPosition].x;
+        yavg += [t normalizedPosition].y;
+    }
+    xavg /= [touches count];
+    yavg /= [touches count];
+    
+    [gameLock lock];
+    _game->gesturePinchEvent((int)xavg, (int)yavg, [event magnification]);
+    [gameLock unlock];
+}
+
+
+@end
+
+@interface FullscreenWindow : NSWindow
+{ 
+}
+@end
+
+@implementation FullscreenWindow
+- (BOOL)canBecomeKeyWindow
+{
+    return YES;
+}
+@end
+
+
+namespace gameplay
+{
+
+extern void print(const char* format, ...)
+{
+    GP_ASSERT(format);
+    va_list argptr;
+    va_start(argptr, format);
+    vfprintf(stderr, format, argptr);
+    va_end(argptr);
+}
+    
+Platform::Platform(Game* game)
+: _game(game)
+{
+    __activeGamepads = [[NSMutableDictionary alloc] init];
+    __gamepads = [[NSMutableArray alloc] init];
+    __hidManagerRef = IOHIDManagerCreate(CFAllocatorGetDefault(), kIOHIDOptionsTypeNone);
+    IOHIDManagerRegisterDeviceMatchingCallback(__hidManagerRef, hidDeviceDiscoveredCallback, NULL);
+    IOHIDManagerRegisterDeviceRemovalCallback(__hidManagerRef, hidDeviceRemovalCallback, NULL);
+    
+    CFMutableArrayRef matching = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+    if (matching)
+    {
+        CFDictionaryRef matchingJoystick = IOHIDCreateDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick);
+        CFDictionaryRef matchingGamepad = IOHIDCreateDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad);
+        
+        if (matchingJoystick && matchingGamepad)
+        {
+            CFArrayAppendValue(matching, matchingJoystick);
+            CFRelease(matchingJoystick);
+            CFArrayAppendValue(matching, matchingGamepad);
+            CFRelease(matchingGamepad);
+            IOHIDManagerSetDeviceMatchingMultiple(__hidManagerRef, matching);
+        }
+    }
+    
+    IOHIDManagerScheduleWithRunLoop(__hidManagerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+    IOReturn kr = IOHIDManagerOpen(__hidManagerRef, kIOHIDOptionsTypeNone);
+    assert(kr == 0);
+}
+
+    
+Platform::~Platform()
+{
+    IOHIDManagerUnscheduleFromRunLoop(__hidManagerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+    IOHIDManagerClose(__hidManagerRef, kIOHIDOptionsTypeNone);
+    
+    CFRelease(__hidManagerRef);
+    __hidManagerRef = NULL;
+    [__activeGamepads release];
+    __activeGamepads = NULL;
+    [__gamepads release];
+    __gamepads = NULL;
+}
+
+    
+Platform* Platform::create(Game* game, void* attachToWindow)
+{
+    __attachToWindow = attachToWindow;
+    Platform* platform = new Platform(game);
+    
+    return platform;
+}
+
+int Platform::enterMessagePump()
+{
+    NSString* bundlePath = [[NSBundle mainBundle] bundlePath];
+    NSString* path = [bundlePath stringByAppendingString:@"/Contents/Resources/"];
+    FileSystem::setResourcePath([path cStringUsingEncoding:NSASCIIStringEncoding]);
+    
+    // Read window settings from config.
+    if (_game->getConfig())
+    {
+        Properties* config = _game->getConfig()->getNamespace("window", true);
+        if (config)
+        {
+            // Read window title.
+            __title = const_cast<char *>(config->getString("title"));
+
+            // Read window size.
+            int width = config->getInt("width");
+            if (width != 0)
+                __width = width;
+            int height = config->getInt("height");
+            if (height != 0)
+                __height = height;
+
+            // Read fullscreen state.
+            __fullscreen = config->getBool("fullscreen");
+            if (__fullscreen && width == 0 && height == 0)
+            {
+                CGRect mainMonitor = CGDisplayBounds(CGMainDisplayID());
+                __width = CGRectGetWidth(mainMonitor);
+                __height = CGRectGetHeight(mainMonitor);
+            }
+            
+            // Read resizable state.
+            __resizable = config->getBool("resizable");
+        }
+    }
+
+    // Set the scale factors for the mouse movement used to simulate the accelerometer.
+    ACCELEROMETER_FACTOR_X = 90.0f / __width;
+    ACCELEROMETER_FACTOR_Y = 90.0f / __height;
+
+    NSAutoreleasePool* pool = [NSAutoreleasePool new];
+    NSApplication* app = [NSApplication sharedApplication];
+    NSRect screenBounds = [[NSScreen mainScreen] frame];
+    NSRect viewBounds = NSMakeRect(0, 0, __width, __height);
+    
+    __view = [[View alloc] initWithFrame:viewBounds];
+    if (__view == NULL)
+    {
+        GP_ERROR("Failed to create view: exiting.");
+        return EXIT_FAILURE;
+    }
+    
+    NSRect centered = NSMakeRect(NSMidX(screenBounds) - NSMidX(viewBounds),
+                                 NSMidY(screenBounds) - NSMidY(viewBounds),
+                                 viewBounds.size.width, 
+                                 viewBounds.size.height);
+    
+    NSWindow* window = NULL;
+    if (__fullscreen)
+    {
+        window = [[FullscreenWindow alloc]
+                   initWithContentRect:screenBounds
+                   styleMask:NSBorderlessWindowMask
+                   backing:NSBackingStoreBuffered
+                   defer:NO];
+    }
+    else
+    {
+        window = [[NSWindow alloc]
+                   initWithContentRect:centered
+                  styleMask:NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask
+                   backing:NSBackingStoreBuffered
+                   defer:NO];
+    }
+    
+    [window setAcceptsMouseMovedEvents:YES];
+    [window setContentView:__view];
+    [window setDelegate:__view];
+    [__view release];
+    
+    [app run];
+    
+    [pool release];
+    return EXIT_SUCCESS;
+}
+
+void Platform::signalShutdown() 
+{
+    [__view haltDisplayRenderer];
+
+    // Don't perform terminate right away, enqueue to give game object
+    // a chance to cleanup
+    NSApplication* app = [NSApplication sharedApplication];
+    [app performSelectorOnMainThread:@selector(terminate:) withObject:nil waitUntilDone:NO];
+}
+
+bool Platform::canExit()
+{
+    return true;
+}
+    
+unsigned int Platform::getDisplayWidth()
+{
+    return __width;
+}
+
+unsigned int Platform::getDisplayHeight()
+{
+    return __height;
+}
+
+double Platform::getAbsoluteTime()
+{
+    __timeAbsolute = getMachTimeInMilliseconds();
+    return __timeAbsolute;
+}
+
+void Platform::setAbsoluteTime(double time)
+{
+    __timeAbsolute = time;
+}
+
+bool Platform::isVsync()
+{
+    return __vsync;
+}
+
+void Platform::setVsync(bool enable)
+{
+    __vsync = enable;
+    GLint swapInt = enable ? 1 : 0;
+    [[__view openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
+}
+
+void Platform::swapBuffers()
+{
+    if (__view)
+        CGLFlushDrawable((CGLContextObj)[[__view openGLContext] CGLContextObj]);
+}
+
+void Platform::sleep(long ms)
+{
+    usleep(ms * 1000);
+}
+
+void Platform::setMultiSampling(bool enabled)
+{
+    if (enabled == __multiSampling)
+    {
+        return;
+    }
+
+    //todo
+
+    __multiSampling = enabled;
+}
+
+bool Platform::isMultiSampling()
+{
+    return __multiSampling;
+}
+
+void Platform::setMultiTouch(bool enabled)
+{
+}
+    
+bool Platform::isMultiTouch()
+{
+    return true;
+}
+    
+void Platform::getAccelerometerValues(float* pitch, float* roll)
+{
+    GP_ASSERT(pitch);
+    GP_ASSERT(roll);
+
+    *pitch = __pitch;
+    *roll = __roll;
+}
+
+void Platform::getArguments(int* argc, char*** argv)
+{
+    if (argc)
+        *argc = __argc;
+    if (argv)
+        *argv = __argv;
+}
+    
+bool Platform::hasMouse()
+{
+    return true;
+}
+
+void Platform::setMouseCaptured(bool captured)
+{
+    if (captured != __mouseCaptured)
+    {
+        if (captured)
+        {
+            [NSCursor hide];
+            __mouseCapturedFirstPass = true;
+        }
+        else
+        {   
+            [NSCursor unhide];
+        }
+        NSWindow* window = __view.window;
+        NSRect rect = window.frame;
+        CGPoint centerPoint;
+        centerPoint.x = rect.origin.x + (rect.size.width / 2);
+        centerPoint.y = rect.origin.y + (rect.size.height / 2);
+        CGDisplayMoveCursorToPoint(CGDisplayPrimaryDisplay(NULL), centerPoint);
+        __mouseCaptured = captured;
+    }
+}
+
+bool Platform::isMouseCaptured()
+{
+    return __mouseCaptured;
+}
+
+void Platform::setCursorVisible(bool visible)
+{
+    if (visible != __cursorVisible)
+    {
+        if (visible)
+        {
+             [NSCursor unhide];
+        }
+        else 
+        {
+             [NSCursor hide];
+        }
+        __cursorVisible = visible;
+    }
+}
+
+bool Platform::isCursorVisible()
+{
+    return __cursorVisible;
+}
+
+void Platform::displayKeyboard(bool display)
+{
+    // Do nothing.
+}
+
+void Platform::shutdownInternal()
+{
+    Game::getInstance()->shutdown();
+}
+
+bool Platform::isGestureSupported(Gesture::GestureEvent evt)
+{
+    // Swipe unsupported as it is considered moving mouse cursor
+    // Two fingers is scrolling
+    // Three fingers is swipe, but is not always enabled on users system
+    // Tap not supported as it is considered a mouse click/button click
+    // on some systems making it difficult to differentiate 
+    switch(evt)
+    {
+        case Gesture::GESTURE_PINCH:
+            return true;
+        default:
+            break;
+    }
+    return false;
+}
+
+void Platform::registerGesture(Gesture::GestureEvent evt)
+{
+    [__view registerGesture:evt];   
+}
+
+void Platform::unregisterGesture(Gesture::GestureEvent evt)
+{
+    [__view unregisterGesture:evt];        
+}
+  
+bool Platform::isGestureRegistered(Gesture::GestureEvent evt)
+{
+     return [__view isGestureRegistered:evt];
+}
+
+void Platform::pollGamepadState(Gamepad* gamepad)
+{
+    HIDGamepad* gp = gamepadForGameHandle(gamepad->_handle);
+    if (gp)
+    {
+        // Haven't figured out how to have the triggers not also show up in the buttons array.
+        // So for now a value of -1 means "Don't map this button."
+        static const int PS3Mapping[17] = {
+            Gamepad::BUTTON_MENU1,  // 0x0001
+            Gamepad::BUTTON_L3,     // 0x0002
+            Gamepad::BUTTON_R3,     // 0x0004
+            Gamepad::BUTTON_MENU2,  // 0x0008
+            Gamepad::BUTTON_UP,     // 0x0010
+            Gamepad::BUTTON_RIGHT,  // 0x0020
+            Gamepad::BUTTON_DOWN,   // 0x0040
+            Gamepad::BUTTON_LEFT,   // 0x0080
+            -1,                     // Gamepad::BUTTON_L2,     // 0x0100
+            -1,                     // Gamepad::BUTTON_R2,     // 0x0200
+            Gamepad::BUTTON_L1,     // 0x0400
+            Gamepad::BUTTON_R1,     // 0x0800
+            Gamepad::BUTTON_Y,      // 0x1000
+            Gamepad::BUTTON_B,      // 0x2000
+            Gamepad::BUTTON_A,      // 0x4000
+            Gamepad::BUTTON_X,      // 0x8000
+            Gamepad::BUTTON_MENU3   // 0x10000
+        };
+        
+        static const int XBox360Mapping[20] = {
+            -1, -1, -1, -1, -1,
+            Gamepad::BUTTON_UP,
+            Gamepad::BUTTON_DOWN,
+            Gamepad::BUTTON_LEFT,
+            Gamepad::BUTTON_RIGHT,
+            Gamepad::BUTTON_MENU2,
+            Gamepad::BUTTON_MENU1,
+            Gamepad::BUTTON_L3,
+            Gamepad::BUTTON_R3,
+            Gamepad::BUTTON_L1,
+            Gamepad::BUTTON_R1,
+            Gamepad::BUTTON_MENU3,
+            Gamepad::BUTTON_A,
+            Gamepad::BUTTON_B,
+            Gamepad::BUTTON_X,
+            Gamepad::BUTTON_Y
+        };
+        
+        static const int SteelSeriesFreeMapping[13] = {
+            Gamepad::BUTTON_A,
+            Gamepad::BUTTON_B,
+            -1,
+            Gamepad::BUTTON_X,
+            Gamepad::BUTTON_Y,
+            -1,
+            Gamepad::BUTTON_L1,
+            Gamepad::BUTTON_R1,
+            -1, -1, -1,
+            Gamepad::BUTTON_MENU2,
+            Gamepad::BUTTON_MENU1
+        };
+        
+        static const int GametelMapping103[12] = {
+            Gamepad::BUTTON_B,
+            Gamepad::BUTTON_X,
+            Gamepad::BUTTON_Y,
+            Gamepad::BUTTON_A,
+            Gamepad::BUTTON_L1,
+            Gamepad::BUTTON_R1,
+            Gamepad::BUTTON_MENU1,
+            Gamepad::BUTTON_MENU2,
+            Gamepad::BUTTON_RIGHT,
+            Gamepad::BUTTON_LEFT,
+            Gamepad::BUTTON_DOWN,
+            Gamepad::BUTTON_UP
+        };
+        
+        const int* mapping = NULL;
+        float axisDeadZone = 0.0f;
+        if (gamepad->_vendorId == SONY_USB_VENDOR_ID &&
+            gamepad->_productId == SONY_USB_PS3_PRODUCT_ID)
+        {
+            mapping = PS3Mapping;
+            axisDeadZone = 0.07f;
+        }
+        else if (gamepad->_vendorId == MICROSOFT_VENDOR_ID &&
+                 gamepad->_productId == MICROSOFT_XBOX360_PRODUCT_ID)
+        {
+            mapping = XBox360Mapping;
+            axisDeadZone = 0.2f;
+        }
+        else if (gamepad->_vendorId == STEELSERIES_VENDOR_ID &&
+                 gamepad->_productId == STEELSERIES_FREE_PRODUCT_ID)
+        {
+            mapping = SteelSeriesFreeMapping;
+            axisDeadZone = 0.005f;
+        }
+        else if (gamepad->_vendorId == FRUCTEL_VENDOR_ID &&
+                 gamepad->_productId == FRUCTEL_GAMETEL_PRODUCT_ID)
+        {
+            int ver = [gp versionNumber];
+            int major = ver >> 8;
+            int minor = ver & 0x00ff;
+            if (major >= 1 && minor > 1)
+            {
+                mapping = GametelMapping103;
+            }
+        }
+        
+        gamepad->_buttons = 0;
+        for (int i = 0; i < [gp numberOfButtons]; ++i)
+        {
+            HIDGamepadButton* b = [gp buttonAtIndex: i];
+            if ([b state])
+            {
+                // This button is down.
+                if (mapping)
+                {
+                    if (mapping[i] >= 0)
+                        gamepad->_buttons |= (1 << mapping[i]);
+                }
+                else
+                {
+                    gamepad->_buttons |= (1 << i);
+                }
+            }
+        }
+        
+        HIDGamepadAxis* hatSwitch = [gp getHatSwitch];
+        if (hatSwitch != NULL)
+        {
+            CFIndex v = [hatSwitch value];
+            switch (v)
+            {
+                case -1:
+                    break;
+                case 0:
+                    gamepad->_buttons |= (1 << Gamepad::BUTTON_UP);
+                    break;
+                case 1:
+                    gamepad->_buttons |= (1 << Gamepad::BUTTON_UP) | (1 << Gamepad::BUTTON_RIGHT);
+                    break;
+                case 2:
+                    gamepad->_buttons |= (1 << Gamepad::BUTTON_RIGHT);
+                    break;
+                case 3:
+                    gamepad->_buttons |= (1 << Gamepad::BUTTON_RIGHT) | (1 << Gamepad::BUTTON_DOWN);
+                    break;
+                case 4:
+                    gamepad->_buttons |= (1 << Gamepad::BUTTON_DOWN);
+                    break;
+                case 5:
+                    gamepad->_buttons |= (1 << Gamepad::BUTTON_DOWN) | (1 << Gamepad::BUTTON_LEFT);
+                    break;
+                case 6:
+                    gamepad->_buttons |= (1 << Gamepad::BUTTON_LEFT);
+                    break;
+                case 7:
+                    gamepad->_buttons |= (1 << Gamepad::BUTTON_LEFT) | (1 << Gamepad::BUTTON_UP);
+                    break;
+            }
+        }
+        
+        for (unsigned int i = 0; i < [gp numberOfSticks]; ++i)
+        {
+            float rawX = [[gp axisAtIndex: i*2] calibratedValue];
+            float rawY = -[[gp axisAtIndex: i*2 + 1] calibratedValue];
+            if (std::fabs(rawX) <= axisDeadZone)
+                rawX = 0;
+            if (std::fabs(rawY) <= axisDeadZone)
+                rawY = 0;
+            
+            gamepad->_joysticks[i].x = rawX;
+            gamepad->_joysticks[i].y = rawY;
+        }
+        
+        for (unsigned int i = 0; i < [gp numberOfTriggerButtons]; ++i)
+        {
+            gamepad->_triggers[i] = [[gp triggerButtonAtIndex: i] calibratedStateValue];
+        }
+    }
+}
+
+}
+
+HIDGamepad* gamepadForLocationID(NSNumber* locationID)
+{
+    HIDGamepad* fgamepad = NULL;
+    for(HIDGamepad* gamepad in __gamepads)
+    {
+        if([[gamepad locationID] isEqual:locationID])
+        {
+            fgamepad = gamepad;
+            break;
+        }
+    }
+    return fgamepad;
+}
+
+HIDGamepad* gamepadForLocationIDValue(unsigned int locationIDValue)
+{
+    return gamepadForLocationID([NSNumber numberWithUnsignedInt:locationIDValue]);
+}
+
+HIDGamepad* gamepadForGameHandle(int gameHandle)
+{
+    HIDGamepad* gamepad = NULL;
+    for(NSNumber* locationID in __activeGamepads)
+    {
+        NSNumber* handleID = [__activeGamepads objectForKey:locationID];
+        if([handleID integerValue] == gameHandle)
+        {
+            gamepad = gamepadForLocationID(locationID);
+            break;
+        }
+    }
+    return gamepad;
+}
+
+CFMutableDictionaryRef IOHIDCreateDeviceMatchingDictionary(UInt32 inUsagePage, UInt32 inUsage) 
+{
+    // create a dictionary to add usage page/usages to
+    CFMutableDictionaryRef result = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+    if (result) {
+        if (inUsagePage) 
+        {
+            // Add key for device type to refine the matching dictionary.
+            CFNumberRef pageCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsagePage);
+            if (pageCFNumberRef) 
+            {
+                CFDictionarySetValue(result, CFSTR( kIOHIDDeviceUsagePageKey ), pageCFNumberRef);
+                CFRelease(pageCFNumberRef);
+                
+                // note: the usage is only valid if the usage page is also defined
+                if (inUsage) 
+                {
+                    CFNumberRef usageCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsage);
+                    if (usageCFNumberRef) 
+                    {
+                        CFDictionarySetValue(result, CFSTR(kIOHIDDeviceUsageKey), usageCFNumberRef);
+                        CFRelease(usageCFNumberRef);
+                    } 
+                    else 
+                    {
+                        fprintf(stderr, "%s: CFNumberCreate( usage ) failed.", __PRETTY_FUNCTION__);
+                    }
+                }
+            } 
+            else 
+            {
+                fprintf( stderr, "%s: CFNumberCreate( usage page ) failed.", __PRETTY_FUNCTION__);
+            }
+        }
+    } 
+    else 
+    {
+        fprintf( stderr, "%s: CFDictionaryCreateMutable failed.", __PRETTY_FUNCTION__);
+    }
+    return result;
+}
+
+CFStringRef IOHIDDeviceGetStringProperty(IOHIDDeviceRef deviceRef, CFStringRef key) 
+{
+    CFTypeRef typeRef = IOHIDDeviceGetProperty(deviceRef, key);
+    if (typeRef == NULL || CFGetTypeID(typeRef) != CFNumberGetTypeID()) 
+    {
+        return NULL;
+    }
+    return (CFStringRef)typeRef;
+}
+
+int IOHIDDeviceGetIntProperty(IOHIDDeviceRef deviceRef, CFStringRef key) 
+{
+    CFTypeRef typeRef = IOHIDDeviceGetProperty(deviceRef, key);
+    if (typeRef == NULL || CFGetTypeID(typeRef) != CFNumberGetTypeID()) 
+    {
+        return 0;
+    }
+    
+    int value;
+    CFNumberGetValue((CFNumberRef) typeRef, kCFNumberSInt32Type, &value);
+    return value;
+}
+
+static void hidDeviceDiscoveredCallback(void* inContext, IOReturn inResult, void* inSender, IOHIDDeviceRef device)
+{
+    CFNumberRef locID = (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDLocationIDKey));
+    if(locID)
+    {
+        HIDGamepad* gamepad = [[HIDGamepad alloc] initWithDevice: device];
+        [__gamepads addObject:gamepad];
+    }
+}
+
+static void hidDeviceRemovalCallback(void* inContext, IOReturn inResult, void* inSender, IOHIDDeviceRef device)
+{
+    int removeIndex = -1;
+    NSNumber *locID = (NSNumber*)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDLocationIDKey));
+    if(locID)
+    {
+        for(int i = 0; i < [__gamepads count]; i++)
+        {
+            HIDGamepad* gamepad = [__gamepads objectAtIndex:i];
+            if([[gamepad locationID] isEqual:locID])
+            {
+                removeIndex = i;
+                break;
+            }
+        }
+    }
+    if(removeIndex >= 0)
+    {
+        [__gamepads removeObjectAtIndex:removeIndex];
+    }
+}
+
+static void hidDeviceValueAvailableCallback(void* inContext, IOReturn inResult,  void* inSender)
+{
+    HIDGamepad* d = (HIDGamepad*)inContext;
+    do
+    {
+        IOHIDValueRef valueRef = IOHIDQueueCopyNextValueWithTimeout( ( IOHIDQueueRef ) inSender, 0. );
+        if (!valueRef) break;
+        [d hidValueAvailable:valueRef];
+        CFRelease(valueRef); // Don't forget to release our HID value reference
+    } while (1);
+}
+
+bool Platform::launchURL(const char *url)
+{
+    if (url == NULL || *url == '\0')
+        return false;
+
+    CFURLRef urlRef = CFURLCreateWithBytes(
+        NULL,
+        (UInt8*)url,
+        strlen(url),
+        kCFStringEncodingASCII,
+        NULL
+    );
+    const OSStatus err = LSOpenCFURLRef(urlRef, 0);
+    CFRelease(urlRef);
+    return (err == noErr);
+}
+
+#endif

+ 8 - 0
gameplay/src/PlatformWindows.cpp

@@ -1136,6 +1136,14 @@ void Platform::getAccelerometerValues(float* pitch, float* roll)
     *roll = __roll;
 }
 
+void Platform::getArguments(int* argc, char*** argv)
+{
+    if (argc)
+        *argc = __argc;
+    if (argv)
+        *argv = __argv;
+}
+
 bool Platform::hasMouse()
 {
     return true;

+ 11 - 0
gameplay/src/PlatformiOS.mm

@@ -32,6 +32,9 @@ extern const int WINDOW_WIDTH  = [[UIScreen mainScreen] bounds].size.height * [[
 extern const int WINDOW_HEIGHT = [[UIScreen mainScreen] bounds].size.width * [[UIScreen mainScreen] scale];
 extern const int WINDOW_SCALE = [[UIScreen mainScreen] scale];
 
+int __argc = 0;
+char** __argv = 0;
+
 @class AppDelegate;
 @class View;
 
@@ -1381,6 +1384,14 @@ void Platform::getAccelerometerValues(float* pitch, float* roll)
 {
     [__appDelegate getAccelerometerPitch:pitch roll:roll];
 }
+    
+void Platform::getArguments(int* argc, char*** argv)
+{
+    if (argc)
+        *argc = __argc;
+    if (argv)
+        *argv = __argv;
+}
 
 bool Platform::hasMouse()
 {

+ 5 - 0
gameplay/src/gameplay-main-blackberry.cpp

@@ -4,11 +4,16 @@
 
 using namespace gameplay;
 
+extern int __argc;
+extern char** __argv;
+
 /**
  * Main entry point.
  */
 int main(int argc, char** argv)
 {
+    __argc = argc;
+    __argv = argv;
     Game* game = Game::getInstance();
     Platform* platform = Platform::create(game);
     GP_ASSERT(platform);

+ 6 - 1
gameplay/src/gameplay-main-ios.mm

@@ -5,11 +5,16 @@
 
 using namespace gameplay;
 
+extern int __argc;
+extern char** __argv;
+
 /**
  * Main entry point.
  */
 int main(int argc, char** argv)
-{   
+{
+    __argc = argc;
+    __argv = argv;
     NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init];
     Game* game = Game::getInstance();
     Platform* platform = Platform::create(game);

+ 5 - 0
gameplay/src/gameplay-main-linux.cpp

@@ -4,11 +4,16 @@
 
 using namespace gameplay;
 
+extern int __argc;
+extern char** __argv;
+
 /**
  * Main entry point.
  */
 int main(int argc, char** argv)
 {
+    __argc = argc;
+    __argv = argv;
     Game* game = Game::getInstance();
     Platform* platform = Platform::create(game);
     GP_ASSERT(platform);

+ 5 - 0
gameplay/src/gameplay-main-macosx.mm

@@ -5,11 +5,16 @@
 
 using namespace gameplay;
 
+extern int __argc;
+extern char** __argv;
+
 /**
  * Main entry point.
  */
 int main(int argc, char** argv)
 {
+    __argc = argc;
+    __argv = argv;
     NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init];
     Game* game = Game::getInstance();
     Platform* platform = Platform::create(game);