/
SDL_android.c
2572 lines (2136 loc) · 91.8 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
443
444
445
446
447
448
449
450
451
452
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");
}
453
mActivityClass = (jclass)((*env)->NewGlobalRef(env, cls));
454
455
midGetNativeSurface = (*env)->GetStaticMethodID(env, mActivityClass,
456
"getNativeSurface","()Landroid/view/Surface;");
457
midSetSurfaceViewFormat = (*env)->GetStaticMethodID(env, mActivityClass,
458
"setSurfaceViewFormat","(I)V");
459
midSetActivityTitle = (*env)->GetStaticMethodID(env, mActivityClass,
460
"setActivityTitle","(Ljava/lang/String;)Z");
461
midSetWindowStyle = (*env)->GetStaticMethodID(env, mActivityClass,
462
"setWindowStyle","(Z)V");
463
midSetOrientation = (*env)->GetStaticMethodID(env, mActivityClass,
464
"setOrientation","(IIZLjava/lang/String;)V");
465
midGetContext = (*env)->GetStaticMethodID(env, mActivityClass,
466
"getContext","()Landroid/content/Context;");
467
midIsTablet = (*env)->GetStaticMethodID(env, mActivityClass,
468
"isTablet", "()Z");
469
midIsAndroidTV = (*env)->GetStaticMethodID(env, mActivityClass,
470
"isAndroidTV","()Z");
471
midIsChromebook = (*env)->GetStaticMethodID(env, mActivityClass,
472
"isChromebook", "()Z");
473
midIsDeXMode = (*env)->GetStaticMethodID(env, mActivityClass,
474
"isDeXMode", "()Z");
475
midManualBackButton = (*env)->GetStaticMethodID(env, mActivityClass,
476
"manualBackButton", "()V");
477
midInitTouch = (*env)->GetStaticMethodID(env, mActivityClass,
478
"initTouch", "()V");
479
midSendMessage = (*env)->GetStaticMethodID(env, mActivityClass,
480
"sendMessage", "(II)Z");
481
midShowTextInput = (*env)->GetStaticMethodID(env, mActivityClass,
482
"showTextInput", "(IIII)Z");
483
midIsScreenKeyboardShown = (*env)->GetStaticMethodID(env, mActivityClass,
484
"isScreenKeyboardShown","()Z");
485
midClipboardSetText = (*env)->GetStaticMethodID(env, mActivityClass,
486
"clipboardSetText", "(Ljava/lang/String;)V");
487
midClipboardGetText = (*env)->GetStaticMethodID(env, mActivityClass,
488
"clipboardGetText", "()Ljava/lang/String;");
489
midClipboardHasText = (*env)->GetStaticMethodID(env, mActivityClass,
490
"clipboardHasText", "()Z");
491
midOpenAPKExpansionInputStream = (*env)->GetStaticMethodID(env, mActivityClass,
492
"openAPKExpansionInputStream", "(Ljava/lang/String;)Ljava/io/InputStream;");
493
494
midGetManifestEnvironmentVariables = (*env)->GetStaticMethodID(env, mActivityClass,
495
"getManifestEnvironmentVariables", "()Z");
496
497
498
499
500
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");
501
502
503
midSupportsRelativeMouse = (*env)->GetStaticMethodID(env, mActivityClass, "supportsRelativeMouse", "()Z");
midSetRelativeMouseEnabled = (*env)->GetStaticMethodID(env, mActivityClass, "setRelativeMouseEnabled", "(Z)Z");
504
505
506
if (!midGetNativeSurface || !midSetSurfaceViewFormat ||
507
!midSetActivityTitle || !midSetWindowStyle || !midSetOrientation || !midGetContext || !midIsTablet || !midIsAndroidTV || !midInitTouch ||
508
!midSendMessage || !midShowTextInput || !midIsScreenKeyboardShown ||
509
!midClipboardSetText || !midClipboardGetText || !midClipboardHasText ||
510
!midOpenAPKExpansionInputStream || !midGetManifestEnvironmentVariables || !midGetDisplayDPI ||
511
!midCreateCustomCursor || !midSetCustomCursor || !midSetSystemCursor || !midSupportsRelativeMouse || !midSetRelativeMouseEnabled ||
512
!midIsChromebook || !midIsDeXMode || !midManualBackButton) {
513
__android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLActivity.java?");
514
}
515
516
fidSeparateMouseAndTouch = (*env)->GetStaticFieldID(env, mActivityClass, "mSeparateMouseAndTouch", "Z");
517
518
if (!fidSeparateMouseAndTouch) {
519
520
521
__android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java static fields, do you have the latest version of SDLActivity.java?");
}
522
523
524
525
checkJNIReady();
}
/* Audio initialization -- called before SDL_main() to initialize JNI bindings */
526
JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cls)
527
528
529
{
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "AUDIO nativeSetupJNI()");
530
mAudioManagerClass = (jclass)((*env)->NewGlobalRef(env, cls));
531
532
midAudioOpen = (*env)->GetStaticMethodID(env, mAudioManagerClass,
533
"audioOpen", "(IIII)[I");
534
midAudioWriteByteBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
535
"audioWriteByteBuffer", "([B)V");
536
midAudioWriteShortBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
537
"audioWriteShortBuffer", "([S)V");
538
midAudioWriteFloatBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
539
"audioWriteFloatBuffer", "([F)V");
540
midAudioClose = (*env)->GetStaticMethodID(env, mAudioManagerClass,
541
"audioClose", "()V");
542
midCaptureOpen = (*env)->GetStaticMethodID(env, mAudioManagerClass,
543
"captureOpen", "(IIII)[I");
544
midCaptureReadByteBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
545
"captureReadByteBuffer", "([BZ)I");
546
midCaptureReadShortBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
547
"captureReadShortBuffer", "([SZ)I");
548
midCaptureReadFloatBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
549
"captureReadFloatBuffer", "([FZ)I");
550
midCaptureClose = (*env)->GetStaticMethodID(env, mAudioManagerClass,
551
"captureClose", "()V");
552
midAudioSetThreadPriority = (*env)->GetStaticMethodID(env, mAudioManagerClass,
553
"audioSetThreadPriority", "(ZI)V");
554
555
if (!midAudioOpen || !midAudioWriteByteBuffer || !midAudioWriteShortBuffer || !midAudioWriteFloatBuffer || !midAudioClose ||
556
!midCaptureOpen || !midCaptureReadByteBuffer || !midCaptureReadShortBuffer || !midCaptureReadFloatBuffer || !midCaptureClose || !midAudioSetThreadPriority) {
557
558
559
560
561
562
563
__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 */
564
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cls)
565
566
567
{
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "CONTROLLER nativeSetupJNI()");
568
mControllerManagerClass = (jclass)((*env)->NewGlobalRef(env, cls));
569
570
midPollInputDevices = (*env)->GetStaticMethodID(env, mControllerManagerClass,
571
"pollInputDevices", "()V");
572
midPollHapticDevices = (*env)->GetStaticMethodID(env, mControllerManagerClass,
573
"pollHapticDevices", "()V");
574
midHapticRun = (*env)->GetStaticMethodID(env, mControllerManagerClass,
575
"hapticRun", "(IFI)V");
576
midHapticStop = (*env)->GetStaticMethodID(env, mControllerManagerClass,
577
"hapticStop", "(I)V");
578
579
if (!midPollInputDevices || !midPollHapticDevices || !midHapticRun || !midHapticStop) {
580
581
582
583
__android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLControllerManager.java?");
}
checkJNIReady();
584
585
586
587
588
589
}
/* SDL main function prototype */
typedef int (*SDL_main_func)(int argc, char *argv[]);
/* Start up the SDL app */
590
JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeRunMain)(JNIEnv *env, jclass cls, jstring library, jstring function, jobject array)
591
592
593
594
595
{
int status = -1;
const char *library_file;
void *library_handle;
596
597
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeRunMain()");
598
599
600
/* Save JNIEnv of SDLThread */
Android_JNI_SetEnv(env);
601
602
603
604
605
606
607
608
609
610
611
612
613
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;
614
SDL_bool isstack;
615
616
617
/* Prepare the arguments. */
len = (*env)->GetArrayLength(env, array);
618
argv = SDL_small_alloc(char *, 1 + len + 1, &isstack); /* !!! FIXME: check for NULL */
619
620
621
622
623
624
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) {
625
626
const char *utf;
char *arg = NULL;
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
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]);
}
651
SDL_small_free(argv, isstack);
652
653
654
655
656
657
658
659
660
661
} 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);
662
}
663
664
(*env)->ReleaseStringUTFChars(env, library, library_file);
665
/* This is a Java thread, it doesn't need to be Detached from the JVM.
666
667
668
* Set to mThreadKey value to NULL not to call pthread_create destructor 'Android_JNI_ThreadDestroyed' */
Android_JNI_SetEnv(NULL);
669
670
/* Do not issue an exit or the whole application will terminate instead of just the SDL thread */
/* exit(status); */
671
672
return status;
673
674
675
}
/* Drop file */
676
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeDropFile)(
677
JNIEnv *env, jclass jcls,
678
679
680
jstring filename)
{
const char *path = (*env)->GetStringUTFChars(env, filename, NULL);
681
SDL_SendDropFile(NULL, path);
682
(*env)->ReleaseStringUTFChars(env, filename, path);
683
SDL_SendDropComplete(NULL);
684
685
686
}
/* Resize */
687
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeResize)(
688
JNIEnv *env, jclass jcls,
689
jint surfaceWidth, jint surfaceHeight,
690
jint deviceWidth, jint deviceHeight, jint format, jfloat rate)
691
{
692
SDL_LockMutex(Android_ActivityMutex);
693
694
Android_SetScreenResolution(Android_Window, surfaceWidth, surfaceHeight, deviceWidth, deviceHeight, format, rate);
695
696
SDL_UnlockMutex(Android_ActivityMutex);
697
698
}
699
700
701
702
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeOrientationChanged)(
JNIEnv *env, jclass jcls,
jint orientation)
{
703
704
705
706
707
SDL_VideoDevice *_this = SDL_GetVideoDevice();
if (_this) {
SDL_VideoDisplay *display = SDL_GetDisplay(0);
SDL_SendDisplayEvent(display, SDL_DISPLAYEVENT_ORIENTATION, orientation);
}
708
709
}
710
711
712
713
714
715
716
717
718
719
720
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);
}
721
/* Paddown */
722
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)(
723
JNIEnv *env, jclass jcls,
724
725
726
727
728
729
jint device_id, jint keycode)
{
return Android_OnPadDown(device_id, keycode);
}
/* Padup */
730
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)(
731
JNIEnv *env, jclass jcls,
732
jint device_id, jint keycode)
733
734
735
736
737
{
return Android_OnPadUp(device_id, keycode);
}
/* Joy */
738
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)(
739
JNIEnv *env, jclass jcls,
740
741
742
743
744
745
jint device_id, jint axis, jfloat value)
{
Android_OnJoy(device_id, axis, value);
}
/* POV Hat */
746
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
747
JNIEnv *env, jclass jcls,
748
749
750
751
752
753
jint device_id, jint hat_id, jint x, jint y)
{
Android_OnHat(device_id, hat_id, x, y);
}
754
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
755
JNIEnv *env, jclass jcls,
756
757
758
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)
759
760
761
{
int retval;
const char *name = (*env)->GetStringUTFChars(env, device_name, NULL);
762
const char *desc = (*env)->GetStringUTFChars(env, device_desc, NULL);
763
764
retval = Android_AddJoystick(device_id, name, desc, vendor_id, product_id, is_accelerometer ? SDL_TRUE : SDL_FALSE, button_mask, naxes, nhats, nballs);
765
766
(*env)->ReleaseStringUTFChars(env, device_name, name);
767
(*env)->ReleaseStringUTFChars(env, device_desc, desc);
768
769
770
771
return retval;
}
772
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)(
773
JNIEnv *env, jclass jcls,
774
jint device_id)
775
776
777
778
{
return Android_RemoveJoystick(device_id);
}
779
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic)(
780
JNIEnv *env, jclass jcls, jint device_id, jstring device_name)
781
782
783
784
785
786
787
788
789
790
791
{
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;
}
792
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)(
793
JNIEnv *env, jclass jcls, jint device_id)
794
795
796
797
{
return Android_RemoveHaptic(device_id);
}
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
/* 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);
}
815
816
/* Called from surfaceChanged() */
817
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(JNIEnv *env, jclass jcls)
818
{
819
SDL_LockMutex(Android_ActivityMutex);
820
821
822
823
824
if (Android_Window && Android_Window->driverdata)
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
SDL_WindowData *data = (SDL_WindowData *) Android_Window->driverdata;
825
826
827
828
/* 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);
829
}
830
831
832
/* GL Context handling is done in the event loop because this function is run from the Java thread */
}
833
834
SDL_UnlockMutex(Android_ActivityMutex);
835
836
}
837
/* Called from surfaceDestroyed() */
838
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)(JNIEnv *env, jclass jcls)
839
{
840
SDL_LockMutex(Android_ActivityMutex);
841
842
843
844
845
if (Android_Window && Android_Window->driverdata)
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
SDL_WindowData *data = (SDL_WindowData *) Android_Window->driverdata;
846
847
848
849
850
/* 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
*/
851
852
853
854
855
856
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;
}
857
858
859
860
861
862
if (data->native_window) {
ANativeWindow_release(data->native_window);
}
data->native_window = NULL;
863
/* GL Context handling is done in the event loop because this function is run from the Java thread */
864
}
865
866
SDL_UnlockMutex(Android_ActivityMutex);
867
868
869
}
/* Keydown */
870
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyDown)(
871
JNIEnv *env, jclass jcls,
872
jint keycode)
873
874
875
876
877
{
Android_OnKeyDown(keycode);
}
/* Keyup */
878
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyUp)(
879
JNIEnv *env, jclass jcls,
880
jint keycode)
881
882
883
884
885
{
Android_OnKeyUp(keycode);
}
/* Keyboard Focus Lost */
886
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyboardFocusLost)(
887
JNIEnv *env, jclass jcls)
888
889
890
891
892
893
894
{
/* Calling SDL_StopTextInput will take care of hiding the keyboard and cleaning up the DummyText widget */
SDL_StopTextInput();
}
/* Touch */
895
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeTouch)(
896
JNIEnv *env, jclass jcls,
897
898
899
jint touch_device_id_in, jint pointer_finger_id_in,
jint action, jfloat x, jfloat y, jfloat p)
{
900
SDL_LockMutex(Android_ActivityMutex);
901
902
Android_OnTouch(Android_Window, touch_device_id_in, pointer_finger_id_in, action, x, y, p);
903
904
SDL_UnlockMutex(Android_ActivityMutex);
905
906
907
}
/* Mouse */
908
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeMouse)(
909
JNIEnv *env, jclass jcls,
910
jint button, jint action, jfloat x, jfloat y, jboolean relative)
911
{
912
SDL_LockMutex(Android_ActivityMutex);
913
914
Android_OnMouse(Android_Window, button, action, x, y, relative);
915
916
SDL_UnlockMutex(Android_ActivityMutex);
917
918
919
}
/* Accelerometer */
920
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeAccel)(
921
JNIEnv *env, jclass jcls,
922
923
924
925
926
927
928
929
jfloat x, jfloat y, jfloat z)
{
fLastAccelerometer[0] = x;
fLastAccelerometer[1] = y;
fLastAccelerometer[2] = z;
bHasNewData = SDL_TRUE;
}
930
931
/* Clipboard */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeClipboardChanged)(
932
JNIEnv *env, jclass jcls)
933
934
935
936
{
SDL_SendClipboardUpdate();
}
937
/* Low memory */
938
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeLowMemory)(
939
JNIEnv *env, jclass cls)
940
941
942
943
{
SDL_SendAppEvent(SDL_APP_LOWMEMORY);
}
944
945
/* Send Quit event to "SDLThread" thread */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSendQuit)(
946
JNIEnv *env, jclass cls)
947
948
{
/* Discard previous events. The user should have handled state storage
949
* in SDL_APP_WILLENTERBACKGROUND. After nativeSendQuit() is called, no
950
951
952
953
954
* 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);
955
956
957
958
/* Robustness: clear any pending Pause */
while (SDL_SemTryWait(Android_PauseSem) == 0) {
/* empty */
}
959
960
/* Resume the event loop so that the app can catch SDL_QUIT which
* should now be the top event in the event queue. */
961
SDL_SemPost(Android_ResumeSem);
962
963
}
964
965
966
967
968
969
970
971
972
973
974
/* 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;
}
975
976
977
978
979
980
981
982
983
984
if (Android_PauseSem) {
SDL_DestroySemaphore(Android_PauseSem);
Android_PauseSem = NULL;
}
if (Android_ResumeSem) {
SDL_DestroySemaphore(Android_ResumeSem);
Android_ResumeSem = NULL;
}
985
986
str = SDL_GetError();
if (str && str[0]) {
987
__android_log_print(ANDROID_LOG_ERROR, "SDL", "SDLActivity thread ends (error=%s)", str);
988
989
990
991
992
} else {
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDLActivity thread ends");
}
}
993
/* Pause */
994
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePause)(
995
JNIEnv *env, jclass cls)
996
{
997
SDL_LockMutex(Android_ActivityMutex);
998
999
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativePause()");
1000