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