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