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:
parent
a0df79a2ee
commit
85b0410084
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 )
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
Reference in New Issue
Block a user