From http://www.jwz.org/xscreensaver/xscreensaver-5.31.tar.gz
[xscreensaver] / android / project / GLWallpaperService / src / net / rbgrn / android / glwallpaperservice / GLWallpaperService.java
diff --git a/android/project/GLWallpaperService/src/net/rbgrn/android/glwallpaperservice/GLWallpaperService.java b/android/project/GLWallpaperService/src/net/rbgrn/android/glwallpaperservice/GLWallpaperService.java
new file mode 100644 (file)
index 0000000..63a5be5
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ * Copyright (c) 2011 Ben Gruver
+ * All rights reserved.
+ *
+ * You may use this code at your option under the following BSD license
+ * or Apache 2.0 license terms
+ *
+ * [The "BSD license"]
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * [The "Apache 2.0 license"]
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.rbgrn.android.glwallpaperservice;
+
+import android.opengl.GLSurfaceView;
+import android.service.wallpaper.WallpaperService;
+import android.view.SurfaceHolder;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class GLWallpaperService extends WallpaperService {
+    public interface Renderer extends GLSurfaceView.Renderer {
+    }
+
+    public class GLEngine extends WallpaperService.Engine {
+       public final static int RENDERMODE_WHEN_DIRTY = 0;
+       public final static int RENDERMODE_CONTINUOUSLY = 1;
+
+        private Object lock = new Object();
+        private GLSurfaceView mGLSurfaceView = null;
+
+        private int debugFlags;
+        private int renderMode;
+
+        /**
+         * If we don't have a GLSurfaceView yet, then we queue up any operations that are requested, until the
+         * GLSurfaceView is created.
+         *
+         * Initially, we created the glSurfaceView in the GLEngine constructor, and things seemed to work. However,
+         * it turns out a few devices aren't set up to handle the surface related events at this point, and crash.
+         *
+         * This is a work around so that we can delay the creation of the GLSurfaceView until the surface is actually
+         * created, so that the underlying code should be in a state to be able to handle the surface related events
+         * that get fired when GLSurfaceView is created.
+         */
+        private List<Runnable> pendingOperations = new ArrayList<Runnable>();
+
+        public GLEngine() {
+        }
+
+        public void setGLWrapper(final GLSurfaceView.GLWrapper glWrapper) {
+            synchronized (lock) {
+                if (mGLSurfaceView != null) {
+                    mGLSurfaceView.setGLWrapper(glWrapper);
+                } else {
+                    pendingOperations.add(new Runnable() {
+                        public void run() {
+                            setGLWrapper(glWrapper);
+                        }
+                    });
+                }
+            }
+        }
+
+        public void setDebugFlags(final int debugFlags) {
+            synchronized (lock) {
+                if (mGLSurfaceView != null) {
+                    mGLSurfaceView.setDebugFlags(debugFlags);
+                } else {
+                    this.debugFlags = debugFlags;
+                    pendingOperations.add(new Runnable() {
+                        public void run() {
+                            setDebugFlags(debugFlags);
+                        }
+                    });
+                }
+            }
+        }
+
+        public int getDebugFlags() {
+            synchronized (lock) {
+                if (mGLSurfaceView != null) {
+                    return mGLSurfaceView.getDebugFlags();
+                } else {
+                    return debugFlags;
+                }
+            }
+        }
+
+        public void setRenderer(final GLSurfaceView.Renderer renderer) {
+            synchronized (lock) {
+                if (mGLSurfaceView != null) {
+                    mGLSurfaceView.setRenderer(renderer);
+                    if (!isVisible()) {
+                        mGLSurfaceView.onPause();
+                    }
+                } else {
+                    pendingOperations.add(new Runnable() {
+                        public void run() {
+                            setRenderer(renderer);
+                        }
+                    });
+                }
+            }
+        }
+
+        public void queueEvent(final Runnable r) {
+            synchronized (lock) {
+                if (mGLSurfaceView != null) {
+                    mGLSurfaceView.queueEvent(r);
+                } else {
+                    pendingOperations.add(new Runnable() {
+                        public void run() {
+                            queueEvent(r);
+                        }
+                    });
+                }
+            }
+        }
+
+        public void setEGLContextFactory(final GLSurfaceView.EGLContextFactory factory) {
+            synchronized (lock) {
+                if (mGLSurfaceView != null) {
+                    mGLSurfaceView.setEGLContextFactory(factory);
+                } else {
+                    pendingOperations.add(new Runnable() {
+                        public void run() {
+                            setEGLContextFactory(factory);
+                        }
+                    });
+                }
+            }
+        }
+
+        public void setEGLWindowSurfaceFactory(final GLSurfaceView.EGLWindowSurfaceFactory factory) {
+            synchronized (lock) {
+                if (mGLSurfaceView != null) {
+                    mGLSurfaceView.setEGLWindowSurfaceFactory(factory);
+                } else {
+                    pendingOperations.add(new Runnable() {
+                        public void run() {
+                            setEGLWindowSurfaceFactory(factory);
+                        }
+                    });
+                }
+            }
+        }
+
+        public void setEGLConfigChooser(final GLSurfaceView.EGLConfigChooser configChooser) {
+            synchronized (lock) {
+                if (mGLSurfaceView != null) {
+                    mGLSurfaceView.setEGLConfigChooser(configChooser);
+                } else {
+                    pendingOperations.add(new Runnable() {
+                        public void run() {
+                            setEGLConfigChooser(configChooser);
+                        }
+                    });
+                }
+            }
+        }
+
+        public void setEGLConfigChooser(final boolean needDepth) {
+            synchronized (lock) {
+                if (mGLSurfaceView != null) {
+                    mGLSurfaceView.setEGLConfigChooser(needDepth);
+                } else {
+                    pendingOperations.add(new Runnable() {
+                        public void run() {
+                            setEGLConfigChooser(needDepth);
+                        }
+                    });
+                }
+            }
+        }
+
+        public void setEGLConfigChooser(final int redSize, final int greenSize, final int blueSize,
+            final int alphaSize, final int depthSize, final int stencilSize) {
+            synchronized (lock) {
+                if (mGLSurfaceView != null) {
+                    mGLSurfaceView.setEGLConfigChooser(redSize, greenSize, blueSize,
+                        alphaSize, depthSize, stencilSize);
+                } else {
+                    pendingOperations.add(new Runnable() {
+                        public void run() {
+                            setEGLConfigChooser(redSize, greenSize, blueSize, alphaSize, depthSize, stencilSize);
+                        }
+                    });
+                }
+            }
+        }
+
+        public void setEGLContextClientVersion(final int version) {
+            synchronized (lock) {
+                Method method = null;
+
+                try {
+                    //the setEGLContextClientVersion method is first available in api level 8, but we would
+                    //like to support compiling against api level 7
+                    method = GLSurfaceView.class.getMethod("setEGLContextClientVersion", int.class);
+                } catch (NoSuchMethodException ex) {
+                    return;
+                }
+
+                if (mGLSurfaceView != null) {
+                    try {
+                        method.invoke(mGLSurfaceView, version);
+                    } catch (IllegalAccessException ex) {
+                        return;
+                    } catch (InvocationTargetException ex) {
+                        return;
+                    }
+                } else {
+                    pendingOperations.add(new Runnable() {
+                        public void run() {
+                            setEGLContextClientVersion(version);
+                        }
+                    });
+                }
+            }
+        }
+
+        public void setRenderMode(final int renderMode) {
+            synchronized (lock) {
+                if (mGLSurfaceView != null) {
+                    mGLSurfaceView.setRenderMode(renderMode);
+                } else {
+                    this.renderMode = renderMode;
+                    pendingOperations.add(new Runnable() {
+                        public void run() {
+                            setRenderMode(renderMode);
+                        }
+                    });
+                }
+            }
+        }
+
+        public int getRenderMode() {
+            synchronized (lock) {
+                if (mGLSurfaceView != null) {
+                    return mGLSurfaceView.getRenderMode();
+                } else {
+                    return renderMode;
+                }
+            }
+        }
+
+        public void requestRender() {
+            if (mGLSurfaceView != null) {
+                mGLSurfaceView.requestRender();
+            }
+        }
+
+        @Override
+        public void onVisibilityChanged(final boolean visible) {
+            super.onVisibilityChanged(visible);
+
+            synchronized (lock) {
+                if (mGLSurfaceView != null) {
+                    if (visible) {
+                        mGLSurfaceView.onResume();
+                    } else {
+                        mGLSurfaceView.onPause();
+                    }
+                } else {
+                    pendingOperations.add(new Runnable() {
+                        public void run() {
+                            if (visible) {
+                                mGLSurfaceView.onResume();
+                            } else {
+                                mGLSurfaceView.onPause();
+                            }
+                        }
+                    });
+                }
+            }
+        }
+
+        @Override
+        public void onSurfaceChanged(final SurfaceHolder holder, final int format, final int width, final int height) {
+            synchronized (lock) {
+                if (mGLSurfaceView != null) {
+                    mGLSurfaceView.surfaceChanged(holder, format, width, height);
+                } else {
+                    pendingOperations.add(new Runnable() {
+                        public void run() {
+                            onSurfaceChanged(holder, format, width, height);
+                        }
+                    });
+                }
+            }
+        }
+
+        @Override
+        public void onSurfaceCreated(SurfaceHolder holder) {
+            synchronized (lock) {
+                if (mGLSurfaceView == null) {
+                    mGLSurfaceView = new GLSurfaceView(GLWallpaperService.this) {
+                        @Override
+                        public SurfaceHolder getHolder() {
+                            return GLEngine.this.getSurfaceHolder();
+                        }
+                    };
+                    for (Runnable pendingOperation: pendingOperations) {
+                        pendingOperation.run();
+                    }
+                    pendingOperations.clear();
+                }
+                mGLSurfaceView.surfaceCreated(holder);
+            }
+        }
+
+        @Override
+        public void onSurfaceDestroyed(SurfaceHolder holder) {
+            synchronized (lock) {
+                if (mGLSurfaceView != null) {
+                    mGLSurfaceView.surfaceDestroyed(holder);
+                }
+            }
+        }
+    }
+}