/
SDL_android.c
2610 lines (2166 loc) · 92.6 KB
1
2
/*
Simple DirectMedia Layer
3
Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#include "SDL_stdinc.h"
#include "SDL_assert.h"
#include "SDL_hints.h"
#include "SDL_log.h"
26
#include "SDL_main.h"
27
28
29
30
31
32
#ifdef __ANDROID__
#include "SDL_system.h"
#include "SDL_android.h"
33
34
#include "keyinfotable.h"
35
36
37
38
39
40
41
#include "../../events/SDL_events_c.h"
#include "../../video/android/SDL_androidkeyboard.h"
#include "../../video/android/SDL_androidmouse.h"
#include "../../video/android/SDL_androidtouch.h"
#include "../../video/android/SDL_androidvideo.h"
#include "../../video/android/SDL_androidwindow.h"
#include "../../joystick/android/SDL_sysjoystick_c.h"
42
#include "../../haptic/android/SDL_syshaptic_c.h"
43
44
45
46
47
#include <android/log.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
48
#include <dlfcn.h>
49
50
51
52
53
#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)
54
55
#define SDL_JAVA_AUDIO_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, SDLAudioManager, function)
#define SDL_JAVA_CONTROLLER_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, SDLControllerManager, function)
56
57
#define SDL_JAVA_INTERFACE_INPUT_CONNECTION(function) CONCAT1(SDL_JAVA_PREFIX, SDLInputConnection, function)
58
59
60
61
/* Audio encoding definitions */
#define ENCODING_PCM_8BIT 3
#define ENCODING_PCM_16BIT 2
#define ENCODING_PCM_FLOAT 4
62
63
/* Java class SDLActivity */
64
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(
65
JNIEnv *env, jclass cls);
66
67
JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeRunMain)(
68
JNIEnv *env, jclass cls,
69
70
jstring library, jstring function, jobject array);
71
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeDropFile)(
72
JNIEnv *env, jclass jcls,
73
74
jstring filename);
75
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetScreenResolution)(
76
JNIEnv *env, jclass jcls,
77
jint surfaceWidth, jint surfaceHeight,
78
jint deviceWidth, jint deviceHeight, jint format, jfloat rate);
79
80
81
82
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeResize)(
JNIEnv *env, jclass cls);
83
84
85
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceCreated)(
JNIEnv *env, jclass jcls);
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(nativeSendQuit)(
123
JNIEnv *env, jclass cls);
124
125
126
127
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeQuit)(
JNIEnv *env, jclass cls);
128
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePause)(
129
JNIEnv *env, jclass cls);
130
131
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeResume)(
132
JNIEnv *env, jclass cls);
133
134
JNIEXPORT jstring JNICALL SDL_JAVA_INTERFACE(nativeGetHint)(
135
JNIEnv *env, jclass cls,
136
137
jstring name);
138
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetenv)(
139
JNIEnv *env, jclass cls,
140
141
142
jstring name, jstring value);
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeEnvironmentVariablesSet)(
143
JNIEnv *env, jclass cls);
144
145
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeOrientationChanged)(
146
JNIEnv *env, jclass cls,
147
148
jint orientation);
149
150
151
152
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeAddTouch)(
JNIEnv* env, jclass cls,
jint touchId, jstring name);
153
154
/* Java class SDLInputConnection */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeCommitText)(
155
JNIEnv *env, jclass cls,
156
157
jstring text, jint newCursorPosition);
158
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeGenerateScancodeForUnichar)(
159
JNIEnv *env, jclass cls,
160
161
jchar chUnicode);
162
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeSetComposingText)(
163
JNIEnv *env, jclass cls,
164
165
jstring text, jint newCursorPosition);
166
167
168
169
170
171
172
173
174
/* 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)(
175
JNIEnv *env, jclass jcls,
176
177
178
jint device_id, jint keycode);
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)(
179
JNIEnv *env, jclass jcls,
180
181
182
jint device_id, jint keycode);
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)(
183
JNIEnv *env, jclass jcls,
184
185
186
jint device_id, jint axis, jfloat value);
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
187
JNIEnv *env, jclass jcls,
188
189
190
jint device_id, jint hat_id, jint x, jint y);
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
191
JNIEnv *env, jclass jcls,
192
193
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);
194
195
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)(
196
JNIEnv *env, jclass jcls,
197
198
199
jint device_id);
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic)(
200
JNIEnv *env, jclass jcls,
201
202
203
jint device_id, jstring device_name);
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)(
204
JNIEnv *env, jclass jcls,
205
206
207
jint device_id);
208
209
210
211
/* Uncomment this to log messages entering and exiting methods in this file */
/* #define DEBUG_JNI */
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
225
static pthread_once_t key_once = PTHREAD_ONCE_INIT;
static JavaVM *mJavaVM = NULL;
226
227
228
229
230
231
/* Main activity */
static jclass mActivityClass;
/* method signatures */
static jmethodID midGetNativeSurface;
232
static jmethodID midSetSurfaceViewFormat;
233
static jmethodID midSetActivityTitle;
234
static jmethodID midSetWindowStyle;
235
236
static jmethodID midSetOrientation;
static jmethodID midGetContext;
237
static jmethodID midIsTablet;
238
static jmethodID midIsAndroidTV;
239
static jmethodID midIsChromebook;
240
static jmethodID midIsDeXMode;
241
static jmethodID midManualBackButton;
242
static jmethodID midInitTouch;
243
244
245
static jmethodID midSendMessage;
static jmethodID midShowTextInput;
static jmethodID midIsScreenKeyboardShown;
246
247
248
static jmethodID midClipboardSetText;
static jmethodID midClipboardGetText;
static jmethodID midClipboardHasText;
249
static jmethodID midOpenAPKExpansionInputStream;
250
static jmethodID midGetManifestEnvironmentVariables;
251
static jmethodID midGetDisplayDPI;
252
253
254
static jmethodID midCreateCustomCursor;
static jmethodID midSetCustomCursor;
static jmethodID midSetSystemCursor;
255
256
static jmethodID midSupportsRelativeMouse;
static jmethodID midSetRelativeMouseEnabled;
257
258
259
260
261
262
263
/* audio manager */
static jclass mAudioManagerClass;
/* method signatures */
static jmethodID midAudioOpen;
static jmethodID midAudioWriteByteBuffer;
264
265
static jmethodID midAudioWriteShortBuffer;
static jmethodID midAudioWriteFloatBuffer;
266
267
268
static jmethodID midAudioClose;
static jmethodID midCaptureOpen;
static jmethodID midCaptureReadByteBuffer;
269
270
static jmethodID midCaptureReadShortBuffer;
static jmethodID midCaptureReadFloatBuffer;
271
static jmethodID midCaptureClose;
272
static jmethodID midAudioSetThreadPriority;
273
274
275
276
277
278
279
280
/* controller manager */
static jclass mControllerManagerClass;
/* method signatures */
static jmethodID midPollInputDevices;
static jmethodID midPollHapticDevices;
static jmethodID midHapticRun;
281
static jmethodID midHapticStop;
282
283
/* Accelerometer data storage */
284
static SDL_DisplayOrientation displayOrientation;
285
286
287
static float fLastAccelerometer[3];
static SDL_bool bHasNewData;
288
static SDL_bool bHasEnvironmentVariables = SDL_FALSE;
289
290
291
292
293
/*******************************************************************************
Functions called by JNI
*******************************************************************************/
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
/* From http://developer.android.com/guide/practices/jni.html
* All threads are Linux threads, scheduled by the kernel.
* They're usually started from managed code (using Thread.start), but they can also be created elsewhere and then
* attached to the JavaVM. For example, a thread started with pthread_create can be attached with the
* JNI AttachCurrentThread or AttachCurrentThreadAsDaemon functions. Until a thread is attached, it has no JNIEnv,
* and cannot make JNI calls.
* Attaching a natively-created thread causes a java.lang.Thread object to be constructed and added to the "main"
* ThreadGroup, making it visible to the debugger. Calling AttachCurrentThread on an already-attached thread
* is a no-op.
* Note: You can call this function any number of times for the same thread, there's no harm in it
*/
/* From http://developer.android.com/guide/practices/jni.html
* Threads attached through JNI must call DetachCurrentThread before they exit. If coding this directly is awkward,
* in Android 2.0 (Eclair) and higher you can use pthread_key_create to define a destructor function that will be
* called before the thread exits, and call DetachCurrentThread from there. (Use that key with pthread_setspecific
* to store the JNIEnv in thread-local-storage; that way it'll be passed into your destructor as the argument.)
* Note: The destructor is not called unless the stored value is != NULL
* Note: You can call this function any number of times for the same thread, there's no harm in it
* (except for some lost CPU cycles)
*/
/* Set local storage value */
static int
Android_JNI_SetEnv(JNIEnv *env) {
int status = pthread_setspecific(mThreadKey, env);
if (status < 0) {
__android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed pthread_setspecific() in Android_JNI_SetEnv() (err=%d)", status);
}
return status;
}
/* Get local storage value */
JNIEnv* Android_JNI_GetEnv(void)
{
/* Get JNIEnv from the Thread local storage */
JNIEnv *env = pthread_getspecific(mThreadKey);
if (env == NULL) {
332
/* If it fails, try to attach ! (e.g the thread isn't created with SDL_CreateThread() */
333
334
335
336
337
int status;
/* There should be a JVM */
if (mJavaVM == NULL) {
__android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed, there is no JavaVM");
338
return NULL;
339
340
341
342
343
344
345
346
347
348
349
350
351
352
}
/* Attach the current thread to the JVM and get a JNIEnv.
* It will be detached by pthread_create destructor 'Android_JNI_ThreadDestroyed' */
status = (*mJavaVM)->AttachCurrentThread(mJavaVM, &env, NULL);
if (status < 0) {
__android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed to attach current thread (err=%d)", status);
return NULL;
}
/* Save JNIEnv into the Thread local storage */
if (Android_JNI_SetEnv(env) < 0) {
return NULL;
}
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
}
return env;
}
/* Set up an external thread for using JNI with Android_JNI_GetEnv() */
int Android_JNI_SetupThread(void)
{
JNIEnv *env;
int status;
/* There should be a JVM */
if (mJavaVM == NULL) {
__android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed, there is no JavaVM");
return 0;
}
/* Attach the current thread to the JVM and get a JNIEnv.
* It will be detached by pthread_create destructor 'Android_JNI_ThreadDestroyed' */
status = (*mJavaVM)->AttachCurrentThread(mJavaVM, &env, NULL);
if (status < 0) {
__android_log_print(ANDROID_LOG_ERROR, "SDL", "Failed to attach current thread (err=%d)", status);
return 0;
}
/* Save JNIEnv into the Thread local storage */
if (Android_JNI_SetEnv(env) < 0) {
return 0;
}
return 1;
}
/* Destructor called for each thread where mThreadKey is not NULL */
static void
Android_JNI_ThreadDestroyed(void *value)
{
/* The thread is being destroyed, detach it from the Java VM and set the mThreadKey value to NULL as required */
JNIEnv *env = (JNIEnv *) value;
if (env != NULL) {
(*mJavaVM)->DetachCurrentThread(mJavaVM);
Android_JNI_SetEnv(NULL);
}
}
/* Creation of local storage mThreadKey */
399
static void
400
Android_JNI_CreateKey(void)
401
402
403
404
405
406
407
{
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);
}
}
408
static void
409
Android_JNI_CreateKey_once(void)
410
411
412
413
414
415
416
{
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);
}
}
417
/* Library init */
418
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
419
420
421
422
423
{
mJavaVM = vm;
return JNI_VERSION_1_4;
}
424
void checkJNIReady(void)
425
426
{
if (!mActivityClass || !mAudioManagerClass || !mControllerManagerClass) {
427
/* We aren't fully initialized, let's just return. */
428
429
430
return;
}
431
SDL_SetMainReady();
432
433
434
}
/* Activity initialization -- called before SDL_main() to initialize JNI bindings */
435
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cls)
436
{
437
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeSetupJNI()");
438
439
440
441
442
443
444
445
446
447
448
449
450
451
/*
* 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");
}
452
/* Use a mutex to prevent concurrency issues between Java Activity and Native thread code, when using 'Android_Window'.
453
454
* (Eg. Java sending Touch events, while native code is destroying the main SDL_Window. )
*/
455
456
if (Android_ActivityMutex == NULL) {
Android_ActivityMutex = SDL_CreateMutex(); /* Could this be created twice if onCreate() is called a second time ? */
457
458
}
459
if (Android_ActivityMutex == NULL) {
460
__android_log_print(ANDROID_LOG_ERROR, "SDL", "failed to create Android_ActivityMutex mutex");
461
462
}
463
464
465
466
467
468
469
470
471
472
473
Android_PauseSem = SDL_CreateSemaphore(0);
if (Android_PauseSem == NULL) {
__android_log_print(ANDROID_LOG_ERROR, "SDL", "failed to create Android_PauseSem semaphore");
}
Android_ResumeSem = SDL_CreateSemaphore(0);
if (Android_ResumeSem == NULL) {
__android_log_print(ANDROID_LOG_ERROR, "SDL", "failed to create Android_ResumeSem semaphore");
}
474
mActivityClass = (jclass)((*env)->NewGlobalRef(env, cls));
475
476
midGetNativeSurface = (*env)->GetStaticMethodID(env, mActivityClass,
477
"getNativeSurface","()Landroid/view/Surface;");
478
midSetSurfaceViewFormat = (*env)->GetStaticMethodID(env, mActivityClass,
479
"setSurfaceViewFormat","(I)V");
480
midSetActivityTitle = (*env)->GetStaticMethodID(env, mActivityClass,
481
"setActivityTitle","(Ljava/lang/String;)Z");
482
midSetWindowStyle = (*env)->GetStaticMethodID(env, mActivityClass,
483
"setWindowStyle","(Z)V");
484
midSetOrientation = (*env)->GetStaticMethodID(env, mActivityClass,
485
"setOrientation","(IIZLjava/lang/String;)V");
486
midGetContext = (*env)->GetStaticMethodID(env, mActivityClass,
487
"getContext","()Landroid/content/Context;");
488
midIsTablet = (*env)->GetStaticMethodID(env, mActivityClass,
489
"isTablet", "()Z");
490
midIsAndroidTV = (*env)->GetStaticMethodID(env, mActivityClass,
491
"isAndroidTV","()Z");
492
midIsChromebook = (*env)->GetStaticMethodID(env, mActivityClass,
493
"isChromebook", "()Z");
494
midIsDeXMode = (*env)->GetStaticMethodID(env, mActivityClass,
495
"isDeXMode", "()Z");
496
midManualBackButton = (*env)->GetStaticMethodID(env, mActivityClass,
497
"manualBackButton", "()V");
498
midInitTouch = (*env)->GetStaticMethodID(env, mActivityClass,
499
"initTouch", "()V");
500
midSendMessage = (*env)->GetStaticMethodID(env, mActivityClass,
501
"sendMessage", "(II)Z");
502
midShowTextInput = (*env)->GetStaticMethodID(env, mActivityClass,
503
"showTextInput", "(IIII)Z");
504
midIsScreenKeyboardShown = (*env)->GetStaticMethodID(env, mActivityClass,
505
"isScreenKeyboardShown","()Z");
506
midClipboardSetText = (*env)->GetStaticMethodID(env, mActivityClass,
507
"clipboardSetText", "(Ljava/lang/String;)V");
508
midClipboardGetText = (*env)->GetStaticMethodID(env, mActivityClass,
509
"clipboardGetText", "()Ljava/lang/String;");
510
midClipboardHasText = (*env)->GetStaticMethodID(env, mActivityClass,
511
"clipboardHasText", "()Z");
512
midOpenAPKExpansionInputStream = (*env)->GetStaticMethodID(env, mActivityClass,
513
"openAPKExpansionInputStream", "(Ljava/lang/String;)Ljava/io/InputStream;");
514
515
midGetManifestEnvironmentVariables = (*env)->GetStaticMethodID(env, mActivityClass,
516
"getManifestEnvironmentVariables", "()Z");
517
518
519
520
521
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");
522
523
524
midSupportsRelativeMouse = (*env)->GetStaticMethodID(env, mActivityClass, "supportsRelativeMouse", "()Z");
midSetRelativeMouseEnabled = (*env)->GetStaticMethodID(env, mActivityClass, "setRelativeMouseEnabled", "(Z)Z");
525
526
527
if (!midGetNativeSurface || !midSetSurfaceViewFormat ||
528
!midSetActivityTitle || !midSetWindowStyle || !midSetOrientation || !midGetContext || !midIsTablet || !midIsAndroidTV || !midInitTouch ||
529
!midSendMessage || !midShowTextInput || !midIsScreenKeyboardShown ||
530
!midClipboardSetText || !midClipboardGetText || !midClipboardHasText ||
531
!midOpenAPKExpansionInputStream || !midGetManifestEnvironmentVariables || !midGetDisplayDPI ||
532
!midCreateCustomCursor || !midSetCustomCursor || !midSetSystemCursor || !midSupportsRelativeMouse || !midSetRelativeMouseEnabled ||
533
!midIsChromebook || !midIsDeXMode || !midManualBackButton) {
534
__android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLActivity.java?");
535
}
536
537
538
539
540
checkJNIReady();
}
/* Audio initialization -- called before SDL_main() to initialize JNI bindings */
541
JNIEXPORT void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cls)
542
543
544
{
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "AUDIO nativeSetupJNI()");
545
mAudioManagerClass = (jclass)((*env)->NewGlobalRef(env, cls));
546
547
midAudioOpen = (*env)->GetStaticMethodID(env, mAudioManagerClass,
548
"audioOpen", "(IIII)[I");
549
midAudioWriteByteBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
550
"audioWriteByteBuffer", "([B)V");
551
midAudioWriteShortBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
552
"audioWriteShortBuffer", "([S)V");
553
midAudioWriteFloatBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
554
"audioWriteFloatBuffer", "([F)V");
555
midAudioClose = (*env)->GetStaticMethodID(env, mAudioManagerClass,
556
"audioClose", "()V");
557
midCaptureOpen = (*env)->GetStaticMethodID(env, mAudioManagerClass,
558
"captureOpen", "(IIII)[I");
559
midCaptureReadByteBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
560
"captureReadByteBuffer", "([BZ)I");
561
midCaptureReadShortBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
562
"captureReadShortBuffer", "([SZ)I");
563
midCaptureReadFloatBuffer = (*env)->GetStaticMethodID(env, mAudioManagerClass,
564
"captureReadFloatBuffer", "([FZ)I");
565
midCaptureClose = (*env)->GetStaticMethodID(env, mAudioManagerClass,
566
"captureClose", "()V");
567
midAudioSetThreadPriority = (*env)->GetStaticMethodID(env, mAudioManagerClass,
568
"audioSetThreadPriority", "(ZI)V");
569
570
if (!midAudioOpen || !midAudioWriteByteBuffer || !midAudioWriteShortBuffer || !midAudioWriteFloatBuffer || !midAudioClose ||
571
!midCaptureOpen || !midCaptureReadByteBuffer || !midCaptureReadShortBuffer || !midCaptureReadFloatBuffer || !midCaptureClose || !midAudioSetThreadPriority) {
572
573
574
575
576
577
578
__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 */
579
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cls)
580
581
582
{
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "CONTROLLER nativeSetupJNI()");
583
mControllerManagerClass = (jclass)((*env)->NewGlobalRef(env, cls));
584
585
midPollInputDevices = (*env)->GetStaticMethodID(env, mControllerManagerClass,
586
"pollInputDevices", "()V");
587
midPollHapticDevices = (*env)->GetStaticMethodID(env, mControllerManagerClass,
588
"pollHapticDevices", "()V");
589
midHapticRun = (*env)->GetStaticMethodID(env, mControllerManagerClass,
590
"hapticRun", "(IFI)V");
591
midHapticStop = (*env)->GetStaticMethodID(env, mControllerManagerClass,
592
"hapticStop", "(I)V");
593
594
if (!midPollInputDevices || !midPollHapticDevices || !midHapticRun || !midHapticStop) {
595
596
597
598
__android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLControllerManager.java?");
}
checkJNIReady();
599
600
601
602
603
604
}
/* SDL main function prototype */
typedef int (*SDL_main_func)(int argc, char *argv[]);
/* Start up the SDL app */
605
JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeRunMain)(JNIEnv *env, jclass cls, jstring library, jstring function, jobject array)
606
607
608
609
610
{
int status = -1;
const char *library_file;
void *library_handle;
611
612
__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeRunMain()");
613
614
615
/* Save JNIEnv of SDLThread */
Android_JNI_SetEnv(env);
616
617
618
619
620
621
622
623
624
625
626
627
628
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;
629
SDL_bool isstack;
630
631
632
/* Prepare the arguments. */
len = (*env)->GetArrayLength(env, array);
633
argv = SDL_small_alloc(char *, 1 + len + 1, &isstack); /* !!! FIXME: check for NULL */
634
635
636
637
638
639
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) {
640
641
const char *utf;
char *arg = NULL;
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
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]);
}
666
SDL_small_free(argv, isstack);
667
668
669
670
671
672
673
674
675
676
} 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);
677
}
678
679
(*env)->ReleaseStringUTFChars(env, library, library_file);
680
/* This is a Java thread, it doesn't need to be Detached from the JVM.
681
682
683
* Set to mThreadKey value to NULL not to call pthread_create destructor 'Android_JNI_ThreadDestroyed' */
Android_JNI_SetEnv(NULL);
684
685
/* Do not issue an exit or the whole application will terminate instead of just the SDL thread */
/* exit(status); */
686
687
return status;
688
689
690
}
/* Drop file */
691
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeDropFile)(
692
JNIEnv *env, jclass jcls,
693
694
695
jstring filename)
{
const char *path = (*env)->GetStringUTFChars(env, filename, NULL);
696
SDL_SendDropFile(NULL, path);
697
(*env)->ReleaseStringUTFChars(env, filename, path);
698
SDL_SendDropComplete(NULL);
699
700
}
701
702
/* Set screen resolution */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetScreenResolution)(
703
JNIEnv *env, jclass jcls,
704
jint surfaceWidth, jint surfaceHeight,
705
jint deviceWidth, jint deviceHeight, jint format, jfloat rate)
706
{
707
SDL_LockMutex(Android_ActivityMutex);
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
Android_SetScreenResolution(surfaceWidth, surfaceHeight, deviceWidth, deviceHeight, format, rate);
SDL_UnlockMutex(Android_ActivityMutex);
}
/* Resize */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeResize)(
JNIEnv *env, jclass jcls)
{
SDL_LockMutex(Android_ActivityMutex);
if (Android_Window)
{
Android_SendResize(Android_Window);
}
724
725
SDL_UnlockMutex(Android_ActivityMutex);
726
727
}
728
729
730
731
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeOrientationChanged)(
JNIEnv *env, jclass jcls,
jint orientation)
{
732
733
SDL_LockMutex(Android_ActivityMutex);
734
displayOrientation = (SDL_DisplayOrientation)orientation;
735
736
737
738
739
740
if (Android_Window)
{
SDL_VideoDisplay *display = SDL_GetDisplay(0);
SDL_SendDisplayEvent(display, SDL_DISPLAYEVENT_ORIENTATION, orientation);
}
741
742
SDL_UnlockMutex(Android_ActivityMutex);
743
744
}
745
746
747
748
749
750
751
752
753
754
755
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);
}
756
/* Paddown */
757
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)(
758
JNIEnv *env, jclass jcls,
759
760
761
762
763
764
jint device_id, jint keycode)
{
return Android_OnPadDown(device_id, keycode);
}
/* Padup */
765
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)(
766
JNIEnv *env, jclass jcls,
767
jint device_id, jint keycode)
768
769
770
771
772
{
return Android_OnPadUp(device_id, keycode);
}
/* Joy */
773
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)(
774
JNIEnv *env, jclass jcls,
775
776
777
778
779
780
jint device_id, jint axis, jfloat value)
{
Android_OnJoy(device_id, axis, value);
}
/* POV Hat */
781
JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
782
JNIEnv *env, jclass jcls,
783
784
785
786
787
788
jint device_id, jint hat_id, jint x, jint y)
{
Android_OnHat(device_id, hat_id, x, y);
}
789
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
790
JNIEnv *env, jclass jcls,
791
792
793
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)
794
795
796
{
int retval;
const char *name = (*env)->GetStringUTFChars(env, device_name, NULL);
797
const char *desc = (*env)->GetStringUTFChars(env, device_desc, NULL);
798
799
retval = Android_AddJoystick(device_id, name, desc, vendor_id, product_id, is_accelerometer ? SDL_TRUE : SDL_FALSE, button_mask, naxes, nhats, nballs);
800
801
(*env)->ReleaseStringUTFChars(env, device_name, name);
802
(*env)->ReleaseStringUTFChars(env, device_desc, desc);
803
804
805
806
return retval;
}
807
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)(
808
JNIEnv *env, jclass jcls,
809
jint device_id)
810
811
812
813
{
return Android_RemoveJoystick(device_id);
}
814
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic)(
815
JNIEnv *env, jclass jcls, jint device_id, jstring device_name)
816
817
818
819
820
821
822
823
824
825
826
{
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;
}
827
JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)(
828
JNIEnv *env, jclass jcls, jint device_id)
829
830
831
832
{
return Android_RemoveHaptic(device_id);
}
833
834
835
836
837
/* Called from surfaceCreated() */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceCreated)(JNIEnv *env, jclass jcls)
{
SDL_LockMutex(Android_ActivityMutex);
838
if (Android_Window)
839
840
841
842
843
844
845
846
847
848
849
{
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);
}
850
851
/* Called from surfaceChanged() */
852
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(JNIEnv *env, jclass jcls)
853
{
854
SDL_LockMutex(Android_ActivityMutex);
855
856
if (Android_Window)
857
858
859
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
SDL_WindowData *data = (SDL_WindowData *) Android_Window->driverdata;
860
861
862
863
/* 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);
864
}
865
866
867
/* GL Context handling is done in the event loop because this function is run from the Java thread */
}
868
869
SDL_UnlockMutex(Android_ActivityMutex);
870
871
}
872
/* Called from surfaceDestroyed() */
873
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)(JNIEnv *env, jclass jcls)
874
{
875
SDL_LockMutex(Android_ActivityMutex);
876
877
if (Android_Window)
878
879
880
{
SDL_VideoDevice *_this = SDL_GetVideoDevice();
SDL_WindowData *data = (SDL_WindowData *) Android_Window->driverdata;
881
882
883
884
885
/* 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
*/
886
887
888
889
890
891
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;
}
892
893
894
895
896
897
if (data->native_window) {
ANativeWindow_release(data->native_window);
}
data->native_window = NULL;
898
/* GL Context handling is done in the event loop because this function is run from the Java thread */
899
}
900
901
SDL_UnlockMutex(Android_ActivityMutex);
902
903
904
}
/* Keydown */
905
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyDown)(
906
JNIEnv *env, jclass jcls,
907
jint keycode)
908
909
910
911
912
{
Android_OnKeyDown(keycode);
}
/* Keyup */
913
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyUp)(
914
JNIEnv *env, jclass jcls,
915
jint keycode)
916
917
918
919
920
{
Android_OnKeyUp(keycode);
}
/* Keyboard Focus Lost */
921
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeKeyboardFocusLost)(
922
JNIEnv *env, jclass jcls)
923
924
925
926
927
928
929
{
/* Calling SDL_StopTextInput will take care of hiding the keyboard and cleaning up the DummyText widget */
SDL_StopTextInput();
}
/* Touch */
930
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeTouch)(
931
JNIEnv *env, jclass jcls,
932
933
934
jint touch_device_id_in, jint pointer_finger_id_in,
jint action, jfloat x, jfloat y, jfloat p)
{
935
SDL_LockMutex(Android_ActivityMutex);
936
937
Android_OnTouch(Android_Window, touch_device_id_in, pointer_finger_id_in, action, x, y, p);
938
939
SDL_UnlockMutex(Android_ActivityMutex);
940
941
942
}
/* Mouse */
943
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeMouse)(
944
JNIEnv *env, jclass jcls,
945
jint button, jint action, jfloat x, jfloat y, jboolean relative)
946
{
947
SDL_LockMutex(Android_ActivityMutex);
948
949
Android_OnMouse(Android_Window, button, action, x, y, relative);
950
951
SDL_UnlockMutex(Android_ActivityMutex);
952
953
954
}
/* Accelerometer */
955
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeAccel)(
956
JNIEnv *env, jclass jcls,
957
958
959
960
961
962
963
964
jfloat x, jfloat y, jfloat z)
{
fLastAccelerometer[0] = x;
fLastAccelerometer[1] = y;
fLastAccelerometer[2] = z;
bHasNewData = SDL_TRUE;
}
965
966
/* Clipboard */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeClipboardChanged)(
967
JNIEnv *env, jclass jcls)
968
969
970
971
{
SDL_SendClipboardUpdate();
}
972
/* Low memory */
973
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeLowMemory)(
974
JNIEnv *env, jclass cls)
975
976
977
978
{
SDL_SendAppEvent(SDL_APP_LOWMEMORY);
}
979
980
/* Send Quit event to "SDLThread" thread */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSendQuit)(
981
JNIEnv *env, jclass cls)
982
983
{
/* Discard previous events. The user should have handled state storage
984
* in SDL_APP_WILLENTERBACKGROUND. After nativeSendQuit() is called, no
985
986
987
988
989
* 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);
990
991
992
993
/* Robustness: clear any pending Pause */
while (SDL_SemTryWait(Android_PauseSem) == 0) {
/* empty */
}
994
995
/* Resume the event loop so that the app can catch SDL_QUIT which
* should now be the top event in the event queue. */
996
SDL_SemPost(Android_ResumeSem);
997
998
}
999
1000
/* Activity ends */
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeQuit)(