From 934d9e6bf99421d2164c2213637c793864f8a7be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 22 Aug 2018 13:01:44 +0200 Subject: [PATCH] Platform: resize events in Sdl2App on Emscripten, autodetecting size. I thought this would "just work", BUT NO. The only way is to poll for canvas size once a frame, apparently. --- doc/changelog.dox | 5 ++ src/Magnum/Platform/Sdl2Application.cpp | 85 +++++++++++++++++++++---- src/Magnum/Platform/Sdl2Application.h | 38 ++++++++--- 3 files changed, 107 insertions(+), 21 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 8d795c089..8ff5664d2 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -88,6 +88,8 @@ See also: - Initial HiDPI support for Linux and Emscripten in @ref Platform::Sdl2Application +- Implemented missing resize event support in @ref Platform::Sdl2Application + on @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten" - Implemented @ref Platform::GlfwApplication::MouseMoveEvent::buttons() for feature parity with @ref Platform::Sdl2Application - Added @ref Platform::Sdl2Application::GLConfiguration::setColorBufferSize() "GLConfiguration::setColorBufferSize()", @@ -189,6 +191,9 @@ See also: - @ref Platform::BasicScreen::viewportEvent() "Platform::Screen::viewportEvent()" is no longer pure virtual to be consistent with all `*Application` implementations +- @ref Platform::Sdl2Application now defaults to the actual canvas size + on @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten", instead of hardcoded + @cpp {640, 480} @ce - @ref Platform::Sdl2Application::Configuration::WindowFlags values that make no sense on Emscripten are not available there anymore diff --git a/src/Magnum/Platform/Sdl2Application.cpp b/src/Magnum/Platform/Sdl2Application.cpp index fac826de3..b7d397a7f 100644 --- a/src/Magnum/Platform/Sdl2Application.cpp +++ b/src/Magnum/Platform/Sdl2Application.cpp @@ -303,12 +303,12 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf 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; - /** @todo Remove when Emscripten has proper SDL2 support */ - #ifndef CORRADE_TARGET_EMSCRIPTEN /* Set context version, if user-specified */ if(glConfiguration.version() != GL::Version::None) { Int major, minor; @@ -446,9 +446,42 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf SDL_GL_GetDrawableSize(_window, &drawableSize.x(), &drawableSize.y()); glViewport(0, 0, drawableSize.x(), drawableSize.y()); #endif - #else + /* Emscripten-specific initialization */ - if(!(_glContext = SDL_SetVideoMode(scaledWindowSize.x(), scaledWindowSize.y(), 24, SDL_OPENGL|SDL_HWSURFACE|SDL_DOUBLEBUF))) { + #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 "module" here, make it configurable from outside */ + { + Vector2d canvasSize; + emscripten_get_element_css_size("module", &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(); + } 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(!(_glContext = SDL_SetVideoMode(scaledWindowSize.x(), scaledWindowSize.y(), 24, flags))) { Error() << "Platform::Sdl2Application::tryCreate(): cannot create context:" << SDL_GetError(); return false; } @@ -478,23 +511,23 @@ bool Sdl2Application::tryCreate(const Configuration& configuration, const GLConf #endif Vector2i Sdl2Application::windowSize() const { - #ifndef CORRADE_TARGET_EMSCRIPTEN Vector2i size; + #ifndef CORRADE_TARGET_EMSCRIPTEN SDL_GetWindowSize(_window, &size.x(), &size.y()); - return size; #else - return {_glContext->w, _glContext->h}; + emscripten_get_canvas_element_size(nullptr, &size.x(), &size.y()); #endif + return size; } Vector2i Sdl2Application::framebufferSize() const { - #ifndef CORRADE_TARGET_EMSCRIPTEN Vector2i size; + #ifndef CORRADE_TARGET_EMSCRIPTEN SDL_GL_GetDrawableSize(_window, &size.x(), &size.y()); - return size; #else - return {_glContext->w, _glContext->h}; + emscripten_get_canvas_element_size(nullptr, &size.x(), &size.y()); #endif + return size; } void Sdl2Application::swapBuffers() { @@ -567,16 +600,44 @@ void Sdl2Application::mainLoopIteration() { 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) { + /** @todo don't hardcode "module" here, make it configurable from outside */ + Vector2d canvasSize; + emscripten_get_element_css_size("module", &canvasSize.x(), &canvasSize.y()); + const Vector2i canvasSizei{canvasSize}; + if(canvasSizei != _lastKnownCanvasSize) { + _lastKnownCanvasSize = canvasSizei; + const Vector2i size = _dpiScaling*canvasSizei; + emscripten_set_canvas_element_size(nullptr, size.x(), size.y()); + ViewportEvent e{size, 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) { case SDL_WINDOWEVENT_RESIZED: { + #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_ASSERT_UNREACHABLE(); + #else ViewportEvent e{{event.window.data1, event.window.data2}, framebufferSize(), _dpiScaling}; /** @todo handle also WM_DPICHANGED events when a window is moved between displays with different DPI */ viewportEvent(e); _flags |= Flag::Redraw; + #endif } break; case SDL_WINDOWEVENT_EXPOSED: _flags |= Flag::Redraw; @@ -772,9 +833,7 @@ Sdl2Application::Configuration::Configuration(): #if !defined(CORRADE_TARGET_EMSCRIPTEN) && !defined(CORRADE_TARGET_IOS) _title("Magnum SDL2 Application"), #endif - #ifdef CORRADE_TARGET_EMSCRIPTEN - _size{640, 480}, - #elif !defined(CORRADE_TARGET_IOS) + #if !defined(CORRADE_TARGET_IOS) && !defined(CORRADE_TARGET_EMSCRIPTEN) _size{800, 600}, #else _size{}, /* SDL2 detects someting for us */ diff --git a/src/Magnum/Platform/Sdl2Application.h b/src/Magnum/Platform/Sdl2Application.h index 8d72a6a1b..4c6387d54 100644 --- a/src/Magnum/Platform/Sdl2Application.h +++ b/src/Magnum/Platform/Sdl2Application.h @@ -230,6 +230,10 @@ to simplify porting. @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 @@ -239,6 +243,17 @@ a particular value for details: - @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. + @section Platform-Sdl2Application-dpi DPI awareness On displays that match the platform default DPI (96 or 72), @@ -880,7 +895,8 @@ class Sdl2Application { Exit = 1 << 3 #endif #ifdef CORRADE_TARGET_EMSCRIPTEN - TextInputActive = 1 << 4 + TextInputActive = 1 << 4, + Resizable = 1 << 5 #endif }; @@ -897,6 +913,8 @@ class Sdl2Application { #ifndef CORRADE_TARGET_EMSCRIPTEN SDL_Window* _window; UnsignedInt _minimalLoopPeriod; + #else + Vector2i _lastKnownCanvasSize; #endif #ifdef MAGNUM_TARGET_GL @@ -1159,8 +1177,12 @@ class Sdl2Application::Configuration { enum class WindowFlag: Uint32 { /** * Resizable window. On iOS this allows the application to respond - * to display orientation changes. Implement @ref viewportEvent() - * to react to the resizing events. + * 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, @@ -1365,11 +1387,11 @@ class Sdl2Application::Configuration { * @param dpiScalingPolicy Policy based on which DPI scaling will be set * @return Reference to self (for method chaining) * - * Default is @cpp {800, 600} @ce and @cpp {640, 480} @ce on - * Emscripten with @p dpiScalingPolicy set to - * @ref DpiScalingPolicy::Default. On iOS it defaults to a size that - * matches display resolution. See @ref Platform-Sdl2Application-dpi - * for more information. + * 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) {