/
SDL_android.c
2672 lines (2219 loc) · 94.6 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
716
717
718
719
720
721
722
/* Lock / Unlock Mutex */
void Android_ActivityMutex_Lock() {
SDL_LockMutex(Android_ActivityMutex);
}
void Android_ActivityMutex_Unlock() {
SDL_UnlockMutex(Android_ActivityMutex);
}
723
724
/* Set screen resolution */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetScreenResolution)(
725
JNIEnv *env, jclass jcls,
726
jint surfaceWidth, jint surfaceHeight,
727
jint deviceWidth, jint deviceHeight, jint format, jfloat rate)
728
{
729
SDL_LockMutex(Android_ActivityMutex);
730
731
732
733
734
735
736
737
738
739
740
741
742
743
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)
{
744
Android_SendResize(Android_Window);
745
}
746
747
SDL_UnlockMutex(Android_ActivityMutex);
748
749
}
750
751
752
753
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeOrientationChanged)(
JNIEnv *env, jclass jcls,
jint orientation)
{
754
755
SDL_LockMutex(Android_ActivityMutex);
756
displayOrientation = (SDL_DisplayOrientation)orientation;
757
758
759
760
761
762
if (Android_Window)
{
SDL_VideoDisplay *display = SDL_GetDisplay(0);
SDL_SendDisplayEvent(display, SDL_DISPLAYEVENT_ORIENTATION, orientation);
}
763
764
SDL_UnlockMutex(Android_ActivityMutex);
765
766
}
767
768
769
770
771
772
773
774
775
776
777
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);
}
778
/* Paddown */
779
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)(
780
JNIEnv *env, jclass jcls,
781
782
783
784
785
786
jint device_id, jint keycode)
{
return Android_OnPadDown(device_id, keycode);
}
/* Padup */
787
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)(
788
JNIEnv *env, jclass jcls,
789
jint device_id, jint keycode)
790
791
792
793
794
{
return Android_OnPadUp(device_id, keycode);
}
/* Joy */
795
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)(
796
JNIEnv *env, jclass jcls,
797
798
799
800
801
802
jint device_id, jint axis, jfloat value)
{
Android_OnJoy(device_id, axis, value);
}
/* POV Hat */
803
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
804
JNIEnv *env, jclass jcls,
805
806
807
808
809
810
jint device_id, jint hat_id, jint x, jint y)
{
Android_OnHat(device_id, hat_id, x, y);
}
811
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
812
JNIEnv *env, jclass jcls,
813
814
815
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)
816
817
818
{
int retval;
const char *name = (*env)->GetStringUTFChars(env, device_name, NULL);
819
const char *desc = (*env)->GetStringUTFChars(env, device_desc, NULL);
820
821
retval = Android_AddJoystick(device_id, name, desc, vendor_id, product_id, is_accelerometer ? SDL_TRUE : SDL_FALSE, button_mask, naxes, nhats, nballs);
822
823
(*env)->ReleaseStringUTFChars(env, device_name, name);
824
(*env)->ReleaseStringUTFChars(env, device_desc, desc);
825
826
827
828
return retval;
}
829
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)(
830
JNIEnv *env, jclass jcls,
831
jint device_id)
832
833
834
835
{
return Android_RemoveJoystick(device_id);
}
836
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic)(
837
JNIEnv *env, jclass jcls, jint device_id, jstring device_name)
838
839
840
841
842
843
844
845
846
847
848
{
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;
}
849
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)(
850
JNIEnv *env, jclass jcls, jint device_id)
851
852
853
854
{
return Android_RemoveHaptic(device_id);
}
855
856
857
858
859
/* Called from surfaceCreated() */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceCreated)(JNIEnv *env, jclass jcls)
{
SDL_LockMutex(Android_ActivityMutex);
860
if (Android_Window)
861
862
863
864
865
866
867
868
869
870
871
{
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);
}
872
873
/* Called from surfaceChanged() */
874
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(JNIEnv *env, jclass jcls)
875
{
876
SDL_LockMutex(Android_ActivityMutex);
877
878
if (Android_Window)
879
880
881
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
SDL_WindowData *data = (SDL_WindowData *) Android_Window->driverdata;
882
883
884
885
/* 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);
886
}
887
888
889
/* GL Context handling is done in the event loop because this function is run from the Java thread */
}
890
891
SDL_UnlockMutex(Android_ActivityMutex);
892
893
}
894
/* Called from surfaceDestroyed() */
895
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)(JNIEnv *env, jclass jcls)
896
{
897
SDL_LockMutex(Android_ActivityMutex);
898
899
if (Android_Window)
900
901
902
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
SDL_WindowData *data = (SDL_WindowData *) Android_Window->driverdata;
903
904
905
906
907
/* 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
*/
908
909
910
911
912
913
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;
}
914
915
916
917
918
919
if (data->native_window) {
ANativeWindow_release(data->native_window);
}
data->native_window = NULL;
920
/* GL Context handling is done in the event loop because this function is run from the Java thread */
921
}
922
923
SDL_UnlockMutex(Android_ActivityMutex);
924
925
926
}
/* Keydown */
927
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyDown)(
928
JNIEnv *env, jclass jcls,
929
jint keycode)
930
931
932
933
934
{
Android_OnKeyDown(keycode);
}
/* Keyup */
935
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyUp)(
936
JNIEnv *env, jclass jcls,
937
jint keycode)
938
939
940
941
{
Android_OnKeyUp(keycode);
}
942
943
944
945
946
947
948
949
950
951
952
/* 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;
}
953
/* Keyboard Focus Lost */
954
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyboardFocusLost)(
955
JNIEnv *env, jclass jcls)
956
957
958
959
960
961
962
{
/* Calling SDL_StopTextInput will take care of hiding the keyboard and cleaning up the DummyText widget */
SDL_StopTextInput();
}
/* Touch */
963
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeTouch)(
964
JNIEnv *env, jclass jcls,
965
966
967
jint touch_device_id_in, jint pointer_finger_id_in,
jint action, jfloat x, jfloat y, jfloat p)
{
968
SDL_LockMutex(Android_ActivityMutex);
969
970
Android_OnTouch(Android_Window, touch_device_id_in, pointer_finger_id_in, action, x, y, p);
971
972
SDL_UnlockMutex(Android_ActivityMutex);
973
974
975
}
/* Mouse */
976
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeMouse)(
977
JNIEnv *env, jclass jcls,
978
jint button, jint action, jfloat x, jfloat y, jboolean relative)
979
{
980
SDL_LockMutex(Android_ActivityMutex);
981
982
Android_OnMouse(Android_Window, button, action, x, y, relative);
983
984
SDL_UnlockMutex(Android_ActivityMutex);
985
986
987
}
/* Accelerometer */
988
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeAccel)(
989
JNIEnv *env, jclass jcls,
990
991
992
993
994
995
996
997
jfloat x, jfloat y, jfloat z)
{
fLastAccelerometer[0] = x;
fLastAccelerometer[1] = y;
fLastAccelerometer[2] = z;
bHasNewData = SDL_TRUE;
}
998
999
/* Clipboard */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeClipboardChanged)(
1000
JNIEnv *env, jclass jcls)