From 257913676d5a21c6192a6146e662c21f299c9b09 Mon Sep 17 00:00:00 2001 From: dhewg Date: Sun, 20 Feb 2011 17:01:46 +0100 Subject: [PATCH] ANDROID: Split code into multiple files And get rid of unnecessary JNI calls to get a pointer to g_system --- backends/platform/android/android.cpp | 970 +----------------- backends/platform/android/android.h | 200 +++- backends/platform/android/gfx.cpp | 459 +++++++++ backends/platform/android/jni.cpp | 387 +++++++ backends/platform/android/module.mk | 6 +- .../android/{video.cpp => texture.cpp} | 2 +- .../platform/android/{video.h => texture.h} | 5 + 7 files changed, 1062 insertions(+), 967 deletions(-) create mode 100644 backends/platform/android/gfx.cpp create mode 100644 backends/platform/android/jni.cpp rename backends/platform/android/{video.cpp => texture.cpp} (99%) rename backends/platform/android/{video.h => texture.h} (98%) diff --git a/backends/platform/android/android.cpp b/backends/platform/android/android.cpp index c49745f8bda3..a0a53474e871 100644 --- a/backends/platform/android/android.cpp +++ b/backends/platform/android/android.cpp @@ -25,22 +25,10 @@ #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 - -#include -#include -#include #include #include +#include -#include "common/archive.h" #include "common/util.h" #include "common/rect.h" #include "common/queue.h" @@ -48,13 +36,11 @@ #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/android.h" #include "backends/platform/android/asset-archive.h" const char *android_log_tag = "ScummVM"; @@ -106,226 +92,13 @@ 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 static inline T scalef(T in, float numerator, float denominator) { return static_cast(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 _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 *g_sys = 0; OSystem_Android::OSystem_Android(jobject am) : _back_ptr(0), @@ -357,6 +130,7 @@ OSystem_Android::~OSystem_Android() { destroyScummVMSurface(); JNIEnv *env = JNU_GetEnv(); + // see below //env->DeleteWeakGlobalRef(_back_ptr); env->DeleteGlobalRef(_back_ptr); @@ -369,14 +143,9 @@ OSystem_Android::~OSystem_Android() { 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; -} - bool OSystem_Android::initJavaHooks(JNIEnv *env, jobject self) { // weak global ref to allow class to be unloaded - // ... except dalvik doesn't implement NewWeakGlobalRef (yet) + // ... except dalvik implements NewWeakGlobalRef only on froyo //_back_ptr = env->NewWeakGlobalRef(self); _back_ptr = env->NewGlobalRef(self); @@ -404,91 +173,11 @@ bool OSystem_Android::initJavaHooks(JNIEnv *env, jobject self) { return true; } -static void ScummVM_create(JNIEnv *env, jobject self, jobject am) { - OSystem_Android *cpp_obj = new OSystem_Android(am); - - // Exception already thrown by initJavaHooks? - if (!cpp_obj->initJavaHooks(env, self)) - return; - - env->SetLongField(self, FID_ScummVM_nativeScummVM, (jlong)cpp_obj); - -#ifdef DYNAMIC_MODULES - PluginManager::instance().addPluginProvider(new AndroidPluginProvider()); -#endif -} - -static void ScummVM_nativeDestroy(JNIEnv *env, jobject self) { - OSystem_Android *cpp_obj = OSystem_Android::fromJavaObject(env, self); - delete cpp_obj; -} - -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); - - if (buf == 0) { - warning("Unable to get Java audio byte array. Skipping"); - return; - } - - Audio::MixerImpl *mixer = - static_cast(cpp_obj->getMixer()); - assert(mixer); - mixer->mixCallback(reinterpret_cast(buf), len); - - env->ReleaseByteArrayElements(jbuf, buf, 0); -} - -static void ScummVM_setConfManInt(JNIEnv *env, jclass cls, - jstring key_obj, jint value) { - ENTER("%p, %d", key_obj, (int)value); - - const char *key = env->GetStringUTFChars(key_obj, 0); - - if (key == 0) - return; - - ConfMan.setInt(key, value); - - env->ReleaseStringUTFChars(key_obj, key); -} - -static void ScummVM_setConfManString(JNIEnv *env, jclass cls, jstring key_obj, - jstring value_obj) { - ENTER("%p, %p", key_obj, value_obj); - - const char *key = env->GetStringUTFChars(key_obj, 0); - - if (key == 0) - return; - - const char *value = env->GetStringUTFChars(value_obj, 0); - - if (value == 0) { - env->ReleaseStringUTFChars(key_obj, key); - return; - } - - ConfMan.set(key, value); - - env->ReleaseStringUTFChars(value_obj, value); - env->ReleaseStringUTFChars(key_obj, key); -} - void *OSystem_Android::timerThreadFunc(void *arg) { OSystem_Android *system = (OSystem_Android *)arg; DefaultTimerManager *timer = (DefaultTimerManager *)(system->_timer); - JNIEnv *env = 0; - jint res = cached_jvm->AttachCurrentThread(&env, 0); - - if (res != JNI_OK) { - LOGE("AttachCurrentThread() failed: %d", res); - abort(); - } + JNU_AttachThread(); struct timespec tv; tv.tv_sec = 0; @@ -499,12 +188,7 @@ void *OSystem_Android::timerThreadFunc(void *arg) { nanosleep(&tv, 0); } - res = cached_jvm->DetachCurrentThread(); - - if (res != JNI_OK) { - LOGE("DetachCurrentThread() failed: %d", res); - abort(); - } + JNU_DetachThread(); return 0; } @@ -627,430 +311,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; @@ -1168,52 +428,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(env->GetIntField( - java_event, FID_Event_kbd_ascii)); - event.kbd.flags = static_cast(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; @@ -1401,177 +615,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(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); + g_sys->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 + diff --git a/backends/platform/android/android.h b/backends/platform/android/android.h index 855fb04b5d0c..b31c0fd38584 100644 --- a/backends/platform/android/android.h +++ b/backends/platform/android/android.h @@ -23,8 +23,24 @@ * */ +#ifndef _ANDROID_H_ +#define _ANDROID_H_ + #if defined(__ANDROID__) +#include "common/fs.h" +#include "common/archive.h" +#include "audio/mixer_intern.h" +#include "graphics/surface.h" +#include "backends/base-backend.h" +#include "backends/plugins/posix/posix-provider.h" +#include "backends/fs/posix/posix-fs-factory.h" + +#include "backends/platform/android/texture.h" + +#include + +#include #include #include @@ -62,9 +78,187 @@ extern void checkGlError(const char *expr, const char *file, int line); #define GLCALL(x) do { (x); } while (false) #endif -// Fix JNIEXPORT declaration to actually do something useful -#undef JNIEXPORT -#define JNIEXPORT __attribute__ ((visibility("default"))) +extern JNIEnv *JNU_GetEnv(); +extern void JNU_AttachThread(); +extern void JNU_DetachThread(); + +class OSystem_Android; +extern OSystem_Android *g_sys; + +#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 _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); + 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); +}; + +#endif #endif diff --git a/backends/platform/android/gfx.cpp b/backends/platform/android/gfx.cpp new file mode 100644 index 000000000000..eb5a66c6d96a --- /dev/null +++ b/backends/platform/android/gfx.cpp @@ -0,0 +1,459 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#if defined(__ANDROID__) + +#include "backends/platform/android/android.h" + +static inline GLfixed xdiv(int numerator, int denominator) { + assert(numerator < (1 << 16)); + return (numerator << 16) / denominator; +} + +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; +} + +#endif + diff --git a/backends/platform/android/jni.cpp b/backends/platform/android/jni.cpp new file mode 100644 index 000000000000..0f1366148f4a --- /dev/null +++ b/backends/platform/android/jni.cpp @@ -0,0 +1,387 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#if defined(__ANDROID__) + +#include "base/main.h" +#include "common/config-manager.h" + +#include "backends/platform/android/android.h" + +// Fix JNIEXPORT declaration to actually do something useful +#undef JNIEXPORT +#define JNIEXPORT __attribute__ ((visibility("default"))) + +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; +} + +void JNU_AttachThread() { + JNIEnv *env = 0; + jint res = cached_jvm->AttachCurrentThread(&env, 0); + + if (res != JNI_OK) { + LOGE("AttachCurrentThread() failed: %d", res); + abort(); + } +} + +void JNU_DetachThread() { + jint res = cached_jvm->DetachCurrentThread(); + + if (res != JNI_OK) { + LOGE("DetachCurrentThread() failed: %d", res); + abort(); + } +} + +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); +} + +static void ScummVM_create(JNIEnv *env, jobject self, jobject am) { + assert(!g_system); + + g_sys = new OSystem_Android(am); + assert(g_sys); + + // Exception already thrown by initJavaHooks? + if (!g_sys->initJavaHooks(env, self)) + return; + + env->SetLongField(self, FID_ScummVM_nativeScummVM, (jlong)g_sys); + + g_system = g_sys; +} + +static void ScummVM_nativeDestroy(JNIEnv *env, jobject self) { + assert(g_sys); + + OSystem_Android *tmp = g_sys; + g_system = 0; + g_sys = 0; + delete tmp; +} + +static jint ScummVM_scummVMMain(JNIEnv *env, jobject self, jobjectArray args) { + assert(g_sys); + + 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(cstr); + + // exception already thrown? + if (cstr == 0) + goto cleanup; + } + + env->DeleteLocalRef(arg); + } + +#ifdef DYNAMIC_MODULES + PluginManager::instance().addPluginProvider(new AndroidPluginProvider()); +#endif + + LOGI("Entering scummvm_main with %d args", argc); + + res = scummvm_main(argc, argv); + + LOGI("Exiting scummvm_main"); + + g_sys->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; +} + +static void ScummVM_pushEvent(JNIEnv *env, jobject self, jobject java_event) { + assert(g_sys); + + 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(env->GetIntField( + java_event, FID_Event_kbd_ascii)); + event.kbd.flags = static_cast(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; + } + + g_sys->pushEvent(event); +} + +static void ScummVM_audioMixCallback(JNIEnv *env, jobject self, + jbyteArray jbuf) { + assert(g_sys); + + jsize len = env->GetArrayLength(jbuf); + jbyte *buf = env->GetByteArrayElements(jbuf, 0); + + if (buf == 0) { + warning("Unable to get Java audio byte array. Skipping"); + return; + } + + Audio::MixerImpl *mixer = + static_cast(g_sys->getMixer()); + assert(mixer); + mixer->mixCallback(reinterpret_cast(buf), len); + + env->ReleaseByteArrayElements(jbuf, buf, 0); +} + +static void ScummVM_setConfManInt(JNIEnv *env, jclass cls, + jstring key_obj, jint value) { + ENTER("%p, %d", key_obj, (int)value); + + const char *key = env->GetStringUTFChars(key_obj, 0); + + if (key == 0) + return; + + ConfMan.setInt(key, value); + + env->ReleaseStringUTFChars(key_obj, key); +} + +static void ScummVM_setConfManString(JNIEnv *env, jclass cls, jstring key_obj, + jstring value_obj) { + ENTER("%p, %p", key_obj, value_obj); + + const char *key = env->GetStringUTFChars(key_obj, 0); + + if (key == 0) + return; + + const char *value = env->GetStringUTFChars(value_obj, 0); + + if (value == 0) { + env->ReleaseStringUTFChars(key_obj, key); + return; + } + + ConfMan.set(key, value); + + env->ReleaseStringUTFChars(value_obj, value); + env->ReleaseStringUTFChars(key_obj, key); +} + +static void ScummVM_enableZoning(JNIEnv *env, jobject self, jboolean enable) { + assert(g_sys); + + g_sys->enableZoning(enable); +} + +static void ScummVM_setSurfaceSize(JNIEnv *env, jobject self, + jint width, jint height) { + assert(g_sys); + + g_sys->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 + diff --git a/backends/platform/android/module.mk b/backends/platform/android/module.mk index 8b120b21ff8a..3bcfa7ad8711 100644 --- a/backends/platform/android/module.mk +++ b/backends/platform/android/module.mk @@ -1,9 +1,11 @@ MODULE := backends/platform/android MODULE_OBJS := \ - android.o \ + jni.o \ + texture.o \ asset-archive.o \ - video.o + android.o \ + gfx.o # We don't use rules.mk but rather manually update OBJS and MODULE_DIRS. MODULE_OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS)) diff --git a/backends/platform/android/video.cpp b/backends/platform/android/texture.cpp similarity index 99% rename from backends/platform/android/video.cpp rename to backends/platform/android/texture.cpp index f8427c2ac85c..b638976dc40e 100644 --- a/backends/platform/android/video.cpp +++ b/backends/platform/android/texture.cpp @@ -33,8 +33,8 @@ #include "common/util.h" #include "common/tokenizer.h" +#include "backends/platform/android/texture.h" #include "backends/platform/android/android.h" -#include "backends/platform/android/video.h" // Unfortunately, Android devices are too varied to make broad assumptions :/ #define TEXSUBIMAGE_IS_EXPENSIVE 0 diff --git a/backends/platform/android/video.h b/backends/platform/android/texture.h similarity index 98% rename from backends/platform/android/video.h rename to backends/platform/android/texture.h index da42ea876da8..050eafa0737e 100644 --- a/backends/platform/android/video.h +++ b/backends/platform/android/texture.h @@ -23,6 +23,9 @@ * */ +#ifndef _ANDROID_TEXTURE_H_ +#define _ANDROID_TEXTURE_H_ + #if defined(__ANDROID__) #include @@ -207,3 +210,5 @@ class GLESPaletteATexture : public GLESPaletteTexture { }; #endif +#endif +