diff --git a/src/Magnum/Platform/EmscriptenApplication.cpp b/src/Magnum/Platform/EmscriptenApplication.cpp index 628e68d5a..84d463675 100644 --- a/src/Magnum/Platform/EmscriptenApplication.cpp +++ b/src/Magnum/Platform/EmscriptenApplication.cpp @@ -238,10 +238,13 @@ Vector2 EmscriptenApplication::dpiScaling(const Configuration& configuration) co return configuration.dpiScaling(); } - /* Take device pixel ratio on Emscripten */ - const Vector2 dpiScaling{Implementation::emscriptenDpiScaling()}; - Debug{verbose} << "Platform::EmscriptenApplication: physical DPI scaling" << dpiScaling.x(); - return dpiScaling; + /* Unlike Sdl2Application, not taking device pixel ratio into account + because here we have window size different from framebuffer size. + However, in order to actually calculate the framebuffer size we need to + query the device pixel ratio. That's done in tryCreate() below, here it + is returning 1.0 to be consistent with behavior on other platforms where + it's either windowSize == 1 */ + return Vector2{1.0f}; } bool EmscriptenApplication::tryCreate(const Configuration& configuration) { @@ -250,19 +253,14 @@ bool EmscriptenApplication::tryCreate(const Configuration& configuration) { return tryCreate(configuration, GLConfiguration{}); } #endif - if(configuration.windowFlags() & Configuration::WindowFlag::Resizable) { - _flags |= Flag::Resizable; - } _dpiScaling = dpiScaling(configuration); + if(!configuration.size().isZero()) { + const Vector2i scaledCanvasSize = configuration.size()*_dpiScaling; + emscripten_set_canvas_element_size("#canvas", scaledCanvasSize.x(), scaledCanvasSize.y()); + } - /* Resize window and match it to the selected format */ - const Vector2i canvasSizei{windowSize()}; - _lastKnownCanvasSize = canvasSizei; - const Vector2i size = _dpiScaling*canvasSizei; - emscripten_set_canvas_element_size("#canvas", size.x(), size.y()); - - setupCallbacks(); + setupCallbacks(!!(configuration.windowFlags() & Configuration::WindowFlag::Resizable)); return true; } @@ -270,11 +268,6 @@ bool EmscriptenApplication::tryCreate(const Configuration& configuration) { #ifdef MAGNUM_TARGET_GL bool EmscriptenApplication::tryCreate(const Configuration& configuration, const GLConfiguration& glConfiguration) { CORRADE_ASSERT(_context->version() == GL::Version::None, "Platform::EmscriptenApplication::tryCreate(): window with OpenGL context already created", false); - if(configuration.windowFlags() & Configuration::WindowFlag::Resizable) { - _flags |= Flag::Resizable; - } - - _dpiScaling = dpiScaling(configuration); /* Create emscripten WebGL context */ EmscriptenWebGLContextAttributes attrs; @@ -313,11 +306,34 @@ bool EmscriptenApplication::tryCreate(const Configuration& configuration, const #error unsupported OpenGL ES version #endif - /* Resize window and match it to the selected format */ - const Vector2i canvasSizei{windowSize()}; - _lastKnownCanvasSize = canvasSizei; - const Vector2i size = _dpiScaling*canvasSizei; - emscripten_set_canvas_element_size("#canvas", size.x(), size.y()); + std::ostream* verbose = _verboseLog ? Debug::output() : nullptr; + + /* Fetch device pixel ratio. Together with DPI scaling (which is 1.0 by + default) this will define framebuffer size. See class docs for why is it + done like that. */ + _devicePixelRatio = Vector2{Float(emscripten_get_device_pixel_ratio())}; + Debug{verbose} << "Platform::EmscriptenApplication: device pixel ratio" << _devicePixelRatio.x(); + + /* Get CSS canvas size and cache it. This is used later to detect canvas + resizes in emscripten_set_resize_callback() and fire viewport events, + because browsers are only required to fire resize events on the window + and not on particular DOM elements. */ + _lastKnownCanvasSize = windowSize(); + + /* 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 canvasSize; + if(!configuration.size().isZero()) { + canvasSize = configuration.size(); + } else { + canvasSize = _lastKnownCanvasSize; + Debug{verbose} << "Platform::EmscriptenApplication::tryCreate(): autodetected canvas size" << canvasSize; + } + _dpiScaling = dpiScaling(configuration); + const Vector2i scaledCanvasSize = canvasSize*_dpiScaling*_devicePixelRatio; + emscripten_set_canvas_element_size("#canvas", scaledCanvasSize.x(), scaledCanvasSize.y()); /* Create surface and context */ EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context = @@ -334,7 +350,7 @@ bool EmscriptenApplication::tryCreate(const Configuration& configuration, const CORRADE_INTERNAL_ASSERT_OUTPUT( emscripten_webgl_make_context_current(context) == EMSCRIPTEN_RESULT_SUCCESS); - setupCallbacks(); + setupCallbacks(!!(configuration.windowFlags() & Configuration::WindowFlag::Resizable)); /* Return true if the initialization succeeds */ return _context->tryCreate(); @@ -342,20 +358,71 @@ bool EmscriptenApplication::tryCreate(const Configuration& configuration, const #endif Vector2i EmscriptenApplication::windowSize() const { - double w, h; - emscripten_get_element_css_size("#canvas", &w, &h); - return {Int(w), Int(h)}; + Vector2d size; + /* 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 */ + /** @todo don't hardcode "#canvas" everywhere, make it configurable from outside */ + emscripten_get_element_css_size("#canvas", &size.x(), &size.y()); + return Vector2i{Math::round(size)}; +} + +#ifdef MAGNUM_TARGET_GL +Vector2i EmscriptenApplication::framebufferSize() const { + Vector2i size; + /* See above why hardcoded */ + emscripten_get_canvas_element_size("#canvas", &size.x(), &size.y()); + return size; } +#endif void EmscriptenApplication::swapBuffers() { emscripten_webgl_commit_frame(); } -void EmscriptenApplication::setupCallbacks() { +void EmscriptenApplication::setupCallbacks(bool resizable) { /* Since 1.38.17 all emscripten_set_*_callback() are macros. Play it safe and wrap all lambdas in () to avoid the preprocessor getting upset when seeing commas */ + /* Set up the resize callback. Because browsers are only required to fire + resize events on the window and not on particular DOM elements, we need + to cache the last known canvas size and fire the event only if that + changes. Better than polling for this change in every frame like + Sdl2Application does, but still not ideal. */ + if(resizable) { + #ifdef EMSCRIPTEN_EVENT_TARGET_WINDOW + const char* target = EMSCRIPTEN_EVENT_TARGET_WINDOW; + #else + const char* target = "#window"; + #endif + auto cb = [](int, const EmscriptenUiEvent*, void* userData) -> Int { + EmscriptenApplication& app = *static_cast(userData); + /* See windowSize() for why we hardcode "#canvas" here */ + const Vector2i canvasSize{app.windowSize()}; + if(canvasSize != app._lastKnownCanvasSize) { + app._lastKnownCanvasSize = canvasSize; + const Vector2i size = canvasSize*app._dpiScaling*app._devicePixelRatio; + emscripten_set_canvas_element_size("#canvas", size.x(), size.y()); + ViewportEvent e{canvasSize, + #ifdef MAGNUM_TARGET_GL + app.framebufferSize(), + #endif + app._dpiScaling, app._devicePixelRatio}; + app.viewportEvent(e); + app._flags |= Flag::Redraw; + } + return false; /** @todo what does ignoring a resize event mean? */ + }; + emscripten_set_resize_callback(target, this, false, cb); + } + + /* See windowSize() for why we hardcode "#canvas" here */ + emscripten_set_mousedown_callback("#canvas", this, false, ([](int, const EmscriptenMouseEvent* event, void* userData) -> Int { MouseEvent e{event}; @@ -483,38 +550,10 @@ EmscriptenApplication::GLConfiguration::GLConfiguration(): #endif void EmscriptenApplication::mainLoopIteration() { - /* 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 - - As this is caused by the DOM3 events spec only requiring browsers to - fire the resize event for `window` not generally for all DOM elemenets, - it also applies to `emscripten_set_resize_callback`. */ - if(_flags & Flag::Resizable) { - /* 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. */ - const Vector2i canvasSizei{windowSize()}; - if(canvasSizei != _lastKnownCanvasSize) { - _lastKnownCanvasSize = canvasSizei; - const Vector2i size = _dpiScaling*canvasSizei; - emscripten_set_canvas_element_size("#canvas", size.x(), size.y()); - #ifdef MAGNUM_TARGET_GL - ViewportEvent e{size, size, _dpiScaling}; - #else - ViewportEvent e{size, _dpiScaling}; - #endif - viewportEvent(e); - _flags |= Flag::Redraw; - } - } + if(!(_flags & Flag::Redraw)) return; - if(_flags & Flag::Redraw) { - _flags &= ~Flag::Redraw; - drawEvent(); - } + _flags &= ~Flag::Redraw; + drawEvent(); } void EmscriptenApplication::exec() { diff --git a/src/Magnum/Platform/EmscriptenApplication.h b/src/Magnum/Platform/EmscriptenApplication.h index bd4f1e718..a8a12eef4 100644 --- a/src/Magnum/Platform/EmscriptenApplication.h +++ b/src/Magnum/Platform/EmscriptenApplication.h @@ -135,8 +135,75 @@ MAGNUM_EMSCRIPTENAPPLICATION_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. +@cpp Platform::Application @ce and the macro is aliased to +@cpp MAGNUM_APPLICATION_MAIN() @ce to simplify porting. + +@section Platform-EmscriptenApplication-browser Browser-specific behavior + +Leaving a default (zero) size in @ref Configuration will cause the app to use a +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-EmscriptenApplication-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. + +Unlike desktop platforms, the browser has no concept of application exit code, +so the return value of @ref exec() is always @cpp 0 @ce and whatever is passed +to @ref exit(int) is ignored. + +@section Platform-EmscriptenApplication-webgl WebGL-specific behavior + +While WebGL itself requires all extensions to be +[enabled explicitly](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Using_Extensions), +by default Emscripten enables all supported extensions that don't have a +negative effect on performance to simplify porting. This is controlled by +@ref GLConfiguration::Flag::EnableExtensionsByDefault and the flag is enabled +by default. When disabled, you are expected to enable desired extensions +manually using @m_class{m-doc-external} [emscripten_webgl_enable_extension()](https://emscripten.org/docs/api_reference/html5.h.html#c.emscripten_webgl_enable_extension). + +@attention Because @ref GLConfiguration::Flag::EnableExtensionsByDefault is + among default flags, calling @ref GLConfiguration::setFlags() will reset + this default, causing crashes at runtime when extension functionality is + used. To be safe, you might want to use @ref GLConfiguration::addFlags() + and @ref GLConfiguration::clearFlags() instead. + +@section Platform-EmscriptenApplication-dpi DPI awareness + +Since this application targets only web browsers, DPI handling isn't as general +as in case of @ref Sdl2Application or @ref GlfwApplication. See +@ref Platform-Sdl2Application-dpi "Sdl2Application DPI awareness" documentation +for a guide covering all platform differences. + +For this application in particular, @ref windowSize() can be different than +@ref framebufferSize() on HiDPI displays --- which is different from +@ref Sdl2Application behavior on Emscripten. By default, @ref dpiScaling() is +@cpp 1.0f @ce in both dimensions but it can be overriden using custom DPI +scaling --- the `--magnum-dpi-scaling` command-line options are supported the +same way as in @ref Sdl2Application, only in the form of URL GET parameters, +similarly to all other @ref platforms-html5-environment "command-line options". + +Having @ref dpiScaling() set to @cpp 1.0f @ce is done in order to have +consistent behavior with other platforms --- platforms have either +@ref windowSize() equivalent to @ref framebufferSize() and then +@ref dpiScaling() specifies the UI scale (Windows/Linux/Android-like) or +@ref windowSize() different from @ref framebufferSize() (which defines the UI +scale) and then @ref dpiScaling() is @cpp 1.0f @ce (macOS/iOS-like), so this +is the second case. The actual device pixel ratio is expressed in the ratio of +@ref windowSize() and @ref framebufferSize() so crossplatform code shouldn't +have a need to query it, however for completeness it's exposed in +@ref devicePixelRatio() and @ref ViewportEvent::devicePixelRatio(). + +Setting custom DPI scaling will affect @ref framebufferSize() +(larger values making the canvas backing framebuffer larger and vice versa), +@ref windowSize() will stay unaffected as it's controlled by the CSS, and +@ref devicePixelRatio() will stay the same as well as it's defined by the +browser. + +To avoid confusion, documentation of all @ref EmscriptenApplication APIs always +mentions only the web case, consult equivalent APIs in @ref Sdl2Application or +@ref GlfwApplication for behavior in those implementations. */ class EmscriptenApplication { public: @@ -319,22 +386,41 @@ class EmscriptenApplication { /** * @brief Canvas size * - * Note that this method is named "windowSize" to be API compatible with - * Application implementations on other platforms. - * - * Window size to which all input event coordinates can be related. + * Canvas size to which all input event coordinates can be related. + * On HiDPI displays, canvas size can be different from + * @ref framebufferSize(). See @ref Platform-Sdl2Application-dpi for + * more information. Note that this method is named "window size" to be + * API-compatible with Application implementations on other platforms. */ Vector2i windowSize() const; + #if defined(MAGNUM_TARGET_GL) || defined(DOXYGEN_GENERATING_OUTPUT) + /** + * @brief Framebuffer size + * + * On HiDPI displays, framebuffer size can be different from + * @ref windowSize(). See @ref Platform-EmscriptenApplication-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; + #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&) const for - * calculating a value independently. See @ref Platform-Sdl2Application-dpi - * for more information. - * @see @ref Sdl2Application::dpiScaling(), @ref framebufferSize() + * calculating a value depending on user configuration. By default set + * to @cpp 1.0 @ce, see @ref Platform-EmscriptenApplication-dpi for + * more information. + * @see @ref framebufferSize(), @ref devicePixelRatio() */ Vector2 dpiScaling() const { return _dpiScaling; } @@ -343,27 +429,21 @@ class EmscriptenApplication { * * 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. + * and custom scaling specified via URL GET parameters. See + * @ref Platform-EmscriptenApplication-dpi for more information. + * @ref devicePixelRatio() */ Vector2 dpiScaling(const Configuration& configuration) const; - #if defined(MAGNUM_TARGET_GL) || defined(DOXYGEN_GENERATING_OUTPUT) /** - * @brief Framebuffer size - * - * Always the same as @ref windowSize() on - * @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". See - * @ref Platform-Sdl2Application-dpi for more information. + * @brief Device pixel ratio * - * @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() + * Crossplatform code shouldn't need to query this value because the + * pixel ratio is already expressed in the ratio of @ref windowSize() + * and @ref framebufferSize() values. + * @see @ref dpiScaling() */ - Vector2i framebufferSize() const { return windowSize(); } - #endif + Vector2 devicePixelRatio() const { return _devicePixelRatio; } protected: /** @@ -488,16 +568,16 @@ class EmscriptenApplication { private: enum class Flag: UnsignedByte { Redraw = 1 << 0, - Resizable = 1 << 1, - TextInputActive = 1 << 2, + TextInputActive = 1 << 1 }; typedef Containers::EnumSet Flags; CORRADE_ENUMSET_FRIEND_OPERATORS(Flags) - void setupCallbacks(); + /* Sorry, but can't use Configuration::WindowFlags here :( */ + void setupCallbacks(bool resizable); - Vector2 _dpiScaling; + Vector2 _devicePixelRatio, _dpiScaling; Vector2i _lastKnownCanvasSize; Flags _flags; @@ -783,12 +863,11 @@ class EmscriptenApplication::Configuration { * @param dpiScaling Custom DPI scaling value * * 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. - * When @p dpiScaling is not a zero vector, this function sets the DPI - * scaling directly. The resulting @ref EmscriptenApplication::windowSize() - * is @cpp size*dpiScaling @ce and @ref EmscriptenApplication::dpiScaling() - * is @p dpiScaling. + * or canvas size is autodetected. See + * @ref Platform-EmscriptenApplication-dpi for more information. When + * @p dpiScaling is not a zero vector, this function sets the DPI + * scaling directly. The resulting @ref windowSize() is + * @cpp size*dpiScaling @ce and @ref dpiScaling() is @p dpiScaling. */ Configuration& setSize(const Vector2i& size, const Vector2& dpiScaling = {}) { _size = size; @@ -800,8 +879,8 @@ class EmscriptenApplication::Configuration { * @brief Custom DPI scaling * * If zero, the devices pixel ratio has a priority over this value. - * The `--magnum-dpi-scaling` command-line option has a priority - * over any application-set value. + * The `--magnum-dpi-scaling` option (specified via URL GET parameters) + * has a priority over any application-set value. * @see @ref setSize(const Vector2i&, const Vector2&) */ Vector2 dpiScaling() const { return _dpiScaling; } @@ -850,11 +929,11 @@ class EmscriptenApplication::ViewportEvent { /** * @brief Canvas size * - * Note that this method is named "windowSize" to be API compatible with - * Application implementations on other platforms. - * - * Equivalent to @ref framebufferSize(). See @ref Platform-Sdl2Application-dpi - * for more information. + * On HiDPI displays, window size can be different from + * @ref framebufferSize(). See @ref Platform-EmscriptenApplication-dpi + * for more information. Note that this method is named "window size" + * to be API-compatible with Application implementations on other + * platforms. * @see @ref EmscriptenApplication::windowSize() */ Vector2i windowSize() const { return _windowSize; } @@ -863,8 +942,9 @@ class EmscriptenApplication::ViewportEvent { /** * @brief Framebuffer size * - * Equivalent to @ref windowSize(). See - * @ref Platform-Sdl2Application-dpi for more information. + * On HiDPI displays, framebuffer size can be different from + * @ref windowSize(). See @ref Platform-EmscriptenApplication-dpi for + * more information. * * @note This function is available only if Magnum is compiled with * @ref MAGNUM_TARGET_GL enabled (done by default). See @@ -878,35 +958,47 @@ class EmscriptenApplication::ViewportEvent { /** * @brief DPI scaling * - * On some platforms moving an app between displays can result in DPI - * scaling value being changed in tandem with a canvas/framebuffer - * size. Simply resizing a canvas doesn't change the DPI scaling value. - * See @ref Platform-Sdl2Application-dpi for more information. + * On some platforms moving a browser window between displays can + * result in DPI scaling value being changed in tandem with a + * canvas/framebuffer size. Simply resizing the canvas doesn't change + * the DPI scaling value. See @ref Platform-EmscriptenApplication-dpi + * for more information. * @see @ref EmscriptenApplication::dpiScaling() */ Vector2 dpiScaling() const { return _dpiScaling; } + /** + * @brief Device pixel ratio + * + * On some platforms moving a browser window between displays can + * result in device pixel ratio value being changed. Crossplatform code + * shouldn't need to query this value because the ratio is already + * expressed in the ratio of @ref windowSize() and @ref framebufferSize() + * values. See @ref Platform-EmscriptenApplication-dpi for more + * information. + * @see @ref EmscriptenApplication::devicePixelRatio() + */ + Vector2 devicePixelRatio() const { return _devicePixelRatio; } + private: friend EmscriptenApplication; - explicit ViewportEvent( - const Vector2i& windowSize, + explicit ViewportEvent(const Vector2i& windowSize, #ifdef MAGNUM_TARGET_GL const Vector2i& framebufferSize, #endif - const Vector2& dpiScaling): + const Vector2& dpiScaling, const Vector2& devicePixelRatio): _windowSize{windowSize}, #ifdef MAGNUM_TARGET_GL _framebufferSize{framebufferSize}, #endif - _dpiScaling{dpiScaling} {} + _dpiScaling{dpiScaling}, _devicePixelRatio{devicePixelRatio} {} const Vector2i _windowSize; #ifdef MAGNUM_TARGET_GL const Vector2i _framebufferSize; #endif - - const Vector2 _dpiScaling; + const Vector2 _dpiScaling, _devicePixelRatio; }; /** diff --git a/src/Magnum/Platform/Sdl2Application.h b/src/Magnum/Platform/Sdl2Application.h index 6fbd84619..c31aad7d4 100644 --- a/src/Magnum/Platform/Sdl2Application.h +++ b/src/Magnum/Platform/Sdl2Application.h @@ -365,7 +365,12 @@ The default is depending on the platform: 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 - overriden using custom DPI scaling. + overriden 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. If your application is saving and restoring window size, it's advisable to take @ref dpiScaling() into account: diff --git a/src/Magnum/Platform/Test/EmscriptenApplicationTest.cpp b/src/Magnum/Platform/Test/EmscriptenApplicationTest.cpp index 9cc164460..f305f873b 100644 --- a/src/Magnum/Platform/Test/EmscriptenApplicationTest.cpp +++ b/src/Magnum/Platform/Test/EmscriptenApplicationTest.cpp @@ -39,6 +39,12 @@ struct EmscriptenApplicationTest: Platform::Application { //, GLConfiguration{}.setFlags({}) } { + Debug{} << "window size" << windowSize() + #ifdef MAGNUM_TARGET_GL + << framebufferSize() + #endif + << dpiScaling() << devicePixelRatio(); + /* This uses a VAO on WebGL 1, so it will crash in case GL flags are missing EnableExtensionsByDefault (uncomment above) */ GL::Mesh mesh; @@ -54,7 +60,7 @@ struct EmscriptenApplicationTest: Platform::Application { #ifdef MAGNUM_TARGET_GL /* For testing HiDPI resize events */ void viewportEvent(ViewportEvent& event) override { - Debug{} << "viewport event" << event.windowSize() << event.framebufferSize() << event.dpiScaling(); + Debug{} << "viewport event" << event.windowSize() << event.framebufferSize() << event.dpiScaling() << event.devicePixelRatio(); } #endif