SDL  2.0
SDL_android.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2020 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 #include "SDL_stdinc.h"
23 #include "SDL_assert.h"
24 #include "SDL_atomic.h"
25 #include "SDL_hints.h"
26 #include "SDL_log.h"
27 #include "SDL_main.h"
28 #include "SDL_timer.h"
29 
30 #ifdef __ANDROID__
31 
32 #include "SDL_system.h"
33 #include "SDL_android.h"
34 
35 #include "keyinfotable.h"
36 
37 #include "../../events/SDL_events_c.h"
38 #include "../../video/android/SDL_androidkeyboard.h"
39 #include "../../video/android/SDL_androidmouse.h"
40 #include "../../video/android/SDL_androidtouch.h"
41 #include "../../video/android/SDL_androidvideo.h"
42 #include "../../video/android/SDL_androidwindow.h"
43 #include "../../joystick/android/SDL_sysjoystick_c.h"
44 #include "../../haptic/android/SDL_syshaptic_c.h"
45 
46 #include <android/log.h>
47 #include <sys/system_properties.h>
48 #include <pthread.h>
49 #include <sys/types.h>
50 #include <unistd.h>
51 #include <dlfcn.h>
52 
53 #define SDL_JAVA_PREFIX org_libsdl_app
54 #define CONCAT1(prefix, class, function) CONCAT2(prefix, class, function)
55 #define CONCAT2(prefix, class, function) Java_ ## prefix ## _ ## class ## _ ## function
56 #define SDL_JAVA_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, SDLActivity, function)
57 #define SDL_JAVA_AUDIO_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, SDLAudioManager, function)
58 #define SDL_JAVA_CONTROLLER_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, SDLControllerManager, function)
59 #define SDL_JAVA_INTERFACE_INPUT_CONNECTION(function) CONCAT1(SDL_JAVA_PREFIX, SDLInputConnection, function)
60 
61 /* Audio encoding definitions */
62 #define ENCODING_PCM_8BIT 3
63 #define ENCODING_PCM_16BIT 2
64 #define ENCODING_PCM_FLOAT 4
65 
66 /* Java class SDLActivity */
67 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(
68  JNIEnv *env, jclass cls);
69 
70 JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeRunMain)(
71  JNIEnv *env, jclass cls,
72  jstring library, jstring function, jobject array);
73 
74 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeDropFile)(
75  JNIEnv *env, jclass jcls,
76  jstring filename);
77 
78 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetScreenResolution)(
79  JNIEnv *env, jclass jcls,
80  jint surfaceWidth, jint surfaceHeight,
81  jint deviceWidth, jint deviceHeight, jint format, jfloat rate);
82 
83 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeResize)(
84  JNIEnv *env, jclass cls);
85 
86 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceCreated)(
87  JNIEnv *env, jclass jcls);
88 
89 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(
90  JNIEnv *env, jclass jcls);
91 
92 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)(
93  JNIEnv *env, jclass jcls);
94 
95 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyDown)(
96  JNIEnv *env, jclass jcls,
97  jint keycode);
98 
99 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyUp)(
100  JNIEnv *env, jclass jcls,
101  jint keycode);
102 
103 JNIEXPORT jboolean JNICALL SDL_JAVA_INTERFACE(onNativeSoftReturnKey)(
104  JNIEnv *env, jclass jcls);
105 
106 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyboardFocusLost)(
107  JNIEnv *env, jclass jcls);
108 
109 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeTouch)(
110  JNIEnv *env, jclass jcls,
111  jint touch_device_id_in, jint pointer_finger_id_in,
112  jint action, jfloat x, jfloat y, jfloat p);
113 
114 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeMouse)(
115  JNIEnv *env, jclass jcls,
116  jint button, jint action, jfloat x, jfloat y, jboolean relative);
117 
118 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeAccel)(
119  JNIEnv *env, jclass jcls,
120  jfloat x, jfloat y, jfloat z);
121 
122 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeClipboardChanged)(
123  JNIEnv *env, jclass jcls);
124 
125 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeLowMemory)(
126  JNIEnv *env, jclass cls);
127 
128 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSendQuit)(
129  JNIEnv *env, jclass cls);
130 
131 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeQuit)(
132  JNIEnv *env, jclass cls);
133 
134 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePause)(
135  JNIEnv *env, jclass cls);
136 
137 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeResume)(
138  JNIEnv *env, jclass cls);
139 
140 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeFocusChanged)(
141  JNIEnv *env, jclass cls, jboolean hasFocus);
142 
143 JNIEXPORT jstring JNICALL SDL_JAVA_INTERFACE(nativeGetHint)(
144  JNIEnv *env, jclass cls,
145  jstring name);
146 
147 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetenv)(
148  JNIEnv *env, jclass cls,
149  jstring name, jstring value);
150 
151 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeOrientationChanged)(
152  JNIEnv *env, jclass cls,
153  jint orientation);
154 
155 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeAddTouch)(
156  JNIEnv* env, jclass cls,
157  jint touchId, jstring name);
158 
159 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePermissionResult)(
160  JNIEnv* env, jclass cls,
161  jint requestCode, jboolean result);
162 
163 static JNINativeMethod SDLActivity_tab[] = {
164  { "nativeSetupJNI", "()I", SDL_JAVA_INTERFACE(nativeSetupJNI) },
165  { "nativeRunMain", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)I", SDL_JAVA_INTERFACE(nativeRunMain) },
166  { "onNativeDropFile", "(Ljava/lang/String;)V", SDL_JAVA_INTERFACE(onNativeDropFile) },
167  { "nativeSetScreenResolution", "(IIIIIF)V", SDL_JAVA_INTERFACE(nativeSetScreenResolution) },
168  { "onNativeResize", "()V", SDL_JAVA_INTERFACE(onNativeResize) },
169  { "onNativeSurfaceCreated", "()V", SDL_JAVA_INTERFACE(onNativeSurfaceCreated) },
170  { "onNativeSurfaceChanged", "()V", SDL_JAVA_INTERFACE(onNativeSurfaceChanged) },
171  { "onNativeSurfaceDestroyed", "()V", SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed) },
172  { "onNativeKeyDown", "(I)V", SDL_JAVA_INTERFACE(onNativeKeyDown) },
173  { "onNativeKeyUp", "(I)V", SDL_JAVA_INTERFACE(onNativeKeyUp) },
174  { "onNativeSoftReturnKey", "()Z", SDL_JAVA_INTERFACE(onNativeSoftReturnKey) },
175  { "onNativeKeyboardFocusLost", "()V", SDL_JAVA_INTERFACE(onNativeKeyboardFocusLost) },
176  { "onNativeTouch", "(IIIFFF)V", SDL_JAVA_INTERFACE(onNativeTouch) },
177  { "onNativeMouse", "(IIFFZ)V", SDL_JAVA_INTERFACE(onNativeMouse) },
178  { "onNativeAccel", "(FFF)V", SDL_JAVA_INTERFACE(onNativeAccel) },
179  { "onNativeClipboardChanged", "()V", SDL_JAVA_INTERFACE(onNativeClipboardChanged) },
180  { "nativeLowMemory", "()V", SDL_JAVA_INTERFACE(nativeLowMemory) },
181  { "nativeSendQuit", "()V", SDL_JAVA_INTERFACE(nativeSendQuit) },
182  { "nativeQuit", "()V", SDL_JAVA_INTERFACE(nativeQuit) },
183  { "nativePause", "()V", SDL_JAVA_INTERFACE(nativePause) },
184  { "nativeResume", "()V", SDL_JAVA_INTERFACE(nativeResume) },
185  { "nativeFocusChanged", "(Z)V", SDL_JAVA_INTERFACE(nativeFocusChanged) },
186  { "nativeGetHint", "(Ljava/lang/String;)Ljava/lang/String;", SDL_JAVA_INTERFACE(nativeGetHint) },
187  { "nativeSetenv", "(Ljava/lang/String;Ljava/lang/String;)V", SDL_JAVA_INTERFACE(nativeSetenv) },
188  { "onNativeOrientationChanged", "(I)V", SDL_JAVA_INTERFACE(onNativeOrientationChanged) },
189  { "nativeAddTouch", "(ILjava/lang/String;)V", SDL_JAVA_INTERFACE(nativeAddTouch) },
190  { "nativePermissionResult", "(IZ)V", SDL_JAVA_INTERFACE(nativePermissionResult) }
191 };
192 
193 /* Java class SDLInputConnection */
194 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeCommitText)(
195  JNIEnv *env, jclass cls,
196  jstring text, jint newCursorPosition);
197 
198 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeGenerateScancodeForUnichar)(
199  JNIEnv *env, jclass cls,
200  jchar chUnicode);
201 
202 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeSetComposingText)(
203  JNIEnv *env, jclass cls,
204  jstring text, jint newCursorPosition);
205 
206 static JNINativeMethod SDLInputConnection_tab[] = {
207  { "nativeCommitText", "(Ljava/lang/String;I)V", SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeCommitText) },
208  { "nativeGenerateScancodeForUnichar", "(C)V", SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeGenerateScancodeForUnichar) },
209  { "nativeSetComposingText", "(Ljava/lang/String;I)V", SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeSetComposingText) }
210 };
211 
212 /* Java class SDLAudioManager */
213 JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(
214  JNIEnv *env, jclass jcls);
215 
216 static JNINativeMethod SDLAudioManager_tab[] = {
217  { "nativeSetupJNI", "()I", SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI) }
218 };
219 
220 /* Java class SDLControllerManager */
221 JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(
222  JNIEnv *env, jclass jcls);
223 
224 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)(
225  JNIEnv *env, jclass jcls,
226  jint device_id, jint keycode);
227 
228 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)(
229  JNIEnv *env, jclass jcls,
230  jint device_id, jint keycode);
231 
232 JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)(
233  JNIEnv *env, jclass jcls,
234  jint device_id, jint axis, jfloat value);
235 
236 JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
237  JNIEnv *env, jclass jcls,
238  jint device_id, jint hat_id, jint x, jint y);
239 
240 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
241  JNIEnv *env, jclass jcls,
242  jint device_id, jstring device_name, jstring device_desc, jint vendor_id, jint product_id,
243  jboolean is_accelerometer, jint button_mask, jint naxes, jint nhats, jint nballs);
244 
245 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)(
246  JNIEnv *env, jclass jcls,
247  jint device_id);
248 
249 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic)(
250  JNIEnv *env, jclass jcls,
251  jint device_id, jstring device_name);
252 
253 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)(
254  JNIEnv *env, jclass jcls,
255  jint device_id);
256 
257 static JNINativeMethod SDLControllerManager_tab[] = {
258  { "nativeSetupJNI", "()I", SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI) },
259  { "onNativePadDown", "(II)I", SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown) },
260  { "onNativePadUp", "(II)I", SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp) },
261  { "onNativeJoy", "(IIF)V", SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy) },
262  { "onNativeHat", "(IIII)V", SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat) },
263  { "nativeAddJoystick", "(ILjava/lang/String;Ljava/lang/String;IIZIIII)I", SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick) },
264  { "nativeRemoveJoystick", "(I)I", SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick) },
265  { "nativeAddHaptic", "(ILjava/lang/String;)I", SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic) },
266  { "nativeRemoveHaptic", "(I)I", SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic) }
267 };
268 
269 
270 /* Uncomment this to log messages entering and exiting methods in this file */
271 /* #define DEBUG_JNI */
272 
273 static void checkJNIReady(void);
274 
275 /*******************************************************************************
276  This file links the Java side of Android with libsdl
277 *******************************************************************************/
278 #include <jni.h>
279 
280 
281 /*******************************************************************************
282  Globals
283 *******************************************************************************/
284 static pthread_key_t mThreadKey;
285 static pthread_once_t key_once = PTHREAD_ONCE_INIT;
286 static JavaVM *mJavaVM = NULL;
287 
288 /* Main activity */
289 static jclass mActivityClass;
290 
291 /* method signatures */
292 static jmethodID midClipboardGetText;
293 static jmethodID midClipboardHasText;
294 static jmethodID midClipboardSetText;
295 static jmethodID midCreateCustomCursor;
296 static jmethodID midGetContext;
297 static jmethodID midGetDisplayDPI;
298 static jmethodID midGetManifestEnvironmentVariables;
299 static jmethodID midGetNativeSurface;
300 static jmethodID midInitTouch;
301 static jmethodID midIsAndroidTV;
302 static jmethodID midIsChromebook;
303 static jmethodID midIsDeXMode;
304 static jmethodID midIsScreenKeyboardShown;
305 static jmethodID midIsTablet;
306 static jmethodID midManualBackButton;
307 static jmethodID midMinimizeWindow;
308 static jmethodID midOpenAPKExpansionInputStream;
309 static jmethodID midRequestPermission;
310 static jmethodID midSendMessage;
311 static jmethodID midSetActivityTitle;
312 static jmethodID midSetCustomCursor;
313 static jmethodID midSetOrientation;
314 static jmethodID midSetRelativeMouseEnabled;
315 static jmethodID midSetSurfaceViewFormat;
316 static jmethodID midSetSystemCursor;
317 static jmethodID midSetWindowStyle;
318 static jmethodID midShouldMinimizeOnFocusLoss;
319 static jmethodID midShowTextInput;
320 static jmethodID midSupportsRelativeMouse;
321 
322 /* audio manager */
323 static jclass mAudioManagerClass;
324 
325 /* method signatures */
326 static jmethodID midAudioOpen;
327 static jmethodID midAudioWriteByteBuffer;
328 static jmethodID midAudioWriteShortBuffer;
329 static jmethodID midAudioWriteFloatBuffer;
330 static jmethodID midAudioClose;
331 static jmethodID midCaptureOpen;
332 static jmethodID midCaptureReadByteBuffer;
333 static jmethodID midCaptureReadShortBuffer;
334 static jmethodID midCaptureReadFloatBuffer;
335 static jmethodID midCaptureClose;
336 static jmethodID midAudioSetThreadPriority;
337 
338 /* controller manager */
339 static jclass mControllerManagerClass;
340 
341 /* method signatures */
342 static jmethodID midPollInputDevices;
343 static jmethodID midPollHapticDevices;
344 static jmethodID midHapticRun;
345 static jmethodID midHapticStop;
346 
347 /* Accelerometer data storage */
348 static SDL_DisplayOrientation displayOrientation;
349 static float fLastAccelerometer[3];
350 static SDL_bool bHasNewData;
351 
352 static SDL_bool bHasEnvironmentVariables;
353 
354 static SDL_atomic_t bPermissionRequestPending;
355 static SDL_bool bPermissionRequestResult;
356 
357 /*******************************************************************************
358  Functions called by JNI
359 *******************************************************************************/
360 
361 /* From http://developer.android.com/guide/practices/jni.html
362  * All threads are Linux threads, scheduled by the kernel.
363  * They're usually started from managed code (using Thread.start), but they can also be created elsewhere and then
364  * attached to the JavaVM. For example, a thread started with pthread_create can be attached with the
365  * JNI AttachCurrentThread or AttachCurrentThreadAsDaemon functions. Until a thread is attached, it has no JNIEnv,
366  * and cannot make JNI calls.
367  * Attaching a natively-created thread causes a java.lang.Thread object to be constructed and added to the "main"
368  * ThreadGroup, making it visible to the debugger. Calling AttachCurrentThread on an already-attached thread
369  * is a no-op.
370  * Note: You can call this function any number of times for the same thread, there's no harm in it
371  */
372 
373 /* From http://developer.android.com/guide/practices/jni.html
374  * Threads attached through JNI must call DetachCurrentThread before they exit. If coding this directly is awkward,
375  * in Android 2.0 (Eclair) and higher you can use pthread_key_create to define a destructor function that will be
376  * called before the thread exits, and call DetachCurrentThread from there. (Use that key with pthread_setspecific
377  * to store the JNIEnv in thread-local-storage; that way it'll be passed into your destructor as the argument.)
378  * Note: The destructor is not called unless the stored value is != NULL
379  * Note: You can call this function any number of times for the same thread, there's no harm in it
380  * (except for some lost CPU cycles)
381  */
382 
383 /* Set local storage value */
384 static int
385 Android_JNI_SetEnv(JNIEnv *env) {
386  int status = pthread_setspecific(mThreadKey, env);
387  if (status < 0) {
388  __android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed pthread_setspecific() in Android_JNI_SetEnv() (err=%d)", status);
389  }
390  return status;
391 }
392 
393 /* Get local storage value */
394 JNIEnv* Android_JNI_GetEnv(void)
395 {
396  /* Get JNIEnv from the Thread local storage */
397  JNIEnv *env = pthread_getspecific(mThreadKey);
398  if (env == NULL) {
399  /* If it fails, try to attach ! (e.g the thread isn't created with SDL_CreateThread() */
400  int status;
401 
402  /* There should be a JVM */
403  if (mJavaVM == NULL) {
404  __android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed, there is no JavaVM");
405  return NULL;
406  }
407 
408  /* Attach the current thread to the JVM and get a JNIEnv.
409  * It will be detached by pthread_create destructor 'Android_JNI_ThreadDestroyed' */
410  status = (*mJavaVM)->AttachCurrentThread(mJavaVM, &env, NULL);
411  if (status < 0) {
412  __android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed to attach current thread (err=%d)", status);
413  return NULL;
414  }
415 
416  /* Save JNIEnv into the Thread local storage */
417  if (Android_JNI_SetEnv(env) < 0) {
418  return NULL;
419  }
420  }
421 
422  return env;
423 }
424 
425 /* Set up an external thread for using JNI with Android_JNI_GetEnv() */
426 int Android_JNI_SetupThread(void)
427 {
428  JNIEnv *env;
429  int status;
430 
431  /* There should be a JVM */
432  if (mJavaVM == NULL) {
433  __android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed, there is no JavaVM");
434  return 0;
435  }
436 
437  /* Attach the current thread to the JVM and get a JNIEnv.
438  * It will be detached by pthread_create destructor 'Android_JNI_ThreadDestroyed' */
439  status = (*mJavaVM)->AttachCurrentThread(mJavaVM, &env, NULL);
440  if (status < 0) {
441  __android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed to attach current thread (err=%d)", status);
442  return 0;
443  }
444 
445  /* Save JNIEnv into the Thread local storage */
446  if (Android_JNI_SetEnv(env) < 0) {
447  return 0;
448  }
449 
450  return 1;
451 }
452 
453 /* Destructor called for each thread where mThreadKey is not NULL */
454 static void
455 Android_JNI_ThreadDestroyed(void *value)
456 {
457  /* The thread is being destroyed, detach it from the Java VM and set the mThreadKey value to NULL as required */
458  JNIEnv *env = (JNIEnv *) value;
459  if (env != NULL) {
460  (*mJavaVM)->DetachCurrentThread(mJavaVM);
461  Android_JNI_SetEnv(NULL);
462  }
463 }
464 
465 /* Creation of local storage mThreadKey */
466 static void
467 Android_JNI_CreateKey(void)
468 {
469  int status = pthread_key_create(&mThreadKey, Android_JNI_ThreadDestroyed);
470  if (status < 0) {
471  __android_log_print(ANDROID_LOG_ERROR, "SDL", "Error initializing mThreadKey with pthread_key_create() (err=%d)", status);
472  }
473 }
474 
475 static void
476 Android_JNI_CreateKey_once(void)
477 {
478  int status = pthread_once(&key_once, Android_JNI_CreateKey);
479  if (status < 0) {
480  __android_log_print(ANDROID_LOG_ERROR, "SDL", "Error initializing mThreadKey with pthread_once() (err=%d)", status);
481  }
482 }
483 
484 static void
485 register_methods(JNIEnv *env, const char *classname, JNINativeMethod *methods, int nb)
486 {
487  jclass clazz = (*env)->FindClass(env, classname);
488  if (clazz == NULL || (*env)->RegisterNatives(env, clazz, methods, nb) < 0) {
489  __android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed to register methods of %s", classname);
490  return;
491  }
492 }
493 
494 /* Library init */
495 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
496 {
497  mJavaVM = vm;
498  JNIEnv *env = NULL;
499 
500  if ((*mJavaVM)->GetEnv(mJavaVM, (void **)&env, JNI_VERSION_1_4) != JNI_OK) {
501  __android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed to get JNI Env");
502  return JNI_VERSION_1_4;
503  }
504 
505  register_methods(env, "org/libsdl/app/SDLActivity", SDLActivity_tab, SDL_arraysize(SDLActivity_tab));
506  register_methods(env, "org/libsdl/app/SDLInputConnection", SDLInputConnection_tab, SDL_arraysize(SDLInputConnection_tab));
507  register_methods(env, "org/libsdl/app/SDLAudioManager", SDLAudioManager_tab, SDL_arraysize(SDLAudioManager_tab));
508  register_methods(env, "org/libsdl/app/SDLControllerManager", SDLControllerManager_tab, SDL_arraysize(SDLControllerManager_tab));
509 
510  return JNI_VERSION_1_4;
511 }
512 
513 void checkJNIReady(void)
514 {
515  if (!mActivityClass || !mAudioManagerClass || !mControllerManagerClass) {
516  /* We aren't fully initialized, let's just return. */
517  return;
518  }
519 
521 }
522 
523 /* Activity initialization -- called before SDL_main() to initialize JNI bindings */
524 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cls)
525 {
526  __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeSetupJNI()");
527 
528  /*
529  * Create mThreadKey so we can keep track of the JNIEnv assigned to each thread
530  * Refer to http://developer.android.com/guide/practices/design/jni.html for the rationale behind this
531  */
532  Android_JNI_CreateKey_once();
533 
534  /* Save JNIEnv of SDLActivity */
535  Android_JNI_SetEnv(env);
536 
537  if (mJavaVM == NULL) {
538  __android_log_print(ANDROID_LOG_ERROR, "SDL", "failed to found a JavaVM");
539  }
540 
541  /* Use a mutex to prevent concurrency issues between Java Activity and Native thread code, when using 'Android_Window'.
542  * (Eg. Java sending Touch events, while native code is destroying the main SDL_Window. )
543  */
544  if (Android_ActivityMutex == NULL) {
545  Android_ActivityMutex = SDL_CreateMutex(); /* Could this be created twice if onCreate() is called a second time ? */
546  }
547 
548  if (Android_ActivityMutex == NULL) {
549  __android_log_print(ANDROID_LOG_ERROR, "SDL", "failed to create Android_ActivityMutex mutex");
550  }
551 
552 
554  if (Android_PauseSem == NULL) {
555  __android_log_print(ANDROID_LOG_ERROR, "SDL", "failed to create Android_PauseSem semaphore");
556  }
557 
559  if (Android_ResumeSem == NULL) {
560  __android_log_print(ANDROID_LOG_ERROR, "SDL", "failed to create Android_ResumeSem semaphore");
561  }
562 
563  mActivityClass = (jclass)((*env)->NewGlobalRef(env, cls));
564 
565  midClipboardGetText = (*env)->GetStaticMethodID(env, mActivityClass, "clipboardGetText", "()Ljava/lang/String;");
566  midClipboardHasText = (*env)->GetStaticMethodID(env, mActivityClass, "clipboardHasText", "()Z");
567  midClipboardSetText = (*env)->GetStaticMethodID(env, mActivityClass, "clipboardSetText", "(Ljava/lang/String;)V");
568  midCreateCustomCursor = (*env)->GetStaticMethodID(env, mActivityClass, "createCustomCursor", "([IIIII)I");
569  midGetContext = (*env)->GetStaticMethodID(env, mActivityClass, "getContext","()Landroid/content/Context;");
570  midGetDisplayDPI = (*env)->GetStaticMethodID(env, mActivityClass, "getDisplayDPI", "()Landroid/util/DisplayMetrics;");
571  midGetManifestEnvironmentVariables = (*env)->GetStaticMethodID(env, mActivityClass, "getManifestEnvironmentVariables", "()Z");
572  midGetNativeSurface = (*env)->GetStaticMethodID(env, mActivityClass, "getNativeSurface","()Landroid/view/Surface;");
573  midInitTouch = (*env)->GetStaticMethodID(env, mActivityClass, "initTouch", "()V");
574  midIsAndroidTV = (*env)->GetStaticMethodID(env, mActivityClass, "isAndroidTV","()Z");
575  midIsChromebook = (*env)->GetStaticMethodID(env, mActivityClass, "isChromebook", "()Z");
576  midIsDeXMode = (*env)->GetStaticMethodID(env, mActivityClass, "isDeXMode", "()Z");
577  midIsScreenKeyboardShown = (*env)->GetStaticMethodID(env, mActivityClass, "isScreenKeyboardShown","()Z");
578  midIsTablet = (*env)->GetStaticMethodID(env, mActivityClass, "isTablet", "()Z");
579  midManualBackButton = (*env)->GetStaticMethodID(env, mActivityClass, "manualBackButton", "()V");
580  midMinimizeWindow = (*env)->GetStaticMethodID(env, mActivityClass, "minimizeWindow","()V");
581  midOpenAPKExpansionInputStream = (*env)->GetStaticMethodID(env, mActivityClass, "openAPKExpansionInputStream", "(Ljava/lang/String;)Ljava/io/InputStream;");
582  midRequestPermission = (*env)->GetStaticMethodID(env, mActivityClass, "requestPermission", "(Ljava/lang/String;I)V");
583  midSendMessage = (*env)->GetStaticMethodID(env, mActivityClass, "sendMessage", "(II)Z");
584  midSetActivityTitle = (*env)->GetStaticMethodID(env, mActivityClass, "setActivityTitle","(Ljava/lang/String;)Z");
585  midSetCustomCursor = (*env)->GetStaticMethodID(env, mActivityClass, "setCustomCursor", "(I)Z");
586  midSetOrientation = (*env)->GetStaticMethodID(env, mActivityClass, "setOrientation","(IIZLjava/lang/String;)V");
587  midSetRelativeMouseEnabled = (*env)->GetStaticMethodID(env, mActivityClass, "setRelativeMouseEnabled", "(Z)Z");
588  midSetSurfaceViewFormat = (*env)->GetStaticMethodID(env, mActivityClass, "setSurfaceViewFormat","(I)V");
589  midSetSystemCursor = (*env)->GetStaticMethodID(env, mActivityClass, "setSystemCursor", "(I)Z");
590  midSetWindowStyle = (*env)->GetStaticMethodID(env, mActivityClass, "setWindowStyle","(Z)V");
591  midShouldMinimizeOnFocusLoss = (*env)->GetStaticMethodID(env, mActivityClass, "shouldMinimizeOnFocusLoss","()Z");
592  midShowTextInput = (*env)->GetStaticMethodID(env, mActivityClass, "showTextInput", "(IIII)Z");
593  midSupportsRelativeMouse = (*env)->GetStaticMethodID(env, mActivityClass, "supportsRelativeMouse", "()Z");
594 
595  if (!midClipboardGetText ||
596  !midClipboardHasText ||
597  !midClipboardSetText ||
598  !midCreateCustomCursor ||
599  !midGetContext ||
600  !midGetDisplayDPI ||
601  !midGetManifestEnvironmentVariables ||
602  !midGetNativeSurface ||
603  !midInitTouch ||
604  !midIsAndroidTV ||
605  !midIsChromebook ||
606  !midIsDeXMode ||
607  !midIsScreenKeyboardShown ||
608  !midIsTablet ||
609  !midManualBackButton ||
610  !midMinimizeWindow ||
611  !midOpenAPKExpansionInputStream ||
612  !midRequestPermission ||
613  !midSendMessage ||
614  !midSetActivityTitle ||
615  !midSetCustomCursor ||
616  !midSetOrientation ||
617  !midSetRelativeMouseEnabled ||
618  !midSetSurfaceViewFormat ||
619  !midSetSystemCursor ||
620  !midSetWindowStyle ||
621  !midShouldMinimizeOnFocusLoss ||
622  !midShowTextInput ||
623  !midSupportsRelativeMouse) {
624  __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLActivity.java?");
625  }
626 
627  checkJNIReady();
628 }
629 
630 /* Audio initialization -- called before SDL_main() to initialize JNI bindings */
631 JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cls)
632 {
633  __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "AUDIO nativeSetupJNI()");
634 
635  mAudioManagerClass = (jclass)((*env)->NewGlobalRef(env, cls));
636 
637  midAudioOpen = (*env)->GetStaticMethodID(env, mAudioManagerClass,
638  "audioOpen", "(IIII)[I");
639  midAudioWriteByteBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
640  "audioWriteByteBuffer", "([B)V");
641  midAudioWriteShortBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
642  "audioWriteShortBuffer", "([S)V");
643  midAudioWriteFloatBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
644  "audioWriteFloatBuffer", "([F)V");
645  midAudioClose = (*env)->GetStaticMethodID(env, mAudioManagerClass,
646  "audioClose", "()V");
647  midCaptureOpen = (*env)->GetStaticMethodID(env, mAudioManagerClass,
648  "captureOpen", "(IIII)[I");
649  midCaptureReadByteBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
650  "captureReadByteBuffer", "([BZ)I");
651  midCaptureReadShortBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
652  "captureReadShortBuffer", "([SZ)I");
653  midCaptureReadFloatBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
654  "captureReadFloatBuffer", "([FZ)I");
655  midCaptureClose = (*env)->GetStaticMethodID(env, mAudioManagerClass,
656  "captureClose", "()V");
657  midAudioSetThreadPriority = (*env)->GetStaticMethodID(env, mAudioManagerClass,
658  "audioSetThreadPriority", "(ZI)V");
659 
660  if (!midAudioOpen || !midAudioWriteByteBuffer || !midAudioWriteShortBuffer || !midAudioWriteFloatBuffer || !midAudioClose ||
661  !midCaptureOpen || !midCaptureReadByteBuffer || !midCaptureReadShortBuffer || !midCaptureReadFloatBuffer || !midCaptureClose || !midAudioSetThreadPriority) {
662  __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLAudioManager.java?");
663  }
664 
665  checkJNIReady();
666 }
667 
668 /* Controller initialization -- called before SDL_main() to initialize JNI bindings */
669 JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cls)
670 {
671  __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "CONTROLLER nativeSetupJNI()");
672 
673  mControllerManagerClass = (jclass)((*env)->NewGlobalRef(env, cls));
674 
675  midPollInputDevices = (*env)->GetStaticMethodID(env, mControllerManagerClass,
676  "pollInputDevices", "()V");
677  midPollHapticDevices = (*env)->GetStaticMethodID(env, mControllerManagerClass,
678  "pollHapticDevices", "()V");
679  midHapticRun = (*env)->GetStaticMethodID(env, mControllerManagerClass,
680  "hapticRun", "(IFI)V");
681  midHapticStop = (*env)->GetStaticMethodID(env, mControllerManagerClass,
682  "hapticStop", "(I)V");
683 
684  if (!midPollInputDevices || !midPollHapticDevices || !midHapticRun || !midHapticStop) {
685  __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLControllerManager.java?");
686  }
687 
688  checkJNIReady();
689 }
690 
691 /* SDL main function prototype */
692 typedef int (*SDL_main_func)(int argc, char *argv[]);
693 
694 /* Start up the SDL app */
695 JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeRunMain)(JNIEnv *env, jclass cls, jstring library, jstring function, jobject array)
696 {
697  int status = -1;
698  const char *library_file;
699  void *library_handle;
700 
701  __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeRunMain()");
702 
703  /* Save JNIEnv of SDLThread */
704  Android_JNI_SetEnv(env);
705 
706  library_file = (*env)->GetStringUTFChars(env, library, NULL);
707  library_handle = dlopen(library_file, RTLD_GLOBAL);
708 
709  if (!library_handle) {
710  /* When deploying android app bundle format uncompressed native libs may not extract from apk to filesystem.
711  In this case we should use lib name without path. https://bugzilla.libsdl.org/show_bug.cgi?id=4739 */
712  const char *library_name = SDL_strrchr(library_file, '/');
713  if (library_name && *library_name) {
714  library_name += 1;
715  library_handle = dlopen(library_name, RTLD_GLOBAL);
716  }
717  }
718 
719  if (library_handle) {
720  const char *function_name;
722 
723  function_name = (*env)->GetStringUTFChars(env, function, NULL);
724  SDL_main = (SDL_main_func)dlsym(library_handle, function_name);
725  if (SDL_main) {
726  int i;
727  int argc;
728  int len;
729  char **argv;
730  SDL_bool isstack;
731 
732  /* Prepare the arguments. */
733  len = (*env)->GetArrayLength(env, array);
734  argv = SDL_small_alloc(char *, 1 + len + 1, &isstack); /* !!! FIXME: check for NULL */
735  argc = 0;
736  /* Use the name "app_process" so PHYSFS_platformCalcBaseDir() works.
737  https://bitbucket.org/MartinFelis/love-android-sdl2/issue/23/release-build-crash-on-start
738  */
739  argv[argc++] = SDL_strdup("app_process");
740  for (i = 0; i < len; ++i) {
741  const char *utf;
742  char *arg = NULL;
743  jstring string = (*env)->GetObjectArrayElement(env, array, i);
744  if (string) {
745  utf = (*env)->GetStringUTFChars(env, string, 0);
746  if (utf) {
747  arg = SDL_strdup(utf);
748  (*env)->ReleaseStringUTFChars(env, string, utf);
749  }
750  (*env)->DeleteLocalRef(env, string);
751  }
752  if (!arg) {
753  arg = SDL_strdup("");
754  }
755  argv[argc++] = arg;
756  }
757  argv[argc] = NULL;
758 
759 
760  /* Run the application. */
761  status = SDL_main(argc, argv);
762 
763  /* Release the arguments. */
764  for (i = 0; i < argc; ++i) {
765  SDL_free(argv[i]);
766  }
767  SDL_small_free(argv, isstack);
768 
769  } else {
770  __android_log_print(ANDROID_LOG_ERROR, "SDL", "nativeRunMain(): Couldn't find function %s in library %s", function_name, library_file);
771  }
772  (*env)->ReleaseStringUTFChars(env, function, function_name);
773 
774  dlclose(library_handle);
775 
776  } else {
777  __android_log_print(ANDROID_LOG_ERROR, "SDL", "nativeRunMain(): Couldn't load library %s", library_file);
778  }
779  (*env)->ReleaseStringUTFChars(env, library, library_file);
780 
781  /* This is a Java thread, it doesn't need to be Detached from the JVM.
782  * Set to mThreadKey value to NULL not to call pthread_create destructor 'Android_JNI_ThreadDestroyed' */
783  Android_JNI_SetEnv(NULL);
784 
785  /* Do not issue an exit or the whole application will terminate instead of just the SDL thread */
786  /* exit(status); */
787 
788  return status;
789 }
790 
791 /* Drop file */
792 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeDropFile)(
793  JNIEnv *env, jclass jcls,
794  jstring filename)
795 {
796  const char *path = (*env)->GetStringUTFChars(env, filename, NULL);
798  (*env)->ReleaseStringUTFChars(env, filename, path);
800 }
801 
802 /* Lock / Unlock Mutex */
805 }
806 
809 }
810 
811 /* Lock the Mutex when the Activity is in its 'Running' state */
813  int pauseSignaled = 0;
814  int resumeSignaled = 0;
815 
816 retry:
817 
819 
820  pauseSignaled = SDL_SemValue(Android_PauseSem);
821  resumeSignaled = SDL_SemValue(Android_ResumeSem);
822 
823  if (pauseSignaled > resumeSignaled) {
825  SDL_Delay(50);
826  goto retry;
827  }
828 }
829 
830 /* Set screen resolution */
831 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetScreenResolution)(
832  JNIEnv *env, jclass jcls,
833  jint surfaceWidth, jint surfaceHeight,
834  jint deviceWidth, jint deviceHeight, jint format, jfloat rate)
835 {
837 
838  Android_SetScreenResolution(surfaceWidth, surfaceHeight, deviceWidth, deviceHeight, format, rate);
839 
841 }
842 
843 /* Resize */
844 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeResize)(
845  JNIEnv *env, jclass jcls)
846 {
848 
849  if (Android_Window)
850  {
852  }
853 
855 }
856 
857 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeOrientationChanged)(
858  JNIEnv *env, jclass jcls,
859  jint orientation)
860 {
862 
863  displayOrientation = (SDL_DisplayOrientation)orientation;
864 
865  if (Android_Window)
866  {
867  SDL_VideoDisplay *display = SDL_GetDisplay(0);
868  SDL_SendDisplayEvent(display, SDL_DISPLAYEVENT_ORIENTATION, orientation);
869  }
870 
872 }
873 
874 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeAddTouch)(
875  JNIEnv* env, jclass cls,
876  jint touchId, jstring name)
877 {
878  const char *utfname = (*env)->GetStringUTFChars(env, name, NULL);
879 
880  SDL_AddTouch((SDL_TouchID) touchId, SDL_TOUCH_DEVICE_DIRECT, utfname);
881 
882  (*env)->ReleaseStringUTFChars(env, name, utfname);
883 }
884 
885 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePermissionResult)(
886  JNIEnv* env, jclass cls,
887  jint requestCode, jboolean result)
888 {
889  bPermissionRequestResult = result;
890  SDL_AtomicSet(&bPermissionRequestPending, SDL_FALSE);
891 }
892 
893 /* Paddown */
894 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)(
895  JNIEnv *env, jclass jcls,
896  jint device_id, jint keycode)
897 {
898  return Android_OnPadDown(device_id, keycode);
899 }
900 
901 /* Padup */
902 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)(
903  JNIEnv *env, jclass jcls,
904  jint device_id, jint keycode)
905 {
906  return Android_OnPadUp(device_id, keycode);
907 }
908 
909 /* Joy */
910 JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)(
911  JNIEnv *env, jclass jcls,
912  jint device_id, jint axis, jfloat value)
913 {
914  Android_OnJoy(device_id, axis, value);
915 }
916 
917 /* POV Hat */
918 JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
919  JNIEnv *env, jclass jcls,
920  jint device_id, jint hat_id, jint x, jint y)
921 {
922  Android_OnHat(device_id, hat_id, x, y);
923 }
924 
925 
926 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
927  JNIEnv *env, jclass jcls,
928  jint device_id, jstring device_name, jstring device_desc,
929  jint vendor_id, jint product_id, jboolean is_accelerometer,
930  jint button_mask, jint naxes, jint nhats, jint nballs)
931 {
932  int retval;
933  const char *name = (*env)->GetStringUTFChars(env, device_name, NULL);
934  const char *desc = (*env)->GetStringUTFChars(env, device_desc, NULL);
935 
936  retval = Android_AddJoystick(device_id, name, desc, vendor_id, product_id, is_accelerometer ? SDL_TRUE : SDL_FALSE, button_mask, naxes, nhats, nballs);
937 
938  (*env)->ReleaseStringUTFChars(env, device_name, name);
939  (*env)->ReleaseStringUTFChars(env, device_desc, desc);
940 
941  return retval;
942 }
943 
944 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)(
945  JNIEnv *env, jclass jcls,
946  jint device_id)
947 {
948  return Android_RemoveJoystick(device_id);
949 }
950 
951 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic)(
952  JNIEnv *env, jclass jcls, jint device_id, jstring device_name)
953 {
954  int retval;
955  const char *name = (*env)->GetStringUTFChars(env, device_name, NULL);
956 
957  retval = Android_AddHaptic(device_id, name);
958 
959  (*env)->ReleaseStringUTFChars(env, device_name, name);
960 
961  return retval;
962 }
963 
964 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)(
965  JNIEnv *env, jclass jcls, jint device_id)
966 {
967  return Android_RemoveHaptic(device_id);
968 }
969 
970 /* Called from surfaceCreated() */
971 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceCreated)(JNIEnv *env, jclass jcls)
972 {
974 
975  if (Android_Window)
976  {
978 
979  data->native_window = Android_JNI_GetNativeWindow();
980  if (data->native_window == NULL) {
981  SDL_SetError("Could not fetch native window from UI thread");
982  }
983  }
984 
986 }
987 
988 /* Called from surfaceChanged() */
989 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(JNIEnv *env, jclass jcls)
990 {
992 
993  if (Android_Window)
994  {
997 
998  /* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
999  if (data->egl_surface == EGL_NO_SURFACE) {
1000  data->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType) data->native_window);
1001  }
1002 
1003  /* GL Context handling is done in the event loop because this function is run from the Java thread */
1004  }
1005 
1007 }
1008 
1009 /* Called from surfaceDestroyed() */
1010 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)(JNIEnv *env, jclass jcls)
1011 {
1012  int nb_attempt = 50;
1013 
1014 retry:
1015 
1017 
1018  if (Android_Window)
1019  {
1022 
1023  /* Wait for Main thread being paused and context un-activated to release 'egl_surface' */
1024  if (! data->backup_done) {
1025  nb_attempt -= 1;
1026  if (nb_attempt == 0) {
1027  SDL_SetError("Try to release egl_surface with context probably still active");
1028  } else {
1030  SDL_Delay(10);
1031  goto retry;
1032  }
1033  }
1034 
1035  if (data->egl_surface != EGL_NO_SURFACE) {
1036  SDL_EGL_DestroySurface(_this, data->egl_surface);
1037  data->egl_surface = EGL_NO_SURFACE;
1038  }
1039 
1040  if (data->native_window) {
1041  ANativeWindow_release(data->native_window);
1042  data->native_window = NULL;
1043  }
1044 
1045  /* GL Context handling is done in the event loop because this function is run from the Java thread */
1046  }
1047 
1049 }
1050 
1051 /* Keydown */
1052 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyDown)(
1053  JNIEnv *env, jclass jcls,
1054  jint keycode)
1055 {
1056  Android_OnKeyDown(keycode);
1057 }
1058 
1059 /* Keyup */
1060 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyUp)(
1061  JNIEnv *env, jclass jcls,
1062  jint keycode)
1063 {
1064  Android_OnKeyUp(keycode);
1065 }
1066 
1067 /* Virtual keyboard return key might stop text input */
1068 JNIEXPORT jboolean JNICALL SDL_JAVA_INTERFACE(onNativeSoftReturnKey)(
1069  JNIEnv *env, jclass jcls)
1070 {
1073  return JNI_TRUE;
1074  }
1075  return JNI_FALSE;
1076 }
1077 
1078 /* Keyboard Focus Lost */
1079 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyboardFocusLost)(
1080  JNIEnv *env, jclass jcls)
1081 {
1082  /* Calling SDL_StopTextInput will take care of hiding the keyboard and cleaning up the DummyText widget */
1084 }
1085 
1086 
1087 /* Touch */
1088 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeTouch)(
1089  JNIEnv *env, jclass jcls,
1090  jint touch_device_id_in, jint pointer_finger_id_in,
1091  jint action, jfloat x, jfloat y, jfloat p)
1092 {
1094 
1095  Android_OnTouch(Android_Window, touch_device_id_in, pointer_finger_id_in, action, x, y, p);
1096 
1098 }
1099 
1100 /* Mouse */
1101 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeMouse)(
1102  JNIEnv *env, jclass jcls,
1103  jint button, jint action, jfloat x, jfloat y, jboolean relative)
1104 {
1106 
1107  Android_OnMouse(Android_Window, button, action, x, y, relative);
1108 
1110 }
1111 
1112 /* Accelerometer */
1113 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeAccel)(
1114  JNIEnv *env, jclass jcls,
1115  jfloat x, jfloat y, jfloat z)
1116 {
1117  fLastAccelerometer[0] = x;
1118  fLastAccelerometer[1] = y;
1119  fLastAccelerometer[2] = z;
1120  bHasNewData = SDL_TRUE;
1121 }
1122 
1123 /* Clipboard */
1124 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeClipboardChanged)(
1125  JNIEnv *env, jclass jcls)
1126 {
1128 }
1129 
1130 /* Low memory */
1131 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeLowMemory)(
1132  JNIEnv *env, jclass cls)
1133 {
1135 }
1136 
1137 /* Send Quit event to "SDLThread" thread */
1138 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSendQuit)(
1139  JNIEnv *env, jclass cls)
1140 {
1141  /* Discard previous events. The user should have handled state storage
1142  * in SDL_APP_WILLENTERBACKGROUND. After nativeSendQuit() is called, no
1143  * events other than SDL_QUIT and SDL_APP_TERMINATING should fire */
1145  /* Inject a SDL_QUIT event */
1146  SDL_SendQuit();
1148  /* Robustness: clear any pending Pause */
1149  while (SDL_SemTryWait(Android_PauseSem) == 0) {
1150  /* empty */
1151  }
1152  /* Resume the event loop so that the app can catch SDL_QUIT which
1153  * should now be the top event in the event queue. */
1155 }
1156 
1157 /* Activity ends */
1158 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeQuit)(
1159  JNIEnv *env, jclass cls)
1160 {
1161  const char *str;
1162 
1163  if (Android_ActivityMutex) {
1166  }
1167 
1168  if (Android_PauseSem) {
1171  }
1172 
1173  if (Android_ResumeSem) {
1176  }
1177 
1178  str = SDL_GetError();
1179  if (str && str[0]) {
1180  __android_log_print(ANDROID_LOG_ERROR, "SDL", "SDLActivity thread ends (error=%s)", str);
1181  } else {
1182  __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDLActivity thread ends");
1183  }
1184 }
1185 
1186 /* Pause */
1187 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePause)(
1188  JNIEnv *env, jclass cls)
1189 {
1190  __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativePause()");
1191 
1192  /* Signal the pause semaphore so the event loop knows to pause and (optionally) block itself.
1193  * Sometimes 2 pauses can be queued (eg pause/resume/pause), so it's always increased. */
1195 }
1196 
1197 /* Resume */
1198 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeResume)(
1199  JNIEnv *env, jclass cls)
1200 {
1201  __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeResume()");
1202 
1203  /* Signal the resume semaphore so the event loop knows to resume and restore the GL Context
1204  * We can't restore the GL Context here because it needs to be done on the SDL main thread
1205  * and this function will be called from the Java thread instead.
1206  */
1208 }
1209 
1210 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeFocusChanged)(
1211  JNIEnv *env, jclass cls, jboolean hasFocus)
1212 {
1214 
1215  if (Android_Window) {
1216  __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeFocusChanged()");
1218  }
1219 
1221 }
1222 
1223 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeCommitText)(
1224  JNIEnv *env, jclass cls,
1225  jstring text, jint newCursorPosition)
1226 {
1227  const char *utftext = (*env)->GetStringUTFChars(env, text, NULL);
1228 
1229  SDL_SendKeyboardText(utftext);
1230 
1231  (*env)->ReleaseStringUTFChars(env, text, utftext);
1232 }
1233 
1234 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeGenerateScancodeForUnichar)(
1235  JNIEnv *env, jclass cls,
1236  jchar chUnicode)
1237 {
1239  uint16_t mod = 0;
1240 
1241  /* We do not care about bigger than 127. */
1242  if (chUnicode < 127) {
1243  AndroidKeyInfo info = unicharToAndroidKeyInfoTable[chUnicode];
1244  code = info.code;
1245  mod = info.mod;
1246  }
1247 
1248  if (mod & KMOD_SHIFT) {
1249  /* If character uses shift, press shift down */
1251  }
1252 
1253  /* send a keydown and keyup even for the character */
1256 
1257  if (mod & KMOD_SHIFT) {
1258  /* If character uses shift, press shift back up */
1260  }
1261 }
1262 
1263 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeSetComposingText)(
1264  JNIEnv *env, jclass cls,
1265  jstring text, jint newCursorPosition)
1266 {
1267  const char *utftext = (*env)->GetStringUTFChars(env, text, NULL);
1268 
1269  SDL_SendEditingText(utftext, 0, 0);
1270 
1271  (*env)->ReleaseStringUTFChars(env, text, utftext);
1272 }
1273 
1274 JNIEXPORT jstring JNICALL SDL_JAVA_INTERFACE(nativeGetHint)(
1275  JNIEnv *env, jclass cls,
1276  jstring name)
1277 {
1278  const char *utfname = (*env)->GetStringUTFChars(env, name, NULL);
1279  const char *hint = SDL_GetHint(utfname);
1280 
1281  jstring result = (*env)->NewStringUTF(env, hint);
1282  (*env)->ReleaseStringUTFChars(env, name, utfname);
1283 
1284  return result;
1285 }
1286 
1287 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetenv)(
1288  JNIEnv *env, jclass cls,
1289  jstring name, jstring value)
1290 {
1291  const char *utfname = (*env)->GetStringUTFChars(env, name, NULL);
1292  const char *utfvalue = (*env)->GetStringUTFChars(env, value, NULL);
1293 
1294  SDL_setenv(utfname, utfvalue, 1);
1295 
1296  (*env)->ReleaseStringUTFChars(env, name, utfname);
1297  (*env)->ReleaseStringUTFChars(env, value, utfvalue);
1298 
1299 }
1300 
1301 /*******************************************************************************
1302  Functions called by SDL into Java
1303 *******************************************************************************/
1304 
1305 static SDL_atomic_t s_active;
1306 struct LocalReferenceHolder
1307 {
1308  JNIEnv *m_env;
1309  const char *m_func;
1310 };
1311 
1312 static struct LocalReferenceHolder LocalReferenceHolder_Setup(const char *func)
1313 {
1314  struct LocalReferenceHolder refholder;
1315  refholder.m_env = NULL;
1316  refholder.m_func = func;
1317 #ifdef DEBUG_JNI
1318  SDL_Log("Entering function %s", func);
1319 #endif
1320  return refholder;
1321 }
1322 
1323 static SDL_bool LocalReferenceHolder_Init(struct LocalReferenceHolder *refholder, JNIEnv *env)
1324 {
1325  const int capacity = 16;
1326  if ((*env)->PushLocalFrame(env, capacity) < 0) {
1327  SDL_SetError("Failed to allocate enough JVM local references");
1328  return SDL_FALSE;
1329  }
1330  SDL_AtomicIncRef(&s_active);
1331  refholder->m_env = env;
1332  return SDL_TRUE;
1333 }
1334 
1335 static void LocalReferenceHolder_Cleanup(struct LocalReferenceHolder *refholder)
1336 {
1337 #ifdef DEBUG_JNI
1338  SDL_Log("Leaving function %s", refholder->m_func);
1339 #endif
1340  if (refholder->m_env) {
1341  JNIEnv *env = refholder->m_env;
1342  (*env)->PopLocalFrame(env, NULL);
1343  SDL_AtomicDecRef(&s_active);
1344  }
1345 }
1346 
1347 ANativeWindow* Android_JNI_GetNativeWindow(void)
1348 {
1349  ANativeWindow *anw = NULL;
1350  jobject s;
1351  JNIEnv *env = Android_JNI_GetEnv();
1352 
1353  s = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetNativeSurface);
1354  if (s) {
1355  anw = ANativeWindow_fromSurface(env, s);
1356  (*env)->DeleteLocalRef(env, s);
1357  }
1358 
1359  return anw;
1360 }
1361 
1363 {
1364  JNIEnv *env = Android_JNI_GetEnv();
1365  int new_format = 0;
1366 
1367  /* Format from android/native_window.h,
1368  * convert to temporary arbitrary values,
1369  * then to java PixelFormat */
1370  if (format == WINDOW_FORMAT_RGBA_8888) {
1371  new_format = 1;
1372  } else if (format == WINDOW_FORMAT_RGBX_8888) {
1373  new_format = 2;
1374  } else if (format == WINDOW_FORMAT_RGB_565) {
1375  /* Default */
1376  new_format = 0;
1377  }
1378 
1379  (*env)->CallStaticVoidMethod(env, mActivityClass, midSetSurfaceViewFormat, new_format);
1380 }
1381 
1382 void Android_JNI_SetActivityTitle(const char *title)
1383 {
1384  JNIEnv *env = Android_JNI_GetEnv();
1385 
1386  jstring jtitle = (*env)->NewStringUTF(env, title);
1387  (*env)->CallStaticBooleanMethod(env, mActivityClass, midSetActivityTitle, jtitle);
1388  (*env)->DeleteLocalRef(env, jtitle);
1389 }
1390 
1391 void Android_JNI_SetWindowStyle(SDL_bool fullscreen)
1392 {
1393  JNIEnv *env = Android_JNI_GetEnv();
1394  (*env)->CallStaticVoidMethod(env, mActivityClass, midSetWindowStyle, fullscreen ? 1 : 0);
1395 }
1396 
1397 void Android_JNI_SetOrientation(int w, int h, int resizable, const char *hint)
1398 {
1399  JNIEnv *env = Android_JNI_GetEnv();
1400 
1401  jstring jhint = (*env)->NewStringUTF(env, (hint ? hint : ""));
1402  (*env)->CallStaticVoidMethod(env, mActivityClass, midSetOrientation, w, h, (resizable? 1 : 0), jhint);
1403  (*env)->DeleteLocalRef(env, jhint);
1404 }
1405 
1407 {
1408  JNIEnv *env = Android_JNI_GetEnv();
1409  (*env)->CallStaticVoidMethod(env, mActivityClass, midMinimizeWindow);
1410 }
1411 
1413 {
1414  JNIEnv *env = Android_JNI_GetEnv();
1415  return (*env)->CallStaticBooleanMethod(env, mActivityClass, midShouldMinimizeOnFocusLoss);
1416 }
1417 
1419 {
1420  int i;
1422 
1423  if (bHasNewData) {
1424  for (i = 0; i < 3; ++i) {
1425  values[i] = fLastAccelerometer[i];
1426  }
1427  bHasNewData = SDL_FALSE;
1428  retval = SDL_TRUE;
1429  }
1430 
1431  return retval;
1432 }
1433 
1434 /*
1435  * Audio support
1436  */
1437 static int audioBufferFormat = 0;
1438 static jobject audioBuffer = NULL;
1439 static void *audioBufferPinned = NULL;
1440 static int captureBufferFormat = 0;
1441 static jobject captureBuffer = NULL;
1442 
1443 int Android_JNI_OpenAudioDevice(int iscapture, SDL_AudioSpec *spec)
1444 {
1445  int audioformat;
1446  jobject jbufobj = NULL;
1447  jobject result;
1448  int *resultElements;
1449  jboolean isCopy;
1450 
1451  JNIEnv *env = Android_JNI_GetEnv();
1452 
1453  switch (spec->format) {
1454  case AUDIO_U8:
1455  audioformat = ENCODING_PCM_8BIT;
1456  break;
1457  case AUDIO_S16:
1458  audioformat = ENCODING_PCM_16BIT;
1459  break;
1460  case AUDIO_F32:
1461  audioformat = ENCODING_PCM_FLOAT;
1462  break;
1463  default:
1464  return SDL_SetError("Unsupported audio format: 0x%x", spec->format);
1465  }
1466 
1467  if (iscapture) {
1468  __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for capture");
1469  result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midCaptureOpen, spec->freq, audioformat, spec->channels, spec->samples);
1470  } else {
1471  __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for output");
1472  result = (*env)->CallStaticObjectMethod(env, mAudioManagerClass, midAudioOpen, spec->freq, audioformat, spec->channels, spec->samples);
1473  }
1474  if (result == NULL) {
1475  /* Error during audio initialization, error printed from Java */
1476  return SDL_SetError("Java-side initialization failed");
1477  }
1478 
1479  if ((*env)->GetArrayLength(env, (jintArray)result) != 4) {
1480  return SDL_SetError("Unexpected results from Java, expected 4, got %d", (*env)->GetArrayLength(env, (jintArray)result));
1481  }
1482  isCopy = JNI_FALSE;
1483  resultElements = (*env)->GetIntArrayElements(env, (jintArray)result, &isCopy);
1484  spec->freq = resultElements[0];
1485  audioformat = resultElements[1];
1486  switch (audioformat) {
1487  case ENCODING_PCM_8BIT:
1488  spec->format = AUDIO_U8;
1489  break;
1490  case ENCODING_PCM_16BIT:
1491  spec->format = AUDIO_S16;
1492  break;
1493  case ENCODING_PCM_FLOAT:
1494  spec->format = AUDIO_F32;
1495  break;
1496  default:
1497  return SDL_SetError("Unexpected audio format from Java: %d\n", audioformat);
1498  }
1499  spec->channels = resultElements[2];
1500  spec->samples = resultElements[3];
1501  (*env)->ReleaseIntArrayElements(env, (jintArray)result, resultElements, JNI_ABORT);
1502  (*env)->DeleteLocalRef(env, result);
1503 
1504  /* Allocating the audio buffer from the Java side and passing it as the return value for audioInit no longer works on
1505  * Android >= 4.2 due to a "stale global reference" error. So now we allocate this buffer directly from this side. */
1506  switch (audioformat) {
1507  case ENCODING_PCM_8BIT:
1508  {
1509  jbyteArray audioBufferLocal = (*env)->NewByteArray(env, spec->samples * spec->channels);
1510  if (audioBufferLocal) {
1511  jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
1512  (*env)->DeleteLocalRef(env, audioBufferLocal);
1513  }
1514  }
1515  break;
1516  case ENCODING_PCM_16BIT:
1517  {
1518  jshortArray audioBufferLocal = (*env)->NewShortArray(env, spec->samples * spec->channels);
1519  if (audioBufferLocal) {
1520  jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
1521  (*env)->DeleteLocalRef(env, audioBufferLocal);
1522  }
1523  }
1524  break;
1525  case ENCODING_PCM_FLOAT:
1526  {
1527  jfloatArray audioBufferLocal = (*env)->NewFloatArray(env, spec->samples * spec->channels);
1528  if (audioBufferLocal) {
1529  jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
1530  (*env)->DeleteLocalRef(env, audioBufferLocal);
1531  }
1532  }
1533  break;
1534  default:
1535  return SDL_SetError("Unexpected audio format from Java: %d\n", audioformat);
1536  }
1537 
1538  if (jbufobj == NULL) {
1539  __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: could not allocate an audio buffer");
1540  return SDL_OutOfMemory();
1541  }
1542 
1543  if (iscapture) {
1544  captureBufferFormat = audioformat;
1545  captureBuffer = jbufobj;
1546  } else {
1547  audioBufferFormat = audioformat;
1548  audioBuffer = jbufobj;
1549  }
1550 
1551  if (!iscapture) {
1552  isCopy = JNI_FALSE;
1553 
1554  switch (audioformat) {
1555  case ENCODING_PCM_8BIT:
1556  audioBufferPinned = (*env)->GetByteArrayElements(env, (jbyteArray)audioBuffer, &isCopy);
1557  break;
1558  case ENCODING_PCM_16BIT:
1559  audioBufferPinned = (*env)->GetShortArrayElements(env, (jshortArray)audioBuffer, &isCopy);
1560  break;
1561  case ENCODING_PCM_FLOAT:
1562  audioBufferPinned = (*env)->GetFloatArrayElements(env, (jfloatArray)audioBuffer, &isCopy);
1563  break;
1564  default:
1565  return SDL_SetError("Unexpected audio format from Java: %d\n", audioformat);
1566  }
1567  }
1568  return 0;
1569 }
1570 
1572 {
1573  return displayOrientation;
1574 }
1575 
1576 int Android_JNI_GetDisplayDPI(float *ddpi, float *xdpi, float *ydpi)
1577 {
1578  JNIEnv *env = Android_JNI_GetEnv();
1579 
1580  jobject jDisplayObj = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetDisplayDPI);
1581  jclass jDisplayClass = (*env)->GetObjectClass(env, jDisplayObj);
1582 
1583  jfieldID fidXdpi = (*env)->GetFieldID(env, jDisplayClass, "xdpi", "F");
1584  jfieldID fidYdpi = (*env)->GetFieldID(env, jDisplayClass, "ydpi", "F");
1585  jfieldID fidDdpi = (*env)->GetFieldID(env, jDisplayClass, "densityDpi", "I");
1586 
1587  float nativeXdpi = (*env)->GetFloatField(env, jDisplayObj, fidXdpi);
1588  float nativeYdpi = (*env)->GetFloatField(env, jDisplayObj, fidYdpi);
1589  int nativeDdpi = (*env)->GetIntField(env, jDisplayObj, fidDdpi);
1590 
1591 
1592  (*env)->DeleteLocalRef(env, jDisplayObj);
1593  (*env)->DeleteLocalRef(env, jDisplayClass);
1594 
1595  if (ddpi) {
1596  *ddpi = (float)nativeDdpi;
1597  }
1598  if (xdpi) {
1599  *xdpi = nativeXdpi;
1600  }
1601  if (ydpi) {
1602  *ydpi = nativeYdpi;
1603  }
1604 
1605  return 0;
1606 }
1607 
1608 void * Android_JNI_GetAudioBuffer(void)
1609 {
1610  return audioBufferPinned;
1611 }
1612 
1614 {
1615  JNIEnv *env = Android_JNI_GetEnv();
1616 
1617  switch (audioBufferFormat) {
1618  case ENCODING_PCM_8BIT:
1619  (*env)->ReleaseByteArrayElements(env, (jbyteArray)audioBuffer, (jbyte *)audioBufferPinned, JNI_COMMIT);
1620  (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midAudioWriteByteBuffer, (jbyteArray)audioBuffer);
1621  break;
1622  case ENCODING_PCM_16BIT:
1623  (*env)->ReleaseShortArrayElements(env, (jshortArray)audioBuffer, (jshort *)audioBufferPinned, JNI_COMMIT);
1624  (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midAudioWriteShortBuffer, (jshortArray)audioBuffer);
1625  break;
1626  case ENCODING_PCM_FLOAT:
1627  (*env)->ReleaseFloatArrayElements(env, (jfloatArray)audioBuffer, (jfloat *)audioBufferPinned, JNI_COMMIT);
1628  (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midAudioWriteFloatBuffer, (jfloatArray)audioBuffer);
1629  break;
1630  default:
1631  __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: unhandled audio buffer format");
1632  break;
1633  }
1634 
1635  /* JNI_COMMIT means the changes are committed to the VM but the buffer remains pinned */
1636 }
1637 
1638 int Android_JNI_CaptureAudioBuffer(void *buffer, int buflen)
1639 {
1640  JNIEnv *env = Android_JNI_GetEnv();
1641  jboolean isCopy = JNI_FALSE;
1642  jint br = -1;
1643 
1644  switch (captureBufferFormat) {
1645  case ENCODING_PCM_8BIT:
1646  SDL_assert((*env)->GetArrayLength(env, (jshortArray)captureBuffer) == buflen);
1647  br = (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_TRUE);
1648  if (br > 0) {
1649  jbyte *ptr = (*env)->GetByteArrayElements(env, (jbyteArray)captureBuffer, &isCopy);
1650  SDL_memcpy(buffer, ptr, br);
1651  (*env)->ReleaseByteArrayElements(env, (jbyteArray)captureBuffer, ptr, JNI_ABORT);
1652  }
1653  break;
1654  case ENCODING_PCM_16BIT:
1655  SDL_assert((*env)->GetArrayLength(env, (jshortArray)captureBuffer) == (buflen / sizeof(Sint16)));
1656  br = (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_TRUE);
1657  if (br > 0) {
1658  jshort *ptr = (*env)->GetShortArrayElements(env, (jshortArray)captureBuffer, &isCopy);
1659  br *= sizeof(Sint16);
1660  SDL_memcpy(buffer, ptr, br);
1661  (*env)->ReleaseShortArrayElements(env, (jshortArray)captureBuffer, ptr, JNI_ABORT);
1662  }
1663  break;
1664  case ENCODING_PCM_FLOAT:
1665  SDL_assert((*env)->GetArrayLength(env, (jfloatArray)captureBuffer) == (buflen / sizeof(float)));
1666  br = (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadFloatBuffer, (jfloatArray)captureBuffer, JNI_TRUE);
1667  if (br > 0) {
1668  jfloat *ptr = (*env)->GetFloatArrayElements(env, (jfloatArray)captureBuffer, &isCopy);
1669  br *= sizeof(float);
1670  SDL_memcpy(buffer, ptr, br);
1671  (*env)->ReleaseFloatArrayElements(env, (jfloatArray)captureBuffer, ptr, JNI_ABORT);
1672  }
1673  break;
1674  default:
1675  __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: unhandled capture buffer format");
1676  break;
1677  }
1678  return br;
1679 }
1680 
1682 {
1683  JNIEnv *env = Android_JNI_GetEnv();
1684 #if 0 /* !!! FIXME: this needs API 23, or it'll do blocking reads and never end. */
1685  switch (captureBufferFormat) {
1686  case ENCODING_PCM_8BIT:
1687  {
1688  const jint len = (*env)->GetArrayLength(env, (jbyteArray)captureBuffer);
1689  while ((*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_FALSE) == len) { /* spin */ }
1690  }
1691  break;
1692  case ENCODING_PCM_16BIT:
1693  {
1694  const jint len = (*env)->GetArrayLength(env, (jshortArray)captureBuffer);
1695  while ((*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_FALSE) == len) { /* spin */ }
1696  }
1697  break;
1698  case ENCODING_PCM_FLOAT:
1699  {
1700  const jint len = (*env)->GetArrayLength(env, (jfloatArray)captureBuffer);
1701  while ((*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadFloatBuffer, (jfloatArray)captureBuffer, JNI_FALSE) == len) { /* spin */ }
1702  }
1703  break;
1704  default:
1705  __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: flushing unhandled capture buffer format");
1706  break;
1707  }
1708 #else
1709  switch (captureBufferFormat) {
1710  case ENCODING_PCM_8BIT:
1711  (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_FALSE);
1712  break;
1713  case ENCODING_PCM_16BIT:
1714  (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_FALSE);
1715  break;
1716  case ENCODING_PCM_FLOAT:
1717  (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadFloatBuffer, (jfloatArray)captureBuffer, JNI_FALSE);
1718  break;
1719  default:
1720  __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: flushing unhandled capture buffer format");
1721  break;
1722  }
1723 #endif
1724 }
1725 
1726 void Android_JNI_CloseAudioDevice(const int iscapture)
1727 {
1728  JNIEnv *env = Android_JNI_GetEnv();
1729 
1730  if (iscapture) {
1731  (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midCaptureClose);
1732  if (captureBuffer) {
1733  (*env)->DeleteGlobalRef(env, captureBuffer);
1734  captureBuffer = NULL;
1735  }
1736  } else {
1737  (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midAudioClose);
1738  if (audioBuffer) {
1739  (*env)->DeleteGlobalRef(env, audioBuffer);
1740  audioBuffer = NULL;
1741  audioBufferPinned = NULL;
1742  }
1743  }
1744 }
1745 
1746 void Android_JNI_AudioSetThreadPriority(int iscapture, int device_id)
1747 {
1748  JNIEnv *env = Android_JNI_GetEnv();
1749  (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midAudioSetThreadPriority, iscapture, device_id);
1750 }
1751 
1752 /* Test for an exception and call SDL_SetError with its detail if one occurs */
1753 /* If the parameter silent is truthy then SDL_SetError() will not be called. */
1754 static SDL_bool Android_JNI_ExceptionOccurred(SDL_bool silent)
1755 {
1756  JNIEnv *env = Android_JNI_GetEnv();
1757  jthrowable exception;
1758 
1759  /* Detect mismatch LocalReferenceHolder_Init/Cleanup */
1760  SDL_assert(SDL_AtomicGet(&s_active) > 0);
1761 
1762  exception = (*env)->ExceptionOccurred(env);
1763  if (exception != NULL) {
1764  jmethodID mid;
1765 
1766  /* Until this happens most JNI operations have undefined behaviour */
1767  (*env)->ExceptionClear(env);
1768 
1769  if (!silent) {
1770  jclass exceptionClass = (*env)->GetObjectClass(env, exception);
1771  jclass classClass = (*env)->FindClass(env, "java/lang/Class");
1772  jstring exceptionName;
1773  const char *exceptionNameUTF8;
1774  jstring exceptionMessage;
1775 
1776  mid = (*env)->GetMethodID(env, classClass, "getName", "()Ljava/lang/String;");
1777  exceptionName = (jstring)(*env)->CallObjectMethod(env, exceptionClass, mid);
1778  exceptionNameUTF8 = (*env)->GetStringUTFChars(env, exceptionName, 0);
1779 
1780  mid = (*env)->GetMethodID(env, exceptionClass, "getMessage", "()Ljava/lang/String;");
1781  exceptionMessage = (jstring)(*env)->CallObjectMethod(env, exception, mid);
1782 
1783  if (exceptionMessage != NULL) {
1784  const char *exceptionMessageUTF8 = (*env)->GetStringUTFChars(env, exceptionMessage, 0);
1785  SDL_SetError("%s: %s", exceptionNameUTF8, exceptionMessageUTF8);
1786  (*env)->ReleaseStringUTFChars(env, exceptionMessage, exceptionMessageUTF8);
1787  } else {
1788  SDL_SetError("%s", exceptionNameUTF8);
1789  }
1790 
1791  (*env)->ReleaseStringUTFChars(env, exceptionName, exceptionNameUTF8);
1792  }
1793 
1794  return SDL_TRUE;
1795  }
1796 
1797  return SDL_FALSE;
1798 }
1799 
1800 static int Internal_Android_JNI_FileOpen(SDL_RWops *ctx)
1801 {
1802  struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
1803 
1804  int result = 0;
1805 
1806  jmethodID mid;
1807  jobject context;
1808  jobject assetManager;
1809  jobject inputStream;
1810  jclass channels;
1811  jobject readableByteChannel;
1812  jstring fileNameJString;
1813  jobject fd;
1814  jclass fdCls;
1815  jfieldID descriptor;
1816 
1817  JNIEnv *env = Android_JNI_GetEnv();
1818  if (!LocalReferenceHolder_Init(&refs, env)) {
1819  goto failure;
1820  }
1821 
1822  fileNameJString = (jstring)ctx->hidden.androidio.fileNameRef;
1823  ctx->hidden.androidio.position = 0;
1824 
1825  /* context = SDLActivity.getContext(); */
1826  context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
1827 
1828  /* assetManager = context.getAssets(); */
1829  mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
1830  "getAssets", "()Landroid/content/res/AssetManager;");
1831  assetManager = (*env)->CallObjectMethod(env, context, mid);
1832 
1833  /* First let's try opening the file to obtain an AssetFileDescriptor.
1834  * This method reads the files directly from the APKs using standard *nix calls
1835  */
1836  mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, assetManager), "openFd", "(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;");
1837  inputStream = (*env)->CallObjectMethod(env, assetManager, mid, fileNameJString);
1838  if (Android_JNI_ExceptionOccurred(SDL_TRUE)) {
1839  goto fallback;
1840  }
1841 
1842  mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, inputStream), "getStartOffset", "()J");
1843  ctx->hidden.androidio.offset = (long)(*env)->CallLongMethod(env, inputStream, mid);
1844  if (Android_JNI_ExceptionOccurred(SDL_TRUE)) {
1845  goto fallback;
1846  }
1847 
1848  mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, inputStream), "getDeclaredLength", "()J");
1849  ctx->hidden.androidio.size = (long)(*env)->CallLongMethod(env, inputStream, mid);
1850  if (Android_JNI_ExceptionOccurred(SDL_TRUE)) {
1851  goto fallback;
1852  }
1853 
1854  mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, inputStream), "getFileDescriptor", "()Ljava/io/FileDescriptor;");
1855  fd = (*env)->CallObjectMethod(env, inputStream, mid);
1856  fdCls = (*env)->GetObjectClass(env, fd);
1857  descriptor = (*env)->GetFieldID(env, fdCls, "descriptor", "I");
1858  ctx->hidden.androidio.fd = (*env)->GetIntField(env, fd, descriptor);
1859  ctx->hidden.androidio.assetFileDescriptorRef = (*env)->NewGlobalRef(env, inputStream);
1860 
1861  /* Seek to the correct offset in the file. */
1862  lseek(ctx->hidden.androidio.fd, (off_t)ctx->hidden.androidio.offset, SEEK_SET);
1863 
1864  if (0) {
1865 fallback:
1866  /* Disabled log message because of spam on the Nexus 7 */
1867  /* __android_log_print(ANDROID_LOG_DEBUG, "SDL", "Falling back to legacy InputStream method for opening file"); */
1868 
1869  /* Try the old method using InputStream */
1870  ctx->hidden.androidio.assetFileDescriptorRef = NULL;
1871 
1872  /* inputStream = assetManager.open(<filename>); */
1873  mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, assetManager),
1874  "open", "(Ljava/lang/String;I)Ljava/io/InputStream;");
1875  inputStream = (*env)->CallObjectMethod(env, assetManager, mid, fileNameJString, 1 /* ACCESS_RANDOM */);
1876  if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
1877  /* Try fallback to APK expansion files */
1878  inputStream = (*env)->CallStaticObjectMethod(env, mActivityClass, midOpenAPKExpansionInputStream, fileNameJString);
1879 
1880  /* Exception is checked first because it always needs to be cleared.
1881  * If no exception occurred then the last SDL error message is kept.
1882  */
1883  if (Android_JNI_ExceptionOccurred(SDL_FALSE) || !inputStream) {
1884  goto failure;
1885  }
1886  }
1887 
1888  ctx->hidden.androidio.inputStreamRef = (*env)->NewGlobalRef(env, inputStream);
1889 
1890  /* Despite all the visible documentation on [Asset]InputStream claiming
1891  * that the .available() method is not guaranteed to return the entire file
1892  * size, comments in <sdk>/samples/<ver>/ApiDemos/src/com/example/ ...
1893  * android/apis/content/ReadAsset.java imply that Android's
1894  * AssetInputStream.available() /will/ always return the total file size
1895  */
1896 
1897  /* size = inputStream.available(); */
1898  mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, inputStream),
1899  "available", "()I");
1900  ctx->hidden.androidio.size = (long)(*env)->CallIntMethod(env, inputStream, mid);
1901  if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
1902  goto failure;
1903  }
1904 
1905  /* readableByteChannel = Channels.newChannel(inputStream); */
1906  channels = (*env)->FindClass(env, "java/nio/channels/Channels");
1907  mid = (*env)->GetStaticMethodID(env, channels,
1908  "newChannel",
1909  "(Ljava/io/InputStream;)Ljava/nio/channels/ReadableByteChannel;");
1910  readableByteChannel = (*env)->CallStaticObjectMethod(
1911  env, channels, mid, inputStream);
1912  if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
1913  goto failure;
1914  }
1915 
1916  ctx->hidden.androidio.readableByteChannelRef =
1917  (*env)->NewGlobalRef(env, readableByteChannel);
1918 
1919  /* Store .read id for reading purposes */
1920  mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, readableByteChannel),
1921  "read", "(Ljava/nio/ByteBuffer;)I");
1922  ctx->hidden.androidio.readMethod = mid;
1923  }
1924 
1925  if (0) {
1926 failure:
1927  result = -1;
1928 
1929  (*env)->DeleteGlobalRef(env, (jobject)ctx->hidden.androidio.fileNameRef);
1930 
1931  if(ctx->hidden.androidio.inputStreamRef != NULL) {
1932  (*env)->DeleteGlobalRef(env, (jobject)ctx->hidden.androidio.inputStreamRef);
1933  }
1934 
1935  if(ctx->hidden.androidio.readableByteChannelRef != NULL) {
1936  (*env)->DeleteGlobalRef(env, (jobject)ctx->hidden.androidio.readableByteChannelRef);
1937  }
1938 
1939  if(ctx->hidden.androidio.assetFileDescriptorRef != NULL) {
1940  (*env)->DeleteGlobalRef(env, (jobject)ctx->hidden.androidio.assetFileDescriptorRef);
1941  }
1942 
1943  }
1944 
1945  LocalReferenceHolder_Cleanup(&refs);
1946  return result;
1947 }
1948 
1950  const char *fileName, const char *mode)
1951 {
1952  struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
1953  JNIEnv *env = Android_JNI_GetEnv();
1954  int retval;
1955  jstring fileNameJString;
1956 
1957  if (!LocalReferenceHolder_Init(&refs, env)) {
1958  LocalReferenceHolder_Cleanup(&refs);
1959  return -1;
1960  }
1961 
1962  if (!ctx) {
1963  LocalReferenceHolder_Cleanup(&refs);
1964  return -1;
1965  }
1966 
1967  fileNameJString = (*env)->NewStringUTF(env, fileName);
1968  ctx->hidden.androidio.fileNameRef = (*env)->NewGlobalRef(env, fileNameJString);
1969  ctx->hidden.androidio.inputStreamRef = NULL;
1970  ctx->hidden.androidio.readableByteChannelRef = NULL;
1971  ctx->hidden.androidio.readMethod = NULL;
1972  ctx->hidden.androidio.assetFileDescriptorRef = NULL;
1973 
1974  retval = Internal_Android_JNI_FileOpen(ctx);
1975  LocalReferenceHolder_Cleanup(&refs);
1976  return retval;
1977 }
1978 
1979 size_t Android_JNI_FileRead(SDL_RWops *ctx, void *buffer,
1980  size_t size, size_t maxnum)
1981 {
1982  struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
1983 
1984  if (ctx->hidden.androidio.assetFileDescriptorRef) {
1985  size_t bytesMax = size * maxnum;
1986  size_t result;
1987  if (ctx->hidden.androidio.size != -1 /* UNKNOWN_LENGTH */ && ctx->hidden.androidio.position + bytesMax > ctx->hidden.androidio.size) {
1988  bytesMax = ctx->hidden.androidio.size - ctx->hidden.androidio.position;
1989  }
1990  result = read(ctx->hidden.androidio.fd, buffer, bytesMax );
1991  if (result > 0) {
1992  ctx->hidden.androidio.position += result;
1993  LocalReferenceHolder_Cleanup(&refs);
1994  return result / size;
1995  }
1996  LocalReferenceHolder_Cleanup(&refs);
1997  return 0;
1998  } else {
1999  jlong bytesRemaining = (jlong) (size * maxnum);
2000  jlong bytesMax = (jlong) (ctx->hidden.androidio.size - ctx->hidden.androidio.position);
2001  int bytesRead = 0;
2002  JNIEnv *env;
2003  jobject readableByteChannel;
2004  jmethodID readMethod;
2005  jobject byteBuffer;
2006 
2007  /* Don't read more bytes than those that remain in the file, otherwise we get an exception */
2008  if (bytesRemaining > bytesMax) bytesRemaining = bytesMax;
2009 
2010  env = Android_JNI_GetEnv();
2011  if (!LocalReferenceHolder_Init(&refs, env)) {
2012  LocalReferenceHolder_Cleanup(&refs);
2013  return 0;
2014  }
2015 
2016  readableByteChannel = (jobject)ctx->hidden.androidio.readableByteChannelRef;
2017  readMethod = (jmethodID)ctx->hidden.androidio.readMethod;
2018  byteBuffer = (*env)->NewDirectByteBuffer(env, buffer, bytesRemaining);
2019 
2020  while (bytesRemaining > 0) {
2021  /* result = readableByteChannel.read(...); */
2022  int result = (*env)->CallIntMethod(env, readableByteChannel, readMethod, byteBuffer);
2023 
2024  if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
2025  LocalReferenceHolder_Cleanup(&refs);
2026  return 0;
2027  }
2028 
2029  if (result < 0) {
2030  break;
2031  }
2032 
2033  bytesRemaining -= result;
2034  bytesRead += result;
2035  ctx->hidden.androidio.position += result;
2036  }
2037  LocalReferenceHolder_Cleanup(&refs);
2038  return bytesRead / size;
2039  }
2040 }
2041 
2042 size_t Android_JNI_FileWrite(SDL_RWops *ctx, const void *buffer,
2043  size_t size, size_t num)
2044 {
2045  SDL_SetError("Cannot write to Android package filesystem");
2046  return 0;
2047 }
2048 
2049 static int Internal_Android_JNI_FileClose(SDL_RWops *ctx, SDL_bool release)
2050 {
2051  struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
2052 
2053  int result = 0;
2054  JNIEnv *env = Android_JNI_GetEnv();
2055 
2056  if (!LocalReferenceHolder_Init(&refs, env)) {
2057  LocalReferenceHolder_Cleanup(&refs);
2058  return SDL_SetError("Failed to allocate enough JVM local references");
2059  }
2060 
2061  if (ctx) {
2062  if (release) {
2063  (*env)->DeleteGlobalRef(env, (jobject)ctx->hidden.androidio.fileNameRef);
2064  }
2065 
2066  if (ctx->hidden.androidio.assetFileDescriptorRef) {
2067  jobject inputStream = (jobject)ctx->hidden.androidio.assetFileDescriptorRef;
2068  jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, inputStream),
2069  "close", "()V");
2070  (*env)->CallVoidMethod(env, inputStream, mid);
2071  (*env)->DeleteGlobalRef(env, (jobject)ctx->hidden.androidio.assetFileDescriptorRef);
2072  if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
2073  result = -1;
2074  }
2075  }
2076  else {
2077  jobject inputStream = (jobject)ctx->hidden.androidio.inputStreamRef;
2078 
2079  /* inputStream.close(); */
2080  jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, inputStream),
2081  "close", "()V");
2082  (*env)->CallVoidMethod(env, inputStream, mid);
2083  (*env)->DeleteGlobalRef(env, (jobject)ctx->hidden.androidio.inputStreamRef);
2084  (*env)->DeleteGlobalRef(env, (jobject)ctx->hidden.androidio.readableByteChannelRef);
2085  if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
2086  result = -1;
2087  }
2088  }
2089 
2090  if (release) {
2091  SDL_FreeRW(ctx);
2092  }
2093  }
2094 
2095  LocalReferenceHolder_Cleanup(&refs);
2096  return result;
2097 }
2098 
2099 
2101 {
2102  return ctx->hidden.androidio.size;
2103 }
2104 
2106 {
2107  if (ctx->hidden.androidio.assetFileDescriptorRef) {
2108  off_t ret;
2109  switch (whence) {
2110  case RW_SEEK_SET:
2111  if (ctx->hidden.androidio.size != -1 /* UNKNOWN_LENGTH */ && offset > ctx->hidden.androidio.size) offset = ctx->hidden.androidio.size;
2112  offset += ctx->hidden.androidio.offset;
2113  break;
2114  case RW_SEEK_CUR:
2115  offset += ctx->hidden.androidio.position;
2116  if (ctx->hidden.androidio.size != -1 /* UNKNOWN_LENGTH */ && offset > ctx->hidden.androidio.size) offset = ctx->hidden.androidio.size;
2117  offset += ctx->hidden.androidio.offset;
2118  break;
2119  case RW_SEEK_END:
2120  offset = ctx->hidden.androidio.offset + ctx->hidden.androidio.size + offset;
2121  break;
2122  default:
2123  return SDL_SetError("Unknown value for 'whence'");
2124  }
2125 
2126  ret = lseek(ctx->hidden.androidio.fd, (off_t)offset, SEEK_SET);
2127  if (ret == -1) return -1;
2128  ctx->hidden.androidio.position = ret - ctx->hidden.androidio.offset;
2129  } else {
2130  Sint64 newPosition;
2131  Sint64 movement;
2132 
2133  switch (whence) {
2134  case RW_SEEK_SET:
2135  newPosition = offset;
2136  break;
2137  case RW_SEEK_CUR:
2138  newPosition = ctx->hidden.androidio.position + offset;
2139  break;
2140  case RW_SEEK_END:
2141  newPosition = ctx->hidden.androidio.size + offset;
2142  break;
2143  default:
2144  return SDL_SetError("Unknown value for 'whence'");
2145  }
2146 
2147  /* Validate the new position */
2148  if (newPosition < 0) {
2149  return SDL_Error(SDL_EFSEEK);
2150  }
2151  if (newPosition > ctx->hidden.androidio.size) {
2152  newPosition = ctx->hidden.androidio.size;
2153  }
2154 
2155  movement = newPosition - ctx->hidden.androidio.position;
2156  if (movement > 0) {
2157  unsigned char buffer[4096];
2158 
2159  /* The easy case where we're seeking forwards */
2160  while (movement > 0) {
2161  Sint64 amount = sizeof (buffer);
2162  size_t result;
2163  if (amount > movement) {
2164  amount = movement;
2165  }
2166  result = Android_JNI_FileRead(ctx, buffer, 1, (size_t)amount);
2167  if (result <= 0) {
2168  /* Failed to read/skip the required amount, so fail */
2169  return -1;
2170  }
2171 
2172  movement -= result;
2173  }
2174 
2175  } else if (movement < 0) {
2176  /* We can't seek backwards so we have to reopen the file and seek */
2177  /* forwards which obviously isn't very efficient */
2178  Internal_Android_JNI_FileClose(ctx, SDL_FALSE);
2179  Internal_Android_JNI_FileOpen(ctx);
2180  Android_JNI_FileSeek(ctx, newPosition, RW_SEEK_SET);
2181  }
2182  }
2183 
2184  return ctx->hidden.androidio.position;
2185 
2186 }
2187 
2189 {
2190  return Internal_Android_JNI_FileClose(ctx, SDL_TRUE);
2191 }
2192 
2193 int Android_JNI_SetClipboardText(const char *text)
2194 {
2195  JNIEnv *env = Android_JNI_GetEnv();
2196  jstring string = (*env)->NewStringUTF(env, text);
2197  (*env)->CallStaticVoidMethod(env, mActivityClass, midClipboardSetText, string);
2198  (*env)->DeleteLocalRef(env, string);
2199  return 0;
2200 }
2201 
2202 char* Android_JNI_GetClipboardText(void)
2203 {
2204  JNIEnv *env = Android_JNI_GetEnv();
2205  char *text = NULL;
2206  jstring string;
2207 
2208  string = (*env)->CallStaticObjectMethod(env, mActivityClass, midClipboardGetText);
2209  if (string) {
2210  const char *utf = (*env)->GetStringUTFChars(env, string, 0);
2211  if (utf) {
2212  text = SDL_strdup(utf);
2213  (*env)->ReleaseStringUTFChars(env, string, utf);
2214  }
2215  (*env)->DeleteLocalRef(env, string);
2216  }
2217 
2218  return (text == NULL) ? SDL_strdup("") : text;
2219 }
2220 
2222 {
2223  JNIEnv *env = Android_JNI_GetEnv();
2224  jboolean retval = (*env)->CallStaticBooleanMethod(env, mActivityClass, midClipboardHasText);
2225  return (retval == JNI_TRUE) ? SDL_TRUE : SDL_FALSE;
2226 }
2227 
2228 /* returns 0 on success or -1 on error (others undefined then)
2229  * returns truthy or falsy value in plugged, charged and battery
2230  * returns the value in seconds and percent or -1 if not available
2231  */
2232 int Android_JNI_GetPowerInfo(int *plugged, int *charged, int *battery, int *seconds, int *percent)
2233 {
2234  struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
2235  JNIEnv *env = Android_JNI_GetEnv();
2236  jmethodID mid;
2237  jobject context;
2238  jstring action;
2239  jclass cls;
2240  jobject filter;
2241  jobject intent;
2242  jstring iname;
2243  jmethodID imid;
2244  jstring bname;
2245  jmethodID bmid;
2246  if (!LocalReferenceHolder_Init(&refs, env)) {
2247  LocalReferenceHolder_Cleanup(&refs);
2248  return -1;
2249  }
2250 
2251 
2252  /* context = SDLActivity.getContext(); */
2253  context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
2254 
2255  action = (*env)->NewStringUTF(env, "android.intent.action.BATTERY_CHANGED");
2256 
2257  cls = (*env)->FindClass(env, "android/content/IntentFilter");
2258 
2259  mid = (*env)->GetMethodID(env, cls, "<init>", "(Ljava/lang/String;)V");
2260  filter = (*env)->NewObject(env, cls, mid, action);
2261 
2262  (*env)->DeleteLocalRef(env, action);
2263 
2264  mid = (*env)->GetMethodID(env, mActivityClass, "registerReceiver", "(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;");
2265  intent = (*env)->CallObjectMethod(env, context, mid, NULL, filter);
2266 
2267  (*env)->DeleteLocalRef(env, filter);
2268 
2269  cls = (*env)->GetObjectClass(env, intent);
2270 
2271  imid = (*env)->GetMethodID(env, cls, "getIntExtra", "(Ljava/lang/String;I)I");
2272 
2273  /* Watch out for C89 scoping rules because of the macro */
2274 #define GET_INT_EXTRA(var, key) \
2275  int var; \
2276  iname = (*env)->NewStringUTF(env, key); \
2277  (var) = (*env)->CallIntMethod(env, intent, imid, iname, -1); \
2278  (*env)->DeleteLocalRef(env, iname);
2279 
2280  bmid = (*env)->GetMethodID(env, cls, "getBooleanExtra", "(Ljava/lang/String;Z)Z");
2281 
2282  /* Watch out for C89 scoping rules because of the macro */
2283 #define GET_BOOL_EXTRA(var, key) \
2284  int var; \
2285  bname = (*env)->NewStringUTF(env, key); \
2286  (var) = (*env)->CallBooleanMethod(env, intent, bmid, bname, JNI_FALSE); \
2287  (*env)->DeleteLocalRef(env, bname);
2288 
2289  if (plugged) {
2290  /* Watch out for C89 scoping rules because of the macro */
2291  GET_INT_EXTRA(plug, "plugged") /* == BatteryManager.EXTRA_PLUGGED (API 5) */
2292  if (plug == -1) {
2293  LocalReferenceHolder_Cleanup(&refs);
2294  return -1;
2295  }
2296  /* 1 == BatteryManager.BATTERY_PLUGGED_AC */
2297  /* 2 == BatteryManager.BATTERY_PLUGGED_USB */
2298  *plugged = (0 < plug) ? 1 : 0;
2299  }
2300 
2301  if (charged) {
2302  /* Watch out for C89 scoping rules because of the macro */
2303  GET_INT_EXTRA(status, "status") /* == BatteryManager.EXTRA_STATUS (API 5) */
2304  if (status == -1) {
2305  LocalReferenceHolder_Cleanup(&refs);
2306  return -1;
2307  }
2308  /* 5 == BatteryManager.BATTERY_STATUS_FULL */
2309  *charged = (status == 5) ? 1 : 0;
2310  }
2311 
2312  if (battery) {
2313  GET_BOOL_EXTRA(present, "present") /* == BatteryManager.EXTRA_PRESENT (API 5) */
2314  *battery = present ? 1 : 0;
2315  }
2316 
2317  if (seconds) {
2318  *seconds = -1; /* not possible */
2319  }
2320 
2321  if (percent) {
2322  int level;
2323  int scale;
2324 
2325  /* Watch out for C89 scoping rules because of the macro */
2326  {
2327  GET_INT_EXTRA(level_temp, "level") /* == BatteryManager.EXTRA_LEVEL (API 5) */
2328  level = level_temp;
2329  }
2330  /* Watch out for C89 scoping rules because of the macro */
2331  {
2332  GET_INT_EXTRA(scale_temp, "scale") /* == BatteryManager.EXTRA_SCALE (API 5) */
2333  scale = scale_temp;
2334  }
2335 
2336  if ((level == -1) || (scale == -1)) {
2337  LocalReferenceHolder_Cleanup(&refs);
2338  return -1;
2339  }
2340  *percent = level * 100 / scale;
2341  }
2342 
2343  (*env)->DeleteLocalRef(env, intent);
2344 
2345  LocalReferenceHolder_Cleanup(&refs);
2346  return 0;
2347 }
2348 
2349 /* Add all touch devices */
2350 void Android_JNI_InitTouch() {
2351  JNIEnv *env = Android_JNI_GetEnv();
2352  (*env)->CallStaticVoidMethod(env, mActivityClass, midInitTouch);
2353 }
2354 
2356 {
2357  JNIEnv *env = Android_JNI_GetEnv();
2358  (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midPollInputDevices);
2359 }
2360 
2362 {
2363  JNIEnv *env = Android_JNI_GetEnv();
2364  (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midPollHapticDevices);
2365 }
2366 
2367 void Android_JNI_HapticRun(int device_id, float intensity, int length)
2368 {
2369  JNIEnv *env = Android_JNI_GetEnv();
2370  (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midHapticRun, device_id, intensity, length);
2371 }
2372 
2373 void Android_JNI_HapticStop(int device_id)
2374 {
2375  JNIEnv *env = Android_JNI_GetEnv();
2376  (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midHapticStop, device_id);
2377 }
2378 
2379 /* See SDLActivity.java for constants. */
2380 #define COMMAND_SET_KEEP_SCREEN_ON 5
2381 
2382 /* sends message to be handled on the UI event dispatch thread */
2383 int Android_JNI_SendMessage(int command, int param)
2384 {
2385  JNIEnv *env = Android_JNI_GetEnv();
2386  jboolean success;
2387  success = (*env)->CallStaticBooleanMethod(env, mActivityClass, midSendMessage, command, param);
2388  return success ? 0 : -1;
2389 }
2390 
2392 {
2393  Android_JNI_SendMessage(COMMAND_SET_KEEP_SCREEN_ON, (suspend == SDL_FALSE) ? 0 : 1);
2394 }
2395 
2396 void Android_JNI_ShowTextInput(SDL_Rect *inputRect)
2397 {
2398  JNIEnv *env = Android_JNI_GetEnv();
2399  (*env)->CallStaticBooleanMethod(env, mActivityClass, midShowTextInput,
2400  inputRect->x,
2401  inputRect->y,
2402  inputRect->w,
2403  inputRect->h );
2404 }
2405 
2406 void Android_JNI_HideTextInput(void)
2407 {
2408  /* has to match Activity constant */
2409  const int COMMAND_TEXTEDIT_HIDE = 3;
2410  Android_JNI_SendMessage(COMMAND_TEXTEDIT_HIDE, 0);
2411 }
2412 
2414 {
2415  JNIEnv *env = Android_JNI_GetEnv();
2416  jboolean is_shown = 0;
2417  is_shown = (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsScreenKeyboardShown);
2418  return is_shown;
2419 }
2420 
2421 
2422 int Android_JNI_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
2423 {
2424  JNIEnv *env;
2425  jclass clazz;
2426  jmethodID mid;
2427  jobject context;
2428  jstring title;
2429  jstring message;
2430  jintArray button_flags;
2431  jintArray button_ids;
2432  jobjectArray button_texts;
2433  jintArray colors;
2434  jobject text;
2435  jint temp;
2436  int i;
2437 
2438  env = Android_JNI_GetEnv();
2439 
2440  /* convert parameters */
2441 
2442  clazz = (*env)->FindClass(env, "java/lang/String");
2443 
2444  title = (*env)->NewStringUTF(env, messageboxdata->title);
2445  message = (*env)->NewStringUTF(env, messageboxdata->message);
2446 
2447  button_flags = (*env)->NewIntArray(env, messageboxdata->numbuttons);
2448  button_ids = (*env)->NewIntArray(env, messageboxdata->numbuttons);
2449  button_texts = (*env)->NewObjectArray(env, messageboxdata->numbuttons,
2450  clazz, NULL);
2451  for (i = 0; i < messageboxdata->numbuttons; ++i) {
2452  const SDL_MessageBoxButtonData *sdlButton;
2453 
2454  if (messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT) {
2455  sdlButton = &messageboxdata->buttons[messageboxdata->numbuttons - 1 - i];
2456  } else {
2457  sdlButton = &messageboxdata->buttons[i];
2458  }
2459 
2460  temp = sdlButton->flags;
2461  (*env)->SetIntArrayRegion(env, button_flags, i, 1, &temp);
2462  temp = sdlButton->buttonid;
2463  (*env)->SetIntArrayRegion(env, button_ids, i, 1, &temp);
2464  text = (*env)->NewStringUTF(env, sdlButton->text);
2465  (*env)->SetObjectArrayElement(env, button_texts, i, text);
2466  (*env)->DeleteLocalRef(env, text);
2467  }
2468 
2469  if (messageboxdata->colorScheme) {
2470  colors = (*env)->NewIntArray(env, SDL_MESSAGEBOX_COLOR_MAX);
2471  for (i = 0; i < SDL_MESSAGEBOX_COLOR_MAX; ++i) {
2472  temp = (0xFFU << 24) |
2473  (messageboxdata->colorScheme->colors[i].r << 16) |
2474  (messageboxdata->colorScheme->colors[i].g << 8) |
2475  (messageboxdata->colorScheme->colors[i].b << 0);
2476  (*env)->SetIntArrayRegion(env, colors, i, 1, &temp);
2477  }
2478  } else {
2479  colors = NULL;
2480  }
2481 
2482  (*env)->DeleteLocalRef(env, clazz);
2483 
2484  /* context = SDLActivity.getContext(); */
2485  context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
2486 
2487  clazz = (*env)->GetObjectClass(env, context);
2488 
2489  mid = (*env)->GetMethodID(env, clazz,
2490  "messageboxShowMessageBox", "(ILjava/lang/String;Ljava/lang/String;[I[I[Ljava/lang/String;[I)I");
2491  *buttonid = (*env)->CallIntMethod(env, context, mid,
2492  messageboxdata->flags,
2493  title,
2494  message,
2495  button_flags,
2496  button_ids,
2497  button_texts,
2498  colors);
2499 
2500  (*env)->DeleteLocalRef(env, context);
2501  (*env)->DeleteLocalRef(env, clazz);
2502 
2503  /* delete parameters */
2504 
2505  (*env)->DeleteLocalRef(env, title);
2506  (*env)->DeleteLocalRef(env, message);
2507  (*env)->DeleteLocalRef(env, button_flags);
2508  (*env)->DeleteLocalRef(env, button_ids);
2509  (*env)->DeleteLocalRef(env, button_texts);
2510  (*env)->DeleteLocalRef(env, colors);
2511 
2512  return 0;
2513 }
2514 
2515 /*
2516 //////////////////////////////////////////////////////////////////////////////
2517 //
2518 // Functions exposed to SDL applications in SDL_system.h
2519 //////////////////////////////////////////////////////////////////////////////
2520 */
2521 
2522 void *SDL_AndroidGetJNIEnv(void)
2523 {
2524  return Android_JNI_GetEnv();
2525 }
2526 
2527 void *SDL_AndroidGetActivity(void)
2528 {
2529  /* See SDL_system.h for caveats on using this function. */
2530 
2531  JNIEnv *env = Android_JNI_GetEnv();
2532  if (!env) {
2533  return NULL;
2534  }
2535 
2536  /* return SDLActivity.getContext(); */
2537  return (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
2538 }
2539 
2540 int SDL_GetAndroidSDKVersion(void)
2541 {
2542  static int sdk_version;
2543  if (!sdk_version) {
2544  char sdk[PROP_VALUE_MAX] = {0};
2545  if (__system_property_get("ro.build.version.sdk", sdk) != 0) {
2546  sdk_version = SDL_atoi(sdk);
2547  }
2548  }
2549  return sdk_version;
2550 }
2551 
2553 {
2554  JNIEnv *env = Android_JNI_GetEnv();
2555  return (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsTablet);
2556 }
2557 
2559 {
2560  JNIEnv *env = Android_JNI_GetEnv();
2561  return (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsAndroidTV);
2562 }
2563 
2565 {
2566  JNIEnv *env = Android_JNI_GetEnv();
2567  return (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsChromebook);
2568 }
2569 
2570 SDL_bool SDL_IsDeXMode(void)
2571 {
2572  JNIEnv *env = Android_JNI_GetEnv();
2573  return (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsDeXMode);
2574 }
2575 
2576 void SDL_AndroidBackButton(void)
2577 {
2578  JNIEnv *env = Android_JNI_GetEnv();
2579  (*env)->CallStaticVoidMethod(env, mActivityClass, midManualBackButton);
2580 }
2581 
2582 const char * SDL_AndroidGetInternalStoragePath(void)
2583 {
2584  static char *s_AndroidInternalFilesPath = NULL;
2585 
2586  if (!s_AndroidInternalFilesPath) {
2587  struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
2588  jmethodID mid;
2589  jobject context;
2590  jobject fileObject;
2591  jstring pathString;
2592  const char *path;
2593 
2594  JNIEnv *env = Android_JNI_GetEnv();
2595  if (!LocalReferenceHolder_Init(&refs, env)) {
2596  LocalReferenceHolder_Cleanup(&refs);
2597  return NULL;
2598  }
2599 
2600  /* context = SDLActivity.getContext(); */
2601  context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
2602  if (!context) {
2603  SDL_SetError("Couldn't get Android context!");
2604  LocalReferenceHolder_Cleanup(&refs);
2605  return NULL;
2606  }
2607 
2608  /* fileObj = context.getFilesDir(); */
2609  mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
2610  "getFilesDir", "()Ljava/io/File;");
2611  fileObject = (*env)->CallObjectMethod(env, context, mid);
2612  if (!fileObject) {
2613  SDL_SetError("Couldn't get internal directory");
2614  LocalReferenceHolder_Cleanup(&refs);
2615  return NULL;
2616  }
2617 
2618  /* path = fileObject.getCanonicalPath(); */
2619  mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, fileObject),
2620  "getCanonicalPath", "()Ljava/lang/String;");
2621  pathString = (jstring)(*env)->CallObjectMethod(env, fileObject, mid);
2622  if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
2623  LocalReferenceHolder_Cleanup(&refs);
2624  return NULL;
2625  }
2626 
2627  path = (*env)->GetStringUTFChars(env, pathString, NULL);
2628  s_AndroidInternalFilesPath = SDL_strdup(path);
2629  (*env)->ReleaseStringUTFChars(env, pathString, path);
2630 
2631  LocalReferenceHolder_Cleanup(&refs);
2632  }
2633  return s_AndroidInternalFilesPath;
2634 }
2635 
2637 {
2638  struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
2639  jmethodID mid;
2640  jclass cls;
2641  jstring stateString;
2642  const char *state;
2643  int stateFlags;
2644 
2645  JNIEnv *env = Android_JNI_GetEnv();
2646  if (!LocalReferenceHolder_Init(&refs, env)) {
2647  LocalReferenceHolder_Cleanup(&refs);
2648  return 0;
2649  }
2650 
2651  cls = (*env)->FindClass(env, "android/os/Environment");
2652  mid = (*env)->GetStaticMethodID(env, cls,
2653  "getExternalStorageState", "()Ljava/lang/String;");
2654  stateString = (jstring)(*env)->CallStaticObjectMethod(env, cls, mid);
2655 
2656  state = (*env)->GetStringUTFChars(env, stateString, NULL);
2657 
2658  /* Print an info message so people debugging know the storage state */
2659  __android_log_print(ANDROID_LOG_INFO, "SDL", "external storage state: %s", state);
2660 
2661  if (SDL_strcmp(state, "mounted") == 0) {
2662  stateFlags = SDL_ANDROID_EXTERNAL_STORAGE_READ |
2663  SDL_ANDROID_EXTERNAL_STORAGE_WRITE;
2664  } else if (SDL_strcmp(state, "mounted_ro") == 0) {
2665  stateFlags = SDL_ANDROID_EXTERNAL_STORAGE_READ;
2666  } else {
2667  stateFlags = 0;
2668  }
2669  (*env)->ReleaseStringUTFChars(env, stateString, state);
2670 
2671  LocalReferenceHolder_Cleanup(&refs);
2672  return stateFlags;
2673 }
2674 
2675 const char * SDL_AndroidGetExternalStoragePath(void)
2676 {
2677  static char *s_AndroidExternalFilesPath = NULL;
2678 
2679  if (!s_AndroidExternalFilesPath) {
2680  struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
2681  jmethodID mid;
2682  jobject context;
2683  jobject fileObject;
2684  jstring pathString;
2685  const char *path;
2686 
2687  JNIEnv *env = Android_JNI_GetEnv();
2688  if (!LocalReferenceHolder_Init(&refs, env)) {
2689  LocalReferenceHolder_Cleanup(&refs);
2690  return NULL;
2691  }
2692 
2693  /* context = SDLActivity.getContext(); */
2694  context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
2695 
2696  /* fileObj = context.getExternalFilesDir(); */
2697  mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
2698  "getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;");
2699  fileObject = (*env)->CallObjectMethod(env, context, mid, NULL);
2700  if (!fileObject) {
2701  SDL_SetError("Couldn't get external directory");
2702  LocalReferenceHolder_Cleanup(&refs);
2703  return NULL;
2704  }
2705 
2706  /* path = fileObject.getAbsolutePath(); */
2707  mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, fileObject),
2708  "getAbsolutePath", "()Ljava/lang/String;");
2709  pathString = (jstring)(*env)->CallObjectMethod(env, fileObject, mid);
2710 
2711  path = (*env)->GetStringUTFChars(env, pathString, NULL);
2712  s_AndroidExternalFilesPath = SDL_strdup(path);
2713  (*env)->ReleaseStringUTFChars(env, pathString, path);
2714 
2715  LocalReferenceHolder_Cleanup(&refs);
2716  }
2717  return s_AndroidExternalFilesPath;
2718 }
2719 
2721 {
2722  if (!mActivityClass || !midGetManifestEnvironmentVariables) {
2723  __android_log_print(ANDROID_LOG_WARN, "SDL", "Request to get environment variables before JNI is ready");
2724  return;
2725  }
2726 
2727  if (!bHasEnvironmentVariables) {
2728  JNIEnv *env = Android_JNI_GetEnv();
2729  SDL_bool ret = (*env)->CallStaticBooleanMethod(env, mActivityClass, midGetManifestEnvironmentVariables);
2730  if (ret) {
2731  bHasEnvironmentVariables = SDL_TRUE;
2732  }
2733  }
2734 }
2735 
2737 {
2738  JNIEnv *env = Android_JNI_GetEnv();
2739  int custom_cursor = 0;
2740  jintArray pixels;
2741  pixels = (*env)->NewIntArray(env, surface->w * surface->h);
2742  if (pixels) {
2743  (*env)->SetIntArrayRegion(env, pixels, 0, surface->w * surface->h, (int *)surface->pixels);
2744  custom_cursor = (*env)->CallStaticIntMethod(env, mActivityClass, midCreateCustomCursor, pixels, surface->w, surface->h, hot_x, hot_y);
2745  (*env)->DeleteLocalRef(env, pixels);
2746  } else {
2747  SDL_OutOfMemory();
2748  }
2749  return custom_cursor;
2750 }
2751 
2752 
2754 {
2755  JNIEnv *env = Android_JNI_GetEnv();
2756  return (*env)->CallStaticBooleanMethod(env, mActivityClass, midSetCustomCursor, cursorID);
2757 }
2758 
2760 {
2761  JNIEnv *env = Android_JNI_GetEnv();
2762  return (*env)->CallStaticBooleanMethod(env, mActivityClass, midSetSystemCursor, cursorID);
2763 }
2764 
2766 {
2767  JNIEnv *env = Android_JNI_GetEnv();
2768  return (*env)->CallStaticBooleanMethod(env, mActivityClass, midSupportsRelativeMouse);
2769 }
2770 
2772 {
2773  JNIEnv *env = Android_JNI_GetEnv();
2774  return (*env)->CallStaticBooleanMethod(env, mActivityClass, midSetRelativeMouseEnabled, (enabled == 1));
2775 }
2776 
2777 SDL_bool Android_JNI_RequestPermission(const char *permission)
2778 {
2779  JNIEnv *env = Android_JNI_GetEnv();
2780  const int requestCode = 1;
2781 
2782  /* Wait for any pending request on another thread */
2783  while (SDL_AtomicGet(&bPermissionRequestPending) == SDL_TRUE) {
2784  SDL_Delay(10);
2785  }
2786  SDL_AtomicSet(&bPermissionRequestPending, SDL_TRUE);
2787 
2788  jstring jpermission = (*env)->NewStringUTF(env, permission);
2789  (*env)->CallStaticVoidMethod(env, mActivityClass, midRequestPermission, jpermission, requestCode);
2790  (*env)->DeleteLocalRef(env, jpermission);
2791 
2792  /* Wait for the request to complete */
2793  while (SDL_AtomicGet(&bPermissionRequestPending) == SDL_TRUE) {
2794  SDL_Delay(10);
2795  }
2796  return bPermissionRequestResult;
2797 }
2798 
2799 #endif /* __ANDROID__ */
2800 
2801 /* vi: set ts=4 sw=4 expandtab: */
Android_JNI_SetOrientation
void Android_JNI_SetOrientation(int w, int h, int resizable, const char *hint)
SDL_TOUCH_DEVICE_DIRECT
@ SDL_TOUCH_DEVICE_DIRECT
Definition: SDL_touch.h:47
format
GLint GLint GLsizei GLsizei GLsizei GLint GLenum format
Definition: SDL_opengl.h:1572
sort_controllers.filename
string filename
Definition: sort_controllers.py:8
SDL_GetError
#define SDL_GetError
Definition: SDL_dynapi_overrides.h:113
SDL_AudioSpec::channels
Uint8 channels
Definition: SDL_audio.h:182
Android_JNI_GetManifestEnvironmentVariables
void Android_JNI_GetManifestEnvironmentVariables(void)
SDL_SetMainReady
#define SDL_SetMainReady
Definition: SDL_dynapi_overrides.h:242
Sint64
int64_t Sint64
Definition: SDL_stdinc.h:210
AndroidKeyInfo
Definition: keyinfotable.h:36
SDL_small_free
#define SDL_small_free(ptr, isstack)
Definition: SDL_internal.h:40
SDL_APP_TERMINATING
@ SDL_APP_TERMINATING
Definition: SDL_events.h:63
Android_JNI_HapticRun
void Android_JNI_HapticRun(int device_id, float intensity, int length)
Android_JNI_SetActivityTitle
void Android_JNI_SetActivityTitle(const char *title)
SDL_MessageBoxColorScheme::colors
SDL_MessageBoxColor colors[SDL_MESSAGEBOX_COLOR_MAX]
Definition: SDL_messagebox.h:88
offset
GLintptr offset
Definition: SDL_opengl_glext.h:541
scale
GLenum GLenum GLenum GLenum GLenum scale
Definition: SDL_opengl_glext.h:9378
Android_JNI_SetupThread
int Android_JNI_SetupThread(void)
SDL_Surface
A collection of pixels used in software blitting.
Definition: SDL_surface.h:71
Android_JNI_SetWindowStyle
void Android_JNI_SetWindowStyle(SDL_bool fullscreen)
SDL_AndroidBackButton
#define SDL_AndroidBackButton
Definition: SDL_dynapi_overrides.h:677
Android_ActivityMutex
SDL_mutex * Android_ActivityMutex
Android_JNI_GetClipboardText
char * Android_JNI_GetClipboardText(void)
SDL_DISPLAYEVENT_ORIENTATION
@ SDL_DISPLAYEVENT_ORIENTATION
Definition: SDL_video.h:177
SDL_main.h
Android_JNI_GetDisplayOrientation
SDL_DisplayOrientation Android_JNI_GetDisplayOrientation(void)
if
set set set set set set set macro pixldst1 abits if abits op else op endif endm macro pixldst2 abits if abits op else op endif endm macro pixldst4 abits if abits op else op endif endm macro pixldst0 abits op endm macro pixldst3 mem_operand op endm macro pixldst30 mem_operand op endm macro pixldst abits if abits elseif abits elseif abits elseif abits elseif abits pixldst0 abits else pixldst0 abits pixldst0 abits pixldst0 abits pixldst0 abits endif elseif abits else pixldst0 abits pixldst0 abits endif elseif abits else error unsupported bpp *numpix else pixst endif endm macro pixld1_s mem_operand if asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl elseif asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl else error unsupported endif endm macro pixld2_s mem_operand if mov asr add asl add asl mov asr sub UNIT_X add asl mov asr add asl add asl mov asr add UNIT_X add asl else pixld1_s mem_operand pixld1_s mem_operand endif endm macro pixld0_s mem_operand if asr adds SRC_WIDTH_FIXED bpl add asl elseif asr adds SRC_WIDTH_FIXED bpl add asl endif endm macro pixld_s_internal mem_operand if mem_operand pixld2_s mem_operand pixdeinterleave basereg elseif mem_operand elseif mem_operand elseif mem_operand elseif mem_operand pixld0_s mem_operand else pixld0_s mem_operand pixld0_s mem_operand pixld0_s mem_operand pixld0_s mem_operand endif elseif mem_operand else pixld0_s mem_operand pixld0_s mem_operand endif elseif mem_operand else error unsupported mem_operand if bpp mem_operand endif endm macro vuzp8 reg2 vuzp d d &reg2 endm macro vzip8 reg2 vzip d d &reg2 endm macro pixdeinterleave basereg basereg basereg basereg basereg endif endm macro pixinterleave basereg basereg basereg basereg basereg endif endm macro PF boost_increment endif if endif PF tst PF addne PF subne PF cmp ORIG_W if endif if endif if endif PF subge ORIG_W PF subges if endif if endif if endif endif endm macro cache_preload_simple endif if dst_r_bpp pld[DST_R, #(PREFETCH_DISTANCE_SIMPLE *dst_r_bpp/8)] endif if mask_bpp pld if[MASK, #(PREFETCH_DISTANCE_SIMPLE *mask_bpp/8)] endif endif endm macro fetch_mask_pixblock pixld mask_basereg pixblock_size MASK endm macro ensure_destination_ptr_alignment process_pixblock_tail_head if beq irp skip1(dst_w_bpp<=(lowbit *8)) &&((lowbit *8)<(pixblock_size *dst_w_bpp)) .if lowbit< 16 tst DST_R
Definition: pixman-arm-neon-asm.h:469
SDL_MessageBoxData::colorScheme
const SDL_MessageBoxColorScheme * colorScheme
Definition: SDL_messagebox.h:104
SDL_CreateSemaphore
#define SDL_CreateSemaphore
Definition: SDL_dynapi_overrides.h:264
SDL_LockMutex
#define SDL_LockMutex
Definition: SDL_dynapi_overrides.h:260
Android_OnMouse
void Android_OnMouse(SDL_Window *window, int button, int action, float x, float y, SDL_bool relative)
SDL_atomic.h
Android_JNI_GetAccelerometerValues
SDL_bool Android_JNI_GetAccelerometerValues(float values[3])
SDL_MessageBoxColor::b
Uint8 b
Definition: SDL_messagebox.h:70
NULL
#define NULL
Definition: begin_code.h:167
surface
EGLSurface surface
Definition: eglext.h:248
SDL_timer.h
message
GLuint GLsizei const GLchar * message
Definition: SDL_opengl_glext.h:2486
SDL_WINDOWEVENT_FOCUS_LOST
@ SDL_WINDOWEVENT_FOCUS_LOST
Definition: SDL_video.h:165
SDL_AudioSpec::samples
Uint16 samples
Definition: SDL_audio.h:184
SDL_MessageBoxData::title
const char * title
Definition: SDL_messagebox.h:98
mode
GLenum mode
Definition: SDL_opengl_glext.h:1125
SDL_HINT_RETURN_KEY_HIDES_IME
#define SDL_HINT_RETURN_KEY_HIDES_IME
A variable to control whether the return key on the soft keyboard should hide the soft keyboard on An...
Definition: SDL_hints.h:981
NativeWindowType
EGLNativeWindowType NativeWindowType
Definition: eglplatform.h:112
SDL_IsAndroidTablet
SDL_bool SDL_IsAndroidTablet(void)
level
GLint level
Definition: SDL_opengl.h:1572
SDL_log.h
SDL_AudioSpec::format
SDL_AudioFormat format
Definition: SDL_audio.h:181
SDL_WindowData
Definition: SDL_androidwindow.h:39
Android_JNI_FileSeek
Sint64 Android_JNI_FileSeek(SDL_RWops *ctx, Sint64 offset, int whence)
RW_SEEK_END
#define RW_SEEK_END
Definition: SDL_rwops.h:176
SDL_Scancode
SDL_Scancode
The SDL keyboard scancode representation.
Definition: SDL_scancode.h:44
SDL_AtomicIncRef
#define SDL_AtomicIncRef(a)
Increment an atomic variable used as a reference count.
Definition: SDL_atomic.h:252
Android_JNI_SetSurfaceViewFormat
void Android_JNI_SetSurfaceViewFormat(int format)
uint16_t
unsigned short uint16_t
Definition: SDL_config_windows.h:61
SDL_CreateMutex
#define SDL_CreateMutex
Definition: SDL_dynapi_overrides.h:259
SDL_AndroidGetExternalStoragePath
#define SDL_AndroidGetExternalStoragePath
Definition: SDL_dynapi_overrides.h:53
z
GLdouble GLdouble z
Definition: SDL_opengl_glext.h:407
SDL_MessageBoxButtonData::flags
Uint32 flags
Definition: SDL_messagebox.h:60
SDL_MessageBoxData::message
const char * message
Definition: SDL_messagebox.h:99
SDL_AudioSpec
Definition: SDL_audio.h:179
num
GLuint num
Definition: SDL_opengl_glext.h:4959
Android_OnTouch
void Android_OnTouch(SDL_Window *window, int touch_device_id_in, int pointer_finger_id_in, int action, float x, float y, float p)
path
GLsizei const GLchar *const * path
Definition: SDL_opengl_glext.h:3733
Android_JNI_CreateCustomCursor
int Android_JNI_CreateCustomCursor(SDL_Surface *surface, int hot_x, int hot_y)
SDL_android.h
SDL_GetHint
#define SDL_GetHint
Definition: SDL_dynapi_overrides.h:191
SDL_RELEASED
#define SDL_RELEASED
Definition: SDL_events.h:49
func
GLenum func
Definition: SDL_opengl_glext.h:660
SDL_APP_LOWMEMORY
@ SDL_APP_LOWMEMORY
Definition: SDL_events.h:67
h
GLfloat GLfloat GLfloat GLfloat h
Definition: SDL_opengl_glext.h:1949
SDL_small_alloc
#define SDL_small_alloc(type, count, pisstack)
Definition: SDL_internal.h:39
length
GLuint GLsizei GLsizei * length
Definition: SDL_opengl_glext.h:672
SDL_Rect::x
int x
Definition: SDL_rect.h:79
Android_JNI_SetRelativeMouseEnabled
SDL_bool Android_JNI_SetRelativeMouseEnabled(SDL_bool enabled)
SDL_SendKeyboardKey
int SDL_SendKeyboardKey(Uint8 state, SDL_Scancode scancode)
Definition: SDL_keyboard.c:679
SDL_FIRSTEVENT
@ SDL_FIRSTEVENT
Definition: SDL_events.h:57
ctx
EGLContext ctx
Definition: eglext.h:208
SDL_SemPost
#define SDL_SemPost
Definition: SDL_dynapi_overrides.h:269
result
GLuint64EXT * result
Definition: SDL_opengl_glext.h:9435
Android_JNI_GetPowerInfo
int Android_JNI_GetPowerInfo(int *plugged, int *charged, int *battery, int *seconds, int *percent)
data
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
SDL_Rect::w
int w
Definition: SDL_rect.h:80
Android_ActivityMutex_Lock
void Android_ActivityMutex_Lock(void)
SDL_setenv
#define SDL_setenv
Definition: SDL_dynapi_overrides.h:379
filter
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Definition: SDL_opengl_glext.h:1187
SDL_SendClipboardUpdate
int SDL_SendClipboardUpdate(void)
Definition: SDL_clipboardevents.c:31
SDL_SemTryWait
#define SDL_SemTryWait
Definition: SDL_dynapi_overrides.h:267
Android_JNI_ShowMessageBox
int Android_JNI_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
AUDIO_U8
#define AUDIO_U8
Definition: SDL_audio.h:89
SDL_AtomicDecRef
#define SDL_AtomicDecRef(a)
Decrement an atomic variable used as a reference count.
Definition: SDL_atomic.h:262
values
GLenum GLsizei GLsizei GLint * values
Definition: SDL_opengl_glext.h:1489
SDL_SendKeyboardText
int SDL_SendKeyboardText(const char *text)
Definition: SDL_keyboard.c:789
SDL_StopTextInput
#define SDL_StopTextInput
Definition: SDL_dynapi_overrides.h:228
SDL_Error
#define SDL_Error
Definition: SDL_dynapi_overrides.h:115
SDL_SendDisplayEvent
int SDL_SendDisplayEvent(SDL_VideoDisplay *display, Uint8 displayevent, int data1)
Definition: SDL_displayevents.c:30
SDL_PRESSED
#define SDL_PRESSED
Definition: SDL_events.h:50
Android_OnKeyDown
int Android_OnKeyDown(int keycode)
Sint16
int16_t Sint16
Definition: SDL_stdinc.h:185
len
GLenum GLsizei len
Definition: SDL_opengl_glext.h:2929
SDL_memcpy
#define SDL_memcpy
Definition: SDL_dynapi_overrides.h:387
SDL_GetHintBoolean
#define SDL_GetHintBoolean
Definition: SDL_dynapi_overrides.h:608
SDL_SendEditingText
int SDL_SendEditingText(const char *text, int start, int length)
Definition: SDL_keyboard.c:812
buffer
GLuint buffer
Definition: SDL_opengl_glext.h:536
SDL_AddTouch
int SDL_AddTouch(SDL_TouchID touchID, SDL_TouchDeviceType type, const char *name)
Definition: SDL_touch.c:155
context
static screen_context_t context
Definition: video.c:25
AndroidKeyInfo::code
SDL_Scancode code
Definition: keyinfotable.h:37
SDL_main_func
int(* SDL_main_func)(int argc, char *argv[])
Definition: SDL_main.h:120
p
GLfloat GLfloat p
Definition: SDL_opengl_glext.h:11093
_this
static SDL_VideoDevice * _this
Definition: SDL_video.c:121
Android_JNI_GetAudioBuffer
void * Android_JNI_GetAudioBuffer(void)
Android_ActivityMutex_Lock_Running
void Android_ActivityMutex_Lock_Running(void)
array
GLenum array
Definition: SDL_opengl_glext.h:6303
retval
SDL_bool retval
Definition: testgamecontroller.c:65
SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT
@ SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT
Definition: SDL_messagebox.h:43
x
GLint GLint GLint GLint GLint x
Definition: SDL_opengl.h:1574
SDL_Log
#define SDL_Log
Definition: SDL_dynapi_overrides.h:31
EGL_NO_SURFACE
#define EGL_NO_SURFACE
Definition: egl.h:100
Android_JNI_MinizeWindow
void Android_JNI_MinizeWindow(void)
SDL_IsAndroidTV
SDL_bool SDL_IsAndroidTV(void)
SDL_Rect::y
int y
Definition: SDL_rect.h:79
SDL_Rect::h
int h
Definition: SDL_rect.h:80
SDL_free
#define SDL_free
Definition: SDL_dynapi_overrides.h:377
SDL_DisplayOrientation
SDL_DisplayOrientation
Definition: SDL_video.h:181
Android_JNI_FlushCapturedAudio
void Android_JNI_FlushCapturedAudio(void)
SDL_MessageBoxData::flags
Uint32 flags
Definition: SDL_messagebox.h:96
Android_SendResize
void Android_SendResize(SDL_Window *window)
SDL_MessageBoxData
MessageBox structure containing title, text, window, etc.
Definition: SDL_messagebox.h:95
SDL_IsChromebook
SDL_bool SDL_IsChromebook(void)
name
GLuint const GLchar * name
Definition: SDL_opengl_glext.h:663
Android_JNI_FileRead
size_t Android_JNI_FileRead(SDL_RWops *ctx, void *buffer, size_t size, size_t maxnum)
SDL_SendDropFile
int SDL_SendDropFile(SDL_Window *window, const char *file)
Definition: SDL_dropevents.c:80
Android_JNI_GetEnv
JNIEnv * Android_JNI_GetEnv(void)
SDL_GetDisplay
SDL_VideoDisplay * SDL_GetDisplay(int displayIndex)
Definition: SDL_video.c:1042
SDL_assert.h
text
static char text[MAX_TEXT_LENGTH]
Definition: testime.c:47
Android_JNI_FileSize
Sint64 Android_JNI_FileSize(SDL_RWops *ctx)
SDL_GetAndroidSDKVersion
int SDL_GetAndroidSDKVersion(void)
Android_JNI_AudioSetThreadPriority
void Android_JNI_AudioSetThreadPriority(int iscapture, int device_id)
Android_JNI_FileOpen
int Android_JNI_FileOpen(SDL_RWops *ctx, const char *fileName, const char *mode)
param
GLfloat param
Definition: SDL_opengl_glext.h:373
SDL_MessageBoxButtonData
Individual button data.
Definition: SDL_messagebox.h:59
Android_JNI_SupportsRelativeMouse
SDL_bool Android_JNI_SupportsRelativeMouse(void)
SDL_TRUE
@ SDL_TRUE
Definition: SDL_stdinc.h:164
SDL_FlushEvents
#define SDL_FlushEvents
Definition: SDL_dynapi_overrides.h:121
SDL_Delay
#define SDL_Delay
Definition: SDL_dynapi_overrides.h:486
SDL_SendDropComplete
int SDL_SendDropComplete(SDL_Window *window)
Definition: SDL_dropevents.c:92
Android_JNI_SetCustomCursor
SDL_bool Android_JNI_SetCustomCursor(int cursorID)
SDL_AudioSpec::freq
int freq
Definition: SDL_audio.h:180
Android_JNI_OpenAudioDevice
int Android_JNI_OpenAudioDevice(int iscapture, SDL_AudioSpec *spec)
Android_JNI_SetSystemCursor
SDL_bool Android_JNI_SetSystemCursor(int cursorID)
KMOD_SHIFT
#define KMOD_SHIFT
Definition: SDL_keycode.h:343
hot_x
int uint32_t uint32_t uint32_t uint32_t uint32_t int drmModeModeInfoPtr mode int uint32_t uint32_t uint32_t uint32_t int32_t hot_x
Definition: SDL_kmsdrmsym.h:57
SDL_assert
#define SDL_assert(condition)
Definition: SDL_assert.h:169
Android_JNI_FileClose
int Android_JNI_FileClose(SDL_RWops *ctx)
pixels
GLint GLint GLsizei GLsizei GLsizei GLint GLenum GLenum const GLvoid * pixels
Definition: SDL_opengl.h:1572
SDL_MessageBoxButtonData::buttonid
int buttonid
Definition: SDL_messagebox.h:61
SDL_TouchID
Sint64 SDL_TouchID
Definition: SDL_touch.h:41
axis
SDL_Texture * axis
Definition: testgamecontroller.c:67
Android_PauseSem
SDL_sem * Android_PauseSem
SDL_VideoDevice
Definition: SDL_sysvideo.h:150
SDL_OutOfMemory
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
size
GLsizeiptr size
Definition: SDL_opengl_glext.h:540
y
GLint GLint GLint GLint GLint GLint y
Definition: SDL_opengl.h:1574
spec
SDL_AudioSpec spec
Definition: loopwave.c:31
SDL_SCANCODE_UNKNOWN
@ SDL_SCANCODE_UNKNOWN
Definition: SDL_scancode.h:45
Android_JNI_PollHapticDevices
void Android_JNI_PollHapticDevices(void)
SDL_SCANCODE_LSHIFT
@ SDL_SCANCODE_LSHIFT
Definition: SDL_scancode.h:329
AndroidKeyInfo::mod
uint16_t mod
Definition: keyinfotable.h:38
Android_JNI_SendMessage
int Android_JNI_SendMessage(int command, int param)
SDL_AndroidGetActivity
#define SDL_AndroidGetActivity
Definition: SDL_dynapi_overrides.h:50
Android_SetScreenResolution
void Android_SetScreenResolution(int surfaceWidth, int surfaceHeight, int deviceWidth, int deviceHeight, Uint32 format, float rate)
Android_JNI_ShowTextInput
void Android_JNI_ShowTextInput(SDL_Rect *inputRect)
action
set set set set set set set set set set set set set set set set set set set set *set set set macro pixldst op &r &cond WK op &r &cond WK op &r &cond WK else op &m &cond &ia op &r &cond WK else op &m &cond &ia elseif elseif else error unsupported base if elseif elseif else error unsupported unaligned pixldst unaligned endm macro pixst base base else pixldst base endif endm macro PF base if bpp PF set rept prefetch_distance PF set OFFSET endr endif endm macro preload_leading_step2 base if bpp ifc DST PF PF else if bpp lsl PF PF lsl PF PF lsl PF PF PF else PF lsl PF lsl PF lsl PF endif SIZE macro preload_middle scratch_holds_offset if bpp if else PF PF endif endif endif endm macro preload_trailing base if bpp if bpp *pix_per_block PF PF lsl PF PF PF PF PF else PF lsl PF lsl PF PF PF PF PF base if bpp if narrow_case &&bpp<=dst_w_bpp) PF bic, WK0, base, #31 PF pld,[WK0] PF add, WK1, base, X, LSL #bpp_shift PF sub, WK1, WK1, #1 PF bic, WK1, WK1, #31 PF cmp, WK1, WK0 PF beq, 90f PF pld,[WK1]90:.else PF bic, WK0, base, #31 PF pld,[WK0] PF add, WK1, base, X, lsl #bpp_shift PF sub, WK1, WK1, #1 PF bic, WK1, WK1, #31 PF cmp, WK1, WK0 PF beq, 92f91:PF add, WK0, WK0, #32 PF cmp, WK0, WK1 PF pld,[WK0] PF bne, 91b92:.endif .endif.endm.macro conditional_process1_helper cond, process_head, process_tail, numbytes, firstreg, unaligned_src, unaligned_mask, decrementx process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, 0 .if decrementx sub &cond X, X, #8 *numbytes/dst_w_bpp .endif process_tail cond, numbytes, firstreg .if !((flags) &FLAG_PROCESS_DOES_STORE) pixst cond, numbytes, firstreg, DST .endif.endm.macro conditional_process1 cond, process_head, process_tail, numbytes, firstreg, unaligned_src, unaligned_mask, decrementx .if(flags) &FLAG_BRANCH_OVER .ifc cond, mi bpl 100f .endif .ifc cond, cs bcc 100f .endif .ifc cond, ne beq 100f .endif conditional_process1_helper, process_head, process_tail, numbytes, firstreg, unaligned_src, unaligned_mask, decrementx100:.else conditional_process1_helper cond, process_head, process_tail, numbytes, firstreg, unaligned_src, unaligned_mask, decrementx .endif.endm.macro conditional_process2 test, cond1, cond2, process_head, process_tail, numbytes1, numbytes2, firstreg1, firstreg2, unaligned_src, unaligned_mask, decrementx .if(flags) &(FLAG_DST_READWRITE|FLAG_BRANCH_OVER|FLAG_PROCESS_CORRUPTS_PSR|FLAG_PROCESS_DOES_STORE) test conditional_process1 cond1, process_head, process_tail, numbytes1, firstreg1, unaligned_src, unaligned_mask, decrementx .if(flags) &FLAG_PROCESS_CORRUPTS_PSR test .endif conditional_process1 cond2, process_head, process_tail, numbytes2, firstreg2, unaligned_src, unaligned_mask, decrementx .else test process_head cond1, numbytes1, firstreg1, unaligned_src, unaligned_mask, 0 process_head cond2, numbytes2, firstreg2, unaligned_src, unaligned_mask, 0 .if decrementx sub &cond1 X, X, #8 *numbytes1/dst_w_bpp sub &cond2 X, X, #8 *numbytes2/dst_w_bpp .endif process_tail cond1, numbytes1, firstreg1 process_tail cond2, numbytes2, firstreg2 pixst cond1, numbytes1, firstreg1, DST pixst cond2, numbytes2, firstreg2, DST .endif.endm.macro test_bits_1_0_ptr .if(flags) &FLAG_PROCESS_CORRUPTS_WK0 movs SCRATCH, X, lsl #32-1 .else movs SCRATCH, WK0, lsl #32-1 .endif.endm.macro test_bits_3_2_ptr .if(flags) &FLAG_PROCESS_CORRUPTS_WK0 movs SCRATCH, X, lsl #32-3 .else movs SCRATCH, WK0, lsl #32-3 .endif.endm.macro leading_15bytes process_head, process_tail .set DECREMENT_X, 1 .if(flags) &FLAG_PROCESS_CORRUPTS_WK0 .set DECREMENT_X, 0 sub X, X, WK0, lsr #dst_bpp_shift str X,[sp, #LINE_SAVED_REG_COUNT *4] mov X, WK0 .endif .if dst_w_bpp==8 conditional_process2 test_bits_1_0_ptr, mi, cs, process_head, process_tail, 1, 2, 1, 2, 1, 1, DECREMENT_X .elseif dst_w_bpp==16 test_bits_1_0_ptr conditional_process1 cs, process_head, process_tail, 2, 2, 1, 1, DECREMENT_X .endif conditional_process2 test_bits_3_2_ptr, mi, cs, process_head, process_tail, 4, 8, 1, 2, 1, 1, DECREMENT_X .if(flags) &FLAG_PROCESS_CORRUPTS_WK0 ldr X,[sp, #LINE_SAVED_REG_COUNT *4] .endif.endm.macro test_bits_3_2_pix movs SCRATCH, X, lsl #dst_bpp_shift+32-3.endm.macro test_bits_1_0_pix .if dst_w_bpp==8 movs SCRATCH, X, lsl #dst_bpp_shift+32-1 .else movs SCRATCH, X, lsr #1 .endif.endm.macro trailing_15bytes process_head, process_tail, unaligned_src, unaligned_mask conditional_process2 test_bits_3_2_pix, cs, mi, process_head, process_tail, 8, 4, 0, 2, unaligned_src, unaligned_mask, 0 .if dst_w_bpp==16 test_bits_1_0_pix conditional_process1 cs, process_head, process_tail, 2, 0, unaligned_src, unaligned_mask, 0 .elseif dst_w_bpp==8 conditional_process2 test_bits_1_0_pix, cs, mi, process_head, process_tail, 2, 1, 0, 1, unaligned_src, unaligned_mask, 0 .endif.endm.macro wide_case_inner_loop process_head, process_tail, unaligned_src, unaligned_mask, dst_alignment110:.set SUBBLOCK, 0 .rept pix_per_block *dst_w_bpp/128 process_head, 16, 0, unaligned_src, unaligned_mask, 1 .if(src_bpp > 0) &&(mask_bpp==0) &&((flags) &FLAG_PROCESS_PRESERVES_SCRATCH) preload_middle src_bpp, SRC, 1 .elseif(src_bpp==0) &&(mask_bpp > 0) &&((flags) &FLAG_PROCESS_PRESERVES_SCRATCH) preload_middle mask_bpp, MASK, 1 .else preload_middle src_bpp, SRC, 0 preload_middle mask_bpp, MASK, 0 .endif .if(dst_r_bpp > 0) &&((SUBBLOCK % 2)==0) &&(((flags) &FLAG_NO_PRELOAD_DST)==0) PF pld,[DST, #32 *prefetch_distance - dst_alignment] .endif process_tail, 16, 0 .if !((flags) &FLAG_PROCESS_DOES_STORE) pixst, 16, 0, DST .endif .set SUBBLOCK, SUBBLOCK+1 .endr subs X, X, #pix_per_block bhs 110b.endm.macro wide_case_inner_loop_and_trailing_pixels process_head, process_tail, process_inner_loop, exit_label, unaligned_src, unaligned_mask .if dst_r_bpp > tst bne process_inner_loop DST_PRELOAD_BIAS endif preload_trailing SRC preload_trailing MASK DST endif add medium_case_inner_loop_and_trailing_pixels unaligned_mask endm macro medium_case_inner_loop_and_trailing_pixels DST endif subs bhs tst beq exit_label trailing_15bytes unaligned_mask endm macro narrow_case_inner_loop_and_trailing_pixels unaligned_mask tst conditional_process1 trailing_15bytes unaligned_mask endm macro switch_on_alignment action
Definition: pixman-arm-simd-asm.h:510
SDL_arraysize
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:115
SDL_MessageBoxData::buttons
const SDL_MessageBoxButtonData * buttons
Definition: SDL_messagebox.h:102
SDL_atoi
#define SDL_atoi
Definition: SDL_dynapi_overrides.h:410
SDL_Window::driverdata
void * driverdata
Definition: SDL_sysvideo.h:112
SDL_IsDeXMode
SDL_bool SDL_IsDeXMode(void)
Android_ActivityMutex_Unlock
void Android_ActivityMutex_Unlock(void)
Android_JNI_RequestPermission
SDL_bool Android_JNI_RequestPermission(const char *permission)
SDL_MessageBoxColor::g
Uint8 g
Definition: SDL_messagebox.h:70
RW_SEEK_SET
#define RW_SEEK_SET
Definition: SDL_rwops.h:174
Android_JNI_SuspendScreenSaver
void Android_JNI_SuspendScreenSaver(SDL_bool suspend)
value
GLsizei const GLfloat * value
Definition: SDL_opengl_glext.h:701
AUDIO_S16
#define AUDIO_S16
Definition: SDL_audio.h:96
SDL_VideoDisplay
Definition: SDL_sysvideo.h:127
Android_JNI_CloseAudioDevice
void Android_JNI_CloseAudioDevice(const int iscapture)
SDL_SetError
#define SDL_SetError
Definition: SDL_dynapi_overrides.h:30
Android_JNI_HasClipboardText
SDL_bool Android_JNI_HasClipboardText(void)
Android_JNI_PollInputDevices
void Android_JNI_PollInputDevices(void)
Android_JNI_GetNativeWindow
ANativeWindow * Android_JNI_GetNativeWindow(void)
s
GLdouble s
Definition: SDL_opengl.h:2063
SDL_Rect
A rectangle, with the origin at the upper left (integer).
Definition: SDL_rect.h:78
SDL_MessageBoxButtonData::text
const char * text
Definition: SDL_messagebox.h:62
SDL_system.h
Android_OnKeyUp
int Android_OnKeyUp(int keycode)
SDL_stdinc.h
Android_JNI_HideTextInput
void Android_JNI_HideTextInput(void)
SDL_hints.h
SDL_atomic_t
A type representing an atomic integer value. It is a struct so people don't accidentally use numeric ...
Definition: SDL_atomic.h:216
Android_JNI_WriteAudioBuffer
void Android_JNI_WriteAudioBuffer(void)
Android_JNI_FileWrite
size_t Android_JNI_FileWrite(SDL_RWops *ctx, const void *buffer, size_t size, size_t num)
SDL_DestroyMutex
#define SDL_DestroyMutex
Definition: SDL_dynapi_overrides.h:263
RW_SEEK_CUR
#define RW_SEEK_CUR
Definition: SDL_rwops.h:175
SDL_SendWindowEvent
int SDL_SendWindowEvent(SDL_Window *window, Uint8 windowevent, int data1, int data2)
Definition: SDL_windowevents.c:74
SDL_FreeRW
#define SDL_FreeRW
Definition: SDL_dynapi_overrides.h:355
SDL_AndroidGetJNIEnv
#define SDL_AndroidGetJNIEnv
Definition: SDL_dynapi_overrides.h:49
pathString
GLenum GLsizei const void * pathString
Definition: SDL_opengl_glext.h:9162
SDL_strdup
#define SDL_strdup
Definition: SDL_dynapi_overrides.h:397
SDL_DestroySemaphore
#define SDL_DestroySemaphore
Definition: SDL_dynapi_overrides.h:265
Android_JNI_HapticStop
void Android_JNI_HapticStop(int device_id)
enabled
GLenum GLenum GLsizei const GLuint GLboolean enabled
Definition: SDL_opengl_glext.h:2482
SDL_main
SDLMAIN_DECLSPEC int SDL_main(int argc, char *argv[])
SDL_SemValue
#define SDL_SemValue
Definition: SDL_dynapi_overrides.h:270
Android_JNI_SetClipboardText
int Android_JNI_SetClipboardText(const char *text)
SDL_bool
SDL_bool
Definition: SDL_stdinc.h:162
Android_JNI_ShouldMinimizeOnFocusLoss
SDL_bool Android_JNI_ShouldMinimizeOnFocusLoss(void)
SDL_GetVideoDevice
SDL_VideoDevice * SDL_GetVideoDevice(void)
Definition: SDL_video.c:586
SDL_strrchr
#define SDL_strrchr
Definition: SDL_dynapi_overrides.h:402
SDL_MESSAGEBOX_COLOR_MAX
@ SDL_MESSAGEBOX_COLOR_MAX
Definition: SDL_messagebox.h:80
SDL_FALSE
@ SDL_FALSE
Definition: SDL_stdinc.h:163
SDL_AndroidGetExternalStorageState
#define SDL_AndroidGetExternalStorageState
Definition: SDL_dynapi_overrides.h:52
SDL_AtomicSet
#define SDL_AtomicSet
Definition: SDL_dynapi_overrides.h:67
SDL_EFSEEK
@ SDL_EFSEEK
Definition: SDL_error.h:60
string
GLsizei const GLchar *const * string
Definition: SDL_opengl_glext.h:691
SDL_AtomicGet
#define SDL_AtomicGet
Definition: SDL_dynapi_overrides.h:68
Android_JNI_InitTouch
void Android_JNI_InitTouch(void)
SDL_LASTEVENT
@ SDL_LASTEVENT
Definition: SDL_events.h:165
SDL_strcmp
#define SDL_strcmp
Definition: SDL_dynapi_overrides.h:417
fd
GLuint64 GLenum GLint fd
Definition: gl2ext.h:1508
SDL_UnlockMutex
#define SDL_UnlockMutex
Definition: SDL_dynapi_overrides.h:262
SDL_MessageBoxColor::r
Uint8 r
Definition: SDL_messagebox.h:70
SDL_WINDOWEVENT_FOCUS_GAINED
@ SDL_WINDOWEVENT_FOCUS_GAINED
Definition: SDL_video.h:164
SDL_SendAppEvent
int SDL_SendAppEvent(SDL_EventType eventType)
Definition: SDL_events.c:972
SDL_RWops
Definition: SDL_rwops.h:53
AUDIO_F32
#define AUDIO_F32
Definition: SDL_audio.h:114
unicharToAndroidKeyInfoTable
static AndroidKeyInfo unicharToAndroidKeyInfoTable[]
Definition: keyinfotable.h:42
ptr
set set set set set set set set set set set set set set set set set set set set *set set set macro pixldst op &r &cond WK op &r &cond WK op &r &cond WK else op &m &cond &ia op &r &cond WK else op &m &cond &ia elseif elseif else error unsupported base if elseif elseif else error unsupported unaligned pixldst unaligned endm macro pixst base base else pixldst base endif endm macro PF ptr
Definition: pixman-arm-simd-asm.h:171
state
struct xkb_state * state
Definition: SDL_waylandsym.h:114
SDL_SendQuit
int SDL_SendQuit(void)
Definition: SDL_quit.c:201
Android_JNI_CaptureAudioBuffer
int Android_JNI_CaptureAudioBuffer(void *buffer, int buflen)
Android_JNI_IsScreenKeyboardShown
SDL_bool Android_JNI_IsScreenKeyboardShown(void)
Android_Window
SDL_Window * Android_Window
button
SDL_Texture * button
Definition: testgamecontroller.c:67
SDL_MessageBoxData::numbuttons
int numbuttons
Definition: SDL_messagebox.h:101
i
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
SDL_AndroidGetInternalStoragePath
#define SDL_AndroidGetInternalStoragePath
Definition: SDL_dynapi_overrides.h:51
Android_JNI_GetDisplayDPI
int Android_JNI_GetDisplayDPI(float *ddpi, float *xdpi, float *ydpi)
w
GLubyte GLubyte GLubyte GLubyte w
Definition: SDL_opengl_glext.h:734
colors
static int colors[7]
Definition: testgesture.c:41
Android_ResumeSem
SDL_sem * Android_ResumeSem
Definition: SDL_androidvideo.h:45