/* * 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. * */ // this is translation of android_native_app_glue.c module android.android_native_app_glue_impl; version(Android): extern(C): @system: import core.sys.posix.pthread; import android.input, android.native_window, android.rect, android.log; import android.configuration, android.looper, android.native_activity; import core.stdc.stdlib; import core.stdc.string; import core.stdc.stdio; import core.stdc.errno; import core.sys.posix.sys.resource; import core.sys.posix.unistd; import android.android_native_app_glue; import android.log; static void free_saved_state(android_app* android_app) { pthread_mutex_lock(&android_app.mutex); if (android_app.savedState != null) { free(android_app.savedState); android_app.savedState = null; android_app.savedStateSize = 0; } pthread_mutex_unlock(&android_app.mutex); } byte android_app_read_cmd(android_app* android_app) { byte cmd; if (read(android_app.msgread, &cmd, cmd.sizeof) == cmd.sizeof) { switch (cmd) { case APP_CMD_SAVE_STATE: free_saved_state(android_app); break; default: break; } return cmd; } else { LOGE("No data on command pipe!"); } return -1; } static void print_cur_config(android_app* android_app) { char[2] lang; char[2] country; AConfiguration_getLanguage(android_app.config, lang.ptr); AConfiguration_getCountry(android_app.config, country.ptr); LOGV("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d " ~ "keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d " ~ "modetype=%d modenight=%d", AConfiguration_getMcc(android_app.config), AConfiguration_getMnc(android_app.config), lang[0], lang[1], country[0], country[1], AConfiguration_getOrientation(android_app.config), AConfiguration_getTouchscreen(android_app.config), AConfiguration_getDensity(android_app.config), AConfiguration_getKeyboard(android_app.config), AConfiguration_getNavigation(android_app.config), AConfiguration_getKeysHidden(android_app.config), AConfiguration_getNavHidden(android_app.config), AConfiguration_getSdkVersion(android_app.config), AConfiguration_getScreenSize(android_app.config), AConfiguration_getScreenLong(android_app.config), AConfiguration_getUiModeType(android_app.config), AConfiguration_getUiModeNight(android_app.config)); } void android_app_pre_exec_cmd(android_app* android_app, byte cmd) { switch (cmd) { case APP_CMD_INPUT_CHANGED: LOGV("APP_CMD_INPUT_CHANGED\n"); pthread_mutex_lock(&android_app.mutex); if (android_app.inputQueue != null) { AInputQueue_detachLooper(android_app.inputQueue); } android_app.inputQueue = android_app.pendingInputQueue; if (android_app.inputQueue != null) { LOGV("Attaching input queue to looper"); AInputQueue_attachLooper(android_app.inputQueue, android_app.looper, LOOPER_ID_INPUT, null, &android_app.inputPollSource); } pthread_cond_broadcast(&android_app.cond); pthread_mutex_unlock(&android_app.mutex); break; case APP_CMD_INIT_WINDOW: LOGV("APP_CMD_INIT_WINDOW\n"); pthread_mutex_lock(&android_app.mutex); android_app.window = android_app.pendingWindow; pthread_cond_broadcast(&android_app.cond); pthread_mutex_unlock(&android_app.mutex); break; case APP_CMD_TERM_WINDOW: LOGV("APP_CMD_TERM_WINDOW\n"); pthread_cond_broadcast(&android_app.cond); break; case APP_CMD_RESUME: case APP_CMD_START: case APP_CMD_PAUSE: case APP_CMD_STOP: LOGV("activityState=%d\n", cmd); pthread_mutex_lock(&android_app.mutex); android_app.activityState = cmd; pthread_cond_broadcast(&android_app.cond); pthread_mutex_unlock(&android_app.mutex); break; case APP_CMD_CONFIG_CHANGED: LOGV("APP_CMD_CONFIG_CHANGED\n"); AConfiguration_fromAssetManager(android_app.config, android_app.activity.assetManager); print_cur_config(android_app); break; case APP_CMD_DESTROY: LOGV("APP_CMD_DESTROY\n"); android_app.destroyRequested = 1; break; default: break; } } void android_app_post_exec_cmd(android_app* android_app, byte cmd) { switch (cmd) { case APP_CMD_TERM_WINDOW: LOGV("APP_CMD_TERM_WINDOW\n"); pthread_mutex_lock(&android_app.mutex); android_app.window = null; pthread_cond_broadcast(&android_app.cond); pthread_mutex_unlock(&android_app.mutex); break; case APP_CMD_SAVE_STATE: LOGV("APP_CMD_SAVE_STATE\n"); pthread_mutex_lock(&android_app.mutex); android_app.stateSaved = 1; pthread_cond_broadcast(&android_app.cond); pthread_mutex_unlock(&android_app.mutex); break; case APP_CMD_RESUME: free_saved_state(android_app); break; default: break; } } void app_dummy() { } static void android_app_destroy(android_app* android_app) { LOGV("android_app_destroy!"); free_saved_state(android_app); pthread_mutex_lock(&android_app.mutex); if (android_app.inputQueue != null) { AInputQueue_detachLooper(android_app.inputQueue); } AConfiguration_delete(android_app.config); android_app.destroyed = 1; pthread_cond_broadcast(&android_app.cond); pthread_mutex_unlock(&android_app.mutex); // Can't touch android_app object after this. } static void process_input(android_app* app, android_poll_source* source) { AInputEvent* event = null; while (AInputQueue_getEvent(app.inputQueue, &event) >= 0) { LOGV("New input event: type=%d\n", AInputEvent_getType(event)); if (AInputQueue_preDispatchEvent(app.inputQueue, event)) { continue; } int handled = 0; if (app.onInputEvent != null) handled = app.onInputEvent(app, event); AInputQueue_finishEvent(app.inputQueue, event, handled); } } static void process_cmd(android_app* app, android_poll_source* source) { byte cmd = android_app_read_cmd(app); android_app_pre_exec_cmd(app, cmd); if (app.onAppCmd != null) app.onAppCmd(app, cmd); android_app_post_exec_cmd(app, cmd); } void* android_app_entry(void* param) { android_app* android_app = cast(android_app*)param; android_app.config = AConfiguration_new(); AConfiguration_fromAssetManager(android_app.config, android_app.activity.assetManager); print_cur_config(android_app); android_app.cmdPollSource.id = LOOPER_ID_MAIN; android_app.cmdPollSource.app = android_app; android_app.cmdPollSource.process = &process_cmd; android_app.inputPollSource.id = LOOPER_ID_INPUT; android_app.inputPollSource.app = android_app; android_app.inputPollSource.process = &process_input; ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS); ALooper_addFd(looper, android_app.msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, null, &android_app.cmdPollSource); android_app.looper = looper; pthread_mutex_lock(&android_app.mutex); android_app.running = 1; pthread_cond_broadcast(&android_app.cond); pthread_mutex_unlock(&android_app.mutex); import core.runtime; rt_init(); android_main(android_app); rt_term(); android_app_destroy(android_app); return null; } // -------------------------------------------------------------------- // Native activity interaction (called from main thread) // -------------------------------------------------------------------- static android_app* android_app_create(ANativeActivity* activity, void* savedState, size_t savedStateSize) { size_t sz = android_app.sizeof; android_app* android_app = cast(android_app*)malloc(sz); memset(android_app, 0, sz); android_app.activity = activity; pthread_mutex_init(&android_app.mutex, null); pthread_cond_init(&android_app.cond, null); if (savedState != null) { android_app.savedState = malloc(savedStateSize); android_app.savedStateSize = savedStateSize; memcpy(android_app.savedState, savedState, savedStateSize); } int[2] msgpipe; if (pipe(msgpipe)) { LOGE("could not create pipe: %s", strerror(errno)); return null; } android_app.msgread = msgpipe[0]; android_app.msgwrite = msgpipe[1]; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); pthread_create(&android_app.thread, &attr, &android_app_entry, android_app); // Wait for thread to start. pthread_mutex_lock(&android_app.mutex); while (!android_app.running) { pthread_cond_wait(&android_app.cond, &android_app.mutex); } pthread_mutex_unlock(&android_app.mutex); return android_app; } static void android_app_write_cmd(android_app* android_app, byte cmd) { if (write(android_app.msgwrite, &cmd, cmd.sizeof) != cmd.sizeof) { LOGE("Failure writing android_app cmd: %s\n", strerror(errno)); } } static void android_app_set_input(android_app* android_app, AInputQueue* inputQueue) { pthread_mutex_lock(&android_app.mutex); android_app.pendingInputQueue = inputQueue; android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED); while (android_app.inputQueue != android_app.pendingInputQueue) { pthread_cond_wait(&android_app.cond, &android_app.mutex); } pthread_mutex_unlock(&android_app.mutex); } static void android_app_set_window(android_app* android_app, ANativeWindow* window) { pthread_mutex_lock(&android_app.mutex); if (android_app.pendingWindow != null) { android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW); } android_app.pendingWindow = window; if (window != null) { android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW); } while (android_app.window != android_app.pendingWindow) { pthread_cond_wait(&android_app.cond, &android_app.mutex); } pthread_mutex_unlock(&android_app.mutex); } static void android_app_set_activity_state(android_app* android_app, byte cmd) { pthread_mutex_lock(&android_app.mutex); android_app_write_cmd(android_app, cmd); while (android_app.activityState != cmd) { pthread_cond_wait(&android_app.cond, &android_app.mutex); } pthread_mutex_unlock(&android_app.mutex); } static void android_app_free(android_app* android_app) { pthread_mutex_lock(&android_app.mutex); android_app_write_cmd(android_app, APP_CMD_DESTROY); while (!android_app.destroyed) { pthread_cond_wait(&android_app.cond, &android_app.mutex); } pthread_mutex_unlock(&android_app.mutex); close(android_app.msgread); close(android_app.msgwrite); pthread_cond_destroy(&android_app.cond); pthread_mutex_destroy(&android_app.mutex); free(android_app); } static void onDestroy(ANativeActivity* activity) { LOGV("Destroy: %p\n", activity); android_app_free(cast(android_app*)activity.instance); } static void onStart(ANativeActivity* activity) { LOGV("Start: %p\n", activity); android_app_set_activity_state(cast(android_app*)activity.instance, APP_CMD_START); } static void onResume(ANativeActivity* activity) { LOGV("Resume: %p\n", activity); android_app_set_activity_state(cast(android_app*)activity.instance, APP_CMD_RESUME); } static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) { android_app* android_app = cast(android_app*)activity.instance; void* savedState = null; LOGV("SaveInstanceState: %p\n", activity); pthread_mutex_lock(&android_app.mutex); android_app.stateSaved = 0; android_app_write_cmd(android_app, APP_CMD_SAVE_STATE); while (!android_app.stateSaved) { pthread_cond_wait(&android_app.cond, &android_app.mutex); } if (android_app.savedState != null) { savedState = android_app.savedState; *outLen = android_app.savedStateSize; android_app.savedState = null; android_app.savedStateSize = 0; } pthread_mutex_unlock(&android_app.mutex); return savedState; } static void onPause(ANativeActivity* activity) { LOGV("Pause: %p\n", activity); android_app_set_activity_state(cast(android_app*)activity.instance, APP_CMD_PAUSE); } static void onStop(ANativeActivity* activity) { LOGV("Stop: %p\n", activity); android_app_set_activity_state(cast(android_app*)activity.instance, APP_CMD_STOP); } static void onConfigurationChanged(ANativeActivity* activity) { android_app* android_app = cast(android_app*)activity.instance; LOGV("ConfigurationChanged: %p\n", activity); android_app_write_cmd(android_app, APP_CMD_CONFIG_CHANGED); } static void onLowMemory(ANativeActivity* activity) { android_app* android_app = cast(android_app*)activity.instance; LOGV("LowMemory: %p\n", activity); android_app_write_cmd(android_app, APP_CMD_LOW_MEMORY); } static void onWindowFocusChanged(ANativeActivity* activity, int focused) { LOGV("WindowFocusChanged: %p -- %d\n", activity, focused); android_app_write_cmd(cast(android_app*)activity.instance, focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS); } static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) { LOGV("NativeWindowCreated: %p -- %p\n", activity, window); android_app_set_window(cast(android_app*)activity.instance, window); } static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) { LOGV("NativeWindowDestroyed: %p -- %p\n", activity, window); android_app_set_window(cast(android_app*)activity.instance, null); } static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) { LOGV("InputQueueCreated: %p -- %p\n", activity, queue); android_app_set_input(cast(android_app*)activity.instance, queue); } static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) { LOGV("InputQueueDestroyed: %p -- %p\n", activity, queue); android_app_set_input(cast(android_app*)activity.instance, null); } void ANativeActivity_onCreate(ANativeActivity* activity, void* savedState, size_t savedStateSize) { LOGV("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.onConfigurationChanged = &onConfigurationChanged; 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 = android_app_create(activity, savedState, savedStateSize); }