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

android.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>
00026 #define FORBIDDEN_SYMBOL_EXCEPTION_time_h
00027 
00028 // Disable printf override in common/forbidden.h to avoid
00029 // clashes with log.h from the Android SDK.
00030 // That header file uses
00031 //   __attribute__ ((format(printf, 3, 4)))
00032 // which gets messed up by our override mechanism; this could
00033 // be avoided by either changing the Android SDK to use the equally
00034 // legal and valid
00035 //   __attribute__ ((format(printf, 3, 4)))
00036 // or by refining our printf override to use a varadic macro
00037 // (which then wouldn't be portable, though).
00038 // Anyway, for now we just disable the printf override globally
00039 // for the Android port
00040 #define FORBIDDEN_SYMBOL_EXCEPTION_printf
00041 
00042 #include <sys/time.h>
00043 #include <sys/resource.h>
00044 #include <sys/system_properties.h>
00045 #include <time.h>
00046 #include <unistd.h>
00047 
00048 #include "common/util.h"
00049 #include "common/textconsole.h"
00050 #include "common/rect.h"
00051 #include "common/queue.h"
00052 #include "common/mutex.h"
00053 #include "common/events.h"
00054 #include "common/config-manager.h"
00055 
00056 #include "backends/keymapper/keymapper.h"
00057 #include "backends/saves/default/default-saves.h"
00058 #include "backends/timer/default/default-timer.h"
00059 
00060 #include "backends/platform/android/jni.h"
00061 #include "backends/platform/android/android.h"
00062 
00063 const char *android_log_tag = "ResidualVM";
00064 
00065 // This replaces the bionic libc assert functions with something that
00066 // actually prints the assertion failure before aborting.
00067 extern "C" {
00068     void __assert(const char *file, int line, const char *expr) {
00069         __android_log_assert(expr, android_log_tag,
00070                                 "Assertion failure: '%s' in %s:%d",
00071                                  expr, file, line);
00072     }
00073 
00074     void __assert2(const char *file, int line, const char *func,
00075                     const char *expr) {
00076         __android_log_assert(expr, android_log_tag,
00077                                 "Assertion failure: '%s' in %s:%d (%s)",
00078                                  expr, file, line, func);
00079     }
00080 }
00081 
00082 #ifdef ANDROID_DEBUG_GL
00083 static const char *getGlErrStr(GLenum error) {
00084     switch (error) {
00085     case GL_INVALID_ENUM:
00086         return "GL_INVALID_ENUM";
00087     case GL_INVALID_VALUE:
00088         return "GL_INVALID_VALUE";
00089     case GL_INVALID_OPERATION:
00090         return "GL_INVALID_OPERATION";
00091     case GL_STACK_OVERFLOW:
00092         return "GL_STACK_OVERFLOW";
00093     case GL_STACK_UNDERFLOW:
00094         return "GL_STACK_UNDERFLOW";
00095     case GL_OUT_OF_MEMORY:
00096         return "GL_OUT_OF_MEMORY";
00097     }
00098 
00099     static char buf[40];
00100     snprintf(buf, sizeof(buf), "(Unknown GL error code 0x%x)", error);
00101 
00102     return buf;
00103 }
00104 
00105 void checkGlError(const char *expr, const char *file, int line) {
00106     GLenum error = glGetError();
00107 
00108     if (error != GL_NO_ERROR)
00109         LOGE("GL ERROR: %s on %s (%s:%d)", getGlErrStr(error), expr, file, line);
00110 }
00111 #endif
00112 
00113 OSystem_Android::OSystem_Android(int audio_sample_rate, int audio_buffer_size) :
00114     _audio_sample_rate(audio_sample_rate),
00115     _audio_buffer_size(audio_buffer_size),
00116     _screen_changeid(0),
00117     _egl_surface_width(0),
00118     _egl_surface_height(0),
00119     _force_redraw(false),
00120     _game_texture(0),
00121     _game_pbuf(),
00122     _overlay_texture(0),
00123     _opengl(false),
00124     _mouse_texture(0),
00125     _mouse_texture_palette(0),
00126     _mouse_texture_rgb(0),
00127     _mouse_hotspot(),
00128     _mouse_keycolor(0),
00129     _use_mouse_palette(false),
00130     _graphicsMode(0),
00131     _fullscreen(true),
00132     _ar_correction(true),
00133     _show_mouse(false),
00134     _show_overlay(false),
00135     _enable_zoning(false),
00136     _mixer(0),
00137     _queuedEventTime(0),
00138     _event_queue_lock(createMutex()),
00139     _touch_pt_down(),
00140     _touch_pt_scroll(),
00141     _touch_pt_dt(),
00142     _eventScaleX(100),
00143     _eventScaleY(100),
00144     // TODO put these values in some option dlg?
00145     _touchpad_mode(true),
00146     _touchpad_scale(66),
00147     _dpad_scale(4),
00148     _fingersDown(0),
00149     _trackball_scale(2) {
00150 
00151     _fsFactory = new POSIXFilesystemFactory();
00152 
00153     Common::String mf = getSystemProperty("ro.product.manufacturer");
00154 
00155     LOGI("Running on: [%s] [%s] [%s] [%s] [%s] SDK:%s ABI:%s",
00156             mf.c_str(),
00157             getSystemProperty("ro.product.model").c_str(),
00158             getSystemProperty("ro.product.brand").c_str(),
00159             getSystemProperty("ro.build.fingerprint").c_str(),
00160             getSystemProperty("ro.build.display.id").c_str(),
00161             getSystemProperty("ro.build.version.sdk").c_str(),
00162             getSystemProperty("ro.product.cpu.abi").c_str());
00163 
00164     mf.toLowercase();
00165 }
00166 
00167 OSystem_Android::~OSystem_Android() {
00168     ENTER();
00169 
00170     delete _mixer;
00171     _mixer = 0;
00172     delete _fsFactory;
00173     _fsFactory = 0;
00174     delete _timerManager;
00175     _timerManager = 0;
00176 
00177     deleteMutex(_event_queue_lock);
00178 }
00179 
00180 void *OSystem_Android::timerThreadFunc(void *arg) {
00181     OSystem_Android *system = (OSystem_Android *)arg;
00182     DefaultTimerManager *timer = (DefaultTimerManager *)(system->_timerManager);
00183 
00184     // renice this thread to boost the audio thread
00185     if (setpriority(PRIO_PROCESS, 0, 19) < 0)
00186         LOGW("couldn't renice the timer thread");
00187 
00188     JNI::attachThread();
00189 
00190     struct timespec tv;
00191     tv.tv_sec = 0;
00192     tv.tv_nsec = 10 * 1000 * 1000; // 10ms
00193 
00194     while (!system->_timer_thread_exit) {
00195         if (JNI::pause) {
00196             LOGD("timer thread going to sleep");
00197             sem_wait(&JNI::pause_sem);
00198             LOGD("timer thread woke up");
00199         }
00200 
00201         timer->handler();
00202         nanosleep(&tv, 0);
00203     }
00204 
00205     JNI::detachThread();
00206 
00207     return 0;
00208 }
00209 
00210 void *OSystem_Android::audioThreadFunc(void *arg) {
00211     JNI::attachThread();
00212 
00213     OSystem_Android *system = (OSystem_Android *)arg;
00214     Audio::MixerImpl *mixer = system->_mixer;
00215 
00216     uint buf_size = system->_audio_buffer_size;
00217 
00218     JNIEnv *env = JNI::getEnv();
00219 
00220     jbyteArray bufa = env->NewByteArray(buf_size);
00221 
00222     bool paused = true;
00223 
00224     byte *buf;
00225     int offset, left, written;
00226     int samples, i;
00227 
00228     struct timespec tv_delay;
00229     tv_delay.tv_sec = 0;
00230     tv_delay.tv_nsec = 20 * 1000 * 1000;
00231 
00232     uint msecs_full = buf_size * 1000 / (mixer->getOutputRate() * 2 * 2);
00233 
00234     struct timespec tv_full;
00235     tv_full.tv_sec = 0;
00236     tv_full.tv_nsec = msecs_full * 1000 * 1000;
00237 
00238     bool silence;
00239     uint silence_count = 33;
00240 
00241     while (!system->_audio_thread_exit) {
00242         if (JNI::pause) {
00243             JNI::setAudioStop();
00244 
00245             paused = true;
00246             silence_count = 33;
00247 
00248             LOGD("audio thread going to sleep");
00249             sem_wait(&JNI::pause_sem);
00250             LOGD("audio thread woke up");
00251         }
00252 
00253         buf = (byte *)env->GetPrimitiveArrayCritical(bufa, 0);
00254         assert(buf);
00255 
00256         samples = mixer->mixCallback(buf, buf_size);
00257 
00258         silence = samples < 1;
00259 
00260         // looks stupid, and it is, but currently there's no way to detect
00261         // silence-only buffers from the mixer
00262         if (!silence) {
00263             silence = true;
00264 
00265             for (i = 0; i < samples; i += 2)
00266                 // SID streams constant crap
00267                 if (READ_UINT16(buf + i) > 32) {
00268                     silence = false;
00269                     break;
00270                 }
00271         }
00272 
00273         env->ReleasePrimitiveArrayCritical(bufa, buf, 0);
00274 
00275         if (silence) {
00276             if (!paused)
00277                 silence_count++;
00278 
00279             // only pause after a while to prevent toggle mania
00280             if (silence_count > 32) {
00281                 if (!paused) {
00282                     LOGD("AudioTrack pause");
00283 
00284                     JNI::setAudioPause();
00285                     paused = true;
00286                 }
00287 
00288                 nanosleep(&tv_full, 0);
00289 
00290                 continue;
00291             }
00292         }
00293 
00294         if (paused) {
00295             LOGD("AudioTrack play");
00296 
00297             JNI::setAudioPlay();
00298             paused = false;
00299 
00300             silence_count = 0;
00301         }
00302 
00303         offset = 0;
00304         left = buf_size;
00305         written = 0;
00306 
00307         while (left > 0) {
00308             written = JNI::writeAudio(env, bufa, offset, left);
00309 
00310             if (written < 0) {
00311                 LOGE("AudioTrack error: %d", written);
00312                 break;
00313             }
00314 
00315             // buffer full
00316             if (written < left)
00317                 nanosleep(&tv_delay, 0);
00318 
00319             offset += written;
00320             left -= written;
00321         }
00322 
00323         if (written < 0)
00324             break;
00325 
00326         // prepare the next buffer, and run into the blocking AudioTrack.write
00327     }
00328 
00329     JNI::setAudioStop();
00330 
00331     env->DeleteLocalRef(bufa);
00332 
00333     JNI::detachThread();
00334 
00335     return 0;
00336 }
00337 
00338 void OSystem_Android::initBackend() {
00339     ENTER();
00340 
00341     _main_thread = pthread_self();
00342 
00343     ConfMan.set("fullscreen", "true");
00344     ConfMan.registerDefault("aspect_ratio", true);
00345     ConfMan.registerDefault("touchpad_mouse_mode", true);
00346     ConfMan.registerDefault("vsync", true);
00347 
00348     ConfMan.setInt("autosave_period", 0);
00349     ConfMan.setBool("FM_high_quality", false);
00350     ConfMan.setBool("FM_medium_quality", true);
00351 
00352     if (ConfMan.hasKey("touchpad_mouse_mode"))
00353         _touchpad_mode = ConfMan.getBool("touchpad_mouse_mode");
00354     else
00355         ConfMan.setBool("touchpad_mouse_mode", true);
00356 
00357     // must happen before creating TimerManager to avoid race in
00358     // creating EventManager
00359     setupKeymapper();
00360 
00361     // BUG: "transient" ConfMan settings get nuked by the options
00362     // screen. Passing the savepath in this way makes it stick
00363     // (via ConfMan.registerDefault)
00364     _savefileManager = new DefaultSaveFileManager(ConfMan.get("savepath"));
00365     _timerManager = new DefaultTimerManager();
00366 
00367     gettimeofday(&_startTime, 0);
00368 
00369     _mixer = new Audio::MixerImpl(this, _audio_sample_rate);
00370     _mixer->setReady(true);
00371 
00372     _timer_thread_exit = false;
00373     pthread_create(&_timer_thread, 0, timerThreadFunc, this);
00374 
00375     _audio_thread_exit = false;
00376     pthread_create(&_audio_thread, 0, audioThreadFunc, this);
00377 
00378     initSurface();
00379     initViewport();
00380 
00381     _game_texture = new GLESFakePalette565Texture();
00382     _overlay_texture = new GLES4444Texture();
00383     _mouse_texture_palette = new GLESFakePalette5551Texture();
00384     _mouse_texture = _mouse_texture_palette;
00385 
00386     initOverlay();
00387 
00388     // renice this thread to boost the audio thread
00389     if (setpriority(PRIO_PROCESS, 0, 19) < 0)
00390         warning("couldn't renice the main thread");
00391 
00392     JNI::setReadyForEvents(true);
00393 
00394     EventsBaseBackend::initBackend();
00395 }
00396 
00397 bool OSystem_Android::hasFeature(Feature f) {
00398     return (f == kFeatureFullscreenMode ||
00399             f == kFeatureAspectRatioCorrection ||
00400             f == kFeatureCursorPalette ||
00401             f == kFeatureVirtualKeyboard ||
00402 #ifdef USE_OPENGL
00403             f == kFeatureOpenGL ||
00404 #endif
00405             f == kFeatureOverlaySupportsAlpha ||
00406             f == kFeatureOpenUrl ||
00407             f == kFeatureTouchpadMode ||
00408             f == kFeatureClipboardSupport);
00409 }
00410 
00411 void OSystem_Android::setFeatureState(Feature f, bool enable) {
00412     ENTER("%d, %d", f, enable);
00413 
00414     switch (f) {
00415     case kFeatureFullscreenMode:
00416         _fullscreen = enable;
00417         updateScreenRect();
00418         break;
00419     case kFeatureAspectRatioCorrection:
00420         _ar_correction = enable;
00421         updateScreenRect();
00422         break;
00423     case kFeatureVirtualKeyboard:
00424         _virtkeybd_on = enable;
00425         showVirtualKeyboard(enable);
00426         break;
00427     case kFeatureCursorPalette:
00428         _use_mouse_palette = enable;
00429         if (!enable)
00430             disableCursorPalette();
00431         break;
00432     case kFeatureTouchpadMode:
00433         ConfMan.setBool("touchpad_mouse_mode", enable);
00434         _touchpad_mode = enable;
00435         break;
00436     default:
00437         break;
00438     }
00439 }
00440 
00441 bool OSystem_Android::getFeatureState(Feature f) {
00442     switch (f) {
00443     case kFeatureFullscreenMode:
00444         return _fullscreen;
00445     case kFeatureAspectRatioCorrection:
00446         return _ar_correction;
00447     case kFeatureVirtualKeyboard:
00448         return _virtkeybd_on;
00449     case kFeatureCursorPalette:
00450         return _use_mouse_palette;
00451     case kFeatureTouchpadMode:
00452         return ConfMan.getBool("touchpad_mouse_mode");
00453     default:
00454         return false;
00455     }
00456 }
00457 
00458 uint32 OSystem_Android::getMillis(bool skipRecord) {
00459     timeval curTime;
00460 
00461     gettimeofday(&curTime, 0);
00462 
00463     return (uint32)(((curTime.tv_sec - _startTime.tv_sec) * 1000) +
00464             ((curTime.tv_usec - _startTime.tv_usec) / 1000));
00465 }
00466 
00467 void OSystem_Android::delayMillis(uint msecs) {
00468     usleep(msecs * 1000);
00469 }
00470 
00471 OSystem::MutexRef OSystem_Android::createMutex() {
00472     pthread_mutexattr_t attr;
00473 
00474     pthread_mutexattr_init(&attr);
00475     pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
00476 
00477     pthread_mutex_t *mutex = new pthread_mutex_t;
00478 
00479     if (pthread_mutex_init(mutex, &attr) != 0) {
00480         warning("pthread_mutex_init() failed");
00481 
00482         delete mutex;
00483 
00484         return 0;
00485     }
00486 
00487     return (MutexRef)mutex;
00488 }
00489 
00490 void OSystem_Android::lockMutex(MutexRef mutex) {
00491     if (pthread_mutex_lock((pthread_mutex_t *)mutex) != 0)
00492         warning("pthread_mutex_lock() failed");
00493 }
00494 
00495 void OSystem_Android::unlockMutex(MutexRef mutex) {
00496     if (pthread_mutex_unlock((pthread_mutex_t *)mutex) != 0)
00497         warning("pthread_mutex_unlock() failed");
00498 }
00499 
00500 void OSystem_Android::deleteMutex(MutexRef mutex) {
00501     pthread_mutex_t *m = (pthread_mutex_t *)mutex;
00502 
00503     if (pthread_mutex_destroy(m) != 0)
00504         warning("pthread_mutex_destroy() failed");
00505     else
00506         delete m;
00507 }
00508 
00509 void OSystem_Android::quit() {
00510     ENTER();
00511 
00512     JNI::setReadyForEvents(false);
00513 
00514     _audio_thread_exit = true;
00515     pthread_join(_audio_thread, 0);
00516 
00517     _timer_thread_exit = true;
00518     pthread_join(_timer_thread, 0);
00519 
00520     delete _game_texture;
00521     delete _overlay_texture;
00522     delete _mouse_texture_palette;
00523     delete _mouse_texture_rgb;
00524 
00525     deinitSurface();
00526 }
00527 
00528 void OSystem_Android::setWindowCaption(const char *caption) {
00529     ENTER("%s", caption);
00530 
00531     JNI::setWindowCaption(caption);
00532 }
00533 
00534 void OSystem_Android::displayMessageOnOSD(const char *msg) {
00535     ENTER("%s", msg);
00536 
00537     JNI::displayMessageOnOSD(msg);
00538 }
00539 
00540 void OSystem_Android::showVirtualKeyboard(bool enable) {
00541     ENTER("%d", enable);
00542 
00543     JNI::showVirtualKeyboard(enable);
00544 }
00545 
00546 Audio::Mixer *OSystem_Android::getMixer() {
00547     assert(_mixer);
00548     return _mixer;
00549 }
00550 
00551 void OSystem_Android::getTimeAndDate(TimeDate &td) const {
00552     struct tm tm;
00553     const time_t curTime = time(0);
00554 
00555     localtime_r(&curTime, &tm);
00556     td.tm_sec = tm.tm_sec;
00557     td.tm_min = tm.tm_min;
00558     td.tm_hour = tm.tm_hour;
00559     td.tm_mday = tm.tm_mday;
00560     td.tm_mon = tm.tm_mon;
00561     td.tm_year = tm.tm_year;
00562     td.tm_wday = tm.tm_wday;
00563 }
00564 
00565 void OSystem_Android::addSysArchivesToSearchSet(Common::SearchSet &s,
00566                                                 int priority) {
00567     ENTER("");
00568 
00569     JNI::addSysArchivesToSearchSet(s, priority);
00570 }
00571 
00572 void OSystem_Android::logMessage(LogMessageType::Type type,
00573                                     const char *message) {
00574     switch (type) {
00575     case LogMessageType::kInfo:
00576         __android_log_write(ANDROID_LOG_INFO, android_log_tag, message);
00577         break;
00578 
00579     case LogMessageType::kDebug:
00580         __android_log_write(ANDROID_LOG_DEBUG, android_log_tag, message);
00581         break;
00582 
00583     case LogMessageType::kWarning:
00584         __android_log_write(ANDROID_LOG_WARN, android_log_tag, message);
00585         break;
00586 
00587     case LogMessageType::kError:
00588         __android_log_write(ANDROID_LOG_ERROR, android_log_tag, message);
00589         break;
00590     }
00591 }
00592 
00593 Common::String OSystem_Android::getSystemLanguage() const {
00594     return Common::String::format("%s_%s",
00595                             getSystemProperty("persist.sys.language").c_str(),
00596                             getSystemProperty("persist.sys.country").c_str());
00597 }
00598 
00599 bool OSystem_Android::openUrl(const Common::String &url) {
00600     return JNI::openUrl(url.c_str());
00601 }
00602 
00603 bool OSystem_Android::hasTextInClipboard() {
00604     return JNI::hasTextInClipboard();
00605 }
00606 
00607 Common::String OSystem_Android::getTextFromClipboard() {
00608     return JNI::getTextFromClipboard();
00609 }
00610 
00611 bool OSystem_Android::setTextInClipboard(const Common::String &text) {
00612     return JNI::setTextInClipboard(text);
00613 }
00614 
00615 Common::String OSystem_Android::getSystemProperty(const char *name) const {
00616     char value[PROP_VALUE_MAX];
00617 
00618     int len = __system_property_get(name, value);
00619 
00620     return Common::String(value, len);
00621 }
00622 
00623 #endif


Generated on Sat May 25 2019 05:00:38 for ResidualVM by doxygen 1.7.1
curved edge   curved edge