src/core/android/SDL_android.c
author Sam Lantinga
Fri, 05 Apr 2019 08:15:01 -0700
changeset 12696 3ef8a628853d
parent 12689 d581caf613cd
child 12709 d268ce129edb
permissions -rw-r--r--
Fixed bug 4579 - SDL_android.c s_active not being atomic

Isaias Brunet

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