android: handle pause/unpause of application + recreate EGL window and OpenGL context...
authorSylvain Beucler <beuc@beuc.net>
Fri, 4 May 2012 07:37:39 +0000 (07:37 +0000)
committerSylvain Beucler <beuc@beuc.net>
Fri, 4 May 2012 07:37:39 +0000 (07:37 +0000)
git-svn-id: svn+ssh://svn.code.sf.net/p/freeglut/code/trunk/freeglut/freeglut@1293 7f0cb862-5218-0410-a997-914c9d46530a

progs/test-shapes-gles1/test-shapes-gles1.c
src/android/fg_internal_android.h
src/android/fg_main_android.c
src/android/fg_runtime_android.c
src/android/fg_window_android.c

index 56b3569..e4d9122 100644 (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
index c5d146e..0feaad6 100644 (file)
 /* -- 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;
index 52700fe..54bb583 100644 (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 )
index b5d6cad..c92d5f1 100644 (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
index a6ce201..5d1bc19 100644 (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;