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 */
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 ) ;
void init_resources() {
glClearColor(1,1,1,1);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
@ -320,7 +302,28 @@ main(int argc, char *argv[])
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
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();
#ifdef _MSC_VER

View File

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

View File

@ -29,6 +29,7 @@
#include <GL/freeglut.h>
#include "fg_internal.h"
#include "fg_main.h"
#include "egl/fg_window_egl.h"
#include <android/log.h>
#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.
*/
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
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.
*/
void handle_cmd(struct android_app* app, int32_t cmd) {
SFG_Window* window = fgWindowByHandle(app->window); /* may be NULL */
switch (cmd) {
/* App life cycle, in that order: */
case APP_CMD_START:
@ -309,15 +313,12 @@ void handle_cmd(struct android_app* app, int32_t cmd) {
break;
case APP_CMD_RESUME:
LOGI("handle_cmd: APP_CMD_RESUME");
/* If coming back from a pause: */
/* - Recreate window context and surface */
/* - Call user-defined hook to restore resources (textures...) */
/* - Unpause GLUT callbacks */
/* Cf. fgPlatformProcessSingleEvent */
break;
case APP_CMD_INIT_WINDOW: /* surfaceCreated */
/* The window is being shown, get it ready. */
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
will now return from fgPlatformProcessSingleEvent() */
break;
@ -326,7 +327,7 @@ void handle_cmd(struct android_app* app, int32_t cmd) {
break;
case 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
after the next SwapBuffer */
glutPostRedisplay();
@ -335,23 +336,22 @@ void handle_cmd(struct android_app* app, int32_t cmd) {
case APP_CMD_SAVE_STATE: /* onSaveInstanceState */
/* The system has asked us to save our current state, when it
pauses the application without destroying it right after. */
/* app->savedState = ... */
/* app->savedStateSize = ... */
app->savedState = strdup("Detect me as non-NULL on next android_main");
app->savedStateSize = strlen(app->savedState) + 1;
LOGI("handle_cmd: APP_CMD_SAVE_STATE");
break;
case APP_CMD_PAUSE:
LOGI("handle_cmd: APP_CMD_PAUSE");
/* - Pause GLUT callbacks */
/* Cf. fgPlatformProcessSingleEvent */
break;
case APP_CMD_LOST_FOCUS:
LOGI("handle_cmd: APP_CMD_LOST_FOCUS");
break;
case APP_CMD_TERM_WINDOW: /* surfaceDestroyed */
/* 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");
fghPlatformCloseWindowEGL(window);
fgDisplay.pDisplay.single_native_window = NULL;
break;
case 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 */
LOGI("handle_cmd: APP_CMD_DESTROY");
/* User closed the application for good, let's kill the window */
if (fgDisplay.pDisplay.single_window != NULL) {
fgDestroyWindow(fgDisplay.pDisplay.single_window);
fgDisplay.pDisplay.single_window = NULL;
{
/* Can't use fgWindowByHandle as app->window is 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 */
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 )
{
/* 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
2.3, that next SwapBuffer is fake (but still necessary to get the
new size). */
SFG_Window* window = fgDisplay.pDisplay.single_window;
SFG_Window* window = fgStructure.CurrentWindow;
if (window != NULL && window->Window.Handle != NULL) {
int32_t width = ANativeWindow_getWidth(window->Window.Handle);
int32_t height = ANativeWindow_getHeight(window->Window.Handle);
@ -420,6 +430,32 @@ void fgPlatformProcessSingleEvent ( void )
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 )

View File

@ -138,7 +138,7 @@ static void extract_assets(struct android_app* app) {
* event loop for receiving input events and doing other things.
*/
void android_main(struct android_app* app) {
LOGI("android_main");
LOGI("android_main savedState=%p", app->savedState);
/* Register window resize callback */
app->activity->callbacks->onNativeWindowResized = onNativeWindowResized;
@ -154,6 +154,7 @@ void android_main(struct android_app* app) {
{
char progname[5] = "self";
char* argv[] = {progname, NULL};
fgDisplay.pDisplay.app = app;
main(1, argv);
/* FreeGLUT will exit() by itself if
GLUT_ACTION_ON_WINDOW_CLOSE == GLUT_ACTION_EXIT */
@ -161,14 +162,6 @@ void android_main(struct android_app* app) {
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 */
/* Users may want to forcibly exit() in their main() anyway because
NativeActivity doesn't dlclose() us, so all statically-assigned

View File

@ -30,7 +30,7 @@
#include <GL/freeglut.h>
#include "fg_internal.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
@ -42,27 +42,32 @@ void fgPlatformOpenWindow( SFG_Window* window, const char* title,
GLboolean gameMode, GLboolean isSubWindow )
{
/* TODO: only one full-screen window possible? */
if (fgDisplay.pDisplay.single_window == NULL) {
fgDisplay.pDisplay.single_window = window;
} else {
if (fgDisplay.pDisplay.single_native_window != NULL) {
fgWarning("You can't have more than one window on Android");
return;
}
fghChooseConfig(&window->Window.pContext.egl.Config);
window->Window.Context = fghCreateNewContextEGL(window);
/* Wait until window is available and OpenGL context is created */
/* First, wait until Activity surface is available */
/* Normally events are processed through glutMainLoop(), but the
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. */
/* We can't return from this function before the OpenGL context is
properly made current with a valid surface. So we wait for the
surface. */
while (fgDisplay.pDisplay.single_window->Window.Handle == NULL) {
while (fgDisplay.pDisplay.single_native_window == NULL) {
/* 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;