1/* 2 * Copyright (C) 2010 The Android Open Source Project 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 18//BEGIN_INCLUDE(all) 19#include <jni.h> 20#include <errno.h> 21 22#include <EGL/egl.h> 23#include <GLES/gl.h> 24 25#include <android/sensor.h> 26#include <android/log.h> 27#include <android_native_app_glue.h> 28 29#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "native-activity", __VA_ARGS__)) 30#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "native-activity", __VA_ARGS__)) 31 32/** 33 * Our saved state data. 34 */ 35struct saved_state { 36 float angle; 37 int32_t x; 38 int32_t y; 39}; 40 41/** 42 * Shared state for our app. 43 */ 44struct engine { 45 struct android_app* app; 46 47 ASensorManager* sensorManager; 48 const ASensor* accelerometerSensor; 49 ASensorEventQueue* sensorEventQueue; 50 51 int animating; 52 EGLDisplay display; 53 EGLSurface surface; 54 EGLContext context; 55 int32_t width; 56 int32_t height; 57 struct saved_state state; 58}; 59 60/** 61 * Initialize an EGL context for the current display. 62 */ 63static int engine_init_display(struct engine* engine) { 64 // initialize OpenGL ES and EGL 65 66 /* 67 * Here specify the attributes of the desired configuration. 68 * Below, we select an EGLConfig with at least 8 bits per color 69 * component compatible with on-screen windows 70 */ 71 const EGLint attribs[] = { 72 EGL_SURFACE_TYPE, EGL_WINDOW_BIT, 73 EGL_BLUE_SIZE, 8, 74 EGL_GREEN_SIZE, 8, 75 EGL_RED_SIZE, 8, 76 EGL_NONE 77 }; 78 EGLint w, h, dummy, format; 79 EGLint numConfigs; 80 EGLConfig config; 81 EGLSurface surface; 82 EGLContext context; 83 84 EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); 85 86 eglInitialize(display, 0, 0); 87 88 /* Here, the application chooses the configuration it desires. In this 89 * sample, we have a very simplified selection process, where we pick 90 * the first EGLConfig that matches our criteria */ 91 eglChooseConfig(display, attribs, &config, 1, &numConfigs); 92 93 /* EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is 94 * guaranteed to be accepted by ANativeWindow_setBuffersGeometry(). 95 * As soon as we picked a EGLConfig, we can safely reconfigure the 96 * ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID. */ 97 eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format); 98 99 ANativeWindow_setBuffersGeometry(engine->app->window, 0, 0, format); 100 101 surface = eglCreateWindowSurface(display, config, engine->app->window, NULL); 102 context = eglCreateContext(display, config, NULL, NULL); 103 104 if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) { 105 LOGW("Unable to eglMakeCurrent"); 106 return -1; 107 } 108 109 eglQuerySurface(display, surface, EGL_WIDTH, &w); 110 eglQuerySurface(display, surface, EGL_HEIGHT, &h); 111 112 engine->display = display; 113 engine->context = context; 114 engine->surface = surface; 115 engine->width = w; 116 engine->height = h; 117 engine->state.angle = 0; 118 119 // Initialize GL state. 120 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); 121 glEnable(GL_CULL_FACE); 122 glShadeModel(GL_SMOOTH); 123 glDisable(GL_DEPTH_TEST); 124 125 return 0; 126} 127 128/** 129 * Just the current frame in the display. 130 */ 131static void engine_draw_frame(struct engine* engine) { 132 if (engine->display == NULL) { 133 // No display. 134 return; 135 } 136 137 // Just fill the screen with a color. 138 glClearColor(((float)engine->state.x)/engine->width, engine->state.angle, 139 ((float)engine->state.y)/engine->height, 1); 140 glClear(GL_COLOR_BUFFER_BIT); 141 142 eglSwapBuffers(engine->display, engine->surface); 143} 144 145/** 146 * Tear down the EGL context currently associated with the display. 147 */ 148static void engine_term_display(struct engine* engine) { 149 if (engine->display != EGL_NO_DISPLAY) { 150 eglMakeCurrent(engine->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 151 if (engine->context != EGL_NO_CONTEXT) { 152 eglDestroyContext(engine->display, engine->context); 153 } 154 if (engine->surface != EGL_NO_SURFACE) { 155 eglDestroySurface(engine->display, engine->surface); 156 } 157 eglTerminate(engine->display); 158 } 159 engine->animating = 0; 160 engine->display = EGL_NO_DISPLAY; 161 engine->context = EGL_NO_CONTEXT; 162 engine->surface = EGL_NO_SURFACE; 163} 164 165/** 166 * Process the next input event. 167 */ 168static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) { 169 struct engine* engine = (struct engine*)app->userData; 170 if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) { 171 engine->animating = 1; 172 engine->state.x = AMotionEvent_getX(event, 0); 173 engine->state.y = AMotionEvent_getY(event, 0); 174 return 1; 175 } 176 return 0; 177} 178 179/** 180 * Process the next main command. 181 */ 182static void engine_handle_cmd(struct android_app* app, int32_t cmd) { 183 struct engine* engine = (struct engine*)app->userData; 184 switch (cmd) { 185 case APP_CMD_SAVE_STATE: 186 // The system has asked us to save our current state. Do so. 187 engine->app->savedState = malloc(sizeof(struct saved_state)); 188 *((struct saved_state*)engine->app->savedState) = engine->state; 189 engine->app->savedStateSize = sizeof(struct saved_state); 190 break; 191 case APP_CMD_INIT_WINDOW: 192 // The window is being shown, get it ready. 193 if (engine->app->window != NULL) { 194 engine_init_display(engine); 195 engine_draw_frame(engine); 196 } 197 break; 198 case APP_CMD_TERM_WINDOW: 199 // The window is being hidden or closed, clean it up. 200 engine_term_display(engine); 201 break; 202 case APP_CMD_GAINED_FOCUS: 203 // When our app gains focus, we start monitoring the accelerometer. 204 if (engine->accelerometerSensor != NULL) { 205 ASensorEventQueue_enableSensor(engine->sensorEventQueue, 206 engine->accelerometerSensor); 207 // We'd like to get 60 events per second (in us). 208 ASensorEventQueue_setEventRate(engine->sensorEventQueue, 209 engine->accelerometerSensor, (1000L/60)*1000); 210 } 211 break; 212 case APP_CMD_LOST_FOCUS: 213 // When our app loses focus, we stop monitoring the accelerometer. 214 // This is to avoid consuming battery while not being used. 215 if (engine->accelerometerSensor != NULL) { 216 ASensorEventQueue_disableSensor(engine->sensorEventQueue, 217 engine->accelerometerSensor); 218 } 219 // Also stop animating. 220 engine->animating = 0; 221 engine_draw_frame(engine); 222 break; 223 } 224} 225 226/** 227 * This is the main entry point of a native application that is using 228 * android_native_app_glue. It runs in its own thread, with its own 229 * event loop for receiving input events and doing other things. 230 */ 231void android_main(struct android_app* state) { 232 struct engine engine; 233 234 // Make sure glue isn't stripped. 235 app_dummy(); 236 237 memset(&engine, 0, sizeof(engine)); 238 state->userData = &engine; 239 state->onAppCmd = engine_handle_cmd; 240 state->onInputEvent = engine_handle_input; 241 engine.app = state; 242 243 // Prepare to monitor accelerometer 244 engine.sensorManager = ASensorManager_getInstance(); 245 engine.accelerometerSensor = ASensorManager_getDefaultSensor(engine.sensorManager, 246 ASENSOR_TYPE_ACCELEROMETER); 247 engine.sensorEventQueue = ASensorManager_createEventQueue(engine.sensorManager, 248 state->looper, LOOPER_ID_USER, NULL, NULL); 249 250 if (state->savedState != NULL) { 251 // We are starting with a previous saved state; restore from it. 252 engine.state = *(struct saved_state*)state->savedState; 253 } 254 255 // loop waiting for stuff to do. 256 257 while (1) { 258 // Read all pending events. 259 int ident; 260 int events; 261 struct android_poll_source* source; 262 263 // If not animating, we will block forever waiting for events. 264 // If animating, we loop until all events are read, then continue 265 // to draw the next frame of animation. 266 while ((ident=ALooper_pollAll(engine.animating ? 0 : -1, NULL, &events, 267 (void**)&source)) >= 0) { 268 269 // Process this event. 270 if (source != NULL) { 271 source->process(state, source); 272 } 273 274 // If a sensor has data, process it now. 275 if (ident == LOOPER_ID_USER) { 276 if (engine.accelerometerSensor != NULL) { 277 ASensorEvent event; 278 while (ASensorEventQueue_getEvents(engine.sensorEventQueue, 279 &event, 1) > 0) { 280 LOGI("accelerometer: x=%f y=%f z=%f", 281 event.acceleration.x, event.acceleration.y, 282 event.acceleration.z); 283 } 284 } 285 } 286 287 // Check if we are exiting. 288 if (state->destroyRequested != 0) { 289 engine_term_display(&engine); 290 return; 291 } 292 } 293 294 if (engine.animating) { 295 // Done with events; draw next animation frame. 296 engine.state.angle += .01f; 297 if (engine.state.angle > 1) { 298 engine.state.angle = 0; 299 } 300 301 // Drawing is throttled to the screen update rate, so there 302 // is no need to do timing here. 303 engine_draw_frame(&engine); 304 } 305 } 306} 307//END_INCLUDE(all) 308