src/core/android/SDL_android.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 25 Sep 2018 20:08:51 -0700
changeset 12224 0582b4e1fa9f
parent 12201 8bdc4d340419
child 12242 df7260f149f2
permissions -rw-r--r--
Fixed bug 4268 - Android_JNI_OpenAudioDevice function has error

alittle

where iscapture == 1

1.param set error

(*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureOpen, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames)

param:audioBuffer16Bit need change to captureBuffer16Bit

2.logic error

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