am eefb5c2d: Merge "Update native_activity sample to use new glue code." into gingerbread
Merge commit 'eefb5c2dca087c2b0a0ef90e15db00fb5523d8af' into gingerbread-plus-aosp * commit 'eefb5c2dca087c2b0a0ef90e15db00fb5523d8af': Update native_activity sample to use new glue code.
This commit is contained in:
@@ -534,10 +534,11 @@ struct AInputQueue;
|
||||
typedef struct AInputQueue AInputQueue;
|
||||
|
||||
/*
|
||||
* Add this input queue to a looper for processing.
|
||||
* Add this input queue to a looper for processing. See
|
||||
* ALooper_addFd() for information on the callback and data params.
|
||||
*/
|
||||
void AInputQueue_attachLooper(AInputQueue* queue, ALooper* looper,
|
||||
ALooper_callbackFunc callback, void* data);
|
||||
ALooper_callbackFunc* callback, void* data);
|
||||
|
||||
/*
|
||||
* Remove the input queue from the looper it is currently attached to.
|
||||
|
||||
@@ -24,25 +24,151 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ALooper
|
||||
*
|
||||
* A looper is the state tracking an event loop for a thread.
|
||||
* Loopers do not define event structures or other such things; rather
|
||||
* they are a lower-level facility to attach one or more discrete objects
|
||||
* listening for an event. An "event" here is simply data available on
|
||||
* a file descriptor: each attached object has an associated file descriptor,
|
||||
* and waiting for "events" means (internally) polling on all of these file
|
||||
* descriptors until one or more of them have data available.
|
||||
*
|
||||
* A thread can have only one ALooper associated with it.
|
||||
*/
|
||||
struct ALooper;
|
||||
typedef struct ALooper ALooper;
|
||||
|
||||
/**
|
||||
* For callback-based event loops, this is the prototype of the function
|
||||
* that is called. It is given the file descriptor it is associated with,
|
||||
* a bitmask of the poll events that were triggered (typically POLLIN), and
|
||||
* the data pointer that was originally supplied.
|
||||
*
|
||||
* Implementations should return 1 to continue receiving callbacks, or 0
|
||||
* to have this file descriptor and callback unregistered from the looper.
|
||||
*/
|
||||
typedef int ALooper_callbackFunc(int fd, int events, void* data);
|
||||
|
||||
/**
|
||||
* Return the ALooper associated with the calling thread, or NULL if
|
||||
* there is not one.
|
||||
*/
|
||||
ALooper* ALooper_forThread();
|
||||
|
||||
ALooper* ALooper_prepare();
|
||||
enum {
|
||||
/**
|
||||
* Option for ALooper_prepare: this ALooper will accept calls to
|
||||
* ALooper_addFd() that do not have a callback (that is provide NULL
|
||||
* for the callback). In this case the caller of ALooper_pollOnce()
|
||||
* or ALooper_pollAll() MUST check the return from these functions to
|
||||
* discover when data is available on such fds and process it.
|
||||
*/
|
||||
ALOOPER_PREPARE_ALLOW_NON_CALLBACKS = 1<<0
|
||||
};
|
||||
|
||||
int32_t ALooper_pollOnce(int timeoutMillis);
|
||||
/**
|
||||
* Prepare an ALooper associated with the calling thread, and return it.
|
||||
* If the thread already has an ALooper, it is returned. Otherwise, a new
|
||||
* one is created, associated with the thread, and returned.
|
||||
*
|
||||
* The opts may be ALOOPER_PREPARE_ALLOW_NON_CALLBACKS or 0.
|
||||
*/
|
||||
ALooper* ALooper_prepare(int32_t opts);
|
||||
|
||||
enum {
|
||||
/**
|
||||
* Result from ALooper_pollOnce() and ALooper_pollAll(): one or
|
||||
* more callbacks were executed.
|
||||
*/
|
||||
ALOOPER_POLL_CALLBACK = -1,
|
||||
|
||||
/**
|
||||
* Result from ALooper_pollOnce() and ALooper_pollAll(): the
|
||||
* timeout expired.
|
||||
*/
|
||||
ALOOPER_POLL_TIMEOUT = -2,
|
||||
|
||||
/**
|
||||
* Result from ALooper_pollOnce() and ALooper_pollAll(): an error
|
||||
* occurred.
|
||||
*/
|
||||
ALOOPER_POLL_ERROR = -3,
|
||||
};
|
||||
|
||||
/**
|
||||
* Wait for events to be available, with optional timeout in milliseconds.
|
||||
* Invokes callbacks for all file descriptors on which an event occurred.
|
||||
*
|
||||
* If the timeout is zero, returns immediately without blocking.
|
||||
* If the timeout is negative, waits indefinitely until an event appears.
|
||||
*
|
||||
* Returns ALOOPER_POLL_CALLBACK if a callback was invoked.
|
||||
*
|
||||
* Returns ALOOPER_POLL_TIMEOUT if there was no data before the given
|
||||
* timeout expired.
|
||||
*
|
||||
* Returns ALOPER_POLL_ERROR if an error occurred.
|
||||
*
|
||||
* Returns a value >= 0 containing a file descriptor if it has data
|
||||
* and it has no callback function (requiring the caller here to handle it).
|
||||
* In this (and only this) case outEvents and outData will contain the poll
|
||||
* events and data associated with the fd.
|
||||
*
|
||||
* This method does not return until it has finished invoking the appropriate callbacks
|
||||
* for all file descriptors that were signalled.
|
||||
*/
|
||||
int32_t ALooper_pollOnce(int timeoutMillis, int* outEvents, void** outData);
|
||||
|
||||
/**
|
||||
* Like ALooper_pollOnce(), but performs all pending callbacks until all
|
||||
* data has been consumed or a file descriptor is available with no callback.
|
||||
* This function will never return ALOOPER_POLL_CALLBACK.
|
||||
*/
|
||||
int32_t ALooper_pollAll(int timeoutMillis, int* outEvents, void** outData);
|
||||
|
||||
/**
|
||||
* Acquire a reference on the given ALooper object. This prevents the object
|
||||
* from being deleted until the reference is removed. This is only needed
|
||||
* to safely hand an ALooper from one thread to another.
|
||||
*/
|
||||
void ALooper_acquire(ALooper* looper);
|
||||
|
||||
/**
|
||||
* Remove a reference that was previously acquired with ALooper_acquire().
|
||||
*/
|
||||
void ALooper_release(ALooper* looper);
|
||||
|
||||
void ALooper_setCallback(ALooper* looper, int fd, int events,
|
||||
/**
|
||||
* Add a new file descriptor to be polled by the looper. If the same file
|
||||
* descriptor was previously added, it is replaced.
|
||||
*
|
||||
* "fd" is the file descriptor to be added.
|
||||
* "events" are the poll events to wake up on. Typically this is POLLIN.
|
||||
* "callback" is the function to call when there is an event on the file
|
||||
* descriptor.
|
||||
* "id" is an identifier to associated with this file descriptor, or 0.
|
||||
* "data" is a private data pointer to supply to the callback.
|
||||
*
|
||||
* There are two main uses of this function:
|
||||
*
|
||||
* (1) If "callback" is non-NULL, then
|
||||
* this function will be called when there is data on the file descriptor. It
|
||||
* should execute any events it has pending, appropriately reading from the
|
||||
* file descriptor.
|
||||
*
|
||||
* (2) If "callback" is NULL, the fd will be returned by ALooper_pollOnce
|
||||
* when it has data available, requiring the caller to take care of processing
|
||||
* it.
|
||||
*/
|
||||
void ALooper_addFd(ALooper* looper, int fd, int events,
|
||||
ALooper_callbackFunc* callback, void* data);
|
||||
|
||||
int32_t ALooper_removeCallback(ALooper* looper, int fd);
|
||||
/**
|
||||
* Remove a previously added file descriptor from the looper.
|
||||
*/
|
||||
int32_t ALooper_removeFd(ALooper* looper, int fd);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* 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 <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
|
||||
#include <android/native_activity.h>
|
||||
#include <android/looper.h>
|
||||
|
||||
/**
|
||||
* This is the interface for the standard glue code of a threaded
|
||||
* application. In this model, the application's code is running
|
||||
* in its own thread separate from the main thread of the process.
|
||||
* It is not required that this thread be associated with the Java
|
||||
* VM, although it will need to be in order to make JNI calls any
|
||||
* Java objects.
|
||||
*/
|
||||
struct android_app {
|
||||
// The application can place a pointer to its own state object
|
||||
// here if it likes.
|
||||
void* userData;
|
||||
|
||||
// The ANativeActivity object instance that this app is running in.
|
||||
ANativeActivity* activity;
|
||||
|
||||
// The ALooper associated with the app's thread.
|
||||
ALooper* looper;
|
||||
|
||||
// When non-NULL, this is the input queue from which the app will
|
||||
// receive user input events.
|
||||
AInputQueue* inputQueue;
|
||||
|
||||
// When non-NULL, this is the window surface that the app can draw in.
|
||||
ANativeWindow* window;
|
||||
|
||||
// Current state of the app's activity. May be either APP_CMD_START,
|
||||
// APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP; see below.
|
||||
int activityState;
|
||||
|
||||
// This is non-zero when the application's NativeActivity is being
|
||||
// destroyed and waiting for the app thread to complete.
|
||||
int destroyRequested;
|
||||
|
||||
// -------------------------------------------------
|
||||
// Below are "private" implementation of the glue code.
|
||||
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cond;
|
||||
|
||||
int msgread;
|
||||
int msgwrite;
|
||||
|
||||
pthread_t thread;
|
||||
|
||||
int running;
|
||||
int destroyed;
|
||||
AInputQueue* pendingInputQueue;
|
||||
ANativeWindow* pendingWindow;
|
||||
};
|
||||
|
||||
enum {
|
||||
/**
|
||||
* Looper data ID of commands coming from the app's main thread.
|
||||
* These can be retrieved and processed with android_app_read_cmd()
|
||||
* and android_app_exec_cmd().
|
||||
*/
|
||||
LOOPER_ID_MAIN = 1,
|
||||
|
||||
/**
|
||||
* Looper data ID of events coming from the AInputQueue of the
|
||||
* application's window. These can be read via the inputQueue
|
||||
* object of android_app.
|
||||
*/
|
||||
LOOPER_ID_EVENT = 2
|
||||
};
|
||||
|
||||
enum {
|
||||
/**
|
||||
* Command from main thread: the AInputQueue has changed. Upon processing
|
||||
* this command, android_app->inputQueue will be updated to the new queue
|
||||
* (or NULL).
|
||||
*/
|
||||
APP_CMD_INPUT_CHANGED,
|
||||
|
||||
/**
|
||||
* Command from main thread: the ANativeWindow has changed. Upon processing
|
||||
* this command, android_app->window will be updated to the new window surface
|
||||
* (or NULL).
|
||||
*/
|
||||
APP_CMD_WINDOW_CHANGED,
|
||||
|
||||
/**
|
||||
* Command from main thread: the app's activity window has gained
|
||||
* input focus.
|
||||
*/
|
||||
APP_CMD_GAINED_FOCUS,
|
||||
|
||||
/**
|
||||
* Command from main thread: the app's activity window has lost
|
||||
* input focus.
|
||||
*/
|
||||
APP_CMD_LOST_FOCUS,
|
||||
|
||||
/**
|
||||
* Command from main thread: the app's activity has been started.
|
||||
*/
|
||||
APP_CMD_START,
|
||||
|
||||
/**
|
||||
* Command from main thread: the app's activity has been resumed.
|
||||
*/
|
||||
APP_CMD_RESUME,
|
||||
|
||||
/**
|
||||
* Command from main thread: the app's activity has been paused.
|
||||
*/
|
||||
APP_CMD_PAUSE,
|
||||
|
||||
/**
|
||||
* Command from main thread: the app's activity has been stopped.
|
||||
*/
|
||||
APP_CMD_STOP,
|
||||
|
||||
/**
|
||||
* Command from main thread: the app's activity is being destroyed,
|
||||
* and waiting for the app thread to clean up and exit before proceeding.
|
||||
*/
|
||||
APP_CMD_DESTROY,
|
||||
};
|
||||
|
||||
/**
|
||||
* Call if android_app->destroyRequested is non-zero. Upon return, the
|
||||
* android_app structure is no longer valid and must not be touched.
|
||||
*/
|
||||
void android_app_destroy(struct android_app* android_app);
|
||||
|
||||
/**
|
||||
* Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next
|
||||
* app command message.
|
||||
*/
|
||||
int8_t android_app_read_cmd(struct android_app* android_app);
|
||||
|
||||
/**
|
||||
* Call with the command returned by android_app_read_cmd() to do the
|
||||
* default processing of the given command.
|
||||
*/
|
||||
void android_app_exec_cmd(struct android_app* android_app, int8_t cmd);
|
||||
|
||||
/**
|
||||
* This is the function that application code must implement, representing
|
||||
* the main entry to the app.
|
||||
*/
|
||||
extern void android_main(struct android_app* app);
|
||||
Binary file not shown.
BIN
ndk/platforms/android-9/arch-arm/usr/lib/libthreaded_app.a
Normal file
BIN
ndk/platforms/android-9/arch-arm/usr/lib/libthreaded_app.a
Normal file
Binary file not shown.
@@ -18,6 +18,6 @@ include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := native-activity
|
||||
LOCAL_SRC_FILES := main.c glutils.c
|
||||
LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM
|
||||
LOCAL_LDLIBS := -lthreaded_app -llog -landroid -lEGL -lGLESv1_CM
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
@@ -18,38 +18,14 @@
|
||||
#include <jni.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <string.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include <android_glue/threaded_app.h>
|
||||
|
||||
#include "glutils.h"
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Rendering and input engine thread
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
struct engine {
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cond;
|
||||
struct android_app* app;
|
||||
|
||||
int msgread;
|
||||
int msgwrite;
|
||||
|
||||
ANativeActivity* activity;
|
||||
pthread_t thread;
|
||||
|
||||
int running;
|
||||
int destroyed;
|
||||
ALooper* looper;
|
||||
AInputQueue* inputQueue;
|
||||
ANativeWindow* window;
|
||||
AInputQueue* pendingInputQueue;
|
||||
ANativeWindow* pendingWindow;
|
||||
|
||||
// private to engine thread.
|
||||
int destroyRequested;
|
||||
int animating;
|
||||
EGLDisplay display;
|
||||
EGLSurface surface;
|
||||
@@ -61,14 +37,6 @@ struct engine {
|
||||
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[] = {
|
||||
@@ -84,8 +52,8 @@ static int engine_init_display(struct engine* engine) {
|
||||
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||
|
||||
eglInitialize(display, 0, 0);
|
||||
selectConfigForNativeWindow(display, attribs, engine->window, &config);
|
||||
surface = eglCreateWindowSurface(display, config, engine->window, NULL);
|
||||
selectConfigForNativeWindow(display, attribs, engine->app->window, &config);
|
||||
surface = eglCreateWindowSurface(display, config, engine->app->window, NULL);
|
||||
context = eglCreateContext(display, config, NULL, NULL);
|
||||
eglQuerySurface(display, surface, EGL_WIDTH, &w);
|
||||
eglQuerySurface(display, surface, EGL_HEIGHT, &h);
|
||||
@@ -161,19 +129,17 @@ static int engine_term_display(struct engine* engine) {
|
||||
engine->surface = EGL_NO_SURFACE;
|
||||
}
|
||||
|
||||
static int engine_process_event(int fd, int events, void* param) {
|
||||
struct engine* engine = (struct engine*)param;
|
||||
|
||||
static int engine_do_ui_event(struct engine* engine) {
|
||||
AInputEvent* event = NULL;
|
||||
if (AInputQueue_getEvent(engine->inputQueue, &event) >= 0) {
|
||||
if (AInputQueue_getEvent(engine->app->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);
|
||||
AInputQueue_finishEvent(engine->inputQueue, event, 1);
|
||||
AInputQueue_finishEvent(engine->app->inputQueue, event, 1);
|
||||
} else {
|
||||
AInputQueue_finishEvent(engine->inputQueue, event, 0);
|
||||
AInputQueue_finishEvent(engine->app->inputQueue, event, 0);
|
||||
}
|
||||
} else {
|
||||
LOGI("Failure reading next input event: %s\n", strerror(errno));
|
||||
@@ -182,262 +148,69 @@ static int engine_process_event(int fd, int events, void* param) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int engine_process_cmd(int fd, int events, void* param) {
|
||||
struct engine* engine = (struct engine*)param;
|
||||
|
||||
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);
|
||||
if (engine->inputQueue != NULL) {
|
||||
AInputQueue_detachLooper(engine->inputQueue);
|
||||
}
|
||||
engine->inputQueue = engine->pendingInputQueue;
|
||||
if (engine->inputQueue != NULL) {
|
||||
LOGI("Attaching input queue to looper");
|
||||
AInputQueue_attachLooper(engine->inputQueue,
|
||||
engine->looper, engine_process_event, engine);
|
||||
}
|
||||
pthread_cond_broadcast(&engine->cond);
|
||||
pthread_mutex_unlock(&engine->mutex);
|
||||
break;
|
||||
|
||||
case ENGINE_CMD_WINDOW_CHANGED:
|
||||
LOGI("Engine: ENGINE_CMD_WINDOW_CHANGED\n");
|
||||
engine_term_display(engine);
|
||||
pthread_mutex_lock(&engine->mutex);
|
||||
engine->window = engine->pendingWindow;
|
||||
pthread_cond_broadcast(&engine->cond);
|
||||
pthread_mutex_unlock(&engine->mutex);
|
||||
if (engine->window != NULL) {
|
||||
engine_init_display(engine);
|
||||
engine_draw_frame(engine);
|
||||
}
|
||||
break;
|
||||
|
||||
case ENGINE_CMD_LOST_FOCUS:
|
||||
engine->animating = 0;
|
||||
static void engine_do_main_cmd(struct engine* engine) {
|
||||
int8_t cmd = android_app_read_cmd(engine->app);
|
||||
switch (cmd) {
|
||||
case APP_CMD_WINDOW_CHANGED:
|
||||
engine_term_display(engine);
|
||||
android_app_exec_cmd(engine->app, cmd);
|
||||
if (engine->app->window != NULL) {
|
||||
engine_init_display(engine);
|
||||
engine_draw_frame(engine);
|
||||
break;
|
||||
|
||||
case ENGINE_CMD_DESTROY:
|
||||
LOGI("Engine: ENGINE_CMD_DESTROY\n");
|
||||
engine->destroyRequested = 1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
LOGW("No data on command pipe!");
|
||||
}
|
||||
break;
|
||||
case APP_CMD_LOST_FOCUS:
|
||||
android_app_exec_cmd(engine->app, cmd);
|
||||
engine->animating = 0;
|
||||
engine_draw_frame(engine);
|
||||
break;
|
||||
default:
|
||||
android_app_exec_cmd(engine->app, cmd);
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void* engine_entry(void* param) {
|
||||
struct engine* engine = (struct engine*)param;
|
||||
void android_main(struct android_app* state) {
|
||||
struct engine engine;
|
||||
|
||||
ALooper* looper = ALooper_prepare();
|
||||
ALooper_setCallback(looper, engine->msgread, POLLIN, engine_process_cmd, engine);
|
||||
engine->looper = looper;
|
||||
|
||||
pthread_mutex_lock(&engine->mutex);
|
||||
engine->running = 1;
|
||||
pthread_cond_broadcast(&engine->cond);
|
||||
pthread_mutex_unlock(&engine->mutex);
|
||||
memset(&engine, 0, sizeof(engine));
|
||||
state->userData = &engine;
|
||||
engine.app = state;
|
||||
|
||||
// loop waiting for stuff to do.
|
||||
|
||||
while (1) {
|
||||
// Read all pending events.
|
||||
while (ALooper_pollOnce(engine->animating ? 0 : -1)) {
|
||||
;
|
||||
int fd;
|
||||
int events;
|
||||
void* data;
|
||||
while ((fd=ALooper_pollAll(engine.animating ? 0 : -1, &events, &data)) >= 0) {
|
||||
LOGI("Poll returned: %d", (int)data);
|
||||
switch ((int)data) {
|
||||
case LOOPER_ID_MAIN:
|
||||
engine_do_main_cmd(&engine);
|
||||
break;
|
||||
case LOOPER_ID_EVENT:
|
||||
engine_do_ui_event(&engine);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (engine->destroyRequested) {
|
||||
if (state->destroyRequested) {
|
||||
LOGI("Engine thread destroy requested!");
|
||||
pthread_mutex_lock(&engine->mutex);
|
||||
engine_term_display(engine);
|
||||
if (engine->inputQueue != NULL) {
|
||||
AInputQueue_detachLooper(engine->inputQueue);
|
||||
}
|
||||
engine->destroyed = 1;
|
||||
pthread_cond_broadcast(&engine->cond);
|
||||
pthread_mutex_unlock(&engine->mutex);
|
||||
// Can't touch engine object after this.
|
||||
return NULL; // EXIT THREAD
|
||||
engine_term_display(&engine);
|
||||
android_app_destroy(state);
|
||||
// Can't touch android_app object after this.
|
||||
return;
|
||||
}
|
||||
|
||||
if (engine->animating) {
|
||||
if (engine.animating) {
|
||||
// Done with events; draw next animation frame.
|
||||
engine->angle += .01f;
|
||||
if (engine->angle > 1) {
|
||||
engine->angle = 0;
|
||||
engine.angle += .01f;
|
||||
if (engine.angle > 1) {
|
||||
engine.angle = 0;
|
||||
}
|
||||
engine_draw_frame(engine);
|
||||
engine_draw_frame(&engine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
free(engine);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// 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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user