From cdcb37cd0c4e893deb738e3382fd56c9c0048e98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 22 Mar 2013 16:04:42 +0100 Subject: [PATCH] Platform: better way to specify application configuration. Each implementation of *Application::Configuration will have different methods tailored to feature set of the underlying toolkit. Currently each windowed application's Configuration has only window title (except NaClApplication) and window size. WindowlessGlxApplication has empty class. More features will come later. Also created introductionary documentation for Platform namespace. --- doc/platform.dox | 144 +++++++++++++++++++++- src/Platform/AbstractXApplication.cpp | 22 +++- src/Platform/AbstractXApplication.h | 76 +++++++++++- src/Platform/GlutApplication.cpp | 25 +++- src/Platform/GlutApplication.h | 89 +++++++++++-- src/Platform/GlxApplication.h | 16 +-- src/Platform/NaClApplication.cpp | 23 +++- src/Platform/NaClApplication.h | 59 ++++++++- src/Platform/Sdl2Application.cpp | 24 +++- src/Platform/Sdl2Application.h | 81 +++++++++--- src/Platform/WindowlessGlxApplication.cpp | 15 +++ src/Platform/WindowlessGlxApplication.h | 36 ++++-- src/Platform/XEglApplication.h | 16 +-- 13 files changed, 542 insertions(+), 84 deletions(-) diff --git a/doc/platform.dox b/doc/platform.dox index 15e664c7c..e74aaaeb3 100644 --- a/doc/platform.dox +++ b/doc/platform.dox @@ -22,14 +22,152 @@ DEALINGS IN THE SOFTWARE. */ -namespace Magnum { +namespace Magnum { namespace Platform { /** @page platform Platform support @brief Integration into windowing toolkits and creation of windowless contexts @tableofcontents -@todoc write when the API is stabilized +Platform namespace provides classes integrating %Magnum engine into various +toolkits, both windowed and windowless. All the classes have common API to +achieve static polymorphism, so basically you can use different toolkits on +different platforms and the only thing you need to change is the class name, +everything else is the same. -*/ +Basic usage is to subclass the chosen `*Application` class and implement +required methods. + +@section platform-windowed Windowed applications + +Windowed applications provide a window and keyboard and mouse handling. The +most basic toolkit (and toolkit available on most platforms) is GLUT, which is +is implemented in GlutApplication. As said above, the usage is similar for all +toolkits, you must provide two-argument constructor and implement at least +@ref GlutApplication::viewportEvent() "viewportEvent()" and +@ref GlutApplication::drawEvent() "drawEvent()". + +Barebone application implementation which will just clear the window to dark +blue color: +@code +#include +#include +#include + +using namespace Magnum; + +class MyApplication: public Platform::GlutApplication { + public: + MyApplication(int& argc, char** argv); + + void viewportEvent(const Vector2i& viewport) override; + void drawEvent() override; +}; + +MyApplication::MyApplication(int& argc, char** argv): Platform::GlutApplication(argc, argv) { + // Set clear color to dark blue + Renderer::setClearColor({0.0f, 0.0f, 0.4f}); +} + +void MyApplication::viewportEvent(const Vector2i& size) { + // Resize the framebuffer to new window size + defaultFramebuffer.setViewport({{}, size}); +} + +void MyApplication::drawEvent() { + // Clear the window + defaultFramebuffer.clear(); + + // The context is double-buffered, swap buffers + swapBuffers(); +} + +// main() function implementation +MAGNUM_GLUTAPPLICATION_MAIN(MyApplication) +@endcode + +@section platform-windowless Windowless applications + +Windowless applications provide just a context for ofscreen rendering or +performing tasks on GPU. There is not yet any platform-independent toolkit +which could handle this in portable way, thus you have to use platform-specific +ones. As example we use WindowlessGlxApplication, you need to implement just +@ref WindowlessGlxApplication::exec() "exec()" function. + +Barebone application which will just print out current OpenGL version and +renderer string and exits: +@code +#include +#include + +using namespace Magnum; + +class MyApplication: public Platform::WindowlessGlxApplication { + public: + MyApplication(int& argc, char** argv); + + int exec() override; +}; + +MyApplication::MyApplication(int& argc, char** argv): Platform::WindowlessGlxApplication(argc, argv) {} + +int MyApplication::exec() { + Debug() << "OpenGL version:" << Context::current()->versionString(); + Debug() << "OpenGL renderer:" << Context::current()->rendererString(); + + // Exit with success + return 0; } + +// main() function implementation +MAGNUM_WINDOWLESSGLXAPPLICATION_MAIN(MyApplication) +@endcode + +@section platform-compilation Compilation with CMake + +Barebone compilation consists just of finding %Magnum library with required +`*Application` component, compilation of the executable and linking the +libraries to it: +@code +find_package(Magnum REQUIRED GlutApplication) + +include_directories(${MAGNUM_INCLUDE_DIRS}) + +add_executable(myapplication MyApplication.cpp) +target_link_libraries(myapplication + ${MAGNUM_LIBRARIES} + ${MAGNUM_GLUTAPPLICATION_LIBRARIES}) +@endcode + +@section platform-configuration Specifying configuration + +By default the application is created with some reasonable defaults (e.g. +window size 800x600 pixels). If you want something else, you can pass +@ref GlutApplication::Configuration "Configuration" instance to application +constructor. Using method chaining it can be done conveniently like this: +@code +MyApplication::MyApplication(int& argc, char** argv): + Platform::GlutApplication(argc, argv, (new Configuration()) + ->setTitle("My Application")->setSize({800, 600}) { + // ... +} +@endcode + +However, sometimes you would need to configure the application based on some +configuration file or system introspection. In that case you can pass `nullptr` +instead of Configuration instance and then specify it later with +@ref GlutApplication::createContext() "createContext()": +@code +MyApplication::MyApplication(int& argc, char** argv): Platform::GlutApplication(argc, argv, nullptr) { + // ... + + createContext((new Configuration()) + ->setTitle("My Application") + ->setSize(size)); + + // ... +} +@endcode + +*/ +}} diff --git a/src/Platform/AbstractXApplication.cpp b/src/Platform/AbstractXApplication.cpp index 37aa4fcc1..7442c6e85 100644 --- a/src/Platform/AbstractXApplication.cpp +++ b/src/Platform/AbstractXApplication.cpp @@ -36,7 +36,19 @@ namespace Magnum { namespace Platform { -AbstractXApplication::AbstractXApplication(AbstractContextHandler* contextHandler, int&, char**, const std::string& title, const Vector2i& size): contextHandler(contextHandler), viewportSize(size), flags(Flag::Redraw) { +AbstractXApplication::AbstractXApplication(AbstractContextHandler* contextHandler, int&, char**): contextHandler(contextHandler), flags(Flag::Redraw) { + createContext(new Configuration); +} + +AbstractXApplication::AbstractXApplication(AbstractContextHandler* contextHandler, int&, char**, Configuration* configuration): contextHandler(contextHandler), flags(Flag::Redraw) { + if(configuration) createContext(configuration); +} + +void AbstractXApplication::createContext(AbstractXApplication::Configuration* configuration) { + CORRADE_ASSERT(!c, "AbstractXApplication::createContext(): context already created", ); + + viewportSize = configuration->size(); + /* Get default X display */ display = XOpenDisplay(0); @@ -61,8 +73,8 @@ AbstractXApplication::AbstractXApplication(AbstractContextHandlervisual, AllocNone); attr.event_mask = 0; unsigned long mask = CWBackPixel|CWBorderPixel|CWColormap|CWEventMask; - window = XCreateWindow(display, root, 20, 20, size.x(), size.y(), 0, visInfo->depth, InputOutput, visInfo->visual, mask, &attr); - XSetStandardProperties(display, window, title.c_str(), 0, None, 0, 0, 0); + window = XCreateWindow(display, root, 20, 20, configuration->size().x(), configuration->size().y(), 0, visInfo->depth, InputOutput, visInfo->visual, mask, &attr); + XSetStandardProperties(display, window, configuration->title().c_str(), 0, None, 0, 0, 0); XFree(visInfo); /* Be notified about closing the window */ @@ -82,6 +94,7 @@ AbstractXApplication::AbstractXApplication(AbstractContextHandlerexperimentalExtensionWranglerFeatures()); c = new Context; + delete configuration; } AbstractXApplication::~AbstractXApplication() { @@ -152,4 +165,7 @@ int AbstractXApplication::exec() { return 0; } +AbstractXApplication::Configuration::Configuration(): _title("Magnum X Application"), _size(800, 600) {} +AbstractXApplication::Configuration::~Configuration() = default; + }} diff --git a/src/Platform/AbstractXApplication.h b/src/Platform/AbstractXApplication.h index 04fdad3c6..f3af833a1 100644 --- a/src/Platform/AbstractXApplication.h +++ b/src/Platform/AbstractXApplication.h @@ -50,28 +50,40 @@ namespace Platform { /** @nosubgrouping @brief Base for X11-based applications -Supports keyboard and mouse handling. - +Supports keyboard and mouse handling. See @ref platform for brief introduction. @note Not meant to be used directly, see subclasses. */ class AbstractXApplication { public: + class Configuration; class InputEvent; class KeyEvent; class MouseEvent; class MouseMoveEvent; + /** + * @brief Default constructor + * @param contextHandler OpenGL context handler + * @param argc Count of arguments of `main()` function + * @param argv Arguments of `main()` function + * + * Creates application with default configuration. See Configuration + * for more information. + */ + explicit AbstractXApplication(AbstractContextHandler* contextHandler, int& argc, char** argv); + /** * @brief Constructor * @param contextHandler OpenGL context handler * @param argc Count of arguments of `main()` function * @param argv Arguments of `main()` function - * @param title Window title - * @param size Window size + * @param configuration Configuration * - * Creates window with double-buffered OpenGL ES 2 context. + * The @p configuration is deleted afterwards. If `nullptr` is passed + * as @p configuration, the context is not created and must be created + * with createContext(). */ - explicit AbstractXApplication(AbstractContextHandler* contextHandler, int& argc, char** argv, const std::string& title = "Magnum X application", const Vector2i& size = Vector2i(800, 600)); + explicit AbstractXApplication(AbstractContextHandler* contextHandler, int& argc, char** argv, Configuration* configuration); /** * @brief Destructor @@ -90,6 +102,9 @@ class AbstractXApplication { inline void exit() { flags |= Flag::Exit; } protected: + /** @copydoc GlutApplication::createContext() */ + void createContext(Configuration* configuration); + /** @{ @name Drawing functions */ /** @copydoc GlutApplication::viewportEvent() */ @@ -154,6 +169,55 @@ class AbstractXApplication { CORRADE_ENUMSET_OPERATORS(AbstractXApplication::Flags) +/** +@brief %Configuration + +Double-buffered OpenGL context. +@see AbstractXApplication(), createContext() +*/ +class AbstractXApplication::Configuration { + Configuration(const Configuration&) = delete; + Configuration(Configuration&&) = delete; + Configuration& operator=(const Configuration&) = delete; + Configuration& operator=(Configuration&&) = delete; + + public: + explicit Configuration(); + ~Configuration(); + + /** @brief Window title */ + inline std::string title() const { return _title; } + + /** + * @brief Set window title + * @return Pointer to self (for method chaining) + * + * Default is `"Magnum X Application"`. + */ + inline Configuration* setTitle(std::string title) { + _title = std::move(title); + return this; + } + + /** @brief Window size */ + inline Vector2i size() const { return _size; } + + /** + * @brief Set window size + * @return Pointer to self (for method chaining) + * + * Default is `{800, 600}`. + */ + inline Configuration* setSize(const Vector2i& size) { + _size = size; + return this; + } + + private: + std::string _title; + Vector2i _size; +}; + /** @brief Base for input events diff --git a/src/Platform/GlutApplication.cpp b/src/Platform/GlutApplication.cpp index cdf7dd5c9..2aa125759 100644 --- a/src/Platform/GlutApplication.cpp +++ b/src/Platform/GlutApplication.cpp @@ -31,16 +31,31 @@ namespace Magnum { namespace Platform { GlutApplication* GlutApplication::instance = nullptr; -GlutApplication::GlutApplication(int& argc, char** argv, const std::string& title, const Vector2i& size) { +GlutApplication::GlutApplication(int& argc, char** argv): c(nullptr) { + initialize(argc, argv); + createContext(new Configuration); +} + +GlutApplication::GlutApplication(int& argc, char** argv, Configuration* configuration): c(nullptr) { + initialize(argc, argv); + if(configuration) createContext(configuration); +} + +void GlutApplication::initialize(int& argc, char** argv) { /* Save global instance */ instance = this; /* Init GLUT */ glutInit(&argc, argv); +} + +void GlutApplication::createContext(Configuration* configuration) { + CORRADE_ASSERT(!c, "GlutApplication::createContext(): context already created", ); + glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION); glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL); - glutInitWindowSize(size.x(), size.y()); - glutCreateWindow(title.c_str()); + glutInitWindowSize(configuration->size().x(), configuration->size().y()); + glutCreateWindow(configuration->title().c_str()); glutReshapeFunc(staticViewportEvent); glutSpecialFunc(staticKeyEvent); glutMouseFunc(staticMouseEvent); @@ -50,6 +65,7 @@ GlutApplication::GlutApplication(int& argc, char** argv, const std::string& titl ExtensionWrangler::initialize(); c = new Context; + delete configuration; } GlutApplication::~GlutApplication() { @@ -74,4 +90,7 @@ void GlutApplication::staticMouseMoveEvent(int x, int y) { instance->mouseMoveEvent(e); } +GlutApplication::Configuration::Configuration(): _title("Magnum GLUT Application"), _size(800, 600) {} +GlutApplication::Configuration::~Configuration() = default; + }} diff --git a/src/Platform/GlutApplication.h b/src/Platform/GlutApplication.h index ccf90113a..861376044 100644 --- a/src/Platform/GlutApplication.h +++ b/src/Platform/GlutApplication.h @@ -45,9 +45,9 @@ namespace Platform { /** @nosubgrouping @brief GLUT application -Creates double-buffered RGBA window with depth and stencil buffers. Supports -keyboard handling for limited subset of keys, mouse handling with support for -changing cursor and mouse tracking and warping. +Supports keyboard handling for limited subset of keys, mouse handling with +support for changing cursor and mouse tracking and warping. See @ref platform +for brief introduction. @section GlutApplication-usage Usage @@ -67,19 +67,33 @@ to simplify porting. */ class GlutApplication { public: + class Configuration; class InputEvent; class KeyEvent; class MouseEvent; class MouseMoveEvent; /** - * @brief Constructor + * @brief Default constructor * @param argc Count of arguments of `main()` function * @param argv Arguments of `main()` function - * @param title Window title - * @param size Window size + * + * Creates application with default configuration. See Configuration + * for more information. */ - explicit GlutApplication(int& argc, char** argv, const std::string& title = "Magnum GLUT application", const Vector2i& size = Vector2i(800, 600)); + explicit GlutApplication(int& argc, char** argv); + + /** + * @brief Constructor + * @param argc Count of arguments of `main()` function + * @param argv Arguments of `main()` function + * @param configuration Configuration + * + * The @p configuration is deleted afterwards. If `nullptr` is passed + * as @p configuration, the context is not created and must be created + * with createContext(). + */ + explicit GlutApplication(int& argc, char** argv, Configuration* configuration); virtual ~GlutApplication(); @@ -92,9 +106,17 @@ class GlutApplication { return 0; } + protected: + /** + * @brief Create context with given configuration + * + * The @p configuration is deleted afterwards. Must be called if and + * only if the context wasn't created by the constructor itself. + */ + void createContext(Configuration* configuration); + /** @{ @name Drawing functions */ - protected: /** * @brief Viewport event * @@ -209,6 +231,8 @@ class GlutApplication { /*@}*/ private: + void initialize(int& argc, char** argv); + inline static void staticViewportEvent(int x, int y) { instance->viewportEvent({x, y}); } @@ -228,6 +252,55 @@ class GlutApplication { Context* c; }; +/** +@brief %Configuration + +Double-buffered RGBA window with depth and stencil buffers. +@see GlutApplication(), createContext() +*/ +class GlutApplication::Configuration { + Configuration(const Configuration&) = delete; + Configuration(Configuration&&) = delete; + Configuration& operator=(const Configuration&) = delete; + Configuration& operator=(Configuration&&) = delete; + + public: + explicit Configuration(); + ~Configuration(); + + /** @brief Window title */ + inline std::string title() const { return _title; } + + /** + * @brief Set window title + * @return Pointer to self (for method chaining) + * + * Default is `"Magnum GLUT Application"`. + */ + inline Configuration* setTitle(std::string title) { + _title = std::move(title); + return this; + } + + /** @brief Window size */ + inline Vector2i size() const { return _size; } + + /** + * @brief Set window size + * @return Pointer to self (for method chaining) + * + * Default is `{800, 600}`. + */ + inline Configuration* setSize(const Vector2i& size) { + _size = size; + return this; + } + + private: + std::string _title; + Vector2i _size; +}; + /** @brief Base for input events diff --git a/src/Platform/GlxApplication.h b/src/Platform/GlxApplication.h index 4984ad6f1..c5a28ac71 100644 --- a/src/Platform/GlxApplication.h +++ b/src/Platform/GlxApplication.h @@ -36,8 +36,7 @@ namespace Magnum { namespace Platform { /** @brief GLX application -Creates window with double-buffered OpenGL or OpenGL ES 2.0 context, if -targeting OpenGL ES. Uses GlxContextHandler. +Uses GlxContextHandler. See @ref platform for brief introduction. @section GlxApplication-usage Usage @@ -57,14 +56,11 @@ to simplify porting. */ class GlxApplication: public AbstractXApplication { public: - /** - * @brief Constructor - * @param argc Count of arguments of `main()` function - * @param argv Arguments of `main()` function - * @param title Window title - * @param size Window size - */ - inline explicit GlxApplication(int& argc, char** argv, const std::string& title = "Magnum GLX application", const Vector2i& size = Vector2i(800, 600)): AbstractXApplication(new GlxContextHandler, argc, argv, title, size) {} + /** @copydoc GlutApplication::GlutApplication(int&, char**) */ + inline explicit GlxApplication(int& argc, char** argv): AbstractXApplication(new GlxContextHandler, argc, argv) {} + + /** @copydoc GlutApplication::GlutApplication(int&, char**, Configuration*) */ + inline explicit GlxApplication(int& argc, char** argv, Configuration* configuration): AbstractXApplication(new GlxContextHandler, argc, argv, configuration) {} }; /** @hideinitializer diff --git a/src/Platform/NaClApplication.cpp b/src/Platform/NaClApplication.cpp index 87a442ccc..d746f1aa1 100644 --- a/src/Platform/NaClApplication.cpp +++ b/src/Platform/NaClApplication.cpp @@ -32,15 +32,27 @@ namespace Magnum { namespace Platform { -NaClApplication::NaClApplication(PP_Instance instance, const Vector2i& size): Instance(instance), Graphics3DClient(this), MouseLock(this), viewportSize(size) { +NaClApplication::NaClApplication(PP_Instance instance): Instance(instance), Graphics3DClient(this), MouseLock(this), c(nullptr) { + createContext(new Configuration); +} + +NaClApplication::NaClApplication(PP_Instance instance, Configuration* configuration): Instance(instance), Graphics3DClient(this), MouseLock(this), c(nullptr) { + if(configuration) createContext(configuration); +} + +void NaClApplication::createContext(NaClApplication::Configuration* configuration) { + CORRADE_ASSERT(!c, "NaClApplication::createContext(): context already created", ); + + viewportSize = configuration->size(); + std::int32_t attributes[] = { PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8, PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 24, PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 8, PP_GRAPHICS3DATTRIB_SAMPLES, 0, PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS, 0, - PP_GRAPHICS3DATTRIB_WIDTH, size.x(), - PP_GRAPHICS3DATTRIB_HEIGHT, size.y(), + PP_GRAPHICS3DATTRIB_WIDTH, configuration->size().x(), + PP_GRAPHICS3DATTRIB_HEIGHT, configuration->size().y(), PP_GRAPHICS3DATTRIB_NONE }; @@ -66,6 +78,8 @@ NaClApplication::NaClApplication(PP_Instance instance, const Vector2i& size): In /* Make sure viewportEvent() is called for first time */ flags |= Flag::ViewportUpdated; + + delete configuration; } NaClApplication::~NaClApplication() { @@ -209,4 +223,7 @@ void NaClApplication::mouseLockCallback(void* applicationInstance, std::int32_t) instance->flags |= Flag::MouseLocked; } +NaClApplication::Configuration::Configuration(): _size(640, 480) {} +NaClApplication::Configuration::~Configuration() = default; + }} diff --git a/src/Platform/NaClApplication.h b/src/Platform/NaClApplication.h index 7ba5aa39d..2772bee12 100644 --- a/src/Platform/NaClApplication.h +++ b/src/Platform/NaClApplication.h @@ -53,8 +53,7 @@ namespace Magnum { namespace Platform { @brief NaCl application Application running in [Google Chrome Native Client](https://developers.google.com/native-client/). -Creates double-buffered RGBA canvas with depth and stencil buffers. Supports -keyboard and mouse handling. +Supports keyboard and mouse handling. See @ref platform for brief introduction. @section NaClApplication-usage Usage @@ -74,17 +73,31 @@ to simplify porting. */ class NaClApplication: public pp::Instance, public pp::Graphics3DClient, public pp::MouseLock { public: + class Configuration; class InputEvent; class KeyEvent; class MouseEvent; class MouseMoveEvent; /** - * @brief Constructor + * @brief Default constructor * @param instance Module instance - * @param size Rendering size + * + * Creates application with default configuration. See Configuration + * for more information. */ - explicit NaClApplication(PP_Instance instance, const Vector2i& size = Vector2i(640, 480)); + explicit NaClApplication(PP_Instance instance); + + /** + * @brief Constructor + * @param instance Module instance + * @param configuration Configuration + * + * The @p configuration is deleted afterwards. If `nullptr` is passed + * as @p configuration, the context is not created and must be created + * with createContext(). + */ + explicit NaClApplication(PP_Instance instance, Configuration* configuration); ~NaClApplication(); @@ -102,6 +115,8 @@ class NaClApplication: public pp::Instance, public pp::Graphics3DClient, public bool setFullscreen(bool enabled); protected: + /** @copydoc GlutApplication::createContext() */ + void createContext(Configuration* configuration); /** @{ @name Drawing functions */ @@ -223,6 +238,40 @@ class NaClApplication: public pp::Instance, public pp::Graphics3DClient, public CORRADE_ENUMSET_FRIEND_OPERATORS(Flags) }; +/** +@brief %Configuration + +Double-buffered RGBA canvas with depth and stencil buffers. +@see NaClApplication(), createContext() +*/ +class NaClApplication::Configuration { + Configuration(const Configuration&) = delete; + Configuration(Configuration&&) = delete; + Configuration& operator=(const Configuration&) = delete; + Configuration& operator=(Configuration&&) = delete; + + public: + explicit Configuration(); + ~Configuration(); + + /** @brief Window size */ + inline Vector2i size() const { return _size; } + + /** + * @brief Set window size + * @return Pointer to self (for method chaining) + * + * Default is `{640, 480}`. + */ + inline Configuration* setSize(const Vector2i& size) { + _size = size; + return this; + } + + private: + Vector2i _size; +}; + /** @brief Base for input events diff --git a/src/Platform/Sdl2Application.cpp b/src/Platform/Sdl2Application.cpp index 6ed239158..9a39d9251 100644 --- a/src/Platform/Sdl2Application.cpp +++ b/src/Platform/Sdl2Application.cpp @@ -49,7 +49,17 @@ Sdl2Application::InputEvent::Modifiers fixedModifiers(Uint16 mod) { } -Sdl2Application::Sdl2Application(int, char**, const std::string& name, const Vector2i& size): flags(Flag::Redraw) { +Sdl2Application::Sdl2Application(int&, char**): context(nullptr), flags(Flag::Redraw) { + createContext(new Configuration); +} + +Sdl2Application::Sdl2Application(int&, char**, Configuration* configuration): context(nullptr), flags(Flag::Redraw) { + if(configuration) createContext(configuration); +} + +void Sdl2Application::createContext(Configuration* configuration) { + CORRADE_ASSERT(!context, "Sdl2Application::createContext(): context already created", ); + if(SDL_Init(SDL_INIT_VIDEO) < 0) { Error() << "Cannot initialize SDL."; std::exit(1); @@ -59,8 +69,8 @@ Sdl2Application::Sdl2Application(int, char**, const std::string& name, const Vec SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); - window = SDL_CreateWindow(name.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, - size.x(), size.y(), SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN); + window = SDL_CreateWindow(configuration->title().c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + configuration->size().x(), configuration->size().y(), SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN); if(!window) { Error() << "Cannot create window."; std::exit(2); @@ -76,11 +86,12 @@ Sdl2Application::Sdl2Application(int, char**, const std::string& name, const Vec SDL_Event* sizeEvent = new SDL_Event; sizeEvent->type = SDL_WINDOWEVENT; sizeEvent->window.event = SDL_WINDOWEVENT_RESIZED; - sizeEvent->window.data1 = size.x(); - sizeEvent->window.data2 = size.y(); + sizeEvent->window.data1 = configuration->size().x(); + sizeEvent->window.data2 = configuration->size().y(); SDL_PushEvent(sizeEvent); c = new Context; + delete configuration; } Sdl2Application::~Sdl2Application() { @@ -150,6 +161,9 @@ void Sdl2Application::setMouseLocked(bool enabled) { SDL_SetRelativeMouseMode(enabled ? SDL_TRUE : SDL_FALSE); } +Sdl2Application::Configuration::Configuration(): _title("Magnum SDL2 Application"), _size(800, 600) {} +Sdl2Application::Configuration::~Configuration() = default; + Sdl2Application::InputEvent::Modifiers Sdl2Application::MouseEvent::modifiers() { if(modifiersLoaded) return _modifiers; modifiersLoaded = true; diff --git a/src/Platform/Sdl2Application.h b/src/Platform/Sdl2Application.h index 356b5109d..9f36b90c4 100644 --- a/src/Platform/Sdl2Application.h +++ b/src/Platform/Sdl2Application.h @@ -47,9 +47,8 @@ namespace Platform { /** @nosubgrouping @brief SDL2 application -Application using [Simple DirectMedia Layer](www.libsdl.org/). Centered -non-resizable window with double-buffered OpenGL context and 24bit depth -buffer. Supports keyboard and mouse handling. +Application using [Simple DirectMedia Layer](www.libsdl.org/). Supports +keyboard and mouse handling. See @ref platform for brief introduction. @section Sdl2Application-usage Usage @@ -69,37 +68,29 @@ to simplify porting. */ class Sdl2Application { public: + class Configuration; class InputEvent; class KeyEvent; class MouseEvent; class MouseMoveEvent; - /** - * @brief Constructor - * @param argc Count of arguments of `main()` function - * @param argv Arguments of `main()` function - * @param title Window title - * @param size Window size - */ - explicit Sdl2Application(int argc, char** argv, const std::string& title = "Magnum SDL2 application", const Vector2i& size = Vector2i(800, 600)); + /** @copydoc GlutApplication::GlutApplication(int&, char**) */ + explicit Sdl2Application(int& argc, char** argv); + + /** @copydoc GlutApplication::GlutApplication(int&, char**, Configuration*) */ + explicit Sdl2Application(int& argc, char** argv, Configuration* configuration); - /** - * @brief Destructor - * - * Deletes context and destroys the window. - */ virtual ~Sdl2Application(); - /** - * @brief Execute main loop - * @return Value for returning from `main()`. - */ + /** @copydoc GlutApplication::exec() */ int exec(); /** @brief Exit application main loop */ inline void exit() { flags |= Flag::Exit; } protected: + /** @copydoc GlutApplication::createContext() */ + void createContext(Configuration* configuration); /** @{ @name Drawing functions */ @@ -183,6 +174,56 @@ class Sdl2Application { CORRADE_ENUMSET_OPERATORS(Sdl2Application::Flags) +/** +@brief %Configuration + +Centered non-resizable window with double-buffered OpenGL context and 24bit +depth buffer. +@see Sdl2Application(), createContext() +*/ +class Sdl2Application::Configuration { + Configuration(const Configuration&) = delete; + Configuration(Configuration&&) = delete; + Configuration& operator=(const Configuration&) = delete; + Configuration& operator=(Configuration&&) = delete; + + public: + explicit Configuration(); + ~Configuration(); + + /** @brief Window title */ + inline std::string title() const { return _title; } + + /** + * @brief Set window title + * @return Pointer to self (for method chaining) + * + * Default is `"Magnum SDL2 Application"`. + */ + inline Configuration* setTitle(std::string title) { + _title = std::move(title); + return this; + } + + /** @brief Window size */ + inline Vector2i size() const { return _size; } + + /** + * @brief Set window size + * @return Pointer to self (for method chaining) + * + * Default is `{800, 600}`. + */ + inline Configuration* setSize(const Vector2i& size) { + _size = size; + return this; + } + + private: + std::string _title; + Vector2i _size; +}; + /** @brief Base for input events diff --git a/src/Platform/WindowlessGlxApplication.cpp b/src/Platform/WindowlessGlxApplication.cpp index 36370c686..12429ab20 100644 --- a/src/Platform/WindowlessGlxApplication.cpp +++ b/src/Platform/WindowlessGlxApplication.cpp @@ -24,6 +24,7 @@ #include "WindowlessGlxApplication.h" +#include #include #include "Context.h" @@ -33,6 +34,16 @@ namespace Magnum { namespace Platform { WindowlessGlxApplication::WindowlessGlxApplication(int&, char**) { + createContext(new Configuration); +} + +WindowlessGlxApplication::WindowlessGlxApplication(int&, char**, Configuration* configuration) { + if(configuration) createContext(configuration); +} + +void WindowlessGlxApplication::createContext(Configuration* configuration) { + CORRADE_ASSERT(!c, "WindowlessGlxApplication::createContext(): context already created", ); + display = XOpenDisplay(nullptr); /* Check version */ @@ -89,6 +100,7 @@ WindowlessGlxApplication::WindowlessGlxApplication(int&, char**) { ExtensionWrangler::initialize(ExtensionWrangler::ExperimentalFeatures::Enable); c = new Context; + delete configuration; } WindowlessGlxApplication::~WindowlessGlxApplication() { @@ -96,4 +108,7 @@ WindowlessGlxApplication::~WindowlessGlxApplication() { glXDestroyContext(display, context); } +WindowlessGlxApplication::Configuration::Configuration() = default; +WindowlessGlxApplication::Configuration::~Configuration() = default; + }} diff --git a/src/Platform/WindowlessGlxApplication.h b/src/Platform/WindowlessGlxApplication.h index 47c0cbced..4e0762ccc 100644 --- a/src/Platform/WindowlessGlxApplication.h +++ b/src/Platform/WindowlessGlxApplication.h @@ -45,6 +45,8 @@ namespace Magnum { namespace Platform { /** @brief Windowless GLX application +See @ref platform for brief introduction. + @section WindowlessGlxApplication-usage Usage Place your code into exec(). The subclass can be then used directly in @@ -62,16 +64,14 @@ If no other application header is included this class is also aliased to */ class WindowlessGlxApplication { public: - /** - * @brief Constructor - * @param argc Count of arguments of `main()` function - * @param argv Arguments of `main()` function - * - * Creates window with double-buffered OpenGL 3.2 core context or - * OpenGL ES 2.0 context, if targeting OpenGL ES. - */ + class Configuration; + + /** @copydoc GlutApplication::GlutApplication(int&, char**) */ explicit WindowlessGlxApplication(int& argc, char** argv); + /** @copydoc GlutApplication::GlutApplication(int&, char**, Configuration*) */ + explicit WindowlessGlxApplication(int& argc, char** argv, Configuration* configuration); + ~WindowlessGlxApplication(); /** @@ -80,6 +80,10 @@ class WindowlessGlxApplication { */ virtual int exec() = 0; + protected: + /** @copydoc GlutApplication::createContext() */ + void createContext(Configuration* configuration); + private: Display* display; GLXContext context; @@ -88,6 +92,22 @@ class WindowlessGlxApplication { Context* c; }; +/** +@brief %Configuration + +@see WindowlessGlxApplication(), createContext() +*/ +class WindowlessGlxApplication::Configuration { + Configuration(const Configuration&) = delete; + Configuration(Configuration&&) = delete; + Configuration& operator=(const Configuration&) = delete; + Configuration& operator=(Configuration&&) = delete; + + public: + explicit Configuration(); + ~Configuration(); +}; + /** @hideinitializer @brief Entry point for windowless GLX application @param className Class name diff --git a/src/Platform/XEglApplication.h b/src/Platform/XEglApplication.h index 65cc2d5ae..f9c716b27 100644 --- a/src/Platform/XEglApplication.h +++ b/src/Platform/XEglApplication.h @@ -36,8 +36,7 @@ namespace Magnum { namespace Platform { /** @brief X/EGL application -Creates window with double-buffered OpenGL ES 2 context. Uses -EglContextHandler. +Uses EglContextHandler. See @ref platform for brief introduction. @section XEglApplication-usage Usage @@ -57,14 +56,11 @@ to simplify porting. */ class XEglApplication: public AbstractXApplication { public: - /** - * @brief Constructor - * @param argc Count of arguments of `main()` function - * @param argv Arguments of `main()` function - * @param title Window title - * @param size Window size - */ - inline explicit XEglApplication(int& argc, char** argv, const std::string& title = "Magnum X/EGL application", const Vector2i& size = Vector2i(800, 600)): AbstractXApplication(new EglContextHandler, argc, argv, title, size) {} + /** @copydoc GlutApplication::GlutApplication(int&, char**) */ + inline explicit XEglApplication(int& argc, char** argv): AbstractXApplication(new EglContextHandler, argc, argv) {} + + /** @copydoc GlutApplication::GlutApplication(int&, char**, Configuration*) */ + inline explicit XEglApplication(int& argc, char** argv, Configuration* configuration): AbstractXApplication(new EglContextHandler, argc, argv, configuration) {} }; /** @hideinitializer