Home | History | Annotate | Download | only in surfaceview
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
      5  * License. You may obtain a copy of the License at
      6  *
      7  * http://www.apache.org/licenses/LICENSE-2.0
      8  *
      9  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
     10  * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
     11  * governing permissions and limitations under the License.
     12  */
     13 
     14 package com.badlogic.gdx.backends.android.surfaceview;
     15 
     16 import javax.microedition.khronos.egl.EGL10;
     17 import javax.microedition.khronos.egl.EGLConfig;
     18 import javax.microedition.khronos.egl.EGLContext;
     19 import javax.microedition.khronos.egl.EGLDisplay;
     20 import android.annotation.TargetApi;
     21 import android.content.Context;
     22 import android.graphics.PixelFormat;
     23 import android.opengl.GLSurfaceView;
     24 import android.os.SystemClock;
     25 import android.util.Log;
     26 import android.view.KeyCharacterMap;
     27 import android.view.KeyEvent;
     28 import android.view.inputmethod.BaseInputConnection;
     29 import android.view.inputmethod.EditorInfo;
     30 import android.view.inputmethod.InputConnection;
     31 
     32 /** <b>This class is a slightly modified copy of GLSurfaceView20 which extends GLSurfaceViewAPI18 instead of the classic GLSurfaceView.
     33  * Especially it is intended to be used on Android 2.x if you need proper support for onAttachedToWindow and onDetachedFromWindow methods.<b>
     34  * <p>It demonstrates how to perform OpenGL ES 2.0 rendering into a GL Surface. Note the following important details:
     35  * <p/>
     36  * - The class must use a custom context factory to enable 2.0 rendering. See ContextFactory class definition below.
     37  * <p/>
     38  * - The class must use a custom EGLConfigChooser to be able to select an EGLConfig that supports 2.0. This is done by providing a
     39  * config specification to eglChooseConfig() that has the attribute EGL10.ELG_RENDERABLE_TYPE containing the EGL_OPENGL_ES2_BIT
     40  * flag set. See ConfigChooser class definition below.
     41  * <p/>
     42  * - The class must select the surface's format, then choose an EGLConfig that matches it exactly (with regards to
     43  * red/green/blue/alpha channels bit depths). Failure to do so would result in an EGL_BAD_MATCH error. */
     44 public class GLSurfaceView20API18 extends GLSurfaceViewAPI18 {
     45 	static String TAG = "GL2JNIView";
     46 	private static final boolean DEBUG = false;
     47 
     48 	final ResolutionStrategy resolutionStrategy;
     49 
     50 	public GLSurfaceView20API18 (Context context, ResolutionStrategy resolutionStrategy) {
     51 		super(context);
     52 		this.resolutionStrategy = resolutionStrategy;
     53 		init(false, 16, 0);
     54 	}
     55 
     56 	public GLSurfaceView20API18 (Context context, boolean translucent, int depth, int stencil, ResolutionStrategy resolutionStrategy) {
     57 		super(context);
     58 		this.resolutionStrategy = resolutionStrategy;
     59 		init(translucent, depth, stencil);
     60 
     61 	}
     62 
     63 	@Override
     64 	protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {
     65 		ResolutionStrategy.MeasuredDimension measures = resolutionStrategy.calcMeasures(widthMeasureSpec, heightMeasureSpec);
     66 		setMeasuredDimension(measures.width, measures.height);
     67 	}
     68 
     69 	@Override
     70 	public InputConnection onCreateInputConnection (EditorInfo outAttrs) {
     71 
     72 		// add this line, the IME can show the selectable words when use chinese input method editor.
     73 		if (outAttrs != null) {
     74 			outAttrs.imeOptions = outAttrs.imeOptions | EditorInfo.IME_FLAG_NO_EXTRACT_UI;
     75 		}
     76 
     77 		BaseInputConnection connection = new BaseInputConnection(this, false) {
     78 			@Override
     79 			public boolean deleteSurroundingText (int beforeLength, int afterLength) {
     80 				int sdkVersion = android.os.Build.VERSION.SDK_INT;
     81 				if (sdkVersion >= 16) {
     82 					/*
     83 					 * In Jelly Bean, they don't send key events for delete. Instead, they send beforeLength = 1, afterLength = 0. So,
     84 					 * we'll just simulate what it used to do.
     85 					 */
     86 					if (beforeLength == 1 && afterLength == 0) {
     87 						sendDownUpKeyEventForBackwardCompatibility(KeyEvent.KEYCODE_DEL);
     88 						return true;
     89 					}
     90 				}
     91 				return super.deleteSurroundingText(beforeLength, afterLength);
     92 			}
     93 
     94 			@TargetApi(16)
     95 			private void sendDownUpKeyEventForBackwardCompatibility (final int code) {
     96 				final long eventTime = SystemClock.uptimeMillis();
     97 				super.sendKeyEvent(new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, code, 0, 0,
     98 					KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE));
     99 				super.sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime, KeyEvent.ACTION_UP, code, 0, 0,
    100 					KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE));
    101 			}
    102 		};
    103 		return connection;
    104 	}
    105 
    106 	private void init (boolean translucent, int depth, int stencil) {
    107 
    108 		/*
    109 		 * By default, GLSurfaceView() creates a RGB_565 opaque surface. If we want a translucent one, we should change the
    110 		 * surface's format here, using PixelFormat.TRANSLUCENT for GL Surfaces is interpreted as any 32-bit surface with alpha by
    111 		 * SurfaceFlinger.
    112 		 */
    113 		if (translucent) {
    114 			this.getHolder().setFormat(PixelFormat.TRANSLUCENT);
    115 		}
    116 
    117 		/*
    118 		 * Setup the context factory for 2.0 rendering. See ContextFactory class definition below
    119 		 */
    120 		setEGLContextFactory(new ContextFactory());
    121 
    122 		/*
    123 		 * We need to choose an EGLConfig that matches the format of our surface exactly. This is going to be done in our custom
    124 		 * config chooser. See ConfigChooser class definition below.
    125 		 */
    126 		setEGLConfigChooser(translucent ? new ConfigChooser(8, 8, 8, 8, depth, stencil) : new ConfigChooser(5, 6, 5, 0, depth,
    127 			stencil));
    128 
    129 		/* Set the renderer responsible for frame rendering */
    130 	}
    131 
    132 	static class ContextFactory implements GLSurfaceViewAPI18.EGLContextFactory {
    133 		private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
    134 
    135 		public EGLContext createContext (EGL10 egl, EGLDisplay display, EGLConfig eglConfig) {
    136 			Log.w(TAG, "creating OpenGL ES 2.0 context");
    137 			checkEglError("Before eglCreateContext", egl);
    138 			int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2,
    139 					GLSurfaceView20.EGL_CONTEXT_PRIORITY_LEVEL_IMG, GLSurfaceView20.EGL_CONTEXT_PRIORITY_LOW_IMG,
    140 					EGL10.EGL_NONE};
    141 			EGLContext context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
    142 			checkEglError("After eglCreateContext", egl);
    143 			return context;
    144 		}
    145 
    146 		public void destroyContext (EGL10 egl, EGLDisplay display, EGLContext context) {
    147 			egl.eglDestroyContext(display, context);
    148 		}
    149 	}
    150 
    151 	static void checkEglError (String prompt, EGL10 egl) {
    152 		int error;
    153 		while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) {
    154 			Log.e(TAG, String.format("%s: EGL error: 0x%x", prompt, error));
    155 		}
    156 	}
    157 
    158 	private static class ConfigChooser implements GLSurfaceView.EGLConfigChooser {
    159 
    160 		public ConfigChooser (int r, int g, int b, int a, int depth, int stencil) {
    161 			mRedSize = r;
    162 			mGreenSize = g;
    163 			mBlueSize = b;
    164 			mAlphaSize = a;
    165 			mDepthSize = depth;
    166 			mStencilSize = stencil;
    167 		}
    168 
    169 		/*
    170 		 * This EGL config specification is used to specify 2.0 rendering. We use a minimum size of 4 bits for red/green/blue, but
    171 		 * will perform actual matching in chooseConfig() below.
    172 		 */
    173 		private static int EGL_OPENGL_ES2_BIT = 4;
    174 		private static int[] s_configAttribs2 = {EGL10.EGL_RED_SIZE, 4, EGL10.EGL_GREEN_SIZE, 4, EGL10.EGL_BLUE_SIZE, 4,
    175 			EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL10.EGL_NONE};
    176 
    177 		public EGLConfig chooseConfig (EGL10 egl, EGLDisplay display) {
    178 
    179 			/*
    180 			 * Get the number of minimally matching EGL configurations
    181 			 */
    182 			int[] num_config = new int[1];
    183 			egl.eglChooseConfig(display, s_configAttribs2, null, 0, num_config);
    184 
    185 			int numConfigs = num_config[0];
    186 
    187 			if (numConfigs <= 0) {
    188 				throw new IllegalArgumentException("No configs match configSpec");
    189 			}
    190 
    191 			/*
    192 			 * Allocate then read the array of minimally matching EGL configs
    193 			 */
    194 			EGLConfig[] configs = new EGLConfig[numConfigs];
    195 			egl.eglChooseConfig(display, s_configAttribs2, configs, numConfigs, num_config);
    196 
    197 			if (DEBUG) {
    198 				printConfigs(egl, display, configs);
    199 			}
    200 			/*
    201 			 * Now return the "best" one
    202 			 */
    203 			return chooseConfig(egl, display, configs);
    204 		}
    205 
    206 		public EGLConfig chooseConfig (EGL10 egl, EGLDisplay display, EGLConfig[] configs) {
    207 			for (EGLConfig config : configs) {
    208 				int d = findConfigAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, 0);
    209 				int s = findConfigAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0);
    210 
    211 				// We need at least mDepthSize and mStencilSize bits
    212 				if (d < mDepthSize || s < mStencilSize) continue;
    213 
    214 				// We want an *exact* match for red/green/blue/alpha
    215 				int r = findConfigAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0);
    216 				int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);
    217 				int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);
    218 				int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);
    219 
    220 				if (r == mRedSize && g == mGreenSize && b == mBlueSize && a == mAlphaSize) return config;
    221 			}
    222 			return null;
    223 		}
    224 
    225 		private int findConfigAttrib (EGL10 egl, EGLDisplay display, EGLConfig config, int attribute, int defaultValue) {
    226 
    227 			if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
    228 				return mValue[0];
    229 			}
    230 			return defaultValue;
    231 		}
    232 
    233 		private void printConfigs (EGL10 egl, EGLDisplay display, EGLConfig[] configs) {
    234 			int numConfigs = configs.length;
    235 			Log.w(TAG, String.format("%d configurations", numConfigs));
    236 			for (int i = 0; i < numConfigs; i++) {
    237 				Log.w(TAG, String.format("Configuration %d:\n", i));
    238 				printConfig(egl, display, configs[i]);
    239 			}
    240 		}
    241 
    242 		private void printConfig (EGL10 egl, EGLDisplay display, EGLConfig config) {
    243 			int[] attributes = {EGL10.EGL_BUFFER_SIZE, EGL10.EGL_ALPHA_SIZE, EGL10.EGL_BLUE_SIZE, EGL10.EGL_GREEN_SIZE,
    244 				EGL10.EGL_RED_SIZE, EGL10.EGL_DEPTH_SIZE, EGL10.EGL_STENCIL_SIZE, EGL10.EGL_CONFIG_CAVEAT, EGL10.EGL_CONFIG_ID,
    245 				EGL10.EGL_LEVEL, EGL10.EGL_MAX_PBUFFER_HEIGHT, EGL10.EGL_MAX_PBUFFER_PIXELS, EGL10.EGL_MAX_PBUFFER_WIDTH,
    246 				EGL10.EGL_NATIVE_RENDERABLE, EGL10.EGL_NATIVE_VISUAL_ID, EGL10.EGL_NATIVE_VISUAL_TYPE,
    247 				0x3030, // EGL10.EGL_PRESERVED_RESOURCES,
    248 				EGL10.EGL_SAMPLES, EGL10.EGL_SAMPLE_BUFFERS, EGL10.EGL_SURFACE_TYPE, EGL10.EGL_TRANSPARENT_TYPE,
    249 				EGL10.EGL_TRANSPARENT_RED_VALUE, EGL10.EGL_TRANSPARENT_GREEN_VALUE, EGL10.EGL_TRANSPARENT_BLUE_VALUE, 0x3039, // EGL10.EGL_BIND_TO_TEXTURE_RGB,
    250 				0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGBA,
    251 				0x303B, // EGL10.EGL_MIN_SWAP_INTERVAL,
    252 				0x303C, // EGL10.EGL_MAX_SWAP_INTERVAL,
    253 				EGL10.EGL_LUMINANCE_SIZE, EGL10.EGL_ALPHA_MASK_SIZE, EGL10.EGL_COLOR_BUFFER_TYPE, EGL10.EGL_RENDERABLE_TYPE, 0x3042 // EGL10.EGL_CONFORMANT
    254 			};
    255 			String[] names = {"EGL_BUFFER_SIZE", "EGL_ALPHA_SIZE", "EGL_BLUE_SIZE", "EGL_GREEN_SIZE", "EGL_RED_SIZE",
    256 				"EGL_DEPTH_SIZE", "EGL_STENCIL_SIZE", "EGL_CONFIG_CAVEAT", "EGL_CONFIG_ID", "EGL_LEVEL", "EGL_MAX_PBUFFER_HEIGHT",
    257 				"EGL_MAX_PBUFFER_PIXELS", "EGL_MAX_PBUFFER_WIDTH", "EGL_NATIVE_RENDERABLE", "EGL_NATIVE_VISUAL_ID",
    258 				"EGL_NATIVE_VISUAL_TYPE", "EGL_PRESERVED_RESOURCES", "EGL_SAMPLES", "EGL_SAMPLE_BUFFERS", "EGL_SURFACE_TYPE",
    259 				"EGL_TRANSPARENT_TYPE", "EGL_TRANSPARENT_RED_VALUE", "EGL_TRANSPARENT_GREEN_VALUE", "EGL_TRANSPARENT_BLUE_VALUE",
    260 				"EGL_BIND_TO_TEXTURE_RGB", "EGL_BIND_TO_TEXTURE_RGBA", "EGL_MIN_SWAP_INTERVAL", "EGL_MAX_SWAP_INTERVAL",
    261 				"EGL_LUMINANCE_SIZE", "EGL_ALPHA_MASK_SIZE", "EGL_COLOR_BUFFER_TYPE", "EGL_RENDERABLE_TYPE", "EGL_CONFORMANT"};
    262 			int[] value = new int[1];
    263 			for (int i = 0; i < attributes.length; i++) {
    264 				int attribute = attributes[i];
    265 				String name = names[i];
    266 				if (egl.eglGetConfigAttrib(display, config, attribute, value)) {
    267 					Log.w(TAG, String.format("  %s: %d\n", name, value[0]));
    268 				} else {
    269 					// Log.w(TAG, String.format("  %s: failed\n", name));
    270 					while (egl.eglGetError() != EGL10.EGL_SUCCESS)
    271 						;
    272 				}
    273 			}
    274 		}
    275 
    276 		// Subclasses can adjust these values:
    277 		protected int mRedSize;
    278 		protected int mGreenSize;
    279 		protected int mBlueSize;
    280 		protected int mAlphaSize;
    281 		protected int mDepthSize;
    282 		protected int mStencilSize;
    283 		private int[] mValue = new int[1];
    284 	}
    285 }