#include #include #include #include #include #include #include #include #include #include #include #include #include #define LOG_TAG "moku" #define _LOG(priority, fmt, ...) \ ((void)__android_log_print((priority), (LOG_TAG), \ (fmt)__VA_OPT__(, ) __VA_ARGS__)) #define LOGE(fmt, ...) _LOG(ANDROID_LOG_ERROR, (fmt)__VA_OPT__(, ) __VA_ARGS__) #define LOGW(fmt, ...) _LOG(ANDROID_LOG_WARN, (fmt)__VA_OPT__(, ) __VA_ARGS__) #define LOGI(fmt, ...) _LOG(ANDROID_LOG_INFO, (fmt)__VA_OPT__(, ) __VA_ARGS__) enum Color { COLOR_RED = 0x000000ff, COLOR_GREEN = 0x0000ff00, COLOR_BLUE = 0x00ff0000 }; struct engine { struct android_app *app; int window_initialized; int running; uint64_t last_update; int color; }; struct engine *engine_new(struct android_app* app) { struct engine *engine; engine = malloc(sizeof(struct engine)); if (!engine) { LOGE("Failed to allocate engine!"); return NULL; } engine->window_initialized = 0; engine->app = app; engine->running = 0; engine->color = COLOR_RED; engine->last_update = (uint64_t)0; return engine; } uint64_t get_timestamp_ns() { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); return (uint64_t)ts.tv_nsec + (uint64_t)ts.tv_sec * 1000000000; } static void engine_schedule_tick(struct engine *engine); static void engine_draw_frame(struct engine *engine) { ANativeWindow_Buffer buffer; int x, y; size_t pixel_idx; if (!engine->app->window) return; if (ANativeWindow_lock(engine->app->window, &buffer, NULL) < 0) { LOGE("Unable to lock window buffer"); return; } if (!engine->window_initialized) { return; } for (y = 0; y < buffer.height; y++) { for (x = 0; x < buffer.width; x++) { pixel_idx = y * buffer.stride + x; ((uint32_t*)(buffer.bits))[pixel_idx] = (uint32_t)engine->color; } } ANativeWindow_unlockAndPost(engine->app->window); } static void engine_update(struct engine *engine) { /* uint64_t now = get_timestamp_ns(); if (now - engine->last_update > 1000000000LL) { switch (engine->color) { case COLOR_RED: engine->color = COLOR_GREEN; break; case COLOR_GREEN: engine->color = COLOR_BLUE; break; case COLOR_BLUE: engine->color = COLOR_RED; break; default: LOGE("unexpected color %08x", (uint32_t)engine->color); } engine->last_update = now; } */ } static void engine_real_tick(struct engine *engine) { if (!engine->running) return; // LOGI("tick"); engine_schedule_tick(engine); engine_update(engine); engine_draw_frame(engine); } static void engine_tick(long frameTimeNanos, void *data) { struct engine *engine; if (!data) return; engine = (struct engine*)data; engine_real_tick(engine); } static void engine_schedule_tick(struct engine *engine) { AChoreographer_postFrameCallback(AChoreographer_getInstance(), engine_tick, (void*)engine); } /* TODO implement these stubs */ void engine_attach_window(struct engine *engine) { if (ANativeWindow_setBuffersGeometry(engine->app->window, 0, 0, AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM) < 0) { engine->window_initialized = 0; return; } engine->window_initialized = 1; engine->last_update = get_timestamp_ns(); }; void engine_detach_window(struct engine *engine) { /* Called when native window for app is destroyed. * Doesn't mean app is being killed, also happens on rotate * Normally, with a traditional OpenGL app, do cleanup here * At the moment, it's fine to just set a flag to disable the rendering loop */ engine->window_initialized = 0; }; void engine_resume(struct engine *engine) { if (!engine->running) { engine->running = 1; engine_schedule_tick(engine); LOGI("engine running"); } }; void engine_pause(struct engine *engine) { engine->running = 0; LOGI("engine paused"); }; static void engine_handle_cmd(struct android_app *app, int32_t cmd) { struct engine *engine; engine = (struct engine *)app->userData; switch (cmd) { case APP_CMD_INIT_WINDOW: engine_attach_window(engine); break; case APP_CMD_TERM_WINDOW: engine_detach_window(engine); break; case APP_CMD_GAINED_FOCUS: engine_resume(engine); break; case APP_CMD_LOST_FOCUS: engine_pause(engine); break; } } void android_main(struct android_app *state) { /* Declarations */ struct android_poll_source *source; struct engine *engine; int events; /* Code starts here */ app_dummy(); engine = engine_new(state); if (!engine) { LOGE("Failed to create engine!"); return; } state->userData = engine; state->onAppCmd = engine_handle_cmd; /* do stuff here */ while (!state->destroyRequested) { if (ALooper_pollAll(-1, NULL, &events, (void**)&source) >= 0) { if (source) { source->process(state, source); } } /* more stuff here */ } free(engine); }