From a0b0b65f0009c6899eb6237e507f0de996c1cbf1 Mon Sep 17 00:00:00 2001 From: Eric Wing Date: Mon, 22 Jul 2013 02:51:45 -0700 Subject: [PATCH] Android: Removed all unnecessary dependencies on C++. C++ is a bit of a minefield on Android. Much functionality still doesn't work, and Android can't decide on which C++ standard library to use, so it provides 3 different ones, all of which are incompatible with each other. (It looks like clang is coming too which will add a new compiler and a 4th standard library.) As middleware, SDL might be distributed as a binary and intermixed with other projects already using C++. If C++ is intermixed in a bad way, bad things will happen. Removing dependencies on C++ will avoid this problem and downstream users won't have to worry/care. --- Android.mk | 2 +- README-android.txt | 4 +- android-project/jni/src/Android.mk | 2 +- .../{SDL_android.cpp => SDL_android.c} | 604 ++++++++++-------- ...DL_android_main.cpp => SDL_android_main.c} | 4 +- 5 files changed, 337 insertions(+), 279 deletions(-) rename src/core/android/{SDL_android.cpp => SDL_android.c} (59%) rename src/main/android/{SDL_android_main.cpp => SDL_android_main.c} (85%) diff --git a/Android.mk b/Android.mk index 8fb4cfc7d..3230f9fea 100755 --- a/Android.mk +++ b/Android.mk @@ -22,7 +22,7 @@ LOCAL_SRC_FILES := \ $(wildcard $(LOCAL_PATH)/src/audio/dummy/*.c) \ $(LOCAL_PATH)/src/atomic/SDL_atomic.c \ $(LOCAL_PATH)/src/atomic/SDL_spinlock.c.arm \ - $(wildcard $(LOCAL_PATH)/src/core/android/*.cpp) \ + $(wildcard $(LOCAL_PATH)/src/core/android/*.c) \ $(wildcard $(LOCAL_PATH)/src/cpuinfo/*.c) \ $(wildcard $(LOCAL_PATH)/src/events/*.c) \ $(wildcard $(LOCAL_PATH)/src/file/*.c) \ diff --git a/README-android.txt b/README-android.txt index fa0f470b9..ecd31ae1b 100644 --- a/README-android.txt +++ b/README-android.txt @@ -28,10 +28,10 @@ android-project/src/org/libsdl/app/SDLActivity.java The Java code loads your game code, the SDL shared library, and dispatches to native functions implemented in the SDL library: -src/SDL_android.cpp +src/SDL_android.c Your project must include some glue code that starts your main() routine: -src/main/android/SDL_android_main.cpp +src/main/android/SDL_android_main.c ================================================================================ diff --git a/android-project/jni/src/Android.mk b/android-project/jni/src/Android.mk index 948be96e5..70ca7dc35 100644 --- a/android-project/jni/src/Android.mk +++ b/android-project/jni/src/Android.mk @@ -9,7 +9,7 @@ SDL_PATH := ../SDL LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(SDL_PATH)/include # Add your application source files here... -LOCAL_SRC_FILES := $(SDL_PATH)/src/main/android/SDL_android_main.cpp \ +LOCAL_SRC_FILES := $(SDL_PATH)/src/main/android/SDL_android_main.c \ YourSourceHere.c LOCAL_SHARED_LIBRARIES := SDL2 diff --git a/src/core/android/SDL_android.cpp b/src/core/android/SDL_android.c similarity index 59% rename from src/core/android/SDL_android.cpp rename to src/core/android/SDL_android.c index 9023df51a..74a41607a 100644 --- a/src/core/android/SDL_android.cpp +++ b/src/core/android/SDL_android.c @@ -29,7 +29,6 @@ #include "SDL_android.h" #include -extern "C" { #include "../../events/SDL_events_c.h" #include "../../video/android/SDL_androidkeyboard.h" #include "../../video/android/SDL_androidtouch.h" @@ -52,13 +51,13 @@ extern "C" { extern void Android_RunAudioThread(); static void Android_JNI_ThreadDestroyed(void*); -} // C /******************************************************************************* This file links the Java side of Android with libsdl *******************************************************************************/ #include #include +#include /******************************************************************************* @@ -87,12 +86,12 @@ static bool bHasNewData; *******************************************************************************/ // Library init -extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) +jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv *env; mJavaVM = vm; LOGI("JNI_OnLoad called"); - if (mJavaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { + if ((*mJavaVM)->GetEnv(mJavaVM, (void**) &env, JNI_VERSION_1_4) != JNI_OK) { LOGE("Failed to get the environment using GetEnv()"); return -1; } @@ -111,25 +110,25 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) } // Called before SDL_main() to initialize JNI bindings -extern "C" void SDL_Android_Init(JNIEnv* mEnv, jclass cls) +void SDL_Android_Init(JNIEnv* mEnv, jclass cls) { __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL_Android_Init()"); Android_JNI_SetupThread(); - mActivityClass = (jclass)mEnv->NewGlobalRef(cls); + mActivityClass = (jclass)((*mEnv)->NewGlobalRef(mEnv, cls)); - midCreateGLContext = mEnv->GetStaticMethodID(mActivityClass, + midCreateGLContext = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "createGLContext","(II[I)Z"); - midFlipBuffers = mEnv->GetStaticMethodID(mActivityClass, + midFlipBuffers = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "flipBuffers","()V"); - midAudioInit = mEnv->GetStaticMethodID(mActivityClass, + midAudioInit = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "audioInit", "(IZZI)V"); - midAudioWriteShortBuffer = mEnv->GetStaticMethodID(mActivityClass, + midAudioWriteShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "audioWriteShortBuffer", "([S)V"); - midAudioWriteByteBuffer = mEnv->GetStaticMethodID(mActivityClass, + midAudioWriteByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "audioWriteByteBuffer", "([B)V"); - midAudioQuit = mEnv->GetStaticMethodID(mActivityClass, + midAudioQuit = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "audioQuit", "()V"); bHasNewData = false; @@ -142,7 +141,7 @@ extern "C" void SDL_Android_Init(JNIEnv* mEnv, jclass cls) } // Resize -extern "C" void Java_org_libsdl_app_SDLActivity_onNativeResize( +void Java_org_libsdl_app_SDLActivity_onNativeResize( JNIEnv* env, jclass jcls, jint width, jint height, jint format) { @@ -150,21 +149,21 @@ extern "C" void Java_org_libsdl_app_SDLActivity_onNativeResize( } // Keydown -extern "C" void Java_org_libsdl_app_SDLActivity_onNativeKeyDown( +void Java_org_libsdl_app_SDLActivity_onNativeKeyDown( JNIEnv* env, jclass jcls, jint keycode) { Android_OnKeyDown(keycode); } // Keyup -extern "C" void Java_org_libsdl_app_SDLActivity_onNativeKeyUp( +void Java_org_libsdl_app_SDLActivity_onNativeKeyUp( JNIEnv* env, jclass jcls, jint keycode) { Android_OnKeyUp(keycode); } // Touch -extern "C" void Java_org_libsdl_app_SDLActivity_onNativeTouch( +void Java_org_libsdl_app_SDLActivity_onNativeTouch( JNIEnv* env, jclass jcls, jint touch_device_id_in, jint pointer_finger_id_in, jint action, jfloat x, jfloat y, jfloat p) @@ -173,7 +172,7 @@ extern "C" void Java_org_libsdl_app_SDLActivity_onNativeTouch( } // Accelerometer -extern "C" void Java_org_libsdl_app_SDLActivity_onNativeAccel( +void Java_org_libsdl_app_SDLActivity_onNativeAccel( JNIEnv* env, jclass jcls, jfloat x, jfloat y, jfloat z) { @@ -184,14 +183,14 @@ extern "C" void Java_org_libsdl_app_SDLActivity_onNativeAccel( } // Low memory -extern "C" void Java_org_libsdl_app_SDLActivity_nativeLowMemory( +void Java_org_libsdl_app_SDLActivity_nativeLowMemory( JNIEnv* env, jclass cls) { SDL_SendAppEvent(SDL_APP_LOWMEMORY); } // Quit -extern "C" void Java_org_libsdl_app_SDLActivity_nativeQuit( +void Java_org_libsdl_app_SDLActivity_nativeQuit( JNIEnv* env, jclass cls) { // Inject a SDL_QUIT event @@ -200,7 +199,7 @@ extern "C" void Java_org_libsdl_app_SDLActivity_nativeQuit( } // Pause -extern "C" void Java_org_libsdl_app_SDLActivity_nativePause( +void Java_org_libsdl_app_SDLActivity_nativePause( JNIEnv* env, jclass cls) { if (Android_Window) { @@ -216,7 +215,7 @@ extern "C" void Java_org_libsdl_app_SDLActivity_nativePause( } // Resume -extern "C" void Java_org_libsdl_app_SDLActivity_nativeResume( +void Java_org_libsdl_app_SDLActivity_nativeResume( JNIEnv* env, jclass cls) { __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeResume()"); @@ -234,7 +233,7 @@ extern "C" void Java_org_libsdl_app_SDLActivity_nativeResume( } } -extern "C" void Java_org_libsdl_app_SDLActivity_nativeRunAudioThread( +void Java_org_libsdl_app_SDLActivity_nativeRunAudioThread( JNIEnv* env, jclass cls) { /* This is the audio thread, with a different environment */ @@ -243,26 +242,26 @@ extern "C" void Java_org_libsdl_app_SDLActivity_nativeRunAudioThread( Android_RunAudioThread(); } -extern "C" void Java_org_libsdl_app_SDLInputConnection_nativeCommitText( +void Java_org_libsdl_app_SDLInputConnection_nativeCommitText( JNIEnv* env, jclass cls, jstring text, jint newCursorPosition) { - const char *utftext = env->GetStringUTFChars(text, NULL); + const char *utftext = (*env)->GetStringUTFChars(env, text, NULL); SDL_SendKeyboardText(utftext); - env->ReleaseStringUTFChars(text, utftext); + (*env)->ReleaseStringUTFChars(env, text, utftext); } -extern "C" void Java_org_libsdl_app_SDLInputConnection_nativeSetComposingText( +void Java_org_libsdl_app_SDLInputConnection_nativeSetComposingText( JNIEnv* env, jclass cls, jstring text, jint newCursorPosition) { - const char *utftext = env->GetStringUTFChars(text, NULL); + const char *utftext = (*env)->GetStringUTFChars(env, text, NULL); SDL_SendEditingText(utftext, 0, 0); - env->ReleaseStringUTFChars(text, utftext); + (*env)->ReleaseStringUTFChars(env, text, utftext); } @@ -271,49 +270,55 @@ extern "C" void Java_org_libsdl_app_SDLInputConnection_nativeSetComposingText( Functions called by SDL into Java *******************************************************************************/ -class LocalReferenceHolder +static int s_active = 0; +struct LocalReferenceHolder { -private: - static int s_active; - -public: - static bool IsActive() { - return s_active > 0; - } + JNIEnv *m_env; + const char *m_func; +}; -public: - LocalReferenceHolder(const char *func) : m_env(NULL), m_func(func) { +static struct LocalReferenceHolder LocalReferenceHolder_Setup(const char *func) +{ + struct LocalReferenceHolder refholder; + refholder.m_env = NULL; + refholder.m_func = func; #ifdef DEBUG_JNI - SDL_Log("Entering function %s", m_func); + SDL_Log("Entering function %s", func); #endif + return refholder; +} + +static SDL_bool LocalReferenceHolder_Init(struct LocalReferenceHolder *refholder, JNIEnv *env) +{ + const int capacity = 16; + if ((*env)->PushLocalFrame(env, capacity) < 0) { + SDL_SetError("Failed to allocate enough JVM local references"); + return false; } - ~LocalReferenceHolder() { + ++s_active; + refholder->m_env = env; + return SDL_TRUE; +} + +static void LocalReferenceHolder_Cleanup(struct LocalReferenceHolder *refholder) +{ #ifdef DEBUG_JNI - SDL_Log("Leaving function %s", m_func); + SDL_Log("Leaving function %s", refholder->m_func); #endif - if (m_env) { - m_env->PopLocalFrame(NULL); - --s_active; - } + if (refholder->m_env) { + JNIEnv* env = refholder->m_env; + (*env)->PopLocalFrame(env, NULL); + --s_active; } +} - bool init(JNIEnv *env, jint capacity = 16) { - if (env->PushLocalFrame(capacity) < 0) { - SDL_SetError("Failed to allocate enough JVM local references"); - return false; - } - ++s_active; - m_env = env; - return true; - } +static SDL_bool LocalReferenceHolder_IsActive() +{ + return s_active > 0; +} -protected: - JNIEnv *m_env; - const char *m_func; -}; -int LocalReferenceHolder::s_active; -extern "C" SDL_bool Android_JNI_CreateContext(int majorVersion, int minorVersion, +SDL_bool Android_JNI_CreateContext(int majorVersion, int minorVersion, int red, int green, int blue, int alpha, int buffer, int depth, int stencil, int buffers, int samples) @@ -337,35 +342,35 @@ extern "C" SDL_bool Android_JNI_CreateContext(int majorVersion, int minorVersion jintArray array; - array = env->NewIntArray(len); - env->SetIntArrayRegion(array, 0, len, attribs); + array = (*env)->NewIntArray(env, len); + (*env)->SetIntArrayRegion(env, array, 0, len, attribs); - jboolean success = env->CallStaticBooleanMethod(mActivityClass, midCreateGLContext, majorVersion, minorVersion, array); + jboolean success = (*env)->CallStaticBooleanMethod(env, mActivityClass, midCreateGLContext, majorVersion, minorVersion, array); - env->DeleteLocalRef(array); + (*env)->DeleteLocalRef(env, array); return success ? SDL_TRUE : SDL_FALSE; } -extern "C" void Android_JNI_SwapWindow() +void Android_JNI_SwapWindow() { JNIEnv *mEnv = Android_JNI_GetEnv(); - mEnv->CallStaticVoidMethod(mActivityClass, midFlipBuffers); + (*mEnv)->CallStaticVoidMethod(mEnv, mActivityClass, midFlipBuffers); } -extern "C" void Android_JNI_SetActivityTitle(const char *title) +void Android_JNI_SetActivityTitle(const char *title) { jmethodID mid; JNIEnv *mEnv = Android_JNI_GetEnv(); - mid = mEnv->GetStaticMethodID(mActivityClass,"setActivityTitle","(Ljava/lang/String;)Z"); + mid = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,"setActivityTitle","(Ljava/lang/String;)Z"); if (mid) { - jstring jtitle = reinterpret_cast(mEnv->NewStringUTF(title)); - mEnv->CallStaticBooleanMethod(mActivityClass, mid, jtitle); - mEnv->DeleteLocalRef(jtitle); + jstring jtitle = (jstring)((*mEnv)->NewStringUTF(mEnv, title)); + (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, mid, jtitle); + (*mEnv)->DeleteLocalRef(mEnv, jtitle); } } -extern "C" SDL_bool Android_JNI_GetAccelerometerValues(float values[3]) +SDL_bool Android_JNI_GetAccelerometerValues(float values[3]) { int i; SDL_bool retval = SDL_FALSE; @@ -385,7 +390,7 @@ static void Android_JNI_ThreadDestroyed(void* value) { /* The thread is being destroyed, detach it from the Java VM and set the mThreadKey value to NULL as required */ JNIEnv *env = (JNIEnv*) value; if (env != NULL) { - mJavaVM->DetachCurrentThread(); + (*mJavaVM)->DetachCurrentThread(mJavaVM); pthread_setspecific(mThreadKey, NULL); } } @@ -404,7 +409,7 @@ JNIEnv* Android_JNI_GetEnv(void) { */ JNIEnv *env; - int status = mJavaVM->AttachCurrentThread(&env, NULL); + int status = (*mJavaVM)->AttachCurrentThread(mJavaVM, &env, NULL); if(status < 0) { LOGE("failed to attach current thread"); return 0; @@ -436,7 +441,7 @@ static jboolean audioBufferStereo = JNI_FALSE; static jobject audioBuffer = NULL; static void* audioBufferPinned = NULL; -extern "C" int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames) +int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames) { int audioBufferFrames; @@ -451,23 +456,23 @@ extern "C" int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int chan audioBuffer16Bit = is16Bit; audioBufferStereo = channelCount > 1; - env->CallStaticVoidMethod(mActivityClass, midAudioInit, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames); + (*env)->CallStaticVoidMethod(env, mActivityClass, midAudioInit, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames); /* Allocating the audio buffer from the Java side and passing it as the return value for audioInit no longer works on * Android >= 4.2 due to a "stale global reference" error. So now we allocate this buffer directly from this side. */ if (is16Bit) { - jshortArray audioBufferLocal = env->NewShortArray(desiredBufferFrames * (audioBufferStereo ? 2 : 1)); + jshortArray audioBufferLocal = (*env)->NewShortArray(env, desiredBufferFrames * (audioBufferStereo ? 2 : 1)); if (audioBufferLocal) { - audioBuffer = env->NewGlobalRef(audioBufferLocal); - env->DeleteLocalRef(audioBufferLocal); + audioBuffer = (*env)->NewGlobalRef(env, audioBufferLocal); + (*env)->DeleteLocalRef(env, audioBufferLocal); } } else { - jbyteArray audioBufferLocal = env->NewByteArray(desiredBufferFrames * (audioBufferStereo ? 2 : 1)); + jbyteArray audioBufferLocal = (*env)->NewByteArray(env, desiredBufferFrames * (audioBufferStereo ? 2 : 1)); if (audioBufferLocal) { - audioBuffer = env->NewGlobalRef(audioBufferLocal); - env->DeleteLocalRef(audioBufferLocal); + audioBuffer = (*env)->NewGlobalRef(env, audioBufferLocal); + (*env)->DeleteLocalRef(env, audioBufferLocal); } } @@ -478,11 +483,11 @@ extern "C" int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int chan jboolean isCopy = JNI_FALSE; if (audioBuffer16Bit) { - audioBufferPinned = env->GetShortArrayElements((jshortArray)audioBuffer, &isCopy); - audioBufferFrames = env->GetArrayLength((jshortArray)audioBuffer); + audioBufferPinned = (*env)->GetShortArrayElements(env, (jshortArray)audioBuffer, &isCopy); + audioBufferFrames = (*env)->GetArrayLength(env, (jshortArray)audioBuffer); } else { - audioBufferPinned = env->GetByteArrayElements((jbyteArray)audioBuffer, &isCopy); - audioBufferFrames = env->GetArrayLength((jbyteArray)audioBuffer); + audioBufferPinned = (*env)->GetByteArrayElements(env, (jbyteArray)audioBuffer, &isCopy); + audioBufferFrames = (*env)->GetArrayLength(env, (jbyteArray)audioBuffer); } if (audioBufferStereo) { audioBufferFrames /= 2; @@ -491,34 +496,34 @@ extern "C" int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int chan return audioBufferFrames; } -extern "C" void * Android_JNI_GetAudioBuffer() +void * Android_JNI_GetAudioBuffer() { return audioBufferPinned; } -extern "C" void Android_JNI_WriteAudioBuffer() +void Android_JNI_WriteAudioBuffer() { JNIEnv *mAudioEnv = Android_JNI_GetEnv(); if (audioBuffer16Bit) { - mAudioEnv->ReleaseShortArrayElements((jshortArray)audioBuffer, (jshort *)audioBufferPinned, JNI_COMMIT); - mAudioEnv->CallStaticVoidMethod(mActivityClass, midAudioWriteShortBuffer, (jshortArray)audioBuffer); + (*mAudioEnv)->ReleaseShortArrayElements(mAudioEnv, (jshortArray)audioBuffer, (jshort *)audioBufferPinned, JNI_COMMIT); + (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mActivityClass, midAudioWriteShortBuffer, (jshortArray)audioBuffer); } else { - mAudioEnv->ReleaseByteArrayElements((jbyteArray)audioBuffer, (jbyte *)audioBufferPinned, JNI_COMMIT); - mAudioEnv->CallStaticVoidMethod(mActivityClass, midAudioWriteByteBuffer, (jbyteArray)audioBuffer); + (*mAudioEnv)->ReleaseByteArrayElements(mAudioEnv, (jbyteArray)audioBuffer, (jbyte *)audioBufferPinned, JNI_COMMIT); + (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mActivityClass, midAudioWriteByteBuffer, (jbyteArray)audioBuffer); } /* JNI_COMMIT means the changes are committed to the VM but the buffer remains pinned */ } -extern "C" void Android_JNI_CloseAudioDevice() +void Android_JNI_CloseAudioDevice() { JNIEnv *env = Android_JNI_GetEnv(); - env->CallStaticVoidMethod(mActivityClass, midAudioQuit); + (*env)->CallStaticVoidMethod(env, mActivityClass, midAudioQuit); if (audioBuffer) { - env->DeleteGlobalRef(audioBuffer); + (*env)->DeleteGlobalRef(env, audioBuffer); audioBuffer = NULL; audioBufferPinned = NULL; } @@ -526,38 +531,38 @@ extern "C" void Android_JNI_CloseAudioDevice() // Test for an exception and call SDL_SetError with its detail if one occurs // If optional parameter silent is truthy then SDL_SetError() is not called. -static bool Android_JNI_ExceptionOccurred(bool silent = false) +static bool Android_JNI_ExceptionOccurred(bool silent) { - SDL_assert(LocalReferenceHolder::IsActive()); + SDL_assert(LocalReferenceHolder_IsActive()); JNIEnv *mEnv = Android_JNI_GetEnv(); - jthrowable exception = mEnv->ExceptionOccurred(); + jthrowable exception = (*mEnv)->ExceptionOccurred(mEnv); if (exception != NULL) { jmethodID mid; // Until this happens most JNI operations have undefined behaviour - mEnv->ExceptionClear(); + (*mEnv)->ExceptionClear(mEnv); if (!silent) { - jclass exceptionClass = mEnv->GetObjectClass(exception); - jclass classClass = mEnv->FindClass("java/lang/Class"); + jclass exceptionClass = (*mEnv)->GetObjectClass(mEnv, exception); + jclass classClass = (*mEnv)->FindClass(mEnv, "java/lang/Class"); - mid = mEnv->GetMethodID(classClass, "getName", "()Ljava/lang/String;"); - jstring exceptionName = (jstring)mEnv->CallObjectMethod(exceptionClass, mid); - const char* exceptionNameUTF8 = mEnv->GetStringUTFChars(exceptionName, 0); + mid = (*mEnv)->GetMethodID(mEnv, classClass, "getName", "()Ljava/lang/String;"); + jstring exceptionName = (jstring)(*mEnv)->CallObjectMethod(mEnv, exceptionClass, mid); + const char* exceptionNameUTF8 = (*mEnv)->GetStringUTFChars(mEnv, exceptionName, 0); - mid = mEnv->GetMethodID(exceptionClass, "getMessage", "()Ljava/lang/String;"); - jstring exceptionMessage = (jstring)mEnv->CallObjectMethod(exception, mid); + mid = (*mEnv)->GetMethodID(mEnv, exceptionClass, "getMessage", "()Ljava/lang/String;"); + jstring exceptionMessage = (jstring)(*mEnv)->CallObjectMethod(mEnv, exception, mid); if (exceptionMessage != NULL) { - const char* exceptionMessageUTF8 = mEnv->GetStringUTFChars(exceptionMessage, 0); + const char* exceptionMessageUTF8 = (*mEnv)->GetStringUTFChars(mEnv, exceptionMessage, 0); SDL_SetError("%s: %s", exceptionNameUTF8, exceptionMessageUTF8); - mEnv->ReleaseStringUTFChars(exceptionMessage, exceptionMessageUTF8); + (*mEnv)->ReleaseStringUTFChars(mEnv, exceptionMessage, exceptionMessageUTF8); } else { SDL_SetError("%s", exceptionNameUTF8); } - mEnv->ReleaseStringUTFChars(exceptionName, exceptionNameUTF8); + (*mEnv)->ReleaseStringUTFChars(mEnv, exceptionName, exceptionNameUTF8); } return true; @@ -566,9 +571,10 @@ static bool Android_JNI_ExceptionOccurred(bool silent = false) return false; } -static int Android_JNI_FileOpen(SDL_RWops* ctx) +static int Internal_Android_JNI_FileOpen(SDL_RWops* ctx) { - LocalReferenceHolder refs(__FUNCTION__); + struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); + int result = 0; jmethodID mid; @@ -583,7 +589,7 @@ static int Android_JNI_FileOpen(SDL_RWops* ctx) jfieldID descriptor; JNIEnv *mEnv = Android_JNI_GetEnv(); - if (!refs.init(mEnv)) { + if (!LocalReferenceHolder_Init(&refs, mEnv)) { goto failure; } @@ -591,43 +597,43 @@ static int Android_JNI_FileOpen(SDL_RWops* ctx) ctx->hidden.androidio.position = 0; // context = SDLActivity.getContext(); - mid = mEnv->GetStaticMethodID(mActivityClass, + mid = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "getContext","()Landroid/content/Context;"); - context = mEnv->CallStaticObjectMethod(mActivityClass, mid); + context = (*mEnv)->CallStaticObjectMethod(mEnv, mActivityClass, mid); // assetManager = context.getAssets(); - mid = mEnv->GetMethodID(mEnv->GetObjectClass(context), + mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, context), "getAssets", "()Landroid/content/res/AssetManager;"); - assetManager = mEnv->CallObjectMethod(context, mid); + assetManager = (*mEnv)->CallObjectMethod(mEnv, context, mid); /* First let's try opening the file to obtain an AssetFileDescriptor. * This method reads the files directly from the APKs using standard *nix calls */ - mid = mEnv->GetMethodID(mEnv->GetObjectClass(assetManager), "openFd", "(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;"); - inputStream = mEnv->CallObjectMethod(assetManager, mid, fileNameJString); + mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, assetManager), "openFd", "(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;"); + inputStream = (*mEnv)->CallObjectMethod(mEnv, assetManager, mid, fileNameJString); if (Android_JNI_ExceptionOccurred(true)) { goto fallback; } - mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream), "getStartOffset", "()J"); - ctx->hidden.androidio.offset = mEnv->CallLongMethod(inputStream, mid); + mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream), "getStartOffset", "()J"); + ctx->hidden.androidio.offset = (*mEnv)->CallLongMethod(mEnv, inputStream, mid); if (Android_JNI_ExceptionOccurred(true)) { goto fallback; } - mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream), "getDeclaredLength", "()J"); - ctx->hidden.androidio.size = mEnv->CallLongMethod(inputStream, mid); + mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream), "getDeclaredLength", "()J"); + ctx->hidden.androidio.size = (*mEnv)->CallLongMethod(mEnv, inputStream, mid); if (Android_JNI_ExceptionOccurred(true)) { goto fallback; } - mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream), "getFileDescriptor", "()Ljava/io/FileDescriptor;"); - fd = mEnv->CallObjectMethod(inputStream, mid); - fdCls = mEnv->GetObjectClass(fd); - descriptor = mEnv->GetFieldID(fdCls, "descriptor", "I"); - ctx->hidden.androidio.fd = mEnv->GetIntField(fd, descriptor); - ctx->hidden.androidio.assetFileDescriptorRef = mEnv->NewGlobalRef(inputStream); + mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream), "getFileDescriptor", "()Ljava/io/FileDescriptor;"); + fd = (*mEnv)->CallObjectMethod(mEnv, inputStream, mid); + fdCls = (*mEnv)->GetObjectClass(mEnv, fd); + descriptor = (*mEnv)->GetFieldID(mEnv, fdCls, "descriptor", "I"); + ctx->hidden.androidio.fd = (*mEnv)->GetIntField(mEnv, fd, descriptor); + ctx->hidden.androidio.assetFileDescriptorRef = (*mEnv)->NewGlobalRef(mEnv, inputStream); // Seek to the correct offset in the file. lseek(ctx->hidden.androidio.fd, (off_t)ctx->hidden.androidio.offset, SEEK_SET); @@ -641,14 +647,14 @@ static int Android_JNI_FileOpen(SDL_RWops* ctx) ctx->hidden.androidio.assetFileDescriptorRef = NULL; // inputStream = assetManager.open(); - mid = mEnv->GetMethodID(mEnv->GetObjectClass(assetManager), + mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, assetManager), "open", "(Ljava/lang/String;I)Ljava/io/InputStream;"); - inputStream = mEnv->CallObjectMethod(assetManager, mid, fileNameJString, 1 /*ACCESS_RANDOM*/); - if (Android_JNI_ExceptionOccurred()) { + inputStream = (*mEnv)->CallObjectMethod(mEnv, assetManager, mid, fileNameJString, 1 /*ACCESS_RANDOM*/); + if (Android_JNI_ExceptionOccurred(false)) { goto failure; } - ctx->hidden.androidio.inputStreamRef = mEnv->NewGlobalRef(inputStream); + ctx->hidden.androidio.inputStreamRef = (*mEnv)->NewGlobalRef(mEnv, inputStream); // Despite all the visible documentation on [Asset]InputStream claiming // that the .available() method is not guaranteed to return the entire file @@ -657,29 +663,29 @@ static int Android_JNI_FileOpen(SDL_RWops* ctx) // AssetInputStream.available() /will/ always return the total file size // size = inputStream.available(); - mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream), + mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream), "available", "()I"); - ctx->hidden.androidio.size = (long)mEnv->CallIntMethod(inputStream, mid); - if (Android_JNI_ExceptionOccurred()) { + ctx->hidden.androidio.size = (long)(*mEnv)->CallIntMethod(mEnv, inputStream, mid); + if (Android_JNI_ExceptionOccurred(false)) { goto failure; } // readableByteChannel = Channels.newChannel(inputStream); - channels = mEnv->FindClass("java/nio/channels/Channels"); - mid = mEnv->GetStaticMethodID(channels, + channels = (*mEnv)->FindClass(mEnv, "java/nio/channels/Channels"); + mid = (*mEnv)->GetStaticMethodID(mEnv, channels, "newChannel", "(Ljava/io/InputStream;)Ljava/nio/channels/ReadableByteChannel;"); - readableByteChannel = mEnv->CallStaticObjectMethod( - channels, mid, inputStream); - if (Android_JNI_ExceptionOccurred()) { + readableByteChannel = (*mEnv)->CallStaticObjectMethod( + mEnv, channels, mid, inputStream); + if (Android_JNI_ExceptionOccurred(false)) { goto failure; } ctx->hidden.androidio.readableByteChannelRef = - mEnv->NewGlobalRef(readableByteChannel); + (*mEnv)->NewGlobalRef(mEnv, readableByteChannel); // Store .read id for reading purposes - mid = mEnv->GetMethodID(mEnv->GetObjectClass(readableByteChannel), + mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, readableByteChannel), "read", "(Ljava/nio/ByteBuffer;)I"); ctx->hidden.androidio.readMethod = mid; } @@ -688,53 +694,59 @@ static int Android_JNI_FileOpen(SDL_RWops* ctx) failure: result = -1; - mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.fileNameRef); + (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.fileNameRef); if(ctx->hidden.androidio.inputStreamRef != NULL) { - mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.inputStreamRef); + (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.inputStreamRef); } if(ctx->hidden.androidio.readableByteChannelRef != NULL) { - mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.readableByteChannelRef); + (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.readableByteChannelRef); } if(ctx->hidden.androidio.assetFileDescriptorRef != NULL) { - mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.assetFileDescriptorRef); + (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.assetFileDescriptorRef); } } - + + LocalReferenceHolder_Cleanup(&refs); return result; } -extern "C" int Android_JNI_FileOpen(SDL_RWops* ctx, - const char* fileName, const char*) +int Android_JNI_FileOpen(SDL_RWops* ctx, + const char* fileName, const char* mode) { - LocalReferenceHolder refs(__FUNCTION__); + struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); JNIEnv *mEnv = Android_JNI_GetEnv(); + int retval; - if (!refs.init(mEnv)) { + if (!LocalReferenceHolder_Init(&refs, mEnv)) { + LocalReferenceHolder_Cleanup(&refs); return -1; } if (!ctx) { + LocalReferenceHolder_Cleanup(&refs); return -1; } - jstring fileNameJString = mEnv->NewStringUTF(fileName); - ctx->hidden.androidio.fileNameRef = mEnv->NewGlobalRef(fileNameJString); + jstring fileNameJString = (*mEnv)->NewStringUTF(mEnv, fileName); + ctx->hidden.androidio.fileNameRef = (*mEnv)->NewGlobalRef(mEnv, fileNameJString); ctx->hidden.androidio.inputStreamRef = NULL; ctx->hidden.androidio.readableByteChannelRef = NULL; ctx->hidden.androidio.readMethod = NULL; ctx->hidden.androidio.assetFileDescriptorRef = NULL; - return Android_JNI_FileOpen(ctx); + retval = Internal_Android_JNI_FileOpen(ctx); + LocalReferenceHolder_Cleanup(&refs); + return retval; } -extern "C" size_t Android_JNI_FileRead(SDL_RWops* ctx, void* buffer, +size_t Android_JNI_FileRead(SDL_RWops* ctx, void* buffer, size_t size, size_t maxnum) { - LocalReferenceHolder refs(__FUNCTION__); + struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); if (ctx->hidden.androidio.assetFileDescriptorRef) { size_t bytesMax = size * maxnum; @@ -744,8 +756,10 @@ extern "C" size_t Android_JNI_FileRead(SDL_RWops* ctx, void* buffer, size_t result = read(ctx->hidden.androidio.fd, buffer, bytesMax ); if (result > 0) { ctx->hidden.androidio.position += result; + LocalReferenceHolder_Cleanup(&refs); return result / size; } + LocalReferenceHolder_Cleanup(&refs); return 0; } else { jlong bytesRemaining = (jlong) (size * maxnum); @@ -756,19 +770,21 @@ extern "C" size_t Android_JNI_FileRead(SDL_RWops* ctx, void* buffer, if (bytesRemaining > bytesMax) bytesRemaining = bytesMax; JNIEnv *mEnv = Android_JNI_GetEnv(); - if (!refs.init(mEnv)) { + if (!LocalReferenceHolder_Init(&refs, mEnv)) { + LocalReferenceHolder_Cleanup(&refs); return 0; } jobject readableByteChannel = (jobject)ctx->hidden.androidio.readableByteChannelRef; jmethodID readMethod = (jmethodID)ctx->hidden.androidio.readMethod; - jobject byteBuffer = mEnv->NewDirectByteBuffer(buffer, bytesRemaining); + jobject byteBuffer = (*mEnv)->NewDirectByteBuffer(mEnv, buffer, bytesRemaining); while (bytesRemaining > 0) { // result = readableByteChannel.read(...); - int result = mEnv->CallIntMethod(readableByteChannel, readMethod, byteBuffer); + int result = (*mEnv)->CallIntMethod(mEnv, readableByteChannel, readMethod, byteBuffer); - if (Android_JNI_ExceptionOccurred()) { + if (Android_JNI_ExceptionOccurred(false)) { + LocalReferenceHolder_Cleanup(&refs); return 0; } @@ -780,39 +796,43 @@ extern "C" size_t Android_JNI_FileRead(SDL_RWops* ctx, void* buffer, bytesRead += result; ctx->hidden.androidio.position += result; } + LocalReferenceHolder_Cleanup(&refs); return bytesRead / size; } + LocalReferenceHolder_Cleanup(&refs); } -extern "C" size_t Android_JNI_FileWrite(SDL_RWops* ctx, const void* buffer, +size_t Android_JNI_FileWrite(SDL_RWops* ctx, const void* buffer, size_t size, size_t num) { SDL_SetError("Cannot write to Android package filesystem"); return 0; } -static int Android_JNI_FileClose(SDL_RWops* ctx, bool release) +static int Internal_Android_JNI_FileClose(SDL_RWops* ctx, bool release) { - LocalReferenceHolder refs(__FUNCTION__); + struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); + int result = 0; JNIEnv *mEnv = Android_JNI_GetEnv(); - if (!refs.init(mEnv)) { + if (!LocalReferenceHolder_Init(&refs, mEnv)) { + LocalReferenceHolder_Cleanup(&refs); return SDL_SetError("Failed to allocate enough JVM local references"); } if (ctx) { if (release) { - mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.fileNameRef); + (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.fileNameRef); } if (ctx->hidden.androidio.assetFileDescriptorRef) { jobject inputStream = (jobject)ctx->hidden.androidio.assetFileDescriptorRef; - jmethodID mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream), + jmethodID mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream), "close", "()V"); - mEnv->CallVoidMethod(inputStream, mid); - mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.assetFileDescriptorRef); - if (Android_JNI_ExceptionOccurred()) { + (*mEnv)->CallVoidMethod(mEnv, inputStream, mid); + (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.assetFileDescriptorRef); + if (Android_JNI_ExceptionOccurred(false)) { result = -1; } } @@ -820,12 +840,12 @@ static int Android_JNI_FileClose(SDL_RWops* ctx, bool release) jobject inputStream = (jobject)ctx->hidden.androidio.inputStreamRef; // inputStream.close(); - jmethodID mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream), + jmethodID mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream), "close", "()V"); - mEnv->CallVoidMethod(inputStream, mid); - mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.inputStreamRef); - mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.readableByteChannelRef); - if (Android_JNI_ExceptionOccurred()) { + (*mEnv)->CallVoidMethod(mEnv, inputStream, mid); + (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.inputStreamRef); + (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.readableByteChannelRef); + if (Android_JNI_ExceptionOccurred(false)) { result = -1; } } @@ -835,16 +855,17 @@ static int Android_JNI_FileClose(SDL_RWops* ctx, bool release) } } + LocalReferenceHolder_Cleanup(&refs); return result; } -extern "C" Sint64 Android_JNI_FileSize(SDL_RWops* ctx) +Sint64 Android_JNI_FileSize(SDL_RWops* ctx) { return ctx->hidden.androidio.size; } -extern "C" Sint64 Android_JNI_FileSeek(SDL_RWops* ctx, Sint64 offset, int whence) +Sint64 Android_JNI_FileSeek(SDL_RWops* ctx, Sint64 offset, int whence) { if (ctx->hidden.androidio.assetFileDescriptorRef) { switch (whence) { @@ -915,8 +936,8 @@ extern "C" Sint64 Android_JNI_FileSeek(SDL_RWops* ctx, Sint64 offset, int whence } else if (movement < 0) { // We can't seek backwards so we have to reopen the file and seek // forwards which obviously isn't very efficient - Android_JNI_FileClose(ctx, false); - Android_JNI_FileOpen(ctx); + Internal_Android_JNI_FileClose(ctx, false); + Internal_Android_JNI_FileOpen(ctx); Android_JNI_FileSeek(ctx, newPosition, RW_SEEK_SET); } } @@ -925,85 +946,107 @@ extern "C" Sint64 Android_JNI_FileSeek(SDL_RWops* ctx, Sint64 offset, int whence } -extern "C" int Android_JNI_FileClose(SDL_RWops* ctx) +int Android_JNI_FileClose(SDL_RWops* ctx) { - return Android_JNI_FileClose(ctx, true); + return Internal_Android_JNI_FileClose(ctx, true); } // returns a new global reference which needs to be released later static jobject Android_JNI_GetSystemServiceObject(const char* name) { - LocalReferenceHolder refs(__FUNCTION__); + struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); JNIEnv* env = Android_JNI_GetEnv(); - if (!refs.init(env)) { + jobject retval = NULL; + + if (!LocalReferenceHolder_Init(&refs, env)) { + LocalReferenceHolder_Cleanup(&refs); return NULL; } - jstring service = env->NewStringUTF(name); + jstring service = (*env)->NewStringUTF(env, name); jmethodID mid; - mid = env->GetStaticMethodID(mActivityClass, "getContext", "()Landroid/content/Context;"); - jobject context = env->CallStaticObjectMethod(mActivityClass, mid); + mid = (*env)->GetStaticMethodID(env, mActivityClass, "getContext", "()Landroid/content/Context;"); + jobject context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid); - mid = env->GetMethodID(mActivityClass, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"); - jobject manager = env->CallObjectMethod(context, mid, service); + mid = (*env)->GetMethodID(env, mActivityClass, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"); + jobject manager = (*env)->CallObjectMethod(env, context, mid, service); - env->DeleteLocalRef(service); + (*env)->DeleteLocalRef(env, service); - return manager ? env->NewGlobalRef(manager) : NULL; + retval = manager ? (*env)->NewGlobalRef(env, manager) : NULL; + LocalReferenceHolder_Cleanup(&refs); + return retval; } #define SETUP_CLIPBOARD(error) \ - LocalReferenceHolder refs(__FUNCTION__); \ + struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); \ JNIEnv* env = Android_JNI_GetEnv(); \ - if (!refs.init(env)) { \ + if (!LocalReferenceHolder_Init(&refs, env)) { \ + LocalReferenceHolder_Cleanup(&refs); \ return error; \ } \ jobject clipboard = Android_JNI_GetSystemServiceObject("clipboard"); \ if (!clipboard) { \ + LocalReferenceHolder_Cleanup(&refs); \ return error; \ } -extern "C" int Android_JNI_SetClipboardText(const char* text) +#define CLEANUP_CLIPBOARD() \ + LocalReferenceHolder_Cleanup(&refs); + +int Android_JNI_SetClipboardText(const char* text) { SETUP_CLIPBOARD(-1) - jmethodID mid = env->GetMethodID(env->GetObjectClass(clipboard), "setText", "(Ljava/lang/CharSequence;)V"); - jstring string = env->NewStringUTF(text); - env->CallVoidMethod(clipboard, mid, string); - env->DeleteGlobalRef(clipboard); - env->DeleteLocalRef(string); + jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "setText", "(Ljava/lang/CharSequence;)V"); + jstring string = (*env)->NewStringUTF(env, text); + (*env)->CallVoidMethod(env, clipboard, mid, string); + (*env)->DeleteGlobalRef(env, clipboard); + (*env)->DeleteLocalRef(env, string); + + CLEANUP_CLIPBOARD(); + return 0; } -extern "C" char* Android_JNI_GetClipboardText() +char* Android_JNI_GetClipboardText() { SETUP_CLIPBOARD(SDL_strdup("")) - jmethodID mid = env->GetMethodID(env->GetObjectClass(clipboard), "getText", "()Ljava/lang/CharSequence;"); - jobject sequence = env->CallObjectMethod(clipboard, mid); - env->DeleteGlobalRef(clipboard); + jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "getText", "()Ljava/lang/CharSequence;"); + jobject sequence = (*env)->CallObjectMethod(env, clipboard, mid); + (*env)->DeleteGlobalRef(env, clipboard); if (sequence) { - mid = env->GetMethodID(env->GetObjectClass(sequence), "toString", "()Ljava/lang/String;"); - jstring string = reinterpret_cast(env->CallObjectMethod(sequence, mid)); - const char* utf = env->GetStringUTFChars(string, 0); + mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, sequence), "toString", "()Ljava/lang/String;"); + jstring string = (jstring)((*env)->CallObjectMethod(env, sequence, mid)); + const char* utf = (*env)->GetStringUTFChars(env, string, 0); if (utf) { char* text = SDL_strdup(utf); - env->ReleaseStringUTFChars(string, utf); + (*env)->ReleaseStringUTFChars(env, string, utf); + + CLEANUP_CLIPBOARD(); + return text; } } + + CLEANUP_CLIPBOARD(); + return SDL_strdup(""); } -extern "C" SDL_bool Android_JNI_HasClipboardText() +SDL_bool Android_JNI_HasClipboardText() { SETUP_CLIPBOARD(SDL_FALSE) - jmethodID mid = env->GetMethodID(env->GetObjectClass(clipboard), "hasText", "()Z"); - jboolean has = env->CallBooleanMethod(clipboard, mid); - env->DeleteGlobalRef(clipboard); + jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "hasText", "()Z"); + jboolean has = (*env)->CallBooleanMethod(env, clipboard, mid); + (*env)->DeleteGlobalRef(env, clipboard); + + CLEANUP_CLIPBOARD(); + return has ? SDL_TRUE : SDL_FALSE; } @@ -1011,54 +1054,56 @@ extern "C" SDL_bool Android_JNI_HasClipboardText() // returns 0 on success or -1 on error (others undefined then) // returns truthy or falsy value in plugged, charged and battery // returns the value in seconds and percent or -1 if not available -extern "C" int Android_JNI_GetPowerInfo(int* plugged, int* charged, int* battery, int* seconds, int* percent) +int Android_JNI_GetPowerInfo(int* plugged, int* charged, int* battery, int* seconds, int* percent) { - LocalReferenceHolder refs(__FUNCTION__); + struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); JNIEnv* env = Android_JNI_GetEnv(); - if (!refs.init(env)) { + if (!LocalReferenceHolder_Init(&refs, env)) { + LocalReferenceHolder_Cleanup(&refs); return -1; } jmethodID mid; - mid = env->GetStaticMethodID(mActivityClass, "getContext", "()Landroid/content/Context;"); - jobject context = env->CallStaticObjectMethod(mActivityClass, mid); + mid = (*env)->GetStaticMethodID(env, mActivityClass, "getContext", "()Landroid/content/Context;"); + jobject context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid); - jstring action = env->NewStringUTF("android.intent.action.BATTERY_CHANGED"); + jstring action = (*env)->NewStringUTF(env, "android.intent.action.BATTERY_CHANGED"); - jclass cls = env->FindClass("android/content/IntentFilter"); + jclass cls = (*env)->FindClass(env, "android/content/IntentFilter"); - mid = env->GetMethodID(cls, "", "(Ljava/lang/String;)V"); - jobject filter = env->NewObject(cls, mid, action); + mid = (*env)->GetMethodID(env, cls, "", "(Ljava/lang/String;)V"); + jobject filter = (*env)->NewObject(env, cls, mid, action); - env->DeleteLocalRef(action); + (*env)->DeleteLocalRef(env, action); - mid = env->GetMethodID(mActivityClass, "registerReceiver", "(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;"); - jobject intent = env->CallObjectMethod(context, mid, NULL, filter); + mid = (*env)->GetMethodID(env, mActivityClass, "registerReceiver", "(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;"); + jobject intent = (*env)->CallObjectMethod(env, context, mid, NULL, filter); - env->DeleteLocalRef(filter); + (*env)->DeleteLocalRef(env, filter); - cls = env->GetObjectClass(intent); + cls = (*env)->GetObjectClass(env, intent); jstring iname; - jmethodID imid = env->GetMethodID(cls, "getIntExtra", "(Ljava/lang/String;I)I"); + jmethodID imid = (*env)->GetMethodID(env, cls, "getIntExtra", "(Ljava/lang/String;I)I"); #define GET_INT_EXTRA(var, key) \ - iname = env->NewStringUTF(key); \ - int var = env->CallIntMethod(intent, imid, iname, -1); \ - env->DeleteLocalRef(iname); + iname = (*env)->NewStringUTF(env, key); \ + int var = (*env)->CallIntMethod(env, intent, imid, iname, -1); \ + (*env)->DeleteLocalRef(env, iname); jstring bname; - jmethodID bmid = env->GetMethodID(cls, "getBooleanExtra", "(Ljava/lang/String;Z)Z"); + jmethodID bmid = (*env)->GetMethodID(env, cls, "getBooleanExtra", "(Ljava/lang/String;Z)Z"); #define GET_BOOL_EXTRA(var, key) \ - bname = env->NewStringUTF(key); \ - int var = env->CallBooleanMethod(intent, bmid, bname, JNI_FALSE); \ - env->DeleteLocalRef(bname); + bname = (*env)->NewStringUTF(env, key); \ + int var = (*env)->CallBooleanMethod(env, intent, bmid, bname, JNI_FALSE); \ + (*env)->DeleteLocalRef(env, bname); if (plugged) { GET_INT_EXTRA(plug, "plugged") // == BatteryManager.EXTRA_PLUGGED (API 5) if (plug == -1) { + LocalReferenceHolder_Cleanup(&refs); return -1; } // 1 == BatteryManager.BATTERY_PLUGGED_AC @@ -1069,6 +1114,7 @@ extern "C" int Android_JNI_GetPowerInfo(int* plugged, int* charged, int* battery if (charged) { GET_INT_EXTRA(status, "status") // == BatteryManager.EXTRA_STATUS (API 5) if (status == -1) { + LocalReferenceHolder_Cleanup(&refs); return -1; } // 5 == BatteryManager.BATTERY_STATUS_FULL @@ -1088,50 +1134,52 @@ extern "C" int Android_JNI_GetPowerInfo(int* plugged, int* charged, int* battery GET_INT_EXTRA(level, "level") // == BatteryManager.EXTRA_LEVEL (API 5) GET_INT_EXTRA(scale, "scale") // == BatteryManager.EXTRA_SCALE (API 5) if ((level == -1) || (scale == -1)) { + LocalReferenceHolder_Cleanup(&refs); return -1; } *percent = level * 100 / scale; } - env->DeleteLocalRef(intent); + (*env)->DeleteLocalRef(env, intent); + LocalReferenceHolder_Cleanup(&refs); return 0; } // sends message to be handled on the UI event dispatch thread -extern "C" int Android_JNI_SendMessage(int command, int param) +int Android_JNI_SendMessage(int command, int param) { JNIEnv *env = Android_JNI_GetEnv(); if (!env) { return -1; } - jmethodID mid = env->GetStaticMethodID(mActivityClass, "sendMessage", "(II)Z"); + jmethodID mid = (*env)->GetStaticMethodID(env, mActivityClass, "sendMessage", "(II)Z"); if (!mid) { return -1; } - jboolean success = env->CallStaticBooleanMethod(mActivityClass, mid, command, param); + jboolean success = (*env)->CallStaticBooleanMethod(env, mActivityClass, mid, command, param); return success ? 0 : -1; } -extern "C" void Android_JNI_ShowTextInput(SDL_Rect *inputRect) +void Android_JNI_ShowTextInput(SDL_Rect *inputRect) { JNIEnv *env = Android_JNI_GetEnv(); if (!env) { return; } - jmethodID mid = env->GetStaticMethodID(mActivityClass, "showTextInput", "(IIII)Z"); + jmethodID mid = (*env)->GetStaticMethodID(env, mActivityClass, "showTextInput", "(IIII)Z"); if (!mid) { return; } - env->CallStaticBooleanMethod( mActivityClass, mid, + (*env)->CallStaticBooleanMethod(env, mActivityClass, mid, inputRect->x, inputRect->y, inputRect->w, inputRect->h ); } -extern "C" void Android_JNI_HideTextInput() +void Android_JNI_HideTextInput() { // has to match Activity constant const int COMMAND_TEXTEDIT_HIDE = 3; @@ -1143,14 +1191,14 @@ extern "C" void Android_JNI_HideTextInput() // Functions exposed to SDL applications in SDL_system.h // -extern "C" void *SDL_AndroidGetJNIEnv() +void *SDL_AndroidGetJNIEnv() { return Android_JNI_GetEnv(); } -extern "C" void *SDL_AndroidGetActivity() +void *SDL_AndroidGetActivity() { /* See SDL_system.h for caveats on using this function. */ @@ -1162,17 +1210,17 @@ extern "C" void *SDL_AndroidGetActivity() } // return SDLActivity.getContext(); - mid = env->GetStaticMethodID(mActivityClass, + mid = (*env)->GetStaticMethodID(env, mActivityClass, "getContext","()Landroid/content/Context;"); - return env->CallStaticObjectMethod(mActivityClass, mid); + return (*env)->CallStaticObjectMethod(env, mActivityClass, mid); } -extern "C" const char * SDL_AndroidGetInternalStoragePath() +const char * SDL_AndroidGetInternalStoragePath() { static char *s_AndroidInternalFilesPath = NULL; if (!s_AndroidInternalFilesPath) { - LocalReferenceHolder refs(__FUNCTION__); + struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); jmethodID mid; jobject context; jobject fileObject; @@ -1180,39 +1228,43 @@ extern "C" const char * SDL_AndroidGetInternalStoragePath() const char *path; JNIEnv *env = Android_JNI_GetEnv(); - if (!refs.init(env)) { + if (!LocalReferenceHolder_Init(&refs, env)) { + LocalReferenceHolder_Cleanup(&refs); return NULL; } // context = SDLActivity.getContext(); - mid = env->GetStaticMethodID(mActivityClass, + mid = (*env)->GetStaticMethodID(env, mActivityClass, "getContext","()Landroid/content/Context;"); - context = env->CallStaticObjectMethod(mActivityClass, mid); + context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid); // fileObj = context.getFilesDir(); - mid = env->GetMethodID(env->GetObjectClass(context), + mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context), "getFilesDir", "()Ljava/io/File;"); - fileObject = env->CallObjectMethod(context, mid); + fileObject = (*env)->CallObjectMethod(env, context, mid); if (!fileObject) { SDL_SetError("Couldn't get internal directory"); + LocalReferenceHolder_Cleanup(&refs); return NULL; } // path = fileObject.getAbsolutePath(); - mid = env->GetMethodID(env->GetObjectClass(fileObject), + mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, fileObject), "getAbsolutePath", "()Ljava/lang/String;"); - pathString = (jstring)env->CallObjectMethod(fileObject, mid); + pathString = (jstring)(*env)->CallObjectMethod(env, fileObject, mid); - path = env->GetStringUTFChars(pathString, NULL); + path = (*env)->GetStringUTFChars(env, pathString, NULL); s_AndroidInternalFilesPath = SDL_strdup(path); - env->ReleaseStringUTFChars(pathString, path); + (*env)->ReleaseStringUTFChars(env, pathString, path); + + LocalReferenceHolder_Cleanup(&refs); } return s_AndroidInternalFilesPath; } -extern "C" int SDL_AndroidGetExternalStorageState() +int SDL_AndroidGetExternalStorageState() { - LocalReferenceHolder refs(__FUNCTION__); + struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); jmethodID mid; jclass cls; jstring stateString; @@ -1220,16 +1272,17 @@ extern "C" int SDL_AndroidGetExternalStorageState() int stateFlags; JNIEnv *env = Android_JNI_GetEnv(); - if (!refs.init(env)) { + if (!LocalReferenceHolder_Init(&refs, env)) { + LocalReferenceHolder_Cleanup(&refs); return 0; } - cls = env->FindClass("android/os/Environment"); - mid = env->GetStaticMethodID(cls, + cls = (*env)->FindClass(env, "android/os/Environment"); + mid = (*env)->GetStaticMethodID(env, cls, "getExternalStorageState", "()Ljava/lang/String;"); - stateString = (jstring)env->CallStaticObjectMethod(cls, mid); + stateString = (jstring)(*env)->CallStaticObjectMethod(env, cls, mid); - state = env->GetStringUTFChars(stateString, NULL); + state = (*env)->GetStringUTFChars(env, stateString, NULL); // Print an info message so people debugging know the storage state __android_log_print(ANDROID_LOG_INFO, "SDL", "external storage state: %s", state); @@ -1242,17 +1295,18 @@ extern "C" int SDL_AndroidGetExternalStorageState() } else { stateFlags = 0; } - env->ReleaseStringUTFChars(stateString, state); + (*env)->ReleaseStringUTFChars(env, stateString, state); + LocalReferenceHolder_Cleanup(&refs); return stateFlags; } -extern "C" const char * SDL_AndroidGetExternalStoragePath() +const char * SDL_AndroidGetExternalStoragePath() { static char *s_AndroidExternalFilesPath = NULL; if (!s_AndroidExternalFilesPath) { - LocalReferenceHolder refs(__FUNCTION__); + struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); jmethodID mid; jobject context; jobject fileObject; @@ -1260,32 +1314,36 @@ extern "C" const char * SDL_AndroidGetExternalStoragePath() const char *path; JNIEnv *env = Android_JNI_GetEnv(); - if (!refs.init(env)) { + if (!LocalReferenceHolder_Init(&refs, env)) { + LocalReferenceHolder_Cleanup(&refs); return NULL; } // context = SDLActivity.getContext(); - mid = env->GetStaticMethodID(mActivityClass, + mid = (*env)->GetStaticMethodID(env, mActivityClass, "getContext","()Landroid/content/Context;"); - context = env->CallStaticObjectMethod(mActivityClass, mid); + context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid); // fileObj = context.getExternalFilesDir(); - mid = env->GetMethodID(env->GetObjectClass(context), + mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context), "getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;"); - fileObject = env->CallObjectMethod(context, mid, NULL); + fileObject = (*env)->CallObjectMethod(env, context, mid, NULL); if (!fileObject) { SDL_SetError("Couldn't get external directory"); + LocalReferenceHolder_Cleanup(&refs); return NULL; } // path = fileObject.getAbsolutePath(); - mid = env->GetMethodID(env->GetObjectClass(fileObject), + mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, fileObject), "getAbsolutePath", "()Ljava/lang/String;"); - pathString = (jstring)env->CallObjectMethod(fileObject, mid); + pathString = (jstring)(*env)->CallObjectMethod(env, fileObject, mid); - path = env->GetStringUTFChars(pathString, NULL); + path = (*env)->GetStringUTFChars(env, pathString, NULL); s_AndroidExternalFilesPath = SDL_strdup(path); - env->ReleaseStringUTFChars(pathString, path); + (*env)->ReleaseStringUTFChars(env, pathString, path); + + LocalReferenceHolder_Cleanup(&refs); } return s_AndroidExternalFilesPath; } diff --git a/src/main/android/SDL_android_main.cpp b/src/main/android/SDL_android_main.c similarity index 85% rename from src/main/android/SDL_android_main.cpp rename to src/main/android/SDL_android_main.c index 48e220f19..0622a12c4 100644 --- a/src/main/android/SDL_android_main.cpp +++ b/src/main/android/SDL_android_main.c @@ -12,10 +12,10 @@ #include // Called before SDL_main() to initialize JNI bindings in SDL library -extern "C" void SDL_Android_Init(JNIEnv* env, jclass cls); +extern void SDL_Android_Init(JNIEnv* env, jclass cls); // Start up the SDL app -extern "C" void Java_org_libsdl_app_SDLActivity_nativeInit(JNIEnv* env, jclass cls, jobject obj) +void Java_org_libsdl_app_SDLActivity_nativeInit(JNIEnv* env, jclass cls, jobject obj) { /* This interface could expand with ABI negotiation, calbacks, etc. */ SDL_Android_Init(env, cls);