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 }