PageRenderTime 137ms CodeModel.GetById 14ms app.highlight 111ms RepoModel.GetById 1ms app.codeStats 1ms

/FallingSandpaper/src/android/opengl/GLWallpaperService.java

http://thelements.googlecode.com/
Java | 1110 lines | 833 code | 133 blank | 144 comment | 108 complexity | 8f9629475aec7ad0b4f0c49ea6f65ef0 MD5 | raw file
   1package android.opengl;
   2
   3import java.io.Writer;
   4import java.util.ArrayList;
   5
   6import javax.microedition.khronos.egl.EGL10;
   7import javax.microedition.khronos.egl.EGL11;
   8import javax.microedition.khronos.egl.EGLConfig;
   9import javax.microedition.khronos.egl.EGLContext;
  10import javax.microedition.khronos.egl.EGLDisplay;
  11import javax.microedition.khronos.egl.EGLSurface;
  12import javax.microedition.khronos.opengles.GL;
  13import javax.microedition.khronos.opengles.GL10;
  14
  15import android.opengl.BaseConfigChooser.ComponentSizeChooser;
  16import android.opengl.BaseConfigChooser.SimpleEGLConfigChooser;
  17import android.service.wallpaper.WallpaperService;
  18import android.util.Log;
  19import android.view.SurfaceHolder;
  20
  21public class GLWallpaperService extends WallpaperService
  22{
  23	private static final String TAG = "GLWallpaperService";
  24
  25	@Override
  26	public Engine onCreateEngine()
  27	{
  28		return new GLEngine();
  29	}
  30
  31	public class GLEngine extends Engine
  32	{
  33		public final static int RENDERMODE_WHEN_DIRTY = 0;
  34		public final static int RENDERMODE_CONTINUOUSLY = 1;
  35
  36		private GLThread mGLThread;
  37		private EGLConfigChooser mEGLConfigChooser;
  38		private EGLContextFactory mEGLContextFactory;
  39		private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory;
  40		private GLWrapper mGLWrapper;
  41		private int mDebugFlags;
  42
  43		public GLEngine()
  44		{
  45			super();
  46		}
  47
  48		@Override
  49		public void onVisibilityChanged(boolean visible)
  50		{
  51			if (visible)
  52			{
  53				onResume();
  54			}
  55			else
  56			{
  57				onPause();
  58			}
  59			super.onVisibilityChanged(visible);
  60		}
  61
  62		@Override
  63		public void onCreate(SurfaceHolder surfaceHolder)
  64		{
  65			super.onCreate(surfaceHolder);
  66			// Log.d(TAG, "GLEngine.onCreate()");
  67		}
  68
  69		@Override
  70		public void onDestroy()
  71		{
  72			super.onDestroy();
  73			// Log.d(TAG, "GLEngine.onDestroy()");
  74			mGLThread.requestExitAndWait();
  75		}
  76
  77		@Override
  78		public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height)
  79		{
  80			// Log.d(TAG, "onSurfaceChanged()");
  81			mGLThread.onWindowResize(width, height);
  82			super.onSurfaceChanged(holder, format, width, height);
  83		}
  84
  85		@Override
  86		public void onSurfaceCreated(SurfaceHolder holder)
  87		{
  88			//Log.d(TAG, "onSurfaceCreated()");
  89			mGLThread.surfaceCreated(holder);
  90			super.onSurfaceCreated(holder);
  91		}
  92
  93		@Override
  94		public void onSurfaceDestroyed(SurfaceHolder holder)
  95		{
  96			//Log.d(TAG, "onSurfaceDestroyed()");
  97			mGLThread.surfaceDestroyed();
  98			super.onSurfaceDestroyed(holder);
  99		}
 100
 101		/**
 102		 * An EGL helper class.
 103		 */
 104		public void setGLWrapper(GLWrapper glWrapper)
 105		{
 106			mGLWrapper = glWrapper;
 107		}
 108
 109		public void setDebugFlags(int debugFlags)
 110		{
 111			mDebugFlags = debugFlags;
 112		}
 113
 114		public int getDebugFlags()
 115		{
 116			return mDebugFlags;
 117		}
 118
 119		public void setRenderer(Renderer renderer)
 120		{
 121			checkRenderThreadState();
 122			if (mEGLConfigChooser == null)
 123			{
 124				mEGLConfigChooser = new SimpleEGLConfigChooser(true);
 125			}
 126			if (mEGLContextFactory == null)
 127			{
 128				mEGLContextFactory = new DefaultContextFactory();
 129			}
 130			if (mEGLWindowSurfaceFactory == null)
 131			{
 132				mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
 133			}
 134			mGLThread = new GLThread(renderer, mEGLConfigChooser, mEGLContextFactory, mEGLWindowSurfaceFactory, mGLWrapper);
 135			mGLThread.start();
 136		}
 137
 138		public void setEGLContextFactory(EGLContextFactory factory)
 139		{
 140			checkRenderThreadState();
 141			mEGLContextFactory = factory;
 142		}
 143
 144		public void setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory factory)
 145		{
 146			checkRenderThreadState();
 147			mEGLWindowSurfaceFactory = factory;
 148		}
 149
 150		public void setEGLConfigChooser(EGLConfigChooser configChooser)
 151		{
 152			checkRenderThreadState();
 153			mEGLConfigChooser = configChooser;
 154		}
 155
 156		public void setEGLConfigChooser(boolean needDepth)
 157		{
 158			setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth));
 159		}
 160
 161		public void setEGLConfigChooser(int redSize, int greenSize, int blueSize, int alphaSize, int depthSize, int stencilSize)
 162		{
 163			setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize, blueSize, alphaSize, depthSize, stencilSize));
 164		}
 165
 166		public void setRenderMode(int renderMode)
 167		{
 168			mGLThread.setRenderMode(renderMode);
 169		}
 170
 171		public int getRenderMode()
 172		{
 173			return mGLThread.getRenderMode();
 174		}
 175
 176		public void requestRender()
 177		{
 178			mGLThread.requestRender();
 179		}
 180
 181		public void onPause()
 182		{
 183			mGLThread.onPause();
 184		}
 185
 186		public void onResume()
 187		{
 188			mGLThread.onResume();
 189		}
 190
 191		public void queueEvent(Runnable r)
 192		{
 193			mGLThread.queueEvent(r);
 194		}
 195
 196		private void checkRenderThreadState()
 197		{
 198			if (mGLThread != null)
 199			{
 200				throw new IllegalStateException("setRenderer has already been called for this instance.");
 201			}
 202		}
 203	}
 204
 205	public interface Renderer
 206	{
 207
 208		public void onSurfaceCreated(GL10 gl, EGLConfig config);
 209
 210		public void onSurfaceChanged(GL10 gl, int width, int height);
 211
 212		public void onDrawFrame(GL10 gl);
 213	}
 214}
 215
 216class LogWriter extends Writer
 217{
 218	private StringBuilder mBuilder = new StringBuilder();
 219
 220	@Override
 221	public void close()
 222	{
 223		flushBuilder();
 224	}
 225
 226	@Override
 227	public void flush()
 228	{
 229		flushBuilder();
 230	}
 231
 232	@Override
 233	public void write(char[] buf, int offset, int count)
 234	{
 235		for (int i = 0; i < count; i++)
 236		{
 237			char c = buf[offset + i];
 238			if (c == '\n')
 239			{
 240				flushBuilder();
 241			}
 242			else
 243			{
 244				mBuilder.append(c);
 245			}
 246		}
 247	}
 248
 249	private void flushBuilder()
 250	{
 251		if (mBuilder.length() > 0)
 252		{
 253			Log.v("GLSurfaceView", mBuilder.toString());
 254			mBuilder.delete(0, mBuilder.length());
 255		}
 256	}
 257}
 258
 259// ----------------------------------------------------------------------
 260
 261/**
 262 * An interface for customizing the eglCreateContext and eglDestroyContext
 263 * calls.
 264 * 
 265 * 
 266 * This interface must be implemented by clients wishing to call
 267 * {@link GLWallpaperService#setEGLContextFactory(EGLContextFactory)}
 268 */
 269interface EGLContextFactory
 270{
 271	EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig);
 272
 273	void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context);
 274}
 275
 276class DefaultContextFactory implements EGLContextFactory
 277{
 278
 279	public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config)
 280	{
 281		return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, null);
 282	}
 283
 284	public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context)
 285	{
 286		egl.eglDestroyContext(display, context);
 287	}
 288}
 289
 290/**
 291 * An interface for customizing the eglCreateWindowSurface and eglDestroySurface
 292 * calls.
 293 * 
 294 * 
 295 * This interface must be implemented by clients wishing to call
 296 * {@link GLWallpaperService#setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory)}
 297 */
 298interface EGLWindowSurfaceFactory
 299{
 300	EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config, Object nativeWindow);
 301
 302	void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface);
 303}
 304
 305class DefaultWindowSurfaceFactory implements EGLWindowSurfaceFactory
 306{
 307
 308	public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config, Object nativeWindow)
 309	{
 310		// this is a bit of a hack to work around Droid init problems - if you
 311		// don't have this, it'll get hung up on orientation changes
 312		EGLSurface eglSurface = null;
 313		while (eglSurface == null)
 314		{
 315			try
 316			{
 317				eglSurface = egl.eglCreateWindowSurface(display, config, nativeWindow, null);
 318			} catch (Throwable t)
 319			{
 320			} finally
 321			{
 322				if (eglSurface == null)
 323				{
 324					try
 325					{
 326						Thread.sleep(10);
 327					} catch (InterruptedException t)
 328					{
 329					}
 330				}
 331			}
 332		}
 333		return eglSurface;
 334	}
 335
 336	public void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface)
 337	{
 338		egl.eglDestroySurface(display, surface);
 339	}
 340}
 341
 342interface GLWrapper
 343{
 344	/**
 345	 * Wraps a gl interface in another gl interface.
 346	 * 
 347	 * @param gl
 348	 *            a GL interface that is to be wrapped.
 349	 * @return either the input argument or another GL object that wraps the
 350	 *         input argument.
 351	 */
 352	GL wrap(GL gl);
 353}
 354
 355class EglHelper
 356{
 357
 358	private EGL10 mEgl;
 359	private EGLDisplay mEglDisplay;
 360	private EGLSurface mEglSurface;
 361	private EGLContext mEglContext;
 362	EGLConfig mEglConfig;
 363
 364	private EGLConfigChooser mEGLConfigChooser;
 365	private EGLContextFactory mEGLContextFactory;
 366	private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory;
 367	private GLWrapper mGLWrapper;
 368
 369	public EglHelper(EGLConfigChooser chooser, EGLContextFactory contextFactory, EGLWindowSurfaceFactory surfaceFactory, GLWrapper wrapper)
 370	{
 371		this.mEGLConfigChooser = chooser;
 372		this.mEGLContextFactory = contextFactory;
 373		this.mEGLWindowSurfaceFactory = surfaceFactory;
 374		this.mGLWrapper = wrapper;
 375	}
 376
 377	/**
 378	 * Initialize EGL for a given configuration spec.
 379	 * 
 380	 * @param configSpec
 381	 */
 382	public void start()
 383	{
 384
 385		if (mEgl == null)
 386		{
 387
 388			/*
 389			 * Get an EGL instance
 390			 */
 391			mEgl = (EGL10) EGLContext.getEGL();
 392		}
 393		else
 394		{
 395
 396		}
 397
 398		if (mEglDisplay == null)
 399		{
 400
 401			/*
 402			 * Get to the default display.
 403			 */
 404			mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
 405		}
 406		else
 407		{
 408
 409		}
 410
 411		if (mEglConfig == null)
 412		{
 413
 414			/*
 415			 * We can now initialize EGL for that display
 416			 */
 417			int[] version = new int[2];
 418			mEgl.eglInitialize(mEglDisplay, version);
 419			mEglConfig = mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);
 420		}
 421		else
 422		{
 423
 424		}
 425
 426		if (mEglContext == null)
 427		{
 428
 429			/*
 430			 * Create an OpenGL ES context. This must be done only once, an
 431			 * OpenGL context is a somewhat heavy object.
 432			 */
 433			mEglContext = mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);
 434			if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT)
 435			{
 436				throw new RuntimeException("createContext failed");
 437			}
 438		}
 439		else
 440		{
 441
 442		}
 443
 444		mEglSurface = null;
 445	}
 446
 447	/*
 448	 * React to the creation of a new surface by creating and returning an
 449	 * OpenGL interface that renders to that surface.
 450	 */
 451	public GL createSurface(SurfaceHolder holder)
 452	{
 453		/*
 454		 * The window size has changed, so we need to create a new surface.
 455		 */
 456		if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE)
 457		{
 458
 459			/*
 460			 * Unbind and destroy the old EGL surface, if there is one.
 461			 */
 462			mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
 463			mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);
 464		}
 465
 466		/*
 467		 * Create an EGL surface we can render into.
 468		 */
 469		mEglSurface = mEGLWindowSurfaceFactory.createWindowSurface(mEgl, mEglDisplay, mEglConfig, holder);
 470
 471		if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE)
 472		{
 473			throw new RuntimeException("createWindowSurface failed");
 474		}
 475
 476		/*
 477		 * Before we can issue GL commands, we need to make sure the context is
 478		 * current and bound to a surface.
 479		 */
 480		if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext))
 481		{
 482			throw new RuntimeException("eglMakeCurrent failed.");
 483		}
 484
 485		GL gl = mEglContext.getGL();
 486		if (mGLWrapper != null)
 487		{
 488			gl = mGLWrapper.wrap(gl);
 489		}
 490
 491		/*
 492		 * if ((mDebugFlags & (DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS))!= 0)
 493		 * { int configFlags = 0; Writer log = null; if ((mDebugFlags &
 494		 * DEBUG_CHECK_GL_ERROR) != 0) { configFlags |=
 495		 * GLDebugHelper.CONFIG_CHECK_GL_ERROR; } if ((mDebugFlags &
 496		 * DEBUG_LOG_GL_CALLS) != 0) { log = new LogWriter(); } gl =
 497		 * GLDebugHelper.wrap(gl, configFlags, log); }
 498		 */
 499		return gl;
 500	}
 501
 502	/**
 503	 * Display the current render surface.
 504	 * 
 505	 * @return false if the context has been lost.
 506	 */
 507	public boolean swap()
 508	{
 509		mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
 510
 511		/*
 512		 * Always check for EGL_CONTEXT_LOST, which means the context and all
 513		 * associated data were lost (For instance because the device went to
 514		 * sleep). We need to sleep until we get a new surface.
 515		 */
 516		return mEgl.eglGetError() != EGL11.EGL_CONTEXT_LOST;
 517	}
 518
 519	public void destroySurface()
 520	{
 521		if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE)
 522		{
 523			mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
 524			mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);
 525			mEglSurface = null;
 526		}
 527	}
 528
 529	public void finish()
 530	{
 531		if (mEglContext != null)
 532		{
 533			mEGLContextFactory.destroyContext(mEgl, mEglDisplay, mEglContext);
 534			mEglContext = null;
 535		}
 536		if (mEglDisplay != null)
 537		{
 538			mEgl.eglTerminate(mEglDisplay);
 539			mEglDisplay = null;
 540		}
 541	}
 542}
 543
 544class GLThread extends Thread
 545{
 546	private final static boolean LOG_THREADS = false;
 547	public final static int DEBUG_CHECK_GL_ERROR = 1;
 548	public final static int DEBUG_LOG_GL_CALLS = 2;
 549
 550	private final GLThreadManager sGLThreadManager = new GLThreadManager();
 551	private GLThread mEglOwner;
 552
 553	private EGLConfigChooser mEGLConfigChooser;
 554	private EGLContextFactory mEGLContextFactory;
 555	private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory;
 556	private GLWrapper mGLWrapper;
 557
 558	public SurfaceHolder mHolder;
 559	private boolean mSizeChanged = true;
 560
 561	// Once the thread is started, all accesses to the following member
 562	// variables are protected by the sGLThreadManager monitor
 563	public boolean mDone;
 564	private boolean mPaused;
 565	private boolean mHasSurface;
 566	private boolean mWaitingForSurface;
 567	private boolean mHaveEgl;
 568	private int mWidth;
 569	private int mHeight;
 570	private int mRenderMode;
 571	private boolean mRequestRender;
 572	private boolean mEventsWaiting;
 573	// End of member variables protected by the sGLThreadManager monitor.
 574
 575	private GLWallpaperService.Renderer mRenderer;
 576	private ArrayList mEventQueue = new ArrayList();
 577	private EglHelper mEglHelper;
 578
 579	GLThread(GLWallpaperService.Renderer renderer, EGLConfigChooser chooser, EGLContextFactory contextFactory, EGLWindowSurfaceFactory surfaceFactory, GLWrapper wrapper)
 580	{
 581		super();
 582		mDone = false;
 583		mWidth = 0;
 584		mHeight = 0;
 585		mRequestRender = true;
 586		mRenderMode = GLWallpaperService.GLEngine.RENDERMODE_CONTINUOUSLY;
 587		mRenderer = renderer;
 588		this.mEGLConfigChooser = chooser;
 589		this.mEGLContextFactory = contextFactory;
 590		this.mEGLWindowSurfaceFactory = surfaceFactory;
 591		this.mGLWrapper = wrapper;
 592	}
 593
 594	@Override
 595	public void run()
 596	{
 597		setName("GLThread " + getId());
 598		if (LOG_THREADS)
 599		{
 600			Log.i("GLThread", "starting tid=" + getId());
 601		}
 602
 603		try
 604		{
 605			guardedRun();
 606		} catch (InterruptedException e)
 607		{
 608			// fall thru and exit normally
 609		} finally
 610		{
 611			sGLThreadManager.threadExiting(this);
 612		}
 613	}
 614
 615	/*
 616	 * This private method should only be called inside a
 617	 * synchronized(sGLThreadManager) block.
 618	 */
 619	private void stopEglLocked()
 620	{
 621		if (mHaveEgl)
 622		{
 623			mHaveEgl = false;
 624			mEglHelper.destroySurface();
 625			sGLThreadManager.releaseEglSurface(this);
 626		}
 627	}
 628
 629	private void guardedRun() throws InterruptedException
 630	{
 631		mEglHelper = new EglHelper(mEGLConfigChooser, mEGLContextFactory, mEGLWindowSurfaceFactory, mGLWrapper);
 632		try
 633		{
 634			GL10 gl = null;
 635			boolean tellRendererSurfaceCreated = true;
 636			boolean tellRendererSurfaceChanged = true;
 637
 638			/*
 639			 * This is our main activity thread's loop, we go until asked to
 640			 * quit.
 641			 */
 642			while (!isDone())
 643			{
 644				/*
 645				 * Update the asynchronous state (window size)
 646				 */
 647				int w = 0;
 648				int h = 0;
 649				boolean changed = false;
 650				boolean needStart = false;
 651				boolean eventsWaiting = false;
 652
 653				synchronized (sGLThreadManager)
 654				{
 655					while (true)
 656					{
 657						// Manage acquiring and releasing the SurfaceView
 658						// surface and the EGL surface.
 659						if (mPaused)
 660						{
 661							stopEglLocked();
 662						}
 663						if (!mHasSurface)
 664						{
 665							if (!mWaitingForSurface)
 666							{
 667								stopEglLocked();
 668								mWaitingForSurface = true;
 669								sGLThreadManager.notifyAll();
 670							}
 671						}
 672						else
 673						{
 674							if (!mHaveEgl)
 675							{
 676								if (sGLThreadManager.tryAcquireEglSurface(this))
 677								{
 678									mHaveEgl = true;
 679									mEglHelper.start();
 680									mRequestRender = true;
 681									needStart = true;
 682								}
 683							}
 684						}
 685
 686						// Check if we need to wait. If not, update any state
 687						// that needs to be updated, copy any state that
 688						// needs to be copied, and use "break" to exit the
 689						// wait loop.
 690
 691						if (mDone)
 692						{
 693							return;
 694						}
 695
 696						if (mEventsWaiting)
 697						{
 698							eventsWaiting = true;
 699							mEventsWaiting = false;
 700							break;
 701						}
 702
 703						if ((!mPaused) && mHasSurface && mHaveEgl && (mWidth > 0) && (mHeight > 0) && (mRequestRender || (mRenderMode == GLWallpaperService.GLEngine.RENDERMODE_CONTINUOUSLY)))
 704						{
 705							changed = mSizeChanged;
 706							w = mWidth;
 707							h = mHeight;
 708							mSizeChanged = false;
 709							mRequestRender = false;
 710							if (mHasSurface && mWaitingForSurface)
 711							{
 712								changed = true;
 713								mWaitingForSurface = false;
 714								sGLThreadManager.notifyAll();
 715							}
 716							break;
 717						}
 718
 719						// By design, this is the only place where we wait().
 720
 721						if (LOG_THREADS)
 722						{
 723							Log.i("GLThread", "waiting tid=" + getId());
 724						}
 725						sGLThreadManager.wait();
 726					}
 727				} // end of synchronized(sGLThreadManager)
 728
 729				/*
 730				 * Handle queued events
 731				 */
 732				if (eventsWaiting)
 733				{
 734					Runnable r;
 735					while ((r = getEvent()) != null)
 736					{
 737						r.run();
 738						if (isDone())
 739						{
 740							return;
 741						}
 742					}
 743					// Go back and see if we need to wait to render.
 744					continue;
 745				}
 746
 747				if (needStart)
 748				{
 749					tellRendererSurfaceCreated = true;
 750					changed = true;
 751				}
 752				if (changed)
 753				{
 754					gl = (GL10) mEglHelper.createSurface(mHolder);
 755					tellRendererSurfaceChanged = true;
 756				}
 757				if (tellRendererSurfaceCreated)
 758				{
 759					mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
 760					tellRendererSurfaceCreated = false;
 761				}
 762				if (tellRendererSurfaceChanged)
 763				{
 764					mRenderer.onSurfaceChanged(gl, w, h);
 765					tellRendererSurfaceChanged = false;
 766				}
 767				if ((w > 0) && (h > 0))
 768				{
 769					/* draw a frame here */
 770					mRenderer.onDrawFrame(gl);
 771
 772					/*
 773					 * Once we're done with GL, we need to call swapBuffers() to
 774					 * instruct the system to display the rendered frame
 775					 */
 776					mEglHelper.swap();
 777				}
 778			}
 779		} finally
 780		{
 781			/*
 782			 * clean-up everything...
 783			 */
 784			synchronized (sGLThreadManager)
 785			{
 786				stopEglLocked();
 787				mEglHelper.finish();
 788			}
 789		}
 790	}
 791
 792	private boolean isDone()
 793	{
 794		synchronized (sGLThreadManager)
 795		{
 796			return mDone;
 797		}
 798	}
 799
 800	public void setRenderMode(int renderMode)
 801	{
 802		if (!((GLWallpaperService.GLEngine.RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= GLWallpaperService.GLEngine.RENDERMODE_CONTINUOUSLY)))
 803		{
 804			throw new IllegalArgumentException("renderMode");
 805		}
 806		synchronized (sGLThreadManager)
 807		{
 808			mRenderMode = renderMode;
 809			if (renderMode == GLWallpaperService.GLEngine.RENDERMODE_CONTINUOUSLY)
 810			{
 811				sGLThreadManager.notifyAll();
 812			}
 813		}
 814	}
 815
 816	public int getRenderMode()
 817	{
 818		synchronized (sGLThreadManager)
 819		{
 820			return mRenderMode;
 821		}
 822	}
 823
 824	public void requestRender()
 825	{
 826		synchronized (sGLThreadManager)
 827		{
 828			mRequestRender = true;
 829			sGLThreadManager.notifyAll();
 830		}
 831	}
 832
 833	public void surfaceCreated(SurfaceHolder holder)
 834	{
 835		mHolder = holder;
 836		synchronized (sGLThreadManager)
 837		{
 838			if (LOG_THREADS)
 839			{
 840				Log.i("GLThread", "surfaceCreated tid=" + getId());
 841			}
 842			mHasSurface = true;
 843			sGLThreadManager.notifyAll();
 844		}
 845	}
 846
 847	public void surfaceDestroyed()
 848	{
 849		synchronized (sGLThreadManager)
 850		{
 851			if (LOG_THREADS)
 852			{
 853				Log.i("GLThread", "surfaceDestroyed tid=" + getId());
 854			}
 855			mHasSurface = false;
 856			sGLThreadManager.notifyAll();
 857			while (!mWaitingForSurface && isAlive() && !mDone)
 858			{
 859				try
 860				{
 861					sGLThreadManager.wait();
 862				} catch (InterruptedException e)
 863				{
 864					Thread.currentThread().interrupt();
 865				}
 866			}
 867		}
 868	}
 869
 870	public void onPause()
 871	{
 872		synchronized (sGLThreadManager)
 873		{
 874			mPaused = true;
 875			sGLThreadManager.notifyAll();
 876		}
 877	}
 878
 879	public void onResume()
 880	{
 881		synchronized (sGLThreadManager)
 882		{
 883			mPaused = false;
 884			mRequestRender = true;
 885			sGLThreadManager.notifyAll();
 886		}
 887	}
 888
 889	public void onWindowResize(int w, int h)
 890	{
 891		synchronized (sGLThreadManager)
 892		{
 893			mWidth = w;
 894			mHeight = h;
 895			mSizeChanged = true;
 896			sGLThreadManager.notifyAll();
 897		}
 898	}
 899
 900	public void requestExitAndWait()
 901	{
 902		// don't call this from GLThread thread or it is a guaranteed
 903		// deadlock!
 904		synchronized (sGLThreadManager)
 905		{
 906			mDone = true;
 907			sGLThreadManager.notifyAll();
 908		}
 909		try
 910		{
 911			join();
 912		} catch (InterruptedException ex)
 913		{
 914			Thread.currentThread().interrupt();
 915		}
 916	}
 917
 918	/**
 919	 * Queue an "event" to be run on the GL rendering thread.
 920	 * 
 921	 * @param r
 922	 *            the runnable to be run on the GL rendering thread.
 923	 */
 924	public void queueEvent(Runnable r)
 925	{
 926		synchronized (this)
 927		{
 928			mEventQueue.add(r);
 929			synchronized (sGLThreadManager)
 930			{
 931				mEventsWaiting = true;
 932				sGLThreadManager.notifyAll();
 933			}
 934		}
 935	}
 936
 937	private Runnable getEvent()
 938	{
 939		synchronized (this)
 940		{
 941			if (mEventQueue.size() > 0)
 942			{
 943				return (Runnable) mEventQueue.remove(0);
 944			}
 945
 946		}
 947		return null;
 948	}
 949
 950	private class GLThreadManager
 951	{
 952
 953		public synchronized void threadExiting(GLThread thread)
 954		{
 955			if (LOG_THREADS)
 956			{
 957				Log.i("GLThread", "exiting tid=" + thread.getId());
 958			}
 959			thread.mDone = true;
 960			if (mEglOwner == thread)
 961			{
 962				mEglOwner = null;
 963			}
 964			notifyAll();
 965		}
 966
 967		/*
 968		 * Tries once to acquire the right to use an EGL surface. Does not
 969		 * block.
 970		 * 
 971		 * @return true if the right to use an EGL surface was acquired.
 972		 */
 973		public synchronized boolean tryAcquireEglSurface(GLThread thread)
 974		{
 975			if (mEglOwner == thread || mEglOwner == null)
 976			{
 977				mEglOwner = thread;
 978				notifyAll();
 979				return true;
 980			}
 981			return false;
 982		}
 983
 984		public synchronized void releaseEglSurface(GLThread thread)
 985		{
 986			if (mEglOwner == thread)
 987			{
 988				mEglOwner = null;
 989			}
 990			notifyAll();
 991		}
 992	}
 993}
 994
 995interface EGLConfigChooser
 996{
 997	EGLConfig chooseConfig(EGL10 egl, EGLDisplay display);
 998}
 999
1000abstract class BaseConfigChooser implements EGLConfigChooser
1001{
1002	public BaseConfigChooser(int[] configSpec)
1003	{
1004		mConfigSpec = configSpec;
1005	}
1006
1007	public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display)
1008	{
1009		int[] num_config = new int[1];
1010		egl.eglChooseConfig(display, mConfigSpec, null, 0, num_config);
1011
1012		int numConfigs = num_config[0];
1013
1014		if (numConfigs <= 0)
1015		{
1016			throw new IllegalArgumentException("No configs match configSpec");
1017		}
1018
1019		EGLConfig[] configs = new EGLConfig[numConfigs];
1020		egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, num_config);
1021		EGLConfig config = chooseConfig(egl, display, configs);
1022		if (config == null)
1023		{
1024			throw new IllegalArgumentException("No config chosen");
1025		}
1026		return config;
1027	}
1028
1029	abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs);
1030
1031	protected int[] mConfigSpec;
1032
1033	public static class ComponentSizeChooser extends BaseConfigChooser
1034	{
1035		public ComponentSizeChooser(int redSize, int greenSize, int blueSize, int alphaSize, int depthSize, int stencilSize)
1036		{
1037			super(new int[] {EGL10.EGL_RED_SIZE, redSize, EGL10.EGL_GREEN_SIZE, greenSize, EGL10.EGL_BLUE_SIZE, blueSize, EGL10.EGL_ALPHA_SIZE, alphaSize, EGL10.EGL_DEPTH_SIZE, depthSize, EGL10.EGL_STENCIL_SIZE, stencilSize, EGL10.EGL_NONE});
1038			mValue = new int[1];
1039			mRedSize = redSize;
1040			mGreenSize = greenSize;
1041			mBlueSize = blueSize;
1042			mAlphaSize = alphaSize;
1043			mDepthSize = depthSize;
1044			mStencilSize = stencilSize;
1045		}
1046
1047		@Override
1048		public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs)
1049		{
1050			EGLConfig closestConfig = null;
1051			int closestDistance = 1000;
1052			for (EGLConfig config : configs)
1053			{
1054				int d = findConfigAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, 0);
1055				int s = findConfigAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0);
1056				if (d >= mDepthSize && s >= mStencilSize)
1057				{
1058					int r = findConfigAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0);
1059					int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);
1060					int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);
1061					int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);
1062					int distance = Math.abs(r - mRedSize) + Math.abs(g - mGreenSize) + Math.abs(b - mBlueSize) + Math.abs(a - mAlphaSize);
1063					if (distance < closestDistance)
1064					{
1065						closestDistance = distance;
1066						closestConfig = config;
1067					}
1068				}
1069			}
1070			return closestConfig;
1071		}
1072
1073		private int findConfigAttrib(EGL10 egl, EGLDisplay display, EGLConfig config, int attribute, int defaultValue)
1074		{
1075
1076			if (egl.eglGetConfigAttrib(display, config, attribute, mValue))
1077			{
1078				return mValue[0];
1079			}
1080			return defaultValue;
1081		}
1082
1083		private int[] mValue;
1084		// Subclasses can adjust these values:
1085		protected int mRedSize;
1086		protected int mGreenSize;
1087		protected int mBlueSize;
1088		protected int mAlphaSize;
1089		protected int mDepthSize;
1090		protected int mStencilSize;
1091	}
1092
1093	/**
1094	 * This class will choose a supported surface as close to RGB565 as
1095	 * possible, with or without a depth buffer.
1096	 * 
1097	 */
1098	public static class SimpleEGLConfigChooser extends ComponentSizeChooser
1099	{
1100		public SimpleEGLConfigChooser(boolean withDepthBuffer)
1101		{
1102			super(4, 4, 4, 0, withDepthBuffer ? 16 : 0, 0);
1103			// Adjust target values. This way we'll accept a 4444 or
1104			// 555 buffer if there's no 565 buffer available.
1105			mRedSize = 5;
1106			mGreenSize = 6;
1107			mBlueSize = 5;
1108		}
1109	}
1110}