/
SDL_android.c
2692 lines (2234 loc) · 95.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
#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
632
633
634
635
636
637
638
639
640
641
642
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;
643
SDL_bool isstack;
644
645
646
/* Prepare the arguments. */
len = (*env)->GetArrayLength(env, array);
647
argv = SDL_small_alloc(char *, 1 + len + 1, &isstack); /* !!! FIXME: check for NULL */
648
649
650
651
652
653
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) {
654
655
const char *utf;
char *arg = NULL;
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
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]);
}
680
SDL_small_free(argv, isstack);
681
682
683
684
685
686
687
688
689
690
} 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);
691
}
692
693
(*env)->ReleaseStringUTFChars(env, library, library_file);
694
/* This is a Java thread, it doesn't need to be Detached from the JVM.
695
696
697
* Set to mThreadKey value to NULL not to call pthread_create destructor 'Android_JNI_ThreadDestroyed' */
Android_JNI_SetEnv(NULL);
698
699
/* Do not issue an exit or the whole application will terminate instead of just the SDL thread */
/* exit(status); */
700
701
return status;
702
703
704
}
/* Drop file */
705
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeDropFile)(
706
JNIEnv *env, jclass jcls,
707
708
709
jstring filename)
{
const char *path = (*env)->GetStringUTFChars(env, filename, NULL);
710
SDL_SendDropFile(NULL, path);
711
(*env)->ReleaseStringUTFChars(env, filename, path);
712
SDL_SendDropComplete(NULL);
713
714
}
715
716
717
718
719
720
721
722
723
/* Lock / Unlock Mutex */
void Android_ActivityMutex_Lock() {
SDL_LockMutex(Android_ActivityMutex);
}
void Android_ActivityMutex_Unlock() {
SDL_UnlockMutex(Android_ActivityMutex);
}
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
/* 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;
}
}
743
744
/* Set screen resolution */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetScreenResolution)(
745
JNIEnv *env, jclass jcls,
746
jint surfaceWidth, jint surfaceHeight,
747
jint deviceWidth, jint deviceHeight, jint format, jfloat rate)
748
{
749
SDL_LockMutex(Android_ActivityMutex);
750
751
752
753
754
755
756
757
758
759
760
761
762
763
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)
{
764
Android_SendResize(Android_Window);
765
}
766
767
SDL_UnlockMutex(Android_ActivityMutex);
768
769
}
770
771
772
773
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeOrientationChanged)(
JNIEnv *env, jclass jcls,
jint orientation)
{
774
775
SDL_LockMutex(Android_ActivityMutex);
776
displayOrientation = (SDL_DisplayOrientation)orientation;
777
778
779
780
781
782
if (Android_Window)
{
SDL_VideoDisplay *display = SDL_GetDisplay(0);
SDL_SendDisplayEvent(display, SDL_DISPLAYEVENT_ORIENTATION, orientation);
}
783
784
SDL_UnlockMutex(Android_ActivityMutex);
785
786
}
787
788
789
790
791
792
793
794
795
796
797
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);
}
798
/* Paddown */
799
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)(
800
JNIEnv *env, jclass jcls,
801
802
803
804
805
806
jint device_id, jint keycode)
{
return Android_OnPadDown(device_id, keycode);
}
/* Padup */
807
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)(
808
JNIEnv *env, jclass jcls,
809
jint device_id, jint keycode)
810
811
812
813
814
{
return Android_OnPadUp(device_id, keycode);
}
/* Joy */
815
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)(
816
JNIEnv *env, jclass jcls,
817
818
819
820
821
822
jint device_id, jint axis, jfloat value)
{
Android_OnJoy(device_id, axis, value);
}
/* POV Hat */
823
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
824
JNIEnv *env, jclass jcls,
825
826
827
828
829
830
jint device_id, jint hat_id, jint x, jint y)
{
Android_OnHat(device_id, hat_id, x, y);
}
831
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
832
JNIEnv *env, jclass jcls,
833
834
835
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)
836
837
838
{
int retval;
const char *name = (*env)->GetStringUTFChars(env, device_name, NULL);
839
const char *desc = (*env)->GetStringUTFChars(env, device_desc, NULL);
840
841
retval = Android_AddJoystick(device_id, name, desc, vendor_id, product_id, is_accelerometer ? SDL_TRUE : SDL_FALSE, button_mask, naxes, nhats, nballs);
842
843
(*env)->ReleaseStringUTFChars(env, device_name, name);
844
(*env)->ReleaseStringUTFChars(env, device_desc, desc);
845
846
847
848
return retval;
}
849
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)(
850
JNIEnv *env, jclass jcls,
851
jint device_id)
852
853
854
855
{
return Android_RemoveJoystick(device_id);
}
856
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic)(
857
JNIEnv *env, jclass jcls, jint device_id, jstring device_name)
858
859
860
861
862
863
864
865
866
867
868
{
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;
}
869
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)(
870
JNIEnv *env, jclass jcls, jint device_id)
871
872
873
874
{
return Android_RemoveHaptic(device_id);
}
875
876
877
878
879
/* Called from surfaceCreated() */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceCreated)(JNIEnv *env, jclass jcls)
{
SDL_LockMutex(Android_ActivityMutex);
880
if (Android_Window)
881
882
883
884
885
886
887
888
889
890
891
{
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);
}
892
893
/* Called from surfaceChanged() */
894
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(JNIEnv *env, jclass jcls)
895
{
896
SDL_LockMutex(Android_ActivityMutex);
897
898
if (Android_Window)
899
900
901
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
SDL_WindowData *data = (SDL_WindowData *) Android_Window->driverdata;
902
903
904
905
/* 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);
906
}
907
908
909
/* GL Context handling is done in the event loop because this function is run from the Java thread */
}
910
911
SDL_UnlockMutex(Android_ActivityMutex);
912
913
}
914
/* Called from surfaceDestroyed() */
915
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)(JNIEnv *env, jclass jcls)
916
{
917
SDL_LockMutex(Android_ActivityMutex);
918
919
if (Android_Window)
920
921
922
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
SDL_WindowData *data = (SDL_WindowData *) Android_Window->driverdata;
923
924
925
926
927
/* 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
*/
928
929
930
931
932
933
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;
}
934
935
936
937
938
939
if (data->native_window) {
ANativeWindow_release(data->native_window);
}
data->native_window = NULL;
940
/* GL Context handling is done in the event loop because this function is run from the Java thread */
941
}
942
943
SDL_UnlockMutex(Android_ActivityMutex);
944
945
946
}
/* Keydown */
947
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyDown)(
948
JNIEnv *env, jclass jcls,
949
jint keycode)
950
951
952
953
954
{
Android_OnKeyDown(keycode);
}
/* Keyup */
955
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyUp)(
956
JNIEnv *env, jclass jcls,
957
jint keycode)
958
959
960
961
{
Android_OnKeyUp(keycode);
}
962
963
964
965
966
967
968
969
970
971
972
/* 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;
}
973
/* Keyboard Focus Lost */
974
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyboardFocusLost)(
975
JNIEnv *env, jclass jcls)
976
977
978
979
980
981
982
{
/* Calling SDL_StopTextInput will take care of hiding the keyboard and cleaning up the DummyText widget */
SDL_StopTextInput();
}
/* Touch */
983
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeTouch)(
984
JNIEnv *env, jclass jcls,
985
986
987
jint touch_device_id_in, jint pointer_finger_id_in,
jint action, jfloat x, jfloat y, jfloat p)
{
988
SDL_LockMutex(Android_ActivityMutex);
989
990
Android_OnTouch(Android_Window, touch_device_id_in, pointer_finger_id_in, action, x, y, p);
991
992
SDL_UnlockMutex(Android_ActivityMutex);
993
994
995
}
/* Mouse */
996
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeMouse)(
997
JNIEnv *env, jclass jcls,
998
jint button, jint action, jfloat x, jfloat y, jboolean relative)
999
{
1000
SDL_LockMutex(Android_ActivityMutex);