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