From e7383d88fccd0b998b24b602a3baa361fc35a628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 15 Nov 2015 22:35:56 +0100 Subject: [PATCH] Initial support for passing arguments directly to the engine. The parameterless Platform::Context::Context() constructor and Platform::Context::tryCreate() function are deprecated in favor functions that take argc/argv pair. The Context class now accepts arguments starting with --magnum-* prefix. Currently there are none except for the implicit --magnum-help, but that will change with the following commits. --- src/Magnum/Context.cpp | 63 +++++++++------- src/Magnum/Context.h | 23 +++++- src/Magnum/Platform/AbstractXApplication.cpp | 6 +- src/Magnum/Platform/AndroidApplication.cpp | 7 +- src/Magnum/Platform/Context.h | 72 ++++++++++++------- src/Magnum/Platform/GlutApplication.cpp | 6 +- src/Magnum/Platform/Sdl2Application.cpp | 9 +-- .../Platform/WindowlessCglApplication.cpp | 6 +- .../Platform/WindowlessGlxApplication.cpp | 11 +-- .../Platform/WindowlessWglApplication.cpp | 7 +- .../WindowlessWindowsEglApplication.cpp | 7 +- 11 files changed, 134 insertions(+), 83 deletions(-) diff --git a/src/Magnum/Context.cpp b/src/Magnum/Context.cpp index 8f556ac45..8bd6cfaeb 100644 --- a/src/Magnum/Context.cpp +++ b/src/Magnum/Context.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -402,19 +403,44 @@ const std::vector& Extension::extensions(Version version) { Context* Context::_current = nullptr; -Context::Context(NoCreateT, void functionLoader()) { - /* Load GL function pointers */ - if(functionLoader) functionLoader(); +Context::Context(NoCreateT, Int argc, char** argv, void functionLoader()): _functionLoader{functionLoader}, _version{Version::None} { + /* Parse arguments */ + Utility::Arguments args{"magnum"}; + args.parse(argc, argv); +} + +Context::Context(Context&& other): _version{std::move(other._version)}, + #ifndef MAGNUM_TARGET_WEBGL + _flags{std::move(other._flags)}, + #endif + _extensionRequiredVersion(std::move(other._extensionRequiredVersion)), + _extensionStatus{std::move(other._extensionStatus)}, + _supportedExtensions{std::move(other._supportedExtensions)}, + _state{std::move(other._state)}, + _detectedDrivers{std::move(other._detectedDrivers)} +{ + other._state = nullptr; + if(_current == &other) _current = this; +} - /* Nothing else, waiting for create() to be called */ +Context::~Context() { + delete _state; + + if(_current == this) _current = nullptr; } -Context::Context(void functionLoader()): Context{NoCreate, functionLoader} { +void Context::create() { /* Hard exit if the context cannot be created */ - if(!create()) std::exit(1); + if(!tryCreate()) std::exit(1); } -bool Context::create() { +bool Context::tryCreate() { + CORRADE_ASSERT(_version == Version::None, + "Platform::Context::tryCreate(): context already created", false); + + /* Load GL function pointers */ + if(_functionLoader) _functionLoader(); + GLint majorVersion, minorVersion; /* Get version on ES 3.0+/WebGL 2.0+ */ @@ -505,6 +531,9 @@ bool Context::create() { #else Error() << "Context: unsupported OpenGL ES version" << std::make_pair(majorVersion, minorVersion); #endif + + /* Reset the version so the context is not marked as successfully created */ + _version = Version::None; return false; } @@ -599,26 +628,6 @@ bool Context::create() { return true; } -Context::Context(Context&& other): _version{std::move(other._version)}, - #ifndef MAGNUM_TARGET_WEBGL - _flags{std::move(other._flags)}, - #endif - _extensionRequiredVersion(std::move(other._extensionRequiredVersion)), - _extensionStatus{std::move(other._extensionStatus)}, - _supportedExtensions{std::move(other._supportedExtensions)}, - _state{std::move(other._state)}, - _detectedDrivers{std::move(other._detectedDrivers)} -{ - other._state = nullptr; - if(_current == &other) _current = this; -} - -Context::~Context() { - delete _state; - - if(_current == this) _current = nullptr; -} - std::vector Context::shadingLanguageVersionStrings() const { #ifndef MAGNUM_TARGET_GLES GLint versionCount = 0; diff --git a/src/Magnum/Context.h b/src/Magnum/Context.h index a7f2086a3..dc6970c9a 100644 --- a/src/Magnum/Context.h +++ b/src/Magnum/Context.h @@ -91,6 +91,22 @@ the instance is available during whole lifetime of `*Application` object. It's also possible to create the context without using any `*Application` class using @ref Platform::Context subclass, see @ref platform documentation for more information. + +## Command-line options + +The context is configurable through command-line options, that are passed +either from the `Platform::*Application` classes or from the @ref Platform::Context +class. The options are as following: + +``` +Usage: + [--magnum-help] ... + +Arguments: + ... main application arguments + (see -h or --help for details) + --magnum-help display this help message and exit +``` */ class MAGNUM_EXPORT Context { friend Platform::Context; @@ -463,14 +479,15 @@ class MAGNUM_EXPORT Context { private: static Context* _current; - explicit Context(NoCreateT, void functionLoader()); - explicit Context(void functionLoader()); + explicit Context(NoCreateT, Int argc, char** argv, void functionLoader()); - bool create(); + bool tryCreate(); + void create(); /* Defined in Implementation/driverSpecific.cpp */ MAGNUM_LOCAL void setupDriverWorkarounds(); + void(*_functionLoader)(); Version _version; #ifndef MAGNUM_TARGET_WEBGL Flags _flags; diff --git a/src/Magnum/Platform/AbstractXApplication.cpp b/src/Magnum/Platform/AbstractXApplication.cpp index d284b48ff..e5e8b090e 100644 --- a/src/Magnum/Platform/AbstractXApplication.cpp +++ b/src/Magnum/Platform/AbstractXApplication.cpp @@ -41,7 +41,7 @@ AbstractXApplication::AbstractXApplication(Implementation::AbstractContextHandle createContext(configuration); } -AbstractXApplication::AbstractXApplication(Implementation::AbstractContextHandler* contextHandler, const Arguments&, std::nullptr_t): _contextHandler{contextHandler}, _flags{Flag::Redraw} {} +AbstractXApplication::AbstractXApplication(Implementation::AbstractContextHandler* contextHandler, const Arguments& arguments, std::nullptr_t): _contextHandler{contextHandler}, _context{new Context{NoCreate, arguments.argc, arguments.argv}}, _flags{Flag::Redraw} {} void AbstractXApplication::createContext() { createContext({}); } @@ -50,7 +50,7 @@ void AbstractXApplication::createContext(const Configuration& configuration) { } bool AbstractXApplication::tryCreateContext(const Configuration& configuration) { - CORRADE_ASSERT(!_context, "AbstractXApplication::tryCreateContext(): context already created", false); + CORRADE_ASSERT(_context->version() == Version::None, "Platform::AbstractXApplication::tryCreateContext(): context already created", false); _viewportSize = configuration.size(); @@ -96,7 +96,7 @@ bool AbstractXApplication::tryCreateContext(const Configuration& configuration) _contextHandler->makeCurrent(); /* Return true if the initialization succeeds */ - return !!(_context = Platform::Context::tryCreate()); + return _context->tryCreate(); } AbstractXApplication::~AbstractXApplication() { diff --git a/src/Magnum/Platform/AndroidApplication.cpp b/src/Magnum/Platform/AndroidApplication.cpp index bc5b5577e..e1e7b0697 100644 --- a/src/Magnum/Platform/AndroidApplication.cpp +++ b/src/Magnum/Platform/AndroidApplication.cpp @@ -28,6 +28,7 @@ #include #include +#include "Magnum/Version.h" #include "Magnum/Platform/Context.h" #include "Implementation/Egl.h" @@ -60,7 +61,7 @@ AndroidApplication::AndroidApplication(const Arguments& arguments, const Configu createContext(configuration); } -AndroidApplication::AndroidApplication(const Arguments& arguments, std::nullptr_t): _state(arguments) { +AndroidApplication::AndroidApplication(const Arguments& arguments, std::nullptr_t): _state{arguments}, _context{new Context{NoCreate, 0, nullptr}} { /* Redirect debug output to Android log */ _logOutput.reset(new LogOutput); } @@ -79,6 +80,8 @@ void AndroidApplication::createContext(const Configuration& configuration) { } bool AndroidApplication::tryCreateContext(const Configuration& configuration) { + CORRADE_ASSERT(_context->version() == Version::None, "Platform::AndroidApplication::tryCreateContext(): context already created", false); + /* Initialize EGL */ _display = eglGetDisplay(EGL_DEFAULT_DISPLAY); if(!eglInitialize(_display, nullptr, nullptr)) { @@ -137,7 +140,7 @@ bool AndroidApplication::tryCreateContext(const Configuration& configuration) { CORRADE_INTERNAL_ASSERT_OUTPUT(eglMakeCurrent(_display, _surface, _surface, _glContext)); /* Return true if the initialization succeeds */ - return !!(_context = Platform::Context::tryCreate()); + return _context->tryCreate(); } void AndroidApplication::swapBuffers() { diff --git a/src/Magnum/Platform/Context.h b/src/Magnum/Platform/Context.h index bfac9d7c6..e34ea4405 100644 --- a/src/Magnum/Platform/Context.h +++ b/src/Magnum/Platform/Context.h @@ -41,44 +41,62 @@ information. */ class Context: public Magnum::Context { public: - /** - * @brief Try to create the context - * - * Unline @ref Context(), this function returns `nullptr` on error - * instead of application exit and thus is usable for context creation - * fallbacks. - */ - static std::unique_ptr tryCreate() { - std::unique_ptr c{new Context{NoCreate}}; - return c->create() ? std::move(c) : nullptr; - } - /** * @brief Constructor * - * Does initial setup, loads OpenGL function pointers using - * platform-specific API, detects available features and enables them - * throughout the engine. If detected version is unsupported or any - * other error occurs, a message is printed to output and the - * application exits. See @ref tryCreate() for an alternative. + * Parses command-line arguments, loads OpenGL function pointers using + * platform-specific API, does initial setup, detects available + * features and enables them throughout the engine. If detected version + * is unsupported or any other error occurs, a message is printed to + * output and the application exits. See @ref Context(NoCreateT, Int, char**) + * and @ref tryCreate() for an alternative. * @see @fn_gl{Get} with @def_gl{MAJOR_VERSION}, @def_gl{MINOR_VERSION}, * @def_gl{CONTEXT_FLAGS}, @def_gl{NUM_EXTENSIONS}, * @fn_gl{GetString} with @def_gl{EXTENSIONS} */ - explicit Context(): - #if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_NACL) - Magnum::Context{flextGLInit} {} - #else - Magnum::Context{nullptr} {} - #endif + explicit Context(Int argc, char** argv): Context{NoCreate, argc, argv} { create(); } + + #ifdef MAGNUM_BUILD_DEPRECATED + /** @copybrief Context(Int, char**) + * @deprecated Use @ref Context(Int, char**) instead. + */ + CORRADE_DEPRECATED("use Context(Int, char**) instead") explicit Context(): Context(0, nullptr) {} + #endif - private: - explicit Context(NoCreateT): + /** + * @brief Construct the class without doing complete setup + * + * Unlike @ref Context(Int, char**) just parses command-line arguments + * and sets @ref version() to @ref Version::None, everything else is + * left in empty state. Use @ref create() or @ref tryCreate() to + * complete the setup. + */ + explicit Context(NoCreateT, Int argc, char** argv): #if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_NACL) - Magnum::Context{NoCreate, flextGLInit} {} + Magnum::Context{NoCreate, argc, argv, flextGLInit} {} #else - Magnum::Context{NoCreate, nullptr} {} + Magnum::Context{NoCreate, argc, argv, nullptr} {} #endif + + /** + * @brief Complete the context setup and exit on failure + * + * Finalizes the setup after the class was created using + * @ref Context(NoCreateT, Int, char**). If detected version is + * unsupported or any other error occurs, a message is printed to error + * output and the application exits. See @ref Context(Int, char**) for + * more information and @ref tryCreate() for an alternative. + */ + void create() { return Magnum::Context::create(); } + + /** + * @brief Complete the context setup + * + * Unlike @ref create() just prints a message to error output and + * returns `false` on error. + */ + bool tryCreate() { return Magnum::Context::tryCreate(); } + }; }} diff --git a/src/Magnum/Platform/GlutApplication.cpp b/src/Magnum/Platform/GlutApplication.cpp index 6a65c4b4d..7553490ef 100644 --- a/src/Magnum/Platform/GlutApplication.cpp +++ b/src/Magnum/Platform/GlutApplication.cpp @@ -43,7 +43,7 @@ GlutApplication::GlutApplication(const Arguments& arguments, const Configuration createContext(configuration); } -GlutApplication::GlutApplication(const Arguments& arguments, std::nullptr_t) { +GlutApplication::GlutApplication(const Arguments& arguments, std::nullptr_t): _context{new Context{NoCreate, arguments.argc, arguments.argv}} { /* Save global instance */ _instance = this; @@ -59,7 +59,7 @@ void GlutApplication::createContext(const Configuration& configuration) { } bool GlutApplication::tryCreateContext(const Configuration& configuration) { - CORRADE_ASSERT(!_context, "Platform::GlutApplication::tryCreateContext(): context already created", false); + CORRADE_ASSERT(_context->version() == Version::None, "Platform::GlutApplication::tryCreateContext(): context already created", false); unsigned int flags = GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL; @@ -97,7 +97,7 @@ bool GlutApplication::tryCreateContext(const Configuration& configuration) { glutDisplayFunc(staticDrawEvent); /* Return true if the initialization succeeds */ - return !!(_context = Platform::Context::tryCreate()); + return _context->tryCreate(); } GlutApplication::~GlutApplication() = default; diff --git a/src/Magnum/Platform/Sdl2Application.cpp b/src/Magnum/Platform/Sdl2Application.cpp index ddd286dfe..40f40ad58 100644 --- a/src/Magnum/Platform/Sdl2Application.cpp +++ b/src/Magnum/Platform/Sdl2Application.cpp @@ -71,11 +71,12 @@ Sdl2Application::Sdl2Application(const Arguments& arguments, const Configuration createContext(configuration); } -Sdl2Application::Sdl2Application(const Arguments&, std::nullptr_t): _glContext{nullptr}, +Sdl2Application::Sdl2Application(const Arguments& arguments, std::nullptr_t): _glContext{nullptr}, #ifndef CORRADE_TARGET_EMSCRIPTEN _minimalLoopPeriod{0}, #endif - _flags{Flag::Redraw} { + _context{new Context{NoCreate, arguments.argc, arguments.argv}}, _flags{Flag::Redraw} +{ #ifdef CORRADE_TARGET_EMSCRIPTEN CORRADE_ASSERT(!_instance, "Platform::Sdl2Application::Sdl2Application(): the instance is already created", ); _instance = this; @@ -94,7 +95,7 @@ void Sdl2Application::createContext(const Configuration& configuration) { } bool Sdl2Application::tryCreateContext(const Configuration& configuration) { - CORRADE_ASSERT(!_glContext, "Platform::Sdl2Application::tryCreateContext(): context already created", false); + CORRADE_ASSERT(_context->version() == Version::None, "Platform::Sdl2Application::tryCreateContext(): context already created", false); /* Enable double buffering and 24bt depth buffer */ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); @@ -234,7 +235,7 @@ bool Sdl2Application::tryCreateContext(const Configuration& configuration) { #endif /* Return true if the initialization succeeds */ - return !!(_context = Platform::Context::tryCreate()); + return _context->tryCreate(); } void Sdl2Application::swapBuffers() { diff --git a/src/Magnum/Platform/WindowlessCglApplication.cpp b/src/Magnum/Platform/WindowlessCglApplication.cpp index c24cccd43..23a8bf1ad 100644 --- a/src/Magnum/Platform/WindowlessCglApplication.cpp +++ b/src/Magnum/Platform/WindowlessCglApplication.cpp @@ -43,7 +43,7 @@ WindowlessCglApplication::WindowlessCglApplication(const Arguments& arguments, c createContext(configuration); } -WindowlessCglApplication::WindowlessCglApplication(const Arguments&, std::nullptr_t) {} +WindowlessCglApplication::WindowlessCglApplication(const Arguments& arguments, std::nullptr_t): _context{new Context{NoCreate, arguments.argc, arguments.argv}} {} void WindowlessCglApplication::createContext() { createContext({}); } @@ -52,7 +52,7 @@ void WindowlessCglApplication::createContext(const Configuration& configuration) } bool WindowlessCglApplication::tryCreateContext(const Configuration&) { - CORRADE_ASSERT(!_context, "Platform::WindowlessCglApplication::tryCreateContext(): context already created", false); + CORRADE_ASSERT(_context->version() == Version::None, "Platform::WindowlessCglApplication::tryCreateContext(): context already created", false); int formatCount; CGLPixelFormatAttribute attributes32[] = { @@ -97,7 +97,7 @@ bool WindowlessCglApplication::tryCreateContext(const Configuration&) { } /* Return true if the initialization succeeds */ - return !!(_context = Platform::Context::tryCreate()); + return _context->tryCreate(); } WindowlessCglApplication::~WindowlessCglApplication() { diff --git a/src/Magnum/Platform/WindowlessGlxApplication.cpp b/src/Magnum/Platform/WindowlessGlxApplication.cpp index 1ebaa0c3b..6fb8b8fd1 100644 --- a/src/Magnum/Platform/WindowlessGlxApplication.cpp +++ b/src/Magnum/Platform/WindowlessGlxApplication.cpp @@ -29,9 +29,11 @@ #include #include +#include "Magnum/Version.h" #include "Magnum/Platform/Context.h" -#define None 0L // redef Xlib nonsense +/* Saner way to define the None Xlib macro (anyway, FUCK YOU XLIB) */ +namespace { enum { None = 0L }; } namespace Magnum { namespace Platform { @@ -43,7 +45,7 @@ WindowlessGlxApplication::WindowlessGlxApplication(const Arguments& arguments, c createContext(configuration); } -WindowlessGlxApplication::WindowlessGlxApplication(const Arguments&, std::nullptr_t) {} +WindowlessGlxApplication::WindowlessGlxApplication(const Arguments& arguments, std::nullptr_t): _context{new Context{NoCreate, arguments.argc, arguments.argv}} {} void WindowlessGlxApplication::createContext() { createContext({}); } @@ -52,8 +54,7 @@ void WindowlessGlxApplication::createContext(const Configuration& configuration) } bool WindowlessGlxApplication::tryCreateContext(const Configuration&) { - CORRADE_ASSERT(!_context, "Platform::WindowlessGlxApplication::tryCreateContext(): context already created", false); - + CORRADE_ASSERT(_context->version() == Version::None, "Platform::WindowlessGlxApplication::tryCreateContext(): context already created", false); _display = XOpenDisplay(nullptr); /* Check version */ @@ -154,7 +155,7 @@ bool WindowlessGlxApplication::tryCreateContext(const Configuration&) { } /* Return true if the initialization succeeds */ - return !!(_context = Platform::Context::tryCreate()); + return _context->tryCreate(); } WindowlessGlxApplication::~WindowlessGlxApplication() { diff --git a/src/Magnum/Platform/WindowlessWglApplication.cpp b/src/Magnum/Platform/WindowlessWglApplication.cpp index a00983bfb..ef6c97798 100644 --- a/src/Magnum/Platform/WindowlessWglApplication.cpp +++ b/src/Magnum/Platform/WindowlessWglApplication.cpp @@ -28,6 +28,7 @@ #include #include +#include "Magnum/Version.h" #include "Magnum/Platform/Context.h" namespace Magnum { namespace Platform { @@ -66,7 +67,7 @@ WindowlessWglApplication::WindowlessWglApplication(const Arguments& arguments, c createContext(configuration); } -WindowlessWglApplication::WindowlessWglApplication(const Arguments& arguments, std::nullptr_t): _window(arguments.window) {} +WindowlessWglApplication::WindowlessWglApplication(const Arguments& arguments, std::nullptr_t): _window(arguments.window), _context{new Context{NoCreate, arguments.argc, arguments.argv}} {} void WindowlessWglApplication::createContext() { createContext({}); } @@ -75,7 +76,7 @@ void WindowlessWglApplication::createContext(const Configuration& configuration) } bool WindowlessWglApplication::tryCreateContext(const Configuration&) { - CORRADE_ASSERT(!_context, "Platform::WindowlessWglApplication::tryCreateContext(): context already created", false); + CORRADE_ASSERT(_context->version() == Version::None, "Platform::WindowlessWglApplication::tryCreateContext(): context already created", false); /* Get device context */ _deviceContext = GetDC(_window); @@ -117,7 +118,7 @@ bool WindowlessWglApplication::tryCreateContext(const Configuration&) { } /* Return true if the initialization succeeds */ - return !!(_context = Platform::Context::tryCreate()); + return _context->tryCreate(); } WindowlessWglApplication::~WindowlessWglApplication() { diff --git a/src/Magnum/Platform/WindowlessWindowsEglApplication.cpp b/src/Magnum/Platform/WindowlessWindowsEglApplication.cpp index 30ccf4ee8..0ab95c191 100644 --- a/src/Magnum/Platform/WindowlessWindowsEglApplication.cpp +++ b/src/Magnum/Platform/WindowlessWindowsEglApplication.cpp @@ -28,6 +28,7 @@ #include #include +#include "Magnum/Version.h" #include "Magnum/Platform/Context.h" #include "Implementation/Egl.h" @@ -68,7 +69,7 @@ WindowlessWindowsEglApplication::WindowlessWindowsEglApplication(const Arguments createContext(configuration); } -WindowlessWindowsEglApplication::WindowlessWindowsEglApplication(const Arguments& arguments, std::nullptr_t): _window(arguments.window) {} +WindowlessWindowsEglApplication::WindowlessWindowsEglApplication(const Arguments& arguments, std::nullptr_t): _window(arguments.window), _context{new Context{NoCreate, arguments.argc, arguments.argv}} {} void WindowlessWindowsEglApplication::createContext() { createContext({}); } @@ -77,7 +78,7 @@ void WindowlessWindowsEglApplication::createContext(const Configuration& configu } bool WindowlessWindowsEglApplication::tryCreateContext(const Configuration&) { - CORRADE_ASSERT(!_context, "Platform::WindowlessWindowsEglApplication::tryCreateContext(): context already created", false); + CORRADE_ASSERT(_context->version() == Version::None, "Platform::WindowlessWindowsEglApplication::tryCreateContext(): context already created", false); /* Initialize */ _display = eglGetDisplay(GetDC(_window)); @@ -156,7 +157,7 @@ bool WindowlessWindowsEglApplication::tryCreateContext(const Configuration&) { } /* Return true if the initialization succeeds */ - return !!(_context = Platform::Context::tryCreate()); + return _context->tryCreate(); } WindowlessWindowsEglApplication::~WindowlessWindowsEglApplication() {