diff options
Diffstat (limited to 'backends')
| -rw-r--r-- | backends/platform/android/android.cpp | 44 | ||||
| -rw-r--r-- | backends/platform/android/android.h | 9 | ||||
| -rw-r--r-- | backends/platform/android/gfx.cpp | 23 | ||||
| -rw-r--r-- | backends/platform/android/jni.cpp | 192 | ||||
| -rw-r--r-- | backends/platform/android/jni.h | 48 | ||||
| -rw-r--r-- | backends/platform/android/org/inodes/gus/scummvm/ScummVM.java | 478 | ||||
| -rw-r--r-- | backends/platform/android/org/inodes/gus/scummvm/ScummVMActivity.java | 91 | 
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;  | 
