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     _virtcontrols_on(false),
00136     _enable_zoning(false),
00137     _mixer(0),
00138     _queuedEventTime(0),
00139     _event_queue_lock(createMutex()),
00140     _touch_pt_down(),
00141     _touch_pt_scroll(),
00142     _touch_pt_dt(),
00143     _eventScaleX(100),
00144     _eventScaleY(100),
00145     // TODO put these values in some option dlg?
00146     _touchpad_mode(true),
00147     _touchpad_scale(66),
00148     _dpad_scale(4),
00149     _fingersDown(0),
00150     _trackball_scale(2) {
00151 
00152     _fsFactory = new POSIXFilesystemFactory();
00153 
00154     Common::String mf = getSystemProperty("ro.product.manufacturer");
00155 
00156     LOGI("Running on: [%s] [%s] [%s] [%s] [%s] SDK:%s ABI:%s",
00157             mf.c_str(),
00158             getSystemProperty("ro.product.model").c_str(),
00159             getSystemProperty("ro.product.brand").c_str(),
00160             getSystemProperty("ro.build.fingerprint").c_str(),
00161             getSystemProperty("ro.build.display.id").c_str(),
00162             getSystemProperty("ro.build.version.sdk").c_str(),
00163             getSystemProperty("ro.product.cpu.abi").c_str());
00164 
00165     mf.toLowercase();
00166 }
00167 
00168 OSystem_Android::~OSystem_Android() {
00169     ENTER();
00170 
00171     delete _mixer;
00172     _mixer = 0;
00173     delete _fsFactory;
00174     _fsFactory = 0;
00175     delete _timerManager;
00176     _timerManager = 0;
00177 
00178     deleteMutex(_event_queue_lock);
00179 }
00180 
00181 void *OSystem_Android::timerThreadFunc(void *arg) {
00182     OSystem_Android *system = (OSystem_Android *)arg;
00183     DefaultTimerManager *timer = (DefaultTimerManager *)(system->_timerManager);
00184 
00185     // renice this thread to boost the audio thread
00186     if (setpriority(PRIO_PROCESS, 0, 19) < 0)
00187         LOGW("couldn't renice the timer thread");
00188 
00189     JNI::attachThread();
00190 
00191     struct timespec tv;
00192     tv.tv_sec = 0;
00193     tv.tv_nsec = 10 * 1000 * 1000; // 10ms
00194 
00195     while (!system->_timer_thread_exit) {
00196         if (JNI::pause) {
00197             LOGD("timer thread going to sleep");
00198             sem_wait(&JNI::pause_sem);
00199             LOGD("timer thread woke up");
00200         }
00201 
00202         timer->handler();
00203         nanosleep(&tv, 0);
00204     }
00205 
00206     JNI::detachThread();
00207 
00208     return 0;
00209 }
00210 
00211 void *OSystem_Android::audioThreadFunc(void *arg) {
00212     JNI::attachThread();
00213 
00214     OSystem_Android *system = (OSystem_Android *)arg;
00215     Audio::MixerImpl *mixer = system->_mixer;
00216 
00217     uint buf_size = system->_audio_buffer_size;
00218 
00219     JNIEnv *env = JNI::getEnv();
00220 
00221     jbyteArray bufa = env->NewByteArray(buf_size);
00222 
00223     bool paused = true;
00224 
00225     byte *buf;
00226     int offset, left, written;
00227     int samples, i;
00228 
00229     struct timespec tv_delay;
00230     tv_delay.tv_sec = 0;
00231     tv_delay.tv_nsec = 20 * 1000 * 1000;
00232 
00233     uint msecs_full = buf_size * 1000 / (mixer->getOutputRate() * 2 * 2);
00234 
00235     struct timespec tv_full;
00236     tv_full.tv_sec = 0;
00237     tv_full.tv_nsec = msecs_full * 1000 * 1000;
00238 
00239     bool silence;
00240     uint silence_count = 33;
00241 
00242     while (!system->_audio_thread_exit) {
00243         if (JNI::pause) {
00244             JNI::setAudioStop();
00245 
00246             paused = true;
00247             silence_count = 33;
00248 
00249             LOGD("audio thread going to sleep");
00250             sem_wait(&JNI::pause_sem);
00251             LOGD("audio thread woke up");
00252         }
00253 
00254         buf = (byte *)env->GetPrimitiveArrayCritical(bufa, 0);
00255         assert(buf);
00256 
00257         samples = mixer->mixCallback(buf, buf_size);
00258 
00259         silence = samples < 1;
00260 
00261         // looks stupid, and it is, but currently there's no way to detect
00262         // silence-only buffers from the mixer
00263         if (!silence) {
00264             silence = true;
00265 
00266             for (i = 0; i < samples; i += 2)
00267                 // SID streams constant crap
00268                 if (READ_UINT16(buf + i) > 32) {
00269                     silence = false;
00270                     break;
00271                 }
00272         }
00273 
00274         env->ReleasePrimitiveArrayCritical(bufa, buf, 0);
00275 
00276         if (silence) {
00277             if (!paused)
00278                 silence_count++;
00279 
00280             // only pause after a while to prevent toggle mania
00281             if (silence_count > 32) {
00282                 if (!paused) {
00283                     LOGD("AudioTrack pause");
00284 
00285                     JNI::setAudioPause();
00286                     paused = true;
00287                 }
00288 
00289                 nanosleep(&tv_full, 0);
00290 
00291                 continue;
00292             }
00293         }
00294 
00295         if (paused) {
00296             LOGD("AudioTrack play");
00297 
00298             JNI::setAudioPlay();
00299             paused = false;
00300 
00301             silence_count = 0;
00302         }
00303 
00304         offset = 0;
00305         left = buf_size;
00306         written = 0;
00307 
00308         while (left > 0) {
00309             written = JNI::writeAudio(env, bufa, offset, left);
00310 
00311             if (written < 0) {
00312                 LOGE("AudioTrack error: %d", written);
00313                 break;
00314             }
00315 
00316             // buffer full
00317             if (written < left)
00318                 nanosleep(&tv_delay, 0);
00319 
00320             offset += written;
00321             left -= written;
00322         }
00323 
00324         if (written < 0)
00325             break;
00326 
00327         // prepare the next buffer, and run into the blocking AudioTrack.write
00328     }
00329 
00330     JNI::setAudioStop();
00331 
00332     env->DeleteLocalRef(bufa);
00333 
00334     JNI::detachThread();
00335 
00336     return 0;
00337 }
00338 
00339 void OSystem_Android::initBackend() {
00340     ENTER();
00341 
00342     _main_thread = pthread_self();
00343 
00344     ConfMan.set("fullscreen", "true");
00345     ConfMan.registerDefault("aspect_ratio", true);
00346 
00347     ConfMan.setInt("autosave_period", 0);
00348     ConfMan.setBool("FM_high_quality", false);
00349     ConfMan.setBool("FM_medium_quality", true);
00350 
00351     // TODO hackity hack
00352     if (ConfMan.hasKey("multi_midi"))
00353         _touchpad_mode = !ConfMan.getBool("multi_midi");
00354 
00355     // must happen before creating TimerManager to avoid race in
00356     // creating EventManager
00357     setupKeymapper();
00358 
00359     // BUG: "transient" ConfMan settings get nuked by the options
00360     // screen. Passing the savepath in this way makes it stick
00361     // (via ConfMan.registerDefault)
00362     _savefileManager = new DefaultSaveFileManager(ConfMan.get("savepath"));
00363     _timerManager = new DefaultTimerManager();
00364 
00365     gettimeofday(&_startTime, 0);
00366 
00367     _mixer = new Audio::MixerImpl(this, _audio_sample_rate);
00368     _mixer->setReady(true);
00369 
00370     _timer_thread_exit = false;
00371     pthread_create(&_timer_thread, 0, timerThreadFunc, this);
00372 
00373     _audio_thread_exit = false;
00374     pthread_create(&_audio_thread, 0, audioThreadFunc, this);
00375 
00376     initSurface();
00377     initViewport();
00378 
00379     _game_texture = new GLESFakePalette565Texture();
00380     _overlay_texture = new GLES4444Texture();
00381     _mouse_texture_palette = new GLESFakePalette5551Texture();
00382     _mouse_texture = _mouse_texture_palette;
00383 
00384     initOverlay();
00385 
00386     // renice this thread to boost the audio thread
00387     if (setpriority(PRIO_PROCESS, 0, 19) < 0)
00388         warning("couldn't renice the main thread");
00389 
00390     JNI::setReadyForEvents(true);
00391 
00392     EventsBaseBackend::initBackend();
00393 }
00394 
00395 bool OSystem_Android::hasFeature(Feature f) {
00396     return (f == kFeatureFullscreenMode ||
00397             f == kFeatureAspectRatioCorrection ||
00398             f == kFeatureCursorPalette ||
00399             f == kFeatureVirtualKeyboard ||
00400             f == kFeatureVirtControls ||
00401 #ifdef USE_OPENGL
00402             f == kFeatureOpenGL ||
00403 #endif
00404             f == kFeatureOverlaySupportsAlpha ||
00405             f == kFeatureOpenUrl);
00406 }
00407 
00408 void OSystem_Android::setFeatureState(Feature f, bool enable) {
00409     ENTER("%d, %d", f, enable);
00410 
00411     switch (f) {
00412     case kFeatureFullscreenMode:
00413         _fullscreen = enable;
00414         updateScreenRect();
00415         break;
00416     case kFeatureAspectRatioCorrection:
00417         _ar_correction = enable;
00418         updateScreenRect();
00419         break;
00420     case kFeatureVirtualKeyboard:
00421         _virtkeybd_on = enable;
00422         showVirtualKeyboard(enable);
00423         break;
00424     case kFeatureVirtControls:
00425         _virtcontrols_on = enable;
00426         break;
00427     case kFeatureCursorPalette:
00428         _use_mouse_palette = enable;
00429         if (!enable)
00430             disableCursorPalette();
00431         break;
00432     default:
00433         break;
00434     }
00435 }
00436 
00437 bool OSystem_Android::getFeatureState(Feature f) {
00438     switch (f) {
00439     case kFeatureFullscreenMode:
00440         return _fullscreen;
00441     case kFeatureAspectRatioCorrection:
00442         return _ar_correction;
00443     case kFeatureVirtualKeyboard:
00444         return _virtkeybd_on;
00445     case kFeatureVirtControls:
00446         return _virtcontrols_on;
00447     case kFeatureCursorPalette:
00448         return _use_mouse_palette;
00449     default:
00450         return false;
00451     }
00452 }
00453 
00454 uint32 OSystem_Android::getMillis(bool skipRecord) {
00455     timeval curTime;
00456 
00457     gettimeofday(&curTime, 0);
00458 
00459     return (uint32)(((curTime.tv_sec - _startTime.tv_sec) * 1000) +
00460             ((curTime.tv_usec - _startTime.tv_usec) / 1000));
00461 }
00462 
00463 void OSystem_Android::delayMillis(uint msecs) {
00464     usleep(msecs * 1000);
00465 }
00466 
00467 OSystem::MutexRef OSystem_Android::createMutex() {
00468     pthread_mutexattr_t attr;
00469 
00470     pthread_mutexattr_init(&attr);
00471     pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
00472 
00473     pthread_mutex_t *mutex = new pthread_mutex_t;
00474 
00475     if (pthread_mutex_init(mutex, &attr) != 0) {
00476         warning("pthread_mutex_init() failed");
00477 
00478         delete mutex;
00479 
00480         return 0;
00481     }
00482 
00483     return (MutexRef)mutex;
00484 }
00485 
00486 void OSystem_Android::lockMutex(MutexRef mutex) {
00487     if (pthread_mutex_lock((pthread_mutex_t *)mutex) != 0)
00488         warning("pthread_mutex_lock() failed");
00489 }
00490 
00491 void OSystem_Android::unlockMutex(MutexRef mutex) {
00492     if (pthread_mutex_unlock((pthread_mutex_t *)mutex) != 0)
00493         warning("pthread_mutex_unlock() failed");
00494 }
00495 
00496 void OSystem_Android::deleteMutex(MutexRef mutex) {
00497     pthread_mutex_t *m = (pthread_mutex_t *)mutex;
00498 
00499     if (pthread_mutex_destroy(m) != 0)
00500         warning("pthread_mutex_destroy() failed");
00501     else
00502         delete m;
00503 }
00504 
00505 void OSystem_Android::quit() {
00506     ENTER();
00507 
00508     JNI::setReadyForEvents(false);
00509 
00510     _audio_thread_exit = true;
00511     pthread_join(_audio_thread, 0);
00512 
00513     _timer_thread_exit = true;
00514     pthread_join(_timer_thread, 0);
00515 
00516     delete _game_texture;
00517     delete _overlay_texture;
00518     delete _mouse_texture_palette;
00519     delete _mouse_texture_rgb;
00520 
00521     deinitSurface();
00522 }
00523 
00524 void OSystem_Android::setWindowCaption(const char *caption) {
00525     ENTER("%s", caption);
00526 
00527     JNI::setWindowCaption(caption);
00528 }
00529 
00530 void OSystem_Android::displayMessageOnOSD(const char *msg) {
00531     ENTER("%s", msg);
00532 
00533     JNI::displayMessageOnOSD(msg);
00534 }
00535 
00536 void OSystem_Android::showVirtualKeyboard(bool enable) {
00537     ENTER("%d", enable);
00538 
00539     JNI::showVirtualKeyboard(enable);
00540 }
00541 
00542 Audio::Mixer *OSystem_Android::getMixer() {
00543     assert(_mixer);
00544     return _mixer;
00545 }
00546 
00547 void OSystem_Android::getTimeAndDate(TimeDate &td) const {
00548     struct tm tm;
00549     const time_t curTime = time(0);
00550 
00551     localtime_r(&curTime, &tm);
00552     td.tm_sec = tm.tm_sec;
00553     td.tm_min = tm.tm_min;
00554     td.tm_hour = tm.tm_hour;
00555     td.tm_mday = tm.tm_mday;
00556     td.tm_mon = tm.tm_mon;
00557     td.tm_year = tm.tm_year;
00558     td.tm_wday = tm.tm_wday;
00559 }
00560 
00561 void OSystem_Android::addSysArchivesToSearchSet(Common::SearchSet &s,
00562                                                 int priority) {
00563     ENTER("");
00564 
00565     JNI::addSysArchivesToSearchSet(s, priority);
00566 }
00567 
00568 void OSystem_Android::logMessage(LogMessageType::Type type,
00569                                     const char *message) {
00570     switch (type) {
00571     case LogMessageType::kInfo:
00572         __android_log_write(ANDROID_LOG_INFO, android_log_tag, message);
00573         break;
00574 
00575     case LogMessageType::kDebug:
00576         __android_log_write(ANDROID_LOG_DEBUG, android_log_tag, message);
00577         break;
00578 
00579     case LogMessageType::kWarning:
00580         __android_log_write(ANDROID_LOG_WARN, android_log_tag, message);
00581         break;
00582 
00583     case LogMessageType::kError:
00584         __android_log_write(ANDROID_LOG_ERROR, android_log_tag, message);
00585         break;
00586     }
00587 }
00588 
00589 Common::String OSystem_Android::getSystemLanguage() const {
00590     return Common::String::format("%s_%s",
00591                             getSystemProperty("persist.sys.language").c_str(),
00592                             getSystemProperty("persist.sys.country").c_str());
00593 }
00594 
00595 bool OSystem_Android::openUrl(const Common::String &url) {
00596     return JNI::openUrl(url.c_str());
00597 }
00598 
00599 Common::String OSystem_Android::getSystemProperty(const char *name) const {
00600     char value[PROP_VALUE_MAX];
00601 
00602     int len = __system_property_get(name, value);
00603 
00604     return Common::String(value, len);
00605 }
00606 
00607 #endif


Generated on Sat Mar 16 2019 05:01:18 for ResidualVM by doxygen 1.7.1
curved edge   curved edge