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