From cb8eecce51945d817088966b45fb37f366396d83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 27 Feb 2014 21:26:29 +0100 Subject: [PATCH] Platform: initial version of AndroidApplication. No input events yet. --- CMakeLists.txt | 4 + doc/building.dox | 1 + modules/FindMagnum.cmake | 11 + src/Magnum/Platform/AndroidApplication.cpp | 242 ++++++++++++++ src/Magnum/Platform/AndroidApplication.h | 357 +++++++++++++++++++++ src/Magnum/Platform/CMakeLists.txt | 19 ++ 6 files changed, 634 insertions(+) create mode 100644 src/Magnum/Platform/AndroidApplication.cpp create mode 100644 src/Magnum/Platform/AndroidApplication.h diff --git a/CMakeLists.txt b/CMakeLists.txt index df893bbf9..ee3a229dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,10 @@ if(CORRADE_TARGET_NACL) option(WITH_NACLAPPLICATION "Build NaClApplication library" OFF) cmake_dependent_option(WITH_WINDOWLESSNACLAPPLICATION "Build WindowlessNaClApplication library" OFF "NOT WITH_MAGNUMINFO" ON) +# Android-specific application libraries +elseif(CORRADE_TARGET_ANDROID) + option(WITH_ANDROIDAPPLICATION "Build AndroidApplication library" OFF) + # X11, GLX and EGL-specific application libraries elseif(CORRADE_TARGET_UNIX AND NOT APPLE) option(WITH_GLXAPPLICATION "Build GlxApplication library" OFF) diff --git a/doc/building.dox b/doc/building.dox index 20aabcd21..15516371b 100644 --- a/doc/building.dox +++ b/doc/building.dox @@ -185,6 +185,7 @@ None of the @ref Platform "application libraries" is built by default (and you need at least one). Choose the one which suits your requirements and your platform best: +- `WITH_ANDROIDAPPLICATION` - @ref Platform::AndroidApplication "AndroidApplication" - `WITH_GLUTAPPLICATION` - @ref Platform::GlutApplication "GlutApplication" - `WITH_GLXAPPLICATION` - @ref Platform::GlxApplication "GlxApplication" - `WITH_NACLAPPLICATION` - @ref Platform::NaClApplication "NaClApplication" diff --git a/modules/FindMagnum.cmake b/modules/FindMagnum.cmake index ec78ddd8b..3257507ee 100644 --- a/modules/FindMagnum.cmake +++ b/modules/FindMagnum.cmake @@ -253,6 +253,17 @@ foreach(component ${Magnum_FIND_COMPONENTS}) if(${component} MATCHES .+Application) set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_SUFFIX Magnum/Platform) + # Android application dependencies + if(${component} STREQUAL AndroidApplication) + find_package(EGL) + if(EGL_FOUND) + set(_MAGNUM_${_COMPONENT}_LIBRARIES android ${EGL_LIBRARY} ${_WINDOWCONTEXT_MAGNUM_LIBRARIES_DEPENDENCY}) + set(_MAGNUM_${_COMPONENT}_INCLUDE_DIRS ${ANDROID_NATIVE_APP_GLUE_INCLUDE_DIR}) + else() + unset(MAGNUM_${_COMPONENT}_LIBRARY) + endif() + endif() + # GLUT application dependencies if(${component} STREQUAL GlutApplication) find_package(GLUT) diff --git a/src/Magnum/Platform/AndroidApplication.cpp b/src/Magnum/Platform/AndroidApplication.cpp new file mode 100644 index 000000000..33eeecea1 --- /dev/null +++ b/src/Magnum/Platform/AndroidApplication.cpp @@ -0,0 +1,242 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "AndroidApplication.h" + +#include +#include + +#include "Magnum/Context.h" + +#include "Implementation/Egl.h" + +namespace Magnum { namespace Platform { + +/** @todo Delegating constructors when support for GCC 4.6 can be dropped */ + +AndroidApplication::AndroidApplication(const Arguments& arguments, const Configuration& configuration): _state(arguments) { + initialize(); + createContext(configuration); +} + +#ifndef DOXYGEN_GENERATING_OUTPUT +AndroidApplication::AndroidApplication(const Arguments& arguments): _state(arguments) { + initialize(); + createContext(); +} +#endif + +AndroidApplication::AndroidApplication(const Arguments& arguments, std::nullptr_t): _state(arguments) { + initialize(); +} + +AndroidApplication::~AndroidApplication() { + eglMakeCurrent(_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(_display, _context); + eglDestroySurface(_display, _surface); + eglTerminate(_display); +} + +struct AndroidApplication::LogOutput { + LogOutput(); + + Utility::AndroidLogStreamBuffer debugBuffer, warningBuffer, errorBuffer; + std::ostream debugStream, warningStream, errorStream; +}; + +AndroidApplication::LogOutput::LogOutput(): + debugBuffer(Utility::AndroidLogStreamBuffer::LogPriority::Info, "magnum"), + warningBuffer(Utility::AndroidLogStreamBuffer::LogPriority::Warning, "magnum"), + errorBuffer(Utility::AndroidLogStreamBuffer::LogPriority::Error, "magnum"), + debugStream(&debugBuffer), warningStream(&warningBuffer), errorStream(&errorBuffer) +{ + Debug::setOutput(&debugStream); + Warning::setOutput(&warningStream); + Error::setOutput(&errorStream); +} + +void AndroidApplication::initialize() { + /* Redirect debug output to Android log */ + _logOutput.reset(new LogOutput); +} + +void AndroidApplication::createContext() { createContext({}); } + +void AndroidApplication::createContext(const Configuration& configuration) { + if(!tryCreateContext(configuration)) std::exit(32); +} + +bool AndroidApplication::tryCreateContext(const Configuration& configuration) { + /* Initialize EGL */ + _display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if(!eglInitialize(_display, nullptr, nullptr)) { + Error() << "Platform::AndroidApplication::tryCreateContext(): cannot initialize EGL:" + << Implementation::eglErrorString(eglGetError()); + return false; + } + + /* Choose config */ + const EGLint configAttributes[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_BLUE_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_RED_SIZE, 8, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + EGLint configCount; + EGLConfig config; + if(!eglChooseConfig(_display, configAttributes, &config, 1, &configCount)) { + Error() << "Platform::AndroidApplication::tryCreateContext(): cannot choose EGL config:" + << Implementation::eglErrorString(eglGetError()); + return false; + } + + /* Resize native window and match it to the selected format */ + EGLint format; + CORRADE_INTERNAL_ASSERT_OUTPUT(eglGetConfigAttrib(_display, config, EGL_NATIVE_VISUAL_ID, &format)); + ANativeWindow_setBuffersGeometry(_state->window, + configuration.size().isZero() ? 0 : configuration.size().x(), + configuration.size().isZero() ? 0 : configuration.size().y(), format); + + /* Create surface and context */ + if(!(_surface = eglCreateWindowSurface(_display, config, _state->window, nullptr))) { + Error() << "Platform::AndroidApplication::tryCreateContext(): cannot create EGL window surface:" + << Implementation::eglErrorString(eglGetError()); + return false; + } + const EGLint contextAttributes[] = { + #ifdef MAGNUM_TARGET_GLES2 + EGL_CONTEXT_CLIENT_VERSION, 2, + #elif defined(MAGNUM_TARGET_GLES3) + EGL_CONTEXT_CLIENT_VERSION, 3, + #else + #error Android with desktop OpenGL? Wow, that is a new thing. + #endif + EGL_NONE + }; + if(!(_context = eglCreateContext(_display, config, EGL_NO_CONTEXT, contextAttributes))) { + Error() << "Platform::AndroidApplication::tryCreateContext(): cannot create EGL context:" + << Implementation::eglErrorString(eglGetError()); + return false; + } + + /* Make the context current */ + CORRADE_INTERNAL_ASSERT_OUTPUT(eglMakeCurrent(_display, _surface, _surface, _context)); + + _c.reset(new Context); + return true; +} + +void AndroidApplication::swapBuffers() { + eglSwapBuffers(_display, _surface); +} + +void AndroidApplication::viewportEvent(const Vector2i&) {} + +namespace { + struct Data { + Data(std::unique_ptr(*instancer)(const AndroidApplication::Arguments&)): instancer(instancer) {} + + std::unique_ptr(*instancer)(const AndroidApplication::Arguments&); + std::unique_ptr instance; + }; +} + +void AndroidApplication::commandEvent(android_app* state, int32_t cmd) { + Data& data = *static_cast(state->userData); + + switch (cmd) { + case APP_CMD_SAVE_STATE: + /** @todo Make use of this */ + break; + + case APP_CMD_INIT_WINDOW: + /* Create the application */ + if(!data.instance) { + data.instance = data.instancer(state); + data.instance->drawEvent(); + } + break; + + case APP_CMD_TERM_WINDOW: + /* Destroy the application */ + data.instance.reset(); + break; + + case APP_CMD_GAINED_FOCUS: + case APP_CMD_LOST_FOCUS: + /** @todo Make use of these */ + break; + } +} + +std::int32_t AndroidApplication::inputEvent(android_app*, AInputEvent*) { + /** @todo Implement input events */ + return 0; +} + +void AndroidApplication::exec(android_app* state, std::unique_ptr(*instancer)(const Arguments&)) { + state->onAppCmd = commandEvent; + state->onInputEvent = inputEvent; + + /* Make sure the glue isn't stripped. WHY WHYYY CAN'T THIS BE DONE SOME + SANE WAY WHYY */ + app_dummy(); + + /** @todo Make use of saved state */ + Data data{instancer}; + state->userData = &data; + + for(;;) { + /* Read all pending events. Block and wait for them only if the app + doesn't want to redraw immediately WHY THIS GODDAMN THING DOESNT + HAVE SOMETHING LIKE WAIT FOR EVENT SO I NEED TO TANGLE THIS TANGLED + MESS OF HELL */ + int ident, events; + android_poll_source* source; + while((ident = ALooper_pollAll( + data.instance && (data.instance->_flags & Flag::Redraw) ? 0 : -1, + nullptr, &events, reinterpret_cast(&source))) >= 0) + { + /* Process this event OH SIR MAY MY POOR EXISTENCE CALL THIS + FUNCTION FOR YOU IF YOU DON'T MIND? */ + if(source) source->process(state, source); + + /* Exit WHY THIS HAS TO BE HANDLED HERE WHILE EVERY OTHER THING + IS HANDLED THROUGH CALLBACK GODDAMMIT */ + if(state->destroyRequested != 0) return; + } + + /* Redraw the app if it wants to be redrawn. Frame limiting is done by + Android itself */ + if(data.instance && (data.instance->_flags & Flag::Redraw)) + data.instance->drawEvent(); + } + + state->userData = nullptr; +} + +}} diff --git a/src/Magnum/Platform/AndroidApplication.h b/src/Magnum/Platform/AndroidApplication.h new file mode 100644 index 000000000..305f09481 --- /dev/null +++ b/src/Magnum/Platform/AndroidApplication.h @@ -0,0 +1,357 @@ +#ifndef Magnum_Platform_AndroidApplication_h +#define Magnum_Platform_AndroidApplication_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/** @file + * @brief Class @ref Magnum::Platform::AndroidApplication + */ + +#include +#include +#include + +#include "Magnum/Magnum.h" +#include "Magnum/Math/Vector2.h" +#include "Magnum/Platform/Platform.h" + +#ifndef CORRADE_TARGET_ANDROID +#error This file is available only on Android +#endif + +namespace Magnum { namespace Platform { + +/** @nosubgrouping +@brief Android application + +Application running in Android. + +This application library is available only in +@ref CORRADE_TARGET_ANDROID "Android", see respective sections +in @ref building-corrade-cross-android "Corrade's" and @ref building-cross-android "Magnum's" +building documentation. It is built if `WITH_ANDROIDAPPLICATION` is enabled in +CMake. + +## Bootstrap application + +Fully contained base application using @ref GlutApplication for desktop build +and @ref AndroidApplication for Android build along with full Android packaging +stuff and CMake setup is available in `base-android` branch of +[Magnum Bootstrap](https://github.com/mosra/magnum-bootstrap) repository, +download it as [tar.gz](https://github.com/mosra/magnum-bootstrap/archive/base-android.tar.gz) +or [zip](https://github.com/mosra/magnum-bootstrap/archive/base-android.zip) file. +After extracting the downloaded archive, you can do the desktop build in the +same way as with @ref GlutApplication. For the Android build you also +need to put the contents of toolchains repository from https://github.com/mosra/toolchains +in `toolchains/` subdirectory. Don't forget to adapt `ANDROID_NDK_ROOT` in +`toolchains/generic/Android-*.cmake` to path where NDK is installed. Default is +`/opt/android-ndk`. Adapt also `ANDROID_SYSROOT` to your preferred API level. +You might also need to update `ANDROID_TOOLCHAIN_PREFIX` and +`ANDROID_TOOLCHAIN_ROOT` to fit your system. + +First you need to update Android project files with the following command. It +will create `build.xml` file for Ant and a bunch of other files. You need to +specify the target for which you will build in the `-t` parameter. List of all +targets can be obtained by calling `android list target`. + + android update project -p . -t "android-19" + +Then create build directories for ARM and x86 and run `cmake` and build command +in them. The toolchains need access to the platform file, so be sure to +properly set **absolute** path to `toolchains/modules/` directory containing +`Platform/Android.cmake`. + + mkdir build-android-arm && cd build-android-arm + cmake .. \ + -DCMAKE_MODULE_PATH="/absolute/path/to/toolchains/modules" \ + -DCMAKE_TOOLCHAIN_FILE="../toolchains/generic/Android-ARM.cmake" + cmake --build . + + mkdir build-android-x86 && cd build-android-x86 + cmake .. \ + -DCMAKE_MODULE_PATH="/absolute/path/to/toolchains/modules" \ + -DCMAKE_TOOLCHAIN_FILE="../toolchains/generic/Android-x86.cmake" + cmake --build . + +The compiled binaries will be put into `lib/armeabi-v7a` and `lib/x86`. You can +then build the APK package simply by running `ant`. The resulting APK package +can be then installed directly on the device or emulator using `adb install`. + + ant debug + adb install bin/NativeActivity-debug.apk + +## General usage + +For CMake you need to copy `FindEGL.cmake` and `FindOpenGLES2.cmake` (or +`FindOpenGLES3.cmake`) from `modules/` directory in %Magnum source to `modules/` +dir in your project (so it is able to find EGL and OpenGL ES libraries). +Request `%AndroidApplication` component, add +`${MAGNUM_ANDROIDAPPLICATION_INCLUDE_DIRS}` to include path and link to +`${MAGNUM_ANDROIDAPPLICATION_LIBRARIES}`. If no other application is requested, +you can also use generic `${MAGNUM_APPLICATION_INCLUDE_DIRS}` and +`${MAGNUM_APPLICATION_LIBRARIES}` aliases to simplify porting. See +@ref building and @ref cmake for more information. Note that unlike on other +platforms you need to create *shared library* instead of executable. The +resulting binary then needs to be copied to `lib/armeabi-v7a` and `lib/x86`, +you can do that automatically in CMake using the following commands: + + file(MAKE_DIRECTORY "${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}") + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}") + +In C++ code you need to implement at least @ref drawEvent() to be able to draw +on the screen. The subclass must be then made accessible from JNI using +@ref MAGNUM_ANDROIDAPPLICATION_MAIN() macro. See @ref platform for more +information. +@code +class MyApplication: public Platform::AndroidApplication { + // implement required methods... +}; +MAGNUM_ANDROIDAPPLICATION_MAIN(MyApplication) +@endcode + +If no other application header is included, this class is also aliased to +`Platform::Application` and the macro is aliased to `MAGNUM_APPLICATION_MAIN()` +to simplify porting. + +### Android packaging stuff + +The application needs at least the `AndroidManifest.xml` with the following +contents: + + + + + + + + + + + + + + + + +Replace `{{name}}` with the actual application name and `{{application}}` with +name of the binary file (without extension). If you plan to use OpenGL ES, set +`android:glEsVersion` to `0x00030000`. + +## Redirecting output to Android log buffer + +The application by default redirects @ref Corrade::Utility::Debug "Debug", +@ref Corrade::Utility::Warning "Warning" and @ref Corrade::Utility::Error "Error" +output to Android log buffer with tag `"magnum"`, which can be then accessed +through `logcat` utility. See also @ref Corrade::Utility::AndroidLogStreamBuffer +for more information. +*/ +class AndroidApplication { + public: + /** @brief Application arguments */ + typedef android_app* Arguments; + + class Configuration; + + /** + * @brief Execute the application + * + * See @ref MAGNUM_ANDROIDAPPLICATION_MAIN() for usage information. + */ + static void exec(android_app* state, std::unique_ptr(*instancer)(const Arguments&)); + + #ifndef DOXYGEN_GENERATING_OUTPUT + template static std::unique_ptr instancer(const Arguments& arguments) { + return std::unique_ptr{new T{arguments}}; + } + #endif + + /** @copydoc Sdl2Application::Sdl2Application(const Arguments&, const Configuration&) */ + #ifdef DOXYGEN_GENERATING_OUTPUT + explicit AndroidApplication(const Arguments& arguments, const Configuration& configuration = Configuration()); + #else + /* To avoid "invalid use of incomplete type" */ + explicit AndroidApplication(const Arguments& arguments, const Configuration& configuration); + explicit AndroidApplication(const Arguments& arguments); + #endif + + /** @copydoc Sdl2Application::Sdl2Application(const Arguments&, std::nullptr_t) */ + explicit AndroidApplication(const Arguments& arguments, std::nullptr_t); + + /** @brief Copying is not allowed */ + AndroidApplication(const AndroidApplication&) = delete; + + /** @brief Moving is not allowed */ + AndroidApplication(AndroidApplication&&) = delete; + + virtual ~AndroidApplication(); + + /** @brief Copying is not allowed */ + AndroidApplication& operator=(const AndroidApplication&) = delete; + + /** @brief Moving is not allowed */ + AndroidApplication& operator=(AndroidApplication&&) = delete; + + protected: + /** @copydoc Sdl2Application::createContext() */ + #ifdef DOXYGEN_GENERATING_OUTPUT + void createContext(const Configuration& configuration = Configuration()); + #else + /* To avoid "invalid use of incomplete type" */ + void createContext(const Configuration& configuration); + void createContext(); + #endif + + /** @copydoc Sdl2Application::tryCreateContext() */ + bool tryCreateContext(const Configuration& configuration); + + /** @{ @name Screen handling */ + + /** @copydoc Sdl2Application::swapBuffers() */ + void swapBuffers(); + + /** @copydoc Sdl2Application::redraw() */ + void redraw() { _flags |= Flag::Redraw; } + + #ifdef DOXYGEN_GENERATING_OUTPUT + protected: + #else + private: + #endif + /** @copydoc Sdl2Application::viewportEvent() */ + virtual void viewportEvent(const Vector2i& size); + + /** @copydoc Sdl2Application::drawEvent() */ + virtual void drawEvent() = 0; + + /*@}*/ + + private: + struct LogOutput; + + enum class Flag: UnsignedByte { + Redraw = 1 << 1 + }; + typedef Containers::EnumSet Flags; + + static void commandEvent(android_app* app, std::int32_t cmd); + static std::int32_t inputEvent(android_app* app, AInputEvent* event); + + void initialize(); + + android_app* const _state; + Flags _flags; + + EGLDisplay _display; + EGLSurface _surface; + EGLContext _context; + + std::unique_ptr _c; + std::unique_ptr _logOutput; + + CORRADE_ENUMSET_FRIEND_OPERATORS(Flags) +}; + +CORRADE_ENUMSET_OPERATORS(AndroidApplication::Flags) + +/** +@brief %Configuration + +Double-buffered RGBA canvas with depth and stencil buffers. +@see @ref AndroidApplication(), @ref createContext(), @ref tryCreateContext() +*/ +class AndroidApplication::Configuration { + public: + constexpr /*implicit*/ Configuration() {} + + /** + * @brief Set window title + * @return Reference to self (for method chaining) + * + * @note This function does nothing and is included only for + * compatibility with other toolkits. You need to set the title + * separately in the XML file. + */ + template Configuration& setTitle(const T&) { return *this; } + + /** @brief Window size */ + Vector2i size() const { return _size; } + + /** + * @brief Set window size + * @return Reference to self (for method chaining) + * + * Default is `{0, 0}`, which means that the size of the physical + * window will be used. If set to different value than the physical + * size, the surface will be scaled. + */ + Configuration& setSize(const Vector2i& size) { + _size = size; + return *this; + } + + /** + * @brief Set context version + * + * @note This function does nothing and is included only for + * compatibility with other toolkits. @ref Version::GLES200 or + * @ref Version::GLES300 is used based on engine compile-time + * settings. + */ + Configuration& setVersion(Version) { return *this; } + + private: + Vector2i _size; +}; + +/** @hideinitializer +@brief Entry point for Android applications +@param className Class name + +See @ref Magnum::Platform::AndroidApplication "Platform::AndroidApplication" +for usage information. This macro abstracts out platform-specific entry point +code (the classic `main()` function cannot be used in Android). See +@ref portability-applications for more information. When no other application +header is included this macro is also aliased to `MAGNUM_APPLICATION_MAIN()`. +*/ +#define MAGNUM_ANDROIDAPPLICATION_MAIN(className) \ + void android_main(android_app* state) { \ + Magnum::Platform::AndroidApplication::exec(state, \ + Magnum::Platform::AndroidApplication::instancer); \ + } + +#ifndef DOXYGEN_GENERATING_OUTPUT +#ifndef MAGNUM_APPLICATION_MAIN +typedef AndroidApplication Application; +typedef BasicScreen Screen; +typedef BasicScreenedApplication ScreenedApplication; +#define MAGNUM_APPLICATION_MAIN(className) MAGNUM_ANDROIDAPPLICATION_MAIN(className) +#else +#undef MAGNUM_APPLICATION_MAIN +#endif +#endif + +}} + +#endif diff --git a/src/Magnum/Platform/CMakeLists.txt b/src/Magnum/Platform/CMakeLists.txt index a3686d64d..320cafdc1 100644 --- a/src/Magnum/Platform/CMakeLists.txt +++ b/src/Magnum/Platform/CMakeLists.txt @@ -32,6 +32,25 @@ set(Platform_HEADERS install(FILES ${Platform_HEADERS} DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Platform) +# Android application +if(WITH_ANDROIDAPPLICATION) + if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL Android) + message(FATAL_ERROR "AndroidApplication is available only when targeting Android. Set WITH_ANDROIDAPPLICATION to OFF to skip building it.") + endif() + + include_directories(${ANDROID_NATIVE_APP_GLUE_INCLUDE_DIR}) + + add_library(MagnumAndroidApplication STATIC + AndroidApplication.cpp + Implementation/Egl.cpp + ${ANDROID_NATIVE_APP_GLUE_SRC}) + install(FILES AndroidApplication.h DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Platform) + install(TARGETS MagnumAndroidApplication + RUNTIME DESTINATION ${MAGNUM_BINARY_INSTALL_DIR} + LIBRARY DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR} + ARCHIVE DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR}) +endif() + # GLUT application if(WITH_GLUTAPPLICATION) find_package(GLUT)