22 #include "SDL_stdinc.h"
32 #include "../../events/SDL_events_c.h"
33 #include "../../video/android/SDL_androidkeyboard.h"
34 #include "../../video/android/SDL_androidtouch.h"
35 #include "../../video/android/SDL_androidvideo.h"
36 #include "../../video/android/SDL_androidwindow.h"
38 #include <android/log.h>
42 #define LOG_TAG "SDL_android"
45 #define LOGI(...) do {} while (false)
46 #define LOGE(...) do {} while (false)
51 static void Android_JNI_ThreadDestroyed(
void*);
57 #include <android/log.h>
64 static pthread_key_t mThreadKey;
65 static JavaVM* mJavaVM;
68 static jclass mActivityClass;
71 static jmethodID midGetNativeSurface;
72 static jmethodID midFlipBuffers;
73 static jmethodID midAudioInit;
74 static jmethodID midAudioWriteShortBuffer;
75 static jmethodID midAudioWriteByteBuffer;
76 static jmethodID midAudioQuit;
79 static float fLastAccelerometer[3];
80 static bool bHasNewData;
87 jint JNI_OnLoad(JavaVM* vm,
void* reserved)
91 LOGI(
"JNI_OnLoad called");
92 if ((*mJavaVM)->GetEnv(mJavaVM, (
void**) &env, JNI_VERSION_1_4) != JNI_OK) {
93 LOGE(
"Failed to get the environment using GetEnv()");
100 if (pthread_key_create(&mThreadKey, Android_JNI_ThreadDestroyed)) {
101 __android_log_print(ANDROID_LOG_ERROR,
"SDL",
"Error initializing pthread key");
107 return JNI_VERSION_1_4;
111 void 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 midFlipBuffers = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
122 "flipBuffers",
"()V");
123 midAudioInit = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
124 "audioInit",
"(IZZI)I");
125 midAudioWriteShortBuffer = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
126 "audioWriteShortBuffer",
"([S)V");
127 midAudioWriteByteBuffer = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
128 "audioWriteByteBuffer",
"([B)V");
129 midAudioQuit = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
134 if(!midGetNativeSurface || !midFlipBuffers || !midAudioInit ||
135 !midAudioWriteShortBuffer || !midAudioWriteByteBuffer || !midAudioQuit) {
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_onNativeResize(
143 JNIEnv* env, jclass jcls,
151 void Java_org_libsdl_app_SDLActivity_onNativeSurfaceChanged(JNIEnv* env, jclass jcls)
177 void Java_org_libsdl_app_SDLActivity_onNativeSurfaceDestroyed(JNIEnv* env, jclass jcls)
194 SDL_EGL_MakeCurrent(_this,
NULL,
NULL);
203 void Java_org_libsdl_app_SDLActivity_nativeFlipBuffers(JNIEnv* env, jclass jcls)
209 void Java_org_libsdl_app_SDLActivity_onNativeKeyDown(
210 JNIEnv* env, jclass jcls, jint keycode)
216 void Java_org_libsdl_app_SDLActivity_onNativeKeyUp(
217 JNIEnv* env, jclass jcls, jint keycode)
223 void Java_org_libsdl_app_SDLActivity_onNativeKeyboardFocusLost(
224 JNIEnv* env, jclass jcls)
232 void Java_org_libsdl_app_SDLActivity_onNativeTouch(
233 JNIEnv* env, jclass jcls,
234 jint touch_device_id_in, jint pointer_finger_id_in,
235 jint action, jfloat
x, jfloat
y, jfloat
p)
237 Android_OnTouch(touch_device_id_in, pointer_finger_id_in, action, x, y, p);
241 void Java_org_libsdl_app_SDLActivity_onNativeAccel(
242 JNIEnv* env, jclass jcls,
243 jfloat
x, jfloat
y, jfloat
z)
245 fLastAccelerometer[0] =
x;
246 fLastAccelerometer[1] =
y;
247 fLastAccelerometer[2] =
z;
252 void Java_org_libsdl_app_SDLActivity_nativeLowMemory(
253 JNIEnv* env, jclass cls)
259 void Java_org_libsdl_app_SDLActivity_nativeQuit(
260 JNIEnv* env, jclass cls)
268 void Java_org_libsdl_app_SDLActivity_nativePause(
269 JNIEnv* env, jclass cls)
278 __android_log_print(ANDROID_LOG_VERBOSE,
"SDL",
"nativePause()");
284 void Java_org_libsdl_app_SDLActivity_nativeResume(
285 JNIEnv* env, jclass cls)
287 __android_log_print(ANDROID_LOG_VERBOSE,
"SDL",
"nativeResume()");
302 void Java_org_libsdl_app_SDLInputConnection_nativeCommitText(
303 JNIEnv* env, jclass cls,
304 jstring text, jint newCursorPosition)
306 const char *utftext = (*env)->GetStringUTFChars(env, text,
NULL);
310 (*env)->ReleaseStringUTFChars(env, text, utftext);
313 void Java_org_libsdl_app_SDLInputConnection_nativeSetComposingText(
314 JNIEnv* env, jclass cls,
315 jstring text, jint newCursorPosition)
317 const char *utftext = (*env)->GetStringUTFChars(env, text,
NULL);
321 (*env)->ReleaseStringUTFChars(env, text, utftext);
330 static int s_active = 0;
331 struct LocalReferenceHolder
337 static struct LocalReferenceHolder LocalReferenceHolder_Setup(
const char *
func)
339 struct LocalReferenceHolder refholder;
340 refholder.m_env =
NULL;
341 refholder.m_func =
func;
348 static SDL_bool LocalReferenceHolder_Init(
struct LocalReferenceHolder *refholder, JNIEnv *env)
350 const int capacity = 16;
351 if ((*env)->PushLocalFrame(env, capacity) < 0) {
352 SDL_SetError(
"Failed to allocate enough JVM local references");
356 refholder->m_env =
env;
360 static void LocalReferenceHolder_Cleanup(
struct LocalReferenceHolder *refholder)
363 SDL_Log(
"Leaving function %s", refholder->m_func);
365 if (refholder->m_env) {
366 JNIEnv* env = refholder->m_env;
367 (*env)->PopLocalFrame(env,
NULL);
372 static SDL_bool LocalReferenceHolder_IsActive()
383 s = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetNativeSurface);
384 anw = ANativeWindow_fromSurface(env, s);
385 (*env)->DeleteLocalRef(env, s);
393 (*mEnv)->CallStaticVoidMethod(mEnv, mActivityClass, midFlipBuffers);
400 mid = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
"setActivityTitle",
"(Ljava/lang/String;)Z");
402 jstring jtitle = (jstring)((*mEnv)->NewStringUTF(mEnv, title));
403 (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, mid, jtitle);
404 (*mEnv)->DeleteLocalRef(mEnv, jtitle);
414 for (i = 0; i < 3; ++
i) {
415 values[
i] = fLastAccelerometer[
i];
424 static void Android_JNI_ThreadDestroyed(
void*
value) {
426 JNIEnv *env = (JNIEnv*) value;
428 (*mJavaVM)->DetachCurrentThread(mJavaVM);
429 pthread_setspecific(mThreadKey,
NULL);
447 int status = (*mJavaVM)->AttachCurrentThread(mJavaVM, &env,
NULL);
449 LOGE(
"failed to attach current thread");
467 pthread_setspecific(mThreadKey, (
void*) env);
474 static jboolean audioBuffer16Bit = JNI_FALSE;
475 static jboolean audioBufferStereo = JNI_FALSE;
476 static jobject audioBuffer =
NULL;
477 static void* audioBufferPinned =
NULL;
481 int audioBufferFrames;
486 LOGE(
"callback_handler: failed to attach current thread");
490 __android_log_print(ANDROID_LOG_VERBOSE,
"SDL",
"SDL audio: opening device");
491 audioBuffer16Bit = is16Bit;
492 audioBufferStereo = channelCount > 1;
494 if ((*env)->CallStaticIntMethod(env, mActivityClass, midAudioInit, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames) != 0) {
496 __android_log_print(ANDROID_LOG_WARN,
"SDL",
"SDL audio: error on AudioTrack initialization!");
504 jshortArray audioBufferLocal = (*env)->NewShortArray(env, desiredBufferFrames * (audioBufferStereo ? 2 : 1));
505 if (audioBufferLocal) {
506 audioBuffer = (*env)->NewGlobalRef(env, audioBufferLocal);
507 (*env)->DeleteLocalRef(env, audioBufferLocal);
511 jbyteArray audioBufferLocal = (*env)->NewByteArray(env, desiredBufferFrames * (audioBufferStereo ? 2 : 1));
512 if (audioBufferLocal) {
513 audioBuffer = (*env)->NewGlobalRef(env, audioBufferLocal);
514 (*env)->DeleteLocalRef(env, audioBufferLocal);
518 if (audioBuffer ==
NULL) {
519 __android_log_print(ANDROID_LOG_WARN,
"SDL",
"SDL audio: could not allocate an audio buffer!");
523 jboolean isCopy = JNI_FALSE;
524 if (audioBuffer16Bit) {
525 audioBufferPinned = (*env)->GetShortArrayElements(env, (jshortArray)audioBuffer, &isCopy);
526 audioBufferFrames = (*env)->GetArrayLength(env, (jshortArray)audioBuffer);
528 audioBufferPinned = (*env)->GetByteArrayElements(env, (jbyteArray)audioBuffer, &isCopy);
529 audioBufferFrames = (*env)->GetArrayLength(env, (jbyteArray)audioBuffer);
531 if (audioBufferStereo) {
532 audioBufferFrames /= 2;
535 return audioBufferFrames;
540 return audioBufferPinned;
547 if (audioBuffer16Bit) {
548 (*mAudioEnv)->ReleaseShortArrayElements(mAudioEnv, (jshortArray)audioBuffer, (jshort *)audioBufferPinned, JNI_COMMIT);
549 (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mActivityClass, midAudioWriteShortBuffer, (jshortArray)audioBuffer);
551 (*mAudioEnv)->ReleaseByteArrayElements(mAudioEnv, (jbyteArray)audioBuffer, (jbyte *)audioBufferPinned, JNI_COMMIT);
552 (*mAudioEnv)->CallStaticVoidMethod(mAudioEnv, mActivityClass, midAudioWriteByteBuffer, (jbyteArray)audioBuffer);
562 (*env)->CallStaticVoidMethod(env, mActivityClass, midAudioQuit);
565 (*env)->DeleteGlobalRef(env, audioBuffer);
567 audioBufferPinned =
NULL;
573 static bool Android_JNI_ExceptionOccurred(
bool silent)
578 jthrowable exception = (*mEnv)->ExceptionOccurred(mEnv);
579 if (exception !=
NULL) {
583 (*mEnv)->ExceptionClear(mEnv);
586 jclass exceptionClass = (*mEnv)->GetObjectClass(mEnv, exception);
587 jclass classClass = (*mEnv)->FindClass(mEnv,
"java/lang/Class");
589 mid = (*mEnv)->GetMethodID(mEnv, classClass,
"getName",
"()Ljava/lang/String;");
590 jstring exceptionName = (jstring)(*mEnv)->CallObjectMethod(mEnv, exceptionClass, mid);
591 const char* exceptionNameUTF8 = (*mEnv)->GetStringUTFChars(mEnv, exceptionName, 0);
593 mid = (*mEnv)->GetMethodID(mEnv, exceptionClass,
"getMessage",
"()Ljava/lang/String;");
594 jstring exceptionMessage = (jstring)(*mEnv)->CallObjectMethod(mEnv, exception, mid);
596 if (exceptionMessage !=
NULL) {
597 const char* exceptionMessageUTF8 = (*mEnv)->GetStringUTFChars(mEnv, exceptionMessage, 0);
598 SDL_SetError(
"%s: %s", exceptionNameUTF8, exceptionMessageUTF8);
599 (*mEnv)->ReleaseStringUTFChars(mEnv, exceptionMessage, exceptionMessageUTF8);
604 (*mEnv)->ReleaseStringUTFChars(mEnv, exceptionName, exceptionNameUTF8);
613 static int Internal_Android_JNI_FileOpen(
SDL_RWops*
ctx)
615 struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
621 jobject assetManager;
624 jobject readableByteChannel;
625 jstring fileNameJString;
631 if (!LocalReferenceHolder_Init(&refs, mEnv)) {
635 fileNameJString = (jstring)ctx->
hidden.androidio.fileNameRef;
636 ctx->
hidden.androidio.position = 0;
639 mid = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,
640 "getContext",
"()Landroid/content/Context;");
641 context = (*mEnv)->CallStaticObjectMethod(mEnv, mActivityClass, mid);
645 mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, context),
646 "getAssets",
"()Landroid/content/res/AssetManager;");
647 assetManager = (*mEnv)->CallObjectMethod(mEnv, context, mid);
652 mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, assetManager),
"openFd",
"(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;");
653 inputStream = (*mEnv)->CallObjectMethod(mEnv, assetManager, mid, fileNameJString);
654 if (Android_JNI_ExceptionOccurred(
true)) {
658 mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
"getStartOffset",
"()J");
659 ctx->
hidden.androidio.
offset = (*mEnv)->CallLongMethod(mEnv, inputStream, mid);
660 if (Android_JNI_ExceptionOccurred(
true)) {
664 mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
"getDeclaredLength",
"()J");
665 ctx->
hidden.androidio.
size = (*mEnv)->CallLongMethod(mEnv, inputStream, mid);
666 if (Android_JNI_ExceptionOccurred(
true)) {
670 mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
"getFileDescriptor",
"()Ljava/io/FileDescriptor;");
671 fd = (*mEnv)->CallObjectMethod(mEnv, inputStream, mid);
672 fdCls = (*mEnv)->GetObjectClass(mEnv, fd);
673 descriptor = (*mEnv)->GetFieldID(mEnv, fdCls,
"descriptor",
"I");
674 ctx->
hidden.androidio.fd = (*mEnv)->GetIntField(mEnv, fd, descriptor);
675 ctx->
hidden.androidio.assetFileDescriptorRef = (*mEnv)->NewGlobalRef(mEnv, inputStream);
686 ctx->
hidden.androidio.assetFileDescriptorRef =
NULL;
689 mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, assetManager),
690 "open",
"(Ljava/lang/String;I)Ljava/io/InputStream;");
691 inputStream = (*mEnv)->CallObjectMethod(mEnv, assetManager, mid, fileNameJString, 1 );
692 if (Android_JNI_ExceptionOccurred(
false)) {
696 ctx->
hidden.androidio.inputStreamRef = (*mEnv)->NewGlobalRef(mEnv, inputStream);
706 mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
708 ctx->
hidden.androidio.
size = (long)(*mEnv)->CallIntMethod(mEnv, inputStream, mid);
709 if (Android_JNI_ExceptionOccurred(
false)) {
714 channels = (*mEnv)->FindClass(mEnv,
"java/nio/channels/Channels");
715 mid = (*mEnv)->GetStaticMethodID(mEnv, channels,
717 "(Ljava/io/InputStream;)Ljava/nio/channels/ReadableByteChannel;");
718 readableByteChannel = (*mEnv)->CallStaticObjectMethod(
719 mEnv, channels, mid, inputStream);
720 if (Android_JNI_ExceptionOccurred(
false)) {
724 ctx->
hidden.androidio.readableByteChannelRef =
725 (*mEnv)->NewGlobalRef(mEnv, readableByteChannel);
728 mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, readableByteChannel),
729 "read",
"(Ljava/nio/ByteBuffer;)I");
730 ctx->
hidden.androidio.readMethod = mid;
737 (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->
hidden.androidio.fileNameRef);
739 if(ctx->
hidden.androidio.inputStreamRef !=
NULL) {
740 (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->
hidden.androidio.inputStreamRef);
743 if(ctx->
hidden.androidio.readableByteChannelRef !=
NULL) {
744 (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->
hidden.androidio.readableByteChannelRef);
747 if(ctx->
hidden.androidio.assetFileDescriptorRef !=
NULL) {
748 (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->
hidden.androidio.assetFileDescriptorRef);
753 LocalReferenceHolder_Cleanup(&refs);
758 const char* fileName,
const char*
mode)
760 struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
764 if (!LocalReferenceHolder_Init(&refs, mEnv)) {
765 LocalReferenceHolder_Cleanup(&refs);
770 LocalReferenceHolder_Cleanup(&refs);
774 jstring fileNameJString = (*mEnv)->NewStringUTF(mEnv, fileName);
775 ctx->
hidden.androidio.fileNameRef = (*mEnv)->NewGlobalRef(mEnv, fileNameJString);
777 ctx->
hidden.androidio.readableByteChannelRef =
NULL;
779 ctx->
hidden.androidio.assetFileDescriptorRef =
NULL;
781 retval = Internal_Android_JNI_FileOpen(ctx);
782 LocalReferenceHolder_Cleanup(&refs);
787 size_t size,
size_t maxnum)
789 struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
791 if (ctx->
hidden.androidio.assetFileDescriptorRef) {
792 size_t bytesMax = size * maxnum;
796 size_t result = read(ctx->
hidden.androidio.fd, buffer, bytesMax );
799 LocalReferenceHolder_Cleanup(&refs);
800 return result /
size;
802 LocalReferenceHolder_Cleanup(&refs);
805 jlong bytesRemaining = (jlong) (size * maxnum);
806 jlong bytesMax = (jlong) (ctx->
hidden.androidio.
size - ctx->
hidden.androidio.position);
810 if (bytesRemaining > bytesMax) bytesRemaining = bytesMax;
813 if (!LocalReferenceHolder_Init(&refs, mEnv)) {
814 LocalReferenceHolder_Cleanup(&refs);
818 jobject readableByteChannel = (jobject)ctx->
hidden.androidio.readableByteChannelRef;
819 jmethodID readMethod = (jmethodID)ctx->
hidden.androidio.readMethod;
820 jobject byteBuffer = (*mEnv)->NewDirectByteBuffer(mEnv, buffer, bytesRemaining);
822 while (bytesRemaining > 0) {
824 int result = (*mEnv)->CallIntMethod(mEnv, readableByteChannel, readMethod, byteBuffer);
826 if (Android_JNI_ExceptionOccurred(
false)) {
827 LocalReferenceHolder_Cleanup(&refs);
839 LocalReferenceHolder_Cleanup(&refs);
840 return bytesRead /
size;
845 size_t size,
size_t num)
847 SDL_SetError(
"Cannot write to Android package filesystem");
851 static int Internal_Android_JNI_FileClose(
SDL_RWops* ctx,
bool release)
853 struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
858 if (!LocalReferenceHolder_Init(&refs, mEnv)) {
859 LocalReferenceHolder_Cleanup(&refs);
860 return SDL_SetError(
"Failed to allocate enough JVM local references");
865 (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->
hidden.androidio.fileNameRef);
868 if (ctx->
hidden.androidio.assetFileDescriptorRef) {
869 jobject inputStream = (jobject)ctx->
hidden.androidio.assetFileDescriptorRef;
870 jmethodID mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
872 (*mEnv)->CallVoidMethod(mEnv, inputStream, mid);
873 (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->
hidden.androidio.assetFileDescriptorRef);
874 if (Android_JNI_ExceptionOccurred(
false)) {
879 jobject inputStream = (jobject)ctx->
hidden.androidio.inputStreamRef;
882 jmethodID mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, inputStream),
884 (*mEnv)->CallVoidMethod(mEnv, inputStream, mid);
885 (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->
hidden.androidio.inputStreamRef);
886 (*mEnv)->DeleteGlobalRef(mEnv, (jobject)ctx->
hidden.androidio.readableByteChannelRef);
887 if (Android_JNI_ExceptionOccurred(
false)) {
897 LocalReferenceHolder_Cleanup(&refs);
909 if (ctx->
hidden.androidio.assetFileDescriptorRef) {
916 offset += ctx->
hidden.androidio.position;
929 if (ret == -1)
return -1;
949 if (newPosition < 0) {
952 if (newPosition > ctx->
hidden.androidio.
size) {
956 Sint64 movement = newPosition - ctx->
hidden.androidio.position;
958 unsigned char buffer[4096];
961 while (movement > 0) {
963 if (amount > movement) {
975 }
else if (movement < 0) {
978 Internal_Android_JNI_FileClose(ctx,
false);
979 Internal_Android_JNI_FileOpen(ctx);
984 return ctx->
hidden.androidio.position;
990 return Internal_Android_JNI_FileClose(ctx,
true);
994 static jobject Android_JNI_GetSystemServiceObject(
const char*
name)
996 struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
998 jobject retval =
NULL;
1000 if (!LocalReferenceHolder_Init(&refs, env)) {
1001 LocalReferenceHolder_Cleanup(&refs);
1005 jstring service = (*env)->NewStringUTF(env, name);
1009 mid = (*env)->GetStaticMethodID(env, mActivityClass,
"getContext",
"()Landroid/content/Context;");
1010 jobject context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
1012 mid = (*env)->GetMethodID(env, mActivityClass,
"getSystemService",
"(Ljava/lang/String;)Ljava/lang/Object;");
1013 jobject manager = (*env)->CallObjectMethod(env, context, mid, service);
1015 (*env)->DeleteLocalRef(env, service);
1017 retval = manager ? (*env)->NewGlobalRef(env, manager) :
NULL;
1018 LocalReferenceHolder_Cleanup(&refs);
1022 #define SETUP_CLIPBOARD(error) \
1023 struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); \
1024 JNIEnv* env = Android_JNI_GetEnv(); \
1025 if (!LocalReferenceHolder_Init(&refs, env)) { \
1026 LocalReferenceHolder_Cleanup(&refs); \
1029 jobject clipboard = Android_JNI_GetSystemServiceObject("clipboard"); \
1031 LocalReferenceHolder_Cleanup(&refs); \
1035 #define CLEANUP_CLIPBOARD() \
1036 LocalReferenceHolder_Cleanup(&refs);
1042 jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "setText", "(Ljava/lang/CharSequence;)V");
1043 jstring
string = (*env)->NewStringUTF(env, text);
1044 (*env)->CallVoidMethod(env, clipboard, mid,
string);
1045 (*env)->DeleteGlobalRef(env, clipboard);
1046 (*env)->DeleteLocalRef(env,
string);
1048 CLEANUP_CLIPBOARD();
1057 jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "getText", "()Ljava/lang/CharSequence;");
1058 jobject sequence = (*env)->CallObjectMethod(env, clipboard, mid);
1059 (*env)->DeleteGlobalRef(env, clipboard);
1061 mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, sequence),
"toString",
"()Ljava/lang/String;");
1062 jstring
string = (jstring)((*env)->CallObjectMethod(env, sequence, mid));
1063 const char* utf = (*env)->GetStringUTFChars(env,
string, 0);
1066 (*env)->ReleaseStringUTFChars(env,
string, utf);
1068 CLEANUP_CLIPBOARD();
1074 CLEANUP_CLIPBOARD();
1083 jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "hasText", "()Z");
1084 jboolean has = (*env)->CallBooleanMethod(env, clipboard, mid);
1085 (*env)->DeleteGlobalRef(env, clipboard);
1087 CLEANUP_CLIPBOARD();
1099 struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
1101 if (!LocalReferenceHolder_Init(&refs, env)) {
1102 LocalReferenceHolder_Cleanup(&refs);
1108 mid = (*env)->GetStaticMethodID(env, mActivityClass,
"getContext",
"()Landroid/content/Context;");
1109 jobject context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
1111 jstring action = (*env)->NewStringUTF(env,
"android.intent.action.BATTERY_CHANGED");
1113 jclass cls = (*env)->FindClass(env,
"android/content/IntentFilter");
1115 mid = (*env)->GetMethodID(env, cls,
"<init>",
"(Ljava/lang/String;)V");
1116 jobject
filter = (*env)->NewObject(env, cls, mid, action);
1118 (*env)->DeleteLocalRef(env, action);
1120 mid = (*env)->GetMethodID(env, mActivityClass,
"registerReceiver",
"(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;");
1121 jobject intent = (*env)->CallObjectMethod(env, context, mid,
NULL, filter);
1123 (*env)->DeleteLocalRef(env, filter);
1125 cls = (*env)->GetObjectClass(env, intent);
1128 jmethodID imid = (*env)->GetMethodID(env, cls,
"getIntExtra",
"(Ljava/lang/String;I)I");
1130 #define GET_INT_EXTRA(var, key) \
1131 iname = (*env)->NewStringUTF(env, key); \
1132 int var = (*env)->CallIntMethod(env, intent, imid, iname, -1); \
1133 (*env)->DeleteLocalRef(env, iname);
1136 jmethodID bmid = (*env)->GetMethodID(env, cls,
"getBooleanExtra",
"(Ljava/lang/String;Z)Z");
1138 #define GET_BOOL_EXTRA(var, key) \
1139 bname = (*env)->NewStringUTF(env, key); \
1140 int var = (*env)->CallBooleanMethod(env, intent, bmid, bname, JNI_FALSE); \
1141 (*env)->DeleteLocalRef(env, bname);
1144 GET_INT_EXTRA(plug,
"plugged")
1146 LocalReferenceHolder_Cleanup(&refs);
1151 *plugged = (0 < plug) ? 1 : 0;
1155 GET_INT_EXTRA(status,
"status")
1157 LocalReferenceHolder_Cleanup(&refs);
1161 *charged = (status == 5) ? 1 : 0;
1165 GET_BOOL_EXTRA(present,
"present")
1166 *battery = present ? 1 : 0;
1174 GET_INT_EXTRA(
level,
"level")
1175 GET_INT_EXTRA(
scale, "scale")
1176 if ((
level == -1) || (scale == -1)) {
1177 LocalReferenceHolder_Cleanup(&refs);
1183 (*env)->DeleteLocalRef(env, intent);
1185 LocalReferenceHolder_Cleanup(&refs);
1196 jmethodID mid = (*env)->GetStaticMethodID(env, mActivityClass,
"sendMessage",
"(II)Z");
1200 jboolean success = (*env)->CallStaticBooleanMethod(env, mActivityClass, mid, command, param);
1201 return success ? 0 : -1;
1211 jmethodID mid = (*env)->GetStaticMethodID(env, mActivityClass,
"showTextInput",
"(IIII)Z");
1215 (*env)->CallStaticBooleanMethod(env, mActivityClass, mid,
1225 const int COMMAND_TEXTEDIT_HIDE = 3;
1236 void *SDL_AndroidGetJNIEnv()
1243 void *SDL_AndroidGetActivity()
1255 mid = (*env)->GetStaticMethodID(env, mActivityClass,
1256 "getContext",
"()Landroid/content/Context;");
1257 return (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
1260 const char * SDL_AndroidGetInternalStoragePath()
1262 static char *s_AndroidInternalFilesPath =
NULL;
1264 if (!s_AndroidInternalFilesPath) {
1265 struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
1273 if (!LocalReferenceHolder_Init(&refs, env)) {
1274 LocalReferenceHolder_Cleanup(&refs);
1279 mid = (*env)->GetStaticMethodID(env, mActivityClass,
1280 "getContext",
"()Landroid/content/Context;");
1281 context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
1284 mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
1285 "getFilesDir",
"()Ljava/io/File;");
1286 fileObject = (*env)->CallObjectMethod(env, context, mid);
1289 LocalReferenceHolder_Cleanup(&refs);
1294 mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, fileObject),
1295 "getAbsolutePath",
"()Ljava/lang/String;");
1296 pathString = (jstring)(*env)->CallObjectMethod(env, fileObject, mid);
1298 path = (*env)->GetStringUTFChars(env, pathString,
NULL);
1299 s_AndroidInternalFilesPath =
SDL_strdup(path);
1300 (*env)->ReleaseStringUTFChars(env, pathString, path);
1302 LocalReferenceHolder_Cleanup(&refs);
1304 return s_AndroidInternalFilesPath;
1307 int SDL_AndroidGetExternalStorageState()
1309 struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
1312 jstring stateString;
1317 if (!LocalReferenceHolder_Init(&refs, env)) {
1318 LocalReferenceHolder_Cleanup(&refs);
1322 cls = (*env)->FindClass(env,
"android/os/Environment");
1323 mid = (*env)->GetStaticMethodID(env, cls,
1324 "getExternalStorageState",
"()Ljava/lang/String;");
1325 stateString = (jstring)(*env)->CallStaticObjectMethod(env, cls, mid);
1327 state = (*env)->GetStringUTFChars(env, stateString,
NULL);
1330 __android_log_print(ANDROID_LOG_INFO,
"SDL",
"external storage state: %s", state);
1333 stateFlags = SDL_ANDROID_EXTERNAL_STORAGE_READ |
1334 SDL_ANDROID_EXTERNAL_STORAGE_WRITE;
1335 }
else if (
SDL_strcmp(state,
"mounted_ro") == 0) {
1336 stateFlags = SDL_ANDROID_EXTERNAL_STORAGE_READ;
1340 (*env)->ReleaseStringUTFChars(env, stateString, state);
1342 LocalReferenceHolder_Cleanup(&refs);
1346 const char * SDL_AndroidGetExternalStoragePath()
1348 static char *s_AndroidExternalFilesPath =
NULL;
1350 if (!s_AndroidExternalFilesPath) {
1351 struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
1359 if (!LocalReferenceHolder_Init(&refs, env)) {
1360 LocalReferenceHolder_Cleanup(&refs);
1365 mid = (*env)->GetStaticMethodID(env, mActivityClass,
1366 "getContext",
"()Landroid/content/Context;");
1367 context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
1370 mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context),
1371 "getExternalFilesDir",
"(Ljava/lang/String;)Ljava/io/File;");
1372 fileObject = (*env)->CallObjectMethod(env, context, mid,
NULL);
1375 LocalReferenceHolder_Cleanup(&refs);
1380 mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, fileObject),
1381 "getAbsolutePath",
"()Ljava/lang/String;");
1382 pathString = (jstring)(*env)->CallObjectMethod(env, fileObject, mid);
1384 path = (*env)->GetStringUTFChars(env, pathString,
NULL);
1385 s_AndroidExternalFilesPath =
SDL_strdup(path);
1386 (*env)->ReleaseStringUTFChars(env, pathString, path);
1388 LocalReferenceHolder_Cleanup(&refs);
1390 return s_AndroidExternalFilesPath;
int Android_JNI_FileClose(SDL_RWops *ctx)
int Android_OnKeyUp(int keycode)
int Android_JNI_SendMessage(int command, int param)
void Android_JNI_ShowTextInput(SDL_Rect *inputRect)
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
GLenum GLsizei const void * pathString
SDL_bool Android_JNI_HasClipboardText()
SDL_bool Android_JNI_GetAccelerometerValues(float values[3])
void Android_JNI_SetActivityTitle(const char *title)
char * Android_JNI_GetClipboardText()
int Android_OnKeyDown(int keycode)
EGLSurface EGLint EGLint EGLint EGLint height
void Android_OnTouch(int touch_device_id_in, int pointer_finger_id_in, int action, float x, float y, float p)
EGLImageKHR EGLint * name
void Android_JNI_HideTextInput()
int SDL_SendWindowEvent(SDL_Window *window, Uint8 windowevent, int data1, int data2)
GLsizei const GLchar *const * path
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)
EGLContext EGLenum EGLClientBuffer buffer
DECLSPEC void SDLCALL SDL_StopTextInput(void)
Stop receiving any text input events. This function will hide the on-screen keyboard if supported...
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
EGLSurface EGLint EGLint EGLint width
int SDL_SendKeyboardText(const char *text)
DECLSPEC char *SDLCALL SDL_strdup(const char *str)
DECLSPEC int SDLCALL SDL_Error(SDL_errorcode code)
DECLSPEC Uint32 SDLCALL SDL_SemValue(SDL_sem *sem)
void Android_JNI_CloseAudioDevice()
DECLSPEC void SDLCALL SDL_FreeRW(SDL_RWops *area)
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
GLint GLenum GLsizei GLsizei GLsizei GLint GLenum format
DECLSPEC int SDLCALL SDL_SetError(const char *fmt,...)
void Android_SetScreenResolution(int width, int height, Uint32 format)
DECLSPEC int SDLCALL SDL_strcmp(const char *str1, const char *str2)
GLenum GLenum GLenum GLenum GLenum scale
void * Android_JNI_GetAudioBuffer()
#define SDL_assert(condition)
EGLSurface EGLint EGLint y
ANativeWindow * Android_JNI_GetNativeWindow(void)
DECLSPEC void SDLCALL SDL_Log(const char *fmt,...)
Log a message with SDL_LOG_CATEGORY_APPLICATION and SDL_LOG_PRIORITY_INFO.
EGLSurface EGLint void ** value
SDL_sem * Android_ResumeSem
size_t Android_JNI_FileRead(SDL_RWops *ctx, void *buffer, size_t size, size_t maxnum)
void Android_JNI_WriteAudioBuffer()
union SDL_RWops::@74 hidden
int Android_JNI_SetClipboardText(const char *text)
GLint GLint GLint GLint z
SDL_VideoDevice * SDL_GetVideoDevice(void)
int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames)
int SDL_SendAppEvent(SDL_EventType eventType)
ANativeWindow * native_window
DECLSPEC int SDLCALL SDL_SemPost(SDL_sem *sem)
int64_t Sint64
A signed 64-bit integer type.
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 GLsizei const GLuint64 * values
void Android_JNI_SwapWindow()
DECLSPEC void SDLCALL SDL_GL_SwapWindow(SDL_Window *window)
Swap the OpenGL buffers for a window, if double-buffering is supported.
Sint64 Android_JNI_FileSize(SDL_RWops *ctx)