/
SDL_android.c
2550 lines (2120 loc) · 91.2 KB
1
2
/*
Simple DirectMedia Layer
3
Copyright (C) 1997-2019 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
28
29
30
31
32
#ifdef __ANDROID__
#include "SDL_system.h"
#include "SDL_android.h"
33
34
#include "keyinfotable.h"
35
36
37
38
39
40
41
#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"
42
#include "../../haptic/android/SDL_syshaptic_c.h"
43
44
45
46
47
#include <android/log.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
48
#include <dlfcn.h>
49
50
51
52
53
#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)
54
55
#define SDL_JAVA_AUDIO_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, SDLAudioManager, function)
#define SDL_JAVA_CONTROLLER_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, SDLControllerManager, function)
56
57
#define SDL_JAVA_INTERFACE_INPUT_CONNECTION(function) CONCAT1(SDL_JAVA_PREFIX, SDLInputConnection, function)
58
59
60
61
/* Audio encoding definitions */
#define ENCODING_PCM_8BIT 3
#define ENCODING_PCM_16BIT 2
#define ENCODING_PCM_FLOAT 4
62
63
/* Java class SDLActivity */
64
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(
65
JNIEnv *env, jclass cls);
66
67
JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeRunMain)(
68
JNIEnv *env, jclass cls,
69
70
jstring library, jstring function, jobject array);
71
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeDropFile)(
72
JNIEnv *env, jclass jcls,
73
74
75
jstring filename);
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeResize)(
76
JNIEnv *env, jclass jcls,
77
jint surfaceWidth, jint surfaceHeight,
78
jint deviceWidth, jint deviceHeight, jint format, jfloat rate);
79
80
81
82
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceCreated)(
JNIEnv *env, jclass jcls);
83
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(
84
JNIEnv *env, jclass jcls);
85
86
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)(
87
JNIEnv *env, jclass jcls);
88
89
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyDown)(
90
JNIEnv *env, jclass jcls,
91
92
93
jint keycode);
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyUp)(
94
JNIEnv *env, jclass jcls,
95
96
97
jint keycode);
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyboardFocusLost)(
98
JNIEnv *env, jclass jcls);
99
100
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeTouch)(
101
JNIEnv *env, jclass jcls,
102
103
104
105
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)(
106
JNIEnv *env, jclass jcls,
107
jint button, jint action, jfloat x, jfloat y, jboolean relative);
108
109
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeAccel)(
110
JNIEnv *env, jclass jcls,
111
112
jfloat x, jfloat y, jfloat z);
113
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeClipboardChanged)(
114
JNIEnv *env, jclass jcls);
115
116
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeLowMemory)(
117
JNIEnv *env, jclass cls);
118
119
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSendQuit)(
120
JNIEnv *env, jclass cls);
121
122
123
124
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeQuit)(
JNIEnv *env, jclass cls);
125
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePause)(
126
JNIEnv *env, jclass cls);
127
128
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeResume)(
129
JNIEnv *env, jclass cls);
130
131
JNIEXPORT jstring JNICALL SDL_JAVA_INTERFACE(nativeGetHint)(
132
JNIEnv *env, jclass cls,
133
134
jstring name);
135
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetenv)(
136
JNIEnv *env, jclass cls,
137
138
139
jstring name, jstring value);
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeEnvironmentVariablesSet)(
140
JNIEnv *env, jclass cls);
141
142
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeOrientationChanged)(
143
JNIEnv *env, jclass cls,
144
145
jint orientation);
146
147
148
149
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeAddTouch)(
JNIEnv* env, jclass cls,
jint touchId, jstring name);
150
151
/* Java class SDLInputConnection */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeCommitText)(
152
JNIEnv *env, jclass cls,
153
154
jstring text, jint newCursorPosition);
155
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeGenerateScancodeForUnichar)(
156
JNIEnv *env, jclass cls,
157
158
jchar chUnicode);
159
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeSetComposingText)(
160
JNIEnv *env, jclass cls,
161
162
jstring text, jint newCursorPosition);
163
164
165
166
167
168
169
170
171
/* Java class SDLAudioManager */
JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(
JNIEnv *env, jclass jcls);
/* Java class SDLControllerManager */
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(
JNIEnv *env, jclass jcls);
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)(
172
JNIEnv *env, jclass jcls,
173
174
175
jint device_id, jint keycode);
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)(
176
JNIEnv *env, jclass jcls,
177
178
179
jint device_id, jint keycode);
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)(
180
JNIEnv *env, jclass jcls,
181
182
183
jint device_id, jint axis, jfloat value);
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
184
JNIEnv *env, jclass jcls,
185
186
187
jint device_id, jint hat_id, jint x, jint y);
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
188
JNIEnv *env, jclass jcls,
189
190
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);
191
192
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)(
193
JNIEnv *env, jclass jcls,
194
195
196
jint device_id);
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic)(
197
JNIEnv *env, jclass jcls,
198
199
200
jint device_id, jstring device_name);
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)(
201
JNIEnv *env, jclass jcls,
202
203
204
jint device_id);
205
206
207
208
/* Uncomment this to log messages entering and exiting methods in this file */
/* #define DEBUG_JNI */
209
static void checkJNIReady(void);
210
211
212
213
214
215
216
217
218
219
220
/*******************************************************************************
This file links the Java side of Android with libsdl
*******************************************************************************/
#include <jni.h>
/*******************************************************************************
Globals
*******************************************************************************/
static pthread_key_t mThreadKey;
221
222
static pthread_once_t key_once = PTHREAD_ONCE_INIT;
static JavaVM *mJavaVM = NULL;
223
224
225
226
227
228
/* Main activity */
static jclass mActivityClass;
/* method signatures */
static jmethodID midGetNativeSurface;
229
static jmethodID midSetSurfaceViewFormat;
230
static jmethodID midSetActivityTitle;
231
static jmethodID midSetWindowStyle;
232
233
static jmethodID midSetOrientation;
static jmethodID midGetContext;
234
static jmethodID midIsTablet;
235
static jmethodID midIsAndroidTV;
236
static jmethodID midIsChromebook;
237
static jmethodID midIsDeXMode;
238
static jmethodID midManualBackButton;
239
static jmethodID midInitTouch;
240
241
242
static jmethodID midSendMessage;
static jmethodID midShowTextInput;
static jmethodID midIsScreenKeyboardShown;
243
244
245
static jmethodID midClipboardSetText;
static jmethodID midClipboardGetText;
static jmethodID midClipboardHasText;
246
static jmethodID midOpenAPKExpansionInputStream;
247
static jmethodID midGetManifestEnvironmentVariables;
248
static jmethodID midGetDisplayDPI;
249
250
251
static jmethodID midCreateCustomCursor;
static jmethodID midSetCustomCursor;
static jmethodID midSetSystemCursor;
252
253
static jmethodID midSupportsRelativeMouse;
static jmethodID midSetRelativeMouseEnabled;
254
255
256
257
258
259
260
/* audio manager */
static jclass mAudioManagerClass;
/* method signatures */
static jmethodID midAudioOpen;
static jmethodID midAudioWriteByteBuffer;
261
262
static jmethodID midAudioWriteShortBuffer;
static jmethodID midAudioWriteFloatBuffer;
263
264
265
static jmethodID midAudioClose;
static jmethodID midCaptureOpen;
static jmethodID midCaptureReadByteBuffer;
266
267
static jmethodID midCaptureReadShortBuffer;
static jmethodID midCaptureReadFloatBuffer;
268
static jmethodID midCaptureClose;
269
static jmethodID midAudioSetThreadPriority;
270
271
272
273
274
275
276
277
/* controller manager */
static jclass mControllerManagerClass;
/* method signatures */
static jmethodID midPollInputDevices;
static jmethodID midPollHapticDevices;
static jmethodID midHapticRun;
278
static jmethodID midHapticStop;
279
280
281
282
/* static fields */
static jfieldID fidSeparateMouseAndTouch;
283
284
285
286
/* Accelerometer data storage */
static float fLastAccelerometer[3];
static SDL_bool bHasNewData;
287
static SDL_bool bHasEnvironmentVariables = SDL_FALSE;
288
289
290
291
292
/*******************************************************************************
Functions called by JNI
*******************************************************************************/
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
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
/* 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) {
__android_log_print(ANDROID_LOG_ERROR, "SDL", "JNIEnv is NULL. Call Android_JNI_SetupThread() first.");
}
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 */
378
379
380
381
382
383
384
385
386
static void
Android_JNI_CreateKey()
{
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);
}
}
387
static void
388
389
390
391
392
393
394
395
Android_JNI_CreateKey_once()
{
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);
}
}
396
/* Library init */
397
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
398
399
400
401
402
{
mJavaVM = vm;
return JNI_VERSION_1_4;
}
403
404
405
void checkJNIReady()
{
if (!mActivityClass || !mAudioManagerClass || !mControllerManagerClass) {
406
/* We aren't fully initialized, let's just return. */
407
408
409
return;
}
410
SDL_SetMainReady();
411
412
413
}
/* Activity initialization -- called before SDL_main() to initialize JNI bindings */
414
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cls)
415
{
416
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeSetupJNI()");
417
418
419
420
421
422
423
424
425
426
427
428
429
430
/*
* 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");
}
431
/* Use a mutex to prevent concurrency issues between Java Activity and Native thread code, when using 'Android_Window'.
432
433
* (Eg. Java sending Touch events, while native code is destroying the main SDL_Window. )
*/
434
435
if (Android_ActivityMutex == NULL) {
Android_ActivityMutex = SDL_CreateMutex(); /* Could this be created twice if onCreate() is called a second time ? */
436
437
}
438
if (Android_ActivityMutex == NULL) {
439
__android_log_print(ANDROID_LOG_ERROR, "SDL", "failed to create Android_ActivityMutex mutex");
440
441
}
442
mActivityClass = (jclass)((*env)->NewGlobalRef(env, cls));
443
444
midGetNativeSurface = (*env)->GetStaticMethodID(env, mActivityClass,
445
"getNativeSurface","()Landroid/view/Surface;");
446
midSetSurfaceViewFormat = (*env)->GetStaticMethodID(env, mActivityClass,
447
"setSurfaceViewFormat","(I)V");
448
midSetActivityTitle = (*env)->GetStaticMethodID(env, mActivityClass,
449
"setActivityTitle","(Ljava/lang/String;)Z");
450
midSetWindowStyle = (*env)->GetStaticMethodID(env, mActivityClass,
451
"setWindowStyle","(Z)V");
452
midSetOrientation = (*env)->GetStaticMethodID(env, mActivityClass,
453
"setOrientation","(IIZLjava/lang/String;)V");
454
midGetContext = (*env)->GetStaticMethodID(env, mActivityClass,
455
"getContext","()Landroid/content/Context;");
456
midIsTablet = (*env)->GetStaticMethodID(env, mActivityClass,
457
"isTablet", "()Z");
458
midIsAndroidTV = (*env)->GetStaticMethodID(env, mActivityClass,
459
"isAndroidTV","()Z");
460
midIsChromebook = (*env)->GetStaticMethodID(env, mActivityClass,
461
"isChromebook", "()Z");
462
midIsDeXMode = (*env)->GetStaticMethodID(env, mActivityClass,
463
"isDeXMode", "()Z");
464
midManualBackButton = (*env)->GetStaticMethodID(env, mActivityClass,
465
"manualBackButton", "()V");
466
midInitTouch = (*env)->GetStaticMethodID(env, mActivityClass,
467
"initTouch", "()V");
468
midSendMessage = (*env)->GetStaticMethodID(env, mActivityClass,
469
"sendMessage", "(II)Z");
470
midShowTextInput = (*env)->GetStaticMethodID(env, mActivityClass,
471
"showTextInput", "(IIII)Z");
472
midIsScreenKeyboardShown = (*env)->GetStaticMethodID(env, mActivityClass,
473
"isScreenKeyboardShown","()Z");
474
midClipboardSetText = (*env)->GetStaticMethodID(env, mActivityClass,
475
"clipboardSetText", "(Ljava/lang/String;)V");
476
midClipboardGetText = (*env)->GetStaticMethodID(env, mActivityClass,
477
"clipboardGetText", "()Ljava/lang/String;");
478
midClipboardHasText = (*env)->GetStaticMethodID(env, mActivityClass,
479
"clipboardHasText", "()Z");
480
midOpenAPKExpansionInputStream = (*env)->GetStaticMethodID(env, mActivityClass,
481
"openAPKExpansionInputStream", "(Ljava/lang/String;)Ljava/io/InputStream;");
482
483
midGetManifestEnvironmentVariables = (*env)->GetStaticMethodID(env, mActivityClass,
484
"getManifestEnvironmentVariables", "()Z");
485
486
487
488
489
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");
490
491
492
midSupportsRelativeMouse = (*env)->GetStaticMethodID(env, mActivityClass, "supportsRelativeMouse", "()Z");
midSetRelativeMouseEnabled = (*env)->GetStaticMethodID(env, mActivityClass, "setRelativeMouseEnabled", "(Z)Z");
493
494
495
if (!midGetNativeSurface || !midSetSurfaceViewFormat ||
496
!midSetActivityTitle || !midSetWindowStyle || !midSetOrientation || !midGetContext || !midIsTablet || !midIsAndroidTV || !midInitTouch ||
497
!midSendMessage || !midShowTextInput || !midIsScreenKeyboardShown ||
498
!midClipboardSetText || !midClipboardGetText || !midClipboardHasText ||
499
!midOpenAPKExpansionInputStream || !midGetManifestEnvironmentVariables || !midGetDisplayDPI ||
500
!midCreateCustomCursor || !midSetCustomCursor || !midSetSystemCursor || !midSupportsRelativeMouse || !midSetRelativeMouseEnabled ||
501
!midIsChromebook || !midIsDeXMode || !midManualBackButton) {
502
__android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLActivity.java?");
503
}
504
505
fidSeparateMouseAndTouch = (*env)->GetStaticFieldID(env, mActivityClass, "mSeparateMouseAndTouch", "Z");
506
507
if (!fidSeparateMouseAndTouch) {
508
509
510
__android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java static fields, do you have the latest version of SDLActivity.java?");
}
511
512
513
514
checkJNIReady();
}
/* Audio initialization -- called before SDL_main() to initialize JNI bindings */
515
JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cls)
516
517
518
{
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "AUDIO nativeSetupJNI()");
519
mAudioManagerClass = (jclass)((*env)->NewGlobalRef(env, cls));
520
521
midAudioOpen = (*env)->GetStaticMethodID(env, mAudioManagerClass,
522
"audioOpen", "(IIII)[I");
523
midAudioWriteByteBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
524
"audioWriteByteBuffer", "([B)V");
525
midAudioWriteShortBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
526
"audioWriteShortBuffer", "([S)V");
527
midAudioWriteFloatBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
528
"audioWriteFloatBuffer", "([F)V");
529
midAudioClose = (*env)->GetStaticMethodID(env, mAudioManagerClass,
530
"audioClose", "()V");
531
midCaptureOpen = (*env)->GetStaticMethodID(env, mAudioManagerClass,
532
"captureOpen", "(IIII)[I");
533
midCaptureReadByteBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
534
"captureReadByteBuffer", "([BZ)I");
535
midCaptureReadShortBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
536
"captureReadShortBuffer", "([SZ)I");
537
midCaptureReadFloatBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
538
"captureReadFloatBuffer", "([FZ)I");
539
midCaptureClose = (*env)->GetStaticMethodID(env, mAudioManagerClass,
540
"captureClose", "()V");
541
midAudioSetThreadPriority = (*env)->GetStaticMethodID(env, mAudioManagerClass,
542
"audioSetThreadPriority", "(ZI)V");
543
544
if (!midAudioOpen || !midAudioWriteByteBuffer || !midAudioWriteShortBuffer || !midAudioWriteFloatBuffer || !midAudioClose ||
545
!midCaptureOpen || !midCaptureReadByteBuffer || !midCaptureReadShortBuffer || !midCaptureReadFloatBuffer || !midCaptureClose || !midAudioSetThreadPriority) {
546
547
548
549
550
551
552
__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 */
553
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cls)
554
555
556
{
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "CONTROLLER nativeSetupJNI()");
557
mControllerManagerClass = (jclass)((*env)->NewGlobalRef(env, cls));
558
559
midPollInputDevices = (*env)->GetStaticMethodID(env, mControllerManagerClass,
560
"pollInputDevices", "()V");
561
midPollHapticDevices = (*env)->GetStaticMethodID(env, mControllerManagerClass,
562
"pollHapticDevices", "()V");
563
midHapticRun = (*env)->GetStaticMethodID(env, mControllerManagerClass,
564
"hapticRun", "(IFI)V");
565
midHapticStop = (*env)->GetStaticMethodID(env, mControllerManagerClass,
566
"hapticStop", "(I)V");
567
568
if (!midPollInputDevices || !midPollHapticDevices || !midHapticRun || !midHapticStop) {
569
570
571
572
__android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLControllerManager.java?");
}
checkJNIReady();
573
574
575
576
577
578
}
/* SDL main function prototype */
typedef int (*SDL_main_func)(int argc, char *argv[]);
/* Start up the SDL app */
579
JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeRunMain)(JNIEnv *env, jclass cls, jstring library, jstring function, jobject array)
580
581
582
583
584
{
int status = -1;
const char *library_file;
void *library_handle;
585
586
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeRunMain()");
587
588
589
/* Save JNIEnv of SDLThread */
Android_JNI_SetEnv(env);
590
591
592
593
594
595
596
597
598
599
600
601
602
library_file = (*env)->GetStringUTFChars(env, library, NULL);
library_handle = dlopen(library_file, RTLD_GLOBAL);
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;
603
SDL_bool isstack;
604
605
606
/* Prepare the arguments. */
len = (*env)->GetArrayLength(env, array);
607
argv = SDL_small_alloc(char *, 1 + len + 1, &isstack); /* !!! FIXME: check for NULL */
608
609
610
611
612
613
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) {
614
615
const char *utf;
char *arg = NULL;
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
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]);
}
640
SDL_small_free(argv, isstack);
641
642
643
644
645
646
647
648
649
650
} 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);
651
}
652
653
(*env)->ReleaseStringUTFChars(env, library, library_file);
654
/* This is a Java thread, it doesn't need to be Detached from the JVM.
655
656
657
* Set to mThreadKey value to NULL not to call pthread_create destructor 'Android_JNI_ThreadDestroyed' */
Android_JNI_SetEnv(NULL);
658
659
/* Do not issue an exit or the whole application will terminate instead of just the SDL thread */
/* exit(status); */
660
661
return status;
662
663
664
}
/* Drop file */
665
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeDropFile)(
666
JNIEnv *env, jclass jcls,
667
668
669
jstring filename)
{
const char *path = (*env)->GetStringUTFChars(env, filename, NULL);
670
SDL_SendDropFile(NULL, path);
671
(*env)->ReleaseStringUTFChars(env, filename, path);
672
SDL_SendDropComplete(NULL);
673
674
675
}
/* Resize */
676
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeResize)(
677
JNIEnv *env, jclass jcls,
678
jint surfaceWidth, jint surfaceHeight,
679
jint deviceWidth, jint deviceHeight, jint format, jfloat rate)
680
{
681
SDL_LockMutex(Android_ActivityMutex);
682
683
Android_SetScreenResolution(Android_Window, surfaceWidth, surfaceHeight, deviceWidth, deviceHeight, format, rate);
684
685
SDL_UnlockMutex(Android_ActivityMutex);
686
687
}
688
689
690
691
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeOrientationChanged)(
JNIEnv *env, jclass jcls,
jint orientation)
{
692
693
694
695
696
SDL_VideoDevice *_this = SDL_GetVideoDevice();
if (_this) {
SDL_VideoDisplay *display = SDL_GetDisplay(0);
SDL_SendDisplayEvent(display, SDL_DISPLAYEVENT_ORIENTATION, orientation);
}
697
698
}
699
700
701
702
703
704
705
706
707
708
709
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);
}
710
/* Paddown */
711
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)(
712
JNIEnv *env, jclass jcls,
713
714
715
716
717
718
jint device_id, jint keycode)
{
return Android_OnPadDown(device_id, keycode);
}
/* Padup */
719
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)(
720
JNIEnv *env, jclass jcls,
721
jint device_id, jint keycode)
722
723
724
725
726
{
return Android_OnPadUp(device_id, keycode);
}
/* Joy */
727
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)(
728
JNIEnv *env, jclass jcls,
729
730
731
732
733
734
jint device_id, jint axis, jfloat value)
{
Android_OnJoy(device_id, axis, value);
}
/* POV Hat */
735
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
736
JNIEnv *env, jclass jcls,
737
738
739
740
741
742
jint device_id, jint hat_id, jint x, jint y)
{
Android_OnHat(device_id, hat_id, x, y);
}
743
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
744
JNIEnv *env, jclass jcls,
745
746
747
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)
748
749
750
{
int retval;
const char *name = (*env)->GetStringUTFChars(env, device_name, NULL);
751
const char *desc = (*env)->GetStringUTFChars(env, device_desc, NULL);
752
753
retval = Android_AddJoystick(device_id, name, desc, vendor_id, product_id, is_accelerometer ? SDL_TRUE : SDL_FALSE, button_mask, naxes, nhats, nballs);
754
755
(*env)->ReleaseStringUTFChars(env, device_name, name);
756
(*env)->ReleaseStringUTFChars(env, device_desc, desc);
757
758
759
760
return retval;
}
761
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)(
762
JNIEnv *env, jclass jcls,
763
jint device_id)
764
765
766
767
{
return Android_RemoveJoystick(device_id);
}
768
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic)(
769
JNIEnv *env, jclass jcls, jint device_id, jstring device_name)
770
771
772
773
774
775
776
777
778
779
780
{
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;
}
781
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)(
782
JNIEnv *env, jclass jcls, jint device_id)
783
784
785
786
{
return Android_RemoveHaptic(device_id);
}
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
/* Called from surfaceCreated() */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceCreated)(JNIEnv *env, jclass jcls)
{
SDL_LockMutex(Android_ActivityMutex);
if (Android_Window && Android_Window->driverdata)
{
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);
}
804
805
/* Called from surfaceChanged() */
806
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(JNIEnv *env, jclass jcls)
807
{
808
SDL_LockMutex(Android_ActivityMutex);
809
810
811
812
813
if (Android_Window && Android_Window->driverdata)
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
SDL_WindowData *data = (SDL_WindowData *) Android_Window->driverdata;
814
815
816
817
/* 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);
818
}
819
820
821
/* GL Context handling is done in the event loop because this function is run from the Java thread */
}
822
823
SDL_UnlockMutex(Android_ActivityMutex);
824
825
}
826
/* Called from surfaceDestroyed() */
827
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)(JNIEnv *env, jclass jcls)
828
{
829
SDL_LockMutex(Android_ActivityMutex);
830
831
832
833
834
if (Android_Window && Android_Window->driverdata)
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
SDL_WindowData *data = (SDL_WindowData *) Android_Window->driverdata;
835
836
837
838
839
/* We have to clear the current context and destroy the egl surface here
* Otherwise there's BAD_NATIVE_WINDOW errors coming from eglCreateWindowSurface on resume
* Ref: http://stackoverflow.com/questions/8762589/eglcreatewindowsurface-on-ics-and-switching-from-2d-to-3d
*/
840
841
842
843
844
845
if (data->egl_surface != EGL_NO_SURFACE) {
SDL_EGL_MakeCurrent(_this, NULL, NULL);
SDL_EGL_DestroySurface(_this, data->egl_surface);
data->egl_surface = EGL_NO_SURFACE;
}
846
847
848
849
850
851
if (data->native_window) {
ANativeWindow_release(data->native_window);
}
data->native_window = NULL;
852
/* GL Context handling is done in the event loop because this function is run from the Java thread */
853
}
854
855
SDL_UnlockMutex(Android_ActivityMutex);
856
857
858
}
/* Keydown */
859
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyDown)(
860
JNIEnv *env, jclass jcls,
861
jint keycode)
862
863
864
865
866
{
Android_OnKeyDown(keycode);
}
/* Keyup */
867
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyUp)(
868
JNIEnv *env, jclass jcls,
869
jint keycode)
870
871
872
873
874
{
Android_OnKeyUp(keycode);
}
/* Keyboard Focus Lost */
875
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyboardFocusLost)(
876
JNIEnv *env, jclass jcls)
877
878
879
880
881
882
883
{
/* Calling SDL_StopTextInput will take care of hiding the keyboard and cleaning up the DummyText widget */
SDL_StopTextInput();
}
/* Touch */
884
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeTouch)(
885
JNIEnv *env, jclass jcls,
886
887
888
jint touch_device_id_in, jint pointer_finger_id_in,
jint action, jfloat x, jfloat y, jfloat p)
{
889
SDL_LockMutex(Android_ActivityMutex);
890
891
Android_OnTouch(Android_Window, touch_device_id_in, pointer_finger_id_in, action, x, y, p);
892
893
SDL_UnlockMutex(Android_ActivityMutex);
894
895
896
}
/* Mouse */
897
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeMouse)(
898
JNIEnv *env, jclass jcls,
899
jint button, jint action, jfloat x, jfloat y, jboolean relative)
900
{
901
SDL_LockMutex(Android_ActivityMutex);
902
903
Android_OnMouse(Android_Window, button, action, x, y, relative);
904
905
SDL_UnlockMutex(Android_ActivityMutex);
906
907
908
}
/* Accelerometer */
909
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeAccel)(
910
JNIEnv *env, jclass jcls,
911
912
913
914
915
916
917
918
jfloat x, jfloat y, jfloat z)
{
fLastAccelerometer[0] = x;
fLastAccelerometer[1] = y;
fLastAccelerometer[2] = z;
bHasNewData = SDL_TRUE;
}
919
920
/* Clipboard */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeClipboardChanged)(
921
JNIEnv *env, jclass jcls)
922
923
924
925
{
SDL_SendClipboardUpdate();
}
926
/* Low memory */
927
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeLowMemory)(
928
JNIEnv *env, jclass cls)
929
930
931
932
{
SDL_SendAppEvent(SDL_APP_LOWMEMORY);
}
933
934
/* Send Quit event to "SDLThread" thread */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSendQuit)(
935
JNIEnv *env, jclass cls)
936
937
{
/* Discard previous events. The user should have handled state storage
938
* in SDL_APP_WILLENTERBACKGROUND. After nativeSendQuit() is called, no
939
940
941
942
943
* events other than SDL_QUIT and SDL_APP_TERMINATING should fire */
SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT);
/* Inject a SDL_QUIT event */
SDL_SendQuit();
SDL_SendAppEvent(SDL_APP_TERMINATING);
944
945
946
947
/* Robustness: clear any pending Pause */
while (SDL_SemTryWait(Android_PauseSem) == 0) {
/* empty */
}
948
949
/* Resume the event loop so that the app can catch SDL_QUIT which
* should now be the top event in the event queue. */
950
SDL_SemPost(Android_ResumeSem);
951
952
}
953
954
955
956
957
958
959
960
961
962
963
964
965
/* Activity ends */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeQuit)(
JNIEnv *env, jclass cls)
{
const char *str;
if (Android_ActivityMutex) {
SDL_DestroyMutex(Android_ActivityMutex);
Android_ActivityMutex = NULL;
}
str = SDL_GetError();
if (str && str[0]) {
966
__android_log_print(ANDROID_LOG_ERROR, "SDL", "SDLActivity thread ends (error=%s)", str);
967
968
969
970
971
} else {
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDLActivity thread ends");
}
}
972
/* Pause */
973
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePause)(
974
JNIEnv *env, jclass cls)
975
{
976
SDL_LockMutex(Android_ActivityMutex);
977
978
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativePause()");
979
980
981
982
983
984
if (Android_Window) {
SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_FOCUS_LOST, 0, 0);
SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
SDL_SendAppEvent(SDL_APP_WILLENTERBACKGROUND);
SDL_SendAppEvent(SDL_APP_DIDENTERBACKGROUND);
985
986
/* *After* sending the relevant events, signal the pause semaphore
987
988
989
990
* so the event loop knows to pause and (optionally) block itself.
* Sometimes 2 pauses can be queued (eg pause/resume/pause), so it's
* always increased. */
SDL_SemPost(Android_PauseSem);
991
}
992
993
SDL_UnlockMutex(Android_ActivityMutex);
994
995
996
}
/* Resume */
997
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeResume)(
998
JNIEnv *env, jclass cls)
999
{
1000
SDL_LockMutex(Android_ActivityMutex);