diff --git a/doc/changelog-old.dox b/doc/changelog-old.dox index 051cb66a26..2941661501 100644 --- a/doc/changelog-old.dox +++ b/doc/changelog-old.dox @@ -108,7 +108,7 @@ Released 2018-05-01, tagged as - Ability to create @ref Platform::Sdl2Application and @ref Platform::GlfwApplication classes without implicitly created OpenGL - context by passing @ref Platform::Sdl2Application::Configuration::WindowFlag::Contextless "Configuration::WindowFlag::Contextless" + context by passing @cpp Configuration::WindowFlag::Contextless @ce to them. These two can be now also built completely without the GL library. - Added @ref Platform::AndroidApplication::windowSize() - Added @ref Platform::AndroidApplication::nativeActivity() to access @@ -157,10 +157,9 @@ Released 2018-05-01, tagged as @subsubsection changelog-2018-04-changes-platform Platform libraries -- Separated @ref Platform::Sdl2Application::Configuration "Platform::*Application::Configuration" - into @ref Platform::Sdl2Application::Configuration "Configuration" and - @ref Platform::Sdl2Application::GLConfiguration "GLConfiguration" to allow - creation of non-GL application instances in the future +- Separated @cpp Platform::*Application::Configuration @ce + into @cpp Configuration @ce and @cpp GLConfiguration @ce to allow creation + of non-GL application instances in the future @subsubsection changelog-2018-04-changes-plugins Plugins @@ -753,10 +752,10 @@ a high-level overview. possible - Ported @ref magnum-gl-info "magnum-info" to Emscripten - First-class support for scroll events in - @ref Platform::Sdl2Application::MouseScrollEvent (see - [mosra/magnum#157](https://github.com/mosra/magnum/pull/157)) -- Added @ref Platform::Sdl2Application::MouseEvent::clickCount() -- Added @ref Platform::Sdl2Application::multiGestureEvent() + @cpp Platform::Sdl2Application::MouseScrollEvent @ce + (see [mosra/magnum#157](https://github.com/mosra/magnum/pull/157)) +- Added @cpp Platform::Sdl2Application::MouseEvent::clickCount() @ce +- Added @cpp Platform::Sdl2Application::multiGestureEvent() @ce - Exposing key repeat in @ref Platform::Sdl2Application::KeyEvent::isRepeated() "Platform::*Application::KeyEvent::isRepeated()" (see [mosra/magnum#161](https://github.com/mosra/magnum/issues/161), @@ -773,8 +772,8 @@ a high-level overview. version is not what the application wants (as opposed to just aborting the application) (see [mosra/magnum#105](https://github.com/mosra/magnum/issues/105)) - Added @cpp Platform::Sdl2Application::Configuration::setSRGBCapable() @ce -- Added @ref Platform::Sdl2Application::Configuration::WindowFlag::Borderless - and `Platform::Sdl2Application::Configuration::WindowFlag::AllowHighDpi` +- Added @cpp Platform::Sdl2Application::Configuration::WindowFlag::Borderless @ce + and @cpp Platform::Sdl2Application::Configuration::WindowFlag::AllowHighDpi @ce for iOS and macOS - Added @ref Platform::WindowlessGlxApplication::Configuration::setFlags() "Platform::Windowless*Application::Configuration::setFlags()" with @ref Platform::WindowlessGlxApplication::Configuration::Flag::Debug "Flag::Debug" @@ -786,7 +785,7 @@ a high-level overview. with @ref Platform::GlfwApplication - Added modifier keys to @ref Platform::Sdl2Application::KeyEvent::Key "Platform::*Application::KeyEvent::Key" -- Added @ref Platform::Sdl2Application::InputEvent::Modifier::Super to be +- Added @cpp Platform::Sdl2Application::InputEvent::Modifier::Super @ce to be consistent with @ref Platform::GlfwApplication (see [mosra/magnum#159](https://github.com/mosra/magnum/pull/159)) - Added @ref Platform::Sdl2Application::KeyEvent::keyName() "Platform::*Application::KeyEvent::keyName()" @@ -1102,10 +1101,10 @@ a high-level overview. `Platform::GlfwApplication::MouseEvent::Button::WheelDown` mouse events are deprecated, use @ref Platform::Sdl2Application::mouseScrollEvent() / @ref Platform::GlfwApplication::mouseScrollEvent() and - @ref Platform::Sdl2Application::MouseScrollEvent / + @cpp Platform::Sdl2Application::MouseScrollEvent @ce / @ref Platform::GlfwApplication::MouseScrollEvent instead -- @ref Platform::Sdl2Application::Sdl2Application() "Platform::*Application::*Application()" - and @ref Platform::WindowlessGlxApplication::WindowlessGlxApplication() "Platform::Windowless*Application::Windowless*Application()" +- @cpp Platform::*Application::*Application() @ce and + @ref Platform::WindowlessGlxApplication::WindowlessGlxApplication() "Platform::Windowless*Application::Windowless*Application()" constructors taking @cpp nullptr @ce are deprecated, use constructors taking @ref NoCreateT instead to create an application without creating OpenGL context @@ -1844,7 +1843,7 @@ for a high-level overview. purely integral coordinates, useful e.g. for UI or 2D platformers. - Detailed collision queries and new `InvertedSphere` shape in Shapes library - Texture support in @cpp Shaders::Flat @ce -- Mouse button queries in @ref Platform::Sdl2Application::MouseMoveEvent "Platform::*Application::MouseMoveEvent" +- Mouse button queries in @cpp Platform::*Application::MouseMoveEvent @ce @subsection changelog-2013-10-changes Changes diff --git a/doc/changelog.dox b/doc/changelog.dox index 5654acdf34..d295bff0e2 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -732,13 +732,13 @@ See also: - Added @ref Platform::EmscriptenApplication::Configuration::addWindowFlags() and @ref Platform::EmscriptenApplication::Configuration::clearWindowFlags() for consistency with other application implementations -- Added @ref Platform::Sdl2Application::KeyEvent::Key::CapsLock, - @relativeref{Platform::Sdl2Application::KeyEvent::Key,ScrollLock}, - @relativeref{Platform::Sdl2Application::KeyEvent::Key,NumLock}, - @relativeref{Platform::Sdl2Application::KeyEvent::Key,PrintScreen}, - @relativeref{Platform::Sdl2Application::KeyEvent::Key,Pause} and - @relativeref{Platform::Sdl2Application::KeyEvent::Key,Menu} for consistency - with @ref Platform::EmscriptenApplication and +- Added @ref Platform::Sdl2ApplicationWindow::KeyEvent::Key::CapsLock, + @relativeref{Platform::Sdl2ApplicationWindow::KeyEvent::Key,ScrollLock}, + @relativeref{Platform::Sdl2ApplicationWindow::KeyEvent::Key,NumLock}, + @relativeref{Platform::Sdl2ApplicationWindow::KeyEvent::Key,PrintScreen}, + @relativeref{Platform::Sdl2ApplicationWindow::KeyEvent::Key,Pause} and + @relativeref{Platform::Sdl2ApplicationWindow::KeyEvent::Key,Menu} for + consistency with @ref Platform::EmscriptenApplication and @ref Platform::GlfwApplication (see [mosra/magnum#547](https://github.com/mosra/magnum/pull/547)) - @ref Platform::Sdl2Application now overrides SDL's default behavior that prevents computer from entering a power-saving mode while the application @@ -1103,9 +1103,9 @@ See also: destroyed could fail with an error saying "cannot make the previous context current" on certain system. This was due to EGL not destroying the context if it's still made current. -- Fixed handling of @ref Platform::Sdl2Application::InputEvent::Modifier::Super, - which was misreported as @relativeref{Platform::Sdl2Application::InputEvent::Modifier,Alt} (see - [mosra/magnum#547](https://github.com/mosra/magnum/pull/547)) +- Fixed handling of @ref Platform::Sdl2ApplicationWindow::InputEvent::Modifier::Super, + which was misreported as @relativeref{Platform::Sdl2ApplicationWindow::InputEvent::Modifier,Alt} + (see [mosra/magnum#547](https://github.com/mosra/magnum/pull/547)) - For meshes with multiple sets of vertex attributes (such as texture coordinates), @ref MeshTools::compile() should be using only the first set but it wasn't. @@ -1273,6 +1273,10 @@ See also: performance and a default, use @relativeref{Platform::EmscriptenApplication::GLConfiguration,Flag::PowerPreferenceLowPower} or @relativeref{Platform::EmscriptenApplication::GLConfiguration,Flag::PowerPreferenceHighPerformance} instead +- GPU-context-related flags were moved from + @ref Platform::Sdl2Application::Configuration::WindowFlag to a new + @ref Platform::Sdl2Application::Configuration::Flag enum, as they're set + globally for all application windows and can't differ per-window - @cpp Shaders::DistanceFieldVector @ce, @cpp Shaders::Flat @ce, @cpp Shaders::Generic @ce, @cpp Shaders::MeshVisualizer2D @ce, @cpp Shaders::MeshVisualizer3D @ce, @cpp Shaders::Phong @ce, @@ -1969,12 +1973,12 @@ Released 2020-06-27, tagged as @ref Platform::AbstractXApplication::mainLoopIteration() for consistency with @ref Platform::Sdl2Application (see [mosra/magnum#387](https://github.com/mosra/magnum/pull/387)) -- Added @ref Platform::Sdl2Application::KeyEvent::Key::Quote "Key::Quote", - @ref Platform::Sdl2Application::KeyEvent::Key::LeftBracket "Key::LeftBracket", - @ref Platform::Sdl2Application::KeyEvent::Key::RightBracket "Key::RightBracket", - @ref Platform::Sdl2Application::KeyEvent::Key::Backslash "Key::Backslash" and - @ref Platform::Sdl2Application::KeyEvent::Key::Backquote "Key::Backquote" - keys to @ref Platform::Sdl2Application::KeyEvent and +- Added @relativeref{Platform::Sdl2ApplicationWindow::KeyEvent,Key::Quote}, + @relativeref{Platform::Sdl2ApplicationWindow::KeyEvent,Key::LeftBracket}, + @relativeref{Platform::Sdl2ApplicationWindow::KeyEvent,Key::RightBracket}, + @relativeref{Platform::Sdl2ApplicationWindow::KeyEvent,Key::Backslash} and + @relativeref{Platform::Sdl2ApplicationWindow::KeyEvent,Key::Backquote} + keys to @cpp Platform::Sdl2Application::KeyEvent @ce and @ref Platform::GlfwApplication::KeyEvent - Added @ref Platform::GlfwApplication::KeyEvent::Key::World1 and @ref Platform::GlfwApplication::KeyEvent::Key::World2 @@ -1992,12 +1996,9 @@ Released 2020-06-27, tagged as - CUDA device selection in @ref Platform::WindowlessEglApplication (see [mosra/magnum#449](https://github.com/mosra/magnum/pull/449)) - Added @ref Platform::GlfwApplication::Configuration::WindowFlag::Borderless -- Added @ref Platform::Sdl2Application::Configuration::WindowFlag::FullscreenDesktop, - @ref Platform::Sdl2Application::Configuration::WindowFlag::AlwaysOnTop "AlwaysOnTop", - @ref Platform::Sdl2Application::Configuration::WindowFlag::SkipTaskbar "SkipTaskbar", - @ref Platform::Sdl2Application::Configuration::WindowFlag::Utility "Utility", - @ref Platform::Sdl2Application::Configuration::WindowFlag::Tooltip "Tooltip" - and @ref Platform::Sdl2Application::Configuration::WindowFlag::PopupMenu "PopupMenu" +- Added @cpp Platform::Sdl2Application::Configuration::WindowFlag::FullscreenDesktop @ce, + @cpp AlwaysOnTop @ce, @cpp SkipTaskbar @ce, @cpp Utility @ce, + @cpp Tooltip @ce and @cpp PopupMenu @ce - Added @ref Platform::Sdl2Application::Configuration::addWindowFlags() and @ref Platform::Sdl2Application::Configuration::clearWindowFlags() "clearWindowFlags()" for consistency with similar functions in @@ -2299,7 +2300,7 @@ Released 2020-06-27, tagged as - The @ref Primitives::cylinderSolid() and @ref Primitives::coneSolid() primitives were missing a face when both caps and texture coordinates were enabled (see [mosra/magnum#386](https://github.com/mosra/magnum/issues/386)) -- @ref Platform::Sdl2Application::Configuration::WindowFlag::Vulkan was +- @cpp Platform::Sdl2Application::Configuration::WindowFlag::Vulkan @ce was enabled conditionally only for SDL >= 2.0.6, but the version defines were never included so it was always disabled - Fixed missing handling of @@ -2330,7 +2331,7 @@ Released 2020-06-27, tagged as caused @ref Platform::Sdl2Application::setMinimalLoopPeriod() "setMinimalLoopPeriod()" to be ignored even though Vsync was in fact not enabled. - It was not possible to override DPI scaling using - @ref Platform::Sdl2Application::Configuration as command-line arguments + @cpp Platform::Sdl2Application::Configuration @ce as command-line arguments always got a priority (see [mosra/magnum#416](https://github.com/mosra/magnum/issues/416)) - Fixed an otherwise harmless OOB access in @ref MeshTools::tipsifyInPlace() that could trigger ASan or debug iterator errors @@ -2832,10 +2833,10 @@ Released 2019-10-24, tagged as in @ref Platform::GlfwApplication and @ref Platform::EmscriptenApplication) for changing window title at runtime as opposed to setting them on application startup -- Added @ref Platform::Sdl2Application::Configuration::WindowFlag::OpenGL and - @ref Platform::Sdl2Application::Configuration::WindowFlag::Vulkan "WindowFlag::Vulkan", - meant to be used together with @ref Platform::Sdl2Application::Configuration::WindowFlag::Contextless for - manual creation of OpenGL contexts and Vulkan instances +- Added @cpp Platform::Sdl2Application::Configuration::WindowFlag::OpenGL @ce + and @cpp WindowFlag::Vulkan @ce, meant to be used together with + @cpp Platform::Sdl2Application::Configuration::WindowFlag::Contextless @ce + for manual creation of OpenGL contexts and Vulkan instances - @ref Platform::WindowlessEglApplication now uses the @m_class{m-doc-external} [EGL_EXT_device_enumeration](https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_device_enumeration.txt), @m_class{m-doc-external} [EGL_EXT_platform_base](https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_base.txt) and diff --git a/doc/platform.dox b/doc/platform.dox index 27636dd1af..78a77486da 100644 --- a/doc/platform.dox +++ b/doc/platform.dox @@ -136,8 +136,8 @@ target_link_libraries(myapplication @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 Platform::Sdl2Application::Configuration "Configuration" instance to +window size 800x600 pixels). If you want something else, you can pass a +@relativeref{Platform::Sdl2ApplicationWindow,Configuration} instance to the application constructor. Using method chaining it can be done conveniently like this: @@ -148,7 +148,7 @@ this: Sometimes you may want to set up the application based on a configuration file or system introspection, which can't really be done inside the base class initializer. You can specify @ref NoCreate in the constructor instead and pass -the @relativeref{Platform::Sdl2Application,Configuration} later to a +the @relativeref{Platform::Sdl2ApplicationWindow,Configuration} later to a @relativeref{Platform::Sdl2Application,create()} function: @snippet MagnumPlatform.cpp createcontext diff --git a/doc/snippets/MagnumTextureTools.cpp b/doc/snippets/MagnumTextureTools.cpp index 448af4033d..7e64af428b 100644 --- a/doc/snippets/MagnumTextureTools.cpp +++ b/doc/snippets/MagnumTextureTools.cpp @@ -57,7 +57,7 @@ atlas.add(stridedArrayView(images).slice(&ImageView2D::size), offsets, rotations /* Copy the image data to the atlas, assuming all are RGBA8Unorm as well */ Image2D output{PixelFormat::RGBA8Unorm, atlas.filledSize().xy(), - Containers::Array{ValueInit, std::size_t(atlas.filledSize().product())}}; + Containers::Array{ValueInit, std::size_t(atlas.filledSize().product()*4)}}; Containers::StridedArrayView2D dst = output.pixels(); for(std::size_t i = 0; i != images.size(); ++i) { /* Rotate 90° counterclockwise if the image is rotated in the atlas */ @@ -82,7 +82,7 @@ atlas.clearFlags(TextureTools::AtlasLandfillFlag::RotatePortrait| /* Copy the image data to the atlas, assuming all are RGBA8Unorm as well */ Image2D output{PixelFormat::RGBA8Unorm, atlas.filledSize().xy(), - Containers::Array{ValueInit, std::size_t(atlas.filledSize().product())}}; + Containers::Array{ValueInit, std::size_t(atlas.filledSize().product()*4)}}; Containers::StridedArrayView2D dst = output.pixels(); for(std::size_t i = 0; i != images.size(); ++i) { Containers::StridedArrayView2D src = images[i].pixels(); @@ -104,9 +104,8 @@ TextureTools::AtlasLandfill atlas{{1024, 1024, 0}}; atlas.add(stridedArrayView(images).slice(&ImageView2D::size), offsets, rotations); /* Copy the image data to the atlas, assuming all are RGBA8Unorm as well */ -Vector3i outputSize = atlas.filledSize(); -Image3D output{PixelFormat::RGBA8Unorm, outputSize, - Containers::Array{ValueInit, std::size_t(outputSize.product())}}; +Image3D output{PixelFormat::RGBA8Unorm, atlas.filledSize(), + Containers::Array{ValueInit, std::size_t(atlas.filledSize().product()*4)}}; Containers::StridedArrayView3D dst = output.pixels(); for(std::size_t i = 0; i != images.size(); ++i) { /* Rotate 90° counterclockwise if the image is rotated in the atlas */ @@ -135,7 +134,7 @@ Int layerCount = TextureTools::atlasArrayPowerOfTwo(layerSize, sizes, offsets); /* Copy the image data to the atlas, assuming all are RGBA8Unorm as well */ Vector3i outputSize{layerSize, layerCount}; Image3D output{PixelFormat::RGBA8Unorm, outputSize, - Containers::Array{ValueInit, std::size_t(outputSize.product())}}; + Containers::Array{ValueInit, std::size_t(outputSize.product()*4)}}; Containers::StridedArrayView3D dst = output.pixels(); for(std::size_t i = 0; i != input.size(); ++i) { Containers::StridedArrayView3D src = input[i].pixels(); diff --git a/src/Magnum/Platform/AndroidApplication.h b/src/Magnum/Platform/AndroidApplication.h index 47a914cb8c..dadab90a8c 100644 --- a/src/Magnum/Platform/AndroidApplication.h +++ b/src/Magnum/Platform/AndroidApplication.h @@ -149,8 +149,8 @@ to simplify porting. @section Platform-AndroidApplication-resizing Responding to viewport events Unlike in desktop application implementations, where this is controlled via -@ref Sdl2Application::Configuration::WindowFlag::Resizable, for example, on -Android you have to describe this in the `AndroidManifest.xml` file, as by +@ref Sdl2ApplicationWindow::Configuration::WindowFlag::Resizable, for example, +on Android you have to describe this in the `AndroidManifest.xml` file, as by default the application gets killed and relaunched on screen orientation change. See the @ref platforms-android-apps-manifest-screen-resize "manifest file docs" for more information. @@ -189,7 +189,7 @@ class AndroidApplication { #endif /** - * @brief Construct with given configuration for OpenGL context + * @brief Construct with an OpenGL context * @param arguments Application arguments * @param configuration Application configuration * @param glConfiguration OpenGL context configuration @@ -202,20 +202,17 @@ class AndroidApplication { explicit AndroidApplication(const Arguments& arguments, const Configuration& configuration, const GLConfiguration& glConfiguration); /** - * @brief Construct with given configuration + * @brief Construct without explicit GPU context configuration * * Equivalent to calling @ref AndroidApplication(const Arguments&, const Configuration&, const GLConfiguration&) * with default-constructed @ref GLConfiguration. */ + #ifdef DOXYGEN_GENERATING_OUTPUT + explicit AndroidApplication(const Arguments& arguments, const Configuration& configuration = Configuration{}); + #else explicit AndroidApplication(const Arguments& arguments, const Configuration& configuration); - - /** - * @brief Construct with default configuration - * - * Equivalent to calling @ref AndroidApplication(const Arguments&, const Configuration&) - * with default-constructed @ref Configuration. - */ explicit AndroidApplication(const Arguments& arguments); + #endif /** * @brief Construct without creating a window diff --git a/src/Magnum/Platform/EmscriptenApplication.cpp b/src/Magnum/Platform/EmscriptenApplication.cpp index adb3c504c6..fcf46b3060 100644 --- a/src/Magnum/Platform/EmscriptenApplication.cpp +++ b/src/Magnum/Platform/EmscriptenApplication.cpp @@ -343,7 +343,10 @@ bool EmscriptenApplication::tryCreate(const Configuration& configuration) { #ifdef MAGNUM_TARGET_GL bool EmscriptenApplication::tryCreate(const Configuration& configuration, const GLConfiguration& glConfiguration) { - CORRADE_ASSERT(_context->version() == GL::Version::None, "Platform::EmscriptenApplication::tryCreate(): window with OpenGL context already created", false); + CORRADE_ASSERT(!(configuration.windowFlags() & Configuration::WindowFlag::Contextless), + "Platform::EmscriptenApplication::tryCreate(): cannot pass Configuration::WindowFlag::Contextless when creating an OpenGL context", false); + CORRADE_ASSERT(_context->version() == GL::Version::None, + "Platform::EmscriptenApplication::tryCreate(): window with OpenGL context already created", false); /* Create emscripten WebGL context */ EmscriptenWebGLContextAttributes attrs; diff --git a/src/Magnum/Platform/EmscriptenApplication.h b/src/Magnum/Platform/EmscriptenApplication.h index 92605426e5..15710aa2c1 100644 --- a/src/Magnum/Platform/EmscriptenApplication.h +++ b/src/Magnum/Platform/EmscriptenApplication.h @@ -302,7 +302,7 @@ class EmscriptenApplication { #ifdef MAGNUM_TARGET_GL /** - * @brief Construct with given configuration for WebGL context + * @brief Construct with a WebGL context * @param arguments Application arguments * @param configuration Application configuration * @param glConfiguration WebGL context configuration @@ -320,7 +320,7 @@ class EmscriptenApplication { #endif /** - * @brief Construct with given configuration + * @brief Construct without explicit GPU context configuration * * If @ref Configuration::WindowFlag::Contextless is present or Magnum * was not built with @ref MAGNUM_TARGET_GL, this creates a window @@ -333,15 +333,13 @@ class EmscriptenApplication { * * See also @ref building-features for more information. */ + #ifdef DOXYGEN_GENERATING_OUTPUT + explicit EmscriptenApplication(const Arguments& arguments, const Configuration& configuration = Configuration{}); + #else + /* Configuration is only forward-declared at this point */ explicit EmscriptenApplication(const Arguments& arguments, const Configuration& configuration); - - /** - * @brief Construct with default configuration - * - * Equivalent to calling @ref EmscriptenApplication(const Arguments&, const Configuration&) - * with default-constructed @ref Configuration. - */ explicit EmscriptenApplication(const Arguments& arguments); + #endif /** * @brief Construct without setting up a canvas @@ -1215,11 +1213,13 @@ class EmscriptenApplication::Configuration { enum class WindowFlag: UnsignedShort { /** * Do not create any GPU context. Use together with - * @ref EmscriptenApplication(const Arguments&), * @ref EmscriptenApplication(const Arguments&, const Configuration&), * @ref create(const Configuration&) or * @ref tryCreate(const Configuration&) to prevent implicit - * creation of an WebGL context. + * creation of an WebGL context. Can't be used with + * @ref EmscriptenApplication(const Arguments&, const Configuration&, const GLConfiguration&), + * @ref create(const Configuration&, const GLConfiguration&) or + * @ref tryCreate(const Configuration&, const GLConfiguration&). */ Contextless = 1 << 0, diff --git a/src/Magnum/Platform/GlfwApplication.cpp b/src/Magnum/Platform/GlfwApplication.cpp index 45fd61e519..4ac3188edc 100644 --- a/src/Magnum/Platform/GlfwApplication.cpp +++ b/src/Magnum/Platform/GlfwApplication.cpp @@ -409,7 +409,10 @@ GlfwApplication::InputEvent::Modifiers currentGlfwModifiers(GLFWwindow* window) #ifdef MAGNUM_TARGET_GL bool GlfwApplication::tryCreate(const Configuration& configuration, const GLConfiguration& glConfiguration) { - CORRADE_ASSERT(!_window && _context->version() == GL::Version::None, "Platform::GlfwApplication::tryCreate(): window with OpenGL context already created", false); + CORRADE_ASSERT(!(configuration.windowFlags() & Configuration::WindowFlag::Contextless), + "Platform::GlfwApplication::tryCreate(): cannot pass Configuration::WindowFlag::Contextless when creating an OpenGL context", false); + CORRADE_ASSERT(!_window && _context->version() == GL::Version::None, + "Platform::GlfwApplication::tryCreate(): window with OpenGL context already created", false); /* Save DPI scaling values from configuration for future use, scale window based on those */ diff --git a/src/Magnum/Platform/GlfwApplication.h b/src/Magnum/Platform/GlfwApplication.h index f788d88812..e2e42cf74e 100644 --- a/src/Magnum/Platform/GlfwApplication.h +++ b/src/Magnum/Platform/GlfwApplication.h @@ -185,7 +185,7 @@ class GlfwApplication { #ifdef MAGNUM_TARGET_GL /** - * @brief Construct with given configuration for OpenGL context + * @brief Construct with an OpenGL context * @param arguments Application arguments * @param configuration Application configuration * @param glConfiguration OpenGL context configuration @@ -203,7 +203,7 @@ class GlfwApplication { #endif /** - * @brief Construct with given configuration + * @brief Construct without explicit GPU context configuration * * If @ref Configuration::WindowFlag::Contextless is present or Magnum * was not built with @ref MAGNUM_TARGET_GL, this creates a window @@ -216,15 +216,13 @@ class GlfwApplication { * * See also @ref building-features for more information. */ + #ifdef DOXYGEN_GENERATING_OUTPUT + explicit GlfwApplication(const Arguments& arguments, const Configuration& configuration = Configuration{}); + #else + /* Configuration is only forward-declared at this point */ explicit GlfwApplication(const Arguments& arguments, const Configuration& configuration); - - /** - * @brief Construct with default configuration - * - * Equivalent to calling @ref GlfwApplication(const Arguments&, const Configuration&) - * with default-constructed @ref Configuration. - */ explicit GlfwApplication(const Arguments& arguments); + #endif /** * @brief Construct without creating a window @@ -1112,11 +1110,13 @@ class GlfwApplication::Configuration { #if defined(DOXYGEN_GENERATING_OUTPUT) || defined(GLFW_NO_API) /** * Do not create any GPU context. Use together with - * @ref GlfwApplication(const Arguments&), * @ref GlfwApplication(const Arguments&, const Configuration&), * @ref create(const Configuration&) or * @ref tryCreate(const Configuration&) to prevent implicit - * creation of an OpenGL context. + * creation of an OpenGL context. Can't be used with + * @ref GlfwApplication(const Arguments&, const Configuration&, const GLConfiguration&), + * @ref create(const Configuration&, const GLConfiguration&) or + * @ref tryCreate(const Configuration&, const GLConfiguration&). * * @note Supported since GLFW 3.2. */ diff --git a/src/Magnum/Platform/GlxApplication.h b/src/Magnum/Platform/GlxApplication.h index e9ececfc18..d1d5f88e43 100644 --- a/src/Magnum/Platform/GlxApplication.h +++ b/src/Magnum/Platform/GlxApplication.h @@ -100,7 +100,7 @@ If no other application header is included, this class is also aliased to class GlxApplication: public AbstractXApplication { public: /** - * @brief Construct with given configuration for OpenGL context + * @brief Construct with an OpenGL context * @param arguments Application arguments * @param configuration Application configuration * @param glConfiguration OpenGL context configuration diff --git a/src/Magnum/Platform/Screen.h b/src/Magnum/Platform/Screen.h index 6442389a35..ce65879aea 100644 --- a/src/Magnum/Platform/Screen.h +++ b/src/Magnum/Platform/Screen.h @@ -175,7 +175,7 @@ template class BasicScreen: * @brief Key event * * Defined only if the application has a - * @ref Sdl2Application::KeyEvent "KeyEvent". + * @relativeref{Sdl2ApplicationWindow,KeyEvent}. */ typedef typename BasicScreenedApplication::KeyEvent KeyEvent; #endif @@ -192,7 +192,7 @@ template class BasicScreen: * @m_since{2019,10} * * Defined only if the application has a - * @ref Sdl2Application::MouseScrollEvent "MouseScrollEvent". + * @relativeref{Sdl2ApplicationWindow,MouseScrollEvent}. */ typedef typename BasicScreenedApplication::MouseScrollEvent MouseScrollEvent; @@ -201,7 +201,7 @@ template class BasicScreen: * @m_since{2019,10} * * Defined only if the application has a - * @ref Sdl2Application::TextInputEvent "TextInputEvent". + * @relativeref{Sdl2ApplicationWindow,TextInputEvent}. */ typedef typename BasicScreenedApplication::TextInputEvent TextInputEvent; @@ -210,7 +210,7 @@ template class BasicScreen: * @m_since{2019,10} * * Defined only if the application has a - * @ref Sdl2Application::TextEditingEvent "TextEditingEvent". + * @relativeref{Sdl2ApplicationWindow,TextEditingEvent}. */ typedef typename BasicScreenedApplication::TextEditingEvent TextEditingEvent; #endif @@ -379,7 +379,7 @@ template class BasicScreen: * Called when @ref PropagatedEvent::Input is enabled and an key is * pressed. See @ref Sdl2Application::keyPressEvent() "*Application::keyPressEvent()" * for more information. Defined only if the application has a - * @ref Sdl2Application::KeyEvent "KeyEvent". + * @relativeref{Sdl2ApplicationWindow,KeyEvent}. */ virtual void keyPressEvent(KeyEvent& event); @@ -389,7 +389,7 @@ template class BasicScreen: * Called when @ref PropagatedEvent::Input is enabled and an key is * released. See @ref Sdl2Application::keyReleaseEvent() "*Application::keyReleaseEvent()" * for more information. Defined only if the application has a - * @ref Sdl2Application::KeyEvent "KeyEvent". + * @relativeref{Sdl2ApplicationWindow,KeyEvent}. */ virtual void keyReleaseEvent(KeyEvent& event); #endif @@ -437,7 +437,7 @@ template class BasicScreen: * Called when @ref PropagatedEvent::Input is enabled and mouse wheel * is rotated. See @ref Sdl2Application::mouseScrollEvent() "*Application::mouseScrollEvent()" * for more information. Defined only if the application has a - * @ref Sdl2Application::MouseScrollEvent "MouseScrollEvent". + * @relativeref{Sdl2ApplicationWindow,MouseScrollEvent}. */ virtual void mouseScrollEvent(MouseScrollEvent& event); #endif @@ -457,7 +457,7 @@ template class BasicScreen: * * Called when @ref PropagatedEvent::Input is enabled and text is being * input. Defined only if the application has a - * @ref Sdl2Application::TextInputEvent "TextInputEvent". + * @relativeref{Sdl2ApplicationWindow,TextInputEvent}. */ virtual void textInputEvent(TextInputEvent& event); @@ -467,7 +467,7 @@ template class BasicScreen: * * Called when @ref PropagatedEvent::Input and the text is being * edited. Defined only if the application has a - * @ref Sdl2Application::TextEditingEvent "TextEditingEvent". + * @relativeref{Sdl2ApplicationWindow,TextEditingEvent}. */ virtual void textEditingEvent(TextEditingEvent& event); #endif diff --git a/src/Magnum/Platform/Sdl2Application.cpp b/src/Magnum/Platform/Sdl2Application.cpp index f2224a068f..3c38f80179 100644 --- a/src/Magnum/Platform/Sdl2Application.cpp +++ b/src/Magnum/Platform/Sdl2Application.cpp @@ -42,6 +42,7 @@ #include #include #endif +#include #include #include @@ -78,142 +79,91 @@ namespace Magnum { namespace Platform { using namespace Containers::Literals; -namespace { - -/* - * Fix up the modifiers -- we want >= operator to work properly on Shift, - * Ctrl, Alt, but SDL generates different event for left / right keys, thus - * (modifiers >= Shift) would pass only if both left and right were pressed, - * which is usually not what the developers wants. - */ -Sdl2Application::InputEvent::Modifiers fixedModifiers(Uint16 mod) { - Sdl2Application::InputEvent::Modifiers modifiers(static_cast(mod)); - if(modifiers & Sdl2Application::InputEvent::Modifier::Shift) modifiers |= Sdl2Application::InputEvent::Modifier::Shift; - if(modifiers & Sdl2Application::InputEvent::Modifier::Ctrl) modifiers |= Sdl2Application::InputEvent::Modifier::Ctrl; - if(modifiers & Sdl2Application::InputEvent::Modifier::Alt) modifiers |= Sdl2Application::InputEvent::Modifier::Alt; - if(modifiers & Sdl2Application::InputEvent::Modifier::Super) modifiers |= Sdl2Application::InputEvent::Modifier::Super; - return modifiers; -} - -} - -enum class Sdl2Application::Flag: UnsignedByte { +enum class Sdl2ApplicationWindow::WindowFlag: UnsignedByte { Redraw = 1 << 0, - VSyncEnabled = 1 << 1, - NoTickEvent = 1 << 2, - NoAnyEvent = 1 << 3, - Exit = 1 << 4, #ifdef CORRADE_TARGET_EMSCRIPTEN - TextInputActive = 1 << 5, - Resizable = 1 << 6, - #endif - #ifdef CORRADE_TARGET_APPLE - HiDpiWarningPrinted = 1 << 7 + TextInputActive = 1 << 1, + Resizable = 1 << 2, #endif }; -Sdl2Application::Sdl2Application(const Arguments& arguments): Sdl2Application{arguments, Configuration{}} {} +Sdl2ApplicationWindow::Sdl2ApplicationWindow(Sdl2Application& application, NoCreateT): _application{application}, _windowFlags{WindowFlag::Redraw} {} -Sdl2Application::Sdl2Application(const Arguments& arguments, const Configuration& configuration): Sdl2Application{arguments, NoCreate} { - create(configuration); +#ifndef CORRADE_TARGET_EMSCRIPTEN +Sdl2ApplicationWindow::Sdl2ApplicationWindow(Sdl2Application& application, const Configuration& configuration): Sdl2ApplicationWindow{application, NoCreate} { + if(!tryCreateWindow(configuration)) std::exit(1); } -#ifdef MAGNUM_TARGET_GL -Sdl2Application::Sdl2Application(const Arguments& arguments, const Configuration& configuration, const GLConfiguration& glConfiguration): Sdl2Application{arguments, NoCreate} { - create(configuration, glConfiguration); -} +Sdl2ApplicationWindow::Sdl2ApplicationWindow(Sdl2Application& application): Sdl2ApplicationWindow{application, Configuration{}} {} #endif -Sdl2Application::Sdl2Application(const Arguments& arguments, NoCreateT): +Sdl2ApplicationWindow::~Sdl2ApplicationWindow() { #ifndef CORRADE_TARGET_EMSCRIPTEN - _minimalLoopPeriod{0}, - #endif - _flags{Flag::Redraw} -{ - Utility::Arguments args{Implementation::windowScalingArguments()}; - #ifdef MAGNUM_TARGET_GL - _context.emplace(NoCreate, args, arguments.argc, arguments.argv); - #else - /** @todo this is duplicated here, in GlfwApplication and in - EmscriptenApplication, figure out a nice non-duplicated way to handle - this */ - args.addOption("log", "default").setHelp("log", "console logging", "default|quiet|verbose") - .setFromEnvironment("log") - .parse(arguments.argc, arguments.argv); + /* If the window isn't created yet because tryCreateWindow(), nothing to + do. This also covers the case for the main application window, which + gets destroyed (and reset to null) from within ~Sdl2Application() + because it has to be done before SDL_Quit(), and this destructor is + called after that. There it also means that it's not needed to remove + itself from the window list, as the list is already gone at this + point. */ + if(_window) + destroyWindow(); #endif +} - /* Available since 2.0.4, disables interception of SIGINT and SIGTERM so - it's possible to Ctrl-C the application even if exitEvent() doesn't set - event.setAccepted(). */ - #ifdef SDL_HINT_NO_SIGNAL_HANDLERS - SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1"); - #endif - /* Available since 2.0.6, uses dedicated OpenGL ES drivers if EGL is used, - and desktop GLES context otherwise. */ - #if defined(MAGNUM_TARGET_GLES) && defined(MAGNUM_TARGET_EGL) && defined(SDL_HINT_OPENGL_ES_DRIVER) - SDL_SetHint(SDL_HINT_OPENGL_ES_DRIVER, "1"); - #endif - /* Available since 2.0.8, disables compositor bypass on X11, which causes - flickering on KWin as the compositor gets shut down on every startup */ - #ifdef SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR - SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"); - #endif - /* By default, SDL behaves like if it was playing a video or whatever, - preventing the computer from turning off the screen or going to sleep. - While it sorta makes sense for games, it's useless and annoying for - regular apps. Together with the compositor disabling those two are the - most stupid defaults. */ - #ifdef SDL_HINT_VIDEO_ALLOW_SCREENSAVER - SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1"); - #endif - /* Available since 2.0.12, use EGL if desired */ - #if defined(MAGNUM_TARGET_EGL) && defined(SDL_HINT_VIDEO_X11_FORCE_EGL) - SDL_SetHint(SDL_HINT_VIDEO_X11_FORCE_EGL, "1"); - #endif +bool Sdl2ApplicationWindow::tryCreateWindow(const Configuration& configuration) { + CORRADE_ASSERT(!_window, + "Platform::Sdl2ApplicationWindow::tryCreateWindow(): window already created", false); - if(SDL_Init(SDL_INIT_VIDEO) < 0) { - Error() << "Cannot initialize SDL:" << SDL_GetError(); - std::exit(1); + /* Save DPI scaling values from configuration for future use, scale window + based on those */ + _configurationDpiScalingPolicy = configuration.dpiScalingPolicy(); + _configurationDpiScaling = configuration.dpiScaling(); + const Vector2i scaledWindowSize = configuration.size()*dpiScaling(configuration); + + /* Create a window */ + if(!(_window = SDL_CreateWindow( + #ifndef CORRADE_TARGET_IOS + configuration.title().data(), + #else + nullptr, + #endif + SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + scaledWindowSize.x(), scaledWindowSize.y(), + SDL_WINDOW_ALLOW_HIGHDPI|Uint32(configuration.windowFlags())|_application._configurationFlags))) + { + Error() << "Platform::Sdl2ApplicationWindow::tryCreateWindow(): cannot create a window:" << SDL_GetError(); + return false; } - /* Save command-line arguments */ - if(args.value("log") == "verbose") _verboseLog = true; - const Containers::StringView dpiScaling = args.value("dpi-scaling"); - if(dpiScaling == "default"_s) - _commandLineDpiScalingPolicy = Implementation::Sdl2DpiScalingPolicy::Default; - #ifdef CORRADE_TARGET_APPLE - else if(dpiScaling == "framebuffer"_s) - _commandLineDpiScalingPolicy = Implementation::Sdl2DpiScalingPolicy::Framebuffer; - #endif - #ifndef CORRADE_TARGET_APPLE - #if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_ANDROID) - else if(dpiScaling == "virtual"_s) - _commandLineDpiScalingPolicy = Implementation::Sdl2DpiScalingPolicy::Virtual; - #endif - else if(dpiScaling == "physical"_s) - _commandLineDpiScalingPolicy = Implementation::Sdl2DpiScalingPolicy::Physical; - #endif - else if(dpiScaling.containsAny(" \t\n"_s)) - _commandLineDpiScaling = args.value("dpi-scaling"); - else - _commandLineDpiScaling = Vector2{args.value("dpi-scaling")}; -} + /* Add itself to the window list */ + const std::size_t windowId = SDL_GetWindowID(_window); + if(windowId >= _application._windows.size()) { + for(Sdl2ApplicationWindow*& i: arrayAppend(_application._windows, NoInit, windowId - _application._windows.size() + 1)) + i = nullptr; + } + CORRADE_INTERNAL_ASSERT(!_application._windows[windowId]); + _application._windows[windowId] = this; -void Sdl2Application::create() { - create(Configuration{}); + return true; } -void Sdl2Application::create(const Configuration& configuration) { - if(!tryCreate(configuration)) std::exit(1); -} +#ifndef CORRADE_TARGET_EMSCRIPTEN +void Sdl2ApplicationWindow::destroyWindow() { + /* To prevent accidental double destructions and such, this function should + only be called if a window is actually created */ + CORRADE_INTERNAL_ASSERT(_window); -#ifdef MAGNUM_TARGET_GL -void Sdl2Application::create(const Configuration& configuration, const GLConfiguration& glConfiguration) { - if(!tryCreate(configuration, glConfiguration)) std::exit(1); + /* Remove itself from the window list */ + const std::size_t id = SDL_GetWindowID(_window); + CORRADE_INTERNAL_ASSERT(id < _application._windows.size()); + _application._windows[id] = nullptr; + + SDL_DestroyWindow(_window); } #endif -Vector2 Sdl2Application::dpiScaling(const Configuration& configuration) { +Vector2 Sdl2ApplicationWindow::dpiScaling(const Configuration& configuration) { /* Print a helpful warning in case some extra steps are needed for HiDPI support */ #ifdef CORRADE_TARGET_APPLE @@ -229,18 +179,18 @@ Vector2 Sdl2Application::dpiScaling(const Configuration& configuration) { return dpiScalingInternal(configuration.dpiScalingPolicy(), configuration.dpiScaling()); } -Vector2 Sdl2Application::dpiScalingInternal(const Implementation::Sdl2DpiScalingPolicy configurationDpiScalingPolicy, const Vector2& configurationDpiScaling) const { - std::ostream* verbose = _verboseLog ? Debug::output() : nullptr; +Vector2 Sdl2ApplicationWindow::dpiScalingInternal(const Implementation::Sdl2DpiScalingPolicy configurationDpiScalingPolicy, const Vector2& configurationDpiScaling) const { + std::ostream* verbose = _application._verboseLog ? Debug::output() : nullptr; /* Use values from the configuration only if not overridden on command line to something non-default. In any case explicit scaling has a precedence before the policy. */ Implementation::Sdl2DpiScalingPolicy dpiScalingPolicy{}; - if(!_commandLineDpiScaling.isZero()) { - Debug{verbose} << "Platform::Sdl2Application: user-defined DPI scaling" << _commandLineDpiScaling; - return _commandLineDpiScaling; - } else if(_commandLineDpiScalingPolicy != Implementation::Sdl2DpiScalingPolicy::Default) { - dpiScalingPolicy = _commandLineDpiScalingPolicy; + if(!_application._commandLineDpiScaling.isZero()) { + Debug{verbose} << "Platform::Sdl2Application: user-defined DPI scaling" << _application._commandLineDpiScaling; + return _application._commandLineDpiScaling; + } else if(_application._commandLineDpiScalingPolicy != Implementation::Sdl2DpiScalingPolicy::Default) { + dpiScalingPolicy = _application._commandLineDpiScalingPolicy; } else if(!configurationDpiScaling.isZero()) { Debug{verbose} << "Platform::Sdl2Application: app-defined DPI scaling" << configurationDpiScaling; return configurationDpiScaling; @@ -346,7 +296,7 @@ Vector2 Sdl2Application::dpiScalingInternal(const Implementation::Sdl2DpiScaling #endif } -void Sdl2Application::setWindowTitle(const Containers::StringView title) { +void Sdl2ApplicationWindow::setWindowTitle(const Containers::StringView title) { #ifndef CORRADE_TARGET_EMSCRIPTEN SDL_SetWindowTitle(_window, Containers::String::nullTerminatedGlobalView(title).data()); @@ -359,7 +309,7 @@ void Sdl2Application::setWindowTitle(const Containers::StringView title) { } #if !defined(CORRADE_TARGET_EMSCRIPTEN) && SDL_MAJOR_VERSION*1000 + SDL_MINOR_VERSION*100 + SDL_PATCHLEVEL >= 2005 -void Sdl2Application::setWindowIcon(const ImageView2D& image) { +void Sdl2ApplicationWindow::setWindowIcon(const ImageView2D& image) { Uint32 format; /** @todo handle sRGB differently? */ switch(image.format()) { case PixelFormat::RGB8Srgb: @@ -371,7 +321,7 @@ void Sdl2Application::setWindowIcon(const ImageView2D& image) { format = SDL_PIXELFORMAT_RGBA32; break; default: - CORRADE_ASSERT_UNREACHABLE("Platform::Sdl2Application::setWindowIcon(): unexpected format" << image.format(), ); + CORRADE_ASSERT_UNREACHABLE("Platform::Sdl2ApplicationWindow::setWindowIcon(): unexpected format" << image.format(), ); } /* Images are loaded with origin at bottom left, flip it to top left. SDL @@ -383,41 +333,320 @@ void Sdl2Application::setWindowIcon(const ImageView2D& image) { const Containers::StridedArrayView3D flippedPixels = flippedImage.pixels(); Utility::copy(image.pixels().flipped<0>(), flippedPixels); - SDL_Surface* const icon = SDL_CreateRGBSurfaceWithFormatFrom(const_cast(flippedPixels.data()) , flippedImage.size().x(), flippedImage.size().y(), 32, flippedPixels.stride()[0], format); - CORRADE_INTERNAL_ASSERT(icon); + SDL_Surface* const icon = SDL_CreateRGBSurfaceWithFormatFrom(const_cast(flippedPixels.data()) , flippedImage.size().x(), flippedImage.size().y(), 32, flippedPixels.stride()[0], format); + CORRADE_INTERNAL_ASSERT(icon); + + SDL_SetWindowIcon(_window, icon); + SDL_FreeSurface(icon); +} +#endif + +Vector2i Sdl2ApplicationWindow::windowSize() const { + Vector2i size; + #ifndef CORRADE_TARGET_EMSCRIPTEN + CORRADE_ASSERT(_window, "Platform::Sdl2Application::windowSize(): no window opened", {}); + SDL_GetWindowSize(_window, &size.x(), &size.y()); + #else + CORRADE_ASSERT(_surface, "Platform::Sdl2Application::windowSize(): no window opened", {}); + emscripten_get_canvas_element_size("#canvas", &size.x(), &size.y()); + #endif + return size; +} + +#ifndef CORRADE_TARGET_EMSCRIPTEN +void Sdl2ApplicationWindow::setWindowSize(const Vector2i& size) { + CORRADE_ASSERT(_window, "Platform::Sdl2Application::setWindowSize(): no window opened", ); + + const Vector2i newSize = dpiScaling()*size; + SDL_SetWindowSize(_window, newSize.x(), newSize.y()); +} + +void Sdl2ApplicationWindow::setMinWindowSize(const Vector2i& size) { + CORRADE_ASSERT(_window, "Platform::Sdl2Application::setMinWindowSize(): no window opened", ); + + const Vector2i newSize = dpiScaling()*size; + SDL_SetWindowMinimumSize(_window, newSize.x(), newSize.y()); +} + +void Sdl2ApplicationWindow::setMaxWindowSize(const Vector2i& size) { + CORRADE_ASSERT(_window, "Platform::Sdl2Application::setMaxWindowSize(): no window opened", ); + + const Vector2i newSize = dpiScaling()*size; + SDL_SetWindowMaximumSize(_window, newSize.x(), newSize.y()); +} +#endif + +#ifdef MAGNUM_TARGET_GL +Vector2i Sdl2ApplicationWindow::framebufferSize() const { + Vector2i size; + #ifndef CORRADE_TARGET_EMSCRIPTEN + CORRADE_ASSERT(_window, "Platform::Sdl2Application::framebufferSize(): no window opened", {}); + SDL_GL_GetDrawableSize(_window, &size.x(), &size.y()); + #else + CORRADE_ASSERT(_surface, "Platform::Sdl2Application::framebufferSize(): no window opened", {}); + emscripten_get_canvas_element_size("#canvas", &size.x(), &size.y()); + #endif + return size; +} +#endif + +Vector2 Sdl2ApplicationWindow::dpiScaling() const { + return dpiScalingInternal(_application._configurationDpiScalingPolicy, _application._configurationDpiScaling); +} + +void Sdl2ApplicationWindow::redraw() { _windowFlags |= WindowFlag::Redraw; } + +namespace { + +#ifndef CORRADE_TARGET_EMSCRIPTEN +constexpr SDL_SystemCursor CursorMap[] { + SDL_SYSTEM_CURSOR_ARROW, + SDL_SYSTEM_CURSOR_IBEAM, + SDL_SYSTEM_CURSOR_WAIT, + SDL_SYSTEM_CURSOR_CROSSHAIR, + SDL_SYSTEM_CURSOR_WAITARROW, + SDL_SYSTEM_CURSOR_SIZENWSE, + SDL_SYSTEM_CURSOR_SIZENESW, + SDL_SYSTEM_CURSOR_SIZEWE, + SDL_SYSTEM_CURSOR_SIZENS, + SDL_SYSTEM_CURSOR_SIZEALL, + SDL_SYSTEM_CURSOR_NO, + SDL_SYSTEM_CURSOR_HAND +}; +#else +constexpr Containers::StringView CursorMap[] { + "default"_s, + "text"_s, + "wait"_s, + "crosshair"_s, + "progress"_s, + "nwse-resize"_s, + "nesw-resize"_s, + "ew-resize"_s, + "ns-resize"_s, + "move"_s, + "not-allowed"_s, + "pointer"_s, + "none"_s + /* Hidden & locked not supported yet */ +}; +#endif + +} + +void Sdl2ApplicationWindow::setCursor(Cursor cursor) { + #ifndef CORRADE_TARGET_EMSCRIPTEN + CORRADE_ASSERT(_window, "Platform::Sdl2ApplicationWindow::setCursor(): no window opened", ); + + if(cursor == Cursor::Hidden) { + SDL_ShowCursor(SDL_DISABLE); + SDL_SetWindowGrab(_window, SDL_FALSE); + SDL_SetRelativeMouseMode(SDL_FALSE); + return; + } else if(cursor == Cursor::HiddenLocked) { + SDL_SetWindowGrab(_window, SDL_TRUE); + SDL_SetRelativeMouseMode(SDL_TRUE); + return; + } else { + SDL_ShowCursor(SDL_ENABLE); + SDL_SetWindowGrab(_window, SDL_FALSE); + SDL_SetRelativeMouseMode(SDL_FALSE); + } + + /* The second condition could be a static assert but it doesn't let me + because "this pointer only accessible in a constexpr function". Thanks + for nothing, C++. */ + CORRADE_INTERNAL_ASSERT(UnsignedInt(cursor) < Containers::arraySize(_application._cursors) && Containers::arraySize(_application._cursors) == Containers::arraySize(CursorMap)); + + if(!_application._cursors[UnsignedInt(cursor)]) + _application._cursors[UnsignedInt(cursor)] = SDL_CreateSystemCursor(CursorMap[UnsignedInt(cursor)]); + + SDL_SetCursor(_application._cursors[UnsignedInt(cursor)]); + #else + CORRADE_ASSERT(_surface, "Platform::Sdl2Application::setCursor(): no window opened", ); + _cursor = cursor; + CORRADE_INTERNAL_ASSERT(UnsignedInt(cursor) < Containers::arraySize(CursorMap)); + magnumPlatformSetCursor(CursorMap[UnsignedInt(cursor)].data(), + CursorMap[UnsignedInt(cursor)].size()); + #endif +} + +Sdl2Application::Cursor Sdl2ApplicationWindow::cursor() { + #ifndef CORRADE_TARGET_EMSCRIPTEN + if(SDL_GetRelativeMouseMode()) + return Cursor::HiddenLocked; + else if(!SDL_ShowCursor(SDL_QUERY)) + return Cursor::Hidden; + + SDL_Cursor* cursor = SDL_GetCursor(); + + if(cursor) for(UnsignedInt i = 0; i < sizeof(_application._cursors); i++) + if(_application._cursors[i] == cursor) return Cursor(i); + + return Cursor::Arrow; + #else + return _cursor; + #endif +} + +void Sdl2ApplicationWindow::viewportEvent(ViewportEvent&) {} +void Sdl2ApplicationWindow::keyPressEvent(KeyEvent&) {} +void Sdl2ApplicationWindow::keyReleaseEvent(KeyEvent&) {} +void Sdl2ApplicationWindow::mousePressEvent(MouseEvent&) {} +void Sdl2ApplicationWindow::mouseReleaseEvent(MouseEvent&) {} +void Sdl2ApplicationWindow::mouseMoveEvent(MouseMoveEvent&) {} +void Sdl2ApplicationWindow::mouseScrollEvent(MouseScrollEvent&) {} +void Sdl2ApplicationWindow::multiGestureEvent(MultiGestureEvent&) {} +void Sdl2ApplicationWindow::textInputEvent(TextInputEvent&) {} +void Sdl2ApplicationWindow::textEditingEvent(TextEditingEvent&) {} + +namespace { + +/* + * Fix up the modifiers -- we want >= operator to work properly on Shift, + * Ctrl, Alt, but SDL generates different event for left / right keys, thus + * (modifiers >= Shift) would pass only if both left and right were pressed, + * which is usually not what the developers wants. + */ +Sdl2Application::InputEvent::Modifiers fixedModifiers(Uint16 mod) { + Sdl2Application::InputEvent::Modifiers modifiers(static_cast(mod)); + if(modifiers & Sdl2Application::InputEvent::Modifier::Shift) modifiers |= Sdl2Application::InputEvent::Modifier::Shift; + if(modifiers & Sdl2Application::InputEvent::Modifier::Ctrl) modifiers |= Sdl2Application::InputEvent::Modifier::Ctrl; + if(modifiers & Sdl2Application::InputEvent::Modifier::Alt) modifiers |= Sdl2Application::InputEvent::Modifier::Alt; + if(modifiers & Sdl2Application::InputEvent::Modifier::Super) modifiers |= Sdl2Application::InputEvent::Modifier::Super; + return modifiers; +} + +} + +enum class Sdl2Application::Flag: UnsignedByte { + VSyncEnabled = 1 << 0, + NoTickEvent = 1 << 1, + NoAnyEvent = 1 << 2, + Exit = 1 << 3, + #ifdef CORRADE_TARGET_APPLE + HiDpiWarningPrinted = 1 << 4 + #endif +}; + +Sdl2Application::Sdl2Application(const Arguments& arguments): Sdl2Application{arguments, Configuration{}} {} + +Sdl2Application::Sdl2Application(const Arguments& arguments, const Configuration& configuration): Sdl2Application{arguments, NoCreate} { + create(configuration); +} + +#ifdef MAGNUM_TARGET_GL +Sdl2Application::Sdl2Application(const Arguments& arguments, const Configuration& configuration, const GLConfiguration& glConfiguration): Sdl2Application{arguments, NoCreate} { + create(configuration, glConfiguration); +} +#endif + +Sdl2Application::Sdl2Application(const Arguments& arguments, NoCreateT): Sdl2ApplicationWindow{*this, NoCreate} + #ifndef CORRADE_TARGET_EMSCRIPTEN + , _minimalLoopPeriod{0} + #endif +{ + Utility::Arguments args{Implementation::windowScalingArguments()}; + #ifdef MAGNUM_TARGET_GL + _context.emplace(NoCreate, args, arguments.argc, arguments.argv); + #else + /** @todo this is duplicated here, in GlfwApplication and in + EmscriptenApplication, figure out a nice non-duplicated way to handle + this */ + args.addOption("log", "default").setHelp("log", "console logging", "default|quiet|verbose") + .setFromEnvironment("log") + .parse(arguments.argc, arguments.argv); + #endif + + /* Available since 2.0.4, disables interception of SIGINT and SIGTERM so + it's possible to Ctrl-C the application even if exitEvent() doesn't set + event.setAccepted(). */ + #ifdef SDL_HINT_NO_SIGNAL_HANDLERS + SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1"); + #endif + /* Available since 2.0.6, uses dedicated OpenGL ES drivers if EGL is used, + and desktop GLES context otherwise. */ + #if defined(MAGNUM_TARGET_GLES) && defined(MAGNUM_TARGET_EGL) && defined(SDL_HINT_OPENGL_ES_DRIVER) + SDL_SetHint(SDL_HINT_OPENGL_ES_DRIVER, "1"); + #endif + /* Available since 2.0.8, disables compositor bypass on X11, which causes + flickering on KWin as the compositor gets shut down on every startup */ + #ifdef SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR + SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"); + #endif + /* By default, SDL behaves like if it was playing a video or whatever, + preventing the computer from turning off the screen or going to sleep. + While it sorta makes sense for games, it's useless and annoying for + regular apps. Together with the compositor disabling those two are the + most stupid defaults. */ + #ifdef SDL_HINT_VIDEO_ALLOW_SCREENSAVER + SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1"); + #endif + /* Available since 2.0.12, use EGL if desired */ + #if defined(MAGNUM_TARGET_EGL) && defined(SDL_HINT_VIDEO_X11_FORCE_EGL) + SDL_SetHint(SDL_HINT_VIDEO_X11_FORCE_EGL, "1"); + #endif + + if(SDL_Init(SDL_INIT_VIDEO) < 0) { + Error() << "Cannot initialize SDL:" << SDL_GetError(); + std::exit(1); + } + + /* Save command-line arguments */ + if(args.value("log") == "verbose") _verboseLog = true; + const Containers::StringView dpiScaling = args.value("dpi-scaling"); + if(dpiScaling == "default"_s) + _commandLineDpiScalingPolicy = Implementation::Sdl2DpiScalingPolicy::Default; + #ifdef CORRADE_TARGET_APPLE + else if(dpiScaling == "framebuffer"_s) + _commandLineDpiScalingPolicy = Implementation::Sdl2DpiScalingPolicy::Framebuffer; + #endif + #ifndef CORRADE_TARGET_APPLE + #if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_ANDROID) + else if(dpiScaling == "virtual"_s) + _commandLineDpiScalingPolicy = Implementation::Sdl2DpiScalingPolicy::Virtual; + #endif + else if(dpiScaling == "physical"_s) + _commandLineDpiScalingPolicy = Implementation::Sdl2DpiScalingPolicy::Physical; + #endif + else if(dpiScaling.containsAny(" \t\n"_s)) + _commandLineDpiScaling = args.value("dpi-scaling"); + else + _commandLineDpiScaling = Vector2{args.value("dpi-scaling")}; +} + +void Sdl2Application::create() { + create(Configuration{}); +} - SDL_SetWindowIcon(_window, icon); - SDL_FreeSurface(icon); +void Sdl2Application::create(const Configuration& configuration) { + if(!tryCreate(configuration)) std::exit(1); +} + +#ifdef MAGNUM_TARGET_GL +void Sdl2Application::create(const Configuration& configuration, const GLConfiguration& glConfiguration) { + if(!tryCreate(configuration, glConfiguration)) std::exit(1); } #endif bool Sdl2Application::tryCreate(const Configuration& configuration) { #ifdef MAGNUM_TARGET_GL - if(!(configuration.windowFlags() & Configuration::WindowFlag::Contextless)) + if(!(configuration.flags() & Configuration::Flag::Contextless)) return tryCreate(configuration, GLConfiguration{}); #endif - #ifndef CORRADE_TARGET_EMSCRIPTEN - /* Save DPI scaling values from configuration for future use, scale window - based on those */ - _configurationDpiScalingPolicy = configuration.dpiScalingPolicy(); - _configurationDpiScaling = configuration.dpiScaling(); - const Vector2i scaledWindowSize = configuration.size()*dpiScaling(configuration); + /* Save the application-global configuration flags to be used to create all + windows */ + _configurationFlags = Uint32(configuration.flags()); - /* Create window */ - if(!(_window = SDL_CreateWindow( - #ifndef CORRADE_TARGET_IOS - configuration.title().data(), - #else - nullptr, - #endif - SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - scaledWindowSize.x(), scaledWindowSize.y(), - SDL_WINDOW_ALLOW_HIGHDPI|SDL_WINDOW_OPENGL|Uint32(configuration.windowFlags() & ~Configuration::WindowFlag::Contextless)))) - { - Error() << "Platform::Sdl2Application::tryCreate(): cannot create window:" << SDL_GetError(); + #ifndef CORRADE_TARGET_EMSCRIPTEN + /* Create the main window */ + if(!tryCreateWindow(configuration)) return false; - } + + /* Register the main window in the window list */ + CORRADE_INTERNAL_ASSERT(_windows.isEmpty()); + arrayAppend(_windows, this); /* Emscripten-specific initialization */ #else @@ -449,7 +678,7 @@ bool Sdl2Application::tryCreate(const Configuration& configuration) { based on those */ _configurationDpiScalingPolicy = configuration.dpiScalingPolicy(); _configurationDpiScaling = configuration.dpiScaling(); - const Vector2i scaledWindowSize = windowSize*dpiScaling(configuration); + const Vector2i scaledWindowSize = configuration.size()*dpiScaling(configuration); Uint32 flags = SDL_OPENGL|SDL_HWSURFACE|SDL_DOUBLEBUF; if(configuration.windowFlags() & Configuration::WindowFlag::Resizable) { @@ -470,7 +699,15 @@ bool Sdl2Application::tryCreate(const Configuration& configuration) { #ifdef MAGNUM_TARGET_GL bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConfiguration& glConfiguration) { - CORRADE_ASSERT(_context->version() == GL::Version::None, "Platform::Sdl2Application::tryCreate(): context already created", false); + CORRADE_ASSERT(!(configuration.flags() & Configuration::Flag::Contextless), + "Platform::Sdl2Application::tryCreate(): cannot pass Configuration::Flag::Contextless when creating an OpenGL context", false); + CORRADE_ASSERT(_context->version() == GL::Version::None, + "Platform::Sdl2Application::tryCreate(): context already created", false); + + /* Save the application-global configuration flags to be used to create all + windows. Since we're creating a GL context, request the window to also + be OpenGL-enabled. */ + _configurationFlags = Uint32(configuration.flags()|Configuration::Flag::OpenGL); /* Enable double buffering, set up buffer sizes */ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); @@ -491,12 +728,6 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf #endif #ifndef CORRADE_TARGET_EMSCRIPTEN - /* Save DPI scaling values from configuration for future use, scale window - based on those */ - _configurationDpiScalingPolicy = configuration.dpiScalingPolicy(); - _configurationDpiScaling = configuration.dpiScaling(); - const Vector2i scaledWindowSize = configuration.size()*dpiScaling(configuration); - /* Request debug context if GpuValidation is enabled either via the configuration or via command-line */ GLConfiguration::Flags glFlags = glConfiguration.flags(); @@ -558,23 +789,18 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf #endif } - /* Create window. Hide it by default so we don't have distracting window - blinking in case we have to destroy it again right away */ - if(!(_window = SDL_CreateWindow( - #ifndef CORRADE_TARGET_IOS - configuration.title().data(), - #else - nullptr, - #endif - SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - scaledWindowSize.x(), scaledWindowSize.y(), - SDL_WINDOW_OPENGL|SDL_WINDOW_HIDDEN|SDL_WINDOW_ALLOW_HIGHDPI|Uint32(configuration.windowFlags())))) - { - Error() << "Platform::Sdl2Application::tryCreate(): cannot create window:" << SDL_GetError(); + /* Hide the main window by default so we don't have distracting window + blinking in case the context creation fails due to unsupported + configuration or if it gets destroyed for fallback context creation + below. */ + Sdl2ApplicationWindow::Configuration hiddenWindowConfiguration{configuration}; + hiddenWindowConfiguration.addWindowFlags(Sdl2ApplicationWindow::Configuration::WindowFlag::Hidden); + + /* Create a window */ + if(!tryCreateWindow(hiddenWindowConfiguration)) return false; - } - /* Create context */ + /* Create a context */ _glContext = SDL_GL_CreateContext(_window); #ifndef MAGNUM_TARGET_GLES @@ -606,7 +832,17 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf << SDL_GetError() << "(falling back to compatibility context)"; else SDL_GL_DeleteContext(_glContext); - SDL_DestroyWindow(_window); + /* Destroy the original window. SDL_GL_SetAttribute() says it should be + called before creating a window, which kind of implies the + attributes affect how the window is created: + https://wiki.libsdl.org/SDL2/SDL_GL_SetAttribute + Which means, if I attempt to set them differently after the window + is created, it *might* not work correctly. It doesn't seem to matter + on Linux at least, but better stay on the safe side as this way + worked correctly for 10+ years on all platforms and reusing an + existing window might not. */ + destroyWindow(); + _window = nullptr; SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); @@ -618,14 +854,9 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf Also mask out the upper 32bits used for other flags. */ SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, int(UnsignedLong(glFlags & ~GLConfiguration::Flag::ForwardCompatible) & 0xffffffffu)); - if(!(_window = SDL_CreateWindow(configuration.title().data(), - SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - scaledWindowSize.x(), scaledWindowSize.y(), - SDL_WINDOW_OPENGL|SDL_WINDOW_HIDDEN|SDL_WINDOW_ALLOW_HIGHDPI|Uint32(configuration.windowFlags()&~Configuration::WindowFlag::Contextless)))) - { - Error() << "Platform::Sdl2Application::tryCreate(): cannot create window:" << SDL_GetError(); + /* Create a new window using the refreshed GL attributes */ + if(!tryCreateWindow(hiddenWindowConfiguration)) return false; - } /* Create compatibility context */ _glContext = SDL_GL_CreateContext(_window); @@ -635,7 +866,7 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf /* Cannot create context (or fallback compatibility context on desktop) */ if(!_glContext) { Error() << "Platform::Sdl2Application::tryCreate(): cannot create context:" << SDL_GetError(); - SDL_DestroyWindow(_window); + destroyWindow(); _window = nullptr; return false; } @@ -681,7 +912,7 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf based on those */ _configurationDpiScalingPolicy = configuration.dpiScalingPolicy(); _configurationDpiScaling = configuration.dpiScaling(); - const Vector2i scaledWindowSize = windowSize*dpiScaling(); + const Vector2i scaledWindowSize = configuration.size()*dpiScaling(configuration); Uint32 flags = SDL_OPENGL|SDL_HWSURFACE|SDL_DOUBLEBUF; if(configuration.windowFlags() & Configuration::WindowFlag::Resizable) { @@ -701,7 +932,7 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf if(!_context->tryCreate(glConfiguration)) { #ifndef CORRADE_TARGET_EMSCRIPTEN SDL_GL_DeleteContext(_glContext); - SDL_DestroyWindow(_window); + destroyWindow(); _window = nullptr; #else SDL_FreeSurface(_surface); @@ -720,66 +951,13 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf } #endif -Vector2i Sdl2Application::windowSize() const { - Vector2i size; - #ifndef CORRADE_TARGET_EMSCRIPTEN - CORRADE_ASSERT(_window, "Platform::Sdl2Application::windowSize(): no window opened", {}); - SDL_GetWindowSize(_window, &size.x(), &size.y()); - #else - CORRADE_ASSERT(_surface, "Platform::Sdl2Application::windowSize(): no window opened", {}); - emscripten_get_canvas_element_size("#canvas", &size.x(), &size.y()); - #endif - return size; -} - -#ifndef CORRADE_TARGET_EMSCRIPTEN -void Sdl2Application::setWindowSize(const Vector2i& size) { - CORRADE_ASSERT(_window, "Platform::Sdl2Application::setWindowSize(): no window opened", ); - - const Vector2i newSize = dpiScaling()*size; - SDL_SetWindowSize(_window, newSize.x(), newSize.y()); -} - -void Sdl2Application::setMinWindowSize(const Vector2i& size) { - CORRADE_ASSERT(_window, "Platform::Sdl2Application::setMinWindowSize(): no window opened", ); - - const Vector2i newSize = dpiScaling()*size; - SDL_SetWindowMinimumSize(_window, newSize.x(), newSize.y()); -} - -void Sdl2Application::setMaxWindowSize(const Vector2i& size) { - CORRADE_ASSERT(_window, "Platform::Sdl2Application::setMaxWindowSize(): no window opened", ); - - const Vector2i newSize = dpiScaling()*size; - SDL_SetWindowMaximumSize(_window, newSize.x(), newSize.y()); -} -#endif - -#ifdef MAGNUM_TARGET_GL -Vector2i Sdl2Application::framebufferSize() const { - Vector2i size; - #ifndef CORRADE_TARGET_EMSCRIPTEN - CORRADE_ASSERT(_window, "Platform::Sdl2Application::framebufferSize(): no window opened", {}); - SDL_GL_GetDrawableSize(_window, &size.x(), &size.y()); - #else - CORRADE_ASSERT(_surface, "Platform::Sdl2Application::framebufferSize(): no window opened", {}); - emscripten_get_canvas_element_size("#canvas", &size.x(), &size.y()); - #endif - return size; -} -#endif - -Vector2 Sdl2Application::dpiScaling() const { - return dpiScalingInternal(_configurationDpiScalingPolicy, _configurationDpiScaling); -} - #ifdef CORRADE_TARGET_EMSCRIPTEN void Sdl2Application::setContainerCssClass(const Containers::StringView cssClass) { magnumPlatformSetContainerCssClass(cssClass.data(), cssClass.size()); } #endif -void Sdl2Application::swapBuffers() { +void Sdl2ApplicationWindow::swapBuffers() { #ifndef CORRADE_TARGET_EMSCRIPTEN SDL_GL_SwapWindow(_window); #else @@ -812,13 +990,21 @@ bool Sdl2Application::setSwapInterval(const Int interval) { return true; } -void Sdl2Application::redraw() { _flags |= Flag::Redraw; } - Sdl2Application::~Sdl2Application() { + #ifndef CORRADE_TARGET_EMSCRIPTEN + /* Destroy the main SDL window first. After that, there should be no + remaining registered windows. */ + if(_window) + destroyWindow(); + for(Sdl2ApplicationWindow* i: _windows) + CORRADE_ASSERT(!i, "Sdl2Application: destructed with windows still open", ); + #else /* SDL_DestroyWindow(_window) crashes on windows when _window is nullptr (it doesn't seem to crash on Linux). Because this seems to be yet another pointless platform difference, to be safe do the same check with - all. */ + all APIs. */ + if(_window) SDL_DestroyWindow(_window); + #endif #ifdef MAGNUM_TARGET_GL /* Destroy Magnum context first to avoid it potentially accessing the @@ -837,9 +1023,6 @@ Sdl2Application::~Sdl2Application() { SDL_FreeCursor(cursor); #endif - #ifndef CORRADE_TARGET_EMSCRIPTEN - if(_window) SDL_DestroyWindow(_window); - #endif SDL_Quit(); } @@ -864,6 +1047,28 @@ void Sdl2Application::exit(const int exitCode) { _exitCode = exitCode; } +#ifndef CORRADE_TARGET_EMSCRIPTEN +void Sdl2Application::makeContextCurrent(Sdl2ApplicationWindow& window) { + /* Only do it if it is not active already */ + if(_activeGlContextWindow != window._window) { + SDL_GL_MakeCurrent(window._window, _glContext); + _activeGlContextWindow = window._window; +#warning TODO + // Context::current().resetState(Context::State::WindowSpecific); + } +} +#endif + +template inline void Sdl2Application::callEventHandler(std::size_t windowId, void(Sdl2ApplicationWindow::*eventHandler)(Args...), Args&&... args) { + // if(!(windowId < _windows.size() && _windows[windowId])) { + // Debug() << "HUH" << windowId << _windows.size(); + // return; + // } + + CORRADE_INTERNAL_ASSERT(windowId < _windows.size() && _windows[windowId]); + (_windows[windowId]->*eventHandler)(std::forward(args)...); +} + bool Sdl2Application::mainLoopIteration() { /* If exit was requested directly in the constructor, exit immediately without calling anything else */ @@ -909,6 +1114,7 @@ bool Sdl2Application::mainLoopIteration() { while(SDL_PollEvent(&event)) { switch(event.type) { case SDL_WINDOWEVENT: + CORRADE_INTERNAL_ASSERT(event.window.windowID < _windows.size() && _windows[event.window.windowID]); switch(event.window.event) { /* Not using SDL_WINDOWEVENT_RESIZED, because that doesn't get fired when the window is resized programmatically @@ -930,15 +1136,18 @@ bool Sdl2Application::mainLoopIteration() { #endif dpiScaling()}; /** @todo handle also WM_DPICHANGED events when a window is moved between displays with different DPI */ - viewportEvent(e); - _flags |= Flag::Redraw; +#warning make context current here? probably?? + makeContextCurrent(*_windows[event.window.windowID]); + callEventHandler(event.window.windowID, &Sdl2ApplicationWindow::viewportEvent, e); + _windows[event.window.windowID]->_windowFlags |= WindowFlag::Redraw; #endif } break; +#warning SDL_WINDOWEVENT_CLOSE /* Direct everything that wasn't exposed via a callback to anyEvent(), so users can implement event handling for things not present in the Application APIs */ case SDL_WINDOWEVENT_EXPOSED: - _flags |= Flag::Redraw; + _windows[event.window.windowID]->_windowFlags |= WindowFlag::Redraw; if(!(_flags & Flag::NoAnyEvent)) anyEvent(event); break; default: @@ -948,7 +1157,9 @@ bool Sdl2Application::mainLoopIteration() { case SDL_KEYDOWN: case SDL_KEYUP: { KeyEvent e{event, static_cast(event.key.keysym.sym), fixedModifiers(event.key.keysym.mod), event.key.repeat != 0}; - event.type == SDL_KEYDOWN ? keyPressEvent(e) : keyReleaseEvent(e); + callEventHandler(event.key.windowID, + event.type == SDL_KEYDOWN ? &Sdl2ApplicationWindow::keyPressEvent : &Sdl2ApplicationWindow::keyReleaseEvent, + e); } break; case SDL_MOUSEBUTTONDOWN: @@ -958,34 +1169,37 @@ bool Sdl2Application::mainLoopIteration() { , event.button.clicks #endif }; - event.type == SDL_MOUSEBUTTONDOWN ? mousePressEvent(e) : mouseReleaseEvent(e); + callEventHandler(event.key.windowID, + event.type == SDL_MOUSEBUTTONDOWN ? &Sdl2ApplicationWindow::mousePressEvent : &Sdl2ApplicationWindow::mouseReleaseEvent, + e); } break; case SDL_MOUSEWHEEL: { MouseScrollEvent e{event, {Float(event.wheel.x), Float(event.wheel.y)}}; - mouseScrollEvent(e); + callEventHandler(event.wheel.windowID, &Sdl2ApplicationWindow::mouseScrollEvent, e); } break; case SDL_MOUSEMOTION: { MouseMoveEvent e{event, {event.motion.x, event.motion.y}, {event.motion.xrel, event.motion.yrel}, static_cast(event.motion.state)}; - mouseMoveEvent(e); + callEventHandler(event.motion.windowID, &Sdl2ApplicationWindow::mouseMoveEvent, e); break; } case SDL_MULTIGESTURE: { MultiGestureEvent e{event, {event.mgesture.x, event.mgesture.y}, event.mgesture.dTheta, event.mgesture.dDist, event.mgesture.numFingers}; +#warning wtf, why no window ID?! multiGestureEvent(e); break; } case SDL_TEXTINPUT: { TextInputEvent e{event, event.text.text}; - textInputEvent(e); + callEventHandler(event.text.windowID, &Sdl2ApplicationWindow::textInputEvent, e); } break; case SDL_TEXTEDITING: { TextEditingEvent e{event, event.edit.text, event.edit.start, event.edit.length}; - textEditingEvent(e); + callEventHandler(event.edit.windowID, &Sdl2ApplicationWindow::textEditingEvent, e); } break; case SDL_QUIT: { @@ -1011,11 +1225,18 @@ bool Sdl2Application::mainLoopIteration() { /* Tick event */ if(!(_flags & Flag::NoTickEvent)) tickEvent(); - /* Draw event */ - if(_flags & Flag::Redraw) { - _flags &= ~Flag::Redraw; - drawEvent(); + /* Draw events */ + bool somethingDrawn = false; + for(std::size_t i = 0; i != _windows.size(); ++i) { + if(!_windows[i] || !(_windows[i]->_windowFlags & WindowFlag::Redraw)) continue; + + _windows[i]->_windowFlags &= ~WindowFlag::Redraw; + callEventHandler(i, + &Sdl2ApplicationWindow::drawEvent); + somethingDrawn = true; + } + if(somethingDrawn) { #ifndef CORRADE_TARGET_EMSCRIPTEN /* If VSync is not enabled, delay to prevent CPU hogging (if set) */ if(!(_flags & Flag::VSyncEnabled) && _minimalLoopPeriod) { @@ -1043,99 +1264,6 @@ bool Sdl2Application::mainLoopIteration() { return !(_flags & Flag::Exit); } -namespace { - -#ifndef CORRADE_TARGET_EMSCRIPTEN -constexpr SDL_SystemCursor CursorMap[] { - SDL_SYSTEM_CURSOR_ARROW, - SDL_SYSTEM_CURSOR_IBEAM, - SDL_SYSTEM_CURSOR_WAIT, - SDL_SYSTEM_CURSOR_CROSSHAIR, - SDL_SYSTEM_CURSOR_WAITARROW, - SDL_SYSTEM_CURSOR_SIZENWSE, - SDL_SYSTEM_CURSOR_SIZENESW, - SDL_SYSTEM_CURSOR_SIZEWE, - SDL_SYSTEM_CURSOR_SIZENS, - SDL_SYSTEM_CURSOR_SIZEALL, - SDL_SYSTEM_CURSOR_NO, - SDL_SYSTEM_CURSOR_HAND -}; -#else -constexpr Containers::StringView CursorMap[] { - "default"_s, - "text"_s, - "wait"_s, - "crosshair"_s, - "progress"_s, - "nwse-resize"_s, - "nesw-resize"_s, - "ew-resize"_s, - "ns-resize"_s, - "move"_s, - "not-allowed"_s, - "pointer"_s, - "none"_s - /* Hidden & locked not supported yet */ -}; -#endif - -} - -void Sdl2Application::setCursor(Cursor cursor) { - #ifndef CORRADE_TARGET_EMSCRIPTEN - CORRADE_ASSERT(_window, "Platform::Sdl2Application::setCursor(): no window opened", ); - - if(cursor == Cursor::Hidden) { - SDL_ShowCursor(SDL_DISABLE); - SDL_SetWindowGrab(_window, SDL_FALSE); - SDL_SetRelativeMouseMode(SDL_FALSE); - return; - } else if(cursor == Cursor::HiddenLocked) { - SDL_SetWindowGrab(_window, SDL_TRUE); - SDL_SetRelativeMouseMode(SDL_TRUE); - return; - } else { - SDL_ShowCursor(SDL_ENABLE); - SDL_SetWindowGrab(_window, SDL_FALSE); - SDL_SetRelativeMouseMode(SDL_FALSE); - } - - /* The second condition could be a static assert but it doesn't let me - because "this pointer only accessible in a constexpr function". Thanks - for nothing, C++. */ - CORRADE_INTERNAL_ASSERT(UnsignedInt(cursor) < Containers::arraySize(_cursors) && Containers::arraySize(_cursors) == Containers::arraySize(CursorMap)); - - if(!_cursors[UnsignedInt(cursor)]) - _cursors[UnsignedInt(cursor)] = SDL_CreateSystemCursor(CursorMap[UnsignedInt(cursor)]); - - SDL_SetCursor(_cursors[UnsignedInt(cursor)]); - #else - CORRADE_ASSERT(_surface, "Platform::Sdl2Application::setCursor(): no window opened", ); - _cursor = cursor; - CORRADE_INTERNAL_ASSERT(UnsignedInt(cursor) < Containers::arraySize(CursorMap)); - magnumPlatformSetCursor(CursorMap[UnsignedInt(cursor)].data(), - CursorMap[UnsignedInt(cursor)].size()); - #endif -} - -Sdl2Application::Cursor Sdl2Application::cursor() { - #ifndef CORRADE_TARGET_EMSCRIPTEN - if(SDL_GetRelativeMouseMode()) - return Cursor::HiddenLocked; - else if(!SDL_ShowCursor(SDL_QUERY)) - return Cursor::Hidden; - - SDL_Cursor* cursor = SDL_GetCursor(); - - if(cursor) for(UnsignedInt i = 0; i < sizeof(_cursors); i++) - if(_cursors[i] == cursor) return Cursor(i); - - return Cursor::Arrow; - #else - return _cursor; - #endif -} - #ifdef MAGNUM_BUILD_DEPRECATED void Sdl2Application::setMouseLocked(bool enabled) { /** @todo Implement this in Emscripten */ @@ -1192,34 +1320,7 @@ void Sdl2Application::anyEvent(SDL_Event&) { _flags |= Flag::NoAnyEvent; } -void Sdl2Application::viewportEvent(ViewportEvent&) {} -void Sdl2Application::keyPressEvent(KeyEvent&) {} -void Sdl2Application::keyReleaseEvent(KeyEvent&) {} -void Sdl2Application::mousePressEvent(MouseEvent&) {} -void Sdl2Application::mouseReleaseEvent(MouseEvent&) {} -void Sdl2Application::mouseMoveEvent(MouseMoveEvent&) {} -void Sdl2Application::mouseScrollEvent(MouseScrollEvent&) {} -void Sdl2Application::multiGestureEvent(MultiGestureEvent&) {} -void Sdl2Application::textInputEvent(TextInputEvent&) {} -void Sdl2Application::textEditingEvent(TextEditingEvent&) {} - -#ifdef MAGNUM_TARGET_GL -Sdl2Application::GLConfiguration::GLConfiguration(): - _colorBufferSize{8, 8, 8, 8}, _depthBufferSize{24}, _stencilBufferSize{0}, - _sampleCount(0) - #ifndef CORRADE_TARGET_EMSCRIPTEN - , _version{GL::Version::None}, _srgbCapable{false} - #endif -{ - #ifndef MAGNUM_TARGET_GLES - addFlags(Flag::ForwardCompatible); - #endif -} - -Sdl2Application::GLConfiguration::~GLConfiguration() = default; -#endif - -Sdl2Application::Configuration::Configuration(): +Sdl2ApplicationWindow::Configuration::Configuration(): #if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_IOS) _title(Containers::String::nullTerminatedGlobalView("Magnum SDL2 Application"_s)), #endif @@ -1230,38 +1331,105 @@ Sdl2Application::Configuration::Configuration(): #endif _dpiScalingPolicy{DpiScalingPolicy::Default} {} -Sdl2Application::Configuration::~Configuration() = default; +Sdl2ApplicationWindow::Configuration::~Configuration() = default; + +#ifdef MAGNUM_BUILD_DEPRECATED +namespace { + CORRADE_IGNORE_DEPRECATED_PUSH + constexpr Sdl2ApplicationWindow::Configuration::WindowFlags DeprecatedWindowFlags = Sdl2ApplicationWindow::Configuration::WindowFlag::Contextless|Sdl2ApplicationWindow::Configuration::WindowFlag::OpenGL|Sdl2ApplicationWindow::Configuration::WindowFlag::Vulkan; + CORRADE_IGNORE_DEPRECATED_POP +} +#endif + +Sdl2Application::Configuration& Sdl2Application::Configuration::setWindowFlags(WindowFlags flags) { + /* If deprecated flags are present, reset these bits in the application + flags instead and remove them from the to-be-set window flags */ + #ifdef MAGNUM_BUILD_DEPRECATED + if(const WindowFlags deprecatedFlags = flags & DeprecatedWindowFlags) { + _flags &= ~Flags(Uint32(DeprecatedWindowFlags)); + _flags |= Flags(Uint32(deprecatedFlags)); + flags &= ~deprecatedFlags; + } + #endif + + Sdl2ApplicationWindow::Configuration::setWindowFlags(flags); + return *this; +} + +Sdl2Application::Configuration& Sdl2Application::Configuration::addWindowFlags(WindowFlags flags) { + /* If deprecated flags are present, add these bits in the application + flags instead and remove them from the to-be-added window flags */ + #ifdef MAGNUM_BUILD_DEPRECATED + if(const WindowFlags deprecatedFlags = flags & DeprecatedWindowFlags) { + _flags |= Flags(Uint32(deprecatedFlags)); + flags &= ~deprecatedFlags; + } + #endif + + Sdl2ApplicationWindow::Configuration::addWindowFlags(flags); + return *this; +} + +Sdl2Application::Configuration& Sdl2Application::Configuration::clearWindowFlags(WindowFlags flags) { + /* If deprecated flags are present, add these bits in the application + flags instead and remove them from the to-be-cleared window flags */ + #ifdef MAGNUM_BUILD_DEPRECATED + if(const WindowFlags deprecatedFlags = flags & DeprecatedWindowFlags) { + _flags &= ~Flags(Uint32(deprecatedFlags)); + flags &= ~deprecatedFlags; + } + #endif + + Sdl2ApplicationWindow::Configuration::clearWindowFlags(flags); + return *this; +} -Containers::StringView Sdl2Application::KeyEvent::keyName(const Key key) { +Containers::StringView Sdl2ApplicationWindow::KeyEvent::keyName(const Key key) { return SDL_GetKeyName(SDL_Keycode(key)); } -Containers::StringView Sdl2Application::KeyEvent::keyName() const { +Containers::StringView Sdl2ApplicationWindow::KeyEvent::keyName() const { return keyName(_key); } -Sdl2Application::InputEvent::Modifiers Sdl2Application::MouseEvent::modifiers() { +Sdl2ApplicationWindow::InputEvent::Modifiers Sdl2ApplicationWindow::MouseEvent::modifiers() { if(_modifiers) return *_modifiers; return *(_modifiers = fixedModifiers(Uint16(SDL_GetModState()))); } -Sdl2Application::InputEvent::Modifiers Sdl2Application::MouseMoveEvent::modifiers() { +Sdl2ApplicationWindow::InputEvent::Modifiers Sdl2ApplicationWindow::MouseMoveEvent::modifiers() { if(_modifiers) return *_modifiers; return *(_modifiers = fixedModifiers(Uint16(SDL_GetModState()))); } -Vector2i Sdl2Application::MouseScrollEvent::position() { +Vector2i Sdl2ApplicationWindow::MouseScrollEvent::position() { if(_position) return *_position; _position = Vector2i{}; SDL_GetMouseState(&_position->x(), &_position->y()); return *_position; } -Sdl2Application::InputEvent::Modifiers Sdl2Application::MouseScrollEvent::modifiers() { +Sdl2ApplicationWindow::InputEvent::Modifiers Sdl2ApplicationWindow::MouseScrollEvent::modifiers() { if(_modifiers) return *_modifiers; return *(_modifiers = fixedModifiers(Uint16(SDL_GetModState()))); } +#ifdef MAGNUM_TARGET_GL +Sdl2Application::GLConfiguration::GLConfiguration(): + _colorBufferSize{8, 8, 8, 8}, _depthBufferSize{24}, _stencilBufferSize{0}, + _sampleCount(0) + #ifndef CORRADE_TARGET_EMSCRIPTEN + , _version{GL::Version::None}, _srgbCapable{false} + #endif +{ + #ifndef MAGNUM_TARGET_GLES + addFlags(Flag::ForwardCompatible); + #endif +} + +Sdl2Application::GLConfiguration::~GLConfiguration() = default; +#endif + template class BasicScreen; template class BasicScreenedApplication; diff --git a/src/Magnum/Platform/Sdl2Application.h b/src/Magnum/Platform/Sdl2Application.h index bc1a95c61d..cc7b681dcd 100644 --- a/src/Magnum/Platform/Sdl2Application.h +++ b/src/Magnum/Platform/Sdl2Application.h @@ -95,1006 +95,1124 @@ namespace Implementation { enum class Sdl2DpiScalingPolicy: UnsignedByte; } -/** @nosubgrouping -@brief SDL2 application +class Sdl2Application; -@m_keywords{Application} +/** +@brief SDL2 application window +@m_since_latest -Application using [Simple DirectMedia Layer](http://www.libsdl.org/) toolkit. -Supports keyboard and mouse handling. This application library is available for -all platforms for which SDL2 is ported except Android (thus also -@ref CORRADE_TARGET_EMSCRIPTEN "Emscripten", see -respective sections in @ref building-corrade-cross-emscripten "Corrade's" and -@ref building-cross-emscripten "Magnum's" building documentation). +@ref TODOTODO document -@m_class{m-block m-success} +See @ref Sdl2Application for more information. +*/ +class Sdl2ApplicationWindow { + public: + class Configuration; + class ViewportEvent; + class InputEvent; + class KeyEvent; + class MouseEvent; + class MouseMoveEvent; + class MouseScrollEvent; + class MultiGestureEvent; + class TextInputEvent; + class TextEditingEvent; -@thirdparty This plugin makes use of the [SDL2](https://www.libsdl.org/) - library, released under the @m_class{m-label m-success} **zlib license** - ([license text](http://www.gzip.org/zlib/zlib_license.html), - [choosealicense.com](https://choosealicense.com/licenses/zlib/)). - Attribution is appreciated but not required. + /** + * @brief Constructor + * + * @ref TODOTODO document + */ + #ifdef DOXYGEN_GENERATING_OUTPUT + explicit Sdl2ApplicationWindow(Sdl2Application& application, const Configuration& configuration = Configuration{}); + #else + /* Configuration is only forward-declared at this point */ + explicit Sdl2ApplicationWindow(Sdl2Application& application, const Configuration& configuration); + explicit Sdl2ApplicationWindow(Sdl2Application& application); + #endif -@section Platform-Sdl2Application-bootstrap Bootstrap application + /** @brief Copying is not allowed */ + Sdl2ApplicationWindow(const Sdl2ApplicationWindow&) = delete; -Fully contained base application using @ref Sdl2Application along with -CMake setup is available in `base` branch of -[Magnum Bootstrap](https://github.com/mosra/magnum-bootstrap) repository, -download it as [tar.gz](https://github.com/mosra/magnum-bootstrap/archive/base.tar.gz) -or [zip](https://github.com/mosra/magnum-bootstrap/archive/base.zip) file. -After extracting the downloaded archive you can build and run the application -with these four commands: + /** @brief Moving is not allowed */ + Sdl2ApplicationWindow(Sdl2ApplicationWindow&&) = delete; -@code{.sh} -mkdir build && cd build -cmake .. -cmake --build . -./src/MyApplication # or ./src/Debug/MyApplication -@endcode + /** @brief Copying is not allowed */ + Sdl2ApplicationWindow& operator=(const Sdl2ApplicationWindow&) = delete; -See @ref cmake for more information. + /** @brief Moving is not allowed */ + Sdl2ApplicationWindow& operator=(Sdl2ApplicationWindow&&) = delete; -@section Platform-Sdl2Application-bootstrap-emscripten Bootstrap application for Emscripten + /* Compared to the top-level application which is never deleted through + a pointer, standalone windows most likely will, so the destructor is + virtual */ + virtual ~Sdl2ApplicationWindow(); -The dedicated application implementation for Emscripten is -@ref EmscriptenApplication, which also provides a bootstrap project along with -full HTML markup and CMake setup. @ref Sdl2Application however supports -Emscripten as well --- set up the bootstrap application as -@ref Platform-EmscriptenApplication-bootstrap "described in the EmscriptenApplication docs" -and then change `src/CMakeLists.txt` and the @cpp #include @ce to use -@ref Sdl2Application for both the native and the web build. + /** @brief Application instance */ + Sdl2Application& application() { return _application; } + const Sdl2Application& application() const { return _application; } /**< @overload */ -@section Platform-Sdl2Application-bootstrap-ios Bootstrap application for iOS + #ifndef CORRADE_TARGET_EMSCRIPTEN + /** + * @brief Underlying window handle + * + * Use in case you need to call SDL functionality directly. Returns + * @cpp nullptr @ce in case the window was not created yet. + * @note Not available in @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". + */ + SDL_Window* window() { return _window; } + #endif -Fully contained base application using @ref Sdl2Application for both desktop -and iOS build along with pre-filled `*.plist` is available in `base-ios` branch -of [Magnum Bootstrap](https://github.com/mosra/magnum-bootstrap) repository, -download it as [tar.gz](https://github.com/mosra/magnum-bootstrap/archive/base-ios.tar.gz) -or [zip](https://github.com/mosra/magnum-bootstrap/archive/base-ios.zip) file. -After extracting the downloaded archive, you can do the desktop build in -the same way as above. For the iOS build you also need to put the contents of -toolchains repository from https://github.com/mosra/toolchains in `toolchains/` -subdirectory. + /** @{ @name Window handling */ -Then create build directory and run `cmake` to generate the Xcode project. Set -`CMAKE_OSX_ROOT` to SDK you want to target and enable all desired architectures -in `CMAKE_OSX_ARCHITECTURES`. Set `CMAKE_PREFIX_PATH` to the directory where -you have all the dependencies. + /** + * @brief Window size + * + * Window size to which all input event coordinates can be related. + * Note that, especially on HiDPI systems, it may be different from + * @ref framebufferSize(). Expects that a window is already created. + * See @ref Platform-Sdl2Application-dpi for more information. + * @see @ref dpiScaling() + */ + Vector2i windowSize() const; -@code{.sh} -mkdir build-ios && cd build-ios -cmake .. \ - -DCMAKE_TOOLCHAIN_FILE=path/to/toolchains/generic/iOS.cmake \ - -DCMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk \ - -DCMAKE_OSX_ARCHITECTURES="arm64;armv7;armv7s" \ - -DCMAKE_PREFIX_PATH=~/ios-libs \ - -G Xcode -@endcode + #if !defined(CORRADE_TARGET_EMSCRIPTEN) || defined(DOXYGEN_GENERATING_OUTPUT) + /** + * @brief Set window size + * @param size The size, in screen coordinates + * @m_since{2020,06} + * + * To make the sizing work independently of the display DPI, @p size is + * internally multiplied with @ref dpiScaling() before getting applied. + * Expects that a window is already created. + * @note Not available in @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". + * @see @ref setMinWindowSize(), @ref setMaxWindowSize() + */ + void setWindowSize(const Vector2i& size); -You can then open the generated project file in Xcode and build/deploy it from -there. + /** + * @brief Set minimum window size + * @param size The minimum size, in screen coordinates + * @m_since{2019,10} + * + * Note that, unlike in @ref GlfwApplication, SDL2 doesn't have a way + * to disable/remove a size limit. To make the sizing work + * independently of the display DPI, @p size is internally multiplied + * with @ref dpiScaling() before getting applied. Expects that a window + * is already created. + * @note Not available in @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". + * @see @ref setMaxWindowSize(), @ref setWindowSize() + */ + void setMinWindowSize(const Vector2i& size); -@section Platform-Sdl2Application-bootstrap-winrt Bootstrap application for Windows RT + /** + * @brief Set maximal window size + * @param size The maximum size, in screen coordinates + * @m_since{2019,10} + * + * Note that, unlike in @ref GlfwApplication, SDL2 doesn't have a way + * to disable/remove a size limit. To make the sizing work + * independently of the display DPI, @p size is internally multiplied + * with @ref dpiScaling() before getting applied. Expects that a window + * is already created. + * @note Not available in @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". + * @see @ref setMinWindowSize(), @ref setMaxWindowSize() + */ + void setMaxWindowSize(const Vector2i& size); + #endif -Fully contained base application using @ref Sdl2Application for both desktop -and Windows Phone / Windows Store build along with all required plumbing is -available in `base-winrt` branch of [Magnum Bootstrap](https://github.com/mosra/magnum-bootstrap) -repository, download it as [zip](https://github.com/mosra/magnum-bootstrap/archive/base-winrt.zip) -file. After extracting the downloaded archive, you can do the desktop build in -the same way as above. + #if defined(MAGNUM_TARGET_GL) || defined(DOXYGEN_GENERATING_OUTPUT) + /** + * @brief Framebuffer size + * + * Size of the default framebuffer. Note that, especially on HiDPI + * systems, it may be different from @ref windowSize(). Expects that a + * window is already created. See @ref Platform-Sdl2Application-dpi for + * more information. + * + * @note This function is available only if Magnum is compiled with + * @ref MAGNUM_TARGET_GL enabled (done by default). See + * @ref building-features for more information. + * + * @see @ref Sdl2Application::framebufferSize(), @ref dpiScaling() + */ + Vector2i framebufferSize() const; + #endif -For the Windows RT build you need to provide [your own *.pfx certificate file](https://msdn.microsoft.com/en-us/library/windows/desktop/jj835832.aspx) and -pass it to CMake in a `SIGNING_CERTIFICATE` variable. The bootstrap application -assumes that SDL2 and ANGLE is built as DLL and both Corrade and Magnum are -built statically. Assuming the native Corrade installation is in `C:/Sys` and -all WinRT dependencies are in `C:/Sys-winrt`, the build can be done similarly -to the following: + /** + * @brief DPI scaling + * + * How the content should be scaled relative to system defaults for + * given @ref windowSize(). If a window is not created yet, returns + * zero vector, use @ref dpiScaling(const Configuration&) for + * calculating a value independently. See @ref Platform-Sdl2Application-dpi + * for more information. + * @see @ref framebufferSize() + */ + Vector2 dpiScaling() const; -@code{.bat} -mkdir build-winrt && cd build-winrt -cmake .. ^ - -DCORRADE_RC_EXECUTABLE="C:/Sys/bin/corrade-rc.exe" ^ - -DCMAKE_PREFIX_PATH="C:/Sys-winrt" ^ - -DCMAKE_SYSTEM_NAME=WindowsStore ^ - -DCMAKE_SYSTEM_VERSION=8.1 ^ - -G "Visual Studio 14 2015" ^ - -DSIGNING_CERTIFICATE= -cmake --build . -@endcode + /** + * @brief DPI scaling for given configuration + * + * Calculates DPI scaling that would be used when creating a window + * with given @p configuration. Takes into account DPI scaling policy + * and custom scaling specified on the command-line. See + * @ref Platform-Sdl2Application-dpi for more information. + */ + Vector2 dpiScaling(const Configuration& configuration); -Change `WindowsStore` to `WindowsPhone` if you want to build for Windows Phone -instead. The `build-winrt/src/AppPackages` directory will then contain the -final package along with a PowerShell script for easy local installation. + /** + * @brief Set window title + * @m_since{2019,10} + * + * The @p title is expected to be encoded in UTF-8. + */ + void setWindowTitle(Containers::StringView title); -@section Platform-Sdl2Application-usage General usage + #if !defined(CORRADE_TARGET_EMSCRIPTEN) && (SDL_MAJOR_VERSION*1000 + SDL_MINOR_VERSION*100 + SDL_PATCHLEVEL >= 2005 || defined(DOXYGEN_GENERATING_OUTPUT)) + /** + * @brief Set window icon + * @m_since{2020,06} + * + * The @p image is expected to be with origin at bottom left (which is + * the default for imported images) and in one of + * @ref PixelFormat::RGB8Unorm, @ref PixelFormat::RGB8Srgb, + * @ref PixelFormat::RGBA8Unorm or @ref PixelFormat::RGBA8Srgb formats. + * Unlike @ref GlfwApplication::setWindowIcon(), SDL doesn't provide a + * way to supply multiple images in different sizes. + * @note Available since SDL 2.0.5. Not available on + * @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten", use + * @cb{.html} @ce in your HTML markup instead. + * Although it's not documented in SDL itself, the function might + * have no effect on macOS / Wayland, similarly to how + * @ref GlfwApplication::setWindowIcon() behaves on those + * platforms. + * @see @ref platform-windows-icon "Excecutable icon on Windows", + * @ref Trade::IcoImporter "IcoImporter" + */ + void setWindowIcon(const ImageView2D& image); + #endif -This application library depends on the [SDL2](http://www.libsdl.org) library -(Emscripten has it built in) and is built if `MAGNUM_WITH_SDL2APPLICATION` is -enabled when building Magnum. To use this library with CMake, put -[FindSDL2.cmake](https://github.com/mosra/magnum/blob/master/modules/FindSDL2.cmake) -into your `modules/` directory, request the `Sdl2Application` component of -the `Magnum` package and link to the `Magnum::Sdl2Application` target: + /** + * @brief Swap buffers + * + * Paints currently rendered framebuffer on screen. + * @see @ref Sdl2Application::setSwapInterval() + */ + void swapBuffers(); -@code{.cmake} -find_package(Magnum REQUIRED Sdl2Application) + /** + * @brief Redraw immediately + * + * Marks the window for redrawing, resulting in call to @ref drawEvent() + * in the next iteration. You can call it from @ref drawEvent() itself + * to redraw immediately without waiting for user input. + */ + void redraw(); -# ... -target_link_libraries(your-app PRIVATE Magnum::Sdl2Application) -@endcode + private: + /** + * @brief Viewport event + * + * Called when window size changes. The default implementation does + * nothing. If you want to respond to size changes, you should pass the + * new *framebuffer* size to @ref GL::DefaultFramebuffer::setViewport() + * (if using OpenGL) and possibly elsewhere (to + * @ref SceneGraph::Camera::setViewport(), other framebuffers...) and + * the new *window* size and DPI scaling to APIs that respond to user + * events or scale UI elements. + * + * Note that this function might not get called at all if the window + * size doesn't change. You should configure the initial state of your + * cameras, framebuffers etc. in application constructor rather than + * relying on this function to be called. Size of the window can be + * retrieved using @ref windowSize(), size of the backing framebuffer + * via @ref framebufferSize() and DPI scaling using @ref dpiScaling(). + * See @ref Platform-Sdl2Application-dpi for detailed info about these + * values. + */ + virtual void viewportEvent(ViewportEvent& event); -Additionally, if you're using Magnum as a CMake subproject, bundle the -[SDL repository](https://github.com/libsdl-org/SDL) and do the following -* *before* calling @cmake find_package() @ce to ensure it's enabled, as the -library is not built by default. If you want to use system-installed SDL2, -omit the first part and point `CMAKE_PREFIX_PATH` to its installation dir if -necessary. + /** + * @brief Draw event + * + * Called when the screen is redrawn. You should clean the framebuffer + * using @ref GL::DefaultFramebuffer::clear() (if using OpenGL) and + * then add your own drawing functions. After drawing is finished, call + * @ref swapBuffers(). If you want to draw immediately again, call also + * @ref redraw(). + */ + virtual void drawEvent() = 0; -@code{.cmake} -# This is the most minimal set of features which still make Sdl2Application -# work. If you need something from these, remove the setting. The SDL_AUDIO and -# SDL_EVENT options should not be needed either as Magnum doesn't use them, but -# if they're disabled they causes compiler or linker errors. Either SDL_DLOPEN -# or SDL_LOADSO needs to be enabled depending on the system to allow linking -# dependencies at runtime, so it's better to just leave them both on. The -# SDL_TIMERS option is important for rendering performance. -set(SDL_ATOMIC OFF CACHE BOOL "" FORCE) -set(SDL_CPUINFO OFF CACHE BOOL "" FORCE) -set(SDL_FILE OFF CACHE BOOL "" FORCE) -set(SDL_FILESYSTEM OFF CACHE BOOL "" FORCE) -set(SDL_HAPTIC OFF CACHE BOOL "" FORCE) -set(SDL_LOCALE OFF CACHE BOOL "" FORCE) -set(SDL_POWER OFF CACHE BOOL "" FORCE) -set(SDL_RENDER OFF CACHE BOOL "" FORCE) -set(SDL_SENSOR OFF CACHE BOOL "" FORCE) -set(SDL_THREADS OFF CACHE BOOL "" FORCE) -# This assumes you want to have SDL as a static library. If not, set SDL_STATIC -# to OFF instead. -set(SDL_SHARED OFF CACHE BOOL "" FORCE) -add_subdirectory(SDL EXCLUDE_FROM_ALL) + /* Since 1.8.17, the original short-hand group closing doesn't work + anymore. FFS. */ + /** + * @} + */ -set(MAGNUM_WITH_SDL2APPLICATION ON CACHE BOOL "" FORCE) -add_subdirectory(magnum EXCLUDE_FROM_ALL) -@endcode + /** @{ @name Keyboard handling */ - + /** + * @brief Key press event + * + * Called when an key is pressed. Default implementation does nothing. + */ + virtual void keyPressEvent(KeyEvent& event); -@m_class{m-note m-warning} + /** + * @brief Key release event + * + * Called when an key is released. Default implementation does nothing. + */ + virtual void keyReleaseEvent(KeyEvent& event); -@par - While SDL itself, being a C project, builds quite fast, when using it as a - CMake subproject be prepared that it will *significantly* increase the - CMake configure time due to excessive platform checks, and pollute the - CMake option list with a lot of unprefixed SDL-specific options. + /* Since 1.8.17, the original short-hand group closing doesn't work + anymore. FFS. */ + /** + * @} + */ -If no other application is requested, you can also use the generic -`Magnum::Application` alias to simplify porting. Again, see @ref building and -@ref cmake for more information. + /** @{ @name Mouse handling */ -In C++ code you need to implement at least @ref drawEvent() to be able to draw -on the screen. The subclass can be then used directly in `main()` --- see -convenience macro @ref MAGNUM_SDL2APPLICATION_MAIN(). See @ref platform for -more information. + public: + /** + * @brief Cursor type + * @m_since{2020,06} + * + * @see @ref setCursor() + */ + enum class Cursor: UnsignedInt { + Arrow, /**< Arrow */ + TextInput, /**< Text input */ + Wait, /**< Wait */ + Crosshair, /**< Crosshair */ + WaitArrow, /**< Small wait cursor */ + ResizeNWSE, /**< Double arrow pointing northwest and southeast */ + ResizeNESW, /**< Double arrow pointing northeast and southwest */ + ResizeWE, /**< Double arrow pointing west and east */ + ResizeNS, /**< Double arrow pointing north and south */ + ResizeAll, /**< Four pointed arrow pointing north, south, east, and west */ + No, /**< Slashed circle or crossbones */ + Hand, /**< Hand */ + Hidden, /**< Hidden */ -@code{.cpp} -class MyApplication: public Platform::Sdl2Application { - // implement required methods... -}; -MAGNUM_SDL2APPLICATION_MAIN(MyApplication) -@endcode + #ifndef CORRADE_TARGET_EMSCRIPTEN + /** + * Hidden and locked. When the mouse is locked, only + * @ref MouseMoveEvent::relativePosition() is changing, absolute + * position stays the same. + * @note Not available in @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". + */ + HiddenLocked + #endif + }; -If no other application header is included, this class is also aliased to -@cpp Platform::Application @ce and the macro is aliased to @cpp MAGNUM_APPLICATION_MAIN() @ce -to simplify porting. + /** + * @brief Set cursor type + * @m_since{2020,06} + * + * Expects that a window is already created. Default is + * @ref Cursor::Arrow. + * @ref TODOTODO uhh clean up the docs to say "that the window is", not "a" + */ + void setCursor(Cursor cursor); -@subsection Platform-Sdl2Application-usage-power Power management + /** + * @brief Get current cursor type + * @m_since{2020,06} + */ + Cursor cursor(); -SDL by default prevents the computer from powering off the or screen going to -sleep. While possibly useful for game-like use cases, it's generally -undesirable for regular applications. @ref Sdl2Application turns this behavior -off. You can restore SDL's default behavior by disabling the -[corresponding SDL hint](https://wiki.libsdl.org/CategoryHints) through an -environment variable or through @cpp SDL_SetHint() @ce from your application. + #ifndef CORRADE_TARGET_EMSCRIPTEN + /** + * @brief Warp mouse cursor to given coordinates + * + * @note Not available in @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". + */ + void warpCursor(const Vector2i& position) { + SDL_WarpMouseInWindow(_window, position.x(), position.y()); + } + #endif -@code{.sh} -SDL_VIDEO_ALLOW_SCREENSAVER=0 ./your-app -@endcode + private: + /** + * @brief Mouse press event + * + * Called when mouse button is pressed. Default implementation does + * nothing. + */ + virtual void mousePressEvent(MouseEvent& event); -@subsection Platform-Sdl2Application-usage-posix POSIX specifics + /** + * @brief Mouse release event + * + * Called when mouse button is released. Default implementation does + * nothing. + */ + virtual void mouseReleaseEvent(MouseEvent& event); -On POSIX systems, SDL by default intercepts the `SIGTERM` signal and generates -an exit event for it, instead of doing the usual application exit. This would -mean that if the application fails to set @ref ExitEvent::setAccepted() in an -@ref exitEvent() override for some reason, pressing -@m_class{m-label m-warning} **Ctrl** @m_class{m-label m-default} **C** would -not terminate it either and you'd have to forcibly kill it instead. When using -SDL >= 2.0.4, @ref Sdl2Application turns this behavior off, making -@ref exitEvent() behave consistently with other application implementations -such as @ref GlfwApplication. You can turn this behavior back on by enabling -the [corresponding SDL hint](https://wiki.libsdl.org/SDL_HINT_NO_SIGNAL_HANDLERS) -through an environment variable: + /** + * @brief Mouse move event + * + * Called when mouse is moved. Default implementation does nothing. + */ + virtual void mouseMoveEvent(MouseMoveEvent& event); -@code{.sh} -SDL_NO_SIGNAL_HANDLERS=1 ./your-app -@endcode + /** + * @brief Mouse scroll event + * + * Called when a scrolling device is used (mouse wheel or scrolling + * area on a touchpad). Default implementation does nothing. + */ + virtual void mouseScrollEvent(MouseScrollEvent& event); -See also the [SDL Wiki](https://wiki.libsdl.org/SDL_EventType#SDL_QUIT) for -details. + /* Since 1.8.17, the original short-hand group closing doesn't work + anymore. FFS. */ + /** + * @} + */ -@subsection Platform-Sdl2Application-usage-linux Linux specifics + /** @{ @name Touch gesture handling */ -SDL by default attempts to disable compositing, which may cause ugly flickering -for non-fullscreen apps (KWin, among others, is known to respect this setting). -When using SDL >= 2.0.8, @ref Sdl2Application turns this behavior off, keeping -the compositor running to avoid the flicker. You can turn this behavior back on -by enabling the [corresponding SDL hint](https://wiki.libsdl.org/CategoryHints) -through an environment variable or through @cpp SDL_SetHint() @ce from your -application. + /** + * @brief Multi gesture event + * + * Called when the user performs a gesture using multiple fingers. + * Default implementation does nothing. + * @experimental + */ + virtual void multiGestureEvent(MultiGestureEvent& event); -@code{.sh} -SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR=1 ./your-app -@endcode + /* Since 1.8.17, the original short-hand group closing doesn't work + anymore. FFS. */ + /** + * @} + */ -If you're running an older version of SDL, you can disallow apps from bypassing -the compositor in system-wide KWin settings. + /** @{ @name Text input handling */ -@subsection Platform-Sdl2Application-usage-ios iOS specifics + /** + * @brief Text input event + * + * Called when text input is active and the text is being input. + * @see @ref Sdl2Application::isTextInputActive() + */ + virtual void textInputEvent(TextInputEvent& event); -Leaving a default (zero) window size in @ref Configuration will cause the app -to autodetect it based on the actual device screen size. This also depends on -@ref Platform-Sdl2Application-dpi "DPI awareness", see below for details. + /** + * @brief Text editing event + * + * Called when text input is active and the text is being edited. + */ + virtual void textEditingEvent(TextEditingEvent& event); -As noted in the @ref platforms-ios-bundle "iOS platform guide", a lot of -options needs to be set via a `*.plist` file. Some options can be configured -from runtime when creating the SDL2 application window, see documentation of -a particular value for details: + /* Since 1.8.17, the original short-hand group closing doesn't work + anymore. FFS. */ + /** + * @} + */ -- @ref Configuration::WindowFlag::Borderless hides the menu bar -- @ref Configuration::WindowFlag::Resizable makes the application respond to - device orientation changes + private: + friend Sdl2Application; -@subsection Platform-Sdl2Application-usage-emscripten Emscripten specifics + enum class WindowFlag: UnsignedByte; + typedef Containers::EnumSet WindowFlags; + CORRADE_ENUMSET_FRIEND_OPERATORS(WindowFlags) -Leaving a default (zero) window size in @ref Configuration will cause the app -to use a window size that corresponds to *CSS pixel size* of the -@cb{.html} @ce element. The size is then multiplied by DPI scaling -value, see @ref Platform-Sdl2Application-dpi "DPI awareness" below for details. + /* Used by Sdl2Application(NoCreateT) */ + explicit Sdl2ApplicationWindow(Sdl2Application& application, NoCreateT); -If you enable @ref Configuration::WindowFlag::Resizable, the canvas will be -resized when size of the canvas changes and you get @ref viewportEvent(). If -the flag is not enabled, no canvas resizing is performed. + Vector2 dpiScalingInternal(Implementation::Sdl2DpiScalingPolicy configurationDpiScalingPolicy, const Vector2& configurationDpiScaling) const; + #ifndef CORRADE_TARGET_EMSCRIPTEN + bool tryCreateWindow(const Configuration& configuration); + void destroyWindow(); + #endif -@note While this implementation supports Esmcripten and is going to continue - supporting it for the foreseeable future, @ref EmscriptenApplication is now - the preferred application implementation for the web. It offers a broader - range of features, more efficient idle behavior and smaller code size. + Sdl2Application& _application; -@subsection Platform-Sdl2Application-usage-gles OpenGL ES specifics + /* These are saved from configuration to be reused in dpiScaling() and + viewportEvent() later */ + Implementation::Sdl2DpiScalingPolicy _configurationDpiScalingPolicy{}; + Vector2 _configurationDpiScaling; -For OpenGL ES, SDL2 defaults to a "desktop GLES" context of the system driver. -Because Magnum has the opposite default behavior, if @ref MAGNUM_TARGET_GLES is -not defined and SDL >= 2.0.6 is used, @ref Sdl2Application sets the -`SDL_HINT_OPENGL_ES_DRIVER` hint to 1, forcing it to load symbols from a -dedicated libGLES library instead, making SDL and Magnum consistently use the -same OpenGL entrypoints. This change also allows @ref platforms-gl-es-angle "ANGLE" -to be used on Windows simply by placing the corresponding `libEGL.dll` and -`libGLESv2.dll` files next to the application executable. + #ifndef CORRADE_TARGET_EMSCRIPTEN + SDL_Window* _window{}; + Vector2i _viewportSize; + #else + SDL_Surface* _surface{}; + Vector2i _lastKnownCanvasSize; + #endif -@section Platform-Sdl2Application-dpi DPI awareness + WindowFlags _windowFlags; +}; -On displays that match the platform default DPI (96 or 72), -@ref Configuration::setSize() will create the window in exactly the requested -size and the framebuffer pixels will match display pixels 1:1. On displays that -have different DPI, there are three possible scenarios, listed below. It's -possible to fine tune the behavior either using extra parameters passed to -@ref Configuration::setSize() or via the `--magnum-dpi-scaling` command-line -option (or the equivalent @cb{.sh} $MAGNUM_DPI_SCALING @ce environment -variable). +/** @nosubgrouping +@brief SDL2 application -- Framebuffer DPI scaling. The window is created with exactly the requested - size and all event coordinates are reported also relative to that size. - However, the window backing framebuffer has a different size. This is only - supported on macOS and iOS. See @ref platforms-macos-hidpi for details how - to enable it. Equivalent to passing - @ref Configuration::DpiScalingPolicy::Framebuffer to - @ref Configuration::setSize() or `framebuffer` via command line / - environment. -- Virtual DPI scaling. Scales the window based on DPI scaling setting in the - system. For example if a 800x600 window is requested and DPI scaling is set - to 200%, the resulting window will have 1600x1200 pixels. The backing - framebuffer will have the same size. This is supported on Linux and - Windows; on Windows the application is first checked for DPI awareness - as described in @ref platforms-windows-hidpi and if the application is not - DPI-aware, 1:1 scaling is used. Equivalent to passing - @ref Configuration::DpiScalingPolicy::Virtual to - @ref Configuration::setSize() or `virtual` on command line. -- Physical DPI scaling. Takes the requested window size as a physical size - that a window would have on platform's default DPI and scales it to have - the same physical size on given display physical DPI. So, for example on a - display with 240 DPI the window size will be 2000x1500 in pixels, but it - will be 21 centimeters wide, the same as a 800x600 window would be on a 96 - DPI display. On platforms that don't have a concept of a window (such - as mobile platforms or @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten"), it - causes the framebuffer to match display pixels 1:1 without any scaling. - This is supported on Linux and all mobile platforms (except iOS) and - Emscripten. On Windows this is equivalent to virtual DPI scaling but - without doing an explicit check for DPI awareness first. Equivalent to - passing @ref Configuration::DpiScalingPolicy::Physical to - @ref Configuration::setSize() or `physical` via command line / environment. +@m_keywords{Application} -Besides the above, it's possible to supply a custom DPI scaling value to -@ref Configuration::setSize() or the `--magnum-dpi-scaling` command-line -option (or environment variable). Using `--magnum-dpi-scaling <number>` -will make the scaling same in both directions, with -`--magnum-dpi-scaling " "` the scaling will be different -in each direction. On desktop systems custom DPI scaling value will affect -physical window size (with the content being scaled), on mobile and web it will -affect sharpness of the contents. +Application using [Simple DirectMedia Layer](http://www.libsdl.org/) toolkit. +Supports keyboard and mouse handling. This application library is available for +all platforms for which SDL2 is ported except Android (thus also +@ref CORRADE_TARGET_EMSCRIPTEN "Emscripten", see +respective sections in @ref building-corrade-cross-emscripten "Corrade's" and +@ref building-cross-emscripten "Magnum's" building documentation). -The default is depending on the platform: +@m_class{m-block m-success} -- On macOS and iOS, the default and only supported option is - @ref Configuration::DpiScalingPolicy::Framebuffer. On this platform, - @ref windowSize() and @ref framebufferSize() will differ depending on - whether `NSHighResolutionCapable` is enabled in the `*.plist` file or not. - By default, @ref dpiScaling() is @cpp 1.0f @ce in both dimensions but it - can be overridden using custom DPI scaling. -- On Windows, the default is @ref Configuration::DpiScalingPolicy::Framebuffer. - The @ref windowSize() and @ref framebufferSize() is always the same. - Depending on whether the DPI awareness was enabled in the manifest file or - set by the `SetProcessDpiAwareness()` API, @ref dpiScaling() is either - @cpp 1.0f @ce in both dimensions, indicating a low-DPI screen or a - non-DPI-aware app, or some other value for HiDPI screens. In both cases the - value can be overridden using custom DPI scaling. -- On Linux, the default is @ref Configuration::DpiScalingPolicy::Virtual, - taken from the `Xft.dpi` property. If the property is not available, it - falls back to @ref Configuration::DpiScalingPolicy::Physical, querying the - monitor DPI value. The @ref windowSize() and @ref framebufferSize() is - always the same, @ref dpiScaling() contains the queried DPI scaling value. - The value can be overridden using custom DPI scaling. -- On @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten", the default is physical DPI - scaling, taken from [Window.getDevicePixelRatio()](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio). The - @ref windowSize() and @ref framebufferSize() is always the same, - @ref dpiScaling() contains the queried DPI scaling value. The value can be - overridden using custom DPI scaling. Note that this is different from the - behavior in @ref EmscriptenApplication --- Emscripten's SDL implementation - has some additional emulation code that reports event coordinates in - framebuffer pixels instead of CSS pixels. See - @ref Platform-EmscriptenApplication-dpi "EmscriptenApplication DPI awareness docs" - for more information. +@thirdparty This plugin makes use of the [SDL2](https://www.libsdl.org/) + library, released under the @m_class{m-label m-success} **zlib license** + ([license text](http://www.gzip.org/zlib/zlib_license.html), + [choosealicense.com](https://choosealicense.com/licenses/zlib/)). + Attribution is appreciated but not required. -With @ref windowSize(), @ref framebufferSize() and @ref dpiScaling() having a -different relation on each platform, the way to calculate context scaling -consistently everywhere is using this expression: +@section Platform-Sdl2Application-bootstrap Bootstrap application -@snippet MagnumPlatform.cpp Sdl2Application-dpi-scaling +Fully contained base application using @ref Sdl2Application along with +CMake setup is available in `base` branch of +[Magnum Bootstrap](https://github.com/mosra/magnum-bootstrap) repository, +download it as [tar.gz](https://github.com/mosra/magnum-bootstrap/archive/base.tar.gz) +or [zip](https://github.com/mosra/magnum-bootstrap/archive/base.zip) file. +After extracting the downloaded archive you can build and run the application +with these four commands: -If your application is saving and restoring window size, it's advisable to take -@ref dpiScaling() into account: +@code{.sh} +mkdir build && cd build +cmake .. +cmake --build . +./src/MyApplication # or ./src/Debug/MyApplication +@endcode -- Either divide the window size by the DPI scaling value and use that to - restore the window next time --- but note this might accumulate slight - differences in window sizes over time, especially if fractional scaling is - involved. -- Or save the scaled size and use @ref Configuration::setSize(const Vector2i&, const Vector2&) - with @cpp 1.0f @ce as custom DPI scaling next time --- but this doesn't - properly handle cases where the window is opened on a display with - different DPI. -*/ -class Sdl2Application { - public: - /** @brief Application arguments */ - struct Arguments { - /** @brief Constructor */ - /*implicit*/ constexpr Arguments(int& argc, char** argv) noexcept: argc{argc}, argv{argv} {} +See @ref cmake for more information. - int& argc; /**< @brief Argument count */ - char** argv; /**< @brief Argument values */ - }; +@section Platform-Sdl2Application-bootstrap-emscripten Bootstrap application for Emscripten - class Configuration; - #ifdef MAGNUM_TARGET_GL - class GLConfiguration; - #endif - class ExitEvent; - class ViewportEvent; - class InputEvent; - class KeyEvent; - class MouseEvent; - class MouseMoveEvent; - class MouseScrollEvent; - class MultiGestureEvent; - class TextInputEvent; - class TextEditingEvent; +The dedicated application implementation for Emscripten is +@ref EmscriptenApplication, which also provides a bootstrap project along with +full HTML markup and CMake setup. @ref Sdl2Application however supports +Emscripten as well --- set up the bootstrap application as +@ref Platform-EmscriptenApplication-bootstrap "described in the EmscriptenApplication docs" +and then change `src/CMakeLists.txt` and the @cpp #include @ce to use +@ref Sdl2Application for both the native and the web build. - #ifdef MAGNUM_TARGET_GL - /** - * @brief Construct with given configuration for OpenGL context - * @param arguments Application arguments - * @param configuration Application configuration - * @param glConfiguration OpenGL context configuration - * - * Creates application with default or user-specified configuration. - * See @ref Configuration for more information. The program exits if - * the context cannot be created, see @ref tryCreate() for an - * alternative. - * - * @note This function is available only if Magnum is compiled with - * @ref MAGNUM_TARGET_GL enabled (done by default). See - * @ref building-features for more information. - */ - explicit Sdl2Application(const Arguments& arguments, const Configuration& configuration, const GLConfiguration& glConfiguration); - #endif +@section Platform-Sdl2Application-bootstrap-ios Bootstrap application for iOS - /** - * @brief Construct with given configuration - * - * If @ref Configuration::WindowFlag::Contextless is present or Magnum - * was not built with @ref MAGNUM_TARGET_GL, this creates a window - * without any GPU context attached, leaving that part on the user. - * - * If none of the flags is present and Magnum was built with - * @ref MAGNUM_TARGET_GL, this is equivalent to calling - * @ref Sdl2Application(const Arguments&, const Configuration&, const GLConfiguration&) - * with default-constructed @ref GLConfiguration. - * - * See also @ref building-features for more information. - */ - explicit Sdl2Application(const Arguments& arguments, const Configuration& configuration); +Fully contained base application using @ref Sdl2Application for both desktop +and iOS build along with pre-filled `*.plist` is available in `base-ios` branch +of [Magnum Bootstrap](https://github.com/mosra/magnum-bootstrap) repository, +download it as [tar.gz](https://github.com/mosra/magnum-bootstrap/archive/base-ios.tar.gz) +or [zip](https://github.com/mosra/magnum-bootstrap/archive/base-ios.zip) file. +After extracting the downloaded archive, you can do the desktop build in +the same way as above. For the iOS build you also need to put the contents of +toolchains repository from https://github.com/mosra/toolchains in `toolchains/` +subdirectory. - /** - * @brief Construct with default configuration - * - * Equivalent to calling @ref Sdl2Application(const Arguments&, const Configuration&) - * with default-constructed @ref Configuration. - */ - explicit Sdl2Application(const Arguments& arguments); +Then create build directory and run `cmake` to generate the Xcode project. Set +`CMAKE_OSX_ROOT` to SDK you want to target and enable all desired architectures +in `CMAKE_OSX_ARCHITECTURES`. Set `CMAKE_PREFIX_PATH` to the directory where +you have all the dependencies. - /** - * @brief Construct without creating a window - * @param arguments Application arguments - * - * Unlike above, the window is not created and must be created later - * with @ref create() or @ref tryCreate(). - */ - explicit Sdl2Application(const Arguments& arguments, NoCreateT); +@code{.sh} +mkdir build-ios && cd build-ios +cmake .. \ + -DCMAKE_TOOLCHAIN_FILE=path/to/toolchains/generic/iOS.cmake \ + -DCMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk \ + -DCMAKE_OSX_ARCHITECTURES="arm64;armv7;armv7s" \ + -DCMAKE_PREFIX_PATH=~/ios-libs \ + -G Xcode +@endcode - /** @brief Copying is not allowed */ - Sdl2Application(const Sdl2Application&) = delete; +You can then open the generated project file in Xcode and build/deploy it from +there. - /** @brief Moving is not allowed */ - Sdl2Application(Sdl2Application&&) = delete; +@section Platform-Sdl2Application-bootstrap-winrt Bootstrap application for Windows RT - /** @brief Copying is not allowed */ - Sdl2Application& operator=(const Sdl2Application&) = delete; +Fully contained base application using @ref Sdl2Application for both desktop +and Windows Phone / Windows Store build along with all required plumbing is +available in `base-winrt` branch of [Magnum Bootstrap](https://github.com/mosra/magnum-bootstrap) +repository, download it as [zip](https://github.com/mosra/magnum-bootstrap/archive/base-winrt.zip) +file. After extracting the downloaded archive, you can do the desktop build in +the same way as above. - /** @brief Moving is not allowed */ - Sdl2Application& operator=(Sdl2Application&&) = delete; +For the Windows RT build you need to provide [your own *.pfx certificate file](https://msdn.microsoft.com/en-us/library/windows/desktop/jj835832.aspx) and +pass it to CMake in a `SIGNING_CERTIFICATE` variable. The bootstrap application +assumes that SDL2 and ANGLE is built as DLL and both Corrade and Magnum are +built statically. Assuming the native Corrade installation is in `C:/Sys` and +all WinRT dependencies are in `C:/Sys-winrt`, the build can be done similarly +to the following: - /** - * @brief Execute application main loop - * @return Value for returning from @cpp main() @ce - * - * Calls @ref mainLoopIteration() in a loop until @ref exit() is - * called. See @ref MAGNUM_SDL2APPLICATION_MAIN() for usage - * information. - */ - int exec(); +@code{.bat} +mkdir build-winrt && cd build-winrt +cmake .. ^ + -DCORRADE_RC_EXECUTABLE="C:/Sys/bin/corrade-rc.exe" ^ + -DCMAKE_PREFIX_PATH="C:/Sys-winrt" ^ + -DCMAKE_SYSTEM_NAME=WindowsStore ^ + -DCMAKE_SYSTEM_VERSION=8.1 ^ + -G "Visual Studio 14 2015" ^ + -DSIGNING_CERTIFICATE= +cmake --build . +@endcode - /** - * @brief Run one iteration of application main loop - * @return @cpp false @ce if @ref exit() was called and the application - * should exit, @cpp true @ce otherwise - * - * Called internally from @ref exec(). If you want to have better - * control over how the main loop behaves, you can call this function - * yourself from your own `main()` function instead of it being called - * automatically from @ref exec() / @ref MAGNUM_SDL2APPLICATION_MAIN(). - */ - bool mainLoopIteration(); +Change `WindowsStore` to `WindowsPhone` if you want to build for Windows Phone +instead. The `build-winrt/src/AppPackages` directory will then contain the +final package along with a PowerShell script for easy local installation. - /** - * @brief Exit application - * @param exitCode The exit code the application should return - * - * When called from application constructor, it will cause the - * application to exit immediately after constructor ends, without - * entering the event loop. When called from within an event handler, - * it will cause it to exit at the start of next event loop iteration. - * Compared to requesting an application exit using the window close - * button or the @m_class{m-label m-default} **Alt** - * @m_class{m-label m-default} **F4** / - * @m_class{m-label m-default} **Cmd** - * @m_class{m-label m-default} **Q** keyboard shortcut, the - * @ref exitEvent() *isn't* called when using this function. - * - * Calling this function from an application constructor is recommended - * over @ref std::exit() or @ref Corrade::Utility::Fatal "Fatal", which - * exit without calling destructors on local scope. Note that, however, - * you need to explicitly @cpp return @ce after calling it, as it can't - * exit the constructor on its own: - * - * @snippet MagnumPlatform.cpp exit-from-constructor - */ - void exit(int exitCode = 0); +@section Platform-Sdl2Application-usage General usage - #ifndef CORRADE_TARGET_EMSCRIPTEN - /** - * @brief Underlying window handle - * - * Use in case you need to call SDL functionality directly. Returns - * @cpp nullptr @ce in case the window was not created yet. - * @note Not available in @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". - */ - SDL_Window* window() { return _window; } - #endif +This application library depends on the [SDL2](http://www.libsdl.org) library +(Emscripten has it built in) and is built if `MAGNUM_WITH_SDL2APPLICATION` is +enabled when building Magnum. To use this library with CMake, put +[FindSDL2.cmake](https://github.com/mosra/magnum/blob/master/modules/FindSDL2.cmake) +into your `modules/` directory, request the `Sdl2Application` component of +the `Magnum` package and link to the `Magnum::Sdl2Application` target: - #if defined(MAGNUM_TARGET_GL) && !defined(CORRADE_TARGET_EMSCRIPTEN) - /** - * @brief Underlying OpenGL context - * @m_since{2019,10} - * - * Use in case you need to call SDL functionality directly. Returns - * @cpp nullptr @ce in case the context was not created yet. - * @note This function is available only if Magnum is compiled with - * @ref MAGNUM_TARGET_GL enabled (done by default). See - * @ref building-features for more information. Not available in - * @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". - */ - SDL_GLContext glContext() { return _glContext; } - #endif +@code{.cmake} +find_package(Magnum REQUIRED Sdl2Application) - protected: - /* Nobody will need to have (and delete) Sdl2Application*, thus this is - faster than public pure virtual destructor */ - ~Sdl2Application(); +# ... +target_link_libraries(your-app PRIVATE Magnum::Sdl2Application) +@endcode - #ifdef MAGNUM_TARGET_GL - /** - * @brief Create a window with given configuration for OpenGL context - * @param configuration Application configuration - * @param glConfiguration OpenGL context configuration - * - * Must be called only if the context wasn't created by the constructor - * itself, i.e. when passing @ref NoCreate to it. Error message is - * printed and the program exits if the context cannot be created, see - * @ref tryCreate() for an alternative. - * - * On desktop GL, if version is not specified in @p glConfiguration, - * the application first tries to create core context (OpenGL 3.2+ on - * macOS, OpenGL 3.1+ elsewhere) and if that fails, falls back to - * compatibility OpenGL 2.1 context. - * - * @note This function is available only if Magnum is compiled with - * @ref MAGNUM_TARGET_GL enabled (done by default). See - * @ref building-features for more information. - */ - void create(const Configuration& configuration, const GLConfiguration& glConfiguration); - #endif +Additionally, if you're using Magnum as a CMake subproject, bundle the +[SDL repository](https://github.com/libsdl-org/SDL) and do the following +* *before* calling @cmake find_package() @ce to ensure it's enabled, as the +library is not built by default. If you want to use system-installed SDL2, +omit the first part and point `CMAKE_PREFIX_PATH` to its installation dir if +necessary. - /** - * @brief Create a window with given configuration - * - * If @ref Configuration::WindowFlag::Contextless is present or Magnum - * was not built with @ref MAGNUM_TARGET_GL, this creates a window - * without any GPU context attached, leaving that part on the user. - * - * If none of the flags is present and Magnum was built with - * @ref MAGNUM_TARGET_GL, this is equivalent to calling - * @ref create(const Configuration&, const GLConfiguration&) with - * default-constructed @ref GLConfiguration. - * - * See also @ref building-features for more information. - */ - void create(const Configuration& configuration); +@code{.cmake} +# This is the most minimal set of features which still make Sdl2Application +# work. If you need something from these, remove the setting. The SDL_AUDIO and +# SDL_EVENT options should not be needed either as Magnum doesn't use them, but +# if they're disabled they causes compiler or linker errors. Either SDL_DLOPEN +# or SDL_LOADSO needs to be enabled depending on the system to allow linking +# dependencies at runtime, so it's better to just leave them both on. The +# SDL_TIMERS option is important for rendering performance. +set(SDL_ATOMIC OFF CACHE BOOL "" FORCE) +set(SDL_CPUINFO OFF CACHE BOOL "" FORCE) +set(SDL_FILE OFF CACHE BOOL "" FORCE) +set(SDL_FILESYSTEM OFF CACHE BOOL "" FORCE) +set(SDL_HAPTIC OFF CACHE BOOL "" FORCE) +set(SDL_LOCALE OFF CACHE BOOL "" FORCE) +set(SDL_POWER OFF CACHE BOOL "" FORCE) +set(SDL_RENDER OFF CACHE BOOL "" FORCE) +set(SDL_SENSOR OFF CACHE BOOL "" FORCE) +set(SDL_THREADS OFF CACHE BOOL "" FORCE) +# This assumes you want to have SDL as a static library. If not, set SDL_STATIC +# to OFF instead. +set(SDL_SHARED OFF CACHE BOOL "" FORCE) +add_subdirectory(SDL EXCLUDE_FROM_ALL) - /** - * @brief Create a window with default configuration and OpenGL context - * - * Equivalent to calling @ref create(const Configuration&) with - * default-constructed @ref Configuration. - */ - void create(); +set(MAGNUM_WITH_SDL2APPLICATION ON CACHE BOOL "" FORCE) +add_subdirectory(magnum EXCLUDE_FROM_ALL) +@endcode - #ifdef MAGNUM_TARGET_GL - /** - * @brief Try to create context with given configuration for OpenGL context - * - * Unlike @ref create(const Configuration&, const GLConfiguration&) - * returns @cpp false @ce if the context cannot be created, - * @cpp true @ce otherwise. - * - * @note This function is available only if Magnum is compiled with - * @ref MAGNUM_TARGET_GL enabled (done by default). See - * @ref building-features for more information. - */ - bool tryCreate(const Configuration& configuration, const GLConfiguration& glConfiguration); - #endif + - /** - * @brief Try to create context with given configuration - * - * Unlike @ref create(const Configuration&) returns @cpp false @ce if - * the context cannot be created, @cpp true @ce otherwise. - */ - bool tryCreate(const Configuration& configuration); +@m_class{m-note m-warning} - /** @{ @name Screen handling */ +@par + While SDL itself, being a C project, builds quite fast, when using it as a + CMake subproject be prepared that it will *significantly* increase the + CMake configure time due to excessive platform checks, and pollute the + CMake option list with a lot of unprefixed SDL-specific options. - public: - /** - * @brief Window size - * - * Window size to which all input event coordinates can be related. - * Note that, especially on HiDPI systems, it may be different from - * @ref framebufferSize(). Expects that a window is already created. - * See @ref Platform-Sdl2Application-dpi for more information. - * @see @ref dpiScaling() - */ - Vector2i windowSize() const; +If no other application is requested, you can also use the generic +`Magnum::Application` alias to simplify porting. Again, see @ref building and +@ref cmake for more information. - #if !defined(CORRADE_TARGET_EMSCRIPTEN) || defined(DOXYGEN_GENERATING_OUTPUT) - /** - * @brief Set window size - * @param size The size, in screen coordinates - * @m_since{2020,06} - * - * To make the sizing work independently of the display DPI, @p size is - * internally multiplied with @ref dpiScaling() before getting applied. - * Expects that a window is already created. - * @note Not available in @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". - * @see @ref setMinWindowSize(), @ref setMaxWindowSize() - */ - void setWindowSize(const Vector2i& size); +In C++ code you need to implement at least @ref drawEvent() to be able to draw +on the screen. The subclass can be then used directly in `main()` --- see +convenience macro @ref MAGNUM_SDL2APPLICATION_MAIN(). See @ref platform for +more information. - /** - * @brief Set minimum window size - * @param size The minimum size, in screen coordinates - * @m_since{2019,10} - * - * Note that, unlike in @ref GlfwApplication, SDL2 doesn't have a way - * to disable/remove a size limit. To make the sizing work - * independently of the display DPI, @p size is internally multiplied - * with @ref dpiScaling() before getting applied. Expects that a window - * is already created. - * @note Not available in @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". - * @see @ref setMaxWindowSize(), @ref setWindowSize() - */ - void setMinWindowSize(const Vector2i& size); +@code{.cpp} +class MyApplication: public Platform::Sdl2Application { + // implement required methods... +}; +MAGNUM_SDL2APPLICATION_MAIN(MyApplication) +@endcode - /** - * @brief Set maximal window size - * @param size The maximum size, in screen coordinates - * @m_since{2019,10} - * - * Note that, unlike in @ref GlfwApplication, SDL2 doesn't have a way - * to disable/remove a size limit. To make the sizing work - * independently of the display DPI, @p size is internally multiplied - * with @ref dpiScaling() before getting applied. Expects that a window - * is already created. - * @note Not available in @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". - * @see @ref setMinWindowSize(), @ref setMaxWindowSize() - */ - void setMaxWindowSize(const Vector2i& size); - #endif +If no other application header is included, this class is also aliased to +@cpp Platform::Application @ce and the macro is aliased to @cpp MAGNUM_APPLICATION_MAIN() @ce +to simplify porting. - #if defined(MAGNUM_TARGET_GL) || defined(DOXYGEN_GENERATING_OUTPUT) - /** - * @brief Framebuffer size - * - * Size of the default framebuffer. Note that, especially on HiDPI - * systems, it may be different from @ref windowSize(). Expects that a - * window is already created. See @ref Platform-Sdl2Application-dpi for - * more information. - * - * @note This function is available only if Magnum is compiled with - * @ref MAGNUM_TARGET_GL enabled (done by default). See - * @ref building-features for more information. - * - * @see @ref Sdl2Application::framebufferSize(), @ref dpiScaling() - */ - Vector2i framebufferSize() const; - #endif +@subsection Platform-Sdl2Application-usage-power Power management - /** - * @brief DPI scaling - * - * How the content should be scaled relative to system defaults for - * given @ref windowSize(). If a window is not created yet, returns - * zero vector, use @ref dpiScaling(const Configuration&) for - * calculating a value independently. See @ref Platform-Sdl2Application-dpi - * for more information. - * @see @ref framebufferSize() - */ - Vector2 dpiScaling() const; +SDL by default prevents the computer from powering off the or screen going to +sleep. While possibly useful for game-like use cases, it's generally +undesirable for regular applications. @ref Sdl2Application turns this behavior +off. You can restore SDL's default behavior by disabling the +[corresponding SDL hint](https://wiki.libsdl.org/CategoryHints) through an +environment variable or through @cpp SDL_SetHint() @ce from your application. - /** - * @brief DPI scaling for given configuration - * - * Calculates DPI scaling that would be used when creating a window - * with given @p configuration. Takes into account DPI scaling policy - * and custom scaling specified on the command-line. See - * @ref Platform-Sdl2Application-dpi for more information. - */ - Vector2 dpiScaling(const Configuration& configuration); +@code{.sh} +SDL_VIDEO_ALLOW_SCREENSAVER=0 ./your-app +@endcode - /** - * @brief Set window title - * @m_since{2019,10} - * - * The @p title is expected to be encoded in UTF-8. - */ - void setWindowTitle(Containers::StringView title); +@subsection Platform-Sdl2Application-usage-posix POSIX specifics - #if !defined(CORRADE_TARGET_EMSCRIPTEN) && (SDL_MAJOR_VERSION*1000 + SDL_MINOR_VERSION*100 + SDL_PATCHLEVEL >= 2005 || defined(DOXYGEN_GENERATING_OUTPUT)) - /** - * @brief Set window icon - * @m_since{2020,06} - * - * The @p image is expected to be with origin at bottom left (which is - * the default for imported images) and in one of - * @ref PixelFormat::RGB8Unorm, @ref PixelFormat::RGB8Srgb, - * @ref PixelFormat::RGBA8Unorm or @ref PixelFormat::RGBA8Srgb formats. - * Unlike @ref GlfwApplication::setWindowIcon(), SDL doesn't provide a - * way to supply multiple images in different sizes. - * @note Available since SDL 2.0.5. Not available on - * @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten", use - * @cb{.html} @ce in your HTML markup instead. - * Although it's not documented in SDL itself, the function might - * have no effect on macOS / Wayland, similarly to how - * @ref GlfwApplication::setWindowIcon() behaves on those - * platforms. - * @see @ref platform-windows-icon "Excecutable icon on Windows", - * @ref Trade::IcoImporter "IcoImporter" - */ - void setWindowIcon(const ImageView2D& image); - #endif +On POSIX systems, SDL by default intercepts the `SIGTERM` signal and generates +an exit event for it, instead of doing the usual application exit. This would +mean that if the application fails to set @ref ExitEvent::setAccepted() in an +@ref exitEvent() override for some reason, pressing +@m_class{m-label m-warning} **Ctrl** @m_class{m-label m-default} **C** would +not terminate it either and you'd have to forcibly kill it instead. When using +SDL >= 2.0.4, @ref Sdl2Application turns this behavior off, making +@ref exitEvent() behave consistently with other application implementations +such as @ref GlfwApplication. You can turn this behavior back on by enabling +the [corresponding SDL hint](https://wiki.libsdl.org/SDL_HINT_NO_SIGNAL_HANDLERS) +through an environment variable: - #if defined(CORRADE_TARGET_EMSCRIPTEN) || defined(DOXYGEN_GENERATING_OUTPUT) - /** - * @brief Set container CSS class - * - * Assigns given CSS class to the @cb{.html}
@ce - * enclosing the application @cb{.html} @ce. Useful for - * example to change aspect ratio of the view or stretch it to cover - * the full page. See @ref platforms-html5-layout for more information - * about possible values. Note that this replaces any existing class - * (except for @cb{.css} .mn-container @ce, which is kept), to set - * multiple classes separate them with whitespace. - * - * @note Only available on @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". - * - * @m_class{m-note m-danger} - * - * @par - * For backwards compatibility purposes the function will look for - * *any* @cb{.html}
@ce in case the - * @cb{.html}
@ce is not found. This - * compatibility is scheduled to be removed in the future. - */ - void setContainerCssClass(Containers::StringView cssClass); - #endif +@code{.sh} +SDL_NO_SIGNAL_HANDLERS=1 ./your-app +@endcode + +See also the [SDL Wiki](https://wiki.libsdl.org/SDL_EventType#SDL_QUIT) for +details. + +@subsection Platform-Sdl2Application-usage-linux Linux specifics + +SDL by default attempts to disable compositing, which may cause ugly flickering +for non-fullscreen apps (KWin, among others, is known to respect this setting). +When using SDL >= 2.0.8, @ref Sdl2Application turns this behavior off, keeping +the compositor running to avoid the flicker. You can turn this behavior back on +by enabling the [corresponding SDL hint](https://wiki.libsdl.org/CategoryHints) +through an environment variable or through @cpp SDL_SetHint() @ce from your +application. + +@code{.sh} +SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR=1 ./your-app +@endcode + +If you're running an older version of SDL, you can disallow apps from bypassing +the compositor in system-wide KWin settings. + +@subsection Platform-Sdl2Application-usage-ios iOS specifics + +Leaving a default (zero) window size in +@relativeref{Sdl2ApplicationWindow,Configuration} will cause the app to +autodetect it based on the actual device screen size. This also depends on +@ref Platform-Sdl2Application-dpi "DPI awareness", see below for details. + +As noted in the @ref platforms-ios-bundle "iOS platform guide", a lot of +options needs to be set via a `*.plist` file. Some options can be configured +from runtime when creating the SDL2 application window, see documentation of +a particular value for details: + +- @relativeref{Sdl2ApplicationWindow,Configuration::WindowFlag::Borderless} + hides the menu bar +- @relativeref{Sdl2ApplicationWindow,Configuration::WindowFlag::Resizable} + makes the application respond to device orientation changes + +@subsection Platform-Sdl2Application-usage-emscripten Emscripten specifics + +Leaving a default (zero) window size in @relativeref{Sdl2ApplicationWindow,Configuration} +will cause the app to use a window size that corresponds to *CSS pixel size* of +the @cb{.html} @ce element. The size is then multiplied by DPI scaling +value, see @ref Platform-Sdl2Application-dpi "DPI awareness" below for details. + +If you enable @relativeref{Sdl2ApplicationWindow,Configuration::WindowFlag::Resizable}, +the canvas will be resized when size of the canvas changes and you get +@ref viewportEvent(). If the flag is not enabled, no canvas resizing is +performed. + +@note While this implementation supports Esmcripten and is going to continue + supporting it for the foreseeable future, @ref EmscriptenApplication is now + the preferred application implementation for the web. It offers a broader + range of features, more efficient idle behavior and smaller code size. + +@subsection Platform-Sdl2Application-usage-gles OpenGL ES specifics + +For OpenGL ES, SDL2 defaults to a "desktop GLES" context of the system driver. +Because Magnum has the opposite default behavior, if @ref MAGNUM_TARGET_GLES is +not defined and SDL >= 2.0.6 is used, @ref Sdl2Application sets the +`SDL_HINT_OPENGL_ES_DRIVER` hint to 1, forcing it to load symbols from a +dedicated libGLES library instead, making SDL and Magnum consistently use the +same OpenGL entrypoints. This change also allows @ref platforms-gl-es-angle "ANGLE" +to be used on Windows simply by placing the corresponding `libEGL.dll` and +`libGLESv2.dll` files next to the application executable. + +@section Platform-Sdl2Application-dpi DPI awareness + +On displays that match the platform default DPI (96 or 72), +@ref Configuration::setSize() will create the window in exactly the requested +size and the framebuffer pixels will match display pixels 1:1. On displays that +have different DPI, there are three possible scenarios, listed below. It's +possible to fine tune the behavior either using extra parameters passed to +@ref Configuration::setSize() or via the `--magnum-dpi-scaling` command-line +option (or the equivalent @cb{.sh} $MAGNUM_DPI_SCALING @ce environment +variable). + +- Framebuffer DPI scaling. The window is created with exactly the requested + size and all event coordinates are reported also relative to that size. + However, the window backing framebuffer has a different size. This is only + supported on macOS and iOS. See @ref platforms-macos-hidpi for details how + to enable it. Equivalent to passing + @relativeref{Sdl2ApplicationWindow,Configuration::DpiScalingPolicy::Framebuffer} + to @ref Configuration::setSize() or `framebuffer` via command line / + environment. +- Virtual DPI scaling. Scales the window based on DPI scaling setting in the + system. For example if a 800x600 window is requested and DPI scaling is set + to 200%, the resulting window will have 1600x1200 pixels. The backing + framebuffer will have the same size. This is supported on Linux and + Windows; on Windows the application is first checked for DPI awareness + as described in @ref platforms-windows-hidpi and if the application is not + DPI-aware, 1:1 scaling is used. Equivalent to passing + @relativeref{Sdl2ApplicationWindow,Configuration::DpiScalingPolicy::Virtual} + to @ref Configuration::setSize() or `virtual` on command line. +- Physical DPI scaling. Takes the requested window size as a physical size + that a window would have on platform's default DPI and scales it to have + the same physical size on given display physical DPI. So, for example on a + display with 240 DPI the window size will be 2000x1500 in pixels, but it + will be 21 centimeters wide, the same as a 800x600 window would be on a 96 + DPI display. On platforms that don't have a concept of a window (such + as mobile platforms or @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten"), it + causes the framebuffer to match display pixels 1:1 without any scaling. + This is supported on Linux and all mobile platforms (except iOS) and + Emscripten. On Windows this is equivalent to virtual DPI scaling but + without doing an explicit check for DPI awareness first. Equivalent to + passing @relativeref{Sdl2ApplicationWindow,Configuration::DpiScalingPolicy::Physical} + to @ref Configuration::setSize() or `physical` via command line / + environment. + +Besides the above, it's possible to supply a custom DPI scaling value to +@ref Configuration::setSize() or the `--magnum-dpi-scaling` command-line +option (or environment variable). Using `--magnum-dpi-scaling <number>` +will make the scaling same in both directions, with +`--magnum-dpi-scaling " "` the scaling will be different +in each direction. On desktop systems custom DPI scaling value will affect +physical window size (with the content being scaled), on mobile and web it will +affect sharpness of the contents. + +The default is depending on the platform: + +- On macOS and iOS, the default and only supported option is + @relativeref{Sdl2ApplicationWindow,Configuration::DpiScalingPolicy::Framebuffer}. + On this platform, @ref windowSize() and @ref framebufferSize() will differ + depending on whether `NSHighResolutionCapable` is enabled in the `*.plist` + file or not. By default, @ref dpiScaling() is @cpp 1.0f @ce in both + dimensions but it can be overridden using custom DPI scaling. +- On Windows, the default is @relativeref{Sdl2ApplicationWindow,Configuration::DpiScalingPolicy::Framebuffer}. + The @ref windowSize() and @ref framebufferSize() is always the same. + Depending on whether the DPI awareness was enabled in the manifest file or + set by the `SetProcessDpiAwareness()` API, @ref dpiScaling() is either + @cpp 1.0f @ce in both dimensions, indicating a low-DPI screen or a + non-DPI-aware app, or some other value for HiDPI screens. In both cases the + value can be overridden using custom DPI scaling. +- On Linux, the default is @relativeref{Sdl2ApplicationWindow,Configuration::DpiScalingPolicy::Virtual}, + taken from the `Xft.dpi` property. If the property is not available, it + falls back to @relativeref{Sdl2ApplicationWindow,Configuration::DpiScalingPolicy::Physical}, + querying the monitor DPI value. The @ref windowSize() and + @ref framebufferSize() is always the same, @ref dpiScaling() contains the + queried DPI scaling value. The value can be overridden using custom DPI + scaling. +- On @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten", the default is physical DPI + scaling, taken from [Window.getDevicePixelRatio()](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio). The + @ref windowSize() and @ref framebufferSize() is always the same, + @ref dpiScaling() contains the queried DPI scaling value. The value can be + overridden using custom DPI scaling. Note that this is different from the + behavior in @ref EmscriptenApplication --- Emscripten's SDL implementation + has some additional emulation code that reports event coordinates in + framebuffer pixels instead of CSS pixels. See + @ref Platform-EmscriptenApplication-dpi "EmscriptenApplication DPI awareness docs" + for more information. + +With @ref windowSize(), @ref framebufferSize() and @ref dpiScaling() having a +different relation on each platform, the way to calculate context scaling +consistently everywhere is using this expression: + +@snippet MagnumPlatform.cpp Sdl2Application-dpi-scaling + +If your application is saving and restoring window size, it's advisable to take +@ref dpiScaling() into account: + +- Either divide the window size by the DPI scaling value and use that to + restore the window next time --- but note this might accumulate slight + differences in window sizes over time, especially if fractional scaling is + involved. +- Or save the scaled size and use @ref Configuration::setSize(const Vector2i&, const Vector2&) + with @cpp 1.0f @ce as custom DPI scaling next time --- but this doesn't + properly handle cases where the window is opened on a display with + different DPI. +*/ +class Sdl2Application: public Sdl2ApplicationWindow { + public: + /** @brief Application arguments */ + struct Arguments { + /** @brief Constructor */ + /*implicit*/ constexpr Arguments(int& argc, char** argv) noexcept: argc{argc}, argv{argv} {} - /** - * @brief Swap buffers - * - * Paints currently rendered framebuffer on screen. - * @see @ref setSwapInterval() - */ - void swapBuffers(); + int& argc; /**< @brief Argument count */ + char** argv; /**< @brief Argument values */ + }; - /** @brief Swap interval */ - Int swapInterval() const; + class Configuration; + #ifdef MAGNUM_TARGET_GL + class GLConfiguration; + #endif + class ExitEvent; + #ifdef MAGNUM_TARGET_GL /** - * @brief Set swap interval + * @brief Construct with an OpenGL context + * @param arguments Application arguments + * @param configuration Application configuration + * @param glConfiguration OpenGL context configuration * - * Set @cpp 0 @ce for no VSync, @cpp 1 @ce for enabled VSync. Some - * platforms support @cpp -1 @ce for late swap tearing. Prints error - * message and returns @cpp false @ce if swap interval cannot be set, - * @cpp true @ce otherwise. Default is driver-dependent, you can query - * the value with @ref swapInterval(). - * @see @ref setMinimalLoopPeriod() - */ - bool setSwapInterval(Int interval); - - #ifndef CORRADE_TARGET_EMSCRIPTEN - /** - * @brief Set minimal loop period + * Creates application with default or user-specified configuration. + * See @relativeref{Sdl2ApplicationWindow,Configuration} for more + * information. The program exits if the context cannot be created, see + * @ref tryCreate() for an alternative. * - * This setting reduces the main loop frequency in case VSync is - * not/cannot be enabled or no drawing is done. Default is @cpp 0 @ce - * (i.e. looping at maximum frequency). If the application is drawing - * on the screen and VSync is enabled, this setting is ignored. - * @note Not available in @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten", - * the browser is managing the frequency instead. - * @see @ref setSwapInterval() + * @note This function is available only if Magnum is compiled with + * @ref MAGNUM_TARGET_GL enabled (done by default). See + * @ref building-features for more information. */ - void setMinimalLoopPeriod(UnsignedInt milliseconds) { - _minimalLoopPeriod = milliseconds; - } + explicit Sdl2Application(const Arguments& arguments, const Configuration& configuration, const GLConfiguration& glConfiguration); #endif /** - * @brief Redraw immediately + * @brief Construct without explicit GPU context configuration * - * Marks the window for redrawing, resulting in call to @ref drawEvent() - * in the next iteration. You can call it from @ref drawEvent() itself - * to redraw immediately without waiting for user input. - */ - void redraw(); - - private: - /** - * @brief Viewport event + * If @relativeref{Sdl2ApplicationWindow,Configuration::WindowFlag::Contextless} + * is present or Magnum was not built with @ref MAGNUM_TARGET_GL, this + * creates a window without any GPU context attached, leaving that part + * on the user. * - * Called when window size changes. The default implementation does - * nothing. If you want to respond to size changes, you should pass the - * new *framebuffer* size to @ref GL::DefaultFramebuffer::setViewport() - * (if using OpenGL) and possibly elsewhere (to - * @ref SceneGraph::Camera::setViewport(), other framebuffers...) and - * the new *window* size and DPI scaling to APIs that respond to user - * events or scale UI elements. + * If none of the flags is present and Magnum was built with + * @ref MAGNUM_TARGET_GL, this is equivalent to calling + * @ref Sdl2Application(const Arguments&, const Configuration&, const GLConfiguration&) + * with default-constructed @ref GLConfiguration. * - * Note that this function might not get called at all if the window - * size doesn't change. You should configure the initial state of your - * cameras, framebuffers etc. in application constructor rather than - * relying on this function to be called. Size of the window can be - * retrieved using @ref windowSize(), size of the backing framebuffer - * via @ref framebufferSize() and DPI scaling using @ref dpiScaling(). - * See @ref Platform-Sdl2Application-dpi for detailed info about these - * values. + * See also @ref building-features for more information. */ - virtual void viewportEvent(ViewportEvent& event); + #ifdef DOXYGEN_GENERATING_OUTPUT + explicit Sdl2Application(const Arguments& arguments, const Configuration& configuration = Configuration{}); + #else + /* Configuration is only forward-declared at this point */ + explicit Sdl2Application(const Arguments& arguments, const Configuration& configuration); + explicit Sdl2Application(const Arguments& arguments); + #endif /** - * @brief Draw event + * @brief Construct without creating a window + * @param arguments Application arguments * - * Called when the screen is redrawn. You should clean the framebuffer - * using @ref GL::DefaultFramebuffer::clear() (if using OpenGL) and - * then add your own drawing functions. After drawing is finished, call - * @ref swapBuffers(). If you want to draw immediately again, call also - * @ref redraw(). + * Unlike above, the window is not created and must be created later + * with @ref create() or @ref tryCreate(). */ - virtual void drawEvent() = 0; + explicit Sdl2Application(const Arguments& arguments, NoCreateT); - /* Since 1.8.17, the original short-hand group closing doesn't work - anymore. FFS. */ - /** - * @} - */ + /** @brief Copying is not allowed */ + Sdl2Application(const Sdl2Application&) = delete; - /** @{ @name Keyboard handling */ + /** @brief Moving is not allowed */ + Sdl2Application(Sdl2Application&&) = delete; + + /** @brief Copying is not allowed */ + Sdl2Application& operator=(const Sdl2Application&) = delete; + + /** @brief Moving is not allowed */ + Sdl2Application& operator=(Sdl2Application&&) = delete; /** - * @brief Key press event + * @brief Execute application main loop + * @return Value for returning from @cpp main() @ce * - * Called when an key is pressed. Default implementation does nothing. + * Calls @ref mainLoopIteration() in a loop until @ref exit() is + * called. See @ref MAGNUM_SDL2APPLICATION_MAIN() for usage + * information. */ - virtual void keyPressEvent(KeyEvent& event); + int exec(); /** - * @brief Key release event + * @brief Run one iteration of application main loop + * @return @cpp false @ce if @ref exit() was called and the application + * should exit, @cpp true @ce otherwise * - * Called when an key is released. Default implementation does nothing. + * Called internally from @ref exec(). If you want to have better + * control over how the main loop behaves, you can call this function + * yourself from your own `main()` function instead of it being called + * automatically from @ref exec() / @ref MAGNUM_SDL2APPLICATION_MAIN(). */ - virtual void keyReleaseEvent(KeyEvent& event); + bool mainLoopIteration(); - /* Since 1.8.17, the original short-hand group closing doesn't work - anymore. FFS. */ /** - * @} + * @brief Exit application + * @param exitCode The exit code the application should return + * + * When called from application constructor, it will cause the + * application to exit immediately after constructor ends, without + * entering the event loop. When called from within an event handler, + * it will cause it to exit at the start of next event loop iteration. + * Compared to requesting an application exit using the window close + * button or the @m_class{m-label m-default} **Alt** + * @m_class{m-label m-default} **F4** / + * @m_class{m-label m-default} **Cmd** + * @m_class{m-label m-default} **Q** keyboard shortcut, the + * @ref exitEvent() *isn't* called when using this function. + * + * Calling this function from an application constructor is recommended + * over @ref std::exit() or @ref Corrade::Utility::Fatal "Fatal", which + * exit without calling destructors on local scope. Note that, however, + * you need to explicitly @cpp return @ce after calling it, as it can't + * exit the constructor on its own: + * + * @snippet MagnumPlatform.cpp exit-from-constructor */ + void exit(int exitCode = 0); - /** @{ @name Mouse handling */ - - public: + #if defined(MAGNUM_TARGET_GL) && !defined(CORRADE_TARGET_EMSCRIPTEN) /** - * @brief Cursor type - * @m_since{2020,06} + * @brief Underlying OpenGL context + * @m_since{2019,10} * - * @see @ref setCursor() + * Use in case you need to call SDL functionality directly. Returns + * @cpp nullptr @ce in case the context was not created yet. + * @note This function is available only if Magnum is compiled with + * @ref MAGNUM_TARGET_GL enabled (done by default). See + * @ref building-features for more information. Not available in + * @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". */ - enum class Cursor: UnsignedInt { - Arrow, /**< Arrow */ - TextInput, /**< Text input */ - Wait, /**< Wait */ - Crosshair, /**< Crosshair */ - WaitArrow, /**< Small wait cursor */ - ResizeNWSE, /**< Double arrow pointing northwest and southeast */ - ResizeNESW, /**< Double arrow pointing northeast and southwest */ - ResizeWE, /**< Double arrow pointing west and east */ - ResizeNS, /**< Double arrow pointing north and south */ - ResizeAll, /**< Four pointed arrow pointing north, south, east, and west */ - No, /**< Slashed circle or crossbones */ - Hand, /**< Hand */ - Hidden, /**< Hidden */ + SDL_GLContext glContext() { return _glContext; } + #endif - #ifndef CORRADE_TARGET_EMSCRIPTEN - /** - * Hidden and locked. When the mouse is locked, only - * @ref MouseMoveEvent::relativePosition() is changing, absolute - * position stays the same. - * @note Not available in @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". - */ - HiddenLocked - #endif - }; + protected: + /* Nobody will need to have (and delete) Sdl2Application*, thus this is + faster than public pure virtual destructor */ + ~Sdl2Application(); + #ifdef MAGNUM_TARGET_GL /** - * @brief Set cursor type - * @m_since{2020,06} + * @brief Create a window with given configuration for OpenGL context + * @param configuration Application configuration + * @param glConfiguration OpenGL context configuration * - * Expects that a window is already created. Default is - * @ref Cursor::Arrow. - */ - void setCursor(Cursor cursor); - - /** - * @brief Get current cursor type - * @m_since{2020,06} + * Must be called only if the context wasn't created by the constructor + * itself, i.e. when passing @ref NoCreate to it. Error message is + * printed and the program exits if the context cannot be created, see + * @ref tryCreate() for an alternative. + * + * On desktop GL, if version is not specified in @p glConfiguration, + * the application first tries to create core context (OpenGL 3.2+ on + * macOS, OpenGL 3.1+ elsewhere) and if that fails, falls back to + * compatibility OpenGL 2.1 context. + * + * @note This function is available only if Magnum is compiled with + * @ref MAGNUM_TARGET_GL enabled (done by default). See + * @ref building-features for more information. */ - Cursor cursor(); + void create(const Configuration& configuration, const GLConfiguration& glConfiguration); + #endif - #ifndef CORRADE_TARGET_EMSCRIPTEN /** - * @brief Warp mouse cursor to given coordinates + * @brief Create a window with given configuration * - * @note Not available in @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". - */ - void warpCursor(const Vector2i& position) { - SDL_WarpMouseInWindow(_window, position.x(), position.y()); - } - #endif + * If @relativeref{Sdl2ApplicationWindow,Configuration::WindowFlag::Contextless} + * is present or Magnum was not built with @ref MAGNUM_TARGET_GL, this + * creates a window without any GPU context attached, leaving that part + * on the user. + * + * If none of the flags is present and Magnum was built with + * @ref MAGNUM_TARGET_GL, this is equivalent to calling + * @ref create(const Configuration&, const GLConfiguration&) with + * default-constructed @ref GLConfiguration. + * + * See also @ref building-features for more information. + */ + void create(const Configuration& configuration); - #ifdef MAGNUM_BUILD_DEPRECATED /** - * @brief Whether mouse is locked + * @brief Create a window with default configuration and OpenGL context * - * @m_deprecated_since{2020,06} Use @ref cursor() together with - * @ref Cursor::HiddenLocked instead. + * Equivalent to calling @ref create(const Configuration&) with + * default-constructed @relativeref{Sdl2ApplicationWindow,Configuration}. */ - CORRADE_DEPRECATED("use cursor() together with Cursor::HiddenLocked instead") bool isMouseLocked() const { return SDL_GetRelativeMouseMode(); } + void create(); + #ifdef MAGNUM_TARGET_GL /** - * @brief Enable or disable mouse locking + * @brief Try to create context with given configuration for OpenGL context * - * @m_deprecated_since{2020,06} Use @ref setCursor() together with - * @ref Cursor::HiddenLocked instead. + * Unlike @ref create(const Configuration&, const GLConfiguration&) + * returns @cpp false @ce if the context cannot be created, + * @cpp true @ce otherwise. + * + * @note This function is available only if Magnum is compiled with + * @ref MAGNUM_TARGET_GL enabled (done by default). See + * @ref building-features for more information. */ - CORRADE_DEPRECATED("use setCursor() together with Cursor::HiddenLocked instead") void setMouseLocked(bool enabled); + bool tryCreate(const Configuration& configuration, const GLConfiguration& glConfiguration); #endif - private: /** - * @brief Mouse press event + * @brief Try to create context with given configuration * - * Called when mouse button is pressed. Default implementation does - * nothing. + * Unlike @ref create(const Configuration&) returns @cpp false @ce if + * the context cannot be created, @cpp true @ce otherwise. */ - virtual void mousePressEvent(MouseEvent& event); + bool tryCreate(const Configuration& configuration); + + /** @{ @name Screen handling */ + public: + #if defined(CORRADE_TARGET_EMSCRIPTEN) || defined(DOXYGEN_GENERATING_OUTPUT) /** - * @brief Mouse release event + * @brief Set container CSS class * - * Called when mouse button is released. Default implementation does - * nothing. + * Assigns given CSS class to the @cb{.html}
@ce + * enclosing the application @cb{.html} @ce. Useful for + * example to change aspect ratio of the view or stretch it to cover + * the full page. See @ref platforms-html5-layout for more information + * about possible values. Note that this replaces any existing class + * (except for @cb{.css} .mn-container @ce, which is kept), to set + * multiple classes separate them with whitespace. + * + * @note Only available on @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". + * + * @m_class{m-note m-danger} + * + * @par + * For backwards compatibility purposes the function will look for + * *any* @cb{.html}
@ce in case the + * @cb{.html}
@ce is not found. This + * compatibility is scheduled to be removed in the future. */ - virtual void mouseReleaseEvent(MouseEvent& event); + void setContainerCssClass(Containers::StringView cssClass); + #endif + + /** @brief Swap interval */ + Int swapInterval() const; /** - * @brief Mouse move event + * @brief Set swap interval * - * Called when mouse is moved. Default implementation does nothing. + * Set @cpp 0 @ce for no VSync, @cpp 1 @ce for enabled VSync. Some + * platforms support @cpp -1 @ce for late swap tearing. Prints error + * message and returns @cpp false @ce if swap interval cannot be set, + * @cpp true @ce otherwise. Default is driver-dependent, you can query + * the value with @ref swapInterval(). + * @see @ref setMinimalLoopPeriod() */ - virtual void mouseMoveEvent(MouseMoveEvent& event); + bool setSwapInterval(Int interval); + #ifndef CORRADE_TARGET_EMSCRIPTEN /** - * @brief Mouse scroll event + * @brief Set minimal loop period * - * Called when a scrolling device is used (mouse wheel or scrolling - * area on a touchpad). Default implementation does nothing. + * This setting reduces the main loop frequency in case VSync is + * not/cannot be enabled or no drawing is done. Default is @cpp 0 @ce + * (i.e. looping at maximum frequency). If the application is drawing + * on the screen and VSync is enabled, this setting is ignored. + * @note Not available in @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten", + * the browser is managing the frequency instead. + * @see @ref setSwapInterval() */ - virtual void mouseScrollEvent(MouseScrollEvent& event); + void setMinimalLoopPeriod(UnsignedInt milliseconds) { + _minimalLoopPeriod = milliseconds; + } + #endif /* Since 1.8.17, the original short-hand group closing doesn't work anymore. FFS. */ @@ -1102,16 +1220,25 @@ class Sdl2Application { * @} */ - /** @{ @name Touch gesture handling */ + /** @{ @name Mouse handling */ + + #ifdef MAGNUM_BUILD_DEPRECATED + /** + * @brief Whether mouse is locked + * + * @m_deprecated_since{2020,06} Use @ref cursor() together with + * @ref Cursor::HiddenLocked instead. + */ + CORRADE_DEPRECATED("use cursor() together with Cursor::HiddenLocked instead") bool isMouseLocked() const { return SDL_GetRelativeMouseMode(); } /** - * @brief Multi gesture event + * @brief Enable or disable mouse locking * - * Called when the user performs a gesture using multiple fingers. - * Default implementation does nothing. - * @experimental + * @m_deprecated_since{2020,06} Use @ref setCursor() together with + * @ref Cursor::HiddenLocked instead. */ - virtual void multiGestureEvent(MultiGestureEvent& event); + CORRADE_DEPRECATED("use setCursor() together with Cursor::HiddenLocked instead") void setMouseLocked(bool enabled); + #endif /* Since 1.8.17, the original short-hand group closing doesn't work anymore. FFS. */ @@ -1120,7 +1247,7 @@ class Sdl2Application { */ /** @{ @name Text input handling */ - public: + /** * @brief Whether text input is active * @@ -1161,22 +1288,6 @@ class Sdl2Application { */ void setTextInputRect(const Range2Di& rect); - private: - /** - * @brief Text input event - * - * Called when text input is active and the text is being input. - * @see @ref isTextInputActive() - */ - virtual void textInputEvent(TextInputEvent& event); - - /** - * @brief Text editing event - * - * Called when text input is active and the text is being edited. - */ - virtual void textEditingEvent(TextEditingEvent& event); - /* Since 1.8.17, the original short-hand group closing doesn't work anymore. FFS. */ /** @@ -1185,6 +1296,7 @@ class Sdl2Application { /** @{ @name Special events */ + private: /** * @brief Exit event * @@ -1236,11 +1348,16 @@ class Sdl2Application { */ private: + friend Sdl2ApplicationWindow; + enum class Flag: UnsignedByte; typedef Containers::EnumSet Flags; CORRADE_ENUMSET_FRIEND_OPERATORS(Flags) - Vector2 dpiScalingInternal(Implementation::Sdl2DpiScalingPolicy configurationDpiScalingPolicy, const Vector2& configurationDpiScaling) const; + #ifndef CORRADE_TARGET_EMSCRIPTEN + void makeContextCurrent(Sdl2ApplicationWindow& window); + #endif + template void callEventHandler(std::size_t windowId, void(Sdl2ApplicationWindow::*eventHandler)(Args...), Args&&...); #ifndef CORRADE_TARGET_EMSCRIPTEN SDL_Cursor* _cursors[12]{}; @@ -1248,29 +1365,33 @@ class Sdl2Application { Cursor _cursor; #endif - /* These are saved from command-line arguments, and from configuration - to be reused in dpiScaling() and viewportEvent() later */ + /* These are saved from command-line arguments to be reused in + dpiScaling() and viewportEvent() later */ bool _verboseLog{}; - Implementation::Sdl2DpiScalingPolicy _commandLineDpiScalingPolicy{}, _configurationDpiScalingPolicy{}; - Vector2 _commandLineDpiScaling, _configurationDpiScaling; + Implementation::Sdl2DpiScalingPolicy _commandLineDpiScalingPolicy{}; + Vector2 _commandLineDpiScaling; + /* Configuration::Flags, propagated to all created windows. Can't use a + concrete type as Configuration is only a forward declaration at this + point. */ + Uint32 _configurationFlags; #ifndef CORRADE_TARGET_EMSCRIPTEN - SDL_Window* _window{}; UnsignedInt _minimalLoopPeriod; - #else - SDL_Surface* _surface{}; - Vector2i _lastKnownCanvasSize; #endif #ifdef MAGNUM_TARGET_GL #ifndef CORRADE_TARGET_EMSCRIPTEN SDL_GLContext _glContext{}; + SDL_Window* _activeGlContextWindow{}; #endif /* Has to be in an Optional because we delay-create it in a constructor with populated Arguments and it gets explicitly destroyed before the GL context */ Containers::Optional _context; #endif + #ifndef CORRADE_TARGET_EMSCRIPTEN + Containers::Array _windows; + #endif Flags _flags; @@ -1287,7 +1408,7 @@ The created window is always with a double-buffered OpenGL context. @ref MAGNUM_TARGET_GL enabled (done by default). See @ref building-features for more information. -@see @ref Sdl2Application(), @ref create(), @ref tryCreate() +@see @ref Sdl2Application(const Arguments&, const Configuration&, const GLConfiguration&), @ref create(), @ref tryCreate() */ class Sdl2Application::GLConfiguration: public GL::Context::Configuration { public: @@ -1587,12 +1708,16 @@ namespace Implementation { } /** -@brief Configuration - -@see @ref Sdl2Application(), @ref GLConfiguration, @ref create(), - @ref tryCreate() +@brief Window configuration + +Inherited by @ref Sdl2Application::Configuration which adds global options for +all windows. +@see @ref Sdl2ApplicationWindow(), + @ref Sdl2Application::Sdl2Application(const Arguments&, const Configuration&, const GLConfiguration&), + @ref Sdl2Application::GLConfiguration, @ref Sdl2Application::create(), + @ref Sdl2Application::tryCreate() */ -class Sdl2Application::Configuration { +class Sdl2ApplicationWindow::Configuration { public: /** * @brief Window flag @@ -1723,35 +1848,34 @@ class Sdl2Application::Configuration { #endif #endif + #ifdef MAGNUM_BUILD_DEPRECATED /** - * Do not create any GPU context. Use together with - * @ref Sdl2Application(const Arguments&, const Configuration&), - * @ref create(const Configuration&) or - * @ref tryCreate(const Configuration&) to prevent implicit - * creation of an OpenGL context. + * Do not create any GPU context. + * @m_deprecated_since_latest Use the application-global + * @ref Sdl2Application::Configuration::Flag::Contextless + * instead. */ - Contextless = 1u << 31, /* Hope this won't ever conflict with anything */ + Contextless CORRADE_DEPRECATED_ENUM("use Sdl2Application::Configuration::Flag::Contextless instead") = 1u << 31, /** - * Request a window for use with OpenGL. Useful in combination with - * @ref WindowFlag::Contextless, otherwise enabled implicitly when - * creating an OpenGL context using @ref Sdl2Application(const Arguments&), - * @ref Sdl2Application(const Arguments&, const Configuration&, const GLConfiguration&), - * @ref create(const Configuration&, const GLConfiguration&) or - * @ref tryCreate(const Configuration&, const GLConfiguration&). - * @m_since{2019,10} + * Request a window for use with OpenGL. + * @m_deprecated_since_latest Use the application-global + * @ref Sdl2Application::Configuration::Flag::OpenGL + * instead. */ - OpenGL = SDL_WINDOW_OPENGL, + OpenGL CORRADE_DEPRECATED_ENUM("use Sdl2Application::Configuration::Flag::OpenGL instead") = SDL_WINDOW_OPENGL, #if !defined(CORRADE_TARGET_EMSCRIPTEN) && (SDL_MAJOR_VERSION*1000 + SDL_MINOR_VERSION*100 + SDL_PATCHLEVEL >= 2006 || defined(DOXYGEN_GENERATING_OUTPUT)) /** - * Request a window for use with Vulkan. Useful in combination with - * @ref WindowFlag::Contextless. + * Request a window for use with Vulkan. + * @m_deprecated_since_latest Use the application-global + * @ref Sdl2Application::Configuration::Flag::Vulkan + * instead. * @note Available since SDL 2.0.6, not available on * @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". - * @m_since{2019,10} */ - Vulkan = SDL_WINDOW_VULKAN + Vulkan CORRADE_DEPRECATED_ENUM("use Sdl2Application::Configuration::Flag::Vulkan instead") = SDL_WINDOW_VULKAN + #endif #endif }; @@ -1760,20 +1884,7 @@ class Sdl2Application::Configuration { * * @see @ref setWindowFlags() */ - #ifndef DOXYGEN_GENERATING_OUTPUT - typedef Containers::EnumSet= 2006 - |SDL_WINDOW_VULKAN - #endif - > WindowFlags; - #else typedef Containers::EnumSet WindowFlags; - #endif /** * @brief DPI scaling policy @@ -1984,6 +2095,142 @@ class Sdl2Application::Configuration { Vector2 _dpiScaling; }; +CORRADE_ENUMSET_OPERATORS(Sdl2ApplicationWindow::Configuration::WindowFlags) + +/** +@brief Application configuration + +Inherits @ref Sdl2ApplicationWindow::Configuration. +@see @ref Sdl2Application(), @ref create(), @ref tryCreate() +*/ +class Sdl2Application::Configuration: public Sdl2ApplicationWindow::Configuration { + public: + /** + * @brief Application flag + * @m_since_latest + * + * @see @ref Flags, @ref setFlags() + */ + enum class Flag: Uint32 { + /** + * Do not create any GPU context. Use together with + * @ref Sdl2Application(const Arguments&, const Configuration&), + * @ref create(const Configuration&) or + * @ref tryCreate(const Configuration&) to prevent implicit + * creation of an OpenGL context. Can't be used with + * @ref Sdl2Application(const Arguments&, const Configuration&, const GLConfiguration&), + * @ref create(const Configuration&, const GLConfiguration&) or + * @ref tryCreate(const Configuration&, const GLConfiguration&). + */ + Contextless = 1u << 31, /* Hope this won't ever conflict with anything */ + + /** + * Request a window for use with OpenGL. Useful in combination with + * @ref Flag::Contextless, otherwise enabled implicitly when + * creating an OpenGL context using + * @ref Sdl2Application(const Arguments&, const Configuration&, const GLConfiguration&), + * @ref create(const Configuration&, const GLConfiguration&) + * or @ref tryCreate(const Configuration&, const GLConfiguration&). + * @m_since{2019,10} + */ + OpenGL = SDL_WINDOW_OPENGL, + + #if !defined(CORRADE_TARGET_EMSCRIPTEN) && (SDL_MAJOR_VERSION*1000 + SDL_MINOR_VERSION*100 + SDL_PATCHLEVEL >= 2006 || defined(DOXYGEN_GENERATING_OUTPUT)) + /** + * Request a window for use with Vulkan. Useful in combination with + * @ref Flag::Contextless. + * @note Available since SDL 2.0.6, not available on + * @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". + * @m_since{2019,10} + */ + Vulkan = SDL_WINDOW_VULKAN + #endif + }; + + /** + * @brief Application flags + * @m_since_latest + * + * @see @ref setFlags() + */ + typedef Containers::EnumSet Flags; + + explicit Configuration() = default; + + /** + * @brief Application flags + * @m_since_latest + */ + Flags flags() const { return _flags; } + + /** + * @brief Set application flags + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Default are none. To avoid clearing default flags by accident, + * prefer to use @ref addFlags() and @ref clearFlags() instead. + */ + Configuration& setFlags(Flags flags) { + _flags = flags; + return *this; + } + + /** + * @brief Add application flags + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Unlike @ref setFlags(), ORs the flags with existing instead of + * replacing them. Useful for preserving the defaults. + * @see @ref clearFlags() + */ + Configuration& addFlags(Flags flags) { + _flags |= flags; + return *this; + } + + /** + * @brief Clear application flags + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Unlike @ref setFlags(), ANDs the inverse of @p flags with existing + * instead of replacing them. Useful for removing default flags. + * @see @ref addFlags() + */ + Configuration& clearFlags(Flags flags) { + _flags &= ~flags; + return *this; + } + + /* Overloads to remove a WTF factor from method chaining order */ + #ifndef DOXYGEN_GENERATING_OUTPUT + Configuration& setTitle(Containers::StringView title) { + Sdl2ApplicationWindow::Configuration::setTitle(title); + return *this; + } + Configuration& setSize(const Vector2i& size, DpiScalingPolicy dpiScalingPolicy = DpiScalingPolicy::Default) { + Sdl2ApplicationWindow::Configuration::setSize(size, dpiScalingPolicy); + return *this; + } + Configuration& setSize(const Vector2i& size, const Vector2& dpiScaling) { + Sdl2ApplicationWindow::Configuration::setSize(size, dpiScaling); + return *this; + } + /* On deprecated builds these propagate the appropriate context flags + to setFlags() / addFlags() / clearFlags() */ + Configuration& setWindowFlags(WindowFlags flags); + Configuration& addWindowFlags(WindowFlags flags); + Configuration& clearWindowFlags(WindowFlags flags); + #endif + + private: + Flags _flags; +}; + +CORRADE_ENUMSET_OPERATORS(Sdl2Application::Configuration::Flags) + /** @brief Exit event @@ -2037,7 +2284,7 @@ class Sdl2Application::ExitEvent { @see @ref viewportEvent() */ -class Sdl2Application::ViewportEvent { +class Sdl2ApplicationWindow::ViewportEvent { public: /** @brief Copying is not allowed */ ViewportEvent(const ViewportEvent&) = delete; @@ -2138,7 +2385,7 @@ class Sdl2Application::ViewportEvent { @ref keyReleaseEvent(), @ref mousePressEvent(), @ref mouseReleaseEvent(), @ref mouseMoveEvent() */ -class Sdl2Application::InputEvent { +class Sdl2ApplicationWindow::InputEvent { public: /** * @brief Modifier @@ -2247,7 +2494,7 @@ class Sdl2Application::InputEvent { @see @ref keyPressEvent(), @ref keyReleaseEvent() */ -class Sdl2Application::KeyEvent: public Sdl2Application::InputEvent { +class Sdl2ApplicationWindow::KeyEvent: public InputEvent { public: /** * @brief Key @@ -2545,7 +2792,7 @@ class Sdl2Application::KeyEvent: public Sdl2Application::InputEvent { @see @ref MouseMoveEvent, @ref MouseScrollEvent, @ref mousePressEvent(), @ref mouseReleaseEvent() */ -class Sdl2Application::MouseEvent: public Sdl2Application::InputEvent { +class Sdl2ApplicationWindow::MouseEvent: public InputEvent { public: /** * @brief Mouse button @@ -2612,7 +2859,7 @@ class Sdl2Application::MouseEvent: public Sdl2Application::InputEvent { @see @ref MouseEvent, @ref MouseScrollEvent, @ref mouseMoveEvent() */ -class Sdl2Application::MouseMoveEvent: public Sdl2Application::InputEvent { +class Sdl2ApplicationWindow::MouseMoveEvent: public InputEvent { public: /** * @brief Mouse button @@ -2673,7 +2920,7 @@ class Sdl2Application::MouseMoveEvent: public Sdl2Application::InputEvent { @see @ref MouseEvent, @ref MouseMoveEvent, @ref mouseScrollEvent() */ -class Sdl2Application::MouseScrollEvent: public Sdl2Application::InputEvent { +class Sdl2ApplicationWindow::MouseScrollEvent: public InputEvent { public: /** @brief Scroll offset */ Vector2 offset() const { return _offset; } @@ -2708,7 +2955,7 @@ class Sdl2Application::MouseScrollEvent: public Sdl2Application::InputEvent { @experimental @see @ref multiGestureEvent() */ -class Sdl2Application::MultiGestureEvent { +class Sdl2ApplicationWindow::MultiGestureEvent { public: /** @brief Copying is not allowed */ MultiGestureEvent(const MultiGestureEvent&) = delete; @@ -2785,7 +3032,7 @@ class Sdl2Application::MultiGestureEvent { @see @ref TextEditingEvent, @ref textInputEvent() */ -class Sdl2Application::TextInputEvent { +class Sdl2ApplicationWindow::TextInputEvent { public: /** @brief Copying is not allowed */ TextInputEvent(const TextInputEvent&) = delete; @@ -2843,7 +3090,7 @@ class Sdl2Application::TextInputEvent { @see @ref textEditingEvent() */ -class Sdl2Application::TextEditingEvent { +class Sdl2ApplicationWindow::TextEditingEvent { public: /** @brief Copying is not allowed */ TextEditingEvent(const TextEditingEvent&) = delete; @@ -2951,6 +3198,7 @@ When no other application header is included this macro is also aliased to #ifndef DOXYGEN_GENERATING_OUTPUT #ifndef MAGNUM_APPLICATION_MAIN typedef Sdl2Application Application; +typedef Sdl2ApplicationWindow ApplicationWindow; typedef BasicScreen Screen; typedef BasicScreenedApplication ScreenedApplication; #define MAGNUM_APPLICATION_MAIN(className) MAGNUM_SDL2APPLICATION_MAIN(className) @@ -2959,9 +3207,8 @@ typedef BasicScreenedApplication ScreenedApplication; #endif #endif -CORRADE_ENUMSET_OPERATORS(Sdl2Application::Configuration::WindowFlags) -CORRADE_ENUMSET_OPERATORS(Sdl2Application::InputEvent::Modifiers) -CORRADE_ENUMSET_OPERATORS(Sdl2Application::MouseMoveEvent::Buttons) +CORRADE_ENUMSET_OPERATORS(Sdl2ApplicationWindow::InputEvent::Modifiers) +CORRADE_ENUMSET_OPERATORS(Sdl2ApplicationWindow::MouseMoveEvent::Buttons) }} diff --git a/src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp b/src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp index cf38df4833..8076aca194 100644 --- a/src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp +++ b/src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp @@ -23,9 +23,11 @@ DEALINGS IN THE SOFTWARE. */ +#include #include #include #include +#include #include #include "Magnum/ImageView.h" @@ -54,6 +56,106 @@ namespace Magnum { namespace Platform { namespace Test { namespace { using namespace Containers::Literals; +#ifndef CORRADE_TARGET_EMSCRIPTEN +struct Sdl2ApplicationTestWindow: Platform::ApplicationWindow { + explicit Sdl2ApplicationTestWindow(Platform::Application& application, std::size_t id): Platform::ApplicationWindow{application, Configuration{} + .setTitle(Utility::format("Window {}", id)) + .setSize({400, 300}) + .addWindowFlags(Configuration::WindowFlag::Resizable) + }, _id{id} {} + + void viewportEvent(ViewportEvent& event) override { + Debug{} << "window" << _id << "viewport event" << event.windowSize() + #ifdef MAGNUM_TARGET_GL + << event.framebufferSize() + #endif + << event.dpiScaling(); +#warning query the GL fb size here and compare + // GL::defaultFramebuffer.setViewport({{}, event.framebufferSize()}); + } + + void drawEvent() override { + Debug{} << "window" << _id << "draw event"; + #ifdef MAGNUM_TARGET_GL + GL::defaultFramebuffer.clear(GL::FramebufferClear::Color); + #endif + + swapBuffers(); + } + + /* For testing event coordinates */ + void mousePressEvent(MouseEvent& event) override { + Debug{} << "window" << _id << "mouse press event:" << event.position() << Int(event.button()); + } + + void mouseReleaseEvent(MouseEvent& event) override { + Debug{} << "window" << _id << "mouse release event:" << event.position() << Int(event.button()); + } + + void mouseMoveEvent(MouseMoveEvent& event) override { + Debug{} << "window" << _id << "mouse move event:" << event.position() << event.relativePosition() << Uint32(event.buttons()); + } + + void mouseScrollEvent(MouseScrollEvent& event) override { + Debug{} << "window" << _id << "mouse scroll event:" << event.offset() << event.position(); + } + + void keyPressEvent(KeyEvent& event) override { + Debug{} << "window" << _id << "key press event:" << SDL_Keycode(event.key()) << event.keyName(); + + if(event.key() == KeyEvent::Key::F1) { + Debug{} << "starting text input"; + application().startTextInput(); + } else if(event.key() == KeyEvent::Key::Esc) { + Debug{} << "stopping text input"; + application().stopTextInput(); + } else if(event.key() == KeyEvent::Key::T) { + Debug{} << "setting window title"; + setWindowTitle("This is a UTF-8 Window Title™ and it should have no exclamation mark!!"_s.exceptSuffix(2)); + } + #ifndef CORRADE_TARGET_EMSCRIPTEN + else if(event.key() == KeyEvent::Key::S) { + Debug{} << "setting window size, which should trigger a viewport event"; + setWindowSize(Vector2i{300, 200}); + } else if(event.key() == KeyEvent::Key::W) { + Debug{} << "setting max window size, which should trigger a viewport event"; + setMaxWindowSize(Vector2i{700, 500}); + } + #endif + else if(event.key() == KeyEvent::Key::H) { + Debug{} << "toggling hand cursor"; + setCursor(cursor() == Cursor::Arrow ? Cursor::Hand : Cursor::Arrow); + } + #ifndef CORRADE_TARGET_EMSCRIPTEN + else if(event.key() == KeyEvent::Key::L) { + Debug{} << "toggling locked mouse"; + setCursor(cursor() == Cursor::Arrow ? Cursor::HiddenLocked : Cursor::Arrow); + } + #else + else if(event.key() == KeyEvent::Key::F) { + Debug{} << "toggling fullscreen"; + setContainerCssClass((_fullscreen ^= true) ? "mn-fullsize" : ""); + } + #endif + else if(event.key() == KeyEvent::Key::X) { + Debug{} << "requesting an exit with code 5"; + exit(5); + } + } + + void keyReleaseEvent(KeyEvent& event) override { + Debug{} << "window" << _id << "key release event:" << SDL_Keycode(event.key()) << event.keyName(); + } + + void textInputEvent(TextInputEvent& event) override { + Debug{} << "window" << _id << "text input event:" << event.text(); + } + + private: + std::size_t _id; +}; +#endif + struct Sdl2ApplicationTest: Platform::Application { explicit Sdl2ApplicationTest(const Arguments& arguments); @@ -118,6 +220,10 @@ struct Sdl2ApplicationTest: Platform::Application { Debug{} << "setting max window size, which should trigger a viewport event"; setMaxWindowSize(Vector2i{700, 500}); } + else if(event.key() == KeyEvent::Key::O) { + Debug{} << "opening window" << _windows.size(); + arrayAppend(_windows, Containers::pointer(*this, _windows.size())); + } #endif else if(event.key() == KeyEvent::Key::H) { Debug{} << "toggling hand cursor"; @@ -157,10 +263,12 @@ struct Sdl2ApplicationTest: Platform::Application { if(event.type == SDL_WINDOWEVENT) d << event.window.event; } - #ifdef CORRADE_TARGET_EMSCRIPTEN private: + #ifndef CORRADE_TARGET_EMSCRIPTEN + Containers::Array> _windows; + #else bool _fullscreen = false; - #endif + #endif }; Sdl2ApplicationTest::Sdl2ApplicationTest(const Arguments& arguments): Platform::Application{arguments, NoCreate} { diff --git a/src/Magnum/Platform/WindowlessCglApplication.cpp b/src/Magnum/Platform/WindowlessCglApplication.cpp index f3f514c915..01a5ce9b17 100644 --- a/src/Magnum/Platform/WindowlessCglApplication.cpp +++ b/src/Magnum/Platform/WindowlessCglApplication.cpp @@ -109,10 +109,6 @@ WindowlessCglContext::Configuration::Configuration() { GL::Context::Configuration::addFlags(GL::Context::Configuration::Flag::Windowless); } -#ifndef DOXYGEN_GENERATING_OUTPUT -WindowlessCglApplication::WindowlessCglApplication(const Arguments& arguments): WindowlessCglApplication{arguments, Configuration{}} {} -#endif - WindowlessCglApplication::WindowlessCglApplication(const Arguments& arguments, const Configuration& configuration): WindowlessCglApplication{arguments, NoCreate} { createContext(configuration); } diff --git a/src/Magnum/Platform/WindowlessCglApplication.h b/src/Magnum/Platform/WindowlessCglApplication.h index 4658f391a0..10b677f154 100644 --- a/src/Magnum/Platform/WindowlessCglApplication.h +++ b/src/Magnum/Platform/WindowlessCglApplication.h @@ -390,13 +390,7 @@ class WindowlessCglApplication { * alternative. * @see @ref WindowlessCglContext */ - #ifdef DOXYGEN_GENERATING_OUTPUT - explicit WindowlessCglApplication(const Arguments& arguments, const Configuration& configuration = Configuration()); - #else - /* To avoid "invalid use of incomplete type" */ - explicit WindowlessCglApplication(const Arguments& arguments, const Configuration& configuration); - explicit WindowlessCglApplication(const Arguments& arguments); - #endif + explicit WindowlessCglApplication(const Arguments& arguments, const Configuration& configuration = Configuration{}); /** * @brief Construct without creating the context diff --git a/src/Magnum/Platform/WindowlessEglApplication.cpp b/src/Magnum/Platform/WindowlessEglApplication.cpp index 17b391b1d5..db759db194 100644 --- a/src/Magnum/Platform/WindowlessEglApplication.cpp +++ b/src/Magnum/Platform/WindowlessEglApplication.cpp @@ -686,10 +686,6 @@ WindowlessEglContext::Configuration::Configuration() #endif } -#ifndef DOXYGEN_GENERATING_OUTPUT -WindowlessEglApplication::WindowlessEglApplication(const Arguments& arguments): WindowlessEglApplication{arguments, Configuration{}} {} -#endif - WindowlessEglApplication::WindowlessEglApplication(const Arguments& arguments, const Configuration& configuration): WindowlessEglApplication{arguments, NoCreate} { createContext(configuration); } diff --git a/src/Magnum/Platform/WindowlessEglApplication.h b/src/Magnum/Platform/WindowlessEglApplication.h index 6b747c3d56..9b7114537d 100644 --- a/src/Magnum/Platform/WindowlessEglApplication.h +++ b/src/Magnum/Platform/WindowlessEglApplication.h @@ -637,13 +637,7 @@ class WindowlessEglApplication { * alternative. * @see @ref WindowlessEglContext */ - #ifdef DOXYGEN_GENERATING_OUTPUT - explicit WindowlessEglApplication(const Arguments& arguments, const Configuration& configuration = Configuration()); - #else - /* To avoid "invalid use of incomplete type" */ - explicit WindowlessEglApplication(const Arguments& arguments, const Configuration& configuration); - explicit WindowlessEglApplication(const Arguments& arguments); - #endif + explicit WindowlessEglApplication(const Arguments& arguments, const Configuration& configuration = Configuration{}); /** * @brief Constructor diff --git a/src/Magnum/Platform/WindowlessGlxApplication.cpp b/src/Magnum/Platform/WindowlessGlxApplication.cpp index a472f9343c..acfcd74769 100644 --- a/src/Magnum/Platform/WindowlessGlxApplication.cpp +++ b/src/Magnum/Platform/WindowlessGlxApplication.cpp @@ -323,10 +323,6 @@ WindowlessGlxContext::Configuration::Configuration() { #endif } -#ifndef DOXYGEN_GENERATING_OUTPUT -WindowlessGlxApplication::WindowlessGlxApplication(const Arguments& arguments): WindowlessGlxApplication{arguments, Configuration{}} {} -#endif - WindowlessGlxApplication::WindowlessGlxApplication(const Arguments& arguments, const Configuration& configuration): WindowlessGlxApplication{arguments, NoCreate} { createContext(configuration); } diff --git a/src/Magnum/Platform/WindowlessGlxApplication.h b/src/Magnum/Platform/WindowlessGlxApplication.h index d0f1b718d4..9b84aa5827 100644 --- a/src/Magnum/Platform/WindowlessGlxApplication.h +++ b/src/Magnum/Platform/WindowlessGlxApplication.h @@ -448,13 +448,7 @@ class WindowlessGlxApplication { * alternative. * @see @ref WindowlessGlxContext */ - #ifdef DOXYGEN_GENERATING_OUTPUT - explicit WindowlessGlxApplication(const Arguments& arguments, const Configuration& configuration = Configuration()); - #else - /* To avoid "invalid use of incomplete type" */ - explicit WindowlessGlxApplication(const Arguments& arguments, const Configuration& configuration); - explicit WindowlessGlxApplication(const Arguments& arguments); - #endif + explicit WindowlessGlxApplication(const Arguments& arguments, const Configuration& configuration = Configuration{}); /** * @brief Constructor diff --git a/src/Magnum/Platform/WindowlessIosApplication.h b/src/Magnum/Platform/WindowlessIosApplication.h index 6d9ff95279..d12a0feea7 100644 --- a/src/Magnum/Platform/WindowlessIosApplication.h +++ b/src/Magnum/Platform/WindowlessIosApplication.h @@ -356,13 +356,7 @@ class WindowlessIosApplication { * alternative. * @see @ref WindowlessIosContext */ - #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 + explicit WindowlessIosApplication(const Arguments& arguments, const Configuration& configuration = Configuration{}); /** * @brief Constructor diff --git a/src/Magnum/Platform/WindowlessIosApplication.mm b/src/Magnum/Platform/WindowlessIosApplication.mm index af257530b1..a12f601984 100644 --- a/src/Magnum/Platform/WindowlessIosApplication.mm +++ b/src/Magnum/Platform/WindowlessIosApplication.mm @@ -87,10 +87,6 @@ copy of this software and associated documentation files (the "Software"), GL::Context::Configuration::addFlags(GL::Context::Configuration::Flag::Windowless); } -#ifndef DOXYGEN_GENERATING_OUTPUT -WindowlessIosApplication::WindowlessIosApplication(const Arguments& arguments): WindowlessIosApplication{arguments, Configuration{}} {} -#endif - WindowlessIosApplication::WindowlessIosApplication(const Arguments& arguments, const Configuration& configuration): WindowlessIosApplication{arguments, NoCreate} { createContext(configuration); } diff --git a/src/Magnum/Platform/WindowlessWglApplication.cpp b/src/Magnum/Platform/WindowlessWglApplication.cpp index cf181b6657..9d28fa41b1 100644 --- a/src/Magnum/Platform/WindowlessWglApplication.cpp +++ b/src/Magnum/Platform/WindowlessWglApplication.cpp @@ -308,10 +308,6 @@ WindowlessWglContext::Configuration::Configuration() { #endif } -#ifndef DOXYGEN_GENERATING_OUTPUT -WindowlessWglApplication::WindowlessWglApplication(const Arguments& arguments): WindowlessWglApplication{arguments, Configuration{}} {} -#endif - WindowlessWglApplication::WindowlessWglApplication(const Arguments& arguments, const Configuration& configuration): WindowlessWglApplication{arguments, NoCreate} { createContext(configuration); } diff --git a/src/Magnum/Platform/WindowlessWglApplication.h b/src/Magnum/Platform/WindowlessWglApplication.h index 7fb464ba72..31314dcf77 100644 --- a/src/Magnum/Platform/WindowlessWglApplication.h +++ b/src/Magnum/Platform/WindowlessWglApplication.h @@ -434,13 +434,7 @@ class WindowlessWglApplication { * alternative. * @see @ref WindowlessWglContext */ - #ifdef DOXYGEN_GENERATING_OUTPUT - explicit WindowlessWglApplication(const Arguments& arguments, const Configuration& configuration = Configuration()); - #else - /* To avoid "invalid use of incomplete type" */ - explicit WindowlessWglApplication(const Arguments& arguments, const Configuration& configuration); - explicit WindowlessWglApplication(const Arguments& arguments); - #endif + explicit WindowlessWglApplication(const Arguments& arguments, const Configuration& configuration = Configuration{}); /** * @brief Constructor diff --git a/src/Magnum/Platform/XEglApplication.h b/src/Magnum/Platform/XEglApplication.h index 53cbfc0677..7feb6af0ea 100644 --- a/src/Magnum/Platform/XEglApplication.h +++ b/src/Magnum/Platform/XEglApplication.h @@ -104,7 +104,7 @@ If no other application header is included, this class is also aliased to class XEglApplication: public AbstractXApplication { public: /** - * @brief Construct with given configuration for OpenGL context + * @brief Construct with an OpenGL context * @param arguments Application arguments * @param configuration Application configuration * @param glConfiguration OpenGL context configuration diff --git a/src/Magnum/SceneGraph/Camera.h b/src/Magnum/SceneGraph/Camera.h index 4f8abcca8a..085639814c 100644 --- a/src/Magnum/SceneGraph/Camera.h +++ b/src/Magnum/SceneGraph/Camera.h @@ -156,7 +156,7 @@ template class Camera: public AbstractFeature