diff options
Diffstat (limited to 'backends/platform/android/android.cpp')
-rw-r--r-- | backends/platform/android/android.cpp | 1347 |
1 files changed, 227 insertions, 1120 deletions
diff --git a/backends/platform/android/android.cpp b/backends/platform/android/android.cpp index c49745f8bd..0cfe7c9a22 100644 --- a/backends/platform/android/android.cpp +++ b/backends/platform/android/android.cpp @@ -25,22 +25,11 @@ #if defined(__ANDROID__) -#include "backends/base-backend.h" -#include "base/main.h" -#include "graphics/surface.h" - -#include "backends/platform/android/android.h" -#include "backends/platform/android/video.h" - -#include <jni.h> - -#include <string.h> -#include <unistd.h> -#include <pthread.h> #include <sys/time.h> +#include <sys/resource.h> #include <time.h> +#include <unistd.h> -#include "common/archive.h" #include "common/util.h" #include "common/rect.h" #include "common/queue.h" @@ -48,14 +37,12 @@ #include "common/events.h" #include "common/config-manager.h" -#include "backends/fs/posix/posix-fs-factory.h" #include "backends/keymapper/keymapper.h" #include "backends/saves/default/default-saves.h" #include "backends/timer/default/default-timer.h" -#include "backends/plugins/posix/posix-provider.h" -#include "audio/mixer_intern.h" -#include "backends/platform/android/asset-archive.h" +#include "backends/platform/android/jni.h" +#include "backends/platform/android/android.h" const char *android_log_tag = "ScummVM"; @@ -68,7 +55,8 @@ extern "C" { expr, file, line); } - void __assert2(const char *file, int line, const char *func, const char *expr) { + void __assert2(const char *file, int line, const char *func, + const char *expr) { __android_log_assert(expr, android_log_tag, "Assertion failure: '%s' in %s:%d (%s)", expr, file, line, func); @@ -106,234 +94,24 @@ void checkGlError(const char *expr, const char *file, int line) { } #endif -static JavaVM *cached_jvm; -static jfieldID FID_Event_type; -static jfieldID FID_Event_synthetic; -static jfieldID FID_Event_kbd_keycode; -static jfieldID FID_Event_kbd_ascii; -static jfieldID FID_Event_kbd_flags; -static jfieldID FID_Event_mouse_x; -static jfieldID FID_Event_mouse_y; -static jfieldID FID_Event_mouse_relative; -static jfieldID FID_ScummVM_nativeScummVM; -static jmethodID MID_Object_wait; - -JNIEnv *JNU_GetEnv() { - JNIEnv *env = 0; - - jint res = cached_jvm->GetEnv((void **)&env, JNI_VERSION_1_2); - - if (res != JNI_OK) { - LOGE("GetEnv() failed: %d", res); - abort(); - } - - return env; -} - -static void JNU_ThrowByName(JNIEnv *env, const char *name, const char *msg) { - jclass cls = env->FindClass(name); - - // if cls is 0, an exception has already been thrown - if (cls != 0) - env->ThrowNew(cls, msg); - - env->DeleteLocalRef(cls); -} - // floating point. use sparingly template <class T> static inline T scalef(T in, float numerator, float denominator) { return static_cast<float>(in) * numerator / denominator; } -static inline GLfixed xdiv(int numerator, int denominator) { - assert(numerator < (1 << 16)); - return (numerator << 16) / denominator; -} - -#ifdef DYNAMIC_MODULES -class AndroidPluginProvider : public POSIXPluginProvider { -protected: - virtual void addCustomDirectories(Common::FSList &dirs) const; -}; -#endif - -class OSystem_Android : public BaseBackend, public PaletteManager { -private: - // back pointer to (java) peer instance - jobject _back_ptr; - - jmethodID MID_displayMessageOnOSD; - jmethodID MID_setWindowCaption; - jmethodID MID_initBackend; - jmethodID MID_audioSampleRate; - jmethodID MID_showVirtualKeyboard; - jmethodID MID_getSysArchives; - jmethodID MID_getPluginDirectories; - jmethodID MID_setupScummVMSurface; - jmethodID MID_destroyScummVMSurface; - jmethodID MID_swapBuffers; - - int _screen_changeid; - int _egl_surface_width; - int _egl_surface_height; - - bool _force_redraw; - - // Game layer - GLESPaletteTexture *_game_texture; - int _shake_offset; - Common::Rect _focus_rect; - - // Overlay layer - GLES4444Texture *_overlay_texture; - bool _show_overlay; - - // Mouse layer - GLESPaletteATexture *_mouse_texture; - Common::Point _mouse_hotspot; - int _mouse_targetscale; - bool _show_mouse; - bool _use_mouse_palette; - - Common::Queue<Common::Event> _event_queue; - MutexRef _event_queue_lock; - - bool _timer_thread_exit; - pthread_t _timer_thread; - static void *timerThreadFunc(void *arg); - - bool _enable_zoning; - bool _virtkeybd_on; - - Common::SaveFileManager *_savefile; - Audio::MixerImpl *_mixer; - Common::TimerManager *_timer; - FilesystemFactory *_fsFactory; - Common::Archive *_asset_archive; - timeval _startTime; - - void setupScummVMSurface(); - void destroyScummVMSurface(); - void setupKeymapper(); - void _setCursorPalette(const byte *colors, uint start, uint num); - -public: - OSystem_Android(jobject am); - virtual ~OSystem_Android(); - bool initJavaHooks(JNIEnv *env, jobject self); - - static OSystem_Android *fromJavaObject(JNIEnv *env, jobject obj); - virtual void initBackend(); - void addPluginDirectories(Common::FSList &dirs) const; - void enableZoning(bool enable) { _enable_zoning = enable; } - void setSurfaceSize(int width, int height) { - _egl_surface_width = width; - _egl_surface_height = height; - } - - virtual bool hasFeature(Feature f); - virtual void setFeatureState(Feature f, bool enable); - virtual bool getFeatureState(Feature f); - virtual const GraphicsMode *getSupportedGraphicsModes() const; - virtual int getDefaultGraphicsMode() const; - bool setGraphicsMode(const char *name); - virtual bool setGraphicsMode(int mode); - virtual int getGraphicsMode() const; - virtual void initSize(uint width, uint height, - const Graphics::PixelFormat *format); - - virtual int getScreenChangeID() const { - return _screen_changeid; - } - - virtual int16 getHeight(); - virtual int16 getWidth(); - - virtual PaletteManager *getPaletteManager() { - return this; - } - -protected: - // PaletteManager API - virtual void setPalette(const byte *colors, uint start, uint num); - virtual void grabPalette(byte *colors, uint start, uint num); - -public: - virtual void copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h); - virtual void updateScreen(); - virtual Graphics::Surface *lockScreen(); - virtual void unlockScreen(); - virtual void setShakePos(int shakeOffset); - virtual void fillScreen(uint32 col); - virtual void setFocusRectangle(const Common::Rect& rect); - virtual void clearFocusRectangle(); - - virtual void showOverlay(); - virtual void hideOverlay(); - virtual void clearOverlay(); - virtual void grabOverlay(OverlayColor *buf, int pitch); - virtual void copyRectToOverlay(const OverlayColor *buf, int pitch, int x, int y, int w, int h); - virtual int16 getOverlayHeight(); - virtual int16 getOverlayWidth(); - - // RGBA 4444 - virtual Graphics::PixelFormat getOverlayFormat() const { - Graphics::PixelFormat format; - - format.bytesPerPixel = 2; - format.rLoss = 8 - 4; - format.gLoss = 8 - 4; - format.bLoss = 8 - 4; - format.aLoss = 8 - 4; - format.rShift = 3 * 4; - format.gShift = 2 * 4; - format.bShift = 1 * 4; - format.aShift = 0 * 4; - - return format; - } - - virtual bool showMouse(bool visible); - - virtual void warpMouse(int x, int y); - virtual void setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int cursorTargetScale, const Graphics::PixelFormat *format); - virtual void setCursorPalette(const byte *colors, uint start, uint num); - virtual void disableCursorPalette(bool disable); - - virtual bool pollEvent(Common::Event &event); - void pushEvent(const Common::Event& event); - virtual uint32 getMillis(); - virtual void delayMillis(uint msecs); - - virtual MutexRef createMutex(void); - virtual void lockMutex(MutexRef mutex); - virtual void unlockMutex(MutexRef mutex); - virtual void deleteMutex(MutexRef mutex); - - virtual void quit(); - - virtual void setWindowCaption(const char *caption); - virtual void displayMessageOnOSD(const char *msg); - virtual void showVirtualKeyboard(bool enable); - - virtual Common::SaveFileManager *getSavefileManager(); - virtual Audio::Mixer *getMixer(); - virtual void getTimeAndDate(TimeDate &t) const; - virtual Common::TimerManager *getTimerManager(); - virtual FilesystemFactory *getFilesystemFactory(); - virtual void logMessage(LogMessageType::Type type, const char *message); - virtual void addSysArchivesToSearchSet(Common::SearchSet &s, int priority = 0); -}; - -OSystem_Android::OSystem_Android(jobject am) : - _back_ptr(0), +OSystem_Android::OSystem_Android(int audio_sample_rate, int audio_buffer_size) : + _audio_sample_rate(audio_sample_rate), + _audio_buffer_size(audio_buffer_size), _screen_changeid(0), + _egl_surface_width(0), + _egl_surface_height(0), _force_redraw(false), _game_texture(0), _overlay_texture(0), _mouse_texture(0), + _mouse_texture_palette(0), + _mouse_texture_rgb(0), _use_mouse_palette(false), _show_mouse(false), _show_overlay(false), @@ -342,7 +120,6 @@ OSystem_Android::OSystem_Android(jobject am) : _mixer(0), _timer(0), _fsFactory(new POSIXFilesystemFactory()), - _asset_archive(new AndroidAssetArchive(am)), _shake_offset(0), _event_queue_lock(createMutex()) { } @@ -350,172 +127,182 @@ OSystem_Android::OSystem_Android(jobject am) : OSystem_Android::~OSystem_Android() { ENTER(); - delete _game_texture; - delete _overlay_texture; - delete _mouse_texture; - - destroyScummVMSurface(); - - JNIEnv *env = JNU_GetEnv(); - //env->DeleteWeakGlobalRef(_back_ptr); - env->DeleteGlobalRef(_back_ptr); - delete _savefile; - delete _mixer; delete _timer; + delete _mixer; delete _fsFactory; - delete _asset_archive; deleteMutex(_event_queue_lock); } -OSystem_Android *OSystem_Android::fromJavaObject(JNIEnv *env, jobject obj) { - jlong peer = env->GetLongField(obj, FID_ScummVM_nativeScummVM); - return (OSystem_Android *)peer; -} +void *OSystem_Android::timerThreadFunc(void *arg) { + OSystem_Android *system = (OSystem_Android *)arg; + DefaultTimerManager *timer = (DefaultTimerManager *)(system->_timer); -bool OSystem_Android::initJavaHooks(JNIEnv *env, jobject self) { - // weak global ref to allow class to be unloaded - // ... except dalvik doesn't implement NewWeakGlobalRef (yet) - //_back_ptr = env->NewWeakGlobalRef(self); - _back_ptr = env->NewGlobalRef(self); - - jclass cls = env->GetObjectClass(_back_ptr); - -#define FIND_METHOD(name, signature) do { \ - MID_ ## name = env->GetMethodID(cls, #name, signature); \ - if (MID_ ## name == 0) \ - return false; \ - } while (0) - - FIND_METHOD(setWindowCaption, "(Ljava/lang/String;)V"); - FIND_METHOD(displayMessageOnOSD, "(Ljava/lang/String;)V"); - FIND_METHOD(initBackend, "()V"); - FIND_METHOD(audioSampleRate, "()I"); - FIND_METHOD(showVirtualKeyboard, "(Z)V"); - FIND_METHOD(getSysArchives, "()[Ljava/lang/String;"); - FIND_METHOD(getPluginDirectories, "()[Ljava/lang/String;"); - FIND_METHOD(setupScummVMSurface, "()V"); - FIND_METHOD(destroyScummVMSurface, "()V"); - FIND_METHOD(swapBuffers, "()Z"); - -#undef FIND_METHOD + // renice this thread to boost the audio thread + if (setpriority(PRIO_PROCESS, 0, 19) < 0) + LOGW("couldn't renice the timer thread"); - return true; -} + JNI::attachThread(); -static void ScummVM_create(JNIEnv *env, jobject self, jobject am) { - OSystem_Android *cpp_obj = new OSystem_Android(am); + struct timespec tv; + tv.tv_sec = 0; + tv.tv_nsec = 100 * 1000 * 1000; // 100ms - // Exception already thrown by initJavaHooks? - if (!cpp_obj->initJavaHooks(env, self)) - return; + while (!system->_timer_thread_exit) { + if (JNI::pause) { + LOGD("timer thread going to sleep"); + sem_wait(&JNI::pause_sem); + LOGD("timer thread woke up"); + } - env->SetLongField(self, FID_ScummVM_nativeScummVM, (jlong)cpp_obj); + timer->handler(); + nanosleep(&tv, 0); + } -#ifdef DYNAMIC_MODULES - PluginManager::instance().addPluginProvider(new AndroidPluginProvider()); -#endif -} + JNI::detachThread(); -static void ScummVM_nativeDestroy(JNIEnv *env, jobject self) { - OSystem_Android *cpp_obj = OSystem_Android::fromJavaObject(env, self); - delete cpp_obj; + return 0; } -static void ScummVM_audioMixCallback(JNIEnv *env, jobject self, - jbyteArray jbuf) { - OSystem_Android *cpp_obj = OSystem_Android::fromJavaObject(env, self); - jsize len = env->GetArrayLength(jbuf); - jbyte *buf = env->GetByteArrayElements(jbuf, 0); +void *OSystem_Android::audioThreadFunc(void *arg) { + JNI::attachThread(); - if (buf == 0) { - warning("Unable to get Java audio byte array. Skipping"); - return; - } + OSystem_Android *system = (OSystem_Android *)arg; + Audio::MixerImpl *mixer = system->_mixer; - Audio::MixerImpl *mixer = - static_cast<Audio::MixerImpl *>(cpp_obj->getMixer()); - assert(mixer); - mixer->mixCallback(reinterpret_cast<byte *>(buf), len); + uint buf_size = system->_audio_buffer_size; - env->ReleaseByteArrayElements(jbuf, buf, 0); -} + JNIEnv *env = JNI::getEnv(); -static void ScummVM_setConfManInt(JNIEnv *env, jclass cls, - jstring key_obj, jint value) { - ENTER("%p, %d", key_obj, (int)value); + jbyteArray bufa = env->NewByteArray(buf_size); - const char *key = env->GetStringUTFChars(key_obj, 0); + bool paused = true; - if (key == 0) - return; + byte *buf; + int offset, left, written; + int samples, i; - ConfMan.setInt(key, value); + struct timespec tv_delay; + tv_delay.tv_sec = 0; + tv_delay.tv_nsec = 20 * 1000 * 1000; - env->ReleaseStringUTFChars(key_obj, key); -} + uint msecs_full = buf_size * 1000 / (mixer->getOutputRate() * 2 * 2); -static void ScummVM_setConfManString(JNIEnv *env, jclass cls, jstring key_obj, - jstring value_obj) { - ENTER("%p, %p", key_obj, value_obj); + struct timespec tv_full; + tv_full.tv_sec = 0; + tv_full.tv_nsec = msecs_full * 1000 * 1000; - const char *key = env->GetStringUTFChars(key_obj, 0); + bool silence; + uint silence_count = 33; - if (key == 0) - return; + while (!system->_audio_thread_exit) { + if (JNI::pause) { + JNI::setAudioStop(); - const char *value = env->GetStringUTFChars(value_obj, 0); + paused = true; + silence_count = 33; - if (value == 0) { - env->ReleaseStringUTFChars(key_obj, key); - return; - } + LOGD("audio thread going to sleep"); + sem_wait(&JNI::pause_sem); + LOGD("audio thread woke up"); + } - ConfMan.set(key, value); + buf = (byte *)env->GetPrimitiveArrayCritical(bufa, 0); + assert(buf); - env->ReleaseStringUTFChars(value_obj, value); - env->ReleaseStringUTFChars(key_obj, key); -} + samples = mixer->mixCallback(buf, buf_size); -void *OSystem_Android::timerThreadFunc(void *arg) { - OSystem_Android *system = (OSystem_Android *)arg; - DefaultTimerManager *timer = (DefaultTimerManager *)(system->_timer); + silence = samples < 1; - JNIEnv *env = 0; - jint res = cached_jvm->AttachCurrentThread(&env, 0); + // looks stupid, and it is, but currently there's no way to detect + // silence-only buffers from the mixer + if (!silence) { + silence = true; - if (res != JNI_OK) { - LOGE("AttachCurrentThread() failed: %d", res); - abort(); - } + for (i = 0; i < samples; i += 2) + // SID streams constant crap + if (READ_UINT16(buf + i) > 32) { + silence = false; + break; + } + } - struct timespec tv; - tv.tv_sec = 0; - tv.tv_nsec = 100 * 1000 * 1000; // 100ms + env->ReleasePrimitiveArrayCritical(bufa, buf, 0); - while (!system->_timer_thread_exit) { - timer->handler(); - nanosleep(&tv, 0); - } + if (silence) { + if (!paused) + silence_count++; + + // only pause after a while to prevent toggle mania + if (silence_count > 32) { + if (!paused) { + LOGD("AudioTrack pause"); + + JNI::setAudioPause(); + paused = true; + } + + nanosleep(&tv_full, 0); + + continue; + } + } + + if (paused) { + LOGD("AudioTrack play"); + + JNI::setAudioPlay(); + paused = false; + + silence_count = 0; + } + + offset = 0; + left = buf_size; + written = 0; - res = cached_jvm->DetachCurrentThread(); + while (left > 0) { + written = JNI::writeAudio(env, bufa, offset, left); - if (res != JNI_OK) { - LOGE("DetachCurrentThread() failed: %d", res); - abort(); + if (written < 0) { + LOGE("AudioTrack error: %d", written); + break; + } + + // buffer full + if (written < left) + nanosleep(&tv_delay, 0); + + offset += written; + left -= written; + } + + if (written < 0) + break; + + // sleep a little, prepare the next buffer, and run into the + // blocking AudioTrack.write + nanosleep(&tv_delay, 0); } + JNI::setAudioStop(); + + env->DeleteLocalRef(bufa); + + JNI::detachThread(); + return 0; } void OSystem_Android::initBackend() { ENTER(); - JNIEnv *env = JNU_GetEnv(); + _main_thread = pthread_self(); ConfMan.setInt("autosave_period", 0); - ConfMan.setInt("FM_medium_quality", true); + ConfMan.setBool("FM_high_quality", false); + ConfMan.setBool("FM_medium_quality", true); // must happen before creating TimerManager to avoid race in // creating EventManager @@ -529,74 +316,34 @@ void OSystem_Android::initBackend() { gettimeofday(&_startTime, 0); - jint sample_rate = env->CallIntMethod(_back_ptr, MID_audioSampleRate); - if (env->ExceptionCheck()) { - warning("Error finding audio sample rate - assuming 11025HZ"); - - env->ExceptionDescribe(); - env->ExceptionClear(); - - sample_rate = 11025; - } - - _mixer = new Audio::MixerImpl(this, sample_rate); + _mixer = new Audio::MixerImpl(this, _audio_sample_rate); _mixer->setReady(true); - env->CallVoidMethod(_back_ptr, MID_initBackend); + _timer_thread_exit = false; + pthread_create(&_timer_thread, 0, timerThreadFunc, this); - if (env->ExceptionCheck()) { - error("Error in Java initBackend"); + _audio_thread_exit = false; + pthread_create(&_audio_thread, 0, audioThreadFunc, this); - env->ExceptionDescribe(); - env->ExceptionClear(); - } + initSurface(); + initViewport(); - _timer_thread_exit = false; - pthread_create(&_timer_thread, 0, timerThreadFunc, this); + _game_texture = new GLESPalette888Texture(); + _overlay_texture = new GLES4444Texture(); + _mouse_texture_palette = new GLESPalette8888Texture(); + _mouse_texture = _mouse_texture_palette; - OSystem::initBackend(); + // renice this thread to boost the audio thread + if (setpriority(PRIO_PROCESS, 0, 19) < 0) + warning("couldn't renice the main thread"); - setupScummVMSurface(); + JNI::setReadyForEvents(true); } void OSystem_Android::addPluginDirectories(Common::FSList &dirs) const { ENTER(); - JNIEnv *env = JNU_GetEnv(); - - jobjectArray array = - (jobjectArray)env->CallObjectMethod(_back_ptr, MID_getPluginDirectories); - if (env->ExceptionCheck()) { - warning("Error finding plugin directories"); - - env->ExceptionDescribe(); - env->ExceptionClear(); - - return; - } - - jsize size = env->GetArrayLength(array); - for (jsize i = 0; i < size; ++i) { - jstring path_obj = (jstring)env->GetObjectArrayElement(array, i); - - if (path_obj == 0) - continue; - - const char *path = env->GetStringUTFChars(path_obj, 0); - if (path == 0) { - warning("Error getting string characters from plugin directory"); - - env->ExceptionClear(); - env->DeleteLocalRef(path_obj); - - continue; - } - - dirs.push_back(Common::FSNode(path)); - - env->ReleaseStringUTFChars(path_obj, path); - env->DeleteLocalRef(path_obj); - } + JNI::getPluginDirectories(dirs); } bool OSystem_Android::hasFeature(Feature f) { @@ -627,430 +374,6 @@ bool OSystem_Android::getFeatureState(Feature f) { } } -const OSystem::GraphicsMode *OSystem_Android::getSupportedGraphicsModes() const { - static const OSystem::GraphicsMode s_supportedGraphicsModes[] = { - { "default", "Default", 1 }, - { 0, 0, 0 }, - }; - - return s_supportedGraphicsModes; -} - - -int OSystem_Android::getDefaultGraphicsMode() const { - return 1; -} - -bool OSystem_Android::setGraphicsMode(const char *mode) { - ENTER("%s", mode); - return true; -} - -bool OSystem_Android::setGraphicsMode(int mode) { - ENTER("%d", mode); - return true; -} - -int OSystem_Android::getGraphicsMode() const { - return 1; -} - -void OSystem_Android::setupScummVMSurface() { - ENTER(); - - JNIEnv *env = JNU_GetEnv(); - env->CallVoidMethod(_back_ptr, MID_setupScummVMSurface); - - if (env->ExceptionCheck()) - return; - - // EGL set up with a new surface. Initialise OpenGLES context. - GLESTexture::initGLExtensions(); - - // Turn off anything that looks like 3D ;) - GLCALL(glDisable(GL_CULL_FACE)); - GLCALL(glDisable(GL_DEPTH_TEST)); - GLCALL(glDisable(GL_LIGHTING)); - GLCALL(glDisable(GL_FOG)); - GLCALL(glDisable(GL_DITHER)); - - GLCALL(glShadeModel(GL_FLAT)); - GLCALL(glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST)); - - GLCALL(glEnable(GL_BLEND)); - GLCALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - - GLCALL(glEnableClientState(GL_VERTEX_ARRAY)); - GLCALL(glEnableClientState(GL_TEXTURE_COORD_ARRAY)); - - GLCALL(glEnable(GL_TEXTURE_2D)); - - if (!_game_texture) - _game_texture = new GLESPaletteTexture(); - else - _game_texture->reinitGL(); - - if (!_overlay_texture) - _overlay_texture = new GLES4444Texture(); - else - _overlay_texture->reinitGL(); - - if (!_mouse_texture) - _mouse_texture = new GLESPaletteATexture(); - else - _mouse_texture->reinitGL(); - - GLCALL(glViewport(0, 0, _egl_surface_width, _egl_surface_height)); - - GLCALL(glMatrixMode(GL_PROJECTION)); - GLCALL(glLoadIdentity()); - GLCALL(glOrthof(0, _egl_surface_width, _egl_surface_height, 0, -1, 1)); - GLCALL(glMatrixMode(GL_MODELVIEW)); - GLCALL(glLoadIdentity()); - - clearFocusRectangle(); -} - -void OSystem_Android::destroyScummVMSurface() { - JNIEnv *env = JNU_GetEnv(); - env->CallVoidMethod(_back_ptr, MID_destroyScummVMSurface); - // Can't use OpenGLES functions after this -} - -void OSystem_Android::initSize(uint width, uint height, - const Graphics::PixelFormat *format) { - ENTER("%d, %d, %p", width, height, format); - - _game_texture->allocBuffer(width, height); - - // Cap at 320x200 or the ScummVM themes abort :/ - GLuint overlay_width = MIN(_egl_surface_width, 320); - GLuint overlay_height = MIN(_egl_surface_height, 200); - _overlay_texture->allocBuffer(overlay_width, overlay_height); - - // Don't know mouse size yet - it gets reallocated in - // setMouseCursor. We need the palette allocated before - // setMouseCursor however, so just take a guess at the desired - // size (it's small). - _mouse_texture->allocBuffer(20, 20); -} - -int16 OSystem_Android::getHeight() { - return _game_texture->height(); -} - -int16 OSystem_Android::getWidth() { - return _game_texture->width(); -} - -void OSystem_Android::setPalette(const byte *colors, uint start, uint num) { - ENTER("%p, %u, %u", colors, start, num); - - if (!_use_mouse_palette) - _setCursorPalette(colors, start, num); - - memcpy(_game_texture->palette() + start * 3, colors, num * 3); -} - -void OSystem_Android::grabPalette(byte *colors, uint start, uint num) { - ENTER("%p, %u, %u", colors, start, num); - memcpy(colors, _game_texture->palette_const() + start * 3, num * 3); -} - -void OSystem_Android::copyRectToScreen(const byte *buf, int pitch, - int x, int y, int w, int h) { - ENTER("%p, %d, %d, %d, %d, %d", buf, pitch, x, y, w, h); - - _game_texture->updateBuffer(x, y, w, h, buf, pitch); -} - -void OSystem_Android::updateScreen() { - //ENTER(); - - if (!_force_redraw && - !_game_texture->dirty() && - !_overlay_texture->dirty() && - !_mouse_texture->dirty()) - return; - - _force_redraw = false; - - GLCALL(glPushMatrix()); - - if (_shake_offset != 0 || - (!_focus_rect.isEmpty() && - !Common::Rect(_game_texture->width(), - _game_texture->height()).contains(_focus_rect))) { - // These are the only cases where _game_texture doesn't - // cover the entire screen. - GLCALL(glClearColorx(0, 0, 0, 1 << 16)); - GLCALL(glClear(GL_COLOR_BUFFER_BIT)); - - // Move everything up by _shake_offset (game) pixels - GLCALL(glTranslatex(0, -_shake_offset << 16, 0)); - } - - if (_focus_rect.isEmpty()) { - _game_texture->drawTexture(0, 0, - _egl_surface_width, _egl_surface_height); - } else { - GLCALL(glPushMatrix()); - GLCALL(glScalex(xdiv(_egl_surface_width, _focus_rect.width()), - xdiv(_egl_surface_height, _focus_rect.height()), - 1 << 16)); - GLCALL(glTranslatex(-_focus_rect.left << 16, - -_focus_rect.top << 16, 0)); - GLCALL(glScalex(xdiv(_game_texture->width(), _egl_surface_width), - xdiv(_game_texture->height(), _egl_surface_height), - 1 << 16)); - - _game_texture->drawTexture(0, 0, - _egl_surface_width, _egl_surface_height); - GLCALL(glPopMatrix()); - } - - int cs = _mouse_targetscale; - - if (_show_overlay) { - // ugly, but the modern theme sets a wacko factor, only god knows why - cs = 1; - - GLCALL(_overlay_texture->drawTexture(0, 0, - _egl_surface_width, - _egl_surface_height)); - } - - if (_show_mouse) { - GLCALL(glPushMatrix()); - - // Scale up ScummVM -> OpenGL (pixel) coordinates - int texwidth, texheight; - - if (_show_overlay) { - texwidth = getOverlayWidth(); - texheight = getOverlayHeight(); - } else { - texwidth = getWidth(); - texheight = getHeight(); - } - - GLCALL(glScalex(xdiv(_egl_surface_width, texwidth), - xdiv(_egl_surface_height, texheight), - 1 << 16)); - - GLCALL(glTranslatex((-_mouse_hotspot.x * cs) << 16, - (-_mouse_hotspot.y * cs) << 16, - 0)); - - // Note the extra half texel to position the mouse in - // the middle of the x,y square: - const Common::Point& mouse = getEventManager()->getMousePos(); - GLCALL(glTranslatex((mouse.x << 16) | 1 << 15, - (mouse.y << 16) | 1 << 15, 0)); - - GLCALL(glScalex(cs << 16, cs << 16, 1 << 16)); - - _mouse_texture->drawTexture(); - - GLCALL(glPopMatrix()); - } - - GLCALL(glPopMatrix()); - - JNIEnv *env = JNU_GetEnv(); - if (!env->CallBooleanMethod(_back_ptr, MID_swapBuffers)) { - // Context lost -> need to reinit GL - destroyScummVMSurface(); - setupScummVMSurface(); - } -} - -Graphics::Surface *OSystem_Android::lockScreen() { - ENTER(); - - Graphics::Surface *surface = _game_texture->surface(); - assert(surface->pixels); - - return surface; -} - -void OSystem_Android::unlockScreen() { - ENTER(); - - assert(_game_texture->dirty()); -} - -void OSystem_Android::setShakePos(int shake_offset) { - ENTER("%d", shake_offset); - - if (_shake_offset != shake_offset) { - _shake_offset = shake_offset; - _force_redraw = true; - } -} - -void OSystem_Android::fillScreen(uint32 col) { - ENTER("%u", col); - - assert(col < 256); - _game_texture->fillBuffer(col); -} - -void OSystem_Android::setFocusRectangle(const Common::Rect& rect) { - ENTER("%d, %d, %d, %d", rect.left, rect.top, rect.right, rect.bottom); - - if (_enable_zoning) { - _focus_rect = rect; - _force_redraw = true; - } -} - -void OSystem_Android::clearFocusRectangle() { - ENTER(); - - if (_enable_zoning) { - _focus_rect = Common::Rect(); - _force_redraw = true; - } -} - -void OSystem_Android::showOverlay() { - ENTER(); - - _show_overlay = true; - _force_redraw = true; -} - -void OSystem_Android::hideOverlay() { - ENTER(); - - _show_overlay = false; - _force_redraw = true; -} - -void OSystem_Android::clearOverlay() { - ENTER(); - - _overlay_texture->fillBuffer(0); - - // Shouldn't need this, but works around a 'blank screen' bug on Nexus1 - updateScreen(); -} - -void OSystem_Android::grabOverlay(OverlayColor *buf, int pitch) { - ENTER("%p, %d", buf, pitch); - - // We support overlay alpha blending, so the pixel data here - // shouldn't actually be used. Let's fill it with zeros, I'm sure - // it will be fine... - const Graphics::Surface *surface = _overlay_texture->surface_const(); - assert(surface->bytesPerPixel == sizeof(buf[0])); - - int h = surface->h; - - do { - memset(buf, 0, surface->w * sizeof(buf[0])); - - // This 'pitch' is pixels not bytes - buf += pitch; - } while (--h); -} - -void OSystem_Android::copyRectToOverlay(const OverlayColor *buf, int pitch, - int x, int y, int w, int h) { - ENTER("%p, %d, %d, %d, %d, %d", buf, pitch, x, y, w, h); - - const Graphics::Surface *surface = _overlay_texture->surface_const(); - assert(surface->bytesPerPixel == sizeof(buf[0])); - - // This 'pitch' is pixels not bytes - _overlay_texture->updateBuffer(x, y, w, h, buf, pitch * sizeof(buf[0])); - - // Shouldn't need this, but works around a 'blank screen' bug on Nexus1? - updateScreen(); -} - -int16 OSystem_Android::getOverlayHeight() { - return _overlay_texture->height(); -} - -int16 OSystem_Android::getOverlayWidth() { - return _overlay_texture->width(); -} - -bool OSystem_Android::showMouse(bool visible) { - ENTER("%d", visible); - - _show_mouse = visible; - - return true; -} - -void OSystem_Android::warpMouse(int x, int y) { - ENTER("%d, %d", x, y); - - // We use only the eventmanager's idea of the current mouse - // position, so there is nothing extra to do here. -} - -void OSystem_Android::setMouseCursor(const byte *buf, uint w, uint h, - int hotspotX, int hotspotY, - uint32 keycolor, int cursorTargetScale, - const Graphics::PixelFormat *format) { - ENTER("%p, %u, %u, %d, %d, %u, %d, %p", buf, w, h, hotspotX, hotspotY, - keycolor, cursorTargetScale, format); - - assert(keycolor < 256); - - _mouse_texture->allocBuffer(w, h); - - // Update palette alpha based on keycolor - byte *palette = _mouse_texture->palette(); - int i = 256; - - do { - palette[3] = 0xff; - palette += 4; - } while (--i); - - palette = _mouse_texture->palette(); - palette[keycolor * 4 + 3] = 0x00; - - _mouse_texture->updateBuffer(0, 0, w, h, buf, w); - - _mouse_hotspot = Common::Point(hotspotX, hotspotY); - _mouse_targetscale = cursorTargetScale; -} - -void OSystem_Android::_setCursorPalette(const byte *colors, - uint start, uint num) { - byte *palette = _mouse_texture->palette() + start * 4; - - do { - for (int i = 0; i < 3; ++i) - palette[i] = colors[i]; - - // Leave alpha untouched to preserve keycolor - - palette += 4; - colors += 3; - } while (--num); -} - -void OSystem_Android::setCursorPalette(const byte *colors, - uint start, uint num) { - ENTER("%p, %u, %u", colors, start, num); - - _setCursorPalette(colors, start, num); - _use_mouse_palette = true; -} - -void OSystem_Android::disableCursorPalette(bool disable) { - ENTER("%d", disable); - - _use_mouse_palette = !disable; -} - void OSystem_Android::setupKeymapper() { #ifdef ENABLE_KEYMAPPER using namespace Common; @@ -1082,6 +405,48 @@ void OSystem_Android::setupKeymapper() { bool OSystem_Android::pollEvent(Common::Event &event) { //ENTER(); + if (pthread_self() == _main_thread) { + if (_screen_changeid != JNI::surface_changeid) { + if (JNI::egl_surface_width > 0 && JNI::egl_surface_height > 0) { + if (_egl_surface_width > 0 && _egl_surface_height > 0) { + // surface still alive but changed + _screen_changeid = JNI::surface_changeid; + _egl_surface_width = JNI::egl_surface_width; + _egl_surface_height = JNI::egl_surface_height; + + initViewport(); + // double buffered, flip twice + _force_redraw = true; + updateScreen(); + _force_redraw = true; + + event.type = Common::EVENT_SCREEN_CHANGED; + + return true; + } else { + // new surface + initSurface(); + _force_redraw = true; + + event.type = Common::EVENT_SCREEN_CHANGED; + + return true; + } + } else { + // surface lost + deinitSurface(); + } + } + + if (JNI::pause) { + deinitSurface(); + + LOGD("main thread going to sleep"); + sem_wait(&JNI::pause_sem); + LOGD("main thread woke up"); + } + } + lockMutex(_event_queue_lock); if (_event_queue.empty()) { @@ -1119,9 +484,12 @@ bool OSystem_Android::pollEvent(Common::Event &event) { } else { // Touchscreen events need to be converted // from device to game coords first. - const GLESTexture *tex = _show_overlay - ? static_cast<GLESTexture *>(_overlay_texture) - : static_cast<GLESTexture *>(_game_texture); + const GLESTexture *tex; + if (_show_overlay) + tex = _overlay_texture; + else + tex = _game_texture; + event.mouse.x = scalef(event.mouse.x, tex->width(), _egl_surface_width); event.mouse.y = scalef(event.mouse.y, tex->height(), @@ -1130,12 +498,6 @@ bool OSystem_Android::pollEvent(Common::Event &event) { } break; } - case Common::EVENT_SCREEN_CHANGED: - debug("EVENT_SCREEN_CHANGED"); - _screen_changeid++; - destroyScummVMSurface(); - setupScummVMSurface(); - break; default: break; } @@ -1168,52 +530,6 @@ void OSystem_Android::pushEvent(const Common::Event& event) { unlockMutex(_event_queue_lock); } -static void ScummVM_pushEvent(JNIEnv *env, jobject self, jobject java_event) { - OSystem_Android *cpp_obj = OSystem_Android::fromJavaObject(env, self); - - Common::Event event; - event.type = (Common::EventType)env->GetIntField(java_event, - FID_Event_type); - - event.synthetic = - env->GetBooleanField(java_event, FID_Event_synthetic); - - switch (event.type) { - case Common::EVENT_KEYDOWN: - case Common::EVENT_KEYUP: - event.kbd.keycode = (Common::KeyCode)env->GetIntField( - java_event, FID_Event_kbd_keycode); - event.kbd.ascii = static_cast<int>(env->GetIntField( - java_event, FID_Event_kbd_ascii)); - event.kbd.flags = static_cast<int>(env->GetIntField( - java_event, FID_Event_kbd_flags)); - break; - case Common::EVENT_MOUSEMOVE: - case Common::EVENT_LBUTTONDOWN: - case Common::EVENT_LBUTTONUP: - case Common::EVENT_RBUTTONDOWN: - case Common::EVENT_RBUTTONUP: - case Common::EVENT_WHEELUP: - case Common::EVENT_WHEELDOWN: - case Common::EVENT_MBUTTONDOWN: - case Common::EVENT_MBUTTONUP: - event.mouse.x = - env->GetIntField(java_event, FID_Event_mouse_x); - event.mouse.y = - env->GetIntField(java_event, FID_Event_mouse_y); - // This is a terrible hack. We stash "relativeness" - // in the kbd.flags field until pollEvent() can work - // it out. - event.kbd.flags = env->GetBooleanField( - java_event, FID_Event_mouse_relative) ? 1 : 0; - break; - default: - break; - } - - cpp_obj->pushEvent(event); -} - uint32 OSystem_Android::getMillis() { timeval curTime; @@ -1229,6 +545,7 @@ void OSystem_Android::delayMillis(uint msecs) { OSystem::MutexRef OSystem_Android::createMutex() { pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); @@ -1267,58 +584,38 @@ void OSystem_Android::deleteMutex(MutexRef mutex) { void OSystem_Android::quit() { ENTER(); + JNI::setReadyForEvents(false); + + _audio_thread_exit = true; + pthread_join(_audio_thread, 0); + _timer_thread_exit = true; pthread_join(_timer_thread, 0); + + delete _game_texture; + delete _overlay_texture; + delete _mouse_texture_palette; + delete _mouse_texture_rgb; + + deinitSurface(); } void OSystem_Android::setWindowCaption(const char *caption) { ENTER("%s", caption); - JNIEnv *env = JNU_GetEnv(); - jstring java_caption = env->NewStringUTF(caption); - env->CallVoidMethod(_back_ptr, MID_setWindowCaption, java_caption); - - if (env->ExceptionCheck()) { - warning("Failed to set window caption"); - - env->ExceptionDescribe(); - env->ExceptionClear(); - } - - env->DeleteLocalRef(java_caption); + JNI::setWindowCaption(caption); } void OSystem_Android::displayMessageOnOSD(const char *msg) { ENTER("%s", msg); - JNIEnv *env = JNU_GetEnv(); - jstring java_msg = env->NewStringUTF(msg); - - env->CallVoidMethod(_back_ptr, MID_displayMessageOnOSD, java_msg); - - if (env->ExceptionCheck()) { - warning("Failed to display OSD message"); - - env->ExceptionDescribe(); - env->ExceptionClear(); - } - - env->DeleteLocalRef(java_msg); + JNI::displayMessageOnOSD(msg); } void OSystem_Android::showVirtualKeyboard(bool enable) { ENTER("%d", enable); - JNIEnv *env = JNU_GetEnv(); - - env->CallVoidMethod(_back_ptr, MID_showVirtualKeyboard, enable); - - if (env->ExceptionCheck()) { - error("Error trying to show virtual keyboard"); - - env->ExceptionDescribe(); - env->ExceptionClear(); - } + JNI::showVirtualKeyboard(enable); } Common::SaveFileManager *OSystem_Android::getSavefileManager() { @@ -1355,37 +652,13 @@ FilesystemFactory *OSystem_Android::getFilesystemFactory() { void OSystem_Android::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) { - s.add("ASSET", _asset_archive, priority, false); - - JNIEnv *env = JNU_GetEnv(); - - jobjectArray array = - (jobjectArray)env->CallObjectMethod(_back_ptr, MID_getSysArchives); - - if (env->ExceptionCheck()) { - warning("Error finding system archive path"); - - env->ExceptionDescribe(); - env->ExceptionClear(); + ENTER(""); - return; - } - - jsize size = env->GetArrayLength(array); - for (jsize i = 0; i < size; ++i) { - jstring path_obj = (jstring)env->GetObjectArrayElement(array, i); - const char *path = env->GetStringUTFChars(path_obj, 0); - - if (path != 0) { - s.addDirectory(path, path, priority); - env->ReleaseStringUTFChars(path_obj, path); - } - - env->DeleteLocalRef(path_obj); - } + JNI::addSysArchivesToSearchSet(s, priority); } -void OSystem_Android::logMessage(LogMessageType::Type type, const char *message) { +void OSystem_Android::logMessage(LogMessageType::Type type, + const char *message) { switch (type) { case LogMessageType::kDebug: __android_log_write(ANDROID_LOG_DEBUG, android_log_tag, message); @@ -1401,177 +674,11 @@ void OSystem_Android::logMessage(LogMessageType::Type type, const char *message) } } -static jint ScummVM_scummVMMain(JNIEnv *env, jobject self, jobjectArray args) { - OSystem_Android *cpp_obj = OSystem_Android::fromJavaObject(env, self); - - const int MAX_NARGS = 32; - int res = -1; - - int argc = env->GetArrayLength(args); - if (argc > MAX_NARGS) { - JNU_ThrowByName(env, "java/lang/IllegalArgumentException", - "too many arguments"); - return 0; - } - - char *argv[MAX_NARGS]; - - // note use in cleanup loop below - int nargs; - - for (nargs = 0; nargs < argc; ++nargs) { - jstring arg = (jstring)env->GetObjectArrayElement(args, nargs); - - if (arg == 0) { - argv[nargs] = 0; - } else { - const char *cstr = env->GetStringUTFChars(arg, 0); - - argv[nargs] = const_cast<char *>(cstr); - - // exception already thrown? - if (cstr == 0) - goto cleanup; - } - - env->DeleteLocalRef(arg); - } - - g_system = cpp_obj; - assert(g_system); - - LOGI("Entering scummvm_main with %d args", argc); - - res = scummvm_main(argc, argv); - - LOGI("Exiting scummvm_main"); - - g_system->quit(); - -cleanup: - nargs--; - - for (int i = 0; i < nargs; ++i) { - if (argv[i] == 0) - continue; - - jstring arg = (jstring)env->GetObjectArrayElement(args, nargs); - - // Exception already thrown? - if (arg == 0) - return res; - - env->ReleaseStringUTFChars(arg, argv[i]); - env->DeleteLocalRef(arg); - } - - return res; -} - #ifdef DYNAMIC_MODULES void AndroidPluginProvider::addCustomDirectories(Common::FSList &dirs) const { - OSystem_Android *g_system_android = (OSystem_Android *)g_system; - g_system_android->addPluginDirectories(dirs); + ((OSystem_Android *)g_system)->addPluginDirectories(dirs); } #endif -static void ScummVM_enableZoning(JNIEnv *env, jobject self, jboolean enable) { - OSystem_Android *cpp_obj = OSystem_Android::fromJavaObject(env, self); - cpp_obj->enableZoning(enable); -} - -static void ScummVM_setSurfaceSize(JNIEnv *env, jobject self, - jint width, jint height) { - OSystem_Android *cpp_obj = OSystem_Android::fromJavaObject(env, self); - cpp_obj->setSurfaceSize(width, height); -} - -const static JNINativeMethod gMethods[] = { - { "create", "(Landroid/content/res/AssetManager;)V", - (void *)ScummVM_create }, - { "nativeDestroy", "()V", - (void *)ScummVM_nativeDestroy }, - { "scummVMMain", "([Ljava/lang/String;)I", - (void *)ScummVM_scummVMMain }, - { "pushEvent", "(Lorg/inodes/gus/scummvm/Event;)V", - (void *)ScummVM_pushEvent }, - { "audioMixCallback", "([B)V", - (void *)ScummVM_audioMixCallback }, - { "setConfMan", "(Ljava/lang/String;I)V", - (void *)ScummVM_setConfManInt }, - { "setConfMan", "(Ljava/lang/String;Ljava/lang/String;)V", - (void *)ScummVM_setConfManString }, - { "enableZoning", "(Z)V", - (void *)ScummVM_enableZoning }, - { "setSurfaceSize", "(II)V", - (void *)ScummVM_setSurfaceSize }, -}; - -JNIEXPORT jint JNICALL -JNI_OnLoad(JavaVM *jvm, void *reserved) { - cached_jvm = jvm; - - JNIEnv *env; - - if (jvm->GetEnv((void **)&env, JNI_VERSION_1_2)) - return JNI_ERR; - - jclass cls = env->FindClass("org/inodes/gus/scummvm/ScummVM"); - if (cls == 0) - return JNI_ERR; - - if (env->RegisterNatives(cls, gMethods, ARRAYSIZE(gMethods)) < 0) - return JNI_ERR; - - FID_ScummVM_nativeScummVM = env->GetFieldID(cls, "nativeScummVM", "J"); - if (FID_ScummVM_nativeScummVM == 0) - return JNI_ERR; - - jclass event = env->FindClass("org/inodes/gus/scummvm/Event"); - if (event == 0) - return JNI_ERR; - - FID_Event_type = env->GetFieldID(event, "type", "I"); - if (FID_Event_type == 0) - return JNI_ERR; - - FID_Event_synthetic = env->GetFieldID(event, "synthetic", "Z"); - if (FID_Event_synthetic == 0) - return JNI_ERR; - - FID_Event_kbd_keycode = env->GetFieldID(event, "kbd_keycode", "I"); - if (FID_Event_kbd_keycode == 0) - return JNI_ERR; - - FID_Event_kbd_ascii = env->GetFieldID(event, "kbd_ascii", "I"); - if (FID_Event_kbd_ascii == 0) - return JNI_ERR; - - FID_Event_kbd_flags = env->GetFieldID(event, "kbd_flags", "I"); - if (FID_Event_kbd_flags == 0) - return JNI_ERR; - - FID_Event_mouse_x = env->GetFieldID(event, "mouse_x", "I"); - if (FID_Event_mouse_x == 0) - return JNI_ERR; - - FID_Event_mouse_y = env->GetFieldID(event, "mouse_y", "I"); - if (FID_Event_mouse_y == 0) - return JNI_ERR; - - FID_Event_mouse_relative = env->GetFieldID(event, "mouse_relative", "Z"); - if (FID_Event_mouse_relative == 0) - return JNI_ERR; - - cls = env->FindClass("java/lang/Object"); - if (cls == 0) - return JNI_ERR; - - MID_Object_wait = env->GetMethodID(cls, "wait", "()V"); - if (MID_Object_wait == 0) - return JNI_ERR; - - return JNI_VERSION_1_2; -} - #endif + |