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