src/core/android/SDL_android.c
changeset 10080 426de7645465
parent 10032 e661304722ce
child 10199 6ab154366a9b
equal deleted inserted replaced
10063:c5e3a4f88be9 10080:426de7645465
   559 
   559 
   560 int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames)
   560 int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames)
   561 {
   561 {
   562     jboolean audioBufferStereo;
   562     jboolean audioBufferStereo;
   563     int audioBufferFrames;
   563     int audioBufferFrames;
       
   564     jboolean isCopy;
   564 
   565 
   565     JNIEnv *env = Android_JNI_GetEnv();
   566     JNIEnv *env = Android_JNI_GetEnv();
   566 
   567 
   567     if (!env) {
   568     if (!env) {
   568         LOGE("callback_handler: failed to attach current thread");
   569         LOGE("callback_handler: failed to attach current thread");
   600     if (audioBuffer == NULL) {
   601     if (audioBuffer == NULL) {
   601         __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: could not allocate an audio buffer!");
   602         __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: could not allocate an audio buffer!");
   602         return 0;
   603         return 0;
   603     }
   604     }
   604 
   605 
   605     jboolean isCopy = JNI_FALSE;
   606     isCopy = JNI_FALSE;
   606     if (audioBuffer16Bit) {
   607     if (audioBuffer16Bit) {
   607         audioBufferPinned = (*env)->GetShortArrayElements(env, (jshortArray)audioBuffer, &isCopy);
   608         audioBufferPinned = (*env)->GetShortArrayElements(env, (jshortArray)audioBuffer, &isCopy);
   608         audioBufferFrames = (*env)->GetArrayLength(env, (jshortArray)audioBuffer);
   609         audioBufferFrames = (*env)->GetArrayLength(env, (jshortArray)audioBuffer);
   609     } else {
   610     } else {
   610         audioBufferPinned = (*env)->GetByteArrayElements(env, (jbyteArray)audioBuffer, &isCopy);
   611         audioBufferPinned = (*env)->GetByteArrayElements(env, (jbyteArray)audioBuffer, &isCopy);
   652 
   653 
   653 /* Test for an exception and call SDL_SetError with its detail if one occurs */
   654 /* Test for an exception and call SDL_SetError with its detail if one occurs */
   654 /* If the parameter silent is truthy then SDL_SetError() will not be called. */
   655 /* If the parameter silent is truthy then SDL_SetError() will not be called. */
   655 static SDL_bool Android_JNI_ExceptionOccurred(SDL_bool silent)
   656 static SDL_bool Android_JNI_ExceptionOccurred(SDL_bool silent)
   656 {
   657 {
       
   658     JNIEnv *mEnv = Android_JNI_GetEnv();
       
   659     jthrowable exception;
       
   660 
   657     SDL_assert(LocalReferenceHolder_IsActive());
   661     SDL_assert(LocalReferenceHolder_IsActive());
   658     JNIEnv *mEnv = Android_JNI_GetEnv();
   662 
   659 
   663     exception = (*mEnv)->ExceptionOccurred(mEnv);
   660     jthrowable exception = (*mEnv)->ExceptionOccurred(mEnv);
       
   661     if (exception != NULL) {
   664     if (exception != NULL) {
   662         jmethodID mid;
   665         jmethodID mid;
   663 
   666 
   664         /* Until this happens most JNI operations have undefined behaviour */
   667         /* Until this happens most JNI operations have undefined behaviour */
   665         (*mEnv)->ExceptionClear(mEnv);
   668         (*mEnv)->ExceptionClear(mEnv);
   666 
   669 
   667         if (!silent) {
   670         if (!silent) {
   668             jclass exceptionClass = (*mEnv)->GetObjectClass(mEnv, exception);
   671             jclass exceptionClass = (*mEnv)->GetObjectClass(mEnv, exception);
   669             jclass classClass = (*mEnv)->FindClass(mEnv, "java/lang/Class");
   672             jclass classClass = (*mEnv)->FindClass(mEnv, "java/lang/Class");
       
   673             jstring exceptionName;
       
   674             const char* exceptionNameUTF8;
       
   675             jstring exceptionMessage;
   670 
   676 
   671             mid = (*mEnv)->GetMethodID(mEnv, classClass, "getName", "()Ljava/lang/String;");
   677             mid = (*mEnv)->GetMethodID(mEnv, classClass, "getName", "()Ljava/lang/String;");
   672             jstring exceptionName = (jstring)(*mEnv)->CallObjectMethod(mEnv, exceptionClass, mid);
   678             exceptionName = (jstring)(*mEnv)->CallObjectMethod(mEnv, exceptionClass, mid);
   673             const char* exceptionNameUTF8 = (*mEnv)->GetStringUTFChars(mEnv, exceptionName, 0);
   679             exceptionNameUTF8 = (*mEnv)->GetStringUTFChars(mEnv, exceptionName, 0);
   674 
   680 
   675             mid = (*mEnv)->GetMethodID(mEnv, exceptionClass, "getMessage", "()Ljava/lang/String;");
   681             mid = (*mEnv)->GetMethodID(mEnv, exceptionClass, "getMessage", "()Ljava/lang/String;");
   676             jstring exceptionMessage = (jstring)(*mEnv)->CallObjectMethod(mEnv, exception, mid);
   682             exceptionMessage = (jstring)(*mEnv)->CallObjectMethod(mEnv, exception, mid);
   677 
   683 
   678             if (exceptionMessage != NULL) {
   684             if (exceptionMessage != NULL) {
   679                 const char* exceptionMessageUTF8 = (*mEnv)->GetStringUTFChars(mEnv, exceptionMessage, 0);
   685                 const char* exceptionMessageUTF8 = (*mEnv)->GetStringUTFChars(mEnv, exceptionMessage, 0);
   680                 SDL_SetError("%s: %s", exceptionNameUTF8, exceptionMessageUTF8);
   686                 SDL_SetError("%s: %s", exceptionNameUTF8, exceptionMessageUTF8);
   681                 (*mEnv)->ReleaseStringUTFChars(mEnv, exceptionMessage, exceptionMessageUTF8);
   687                 (*mEnv)->ReleaseStringUTFChars(mEnv, exceptionMessage, exceptionMessageUTF8);
   854         const char* fileName, const char* mode)
   860         const char* fileName, const char* mode)
   855 {
   861 {
   856     struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
   862     struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
   857     JNIEnv *mEnv = Android_JNI_GetEnv();
   863     JNIEnv *mEnv = Android_JNI_GetEnv();
   858     int retval;
   864     int retval;
       
   865     jstring fileNameJString;
   859 
   866 
   860     if (!LocalReferenceHolder_Init(&refs, mEnv)) {
   867     if (!LocalReferenceHolder_Init(&refs, mEnv)) {
   861         LocalReferenceHolder_Cleanup(&refs);        
   868         LocalReferenceHolder_Cleanup(&refs);        
   862         return -1;
   869         return -1;
   863     }
   870     }
   865     if (!ctx) {
   872     if (!ctx) {
   866         LocalReferenceHolder_Cleanup(&refs);
   873         LocalReferenceHolder_Cleanup(&refs);
   867         return -1;
   874         return -1;
   868     }
   875     }
   869 
   876 
   870     jstring fileNameJString = (*mEnv)->NewStringUTF(mEnv, fileName);
   877     fileNameJString = (*mEnv)->NewStringUTF(mEnv, fileName);
   871     ctx->hidden.androidio.fileNameRef = (*mEnv)->NewGlobalRef(mEnv, fileNameJString);
   878     ctx->hidden.androidio.fileNameRef = (*mEnv)->NewGlobalRef(mEnv, fileNameJString);
   872     ctx->hidden.androidio.inputStreamRef = NULL;
   879     ctx->hidden.androidio.inputStreamRef = NULL;
   873     ctx->hidden.androidio.readableByteChannelRef = NULL;
   880     ctx->hidden.androidio.readableByteChannelRef = NULL;
   874     ctx->hidden.androidio.readMethod = NULL;
   881     ctx->hidden.androidio.readMethod = NULL;
   875     ctx->hidden.androidio.assetFileDescriptorRef = NULL;
   882     ctx->hidden.androidio.assetFileDescriptorRef = NULL;
   884 {
   891 {
   885     struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
   892     struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
   886 
   893 
   887     if (ctx->hidden.androidio.assetFileDescriptorRef) {
   894     if (ctx->hidden.androidio.assetFileDescriptorRef) {
   888         size_t bytesMax = size * maxnum;
   895         size_t bytesMax = size * maxnum;
       
   896         size_t result;
   889         if (ctx->hidden.androidio.size != -1 /* UNKNOWN_LENGTH */ && ctx->hidden.androidio.position + bytesMax > ctx->hidden.androidio.size) {
   897         if (ctx->hidden.androidio.size != -1 /* UNKNOWN_LENGTH */ && ctx->hidden.androidio.position + bytesMax > ctx->hidden.androidio.size) {
   890             bytesMax = ctx->hidden.androidio.size - ctx->hidden.androidio.position;
   898             bytesMax = ctx->hidden.androidio.size - ctx->hidden.androidio.position;
   891         }
   899         }
   892         size_t result = read(ctx->hidden.androidio.fd, buffer, bytesMax );
   900         result = read(ctx->hidden.androidio.fd, buffer, bytesMax );
   893         if (result > 0) {
   901         if (result > 0) {
   894             ctx->hidden.androidio.position += result;
   902             ctx->hidden.androidio.position += result;
   895             LocalReferenceHolder_Cleanup(&refs);
   903             LocalReferenceHolder_Cleanup(&refs);
   896             return result / size;
   904             return result / size;
   897         }
   905         }
   899         return 0;
   907         return 0;
   900     } else {
   908     } else {
   901         jlong bytesRemaining = (jlong) (size * maxnum);
   909         jlong bytesRemaining = (jlong) (size * maxnum);
   902         jlong bytesMax = (jlong) (ctx->hidden.androidio.size -  ctx->hidden.androidio.position);
   910         jlong bytesMax = (jlong) (ctx->hidden.androidio.size -  ctx->hidden.androidio.position);
   903         int bytesRead = 0;
   911         int bytesRead = 0;
       
   912         JNIEnv *mEnv;
       
   913         jobject readableByteChannel;
       
   914         jmethodID readMethod;
       
   915         jobject byteBuffer;
   904 
   916 
   905         /* Don't read more bytes than those that remain in the file, otherwise we get an exception */
   917         /* Don't read more bytes than those that remain in the file, otherwise we get an exception */
   906         if (bytesRemaining >  bytesMax) bytesRemaining = bytesMax;
   918         if (bytesRemaining >  bytesMax) bytesRemaining = bytesMax;
   907 
   919 
   908         JNIEnv *mEnv = Android_JNI_GetEnv();
   920         mEnv = Android_JNI_GetEnv();
   909         if (!LocalReferenceHolder_Init(&refs, mEnv)) {
   921         if (!LocalReferenceHolder_Init(&refs, mEnv)) {
   910             LocalReferenceHolder_Cleanup(&refs);            
   922             LocalReferenceHolder_Cleanup(&refs);            
   911             return 0;
   923             return 0;
   912         }
   924         }
   913 
   925 
   914         jobject readableByteChannel = (jobject)ctx->hidden.androidio.readableByteChannelRef;
   926         readableByteChannel = (jobject)ctx->hidden.androidio.readableByteChannelRef;
   915         jmethodID readMethod = (jmethodID)ctx->hidden.androidio.readMethod;
   927         readMethod = (jmethodID)ctx->hidden.androidio.readMethod;
   916         jobject byteBuffer = (*mEnv)->NewDirectByteBuffer(mEnv, buffer, bytesRemaining);
   928         byteBuffer = (*mEnv)->NewDirectByteBuffer(mEnv, buffer, bytesRemaining);
   917 
   929 
   918         while (bytesRemaining > 0) {
   930         while (bytesRemaining > 0) {
   919             /* result = readableByteChannel.read(...); */
   931             /* result = readableByteChannel.read(...); */
   920             int result = (*mEnv)->CallIntMethod(mEnv, readableByteChannel, readMethod, byteBuffer);
   932             int result = (*mEnv)->CallIntMethod(mEnv, readableByteChannel, readMethod, byteBuffer);
   921 
   933 
  1001 }
  1013 }
  1002 
  1014 
  1003 Sint64 Android_JNI_FileSeek(SDL_RWops* ctx, Sint64 offset, int whence)
  1015 Sint64 Android_JNI_FileSeek(SDL_RWops* ctx, Sint64 offset, int whence)
  1004 {
  1016 {
  1005     if (ctx->hidden.androidio.assetFileDescriptorRef) {
  1017     if (ctx->hidden.androidio.assetFileDescriptorRef) {
       
  1018         off_t ret;
  1006         switch (whence) {
  1019         switch (whence) {
  1007             case RW_SEEK_SET:
  1020             case RW_SEEK_SET:
  1008                 if (ctx->hidden.androidio.size != -1 /* UNKNOWN_LENGTH */ && offset > ctx->hidden.androidio.size) offset = ctx->hidden.androidio.size;
  1021                 if (ctx->hidden.androidio.size != -1 /* UNKNOWN_LENGTH */ && offset > ctx->hidden.androidio.size) offset = ctx->hidden.androidio.size;
  1009                 offset += ctx->hidden.androidio.offset;
  1022                 offset += ctx->hidden.androidio.offset;
  1010                 break;
  1023                 break;
  1019             default:
  1032             default:
  1020                 return SDL_SetError("Unknown value for 'whence'");
  1033                 return SDL_SetError("Unknown value for 'whence'");
  1021         }
  1034         }
  1022         whence = SEEK_SET;
  1035         whence = SEEK_SET;
  1023 
  1036 
  1024         off_t ret = lseek(ctx->hidden.androidio.fd, (off_t)offset, SEEK_SET);
  1037         ret = lseek(ctx->hidden.androidio.fd, (off_t)offset, SEEK_SET);
  1025         if (ret == -1) return -1;
  1038         if (ret == -1) return -1;
  1026         ctx->hidden.androidio.position = ret - ctx->hidden.androidio.offset;
  1039         ctx->hidden.androidio.position = ret - ctx->hidden.androidio.offset;
  1027     } else {
  1040     } else {
  1028         Sint64 newPosition;
  1041         Sint64 newPosition;
       
  1042         Sint64 movement;
  1029 
  1043 
  1030         switch (whence) {
  1044         switch (whence) {
  1031             case RW_SEEK_SET:
  1045             case RW_SEEK_SET:
  1032                 newPosition = offset;
  1046                 newPosition = offset;
  1033                 break;
  1047                 break;
  1047         }
  1061         }
  1048         if (newPosition > ctx->hidden.androidio.size) {
  1062         if (newPosition > ctx->hidden.androidio.size) {
  1049             newPosition = ctx->hidden.androidio.size;
  1063             newPosition = ctx->hidden.androidio.size;
  1050         }
  1064         }
  1051 
  1065 
  1052         Sint64 movement = newPosition - ctx->hidden.androidio.position;
  1066         movement = newPosition - ctx->hidden.androidio.position;
  1053         if (movement > 0) {
  1067         if (movement > 0) {
  1054             unsigned char buffer[4096];
  1068             unsigned char buffer[4096];
  1055 
  1069 
  1056             /* The easy case where we're seeking forwards */
  1070             /* The easy case where we're seeking forwards */
  1057             while (movement > 0) {
  1071             while (movement > 0) {
  1058                 Sint64 amount = sizeof (buffer);
  1072                 Sint64 amount = sizeof (buffer);
       
  1073                 size_t result;
  1059                 if (amount > movement) {
  1074                 if (amount > movement) {
  1060                     amount = movement;
  1075                     amount = movement;
  1061                 }
  1076                 }
  1062                 size_t result = Android_JNI_FileRead(ctx, buffer, 1, amount);
  1077                 result = Android_JNI_FileRead(ctx, buffer, 1, amount);
  1063                 if (result <= 0) {
  1078                 if (result <= 0) {
  1064                     /* Failed to read/skip the required amount, so fail */
  1079                     /* Failed to read/skip the required amount, so fail */
  1065                     return -1;
  1080                     return -1;
  1066                 }
  1081                 }
  1067 
  1082 
  1090 static jobject Android_JNI_GetSystemServiceObject(const char* name)
  1105 static jobject Android_JNI_GetSystemServiceObject(const char* name)
  1091 {
  1106 {
  1092     struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
  1107     struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
  1093     JNIEnv* env = Android_JNI_GetEnv();
  1108     JNIEnv* env = Android_JNI_GetEnv();
  1094     jobject retval = NULL;
  1109     jobject retval = NULL;
       
  1110     jstring service;
       
  1111     jmethodID mid;
       
  1112     jobject context;
       
  1113     jobject manager;
  1095 
  1114 
  1096     if (!LocalReferenceHolder_Init(&refs, env)) {
  1115     if (!LocalReferenceHolder_Init(&refs, env)) {
  1097         LocalReferenceHolder_Cleanup(&refs);
  1116         LocalReferenceHolder_Cleanup(&refs);
  1098         return NULL;
  1117         return NULL;
  1099     }
  1118     }
  1100 
  1119 
  1101     jstring service = (*env)->NewStringUTF(env, name);
  1120     service = (*env)->NewStringUTF(env, name);
  1102 
       
  1103     jmethodID mid;
       
  1104 
  1121 
  1105     mid = (*env)->GetStaticMethodID(env, mActivityClass, "getContext", "()Landroid/content/Context;");
  1122     mid = (*env)->GetStaticMethodID(env, mActivityClass, "getContext", "()Landroid/content/Context;");
  1106     jobject context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
  1123     context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
  1107 
  1124 
  1108     mid = (*env)->GetMethodID(env, mActivityClass, "getSystemServiceFromUiThread", "(Ljava/lang/String;)Ljava/lang/Object;");
  1125     mid = (*env)->GetMethodID(env, mActivityClass, "getSystemServiceFromUiThread", "(Ljava/lang/String;)Ljava/lang/Object;");
  1109     jobject manager = (*env)->CallObjectMethod(env, context, mid, service);
  1126     manager = (*env)->CallObjectMethod(env, context, mid, service);
  1110 
  1127 
  1111     (*env)->DeleteLocalRef(env, service);
  1128     (*env)->DeleteLocalRef(env, service);
  1112 
  1129 
  1113     retval = manager ? (*env)->NewGlobalRef(env, manager) : NULL;
  1130     retval = manager ? (*env)->NewGlobalRef(env, manager) : NULL;
  1114     LocalReferenceHolder_Cleanup(&refs);
  1131     LocalReferenceHolder_Cleanup(&refs);
  1116 }
  1133 }
  1117 
  1134 
  1118 #define SETUP_CLIPBOARD(error) \
  1135 #define SETUP_CLIPBOARD(error) \
  1119     struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); \
  1136     struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); \
  1120     JNIEnv* env = Android_JNI_GetEnv(); \
  1137     JNIEnv* env = Android_JNI_GetEnv(); \
       
  1138     jobject clipboard; \
  1121     if (!LocalReferenceHolder_Init(&refs, env)) { \
  1139     if (!LocalReferenceHolder_Init(&refs, env)) { \
  1122         LocalReferenceHolder_Cleanup(&refs); \
  1140         LocalReferenceHolder_Cleanup(&refs); \
  1123         return error; \
  1141         return error; \
  1124     } \
  1142     } \
  1125     jobject clipboard = Android_JNI_GetSystemServiceObject("clipboard"); \
  1143     clipboard = Android_JNI_GetSystemServiceObject("clipboard"); \
  1126     if (!clipboard) { \
  1144     if (!clipboard) { \
  1127         LocalReferenceHolder_Cleanup(&refs); \
  1145         LocalReferenceHolder_Cleanup(&refs); \
  1128         return error; \
  1146         return error; \
  1129     }
  1147     }
  1130 
  1148 
  1131 #define CLEANUP_CLIPBOARD() \
  1149 #define CLEANUP_CLIPBOARD() \
  1132     LocalReferenceHolder_Cleanup(&refs);
  1150     LocalReferenceHolder_Cleanup(&refs);
  1133 
  1151 
  1134 int Android_JNI_SetClipboardText(const char* text)
  1152 int Android_JNI_SetClipboardText(const char* text)
  1135 {
  1153 {
       
  1154     /* Watch out for C89 scoping rules because of the macro */
  1136     SETUP_CLIPBOARD(-1)
  1155     SETUP_CLIPBOARD(-1)
  1137 
  1156 
  1138     jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "setText", "(Ljava/lang/CharSequence;)V");
  1157     /* Nest the following in a scope to avoid C89 declaration rules triggered by the macro */
  1139     jstring string = (*env)->NewStringUTF(env, text);
  1158     {
  1140     (*env)->CallVoidMethod(env, clipboard, mid, string);
  1159         jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "setText", "(Ljava/lang/CharSequence;)V");
  1141     (*env)->DeleteGlobalRef(env, clipboard);
  1160         jstring string = (*env)->NewStringUTF(env, text);
  1142     (*env)->DeleteLocalRef(env, string);
  1161         (*env)->CallVoidMethod(env, clipboard, mid, string);
  1143 
  1162         (*env)->DeleteGlobalRef(env, clipboard);
       
  1163         (*env)->DeleteLocalRef(env, string);
       
  1164     }
  1144     CLEANUP_CLIPBOARD();
  1165     CLEANUP_CLIPBOARD();
  1145 
  1166 
  1146     return 0;
  1167     return 0;
  1147 }
  1168 }
  1148 
  1169 
  1149 char* Android_JNI_GetClipboardText(void)
  1170 char* Android_JNI_GetClipboardText(void)
  1150 {
  1171 {
       
  1172     /* Watch out for C89 scoping rules because of the macro */
  1151     SETUP_CLIPBOARD(SDL_strdup(""))
  1173     SETUP_CLIPBOARD(SDL_strdup(""))
  1152 
  1174 
  1153     jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "getText", "()Ljava/lang/CharSequence;");
  1175     /* Nest the following in a scope to avoid C89 declaration rules triggered by the macro */
  1154     jobject sequence = (*env)->CallObjectMethod(env, clipboard, mid);
  1176     {
  1155     (*env)->DeleteGlobalRef(env, clipboard);
  1177         jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "getText", "()Ljava/lang/CharSequence;");
  1156     if (sequence) {
  1178         jobject sequence = (*env)->CallObjectMethod(env, clipboard, mid);
  1157         mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, sequence), "toString", "()Ljava/lang/String;");
  1179         (*env)->DeleteGlobalRef(env, clipboard);
  1158         jstring string = (jstring)((*env)->CallObjectMethod(env, sequence, mid));
  1180         if (sequence) {
  1159         const char* utf = (*env)->GetStringUTFChars(env, string, 0);
  1181             jstring string;
  1160         if (utf) {
  1182             const char* utf;
  1161             char* text = SDL_strdup(utf);
  1183             mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, sequence), "toString", "()Ljava/lang/String;");
  1162             (*env)->ReleaseStringUTFChars(env, string, utf);
  1184             string = (jstring)((*env)->CallObjectMethod(env, sequence, mid));
  1163 
  1185             utf = (*env)->GetStringUTFChars(env, string, 0);
  1164             CLEANUP_CLIPBOARD();
  1186             if (utf) {
  1165 
  1187                 char* text = SDL_strdup(utf);
  1166             return text;
  1188                 (*env)->ReleaseStringUTFChars(env, string, utf);
  1167         }
  1189 
  1168     }
  1190                 CLEANUP_CLIPBOARD();
  1169 
  1191 
       
  1192                 return text;
       
  1193             }
       
  1194         }
       
  1195     }
  1170     CLEANUP_CLIPBOARD();    
  1196     CLEANUP_CLIPBOARD();    
  1171 
  1197 
  1172     return SDL_strdup("");
  1198     return SDL_strdup("");
  1173 }
  1199 }
  1174 
  1200 
  1175 SDL_bool Android_JNI_HasClipboardText(void)
  1201 SDL_bool Android_JNI_HasClipboardText(void)
  1176 {
  1202 {
       
  1203     jmethodID mid;
       
  1204     jboolean has;
       
  1205     /* Watch out for C89 scoping rules because of the macro */
  1177     SETUP_CLIPBOARD(SDL_FALSE)
  1206     SETUP_CLIPBOARD(SDL_FALSE)
  1178 
  1207 
  1179     jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "hasText", "()Z");
  1208     mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "hasText", "()Z");
  1180     jboolean has = (*env)->CallBooleanMethod(env, clipboard, mid);
  1209     has = (*env)->CallBooleanMethod(env, clipboard, mid);
  1181     (*env)->DeleteGlobalRef(env, clipboard);
  1210     (*env)->DeleteGlobalRef(env, clipboard);
  1182 
  1211 
  1183     CLEANUP_CLIPBOARD();
  1212     CLEANUP_CLIPBOARD();
  1184     
  1213     
  1185     return has ? SDL_TRUE : SDL_FALSE;
  1214     return has ? SDL_TRUE : SDL_FALSE;
  1192  */
  1221  */
  1193 int Android_JNI_GetPowerInfo(int* plugged, int* charged, int* battery, int* seconds, int* percent)
  1222 int Android_JNI_GetPowerInfo(int* plugged, int* charged, int* battery, int* seconds, int* percent)
  1194 {
  1223 {
  1195     struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
  1224     struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__);
  1196     JNIEnv* env = Android_JNI_GetEnv();
  1225     JNIEnv* env = Android_JNI_GetEnv();
       
  1226     jmethodID mid;
       
  1227     jobject context;
       
  1228     jstring action;
       
  1229     jclass cls;
       
  1230     jobject filter;
       
  1231     jobject intent;
       
  1232     jstring iname;
       
  1233     jmethodID imid;
       
  1234     jstring bname;
       
  1235     jmethodID bmid;
  1197     if (!LocalReferenceHolder_Init(&refs, env)) {
  1236     if (!LocalReferenceHolder_Init(&refs, env)) {
  1198         LocalReferenceHolder_Cleanup(&refs);
  1237         LocalReferenceHolder_Cleanup(&refs);
  1199         return -1;
  1238         return -1;
  1200     }
  1239     }
  1201 
  1240 
  1202     jmethodID mid;
       
  1203 
  1241 
  1204     mid = (*env)->GetStaticMethodID(env, mActivityClass, "getContext", "()Landroid/content/Context;");
  1242     mid = (*env)->GetStaticMethodID(env, mActivityClass, "getContext", "()Landroid/content/Context;");
  1205     jobject context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
  1243     context = (*env)->CallStaticObjectMethod(env, mActivityClass, mid);
  1206 
  1244 
  1207     jstring action = (*env)->NewStringUTF(env, "android.intent.action.BATTERY_CHANGED");
  1245     action = (*env)->NewStringUTF(env, "android.intent.action.BATTERY_CHANGED");
  1208 
  1246 
  1209     jclass cls = (*env)->FindClass(env, "android/content/IntentFilter");
  1247     cls = (*env)->FindClass(env, "android/content/IntentFilter");
  1210 
  1248 
  1211     mid = (*env)->GetMethodID(env, cls, "<init>", "(Ljava/lang/String;)V");
  1249     mid = (*env)->GetMethodID(env, cls, "<init>", "(Ljava/lang/String;)V");
  1212     jobject filter = (*env)->NewObject(env, cls, mid, action);
  1250     filter = (*env)->NewObject(env, cls, mid, action);
  1213 
  1251 
  1214     (*env)->DeleteLocalRef(env, action);
  1252     (*env)->DeleteLocalRef(env, action);
  1215 
  1253 
  1216     mid = (*env)->GetMethodID(env, mActivityClass, "registerReceiver", "(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;");
  1254     mid = (*env)->GetMethodID(env, mActivityClass, "registerReceiver", "(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;");
  1217     jobject intent = (*env)->CallObjectMethod(env, context, mid, NULL, filter);
  1255     intent = (*env)->CallObjectMethod(env, context, mid, NULL, filter);
  1218 
  1256 
  1219     (*env)->DeleteLocalRef(env, filter);
  1257     (*env)->DeleteLocalRef(env, filter);
  1220 
  1258 
  1221     cls = (*env)->GetObjectClass(env, intent);
  1259     cls = (*env)->GetObjectClass(env, intent);
  1222 
  1260 
  1223     jstring iname;
  1261     imid = (*env)->GetMethodID(env, cls, "getIntExtra", "(Ljava/lang/String;I)I");
  1224     jmethodID imid = (*env)->GetMethodID(env, cls, "getIntExtra", "(Ljava/lang/String;I)I");
  1262 
  1225 
  1263     /* Watch out for C89 scoping rules because of the macro */
  1226 #define GET_INT_EXTRA(var, key) \
  1264 #define GET_INT_EXTRA(var, key) \
       
  1265     int var; \
  1227     iname = (*env)->NewStringUTF(env, key); \
  1266     iname = (*env)->NewStringUTF(env, key); \
  1228     int var = (*env)->CallIntMethod(env, intent, imid, iname, -1); \
  1267     var = (*env)->CallIntMethod(env, intent, imid, iname, -1); \
  1229     (*env)->DeleteLocalRef(env, iname);
  1268     (*env)->DeleteLocalRef(env, iname);
  1230 
  1269 
  1231     jstring bname;
  1270     bmid = (*env)->GetMethodID(env, cls, "getBooleanExtra", "(Ljava/lang/String;Z)Z");
  1232     jmethodID bmid = (*env)->GetMethodID(env, cls, "getBooleanExtra", "(Ljava/lang/String;Z)Z");
  1271 
  1233 
  1272     /* Watch out for C89 scoping rules because of the macro */
  1234 #define GET_BOOL_EXTRA(var, key) \
  1273 #define GET_BOOL_EXTRA(var, key) \
       
  1274     int var; \
  1235     bname = (*env)->NewStringUTF(env, key); \
  1275     bname = (*env)->NewStringUTF(env, key); \
  1236     int var = (*env)->CallBooleanMethod(env, intent, bmid, bname, JNI_FALSE); \
  1276     var = (*env)->CallBooleanMethod(env, intent, bmid, bname, JNI_FALSE); \
  1237     (*env)->DeleteLocalRef(env, bname);
  1277     (*env)->DeleteLocalRef(env, bname);
  1238 
  1278 
  1239     if (plugged) {
  1279     if (plugged) {
       
  1280         /* Watch out for C89 scoping rules because of the macro */
  1240         GET_INT_EXTRA(plug, "plugged") /* == BatteryManager.EXTRA_PLUGGED (API 5) */
  1281         GET_INT_EXTRA(plug, "plugged") /* == BatteryManager.EXTRA_PLUGGED (API 5) */
  1241         if (plug == -1) {
  1282         if (plug == -1) {
  1242             LocalReferenceHolder_Cleanup(&refs);
  1283             LocalReferenceHolder_Cleanup(&refs);
  1243             return -1;
  1284             return -1;
  1244         }
  1285         }
  1246         /* 2 == BatteryManager.BATTERY_PLUGGED_USB */
  1287         /* 2 == BatteryManager.BATTERY_PLUGGED_USB */
  1247         *plugged = (0 < plug) ? 1 : 0;
  1288         *plugged = (0 < plug) ? 1 : 0;
  1248     }
  1289     }
  1249 
  1290 
  1250     if (charged) {
  1291     if (charged) {
       
  1292         /* Watch out for C89 scoping rules because of the macro */
  1251         GET_INT_EXTRA(status, "status") /* == BatteryManager.EXTRA_STATUS (API 5) */
  1293         GET_INT_EXTRA(status, "status") /* == BatteryManager.EXTRA_STATUS (API 5) */
  1252         if (status == -1) {
  1294         if (status == -1) {
  1253             LocalReferenceHolder_Cleanup(&refs);
  1295             LocalReferenceHolder_Cleanup(&refs);
  1254             return -1;
  1296             return -1;
  1255         }
  1297         }
  1265     if (seconds) {
  1307     if (seconds) {
  1266         *seconds = -1; /* not possible */
  1308         *seconds = -1; /* not possible */
  1267     }
  1309     }
  1268 
  1310 
  1269     if (percent) {
  1311     if (percent) {
  1270         GET_INT_EXTRA(level, "level") /* == BatteryManager.EXTRA_LEVEL (API 5) */
  1312         int level;
  1271         GET_INT_EXTRA(scale, "scale") /* == BatteryManager.EXTRA_SCALE (API 5) */
  1313         int scale;
       
  1314         
       
  1315         /* Watch out for C89 scoping rules because of the macro */
       
  1316         {
       
  1317             GET_INT_EXTRA(level_temp, "level") /* == BatteryManager.EXTRA_LEVEL (API 5) */
       
  1318             level = level_temp;
       
  1319         }
       
  1320         /* Watch out for C89 scoping rules because of the macro */
       
  1321         {
       
  1322             GET_INT_EXTRA(scale_temp, "scale") /* == BatteryManager.EXTRA_SCALE (API 5) */
       
  1323             scale = scale_temp;
       
  1324         }
       
  1325         
  1272         if ((level == -1) || (scale == -1)) {
  1326         if ((level == -1) || (scale == -1)) {
  1273             LocalReferenceHolder_Cleanup(&refs);
  1327             LocalReferenceHolder_Cleanup(&refs);
  1274             return -1;
  1328             return -1;
  1275         }
  1329         }
  1276         *percent = level * 100 / scale;
  1330         *percent = level * 100 / scale;
  1319 
  1373 
  1320 /* sends message to be handled on the UI event dispatch thread */
  1374 /* sends message to be handled on the UI event dispatch thread */
  1321 int Android_JNI_SendMessage(int command, int param)
  1375 int Android_JNI_SendMessage(int command, int param)
  1322 {
  1376 {
  1323     JNIEnv *env = Android_JNI_GetEnv();
  1377     JNIEnv *env = Android_JNI_GetEnv();
       
  1378     jmethodID mid;
       
  1379     jboolean success;
  1324     if (!env) {
  1380     if (!env) {
  1325         return -1;
  1381         return -1;
  1326     }
  1382     }
  1327     jmethodID mid = (*env)->GetStaticMethodID(env, mActivityClass, "sendMessage", "(II)Z");
  1383     mid = (*env)->GetStaticMethodID(env, mActivityClass, "sendMessage", "(II)Z");
  1328     if (!mid) {
  1384     if (!mid) {
  1329         return -1;
  1385         return -1;
  1330     }
  1386     }
  1331     jboolean success = (*env)->CallStaticBooleanMethod(env, mActivityClass, mid, command, param);
  1387     success = (*env)->CallStaticBooleanMethod(env, mActivityClass, mid, command, param);
  1332     return success ? 0 : -1;
  1388     return success ? 0 : -1;
  1333 }
  1389 }
  1334 
  1390 
  1335 void Android_JNI_SuspendScreenSaver(SDL_bool suspend)
  1391 void Android_JNI_SuspendScreenSaver(SDL_bool suspend)
  1336 {
  1392 {
  1338 }
  1394 }
  1339 
  1395 
  1340 void Android_JNI_ShowTextInput(SDL_Rect *inputRect)
  1396 void Android_JNI_ShowTextInput(SDL_Rect *inputRect)
  1341 {
  1397 {
  1342     JNIEnv *env = Android_JNI_GetEnv();
  1398     JNIEnv *env = Android_JNI_GetEnv();
       
  1399     jmethodID mid;
  1343     if (!env) {
  1400     if (!env) {
  1344         return;
  1401         return;
  1345     }
  1402     }
  1346 
  1403 
  1347     jmethodID mid = (*env)->GetStaticMethodID(env, mActivityClass, "showTextInput", "(IIII)Z");
  1404     mid = (*env)->GetStaticMethodID(env, mActivityClass, "showTextInput", "(IIII)Z");
  1348     if (!mid) {
  1405     if (!mid) {
  1349         return;
  1406         return;
  1350     }
  1407     }
  1351     (*env)->CallStaticBooleanMethod(env, mActivityClass, mid,
  1408     (*env)->CallStaticBooleanMethod(env, mActivityClass, mid,
  1352                                inputRect->x,
  1409                                inputRect->x,