From http://www.jwz.org/xscreensaver/xscreensaver-5.34.tar.gz
[xscreensaver] / android / project / GLWallpaperService / src / net / rbgrn / android / glwallpaperservice / GLWallpaperService.java
1 /*
2  * Copyright (c) 2011 Ben Gruver
3  * All rights reserved.
4  *
5  * You may use this code at your option under the following BSD license
6  * or Apache 2.0 license terms
7  *
8  * [The "BSD license"]
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. The name of the author may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  *
31  * [The "Apache 2.0 license"]
32  * Licensed under the Apache License, Version 2.0 (the "License");
33  * you may not use this file except in compliance with the License.
34  * You may obtain a copy of the License at
35  *
36  *      http://www.apache.org/licenses/LICENSE-2.0
37  *
38  * Unless required by applicable law or agreed to in writing, software
39  * distributed under the License is distributed on an "AS IS" BASIS,
40  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
41  * See the License for the specific language governing permissions and
42  * limitations under the License.
43  */
44
45 package net.rbgrn.android.glwallpaperservice;
46
47 import android.opengl.GLSurfaceView;
48 import android.service.wallpaper.WallpaperService;
49 import android.view.SurfaceHolder;
50
51 import java.lang.reflect.InvocationTargetException;
52 import java.lang.reflect.Method;
53 import java.util.ArrayList;
54 import java.util.List;
55
56 public abstract class GLWallpaperService extends WallpaperService {
57     public interface Renderer extends GLSurfaceView.Renderer {
58     }
59
60     public class GLEngine extends WallpaperService.Engine {
61         public final static int RENDERMODE_WHEN_DIRTY = 0;
62         public final static int RENDERMODE_CONTINUOUSLY = 1;
63
64         private Object lock = new Object();
65         private GLSurfaceView mGLSurfaceView = null;
66
67         private int debugFlags;
68         private int renderMode;
69
70         /**
71          * If we don't have a GLSurfaceView yet, then we queue up any operations that are requested, until the
72          * GLSurfaceView is created.
73          *
74          * Initially, we created the glSurfaceView in the GLEngine constructor, and things seemed to work. However,
75          * it turns out a few devices aren't set up to handle the surface related events at this point, and crash.
76          *
77          * This is a work around so that we can delay the creation of the GLSurfaceView until the surface is actually
78          * created, so that the underlying code should be in a state to be able to handle the surface related events
79          * that get fired when GLSurfaceView is created.
80          */
81         private List<Runnable> pendingOperations = new ArrayList<Runnable>();
82
83         public GLEngine() {
84         }
85
86         public void setGLWrapper(final GLSurfaceView.GLWrapper glWrapper) {
87             synchronized (lock) {
88                 if (mGLSurfaceView != null) {
89                     mGLSurfaceView.setGLWrapper(glWrapper);
90                 } else {
91                     pendingOperations.add(new Runnable() {
92                         public void run() {
93                             setGLWrapper(glWrapper);
94                         }
95                     });
96                 }
97             }
98         }
99
100         public void setDebugFlags(final int debugFlags) {
101             synchronized (lock) {
102                 if (mGLSurfaceView != null) {
103                     mGLSurfaceView.setDebugFlags(debugFlags);
104                 } else {
105                     this.debugFlags = debugFlags;
106                     pendingOperations.add(new Runnable() {
107                         public void run() {
108                             setDebugFlags(debugFlags);
109                         }
110                     });
111                 }
112             }
113         }
114
115         public int getDebugFlags() {
116             synchronized (lock) {
117                 if (mGLSurfaceView != null) {
118                     return mGLSurfaceView.getDebugFlags();
119                 } else {
120                     return debugFlags;
121                 }
122             }
123         }
124
125         public void setRenderer(final GLSurfaceView.Renderer renderer) {
126             synchronized (lock) {
127                 if (mGLSurfaceView != null) {
128                     mGLSurfaceView.setRenderer(renderer);
129                     if (!isVisible()) {
130                         mGLSurfaceView.onPause();
131                     }
132                 } else {
133                     pendingOperations.add(new Runnable() {
134                         public void run() {
135                             setRenderer(renderer);
136                         }
137                     });
138                 }
139             }
140         }
141
142         public void queueEvent(final Runnable r) {
143             synchronized (lock) {
144                 if (mGLSurfaceView != null) {
145                     mGLSurfaceView.queueEvent(r);
146                 } else {
147                     pendingOperations.add(new Runnable() {
148                         public void run() {
149                             queueEvent(r);
150                         }
151                     });
152                 }
153             }
154         }
155
156         public void setEGLContextFactory(final GLSurfaceView.EGLContextFactory factory) {
157             synchronized (lock) {
158                 if (mGLSurfaceView != null) {
159                     mGLSurfaceView.setEGLContextFactory(factory);
160                 } else {
161                     pendingOperations.add(new Runnable() {
162                         public void run() {
163                             setEGLContextFactory(factory);
164                         }
165                     });
166                 }
167             }
168         }
169
170         public void setEGLWindowSurfaceFactory(final GLSurfaceView.EGLWindowSurfaceFactory factory) {
171             synchronized (lock) {
172                 if (mGLSurfaceView != null) {
173                     mGLSurfaceView.setEGLWindowSurfaceFactory(factory);
174                 } else {
175                     pendingOperations.add(new Runnable() {
176                         public void run() {
177                             setEGLWindowSurfaceFactory(factory);
178                         }
179                     });
180                 }
181             }
182         }
183
184         public void setEGLConfigChooser(final GLSurfaceView.EGLConfigChooser configChooser) {
185             synchronized (lock) {
186                 if (mGLSurfaceView != null) {
187                     mGLSurfaceView.setEGLConfigChooser(configChooser);
188                 } else {
189                     pendingOperations.add(new Runnable() {
190                         public void run() {
191                             setEGLConfigChooser(configChooser);
192                         }
193                     });
194                 }
195             }
196         }
197
198         public void setEGLConfigChooser(final boolean needDepth) {
199             synchronized (lock) {
200                 if (mGLSurfaceView != null) {
201                     mGLSurfaceView.setEGLConfigChooser(needDepth);
202                 } else {
203                     pendingOperations.add(new Runnable() {
204                         public void run() {
205                             setEGLConfigChooser(needDepth);
206                         }
207                     });
208                 }
209             }
210         }
211
212         public void setEGLConfigChooser(final int redSize, final int greenSize, final int blueSize,
213             final int alphaSize, final int depthSize, final int stencilSize) {
214             synchronized (lock) {
215                 if (mGLSurfaceView != null) {
216                     mGLSurfaceView.setEGLConfigChooser(redSize, greenSize, blueSize,
217                         alphaSize, depthSize, stencilSize);
218                 } else {
219                     pendingOperations.add(new Runnable() {
220                         public void run() {
221                             setEGLConfigChooser(redSize, greenSize, blueSize, alphaSize, depthSize, stencilSize);
222                         }
223                     });
224                 }
225             }
226         }
227
228         public void setEGLContextClientVersion(final int version) {
229             synchronized (lock) {
230                 Method method = null;
231
232                 try {
233                     //the setEGLContextClientVersion method is first available in api level 8, but we would
234                     //like to support compiling against api level 7
235                     method = GLSurfaceView.class.getMethod("setEGLContextClientVersion", int.class);
236                 } catch (NoSuchMethodException ex) {
237                     return;
238                 }
239
240                 if (mGLSurfaceView != null) {
241                     try {
242                         method.invoke(mGLSurfaceView, version);
243                     } catch (IllegalAccessException ex) {
244                         return;
245                     } catch (InvocationTargetException ex) {
246                         return;
247                     }
248                 } else {
249                     pendingOperations.add(new Runnable() {
250                         public void run() {
251                             setEGLContextClientVersion(version);
252                         }
253                     });
254                 }
255             }
256         }
257
258         public void setRenderMode(final int renderMode) {
259             synchronized (lock) {
260                 if (mGLSurfaceView != null) {
261                     mGLSurfaceView.setRenderMode(renderMode);
262                 } else {
263                     this.renderMode = renderMode;
264                     pendingOperations.add(new Runnable() {
265                         public void run() {
266                             setRenderMode(renderMode);
267                         }
268                     });
269                 }
270             }
271         }
272
273         public int getRenderMode() {
274             synchronized (lock) {
275                 if (mGLSurfaceView != null) {
276                     return mGLSurfaceView.getRenderMode();
277                 } else {
278                     return renderMode;
279                 }
280             }
281         }
282
283         public void requestRender() {
284             if (mGLSurfaceView != null) {
285                 mGLSurfaceView.requestRender();
286             }
287         }
288
289         @Override
290         public void onVisibilityChanged(final boolean visible) {
291             super.onVisibilityChanged(visible);
292
293             synchronized (lock) {
294                 if (mGLSurfaceView != null) {
295                     if (visible) {
296                         mGLSurfaceView.onResume();
297                     } else {
298                         mGLSurfaceView.onPause();
299                     }
300                 } else {
301                     pendingOperations.add(new Runnable() {
302                         public void run() {
303                             if (visible) {
304                                 mGLSurfaceView.onResume();
305                             } else {
306                                 mGLSurfaceView.onPause();
307                             }
308                         }
309                     });
310                 }
311             }
312         }
313
314         @Override
315         public void onSurfaceChanged(final SurfaceHolder holder, final int format, final int width, final int height) {
316             synchronized (lock) {
317                 if (mGLSurfaceView != null) {
318                     mGLSurfaceView.surfaceChanged(holder, format, width, height);
319                 } else {
320                     pendingOperations.add(new Runnable() {
321                         public void run() {
322                             onSurfaceChanged(holder, format, width, height);
323                         }
324                     });
325                 }
326             }
327         }
328
329         @Override
330         public void onSurfaceCreated(SurfaceHolder holder) {
331             synchronized (lock) {
332                 if (mGLSurfaceView == null) {
333                     mGLSurfaceView = new GLSurfaceView(GLWallpaperService.this) {
334                         @Override
335                         public SurfaceHolder getHolder() {
336                             return GLEngine.this.getSurfaceHolder();
337                         }
338                     };
339                     for (Runnable pendingOperation: pendingOperations) {
340                         pendingOperation.run();
341                     }
342                     pendingOperations.clear();
343                 }
344                 mGLSurfaceView.surfaceCreated(holder);
345             }
346         }
347
348         @Override
349         public void onSurfaceDestroyed(SurfaceHolder holder) {
350             synchronized (lock) {
351                 if (mGLSurfaceView != null) {
352                     mGLSurfaceView.surfaceDestroyed(holder);
353                 }
354             }
355         }
356     }
357 }