diff --git a/CMakeLists.txt b/CMakeLists.txt index 0638ff8..6f81885 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/corrade EXCLUDE_FROM_ALL message("=== Configure Magnum ===") # Add Magnum as a subproject, enable Sdl2Application set(MAGNUM_BUILD_STATIC ON CACHE BOOL "" FORCE) -set(MAGNUM_WITH_SDL2APPLICATION ON CACHE BOOL "" FORCE) +set(MAGNUM_WITH_WGLCONTEXT ON CACHE BOOL "" FORCE) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/magnum EXCLUDE_FROM_ALL) message("=== Configure QuickJS ===") @@ -41,8 +41,8 @@ target_include_directories(quickjs ) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) -file(GLOB files src/main.cpp) +file(GLOB files src/*.cpp) add_executable(${CMAKE_PROJECT_NAME} ${files}) target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE include) target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE src) -target_link_libraries(${CMAKE_PROJECT_NAME} SDL2::SDL2-static Magnum::Sdl2Application Magnum::Magnum Magnum::GL quickjs) \ No newline at end of file +target_link_libraries(${CMAKE_PROJECT_NAME} SDL2::SDL2-static Magnum::Magnum Magnum::GL Magnum::WglContext quickjs) \ No newline at end of file diff --git a/my-game.exe b/my-game.exe index 4814c38..c2af6b7 100644 Binary files a/my-game.exe and b/my-game.exe differ diff --git a/src/Sdl2Application.cpp b/src/Sdl2Application.cpp deleted file mode 100644 index 658aa9c..0000000 --- a/src/Sdl2Application.cpp +++ /dev/null @@ -1,1277 +0,0 @@ -/* - This file is part of Magnum. - - Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, - 2020, 2021, 2022 Vladimír Vondruš - Copyright © 2019 Marco Melorio - Copyright © 2022 Pablo Escobar - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included - in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - DEALINGS IN THE SOFTWARE. -*/ - -#include "Sdl2Application.h" - -#ifdef CORRADE_TARGET_CLANG_CL -/* SDL does #pragma pack(push,8) and #pragma pack(pop,8) in different headers - (begin_code.h and end_code.h) and clang-cl doesn't like that, even though it - is completely fine. Silence the warning. */ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpragma-pack" -#endif -#include -#ifdef CORRADE_TARGET_CLANG_CL -#pragma clang diagnostic pop -#endif -#ifndef CORRADE_TARGET_EMSCRIPTEN -#include -#else -#include -#include -#endif -#include -#include - -#include "Magnum/Image.h" -#include "Magnum/ImageView.h" -#include "Magnum/PixelFormat.h" -#include "Magnum/Math/ConfigurationValue.h" -#include "Magnum/Math/FunctionsBatch.h" -#include "Magnum/Math/Range.h" -#include "Magnum/Platform/ScreenedApplication.hpp" -#include "Magnum/Platform/Implementation/DpiScaling.h" - -#ifdef MAGNUM_TARGET_GL -#include "Magnum/GL/Version.h" -#endif - -#if defined(CORRADE_TARGET_WINDOWS) && !defined(CORRADE_TARGET_WINDOWS_RT) -/* For physical DPI scaling */ -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include -#endif - -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 { - 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 - #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): - #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); - #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{}); -} - -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) { - std::ostream* verbose = _verboseLog ? Debug::output() : nullptr; - - /* Print a helpful warning in case some extra steps are needed for HiDPI - support */ - #ifdef CORRADE_TARGET_APPLE - if(!Implementation::isAppleBundleHiDpiEnabled() && !(_flags & Flag::HiDpiWarningPrinted)) { - Warning{} << "Platform::Sdl2Application: warning: the executable is not a HiDPI-enabled app bundle"; - _flags |= Flag::HiDpiWarningPrinted; - } - #elif defined(CORRADE_TARGET_WINDOWS) - /* Handled below, warning printed only when using virtual DPI scaling */ - #endif - - /* 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; - } else if(!configuration.dpiScaling().isZero()) { - Debug{verbose} << "Platform::Sdl2Application: app-defined DPI scaling" << configuration.dpiScaling(); - return configuration.dpiScaling(); - } else { - dpiScalingPolicy = configuration.dpiScalingPolicy(); - } - - /* There's no choice on Apple, it's all controlled by the plist file. So - unless someone specified custom scaling via config or command-line - above, return the default. */ - #ifdef CORRADE_TARGET_APPLE - return Vector2{1.0f}; - - /* Otherwise there's a choice between virtual and physical DPI scaling */ - #else - /* Try to get virtual DPI scaling first, if supported and requested */ - #if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_ANDROID) - if(dpiScalingPolicy == Implementation::Sdl2DpiScalingPolicy::Virtual) { - /* Use Xft.dpi on X11, because SDL_GetDisplayDPI() returns the useless - physical value on Linux, while the virtual value on Windows. */ - #ifdef _MAGNUM_PLATFORM_USE_X11 - const Vector2 dpiScaling{Implementation::x11DpiScaling()}; - if(!dpiScaling.isZero()) { - Debug{verbose} << "Platform::Sdl2Application: virtual DPI scaling" << dpiScaling.x(); - return dpiScaling; - } - - /* Check for DPI awareness on (non-RT) Windows and then ask for DPI. - SDL_GetDisplayDPI() is querying GetDpiForMonitor() -- - https://github.com/spurious/SDL-mirror/blob/17af4584cb28cdb3c2feba17e7d989a806007d9f/src/video/windows/SDL_windowsmodes.c#L266 - and GetDpiForMonitor() returns 96 if the application is DPI unaware. - So we instead check for DPI awareness first (and tell the user if - not), and only if the app is, then we use SDL_GetDisplayDPI(). */ - #elif defined(CORRADE_TARGET_WINDOWS) && !defined(CORRADE_TARGET_WINDOWS_RT) - if(!Implementation::isWindowsAppDpiAware()) { - Warning{verbose} << "Platform::Sdl2Application: your application is not set as DPI-aware, DPI scaling won't be used"; - return Vector2{1.0f}; - } - Vector2 dpi; - if(SDL_GetDisplayDPI(0, nullptr, &dpi.x(), &dpi.y()) == 0) { - const Vector2 dpiScaling{dpi/96.0f}; - Debug{verbose} << "Platform::Sdl2Application: virtual DPI scaling" << dpiScaling; - return dpiScaling; - } - - /* Otherwise ¯\_(ツ)_/¯ */ - #else - Debug{verbose} << "Platform::Sdl2Application: sorry, virtual DPI scaling not implemented on this platform yet, falling back to physical DPI scaling"; - #endif - } - #endif - - /* At this point, either the virtual DPI query failed or a physical DPI - scaling is requested */ - #if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_ANDROID) - CORRADE_INTERNAL_ASSERT(dpiScalingPolicy == Implementation::Sdl2DpiScalingPolicy::Virtual || dpiScalingPolicy == Implementation::Sdl2DpiScalingPolicy::Physical); - #else - CORRADE_INTERNAL_ASSERT(dpiScalingPolicy == Implementation::Sdl2DpiScalingPolicy::Physical); - #endif - - /* Take device pixel ratio on Emscripten */ - #ifdef CORRADE_TARGET_EMSCRIPTEN - const Vector2 dpiScaling{Implementation::emscriptenDpiScaling()}; - Debug{verbose} << "Platform::Sdl2Application: physical DPI scaling" << dpiScaling.x(); - return dpiScaling; - - /* Take a physical display DPI. On Linux it gets the (usually very off) - physical value from X11. Also only since SDL 2.0.4. */ - #elif defined(CORRADE_TARGET_UNIX) - #if SDL_VERSION_ATLEAST(2, 0, 4) - Vector2 dpi; - if(SDL_GetDisplayDPI(0, nullptr, &dpi.x(), &dpi.y()) == 0) { - const Vector2 dpiScaling{dpi/96.0f}; - Debug{verbose} << "Platform::Sdl2Application: physical DPI scaling" << dpiScaling; - return dpiScaling; - } - - Warning{} << "Platform::Sdl2Application: can't get physical display DPI, falling back to no scaling:" << SDL_GetError(); - #else - Debug{verbose} << "Platform::Sdl2Application: sorry, physical DPI scaling only available on SDL 2.0.4+"; - #endif - 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 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 -} - -#if !defined(CORRADE_TARGET_EMSCRIPTEN) && SDL_MAJOR_VERSION*1000 + SDL_MINOR_VERSION*100 + SDL_PATCHLEVEL >= 2005 -void Sdl2Application::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::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{NoInit, std::size_t(image.size().product()*image.pixelSize())}}; - 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_SetWindowIcon(_window, icon); - SDL_FreeSurface(icon); -} -#endif - -bool Sdl2Application::tryCreate(const Configuration& configuration) { - #ifdef MAGNUM_TARGET_GL - if(!(configuration.windowFlags() & Configuration::WindowFlag::Contextless)) - return tryCreate(configuration, GLConfiguration{}); - #endif - - #ifndef CORRADE_TARGET_EMSCRIPTEN - /* Scale window based on DPI */ - _dpiScaling = dpiScaling(configuration); - const Vector2i scaledWindowSize = configuration.size()*_dpiScaling; - - /* 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(); - return false; - } - - /* Emscripten-specific initialization */ - #else - /* Get CSS canvas size. This is used later to detect canvas resizes and - fire viewport events, because Emscripten doesn't do that. Related info: - https://github.com/kripken/emscripten/issues/1731 */ - /** @todo don't hardcode "#canvas" here, make it configurable from outside */ - { - Vector2d canvasSize; - /* Emscripten 1.38.27 changed to generic CSS selectors from element - IDs depending on -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1 - being set (which we can't detect at compile time). Fortunately, - using #canvas works the same way both in the previous versions and - the current one. Unfortunately, this is also the only value that - works the same way for both. Further details at - https://github.com/emscripten-core/emscripten/pull/7977 */ - emscripten_get_element_css_size("#canvas", &canvasSize.x(), &canvasSize.y()); - _lastKnownCanvasSize = Vector2i{canvasSize}; - } - - /* By default Emscripten creates a 300x150 canvas. That's so freaking - random I'm getting mad. Use the real (CSS pixels) canvas size instead, - if the size is not hardcoded from the configuration. This is then - multiplied by the DPI scaling. */ - Vector2i windowSize; - if(!configuration.size().isZero()) { - windowSize = configuration.size(); - /* Because hardcoding canvas size for WebGL is usually a wrong thing to - do, notify about that in the verbose output */ - Debug{_verboseLog ? Debug::output() : nullptr} << "Platform::Sdl2Application::tryCreate(): hardcoded canvas size" << windowSize; - } else { - windowSize = _lastKnownCanvasSize; - Debug{_verboseLog ? Debug::output() : nullptr} << "Platform::Sdl2Application::tryCreate(): autodetected canvas size" << windowSize; - } - _dpiScaling = dpiScaling(configuration); - const Vector2i scaledWindowSize = windowSize*_dpiScaling; - - Uint32 flags = SDL_OPENGL|SDL_HWSURFACE|SDL_DOUBLEBUF; - if(configuration.windowFlags() & Configuration::WindowFlag::Resizable) { - _flags |= Flag::Resizable; - /* Actually not sure if this makes any difference: - https://github.com/kripken/emscripten/issues/1731 */ - flags |= SDL_RESIZABLE; - } - - if(!(_surface = SDL_SetVideoMode(scaledWindowSize.x(), scaledWindowSize.y(), 24, flags))) { - Error() << "Platform::Sdl2Application::tryCreate(): cannot create context:" << SDL_GetError(); - return false; - } - #endif - - return true; -} - -#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); - - /* Enable double buffering, set up buffer sizes */ - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - SDL_GL_SetAttribute(SDL_GL_RED_SIZE, glConfiguration.colorBufferSize().r()); - SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, glConfiguration.colorBufferSize().g()); - SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, glConfiguration.colorBufferSize().b()); - SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, glConfiguration.colorBufferSize().a()); - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, glConfiguration.depthBufferSize()); - SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, glConfiguration.stencilBufferSize()); - - /* Multisampling */ - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, glConfiguration.sampleCount() > 1 ? 1 : 0); - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, glConfiguration.sampleCount()); - - #ifndef CORRADE_TARGET_EMSCRIPTEN - /* sRGB */ - SDL_GL_SetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, glConfiguration.isSrgbCapable()); - #endif - - /** @todo Remove when Emscripten has proper SDL2 support */ - #ifndef CORRADE_TARGET_EMSCRIPTEN - /* Scale window based on DPI */ - _dpiScaling = dpiScaling(configuration); - const Vector2i scaledWindowSize = configuration.size()*_dpiScaling; - - /* Request debug context if GpuValidation is enabled either via the - configuration or via command-line */ - GLConfiguration::Flags glFlags = glConfiguration.flags(); - if((glFlags & GLConfiguration::Flag::GpuValidation) || (_context->configurationFlags() & GL::Context::Configuration::Flag::GpuValidation)) - glFlags |= GLConfiguration::Flag::Debug; - #if SDL_MAJOR_VERSION*1000 + SDL_MINOR_VERSION*100 + SDL_PATCHLEVEL >= 2006 - else if((glFlags & GLConfiguration::Flag::GpuValidationNoError) || (_context->configurationFlags() & GL::Context::Configuration::Flag::GpuValidationNoError)) - glFlags |= GLConfiguration::Flag::NoError; - #endif - - #if SDL_MAJOR_VERSION*1000 + SDL_MINOR_VERSION*100 + SDL_PATCHLEVEL >= 2006 - SDL_GL_SetAttribute(SDL_GL_CONTEXT_NO_ERROR, glFlags >= GLConfiguration::Flag::NoError); - #endif - - /* Set context version, if user-specified */ - if(glConfiguration.version() != GL::Version::None) { - Int major, minor; - std::tie(major, minor) = version(glConfiguration.version()); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor); - - #ifndef MAGNUM_TARGET_GLES - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, glConfiguration.version() >= GL::Version::GL310 ? - SDL_GL_CONTEXT_PROFILE_CORE : SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); - #else - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); - #endif - - /* Mask out the upper 32bits used for other flags */ - SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, int(UnsignedLong(glFlags) & 0xffffffffu)); - - /* Request usable version otherwise */ - } else { - #ifndef MAGNUM_TARGET_GLES - /* First try to create core context. This is needed mainly on macOS and - Mesa, as support for recent OpenGL versions isn't implemented in - compatibility contexts (which are the default). At least GL 3.2 is - needed on macOS, at least GL 3.1 is needed on Mesa. Bite the bullet - and try 3.1 also elsewhere. */ - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - #ifdef CORRADE_TARGET_APPLE - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); - #else - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); - #endif - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - /* Mask out the upper 32bits used for other flags */ - SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, int(UnsignedLong(glFlags) & 0xffffffffu)); - #else - /* For ES the major context version is compile-time constant */ - #ifdef MAGNUM_TARGET_GLES3 - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - #elif defined(MAGNUM_TARGET_GLES2) - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); - #else - #error unsupported OpenGL ES version - #endif - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); - #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(); - return false; - } - - /* Create context */ - _glContext = SDL_GL_CreateContext(_window); - - #ifndef MAGNUM_TARGET_GLES - /* Fall back to (forward compatible) GL 2.1, if version is not - user-specified and either core context creation fails or we are on - binary NVidia/AMD drivers on Linux/Windows or Intel Windows drivers. - Instead of creating forward-compatible context with highest available - version, they force the version to the one specified, which is - completely useless behavior. */ - #ifndef CORRADE_TARGET_APPLE - Containers::StringView vendorString; - #endif - if(glConfiguration.version() == GL::Version::None && (!_glContext - #ifndef CORRADE_TARGET_APPLE - /* Sorry about the UGLY code, HOPEFULLY THERE WON'T BE MORE WORKAROUNDS */ - || (vendorString = reinterpret_cast(glGetString(GL_VENDOR)), - (vendorString == "NVIDIA Corporation"_s || - #ifdef CORRADE_TARGET_WINDOWS - vendorString == "Intel"_s || - #endif - vendorString == "ATI Technologies Inc."_s) - && !_context->isDriverWorkaroundDisabled("no-forward-compatible-core-context"_s)) - #endif - )) { - /* Don't print any warning when doing the workaround, because the bug - will be there probably forever */ - if(!_glContext) Warning() - << "Platform::Sdl2Application::tryCreate(): cannot create core context:" - << SDL_GetError() << "(falling back to compatibility context)"; - else SDL_GL_DeleteContext(_glContext); - - SDL_DestroyWindow(_window); - - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); - /* Discard the ForwardCompatible flag for the fallback. Having it set - makes the fallback context creation fail on Mesa's Zink (which is - just 2.1) and I assume on others as well. - - 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(); - return false; - } - - /* Create compatibility context */ - _glContext = SDL_GL_CreateContext(_window); - } - #endif - - /* Cannot create context (or fallback compatibility context on desktop) */ - if(!_glContext) { - Error() << "Platform::Sdl2Application::tryCreate(): cannot create context:" << SDL_GetError(); - SDL_DestroyWindow(_window); - _window = nullptr; - return false; - } - - #ifdef CORRADE_TARGET_IOS - /* iOS has zero initial GL_VIEWPORT size, get drawable size and put it back - in so all other code can assume that the viewport is set to sane values. - Fortunately on iOS we also don't have to load any function pointers so - it's safe to do the glViewport() call as it is linked statically. */ - { - const Vector2i viewport = framebufferSize(); - glViewport(0, 0, viewport.x(), viewport.y()); - } - #endif - - /* Emscripten-specific initialization */ - #else - /* Get CSS canvas size. This is used later to detect canvas resizes and - fire viewport events, because Emscripten doesn't do that. Related info: - https://github.com/kripken/emscripten/issues/1731 */ - /** @todo don't hardcode "#canvas" here, make it configurable from outside */ - { - Vector2d canvasSize; - /* Emscripten 1.38.27 changed to generic CSS selectors from element - IDs depending on -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1 - being set (which we can't detect at compile time). Fortunately, - using #canvas works the same way both in the previous versions and - the current one. Unfortunately, this is also the only value that - works the same way for both. Further details at - https://github.com/emscripten-core/emscripten/pull/7977 */ - emscripten_get_element_css_size("#canvas", &canvasSize.x(), &canvasSize.y()); - _lastKnownCanvasSize = Vector2i{canvasSize}; - } - - /* By default Emscripten creates a 300x150 canvas. That's so freaking - random I'm getting mad. Use the real (CSS pixels) canvas size instead, - if the size is not hardcoded from the configuration. This is then - multiplied by the DPI scaling. */ - Vector2i windowSize; - if(!configuration.size().isZero()) { - windowSize = configuration.size(); - /* Because hardcoding canvas size for WebGL is usually a wrong thing to - do, notify about that in the verbose output */ - Debug{_verboseLog ? Debug::output() : nullptr} << "Platform::Sdl2Application::tryCreate(): hardcoded canvas size" << windowSize; - } else { - windowSize = _lastKnownCanvasSize; - Debug{_verboseLog ? Debug::output() : nullptr} << "Platform::Sdl2Application::tryCreate(): autodetected canvas size" << windowSize; - } - _dpiScaling = dpiScaling(configuration); - const Vector2i scaledWindowSize = windowSize*_dpiScaling; - - Uint32 flags = SDL_OPENGL|SDL_HWSURFACE|SDL_DOUBLEBUF; - if(configuration.windowFlags() & Configuration::WindowFlag::Resizable) { - _flags |= Flag::Resizable; - /* Actually not sure if this makes any difference: - https://github.com/kripken/emscripten/issues/1731 */ - flags |= SDL_RESIZABLE; - } - - if(!(_surface = SDL_SetVideoMode(scaledWindowSize.x(), scaledWindowSize.y(), 24, flags))) { - Error() << "Platform::Sdl2Application::tryCreate(): cannot create context:" << SDL_GetError(); - return false; - } - #endif - - /* Destroy everything also when the Magnum context creation fails */ - if(!_context->tryCreate(glConfiguration)) { - #ifndef CORRADE_TARGET_EMSCRIPTEN - SDL_GL_DeleteContext(_glContext); - SDL_DestroyWindow(_window); - _window = nullptr; - #else - SDL_FreeSurface(_surface); - #endif - return false; - } - - #ifndef CORRADE_TARGET_EMSCRIPTEN - /* Show the window once we are sure that everything is okay */ - if(!(configuration.windowFlags() & Configuration::WindowFlag::Hidden)) - SDL_ShowWindow(_window); - #endif - - /* Return true if the initialization succeeds */ - return true; -} -#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 - -#ifdef CORRADE_TARGET_EMSCRIPTEN -void Sdl2Application::setContainerCssClass(const Containers::StringView cssClass) { - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdollar-in-identifier-extension" - EM_ASM_({ - /* Handle also the classic #container for backwards compatibility. We - also need to preserve the mn-container otherwise next time we'd have - no way to look for it anymore. - - Using UTF8ToString() instead of AsciiToString() as it has an - explicit size parameter and thus doesn't need a null-terminated - input, which would potentially require yet another allocation. */ - (Module['canvas'].closest('.mn-container') || - document.getElementById('container')).className = (['mn-container', UTF8ToString($0, $1)]).join(' '); - }, cssClass.data(), cssClass.size()); - #pragma GCC diagnostic pop -} -#endif - -void Sdl2Application::swapBuffers() { - #ifndef CORRADE_TARGET_EMSCRIPTEN - SDL_GL_SwapWindow(_window); - #else - SDL_Flip(_surface); - #endif -} - -Int Sdl2Application::swapInterval() const { - return SDL_GL_GetSwapInterval(); -} - -bool Sdl2Application::setSwapInterval(const Int interval) { - if(SDL_GL_SetSwapInterval(interval) == -1) { - Error() << "Platform::Sdl2Application::setSwapInterval(): cannot set swap interval:" << SDL_GetError(); - _flags &= ~Flag::VSyncEnabled; - return false; - } - - /* Setting interval to 1 may cause SDL_GL_GetSwapInterval() to return -1, - which is a valid case */ - const Int actualInterval = SDL_GL_GetSwapInterval(); - if(actualInterval != interval && !(interval == 1 && actualInterval == -1)) { - Error() << "Platform::Sdl2Application::setSwapInterval(): swap interval setting ignored by the driver:" << SDL_GetError(); - _flags &= ~Flag::VSyncEnabled; - return false; - } - - if(interval) _flags |= Flag::VSyncEnabled; - else _flags &= ~Flag::VSyncEnabled; - return true; -} - -void Sdl2Application::redraw() { _flags |= Flag::Redraw; } - -Sdl2Application::~Sdl2Application() { - /* 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. */ - - #ifdef MAGNUM_TARGET_GL - /* Destroy Magnum context first to avoid it potentially accessing the - now-destroyed GL context after */ - _context = Containers::NullOpt; - - #ifndef CORRADE_TARGET_EMSCRIPTEN - if(_glContext) SDL_GL_DeleteContext(_glContext); - #else - if(_surface) SDL_FreeSurface(_surface); - #endif - #endif - - #ifndef CORRADE_TARGET_EMSCRIPTEN - for(auto& cursor: _cursors) - SDL_FreeCursor(cursor); - #endif - - #ifndef CORRADE_TARGET_EMSCRIPTEN - if(_window) SDL_DestroyWindow(_window); - #endif - SDL_Quit(); -} - -int Sdl2Application::exec() { - #ifndef CORRADE_TARGET_EMSCRIPTEN - while(mainLoopIteration()) {} - #else - emscripten_set_main_loop_arg([](void* arg) { - static_cast(arg)->mainLoopIteration(); - }, this, 0, true); - #endif - return _exitCode; -} - -void Sdl2Application::exit(const int exitCode) { - /* On Emscripten this flag is used only to indicate a desire to exit from - mainLoopIteration() */ - _flags |= Flag::Exit; - #ifdef CORRADE_TARGET_EMSCRIPTEN - emscripten_cancel_main_loop(); - #endif - _exitCode = exitCode; -} - -bool Sdl2Application::mainLoopIteration() { - /* If exit was requested directly in the constructor, exit immediately - without calling anything else */ - if(_flags & Flag::Exit) return false; - - #ifndef CORRADE_TARGET_EMSCRIPTEN - CORRADE_ASSERT(_window, "Platform::Sdl2Application::mainLoopIteration(): no window opened", {}); - #else - CORRADE_ASSERT(_surface, "Platform::Sdl2Application::mainLoopIteration(): no window opened", {}); - #endif - - #ifndef CORRADE_TARGET_EMSCRIPTEN - const UnsignedInt timeBefore = _minimalLoopPeriod ? SDL_GetTicks() : 0; - #endif - - #ifdef CORRADE_TARGET_EMSCRIPTEN - /* The resize event is not fired on window resize, so poll for the canvas - size here. But only if the window was requested to be resizable, to - avoid resizing the canvas when the user doesn't want that. Related - issue: https://github.com/kripken/emscripten/issues/1731 */ - if(_flags & Flag::Resizable) { - Vector2d canvasSize; - /* Emscripten 1.38.27 changed to generic CSS selectors from element - IDs depending on -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1 - being set (which we can't detect at compile time). See above for the - reason why we hardcode #canvas here. */ - emscripten_get_element_css_size("#canvas", &canvasSize.x(), &canvasSize.y()); - - const Vector2i canvasSizei{canvasSize}; - if(canvasSizei != _lastKnownCanvasSize) { - _lastKnownCanvasSize = canvasSizei; - const Vector2i size = _dpiScaling*canvasSizei; - emscripten_set_canvas_element_size("#canvas", size.x(), size.y()); - ViewportEvent e{ - #ifdef MAGNUM_TARGET_GL - size, - #endif - size, _dpiScaling}; - viewportEvent(e); - _flags |= Flag::Redraw; - } - } - #endif - - SDL_Event event; - while(SDL_PollEvent(&event)) { - switch(event.type) { - case SDL_WINDOWEVENT: - switch(event.window.event) { - /* Not using SDL_WINDOWEVENT_RESIZED, because that doesn't - get fired when the window is resized programmatically - (such as through setMaxWindowSize()) */ - case SDL_WINDOWEVENT_SIZE_CHANGED: { - #ifdef CORRADE_TARGET_EMSCRIPTEN - /* If anybody sees this assert, then emscripten finally - implemented resize events. Praise them for that. - https://github.com/kripken/emscripten/issues/1731 */ - CORRADE_INTERNAL_ASSERT_UNREACHABLE(); - #else - /* {event.window.data1, event.window.data2} seems to be - framebuffer size and not window size on macOS, which - is weird. Query the values directly instead to be - really sure. */ - ViewportEvent e{event, windowSize(), - #ifdef MAGNUM_TARGET_GL - framebufferSize(), - #endif - _dpiScaling}; - /** @todo handle also WM_DPICHANGED events when a window is moved between displays with different DPI */ - viewportEvent(e); - _flags |= Flag::Redraw; - #endif - } break; - /* 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; - if(!(_flags & Flag::NoAnyEvent)) anyEvent(event); - break; - default: - if(!(_flags & Flag::NoAnyEvent)) anyEvent(event); - } break; - - 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); - } break; - - case SDL_MOUSEBUTTONDOWN: - case SDL_MOUSEBUTTONUP: { - MouseEvent e{event, static_cast(event.button.button), {event.button.x, event.button.y} - #ifndef CORRADE_TARGET_EMSCRIPTEN - , event.button.clicks - #endif - }; - event.type == SDL_MOUSEBUTTONDOWN ? mousePressEvent(e) : mouseReleaseEvent(e); - } break; - - case SDL_MOUSEWHEEL: { - MouseScrollEvent e{event, {Float(event.wheel.x), Float(event.wheel.y)}}; - 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); - break; - } - - case SDL_MULTIGESTURE: { - MultiGestureEvent e{event, {event.mgesture.x, event.mgesture.y}, event.mgesture.dTheta, event.mgesture.dDist, event.mgesture.numFingers}; - multiGestureEvent(e); - break; - } - - case SDL_TEXTINPUT: { - TextInputEvent e{event, event.text.text}; - textInputEvent(e); - } break; - - case SDL_TEXTEDITING: { - TextEditingEvent e{event, event.edit.text, event.edit.start, event.edit.length}; - textEditingEvent(e); - } break; - - case SDL_QUIT: { - ExitEvent e{event}; - exitEvent(e); - if(e.isAccepted()) { - /* On Emscripten this flag is used only to indicate a - desire to exit from mainLoopIteration() */ - _flags |= Flag::Exit; - #ifdef CORRADE_TARGET_EMSCRIPTEN - emscripten_cancel_main_loop(); - #endif - return !(_flags & Flag::Exit); - } - } break; - - /* Direct everything else to anyEvent(), so users can implement - event handling for things not present in the Application APIs */ - default: if(!(_flags & Flag::NoAnyEvent)) anyEvent(event); - } - } - - /* Tick event */ - if(!(_flags & Flag::NoTickEvent)) tickEvent(); - - /* Draw event */ - if(_flags & Flag::Redraw) { - _flags &= ~Flag::Redraw; - drawEvent(); - - #ifndef CORRADE_TARGET_EMSCRIPTEN - /* If VSync is not enabled, delay to prevent CPU hogging (if set) */ - if(!(_flags & Flag::VSyncEnabled) && _minimalLoopPeriod) { - const UnsignedInt loopTime = SDL_GetTicks() - timeBefore; - if(loopTime < _minimalLoopPeriod) - SDL_Delay(_minimalLoopPeriod - loopTime); - } - #endif - - return !(_flags & Flag::Exit); - } - - #ifndef CORRADE_TARGET_EMSCRIPTEN - /* If not drawing anything, delay to prevent CPU hogging (if set) */ - if(_minimalLoopPeriod) { - const UnsignedInt loopTime = SDL_GetTicks() - timeBefore; - if(loopTime < _minimalLoopPeriod) - SDL_Delay(_minimalLoopPeriod - loopTime); - } - - /* Then, if the tick event doesn't need to be called periodically, wait - indefinitely for next input event */ - if(_flags & Flag::NoTickEvent) SDL_WaitEvent(nullptr); - #endif - 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 const char* CursorMap[] { - "default", - "text", - "wait", - "crosshair", - "progress", - "nwse-resize", - "nesw-resize", - "ew-resize", - "ns-resize", - "move", - "not-allowed", - "pointer", - "none" - /* 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)); - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdollar-in-identifier-extension" - EM_ASM_({Module['canvas'].style.cursor = AsciiToString($0);}, CursorMap[UnsignedInt(cursor)]); - #pragma GCC diagnostic pop - #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 */ - #ifndef CORRADE_TARGET_EMSCRIPTEN - SDL_SetWindowGrab(_window, enabled ? SDL_TRUE : SDL_FALSE); - SDL_SetRelativeMouseMode(enabled ? SDL_TRUE : SDL_FALSE); - #else - CORRADE_ASSERT_UNREACHABLE("Sdl2Application::setMouseLocked(): not implemented", ); - static_cast(enabled); - #endif -} -#endif - -bool Sdl2Application::isTextInputActive() { - #ifndef CORRADE_TARGET_EMSCRIPTEN - return SDL_IsTextInputActive(); - #else - return !!(_flags & Flag::TextInputActive); - #endif -} - -void Sdl2Application::startTextInput() { - SDL_StartTextInput(); - #ifdef CORRADE_TARGET_EMSCRIPTEN - _flags |= Flag::TextInputActive; - #endif -} - -void Sdl2Application::stopTextInput() { - SDL_StopTextInput(); - #ifdef CORRADE_TARGET_EMSCRIPTEN - _flags &= ~Flag::TextInputActive; - #endif -} - -void Sdl2Application::setTextInputRect(const Range2Di& rect) { - SDL_Rect r{rect.min().x(), rect.min().y(), rect.sizeX(), rect.sizeY()}; - SDL_SetTextInputRect(&r); -} - -void Sdl2Application::exitEvent(ExitEvent& event) { - event.setAccepted(); -} - -void Sdl2Application::tickEvent() { - /* If this got called, the tick event is not implemented by user and thus - we don't need to call it ever again */ - _flags |= Flag::NoTickEvent; -} - -void Sdl2Application::anyEvent(SDL_Event&) { - /* If this got called, the any event is not implemented by user and thus - we don't need to call it ever again */ - _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(): - #if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_IOS) - _title(Containers::String::nullTerminatedGlobalView("Magnum SDL2 Application"_s)), - #endif - #if !defined(CORRADE_TARGET_IOS) && !defined(CORRADE_TARGET_EMSCRIPTEN) - _size{800, 600}, - #else - _size{}, /* SDL2 detects someting for us */ - #endif - _dpiScalingPolicy{DpiScalingPolicy::Default} {} - -Sdl2Application::Configuration::~Configuration() = default; - -Containers::StringView Sdl2Application::KeyEvent::keyName(const Key key) { - return SDL_GetKeyName(SDL_Keycode(key)); -} - -Containers::StringView Sdl2Application::KeyEvent::keyName() const { - return keyName(_key); -} - -Sdl2Application::InputEvent::Modifiers Sdl2Application::MouseEvent::modifiers() { - if(_modifiers) return *_modifiers; - return *(_modifiers = fixedModifiers(Uint16(SDL_GetModState()))); -} - -Sdl2Application::InputEvent::Modifiers Sdl2Application::MouseMoveEvent::modifiers() { - if(_modifiers) return *_modifiers; - return *(_modifiers = fixedModifiers(Uint16(SDL_GetModState()))); -} - -Vector2i Sdl2Application::MouseScrollEvent::position() { - if(_position) return *_position; - _position = Vector2i{}; - SDL_GetMouseState(&_position->x(), &_position->y()); - return *_position; -} - -Sdl2Application::InputEvent::Modifiers Sdl2Application::MouseScrollEvent::modifiers() { - if(_modifiers) return *_modifiers; - return *(_modifiers = fixedModifiers(Uint16(SDL_GetModState()))); -} - -template class BasicScreen; -template class BasicScreenedApplication; - -}} diff --git a/src/Sdl2Application.h b/src/Sdl2Application.h deleted file mode 100644 index aac12d8..0000000 --- a/src/Sdl2Application.h +++ /dev/null @@ -1,2967 +0,0 @@ -#ifndef Magnum_Platform_Sdl2Application_h -#define Magnum_Platform_Sdl2Application_h -/* - This file is part of Magnum. - - Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, - 2020, 2021, 2022 Vladimír Vondruš - Copyright © 2019 Marco Melorio - Copyright © 2022 Pablo Escobar - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included - in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - DEALINGS IN THE SOFTWARE. -*/ - -/** @file - * @brief Class @ref Magnum::Platform::Sdl2Application, macro @ref MAGNUM_SDL2APPLICATION_MAIN() - */ - -#include -#include -#include /** @todo PIMPL Configuration instead? */ - -#include "Magnum/Magnum.h" -#include "Magnum/Tags.h" -#include "Magnum/Math/Vector4.h" -#include "Magnum/Platform/Platform.h" - -#ifdef MAGNUM_TARGET_GL -#include "Magnum/Platform/GLContext.h" -#endif - -#ifdef CORRADE_TARGET_WINDOWS /* Windows version of SDL2 redefines main(), we don't want that */ -#define SDL_MAIN_HANDLED -#endif - -#ifdef CORRADE_TARGET_CLANG_CL -/* SDL does #pragma pack(push,8) and #pragma pack(pop,8) in different headers - (begin_code.h and end_code.h) and clang-cl doesn't like that, even though it - is completely fine. Silence the warning. */ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpragma-pack" -#endif -/* SDL.h includes the world, adding 50k LOC. We don't want that either. */ -#include -#include -#include /* huh, why is this not pulled in implicitly?! */ -#include -#include - -#ifdef CORRADE_TARGET_IOS -/* Including SDL_main.h unconditionally would mean it'd override Corrade::Main - on Windows (both are parsing wargv and converting them to UTF-8, but ours - does that better and additionally enables ANSI colors and UTF-8 console - output). Right now (SDL 2.0.11) it's only needed for WinRT (which is done - below), Android (which we don't support for SDL) and iOS, so whitelist it - only for iOS. */ -#include -#endif - -#ifdef CORRADE_TARGET_WINDOWS_RT -#include /* For SDL_WinRTRunApp */ -#include /* For the WinMain entrypoint */ -#endif -#ifdef CORRADE_TARGET_CLANG_CL -#pragma clang diagnostic pop -#endif - -#ifdef MAGNUM_BUILD_DEPRECATED -/* Some APIs used to take or return a std::string before */ -#include -#endif - -#ifndef DOXYGEN_GENERATING_OUTPUT -union SDL_Event; /* for anyEvent() */ -#endif - -namespace Magnum { namespace Platform { - -namespace Implementation { - enum class Sdl2DpiScalingPolicy: UnsignedByte; -} - -/** @nosubgrouping -@brief SDL2 application - -@m_keywords{Application} - -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). - -@m_class{m-block m-success} - -@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. - -@section Platform-Sdl2Application-bootstrap Bootstrap application - -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: - -@code{.sh} -mkdir build && cd build -cmake .. -cmake --build . -./src/MyApplication # or ./src/Debug/MyApplication -@endcode - -See @ref cmake for more information. - -@section Platform-Sdl2Application-bootstrap-emscripten Bootstrap application for Emscripten - -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. - -@section Platform-Sdl2Application-bootstrap-ios Bootstrap application for iOS - -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. - -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. - -@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 - -You can then open the generated project file in Xcode and build/deploy it from -there. - -@section Platform-Sdl2Application-bootstrap-winrt Bootstrap application for Windows RT - -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. - -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: - -@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 - -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. - -@section Platform-Sdl2Application-usage General usage - -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: - -@code{.cmake} -find_package(Magnum REQUIRED Sdl2Application) - -# ... -target_link_libraries(your-app PRIVATE Magnum::Sdl2Application) -@endcode - -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. - -@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) - -set(MAGNUM_WITH_SDL2APPLICATION ON CACHE BOOL "" FORCE) -add_subdirectory(magnum EXCLUDE_FROM_ALL) -@endcode - - - -@m_class{m-note m-warning} - -@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. - -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. - -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. - -@code{.cpp} -class MyApplication: public Platform::Sdl2Application { - // implement required methods... -}; -MAGNUM_SDL2APPLICATION_MAIN(MyApplication) -@endcode - -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. - -@subsection Platform-Sdl2Application-usage-power Power management - -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. - -@code{.sh} -SDL_VIDEO_ALLOW_SCREENSAVER=0 ./your-app -@endcode - -@subsection Platform-Sdl2Application-usage-posix POSIX specifics - -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: - -@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 @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. - -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: - -- @ref Configuration::WindowFlag::Borderless hides the menu bar -- @ref 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 @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. - -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. - -@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 - @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. - -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 - @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. - -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: - /** @brief Application arguments */ - struct Arguments { - /** @brief Constructor */ - /*implicit*/ constexpr Arguments(int& argc, char** argv) noexcept: argc{argc}, argv{argv} {} - - int& argc; /**< @brief Argument count */ - char** argv; /**< @brief Argument values */ - }; - - 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; - - #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 - - /** - * @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); - - /** - * @brief Construct with default configuration - * - * Equivalent to calling @ref Sdl2Application(const Arguments&, const Configuration&) - * with default-constructed @ref Configuration. - */ - explicit Sdl2Application(const Arguments& arguments); - - /** - * @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); - - /** @brief Copying is not allowed */ - Sdl2Application(const Sdl2Application&) = delete; - - /** @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 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(); - - /** - * @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(); - - /** - * @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); - - #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 - - #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 - - protected: - /* Nobody will need to have (and delete) Sdl2Application*, thus this is - faster than public pure virtual destructor */ - ~Sdl2Application(); - - #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 - - /** - * @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); - - /** - * @brief Create a window with default configuration and OpenGL context - * - * Equivalent to calling @ref create(const Configuration&) with - * default-constructed @ref Configuration. - */ - void create(); - - #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); - - /** @{ @name Screen handling */ - - 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 !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); - - /** - * @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); - - /** - * @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 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 - - /** - * @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 { return _dpiScaling; } - - /** - * @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); - - /** - * @brief Set window title - * @m_since{2019,10} - * - * The @p title is expected to be encoded in UTF-8. - */ - void setWindowTitle(Containers::StringView title); - - #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 - - #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 - - /** - * @brief Swap buffers - * - * Paints currently rendered framebuffer on screen. - * @see @ref setSwapInterval() - */ - void swapBuffers(); - - /** @brief Swap interval */ - Int swapInterval() const; - - /** - * @brief Set swap interval - * - * 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 - * - * 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() - */ - void setMinimalLoopPeriod(UnsignedInt milliseconds) { - _minimalLoopPeriod = milliseconds; - } - #endif - - /** - * @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(); - - 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); - - /** - * @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; - - /* Since 1.8.17, the original short-hand group closing doesn't work - anymore. FFS. */ - /** - * @} - */ - - /** @{ @name Keyboard handling */ - - /** - * @brief Key press event - * - * Called when an key is pressed. Default implementation does nothing. - */ - virtual void keyPressEvent(KeyEvent& event); - - /** - * @brief Key release event - * - * Called when an key is released. Default implementation does nothing. - */ - virtual void keyReleaseEvent(KeyEvent& event); - - /* Since 1.8.17, the original short-hand group closing doesn't work - anymore. FFS. */ - /** - * @} - */ - - /** @{ @name Mouse handling */ - - 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 */ - - #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 - }; - - /** - * @brief Set cursor type - * @m_since{2020,06} - * - * 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} - */ - Cursor cursor(); - - #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 - - #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 Enable or disable mouse locking - * - * @m_deprecated_since{2020,06} Use @ref setCursor() together with - * @ref Cursor::HiddenLocked instead. - */ - CORRADE_DEPRECATED("use setCursor() together with Cursor::HiddenLocked instead") void setMouseLocked(bool enabled); - #endif - - private: - /** - * @brief Mouse press event - * - * Called when mouse button is pressed. Default implementation does - * nothing. - */ - virtual void mousePressEvent(MouseEvent& event); - - /** - * @brief Mouse release event - * - * Called when mouse button is released. Default implementation does - * nothing. - */ - virtual void mouseReleaseEvent(MouseEvent& event); - - /** - * @brief Mouse move event - * - * Called when mouse is moved. Default implementation does nothing. - */ - virtual void mouseMoveEvent(MouseMoveEvent& event); - - /** - * @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); - - /* Since 1.8.17, the original short-hand group closing doesn't work - anymore. FFS. */ - /** - * @} - */ - - /** @{ @name Touch gesture handling */ - - /** - * @brief Multi gesture event - * - * Called when the user performs a gesture using multiple fingers. - * Default implementation does nothing. - * @experimental - */ - virtual void multiGestureEvent(MultiGestureEvent& event); - - /* Since 1.8.17, the original short-hand group closing doesn't work - anymore. FFS. */ - /** - * @} - */ - - /** @{ @name Text input handling */ - public: - /** - * @brief Whether text input is active - * - * If text input is active, text input events go to @ref textInputEvent() - * and @ref textEditingEvent(). - * @note Note that in @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten" the - * value is emulated and might not reflect external events like - * closing on-screen keyboard. - * @see @ref startTextInput(), @ref stopTextInput() - */ - bool isTextInputActive(); - - /** - * @brief Start text input - * - * Starts text input that will go to @ref textInputEvent() and - * @ref textEditingEvent(). - * @see @ref stopTextInput(), @ref isTextInputActive(), - * @ref setTextInputRect() - */ - void startTextInput(); - - /** - * @brief Stop text input - * - * Stops text input that went to @ref textInputEvent() and - * @ref textEditingEvent(). - * @see @ref startTextInput(), @ref isTextInputActive(), @ref textInputEvent() - * @ref textEditingEvent() - */ - void stopTextInput(); - - /** - * @brief Set text input rectangle - * - * The @p rect defines an area where the text is being displayed, for - * example to hint the system where to place on-screen keyboard. - */ - 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. */ - /** - * @} - */ - - /** @{ @name Special events */ - - /** - * @brief Exit event - * - * If implemented, it allows the application to react to an application - * exit (for example to save its internal state) and suppress it as - * well (for example to show a exit confirmation dialog). The default - * implementation calls @ref ExitEvent::setAccepted() on @p event, - * which tells the application that it's safe to exit. - * - * SDL has special behavior on POSIX systems regarding `SIGINT` and - * `SIGTERM` handling, see @ref Platform-Sdl2Application-usage-posix - * for more information. - */ - virtual void exitEvent(ExitEvent& event); - - protected: - /** - * @brief Tick event - * - * If implemented, this function is called periodically after - * processing all input events and before draw event even though there - * might be no input events and redraw is not requested. Useful e.g. - * for asynchronous task polling. Use @ref setMinimalLoopPeriod()/ - * @ref setSwapInterval() to control main loop frequency. - * - * If this implementation gets called from its @cpp override @ce, it - * will effectively stop the tick event from being fired and the app - * returns back to waiting for input events. This can be used to - * disable the tick event when not needed. - */ - virtual void tickEvent(); - - private: - /** - * @brief Any event - * - * Called in case a SDL event is not handled by any other event - * functions above. - * @see @ref ViewportEvent::event(), @ref InputEvent::event(), - * @ref MultiGestureEvent::event(), @ref TextInputEvent::event(), - * @ref TextEditingEvent::event(), @ref ExitEvent::event() - */ - virtual void anyEvent(SDL_Event& event); - - /* Since 1.8.17, the original short-hand group closing doesn't work - anymore. FFS. */ - /** - * @} - */ - - private: - enum class Flag: UnsignedByte; - typedef Containers::EnumSet Flags; - CORRADE_ENUMSET_FRIEND_OPERATORS(Flags) - - #ifndef CORRADE_TARGET_EMSCRIPTEN - SDL_Cursor* _cursors[12]{}; - #else - Cursor _cursor; - #endif - - /* These are saved from command-line arguments */ - bool _verboseLog{}; - Implementation::Sdl2DpiScalingPolicy _commandLineDpiScalingPolicy{}; - Vector2 _commandLineDpiScaling; - - Vector2 _dpiScaling; - - #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{}; - #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 - - Flags _flags; - - int _exitCode = 0; -}; - -#ifdef MAGNUM_TARGET_GL -/** -@brief OpenGL context configuration - -The created window is always with a double-buffered OpenGL 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. - -@see @ref Sdl2Application(), @ref create(), @ref tryCreate() -*/ -class Sdl2Application::GLConfiguration: public GL::Context::Configuration { - public: - /** - * @brief Context flag - * - * Includes also everything from @ref GL::Context::Configuration::Flag - * except for @relativeref{GL::Context::Configuration,Flag::Windowless}, - * which is not meant to be enabled for windowed apps. - * @see @ref Flags, @ref setFlags(), @ref GL::Context::Flag - */ - enum class Flag: UnsignedLong { - #ifndef CORRADE_TARGET_EMSCRIPTEN - #ifndef MAGNUM_TARGET_GLES - /** - * Forward compatible context. - * @requires_gl Core/compatibility profile distinction and forward - * compatibility applies only to desktop GL. - */ - ForwardCompatible = SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG, - #endif - - /** - * Debug context. Enabled automatically if supported by the driver - * and the @ref Flag::GpuValidation flag is set or if the - * `--magnum-gpu-validation` @ref GL-Context-usage-command-line "command-line option" - * is set to `on`. - * @requires_gles Context flags are not available in WebGL. - */ - Debug = SDL_GL_CONTEXT_DEBUG_FLAG, - - /** - * Context with robust access. - * @requires_gles Context flags are not available in WebGL. - */ - RobustAccess = SDL_GL_CONTEXT_ROBUST_ACCESS_FLAG, - - /** - * Context with reset isolation. - * @requires_gles Context flags are not available in WebGL. - */ - ResetIsolation = SDL_GL_CONTEXT_RESET_ISOLATION_FLAG, - - #if SDL_MAJOR_VERSION*1000 + SDL_MINOR_VERSION*100 + SDL_PATCHLEVEL >= 2006 || defined(DOXYGEN_GENERATING_OUTPUT) - /** - * Context without error reporting. Might result in better - * performance, but situations that would have generated errors - * instead cause undefined behavior. Enabled automatically if - * supported by the driver and the @ref Flag::GpuValidationNoError - * flag is set or if the `--magnum-gpu-validation` @ref GL-Context-usage-command-line "command-line option" - * is set to `no-error`. - * - * @note Available since SDL 2.0.6. - * @requires_gles Context flags are not available in WebGL. - * @m_since_latest - */ - /* Treated as a separate attribute and not a flag in SDL, thus - handling manually. */ - NoError = 1ull << 32, - #endif - #endif - - /** - * @copydoc GL::Context::Configuration::Flag::QuietLog - * @m_since_latest - */ - QuietLog = UnsignedLong(GL::Context::Configuration::Flag::QuietLog), - - /** - * @copydoc GL::Context::Configuration::Flag::VerboseLog - * @m_since_latest - */ - VerboseLog = UnsignedLong(GL::Context::Configuration::Flag::VerboseLog), - - /** - * @copydoc GL::Context::Configuration::Flag::GpuValidation - * @m_since_latest - */ - GpuValidation = UnsignedLong(GL::Context::Configuration::Flag::GpuValidation), - - /** - * @copydoc GL::Context::Configuration::Flag::GpuValidationNoError - * @m_since_latest - */ - GpuValidationNoError = UnsignedLong(GL::Context::Configuration::Flag::GpuValidationNoError) - }; - - /** - * @brief Context flags - * - * @see @ref setFlags(), @ref GL::Context::Flags - */ - typedef Containers::EnumSet Flags; - - explicit GLConfiguration(); - ~GLConfiguration(); - - /** @brief Context flags */ - Flags flags() const { - return Flag(UnsignedLong(GL::Context::Configuration::flags())); - } - - /** - * @brief Set context flags - * @return Reference to self (for method chaining) - * - * Default is @ref Flag::ForwardCompatible on desktop GL and no flags - * on OpenGL ES. To avoid clearing default flags by accident, prefer to - * use @ref addFlags() and @ref clearFlags() instead. - * @see @ref GL::Context::flags() - */ - GLConfiguration& setFlags(Flags flags) { - GL::Context::Configuration::setFlags(GL::Context::Configuration::Flag(UnsignedLong(flags))); - return *this; - } - - /** - * @brief Add context flags - * @return Reference to self (for method chaining) - * - * Unlike @ref setFlags(), ORs the flags with existing instead of - * replacing them. Useful for preserving the defaults. - * @see @ref clearFlags() - */ - GLConfiguration& addFlags(Flags flags) { - GL::Context::Configuration::addFlags(GL::Context::Configuration::Flag(UnsignedLong(flags))); - return *this; - } - - /** - * @brief Clear context flags - * @return Reference to self (for method chaining) - * - * Unlike @ref setFlags(), ANDs the inverse of @p flags with existing - * instead of replacing them. Useful for removing default flags. - * @see @ref addFlags() - */ - GLConfiguration& clearFlags(Flags flags) { - GL::Context::Configuration::clearFlags(GL::Context::Configuration::Flag(UnsignedLong(flags))); - return *this; - } - - #ifndef CORRADE_TARGET_EMSCRIPTEN - /** - * @brief Context version - * - * @note Not available in @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". - */ - GL::Version version() const { return _version; } - #endif - - /** - * @brief Set context version - * - * If requesting version greater or equal to OpenGL 3.1, core profile - * is used. The created context will then have any version which is - * backwards-compatible with requested one. Default is - * @ref GL::Version::None, i.e. any provided version is used. - * @note In @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten" this function - * does nothing (@ref GL::Version::GLES200 or - * @ref GL::Version::GLES300 is used implicitly based on the - * target). - */ - GLConfiguration& setVersion(GL::Version version) { - #ifndef CORRADE_TARGET_EMSCRIPTEN - _version = version; - #else - static_cast(version); - #endif - return *this; - } - - /** @brief Color buffer size */ - Vector4i colorBufferSize() const { return _colorBufferSize; } - - /** - * @brief Set color buffer size - * - * Default is @cpp {8, 8, 8, 8} @ce (8-bit-per-channel RGBA). - * @see @ref setDepthBufferSize(), @ref setStencilBufferSize() - */ - GLConfiguration& setColorBufferSize(const Vector4i& size) { - _colorBufferSize = size; - return *this; - } - - /** @brief Depth buffer size */ - Int depthBufferSize() const { return _depthBufferSize; } - - /** - * @brief Set depth buffer size - * - * Default is @cpp 24 @ce bits. - * @see @ref setColorBufferSize(), @ref setStencilBufferSize() - */ - GLConfiguration& setDepthBufferSize(Int size) { - _depthBufferSize = size; - return *this; - } - - /** @brief Stencil buffer size */ - Int stencilBufferSize() const { return _stencilBufferSize; } - - /** - * @brief Set stencil buffer size - * - * Default is @cpp 0 @ce bits (i.e., no stencil buffer). - * @see @ref setColorBufferSize(), @ref setDepthBufferSize() - */ - GLConfiguration& setStencilBufferSize(Int size) { - _stencilBufferSize = size; - return *this; - } - - /** @brief Sample count */ - Int sampleCount() const { return _sampleCount; } - - /** - * @brief Set sample count - * @return Reference to self (for method chaining) - * - * Default is @cpp 0 @ce, thus no multisampling. See also - * @ref GL::Renderer::Feature::Multisampling. - */ - GLConfiguration& setSampleCount(Int count) { - _sampleCount = count; - return *this; - } - - #ifndef CORRADE_TARGET_EMSCRIPTEN - /** - * @brief sRGB-capable default framebuffer - * - * @note Not available in @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". - */ - bool isSrgbCapable() const { return _srgbCapable; } - - /** - * @brief Set sRGB-capable default framebuffer - * @return Reference to self (for method chaining) - * - * Default is @cpp false @ce. See also - * @ref GL::Renderer::Feature::FramebufferSrgb. - * @note Not available in @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". - */ - GLConfiguration& setSrgbCapable(bool enabled) { - _srgbCapable = enabled; - return *this; - } - #endif - - /* Overloads to remove WTF-factor from method chaining order */ - #ifndef DOXYGEN_GENERATING_OUTPUT - MAGNUM_GL_CONTEXT_CONFIGURATION_SUBCLASS_IMPLEMENTATION(GLConfiguration) - #endif - - private: - Vector4i _colorBufferSize; - Int _depthBufferSize, _stencilBufferSize; - Int _sampleCount; - #ifndef CORRADE_TARGET_EMSCRIPTEN - GL::Version _version; - bool _srgbCapable; - #endif -}; - -#ifndef CORRADE_TARGET_EMSCRIPTEN -CORRADE_ENUMSET_OPERATORS(Sdl2Application::GLConfiguration::Flags) -#endif -#endif - -namespace Implementation { - enum class Sdl2DpiScalingPolicy: UnsignedByte { - /* Using 0 for an "unset" value */ - - #ifdef CORRADE_TARGET_APPLE - Framebuffer = 1, - #endif - - #ifndef CORRADE_TARGET_APPLE - #if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_ANDROID) - Virtual = 2, - #endif - - Physical = 3, - #endif - - Default - #ifdef CORRADE_TARGET_APPLE - = Framebuffer - #elif !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_ANDROID) - = Virtual - #else - = Physical - #endif - }; -} - -/** -@brief Configuration - -@see @ref Sdl2Application(), @ref GLConfiguration, @ref create(), - @ref tryCreate() -*/ -class Sdl2Application::Configuration { - public: - /** - * @brief Window flag - * - * @see @ref WindowFlags, @ref setWindowFlags() - */ - enum class WindowFlag: Uint32 { - /** - * Resizable window. On iOS this allows the application to respond - * to display orientation changes, on - * @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten" this causes the - * framebuffer to be resized when the @cb{.html} @ce size - * changes. - * - * Implement @ref viewportEvent() to react to the resizing events. - */ - Resizable = SDL_WINDOW_RESIZABLE, - - #ifndef CORRADE_TARGET_EMSCRIPTEN - /** - * Fullscreen window - * - * @note Not available on @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". - */ - Fullscreen = SDL_WINDOW_FULLSCREEN, - - /** - * Fullscreen window at the current desktop resolution - * @m_since{2020,06} - * - * @note Not available on @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". - */ - FullscreenDesktop = SDL_WINDOW_FULLSCREEN_DESKTOP, - - /** - * No window decoration. On iOS this hides the menu bar. - * - * @note Not available on @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". - */ - Borderless = SDL_WINDOW_BORDERLESS, - #endif - - #ifndef CORRADE_TARGET_EMSCRIPTEN - /** - * Hidden window - * - * @note Not available on @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". - */ - Hidden = SDL_WINDOW_HIDDEN, - - /** - * Maximized window - * - * @note Not available on @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". - */ - Maximized = SDL_WINDOW_MAXIMIZED, - - /** - * Minimized window - * - * @note Not available on @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". - */ - Minimized = SDL_WINDOW_MINIMIZED, - - /** - * Window with mouse locked - * - * @note Not available on @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". - * @todo SDL_WINDOW_MOUSE_CAPTURE, also what all those do? isn't it - * redundant / better handled with cursor APIs? - */ - MouseLocked = SDL_WINDOW_INPUT_GRABBED, - - /** @todo SDL_WINDOW_INPUT_FOCUS, SDL_WINDOW_MOUSE_FOCUS, GLFW has - GLFW_FOCUSED (exposed as Focused) and GLFW_FOCUS_ON_SHOW (not - exposed) -- what's the relation? How to make these compatible? */ - - #if SDL_MAJOR_VERSION*1000 + SDL_MINOR_VERSION*100 + SDL_PATCHLEVEL >= 2005 || defined(DOXYGEN_GENERATING_OUTPUT) - /** - * Always on top - * @m_since{2020,06} - * - * @note Available since SDL 2.0.5, not available on - * @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". According to - * SDL docs works only on X11. - */ - AlwaysOnTop = SDL_WINDOW_ALWAYS_ON_TOP, - - /** - * Don't add the window to taskbar - * @m_since{2020,06} - * - * @note Available since SDL 2.0.5, not available on - * @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". According to - * SDL docs works only on X11. - */ - SkipTaskbar = SDL_WINDOW_SKIP_TASKBAR, - - /** - * Window should be treated as a utility window - * @m_since{2020,06} - * - * @note Available since SDL 2.0.5, not available on - * @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". According to - * SDL docs works only on X11. - */ - Utility = SDL_WINDOW_UTILITY, - - /** - * Window should be treated as a tooltip - * @m_since{2020,06} - * - * @note Available since SDL 2.0.5, not available on - * @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". According to - * SDL docs works only on X11. - */ - Tooltip = SDL_WINDOW_TOOLTIP, - - /** - * Window should be treated as a popup menu - * @m_since{2020,06} - * - * @note Available since SDL 2.0.5, not available on - * @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". According to - * SDL docs works only on X11. - */ - PopupMenu = SDL_WINDOW_POPUP_MENU, - #endif - #endif - - /** - * 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. - */ - Contextless = 1u << 31, /* Hope this won't ever conflict with anything */ - - /** - * 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} - */ - 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 WindowFlag::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 Window flags - * - * @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 - * - * DPI scaling policy when requesting a particular window size. Can - * be overridden on command-line using `--magnum-dpi-scaling` or via - * the `MAGNUM_DPI_SCALING` environment variable. - * @see @ref setSize(), @ref Platform-Sdl2Application-dpi - */ - #ifdef DOXYGEN_GENERATING_OUTPUT - enum class DpiScalingPolicy: UnsignedByte { - /** - * Framebuffer DPI scaling. The window will have the same size as - * requested, but the framebuffer size will be different. Supported - * only on macOS and iOS and is also the only supported value - * there. - */ - Framebuffer, - - /** - * Virtual DPI scaling. Scales the window based on UI scaling - * setting in the system. Falls back to - * @ref DpiScalingPolicy::Physical on platforms that don't support - * it. Supported only on desktop platforms (except macOS) and it's - * the default there. - * - * Equivalent to `--magnum-dpi-scaling virtual` passed on - * command-line. - */ - Virtual, - - /** - * 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 size on given display physical - * DPI. On platforms that don't have a concept of a window it - * causes the framebuffer to match screen pixels 1:1 without any - * scaling. Supported on desktop platforms except macOS and on - * mobile and web. Default on mobile and web. - * - * Equivalent to `--magnum-dpi-scaling physical` passed on - * command-line. - */ - Physical, - - /** - * Default policy for current platform. Alias to one of - * @ref DpiScalingPolicy::Framebuffer, @ref DpiScalingPolicy::Virtual - * or @ref DpiScalingPolicy::Physical depending on platform. See - * @ref Platform-Sdl2Application-dpi for details. - */ - Default - }; - #else - typedef Implementation::Sdl2DpiScalingPolicy DpiScalingPolicy; - #endif - - /*implicit*/ Configuration(); - ~Configuration(); - - #if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_IOS) - /** - * @brief Window title - * - * The returned string view is - * @relativeref{Corrade,Containers::StringViewFlag::NullTerminated} and - * is valid until the next call to @ref setTitle(). - * @note Not available in @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten" - * and @ref CORRADE_TARGET_IOS "iOS". - */ - Containers::StringView title() const { return _title; } - #endif - - /** - * @brief Set window title - * @return Reference to self (for method chaining) - * - * Default is @cpp "Magnum SDL2 Application" @ce. - * @note On @ref CORRADE_TARGET_IOS "iOS" this function does nothing - * and is included only for compatibility. You need to set the - * title separately in platform-specific configuration file. - * @note Similarly, on @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten" this - * function is only for compatibility, as the page title is - * expected to be set by the HTML markup. However, it's possible - * to change the page title later (for example in response to - * application state change) using @ref setWindowTitle(). - */ - #if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_IOS) - Configuration& setTitle(const Containers::StringView title) { - _title = Containers::String::nullTerminatedGlobalView(title); - return *this; - } - #else - template Configuration& setTitle(const T&) { return *this; } - #endif - - /** @brief Window size */ - Vector2i size() const { return _size; } - - /** - * @brief DPI scaling policy - * - * If @ref dpiScaling() is non-zero, it has a priority over this value. - * The `--magnum-dpi-scaling` command-line option has a priority over - * any application-set value. - * @see @ref setSize(const Vector2i&, DpiScalingPolicy) - */ - DpiScalingPolicy dpiScalingPolicy() const { return _dpiScalingPolicy; } - - /** - * @brief Custom DPI scaling - * - * If zero, then @ref dpiScalingPolicy() has a priority over this - * value. The `--magnum-dpi-scaling` command-line option has a priority - * over any application-set value. - * @see @ref setSize(const Vector2i&, const Vector2&) - */ - Vector2 dpiScaling() const { return _dpiScaling; } - - /** - * @brief Set window size - * @param size Desired window size - * @param dpiScalingPolicy Policy based on which DPI scaling will be set - * @return Reference to self (for method chaining) - * - * Default is @cpp {800, 600} @ce on desktop platforms. On - * @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten" and iOS the default is a - * zero vector, meaning a value that matches the display or canvas size - * is autodetected. See @ref Platform-Sdl2Application-dpi for more - * information. - * @see @ref setSize(const Vector2i&, const Vector2&) - */ - Configuration& setSize(const Vector2i& size, DpiScalingPolicy dpiScalingPolicy = DpiScalingPolicy::Default) { - _size = size; - _dpiScalingPolicy = dpiScalingPolicy; - return *this; - } - - /** - * @brief Set window size with custom DPI scaling - * @param size Desired window size - * @param dpiScaling Custom DPI scaling value - * - * Compared to @ref setSize(const Vector2i&, DpiScalingPolicy) which - * autodetects the DPI scaling value according to given policy, this - * function sets the DPI scaling directly. The resulting - * @ref Sdl2Application::windowSize() is @cpp size*dpiScaling @ce and - * @ref Sdl2Application::dpiScaling() is @p dpiScaling. - */ - Configuration& setSize(const Vector2i& size, const Vector2& dpiScaling) { - _size = size; - _dpiScaling = dpiScaling; - return *this; - } - - /** @brief Window flags */ - WindowFlags windowFlags() const { return _windowFlags; } - - /** - * @brief Set window flags - * @return Reference to self (for method chaining) - * - * Default are none. To avoid clearing default flags by accident, - * prefer to use @ref addWindowFlags() and @ref clearWindowFlags() - * instead. - */ - Configuration& setWindowFlags(WindowFlags flags) { - _windowFlags = flags; - return *this; - } - - /** - * @brief Add window flags - * @return Reference to self (for method chaining) - * @m_since{2020,06} - * - * Unlike @ref setWindowFlags(), ORs the flags with existing instead of - * replacing them. Useful for preserving the defaults. - * @see @ref clearWindowFlags() - */ - Configuration& addWindowFlags(WindowFlags flags) { - _windowFlags |= flags; - return *this; - } - - /** - * @brief Clear window flags - * @return Reference to self (for method chaining) - * @m_since{2020,06} - * - * Unlike @ref setWindowFlags(), ANDs the inverse of @p flags with - * existing instead of replacing them. Useful for removing default - * flags. - * @see @ref addWindowFlags() - */ - Configuration& clearWindowFlags(WindowFlags flags) { - _windowFlags &= ~flags; - return *this; - } - - private: - #if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_IOS) - Containers::String _title; - #endif - Vector2i _size; - DpiScalingPolicy _dpiScalingPolicy; - WindowFlags _windowFlags; - Vector2 _dpiScaling; -}; - -/** -@brief Exit event - -@see @ref exitEvent() -*/ -class Sdl2Application::ExitEvent { - public: - /** @brief Copying is not allowed */ - ExitEvent(const ExitEvent&) = delete; - - /** @brief Moving is not allowed */ - ExitEvent(ExitEvent&&) = delete; - - /** @brief Copying is not allowed */ - ExitEvent& operator=(const ExitEvent&) = delete; - - /** @brief Moving is not allowed */ - ExitEvent& operator=(ExitEvent&&) = delete; - - /** @brief Whether the event is accepted */ - bool isAccepted() const { return _accepted; } - - /** - * @brief Set event as accepted - * - * If the event is ignored (i.e., not set as accepted) in - * @ref exitEvent(), the application won't exit. Default implementation - * of @ref exitEvent() accepts the event. - */ - void setAccepted(bool accepted = true) { _accepted = accepted; } - - /** - * @brief Underlying SDL event - * - * Of type `SDL_QUIT`. - * @see @ref Sdl2Application::anyEvent() - */ - const SDL_Event& event() const { return _event; } - - private: - friend Sdl2Application; - - explicit ExitEvent(const SDL_Event& event): _event(event), _accepted(false) {} - - const SDL_Event& _event; - bool _accepted; -}; - -/** -@brief Viewport event - -@see @ref viewportEvent() -*/ -class Sdl2Application::ViewportEvent { - public: - /** @brief Copying is not allowed */ - ViewportEvent(const ViewportEvent&) = delete; - - /** @brief Moving is not allowed */ - ViewportEvent(ViewportEvent&&) = delete; - - /** @brief Copying is not allowed */ - ViewportEvent& operator=(const ViewportEvent&) = delete; - - /** @brief Moving is not allowed */ - ViewportEvent& operator=(ViewportEvent&&) = delete; - - /** - * @brief Window size - * - * On some platforms with HiDPI displays, window size can be different - * from @ref framebufferSize(). See @ref Platform-Sdl2Application-dpi - * for more information. - * @see @ref Sdl2Application::windowSize() - */ - Vector2i windowSize() const { return _windowSize; } - - #if defined(MAGNUM_TARGET_GL) || defined(DOXYGEN_GENERATING_OUTPUT) - /** - * @brief Framebuffer size - * - * On some platforms with HiDPI displays, framebuffer size can be - * different from @ref windowSize(). 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() - */ - Vector2i framebufferSize() const { return _framebufferSize; } - #endif - - /** - * @brief DPI scaling - * - * On some platforms moving an app between displays can result in DPI - * scaling value being changed in tandem with a window/framebuffer - * size. Simply resizing a window doesn't change the DPI scaling value. - * See @ref Platform-Sdl2Application-dpi for more information. - * @see @ref Sdl2Application::dpiScaling() - */ - Vector2 dpiScaling() const { return _dpiScaling; } - - #ifndef CORRADE_TARGET_EMSCRIPTEN - /** - * @brief Underlying SDL event - * - * Of type `SDL_WINDOWEVENT`. - * @note Not available in @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". - * @see @ref Sdl2Application::anyEvent() - */ - const SDL_Event& event() const { return _event; } - #endif - - private: - friend Sdl2Application; - - explicit ViewportEvent( - #ifndef CORRADE_TARGET_EMSCRIPTEN - const SDL_Event& event, - #endif - const Vector2i& windowSize, - #ifdef MAGNUM_TARGET_GL - const Vector2i& framebufferSize, - #endif - const Vector2& dpiScaling): - #ifndef CORRADE_TARGET_EMSCRIPTEN - _event(event), - #endif - _windowSize{windowSize}, - #ifdef MAGNUM_TARGET_GL - _framebufferSize{framebufferSize}, - #endif - _dpiScaling{dpiScaling} {} - - #ifndef CORRADE_TARGET_EMSCRIPTEN - const SDL_Event& _event; - #endif - const Vector2i _windowSize; - #ifdef MAGNUM_TARGET_GL - const Vector2i _framebufferSize; - #endif - const Vector2 _dpiScaling; -}; - -/** -@brief Base for input events - -@see @ref KeyEvent, @ref MouseEvent, @ref MouseMoveEvent, @ref keyPressEvent(), - @ref keyReleaseEvent(), @ref mousePressEvent(), @ref mouseReleaseEvent(), - @ref mouseMoveEvent() -*/ -class Sdl2Application::InputEvent { - public: - /** - * @brief Modifier - * - * @see @ref Modifiers, @ref KeyEvent::modifiers(), - * @ref MouseEvent::modifiers(), @ref MouseMoveEvent::modifiers() - */ - enum class Modifier: Uint16 { - /** - * Shift - * - * @see @ref KeyEvent::Key::LeftShift, @ref KeyEvent::Key::RightShift - */ - Shift = KMOD_SHIFT, - - /** - * Ctrl - * - * @see @ref KeyEvent::Key::LeftCtrl, @ref KeyEvent::Key::RightCtrl - */ - Ctrl = KMOD_CTRL, - - /** - * Alt - * - * @see @ref KeyEvent::Key::LeftAlt, @ref KeyEvent::Key::RightAlt - */ - Alt = KMOD_ALT, - - /** - * Super key (Windows/⌘) - * - * @see @ref KeyEvent::Key::LeftSuper, @ref KeyEvent::Key::RightSuper - */ - Super = KMOD_GUI, - - /** - * AltGr - * - * @see @ref KeyEvent::Key::AltGr - */ - AltGr = KMOD_MODE, - - CapsLock = KMOD_CAPS, /**< Caps lock */ - NumLock = KMOD_NUM /**< Num lock */ - }; - - /** - * @brief Set of modifiers - * - * @see @ref KeyEvent::modifiers(), @ref MouseEvent::modifiers(), - * @ref MouseMoveEvent::modifiers() - */ - typedef Containers::EnumSet Modifiers; - - /** @brief Copying is not allowed */ - InputEvent(const InputEvent&) = delete; - - /** @brief Moving is not allowed */ - InputEvent(InputEvent&&) = delete; - - /** @brief Copying is not allowed */ - InputEvent& operator=(const InputEvent&) = delete; - - /** @brief Moving is not allowed */ - InputEvent& operator=(InputEvent&&) = delete; - - /** @brief Whether the event is accepted */ - bool isAccepted() const { return _accepted; } - - /** - * @brief Set event as accepted - * - * If the event is ignored (i.e., not set as accepted), it might be - * propagated elsewhere, for example to another screen when using - * @ref BasicScreenedApplication "ScreenedApplication". By default is - * each event ignored and thus propagated. - */ - void setAccepted(bool accepted = true) { _accepted = accepted; } - - /** - * @brief Underlying SDL event - * - * Of type `SDL_KEYDOWN` / `SDL_KEYUP` for @ref KeyEvent, - * `SDL_MOUSEBUTTONUP` / `SDL_MOUSEBUTTONDOWN` for @ref MouseEvent, - * `SDL_MOUSEWHEEL` for @ref MouseScrollEvent and `SDL_MOUSEMOTION` for - * @ref MouseMoveEvent. - * @see @ref Sdl2Application::anyEvent() - */ - const SDL_Event& event() const { return _event; } - - #ifndef DOXYGEN_GENERATING_OUTPUT - protected: - explicit InputEvent(const SDL_Event& event): _event(event), _accepted(false) {} - - ~InputEvent() = default; - #endif - - private: - const SDL_Event& _event; - bool _accepted; -}; - -/** -@brief Key event - -@see @ref keyPressEvent(), @ref keyReleaseEvent() -*/ -class Sdl2Application::KeyEvent: public Sdl2Application::InputEvent { - public: - /** - * @brief Key - * - * @see @ref key() - */ - enum class Key: SDL_Keycode { - Unknown = SDLK_UNKNOWN, /**< Unknown key */ - - /** - * Left Shift - * - * @see @ref InputEvent::Modifier::Shift - */ - LeftShift = SDLK_LSHIFT, - - /** - * Right Shift - * - * @see @ref InputEvent::Modifier::Shift - */ - RightShift = SDLK_RSHIFT, - - /** - * Left Ctrl - * - * @see @ref InputEvent::Modifier::Ctrl - */ - LeftCtrl = SDLK_LCTRL, - - /** - * Right Ctrl - * - * @see @ref InputEvent::Modifier::Ctrl - */ - RightCtrl = SDLK_RCTRL, - - /** - * Left Alt - * - * @see @ref InputEvent::Modifier::Alt - */ - LeftAlt = SDLK_LALT, - - /** - * Right Alt - * - * @see @ref InputEvent::Modifier::Alt - */ - RightAlt = SDLK_RALT, - - /** - * Left Super key (Windows/⌘) - * - * @see @ref InputEvent::Modifier::Super - */ - LeftSuper = SDLK_LGUI, - - /** - * Right Super key (Windows/⌘) - * - * @see @ref InputEvent::Modifier::Super - */ - RightSuper = SDLK_RGUI, - - /** - * AltGr - * - * @see @ref InputEvent::Modifier::AltGr - */ - AltGr = SDLK_MODE, - - Enter = SDLK_RETURN, /**< Enter */ - Esc = SDLK_ESCAPE, /**< Escape */ - - Up = SDLK_UP, /**< Up arrow */ - Down = SDLK_DOWN, /**< Down arrow */ - Left = SDLK_LEFT, /**< Left arrow */ - Right = SDLK_RIGHT, /**< Right arrow */ - Home = SDLK_HOME, /**< Home */ - End = SDLK_END, /**< End */ - PageUp = SDLK_PAGEUP, /**< Page up */ - PageDown = SDLK_PAGEDOWN, /**< Page down */ - Backspace = SDLK_BACKSPACE, /**< Backspace */ - Insert = SDLK_INSERT, /**< Insert */ - Delete = SDLK_DELETE, /**< Delete */ - - F1 = SDLK_F1, /**< F1 */ - F2 = SDLK_F2, /**< F2 */ - F3 = SDLK_F3, /**< F3 */ - F4 = SDLK_F4, /**< F4 */ - F5 = SDLK_F5, /**< F5 */ - F6 = SDLK_F6, /**< F6 */ - F7 = SDLK_F7, /**< F7 */ - F8 = SDLK_F8, /**< F8 */ - F9 = SDLK_F9, /**< F9 */ - F10 = SDLK_F10, /**< F10 */ - F11 = SDLK_F11, /**< F11 */ - F12 = SDLK_F12, /**< F12 */ - - Space = SDLK_SPACE, /**< Space */ - Tab = SDLK_TAB, /**< Tab */ - - /** - * Quote (') - * @m_since{2020,06} - */ - Quote = SDLK_QUOTE, - - Comma = SDLK_COMMA, /**< Comma */ - Period = SDLK_PERIOD, /**< Period */ - Minus = SDLK_MINUS, /**< Minus */ - Plus = SDLK_PLUS, /**< Plus */ - Slash = SDLK_SLASH, /**< Slash */ - Percent = SDLK_PERCENT, /**< Percent */ - Semicolon = SDLK_SEMICOLON, /**< Semicolon (`;`) */ - Equal = SDLK_EQUALS, /**< Equal */ - - /** - * Left bracket (`[`) - * @m_since{2020,06} - */ - LeftBracket = SDLK_LEFTBRACKET, - - /** - * Right bracket (`]`) - * @m_since{2020,06} - */ - RightBracket = SDLK_RIGHTBRACKET, - - /** - * Backslash (`\`) - * @m_since{2020,06} - */ - Backslash = SDLK_BACKSLASH, - - /** - * Backquote (`) - * @m_since{2020,06} - */ - Backquote = SDLK_BACKQUOTE, - - /* no equivalent for GlfwApplication's World1 / World2 */ - - Zero = SDLK_0, /**< Zero */ - One = SDLK_1, /**< One */ - Two = SDLK_2, /**< Two */ - Three = SDLK_3, /**< Three */ - Four = SDLK_4, /**< Four */ - Five = SDLK_5, /**< Five */ - Six = SDLK_6, /**< Six */ - Seven = SDLK_7, /**< Seven */ - Eight = SDLK_8, /**< Eight */ - Nine = SDLK_9, /**< Nine */ - - A = SDLK_a, /**< Letter A */ - B = SDLK_b, /**< Letter B */ - C = SDLK_c, /**< Letter C */ - D = SDLK_d, /**< Letter D */ - E = SDLK_e, /**< Letter E */ - F = SDLK_f, /**< Letter F */ - G = SDLK_g, /**< Letter G */ - H = SDLK_h, /**< Letter H */ - I = SDLK_i, /**< Letter I */ - J = SDLK_j, /**< Letter J */ - K = SDLK_k, /**< Letter K */ - L = SDLK_l, /**< Letter L */ - M = SDLK_m, /**< Letter M */ - N = SDLK_n, /**< Letter N */ - O = SDLK_o, /**< Letter O */ - P = SDLK_p, /**< Letter P */ - Q = SDLK_q, /**< Letter Q */ - R = SDLK_r, /**< Letter R */ - S = SDLK_s, /**< Letter S */ - T = SDLK_t, /**< Letter T */ - U = SDLK_u, /**< Letter U */ - V = SDLK_v, /**< Letter V */ - W = SDLK_w, /**< Letter W */ - X = SDLK_x, /**< Letter X */ - Y = SDLK_y, /**< Letter Y */ - Z = SDLK_z, /**< Letter Z */ - - /** - * Caps lock - * @m_since_latest - */ - CapsLock = SDLK_CAPSLOCK, - - /** - * Scroll lock - * @m_since_latest - */ - ScrollLock = SDLK_SCROLLLOCK, - - /** - * Num lock - * @m_since_latest - */ - NumLock = SDLK_NUMLOCKCLEAR, - - /** - * Print screen - * @m_since_latest - */ - PrintScreen = SDLK_PRINTSCREEN, - - /** - * Pause - * @m_since_latest - */ - Pause = SDLK_PAUSE, - - /** - * Menu - * @m_since_latest - */ - Menu = SDLK_APPLICATION, - - NumZero = SDLK_KP_0, /**< Numpad zero */ - NumOne = SDLK_KP_1, /**< Numpad one */ - NumTwo = SDLK_KP_2, /**< Numpad two */ - NumThree = SDLK_KP_3, /**< Numpad three */ - NumFour = SDLK_KP_4, /**< Numpad four */ - NumFive = SDLK_KP_5, /**< Numpad five */ - NumSix = SDLK_KP_6, /**< Numpad six */ - NumSeven = SDLK_KP_7, /**< Numpad seven */ - NumEight = SDLK_KP_8, /**< Numpad eight */ - NumNine = SDLK_KP_9, /**< Numpad nine */ - NumDecimal = SDLK_KP_DECIMAL, /**< Numpad decimal */ - NumDivide = SDLK_KP_DIVIDE, /**< Numpad divide */ - NumMultiply = SDLK_KP_MULTIPLY, /**< Numpad multiply */ - NumSubtract = SDLK_KP_MINUS, /**< Numpad subtract */ - NumAdd = SDLK_KP_PLUS, /**< Numpad add */ - NumEnter = SDLK_KP_ENTER, /**< Numpad enter */ - NumEqual = SDLK_KP_EQUALS /**< Numpad equal */ - }; - - /** - * @brief Name for given key - * - * Human-readable localized UTF-8 name for given @p key, intended for - * displaying to the user in e.g. key binding configuration. If there - * is no name for given key, empty string is returned. The returned - * view is always @relativeref{Corrade,Containers::StringViewFlag::NullTerminated} - * and is valid at least until the next call to this function, to - * @ref keyName() const or to the underlying @cpp SDL_GetKeyName() @ce - * API. - */ - static Containers::StringView keyName(Key key); - - /** - * @brief Key - * - * @see @ref keyName() - */ - Key key() const { return _key; } - - /** - * @brief Key name - * - * Human-readable localized UTF-8 name for the key returned by - * @ref key(), intended for displaying to the user in e.g. - * key binding configuration. If there is no name for that key, empty - * string is returned. The returned string is always @relativeref{Corrade,Containers::StringViewFlag::NullTerminated} - * and is valid at least until the next call to this function, to - * @ref keyName(Key) or to the underlying @cpp SDL_GetKeyName() @ce - * API. - */ - Containers::StringView keyName() const; - - /** @brief Modifiers */ - Modifiers modifiers() const { return _modifiers; } - - /** - * @brief Whether the key press is repeated - * - * Returns @cpp true @ce if the key press event is repeated, - * @cpp false @ce if not or if this was key release event. - */ - bool isRepeated() const { return _repeated; } - - private: - friend Sdl2Application; - - explicit KeyEvent(const SDL_Event& event, Key key, Modifiers modifiers, bool repeated): InputEvent{event}, _key{key}, _modifiers{modifiers}, _repeated{repeated} {} - - const Key _key; - const Modifiers _modifiers; - const bool _repeated; -}; - -/** -@brief Mouse event - -@see @ref MouseMoveEvent, @ref MouseScrollEvent, @ref mousePressEvent(), - @ref mouseReleaseEvent() -*/ -class Sdl2Application::MouseEvent: public Sdl2Application::InputEvent { - public: - /** - * @brief Mouse button - * - * @see @ref button() - */ - enum class Button: Uint8 { - Left = SDL_BUTTON_LEFT, /**< Left button */ - Middle = SDL_BUTTON_MIDDLE, /**< Middle button */ - Right = SDL_BUTTON_RIGHT, /**< Right button */ - - /** First extra button (e.g. wheel left) */ - X1 = SDL_BUTTON_X1, - - /** Second extra button (e.g. wheel right) */ - X2 = SDL_BUTTON_X2, - }; - - /** @brief Button */ - Button button() const { return _button; } - - /** @brief Position */ - Vector2i position() const { return _position; } - - #ifndef CORRADE_TARGET_EMSCRIPTEN - /** - * @brief Click count - * - * @note Not available in @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". - */ - Int clickCount() const { return _clickCount; } - #endif - - /** - * @brief Modifiers - * - * Lazily populated on first request. - */ - Modifiers modifiers(); - - private: - friend Sdl2Application; - - explicit MouseEvent(const SDL_Event& event, Button button, const Vector2i& position - #ifndef CORRADE_TARGET_EMSCRIPTEN - , Int clickCount - #endif - ): InputEvent{event}, _button{button}, _position{position} - #ifndef CORRADE_TARGET_EMSCRIPTEN - , _clickCount{clickCount} - #endif - {} - - const Button _button; - const Vector2i _position; - #ifndef CORRADE_TARGET_EMSCRIPTEN - const Int _clickCount; - #endif - Containers::Optional _modifiers; -}; - -/** -@brief Mouse move event - -@see @ref MouseEvent, @ref MouseScrollEvent, @ref mouseMoveEvent() -*/ -class Sdl2Application::MouseMoveEvent: public Sdl2Application::InputEvent { - public: - /** - * @brief Mouse button - * - * @see @ref Buttons, @ref buttons() - */ - enum class Button: Uint32 { - Left = SDL_BUTTON_LMASK, /**< Left button */ - Middle = SDL_BUTTON_MMASK, /**< Middle button */ - Right = SDL_BUTTON_RMASK, /**< Right button */ - - /** First extra button (e.g. wheel left) */ - X1 = SDL_BUTTON_X1MASK, - - /** Second extra button (e.g. wheel right) */ - X2 = SDL_BUTTON_X2MASK - }; - - /** - * @brief Set of mouse buttons - * - * @see @ref buttons() - */ - typedef Containers::EnumSet