| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 #include "embedders/openglui/android/eventloop.h" | |
| 6 | |
| 7 #include <time.h> | |
| 8 #include "embedders/openglui/common/log.h" | |
| 9 | |
| 10 /* | |
| 11 * The Android lifecycle events are: | |
| 12 * | |
| 13 * OnCreate: In NDK this is the call to android_main | |
| 14 * | |
| 15 * OnStart: technically, called to initiate the “visible” lifespan of the | |
| 16 * app; at any time between OnStart and OnStop, the app may be visible. | |
| 17 * We can either be OnResume’d or OnStop’ped from this state. Note that | |
| 18 * there is also an event for OnRestart, which is called before OnStart | |
| 19 * if the application is transitioning from OnStop to OnStart instead of | |
| 20 * being started from scratch. | |
| 21 * | |
| 22 * OnResume: technically, the start of the “foreground” lifespan of the app, | |
| 23 * but this does not mean that the app is fully visible and should be | |
| 24 * rendering – more on that later | |
| 25 * | |
| 26 * OnPause: the app is losing its foregrounded state; this is normally an | |
| 27 * indication that something is fully covering the app. On versions of | |
| 28 * Android before Honeycomb, once we returned from this callback, we | |
| 29 * could be killed at any time with no further app code called. We can | |
| 30 * either be OnResume’d or OnStop’ped from this state. OnPause halts the | |
| 31 * visible UI thread and so should be handled as quickly as possible. | |
| 32 * | |
| 33 * OnStop: the end of the current visible lifespan of the app – we may | |
| 34 * transition to On(Re)Start to become visible again, or to OnDestroy if | |
| 35 * we are shutting down entirely. Once we return from this callback, we | |
| 36 * can be killed at any time with no further app code called on any | |
| 37 * version of Android. | |
| 38 * | |
| 39 * OnDestroy: this can only be followed by the application being killed or | |
| 40 * restarted with OnCreate. | |
| 41 * | |
| 42 * The above events occur in fixed orders. The events below can occur at | |
| 43 * various times: | |
| 44 * | |
| 45 * OnGainedFocus: happens between OnResume and OnPause. | |
| 46 * OnLostFocus: may occur at arbitrary times, and may in fact not happen | |
| 47 * at all if the app is being destroyed. So OnPause and OnGainedFocus/ | |
| 48 * OnLostFocus must be used together to determine the visible and | |
| 49 * interactable state of the app. | |
| 50 * | |
| 51 * OnCreateWindow: usually happens in the resumed state | |
| 52 * OnDestroyWindow: can happen after OnPause or OnDestroy, so apps may | |
| 53 * need to handle shutting down EGL before their surfaces are | |
| 54 * destroyed. | |
| 55 * OnConfigurationChanged: typically means the size has changed | |
| 56 * | |
| 57 * An application's EGL surface may only exist between the OnCreateWindow | |
| 58 * and OnDestroyWindow callbacks. | |
| 59 * | |
| 60 * An application is "killable" after OnStop or OnDestroy (in earlier | |
| 61 * Android versions, after OnPause too). That means any critical state | |
| 62 * or resources must be handled by any of these callbacks. | |
| 63 * | |
| 64 * These callbacks run on the main thread. If they block any event | |
| 65 * processing for more than 5 seconds this will result in an ANR | |
| 66 * (Application Not Responding message). | |
| 67 * | |
| 68 * On application launch, this sequence will occur: | |
| 69 * | |
| 70 * - OnCreate | |
| 71 * - OnStart | |
| 72 * - OnResume | |
| 73 * - OnCreateWindow | |
| 74 * - OnConfigurationChanged | |
| 75 * - OnGainedFocus | |
| 76 * | |
| 77 * If the back button is pressed, and the app does not handle it, | |
| 78 * Android will pop the app of the UI stack and destroy the application's | |
| 79 * activity: | |
| 80 * | |
| 81 * - OnPause | |
| 82 * - OnLostFocus | |
| 83 * - OnDestroyWindow | |
| 84 * - OnStop | |
| 85 * - OnDestroy | |
| 86 * | |
| 87 * If the home button is pressed, the app is sent down in the UI stack. | |
| 88 * The app's Activity still exists but may be killed at any time, and | |
| 89 * it loses its rendering surface: | |
| 90 * | |
| 91 * - OnPause | |
| 92 * - OnLostFocus | |
| 93 * - OnDestroyWindow | |
| 94 * - OnStop | |
| 95 * | |
| 96 * If the app is then restarted (without having been killed in between): | |
| 97 * | |
| 98 * - OnRestart | |
| 99 * - OnStart | |
| 100 * - OnResume | |
| 101 * - OnCreateWindow | |
| 102 * - OnConfigurationChanged | |
| 103 * - OnGainedFocus | |
| 104 * | |
| 105 * If a status icon pop up is opened, the app is still visible and can render | |
| 106 * but is not focused and cannot receive input: | |
| 107 * | |
| 108 * - OnLostFocus | |
| 109 * | |
| 110 * When the popup is dismissed, the app regains focus: | |
| 111 * | |
| 112 * - OnGainedFocus | |
| 113 * | |
| 114 * When the device is suspended (power button or screen saver), the application | |
| 115 * will typically be paused and lose focus: | |
| 116 * | |
| 117 * - OnPause | |
| 118 * - OnLostFocus (sometimes this will only come when the device is resumed | |
| 119 * to the lock screen) | |
| 120 * | |
| 121 * The application should have stopped all rendering and sound and any | |
| 122 * non-critical background processing. | |
| 123 * | |
| 124 * When the device is resumed but not yet unlocked, the app will be resumed: | |
| 125 * | |
| 126 * - OnResume | |
| 127 * | |
| 128 * The application should not perform sound or graphics yet. If the lock | |
| 129 * screen times out, the app will be paused again. If the screen is | |
| 130 * unlocked, the app will regain focus. | |
| 131 * | |
| 132 * Turning all of this into a general framework, we can use the following: | |
| 133 * | |
| 134 * 1. In OnCreate/android_main, set up the main classes and possibly | |
| 135 * load some lightweight resources. | |
| 136 * 2. In OnCreateWindow, create the EGLSurface, bind the context, load | |
| 137 * OpenGL resources. No rendering. | |
| 138 * 3. When we are between OnResume and OnPause, and between OnCreateWindow | |
| 139 * and OnDestroyWindow, and between OnGainedFocus and OnLostFocus, | |
| 140 * we can render and process input. | |
| 141 * 4. In OnLostFocus, stop sounds from playing, and stop rendering. | |
| 142 * 5. In OnPause, stop all rendering | |
| 143 * 6. In OnResume, prepare to start rendering again, but don't render. | |
| 144 * 7. In OnGainedFocus after OnResume, start rendering and sound again. | |
| 145 * 8. In OnStop, free all graphic resources, either through GLES calls | |
| 146 * if the EGLContext is still bound, or via eglDestroyContext if the | |
| 147 * context has been unbound because the rendering surface was destroyed. | |
| 148 * 9. In OnDestroy, release all other resources. | |
| 149 */ | |
| 150 EventLoop::EventLoop(android_app* application) | |
| 151 : enabled_(false), | |
| 152 quit_(false), | |
| 153 isResumed_(false), | |
| 154 hasSurface_(false), | |
| 155 hasFocus_(false), | |
| 156 application_(application), | |
| 157 lifecycle_handler_(NULL), | |
| 158 input_handler_(NULL), | |
| 159 sensor_manager_(NULL), | |
| 160 sensor_event_queue_(NULL), | |
| 161 sensor_poll_source_() { | |
| 162 application_->onAppCmd = ActivityCallback; | |
| 163 application_->onInputEvent = InputCallback; | |
| 164 application_->userData = this; | |
| 165 } | |
| 166 | |
| 167 static int64_t getTimeInMillis() { | |
| 168 struct timespec res; | |
| 169 clock_gettime(CLOCK_REALTIME, &res); | |
| 170 return 1000 * res.tv_sec + res.tv_nsec / 1000000; | |
| 171 } | |
| 172 | |
| 173 void EventLoop::Run(LifeCycleHandler* lifecycle_handler, | |
| 174 InputHandler* input_handler) { | |
| 175 int32_t result; | |
| 176 int32_t events; | |
| 177 android_poll_source* source; | |
| 178 | |
| 179 lifecycle_handler_ = lifecycle_handler; | |
| 180 input_handler_ = input_handler; | |
| 181 int64_t last_frame_time = getTimeInMillis(); | |
| 182 if (lifecycle_handler_->OnStart() == 0) { | |
| 183 LOGI("Starting event loop"); | |
| 184 while (!quit_) { | |
| 185 // If not enabled, block indefinitely on events. If enabled, block | |
| 186 // briefly so we can do useful work in onStep, but only long | |
| 187 // enough that we can still do work at 60fps if possible. | |
| 188 int64_t next_frame_time = last_frame_time + (1000/60); | |
| 189 int64_t next_frame_delay = next_frame_time - getTimeInMillis(); | |
| 190 if (next_frame_delay < 0) next_frame_delay = 0; | |
| 191 while ((result = ALooper_pollAll(enabled_ ? next_frame_delay : -1, NULL, | |
| 192 &events, reinterpret_cast<void**>(&source))) >= 0) { | |
| 193 if (source != NULL) { | |
| 194 source->process(application_, source); | |
| 195 } | |
| 196 if (application_->destroyRequested) { | |
| 197 return; | |
| 198 } | |
| 199 } | |
| 200 if (enabled_ && !quit_) { | |
| 201 int64_t now = getTimeInMillis(); | |
| 202 if (now >= next_frame_time) { | |
| 203 LOGI("step"); | |
| 204 last_frame_time = now; | |
| 205 if (lifecycle_handler_->OnStep() != 0) { | |
| 206 quit_ = true; | |
| 207 } | |
| 208 } | |
| 209 } | |
| 210 } | |
| 211 } | |
| 212 ANativeActivity_finish(application_->activity); | |
| 213 } | |
| 214 | |
| 215 void EventLoop::EnableSensorEvents() { | |
| 216 sensor_poll_source_.id = LOOPER_ID_USER; | |
| 217 sensor_poll_source_.app = application_; | |
| 218 sensor_poll_source_.process = SensorCallback; | |
| 219 sensor_manager_ = ASensorManager_getInstance(); | |
| 220 if (sensor_manager_ != NULL) { | |
| 221 sensor_event_queue_ = ASensorManager_createEventQueue( | |
| 222 sensor_manager_, application_->looper, | |
| 223 LOOPER_ID_USER, NULL, &sensor_poll_source_); | |
| 224 | |
| 225 sensor_ = ASensorManager_getDefaultSensor(sensor_manager_, | |
| 226 ASENSOR_TYPE_ACCELEROMETER); | |
| 227 if (sensor_ != NULL) { | |
| 228 int32_t min_delay = ASensor_getMinDelay(sensor_); | |
| 229 if (ASensorEventQueue_enableSensor(sensor_event_queue_, sensor_) < 0 || | |
| 230 ASensorEventQueue_setEventRate(sensor_event_queue_, sensor_, | |
| 231 min_delay) < 0) { | |
| 232 LOGE("Error while activating sensor."); | |
| 233 DisableSensorEvents(); | |
| 234 return; | |
| 235 } | |
| 236 LOGI("Activating sensor:"); | |
| 237 LOGI("Name : %s", ASensor_getName(sensor_)); | |
| 238 LOGI("Vendor : %s", ASensor_getVendor(sensor_)); | |
| 239 LOGI("Resolution : %f", ASensor_getResolution(sensor_)); | |
| 240 LOGI("Min Delay : %d", min_delay); | |
| 241 } else { | |
| 242 LOGI("No sensor"); | |
| 243 } | |
| 244 } | |
| 245 } | |
| 246 | |
| 247 void EventLoop::DisableSensorEvents() { | |
| 248 if (sensor_ != NULL) { | |
| 249 if (ASensorEventQueue_disableSensor(sensor_event_queue_, sensor_) < 0) { | |
| 250 LOGE("Error while deactivating sensor."); | |
| 251 } | |
| 252 sensor_ = NULL; | |
| 253 } | |
| 254 if (sensor_event_queue_ != NULL) { | |
| 255 ASensorManager_destroyEventQueue(sensor_manager_, | |
| 256 sensor_event_queue_); | |
| 257 sensor_event_queue_ = NULL; | |
| 258 } | |
| 259 sensor_manager_ = NULL; | |
| 260 } | |
| 261 | |
| 262 void EventLoop::ProcessActivityEvent(int32_t command) { | |
| 263 switch (command) { | |
| 264 case APP_CMD_INIT_WINDOW: | |
| 265 if (lifecycle_handler_->Activate() != 0) { | |
| 266 quit_ = true; | |
| 267 } else { | |
| 268 hasSurface_ = true; | |
| 269 } | |
| 270 break; | |
| 271 case APP_CMD_CONFIG_CHANGED: | |
| 272 lifecycle_handler_->OnConfigurationChanged(); | |
| 273 break; | |
| 274 case APP_CMD_DESTROY: | |
| 275 hasFocus_ = false; | |
| 276 lifecycle_handler_->FreeAllResources(); | |
| 277 break; | |
| 278 case APP_CMD_GAINED_FOCUS: | |
| 279 hasFocus_ = true; | |
| 280 if (hasSurface_ && isResumed_ && hasFocus_) { | |
| 281 enabled_ = (lifecycle_handler_->Resume() == 0); | |
| 282 if (enabled_) { | |
| 283 EnableSensorEvents(); | |
| 284 } | |
| 285 } | |
| 286 break; | |
| 287 case APP_CMD_LOST_FOCUS: | |
| 288 hasFocus_ = false; | |
| 289 enabled_ = false; | |
| 290 DisableSensorEvents(); | |
| 291 lifecycle_handler_->Pause(); | |
| 292 break; | |
| 293 case APP_CMD_LOW_MEMORY: | |
| 294 lifecycle_handler_->OnLowMemory(); | |
| 295 break; | |
| 296 case APP_CMD_PAUSE: | |
| 297 isResumed_ = false; | |
| 298 enabled_ = false; | |
| 299 DisableSensorEvents(); | |
| 300 lifecycle_handler_->Pause(); | |
| 301 break; | |
| 302 case APP_CMD_RESUME: | |
| 303 isResumed_ = true; | |
| 304 break; | |
| 305 case APP_CMD_SAVE_STATE: | |
| 306 lifecycle_handler_->OnSaveState(&application_->savedState, | |
| 307 &application_->savedStateSize); | |
| 308 break; | |
| 309 case APP_CMD_START: | |
| 310 break; | |
| 311 case APP_CMD_STOP: | |
| 312 hasFocus_ = false; | |
| 313 lifecycle_handler_->Deactivate(); | |
| 314 break; | |
| 315 case APP_CMD_TERM_WINDOW: | |
| 316 hasFocus_ = false; | |
| 317 hasSurface_ = false; | |
| 318 enabled_ = false; | |
| 319 DisableSensorEvents(); | |
| 320 lifecycle_handler_->Pause(); | |
| 321 break; | |
| 322 default: | |
| 323 break; | |
| 324 } | |
| 325 } | |
| 326 | |
| 327 void EventLoop::ProcessSensorEvent() { | |
| 328 ASensorEvent event; | |
| 329 while (ASensorEventQueue_getEvents(sensor_event_queue_, &event, 1) > 0) { | |
| 330 switch (event.type) { | |
| 331 case ASENSOR_TYPE_ACCELEROMETER: | |
| 332 input_handler_->OnAccelerometerEvent(event.vector.x, | |
| 333 event.vector.y, event.vector.z); | |
| 334 break; | |
| 335 } | |
| 336 } | |
| 337 } | |
| 338 | |
| 339 bool EventLoop::OnTouchEvent(AInputEvent* event) { | |
| 340 int32_t type = AMotionEvent_getAction(event); | |
| 341 MotionEvent motion_event; | |
| 342 switch (type) { | |
| 343 case AMOTION_EVENT_ACTION_DOWN: | |
| 344 motion_event = kMotionDown; | |
| 345 break; | |
| 346 case AMOTION_EVENT_ACTION_UP: | |
| 347 motion_event = kMotionUp; | |
| 348 break; | |
| 349 case AMOTION_EVENT_ACTION_MOVE: | |
| 350 motion_event = kMotionMove; | |
| 351 break; | |
| 352 case AMOTION_EVENT_ACTION_CANCEL: | |
| 353 motion_event = kMotionCancel; | |
| 354 break; | |
| 355 case AMOTION_EVENT_ACTION_OUTSIDE: | |
| 356 motion_event = kMotionOutside; | |
| 357 break; | |
| 358 case AMOTION_EVENT_ACTION_POINTER_DOWN: | |
| 359 motion_event = kMotionPointerDown; | |
| 360 break; | |
| 361 case AMOTION_EVENT_ACTION_POINTER_UP: | |
| 362 motion_event = kMotionPointerUp; | |
| 363 break; | |
| 364 default: | |
| 365 return false; | |
| 366 } | |
| 367 // For now we just get the last coords. | |
| 368 float move_x = AMotionEvent_getX(event, 0); | |
| 369 float move_y = AMotionEvent_getY(event, 0); | |
| 370 int64_t when = AMotionEvent_getEventTime(event); | |
| 371 LOGI("Got motion event %d at %f, %f", type, move_x, move_y); | |
| 372 | |
| 373 if (input_handler_->OnMotionEvent(motion_event, when, move_x, move_y) != 0) { | |
| 374 return false; | |
| 375 } | |
| 376 return true; | |
| 377 } | |
| 378 | |
| 379 bool EventLoop::OnKeyEvent(AInputEvent* event) { | |
| 380 int32_t type = AKeyEvent_getAction(event); | |
| 381 KeyEvent key_event; | |
| 382 switch (type) { | |
| 383 case AKEY_EVENT_ACTION_DOWN: | |
| 384 key_event = kKeyDown; | |
| 385 break; | |
| 386 case AKEY_EVENT_ACTION_UP: | |
| 387 key_event = kKeyUp; | |
| 388 break; | |
| 389 case AKEY_EVENT_ACTION_MULTIPLE: | |
| 390 key_event = kKeyMultiple; | |
| 391 break; | |
| 392 default: | |
| 393 return false; | |
| 394 } | |
| 395 /* Get the key code of the key event. | |
| 396 * This is the physical key that was pressed, not the Unicode character. */ | |
| 397 int32_t key_code = AKeyEvent_getKeyCode(event); | |
| 398 /* Get the meta key state. */ | |
| 399 int32_t meta_state = AKeyEvent_getMetaState(event); | |
| 400 /* Get the repeat count of the event. | |
| 401 * For both key up an key down events, this is the number of times the key | |
| 402 * has repeated with the first down starting at 0 and counting up from | |
| 403 * there. For multiple key events, this is the number of down/up pairs | |
| 404 * that have occurred. */ | |
| 405 int32_t repeat = AKeyEvent_getRepeatCount(event); | |
| 406 | |
| 407 /* Get the time of the most recent key down event, in the | |
| 408 * java.lang.System.nanoTime() time base. If this is a down event, | |
| 409 * this will be the same as eventTime. | |
| 410 * Note that when chording keys, this value is the down time of the most | |
| 411 * recently pressed key, which may not be the same physical key of this | |
| 412 * event. */ | |
| 413 // TODO(gram): Use or remove this. | |
| 414 // int64_t key_down_time = AKeyEvent_getDownTime(event); | |
| 415 | |
| 416 /* Get the time this event occurred, in the | |
| 417 * java.lang.System.nanoTime() time base. */ | |
| 418 int64_t when = AKeyEvent_getEventTime(event); | |
| 419 bool isAltKeyDown = (meta_state & AMETA_ALT_ON) != 0; | |
| 420 bool isShiftKeyDown = (meta_state & AMETA_SHIFT_ON) != 0; | |
| 421 bool isCtrlKeyDown = key_code < 32; | |
| 422 | |
| 423 LOGI("Got key event %d %d", type, key_code); | |
| 424 | |
| 425 if (input_handler_->OnKeyEvent(key_event, when, key_code, | |
| 426 isAltKeyDown, isCtrlKeyDown, isShiftKeyDown, | |
| 427 repeat) != 0) { | |
| 428 return false; | |
| 429 } | |
| 430 return true; | |
| 431 } | |
| 432 | |
| 433 int32_t EventLoop::ProcessInputEvent(AInputEvent* event) { | |
| 434 int32_t event_type = AInputEvent_getType(event); | |
| 435 LOGI("Got input event type %d", event_type); | |
| 436 switch (event_type) { | |
| 437 case AINPUT_EVENT_TYPE_MOTION: | |
| 438 if (AInputEvent_getSource(event) == AINPUT_SOURCE_TOUCHSCREEN) { | |
| 439 return OnTouchEvent(event); | |
| 440 } | |
| 441 break; | |
| 442 case AINPUT_EVENT_TYPE_KEY: | |
| 443 return OnKeyEvent(event); | |
| 444 } | |
| 445 return 0; | |
| 446 } | |
| 447 | |
| 448 void EventLoop::ActivityCallback(android_app* application, int32_t command) { | |
| 449 EventLoop* event_loop = reinterpret_cast<EventLoop*>(application->userData); | |
| 450 event_loop->ProcessActivityEvent(command); | |
| 451 } | |
| 452 | |
| 453 int32_t EventLoop::InputCallback(android_app* application, | |
| 454 AInputEvent* event) { | |
| 455 EventLoop* event_loop = reinterpret_cast<EventLoop*>(application->userData); | |
| 456 return event_loop->ProcessInputEvent(event); | |
| 457 } | |
| 458 | |
| 459 void EventLoop::SensorCallback(android_app* application, | |
| 460 android_poll_source* source) { | |
| 461 EventLoop* event_loop = reinterpret_cast<EventLoop*>(application->userData); | |
| 462 event_loop->ProcessSensorEvent(); | |
| 463 } | |
| 464 | |
| OLD | NEW |