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