diff options
| author | 3gg <3gg@shellblade.net> | 2025-12-27 12:03:39 -0800 |
|---|---|---|
| committer | 3gg <3gg@shellblade.net> | 2025-12-27 12:03:39 -0800 |
| commit | 5a079a2d114f96d4847d1ee305d5b7c16eeec50e (patch) | |
| tree | 8926ab44f168acf787d8e19608857b3af0f82758 /contrib/SDL-3.2.8/src/core/android | |
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/src/core/android')
| -rw-r--r-- | contrib/SDL-3.2.8/src/core/android/SDL_android.c | 2824 | ||||
| -rw-r--r-- | contrib/SDL-3.2.8/src/core/android/SDL_android.h | 163 |
2 files changed, 2987 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/core/android/SDL_android.c b/contrib/SDL-3.2.8/src/core/android/SDL_android.c new file mode 100644 index 0000000..daf0f29 --- /dev/null +++ b/contrib/SDL-3.2.8/src/core/android/SDL_android.c | |||
| @@ -0,0 +1,2824 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org> | ||
| 4 | |||
| 5 | This software is provided 'as-is', without any express or implied | ||
| 6 | warranty. In no event will the authors be held liable for any damages | ||
| 7 | arising from the use of this software. | ||
| 8 | |||
| 9 | Permission is granted to anyone to use this software for any purpose, | ||
| 10 | including commercial applications, and to alter it and redistribute it | ||
| 11 | freely, subject to the following restrictions: | ||
| 12 | |||
| 13 | 1. The origin of this software must not be misrepresented; you must not | ||
| 14 | claim that you wrote the original software. If you use this software | ||
| 15 | in a product, an acknowledgment in the product documentation would be | ||
| 16 | appreciated but is not required. | ||
| 17 | 2. Altered source versions must be plainly marked as such, and must not be | ||
| 18 | misrepresented as being the original software. | ||
| 19 | 3. This notice may not be removed or altered from any source distribution. | ||
| 20 | */ | ||
| 21 | #include "SDL_internal.h" | ||
| 22 | |||
| 23 | #ifdef SDL_PLATFORM_ANDROID | ||
| 24 | |||
| 25 | #include "SDL_android.h" | ||
| 26 | |||
| 27 | #include "../../events/SDL_events_c.h" | ||
| 28 | #include "../../video/android/SDL_androidkeyboard.h" | ||
| 29 | #include "../../video/android/SDL_androidmouse.h" | ||
| 30 | #include "../../video/android/SDL_androidtouch.h" | ||
| 31 | #include "../../video/android/SDL_androidpen.h" | ||
| 32 | #include "../../video/android/SDL_androidvideo.h" | ||
| 33 | #include "../../video/android/SDL_androidwindow.h" | ||
| 34 | #include "../../joystick/android/SDL_sysjoystick_c.h" | ||
| 35 | #include "../../haptic/android/SDL_syshaptic_c.h" | ||
| 36 | #include "../../hidapi/android/hid.h" | ||
| 37 | #include "../../SDL_hints_c.h" | ||
| 38 | |||
| 39 | #include <android/log.h> | ||
| 40 | #include <android/configuration.h> | ||
| 41 | #include <android/asset_manager_jni.h> | ||
| 42 | #include <sys/system_properties.h> | ||
| 43 | #include <pthread.h> | ||
| 44 | #include <sys/types.h> | ||
| 45 | #include <unistd.h> | ||
| 46 | #include <dlfcn.h> | ||
| 47 | |||
| 48 | #define SDL_JAVA_PREFIX org_libsdl_app | ||
| 49 | #define CONCAT1(prefix, class, function) CONCAT2(prefix, class, function) | ||
| 50 | #define CONCAT2(prefix, class, function) Java_##prefix##_##class##_##function | ||
| 51 | #define SDL_JAVA_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, SDLActivity, function) | ||
| 52 | #define SDL_JAVA_AUDIO_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, SDLAudioManager, function) | ||
| 53 | #define SDL_JAVA_CONTROLLER_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, SDLControllerManager, function) | ||
| 54 | #define SDL_JAVA_INTERFACE_INPUT_CONNECTION(function) CONCAT1(SDL_JAVA_PREFIX, SDLInputConnection, function) | ||
| 55 | |||
| 56 | // Audio encoding definitions | ||
| 57 | #define ENCODING_PCM_8BIT 3 | ||
| 58 | #define ENCODING_PCM_16BIT 2 | ||
| 59 | #define ENCODING_PCM_FLOAT 4 | ||
| 60 | |||
| 61 | // Java class SDLActivity | ||
| 62 | JNIEXPORT jstring JNICALL SDL_JAVA_INTERFACE(nativeGetVersion)( | ||
| 63 | JNIEnv *env, jclass cls); | ||
| 64 | |||
| 65 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)( | ||
| 66 | JNIEnv *env, jclass cls); | ||
| 67 | |||
| 68 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeInitMainThread)( | ||
| 69 | JNIEnv *env, jclass cls); | ||
| 70 | |||
| 71 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeCleanupMainThread)( | ||
| 72 | JNIEnv *env, jclass cls); | ||
| 73 | |||
| 74 | JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeRunMain)( | ||
| 75 | JNIEnv *env, jclass cls, | ||
| 76 | jstring library, jstring function, jobject array); | ||
| 77 | |||
| 78 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeDropFile)( | ||
| 79 | JNIEnv *env, jclass jcls, | ||
| 80 | jstring filename); | ||
| 81 | |||
| 82 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetScreenResolution)( | ||
| 83 | JNIEnv *env, jclass jcls, | ||
| 84 | jint surfaceWidth, jint surfaceHeight, | ||
| 85 | jint deviceWidth, jint deviceHeight, jfloat density, jfloat rate); | ||
| 86 | |||
| 87 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeResize)( | ||
| 88 | JNIEnv *env, jclass cls); | ||
| 89 | |||
| 90 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceCreated)( | ||
| 91 | JNIEnv *env, jclass jcls); | ||
| 92 | |||
| 93 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)( | ||
| 94 | JNIEnv *env, jclass jcls); | ||
| 95 | |||
| 96 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)( | ||
| 97 | JNIEnv *env, jclass jcls); | ||
| 98 | |||
| 99 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyDown)( | ||
| 100 | JNIEnv *env, jclass jcls, | ||
| 101 | jint keycode); | ||
| 102 | |||
| 103 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyUp)( | ||
| 104 | JNIEnv *env, jclass jcls, | ||
| 105 | jint keycode); | ||
| 106 | |||
| 107 | JNIEXPORT jboolean JNICALL SDL_JAVA_INTERFACE(onNativeSoftReturnKey)( | ||
| 108 | JNIEnv *env, jclass jcls); | ||
| 109 | |||
| 110 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyboardFocusLost)( | ||
| 111 | JNIEnv *env, jclass jcls); | ||
| 112 | |||
| 113 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeTouch)( | ||
| 114 | JNIEnv *env, jclass jcls, | ||
| 115 | jint touch_device_id_in, jint pointer_finger_id_in, | ||
| 116 | jint action, jfloat x, jfloat y, jfloat p); | ||
| 117 | |||
| 118 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeMouse)( | ||
| 119 | JNIEnv *env, jclass jcls, | ||
| 120 | jint button, jint action, jfloat x, jfloat y, jboolean relative); | ||
| 121 | |||
| 122 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativePen)( | ||
| 123 | JNIEnv *env, jclass jcls, | ||
| 124 | jint pen_id_in, jint button, jint action, jfloat x, jfloat y, jfloat p); | ||
| 125 | |||
| 126 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeAccel)( | ||
| 127 | JNIEnv *env, jclass jcls, | ||
| 128 | jfloat x, jfloat y, jfloat z); | ||
| 129 | |||
| 130 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeClipboardChanged)( | ||
| 131 | JNIEnv *env, jclass jcls); | ||
| 132 | |||
| 133 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeLowMemory)( | ||
| 134 | JNIEnv *env, jclass cls); | ||
| 135 | |||
| 136 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeLocaleChanged)( | ||
| 137 | JNIEnv *env, jclass cls); | ||
| 138 | |||
| 139 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeDarkModeChanged)( | ||
| 140 | JNIEnv *env, jclass cls, jboolean enabled); | ||
| 141 | |||
| 142 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSendQuit)( | ||
| 143 | JNIEnv *env, jclass cls); | ||
| 144 | |||
| 145 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeQuit)( | ||
| 146 | JNIEnv *env, jclass cls); | ||
| 147 | |||
| 148 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePause)( | ||
| 149 | JNIEnv *env, jclass cls); | ||
| 150 | |||
| 151 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeResume)( | ||
| 152 | JNIEnv *env, jclass cls); | ||
| 153 | |||
| 154 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeFocusChanged)( | ||
| 155 | JNIEnv *env, jclass cls, jboolean hasFocus); | ||
| 156 | |||
| 157 | JNIEXPORT jstring JNICALL SDL_JAVA_INTERFACE(nativeGetHint)( | ||
| 158 | JNIEnv *env, jclass cls, | ||
| 159 | jstring name); | ||
| 160 | |||
| 161 | JNIEXPORT jboolean JNICALL SDL_JAVA_INTERFACE(nativeGetHintBoolean)( | ||
| 162 | JNIEnv *env, jclass cls, | ||
| 163 | jstring name, jboolean default_value); | ||
| 164 | |||
| 165 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetenv)( | ||
| 166 | JNIEnv *env, jclass cls, | ||
| 167 | jstring name, jstring value); | ||
| 168 | |||
| 169 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetNaturalOrientation)( | ||
| 170 | JNIEnv *env, jclass cls, | ||
| 171 | jint orientation); | ||
| 172 | |||
| 173 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeRotationChanged)( | ||
| 174 | JNIEnv *env, jclass cls, | ||
| 175 | jint rotation); | ||
| 176 | |||
| 177 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeInsetsChanged)( | ||
| 178 | JNIEnv *env, jclass cls, | ||
| 179 | jint left, jint right, jint top, jint bottom); | ||
| 180 | |||
| 181 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeAddTouch)( | ||
| 182 | JNIEnv *env, jclass cls, | ||
| 183 | jint touchId, jstring name); | ||
| 184 | |||
| 185 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePermissionResult)( | ||
| 186 | JNIEnv *env, jclass cls, | ||
| 187 | jint requestCode, jboolean result); | ||
| 188 | |||
| 189 | JNIEXPORT jboolean JNICALL SDL_JAVA_INTERFACE(nativeAllowRecreateActivity)( | ||
| 190 | JNIEnv *env, jclass jcls); | ||
| 191 | |||
| 192 | JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeCheckSDLThreadCounter)( | ||
| 193 | JNIEnv *env, jclass jcls); | ||
| 194 | |||
| 195 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeFileDialog)( | ||
| 196 | JNIEnv *env, jclass jcls, | ||
| 197 | jint requestCode, jobjectArray fileList, jint filter); | ||
| 198 | |||
| 199 | static JNINativeMethod SDLActivity_tab[] = { | ||
| 200 | { "nativeGetVersion", "()Ljava/lang/String;", SDL_JAVA_INTERFACE(nativeGetVersion) }, | ||
| 201 | { "nativeSetupJNI", "()I", SDL_JAVA_INTERFACE(nativeSetupJNI) }, | ||
| 202 | { "nativeInitMainThread", "()V", SDL_JAVA_INTERFACE(nativeInitMainThread) }, | ||
| 203 | { "nativeCleanupMainThread", "()V", SDL_JAVA_INTERFACE(nativeCleanupMainThread) }, | ||
| 204 | { "nativeRunMain", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)I", SDL_JAVA_INTERFACE(nativeRunMain) }, | ||
| 205 | { "onNativeDropFile", "(Ljava/lang/String;)V", SDL_JAVA_INTERFACE(onNativeDropFile) }, | ||
| 206 | { "nativeSetScreenResolution", "(IIIIFF)V", SDL_JAVA_INTERFACE(nativeSetScreenResolution) }, | ||
| 207 | { "onNativeResize", "()V", SDL_JAVA_INTERFACE(onNativeResize) }, | ||
| 208 | { "onNativeSurfaceCreated", "()V", SDL_JAVA_INTERFACE(onNativeSurfaceCreated) }, | ||
| 209 | { "onNativeSurfaceChanged", "()V", SDL_JAVA_INTERFACE(onNativeSurfaceChanged) }, | ||
| 210 | { "onNativeSurfaceDestroyed", "()V", SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed) }, | ||
| 211 | { "onNativeKeyDown", "(I)V", SDL_JAVA_INTERFACE(onNativeKeyDown) }, | ||
| 212 | { "onNativeKeyUp", "(I)V", SDL_JAVA_INTERFACE(onNativeKeyUp) }, | ||
| 213 | { "onNativeSoftReturnKey", "()Z", SDL_JAVA_INTERFACE(onNativeSoftReturnKey) }, | ||
| 214 | { "onNativeKeyboardFocusLost", "()V", SDL_JAVA_INTERFACE(onNativeKeyboardFocusLost) }, | ||
| 215 | { "onNativeTouch", "(IIIFFF)V", SDL_JAVA_INTERFACE(onNativeTouch) }, | ||
| 216 | { "onNativeMouse", "(IIFFZ)V", SDL_JAVA_INTERFACE(onNativeMouse) }, | ||
| 217 | { "onNativePen", "(IIIFFF)V", SDL_JAVA_INTERFACE(onNativePen) }, | ||
| 218 | { "onNativeAccel", "(FFF)V", SDL_JAVA_INTERFACE(onNativeAccel) }, | ||
| 219 | { "onNativeClipboardChanged", "()V", SDL_JAVA_INTERFACE(onNativeClipboardChanged) }, | ||
| 220 | { "nativeLowMemory", "()V", SDL_JAVA_INTERFACE(nativeLowMemory) }, | ||
| 221 | { "onNativeLocaleChanged", "()V", SDL_JAVA_INTERFACE(onNativeLocaleChanged) }, | ||
| 222 | { "onNativeDarkModeChanged", "(Z)V", SDL_JAVA_INTERFACE(onNativeDarkModeChanged) }, | ||
| 223 | { "nativeSendQuit", "()V", SDL_JAVA_INTERFACE(nativeSendQuit) }, | ||
| 224 | { "nativeQuit", "()V", SDL_JAVA_INTERFACE(nativeQuit) }, | ||
| 225 | { "nativePause", "()V", SDL_JAVA_INTERFACE(nativePause) }, | ||
| 226 | { "nativeResume", "()V", SDL_JAVA_INTERFACE(nativeResume) }, | ||
| 227 | { "nativeFocusChanged", "(Z)V", SDL_JAVA_INTERFACE(nativeFocusChanged) }, | ||
| 228 | { "nativeGetHint", "(Ljava/lang/String;)Ljava/lang/String;", SDL_JAVA_INTERFACE(nativeGetHint) }, | ||
| 229 | { "nativeGetHintBoolean", "(Ljava/lang/String;Z)Z", SDL_JAVA_INTERFACE(nativeGetHintBoolean) }, | ||
| 230 | { "nativeSetenv", "(Ljava/lang/String;Ljava/lang/String;)V", SDL_JAVA_INTERFACE(nativeSetenv) }, | ||
| 231 | { "nativeSetNaturalOrientation", "(I)V", SDL_JAVA_INTERFACE(nativeSetNaturalOrientation) }, | ||
| 232 | { "onNativeRotationChanged", "(I)V", SDL_JAVA_INTERFACE(onNativeRotationChanged) }, | ||
| 233 | { "onNativeInsetsChanged", "(IIII)V", SDL_JAVA_INTERFACE(onNativeInsetsChanged) }, | ||
| 234 | { "nativeAddTouch", "(ILjava/lang/String;)V", SDL_JAVA_INTERFACE(nativeAddTouch) }, | ||
| 235 | { "nativePermissionResult", "(IZ)V", SDL_JAVA_INTERFACE(nativePermissionResult) }, | ||
| 236 | { "nativeAllowRecreateActivity", "()Z", SDL_JAVA_INTERFACE(nativeAllowRecreateActivity) }, | ||
| 237 | { "nativeCheckSDLThreadCounter", "()I", SDL_JAVA_INTERFACE(nativeCheckSDLThreadCounter) }, | ||
| 238 | { "onNativeFileDialog", "(I[Ljava/lang/String;I)V", SDL_JAVA_INTERFACE(onNativeFileDialog) } | ||
| 239 | }; | ||
| 240 | |||
| 241 | // Java class SDLInputConnection | ||
| 242 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeCommitText)( | ||
| 243 | JNIEnv *env, jclass cls, | ||
| 244 | jstring text, jint newCursorPosition); | ||
| 245 | |||
| 246 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeGenerateScancodeForUnichar)( | ||
| 247 | JNIEnv *env, jclass cls, | ||
| 248 | jchar chUnicode); | ||
| 249 | |||
| 250 | static JNINativeMethod SDLInputConnection_tab[] = { | ||
| 251 | { "nativeCommitText", "(Ljava/lang/String;I)V", SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeCommitText) }, | ||
| 252 | { "nativeGenerateScancodeForUnichar", "(C)V", SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeGenerateScancodeForUnichar) } | ||
| 253 | }; | ||
| 254 | |||
| 255 | // Java class SDLAudioManager | ||
| 256 | JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)( | ||
| 257 | JNIEnv *env, jclass jcls); | ||
| 258 | |||
| 259 | JNIEXPORT void JNICALL | ||
| 260 | SDL_JAVA_AUDIO_INTERFACE(addAudioDevice)(JNIEnv *env, jclass jcls, jboolean recording, jstring name, | ||
| 261 | jint device_id); | ||
| 262 | |||
| 263 | JNIEXPORT void JNICALL | ||
| 264 | SDL_JAVA_AUDIO_INTERFACE(removeAudioDevice)(JNIEnv *env, jclass jcls, jboolean recording, | ||
| 265 | jint device_id); | ||
| 266 | |||
| 267 | static JNINativeMethod SDLAudioManager_tab[] = { | ||
| 268 | { "nativeSetupJNI", "()I", SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI) }, | ||
| 269 | { "addAudioDevice", "(ZLjava/lang/String;I)V", SDL_JAVA_AUDIO_INTERFACE(addAudioDevice) }, | ||
| 270 | { "removeAudioDevice", "(ZI)V", SDL_JAVA_AUDIO_INTERFACE(removeAudioDevice) } | ||
| 271 | }; | ||
| 272 | |||
| 273 | // Java class SDLControllerManager | ||
| 274 | JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)( | ||
| 275 | JNIEnv *env, jclass jcls); | ||
| 276 | |||
| 277 | JNIEXPORT jboolean JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)( | ||
| 278 | JNIEnv *env, jclass jcls, | ||
| 279 | jint device_id, jint keycode); | ||
| 280 | |||
| 281 | JNIEXPORT jboolean JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)( | ||
| 282 | JNIEnv *env, jclass jcls, | ||
| 283 | jint device_id, jint keycode); | ||
| 284 | |||
| 285 | JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)( | ||
| 286 | JNIEnv *env, jclass jcls, | ||
| 287 | jint device_id, jint axis, jfloat value); | ||
| 288 | |||
| 289 | JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)( | ||
| 290 | JNIEnv *env, jclass jcls, | ||
| 291 | jint device_id, jint hat_id, jint x, jint y); | ||
| 292 | |||
| 293 | JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)( | ||
| 294 | JNIEnv *env, jclass jcls, | ||
| 295 | jint device_id, jstring device_name, jstring device_desc, jint vendor_id, jint product_id, | ||
| 296 | jint button_mask, jint naxes, jint axis_mask, jint nhats, jboolean can_rumble); | ||
| 297 | |||
| 298 | JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)( | ||
| 299 | JNIEnv *env, jclass jcls, | ||
| 300 | jint device_id); | ||
| 301 | |||
| 302 | JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic)( | ||
| 303 | JNIEnv *env, jclass jcls, | ||
| 304 | jint device_id, jstring device_name); | ||
| 305 | |||
| 306 | JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)( | ||
| 307 | JNIEnv *env, jclass jcls, | ||
| 308 | jint device_id); | ||
| 309 | |||
| 310 | static JNINativeMethod SDLControllerManager_tab[] = { | ||
| 311 | { "nativeSetupJNI", "()I", SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI) }, | ||
| 312 | { "onNativePadDown", "(II)Z", SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown) }, | ||
| 313 | { "onNativePadUp", "(II)Z", SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp) }, | ||
| 314 | { "onNativeJoy", "(IIF)V", SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy) }, | ||
| 315 | { "onNativeHat", "(IIII)V", SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat) }, | ||
| 316 | { "nativeAddJoystick", "(ILjava/lang/String;Ljava/lang/String;IIIIIIZ)V", SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick) }, | ||
| 317 | { "nativeRemoveJoystick", "(I)V", SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick) }, | ||
| 318 | { "nativeAddHaptic", "(ILjava/lang/String;)V", SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic) }, | ||
| 319 | { "nativeRemoveHaptic", "(I)V", SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic) } | ||
| 320 | }; | ||
| 321 | |||
| 322 | // Uncomment this to log messages entering and exiting methods in this file | ||
| 323 | // #define DEBUG_JNI | ||
| 324 | |||
| 325 | static void checkJNIReady(void); | ||
| 326 | |||
| 327 | /******************************************************************************* | ||
| 328 | This file links the Java side of Android with libsdl | ||
| 329 | *******************************************************************************/ | ||
| 330 | #include <jni.h> | ||
| 331 | |||
| 332 | /******************************************************************************* | ||
| 333 | Globals | ||
| 334 | *******************************************************************************/ | ||
| 335 | static pthread_key_t mThreadKey; | ||
| 336 | static pthread_once_t key_once = PTHREAD_ONCE_INIT; | ||
| 337 | static JavaVM *mJavaVM = NULL; | ||
| 338 | |||
| 339 | // Main activity | ||
| 340 | static jclass mActivityClass; | ||
| 341 | |||
| 342 | // method signatures | ||
| 343 | static jmethodID midClipboardGetText; | ||
| 344 | static jmethodID midClipboardHasText; | ||
| 345 | static jmethodID midClipboardSetText; | ||
| 346 | static jmethodID midCreateCustomCursor; | ||
| 347 | static jmethodID midDestroyCustomCursor; | ||
| 348 | static jmethodID midGetContext; | ||
| 349 | static jmethodID midGetManifestEnvironmentVariables; | ||
| 350 | static jmethodID midGetNativeSurface; | ||
| 351 | static jmethodID midInitTouch; | ||
| 352 | static jmethodID midIsAndroidTV; | ||
| 353 | static jmethodID midIsChromebook; | ||
| 354 | static jmethodID midIsDeXMode; | ||
| 355 | static jmethodID midIsScreenKeyboardShown; | ||
| 356 | static jmethodID midIsTablet; | ||
| 357 | static jmethodID midManualBackButton; | ||
| 358 | static jmethodID midMinimizeWindow; | ||
| 359 | static jmethodID midOpenURL; | ||
| 360 | static jmethodID midRequestPermission; | ||
| 361 | static jmethodID midShowToast; | ||
| 362 | static jmethodID midSendMessage; | ||
| 363 | static jmethodID midSetActivityTitle; | ||
| 364 | static jmethodID midSetCustomCursor; | ||
| 365 | static jmethodID midSetOrientation; | ||
| 366 | static jmethodID midSetRelativeMouseEnabled; | ||
| 367 | static jmethodID midSetSystemCursor; | ||
| 368 | static jmethodID midSetWindowStyle; | ||
| 369 | static jmethodID midShouldMinimizeOnFocusLoss; | ||
| 370 | static jmethodID midShowTextInput; | ||
| 371 | static jmethodID midSupportsRelativeMouse; | ||
| 372 | static jmethodID midOpenFileDescriptor; | ||
| 373 | static jmethodID midShowFileDialog; | ||
| 374 | |||
| 375 | // audio manager | ||
| 376 | static jclass mAudioManagerClass; | ||
| 377 | |||
| 378 | // method signatures | ||
| 379 | static jmethodID midRegisterAudioDeviceCallback; | ||
| 380 | static jmethodID midUnregisterAudioDeviceCallback; | ||
| 381 | static jmethodID midAudioSetThreadPriority; | ||
| 382 | |||
| 383 | // controller manager | ||
| 384 | static jclass mControllerManagerClass; | ||
| 385 | |||
| 386 | // method signatures | ||
| 387 | static jmethodID midPollInputDevices; | ||
| 388 | static jmethodID midPollHapticDevices; | ||
| 389 | static jmethodID midHapticRun; | ||
| 390 | static jmethodID midHapticRumble; | ||
| 391 | static jmethodID midHapticStop; | ||
| 392 | |||
| 393 | // Accelerometer data storage | ||
| 394 | static SDL_DisplayOrientation displayNaturalOrientation; | ||
| 395 | static SDL_DisplayOrientation displayCurrentOrientation; | ||
| 396 | static float fLastAccelerometer[3]; | ||
| 397 | static bool bHasNewData; | ||
| 398 | |||
| 399 | static bool bHasEnvironmentVariables; | ||
| 400 | |||
| 401 | // Android AssetManager | ||
| 402 | static void Internal_Android_Create_AssetManager(void); | ||
| 403 | static void Internal_Android_Destroy_AssetManager(void); | ||
| 404 | static AAssetManager *asset_manager = NULL; | ||
| 405 | static jobject javaAssetManagerRef = 0; | ||
| 406 | |||
| 407 | static SDL_Mutex *Android_ActivityMutex = NULL; | ||
| 408 | static SDL_Mutex *Android_LifecycleMutex = NULL; | ||
| 409 | static SDL_Semaphore *Android_LifecycleEventSem = NULL; | ||
| 410 | static SDL_AndroidLifecycleEvent Android_LifecycleEvents[SDL_NUM_ANDROID_LIFECYCLE_EVENTS]; | ||
| 411 | static int Android_NumLifecycleEvents; | ||
| 412 | |||
| 413 | /******************************************************************************* | ||
| 414 | Functions called by JNI | ||
| 415 | *******************************************************************************/ | ||
| 416 | |||
| 417 | /* From http://developer.android.com/guide/practices/jni.html | ||
| 418 | * All threads are Linux threads, scheduled by the kernel. | ||
| 419 | * They're usually started from managed code (using Thread.start), but they can also be created elsewhere and then | ||
| 420 | * attached to the JavaVM. For example, a thread started with pthread_create can be attached with the | ||
| 421 | * JNI AttachCurrentThread or AttachCurrentThreadAsDaemon functions. Until a thread is attached, it has no JNIEnv, | ||
| 422 | * and cannot make JNI calls. | ||
| 423 | * Attaching a natively-created thread causes a java.lang.Thread object to be constructed and added to the "main" | ||
| 424 | * ThreadGroup, making it visible to the debugger. Calling AttachCurrentThread on an already-attached thread | ||
| 425 | * is a no-op. | ||
| 426 | * Note: You can call this function any number of times for the same thread, there's no harm in it | ||
| 427 | */ | ||
| 428 | |||
| 429 | /* From http://developer.android.com/guide/practices/jni.html | ||
| 430 | * Threads attached through JNI must call DetachCurrentThread before they exit. If coding this directly is awkward, | ||
| 431 | * in Android 2.0 (Eclair) and higher you can use pthread_key_create to define a destructor function that will be | ||
| 432 | * called before the thread exits, and call DetachCurrentThread from there. (Use that key with pthread_setspecific | ||
| 433 | * to store the JNIEnv in thread-local-storage; that way it'll be passed into your destructor as the argument.) | ||
| 434 | * Note: The destructor is not called unless the stored value is != NULL | ||
| 435 | * Note: You can call this function any number of times for the same thread, there's no harm in it | ||
| 436 | * (except for some lost CPU cycles) | ||
| 437 | */ | ||
| 438 | |||
| 439 | // Set local storage value | ||
| 440 | static bool Android_JNI_SetEnv(JNIEnv *env) | ||
| 441 | { | ||
| 442 | int status = pthread_setspecific(mThreadKey, env); | ||
| 443 | if (status < 0) { | ||
| 444 | __android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed pthread_setspecific() in Android_JNI_SetEnv() (err=%d)", status); | ||
| 445 | return false; | ||
| 446 | } | ||
| 447 | return true; | ||
| 448 | } | ||
| 449 | |||
| 450 | // Get local storage value | ||
| 451 | JNIEnv *Android_JNI_GetEnv(void) | ||
| 452 | { | ||
| 453 | // Get JNIEnv from the Thread local storage | ||
| 454 | JNIEnv *env = pthread_getspecific(mThreadKey); | ||
| 455 | if (!env) { | ||
| 456 | // If it fails, try to attach ! (e.g the thread isn't created with SDL_CreateThread() | ||
| 457 | int status; | ||
| 458 | |||
| 459 | // There should be a JVM | ||
| 460 | if (!mJavaVM) { | ||
| 461 | __android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed, there is no JavaVM"); | ||
| 462 | return NULL; | ||
| 463 | } | ||
| 464 | |||
| 465 | /* Attach the current thread to the JVM and get a JNIEnv. | ||
| 466 | * It will be detached by pthread_create destructor 'Android_JNI_ThreadDestroyed' */ | ||
| 467 | status = (*mJavaVM)->AttachCurrentThread(mJavaVM, &env, NULL); | ||
| 468 | if (status < 0) { | ||
| 469 | __android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed to attach current thread (err=%d)", status); | ||
| 470 | return NULL; | ||
| 471 | } | ||
| 472 | |||
| 473 | // Save JNIEnv into the Thread local storage | ||
| 474 | if (!Android_JNI_SetEnv(env)) { | ||
| 475 | return NULL; | ||
| 476 | } | ||
| 477 | } | ||
| 478 | |||
| 479 | return env; | ||
| 480 | } | ||
| 481 | |||
| 482 | // Set up an external thread for using JNI with Android_JNI_GetEnv() | ||
| 483 | bool Android_JNI_SetupThread(void) | ||
| 484 | { | ||
| 485 | JNIEnv *env; | ||
| 486 | int status; | ||
| 487 | |||
| 488 | // There should be a JVM | ||
| 489 | if (!mJavaVM) { | ||
| 490 | __android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed, there is no JavaVM"); | ||
| 491 | return false; | ||
| 492 | } | ||
| 493 | |||
| 494 | /* Attach the current thread to the JVM and get a JNIEnv. | ||
| 495 | * It will be detached by pthread_create destructor 'Android_JNI_ThreadDestroyed' */ | ||
| 496 | status = (*mJavaVM)->AttachCurrentThread(mJavaVM, &env, NULL); | ||
| 497 | if (status < 0) { | ||
| 498 | __android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed to attach current thread (err=%d)", status); | ||
| 499 | return false; | ||
| 500 | } | ||
| 501 | |||
| 502 | // Save JNIEnv into the Thread local storage | ||
| 503 | if (!Android_JNI_SetEnv(env)) { | ||
| 504 | return false; | ||
| 505 | } | ||
| 506 | |||
| 507 | return true; | ||
| 508 | } | ||
| 509 | |||
| 510 | // Destructor called for each thread where mThreadKey is not NULL | ||
| 511 | static void Android_JNI_ThreadDestroyed(void *value) | ||
| 512 | { | ||
| 513 | // The thread is being destroyed, detach it from the Java VM and set the mThreadKey value to NULL as required | ||
| 514 | JNIEnv *env = (JNIEnv *)value; | ||
| 515 | if (env) { | ||
| 516 | (*mJavaVM)->DetachCurrentThread(mJavaVM); | ||
| 517 | Android_JNI_SetEnv(NULL); | ||
| 518 | } | ||
| 519 | } | ||
| 520 | |||
| 521 | // Creation of local storage mThreadKey | ||
| 522 | static void Android_JNI_CreateKey(void) | ||
| 523 | { | ||
| 524 | int status = pthread_key_create(&mThreadKey, Android_JNI_ThreadDestroyed); | ||
| 525 | if (status < 0) { | ||
| 526 | __android_log_print(ANDROID_LOG_ERROR, "SDL", "Error initializing mThreadKey with pthread_key_create() (err=%d)", status); | ||
| 527 | } | ||
| 528 | } | ||
| 529 | |||
| 530 | static void Android_JNI_CreateKey_once(void) | ||
| 531 | { | ||
| 532 | int status = pthread_once(&key_once, Android_JNI_CreateKey); | ||
| 533 | if (status < 0) { | ||
| 534 | __android_log_print(ANDROID_LOG_ERROR, "SDL", "Error initializing mThreadKey with pthread_once() (err=%d)", status); | ||
| 535 | } | ||
| 536 | } | ||
| 537 | |||
| 538 | static void register_methods(JNIEnv *env, const char *classname, JNINativeMethod *methods, int nb) | ||
| 539 | { | ||
| 540 | jclass clazz = (*env)->FindClass(env, classname); | ||
| 541 | if (!clazz || (*env)->RegisterNatives(env, clazz, methods, nb) < 0) { | ||
| 542 | __android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed to register methods of %s", classname); | ||
| 543 | return; | ||
| 544 | } | ||
| 545 | } | ||
| 546 | |||
| 547 | // Library init | ||
| 548 | JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) | ||
| 549 | { | ||
| 550 | JNIEnv *env = NULL; | ||
| 551 | |||
| 552 | mJavaVM = vm; | ||
| 553 | |||
| 554 | if ((*mJavaVM)->GetEnv(mJavaVM, (void **)&env, JNI_VERSION_1_4) != JNI_OK) { | ||
| 555 | __android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed to get JNI Env"); | ||
| 556 | return JNI_VERSION_1_4; | ||
| 557 | } | ||
| 558 | |||
| 559 | register_methods(env, "org/libsdl/app/SDLActivity", SDLActivity_tab, SDL_arraysize(SDLActivity_tab)); | ||
| 560 | register_methods(env, "org/libsdl/app/SDLInputConnection", SDLInputConnection_tab, SDL_arraysize(SDLInputConnection_tab)); | ||
| 561 | register_methods(env, "org/libsdl/app/SDLAudioManager", SDLAudioManager_tab, SDL_arraysize(SDLAudioManager_tab)); | ||
| 562 | register_methods(env, "org/libsdl/app/SDLControllerManager", SDLControllerManager_tab, SDL_arraysize(SDLControllerManager_tab)); | ||
| 563 | register_methods(env, "org/libsdl/app/HIDDeviceManager", HIDDeviceManager_tab, SDL_arraysize(HIDDeviceManager_tab)); | ||
| 564 | |||
| 565 | return JNI_VERSION_1_4; | ||
| 566 | } | ||
| 567 | |||
| 568 | void checkJNIReady(void) | ||
| 569 | { | ||
| 570 | if (!mActivityClass || !mAudioManagerClass || !mControllerManagerClass) { | ||
| 571 | // We aren't fully initialized, let's just return. | ||
| 572 | return; | ||
| 573 | } | ||
| 574 | |||
| 575 | SDL_SetMainReady(); | ||
| 576 | } | ||
| 577 | |||
| 578 | // Get SDL version -- called before SDL_main() to verify JNI bindings | ||
| 579 | JNIEXPORT jstring JNICALL SDL_JAVA_INTERFACE(nativeGetVersion)(JNIEnv *env, jclass cls) | ||
| 580 | { | ||
| 581 | char version[128]; | ||
| 582 | |||
| 583 | SDL_snprintf(version, sizeof(version), "%d.%d.%d", SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_MICRO_VERSION); | ||
| 584 | |||
| 585 | return (*env)->NewStringUTF(env, version); | ||
| 586 | } | ||
| 587 | |||
| 588 | // Activity initialization -- called before SDL_main() to initialize JNI bindings | ||
| 589 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cls) | ||
| 590 | { | ||
| 591 | __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeSetupJNI()"); | ||
| 592 | |||
| 593 | // Start with a clean slate | ||
| 594 | SDL_ClearError(); | ||
| 595 | |||
| 596 | /* | ||
| 597 | * Create mThreadKey so we can keep track of the JNIEnv assigned to each thread | ||
| 598 | * Refer to http://developer.android.com/guide/practices/design/jni.html for the rationale behind this | ||
| 599 | */ | ||
| 600 | Android_JNI_CreateKey_once(); | ||
| 601 | |||
| 602 | // Save JNIEnv of SDLActivity | ||
| 603 | Android_JNI_SetEnv(env); | ||
| 604 | |||
| 605 | if (!mJavaVM) { | ||
| 606 | __android_log_print(ANDROID_LOG_ERROR, "SDL", "failed to found a JavaVM"); | ||
| 607 | } | ||
| 608 | |||
| 609 | /* Use a mutex to prevent concurrency issues between Java Activity and Native thread code, when using 'Android_Window'. | ||
| 610 | * (Eg. Java sending Touch events, while native code is destroying the main SDL_Window. ) | ||
| 611 | */ | ||
| 612 | if (!Android_ActivityMutex) { | ||
| 613 | Android_ActivityMutex = SDL_CreateMutex(); // Could this be created twice if onCreate() is called a second time ? | ||
| 614 | } | ||
| 615 | |||
| 616 | if (!Android_ActivityMutex) { | ||
| 617 | __android_log_print(ANDROID_LOG_ERROR, "SDL", "failed to create Android_ActivityMutex mutex"); | ||
| 618 | } | ||
| 619 | |||
| 620 | Android_LifecycleMutex = SDL_CreateMutex(); | ||
| 621 | if (!Android_LifecycleMutex) { | ||
| 622 | __android_log_print(ANDROID_LOG_ERROR, "SDL", "failed to create Android_LifecycleMutex mutex"); | ||
| 623 | } | ||
| 624 | |||
| 625 | Android_LifecycleEventSem = SDL_CreateSemaphore(0); | ||
| 626 | if (!Android_LifecycleEventSem) { | ||
| 627 | __android_log_print(ANDROID_LOG_ERROR, "SDL", "failed to create Android_LifecycleEventSem semaphore"); | ||
| 628 | } | ||
| 629 | |||
| 630 | mActivityClass = (jclass)((*env)->NewGlobalRef(env, cls)); | ||
| 631 | |||
| 632 | midClipboardGetText = (*env)->GetStaticMethodID(env, mActivityClass, "clipboardGetText", "()Ljava/lang/String;"); | ||
| 633 | midClipboardHasText = (*env)->GetStaticMethodID(env, mActivityClass, "clipboardHasText", "()Z"); | ||
| 634 | midClipboardSetText = (*env)->GetStaticMethodID(env, mActivityClass, "clipboardSetText", "(Ljava/lang/String;)V"); | ||
| 635 | midCreateCustomCursor = (*env)->GetStaticMethodID(env, mActivityClass, "createCustomCursor", "([IIIII)I"); | ||
| 636 | midDestroyCustomCursor = (*env)->GetStaticMethodID(env, mActivityClass, "destroyCustomCursor", "(I)V"); | ||
| 637 | midGetContext = (*env)->GetStaticMethodID(env, mActivityClass, "getContext", "()Landroid/content/Context;"); | ||
| 638 | midGetManifestEnvironmentVariables = (*env)->GetStaticMethodID(env, mActivityClass, "getManifestEnvironmentVariables", "()Z"); | ||
| 639 | midGetNativeSurface = (*env)->GetStaticMethodID(env, mActivityClass, "getNativeSurface", "()Landroid/view/Surface;"); | ||
| 640 | midInitTouch = (*env)->GetStaticMethodID(env, mActivityClass, "initTouch", "()V"); | ||
| 641 | midIsAndroidTV = (*env)->GetStaticMethodID(env, mActivityClass, "isAndroidTV", "()Z"); | ||
| 642 | midIsChromebook = (*env)->GetStaticMethodID(env, mActivityClass, "isChromebook", "()Z"); | ||
| 643 | midIsDeXMode = (*env)->GetStaticMethodID(env, mActivityClass, "isDeXMode", "()Z"); | ||
| 644 | midIsScreenKeyboardShown = (*env)->GetStaticMethodID(env, mActivityClass, "isScreenKeyboardShown", "()Z"); | ||
| 645 | midIsTablet = (*env)->GetStaticMethodID(env, mActivityClass, "isTablet", "()Z"); | ||
| 646 | midManualBackButton = (*env)->GetStaticMethodID(env, mActivityClass, "manualBackButton", "()V"); | ||
| 647 | midMinimizeWindow = (*env)->GetStaticMethodID(env, mActivityClass, "minimizeWindow", "()V"); | ||
| 648 | midOpenURL = (*env)->GetStaticMethodID(env, mActivityClass, "openURL", "(Ljava/lang/String;)Z"); | ||
| 649 | midRequestPermission = (*env)->GetStaticMethodID(env, mActivityClass, "requestPermission", "(Ljava/lang/String;I)V"); | ||
| 650 | midShowToast = (*env)->GetStaticMethodID(env, mActivityClass, "showToast", "(Ljava/lang/String;IIII)Z"); | ||
| 651 | midSendMessage = (*env)->GetStaticMethodID(env, mActivityClass, "sendMessage", "(II)Z"); | ||
| 652 | midSetActivityTitle = (*env)->GetStaticMethodID(env, mActivityClass, "setActivityTitle", "(Ljava/lang/String;)Z"); | ||
| 653 | midSetCustomCursor = (*env)->GetStaticMethodID(env, mActivityClass, "setCustomCursor", "(I)Z"); | ||
| 654 | midSetOrientation = (*env)->GetStaticMethodID(env, mActivityClass, "setOrientation", "(IIZLjava/lang/String;)V"); | ||
| 655 | midSetRelativeMouseEnabled = (*env)->GetStaticMethodID(env, mActivityClass, "setRelativeMouseEnabled", "(Z)Z"); | ||
| 656 | midSetSystemCursor = (*env)->GetStaticMethodID(env, mActivityClass, "setSystemCursor", "(I)Z"); | ||
| 657 | midSetWindowStyle = (*env)->GetStaticMethodID(env, mActivityClass, "setWindowStyle", "(Z)V"); | ||
| 658 | midShouldMinimizeOnFocusLoss = (*env)->GetStaticMethodID(env, mActivityClass, "shouldMinimizeOnFocusLoss", "()Z"); | ||
| 659 | midShowTextInput = (*env)->GetStaticMethodID(env, mActivityClass, "showTextInput", "(IIIII)Z"); | ||
| 660 | midSupportsRelativeMouse = (*env)->GetStaticMethodID(env, mActivityClass, "supportsRelativeMouse", "()Z"); | ||
| 661 | midOpenFileDescriptor = (*env)->GetStaticMethodID(env, mActivityClass, "openFileDescriptor", "(Ljava/lang/String;Ljava/lang/String;)I"); | ||
| 662 | midShowFileDialog = (*env)->GetStaticMethodID(env, mActivityClass, "showFileDialog", "([Ljava/lang/String;ZZI)Z"); | ||
| 663 | |||
| 664 | if (!midClipboardGetText || | ||
| 665 | !midClipboardHasText || | ||
| 666 | !midClipboardSetText || | ||
| 667 | !midCreateCustomCursor || | ||
| 668 | !midDestroyCustomCursor || | ||
| 669 | !midGetContext || | ||
| 670 | !midGetManifestEnvironmentVariables || | ||
| 671 | !midGetNativeSurface || | ||
| 672 | !midInitTouch || | ||
| 673 | !midIsAndroidTV || | ||
| 674 | !midIsChromebook || | ||
| 675 | !midIsDeXMode || | ||
| 676 | !midIsScreenKeyboardShown || | ||
| 677 | !midIsTablet || | ||
| 678 | !midManualBackButton || | ||
| 679 | !midMinimizeWindow || | ||
| 680 | !midOpenURL || | ||
| 681 | !midRequestPermission || | ||
| 682 | !midShowToast || | ||
| 683 | !midSendMessage || | ||
| 684 | !midSetActivityTitle || | ||
| 685 | !midSetCustomCursor || | ||
| 686 | !midSetOrientation || | ||
| 687 | !midSetRelativeMouseEnabled || | ||
| 688 | !midSetSystemCursor || | ||
| 689 | !midSetWindowStyle || | ||
| 690 | !midShouldMinimizeOnFocusLoss || | ||
| 691 | !midShowTextInput || | ||
| 692 | !midSupportsRelativeMouse || | ||
| 693 | !midOpenFileDescriptor || | ||
| 694 | !midShowFileDialog) { | ||
| 695 | __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLActivity.java?"); | ||
| 696 | } | ||
| 697 | |||
| 698 | checkJNIReady(); | ||
| 699 | } | ||
| 700 | |||
| 701 | // Audio initialization -- called before SDL_main() to initialize JNI bindings | ||
| 702 | JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cls) | ||
| 703 | { | ||
| 704 | __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "AUDIO nativeSetupJNI()"); | ||
| 705 | |||
| 706 | mAudioManagerClass = (jclass)((*env)->NewGlobalRef(env, cls)); | ||
| 707 | |||
| 708 | midRegisterAudioDeviceCallback = (*env)->GetStaticMethodID(env, mAudioManagerClass, | ||
| 709 | "registerAudioDeviceCallback", | ||
| 710 | "()V"); | ||
| 711 | midUnregisterAudioDeviceCallback = (*env)->GetStaticMethodID(env, mAudioManagerClass, | ||
| 712 | "unregisterAudioDeviceCallback", | ||
| 713 | "()V"); | ||
| 714 | midAudioSetThreadPriority = (*env)->GetStaticMethodID(env, mAudioManagerClass, | ||
| 715 | "audioSetThreadPriority", "(ZI)V"); | ||
| 716 | |||
| 717 | if (!midRegisterAudioDeviceCallback || !midUnregisterAudioDeviceCallback || !midAudioSetThreadPriority) { | ||
| 718 | __android_log_print(ANDROID_LOG_WARN, "SDL", | ||
| 719 | "Missing some Java callbacks, do you have the latest version of SDLAudioManager.java?"); | ||
| 720 | } | ||
| 721 | |||
| 722 | checkJNIReady(); | ||
| 723 | } | ||
| 724 | |||
| 725 | // Controller initialization -- called before SDL_main() to initialize JNI bindings | ||
| 726 | JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cls) | ||
| 727 | { | ||
| 728 | __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "CONTROLLER nativeSetupJNI()"); | ||
| 729 | |||
| 730 | mControllerManagerClass = (jclass)((*env)->NewGlobalRef(env, cls)); | ||
| 731 | |||
| 732 | midPollInputDevices = (*env)->GetStaticMethodID(env, mControllerManagerClass, | ||
| 733 | "pollInputDevices", "()V"); | ||
| 734 | midPollHapticDevices = (*env)->GetStaticMethodID(env, mControllerManagerClass, | ||
| 735 | "pollHapticDevices", "()V"); | ||
| 736 | midHapticRun = (*env)->GetStaticMethodID(env, mControllerManagerClass, | ||
| 737 | "hapticRun", "(IFI)V"); | ||
| 738 | midHapticRumble = (*env)->GetStaticMethodID(env, mControllerManagerClass, | ||
| 739 | "hapticRumble", "(IFFI)V"); | ||
| 740 | midHapticStop = (*env)->GetStaticMethodID(env, mControllerManagerClass, | ||
| 741 | "hapticStop", "(I)V"); | ||
| 742 | |||
| 743 | if (!midPollInputDevices || !midPollHapticDevices || !midHapticRun || !midHapticRumble || !midHapticStop) { | ||
| 744 | __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLControllerManager.java?"); | ||
| 745 | } | ||
| 746 | |||
| 747 | checkJNIReady(); | ||
| 748 | } | ||
| 749 | |||
| 750 | // SDL main function prototype | ||
| 751 | typedef int (*SDL_main_func)(int argc, char *argv[]); | ||
| 752 | |||
| 753 | static int run_count = 0; | ||
| 754 | |||
| 755 | JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeCheckSDLThreadCounter)( | ||
| 756 | JNIEnv *env, jclass jcls) | ||
| 757 | { | ||
| 758 | int tmp = run_count; | ||
| 759 | run_count += 1; | ||
| 760 | return tmp; | ||
| 761 | } | ||
| 762 | |||
| 763 | JNIEXPORT jboolean JNICALL SDL_JAVA_INTERFACE(nativeAllowRecreateActivity)( | ||
| 764 | JNIEnv *env, jclass jcls) | ||
| 765 | { | ||
| 766 | return SDL_GetHintBoolean(SDL_HINT_ANDROID_ALLOW_RECREATE_ACTIVITY, false); | ||
| 767 | } | ||
| 768 | |||
| 769 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeInitMainThread)( | ||
| 770 | JNIEnv *env, jclass jcls) | ||
| 771 | { | ||
| 772 | __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeInitSDLThread() %d time", run_count); | ||
| 773 | run_count += 1; | ||
| 774 | |||
| 775 | // Save JNIEnv of SDLThread | ||
| 776 | Android_JNI_SetEnv(env); | ||
| 777 | } | ||
| 778 | |||
| 779 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeCleanupMainThread)( | ||
| 780 | JNIEnv *env, jclass jcls) | ||
| 781 | { | ||
| 782 | /* This is a Java thread, it doesn't need to be Detached from the JVM. | ||
| 783 | * Set to mThreadKey value to NULL not to call pthread_create destructor 'Android_JNI_ThreadDestroyed' */ | ||
| 784 | Android_JNI_SetEnv(NULL); | ||
| 785 | } | ||
| 786 | |||
| 787 | // Start up the SDL app | ||
| 788 | JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeRunMain)(JNIEnv *env, jclass cls, jstring library, jstring function, jobject array) | ||
| 789 | { | ||
| 790 | int status = -1; | ||
| 791 | const char *library_file; | ||
| 792 | void *library_handle; | ||
| 793 | |||
| 794 | library_file = (*env)->GetStringUTFChars(env, library, NULL); | ||
| 795 | library_handle = dlopen(library_file, RTLD_GLOBAL); | ||
| 796 | |||
| 797 | if (library_handle == NULL) { | ||
| 798 | /* When deploying android app bundle format uncompressed native libs may not extract from apk to filesystem. | ||
| 799 | In this case we should use lib name without path. https://bugzilla.libsdl.org/show_bug.cgi?id=4739 */ | ||
| 800 | const char *library_name = SDL_strrchr(library_file, '/'); | ||
| 801 | if (library_name && *library_name) { | ||
| 802 | library_name += 1; | ||
| 803 | library_handle = dlopen(library_name, RTLD_GLOBAL); | ||
| 804 | } | ||
| 805 | } | ||
| 806 | |||
| 807 | if (library_handle) { | ||
| 808 | const char *function_name; | ||
| 809 | SDL_main_func SDL_main; | ||
| 810 | |||
| 811 | function_name = (*env)->GetStringUTFChars(env, function, NULL); | ||
| 812 | SDL_main = (SDL_main_func)dlsym(library_handle, function_name); | ||
| 813 | if (SDL_main) { | ||
| 814 | int i; | ||
| 815 | int argc; | ||
| 816 | int len; | ||
| 817 | char **argv; | ||
| 818 | bool isstack; | ||
| 819 | |||
| 820 | // Prepare the arguments. | ||
| 821 | len = (*env)->GetArrayLength(env, array); | ||
| 822 | argv = SDL_small_alloc(char *, 1 + len + 1, &isstack); // !!! FIXME: check for NULL | ||
| 823 | argc = 0; | ||
| 824 | /* Use the name "app_process" so PHYSFS_platformCalcBaseDir() works. | ||
| 825 | https://github.com/love2d/love-android/issues/24 | ||
| 826 | */ | ||
| 827 | argv[argc++] = SDL_strdup("app_process"); | ||
| 828 | for (i = 0; i < len; ++i) { | ||
| 829 | char *arg = NULL; | ||
| 830 | jstring string = (*env)->GetObjectArrayElement(env, array, i); | ||
| 831 | if (string) { | ||
| 832 | const char *utf = (*env)->GetStringUTFChars(env, string, 0); | ||
| 833 | if (utf) { | ||
| 834 | arg = SDL_strdup(utf); | ||
| 835 | (*env)->ReleaseStringUTFChars(env, string, utf); | ||
| 836 | } | ||
| 837 | (*env)->DeleteLocalRef(env, string); | ||
| 838 | } | ||
| 839 | if (arg == NULL) { | ||
| 840 | arg = SDL_strdup(""); | ||
| 841 | } | ||
| 842 | argv[argc++] = arg; | ||
| 843 | } | ||
| 844 | argv[argc] = NULL; | ||
| 845 | |||
| 846 | // Run the application. | ||
| 847 | status = SDL_main(argc, argv); | ||
| 848 | |||
| 849 | // Release the arguments. | ||
| 850 | for (i = 0; i < argc; ++i) { | ||
| 851 | SDL_free(argv[i]); | ||
| 852 | } | ||
| 853 | SDL_small_free(argv, isstack); | ||
| 854 | |||
| 855 | } else { | ||
| 856 | __android_log_print(ANDROID_LOG_ERROR, "SDL", "nativeRunMain(): Couldn't find function %s in library %s", function_name, library_file); | ||
| 857 | } | ||
| 858 | (*env)->ReleaseStringUTFChars(env, function, function_name); | ||
| 859 | |||
| 860 | dlclose(library_handle); | ||
| 861 | |||
| 862 | } else { | ||
| 863 | __android_log_print(ANDROID_LOG_ERROR, "SDL", "nativeRunMain(): Couldn't load library %s", library_file); | ||
| 864 | } | ||
| 865 | (*env)->ReleaseStringUTFChars(env, library, library_file); | ||
| 866 | |||
| 867 | // Do not issue an exit or the whole application will terminate instead of just the SDL thread | ||
| 868 | // exit(status); | ||
| 869 | |||
| 870 | return status; | ||
| 871 | } | ||
| 872 | |||
| 873 | static int FindLifecycleEvent(SDL_AndroidLifecycleEvent event) | ||
| 874 | { | ||
| 875 | for (int index = 0; index < Android_NumLifecycleEvents; ++index) { | ||
| 876 | if (Android_LifecycleEvents[index] == event) { | ||
| 877 | return index; | ||
| 878 | } | ||
| 879 | } | ||
| 880 | return -1; | ||
| 881 | } | ||
| 882 | |||
| 883 | static void RemoveLifecycleEvent(int index) | ||
| 884 | { | ||
| 885 | if (index < Android_NumLifecycleEvents - 1) { | ||
| 886 | SDL_memmove(&Android_LifecycleEvents[index], &Android_LifecycleEvents[index+1], (Android_NumLifecycleEvents - index - 1) * sizeof(Android_LifecycleEvents[index])); | ||
| 887 | } | ||
| 888 | --Android_NumLifecycleEvents; | ||
| 889 | } | ||
| 890 | |||
| 891 | void Android_SendLifecycleEvent(SDL_AndroidLifecycleEvent event) | ||
| 892 | { | ||
| 893 | SDL_LockMutex(Android_LifecycleMutex); | ||
| 894 | { | ||
| 895 | int index; | ||
| 896 | bool add_event = true; | ||
| 897 | |||
| 898 | switch (event) { | ||
| 899 | case SDL_ANDROID_LIFECYCLE_WAKE: | ||
| 900 | // We don't need more than one wake queued | ||
| 901 | index = FindLifecycleEvent(SDL_ANDROID_LIFECYCLE_WAKE); | ||
| 902 | if (index >= 0) { | ||
| 903 | add_event = false; | ||
| 904 | } | ||
| 905 | break; | ||
| 906 | case SDL_ANDROID_LIFECYCLE_PAUSE: | ||
| 907 | // If we have a resume queued, just stay in the paused state | ||
| 908 | index = FindLifecycleEvent(SDL_ANDROID_LIFECYCLE_RESUME); | ||
| 909 | if (index >= 0) { | ||
| 910 | RemoveLifecycleEvent(index); | ||
| 911 | add_event = false; | ||
| 912 | } | ||
| 913 | break; | ||
| 914 | case SDL_ANDROID_LIFECYCLE_RESUME: | ||
| 915 | // If we have a pause queued, just stay in the resumed state | ||
| 916 | index = FindLifecycleEvent(SDL_ANDROID_LIFECYCLE_PAUSE); | ||
| 917 | if (index >= 0) { | ||
| 918 | RemoveLifecycleEvent(index); | ||
| 919 | add_event = false; | ||
| 920 | } | ||
| 921 | break; | ||
| 922 | case SDL_ANDROID_LIFECYCLE_LOWMEMORY: | ||
| 923 | // We don't need more than one low memory event queued | ||
| 924 | index = FindLifecycleEvent(SDL_ANDROID_LIFECYCLE_LOWMEMORY); | ||
| 925 | if (index >= 0) { | ||
| 926 | add_event = false; | ||
| 927 | } | ||
| 928 | break; | ||
| 929 | case SDL_ANDROID_LIFECYCLE_DESTROY: | ||
| 930 | // Remove all other events, we're done! | ||
| 931 | while (Android_NumLifecycleEvents > 0) { | ||
| 932 | RemoveLifecycleEvent(0); | ||
| 933 | } | ||
| 934 | break; | ||
| 935 | default: | ||
| 936 | SDL_assert(!"Sending unexpected lifecycle event"); | ||
| 937 | add_event = false; | ||
| 938 | break; | ||
| 939 | } | ||
| 940 | |||
| 941 | if (add_event) { | ||
| 942 | SDL_assert(Android_NumLifecycleEvents < SDL_arraysize(Android_LifecycleEvents)); | ||
| 943 | Android_LifecycleEvents[Android_NumLifecycleEvents++] = event; | ||
| 944 | SDL_SignalSemaphore(Android_LifecycleEventSem); | ||
| 945 | } | ||
| 946 | } | ||
| 947 | SDL_UnlockMutex(Android_LifecycleMutex); | ||
| 948 | } | ||
| 949 | |||
| 950 | bool Android_WaitLifecycleEvent(SDL_AndroidLifecycleEvent *event, Sint64 timeoutNS) | ||
| 951 | { | ||
| 952 | bool got_event = false; | ||
| 953 | |||
| 954 | while (!got_event && SDL_WaitSemaphoreTimeoutNS(Android_LifecycleEventSem, timeoutNS)) { | ||
| 955 | SDL_LockMutex(Android_LifecycleMutex); | ||
| 956 | { | ||
| 957 | if (Android_NumLifecycleEvents > 0) { | ||
| 958 | *event = Android_LifecycleEvents[0]; | ||
| 959 | RemoveLifecycleEvent(0); | ||
| 960 | got_event = true; | ||
| 961 | } | ||
| 962 | } | ||
| 963 | SDL_UnlockMutex(Android_LifecycleMutex); | ||
| 964 | } | ||
| 965 | return got_event; | ||
| 966 | } | ||
| 967 | |||
| 968 | void Android_LockActivityMutex(void) | ||
| 969 | { | ||
| 970 | SDL_LockMutex(Android_ActivityMutex); | ||
| 971 | } | ||
| 972 | |||
| 973 | void Android_UnlockActivityMutex(void) | ||
| 974 | { | ||
| 975 | SDL_UnlockMutex(Android_ActivityMutex); | ||
| 976 | } | ||
| 977 | |||
| 978 | // Drop file | ||
| 979 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeDropFile)( | ||
| 980 | JNIEnv *env, jclass jcls, | ||
| 981 | jstring filename) | ||
| 982 | { | ||
| 983 | const char *path = (*env)->GetStringUTFChars(env, filename, NULL); | ||
| 984 | SDL_SendDropFile(NULL, NULL, path); | ||
| 985 | (*env)->ReleaseStringUTFChars(env, filename, path); | ||
| 986 | SDL_SendDropComplete(NULL); | ||
| 987 | } | ||
| 988 | |||
| 989 | // Set screen resolution | ||
| 990 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetScreenResolution)( | ||
| 991 | JNIEnv *env, jclass jcls, | ||
| 992 | jint surfaceWidth, jint surfaceHeight, | ||
| 993 | jint deviceWidth, jint deviceHeight, jfloat density, jfloat rate) | ||
| 994 | { | ||
| 995 | SDL_LockMutex(Android_ActivityMutex); | ||
| 996 | |||
| 997 | Android_SetScreenResolution(surfaceWidth, surfaceHeight, deviceWidth, deviceHeight, density, rate); | ||
| 998 | |||
| 999 | SDL_UnlockMutex(Android_ActivityMutex); | ||
| 1000 | } | ||
| 1001 | |||
| 1002 | // Resize | ||
| 1003 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeResize)( | ||
| 1004 | JNIEnv *env, jclass jcls) | ||
| 1005 | { | ||
| 1006 | SDL_LockMutex(Android_ActivityMutex); | ||
| 1007 | |||
| 1008 | if (Android_Window) { | ||
| 1009 | Android_SendResize(Android_Window); | ||
| 1010 | } | ||
| 1011 | |||
| 1012 | SDL_UnlockMutex(Android_ActivityMutex); | ||
| 1013 | } | ||
| 1014 | |||
| 1015 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetNaturalOrientation)( | ||
| 1016 | JNIEnv *env, jclass jcls, | ||
| 1017 | jint orientation) | ||
| 1018 | { | ||
| 1019 | displayNaturalOrientation = (SDL_DisplayOrientation)orientation; | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeRotationChanged)( | ||
| 1023 | JNIEnv *env, jclass jcls, | ||
| 1024 | jint rotation) | ||
| 1025 | { | ||
| 1026 | SDL_LockMutex(Android_ActivityMutex); | ||
| 1027 | |||
| 1028 | if (displayNaturalOrientation == SDL_ORIENTATION_LANDSCAPE) { | ||
| 1029 | rotation += 90; | ||
| 1030 | } | ||
| 1031 | |||
| 1032 | switch (rotation % 360) { | ||
| 1033 | case 0: | ||
| 1034 | displayCurrentOrientation = SDL_ORIENTATION_PORTRAIT; | ||
| 1035 | break; | ||
| 1036 | case 90: | ||
| 1037 | displayCurrentOrientation = SDL_ORIENTATION_LANDSCAPE; | ||
| 1038 | break; | ||
| 1039 | case 180: | ||
| 1040 | displayCurrentOrientation = SDL_ORIENTATION_PORTRAIT_FLIPPED; | ||
| 1041 | break; | ||
| 1042 | case 270: | ||
| 1043 | displayCurrentOrientation = SDL_ORIENTATION_LANDSCAPE_FLIPPED; | ||
| 1044 | break; | ||
| 1045 | default: | ||
| 1046 | displayCurrentOrientation = SDL_ORIENTATION_UNKNOWN; | ||
| 1047 | break; | ||
| 1048 | } | ||
| 1049 | |||
| 1050 | Android_SetOrientation(displayCurrentOrientation); | ||
| 1051 | |||
| 1052 | SDL_UnlockMutex(Android_ActivityMutex); | ||
| 1053 | } | ||
| 1054 | |||
| 1055 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeInsetsChanged)( | ||
| 1056 | JNIEnv *env, jclass jcls, | ||
| 1057 | jint left, jint right, jint top, jint bottom) | ||
| 1058 | { | ||
| 1059 | SDL_LockMutex(Android_ActivityMutex); | ||
| 1060 | |||
| 1061 | Android_SetWindowSafeAreaInsets(left, right, top, bottom); | ||
| 1062 | |||
| 1063 | SDL_UnlockMutex(Android_ActivityMutex); | ||
| 1064 | } | ||
| 1065 | |||
| 1066 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeAddTouch)( | ||
| 1067 | JNIEnv *env, jclass cls, | ||
| 1068 | jint touchId, jstring name) | ||
| 1069 | { | ||
| 1070 | const char *utfname = (*env)->GetStringUTFChars(env, name, NULL); | ||
| 1071 | |||
| 1072 | SDL_AddTouch((SDL_TouchID)touchId, SDL_TOUCH_DEVICE_DIRECT, utfname); | ||
| 1073 | |||
| 1074 | (*env)->ReleaseStringUTFChars(env, name, utfname); | ||
| 1075 | } | ||
| 1076 | |||
| 1077 | JNIEXPORT void JNICALL | ||
| 1078 | SDL_JAVA_AUDIO_INTERFACE(addAudioDevice)(JNIEnv *env, jclass jcls, jboolean recording, | ||
| 1079 | jstring name, jint device_id) | ||
| 1080 | { | ||
| 1081 | #if ALLOW_MULTIPLE_ANDROID_AUDIO_DEVICES | ||
| 1082 | if (SDL_GetCurrentAudioDriver() != NULL) { | ||
| 1083 | void *handle = (void *)((size_t)device_id); | ||
| 1084 | if (!SDL_FindPhysicalAudioDeviceByHandle(handle)) { | ||
| 1085 | const char *utf8name = (*env)->GetStringUTFChars(env, name, NULL); | ||
| 1086 | SDL_AddAudioDevice(recording, SDL_strdup(utf8name), NULL, handle); | ||
| 1087 | (*env)->ReleaseStringUTFChars(env, name, utf8name); | ||
| 1088 | } | ||
| 1089 | } | ||
| 1090 | #endif | ||
| 1091 | } | ||
| 1092 | |||
| 1093 | JNIEXPORT void JNICALL | ||
| 1094 | SDL_JAVA_AUDIO_INTERFACE(removeAudioDevice)(JNIEnv *env, jclass jcls, jboolean recording, | ||
| 1095 | jint device_id) | ||
| 1096 | { | ||
| 1097 | #if ALLOW_MULTIPLE_ANDROID_AUDIO_DEVICES | ||
| 1098 | if (SDL_GetCurrentAudioDriver() != NULL) { | ||
| 1099 | SDL_Log("Removing device with handle %d, recording %d", device_id, recording); | ||
| 1100 | SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByHandle((void *)((size_t)device_id))); | ||
| 1101 | } | ||
| 1102 | #endif | ||
| 1103 | } | ||
| 1104 | |||
| 1105 | // Paddown | ||
| 1106 | JNIEXPORT jboolean JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)( | ||
| 1107 | JNIEnv *env, jclass jcls, | ||
| 1108 | jint device_id, jint keycode) | ||
| 1109 | { | ||
| 1110 | #ifdef SDL_JOYSTICK_ANDROID | ||
| 1111 | return Android_OnPadDown(device_id, keycode); | ||
| 1112 | #else | ||
| 1113 | return false; | ||
| 1114 | #endif // SDL_JOYSTICK_ANDROID | ||
| 1115 | } | ||
| 1116 | |||
| 1117 | // Padup | ||
| 1118 | JNIEXPORT jboolean JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)( | ||
| 1119 | JNIEnv *env, jclass jcls, | ||
| 1120 | jint device_id, jint keycode) | ||
| 1121 | { | ||
| 1122 | #ifdef SDL_JOYSTICK_ANDROID | ||
| 1123 | return Android_OnPadUp(device_id, keycode); | ||
| 1124 | #else | ||
| 1125 | return false; | ||
| 1126 | #endif // SDL_JOYSTICK_ANDROID | ||
| 1127 | } | ||
| 1128 | |||
| 1129 | // Joy | ||
| 1130 | JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)( | ||
| 1131 | JNIEnv *env, jclass jcls, | ||
| 1132 | jint device_id, jint axis, jfloat value) | ||
| 1133 | { | ||
| 1134 | #ifdef SDL_JOYSTICK_ANDROID | ||
| 1135 | Android_OnJoy(device_id, axis, value); | ||
| 1136 | #endif // SDL_JOYSTICK_ANDROID | ||
| 1137 | } | ||
| 1138 | |||
| 1139 | // POV Hat | ||
| 1140 | JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)( | ||
| 1141 | JNIEnv *env, jclass jcls, | ||
| 1142 | jint device_id, jint hat_id, jint x, jint y) | ||
| 1143 | { | ||
| 1144 | #ifdef SDL_JOYSTICK_ANDROID | ||
| 1145 | Android_OnHat(device_id, hat_id, x, y); | ||
| 1146 | #endif // SDL_JOYSTICK_ANDROID | ||
| 1147 | } | ||
| 1148 | |||
| 1149 | JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)( | ||
| 1150 | JNIEnv *env, jclass jcls, | ||
| 1151 | jint device_id, jstring device_name, jstring device_desc, | ||
| 1152 | jint vendor_id, jint product_id, | ||
| 1153 | jint button_mask, jint naxes, jint axis_mask, jint nhats, jboolean can_rumble) | ||
| 1154 | { | ||
| 1155 | #ifdef SDL_JOYSTICK_ANDROID | ||
| 1156 | const char *name = (*env)->GetStringUTFChars(env, device_name, NULL); | ||
| 1157 | const char *desc = (*env)->GetStringUTFChars(env, device_desc, NULL); | ||
| 1158 | |||
| 1159 | Android_AddJoystick(device_id, name, desc, vendor_id, product_id, button_mask, naxes, axis_mask, nhats, can_rumble); | ||
| 1160 | |||
| 1161 | (*env)->ReleaseStringUTFChars(env, device_name, name); | ||
| 1162 | (*env)->ReleaseStringUTFChars(env, device_desc, desc); | ||
| 1163 | #endif // SDL_JOYSTICK_ANDROID | ||
| 1164 | } | ||
| 1165 | |||
| 1166 | JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)( | ||
| 1167 | JNIEnv *env, jclass jcls, | ||
| 1168 | jint device_id) | ||
| 1169 | { | ||
| 1170 | #ifdef SDL_JOYSTICK_ANDROID | ||
| 1171 | Android_RemoveJoystick(device_id); | ||
| 1172 | #endif // SDL_JOYSTICK_ANDROID | ||
| 1173 | } | ||
| 1174 | |||
| 1175 | JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic)( | ||
| 1176 | JNIEnv *env, jclass jcls, jint device_id, jstring device_name) | ||
| 1177 | { | ||
| 1178 | #ifdef SDL_HAPTIC_ANDROID | ||
| 1179 | const char *name = (*env)->GetStringUTFChars(env, device_name, NULL); | ||
| 1180 | |||
| 1181 | Android_AddHaptic(device_id, name); | ||
| 1182 | |||
| 1183 | (*env)->ReleaseStringUTFChars(env, device_name, name); | ||
| 1184 | #endif // SDL_HAPTIC_ANDROID | ||
| 1185 | } | ||
| 1186 | |||
| 1187 | JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)( | ||
| 1188 | JNIEnv *env, jclass jcls, jint device_id) | ||
| 1189 | { | ||
| 1190 | #ifdef SDL_HAPTIC_ANDROID | ||
| 1191 | Android_RemoveHaptic(device_id); | ||
| 1192 | #endif | ||
| 1193 | } | ||
| 1194 | |||
| 1195 | // Called from surfaceCreated() | ||
| 1196 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceCreated)(JNIEnv *env, jclass jcls) | ||
| 1197 | { | ||
| 1198 | SDL_LockMutex(Android_ActivityMutex); | ||
| 1199 | |||
| 1200 | if (Android_Window) { | ||
| 1201 | SDL_WindowData *data = Android_Window->internal; | ||
| 1202 | |||
| 1203 | data->native_window = Android_JNI_GetNativeWindow(); | ||
| 1204 | SDL_SetPointerProperty(SDL_GetWindowProperties(Android_Window), SDL_PROP_WINDOW_ANDROID_WINDOW_POINTER, data->native_window); | ||
| 1205 | if (data->native_window == NULL) { | ||
| 1206 | SDL_SetError("Could not fetch native window from UI thread"); | ||
| 1207 | } | ||
| 1208 | } | ||
| 1209 | |||
| 1210 | SDL_UnlockMutex(Android_ActivityMutex); | ||
| 1211 | } | ||
| 1212 | |||
| 1213 | // Called from surfaceChanged() | ||
| 1214 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(JNIEnv *env, jclass jcls) | ||
| 1215 | { | ||
| 1216 | SDL_LockMutex(Android_ActivityMutex); | ||
| 1217 | |||
| 1218 | #ifdef SDL_VIDEO_OPENGL_EGL | ||
| 1219 | if (Android_Window && (Android_Window->flags & SDL_WINDOW_OPENGL)) { | ||
| 1220 | SDL_VideoDevice *_this = SDL_GetVideoDevice(); | ||
| 1221 | SDL_WindowData *data = Android_Window->internal; | ||
| 1222 | |||
| 1223 | // If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here | ||
| 1224 | if (data->egl_surface == EGL_NO_SURFACE) { | ||
| 1225 | data->egl_surface = SDL_EGL_CreateSurface(_this, Android_Window, (NativeWindowType)data->native_window); | ||
| 1226 | SDL_SetPointerProperty(SDL_GetWindowProperties(Android_Window), SDL_PROP_WINDOW_ANDROID_SURFACE_POINTER, data->egl_surface); | ||
| 1227 | } | ||
| 1228 | |||
| 1229 | // GL Context handling is done in the event loop because this function is run from the Java thread | ||
| 1230 | } | ||
| 1231 | #endif | ||
| 1232 | |||
| 1233 | SDL_UnlockMutex(Android_ActivityMutex); | ||
| 1234 | } | ||
| 1235 | |||
| 1236 | // Called from surfaceDestroyed() | ||
| 1237 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)(JNIEnv *env, jclass jcls) | ||
| 1238 | { | ||
| 1239 | int nb_attempt = 50; | ||
| 1240 | |||
| 1241 | retry: | ||
| 1242 | |||
| 1243 | SDL_LockMutex(Android_ActivityMutex); | ||
| 1244 | |||
| 1245 | if (Android_Window) { | ||
| 1246 | SDL_WindowData *data = Android_Window->internal; | ||
| 1247 | |||
| 1248 | // Wait for Main thread being paused and context un-activated to release 'egl_surface' | ||
| 1249 | if ((Android_Window->flags & SDL_WINDOW_OPENGL) && !data->backup_done) { | ||
| 1250 | nb_attempt -= 1; | ||
| 1251 | if (nb_attempt == 0) { | ||
| 1252 | SDL_SetError("Try to release egl_surface with context probably still active"); | ||
| 1253 | } else { | ||
| 1254 | SDL_UnlockMutex(Android_ActivityMutex); | ||
| 1255 | SDL_Delay(10); | ||
| 1256 | goto retry; | ||
| 1257 | } | ||
| 1258 | } | ||
| 1259 | |||
| 1260 | #ifdef SDL_VIDEO_OPENGL_EGL | ||
| 1261 | if (data->egl_surface != EGL_NO_SURFACE) { | ||
| 1262 | SDL_EGL_DestroySurface(SDL_GetVideoDevice(), data->egl_surface); | ||
| 1263 | data->egl_surface = EGL_NO_SURFACE; | ||
| 1264 | } | ||
| 1265 | #endif | ||
| 1266 | |||
| 1267 | if (data->native_window) { | ||
| 1268 | ANativeWindow_release(data->native_window); | ||
| 1269 | data->native_window = NULL; | ||
| 1270 | } | ||
| 1271 | |||
| 1272 | // GL Context handling is done in the event loop because this function is run from the Java thread | ||
| 1273 | } | ||
| 1274 | |||
| 1275 | SDL_UnlockMutex(Android_ActivityMutex); | ||
| 1276 | } | ||
| 1277 | |||
| 1278 | // Keydown | ||
| 1279 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyDown)( | ||
| 1280 | JNIEnv *env, jclass jcls, | ||
| 1281 | jint keycode) | ||
| 1282 | { | ||
| 1283 | SDL_LockMutex(Android_ActivityMutex); | ||
| 1284 | |||
| 1285 | if (Android_Window) { | ||
| 1286 | Android_OnKeyDown(keycode); | ||
| 1287 | } | ||
| 1288 | |||
| 1289 | SDL_UnlockMutex(Android_ActivityMutex); | ||
| 1290 | } | ||
| 1291 | |||
| 1292 | // Keyup | ||
| 1293 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyUp)( | ||
| 1294 | JNIEnv *env, jclass jcls, | ||
| 1295 | jint keycode) | ||
| 1296 | { | ||
| 1297 | SDL_LockMutex(Android_ActivityMutex); | ||
| 1298 | |||
| 1299 | if (Android_Window) { | ||
| 1300 | Android_OnKeyUp(keycode); | ||
| 1301 | } | ||
| 1302 | |||
| 1303 | SDL_UnlockMutex(Android_ActivityMutex); | ||
| 1304 | } | ||
| 1305 | |||
| 1306 | // Virtual keyboard return key might stop text input | ||
| 1307 | JNIEXPORT jboolean JNICALL SDL_JAVA_INTERFACE(onNativeSoftReturnKey)( | ||
| 1308 | JNIEnv *env, jclass jcls) | ||
| 1309 | { | ||
| 1310 | if (SDL_GetHintBoolean(SDL_HINT_RETURN_KEY_HIDES_IME, false)) { | ||
| 1311 | SDL_StopTextInput(Android_Window); | ||
| 1312 | return JNI_TRUE; | ||
| 1313 | } | ||
| 1314 | return JNI_FALSE; | ||
| 1315 | } | ||
| 1316 | |||
| 1317 | // Keyboard Focus Lost | ||
| 1318 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyboardFocusLost)( | ||
| 1319 | JNIEnv *env, jclass jcls) | ||
| 1320 | { | ||
| 1321 | // Calling SDL_StopTextInput will take care of hiding the keyboard and cleaning up the DummyText widget | ||
| 1322 | SDL_StopTextInput(Android_Window); | ||
| 1323 | } | ||
| 1324 | |||
| 1325 | // Touch | ||
| 1326 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeTouch)( | ||
| 1327 | JNIEnv *env, jclass jcls, | ||
| 1328 | jint touch_device_id_in, jint pointer_finger_id_in, | ||
| 1329 | jint action, jfloat x, jfloat y, jfloat p) | ||
| 1330 | { | ||
| 1331 | SDL_LockMutex(Android_ActivityMutex); | ||
| 1332 | |||
| 1333 | Android_OnTouch(Android_Window, touch_device_id_in, pointer_finger_id_in, action, x, y, p); | ||
| 1334 | |||
| 1335 | SDL_UnlockMutex(Android_ActivityMutex); | ||
| 1336 | } | ||
| 1337 | |||
| 1338 | // Mouse | ||
| 1339 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeMouse)( | ||
| 1340 | JNIEnv *env, jclass jcls, | ||
| 1341 | jint button, jint action, jfloat x, jfloat y, jboolean relative) | ||
| 1342 | { | ||
| 1343 | SDL_LockMutex(Android_ActivityMutex); | ||
| 1344 | |||
| 1345 | Android_OnMouse(Android_Window, button, action, x, y, relative); | ||
| 1346 | |||
| 1347 | SDL_UnlockMutex(Android_ActivityMutex); | ||
| 1348 | } | ||
| 1349 | |||
| 1350 | // Pen | ||
| 1351 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativePen)( | ||
| 1352 | JNIEnv *env, jclass jcls, | ||
| 1353 | jint pen_id_in, jint button, jint action, jfloat x, jfloat y, jfloat p) | ||
| 1354 | { | ||
| 1355 | SDL_LockMutex(Android_ActivityMutex); | ||
| 1356 | |||
| 1357 | Android_OnPen(Android_Window, pen_id_in, button, action, x, y, p); | ||
| 1358 | |||
| 1359 | SDL_UnlockMutex(Android_ActivityMutex); | ||
| 1360 | } | ||
| 1361 | |||
| 1362 | // Accelerometer | ||
| 1363 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeAccel)( | ||
| 1364 | JNIEnv *env, jclass jcls, | ||
| 1365 | jfloat x, jfloat y, jfloat z) | ||
| 1366 | { | ||
| 1367 | fLastAccelerometer[0] = x; | ||
| 1368 | fLastAccelerometer[1] = y; | ||
| 1369 | fLastAccelerometer[2] = z; | ||
| 1370 | bHasNewData = true; | ||
| 1371 | } | ||
| 1372 | |||
| 1373 | // Clipboard | ||
| 1374 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeClipboardChanged)( | ||
| 1375 | JNIEnv *env, jclass jcls) | ||
| 1376 | { | ||
| 1377 | // TODO: compute new mime types | ||
| 1378 | SDL_SendClipboardUpdate(false, NULL, 0); | ||
| 1379 | } | ||
| 1380 | |||
| 1381 | // Low memory | ||
| 1382 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeLowMemory)( | ||
| 1383 | JNIEnv *env, jclass cls) | ||
| 1384 | { | ||
| 1385 | Android_SendLifecycleEvent(SDL_ANDROID_LIFECYCLE_LOWMEMORY); | ||
| 1386 | } | ||
| 1387 | |||
| 1388 | /* Locale | ||
| 1389 | * requires android:configChanges="layoutDirection|locale" in AndroidManifest.xml */ | ||
| 1390 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeLocaleChanged)( | ||
| 1391 | JNIEnv *env, jclass cls) | ||
| 1392 | { | ||
| 1393 | SDL_SendAppEvent(SDL_EVENT_LOCALE_CHANGED); | ||
| 1394 | } | ||
| 1395 | |||
| 1396 | // Dark mode | ||
| 1397 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeDarkModeChanged)( | ||
| 1398 | JNIEnv *env, jclass cls, jboolean enabled) | ||
| 1399 | { | ||
| 1400 | Android_SetDarkMode(enabled); | ||
| 1401 | } | ||
| 1402 | |||
| 1403 | // Send Quit event to "SDLThread" thread | ||
| 1404 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSendQuit)( | ||
| 1405 | JNIEnv *env, jclass cls) | ||
| 1406 | { | ||
| 1407 | Android_SendLifecycleEvent(SDL_ANDROID_LIFECYCLE_DESTROY); | ||
| 1408 | } | ||
| 1409 | |||
| 1410 | // Activity ends | ||
| 1411 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeQuit)( | ||
| 1412 | JNIEnv *env, jclass cls) | ||
| 1413 | { | ||
| 1414 | const char *str; | ||
| 1415 | |||
| 1416 | if (Android_ActivityMutex) { | ||
| 1417 | SDL_DestroyMutex(Android_ActivityMutex); | ||
| 1418 | Android_ActivityMutex = NULL; | ||
| 1419 | } | ||
| 1420 | |||
| 1421 | if (Android_LifecycleMutex) { | ||
| 1422 | SDL_DestroyMutex(Android_LifecycleMutex); | ||
| 1423 | Android_LifecycleMutex = NULL; | ||
| 1424 | } | ||
| 1425 | |||
| 1426 | if (Android_LifecycleEventSem) { | ||
| 1427 | SDL_DestroySemaphore(Android_LifecycleEventSem); | ||
| 1428 | Android_LifecycleEventSem = NULL; | ||
| 1429 | } | ||
| 1430 | |||
| 1431 | Android_NumLifecycleEvents = 0; | ||
| 1432 | |||
| 1433 | Internal_Android_Destroy_AssetManager(); | ||
| 1434 | |||
| 1435 | str = SDL_GetError(); | ||
| 1436 | if (str && str[0]) { | ||
| 1437 | __android_log_print(ANDROID_LOG_ERROR, "SDL", "SDLActivity thread ends (error=%s)", str); | ||
| 1438 | } else { | ||
| 1439 | __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDLActivity thread ends"); | ||
| 1440 | } | ||
| 1441 | } | ||
| 1442 | |||
| 1443 | // Pause | ||
| 1444 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePause)( | ||
| 1445 | JNIEnv *env, jclass cls) | ||
| 1446 | { | ||
| 1447 | __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativePause()"); | ||
| 1448 | |||
| 1449 | Android_SendLifecycleEvent(SDL_ANDROID_LIFECYCLE_PAUSE); | ||
| 1450 | } | ||
| 1451 | |||
| 1452 | // Resume | ||
| 1453 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeResume)( | ||
| 1454 | JNIEnv *env, jclass cls) | ||
| 1455 | { | ||
| 1456 | __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeResume()"); | ||
| 1457 | |||
| 1458 | Android_SendLifecycleEvent(SDL_ANDROID_LIFECYCLE_RESUME); | ||
| 1459 | } | ||
| 1460 | |||
| 1461 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeFocusChanged)( | ||
| 1462 | JNIEnv *env, jclass cls, jboolean hasFocus) | ||
| 1463 | { | ||
| 1464 | SDL_LockMutex(Android_ActivityMutex); | ||
| 1465 | |||
| 1466 | if (Android_Window) { | ||
| 1467 | __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeFocusChanged()"); | ||
| 1468 | SDL_SendWindowEvent(Android_Window, (hasFocus ? SDL_EVENT_WINDOW_FOCUS_GAINED : SDL_EVENT_WINDOW_FOCUS_LOST), 0, 0); | ||
| 1469 | } | ||
| 1470 | |||
| 1471 | SDL_UnlockMutex(Android_ActivityMutex); | ||
| 1472 | } | ||
| 1473 | |||
| 1474 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeCommitText)( | ||
| 1475 | JNIEnv *env, jclass cls, | ||
| 1476 | jstring text, jint newCursorPosition) | ||
| 1477 | { | ||
| 1478 | const char *utftext = (*env)->GetStringUTFChars(env, text, NULL); | ||
| 1479 | |||
| 1480 | SDL_SendKeyboardText(utftext); | ||
| 1481 | |||
| 1482 | (*env)->ReleaseStringUTFChars(env, text, utftext); | ||
| 1483 | } | ||
| 1484 | |||
| 1485 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeGenerateScancodeForUnichar)( | ||
| 1486 | JNIEnv *env, jclass cls, | ||
| 1487 | jchar chUnicode) | ||
| 1488 | { | ||
| 1489 | SDL_SendKeyboardUnicodeKey(0, chUnicode); | ||
| 1490 | } | ||
| 1491 | |||
| 1492 | JNIEXPORT jstring JNICALL SDL_JAVA_INTERFACE(nativeGetHint)( | ||
| 1493 | JNIEnv *env, jclass cls, | ||
| 1494 | jstring name) | ||
| 1495 | { | ||
| 1496 | const char *utfname = (*env)->GetStringUTFChars(env, name, NULL); | ||
| 1497 | const char *hint = SDL_GetHint(utfname); | ||
| 1498 | |||
| 1499 | jstring result = (*env)->NewStringUTF(env, hint); | ||
| 1500 | (*env)->ReleaseStringUTFChars(env, name, utfname); | ||
| 1501 | |||
| 1502 | return result; | ||
| 1503 | } | ||
| 1504 | |||
| 1505 | JNIEXPORT jboolean JNICALL SDL_JAVA_INTERFACE(nativeGetHintBoolean)( | ||
| 1506 | JNIEnv *env, jclass cls, | ||
| 1507 | jstring name, jboolean default_value) | ||
| 1508 | { | ||
| 1509 | jboolean result; | ||
| 1510 | |||
| 1511 | const char *utfname = (*env)->GetStringUTFChars(env, name, NULL); | ||
| 1512 | result = SDL_GetHintBoolean(utfname, default_value); | ||
| 1513 | (*env)->ReleaseStringUTFChars(env, name, utfname); | ||
| 1514 | |||
| 1515 | return result; | ||
| 1516 | } | ||
| 1517 | |||
| 1518 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetenv)( | ||
| 1519 | JNIEnv *env, jclass cls, | ||
| 1520 | jstring name, jstring value) | ||
| 1521 | { | ||
| 1522 | const char *utfname = (*env)->GetStringUTFChars(env, name, NULL); | ||
| 1523 | const char *utfvalue = (*env)->GetStringUTFChars(env, value, NULL); | ||
| 1524 | |||
| 1525 | // This is only called at startup, to initialize the environment | ||
| 1526 | // Note that we call setenv() directly to avoid affecting SDL environments | ||
| 1527 | setenv(utfname, utfvalue, 1); // This should NOT be SDL_setenv() | ||
| 1528 | |||
| 1529 | (*env)->ReleaseStringUTFChars(env, name, utfname); | ||
| 1530 | (*env)->ReleaseStringUTFChars(env, value, utfvalue); | ||
| 1531 | } | ||
| 1532 | |||
| 1533 | /******************************************************************************* | ||
| 1534 | Functions called by SDL into Java | ||
| 1535 | *******************************************************************************/ | ||
| 1536 | |||
| 1537 | static SDL_AtomicInt s_active; | ||
| 1538 | struct LocalReferenceHolder | ||
| 1539 | { | ||
| 1540 | JNIEnv *m_env; | ||
| 1541 | const char *m_func; | ||
| 1542 | }; | ||
| 1543 | |||
| 1544 | static struct LocalReferenceHolder LocalReferenceHolder_Setup(const char *func) | ||
| 1545 | { | ||
| 1546 | struct LocalReferenceHolder refholder; | ||
| 1547 | refholder.m_env = NULL; | ||
| 1548 | refholder.m_func = func; | ||
| 1549 | #ifdef DEBUG_JNI | ||
| 1550 | SDL_Log("Entering function %s", func); | ||
| 1551 | #endif | ||
| 1552 | return refholder; | ||
| 1553 | } | ||
| 1554 | |||
| 1555 | static bool LocalReferenceHolder_Init(struct LocalReferenceHolder *refholder, JNIEnv *env) | ||
| 1556 | { | ||
| 1557 | const int capacity = 16; | ||
| 1558 | if ((*env)->PushLocalFrame(env, capacity) < 0) { | ||
| 1559 | SDL_SetError("Failed to allocate enough JVM local references"); | ||
| 1560 | return false; | ||
| 1561 | } | ||
| 1562 | SDL_AtomicIncRef(&s_active); | ||
| 1563 | refholder->m_env = env; | ||
| 1564 | return true; | ||
| 1565 | } | ||
| 1566 | |||
| 1567 | static void LocalReferenceHolder_Cleanup(struct LocalReferenceHolder *refholder) | ||
| 1568 | { | ||
| 1569 | #ifdef DEBUG_JNI | ||
| 1570 | SDL_Log("Leaving function %s", refholder->m_func); | ||
| 1571 | #endif | ||
| 1572 | if (refholder->m_env) { | ||
| 1573 | JNIEnv *env = refholder->m_env; | ||
| 1574 | (*env)->PopLocalFrame(env, NULL); | ||
| 1575 | SDL_AtomicDecRef(&s_active); | ||
| 1576 | } | ||
| 1577 | } | ||
| 1578 | |||
| 1579 | ANativeWindow *Android_JNI_GetNativeWindow(void) | ||
| 1580 | { | ||
| 1581 | ANativeWindow *anw = NULL; | ||
| 1582 | jobject s; | ||
| 1583 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 1584 | |||
| 1585 | s = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetNativeSurface); | ||
| 1586 | if (s) { | ||
| 1587 | anw = ANativeWindow_fromSurface(env, s); | ||
| 1588 | (*env)->DeleteLocalRef(env, s); | ||
| 1589 | } | ||
| 1590 | |||
| 1591 | return anw; | ||
| 1592 | } | ||
| 1593 | |||
| 1594 | void Android_JNI_SetActivityTitle(const char *title) | ||
| 1595 | { | ||
| 1596 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 1597 | |||
| 1598 | jstring jtitle = (*env)->NewStringUTF(env, title); | ||
| 1599 | (*env)->CallStaticBooleanMethod(env, mActivityClass, midSetActivityTitle, jtitle); | ||
| 1600 | (*env)->DeleteLocalRef(env, jtitle); | ||
| 1601 | } | ||
| 1602 | |||
| 1603 | void Android_JNI_SetWindowStyle(bool fullscreen) | ||
| 1604 | { | ||
| 1605 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 1606 | (*env)->CallStaticVoidMethod(env, mActivityClass, midSetWindowStyle, fullscreen ? 1 : 0); | ||
| 1607 | } | ||
| 1608 | |||
| 1609 | void Android_JNI_SetOrientation(int w, int h, int resizable, const char *hint) | ||
| 1610 | { | ||
| 1611 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 1612 | |||
| 1613 | jstring jhint = (*env)->NewStringUTF(env, (hint ? hint : "")); | ||
| 1614 | (*env)->CallStaticVoidMethod(env, mActivityClass, midSetOrientation, w, h, (resizable ? 1 : 0), jhint); | ||
| 1615 | (*env)->DeleteLocalRef(env, jhint); | ||
| 1616 | } | ||
| 1617 | |||
| 1618 | SDL_DisplayOrientation Android_JNI_GetDisplayNaturalOrientation(void) | ||
| 1619 | { | ||
| 1620 | return displayNaturalOrientation; | ||
| 1621 | } | ||
| 1622 | |||
| 1623 | SDL_DisplayOrientation Android_JNI_GetDisplayCurrentOrientation(void) | ||
| 1624 | { | ||
| 1625 | return displayCurrentOrientation; | ||
| 1626 | } | ||
| 1627 | |||
| 1628 | void Android_JNI_MinizeWindow(void) | ||
| 1629 | { | ||
| 1630 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 1631 | (*env)->CallStaticVoidMethod(env, mActivityClass, midMinimizeWindow); | ||
| 1632 | } | ||
| 1633 | |||
| 1634 | bool Android_JNI_ShouldMinimizeOnFocusLoss(void) | ||
| 1635 | { | ||
| 1636 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 1637 | return (*env)->CallStaticBooleanMethod(env, mActivityClass, midShouldMinimizeOnFocusLoss); | ||
| 1638 | } | ||
| 1639 | |||
| 1640 | bool Android_JNI_GetAccelerometerValues(float values[3]) | ||
| 1641 | { | ||
| 1642 | bool result = false; | ||
| 1643 | |||
| 1644 | if (bHasNewData) { | ||
| 1645 | int i; | ||
| 1646 | for (i = 0; i < 3; ++i) { | ||
| 1647 | values[i] = fLastAccelerometer[i]; | ||
| 1648 | } | ||
| 1649 | bHasNewData = false; | ||
| 1650 | result = true; | ||
| 1651 | } | ||
| 1652 | |||
| 1653 | return result; | ||
| 1654 | } | ||
| 1655 | |||
| 1656 | /* | ||
| 1657 | * Audio support | ||
| 1658 | */ | ||
| 1659 | void Android_StartAudioHotplug(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording) | ||
| 1660 | { | ||
| 1661 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 1662 | // this will fire the callback for each existing device right away (which will eventually SDL_AddAudioDevice), and again later when things change. | ||
| 1663 | (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midRegisterAudioDeviceCallback); | ||
| 1664 | *default_playback = *default_recording = NULL; // !!! FIXME: how do you decide the default device id? | ||
| 1665 | } | ||
| 1666 | |||
| 1667 | void Android_StopAudioHotplug(void) | ||
| 1668 | { | ||
| 1669 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 1670 | (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midUnregisterAudioDeviceCallback); | ||
| 1671 | } | ||
| 1672 | |||
| 1673 | static void Android_JNI_AudioSetThreadPriority(int recording, int device_id) | ||
| 1674 | { | ||
| 1675 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 1676 | (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midAudioSetThreadPriority, recording, device_id); | ||
| 1677 | } | ||
| 1678 | |||
| 1679 | void Android_AudioThreadInit(SDL_AudioDevice *device) | ||
| 1680 | { | ||
| 1681 | Android_JNI_AudioSetThreadPriority((int) device->recording, (int)device->instance_id); | ||
| 1682 | } | ||
| 1683 | |||
| 1684 | // Test for an exception and call SDL_SetError with its detail if one occurs | ||
| 1685 | // If the parameter silent is truthy then SDL_SetError() will not be called. | ||
| 1686 | static bool Android_JNI_ExceptionOccurred(bool silent) | ||
| 1687 | { | ||
| 1688 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 1689 | jthrowable exception; | ||
| 1690 | |||
| 1691 | // Detect mismatch LocalReferenceHolder_Init/Cleanup | ||
| 1692 | SDL_assert(SDL_GetAtomicInt(&s_active) > 0); | ||
| 1693 | |||
| 1694 | exception = (*env)->ExceptionOccurred(env); | ||
| 1695 | if (exception != NULL) { | ||
| 1696 | jmethodID mid; | ||
| 1697 | |||
| 1698 | // Until this happens most JNI operations have undefined behaviour | ||
| 1699 | (*env)->ExceptionClear(env); | ||
| 1700 | |||
| 1701 | if (!silent) { | ||
| 1702 | jclass exceptionClass = (*env)->GetObjectClass(env, exception); | ||
| 1703 | jclass classClass = (*env)->FindClass(env, "java/lang/Class"); | ||
| 1704 | jstring exceptionName; | ||
| 1705 | const char *exceptionNameUTF8; | ||
| 1706 | jstring exceptionMessage; | ||
| 1707 | |||
| 1708 | mid = (*env)->GetMethodID(env, classClass, "getName", "()Ljava/lang/String;"); | ||
| 1709 | exceptionName = (jstring)(*env)->CallObjectMethod(env, exceptionClass, mid); | ||
| 1710 | exceptionNameUTF8 = (*env)->GetStringUTFChars(env, exceptionName, 0); | ||
| 1711 | |||
| 1712 | mid = (*env)->GetMethodID(env, exceptionClass, "getMessage", "()Ljava/lang/String;"); | ||
| 1713 | exceptionMessage = (jstring)(*env)->CallObjectMethod(env, exception, mid); | ||
| 1714 | |||
| 1715 | if (exceptionMessage != NULL) { | ||
| 1716 | const char *exceptionMessageUTF8 = (*env)->GetStringUTFChars(env, exceptionMessage, 0); | ||
| 1717 | SDL_SetError("%s: %s", exceptionNameUTF8, exceptionMessageUTF8); | ||
| 1718 | (*env)->ReleaseStringUTFChars(env, exceptionMessage, exceptionMessageUTF8); | ||
| 1719 | } else { | ||
| 1720 | SDL_SetError("%s", exceptionNameUTF8); | ||
| 1721 | } | ||
| 1722 | |||
| 1723 | (*env)->ReleaseStringUTFChars(env, exceptionName, exceptionNameUTF8); | ||
| 1724 | } | ||
| 1725 | |||
| 1726 | return true; | ||
| 1727 | } | ||
| 1728 | |||
| 1729 | return false; | ||
| 1730 | } | ||
| 1731 | |||
| 1732 | static void Internal_Android_Create_AssetManager(void) | ||
| 1733 | { | ||
| 1734 | |||
| 1735 | struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); | ||
| 1736 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 1737 | jmethodID mid; | ||
| 1738 | jobject context; | ||
| 1739 | jobject javaAssetManager; | ||
| 1740 | |||
| 1741 | if (!LocalReferenceHolder_Init(&refs, env)) { | ||
| 1742 | LocalReferenceHolder_Cleanup(&refs); | ||
| 1743 | return; | ||
| 1744 | } | ||
| 1745 | |||
| 1746 | // context = SDLActivity.getContext(); | ||
| 1747 | context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext); | ||
| 1748 | |||
| 1749 | // javaAssetManager = context.getAssets(); | ||
| 1750 | mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context), | ||
| 1751 | "getAssets", "()Landroid/content/res/AssetManager;"); | ||
| 1752 | javaAssetManager = (*env)->CallObjectMethod(env, context, mid); | ||
| 1753 | |||
| 1754 | /** | ||
| 1755 | * Given a Dalvik AssetManager object, obtain the corresponding native AAssetManager | ||
| 1756 | * object. Note that the caller is responsible for obtaining and holding a VM reference | ||
| 1757 | * to the jobject to prevent its being garbage collected while the native object is | ||
| 1758 | * in use. | ||
| 1759 | */ | ||
| 1760 | javaAssetManagerRef = (*env)->NewGlobalRef(env, javaAssetManager); | ||
| 1761 | asset_manager = AAssetManager_fromJava(env, javaAssetManagerRef); | ||
| 1762 | |||
| 1763 | if (!asset_manager) { | ||
| 1764 | (*env)->DeleteGlobalRef(env, javaAssetManagerRef); | ||
| 1765 | Android_JNI_ExceptionOccurred(true); | ||
| 1766 | } | ||
| 1767 | |||
| 1768 | LocalReferenceHolder_Cleanup(&refs); | ||
| 1769 | } | ||
| 1770 | |||
| 1771 | static void Internal_Android_Destroy_AssetManager(void) | ||
| 1772 | { | ||
| 1773 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 1774 | |||
| 1775 | if (asset_manager) { | ||
| 1776 | (*env)->DeleteGlobalRef(env, javaAssetManagerRef); | ||
| 1777 | asset_manager = NULL; | ||
| 1778 | } | ||
| 1779 | } | ||
| 1780 | |||
| 1781 | bool Android_JNI_FileOpen(void **puserdata, const char *fileName, const char *mode) | ||
| 1782 | { | ||
| 1783 | SDL_assert(puserdata != NULL); | ||
| 1784 | |||
| 1785 | AAsset *asset = NULL; | ||
| 1786 | *puserdata = NULL; | ||
| 1787 | |||
| 1788 | if (!asset_manager) { | ||
| 1789 | Internal_Android_Create_AssetManager(); | ||
| 1790 | } | ||
| 1791 | |||
| 1792 | if (!asset_manager) { | ||
| 1793 | return SDL_SetError("Couldn't create asset manager"); | ||
| 1794 | } | ||
| 1795 | |||
| 1796 | asset = AAssetManager_open(asset_manager, fileName, AASSET_MODE_UNKNOWN); | ||
| 1797 | if (!asset) { | ||
| 1798 | return SDL_SetError("Couldn't open asset '%s'", fileName); | ||
| 1799 | } | ||
| 1800 | |||
| 1801 | *puserdata = (void *)asset; | ||
| 1802 | return true; | ||
| 1803 | } | ||
| 1804 | |||
| 1805 | size_t Android_JNI_FileRead(void *userdata, void *buffer, size_t size, SDL_IOStatus *status) | ||
| 1806 | { | ||
| 1807 | const int bytes = AAsset_read((AAsset *)userdata, buffer, size); | ||
| 1808 | if (bytes < 0) { | ||
| 1809 | SDL_SetError("AAsset_read() failed"); | ||
| 1810 | return 0; | ||
| 1811 | } | ||
| 1812 | return (size_t)bytes; | ||
| 1813 | } | ||
| 1814 | |||
| 1815 | size_t Android_JNI_FileWrite(void *userdata, const void *buffer, size_t size, SDL_IOStatus *status) | ||
| 1816 | { | ||
| 1817 | SDL_SetError("Cannot write to Android package filesystem"); | ||
| 1818 | return 0; | ||
| 1819 | } | ||
| 1820 | |||
| 1821 | Sint64 Android_JNI_FileSize(void *userdata) | ||
| 1822 | { | ||
| 1823 | return (Sint64) AAsset_getLength64((AAsset *)userdata); | ||
| 1824 | } | ||
| 1825 | |||
| 1826 | Sint64 Android_JNI_FileSeek(void *userdata, Sint64 offset, SDL_IOWhence whence) | ||
| 1827 | { | ||
| 1828 | return (Sint64) AAsset_seek64((AAsset *)userdata, offset, (int)whence); | ||
| 1829 | } | ||
| 1830 | |||
| 1831 | bool Android_JNI_FileClose(void *userdata) | ||
| 1832 | { | ||
| 1833 | AAsset_close((AAsset *)userdata); | ||
| 1834 | return true; | ||
| 1835 | } | ||
| 1836 | |||
| 1837 | bool Android_JNI_SetClipboardText(const char *text) | ||
| 1838 | { | ||
| 1839 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 1840 | jstring string = (*env)->NewStringUTF(env, text); | ||
| 1841 | (*env)->CallStaticVoidMethod(env, mActivityClass, midClipboardSetText, string); | ||
| 1842 | (*env)->DeleteLocalRef(env, string); | ||
| 1843 | return true; | ||
| 1844 | } | ||
| 1845 | |||
| 1846 | char *Android_JNI_GetClipboardText(void) | ||
| 1847 | { | ||
| 1848 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 1849 | char *text = NULL; | ||
| 1850 | jstring string; | ||
| 1851 | |||
| 1852 | string = (*env)->CallStaticObjectMethod(env, mActivityClass, midClipboardGetText); | ||
| 1853 | if (string) { | ||
| 1854 | const char *utf = (*env)->GetStringUTFChars(env, string, 0); | ||
| 1855 | if (utf) { | ||
| 1856 | text = SDL_strdup(utf); | ||
| 1857 | (*env)->ReleaseStringUTFChars(env, string, utf); | ||
| 1858 | } | ||
| 1859 | (*env)->DeleteLocalRef(env, string); | ||
| 1860 | } | ||
| 1861 | |||
| 1862 | return (!text) ? SDL_strdup("") : text; | ||
| 1863 | } | ||
| 1864 | |||
| 1865 | bool Android_JNI_HasClipboardText(void) | ||
| 1866 | { | ||
| 1867 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 1868 | return (*env)->CallStaticBooleanMethod(env, mActivityClass, midClipboardHasText); | ||
| 1869 | } | ||
| 1870 | |||
| 1871 | /* returns 0 on success or -1 on error (others undefined then) | ||
| 1872 | * returns truthy or falsy value in plugged, charged and battery | ||
| 1873 | * returns the value in seconds and percent or -1 if not available | ||
| 1874 | */ | ||
| 1875 | int Android_JNI_GetPowerInfo(int *plugged, int *charged, int *battery, int *seconds, int *percent) | ||
| 1876 | { | ||
| 1877 | struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); | ||
| 1878 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 1879 | jmethodID mid; | ||
| 1880 | jobject context; | ||
| 1881 | jstring action; | ||
| 1882 | jclass cls; | ||
| 1883 | jobject filter; | ||
| 1884 | jobject intent; | ||
| 1885 | jstring iname; | ||
| 1886 | jmethodID imid; | ||
| 1887 | jstring bname; | ||
| 1888 | jmethodID bmid; | ||
| 1889 | if (!LocalReferenceHolder_Init(&refs, env)) { | ||
| 1890 | LocalReferenceHolder_Cleanup(&refs); | ||
| 1891 | return -1; | ||
| 1892 | } | ||
| 1893 | |||
| 1894 | // context = SDLActivity.getContext(); | ||
| 1895 | context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext); | ||
| 1896 | |||
| 1897 | action = (*env)->NewStringUTF(env, "android.intent.action.BATTERY_CHANGED"); | ||
| 1898 | |||
| 1899 | cls = (*env)->FindClass(env, "android/content/IntentFilter"); | ||
| 1900 | |||
| 1901 | mid = (*env)->GetMethodID(env, cls, "<init>", "(Ljava/lang/String;)V"); | ||
| 1902 | filter = (*env)->NewObject(env, cls, mid, action); | ||
| 1903 | |||
| 1904 | (*env)->DeleteLocalRef(env, action); | ||
| 1905 | |||
| 1906 | mid = (*env)->GetMethodID(env, mActivityClass, "registerReceiver", "(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;"); | ||
| 1907 | intent = (*env)->CallObjectMethod(env, context, mid, NULL, filter); | ||
| 1908 | |||
| 1909 | (*env)->DeleteLocalRef(env, filter); | ||
| 1910 | |||
| 1911 | cls = (*env)->GetObjectClass(env, intent); | ||
| 1912 | |||
| 1913 | imid = (*env)->GetMethodID(env, cls, "getIntExtra", "(Ljava/lang/String;I)I"); | ||
| 1914 | |||
| 1915 | // Watch out for C89 scoping rules because of the macro | ||
| 1916 | #define GET_INT_EXTRA(var, key) \ | ||
| 1917 | int var; \ | ||
| 1918 | iname = (*env)->NewStringUTF(env, key); \ | ||
| 1919 | (var) = (*env)->CallIntMethod(env, intent, imid, iname, -1); \ | ||
| 1920 | (*env)->DeleteLocalRef(env, iname); | ||
| 1921 | |||
| 1922 | bmid = (*env)->GetMethodID(env, cls, "getBooleanExtra", "(Ljava/lang/String;Z)Z"); | ||
| 1923 | |||
| 1924 | // Watch out for C89 scoping rules because of the macro | ||
| 1925 | #define GET_BOOL_EXTRA(var, key) \ | ||
| 1926 | int var; \ | ||
| 1927 | bname = (*env)->NewStringUTF(env, key); \ | ||
| 1928 | (var) = (*env)->CallBooleanMethod(env, intent, bmid, bname, JNI_FALSE); \ | ||
| 1929 | (*env)->DeleteLocalRef(env, bname); | ||
| 1930 | |||
| 1931 | if (plugged) { | ||
| 1932 | // Watch out for C89 scoping rules because of the macro | ||
| 1933 | GET_INT_EXTRA(plug, "plugged") // == BatteryManager.EXTRA_PLUGGED (API 5) | ||
| 1934 | if (plug == -1) { | ||
| 1935 | LocalReferenceHolder_Cleanup(&refs); | ||
| 1936 | return -1; | ||
| 1937 | } | ||
| 1938 | // 1 == BatteryManager.BATTERY_PLUGGED_AC | ||
| 1939 | // 2 == BatteryManager.BATTERY_PLUGGED_USB | ||
| 1940 | *plugged = (0 < plug) ? 1 : 0; | ||
| 1941 | } | ||
| 1942 | |||
| 1943 | if (charged) { | ||
| 1944 | // Watch out for C89 scoping rules because of the macro | ||
| 1945 | GET_INT_EXTRA(status, "status") // == BatteryManager.EXTRA_STATUS (API 5) | ||
| 1946 | if (status == -1) { | ||
| 1947 | LocalReferenceHolder_Cleanup(&refs); | ||
| 1948 | return -1; | ||
| 1949 | } | ||
| 1950 | // 5 == BatteryManager.BATTERY_STATUS_FULL | ||
| 1951 | *charged = (status == 5) ? 1 : 0; | ||
| 1952 | } | ||
| 1953 | |||
| 1954 | if (battery) { | ||
| 1955 | GET_BOOL_EXTRA(present, "present") // == BatteryManager.EXTRA_PRESENT (API 5) | ||
| 1956 | *battery = present ? 1 : 0; | ||
| 1957 | } | ||
| 1958 | |||
| 1959 | if (seconds) { | ||
| 1960 | *seconds = -1; // not possible | ||
| 1961 | } | ||
| 1962 | |||
| 1963 | if (percent) { | ||
| 1964 | int level; | ||
| 1965 | int scale; | ||
| 1966 | |||
| 1967 | // Watch out for C89 scoping rules because of the macro | ||
| 1968 | { | ||
| 1969 | GET_INT_EXTRA(level_temp, "level") // == BatteryManager.EXTRA_LEVEL (API 5) | ||
| 1970 | level = level_temp; | ||
| 1971 | } | ||
| 1972 | // Watch out for C89 scoping rules because of the macro | ||
| 1973 | { | ||
| 1974 | GET_INT_EXTRA(scale_temp, "scale") // == BatteryManager.EXTRA_SCALE (API 5) | ||
| 1975 | scale = scale_temp; | ||
| 1976 | } | ||
| 1977 | |||
| 1978 | if ((level == -1) || (scale == -1)) { | ||
| 1979 | LocalReferenceHolder_Cleanup(&refs); | ||
| 1980 | return -1; | ||
| 1981 | } | ||
| 1982 | *percent = level * 100 / scale; | ||
| 1983 | } | ||
| 1984 | |||
| 1985 | (*env)->DeleteLocalRef(env, intent); | ||
| 1986 | |||
| 1987 | LocalReferenceHolder_Cleanup(&refs); | ||
| 1988 | return 0; | ||
| 1989 | } | ||
| 1990 | |||
| 1991 | // Add all touch devices | ||
| 1992 | void Android_JNI_InitTouch(void) | ||
| 1993 | { | ||
| 1994 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 1995 | (*env)->CallStaticVoidMethod(env, mActivityClass, midInitTouch); | ||
| 1996 | } | ||
| 1997 | |||
| 1998 | void Android_JNI_PollInputDevices(void) | ||
| 1999 | { | ||
| 2000 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2001 | (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midPollInputDevices); | ||
| 2002 | } | ||
| 2003 | |||
| 2004 | void Android_JNI_PollHapticDevices(void) | ||
| 2005 | { | ||
| 2006 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2007 | (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midPollHapticDevices); | ||
| 2008 | } | ||
| 2009 | |||
| 2010 | void Android_JNI_HapticRun(int device_id, float intensity, int length) | ||
| 2011 | { | ||
| 2012 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2013 | (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midHapticRun, device_id, intensity, length); | ||
| 2014 | } | ||
| 2015 | |||
| 2016 | void Android_JNI_HapticRumble(int device_id, float low_frequency_intensity, float high_frequency_intensity, int length) | ||
| 2017 | { | ||
| 2018 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2019 | (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midHapticRumble, device_id, low_frequency_intensity, high_frequency_intensity, length); | ||
| 2020 | } | ||
| 2021 | |||
| 2022 | void Android_JNI_HapticStop(int device_id) | ||
| 2023 | { | ||
| 2024 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2025 | (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midHapticStop, device_id); | ||
| 2026 | } | ||
| 2027 | |||
| 2028 | // See SDLActivity.java for constants. | ||
| 2029 | #define COMMAND_SET_KEEP_SCREEN_ON 5 | ||
| 2030 | |||
| 2031 | bool SDL_SendAndroidMessage(Uint32 command, int param) | ||
| 2032 | { | ||
| 2033 | if (command < 0x8000) { | ||
| 2034 | return SDL_InvalidParamError("command"); | ||
| 2035 | } | ||
| 2036 | return Android_JNI_SendMessage(command, param); | ||
| 2037 | } | ||
| 2038 | |||
| 2039 | // sends message to be handled on the UI event dispatch thread | ||
| 2040 | bool Android_JNI_SendMessage(int command, int param) | ||
| 2041 | { | ||
| 2042 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2043 | return (*env)->CallStaticBooleanMethod(env, mActivityClass, midSendMessage, command, param); | ||
| 2044 | } | ||
| 2045 | |||
| 2046 | bool Android_JNI_SuspendScreenSaver(bool suspend) | ||
| 2047 | { | ||
| 2048 | return Android_JNI_SendMessage(COMMAND_SET_KEEP_SCREEN_ON, (suspend == false) ? 0 : 1); | ||
| 2049 | } | ||
| 2050 | |||
| 2051 | void Android_JNI_ShowScreenKeyboard(int input_type, SDL_Rect *inputRect) | ||
| 2052 | { | ||
| 2053 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2054 | (*env)->CallStaticBooleanMethod(env, mActivityClass, midShowTextInput, | ||
| 2055 | input_type, | ||
| 2056 | inputRect->x, | ||
| 2057 | inputRect->y, | ||
| 2058 | inputRect->w, | ||
| 2059 | inputRect->h); | ||
| 2060 | } | ||
| 2061 | |||
| 2062 | void Android_JNI_HideScreenKeyboard(void) | ||
| 2063 | { | ||
| 2064 | // has to match Activity constant | ||
| 2065 | const int COMMAND_TEXTEDIT_HIDE = 3; | ||
| 2066 | Android_JNI_SendMessage(COMMAND_TEXTEDIT_HIDE, 0); | ||
| 2067 | } | ||
| 2068 | |||
| 2069 | bool Android_JNI_IsScreenKeyboardShown(void) | ||
| 2070 | { | ||
| 2071 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2072 | jboolean is_shown = 0; | ||
| 2073 | is_shown = (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsScreenKeyboardShown); | ||
| 2074 | return is_shown; | ||
| 2075 | } | ||
| 2076 | |||
| 2077 | bool Android_JNI_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID) | ||
| 2078 | { | ||
| 2079 | JNIEnv *env; | ||
| 2080 | jclass clazz; | ||
| 2081 | jmethodID mid; | ||
| 2082 | jobject context; | ||
| 2083 | jstring title; | ||
| 2084 | jstring message; | ||
| 2085 | jintArray button_flags; | ||
| 2086 | jintArray button_ids; | ||
| 2087 | jobjectArray button_texts; | ||
| 2088 | jintArray colors; | ||
| 2089 | jobject text; | ||
| 2090 | jint temp; | ||
| 2091 | int i; | ||
| 2092 | |||
| 2093 | env = Android_JNI_GetEnv(); | ||
| 2094 | |||
| 2095 | // convert parameters | ||
| 2096 | |||
| 2097 | clazz = (*env)->FindClass(env, "java/lang/String"); | ||
| 2098 | |||
| 2099 | title = (*env)->NewStringUTF(env, messageboxdata->title); | ||
| 2100 | message = (*env)->NewStringUTF(env, messageboxdata->message); | ||
| 2101 | |||
| 2102 | button_flags = (*env)->NewIntArray(env, messageboxdata->numbuttons); | ||
| 2103 | button_ids = (*env)->NewIntArray(env, messageboxdata->numbuttons); | ||
| 2104 | button_texts = (*env)->NewObjectArray(env, messageboxdata->numbuttons, | ||
| 2105 | clazz, NULL); | ||
| 2106 | for (i = 0; i < messageboxdata->numbuttons; ++i) { | ||
| 2107 | const SDL_MessageBoxButtonData *sdlButton; | ||
| 2108 | |||
| 2109 | if (messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT) { | ||
| 2110 | sdlButton = &messageboxdata->buttons[messageboxdata->numbuttons - 1 - i]; | ||
| 2111 | } else { | ||
| 2112 | sdlButton = &messageboxdata->buttons[i]; | ||
| 2113 | } | ||
| 2114 | |||
| 2115 | temp = sdlButton->flags; | ||
| 2116 | (*env)->SetIntArrayRegion(env, button_flags, i, 1, &temp); | ||
| 2117 | temp = sdlButton->buttonID; | ||
| 2118 | (*env)->SetIntArrayRegion(env, button_ids, i, 1, &temp); | ||
| 2119 | text = (*env)->NewStringUTF(env, sdlButton->text); | ||
| 2120 | (*env)->SetObjectArrayElement(env, button_texts, i, text); | ||
| 2121 | (*env)->DeleteLocalRef(env, text); | ||
| 2122 | } | ||
| 2123 | |||
| 2124 | if (messageboxdata->colorScheme) { | ||
| 2125 | colors = (*env)->NewIntArray(env, SDL_MESSAGEBOX_COLOR_COUNT); | ||
| 2126 | for (i = 0; i < SDL_MESSAGEBOX_COLOR_COUNT; ++i) { | ||
| 2127 | temp = (0xFFU << 24) | | ||
| 2128 | (messageboxdata->colorScheme->colors[i].r << 16) | | ||
| 2129 | (messageboxdata->colorScheme->colors[i].g << 8) | | ||
| 2130 | (messageboxdata->colorScheme->colors[i].b << 0); | ||
| 2131 | (*env)->SetIntArrayRegion(env, colors, i, 1, &temp); | ||
| 2132 | } | ||
| 2133 | } else { | ||
| 2134 | colors = NULL; | ||
| 2135 | } | ||
| 2136 | |||
| 2137 | (*env)->DeleteLocalRef(env, clazz); | ||
| 2138 | |||
| 2139 | // context = SDLActivity.getContext(); | ||
| 2140 | context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext); | ||
| 2141 | |||
| 2142 | clazz = (*env)->GetObjectClass(env, context); | ||
| 2143 | |||
| 2144 | mid = (*env)->GetMethodID(env, clazz, | ||
| 2145 | "messageboxShowMessageBox", "(ILjava/lang/String;Ljava/lang/String;[I[I[Ljava/lang/String;[I)I"); | ||
| 2146 | *buttonID = (*env)->CallIntMethod(env, context, mid, | ||
| 2147 | messageboxdata->flags, | ||
| 2148 | title, | ||
| 2149 | message, | ||
| 2150 | button_flags, | ||
| 2151 | button_ids, | ||
| 2152 | button_texts, | ||
| 2153 | colors); | ||
| 2154 | |||
| 2155 | (*env)->DeleteLocalRef(env, context); | ||
| 2156 | (*env)->DeleteLocalRef(env, clazz); | ||
| 2157 | |||
| 2158 | // delete parameters | ||
| 2159 | |||
| 2160 | (*env)->DeleteLocalRef(env, title); | ||
| 2161 | (*env)->DeleteLocalRef(env, message); | ||
| 2162 | (*env)->DeleteLocalRef(env, button_flags); | ||
| 2163 | (*env)->DeleteLocalRef(env, button_ids); | ||
| 2164 | (*env)->DeleteLocalRef(env, button_texts); | ||
| 2165 | (*env)->DeleteLocalRef(env, colors); | ||
| 2166 | |||
| 2167 | return true; | ||
| 2168 | } | ||
| 2169 | |||
| 2170 | /* | ||
| 2171 | ////////////////////////////////////////////////////////////////////////////// | ||
| 2172 | // | ||
| 2173 | // Functions exposed to SDL applications in SDL_system.h | ||
| 2174 | ////////////////////////////////////////////////////////////////////////////// | ||
| 2175 | */ | ||
| 2176 | |||
| 2177 | void *SDL_GetAndroidJNIEnv(void) | ||
| 2178 | { | ||
| 2179 | return Android_JNI_GetEnv(); | ||
| 2180 | } | ||
| 2181 | |||
| 2182 | void *SDL_GetAndroidActivity(void) | ||
| 2183 | { | ||
| 2184 | // See SDL_system.h for caveats on using this function. | ||
| 2185 | |||
| 2186 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2187 | if (!env) { | ||
| 2188 | return NULL; | ||
| 2189 | } | ||
| 2190 | |||
| 2191 | // return SDLActivity.getContext(); | ||
| 2192 | return (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext); | ||
| 2193 | } | ||
| 2194 | |||
| 2195 | int SDL_GetAndroidSDKVersion(void) | ||
| 2196 | { | ||
| 2197 | static int sdk_version; | ||
| 2198 | if (!sdk_version) { | ||
| 2199 | char sdk[PROP_VALUE_MAX] = { 0 }; | ||
| 2200 | if (__system_property_get("ro.build.version.sdk", sdk) != 0) { | ||
| 2201 | sdk_version = SDL_atoi(sdk); | ||
| 2202 | } | ||
| 2203 | } | ||
| 2204 | return sdk_version; | ||
| 2205 | } | ||
| 2206 | |||
| 2207 | bool SDL_IsAndroidTablet(void) | ||
| 2208 | { | ||
| 2209 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2210 | return (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsTablet); | ||
| 2211 | } | ||
| 2212 | |||
| 2213 | bool SDL_IsAndroidTV(void) | ||
| 2214 | { | ||
| 2215 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2216 | return (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsAndroidTV); | ||
| 2217 | } | ||
| 2218 | |||
| 2219 | bool SDL_IsChromebook(void) | ||
| 2220 | { | ||
| 2221 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2222 | return (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsChromebook); | ||
| 2223 | } | ||
| 2224 | |||
| 2225 | bool SDL_IsDeXMode(void) | ||
| 2226 | { | ||
| 2227 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2228 | return (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsDeXMode); | ||
| 2229 | } | ||
| 2230 | |||
| 2231 | void SDL_SendAndroidBackButton(void) | ||
| 2232 | { | ||
| 2233 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2234 | (*env)->CallStaticVoidMethod(env, mActivityClass, midManualBackButton); | ||
| 2235 | } | ||
| 2236 | |||
| 2237 | const char *SDL_GetAndroidInternalStoragePath(void) | ||
| 2238 | { | ||
| 2239 | static char *s_AndroidInternalFilesPath = NULL; | ||
| 2240 | |||
| 2241 | if (!s_AndroidInternalFilesPath) { | ||
| 2242 | struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); | ||
| 2243 | jmethodID mid; | ||
| 2244 | jobject context; | ||
| 2245 | jobject fileObject; | ||
| 2246 | jstring pathString; | ||
| 2247 | const char *path; | ||
| 2248 | |||
| 2249 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2250 | if (!LocalReferenceHolder_Init(&refs, env)) { | ||
| 2251 | LocalReferenceHolder_Cleanup(&refs); | ||
| 2252 | return NULL; | ||
| 2253 | } | ||
| 2254 | |||
| 2255 | // context = SDLActivity.getContext(); | ||
| 2256 | context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext); | ||
| 2257 | if (!context) { | ||
| 2258 | SDL_SetError("Couldn't get Android context!"); | ||
| 2259 | LocalReferenceHolder_Cleanup(&refs); | ||
| 2260 | return NULL; | ||
| 2261 | } | ||
| 2262 | |||
| 2263 | // fileObj = context.getFilesDir(); | ||
| 2264 | mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context), | ||
| 2265 | "getFilesDir", "()Ljava/io/File;"); | ||
| 2266 | fileObject = (*env)->CallObjectMethod(env, context, mid); | ||
| 2267 | if (!fileObject) { | ||
| 2268 | SDL_SetError("Couldn't get internal directory"); | ||
| 2269 | LocalReferenceHolder_Cleanup(&refs); | ||
| 2270 | return NULL; | ||
| 2271 | } | ||
| 2272 | |||
| 2273 | // path = fileObject.getCanonicalPath(); | ||
| 2274 | mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, fileObject), | ||
| 2275 | "getCanonicalPath", "()Ljava/lang/String;"); | ||
| 2276 | pathString = (jstring)(*env)->CallObjectMethod(env, fileObject, mid); | ||
| 2277 | if (Android_JNI_ExceptionOccurred(false)) { | ||
| 2278 | LocalReferenceHolder_Cleanup(&refs); | ||
| 2279 | return NULL; | ||
| 2280 | } | ||
| 2281 | |||
| 2282 | path = (*env)->GetStringUTFChars(env, pathString, NULL); | ||
| 2283 | s_AndroidInternalFilesPath = SDL_strdup(path); | ||
| 2284 | (*env)->ReleaseStringUTFChars(env, pathString, path); | ||
| 2285 | |||
| 2286 | LocalReferenceHolder_Cleanup(&refs); | ||
| 2287 | } | ||
| 2288 | return s_AndroidInternalFilesPath; | ||
| 2289 | } | ||
| 2290 | |||
| 2291 | Uint32 SDL_GetAndroidExternalStorageState(void) | ||
| 2292 | { | ||
| 2293 | struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); | ||
| 2294 | jmethodID mid; | ||
| 2295 | jclass cls; | ||
| 2296 | jstring stateString; | ||
| 2297 | const char *state_string; | ||
| 2298 | Uint32 stateFlags; | ||
| 2299 | |||
| 2300 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2301 | if (!LocalReferenceHolder_Init(&refs, env)) { | ||
| 2302 | LocalReferenceHolder_Cleanup(&refs); | ||
| 2303 | return 0; | ||
| 2304 | } | ||
| 2305 | |||
| 2306 | cls = (*env)->FindClass(env, "android/os/Environment"); | ||
| 2307 | mid = (*env)->GetStaticMethodID(env, cls, | ||
| 2308 | "getExternalStorageState", "()Ljava/lang/String;"); | ||
| 2309 | stateString = (jstring)(*env)->CallStaticObjectMethod(env, cls, mid); | ||
| 2310 | |||
| 2311 | state_string = (*env)->GetStringUTFChars(env, stateString, NULL); | ||
| 2312 | |||
| 2313 | // Print an info message so people debugging know the storage state | ||
| 2314 | __android_log_print(ANDROID_LOG_INFO, "SDL", "external storage state: %s", state_string); | ||
| 2315 | |||
| 2316 | if (SDL_strcmp(state_string, "mounted") == 0) { | ||
| 2317 | stateFlags = SDL_ANDROID_EXTERNAL_STORAGE_READ | | ||
| 2318 | SDL_ANDROID_EXTERNAL_STORAGE_WRITE; | ||
| 2319 | } else if (SDL_strcmp(state_string, "mounted_ro") == 0) { | ||
| 2320 | stateFlags = SDL_ANDROID_EXTERNAL_STORAGE_READ; | ||
| 2321 | } else { | ||
| 2322 | stateFlags = 0; | ||
| 2323 | } | ||
| 2324 | (*env)->ReleaseStringUTFChars(env, stateString, state_string); | ||
| 2325 | |||
| 2326 | LocalReferenceHolder_Cleanup(&refs); | ||
| 2327 | |||
| 2328 | return stateFlags; | ||
| 2329 | } | ||
| 2330 | |||
| 2331 | const char *SDL_GetAndroidExternalStoragePath(void) | ||
| 2332 | { | ||
| 2333 | static char *s_AndroidExternalFilesPath = NULL; | ||
| 2334 | |||
| 2335 | if (!s_AndroidExternalFilesPath) { | ||
| 2336 | struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); | ||
| 2337 | jmethodID mid; | ||
| 2338 | jobject context; | ||
| 2339 | jobject fileObject; | ||
| 2340 | jstring pathString; | ||
| 2341 | const char *path; | ||
| 2342 | |||
| 2343 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2344 | if (!LocalReferenceHolder_Init(&refs, env)) { | ||
| 2345 | LocalReferenceHolder_Cleanup(&refs); | ||
| 2346 | return NULL; | ||
| 2347 | } | ||
| 2348 | |||
| 2349 | // context = SDLActivity.getContext(); | ||
| 2350 | context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext); | ||
| 2351 | |||
| 2352 | // fileObj = context.getExternalFilesDir(); | ||
| 2353 | mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context), | ||
| 2354 | "getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;"); | ||
| 2355 | fileObject = (*env)->CallObjectMethod(env, context, mid, NULL); | ||
| 2356 | if (!fileObject) { | ||
| 2357 | SDL_SetError("Couldn't get external directory"); | ||
| 2358 | LocalReferenceHolder_Cleanup(&refs); | ||
| 2359 | return NULL; | ||
| 2360 | } | ||
| 2361 | |||
| 2362 | // path = fileObject.getAbsolutePath(); | ||
| 2363 | mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, fileObject), | ||
| 2364 | "getAbsolutePath", "()Ljava/lang/String;"); | ||
| 2365 | pathString = (jstring)(*env)->CallObjectMethod(env, fileObject, mid); | ||
| 2366 | |||
| 2367 | path = (*env)->GetStringUTFChars(env, pathString, NULL); | ||
| 2368 | s_AndroidExternalFilesPath = SDL_strdup(path); | ||
| 2369 | (*env)->ReleaseStringUTFChars(env, pathString, path); | ||
| 2370 | |||
| 2371 | LocalReferenceHolder_Cleanup(&refs); | ||
| 2372 | } | ||
| 2373 | return s_AndroidExternalFilesPath; | ||
| 2374 | } | ||
| 2375 | |||
| 2376 | const char *SDL_GetAndroidCachePath(void) | ||
| 2377 | { | ||
| 2378 | // !!! FIXME: lots of duplication with SDL_GetAndroidExternalStoragePath and SDL_GetAndroidInternalStoragePath; consolidate these functions! | ||
| 2379 | static char *s_AndroidCachePath = NULL; | ||
| 2380 | |||
| 2381 | if (!s_AndroidCachePath) { | ||
| 2382 | struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); | ||
| 2383 | jmethodID mid; | ||
| 2384 | jobject context; | ||
| 2385 | jobject fileObject; | ||
| 2386 | jstring pathString; | ||
| 2387 | const char *path; | ||
| 2388 | |||
| 2389 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2390 | if (!LocalReferenceHolder_Init(&refs, env)) { | ||
| 2391 | LocalReferenceHolder_Cleanup(&refs); | ||
| 2392 | return NULL; | ||
| 2393 | } | ||
| 2394 | |||
| 2395 | // context = SDLActivity.getContext(); | ||
| 2396 | context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext); | ||
| 2397 | |||
| 2398 | // fileObj = context.getExternalFilesDir(); | ||
| 2399 | mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context), | ||
| 2400 | "getCacheDir", "()Ljava/io/File;"); | ||
| 2401 | fileObject = (*env)->CallObjectMethod(env, context, mid, NULL); | ||
| 2402 | if (!fileObject) { | ||
| 2403 | SDL_SetError("Couldn't get cache directory"); | ||
| 2404 | LocalReferenceHolder_Cleanup(&refs); | ||
| 2405 | return NULL; | ||
| 2406 | } | ||
| 2407 | |||
| 2408 | // path = fileObject.getAbsolutePath(); | ||
| 2409 | mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, fileObject), | ||
| 2410 | "getAbsolutePath", "()Ljava/lang/String;"); | ||
| 2411 | pathString = (jstring)(*env)->CallObjectMethod(env, fileObject, mid); | ||
| 2412 | |||
| 2413 | path = (*env)->GetStringUTFChars(env, pathString, NULL); | ||
| 2414 | s_AndroidCachePath = SDL_strdup(path); | ||
| 2415 | (*env)->ReleaseStringUTFChars(env, pathString, path); | ||
| 2416 | |||
| 2417 | LocalReferenceHolder_Cleanup(&refs); | ||
| 2418 | } | ||
| 2419 | return s_AndroidCachePath; | ||
| 2420 | } | ||
| 2421 | |||
| 2422 | bool SDL_ShowAndroidToast(const char *message, int duration, int gravity, int xOffset, int yOffset) | ||
| 2423 | { | ||
| 2424 | return Android_JNI_ShowToast(message, duration, gravity, xOffset, yOffset); | ||
| 2425 | } | ||
| 2426 | |||
| 2427 | void Android_JNI_GetManifestEnvironmentVariables(void) | ||
| 2428 | { | ||
| 2429 | if (!mActivityClass || !midGetManifestEnvironmentVariables) { | ||
| 2430 | __android_log_print(ANDROID_LOG_WARN, "SDL", "Request to get environment variables before JNI is ready"); | ||
| 2431 | return; | ||
| 2432 | } | ||
| 2433 | |||
| 2434 | if (!bHasEnvironmentVariables) { | ||
| 2435 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2436 | bool ret = (*env)->CallStaticBooleanMethod(env, mActivityClass, midGetManifestEnvironmentVariables); | ||
| 2437 | if (ret) { | ||
| 2438 | bHasEnvironmentVariables = true; | ||
| 2439 | } | ||
| 2440 | } | ||
| 2441 | } | ||
| 2442 | |||
| 2443 | int Android_JNI_CreateCustomCursor(SDL_Surface *surface, int hot_x, int hot_y) | ||
| 2444 | { | ||
| 2445 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2446 | int custom_cursor = 0; | ||
| 2447 | jintArray pixels; | ||
| 2448 | pixels = (*env)->NewIntArray(env, surface->w * surface->h); | ||
| 2449 | if (pixels) { | ||
| 2450 | (*env)->SetIntArrayRegion(env, pixels, 0, surface->w * surface->h, (int *)surface->pixels); | ||
| 2451 | custom_cursor = (*env)->CallStaticIntMethod(env, mActivityClass, midCreateCustomCursor, pixels, surface->w, surface->h, hot_x, hot_y); | ||
| 2452 | (*env)->DeleteLocalRef(env, pixels); | ||
| 2453 | } else { | ||
| 2454 | SDL_OutOfMemory(); | ||
| 2455 | } | ||
| 2456 | return custom_cursor; | ||
| 2457 | } | ||
| 2458 | |||
| 2459 | void Android_JNI_DestroyCustomCursor(int cursorID) | ||
| 2460 | { | ||
| 2461 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2462 | (*env)->CallStaticVoidMethod(env, mActivityClass, midDestroyCustomCursor, cursorID); | ||
| 2463 | } | ||
| 2464 | |||
| 2465 | bool Android_JNI_SetCustomCursor(int cursorID) | ||
| 2466 | { | ||
| 2467 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2468 | return (*env)->CallStaticBooleanMethod(env, mActivityClass, midSetCustomCursor, cursorID); | ||
| 2469 | } | ||
| 2470 | |||
| 2471 | bool Android_JNI_SetSystemCursor(int cursorID) | ||
| 2472 | { | ||
| 2473 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2474 | return (*env)->CallStaticBooleanMethod(env, mActivityClass, midSetSystemCursor, cursorID); | ||
| 2475 | } | ||
| 2476 | |||
| 2477 | bool Android_JNI_SupportsRelativeMouse(void) | ||
| 2478 | { | ||
| 2479 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2480 | return (*env)->CallStaticBooleanMethod(env, mActivityClass, midSupportsRelativeMouse); | ||
| 2481 | } | ||
| 2482 | |||
| 2483 | bool Android_JNI_SetRelativeMouseEnabled(bool enabled) | ||
| 2484 | { | ||
| 2485 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2486 | return (*env)->CallStaticBooleanMethod(env, mActivityClass, midSetRelativeMouseEnabled, (enabled == 1)); | ||
| 2487 | } | ||
| 2488 | |||
| 2489 | typedef struct NativePermissionRequestInfo | ||
| 2490 | { | ||
| 2491 | int request_code; | ||
| 2492 | char *permission; | ||
| 2493 | SDL_RequestAndroidPermissionCallback callback; | ||
| 2494 | void *userdata; | ||
| 2495 | struct NativePermissionRequestInfo *next; | ||
| 2496 | } NativePermissionRequestInfo; | ||
| 2497 | |||
| 2498 | static NativePermissionRequestInfo pending_permissions; | ||
| 2499 | |||
| 2500 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePermissionResult)( | ||
| 2501 | JNIEnv *env, jclass cls, | ||
| 2502 | jint requestCode, jboolean result) | ||
| 2503 | { | ||
| 2504 | SDL_LockMutex(Android_ActivityMutex); | ||
| 2505 | NativePermissionRequestInfo *prev = &pending_permissions; | ||
| 2506 | for (NativePermissionRequestInfo *info = prev->next; info != NULL; info = info->next) { | ||
| 2507 | if (info->request_code == (int) requestCode) { | ||
| 2508 | prev->next = info->next; | ||
| 2509 | SDL_UnlockMutex(Android_ActivityMutex); | ||
| 2510 | info->callback(info->userdata, info->permission, result ? true : false); | ||
| 2511 | SDL_free(info->permission); | ||
| 2512 | SDL_free(info); | ||
| 2513 | return; | ||
| 2514 | } | ||
| 2515 | prev = info; | ||
| 2516 | } | ||
| 2517 | |||
| 2518 | SDL_UnlockMutex(Android_ActivityMutex); | ||
| 2519 | } | ||
| 2520 | |||
| 2521 | bool SDL_RequestAndroidPermission(const char *permission, SDL_RequestAndroidPermissionCallback cb, void *userdata) | ||
| 2522 | { | ||
| 2523 | if (!permission) { | ||
| 2524 | return SDL_InvalidParamError("permission"); | ||
| 2525 | } else if (!cb) { | ||
| 2526 | return SDL_InvalidParamError("cb"); | ||
| 2527 | } | ||
| 2528 | |||
| 2529 | NativePermissionRequestInfo *info = (NativePermissionRequestInfo *) SDL_calloc(1, sizeof (NativePermissionRequestInfo)); | ||
| 2530 | if (!info) { | ||
| 2531 | return false; | ||
| 2532 | } | ||
| 2533 | |||
| 2534 | info->permission = SDL_strdup(permission); | ||
| 2535 | if (!info->permission) { | ||
| 2536 | SDL_free(info); | ||
| 2537 | return false; | ||
| 2538 | } | ||
| 2539 | |||
| 2540 | static SDL_AtomicInt next_request_code; | ||
| 2541 | info->request_code = SDL_AddAtomicInt(&next_request_code, 1); | ||
| 2542 | |||
| 2543 | info->callback = cb; | ||
| 2544 | info->userdata = userdata; | ||
| 2545 | |||
| 2546 | SDL_LockMutex(Android_ActivityMutex); | ||
| 2547 | info->next = pending_permissions.next; | ||
| 2548 | pending_permissions.next = info; | ||
| 2549 | SDL_UnlockMutex(Android_ActivityMutex); | ||
| 2550 | |||
| 2551 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2552 | jstring jpermission = (*env)->NewStringUTF(env, permission); | ||
| 2553 | (*env)->CallStaticVoidMethod(env, mActivityClass, midRequestPermission, jpermission, info->request_code); | ||
| 2554 | (*env)->DeleteLocalRef(env, jpermission); | ||
| 2555 | |||
| 2556 | return true; | ||
| 2557 | } | ||
| 2558 | |||
| 2559 | // Show toast notification | ||
| 2560 | bool Android_JNI_ShowToast(const char *message, int duration, int gravity, int xOffset, int yOffset) | ||
| 2561 | { | ||
| 2562 | bool result; | ||
| 2563 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2564 | jstring jmessage = (*env)->NewStringUTF(env, message); | ||
| 2565 | result = (*env)->CallStaticBooleanMethod(env, mActivityClass, midShowToast, jmessage, duration, gravity, xOffset, yOffset); | ||
| 2566 | (*env)->DeleteLocalRef(env, jmessage); | ||
| 2567 | return result; | ||
| 2568 | } | ||
| 2569 | |||
| 2570 | bool Android_JNI_GetLocale(char *buf, size_t buflen) | ||
| 2571 | { | ||
| 2572 | AConfiguration *cfg; | ||
| 2573 | |||
| 2574 | SDL_assert(buflen > 6); | ||
| 2575 | |||
| 2576 | // Need to re-create the asset manager if locale has changed (SDL_EVENT_LOCALE_CHANGED) | ||
| 2577 | Internal_Android_Destroy_AssetManager(); | ||
| 2578 | |||
| 2579 | if (!asset_manager) { | ||
| 2580 | Internal_Android_Create_AssetManager(); | ||
| 2581 | } | ||
| 2582 | |||
| 2583 | if (!asset_manager) { | ||
| 2584 | return false; | ||
| 2585 | } | ||
| 2586 | |||
| 2587 | cfg = AConfiguration_new(); | ||
| 2588 | if (!cfg) { | ||
| 2589 | return false; | ||
| 2590 | } | ||
| 2591 | |||
| 2592 | { | ||
| 2593 | char language[2] = {}; | ||
| 2594 | char country[2] = {}; | ||
| 2595 | size_t id = 0; | ||
| 2596 | |||
| 2597 | AConfiguration_fromAssetManager(cfg, asset_manager); | ||
| 2598 | AConfiguration_getLanguage(cfg, language); | ||
| 2599 | AConfiguration_getCountry(cfg, country); | ||
| 2600 | |||
| 2601 | // Indonesian is "id" according to ISO 639.2, but on Android is "in" because of Java backwards compatibility | ||
| 2602 | if (language[0] == 'i' && language[1] == 'n') { | ||
| 2603 | language[1] = 'd'; | ||
| 2604 | } | ||
| 2605 | |||
| 2606 | // copy language (not null terminated) | ||
| 2607 | if (language[0]) { | ||
| 2608 | buf[id++] = language[0]; | ||
| 2609 | if (language[1]) { | ||
| 2610 | buf[id++] = language[1]; | ||
| 2611 | } | ||
| 2612 | } | ||
| 2613 | |||
| 2614 | buf[id++] = '_'; | ||
| 2615 | |||
| 2616 | // copy country (not null terminated) | ||
| 2617 | if (country[0]) { | ||
| 2618 | buf[id++] = country[0]; | ||
| 2619 | if (country[1]) { | ||
| 2620 | buf[id++] = country[1]; | ||
| 2621 | } | ||
| 2622 | } | ||
| 2623 | |||
| 2624 | buf[id++] = '\0'; | ||
| 2625 | SDL_assert(id <= buflen); | ||
| 2626 | } | ||
| 2627 | |||
| 2628 | AConfiguration_delete(cfg); | ||
| 2629 | |||
| 2630 | return true; | ||
| 2631 | } | ||
| 2632 | |||
| 2633 | bool Android_JNI_OpenURL(const char *url) | ||
| 2634 | { | ||
| 2635 | bool result; | ||
| 2636 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2637 | jstring jurl = (*env)->NewStringUTF(env, url); | ||
| 2638 | result = (*env)->CallStaticBooleanMethod(env, mActivityClass, midOpenURL, jurl); | ||
| 2639 | (*env)->DeleteLocalRef(env, jurl); | ||
| 2640 | return result; | ||
| 2641 | } | ||
| 2642 | |||
| 2643 | int Android_JNI_OpenFileDescriptor(const char *uri, const char *mode) | ||
| 2644 | { | ||
| 2645 | // Get fopen-style modes | ||
| 2646 | int moderead = 0, modewrite = 0, modeappend = 0, modeupdate = 0; | ||
| 2647 | |||
| 2648 | for (const char *cmode = mode; *cmode; cmode++) { | ||
| 2649 | switch (*cmode) { | ||
| 2650 | case 'a': | ||
| 2651 | modeappend = 1; | ||
| 2652 | break; | ||
| 2653 | case 'r': | ||
| 2654 | moderead = 1; | ||
| 2655 | break; | ||
| 2656 | case 'w': | ||
| 2657 | modewrite = 1; | ||
| 2658 | break; | ||
| 2659 | case '+': | ||
| 2660 | modeupdate = 1; | ||
| 2661 | break; | ||
| 2662 | default: | ||
| 2663 | break; | ||
| 2664 | } | ||
| 2665 | } | ||
| 2666 | |||
| 2667 | // Translate fopen-style modes to ContentResolver modes. | ||
| 2668 | // Android only allows "r", "w", "wt", "wa", "rw" or "rwt". | ||
| 2669 | const char *contentResolverMode = "r"; | ||
| 2670 | |||
| 2671 | if (moderead) { | ||
| 2672 | if (modewrite) { | ||
| 2673 | contentResolverMode = "rwt"; | ||
| 2674 | } else { | ||
| 2675 | contentResolverMode = modeupdate ? "rw" : "r"; | ||
| 2676 | } | ||
| 2677 | } else if (modewrite) { | ||
| 2678 | contentResolverMode = modeupdate ? "rwt" : "wt"; | ||
| 2679 | } else if (modeappend) { | ||
| 2680 | contentResolverMode = modeupdate ? "rw" : "wa"; | ||
| 2681 | } | ||
| 2682 | |||
| 2683 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2684 | jstring jstringUri = (*env)->NewStringUTF(env, uri); | ||
| 2685 | jstring jstringMode = (*env)->NewStringUTF(env, contentResolverMode); | ||
| 2686 | jint fd = (*env)->CallStaticIntMethod(env, mActivityClass, midOpenFileDescriptor, jstringUri, jstringMode); | ||
| 2687 | (*env)->DeleteLocalRef(env, jstringUri); | ||
| 2688 | (*env)->DeleteLocalRef(env, jstringMode); | ||
| 2689 | |||
| 2690 | if (fd == -1) { | ||
| 2691 | SDL_SetError("Unspecified error in JNI"); | ||
| 2692 | } | ||
| 2693 | |||
| 2694 | return fd; | ||
| 2695 | } | ||
| 2696 | |||
| 2697 | static struct AndroidFileDialog | ||
| 2698 | { | ||
| 2699 | int request_code; | ||
| 2700 | SDL_DialogFileCallback callback; | ||
| 2701 | void *userdata; | ||
| 2702 | } mAndroidFileDialogData; | ||
| 2703 | |||
| 2704 | JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeFileDialog)( | ||
| 2705 | JNIEnv *env, jclass jcls, | ||
| 2706 | jint requestCode, jobjectArray fileList, jint filter) | ||
| 2707 | { | ||
| 2708 | if (mAndroidFileDialogData.callback != NULL && mAndroidFileDialogData.request_code == requestCode) { | ||
| 2709 | if (fileList == NULL) { | ||
| 2710 | SDL_SetError("Unspecified error in JNI"); | ||
| 2711 | mAndroidFileDialogData.callback(mAndroidFileDialogData.userdata, NULL, -1); | ||
| 2712 | mAndroidFileDialogData.callback = NULL; | ||
| 2713 | return; | ||
| 2714 | } | ||
| 2715 | |||
| 2716 | // Convert fileList to string | ||
| 2717 | size_t count = (*env)->GetArrayLength(env, fileList); | ||
| 2718 | char **charFileList = SDL_calloc(count + 1, sizeof(char*)); | ||
| 2719 | |||
| 2720 | if (charFileList == NULL) { | ||
| 2721 | mAndroidFileDialogData.callback(mAndroidFileDialogData.userdata, NULL, -1); | ||
| 2722 | mAndroidFileDialogData.callback = NULL; | ||
| 2723 | return; | ||
| 2724 | } | ||
| 2725 | |||
| 2726 | // Convert to UTF-8 | ||
| 2727 | // TODO: Fix modified UTF-8 to classic UTF-8 | ||
| 2728 | for (int i = 0; i < count; i++) { | ||
| 2729 | jstring string = (*env)->GetObjectArrayElement(env, fileList, i); | ||
| 2730 | if (!string) { | ||
| 2731 | continue; | ||
| 2732 | } | ||
| 2733 | |||
| 2734 | const char *utf8string = (*env)->GetStringUTFChars(env, string, NULL); | ||
| 2735 | if (!utf8string) { | ||
| 2736 | (*env)->DeleteLocalRef(env, string); | ||
| 2737 | continue; | ||
| 2738 | } | ||
| 2739 | |||
| 2740 | char *newFile = SDL_strdup(utf8string); | ||
| 2741 | if (!newFile) { | ||
| 2742 | (*env)->ReleaseStringUTFChars(env, string, utf8string); | ||
| 2743 | (*env)->DeleteLocalRef(env, string); | ||
| 2744 | mAndroidFileDialogData.callback(mAndroidFileDialogData.userdata, NULL, -1); | ||
| 2745 | mAndroidFileDialogData.callback = NULL; | ||
| 2746 | |||
| 2747 | // Cleanup memory | ||
| 2748 | for (int j = 0; j < i; j++) { | ||
| 2749 | SDL_free(charFileList[j]); | ||
| 2750 | } | ||
| 2751 | SDL_free(charFileList); | ||
| 2752 | return; | ||
| 2753 | } | ||
| 2754 | |||
| 2755 | charFileList[i] = newFile; | ||
| 2756 | (*env)->ReleaseStringUTFChars(env, string, utf8string); | ||
| 2757 | (*env)->DeleteLocalRef(env, string); | ||
| 2758 | } | ||
| 2759 | |||
| 2760 | // Call user-provided callback | ||
| 2761 | SDL_ClearError(); | ||
| 2762 | mAndroidFileDialogData.callback(mAndroidFileDialogData.userdata, (const char *const *) charFileList, filter); | ||
| 2763 | mAndroidFileDialogData.callback = NULL; | ||
| 2764 | |||
| 2765 | // Cleanup memory | ||
| 2766 | for (int i = 0; i < count; i++) { | ||
| 2767 | SDL_free(charFileList[i]); | ||
| 2768 | } | ||
| 2769 | SDL_free(charFileList); | ||
| 2770 | } | ||
| 2771 | } | ||
| 2772 | |||
| 2773 | bool Android_JNI_OpenFileDialog( | ||
| 2774 | SDL_DialogFileCallback callback, void* userdata, | ||
| 2775 | const SDL_DialogFileFilter *filters, int nfilters, bool forwrite, | ||
| 2776 | bool multiple) | ||
| 2777 | { | ||
| 2778 | if (mAndroidFileDialogData.callback != NULL) { | ||
| 2779 | SDL_SetError("Only one file dialog can be run at a time."); | ||
| 2780 | return false; | ||
| 2781 | } | ||
| 2782 | |||
| 2783 | if (forwrite) { | ||
| 2784 | multiple = false; | ||
| 2785 | } | ||
| 2786 | |||
| 2787 | JNIEnv *env = Android_JNI_GetEnv(); | ||
| 2788 | |||
| 2789 | // Setup filters | ||
| 2790 | jobjectArray filtersArray = NULL; | ||
| 2791 | if (filters) { | ||
| 2792 | jclass stringClass = (*env)->FindClass(env, "java/lang/String"); | ||
| 2793 | filtersArray = (*env)->NewObjectArray(env, nfilters, stringClass, NULL); | ||
| 2794 | |||
| 2795 | // Convert to string | ||
| 2796 | for (int i = 0; i < nfilters; i++) { | ||
| 2797 | jstring str = (*env)->NewStringUTF(env, filters[i].pattern); | ||
| 2798 | (*env)->SetObjectArrayElement(env, filtersArray, i, str); | ||
| 2799 | (*env)->DeleteLocalRef(env, str); | ||
| 2800 | } | ||
| 2801 | } | ||
| 2802 | |||
| 2803 | // Setup data | ||
| 2804 | static SDL_AtomicInt next_request_code; | ||
| 2805 | mAndroidFileDialogData.request_code = SDL_AddAtomicInt(&next_request_code, 1); | ||
| 2806 | mAndroidFileDialogData.userdata = userdata; | ||
| 2807 | mAndroidFileDialogData.callback = callback; | ||
| 2808 | |||
| 2809 | // Invoke JNI | ||
| 2810 | jboolean success = (*env)->CallStaticBooleanMethod(env, mActivityClass, | ||
| 2811 | midShowFileDialog, filtersArray, (jboolean) multiple, (jboolean) forwrite, mAndroidFileDialogData.request_code); | ||
| 2812 | (*env)->DeleteLocalRef(env, filtersArray); | ||
| 2813 | if (!success) { | ||
| 2814 | mAndroidFileDialogData.callback = NULL; | ||
| 2815 | SDL_AddAtomicInt(&next_request_code, -1); | ||
| 2816 | SDL_SetError("Unspecified error in JNI"); | ||
| 2817 | |||
| 2818 | return false; | ||
| 2819 | } | ||
| 2820 | |||
| 2821 | return true; | ||
| 2822 | } | ||
| 2823 | |||
| 2824 | #endif // SDL_PLATFORM_ANDROID | ||
diff --git a/contrib/SDL-3.2.8/src/core/android/SDL_android.h b/contrib/SDL-3.2.8/src/core/android/SDL_android.h new file mode 100644 index 0000000..3541c2a --- /dev/null +++ b/contrib/SDL-3.2.8/src/core/android/SDL_android.h | |||
| @@ -0,0 +1,163 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org> | ||
| 4 | |||
| 5 | This software is provided 'as-is', without any express or implied | ||
| 6 | warranty. In no event will the authors be held liable for any damages | ||
| 7 | arising from the use of this software. | ||
| 8 | |||
| 9 | Permission is granted to anyone to use this software for any purpose, | ||
| 10 | including commercial applications, and to alter it and redistribute it | ||
| 11 | freely, subject to the following restrictions: | ||
| 12 | |||
| 13 | 1. The origin of this software must not be misrepresented; you must not | ||
| 14 | claim that you wrote the original software. If you use this software | ||
| 15 | in a product, an acknowledgment in the product documentation would be | ||
| 16 | appreciated but is not required. | ||
| 17 | 2. Altered source versions must be plainly marked as such, and must not be | ||
| 18 | misrepresented as being the original software. | ||
| 19 | 3. This notice may not be removed or altered from any source distribution. | ||
| 20 | */ | ||
| 21 | #include "SDL_internal.h" | ||
| 22 | |||
| 23 | #ifndef SDL_android_h | ||
| 24 | #define SDL_android_h | ||
| 25 | |||
| 26 | // Set up for C function definitions, even when using C++ | ||
| 27 | #ifdef __cplusplus | ||
| 28 | /* *INDENT-OFF* */ | ||
| 29 | extern "C" { | ||
| 30 | /* *INDENT-ON* */ | ||
| 31 | #endif | ||
| 32 | |||
| 33 | #include <EGL/eglplatform.h> | ||
| 34 | #include <android/native_window_jni.h> | ||
| 35 | |||
| 36 | #include "../../audio/SDL_sysaudio.h" | ||
| 37 | |||
| 38 | // this appears to be broken right now (on Android, not SDL, I think...?). | ||
| 39 | #define ALLOW_MULTIPLE_ANDROID_AUDIO_DEVICES 0 | ||
| 40 | |||
| 41 | // Life cycle | ||
| 42 | typedef enum | ||
| 43 | { | ||
| 44 | SDL_ANDROID_LIFECYCLE_WAKE, | ||
| 45 | SDL_ANDROID_LIFECYCLE_PAUSE, | ||
| 46 | SDL_ANDROID_LIFECYCLE_RESUME, | ||
| 47 | SDL_ANDROID_LIFECYCLE_LOWMEMORY, | ||
| 48 | SDL_ANDROID_LIFECYCLE_DESTROY, | ||
| 49 | SDL_NUM_ANDROID_LIFECYCLE_EVENTS | ||
| 50 | } SDL_AndroidLifecycleEvent; | ||
| 51 | |||
| 52 | void Android_SendLifecycleEvent(SDL_AndroidLifecycleEvent event); | ||
| 53 | bool Android_WaitLifecycleEvent(SDL_AndroidLifecycleEvent *event, Sint64 timeoutNS); | ||
| 54 | |||
| 55 | void Android_LockActivityMutex(void); | ||
| 56 | void Android_UnlockActivityMutex(void); | ||
| 57 | |||
| 58 | // Interface from the SDL library into the Android Java activity | ||
| 59 | extern void Android_JNI_SetActivityTitle(const char *title); | ||
| 60 | extern void Android_JNI_SetWindowStyle(bool fullscreen); | ||
| 61 | extern void Android_JNI_SetOrientation(int w, int h, int resizable, const char *hint); | ||
| 62 | extern void Android_JNI_MinizeWindow(void); | ||
| 63 | extern bool Android_JNI_ShouldMinimizeOnFocusLoss(void); | ||
| 64 | |||
| 65 | extern bool Android_JNI_GetAccelerometerValues(float values[3]); | ||
| 66 | extern void Android_JNI_ShowScreenKeyboard(int input_type, SDL_Rect *inputRect); | ||
| 67 | extern void Android_JNI_HideScreenKeyboard(void); | ||
| 68 | extern bool Android_JNI_IsScreenKeyboardShown(void); | ||
| 69 | extern ANativeWindow *Android_JNI_GetNativeWindow(void); | ||
| 70 | |||
| 71 | extern SDL_DisplayOrientation Android_JNI_GetDisplayNaturalOrientation(void); | ||
| 72 | extern SDL_DisplayOrientation Android_JNI_GetDisplayCurrentOrientation(void); | ||
| 73 | |||
| 74 | // Audio support | ||
| 75 | void Android_StartAudioHotplug(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording); | ||
| 76 | void Android_StopAudioHotplug(void); | ||
| 77 | extern void Android_AudioThreadInit(SDL_AudioDevice *device); | ||
| 78 | |||
| 79 | // Detecting device type | ||
| 80 | extern bool Android_IsDeXMode(void); | ||
| 81 | extern bool Android_IsChromebook(void); | ||
| 82 | |||
| 83 | bool Android_JNI_FileOpen(void **puserdata, const char *fileName, const char *mode); | ||
| 84 | Sint64 Android_JNI_FileSize(void *userdata); | ||
| 85 | Sint64 Android_JNI_FileSeek(void *userdata, Sint64 offset, SDL_IOWhence whence); | ||
| 86 | size_t Android_JNI_FileRead(void *userdata, void *buffer, size_t size, SDL_IOStatus *status); | ||
| 87 | size_t Android_JNI_FileWrite(void *userdata, const void *buffer, size_t size, SDL_IOStatus *status); | ||
| 88 | bool Android_JNI_FileClose(void *userdata); | ||
| 89 | |||
| 90 | // Environment support | ||
| 91 | void Android_JNI_GetManifestEnvironmentVariables(void); | ||
| 92 | int Android_JNI_OpenFileDescriptor(const char *uri, const char *mode); | ||
| 93 | |||
| 94 | // Clipboard support | ||
| 95 | bool Android_JNI_SetClipboardText(const char *text); | ||
| 96 | char *Android_JNI_GetClipboardText(void); | ||
| 97 | bool Android_JNI_HasClipboardText(void); | ||
| 98 | |||
| 99 | // Power support | ||
| 100 | int Android_JNI_GetPowerInfo(int *plugged, int *charged, int *battery, int *seconds, int *percent); | ||
| 101 | |||
| 102 | // Joystick support | ||
| 103 | void Android_JNI_PollInputDevices(void); | ||
| 104 | |||
| 105 | // Haptic support | ||
| 106 | void Android_JNI_PollHapticDevices(void); | ||
| 107 | void Android_JNI_HapticRun(int device_id, float intensity, int length); | ||
| 108 | void Android_JNI_HapticRumble(int device_id, float low_frequency_intensity, float high_frequency_intensity, int length); | ||
| 109 | void Android_JNI_HapticStop(int device_id); | ||
| 110 | |||
| 111 | // Video | ||
| 112 | bool Android_JNI_SuspendScreenSaver(bool suspend); | ||
| 113 | |||
| 114 | // Touch support | ||
| 115 | void Android_JNI_InitTouch(void); | ||
| 116 | |||
| 117 | // Threads | ||
| 118 | #include <jni.h> | ||
| 119 | JNIEnv *Android_JNI_GetEnv(void); | ||
| 120 | bool Android_JNI_SetupThread(void); | ||
| 121 | |||
| 122 | // Locale | ||
| 123 | bool Android_JNI_GetLocale(char *buf, size_t buflen); | ||
| 124 | |||
| 125 | // Generic messages | ||
| 126 | bool Android_JNI_SendMessage(int command, int param); | ||
| 127 | |||
| 128 | // MessageBox | ||
| 129 | bool Android_JNI_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID); | ||
| 130 | |||
| 131 | // Cursor support | ||
| 132 | int Android_JNI_CreateCustomCursor(SDL_Surface *surface, int hot_x, int hot_y); | ||
| 133 | void Android_JNI_DestroyCustomCursor(int cursorID); | ||
| 134 | bool Android_JNI_SetCustomCursor(int cursorID); | ||
| 135 | bool Android_JNI_SetSystemCursor(int cursorID); | ||
| 136 | |||
| 137 | // Relative mouse support | ||
| 138 | bool Android_JNI_SupportsRelativeMouse(void); | ||
| 139 | bool Android_JNI_SetRelativeMouseEnabled(bool enabled); | ||
| 140 | |||
| 141 | // Show toast notification | ||
| 142 | bool Android_JNI_ShowToast(const char *message, int duration, int gravity, int xOffset, int yOffset); | ||
| 143 | |||
| 144 | bool Android_JNI_OpenURL(const char *url); | ||
| 145 | |||
| 146 | int SDL_GetAndroidSDKVersion(void); | ||
| 147 | |||
| 148 | bool SDL_IsAndroidTablet(void); | ||
| 149 | bool SDL_IsAndroidTV(void); | ||
| 150 | |||
| 151 | // File Dialogs | ||
| 152 | bool Android_JNI_OpenFileDialog(SDL_DialogFileCallback callback, void* userdata, | ||
| 153 | const SDL_DialogFileFilter *filters, int nfilters, bool forwrite, | ||
| 154 | bool multiple); | ||
| 155 | |||
| 156 | // Ends C function definitions when using C++ | ||
| 157 | #ifdef __cplusplus | ||
| 158 | /* *INDENT-OFF* */ | ||
| 159 | } | ||
| 160 | /* *INDENT-ON* */ | ||
| 161 | #endif | ||
| 162 | |||
| 163 | #endif // SDL_android_h | ||
