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