diff --git a/alienblaster/ChangeAppSettings.sh b/alienblaster/ChangeAppSettings.sh index efdef2215..55ccfceea 100755 --- a/alienblaster/ChangeAppSettings.sh +++ b/alienblaster/ChangeAppSettings.sh @@ -61,9 +61,16 @@ cat project/AndroidManifest.xml | \ project/AndroidManifest.xml.1 mv -f project/AndroidManifest.xml.1 project/AndroidManifest.xml +for F in project/src/*.java; do + echo Patching $F + cat $F | \ + sed "s/package .*;/package $AppFullName;/" > \ + $F.1 + mv -f $F.1 $F +done + echo Patching project/src/DemoActivity.java cat project/src/DemoActivity.java | \ - sed "s/package .*;/package $AppFullName;/" | \ sed "s/public static String ApplicationName = .*;/public static String ApplicationName = \"$AppShortName\";/" | \ sed "s^public static String DataDownloadUrl = \".*\";^public static String DataDownloadUrl = \"$AppDataDownloadUrl1\";^" | \ sed "s/public static boolean DownloadToSdcard = .*;/public static boolean DownloadToSdcard = $DownloadToSdcard1;/" > \ diff --git a/alienblaster/project/jni/sdl/src/video/android/SDL_androidvideo.c b/alienblaster/project/jni/sdl/src/video/android/SDL_androidvideo.c index 5b5ab6217..7c6ca3756 100644 --- a/alienblaster/project/jni/sdl/src/video/android/SDL_androidvideo.c +++ b/alienblaster/project/jni/sdl/src/video/android/SDL_androidvideo.c @@ -125,20 +125,20 @@ static void * memBuffer1 = NULL; static void * memBuffer2 = NULL; static void * memBuffer = NULL; static int sdl_opengl = 0; -// We have one Java thread drawing on GL surface, and another native C thread (typically main()) feeding it with video data -extern SDL_Thread * SDL_mainThread; -SDL_Thread * SDL_mainThread = NULL; -// Some wicked multithreading -static SDL_mutex * WaitForNativeRender = NULL; -static SDL_cond * WaitForNativeRender1 = NULL; -static enum { Render_State_Started, Render_State_Processing, Render_State_Finished } - WaitForNativeRenderState = Render_State_Finished; // Some wicked GLES stuff -static enum { GL_State_Init, GL_State_Ready, GL_State_Uninit, GL_State_Uninit2 } openglInitialized = GL_State_Uninit2; static GLuint texture = 0; +// Extremely wicked JNI environment to call Java functions from C code +static JNIEnv* JavaEnv = NULL; +static jclass JavaRendererClass = NULL; +static jobject JavaRenderer = NULL; +static jmethodID JavaSwapBuffers = NULL; + + static SDLKey keymap[KEYCODE_LAST+1]; +static int CallJavaSwapBuffers(); +static void SdlGlRenderInit(); static int processAndroidTrackballKeyDelays( int key, int action ); /* ANDROID driver bootstrap functions */ @@ -272,7 +272,7 @@ SDL_Surface *ANDROID_SetVideoMode(_THIS, SDL_Surface *current, memX = width; memY = height; - //if( ! sdl_opengl ) + if( ! sdl_opengl ) { memBuffer1 = SDL_malloc(memX * memY * (bpp / 8)); if ( ! memBuffer1 ) { @@ -295,8 +295,6 @@ SDL_Surface *ANDROID_SetVideoMode(_THIS, SDL_Surface *current, memBuffer = memBuffer1; } - openglInitialized = GL_State_Init; - /* Allocate the new pixel format for the screen */ if ( ! SDL_ReallocFormat(current, bpp, 0, 0, 0, 0) ) { if(memBuffer) @@ -313,15 +311,9 @@ SDL_Surface *ANDROID_SetVideoMode(_THIS, SDL_Surface *current, current->h = height; current->pitch = memX * (bpp / 8); current->pixels = memBuffer; - - if( ! WaitForNativeRender ) - { - WaitForNativeRender = SDL_CreateMutex(); - WaitForNativeRender1 = SDL_CreateCond(); - } - /* Wait 'till we can draw */ - ANDROID_FlipHWSurface(this, current); + SdlGlRenderInit(); + /* We're done */ return(current); } @@ -331,9 +323,12 @@ SDL_Surface *ANDROID_SetVideoMode(_THIS, SDL_Surface *current, */ void ANDROID_VideoQuit(_THIS) { - openglInitialized = GL_State_Uninit; - while( openglInitialized != GL_State_Uninit2 ) - SDL_Delay(50); + if( ! sdl_opengl ) + { + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + glDeleteTextures(1, &texture); + } memX = 0; memY = 0; @@ -343,10 +338,6 @@ void ANDROID_VideoQuit(_THIS) if( memBuffer2 ) SDL_free( memBuffer2 ); memBuffer2 = NULL; - SDL_DestroyMutex( WaitForNativeRender ); - WaitForNativeRender = NULL; - SDL_DestroyCond( WaitForNativeRender1 ); - WaitForNativeRender1 = NULL; int i; @@ -397,37 +388,15 @@ static void ANDROID_UpdateRects(_THIS, int numrects, SDL_Rect *rects) static int ANDROID_FlipHWSurface(_THIS, SDL_Surface *surface) { - if( ! WaitForNativeRender ) + if( ! sdl_opengl ) { - __android_log_print(ANDROID_LOG_ERROR, "libSDL", "FlipHWSurface: called before SetVideoMode"); - return 0; - } - SDL_mutexP(WaitForNativeRender); - while( WaitForNativeRenderState != Render_State_Finished ) - { - if( SDL_CondWaitTimeout( WaitForNativeRender1, WaitForNativeRender, 5000 ) != 0 ) - { - __android_log_print(ANDROID_LOG_INFO, "libSDL", "FlipHWSurface: Frame failed to render"); - SDL_mutexV(WaitForNativeRender); - return(0); - } - } + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, memX, memY, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, memBuffer); + if( sWindowHeight < memY || sWindowWidth < memX ) + glDrawTexiOES(0, 0, 1, sWindowWidth, sWindowHeight); // Larger than screen - shrink to fit + else + glDrawTexiOES(0, sWindowHeight-memY, 1, memX, memY); // Smaller than screen - do not scale, it's faster that way - WaitForNativeRenderState = Render_State_Started; - - SDL_mutexV(WaitForNativeRender); - SDL_CondSignal(WaitForNativeRender1); - SDL_mutexP(WaitForNativeRender); - - if( WaitForNativeRenderState == Render_State_Started ) - if( SDL_CondWaitTimeout( WaitForNativeRender1, WaitForNativeRender, 5000 ) != 0 ) - { - __android_log_print(ANDROID_LOG_INFO, "libSDL", "FlipHWSurface: Frame rendering timed out"); - } - - if( WaitForNativeRenderState != Render_State_Started ) - { - if( ! sdl_opengl && surface && surface->flags & SDL_DOUBLEBUF ) + if( surface->flags & SDL_DOUBLEBUF ) { if( memBuffer == memBuffer1 ) memBuffer = memBuffer2; @@ -437,9 +406,11 @@ static int ANDROID_FlipHWSurface(_THIS, SDL_Surface *surface) } } - SDL_mutexV(WaitForNativeRender); + CallJavaSwapBuffers(); processAndroidTrackballKeyDelays( -1, 0 ); + + SDL_Delay(10); return(0); }; @@ -447,7 +418,6 @@ static int ANDROID_FlipHWSurface(_THIS, SDL_Surface *surface) void ANDROID_GL_SwapBuffers(_THIS) { ANDROID_FlipHWSurface(this, NULL); - //__android_log_print(ANDROID_LOG_INFO, "libSDL", "GL_SwapBuffers: Frame rendered"); }; int ANDROID_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors) @@ -476,14 +446,15 @@ JAVA_EXPORT_NAME(DemoRenderer_nativeResize) ( JNIEnv* env, jobject thiz, jint extern void JAVA_EXPORT_NAME(DemoRenderer_nativeDone) ( JNIEnv* env, jobject thiz ) { - if( SDL_mainThread ) - { - __android_log_print(ANDROID_LOG_INFO, "libSDL", "quitting..."); - SDL_PrivateQuit(); - SDL_WaitThread(SDL_mainThread, NULL); - SDL_mainThread = NULL; - __android_log_print(ANDROID_LOG_INFO, "libSDL", "quit OK"); - } + __android_log_print(ANDROID_LOG_INFO, "libSDL", "quitting..."); + SDL_PrivateQuit(); + __android_log_print(ANDROID_LOG_INFO, "libSDL", "quit OK"); +} + +extern void +JAVA_EXPORT_NAME(AccelerometerReader_nativeAccelerometer) ( JNIEnv* env, jobject thiz, jfloat accX, jfloat accY, jfloat accZ ) +{ + // TODO: use accelerometer as joystick } enum MOUSE_ACTION { MOUSE_DOWN = 0, MOUSE_UP=1, MOUSE_MOVE=2 }; @@ -518,78 +489,9 @@ static SDL_keysym *TranslateKey(int scancode, SDL_keysym *keysym) return(keysym); } -static int AndroidTrackballKeyDelays[4] = {0,0,0,0}; - -// Key = -1 if we want to send KeyUp events from main loop -static int processAndroidTrackballKeyDelays( int key, int action ) -{ - #if ! defined(SDL_TRACKBALL_KEYUP_DELAY) || (SDL_TRACKBALL_KEYUP_DELAY == 0) - return 0; - #else - // Send Directional Pad Up events with a delay, so app wil lthink we're holding the key a bit - static const int KeysMapping[4] = {KEYCODE_DPAD_UP, KEYCODE_DPAD_DOWN, KEYCODE_DPAD_LEFT, KEYCODE_DPAD_RIGHT}; - int idx, idx2; - SDL_keysym keysym; - - if( key < 0 ) - { - for( idx = 0; idx < 4; idx ++ ) - { - if( AndroidTrackballKeyDelays[idx] > 0 ) - { - AndroidTrackballKeyDelays[idx] --; - if( AndroidTrackballKeyDelays[idx] == 0 ) - SDL_PrivateKeyboard( SDL_RELEASED, TranslateKey(KeysMapping[idx], &keysym) ); - } - } - } - else - { - idx = -1; - // Too lazy to do switch or function - if( key == KEYCODE_DPAD_UP ) - idx = 0; - else if( key == KEYCODE_DPAD_DOWN ) - idx = 1; - else if( key == KEYCODE_DPAD_LEFT ) - idx = 2; - else if( key == KEYCODE_DPAD_RIGHT ) - idx = 3; - if( idx >= 0 ) - { - if( action && AndroidTrackballKeyDelays[idx] == 0 ) - { - // User pressed key for the first time - idx2 = (idx + 2) % 4; // Opposite key for current key - if it's still pressing, release it - if( AndroidTrackballKeyDelays[idx2] > 0 ) - { - AndroidTrackballKeyDelays[idx2] = 0; - SDL_PrivateKeyboard( SDL_RELEASED, TranslateKey(KeysMapping[idx2], &keysym) ); - } - SDL_PrivateKeyboard( SDL_PRESSED, TranslateKey(key, &keysym) ); - } - else if( !action && AndroidTrackballKeyDelays[idx] == 0 ) - { - // User released key - make a delay, do not send release event - AndroidTrackballKeyDelays[idx] = SDL_TRACKBALL_KEYUP_DELAY; - } - else if( action && AndroidTrackballKeyDelays[idx] > 0 ) - { - // User pressed key another time - add some more time for key to be pressed - AndroidTrackballKeyDelays[idx] += SDL_TRACKBALL_KEYUP_DELAY; - if( AndroidTrackballKeyDelays[idx] < SDL_TRACKBALL_KEYUP_DELAY * 4 ) - AndroidTrackballKeyDelays[idx] = SDL_TRACKBALL_KEYUP_DELAY * 4; - } - return 1; - } - } - return 0; - - #endif -} void -JAVA_EXPORT_NAME(DemoGLSurfaceView_nativeKey) ( JNIEnv* env, jobject thiz, jint key, jint action ) +JAVA_EXPORT_NAME(DemoGLSurfaceView_nativeKey) ( JNIEnv* env, jobject thiz, jint key, jint action ) { //__android_log_print(ANDROID_LOG_INFO, "libSDL", "key event %i %s", key, action ? "down" : "up"); SDL_keysym keysym; @@ -597,9 +499,7 @@ JAVA_EXPORT_NAME(DemoGLSurfaceView_nativeKey) ( JNIEnv* env, jobject thiz, jin SDL_PrivateKeyboard( action ? SDL_PRESSED : SDL_RELEASED, TranslateKey(key, &keysym) ); } -// The most wicked routine out there, all wicked multithreading and GL-ES stuff here -extern void -JAVA_EXPORT_NAME(DemoRenderer_nativeRender) ( JNIEnv* env, jobject thiz, jfloat accX, jfloat accY, jfloat accZ ) +void SdlGlRenderInit() { // Set up an array of values to use as the sprite vertices. static GLfloat vertices[] = @@ -629,12 +529,8 @@ JAVA_EXPORT_NAME(DemoRenderer_nativeRender) ( JNIEnv* env, jobject thiz, jfloa int textX, textY; void * memBufferTemp; - if( !sdl_opengl && memBuffer && openglInitialized != GL_State_Uninit2 ) + if( !sdl_opengl && memBuffer ) { - if( openglInitialized == GL_State_Init ) - { - openglInitialized = GL_State_Ready; - // Texture sizes should be 2^n textX = memX; textY = memY; @@ -714,122 +610,24 @@ JAVA_EXPORT_NAME(DemoRenderer_nativeRender) ( JNIEnv* env, jobject thiz, jfloa glFinish(); SDL_free( textBuffer ); - - } - else if( openglInitialized == GL_State_Uninit ) - { - openglInitialized = GL_State_Uninit2; - - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); - - glDeleteTextures(1, &texture); - - return; - } - - if( WaitForNativeRender ) - { - SDL_mutexP(WaitForNativeRender); - - WaitForNativeRenderState = Render_State_Finished; - - SDL_mutexV(WaitForNativeRender); - SDL_CondSignal(WaitForNativeRender1); - SDL_mutexP(WaitForNativeRender); - - while( WaitForNativeRenderState != Render_State_Started ) - { - if( SDL_CondWaitTimeout( WaitForNativeRender1, WaitForNativeRender, 5000 ) != 0 ) - { - __android_log_print(ANDROID_LOG_INFO, "libSDL", "nativeRender: Frame failed to render"); - SDL_mutexV(WaitForNativeRender); - return; - } - } - - memBufferTemp = memBuffer; - - WaitForNativeRenderState = Render_State_Processing; - - SDL_mutexV(WaitForNativeRender); - - SDL_CondSignal(WaitForNativeRender1); - } - else - memBufferTemp = memBuffer; - - // TODO: use accelerometer as joystick - - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, memX, memY, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, memBufferTemp); - if( sWindowHeight < memY || sWindowWidth < memX ) - glDrawTexiOES(0, 0, 1, sWindowWidth, sWindowHeight); // Larger than screen - shrink to fit - else - glDrawTexiOES(0, sWindowHeight-memY, 1, memX, memY); // Smaller than screen - do not scale, it's faster that way - - //glFinish(); //glFlush(); - } - else if( sdl_opengl && openglInitialized != GL_State_Uninit2 ) - { - if( openglInitialized == GL_State_Init ) - { - openglInitialized = GL_State_Ready; +} - glViewport(0, 0, memX, memY); - glClearColor(0,0,0,0); - } - else if( openglInitialized == GL_State_Uninit ) - { - openglInitialized = GL_State_Uninit2; - return; - } - - if( WaitForNativeRender ) - { - SDL_mutexP(WaitForNativeRender); - - WaitForNativeRenderState = Render_State_Finished; +void +JAVA_EXPORT_NAME(DemoRenderer_nativeInitJavaCallbacks) ( JNIEnv* env, jobject thiz ) +{ + char classPath[1024]; + JavaEnv = env; + JavaRenderer = thiz; + + JavaRendererClass = (*JavaEnv)->GetObjectClass(JavaEnv, thiz); + JavaSwapBuffers = (*JavaEnv)->GetMethodID(JavaEnv, JavaRendererClass, "swapBuffers", "()I"); +} - SDL_mutexV(WaitForNativeRender); - SDL_CondSignal(WaitForNativeRender1); - SDL_mutexP(WaitForNativeRender); - - while( WaitForNativeRenderState != Render_State_Started ) - { - if( SDL_CondWaitTimeout( WaitForNativeRender1, WaitForNativeRender, 5000 ) != 0 ) - { - __android_log_print(ANDROID_LOG_INFO, "libSDL", "nativeRender: Frame failed to render"); - SDL_mutexV(WaitForNativeRender); - return; - } - } - - //__android_log_print(ANDROID_LOG_INFO, "libSDL", "nativeRender: Frame rendered"); - - WaitForNativeRenderState = Render_State_Processing; - - SDL_mutexV(WaitForNativeRender); - - SDL_CondSignal(WaitForNativeRender1); - } - } - else - { - /* - // Flash the screen - if( clearColor >= 1.0f ) - clearColorDir = -1; - else if( clearColor <= 0.0f ) - clearColorDir = 1; - - clearColor += (float)clearColorDir * 0.01f; - glClearColor(clearColor,clearColor,clearColor,0); - glClear(GL_COLOR_BUFFER_BIT); - SDL_Delay(50); - */ - } +int CallJavaSwapBuffers() +{ + return (*JavaEnv)->CallIntMethod( JavaEnv, JavaRenderer, JavaSwapBuffers ); } void ANDROID_InitOSKeymap(_THIS) @@ -953,3 +751,72 @@ void ANDROID_InitOSKeymap(_THIS) } +static int AndroidTrackballKeyDelays[4] = {0,0,0,0}; + +// Key = -1 if we want to send KeyUp events from main loop +int processAndroidTrackballKeyDelays( int key, int action ) +{ + #if ! defined(SDL_TRACKBALL_KEYUP_DELAY) || (SDL_TRACKBALL_KEYUP_DELAY == 0) + return 0; + #else + // Send Directional Pad Up events with a delay, so app wil lthink we're holding the key a bit + static const int KeysMapping[4] = {KEYCODE_DPAD_UP, KEYCODE_DPAD_DOWN, KEYCODE_DPAD_LEFT, KEYCODE_DPAD_RIGHT}; + int idx, idx2; + SDL_keysym keysym; + + if( key < 0 ) + { + for( idx = 0; idx < 4; idx ++ ) + { + if( AndroidTrackballKeyDelays[idx] > 0 ) + { + AndroidTrackballKeyDelays[idx] --; + if( AndroidTrackballKeyDelays[idx] == 0 ) + SDL_PrivateKeyboard( SDL_RELEASED, TranslateKey(KeysMapping[idx], &keysym) ); + } + } + } + else + { + idx = -1; + // Too lazy to do switch or function + if( key == KEYCODE_DPAD_UP ) + idx = 0; + else if( key == KEYCODE_DPAD_DOWN ) + idx = 1; + else if( key == KEYCODE_DPAD_LEFT ) + idx = 2; + else if( key == KEYCODE_DPAD_RIGHT ) + idx = 3; + if( idx >= 0 ) + { + if( action && AndroidTrackballKeyDelays[idx] == 0 ) + { + // User pressed key for the first time + idx2 = (idx + 2) % 4; // Opposite key for current key - if it's still pressing, release it + if( AndroidTrackballKeyDelays[idx2] > 0 ) + { + AndroidTrackballKeyDelays[idx2] = 0; + SDL_PrivateKeyboard( SDL_RELEASED, TranslateKey(KeysMapping[idx2], &keysym) ); + } + SDL_PrivateKeyboard( SDL_PRESSED, TranslateKey(key, &keysym) ); + } + else if( !action && AndroidTrackballKeyDelays[idx] == 0 ) + { + // User released key - make a delay, do not send release event + AndroidTrackballKeyDelays[idx] = SDL_TRACKBALL_KEYUP_DELAY; + } + else if( action && AndroidTrackballKeyDelays[idx] > 0 ) + { + // User pressed key another time - add some more time for key to be pressed + AndroidTrackballKeyDelays[idx] += SDL_TRACKBALL_KEYUP_DELAY; + if( AndroidTrackballKeyDelays[idx] < SDL_TRACKBALL_KEYUP_DELAY * 4 ) + AndroidTrackballKeyDelays[idx] = SDL_TRACKBALL_KEYUP_DELAY * 4; + } + return 1; + } + } + return 0; + + #endif +} diff --git a/alienblaster/project/jni/sdl_main/sdl_main.c b/alienblaster/project/jni/sdl_main/sdl_main.c index 1ca6e593e..c76214618 100644 --- a/alienblaster/project/jni/sdl_main/sdl_main.c +++ b/alienblaster/project/jni/sdl_main/sdl_main.c @@ -15,15 +15,6 @@ #endif -extern C_LINKAGE int main( int argc, char ** argv ); -static int SDLCALL MainThreadWrapper(void * dummy) -{ - int argc = 1; - char * argv[] = { "sdl" }; - chdir(SDL_CURDIR_PATH); - return main( argc, argv ); -}; - #ifndef SDL_JAVA_PACKAGE_PATH #error You have to define SDL_JAVA_PACKAGE_PATH to your package path with dots replaced with underscores, for example "com_example_SanAngeles" #endif @@ -31,13 +22,14 @@ static int SDLCALL MainThreadWrapper(void * dummy) #define JAVA_EXPORT_NAME1(name,package) JAVA_EXPORT_NAME2(name,package) #define JAVA_EXPORT_NAME(name) JAVA_EXPORT_NAME1(name,SDL_JAVA_PACKAGE_PATH) -extern C_LINKAGE SDL_Thread * SDL_mainThread; - extern C_LINKAGE void -JAVA_EXPORT_NAME(DemoRenderer_nativeInit) ( JNIEnv* env, jobject thiz ) +JAVA_EXPORT_NAME(DemoRenderer_nativeInit) ( JNIEnv* env, jobject thiz ) { - SDL_mainThread = SDL_CreateThread( MainThreadWrapper, NULL ); -} + int argc = 1; + char * argv[] = { "sdl" }; + chdir(SDL_CURDIR_PATH); + main( argc, argv ); +}; #undef JAVA_EXPORT_NAME #undef JAVA_EXPORT_NAME1 diff --git a/alienblaster/project/src/DemoActivity.java b/alienblaster/project/src/DemoActivity.java index 7e3052e3a..87bcc1839 100644 --- a/alienblaster/project/src/DemoActivity.java +++ b/alienblaster/project/src/DemoActivity.java @@ -1,9 +1,15 @@ // This string is autogenerated by ChangeAppSettings.sh, do not change spaces amount package de.schwardtnet.alienblaster; -import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGL11; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLContext; +import javax.microedition.khronos.egl.EGLDisplay; +import javax.microedition.khronos.egl.EGLSurface; + import android.app.Activity; import android.content.Context; import android.opengl.GLSurfaceView; @@ -65,22 +71,31 @@ class AccelerometerReader implements SensorListener { private long timekeeper; private float [] v; + + private SensorManager _manager = null; public AccelerometerReader(Activity context) { v = new float[3]; - SensorManager sma = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); - if( sma != null ) + _manager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + if( _manager != null ) { timekeeper = android.os.SystemClock.uptimeMillis(); int mask = 0; - mask |= SensorManager.SENSOR_ORIENTATION; + //mask |= SensorManager.SENSOR_ORIENTATION; mask |= SensorManager.SENSOR_ACCELEROMETER; - sma.registerListener(this, mask, SensorManager.SENSOR_DELAY_GAME); + _manager.registerListener(this, mask, SensorManager.SENSOR_DELAY_GAME); + } + } + + public synchronized void stop() { + if( _manager != null ) + { + _manager.unregisterListener(this); } } public synchronized void onSensorChanged(int sensor, float[] values) { - if (android.os.SystemClock.uptimeMillis() < timekeeper + 20) return; + //if (android.os.SystemClock.uptimeMillis() < timekeeper + 20) return; timekeeper = android.os.SystemClock.uptimeMillis(); if (sensor == SensorManager.SENSOR_ACCELEROMETER) { @@ -90,8 +105,9 @@ class AccelerometerReader implements SensorListener { v[1] = values[1]; if( values.length >= 3 ) v[2] = values[2]; + nativeAccelerometer(v[0], v[1], v[2]); } - + } public synchronized void onAccuracyChanged(int i, int i1) { @@ -106,15 +122,16 @@ class AccelerometerReader implements SensorListener { ret[2] = v[2]; return ret; }; + + private native void nativeAccelerometer(float accX, float accY, float accZ); } -class DemoRenderer implements GLSurfaceView.Renderer { +class DemoRenderer extends GLSurfaceView_SDL.Renderer { public DemoRenderer(Activity _context) { - super(); context = _context; } @@ -128,30 +145,43 @@ class DemoRenderer implements GLSurfaceView.Renderer { } public void onDrawFrame(GL10 gl) { - if( accelerometer == null) { - accelerometer = new AccelerometerReader(context); - nativeInit(); - } - float [] f = accelerometer.readAccelerometer(); - nativeRender(f[0], f[1], f[2]); + + nativeInitJavaCallbacks(); + + nativeInit(); // Calls main() and never returns, hehe - we'll call eglSwapBuffers() from native code + + } + + public int swapBuffers() // Called from native code, returns 1 on success, 0 when GL context lost (user put app to background) + { + System.out.println("Java: swapBuffers() called"); + return super.SwapBuffers() ? 1 : 0; } public void exitApp() { nativeDone(); }; - private static native void nativeInit(); - private static native void nativeResize(int w, int h); - private static native void nativeRender(float accX, float accY, float accZ); - private static native void nativeDone(); - private AccelerometerReader accelerometer = null; + private native void nativeInitJavaCallbacks(); + private native void nativeInit(); + private native void nativeResize(int w, int h); + private native void nativeDone(); + private Activity context = null; + + private EGL10 mEgl = null; + private EGLDisplay mEglDisplay = null; + private EGLSurface mEglSurface = null; + private EGLContext mEglContext = null; + private int skipFrames = 0; + } -class DemoGLSurfaceView extends GLSurfaceView { +class DemoGLSurfaceView extends GLSurfaceView_SDL { public DemoGLSurfaceView(Activity context) { super(context); mParent = context; + accelerometer = new AccelerometerReader(context); mRenderer = new DemoRenderer(context); setRenderer(mRenderer); } @@ -175,6 +205,8 @@ class DemoGLSurfaceView extends GLSurfaceView { public void exitApp() { mRenderer.exitApp(); + accelerometer.stop(); + accelerometer = null; }; @Override @@ -191,9 +223,10 @@ class DemoGLSurfaceView extends GLSurfaceView { DemoRenderer mRenderer; Activity mParent; + AccelerometerReader accelerometer = null; - public static native void nativeMouse( int x, int y, int action ); - public static native void nativeKey( int keyCode, int down ); + public native void nativeMouse( int x, int y, int action ); + public native void nativeKey( int keyCode, int down ); } class AudioThread extends Thread { @@ -267,10 +300,10 @@ class AudioThread extends Thread { } } - private static native int[] nativeAudioInit(); - private static native int nativeAudioInit2(byte[] buf); - private static native int nativeAudioBufferLock(); - private static native int nativeAudioBufferUnlock(); + private native int[] nativeAudioInit(); + private native int nativeAudioInit2(byte[] buf); + private native int nativeAudioBufferLock(); + private native int nativeAudioBufferUnlock(); } diff --git a/alienblaster/project/src/GLSurfaceView_SDL.java b/alienblaster/project/src/GLSurfaceView_SDL.java new file mode 100644 index 000000000..19f748108 --- /dev/null +++ b/alienblaster/project/src/GLSurfaceView_SDL.java @@ -0,0 +1,1355 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This is GLSurfaceView class ripped out of Android 2.1 sources, + fixed with a hammer and rasp to work with libSDL port */ + +// This string is autogenerated by ChangeAppSettings.sh, do not change spaces amount +package de.schwardtnet.alienblaster; + +import java.io.Writer; +import java.util.ArrayList; + +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGL11; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLContext; +import javax.microedition.khronos.egl.EGLDisplay; +import javax.microedition.khronos.egl.EGLSurface; +import javax.microedition.khronos.opengles.GL; +import javax.microedition.khronos.opengles.GL10; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.Log; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +/** + * An implementation of SurfaceView that uses the dedicated surface for + * displaying OpenGL rendering. + *
+ * A GLSurfaceView provides the following features: + *
+ *
+ * Typically you use GLSurfaceView by subclassing it and overriding one or more of the + * View system input event methods. If your application does not need to override event + * methods then GLSurfaceView can be used as-is. For the most part + * GLSurfaceView behavior is customized by calling "set" methods rather than by subclassing. + * For example, unlike a regular View, drawing is delegated to a separate Renderer object which + * is registered with the GLSurfaceView + * using the {@link #setRenderer(Renderer)} call. + *
+ *
+ *
+ * By default GLSurfaceView chooses an available surface that's closest to a 16-bit R5G6B5 surface + * with a 16-bit depth buffer and no stencil. If you would prefer a different surface (for example, + * if you do not need a depth buffer) you can override the default behavior by calling one of the + * setEGLConfigChooser methods. + *
+ *
+ *
+ *
+ *
+ *
+ * To handle an event you will typically subclass GLSurfaceView and override the + * appropriate method, just as you would with any other View. However, when handling + * the event, you may need to communicate with the Renderer object + * that's running in the rendering thread. You can do this using any + * standard Java cross-thread communication mechanism. In addition, + * one relatively easy way to communicate with your renderer is + * to call + * {@link #queueEvent(Runnable)}. For example: + *
+ * class MyGLSurfaceView extends GLSurfaceView {
+ *
+ * private MyRenderer mMyRenderer;
+ *
+ * public void start() {
+ * mMyRenderer = ...;
+ * setRenderer(mMyRenderer);
+ * }
+ *
+ * public boolean onKeyDown(int keyCode, KeyEvent event) {
+ * if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
+ * queueEvent(new Runnable() {
+ * // This method will be called on the rendering
+ * // thread:
+ * public void run() {
+ * mMyRenderer.handleDpadCenter();
+ * }});
+ * return true;
+ * }
+ * return super.onKeyDown(keyCode, event);
+ * }
+ * }
+ *
+ *
+ */
+public class GLSurfaceView_SDL extends SurfaceView implements SurfaceHolder.Callback {
+ private final static boolean LOG_THREADS = false;
+ private final static boolean LOG_SURFACE = false;
+ private final static boolean LOG_RENDERER = false;
+ // Work-around for bug 2263168
+ private final static boolean DRAW_TWICE_AFTER_SIZE_CHANGED = true;
+ /**
+ * The renderer only renders
+ * when the surface is created, or when {@link #requestRender} is called.
+ *
+ * @see #getRenderMode()
+ * @see #setRenderMode(int)
+ */
+ public final static int RENDERMODE_WHEN_DIRTY = 0;
+ /**
+ * The renderer is called
+ * continuously to re-render the scene.
+ *
+ * @see #getRenderMode()
+ * @see #setRenderMode(int)
+ * @see #requestRender()
+ */
+ public final static int RENDERMODE_CONTINUOUSLY = 1;
+
+ /**
+ * Check glError() after every GL call and throw an exception if glError indicates
+ * that an error has occurred. This can be used to help track down which OpenGL ES call
+ * is causing an error.
+ *
+ * @see #getDebugFlags
+ * @see #setDebugFlags
+ */
+ public final static int DEBUG_CHECK_GL_ERROR = 1;
+
+ /**
+ * Log GL calls to the system log at "verbose" level with tag "GLSurfaceView".
+ *
+ * @see #getDebugFlags
+ * @see #setDebugFlags
+ */
+ public final static int DEBUG_LOG_GL_CALLS = 2;
+
+ /**
+ * Standard View constructor. In order to render something, you
+ * must call {@link #setRenderer} to register a renderer.
+ */
+ public GLSurfaceView_SDL(Context context) {
+ super(context);
+ init();
+ }
+
+ /**
+ * Standard View constructor. In order to render something, you
+ * must call {@link #setRenderer} to register a renderer.
+ */
+ public GLSurfaceView_SDL(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ private void init() {
+ // Install a SurfaceHolder.Callback so we get notified when the
+ // underlying surface is created and destroyed
+ SurfaceHolder holder = getHolder();
+ holder.addCallback(this);
+ }
+
+ /**
+ * Set the glWrapper. If the glWrapper is not null, its
+ * {@link GLWrapper#wrap(GL)} method is called
+ * whenever a surface is created. A GLWrapper can be used to wrap
+ * the GL object that's passed to the renderer. Wrapping a GL
+ * object enables examining and modifying the behavior of the
+ * GL calls made by the renderer.
+ * + * Wrapping is typically used for debugging purposes. + *
+ * The default value is null. + * @param glWrapper the new GLWrapper + */ + public void setGLWrapper(GLWrapper glWrapper) { + mGLWrapper = glWrapper; + } + + /** + * Set the debug flags to a new value. The value is + * constructed by OR-together zero or more + * of the DEBUG_CHECK_* constants. The debug flags take effect + * whenever a surface is created. The default value is zero. + * @param debugFlags the new debug flags + * @see #DEBUG_CHECK_GL_ERROR + * @see #DEBUG_LOG_GL_CALLS + */ + public void setDebugFlags(int debugFlags) { + mDebugFlags = debugFlags; + } + + /** + * Get the current value of the debug flags. + * @return the current value of the debug flags. + */ + public int getDebugFlags() { + return mDebugFlags; + } + + /** + * Set the renderer associated with this view. Also starts the thread that + * will call the renderer, which in turn causes the rendering to start. + *
This method should be called once and only once in the life-cycle of + * a GLSurfaceView. + *
The following GLSurfaceView methods can only be called before + * setRenderer is called: + *
+ * The following GLSurfaceView methods can only be called after + * setRenderer is called: + *
If this method is + * called, it must be called before {@link #setRenderer(Renderer)} + * is called. + *
+ * If this method is not called, then by default + * a context will be created with no shared context and + * with a null attribute list. + */ + public void setEGLContextFactory(EGLContextFactory factory) { + checkRenderThreadState(); + mEGLContextFactory = factory; + } + + /** + * Install a custom EGLWindowSurfaceFactory. + *
If this method is + * called, it must be called before {@link #setRenderer(Renderer)} + * is called. + *
+ * If this method is not called, then by default + * a window surface will be created with a null attribute list. + */ + public void setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory factory) { + checkRenderThreadState(); + mEGLWindowSurfaceFactory = factory; + } + + /** + * Install a custom EGLConfigChooser. + *
If this method is + * called, it must be called before {@link #setRenderer(Renderer)} + * is called. + *
+ * If no setEGLConfigChooser method is called, then by default the + * view will choose a config as close to 16-bit RGB as possible, with + * a depth buffer as close to 16 bits as possible. + * @param configChooser + */ + public void setEGLConfigChooser(EGLConfigChooser configChooser) { + checkRenderThreadState(); + mEGLConfigChooser = configChooser; + } + + /** + * Install a config chooser which will choose a config + * as close to 16-bit RGB as possible, with or without an optional depth + * buffer as close to 16-bits as possible. + *
If this method is + * called, it must be called before {@link #setRenderer(Renderer)} + * is called. + *
+ * If no setEGLConfigChooser method is called, then by default the + * view will choose a config as close to 16-bit RGB as possible, with + * a depth buffer as close to 16 bits as possible. + * + * @param needDepth + */ + public void setEGLConfigChooser(boolean needDepth) { + setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth)); + } + + /** + * Install a config chooser which will choose a config + * with at least the specified component sizes, and as close + * to the specified component sizes as possible. + *
If this method is + * called, it must be called before {@link #setRenderer(Renderer)} + * is called. + *
+ * If no setEGLConfigChooser method is called, then by default the + * view will choose a config as close to 16-bit RGB as possible, with + * a depth buffer as close to 16 bits as possible. + * + */ + public void setEGLConfigChooser(int redSize, int greenSize, int blueSize, + int alphaSize, int depthSize, int stencilSize) { + setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize, + blueSize, alphaSize, depthSize, stencilSize)); + } + /** + * Set the rendering mode. When renderMode is + * RENDERMODE_CONTINUOUSLY, the renderer is called + * repeatedly to re-render the scene. When renderMode + * is RENDERMODE_WHEN_DIRTY, the renderer only rendered when the surface + * is created, or when {@link #requestRender} is called. Defaults to RENDERMODE_CONTINUOUSLY. + *
+ * Using RENDERMODE_WHEN_DIRTY can improve battery life and overall system performance + * by allowing the GPU and CPU to idle when the view does not need to be updated. + *
+ * This method can only be called after {@link #setRenderer(Renderer)} + * + * @param renderMode one of the RENDERMODE_X constants + * @see #RENDERMODE_CONTINUOUSLY + * @see #RENDERMODE_WHEN_DIRTY + */ + public void setRenderMode(int renderMode) { + mGLThread.setRenderMode(renderMode); + } + + /** + * Get the current rendering mode. May be called + * from any thread. Must not be called before a renderer has been set. + * @return the current rendering mode. + * @see #RENDERMODE_CONTINUOUSLY + * @see #RENDERMODE_WHEN_DIRTY + */ + public int getRenderMode() { + return mGLThread.getRenderMode(); + } + + /** + * Request that the renderer render a frame. + * This method is typically used when the render mode has been set to + * {@link #RENDERMODE_WHEN_DIRTY}, so that frames are only rendered on demand. + * May be called + * from any thread. Must not be called before a renderer has been set. + */ + public void requestRender() { + mGLThread.requestRender(); + } + + /** + * This method is part of the SurfaceHolder.Callback interface, and is + * not normally called or subclassed by clients of GLSurfaceView. + */ + public void surfaceCreated(SurfaceHolder holder) { + mGLThread.surfaceCreated(); + } + + /** + * This method is part of the SurfaceHolder.Callback interface, and is + * not normally called or subclassed by clients of GLSurfaceView. + */ + public void surfaceDestroyed(SurfaceHolder holder) { + // Surface will be destroyed when we return + mGLThread.surfaceDestroyed(); + } + + /** + * This method is part of the SurfaceHolder.Callback interface, and is + * not normally called or subclassed by clients of GLSurfaceView. + */ + public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { + mGLThread.onWindowResize(w, h); + } + + /** + * Inform the view that the activity is paused. The owner of this view must + * call this method when the activity is paused. Calling this method will + * pause the rendering thread. + * Must not be called before a renderer has been set. + */ + public void onPause() { + mGLThread.onPause(); + } + + /** + * Inform the view that the activity is resumed. The owner of this view must + * call this method when the activity is resumed. Calling this method will + * recreate the OpenGL display and resume the rendering + * thread. + * Must not be called before a renderer has been set. + */ + public void onResume() { + mGLThread.onResume(); + } + + /** + * Queue a runnable to be run on the GL rendering thread. This can be used + * to communicate with the Renderer on the rendering thread. + * Must not be called before a renderer has been set. + * @param r the runnable to be run on the GL rendering thread. + */ + public void queueEvent(Runnable r) { + mGLThread.queueEvent(r); + } + + /** + * This method is used as part of the View class and is not normally + * called or subclassed by clients of GLSurfaceView. + * Must not be called before a renderer has been set. + */ + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + mGLThread.requestExitAndWait(); + } + + // ---------------------------------------------------------------------- + + /** + * An interface used to wrap a GL interface. + *
Typically + * used for implementing debugging and tracing on top of the default + * GL interface. You would typically use this by creating your own class + * that implemented all the GL methods by delegating to another GL instance. + * Then you could add your own behavior before or after calling the + * delegate. All the GLWrapper would do was instantiate and return the + * wrapper GL instance: + *
+ * class MyGLWrapper implements GLWrapper {
+ * GL wrap(GL gl) {
+ * return new MyGLImplementation(gl);
+ * }
+ * static class MyGLImplementation implements GL,GL10,GL11,... {
+ * ...
+ * }
+ * }
+ *
+ * @see #setGLWrapper(GLWrapper)
+ */
+ public interface GLWrapper {
+ /**
+ * Wraps a gl interface in another gl interface.
+ * @param gl a GL interface that is to be wrapped.
+ * @return either the input argument or another GL object that wraps the input argument.
+ */
+ GL wrap(GL gl);
+ }
+
+ /**
+ * A generic renderer interface.
+ * + * The renderer is responsible for making OpenGL calls to render a frame. + *
+ * GLSurfaceView clients typically create their own classes that implement + * this interface, and then call {@link GLSurfaceView#setRenderer} to + * register the renderer with the GLSurfaceView. + *
+ *
+ *
+ * Called when the rendering thread + * starts and whenever the EGL context is lost. The context will typically + * be lost when the Android device awakes after going to sleep. + *
+ * Since this method is called at the beginning of rendering, as well as + * every time the EGL context is lost, this method is a convenient place to put + * code to create resources that need to be created when the rendering + * starts, and that need to be recreated when the EGL context is lost. + * Textures are an example of a resource that you might want to create + * here. + *
+ * Note that when the EGL context is lost, all OpenGL resources associated + * with that context will be automatically deleted. You do not need to call + * the corresponding "glDelete" methods such as glDeleteTextures to + * manually delete these lost resources. + *
+ * @param gl the GL interface. Use instanceof to
+ * test if the interface supports GL11 or higher interfaces.
+ * @param config the EGLConfig of the created surface. Can be used
+ * to create matching pbuffers.
+ */
+ public abstract void onSurfaceCreated(GL10 gl, EGLConfig config);
+
+ /**
+ * Called when the surface changed size.
+ *
+ * Called after the surface is created and whenever + * the OpenGL ES surface size changes. + *
+ * Typically you will set your viewport here. If your camera + * is fixed then you could also set your projection matrix here: + *
+ * void onSurfaceChanged(GL10 gl, int width, int height) {
+ * gl.glViewport(0, 0, width, height);
+ * // for a fixed camera, set the projection too
+ * float ratio = (float) width / height;
+ * gl.glMatrixMode(GL10.GL_PROJECTION);
+ * gl.glLoadIdentity();
+ * gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
+ * }
+ *
+ * @param gl the GL interface. Use instanceof to
+ * test if the interface supports GL11 or higher interfaces.
+ * @param width
+ * @param height
+ */
+ public abstract void onSurfaceChanged(GL10 gl, int width, int height);
+
+ /**
+ * Called to draw the current frame.
+ * + * This method is responsible for drawing the current frame. + *
+ * The implementation of this method typically looks like this: + *
+ * void onDrawFrame(GL10 gl) {
+ * gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
+ * //... other gl calls to render the scene ...
+ * }
+ *
+ * @param gl the GL interface. Use instanceof to
+ * test if the interface supports GL11 or higher interfaces.
+ */
+ public abstract void onDrawFrame(GL10 gl);
+
+ public boolean SwapBuffers() {
+ if( mSwapBuffersCallback != null )
+ return mSwapBuffersCallback.SwapBuffers();
+ return false;
+ }
+
+ public void setSwapBuffersCallback( SwapBuffersCallback c ) {
+ mSwapBuffersCallback = c;
+ }
+
+ private SwapBuffersCallback mSwapBuffersCallback = null;
+ }
+
+ /**
+ * An interface for customizing the eglCreateContext and eglDestroyContext calls.
+ * + * This interface must be implemented by clients wishing to call + * {@link GLSurfaceView#setEGLContextFactory(EGLContextFactory)} + */ + public interface EGLContextFactory { + EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig); + void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context); + } + + private static class DefaultContextFactory implements EGLContextFactory { + + public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) { + return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, null); + } + + public void destroyContext(EGL10 egl, EGLDisplay display, + EGLContext context) { + egl.eglDestroyContext(display, context); + } + } + + /** + * An interface for customizing the eglCreateWindowSurface and eglDestroySurface calls. + *
+ * This interface must be implemented by clients wishing to call + * {@link GLSurfaceView#setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory)} + */ + public interface EGLWindowSurfaceFactory { + EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config, + Object nativeWindow); + void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface); + } + + private static class DefaultWindowSurfaceFactory implements EGLWindowSurfaceFactory { + + public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, + EGLConfig config, Object nativeWindow) { + return egl.eglCreateWindowSurface(display, config, nativeWindow, null); + } + + public void destroySurface(EGL10 egl, EGLDisplay display, + EGLSurface surface) { + egl.eglDestroySurface(display, surface); + } + } + + /** + * An interface for choosing an EGLConfig configuration from a list of + * potential configurations. + *
+ * This interface must be implemented by clients wishing to call
+ * {@link GLSurfaceView#setEGLConfigChooser(EGLConfigChooser)}
+ */
+ public interface EGLConfigChooser {
+ /**
+ * Choose a configuration from the list. Implementors typically
+ * implement this method by calling
+ * {@link EGL10#eglChooseConfig} and iterating through the results. Please consult the
+ * EGL specification available from The Khronos Group to learn how to call eglChooseConfig.
+ * @param egl the EGL10 for the current display.
+ * @param display the current display.
+ * @return the chosen configuration.
+ */
+ EGLConfig chooseConfig(EGL10 egl, EGLDisplay display);
+ }
+
+ private static abstract class BaseConfigChooser
+ implements EGLConfigChooser {
+ public BaseConfigChooser(int[] configSpec) {
+ mConfigSpec = configSpec;
+ }
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
+ int[] num_config = new int[1];
+ egl.eglChooseConfig(display, mConfigSpec, null, 0, num_config);
+
+ int numConfigs = num_config[0];
+
+ if (numConfigs <= 0) {
+ throw new IllegalArgumentException(
+ "No configs match configSpec");
+ }
+
+ EGLConfig[] configs = new EGLConfig[numConfigs];
+ egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs,
+ num_config);
+ EGLConfig config = chooseConfig(egl, display, configs);
+ if (config == null) {
+ throw new IllegalArgumentException("No config chosen");
+ }
+ return config;
+ }
+
+ abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
+ EGLConfig[] configs);
+
+ protected int[] mConfigSpec;
+ }
+
+ private static class ComponentSizeChooser extends BaseConfigChooser {
+ public ComponentSizeChooser(int redSize, int greenSize, int blueSize,
+ int alphaSize, int depthSize, int stencilSize) {
+ super(new int[] {
+ EGL10.EGL_RED_SIZE, redSize,
+ EGL10.EGL_GREEN_SIZE, greenSize,
+ EGL10.EGL_BLUE_SIZE, blueSize,
+ EGL10.EGL_ALPHA_SIZE, alphaSize,
+ EGL10.EGL_DEPTH_SIZE, depthSize,
+ EGL10.EGL_STENCIL_SIZE, stencilSize,
+ EGL10.EGL_NONE});
+ mValue = new int[1];
+ mRedSize = redSize;
+ mGreenSize = greenSize;
+ mBlueSize = blueSize;
+ mAlphaSize = alphaSize;
+ mDepthSize = depthSize;
+ mStencilSize = stencilSize;
+ }
+
+ @Override
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
+ EGLConfig[] configs) {
+ EGLConfig closestConfig = null;
+ int closestDistance = 1000;
+ for(EGLConfig config : configs) {
+ int d = findConfigAttrib(egl, display, config,
+ EGL10.EGL_DEPTH_SIZE, 0);
+ int s = findConfigAttrib(egl, display, config,
+ EGL10.EGL_STENCIL_SIZE, 0);
+ if (d >= mDepthSize && s>= mStencilSize) {
+ int r = findConfigAttrib(egl, display, config,
+ EGL10.EGL_RED_SIZE, 0);
+ int g = findConfigAttrib(egl, display, config,
+ EGL10.EGL_GREEN_SIZE, 0);
+ int b = findConfigAttrib(egl, display, config,
+ EGL10.EGL_BLUE_SIZE, 0);
+ int a = findConfigAttrib(egl, display, config,
+ EGL10.EGL_ALPHA_SIZE, 0);
+ int distance = Math.abs(r - mRedSize)
+ + Math.abs(g - mGreenSize)
+ + Math.abs(b - mBlueSize)
+ + Math.abs(a - mAlphaSize);
+ if (distance < closestDistance) {
+ closestDistance = distance;
+ closestConfig = config;
+ }
+ }
+ }
+ return closestConfig;
+ }
+
+ private int findConfigAttrib(EGL10 egl, EGLDisplay display,
+ EGLConfig config, int attribute, int defaultValue) {
+
+ if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
+ return mValue[0];
+ }
+ return defaultValue;
+ }
+
+ private int[] mValue;
+ // Subclasses can adjust these values:
+ protected int mRedSize;
+ protected int mGreenSize;
+ protected int mBlueSize;
+ protected int mAlphaSize;
+ protected int mDepthSize;
+ protected int mStencilSize;
+ }
+
+ /**
+ * This class will choose a supported surface as close to
+ * RGB565 as possible, with or without a depth buffer.
+ *
+ */
+ private static class SimpleEGLConfigChooser extends ComponentSizeChooser {
+ public SimpleEGLConfigChooser(boolean withDepthBuffer) {
+ super(4, 4, 4, 0, withDepthBuffer ? 16 : 0, 0);
+ // Adjust target values. This way we'll accept a 4444 or
+ // 555 buffer if there's no 565 buffer available.
+ mRedSize = 5;
+ mGreenSize = 6;
+ mBlueSize = 5;
+ }
+ }
+
+ /**
+ * An EGL helper class.
+ */
+
+ private class EglHelper {
+ public EglHelper() {
+
+ }
+
+ /**
+ * Initialize EGL for a given configuration spec.
+ * @param configSpec
+ */
+ public void start(){
+ /*
+ * Get an EGL instance
+ */
+ mEgl = (EGL10) EGLContext.getEGL();
+
+ /*
+ * Get to the default display.
+ */
+ mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+
+ /*
+ * We can now initialize EGL for that display
+ */
+ int[] version = new int[2];
+ mEgl.eglInitialize(mEglDisplay, version);
+ mEglConfig = mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);
+
+ /*
+ * Create an OpenGL ES context. This must be done only once, an
+ * OpenGL context is a somewhat heavy object.
+ */
+ mEglContext = mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);
+ if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {
+ throw new RuntimeException("createContext failed");
+ }
+
+ mEglSurface = null;
+ }
+
+ /*
+ * React to the creation of a new surface by creating and returning an
+ * OpenGL interface that renders to that surface.
+ */
+ public GL createSurface(SurfaceHolder holder) {
+ /*
+ * The window size has changed, so we need to create a new
+ * surface.
+ */
+ if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
+
+ /*
+ * Unbind and destroy the old EGL surface, if
+ * there is one.
+ */
+ mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
+ EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
+ mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);
+ }
+
+ /*
+ * Create an EGL surface we can render into.
+ */
+ mEglSurface = mEGLWindowSurfaceFactory.createWindowSurface(mEgl,
+ mEglDisplay, mEglConfig, holder);
+
+ if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
+ throwEglException("createWindowSurface");
+ }
+
+ /*
+ * Before we can issue GL commands, we need to make sure
+ * the context is current and bound to a surface.
+ */
+ if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+ throwEglException("eglMakeCurrent");
+ }
+
+ GL gl = mEglContext.getGL();
+ if (mGLWrapper != null) {
+ gl = mGLWrapper.wrap(gl);
+ }
+
+ return gl;
+ }
+
+ /**
+ * Display the current render surface.
+ * @return false if the context has been lost.
+ */
+ public boolean swap() {
+ mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
+
+ /*
+ * Always check for EGL_CONTEXT_LOST, which means the context
+ * and all associated data were lost (For instance because
+ * the device went to sleep). We need to sleep until we
+ * get a new surface.
+ */
+ return mEgl.eglGetError() != EGL11.EGL_CONTEXT_LOST;
+ }
+
+ public void destroySurface() {
+ if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
+ mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
+ EGL10.EGL_NO_SURFACE,
+ EGL10.EGL_NO_CONTEXT);
+ mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);
+ mEglSurface = null;
+ }
+ }
+
+ public void finish() {
+ if (mEglContext != null) {
+ mEGLContextFactory.destroyContext(mEgl, mEglDisplay, mEglContext);
+ mEglContext = null;
+ }
+ if (mEglDisplay != null) {
+ mEgl.eglTerminate(mEglDisplay);
+ mEglDisplay = null;
+ }
+ }
+
+ private void throwEglException(String function) {
+ throw new RuntimeException(function + " failed: " + mEgl.eglGetError());
+ }
+
+ EGL10 mEgl;
+ EGLDisplay mEglDisplay;
+ EGLSurface mEglSurface;
+ EGLConfig mEglConfig;
+ EGLContext mEglContext;
+ }
+
+ /**
+ * A generic GL Thread. Takes care of initializing EGL and GL. Delegates
+ * to a Renderer instance to do the actual drawing. Can be configured to
+ * render continuously or on request.
+ *
+ * All potentially blocking synchronization is done through the
+ * sGLThreadManager object. This avoids multiple-lock ordering issues.
+ *
+ */
+ class GLThread extends Thread implements SwapBuffersCallback {
+ GLThread(Renderer renderer) {
+ super();
+ mWidth = 0;
+ mHeight = 0;
+ mRequestRender = true;
+ mRenderMode = RENDERMODE_CONTINUOUSLY;
+ mRenderer = renderer;
+ mRenderer.setSwapBuffersCallback(this);
+ }
+
+ @Override
+ public void run() {
+ setName("GLThread " + getId());
+ if (LOG_THREADS) {
+ Log.i("GLThread", "starting tid=" + getId());
+ }
+
+ mEglHelper = new EglHelper();
+ SwapBuffers();
+ SwapBuffers();
+
+ mRenderer.onDrawFrame(mGL);
+
+ synchronized (sGLThreadManager) {
+ stopEglLocked();
+ }
+
+ sGLThreadManager.threadExiting(this);
+ }
+
+ /*
+ * This private method should only be called inside a
+ * synchronized(sGLThreadManager) block.
+ */
+ private void stopEglLocked() {
+ if (mHaveEgl) {
+ mHaveEgl = false;
+ mEglHelper.destroySurface();
+ mEglHelper.finish();
+ sGLThreadManager.releaseEglSurfaceLocked(this);
+ }
+ }
+
+ public boolean SwapBuffers() {
+ try {
+
+ boolean createEglSurface = false;
+ boolean sizeChanged = false;
+ int w = 0;
+ int h = 0;
+ Runnable event = null;
+
+ synchronized (sGLThreadManager) {
+ while (true) {
+ if (mShouldExit) {
+ return false;
+ }
+
+ if (! mEventQueue.isEmpty()) {
+ event = mEventQueue.remove(0);
+ break;
+ }
+
+ // Do we need to release the EGL surface?
+ if (mHaveEgl && mPaused) {
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "releasing EGL surface because paused tid=" + getId());
+ }
+ stopEglLocked();
+ }
+
+ // Have we lost the surface view surface?
+ if ((! mHasSurface) && (! mWaitingForSurface)) {
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "noticed surfaceView surface lost tid=" + getId());
+ }
+ if (mHaveEgl) {
+ stopEglLocked();
+ }
+ mWaitingForSurface = true;
+ sGLThreadManager.notifyAll();
+ }
+
+ // Have we acquired the surface view surface?
+ if (mHasSurface && mWaitingForSurface) {
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "noticed surfaceView surface acquired tid=" + getId());
+ }
+ mWaitingForSurface = false;
+ sGLThreadManager.notifyAll();
+ }
+
+ // Ready to draw?
+ if ((!mPaused) && mHasSurface
+ && (mWidth > 0) && (mHeight > 0)
+ && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY))) {
+
+ // If we don't have an egl surface, try to acquire one.
+ if ((! mHaveEgl) && sGLThreadManager.tryAcquireEglSurfaceLocked(this)) {
+ mHaveEgl = true;
+ mEglHelper.start();
+ createEglSurface = true;
+ sizeChanged = true;
+ sGLThreadManager.notifyAll();
+ }
+
+ if (mHaveEgl) {
+ if (mSizeChanged) {
+ sizeChanged = true;
+ w = mWidth;
+ h = mHeight;
+ if (DRAW_TWICE_AFTER_SIZE_CHANGED) {
+ // We keep mRequestRender true so that we draw twice after the size changes.
+ // (Once because of mSizeChanged, the second time because of mRequestRender.)
+ // This forces the updated graphics onto the screen.
+ } else {
+ mRequestRender = false;
+ }
+ mSizeChanged = false;
+ } else {
+ mRequestRender = false;
+ }
+ sGLThreadManager.notifyAll();
+ break;
+ }
+ }
+
+ // By design, this is the only place in a GLThread thread where we wait().
+ if (LOG_THREADS) {
+ Log.i("GLThread", "waiting tid=" + getId());
+ }
+ sGLThreadManager.wait();
+ }
+ } // end of synchronized(sGLThreadManager)
+
+ if (event != null) {
+ event.run();
+ event = null;
+ }
+
+ if (createEglSurface) {
+ mGL = (GL10) mEglHelper.createSurface(getHolder());
+ if (LOG_RENDERER) {
+ Log.w("GLThread", "onSurfaceCreated");
+ }
+ mRenderer.onSurfaceCreated(mGL, mEglHelper.mEglConfig);
+ createEglSurface = false;
+ }
+
+ if (sizeChanged) {
+ if (LOG_RENDERER) {
+ Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")");
+ }
+ mRenderer.onSurfaceChanged(mGL, w, h);
+ sizeChanged = false;
+ }
+
+ if (LOG_RENDERER) {
+ Log.w("GLThread", "onDrawFrame");
+ }
+ if(!mEglHelper.swap()) {
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "egl surface lost tid=" + getId());
+ return false;
+ }
+ }
+
+ } catch (InterruptedException e) {
+ return false;
+ }
+ return true;
+ }
+
+ public void setRenderMode(int renderMode) {
+ if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) {
+ throw new IllegalArgumentException("renderMode");
+ }
+ synchronized(sGLThreadManager) {
+ mRenderMode = renderMode;
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ public int getRenderMode() {
+ synchronized(sGLThreadManager) {
+ return mRenderMode;
+ }
+ }
+
+ public void requestRender() {
+ synchronized(sGLThreadManager) {
+ mRequestRender = true;
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ public void surfaceCreated() {
+ synchronized(sGLThreadManager) {
+ if (LOG_THREADS) {
+ Log.i("GLThread", "surfaceCreated tid=" + getId());
+ }
+ mHasSurface = true;
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ public void surfaceDestroyed() {
+ synchronized(sGLThreadManager) {
+ if (LOG_THREADS) {
+ Log.i("GLThread", "surfaceDestroyed tid=" + getId());
+ }
+ mHasSurface = false;
+ sGLThreadManager.notifyAll();
+ while((!mWaitingForSurface) && (!mExited)) {
+ try {
+ sGLThreadManager.wait();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ public void onPause() {
+ synchronized (sGLThreadManager) {
+ mPaused = true;
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ public void onResume() {
+ synchronized (sGLThreadManager) {
+ mPaused = false;
+ mRequestRender = true;
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ public void onWindowResize(int w, int h) {
+ synchronized (sGLThreadManager) {
+ mWidth = w;
+ mHeight = h;
+ mSizeChanged = true;
+ mRequestRender = true;
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ public void requestExitAndWait() {
+ // don't call this from GLThread thread or it is a guaranteed
+ // deadlock!
+ synchronized(sGLThreadManager) {
+ mShouldExit = true;
+ sGLThreadManager.notifyAll();
+ while (! mExited) {
+ try {
+ sGLThreadManager.wait();
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ /**
+ * Queue an "event" to be run on the GL rendering thread.
+ * @param r the runnable to be run on the GL rendering thread.
+ */
+ public void queueEvent(Runnable r) {
+ if (r == null) {
+ throw new IllegalArgumentException("r must not be null");
+ }
+ synchronized(sGLThreadManager) {
+ mEventQueue.add(r);
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ // Once the thread is started, all accesses to the following member
+ // variables are protected by the sGLThreadManager monitor
+ private boolean mShouldExit;
+ private boolean mExited;
+ private boolean mPaused;
+ private boolean mHasSurface;
+ private boolean mWaitingForSurface;
+ private boolean mHaveEgl;
+ private int mWidth;
+ private int mHeight;
+ private int mRenderMode;
+ private boolean mRequestRender;
+ private ArrayList