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