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