/
SDL_android.c
2576 lines (2138 loc) · 91.7 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);
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);
116
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeLowMemory)(
117
JNIEnv *env, jclass cls);
119
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSendQuit)(
120
JNIEnv *env, jclass cls);
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);
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);
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;
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;
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;
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)
416
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeSetupJNI()");
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 ? */
438
if (Android_ActivityMutex == NULL) {
439
__android_log_print(ANDROID_LOG_ERROR, "SDL", "failed to create Android_ActivityMutex mutex");
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));
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;");
494
midGetManifestEnvironmentVariables = (*env)->GetStaticMethodID(env, mActivityClass,
495
"getManifestEnvironmentVariables", "()Z");
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");
502
503
midSupportsRelativeMouse = (*env)->GetStaticMethodID(env, mActivityClass, "supportsRelativeMouse", "()Z");
midSetRelativeMouseEnabled = (*env)->GetStaticMethodID(env, mActivityClass, "setRelativeMouseEnabled", "(Z)Z");
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?");
516
fidSeparateMouseAndTouch = (*env)->GetStaticFieldID(env, mActivityClass, "mSeparateMouseAndTouch", "Z");
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));
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");
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));
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");
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);
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); */
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)
692
SDL_LockMutex(Android_ActivityMutex);
694
Android_SetScreenResolution(Android_Window, surfaceWidth, surfaceHeight, deviceWidth, deviceHeight, format, rate);
696
SDL_UnlockMutex(Android_ActivityMutex);
699
700
701
702
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeOrientationChanged)(
JNIEnv *env, jclass jcls,
jint orientation)
{
703
704
705
706
SDL_LockMutex(Android_ActivityMutex);
if (Android_Window)
{
707
708
709
SDL_VideoDisplay *display = SDL_GetDisplay(0);
SDL_SendDisplayEvent(display, SDL_DISPLAYEVENT_ORIENTATION, orientation);
}
710
711
SDL_UnlockMutex(Android_ActivityMutex);
714
715
716
717
718
719
720
721
722
723
724
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);
}
726
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)(
727
JNIEnv *env, jclass jcls,
728
729
730
731
732
733
jint device_id, jint keycode)
{
return Android_OnPadDown(device_id, keycode);
}
/* Padup */
734
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)(
735
JNIEnv *env, jclass jcls,
736
jint device_id, jint keycode)
737
738
739
740
741
{
return Android_OnPadUp(device_id, keycode);
}
/* Joy */
742
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)(
743
JNIEnv *env, jclass jcls,
744
745
746
747
748
749
jint device_id, jint axis, jfloat value)
{
Android_OnJoy(device_id, axis, value);
}
/* POV Hat */
750
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
751
JNIEnv *env, jclass jcls,
752
753
754
755
756
757
jint device_id, jint hat_id, jint x, jint y)
{
Android_OnHat(device_id, hat_id, x, y);
}
758
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
759
JNIEnv *env, jclass jcls,
760
761
762
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)
763
764
765
{
int retval;
const char *name = (*env)->GetStringUTFChars(env, device_name, NULL);
766
const char *desc = (*env)->GetStringUTFChars(env, device_desc, NULL);
768
retval = Android_AddJoystick(device_id, name, desc, vendor_id, product_id, is_accelerometer ? SDL_TRUE : SDL_FALSE, button_mask, naxes, nhats, nballs);
769
770
(*env)->ReleaseStringUTFChars(env, device_name, name);
771
(*env)->ReleaseStringUTFChars(env, device_desc, desc);
773
774
775
return retval;
}
776
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)(
777
JNIEnv *env, jclass jcls,
778
jint device_id)
779
780
781
782
{
return Android_RemoveJoystick(device_id);
}
783
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic)(
784
JNIEnv *env, jclass jcls, jint device_id, jstring device_name)
785
786
787
788
789
790
791
792
793
794
795
{
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;
}
796
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)(
797
JNIEnv *env, jclass jcls, jint device_id)
798
799
800
801
{
return Android_RemoveHaptic(device_id);
}
802
803
804
805
806
/* Called from surfaceCreated() */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceCreated)(JNIEnv *env, jclass jcls)
{
SDL_LockMutex(Android_ActivityMutex);
807
if (Android_Window)
808
809
810
811
812
813
814
815
816
817
818
{
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);
}
820
/* Called from surfaceChanged() */
821
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(JNIEnv *env, jclass jcls)
823
SDL_LockMutex(Android_ActivityMutex);
825
if (Android_Window)
826
827
828
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
SDL_WindowData *data = (SDL_WindowData *) Android_Window->driverdata;
830
831
832
/* 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);
835
836
/* GL Context handling is done in the event loop because this function is run from the Java thread */
}
838
SDL_UnlockMutex(Android_ActivityMutex);
841
/* Called from surfaceDestroyed() */
842
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)(JNIEnv *env, jclass jcls)
844
SDL_LockMutex(Android_ActivityMutex);
846
if (Android_Window)
847
848
849
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
SDL_WindowData *data = (SDL_WindowData *) Android_Window->driverdata;
851
852
853
854
/* 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
*/
856
857
858
859
860
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;
}
862
863
864
865
866
if (data->native_window) {
ANativeWindow_release(data->native_window);
}
data->native_window = NULL;
867
/* GL Context handling is done in the event loop because this function is run from the Java thread */
870
SDL_UnlockMutex(Android_ActivityMutex);
871
872
873
}
/* Keydown */
874
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyDown)(
875
JNIEnv *env, jclass jcls,
876
jint keycode)
877
878
879
880
881
{
Android_OnKeyDown(keycode);
}
/* Keyup */
882
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyUp)(
883
JNIEnv *env, jclass jcls,
884
jint keycode)
885
886
887
888
889
{
Android_OnKeyUp(keycode);
}
/* Keyboard Focus Lost */
890
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyboardFocusLost)(
891
JNIEnv *env, jclass jcls)
892
893
894
895
896
897
898
{
/* Calling SDL_StopTextInput will take care of hiding the keyboard and cleaning up the DummyText widget */
SDL_StopTextInput();
}
/* Touch */
899
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeTouch)(
900
JNIEnv *env, jclass jcls,
901
902
903
jint touch_device_id_in, jint pointer_finger_id_in,
jint action, jfloat x, jfloat y, jfloat p)
{
904
SDL_LockMutex(Android_ActivityMutex);
906
Android_OnTouch(Android_Window, touch_device_id_in, pointer_finger_id_in, action, x, y, p);
908
SDL_UnlockMutex(Android_ActivityMutex);
909
910
911
}
/* Mouse */
912
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeMouse)(
913
JNIEnv *env, jclass jcls,
914
jint button, jint action, jfloat x, jfloat y, jboolean relative)
916
SDL_LockMutex(Android_ActivityMutex);
918
Android_OnMouse(Android_Window, button, action, x, y, relative);
920
SDL_UnlockMutex(Android_ActivityMutex);
921
922
923
}
/* Accelerometer */
924
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeAccel)(
925
JNIEnv *env, jclass jcls,
926
927
928
929
930
931
932
933
jfloat x, jfloat y, jfloat z)
{
fLastAccelerometer[0] = x;
fLastAccelerometer[1] = y;
fLastAccelerometer[2] = z;
bHasNewData = SDL_TRUE;
}
934
935
/* Clipboard */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeClipboardChanged)(
936
JNIEnv *env, jclass jcls)
937
938
939
940
{
SDL_SendClipboardUpdate();
}
941
/* Low memory */
942
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeLowMemory)(
943
JNIEnv *env, jclass cls)
944
945
946
947
{
SDL_SendAppEvent(SDL_APP_LOWMEMORY);
}
948
949
/* Send Quit event to "SDLThread" thread */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSendQuit)(
950
JNIEnv *env, jclass cls)
951
952
{
/* Discard previous events. The user should have handled state storage
953
* in SDL_APP_WILLENTERBACKGROUND. After nativeSendQuit() is called, no
954
955
956
957
958
* 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);
959
960
961
962
/* Robustness: clear any pending Pause */
while (SDL_SemTryWait(Android_PauseSem) == 0) {
/* empty */
}
963
964
/* Resume the event loop so that the app can catch SDL_QUIT which
* should now be the top event in the event queue. */
965
SDL_SemPost(Android_ResumeSem);
968
969
970
971
972
973
974
975
976
977
978
/* 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;
}
979
980
981
982
983
984
985
986
987
988
if (Android_PauseSem) {
SDL_DestroySemaphore(Android_PauseSem);
Android_PauseSem = NULL;
}
if (Android_ResumeSem) {
SDL_DestroySemaphore(Android_ResumeSem);
Android_ResumeSem = NULL;
}
989
990
str = SDL_GetError();
if (str && str[0]) {
991
__android_log_print(ANDROID_LOG_ERROR, "SDL", "SDLActivity thread ends (error=%s)", str);
992
993
994
995
996
} else {
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDLActivity thread ends");
}
}
998
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePause)(
999
JNIEnv *env, jclass cls)