diff options
| -rw-r--r-- | backends/platform/android/android.cpp | 970 | ||||
| -rw-r--r-- | backends/platform/android/android.h | 200 | ||||
| -rw-r--r-- | backends/platform/android/gfx.cpp | 459 | ||||
| -rw-r--r-- | backends/platform/android/jni.cpp | 387 | ||||
| -rw-r--r-- | backends/platform/android/module.mk | 6 | ||||
| -rw-r--r-- | backends/platform/android/texture.cpp (renamed from backends/platform/android/video.cpp) | 2 | ||||
| -rw-r--r-- | backends/platform/android/texture.h (renamed from backends/platform/android/video.h) | 5 | 
7 files changed, 1062 insertions, 967 deletions
diff --git a/backends/platform/android/android.cpp b/backends/platform/android/android.cpp index c49745f8bd..a0a53474e8 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 <jni.h> - -#include <string.h> -#include <unistd.h> -#include <pthread.h>  #include <sys/time.h>  #include <time.h> +#include <unistd.h> -#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 <class T>  static inline T scalef(T in, float numerator, float denominator) {  	return static_cast<float>(in) * numerator / denominator;  } -static inline GLfixed xdiv(int numerator, int denominator) { -	assert(numerator < (1 << 16)); -	return (numerator << 16) / denominator; -} - -#ifdef DYNAMIC_MODULES -class AndroidPluginProvider : public POSIXPluginProvider { -protected: -	virtual void addCustomDirectories(Common::FSList &dirs) const; -}; -#endif - -class OSystem_Android : public BaseBackend, public PaletteManager { -private: -	// back pointer to (java) peer instance -	jobject _back_ptr; - -	jmethodID MID_displayMessageOnOSD; -	jmethodID MID_setWindowCaption; -	jmethodID MID_initBackend; -	jmethodID MID_audioSampleRate; -	jmethodID MID_showVirtualKeyboard; -	jmethodID MID_getSysArchives; -	jmethodID MID_getPluginDirectories; -	jmethodID MID_setupScummVMSurface; -	jmethodID MID_destroyScummVMSurface; -	jmethodID MID_swapBuffers; - -	int _screen_changeid; -	int _egl_surface_width; -	int _egl_surface_height; - -	bool _force_redraw; - -	// Game layer -	GLESPaletteTexture *_game_texture; -	int _shake_offset; -	Common::Rect _focus_rect; - -	// Overlay layer -	GLES4444Texture *_overlay_texture; -	bool _show_overlay; - -	// Mouse layer -	GLESPaletteATexture *_mouse_texture; -	Common::Point _mouse_hotspot; -	int _mouse_targetscale; -	bool _show_mouse; -	bool _use_mouse_palette; - -	Common::Queue<Common::Event> _event_queue; -	MutexRef _event_queue_lock; - -	bool _timer_thread_exit; -	pthread_t _timer_thread; -	static void *timerThreadFunc(void *arg); - -	bool _enable_zoning; -	bool _virtkeybd_on; - -	Common::SaveFileManager *_savefile; -	Audio::MixerImpl *_mixer; -	Common::TimerManager *_timer; -	FilesystemFactory *_fsFactory; -	Common::Archive *_asset_archive; -	timeval _startTime; - -	void setupScummVMSurface(); -	void destroyScummVMSurface(); -	void setupKeymapper(); -	void _setCursorPalette(const byte *colors, uint start, uint num); - -public: -	OSystem_Android(jobject am); -	virtual ~OSystem_Android(); -	bool initJavaHooks(JNIEnv *env, jobject self); - -	static OSystem_Android *fromJavaObject(JNIEnv *env, jobject obj); -	virtual void initBackend(); -	void addPluginDirectories(Common::FSList &dirs) const; -	void enableZoning(bool enable) { _enable_zoning = enable; } -	void setSurfaceSize(int width, int height) { -		_egl_surface_width = width; -		_egl_surface_height = height; -	} - -	virtual bool hasFeature(Feature f); -	virtual void setFeatureState(Feature f, bool enable); -	virtual bool getFeatureState(Feature f); -	virtual const GraphicsMode *getSupportedGraphicsModes() const; -	virtual int getDefaultGraphicsMode() const; -	bool setGraphicsMode(const char *name); -	virtual bool setGraphicsMode(int mode); -	virtual int getGraphicsMode() const; -	virtual void initSize(uint width, uint height, -							const Graphics::PixelFormat *format); - -	virtual int getScreenChangeID() const { -		return _screen_changeid; -	} - -	virtual int16 getHeight(); -	virtual int16 getWidth(); - -	virtual PaletteManager *getPaletteManager() { -		return this; -	} - -protected: -	// PaletteManager API -	virtual void setPalette(const byte *colors, uint start, uint num); -	virtual void grabPalette(byte *colors, uint start, uint num); - -public: -	virtual void copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h); -	virtual void updateScreen(); -	virtual Graphics::Surface *lockScreen(); -	virtual void unlockScreen(); -	virtual void setShakePos(int shakeOffset); -	virtual void fillScreen(uint32 col); -	virtual void setFocusRectangle(const Common::Rect& rect); -	virtual void clearFocusRectangle(); - -	virtual void showOverlay(); -	virtual void hideOverlay(); -	virtual void clearOverlay(); -	virtual void grabOverlay(OverlayColor *buf, int pitch); -	virtual void copyRectToOverlay(const OverlayColor *buf, int pitch, int x, int y, int w, int h); -	virtual int16 getOverlayHeight(); -	virtual int16 getOverlayWidth(); - -	// RGBA 4444 -	virtual Graphics::PixelFormat getOverlayFormat() const { -		Graphics::PixelFormat format; - -		format.bytesPerPixel = 2; -		format.rLoss = 8 - 4; -		format.gLoss = 8 - 4; -		format.bLoss = 8 - 4; -		format.aLoss = 8 - 4; -		format.rShift = 3 * 4; -		format.gShift = 2 * 4; -		format.bShift = 1 * 4; -		format.aShift = 0 * 4; - -		return format; -	} - -	virtual bool showMouse(bool visible); - -	virtual void warpMouse(int x, int y); -	virtual void setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int cursorTargetScale, const Graphics::PixelFormat *format); -	virtual void setCursorPalette(const byte *colors, uint start, uint num); -	virtual void disableCursorPalette(bool disable); - -	virtual bool pollEvent(Common::Event &event); -	void pushEvent(const Common::Event& event); -	virtual uint32 getMillis(); -	virtual void delayMillis(uint msecs); - -	virtual MutexRef createMutex(void); -	virtual void lockMutex(MutexRef mutex); -	virtual void unlockMutex(MutexRef mutex); -	virtual void deleteMutex(MutexRef mutex); - -	virtual void quit(); - -	virtual void setWindowCaption(const char *caption); -	virtual void displayMessageOnOSD(const char *msg); -	virtual void showVirtualKeyboard(bool enable); - -	virtual Common::SaveFileManager *getSavefileManager(); -	virtual Audio::Mixer *getMixer(); -	virtual void getTimeAndDate(TimeDate &t) const; -	virtual Common::TimerManager *getTimerManager(); -	virtual FilesystemFactory *getFilesystemFactory(); -	virtual void logMessage(LogMessageType::Type type, const char *message); -	virtual void addSysArchivesToSearchSet(Common::SearchSet &s, int priority = 0); -}; +OSystem_Android *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<Audio::MixerImpl *>(cpp_obj->getMixer()); -	assert(mixer); -	mixer->mixCallback(reinterpret_cast<byte *>(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<int>(env->GetIntField( -			java_event, FID_Event_kbd_ascii)); -		event.kbd.flags = static_cast<int>(env->GetIntField( -			java_event, FID_Event_kbd_flags)); -		break; -	case Common::EVENT_MOUSEMOVE: -	case Common::EVENT_LBUTTONDOWN: -	case Common::EVENT_LBUTTONUP: -	case Common::EVENT_RBUTTONDOWN: -	case Common::EVENT_RBUTTONUP: -	case Common::EVENT_WHEELUP: -	case Common::EVENT_WHEELDOWN: -	case Common::EVENT_MBUTTONDOWN: -	case Common::EVENT_MBUTTONUP: -		event.mouse.x = -			env->GetIntField(java_event, FID_Event_mouse_x); -		event.mouse.y = -			env->GetIntField(java_event, FID_Event_mouse_y); -		// This is a terrible hack.	 We stash "relativeness" -		// in the kbd.flags field until pollEvent() can work -		// it out. -		event.kbd.flags = env->GetBooleanField( -			java_event, FID_Event_mouse_relative) ? 1 : 0; -		break; -	default: -		break; -	} - -	cpp_obj->pushEvent(event); -} -  uint32 OSystem_Android::getMillis() {  	timeval curTime; @@ -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<char *>(cstr); - -			// exception already thrown? -			if (cstr == 0) -				goto cleanup; -		} - -		env->DeleteLocalRef(arg); -	} - -	g_system = cpp_obj; -	assert(g_system); - -	LOGI("Entering scummvm_main with %d args", argc); - -	res = scummvm_main(argc, argv); - -	LOGI("Exiting scummvm_main"); - -	g_system->quit(); - -cleanup: -	nargs--; - -	for (int i = 0; i < nargs; ++i) { -		if (argv[i] == 0) -			continue; - -		jstring arg = (jstring)env->GetObjectArrayElement(args, nargs); - -		// Exception already thrown? -		if (arg == 0) -			return res; - -		env->ReleaseStringUTFChars(arg, argv[i]); -		env->DeleteLocalRef(arg); -	} - -	return res; -} -  #ifdef DYNAMIC_MODULES  void AndroidPluginProvider::addCustomDirectories(Common::FSList &dirs) const { -	OSystem_Android *g_system_android = (OSystem_Android *)g_system; -	g_system_android->addPluginDirectories(dirs); +	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 855fb04b5d..b31c0fd385 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 <pthread.h> + +#include <jni.h>  #include <android/log.h>  #include <GLES/gl.h> @@ -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<Common::Event> _event_queue; +	MutexRef _event_queue_lock; + +	bool _timer_thread_exit; +	pthread_t _timer_thread; +	static void *timerThreadFunc(void *arg); + +	bool _enable_zoning; +	bool _virtkeybd_on; + +	Common::SaveFileManager *_savefile; +	Audio::MixerImpl *_mixer; +	Common::TimerManager *_timer; +	FilesystemFactory *_fsFactory; +	Common::Archive *_asset_archive; +	timeval _startTime; + +	void setupScummVMSurface(); +	void destroyScummVMSurface(); +	void setupKeymapper(); +	void _setCursorPalette(const byte *colors, uint start, uint num); + +public: +	OSystem_Android(jobject am); +	virtual ~OSystem_Android(); +	bool initJavaHooks(JNIEnv *env, jobject self); +	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 0000000000..eb5a66c6d9 --- /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 0000000000..0f1366148f --- /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<char *>(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<int>(env->GetIntField( +			java_event, FID_Event_kbd_ascii)); +		event.kbd.flags = static_cast<int>(env->GetIntField( +			java_event, FID_Event_kbd_flags)); +		break; +	case Common::EVENT_MOUSEMOVE: +	case Common::EVENT_LBUTTONDOWN: +	case Common::EVENT_LBUTTONUP: +	case Common::EVENT_RBUTTONDOWN: +	case Common::EVENT_RBUTTONUP: +	case Common::EVENT_WHEELUP: +	case Common::EVENT_WHEELDOWN: +	case Common::EVENT_MBUTTONDOWN: +	case Common::EVENT_MBUTTONUP: +		event.mouse.x = +			env->GetIntField(java_event, FID_Event_mouse_x); +		event.mouse.y = +			env->GetIntField(java_event, FID_Event_mouse_y); +		// This is a terrible hack.	 We stash "relativeness" +		// in the kbd.flags field until pollEvent() can work +		// it out. +		event.kbd.flags = env->GetBooleanField( +			java_event, FID_Event_mouse_relative) ? 1 : 0; +		break; +	default: +		break; +	} + +	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<Audio::MixerImpl *>(g_sys->getMixer()); +	assert(mixer); +	mixer->mixCallback(reinterpret_cast<byte *>(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 8b120b21ff..3bcfa7ad87 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 index f8427c2ac8..b638976dc4 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 index da42ea876d..050eafa073 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 <GLES/gl.h> @@ -207,3 +210,5 @@ protected:  };  #endif +#endif +  | 
