Fixed bug 1293 - [Android] Support Pause/Resume
authorSam Lantinga <slouken@libsdl.org>
Sun, 08 Jan 2012 01:05:25 -0500
changeset 6186e565ac981de6
parent 6185 b91e7565e877
child 6187 6b1932e5a36b
Fixed bug 1293 - [Android] Support Pause/Resume

Gabriel Jacobo 2011-12-23 12:55:11 PST

The attached files provide some improvement over the current handling of
pause/resume in Android.
- I disabled the exit(status) instruction in SDL_main as that makes the entire
app instead of the SDL thread exit (while not needed for pause/resume it is
needed for Live Wallpapers, an SDLActivity for which I'll upload in a separate
bug).
- Added nativePause and nativeResume which basically just mark the window as
visible/hidden, something that the end user needs to take into consideration
(ideally pausing the event loop).

Also, this arrangement creates a new GL context when needed, which at least in
my test system is every time you go away from the app and come back to it. So,
this means that the textures need to be generated again after resuming (a
problem the end user didn't have before because the app exited completely when
it should've been pausing). I'd like to know if there's a standard way of
letting the user know that the GL context has changed and that he needs to
refresh his textures, or if this is out of the scope of the library and each
user handles it in their own way (I don't know how/if this same thing is
handled in the iPhone backend, but it would be wise to try to imitate that).

Gabriel Jacobo 2011-12-23 12:57:10 PST
Also, in the SDLActivity the EGL handling code is moved up to the Activity from
the Surface code, as I think it is possible (in theory) that the surface is
destroyed temporarily while the context remains alive (though in practice in my
test system this is not the case)
android-project/src/org/libsdl/app/SDLActivity.java
src/core/android/SDL_android.cpp
src/main/android/SDL_android_main.cpp
     1.1 --- a/android-project/src/org/libsdl/app/SDLActivity.java	Sun Jan 08 00:39:41 2012 -0500
     1.2 +++ b/android-project/src/org/libsdl/app/SDLActivity.java	Sun Jan 08 01:05:25 2012 -0500
     1.3 @@ -30,10 +30,20 @@
     1.4      private static SDLActivity mSingleton;
     1.5      private static SDLSurface mSurface;
     1.6  
     1.7 +    // This is what SDL runs in. It invokes SDL_main(), eventually
     1.8 +    private static Thread mSDLThread;
     1.9 +
    1.10      // Audio
    1.11      private static Thread mAudioThread;
    1.12      private static AudioTrack mAudioTrack;
    1.13  
    1.14 +    // EGL private objects
    1.15 +    private static EGLContext  mEGLContext;
    1.16 +    private static EGLSurface  mEGLSurface;
    1.17 +    private static EGLDisplay  mEGLDisplay;
    1.18 +    private static EGLConfig   mEGLConfig;
    1.19 +    private static int mGLMajor, mGLMinor;
    1.20 +
    1.21      // Load the .so
    1.22      static {
    1.23          System.loadLibrary("SDL");
    1.24 @@ -55,18 +65,38 @@
    1.25          mSurface = new SDLSurface(getApplication());
    1.26          setContentView(mSurface);
    1.27          SurfaceHolder holder = mSurface.getHolder();
    1.28 -        holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
    1.29      }
    1.30  
    1.31      // Events
    1.32      protected void onPause() {
    1.33 -        //Log.v("SDL", "onPause()");
    1.34 +        Log.v("SDL", "onPause()");
    1.35          super.onPause();
    1.36 +        SDLActivity.nativePause();
    1.37      }
    1.38  
    1.39      protected void onResume() {
    1.40 -        //Log.v("SDL", "onResume()");
    1.41 +        Log.v("SDL", "onResume()");
    1.42          super.onResume();
    1.43 +        SDLActivity.nativeResume();
    1.44 +    }
    1.45 +
    1.46 +    protected void onDestroy() {
    1.47 +        super.onDestroy();
    1.48 +        Log.v("SDL", "onDestroy()");
    1.49 +        // Send a quit message to the application
    1.50 +        SDLActivity.nativeQuit();
    1.51 +
    1.52 +        // Now wait for the SDL thread to quit
    1.53 +        if (mSDLThread != null) {
    1.54 +            try {
    1.55 +                mSDLThread.join();
    1.56 +            } catch(Exception e) {
    1.57 +                Log.v("SDL", "Problem stopping thread: " + e);
    1.58 +            }
    1.59 +            mSDLThread = null;
    1.60 +
    1.61 +            //Log.v("SDL", "Finished waiting for SDL thread");
    1.62 +        }
    1.63      }
    1.64  
    1.65      // Messages from the SDLMain thread
    1.66 @@ -92,6 +122,8 @@
    1.67      // C functions we call
    1.68      public static native void nativeInit();
    1.69      public static native void nativeQuit();
    1.70 +    public static native void nativePause();
    1.71 +    public static native void nativeResume();
    1.72      public static native void onNativeResize(int x, int y, int format);
    1.73      public static native void onNativeKeyDown(int keycode);
    1.74      public static native void onNativeKeyUp(int keycode);
    1.75 @@ -105,11 +137,11 @@
    1.76      // Java functions called from C
    1.77  
    1.78      public static boolean createGLContext(int majorVersion, int minorVersion) {
    1.79 -        return mSurface.initEGL(majorVersion, minorVersion);
    1.80 +        return initEGL(majorVersion, minorVersion);
    1.81      }
    1.82  
    1.83      public static void flipBuffers() {
    1.84 -        mSurface.flipEGL();
    1.85 +        flipEGL();
    1.86      }
    1.87  
    1.88      public static void setActivityTitle(String title) {
    1.89 @@ -121,6 +153,138 @@
    1.90          return mSingleton;
    1.91      }
    1.92  
    1.93 +    public static void startApp() {
    1.94 +        // Start up the C app thread
    1.95 +        if (mSDLThread == null) {
    1.96 +            mSDLThread = new Thread(new SDLMain(), "SDLThread");
    1.97 +            mSDLThread.start();
    1.98 +        }
    1.99 +        else {
   1.100 +            SDLActivity.nativeResume();
   1.101 +        }
   1.102 +    }
   1.103 +
   1.104 +    // EGL functions
   1.105 +    public static boolean initEGL(int majorVersion, int minorVersion) {
   1.106 +        if (SDLActivity.mEGLDisplay == null) {
   1.107 +            //Log.v("SDL", "Starting up OpenGL ES " + majorVersion + "." + minorVersion);
   1.108 +
   1.109 +            try {
   1.110 +                EGL10 egl = (EGL10)EGLContext.getEGL();
   1.111 +
   1.112 +                EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
   1.113 +
   1.114 +                int[] version = new int[2];
   1.115 +                egl.eglInitialize(dpy, version);
   1.116 +
   1.117 +                int EGL_OPENGL_ES_BIT = 1;
   1.118 +                int EGL_OPENGL_ES2_BIT = 4;
   1.119 +                int renderableType = 0;
   1.120 +                if (majorVersion == 2) {
   1.121 +                    renderableType = EGL_OPENGL_ES2_BIT;
   1.122 +                } else if (majorVersion == 1) {
   1.123 +                    renderableType = EGL_OPENGL_ES_BIT;
   1.124 +                }
   1.125 +                int[] configSpec = {
   1.126 +                    //EGL10.EGL_DEPTH_SIZE,   16,
   1.127 +                    EGL10.EGL_RENDERABLE_TYPE, renderableType,
   1.128 +                    EGL10.EGL_NONE
   1.129 +                };
   1.130 +                EGLConfig[] configs = new EGLConfig[1];
   1.131 +                int[] num_config = new int[1];
   1.132 +                if (!egl.eglChooseConfig(dpy, configSpec, configs, 1, num_config) || num_config[0] == 0) {
   1.133 +                    Log.e("SDL", "No EGL config available");
   1.134 +                    return false;
   1.135 +                }
   1.136 +                EGLConfig config = configs[0];
   1.137 +
   1.138 +                /*int EGL_CONTEXT_CLIENT_VERSION=0x3098;
   1.139 +                int contextAttrs[] = new int[] { EGL_CONTEXT_CLIENT_VERSION, majorVersion, EGL10.EGL_NONE };
   1.140 +                EGLContext ctx = egl.eglCreateContext(dpy, config, EGL10.EGL_NO_CONTEXT, contextAttrs);
   1.141 +
   1.142 +                if (ctx == EGL10.EGL_NO_CONTEXT) {
   1.143 +                    Log.e("SDL", "Couldn't create context");
   1.144 +                    return false;
   1.145 +                }
   1.146 +                SDLActivity.mEGLContext = ctx;*/
   1.147 +                SDLActivity.mEGLDisplay = dpy;
   1.148 +                SDLActivity.mEGLConfig = config;
   1.149 +                SDLActivity.mGLMajor = majorVersion;
   1.150 +                SDLActivity.mGLMinor = minorVersion;
   1.151 +
   1.152 +                SDLActivity.createEGLSurface();
   1.153 +            } catch(Exception e) {
   1.154 +                Log.v("SDL", e + "");
   1.155 +                for (StackTraceElement s : e.getStackTrace()) {
   1.156 +                    Log.v("SDL", s.toString());
   1.157 +                }
   1.158 +            }
   1.159 +        }
   1.160 +        else SDLActivity.createEGLSurface();
   1.161 +
   1.162 +        return true;
   1.163 +    }
   1.164 +
   1.165 +    public static boolean createEGLContext() {
   1.166 +        EGL10 egl = (EGL10)EGLContext.getEGL();
   1.167 +        int EGL_CONTEXT_CLIENT_VERSION=0x3098;
   1.168 +        int contextAttrs[] = new int[] { EGL_CONTEXT_CLIENT_VERSION, SDLActivity.mGLMajor, EGL10.EGL_NONE };
   1.169 +        SDLActivity.mEGLContext = egl.eglCreateContext(SDLActivity.mEGLDisplay, SDLActivity.mEGLConfig, EGL10.EGL_NO_CONTEXT, contextAttrs);
   1.170 +        if (SDLActivity.mEGLContext == EGL10.EGL_NO_CONTEXT) {
   1.171 +            Log.e("SDL", "Couldn't create context");
   1.172 +            return false;
   1.173 +        }
   1.174 +        return true;
   1.175 +    }
   1.176 +
   1.177 +    public static boolean createEGLSurface() {
   1.178 +        if (SDLActivity.mEGLDisplay != null && SDLActivity.mEGLConfig != null) {
   1.179 +            EGL10 egl = (EGL10)EGLContext.getEGL();
   1.180 +            if (SDLActivity.mEGLContext == null) createEGLContext();
   1.181 +
   1.182 +            Log.v("SDL", "Creating new EGL Surface");
   1.183 +            EGLSurface surface = egl.eglCreateWindowSurface(SDLActivity.mEGLDisplay, SDLActivity.mEGLConfig, SDLActivity.mSurface, null);
   1.184 +            if (surface == EGL10.EGL_NO_SURFACE) {
   1.185 +                Log.e("SDL", "Couldn't create surface");
   1.186 +                return false;
   1.187 +            }
   1.188 +
   1.189 +            if (!egl.eglMakeCurrent(SDLActivity.mEGLDisplay, surface, surface, SDLActivity.mEGLContext)) {
   1.190 +                Log.e("SDL", "Old EGL Context doesnt work, trying with a new one");
   1.191 +                createEGLContext();
   1.192 +                if (!egl.eglMakeCurrent(SDLActivity.mEGLDisplay, surface, surface, SDLActivity.mEGLContext)) {
   1.193 +                    Log.e("SDL", "Failed making EGL Context current");
   1.194 +                    return false;
   1.195 +                }
   1.196 +            }
   1.197 +            SDLActivity.mEGLSurface = surface;
   1.198 +            return true;
   1.199 +        }
   1.200 +        return false;
   1.201 +    }
   1.202 +
   1.203 +    // EGL buffer flip
   1.204 +    public static void flipEGL() {
   1.205 +        try {
   1.206 +            EGL10 egl = (EGL10)EGLContext.getEGL();
   1.207 +
   1.208 +            egl.eglWaitNative(EGL10.EGL_CORE_NATIVE_ENGINE, null);
   1.209 +
   1.210 +            // drawing here
   1.211 +
   1.212 +            egl.eglWaitGL();
   1.213 +
   1.214 +            egl.eglSwapBuffers(SDLActivity.mEGLDisplay, SDLActivity.mEGLSurface);
   1.215 +
   1.216 +
   1.217 +        } catch(Exception e) {
   1.218 +            Log.v("SDL", "flipEGL(): " + e);
   1.219 +            for (StackTraceElement s : e.getStackTrace()) {
   1.220 +                Log.v("SDL", s.toString());
   1.221 +            }
   1.222 +        }
   1.223 +    }
   1.224 +
   1.225      // Audio
   1.226      private static Object buf;
   1.227      
   1.228 @@ -241,14 +405,6 @@
   1.229  class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, 
   1.230      View.OnKeyListener, View.OnTouchListener, SensorEventListener  {
   1.231  
   1.232 -    // This is what SDL runs in. It invokes SDL_main(), eventually
   1.233 -    private Thread mSDLThread;    
   1.234 -    
   1.235 -    // EGL private objects
   1.236 -    private EGLContext  mEGLContext;
   1.237 -    private EGLSurface  mEGLSurface;
   1.238 -    private EGLDisplay  mEGLDisplay;
   1.239 -
   1.240      // Sensors
   1.241      private static SensorManager mSensorManager;
   1.242  
   1.243 @@ -268,37 +424,23 @@
   1.244  
   1.245      // Called when we have a valid drawing surface
   1.246      public void surfaceCreated(SurfaceHolder holder) {
   1.247 -        //Log.v("SDL", "surfaceCreated()");
   1.248 -
   1.249 +        Log.v("SDL", "surfaceCreated()");
   1.250 +        holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
   1.251 +        SDLActivity.createEGLSurface();
   1.252          enableSensor(Sensor.TYPE_ACCELEROMETER, true);
   1.253      }
   1.254  
   1.255      // Called when we lose the surface
   1.256      public void surfaceDestroyed(SurfaceHolder holder) {
   1.257 -        //Log.v("SDL", "surfaceDestroyed()");
   1.258 -
   1.259 -        // Send a quit message to the application
   1.260 -        SDLActivity.nativeQuit();
   1.261 -
   1.262 -        // Now wait for the SDL thread to quit
   1.263 -        if (mSDLThread != null) {
   1.264 -            try {
   1.265 -                mSDLThread.join();
   1.266 -            } catch(Exception e) {
   1.267 -                Log.v("SDL", "Problem stopping thread: " + e);
   1.268 -            }
   1.269 -            mSDLThread = null;
   1.270 -
   1.271 -            //Log.v("SDL", "Finished waiting for SDL thread");
   1.272 -        }
   1.273 -
   1.274 +        Log.v("SDL", "surfaceDestroyed()");
   1.275 +        SDLActivity.nativePause();
   1.276          enableSensor(Sensor.TYPE_ACCELEROMETER, false);
   1.277      }
   1.278  
   1.279      // Called when the surface is resized
   1.280      public void surfaceChanged(SurfaceHolder holder,
   1.281                                 int format, int width, int height) {
   1.282 -        //Log.v("SDL", "surfaceChanged()");
   1.283 +        Log.v("SDL", "surfaceChanged()");
   1.284  
   1.285          int sdlFormat = 0x85151002; // SDL_PIXELFORMAT_RGB565 by default
   1.286          switch (format) {
   1.287 @@ -345,109 +487,16 @@
   1.288              break;
   1.289          }
   1.290          SDLActivity.onNativeResize(width, height, sdlFormat);
   1.291 +        Log.v("SDL", "Window size:" + width + "x"+height);
   1.292  
   1.293 -        // Now start up the C app thread
   1.294 -        if (mSDLThread == null) {
   1.295 -            mSDLThread = new Thread(new SDLMain(), "SDLThread"); 
   1.296 -            mSDLThread.start();       
   1.297 -        }
   1.298 +        SDLActivity.startApp();
   1.299      }
   1.300  
   1.301      // unused
   1.302      public void onDraw(Canvas canvas) {}
   1.303  
   1.304  
   1.305 -    // EGL functions
   1.306 -    public boolean initEGL(int majorVersion, int minorVersion) {
   1.307 -        Log.v("SDL", "Starting up OpenGL ES " + majorVersion + "." + minorVersion);
   1.308  
   1.309 -        try {
   1.310 -            EGL10 egl = (EGL10)EGLContext.getEGL();
   1.311 -
   1.312 -            EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
   1.313 -
   1.314 -            int[] version = new int[2];
   1.315 -            egl.eglInitialize(dpy, version);
   1.316 -
   1.317 -            int EGL_OPENGL_ES_BIT = 1;
   1.318 -            int EGL_OPENGL_ES2_BIT = 4;
   1.319 -            int renderableType = 0;
   1.320 -            if (majorVersion == 2) {
   1.321 -                renderableType = EGL_OPENGL_ES2_BIT;
   1.322 -            } else if (majorVersion == 1) {
   1.323 -                renderableType = EGL_OPENGL_ES_BIT;
   1.324 -            }
   1.325 -            int[] configSpec = {
   1.326 -                //EGL10.EGL_DEPTH_SIZE,   16,
   1.327 -                EGL10.EGL_RENDERABLE_TYPE, renderableType,
   1.328 -                EGL10.EGL_NONE
   1.329 -            };
   1.330 -            EGLConfig[] configs = new EGLConfig[1];
   1.331 -            int[] num_config = new int[1];
   1.332 -            if (!egl.eglChooseConfig(dpy, configSpec, configs, 1, num_config) || num_config[0] == 0) {
   1.333 -                Log.e("SDL", "No EGL config available");
   1.334 -                return false;
   1.335 -            }
   1.336 -            EGLConfig config = configs[0];
   1.337 -
   1.338 -            int EGL_CONTEXT_CLIENT_VERSION=0x3098;
   1.339 -            int contextAttrs[] = new int[]
   1.340 -            {
   1.341 -                EGL_CONTEXT_CLIENT_VERSION, majorVersion,
   1.342 -                EGL10.EGL_NONE
   1.343 -            }; 
   1.344 -            EGLContext ctx = egl.eglCreateContext(dpy, config, EGL10.EGL_NO_CONTEXT, contextAttrs);
   1.345 -            if (ctx == EGL10.EGL_NO_CONTEXT) {
   1.346 -                Log.e("SDL", "Couldn't create context");
   1.347 -                return false;
   1.348 -            }
   1.349 -
   1.350 -            EGLSurface surface = egl.eglCreateWindowSurface(dpy, config, this, null);
   1.351 -            if (surface == EGL10.EGL_NO_SURFACE) {
   1.352 -                Log.e("SDL", "Couldn't create surface");
   1.353 -                return false;
   1.354 -            }
   1.355 -
   1.356 -            if (!egl.eglMakeCurrent(dpy, surface, surface, ctx)) {
   1.357 -                Log.e("SDL", "Couldn't make context current");
   1.358 -                return false;
   1.359 -            }
   1.360 -
   1.361 -            mEGLContext = ctx;
   1.362 -            mEGLDisplay = dpy;
   1.363 -            mEGLSurface = surface;
   1.364 -
   1.365 -        } catch(Exception e) {
   1.366 -            Log.v("SDL", e + "");
   1.367 -            for (StackTraceElement s : e.getStackTrace()) {
   1.368 -                Log.v("SDL", s.toString());
   1.369 -            }
   1.370 -        }
   1.371 -
   1.372 -        return true;
   1.373 -    }
   1.374 -
   1.375 -    // EGL buffer flip
   1.376 -    public void flipEGL() {
   1.377 -        try {
   1.378 -            EGL10 egl = (EGL10)EGLContext.getEGL();
   1.379 -
   1.380 -            egl.eglWaitNative(EGL10.EGL_CORE_NATIVE_ENGINE, null);
   1.381 -
   1.382 -            // drawing here
   1.383 -
   1.384 -            egl.eglWaitGL();
   1.385 -
   1.386 -            egl.eglSwapBuffers(mEGLDisplay, mEGLSurface);
   1.387 -
   1.388 -            
   1.389 -        } catch(Exception e) {
   1.390 -            Log.v("SDL", "flipEGL(): " + e);
   1.391 -            for (StackTraceElement s : e.getStackTrace()) {
   1.392 -                Log.v("SDL", s.toString());
   1.393 -            }
   1.394 -        }
   1.395 -    }
   1.396  
   1.397      // Key events
   1.398      public boolean onKey(View  v, int keyCode, KeyEvent event) {
     2.1 --- a/src/core/android/SDL_android.cpp	Sun Jan 08 00:39:41 2012 -0500
     2.2 +++ b/src/core/android/SDL_android.cpp	Sun Jan 08 01:05:25 2012 -0500
     2.3 @@ -167,6 +167,26 @@
     2.4      SDL_SendQuit();
     2.5  }
     2.6  
     2.7 +// Pause
     2.8 +extern "C" void Java_org_libsdl_app_SDLActivity_nativePause(
     2.9 +                                    JNIEnv* env, jclass cls)
    2.10 +{
    2.11 +    if (Android_Window) {
    2.12 +        SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_HIDDEN, 0, 0);
    2.13 +        SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_FOCUS_LOST, 0, 0);
    2.14 +    }
    2.15 +}
    2.16 +
    2.17 +// Resume
    2.18 +extern "C" void Java_org_libsdl_app_SDLActivity_nativeResume(
    2.19 +                                    JNIEnv* env, jclass cls)
    2.20 +{
    2.21 +    if (Android_Window) {
    2.22 +        SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_SHOWN, 0, 0);
    2.23 +        SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_FOCUS_GAINED, 0, 0);
    2.24 +    }
    2.25 +}
    2.26 +
    2.27  extern "C" void Java_org_libsdl_app_SDLActivity_nativeRunAudioThread(
    2.28                                      JNIEnv* env, jclass cls)
    2.29  {
     3.1 --- a/src/main/android/SDL_android_main.cpp	Sun Jan 08 00:39:41 2012 -0500
     3.2 +++ b/src/main/android/SDL_android_main.cpp	Sun Jan 08 01:05:25 2012 -0500
     3.3 @@ -33,8 +33,8 @@
     3.4      argv[1] = NULL;
     3.5      status = SDL_main(1, argv);
     3.6  
     3.7 -    /* We exit here for consistency with other platforms. */
     3.8 -    exit(status);
     3.9 +    /* Do not issue an exit or the whole application will terminate instead of just the SDL thread */
    3.10 +    //exit(status);
    3.11  }
    3.12  
    3.13  #endif /* __ANDROID__ */