SDL  2.0
SDL_android.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2018 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 
28 #ifdef __ANDROID__
29 
30 #include "SDL_system.h"
31 #include "SDL_android.h"
32 
33 #include "keyinfotable.h"
34 
35 #include "../../events/SDL_events_c.h"
36 #include "../../video/android/SDL_androidkeyboard.h"
37 #include "../../video/android/SDL_androidmouse.h"
38 #include "../../video/android/SDL_androidtouch.h"
39 #include "../../video/android/SDL_androidvideo.h"
40 #include "../../video/android/SDL_androidwindow.h"
41 #include "../../joystick/android/SDL_sysjoystick_c.h"
42 #include "../../haptic/android/SDL_syshaptic_c.h"
43 
44 #include <android/log.h>
45 #include <pthread.h>
46 #include <sys/types.h>
47 #include <unistd.h>
48 #include <dlfcn.h>
49 /* #define LOG_TAG "SDL_android" */
50 /* #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) */
51 /* #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) */
52 #define LOGI(...) do {} while (0)
53 #define LOGE(...) do {} while (0)
54 
55 
56 #define SDL_JAVA_PREFIX org_libsdl_app
57 #define CONCAT1(prefix, class, function) CONCAT2(prefix, class, function)
58 #define CONCAT2(prefix, class, function) Java_ ## prefix ## _ ## class ## _ ## function
59 #define SDL_JAVA_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, SDLActivity, function)
60 #define SDL_JAVA_AUDIO_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, SDLAudioManager, function)
61 #define SDL_JAVA_CONTROLLER_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, SDLControllerManager, function)
62 #define SDL_JAVA_INTERFACE_INPUT_CONNECTION(function) CONCAT1(SDL_JAVA_PREFIX, SDLInputConnection, function)
63 
64 
65 /* Java class SDLActivity */
66 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(
67  JNIEnv* mEnv, 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(onNativeResize)(
78  JNIEnv* env, jclass jcls,
79  jint width, jint height, jint format, jfloat rate);
80 
81 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(
82  JNIEnv* env, jclass jcls);
83 
84 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)(
85  JNIEnv* env, jclass jcls);
86 
87 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyDown)(
88  JNIEnv* env, jclass jcls,
89  jint keycode);
90 
91 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyUp)(
92  JNIEnv* env, jclass jcls,
93  jint keycode);
94 
95 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyboardFocusLost)(
96  JNIEnv* env, jclass jcls);
97 
98 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeTouch)(
99  JNIEnv* env, jclass jcls,
100  jint touch_device_id_in, jint pointer_finger_id_in,
101  jint action, jfloat x, jfloat y, jfloat p);
102 
103 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeMouse)(
104  JNIEnv* env, jclass jcls,
105  jint button, jint action, jfloat x, jfloat y);
106 
107 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeAccel)(
108  JNIEnv* env, jclass jcls,
109  jfloat x, jfloat y, jfloat z);
110 
111 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeClipboardChanged)(
112  JNIEnv* env, jclass jcls);
113 
114 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeLowMemory)(
115  JNIEnv* env, jclass cls);
116 
117 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeQuit)(
118  JNIEnv* env, jclass cls);
119 
120 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePause)(
121  JNIEnv* env, jclass cls);
122 
123 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeResume)(
124  JNIEnv* env, jclass cls);
125 
126 JNIEXPORT jstring JNICALL SDL_JAVA_INTERFACE(nativeGetHint)(
127  JNIEnv* env, jclass cls,
128  jstring name);
129 
130 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetenv)(
131  JNIEnv* env, jclass cls,
132  jstring name, jstring value);
133 
134 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeEnvironmentVariablesSet)(
135  JNIEnv* env, jclass cls);
136 
137 /* Java class SDLInputConnection */
138 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeCommitText)(
139  JNIEnv* env, jclass cls,
140  jstring text, jint newCursorPosition);
141 
142 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeSetComposingText)(
143  JNIEnv* env, jclass cls,
144  jstring text, jint newCursorPosition);
145 
146 /* Java class SDLAudioManager */
147 JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(
148  JNIEnv *env, jclass jcls);
149 
150 /* Java class SDLControllerManager */
151 JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(
152  JNIEnv *env, jclass jcls);
153 
154 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)(
155  JNIEnv* env, jclass jcls,
156  jint device_id, jint keycode);
157 
158 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)(
159  JNIEnv* env, jclass jcls,
160  jint device_id, jint keycode);
161 
162 JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)(
163  JNIEnv* env, jclass jcls,
164  jint device_id, jint axis, jfloat value);
165 
166 JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
167  JNIEnv* env, jclass jcls,
168  jint device_id, jint hat_id, jint x, jint y);
169 
170 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
171  JNIEnv* env, jclass jcls,
172  jint device_id, jstring device_name, jstring device_desc, jint is_accelerometer,
173  jint nbuttons, jint naxes, jint nhats, jint nballs);
174 
175 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)(
176  JNIEnv* env, jclass jcls,
177  jint device_id);
178 
179 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic)(
180  JNIEnv* env, jclass jcls,
181  jint device_id, jstring device_name);
182 
183 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)(
184  JNIEnv* env, jclass jcls,
185  jint device_id);
186 
187 
188 
189 /* Uncomment this to log messages entering and exiting methods in this file */
190 /* #define DEBUG_JNI */
191 
192 static void Android_JNI_ThreadDestroyed(void*);
193 
194 /*******************************************************************************
195  This file links the Java side of Android with libsdl
196 *******************************************************************************/
197 #include <jni.h>
198 
199 
200 /*******************************************************************************
201  Globals
202 *******************************************************************************/
203 static pthread_key_t mThreadKey;
204 static JavaVM* mJavaVM;
205 
206 /* Main activity */
207 static jclass mActivityClass;
208 
209 /* method signatures */
210 static jmethodID midGetNativeSurface;
211 static jmethodID midSetActivityTitle;
212 static jmethodID midSetWindowStyle;
213 static jmethodID midSetOrientation;
214 static jmethodID midGetContext;
215 static jmethodID midIsAndroidTV;
216 static jmethodID midInputGetInputDeviceIds;
217 static jmethodID midSendMessage;
218 static jmethodID midShowTextInput;
219 static jmethodID midIsScreenKeyboardShown;
220 static jmethodID midClipboardSetText;
221 static jmethodID midClipboardGetText;
222 static jmethodID midClipboardHasText;
223 static jmethodID midOpenAPKExpansionInputStream;
224 static jmethodID midGetManifestEnvironmentVariables;
225 static jmethodID midGetDisplayDPI;
226 
227 /* audio manager */
228 static jclass mAudioManagerClass;
229 
230 /* method signatures */
231 static jmethodID midAudioOpen;
232 static jmethodID midAudioWriteShortBuffer;
233 static jmethodID midAudioWriteByteBuffer;
234 static jmethodID midAudioClose;
235 static jmethodID midCaptureOpen;
236 static jmethodID midCaptureReadShortBuffer;
237 static jmethodID midCaptureReadByteBuffer;
238 static jmethodID midCaptureClose;
239 
240 /* controller manager */
241 static jclass mControllerManagerClass;
242 
243 /* method signatures */
244 static jmethodID midPollInputDevices;
245 static jmethodID midPollHapticDevices;
246 static jmethodID midHapticRun;
247 
248 /* static fields */
249 static jfieldID fidSeparateMouseAndTouch;
250 
251 /* Accelerometer data storage */
252 static float fLastAccelerometer[3];
253 static SDL_bool bHasNewData;
254 
255 static SDL_bool bHasEnvironmentVariables = SDL_FALSE;
256 
257 /*******************************************************************************
258  Functions called by JNI
259 *******************************************************************************/
260 
261 /* Library init */
262 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
263 {
264  JNIEnv *env;
265  mJavaVM = vm;
266  LOGI("JNI_OnLoad called");
267  if ((*mJavaVM)->GetEnv(mJavaVM, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
268  LOGE("Failed to get the environment using GetEnv()");
269  return -1;
270  }
271  /*
272  * Create mThreadKey so we can keep track of the JNIEnv assigned to each thread
273  * Refer to http://developer.android.com/guide/practices/design/jni.html for the rationale behind this
274  */
275  if (pthread_key_create(&mThreadKey, Android_JNI_ThreadDestroyed) != 0) {
276  __android_log_print(ANDROID_LOG_ERROR, "SDL", "Error initializing pthread key");
277  }
279 
280  return JNI_VERSION_1_4;
281 }
282 
283 void checkJNIReady()
284 {
285  if (!mActivityClass || !mAudioManagerClass || !mControllerManagerClass) {
286  // We aren't fully initialized, let's just return.
287  return;
288  }
289 
290  SDL_SetMainReady();
291 }
292 
293 /* Activity initialization -- called before SDL_main() to initialize JNI bindings */
294 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv* mEnv, jclass cls)
295 {
296  __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeSetupJNI()");
297 
299 
300  mActivityClass = (jclass)((*mEnv)->NewGlobalRef(mEnv, cls));
301 
302  midGetNativeSurface = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
303  "getNativeSurface","()Landroid/view/Surface;");
304  midSetActivityTitle = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
305  "setActivityTitle","(Ljava/lang/String;)Z");
306  midSetWindowStyle = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
307  "setWindowStyle","(Z)V");
308  midSetOrientation = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
309  "setOrientation","(IIZLjava/lang/String;)V");
310  midGetContext = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
311  "getContext","()Landroid/content/Context;");
312  midIsAndroidTV = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
313  "isAndroidTV","()Z");
314  midInputGetInputDeviceIds = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
315  "inputGetInputDeviceIds", "(I)[I");
316  midSendMessage = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
317  "sendMessage", "(II)Z");
318  midShowTextInput = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
319  "showTextInput", "(IIII)Z");
320  midIsScreenKeyboardShown = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
321  "isScreenKeyboardShown","()Z");
322  midClipboardSetText = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
323  "clipboardSetText", "(Ljava/lang/String;)V");
324  midClipboardGetText = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
325  "clipboardGetText", "()Ljava/lang/String;");
326  midClipboardHasText = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
327  "clipboardHasText", "()Z");
328  midOpenAPKExpansionInputStream = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
329  "openAPKExpansionInputStream", "(Ljava/lang/String;)Ljava/io/InputStream;");
330 
331  midGetManifestEnvironmentVariables = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
332  "getManifestEnvironmentVariables", "()Z");
333 
334  midGetDisplayDPI = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "getDisplayDPI", "()Landroid/util/DisplayMetrics;");
335  midGetDisplayDPI = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "getDisplayDPI", "()Landroid/util/DisplayMetrics;");
336 
337  if (!midGetNativeSurface ||
338  !midSetActivityTitle || !midSetWindowStyle || !midSetOrientation || !midGetContext || !midIsAndroidTV || !midInputGetInputDeviceIds ||
339  !midSendMessage || !midShowTextInput || !midIsScreenKeyboardShown ||
340  !midClipboardSetText || !midClipboardGetText || !midClipboardHasText ||
341  !midOpenAPKExpansionInputStream || !midGetManifestEnvironmentVariables|| !midGetDisplayDPI) {
342  __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLActivity.java?");
343  }
344 
345  fidSeparateMouseAndTouch = (*mEnv)->GetStaticFieldID(mEnv, mActivityClass, "mSeparateMouseAndTouch", "Z");
346 
347  if (!fidSeparateMouseAndTouch) {
348  __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java static fields, do you have the latest version of SDLActivity.java?");
349  }
350 
351  checkJNIReady();
352 }
353 
354 /* Audio initialization -- called before SDL_main() to initialize JNI bindings */
355 JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv* mEnv, jclass cls)
356 {
357  __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "AUDIO nativeSetupJNI()");
358 
360 
361  mAudioManagerClass = (jclass)((*mEnv)->NewGlobalRef(mEnv, cls));
362 
363  midAudioOpen = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
364  "audioOpen", "(IZZI)I");
365  midAudioWriteShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
366  "audioWriteShortBuffer", "([S)V");
367  midAudioWriteByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
368  "audioWriteByteBuffer", "([B)V");
369  midAudioClose = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
370  "audioClose", "()V");
371  midCaptureOpen = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
372  "captureOpen", "(IZZI)I");
373  midCaptureReadShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
374  "captureReadShortBuffer", "([SZ)I");
375  midCaptureReadByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
376  "captureReadByteBuffer", "([BZ)I");
377  midCaptureClose = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
378  "captureClose", "()V");
379 
380  if (!midAudioOpen || !midAudioWriteShortBuffer || !midAudioWriteByteBuffer || !midAudioClose ||
381  !midCaptureOpen || !midCaptureReadShortBuffer || !midCaptureReadByteBuffer || !midCaptureClose) {
382  __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLAudioManager.java?");
383  }
384 
385  checkJNIReady();
386 }
387 
388 /* Controller initialization -- called before SDL_main() to initialize JNI bindings */
389 JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(JNIEnv* mEnv, jclass cls)
390 {
391  __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "CONTROLLER nativeSetupJNI()");
392 
394 
395  mControllerManagerClass = (jclass)((*mEnv)->NewGlobalRef(mEnv, cls));
396 
397  midPollInputDevices = (*mEnv)->GetStaticMethodID(mEnv, mControllerManagerClass,
398  "pollInputDevices", "()V");
399  midPollHapticDevices = (*mEnv)->GetStaticMethodID(mEnv, mControllerManagerClass,
400  "pollHapticDevices", "()V");
401  midHapticRun = (*mEnv)->GetStaticMethodID(mEnv, mControllerManagerClass,
402  "hapticRun", "(II)V");
403 
404  if (!midPollInputDevices || !midPollHapticDevices || !midHapticRun) {
405  __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLControllerManager.java?");
406  }
407 
408  checkJNIReady();
409 }
410 
411 /* SDL main function prototype */
412 typedef int (*SDL_main_func)(int argc, char *argv[]);
413 
414 /* Start up the SDL app */
415 JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeRunMain)(JNIEnv* env, jclass cls, jstring library, jstring function, jobject array)
416 {
417  int status = -1;
418  const char *library_file;
419  void *library_handle;
420 
421  __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeRunMain()");
422 
423  library_file = (*env)->GetStringUTFChars(env, library, NULL);
424  library_handle = dlopen(library_file, RTLD_GLOBAL);
425  if (library_handle) {
426  const char *function_name;
427  SDL_main_func SDL_main;
428 
429  function_name = (*env)->GetStringUTFChars(env, function, NULL);
430  SDL_main = (SDL_main_func)dlsym(library_handle, function_name);
431  if (SDL_main) {
432  int i;
433  int argc;
434  int len;
435  char **argv;
436 
437  /* Prepare the arguments. */
438  len = (*env)->GetArrayLength(env, array);
439  argv = SDL_stack_alloc(char*, 1 + len + 1);
440  argc = 0;
441  /* Use the name "app_process" so PHYSFS_platformCalcBaseDir() works.
442  https://bitbucket.org/MartinFelis/love-android-sdl2/issue/23/release-build-crash-on-start
443  */
444  argv[argc++] = SDL_strdup("app_process");
445  for (i = 0; i < len; ++i) {
446  const char* utf;
447  char* arg = NULL;
448  jstring string = (*env)->GetObjectArrayElement(env, array, i);
449  if (string) {
450  utf = (*env)->GetStringUTFChars(env, string, 0);
451  if (utf) {
452  arg = SDL_strdup(utf);
453  (*env)->ReleaseStringUTFChars(env, string, utf);
454  }
455  (*env)->DeleteLocalRef(env, string);
456  }
457  if (!arg) {
458  arg = SDL_strdup("");
459  }
460  argv[argc++] = arg;
461  }
462  argv[argc] = NULL;
463 
464 
465  /* Run the application. */
466  status = SDL_main(argc, argv);
467 
468  /* Release the arguments. */
469  for (i = 0; i < argc; ++i) {
470  SDL_free(argv[i]);
471  }
472  SDL_stack_free(argv);
473 
474  } else {
475  __android_log_print(ANDROID_LOG_ERROR, "SDL", "nativeRunMain(): Couldn't find function %s in library %s", function_name, library_file);
476  }
477  (*env)->ReleaseStringUTFChars(env, function, function_name);
478 
479  dlclose(library_handle);
480 
481  } else {
482  __android_log_print(ANDROID_LOG_ERROR, "SDL", "nativeRunMain(): Couldn't load library %s", library_file);
483  }
484  (*env)->ReleaseStringUTFChars(env, library, library_file);
485 
486  /* Do not issue an exit or the whole application will terminate instead of just the SDL thread */
487  /* exit(status); */
488 
489  return status;
490 }
491 
492 /* Drop file */
493 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeDropFile)(
494  JNIEnv* env, jclass jcls,
495  jstring filename)
496 {
497  const char *path = (*env)->GetStringUTFChars(env, filename, NULL);
498  SDL_SendDropFile(NULL, path);
499  (*env)->ReleaseStringUTFChars(env, filename, path);
501 }
502 
503 /* Resize */
504 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeResize)(
505  JNIEnv* env, jclass jcls,
506  jint width, jint height, jint format, jfloat rate)
507 {
508  Android_SetScreenResolution(width, height, format, rate);
509 }
510 
511 /* Paddown */
512 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)(
513  JNIEnv* env, jclass jcls,
514  jint device_id, jint keycode)
515 {
516  return Android_OnPadDown(device_id, keycode);
517 }
518 
519 /* Padup */
520 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)(
521  JNIEnv* env, jclass jcls,
522  jint device_id, jint keycode)
523 {
524  return Android_OnPadUp(device_id, keycode);
525 }
526 
527 /* Joy */
528 JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)(
529  JNIEnv* env, jclass jcls,
530  jint device_id, jint axis, jfloat value)
531 {
532  Android_OnJoy(device_id, axis, value);
533 }
534 
535 /* POV Hat */
536 JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
537  JNIEnv* env, jclass jcls,
538  jint device_id, jint hat_id, jint x, jint y)
539 {
540  Android_OnHat(device_id, hat_id, x, y);
541 }
542 
543 
544 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
545  JNIEnv* env, jclass jcls,
546  jint device_id, jstring device_name, jstring device_desc, jint is_accelerometer,
547  jint nbuttons, jint naxes, jint nhats, jint nballs)
548 {
549  int retval;
550  const char *name = (*env)->GetStringUTFChars(env, device_name, NULL);
551  const char *desc = (*env)->GetStringUTFChars(env, device_desc, NULL);
552 
553  retval = Android_AddJoystick(device_id, name, desc, (SDL_bool) is_accelerometer, nbuttons, naxes, nhats, nballs);
554 
555  (*env)->ReleaseStringUTFChars(env, device_name, name);
556  (*env)->ReleaseStringUTFChars(env, device_desc, desc);
557 
558  return retval;
559 }
560 
561 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)(
562  JNIEnv* env, jclass jcls,
563  jint device_id)
564 {
565  return Android_RemoveJoystick(device_id);
566 }
567 
568 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic)(
569  JNIEnv* env, jclass jcls, jint device_id, jstring device_name)
570 {
571  int retval;
572  const char *name = (*env)->GetStringUTFChars(env, device_name, NULL);
573 
574  retval = Android_AddHaptic(device_id, name);
575 
576  (*env)->ReleaseStringUTFChars(env, device_name, name);
577 
578  return retval;
579 }
580 
581 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)(
582  JNIEnv* env, jclass jcls, jint device_id)
583 {
584  return Android_RemoveHaptic(device_id);
585 }
586 
587 
588 /* Surface Created */
589 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(JNIEnv* env, jclass jcls)
590 {
593 
595  return;
596  }
597 
598  _this = SDL_GetVideoDevice();
600 
601  /* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
602  if (data->egl_surface == EGL_NO_SURFACE) {
603  if(data->native_window) {
604  ANativeWindow_release(data->native_window);
605  }
607  data->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType) data->native_window);
608  }
609 
610  /* GL Context handling is done in the event loop because this function is run from the Java thread */
611 
612 }
613 
614 /* Surface Destroyed */
615 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)(JNIEnv* env, jclass jcls)
616 {
617  /* We have to clear the current context and destroy the egl surface here
618  * Otherwise there's BAD_NATIVE_WINDOW errors coming from eglCreateWindowSurface on resume
619  * Ref: http://stackoverflow.com/questions/8762589/eglcreatewindowsurface-on-ics-and-switching-from-2d-to-3d
620  */
623 
625  return;
626  }
627 
628  _this = SDL_GetVideoDevice();
630 
631  if (data->egl_surface != EGL_NO_SURFACE) {
632  SDL_EGL_MakeCurrent(_this, NULL, NULL);
633  SDL_EGL_DestroySurface(_this, data->egl_surface);
634  data->egl_surface = EGL_NO_SURFACE;
635  }
636 
637  /* GL Context handling is done in the event loop because this function is run from the Java thread */
638 
639 }
640 
641 /* Keydown */
642 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyDown)(
643  JNIEnv* env, jclass jcls,
644  jint keycode)
645 {
646  Android_OnKeyDown(keycode);
647 }
648 
649 /* Keyup */
650 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyUp)(
651  JNIEnv* env, jclass jcls,
652  jint keycode)
653 {
654  Android_OnKeyUp(keycode);
655 }
656 
657 /* Keyboard Focus Lost */
658 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyboardFocusLost)(
659  JNIEnv* env, jclass jcls)
660 {
661  /* Calling SDL_StopTextInput will take care of hiding the keyboard and cleaning up the DummyText widget */
663 }
664 
665 
666 /* Touch */
667 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeTouch)(
668  JNIEnv* env, jclass jcls,
669  jint touch_device_id_in, jint pointer_finger_id_in,
670  jint action, jfloat x, jfloat y, jfloat p)
671 {
672  Android_OnTouch(touch_device_id_in, pointer_finger_id_in, action, x, y, p);
673 }
674 
675 /* Mouse */
676 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeMouse)(
677  JNIEnv* env, jclass jcls,
678  jint button, jint action, jfloat x, jfloat y)
679 {
680  Android_OnMouse(button, action, x, y);
681 }
682 
683 /* Accelerometer */
684 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeAccel)(
685  JNIEnv* env, jclass jcls,
686  jfloat x, jfloat y, jfloat z)
687 {
688  fLastAccelerometer[0] = x;
689  fLastAccelerometer[1] = y;
690  fLastAccelerometer[2] = z;
691  bHasNewData = SDL_TRUE;
692 }
693 
694 /* Clipboard */
695 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeClipboardChanged)(
696  JNIEnv* env, jclass jcls)
697 {
699 }
700 
701 /* Low memory */
702 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeLowMemory)(
703  JNIEnv* env, jclass cls)
704 {
706 }
707 
708 /* Quit */
709 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeQuit)(
710  JNIEnv* env, jclass cls)
711 {
712  /* Discard previous events. The user should have handled state storage
713  * in SDL_APP_WILLENTERBACKGROUND. After nativeQuit() is called, no
714  * events other than SDL_QUIT and SDL_APP_TERMINATING should fire */
716  /* Inject a SDL_QUIT event */
717  SDL_SendQuit();
719  /* Resume the event loop so that the app can catch SDL_QUIT which
720  * should now be the top event in the event queue. */
722 }
723 
724 /* Pause */
725 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePause)(
726  JNIEnv* env, jclass cls)
727 {
728  __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativePause()");
729 
730  if (Android_Window) {
735 
736  /* *After* sending the relevant events, signal the pause semaphore
737  * so the event loop knows to pause and (optionally) block itself */
739  }
740 }
741 
742 /* Resume */
743 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeResume)(
744  JNIEnv* env, jclass cls)
745 {
746  __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeResume()");
747 
748  if (Android_Window) {
753  /* Signal the resume semaphore so the event loop knows to resume and restore the GL Context
754  * We can't restore the GL Context here because it needs to be done on the SDL main thread
755  * and this function will be called from the Java thread instead.
756  */
758  }
759 }
760 
761 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeCommitText)(
762  JNIEnv* env, jclass cls,
763  jstring text, jint newCursorPosition)
764 {
765  const char *utftext = (*env)->GetStringUTFChars(env, text, NULL);
766 
767  SDL_SendKeyboardText(utftext);
768 
769  (*env)->ReleaseStringUTFChars(env, text, utftext);
770 }
771 
772 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeGenerateScancodeForUnichar)(
773  JNIEnv* env, jclass cls,
774  jchar chUnicode)
775 {
777  uint16_t mod = 0;
778 
779  // We do not care about bigger than 127.
780  if (chUnicode < 127) {
782  code = info.code;
783  mod = info.mod;
784  }
785 
786  if (mod & KMOD_SHIFT) {
787  /* If character uses shift, press shift down */
789  }
790 
791  /* send a keydown and keyup even for the character */
794 
795  if (mod & KMOD_SHIFT) {
796  /* If character uses shift, press shift back up */
798  }
799 }
800 
801 
802 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeSetComposingText)(
803  JNIEnv* env, jclass cls,
804  jstring text, jint newCursorPosition)
805 {
806  const char *utftext = (*env)->GetStringUTFChars(env, text, NULL);
807 
808  SDL_SendEditingText(utftext, 0, 0);
809 
810  (*env)->ReleaseStringUTFChars(env, text, utftext);
811 }
812 
813 JNIEXPORT jstring JNICALL SDL_JAVA_INTERFACE(nativeGetHint)(
814  JNIEnv* env, jclass cls,
815  jstring name)
816 {
817  const char *utfname = (*env)->GetStringUTFChars(env, name, NULL);
818  const char *hint = SDL_GetHint(utfname);
819 
820  jstring result = (*env)->NewStringUTF(env, hint);
821  (*env)->ReleaseStringUTFChars(env, name, utfname);
822 
823  return result;
824 }
825 
826 JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetenv)(
827  JNIEnv* env, jclass cls,
828  jstring name, jstring value)
829 {
830  const char *utfname = (*env)->GetStringUTFChars(env, name, NULL);
831  const char *utfvalue = (*env)->GetStringUTFChars(env, value, NULL);
832 
833  SDL_setenv(utfname, utfvalue, 1);
834 
835  (*env)->ReleaseStringUTFChars(env, name, utfname);
836  (*env)->ReleaseStringUTFChars(env, value, utfvalue);
837 
838 }
839 
840 /*******************************************************************************
841  Functions called by SDL into Java
842 *******************************************************************************/
843 
844 static int s_active = 0;
845 struct LocalReferenceHolder
846 {
847  JNIEnv *m_env;
848  const char *m_func;
849 };
850 
851 static struct LocalReferenceHolder LocalReferenceHolder_Setup(const char *func)
852 {
853  struct LocalReferenceHolder refholder;
854  refholder.m_env = NULL;
855  refholder.m_func = func;
856 #ifdef DEBUG_JNI
857  SDL_Log("Entering function %s", func);
858 #endif
859  return refholder;
860 }
861 
862 static SDL_bool LocalReferenceHolder_Init(struct LocalReferenceHolder *refholder, JNIEnv *env)
863 {
864  const int capacity = 16;
865  if ((*env)->PushLocalFrame(env, capacity) < 0) {
866  SDL_SetError("Failed to allocate enough JVM local references");
867  return SDL_FALSE;
868  }
869  ++s_active;
870  refholder->m_env = env;
871  return SDL_TRUE;
872 }
873 
874 static void LocalReferenceHolder_Cleanup(struct LocalReferenceHolder *refholder)
875 {
876 #ifdef DEBUG_JNI
877  SDL_Log("Leaving function %s", refholder->m_func);
878 #endif
879  if (refholder->m_env) {
880  JNIEnv* env = refholder->m_env;
881  (*env)->PopLocalFrame(env, NULL);
882  --s_active;
883  }
884 }
885 
886 static SDL_bool LocalReferenceHolder_IsActive(void)
887 {
888  return s_active > 0;
889 }
890 
891 ANativeWindow* Android_JNI_GetNativeWindow(void)
892 {
893  ANativeWindow* anw;
894  jobject s;
895  JNIEnv *env = Android_JNI_GetEnv();
896 
897  s = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetNativeSurface);
898  anw = ANativeWindow_fromSurface(env, s);
899  (*env)->DeleteLocalRef(env, s);
900 
901  return anw;
902 }
903 
904 void Android_JNI_SetActivityTitle(const char *title)
905 {
906  JNIEnv *mEnv = Android_JNI_GetEnv();
907 
908  jstring jtitle = (jstring)((*mEnv)->NewStringUTF(mEnv, title));
909  (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, midSetActivityTitle, jtitle);
910  (*mEnv)->DeleteLocalRef(mEnv, jtitle);
911 }
912 
913 void Android_JNI_SetWindowStyle(SDL_bool fullscreen)
914 {
915  JNIEnv *mEnv = Android_JNI_GetEnv();
916  (*mEnv)->CallStaticVoidMethod(mEnv, mActivityClass, midSetWindowStyle, fullscreen ? 1 : 0);
917 }
918 
919 void Android_JNI_SetOrientation(int w, int h, int resizable, const char *hint)
920 {
921  JNIEnv *mEnv = Android_JNI_GetEnv();
922 
923  jstring jhint = (jstring)((*mEnv)->NewStringUTF(mEnv, (hint ? hint : "")));
924  (*mEnv)->CallStaticVoidMethod(mEnv, mActivityClass, midSetOrientation, w, h, (resizable? 1 : 0), jhint);
925  (*mEnv)->DeleteLocalRef(mEnv, jhint);
926 }
927 
929 {
930  int i;
931  SDL_bool retval = SDL_FALSE;
932 
933  if (bHasNewData) {
934  for (i = 0; i < 3; ++i) {
935  values[i] = fLastAccelerometer[i];
936  }
937  bHasNewData = SDL_FALSE;
938  retval = SDL_TRUE;
939  }
940 
941  return retval;
942 }
943 
944 static void Android_JNI_ThreadDestroyed(void* value)
945 {
946  /* The thread is being destroyed, detach it from the Java VM and set the mThreadKey value to NULL as required */
947  JNIEnv *env = (JNIEnv*) value;
948  if (env != NULL) {
949  (*mJavaVM)->DetachCurrentThread(mJavaVM);
950  pthread_setspecific(mThreadKey, NULL);
951  }
952 }
953 
954 JNIEnv* Android_JNI_GetEnv(void)
955 {
956  /* From http://developer.android.com/guide/practices/jni.html
957  * All threads are Linux threads, scheduled by the kernel.
958  * They're usually started from managed code (using Thread.start), but they can also be created elsewhere and then
959  * attached to the JavaVM. For example, a thread started with pthread_create can be attached with the
960  * JNI AttachCurrentThread or AttachCurrentThreadAsDaemon functions. Until a thread is attached, it has no JNIEnv,
961  * and cannot make JNI calls.
962  * Attaching a natively-created thread causes a java.lang.Thread object to be constructed and added to the "main"
963  * ThreadGroup, making it visible to the debugger. Calling AttachCurrentThread on an already-attached thread
964  * is a no-op.
965  * Note: You can call this function any number of times for the same thread, there's no harm in it
966  */
967 
968  JNIEnv *env;
969  int status = (*mJavaVM)->AttachCurrentThread(mJavaVM, &env, NULL);
970  if(status < 0) {
971  LOGE("failed to attach current thread");
972  return 0;
973  }
974 
975  /* From http://developer.android.com/guide/practices/jni.html
976  * Threads attached through JNI must call DetachCurrentThread before they exit. If coding this directly is awkward,
977  * in Android 2.0 (Eclair) and higher you can use pthread_key_create to define a destructor function that will be
978  * called before the thread exits, and call DetachCurrentThread from there. (Use that key with pthread_setspecific
979  * to store the JNIEnv in thread-local-storage; that way it'll be passed into your destructor as the argument.)
980  * Note: The destructor is not called unless the stored value is != NULL
981  * Note: You can call this function any number of times for the same thread, there's no harm in it
982  * (except for some lost CPU cycles)
983  */
984  pthread_setspecific(mThreadKey, (void*) env);
985 
986  return env;
987 }
988 
989 int Android_JNI_SetupThread(void)
990 {
992  return 1;
993 }
994 
995 /*
996  * Audio support
997  */
998 static jboolean audioBuffer16Bit = JNI_FALSE;
999 static jobject audioBuffer = NULL;
1000 static void* audioBufferPinned = NULL;
1001 static jboolean captureBuffer16Bit = JNI_FALSE;
1002 static jobject captureBuffer = NULL;
1003 
1004 int Android_JNI_OpenAudioDevice(int iscapture, int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames)
1005 {
1006  jboolean audioBufferStereo;
1007  int audioBufferFrames;
1008  jobject jbufobj = NULL;
1009  jboolean isCopy;
1010 
1011  JNIEnv *env = Android_JNI_GetEnv();
1012 
1013  if (!env) {
1014  LOGE("callback_handler: failed to attach current thread");
1015  }
1017 
1018  audioBufferStereo = channelCount > 1;
1019 
1020  if (iscapture) {
1021  __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for capture");
1022  captureBuffer16Bit = is16Bit;
1023  if ((*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureOpen, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames) != 0) {
1024  /* Error during audio initialization */
1025  __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: error on AudioRecord initialization!");
1026  return 0;
1027  }
1028  } else {
1029  __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device for output");
1030  audioBuffer16Bit = is16Bit;
1031  if ((*env)->CallStaticIntMethod(env, mAudioManagerClass, midAudioOpen, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames) != 0) {
1032  /* Error during audio initialization */
1033  __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: error on AudioTrack initialization!");
1034  return 0;
1035  }
1036  }
1037 
1038  /* Allocating the audio buffer from the Java side and passing it as the return value for audioInit no longer works on
1039  * Android >= 4.2 due to a "stale global reference" error. So now we allocate this buffer directly from this side. */
1040 
1041  if (is16Bit) {
1042  jshortArray audioBufferLocal = (*env)->NewShortArray(env, desiredBufferFrames * (audioBufferStereo ? 2 : 1));
1043  if (audioBufferLocal) {
1044  jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
1045  (*env)->DeleteLocalRef(env, audioBufferLocal);
1046  }
1047  }
1048  else {
1049  jbyteArray audioBufferLocal = (*env)->NewByteArray(env, desiredBufferFrames * (audioBufferStereo ? 2 : 1));
1050  if (audioBufferLocal) {
1051  jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
1052  (*env)->DeleteLocalRef(env, audioBufferLocal);
1053  }
1054  }
1055 
1056  if (jbufobj == NULL) {
1057  __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: could not allocate an audio buffer!");
1058  return 0;
1059  }
1060 
1061  if (iscapture) {
1062  captureBuffer = jbufobj;
1063  } else {
1064  audioBuffer = jbufobj;
1065  }
1066 
1067  isCopy = JNI_FALSE;
1068 
1069  if (is16Bit) {
1070  if (!iscapture) {
1071  audioBufferPinned = (*env)->GetShortArrayElements(env, (jshortArray)audioBuffer, &isCopy);
1072  }
1073  audioBufferFrames = (*env)->GetArrayLength(env, (jshortArray)audioBuffer);
1074  } else {
1075  if (!iscapture) {
1076  audioBufferPinned = (*env)->GetByteArrayElements(env, (jbyteArray)audioBuffer, &isCopy);
1077  }
1078  audioBufferFrames = (*env)->GetArrayLength(env, (jbyteArray)audioBuffer);
1079  }
1080 
1081  if (audioBufferStereo) {
1082  audioBufferFrames /= 2;
1083  }
1084 
1085  return audioBufferFrames;
1086 }
1087 
1088 int Android_JNI_GetDisplayDPI(float *ddpi, float *xdpi, float *ydpi)
1089 {
1090  JNIEnv *env = Android_JNI_GetEnv();
1091 
1092  jobject jDisplayObj = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetDisplayDPI);
1093  jclass jDisplayClass = (*env)->GetObjectClass(env, jDisplayObj);
1094 
1095  jfieldID fidXdpi = (*env)->GetFieldID(env, jDisplayClass, "xdpi", "F");
1096  jfieldID fidYdpi = (*env)->GetFieldID(env, jDisplayClass, "ydpi", "F");
1097  jfieldID fidDdpi = (*env)->GetFieldID(env, jDisplayClass, "densityDpi", "I");
1098 
1099  float nativeXdpi = (*env)->GetFloatField(env, jDisplayObj, fidXdpi);
1100  float nativeYdpi = (*env)->GetFloatField(env, jDisplayObj, fidYdpi);
1101  int nativeDdpi = (*env)->GetIntField(env, jDisplayObj, fidDdpi);
1102 
1103 
1104  (*env)->DeleteLocalRef(env, jDisplayObj);
1105  (*env)->DeleteLocalRef(env, jDisplayClass);
1106 
1107  if (ddpi) {
1108  *ddpi = (float)nativeDdpi;
1109  }
1110  if (xdpi) {
1111  *xdpi = nativeXdpi;
1112  }
1113  if (ydpi) {
1114  *ydpi = nativeYdpi;
1115  }
1116 
1117  return 0;
1118 }
1119 
1120 void * Android_JNI_GetAudioBuffer(void)
1121 {
1122  return audioBufferPinned;
1123 }
1124 
1126 {
1127  JNIEnv *mAudioEnv = Android_JNI_GetEnv();
1128 
1129  if (audioBuffer16Bit) {
1130  (*mAudioEnv)->ReleaseShortArrayElements(mAudioEnv, (jshortArray)audioBuffer, (jshort *)audioBufferPinned, JNI_COMMIT);
1131  (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mAudioManagerClass, midAudioWriteShortBuffer, (jshortArray)audioBuffer);
1132  } else {
1133  (*mAudioEnv)->ReleaseByteArrayElements(mAudioEnv, (jbyteArray)audioBuffer, (jbyte *)audioBufferPinned, JNI_COMMIT);
1134  (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mAudioManagerClass, midAudioWriteByteBuffer, (jbyteArray)audioBuffer);
1135  }
1136 
1137  /* JNI_COMMIT means the changes are committed to the VM but the buffer remains pinned */
1138 }
1139 
1140 int Android_JNI_CaptureAudioBuffer(void *buffer, int buflen)
1141 {
1142  JNIEnv *env = Android_JNI_GetEnv();
1143  jboolean isCopy = JNI_FALSE;
1144  jint br;
1145 
1146  if (captureBuffer16Bit) {
1147  SDL_assert((*env)->GetArrayLength(env, (jshortArray)captureBuffer) == (buflen / 2));
1148  br = (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_TRUE);
1149  if (br > 0) {
1150  jshort *ptr = (*env)->GetShortArrayElements(env, (jshortArray)captureBuffer, &isCopy);
1151  br *= 2;
1152  SDL_memcpy(buffer, ptr, br);
1153  (*env)->ReleaseShortArrayElements(env, (jshortArray)captureBuffer, (jshort *)ptr, JNI_ABORT);
1154  }
1155  } else {
1156  SDL_assert((*env)->GetArrayLength(env, (jshortArray)captureBuffer) == buflen);
1157  br = (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_TRUE);
1158  if (br > 0) {
1159  jbyte *ptr = (*env)->GetByteArrayElements(env, (jbyteArray)captureBuffer, &isCopy);
1160  SDL_memcpy(buffer, ptr, br);
1161  (*env)->ReleaseByteArrayElements(env, (jbyteArray)captureBuffer, (jbyte *)ptr, JNI_ABORT);
1162  }
1163  }
1164 
1165  return (int) br;
1166 }
1167 
1169 {
1170  JNIEnv *env = Android_JNI_GetEnv();
1171 #if 0 /* !!! FIXME: this needs API 23, or it'll do blocking reads and never end. */
1172  if (captureBuffer16Bit) {
1173  const jint len = (*env)->GetArrayLength(env, (jshortArray)captureBuffer);
1174  while ((*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_FALSE) == len) { /* spin */ }
1175  } else {
1176  const jint len = (*env)->GetArrayLength(env, (jbyteArray)captureBuffer);
1177  while ((*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_FALSE) == len) { /* spin */ }
1178  }
1179 #else
1180  if (captureBuffer16Bit) {
1181  (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_FALSE);
1182  } else {
1183  (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_FALSE);
1184  }
1185 #endif
1186 }
1187 
1188 void Android_JNI_CloseAudioDevice(const int iscapture)
1189 {
1190  JNIEnv *env = Android_JNI_GetEnv();
1191 
1192  if (iscapture) {
1193  (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midCaptureClose);
1194  if (captureBuffer) {
1195  (*env)->DeleteGlobalRef(env, captureBuffer);
1196  captureBuffer = NULL;
1197  }
1198  } else {
1199  (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midAudioClose);
1200  if (audioBuffer) {
1201  (*env)->DeleteGlobalRef(env, audioBuffer);
1202  audioBuffer = NULL;
1203  audioBufferPinned = NULL;
1204  }
1205  }
1206 }
1207 
1208 /* Test for an exception and call SDL_SetError with its detail if one occurs */
1209 /* If the parameter silent is truthy then SDL_SetError() will not be called. */
1210 static SDL_bool Android_JNI_ExceptionOccurred(SDL_bool silent)
1211 {
1212  JNIEnv *mEnv = Android_JNI_GetEnv();
1213  jthrowable exception;
1214 
1215  SDL_assert(LocalReferenceHolder_IsActive());
1216 
1217  exception = (*mEnv)->ExceptionOccurred(mEnv);
1218  if (exception != NULL) {
1219  jmethodID mid;
1220 
1221  /* Until this happens most JNI operations have undefined behaviour */
1222  (*mEnv)->ExceptionClear(mEnv);
1223 
1224  if (!silent) {
1225  jclass exceptionClass = (*mEnv)->GetObjectClass(mEnv, exception);
1226  jclass classClass = (*mEnv)->FindClass(mEnv, "java/lang/Class");
1227  jstring exceptionName;
1228  const char* exceptionNameUTF8;
1229  jstring exceptionMessage;
1230 
1231  mid = (*mEnv)->GetMethodID(mEnv, classClass, "getName", "()Ljava/lang/String;");
1232  exceptionName = (jstring)(*mEnv)->CallObjectMethod(mEnv, exceptionClass, mid);
1233  exceptionNameUTF8 = (*mEnv)->GetStringUTFChars(mEnv, exceptionName, 0);
1234 
1235  mid = (*mEnv)->GetMethodID(mEnv, exceptionClass, "getMessage", "()Ljava/lang/String;");
1236  exceptionMessage = (jstring)(*mEnv)->CallObjectMethod(mEnv, exception, mid);
1237 
1238  if (exceptionMessage != NULL) {
1239  const char* exceptionMessageUTF8 = (*mEnv)->GetStringUTFChars(mEnv, exceptionMessage, 0);
1240  SDL_SetError("%s: %s", exceptionNameUTF8, exceptionMessageUTF8);
1241  (*mEnv)->ReleaseStringUTFChars(mEnv, exceptionMessage, exceptionMessageUTF8);
1242  } else {
1243  SDL_SetError("%s", exceptionNameUTF8);
1244  }
1245 
1246  (*mEnv)->ReleaseStringUTFChars(mEnv, exceptionName, exceptionNameUTF8);
1247  }
1248 
1249  return SDL_TRUE;
1250  }
1251 
1252  return SDL_FALSE;
1253 }
1254 
1255 static int Internal_Android_JNI_FileOpen(SDL_RWops* ctx)
1256 {
1257  struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
1258 
1259  int result = 0;
1260 
1261  jmethodID mid;
1262  jobject context;
1263  jobject assetManager;
1264  jobject inputStream;
1265  jclass channels;
1266  jobject readableByteChannel;
1267  jstring fileNameJString;
1268  jobject fd;
1269  jclass fdCls;
1270  jfieldID descriptor;
1271 
1272  JNIEnv *mEnv = Android_JNI_GetEnv();
1273  if (!LocalReferenceHolder_Init(&refs, mEnv)) {
1274  goto failure;
1275  }
1276 
1277  fileNameJString = (jstring)ctx->hidden.androidio.fileNameRef;
1278  ctx->hidden.androidio.position = 0;
1279 
1280  /* context = SDLActivity.getContext(); */
1281  context = (*mEnv)->CallStaticObjectMethod(mEnv, mActivityClass, midGetContext);
1282 
1283  /* assetManager = context.getAssets(); */
1284  mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, context),
1285  "getAssets", "()Landroid/content/res/AssetManager;");
1286  assetManager = (*mEnv)->CallObjectMethod(mEnv, context, mid);
1287 
1288  /* First let's try opening the file to obtain an AssetFileDescriptor.
1289  * This method reads the files directly from the APKs using standard *nix calls
1290  */
1291  mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, assetManager), "openFd", "(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;");
1292  inputStream = (*mEnv)->CallObjectMethod(mEnv, assetManager, mid, fileNameJString);
1293  if (Android_JNI_ExceptionOccurred(SDL_TRUE)) {
1294  goto fallback;
1295  }
1296 
1297  mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream), "getStartOffset", "()J");
1298  ctx->hidden.androidio.offset = (*mEnv)->CallLongMethod(mEnv, inputStream, mid);
1299  if (Android_JNI_ExceptionOccurred(SDL_TRUE)) {
1300  goto fallback;
1301  }
1302 
1303  mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream), "getDeclaredLength", "()J");
1304  ctx->hidden.androidio.size = (*mEnv)->CallLongMethod(mEnv, inputStream, mid);
1305  if (Android_JNI_ExceptionOccurred(SDL_TRUE)) {
1306  goto fallback;
1307  }
1308 
1309  mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream), "getFileDescriptor", "()Ljava/io/FileDescriptor;");
1310  fd = (*mEnv)->CallObjectMethod(mEnv, inputStream, mid);
1311  fdCls = (*mEnv)->GetObjectClass(mEnv, fd);
1312  descriptor = (*mEnv)->GetFieldID(mEnv, fdCls, "descriptor", "I");
1313  ctx->hidden.androidio.fd = (*mEnv)->GetIntField(mEnv, fd, descriptor);
1314  ctx->hidden.androidio.assetFileDescriptorRef = (*mEnv)->NewGlobalRef(mEnv, inputStream);
1315 
1316  /* Seek to the correct offset in the file. */
1317  lseek(ctx->hidden.androidio.fd, (off_t)ctx->hidden.androidio.offset, SEEK_SET);
1318 
1319  if (0) {
1320 fallback:
1321  /* Disabled log message because of spam on the Nexus 7 */
1322  /* __android_log_print(ANDROID_LOG_DEBUG, "SDL", "Falling back to legacy InputStream method for opening file"); */
1323 
1324  /* Try the old method using InputStream */
1325  ctx->hidden.androidio.assetFileDescriptorRef = NULL;
1326 
1327  /* inputStream = assetManager.open(<filename>); */
1328  mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, assetManager),
1329  "open", "(Ljava/lang/String;I)Ljava/io/InputStream;");
1330  inputStream = (*mEnv)->CallObjectMethod(mEnv, assetManager, mid, fileNameJString, 1 /* ACCESS_RANDOM */);
1331  if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
1332  /* Try fallback to APK expansion files */
1333  inputStream = (*mEnv)->CallStaticObjectMethod(mEnv, mActivityClass, midOpenAPKExpansionInputStream, fileNameJString);
1334 
1335  /* Exception is checked first because it always needs to be cleared.
1336  * If no exception occurred then the last SDL error message is kept.
1337  */
1338  if (Android_JNI_ExceptionOccurred(SDL_FALSE) || !inputStream) {
1339  goto failure;
1340  }
1341  }
1342 
1343  ctx->hidden.androidio.inputStreamRef = (*mEnv)->NewGlobalRef(mEnv, inputStream);
1344 
1345  /* Despite all the visible documentation on [Asset]InputStream claiming
1346  * that the .available() method is not guaranteed to return the entire file
1347  * size, comments in <sdk>/samples/<ver>/ApiDemos/src/com/example/ ...
1348  * android/apis/content/ReadAsset.java imply that Android's
1349  * AssetInputStream.available() /will/ always return the total file size
1350  */
1351 
1352  /* size = inputStream.available(); */
1353  mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
1354  "available", "()I");
1355  ctx->hidden.androidio.size = (long)(*mEnv)->CallIntMethod(mEnv, inputStream, mid);
1356  if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
1357  goto failure;
1358  }
1359 
1360  /* readableByteChannel = Channels.newChannel(inputStream); */
1361  channels = (*mEnv)->FindClass(mEnv, "java/nio/channels/Channels");
1362  mid = (*mEnv)->GetStaticMethodID(mEnv, channels,
1363  "newChannel",
1364  "(Ljava/io/InputStream;)Ljava/nio/channels/ReadableByteChannel;");
1365  readableByteChannel = (*mEnv)->CallStaticObjectMethod(
1366  mEnv, channels, mid, inputStream);
1367  if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
1368  goto failure;
1369  }
1370 
1371  ctx->hidden.androidio.readableByteChannelRef =
1372  (*mEnv)->NewGlobalRef(mEnv, readableByteChannel);
1373 
1374  /* Store .read id for reading purposes */
1375  mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, readableByteChannel),
1376  "read", "(Ljava/nio/ByteBuffer;)I");
1377  ctx->hidden.androidio.readMethod = mid;
1378  }
1379 
1380  if (0) {
1381 failure:
1382  result = -1;
1383 
1384  (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.fileNameRef);
1385 
1386  if(ctx->hidden.androidio.inputStreamRef != NULL) {
1387  (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.inputStreamRef);
1388  }
1389 
1390  if(ctx->hidden.androidio.readableByteChannelRef != NULL) {
1391  (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.readableByteChannelRef);
1392  }
1393 
1394  if(ctx->hidden.androidio.assetFileDescriptorRef != NULL) {
1395  (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.assetFileDescriptorRef);
1396  }
1397 
1398  }
1399 
1400  LocalReferenceHolder_Cleanup(&refs);
1401  return result;
1402 }
1403 
1405  const char* fileName, const char* mode)
1406 {
1407  struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
1408  JNIEnv *mEnv = Android_JNI_GetEnv();
1409  int retval;
1410  jstring fileNameJString;
1411 
1412  if (!LocalReferenceHolder_Init(&refs, mEnv)) {
1413  LocalReferenceHolder_Cleanup(&refs);
1414  return -1;
1415  }
1416 
1417  if (!ctx) {
1418  LocalReferenceHolder_Cleanup(&refs);
1419  return -1;
1420  }
1421 
1422  fileNameJString = (*mEnv)->NewStringUTF(mEnv, fileName);
1423  ctx->hidden.androidio.fileNameRef = (*mEnv)->NewGlobalRef(mEnv, fileNameJString);
1424  ctx->hidden.androidio.inputStreamRef = NULL;
1425  ctx->hidden.androidio.readableByteChannelRef = NULL;
1426  ctx->hidden.androidio.readMethod = NULL;
1427  ctx->hidden.androidio.assetFileDescriptorRef = NULL;
1428 
1429  retval = Internal_Android_JNI_FileOpen(ctx);
1430  LocalReferenceHolder_Cleanup(&refs);
1431  return retval;
1432 }
1433 
1434 size_t Android_JNI_FileRead(SDL_RWops* ctx, void* buffer,
1435  size_t size, size_t maxnum)
1436 {
1437  struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
1438 
1439  if (ctx->hidden.androidio.assetFileDescriptorRef) {
1440  size_t bytesMax = size * maxnum;
1441  size_t result;
1442  if (ctx->hidden.androidio.size != -1 /* UNKNOWN_LENGTH */ && ctx->hidden.androidio.position + bytesMax > ctx->hidden.androidio.size) {
1443  bytesMax = ctx->hidden.androidio.size - ctx->hidden.androidio.position;
1444  }
1445  result = read(ctx->hidden.androidio.fd, buffer, bytesMax );
1446  if (result > 0) {
1447  ctx->hidden.androidio.position += result;
1448  LocalReferenceHolder_Cleanup(&refs);
1449  return result / size;
1450  }
1451  LocalReferenceHolder_Cleanup(&refs);
1452  return 0;
1453  } else {
1454  jlong bytesRemaining = (jlong) (size * maxnum);
1455  jlong bytesMax = (jlong) (ctx->hidden.androidio.size - ctx->hidden.androidio.position);
1456  int bytesRead = 0;
1457  JNIEnv *mEnv;
1458  jobject readableByteChannel;
1459  jmethodID readMethod;
1460  jobject byteBuffer;
1461 
1462  /* Don't read more bytes than those that remain in the file, otherwise we get an exception */
1463  if (bytesRemaining > bytesMax) bytesRemaining = bytesMax;
1464 
1465  mEnv = Android_JNI_GetEnv();
1466  if (!LocalReferenceHolder_Init(&refs, mEnv)) {
1467  LocalReferenceHolder_Cleanup(&refs);
1468  return 0;
1469  }
1470 
1471  readableByteChannel = (jobject)ctx->hidden.androidio.readableByteChannelRef;
1472  readMethod = (jmethodID)ctx->hidden.androidio.readMethod;
1473  byteBuffer = (*mEnv)->NewDirectByteBuffer(mEnv, buffer, bytesRemaining);
1474 
1475  while (bytesRemaining > 0) {
1476  /* result = readableByteChannel.read(...); */
1477  int result = (*mEnv)->CallIntMethod(mEnv, readableByteChannel, readMethod, byteBuffer);
1478 
1479  if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
1480  LocalReferenceHolder_Cleanup(&refs);
1481  return 0;
1482  }
1483 
1484  if (result < 0) {
1485  break;
1486  }
1487 
1488  bytesRemaining -= result;
1489  bytesRead += result;
1490  ctx->hidden.androidio.position += result;
1491  }
1492  LocalReferenceHolder_Cleanup(&refs);
1493  return bytesRead / size;
1494  }
1495 }
1496 
1497 size_t Android_JNI_FileWrite(SDL_RWops* ctx, const void* buffer,
1498  size_t size, size_t num)
1499 {
1500  SDL_SetError("Cannot write to Android package filesystem");
1501  return 0;
1502 }
1503 
1504 static int Internal_Android_JNI_FileClose(SDL_RWops* ctx, SDL_bool release)
1505 {
1506  struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
1507 
1508  int result = 0;
1509  JNIEnv *mEnv = Android_JNI_GetEnv();
1510 
1511  if (!LocalReferenceHolder_Init(&refs, mEnv)) {
1512  LocalReferenceHolder_Cleanup(&refs);
1513  return SDL_SetError("Failed to allocate enough JVM local references");
1514  }
1515 
1516  if (ctx) {
1517  if (release) {
1518  (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.fileNameRef);
1519  }
1520 
1521  if (ctx->hidden.androidio.assetFileDescriptorRef) {
1522  jobject inputStream = (jobject)ctx->hidden.androidio.assetFileDescriptorRef;
1523  jmethodID mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
1524  "close", "()V");
1525  (*mEnv)->CallVoidMethod(mEnv, inputStream, mid);
1526  (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.assetFileDescriptorRef);
1527  if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
1528  result = -1;
1529  }
1530  }
1531  else {
1532  jobject inputStream = (jobject)ctx->hidden.androidio.inputStreamRef;
1533 
1534  /* inputStream.close(); */
1535  jmethodID mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
1536  "close", "()V");
1537  (*mEnv)->CallVoidMethod(mEnv, inputStream, mid);
1538  (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.inputStreamRef);
1539  (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->hidden.androidio.readableByteChannelRef);
1540  if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
1541  result = -1;
1542  }
1543  }
1544 
1545  if (release) {
1546  SDL_FreeRW(ctx);
1547  }
1548  }
1549 
1550  LocalReferenceHolder_Cleanup(&refs);
1551  return result;
1552 }
1553 
1554 
1556 {
1557  return ctx->hidden.androidio.size;
1558 }
1559 
1561 {
1562  if (ctx->hidden.androidio.assetFileDescriptorRef) {
1563  off_t ret;
1564  switch (whence) {
1565  case RW_SEEK_SET:
1566  if (ctx->hidden.androidio.size != -1 /* UNKNOWN_LENGTH */ && offset > ctx->hidden.androidio.size) offset = ctx->hidden.androidio.size;
1567  offset += ctx->hidden.androidio.offset;
1568  break;
1569  case RW_SEEK_CUR:
1570  offset += ctx->hidden.androidio.position;
1571  if (ctx->hidden.androidio.size != -1 /* UNKNOWN_LENGTH */ && offset > ctx->hidden.androidio.size) offset = ctx->hidden.androidio.size;
1572  offset += ctx->hidden.androidio.offset;
1573  break;
1574  case RW_SEEK_END:
1575  offset = ctx->hidden.androidio.offset + ctx->hidden.androidio.size + offset;
1576  break;
1577  default:
1578  return SDL_SetError("Unknown value for 'whence'");
1579  }
1580  whence = SEEK_SET;
1581 
1582  ret = lseek(ctx->hidden.androidio.fd, (off_t)offset, SEEK_SET);
1583  if (ret == -1) return -1;
1584  ctx->hidden.androidio.position = ret - ctx->hidden.androidio.offset;
1585  } else {
1586  Sint64 newPosition;
1587  Sint64 movement;
1588 
1589  switch (whence) {
1590  case RW_SEEK_SET:
1591  newPosition = offset;
1592  break;
1593  case RW_SEEK_CUR:
1594  newPosition = ctx->hidden.androidio.position + offset;
1595  break;
1596  case RW_SEEK_END:
1597  newPosition = ctx->hidden.androidio.size + offset;
1598  break;
1599  default:
1600  return SDL_SetError("Unknown value for 'whence'");
1601  }
1602 
1603  /* Validate the new position */
1604  if (newPosition < 0) {
1605  return SDL_Error(SDL_EFSEEK);
1606  }
1607  if (newPosition > ctx->hidden.androidio.size) {
1608  newPosition = ctx->hidden.androidio.size;
1609  }
1610 
1611  movement = newPosition - ctx->hidden.androidio.position;
1612  if (movement > 0) {
1613  unsigned char buffer[4096];
1614 
1615  /* The easy case where we're seeking forwards */
1616  while (movement > 0) {
1617  Sint64 amount = sizeof (buffer);
1618  size_t result;
1619  if (amount > movement) {
1620  amount = movement;
1621  }
1622  result = Android_JNI_FileRead(ctx, buffer, 1, amount);
1623  if (result <= 0) {
1624  /* Failed to read/skip the required amount, so fail */
1625  return -1;
1626  }
1627 
1628  movement -= result;
1629  }
1630 
1631  } else if (movement < 0) {
1632  /* We can't seek backwards so we have to reopen the file and seek */
1633  /* forwards which obviously isn't very efficient */
1634  Internal_Android_JNI_FileClose(ctx, SDL_FALSE);
1635  Internal_Android_JNI_FileOpen(ctx);
1636  Android_JNI_FileSeek(ctx, newPosition, RW_SEEK_SET);
1637  }
1638  }
1639 
1640  return ctx->hidden.androidio.position;
1641 
1642 }
1643 
1645 {
1646  return Internal_Android_JNI_FileClose(ctx, SDL_TRUE);
1647 }
1648 
1649 int Android_JNI_SetClipboardText(const char* text)
1650 {
1651  JNIEnv* env = Android_JNI_GetEnv();
1652  jstring string = (*env)->NewStringUTF(env, text);
1653  (*env)->CallStaticVoidMethod(env, mActivityClass, midClipboardSetText, string);
1654  (*env)->DeleteLocalRef(env, string);
1655  return 0;
1656 }
1657 
1658 char* Android_JNI_GetClipboardText(void)
1659 {
1660  JNIEnv* env = Android_JNI_GetEnv();
1661  char* text = NULL;
1662  jstring string;
1663 
1664  string = (*env)->CallStaticObjectMethod(env, mActivityClass, midClipboardGetText);
1665  if (string) {
1666  const char* utf = (*env)->GetStringUTFChars(env, string, 0);
1667  if (utf) {
1668  text = SDL_strdup(utf);
1669  (*env)->ReleaseStringUTFChars(env, string, utf);
1670  }
1671  (*env)->DeleteLocalRef(env, string);
1672  }
1673 
1674  return (text == NULL) ? SDL_strdup("") : text;
1675 }
1676 
1678 {
1679  JNIEnv* env = Android_JNI_GetEnv();
1680  jboolean retval = (*env)->CallStaticBooleanMethod(env, mActivityClass, midClipboardHasText);
1681  return (retval == JNI_TRUE) ? SDL_TRUE : SDL_FALSE;
1682 }
1683 
1684 /* returns 0 on success or -1 on error (others undefined then)
1685  * returns truthy or falsy value in plugged, charged and battery
1686  * returns the value in seconds and percent or -1 if not available
1687  */
1688 int Android_JNI_GetPowerInfo(int* plugged, int* charged, int* battery, int* seconds, int* percent)
1689 {
1690  struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
1691  JNIEnv* env = Android_JNI_GetEnv();
1692  jmethodID mid;
1693  jobject context;
1694  jstring action;
1695  jclass cls;
1696  jobject filter;
1697  jobject intent;
1698  jstring iname;
1699  jmethodID imid;
1700  jstring bname;
1701  jmethodID bmid;
1702  if (!LocalReferenceHolder_Init(&refs, env)) {
1703  LocalReferenceHolder_Cleanup(&refs);
1704  return -1;
1705  }
1706 
1707 
1708  /* context = SDLActivity.getContext(); */
1709  context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
1710 
1711  action = (*env)->NewStringUTF(env, "android.intent.action.BATTERY_CHANGED");
1712 
1713  cls = (*env)->FindClass(env, "android/content/IntentFilter");
1714 
1715  mid = (*env)->GetMethodID(env, cls, "<init>", "(Ljava/lang/String;)V");
1716  filter = (*env)->NewObject(env, cls, mid, action);
1717 
1718  (*env)->DeleteLocalRef(env, action);
1719 
1720  mid = (*env)->GetMethodID(env, mActivityClass, "registerReceiver", "(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;");
1721  intent = (*env)->CallObjectMethod(env, context, mid, NULL, filter);
1722 
1723  (*env)->DeleteLocalRef(env, filter);
1724 
1725  cls = (*env)->GetObjectClass(env, intent);
1726 
1727  imid = (*env)->GetMethodID(env, cls, "getIntExtra", "(Ljava/lang/String;I)I");
1728 
1729  /* Watch out for C89 scoping rules because of the macro */
1730 #define GET_INT_EXTRA(var, key) \
1731  int var; \
1732  iname = (*env)->NewStringUTF(env, key); \
1733  var = (*env)->CallIntMethod(env, intent, imid, iname, -1); \
1734  (*env)->DeleteLocalRef(env, iname);
1735 
1736  bmid = (*env)->GetMethodID(env, cls, "getBooleanExtra", "(Ljava/lang/String;Z)Z");
1737 
1738  /* Watch out for C89 scoping rules because of the macro */
1739 #define GET_BOOL_EXTRA(var, key) \
1740  int var; \
1741  bname = (*env)->NewStringUTF(env, key); \
1742  var = (*env)->CallBooleanMethod(env, intent, bmid, bname, JNI_FALSE); \
1743  (*env)->DeleteLocalRef(env, bname);
1744 
1745  if (plugged) {
1746  /* Watch out for C89 scoping rules because of the macro */
1747  GET_INT_EXTRA(plug, "plugged") /* == BatteryManager.EXTRA_PLUGGED (API 5) */
1748  if (plug == -1) {
1749  LocalReferenceHolder_Cleanup(&refs);
1750  return -1;
1751  }
1752  /* 1 == BatteryManager.BATTERY_PLUGGED_AC */
1753  /* 2 == BatteryManager.BATTERY_PLUGGED_USB */
1754  *plugged = (0 < plug) ? 1 : 0;
1755  }
1756 
1757  if (charged) {
1758  /* Watch out for C89 scoping rules because of the macro */
1759  GET_INT_EXTRA(status, "status") /* == BatteryManager.EXTRA_STATUS (API 5) */
1760  if (status == -1) {
1761  LocalReferenceHolder_Cleanup(&refs);
1762  return -1;
1763  }
1764  /* 5 == BatteryManager.BATTERY_STATUS_FULL */
1765  *charged = (status == 5) ? 1 : 0;
1766  }
1767 
1768  if (battery) {
1769  GET_BOOL_EXTRA(present, "present") /* == BatteryManager.EXTRA_PRESENT (API 5) */
1770  *battery = present ? 1 : 0;
1771  }
1772 
1773  if (seconds) {
1774  *seconds = -1; /* not possible */
1775  }
1776 
1777  if (percent) {
1778  int level;
1779  int scale;
1780 
1781  /* Watch out for C89 scoping rules because of the macro */
1782  {
1783  GET_INT_EXTRA(level_temp, "level") /* == BatteryManager.EXTRA_LEVEL (API 5) */
1784  level = level_temp;
1785  }
1786  /* Watch out for C89 scoping rules because of the macro */
1787  {
1788  GET_INT_EXTRA(scale_temp, "scale") /* == BatteryManager.EXTRA_SCALE (API 5) */
1789  scale = scale_temp;
1790  }
1791 
1792  if ((level == -1) || (scale == -1)) {
1793  LocalReferenceHolder_Cleanup(&refs);
1794  return -1;
1795  }
1796  *percent = level * 100 / scale;
1797  }
1798 
1799  (*env)->DeleteLocalRef(env, intent);
1800 
1801  LocalReferenceHolder_Cleanup(&refs);
1802  return 0;
1803 }
1804 
1805 /* returns number of found touch devices as return value and ids in parameter ids */
1807  JNIEnv *env = Android_JNI_GetEnv();
1808  jint sources = 4098; /* == InputDevice.SOURCE_TOUCHSCREEN */
1809  jintArray array = (jintArray) (*env)->CallStaticObjectMethod(env, mActivityClass, midInputGetInputDeviceIds, sources);
1810  int number = 0;
1811  *ids = NULL;
1812  if (array) {
1813  number = (int) (*env)->GetArrayLength(env, array);
1814  if (0 < number) {
1815  jint* elements = (*env)->GetIntArrayElements(env, array, NULL);
1816  if (elements) {
1817  int i;
1818  *ids = SDL_malloc(number * sizeof (**ids));
1819  for (i = 0; i < number; ++i) { /* not assuming sizeof (jint) == sizeof (int) */
1820  (*ids)[i] = elements[i];
1821  }
1822  (*env)->ReleaseIntArrayElements(env, array, elements, JNI_ABORT);
1823  }
1824  }
1825  (*env)->DeleteLocalRef(env, array);
1826  }
1827  return number;
1828 }
1829 
1830 /* sets the mSeparateMouseAndTouch field */
1832 {
1833  JNIEnv *env = Android_JNI_GetEnv();
1834  (*env)->SetStaticBooleanField(env, mActivityClass, fidSeparateMouseAndTouch, new_value ? JNI_TRUE : JNI_FALSE);
1835 }
1836 
1838 {
1839  JNIEnv *env = Android_JNI_GetEnv();
1840  (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midPollInputDevices);
1841 }
1842 
1844 {
1845  JNIEnv *env = Android_JNI_GetEnv();
1846  (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midPollHapticDevices);
1847 }
1848 
1849 void Android_JNI_HapticRun(int device_id, int length)
1850 {
1851  JNIEnv *env = Android_JNI_GetEnv();
1852  (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midHapticRun, device_id, length);
1853 }
1854 
1855 
1856 /* See SDLActivity.java for constants. */
1857 #define COMMAND_SET_KEEP_SCREEN_ON 5
1858 
1859 /* sends message to be handled on the UI event dispatch thread */
1860 int Android_JNI_SendMessage(int command, int param)
1861 {
1862  JNIEnv *env = Android_JNI_GetEnv();
1863  jboolean success;
1864  success = (*env)->CallStaticBooleanMethod(env, mActivityClass, midSendMessage, command, param);
1865  return success ? 0 : -1;
1866 }
1867 
1869 {
1870  Android_JNI_SendMessage(COMMAND_SET_KEEP_SCREEN_ON, (suspend == SDL_FALSE) ? 0 : 1);
1871 }
1872 
1873 void Android_JNI_ShowTextInput(SDL_Rect *inputRect)
1874 {
1875  JNIEnv *env = Android_JNI_GetEnv();
1876  (*env)->CallStaticBooleanMethod(env, mActivityClass, midShowTextInput,
1877  inputRect->x,
1878  inputRect->y,
1879  inputRect->w,
1880  inputRect->h );
1881 }
1882 
1883 void Android_JNI_HideTextInput(void)
1884 {
1885  /* has to match Activity constant */
1886  const int COMMAND_TEXTEDIT_HIDE = 3;
1887  Android_JNI_SendMessage(COMMAND_TEXTEDIT_HIDE, 0);
1888 }
1889 
1891 {
1892  JNIEnv *mEnv = Android_JNI_GetEnv();
1893  jboolean is_shown = 0;
1894  is_shown = (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, midIsScreenKeyboardShown);
1895  return is_shown;
1896 }
1897 
1898 
1899 int Android_JNI_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
1900 {
1901  JNIEnv *env;
1902  jclass clazz;
1903  jmethodID mid;
1904  jobject context;
1905  jstring title;
1906  jstring message;
1907  jintArray button_flags;
1908  jintArray button_ids;
1909  jobjectArray button_texts;
1910  jintArray colors;
1911  jobject text;
1912  jint temp;
1913  int i;
1914 
1915  env = Android_JNI_GetEnv();
1916 
1917  /* convert parameters */
1918 
1919  clazz = (*env)->FindClass(env, "java/lang/String");
1920 
1921  title = (*env)->NewStringUTF(env, messageboxdata->title);
1922  message = (*env)->NewStringUTF(env, messageboxdata->message);
1923 
1924  button_flags = (*env)->NewIntArray(env, messageboxdata->numbuttons);
1925  button_ids = (*env)->NewIntArray(env, messageboxdata->numbuttons);
1926  button_texts = (*env)->NewObjectArray(env, messageboxdata->numbuttons,
1927  clazz, NULL);
1928  for (i = 0; i < messageboxdata->numbuttons; ++i) {
1929  temp = messageboxdata->buttons[i].flags;
1930  (*env)->SetIntArrayRegion(env, button_flags, i, 1, &temp);
1931  temp = messageboxdata->buttons[i].buttonid;
1932  (*env)->SetIntArrayRegion(env, button_ids, i, 1, &temp);
1933  text = (*env)->NewStringUTF(env, messageboxdata->buttons[i].text);
1934  (*env)->SetObjectArrayElement(env, button_texts, i, text);
1935  (*env)->DeleteLocalRef(env, text);
1936  }
1937 
1938  if (messageboxdata->colorScheme) {
1939  colors = (*env)->NewIntArray(env, SDL_MESSAGEBOX_COLOR_MAX);
1940  for (i = 0; i < SDL_MESSAGEBOX_COLOR_MAX; ++i) {
1941  temp = (0xFF << 24) |
1942  (messageboxdata->colorScheme->colors[i].r << 16) |
1943  (messageboxdata->colorScheme->colors[i].g << 8) |
1944  (messageboxdata->colorScheme->colors[i].b << 0);
1945  (*env)->SetIntArrayRegion(env, colors, i, 1, &temp);
1946  }
1947  } else {
1948  colors = NULL;
1949  }
1950 
1951  (*env)->DeleteLocalRef(env, clazz);
1952 
1953  /* context = SDLActivity.getContext(); */
1954  context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
1955 
1956  clazz = (*env)->GetObjectClass(env, context);
1957 
1958  mid = (*env)->GetMethodID(env, clazz,
1959  "messageboxShowMessageBox", "(ILjava/lang/String;Ljava/lang/String;[I[I[Ljava/lang/String;[I)I");
1960  *buttonid = (*env)->CallIntMethod(env, context, mid,
1961  messageboxdata->flags,
1962  title,
1963  message,
1964  button_flags,
1965  button_ids,
1966  button_texts,
1967  colors);
1968 
1969  (*env)->DeleteLocalRef(env, context);
1970  (*env)->DeleteLocalRef(env, clazz);
1971 
1972  /* delete parameters */
1973 
1974  (*env)->DeleteLocalRef(env, title);
1975  (*env)->DeleteLocalRef(env, message);
1976  (*env)->DeleteLocalRef(env, button_flags);
1977  (*env)->DeleteLocalRef(env, button_ids);
1978  (*env)->DeleteLocalRef(env, button_texts);
1979  (*env)->DeleteLocalRef(env, colors);
1980 
1981  return 0;
1982 }
1983 
1984 /*
1985 //////////////////////////////////////////////////////////////////////////////
1986 //
1987 // Functions exposed to SDL applications in SDL_system.h
1988 //////////////////////////////////////////////////////////////////////////////
1989 */
1990 
1991 void *SDL_AndroidGetJNIEnv(void)
1992 {
1993  return Android_JNI_GetEnv();
1994 }
1995 
1996 void *SDL_AndroidGetActivity(void)
1997 {
1998  /* See SDL_system.h for caveats on using this function. */
1999 
2000  JNIEnv *env = Android_JNI_GetEnv();
2001  if (!env) {
2002  return NULL;
2003  }
2004 
2005  /* return SDLActivity.getContext(); */
2006  return (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
2007 }
2008 
2010 {
2011  JNIEnv *env = Android_JNI_GetEnv();
2012  return (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsAndroidTV);
2013 }
2014 
2015 const char * SDL_AndroidGetInternalStoragePath(void)
2016 {
2017  static char *s_AndroidInternalFilesPath = NULL;
2018 
2019  if (!s_AndroidInternalFilesPath) {
2020  struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
2021  jmethodID mid;
2022  jobject context;
2023  jobject fileObject;
2024  jstring pathString;
2025  const char *path;
2026 
2027  JNIEnv *env = Android_JNI_GetEnv();
2028  if (!LocalReferenceHolder_Init(&refs, env)) {
2029  LocalReferenceHolder_Cleanup(&refs);
2030  return NULL;
2031  }
2032 
2033  /* context = SDLActivity.getContext(); */
2034  context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
2035  if (!context) {
2036  SDL_SetError("Couldn't get Android context!");
2037  LocalReferenceHolder_Cleanup(&refs);
2038  return NULL;
2039  }
2040 
2041  /* fileObj = context.getFilesDir(); */
2042  mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
2043  "getFilesDir", "()Ljava/io/File;");
2044  fileObject = (*env)->CallObjectMethod(env, context, mid);
2045  if (!fileObject) {
2046  SDL_SetError("Couldn't get internal directory");
2047  LocalReferenceHolder_Cleanup(&refs);
2048  return NULL;
2049  }
2050 
2051  /* path = fileObject.getCanonicalPath(); */
2052  mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, fileObject),
2053  "getCanonicalPath", "()Ljava/lang/String;");
2054  pathString = (jstring)(*env)->CallObjectMethod(env, fileObject, mid);
2055  if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
2056  LocalReferenceHolder_Cleanup(&refs);
2057  return NULL;
2058  }
2059 
2060  path = (*env)->GetStringUTFChars(env, pathString, NULL);
2061  s_AndroidInternalFilesPath = SDL_strdup(path);
2062  (*env)->ReleaseStringUTFChars(env, pathString, path);
2063 
2064  LocalReferenceHolder_Cleanup(&refs);
2065  }
2066  return s_AndroidInternalFilesPath;
2067 }
2068 
2070 {
2071  struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
2072  jmethodID mid;
2073  jclass cls;
2074  jstring stateString;
2075  const char *state;
2076  int stateFlags;
2077 
2078  JNIEnv *env = Android_JNI_GetEnv();
2079  if (!LocalReferenceHolder_Init(&refs, env)) {
2080  LocalReferenceHolder_Cleanup(&refs);
2081  return 0;
2082  }
2083 
2084  cls = (*env)->FindClass(env, "android/os/Environment");
2085  mid = (*env)->GetStaticMethodID(env, cls,
2086  "getExternalStorageState", "()Ljava/lang/String;");
2087  stateString = (jstring)(*env)->CallStaticObjectMethod(env, cls, mid);
2088 
2089  state = (*env)->GetStringUTFChars(env, stateString, NULL);
2090 
2091  /* Print an info message so people debugging know the storage state */
2092  __android_log_print(ANDROID_LOG_INFO, "SDL", "external storage state: %s", state);
2093 
2094  if (SDL_strcmp(state, "mounted") == 0) {
2095  stateFlags = SDL_ANDROID_EXTERNAL_STORAGE_READ |
2096  SDL_ANDROID_EXTERNAL_STORAGE_WRITE;
2097  } else if (SDL_strcmp(state, "mounted_ro") == 0) {
2098  stateFlags = SDL_ANDROID_EXTERNAL_STORAGE_READ;
2099  } else {
2100  stateFlags = 0;
2101  }
2102  (*env)->ReleaseStringUTFChars(env, stateString, state);
2103 
2104  LocalReferenceHolder_Cleanup(&refs);
2105  return stateFlags;
2106 }
2107 
2108 const char * SDL_AndroidGetExternalStoragePath(void)
2109 {
2110  static char *s_AndroidExternalFilesPath = NULL;
2111 
2112  if (!s_AndroidExternalFilesPath) {
2113  struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
2114  jmethodID mid;
2115  jobject context;
2116  jobject fileObject;
2117  jstring pathString;
2118  const char *path;
2119 
2120  JNIEnv *env = Android_JNI_GetEnv();
2121  if (!LocalReferenceHolder_Init(&refs, env)) {
2122  LocalReferenceHolder_Cleanup(&refs);
2123  return NULL;
2124  }
2125 
2126  /* context = SDLActivity.getContext(); */
2127  context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
2128 
2129  /* fileObj = context.getExternalFilesDir(); */
2130  mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
2131  "getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;");
2132  fileObject = (*env)->CallObjectMethod(env, context, mid, NULL);
2133  if (!fileObject) {
2134  SDL_SetError("Couldn't get external directory");
2135  LocalReferenceHolder_Cleanup(&refs);
2136  return NULL;
2137  }
2138 
2139  /* path = fileObject.getAbsolutePath(); */
2140  mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, fileObject),
2141  "getAbsolutePath", "()Ljava/lang/String;");
2142  pathString = (jstring)(*env)->CallObjectMethod(env, fileObject, mid);
2143 
2144  path = (*env)->GetStringUTFChars(env, pathString, NULL);
2145  s_AndroidExternalFilesPath = SDL_strdup(path);
2146  (*env)->ReleaseStringUTFChars(env, pathString, path);
2147 
2148  LocalReferenceHolder_Cleanup(&refs);
2149  }
2150  return s_AndroidExternalFilesPath;
2151 }
2152 
2154 {
2155  if (!mActivityClass || !midGetManifestEnvironmentVariables) {
2156  __android_log_print(ANDROID_LOG_WARN, "SDL", "Request to get environment variables before JNI is ready");
2157  return;
2158  }
2159 
2160  if (!bHasEnvironmentVariables) {
2161  JNIEnv *env = Android_JNI_GetEnv();
2162  SDL_bool ret = (*env)->CallStaticBooleanMethod(env, mActivityClass, midGetManifestEnvironmentVariables);
2163  if (ret) {
2164  bHasEnvironmentVariables = SDL_TRUE;
2165  }
2166  }
2167 }
2168 
2169 #endif /* __ANDROID__ */
2170 
2171 /* vi: set ts=4 sw=4 expandtab: */
GLuint * ids
int Android_JNI_CaptureAudioBuffer(void *buffer, int buflen)
void Android_SetScreenResolution(int width, int height, Uint32 format, float rate)
GLenum GLenum GLenum GLenum GLenum scale
const char * message
int Android_JNI_GetDisplayDPI(float *ddpi, float *xdpi, float *ydpi)
int Android_JNI_FileClose(SDL_RWops *ctx)
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)
const char * title
GLsizei GLenum * sources
GLsizei const GLchar *const * string
#define SDL_SetMainReady
GLint GLint GLint GLint GLint x
Definition: SDL_opengl.h:1574
#define EGL_NO_SURFACE
Definition: egl.h:100
GLuint GLsizei const GLchar * message
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)
GLfloat GLfloat GLfloat GLfloat h
#define SDL_AndroidGetExternalStoragePath
struct xkb_state * state
GLfloat GLfloat p
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)
void Android_OnTouch(int touch_device_id_in, int pointer_finger_id_in, int action, float x, float y, float p)
static AndroidKeyInfo unicharToAndroidKeyInfoTable[]
Definition: keyinfotable.h:42
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
void Android_JNI_SetSeparateMouseAndTouch(SDL_bool new_value)
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)
#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
GLint GLint GLsizei width
Definition: SDL_opengl.h:1572
int SDL_SendKeyboardKey(Uint8 state, SDL_Scancode scancode)
Definition: SDL_keyboard.c:679
#define SDL_SemPost
SDL_sem * Android_PauseSem
int Android_JNI_OpenAudioDevice(int iscapture, int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames)
#define SDL_setenv
static SDL_VideoDevice * _this
Definition: SDL_video.c:121
EGLNativeWindowType NativeWindowType
Definition: eglplatform.h:112
GLint GLint GLsizei GLsizei GLsizei GLint GLenum format
Definition: SDL_opengl.h:1572
int Android_JNI_SetupThread(void)
GLenum GLsizei GLsizei GLint * values
SDL_bool retval
#define SDL_Log
void Android_JNI_FlushCapturedAudio(void)
#define SDL_memcpy
#define SDL_StopTextInput
GLenum GLsizei const void * pathString
#define KMOD_SHIFT
Definition: SDL_keycode.h:343
int SDL_SendClipboardUpdate(void)
int SDL_SendKeyboardText(const char *text)
Definition: SDL_keyboard.c:789
#define SDL_free
#define SDL_stack_alloc(type, count)
Definition: SDL_stdinc.h:354
void Android_JNI_CloseAudioDevice(const int iscapture)
#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
GLubyte GLubyte GLubyte GLubyte w
GLsizei const GLfloat * value
void * Android_JNI_GetAudioBuffer(void)
GLint GLint GLint GLint GLint GLint y
Definition: SDL_opengl.h:1574
void Android_JNI_SuspendScreenSaver(SDL_bool suspend)
int x
Definition: SDL_rect.h:66
SDL_Scancode code
Definition: keyinfotable.h:37
void Android_JNI_PollHapticDevices(void)
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)
GLsizeiptr size
#define SDL_IsAndroidTV
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:164
SDL_bool
Definition: SDL_stdinc.h:139
GLuint buffer
ANativeWindow * Android_JNI_GetNativeWindow(void)
#define SDL_SetError
void Android_JNI_GetManifestEnvironmentVariables(void)
union SDL_RWops::@10 hidden
static char text[MAX_TEXT_LENGTH]
Definition: testime.c:47
GLint GLint GLsizei GLsizei height
Definition: SDL_opengl.h:1572
GLenum func
void Android_JNI_HideTextInput(void)
#define SDL_AndroidGetActivity
SDL_sem * Android_ResumeSem
size_t Android_JNI_FileRead(SDL_RWops *ctx, void *buffer, size_t size, size_t maxnum)
C_LINKAGE SDLMAIN_DECLSPEC int SDL_main(int argc, char *argv[])
int h
Definition: SDL_rect.h:67
#define SDL_strdup
#define SDL_FreeRW
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:586
SDL_bool Android_JNI_IsScreenKeyboardShown(void)
GLenum array
void Android_JNI_SetWindowStyle(SDL_bool fullscreen)
#define SDL_malloc
GLsizei const GLchar *const * path
int SDL_SendAppEvent(SDL_EventType eventType)
Definition: SDL_events.c:919
#define SDL_strcmp
void * driverdata
Definition: SDL_sysvideo.h:111
#define RW_SEEK_CUR
Definition: SDL_rwops.h:175
void Android_JNI_PollInputDevices(void)
int64_t Sint64
Definition: SDL_stdinc.h:188
#define SDL_PRESSED
Definition: SDL_events.h:50
#define SDL_stack_free(data)
Definition: SDL_stdinc.h:355
#define SDL_SemValue
#define SDL_AndroidGetJNIEnv
ANativeWindow * native_window
int Android_JNI_GetTouchDeviceIds(int **ids)
GLuint GLsizei GLsizei * length
static int colors[7]
Definition: testgesture.c:39
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:66
JNIEnv * Android_JNI_GetEnv(void)
SDL_Scancode
The SDL keyboard scancode representation.
Definition: SDL_scancode.h:43
void Android_JNI_HapticRun(int device_id, int length)
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.
Definition: SDL_rect.h:64
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
int SDL_SendQuit(void)
Definition: SDL_quit.c:137
uint16_t mod
Definition: keyinfotable.h:38
Sint64 Android_JNI_FileSize(SDL_RWops *ctx)
EGLContext ctx
Definition: eglext.h:208