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