SDL  2.0
SDL_android.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2016 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 
27 #ifdef __ANDROID__
28 
29 #include "SDL_system.h"
30 #include "SDL_android.h"
31 #include <EGL/egl.h>
32 
33 #include "../../events/SDL_events_c.h"
34 #include "../../video/android/SDL_androidkeyboard.h"
35 #include "../../video/android/SDL_androidmouse.h"
36 #include "../../video/android/SDL_androidtouch.h"
37 #include "../../video/android/SDL_androidvideo.h"
38 #include "../../video/android/SDL_androidwindow.h"
39 #include "../../joystick/android/SDL_sysjoystick_c.h"
40 
41 #include <android/log.h>
42 #include <pthread.h>
43 #include <sys/types.h>
44 #include <unistd.h>
45 #define LOG_TAG "SDL_android"
46 /* #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) */
47 /* #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) */
48 #define LOGI(...) do {} while (0)
49 #define LOGE(...) do {} while (0)
50 
51 /* Uncomment this to log messages entering and exiting methods in this file */
52 /* #define DEBUG_JNI */
53 
54 static void Android_JNI_ThreadDestroyed(void*);
55 
56 /*******************************************************************************
57  This file links the Java side of Android with libsdl
58 *******************************************************************************/
59 #include <jni.h>
60 #include <android/log.h>
61 
62 
63 /*******************************************************************************
64  Globals
65 *******************************************************************************/
66 static pthread_key_t mThreadKey;
67 static JavaVM* mJavaVM;
68 
69 /* Main activity */
70 static jclass mActivityClass;
71 
72 /* method signatures */
73 static jmethodID midGetNativeSurface;
74 static jmethodID midAudioInit;
75 static jmethodID midAudioWriteShortBuffer;
76 static jmethodID midAudioWriteByteBuffer;
77 static jmethodID midAudioQuit;
78 static jmethodID midPollInputDevices;
79 
80 /* Accelerometer data storage */
81 static float fLastAccelerometer[3];
82 static SDL_bool bHasNewData;
83 
84 /*******************************************************************************
85  Functions called by JNI
86 *******************************************************************************/
87 
88 /* Library init */
89 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
90 {
91  JNIEnv *env;
92  mJavaVM = vm;
93  LOGI("JNI_OnLoad called");
94  if ((*mJavaVM)->GetEnv(mJavaVM, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
95  LOGE("Failed to get the environment using GetEnv()");
96  return -1;
97  }
98  /*
99  * Create mThreadKey so we can keep track of the JNIEnv assigned to each thread
100  * Refer to http://developer.android.com/guide/practices/design/jni.html for the rationale behind this
101  */
102  if (pthread_key_create(&mThreadKey, Android_JNI_ThreadDestroyed) != 0) {
103  __android_log_print(ANDROID_LOG_ERROR, "SDL", "Error initializing pthread key");
104  }
106 
107  return JNI_VERSION_1_4;
108 }
109 
110 /* Called before SDL_main() to initialize JNI bindings */
111 JNIEXPORT void JNICALL SDL_Android_Init(JNIEnv* mEnv, jclass cls)
112 {
113  __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL_Android_Init()");
114 
116 
117  mActivityClass = (jclass)((*mEnv)->NewGlobalRef(mEnv, cls));
118 
119  midGetNativeSurface = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
120  "getNativeSurface","()Landroid/view/Surface;");
121  midAudioInit = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
122  "audioInit", "(IZZI)I");
123  midAudioWriteShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
124  "audioWriteShortBuffer", "([S)V");
125  midAudioWriteByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
126  "audioWriteByteBuffer", "([B)V");
127  midAudioQuit = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
128  "audioQuit", "()V");
129  midPollInputDevices = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
130  "pollInputDevices", "()V");
131 
132  bHasNewData = SDL_FALSE;
133 
134  if (!midGetNativeSurface || !midAudioInit ||
135  !midAudioWriteShortBuffer || !midAudioWriteByteBuffer || !midAudioQuit || !midPollInputDevices) {
136  __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL: Couldn't locate Java callbacks, check that they're named and typed correctly");
137  }
138  __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL_Android_Init() finished!");
139 }
140 
141 /* Drop file */
142 void Java_org_libsdl_app_SDLActivity_onNativeDropFile(
143  JNIEnv* env, jclass jcls,
144  jstring filename)
145 {
146  const char *path = (*env)->GetStringUTFChars(env, filename, NULL);
147  SDL_SendDropFile(path);
148  (*env)->ReleaseStringUTFChars(env, filename, path);
149 }
150 
151 /* Resize */
152 JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_onNativeResize(
153  JNIEnv* env, jclass jcls,
154  jint width, jint height, jint format, jfloat rate)
155 {
156  Android_SetScreenResolution(width, height, format, rate);
157 }
158 
159 /* Paddown */
160 JNIEXPORT jint JNICALL Java_org_libsdl_app_SDLActivity_onNativePadDown(
161  JNIEnv* env, jclass jcls,
162  jint device_id, jint keycode)
163 {
164  return Android_OnPadDown(device_id, keycode);
165 }
166 
167 /* Padup */
168 JNIEXPORT jint JNICALL Java_org_libsdl_app_SDLActivity_onNativePadUp(
169  JNIEnv* env, jclass jcls,
170  jint device_id, jint keycode)
171 {
172  return Android_OnPadUp(device_id, keycode);
173 }
174 
175 /* Joy */
176 JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_onNativeJoy(
177  JNIEnv* env, jclass jcls,
178  jint device_id, jint axis, jfloat value)
179 {
180  Android_OnJoy(device_id, axis, value);
181 }
182 
183 /* POV Hat */
184 JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_onNativeHat(
185  JNIEnv* env, jclass jcls,
186  jint device_id, jint hat_id, jint x, jint y)
187 {
188  Android_OnHat(device_id, hat_id, x, y);
189 }
190 
191 
192 JNIEXPORT jint JNICALL Java_org_libsdl_app_SDLActivity_nativeAddJoystick(
193  JNIEnv* env, jclass jcls,
194  jint device_id, jstring device_name, jint is_accelerometer,
195  jint nbuttons, jint naxes, jint nhats, jint nballs)
196 {
197  int retval;
198  const char *name = (*env)->GetStringUTFChars(env, device_name, NULL);
199 
200  retval = Android_AddJoystick(device_id, name, (SDL_bool) is_accelerometer, nbuttons, naxes, nhats, nballs);
201 
202  (*env)->ReleaseStringUTFChars(env, device_name, name);
203 
204  return retval;
205 }
206 
207 JNIEXPORT jint JNICALL Java_org_libsdl_app_SDLActivity_nativeRemoveJoystick(
208  JNIEnv* env, jclass jcls, jint device_id)
209 {
210  return Android_RemoveJoystick(device_id);
211 }
212 
213 
214 /* Surface Created */
215 JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_onNativeSurfaceChanged(JNIEnv* env, jclass jcls)
216 {
219 
221  return;
222  }
223 
224  _this = SDL_GetVideoDevice();
226 
227  /* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
228  if (data->egl_surface == EGL_NO_SURFACE) {
229  if(data->native_window) {
230  ANativeWindow_release(data->native_window);
231  }
233  data->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType) data->native_window);
234  }
235 
236  /* GL Context handling is done in the event loop because this function is run from the Java thread */
237 
238 }
239 
240 /* Surface Destroyed */
241 JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_onNativeSurfaceDestroyed(JNIEnv* env, jclass jcls)
242 {
243  /* We have to clear the current context and destroy the egl surface here
244  * Otherwise there's BAD_NATIVE_WINDOW errors coming from eglCreateWindowSurface on resume
245  * Ref: http://stackoverflow.com/questions/8762589/eglcreatewindowsurface-on-ics-and-switching-from-2d-to-3d
246  */
249 
251  return;
252  }
253 
254  _this = SDL_GetVideoDevice();
256 
257  if (data->egl_surface != EGL_NO_SURFACE) {
258  SDL_EGL_MakeCurrent(_this, NULL, NULL);
259  SDL_EGL_DestroySurface(_this, data->egl_surface);
260  data->egl_surface = EGL_NO_SURFACE;
261  }
262 
263  /* GL Context handling is done in the event loop because this function is run from the Java thread */
264 
265 }
266 
267 /* Keydown */
268 JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_onNativeKeyDown(
269  JNIEnv* env, jclass jcls, jint keycode)
270 {
271  Android_OnKeyDown(keycode);
272 }
273 
274 /* Keyup */
275 JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_onNativeKeyUp(
276  JNIEnv* env, jclass jcls, jint keycode)
277 {
278  Android_OnKeyUp(keycode);
279 }
280 
281 /* Keyboard Focus Lost */
282 JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_onNativeKeyboardFocusLost(
283  JNIEnv* env, jclass jcls)
284 {
285  /* Calling SDL_StopTextInput will take care of hiding the keyboard and cleaning up the DummyText widget */
287 }
288 
289 
290 /* Touch */
291 JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_onNativeTouch(
292  JNIEnv* env, jclass jcls,
293  jint touch_device_id_in, jint pointer_finger_id_in,
294  jint action, jfloat x, jfloat y, jfloat p)
295 {
296  Android_OnTouch(touch_device_id_in, pointer_finger_id_in, action, x, y, p);
297 }
298 
299 /* Mouse */
300 JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_onNativeMouse(
301  JNIEnv* env, jclass jcls,
302  jint button, jint action, jfloat x, jfloat y)
303 {
304  Android_OnMouse(button, action, x, y);
305 }
306 
307 /* Accelerometer */
308 JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_onNativeAccel(
309  JNIEnv* env, jclass jcls,
310  jfloat x, jfloat y, jfloat z)
311 {
312  fLastAccelerometer[0] = x;
313  fLastAccelerometer[1] = y;
314  fLastAccelerometer[2] = z;
315  bHasNewData = SDL_TRUE;
316 }
317 
318 /* Low memory */
319 JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_nativeLowMemory(
320  JNIEnv* env, jclass cls)
321 {
323 }
324 
325 /* Quit */
326 JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_nativeQuit(
327  JNIEnv* env, jclass cls)
328 {
329  /* Discard previous events. The user should have handled state storage
330  * in SDL_APP_WILLENTERBACKGROUND. After nativeQuit() is called, no
331  * events other than SDL_QUIT and SDL_APP_TERMINATING should fire */
333  /* Inject a SDL_QUIT event */
334  SDL_SendQuit();
336  /* Resume the event loop so that the app can catch SDL_QUIT which
337  * should now be the top event in the event queue. */
339 }
340 
341 /* Pause */
342 JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_nativePause(
343  JNIEnv* env, jclass cls)
344 {
345  __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativePause()");
346  if (Android_Window) {
351 
352  /* *After* sending the relevant events, signal the pause semaphore
353  * so the event loop knows to pause and (optionally) block itself */
355  }
356 }
357 
358 /* Resume */
359 JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_nativeResume(
360  JNIEnv* env, jclass cls)
361 {
362  __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeResume()");
363 
364  if (Android_Window) {
369  /* Signal the resume semaphore so the event loop knows to resume and restore the GL Context
370  * We can't restore the GL Context here because it needs to be done on the SDL main thread
371  * and this function will be called from the Java thread instead.
372  */
374  }
375 }
376 
377 JNIEXPORT void JNICALL Java_org_libsdl_app_SDLInputConnection_nativeCommitText(
378  JNIEnv* env, jclass cls,
379  jstring text, jint newCursorPosition)
380 {
381  const char *utftext = (*env)->GetStringUTFChars(env, text, NULL);
382 
383  SDL_SendKeyboardText(utftext);
384 
385  (*env)->ReleaseStringUTFChars(env, text, utftext);
386 }
387 
388 JNIEXPORT void JNICALL Java_org_libsdl_app_SDLInputConnection_nativeSetComposingText(
389  JNIEnv* env, jclass cls,
390  jstring text, jint newCursorPosition)
391 {
392  const char *utftext = (*env)->GetStringUTFChars(env, text, NULL);
393 
394  SDL_SendEditingText(utftext, 0, 0);
395 
396  (*env)->ReleaseStringUTFChars(env, text, utftext);
397 }
398 
399 JNIEXPORT jstring JNICALL Java_org_libsdl_app_SDLActivity_nativeGetHint(JNIEnv* env, jclass cls, jstring name) {
400  const char *utfname = (*env)->GetStringUTFChars(env, name, NULL);
401  const char *hint = SDL_GetHint(utfname);
402 
403  jstring result = (*env)->NewStringUTF(env, hint);
404  (*env)->ReleaseStringUTFChars(env, name, utfname);
405 
406  return result;
407 }
408 
409 /*******************************************************************************
410  Functions called by SDL into Java
411 *******************************************************************************/
412 
413 static int s_active = 0;
414 struct LocalReferenceHolder
415 {
416  JNIEnv *m_env;
417  const char *m_func;
418 };
419 
420 static struct LocalReferenceHolder LocalReferenceHolder_Setup(const char *func)
421 {
422  struct LocalReferenceHolder refholder;
423  refholder.m_env = NULL;
424  refholder.m_func = func;
425 #ifdef DEBUG_JNI
426  SDL_Log("Entering function %s", func);
427 #endif
428  return refholder;
429 }
430 
431 static SDL_bool LocalReferenceHolder_Init(struct LocalReferenceHolder *refholder, JNIEnv *env)
432 {
433  const int capacity = 16;
434  if ((*env)->PushLocalFrame(env, capacity) < 0) {
435  SDL_SetError("Failed to allocate enough JVM local references");
436  return SDL_FALSE;
437  }
438  ++s_active;
439  refholder->m_env = env;
440  return SDL_TRUE;
441 }
442 
443 static void LocalReferenceHolder_Cleanup(struct LocalReferenceHolder *refholder)
444 {
445 #ifdef DEBUG_JNI
446  SDL_Log("Leaving function %s", refholder->m_func);
447 #endif
448  if (refholder->m_env) {
449  JNIEnv* env = refholder->m_env;
450  (*env)->PopLocalFrame(env, NULL);
451  --s_active;
452  }
453 }
454 
455 static SDL_bool LocalReferenceHolder_IsActive(void)
456 {
457  return s_active > 0;
458 }
459 
460 ANativeWindow* Android_JNI_GetNativeWindow(void)
461 {
462  ANativeWindow* anw;
463  jobject s;
464  JNIEnv *env = Android_JNI_GetEnv();
465 
466  s = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetNativeSurface);
467  anw = ANativeWindow_fromSurface(env, s);
468  (*env)->DeleteLocalRef(env, s);
469 
470  return anw;
471 }
472 
473 void Android_JNI_SetActivityTitle(const char *title)
474 {
475  jmethodID mid;
476  JNIEnv *mEnv = Android_JNI_GetEnv();
477  mid = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,"setActivityTitle","(Ljava/lang/String;)Z");
478  if (mid) {
479  jstring jtitle = (jstring)((*mEnv)->NewStringUTF(mEnv, title));
480  (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, mid, jtitle);
481  (*mEnv)->DeleteLocalRef(mEnv, jtitle);
482  }
483 }
484 
486 {
487  int i;
488  SDL_bool retval = SDL_FALSE;
489 
490  if (bHasNewData) {
491  for (i = 0; i < 3; ++i) {
492  values[i] = fLastAccelerometer[i];
493  }
494  bHasNewData = SDL_FALSE;
495  retval = SDL_TRUE;
496  }
497 
498  return retval;
499 }
500 
501 static void Android_JNI_ThreadDestroyed(void* value)
502 {
503  /* The thread is being destroyed, detach it from the Java VM and set the mThreadKey value to NULL as required */
504  JNIEnv *env = (JNIEnv*) value;
505  if (env != NULL) {
506  (*mJavaVM)->DetachCurrentThread(mJavaVM);
507  pthread_setspecific(mThreadKey, NULL);
508  }
509 }
510 
511 JNIEnv* Android_JNI_GetEnv(void)
512 {
513  /* From http://developer.android.com/guide/practices/jni.html
514  * All threads are Linux threads, scheduled by the kernel.
515  * They're usually started from managed code (using Thread.start), but they can also be created elsewhere and then
516  * attached to the JavaVM. For example, a thread started with pthread_create can be attached with the
517  * JNI AttachCurrentThread or AttachCurrentThreadAsDaemon functions. Until a thread is attached, it has no JNIEnv,
518  * and cannot make JNI calls.
519  * Attaching a natively-created thread causes a java.lang.Thread object to be constructed and added to the "main"
520  * ThreadGroup, making it visible to the debugger. Calling AttachCurrentThread on an already-attached thread
521  * is a no-op.
522  * Note: You can call this function any number of times for the same thread, there's no harm in it
523  */
524 
525  JNIEnv *env;
526  int status = (*mJavaVM)->AttachCurrentThread(mJavaVM, &env, NULL);
527  if(status < 0) {
528  LOGE("failed to attach current thread");
529  return 0;
530  }
531 
532  /* From http://developer.android.com/guide/practices/jni.html
533  * Threads attached through JNI must call DetachCurrentThread before they exit. If coding this directly is awkward,
534  * in Android 2.0 (Eclair) and higher you can use pthread_key_create to define a destructor function that will be
535  * called before the thread exits, and call DetachCurrentThread from there. (Use that key with pthread_setspecific
536  * to store the JNIEnv in thread-local-storage; that way it'll be passed into your destructor as the argument.)
537  * Note: The destructor is not called unless the stored value is != NULL
538  * Note: You can call this function any number of times for the same thread, there's no harm in it
539  * (except for some lost CPU cycles)
540  */
541  pthread_setspecific(mThreadKey, (void*) env);
542 
543  return env;
544 }
545 
546 int Android_JNI_SetupThread(void)
547 {
549  return 1;
550 }
551 
552 /*
553  * Audio support
554  */
555 static jboolean audioBuffer16Bit = JNI_FALSE;
556 static jobject audioBuffer = NULL;
557 static void* audioBufferPinned = NULL;
558 
559 int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames)
560 {
561  jboolean audioBufferStereo;
562  int audioBufferFrames;
563 
564  JNIEnv *env = Android_JNI_GetEnv();
565 
566  if (!env) {
567  LOGE("callback_handler: failed to attach current thread");
568  }
570 
571  __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device");
572  audioBuffer16Bit = is16Bit;
573  audioBufferStereo = channelCount > 1;
574 
575  if ((*env)->CallStaticIntMethod(env, mActivityClass, midAudioInit, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames) != 0) {
576  /* Error during audio initialization */
577  __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: error on AudioTrack initialization!");
578  return 0;
579  }
580 
581  /* Allocating the audio buffer from the Java side and passing it as the return value for audioInit no longer works on
582  * Android >= 4.2 due to a "stale global reference" error. So now we allocate this buffer directly from this side. */
583 
584  if (is16Bit) {
585  jshortArray audioBufferLocal = (*env)->NewShortArray(env, desiredBufferFrames * (audioBufferStereo ? 2 : 1));
586  if (audioBufferLocal) {
587  audioBuffer = (*env)->NewGlobalRef(env, audioBufferLocal);
588  (*env)->DeleteLocalRef(env, audioBufferLocal);
589  }
590  }
591  else {
592  jbyteArray audioBufferLocal = (*env)->NewByteArray(env, desiredBufferFrames * (audioBufferStereo ? 2 : 1));
593  if (audioBufferLocal) {
594  audioBuffer = (*env)->NewGlobalRef(env, audioBufferLocal);
595  (*env)->DeleteLocalRef(env, audioBufferLocal);
596  }
597  }
598 
599  if (audioBuffer == NULL) {
600  __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: could not allocate an audio buffer!");
601  return 0;
602  }
603 
604  jboolean isCopy = JNI_FALSE;
605  if (audioBuffer16Bit) {
606  audioBufferPinned = (*env)->GetShortArrayElements(env, (jshortArray)audioBuffer, &isCopy);
607  audioBufferFrames = (*env)->GetArrayLength(env, (jshortArray)audioBuffer);
608  } else {
609  audioBufferPinned = (*env)->GetByteArrayElements(env, (jbyteArray)audioBuffer, &isCopy);
610  audioBufferFrames = (*env)->GetArrayLength(env, (jbyteArray)audioBuffer);
611  }
612  if (audioBufferStereo) {
613  audioBufferFrames /= 2;
614  }
615 
616  return audioBufferFrames;
617 }
618 
619 void * Android_JNI_GetAudioBuffer(void)
620 {
621  return audioBufferPinned;
622 }
623 
625 {
626  JNIEnv *mAudioEnv = Android_JNI_GetEnv();
627 
628  if (audioBuffer16Bit) {
629  (*mAudioEnv)->ReleaseShortArrayElements(mAudioEnv, (jshortArray)audioBuffer, (jshort *)audioBufferPinned, JNI_COMMIT);
630  (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mActivityClass, midAudioWriteShortBuffer, (jshortArray)audioBuffer);
631  } else {
632  (*mAudioEnv)->ReleaseByteArrayElements(mAudioEnv, (jbyteArray)audioBuffer, (jbyte *)audioBufferPinned, JNI_COMMIT);
633  (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mActivityClass, midAudioWriteByteBuffer, (jbyteArray)audioBuffer);
634  }
635 
636  /* JNI_COMMIT means the changes are committed to the VM but the buffer remains pinned */
637 }
638 
640 {
641  JNIEnv *env = Android_JNI_GetEnv();
642 
643  (*env)->CallStaticVoidMethod(env, mActivityClass, midAudioQuit);
644 
645  if (audioBuffer) {
646  (*env)->DeleteGlobalRef(env, audioBuffer);
647  audioBuffer = NULL;
648  audioBufferPinned = NULL;
649  }
650 }
651 
652 /* Test for an exception and call SDL_SetError with its detail if one occurs */
653 /* If the parameter silent is truthy then SDL_SetError() will not be called. */
654 static SDL_bool Android_JNI_ExceptionOccurred(SDL_bool silent)
655 {
656  SDL_assert(LocalReferenceHolder_IsActive());
657  JNIEnv *mEnv = Android_JNI_GetEnv();
658 
659  jthrowable exception = (*mEnv)->ExceptionOccurred(mEnv);
660  if (exception != NULL) {
661  jmethodID mid;
662 
663  /* Until this happens most JNI operations have undefined behaviour */
664  (*mEnv)->ExceptionClear(mEnv);
665 
666  if (!silent) {
667  jclass exceptionClass = (*mEnv)->GetObjectClass(mEnv, exception);
668  jclass classClass = (*mEnv)->FindClass(mEnv, "java/lang/Class");
669 
670  mid = (*mEnv)->GetMethodID(mEnv, classClass, "getName", "()Ljava/lang/String;");
671  jstring exceptionName = (jstring)(*mEnv)->CallObjectMethod(mEnv, exceptionClass, mid);
672  const char* exceptionNameUTF8 = (*mEnv)->GetStringUTFChars(mEnv, exceptionName, 0);
673 
674  mid = (*mEnv)->GetMethodID(mEnv, exceptionClass, "getMessage", "()Ljava/lang/String;");
675  jstring exceptionMessage = (jstring)(*mEnv)->CallObjectMethod(mEnv, exception, mid);
676 
677  if (exceptionMessage != NULL) {
678  const char* exceptionMessageUTF8 = (*mEnv)->GetStringUTFChars(mEnv, exceptionMessage, 0);
679  SDL_SetError("%s: %s", exceptionNameUTF8, exceptionMessageUTF8);
680  (*mEnv)->ReleaseStringUTFChars(mEnv, exceptionMessage, exceptionMessageUTF8);
681  } else {
682  SDL_SetError("%s", exceptionNameUTF8);
683  }
684 
685  (*mEnv)->ReleaseStringUTFChars(mEnv, exceptionName, exceptionNameUTF8);
686  }
687 
688  return SDL_TRUE;
689  }
690 
691  return SDL_FALSE;
692 }
693 
694 static int Internal_Android_JNI_FileOpen(SDL_RWops* ctx)
695 {
696  struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
697 
698  int result = 0;
699 
700  jmethodID mid;
701  jobject context;
702  jobject assetManager;
703  jobject inputStream;
704  jclass channels;
705  jobject readableByteChannel;
706  jstring fileNameJString;
707  jobject fd;
708  jclass fdCls;
709  jfieldID descriptor;
710 
711  JNIEnv *mEnv = Android_JNI_GetEnv();
712  if (!LocalReferenceHolder_Init(&refs, mEnv)) {
713  goto failure;
714  }
715 
716  fileNameJString = (jstring)ctx->hidden.androidio.fileNameRef;
717  ctx->hidden.androidio.position = 0;
718 
719  /* context = SDLActivity.getContext(); */
720  mid = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
721  "getContext","()Landroid/content/Context;");
722  context = (*mEnv)->CallStaticObjectMethod(mEnv, mActivityClass, mid);
723 
724 
725  /* assetManager = context.getAssets(); */
726  mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, context),
727  "getAssets", "()Landroid/content/res/AssetManager;");
728  assetManager = (*mEnv)->CallObjectMethod(mEnv, context, mid);
729 
730  /* First let's try opening the file to obtain an AssetFileDescriptor.
731  * This method reads the files directly from the APKs using standard *nix calls
732  */
733  mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, assetManager), "openFd", "(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;");
734  inputStream = (*mEnv)->CallObjectMethod(mEnv, assetManager, mid, fileNameJString);
735  if (Android_JNI_ExceptionOccurred(SDL_TRUE)) {
736  goto fallback;
737  }
738 
739  mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream), "getStartOffset", "()J");
740  ctx->hidden.androidio.offset = (*mEnv)->CallLongMethod(mEnv, inputStream, mid);
741  if (Android_JNI_ExceptionOccurred(SDL_TRUE)) {
742  goto fallback;
743  }
744 
745  mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream), "getDeclaredLength", "()J");
746  ctx->hidden.androidio.size = (*mEnv)->CallLongMethod(mEnv, inputStream, mid);
747  if (Android_JNI_ExceptionOccurred(SDL_TRUE)) {
748  goto fallback;
749  }
750 
751  mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream), "getFileDescriptor", "()Ljava/io/FileDescriptor;");
752  fd = (*mEnv)->CallObjectMethod(mEnv, inputStream, mid);
753  fdCls = (*mEnv)->GetObjectClass(mEnv, fd);
754  descriptor = (*mEnv)->GetFieldID(mEnv, fdCls, "descriptor", "I");
755  ctx->hidden.androidio.fd = (*mEnv)->GetIntField(mEnv, fd, descriptor);
756  ctx->hidden.androidio.assetFileDescriptorRef = (*mEnv)->NewGlobalRef(mEnv, inputStream);
757 
758  /* Seek to the correct offset in the file. */
759  lseek(ctx->hidden.androidio.fd, (off_t)ctx->hidden.androidio.offset, SEEK_SET);
760 
761  if (0) {
762 fallback:
763  /* Disabled log message because of spam on the Nexus 7 */
764  /* __android_log_print(ANDROID_LOG_DEBUG, "SDL", "Falling back to legacy InputStream method for opening file"); */
765 
766  /* Try the old method using InputStream */
767  ctx->hidden.androidio.assetFileDescriptorRef = NULL;
768 
769  /* inputStream = assetManager.open(<filename>); */
770  mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, assetManager),
771  "open", "(Ljava/lang/String;I)Ljava/io/InputStream;");
772  inputStream = (*mEnv)->CallObjectMethod(mEnv, assetManager, mid, fileNameJString, 1 /* ACCESS_RANDOM */);
773  if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
774  /* Try fallback to APK expansion files */
775  mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, context),
776  "openAPKExpansionInputStream", "(Ljava/lang/String;)Ljava/io/InputStream;");
777  if (!mid) {
778  SDL_SetError("No openAPKExpansionInputStream() in Java class");
779  goto failure; /* Java class is missing the required method */
780  }
781  inputStream = (*mEnv)->CallObjectMethod(mEnv, context, mid, fileNameJString);
782 
783  /* Exception is checked first because it always needs to be cleared.
784  * If no exception occurred then the last SDL error message is kept.
785  */
786  if (Android_JNI_ExceptionOccurred(SDL_FALSE) || !inputStream) {
787  goto failure;
788  }
789  }
790 
791  ctx->hidden.androidio.inputStreamRef = (*mEnv)->NewGlobalRef(mEnv, inputStream);
792 
793  /* Despite all the visible documentation on [Asset]InputStream claiming
794  * that the .available() method is not guaranteed to return the entire file
795  * size, comments in <sdk>/samples/<ver>/ApiDemos/src/com/example/ ...
796  * android/apis/content/ReadAsset.java imply that Android's
797  * AssetInputStream.available() /will/ always return the total file size
798  */
799 
800  /* size = inputStream.available(); */
801  mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
802  "available", "()I");
803  ctx->hidden.androidio.size = (long)(*mEnv)->CallIntMethod(mEnv, inputStream, mid);
804  if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
805  goto failure;
806  }
807 
808  /* readableByteChannel = Channels.newChannel(inputStream); */
809  channels = (*mEnv)->FindClass(mEnv, "java/nio/channels/Channels");
810  mid = (*mEnv)->GetStaticMethodID(mEnv, channels,
811  "newChannel",
812  "(Ljava/io/InputStream;)Ljava/nio/channels/ReadableByteChannel;");
813  readableByteChannel = (*mEnv)->CallStaticObjectMethod(
814  mEnv, channels, mid, inputStream);
815  if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
816  goto failure;
817  }
818 
819  ctx->hidden.androidio.readableByteChannelRef =
820  (*mEnv)->NewGlobalRef(mEnv, readableByteChannel);
821 
822  /* Store .read id for reading purposes */
823  mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, readableByteChannel),
824  "read", "(Ljava/nio/ByteBuffer;)I");
825  ctx->hidden.androidio.readMethod = mid;
826  }
827 
828  if (0) {
829 failure:
830  result = -1;
831 
832  (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.fileNameRef);
833 
834  if(ctx->hidden.androidio.inputStreamRef != NULL) {
835  (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.inputStreamRef);
836  }
837 
838  if(ctx->hidden.androidio.readableByteChannelRef != NULL) {
839  (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.readableByteChannelRef);
840  }
841 
842  if(ctx->hidden.androidio.assetFileDescriptorRef != NULL) {
843  (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.assetFileDescriptorRef);
844  }
845 
846  }
847 
848  LocalReferenceHolder_Cleanup(&refs);
849  return result;
850 }
851 
853  const char* fileName, const char* mode)
854 {
855  struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
856  JNIEnv *mEnv = Android_JNI_GetEnv();
857  int retval;
858 
859  if (!LocalReferenceHolder_Init(&refs, mEnv)) {
860  LocalReferenceHolder_Cleanup(&refs);
861  return -1;
862  }
863 
864  if (!ctx) {
865  LocalReferenceHolder_Cleanup(&refs);
866  return -1;
867  }
868 
869  jstring fileNameJString = (*mEnv)->NewStringUTF(mEnv, fileName);
870  ctx->hidden.androidio.fileNameRef = (*mEnv)->NewGlobalRef(mEnv, fileNameJString);
871  ctx->hidden.androidio.inputStreamRef = NULL;
872  ctx->hidden.androidio.readableByteChannelRef = NULL;
873  ctx->hidden.androidio.readMethod = NULL;
874  ctx->hidden.androidio.assetFileDescriptorRef = NULL;
875 
876  retval = Internal_Android_JNI_FileOpen(ctx);
877  LocalReferenceHolder_Cleanup(&refs);
878  return retval;
879 }
880 
881 size_t Android_JNI_FileRead(SDL_RWops* ctx, void* buffer,
882  size_t size, size_t maxnum)
883 {
884  struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
885 
886  if (ctx->hidden.androidio.assetFileDescriptorRef) {
887  size_t bytesMax = size * maxnum;
888  if (ctx->hidden.androidio.size != -1 /* UNKNOWN_LENGTH */ && ctx->hidden.androidio.position + bytesMax > ctx->hidden.androidio.size) {
889  bytesMax = ctx->hidden.androidio.size - ctx->hidden.androidio.position;
890  }
891  size_t result = read(ctx->hidden.androidio.fd, buffer, bytesMax );
892  if (result > 0) {
893  ctx->hidden.androidio.position += result;
894  LocalReferenceHolder_Cleanup(&refs);
895  return result / size;
896  }
897  LocalReferenceHolder_Cleanup(&refs);
898  return 0;
899  } else {
900  jlong bytesRemaining = (jlong) (size * maxnum);
901  jlong bytesMax = (jlong) (ctx->hidden.androidio.size - ctx->hidden.androidio.position);
902  int bytesRead = 0;
903 
904  /* Don't read more bytes than those that remain in the file, otherwise we get an exception */
905  if (bytesRemaining > bytesMax) bytesRemaining = bytesMax;
906 
907  JNIEnv *mEnv = Android_JNI_GetEnv();
908  if (!LocalReferenceHolder_Init(&refs, mEnv)) {
909  LocalReferenceHolder_Cleanup(&refs);
910  return 0;
911  }
912 
913  jobject readableByteChannel = (jobject)ctx->hidden.androidio.readableByteChannelRef;
914  jmethodID readMethod = (jmethodID)ctx->hidden.androidio.readMethod;
915  jobject byteBuffer = (*mEnv)->NewDirectByteBuffer(mEnv, buffer, bytesRemaining);
916 
917  while (bytesRemaining > 0) {
918  /* result = readableByteChannel.read(...); */
919  int result = (*mEnv)->CallIntMethod(mEnv, readableByteChannel, readMethod, byteBuffer);
920 
921  if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
922  LocalReferenceHolder_Cleanup(&refs);
923  return 0;
924  }
925 
926  if (result < 0) {
927  break;
928  }
929 
930  bytesRemaining -= result;
931  bytesRead += result;
932  ctx->hidden.androidio.position += result;
933  }
934  LocalReferenceHolder_Cleanup(&refs);
935  return bytesRead / size;
936  }
937 }
938 
939 size_t Android_JNI_FileWrite(SDL_RWops* ctx, const void* buffer,
940  size_t size, size_t num)
941 {
942  SDL_SetError("Cannot write to Android package filesystem");
943  return 0;
944 }
945 
946 static int Internal_Android_JNI_FileClose(SDL_RWops* ctx, SDL_bool release)
947 {
948  struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
949 
950  int result = 0;
951  JNIEnv *mEnv = Android_JNI_GetEnv();
952 
953  if (!LocalReferenceHolder_Init(&refs, mEnv)) {
954  LocalReferenceHolder_Cleanup(&refs);
955  return SDL_SetError("Failed to allocate enough JVM local references");
956  }
957 
958  if (ctx) {
959  if (release) {
960  (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.fileNameRef);
961  }
962 
963  if (ctx->hidden.androidio.assetFileDescriptorRef) {
964  jobject inputStream = (jobject)ctx->hidden.androidio.assetFileDescriptorRef;
965  jmethodID mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
966  "close", "()V");
967  (*mEnv)->CallVoidMethod(mEnv, inputStream, mid);
968  (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.assetFileDescriptorRef);
969  if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
970  result = -1;
971  }
972  }
973  else {
974  jobject inputStream = (jobject)ctx->hidden.androidio.inputStreamRef;
975 
976  /* inputStream.close(); */
977  jmethodID mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
978  "close", "()V");
979  (*mEnv)->CallVoidMethod(mEnv, inputStream, mid);
980  (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.inputStreamRef);
981  (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.readableByteChannelRef);
982  if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
983  result = -1;
984  }
985  }
986 
987  if (release) {
988  SDL_FreeRW(ctx);
989  }
990  }
991 
992  LocalReferenceHolder_Cleanup(&refs);
993  return result;
994 }
995 
996 
998 {
999  return ctx->hidden.androidio.size;
1000 }
1001 
1003 {
1004  if (ctx->hidden.androidio.assetFileDescriptorRef) {
1005  switch (whence) {
1006  case RW_SEEK_SET:
1007  if (ctx->hidden.androidio.size != -1 /* UNKNOWN_LENGTH */ && offset > ctx->hidden.androidio.size) offset = ctx->hidden.androidio.size;
1008  offset += ctx->hidden.androidio.offset;
1009  break;
1010  case RW_SEEK_CUR:
1011  offset += ctx->hidden.androidio.position;
1012  if (ctx->hidden.androidio.size != -1 /* UNKNOWN_LENGTH */ && offset > ctx->hidden.androidio.size) offset = ctx->hidden.androidio.size;
1013  offset += ctx->hidden.androidio.offset;
1014  break;
1015  case RW_SEEK_END:
1016  offset = ctx->hidden.androidio.offset + ctx->hidden.androidio.size + offset;
1017  break;
1018  default:
1019  return SDL_SetError("Unknown value for 'whence'");
1020  }
1021  whence = SEEK_SET;
1022 
1023  off_t ret = lseek(ctx->hidden.androidio.fd, (off_t)offset, SEEK_SET);
1024  if (ret == -1) return -1;
1025  ctx->hidden.androidio.position = ret - ctx->hidden.androidio.offset;
1026  } else {
1027  Sint64 newPosition;
1028 
1029  switch (whence) {
1030  case RW_SEEK_SET:
1031  newPosition = offset;
1032  break;
1033  case RW_SEEK_CUR:
1034  newPosition = ctx->hidden.androidio.position + offset;
1035  break;
1036  case RW_SEEK_END:
1037  newPosition = ctx->hidden.androidio.size + offset;
1038  break;
1039  default:
1040  return SDL_SetError("Unknown value for 'whence'");
1041  }
1042 
1043  /* Validate the new position */
1044  if (newPosition < 0) {
1045  return SDL_Error(SDL_EFSEEK);
1046  }
1047  if (newPosition > ctx->hidden.androidio.size) {
1048  newPosition = ctx->hidden.androidio.size;
1049  }
1050 
1051  Sint64 movement = newPosition - ctx->hidden.androidio.position;
1052  if (movement > 0) {
1053  unsigned char buffer[4096];
1054 
1055  /* The easy case where we're seeking forwards */
1056  while (movement > 0) {
1057  Sint64 amount = sizeof (buffer);
1058  if (amount > movement) {
1059  amount = movement;
1060  }
1061  size_t result = Android_JNI_FileRead(ctx, buffer, 1, amount);
1062  if (result <= 0) {
1063  /* Failed to read/skip the required amount, so fail */
1064  return -1;
1065  }
1066 
1067  movement -= result;
1068  }
1069 
1070  } else if (movement < 0) {
1071  /* We can't seek backwards so we have to reopen the file and seek */
1072  /* forwards which obviously isn't very efficient */
1073  Internal_Android_JNI_FileClose(ctx, SDL_FALSE);
1074  Internal_Android_JNI_FileOpen(ctx);
1075  Android_JNI_FileSeek(ctx, newPosition, RW_SEEK_SET);
1076  }
1077  }
1078 
1079  return ctx->hidden.androidio.position;
1080 
1081 }
1082 
1084 {
1085  return Internal_Android_JNI_FileClose(ctx, SDL_TRUE);
1086 }
1087 
1088 /* returns a new global reference which needs to be released later */
1089 static jobject Android_JNI_GetSystemServiceObject(const char* name)
1090 {
1091  struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
1092  JNIEnv* env = Android_JNI_GetEnv();
1093  jobject retval = NULL;
1094 
1095  if (!LocalReferenceHolder_Init(&refs, env)) {
1096  LocalReferenceHolder_Cleanup(&refs);
1097  return NULL;
1098  }
1099 
1100  jstring service = (*env)->NewStringUTF(env, name);
1101 
1102  jmethodID mid;
1103 
1104  mid = (*env)->GetStaticMethodID(env, mActivityClass, "getContext", "()Landroid/content/Context;");
1105  jobject context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
1106 
1107  mid = (*env)->GetMethodID(env, mActivityClass, "getSystemServiceFromUiThread", "(Ljava/lang/String;)Ljava/lang/Object;");
1108  jobject manager = (*env)->CallObjectMethod(env, context, mid, service);
1109 
1110  (*env)->DeleteLocalRef(env, service);
1111 
1112  retval = manager ? (*env)->NewGlobalRef(env, manager) : NULL;
1113  LocalReferenceHolder_Cleanup(&refs);
1114  return retval;
1115 }
1116 
1117 #define SETUP_CLIPBOARD(error) \
1118  struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); \
1119  JNIEnv* env = Android_JNI_GetEnv(); \
1120  if (!LocalReferenceHolder_Init(&refs, env)) { \
1121  LocalReferenceHolder_Cleanup(&refs); \
1122  return error; \
1123  } \
1124  jobject clipboard = Android_JNI_GetSystemServiceObject("clipboard"); \
1125  if (!clipboard) { \
1126  LocalReferenceHolder_Cleanup(&refs); \
1127  return error; \
1128  }
1129 
1130 #define CLEANUP_CLIPBOARD() \
1131  LocalReferenceHolder_Cleanup(&refs);
1132 
1133 int Android_JNI_SetClipboardText(const char* text)
1134 {
1135  SETUP_CLIPBOARD(-1)
1136 
1137  jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "setText", "(Ljava/lang/CharSequence;)V");
1138  jstring string = (*env)->NewStringUTF(env, text);
1139  (*env)->CallVoidMethod(env, clipboard, mid, string);
1140  (*env)->DeleteGlobalRef(env, clipboard);
1141  (*env)->DeleteLocalRef(env, string);
1142 
1143  CLEANUP_CLIPBOARD();
1144 
1145  return 0;
1146 }
1147 
1148 char* Android_JNI_GetClipboardText(void)
1149 {
1150  SETUP_CLIPBOARD(SDL_strdup(""))
1151 
1152  jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "getText", "()Ljava/lang/CharSequence;");
1153  jobject sequence = (*env)->CallObjectMethod(env, clipboard, mid);
1154  (*env)->DeleteGlobalRef(env, clipboard);
1155  if (sequence) {
1156  mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, sequence), "toString", "()Ljava/lang/String;");
1157  jstring string = (jstring)((*env)->CallObjectMethod(env, sequence, mid));
1158  const char* utf = (*env)->GetStringUTFChars(env, string, 0);
1159  if (utf) {
1160  char* text = SDL_strdup(utf);
1161  (*env)->ReleaseStringUTFChars(env, string, utf);
1162 
1163  CLEANUP_CLIPBOARD();
1164 
1165  return text;
1166  }
1167  }
1168 
1169  CLEANUP_CLIPBOARD();
1170 
1171  return SDL_strdup("");
1172 }
1173 
1175 {
1176  SETUP_CLIPBOARD(SDL_FALSE)
1177 
1178  jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "hasText", "()Z");
1179  jboolean has = (*env)->CallBooleanMethod(env, clipboard, mid);
1180  (*env)->DeleteGlobalRef(env, clipboard);
1181 
1182  CLEANUP_CLIPBOARD();
1183 
1184  return has ? SDL_TRUE : SDL_FALSE;
1185 }
1186 
1187 
1188 /* returns 0 on success or -1 on error (others undefined then)
1189  * returns truthy or falsy value in plugged, charged and battery
1190  * returns the value in seconds and percent or -1 if not available
1191  */
1192 int Android_JNI_GetPowerInfo(int* plugged, int* charged, int* battery, int* seconds, int* percent)
1193 {
1194  struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
1195  JNIEnv* env = Android_JNI_GetEnv();
1196  if (!LocalReferenceHolder_Init(&refs, env)) {
1197  LocalReferenceHolder_Cleanup(&refs);
1198  return -1;
1199  }
1200 
1201  jmethodID mid;
1202 
1203  mid = (*env)->GetStaticMethodID(env, mActivityClass, "getContext", "()Landroid/content/Context;");
1204  jobject context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
1205 
1206  jstring action = (*env)->NewStringUTF(env, "android.intent.action.BATTERY_CHANGED");
1207 
1208  jclass cls = (*env)->FindClass(env, "android/content/IntentFilter");
1209 
1210  mid = (*env)->GetMethodID(env, cls, "<init>", "(Ljava/lang/String;)V");
1211  jobject filter = (*env)->NewObject(env, cls, mid, action);
1212 
1213  (*env)->DeleteLocalRef(env, action);
1214 
1215  mid = (*env)->GetMethodID(env, mActivityClass, "registerReceiver", "(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;");
1216  jobject intent = (*env)->CallObjectMethod(env, context, mid, NULL, filter);
1217 
1218  (*env)->DeleteLocalRef(env, filter);
1219 
1220  cls = (*env)->GetObjectClass(env, intent);
1221 
1222  jstring iname;
1223  jmethodID imid = (*env)->GetMethodID(env, cls, "getIntExtra", "(Ljava/lang/String;I)I");
1224 
1225 #define GET_INT_EXTRA(var, key) \
1226  iname = (*env)->NewStringUTF(env, key); \
1227  int var = (*env)->CallIntMethod(env, intent, imid, iname, -1); \
1228  (*env)->DeleteLocalRef(env, iname);
1229 
1230  jstring bname;
1231  jmethodID bmid = (*env)->GetMethodID(env, cls, "getBooleanExtra", "(Ljava/lang/String;Z)Z");
1232 
1233 #define GET_BOOL_EXTRA(var, key) \
1234  bname = (*env)->NewStringUTF(env, key); \
1235  int var = (*env)->CallBooleanMethod(env, intent, bmid, bname, JNI_FALSE); \
1236  (*env)->DeleteLocalRef(env, bname);
1237 
1238  if (plugged) {
1239  GET_INT_EXTRA(plug, "plugged") /* == BatteryManager.EXTRA_PLUGGED (API 5) */
1240  if (plug == -1) {
1241  LocalReferenceHolder_Cleanup(&refs);
1242  return -1;
1243  }
1244  /* 1 == BatteryManager.BATTERY_PLUGGED_AC */
1245  /* 2 == BatteryManager.BATTERY_PLUGGED_USB */
1246  *plugged = (0 < plug) ? 1 : 0;
1247  }
1248 
1249  if (charged) {
1250  GET_INT_EXTRA(status, "status") /* == BatteryManager.EXTRA_STATUS (API 5) */
1251  if (status == -1) {
1252  LocalReferenceHolder_Cleanup(&refs);
1253  return -1;
1254  }
1255  /* 5 == BatteryManager.BATTERY_STATUS_FULL */
1256  *charged = (status == 5) ? 1 : 0;
1257  }
1258 
1259  if (battery) {
1260  GET_BOOL_EXTRA(present, "present") /* == BatteryManager.EXTRA_PRESENT (API 5) */
1261  *battery = present ? 1 : 0;
1262  }
1263 
1264  if (seconds) {
1265  *seconds = -1; /* not possible */
1266  }
1267 
1268  if (percent) {
1269  GET_INT_EXTRA(level, "level") /* == BatteryManager.EXTRA_LEVEL (API 5) */
1270  GET_INT_EXTRA(scale, "scale") /* == BatteryManager.EXTRA_SCALE (API 5) */
1271  if ((level == -1) || (scale == -1)) {
1272  LocalReferenceHolder_Cleanup(&refs);
1273  return -1;
1274  }
1275  *percent = level * 100 / scale;
1276  }
1277 
1278  (*env)->DeleteLocalRef(env, intent);
1279 
1280  LocalReferenceHolder_Cleanup(&refs);
1281  return 0;
1282 }
1283 
1284 /* returns number of found touch devices as return value and ids in parameter ids */
1286  JNIEnv *env = Android_JNI_GetEnv();
1287  jint sources = 4098; /* == InputDevice.SOURCE_TOUCHSCREEN */
1288  jmethodID mid = (*env)->GetStaticMethodID(env, mActivityClass, "inputGetInputDeviceIds", "(I)[I");
1289  jintArray array = (jintArray) (*env)->CallStaticObjectMethod(env, mActivityClass, mid, sources);
1290  int number = 0;
1291  *ids = NULL;
1292  if (array) {
1293  number = (int) (*env)->GetArrayLength(env, array);
1294  if (0 < number) {
1295  jint* elements = (*env)->GetIntArrayElements(env, array, NULL);
1296  if (elements) {
1297  int i;
1298  *ids = SDL_malloc(number * sizeof (**ids));
1299  for (i = 0; i < number; ++i) { /* not assuming sizeof (jint) == sizeof (int) */
1300  (*ids)[i] = elements[i];
1301  }
1302  (*env)->ReleaseIntArrayElements(env, array, elements, JNI_ABORT);
1303  }
1304  }
1305  (*env)->DeleteLocalRef(env, array);
1306  }
1307  return number;
1308 }
1309 
1311 {
1312  JNIEnv *env = Android_JNI_GetEnv();
1313  (*env)->CallStaticVoidMethod(env, mActivityClass, midPollInputDevices);
1314 }
1315 
1316 /* See SDLActivity.java for constants. */
1317 #define COMMAND_SET_KEEP_SCREEN_ON 5
1318 
1319 /* sends message to be handled on the UI event dispatch thread */
1320 int Android_JNI_SendMessage(int command, int param)
1321 {
1322  JNIEnv *env = Android_JNI_GetEnv();
1323  if (!env) {
1324  return -1;
1325  }
1326  jmethodID mid = (*env)->GetStaticMethodID(env, mActivityClass, "sendMessage", "(II)Z");
1327  if (!mid) {
1328  return -1;
1329  }
1330  jboolean success = (*env)->CallStaticBooleanMethod(env, mActivityClass, mid, command, param);
1331  return success ? 0 : -1;
1332 }
1333 
1335 {
1336  Android_JNI_SendMessage(COMMAND_SET_KEEP_SCREEN_ON, (suspend == SDL_FALSE) ? 0 : 1);
1337 }
1338 
1339 void Android_JNI_ShowTextInput(SDL_Rect *inputRect)
1340 {
1341  JNIEnv *env = Android_JNI_GetEnv();
1342  if (!env) {
1343  return;
1344  }
1345 
1346  jmethodID mid = (*env)->GetStaticMethodID(env, mActivityClass, "showTextInput", "(IIII)Z");
1347  if (!mid) {
1348  return;
1349  }
1350  (*env)->CallStaticBooleanMethod(env, mActivityClass, mid,
1351  inputRect->x,
1352  inputRect->y,
1353  inputRect->w,
1354  inputRect->h );
1355 }
1356 
1357 void Android_JNI_HideTextInput(void)
1358 {
1359  /* has to match Activity constant */
1360  const int COMMAND_TEXTEDIT_HIDE = 3;
1361  Android_JNI_SendMessage(COMMAND_TEXTEDIT_HIDE, 0);
1362 }
1363 
1364 int Android_JNI_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
1365 {
1366  JNIEnv *env;
1367  jclass clazz;
1368  jmethodID mid;
1369  jobject context;
1370  jstring title;
1371  jstring message;
1372  jintArray button_flags;
1373  jintArray button_ids;
1374  jobjectArray button_texts;
1375  jintArray colors;
1376  jobject text;
1377  jint temp;
1378  int i;
1379 
1380  env = Android_JNI_GetEnv();
1381 
1382  /* convert parameters */
1383 
1384  clazz = (*env)->FindClass(env, "java/lang/String");
1385 
1386  title = (*env)->NewStringUTF(env, messageboxdata->title);
1387  message = (*env)->NewStringUTF(env, messageboxdata->message);
1388 
1389  button_flags = (*env)->NewIntArray(env, messageboxdata->numbuttons);
1390  button_ids = (*env)->NewIntArray(env, messageboxdata->numbuttons);
1391  button_texts = (*env)->NewObjectArray(env, messageboxdata->numbuttons,
1392  clazz, NULL);
1393  for (i = 0; i < messageboxdata->numbuttons; ++i) {
1394  temp = messageboxdata->buttons[i].flags;
1395  (*env)->SetIntArrayRegion(env, button_flags, i, 1, &temp);
1396  temp = messageboxdata->buttons[i].buttonid;
1397  (*env)->SetIntArrayRegion(env, button_ids, i, 1, &temp);
1398  text = (*env)->NewStringUTF(env, messageboxdata->buttons[i].text);
1399  (*env)->SetObjectArrayElement(env, button_texts, i, text);
1400  (*env)->DeleteLocalRef(env, text);
1401  }
1402 
1403  if (messageboxdata->colorScheme) {
1404  colors = (*env)->NewIntArray(env, SDL_MESSAGEBOX_COLOR_MAX);
1405  for (i = 0; i < SDL_MESSAGEBOX_COLOR_MAX; ++i) {
1406  temp = (0xFF << 24) |
1407  (messageboxdata->colorScheme->colors[i].r << 16) |
1408  (messageboxdata->colorScheme->colors[i].g << 8) |
1409  (messageboxdata->colorScheme->colors[i].b << 0);
1410  (*env)->SetIntArrayRegion(env, colors, i, 1, &temp);
1411  }
1412  } else {
1413  colors = NULL;
1414  }
1415 
1416  (*env)->DeleteLocalRef(env, clazz);
1417 
1418  /* call function */
1419 
1420  mid = (*env)->GetStaticMethodID(env, mActivityClass, "getContext","()Landroid/content/Context;");
1421 
1422  context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
1423 
1424  clazz = (*env)->GetObjectClass(env, context);
1425 
1426  mid = (*env)->GetMethodID(env, clazz,
1427  "messageboxShowMessageBox", "(ILjava/lang/String;Ljava/lang/String;[I[I[Ljava/lang/String;[I)I");
1428  *buttonid = (*env)->CallIntMethod(env, context, mid,
1429  messageboxdata->flags,
1430  title,
1431  message,
1432  button_flags,
1433  button_ids,
1434  button_texts,
1435  colors);
1436 
1437  (*env)->DeleteLocalRef(env, context);
1438  (*env)->DeleteLocalRef(env, clazz);
1439 
1440  /* delete parameters */
1441 
1442  (*env)->DeleteLocalRef(env, title);
1443  (*env)->DeleteLocalRef(env, message);
1444  (*env)->DeleteLocalRef(env, button_flags);
1445  (*env)->DeleteLocalRef(env, button_ids);
1446  (*env)->DeleteLocalRef(env, button_texts);
1447  (*env)->DeleteLocalRef(env, colors);
1448 
1449  return 0;
1450 }
1451 
1452 /*
1453 //////////////////////////////////////////////////////////////////////////////
1454 //
1455 // Functions exposed to SDL applications in SDL_system.h
1456 //////////////////////////////////////////////////////////////////////////////
1457 */
1458 
1459 void *SDL_AndroidGetJNIEnv()
1460 {
1461  return Android_JNI_GetEnv();
1462 }
1463 
1464 
1465 
1466 void *SDL_AndroidGetActivity()
1467 {
1468  /* See SDL_system.h for caveats on using this function. */
1469 
1470  jmethodID mid;
1471 
1472  JNIEnv *env = Android_JNI_GetEnv();
1473  if (!env) {
1474  return NULL;
1475  }
1476 
1477  /* return SDLActivity.getContext(); */
1478  mid = (*env)->GetStaticMethodID(env, mActivityClass,
1479  "getContext","()Landroid/content/Context;");
1480  return (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
1481 }
1482 
1483 const char * SDL_AndroidGetInternalStoragePath()
1484 {
1485  static char *s_AndroidInternalFilesPath = NULL;
1486 
1487  if (!s_AndroidInternalFilesPath) {
1488  struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
1489  jmethodID mid;
1490  jobject context;
1491  jobject fileObject;
1492  jstring pathString;
1493  const char *path;
1494 
1495  JNIEnv *env = Android_JNI_GetEnv();
1496  if (!LocalReferenceHolder_Init(&refs, env)) {
1497  LocalReferenceHolder_Cleanup(&refs);
1498  return NULL;
1499  }
1500 
1501  /* context = SDLActivity.getContext(); */
1502  mid = (*env)->GetStaticMethodID(env, mActivityClass,
1503  "getContext","()Landroid/content/Context;");
1504  context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
1505 
1506  /* fileObj = context.getFilesDir(); */
1507  mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
1508  "getFilesDir", "()Ljava/io/File;");
1509  fileObject = (*env)->CallObjectMethod(env, context, mid);
1510  if (!fileObject) {
1511  SDL_SetError("Couldn't get internal directory");
1512  LocalReferenceHolder_Cleanup(&refs);
1513  return NULL;
1514  }
1515 
1516  /* path = fileObject.getAbsolutePath(); */
1517  mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, fileObject),
1518  "getAbsolutePath", "()Ljava/lang/String;");
1519  pathString = (jstring)(*env)->CallObjectMethod(env, fileObject, mid);
1520 
1521  path = (*env)->GetStringUTFChars(env, pathString, NULL);
1522  s_AndroidInternalFilesPath = SDL_strdup(path);
1523  (*env)->ReleaseStringUTFChars(env, pathString, path);
1524 
1525  LocalReferenceHolder_Cleanup(&refs);
1526  }
1527  return s_AndroidInternalFilesPath;
1528 }
1529 
1531 {
1532  struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
1533  jmethodID mid;
1534  jclass cls;
1535  jstring stateString;
1536  const char *state;
1537  int stateFlags;
1538 
1539  JNIEnv *env = Android_JNI_GetEnv();
1540  if (!LocalReferenceHolder_Init(&refs, env)) {
1541  LocalReferenceHolder_Cleanup(&refs);
1542  return 0;
1543  }
1544 
1545  cls = (*env)->FindClass(env, "android/os/Environment");
1546  mid = (*env)->GetStaticMethodID(env, cls,
1547  "getExternalStorageState", "()Ljava/lang/String;");
1548  stateString = (jstring)(*env)->CallStaticObjectMethod(env, cls, mid);
1549 
1550  state = (*env)->GetStringUTFChars(env, stateString, NULL);
1551 
1552  /* Print an info message so people debugging know the storage state */
1553  __android_log_print(ANDROID_LOG_INFO, "SDL", "external storage state: %s", state);
1554 
1555  if (SDL_strcmp(state, "mounted") == 0) {
1556  stateFlags = SDL_ANDROID_EXTERNAL_STORAGE_READ |
1557  SDL_ANDROID_EXTERNAL_STORAGE_WRITE;
1558  } else if (SDL_strcmp(state, "mounted_ro") == 0) {
1559  stateFlags = SDL_ANDROID_EXTERNAL_STORAGE_READ;
1560  } else {
1561  stateFlags = 0;
1562  }
1563  (*env)->ReleaseStringUTFChars(env, stateString, state);
1564 
1565  LocalReferenceHolder_Cleanup(&refs);
1566  return stateFlags;
1567 }
1568 
1569 const char * SDL_AndroidGetExternalStoragePath()
1570 {
1571  static char *s_AndroidExternalFilesPath = NULL;
1572 
1573  if (!s_AndroidExternalFilesPath) {
1574  struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
1575  jmethodID mid;
1576  jobject context;
1577  jobject fileObject;
1578  jstring pathString;
1579  const char *path;
1580 
1581  JNIEnv *env = Android_JNI_GetEnv();
1582  if (!LocalReferenceHolder_Init(&refs, env)) {
1583  LocalReferenceHolder_Cleanup(&refs);
1584  return NULL;
1585  }
1586 
1587  /* context = SDLActivity.getContext(); */
1588  mid = (*env)->GetStaticMethodID(env, mActivityClass,
1589  "getContext","()Landroid/content/Context;");
1590  context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
1591 
1592  /* fileObj = context.getExternalFilesDir(); */
1593  mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
1594  "getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;");
1595  fileObject = (*env)->CallObjectMethod(env, context, mid, NULL);
1596  if (!fileObject) {
1597  SDL_SetError("Couldn't get external directory");
1598  LocalReferenceHolder_Cleanup(&refs);
1599  return NULL;
1600  }
1601 
1602  /* path = fileObject.getAbsolutePath(); */
1603  mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, fileObject),
1604  "getAbsolutePath", "()Ljava/lang/String;");
1605  pathString = (jstring)(*env)->CallObjectMethod(env, fileObject, mid);
1606 
1607  path = (*env)->GetStringUTFChars(env, pathString, NULL);
1608  s_AndroidExternalFilesPath = SDL_strdup(path);
1609  (*env)->ReleaseStringUTFChars(env, pathString, path);
1610 
1611  LocalReferenceHolder_Cleanup(&refs);
1612  }
1613  return s_AndroidExternalFilesPath;
1614 }
1615 
1616 jclass Android_JNI_GetActivityClass(void)
1617 {
1618  return mActivityClass;
1619 }
1620 
1621 #endif /* __ANDROID__ */
1622 
1623 /* vi: set ts=4 sw=4 expandtab: */
1624 
GLuint * ids
void Android_SetScreenResolution(int width, int height, Uint32 format, float rate)
GLenum GLenum GLenum GLenum GLenum scale
const char * message
int Android_JNI_FileClose(SDL_RWops *ctx)
int Android_OnKeyUp(int keycode)
GLuint num
SDL_Texture * button
void Android_JNI_CloseAudioDevice(void)
int Android_JNI_SendMessage(int command, int param)
GLuint64EXT * result
void Android_JNI_ShowTextInput(SDL_Rect *inputRect)
void Android_JNI_WriteAudioBuffer(void)
const char * title
GLsizei GLenum * sources
GLuint GLsizei const GLchar * message
SDL_bool Android_JNI_GetAccelerometerValues(float values[3])
#define SDL_AndroidGetExternalStoragePath
struct xkb_state * state
GLfloat GLfloat p
void Android_JNI_SetActivityTitle(const char *title)
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1967
#define SDL_GetHint
char * Android_JNI_GetClipboardText(void)
int Android_OnKeyDown(int keycode)
void Android_OnTouch(int touch_device_id_in, int pointer_finger_id_in, int action, float x, float y, float p)
GLint GLint GLsizei width
Definition: SDL_opengl.h:1565
#define RW_SEEK_END
Definition: SDL_rwops.h:176
GLuint const GLchar * name
SDL_Texture * axis
int SDL_SendWindowEvent(SDL_Window *window, Uint8 windowevent, int data1, int data2)
GLint GLint GLint GLint GLint x
Definition: SDL_opengl.h:1567
#define SDL_Error
GLsizeiptr size
int Android_JNI_FileOpen(SDL_RWops *ctx, const char *fileName, const char *mode)
GLfloat param
#define SDL_SemPost
SDL_sem * Android_PauseSem
static SDL_VideoDevice * _this
Definition: SDL_video.c:114
SDL_bool
Definition: SDL_stdinc.h:126
int Android_JNI_SetupThread(void)
SDL_bool retval
#define SDL_Log
GLdouble s
Definition: SDL_opengl.h:2056
#define SDL_StopTextInput
GLenum GLsizei const void * pathString
GLint GLint GLsizei GLsizei height
Definition: SDL_opengl.h:1565
GLsizei const GLfloat * value
int SDL_SendKeyboardText(const char *text)
Definition: SDL_keyboard.c:774
#define SDL_FlushEvents
SDL_bool Android_JNI_HasClipboardText(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)
SDL_Window * Android_Window
GLenum mode
void * Android_JNI_GetAudioBuffer(void)
GLenum GLsizei GLsizei GLint * values
void Android_JNI_SuspendScreenSaver(SDL_bool suspend)
int x
Definition: SDL_rect.h:66
const SDL_MessageBoxButtonData * buttons
int w
Definition: SDL_rect.h:67
MessageBox structure containing title, text, window, etc.
void Android_OnMouse(int button, int action, float x, float y)
GLint level
Definition: SDL_opengl.h:1565
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:42
Sint64(* size)(struct SDL_RWops *context)
Definition: SDL_rwops.h:57
#define SDL_assert(condition)
Definition: SDL_assert.h:167
#define NULL
Definition: begin_code.h:143
GLintptr offset
ANativeWindow * Android_JNI_GetNativeWindow(void)
jclass Android_JNI_GetActivityClass(void)
#define SDL_SetError
union SDL_RWops::@10 hidden
static char text[MAX_TEXT_LENGTH]
Definition: testime.c:34
GLenum func
void Android_JNI_HideTextInput(void)
GLdouble GLdouble z
#define SDL_AndroidGetActivity
GLint GLint GLsizei GLsizei GLsizei GLint GLenum format
Definition: SDL_opengl.h:1565
GLint GLint GLint GLint GLint GLint y
Definition: SDL_opengl.h:1567
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:67
#define SDL_strdup
int SDL_SendDropFile(const char *file)
#define SDL_FreeRW
GLuint buffer
int Android_JNI_SetClipboardText(const char *text)
#define SDL_AndroidGetExternalStorageState
#define RW_SEEK_SET
Definition: SDL_rwops.h:174
SDL_VideoDevice * SDL_GetVideoDevice(void)
Definition: SDL_video.c:573
GLenum array
int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames)
#define SDL_malloc
GLsizei const GLchar *const * path
int SDL_SendAppEvent(SDL_EventType eventType)
Definition: SDL_events.c:622
#define SDL_strcmp
void * driverdata
Definition: SDL_sysvideo.h:106
#define RW_SEEK_CUR
Definition: SDL_rwops.h:175
void Android_JNI_PollInputDevices(void)
int64_t Sint64
A signed 64-bit integer type.
Definition: SDL_stdinc.h:160
#define SDL_SemValue
#define SDL_AndroidGetJNIEnv
ANativeWindow * native_window
int Android_JNI_GetTouchDeviceIds(int **ids)
static int colors[7]
Definition: testgesture.c:40
const SDL_MessageBoxColorScheme * colorScheme
SDL_MessageBoxColor colors[SDL_MESSAGEBOX_COLOR_MAX]
#define SDL_AndroidGetInternalStoragePath
int y
Definition: SDL_rect.h:66
JNIEnv * Android_JNI_GetEnv(void)
int SDL_SendEditingText(const char *text, int start, int length)
Definition: SDL_keyboard.c:797
Sint64 Android_JNI_FileSeek(SDL_RWops *ctx, Sint64 offset, int whence)
EGLSurface egl_surface
A rectangle, with the origin at the upper left.
Definition: SDL_rect.h:64
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
int SDL_SendQuit(void)
Definition: SDL_quit.c:139
Sint64 Android_JNI_FileSize(SDL_RWops *ctx)