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 */
|
/* 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
|
||||||
|
@ -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;
|
||||||
|
@ -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 )
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user