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