| | |
| | | /* |
| | | Simple DirectMedia Layer |
| | | Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> |
| | | Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org> |
| | | |
| | | This software is provided 'as-is', without any express or implied |
| | | warranty. In no event will the authors be held liable for any damages |
| | |
| | | #include "../../SDL_internal.h" |
| | | #include "SDL_stdinc.h" |
| | | #include "SDL_assert.h" |
| | | #include "SDL_atomic.h" |
| | | #include "SDL_hints.h" |
| | | #include "SDL_log.h" |
| | | #include "SDL_main.h" |
| | | #include "SDL_timer.h" |
| | | |
| | | #ifdef __ANDROID__ |
| | | |
| | |
| | | #include "../../haptic/android/SDL_syshaptic_c.h" |
| | | |
| | | #include <android/log.h> |
| | | #include <sys/system_properties.h> |
| | | #include <pthread.h> |
| | | #include <sys/types.h> |
| | | #include <unistd.h> |
| | | #include <dlfcn.h> |
| | | /* #define LOG_TAG "SDL_android" */ |
| | | /* #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) */ |
| | | /* #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) */ |
| | | #define LOGI(...) do {} while (0) |
| | | #define LOGE(...) do {} while (0) |
| | | |
| | | |
| | | #define SDL_JAVA_PREFIX org_libsdl_app |
| | | #define CONCAT1(prefix, class, function) CONCAT2(prefix, class, function) |
| | |
| | | |
| | | /* Java class SDLActivity */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)( |
| | | JNIEnv* mEnv, jclass cls); |
| | | JNIEnv *env, jclass cls); |
| | | |
| | | JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeRunMain)( |
| | | JNIEnv* env, jclass cls, |
| | | JNIEnv *env, jclass cls, |
| | | jstring library, jstring function, jobject array); |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeDropFile)( |
| | | JNIEnv* env, jclass jcls, |
| | | JNIEnv *env, jclass jcls, |
| | | jstring filename); |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeResize)( |
| | | JNIEnv* env, jclass jcls, |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetScreenResolution)( |
| | | JNIEnv *env, jclass jcls, |
| | | jint surfaceWidth, jint surfaceHeight, |
| | | jint deviceWidth, jint deviceHeight, jint format, jfloat rate); |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeResize)( |
| | | JNIEnv *env, jclass cls); |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceCreated)( |
| | | JNIEnv *env, jclass jcls); |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)( |
| | | JNIEnv* env, jclass jcls); |
| | | JNIEnv *env, jclass jcls); |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)( |
| | | JNIEnv* env, jclass jcls); |
| | | JNIEnv *env, jclass jcls); |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyDown)( |
| | | JNIEnv* env, jclass jcls, |
| | | JNIEnv *env, jclass jcls, |
| | | jint keycode); |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyUp)( |
| | | JNIEnv* env, jclass jcls, |
| | | JNIEnv *env, jclass jcls, |
| | | jint keycode); |
| | | |
| | | JNIEXPORT jboolean JNICALL SDL_JAVA_INTERFACE(onNativeSoftReturnKey)( |
| | | JNIEnv *env, jclass jcls); |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyboardFocusLost)( |
| | | JNIEnv* env, jclass jcls); |
| | | JNIEnv *env, jclass jcls); |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeTouch)( |
| | | JNIEnv* env, jclass jcls, |
| | | JNIEnv *env, jclass jcls, |
| | | jint touch_device_id_in, jint pointer_finger_id_in, |
| | | jint action, jfloat x, jfloat y, jfloat p); |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeMouse)( |
| | | JNIEnv* env, jclass jcls, |
| | | JNIEnv *env, jclass jcls, |
| | | jint button, jint action, jfloat x, jfloat y, jboolean relative); |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeAccel)( |
| | | JNIEnv* env, jclass jcls, |
| | | JNIEnv *env, jclass jcls, |
| | | jfloat x, jfloat y, jfloat z); |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeClipboardChanged)( |
| | | JNIEnv* env, jclass jcls); |
| | | JNIEnv *env, jclass jcls); |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeLowMemory)( |
| | | JNIEnv* env, jclass cls); |
| | | JNIEnv *env, jclass cls); |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSendQuit)( |
| | | JNIEnv *env, jclass cls); |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeQuit)( |
| | | JNIEnv* env, jclass cls); |
| | | JNIEnv *env, jclass cls); |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePause)( |
| | | JNIEnv* env, jclass cls); |
| | | JNIEnv *env, jclass cls); |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeResume)( |
| | | JNIEnv* env, jclass cls); |
| | | JNIEnv *env, jclass cls); |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeFocusChanged)( |
| | | JNIEnv *env, jclass cls, jboolean hasFocus); |
| | | |
| | | JNIEXPORT jstring JNICALL SDL_JAVA_INTERFACE(nativeGetHint)( |
| | | JNIEnv* env, jclass cls, |
| | | JNIEnv *env, jclass cls, |
| | | jstring name); |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetenv)( |
| | | JNIEnv* env, jclass cls, |
| | | JNIEnv *env, jclass cls, |
| | | jstring name, jstring value); |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeEnvironmentVariablesSet)( |
| | | JNIEnv* env, jclass cls); |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeOrientationChanged)( |
| | | JNIEnv* env, jclass cls, |
| | | JNIEnv *env, jclass cls, |
| | | jint orientation); |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeAddTouch)( |
| | | JNIEnv* env, jclass cls, |
| | | jint touchId, jstring name); |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePermissionResult)( |
| | | JNIEnv* env, jclass cls, |
| | | jint requestCode, jboolean result); |
| | | |
| | | static JNINativeMethod SDLActivity_tab[] = { |
| | | { "nativeSetupJNI", "()I", SDL_JAVA_INTERFACE(nativeSetupJNI) }, |
| | | { "nativeRunMain", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)I", SDL_JAVA_INTERFACE(nativeRunMain) }, |
| | | { "onNativeDropFile", "(Ljava/lang/String;)V", SDL_JAVA_INTERFACE(onNativeDropFile) }, |
| | | { "nativeSetScreenResolution", "(IIIIIF)V", SDL_JAVA_INTERFACE(nativeSetScreenResolution) }, |
| | | { "onNativeResize", "()V", SDL_JAVA_INTERFACE(onNativeResize) }, |
| | | { "onNativeSurfaceCreated", "()V", SDL_JAVA_INTERFACE(onNativeSurfaceCreated) }, |
| | | { "onNativeSurfaceChanged", "()V", SDL_JAVA_INTERFACE(onNativeSurfaceChanged) }, |
| | | { "onNativeSurfaceDestroyed", "()V", SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed) }, |
| | | { "onNativeKeyDown", "(I)V", SDL_JAVA_INTERFACE(onNativeKeyDown) }, |
| | | { "onNativeKeyUp", "(I)V", SDL_JAVA_INTERFACE(onNativeKeyUp) }, |
| | | { "onNativeSoftReturnKey", "()Z", SDL_JAVA_INTERFACE(onNativeSoftReturnKey) }, |
| | | { "onNativeKeyboardFocusLost", "()V", SDL_JAVA_INTERFACE(onNativeKeyboardFocusLost) }, |
| | | { "onNativeTouch", "(IIIFFF)V", SDL_JAVA_INTERFACE(onNativeTouch) }, |
| | | { "onNativeMouse", "(IIFFZ)V", SDL_JAVA_INTERFACE(onNativeMouse) }, |
| | | { "onNativeAccel", "(FFF)V", SDL_JAVA_INTERFACE(onNativeAccel) }, |
| | | { "onNativeClipboardChanged", "()V", SDL_JAVA_INTERFACE(onNativeClipboardChanged) }, |
| | | { "nativeLowMemory", "()V", SDL_JAVA_INTERFACE(nativeLowMemory) }, |
| | | { "nativeSendQuit", "()V", SDL_JAVA_INTERFACE(nativeSendQuit) }, |
| | | { "nativeQuit", "()V", SDL_JAVA_INTERFACE(nativeQuit) }, |
| | | { "nativePause", "()V", SDL_JAVA_INTERFACE(nativePause) }, |
| | | { "nativeResume", "()V", SDL_JAVA_INTERFACE(nativeResume) }, |
| | | { "nativeFocusChanged", "(Z)V", SDL_JAVA_INTERFACE(nativeFocusChanged) }, |
| | | { "nativeGetHint", "(Ljava/lang/String;)Ljava/lang/String;", SDL_JAVA_INTERFACE(nativeGetHint) }, |
| | | { "nativeSetenv", "(Ljava/lang/String;Ljava/lang/String;)V", SDL_JAVA_INTERFACE(nativeSetenv) }, |
| | | { "onNativeOrientationChanged", "(I)V", SDL_JAVA_INTERFACE(onNativeOrientationChanged) }, |
| | | { "nativeAddTouch", "(ILjava/lang/String;)V", SDL_JAVA_INTERFACE(nativeAddTouch) }, |
| | | { "nativePermissionResult", "(IZ)V", SDL_JAVA_INTERFACE(nativePermissionResult) } |
| | | }; |
| | | |
| | | /* Java class SDLInputConnection */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeCommitText)( |
| | | JNIEnv* env, jclass cls, |
| | | JNIEnv *env, jclass cls, |
| | | jstring text, jint newCursorPosition); |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeGenerateScancodeForUnichar)( |
| | | JNIEnv* env, jclass cls, |
| | | JNIEnv *env, jclass cls, |
| | | jchar chUnicode); |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeSetComposingText)( |
| | | JNIEnv* env, jclass cls, |
| | | JNIEnv *env, jclass cls, |
| | | jstring text, jint newCursorPosition); |
| | | |
| | | static JNINativeMethod SDLInputConnection_tab[] = { |
| | | { "nativeCommitText", "(Ljava/lang/String;I)V", SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeCommitText) }, |
| | | { "nativeGenerateScancodeForUnichar", "(C)V", SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeGenerateScancodeForUnichar) }, |
| | | { "nativeSetComposingText", "(Ljava/lang/String;I)V", SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeSetComposingText) } |
| | | }; |
| | | |
| | | /* Java class SDLAudioManager */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)( |
| | | JNIEnv *env, jclass jcls); |
| | | |
| | | static JNINativeMethod SDLAudioManager_tab[] = { |
| | | { "nativeSetupJNI", "()I", SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI) } |
| | | }; |
| | | |
| | | /* Java class SDLControllerManager */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)( |
| | | JNIEnv *env, jclass jcls); |
| | | |
| | | JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)( |
| | | JNIEnv* env, jclass jcls, |
| | | JNIEnv *env, jclass jcls, |
| | | jint device_id, jint keycode); |
| | | |
| | | JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)( |
| | | JNIEnv* env, jclass jcls, |
| | | JNIEnv *env, jclass jcls, |
| | | jint device_id, jint keycode); |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)( |
| | | JNIEnv* env, jclass jcls, |
| | | JNIEnv *env, jclass jcls, |
| | | jint device_id, jint axis, jfloat value); |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)( |
| | | JNIEnv* env, jclass jcls, |
| | | JNIEnv *env, jclass jcls, |
| | | jint device_id, jint hat_id, jint x, jint y); |
| | | |
| | | JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)( |
| | | JNIEnv* env, jclass jcls, |
| | | JNIEnv *env, jclass jcls, |
| | | jint device_id, jstring device_name, jstring device_desc, jint vendor_id, jint product_id, |
| | | jboolean is_accelerometer, jint button_mask, jint naxes, jint nhats, jint nballs); |
| | | |
| | | JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)( |
| | | JNIEnv* env, jclass jcls, |
| | | JNIEnv *env, jclass jcls, |
| | | jint device_id); |
| | | |
| | | JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic)( |
| | | JNIEnv* env, jclass jcls, |
| | | JNIEnv *env, jclass jcls, |
| | | jint device_id, jstring device_name); |
| | | |
| | | JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)( |
| | | JNIEnv* env, jclass jcls, |
| | | JNIEnv *env, jclass jcls, |
| | | jint device_id); |
| | | |
| | | static JNINativeMethod SDLControllerManager_tab[] = { |
| | | { "nativeSetupJNI", "()I", SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI) }, |
| | | { "onNativePadDown", "(II)I", SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown) }, |
| | | { "onNativePadUp", "(II)I", SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp) }, |
| | | { "onNativeJoy", "(IIF)V", SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy) }, |
| | | { "onNativeHat", "(IIII)V", SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat) }, |
| | | { "nativeAddJoystick", "(ILjava/lang/String;Ljava/lang/String;IIZIIII)I", SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick) }, |
| | | { "nativeRemoveJoystick", "(I)I", SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick) }, |
| | | { "nativeAddHaptic", "(ILjava/lang/String;)I", SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic) }, |
| | | { "nativeRemoveHaptic", "(I)I", SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic) } |
| | | }; |
| | | |
| | | |
| | | /* Uncomment this to log messages entering and exiting methods in this file */ |
| | | /* #define DEBUG_JNI */ |
| | | |
| | | static void Android_JNI_ThreadDestroyed(void*); |
| | | static void checkJNIReady(void); |
| | | |
| | | /******************************************************************************* |
| | |
| | | Globals |
| | | *******************************************************************************/ |
| | | static pthread_key_t mThreadKey; |
| | | static JavaVM* mJavaVM; |
| | | static pthread_once_t key_once = PTHREAD_ONCE_INIT; |
| | | static JavaVM *mJavaVM = NULL; |
| | | |
| | | /* Main activity */ |
| | | static jclass mActivityClass; |
| | | |
| | | /* method signatures */ |
| | | static jmethodID midGetNativeSurface; |
| | | static jmethodID midSetActivityTitle; |
| | | static jmethodID midSetWindowStyle; |
| | | static jmethodID midSetOrientation; |
| | | static jmethodID midClipboardGetText; |
| | | static jmethodID midClipboardHasText; |
| | | static jmethodID midClipboardSetText; |
| | | static jmethodID midCreateCustomCursor; |
| | | static jmethodID midGetContext; |
| | | static jmethodID midIsTablet; |
| | | static jmethodID midGetDisplayDPI; |
| | | static jmethodID midGetManifestEnvironmentVariables; |
| | | static jmethodID midGetNativeSurface; |
| | | static jmethodID midInitTouch; |
| | | static jmethodID midIsAndroidTV; |
| | | static jmethodID midIsChromebook; |
| | | static jmethodID midIsDeXMode; |
| | | static jmethodID midManualBackButton; |
| | | static jmethodID midInputGetInputDeviceIds; |
| | | static jmethodID midSendMessage; |
| | | static jmethodID midShowTextInput; |
| | | static jmethodID midIsScreenKeyboardShown; |
| | | static jmethodID midClipboardSetText; |
| | | static jmethodID midClipboardGetText; |
| | | static jmethodID midClipboardHasText; |
| | | static jmethodID midIsTablet; |
| | | static jmethodID midManualBackButton; |
| | | static jmethodID midMinimizeWindow; |
| | | static jmethodID midOpenAPKExpansionInputStream; |
| | | static jmethodID midGetManifestEnvironmentVariables; |
| | | static jmethodID midGetDisplayDPI; |
| | | static jmethodID midCreateCustomCursor; |
| | | static jmethodID midRequestPermission; |
| | | static jmethodID midSendMessage; |
| | | static jmethodID midSetActivityTitle; |
| | | static jmethodID midSetCustomCursor; |
| | | static jmethodID midSetSystemCursor; |
| | | static jmethodID midSupportsRelativeMouse; |
| | | static jmethodID midSetOrientation; |
| | | static jmethodID midSetRelativeMouseEnabled; |
| | | static jmethodID midSetSurfaceViewFormat; |
| | | static jmethodID midSetSystemCursor; |
| | | static jmethodID midSetWindowStyle; |
| | | static jmethodID midShouldMinimizeOnFocusLoss; |
| | | static jmethodID midShowTextInput; |
| | | static jmethodID midSupportsRelativeMouse; |
| | | |
| | | /* audio manager */ |
| | | static jclass mAudioManagerClass; |
| | |
| | | static jmethodID midCaptureReadShortBuffer; |
| | | static jmethodID midCaptureReadFloatBuffer; |
| | | static jmethodID midCaptureClose; |
| | | static jmethodID midAudioSetThreadPriority; |
| | | |
| | | /* controller manager */ |
| | | static jclass mControllerManagerClass; |
| | |
| | | static jmethodID midHapticRun; |
| | | static jmethodID midHapticStop; |
| | | |
| | | /* static fields */ |
| | | static jfieldID fidSeparateMouseAndTouch; |
| | | |
| | | /* Accelerometer data storage */ |
| | | static SDL_DisplayOrientation displayOrientation; |
| | | static float fLastAccelerometer[3]; |
| | | static SDL_bool bHasNewData; |
| | | |
| | | static SDL_bool bHasEnvironmentVariables = SDL_FALSE; |
| | | static SDL_bool bHasEnvironmentVariables; |
| | | |
| | | static SDL_atomic_t bPermissionRequestPending; |
| | | static SDL_bool bPermissionRequestResult; |
| | | |
| | | /******************************************************************************* |
| | | Functions called by JNI |
| | | *******************************************************************************/ |
| | | |
| | | /* Library init */ |
| | | JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) |
| | | /* From http://developer.android.com/guide/practices/jni.html |
| | | * All threads are Linux threads, scheduled by the kernel. |
| | | * They're usually started from managed code (using Thread.start), but they can also be created elsewhere and then |
| | | * attached to the JavaVM. For example, a thread started with pthread_create can be attached with the |
| | | * JNI AttachCurrentThread or AttachCurrentThreadAsDaemon functions. Until a thread is attached, it has no JNIEnv, |
| | | * and cannot make JNI calls. |
| | | * Attaching a natively-created thread causes a java.lang.Thread object to be constructed and added to the "main" |
| | | * ThreadGroup, making it visible to the debugger. Calling AttachCurrentThread on an already-attached thread |
| | | * is a no-op. |
| | | * Note: You can call this function any number of times for the same thread, there's no harm in it |
| | | */ |
| | | |
| | | /* From http://developer.android.com/guide/practices/jni.html |
| | | * Threads attached through JNI must call DetachCurrentThread before they exit. If coding this directly is awkward, |
| | | * in Android 2.0 (Eclair) and higher you can use pthread_key_create to define a destructor function that will be |
| | | * called before the thread exits, and call DetachCurrentThread from there. (Use that key with pthread_setspecific |
| | | * to store the JNIEnv in thread-local-storage; that way it'll be passed into your destructor as the argument.) |
| | | * Note: The destructor is not called unless the stored value is != NULL |
| | | * Note: You can call this function any number of times for the same thread, there's no harm in it |
| | | * (except for some lost CPU cycles) |
| | | */ |
| | | |
| | | /* Set local storage value */ |
| | | static int |
| | | Android_JNI_SetEnv(JNIEnv *env) { |
| | | int status = pthread_setspecific(mThreadKey, env); |
| | | if (status < 0) { |
| | | __android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed pthread_setspecific() in Android_JNI_SetEnv() (err=%d)", status); |
| | | } |
| | | return status; |
| | | } |
| | | |
| | | /* Get local storage value */ |
| | | JNIEnv* Android_JNI_GetEnv(void) |
| | | { |
| | | /* Get JNIEnv from the Thread local storage */ |
| | | JNIEnv *env = pthread_getspecific(mThreadKey); |
| | | if (env == NULL) { |
| | | /* If it fails, try to attach ! (e.g the thread isn't created with SDL_CreateThread() */ |
| | | int status; |
| | | |
| | | /* There should be a JVM */ |
| | | if (mJavaVM == NULL) { |
| | | __android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed, there is no JavaVM"); |
| | | return NULL; |
| | | } |
| | | |
| | | /* Attach the current thread to the JVM and get a JNIEnv. |
| | | * It will be detached by pthread_create destructor 'Android_JNI_ThreadDestroyed' */ |
| | | status = (*mJavaVM)->AttachCurrentThread(mJavaVM, &env, NULL); |
| | | if (status < 0) { |
| | | __android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed to attach current thread (err=%d)", status); |
| | | return NULL; |
| | | } |
| | | |
| | | /* Save JNIEnv into the Thread local storage */ |
| | | if (Android_JNI_SetEnv(env) < 0) { |
| | | return NULL; |
| | | } |
| | | } |
| | | |
| | | return env; |
| | | } |
| | | |
| | | /* Set up an external thread for using JNI with Android_JNI_GetEnv() */ |
| | | int Android_JNI_SetupThread(void) |
| | | { |
| | | JNIEnv *env; |
| | | int status; |
| | | |
| | | /* There should be a JVM */ |
| | | if (mJavaVM == NULL) { |
| | | __android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed, there is no JavaVM"); |
| | | return 0; |
| | | } |
| | | |
| | | /* Attach the current thread to the JVM and get a JNIEnv. |
| | | * It will be detached by pthread_create destructor 'Android_JNI_ThreadDestroyed' */ |
| | | status = (*mJavaVM)->AttachCurrentThread(mJavaVM, &env, NULL); |
| | | if (status < 0) { |
| | | __android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed to attach current thread (err=%d)", status); |
| | | return 0; |
| | | } |
| | | |
| | | /* Save JNIEnv into the Thread local storage */ |
| | | if (Android_JNI_SetEnv(env) < 0) { |
| | | return 0; |
| | | } |
| | | |
| | | return 1; |
| | | } |
| | | |
| | | /* Destructor called for each thread where mThreadKey is not NULL */ |
| | | 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); |
| | | Android_JNI_SetEnv(NULL); |
| | | } |
| | | } |
| | | |
| | | /* Creation of local storage mThreadKey */ |
| | | static void |
| | | Android_JNI_CreateKey(void) |
| | | { |
| | | int status = pthread_key_create(&mThreadKey, Android_JNI_ThreadDestroyed); |
| | | if (status < 0) { |
| | | __android_log_print(ANDROID_LOG_ERROR, "SDL", "Error initializing mThreadKey with pthread_key_create() (err=%d)", status); |
| | | } |
| | | } |
| | | |
| | | static void |
| | | Android_JNI_CreateKey_once(void) |
| | | { |
| | | int status = pthread_once(&key_once, Android_JNI_CreateKey); |
| | | if (status < 0) { |
| | | __android_log_print(ANDROID_LOG_ERROR, "SDL", "Error initializing mThreadKey with pthread_once() (err=%d)", status); |
| | | } |
| | | } |
| | | |
| | | static void |
| | | register_methods(JNIEnv *env, const char *classname, JNINativeMethod *methods, int nb) |
| | | { |
| | | jclass clazz = (*env)->FindClass(env, classname); |
| | | if (clazz == NULL || (*env)->RegisterNatives(env, clazz, methods, nb) < 0) { |
| | | __android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed to register methods of %s", classname); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | /* Library init */ |
| | | JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) |
| | | { |
| | | mJavaVM = vm; |
| | | LOGI("JNI_OnLoad called"); |
| | | if ((*mJavaVM)->GetEnv(mJavaVM, (void**) &env, JNI_VERSION_1_4) != JNI_OK) { |
| | | LOGE("Failed to get the environment using GetEnv()"); |
| | | return -1; |
| | | JNIEnv *env = NULL; |
| | | |
| | | if ((*mJavaVM)->GetEnv(mJavaVM, (void **)&env, JNI_VERSION_1_4) != JNI_OK) { |
| | | __android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed to get JNI Env"); |
| | | return JNI_VERSION_1_4; |
| | | } |
| | | /* |
| | | * Create mThreadKey so we can keep track of the JNIEnv assigned to each thread |
| | | * Refer to http://developer.android.com/guide/practices/design/jni.html for the rationale behind this |
| | | */ |
| | | if (pthread_key_create(&mThreadKey, Android_JNI_ThreadDestroyed) != 0) { |
| | | __android_log_print(ANDROID_LOG_ERROR, "SDL", "Error initializing pthread key"); |
| | | } |
| | | Android_JNI_SetupThread(); |
| | | |
| | | register_methods(env, "org/libsdl/app/SDLActivity", SDLActivity_tab, SDL_arraysize(SDLActivity_tab)); |
| | | register_methods(env, "org/libsdl/app/SDLInputConnection", SDLInputConnection_tab, SDL_arraysize(SDLInputConnection_tab)); |
| | | register_methods(env, "org/libsdl/app/SDLAudioManager", SDLAudioManager_tab, SDL_arraysize(SDLAudioManager_tab)); |
| | | register_methods(env, "org/libsdl/app/SDLControllerManager", SDLControllerManager_tab, SDL_arraysize(SDLControllerManager_tab)); |
| | | |
| | | return JNI_VERSION_1_4; |
| | | } |
| | | |
| | | void checkJNIReady() |
| | | void checkJNIReady(void) |
| | | { |
| | | if (!mActivityClass || !mAudioManagerClass || !mControllerManagerClass) { |
| | | // We aren't fully initialized, let's just return. |
| | | /* We aren't fully initialized, let's just return. */ |
| | | return; |
| | | } |
| | | |
| | | SDL_SetMainReady(); |
| | | SDL_SetMainReady(); |
| | | } |
| | | |
| | | /* Activity initialization -- called before SDL_main() to initialize JNI bindings */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv* mEnv, jclass cls) |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cls) |
| | | { |
| | | __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeSetupJNI()"); |
| | | |
| | | Android_JNI_SetupThread(); |
| | | /* |
| | | * Create mThreadKey so we can keep track of the JNIEnv assigned to each thread |
| | | * Refer to http://developer.android.com/guide/practices/design/jni.html for the rationale behind this |
| | | */ |
| | | Android_JNI_CreateKey_once(); |
| | | |
| | | mActivityClass = (jclass)((*mEnv)->NewGlobalRef(mEnv, cls)); |
| | | /* Save JNIEnv of SDLActivity */ |
| | | Android_JNI_SetEnv(env); |
| | | |
| | | midGetNativeSurface = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, |
| | | "getNativeSurface","()Landroid/view/Surface;"); |
| | | midSetActivityTitle = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, |
| | | "setActivityTitle","(Ljava/lang/String;)Z"); |
| | | midSetWindowStyle = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, |
| | | "setWindowStyle","(Z)V"); |
| | | midSetOrientation = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, |
| | | "setOrientation","(IIZLjava/lang/String;)V"); |
| | | midGetContext = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, |
| | | "getContext","()Landroid/content/Context;"); |
| | | midIsTablet = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, |
| | | "isTablet", "()Z"); |
| | | midIsAndroidTV = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, |
| | | "isAndroidTV","()Z"); |
| | | midIsChromebook = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, |
| | | "isChromebook", "()Z"); |
| | | midIsDeXMode = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, |
| | | "isDeXMode", "()Z"); |
| | | midManualBackButton = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, |
| | | "manualBackButton", "()V"); |
| | | midInputGetInputDeviceIds = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, |
| | | "inputGetInputDeviceIds", "(I)[I"); |
| | | midSendMessage = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, |
| | | "sendMessage", "(II)Z"); |
| | | midShowTextInput = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, |
| | | "showTextInput", "(IIII)Z"); |
| | | midIsScreenKeyboardShown = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, |
| | | "isScreenKeyboardShown","()Z"); |
| | | midClipboardSetText = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, |
| | | "clipboardSetText", "(Ljava/lang/String;)V"); |
| | | midClipboardGetText = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, |
| | | "clipboardGetText", "()Ljava/lang/String;"); |
| | | midClipboardHasText = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, |
| | | "clipboardHasText", "()Z"); |
| | | midOpenAPKExpansionInputStream = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, |
| | | "openAPKExpansionInputStream", "(Ljava/lang/String;)Ljava/io/InputStream;"); |
| | | |
| | | midGetManifestEnvironmentVariables = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, |
| | | "getManifestEnvironmentVariables", "()Z"); |
| | | |
| | | midGetDisplayDPI = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "getDisplayDPI", "()Landroid/util/DisplayMetrics;"); |
| | | midCreateCustomCursor = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "createCustomCursor", "([IIIII)I"); |
| | | midSetCustomCursor = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "setCustomCursor", "(I)Z"); |
| | | midSetSystemCursor = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "setSystemCursor", "(I)Z"); |
| | | |
| | | midSupportsRelativeMouse = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "supportsRelativeMouse", "()Z"); |
| | | midSetRelativeMouseEnabled = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "setRelativeMouseEnabled", "(Z)Z"); |
| | | |
| | | |
| | | if (!midGetNativeSurface || |
| | | !midSetActivityTitle || !midSetWindowStyle || !midSetOrientation || !midGetContext || !midIsTablet || !midIsAndroidTV || !midInputGetInputDeviceIds || |
| | | !midSendMessage || !midShowTextInput || !midIsScreenKeyboardShown || |
| | | !midClipboardSetText || !midClipboardGetText || !midClipboardHasText || |
| | | !midOpenAPKExpansionInputStream || !midGetManifestEnvironmentVariables || !midGetDisplayDPI || |
| | | !midCreateCustomCursor || !midSetCustomCursor || !midSetSystemCursor || !midSupportsRelativeMouse || !midSetRelativeMouseEnabled || |
| | | !midIsChromebook || !midIsDeXMode || !midManualBackButton) { |
| | | __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLActivity.java?"); |
| | | if (mJavaVM == NULL) { |
| | | __android_log_print(ANDROID_LOG_ERROR, "SDL", "failed to found a JavaVM"); |
| | | } |
| | | |
| | | fidSeparateMouseAndTouch = (*mEnv)->GetStaticFieldID(mEnv, mActivityClass, "mSeparateMouseAndTouch", "Z"); |
| | | /* Use a mutex to prevent concurrency issues between Java Activity and Native thread code, when using 'Android_Window'. |
| | | * (Eg. Java sending Touch events, while native code is destroying the main SDL_Window. ) |
| | | */ |
| | | if (Android_ActivityMutex == NULL) { |
| | | Android_ActivityMutex = SDL_CreateMutex(); /* Could this be created twice if onCreate() is called a second time ? */ |
| | | } |
| | | |
| | | if (!fidSeparateMouseAndTouch) { |
| | | __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java static fields, do you have the latest version of SDLActivity.java?"); |
| | | if (Android_ActivityMutex == NULL) { |
| | | __android_log_print(ANDROID_LOG_ERROR, "SDL", "failed to create Android_ActivityMutex mutex"); |
| | | } |
| | | |
| | | |
| | | Android_PauseSem = SDL_CreateSemaphore(0); |
| | | if (Android_PauseSem == NULL) { |
| | | __android_log_print(ANDROID_LOG_ERROR, "SDL", "failed to create Android_PauseSem semaphore"); |
| | | } |
| | | |
| | | Android_ResumeSem = SDL_CreateSemaphore(0); |
| | | if (Android_ResumeSem == NULL) { |
| | | __android_log_print(ANDROID_LOG_ERROR, "SDL", "failed to create Android_ResumeSem semaphore"); |
| | | } |
| | | |
| | | mActivityClass = (jclass)((*env)->NewGlobalRef(env, cls)); |
| | | |
| | | midClipboardGetText = (*env)->GetStaticMethodID(env, mActivityClass, "clipboardGetText", "()Ljava/lang/String;"); |
| | | midClipboardHasText = (*env)->GetStaticMethodID(env, mActivityClass, "clipboardHasText", "()Z"); |
| | | midClipboardSetText = (*env)->GetStaticMethodID(env, mActivityClass, "clipboardSetText", "(Ljava/lang/String;)V"); |
| | | midCreateCustomCursor = (*env)->GetStaticMethodID(env, mActivityClass, "createCustomCursor", "([IIIII)I"); |
| | | midGetContext = (*env)->GetStaticMethodID(env, mActivityClass, "getContext","()Landroid/content/Context;"); |
| | | midGetDisplayDPI = (*env)->GetStaticMethodID(env, mActivityClass, "getDisplayDPI", "()Landroid/util/DisplayMetrics;"); |
| | | midGetManifestEnvironmentVariables = (*env)->GetStaticMethodID(env, mActivityClass, "getManifestEnvironmentVariables", "()Z"); |
| | | midGetNativeSurface = (*env)->GetStaticMethodID(env, mActivityClass, "getNativeSurface","()Landroid/view/Surface;"); |
| | | midInitTouch = (*env)->GetStaticMethodID(env, mActivityClass, "initTouch", "()V"); |
| | | midIsAndroidTV = (*env)->GetStaticMethodID(env, mActivityClass, "isAndroidTV","()Z"); |
| | | midIsChromebook = (*env)->GetStaticMethodID(env, mActivityClass, "isChromebook", "()Z"); |
| | | midIsDeXMode = (*env)->GetStaticMethodID(env, mActivityClass, "isDeXMode", "()Z"); |
| | | midIsScreenKeyboardShown = (*env)->GetStaticMethodID(env, mActivityClass, "isScreenKeyboardShown","()Z"); |
| | | midIsTablet = (*env)->GetStaticMethodID(env, mActivityClass, "isTablet", "()Z"); |
| | | midManualBackButton = (*env)->GetStaticMethodID(env, mActivityClass, "manualBackButton", "()V"); |
| | | midMinimizeWindow = (*env)->GetStaticMethodID(env, mActivityClass, "minimizeWindow","()V"); |
| | | midOpenAPKExpansionInputStream = (*env)->GetStaticMethodID(env, mActivityClass, "openAPKExpansionInputStream", "(Ljava/lang/String;)Ljava/io/InputStream;"); |
| | | midRequestPermission = (*env)->GetStaticMethodID(env, mActivityClass, "requestPermission", "(Ljava/lang/String;I)V"); |
| | | midSendMessage = (*env)->GetStaticMethodID(env, mActivityClass, "sendMessage", "(II)Z"); |
| | | midSetActivityTitle = (*env)->GetStaticMethodID(env, mActivityClass, "setActivityTitle","(Ljava/lang/String;)Z"); |
| | | midSetCustomCursor = (*env)->GetStaticMethodID(env, mActivityClass, "setCustomCursor", "(I)Z"); |
| | | midSetOrientation = (*env)->GetStaticMethodID(env, mActivityClass, "setOrientation","(IIZLjava/lang/String;)V"); |
| | | midSetRelativeMouseEnabled = (*env)->GetStaticMethodID(env, mActivityClass, "setRelativeMouseEnabled", "(Z)Z"); |
| | | midSetSurfaceViewFormat = (*env)->GetStaticMethodID(env, mActivityClass, "setSurfaceViewFormat","(I)V"); |
| | | midSetSystemCursor = (*env)->GetStaticMethodID(env, mActivityClass, "setSystemCursor", "(I)Z"); |
| | | midSetWindowStyle = (*env)->GetStaticMethodID(env, mActivityClass, "setWindowStyle","(Z)V"); |
| | | midShouldMinimizeOnFocusLoss = (*env)->GetStaticMethodID(env, mActivityClass, "shouldMinimizeOnFocusLoss","()Z"); |
| | | midShowTextInput = (*env)->GetStaticMethodID(env, mActivityClass, "showTextInput", "(IIII)Z"); |
| | | midSupportsRelativeMouse = (*env)->GetStaticMethodID(env, mActivityClass, "supportsRelativeMouse", "()Z"); |
| | | |
| | | if (!midClipboardGetText || |
| | | !midClipboardHasText || |
| | | !midClipboardSetText || |
| | | !midCreateCustomCursor || |
| | | !midGetContext || |
| | | !midGetDisplayDPI || |
| | | !midGetManifestEnvironmentVariables || |
| | | !midGetNativeSurface || |
| | | !midInitTouch || |
| | | !midIsAndroidTV || |
| | | !midIsChromebook || |
| | | !midIsDeXMode || |
| | | !midIsScreenKeyboardShown || |
| | | !midIsTablet || |
| | | !midManualBackButton || |
| | | !midMinimizeWindow || |
| | | !midOpenAPKExpansionInputStream || |
| | | !midRequestPermission || |
| | | !midSendMessage || |
| | | !midSetActivityTitle || |
| | | !midSetCustomCursor || |
| | | !midSetOrientation || |
| | | !midSetRelativeMouseEnabled || |
| | | !midSetSurfaceViewFormat || |
| | | !midSetSystemCursor || |
| | | !midSetWindowStyle || |
| | | !midShouldMinimizeOnFocusLoss || |
| | | !midShowTextInput || |
| | | !midSupportsRelativeMouse) { |
| | | __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLActivity.java?"); |
| | | } |
| | | |
| | | checkJNIReady(); |
| | | } |
| | | |
| | | /* Audio initialization -- called before SDL_main() to initialize JNI bindings */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv* mEnv, jclass cls) |
| | | JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cls) |
| | | { |
| | | __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "AUDIO nativeSetupJNI()"); |
| | | |
| | | Android_JNI_SetupThread(); |
| | | mAudioManagerClass = (jclass)((*env)->NewGlobalRef(env, cls)); |
| | | |
| | | mAudioManagerClass = (jclass)((*mEnv)->NewGlobalRef(mEnv, cls)); |
| | | |
| | | midAudioOpen = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass, |
| | | midAudioOpen = (*env)->GetStaticMethodID(env, mAudioManagerClass, |
| | | "audioOpen", "(IIII)[I"); |
| | | midAudioWriteByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass, |
| | | midAudioWriteByteBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass, |
| | | "audioWriteByteBuffer", "([B)V"); |
| | | midAudioWriteShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass, |
| | | midAudioWriteShortBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass, |
| | | "audioWriteShortBuffer", "([S)V"); |
| | | midAudioWriteFloatBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass, |
| | | midAudioWriteFloatBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass, |
| | | "audioWriteFloatBuffer", "([F)V"); |
| | | midAudioClose = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass, |
| | | midAudioClose = (*env)->GetStaticMethodID(env, mAudioManagerClass, |
| | | "audioClose", "()V"); |
| | | midCaptureOpen = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass, |
| | | midCaptureOpen = (*env)->GetStaticMethodID(env, mAudioManagerClass, |
| | | "captureOpen", "(IIII)[I"); |
| | | midCaptureReadByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass, |
| | | midCaptureReadByteBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass, |
| | | "captureReadByteBuffer", "([BZ)I"); |
| | | midCaptureReadShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass, |
| | | midCaptureReadShortBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass, |
| | | "captureReadShortBuffer", "([SZ)I"); |
| | | midCaptureReadFloatBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass, |
| | | midCaptureReadFloatBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass, |
| | | "captureReadFloatBuffer", "([FZ)I"); |
| | | midCaptureClose = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass, |
| | | midCaptureClose = (*env)->GetStaticMethodID(env, mAudioManagerClass, |
| | | "captureClose", "()V"); |
| | | midAudioSetThreadPriority = (*env)->GetStaticMethodID(env, mAudioManagerClass, |
| | | "audioSetThreadPriority", "(ZI)V"); |
| | | |
| | | if (!midAudioOpen || !midAudioWriteByteBuffer || !midAudioWriteShortBuffer || !midAudioWriteFloatBuffer || !midAudioClose || |
| | | !midCaptureOpen || !midCaptureReadByteBuffer || !midCaptureReadShortBuffer || !midCaptureReadFloatBuffer || !midCaptureClose) { |
| | | !midCaptureOpen || !midCaptureReadByteBuffer || !midCaptureReadShortBuffer || !midCaptureReadFloatBuffer || !midCaptureClose || !midAudioSetThreadPriority) { |
| | | __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLAudioManager.java?"); |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | /* Controller initialization -- called before SDL_main() to initialize JNI bindings */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(JNIEnv* mEnv, jclass cls) |
| | | JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cls) |
| | | { |
| | | __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "CONTROLLER nativeSetupJNI()"); |
| | | |
| | | Android_JNI_SetupThread(); |
| | | mControllerManagerClass = (jclass)((*env)->NewGlobalRef(env, cls)); |
| | | |
| | | mControllerManagerClass = (jclass)((*mEnv)->NewGlobalRef(mEnv, cls)); |
| | | |
| | | midPollInputDevices = (*mEnv)->GetStaticMethodID(mEnv, mControllerManagerClass, |
| | | midPollInputDevices = (*env)->GetStaticMethodID(env, mControllerManagerClass, |
| | | "pollInputDevices", "()V"); |
| | | midPollHapticDevices = (*mEnv)->GetStaticMethodID(mEnv, mControllerManagerClass, |
| | | midPollHapticDevices = (*env)->GetStaticMethodID(env, mControllerManagerClass, |
| | | "pollHapticDevices", "()V"); |
| | | midHapticRun = (*mEnv)->GetStaticMethodID(mEnv, mControllerManagerClass, |
| | | midHapticRun = (*env)->GetStaticMethodID(env, mControllerManagerClass, |
| | | "hapticRun", "(IFI)V"); |
| | | midHapticStop = (*mEnv)->GetStaticMethodID(mEnv, mControllerManagerClass, |
| | | midHapticStop = (*env)->GetStaticMethodID(env, mControllerManagerClass, |
| | | "hapticStop", "(I)V"); |
| | | |
| | | if (!midPollInputDevices || !midPollHapticDevices || !midHapticRun || !midHapticStop) { |
| | |
| | | typedef int (*SDL_main_func)(int argc, char *argv[]); |
| | | |
| | | /* Start up the SDL app */ |
| | | JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeRunMain)(JNIEnv* env, jclass cls, jstring library, jstring function, jobject array) |
| | | JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeRunMain)(JNIEnv *env, jclass cls, jstring library, jstring function, jobject array) |
| | | { |
| | | int status = -1; |
| | | const char *library_file; |
| | |
| | | |
| | | __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeRunMain()"); |
| | | |
| | | /* Save JNIEnv of SDLThread */ |
| | | Android_JNI_SetEnv(env); |
| | | |
| | | library_file = (*env)->GetStringUTFChars(env, library, NULL); |
| | | library_handle = dlopen(library_file, RTLD_GLOBAL); |
| | | |
| | | if (!library_handle) { |
| | | /* When deploying android app bundle format uncompressed native libs may not extract from apk to filesystem. |
| | | In this case we should use lib name without path. https://bugzilla.libsdl.org/show_bug.cgi?id=4739 */ |
| | | const char *library_name = SDL_strrchr(library_file, '/'); |
| | | if (library_name && *library_name) { |
| | | library_name += 1; |
| | | library_handle = dlopen(library_name, RTLD_GLOBAL); |
| | | } |
| | | } |
| | | |
| | | if (library_handle) { |
| | | const char *function_name; |
| | | SDL_main_func SDL_main; |
| | |
| | | int argc; |
| | | int len; |
| | | char **argv; |
| | | SDL_bool isstack; |
| | | |
| | | /* Prepare the arguments. */ |
| | | len = (*env)->GetArrayLength(env, array); |
| | | argv = SDL_stack_alloc(char*, 1 + len + 1); |
| | | argv = SDL_small_alloc(char *, 1 + len + 1, &isstack); /* !!! FIXME: check for NULL */ |
| | | argc = 0; |
| | | /* Use the name "app_process" so PHYSFS_platformCalcBaseDir() works. |
| | | https://bitbucket.org/MartinFelis/love-android-sdl2/issue/23/release-build-crash-on-start |
| | | */ |
| | | argv[argc++] = SDL_strdup("app_process"); |
| | | for (i = 0; i < len; ++i) { |
| | | const char* utf; |
| | | char* arg = NULL; |
| | | const char *utf; |
| | | char *arg = NULL; |
| | | jstring string = (*env)->GetObjectArrayElement(env, array, i); |
| | | if (string) { |
| | | utf = (*env)->GetStringUTFChars(env, string, 0); |
| | |
| | | for (i = 0; i < argc; ++i) { |
| | | SDL_free(argv[i]); |
| | | } |
| | | SDL_stack_free(argv); |
| | | SDL_small_free(argv, isstack); |
| | | |
| | | } else { |
| | | __android_log_print(ANDROID_LOG_ERROR, "SDL", "nativeRunMain(): Couldn't find function %s in library %s", function_name, library_file); |
| | |
| | | } |
| | | (*env)->ReleaseStringUTFChars(env, library, library_file); |
| | | |
| | | /* This is a Java thread, it doesn't need to be Detached from the JVM. |
| | | * Set to mThreadKey value to NULL not to call pthread_create destructor 'Android_JNI_ThreadDestroyed' */ |
| | | Android_JNI_SetEnv(NULL); |
| | | |
| | | /* Do not issue an exit or the whole application will terminate instead of just the SDL thread */ |
| | | /* exit(status); */ |
| | | |
| | |
| | | |
| | | /* Drop file */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeDropFile)( |
| | | JNIEnv* env, jclass jcls, |
| | | JNIEnv *env, jclass jcls, |
| | | jstring filename) |
| | | { |
| | | const char *path = (*env)->GetStringUTFChars(env, filename, NULL); |
| | |
| | | SDL_SendDropComplete(NULL); |
| | | } |
| | | |
| | | /* Resize */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeResize)( |
| | | JNIEnv* env, jclass jcls, |
| | | /* Lock / Unlock Mutex */ |
| | | void Android_ActivityMutex_Lock() { |
| | | SDL_LockMutex(Android_ActivityMutex); |
| | | } |
| | | |
| | | void Android_ActivityMutex_Unlock() { |
| | | SDL_UnlockMutex(Android_ActivityMutex); |
| | | } |
| | | |
| | | /* Lock the Mutex when the Activity is in its 'Running' state */ |
| | | void Android_ActivityMutex_Lock_Running() { |
| | | int pauseSignaled = 0; |
| | | int resumeSignaled = 0; |
| | | |
| | | retry: |
| | | |
| | | SDL_LockMutex(Android_ActivityMutex); |
| | | |
| | | pauseSignaled = SDL_SemValue(Android_PauseSem); |
| | | resumeSignaled = SDL_SemValue(Android_ResumeSem); |
| | | |
| | | if (pauseSignaled > resumeSignaled) { |
| | | SDL_UnlockMutex(Android_ActivityMutex); |
| | | SDL_Delay(50); |
| | | goto retry; |
| | | } |
| | | } |
| | | |
| | | /* Set screen resolution */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetScreenResolution)( |
| | | JNIEnv *env, jclass jcls, |
| | | jint surfaceWidth, jint surfaceHeight, |
| | | jint deviceWidth, jint deviceHeight, jint format, jfloat rate) |
| | | { |
| | | SDL_LockMutex(Android_ActivityMutex); |
| | | |
| | | Android_SetScreenResolution(surfaceWidth, surfaceHeight, deviceWidth, deviceHeight, format, rate); |
| | | |
| | | SDL_UnlockMutex(Android_ActivityMutex); |
| | | } |
| | | |
| | | /* Resize */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeResize)( |
| | | JNIEnv *env, jclass jcls) |
| | | { |
| | | SDL_LockMutex(Android_ActivityMutex); |
| | | |
| | | if (Android_Window) |
| | | { |
| | | Android_SendResize(Android_Window); |
| | | } |
| | | |
| | | SDL_UnlockMutex(Android_ActivityMutex); |
| | | } |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeOrientationChanged)( |
| | | JNIEnv *env, jclass jcls, |
| | | jint orientation) |
| | | { |
| | | SDL_VideoDisplay *display = SDL_GetDisplay(0); |
| | | SDL_SendDisplayEvent(display, SDL_DISPLAYEVENT_ORIENTATION, orientation); |
| | | SDL_LockMutex(Android_ActivityMutex); |
| | | |
| | | displayOrientation = (SDL_DisplayOrientation)orientation; |
| | | |
| | | if (Android_Window) |
| | | { |
| | | SDL_VideoDisplay *display = SDL_GetDisplay(0); |
| | | SDL_SendDisplayEvent(display, SDL_DISPLAYEVENT_ORIENTATION, orientation); |
| | | } |
| | | |
| | | SDL_UnlockMutex(Android_ActivityMutex); |
| | | } |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeAddTouch)( |
| | | JNIEnv* env, jclass cls, |
| | | jint touchId, jstring name) |
| | | { |
| | | const char *utfname = (*env)->GetStringUTFChars(env, name, NULL); |
| | | |
| | | SDL_AddTouch((SDL_TouchID) touchId, SDL_TOUCH_DEVICE_DIRECT, utfname); |
| | | |
| | | (*env)->ReleaseStringUTFChars(env, name, utfname); |
| | | } |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePermissionResult)( |
| | | JNIEnv* env, jclass cls, |
| | | jint requestCode, jboolean result) |
| | | { |
| | | bPermissionRequestResult = result; |
| | | SDL_AtomicSet(&bPermissionRequestPending, SDL_FALSE); |
| | | } |
| | | |
| | | /* Paddown */ |
| | | JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)( |
| | | JNIEnv* env, jclass jcls, |
| | | JNIEnv *env, jclass jcls, |
| | | jint device_id, jint keycode) |
| | | { |
| | | return Android_OnPadDown(device_id, keycode); |
| | |
| | | |
| | | /* Padup */ |
| | | JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)( |
| | | JNIEnv* env, jclass jcls, |
| | | JNIEnv *env, jclass jcls, |
| | | jint device_id, jint keycode) |
| | | { |
| | | return Android_OnPadUp(device_id, keycode); |
| | |
| | | |
| | | /* Joy */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)( |
| | | JNIEnv* env, jclass jcls, |
| | | JNIEnv *env, jclass jcls, |
| | | jint device_id, jint axis, jfloat value) |
| | | { |
| | | Android_OnJoy(device_id, axis, value); |
| | |
| | | |
| | | /* POV Hat */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)( |
| | | JNIEnv* env, jclass jcls, |
| | | JNIEnv *env, jclass jcls, |
| | | jint device_id, jint hat_id, jint x, jint y) |
| | | { |
| | | Android_OnHat(device_id, hat_id, x, y); |
| | |
| | | |
| | | |
| | | JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)( |
| | | JNIEnv* env, jclass jcls, |
| | | JNIEnv *env, jclass jcls, |
| | | jint device_id, jstring device_name, jstring device_desc, |
| | | jint vendor_id, jint product_id, jboolean is_accelerometer, |
| | | jint button_mask, jint naxes, jint nhats, jint nballs) |
| | |
| | | } |
| | | |
| | | JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)( |
| | | JNIEnv* env, jclass jcls, |
| | | JNIEnv *env, jclass jcls, |
| | | jint device_id) |
| | | { |
| | | return Android_RemoveJoystick(device_id); |
| | | } |
| | | |
| | | JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic)( |
| | | JNIEnv* env, jclass jcls, jint device_id, jstring device_name) |
| | | JNIEnv *env, jclass jcls, jint device_id, jstring device_name) |
| | | { |
| | | int retval; |
| | | const char *name = (*env)->GetStringUTFChars(env, device_name, NULL); |
| | |
| | | } |
| | | |
| | | JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)( |
| | | JNIEnv* env, jclass jcls, jint device_id) |
| | | JNIEnv *env, jclass jcls, jint device_id) |
| | | { |
| | | return Android_RemoveHaptic(device_id); |
| | | } |
| | | |
| | | |
| | | /* Surface Created */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(JNIEnv* env, jclass jcls) |
| | | /* Called from surfaceCreated() */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceCreated)(JNIEnv *env, jclass jcls) |
| | | { |
| | | SDL_WindowData *data; |
| | | SDL_VideoDevice *_this; |
| | | SDL_LockMutex(Android_ActivityMutex); |
| | | |
| | | if (Android_Window == NULL || Android_Window->driverdata == NULL ) { |
| | | return; |
| | | } |
| | | if (Android_Window) |
| | | { |
| | | SDL_WindowData *data = (SDL_WindowData *) Android_Window->driverdata; |
| | | |
| | | _this = SDL_GetVideoDevice(); |
| | | data = (SDL_WindowData *) Android_Window->driverdata; |
| | | |
| | | /* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */ |
| | | if (data->egl_surface == EGL_NO_SURFACE) { |
| | | if(data->native_window) { |
| | | ANativeWindow_release(data->native_window); |
| | | } |
| | | data->native_window = Android_JNI_GetNativeWindow(); |
| | | data->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType) data->native_window); |
| | | if (data->native_window == NULL) { |
| | | SDL_SetError("Could not fetch native window from UI thread"); |
| | | } |
| | | } |
| | | |
| | | /* GL Context handling is done in the event loop because this function is run from the Java thread */ |
| | | |
| | | SDL_UnlockMutex(Android_ActivityMutex); |
| | | } |
| | | |
| | | /* Surface Destroyed */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)(JNIEnv* env, jclass jcls) |
| | | /* Called from surfaceChanged() */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(JNIEnv *env, jclass jcls) |
| | | { |
| | | /* We have to clear the current context and destroy the egl surface here |
| | | * Otherwise there's BAD_NATIVE_WINDOW errors coming from eglCreateWindowSurface on resume |
| | | * Ref: http://stackoverflow.com/questions/8762589/eglcreatewindowsurface-on-ics-and-switching-from-2d-to-3d |
| | | */ |
| | | SDL_WindowData *data; |
| | | SDL_VideoDevice *_this; |
| | | SDL_LockMutex(Android_ActivityMutex); |
| | | |
| | | if (Android_Window == NULL || Android_Window->driverdata == NULL ) { |
| | | return; |
| | | if (Android_Window) |
| | | { |
| | | SDL_VideoDevice *_this = SDL_GetVideoDevice(); |
| | | SDL_WindowData *data = (SDL_WindowData *) Android_Window->driverdata; |
| | | |
| | | /* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */ |
| | | if (data->egl_surface == EGL_NO_SURFACE) { |
| | | data->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType) data->native_window); |
| | | } |
| | | |
| | | /* GL Context handling is done in the event loop because this function is run from the Java thread */ |
| | | } |
| | | |
| | | _this = SDL_GetVideoDevice(); |
| | | data = (SDL_WindowData *) Android_Window->driverdata; |
| | | SDL_UnlockMutex(Android_ActivityMutex); |
| | | } |
| | | |
| | | if (data->egl_surface != EGL_NO_SURFACE) { |
| | | SDL_EGL_MakeCurrent(_this, NULL, NULL); |
| | | SDL_EGL_DestroySurface(_this, data->egl_surface); |
| | | data->egl_surface = EGL_NO_SURFACE; |
| | | /* Called from surfaceDestroyed() */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)(JNIEnv *env, jclass jcls) |
| | | { |
| | | int nb_attempt = 50; |
| | | |
| | | retry: |
| | | |
| | | SDL_LockMutex(Android_ActivityMutex); |
| | | |
| | | if (Android_Window) |
| | | { |
| | | SDL_VideoDevice *_this = SDL_GetVideoDevice(); |
| | | SDL_WindowData *data = (SDL_WindowData *) Android_Window->driverdata; |
| | | |
| | | /* Wait for Main thread being paused and context un-activated to release 'egl_surface' */ |
| | | if (! data->backup_done) { |
| | | nb_attempt -= 1; |
| | | if (nb_attempt == 0) { |
| | | SDL_SetError("Try to release egl_surface with context probably still active"); |
| | | } else { |
| | | SDL_UnlockMutex(Android_ActivityMutex); |
| | | SDL_Delay(10); |
| | | goto retry; |
| | | } |
| | | } |
| | | |
| | | if (data->egl_surface != EGL_NO_SURFACE) { |
| | | SDL_EGL_DestroySurface(_this, data->egl_surface); |
| | | data->egl_surface = EGL_NO_SURFACE; |
| | | } |
| | | |
| | | if (data->native_window) { |
| | | ANativeWindow_release(data->native_window); |
| | | data->native_window = NULL; |
| | | } |
| | | |
| | | /* GL Context handling is done in the event loop because this function is run from the Java thread */ |
| | | } |
| | | |
| | | /* GL Context handling is done in the event loop because this function is run from the Java thread */ |
| | | |
| | | SDL_UnlockMutex(Android_ActivityMutex); |
| | | } |
| | | |
| | | /* Keydown */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyDown)( |
| | | JNIEnv* env, jclass jcls, |
| | | JNIEnv *env, jclass jcls, |
| | | jint keycode) |
| | | { |
| | | Android_OnKeyDown(keycode); |
| | |
| | | |
| | | /* Keyup */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyUp)( |
| | | JNIEnv* env, jclass jcls, |
| | | JNIEnv *env, jclass jcls, |
| | | jint keycode) |
| | | { |
| | | Android_OnKeyUp(keycode); |
| | | } |
| | | |
| | | /* Virtual keyboard return key might stop text input */ |
| | | JNIEXPORT jboolean JNICALL SDL_JAVA_INTERFACE(onNativeSoftReturnKey)( |
| | | JNIEnv *env, jclass jcls) |
| | | { |
| | | if (SDL_GetHintBoolean(SDL_HINT_RETURN_KEY_HIDES_IME, SDL_FALSE)) { |
| | | SDL_StopTextInput(); |
| | | return JNI_TRUE; |
| | | } |
| | | return JNI_FALSE; |
| | | } |
| | | |
| | | /* Keyboard Focus Lost */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyboardFocusLost)( |
| | | JNIEnv* env, jclass jcls) |
| | | JNIEnv *env, jclass jcls) |
| | | { |
| | | /* Calling SDL_StopTextInput will take care of hiding the keyboard and cleaning up the DummyText widget */ |
| | | SDL_StopTextInput(); |
| | |
| | | |
| | | /* Touch */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeTouch)( |
| | | JNIEnv* env, jclass jcls, |
| | | JNIEnv *env, jclass jcls, |
| | | jint touch_device_id_in, jint pointer_finger_id_in, |
| | | jint action, jfloat x, jfloat y, jfloat p) |
| | | { |
| | | Android_OnTouch(touch_device_id_in, pointer_finger_id_in, action, x, y, p); |
| | | SDL_LockMutex(Android_ActivityMutex); |
| | | |
| | | Android_OnTouch(Android_Window, touch_device_id_in, pointer_finger_id_in, action, x, y, p); |
| | | |
| | | SDL_UnlockMutex(Android_ActivityMutex); |
| | | } |
| | | |
| | | /* Mouse */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeMouse)( |
| | | JNIEnv* env, jclass jcls, |
| | | JNIEnv *env, jclass jcls, |
| | | jint button, jint action, jfloat x, jfloat y, jboolean relative) |
| | | { |
| | | Android_OnMouse(button, action, x, y, relative); |
| | | SDL_LockMutex(Android_ActivityMutex); |
| | | |
| | | Android_OnMouse(Android_Window, button, action, x, y, relative); |
| | | |
| | | SDL_UnlockMutex(Android_ActivityMutex); |
| | | } |
| | | |
| | | /* Accelerometer */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeAccel)( |
| | | JNIEnv* env, jclass jcls, |
| | | JNIEnv *env, jclass jcls, |
| | | jfloat x, jfloat y, jfloat z) |
| | | { |
| | | fLastAccelerometer[0] = x; |
| | |
| | | |
| | | /* Clipboard */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeClipboardChanged)( |
| | | JNIEnv* env, jclass jcls) |
| | | JNIEnv *env, jclass jcls) |
| | | { |
| | | SDL_SendClipboardUpdate(); |
| | | } |
| | | |
| | | /* Low memory */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeLowMemory)( |
| | | JNIEnv* env, jclass cls) |
| | | JNIEnv *env, jclass cls) |
| | | { |
| | | SDL_SendAppEvent(SDL_APP_LOWMEMORY); |
| | | } |
| | | |
| | | /* Quit */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeQuit)( |
| | | JNIEnv* env, jclass cls) |
| | | /* Send Quit event to "SDLThread" thread */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSendQuit)( |
| | | JNIEnv *env, jclass cls) |
| | | { |
| | | /* Discard previous events. The user should have handled state storage |
| | | * in SDL_APP_WILLENTERBACKGROUND. After nativeQuit() is called, no |
| | | * in SDL_APP_WILLENTERBACKGROUND. After nativeSendQuit() is called, no |
| | | * events other than SDL_QUIT and SDL_APP_TERMINATING should fire */ |
| | | SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT); |
| | | /* Inject a SDL_QUIT event */ |
| | | SDL_SendQuit(); |
| | | SDL_SendAppEvent(SDL_APP_TERMINATING); |
| | | /* Robustness: clear any pending Pause */ |
| | | while (SDL_SemTryWait(Android_PauseSem) == 0) { |
| | | /* empty */ |
| | | } |
| | | /* Resume the event loop so that the app can catch SDL_QUIT which |
| | | * should now be the top event in the event queue. */ |
| | | if (!SDL_SemValue(Android_ResumeSem)) SDL_SemPost(Android_ResumeSem); |
| | | SDL_SemPost(Android_ResumeSem); |
| | | } |
| | | |
| | | /* Activity ends */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeQuit)( |
| | | JNIEnv *env, jclass cls) |
| | | { |
| | | const char *str; |
| | | |
| | | if (Android_ActivityMutex) { |
| | | SDL_DestroyMutex(Android_ActivityMutex); |
| | | Android_ActivityMutex = NULL; |
| | | } |
| | | |
| | | if (Android_PauseSem) { |
| | | SDL_DestroySemaphore(Android_PauseSem); |
| | | Android_PauseSem = NULL; |
| | | } |
| | | |
| | | if (Android_ResumeSem) { |
| | | SDL_DestroySemaphore(Android_ResumeSem); |
| | | Android_ResumeSem = NULL; |
| | | } |
| | | |
| | | str = SDL_GetError(); |
| | | if (str && str[0]) { |
| | | __android_log_print(ANDROID_LOG_ERROR, "SDL", "SDLActivity thread ends (error=%s)", str); |
| | | } else { |
| | | __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDLActivity thread ends"); |
| | | } |
| | | } |
| | | |
| | | /* Pause */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePause)( |
| | | JNIEnv* env, jclass cls) |
| | | JNIEnv *env, jclass cls) |
| | | { |
| | | __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativePause()"); |
| | | |
| | | if (Android_Window) { |
| | | SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_FOCUS_LOST, 0, 0); |
| | | SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_MINIMIZED, 0, 0); |
| | | SDL_SendAppEvent(SDL_APP_WILLENTERBACKGROUND); |
| | | SDL_SendAppEvent(SDL_APP_DIDENTERBACKGROUND); |
| | | |
| | | /* *After* sending the relevant events, signal the pause semaphore |
| | | * so the event loop knows to pause and (optionally) block itself */ |
| | | if (!SDL_SemValue(Android_PauseSem)) SDL_SemPost(Android_PauseSem); |
| | | } |
| | | /* Signal the pause semaphore so the event loop knows to pause and (optionally) block itself. |
| | | * Sometimes 2 pauses can be queued (eg pause/resume/pause), so it's always increased. */ |
| | | SDL_SemPost(Android_PauseSem); |
| | | } |
| | | |
| | | /* Resume */ |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeResume)( |
| | | JNIEnv* env, jclass cls) |
| | | JNIEnv *env, jclass cls) |
| | | { |
| | | __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeResume()"); |
| | | |
| | | /* Signal the resume semaphore so the event loop knows to resume and restore the GL Context |
| | | * We can't restore the GL Context here because it needs to be done on the SDL main thread |
| | | * and this function will be called from the Java thread instead. |
| | | */ |
| | | SDL_SemPost(Android_ResumeSem); |
| | | } |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeFocusChanged)( |
| | | JNIEnv *env, jclass cls, jboolean hasFocus) |
| | | { |
| | | SDL_LockMutex(Android_ActivityMutex); |
| | | |
| | | if (Android_Window) { |
| | | SDL_SendAppEvent(SDL_APP_WILLENTERFOREGROUND); |
| | | SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND); |
| | | SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_FOCUS_GAINED, 0, 0); |
| | | SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_RESTORED, 0, 0); |
| | | /* Signal the resume semaphore so the event loop knows to resume and restore the GL Context |
| | | * We can't restore the GL Context here because it needs to be done on the SDL main thread |
| | | * and this function will be called from the Java thread instead. |
| | | */ |
| | | if (!SDL_SemValue(Android_ResumeSem)) SDL_SemPost(Android_ResumeSem); |
| | | __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeFocusChanged()"); |
| | | SDL_SendWindowEvent(Android_Window, (hasFocus ? SDL_WINDOWEVENT_FOCUS_GAINED : SDL_WINDOWEVENT_FOCUS_LOST), 0, 0); |
| | | } |
| | | |
| | | SDL_UnlockMutex(Android_ActivityMutex); |
| | | } |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeCommitText)( |
| | | JNIEnv* env, jclass cls, |
| | | JNIEnv *env, jclass cls, |
| | | jstring text, jint newCursorPosition) |
| | | { |
| | | const char *utftext = (*env)->GetStringUTFChars(env, text, NULL); |
| | |
| | | } |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeGenerateScancodeForUnichar)( |
| | | JNIEnv* env, jclass cls, |
| | | JNIEnv *env, jclass cls, |
| | | jchar chUnicode) |
| | | { |
| | | SDL_Scancode code = SDL_SCANCODE_UNKNOWN; |
| | | uint16_t mod = 0; |
| | | |
| | | // We do not care about bigger than 127. |
| | | /* We do not care about bigger than 127. */ |
| | | if (chUnicode < 127) { |
| | | AndroidKeyInfo info = unicharToAndroidKeyInfoTable[chUnicode]; |
| | | code = info.code; |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeSetComposingText)( |
| | | JNIEnv* env, jclass cls, |
| | | JNIEnv *env, jclass cls, |
| | | jstring text, jint newCursorPosition) |
| | | { |
| | | const char *utftext = (*env)->GetStringUTFChars(env, text, NULL); |
| | |
| | | } |
| | | |
| | | JNIEXPORT jstring JNICALL SDL_JAVA_INTERFACE(nativeGetHint)( |
| | | JNIEnv* env, jclass cls, |
| | | JNIEnv *env, jclass cls, |
| | | jstring name) |
| | | { |
| | | const char *utfname = (*env)->GetStringUTFChars(env, name, NULL); |
| | |
| | | } |
| | | |
| | | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetenv)( |
| | | JNIEnv* env, jclass cls, |
| | | JNIEnv *env, jclass cls, |
| | | jstring name, jstring value) |
| | | { |
| | | const char *utfname = (*env)->GetStringUTFChars(env, name, NULL); |
| | |
| | | Functions called by SDL into Java |
| | | *******************************************************************************/ |
| | | |
| | | static int s_active = 0; |
| | | static SDL_atomic_t s_active; |
| | | struct LocalReferenceHolder |
| | | { |
| | | JNIEnv *m_env; |
| | |
| | | SDL_SetError("Failed to allocate enough JVM local references"); |
| | | return SDL_FALSE; |
| | | } |
| | | ++s_active; |
| | | SDL_AtomicIncRef(&s_active); |
| | | refholder->m_env = env; |
| | | return SDL_TRUE; |
| | | } |
| | |
| | | SDL_Log("Leaving function %s", refholder->m_func); |
| | | #endif |
| | | if (refholder->m_env) { |
| | | JNIEnv* env = refholder->m_env; |
| | | JNIEnv *env = refholder->m_env; |
| | | (*env)->PopLocalFrame(env, NULL); |
| | | --s_active; |
| | | SDL_AtomicDecRef(&s_active); |
| | | } |
| | | } |
| | | |
| | | static SDL_bool LocalReferenceHolder_IsActive(void) |
| | | { |
| | | return s_active > 0; |
| | | } |
| | | |
| | | ANativeWindow* Android_JNI_GetNativeWindow(void) |
| | | { |
| | | ANativeWindow* anw; |
| | | ANativeWindow *anw = NULL; |
| | | jobject s; |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | |
| | | s = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetNativeSurface); |
| | | anw = ANativeWindow_fromSurface(env, s); |
| | | (*env)->DeleteLocalRef(env, s); |
| | | if (s) { |
| | | anw = ANativeWindow_fromSurface(env, s); |
| | | (*env)->DeleteLocalRef(env, s); |
| | | } |
| | | |
| | | return anw; |
| | | } |
| | | |
| | | void Android_JNI_SetSurfaceViewFormat(int format) |
| | | { |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | int new_format = 0; |
| | | |
| | | /* Format from android/native_window.h, |
| | | * convert to temporary arbitrary values, |
| | | * then to java PixelFormat */ |
| | | if (format == WINDOW_FORMAT_RGBA_8888) { |
| | | new_format = 1; |
| | | } else if (format == WINDOW_FORMAT_RGBX_8888) { |
| | | new_format = 2; |
| | | } else if (format == WINDOW_FORMAT_RGB_565) { |
| | | /* Default */ |
| | | new_format = 0; |
| | | } |
| | | |
| | | (*env)->CallStaticVoidMethod(env, mActivityClass, midSetSurfaceViewFormat, new_format); |
| | | } |
| | | |
| | | void Android_JNI_SetActivityTitle(const char *title) |
| | | { |
| | | JNIEnv *mEnv = Android_JNI_GetEnv(); |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | |
| | | jstring jtitle = (jstring)((*mEnv)->NewStringUTF(mEnv, title)); |
| | | (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, midSetActivityTitle, jtitle); |
| | | (*mEnv)->DeleteLocalRef(mEnv, jtitle); |
| | | jstring jtitle = (*env)->NewStringUTF(env, title); |
| | | (*env)->CallStaticBooleanMethod(env, mActivityClass, midSetActivityTitle, jtitle); |
| | | (*env)->DeleteLocalRef(env, jtitle); |
| | | } |
| | | |
| | | void Android_JNI_SetWindowStyle(SDL_bool fullscreen) |
| | | { |
| | | JNIEnv *mEnv = Android_JNI_GetEnv(); |
| | | (*mEnv)->CallStaticVoidMethod(mEnv, mActivityClass, midSetWindowStyle, fullscreen ? 1 : 0); |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | (*env)->CallStaticVoidMethod(env, mActivityClass, midSetWindowStyle, fullscreen ? 1 : 0); |
| | | } |
| | | |
| | | void Android_JNI_SetOrientation(int w, int h, int resizable, const char *hint) |
| | | { |
| | | JNIEnv *mEnv = Android_JNI_GetEnv(); |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | |
| | | jstring jhint = (jstring)((*mEnv)->NewStringUTF(mEnv, (hint ? hint : ""))); |
| | | (*mEnv)->CallStaticVoidMethod(mEnv, mActivityClass, midSetOrientation, w, h, (resizable? 1 : 0), jhint); |
| | | (*mEnv)->DeleteLocalRef(mEnv, jhint); |
| | | jstring jhint = (*env)->NewStringUTF(env, (hint ? hint : "")); |
| | | (*env)->CallStaticVoidMethod(env, mActivityClass, midSetOrientation, w, h, (resizable? 1 : 0), jhint); |
| | | (*env)->DeleteLocalRef(env, jhint); |
| | | } |
| | | |
| | | void Android_JNI_MinizeWindow() |
| | | { |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | (*env)->CallStaticVoidMethod(env, mActivityClass, midMinimizeWindow); |
| | | } |
| | | |
| | | SDL_bool Android_JNI_ShouldMinimizeOnFocusLoss() |
| | | { |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | return (*env)->CallStaticBooleanMethod(env, mActivityClass, midShouldMinimizeOnFocusLoss); |
| | | } |
| | | |
| | | SDL_bool Android_JNI_GetAccelerometerValues(float values[3]) |
| | |
| | | return retval; |
| | | } |
| | | |
| | | 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); |
| | | pthread_setspecific(mThreadKey, NULL); |
| | | } |
| | | } |
| | | |
| | | JNIEnv* Android_JNI_GetEnv(void) |
| | | { |
| | | /* From http://developer.android.com/guide/practices/jni.html |
| | | * All threads are Linux threads, scheduled by the kernel. |
| | | * They're usually started from managed code (using Thread.start), but they can also be created elsewhere and then |
| | | * attached to the JavaVM. For example, a thread started with pthread_create can be attached with the |
| | | * JNI AttachCurrentThread or AttachCurrentThreadAsDaemon functions. Until a thread is attached, it has no JNIEnv, |
| | | * and cannot make JNI calls. |
| | | * Attaching a natively-created thread causes a java.lang.Thread object to be constructed and added to the "main" |
| | | * ThreadGroup, making it visible to the debugger. Calling AttachCurrentThread on an already-attached thread |
| | | * is a no-op. |
| | | * Note: You can call this function any number of times for the same thread, there's no harm in it |
| | | */ |
| | | |
| | | JNIEnv *env; |
| | | int status = (*mJavaVM)->AttachCurrentThread(mJavaVM, &env, NULL); |
| | | if(status < 0) { |
| | | LOGE("failed to attach current thread"); |
| | | return 0; |
| | | } |
| | | |
| | | /* From http://developer.android.com/guide/practices/jni.html |
| | | * Threads attached through JNI must call DetachCurrentThread before they exit. If coding this directly is awkward, |
| | | * in Android 2.0 (Eclair) and higher you can use pthread_key_create to define a destructor function that will be |
| | | * called before the thread exits, and call DetachCurrentThread from there. (Use that key with pthread_setspecific |
| | | * to store the JNIEnv in thread-local-storage; that way it'll be passed into your destructor as the argument.) |
| | | * Note: The destructor is not called unless the stored value is != NULL |
| | | * Note: You can call this function any number of times for the same thread, there's no harm in it |
| | | * (except for some lost CPU cycles) |
| | | */ |
| | | pthread_setspecific(mThreadKey, (void*) env); |
| | | |
| | | return env; |
| | | } |
| | | |
| | | int Android_JNI_SetupThread(void) |
| | | { |
| | | Android_JNI_GetEnv(); |
| | | return 1; |
| | | } |
| | | |
| | | /* |
| | | * Audio support |
| | | */ |
| | | static int audioBufferFormat = 0; |
| | | static jobject audioBuffer = NULL; |
| | | static void* audioBufferPinned = NULL; |
| | | static void *audioBufferPinned = NULL; |
| | | static int captureBufferFormat = 0; |
| | | static jobject captureBuffer = NULL; |
| | | |
| | | int Android_JNI_OpenAudioDevice(int iscapture, SDL_AudioSpec *spec) |
| | | { |
| | | int audioformat; |
| | | int numBufferFrames; |
| | | jobject jbufobj = NULL; |
| | | jobject result; |
| | | int *resultElements; |
| | | jboolean isCopy; |
| | | |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | |
| | | if (!env) { |
| | | LOGE("callback_handler: failed to attach current thread"); |
| | | } |
| | | Android_JNI_SetupThread(); |
| | | |
| | | switch (spec->format) { |
| | | case AUDIO_U8: |
| | |
| | | audioBufferFormat = audioformat; |
| | | audioBuffer = jbufobj; |
| | | } |
| | | numBufferFrames = (*env)->GetArrayLength(env, (jarray)jbufobj); |
| | | |
| | | if (!iscapture) { |
| | | isCopy = JNI_FALSE; |
| | |
| | | } |
| | | } |
| | | return 0; |
| | | } |
| | | |
| | | SDL_DisplayOrientation Android_JNI_GetDisplayOrientation(void) |
| | | { |
| | | return displayOrientation; |
| | | } |
| | | |
| | | int Android_JNI_GetDisplayDPI(float *ddpi, float *xdpi, float *ydpi) |
| | |
| | | |
| | | void Android_JNI_WriteAudioBuffer(void) |
| | | { |
| | | JNIEnv *mAudioEnv = Android_JNI_GetEnv(); |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | |
| | | switch (audioBufferFormat) { |
| | | case ENCODING_PCM_8BIT: |
| | | (*mAudioEnv)->ReleaseByteArrayElements(mAudioEnv, (jbyteArray)audioBuffer, (jbyte *)audioBufferPinned, JNI_COMMIT); |
| | | (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mAudioManagerClass, midAudioWriteByteBuffer, (jbyteArray)audioBuffer); |
| | | (*env)->ReleaseByteArrayElements(env, (jbyteArray)audioBuffer, (jbyte *)audioBufferPinned, JNI_COMMIT); |
| | | (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midAudioWriteByteBuffer, (jbyteArray)audioBuffer); |
| | | break; |
| | | case ENCODING_PCM_16BIT: |
| | | (*mAudioEnv)->ReleaseShortArrayElements(mAudioEnv, (jshortArray)audioBuffer, (jshort *)audioBufferPinned, JNI_COMMIT); |
| | | (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mAudioManagerClass, midAudioWriteShortBuffer, (jshortArray)audioBuffer); |
| | | (*env)->ReleaseShortArrayElements(env, (jshortArray)audioBuffer, (jshort *)audioBufferPinned, JNI_COMMIT); |
| | | (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midAudioWriteShortBuffer, (jshortArray)audioBuffer); |
| | | break; |
| | | case ENCODING_PCM_FLOAT: |
| | | (*mAudioEnv)->ReleaseFloatArrayElements(mAudioEnv, (jfloatArray)audioBuffer, (jfloat *)audioBufferPinned, JNI_COMMIT); |
| | | (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mAudioManagerClass, midAudioWriteFloatBuffer, (jfloatArray)audioBuffer); |
| | | (*env)->ReleaseFloatArrayElements(env, (jfloatArray)audioBuffer, (jfloat *)audioBufferPinned, JNI_COMMIT); |
| | | (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midAudioWriteFloatBuffer, (jfloatArray)audioBuffer); |
| | | break; |
| | | default: |
| | | __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: unhandled audio buffer format"); |
| | |
| | | { |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | jboolean isCopy = JNI_FALSE; |
| | | jint br; |
| | | jint br = -1; |
| | | |
| | | switch (captureBufferFormat) { |
| | | case ENCODING_PCM_8BIT: |
| | |
| | | if (br > 0) { |
| | | jbyte *ptr = (*env)->GetByteArrayElements(env, (jbyteArray)captureBuffer, &isCopy); |
| | | SDL_memcpy(buffer, ptr, br); |
| | | (*env)->ReleaseByteArrayElements(env, (jbyteArray)captureBuffer, (jbyte *)ptr, JNI_ABORT); |
| | | (*env)->ReleaseByteArrayElements(env, (jbyteArray)captureBuffer, ptr, JNI_ABORT); |
| | | } |
| | | break; |
| | | case ENCODING_PCM_16BIT: |
| | |
| | | jshort *ptr = (*env)->GetShortArrayElements(env, (jshortArray)captureBuffer, &isCopy); |
| | | br *= sizeof(Sint16); |
| | | SDL_memcpy(buffer, ptr, br); |
| | | (*env)->ReleaseShortArrayElements(env, (jshortArray)captureBuffer, (jshort *)ptr, JNI_ABORT); |
| | | (*env)->ReleaseShortArrayElements(env, (jshortArray)captureBuffer, ptr, JNI_ABORT); |
| | | } |
| | | break; |
| | | case ENCODING_PCM_FLOAT: |
| | |
| | | jfloat *ptr = (*env)->GetFloatArrayElements(env, (jfloatArray)captureBuffer, &isCopy); |
| | | br *= sizeof(float); |
| | | SDL_memcpy(buffer, ptr, br); |
| | | (*env)->ReleaseFloatArrayElements(env, (jfloatArray)captureBuffer, (jfloat *)ptr, JNI_ABORT); |
| | | (*env)->ReleaseFloatArrayElements(env, (jfloatArray)captureBuffer, ptr, JNI_ABORT); |
| | | } |
| | | break; |
| | | default: |
| | |
| | | } |
| | | } |
| | | |
| | | void Android_JNI_AudioSetThreadPriority(int iscapture, int device_id) |
| | | { |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midAudioSetThreadPriority, iscapture, device_id); |
| | | } |
| | | |
| | | /* Test for an exception and call SDL_SetError with its detail if one occurs */ |
| | | /* If the parameter silent is truthy then SDL_SetError() will not be called. */ |
| | | static SDL_bool Android_JNI_ExceptionOccurred(SDL_bool silent) |
| | | { |
| | | JNIEnv *mEnv = Android_JNI_GetEnv(); |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | jthrowable exception; |
| | | |
| | | SDL_assert(LocalReferenceHolder_IsActive()); |
| | | /* Detect mismatch LocalReferenceHolder_Init/Cleanup */ |
| | | SDL_assert(SDL_AtomicGet(&s_active) > 0); |
| | | |
| | | exception = (*mEnv)->ExceptionOccurred(mEnv); |
| | | exception = (*env)->ExceptionOccurred(env); |
| | | if (exception != NULL) { |
| | | jmethodID mid; |
| | | |
| | | /* Until this happens most JNI operations have undefined behaviour */ |
| | | (*mEnv)->ExceptionClear(mEnv); |
| | | (*env)->ExceptionClear(env); |
| | | |
| | | if (!silent) { |
| | | jclass exceptionClass = (*mEnv)->GetObjectClass(mEnv, exception); |
| | | jclass classClass = (*mEnv)->FindClass(mEnv, "java/lang/Class"); |
| | | jclass exceptionClass = (*env)->GetObjectClass(env, exception); |
| | | jclass classClass = (*env)->FindClass(env, "java/lang/Class"); |
| | | jstring exceptionName; |
| | | const char* exceptionNameUTF8; |
| | | const char *exceptionNameUTF8; |
| | | jstring exceptionMessage; |
| | | |
| | | mid = (*mEnv)->GetMethodID(mEnv, classClass, "getName", "()Ljava/lang/String;"); |
| | | exceptionName = (jstring)(*mEnv)->CallObjectMethod(mEnv, exceptionClass, mid); |
| | | exceptionNameUTF8 = (*mEnv)->GetStringUTFChars(mEnv, exceptionName, 0); |
| | | mid = (*env)->GetMethodID(env, classClass, "getName", "()Ljava/lang/String;"); |
| | | exceptionName = (jstring)(*env)->CallObjectMethod(env, exceptionClass, mid); |
| | | exceptionNameUTF8 = (*env)->GetStringUTFChars(env, exceptionName, 0); |
| | | |
| | | mid = (*mEnv)->GetMethodID(mEnv, exceptionClass, "getMessage", "()Ljava/lang/String;"); |
| | | exceptionMessage = (jstring)(*mEnv)->CallObjectMethod(mEnv, exception, mid); |
| | | mid = (*env)->GetMethodID(env, exceptionClass, "getMessage", "()Ljava/lang/String;"); |
| | | exceptionMessage = (jstring)(*env)->CallObjectMethod(env, exception, mid); |
| | | |
| | | if (exceptionMessage != NULL) { |
| | | const char* exceptionMessageUTF8 = (*mEnv)->GetStringUTFChars(mEnv, exceptionMessage, 0); |
| | | const char *exceptionMessageUTF8 = (*env)->GetStringUTFChars(env, exceptionMessage, 0); |
| | | SDL_SetError("%s: %s", exceptionNameUTF8, exceptionMessageUTF8); |
| | | (*mEnv)->ReleaseStringUTFChars(mEnv, exceptionMessage, exceptionMessageUTF8); |
| | | (*env)->ReleaseStringUTFChars(env, exceptionMessage, exceptionMessageUTF8); |
| | | } else { |
| | | SDL_SetError("%s", exceptionNameUTF8); |
| | | } |
| | | |
| | | (*mEnv)->ReleaseStringUTFChars(mEnv, exceptionName, exceptionNameUTF8); |
| | | (*env)->ReleaseStringUTFChars(env, exceptionName, exceptionNameUTF8); |
| | | } |
| | | |
| | | return SDL_TRUE; |
| | |
| | | return SDL_FALSE; |
| | | } |
| | | |
| | | static int Internal_Android_JNI_FileOpen(SDL_RWops* ctx) |
| | | static int Internal_Android_JNI_FileOpen(SDL_RWops *ctx) |
| | | { |
| | | struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); |
| | | |
| | |
| | | jclass fdCls; |
| | | jfieldID descriptor; |
| | | |
| | | JNIEnv *mEnv = Android_JNI_GetEnv(); |
| | | if (!LocalReferenceHolder_Init(&refs, mEnv)) { |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | if (!LocalReferenceHolder_Init(&refs, env)) { |
| | | goto failure; |
| | | } |
| | | |
| | |
| | | ctx->hidden.androidio.position = 0; |
| | | |
| | | /* context = SDLActivity.getContext(); */ |
| | | context = (*mEnv)->CallStaticObjectMethod(mEnv, mActivityClass, midGetContext); |
| | | context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext); |
| | | |
| | | /* assetManager = context.getAssets(); */ |
| | | mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, context), |
| | | mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context), |
| | | "getAssets", "()Landroid/content/res/AssetManager;"); |
| | | assetManager = (*mEnv)->CallObjectMethod(mEnv, context, mid); |
| | | assetManager = (*env)->CallObjectMethod(env, 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, (*mEnv)->GetObjectClass(mEnv, assetManager), "openFd", "(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;"); |
| | | inputStream = (*mEnv)->CallObjectMethod(mEnv, assetManager, mid, fileNameJString); |
| | | mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, assetManager), "openFd", "(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;"); |
| | | inputStream = (*env)->CallObjectMethod(env, assetManager, mid, fileNameJString); |
| | | if (Android_JNI_ExceptionOccurred(SDL_TRUE)) { |
| | | goto fallback; |
| | | } |
| | | |
| | | mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream), "getStartOffset", "()J"); |
| | | ctx->hidden.androidio.offset = (*mEnv)->CallLongMethod(mEnv, inputStream, mid); |
| | | mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, inputStream), "getStartOffset", "()J"); |
| | | ctx->hidden.androidio.offset = (long)(*env)->CallLongMethod(env, inputStream, mid); |
| | | if (Android_JNI_ExceptionOccurred(SDL_TRUE)) { |
| | | goto fallback; |
| | | } |
| | | |
| | | mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream), "getDeclaredLength", "()J"); |
| | | ctx->hidden.androidio.size = (*mEnv)->CallLongMethod(mEnv, inputStream, mid); |
| | | mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, inputStream), "getDeclaredLength", "()J"); |
| | | ctx->hidden.androidio.size = (long)(*env)->CallLongMethod(env, inputStream, mid); |
| | | if (Android_JNI_ExceptionOccurred(SDL_TRUE)) { |
| | | goto fallback; |
| | | } |
| | | |
| | | 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); |
| | | mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, inputStream), "getFileDescriptor", "()Ljava/io/FileDescriptor;"); |
| | | fd = (*env)->CallObjectMethod(env, inputStream, mid); |
| | | fdCls = (*env)->GetObjectClass(env, fd); |
| | | descriptor = (*env)->GetFieldID(env, fdCls, "descriptor", "I"); |
| | | ctx->hidden.androidio.fd = (*env)->GetIntField(env, fd, descriptor); |
| | | ctx->hidden.androidio.assetFileDescriptorRef = (*env)->NewGlobalRef(env, inputStream); |
| | | |
| | | /* Seek to the correct offset in the file. */ |
| | | lseek(ctx->hidden.androidio.fd, (off_t)ctx->hidden.androidio.offset, SEEK_SET); |
| | |
| | | ctx->hidden.androidio.assetFileDescriptorRef = NULL; |
| | | |
| | | /* inputStream = assetManager.open(<filename>); */ |
| | | mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, assetManager), |
| | | mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, assetManager), |
| | | "open", "(Ljava/lang/String;I)Ljava/io/InputStream;"); |
| | | inputStream = (*mEnv)->CallObjectMethod(mEnv, assetManager, mid, fileNameJString, 1 /* ACCESS_RANDOM */); |
| | | inputStream = (*env)->CallObjectMethod(env, assetManager, mid, fileNameJString, 1 /* ACCESS_RANDOM */); |
| | | if (Android_JNI_ExceptionOccurred(SDL_FALSE)) { |
| | | /* Try fallback to APK expansion files */ |
| | | inputStream = (*mEnv)->CallStaticObjectMethod(mEnv, mActivityClass, midOpenAPKExpansionInputStream, fileNameJString); |
| | | inputStream = (*env)->CallStaticObjectMethod(env, mActivityClass, midOpenAPKExpansionInputStream, fileNameJString); |
| | | |
| | | /* Exception is checked first because it always needs to be cleared. |
| | | * If no exception occurred then the last SDL error message is kept. |
| | |
| | | } |
| | | } |
| | | |
| | | ctx->hidden.androidio.inputStreamRef = (*mEnv)->NewGlobalRef(mEnv, inputStream); |
| | | ctx->hidden.androidio.inputStreamRef = (*env)->NewGlobalRef(env, inputStream); |
| | | |
| | | /* Despite all the visible documentation on [Asset]InputStream claiming |
| | | * that the .available() method is not guaranteed to return the entire file |
| | |
| | | */ |
| | | |
| | | /* size = inputStream.available(); */ |
| | | mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream), |
| | | mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, inputStream), |
| | | "available", "()I"); |
| | | ctx->hidden.androidio.size = (long)(*mEnv)->CallIntMethod(mEnv, inputStream, mid); |
| | | ctx->hidden.androidio.size = (long)(*env)->CallIntMethod(env, inputStream, mid); |
| | | if (Android_JNI_ExceptionOccurred(SDL_FALSE)) { |
| | | goto failure; |
| | | } |
| | | |
| | | /* readableByteChannel = Channels.newChannel(inputStream); */ |
| | | channels = (*mEnv)->FindClass(mEnv, "java/nio/channels/Channels"); |
| | | mid = (*mEnv)->GetStaticMethodID(mEnv, channels, |
| | | channels = (*env)->FindClass(env, "java/nio/channels/Channels"); |
| | | mid = (*env)->GetStaticMethodID(env, channels, |
| | | "newChannel", |
| | | "(Ljava/io/InputStream;)Ljava/nio/channels/ReadableByteChannel;"); |
| | | readableByteChannel = (*mEnv)->CallStaticObjectMethod( |
| | | mEnv, channels, mid, inputStream); |
| | | readableByteChannel = (*env)->CallStaticObjectMethod( |
| | | env, channels, mid, inputStream); |
| | | if (Android_JNI_ExceptionOccurred(SDL_FALSE)) { |
| | | goto failure; |
| | | } |
| | | |
| | | ctx->hidden.androidio.readableByteChannelRef = |
| | | (*mEnv)->NewGlobalRef(mEnv, readableByteChannel); |
| | | (*env)->NewGlobalRef(env, readableByteChannel); |
| | | |
| | | /* Store .read id for reading purposes */ |
| | | mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, readableByteChannel), |
| | | mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, readableByteChannel), |
| | | "read", "(Ljava/nio/ByteBuffer;)I"); |
| | | ctx->hidden.androidio.readMethod = mid; |
| | | } |
| | |
| | | failure: |
| | | result = -1; |
| | | |
| | | (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.fileNameRef); |
| | | (*env)->DeleteGlobalRef(env, (jobject)ctx->hidden.androidio.fileNameRef); |
| | | |
| | | if(ctx->hidden.androidio.inputStreamRef != NULL) { |
| | | (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.inputStreamRef); |
| | | (*env)->DeleteGlobalRef(env, (jobject)ctx->hidden.androidio.inputStreamRef); |
| | | } |
| | | |
| | | if(ctx->hidden.androidio.readableByteChannelRef != NULL) { |
| | | (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.readableByteChannelRef); |
| | | (*env)->DeleteGlobalRef(env, (jobject)ctx->hidden.androidio.readableByteChannelRef); |
| | | } |
| | | |
| | | if(ctx->hidden.androidio.assetFileDescriptorRef != NULL) { |
| | | (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.assetFileDescriptorRef); |
| | | (*env)->DeleteGlobalRef(env, (jobject)ctx->hidden.androidio.assetFileDescriptorRef); |
| | | } |
| | | |
| | | } |
| | |
| | | return result; |
| | | } |
| | | |
| | | int Android_JNI_FileOpen(SDL_RWops* ctx, |
| | | const char* fileName, const char* mode) |
| | | int Android_JNI_FileOpen(SDL_RWops *ctx, |
| | | const char *fileName, const char *mode) |
| | | { |
| | | struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); |
| | | JNIEnv *mEnv = Android_JNI_GetEnv(); |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | int retval; |
| | | jstring fileNameJString; |
| | | |
| | | if (!LocalReferenceHolder_Init(&refs, mEnv)) { |
| | | if (!LocalReferenceHolder_Init(&refs, env)) { |
| | | LocalReferenceHolder_Cleanup(&refs); |
| | | return -1; |
| | | } |
| | |
| | | return -1; |
| | | } |
| | | |
| | | fileNameJString = (*mEnv)->NewStringUTF(mEnv, fileName); |
| | | ctx->hidden.androidio.fileNameRef = (*mEnv)->NewGlobalRef(mEnv, fileNameJString); |
| | | fileNameJString = (*env)->NewStringUTF(env, fileName); |
| | | ctx->hidden.androidio.fileNameRef = (*env)->NewGlobalRef(env, fileNameJString); |
| | | ctx->hidden.androidio.inputStreamRef = NULL; |
| | | ctx->hidden.androidio.readableByteChannelRef = NULL; |
| | | ctx->hidden.androidio.readMethod = NULL; |
| | |
| | | return retval; |
| | | } |
| | | |
| | | 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) |
| | | { |
| | | struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); |
| | |
| | | jlong bytesRemaining = (jlong) (size * maxnum); |
| | | jlong bytesMax = (jlong) (ctx->hidden.androidio.size - ctx->hidden.androidio.position); |
| | | int bytesRead = 0; |
| | | JNIEnv *mEnv; |
| | | JNIEnv *env; |
| | | jobject readableByteChannel; |
| | | jmethodID readMethod; |
| | | jobject byteBuffer; |
| | |
| | | /* Don't read more bytes than those that remain in the file, otherwise we get an exception */ |
| | | if (bytesRemaining > bytesMax) bytesRemaining = bytesMax; |
| | | |
| | | mEnv = Android_JNI_GetEnv(); |
| | | if (!LocalReferenceHolder_Init(&refs, mEnv)) { |
| | | env = Android_JNI_GetEnv(); |
| | | if (!LocalReferenceHolder_Init(&refs, env)) { |
| | | LocalReferenceHolder_Cleanup(&refs); |
| | | return 0; |
| | | } |
| | | |
| | | readableByteChannel = (jobject)ctx->hidden.androidio.readableByteChannelRef; |
| | | readMethod = (jmethodID)ctx->hidden.androidio.readMethod; |
| | | byteBuffer = (*mEnv)->NewDirectByteBuffer(mEnv, buffer, bytesRemaining); |
| | | byteBuffer = (*env)->NewDirectByteBuffer(env, buffer, bytesRemaining); |
| | | |
| | | while (bytesRemaining > 0) { |
| | | /* result = readableByteChannel.read(...); */ |
| | | int result = (*mEnv)->CallIntMethod(mEnv, readableByteChannel, readMethod, byteBuffer); |
| | | int result = (*env)->CallIntMethod(env, readableByteChannel, readMethod, byteBuffer); |
| | | |
| | | if (Android_JNI_ExceptionOccurred(SDL_FALSE)) { |
| | | LocalReferenceHolder_Cleanup(&refs); |
| | |
| | | } |
| | | } |
| | | |
| | | 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 Internal_Android_JNI_FileClose(SDL_RWops* ctx, SDL_bool release) |
| | | static int Internal_Android_JNI_FileClose(SDL_RWops *ctx, SDL_bool release) |
| | | { |
| | | struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); |
| | | |
| | | int result = 0; |
| | | JNIEnv *mEnv = Android_JNI_GetEnv(); |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | |
| | | if (!LocalReferenceHolder_Init(&refs, mEnv)) { |
| | | if (!LocalReferenceHolder_Init(&refs, env)) { |
| | | LocalReferenceHolder_Cleanup(&refs); |
| | | return SDL_SetError("Failed to allocate enough JVM local references"); |
| | | } |
| | | |
| | | if (ctx) { |
| | | if (release) { |
| | | (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.fileNameRef); |
| | | (*env)->DeleteGlobalRef(env, (jobject)ctx->hidden.androidio.fileNameRef); |
| | | } |
| | | |
| | | if (ctx->hidden.androidio.assetFileDescriptorRef) { |
| | | jobject inputStream = (jobject)ctx->hidden.androidio.assetFileDescriptorRef; |
| | | jmethodID mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream), |
| | | jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, inputStream), |
| | | "close", "()V"); |
| | | (*mEnv)->CallVoidMethod(mEnv, inputStream, mid); |
| | | (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.assetFileDescriptorRef); |
| | | (*env)->CallVoidMethod(env, inputStream, mid); |
| | | (*env)->DeleteGlobalRef(env, (jobject)ctx->hidden.androidio.assetFileDescriptorRef); |
| | | if (Android_JNI_ExceptionOccurred(SDL_FALSE)) { |
| | | result = -1; |
| | | } |
| | |
| | | jobject inputStream = (jobject)ctx->hidden.androidio.inputStreamRef; |
| | | |
| | | /* inputStream.close(); */ |
| | | jmethodID mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream), |
| | | jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, inputStream), |
| | | "close", "()V"); |
| | | (*mEnv)->CallVoidMethod(mEnv, inputStream, mid); |
| | | (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.inputStreamRef); |
| | | (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.readableByteChannelRef); |
| | | (*env)->CallVoidMethod(env, inputStream, mid); |
| | | (*env)->DeleteGlobalRef(env, (jobject)ctx->hidden.androidio.inputStreamRef); |
| | | (*env)->DeleteGlobalRef(env, (jobject)ctx->hidden.androidio.readableByteChannelRef); |
| | | if (Android_JNI_ExceptionOccurred(SDL_FALSE)) { |
| | | result = -1; |
| | | } |
| | |
| | | } |
| | | |
| | | |
| | | Sint64 Android_JNI_FileSize(SDL_RWops* ctx) |
| | | Sint64 Android_JNI_FileSize(SDL_RWops *ctx) |
| | | { |
| | | return ctx->hidden.androidio.size; |
| | | } |
| | | |
| | | 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) { |
| | | off_t ret; |
| | |
| | | default: |
| | | return SDL_SetError("Unknown value for 'whence'"); |
| | | } |
| | | whence = SEEK_SET; |
| | | |
| | | ret = lseek(ctx->hidden.androidio.fd, (off_t)offset, SEEK_SET); |
| | | if (ret == -1) return -1; |
| | |
| | | if (amount > movement) { |
| | | amount = movement; |
| | | } |
| | | result = Android_JNI_FileRead(ctx, buffer, 1, amount); |
| | | result = Android_JNI_FileRead(ctx, buffer, 1, (size_t)amount); |
| | | if (result <= 0) { |
| | | /* Failed to read/skip the required amount, so fail */ |
| | | return -1; |
| | |
| | | |
| | | } |
| | | |
| | | int Android_JNI_FileClose(SDL_RWops* ctx) |
| | | int Android_JNI_FileClose(SDL_RWops *ctx) |
| | | { |
| | | return Internal_Android_JNI_FileClose(ctx, SDL_TRUE); |
| | | } |
| | | |
| | | int Android_JNI_SetClipboardText(const char* text) |
| | | int Android_JNI_SetClipboardText(const char *text) |
| | | { |
| | | JNIEnv* env = Android_JNI_GetEnv(); |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | jstring string = (*env)->NewStringUTF(env, text); |
| | | (*env)->CallStaticVoidMethod(env, mActivityClass, midClipboardSetText, string); |
| | | (*env)->DeleteLocalRef(env, string); |
| | |
| | | |
| | | char* Android_JNI_GetClipboardText(void) |
| | | { |
| | | JNIEnv* env = Android_JNI_GetEnv(); |
| | | char* text = NULL; |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | char *text = NULL; |
| | | jstring string; |
| | | |
| | | |
| | | string = (*env)->CallStaticObjectMethod(env, mActivityClass, midClipboardGetText); |
| | | if (string) { |
| | | const char* utf = (*env)->GetStringUTFChars(env, string, 0); |
| | | const char *utf = (*env)->GetStringUTFChars(env, string, 0); |
| | | if (utf) { |
| | | text = SDL_strdup(utf); |
| | | (*env)->ReleaseStringUTFChars(env, string, utf); |
| | | } |
| | | (*env)->DeleteLocalRef(env, string); |
| | | } |
| | | |
| | | |
| | | return (text == NULL) ? SDL_strdup("") : text; |
| | | } |
| | | |
| | | SDL_bool Android_JNI_HasClipboardText(void) |
| | | { |
| | | JNIEnv* env = Android_JNI_GetEnv(); |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | jboolean retval = (*env)->CallStaticBooleanMethod(env, mActivityClass, midClipboardHasText); |
| | | return (retval == JNI_TRUE) ? SDL_TRUE : SDL_FALSE; |
| | | } |
| | |
| | | * returns truthy or falsy value in plugged, charged and battery |
| | | * returns the value in seconds and percent or -1 if not available |
| | | */ |
| | | 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) |
| | | { |
| | | struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); |
| | | JNIEnv* env = Android_JNI_GetEnv(); |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | jmethodID mid; |
| | | jobject context; |
| | | jstring action; |
| | |
| | | #define GET_INT_EXTRA(var, key) \ |
| | | int var; \ |
| | | iname = (*env)->NewStringUTF(env, key); \ |
| | | var = (*env)->CallIntMethod(env, intent, imid, iname, -1); \ |
| | | (var) = (*env)->CallIntMethod(env, intent, imid, iname, -1); \ |
| | | (*env)->DeleteLocalRef(env, iname); |
| | | |
| | | bmid = (*env)->GetMethodID(env, cls, "getBooleanExtra", "(Ljava/lang/String;Z)Z"); |
| | |
| | | #define GET_BOOL_EXTRA(var, key) \ |
| | | int var; \ |
| | | bname = (*env)->NewStringUTF(env, key); \ |
| | | var = (*env)->CallBooleanMethod(env, intent, bmid, bname, JNI_FALSE); \ |
| | | (var) = (*env)->CallBooleanMethod(env, intent, bmid, bname, JNI_FALSE); \ |
| | | (*env)->DeleteLocalRef(env, bname); |
| | | |
| | | if (plugged) { |
| | |
| | | return 0; |
| | | } |
| | | |
| | | /* returns number of found touch devices as return value and ids in parameter ids */ |
| | | int Android_JNI_GetTouchDeviceIds(int **ids) { |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | jint sources = 4098; /* == InputDevice.SOURCE_TOUCHSCREEN */ |
| | | jintArray array = (jintArray) (*env)->CallStaticObjectMethod(env, mActivityClass, midInputGetInputDeviceIds, sources); |
| | | int number = 0; |
| | | *ids = NULL; |
| | | if (array) { |
| | | number = (int) (*env)->GetArrayLength(env, array); |
| | | if (0 < number) { |
| | | jint* elements = (*env)->GetIntArrayElements(env, array, NULL); |
| | | if (elements) { |
| | | int i; |
| | | *ids = SDL_malloc(number * sizeof (**ids)); |
| | | for (i = 0; i < number; ++i) { /* not assuming sizeof (jint) == sizeof (int) */ |
| | | (*ids)[i] = elements[i]; |
| | | } |
| | | (*env)->ReleaseIntArrayElements(env, array, elements, JNI_ABORT); |
| | | } |
| | | } |
| | | (*env)->DeleteLocalRef(env, array); |
| | | } |
| | | return number; |
| | | } |
| | | |
| | | /* sets the mSeparateMouseAndTouch field */ |
| | | void Android_JNI_SetSeparateMouseAndTouch(SDL_bool new_value) |
| | | { |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | (*env)->SetStaticBooleanField(env, mActivityClass, fidSeparateMouseAndTouch, new_value ? JNI_TRUE : JNI_FALSE); |
| | | /* Add all touch devices */ |
| | | void Android_JNI_InitTouch() { |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | (*env)->CallStaticVoidMethod(env, mActivityClass, midInitTouch); |
| | | } |
| | | |
| | | void Android_JNI_PollInputDevices(void) |
| | |
| | | Android_JNI_SendMessage(COMMAND_TEXTEDIT_HIDE, 0); |
| | | } |
| | | |
| | | SDL_bool Android_JNI_IsScreenKeyboardShown() |
| | | SDL_bool Android_JNI_IsScreenKeyboardShown(void) |
| | | { |
| | | JNIEnv *mEnv = Android_JNI_GetEnv(); |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | jboolean is_shown = 0; |
| | | is_shown = (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, midIsScreenKeyboardShown); |
| | | is_shown = (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsScreenKeyboardShown); |
| | | return is_shown; |
| | | } |
| | | |
| | |
| | | button_texts = (*env)->NewObjectArray(env, messageboxdata->numbuttons, |
| | | clazz, NULL); |
| | | for (i = 0; i < messageboxdata->numbuttons; ++i) { |
| | | temp = messageboxdata->buttons[i].flags; |
| | | const SDL_MessageBoxButtonData *sdlButton; |
| | | |
| | | if (messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT) { |
| | | sdlButton = &messageboxdata->buttons[messageboxdata->numbuttons - 1 - i]; |
| | | } else { |
| | | sdlButton = &messageboxdata->buttons[i]; |
| | | } |
| | | |
| | | temp = sdlButton->flags; |
| | | (*env)->SetIntArrayRegion(env, button_flags, i, 1, &temp); |
| | | temp = messageboxdata->buttons[i].buttonid; |
| | | temp = sdlButton->buttonid; |
| | | (*env)->SetIntArrayRegion(env, button_ids, i, 1, &temp); |
| | | text = (*env)->NewStringUTF(env, messageboxdata->buttons[i].text); |
| | | text = (*env)->NewStringUTF(env, sdlButton->text); |
| | | (*env)->SetObjectArrayElement(env, button_texts, i, text); |
| | | (*env)->DeleteLocalRef(env, text); |
| | | } |
| | |
| | | if (messageboxdata->colorScheme) { |
| | | colors = (*env)->NewIntArray(env, SDL_MESSAGEBOX_COLOR_MAX); |
| | | for (i = 0; i < SDL_MESSAGEBOX_COLOR_MAX; ++i) { |
| | | temp = (0xFF << 24) | |
| | | temp = (0xFFU << 24) | |
| | | (messageboxdata->colorScheme->colors[i].r << 16) | |
| | | (messageboxdata->colorScheme->colors[i].g << 8) | |
| | | (messageboxdata->colorScheme->colors[i].b << 0); |
| | |
| | | return (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext); |
| | | } |
| | | |
| | | int SDL_GetAndroidSDKVersion(void) |
| | | { |
| | | static int sdk_version; |
| | | if (!sdk_version) { |
| | | char sdk[PROP_VALUE_MAX] = {0}; |
| | | if (__system_property_get("ro.build.version.sdk", sdk) != 0) { |
| | | sdk_version = SDL_atoi(sdk); |
| | | } |
| | | } |
| | | return sdk_version; |
| | | } |
| | | |
| | | SDL_bool SDL_IsAndroidTablet(void) |
| | | { |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | |
| | | void SDL_AndroidBackButton(void) |
| | | { |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | return (*env)->CallStaticVoidMethod(env, mActivityClass, midManualBackButton); |
| | | (*env)->CallStaticVoidMethod(env, mActivityClass, midManualBackButton); |
| | | } |
| | | |
| | | const char * SDL_AndroidGetInternalStoragePath(void) |
| | |
| | | |
| | | int Android_JNI_CreateCustomCursor(SDL_Surface *surface, int hot_x, int hot_y) |
| | | { |
| | | JNIEnv *mEnv = Android_JNI_GetEnv(); |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | int custom_cursor = 0; |
| | | jintArray pixels; |
| | | pixels = (*mEnv)->NewIntArray(mEnv, surface->w * surface->h); |
| | | pixels = (*env)->NewIntArray(env, surface->w * surface->h); |
| | | if (pixels) { |
| | | (*mEnv)->SetIntArrayRegion(mEnv, pixels, 0, surface->w * surface->h, (int *)surface->pixels); |
| | | custom_cursor = (*mEnv)->CallStaticIntMethod(mEnv, mActivityClass, midCreateCustomCursor, pixels, surface->w, surface->h, hot_x, hot_y); |
| | | (*mEnv)->DeleteLocalRef(mEnv, pixels); |
| | | (*env)->SetIntArrayRegion(env, pixels, 0, surface->w * surface->h, (int *)surface->pixels); |
| | | custom_cursor = (*env)->CallStaticIntMethod(env, mActivityClass, midCreateCustomCursor, pixels, surface->w, surface->h, hot_x, hot_y); |
| | | (*env)->DeleteLocalRef(env, pixels); |
| | | } else { |
| | | SDL_OutOfMemory(); |
| | | } |
| | |
| | | |
| | | SDL_bool Android_JNI_SetCustomCursor(int cursorID) |
| | | { |
| | | JNIEnv *mEnv = Android_JNI_GetEnv(); |
| | | return (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, midSetCustomCursor, cursorID); |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | return (*env)->CallStaticBooleanMethod(env, mActivityClass, midSetCustomCursor, cursorID); |
| | | } |
| | | |
| | | SDL_bool Android_JNI_SetSystemCursor(int cursorID) |
| | | { |
| | | JNIEnv *mEnv = Android_JNI_GetEnv(); |
| | | return (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, midSetSystemCursor, cursorID); |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | return (*env)->CallStaticBooleanMethod(env, mActivityClass, midSetSystemCursor, cursorID); |
| | | } |
| | | |
| | | SDL_bool Android_JNI_SupportsRelativeMouse() |
| | | SDL_bool Android_JNI_SupportsRelativeMouse(void) |
| | | { |
| | | JNIEnv *mEnv = Android_JNI_GetEnv(); |
| | | return (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, midSupportsRelativeMouse); |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | return (*env)->CallStaticBooleanMethod(env, mActivityClass, midSupportsRelativeMouse); |
| | | } |
| | | |
| | | SDL_bool Android_JNI_SetRelativeMouseEnabled(SDL_bool enabled) |
| | | { |
| | | JNIEnv *mEnv = Android_JNI_GetEnv(); |
| | | return (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, midSetRelativeMouseEnabled, (enabled == 1)); |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | return (*env)->CallStaticBooleanMethod(env, mActivityClass, midSetRelativeMouseEnabled, (enabled == 1)); |
| | | } |
| | | |
| | | SDL_bool Android_JNI_RequestPermission(const char *permission) |
| | | { |
| | | JNIEnv *env = Android_JNI_GetEnv(); |
| | | const int requestCode = 1; |
| | | |
| | | /* Wait for any pending request on another thread */ |
| | | while (SDL_AtomicGet(&bPermissionRequestPending) == SDL_TRUE) { |
| | | SDL_Delay(10); |
| | | } |
| | | SDL_AtomicSet(&bPermissionRequestPending, SDL_TRUE); |
| | | |
| | | jstring jpermission = (*env)->NewStringUTF(env, permission); |
| | | (*env)->CallStaticVoidMethod(env, mActivityClass, midRequestPermission, jpermission, requestCode); |
| | | (*env)->DeleteLocalRef(env, jpermission); |
| | | |
| | | /* Wait for the request to complete */ |
| | | while (SDL_AtomicGet(&bPermissionRequestPending) == SDL_TRUE) { |
| | | SDL_Delay(10); |
| | | } |
| | | return bPermissionRequestResult; |
| | | } |
| | | |
| | | #endif /* __ANDROID__ */ |
| | | |