Browse Source

[wip] Platform: create a Sdl2ApplicationWindow base class.

Mostly just code motion and related documentation updates because
Doxygen is such a crap that it can link to functions defined in a base
class but not types! FFS.

No actual multi-window support yet, that'll be in the next commits to
avoid this noise obscuring them.

TODO: Sdl2Window instead?
TODO: unsure about certain APIs, whether they should be per-window or
not
 - contextless / gl / vulkan?
 - mouse warp?
 - ...?
pull/168/head
Vladimír Vondruš 3 years ago
parent
commit
a902b8475d
  1. 31
      doc/changelog-old.dox
  2. 53
      doc/changelog.dox
  3. 6
      doc/platform.dox
  4. 4
      src/Magnum/Platform/AndroidApplication.h
  5. 18
      src/Magnum/Platform/Screen.h
  6. 779
      src/Magnum/Platform/Sdl2Application.cpp
  7. 973
      src/Magnum/Platform/Sdl2Application.h
  8. 2
      src/Magnum/SceneGraph/Camera.h

31
doc/changelog-old.dox

@ -108,7 +108,7 @@ Released 2018-05-01, tagged as
- Ability to create @ref Platform::Sdl2Application and - Ability to create @ref Platform::Sdl2Application and
@ref Platform::GlfwApplication classes without implicitly created OpenGL @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. to them. These two can be now also built completely without the GL library.
- Added @ref Platform::AndroidApplication::windowSize() - Added @ref Platform::AndroidApplication::windowSize()
- Added @ref Platform::AndroidApplication::nativeActivity() to access - 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 @subsubsection changelog-2018-04-changes-platform Platform libraries
- Separated @ref Platform::Sdl2Application::Configuration "Platform::*Application::Configuration" - Separated @cpp Platform::*Application::Configuration @ce
into @ref Platform::Sdl2Application::Configuration "Configuration" and into @cpp Configuration @ce and @cpp GLConfiguration @ce to allow creation
@ref Platform::Sdl2Application::GLConfiguration "GLConfiguration" to allow of non-GL application instances in the future
creation of non-GL application instances in the future
@subsubsection changelog-2018-04-changes-plugins Plugins @subsubsection changelog-2018-04-changes-plugins Plugins
@ -753,10 +752,10 @@ a high-level overview.
possible possible
- Ported @ref magnum-gl-info "magnum-info" to Emscripten - Ported @ref magnum-gl-info "magnum-info" to Emscripten
- First-class support for scroll events in - First-class support for scroll events in
@ref Platform::Sdl2Application::MouseScrollEvent (see @cpp Platform::Sdl2Application::MouseScrollEvent @ce
[mosra/magnum#157](https://github.com/mosra/magnum/pull/157)) (see [mosra/magnum#157](https://github.com/mosra/magnum/pull/157))
- Added @ref Platform::Sdl2Application::MouseEvent::clickCount() - Added @cpp Platform::Sdl2Application::MouseEvent::clickCount() @ce
- Added @ref Platform::Sdl2Application::multiGestureEvent() - Added @cpp Platform::Sdl2Application::multiGestureEvent() @ce
- Exposing key repeat in - Exposing key repeat in
@ref Platform::Sdl2Application::KeyEvent::isRepeated() "Platform::*Application::KeyEvent::isRepeated()" @ref Platform::Sdl2Application::KeyEvent::isRepeated() "Platform::*Application::KeyEvent::isRepeated()"
(see [mosra/magnum#161](https://github.com/mosra/magnum/issues/161), (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 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)) application) (see [mosra/magnum#105](https://github.com/mosra/magnum/issues/105))
- Added @cpp Platform::Sdl2Application::Configuration::setSRGBCapable() @ce - Added @cpp Platform::Sdl2Application::Configuration::setSRGBCapable() @ce
- Added @ref Platform::Sdl2Application::Configuration::WindowFlag::Borderless - Added @cpp Platform::Sdl2Application::Configuration::WindowFlag::Borderless @ce
and `Platform::Sdl2Application::Configuration::WindowFlag::AllowHighDpi` and @cpp Platform::Sdl2Application::Configuration::WindowFlag::AllowHighDpi @ce
for iOS and macOS for iOS and macOS
- Added @ref Platform::WindowlessGlxApplication::Configuration::setFlags() "Platform::Windowless*Application::Configuration::setFlags()" with - Added @ref Platform::WindowlessGlxApplication::Configuration::setFlags() "Platform::Windowless*Application::Configuration::setFlags()" with
@ref Platform::WindowlessGlxApplication::Configuration::Flag::Debug "Flag::Debug" @ref Platform::WindowlessGlxApplication::Configuration::Flag::Debug "Flag::Debug"
@ -786,7 +785,7 @@ a high-level overview.
with @ref Platform::GlfwApplication with @ref Platform::GlfwApplication
- Added modifier keys to - Added modifier keys to
@ref Platform::Sdl2Application::KeyEvent::Key "Platform::*Application::KeyEvent::Key" @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 consistent with @ref Platform::GlfwApplication (see
[mosra/magnum#159](https://github.com/mosra/magnum/pull/159)) [mosra/magnum#159](https://github.com/mosra/magnum/pull/159))
- Added @ref Platform::Sdl2Application::KeyEvent::keyName() "Platform::*Application::KeyEvent::keyName()" - 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 `Platform::GlfwApplication::MouseEvent::Button::WheelDown` mouse events are
deprecated, use @ref Platform::Sdl2Application::mouseScrollEvent() / deprecated, use @ref Platform::Sdl2Application::mouseScrollEvent() /
@ref Platform::GlfwApplication::mouseScrollEvent() and @ref Platform::GlfwApplication::mouseScrollEvent() and
@ref Platform::Sdl2Application::MouseScrollEvent / @cpp Platform::Sdl2Application::MouseScrollEvent @ce /
@ref Platform::GlfwApplication::MouseScrollEvent instead @ref Platform::GlfwApplication::MouseScrollEvent instead
- @ref Platform::Sdl2Application::Sdl2Application() "Platform::*Application::*Application()" - @cpp Platform::*Application::*Application() @ce and
and @ref Platform::WindowlessGlxApplication::WindowlessGlxApplication() "Platform::Windowless*Application::Windowless*Application()" @ref Platform::WindowlessGlxApplication::WindowlessGlxApplication() "Platform::Windowless*Application::Windowless*Application()"
constructors taking @cpp nullptr @ce are deprecated, use constructors constructors taking @cpp nullptr @ce are deprecated, use constructors
taking @ref NoCreateT instead to create an application without creating taking @ref NoCreateT instead to create an application without creating
OpenGL context OpenGL context
@ -1844,7 +1843,7 @@ for a high-level overview.
purely integral coordinates, useful e.g. for UI or 2D platformers. purely integral coordinates, useful e.g. for UI or 2D platformers.
- Detailed collision queries and new `InvertedSphere` shape in Shapes library - Detailed collision queries and new `InvertedSphere` shape in Shapes library
- Texture support in @cpp Shaders::Flat @ce - 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 @subsection changelog-2013-10-changes Changes

53
doc/changelog.dox

@ -732,13 +732,13 @@ See also:
- Added @ref Platform::EmscriptenApplication::Configuration::addWindowFlags() - Added @ref Platform::EmscriptenApplication::Configuration::addWindowFlags()
and @ref Platform::EmscriptenApplication::Configuration::clearWindowFlags() and @ref Platform::EmscriptenApplication::Configuration::clearWindowFlags()
for consistency with other application implementations for consistency with other application implementations
- Added @ref Platform::Sdl2Application::KeyEvent::Key::CapsLock, - Added @ref Platform::Sdl2ApplicationWindow::KeyEvent::Key::CapsLock,
@relativeref{Platform::Sdl2Application::KeyEvent::Key,ScrollLock}, @relativeref{Platform::Sdl2ApplicationWindow::KeyEvent::Key,ScrollLock},
@relativeref{Platform::Sdl2Application::KeyEvent::Key,NumLock}, @relativeref{Platform::Sdl2ApplicationWindow::KeyEvent::Key,NumLock},
@relativeref{Platform::Sdl2Application::KeyEvent::Key,PrintScreen}, @relativeref{Platform::Sdl2ApplicationWindow::KeyEvent::Key,PrintScreen},
@relativeref{Platform::Sdl2Application::KeyEvent::Key,Pause} and @relativeref{Platform::Sdl2ApplicationWindow::KeyEvent::Key,Pause} and
@relativeref{Platform::Sdl2Application::KeyEvent::Key,Menu} for consistency @relativeref{Platform::Sdl2ApplicationWindow::KeyEvent::Key,Menu} for
with @ref Platform::EmscriptenApplication and consistency with @ref Platform::EmscriptenApplication and
@ref Platform::GlfwApplication (see [mosra/magnum#547](https://github.com/mosra/magnum/pull/547)) @ref Platform::GlfwApplication (see [mosra/magnum#547](https://github.com/mosra/magnum/pull/547))
- @ref Platform::Sdl2Application now overrides SDL's default behavior that - @ref Platform::Sdl2Application now overrides SDL's default behavior that
prevents computer from entering a power-saving mode while the application 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 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 current" on certain system. This was due to EGL not destroying the context
if it's still made current. if it's still made current.
- Fixed handling of @ref Platform::Sdl2Application::InputEvent::Modifier::Super, - Fixed handling of @ref Platform::Sdl2ApplicationWindow::InputEvent::Modifier::Super,
which was misreported as @relativeref{Platform::Sdl2Application::InputEvent::Modifier,Alt} (see which was misreported as @relativeref{Platform::Sdl2ApplicationWindow::InputEvent::Modifier,Alt}
[mosra/magnum#547](https://github.com/mosra/magnum/pull/547)) (see [mosra/magnum#547](https://github.com/mosra/magnum/pull/547))
- For meshes with multiple sets of vertex attributes (such as texture - For meshes with multiple sets of vertex attributes (such as texture
coordinates), @ref MeshTools::compile() should be using only the first set coordinates), @ref MeshTools::compile() should be using only the first set
but it wasn't. but it wasn't.
@ -1969,12 +1969,12 @@ Released 2020-06-27, tagged as
@ref Platform::AbstractXApplication::mainLoopIteration() for consistency @ref Platform::AbstractXApplication::mainLoopIteration() for consistency
with @ref Platform::Sdl2Application (see with @ref Platform::Sdl2Application (see
[mosra/magnum#387](https://github.com/mosra/magnum/pull/387)) [mosra/magnum#387](https://github.com/mosra/magnum/pull/387))
- Added @ref Platform::Sdl2Application::KeyEvent::Key::Quote "Key::Quote", - Added @relativeref{Platform::Sdl2ApplicationWindow::KeyEvent,Key::Quote},
@ref Platform::Sdl2Application::KeyEvent::Key::LeftBracket "Key::LeftBracket", @relativeref{Platform::Sdl2ApplicationWindow::KeyEvent,Key::LeftBracket},
@ref Platform::Sdl2Application::KeyEvent::Key::RightBracket "Key::RightBracket", @relativeref{Platform::Sdl2ApplicationWindow::KeyEvent,Key::RightBracket},
@ref Platform::Sdl2Application::KeyEvent::Key::Backslash "Key::Backslash" and @relativeref{Platform::Sdl2ApplicationWindow::KeyEvent,Key::Backslash} and
@ref Platform::Sdl2Application::KeyEvent::Key::Backquote "Key::Backquote" @relativeref{Platform::Sdl2ApplicationWindow::KeyEvent,Key::Backquote}
keys to @ref Platform::Sdl2Application::KeyEvent and keys to @cpp Platform::Sdl2Application::KeyEvent @ce and
@ref Platform::GlfwApplication::KeyEvent @ref Platform::GlfwApplication::KeyEvent
- Added @ref Platform::GlfwApplication::KeyEvent::Key::World1 and - Added @ref Platform::GlfwApplication::KeyEvent::Key::World1 and
@ref Platform::GlfwApplication::KeyEvent::Key::World2 @ref Platform::GlfwApplication::KeyEvent::Key::World2
@ -1992,12 +1992,9 @@ Released 2020-06-27, tagged as
- CUDA device selection in @ref Platform::WindowlessEglApplication (see - CUDA device selection in @ref Platform::WindowlessEglApplication (see
[mosra/magnum#449](https://github.com/mosra/magnum/pull/449)) [mosra/magnum#449](https://github.com/mosra/magnum/pull/449))
- Added @ref Platform::GlfwApplication::Configuration::WindowFlag::Borderless - Added @ref Platform::GlfwApplication::Configuration::WindowFlag::Borderless
- Added @ref Platform::Sdl2Application::Configuration::WindowFlag::FullscreenDesktop, - Added @cpp Platform::Sdl2Application::Configuration::WindowFlag::FullscreenDesktop @ce,
@ref Platform::Sdl2Application::Configuration::WindowFlag::AlwaysOnTop "AlwaysOnTop", @cpp AlwaysOnTop @ce, @cpp SkipTaskbar @ce, @cpp Utility @ce,
@ref Platform::Sdl2Application::Configuration::WindowFlag::SkipTaskbar "SkipTaskbar", @cpp Tooltip @ce and @cpp PopupMenu @ce
@ref Platform::Sdl2Application::Configuration::WindowFlag::Utility "Utility",
@ref Platform::Sdl2Application::Configuration::WindowFlag::Tooltip "Tooltip"
and @ref Platform::Sdl2Application::Configuration::WindowFlag::PopupMenu "PopupMenu"
- Added @ref Platform::Sdl2Application::Configuration::addWindowFlags() and - Added @ref Platform::Sdl2Application::Configuration::addWindowFlags() and
@ref Platform::Sdl2Application::Configuration::clearWindowFlags() "clearWindowFlags()" @ref Platform::Sdl2Application::Configuration::clearWindowFlags() "clearWindowFlags()"
for consistency with similar functions in for consistency with similar functions in
@ -2299,7 +2296,7 @@ Released 2020-06-27, tagged as
- The @ref Primitives::cylinderSolid() and @ref Primitives::coneSolid() - The @ref Primitives::cylinderSolid() and @ref Primitives::coneSolid()
primitives were missing a face when both caps and texture coordinates were primitives were missing a face when both caps and texture coordinates were
enabled (see [mosra/magnum#386](https://github.com/mosra/magnum/issues/386)) 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 enabled conditionally only for SDL >= 2.0.6, but the version defines were
never included so it was always disabled never included so it was always disabled
- Fixed missing handling of - Fixed missing handling of
@ -2330,7 +2327,7 @@ Released 2020-06-27, tagged as
caused @ref Platform::Sdl2Application::setMinimalLoopPeriod() "setMinimalLoopPeriod()" caused @ref Platform::Sdl2Application::setMinimalLoopPeriod() "setMinimalLoopPeriod()"
to be ignored even though Vsync was in fact not enabled. to be ignored even though Vsync was in fact not enabled.
- It was not possible to override DPI scaling using - 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)) 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() - Fixed an otherwise harmless OOB access in @ref MeshTools::tipsifyInPlace()
that could trigger ASan or debug iterator errors that could trigger ASan or debug iterator errors
@ -2832,10 +2829,10 @@ Released 2019-10-24, tagged as
in @ref Platform::GlfwApplication and @ref Platform::EmscriptenApplication) in @ref Platform::GlfwApplication and @ref Platform::EmscriptenApplication)
for changing window title at runtime as opposed to setting them on for changing window title at runtime as opposed to setting them on
application startup application startup
- Added @ref Platform::Sdl2Application::Configuration::WindowFlag::OpenGL and - Added @cpp Platform::Sdl2Application::Configuration::WindowFlag::OpenGL @ce
@ref Platform::Sdl2Application::Configuration::WindowFlag::Vulkan "WindowFlag::Vulkan", and @cpp WindowFlag::Vulkan @ce, meant to be used together with
meant to be used together with @ref Platform::Sdl2Application::Configuration::WindowFlag::Contextless for @cpp Platform::Sdl2Application::Configuration::WindowFlag::Contextless @ce
manual creation of OpenGL contexts and Vulkan instances for manual creation of OpenGL contexts and Vulkan instances
- @ref Platform::WindowlessEglApplication now uses the - @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_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 @m_class{m-doc-external} [EGL_EXT_platform_base](https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_base.txt) and

6
doc/platform.dox

@ -136,8 +136,8 @@ target_link_libraries(myapplication
@section platform-configuration Specifying configuration @section platform-configuration Specifying configuration
By default the application is created with some reasonable defaults (e.g. By default the application is created with some reasonable defaults (e.g.
window size 800x600 pixels). If you want something else, you can pass window size 800x600 pixels). If you want something else, you can pass a
@ref Platform::Sdl2Application::Configuration "Configuration" instance to @relativeref{Platform::Sdl2ApplicationWindow,Configuration} instance to the
application constructor. Using method chaining it can be done conveniently like application constructor. Using method chaining it can be done conveniently like
this: this:
@ -148,7 +148,7 @@ this:
Sometimes you may want to set up the application based on a configuration file 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 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 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: @relativeref{Platform::Sdl2Application,create()} function:
@snippet MagnumPlatform.cpp createcontext @snippet MagnumPlatform.cpp createcontext

4
src/Magnum/Platform/AndroidApplication.h

@ -149,8 +149,8 @@ to simplify porting.
@section Platform-AndroidApplication-resizing Responding to viewport events @section Platform-AndroidApplication-resizing Responding to viewport events
Unlike in desktop application implementations, where this is controlled via Unlike in desktop application implementations, where this is controlled via
@ref Sdl2Application::Configuration::WindowFlag::Resizable, for example, on @ref Sdl2ApplicationWindow::Configuration::WindowFlag::Resizable, for example,
Android you have to describe this in the `AndroidManifest.xml` file, as by on Android you have to describe this in the `AndroidManifest.xml` file, as by
default the application gets killed and relaunched on screen orientation default the application gets killed and relaunched on screen orientation
change. See the @ref platforms-android-apps-manifest-screen-resize "manifest file docs" change. See the @ref platforms-android-apps-manifest-screen-resize "manifest file docs"
for more information. for more information.

18
src/Magnum/Platform/Screen.h

@ -175,7 +175,7 @@ template<class Application> class BasicScreen:
* @brief Key event * @brief Key event
* *
* Defined only if the application has a * Defined only if the application has a
* @ref Sdl2Application::KeyEvent "KeyEvent". * @relativeref{Sdl2ApplicationWindow,KeyEvent}.
*/ */
typedef typename BasicScreenedApplication<Application>::KeyEvent KeyEvent; typedef typename BasicScreenedApplication<Application>::KeyEvent KeyEvent;
#endif #endif
@ -192,7 +192,7 @@ template<class Application> class BasicScreen:
* @m_since{2019,10} * @m_since{2019,10}
* *
* Defined only if the application has a * Defined only if the application has a
* @ref Sdl2Application::MouseScrollEvent "MouseScrollEvent". * @relativeref{Sdl2ApplicationWindow,MouseScrollEvent}.
*/ */
typedef typename BasicScreenedApplication<Application>::MouseScrollEvent MouseScrollEvent; typedef typename BasicScreenedApplication<Application>::MouseScrollEvent MouseScrollEvent;
@ -201,7 +201,7 @@ template<class Application> class BasicScreen:
* @m_since{2019,10} * @m_since{2019,10}
* *
* Defined only if the application has a * Defined only if the application has a
* @ref Sdl2Application::TextInputEvent "TextInputEvent". * @relativeref{Sdl2ApplicationWindow,TextInputEvent}.
*/ */
typedef typename BasicScreenedApplication<Application>::TextInputEvent TextInputEvent; typedef typename BasicScreenedApplication<Application>::TextInputEvent TextInputEvent;
@ -210,7 +210,7 @@ template<class Application> class BasicScreen:
* @m_since{2019,10} * @m_since{2019,10}
* *
* Defined only if the application has a * Defined only if the application has a
* @ref Sdl2Application::TextEditingEvent "TextEditingEvent". * @relativeref{Sdl2ApplicationWindow,TextEditingEvent}.
*/ */
typedef typename BasicScreenedApplication<Application>::TextEditingEvent TextEditingEvent; typedef typename BasicScreenedApplication<Application>::TextEditingEvent TextEditingEvent;
#endif #endif
@ -379,7 +379,7 @@ template<class Application> class BasicScreen:
* Called when @ref PropagatedEvent::Input is enabled and an key is * Called when @ref PropagatedEvent::Input is enabled and an key is
* pressed. See @ref Sdl2Application::keyPressEvent() "*Application::keyPressEvent()" * pressed. See @ref Sdl2Application::keyPressEvent() "*Application::keyPressEvent()"
* for more information. Defined only if the application has a * for more information. Defined only if the application has a
* @ref Sdl2Application::KeyEvent "KeyEvent". * @relativeref{Sdl2ApplicationWindow,KeyEvent}.
*/ */
virtual void keyPressEvent(KeyEvent& event); virtual void keyPressEvent(KeyEvent& event);
@ -389,7 +389,7 @@ template<class Application> class BasicScreen:
* Called when @ref PropagatedEvent::Input is enabled and an key is * Called when @ref PropagatedEvent::Input is enabled and an key is
* released. See @ref Sdl2Application::keyReleaseEvent() "*Application::keyReleaseEvent()" * released. See @ref Sdl2Application::keyReleaseEvent() "*Application::keyReleaseEvent()"
* for more information. Defined only if the application has a * for more information. Defined only if the application has a
* @ref Sdl2Application::KeyEvent "KeyEvent". * @relativeref{Sdl2ApplicationWindow,KeyEvent}.
*/ */
virtual void keyReleaseEvent(KeyEvent& event); virtual void keyReleaseEvent(KeyEvent& event);
#endif #endif
@ -437,7 +437,7 @@ template<class Application> class BasicScreen:
* Called when @ref PropagatedEvent::Input is enabled and mouse wheel * Called when @ref PropagatedEvent::Input is enabled and mouse wheel
* is rotated. See @ref Sdl2Application::mouseScrollEvent() "*Application::mouseScrollEvent()" * is rotated. See @ref Sdl2Application::mouseScrollEvent() "*Application::mouseScrollEvent()"
* for more information. Defined only if the application has a * for more information. Defined only if the application has a
* @ref Sdl2Application::MouseScrollEvent "MouseScrollEvent". * @relativeref{Sdl2ApplicationWindow,MouseScrollEvent}.
*/ */
virtual void mouseScrollEvent(MouseScrollEvent& event); virtual void mouseScrollEvent(MouseScrollEvent& event);
#endif #endif
@ -457,7 +457,7 @@ template<class Application> class BasicScreen:
* *
* Called when @ref PropagatedEvent::Input is enabled and text is being * Called when @ref PropagatedEvent::Input is enabled and text is being
* input. Defined only if the application has a * input. Defined only if the application has a
* @ref Sdl2Application::TextInputEvent "TextInputEvent". * @relativeref{Sdl2ApplicationWindow,TextInputEvent}.
*/ */
virtual void textInputEvent(TextInputEvent& event); virtual void textInputEvent(TextInputEvent& event);
@ -467,7 +467,7 @@ template<class Application> class BasicScreen:
* *
* Called when @ref PropagatedEvent::Input and the text is being * Called when @ref PropagatedEvent::Input and the text is being
* edited. Defined only if the application has a * edited. Defined only if the application has a
* @ref Sdl2Application::TextEditingEvent "TextEditingEvent". * @relativeref{Sdl2ApplicationWindow,TextEditingEvent}.
*/ */
virtual void textEditingEvent(TextEditingEvent& event); virtual void textEditingEvent(TextEditingEvent& event);
#endif #endif

779
src/Magnum/Platform/Sdl2Application.cpp

@ -78,142 +78,25 @@ namespace Magnum { namespace Platform {
using namespace Containers::Literals; using namespace Containers::Literals;
namespace { enum class Sdl2ApplicationWindow::WindowFlag: UnsignedByte {
/*
* 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<Sdl2Application::InputEvent::Modifier>(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 {
Redraw = 1 << 0, Redraw = 1 << 0,
VSyncEnabled = 1 << 1,
NoTickEvent = 1 << 2,
NoAnyEvent = 1 << 3,
Exit = 1 << 4,
#ifdef CORRADE_TARGET_EMSCRIPTEN #ifdef CORRADE_TARGET_EMSCRIPTEN
TextInputActive = 1 << 5, TextInputActive = 1 << 1,
Resizable = 1 << 6, Resizable = 1 << 2,
#endif
#ifdef CORRADE_TARGET_APPLE
HiDpiWarningPrinted = 1 << 7
#endif #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);
}
#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::~Sdl2ApplicationWindow() {
/* SDL_DestroyWindow(_window) crashes on windows when _window is nullptr
(it doesn't seem to crash on Linux) */
#ifndef CORRADE_TARGET_EMSCRIPTEN #ifndef CORRADE_TARGET_EMSCRIPTEN
_minimalLoopPeriod{0}, if(_window) SDL_DestroyWindow(_window);
#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);
#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<Containers::StringView>("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 #endif
else if(dpiScaling.containsAny(" \t\n"_s))
_commandLineDpiScaling = args.value<Vector2>("dpi-scaling");
else
_commandLineDpiScaling = Vector2{args.value<Float>("dpi-scaling")};
} }
void Sdl2Application::create() { Vector2 Sdl2ApplicationWindow::dpiScaling(const Configuration& configuration) {
create(Configuration{});
}
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
Vector2 Sdl2Application::dpiScaling(const Configuration& configuration) {
/* Print a helpful warning in case some extra steps are needed for HiDPI /* Print a helpful warning in case some extra steps are needed for HiDPI
support */ support */
#ifdef CORRADE_TARGET_APPLE #ifdef CORRADE_TARGET_APPLE
@ -229,18 +112,18 @@ Vector2 Sdl2Application::dpiScaling(const Configuration& configuration) {
return dpiScalingInternal(configuration.dpiScalingPolicy(), configuration.dpiScaling()); return dpiScalingInternal(configuration.dpiScalingPolicy(), configuration.dpiScaling());
} }
Vector2 Sdl2Application::dpiScalingInternal(const Implementation::Sdl2DpiScalingPolicy configurationDpiScalingPolicy, const Vector2& configurationDpiScaling) const { Vector2 Sdl2ApplicationWindow::dpiScalingInternal(const Implementation::Sdl2DpiScalingPolicy configurationDpiScalingPolicy, const Vector2& configurationDpiScaling) const {
std::ostream* verbose = _verboseLog ? Debug::output() : nullptr; std::ostream* verbose = _application._verboseLog ? Debug::output() : nullptr;
/* Use values from the configuration only if not overridden on command line /* Use values from the configuration only if not overridden on command line
to something non-default. In any case explicit scaling has a precedence to something non-default. In any case explicit scaling has a precedence
before the policy. */ before the policy. */
Implementation::Sdl2DpiScalingPolicy dpiScalingPolicy{}; Implementation::Sdl2DpiScalingPolicy dpiScalingPolicy{};
if(!_commandLineDpiScaling.isZero()) { if(!_application._commandLineDpiScaling.isZero()) {
Debug{verbose} << "Platform::Sdl2Application: user-defined DPI scaling" << _commandLineDpiScaling; Debug{verbose} << "Platform::Sdl2Application: user-defined DPI scaling" << _application._commandLineDpiScaling;
return _commandLineDpiScaling; return _application._commandLineDpiScaling;
} else if(_commandLineDpiScalingPolicy != Implementation::Sdl2DpiScalingPolicy::Default) { } else if(_application._commandLineDpiScalingPolicy != Implementation::Sdl2DpiScalingPolicy::Default) {
dpiScalingPolicy = _commandLineDpiScalingPolicy; dpiScalingPolicy = _application._commandLineDpiScalingPolicy;
} else if(!configurationDpiScaling.isZero()) { } else if(!configurationDpiScaling.isZero()) {
Debug{verbose} << "Platform::Sdl2Application: app-defined DPI scaling" << configurationDpiScaling; Debug{verbose} << "Platform::Sdl2Application: app-defined DPI scaling" << configurationDpiScaling;
return configurationDpiScaling; return configurationDpiScaling;
@ -322,72 +205,360 @@ Vector2 Sdl2Application::dpiScalingInternal(const Implementation::Sdl2DpiScaling
#else #else
Debug{verbose} << "Platform::Sdl2Application: sorry, physical DPI scaling only available on SDL 2.0.4+"; Debug{verbose} << "Platform::Sdl2Application: sorry, physical DPI scaling only available on SDL 2.0.4+";
#endif #endif
return Vector2{1.0f}; return Vector2{1.0f};
/* HOWEVER, on Windows it gets the virtual DPI scaling, which we don't
want, so we need to call Windows APIs directly instead. Consistency my
ass. Related bug report that will probably never get actually
implemented: https://bugzilla.libsdl.org/show_bug.cgi?id=2473 */
#elif defined(CORRADE_TARGET_WINDOWS) && !defined(CORRADE_TARGET_WINDOWS_RT)
HDC hDC = GetWindowDC(nullptr);
Vector2i monitorSize{GetDeviceCaps(hDC, HORZSIZE), GetDeviceCaps(hDC, VERTSIZE)};
SDL_DisplayMode mode;
CORRADE_INTERNAL_ASSERT(SDL_GetDesktopDisplayMode(0, &mode) == 0);
auto dpi = Vector2{Vector2i{mode.w, mode.h}*25.4f/Vector2{monitorSize}};
const Vector2 dpiScaling{dpi/96.0f};
Debug{verbose} << "Platform::Sdl2Application: physical DPI scaling" << dpiScaling;
return dpiScaling;
/* Not implemented otherwise */
#else
Debug{verbose} << "Platform::Sdl2Application: sorry, physical DPI scaling not implemented on this platform yet";
return Vector2{1.0f};
#endif
#endif
}
void Sdl2ApplicationWindow::setWindowTitle(const Containers::StringView title) {
#ifndef CORRADE_TARGET_EMSCRIPTEN
SDL_SetWindowTitle(_window,
Containers::String::nullTerminatedGlobalView(title).data());
#else
/* We don't have the _window because SDL_CreateWindow() doesn't exist in
the SDL1/2 hybrid. But it's not used anyway, so pass nullptr there. */
SDL_SetWindowTitle(nullptr,
Containers::String::nullTerminatedGlobalView(title).data());
#endif
}
#if !defined(CORRADE_TARGET_EMSCRIPTEN) && SDL_MAJOR_VERSION*1000 + SDL_MINOR_VERSION*100 + SDL_PATCHLEVEL >= 2005
void Sdl2ApplicationWindow::setWindowIcon(const ImageView2D& image) {
Uint32 format; /** @todo handle sRGB differently? */
switch(image.format()) {
case PixelFormat::RGB8Srgb:
case PixelFormat::RGB8Unorm:
format = SDL_PIXELFORMAT_RGB24;
break;
case PixelFormat::RGBA8Srgb:
case PixelFormat::RGBA8Unorm:
format = SDL_PIXELFORMAT_RGBA32;
break;
default:
CORRADE_ASSERT_UNREACHABLE("Platform::Sdl2ApplicationWindow::setWindowIcon(): unexpected format" << image.format(), );
}
/* Images are loaded with origin at bottom left, flip it to top left. SDL
only accepted a negative stride until version 2.23.1 and commit
https://github.com/libsdl-org/SDL/commit/535fdc3adcdc08a193ab0d45540014fd536cf251
so we need to manually flip the image now */
/** @todo take ImageFlag::YUp into account once it exists */
Image2D flippedImage{PixelStorage{}.setAlignment(1), image.format(), image.size(), Containers::Array<char>{NoInit, std::size_t(image.size().product()*image.pixelSize())}};
const Containers::StridedArrayView3D<char> flippedPixels = flippedImage.pixels();
Utility::copy(image.pixels().flipped<0>(), flippedPixels);
SDL_Surface* const icon = SDL_CreateRGBSurfaceWithFormatFrom(const_cast<void*>(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<Sdl2Application::InputEvent::Modifier>(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
/* HOWEVER, on Windows it gets the virtual DPI scaling, which we don't if(SDL_Init(SDL_INIT_VIDEO) < 0) {
want, so we need to call Windows APIs directly instead. Consistency my Error() << "Cannot initialize SDL:" << SDL_GetError();
ass. Related bug report that will probably never get actually std::exit(1);
implemented: https://bugzilla.libsdl.org/show_bug.cgi?id=2473 */ }
#elif defined(CORRADE_TARGET_WINDOWS) && !defined(CORRADE_TARGET_WINDOWS_RT)
HDC hDC = GetWindowDC(nullptr);
Vector2i monitorSize{GetDeviceCaps(hDC, HORZSIZE), GetDeviceCaps(hDC, VERTSIZE)};
SDL_DisplayMode mode;
CORRADE_INTERNAL_ASSERT(SDL_GetDesktopDisplayMode(0, &mode) == 0);
auto dpi = Vector2{Vector2i{mode.w, mode.h}*25.4f/Vector2{monitorSize}};
const Vector2 dpiScaling{dpi/96.0f};
Debug{verbose} << "Platform::Sdl2Application: physical DPI scaling" << dpiScaling;
return dpiScaling;
/* Not implemented otherwise */ /* Save command-line arguments */
#else if(args.value("log") == "verbose") _verboseLog = true;
Debug{verbose} << "Platform::Sdl2Application: sorry, physical DPI scaling not implemented on this platform yet"; const Containers::StringView dpiScaling = args.value<Containers::StringView>("dpi-scaling");
return Vector2{1.0f}; if(dpiScaling == "default"_s)
_commandLineDpiScalingPolicy = Implementation::Sdl2DpiScalingPolicy::Default;
#ifdef CORRADE_TARGET_APPLE
else if(dpiScaling == "framebuffer"_s)
_commandLineDpiScalingPolicy = Implementation::Sdl2DpiScalingPolicy::Framebuffer;
#endif #endif
#ifndef CORRADE_TARGET_APPLE
#if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_ANDROID)
else if(dpiScaling == "virtual"_s)
_commandLineDpiScalingPolicy = Implementation::Sdl2DpiScalingPolicy::Virtual;
#endif #endif
} else if(dpiScaling == "physical"_s)
_commandLineDpiScalingPolicy = Implementation::Sdl2DpiScalingPolicy::Physical;
void Sdl2Application::setWindowTitle(const Containers::StringView title) {
#ifndef CORRADE_TARGET_EMSCRIPTEN
SDL_SetWindowTitle(_window,
Containers::String::nullTerminatedGlobalView(title).data());
#else
/* We don't have the _window because SDL_CreateWindow() doesn't exist in
the SDL1/2 hybrid. But it's not used anyway, so pass nullptr there. */
SDL_SetWindowTitle(nullptr,
Containers::String::nullTerminatedGlobalView(title).data());
#endif #endif
else if(dpiScaling.containsAny(" \t\n"_s))
_commandLineDpiScaling = args.value<Vector2>("dpi-scaling");
else
_commandLineDpiScaling = Vector2{args.value<Float>("dpi-scaling")};
} }
#if !defined(CORRADE_TARGET_EMSCRIPTEN) && SDL_MAJOR_VERSION*1000 + SDL_MINOR_VERSION*100 + SDL_PATCHLEVEL >= 2005 void Sdl2Application::create() {
void Sdl2Application::setWindowIcon(const ImageView2D& image) { create(Configuration{});
Uint32 format; /** @todo handle sRGB differently? */ }
switch(image.format()) {
case PixelFormat::RGB8Srgb:
case PixelFormat::RGB8Unorm:
format = SDL_PIXELFORMAT_RGB24;
break;
case PixelFormat::RGBA8Srgb:
case PixelFormat::RGBA8Unorm:
format = SDL_PIXELFORMAT_RGBA32;
break;
default:
CORRADE_ASSERT_UNREACHABLE("Platform::Sdl2Application::setWindowIcon(): unexpected format" << image.format(), );
}
/* Images are loaded with origin at bottom left, flip it to top left. SDL
only accepted a negative stride until version 2.23.1 and commit
https://github.com/libsdl-org/SDL/commit/535fdc3adcdc08a193ab0d45540014fd536cf251
so we need to manually flip the image now */
/** @todo take ImageFlag::YUp into account once it exists */
Image2D flippedImage{PixelStorage{}.setAlignment(1), image.format(), image.size(), Containers::Array<char>{NoInit, std::size_t(image.size().product()*image.pixelSize())}};
const Containers::StridedArrayView3D<char> flippedPixels = flippedImage.pixels();
Utility::copy(image.pixels().flipped<0>(), flippedPixels);
SDL_Surface* const icon = SDL_CreateRGBSurfaceWithFormatFrom(const_cast<void*>(flippedPixels.data()) , flippedImage.size().x(), flippedImage.size().y(), 32, flippedPixels.stride()[0], format); void Sdl2Application::create(const Configuration& configuration) {
CORRADE_INTERNAL_ASSERT(icon); if(!tryCreate(configuration)) std::exit(1);
}
SDL_SetWindowIcon(_window, icon); #ifdef MAGNUM_TARGET_GL
SDL_FreeSurface(icon); void Sdl2Application::create(const Configuration& configuration, const GLConfiguration& glConfiguration) {
if(!tryCreate(configuration, glConfiguration)) std::exit(1);
} }
#endif #endif
@ -735,66 +906,13 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf
} }
#endif #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 #ifdef CORRADE_TARGET_EMSCRIPTEN
void Sdl2Application::setContainerCssClass(const Containers::StringView cssClass) { void Sdl2Application::setContainerCssClass(const Containers::StringView cssClass) {
magnumPlatformSetContainerCssClass(cssClass.data(), cssClass.size()); magnumPlatformSetContainerCssClass(cssClass.data(), cssClass.size());
} }
#endif #endif
void Sdl2Application::swapBuffers() { void Sdl2ApplicationWindow::swapBuffers() {
#ifndef CORRADE_TARGET_EMSCRIPTEN #ifndef CORRADE_TARGET_EMSCRIPTEN
SDL_GL_SwapWindow(_window); SDL_GL_SwapWindow(_window);
#else #else
@ -827,8 +945,6 @@ bool Sdl2Application::setSwapInterval(const Int interval) {
return true; return true;
} }
void Sdl2Application::redraw() { _flags |= Flag::Redraw; }
Sdl2Application::~Sdl2Application() { Sdl2Application::~Sdl2Application() {
/* SDL_DestroyWindow(_window) crashes on windows when _window is nullptr /* SDL_DestroyWindow(_window) crashes on windows when _window is nullptr
(it doesn't seem to crash on Linux). Because this seems to be yet (it doesn't seem to crash on Linux). Because this seems to be yet
@ -852,9 +968,14 @@ Sdl2Application::~Sdl2Application() {
SDL_FreeCursor(cursor); SDL_FreeCursor(cursor);
#endif #endif
#ifndef CORRADE_TARGET_EMSCRIPTEN /* The window would be destroyed in the base ~Sdl2ApplicationWindow(), but
if(_window) SDL_DestroyWindow(_window); that's too late. Do it before calling SDL_Quit() and then reset the
#endif window pointer so it's not called again after. */
if(_window) {
SDL_DestroyWindow(_window);
_window = nullptr;
}
SDL_Quit(); SDL_Quit();
} }
@ -946,14 +1067,14 @@ bool Sdl2Application::mainLoopIteration() {
dpiScaling()}; dpiScaling()};
/** @todo handle also WM_DPICHANGED events when a window is moved between displays with different DPI */ /** @todo handle also WM_DPICHANGED events when a window is moved between displays with different DPI */
viewportEvent(e); viewportEvent(e);
_flags |= Flag::Redraw; _windowFlags |= WindowFlag::Redraw;
#endif #endif
} break; } break;
/* Direct everything that wasn't exposed via a callback to /* Direct everything that wasn't exposed via a callback to
anyEvent(), so users can implement event handling for anyEvent(), so users can implement event handling for
things not present in the Application APIs */ things not present in the Application APIs */
case SDL_WINDOWEVENT_EXPOSED: case SDL_WINDOWEVENT_EXPOSED:
_flags |= Flag::Redraw; _windowFlags |= WindowFlag::Redraw;
if(!(_flags & Flag::NoAnyEvent)) anyEvent(event); if(!(_flags & Flag::NoAnyEvent)) anyEvent(event);
break; break;
default: default:
@ -1027,8 +1148,8 @@ bool Sdl2Application::mainLoopIteration() {
if(!(_flags & Flag::NoTickEvent)) tickEvent(); if(!(_flags & Flag::NoTickEvent)) tickEvent();
/* Draw event */ /* Draw event */
if(_flags & Flag::Redraw) { if(_windowFlags & WindowFlag::Redraw) {
_flags &= ~Flag::Redraw; _windowFlags &= ~WindowFlag::Redraw;
drawEvent(); drawEvent();
#ifndef CORRADE_TARGET_EMSCRIPTEN #ifndef CORRADE_TARGET_EMSCRIPTEN
@ -1058,99 +1179,6 @@ bool Sdl2Application::mainLoopIteration() {
return !(_flags & Flag::Exit); 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 #ifdef MAGNUM_BUILD_DEPRECATED
void Sdl2Application::setMouseLocked(bool enabled) { void Sdl2Application::setMouseLocked(bool enabled) {
/** @todo Implement this in Emscripten */ /** @todo Implement this in Emscripten */
@ -1207,34 +1235,7 @@ void Sdl2Application::anyEvent(SDL_Event&) {
_flags |= Flag::NoAnyEvent; _flags |= Flag::NoAnyEvent;
} }
void Sdl2Application::viewportEvent(ViewportEvent&) {} Sdl2ApplicationWindow::Configuration::Configuration():
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():
#if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_IOS) #if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_IOS)
_title(Containers::String::nullTerminatedGlobalView("Magnum SDL2 Application"_s)), _title(Containers::String::nullTerminatedGlobalView("Magnum SDL2 Application"_s)),
#endif #endif
@ -1245,38 +1246,54 @@ Sdl2Application::Configuration::Configuration():
#endif #endif
_dpiScalingPolicy{DpiScalingPolicy::Default} {} _dpiScalingPolicy{DpiScalingPolicy::Default} {}
Sdl2Application::Configuration::~Configuration() = default; Sdl2ApplicationWindow::Configuration::~Configuration() = default;
Containers::StringView Sdl2Application::KeyEvent::keyName(const Key key) { Containers::StringView Sdl2ApplicationWindow::KeyEvent::keyName(const Key key) {
return SDL_GetKeyName(SDL_Keycode(key)); return SDL_GetKeyName(SDL_Keycode(key));
} }
Containers::StringView Sdl2Application::KeyEvent::keyName() const { Containers::StringView Sdl2ApplicationWindow::KeyEvent::keyName() const {
return keyName(_key); return keyName(_key);
} }
Sdl2Application::InputEvent::Modifiers Sdl2Application::MouseEvent::modifiers() { Sdl2ApplicationWindow::InputEvent::Modifiers Sdl2ApplicationWindow::MouseEvent::modifiers() {
if(_modifiers) return *_modifiers; if(_modifiers) return *_modifiers;
return *(_modifiers = fixedModifiers(Uint16(SDL_GetModState()))); return *(_modifiers = fixedModifiers(Uint16(SDL_GetModState())));
} }
Sdl2Application::InputEvent::Modifiers Sdl2Application::MouseMoveEvent::modifiers() { Sdl2ApplicationWindow::InputEvent::Modifiers Sdl2ApplicationWindow::MouseMoveEvent::modifiers() {
if(_modifiers) return *_modifiers; if(_modifiers) return *_modifiers;
return *(_modifiers = fixedModifiers(Uint16(SDL_GetModState()))); return *(_modifiers = fixedModifiers(Uint16(SDL_GetModState())));
} }
Vector2i Sdl2Application::MouseScrollEvent::position() { Vector2i Sdl2ApplicationWindow::MouseScrollEvent::position() {
if(_position) return *_position; if(_position) return *_position;
_position = Vector2i{}; _position = Vector2i{};
SDL_GetMouseState(&_position->x(), &_position->y()); SDL_GetMouseState(&_position->x(), &_position->y());
return *_position; return *_position;
} }
Sdl2Application::InputEvent::Modifiers Sdl2Application::MouseScrollEvent::modifiers() { Sdl2ApplicationWindow::InputEvent::Modifiers Sdl2ApplicationWindow::MouseScrollEvent::modifiers() {
if(_modifiers) return *_modifiers; if(_modifiers) return *_modifiers;
return *(_modifiers = fixedModifiers(Uint16(SDL_GetModState()))); 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<Sdl2Application>; template class BasicScreen<Sdl2Application>;
template class BasicScreenedApplication<Sdl2Application>; template class BasicScreenedApplication<Sdl2Application>;

973
src/Magnum/Platform/Sdl2Application.h

File diff suppressed because it is too large Load Diff

2
src/Magnum/SceneGraph/Camera.h

@ -156,7 +156,7 @@ template<UnsignedInt dimensions, class T> class Camera: public AbstractFeature<d
* *
* Conversion from integer window-space coordinates with origin in top * Conversion from integer window-space coordinates with origin in top
* left corner and Y down (e.g. from * left corner and Y down (e.g. from
* @ref Platform::Sdl2Application::MouseEvent "Platform::*Application::MouseEvent") * @ref Platform::Sdl2ApplicationWindow::MouseEvent "Platform::*Application::MouseEvent")
* to floating-point coordinates on near XY plane with origin at camera * to floating-point coordinates on near XY plane with origin at camera
* position and Y up can be done using the following snippet: * position and Y up can be done using the following snippet:
* *

Loading…
Cancel
Save