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