ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

jni.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #if defined(__ANDROID__)
00024 
00025 // Allow use of stuff in <time.h> and abort()
00026 #define FORBIDDEN_SYMBOL_EXCEPTION_time_h
00027 #define FORBIDDEN_SYMBOL_EXCEPTION_abort
00028 
00029 // Disable printf override in common/forbidden.h to avoid
00030 // clashes with log.h from the Android SDK.
00031 // That header file uses
00032 //   __attribute__ ((format(printf, 3, 4)))
00033 // which gets messed up by our override mechanism; this could
00034 // be avoided by either changing the Android SDK to use the equally
00035 // legal and valid
00036 //   __attribute__ ((format(printf, 3, 4)))
00037 // or by refining our printf override to use a varadic macro
00038 // (which then wouldn't be portable, though).
00039 // Anyway, for now we just disable the printf override globally
00040 // for the Android port
00041 #define FORBIDDEN_SYMBOL_EXCEPTION_printf
00042 
00043 #include "base/main.h"
00044 #include "base/version.h"
00045 #include "common/config-manager.h"
00046 #include "common/error.h"
00047 #include "common/textconsole.h"
00048 #include "engines/engine.h"
00049 
00050 #include "backends/platform/android/android.h"
00051 #include "backends/platform/android/asset-archive.h"
00052 #include "backends/platform/android/jni.h"
00053 
00054 __attribute__ ((visibility("default")))
00055 jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
00056     return JNI::onLoad(vm);
00057 }
00058 
00059 JavaVM *JNI::_vm = 0;
00060 jobject JNI::_jobj = 0;
00061 jobject JNI::_jobj_audio_track = 0;
00062 jobject JNI::_jobj_egl = 0;
00063 jobject JNI::_jobj_egl_display = 0;
00064 jobject JNI::_jobj_egl_surface = 0;
00065 
00066 Common::Archive *JNI::_asset_archive = 0;
00067 OSystem_Android *JNI::_system = 0;
00068 
00069 bool JNI::pause = false;
00070 sem_t JNI::pause_sem = { 0 };
00071 
00072 int JNI::surface_changeid = 0;
00073 int JNI::egl_surface_width = 0;
00074 int JNI::egl_surface_height = 0;
00075 bool JNI::_ready_for_events = 0;
00076 
00077 jmethodID JNI::_MID_getDPI = 0;
00078 jmethodID JNI::_MID_displayMessageOnOSD = 0;
00079 jmethodID JNI::_MID_openUrl = 0;
00080 jmethodID JNI::_MID_isConnectionLimited = 0;
00081 jmethodID JNI::_MID_setWindowCaption = 0;
00082 jmethodID JNI::_MID_showVirtualKeyboard = 0;
00083 jmethodID JNI::_MID_getSysArchives = 0;
00084 jmethodID JNI::_MID_initSurface = 0;
00085 jmethodID JNI::_MID_deinitSurface = 0;
00086 
00087 jmethodID JNI::_MID_EGL10_eglSwapBuffers = 0;
00088 
00089 jmethodID JNI::_MID_AudioTrack_flush = 0;
00090 jmethodID JNI::_MID_AudioTrack_pause = 0;
00091 jmethodID JNI::_MID_AudioTrack_play = 0;
00092 jmethodID JNI::_MID_AudioTrack_stop = 0;
00093 jmethodID JNI::_MID_AudioTrack_write = 0;
00094 
00095 const JNINativeMethod JNI::_natives[] = {
00096     { "create", "(Landroid/content/res/AssetManager;"
00097                 "Ljavax/microedition/khronos/egl/EGL10;"
00098                 "Ljavax/microedition/khronos/egl/EGLDisplay;"
00099                 "Landroid/media/AudioTrack;II)V",
00100         (void *)JNI::create },
00101     { "destroy", "()V",
00102         (void *)JNI::destroy },
00103     { "setSurface", "(II)V",
00104         (void *)JNI::setSurface },
00105     { "main", "([Ljava/lang/String;)I",
00106         (void *)JNI::main },
00107     { "pushEvent", "(IIIIIII)V",
00108         (void *)JNI::pushEvent },
00109     { "enableZoning", "(Z)V",
00110         (void *)JNI::enableZoning },
00111     { "setPause", "(Z)V",
00112         (void *)JNI::setPause }
00113 };
00114 
00115 JNI::JNI() {
00116 }
00117 
00118 JNI::~JNI() {
00119 }
00120 
00121 jint JNI::onLoad(JavaVM *vm) {
00122     _vm = vm;
00123 
00124     JNIEnv *env;
00125 
00126     if (_vm->GetEnv((void **)&env, JNI_VERSION_1_2))
00127         return JNI_ERR;
00128 
00129     jclass cls = env->FindClass("org/residualvm/residualvm/ResidualVM");
00130     if (cls == 0)
00131         return JNI_ERR;
00132 
00133     if (env->RegisterNatives(cls, _natives, ARRAYSIZE(_natives)) < 0)
00134         return JNI_ERR;
00135 
00136     return JNI_VERSION_1_2;
00137 }
00138 
00139 JNIEnv *JNI::getEnv() {
00140     JNIEnv *env = 0;
00141 
00142     jint res = _vm->GetEnv((void **)&env, JNI_VERSION_1_2);
00143 
00144     if (res != JNI_OK) {
00145         LOGE("GetEnv() failed: %d", res);
00146         abort();
00147     }
00148 
00149     return env;
00150 }
00151 
00152 void JNI::attachThread() {
00153     JNIEnv *env = 0;
00154 
00155     jint res = _vm->AttachCurrentThread(&env, 0);
00156 
00157     if (res != JNI_OK) {
00158         LOGE("AttachCurrentThread() failed: %d", res);
00159         abort();
00160     }
00161 }
00162 
00163 void JNI::detachThread() {
00164     jint res = _vm->DetachCurrentThread();
00165 
00166     if (res != JNI_OK) {
00167         LOGE("DetachCurrentThread() failed: %d", res);
00168         abort();
00169     }
00170 }
00171 
00172 void JNI::setReadyForEvents(bool ready) {
00173     _ready_for_events = ready;
00174 }
00175 
00176 void JNI::throwByName(JNIEnv *env, const char *name, const char *msg) {
00177     jclass cls = env->FindClass(name);
00178 
00179     // if cls is 0, an exception has already been thrown
00180     if (cls != 0)
00181         env->ThrowNew(cls, msg);
00182 
00183     env->DeleteLocalRef(cls);
00184 }
00185 
00186 void JNI::throwRuntimeException(JNIEnv *env, const char *msg) {
00187     throwByName(env, "java/lang/RuntimeException", msg);
00188 }
00189 
00190 // calls to the dark side
00191 
00192 void JNI::getDPI(float *values) {
00193     values[0] = 0.0;
00194     values[1] = 0.0;
00195 
00196     JNIEnv *env = JNI::getEnv();
00197 
00198     jfloatArray array = env->NewFloatArray(2);
00199 
00200     env->CallVoidMethod(_jobj, _MID_getDPI, array);
00201 
00202     if (env->ExceptionCheck()) {
00203         LOGE("Failed to get DPIs");
00204 
00205         env->ExceptionDescribe();
00206         env->ExceptionClear();
00207     } else {
00208         jfloat *res = env->GetFloatArrayElements(array, 0);
00209 
00210         if (res) {
00211             values[0] = res[0];
00212             values[1] = res[1];
00213 
00214             env->ReleaseFloatArrayElements(array, res, 0);
00215         }
00216     }
00217 
00218     env->DeleteLocalRef(array);
00219 }
00220 
00221 void JNI::displayMessageOnOSD(const char *msg) {
00222     JNIEnv *env = JNI::getEnv();
00223     jstring java_msg = env->NewStringUTF(msg);
00224 
00225     env->CallVoidMethod(_jobj, _MID_displayMessageOnOSD, java_msg);
00226 
00227     if (env->ExceptionCheck()) {
00228         LOGE("Failed to display OSD message");
00229 
00230         env->ExceptionDescribe();
00231         env->ExceptionClear();
00232     }
00233 
00234     env->DeleteLocalRef(java_msg);
00235 }
00236 
00237 bool JNI::openUrl(const char *url) {
00238     bool success = true;
00239     JNIEnv *env = JNI::getEnv();
00240     jstring javaUrl = env->NewStringUTF(url);
00241 
00242     env->CallVoidMethod(_jobj, _MID_openUrl, javaUrl);
00243 
00244     if (env->ExceptionCheck()) {
00245         LOGE("Failed to open URL");
00246 
00247         env->ExceptionDescribe();
00248         env->ExceptionClear();
00249         success = false;
00250     }
00251 
00252     env->DeleteLocalRef(javaUrl);
00253     return success;
00254 }
00255 
00256 bool JNI::isConnectionLimited() {
00257     bool limited = false;
00258     JNIEnv *env = JNI::getEnv();
00259     limited = env->CallBooleanMethod(_jobj, _MID_isConnectionLimited);
00260 
00261     if (env->ExceptionCheck()) {
00262         LOGE("Failed to check whether connection's limited");
00263 
00264         env->ExceptionDescribe();
00265         env->ExceptionClear();
00266         limited = true;
00267     }
00268 
00269     return limited;
00270 }
00271 
00272 void JNI::setWindowCaption(const char *caption) {
00273     JNIEnv *env = JNI::getEnv();
00274     jstring java_caption = env->NewStringUTF(caption);
00275 
00276     env->CallVoidMethod(_jobj, _MID_setWindowCaption, java_caption);
00277 
00278     if (env->ExceptionCheck()) {
00279         LOGE("Failed to set window caption");
00280 
00281         env->ExceptionDescribe();
00282         env->ExceptionClear();
00283     }
00284 
00285     env->DeleteLocalRef(java_caption);
00286 }
00287 
00288 void JNI::showVirtualKeyboard(bool enable) {
00289     JNIEnv *env = JNI::getEnv();
00290 
00291     env->CallVoidMethod(_jobj, _MID_showVirtualKeyboard, enable);
00292 
00293     if (env->ExceptionCheck()) {
00294         LOGE("Error trying to show virtual keyboard");
00295 
00296         env->ExceptionDescribe();
00297         env->ExceptionClear();
00298     }
00299 }
00300 
00301 void JNI::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) {
00302     JNIEnv *env = JNI::getEnv();
00303 
00304     s.add("ASSET", _asset_archive, priority, false);
00305 
00306     jobjectArray array =
00307         (jobjectArray)env->CallObjectMethod(_jobj, _MID_getSysArchives);
00308 
00309     if (env->ExceptionCheck()) {
00310         LOGE("Error finding system archive path");
00311 
00312         env->ExceptionDescribe();
00313         env->ExceptionClear();
00314 
00315         return;
00316     }
00317 
00318     jsize size = env->GetArrayLength(array);
00319     for (jsize i = 0; i < size; ++i) {
00320         jstring path_obj = (jstring)env->GetObjectArrayElement(array, i);
00321         const char *path = env->GetStringUTFChars(path_obj, 0);
00322 
00323         if (path != 0) {
00324             s.addDirectory(path, path, priority);
00325             env->ReleaseStringUTFChars(path_obj, path);
00326         }
00327 
00328         env->DeleteLocalRef(path_obj);
00329     }
00330 }
00331 
00332 bool JNI::initSurface() {
00333     JNIEnv *env = JNI::getEnv();
00334 
00335     jobject obj = env->CallObjectMethod(_jobj, _MID_initSurface);
00336 
00337     if (!obj || env->ExceptionCheck()) {
00338         LOGE("initSurface failed");
00339 
00340         env->ExceptionDescribe();
00341         env->ExceptionClear();
00342 
00343         return false;
00344     }
00345 
00346     _jobj_egl_surface = env->NewGlobalRef(obj);
00347 
00348     return true;
00349 }
00350 
00351 void JNI::deinitSurface() {
00352     JNIEnv *env = JNI::getEnv();
00353 
00354     env->CallVoidMethod(_jobj, _MID_deinitSurface);
00355 
00356     if (env->ExceptionCheck()) {
00357         LOGE("deinitSurface failed");
00358 
00359         env->ExceptionDescribe();
00360         env->ExceptionClear();
00361     }
00362 
00363     env->DeleteGlobalRef(_jobj_egl_surface);
00364     _jobj_egl_surface = 0;
00365 }
00366 
00367 void JNI::setAudioPause() {
00368     JNIEnv *env = JNI::getEnv();
00369 
00370     env->CallVoidMethod(_jobj_audio_track, _MID_AudioTrack_flush);
00371 
00372     if (env->ExceptionCheck()) {
00373         LOGE("Error flushing AudioTrack");
00374 
00375         env->ExceptionDescribe();
00376         env->ExceptionClear();
00377     }
00378 
00379     env->CallVoidMethod(_jobj_audio_track, _MID_AudioTrack_pause);
00380 
00381     if (env->ExceptionCheck()) {
00382         LOGE("Error setting AudioTrack: pause");
00383 
00384         env->ExceptionDescribe();
00385         env->ExceptionClear();
00386     }
00387 }
00388 
00389 void JNI::setAudioPlay() {
00390     JNIEnv *env = JNI::getEnv();
00391 
00392     env->CallVoidMethod(_jobj_audio_track, _MID_AudioTrack_play);
00393 
00394     if (env->ExceptionCheck()) {
00395         LOGE("Error setting AudioTrack: play");
00396 
00397         env->ExceptionDescribe();
00398         env->ExceptionClear();
00399     }
00400 }
00401 
00402 void JNI::setAudioStop() {
00403     JNIEnv *env = JNI::getEnv();
00404 
00405     env->CallVoidMethod(_jobj_audio_track, _MID_AudioTrack_stop);
00406 
00407     if (env->ExceptionCheck()) {
00408         LOGE("Error setting AudioTrack: stop");
00409 
00410         env->ExceptionDescribe();
00411         env->ExceptionClear();
00412     }
00413 }
00414 
00415 // natives for the dark side
00416 
00417 void JNI::create(JNIEnv *env, jobject self, jobject asset_manager,
00418                 jobject egl, jobject egl_display,
00419                 jobject at, jint audio_sample_rate, jint audio_buffer_size) {
00420     LOGI(gScummVMFullVersion);
00421 
00422     assert(!_system);
00423 
00424     pause = false;
00425     // initial value of zero!
00426     sem_init(&pause_sem, 0, 0);
00427 
00428     _asset_archive = new AndroidAssetArchive(asset_manager);
00429     assert(_asset_archive);
00430 
00431     _system = new OSystem_Android(audio_sample_rate, audio_buffer_size);
00432     assert(_system);
00433 
00434     // weak global ref to allow class to be unloaded
00435     // ... except dalvik implements NewWeakGlobalRef only on froyo
00436     //_jobj = env->NewWeakGlobalRef(self);
00437 
00438     _jobj = env->NewGlobalRef(self);
00439 
00440     jclass cls = env->GetObjectClass(_jobj);
00441 
00442 #define FIND_METHOD(prefix, name, signature) do {                           \
00443         _MID_ ## prefix ## name = env->GetMethodID(cls, #name, signature);  \
00444         if (_MID_ ## prefix ## name == 0)                                   \
00445             return;                                                         \
00446     } while (0)
00447 
00448     FIND_METHOD(, setWindowCaption, "(Ljava/lang/String;)V");
00449     FIND_METHOD(, getDPI, "([F)V");
00450     FIND_METHOD(, displayMessageOnOSD, "(Ljava/lang/String;)V");
00451     FIND_METHOD(, openUrl, "(Ljava/lang/String;)V");
00452     FIND_METHOD(, isConnectionLimited, "()Z");
00453     FIND_METHOD(, showVirtualKeyboard, "(Z)V");
00454     FIND_METHOD(, getSysArchives, "()[Ljava/lang/String;");
00455     FIND_METHOD(, initSurface, "()Ljavax/microedition/khronos/egl/EGLSurface;");
00456     FIND_METHOD(, deinitSurface, "()V");
00457 
00458     _jobj_egl = env->NewGlobalRef(egl);
00459     _jobj_egl_display = env->NewGlobalRef(egl_display);
00460 
00461     cls = env->GetObjectClass(_jobj_egl);
00462 
00463     FIND_METHOD(EGL10_, eglSwapBuffers,
00464                 "(Ljavax/microedition/khronos/egl/EGLDisplay;"
00465                 "Ljavax/microedition/khronos/egl/EGLSurface;)Z");
00466 
00467     _jobj_audio_track = env->NewGlobalRef(at);
00468 
00469     cls = env->GetObjectClass(_jobj_audio_track);
00470 
00471     FIND_METHOD(AudioTrack_, flush, "()V");
00472     FIND_METHOD(AudioTrack_, pause, "()V");
00473     FIND_METHOD(AudioTrack_, play, "()V");
00474     FIND_METHOD(AudioTrack_, stop, "()V");
00475     FIND_METHOD(AudioTrack_, write, "([BII)I");
00476 
00477 #undef FIND_METHOD
00478 
00479     g_system = _system;
00480 }
00481 
00482 void JNI::destroy(JNIEnv *env, jobject self) {
00483     delete _asset_archive;
00484     _asset_archive = 0;
00485 
00486     delete _system;
00487     g_system = 0;
00488     _system = 0;
00489 
00490     sem_destroy(&pause_sem);
00491 
00492     // see above
00493     //JNI::getEnv()->DeleteWeakGlobalRef(_jobj);
00494 
00495     JNI::getEnv()->DeleteGlobalRef(_jobj_egl_display);
00496     JNI::getEnv()->DeleteGlobalRef(_jobj_egl);
00497     JNI::getEnv()->DeleteGlobalRef(_jobj_audio_track);
00498     JNI::getEnv()->DeleteGlobalRef(_jobj);
00499 }
00500 
00501 void JNI::setSurface(JNIEnv *env, jobject self, jint width, jint height) {
00502     egl_surface_width = width;
00503     egl_surface_height = height;
00504     surface_changeid++;
00505 }
00506 
00507 jint JNI::main(JNIEnv *env, jobject self, jobjectArray args) {
00508     assert(_system);
00509 
00510     const int MAX_NARGS = 32;
00511     int res = -1;
00512 
00513     int argc = env->GetArrayLength(args);
00514     if (argc > MAX_NARGS) {
00515         throwByName(env, "java/lang/IllegalArgumentException",
00516                     "too many arguments");
00517         return 0;
00518     }
00519 
00520     char *argv[MAX_NARGS];
00521 
00522     // note use in cleanup loop below
00523     int nargs;
00524 
00525     for (nargs = 0; nargs < argc; ++nargs) {
00526         jstring arg = (jstring)env->GetObjectArrayElement(args, nargs);
00527 
00528         if (arg == 0) {
00529             argv[nargs] = 0;
00530         } else {
00531             const char *cstr = env->GetStringUTFChars(arg, 0);
00532 
00533             argv[nargs] = const_cast<char *>(cstr);
00534 
00535             // exception already thrown?
00536             if (cstr == 0)
00537                 goto cleanup;
00538         }
00539 
00540         env->DeleteLocalRef(arg);
00541     }
00542 
00543     LOGI("Entering scummvm_main with %d args", argc);
00544 
00545     res = scummvm_main(argc, argv);
00546 
00547     LOGI("scummvm_main exited with code %d", res);
00548 
00549     _system->quit();
00550 
00551 cleanup:
00552     nargs--;
00553 
00554     for (int i = 0; i < nargs; ++i) {
00555         if (argv[i] == 0)
00556             continue;
00557 
00558         jstring arg = (jstring)env->GetObjectArrayElement(args, nargs);
00559 
00560         // Exception already thrown?
00561         if (arg == 0)
00562             return res;
00563 
00564         env->ReleaseStringUTFChars(arg, argv[i]);
00565         env->DeleteLocalRef(arg);
00566     }
00567 
00568     return res;
00569 }
00570 
00571 void JNI::pushEvent(JNIEnv *env, jobject self, int type, int arg1, int arg2,
00572                     int arg3, int arg4, int arg5, int arg6) {
00573     // drop events until we're ready and after we quit
00574     if (!_ready_for_events) {
00575         LOGW("dropping event");
00576         return;
00577     }
00578 
00579     assert(_system);
00580 
00581     _system->pushEvent(type, arg1, arg2, arg3, arg4, arg5, arg6);
00582 }
00583 
00584 void JNI::enableZoning(JNIEnv *env, jobject self, jboolean enable) {
00585     assert(_system);
00586 
00587     _system->enableZoning(enable);
00588 }
00589 
00590 void JNI::setPause(JNIEnv *env, jobject self, jboolean value) {
00591     if (!_system)
00592         return;
00593 
00594     if (g_engine) {
00595         LOGD("pauseEngine: %d", value);
00596 
00597         g_engine->pauseEngine(value);
00598 
00599         if (value &&
00600                 g_engine->hasFeature(Engine::kSupportsSavingDuringRuntime) &&
00601                 g_engine->canSaveGameStateCurrently())
00602             g_engine->saveGameState(0, "Android parachute");
00603     }
00604 
00605     pause = value;
00606 
00607     if (!pause) {
00608         // wake up all threads
00609         for (uint i = 0; i < 3; ++i)
00610             sem_post(&pause_sem);
00611     }
00612 }
00613 
00614 #endif


Generated on Sat Mar 23 2019 05:01:44 for ResidualVM by doxygen 1.7.1
curved edge   curved edge