aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordhewg2011-02-27 20:13:48 +0100
committerdhewg2011-03-02 23:18:34 +0100
commit2333a32697cda8f5f73861856889001839f38f25 (patch)
tree048a743c33bdcf2f508fd031556abd1079d735c6
parentb84b56b2481bac8ad92ffe6870b28b288011bf6a (diff)
downloadscummvm-rg350-2333a32697cda8f5f73861856889001839f38f25.tar.gz
scummvm-rg350-2333a32697cda8f5f73861856889001839f38f25.tar.bz2
scummvm-rg350-2333a32697cda8f5f73861856889001839f38f25.zip
ANDROID: Untangle JNI interweaving
- make the startup sequence more linear - use SurfaceHolder events - get rid of the surface lock - remove unnecessary JNI calls - make the ScummVM class implement Runnable - cleanup
-rw-r--r--backends/platform/android/android.cpp44
-rw-r--r--backends/platform/android/android.h9
-rw-r--r--backends/platform/android/gfx.cpp23
-rw-r--r--backends/platform/android/jni.cpp192
-rw-r--r--backends/platform/android/jni.h48
-rw-r--r--backends/platform/android/org/inodes/gus/scummvm/ScummVM.java478
-rw-r--r--backends/platform/android/org/inodes/gus/scummvm/ScummVMActivity.java91
7 files changed, 443 insertions, 442 deletions
diff --git a/backends/platform/android/android.cpp b/backends/platform/android/android.cpp
index f886347685..2af367e903 100644
--- a/backends/platform/android/android.cpp
+++ b/backends/platform/android/android.cpp
@@ -103,6 +103,8 @@ 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),
@@ -126,11 +128,9 @@ OSystem_Android::~OSystem_Android() {
delete _overlay_texture;
delete _mouse_texture;
- JNI::destroySurface();
-
delete _savefile;
- delete _mixer;
delete _timer;
+ delete _mixer;
delete _fsFactory;
deleteMutex(_event_queue_lock);
@@ -303,21 +303,19 @@ void OSystem_Android::initBackend() {
_mixer = new Audio::MixerImpl(this, _audio_sample_rate);
_mixer->setReady(true);
- JNI::initBackend();
-
_timer_thread_exit = false;
pthread_create(&_timer_thread, 0, timerThreadFunc, this);
_audio_thread_exit = false;
pthread_create(&_audio_thread, 0, audioThreadFunc, this);
- OSystem::initBackend();
-
setupSurface();
// renice this thread to boost the audio thread
if (setpriority(PRIO_PROCESS, 0, 19) < 0)
warning("couldn't renice the main thread");
+
+ JNI::setReadyForEvents(true);
}
void OSystem_Android::addPluginDirectories(Common::FSList &dirs) const {
@@ -385,6 +383,28 @@ 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) {
+ LOGD("initializing surface");
+
+ JNI::deinitSurface();
+ setupSurface();
+
+ event.type = Common::EVENT_SCREEN_CHANGED;
+
+ return true;
+ }
+
+ LOGD("deinitialiting surface");
+
+ _screen_changeid = JNI::surface_changeid;
+ JNI::deinitSurface();
+
+ // TODO prevent swapBuffers
+ }
+ }
+
lockMutex(_event_queue_lock);
if (_event_queue.empty()) {
@@ -433,12 +453,6 @@ bool OSystem_Android::pollEvent(Common::Event &event) {
}
break;
}
- case Common::EVENT_SCREEN_CHANGED:
- debug("EVENT_SCREEN_CHANGED");
- _screen_changeid++;
- JNI::destroySurface();
- setupSurface();
- break;
default:
break;
}
@@ -525,11 +539,15 @@ 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);
+
+ JNI::deinitSurface();
}
void OSystem_Android::setWindowCaption(const char *caption) {
diff --git a/backends/platform/android/android.h b/backends/platform/android/android.h
index f4e9d611d4..aa6016e3d2 100644
--- a/backends/platform/android/android.h
+++ b/backends/platform/android/android.h
@@ -151,10 +151,6 @@ public:
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);
@@ -166,10 +162,7 @@ public:
virtual int getGraphicsMode() const;
virtual void initSize(uint width, uint height,
const Graphics::PixelFormat *format);
-
- virtual int getScreenChangeID() const {
- return _screen_changeid;
- }
+ virtual int getScreenChangeID() const;
virtual int16 getHeight();
virtual int16 getWidth();
diff --git a/backends/platform/android/gfx.cpp b/backends/platform/android/gfx.cpp
index 8601a3bfca..49f3d40e39 100644
--- a/backends/platform/android/gfx.cpp
+++ b/backends/platform/android/gfx.cpp
@@ -65,8 +65,13 @@ int OSystem_Android::getGraphicsMode() const {
void OSystem_Android::setupSurface() {
ENTER();
- if (!JNI::setupSurface())
- return;
+ _screen_changeid = JNI::surface_changeid;
+ JNI::initSurface();
+
+ _egl_surface_width = JNI::egl_surface_width;
+ _egl_surface_height = JNI::egl_surface_height;
+
+ assert(_egl_surface_width > 0 && _egl_surface_height > 0);
// EGL set up with a new surface. Initialise OpenGLES context.
GLESTexture::initGLExtensions();
@@ -148,6 +153,10 @@ void OSystem_Android::initSize(uint width, uint height,
_mouse_texture->allocBuffer(20, 20);
}
+int OSystem_Android::getScreenChangeID() const {
+ return _screen_changeid;
+}
+
int16 OSystem_Android::getHeight() {
return _game_texture->height();
}
@@ -279,10 +288,14 @@ void OSystem_Android::updateScreen() {
GLCALL(glPopMatrix());
- if (!JNI::swapBuffers()) {
- // Context lost -> need to reinit GL
- JNI::destroySurface();
+ int res = JNI::swapBuffers();
+
+ if (res) {
+ warning("swapBuffers returned 0x%x", res);
+#if 0
+ JNI::initSurface();
setupSurface();
+#endif
}
}
diff --git a/backends/platform/android/jni.cpp b/backends/platform/android/jni.cpp
index 99934fb640..28a03d0555 100644
--- a/backends/platform/android/jni.cpp
+++ b/backends/platform/android/jni.cpp
@@ -44,6 +44,11 @@ jobject JNI::_jobj_audio_track = 0;
Common::Archive *JNI::_asset_archive = 0;
OSystem_Android *JNI::_system = 0;
+int JNI::surface_changeid = 0;
+int JNI::egl_surface_width = 0;
+int JNI::egl_surface_height = 0;
+bool JNI::_ready_for_events = 0;
+
jfieldID JNI::_FID_Event_type = 0;
jfieldID JNI::_FID_Event_synthetic = 0;
jfieldID JNI::_FID_Event_kbd_keycode = 0;
@@ -55,13 +60,12 @@ jfieldID JNI::_FID_Event_mouse_relative = 0;
jmethodID JNI::_MID_displayMessageOnOSD = 0;
jmethodID JNI::_MID_setWindowCaption = 0;
-jmethodID JNI::_MID_initBackend = 0;
jmethodID JNI::_MID_showVirtualKeyboard = 0;
jmethodID JNI::_MID_getSysArchives = 0;
jmethodID JNI::_MID_getPluginDirectories = 0;
-jmethodID JNI::_MID_setupScummVMSurface = 0;
-jmethodID JNI::_MID_destroyScummVMSurface = 0;
jmethodID JNI::_MID_swapBuffers = 0;
+jmethodID JNI::_MID_initSurface = 0;
+jmethodID JNI::_MID_deinitSurface = 0;
jmethodID JNI::_MID_AudioTrack_flush = 0;
jmethodID JNI::_MID_AudioTrack_pause = 0;
@@ -73,16 +77,16 @@ const JNINativeMethod JNI::_natives[] = {
{ "create", "(Landroid/content/res/AssetManager;"
"Landroid/media/AudioTrack;II)V",
(void *)JNI::create },
- { "nativeDestroy", "()V",
+ { "destroy", "()V",
(void *)JNI::destroy },
- { "scummVMMain", "([Ljava/lang/String;)I",
+ { "setSurface", "(II)V",
+ (void *)JNI::setSurface },
+ { "main", "([Ljava/lang/String;)I",
(void *)JNI::main },
{ "pushEvent", "(Lorg/inodes/gus/scummvm/Event;)V",
(void *)JNI::pushEvent },
{ "enableZoning", "(Z)V",
(void *)JNI::enableZoning },
- { "setSurfaceSize", "(II)V",
- (void *)JNI::setSurfaceSize },
};
JNI::JNI() {
@@ -178,6 +182,10 @@ void JNI::detachThread() {
}
}
+void JNI::setReadyForEvents(bool ready) {
+ _ready_for_events = ready;
+}
+
void JNI::throwByName(JNIEnv *env, const char *name, const char *msg) {
jclass cls = env->FindClass(name);
@@ -188,61 +196,26 @@ void JNI::throwByName(JNIEnv *env, const char *name, const char *msg) {
env->DeleteLocalRef(cls);
}
-// calls to the dark side
-
-void JNI::initBackend() {
- JNIEnv *env = JNI::getEnv();
-
- env->CallVoidMethod(_jobj, _MID_initBackend);
-
- if (env->ExceptionCheck()) {
- error("Error in Java initBackend");
-
- env->ExceptionDescribe();
- env->ExceptionClear();
-
- // TODO now what?
- }
+void JNI::throwRuntimeException(JNIEnv *env, const char *msg) {
+ throwByName(env, "java/lang/RuntimeException", msg);
}
-void JNI::getPluginDirectories(Common::FSList &dirs) {
+// calls to the dark side
+
+void JNI::displayMessageOnOSD(const char *msg) {
JNIEnv *env = JNI::getEnv();
+ jstring java_msg = env->NewStringUTF(msg);
- jobjectArray array =
- (jobjectArray)env->CallObjectMethod(_jobj, _MID_getPluginDirectories);
+ env->CallVoidMethod(_jobj, _MID_displayMessageOnOSD, java_msg);
if (env->ExceptionCheck()) {
- LOGE("Error finding plugin directories");
+ LOGE("Failed to display OSD message");
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) {
- LOGE("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);
- }
+ env->DeleteLocalRef(java_msg);
}
void JNI::setWindowCaption(const char *caption) {
@@ -261,22 +234,6 @@ void JNI::setWindowCaption(const char *caption) {
env->DeleteLocalRef(java_caption);
}
-void JNI::displayMessageOnOSD(const char *msg) {
- JNIEnv *env = JNI::getEnv();
- jstring java_msg = env->NewStringUTF(msg);
-
- env->CallVoidMethod(_jobj, _MID_displayMessageOnOSD, java_msg);
-
- if (env->ExceptionCheck()) {
- LOGE("Failed to display OSD message");
-
- env->ExceptionDescribe();
- env->ExceptionClear();
- }
-
- env->DeleteLocalRef(java_msg);
-}
-
void JNI::showVirtualKeyboard(bool enable) {
JNIEnv *env = JNI::getEnv();
@@ -321,6 +278,72 @@ void JNI::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) {
}
}
+void JNI::getPluginDirectories(Common::FSList &dirs) {
+ JNIEnv *env = JNI::getEnv();
+
+ jobjectArray array =
+ (jobjectArray)env->CallObjectMethod(_jobj, _MID_getPluginDirectories);
+
+ if (env->ExceptionCheck()) {
+ LOGE("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) {
+ LOGE("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);
+ }
+}
+
+void JNI::initSurface() {
+ JNIEnv *env = JNI::getEnv();
+
+ env->CallVoidMethod(_jobj, _MID_initSurface);
+
+ if (env->ExceptionCheck()) {
+ LOGE("initSurface failed");
+
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
+}
+
+void JNI::deinitSurface() {
+ JNIEnv *env = JNI::getEnv();
+
+ env->CallVoidMethod(_jobj, _MID_deinitSurface);
+
+ if (env->ExceptionCheck()) {
+ LOGE("deinitSurface failed");
+
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
+}
+
void JNI::setAudioPause() {
JNIEnv *env = JNI::getEnv();
@@ -396,13 +419,12 @@ void JNI::create(JNIEnv *env, jobject self, jobject am, jobject at,
FIND_METHOD(setWindowCaption, "(Ljava/lang/String;)V");
FIND_METHOD(displayMessageOnOSD, "(Ljava/lang/String;)V");
- FIND_METHOD(initBackend, "()V");
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");
+ FIND_METHOD(swapBuffers, "()I");
+ FIND_METHOD(initSurface, "()V");
+ FIND_METHOD(deinitSurface, "()V");
#undef FIND_METHOD
@@ -428,16 +450,12 @@ void JNI::create(JNIEnv *env, jobject self, jobject am, jobject at,
}
void JNI::destroy(JNIEnv *env, jobject self) {
- if (!_system)
- return;
+ delete _asset_archive;
+ _asset_archive = 0;
- OSystem_Android *tmp = _system;
+ delete _system;
g_system = 0;
_system = 0;
- delete tmp;
-
- delete _asset_archive;
- _asset_archive = 0;
// see above
//JNI::getEnv()->DeleteWeakGlobalRef(_jobj);
@@ -445,6 +463,12 @@ void JNI::destroy(JNIEnv *env, jobject self) {
JNI::getEnv()->DeleteGlobalRef(_jobj);
}
+void JNI::setSurface(JNIEnv *env, jobject self, jint width, jint height) {
+ egl_surface_width = width;
+ egl_surface_height = height;
+ surface_changeid++;
+}
+
jint JNI::main(JNIEnv *env, jobject self, jobjectArray args) {
assert(_system);
@@ -489,7 +513,7 @@ jint JNI::main(JNIEnv *env, jobject self, jobjectArray args) {
res = scummvm_main(argc, argv);
- LOGI("Exiting scummvm_main");
+ LOGI("scummvm_main exited with code %d", res);
_system->quit();
@@ -514,6 +538,10 @@ cleanup:
}
void JNI::pushEvent(JNIEnv *env, jobject self, jobject java_event) {
+ // drop events until we're ready and after we quit
+ if (!_ready_for_events)
+ return;
+
assert(_system);
Common::Event event;
@@ -565,11 +593,5 @@ void JNI::enableZoning(JNIEnv *env, jobject self, jboolean enable) {
_system->enableZoning(enable);
}
-void JNI::setSurfaceSize(JNIEnv *env, jobject self, jint width, jint height) {
- assert(_system);
-
- _system->setSurfaceSize(width, height);
-}
-
#endif
diff --git a/backends/platform/android/jni.h b/backends/platform/android/jni.h
index 0bc64980e2..0005136966 100644
--- a/backends/platform/android/jni.h
+++ b/backends/platform/android/jni.h
@@ -41,6 +41,10 @@ private:
virtual ~JNI();
public:
+ static int surface_changeid;
+ static int egl_surface_width;
+ static int egl_surface_height;
+
static jint onLoad(JavaVM *vm);
static JNIEnv *getEnv();
@@ -48,16 +52,17 @@ public:
static void attachThread();
static void detachThread();
- static void initBackend();
+ static void setReadyForEvents(bool ready);
+
static void getPluginDirectories(Common::FSList &dirs);
static void setWindowCaption(const char *caption);
static void displayMessageOnOSD(const char *msg);
static void showVirtualKeyboard(bool enable);
static void addSysArchivesToSearchSet(Common::SearchSet &s, int priority);
- static inline bool setupSurface();
- static inline void destroySurface();
- static inline bool swapBuffers();
+ static inline int swapBuffers();
+ static void initSurface();
+ static void deinitSurface();
static void setAudioPause();
static void setAudioPlay();
@@ -75,6 +80,8 @@ private:
static Common::Archive *_asset_archive;
static OSystem_Android *_system;
+ static bool _ready_for_events;
+
static jfieldID _FID_Event_type;
static jfieldID _FID_Event_synthetic;
static jfieldID _FID_Event_kbd_keycode;
@@ -87,13 +94,12 @@ private:
static jmethodID _MID_displayMessageOnOSD;
static jmethodID _MID_setWindowCaption;
- static jmethodID _MID_initBackend;
static jmethodID _MID_showVirtualKeyboard;
static jmethodID _MID_getSysArchives;
static jmethodID _MID_getPluginDirectories;
- static jmethodID _MID_setupScummVMSurface;
- static jmethodID _MID_destroyScummVMSurface;
static jmethodID _MID_swapBuffers;
+ static jmethodID _MID_initSurface;
+ static jmethodID _MID_deinitSurface;
static jmethodID _MID_AudioTrack_flush;
static jmethodID _MID_AudioTrack_pause;
@@ -104,40 +110,24 @@ private:
static const JNINativeMethod _natives[];
static void throwByName(JNIEnv *env, const char *name, const char *msg);
+ static void throwRuntimeException(JNIEnv *env, const char *msg);
// natives for the dark side
static void create(JNIEnv *env, jobject self, jobject am, jobject at,
jint sample_rate, jint buffer_size);
static void destroy(JNIEnv *env, jobject self);
+
+ static void setSurface(JNIEnv *env, jobject self, jint width, jint height);
static jint main(JNIEnv *env, jobject self, jobjectArray args);
+
static void pushEvent(JNIEnv *env, jobject self, jobject java_event);
- static void setConfManInt(JNIEnv *env, jclass cls, jstring key_obj,
- jint value);
- static void setConfManString(JNIEnv *env, jclass cls, jstring key_obj,
- jstring value_obj);
static void enableZoning(JNIEnv *env, jobject self, jboolean enable);
- static void setSurfaceSize(JNIEnv *env, jobject self, jint width,
- jint height);
};
-inline bool JNI::setupSurface() {
- JNIEnv *env = JNI::getEnv();
-
- env->CallVoidMethod(_jobj, _MID_setupScummVMSurface);
-
- return !env->ExceptionCheck();
-}
-
-inline void JNI::destroySurface() {
- JNIEnv *env = JNI::getEnv();
-
- env->CallVoidMethod(_jobj, _MID_destroyScummVMSurface);
-}
-
-inline bool JNI::swapBuffers() {
+inline int JNI::swapBuffers() {
JNIEnv *env = JNI::getEnv();
- return env->CallBooleanMethod(_jobj, _MID_swapBuffers);
+ return env->CallIntMethod(_jobj, _MID_swapBuffers);
}
inline int JNI::writeAudio(JNIEnv *env, jbyteArray &data, int offset, int size) {
diff --git a/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java b/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java
index ae7204f752..db83303c7d 100644
--- a/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java
+++ b/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java
@@ -1,18 +1,12 @@
package org.inodes.gus.scummvm;
-import android.content.Context;
+import android.util.Log;
import android.content.res.AssetManager;
+import android.view.SurfaceHolder;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Process;
-import android.util.Log;
-import android.view.Surface;
-import android.view.SurfaceHolder;
-import javax.microedition.khronos.opengles.GL;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGL11;
@@ -22,28 +16,242 @@ import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
import java.io.File;
-import java.util.concurrent.Semaphore;
import java.util.Map;
import java.util.LinkedHashMap;
+public abstract class ScummVM implements SurfaceHolder.Callback, Runnable {
+ final protected static String LOG_TAG = "ScummVM";
+ final private AssetManager asset_manager;
+ final private Object sem_surface;
+
+ private EGL10 egl;
+ private EGLDisplay eglDisplay = EGL10.EGL_NO_DISPLAY;
+ private EGLConfig eglConfig;
+ private EGLContext eglContext = EGL10.EGL_NO_CONTEXT;
+ private EGLSurface eglSurface = EGL10.EGL_NO_SURFACE;
+
+ private SurfaceHolder surface_holder;
+ private AudioTrack audio_track;
+ private int sample_rate = 0;
+ private int buffer_size = 0;
+
+ private String[] args;
+
+ final private native void create(AssetManager asset_manager,
+ AudioTrack audio_track,
+ int sample_rate, int buffer_size);
+ final private native void destroy();
+ final private native void setSurface(int width, int height);
+ final private native int main(String[] args);
+
+ // Set scummvm config options
+ final public native void enableZoning(boolean enable);
+ // Feed an event to ScummVM. Safe to call from other threads.
+ final public native void pushEvent(Event e);
+
+ // Callbacks from C++ peer instance
+ abstract protected void displayMessageOnOSD(String msg);
+ abstract protected void setWindowCaption(String caption);
+ abstract protected String[] getPluginDirectories();
+ abstract protected void showVirtualKeyboard(boolean enable);
+ abstract protected String[] getSysArchives();
+
+ final protected int swapBuffers() {
+ if (!egl.eglSwapBuffers(eglDisplay, eglSurface))
+ return egl.eglGetError();
+
+ return 0;
+ }
+
+ public ScummVM(AssetManager asset_manager, SurfaceHolder holder) {
+ this.asset_manager = asset_manager;
+ sem_surface = new Object();
+
+ holder.addCallback(this);
+ }
+
+ // SurfaceHolder callback
+ final public void surfaceCreated(SurfaceHolder holder) {
+ Log.d(LOG_TAG, "surfaceCreated");
+
+ // no need to do anything, surfaceChanged() will be called in any case
+ }
+
+ // SurfaceHolder callback
+ final public void surfaceChanged(SurfaceHolder holder, int format,
+ int width, int height) {
+ Log.d(LOG_TAG, String.format("surfaceChanged: %dx%d (%d)",
+ width, height, format));
+
+ synchronized(sem_surface) {
+ surface_holder = holder;
+ sem_surface.notifyAll();
+ }
+
+ // store values for the native code
+ setSurface(width, height);
+ }
+
+ // SurfaceHolder callback
+ final public void surfaceDestroyed(SurfaceHolder holder) {
+ Log.d(LOG_TAG, "surfaceDestroyed");
+
+ synchronized(sem_surface) {
+ surface_holder = null;
+ sem_surface.notifyAll();
+ }
+
+ // clear values for the native code
+ setSurface(0, 0);
+ }
+
+ final public void setArgs(String[] args) {
+ this.args = args;
+ }
+
+ final public void run() {
+ try {
+ initAudio();
+ initEGL();
+
+ // wait for the surfaceChanged callback
+ synchronized(sem_surface) {
+ while (surface_holder == null)
+ sem_surface.wait();
+ }
+ } catch (Exception e) {
+ deinitEGL();
+ deinitAudio();
+
+ throw new RuntimeException("Error preparing the ScummVM thread", e);
+ }
+
+ create(asset_manager, audio_track, sample_rate, buffer_size);
+
+ int res = main(args);
+
+ destroy();
+
+ deinitEGL();
+ deinitAudio();
+
+ // On exit, tear everything down for a fresh restart next time.
+ System.exit(res);
+ }
+
+ public void pause() {
+ // TODO
+ }
+
+ public void resume() {
+ // TODO
+ }
+
+ final private void initEGL() throws Exception {
+ egl = (EGL10)EGLContext.getEGL();
+ eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+
+ int[] version = new int[2];
+ egl.eglInitialize(eglDisplay, version);
+
+ int[] num_config = new int[1];
+ egl.eglChooseConfig(eglDisplay, configSpec, null, 0, num_config);
+
+ final int numConfigs = num_config[0];
+
+ if (numConfigs <= 0)
+ throw new IllegalArgumentException("No configs match configSpec");
+
+ EGLConfig[] configs = new EGLConfig[numConfigs];
+ egl.eglChooseConfig(eglDisplay, configSpec, configs, numConfigs,
+ num_config);
+
+ if (false) {
+ Log.d(LOG_TAG, String.format("Found %d EGL configurations.",
+ numConfigs));
+ for (EGLConfig config : configs)
+ dumpEglConfig(config);
+ }
+
+ // Android's eglChooseConfig is busted in several versions and
+ // devices so we have to filter/rank the configs again ourselves.
+ eglConfig = chooseEglConfig(configs);
+
+ if (false) {
+ Log.d(LOG_TAG, String.format("Chose from %d EGL configs",
+ numConfigs));
+ dumpEglConfig(eglConfig);
+ }
+
+ eglContext = egl.eglCreateContext(eglDisplay, eglConfig,
+ EGL10.EGL_NO_CONTEXT, null);
+
+ if (eglContext == EGL10.EGL_NO_CONTEXT)
+ throw new Exception(String.format("Failed to create context: 0x%x",
+ egl.eglGetError()));
+ }
+
+ // Callback from C++ peer instance
+ final protected void initSurface() throws Exception {
+ eglSurface = egl.eglCreateWindowSurface(eglDisplay, eglConfig,
+ surface_holder, null);
+
+ if (eglSurface == EGL10.EGL_NO_SURFACE)
+ throw new Exception(String.format(
+ "eglCreateWindowSurface failed: 0x%x", egl.eglGetError()));
+
+ egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
+
+ GL10 gl = (GL10)eglContext.getGL();
+
+ Log.i(LOG_TAG, String.format("Using EGL %s (%s); GL %s/%s (%s)",
+ egl.eglQueryString(eglDisplay, EGL10.EGL_VERSION),
+ egl.eglQueryString(eglDisplay, EGL10.EGL_VENDOR),
+ gl.glGetString(GL10.GL_VERSION),
+ gl.glGetString(GL10.GL_RENDERER),
+ gl.glGetString(GL10.GL_VENDOR)));
+ }
+
+ // Callback from C++ peer instance
+ final protected void deinitSurface() {
+ if (eglDisplay != EGL10.EGL_NO_DISPLAY) {
+ egl.eglMakeCurrent(eglDisplay, EGL10.EGL_NO_SURFACE,
+ EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
+
+ if (eglSurface != EGL10.EGL_NO_SURFACE)
+ egl.eglDestroySurface(eglDisplay, eglSurface);
+ }
+
+ eglSurface = EGL10.EGL_NO_SURFACE;
+ }
-// At least in Android 2.1, eglCreateWindowSurface() requires an
-// EGLNativeWindowSurface object, which is hidden deep in the bowels
-// of libui. Until EGL is properly exposed, it's probably safer to
-// use the Java versions of most EGL functions :(
+ final private void deinitEGL() {
+ if (eglDisplay != EGL10.EGL_NO_DISPLAY) {
+ egl.eglMakeCurrent(eglDisplay, EGL10.EGL_NO_SURFACE,
+ EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
-public class ScummVM implements SurfaceHolder.Callback {
- protected final static String LOG_TAG = "ScummVM";
+ if (eglSurface != EGL10.EGL_NO_SURFACE)
+ egl.eglDestroySurface(eglDisplay, eglSurface);
+
+ if (eglContext != EGL10.EGL_NO_CONTEXT)
+ egl.eglDestroyContext(eglDisplay, eglContext);
+
+ egl.eglTerminate(eglDisplay);
+ }
- private native void create(AssetManager am, AudioTrack audio_track,
- int sample_rate, int buffer_size);
+ eglSurface = EGL10.EGL_NO_SURFACE;
+ eglContext = EGL10.EGL_NO_CONTEXT;
+ eglConfig = null;
+ eglDisplay = EGL10.EGL_NO_DISPLAY;
+ egl = null;
+ }
- public ScummVM(Context context) throws Exception {
- int sample_rate = AudioTrack.getNativeOutputSampleRate(
- AudioManager.STREAM_MUSIC);
- int buffer_size = AudioTrack.getMinBufferSize(sample_rate,
- AudioFormat.CHANNEL_CONFIGURATION_STEREO,
- AudioFormat.ENCODING_PCM_16BIT);
+ final private void initAudio() throws Exception {
+ sample_rate = AudioTrack.getNativeOutputSampleRate(
+ AudioManager.STREAM_MUSIC);
+ buffer_size = AudioTrack.getMinBufferSize(sample_rate,
+ AudioFormat.CHANNEL_CONFIGURATION_STEREO,
+ AudioFormat.ENCODING_PCM_16BIT);
// ~100ms
int buffer_size_want = (sample_rate * 2 * 2 / 10) & ~1023;
@@ -58,48 +266,28 @@ public class ScummVM implements SurfaceHolder.Callback {
Log.i(LOG_TAG, String.format("Using %d bytes buffer for %dHz audio",
buffer_size, sample_rate));
- AudioTrack audio_track =
- new AudioTrack(AudioManager.STREAM_MUSIC,
- sample_rate,
- AudioFormat.CHANNEL_CONFIGURATION_STEREO,
- AudioFormat.ENCODING_PCM_16BIT,
- buffer_size,
- AudioTrack.MODE_STREAM);
+ audio_track = new AudioTrack(AudioManager.STREAM_MUSIC,
+ sample_rate,
+ AudioFormat.CHANNEL_CONFIGURATION_STEREO,
+ AudioFormat.ENCODING_PCM_16BIT,
+ buffer_size,
+ AudioTrack.MODE_STREAM);
if (audio_track.getState() != AudioTrack.STATE_INITIALIZED)
throw new Exception(
String.format("Error initialising AudioTrack: %d",
audio_track.getState()));
-
- // Init C++ code
- create(context.getAssets(), audio_track, sample_rate, buffer_size);
}
- private native void nativeDestroy();
+ final private void deinitAudio() {
+ if (audio_track != null)
+ audio_track.stop();
- public synchronized void destroy() {
- nativeDestroy();
+ audio_track = null;
+ buffer_size = 0;
+ sample_rate = 0;
}
- protected void finalize() {
- destroy();
- }
-
- // Surface creation:
- // GUI thread: create surface, release lock
- // ScummVM thread: acquire lock (block), read surface
-
- // Surface deletion:
- // GUI thread: post event, acquire lock (block), return
- // ScummVM thread: read event, free surface, release lock
-
- // In other words, ScummVM thread does this:
- // acquire lock
- // setup surface
- // when SCREEN_CHANGED arrives:
- // destroy surface
- // release lock
- // back to acquire lock
static final int configSpec[] = {
EGL10.EGL_RED_SIZE, 5,
EGL10.EGL_GREEN_SIZE, 5,
@@ -109,33 +297,6 @@ public class ScummVM implements SurfaceHolder.Callback {
EGL10.EGL_NONE,
};
- EGL10 egl;
- EGLDisplay eglDisplay = EGL10.EGL_NO_DISPLAY;
- EGLConfig eglConfig;
- EGLContext eglContext = EGL10.EGL_NO_CONTEXT;
- EGLSurface eglSurface = EGL10.EGL_NO_SURFACE;
- Semaphore surfaceLock = new Semaphore(0, true);
- SurfaceHolder nativeSurface;
-
- public void surfaceCreated(SurfaceHolder holder) {
- nativeSurface = holder;
- surfaceLock.release();
- }
-
- public void surfaceChanged(SurfaceHolder holder, int format,
- int width, int height) {
- // Disabled while I debug GL problems
- pushEvent(new Event(Event.EVENT_SCREEN_CHANGED));
- }
-
- public void surfaceDestroyed(SurfaceHolder holder) {
- try {
- surfaceLock.acquire();
- } catch (InterruptedException e) {
- Log.e(LOG_TAG, "Interrupted while waiting for surface lock", e);
- }
- }
-
// For debugging
private static final Map<String, Integer> attribs;
@@ -170,7 +331,7 @@ public class ScummVM implements SurfaceHolder.Callback {
attribs.put("TRANSPARENT_BLUE_VALUE", EGL10.EGL_TRANSPARENT_BLUE_VALUE);
}
- private void dumpEglConfig(EGLConfig config) {
+ final private void dumpEglConfig(EGLConfig config) {
int[] value = new int[1];
for (Map.Entry<String, Integer> entry : attribs.entrySet()) {
@@ -184,49 +345,7 @@ public class ScummVM implements SurfaceHolder.Callback {
}
}
- // Called by ScummVM thread (from initBackend)
- private void createScummVMGLContext() {
- egl = (EGL10)EGLContext.getEGL();
- eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
-
- int[] version = new int[2];
- egl.eglInitialize(eglDisplay, version);
-
- int[] num_config = new int[1];
- egl.eglChooseConfig(eglDisplay, configSpec, null, 0, num_config);
-
- final int numConfigs = num_config[0];
-
- if (numConfigs <= 0)
- throw new IllegalArgumentException("No configs match configSpec");
-
- EGLConfig[] configs = new EGLConfig[numConfigs];
- egl.eglChooseConfig(eglDisplay, configSpec, configs, numConfigs,
- num_config);
-
- if (false) {
- Log.d(LOG_TAG, String.format("Found %d EGL configurations.", numConfigs));
- for (EGLConfig config : configs)
- dumpEglConfig(config);
- }
-
- // Android's eglChooseConfig is busted in several versions and
- // devices so we have to filter/rank the configs again ourselves.
- eglConfig = chooseEglConfig(configs);
-
- if (false) {
- Log.d(LOG_TAG, String.format("Chose EGL config from %d possibilities.", numConfigs));
- dumpEglConfig(eglConfig);
- }
-
- eglContext = egl.eglCreateContext(eglDisplay, eglConfig,
- EGL10.EGL_NO_CONTEXT, null);
-
- if (eglContext == EGL10.EGL_NO_CONTEXT)
- throw new RuntimeException("Failed to create context");
- }
-
- private EGLConfig chooseEglConfig(EGLConfig[] configs) {
+ final private EGLConfig chooseEglConfig(EGLConfig[] configs) {
int best = 0;
int bestScore = -1;
int[] value = new int[1];
@@ -286,111 +405,6 @@ public class ScummVM implements SurfaceHolder.Callback {
return configs[best];
}
- // Called by ScummVM thread
- static private boolean _log_version = true;
-
- protected void setupScummVMSurface() {
- try {
- surfaceLock.acquire();
- } catch (InterruptedException e) {
- Log.e(LOG_TAG, "Interrupted while waiting for surface lock", e);
- return;
- }
-
- eglSurface = egl.eglCreateWindowSurface(eglDisplay, eglConfig,
- nativeSurface, null);
-
- if (eglSurface == EGL10.EGL_NO_SURFACE)
- Log.e(LOG_TAG, "CreateWindowSurface failed!");
-
- egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
-
- GL10 gl = (GL10)eglContext.getGL();
-
- if (_log_version) {
- Log.i(LOG_TAG, String.format("Using EGL %s (%s); GL %s/%s (%s)",
- egl.eglQueryString(eglDisplay, EGL10.EGL_VERSION),
- egl.eglQueryString(eglDisplay, EGL10.EGL_VENDOR),
- gl.glGetString(GL10.GL_VERSION),
- gl.glGetString(GL10.GL_RENDERER),
- gl.glGetString(GL10.GL_VENDOR)));
-
- // only log this once
- _log_version = false;
- }
-
- int[] value = new int[1];
- egl.eglQuerySurface(eglDisplay, eglSurface, EGL10.EGL_WIDTH, value);
-
- int width = value[0];
- egl.eglQuerySurface(eglDisplay, eglSurface, EGL10.EGL_HEIGHT, value);
-
- int height = value[0];
- Log.i(LOG_TAG, String.format("New surface is %dx%d", width, height));
- setSurfaceSize(width, height);
- }
-
- // Called by ScummVM thread
- protected void destroyScummVMSurface() {
- if (eglSurface != null) {
- egl.eglMakeCurrent(eglDisplay, EGL10.EGL_NO_SURFACE,
- EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
-
- egl.eglDestroySurface(eglDisplay, eglSurface);
- eglSurface = EGL10.EGL_NO_SURFACE;
- }
-
- surfaceLock.release();
- }
-
- public void setSurface(SurfaceHolder holder) {
- holder.addCallback(this);
- }
-
- final public boolean swapBuffers() {
- if (!egl.eglSwapBuffers(eglDisplay, eglSurface)) {
- int error = egl.eglGetError();
- Log.w(LOG_TAG, String.format("eglSwapBuffers exited with error 0x%x", error));
- if (error == EGL11.EGL_CONTEXT_LOST)
- return false;
- }
-
- return true;
- }
-
- // Set scummvm config options
- final public native void enableZoning(boolean enable);
- final public native void setSurfaceSize(int width, int height);
-
- // Feed an event to ScummVM. Safe to call from other threads.
- final public native void pushEvent(Event e);
-
- // Runs the actual ScummVM program and returns when it does.
- // This should not be called from multiple threads simultaneously...
- final public native int scummVMMain(String[] argv);
-
- // Callbacks from C++ peer instance
- //protected GraphicsMode[] getSupportedGraphicsModes() {}
- protected void displayMessageOnOSD(String msg) {}
- protected void setWindowCaption(String caption) {}
- protected void showVirtualKeyboard(boolean enable) {}
- protected String[] getSysArchives() { return new String[0]; }
- protected String[] getPluginDirectories() { return new String[0]; }
-
- protected void initBackend() {
- createScummVMGLContext();
- }
-
- public void pause() {
- // TODO: need to pause audio
- // TODO: need to pause engine
- }
-
- public void resume() {
- // TODO: need to resume audio
- // TODO: need to resume engine
- }
-
static {
// For grabbing with gdb...
final boolean sleep_for_debugger = false;
@@ -401,10 +415,10 @@ public class ScummVM implements SurfaceHolder.Callback {
}
}
- //System.loadLibrary("scummvm");
File cache_dir = ScummVMApplication.getLastCacheDir();
String libname = System.mapLibraryName("scummvm");
File libpath = new File(cache_dir, libname);
+
System.load(libpath.getPath());
}
}
diff --git a/backends/platform/android/org/inodes/gus/scummvm/ScummVMActivity.java b/backends/platform/android/org/inodes/gus/scummvm/ScummVMActivity.java
index fb6020cf1c..92247dea96 100644
--- a/backends/platform/android/org/inodes/gus/scummvm/ScummVMActivity.java
+++ b/backends/platform/android/org/inodes/gus/scummvm/ScummVMActivity.java
@@ -3,6 +3,7 @@ package org.inodes.gus.scummvm;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
+import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.media.AudioManager;
import android.os.Bundle;
@@ -14,6 +15,7 @@ import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceView;
+import android.view.SurfaceHolder;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.inputmethod.InputMethodManager;
@@ -30,8 +32,6 @@ public class ScummVMActivity extends Activity {
private final static int TRACKBALL_SCALE = 2;
private class MyScummVM extends ScummVM {
- private boolean scummvmRunning = false;
-
private boolean usingSmallScreen() {
// Multiple screen sizes came in with Android 1.6. Have
// to use reflection in order to continue supporting 1.5
@@ -49,8 +49,8 @@ public class ScummVMActivity extends Activity {
}
}
- public MyScummVM() throws Exception {
- super(ScummVMActivity.this);
+ public MyScummVM(SurfaceHolder holder) {
+ super(ScummVMActivity.this.getAssets(), holder);
// Enable ScummVM zoning on 'small' screens.
// FIXME make this optional for the user
@@ -59,23 +59,6 @@ public class ScummVMActivity extends Activity {
}
@Override
- protected void initBackend() {
- synchronized (this) {
- scummvmRunning = true;
- notifyAll();
- }
-
- super.initBackend();
- }
-
- public void waitUntilRunning() throws InterruptedException {
- synchronized (this) {
- while (!scummvmRunning)
- wait();
- }
- }
-
- @Override
protected void displayMessageOnOSD(String msg) {
Log.i(LOG_TAG, "OSD: " + msg);
Toast.makeText(ScummVMActivity.this, msg, Toast.LENGTH_LONG).show();
@@ -105,6 +88,12 @@ public class ScummVMActivity extends Activity {
}
});
}
+
+ @Override
+ protected String[] getSysArchives() {
+ return new String[0];
+ }
+
}
private MyScummVM scummvm;
@@ -140,74 +129,36 @@ public class ScummVMActivity extends Activity {
}
SurfaceView main_surface = (SurfaceView)findViewById(R.id.main_surface);
+
main_surface.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
return onTouchEvent(event);
}
});
+
main_surface.setOnKeyListener(new View.OnKeyListener() {
public boolean onKey(View v, int code, KeyEvent ev) {
return onKeyDown(code, ev);
}
});
- main_surface.requestFocus();
- // Start ScummVM
- try {
- scummvm = new MyScummVM();
- } catch (Exception e) {
- Log.e(ScummVM.LOG_TAG, "Fatal error", e);
- finish();
- return;
- }
-
- scummvm_thread = new Thread(new Runnable() {
- public void run() {
- try {
- runScummVM();
- } catch (Exception e) {
- Log.e(ScummVM.LOG_TAG, "Fatal error in ScummVM thread", e);
- new AlertDialog.Builder(ScummVMActivity.this)
- .setTitle("Error")
- .setMessage(e.toString())
- .setIcon(android.R.drawable.ic_dialog_alert)
- .show();
- finish();
- }
- }
- }, "ScummVM");
-
- scummvm_thread.start();
+ main_surface.requestFocus();
- // Block UI thread until ScummVM has started. In particular,
- // this means that surface and event callbacks should be safe
- // after this point.
- try {
- scummvm.waitUntilRunning();
- } catch (InterruptedException e) {
- Log.e(ScummVM.LOG_TAG, "Interrupted while waiting for ScummVM.initBackend", e);
- finish();
- }
+ getFilesDir().mkdirs();
- scummvm.setSurface(main_surface.getHolder());
- }
+ // Start ScummVM
+ scummvm = new MyScummVM(main_surface.getHolder());
- // Runs in another thread
- private void runScummVM() throws IOException {
- getFilesDir().mkdirs();
- String[] args = {
- "ScummVM-lib",
+ scummvm.setArgs(new String[] {
+ "ScummVM",
"--config=" + getFileStreamPath("scummvmrc").getPath(),
"--path=" + Environment.getExternalStorageDirectory().getPath(),
"--gui-theme=scummmodern",
"--savepath=" + getDir("saves", 0).getPath()
- };
+ });
- int ret = scummvm.scummVMMain(args);
-
- // On exit, tear everything down for a fresh
- // restart next time.
- System.exit(ret);
+ scummvm_thread = new Thread(scummvm, "ScummVM");
+ scummvm_thread.start();
}
private boolean was_paused = false;