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