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