/* * Copyright 2011-2014 Branimir Karadzic. All rights reserved. * License: http://www.opensource.org/licenses/BSD-2-Clause */ #include "entry_p.h" #if ENTRY_CONFIG_USE_NATIVE && BX_PLATFORM_ANDROID #include #include #include #include #include #include #include #include extern "C" { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #include #pragma GCC diagnostic pop } // extern "C" namespace entry { struct MainThreadEntry { int m_argc; char** m_argv; static int32_t threadFunc(void* _userData); }; struct Context { Context() : m_window(NULL) , m_count(0) { } void run(android_app* _app) { m_app = _app; m_app->userData = (void*)this; m_app->onAppCmd = onAppCmdCB; m_app->onInputEvent = onInputEventCB; ANativeActivity_setWindowFlags(m_app->activity, 0 | AWINDOW_FLAG_FULLSCREEN | AWINDOW_FLAG_KEEP_SCREEN_ON , 0 ); const char* argv[1] = { "android.so" }; m_mte.m_argc = 1; m_mte.m_argv = const_cast(argv); while (0 == m_app->destroyRequested) { int32_t num; android_poll_source* source; /*int32_t id =*/ ALooper_pollAll(-1, NULL, &num, (void**)&source); if (NULL != source) { source->process(m_app, source); } } m_thread.shutdown(); } void onAppCmd(int32_t _cmd) { switch (_cmd) { case APP_CMD_INPUT_CHANGED: // Command from main thread: the AInputQueue has changed. Upon processing // this command, android_app->inputQueue will be updated to the new queue // (or NULL). break; case APP_CMD_INIT_WINDOW: // Command from main thread: a new ANativeWindow is ready for use. Upon // receiving this command, android_app->window will contain the new window // surface. if (m_window == NULL) { m_window = m_app->window; bgfx::androidSetWindow(m_window); int32_t width = ANativeWindow_getWidth(m_window); int32_t height = ANativeWindow_getHeight(m_window); DBG("ANativeWindow width %d, height %d", width, height); WindowHandle defaultWindow = { 0 }; m_eventQueue.postSizeEvent(defaultWindow, width, height); m_thread.init(MainThreadEntry::threadFunc, &m_mte); } break; case APP_CMD_TERM_WINDOW: // Command from main thread: the existing ANativeWindow needs to be // terminated. Upon receiving this command, android_app->window still // contains the existing window; after calling android_app_exec_cmd // it will be set to NULL. break; case APP_CMD_WINDOW_RESIZED: // Command from main thread: the current ANativeWindow has been resized. // Please redraw with its new size. break; case APP_CMD_WINDOW_REDRAW_NEEDED: // Command from main thread: the system needs that the current ANativeWindow // be redrawn. You should redraw the window before handing this to // android_app_exec_cmd() in order to avoid transient drawing glitches. break; case APP_CMD_CONTENT_RECT_CHANGED: // Command from main thread: the content area of the window has changed, // such as from the soft input window being shown or hidden. You can // find the new content rect in android_app::contentRect. break; case APP_CMD_GAINED_FOCUS: // Command from main thread: the app's activity window has gained // input focus. break; case APP_CMD_LOST_FOCUS: // Command from main thread: the app's activity window has lost // input focus. break; case APP_CMD_CONFIG_CHANGED: // Command from main thread: the current device configuration has changed. break; case APP_CMD_LOW_MEMORY: // Command from main thread: the system is running low on memory. // Try to reduce your memory use. break; case APP_CMD_START: // Command from main thread: the app's activity has been started. break; case APP_CMD_RESUME: // Command from main thread: the app's activity has been resumed. break; case APP_CMD_SAVE_STATE: // Command from main thread: the app should generate a new saved state // for itself, to restore from later if needed. If you have saved state, // allocate it with malloc and place it in android_app.savedState with // the size in android_app.savedStateSize. The will be freed for you // later. break; case APP_CMD_PAUSE: // Command from main thread: the app's activity has been paused. break; case APP_CMD_STOP: // Command from main thread: the app's activity has been stopped. break; case APP_CMD_DESTROY: // Command from main thread: the app's activity is being destroyed, // and waiting for the app thread to clean up and exit before proceeding. m_eventQueue.postExitEvent(); break; } } int32_t onInputEvent(AInputEvent* _event) { int32_t type = AInputEvent_getType(_event); switch (type) { case AINPUT_EVENT_TYPE_MOTION: { float mx = AMotionEvent_getX(_event, 0); float my = AMotionEvent_getY(_event, 0); int32_t count = AMotionEvent_getPointerCount(_event); int32_t actionBits = AMotionEvent_getAction(_event); int32_t action = (actionBits & AMOTION_EVENT_ACTION_MASK); int32_t index = (actionBits & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; WindowHandle defaultWindow = { 0 }; count = m_count; switch (action) { case AMOTION_EVENT_ACTION_DOWN: case AMOTION_EVENT_ACTION_POINTER_DOWN: m_count++; break; case AMOTION_EVENT_ACTION_UP: case AMOTION_EVENT_ACTION_POINTER_UP: m_count--; break; default: break; } if (count != m_count) { m_eventQueue.postMouseEvent(defaultWindow , (int32_t)mx , (int32_t)my , 0 , 1 == count ? MouseButton::Left : MouseButton::Right , false ); if (0 != m_count) { m_eventQueue.postMouseEvent(defaultWindow , (int32_t)mx , (int32_t)my , 0 , 1 == m_count ? MouseButton::Left : MouseButton::Right , true ); } } switch (action) { case AMOTION_EVENT_ACTION_MOVE: if (0 == index) { m_eventQueue.postMouseEvent(defaultWindow , (int32_t)mx , (int32_t)my , 0 ); } break; default: break; } } break; case AINPUT_EVENT_TYPE_KEY: break; } return 0; } static void onAppCmdCB(struct android_app* _app, int32_t _cmd) { Context* self = (Context*)_app->userData; self->onAppCmd(_cmd); } static int32_t onInputEventCB(struct android_app* _app, AInputEvent* _event) { Context* self = (Context*)_app->userData; return self->onInputEvent(_event); } MainThreadEntry m_mte; bx::Thread m_thread; EventQueue m_eventQueue; ANativeWindow* m_window; android_app* m_app; int32_t m_count; }; static Context s_ctx; const Event* poll() { return s_ctx.m_eventQueue.poll(); } const Event* poll(WindowHandle _handle) { return s_ctx.m_eventQueue.poll(_handle); } void release(const Event* _event) { s_ctx.m_eventQueue.release(_event); } WindowHandle createWindow(int32_t _x, int32_t _y, uint32_t _width, uint32_t _height, uint32_t _flags, const char* _title) { BX_UNUSED(_x, _y, _width, _height, _flags, _title); WindowHandle handle = { UINT16_MAX }; return handle; } void destroyWindow(WindowHandle _handle) { BX_UNUSED(_handle); } void setWindowPos(WindowHandle _handle, int32_t _x, int32_t _y) { BX_UNUSED(_handle, _x, _y); } void setWindowSize(WindowHandle _handle, uint32_t _width, uint32_t _height) { BX_UNUSED(_handle, _width, _height); } void setWindowTitle(WindowHandle _handle, const char* _title) { BX_UNUSED(_handle, _title); } void toggleWindowFrame(WindowHandle _handle) { BX_UNUSED(_handle); } void setMouseLock(WindowHandle _handle, bool _lock) { BX_UNUSED(_handle, _lock); } int32_t MainThreadEntry::threadFunc(void* _userData) { int32_t result = chdir("/sdcard/bgfx/examples/runtime"); BX_CHECK(0 == result, "Failed to chdir to dir. android.permission.WRITE_EXTERNAL_STORAGE?", errno); MainThreadEntry* self = (MainThreadEntry*)_userData; result = main(self->m_argc, self->m_argv); // PostMessage(s_ctx.m_hwnd, WM_QUIT, 0, 0); return result; } } // namespace entry extern "C" void android_main(android_app* _app) { using namespace entry; s_ctx.run(_app); } #endif // ENTRY_CONFIG_USE_NATIVE && BX_PLATFORM_ANDROID