From 6379f8a616742de5517ea53ae9832244a7f4da21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 13 Apr 2016 16:52:29 +0200 Subject: [PATCH] Platform: added WindowlessIosApplication. --- CMakeLists.txt | 4 + doc/building.dox | 1 + doc/cmake.dox | 1 + modules/FindMagnum.cmake | 12 +- src/Magnum/Platform/CMakeLists.txt | 35 +++ .../Platform/WindowlessIosApplication.h | 230 ++++++++++++++++++ .../Platform/WindowlessIosApplication.mm | 89 +++++++ 7 files changed, 371 insertions(+), 1 deletion(-) create mode 100644 src/Magnum/Platform/WindowlessIosApplication.h create mode 100644 src/Magnum/Platform/WindowlessIosApplication.mm diff --git a/CMakeLists.txt b/CMakeLists.txt index 117d4a8a7..5777e8f3c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,6 +100,10 @@ if(CORRADE_TARGET_NACL) elseif(CORRADE_TARGET_ANDROID) option(WITH_ANDROIDAPPLICATION "Build AndroidApplication library" OFF) +# iOS-specific application libraries +elseif(CORRADE_TARGET_IOS) + option(WITH_WINDOWLESSIOSAPPLICATION "Build WindowlessIosApplication library" OFF) + # OS X-specific application libraries elseif(CORRADE_TARGET_APPLE) cmake_dependent_option(WITH_WINDOWLESSCGLAPPLICATION "Build WindowlessCglApplication library" OFF "NOT WITH_MAGNUMINFO;NOT WITH_FONTCONVERTER;NOT WITH_DISTANCEFIELDCONVERTER" ON) diff --git a/doc/building.dox b/doc/building.dox index c454b6e00..3f24cecf9 100644 --- a/doc/building.dox +++ b/doc/building.dox @@ -214,6 +214,7 @@ platform best: - `WITH_WINDOWLESSCGLAPPLICATION` - @ref Platform::WindowlessCglApplication "WindowlessCglApplication" - `WITH_WINDOWLESSEGLAPPLICATION` - @ref Platform::WindowlessEglApplication "WindowlessEglApplication" - `WITH_WINDOWLESSGLXAPPLICATION` - @ref Platform::WindowlessGlxApplication "WindowlessGlxApplication" +- `WITH_WINDOWLESSIOSAPPLICATION` - @ref Platform::WindowlessIosApplication "WindowlessIosApplication" - `WITH_WINDOWLESSNACLAPPLICATION` - @ref Platform::WindowlessNaClApplication "WindowlessNaClApplication" - `WITH_WINDOWLESSWGLAPPLICATION` - @ref Platform::WindowlessWglApplication "WindowlessWglApplication" - `WITH_WINDOWLESSWINDOWSEGLAPPLICATION` - @ref Platform::WindowlessWindowsEglApplication "WindowlessWindowsEglApplication" diff --git a/doc/cmake.dox b/doc/cmake.dox index dd4d9fe62..daede57fc 100644 --- a/doc/cmake.dox +++ b/doc/cmake.dox @@ -102,6 +102,7 @@ Platform namespace is split into more components: - `WindowlessCglApplication` -- @ref Platform::WindowlessCglApplication "WindowlessCglApplication" - `WindowlessEglApplication` -- @ref Platform::WindowlessEglApplication "WindowlessEglApplication" - `WindowlessGlxApplication` -- @ref Platform::WindowlessGlxApplication "WindowlessGlxApplication" +- `WindowlessIosApplication` -- @ref Platform::WindowlessIosApplication "WindowlessIosApplication" - `WindowlessNaClApplication` -- @ref Platform::WindowlessNaClApplication "WindowlessNaClApplication" - `WindowlessWglApplication` -- @ref Platform::WindowlessWglApplication "WindowlessWglApplication" - `WindowlessWindowsEglApplication` -- @ref Platform::WindowlessWindowsEglApplication "WindowlessWindowsEglApplication" diff --git a/modules/FindMagnum.cmake b/modules/FindMagnum.cmake index 67895a17c..e360fce05 100644 --- a/modules/FindMagnum.cmake +++ b/modules/FindMagnum.cmake @@ -55,6 +55,7 @@ # WindowlessCglApplication - Windowless CGL application # WindowlessEglApplication - Windowless EGL application # WindowlessGlxApplication - Windowless GLX application +# WindowlessIosApplication - Windowless iOS application # WindowlessNaClApplication - Windowless NaCl application # WindowlessWglApplication - Windowless WGL application # WindowlessWindowsEglApplication - Windowless Windows/EGL application @@ -337,7 +338,7 @@ endif() # Component distinction (listing them explicitly to avoid mistakes with finding # components from other repositories) -set(_MAGNUM_LIBRARY_COMPONENTS "^(Audio|DebugTools|MeshTools|Primitives|SceneGraph|Shaders|Shapes|Text|TextureTools|AndroidApplication|GlutApplication|GlxApplication|NaClApplication|Sdl2Application|XEglApplication|WindowlessCglApplication|WindowlessEglApplication|WindowlessGlxApplication|WindowlessNaClApplication|WindowlessWglApplication|WindowlessWindowsEglApplication|CglContext|EglContext|GlxContext|WglContext)$") +set(_MAGNUM_LIBRARY_COMPONENTS "^(Audio|DebugTools|MeshTools|Primitives|SceneGraph|Shaders|Shapes|Text|TextureTools|AndroidApplication|GlutApplication|GlxApplication|NaClApplication|Sdl2Application|XEglApplication|WindowlessCglApplication|WindowlessEglApplication|WindowlessGlxApplication|WindowlessIosApplication|WindowlessNaClApplication|WindowlessWglApplication|WindowlessWindowsEglApplication|CglContext|EglContext|GlxContext|WglContext)$") set(_MAGNUM_PLUGIN_COMPONENTS "^(MagnumFont|MagnumFontConverter|ObjImporter|TgaImageConverter|TgaImporter|WavAudioImporter)$") set(_MAGNUM_EXECUTABLE_COMPONENTS "^(distancefieldconverter|fontconverter|info)$") @@ -500,6 +501,15 @@ foreach(_component ${Magnum_FIND_COMPONENTS}) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES EGL::EGL) + # Windowless iOS application dependencies + elseif(_component STREQUAL WindowlessIosApplication) + # We need to link to Foundation framework to use ObjC + find_library(_MAGNUM_IOS_FOUNDATION_FRAMEWORK_LIBRARY Foundation) + mark_as_advanced(_MAGNUM_IOS_FOUNDATION_FRAMEWORK_LIBRARY) + find_package(EGL) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES EGL::EGL ${_MAGNUM_IOS_FOUNDATION_FRAMEWORK_LIBRARY}) + # Windowless WGL application has no additional dependencies # Windowless Windows/EGL application dependencies diff --git a/src/Magnum/Platform/CMakeLists.txt b/src/Magnum/Platform/CMakeLists.txt index 89f348dfc..43fc624b0 100644 --- a/src/Magnum/Platform/CMakeLists.txt +++ b/src/Magnum/Platform/CMakeLists.txt @@ -357,6 +357,41 @@ if(WITH_WINDOWLESSGLXAPPLICATION) add_library(Magnum::WindowlessGlxApplication ALIAS MagnumWindowlessGlxApplication) endif() +# Windowless iOS application +if(WITH_WINDOWLESSIOSAPPLICATION) + set(NEED_EGLCONTEXT 1) + + # We need to link to Foundation framework to use ObjC + find_library(_MAGNUM_IOS_FOUNDATION_FRAMEWORK_LIBRARY Foundation) + mark_as_advanced(_MAGNUM_IOS_FOUNDATION_FRAMEWORK_LIBRARY) + + set(MagnumWindowlessIosApplication_SRCS + WindowlessIosApplication.mm + $) + set(MagnumWindowlessIosApplication_HEADERS + WindowlessIosApplication.h) + + add_library(MagnumWindowlessIosApplication STATIC + ${MagnumWindowlessIosApplication_SRCS} + ${MagnumWindowlessIosApplication_HEADERS}) + set_target_properties(MagnumWindowlessIosApplication PROPERTIES DEBUG_POSTFIX "-d") + target_link_libraries(MagnumWindowlessIosApplication + Magnum + EGL::EGL + ${_MAGNUM_IOS_FOUNDATION_FRAMEWORK_LIBRARY}) + # Assuming that PIC is not needed because the Application lib is always + # linked to the executable and not to any intermediate shared lib + + install(FILES ${MagnumWindowlessIosApplication_HEADERS} DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Platform) + install(TARGETS MagnumWindowlessIosApplication + RUNTIME DESTINATION ${MAGNUM_BINARY_INSTALL_DIR} + LIBRARY DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR} + ARCHIVE DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR}) + + # Magnum WindowlessIosApplication target alias for superprojects + add_library(Magnum::WindowlessIosApplication ALIAS MagnumWindowlessIosApplication) +endif() + # Windowless WGL application if(WITH_WINDOWLESSWGLAPPLICATION) set(NEED_WGLCONTEXT 1) diff --git a/src/Magnum/Platform/WindowlessIosApplication.h b/src/Magnum/Platform/WindowlessIosApplication.h new file mode 100644 index 000000000..aa08accdb --- /dev/null +++ b/src/Magnum/Platform/WindowlessIosApplication.h @@ -0,0 +1,230 @@ +#ifndef Magnum_Platform_WindowlessIosApplication_h +#define Magnum_Platform_WindowlessIosApplication_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015 + 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::WindowlessIosApplication, macro @ref MAGNUM_WINDOWLESSIOSAPPLICATION_MAIN() + */ + +#include +#include + +#include "Magnum/Magnum.h" +#include "Magnum/OpenGL.h" +#include "Magnum/Platform/Platform.h" + +#ifdef __OBJC__ +@class EAGLContext; +#else +struct EAGLContext; +#endif + +namespace Magnum { namespace Platform { + +/** +@brief Windowless iOS application + +Application for offscreen rendering using EAGL on iOS. Does not have any +default framebuffer. It is built if `WITH_WINDOWLESSIOSAPPLICATION` is enabled +in CMake. + +## Bootstrap application + +Fully contained windowless application using @ref WindowlessIosApplication +along with CMake setup is available in `windowless` branch of +[Magnum Bootstrap](https://github.com/mosra/magnum-bootstrap) repository, +download it as [tar.gz](https://github.com/mosra/magnum-bootstrap/archive/windowless.tar.gz) +or [zip](https://github.com/mosra/magnum-bootstrap/archive/windowless.zip) +file. After extracting the downloaded archive you can build and run the +application with these four commands: + + mkdir build && cd build + cmake .. + cmake --build . + ./src/MyApplication # or ./src/Debug/MyApplication + +See @ref cmake for more information. + +## General usage + +In CMake you need to request `WindowlessIosApplication` component and link to +`Magnum::WindowlessIosApplication` target. If no other windowless application +is requested, you can also use generic `Magnum::WindowlessApplication` alias to +simplify porting. Again, see @ref building and @ref cmake for more information. + +Place your code into @ref exec(). The subclass can be then used in main +function using @ref MAGNUM_WINDOWLESSIOSAPPLICATION_MAIN() macro. See +@ref platform for more information. +@code +class MyApplication: public Platform::WindowlessIosApplication { + // implement required methods... +}; +MAGNUM_WINDOWLESSIOSAPPLICATION_MAIN(MyApplication) +@endcode + +If no other application header is included, this class is also aliased to +`Platform::WindowlessApplication` and the macro is aliased to +`MAGNUM_WINDOWLESSAPPLICATION_MAIN()` to simplify porting. +*/ +class WindowlessIosApplication { + public: + /** @brief Application arguments */ + struct Arguments { + /** @brief Constructor */ + /*implicit*/ constexpr Arguments(int& argc, char** argv) noexcept: argc{argc}, argv{argv} {} + + int& argc; /**< @brief Argument count */ + char** argv; /**< @brief Argument values */ + }; + + class Configuration; + + /** @copydoc Sdl2Application::Sdl2Application(const Arguments&, const Configuration&) */ + #ifdef DOXYGEN_GENERATING_OUTPUT + explicit WindowlessIosApplication(const Arguments& arguments, const Configuration& configuration = Configuration()); + #else + /* To avoid "invalid use of incomplete type" */ + explicit WindowlessIosApplication(const Arguments& arguments, const Configuration& configuration); + explicit WindowlessIosApplication(const Arguments& arguments); + #endif + + /** @copydoc Sdl2Application::Sdl2Application(const Arguments&, std::nullptr_t) */ + explicit WindowlessIosApplication(const Arguments& arguments, std::nullptr_t); + + /** @brief Copying is not allowed */ + WindowlessIosApplication(const WindowlessIosApplication&) = delete; + + /** @brief Moving is not allowed */ + WindowlessIosApplication(WindowlessIosApplication&&) = delete; + + /** @brief Copying is not allowed */ + WindowlessIosApplication& operator=(const WindowlessIosApplication&) = delete; + + /** @brief Moving is not allowed */ + WindowlessIosApplication& operator=(WindowlessIosApplication&&) = delete; + + /** + * @brief Execute application + * @return Value for returning from `main()` + * + * See @ref MAGNUM_WINDOWLESSIOSAPPLICATION_MAIN() for usage + * information. + */ + virtual int exec() = 0; + + protected: + /* Nobody will need to have (and delete) WindowlessIosApplication*, + thus this is faster than public pure virtual destructor */ + ~WindowlessIosApplication(); + + /** @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); + + private: + EAGLContext* _glContext; + + std::unique_ptr _context; +}; + +/** +@brief Configuration + +@see @ref WindowlessIosApplication(), @ref createContext(), + @ref tryCreateContext() +*/ +class WindowlessIosApplication::Configuration { + public: + /** + * @brief Context flag + * + * @see @ref Flags, @ref setFlags(), @ref Context::Flag + */ + enum class Flag: int {}; + + /** + * @brief Context flags + * + * @see @ref setFlags(), @ref Context::Flags + */ + typedef Containers::EnumSet Flags; + + constexpr /*implicit*/ Configuration() {} + + /** @brief Context flags */ + Flags flags() const { return _flags; } + + /** + * @brief Set context flags + * @return Reference to self (for method chaining) + * + * Default is no flag. See also @ref Context::flags(). + */ + Configuration& setFlags(Flags flags) { + _flags = flags; + return *this; + } + + private: + Flags _flags; +}; + +/** @hideinitializer +@brief Entry point for windowless EGL application +@param className Class name + +See @ref Magnum::Platform::WindowlessIosApplication "Platform::WindowlessIosApplication" +for usage information. This macro abstracts out platform-specific entry point +code, see @ref portability-applications for more information. When no other +windowless application header is included this macro is also aliased to +`MAGNUM_WINDOWLESSAPPLICATION_MAIN()`. +*/ +#define MAGNUM_WINDOWLESSIOSAPPLICATION_MAIN(className) \ + int main(int argc, char** argv) { \ + className app({argc, argv}); \ + return app.exec(); \ + } + +#ifndef DOXYGEN_GENERATING_OUTPUT +#ifndef MAGNUM_WINDOWLESSAPPLICATION_MAIN +typedef WindowlessIosApplication WindowlessApplication; +#define MAGNUM_WINDOWLESSAPPLICATION_MAIN(className) MAGNUM_WINDOWLESSIOSAPPLICATION_MAIN(className) +#else +#undef MAGNUM_WINDOWLESSAPPLICATION_MAIN +#endif +#endif + +}} + +#endif diff --git a/src/Magnum/Platform/WindowlessIosApplication.mm b/src/Magnum/Platform/WindowlessIosApplication.mm new file mode 100644 index 000000000..921f5d261 --- /dev/null +++ b/src/Magnum/Platform/WindowlessIosApplication.mm @@ -0,0 +1,89 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015 + 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 "WindowlessIosApplication.h" + +#include +#include + +#include "Magnum/Version.h" +#include "Magnum/Platform/Context.h" + +#import "EAGL.h" + +#if __has_feature(objc_arc) +#error no, we want to use manual memory management +#endif + +namespace Magnum { namespace Platform { + +#ifndef DOXYGEN_GENERATING_OUTPUT +WindowlessIosApplication::WindowlessIosApplication(const Arguments& arguments): WindowlessIosApplication{arguments, Configuration{}} {} +#endif + +WindowlessIosApplication::WindowlessIosApplication(const Arguments& arguments, const Configuration& configuration): WindowlessIosApplication{arguments, nullptr} { + createContext(configuration); +} + +WindowlessIosApplication::WindowlessIosApplication(const Arguments& arguments, std::nullptr_t): _context{new Context{NoCreate, arguments.argc, arguments.argv}} {} + +void WindowlessIosApplication::createContext() { createContext({}); } + +void WindowlessIosApplication::createContext(const Configuration& configuration) { + if(!tryCreateContext(configuration)) std::exit(1); +} + +bool WindowlessIosApplication::tryCreateContext(const Configuration&) { + CORRADE_ASSERT(_context->version() == Version::None, "Platform::WindowlessIosApplication::tryCreateContext(): context already created", false); + + /* Initialize */ + if(!(_glContext = [[EAGLContext alloc] + #ifdef MAGNUM_TARGET_GLES2 + initWithAPI:kEAGLRenderingAPIOpenGLES2 + #else + initWithAPI:kEAGLRenderingAPIOpenGLES3 + #endif + ])) + { + Error() << "Platform::WindowlessIosApplication::tryCreateContext(): cannot create EAGL context"; + return false; + } + + if(![EAGLContext setCurrentContext:_glContext]) { + Error() << "Platform::WindowlessIosApplication::tryCreateContext(): cannot make context current"; + return false; + } + + /* Return true if the initialization succeeds */ + return _context->tryCreate(); +} + +WindowlessIosApplication::~WindowlessIosApplication() { + _context.reset(); + + [_glContext dealloc]; +} + +}}