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