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


Generated on Sat Jul 20 2019 05:00:45 for ResidualVM by doxygen 1.7.1
curved edge   curved edge