android: handle pause/unpause of application + recreate EGL window and OpenGL context accordingly

git-svn-id: https://svn.code.sf.net/p/freeglut/code/trunk@1293 7f0cb862-5218-0410-a997-914c9d46530a
This commit is contained in:
beuc 2012-05-04 07:37:39 +00:00
parent a0df79a2ee
commit 85b0410084
5 changed files with 95 additions and 56 deletions

View File

@ -281,25 +281,7 @@ const GLfloat high_shininess[] = { 100.0f };
/* Program entry point */ /* Program entry point */
int void init_resources() {
main(int argc, char *argv[])
{
glutInitWindowSize(640,480);
glutInitWindowPosition(40,40);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_MULTISAMPLE);
glutCreateWindow("FreeGLUT Shapes");
glutReshapeFunc(resize);
glutDisplayFunc(display);
glutKeyboardFunc(key);
glutSpecialFunc(special);
glutIdleFunc(idle);
glutMouseFunc(onMouseClick);
glutSetOption ( GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION ) ;
glClearColor(1,1,1,1); glClearColor(1,1,1,1);
glEnable(GL_CULL_FACE); glEnable(GL_CULL_FACE);
glCullFace(GL_BACK); glCullFace(GL_BACK);
@ -320,7 +302,28 @@ main(int argc, char *argv[])
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, high_shininess); glMaterialfv(GL_FRONT, GL_SHININESS, high_shininess);
}
int
main(int argc, char *argv[])
{
glutInitWindowSize(640,480);
glutInitWindowPosition(40,40);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_MULTISAMPLE);
glutCreateWindow("FreeGLUT Shapes");
glutReshapeFunc(resize);
glutDisplayFunc(display);
glutKeyboardFunc(key);
glutSpecialFunc(special);
glutIdleFunc(idle);
glutMouseFunc(onMouseClick);
glutSetOption ( GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION ) ;
init_resources();
glutMainLoop(); glutMainLoop();
#ifdef _MSC_VER #ifdef _MSC_VER

View File

@ -34,10 +34,12 @@
/* -- GLOBAL TYPE DEFINITIONS ---------------------------------------------- */ /* -- GLOBAL TYPE DEFINITIONS ---------------------------------------------- */
/* The structure used by display initialization in freeglut_init.c */ /* The structure used by display initialization in freeglut_init.c */
typedef struct tagSFG_PlatformDisplay SFG_PlatformDisplay; typedef struct tagSFG_PlatformDisplay SFG_PlatformDisplay;
struct android_app;
struct tagSFG_PlatformDisplay struct tagSFG_PlatformDisplay
{ {
struct tagSFG_PlatformDisplayEGL egl; struct tagSFG_PlatformDisplayEGL egl;
struct tagSFG_Window* single_window; EGLNativeWindowType single_native_window;
struct android_app* app;
}; };
typedef struct tagSFG_PlatformContext SFG_PlatformContext; typedef struct tagSFG_PlatformContext SFG_PlatformContext;

View File

@ -29,6 +29,7 @@
#include <GL/freeglut.h> #include <GL/freeglut.h>
#include "fg_internal.h" #include "fg_internal.h"
#include "fg_main.h" #include "fg_main.h"
#include "egl/fg_window_egl.h"
#include <android/log.h> #include <android/log.h>
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "FreeGLUT", __VA_ARGS__)) #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "FreeGLUT", __VA_ARGS__))
@ -180,7 +181,9 @@ void fgPlatformSleepForEvents( long msec )
* Process the next input event. * Process the next input event.
*/ */
int32_t handle_input(struct android_app* app, AInputEvent* event) { int32_t handle_input(struct android_app* app, AInputEvent* event) {
SFG_Window* window = fgStructure.CurrentWindow; SFG_Window* window = fgWindowByHandle(app->window);
if (window == NULL)
return EVENT_NOT_HANDLED;
/* FIXME: in Android, when key is repeated, down and up events /* FIXME: in Android, when key is repeated, down and up events
happen most often at the exact same time. This makes it happen most often at the exact same time. This makes it
@ -302,6 +305,7 @@ int32_t handle_input(struct android_app* app, AInputEvent* event) {
* Process the next main command. * Process the next main command.
*/ */
void handle_cmd(struct android_app* app, int32_t cmd) { void handle_cmd(struct android_app* app, int32_t cmd) {
SFG_Window* window = fgWindowByHandle(app->window); /* may be NULL */
switch (cmd) { switch (cmd) {
/* App life cycle, in that order: */ /* App life cycle, in that order: */
case APP_CMD_START: case APP_CMD_START:
@ -309,15 +313,12 @@ void handle_cmd(struct android_app* app, int32_t cmd) {
break; break;
case APP_CMD_RESUME: case APP_CMD_RESUME:
LOGI("handle_cmd: APP_CMD_RESUME"); LOGI("handle_cmd: APP_CMD_RESUME");
/* If coming back from a pause: */ /* Cf. fgPlatformProcessSingleEvent */
/* - Recreate window context and surface */
/* - Call user-defined hook to restore resources (textures...) */
/* - Unpause GLUT callbacks */
break; break;
case APP_CMD_INIT_WINDOW: /* surfaceCreated */ case APP_CMD_INIT_WINDOW: /* surfaceCreated */
/* The window is being shown, get it ready. */ /* The window is being shown, get it ready. */
LOGI("handle_cmd: APP_CMD_INIT_WINDOW"); LOGI("handle_cmd: APP_CMD_INIT_WINDOW");
fgDisplay.pDisplay.single_window->Window.Handle = app->window; fgDisplay.pDisplay.single_native_window = app->window;
/* glPlatformOpenWindow was waiting for Handle to be defined and /* glPlatformOpenWindow was waiting for Handle to be defined and
will now return from fgPlatformProcessSingleEvent() */ will now return from fgPlatformProcessSingleEvent() */
break; break;
@ -326,7 +327,7 @@ void handle_cmd(struct android_app* app, int32_t cmd) {
break; break;
case APP_CMD_WINDOW_RESIZED: case APP_CMD_WINDOW_RESIZED:
LOGI("handle_cmd: APP_CMD_WINDOW_RESIZED"); LOGI("handle_cmd: APP_CMD_WINDOW_RESIZED");
if (fgDisplay.pDisplay.single_window->Window.pContext.egl.Surface != EGL_NO_SURFACE) if (window->Window.pContext.egl.Surface != EGL_NO_SURFACE)
/* Make ProcessSingleEvent detect the new size, only available /* Make ProcessSingleEvent detect the new size, only available
after the next SwapBuffer */ after the next SwapBuffer */
glutPostRedisplay(); glutPostRedisplay();
@ -335,23 +336,22 @@ void handle_cmd(struct android_app* app, int32_t cmd) {
case APP_CMD_SAVE_STATE: /* onSaveInstanceState */ case APP_CMD_SAVE_STATE: /* onSaveInstanceState */
/* The system has asked us to save our current state, when it /* The system has asked us to save our current state, when it
pauses the application without destroying it right after. */ pauses the application without destroying it right after. */
/* app->savedState = ... */ app->savedState = strdup("Detect me as non-NULL on next android_main");
/* app->savedStateSize = ... */ app->savedStateSize = strlen(app->savedState) + 1;
LOGI("handle_cmd: APP_CMD_SAVE_STATE"); LOGI("handle_cmd: APP_CMD_SAVE_STATE");
break; break;
case APP_CMD_PAUSE: case APP_CMD_PAUSE:
LOGI("handle_cmd: APP_CMD_PAUSE"); LOGI("handle_cmd: APP_CMD_PAUSE");
/* - Pause GLUT callbacks */ /* Cf. fgPlatformProcessSingleEvent */
break; break;
case APP_CMD_LOST_FOCUS: case APP_CMD_LOST_FOCUS:
LOGI("handle_cmd: APP_CMD_LOST_FOCUS"); LOGI("handle_cmd: APP_CMD_LOST_FOCUS");
break; break;
case APP_CMD_TERM_WINDOW: /* surfaceDestroyed */ case APP_CMD_TERM_WINDOW: /* surfaceDestroyed */
/* The application is being hidden, but may be restored */ /* The application is being hidden, but may be restored */
/* TODO: Pausing/resuming windows not ready yet, so killing it now */
fgDestroyWindow(fgDisplay.pDisplay.single_window);
fgDisplay.pDisplay.single_window = NULL;
LOGI("handle_cmd: APP_CMD_TERM_WINDOW"); LOGI("handle_cmd: APP_CMD_TERM_WINDOW");
fghPlatformCloseWindowEGL(window);
fgDisplay.pDisplay.single_native_window = NULL;
break; break;
case APP_CMD_STOP: case APP_CMD_STOP:
LOGI("handle_cmd: APP_CMD_STOP"); LOGI("handle_cmd: APP_CMD_STOP");
@ -359,9 +359,14 @@ void handle_cmd(struct android_app* app, int32_t cmd) {
case APP_CMD_DESTROY: /* Activity.onDestroy */ case APP_CMD_DESTROY: /* Activity.onDestroy */
LOGI("handle_cmd: APP_CMD_DESTROY"); LOGI("handle_cmd: APP_CMD_DESTROY");
/* User closed the application for good, let's kill the window */ /* User closed the application for good, let's kill the window */
if (fgDisplay.pDisplay.single_window != NULL) { {
fgDestroyWindow(fgDisplay.pDisplay.single_window); /* Can't use fgWindowByHandle as app->window is NULL */
fgDisplay.pDisplay.single_window = NULL; SFG_Window* window = fgStructure.CurrentWindow;
if (window != NULL) {
fgDestroyWindow(window);
} else {
LOGI("APP_CMD_DESTROY: No current window");
}
} }
/* glue has already set android_app->destroyRequested=1 */ /* glue has already set android_app->destroyRequested=1 */
break; break;
@ -378,6 +383,11 @@ void handle_cmd(struct android_app* app, int32_t cmd) {
} }
} }
void fgPlatformOpenWindow( SFG_Window* window, const char* title,
GLboolean positionUse, int x, int y,
GLboolean sizeUse, int w, int h,
GLboolean gameMode, GLboolean isSubWindow );
void fgPlatformProcessSingleEvent ( void ) void fgPlatformProcessSingleEvent ( void )
{ {
/* When the screen is resized, the window handle still points to the /* When the screen is resized, the window handle still points to the
@ -389,7 +399,7 @@ void fgPlatformProcessSingleEvent ( void )
/* Interestingly, on a Samsung Galaxy S/PowerVR SGX540 GPU/Android /* Interestingly, on a Samsung Galaxy S/PowerVR SGX540 GPU/Android
2.3, that next SwapBuffer is fake (but still necessary to get the 2.3, that next SwapBuffer is fake (but still necessary to get the
new size). */ new size). */
SFG_Window* window = fgDisplay.pDisplay.single_window; SFG_Window* window = fgStructure.CurrentWindow;
if (window != NULL && window->Window.Handle != NULL) { if (window != NULL && window->Window.Handle != NULL) {
int32_t width = ANativeWindow_getWidth(window->Window.Handle); int32_t width = ANativeWindow_getWidth(window->Window.Handle);
int32_t height = ANativeWindow_getHeight(window->Window.Handle); int32_t height = ANativeWindow_getHeight(window->Window.Handle);
@ -420,6 +430,32 @@ void fgPlatformProcessSingleEvent ( void )
source->process(source->app, source); source->process(source->app, source);
} }
} }
/* If we're not in RESUME state, Android paused us, so wait */
struct android_app* app = fgDisplay.pDisplay.app;
if (app->destroyRequested != 1 && app->activityState != APP_CMD_RESUME) {
int FOREVER = -1;
while (app->destroyRequested != 1 && (app->activityState != APP_CMD_RESUME)) {
if ((ident=ALooper_pollOnce(FOREVER, NULL, &events, (void**)&source)) >= 0) {
/* Process this event. */
if (source != NULL) {
source->process(source->app, source);
}
}
}
/* If coming back from a pause: */
/* - Recreate window context and surface */
/* - Call user-defined hook to restore resources (textures...) */
/* - Exit pause looop */
if (app->destroyRequested != 1) {
/* Android is full-screen only, simplified call.. */
/* Ideally we'd have a fgPlatformReopenWindow() */
fgPlatformOpenWindow(window, "", GL_FALSE, 0, 0, GL_FALSE, 0, 0, GL_FALSE, GL_FALSE);
/* TODO: INVOKE_WCB(*window, Pause?); */
/* TODO: INVOKE_WCB(*window, LoadResources/ContextLost/...?); */
/* TODO: INVOKE_WCB(*window, Resume?); */
}
}
} }
void fgPlatformMainLoopPreliminaryWork ( void ) void fgPlatformMainLoopPreliminaryWork ( void )

View File

@ -138,7 +138,7 @@ static void extract_assets(struct android_app* app) {
* event loop for receiving input events and doing other things. * event loop for receiving input events and doing other things.
*/ */
void android_main(struct android_app* app) { void android_main(struct android_app* app) {
LOGI("android_main"); LOGI("android_main savedState=%p", app->savedState);
/* Register window resize callback */ /* Register window resize callback */
app->activity->callbacks->onNativeWindowResized = onNativeWindowResized; app->activity->callbacks->onNativeWindowResized = onNativeWindowResized;
@ -154,6 +154,7 @@ void android_main(struct android_app* app) {
{ {
char progname[5] = "self"; char progname[5] = "self";
char* argv[] = {progname, NULL}; char* argv[] = {progname, NULL};
fgDisplay.pDisplay.app = app;
main(1, argv); main(1, argv);
/* FreeGLUT will exit() by itself if /* FreeGLUT will exit() by itself if
GLUT_ACTION_ON_WINDOW_CLOSE == GLUT_ACTION_EXIT */ GLUT_ACTION_ON_WINDOW_CLOSE == GLUT_ACTION_EXIT */
@ -161,14 +162,6 @@ void android_main(struct android_app* app) {
LOGI("android_main: end"); LOGI("android_main: end");
/* TODO: Pausing/resuming windows not ready yet, so exiting now */
exit(0);
/* Finish processing all events (namely APP_CMD_DESTROY) before
exiting thread */
while (!app->destroyRequested)
fgPlatformProcessSingleEvent();
/* Let NativeActivity restart us */ /* Let NativeActivity restart us */
/* Users may want to forcibly exit() in their main() anyway because /* Users may want to forcibly exit() in their main() anyway because
NativeActivity doesn't dlclose() us, so all statically-assigned NativeActivity doesn't dlclose() us, so all statically-assigned

View File

@ -30,7 +30,7 @@
#include <GL/freeglut.h> #include <GL/freeglut.h>
#include "fg_internal.h" #include "fg_internal.h"
#include "egl/fg_window_egl.h" #include "egl/fg_window_egl.h"
#include "android/fg_main_android.h" #include <android/native_app_glue/android_native_app_glue.h>
/* /*
* Opens a window. Requires a SFG_Window object created and attached * Opens a window. Requires a SFG_Window object created and attached
@ -42,27 +42,32 @@ void fgPlatformOpenWindow( SFG_Window* window, const char* title,
GLboolean gameMode, GLboolean isSubWindow ) GLboolean gameMode, GLboolean isSubWindow )
{ {
/* TODO: only one full-screen window possible? */ /* TODO: only one full-screen window possible? */
if (fgDisplay.pDisplay.single_window == NULL) { if (fgDisplay.pDisplay.single_native_window != NULL) {
fgDisplay.pDisplay.single_window = window;
} else {
fgWarning("You can't have more than one window on Android"); fgWarning("You can't have more than one window on Android");
return; return;
} }
fghChooseConfig(&window->Window.pContext.egl.Config); /* First, wait until Activity surface is available */
window->Window.Context = fghCreateNewContextEGL(window);
/* Wait until window is available and OpenGL context is created */
/* Normally events are processed through glutMainLoop(), but the /* Normally events are processed through glutMainLoop(), but the
user didn't call it yet, and the Android may not have initialized user didn't call it yet, and the Android may not have initialized
the View yet. So we need to wait for that to happen. */ the View yet. So we need to wait for that to happen. */
/* We can't return from this function before the OpenGL context is /* We can't return from this function before the OpenGL context is
properly made current with a valid surface. So we wait for the properly made current with a valid surface. So we wait for the
surface. */ surface. */
while (fgDisplay.pDisplay.single_window->Window.Handle == NULL) { while (fgDisplay.pDisplay.single_native_window == NULL) {
/* APP_CMD_INIT_WINDOW will do the job */ /* APP_CMD_INIT_WINDOW will do the job */
fgPlatformProcessSingleEvent(); int ident;
int events;
struct android_poll_source* source;
if ((ident=ALooper_pollOnce(0, NULL, &events, (void**)&source)) >= 0)
if (source != NULL) source->process(source->app, source);
/* fgPlatformProcessSingleEvent(); */
} }
window->Window.Handle = fgDisplay.pDisplay.single_native_window;
/* Create context */
fghChooseConfig(&window->Window.pContext.egl.Config);
window->Window.Context = fghCreateNewContextEGL(window);
EGLDisplay display = fgDisplay.pDisplay.egl.Display; EGLDisplay display = fgDisplay.pDisplay.egl.Display;