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