src/core/android/SDL_android.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 02 Nov 2018 17:18:03 -0700
changeset 12387 2f02c753a5dc
parent 12381 dc9108cd4340
child 12464 db15b3c90139
permissions -rw-r--r--
Fixed bug 4315 - little Warning in Android_JNI_CaptureAudioBuffer

Sylvain

SDL_android.c
src/core/android/SDL_android.c:1302:5: warning: variable 'br' is used uninitialized whenever switch default is taken [-Wsometimes-uninitialized]
default:
^~~~~~~
src/core/android/SDL_android.c:1306:12: note: uninitialized use occurs here
return br;
^~
src/core/android/SDL_android.c:1270:12: note: initialize the variable 'br' to silence this warning
jint br;
^


Maybe we could add some basics warning flags, not to see all warnings, but so that new warnings are caught sooner.

I would go for -Wall -Wextra, and some -Wno-warning for the allowed warnings.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../../SDL_internal.h"
    22 #include "SDL_stdinc.h"
    23 #include "SDL_assert.h"
    24 #include "SDL_hints.h"
    25 #include "SDL_log.h"
    26 #include "SDL_main.h"
    27 
    28 #ifdef __ANDROID__
    29 
    30 #include "SDL_system.h"
    31 #include "SDL_android.h"
    32 
    33 #include "keyinfotable.h"
    34 
    35 #include "../../events/SDL_events_c.h"
    36 #include "../../video/android/SDL_androidkeyboard.h"
    37 #include "../../video/android/SDL_androidmouse.h"
    38 #include "../../video/android/SDL_androidtouch.h"
    39 #include "../../video/android/SDL_androidvideo.h"
    40 #include "../../video/android/SDL_androidwindow.h"
    41 #include "../../joystick/android/SDL_sysjoystick_c.h"
    42 #include "../../haptic/android/SDL_syshaptic_c.h"
    43 
    44 #include <android/log.h>
    45 #include <pthread.h>
    46 #include <sys/types.h>
    47 #include <unistd.h>
    48 #include <dlfcn.h>
    49 /* #define LOG_TAG "SDL_android" */
    50 /* #define LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) */
    51 /* #define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) */
    52 #define LOGI(...) do {} while (0)
    53 #define LOGE(...) do {} while (0)
    54 
    55 
    56 #define SDL_JAVA_PREFIX                                 org_libsdl_app
    57 #define CONCAT1(prefix, class, function)                CONCAT2(prefix, class, function)
    58 #define CONCAT2(prefix, class, function)                Java_ ## prefix ## _ ## class ## _ ## function
    59 #define SDL_JAVA_INTERFACE(function)                    CONCAT1(SDL_JAVA_PREFIX, SDLActivity, function)
    60 #define SDL_JAVA_AUDIO_INTERFACE(function)              CONCAT1(SDL_JAVA_PREFIX, SDLAudioManager, function)
    61 #define SDL_JAVA_CONTROLLER_INTERFACE(function)         CONCAT1(SDL_JAVA_PREFIX, SDLControllerManager, function)
    62 #define SDL_JAVA_INTERFACE_INPUT_CONNECTION(function)   CONCAT1(SDL_JAVA_PREFIX, SDLInputConnection, function)
    63 
    64 /* Audio encoding definitions */
    65 #define ENCODING_PCM_8BIT   3
    66 #define ENCODING_PCM_16BIT  2
    67 #define ENCODING_PCM_FLOAT  4
    68 
    69 /* Java class SDLActivity */
    70 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(
    71         JNIEnv* mEnv, jclass cls);
    72 
    73 JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeRunMain)(
    74         JNIEnv* env, jclass cls,
    75         jstring library, jstring function, jobject array);
    76 
    77 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeDropFile)(
    78         JNIEnv* env, jclass jcls,
    79         jstring filename);
    80 
    81 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeResize)(
    82         JNIEnv* env, jclass jcls,
    83         jint surfaceWidth, jint surfaceHeight,
    84         jint deviceWidth, jint deviceHeight, jint format, jfloat rate);
    85 
    86 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(
    87         JNIEnv* env, jclass jcls);
    88 
    89 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)(
    90         JNIEnv* env, jclass jcls);
    91 
    92 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyDown)(
    93         JNIEnv* env, jclass jcls,
    94         jint keycode);
    95 
    96 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyUp)(
    97         JNIEnv* env, jclass jcls,
    98         jint keycode);
    99 
   100 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyboardFocusLost)(
   101         JNIEnv* env, jclass jcls);
   102 
   103 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeTouch)(
   104         JNIEnv* env, jclass jcls,
   105         jint touch_device_id_in, jint pointer_finger_id_in,
   106         jint action, jfloat x, jfloat y, jfloat p);
   107 
   108 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeMouse)(
   109         JNIEnv* env, jclass jcls,
   110         jint button, jint action, jfloat x, jfloat y, jboolean relative);
   111 
   112 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeAccel)(
   113         JNIEnv* env, jclass jcls,
   114         jfloat x, jfloat y, jfloat z);
   115 
   116 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeClipboardChanged)(
   117         JNIEnv* env, jclass jcls);
   118 
   119 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeLowMemory)(
   120         JNIEnv* env, jclass cls);
   121 
   122 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeQuit)(
   123         JNIEnv* env, jclass cls);
   124 
   125 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePause)(
   126         JNIEnv* env, jclass cls);
   127 
   128 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeResume)(
   129         JNIEnv* env, jclass cls);
   130 
   131 JNIEXPORT jstring JNICALL SDL_JAVA_INTERFACE(nativeGetHint)(
   132         JNIEnv* env, jclass cls,
   133         jstring name);
   134 
   135 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetenv)(
   136         JNIEnv* env, jclass cls,
   137         jstring name, jstring value);
   138 
   139 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeEnvironmentVariablesSet)(
   140         JNIEnv* env, jclass cls);
   141 
   142 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeOrientationChanged)(
   143         JNIEnv* env, jclass cls,
   144         jint orientation);
   145 
   146 /* Java class SDLInputConnection */
   147 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeCommitText)(
   148         JNIEnv* env, jclass cls,
   149         jstring text, jint newCursorPosition);
   150 
   151 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeGenerateScancodeForUnichar)(
   152         JNIEnv* env, jclass cls,
   153         jchar chUnicode);
   154 
   155 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeSetComposingText)(
   156         JNIEnv* env, jclass cls,
   157         jstring text, jint newCursorPosition);
   158 
   159 /* Java class SDLAudioManager */
   160 JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(
   161         JNIEnv *env, jclass jcls);
   162 
   163 /* Java class SDLControllerManager */
   164 JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(
   165         JNIEnv *env, jclass jcls);
   166 
   167 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)(
   168         JNIEnv* env, jclass jcls,
   169         jint device_id, jint keycode);
   170 
   171 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)(
   172         JNIEnv* env, jclass jcls,
   173         jint device_id, jint keycode);
   174 
   175 JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)(
   176         JNIEnv* env, jclass jcls,
   177         jint device_id, jint axis, jfloat value);
   178 
   179 JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
   180         JNIEnv* env, jclass jcls,
   181         jint device_id, jint hat_id, jint x, jint y);
   182 
   183 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
   184         JNIEnv* env, jclass jcls,
   185         jint device_id, jstring device_name, jstring device_desc, jint vendor_id, jint product_id,
   186         jboolean is_accelerometer, jint button_mask, jint naxes, jint nhats, jint nballs);
   187 
   188 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)(
   189         JNIEnv* env, jclass jcls,
   190         jint device_id);
   191 
   192 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic)(
   193         JNIEnv* env, jclass jcls,
   194         jint device_id, jstring device_name);
   195 
   196 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)(
   197         JNIEnv* env, jclass jcls,
   198         jint device_id);
   199 
   200 
   201 
   202 /* Uncomment this to log messages entering and exiting methods in this file */
   203 /* #define DEBUG_JNI */
   204 
   205 static void Android_JNI_ThreadDestroyed(void*);
   206 static void checkJNIReady(void);
   207 
   208 /*******************************************************************************
   209  This file links the Java side of Android with libsdl
   210 *******************************************************************************/
   211 #include <jni.h>
   212 
   213 
   214 /*******************************************************************************
   215                                Globals
   216 *******************************************************************************/
   217 static pthread_key_t mThreadKey;
   218 static JavaVM* mJavaVM;
   219 
   220 /* Main activity */
   221 static jclass mActivityClass;
   222 
   223 /* method signatures */
   224 static jmethodID midGetNativeSurface;
   225 static jmethodID midSetActivityTitle;
   226 static jmethodID midSetWindowStyle;
   227 static jmethodID midSetOrientation;
   228 static jmethodID midGetContext;
   229 static jmethodID midIsTablet;
   230 static jmethodID midIsAndroidTV;
   231 static jmethodID midIsChromebook;
   232 static jmethodID midIsDeXMode;
   233 static jmethodID midManualBackButton;
   234 static jmethodID midInputGetInputDeviceIds;
   235 static jmethodID midSendMessage;
   236 static jmethodID midShowTextInput;
   237 static jmethodID midIsScreenKeyboardShown;
   238 static jmethodID midClipboardSetText;
   239 static jmethodID midClipboardGetText;
   240 static jmethodID midClipboardHasText;
   241 static jmethodID midOpenAPKExpansionInputStream;
   242 static jmethodID midGetManifestEnvironmentVariables;
   243 static jmethodID midGetDisplayDPI;
   244 static jmethodID midCreateCustomCursor;
   245 static jmethodID midSetCustomCursor;
   246 static jmethodID midSetSystemCursor;
   247 static jmethodID midSupportsRelativeMouse;
   248 static jmethodID midSetRelativeMouseEnabled;
   249 
   250 /* audio manager */
   251 static jclass mAudioManagerClass;
   252 
   253 /* method signatures */
   254 static jmethodID midAudioOpen;
   255 static jmethodID midAudioWriteByteBuffer;
   256 static jmethodID midAudioWriteShortBuffer;
   257 static jmethodID midAudioWriteFloatBuffer;
   258 static jmethodID midAudioClose;
   259 static jmethodID midCaptureOpen;
   260 static jmethodID midCaptureReadByteBuffer;
   261 static jmethodID midCaptureReadShortBuffer;
   262 static jmethodID midCaptureReadFloatBuffer;
   263 static jmethodID midCaptureClose;
   264 
   265 /* controller manager */
   266 static jclass mControllerManagerClass;
   267 
   268 /* method signatures */
   269 static jmethodID midPollInputDevices;
   270 static jmethodID midPollHapticDevices;
   271 static jmethodID midHapticRun;
   272 static jmethodID midHapticStop;
   273 
   274 /* static fields */
   275 static jfieldID fidSeparateMouseAndTouch;
   276 
   277 /* Accelerometer data storage */
   278 static float fLastAccelerometer[3];
   279 static SDL_bool bHasNewData;
   280 
   281 static SDL_bool bHasEnvironmentVariables = SDL_FALSE;
   282 
   283 /*******************************************************************************
   284                  Functions called by JNI
   285 *******************************************************************************/
   286 
   287 /* Library init */
   288 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
   289 {
   290     JNIEnv *env;
   291     mJavaVM = vm;
   292     LOGI("JNI_OnLoad called");
   293     if ((*mJavaVM)->GetEnv(mJavaVM, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
   294         LOGE("Failed to get the environment using GetEnv()");
   295         return -1;
   296     }
   297     /*
   298      * Create mThreadKey so we can keep track of the JNIEnv assigned to each thread
   299      * Refer to http://developer.android.com/guide/practices/design/jni.html for the rationale behind this
   300      */
   301     if (pthread_key_create(&mThreadKey, Android_JNI_ThreadDestroyed) != 0) {
   302         __android_log_print(ANDROID_LOG_ERROR, "SDL", "Error initializing pthread key");
   303     }
   304     Android_JNI_SetupThread();
   305 
   306     return JNI_VERSION_1_4;
   307 }
   308 
   309 void checkJNIReady()
   310 {
   311     if (!mActivityClass || !mAudioManagerClass || !mControllerManagerClass) {
   312         // We aren't fully initialized, let's just return.
   313         return;
   314     }
   315 
   316     SDL_SetMainReady();    
   317 }
   318 
   319 /* Activity initialization -- called before SDL_main() to initialize JNI bindings */
   320 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv* mEnv, jclass cls)
   321 {
   322     __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeSetupJNI()");
   323 
   324     Android_JNI_SetupThread();
   325 
   326     mActivityClass = (jclass)((*mEnv)->NewGlobalRef(mEnv, cls));
   327 
   328     midGetNativeSurface = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   329                                 "getNativeSurface","()Landroid/view/Surface;");
   330     midSetActivityTitle = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   331                                 "setActivityTitle","(Ljava/lang/String;)Z");
   332     midSetWindowStyle = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   333                                 "setWindowStyle","(Z)V");
   334     midSetOrientation = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   335                                 "setOrientation","(IIZLjava/lang/String;)V");
   336     midGetContext = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   337                                 "getContext","()Landroid/content/Context;");
   338     midIsTablet = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   339                                 "isTablet", "()Z");
   340     midIsAndroidTV = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   341                                 "isAndroidTV","()Z");
   342     midIsChromebook = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   343                                 "isChromebook", "()Z");
   344     midIsDeXMode = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   345                                 "isDeXMode", "()Z");
   346     midManualBackButton = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   347                                 "manualBackButton", "()V");
   348     midInputGetInputDeviceIds = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   349                                 "inputGetInputDeviceIds", "(I)[I");
   350     midSendMessage = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   351                                 "sendMessage", "(II)Z");
   352     midShowTextInput =  (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   353                                 "showTextInput", "(IIII)Z");
   354     midIsScreenKeyboardShown = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   355                                 "isScreenKeyboardShown","()Z");
   356     midClipboardSetText = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   357                                 "clipboardSetText", "(Ljava/lang/String;)V");
   358     midClipboardGetText = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   359                                 "clipboardGetText", "()Ljava/lang/String;");
   360     midClipboardHasText = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   361                                 "clipboardHasText", "()Z");
   362     midOpenAPKExpansionInputStream = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   363                                 "openAPKExpansionInputStream", "(Ljava/lang/String;)Ljava/io/InputStream;");
   364 
   365     midGetManifestEnvironmentVariables = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
   366                                 "getManifestEnvironmentVariables", "()Z");
   367 
   368     midGetDisplayDPI = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "getDisplayDPI", "()Landroid/util/DisplayMetrics;");
   369     midCreateCustomCursor = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "createCustomCursor", "([IIIII)I");
   370     midSetCustomCursor = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "setCustomCursor", "(I)Z");
   371     midSetSystemCursor = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "setSystemCursor", "(I)Z");
   372 
   373     midSupportsRelativeMouse = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "supportsRelativeMouse", "()Z");
   374     midSetRelativeMouseEnabled = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "setRelativeMouseEnabled", "(Z)Z");
   375 
   376 
   377     if (!midGetNativeSurface ||
   378        !midSetActivityTitle || !midSetWindowStyle || !midSetOrientation || !midGetContext || !midIsTablet || !midIsAndroidTV || !midInputGetInputDeviceIds ||
   379        !midSendMessage || !midShowTextInput || !midIsScreenKeyboardShown ||
   380        !midClipboardSetText || !midClipboardGetText || !midClipboardHasText ||
   381        !midOpenAPKExpansionInputStream || !midGetManifestEnvironmentVariables || !midGetDisplayDPI ||
   382        !midCreateCustomCursor || !midSetCustomCursor || !midSetSystemCursor || !midSupportsRelativeMouse || !midSetRelativeMouseEnabled ||
   383        !midIsChromebook || !midIsDeXMode || !midManualBackButton) {
   384         __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLActivity.java?");
   385     }
   386 
   387     fidSeparateMouseAndTouch = (*mEnv)->GetStaticFieldID(mEnv, mActivityClass, "mSeparateMouseAndTouch", "Z");
   388 
   389     if (!fidSeparateMouseAndTouch) {
   390         __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java static fields, do you have the latest version of SDLActivity.java?");
   391     }
   392 
   393     checkJNIReady();
   394 }
   395 
   396 /* Audio initialization -- called before SDL_main() to initialize JNI bindings */
   397 JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv* mEnv, jclass cls)
   398 {
   399     __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "AUDIO nativeSetupJNI()");
   400 
   401     Android_JNI_SetupThread();
   402 
   403     mAudioManagerClass = (jclass)((*mEnv)->NewGlobalRef(mEnv, cls));
   404 
   405     midAudioOpen = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
   406                                 "audioOpen", "(IIII)[I");
   407     midAudioWriteByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
   408                                 "audioWriteByteBuffer", "([B)V");
   409     midAudioWriteShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
   410                                 "audioWriteShortBuffer", "([S)V");
   411     midAudioWriteFloatBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
   412                                 "audioWriteFloatBuffer", "([F)V");
   413     midAudioClose = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
   414                                 "audioClose", "()V");
   415     midCaptureOpen = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
   416                                 "captureOpen", "(IIII)[I");
   417     midCaptureReadByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
   418                                 "captureReadByteBuffer", "([BZ)I");
   419     midCaptureReadShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
   420                                 "captureReadShortBuffer", "([SZ)I");
   421     midCaptureReadFloatBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
   422                                 "captureReadFloatBuffer", "([FZ)I");
   423     midCaptureClose = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
   424                                 "captureClose", "()V");
   425 
   426     if (!midAudioOpen || !midAudioWriteByteBuffer || !midAudioWriteShortBuffer || !midAudioWriteFloatBuffer || !midAudioClose ||
   427        !midCaptureOpen || !midCaptureReadByteBuffer || !midCaptureReadShortBuffer || !midCaptureReadFloatBuffer || !midCaptureClose) {
   428         __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLAudioManager.java?");
   429     }
   430 
   431     checkJNIReady();
   432 }
   433 
   434 /* Controller initialization -- called before SDL_main() to initialize JNI bindings */
   435 JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(JNIEnv* mEnv, jclass cls)
   436 {
   437     __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "CONTROLLER nativeSetupJNI()");
   438 
   439     Android_JNI_SetupThread();
   440 
   441     mControllerManagerClass = (jclass)((*mEnv)->NewGlobalRef(mEnv, cls));
   442 
   443     midPollInputDevices = (*mEnv)->GetStaticMethodID(mEnv, mControllerManagerClass,
   444                                 "pollInputDevices", "()V");
   445     midPollHapticDevices = (*mEnv)->GetStaticMethodID(mEnv, mControllerManagerClass,
   446                                 "pollHapticDevices", "()V");
   447     midHapticRun = (*mEnv)->GetStaticMethodID(mEnv, mControllerManagerClass,
   448                                 "hapticRun", "(IFI)V");
   449     midHapticStop = (*mEnv)->GetStaticMethodID(mEnv, mControllerManagerClass,
   450                                 "hapticStop", "(I)V");
   451 
   452     if (!midPollInputDevices || !midPollHapticDevices || !midHapticRun || !midHapticStop) {
   453         __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLControllerManager.java?");
   454     }
   455 
   456     checkJNIReady();
   457 }
   458 
   459 /* SDL main function prototype */
   460 typedef int (*SDL_main_func)(int argc, char *argv[]);
   461 
   462 /* Start up the SDL app */
   463 JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeRunMain)(JNIEnv* env, jclass cls, jstring library, jstring function, jobject array)
   464 {
   465     int status = -1;
   466     const char *library_file;
   467     void *library_handle;
   468 
   469     __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeRunMain()");
   470 
   471     library_file = (*env)->GetStringUTFChars(env, library, NULL);
   472     library_handle = dlopen(library_file, RTLD_GLOBAL);
   473     if (library_handle) {
   474         const char *function_name;
   475         SDL_main_func SDL_main;
   476 
   477         function_name = (*env)->GetStringUTFChars(env, function, NULL);
   478         SDL_main = (SDL_main_func)dlsym(library_handle, function_name);
   479         if (SDL_main) {
   480             int i;
   481             int argc;
   482             int len;
   483             char **argv;
   484             SDL_bool isstack;
   485 
   486             /* Prepare the arguments. */
   487             len = (*env)->GetArrayLength(env, array);
   488             argv = SDL_small_alloc(char*, 1 + len + 1, &isstack);  /* !!! FIXME: check for NULL */
   489             argc = 0;
   490             /* Use the name "app_process" so PHYSFS_platformCalcBaseDir() works.
   491                https://bitbucket.org/MartinFelis/love-android-sdl2/issue/23/release-build-crash-on-start
   492              */
   493             argv[argc++] = SDL_strdup("app_process");
   494             for (i = 0; i < len; ++i) {
   495                 const char* utf;
   496                 char* arg = NULL;
   497                 jstring string = (*env)->GetObjectArrayElement(env, array, i);
   498                 if (string) {
   499                     utf = (*env)->GetStringUTFChars(env, string, 0);
   500                     if (utf) {
   501                         arg = SDL_strdup(utf);
   502                         (*env)->ReleaseStringUTFChars(env, string, utf);
   503                     }
   504                     (*env)->DeleteLocalRef(env, string);
   505                 }
   506                 if (!arg) {
   507                     arg = SDL_strdup("");
   508                 }
   509                 argv[argc++] = arg;
   510             }
   511             argv[argc] = NULL;
   512 
   513 
   514             /* Run the application. */
   515             status = SDL_main(argc, argv);
   516 
   517             /* Release the arguments. */
   518             for (i = 0; i < argc; ++i) {
   519                 SDL_free(argv[i]);
   520             }
   521             SDL_small_free(argv, isstack);
   522 
   523         } else {
   524             __android_log_print(ANDROID_LOG_ERROR, "SDL", "nativeRunMain(): Couldn't find function %s in library %s", function_name, library_file);
   525         }
   526         (*env)->ReleaseStringUTFChars(env, function, function_name);
   527 
   528         dlclose(library_handle);
   529 
   530     } else {
   531         __android_log_print(ANDROID_LOG_ERROR, "SDL", "nativeRunMain(): Couldn't load library %s", library_file);
   532     }
   533     (*env)->ReleaseStringUTFChars(env, library, library_file);
   534 
   535     /* Do not issue an exit or the whole application will terminate instead of just the SDL thread */
   536     /* exit(status); */
   537 
   538     return status;
   539 }
   540 
   541 /* Drop file */
   542 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeDropFile)(
   543                                     JNIEnv* env, jclass jcls,
   544                                     jstring filename)
   545 {
   546     const char *path = (*env)->GetStringUTFChars(env, filename, NULL);
   547     SDL_SendDropFile(NULL, path);
   548     (*env)->ReleaseStringUTFChars(env, filename, path);
   549     SDL_SendDropComplete(NULL);
   550 }
   551 
   552 /* Resize */
   553 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeResize)(
   554                                     JNIEnv* env, jclass jcls,
   555                                     jint surfaceWidth, jint surfaceHeight,
   556                                     jint deviceWidth, jint deviceHeight, jint format, jfloat rate)
   557 {
   558     Android_SetScreenResolution(surfaceWidth, surfaceHeight, deviceWidth, deviceHeight, format, rate);
   559 }
   560 
   561 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeOrientationChanged)(
   562                                     JNIEnv *env, jclass jcls,
   563                                     jint orientation)
   564 {
   565     SDL_VideoDisplay *display = SDL_GetDisplay(0);
   566     SDL_SendDisplayEvent(display, SDL_DISPLAYEVENT_ORIENTATION, orientation);
   567 }
   568 
   569 /* Paddown */
   570 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)(
   571                                     JNIEnv* env, jclass jcls,
   572                                     jint device_id, jint keycode)
   573 {
   574     return Android_OnPadDown(device_id, keycode);
   575 }
   576 
   577 /* Padup */
   578 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)(
   579                                     JNIEnv* env, jclass jcls,
   580                                     jint device_id, jint keycode)
   581 {
   582     return Android_OnPadUp(device_id, keycode);
   583 }
   584 
   585 /* Joy */
   586 JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)(
   587                                     JNIEnv* env, jclass jcls,
   588                                     jint device_id, jint axis, jfloat value)
   589 {
   590     Android_OnJoy(device_id, axis, value);
   591 }
   592 
   593 /* POV Hat */
   594 JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
   595                                     JNIEnv* env, jclass jcls,
   596                                     jint device_id, jint hat_id, jint x, jint y)
   597 {
   598     Android_OnHat(device_id, hat_id, x, y);
   599 }
   600 
   601 
   602 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
   603                                     JNIEnv* env, jclass jcls,
   604                                     jint device_id, jstring device_name, jstring device_desc,
   605                                     jint vendor_id, jint product_id, jboolean is_accelerometer,
   606                                     jint button_mask, jint naxes, jint nhats, jint nballs)
   607 {
   608     int retval;
   609     const char *name = (*env)->GetStringUTFChars(env, device_name, NULL);
   610     const char *desc = (*env)->GetStringUTFChars(env, device_desc, NULL);
   611 
   612     retval = Android_AddJoystick(device_id, name, desc, vendor_id, product_id, is_accelerometer ? SDL_TRUE : SDL_FALSE, button_mask, naxes, nhats, nballs);
   613 
   614     (*env)->ReleaseStringUTFChars(env, device_name, name);
   615     (*env)->ReleaseStringUTFChars(env, device_desc, desc);
   616 
   617     return retval;
   618 }
   619 
   620 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)(
   621                                     JNIEnv* env, jclass jcls,
   622                                     jint device_id)
   623 {
   624     return Android_RemoveJoystick(device_id);
   625 }
   626 
   627 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic)(
   628     JNIEnv* env, jclass jcls, jint device_id, jstring device_name)
   629 {
   630     int retval;
   631     const char *name = (*env)->GetStringUTFChars(env, device_name, NULL);
   632 
   633     retval = Android_AddHaptic(device_id, name);
   634 
   635     (*env)->ReleaseStringUTFChars(env, device_name, name);
   636 
   637     return retval;
   638 }
   639 
   640 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)(
   641     JNIEnv* env, jclass jcls, jint device_id)
   642 {
   643     return Android_RemoveHaptic(device_id);
   644 }
   645 
   646 
   647 /* Surface Created */
   648 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(JNIEnv* env, jclass jcls)
   649 {
   650     SDL_WindowData *data;
   651     SDL_VideoDevice *_this;
   652 
   653     if (Android_Window == NULL || Android_Window->driverdata == NULL ) {
   654         return;
   655     }
   656 
   657     _this =  SDL_GetVideoDevice();
   658     data =  (SDL_WindowData *) Android_Window->driverdata;
   659 
   660     /* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
   661     if (data->egl_surface == EGL_NO_SURFACE) {
   662         if(data->native_window) {
   663             ANativeWindow_release(data->native_window);
   664         }
   665         data->native_window = Android_JNI_GetNativeWindow();
   666         data->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType) data->native_window);
   667     }
   668 
   669     /* GL Context handling is done in the event loop because this function is run from the Java thread */
   670 
   671 }
   672 
   673 /* Surface Destroyed */
   674 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)(JNIEnv* env, jclass jcls)
   675 {
   676     /* We have to clear the current context and destroy the egl surface here
   677      * Otherwise there's BAD_NATIVE_WINDOW errors coming from eglCreateWindowSurface on resume
   678      * Ref: http://stackoverflow.com/questions/8762589/eglcreatewindowsurface-on-ics-and-switching-from-2d-to-3d
   679      */
   680     SDL_WindowData *data;
   681     SDL_VideoDevice *_this;
   682 
   683     if (Android_Window == NULL || Android_Window->driverdata == NULL ) {
   684         return;
   685     }
   686 
   687     _this =  SDL_GetVideoDevice();
   688     data = (SDL_WindowData *) Android_Window->driverdata;
   689 
   690     if (data->egl_surface != EGL_NO_SURFACE) {
   691         SDL_EGL_MakeCurrent(_this, NULL, NULL);
   692         SDL_EGL_DestroySurface(_this, data->egl_surface);
   693         data->egl_surface = EGL_NO_SURFACE;
   694     }
   695 
   696     /* GL Context handling is done in the event loop because this function is run from the Java thread */
   697 
   698 }
   699 
   700 /* Keydown */
   701 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyDown)(
   702                                     JNIEnv* env, jclass jcls,
   703                                     jint keycode)
   704 {
   705     Android_OnKeyDown(keycode);
   706 }
   707 
   708 /* Keyup */
   709 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyUp)(
   710                                     JNIEnv* env, jclass jcls,
   711                                     jint keycode)
   712 {
   713     Android_OnKeyUp(keycode);
   714 }
   715 
   716 /* Keyboard Focus Lost */
   717 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyboardFocusLost)(
   718                                     JNIEnv* env, jclass jcls)
   719 {
   720     /* Calling SDL_StopTextInput will take care of hiding the keyboard and cleaning up the DummyText widget */
   721     SDL_StopTextInput();
   722 }
   723 
   724 
   725 /* Touch */
   726 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeTouch)(
   727                                     JNIEnv* env, jclass jcls,
   728                                     jint touch_device_id_in, jint pointer_finger_id_in,
   729                                     jint action, jfloat x, jfloat y, jfloat p)
   730 {
   731     Android_OnTouch(touch_device_id_in, pointer_finger_id_in, action, x, y, p);
   732 }
   733 
   734 /* Mouse */
   735 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeMouse)(
   736                                     JNIEnv* env, jclass jcls,
   737                                     jint button, jint action, jfloat x, jfloat y, jboolean relative)
   738 {
   739     Android_OnMouse(button, action, x, y, relative);
   740 }
   741 
   742 /* Accelerometer */
   743 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeAccel)(
   744                                     JNIEnv* env, jclass jcls,
   745                                     jfloat x, jfloat y, jfloat z)
   746 {
   747     fLastAccelerometer[0] = x;
   748     fLastAccelerometer[1] = y;
   749     fLastAccelerometer[2] = z;
   750     bHasNewData = SDL_TRUE;
   751 }
   752 
   753 /* Clipboard */
   754 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeClipboardChanged)(
   755                                     JNIEnv* env, jclass jcls)
   756 {
   757     SDL_SendClipboardUpdate();
   758 }
   759 
   760 /* Low memory */
   761 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeLowMemory)(
   762                                     JNIEnv* env, jclass cls)
   763 {
   764     SDL_SendAppEvent(SDL_APP_LOWMEMORY);
   765 }
   766 
   767 /* Quit */
   768 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeQuit)(
   769                                     JNIEnv* env, jclass cls)
   770 {
   771     /* Discard previous events. The user should have handled state storage
   772      * in SDL_APP_WILLENTERBACKGROUND. After nativeQuit() is called, no
   773      * events other than SDL_QUIT and SDL_APP_TERMINATING should fire */
   774     SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT);
   775     /* Inject a SDL_QUIT event */
   776     SDL_SendQuit();
   777     SDL_SendAppEvent(SDL_APP_TERMINATING);
   778     /* Resume the event loop so that the app can catch SDL_QUIT which
   779      * should now be the top event in the event queue. */
   780     if (!SDL_SemValue(Android_ResumeSem)) SDL_SemPost(Android_ResumeSem);
   781 }
   782 
   783 /* Pause */
   784 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePause)(
   785                                     JNIEnv* env, jclass cls)
   786 {
   787     __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativePause()");
   788 
   789     if (Android_Window) {
   790         SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_FOCUS_LOST, 0, 0);
   791         SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
   792         SDL_SendAppEvent(SDL_APP_WILLENTERBACKGROUND);
   793         SDL_SendAppEvent(SDL_APP_DIDENTERBACKGROUND);
   794 
   795         /* *After* sending the relevant events, signal the pause semaphore
   796          * so the event loop knows to pause and (optionally) block itself */
   797         if (!SDL_SemValue(Android_PauseSem)) SDL_SemPost(Android_PauseSem);
   798     }
   799 }
   800 
   801 /* Resume */
   802 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeResume)(
   803                                     JNIEnv* env, jclass cls)
   804 {
   805     __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeResume()");
   806 
   807     if (Android_Window) {
   808         SDL_SendAppEvent(SDL_APP_WILLENTERFOREGROUND);
   809         SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND);
   810         SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_FOCUS_GAINED, 0, 0);
   811         SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_RESTORED, 0, 0);
   812         /* Signal the resume semaphore so the event loop knows to resume and restore the GL Context
   813          * We can't restore the GL Context here because it needs to be done on the SDL main thread
   814          * and this function will be called from the Java thread instead.
   815          */
   816         if (!SDL_SemValue(Android_ResumeSem)) SDL_SemPost(Android_ResumeSem);
   817     }
   818 }
   819 
   820 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeCommitText)(
   821                                     JNIEnv* env, jclass cls,
   822                                     jstring text, jint newCursorPosition)
   823 {
   824     const char *utftext = (*env)->GetStringUTFChars(env, text, NULL);
   825 
   826     SDL_SendKeyboardText(utftext);
   827 
   828     (*env)->ReleaseStringUTFChars(env, text, utftext);
   829 }
   830 
   831 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeGenerateScancodeForUnichar)(
   832                                     JNIEnv* env, jclass cls,
   833                                     jchar chUnicode)
   834 {
   835     SDL_Scancode code = SDL_SCANCODE_UNKNOWN;
   836     uint16_t mod = 0;
   837 
   838     // We do not care about bigger than 127.
   839     if (chUnicode < 127) {
   840         AndroidKeyInfo info = unicharToAndroidKeyInfoTable[chUnicode];
   841         code = info.code;
   842         mod = info.mod;
   843     }
   844 
   845     if (mod & KMOD_SHIFT) {
   846         /* If character uses shift, press shift down */
   847         SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_LSHIFT);
   848     }
   849 
   850     /* send a keydown and keyup even for the character */
   851     SDL_SendKeyboardKey(SDL_PRESSED, code);
   852     SDL_SendKeyboardKey(SDL_RELEASED, code);
   853 
   854     if (mod & KMOD_SHIFT) {
   855         /* If character uses shift, press shift back up */
   856         SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LSHIFT);
   857     }
   858 }
   859 
   860 
   861 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeSetComposingText)(
   862                                     JNIEnv* env, jclass cls,
   863                                     jstring text, jint newCursorPosition)
   864 {
   865     const char *utftext = (*env)->GetStringUTFChars(env, text, NULL);
   866 
   867     SDL_SendEditingText(utftext, 0, 0);
   868 
   869     (*env)->ReleaseStringUTFChars(env, text, utftext);
   870 }
   871 
   872 JNIEXPORT jstring JNICALL SDL_JAVA_INTERFACE(nativeGetHint)(
   873                                     JNIEnv* env, jclass cls,
   874                                     jstring name)
   875 {
   876     const char *utfname = (*env)->GetStringUTFChars(env, name, NULL);
   877     const char *hint = SDL_GetHint(utfname);
   878 
   879     jstring result = (*env)->NewStringUTF(env, hint);
   880     (*env)->ReleaseStringUTFChars(env, name, utfname);
   881 
   882     return result;
   883 }
   884 
   885 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetenv)(
   886                                     JNIEnv* env, jclass cls,
   887                                     jstring name, jstring value)
   888 {
   889     const char *utfname = (*env)->GetStringUTFChars(env, name, NULL);
   890     const char *utfvalue = (*env)->GetStringUTFChars(env, value, NULL);
   891 
   892     SDL_setenv(utfname, utfvalue, 1);
   893 
   894     (*env)->ReleaseStringUTFChars(env, name, utfname);
   895     (*env)->ReleaseStringUTFChars(env, value, utfvalue);
   896 
   897 }
   898 
   899 /*******************************************************************************
   900              Functions called by SDL into Java
   901 *******************************************************************************/
   902 
   903 static int s_active = 0;
   904 struct LocalReferenceHolder
   905 {
   906     JNIEnv *m_env;
   907     const char *m_func;
   908 };
   909 
   910 static struct LocalReferenceHolder LocalReferenceHolder_Setup(const char *func)
   911 {
   912     struct LocalReferenceHolder refholder;
   913     refholder.m_env = NULL;
   914     refholder.m_func = func;
   915 #ifdef DEBUG_JNI
   916     SDL_Log("Entering function %s", func);
   917 #endif
   918     return refholder;
   919 }
   920 
   921 static SDL_bool LocalReferenceHolder_Init(struct LocalReferenceHolder *refholder, JNIEnv *env)
   922 {
   923     const int capacity = 16;
   924     if ((*env)->PushLocalFrame(env, capacity) < 0) {
   925         SDL_SetError("Failed to allocate enough JVM local references");
   926         return SDL_FALSE;
   927     }
   928     ++s_active;
   929     refholder->m_env = env;
   930     return SDL_TRUE;
   931 }
   932 
   933 static void LocalReferenceHolder_Cleanup(struct LocalReferenceHolder *refholder)
   934 {
   935 #ifdef DEBUG_JNI
   936     SDL_Log("Leaving function %s", refholder->m_func);
   937 #endif
   938     if (refholder->m_env) {
   939         JNIEnv* env = refholder->m_env;
   940         (*env)->PopLocalFrame(env, NULL);
   941         --s_active;
   942     }
   943 }
   944 
   945 static SDL_bool LocalReferenceHolder_IsActive(void)
   946 {
   947     return s_active > 0;
   948 }
   949 
   950 ANativeWindow* Android_JNI_GetNativeWindow(void)
   951 {
   952     ANativeWindow* anw;
   953     jobject s;
   954     JNIEnv *env = Android_JNI_GetEnv();
   955 
   956     s = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetNativeSurface);
   957     anw = ANativeWindow_fromSurface(env, s);
   958     (*env)->DeleteLocalRef(env, s);
   959 
   960     return anw;
   961 }
   962 
   963 void Android_JNI_SetActivityTitle(const char *title)
   964 {
   965     JNIEnv *mEnv = Android_JNI_GetEnv();
   966 
   967     jstring jtitle = (jstring)((*mEnv)->NewStringUTF(mEnv, title));
   968     (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, midSetActivityTitle, jtitle);
   969     (*mEnv)->DeleteLocalRef(mEnv, jtitle);
   970 }
   971 
   972 void Android_JNI_SetWindowStyle(SDL_bool fullscreen)
   973 {
   974     JNIEnv *mEnv = Android_JNI_GetEnv();
   975     (*mEnv)->CallStaticVoidMethod(mEnv, mActivityClass, midSetWindowStyle, fullscreen ? 1 : 0);
   976 }
   977 
   978 void Android_JNI_SetOrientation(int w, int h, int resizable, const char *hint)
   979 {
   980     JNIEnv *mEnv = Android_JNI_GetEnv();
   981 
   982     jstring jhint = (jstring)((*mEnv)->NewStringUTF(mEnv, (hint ? hint : "")));
   983     (*mEnv)->CallStaticVoidMethod(mEnv, mActivityClass, midSetOrientation, w, h, (resizable? 1 : 0), jhint);
   984     (*mEnv)->DeleteLocalRef(mEnv, jhint);
   985 }
   986 
   987 SDL_bool Android_JNI_GetAccelerometerValues(float values[3])
   988 {
   989     int i;
   990     SDL_bool retval = SDL_FALSE;
   991 
   992     if (bHasNewData) {
   993         for (i = 0; i < 3; ++i) {
   994             values[i] = fLastAccelerometer[i];
   995         }
   996         bHasNewData = SDL_FALSE;
   997         retval = SDL_TRUE;
   998     }
   999 
  1000     return retval;
  1001 }
  1002 
  1003 static void Android_JNI_ThreadDestroyed(void* value)
  1004 {
  1005     /* The thread is being destroyed, detach it from the Java VM and set the mThreadKey value to NULL as required */
  1006     JNIEnv *env = (JNIEnv*) value;
  1007     if (env != NULL) {
  1008         (*mJavaVM)->DetachCurrentThread(mJavaVM);
  1009         pthread_setspecific(mThreadKey, NULL);
  1010     }
  1011 }
  1012 
  1013 JNIEnv* Android_JNI_GetEnv(void)
  1014 {
  1015     /* From http://developer.android.com/guide/practices/jni.html
  1016      * All threads are Linux threads, scheduled by the kernel.
  1017      * They're usually started from managed code (using Thread.start), but they can also be created elsewhere and then
  1018      * attached to the JavaVM. For example, a thread started with pthread_create can be attached with the
  1019      * JNI AttachCurrentThread or AttachCurrentThreadAsDaemon functions. Until a thread is attached, it has no JNIEnv,
  1020      * and cannot make JNI calls.
  1021      * Attaching a natively-created thread causes a java.lang.Thread object to be constructed and added to the "main"
  1022      * ThreadGroup, making it visible to the debugger. Calling AttachCurrentThread on an already-attached thread
  1023      * is a no-op.
  1024      * Note: You can call this function any number of times for the same thread, there's no harm in it
  1025      */
  1026 
  1027     JNIEnv *env;
  1028     int status = (*mJavaVM)->AttachCurrentThread(mJavaVM, &env, NULL);
  1029     if(status < 0) {
  1030         LOGE("failed to attach current thread");
  1031         return 0;
  1032     }
  1033 
  1034     /* From http://developer.android.com/guide/practices/jni.html
  1035      * Threads attached through JNI must call DetachCurrentThread before they exit. If coding this directly is awkward,
  1036      * in Android 2.0 (Eclair) and higher you can use pthread_key_create to define a destructor function that will be
  1037      * called before the thread exits, and call DetachCurrentThread from there. (Use that key with pthread_setspecific
  1038      * to store the JNIEnv in thread-local-storage; that way it'll be passed into your destructor as the argument.)
  1039      * Note: The destructor is not called unless the stored value is != NULL
  1040      * Note: You can call this function any number of times for the same thread, there's no harm in it
  1041      *       (except for some lost CPU cycles)
  1042      */
  1043     pthread_setspecific(mThreadKey, (void*) env);
  1044 
  1045     return env;
  1046 }
  1047 
  1048 int Android_JNI_SetupThread(void)
  1049 {
  1050     Android_JNI_GetEnv();
  1051     return 1;
  1052 }
  1053 
  1054 /*
  1055  * Audio support
  1056  */
  1057 static int audioBufferFormat = 0;
  1058 static jobject audioBuffer = NULL;
  1059 static void* audioBufferPinned = NULL;
  1060 static int captureBufferFormat = 0;
  1061 static jobject captureBuffer = NULL;
  1062 
  1063 int Android_JNI_OpenAudioDevice(int iscapture, SDL_AudioSpec *spec)
  1064 {
  1065     int audioformat;
  1066     int numBufferFrames;
  1067     jobject jbufobj = NULL;
  1068     jobject result;
  1069     int *resultElements;
  1070     jboolean isCopy;
  1071 
  1072     JNIEnv *env = Android_JNI_GetEnv();
  1073 
  1074     if (!env) {
  1075         LOGE("callback_handler: failed to attach current thread");
  1076     }
  1077     Android_JNI_SetupThread();
  1078 
  1079     switch (spec->format) {
  1080     case AUDIO_U8:
  1081         audioformat = ENCODING_PCM_8BIT;
  1082         break;
  1083     case AUDIO_S16:
  1084         audioformat = ENCODING_PCM_16BIT;
  1085         break;
  1086     case AUDIO_F32:
  1087         audioformat = ENCODING_PCM_FLOAT;
  1088         break;
  1089     default:
  1090         return SDL_SetError("Unsupported audio format: 0x%x", spec->format);
  1091     }
  1092 
  1093     if (iscapture) {
  1094         __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for capture");
  1095         result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midCaptureOpen, spec->freq, audioformat, spec->channels, spec->samples);
  1096     } else {
  1097         __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for output");
  1098         result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midAudioOpen, spec->freq, audioformat, spec->channels, spec->samples);
  1099     }
  1100     if (result == NULL) {
  1101         /* Error during audio initialization, error printed from Java */
  1102         return SDL_SetError("Java-side initialization failed");
  1103     }
  1104 
  1105     if ((*env)->GetArrayLength(env, (jintArray)result) != 4) {
  1106         return SDL_SetError("Unexpected results from Java, expected 4, got %d", (*env)->GetArrayLength(env, (jintArray)result));
  1107     }
  1108     isCopy = JNI_FALSE;
  1109     resultElements = (*env)->GetIntArrayElements(env, (jintArray)result, &isCopy);
  1110     spec->freq = resultElements[0];
  1111     audioformat = resultElements[1];
  1112     switch (audioformat) {
  1113     case ENCODING_PCM_8BIT:
  1114         spec->format = AUDIO_U8;
  1115         break;
  1116     case ENCODING_PCM_16BIT:
  1117         spec->format = AUDIO_S16;
  1118         break;
  1119     case ENCODING_PCM_FLOAT:
  1120         spec->format = AUDIO_F32;
  1121         break;
  1122     default:
  1123         return SDL_SetError("Unexpected audio format from Java: %d\n", audioformat);
  1124     }
  1125     spec->channels = resultElements[2];
  1126     spec->samples = resultElements[3];
  1127     (*env)->ReleaseIntArrayElements(env, (jintArray)result, resultElements, JNI_ABORT);
  1128     (*env)->DeleteLocalRef(env, result);
  1129 
  1130     /* Allocating the audio buffer from the Java side and passing it as the return value for audioInit no longer works on
  1131      * Android >= 4.2 due to a "stale global reference" error. So now we allocate this buffer directly from this side. */
  1132     switch (audioformat) {
  1133     case ENCODING_PCM_8BIT:
  1134         {
  1135             jbyteArray audioBufferLocal = (*env)->NewByteArray(env, spec->samples * spec->channels);
  1136             if (audioBufferLocal) {
  1137                 jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
  1138                 (*env)->DeleteLocalRef(env, audioBufferLocal);
  1139             }
  1140         }
  1141         break;
  1142     case ENCODING_PCM_16BIT:
  1143         {
  1144             jshortArray audioBufferLocal = (*env)->NewShortArray(env, spec->samples * spec->channels);
  1145             if (audioBufferLocal) {
  1146                 jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
  1147                 (*env)->DeleteLocalRef(env, audioBufferLocal);
  1148             }
  1149         }
  1150         break;
  1151     case ENCODING_PCM_FLOAT:
  1152         {
  1153             jfloatArray audioBufferLocal = (*env)->NewFloatArray(env, spec->samples * spec->channels);
  1154             if (audioBufferLocal) {
  1155                 jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
  1156                 (*env)->DeleteLocalRef(env, audioBufferLocal);
  1157             }
  1158         }
  1159         break;
  1160     default:
  1161         return SDL_SetError("Unexpected audio format from Java: %d\n", audioformat);
  1162     }
  1163 
  1164     if (jbufobj == NULL) {
  1165         __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: could not allocate an audio buffer");
  1166         return SDL_OutOfMemory();
  1167     }
  1168 
  1169     if (iscapture) {
  1170         captureBufferFormat = audioformat;
  1171         captureBuffer = jbufobj;
  1172     } else {
  1173         audioBufferFormat = audioformat;
  1174         audioBuffer = jbufobj;
  1175     }
  1176     numBufferFrames = (*env)->GetArrayLength(env, (jarray)jbufobj);
  1177 
  1178     if (!iscapture) {
  1179         isCopy = JNI_FALSE;
  1180 
  1181         switch (audioformat) {
  1182         case ENCODING_PCM_8BIT:
  1183             audioBufferPinned = (*env)->GetByteArrayElements(env, (jbyteArray)audioBuffer, &isCopy);
  1184             break;
  1185         case ENCODING_PCM_16BIT:
  1186             audioBufferPinned = (*env)->GetShortArrayElements(env, (jshortArray)audioBuffer, &isCopy);
  1187             break;
  1188         case ENCODING_PCM_FLOAT:
  1189             audioBufferPinned = (*env)->GetFloatArrayElements(env, (jfloatArray)audioBuffer, &isCopy);
  1190             break;
  1191         default:
  1192             return SDL_SetError("Unexpected audio format from Java: %d\n", audioformat);
  1193         }
  1194     }
  1195     return 0;
  1196 }
  1197 
  1198 int Android_JNI_GetDisplayDPI(float *ddpi, float *xdpi, float *ydpi)
  1199 {
  1200     JNIEnv *env = Android_JNI_GetEnv();
  1201 
  1202     jobject jDisplayObj = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetDisplayDPI);
  1203     jclass jDisplayClass = (*env)->GetObjectClass(env, jDisplayObj);
  1204 
  1205     jfieldID fidXdpi = (*env)->GetFieldID(env, jDisplayClass, "xdpi", "F");
  1206     jfieldID fidYdpi = (*env)->GetFieldID(env, jDisplayClass, "ydpi", "F");
  1207     jfieldID fidDdpi = (*env)->GetFieldID(env, jDisplayClass, "densityDpi", "I");
  1208 
  1209     float nativeXdpi = (*env)->GetFloatField(env, jDisplayObj, fidXdpi);
  1210     float nativeYdpi = (*env)->GetFloatField(env, jDisplayObj, fidYdpi);
  1211     int nativeDdpi = (*env)->GetIntField(env, jDisplayObj, fidDdpi);
  1212 
  1213 
  1214     (*env)->DeleteLocalRef(env, jDisplayObj);
  1215     (*env)->DeleteLocalRef(env, jDisplayClass);
  1216 
  1217     if (ddpi) {
  1218         *ddpi = (float)nativeDdpi;
  1219     }
  1220     if (xdpi) {
  1221         *xdpi = nativeXdpi;
  1222     }
  1223     if (ydpi) {
  1224         *ydpi = nativeYdpi;
  1225     }
  1226 
  1227     return 0;
  1228 }
  1229 
  1230 void * Android_JNI_GetAudioBuffer(void)
  1231 {
  1232     return audioBufferPinned;
  1233 }
  1234 
  1235 void Android_JNI_WriteAudioBuffer(void)
  1236 {
  1237     JNIEnv *mAudioEnv = Android_JNI_GetEnv();
  1238 
  1239     switch (audioBufferFormat) {
  1240     case ENCODING_PCM_8BIT:
  1241         (*mAudioEnv)->ReleaseByteArrayElements(mAudioEnv, (jbyteArray)audioBuffer, (jbyte *)audioBufferPinned, JNI_COMMIT);
  1242         (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mAudioManagerClass, midAudioWriteByteBuffer, (jbyteArray)audioBuffer);
  1243         break;
  1244     case ENCODING_PCM_16BIT:
  1245         (*mAudioEnv)->ReleaseShortArrayElements(mAudioEnv, (jshortArray)audioBuffer, (jshort *)audioBufferPinned, JNI_COMMIT);
  1246         (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mAudioManagerClass, midAudioWriteShortBuffer, (jshortArray)audioBuffer);
  1247         break;
  1248     case ENCODING_PCM_FLOAT:
  1249         (*mAudioEnv)->ReleaseFloatArrayElements(mAudioEnv, (jfloatArray)audioBuffer, (jfloat *)audioBufferPinned, JNI_COMMIT);
  1250         (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mAudioManagerClass, midAudioWriteFloatBuffer, (jfloatArray)audioBuffer);
  1251         break;
  1252     default:
  1253         __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: unhandled audio buffer format");
  1254         break;
  1255     }
  1256 
  1257     /* JNI_COMMIT means the changes are committed to the VM but the buffer remains pinned */
  1258 }
  1259 
  1260 int Android_JNI_CaptureAudioBuffer(void *buffer, int buflen)
  1261 {
  1262     JNIEnv *env = Android_JNI_GetEnv();
  1263     jboolean isCopy = JNI_FALSE;
  1264     jint br = -1;
  1265 
  1266     switch (captureBufferFormat) {
  1267     case ENCODING_PCM_8BIT:
  1268         SDL_assert((*env)->GetArrayLength(env, (jshortArray)captureBuffer) == buflen);
  1269         br = (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_TRUE);
  1270         if (br > 0) {
  1271             jbyte *ptr = (*env)->GetByteArrayElements(env, (jbyteArray)captureBuffer, &isCopy);
  1272             SDL_memcpy(buffer, ptr, br);
  1273             (*env)->ReleaseByteArrayElements(env, (jbyteArray)captureBuffer, (jbyte *)ptr, JNI_ABORT);
  1274         }
  1275         break;
  1276     case ENCODING_PCM_16BIT:
  1277         SDL_assert((*env)->GetArrayLength(env, (jshortArray)captureBuffer) == (buflen / sizeof(Sint16)));
  1278         br = (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_TRUE);
  1279         if (br > 0) {
  1280             jshort *ptr = (*env)->GetShortArrayElements(env, (jshortArray)captureBuffer, &isCopy);
  1281             br *= sizeof(Sint16);
  1282             SDL_memcpy(buffer, ptr, br);
  1283             (*env)->ReleaseShortArrayElements(env, (jshortArray)captureBuffer, (jshort *)ptr, JNI_ABORT);
  1284         }
  1285         break;
  1286     case ENCODING_PCM_FLOAT:
  1287         SDL_assert((*env)->GetArrayLength(env, (jfloatArray)captureBuffer) == (buflen / sizeof(float)));
  1288         br = (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadFloatBuffer, (jfloatArray)captureBuffer, JNI_TRUE);
  1289         if (br > 0) {
  1290             jfloat *ptr = (*env)->GetFloatArrayElements(env, (jfloatArray)captureBuffer, &isCopy);
  1291             br *= sizeof(float);
  1292             SDL_memcpy(buffer, ptr, br);
  1293             (*env)->ReleaseFloatArrayElements(env, (jfloatArray)captureBuffer, (jfloat *)ptr, JNI_ABORT);
  1294         }
  1295         break;
  1296     default:
  1297         __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: unhandled capture buffer format");
  1298         break;
  1299     }
  1300     return br;
  1301 }
  1302 
  1303 void Android_JNI_FlushCapturedAudio(void)
  1304 {
  1305     JNIEnv *env = Android_JNI_GetEnv();
  1306 #if 0  /* !!! FIXME: this needs API 23, or it'll do blocking reads and never end. */
  1307     switch (captureBufferFormat) {
  1308     case ENCODING_PCM_8BIT:
  1309         {
  1310             const jint len = (*env)->GetArrayLength(env, (jbyteArray)captureBuffer);
  1311             while ((*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_FALSE) == len) { /* spin */ }
  1312         }
  1313         break;
  1314     case ENCODING_PCM_16BIT:
  1315         {
  1316             const jint len = (*env)->GetArrayLength(env, (jshortArray)captureBuffer);
  1317             while ((*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_FALSE) == len) { /* spin */ }
  1318         }
  1319         break;
  1320     case ENCODING_PCM_FLOAT:
  1321         {
  1322             const jint len = (*env)->GetArrayLength(env, (jfloatArray)captureBuffer);
  1323             while ((*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadFloatBuffer, (jfloatArray)captureBuffer, JNI_FALSE) == len) { /* spin */ }
  1324         }
  1325         break;
  1326     default:
  1327         __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: flushing unhandled capture buffer format");
  1328         break;
  1329     }
  1330 #else
  1331     switch (captureBufferFormat) {
  1332     case ENCODING_PCM_8BIT:
  1333         (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_FALSE);
  1334         break;
  1335     case ENCODING_PCM_16BIT:
  1336         (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_FALSE);
  1337         break;
  1338     case ENCODING_PCM_FLOAT:
  1339         (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadFloatBuffer, (jfloatArray)captureBuffer, JNI_FALSE);
  1340         break;
  1341     default:
  1342         __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: flushing unhandled capture buffer format");
  1343         break;
  1344     }
  1345 #endif
  1346 }
  1347 
  1348 void Android_JNI_CloseAudioDevice(const int iscapture)
  1349 {
  1350     JNIEnv *env = Android_JNI_GetEnv();
  1351 
  1352     if (iscapture) {
  1353         (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midCaptureClose);
  1354         if (captureBuffer) {
  1355             (*env)->DeleteGlobalRef(env, captureBuffer);
  1356             captureBuffer = NULL;
  1357         }
  1358     } else {
  1359         (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midAudioClose);
  1360         if (audioBuffer) {
  1361             (*env)->DeleteGlobalRef(env, audioBuffer);
  1362             audioBuffer = NULL;
  1363             audioBufferPinned = NULL;
  1364         }
  1365     }
  1366 }
  1367 
  1368 /* Test for an exception and call SDL_SetError with its detail if one occurs */
  1369 /* If the parameter silent is truthy then SDL_SetError() will not be called. */
  1370 static SDL_bool Android_JNI_ExceptionOccurred(SDL_bool silent)
  1371 {
  1372     JNIEnv *mEnv = Android_JNI_GetEnv();
  1373     jthrowable exception;
  1374 
  1375     SDL_assert(LocalReferenceHolder_IsActive());
  1376 
  1377     exception = (*mEnv)->ExceptionOccurred(mEnv);
  1378     if (exception != NULL) {
  1379         jmethodID mid;
  1380 
  1381         /* Until this happens most JNI operations have undefined behaviour */
  1382         (*mEnv)->ExceptionClear(mEnv);
  1383 
  1384         if (!silent) {
  1385             jclass exceptionClass = (*mEnv)->GetObjectClass(mEnv, exception);
  1386             jclass classClass = (*mEnv)->FindClass(mEnv, "java/lang/Class");
  1387             jstring exceptionName;
  1388             const char* exceptionNameUTF8;
  1389             jstring exceptionMessage;
  1390 
  1391             mid = (*mEnv)->GetMethodID(mEnv, classClass, "getName", "()Ljava/lang/String;");
  1392             exceptionName = (jstring)(*mEnv)->CallObjectMethod(mEnv, exceptionClass, mid);
  1393             exceptionNameUTF8 = (*mEnv)->GetStringUTFChars(mEnv, exceptionName, 0);
  1394 
  1395             mid = (*mEnv)->GetMethodID(mEnv, exceptionClass, "getMessage", "()Ljava/lang/String;");
  1396             exceptionMessage = (jstring)(*mEnv)->CallObjectMethod(mEnv, exception, mid);
  1397 
  1398             if (exceptionMessage != NULL) {
  1399                 const char* exceptionMessageUTF8 = (*mEnv)->GetStringUTFChars(mEnv, exceptionMessage, 0);
  1400                 SDL_SetError("%s: %s", exceptionNameUTF8, exceptionMessageUTF8);
  1401                 (*mEnv)->ReleaseStringUTFChars(mEnv, exceptionMessage, exceptionMessageUTF8);
  1402             } else {
  1403                 SDL_SetError("%s", exceptionNameUTF8);
  1404             }
  1405 
  1406             (*mEnv)->ReleaseStringUTFChars(mEnv, exceptionName, exceptionNameUTF8);
  1407         }
  1408 
  1409         return SDL_TRUE;
  1410     }
  1411 
  1412     return SDL_FALSE;
  1413 }
  1414 
  1415 static int Internal_Android_JNI_FileOpen(SDL_RWops* ctx)
  1416 {
  1417     struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
  1418 
  1419     int result = 0;
  1420 
  1421     jmethodID mid;
  1422     jobject context;
  1423     jobject assetManager;
  1424     jobject inputStream;
  1425     jclass channels;
  1426     jobject readableByteChannel;
  1427     jstring fileNameJString;
  1428     jobject fd;
  1429     jclass fdCls;
  1430     jfieldID descriptor;
  1431 
  1432     JNIEnv *mEnv = Android_JNI_GetEnv();
  1433     if (!LocalReferenceHolder_Init(&refs, mEnv)) {
  1434         goto failure;
  1435     }
  1436 
  1437     fileNameJString = (jstring)ctx->hidden.androidio.fileNameRef;
  1438     ctx->hidden.androidio.position = 0;
  1439 
  1440     /* context = SDLActivity.getContext(); */
  1441     context = (*mEnv)->CallStaticObjectMethod(mEnv, mActivityClass, midGetContext);
  1442 
  1443     /* assetManager = context.getAssets(); */
  1444     mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, context),
  1445             "getAssets", "()Landroid/content/res/AssetManager;");
  1446     assetManager = (*mEnv)->CallObjectMethod(mEnv, context, mid);
  1447 
  1448     /* First let's try opening the file to obtain an AssetFileDescriptor.
  1449     * This method reads the files directly from the APKs using standard *nix calls
  1450     */
  1451     mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, assetManager), "openFd", "(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;");
  1452     inputStream = (*mEnv)->CallObjectMethod(mEnv, assetManager, mid, fileNameJString);
  1453     if (Android_JNI_ExceptionOccurred(SDL_TRUE)) {
  1454         goto fallback;
  1455     }
  1456 
  1457     mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream), "getStartOffset", "()J");
  1458     ctx->hidden.androidio.offset = (*mEnv)->CallLongMethod(mEnv, inputStream, mid);
  1459     if (Android_JNI_ExceptionOccurred(SDL_TRUE)) {
  1460         goto fallback;
  1461     }
  1462 
  1463     mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream), "getDeclaredLength", "()J");
  1464     ctx->hidden.androidio.size = (*mEnv)->CallLongMethod(mEnv, inputStream, mid);
  1465     if (Android_JNI_ExceptionOccurred(SDL_TRUE)) {
  1466         goto fallback;
  1467     }
  1468 
  1469     mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream), "getFileDescriptor", "()Ljava/io/FileDescriptor;");
  1470     fd = (*mEnv)->CallObjectMethod(mEnv, inputStream, mid);
  1471     fdCls = (*mEnv)->GetObjectClass(mEnv, fd);
  1472     descriptor = (*mEnv)->GetFieldID(mEnv, fdCls, "descriptor", "I");
  1473     ctx->hidden.androidio.fd = (*mEnv)->GetIntField(mEnv, fd, descriptor);
  1474     ctx->hidden.androidio.assetFileDescriptorRef = (*mEnv)->NewGlobalRef(mEnv, inputStream);
  1475 
  1476     /* Seek to the correct offset in the file. */
  1477     lseek(ctx->hidden.androidio.fd, (off_t)ctx->hidden.androidio.offset, SEEK_SET);
  1478 
  1479     if (0) {
  1480 fallback:
  1481         /* Disabled log message because of spam on the Nexus 7 */
  1482         /* __android_log_print(ANDROID_LOG_DEBUG, "SDL", "Falling back to legacy InputStream method for opening file"); */
  1483 
  1484         /* Try the old method using InputStream */
  1485         ctx->hidden.androidio.assetFileDescriptorRef = NULL;
  1486 
  1487         /* inputStream = assetManager.open(<filename>); */
  1488         mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, assetManager),
  1489                 "open", "(Ljava/lang/String;I)Ljava/io/InputStream;");
  1490         inputStream = (*mEnv)->CallObjectMethod(mEnv, assetManager, mid, fileNameJString, 1 /* ACCESS_RANDOM */);
  1491         if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
  1492             /* Try fallback to APK expansion files */
  1493             inputStream = (*mEnv)->CallStaticObjectMethod(mEnv, mActivityClass, midOpenAPKExpansionInputStream, fileNameJString);
  1494 
  1495             /* Exception is checked first because it always needs to be cleared.
  1496              * If no exception occurred then the last SDL error message is kept.
  1497              */
  1498             if (Android_JNI_ExceptionOccurred(SDL_FALSE) || !inputStream) {
  1499                 goto failure;
  1500             }
  1501         }
  1502 
  1503         ctx->hidden.androidio.inputStreamRef = (*mEnv)->NewGlobalRef(mEnv, inputStream);
  1504 
  1505         /* Despite all the visible documentation on [Asset]InputStream claiming
  1506          * that the .available() method is not guaranteed to return the entire file
  1507          * size, comments in <sdk>/samples/<ver>/ApiDemos/src/com/example/ ...
  1508          * android/apis/content/ReadAsset.java imply that Android's
  1509          * AssetInputStream.available() /will/ always return the total file size
  1510         */
  1511 
  1512         /* size = inputStream.available(); */
  1513         mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
  1514                 "available", "()I");
  1515         ctx->hidden.androidio.size = (long)(*mEnv)->CallIntMethod(mEnv, inputStream, mid);
  1516         if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
  1517             goto failure;
  1518         }
  1519 
  1520         /* readableByteChannel = Channels.newChannel(inputStream); */
  1521         channels = (*mEnv)->FindClass(mEnv, "java/nio/channels/Channels");
  1522         mid = (*mEnv)->GetStaticMethodID(mEnv, channels,
  1523                 "newChannel",
  1524                 "(Ljava/io/InputStream;)Ljava/nio/channels/ReadableByteChannel;");
  1525         readableByteChannel = (*mEnv)->CallStaticObjectMethod(
  1526                 mEnv, channels, mid, inputStream);
  1527         if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
  1528             goto failure;
  1529         }
  1530 
  1531         ctx->hidden.androidio.readableByteChannelRef =
  1532             (*mEnv)->NewGlobalRef(mEnv, readableByteChannel);
  1533 
  1534         /* Store .read id for reading purposes */
  1535         mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, readableByteChannel),
  1536                 "read", "(Ljava/nio/ByteBuffer;)I");
  1537         ctx->hidden.androidio.readMethod = mid;
  1538     }
  1539 
  1540     if (0) {
  1541 failure:
  1542         result = -1;
  1543 
  1544         (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.fileNameRef);
  1545 
  1546         if(ctx->hidden.androidio.inputStreamRef != NULL) {
  1547             (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.inputStreamRef);
  1548         }
  1549 
  1550         if(ctx->hidden.androidio.readableByteChannelRef != NULL) {
  1551             (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.readableByteChannelRef);
  1552         }
  1553 
  1554         if(ctx->hidden.androidio.assetFileDescriptorRef != NULL) {
  1555             (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.assetFileDescriptorRef);
  1556         }
  1557 
  1558     }
  1559 
  1560     LocalReferenceHolder_Cleanup(&refs);
  1561     return result;
  1562 }
  1563 
  1564 int Android_JNI_FileOpen(SDL_RWops* ctx,
  1565         const char* fileName, const char* mode)
  1566 {
  1567     struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
  1568     JNIEnv *mEnv = Android_JNI_GetEnv();
  1569     int retval;
  1570     jstring fileNameJString;
  1571 
  1572     if (!LocalReferenceHolder_Init(&refs, mEnv)) {
  1573         LocalReferenceHolder_Cleanup(&refs);
  1574         return -1;
  1575     }
  1576 
  1577     if (!ctx) {
  1578         LocalReferenceHolder_Cleanup(&refs);
  1579         return -1;
  1580     }
  1581 
  1582     fileNameJString = (*mEnv)->NewStringUTF(mEnv, fileName);
  1583     ctx->hidden.androidio.fileNameRef = (*mEnv)->NewGlobalRef(mEnv, fileNameJString);
  1584     ctx->hidden.androidio.inputStreamRef = NULL;
  1585     ctx->hidden.androidio.readableByteChannelRef = NULL;
  1586     ctx->hidden.androidio.readMethod = NULL;
  1587     ctx->hidden.androidio.assetFileDescriptorRef = NULL;
  1588 
  1589     retval = Internal_Android_JNI_FileOpen(ctx);
  1590     LocalReferenceHolder_Cleanup(&refs);
  1591     return retval;
  1592 }
  1593 
  1594 size_t Android_JNI_FileRead(SDL_RWops* ctx, void* buffer,
  1595         size_t size, size_t maxnum)
  1596 {
  1597     struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
  1598 
  1599     if (ctx->hidden.androidio.assetFileDescriptorRef) {
  1600         size_t bytesMax = size * maxnum;
  1601         size_t result;
  1602         if (ctx->hidden.androidio.size != -1 /* UNKNOWN_LENGTH */ && ctx->hidden.androidio.position + bytesMax > ctx->hidden.androidio.size) {
  1603             bytesMax = ctx->hidden.androidio.size - ctx->hidden.androidio.position;
  1604         }
  1605         result = read(ctx->hidden.androidio.fd, buffer, bytesMax );
  1606         if (result > 0) {
  1607             ctx->hidden.androidio.position += result;
  1608             LocalReferenceHolder_Cleanup(&refs);
  1609             return result / size;
  1610         }
  1611         LocalReferenceHolder_Cleanup(&refs);
  1612         return 0;
  1613     } else {
  1614         jlong bytesRemaining = (jlong) (size * maxnum);
  1615         jlong bytesMax = (jlong) (ctx->hidden.androidio.size -  ctx->hidden.androidio.position);
  1616         int bytesRead = 0;
  1617         JNIEnv *mEnv;
  1618         jobject readableByteChannel;
  1619         jmethodID readMethod;
  1620         jobject byteBuffer;
  1621 
  1622         /* Don't read more bytes than those that remain in the file, otherwise we get an exception */
  1623         if (bytesRemaining >  bytesMax) bytesRemaining = bytesMax;
  1624 
  1625         mEnv = Android_JNI_GetEnv();
  1626         if (!LocalReferenceHolder_Init(&refs, mEnv)) {
  1627             LocalReferenceHolder_Cleanup(&refs);
  1628             return 0;
  1629         }
  1630 
  1631         readableByteChannel = (jobject)ctx->hidden.androidio.readableByteChannelRef;
  1632         readMethod = (jmethodID)ctx->hidden.androidio.readMethod;
  1633         byteBuffer = (*mEnv)->NewDirectByteBuffer(mEnv, buffer, bytesRemaining);
  1634 
  1635         while (bytesRemaining > 0) {
  1636             /* result = readableByteChannel.read(...); */
  1637             int result = (*mEnv)->CallIntMethod(mEnv, readableByteChannel, readMethod, byteBuffer);
  1638 
  1639             if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
  1640                 LocalReferenceHolder_Cleanup(&refs);
  1641                 return 0;
  1642             }
  1643 
  1644             if (result < 0) {
  1645                 break;
  1646             }
  1647 
  1648             bytesRemaining -= result;
  1649             bytesRead += result;
  1650             ctx->hidden.androidio.position += result;
  1651         }
  1652         LocalReferenceHolder_Cleanup(&refs);
  1653         return bytesRead / size;
  1654     }
  1655 }
  1656 
  1657 size_t Android_JNI_FileWrite(SDL_RWops* ctx, const void* buffer,
  1658         size_t size, size_t num)
  1659 {
  1660     SDL_SetError("Cannot write to Android package filesystem");
  1661     return 0;
  1662 }
  1663 
  1664 static int Internal_Android_JNI_FileClose(SDL_RWops* ctx, SDL_bool release)
  1665 {
  1666     struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
  1667 
  1668     int result = 0;
  1669     JNIEnv *mEnv = Android_JNI_GetEnv();
  1670 
  1671     if (!LocalReferenceHolder_Init(&refs, mEnv)) {
  1672         LocalReferenceHolder_Cleanup(&refs);
  1673         return SDL_SetError("Failed to allocate enough JVM local references");
  1674     }
  1675 
  1676     if (ctx) {
  1677         if (release) {
  1678             (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.fileNameRef);
  1679         }
  1680 
  1681         if (ctx->hidden.androidio.assetFileDescriptorRef) {
  1682             jobject inputStream = (jobject)ctx->hidden.androidio.assetFileDescriptorRef;
  1683             jmethodID mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
  1684                     "close", "()V");
  1685             (*mEnv)->CallVoidMethod(mEnv, inputStream, mid);
  1686             (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.assetFileDescriptorRef);
  1687             if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
  1688                 result = -1;
  1689             }
  1690         }
  1691         else {
  1692             jobject inputStream = (jobject)ctx->hidden.androidio.inputStreamRef;
  1693 
  1694             /* inputStream.close(); */
  1695             jmethodID mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
  1696                     "close", "()V");
  1697             (*mEnv)->CallVoidMethod(mEnv, inputStream, mid);
  1698             (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.inputStreamRef);
  1699             (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.readableByteChannelRef);
  1700             if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
  1701                 result = -1;
  1702             }
  1703         }
  1704 
  1705         if (release) {
  1706             SDL_FreeRW(ctx);
  1707         }
  1708     }
  1709 
  1710     LocalReferenceHolder_Cleanup(&refs);
  1711     return result;
  1712 }
  1713 
  1714 
  1715 Sint64 Android_JNI_FileSize(SDL_RWops* ctx)
  1716 {
  1717     return ctx->hidden.androidio.size;
  1718 }
  1719 
  1720 Sint64 Android_JNI_FileSeek(SDL_RWops* ctx, Sint64 offset, int whence)
  1721 {
  1722     if (ctx->hidden.androidio.assetFileDescriptorRef) {
  1723         off_t ret;
  1724         switch (whence) {
  1725             case RW_SEEK_SET:
  1726                 if (ctx->hidden.androidio.size != -1 /* UNKNOWN_LENGTH */ && offset > ctx->hidden.androidio.size) offset = ctx->hidden.androidio.size;
  1727                 offset += ctx->hidden.androidio.offset;
  1728                 break;
  1729             case RW_SEEK_CUR:
  1730                 offset += ctx->hidden.androidio.position;
  1731                 if (ctx->hidden.androidio.size != -1 /* UNKNOWN_LENGTH */ && offset > ctx->hidden.androidio.size) offset = ctx->hidden.androidio.size;
  1732                 offset += ctx->hidden.androidio.offset;
  1733                 break;
  1734             case RW_SEEK_END:
  1735                 offset = ctx->hidden.androidio.offset + ctx->hidden.androidio.size + offset;
  1736                 break;
  1737             default:
  1738                 return SDL_SetError("Unknown value for 'whence'");
  1739         }
  1740         whence = SEEK_SET;
  1741 
  1742         ret = lseek(ctx->hidden.androidio.fd, (off_t)offset, SEEK_SET);
  1743         if (ret == -1) return -1;
  1744         ctx->hidden.androidio.position = ret - ctx->hidden.androidio.offset;
  1745     } else {
  1746         Sint64 newPosition;
  1747         Sint64 movement;
  1748 
  1749         switch (whence) {
  1750             case RW_SEEK_SET:
  1751                 newPosition = offset;
  1752                 break;
  1753             case RW_SEEK_CUR:
  1754                 newPosition = ctx->hidden.androidio.position + offset;
  1755                 break;
  1756             case RW_SEEK_END:
  1757                 newPosition = ctx->hidden.androidio.size + offset;
  1758                 break;
  1759             default:
  1760                 return SDL_SetError("Unknown value for 'whence'");
  1761         }
  1762 
  1763         /* Validate the new position */
  1764         if (newPosition < 0) {
  1765             return SDL_Error(SDL_EFSEEK);
  1766         }
  1767         if (newPosition > ctx->hidden.androidio.size) {
  1768             newPosition = ctx->hidden.androidio.size;
  1769         }
  1770 
  1771         movement = newPosition - ctx->hidden.androidio.position;
  1772         if (movement > 0) {
  1773             unsigned char buffer[4096];
  1774 
  1775             /* The easy case where we're seeking forwards */
  1776             while (movement > 0) {
  1777                 Sint64 amount = sizeof (buffer);
  1778                 size_t result;
  1779                 if (amount > movement) {
  1780                     amount = movement;
  1781                 }
  1782                 result = Android_JNI_FileRead(ctx, buffer, 1, amount);
  1783                 if (result <= 0) {
  1784                     /* Failed to read/skip the required amount, so fail */
  1785                     return -1;
  1786                 }
  1787 
  1788                 movement -= result;
  1789             }
  1790 
  1791         } else if (movement < 0) {
  1792             /* We can't seek backwards so we have to reopen the file and seek */
  1793             /* forwards which obviously isn't very efficient */
  1794             Internal_Android_JNI_FileClose(ctx, SDL_FALSE);
  1795             Internal_Android_JNI_FileOpen(ctx);
  1796             Android_JNI_FileSeek(ctx, newPosition, RW_SEEK_SET);
  1797         }
  1798     }
  1799 
  1800     return ctx->hidden.androidio.position;
  1801 
  1802 }
  1803 
  1804 int Android_JNI_FileClose(SDL_RWops* ctx)
  1805 {
  1806     return Internal_Android_JNI_FileClose(ctx, SDL_TRUE);
  1807 }
  1808 
  1809 int Android_JNI_SetClipboardText(const char* text)
  1810 {
  1811     JNIEnv* env = Android_JNI_GetEnv();
  1812     jstring string = (*env)->NewStringUTF(env, text);
  1813     (*env)->CallStaticVoidMethod(env, mActivityClass, midClipboardSetText, string);
  1814     (*env)->DeleteLocalRef(env, string);
  1815     return 0;
  1816 }
  1817 
  1818 char* Android_JNI_GetClipboardText(void)
  1819 {
  1820     JNIEnv* env = Android_JNI_GetEnv();
  1821     char* text = NULL;
  1822     jstring string;
  1823     
  1824     string = (*env)->CallStaticObjectMethod(env, mActivityClass, midClipboardGetText);
  1825     if (string) {
  1826         const char* utf = (*env)->GetStringUTFChars(env, string, 0);
  1827         if (utf) {
  1828             text = SDL_strdup(utf);
  1829             (*env)->ReleaseStringUTFChars(env, string, utf);
  1830         }
  1831         (*env)->DeleteLocalRef(env, string);
  1832     }
  1833     
  1834     return (text == NULL) ? SDL_strdup("") : text;
  1835 }
  1836 
  1837 SDL_bool Android_JNI_HasClipboardText(void)
  1838 {
  1839     JNIEnv* env = Android_JNI_GetEnv();
  1840     jboolean retval = (*env)->CallStaticBooleanMethod(env, mActivityClass, midClipboardHasText);
  1841     return (retval == JNI_TRUE) ? SDL_TRUE : SDL_FALSE;
  1842 }
  1843 
  1844 /* returns 0 on success or -1 on error (others undefined then)
  1845  * returns truthy or falsy value in plugged, charged and battery
  1846  * returns the value in seconds and percent or -1 if not available
  1847  */
  1848 int Android_JNI_GetPowerInfo(int* plugged, int* charged, int* battery, int* seconds, int* percent)
  1849 {
  1850     struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
  1851     JNIEnv* env = Android_JNI_GetEnv();
  1852     jmethodID mid;
  1853     jobject context;
  1854     jstring action;
  1855     jclass cls;
  1856     jobject filter;
  1857     jobject intent;
  1858     jstring iname;
  1859     jmethodID imid;
  1860     jstring bname;
  1861     jmethodID bmid;
  1862     if (!LocalReferenceHolder_Init(&refs, env)) {
  1863         LocalReferenceHolder_Cleanup(&refs);
  1864         return -1;
  1865     }
  1866 
  1867 
  1868     /* context = SDLActivity.getContext(); */
  1869     context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
  1870 
  1871     action = (*env)->NewStringUTF(env, "android.intent.action.BATTERY_CHANGED");
  1872 
  1873     cls = (*env)->FindClass(env, "android/content/IntentFilter");
  1874 
  1875     mid = (*env)->GetMethodID(env, cls, "<init>", "(Ljava/lang/String;)V");
  1876     filter = (*env)->NewObject(env, cls, mid, action);
  1877 
  1878     (*env)->DeleteLocalRef(env, action);
  1879 
  1880     mid = (*env)->GetMethodID(env, mActivityClass, "registerReceiver", "(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;");
  1881     intent = (*env)->CallObjectMethod(env, context, mid, NULL, filter);
  1882 
  1883     (*env)->DeleteLocalRef(env, filter);
  1884 
  1885     cls = (*env)->GetObjectClass(env, intent);
  1886 
  1887     imid = (*env)->GetMethodID(env, cls, "getIntExtra", "(Ljava/lang/String;I)I");
  1888 
  1889     /* Watch out for C89 scoping rules because of the macro */
  1890 #define GET_INT_EXTRA(var, key) \
  1891     int var; \
  1892     iname = (*env)->NewStringUTF(env, key); \
  1893     var = (*env)->CallIntMethod(env, intent, imid, iname, -1); \
  1894     (*env)->DeleteLocalRef(env, iname);
  1895 
  1896     bmid = (*env)->GetMethodID(env, cls, "getBooleanExtra", "(Ljava/lang/String;Z)Z");
  1897 
  1898     /* Watch out for C89 scoping rules because of the macro */
  1899 #define GET_BOOL_EXTRA(var, key) \
  1900     int var; \
  1901     bname = (*env)->NewStringUTF(env, key); \
  1902     var = (*env)->CallBooleanMethod(env, intent, bmid, bname, JNI_FALSE); \
  1903     (*env)->DeleteLocalRef(env, bname);
  1904 
  1905     if (plugged) {
  1906         /* Watch out for C89 scoping rules because of the macro */
  1907         GET_INT_EXTRA(plug, "plugged") /* == BatteryManager.EXTRA_PLUGGED (API 5) */
  1908         if (plug == -1) {
  1909             LocalReferenceHolder_Cleanup(&refs);
  1910             return -1;
  1911         }
  1912         /* 1 == BatteryManager.BATTERY_PLUGGED_AC */
  1913         /* 2 == BatteryManager.BATTERY_PLUGGED_USB */
  1914         *plugged = (0 < plug) ? 1 : 0;
  1915     }
  1916 
  1917     if (charged) {
  1918         /* Watch out for C89 scoping rules because of the macro */
  1919         GET_INT_EXTRA(status, "status") /* == BatteryManager.EXTRA_STATUS (API 5) */
  1920         if (status == -1) {
  1921             LocalReferenceHolder_Cleanup(&refs);
  1922             return -1;
  1923         }
  1924         /* 5 == BatteryManager.BATTERY_STATUS_FULL */
  1925         *charged = (status == 5) ? 1 : 0;
  1926     }
  1927 
  1928     if (battery) {
  1929         GET_BOOL_EXTRA(present, "present") /* == BatteryManager.EXTRA_PRESENT (API 5) */
  1930         *battery = present ? 1 : 0;
  1931     }
  1932 
  1933     if (seconds) {
  1934         *seconds = -1; /* not possible */
  1935     }
  1936 
  1937     if (percent) {
  1938         int level;
  1939         int scale;
  1940 
  1941         /* Watch out for C89 scoping rules because of the macro */
  1942         {
  1943             GET_INT_EXTRA(level_temp, "level") /* == BatteryManager.EXTRA_LEVEL (API 5) */
  1944             level = level_temp;
  1945         }
  1946         /* Watch out for C89 scoping rules because of the macro */
  1947         {
  1948             GET_INT_EXTRA(scale_temp, "scale") /* == BatteryManager.EXTRA_SCALE (API 5) */
  1949             scale = scale_temp;
  1950         }
  1951 
  1952         if ((level == -1) || (scale == -1)) {
  1953             LocalReferenceHolder_Cleanup(&refs);
  1954             return -1;
  1955         }
  1956         *percent = level * 100 / scale;
  1957     }
  1958 
  1959     (*env)->DeleteLocalRef(env, intent);
  1960 
  1961     LocalReferenceHolder_Cleanup(&refs);
  1962     return 0;
  1963 }
  1964 
  1965 /* returns number of found touch devices as return value and ids in parameter ids */
  1966 int Android_JNI_GetTouchDeviceIds(int **ids) {
  1967     JNIEnv *env = Android_JNI_GetEnv();
  1968     jint sources = 4098; /* == InputDevice.SOURCE_TOUCHSCREEN */
  1969     jintArray array = (jintArray) (*env)->CallStaticObjectMethod(env, mActivityClass, midInputGetInputDeviceIds, sources);
  1970     int number = 0;
  1971     *ids = NULL;
  1972     if (array) {
  1973         number = (int) (*env)->GetArrayLength(env, array);
  1974         if (0 < number) {
  1975             jint* elements = (*env)->GetIntArrayElements(env, array, NULL);
  1976             if (elements) {
  1977                 int i;
  1978                 *ids = SDL_malloc(number * sizeof (**ids));
  1979                 for (i = 0; i < number; ++i) { /* not assuming sizeof (jint) == sizeof (int) */
  1980                     (*ids)[i] = elements[i];
  1981                 }
  1982                 (*env)->ReleaseIntArrayElements(env, array, elements, JNI_ABORT);
  1983             }
  1984         }
  1985         (*env)->DeleteLocalRef(env, array);
  1986     }
  1987     return number;
  1988 }
  1989 
  1990 /* sets the mSeparateMouseAndTouch field */
  1991 void Android_JNI_SetSeparateMouseAndTouch(SDL_bool new_value)
  1992 {
  1993     JNIEnv *env = Android_JNI_GetEnv();
  1994     (*env)->SetStaticBooleanField(env, mActivityClass, fidSeparateMouseAndTouch, new_value ? JNI_TRUE : JNI_FALSE);
  1995 }
  1996 
  1997 void Android_JNI_PollInputDevices(void)
  1998 {
  1999     JNIEnv *env = Android_JNI_GetEnv();
  2000     (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midPollInputDevices);
  2001 }
  2002 
  2003 void Android_JNI_PollHapticDevices(void)
  2004 {
  2005     JNIEnv *env = Android_JNI_GetEnv();
  2006     (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midPollHapticDevices);
  2007 }
  2008 
  2009 void Android_JNI_HapticRun(int device_id, float intensity, int length)
  2010 {
  2011     JNIEnv *env = Android_JNI_GetEnv();
  2012     (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midHapticRun, device_id, intensity, length);
  2013 }
  2014 
  2015 void Android_JNI_HapticStop(int device_id)
  2016 {
  2017     JNIEnv *env = Android_JNI_GetEnv();
  2018     (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midHapticStop, device_id);
  2019 }
  2020 
  2021 /* See SDLActivity.java for constants. */
  2022 #define COMMAND_SET_KEEP_SCREEN_ON    5
  2023 
  2024 /* sends message to be handled on the UI event dispatch thread */
  2025 int Android_JNI_SendMessage(int command, int param)
  2026 {
  2027     JNIEnv *env = Android_JNI_GetEnv();
  2028     jboolean success;
  2029     success = (*env)->CallStaticBooleanMethod(env, mActivityClass, midSendMessage, command, param);
  2030     return success ? 0 : -1;
  2031 }
  2032 
  2033 void Android_JNI_SuspendScreenSaver(SDL_bool suspend)
  2034 {
  2035     Android_JNI_SendMessage(COMMAND_SET_KEEP_SCREEN_ON, (suspend == SDL_FALSE) ? 0 : 1);
  2036 }
  2037 
  2038 void Android_JNI_ShowTextInput(SDL_Rect *inputRect)
  2039 {
  2040     JNIEnv *env = Android_JNI_GetEnv();
  2041     (*env)->CallStaticBooleanMethod(env, mActivityClass, midShowTextInput,
  2042                                inputRect->x,
  2043                                inputRect->y,
  2044                                inputRect->w,
  2045                                inputRect->h );
  2046 }
  2047 
  2048 void Android_JNI_HideTextInput(void)
  2049 {
  2050     /* has to match Activity constant */
  2051     const int COMMAND_TEXTEDIT_HIDE = 3;
  2052     Android_JNI_SendMessage(COMMAND_TEXTEDIT_HIDE, 0);
  2053 }
  2054 
  2055 SDL_bool Android_JNI_IsScreenKeyboardShown()
  2056 {
  2057     JNIEnv *mEnv = Android_JNI_GetEnv();
  2058     jboolean is_shown = 0;
  2059     is_shown = (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, midIsScreenKeyboardShown);
  2060     return is_shown;
  2061 }
  2062 
  2063 
  2064 int Android_JNI_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
  2065 {
  2066     JNIEnv *env;
  2067     jclass clazz;
  2068     jmethodID mid;
  2069     jobject context;
  2070     jstring title;
  2071     jstring message;
  2072     jintArray button_flags;
  2073     jintArray button_ids;
  2074     jobjectArray button_texts;
  2075     jintArray colors;
  2076     jobject text;
  2077     jint temp;
  2078     int i;
  2079 
  2080     env = Android_JNI_GetEnv();
  2081 
  2082     /* convert parameters */
  2083 
  2084     clazz = (*env)->FindClass(env, "java/lang/String");
  2085 
  2086     title = (*env)->NewStringUTF(env, messageboxdata->title);
  2087     message = (*env)->NewStringUTF(env, messageboxdata->message);
  2088 
  2089     button_flags = (*env)->NewIntArray(env, messageboxdata->numbuttons);
  2090     button_ids = (*env)->NewIntArray(env, messageboxdata->numbuttons);
  2091     button_texts = (*env)->NewObjectArray(env, messageboxdata->numbuttons,
  2092         clazz, NULL);
  2093     for (i = 0; i < messageboxdata->numbuttons; ++i) {
  2094         temp = messageboxdata->buttons[i].flags;
  2095         (*env)->SetIntArrayRegion(env, button_flags, i, 1, &temp);
  2096         temp = messageboxdata->buttons[i].buttonid;
  2097         (*env)->SetIntArrayRegion(env, button_ids, i, 1, &temp);
  2098         text = (*env)->NewStringUTF(env, messageboxdata->buttons[i].text);
  2099         (*env)->SetObjectArrayElement(env, button_texts, i, text);
  2100         (*env)->DeleteLocalRef(env, text);
  2101     }
  2102 
  2103     if (messageboxdata->colorScheme) {
  2104         colors = (*env)->NewIntArray(env, SDL_MESSAGEBOX_COLOR_MAX);
  2105         for (i = 0; i < SDL_MESSAGEBOX_COLOR_MAX; ++i) {
  2106             temp = (0xFF << 24) |
  2107                    (messageboxdata->colorScheme->colors[i].r << 16) |
  2108                    (messageboxdata->colorScheme->colors[i].g << 8) |
  2109                    (messageboxdata->colorScheme->colors[i].b << 0);
  2110             (*env)->SetIntArrayRegion(env, colors, i, 1, &temp);
  2111         }
  2112     } else {
  2113         colors = NULL;
  2114     }
  2115 
  2116     (*env)->DeleteLocalRef(env, clazz);
  2117 
  2118     /* context = SDLActivity.getContext(); */
  2119     context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
  2120 
  2121     clazz = (*env)->GetObjectClass(env, context);
  2122 
  2123     mid = (*env)->GetMethodID(env, clazz,
  2124         "messageboxShowMessageBox", "(ILjava/lang/String;Ljava/lang/String;[I[I[Ljava/lang/String;[I)I");
  2125     *buttonid = (*env)->CallIntMethod(env, context, mid,
  2126         messageboxdata->flags,
  2127         title,
  2128         message,
  2129         button_flags,
  2130         button_ids,
  2131         button_texts,
  2132         colors);
  2133 
  2134     (*env)->DeleteLocalRef(env, context);
  2135     (*env)->DeleteLocalRef(env, clazz);
  2136 
  2137     /* delete parameters */
  2138 
  2139     (*env)->DeleteLocalRef(env, title);
  2140     (*env)->DeleteLocalRef(env, message);
  2141     (*env)->DeleteLocalRef(env, button_flags);
  2142     (*env)->DeleteLocalRef(env, button_ids);
  2143     (*env)->DeleteLocalRef(env, button_texts);
  2144     (*env)->DeleteLocalRef(env, colors);
  2145 
  2146     return 0;
  2147 }
  2148 
  2149 /*
  2150 //////////////////////////////////////////////////////////////////////////////
  2151 //
  2152 // Functions exposed to SDL applications in SDL_system.h
  2153 //////////////////////////////////////////////////////////////////////////////
  2154 */
  2155 
  2156 void *SDL_AndroidGetJNIEnv(void)
  2157 {
  2158     return Android_JNI_GetEnv();
  2159 }
  2160 
  2161 void *SDL_AndroidGetActivity(void)
  2162 {
  2163     /* See SDL_system.h for caveats on using this function. */
  2164 
  2165     JNIEnv *env = Android_JNI_GetEnv();
  2166     if (!env) {
  2167         return NULL;
  2168     }
  2169 
  2170     /* return SDLActivity.getContext(); */
  2171     return (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
  2172 }
  2173 
  2174 SDL_bool SDL_IsAndroidTablet(void)
  2175 {
  2176     JNIEnv *env = Android_JNI_GetEnv();
  2177     return (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsTablet);
  2178 }
  2179 
  2180 SDL_bool SDL_IsAndroidTV(void)
  2181 {
  2182     JNIEnv *env = Android_JNI_GetEnv();
  2183     return (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsAndroidTV);
  2184 }
  2185 
  2186 SDL_bool SDL_IsChromebook(void)
  2187 {
  2188     JNIEnv *env = Android_JNI_GetEnv();
  2189     return (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsChromebook);
  2190 }
  2191 
  2192 SDL_bool SDL_IsDeXMode(void)
  2193 {
  2194     JNIEnv *env = Android_JNI_GetEnv();
  2195     return (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsDeXMode);
  2196 }
  2197 
  2198 void SDL_AndroidBackButton(void)
  2199 {
  2200     JNIEnv *env = Android_JNI_GetEnv();
  2201     return (*env)->CallStaticVoidMethod(env, mActivityClass, midManualBackButton);
  2202 }
  2203 
  2204 const char * SDL_AndroidGetInternalStoragePath(void)
  2205 {
  2206     static char *s_AndroidInternalFilesPath = NULL;
  2207 
  2208     if (!s_AndroidInternalFilesPath) {
  2209         struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
  2210         jmethodID mid;
  2211         jobject context;
  2212         jobject fileObject;
  2213         jstring pathString;
  2214         const char *path;
  2215 
  2216         JNIEnv *env = Android_JNI_GetEnv();
  2217         if (!LocalReferenceHolder_Init(&refs, env)) {
  2218             LocalReferenceHolder_Cleanup(&refs);
  2219             return NULL;
  2220         }
  2221 
  2222         /* context = SDLActivity.getContext(); */
  2223         context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
  2224         if (!context) {
  2225             SDL_SetError("Couldn't get Android context!");
  2226             LocalReferenceHolder_Cleanup(&refs);
  2227             return NULL;
  2228         }
  2229 
  2230         /* fileObj = context.getFilesDir(); */
  2231         mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
  2232                 "getFilesDir", "()Ljava/io/File;");
  2233         fileObject = (*env)->CallObjectMethod(env, context, mid);
  2234         if (!fileObject) {
  2235             SDL_SetError("Couldn't get internal directory");
  2236             LocalReferenceHolder_Cleanup(&refs);
  2237             return NULL;
  2238         }
  2239 
  2240         /* path = fileObject.getCanonicalPath(); */
  2241         mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, fileObject),
  2242                 "getCanonicalPath", "()Ljava/lang/String;");
  2243         pathString = (jstring)(*env)->CallObjectMethod(env, fileObject, mid);
  2244         if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
  2245             LocalReferenceHolder_Cleanup(&refs);
  2246             return NULL;
  2247         }
  2248 
  2249         path = (*env)->GetStringUTFChars(env, pathString, NULL);
  2250         s_AndroidInternalFilesPath = SDL_strdup(path);
  2251         (*env)->ReleaseStringUTFChars(env, pathString, path);
  2252 
  2253         LocalReferenceHolder_Cleanup(&refs);
  2254     }
  2255     return s_AndroidInternalFilesPath;
  2256 }
  2257 
  2258 int SDL_AndroidGetExternalStorageState(void)
  2259 {
  2260     struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
  2261     jmethodID mid;
  2262     jclass cls;
  2263     jstring stateString;
  2264     const char *state;
  2265     int stateFlags;
  2266 
  2267     JNIEnv *env = Android_JNI_GetEnv();
  2268     if (!LocalReferenceHolder_Init(&refs, env)) {
  2269         LocalReferenceHolder_Cleanup(&refs);
  2270         return 0;
  2271     }
  2272 
  2273     cls = (*env)->FindClass(env, "android/os/Environment");
  2274     mid = (*env)->GetStaticMethodID(env, cls,
  2275             "getExternalStorageState", "()Ljava/lang/String;");
  2276     stateString = (jstring)(*env)->CallStaticObjectMethod(env, cls, mid);
  2277 
  2278     state = (*env)->GetStringUTFChars(env, stateString, NULL);
  2279 
  2280     /* Print an info message so people debugging know the storage state */
  2281     __android_log_print(ANDROID_LOG_INFO, "SDL", "external storage state: %s", state);
  2282 
  2283     if (SDL_strcmp(state, "mounted") == 0) {
  2284         stateFlags = SDL_ANDROID_EXTERNAL_STORAGE_READ |
  2285                      SDL_ANDROID_EXTERNAL_STORAGE_WRITE;
  2286     } else if (SDL_strcmp(state, "mounted_ro") == 0) {
  2287         stateFlags = SDL_ANDROID_EXTERNAL_STORAGE_READ;
  2288     } else {
  2289         stateFlags = 0;
  2290     }
  2291     (*env)->ReleaseStringUTFChars(env, stateString, state);
  2292 
  2293     LocalReferenceHolder_Cleanup(&refs);
  2294     return stateFlags;
  2295 }
  2296 
  2297 const char * SDL_AndroidGetExternalStoragePath(void)
  2298 {
  2299     static char *s_AndroidExternalFilesPath = NULL;
  2300 
  2301     if (!s_AndroidExternalFilesPath) {
  2302         struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
  2303         jmethodID mid;
  2304         jobject context;
  2305         jobject fileObject;
  2306         jstring pathString;
  2307         const char *path;
  2308 
  2309         JNIEnv *env = Android_JNI_GetEnv();
  2310         if (!LocalReferenceHolder_Init(&refs, env)) {
  2311             LocalReferenceHolder_Cleanup(&refs);
  2312             return NULL;
  2313         }
  2314 
  2315         /* context = SDLActivity.getContext(); */
  2316         context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
  2317 
  2318         /* fileObj = context.getExternalFilesDir(); */
  2319         mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
  2320                 "getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;");
  2321         fileObject = (*env)->CallObjectMethod(env, context, mid, NULL);
  2322         if (!fileObject) {
  2323             SDL_SetError("Couldn't get external directory");
  2324             LocalReferenceHolder_Cleanup(&refs);
  2325             return NULL;
  2326         }
  2327 
  2328         /* path = fileObject.getAbsolutePath(); */
  2329         mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, fileObject),
  2330                 "getAbsolutePath", "()Ljava/lang/String;");
  2331         pathString = (jstring)(*env)->CallObjectMethod(env, fileObject, mid);
  2332 
  2333         path = (*env)->GetStringUTFChars(env, pathString, NULL);
  2334         s_AndroidExternalFilesPath = SDL_strdup(path);
  2335         (*env)->ReleaseStringUTFChars(env, pathString, path);
  2336 
  2337         LocalReferenceHolder_Cleanup(&refs);
  2338     }
  2339     return s_AndroidExternalFilesPath;
  2340 }
  2341 
  2342 void Android_JNI_GetManifestEnvironmentVariables(void)
  2343 {
  2344     if (!mActivityClass || !midGetManifestEnvironmentVariables) {
  2345         __android_log_print(ANDROID_LOG_WARN, "SDL", "Request to get environment variables before JNI is ready");
  2346         return;
  2347     }
  2348 
  2349     if (!bHasEnvironmentVariables) {
  2350         JNIEnv *env = Android_JNI_GetEnv();
  2351         SDL_bool ret = (*env)->CallStaticBooleanMethod(env, mActivityClass, midGetManifestEnvironmentVariables);
  2352         if (ret) {
  2353             bHasEnvironmentVariables = SDL_TRUE;
  2354         }
  2355     }
  2356 }
  2357 
  2358 int Android_JNI_CreateCustomCursor(SDL_Surface *surface, int hot_x, int hot_y)
  2359 {
  2360     JNIEnv *mEnv = Android_JNI_GetEnv();
  2361     int custom_cursor = 0;
  2362     jintArray pixels;
  2363     pixels = (*mEnv)->NewIntArray(mEnv, surface->w * surface->h);
  2364     if (pixels) {
  2365         (*mEnv)->SetIntArrayRegion(mEnv, pixels, 0, surface->w * surface->h, (int *)surface->pixels);
  2366         custom_cursor = (*mEnv)->CallStaticIntMethod(mEnv, mActivityClass, midCreateCustomCursor, pixels, surface->w, surface->h, hot_x, hot_y);
  2367         (*mEnv)->DeleteLocalRef(mEnv, pixels);
  2368     } else {
  2369         SDL_OutOfMemory();
  2370     }
  2371     return custom_cursor;
  2372 }
  2373 
  2374 
  2375 SDL_bool Android_JNI_SetCustomCursor(int cursorID)
  2376 {
  2377     JNIEnv *mEnv = Android_JNI_GetEnv();
  2378     return (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, midSetCustomCursor, cursorID);
  2379 }
  2380 
  2381 SDL_bool Android_JNI_SetSystemCursor(int cursorID)
  2382 {
  2383     JNIEnv *mEnv = Android_JNI_GetEnv();
  2384     return (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, midSetSystemCursor, cursorID);
  2385 }
  2386 
  2387 SDL_bool Android_JNI_SupportsRelativeMouse()
  2388 {
  2389     JNIEnv *mEnv = Android_JNI_GetEnv();
  2390     return (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, midSupportsRelativeMouse);
  2391 }
  2392 
  2393 SDL_bool Android_JNI_SetRelativeMouseEnabled(SDL_bool enabled)
  2394 {
  2395     JNIEnv *mEnv = Android_JNI_GetEnv();
  2396     return (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, midSetRelativeMouseEnabled, (enabled == 1));
  2397 }
  2398 
  2399 
  2400 #endif /* __ANDROID__ */
  2401 
  2402 /* vi: set ts=4 sw=4 expandtab: */