21 #include "../../SDL_internal.h"
33 #include "keyinfotable.h"
35 #include "../../events/SDL_events_c.h"
36 #include "../../video/android/SDL_androidkeyboard.h"
37 #include "../../video/android/SDL_androidmouse.h"
38 #include "../../video/android/SDL_androidtouch.h"
39 #include "../../video/android/SDL_androidvideo.h"
40 #include "../../video/android/SDL_androidwindow.h"
41 #include "../../joystick/android/SDL_sysjoystick_c.h"
42 #include "../../haptic/android/SDL_syshaptic_c.h"
44 #include <android/log.h>
46 #include <sys/types.h>
52 #define LOGI(...) do {} while (0)
53 #define LOGE(...) do {} while (0)
56 #define SDL_JAVA_PREFIX org_libsdl_app
57 #define CONCAT1(prefix, class, function) CONCAT2(prefix, class, function)
58 #define CONCAT2(prefix, class, function) Java_ ## prefix ## _ ## class ## _ ## function
59 #define SDL_JAVA_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, SDLActivity, function)
60 #define SDL_JAVA_AUDIO_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, SDLAudioManager, function)
61 #define SDL_JAVA_CONTROLLER_INTERFACE(function) CONCAT1(SDL_JAVA_PREFIX, SDLControllerManager, function)
62 #define SDL_JAVA_INTERFACE_INPUT_CONNECTION(function) CONCAT1(SDL_JAVA_PREFIX, SDLInputConnection, function)
65 #define ENCODING_PCM_8BIT 3
66 #define ENCODING_PCM_16BIT 2
67 #define ENCODING_PCM_FLOAT 4
70 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(
71 JNIEnv* mEnv, jclass cls);
73 JNIEXPORT
int JNICALL SDL_JAVA_INTERFACE(nativeRunMain)(
74 JNIEnv* env, jclass cls,
75 jstring library, jstring
function, jobject
array);
77 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(onNativeDropFile)(
78 JNIEnv* env, jclass jcls,
81 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(onNativeResize)(
82 JNIEnv* env, jclass jcls,
83 jint surfaceWidth, jint surfaceHeight,
84 jint deviceWidth, jint deviceHeight, jint
format, jfloat rate);
86 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(
87 JNIEnv* env, jclass jcls);
89 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)(
90 JNIEnv* env, jclass jcls);
92 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(onNativeKeyDown)(
93 JNIEnv* env, jclass jcls,
96 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(onNativeKeyUp)(
97 JNIEnv* env, jclass jcls,
100 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(onNativeKeyboardFocusLost)(
101 JNIEnv* env, jclass jcls);
103 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(onNativeTouch)(
104 JNIEnv* env, jclass jcls,
105 jint touch_device_id_in, jint pointer_finger_id_in,
106 jint action, jfloat
x, jfloat
y, jfloat
p);
108 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(onNativeMouse)(
109 JNIEnv* env, jclass jcls,
110 jint
button, jint action, jfloat
x, jfloat
y, jboolean relative);
112 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(onNativeAccel)(
113 JNIEnv* env, jclass jcls,
114 jfloat
x, jfloat
y, jfloat
z);
116 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(onNativeClipboardChanged)(
117 JNIEnv* env, jclass jcls);
119 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(nativeLowMemory)(
120 JNIEnv* env, jclass cls);
122 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(nativeQuit)(
123 JNIEnv* env, jclass cls);
125 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(nativePause)(
126 JNIEnv* env, jclass cls);
128 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(nativeResume)(
129 JNIEnv* env, jclass cls);
131 JNIEXPORT jstring JNICALL SDL_JAVA_INTERFACE(nativeGetHint)(
132 JNIEnv* env, jclass cls,
135 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(nativeSetenv)(
136 JNIEnv* env, jclass cls,
139 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(nativeEnvironmentVariablesSet)(
140 JNIEnv* env, jclass cls);
142 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(onNativeOrientationChanged)(
143 JNIEnv* env, jclass cls,
147 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeCommitText)(
148 JNIEnv* env, jclass cls,
149 jstring
text, jint newCursorPosition);
151 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeGenerateScancodeForUnichar)(
152 JNIEnv* env, jclass cls,
155 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeSetComposingText)(
156 JNIEnv* env, jclass cls,
157 jstring
text, jint newCursorPosition);
160 JNIEXPORT
void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(
161 JNIEnv *env, jclass jcls);
164 JNIEXPORT
void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(
165 JNIEnv *env, jclass jcls);
167 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)(
168 JNIEnv* env, jclass jcls,
169 jint device_id, jint keycode);
171 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)(
172 JNIEnv* env, jclass jcls,
173 jint device_id, jint keycode);
175 JNIEXPORT
void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)(
176 JNIEnv* env, jclass jcls,
177 jint device_id, jint
axis, jfloat
value);
179 JNIEXPORT
void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
180 JNIEnv* env, jclass jcls,
181 jint device_id, jint hat_id, jint
x, jint
y);
183 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
184 JNIEnv* env, jclass jcls,
185 jint device_id, jstring device_name, jstring device_desc, jint vendor_id, jint product_id,
186 jboolean is_accelerometer, jint button_mask, jint naxes, jint nhats, jint nballs);
188 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)(
189 JNIEnv* env, jclass jcls,
192 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic)(
193 JNIEnv* env, jclass jcls,
194 jint device_id, jstring device_name);
196 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)(
197 JNIEnv* env, jclass jcls,
205 static void Android_JNI_ThreadDestroyed(
void*);
206 static void checkJNIReady(
void);
217 static pthread_key_t mThreadKey;
218 static JavaVM* mJavaVM;
221 static jclass mActivityClass;
224 static jmethodID midGetNativeSurface;
225 static jmethodID midSetActivityTitle;
226 static jmethodID midSetWindowStyle;
227 static jmethodID midSetOrientation;
228 static jmethodID midGetContext;
229 static jmethodID midIsTablet;
230 static jmethodID midIsAndroidTV;
231 static jmethodID midIsChromebook;
232 static jmethodID midIsDeXMode;
233 static jmethodID midManualBackButton;
234 static jmethodID midInputGetInputDeviceIds;
235 static jmethodID midSendMessage;
236 static jmethodID midShowTextInput;
237 static jmethodID midIsScreenKeyboardShown;
238 static jmethodID midClipboardSetText;
239 static jmethodID midClipboardGetText;
240 static jmethodID midClipboardHasText;
241 static jmethodID midOpenAPKExpansionInputStream;
242 static jmethodID midGetManifestEnvironmentVariables;
243 static jmethodID midGetDisplayDPI;
244 static jmethodID midCreateCustomCursor;
245 static jmethodID midSetCustomCursor;
246 static jmethodID midSetSystemCursor;
247 static jmethodID midSupportsRelativeMouse;
248 static jmethodID midSetRelativeMouseEnabled;
251 static jclass mAudioManagerClass;
254 static jmethodID midAudioOpen;
255 static jmethodID midAudioWriteByteBuffer;
256 static jmethodID midAudioWriteShortBuffer;
257 static jmethodID midAudioWriteFloatBuffer;
258 static jmethodID midAudioClose;
259 static jmethodID midCaptureOpen;
260 static jmethodID midCaptureReadByteBuffer;
261 static jmethodID midCaptureReadShortBuffer;
262 static jmethodID midCaptureReadFloatBuffer;
263 static jmethodID midCaptureClose;
266 static jclass mControllerManagerClass;
269 static jmethodID midPollInputDevices;
270 static jmethodID midPollHapticDevices;
271 static jmethodID midHapticRun;
272 static jmethodID midHapticStop;
275 static jfieldID fidSeparateMouseAndTouch;
278 static float fLastAccelerometer[3];
288 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm,
void* reserved)
292 LOGI(
"JNI_OnLoad called");
293 if ((*mJavaVM)->GetEnv(mJavaVM, (
void**) &env, JNI_VERSION_1_4) != JNI_OK) {
294 LOGE(
"Failed to get the environment using GetEnv()");
301 if (pthread_key_create(&mThreadKey, Android_JNI_ThreadDestroyed) != 0) {
302 __android_log_print(ANDROID_LOG_ERROR,
"SDL",
"Error initializing pthread key");
306 return JNI_VERSION_1_4;
311 if (!mActivityClass || !mAudioManagerClass || !mControllerManagerClass) {
320 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv* mEnv, jclass cls)
322 __android_log_print(ANDROID_LOG_VERBOSE,
"SDL",
"nativeSetupJNI()");
326 mActivityClass = (jclass)((*mEnv)->NewGlobalRef(mEnv, cls));
328 midGetNativeSurface = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
329 "getNativeSurface",
"()Landroid/view/Surface;");
330 midSetActivityTitle = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
331 "setActivityTitle",
"(Ljava/lang/String;)Z");
332 midSetWindowStyle = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
333 "setWindowStyle",
"(Z)V");
334 midSetOrientation = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
335 "setOrientation",
"(IIZLjava/lang/String;)V");
336 midGetContext = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
337 "getContext",
"()Landroid/content/Context;");
338 midIsTablet = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
340 midIsAndroidTV = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
341 "isAndroidTV",
"()Z");
342 midIsChromebook = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
343 "isChromebook",
"()Z");
344 midIsDeXMode = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
346 midManualBackButton = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
347 "manualBackButton",
"()V");
348 midInputGetInputDeviceIds = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
349 "inputGetInputDeviceIds",
"(I)[I");
350 midSendMessage = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
351 "sendMessage",
"(II)Z");
352 midShowTextInput = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
353 "showTextInput",
"(IIII)Z");
354 midIsScreenKeyboardShown = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
355 "isScreenKeyboardShown",
"()Z");
356 midClipboardSetText = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
357 "clipboardSetText",
"(Ljava/lang/String;)V");
358 midClipboardGetText = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
359 "clipboardGetText",
"()Ljava/lang/String;");
360 midClipboardHasText = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
361 "clipboardHasText",
"()Z");
362 midOpenAPKExpansionInputStream = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
363 "openAPKExpansionInputStream",
"(Ljava/lang/String;)Ljava/io/InputStream;");
365 midGetManifestEnvironmentVariables = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
366 "getManifestEnvironmentVariables",
"()Z");
368 midGetDisplayDPI = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
"getDisplayDPI",
"()Landroid/util/DisplayMetrics;");
369 midCreateCustomCursor = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
"createCustomCursor",
"([IIIII)I");
370 midSetCustomCursor = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
"setCustomCursor",
"(I)Z");
371 midSetSystemCursor = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
"setSystemCursor",
"(I)Z");
373 midSupportsRelativeMouse = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
"supportsRelativeMouse",
"()Z");
374 midSetRelativeMouseEnabled = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
"setRelativeMouseEnabled",
"(Z)Z");
377 if (!midGetNativeSurface ||
378 !midSetActivityTitle || !midSetWindowStyle || !midSetOrientation || !midGetContext || !midIsTablet || !midIsAndroidTV || !midInputGetInputDeviceIds ||
379 !midSendMessage || !midShowTextInput || !midIsScreenKeyboardShown ||
380 !midClipboardSetText || !midClipboardGetText || !midClipboardHasText ||
381 !midOpenAPKExpansionInputStream || !midGetManifestEnvironmentVariables || !midGetDisplayDPI ||
382 !midCreateCustomCursor || !midSetCustomCursor || !midSetSystemCursor || !midSupportsRelativeMouse || !midSetRelativeMouseEnabled ||
383 !midIsChromebook || !midIsDeXMode || !midManualBackButton) {
384 __android_log_print(ANDROID_LOG_WARN,
"SDL",
"Missing some Java callbacks, do you have the latest version of SDLActivity.java?");
387 fidSeparateMouseAndTouch = (*mEnv)->GetStaticFieldID(mEnv, mActivityClass,
"mSeparateMouseAndTouch",
"Z");
389 if (!fidSeparateMouseAndTouch) {
390 __android_log_print(ANDROID_LOG_WARN,
"SDL",
"Missing some Java static fields, do you have the latest version of SDLActivity.java?");
397 JNIEXPORT
void JNICALL SDL_JAVA_AUDIO_INTERFACE(nativeSetupJNI)(JNIEnv* mEnv, jclass cls)
399 __android_log_print(ANDROID_LOG_VERBOSE,
"SDL",
"AUDIO nativeSetupJNI()");
403 mAudioManagerClass = (jclass)((*mEnv)->NewGlobalRef(mEnv, cls));
405 midAudioOpen = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
406 "audioOpen",
"(IIII)[I");
407 midAudioWriteByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
408 "audioWriteByteBuffer",
"([B)V");
409 midAudioWriteShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
410 "audioWriteShortBuffer",
"([S)V");
411 midAudioWriteFloatBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
412 "audioWriteFloatBuffer",
"([F)V");
413 midAudioClose = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
414 "audioClose",
"()V");
415 midCaptureOpen = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
416 "captureOpen",
"(IIII)[I");
417 midCaptureReadByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
418 "captureReadByteBuffer",
"([BZ)I");
419 midCaptureReadShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
420 "captureReadShortBuffer",
"([SZ)I");
421 midCaptureReadFloatBuffer = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
422 "captureReadFloatBuffer",
"([FZ)I");
423 midCaptureClose = (*mEnv)->GetStaticMethodID(mEnv, mAudioManagerClass,
424 "captureClose",
"()V");
426 if (!midAudioOpen || !midAudioWriteByteBuffer || !midAudioWriteShortBuffer || !midAudioWriteFloatBuffer || !midAudioClose ||
427 !midCaptureOpen || !midCaptureReadByteBuffer || !midCaptureReadShortBuffer || !midCaptureReadFloatBuffer || !midCaptureClose) {
428 __android_log_print(ANDROID_LOG_WARN,
"SDL",
"Missing some Java callbacks, do you have the latest version of SDLAudioManager.java?");
435 JNIEXPORT
void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(JNIEnv* mEnv, jclass cls)
437 __android_log_print(ANDROID_LOG_VERBOSE,
"SDL",
"CONTROLLER nativeSetupJNI()");
441 mControllerManagerClass = (jclass)((*mEnv)->NewGlobalRef(mEnv, cls));
443 midPollInputDevices = (*mEnv)->GetStaticMethodID(mEnv, mControllerManagerClass,
444 "pollInputDevices",
"()V");
445 midPollHapticDevices = (*mEnv)->GetStaticMethodID(mEnv, mControllerManagerClass,
446 "pollHapticDevices",
"()V");
447 midHapticRun = (*mEnv)->GetStaticMethodID(mEnv, mControllerManagerClass,
448 "hapticRun",
"(IFI)V");
449 midHapticStop = (*mEnv)->GetStaticMethodID(mEnv, mControllerManagerClass,
450 "hapticStop",
"(I)V");
452 if (!midPollInputDevices || !midPollHapticDevices || !midHapticRun || !midHapticStop) {
453 __android_log_print(ANDROID_LOG_WARN,
"SDL",
"Missing some Java callbacks, do you have the latest version of SDLControllerManager.java?");
460 typedef int (*SDL_main_func)(
int argc,
char *argv[]);
463 JNIEXPORT
int JNICALL SDL_JAVA_INTERFACE(nativeRunMain)(JNIEnv* env, jclass cls, jstring library, jstring
function, jobject
array)
466 const char *library_file;
467 void *library_handle;
469 __android_log_print(ANDROID_LOG_VERBOSE,
"SDL",
"nativeRunMain()");
471 library_file = (*env)->GetStringUTFChars(env, library,
NULL);
472 library_handle = dlopen(library_file, RTLD_GLOBAL);
473 if (library_handle) {
474 const char *function_name;
477 function_name = (*env)->GetStringUTFChars(env,
function,
NULL);
478 SDL_main = (SDL_main_func)dlsym(library_handle, function_name);
486 len = (*env)->GetArrayLength(env,
array);
493 for (
i = 0;
i <
len; ++
i) {
496 jstring
string = (*env)->GetObjectArrayElement(env,
array,
i);
498 utf = (*env)->GetStringUTFChars(env,
string, 0);
501 (*env)->ReleaseStringUTFChars(env,
string, utf);
503 (*env)->DeleteLocalRef(env,
string);
517 for (
i = 0;
i < argc; ++
i) {
523 __android_log_print(ANDROID_LOG_ERROR,
"SDL",
"nativeRunMain(): Couldn't find function %s in library %s", function_name, library_file);
525 (*env)->ReleaseStringUTFChars(env,
function, function_name);
527 dlclose(library_handle);
530 __android_log_print(ANDROID_LOG_ERROR,
"SDL",
"nativeRunMain(): Couldn't load library %s", library_file);
532 (*env)->ReleaseStringUTFChars(env, library, library_file);
541 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(onNativeDropFile)(
542 JNIEnv* env, jclass jcls,
552 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(onNativeResize)(
553 JNIEnv* env, jclass jcls,
554 jint surfaceWidth, jint surfaceHeight,
555 jint deviceWidth, jint deviceHeight, jint
format, jfloat rate)
560 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(onNativeOrientationChanged)(
561 JNIEnv *env, jclass jcls,
569 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadDown)(
570 JNIEnv* env, jclass jcls,
571 jint device_id, jint keycode)
573 return Android_OnPadDown(device_id, keycode);
577 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativePadUp)(
578 JNIEnv* env, jclass jcls,
579 jint device_id, jint keycode)
581 return Android_OnPadUp(device_id, keycode);
585 JNIEXPORT
void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeJoy)(
586 JNIEnv* env, jclass jcls,
593 JNIEXPORT
void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(onNativeHat)(
594 JNIEnv* env, jclass jcls,
595 jint device_id, jint hat_id, jint
x, jint
y)
597 Android_OnHat(device_id, hat_id,
x,
y);
601 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddJoystick)(
602 JNIEnv* env, jclass jcls,
603 jint device_id, jstring device_name, jstring device_desc,
604 jint vendor_id, jint product_id, jboolean is_accelerometer,
605 jint button_mask, jint naxes, jint nhats, jint nballs)
608 const char *
name = (*env)->GetStringUTFChars(env, device_name,
NULL);
609 const char *desc = (*env)->GetStringUTFChars(env, device_desc,
NULL);
611 retval = Android_AddJoystick(device_id,
name, desc, vendor_id, product_id, is_accelerometer ?
SDL_TRUE :
SDL_FALSE, button_mask, naxes, nhats, nballs);
613 (*env)->ReleaseStringUTFChars(env, device_name,
name);
614 (*env)->ReleaseStringUTFChars(env, device_desc, desc);
619 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveJoystick)(
620 JNIEnv* env, jclass jcls,
623 return Android_RemoveJoystick(device_id);
626 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeAddHaptic)(
627 JNIEnv* env, jclass jcls, jint device_id, jstring device_name)
630 const char *
name = (*env)->GetStringUTFChars(env, device_name,
NULL);
634 (*env)->ReleaseStringUTFChars(env, device_name,
name);
639 JNIEXPORT jint JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeRemoveHaptic)(
640 JNIEnv* env, jclass jcls, jint device_id)
642 return Android_RemoveHaptic(device_id);
647 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(JNIEnv* env, jclass jcls)
661 if(
data->native_window) {
662 ANativeWindow_release(
data->native_window);
673 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)(JNIEnv* env, jclass jcls)
691 SDL_EGL_DestroySurface(
_this,
data->egl_surface);
700 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(onNativeKeyDown)(
701 JNIEnv* env, jclass jcls,
708 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(onNativeKeyUp)(
709 JNIEnv* env, jclass jcls,
716 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(onNativeKeyboardFocusLost)(
717 JNIEnv* env, jclass jcls)
725 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(onNativeTouch)(
726 JNIEnv* env, jclass jcls,
727 jint touch_device_id_in, jint pointer_finger_id_in,
728 jint action, jfloat
x, jfloat
y, jfloat
p)
734 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(onNativeMouse)(
735 JNIEnv* env, jclass jcls,
736 jint
button, jint action, jfloat
x, jfloat
y, jboolean relative)
742 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(onNativeAccel)(
743 JNIEnv* env, jclass jcls,
744 jfloat
x, jfloat
y, jfloat
z)
746 fLastAccelerometer[0] =
x;
747 fLastAccelerometer[1] =
y;
748 fLastAccelerometer[2] =
z;
753 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(onNativeClipboardChanged)(
754 JNIEnv* env, jclass jcls)
760 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(nativeLowMemory)(
761 JNIEnv* env, jclass cls)
767 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(nativeQuit)(
768 JNIEnv* env, jclass cls)
783 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(nativePause)(
784 JNIEnv* env, jclass cls)
786 __android_log_print(ANDROID_LOG_VERBOSE,
"SDL",
"nativePause()");
801 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(nativeResume)(
802 JNIEnv* env, jclass cls)
804 __android_log_print(ANDROID_LOG_VERBOSE,
"SDL",
"nativeResume()");
819 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeCommitText)(
820 JNIEnv* env, jclass cls,
821 jstring
text, jint newCursorPosition)
823 const char *utftext = (*env)->GetStringUTFChars(env,
text,
NULL);
827 (*env)->ReleaseStringUTFChars(env,
text, utftext);
830 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeGenerateScancodeForUnichar)(
831 JNIEnv* env, jclass cls,
838 if (chUnicode < 127) {
860 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeSetComposingText)(
861 JNIEnv* env, jclass cls,
862 jstring
text, jint newCursorPosition)
864 const char *utftext = (*env)->GetStringUTFChars(env,
text,
NULL);
868 (*env)->ReleaseStringUTFChars(env,
text, utftext);
871 JNIEXPORT jstring JNICALL SDL_JAVA_INTERFACE(nativeGetHint)(
872 JNIEnv* env, jclass cls,
875 const char *utfname = (*env)->GetStringUTFChars(env,
name,
NULL);
878 jstring
result = (*env)->NewStringUTF(env, hint);
879 (*env)->ReleaseStringUTFChars(env,
name, utfname);
884 JNIEXPORT
void JNICALL SDL_JAVA_INTERFACE(nativeSetenv)(
885 JNIEnv* env, jclass cls,
888 const char *utfname = (*env)->GetStringUTFChars(env,
name,
NULL);
889 const char *utfvalue = (*env)->GetStringUTFChars(env,
value,
NULL);
893 (*env)->ReleaseStringUTFChars(env,
name, utfname);
894 (*env)->ReleaseStringUTFChars(env,
value, utfvalue);
902 static int s_active = 0;
903 struct LocalReferenceHolder
909 static struct LocalReferenceHolder LocalReferenceHolder_Setup(const char *
func)
911 struct LocalReferenceHolder refholder;
912 refholder.m_env =
NULL;
913 refholder.m_func =
func;
920 static SDL_bool LocalReferenceHolder_Init(
struct LocalReferenceHolder *refholder, JNIEnv *env)
922 const int capacity = 16;
923 if ((*env)->PushLocalFrame(env, capacity) < 0) {
924 SDL_SetError(
"Failed to allocate enough JVM local references");
928 refholder->m_env = env;
932 static void LocalReferenceHolder_Cleanup(
struct LocalReferenceHolder *refholder)
935 SDL_Log(
"Leaving function %s", refholder->m_func);
937 if (refholder->m_env) {
938 JNIEnv* env = refholder->m_env;
939 (*env)->PopLocalFrame(env,
NULL);
944 static SDL_bool LocalReferenceHolder_IsActive(
void)
955 s = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetNativeSurface);
956 anw = ANativeWindow_fromSurface(env,
s);
957 (*env)->DeleteLocalRef(env,
s);
966 jstring jtitle = (jstring)((*mEnv)->NewStringUTF(mEnv, title));
967 (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, midSetActivityTitle, jtitle);
968 (*mEnv)->DeleteLocalRef(mEnv, jtitle);
974 (*mEnv)->CallStaticVoidMethod(mEnv, mActivityClass, midSetWindowStyle, fullscreen ? 1 : 0);
981 jstring jhint = (jstring)((*mEnv)->NewStringUTF(mEnv, (hint ? hint :
"")));
982 (*mEnv)->CallStaticVoidMethod(mEnv, mActivityClass, midSetOrientation,
w,
h, (resizable? 1 : 0), jhint);
983 (*mEnv)->DeleteLocalRef(mEnv, jhint);
992 for (
i = 0;
i < 3; ++
i) {
1002 static void Android_JNI_ThreadDestroyed(
void*
value)
1005 JNIEnv *env = (JNIEnv*)
value;
1007 (*mJavaVM)->DetachCurrentThread(mJavaVM);
1008 pthread_setspecific(mThreadKey,
NULL);
1027 int status = (*mJavaVM)->AttachCurrentThread(mJavaVM, &env,
NULL);
1029 LOGE(
"failed to attach current thread");
1042 pthread_setspecific(mThreadKey, (
void*) env);
1056 static int audioBufferFormat = 0;
1057 static jobject audioBuffer =
NULL;
1058 static void* audioBufferPinned =
NULL;
1059 static int captureBufferFormat = 0;
1060 static jobject captureBuffer =
NULL;
1065 int numBufferFrames;
1066 jobject jbufobj =
NULL;
1068 int *resultElements;
1074 LOGE(
"callback_handler: failed to attach current thread");
1080 audioformat = ENCODING_PCM_8BIT;
1083 audioformat = ENCODING_PCM_16BIT;
1086 audioformat = ENCODING_PCM_FLOAT;
1093 __android_log_print(ANDROID_LOG_VERBOSE,
"SDL",
"SDL audio: opening device for capture");
1096 __android_log_print(ANDROID_LOG_VERBOSE,
"SDL",
"SDL audio: opening device for output");
1101 return SDL_SetError(
"Java-side initialization failed");
1104 if ((*env)->GetArrayLength(env, (jintArray)
result) != 4) {
1105 return SDL_SetError(
"Unexpected results from Java, expected 4, got %d", (*env)->GetArrayLength(env, (jintArray)
result));
1108 resultElements = (*env)->GetIntArrayElements(env, (jintArray)
result, &isCopy);
1110 audioformat = resultElements[1];
1111 switch (audioformat) {
1112 case ENCODING_PCM_8BIT:
1115 case ENCODING_PCM_16BIT:
1118 case ENCODING_PCM_FLOAT:
1122 return SDL_SetError(
"Unexpected audio format from Java: %d\n", audioformat);
1126 (*env)->ReleaseIntArrayElements(env, (jintArray)
result, resultElements, JNI_ABORT);
1127 (*env)->DeleteLocalRef(env,
result);
1131 switch (audioformat) {
1132 case ENCODING_PCM_8BIT:
1135 if (audioBufferLocal) {
1136 jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
1137 (*env)->DeleteLocalRef(env, audioBufferLocal);
1141 case ENCODING_PCM_16BIT:
1144 if (audioBufferLocal) {
1145 jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
1146 (*env)->DeleteLocalRef(env, audioBufferLocal);
1150 case ENCODING_PCM_FLOAT:
1153 if (audioBufferLocal) {
1154 jbufobj = (*env)->NewGlobalRef(env, audioBufferLocal);
1155 (*env)->DeleteLocalRef(env, audioBufferLocal);
1160 return SDL_SetError(
"Unexpected audio format from Java: %d\n", audioformat);
1163 if (jbufobj ==
NULL) {
1164 __android_log_print(ANDROID_LOG_WARN,
"SDL",
"SDL audio: could not allocate an audio buffer");
1169 captureBufferFormat = audioformat;
1170 captureBuffer = jbufobj;
1172 audioBufferFormat = audioformat;
1173 audioBuffer = jbufobj;
1175 numBufferFrames = (*env)->GetArrayLength(env, (jarray)jbufobj);
1180 switch (audioformat) {
1181 case ENCODING_PCM_8BIT:
1182 audioBufferPinned = (*env)->GetByteArrayElements(env, (jbyteArray)audioBuffer, &isCopy);
1184 case ENCODING_PCM_16BIT:
1185 audioBufferPinned = (*env)->GetShortArrayElements(env, (jshortArray)audioBuffer, &isCopy);
1187 case ENCODING_PCM_FLOAT:
1188 audioBufferPinned = (*env)->GetFloatArrayElements(env, (jfloatArray)audioBuffer, &isCopy);
1191 return SDL_SetError(
"Unexpected audio format from Java: %d\n", audioformat);
1201 jobject jDisplayObj = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetDisplayDPI);
1202 jclass jDisplayClass = (*env)->GetObjectClass(env, jDisplayObj);
1204 jfieldID fidXdpi = (*env)->GetFieldID(env, jDisplayClass,
"xdpi",
"F");
1205 jfieldID fidYdpi = (*env)->GetFieldID(env, jDisplayClass,
"ydpi",
"F");
1206 jfieldID fidDdpi = (*env)->GetFieldID(env, jDisplayClass,
"densityDpi",
"I");
1208 float nativeXdpi = (*env)->GetFloatField(env, jDisplayObj, fidXdpi);
1209 float nativeYdpi = (*env)->GetFloatField(env, jDisplayObj, fidYdpi);
1210 int nativeDdpi = (*env)->GetIntField(env, jDisplayObj, fidDdpi);
1213 (*env)->DeleteLocalRef(env, jDisplayObj);
1214 (*env)->DeleteLocalRef(env, jDisplayClass);
1217 *ddpi = (float)nativeDdpi;
1231 return audioBufferPinned;
1238 switch (audioBufferFormat) {
1239 case ENCODING_PCM_8BIT:
1240 (*mAudioEnv)->ReleaseByteArrayElements(mAudioEnv, (jbyteArray)audioBuffer, (jbyte *)audioBufferPinned, JNI_COMMIT);
1241 (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mAudioManagerClass, midAudioWriteByteBuffer, (jbyteArray)audioBuffer);
1243 case ENCODING_PCM_16BIT:
1244 (*mAudioEnv)->ReleaseShortArrayElements(mAudioEnv, (jshortArray)audioBuffer, (jshort *)audioBufferPinned, JNI_COMMIT);
1245 (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mAudioManagerClass, midAudioWriteShortBuffer, (jshortArray)audioBuffer);
1247 case ENCODING_PCM_FLOAT:
1248 (*mAudioEnv)->ReleaseFloatArrayElements(mAudioEnv, (jfloatArray)audioBuffer, (jfloat *)audioBufferPinned, JNI_COMMIT);
1249 (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mAudioManagerClass, midAudioWriteFloatBuffer, (jfloatArray)audioBuffer);
1252 __android_log_print(ANDROID_LOG_WARN,
"SDL",
"SDL audio: unhandled audio buffer format");
1262 jboolean isCopy = JNI_FALSE;
1265 switch (captureBufferFormat) {
1266 case ENCODING_PCM_8BIT:
1267 SDL_assert((*env)->GetArrayLength(env, (jshortArray)captureBuffer) == buflen);
1268 br = (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_TRUE);
1270 jbyte *ptr = (*env)->GetByteArrayElements(env, (jbyteArray)captureBuffer, &isCopy);
1272 (*env)->ReleaseByteArrayElements(env, (jbyteArray)captureBuffer, (jbyte *)ptr, JNI_ABORT);
1275 case ENCODING_PCM_16BIT:
1276 SDL_assert((*env)->GetArrayLength(env, (jshortArray)captureBuffer) == (buflen /
sizeof(
Sint16)));
1277 br = (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_TRUE);
1279 jshort *ptr = (*env)->GetShortArrayElements(env, (jshortArray)captureBuffer, &isCopy);
1282 (*env)->ReleaseShortArrayElements(env, (jshortArray)captureBuffer, (jshort *)ptr, JNI_ABORT);
1285 case ENCODING_PCM_FLOAT:
1286 SDL_assert((*env)->GetArrayLength(env, (jfloatArray)captureBuffer) == (buflen /
sizeof(
float)));
1287 br = (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadFloatBuffer, (jfloatArray)captureBuffer, JNI_TRUE);
1289 jfloat *ptr = (*env)->GetFloatArrayElements(env, (jfloatArray)captureBuffer, &isCopy);
1290 br *=
sizeof(float);
1292 (*env)->ReleaseFloatArrayElements(env, (jfloatArray)captureBuffer, (jfloat *)ptr, JNI_ABORT);
1296 __android_log_print(ANDROID_LOG_WARN,
"SDL",
"SDL audio: unhandled capture buffer format");
1306 switch (captureBufferFormat) {
1307 case ENCODING_PCM_8BIT:
1309 const jint
len = (*env)->GetArrayLength(env, (jbyteArray)captureBuffer);
1310 while ((*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_FALSE) ==
len) { }
1313 case ENCODING_PCM_16BIT:
1315 const jint
len = (*env)->GetArrayLength(env, (jshortArray)captureBuffer);
1316 while ((*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_FALSE) ==
len) { }
1319 case ENCODING_PCM_FLOAT:
1321 const jint
len = (*env)->GetArrayLength(env, (jfloatArray)captureBuffer);
1322 while ((*env)->CallStaticIntMethod(env, mActivityClass, midCaptureReadFloatBuffer, (jfloatArray)captureBuffer, JNI_FALSE) ==
len) { }
1326 __android_log_print(ANDROID_LOG_WARN,
"SDL",
"SDL audio: flushing unhandled capture buffer format");
1330 switch (captureBufferFormat) {
1331 case ENCODING_PCM_8BIT:
1332 (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadByteBuffer, (jbyteArray)captureBuffer, JNI_FALSE);
1334 case ENCODING_PCM_16BIT:
1335 (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadShortBuffer, (jshortArray)captureBuffer, JNI_FALSE);
1337 case ENCODING_PCM_FLOAT:
1338 (*env)->CallStaticIntMethod(env, mAudioManagerClass, midCaptureReadFloatBuffer, (jfloatArray)captureBuffer, JNI_FALSE);
1341 __android_log_print(ANDROID_LOG_WARN,
"SDL",
"SDL audio: flushing unhandled capture buffer format");
1352 (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midCaptureClose);
1353 if (captureBuffer) {
1354 (*env)->DeleteGlobalRef(env, captureBuffer);
1355 captureBuffer =
NULL;
1358 (*env)->CallStaticVoidMethod(env, mAudioManagerClass, midAudioClose);
1360 (*env)->DeleteGlobalRef(env, audioBuffer);
1362 audioBufferPinned =
NULL;
1372 jthrowable exception;
1376 exception = (*mEnv)->ExceptionOccurred(mEnv);
1377 if (exception !=
NULL) {
1381 (*mEnv)->ExceptionClear(mEnv);
1384 jclass exceptionClass = (*mEnv)->GetObjectClass(mEnv, exception);
1385 jclass classClass = (*mEnv)->FindClass(mEnv,
"java/lang/Class");
1386 jstring exceptionName;
1387 const char* exceptionNameUTF8;
1388 jstring exceptionMessage;
1390 mid = (*mEnv)->GetMethodID(mEnv, classClass,
"getName",
"()Ljava/lang/String;");
1391 exceptionName = (jstring)(*mEnv)->CallObjectMethod(mEnv, exceptionClass, mid);
1392 exceptionNameUTF8 = (*mEnv)->GetStringUTFChars(mEnv, exceptionName, 0);
1394 mid = (*mEnv)->GetMethodID(mEnv, exceptionClass,
"getMessage",
"()Ljava/lang/String;");
1395 exceptionMessage = (jstring)(*mEnv)->CallObjectMethod(mEnv, exception, mid);
1397 if (exceptionMessage !=
NULL) {
1398 const char* exceptionMessageUTF8 = (*mEnv)->GetStringUTFChars(mEnv, exceptionMessage, 0);
1399 SDL_SetError(
"%s: %s", exceptionNameUTF8, exceptionMessageUTF8);
1400 (*mEnv)->ReleaseStringUTFChars(mEnv, exceptionMessage, exceptionMessageUTF8);
1405 (*mEnv)->ReleaseStringUTFChars(mEnv, exceptionName, exceptionNameUTF8);
1414 static int Internal_Android_JNI_FileOpen(
SDL_RWops*
ctx)
1416 struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
1422 jobject assetManager;
1423 jobject inputStream;
1425 jobject readableByteChannel;
1426 jstring fileNameJString;
1429 jfieldID descriptor;
1432 if (!LocalReferenceHolder_Init(&refs, mEnv)) {
1436 fileNameJString = (jstring)
ctx->hidden.androidio.fileNameRef;
1437 ctx->hidden.androidio.position = 0;
1440 context = (*mEnv)->CallStaticObjectMethod(mEnv, mActivityClass, midGetContext);
1443 mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv,
context),
1444 "getAssets",
"()Landroid/content/res/AssetManager;");
1445 assetManager = (*mEnv)->CallObjectMethod(mEnv,
context, mid);
1450 mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, assetManager),
"openFd",
"(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;");
1451 inputStream = (*mEnv)->CallObjectMethod(mEnv, assetManager, mid, fileNameJString);
1452 if (Android_JNI_ExceptionOccurred(
SDL_TRUE)) {
1456 mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
"getStartOffset",
"()J");
1457 ctx->hidden.androidio.offset = (*mEnv)->CallLongMethod(mEnv, inputStream, mid);
1458 if (Android_JNI_ExceptionOccurred(
SDL_TRUE)) {
1462 mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
"getDeclaredLength",
"()J");
1463 ctx->hidden.androidio.size = (*mEnv)->CallLongMethod(mEnv, inputStream, mid);
1464 if (Android_JNI_ExceptionOccurred(
SDL_TRUE)) {
1468 mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
"getFileDescriptor",
"()Ljava/io/FileDescriptor;");
1469 fd = (*mEnv)->CallObjectMethod(mEnv, inputStream, mid);
1470 fdCls = (*mEnv)->GetObjectClass(mEnv,
fd);
1471 descriptor = (*mEnv)->GetFieldID(mEnv, fdCls,
"descriptor",
"I");
1472 ctx->hidden.androidio.fd = (*mEnv)->GetIntField(mEnv,
fd, descriptor);
1473 ctx->hidden.androidio.assetFileDescriptorRef = (*mEnv)->NewGlobalRef(mEnv, inputStream);
1476 lseek(
ctx->hidden.androidio.fd, (off_t)
ctx->hidden.androidio.offset, SEEK_SET);
1484 ctx->hidden.androidio.assetFileDescriptorRef =
NULL;
1487 mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, assetManager),
1488 "open",
"(Ljava/lang/String;I)Ljava/io/InputStream;");
1489 inputStream = (*mEnv)->CallObjectMethod(mEnv, assetManager, mid, fileNameJString, 1 );
1490 if (Android_JNI_ExceptionOccurred(
SDL_FALSE)) {
1492 inputStream = (*mEnv)->CallStaticObjectMethod(mEnv, mActivityClass, midOpenAPKExpansionInputStream, fileNameJString);
1497 if (Android_JNI_ExceptionOccurred(
SDL_FALSE) || !inputStream) {
1502 ctx->hidden.androidio.inputStreamRef = (*mEnv)->NewGlobalRef(mEnv, inputStream);
1512 mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
1513 "available",
"()I");
1514 ctx->hidden.androidio.size = (long)(*mEnv)->CallIntMethod(mEnv, inputStream, mid);
1515 if (Android_JNI_ExceptionOccurred(
SDL_FALSE)) {
1520 channels = (*mEnv)->FindClass(mEnv,
"java/nio/channels/Channels");
1521 mid = (*mEnv)->GetStaticMethodID(mEnv, channels,
1523 "(Ljava/io/InputStream;)Ljava/nio/channels/ReadableByteChannel;");
1524 readableByteChannel = (*mEnv)->CallStaticObjectMethod(
1525 mEnv, channels, mid, inputStream);
1526 if (Android_JNI_ExceptionOccurred(
SDL_FALSE)) {
1530 ctx->hidden.androidio.readableByteChannelRef =
1531 (*mEnv)->NewGlobalRef(mEnv, readableByteChannel);
1534 mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, readableByteChannel),
1535 "read",
"(Ljava/nio/ByteBuffer;)I");
1536 ctx->hidden.androidio.readMethod = mid;
1543 (*mEnv)->DeleteGlobalRef(mEnv, (jobject)
ctx->hidden.androidio.fileNameRef);
1545 if(
ctx->hidden.androidio.inputStreamRef !=
NULL) {
1546 (*mEnv)->DeleteGlobalRef(mEnv, (jobject)
ctx->hidden.androidio.inputStreamRef);
1549 if(
ctx->hidden.androidio.readableByteChannelRef !=
NULL) {
1550 (*mEnv)->DeleteGlobalRef(mEnv, (jobject)
ctx->hidden.androidio.readableByteChannelRef);
1553 if(
ctx->hidden.androidio.assetFileDescriptorRef !=
NULL) {
1554 (*mEnv)->DeleteGlobalRef(mEnv, (jobject)
ctx->hidden.androidio.assetFileDescriptorRef);
1559 LocalReferenceHolder_Cleanup(&refs);
1564 const char* fileName,
const char*
mode)
1566 struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
1569 jstring fileNameJString;
1571 if (!LocalReferenceHolder_Init(&refs, mEnv)) {
1572 LocalReferenceHolder_Cleanup(&refs);
1577 LocalReferenceHolder_Cleanup(&refs);
1581 fileNameJString = (*mEnv)->NewStringUTF(mEnv, fileName);
1582 ctx->hidden.androidio.fileNameRef = (*mEnv)->NewGlobalRef(mEnv, fileNameJString);
1583 ctx->hidden.androidio.inputStreamRef =
NULL;
1584 ctx->hidden.androidio.readableByteChannelRef =
NULL;
1585 ctx->hidden.androidio.readMethod =
NULL;
1586 ctx->hidden.androidio.assetFileDescriptorRef =
NULL;
1588 retval = Internal_Android_JNI_FileOpen(
ctx);
1589 LocalReferenceHolder_Cleanup(&refs);
1594 size_t size,
size_t maxnum)
1596 struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
1598 if (
ctx->hidden.androidio.assetFileDescriptorRef) {
1599 size_t bytesMax =
size * maxnum;
1601 if (
ctx->hidden.androidio.size != -1 &&
ctx->hidden.androidio.position + bytesMax >
ctx->hidden.androidio.size) {
1602 bytesMax =
ctx->hidden.androidio.size -
ctx->hidden.androidio.position;
1606 ctx->hidden.androidio.position +=
result;
1607 LocalReferenceHolder_Cleanup(&refs);
1610 LocalReferenceHolder_Cleanup(&refs);
1613 jlong bytesRemaining = (jlong) (
size * maxnum);
1614 jlong bytesMax = (jlong) (
ctx->hidden.androidio.size -
ctx->hidden.androidio.position);
1617 jobject readableByteChannel;
1618 jmethodID readMethod;
1622 if (bytesRemaining > bytesMax) bytesRemaining = bytesMax;
1625 if (!LocalReferenceHolder_Init(&refs, mEnv)) {
1626 LocalReferenceHolder_Cleanup(&refs);
1630 readableByteChannel = (jobject)
ctx->hidden.androidio.readableByteChannelRef;
1631 readMethod = (jmethodID)
ctx->hidden.androidio.readMethod;
1632 byteBuffer = (*mEnv)->NewDirectByteBuffer(mEnv,
buffer, bytesRemaining);
1634 while (bytesRemaining > 0) {
1636 int result = (*mEnv)->CallIntMethod(mEnv, readableByteChannel, readMethod, byteBuffer);
1638 if (Android_JNI_ExceptionOccurred(
SDL_FALSE)) {
1639 LocalReferenceHolder_Cleanup(&refs);
1647 bytesRemaining -=
result;
1649 ctx->hidden.androidio.position +=
result;
1651 LocalReferenceHolder_Cleanup(&refs);
1652 return bytesRead /
size;
1659 SDL_SetError(
"Cannot write to Android package filesystem");
1665 struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
1670 if (!LocalReferenceHolder_Init(&refs, mEnv)) {
1671 LocalReferenceHolder_Cleanup(&refs);
1672 return SDL_SetError(
"Failed to allocate enough JVM local references");
1677 (*mEnv)->DeleteGlobalRef(mEnv, (jobject)
ctx->hidden.androidio.fileNameRef);
1680 if (
ctx->hidden.androidio.assetFileDescriptorRef) {
1681 jobject inputStream = (jobject)
ctx->hidden.androidio.assetFileDescriptorRef;
1682 jmethodID mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
1684 (*mEnv)->CallVoidMethod(mEnv, inputStream, mid);
1685 (*mEnv)->DeleteGlobalRef(mEnv, (jobject)
ctx->hidden.androidio.assetFileDescriptorRef);
1686 if (Android_JNI_ExceptionOccurred(
SDL_FALSE)) {
1691 jobject inputStream = (jobject)
ctx->hidden.androidio.inputStreamRef;
1694 jmethodID mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
1696 (*mEnv)->CallVoidMethod(mEnv, inputStream, mid);
1697 (*mEnv)->DeleteGlobalRef(mEnv, (jobject)
ctx->hidden.androidio.inputStreamRef);
1698 (*mEnv)->DeleteGlobalRef(mEnv, (jobject)
ctx->hidden.androidio.readableByteChannelRef);
1699 if (Android_JNI_ExceptionOccurred(
SDL_FALSE)) {
1709 LocalReferenceHolder_Cleanup(&refs);
1716 return ctx->hidden.androidio.size;
1721 if (
ctx->hidden.androidio.assetFileDescriptorRef) {
1725 if (
ctx->hidden.androidio.size != -1 &&
offset >
ctx->hidden.androidio.size)
offset =
ctx->hidden.androidio.size;
1729 offset +=
ctx->hidden.androidio.position;
1730 if (
ctx->hidden.androidio.size != -1 &&
offset >
ctx->hidden.androidio.size)
offset =
ctx->hidden.androidio.size;
1741 ret = lseek(
ctx->hidden.androidio.fd, (off_t)
offset, SEEK_SET);
1742 if (ret == -1)
return -1;
1743 ctx->hidden.androidio.position = ret -
ctx->hidden.androidio.offset;
1753 newPosition =
ctx->hidden.androidio.position +
offset;
1756 newPosition =
ctx->hidden.androidio.size +
offset;
1763 if (newPosition < 0) {
1766 if (newPosition >
ctx->hidden.androidio.size) {
1767 newPosition =
ctx->hidden.androidio.size;
1770 movement = newPosition -
ctx->hidden.androidio.position;
1772 unsigned char buffer[4096];
1775 while (movement > 0) {
1778 if (amount > movement) {
1790 }
else if (movement < 0) {
1794 Internal_Android_JNI_FileOpen(
ctx);
1799 return ctx->hidden.androidio.position;
1805 return Internal_Android_JNI_FileClose(
ctx,
SDL_TRUE);
1811 jstring
string = (*env)->NewStringUTF(env,
text);
1812 (*env)->CallStaticVoidMethod(env, mActivityClass, midClipboardSetText,
string);
1813 (*env)->DeleteLocalRef(env,
string);
1823 string = (*env)->CallStaticObjectMethod(env, mActivityClass, midClipboardGetText);
1825 const char* utf = (*env)->GetStringUTFChars(env,
string, 0);
1828 (*env)->ReleaseStringUTFChars(env,
string, utf);
1830 (*env)->DeleteLocalRef(env,
string);
1839 jboolean
retval = (*env)->CallStaticBooleanMethod(env, mActivityClass, midClipboardHasText);
1849 struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
1861 if (!LocalReferenceHolder_Init(&refs, env)) {
1862 LocalReferenceHolder_Cleanup(&refs);
1868 context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
1870 action = (*env)->NewStringUTF(env,
"android.intent.action.BATTERY_CHANGED");
1872 cls = (*env)->FindClass(env,
"android/content/IntentFilter");
1874 mid = (*env)->GetMethodID(env, cls,
"<init>",
"(Ljava/lang/String;)V");
1875 filter = (*env)->NewObject(env, cls, mid, action);
1877 (*env)->DeleteLocalRef(env, action);
1879 mid = (*env)->GetMethodID(env, mActivityClass,
"registerReceiver",
"(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;");
1882 (*env)->DeleteLocalRef(env,
filter);
1884 cls = (*env)->GetObjectClass(env, intent);
1886 imid = (*env)->GetMethodID(env, cls,
"getIntExtra",
"(Ljava/lang/String;I)I");
1889 #define GET_INT_EXTRA(var, key) \
1891 iname = (*env)->NewStringUTF(env, key); \
1892 var = (*env)->CallIntMethod(env, intent, imid, iname, -1); \
1893 (*env)->DeleteLocalRef(env, iname);
1895 bmid = (*env)->GetMethodID(env, cls,
"getBooleanExtra",
"(Ljava/lang/String;Z)Z");
1898 #define GET_BOOL_EXTRA(var, key) \
1900 bname = (*env)->NewStringUTF(env, key); \
1901 var = (*env)->CallBooleanMethod(env, intent, bmid, bname, JNI_FALSE); \
1902 (*env)->DeleteLocalRef(env, bname);
1906 GET_INT_EXTRA(plug,
"plugged")
1908 LocalReferenceHolder_Cleanup(&refs);
1913 *plugged = (0 < plug) ? 1 : 0;
1918 GET_INT_EXTRA(status,
"status")
1920 LocalReferenceHolder_Cleanup(&refs);
1924 *charged = (status == 5) ? 1 : 0;
1928 GET_BOOL_EXTRA(present,
"present")
1929 *battery = present ? 1 : 0;
1942 GET_INT_EXTRA(level_temp,
"level")
1947 GET_INT_EXTRA(scale_temp,
"scale")
1952 LocalReferenceHolder_Cleanup(&refs);
1958 (*env)->DeleteLocalRef(env, intent);
1960 LocalReferenceHolder_Cleanup(&refs);
1968 jintArray
array = (jintArray) (*env)->CallStaticObjectMethod(env, mActivityClass, midInputGetInputDeviceIds,
sources);
1972 number = (int) (*env)->GetArrayLength(env,
array);
1974 jint* elements = (*env)->GetIntArrayElements(env,
array,
NULL);
1978 for (
i = 0;
i < number; ++
i) {
1979 (*ids)[
i] = elements[
i];
1981 (*env)->ReleaseIntArrayElements(env,
array, elements, JNI_ABORT);
1984 (*env)->DeleteLocalRef(env,
array);
1993 (*env)->SetStaticBooleanField(env, mActivityClass, fidSeparateMouseAndTouch, new_value ? JNI_TRUE : JNI_FALSE);
1999 (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midPollInputDevices);
2005 (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midPollHapticDevices);
2011 (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midHapticRun, device_id, intensity,
length);
2017 (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midHapticStop, device_id);
2021 #define COMMAND_SET_KEEP_SCREEN_ON 5
2028 success = (*env)->CallStaticBooleanMethod(env, mActivityClass, midSendMessage, command,
param);
2029 return success ? 0 : -1;
2040 (*env)->CallStaticBooleanMethod(env, mActivityClass, midShowTextInput,
2050 const int COMMAND_TEXTEDIT_HIDE = 3;
2057 jboolean is_shown = 0;
2058 is_shown = (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, midIsScreenKeyboardShown);
2071 jintArray button_flags;
2072 jintArray button_ids;
2073 jobjectArray button_texts;
2083 clazz = (*env)->FindClass(env,
"java/lang/String");
2085 title = (*env)->NewStringUTF(env, messageboxdata->
title);
2088 button_flags = (*env)->NewIntArray(env, messageboxdata->
numbuttons);
2089 button_ids = (*env)->NewIntArray(env, messageboxdata->
numbuttons);
2090 button_texts = (*env)->NewObjectArray(env, messageboxdata->
numbuttons,
2094 (*env)->SetIntArrayRegion(env, button_flags,
i, 1, &temp);
2096 (*env)->SetIntArrayRegion(env, button_ids,
i, 1, &temp);
2098 (*env)->SetObjectArrayElement(env, button_texts,
i,
text);
2099 (*env)->DeleteLocalRef(env,
text);
2105 temp = (0xFF << 24) |
2109 (*env)->SetIntArrayRegion(env,
colors,
i, 1, &temp);
2115 (*env)->DeleteLocalRef(env, clazz);
2118 context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
2120 clazz = (*env)->GetObjectClass(env,
context);
2122 mid = (*env)->GetMethodID(env, clazz,
2123 "messageboxShowMessageBox",
"(ILjava/lang/String;Ljava/lang/String;[I[I[Ljava/lang/String;[I)I");
2124 *buttonid = (*env)->CallIntMethod(env,
context, mid,
2125 messageboxdata->
flags,
2133 (*env)->DeleteLocalRef(env,
context);
2134 (*env)->DeleteLocalRef(env, clazz);
2138 (*env)->DeleteLocalRef(env, title);
2139 (*env)->DeleteLocalRef(env,
message);
2140 (*env)->DeleteLocalRef(env, button_flags);
2141 (*env)->DeleteLocalRef(env, button_ids);
2142 (*env)->DeleteLocalRef(env, button_texts);
2143 (*env)->DeleteLocalRef(env,
colors);
2151 // Functions exposed to SDL applications in SDL_system.h
2170 return (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
2176 return (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsTablet);
2182 return (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsAndroidTV);
2188 return (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsChromebook);
2194 return (*env)->CallStaticBooleanMethod(env, mActivityClass, midIsDeXMode);
2200 return (*env)->CallStaticVoidMethod(env, mActivityClass, midManualBackButton);
2205 static char *s_AndroidInternalFilesPath =
NULL;
2207 if (!s_AndroidInternalFilesPath) {
2208 struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
2216 if (!LocalReferenceHolder_Init(&refs, env)) {
2217 LocalReferenceHolder_Cleanup(&refs);
2222 context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
2225 LocalReferenceHolder_Cleanup(&refs);
2230 mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env,
context),
2231 "getFilesDir",
"()Ljava/io/File;");
2232 fileObject = (*env)->CallObjectMethod(env,
context, mid);
2235 LocalReferenceHolder_Cleanup(&refs);
2240 mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, fileObject),
2241 "getCanonicalPath",
"()Ljava/lang/String;");
2242 pathString = (jstring)(*env)->CallObjectMethod(env, fileObject, mid);
2243 if (Android_JNI_ExceptionOccurred(
SDL_FALSE)) {
2244 LocalReferenceHolder_Cleanup(&refs);
2252 LocalReferenceHolder_Cleanup(&refs);
2254 return s_AndroidInternalFilesPath;
2259 struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
2262 jstring stateString;
2267 if (!LocalReferenceHolder_Init(&refs, env)) {
2268 LocalReferenceHolder_Cleanup(&refs);
2272 cls = (*env)->FindClass(env,
"android/os/Environment");
2273 mid = (*env)->GetStaticMethodID(env, cls,
2274 "getExternalStorageState",
"()Ljava/lang/String;");
2275 stateString = (jstring)(*env)->CallStaticObjectMethod(env, cls, mid);
2277 state = (*env)->GetStringUTFChars(env, stateString,
NULL);
2280 __android_log_print(ANDROID_LOG_INFO,
"SDL",
"external storage state: %s",
state);
2283 stateFlags = SDL_ANDROID_EXTERNAL_STORAGE_READ |
2284 SDL_ANDROID_EXTERNAL_STORAGE_WRITE;
2286 stateFlags = SDL_ANDROID_EXTERNAL_STORAGE_READ;
2290 (*env)->ReleaseStringUTFChars(env, stateString,
state);
2292 LocalReferenceHolder_Cleanup(&refs);
2298 static char *s_AndroidExternalFilesPath =
NULL;
2300 if (!s_AndroidExternalFilesPath) {
2301 struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
2309 if (!LocalReferenceHolder_Init(&refs, env)) {
2310 LocalReferenceHolder_Cleanup(&refs);
2315 context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
2318 mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env,
context),
2319 "getExternalFilesDir",
"(Ljava/lang/String;)Ljava/io/File;");
2320 fileObject = (*env)->CallObjectMethod(env,
context, mid,
NULL);
2323 LocalReferenceHolder_Cleanup(&refs);
2328 mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, fileObject),
2329 "getAbsolutePath",
"()Ljava/lang/String;");
2330 pathString = (jstring)(*env)->CallObjectMethod(env, fileObject, mid);
2336 LocalReferenceHolder_Cleanup(&refs);
2338 return s_AndroidExternalFilesPath;
2343 if (!mActivityClass || !midGetManifestEnvironmentVariables) {
2344 __android_log_print(ANDROID_LOG_WARN,
"SDL",
"Request to get environment variables before JNI is ready");
2348 if (!bHasEnvironmentVariables) {
2350 SDL_bool ret = (*env)->CallStaticBooleanMethod(env, mActivityClass, midGetManifestEnvironmentVariables);
2352 bHasEnvironmentVariables =
SDL_TRUE;
2360 int custom_cursor = 0;
2365 custom_cursor = (*mEnv)->CallStaticIntMethod(mEnv, mActivityClass, midCreateCustomCursor,
pixels,
surface->w,
surface->h,
hot_x, hot_y);
2366 (*mEnv)->DeleteLocalRef(mEnv,
pixels);
2370 return custom_cursor;
2377 return (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, midSetCustomCursor, cursorID);
2383 return (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, midSetSystemCursor, cursorID);
2389 return (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, midSupportsRelativeMouse);
2395 return (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, midSetRelativeMouseEnabled, (
enabled == 1));