/
SDL_android.c
2755 lines (2293 loc) · 99.8 KB
1
2
/*
Simple DirectMedia Layer
3
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#include "SDL_stdinc.h"
#include "SDL_assert.h"
#include "SDL_hints.h"
#include "SDL_log.h"
26
#include "SDL_main.h"
27
#include "SDL_timer.h"
28
29
30
31
32
33
#ifdef __ANDROID__
#include "SDL_system.h"
#include "SDL_android.h"
34
35
#include "keyinfotable.h"
36
37
38
39
40
41
42
#include "../../events/SDL_events_c.h"
#include "../../video/android/SDL_androidkeyboard.h"
#include "../../video/android/SDL_androidmouse.h"
#include "../../video/android/SDL_androidtouch.h"
#include "../../video/android/SDL_androidvideo.h"
#include "../../video/android/SDL_androidwindow.h"
#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
48
49
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
50
#include <dlfcn.h>
51
52
53
54
55
#define SDL_JAVA_PREFIX org_libsdl_app
#define CONCAT1(prefix, class, function) CONCAT2(prefix, class, function)
#define CONCAT2(prefix, class, function) Java_ ## prefix ## _ ## class ## _ ## function
#define SDL_JAVA_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, SDLActivity, function)
56
57
#define SDL_JAVA_AUDIO_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, SDLAudioManager, function)
#define SDL_JAVA_CONTROLLER_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, SDLControllerManager, function)
58
59
#define SDL_JAVA_INTERFACE_INPUT_CONNECTION(function) CONCAT1(SDL_JAVA_PREFIX, SDLInputConnection, function)
60
61
62
63
/* Audio encoding definitions */
#define ENCODING_PCM_8BIT 3
#define ENCODING_PCM_16BIT 2
#define ENCODING_PCM_FLOAT 4
64
65
/* Java class SDLActivity */
66
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(
67
JNIEnv *env, jclass cls);
68
69
JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeRunMain)(
70
JNIEnv *env, jclass cls,
71
72
jstring library, jstring function, jobject array);
73
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeDropFile)(
74
JNIEnv *env, jclass jcls,
75
76
jstring filename);
77
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetScreenResolution)(
78
JNIEnv *env, jclass jcls,
79
jint surfaceWidth, jint surfaceHeight,
80
jint deviceWidth, jint deviceHeight, jint format, jfloat rate);
81
82
83
84
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeResize)(
JNIEnv *env, jclass cls);
85
86
87
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceCreated)(
JNIEnv *env, jclass jcls);
88
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(
89
JNIEnv *env, jclass jcls);
90
91
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)(
92
JNIEnv *env, jclass jcls);
93
94
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyDown)(
95
JNIEnv *env, jclass jcls,
96
97
98
jint keycode);
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyUp)(
99
JNIEnv *env, jclass jcls,
100
101
jint keycode);
102
103
104
JNIEXPORT jboolean JNICALL SDL_JAVA_INTERFACE(onNativeSoftReturnKey)(
JNIEnv *env, jclass jcls);
105
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyboardFocusLost)(
106
JNIEnv *env, jclass jcls);
107
108
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeTouch)(
109
JNIEnv *env, jclass jcls,
110
111
112
113
jint touch_device_id_in, jint pointer_finger_id_in,
jint action, jfloat x, jfloat y, jfloat p);
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeMouse)(
114
JNIEnv *env, jclass jcls,
115
jint button, jint action, jfloat x, jfloat y, jboolean relative);
116
117
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeAccel)(
118
JNIEnv *env, jclass jcls,
119
120
jfloat x, jfloat y, jfloat z);
121
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeClipboardChanged)(
122
JNIEnv *env, jclass jcls);
123
124
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeLowMemory)(
125
JNIEnv *env, jclass cls);
126
127
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSendQuit)(
128
JNIEnv *env, jclass cls);
129
130
131
132
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeQuit)(
JNIEnv *env, jclass cls);
133
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePause)(
134
JNIEnv *env, jclass cls);
135
136
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeResume)(
137
JNIEnv *env, jclass cls);
138
139
140
141
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeFocusChanged)(
JNIEnv *env, jclass cls, jboolean hasFocus);
142
JNIEXPORT jstring JNICALL SDL_JAVA_INTERFACE(nativeGetHint)(
143
JNIEnv *env, jclass cls,
144
145
jstring name);
146
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetenv)(
147
JNIEnv *env, jclass cls,
148
149
jstring name, jstring value);
150
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeOrientationChanged)(
151
JNIEnv *env, jclass cls,
152
153
jint orientation);
154
155
156
157
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeAddTouch)(
JNIEnv* env, jclass cls,
jint touchId, jstring name);
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
static JNINativeMethod SDLActivity_tab[] = {
{ "nativeSetupJNI", "()I", SDL_JAVA_INTERFACE(nativeSetupJNI) },
{ "nativeRunMain", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)I", SDL_JAVA_INTERFACE(nativeRunMain) },
{ "onNativeDropFile", "(Ljava/lang/String;)V", SDL_JAVA_INTERFACE(onNativeDropFile) },
{ "nativeSetScreenResolution", "(IIIIIF)V", SDL_JAVA_INTERFACE(nativeSetScreenResolution) },
{ "onNativeResize", "()V", SDL_JAVA_INTERFACE(onNativeResize) },
{ "onNativeSurfaceCreated", "()V", SDL_JAVA_INTERFACE(onNativeSurfaceCreated) },
{ "onNativeSurfaceChanged", "()V", SDL_JAVA_INTERFACE(onNativeSurfaceChanged) },
{ "onNativeSurfaceDestroyed", "()V", SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed) },
{ "onNativeKeyDown", "(I)V", SDL_JAVA_INTERFACE(onNativeKeyDown) },
{ "onNativeKeyUp", "(I)V", SDL_JAVA_INTERFACE(onNativeKeyUp) },
{ "onNativeSoftReturnKey", "()Z", SDL_JAVA_INTERFACE(onNativeSoftReturnKey) },
{ "onNativeKeyboardFocusLost", "()V", SDL_JAVA_INTERFACE(onNativeKeyboardFocusLost) },
{ "onNativeTouch", "(IIIFFF)V", SDL_JAVA_INTERFACE(onNativeTouch) },
{ "onNativeMouse", "(IIFFZ)V", SDL_JAVA_INTERFACE(onNativeMouse) },
{ "onNativeAccel", "(FFF)V", SDL_JAVA_INTERFACE(onNativeAccel) },
{ "onNativeClipboardChanged", "()V", SDL_JAVA_INTERFACE(onNativeClipboardChanged) },
{ "nativeLowMemory", "()V", SDL_JAVA_INTERFACE(nativeLowMemory) },
{ "nativeSendQuit", "()V", SDL_JAVA_INTERFACE(nativeSendQuit) },
{ "nativeQuit", "()V", SDL_JAVA_INTERFACE(nativeQuit) },
{ "nativePause", "()V", SDL_JAVA_INTERFACE(nativePause) },
{ "nativeResume", "()V", SDL_JAVA_INTERFACE(nativeResume) },
{ "nativeFocusChanged", "(Z)V", SDL_JAVA_INTERFACE(nativeFocusChanged) },
{ "nativeGetHint", "(Ljava/lang/String;)Ljava/lang/String;", SDL_JAVA_INTERFACE(nativeGetHint) },
{ "nativeSetenv", "(Ljava/lang/String;Ljava/lang/String;)V", SDL_JAVA_INTERFACE(nativeSetenv) },
{ "onNativeOrientationChanged", "(I)V", SDL_JAVA_INTERFACE(onNativeOrientationChanged) },
{ "nativeAddTouch", "(ILjava/lang/String;)V", SDL_JAVA_INTERFACE(nativeAddTouch) }
};
187
188
/* Java class SDLInputConnection */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeCommitText)(
189
JNIEnv *env, jclass cls,
190
191
jstring text, jint newCursorPosition);
192
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeGenerateScancodeForUnichar)(
193
JNIEnv *env, jclass cls,
194
195
jchar chUnicode);
196
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeSetComposingText)(
197
JNIEnv *env, jclass cls,
198
199
jstring text, jint newCursorPosition);
200
201
202
203
204
205
static JNINativeMethod SDLInputConnection_tab[] = {
{ "nativeCommitText", "(Ljava/lang/String;I)V", SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeCommitText) },
{ "nativeGenerateScancodeForUnichar", "(C)V", SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeGenerateScancodeForUnichar) },
{ "nativeSetComposingText", "(Ljava/lang/String;I)V", SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeSetComposingText) }
};
206
207
208
209
/* Java class SDLAudioManager */
JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(
JNIEnv *env, jclass jcls);
210
211
212
213
static JNINativeMethod SDLAudioManager_tab[] = {
{ "nativeSetupJNI", "()I", SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI) }
};
214
215
216
217
218
/* Java class SDLControllerManager */
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(
JNIEnv *env, jclass jcls);
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)(
219
JNIEnv *env, jclass jcls,
220
221
222
jint device_id, jint keycode);
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)(
223
JNIEnv *env, jclass jcls,
224
225
226
jint device_id, jint keycode);
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)(
227
JNIEnv *env, jclass jcls,
228
229
230
jint device_id, jint axis, jfloat value);
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
231
JNIEnv *env, jclass jcls,
232
233
234
jint device_id, jint hat_id, jint x, jint y);
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
235
JNIEnv *env, jclass jcls,
236
237
jint device_id, jstring device_name, jstring device_desc, jint vendor_id, jint product_id,
jboolean is_accelerometer, jint button_mask, jint naxes, jint nhats, jint nballs);
238
239
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)(
240
JNIEnv *env, jclass jcls,
241
242
243
jint device_id);
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic)(
244
JNIEnv *env, jclass jcls,
245
246
247
jint device_id, jstring device_name);
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)(
248
JNIEnv *env, jclass jcls,
249
250
jint device_id);
251
252
253
254
255
256
257
258
259
260
261
static JNINativeMethod SDLControllerManager_tab[] = {
{ "nativeSetupJNI", "()I", SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI) },
{ "onNativePadDown", "(II)I", SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown) },
{ "onNativePadUp", "(II)I", SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp) },
{ "onNativeJoy", "(IIF)V", SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy) },
{ "onNativeHat", "(IIII)V", SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat) },
{ "nativeAddJoystick", "(ILjava/lang/String;Ljava/lang/String;IIZIIII)I", SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick) },
{ "nativeRemoveJoystick", "(I)I", SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick) },
{ "nativeAddHaptic", "(ILjava/lang/String;)I", SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic) },
{ "nativeRemoveHaptic", "(I)I", SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic) }
};
262
263
264
265
266
/* Uncomment this to log messages entering and exiting methods in this file */
/* #define DEBUG_JNI */
267
static void checkJNIReady(void);
268
269
270
271
272
273
274
275
276
277
278
/*******************************************************************************
This file links the Java side of Android with libsdl
*******************************************************************************/
#include <jni.h>
/*******************************************************************************
Globals
*******************************************************************************/
static pthread_key_t mThreadKey;
279
280
static pthread_once_t key_once = PTHREAD_ONCE_INIT;
static JavaVM *mJavaVM = NULL;
281
282
283
284
285
286
/* Main activity */
static jclass mActivityClass;
/* method signatures */
static jmethodID midGetNativeSurface;
287
static jmethodID midSetSurfaceViewFormat;
288
static jmethodID midSetActivityTitle;
289
static jmethodID midSetWindowStyle;
290
static jmethodID midSetOrientation;
291
292
static jmethodID midMinimizeWindow;
static jmethodID midShouldMinimizeOnFocusLoss;
293
static jmethodID midGetContext;
294
static jmethodID midIsTablet;
295
static jmethodID midIsAndroidTV;
296
static jmethodID midIsChromebook;
297
static jmethodID midIsDeXMode;
298
static jmethodID midManualBackButton;
299
static jmethodID midInitTouch;
300
301
302
static jmethodID midSendMessage;
static jmethodID midShowTextInput;
static jmethodID midIsScreenKeyboardShown;
303
304
305
static jmethodID midClipboardSetText;
static jmethodID midClipboardGetText;
static jmethodID midClipboardHasText;
306
static jmethodID midOpenAPKExpansionInputStream;
307
static jmethodID midGetManifestEnvironmentVariables;
308
static jmethodID midGetDisplayDPI;
309
310
311
static jmethodID midCreateCustomCursor;
static jmethodID midSetCustomCursor;
static jmethodID midSetSystemCursor;
312
313
static jmethodID midSupportsRelativeMouse;
static jmethodID midSetRelativeMouseEnabled;
314
315
316
317
318
319
320
/* audio manager */
static jclass mAudioManagerClass;
/* method signatures */
static jmethodID midAudioOpen;
static jmethodID midAudioWriteByteBuffer;
321
322
static jmethodID midAudioWriteShortBuffer;
static jmethodID midAudioWriteFloatBuffer;
323
324
325
static jmethodID midAudioClose;
static jmethodID midCaptureOpen;
static jmethodID midCaptureReadByteBuffer;
326
327
static jmethodID midCaptureReadShortBuffer;
static jmethodID midCaptureReadFloatBuffer;
328
static jmethodID midCaptureClose;
329
static jmethodID midAudioSetThreadPriority;
330
331
332
333
334
335
336
337
/* controller manager */
static jclass mControllerManagerClass;
/* method signatures */
static jmethodID midPollInputDevices;
static jmethodID midPollHapticDevices;
static jmethodID midHapticRun;
338
static jmethodID midHapticStop;
339
340
/* Accelerometer data storage */
341
static SDL_DisplayOrientation displayOrientation;
342
343
344
static float fLastAccelerometer[3];
static SDL_bool bHasNewData;
345
static SDL_bool bHasEnvironmentVariables = SDL_FALSE;
346
347
348
349
350
/*******************************************************************************
Functions called by JNI
*******************************************************************************/
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
/* From http://developer.android.com/guide/practices/jni.html
* All threads are Linux threads, scheduled by the kernel.
* They're usually started from managed code (using Thread.start), but they can also be created elsewhere and then
* attached to the JavaVM. For example, a thread started with pthread_create can be attached with the
* JNI AttachCurrentThread or AttachCurrentThreadAsDaemon functions. Until a thread is attached, it has no JNIEnv,
* and cannot make JNI calls.
* Attaching a natively-created thread causes a java.lang.Thread object to be constructed and added to the "main"
* ThreadGroup, making it visible to the debugger. Calling AttachCurrentThread on an already-attached thread
* is a no-op.
* Note: You can call this function any number of times for the same thread, there's no harm in it
*/
/* From http://developer.android.com/guide/practices/jni.html
* Threads attached through JNI must call DetachCurrentThread before they exit. If coding this directly is awkward,
* in Android 2.0 (Eclair) and higher you can use pthread_key_create to define a destructor function that will be
* called before the thread exits, and call DetachCurrentThread from there. (Use that key with pthread_setspecific
* to store the JNIEnv in thread-local-storage; that way it'll be passed into your destructor as the argument.)
* Note: The destructor is not called unless the stored value is != NULL
* Note: You can call this function any number of times for the same thread, there's no harm in it
* (except for some lost CPU cycles)
*/
/* Set local storage value */
static int
Android_JNI_SetEnv(JNIEnv *env) {
int status = pthread_setspecific(mThreadKey, env);
if (status < 0) {
__android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed pthread_setspecific() in Android_JNI_SetEnv() (err=%d)", status);
}
return status;
}
/* Get local storage value */
JNIEnv* Android_JNI_GetEnv(void)
{
/* Get JNIEnv from the Thread local storage */
JNIEnv *env = pthread_getspecific(mThreadKey);
if (env == NULL) {
389
/* If it fails, try to attach ! (e.g the thread isn't created with SDL_CreateThread() */
390
391
392
393
394
int status;
/* There should be a JVM */
if (mJavaVM == NULL) {
__android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed, there is no JavaVM");
395
return NULL;
396
397
398
399
400
401
402
403
404
405
406
407
408
409
}
/* Attach the current thread to the JVM and get a JNIEnv.
* It will be detached by pthread_create destructor 'Android_JNI_ThreadDestroyed' */
status = (*mJavaVM)->AttachCurrentThread(mJavaVM, &env, NULL);
if (status < 0) {
__android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed to attach current thread (err=%d)", status);
return NULL;
}
/* Save JNIEnv into the Thread local storage */
if (Android_JNI_SetEnv(env) < 0) {
return NULL;
}
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
}
return env;
}
/* Set up an external thread for using JNI with Android_JNI_GetEnv() */
int Android_JNI_SetupThread(void)
{
JNIEnv *env;
int status;
/* There should be a JVM */
if (mJavaVM == NULL) {
__android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed, there is no JavaVM");
return 0;
}
/* Attach the current thread to the JVM and get a JNIEnv.
* It will be detached by pthread_create destructor 'Android_JNI_ThreadDestroyed' */
status = (*mJavaVM)->AttachCurrentThread(mJavaVM, &env, NULL);
if (status < 0) {
__android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed to attach current thread (err=%d)", status);
return 0;
}
/* Save JNIEnv into the Thread local storage */
if (Android_JNI_SetEnv(env) < 0) {
return 0;
}
return 1;
}
/* Destructor called for each thread where mThreadKey is not NULL */
static void
Android_JNI_ThreadDestroyed(void *value)
{
/* The thread is being destroyed, detach it from the Java VM and set the mThreadKey value to NULL as required */
JNIEnv *env = (JNIEnv *) value;
if (env != NULL) {
(*mJavaVM)->DetachCurrentThread(mJavaVM);
Android_JNI_SetEnv(NULL);
}
}
/* Creation of local storage mThreadKey */
456
static void
457
Android_JNI_CreateKey(void)
458
459
460
461
462
463
464
{
int status = pthread_key_create(&mThreadKey, Android_JNI_ThreadDestroyed);
if (status < 0) {
__android_log_print(ANDROID_LOG_ERROR, "SDL", "Error initializing mThreadKey with pthread_key_create() (err=%d)", status);
}
}
465
static void
466
Android_JNI_CreateKey_once(void)
467
468
469
470
471
472
473
{
int status = pthread_once(&key_once, Android_JNI_CreateKey);
if (status < 0) {
__android_log_print(ANDROID_LOG_ERROR, "SDL", "Error initializing mThreadKey with pthread_once() (err=%d)", status);
}
}
474
475
476
477
478
479
480
481
482
483
static void
register_methods(JNIEnv *env, const char *classname, JNINativeMethod *methods, int nb)
{
jclass clazz = (*env)->FindClass(env, classname);
if (clazz == NULL || (*env)->RegisterNatives(env, clazz, methods, nb) < 0) {
__android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed to register methods of %s", classname);
return;
}
}
484
/* Library init */
485
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
486
487
{
mJavaVM = vm;
488
489
490
491
492
493
494
JNIEnv *env = NULL;
if ((*mJavaVM)->GetEnv(mJavaVM, (void **)&env, JNI_VERSION_1_4) != JNI_OK) {
__android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed to get JNI Env");
return JNI_VERSION_1_4;
}
495
496
497
498
register_methods(env, "org/libsdl/app/SDLActivity", SDLActivity_tab, SDL_arraysize(SDLActivity_tab));
register_methods(env, "org/libsdl/app/SDLInputConnection", SDLInputConnection_tab, SDL_arraysize(SDLInputConnection_tab));
register_methods(env, "org/libsdl/app/SDLAudioManager", SDLAudioManager_tab, SDL_arraysize(SDLAudioManager_tab));
register_methods(env, "org/libsdl/app/SDLControllerManager", SDLControllerManager_tab, SDL_arraysize(SDLControllerManager_tab));
499
500
501
502
return JNI_VERSION_1_4;
}
503
void checkJNIReady(void)
504
505
{
if (!mActivityClass || !mAudioManagerClass || !mControllerManagerClass) {
506
/* We aren't fully initialized, let's just return. */
507
508
509
return;
}
510
SDL_SetMainReady();
511
512
513
}
/* Activity initialization -- called before SDL_main() to initialize JNI bindings */
514
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cls)
515
{
516
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeSetupJNI()");
517
518
519
520
521
522
523
524
525
526
527
528
529
530
/*
* Create mThreadKey so we can keep track of the JNIEnv assigned to each thread
* Refer to http://developer.android.com/guide/practices/design/jni.html for the rationale behind this
*/
Android_JNI_CreateKey_once();
/* Save JNIEnv of SDLActivity */
Android_JNI_SetEnv(env);
if (mJavaVM == NULL) {
__android_log_print(ANDROID_LOG_ERROR, "SDL", "failed to found a JavaVM");
}
531
/* Use a mutex to prevent concurrency issues between Java Activity and Native thread code, when using 'Android_Window'.
532
533
* (Eg. Java sending Touch events, while native code is destroying the main SDL_Window. )
*/
534
535
if (Android_ActivityMutex == NULL) {
Android_ActivityMutex = SDL_CreateMutex(); /* Could this be created twice if onCreate() is called a second time ? */
536
537
}
538
if (Android_ActivityMutex == NULL) {
539
__android_log_print(ANDROID_LOG_ERROR, "SDL", "failed to create Android_ActivityMutex mutex");
540
541
}
542
543
544
545
546
547
548
549
550
551
552
Android_PauseSem = SDL_CreateSemaphore(0);
if (Android_PauseSem == NULL) {
__android_log_print(ANDROID_LOG_ERROR, "SDL", "failed to create Android_PauseSem semaphore");
}
Android_ResumeSem = SDL_CreateSemaphore(0);
if (Android_ResumeSem == NULL) {
__android_log_print(ANDROID_LOG_ERROR, "SDL", "failed to create Android_ResumeSem semaphore");
}
553
mActivityClass = (jclass)((*env)->NewGlobalRef(env, cls));
554
555
midGetNativeSurface = (*env)->GetStaticMethodID(env, mActivityClass,
556
"getNativeSurface","()Landroid/view/Surface;");
557
midSetSurfaceViewFormat = (*env)->GetStaticMethodID(env, mActivityClass,
558
"setSurfaceViewFormat","(I)V");
559
midSetActivityTitle = (*env)->GetStaticMethodID(env, mActivityClass,
560
"setActivityTitle","(Ljava/lang/String;)Z");
561
midSetWindowStyle = (*env)->GetStaticMethodID(env, mActivityClass,
562
"setWindowStyle","(Z)V");
563
midSetOrientation = (*env)->GetStaticMethodID(env, mActivityClass,
564
"setOrientation","(IIZLjava/lang/String;)V");
565
566
567
568
midMinimizeWindow = (*env)->GetStaticMethodID(env, mActivityClass,
"minimizeWindow","()V");
midShouldMinimizeOnFocusLoss = (*env)->GetStaticMethodID(env, mActivityClass,
"shouldMinimizeOnFocusLoss","()Z");
569
midGetContext = (*env)->GetStaticMethodID(env, mActivityClass,
570
"getContext","()Landroid/content/Context;");
571
midIsTablet = (*env)->GetStaticMethodID(env, mActivityClass,
572
"isTablet", "()Z");
573
midIsAndroidTV = (*env)->GetStaticMethodID(env, mActivityClass,
574
"isAndroidTV","()Z");
575
midIsChromebook = (*env)->GetStaticMethodID(env, mActivityClass,
576
"isChromebook", "()Z");
577
midIsDeXMode = (*env)->GetStaticMethodID(env, mActivityClass,
578
"isDeXMode", "()Z");
579
midManualBackButton = (*env)->GetStaticMethodID(env, mActivityClass,
580
"manualBackButton", "()V");
581
midInitTouch = (*env)->GetStaticMethodID(env, mActivityClass,
582
"initTouch", "()V");
583
midSendMessage = (*env)->GetStaticMethodID(env, mActivityClass,
584
"sendMessage", "(II)Z");
585
midShowTextInput = (*env)->GetStaticMethodID(env, mActivityClass,
586
"showTextInput", "(IIII)Z");
587
midIsScreenKeyboardShown = (*env)->GetStaticMethodID(env, mActivityClass,
588
"isScreenKeyboardShown","()Z");
589
midClipboardSetText = (*env)->GetStaticMethodID(env, mActivityClass,
590
"clipboardSetText", "(Ljava/lang/String;)V");
591
midClipboardGetText = (*env)->GetStaticMethodID(env, mActivityClass,
592
"clipboardGetText", "()Ljava/lang/String;");
593
midClipboardHasText = (*env)->GetStaticMethodID(env, mActivityClass,
594
"clipboardHasText", "()Z");
595
midOpenAPKExpansionInputStream = (*env)->GetStaticMethodID(env, mActivityClass,
596
"openAPKExpansionInputStream", "(Ljava/lang/String;)Ljava/io/InputStream;");
597
598
midGetManifestEnvironmentVariables = (*env)->GetStaticMethodID(env, mActivityClass,
599
"getManifestEnvironmentVariables", "()Z");
600
601
602
603
604
midGetDisplayDPI = (*env)->GetStaticMethodID(env, mActivityClass, "getDisplayDPI", "()Landroid/util/DisplayMetrics;");
midCreateCustomCursor = (*env)->GetStaticMethodID(env, mActivityClass, "createCustomCursor", "([IIIII)I");
midSetCustomCursor = (*env)->GetStaticMethodID(env, mActivityClass, "setCustomCursor", "(I)Z");
midSetSystemCursor = (*env)->GetStaticMethodID(env, mActivityClass, "setSystemCursor", "(I)Z");
605
606
607
midSupportsRelativeMouse = (*env)->GetStaticMethodID(env, mActivityClass, "supportsRelativeMouse", "()Z");
midSetRelativeMouseEnabled = (*env)->GetStaticMethodID(env, mActivityClass, "setRelativeMouseEnabled", "(Z)Z");
608
609
610
if (!midGetNativeSurface || !midSetSurfaceViewFormat ||
611
!midSetActivityTitle || !midSetWindowStyle || !midSetOrientation || !midMinimizeWindow || !midShouldMinimizeOnFocusLoss || !midGetContext || !midIsTablet || !midIsAndroidTV || !midInitTouch ||
612
!midSendMessage || !midShowTextInput || !midIsScreenKeyboardShown ||
613
!midClipboardSetText || !midClipboardGetText || !midClipboardHasText ||
614
!midOpenAPKExpansionInputStream || !midGetManifestEnvironmentVariables || !midGetDisplayDPI ||
615
!midCreateCustomCursor || !midSetCustomCursor || !midSetSystemCursor || !midSupportsRelativeMouse || !midSetRelativeMouseEnabled ||
616
!midIsChromebook || !midIsDeXMode || !midManualBackButton) {
617
__android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLActivity.java?");
618
}
619
620
621
622
623
checkJNIReady();
}
/* Audio initialization -- called before SDL_main() to initialize JNI bindings */
624
JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cls)
625
626
627
{
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "AUDIO nativeSetupJNI()");
628
mAudioManagerClass = (jclass)((*env)->NewGlobalRef(env, cls));
629
630
midAudioOpen = (*env)->GetStaticMethodID(env, mAudioManagerClass,
631
"audioOpen", "(IIII)[I");
632
midAudioWriteByteBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
633
"audioWriteByteBuffer", "([B)V");
634
midAudioWriteShortBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
635
"audioWriteShortBuffer", "([S)V");
636
midAudioWriteFloatBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
637
"audioWriteFloatBuffer", "([F)V");
638
midAudioClose = (*env)->GetStaticMethodID(env, mAudioManagerClass,
639
"audioClose", "()V");
640
midCaptureOpen = (*env)->GetStaticMethodID(env, mAudioManagerClass,
641
"captureOpen", "(IIII)[I");
642
midCaptureReadByteBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
643
"captureReadByteBuffer", "([BZ)I");
644
midCaptureReadShortBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
645
"captureReadShortBuffer", "([SZ)I");
646
midCaptureReadFloatBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
647
"captureReadFloatBuffer", "([FZ)I");
648
midCaptureClose = (*env)->GetStaticMethodID(env, mAudioManagerClass,
649
"captureClose", "()V");
650
midAudioSetThreadPriority = (*env)->GetStaticMethodID(env, mAudioManagerClass,
651
"audioSetThreadPriority", "(ZI)V");
652
653
if (!midAudioOpen || !midAudioWriteByteBuffer || !midAudioWriteShortBuffer || !midAudioWriteFloatBuffer || !midAudioClose ||
654
!midCaptureOpen || !midCaptureReadByteBuffer || !midCaptureReadShortBuffer || !midCaptureReadFloatBuffer || !midCaptureClose || !midAudioSetThreadPriority) {
655
656
657
658
659
660
661
__android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLAudioManager.java?");
}
checkJNIReady();
}
/* Controller initialization -- called before SDL_main() to initialize JNI bindings */
662
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cls)
663
664
665
{
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "CONTROLLER nativeSetupJNI()");
666
mControllerManagerClass = (jclass)((*env)->NewGlobalRef(env, cls));
667
668
midPollInputDevices = (*env)->GetStaticMethodID(env, mControllerManagerClass,
669
"pollInputDevices", "()V");
670
midPollHapticDevices = (*env)->GetStaticMethodID(env, mControllerManagerClass,
671
"pollHapticDevices", "()V");
672
midHapticRun = (*env)->GetStaticMethodID(env, mControllerManagerClass,
673
"hapticRun", "(IFI)V");
674
midHapticStop = (*env)->GetStaticMethodID(env, mControllerManagerClass,
675
"hapticStop", "(I)V");
676
677
if (!midPollInputDevices || !midPollHapticDevices || !midHapticRun || !midHapticStop) {
678
679
680
681
__android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLControllerManager.java?");
}
checkJNIReady();
682
683
684
685
686
687
}
/* SDL main function prototype */
typedef int (*SDL_main_func)(int argc, char *argv[]);
/* Start up the SDL app */
688
JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeRunMain)(JNIEnv *env, jclass cls, jstring library, jstring function, jobject array)
689
690
691
692
693
{
int status = -1;
const char *library_file;
void *library_handle;
694
695
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeRunMain()");
696
697
698
/* Save JNIEnv of SDLThread */
Android_JNI_SetEnv(env);
699
700
library_file = (*env)->GetStringUTFChars(env, library, NULL);
library_handle = dlopen(library_file, RTLD_GLOBAL);
701
702
703
704
705
706
707
708
709
710
711
if (!library_handle) {
/* When deploying android app bundle format uncompressed native libs may not extract from apk to filesystem.
In this case we should use lib name without path. https://bugzilla.libsdl.org/show_bug.cgi?id=4739 */
const char *library_name = SDL_strrchr(library_file, '/');
if (library_name && *library_name) {
library_name += 1;
library_handle = dlopen(library_name, RTLD_GLOBAL);
}
}
712
713
714
715
716
717
718
719
720
721
722
if (library_handle) {
const char *function_name;
SDL_main_func SDL_main;
function_name = (*env)->GetStringUTFChars(env, function, NULL);
SDL_main = (SDL_main_func)dlsym(library_handle, function_name);
if (SDL_main) {
int i;
int argc;
int len;
char **argv;
723
SDL_bool isstack;
724
725
726
/* Prepare the arguments. */
len = (*env)->GetArrayLength(env, array);
727
argv = SDL_small_alloc(char *, 1 + len + 1, &isstack); /* !!! FIXME: check for NULL */
728
729
730
731
732
733
argc = 0;
/* Use the name "app_process" so PHYSFS_platformCalcBaseDir() works.
https://bitbucket.org/MartinFelis/love-android-sdl2/issue/23/release-build-crash-on-start
*/
argv[argc++] = SDL_strdup("app_process");
for (i = 0; i < len; ++i) {
734
735
const char *utf;
char *arg = NULL;
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
jstring string = (*env)->GetObjectArrayElement(env, array, i);
if (string) {
utf = (*env)->GetStringUTFChars(env, string, 0);
if (utf) {
arg = SDL_strdup(utf);
(*env)->ReleaseStringUTFChars(env, string, utf);
}
(*env)->DeleteLocalRef(env, string);
}
if (!arg) {
arg = SDL_strdup("");
}
argv[argc++] = arg;
}
argv[argc] = NULL;
/* Run the application. */
status = SDL_main(argc, argv);
/* Release the arguments. */
for (i = 0; i < argc; ++i) {
SDL_free(argv[i]);
}
760
SDL_small_free(argv, isstack);
761
762
763
764
765
766
767
768
769
770
} else {
__android_log_print(ANDROID_LOG_ERROR, "SDL", "nativeRunMain(): Couldn't find function %s in library %s", function_name, library_file);
}
(*env)->ReleaseStringUTFChars(env, function, function_name);
dlclose(library_handle);
} else {
__android_log_print(ANDROID_LOG_ERROR, "SDL", "nativeRunMain(): Couldn't load library %s", library_file);
771
}
772
773
(*env)->ReleaseStringUTFChars(env, library, library_file);
774
/* This is a Java thread, it doesn't need to be Detached from the JVM.
775
776
777
* Set to mThreadKey value to NULL not to call pthread_create destructor 'Android_JNI_ThreadDestroyed' */
Android_JNI_SetEnv(NULL);
778
779
/* Do not issue an exit or the whole application will terminate instead of just the SDL thread */
/* exit(status); */
780
781
return status;
782
783
784
}
/* Drop file */
785
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeDropFile)(
786
JNIEnv *env, jclass jcls,
787
788
789
jstring filename)
{
const char *path = (*env)->GetStringUTFChars(env, filename, NULL);
790
SDL_SendDropFile(NULL, path);
791
(*env)->ReleaseStringUTFChars(env, filename, path);
792
SDL_SendDropComplete(NULL);
793
794
}
795
796
797
798
799
800
801
802
803
/* Lock / Unlock Mutex */
void Android_ActivityMutex_Lock() {
SDL_LockMutex(Android_ActivityMutex);
}
void Android_ActivityMutex_Unlock() {
SDL_UnlockMutex(Android_ActivityMutex);
}
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
/* Lock the Mutex when the Activity is in its 'Running' state */
void Android_ActivityMutex_Lock_Running() {
int pauseSignaled = 0;
int resumeSignaled = 0;
retry:
SDL_LockMutex(Android_ActivityMutex);
pauseSignaled = SDL_SemValue(Android_PauseSem);
resumeSignaled = SDL_SemValue(Android_ResumeSem);
if (pauseSignaled > resumeSignaled) {
SDL_UnlockMutex(Android_ActivityMutex);
SDL_Delay(50);
goto retry;
}
}
823
824
/* Set screen resolution */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetScreenResolution)(
825
JNIEnv *env, jclass jcls,
826
jint surfaceWidth, jint surfaceHeight,
827
jint deviceWidth, jint deviceHeight, jint format, jfloat rate)
828
{
829
SDL_LockMutex(Android_ActivityMutex);
830
831
832
833
834
835
836
837
838
839
840
841
842
843
Android_SetScreenResolution(surfaceWidth, surfaceHeight, deviceWidth, deviceHeight, format, rate);
SDL_UnlockMutex(Android_ActivityMutex);
}
/* Resize */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeResize)(
JNIEnv *env, jclass jcls)
{
SDL_LockMutex(Android_ActivityMutex);
if (Android_Window)
{
844
Android_SendResize(Android_Window);
845
}
846
847
SDL_UnlockMutex(Android_ActivityMutex);
848
849
}
850
851
852
853
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeOrientationChanged)(
JNIEnv *env, jclass jcls,
jint orientation)
{
854
855
SDL_LockMutex(Android_ActivityMutex);
856
displayOrientation = (SDL_DisplayOrientation)orientation;
857
858
859
860
861
862
if (Android_Window)
{
SDL_VideoDisplay *display = SDL_GetDisplay(0);
SDL_SendDisplayEvent(display, SDL_DISPLAYEVENT_ORIENTATION, orientation);
}
863
864
SDL_UnlockMutex(Android_ActivityMutex);
865
866
}
867
868
869
870
871
872
873
874
875
876
877
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeAddTouch)(
JNIEnv* env, jclass cls,
jint touchId, jstring name)
{
const char *utfname = (*env)->GetStringUTFChars(env, name, NULL);
SDL_AddTouch((SDL_TouchID) touchId, SDL_TOUCH_DEVICE_DIRECT, utfname);
(*env)->ReleaseStringUTFChars(env, name, utfname);
}
878
/* Paddown */
879
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)(
880
JNIEnv *env, jclass jcls,
881
882
883
884
885
886
jint device_id, jint keycode)
{
return Android_OnPadDown(device_id, keycode);
}
/* Padup */
887
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)(
888
JNIEnv *env, jclass jcls,
889
jint device_id, jint keycode)
890
891
892
893
894
{
return Android_OnPadUp(device_id, keycode);
}
/* Joy */
895
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)(
896
JNIEnv *env, jclass jcls,
897
898
899
900
901
902
jint device_id, jint axis, jfloat value)
{
Android_OnJoy(device_id, axis, value);
}
/* POV Hat */
903
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
904
JNIEnv *env, jclass jcls,
905
906
907
908
909
910
jint device_id, jint hat_id, jint x, jint y)
{
Android_OnHat(device_id, hat_id, x, y);
}
911
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
912
JNIEnv *env, jclass jcls,
913
914
915
jint device_id, jstring device_name, jstring device_desc,
jint vendor_id, jint product_id, jboolean is_accelerometer,
jint button_mask, jint naxes, jint nhats, jint nballs)
916
917
918
{
int retval;
const char *name = (*env)->GetStringUTFChars(env, device_name, NULL);
919
const char *desc = (*env)->GetStringUTFChars(env, device_desc, NULL);
920
921
retval = Android_AddJoystick(device_id, name, desc, vendor_id, product_id, is_accelerometer ? SDL_TRUE : SDL_FALSE, button_mask, naxes, nhats, nballs);
922
923
(*env)->ReleaseStringUTFChars(env, device_name, name);
924
(*env)->ReleaseStringUTFChars(env, device_desc, desc);
925
926
927
928
return retval;
}
929
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)(
930
JNIEnv *env, jclass jcls,
931
jint device_id)
932
933
934
935
{
return Android_RemoveJoystick(device_id);
}
936
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic)(
937
JNIEnv *env, jclass jcls, jint device_id, jstring device_name)
938
939
940
941
942
943
944
945
946
947
948
{
int retval;
const char *name = (*env)->GetStringUTFChars(env, device_name, NULL);
retval = Android_AddHaptic(device_id, name);
(*env)->ReleaseStringUTFChars(env, device_name, name);
return retval;
}
949
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)(
950
JNIEnv *env, jclass jcls, jint device_id)
951
952
953
954
{
return Android_RemoveHaptic(device_id);
}
955
956
957
958
959
/* Called from surfaceCreated() */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceCreated)(JNIEnv *env, jclass jcls)
{
SDL_LockMutex(Android_ActivityMutex);
960
if (Android_Window)
961
962
963
964
965
966
967
968
969
970
971
{
SDL_WindowData *data = (SDL_WindowData *) Android_Window->driverdata;
data->native_window = Android_JNI_GetNativeWindow();
if (data->native_window == NULL) {
SDL_SetError("Could not fetch native window from UI thread");
}
}
SDL_UnlockMutex(Android_ActivityMutex);
}
972
973
/* Called from surfaceChanged() */
974
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(JNIEnv *env, jclass jcls)
975
{
976
SDL_LockMutex(Android_ActivityMutex);
977
978
if (Android_Window)
979
980
981
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
SDL_WindowData *data = (SDL_WindowData *) Android_Window->driverdata;
982
983
984
985
/* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
if (data->egl_surface == EGL_NO_SURFACE) {
data->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType) data->native_window);
986
}
987
988
989
/* GL Context handling is done in the event loop because this function is run from the Java thread */
}
990
991
SDL_UnlockMutex(Android_ActivityMutex);
992
993
}
994
/* Called from surfaceDestroyed() */
995
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)(JNIEnv *env, jclass jcls)
996
{
997
SDL_LockMutex(Android_ActivityMutex);
998
999
if (Android_Window)
1000
{