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 // this is translation of android_native_app_glue.c 19 module android.android_native_app_glue_impl; 20 21 version(Android): 22 extern(C): 23 @system: 24 25 import core.sys.posix.pthread; 26 import android.input, android.native_window, android.rect, android.log; 27 import android.configuration, android.looper, android.native_activity; 28 import core.stdc.stdlib; 29 import core.stdc.string; 30 import core.stdc.stdio; 31 import core.stdc.errno; 32 import core.sys.posix.sys.resource; 33 import core.sys.posix.unistd; 34 35 import android.android_native_app_glue; 36 import android.log; 37 38 static void free_saved_state(android_app* android_app) { 39 pthread_mutex_lock(&android_app.mutex); 40 if (android_app.savedState != null) { 41 free(android_app.savedState); 42 android_app.savedState = null; 43 android_app.savedStateSize = 0; 44 } 45 pthread_mutex_unlock(&android_app.mutex); 46 } 47 48 byte android_app_read_cmd(android_app* android_app) { 49 byte cmd; 50 if (read(android_app.msgread, &cmd, cmd.sizeof) == cmd.sizeof) { 51 switch (cmd) { 52 case APP_CMD_SAVE_STATE: 53 free_saved_state(android_app); 54 break; 55 default: 56 break; 57 } 58 return cmd; 59 } else { 60 LOGE("No data on command pipe!"); 61 } 62 return -1; 63 } 64 65 static void print_cur_config(android_app* android_app) { 66 67 char[2] lang; 68 char[2] country; 69 AConfiguration_getLanguage(android_app.config, lang.ptr); 70 AConfiguration_getCountry(android_app.config, country.ptr); 71 72 LOGV("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d " ~ 73 "keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d " ~ 74 "modetype=%d modenight=%d", 75 AConfiguration_getMcc(android_app.config), 76 AConfiguration_getMnc(android_app.config), 77 lang[0], lang[1], country[0], country[1], 78 AConfiguration_getOrientation(android_app.config), 79 AConfiguration_getTouchscreen(android_app.config), 80 AConfiguration_getDensity(android_app.config), 81 AConfiguration_getKeyboard(android_app.config), 82 AConfiguration_getNavigation(android_app.config), 83 AConfiguration_getKeysHidden(android_app.config), 84 AConfiguration_getNavHidden(android_app.config), 85 AConfiguration_getSdkVersion(android_app.config), 86 AConfiguration_getScreenSize(android_app.config), 87 AConfiguration_getScreenLong(android_app.config), 88 AConfiguration_getUiModeType(android_app.config), 89 AConfiguration_getUiModeNight(android_app.config)); 90 } 91 92 void android_app_pre_exec_cmd(android_app* android_app, byte cmd) { 93 switch (cmd) { 94 case APP_CMD_INPUT_CHANGED: 95 LOGV("APP_CMD_INPUT_CHANGED\n"); 96 pthread_mutex_lock(&android_app.mutex); 97 if (android_app.inputQueue != null) { 98 AInputQueue_detachLooper(android_app.inputQueue); 99 } 100 android_app.inputQueue = android_app.pendingInputQueue; 101 if (android_app.inputQueue != null) { 102 LOGV("Attaching input queue to looper"); 103 AInputQueue_attachLooper(android_app.inputQueue, 104 android_app.looper, LOOPER_ID_INPUT, null, 105 &android_app.inputPollSource); 106 } 107 pthread_cond_broadcast(&android_app.cond); 108 pthread_mutex_unlock(&android_app.mutex); 109 break; 110 111 case APP_CMD_INIT_WINDOW: 112 LOGV("APP_CMD_INIT_WINDOW\n"); 113 pthread_mutex_lock(&android_app.mutex); 114 android_app.window = android_app.pendingWindow; 115 pthread_cond_broadcast(&android_app.cond); 116 pthread_mutex_unlock(&android_app.mutex); 117 break; 118 119 case APP_CMD_TERM_WINDOW: 120 LOGV("APP_CMD_TERM_WINDOW\n"); 121 pthread_cond_broadcast(&android_app.cond); 122 break; 123 124 case APP_CMD_RESUME: 125 case APP_CMD_START: 126 case APP_CMD_PAUSE: 127 case APP_CMD_STOP: 128 LOGV("activityState=%d\n", cmd); 129 pthread_mutex_lock(&android_app.mutex); 130 android_app.activityState = cmd; 131 pthread_cond_broadcast(&android_app.cond); 132 pthread_mutex_unlock(&android_app.mutex); 133 break; 134 135 case APP_CMD_CONFIG_CHANGED: 136 LOGV("APP_CMD_CONFIG_CHANGED\n"); 137 AConfiguration_fromAssetManager(android_app.config, 138 android_app.activity.assetManager); 139 print_cur_config(android_app); 140 break; 141 142 case APP_CMD_DESTROY: 143 LOGV("APP_CMD_DESTROY\n"); 144 android_app.destroyRequested = 1; 145 break; 146 default: 147 break; 148 } 149 } 150 151 void android_app_post_exec_cmd(android_app* android_app, byte cmd) { 152 switch (cmd) { 153 case APP_CMD_TERM_WINDOW: 154 LOGV("APP_CMD_TERM_WINDOW\n"); 155 pthread_mutex_lock(&android_app.mutex); 156 android_app.window = null; 157 pthread_cond_broadcast(&android_app.cond); 158 pthread_mutex_unlock(&android_app.mutex); 159 break; 160 161 case APP_CMD_SAVE_STATE: 162 LOGV("APP_CMD_SAVE_STATE\n"); 163 pthread_mutex_lock(&android_app.mutex); 164 android_app.stateSaved = 1; 165 pthread_cond_broadcast(&android_app.cond); 166 pthread_mutex_unlock(&android_app.mutex); 167 break; 168 169 case APP_CMD_RESUME: 170 free_saved_state(android_app); 171 break; 172 default: 173 break; 174 } 175 } 176 177 void app_dummy() { 178 179 } 180 181 static void android_app_destroy(android_app* android_app) { 182 LOGV("android_app_destroy!"); 183 free_saved_state(android_app); 184 pthread_mutex_lock(&android_app.mutex); 185 if (android_app.inputQueue != null) { 186 AInputQueue_detachLooper(android_app.inputQueue); 187 } 188 AConfiguration_delete(android_app.config); 189 android_app.destroyed = 1; 190 pthread_cond_broadcast(&android_app.cond); 191 pthread_mutex_unlock(&android_app.mutex); 192 // Can't touch android_app object after this. 193 } 194 195 static void process_input(android_app* app, android_poll_source* source) { 196 AInputEvent* event = null; 197 while (AInputQueue_getEvent(app.inputQueue, &event) >= 0) { 198 LOGV("New input event: type=%d\n", AInputEvent_getType(event)); 199 if (AInputQueue_preDispatchEvent(app.inputQueue, event)) { 200 continue; 201 } 202 int handled = 0; 203 if (app.onInputEvent != null) handled = app.onInputEvent(app, event); 204 AInputQueue_finishEvent(app.inputQueue, event, handled); 205 } 206 } 207 208 static void process_cmd(android_app* app, android_poll_source* source) { 209 byte cmd = android_app_read_cmd(app); 210 android_app_pre_exec_cmd(app, cmd); 211 if (app.onAppCmd != null) app.onAppCmd(app, cmd); 212 android_app_post_exec_cmd(app, cmd); 213 } 214 215 void* android_app_entry(void* param) { 216 android_app* android_app = cast(android_app*)param; 217 218 android_app.config = AConfiguration_new(); 219 AConfiguration_fromAssetManager(android_app.config, android_app.activity.assetManager); 220 221 print_cur_config(android_app); 222 223 android_app.cmdPollSource.id = LOOPER_ID_MAIN; 224 android_app.cmdPollSource.app = android_app; 225 android_app.cmdPollSource.process = &process_cmd; 226 android_app.inputPollSource.id = LOOPER_ID_INPUT; 227 android_app.inputPollSource.app = android_app; 228 android_app.inputPollSource.process = &process_input; 229 230 ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS); 231 ALooper_addFd(looper, android_app.msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, null, 232 &android_app.cmdPollSource); 233 android_app.looper = looper; 234 235 pthread_mutex_lock(&android_app.mutex); 236 android_app.running = 1; 237 pthread_cond_broadcast(&android_app.cond); 238 pthread_mutex_unlock(&android_app.mutex); 239 240 241 import core.runtime; 242 rt_init(); 243 android_main(android_app); 244 rt_term(); 245 246 android_app_destroy(android_app); 247 return null; 248 } 249 250 // -------------------------------------------------------------------- 251 // Native activity interaction (called from main thread) 252 // -------------------------------------------------------------------- 253 254 static android_app* android_app_create(ANativeActivity* activity, 255 void* savedState, size_t savedStateSize) { 256 size_t sz = android_app.sizeof; 257 android_app* android_app = cast(android_app*)malloc(sz); 258 memset(android_app, 0, sz); 259 android_app.activity = activity; 260 261 pthread_mutex_init(&android_app.mutex, null); 262 pthread_cond_init(&android_app.cond, null); 263 264 if (savedState != null) { 265 android_app.savedState = malloc(savedStateSize); 266 android_app.savedStateSize = savedStateSize; 267 memcpy(android_app.savedState, savedState, savedStateSize); 268 } 269 270 int[2] msgpipe; 271 if (pipe(msgpipe)) { 272 LOGE("could not create pipe: %s", strerror(errno)); 273 return null; 274 } 275 android_app.msgread = msgpipe[0]; 276 android_app.msgwrite = msgpipe[1]; 277 278 pthread_attr_t attr; 279 pthread_attr_init(&attr); 280 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 281 pthread_create(&android_app.thread, &attr, &android_app_entry, android_app); 282 283 // Wait for thread to start. 284 pthread_mutex_lock(&android_app.mutex); 285 while (!android_app.running) { 286 pthread_cond_wait(&android_app.cond, &android_app.mutex); 287 } 288 pthread_mutex_unlock(&android_app.mutex); 289 290 return android_app; 291 } 292 293 static void android_app_write_cmd(android_app* android_app, byte cmd) { 294 if (write(android_app.msgwrite, &cmd, cmd.sizeof) != cmd.sizeof) { 295 LOGE("Failure writing android_app cmd: %s\n", strerror(errno)); 296 } 297 } 298 299 static void android_app_set_input(android_app* android_app, AInputQueue* inputQueue) { 300 pthread_mutex_lock(&android_app.mutex); 301 android_app.pendingInputQueue = inputQueue; 302 android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED); 303 while (android_app.inputQueue != android_app.pendingInputQueue) { 304 pthread_cond_wait(&android_app.cond, &android_app.mutex); 305 } 306 pthread_mutex_unlock(&android_app.mutex); 307 } 308 309 static void android_app_set_window(android_app* android_app, ANativeWindow* window) { 310 pthread_mutex_lock(&android_app.mutex); 311 if (android_app.pendingWindow != null) { 312 android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW); 313 } 314 android_app.pendingWindow = window; 315 if (window != null) { 316 android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW); 317 } 318 while (android_app.window != android_app.pendingWindow) { 319 pthread_cond_wait(&android_app.cond, &android_app.mutex); 320 } 321 pthread_mutex_unlock(&android_app.mutex); 322 } 323 324 static void android_app_set_activity_state(android_app* android_app, byte cmd) { 325 pthread_mutex_lock(&android_app.mutex); 326 android_app_write_cmd(android_app, cmd); 327 while (android_app.activityState != cmd) { 328 pthread_cond_wait(&android_app.cond, &android_app.mutex); 329 } 330 pthread_mutex_unlock(&android_app.mutex); 331 } 332 333 static void android_app_free(android_app* android_app) { 334 pthread_mutex_lock(&android_app.mutex); 335 android_app_write_cmd(android_app, APP_CMD_DESTROY); 336 while (!android_app.destroyed) { 337 pthread_cond_wait(&android_app.cond, &android_app.mutex); 338 } 339 pthread_mutex_unlock(&android_app.mutex); 340 341 close(android_app.msgread); 342 close(android_app.msgwrite); 343 pthread_cond_destroy(&android_app.cond); 344 pthread_mutex_destroy(&android_app.mutex); 345 free(android_app); 346 } 347 348 static void onDestroy(ANativeActivity* activity) { 349 LOGV("Destroy: %p\n", activity); 350 android_app_free(cast(android_app*)activity.instance); 351 } 352 353 static void onStart(ANativeActivity* activity) { 354 LOGV("Start: %p\n", activity); 355 android_app_set_activity_state(cast(android_app*)activity.instance, APP_CMD_START); 356 } 357 358 static void onResume(ANativeActivity* activity) { 359 LOGV("Resume: %p\n", activity); 360 android_app_set_activity_state(cast(android_app*)activity.instance, APP_CMD_RESUME); 361 } 362 363 static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) { 364 android_app* android_app = cast(android_app*)activity.instance; 365 void* savedState = null; 366 367 LOGV("SaveInstanceState: %p\n", activity); 368 pthread_mutex_lock(&android_app.mutex); 369 android_app.stateSaved = 0; 370 android_app_write_cmd(android_app, APP_CMD_SAVE_STATE); 371 while (!android_app.stateSaved) { 372 pthread_cond_wait(&android_app.cond, &android_app.mutex); 373 } 374 375 if (android_app.savedState != null) { 376 savedState = android_app.savedState; 377 *outLen = android_app.savedStateSize; 378 android_app.savedState = null; 379 android_app.savedStateSize = 0; 380 } 381 382 pthread_mutex_unlock(&android_app.mutex); 383 384 return savedState; 385 } 386 387 static void onPause(ANativeActivity* activity) { 388 LOGV("Pause: %p\n", activity); 389 android_app_set_activity_state(cast(android_app*)activity.instance, APP_CMD_PAUSE); 390 } 391 392 static void onStop(ANativeActivity* activity) { 393 LOGV("Stop: %p\n", activity); 394 android_app_set_activity_state(cast(android_app*)activity.instance, APP_CMD_STOP); 395 } 396 397 static void onConfigurationChanged(ANativeActivity* activity) { 398 android_app* android_app = cast(android_app*)activity.instance; 399 LOGV("ConfigurationChanged: %p\n", activity); 400 android_app_write_cmd(android_app, APP_CMD_CONFIG_CHANGED); 401 } 402 403 static void onLowMemory(ANativeActivity* activity) { 404 android_app* android_app = cast(android_app*)activity.instance; 405 LOGV("LowMemory: %p\n", activity); 406 android_app_write_cmd(android_app, APP_CMD_LOW_MEMORY); 407 } 408 409 static void onWindowFocusChanged(ANativeActivity* activity, int focused) { 410 LOGV("WindowFocusChanged: %p -- %d\n", activity, focused); 411 android_app_write_cmd(cast(android_app*)activity.instance, 412 focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS); 413 } 414 415 static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) { 416 LOGV("NativeWindowCreated: %p -- %p\n", activity, window); 417 android_app_set_window(cast(android_app*)activity.instance, window); 418 } 419 420 static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) { 421 LOGV("NativeWindowDestroyed: %p -- %p\n", activity, window); 422 android_app_set_window(cast(android_app*)activity.instance, null); 423 } 424 425 static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) { 426 LOGV("InputQueueCreated: %p -- %p\n", activity, queue); 427 android_app_set_input(cast(android_app*)activity.instance, queue); 428 } 429 430 static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) { 431 LOGV("InputQueueDestroyed: %p -- %p\n", activity, queue); 432 android_app_set_input(cast(android_app*)activity.instance, null); 433 } 434 435 void ANativeActivity_onCreate(ANativeActivity* activity, 436 void* savedState, size_t savedStateSize) { 437 LOGV("Creating: %p\n", activity); 438 activity.callbacks.onDestroy = &onDestroy; 439 activity.callbacks.onStart = &onStart; 440 activity.callbacks.onResume = &onResume; 441 activity.callbacks.onSaveInstanceState = &onSaveInstanceState; 442 activity.callbacks.onPause = &onPause; 443 activity.callbacks.onStop = &onStop; 444 activity.callbacks.onConfigurationChanged = &onConfigurationChanged; 445 activity.callbacks.onLowMemory = &onLowMemory; 446 activity.callbacks.onWindowFocusChanged = &onWindowFocusChanged; 447 activity.callbacks.onNativeWindowCreated = &onNativeWindowCreated; 448 activity.callbacks.onNativeWindowDestroyed = &onNativeWindowDestroyed; 449 activity.callbacks.onInputQueueCreated = &onInputQueueCreated; 450 activity.callbacks.onInputQueueDestroyed = &onInputQueueDestroyed; 451 452 activity.instance = android_app_create(activity, savedState, savedStateSize); 453 }