Home | History | Annotate | Download | only in android
      1 /*******************************************************************************
      2  * Copyright 2011 See AUTHORS file.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *   http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  ******************************************************************************/
     16 
     17 package com.badlogic.gdx.backends.android;
     18 
     19 import java.text.NumberFormat;
     20 import java.text.ParseException;
     21 
     22 import javax.microedition.khronos.egl.EGL10;
     23 import javax.microedition.khronos.egl.EGLConfig;
     24 import javax.microedition.khronos.egl.EGLContext;
     25 import javax.microedition.khronos.egl.EGLDisplay;
     26 import javax.microedition.khronos.opengles.GL10;
     27 
     28 import android.opengl.GLSurfaceView;
     29 import android.opengl.GLSurfaceView.EGLConfigChooser;
     30 import android.opengl.GLSurfaceView.Renderer;
     31 import android.util.DisplayMetrics;
     32 import android.view.Display;
     33 import android.view.View;
     34 import android.view.WindowManager.LayoutParams;
     35 
     36 import com.badlogic.gdx.Application;
     37 import com.badlogic.gdx.Gdx;
     38 import com.badlogic.gdx.Graphics;
     39 import com.badlogic.gdx.LifecycleListener;
     40 import com.badlogic.gdx.backends.android.surfaceview.GLSurfaceView20;
     41 import com.badlogic.gdx.backends.android.surfaceview.GLSurfaceView20API18;
     42 import com.badlogic.gdx.backends.android.surfaceview.GLSurfaceViewAPI18;
     43 import com.badlogic.gdx.backends.android.surfaceview.GdxEglConfigChooser;
     44 import com.badlogic.gdx.backends.android.surfaceview.ResolutionStrategy;
     45 import com.badlogic.gdx.graphics.Cubemap;
     46 import com.badlogic.gdx.graphics.Cursor;
     47 import com.badlogic.gdx.graphics.Cursor.SystemCursor;
     48 import com.badlogic.gdx.graphics.GL20;
     49 import com.badlogic.gdx.graphics.GL30;
     50 import com.badlogic.gdx.graphics.Mesh;
     51 import com.badlogic.gdx.graphics.Pixmap;
     52 import com.badlogic.gdx.graphics.Texture;
     53 import com.badlogic.gdx.graphics.TextureArray;
     54 import com.badlogic.gdx.graphics.glutils.FrameBuffer;
     55 import com.badlogic.gdx.graphics.glutils.GLVersion;
     56 import com.badlogic.gdx.graphics.glutils.ShaderProgram;
     57 import com.badlogic.gdx.math.WindowedMean;
     58 import com.badlogic.gdx.utils.Array;
     59 import com.badlogic.gdx.utils.GdxRuntimeException;
     60 import com.badlogic.gdx.utils.SnapshotArray;
     61 
     62 /** An implementation of {@link Graphics} for Android.
     63  *
     64  * @author mzechner */
     65 public class AndroidGraphics implements Graphics, Renderer {
     66 
     67 	private static final String LOG_TAG = "AndroidGraphics";
     68 
     69 	/** When {@link AndroidFragmentApplication#onPause()} or {@link AndroidApplication#onPause()} call
     70 	 * {@link AndroidGraphics#pause()} they <b>MUST</b> enforce continuous rendering. If not, {@link #onDrawFrame(GL10)} will not
     71 	 * be called in the GLThread while {@link #pause()} is sleeping in the Android UI Thread which will cause the
     72 	 * {@link AndroidGraphics#pause} variable never be set to false. As a result, the {@link AndroidGraphics#pause()} method will
     73 	 * kill the current process to avoid ANR */
     74 	static volatile boolean enforceContinuousRendering = false;
     75 
     76 	final View view;
     77 	int width;
     78 	int height;
     79 	AndroidApplicationBase app;
     80 	GL20 gl20;
     81 	GL30 gl30;
     82 	EGLContext eglContext;
     83 	GLVersion glVersion;
     84 	String extensions;
     85 
     86 	protected long lastFrameTime = System.nanoTime();
     87 	protected float deltaTime = 0;
     88 	protected long frameStart = System.nanoTime();
     89 	protected long frameId = -1;
     90 	protected int frames = 0;
     91 	protected int fps;
     92 	protected WindowedMean mean = new WindowedMean(5);
     93 
     94 	volatile boolean created = false;
     95 	volatile boolean running = false;
     96 	volatile boolean pause = false;
     97 	volatile boolean resume = false;
     98 	volatile boolean destroy = false;
     99 
    100 	private float ppiX = 0;
    101 	private float ppiY = 0;
    102 	private float ppcX = 0;
    103 	private float ppcY = 0;
    104 	private float density = 1;
    105 
    106 	protected final AndroidApplicationConfiguration config;
    107 	private BufferFormat bufferFormat = new BufferFormat(5, 6, 5, 0, 16, 0, 0, false);
    108 	private boolean isContinuous = true;
    109 
    110 	public AndroidGraphics (AndroidApplicationBase application, AndroidApplicationConfiguration config,
    111 		ResolutionStrategy resolutionStrategy) {
    112 		this(application, config, resolutionStrategy, true);
    113 	}
    114 
    115 	public AndroidGraphics (AndroidApplicationBase application, AndroidApplicationConfiguration config,
    116 		ResolutionStrategy resolutionStrategy, boolean focusableView) {
    117 		this.config = config;
    118 		this.app = application;
    119 		view = createGLSurfaceView(application, resolutionStrategy);
    120 		preserveEGLContextOnPause();
    121 		if (focusableView) {
    122 			view.setFocusable(true);
    123 			view.setFocusableInTouchMode(true);
    124 		}
    125 	}
    126 
    127 	protected void preserveEGLContextOnPause () {
    128 		int sdkVersion = android.os.Build.VERSION.SDK_INT;
    129 		if ((sdkVersion >= 11 && view instanceof GLSurfaceView20) || view instanceof GLSurfaceView20API18) {
    130 			try {
    131 				view.getClass().getMethod("setPreserveEGLContextOnPause", boolean.class).invoke(view, true);
    132 			} catch (Exception e) {
    133 				Gdx.app.log(LOG_TAG, "Method GLSurfaceView.setPreserveEGLContextOnPause not found");
    134 			}
    135 		}
    136 	}
    137 
    138 	protected View createGLSurfaceView (AndroidApplicationBase application, final ResolutionStrategy resolutionStrategy) {
    139 		if (!checkGL20()) throw new GdxRuntimeException("Libgdx requires OpenGL ES 2.0");
    140 
    141 		EGLConfigChooser configChooser = getEglConfigChooser();
    142 		int sdkVersion = android.os.Build.VERSION.SDK_INT;
    143 		if (sdkVersion <= 10 && config.useGLSurfaceView20API18) {
    144 			GLSurfaceView20API18 view = new GLSurfaceView20API18(application.getContext(), resolutionStrategy);
    145 			if (configChooser != null)
    146 				view.setEGLConfigChooser(configChooser);
    147 			else
    148 				view.setEGLConfigChooser(config.r, config.g, config.b, config.a, config.depth, config.stencil);
    149 			view.setRenderer(this);
    150 			return view;
    151 		} else {
    152 			GLSurfaceView20 view = new GLSurfaceView20(application.getContext(), resolutionStrategy, config.useGL30 ? 3 : 2);
    153 			if (configChooser != null)
    154 				view.setEGLConfigChooser(configChooser);
    155 			else
    156 				view.setEGLConfigChooser(config.r, config.g, config.b, config.a, config.depth, config.stencil);
    157 			view.setRenderer(this);
    158 			return view;
    159 		}
    160 	}
    161 
    162 	public void onPauseGLSurfaceView () {
    163 		if (view != null) {
    164 			if (view instanceof GLSurfaceViewAPI18) ((GLSurfaceViewAPI18)view).onPause();
    165 			if (view instanceof GLSurfaceView) ((GLSurfaceView)view).onPause();
    166 		}
    167 	}
    168 
    169 	public void onResumeGLSurfaceView () {
    170 		if (view != null) {
    171 			if (view instanceof GLSurfaceViewAPI18) ((GLSurfaceViewAPI18)view).onResume();
    172 			if (view instanceof GLSurfaceView) ((GLSurfaceView)view).onResume();
    173 		}
    174 	}
    175 
    176 	protected EGLConfigChooser getEglConfigChooser () {
    177 		return new GdxEglConfigChooser(config.r, config.g, config.b, config.a, config.depth, config.stencil, config.numSamples);
    178 	}
    179 
    180 	private void updatePpi () {
    181 		DisplayMetrics metrics = new DisplayMetrics();
    182 		app.getWindowManager().getDefaultDisplay().getMetrics(metrics);
    183 
    184 		ppiX = metrics.xdpi;
    185 		ppiY = metrics.ydpi;
    186 		ppcX = metrics.xdpi / 2.54f;
    187 		ppcY = metrics.ydpi / 2.54f;
    188 		density = metrics.density;
    189 	}
    190 
    191 	protected boolean checkGL20 () {
    192 		EGL10 egl = (EGL10)EGLContext.getEGL();
    193 		EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
    194 
    195 		int[] version = new int[2];
    196 		egl.eglInitialize(display, version);
    197 
    198 		int EGL_OPENGL_ES2_BIT = 4;
    199 		int[] configAttribs = {EGL10.EGL_RED_SIZE, 4, EGL10.EGL_GREEN_SIZE, 4, EGL10.EGL_BLUE_SIZE, 4, EGL10.EGL_RENDERABLE_TYPE,
    200 			EGL_OPENGL_ES2_BIT, EGL10.EGL_NONE};
    201 
    202 		EGLConfig[] configs = new EGLConfig[10];
    203 		int[] num_config = new int[1];
    204 		egl.eglChooseConfig(display, configAttribs, configs, 10, num_config);
    205 		egl.eglTerminate(display);
    206 		return num_config[0] > 0;
    207 	}
    208 
    209 	/** {@inheritDoc} */
    210 	@Override
    211 	public GL20 getGL20 () {
    212 		return gl20;
    213 	}
    214 
    215 	/** {@inheritDoc} */
    216 	@Override
    217 	public int getHeight () {
    218 		return height;
    219 	}
    220 
    221 	/** {@inheritDoc} */
    222 	@Override
    223 	public int getWidth () {
    224 		return width;
    225 	}
    226 
    227 	@Override
    228 	public int getBackBufferWidth () {
    229 		return width;
    230 	}
    231 
    232 	@Override
    233 	public int getBackBufferHeight () {
    234 		return height;
    235 	}
    236 
    237 	/** This instantiates the GL10, GL11 and GL20 instances. Includes the check for certain devices that pretend to support GL11 but
    238 	 * fuck up vertex buffer objects. This includes the pixelflinger which segfaults when buffers are deleted as well as the
    239 	 * Motorola CLIQ and the Samsung Behold II.
    240 	 *
    241 	 * @param gl */
    242 	private void setupGL (javax.microedition.khronos.opengles.GL10 gl) {
    243 		String versionString = gl.glGetString(GL10.GL_VERSION);
    244 		String vendorString = gl.glGetString(GL10.GL_VENDOR);
    245 		String rendererString = gl.glGetString(GL10.GL_RENDERER);
    246 		glVersion = new GLVersion(Application.ApplicationType.Android, versionString, vendorString, rendererString);
    247 		if (config.useGL30 && glVersion.getMajorVersion() > 2) {
    248 			if (gl30 != null) return;
    249 			gl20 = gl30 = new AndroidGL30();
    250 
    251 			Gdx.gl = gl30;
    252 			Gdx.gl20 = gl30;
    253 			Gdx.gl30 = gl30;
    254 		} else {
    255 			if (gl20 != null) return;
    256 			gl20 = new AndroidGL20();
    257 
    258 			Gdx.gl = gl20;
    259 			Gdx.gl20 = gl20;
    260 		}
    261 
    262 		Gdx.app.log(LOG_TAG, "OGL renderer: " + gl.glGetString(GL10.GL_RENDERER));
    263 		Gdx.app.log(LOG_TAG, "OGL vendor: " + gl.glGetString(GL10.GL_VENDOR));
    264 		Gdx.app.log(LOG_TAG, "OGL version: " + gl.glGetString(GL10.GL_VERSION));
    265 		Gdx.app.log(LOG_TAG, "OGL extensions: " + gl.glGetString(GL10.GL_EXTENSIONS));
    266 	}
    267 
    268 	@Override
    269 	public void onSurfaceChanged (javax.microedition.khronos.opengles.GL10 gl, int width, int height) {
    270 		this.width = width;
    271 		this.height = height;
    272 		updatePpi();
    273 		gl.glViewport(0, 0, this.width, this.height);
    274 		if (created == false) {
    275 			app.getApplicationListener().create();
    276 			created = true;
    277 			synchronized (this) {
    278 				running = true;
    279 			}
    280 		}
    281 		app.getApplicationListener().resize(width, height);
    282 	}
    283 
    284 	@Override
    285 	public void onSurfaceCreated (javax.microedition.khronos.opengles.GL10 gl, EGLConfig config) {
    286 		eglContext = ((EGL10)EGLContext.getEGL()).eglGetCurrentContext();
    287 		setupGL(gl);
    288 		logConfig(config);
    289 		updatePpi();
    290 
    291 		Mesh.invalidateAllMeshes(app);
    292 		Texture.invalidateAllTextures(app);
    293 		Cubemap.invalidateAllCubemaps(app);
    294 		TextureArray.invalidateAllTextureArrays(app);
    295 		ShaderProgram.invalidateAllShaderPrograms(app);
    296 		FrameBuffer.invalidateAllFrameBuffers(app);
    297 
    298 		logManagedCachesStatus();
    299 
    300 		Display display = app.getWindowManager().getDefaultDisplay();
    301 		this.width = display.getWidth();
    302 		this.height = display.getHeight();
    303 		this.mean = new WindowedMean(5);
    304 		this.lastFrameTime = System.nanoTime();
    305 
    306 		gl.glViewport(0, 0, this.width, this.height);
    307 	}
    308 
    309 	private void logConfig (EGLConfig config) {
    310 		EGL10 egl = (EGL10)EGLContext.getEGL();
    311 		EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
    312 		int r = getAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0);
    313 		int g = getAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);
    314 		int b = getAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);
    315 		int a = getAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);
    316 		int d = getAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, 0);
    317 		int s = getAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0);
    318 		int samples = Math.max(getAttrib(egl, display, config, EGL10.EGL_SAMPLES, 0),
    319 			getAttrib(egl, display, config, GdxEglConfigChooser.EGL_COVERAGE_SAMPLES_NV, 0));
    320 		boolean coverageSample = getAttrib(egl, display, config, GdxEglConfigChooser.EGL_COVERAGE_SAMPLES_NV, 0) != 0;
    321 
    322 		Gdx.app.log(LOG_TAG, "framebuffer: (" + r + ", " + g + ", " + b + ", " + a + ")");
    323 		Gdx.app.log(LOG_TAG, "depthbuffer: (" + d + ")");
    324 		Gdx.app.log(LOG_TAG, "stencilbuffer: (" + s + ")");
    325 		Gdx.app.log(LOG_TAG, "samples: (" + samples + ")");
    326 		Gdx.app.log(LOG_TAG, "coverage sampling: (" + coverageSample + ")");
    327 
    328 		bufferFormat = new BufferFormat(r, g, b, a, d, s, samples, coverageSample);
    329 	}
    330 
    331 	int[] value = new int[1];
    332 
    333 	private int getAttrib (EGL10 egl, EGLDisplay display, EGLConfig config, int attrib, int defValue) {
    334 		if (egl.eglGetConfigAttrib(display, config, attrib, value)) {
    335 			return value[0];
    336 		}
    337 		return defValue;
    338 	}
    339 
    340 	Object synch = new Object();
    341 
    342 	void resume () {
    343 		synchronized (synch) {
    344 			running = true;
    345 			resume = true;
    346 		}
    347 	}
    348 
    349 	void pause () {
    350 		synchronized (synch) {
    351 			if (!running) return;
    352 			running = false;
    353 			pause = true;
    354 			while (pause) {
    355 				try {
    356 					// TODO: fix deadlock race condition with quick resume/pause.
    357 					// Temporary workaround:
    358 					// Android ANR time is 5 seconds, so wait up to 4 seconds before assuming
    359 					// deadlock and killing process. This can easily be triggered by opening the
    360 					// Recent Apps list and then double-tapping the Recent Apps button with
    361 					// ~500ms between taps.
    362 					synch.wait(4000);
    363 					if (pause) {
    364 						// pause will never go false if onDrawFrame is never called by the GLThread
    365 						// when entering this method, we MUST enforce continuous rendering
    366 						Gdx.app.error(LOG_TAG, "waiting for pause synchronization took too long; assuming deadlock and killing");
    367 						android.os.Process.killProcess(android.os.Process.myPid());
    368 					}
    369 				} catch (InterruptedException ignored) {
    370 					Gdx.app.log(LOG_TAG, "waiting for pause synchronization failed!");
    371 				}
    372 			}
    373 		}
    374 	}
    375 
    376 	void destroy () {
    377 		synchronized (synch) {
    378 			running = false;
    379 			destroy = true;
    380 
    381 			while (destroy) {
    382 				try {
    383 					synch.wait();
    384 				} catch (InterruptedException ex) {
    385 					Gdx.app.log(LOG_TAG, "waiting for destroy synchronization failed!");
    386 				}
    387 			}
    388 		}
    389 	}
    390 
    391 	@Override
    392 	public void onDrawFrame (javax.microedition.khronos.opengles.GL10 gl) {
    393 		long time = System.nanoTime();
    394 		deltaTime = (time - lastFrameTime) / 1000000000.0f;
    395 		lastFrameTime = time;
    396 
    397 		// After pause deltaTime can have somewhat huge value that destabilizes the mean, so let's cut it off
    398 		if (!resume) {
    399 			mean.addValue(deltaTime);
    400 		} else {
    401 			deltaTime = 0;
    402 		}
    403 
    404 		boolean lrunning = false;
    405 		boolean lpause = false;
    406 		boolean ldestroy = false;
    407 		boolean lresume = false;
    408 
    409 		synchronized (synch) {
    410 			lrunning = running;
    411 			lpause = pause;
    412 			ldestroy = destroy;
    413 			lresume = resume;
    414 
    415 			if (resume) {
    416 				resume = false;
    417 			}
    418 
    419 			if (pause) {
    420 				pause = false;
    421 				synch.notifyAll();
    422 			}
    423 
    424 			if (destroy) {
    425 				destroy = false;
    426 				synch.notifyAll();
    427 			}
    428 		}
    429 
    430 		if (lresume) {
    431 			SnapshotArray<LifecycleListener> lifecycleListeners = app.getLifecycleListeners();
    432 			synchronized (lifecycleListeners) {
    433 				LifecycleListener[] listeners = lifecycleListeners.begin();
    434 				for (int i = 0, n = lifecycleListeners.size; i < n; ++i) {
    435 					listeners[i].resume();
    436 				}
    437 				lifecycleListeners.end();
    438 			}
    439 			app.getApplicationListener().resume();
    440 			Gdx.app.log(LOG_TAG, "resumed");
    441 		}
    442 
    443 		if (lrunning) {
    444 			synchronized (app.getRunnables()) {
    445 				app.getExecutedRunnables().clear();
    446 				app.getExecutedRunnables().addAll(app.getRunnables());
    447 				app.getRunnables().clear();
    448 			}
    449 
    450 			for (int i = 0; i < app.getExecutedRunnables().size; i++) {
    451 				try {
    452 					app.getExecutedRunnables().get(i).run();
    453 				} catch (Throwable t) {
    454 					t.printStackTrace();
    455 				}
    456 			}
    457 			app.getInput().processEvents();
    458 			frameId++;
    459 			app.getApplicationListener().render();
    460 		}
    461 
    462 		if (lpause) {
    463 			SnapshotArray<LifecycleListener> lifecycleListeners = app.getLifecycleListeners();
    464 			synchronized (lifecycleListeners) {
    465 				LifecycleListener[] listeners = lifecycleListeners.begin();
    466 				for (int i = 0, n = lifecycleListeners.size; i < n; ++i) {
    467 					listeners[i].pause();
    468 				}
    469 			}
    470 			app.getApplicationListener().pause();
    471 			Gdx.app.log(LOG_TAG, "paused");
    472 		}
    473 
    474 		if (ldestroy) {
    475 			SnapshotArray<LifecycleListener> lifecycleListeners = app.getLifecycleListeners();
    476 			synchronized (lifecycleListeners) {
    477 				LifecycleListener[] listeners = lifecycleListeners.begin();
    478 				for (int i = 0, n = lifecycleListeners.size; i < n; ++i) {
    479 					listeners[i].dispose();
    480 				}
    481 			}
    482 			app.getApplicationListener().dispose();
    483 			Gdx.app.log(LOG_TAG, "destroyed");
    484 		}
    485 
    486 		if (time - frameStart > 1000000000) {
    487 			fps = frames;
    488 			frames = 0;
    489 			frameStart = time;
    490 		}
    491 		frames++;
    492 	}
    493 
    494 	@Override
    495 	public long getFrameId () {
    496 		return frameId;
    497 	}
    498 
    499 	/** {@inheritDoc} */
    500 	@Override
    501 	public float getDeltaTime () {
    502 		return mean.getMean() == 0 ? deltaTime : mean.getMean();
    503 	}
    504 
    505 	@Override
    506 	public float getRawDeltaTime () {
    507 		return deltaTime;
    508 	}
    509 
    510 	/** {@inheritDoc} */
    511 	@Override
    512 	public GraphicsType getType () {
    513 		return GraphicsType.AndroidGL;
    514 	}
    515 
    516 	/** {@inheritDoc} */
    517 	@Override
    518 	public GLVersion getGLVersion () {
    519 		return glVersion;
    520 	}
    521 
    522 	/** {@inheritDoc} */
    523 	@Override
    524 	public int getFramesPerSecond () {
    525 		return fps;
    526 	}
    527 
    528 	public void clearManagedCaches () {
    529 		Mesh.clearAllMeshes(app);
    530 		Texture.clearAllTextures(app);
    531 		Cubemap.clearAllCubemaps(app);
    532 		TextureArray.clearAllTextureArrays(app);
    533 		ShaderProgram.clearAllShaderPrograms(app);
    534 		FrameBuffer.clearAllFrameBuffers(app);
    535 
    536 		logManagedCachesStatus();
    537 	}
    538 
    539 	protected void logManagedCachesStatus () {
    540 		Gdx.app.log(LOG_TAG, Mesh.getManagedStatus());
    541 		Gdx.app.log(LOG_TAG, Texture.getManagedStatus());
    542 		Gdx.app.log(LOG_TAG, Cubemap.getManagedStatus());
    543 		Gdx.app.log(LOG_TAG, ShaderProgram.getManagedStatus());
    544 		Gdx.app.log(LOG_TAG, FrameBuffer.getManagedStatus());
    545 	}
    546 
    547 	public View getView () {
    548 		return view;
    549 	}
    550 
    551 	@Override
    552 	public float getPpiX () {
    553 		return ppiX;
    554 	}
    555 
    556 	@Override
    557 	public float getPpiY () {
    558 		return ppiY;
    559 	}
    560 
    561 	@Override
    562 	public float getPpcX () {
    563 		return ppcX;
    564 	}
    565 
    566 	@Override
    567 	public float getPpcY () {
    568 		return ppcY;
    569 	}
    570 
    571 	@Override
    572 	public float getDensity () {
    573 		return density;
    574 	}
    575 
    576 	@Override
    577 	public boolean supportsDisplayModeChange () {
    578 		return false;
    579 	}
    580 
    581 	@Override
    582 	public boolean setFullscreenMode (DisplayMode displayMode) {
    583 		return false;
    584 	}
    585 
    586 	@Override
    587 	public Monitor getPrimaryMonitor () {
    588 		return new AndroidMonitor(0, 0, "Primary Monitor");
    589 	}
    590 
    591 	@Override
    592 	public Monitor getMonitor () {
    593 		return getPrimaryMonitor();
    594 	}
    595 
    596 	@Override
    597 	public Monitor[] getMonitors () {
    598 		return new Monitor[] { getPrimaryMonitor() };
    599 	}
    600 
    601 	@Override
    602 	public DisplayMode[] getDisplayModes (Monitor monitor) {
    603 		return getDisplayModes();
    604 	}
    605 
    606 	@Override
    607 	public DisplayMode getDisplayMode (Monitor monitor) {
    608 		return getDisplayMode();
    609 	}
    610 
    611 	@Override
    612 	public DisplayMode[] getDisplayModes () {
    613 		return new DisplayMode[] {getDisplayMode()};
    614 	}
    615 
    616 	@Override
    617 	public boolean setWindowedMode (int width, int height) {
    618 		return false;
    619 	}
    620 
    621 	@Override
    622 	public void setTitle (String title) {
    623 
    624 	}
    625 
    626 	@Override
    627 	public void setUndecorated (boolean undecorated) {
    628 		final int mask = (undecorated) ? 1 : 0;
    629 		app.getApplicationWindow().setFlags(LayoutParams.FLAG_FULLSCREEN, mask);
    630 	}
    631 
    632 	@Override
    633 	public void setResizable (boolean resizable) {
    634 
    635 	}
    636 
    637 	@Override
    638 	public DisplayMode getDisplayMode () {
    639 		DisplayMetrics metrics = new DisplayMetrics();
    640 		app.getWindowManager().getDefaultDisplay().getMetrics(metrics);
    641 		return new AndroidDisplayMode(metrics.widthPixels, metrics.heightPixels, 0, 0);
    642 	}
    643 
    644 	@Override
    645 	public BufferFormat getBufferFormat () {
    646 		return bufferFormat;
    647 	}
    648 
    649 	@Override
    650 	public void setVSync (boolean vsync) {
    651 	}
    652 
    653 	@Override
    654 	public boolean supportsExtension (String extension) {
    655 		if (extensions == null) extensions = Gdx.gl.glGetString(GL10.GL_EXTENSIONS);
    656 		return extensions.contains(extension);
    657 	}
    658 
    659 	@Override
    660 	public void setContinuousRendering (boolean isContinuous) {
    661 		if (view != null) {
    662 			// ignore setContinuousRendering(false) while pausing
    663 			this.isContinuous = enforceContinuousRendering || isContinuous;
    664 			int renderMode = this.isContinuous ? GLSurfaceView.RENDERMODE_CONTINUOUSLY : GLSurfaceView.RENDERMODE_WHEN_DIRTY;
    665 			if (view instanceof GLSurfaceViewAPI18) ((GLSurfaceViewAPI18)view).setRenderMode(renderMode);
    666 			if (view instanceof GLSurfaceView) ((GLSurfaceView)view).setRenderMode(renderMode);
    667 			mean.clear();
    668 		}
    669 	}
    670 
    671 	@Override
    672 	public boolean isContinuousRendering () {
    673 		return isContinuous;
    674 	}
    675 
    676 	@Override
    677 	public void requestRendering () {
    678 		if (view != null) {
    679 			if (view instanceof GLSurfaceViewAPI18) ((GLSurfaceViewAPI18)view).requestRender();
    680 			if (view instanceof GLSurfaceView) ((GLSurfaceView)view).requestRender();
    681 		}
    682 	}
    683 
    684 	@Override
    685 	public boolean isFullscreen () {
    686 		return true;
    687 	}
    688 
    689 	@Override
    690 	public boolean isGL30Available () {
    691 		return gl30 != null;
    692 	}
    693 
    694 	@Override
    695 	public GL30 getGL30 () {
    696 		return gl30;
    697 	}
    698 
    699 	@Override
    700 	public Cursor newCursor (Pixmap pixmap, int xHotspot, int yHotspot) {
    701 		return null;
    702 	}
    703 
    704 	@Override
    705 	public void setCursor (Cursor cursor) {
    706 	}
    707 
    708 	@Override
    709 	public void setSystemCursor (SystemCursor systemCursor) {
    710 	}
    711 
    712 	private class AndroidDisplayMode extends DisplayMode {
    713 		protected AndroidDisplayMode (int width, int height, int refreshRate, int bitsPerPixel) {
    714 			super(width, height, refreshRate, bitsPerPixel);
    715 		}
    716 	}
    717 
    718 	private class AndroidMonitor extends Monitor {
    719 		public AndroidMonitor (int virtualX, int virtualY, String name) {
    720 			super(virtualX, virtualY, name);
    721 		}
    722 	}
    723 }
    724