blob: 0b784154a32085f8742b0cdbcbc12254f9f9186c [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <jni.h>
#include <errno.h>
#include <poll.h>
#include <pthread.h>
#include <sched.h>
#include <string.h>
#include <sys/resource.h>
#include "glutils.h"
// --------------------------------------------------------------------
// Rendering and input engine thread
// --------------------------------------------------------------------
struct engine {
pthread_mutex_t mutex;
pthread_cond_t cond;
int msgread;
int msgwrite;
ANativeActivity* activity;
pthread_t thread;
int running;
int destroyed;
AInputQueue* inputQueue;
ANativeWindow* window;
AInputQueue* pendingInputQueue;
ANativeWindow* pendingWindow;
// private to engine thread.
int animating;
EGLDisplay display;
EGLSurface surface;
EGLContext context;
int32_t width;
int32_t height;
float angle;
int32_t x;
int32_t y;
};
enum {
ENGINE_CMD_INPUT_CHANGED,
ENGINE_CMD_WINDOW_CHANGED,
ENGINE_CMD_GAINED_FOCUS,
ENGINE_CMD_LOST_FOCUS,
ENGINE_CMD_DESTROY,
};
static int engine_init_display(struct engine* engine) {
// initialize opengl and egl
const EGLint attribs[] = {
EGL_DEPTH_SIZE, 16,
EGL_NONE
};
EGLint w, h, dummy;
EGLint numConfigs;
EGLConfig config;
EGLSurface surface;
EGLContext context;
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(display, 0, 0);
selectConfigForNativeWindow(display, attribs, engine->window, &config);
surface = eglCreateWindowSurface(display, config, engine->window, NULL);
context = eglCreateContext(display, config, NULL, NULL);
eglQuerySurface(display, surface, EGL_WIDTH, &w);
eglQuerySurface(display, surface, EGL_HEIGHT, &h);
if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) {
LOGW("Unable to eglMakeCurrent");
return -1;
}
engine->display = display;
engine->context = context;
engine->surface = surface;
engine->width = w;
engine->height = h;
engine->angle = 0;
// Initialize GL state.
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
glEnable(GL_CULL_FACE);
glShadeModel(GL_SMOOTH);
glEnable(GL_DEPTH_TEST);
return 0;
}
static void engine_draw_frame(struct engine* engine) {
if (engine->display == NULL) {
// No display.
return;
}
glClearColor(((float)engine->x)/engine->width, engine->angle,
((float)engine->y)/engine->height, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
#if 0
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0, 0, -3.0f);
glRotatef(engine->angle, 0, 1, 0);
glRotatef(engine->angle*0.25f, 1, 0, 0);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
//mCube.draw(gl);
glRotatef(engine->angle*2.0f, 0, 1, 1);
glTranslatef(0.5f, 0.5f, 0.5f);
//mCube.draw(gl);
#endif
eglSwapBuffers(engine->display, engine->surface);
//engine->angle += 1.2f;
}
static int engine_term_display(struct engine* engine) {
if (engine->display != EGL_NO_DISPLAY) {
eglMakeCurrent(engine->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (engine->context != EGL_NO_CONTEXT) {
eglDestroyContext(engine->display, engine->context);
}
if (engine->surface != EGL_NO_SURFACE) {
eglDestroySurface(engine->display, engine->surface);
}
eglTerminate(engine->display);
}
engine->animating = 0;
engine->display = EGL_NO_DISPLAY;
engine->context = EGL_NO_CONTEXT;
engine->surface = EGL_NO_SURFACE;
}
static void* engine_entry(void* param) {
struct engine* engine = (struct engine*)param;
struct pollfd pfd[2];
int numfd;
pthread_mutex_lock(&engine->mutex);
engine->running = 1;
pthread_cond_broadcast(&engine->cond);
pthread_mutex_unlock(&engine->mutex);
// loop waiting for stuff to do. we wait for input events or
// commands from the main thread.
pfd[0].fd = engine->msgread;
pfd[0].events = POLLIN;
pfd[0].revents = 0;
while (1) {
if (engine->inputQueue != NULL) {
numfd = 2;
pfd[1].fd = AInputQueue_getFd(engine->inputQueue);
pfd[1].events = POLLIN;
} else {
numfd = 1;
}
pfd[0].revents = 0;
pfd[1].revents = 0;
int nfd = poll(pfd, numfd, engine->animating ? 0 : -1);
if (nfd == 0 && engine->animating) {
// There is no work to do -- step next animation.
engine->angle += .01f;
if (engine->angle > 1) {
engine->angle = 0;
}
engine_draw_frame(engine);
}
if (nfd < 0) {
LOGI("Engine error in poll: %s\n", strerror(errno));
// Should cleanly exit!
continue;
}
if (pfd[0].revents == POLLIN) {
int8_t cmd;
if (read(engine->msgread, &cmd, sizeof(cmd)) == sizeof(cmd)) {
switch (cmd) {
case ENGINE_CMD_INPUT_CHANGED:
LOGI("Engine: ENGINE_CMD_INPUT_CHANGED\n");
pthread_mutex_lock(&engine->mutex);
engine->inputQueue = engine->pendingInputQueue;
pthread_cond_broadcast(&engine->cond);
pthread_mutex_unlock(&engine->mutex);
break;
case ENGINE_CMD_WINDOW_CHANGED:
LOGI("Engine: ENGINE_CMD_WINDOW_CHANGED\n");
pthread_mutex_lock(&engine->mutex);
engine_term_display(engine);
engine->window = engine->pendingWindow;
if (engine->window != NULL) {
engine_init_display(engine);
engine_draw_frame(engine);
}
pthread_cond_broadcast(&engine->cond);
pthread_mutex_unlock(&engine->mutex);
break;
case ENGINE_CMD_LOST_FOCUS:
engine->animating = 0;
engine_draw_frame(engine);
break;
case ENGINE_CMD_DESTROY:
LOGI("Engine: ENGINE_CMD_DESTROY\n");
pthread_mutex_lock(&engine->mutex);
engine_term_display(engine);
engine->destroyed = 1;
pthread_cond_broadcast(&engine->cond);
pthread_mutex_unlock(&engine->mutex);
// Can't touch engine object after this.
return NULL; // EXIT THREAD
}
} else {
LOGI("Failure reading engine cmd: %s\n", strerror(errno));
}
} else if (engine->inputQueue != NULL && pfd[1].revents == POLLIN) {
AInputEvent* event = NULL;
if (AInputQueue_getEvent(engine->inputQueue, &event) >= 0) {
LOGI("New input event: type=%d\n", AInputEvent_getType(event));
if (AInputEvent_getType(event) == INPUT_EVENT_TYPE_MOTION) {
engine->animating = 1;
engine->x = AMotionEvent_getX(event, 0);
engine->y = AMotionEvent_getY(event, 0);
engine_draw_frame(engine);
}
AInputQueue_finishEvent(engine->inputQueue, event, 0);
} else {
LOGI("Failure reading next input event: %s\n", strerror(errno));
}
}
}
}
static struct engine* engine_create(ANativeActivity* activity) {
struct engine* engine = (struct engine*)malloc(sizeof(struct engine));
memset(engine, 0, sizeof(struct engine));
engine->activity = activity;
pthread_mutex_init(&engine->mutex, NULL);
pthread_cond_init(&engine->cond, NULL);
int msgpipe[2];
if (pipe(msgpipe)) {
LOGI("could not create pipe: %s", strerror(errno));
}
engine->msgread = msgpipe[0];
engine->msgwrite = msgpipe[1];
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&engine->thread, &attr, engine_entry, engine);
// Wait for thread to start.
pthread_mutex_lock(&engine->mutex);
while (!engine->running) {
pthread_cond_wait(&engine->cond, &engine->mutex);
}
pthread_mutex_unlock(&engine->mutex);
return engine;
}
static void engine_write_cmd(struct engine* engine, int8_t cmd) {
if (write(engine->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) {
LOGI("Failure writing engine cmd: %s\n", strerror(errno));
}
}
static void engine_set_input(struct engine* engine, AInputQueue* inputQueue) {
pthread_mutex_lock(&engine->mutex);
engine->pendingInputQueue = inputQueue;
engine_write_cmd(engine, ENGINE_CMD_INPUT_CHANGED);
while (engine->inputQueue != engine->pendingInputQueue) {
pthread_cond_wait(&engine->cond, &engine->mutex);
}
pthread_mutex_unlock(&engine->mutex);
}
static void engine_set_window(struct engine* engine, ANativeWindow* window) {
pthread_mutex_lock(&engine->mutex);
engine->pendingWindow = window;
engine_write_cmd(engine, ENGINE_CMD_WINDOW_CHANGED);
while (engine->window != engine->pendingWindow) {
pthread_cond_wait(&engine->cond, &engine->mutex);
}
pthread_mutex_unlock(&engine->mutex);
}
static void engine_destroy(struct engine* engine) {
pthread_mutex_lock(&engine->mutex);
engine_write_cmd(engine, ENGINE_CMD_DESTROY);
while (!engine->destroyed) {
pthread_cond_wait(&engine->cond, &engine->mutex);
}
pthread_mutex_unlock(&engine->mutex);
close(engine->msgread);
close(engine->msgwrite);
pthread_cond_destroy(&engine->cond);
pthread_mutex_destroy(&engine->mutex);
}
// --------------------------------------------------------------------
// Native activity interaction (called from main thread)
// --------------------------------------------------------------------
static void onDestroy(ANativeActivity* activity)
{
LOGI("Destroy: %p\n", activity);
engine_destroy((struct engine*)activity->instance);
}
static void onStart(ANativeActivity* activity)
{
LOGI("Start: %p\n", activity);
}
static void onResume(ANativeActivity* activity)
{
LOGI("Resume: %p\n", activity);
}
static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen)
{
LOGI("SaveInstanceState: %p\n", activity);
return NULL;
}
static void onPause(ANativeActivity* activity)
{
LOGI("Pause: %p\n", activity);
}
static void onStop(ANativeActivity* activity)
{
LOGI("Stop: %p\n", activity);
}
static void onLowMemory(ANativeActivity* activity)
{
LOGI("LowMemory: %p\n", activity);
}
static void onWindowFocusChanged(ANativeActivity* activity, int focused)
{
LOGI("WindowFocusChanged: %p -- %d\n", activity, focused);
engine_write_cmd((struct engine*)activity->instance,
focused ? ENGINE_CMD_GAINED_FOCUS : ENGINE_CMD_LOST_FOCUS);
}
static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window)
{
LOGI("NativeWindowCreated: %p -- %p\n", activity, window);
engine_set_window((struct engine*)activity->instance, window);
}
static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window)
{
LOGI("NativeWindowDestroyed: %p -- %p\n", activity, window);
engine_set_window((struct engine*)activity->instance, NULL);
}
static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue)
{
LOGI("InputQueueCreated: %p -- %p\n", activity, queue);
engine_set_input((struct engine*)activity->instance, queue);
}
static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue)
{
LOGI("InputQueueDestroyed: %p -- %p\n", activity, queue);
engine_set_input((struct engine*)activity->instance, NULL);
}
void ANativeActivity_onCreate(ANativeActivity* activity,
void* savedState, size_t savedStateSize)
{
LOGI("Creating: %p\n", activity);
activity->callbacks->onDestroy = onDestroy;
activity->callbacks->onStart = onStart;
activity->callbacks->onResume = onResume;
activity->callbacks->onSaveInstanceState = onSaveInstanceState;
activity->callbacks->onPause = onPause;
activity->callbacks->onStop = onStop;
activity->callbacks->onLowMemory = onLowMemory;
activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
activity->callbacks->onInputQueueCreated = onInputQueueCreated;
activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
activity->instance = engine_create(activity);
}