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