00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #if defined(__ANDROID__)
00024
00025
00026 #define FORBIDDEN_SYMBOL_EXCEPTION_time_h
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
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
00066
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
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
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;
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
00262
00263 if (!silence) {
00264 silence = true;
00265
00266 for (i = 0; i < samples; i += 2)
00267
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
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
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
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
00352 if (ConfMan.hasKey("multi_midi"))
00353 _touchpad_mode = !ConfMan.getBool("multi_midi");
00354
00355
00356
00357 setupKeymapper();
00358
00359
00360
00361
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
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