21 #include "../../SDL_internal.h" 33 #include "../../events/SDL_events_c.h" 34 #include "../../video/android/SDL_androidkeyboard.h" 35 #include "../../video/android/SDL_androidmouse.h" 36 #include "../../video/android/SDL_androidtouch.h" 37 #include "../../video/android/SDL_androidvideo.h" 38 #include "../../video/android/SDL_androidwindow.h" 39 #include "../../joystick/android/SDL_sysjoystick_c.h" 41 #include <android/log.h> 43 #include <sys/types.h> 45 #define LOG_TAG "SDL_android" 48 #define LOGI(...) do {} while (0) 49 #define LOGE(...) do {} while (0) 54 static void Android_JNI_ThreadDestroyed(
void*);
60 #include <android/log.h> 66 static pthread_key_t mThreadKey;
67 static JavaVM* mJavaVM;
70 static jclass mActivityClass;
73 static jmethodID midGetNativeSurface;
74 static jmethodID midAudioInit;
75 static jmethodID midAudioWriteShortBuffer;
76 static jmethodID midAudioWriteByteBuffer;
77 static jmethodID midAudioQuit;
78 static jmethodID midPollInputDevices;
81 static float fLastAccelerometer[3];
89 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm,
void* reserved)
93 LOGI(
"JNI_OnLoad called");
94 if ((*mJavaVM)->GetEnv(mJavaVM, (
void**) &env, JNI_VERSION_1_4) != JNI_OK) {
95 LOGE(
"Failed to get the environment using GetEnv()");
102 if (pthread_key_create(&mThreadKey, Android_JNI_ThreadDestroyed) != 0) {
103 __android_log_print(ANDROID_LOG_ERROR,
"SDL",
"Error initializing pthread key");
107 return JNI_VERSION_1_4;
111 JNIEXPORT
void JNICALL SDL_Android_Init(JNIEnv* mEnv, jclass cls)
113 __android_log_print(ANDROID_LOG_INFO,
"SDL",
"SDL_Android_Init()");
117 mActivityClass = (jclass)((*mEnv)->NewGlobalRef(mEnv, cls));
119 midGetNativeSurface = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
120 "getNativeSurface",
"()Landroid/view/Surface;");
121 midAudioInit = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
122 "audioInit",
"(IZZI)I");
123 midAudioWriteShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
124 "audioWriteShortBuffer",
"([S)V");
125 midAudioWriteByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
126 "audioWriteByteBuffer",
"([B)V");
127 midAudioQuit = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
129 midPollInputDevices = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
130 "pollInputDevices",
"()V");
134 if (!midGetNativeSurface || !midAudioInit ||
135 !midAudioWriteShortBuffer || !midAudioWriteByteBuffer || !midAudioQuit || !midPollInputDevices) {
136 __android_log_print(ANDROID_LOG_WARN,
"SDL",
"SDL: Couldn't locate Java callbacks, check that they're named and typed correctly");
138 __android_log_print(ANDROID_LOG_INFO,
"SDL",
"SDL_Android_Init() finished!");
142 void Java_org_libsdl_app_SDLActivity_onNativeDropFile(
143 JNIEnv* env, jclass jcls,
146 const char *
path = (*env)->GetStringUTFChars(env, filename,
NULL);
148 (*env)->ReleaseStringUTFChars(env, filename, path);
152 JNIEXPORT
void JNICALL Java_org_libsdl_app_SDLActivity_onNativeResize(
153 JNIEnv* env, jclass jcls,
160 JNIEXPORT jint JNICALL Java_org_libsdl_app_SDLActivity_onNativePadDown(
161 JNIEnv* env, jclass jcls,
162 jint device_id, jint keycode)
164 return Android_OnPadDown(device_id, keycode);
168 JNIEXPORT jint JNICALL Java_org_libsdl_app_SDLActivity_onNativePadUp(
169 JNIEnv* env, jclass jcls,
170 jint device_id, jint keycode)
172 return Android_OnPadUp(device_id, keycode);
176 JNIEXPORT
void JNICALL Java_org_libsdl_app_SDLActivity_onNativeJoy(
177 JNIEnv* env, jclass jcls,
180 Android_OnJoy(device_id, axis, value);
184 JNIEXPORT
void JNICALL Java_org_libsdl_app_SDLActivity_onNativeHat(
185 JNIEnv* env, jclass jcls,
186 jint device_id, jint hat_id, jint
x, jint
y)
188 Android_OnHat(device_id, hat_id, x, y);
192 JNIEXPORT jint JNICALL Java_org_libsdl_app_SDLActivity_nativeAddJoystick(
193 JNIEnv* env, jclass jcls,
194 jint device_id, jstring device_name, jint is_accelerometer,
195 jint nbuttons, jint naxes, jint nhats, jint nballs)
198 const char *
name = (*env)->GetStringUTFChars(env, device_name,
NULL);
200 retval = Android_AddJoystick(device_id, name, (
SDL_bool) is_accelerometer, nbuttons, naxes, nhats, nballs);
202 (*env)->ReleaseStringUTFChars(env, device_name, name);
207 JNIEXPORT jint JNICALL Java_org_libsdl_app_SDLActivity_nativeRemoveJoystick(
208 JNIEnv* env, jclass jcls, jint device_id)
210 return Android_RemoveJoystick(device_id);
215 JNIEXPORT
void JNICALL Java_org_libsdl_app_SDLActivity_onNativeSurfaceChanged(JNIEnv* env, jclass jcls)
241 JNIEXPORT
void JNICALL Java_org_libsdl_app_SDLActivity_onNativeSurfaceDestroyed(JNIEnv* env, jclass jcls)
258 SDL_EGL_MakeCurrent(_this,
NULL,
NULL);
268 JNIEXPORT
void JNICALL Java_org_libsdl_app_SDLActivity_onNativeKeyDown(
269 JNIEnv* env, jclass jcls, jint keycode)
275 JNIEXPORT
void JNICALL Java_org_libsdl_app_SDLActivity_onNativeKeyUp(
276 JNIEnv* env, jclass jcls, jint keycode)
282 JNIEXPORT
void JNICALL Java_org_libsdl_app_SDLActivity_onNativeKeyboardFocusLost(
283 JNIEnv* env, jclass jcls)
291 JNIEXPORT
void JNICALL Java_org_libsdl_app_SDLActivity_onNativeTouch(
292 JNIEnv* env, jclass jcls,
293 jint touch_device_id_in, jint pointer_finger_id_in,
294 jint action, jfloat
x, jfloat
y, jfloat
p)
296 Android_OnTouch(touch_device_id_in, pointer_finger_id_in, action, x, y, p);
300 JNIEXPORT
void JNICALL Java_org_libsdl_app_SDLActivity_onNativeMouse(
301 JNIEnv* env, jclass jcls,
302 jint
button, jint action, jfloat
x, jfloat
y)
308 JNIEXPORT
void JNICALL Java_org_libsdl_app_SDLActivity_onNativeAccel(
309 JNIEnv* env, jclass jcls,
310 jfloat
x, jfloat
y, jfloat
z)
312 fLastAccelerometer[0] =
x;
313 fLastAccelerometer[1] =
y;
314 fLastAccelerometer[2] =
z;
319 JNIEXPORT
void JNICALL Java_org_libsdl_app_SDLActivity_nativeLowMemory(
320 JNIEnv* env, jclass cls)
326 JNIEXPORT
void JNICALL Java_org_libsdl_app_SDLActivity_nativeQuit(
327 JNIEnv* env, jclass cls)
342 JNIEXPORT
void JNICALL Java_org_libsdl_app_SDLActivity_nativePause(
343 JNIEnv* env, jclass cls)
345 __android_log_print(ANDROID_LOG_VERBOSE,
"SDL",
"nativePause()");
359 JNIEXPORT
void JNICALL Java_org_libsdl_app_SDLActivity_nativeResume(
360 JNIEnv* env, jclass cls)
362 __android_log_print(ANDROID_LOG_VERBOSE,
"SDL",
"nativeResume()");
377 JNIEXPORT
void JNICALL Java_org_libsdl_app_SDLInputConnection_nativeCommitText(
378 JNIEnv* env, jclass cls,
379 jstring
text, jint newCursorPosition)
381 const char *utftext = (*env)->GetStringUTFChars(env, text,
NULL);
385 (*env)->ReleaseStringUTFChars(env, text, utftext);
388 JNIEXPORT
void JNICALL Java_org_libsdl_app_SDLInputConnection_nativeSetComposingText(
389 JNIEnv* env, jclass cls,
390 jstring text, jint newCursorPosition)
392 const char *utftext = (*env)->GetStringUTFChars(env, text,
NULL);
396 (*env)->ReleaseStringUTFChars(env, text, utftext);
399 JNIEXPORT jstring JNICALL Java_org_libsdl_app_SDLActivity_nativeGetHint(JNIEnv* env, jclass cls, jstring name) {
400 const char *utfname = (*env)->GetStringUTFChars(env, name,
NULL);
403 jstring
result = (*env)->NewStringUTF(env, hint);
404 (*env)->ReleaseStringUTFChars(env, name, utfname);
413 static int s_active = 0;
414 struct LocalReferenceHolder
420 static struct LocalReferenceHolder LocalReferenceHolder_Setup(const char *
func)
422 struct LocalReferenceHolder refholder;
423 refholder.m_env =
NULL;
424 refholder.m_func =
func;
431 static SDL_bool LocalReferenceHolder_Init(
struct LocalReferenceHolder *refholder, JNIEnv *env)
433 const int capacity = 16;
434 if ((*env)->PushLocalFrame(env, capacity) < 0) {
435 SDL_SetError(
"Failed to allocate enough JVM local references");
439 refholder->m_env = env;
443 static void LocalReferenceHolder_Cleanup(
struct LocalReferenceHolder *refholder)
446 SDL_Log(
"Leaving function %s", refholder->m_func);
448 if (refholder->m_env) {
449 JNIEnv* env = refholder->m_env;
450 (*env)->PopLocalFrame(env,
NULL);
455 static SDL_bool LocalReferenceHolder_IsActive(
void)
466 s = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetNativeSurface);
467 anw = ANativeWindow_fromSurface(env, s);
468 (*env)->DeleteLocalRef(env, s);
477 mid = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
"setActivityTitle",
"(Ljava/lang/String;)Z");
479 jstring jtitle = (jstring)((*mEnv)->NewStringUTF(mEnv, title));
480 (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, mid, jtitle);
481 (*mEnv)->DeleteLocalRef(mEnv, jtitle);
491 for (i = 0; i < 3; ++
i) {
492 values[
i] = fLastAccelerometer[
i];
501 static void Android_JNI_ThreadDestroyed(
void*
value)
504 JNIEnv *env = (JNIEnv*) value;
506 (*mJavaVM)->DetachCurrentThread(mJavaVM);
507 pthread_setspecific(mThreadKey,
NULL);
526 int status = (*mJavaVM)->AttachCurrentThread(mJavaVM, &env,
NULL);
528 LOGE(
"failed to attach current thread");
541 pthread_setspecific(mThreadKey, (
void*) env);
555 static jboolean audioBuffer16Bit = JNI_FALSE;
556 static jobject audioBuffer =
NULL;
557 static void* audioBufferPinned =
NULL;
561 jboolean audioBufferStereo;
562 int audioBufferFrames;
567 LOGE(
"callback_handler: failed to attach current thread");
571 __android_log_print(ANDROID_LOG_VERBOSE,
"SDL",
"SDL audio: opening device");
572 audioBuffer16Bit = is16Bit;
573 audioBufferStereo = channelCount > 1;
575 if ((*env)->CallStaticIntMethod(env, mActivityClass, midAudioInit, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames) != 0) {
577 __android_log_print(ANDROID_LOG_WARN,
"SDL",
"SDL audio: error on AudioTrack initialization!");
585 jshortArray audioBufferLocal = (*env)->NewShortArray(env, desiredBufferFrames * (audioBufferStereo ? 2 : 1));
586 if (audioBufferLocal) {
587 audioBuffer = (*env)->NewGlobalRef(env, audioBufferLocal);
588 (*env)->DeleteLocalRef(env, audioBufferLocal);
592 jbyteArray audioBufferLocal = (*env)->NewByteArray(env, desiredBufferFrames * (audioBufferStereo ? 2 : 1));
593 if (audioBufferLocal) {
594 audioBuffer = (*env)->NewGlobalRef(env, audioBufferLocal);
595 (*env)->DeleteLocalRef(env, audioBufferLocal);
599 if (audioBuffer ==
NULL) {
600 __android_log_print(ANDROID_LOG_WARN,
"SDL",
"SDL audio: could not allocate an audio buffer!");
604 jboolean isCopy = JNI_FALSE;
605 if (audioBuffer16Bit) {
606 audioBufferPinned = (*env)->GetShortArrayElements(env, (jshortArray)audioBuffer, &isCopy);
607 audioBufferFrames = (*env)->GetArrayLength(env, (jshortArray)audioBuffer);
609 audioBufferPinned = (*env)->GetByteArrayElements(env, (jbyteArray)audioBuffer, &isCopy);
610 audioBufferFrames = (*env)->GetArrayLength(env, (jbyteArray)audioBuffer);
612 if (audioBufferStereo) {
613 audioBufferFrames /= 2;
616 return audioBufferFrames;
621 return audioBufferPinned;
628 if (audioBuffer16Bit) {
629 (*mAudioEnv)->ReleaseShortArrayElements(mAudioEnv, (jshortArray)audioBuffer, (jshort *)audioBufferPinned, JNI_COMMIT);
630 (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mActivityClass, midAudioWriteShortBuffer, (jshortArray)audioBuffer);
632 (*mAudioEnv)->ReleaseByteArrayElements(mAudioEnv, (jbyteArray)audioBuffer, (jbyte *)audioBufferPinned, JNI_COMMIT);
633 (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mActivityClass, midAudioWriteByteBuffer, (jbyteArray)audioBuffer);
643 (*env)->CallStaticVoidMethod(env, mActivityClass, midAudioQuit);
646 (*env)->DeleteGlobalRef(env, audioBuffer);
648 audioBufferPinned =
NULL;
659 jthrowable exception = (*mEnv)->ExceptionOccurred(mEnv);
660 if (exception !=
NULL) {
664 (*mEnv)->ExceptionClear(mEnv);
667 jclass exceptionClass = (*mEnv)->GetObjectClass(mEnv, exception);
668 jclass classClass = (*mEnv)->FindClass(mEnv,
"java/lang/Class");
670 mid = (*mEnv)->GetMethodID(mEnv, classClass,
"getName",
"()Ljava/lang/String;");
671 jstring exceptionName = (jstring)(*mEnv)->CallObjectMethod(mEnv, exceptionClass, mid);
672 const char* exceptionNameUTF8 = (*mEnv)->GetStringUTFChars(mEnv, exceptionName, 0);
674 mid = (*mEnv)->GetMethodID(mEnv, exceptionClass,
"getMessage",
"()Ljava/lang/String;");
675 jstring exceptionMessage = (jstring)(*mEnv)->CallObjectMethod(mEnv, exception, mid);
677 if (exceptionMessage !=
NULL) {
678 const char* exceptionMessageUTF8 = (*mEnv)->GetStringUTFChars(mEnv, exceptionMessage, 0);
679 SDL_SetError(
"%s: %s", exceptionNameUTF8, exceptionMessageUTF8);
680 (*mEnv)->ReleaseStringUTFChars(mEnv, exceptionMessage, exceptionMessageUTF8);
685 (*mEnv)->ReleaseStringUTFChars(mEnv, exceptionName, exceptionNameUTF8);
694 static int Internal_Android_JNI_FileOpen(
SDL_RWops* ctx)
696 struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
702 jobject assetManager;
705 jobject readableByteChannel;
706 jstring fileNameJString;
712 if (!LocalReferenceHolder_Init(&refs, mEnv)) {
716 fileNameJString = (jstring)ctx->
hidden.androidio.fileNameRef;
717 ctx->
hidden.androidio.position = 0;
720 mid = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
721 "getContext",
"()Landroid/content/Context;");
722 context = (*mEnv)->CallStaticObjectMethod(mEnv, mActivityClass, mid);
726 mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, context),
727 "getAssets",
"()Landroid/content/res/AssetManager;");
728 assetManager = (*mEnv)->CallObjectMethod(mEnv, context, mid);
733 mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, assetManager),
"openFd",
"(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;");
734 inputStream = (*mEnv)->CallObjectMethod(mEnv, assetManager, mid, fileNameJString);
735 if (Android_JNI_ExceptionOccurred(
SDL_TRUE)) {
739 mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
"getStartOffset",
"()J");
740 ctx->
hidden.androidio.offset = (*mEnv)->CallLongMethod(mEnv, inputStream, mid);
741 if (Android_JNI_ExceptionOccurred(
SDL_TRUE)) {
745 mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
"getDeclaredLength",
"()J");
746 ctx->
hidden.androidio.
size = (*mEnv)->CallLongMethod(mEnv, inputStream, mid);
747 if (Android_JNI_ExceptionOccurred(
SDL_TRUE)) {
751 mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
"getFileDescriptor",
"()Ljava/io/FileDescriptor;");
752 fd = (*mEnv)->CallObjectMethod(mEnv, inputStream, mid);
753 fdCls = (*mEnv)->GetObjectClass(mEnv, fd);
754 descriptor = (*mEnv)->GetFieldID(mEnv, fdCls,
"descriptor",
"I");
755 ctx->
hidden.androidio.fd = (*mEnv)->GetIntField(mEnv, fd, descriptor);
756 ctx->
hidden.androidio.assetFileDescriptorRef = (*mEnv)->NewGlobalRef(mEnv, inputStream);
759 lseek(ctx->
hidden.androidio.fd, (off_t)ctx->
hidden.androidio.offset, SEEK_SET);
767 ctx->
hidden.androidio.assetFileDescriptorRef =
NULL;
770 mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, assetManager),
771 "open",
"(Ljava/lang/String;I)Ljava/io/InputStream;");
772 inputStream = (*mEnv)->CallObjectMethod(mEnv, assetManager, mid, fileNameJString, 1 );
773 if (Android_JNI_ExceptionOccurred(
SDL_FALSE)) {
775 mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, context),
776 "openAPKExpansionInputStream",
"(Ljava/lang/String;)Ljava/io/InputStream;");
778 SDL_SetError(
"No openAPKExpansionInputStream() in Java class");
781 inputStream = (*mEnv)->CallObjectMethod(mEnv, context, mid, fileNameJString);
786 if (Android_JNI_ExceptionOccurred(
SDL_FALSE) || !inputStream) {
791 ctx->
hidden.androidio.inputStreamRef = (*mEnv)->NewGlobalRef(mEnv, inputStream);
801 mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
803 ctx->
hidden.androidio.
size = (long)(*mEnv)->CallIntMethod(mEnv, inputStream, mid);
804 if (Android_JNI_ExceptionOccurred(
SDL_FALSE)) {
809 channels = (*mEnv)->FindClass(mEnv,
"java/nio/channels/Channels");
810 mid = (*mEnv)->GetStaticMethodID(mEnv, channels,
812 "(Ljava/io/InputStream;)Ljava/nio/channels/ReadableByteChannel;");
813 readableByteChannel = (*mEnv)->CallStaticObjectMethod(
814 mEnv, channels, mid, inputStream);
815 if (Android_JNI_ExceptionOccurred(
SDL_FALSE)) {
819 ctx->
hidden.androidio.readableByteChannelRef =
820 (*mEnv)->NewGlobalRef(mEnv, readableByteChannel);
823 mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, readableByteChannel),
824 "read",
"(Ljava/nio/ByteBuffer;)I");
825 ctx->
hidden.androidio.readMethod = mid;
832 (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->
hidden.androidio.fileNameRef);
834 if(ctx->
hidden.androidio.inputStreamRef !=
NULL) {
835 (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->
hidden.androidio.inputStreamRef);
838 if(ctx->
hidden.androidio.readableByteChannelRef !=
NULL) {
839 (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->
hidden.androidio.readableByteChannelRef);
842 if(ctx->
hidden.androidio.assetFileDescriptorRef !=
NULL) {
843 (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->
hidden.androidio.assetFileDescriptorRef);
848 LocalReferenceHolder_Cleanup(&refs);
853 const char* fileName,
const char*
mode)
855 struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
859 if (!LocalReferenceHolder_Init(&refs, mEnv)) {
860 LocalReferenceHolder_Cleanup(&refs);
865 LocalReferenceHolder_Cleanup(&refs);
869 jstring fileNameJString = (*mEnv)->NewStringUTF(mEnv, fileName);
870 ctx->
hidden.androidio.fileNameRef = (*mEnv)->NewGlobalRef(mEnv, fileNameJString);
872 ctx->
hidden.androidio.readableByteChannelRef =
NULL;
874 ctx->
hidden.androidio.assetFileDescriptorRef =
NULL;
876 retval = Internal_Android_JNI_FileOpen(ctx);
877 LocalReferenceHolder_Cleanup(&refs);
882 size_t size,
size_t maxnum)
884 struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
886 if (ctx->
hidden.androidio.assetFileDescriptorRef) {
887 size_t bytesMax = size * maxnum;
891 size_t result = read(ctx->
hidden.androidio.fd, buffer, bytesMax );
894 LocalReferenceHolder_Cleanup(&refs);
895 return result /
size;
897 LocalReferenceHolder_Cleanup(&refs);
900 jlong bytesRemaining = (jlong) (size * maxnum);
901 jlong bytesMax = (jlong) (ctx->
hidden.androidio.
size - ctx->
hidden.androidio.position);
905 if (bytesRemaining > bytesMax) bytesRemaining = bytesMax;
908 if (!LocalReferenceHolder_Init(&refs, mEnv)) {
909 LocalReferenceHolder_Cleanup(&refs);
913 jobject readableByteChannel = (jobject)ctx->
hidden.androidio.readableByteChannelRef;
914 jmethodID readMethod = (jmethodID)ctx->
hidden.androidio.readMethod;
915 jobject byteBuffer = (*mEnv)->NewDirectByteBuffer(mEnv, buffer, bytesRemaining);
917 while (bytesRemaining > 0) {
919 int result = (*mEnv)->CallIntMethod(mEnv, readableByteChannel, readMethod, byteBuffer);
921 if (Android_JNI_ExceptionOccurred(
SDL_FALSE)) {
922 LocalReferenceHolder_Cleanup(&refs);
934 LocalReferenceHolder_Cleanup(&refs);
935 return bytesRead /
size;
940 size_t size,
size_t num)
942 SDL_SetError(
"Cannot write to Android package filesystem");
948 struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
953 if (!LocalReferenceHolder_Init(&refs, mEnv)) {
954 LocalReferenceHolder_Cleanup(&refs);
955 return SDL_SetError(
"Failed to allocate enough JVM local references");
960 (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->
hidden.androidio.fileNameRef);
963 if (ctx->
hidden.androidio.assetFileDescriptorRef) {
964 jobject inputStream = (jobject)ctx->
hidden.androidio.assetFileDescriptorRef;
965 jmethodID mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
967 (*mEnv)->CallVoidMethod(mEnv, inputStream, mid);
968 (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->
hidden.androidio.assetFileDescriptorRef);
969 if (Android_JNI_ExceptionOccurred(
SDL_FALSE)) {
974 jobject inputStream = (jobject)ctx->
hidden.androidio.inputStreamRef;
977 jmethodID mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
979 (*mEnv)->CallVoidMethod(mEnv, inputStream, mid);
980 (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->
hidden.androidio.inputStreamRef);
981 (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->
hidden.androidio.readableByteChannelRef);
982 if (Android_JNI_ExceptionOccurred(
SDL_FALSE)) {
992 LocalReferenceHolder_Cleanup(&refs);
1004 if (ctx->
hidden.androidio.assetFileDescriptorRef) {
1008 offset += ctx->
hidden.androidio.offset;
1011 offset += ctx->
hidden.androidio.position;
1013 offset += ctx->
hidden.androidio.offset;
1023 off_t ret = lseek(ctx->
hidden.androidio.fd, (off_t)offset, SEEK_SET);
1024 if (ret == -1)
return -1;
1025 ctx->
hidden.androidio.position = ret - ctx->
hidden.androidio.offset;
1044 if (newPosition < 0) {
1047 if (newPosition > ctx->
hidden.androidio.
size) {
1051 Sint64 movement = newPosition - ctx->
hidden.androidio.position;
1053 unsigned char buffer[4096];
1056 while (movement > 0) {
1058 if (amount > movement) {
1070 }
else if (movement < 0) {
1073 Internal_Android_JNI_FileClose(ctx,
SDL_FALSE);
1074 Internal_Android_JNI_FileOpen(ctx);
1079 return ctx->
hidden.androidio.position;
1085 return Internal_Android_JNI_FileClose(ctx,
SDL_TRUE);
1089 static jobject Android_JNI_GetSystemServiceObject(
const char* name)
1091 struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
1093 jobject retval =
NULL;
1095 if (!LocalReferenceHolder_Init(&refs, env)) {
1096 LocalReferenceHolder_Cleanup(&refs);
1100 jstring service = (*env)->NewStringUTF(env, name);
1104 mid = (*env)->GetStaticMethodID(env, mActivityClass,
"getContext",
"()Landroid/content/Context;");
1105 jobject context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
1107 mid = (*env)->GetMethodID(env, mActivityClass,
"getSystemServiceFromUiThread",
"(Ljava/lang/String;)Ljava/lang/Object;");
1108 jobject manager = (*env)->CallObjectMethod(env, context, mid, service);
1110 (*env)->DeleteLocalRef(env, service);
1112 retval = manager ? (*env)->NewGlobalRef(env, manager) :
NULL;
1113 LocalReferenceHolder_Cleanup(&refs);
1117 #define SETUP_CLIPBOARD(error) \ 1118 struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); \ 1119 JNIEnv* env = Android_JNI_GetEnv(); \ 1120 if (!LocalReferenceHolder_Init(&refs, env)) { \ 1121 LocalReferenceHolder_Cleanup(&refs); \ 1124 jobject clipboard = Android_JNI_GetSystemServiceObject("clipboard"); \ 1126 LocalReferenceHolder_Cleanup(&refs); \ 1130 #define CLEANUP_CLIPBOARD() \ 1131 LocalReferenceHolder_Cleanup(&refs); 1137 jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "setText", "(Ljava/lang/CharSequence;)V");
1138 jstring
string = (*env)->NewStringUTF(env, text);
1139 (*env)->CallVoidMethod(env, clipboard, mid,
string);
1140 (*env)->DeleteGlobalRef(env, clipboard);
1141 (*env)->DeleteLocalRef(env,
string);
1143 CLEANUP_CLIPBOARD();
1152 jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "getText", "()Ljava/lang/CharSequence;");
1153 jobject sequence = (*env)->CallObjectMethod(env, clipboard, mid);
1154 (*env)->DeleteGlobalRef(env, clipboard);
1156 mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, sequence),
"toString",
"()Ljava/lang/String;");
1157 jstring
string = (jstring)((*env)->CallObjectMethod(env, sequence, mid));
1158 const char* utf = (*env)->GetStringUTFChars(env,
string, 0);
1161 (*env)->ReleaseStringUTFChars(env,
string, utf);
1163 CLEANUP_CLIPBOARD();
1169 CLEANUP_CLIPBOARD();
1178 jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "hasText", "()Z");
1179 jboolean has = (*env)->CallBooleanMethod(env, clipboard, mid);
1180 (*env)->DeleteGlobalRef(env, clipboard);
1182 CLEANUP_CLIPBOARD();
1194 struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
1196 if (!LocalReferenceHolder_Init(&refs, env)) {
1197 LocalReferenceHolder_Cleanup(&refs);
1203 mid = (*env)->GetStaticMethodID(env, mActivityClass,
"getContext",
"()Landroid/content/Context;");
1204 jobject context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
1206 jstring action = (*env)->NewStringUTF(env,
"android.intent.action.BATTERY_CHANGED");
1208 jclass cls = (*env)->FindClass(env,
"android/content/IntentFilter");
1210 mid = (*env)->GetMethodID(env, cls,
"<init>",
"(Ljava/lang/String;)V");
1211 jobject
filter = (*env)->NewObject(env, cls, mid, action);
1213 (*env)->DeleteLocalRef(env, action);
1215 mid = (*env)->GetMethodID(env, mActivityClass,
"registerReceiver",
"(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;");
1216 jobject intent = (*env)->CallObjectMethod(env, context, mid,
NULL, filter);
1218 (*env)->DeleteLocalRef(env, filter);
1220 cls = (*env)->GetObjectClass(env, intent);
1223 jmethodID imid = (*env)->GetMethodID(env, cls,
"getIntExtra",
"(Ljava/lang/String;I)I");
1225 #define GET_INT_EXTRA(var, key) \ 1226 iname = (*env)->NewStringUTF(env, key); \ 1227 int var = (*env)->CallIntMethod(env, intent, imid, iname, -1); \ 1228 (*env)->DeleteLocalRef(env, iname); 1231 jmethodID bmid = (*env)->GetMethodID(env, cls,
"getBooleanExtra",
"(Ljava/lang/String;Z)Z");
1233 #define GET_BOOL_EXTRA(var, key) \ 1234 bname = (*env)->NewStringUTF(env, key); \ 1235 int var = (*env)->CallBooleanMethod(env, intent, bmid, bname, JNI_FALSE); \ 1236 (*env)->DeleteLocalRef(env, bname); 1239 GET_INT_EXTRA(plug,
"plugged")
1241 LocalReferenceHolder_Cleanup(&refs);
1246 *plugged = (0 < plug) ? 1 : 0;
1250 GET_INT_EXTRA(status,
"status")
1252 LocalReferenceHolder_Cleanup(&refs);
1256 *charged = (status == 5) ? 1 : 0;
1260 GET_BOOL_EXTRA(present,
"present")
1261 *battery = present ? 1 : 0;
1269 GET_INT_EXTRA(
level,
"level")
1270 GET_INT_EXTRA(
scale, "scale")
1271 if ((
level == -1) || (scale == -1)) {
1272 LocalReferenceHolder_Cleanup(&refs);
1278 (*env)->DeleteLocalRef(env, intent);
1280 LocalReferenceHolder_Cleanup(&refs);
1288 jmethodID mid = (*env)->GetStaticMethodID(env, mActivityClass,
"inputGetInputDeviceIds",
"(I)[I");
1289 jintArray
array = (jintArray) (*env)->CallStaticObjectMethod(env, mActivityClass, mid, sources);
1293 number = (int) (*env)->GetArrayLength(env, array);
1295 jint* elements = (*env)->GetIntArrayElements(env, array,
NULL);
1299 for (i = 0; i < number; ++
i) {
1300 (*ids)[
i] = elements[
i];
1302 (*env)->ReleaseIntArrayElements(env, array, elements, JNI_ABORT);
1305 (*env)->DeleteLocalRef(env, array);
1313 (*env)->CallStaticVoidMethod(env, mActivityClass, midPollInputDevices);
1317 #define COMMAND_SET_KEEP_SCREEN_ON 5 1326 jmethodID mid = (*env)->GetStaticMethodID(env, mActivityClass,
"sendMessage",
"(II)Z");
1330 jboolean success = (*env)->CallStaticBooleanMethod(env, mActivityClass, mid, command, param);
1331 return success ? 0 : -1;
1346 jmethodID mid = (*env)->GetStaticMethodID(env, mActivityClass,
"showTextInput",
"(IIII)Z");
1350 (*env)->CallStaticBooleanMethod(env, mActivityClass, mid,
1360 const int COMMAND_TEXTEDIT_HIDE = 3;
1364 int Android_JNI_ShowMessageBox(
const SDL_MessageBoxData *messageboxdata,
int *buttonid)
1372 jintArray button_flags;
1373 jintArray button_ids;
1374 jobjectArray button_texts;
1384 clazz = (*env)->FindClass(env,
"java/lang/String");
1386 title = (*env)->NewStringUTF(env, messageboxdata->
title);
1387 message = (*env)->NewStringUTF(env, messageboxdata->
message);
1389 button_flags = (*env)->NewIntArray(env, messageboxdata->
numbuttons);
1390 button_ids = (*env)->NewIntArray(env, messageboxdata->
numbuttons);
1391 button_texts = (*env)->NewObjectArray(env, messageboxdata->
numbuttons,
1393 for (i = 0; i < messageboxdata->
numbuttons; ++
i) {
1395 (*env)->SetIntArrayRegion(env, button_flags, i, 1, &temp);
1397 (*env)->SetIntArrayRegion(env, button_ids, i, 1, &temp);
1398 text = (*env)->NewStringUTF(env, messageboxdata->
buttons[i].
text);
1399 (*env)->SetObjectArrayElement(env, button_texts, i, text);
1400 (*env)->DeleteLocalRef(env, text);
1406 temp = (0xFF << 24) |
1410 (*env)->SetIntArrayRegion(env, colors, i, 1, &temp);
1416 (*env)->DeleteLocalRef(env, clazz);
1420 mid = (*env)->GetStaticMethodID(env, mActivityClass,
"getContext",
"()Landroid/content/Context;");
1422 context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
1424 clazz = (*env)->GetObjectClass(env, context);
1426 mid = (*env)->GetMethodID(env, clazz,
1427 "messageboxShowMessageBox",
"(ILjava/lang/String;Ljava/lang/String;[I[I[Ljava/lang/String;[I)I");
1428 *buttonid = (*env)->CallIntMethod(env, context, mid,
1429 messageboxdata->
flags,
1437 (*env)->DeleteLocalRef(env, context);
1438 (*env)->DeleteLocalRef(env, clazz);
1442 (*env)->DeleteLocalRef(env, title);
1443 (*env)->DeleteLocalRef(env, message);
1444 (*env)->DeleteLocalRef(env, button_flags);
1445 (*env)->DeleteLocalRef(env, button_ids);
1446 (*env)->DeleteLocalRef(env, button_texts);
1447 (*env)->DeleteLocalRef(env, colors);
1455 // Functions exposed to SDL applications in SDL_system.h
1478 mid = (*env)->GetStaticMethodID(env, mActivityClass,
1479 "getContext",
"()Landroid/content/Context;");
1480 return (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
1485 static char *s_AndroidInternalFilesPath =
NULL;
1487 if (!s_AndroidInternalFilesPath) {
1488 struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
1496 if (!LocalReferenceHolder_Init(&refs, env)) {
1497 LocalReferenceHolder_Cleanup(&refs);
1502 mid = (*env)->GetStaticMethodID(env, mActivityClass,
1503 "getContext",
"()Landroid/content/Context;");
1504 context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
1507 mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
1508 "getFilesDir",
"()Ljava/io/File;");
1509 fileObject = (*env)->CallObjectMethod(env, context, mid);
1512 LocalReferenceHolder_Cleanup(&refs);
1517 mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, fileObject),
1518 "getAbsolutePath",
"()Ljava/lang/String;");
1519 pathString = (jstring)(*env)->CallObjectMethod(env, fileObject, mid);
1521 path = (*env)->GetStringUTFChars(env, pathString,
NULL);
1522 s_AndroidInternalFilesPath =
SDL_strdup(path);
1523 (*env)->ReleaseStringUTFChars(env, pathString, path);
1525 LocalReferenceHolder_Cleanup(&refs);
1527 return s_AndroidInternalFilesPath;
1532 struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
1535 jstring stateString;
1540 if (!LocalReferenceHolder_Init(&refs, env)) {
1541 LocalReferenceHolder_Cleanup(&refs);
1545 cls = (*env)->FindClass(env,
"android/os/Environment");
1546 mid = (*env)->GetStaticMethodID(env, cls,
1547 "getExternalStorageState",
"()Ljava/lang/String;");
1548 stateString = (jstring)(*env)->CallStaticObjectMethod(env, cls, mid);
1550 state = (*env)->GetStringUTFChars(env, stateString,
NULL);
1553 __android_log_print(ANDROID_LOG_INFO,
"SDL",
"external storage state: %s", state);
1556 stateFlags = SDL_ANDROID_EXTERNAL_STORAGE_READ |
1557 SDL_ANDROID_EXTERNAL_STORAGE_WRITE;
1558 }
else if (
SDL_strcmp(state,
"mounted_ro") == 0) {
1559 stateFlags = SDL_ANDROID_EXTERNAL_STORAGE_READ;
1563 (*env)->ReleaseStringUTFChars(env, stateString, state);
1565 LocalReferenceHolder_Cleanup(&refs);
1571 static char *s_AndroidExternalFilesPath =
NULL;
1573 if (!s_AndroidExternalFilesPath) {
1574 struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
1582 if (!LocalReferenceHolder_Init(&refs, env)) {
1583 LocalReferenceHolder_Cleanup(&refs);
1588 mid = (*env)->GetStaticMethodID(env, mActivityClass,
1589 "getContext",
"()Landroid/content/Context;");
1590 context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
1593 mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
1594 "getExternalFilesDir",
"(Ljava/lang/String;)Ljava/io/File;");
1595 fileObject = (*env)->CallObjectMethod(env, context, mid,
NULL);
1598 LocalReferenceHolder_Cleanup(&refs);
1603 mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, fileObject),
1604 "getAbsolutePath",
"()Ljava/lang/String;");
1605 pathString = (jstring)(*env)->CallObjectMethod(env, fileObject, mid);
1607 path = (*env)->GetStringUTFChars(env, pathString,
NULL);
1608 s_AndroidExternalFilesPath =
SDL_strdup(path);
1609 (*env)->ReleaseStringUTFChars(env, pathString, path);
1611 LocalReferenceHolder_Cleanup(&refs);
1613 return s_AndroidExternalFilesPath;
1618 return mActivityClass;
void Android_SetScreenResolution(int width, int height, Uint32 format, float rate)
GLenum GLenum GLenum GLenum GLenum scale
GLint GLint GLsizei GLsizei GLsizei GLint GLenum format
int Android_JNI_FileClose(SDL_RWops *ctx)
int Android_OnKeyUp(int keycode)
void Android_JNI_CloseAudioDevice(void)
int Android_JNI_SendMessage(int command, int param)
void Android_JNI_ShowTextInput(SDL_Rect *inputRect)
GLint GLint GLsizei width
void Android_JNI_WriteAudioBuffer(void)
GLuint GLsizei const GLchar * message
GLint GLint GLint GLint GLint x
SDL_bool Android_JNI_GetAccelerometerValues(float values[3])
#define SDL_AndroidGetExternalStoragePath
void Android_JNI_SetActivityTitle(const char *title)
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
char * Android_JNI_GetClipboardText(void)
int Android_OnKeyDown(int keycode)
void Android_OnTouch(int touch_device_id_in, int pointer_finger_id_in, int action, float x, float y, float p)
GLuint const GLchar * name
int SDL_SendWindowEvent(SDL_Window *window, Uint8 windowevent, int data1, int data2)
GLint GLint GLsizei GLsizei height
int Android_JNI_FileOpen(SDL_RWops *ctx, const char *fileName, const char *mode)
SDL_sem * Android_PauseSem
static SDL_VideoDevice * _this
int Android_JNI_SetupThread(void)
#define SDL_StopTextInput
GLenum GLsizei const void * pathString
GLint GLint GLint GLint GLint GLint y
GLsizei const GLfloat * value
int SDL_SendKeyboardText(const char *text)
SDL_bool Android_JNI_HasClipboardText(void)
int Android_JNI_GetPowerInfo(int *plugged, int *charged, int *battery, int *seconds, int *percent)
size_t Android_JNI_FileWrite(SDL_RWops *ctx, const void *buffer, size_t size, size_t num)
SDL_Window * Android_Window
void * Android_JNI_GetAudioBuffer(void)
GLenum GLsizei GLsizei GLint * values
void Android_JNI_SuspendScreenSaver(SDL_bool suspend)
const SDL_MessageBoxButtonData * buttons
MessageBox structure containing title, text, window, etc.
void Android_OnMouse(int button, int action, float x, float y)
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Sint64(* size)(struct SDL_RWops *context)
#define SDL_assert(condition)
ANativeWindow * Android_JNI_GetNativeWindow(void)
jclass Android_JNI_GetActivityClass(void)
union SDL_RWops::@10 hidden
static char text[MAX_TEXT_LENGTH]
void Android_JNI_HideTextInput(void)
#define SDL_AndroidGetActivity
SDL_sem * Android_ResumeSem
size_t Android_JNI_FileRead(SDL_RWops *ctx, void *buffer, size_t size, size_t maxnum)
int SDL_SendDropFile(const char *file)
int Android_JNI_SetClipboardText(const char *text)
#define SDL_AndroidGetExternalStorageState
SDL_VideoDevice * SDL_GetVideoDevice(void)
int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames)
GLsizei const GLchar *const * path
int SDL_SendAppEvent(SDL_EventType eventType)
void Android_JNI_PollInputDevices(void)
int64_t Sint64
A signed 64-bit integer type.
#define SDL_AndroidGetJNIEnv
ANativeWindow * native_window
int Android_JNI_GetTouchDeviceIds(int **ids)
const SDL_MessageBoxColorScheme * colorScheme
SDL_MessageBoxColor colors[SDL_MESSAGEBOX_COLOR_MAX]
#define SDL_AndroidGetInternalStoragePath
JNIEnv * Android_JNI_GetEnv(void)
int SDL_SendEditingText(const char *text, int start, int length)
Sint64 Android_JNI_FileSeek(SDL_RWops *ctx, Sint64 offset, int whence)
A rectangle, with the origin at the upper left.
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Sint64 Android_JNI_FileSize(SDL_RWops *ctx)