Browse Source

Platform: rework DPI awareness and resize event in EmscriptenApp.

By mistake I thought it's the same as in Emscripten's SDL, but there
Emscripten does some emulation to ensure windowSize() ==
framebufferSize().

For the resize event it's possible to hook into the window resize
callback instead of polling for the size every frame, That's much more
efficient.
pull/300/head
Vladimír Vondruš 7 years ago
parent
commit
8254e16c91
  1. 159
      src/Magnum/Platform/EmscriptenApplication.cpp
  2. 198
      src/Magnum/Platform/EmscriptenApplication.h
  3. 7
      src/Magnum/Platform/Sdl2Application.h
  4. 8
      src/Magnum/Platform/Test/EmscriptenApplicationTest.cpp

159
src/Magnum/Platform/EmscriptenApplication.cpp

@ -238,10 +238,13 @@ Vector2 EmscriptenApplication::dpiScaling(const Configuration& configuration) co
return configuration.dpiScaling(); return configuration.dpiScaling();
} }
/* Take device pixel ratio on Emscripten */ /* Unlike Sdl2Application, not taking device pixel ratio into account
const Vector2 dpiScaling{Implementation::emscriptenDpiScaling()}; because here we have window size different from framebuffer size.
Debug{verbose} << "Platform::EmscriptenApplication: physical DPI scaling" << dpiScaling.x(); However, in order to actually calculate the framebuffer size we need to
return dpiScaling; 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) { bool EmscriptenApplication::tryCreate(const Configuration& configuration) {
@ -250,19 +253,14 @@ bool EmscriptenApplication::tryCreate(const Configuration& configuration) {
return tryCreate(configuration, GLConfiguration{}); return tryCreate(configuration, GLConfiguration{});
} }
#endif #endif
if(configuration.windowFlags() & Configuration::WindowFlag::Resizable) {
_flags |= Flag::Resizable;
}
_dpiScaling = dpiScaling(configuration); _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 */ setupCallbacks(!!(configuration.windowFlags() & Configuration::WindowFlag::Resizable));
const Vector2i canvasSizei{windowSize()};
_lastKnownCanvasSize = canvasSizei;
const Vector2i size = _dpiScaling*canvasSizei;
emscripten_set_canvas_element_size("#canvas", size.x(), size.y());
setupCallbacks();
return true; return true;
} }
@ -270,11 +268,6 @@ bool EmscriptenApplication::tryCreate(const Configuration& configuration) {
#ifdef MAGNUM_TARGET_GL #ifdef MAGNUM_TARGET_GL
bool EmscriptenApplication::tryCreate(const Configuration& configuration, const GLConfiguration& glConfiguration) { bool EmscriptenApplication::tryCreate(const Configuration& configuration, const GLConfiguration& glConfiguration) {
CORRADE_ASSERT(_context->version() == GL::Version::None, "Platform::EmscriptenApplication::tryCreate(): window with OpenGL context already created", false); CORRADE_ASSERT(_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 */ /* Create emscripten WebGL context */
EmscriptenWebGLContextAttributes attrs; EmscriptenWebGLContextAttributes attrs;
@ -313,11 +306,34 @@ bool EmscriptenApplication::tryCreate(const Configuration& configuration, const
#error unsupported OpenGL ES version #error unsupported OpenGL ES version
#endif #endif
/* Resize window and match it to the selected format */ std::ostream* verbose = _verboseLog ? Debug::output() : nullptr;
const Vector2i canvasSizei{windowSize()};
_lastKnownCanvasSize = canvasSizei; /* Fetch device pixel ratio. Together with DPI scaling (which is 1.0 by
const Vector2i size = _dpiScaling*canvasSizei; default) this will define framebuffer size. See class docs for why is it
emscripten_set_canvas_element_size("#canvas", size.x(), size.y()); 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 */ /* Create surface and context */
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context = EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context =
@ -334,7 +350,7 @@ bool EmscriptenApplication::tryCreate(const Configuration& configuration, const
CORRADE_INTERNAL_ASSERT_OUTPUT( CORRADE_INTERNAL_ASSERT_OUTPUT(
emscripten_webgl_make_context_current(context) == EMSCRIPTEN_RESULT_SUCCESS); emscripten_webgl_make_context_current(context) == EMSCRIPTEN_RESULT_SUCCESS);
setupCallbacks(); setupCallbacks(!!(configuration.windowFlags() & Configuration::WindowFlag::Resizable));
/* Return true if the initialization succeeds */ /* Return true if the initialization succeeds */
return _context->tryCreate(); return _context->tryCreate();
@ -342,20 +358,71 @@ bool EmscriptenApplication::tryCreate(const Configuration& configuration, const
#endif #endif
Vector2i EmscriptenApplication::windowSize() const { Vector2i EmscriptenApplication::windowSize() const {
double w, h; Vector2d size;
emscripten_get_element_css_size("#canvas", &w, &h); /* Emscripten 1.38.27 changed to generic CSS selectors from element IDs
return {Int(w), Int(h)}; 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() { void EmscriptenApplication::swapBuffers() {
emscripten_webgl_commit_frame(); 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 /* 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 and wrap all lambdas in () to avoid the preprocessor getting upset when
seeing commas */ 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<EmscriptenApplication*>(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, emscripten_set_mousedown_callback("#canvas", this, false,
([](int, const EmscriptenMouseEvent* event, void* userData) -> Int { ([](int, const EmscriptenMouseEvent* event, void* userData) -> Int {
MouseEvent e{event}; MouseEvent e{event};
@ -483,38 +550,10 @@ EmscriptenApplication::GLConfiguration::GLConfiguration():
#endif #endif
void EmscriptenApplication::mainLoopIteration() { void EmscriptenApplication::mainLoopIteration() {
/* The resize event is not fired on window resize, so poll for the canvas if(!(_flags & Flag::Redraw)) return;
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) { _flags &= ~Flag::Redraw;
_flags &= ~Flag::Redraw; drawEvent();
drawEvent();
}
} }
void EmscriptenApplication::exec() { void EmscriptenApplication::exec() {

198
src/Magnum/Platform/EmscriptenApplication.h

@ -135,8 +135,75 @@ MAGNUM_EMSCRIPTENAPPLICATION_MAIN(MyApplication)
@endcode @endcode
If no other application header is included, this class is also aliased to 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 @cpp Platform::Application @ce and the macro is aliased to
to simplify porting. @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} <canvas> @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 { class EmscriptenApplication {
public: public:
@ -319,22 +386,41 @@ class EmscriptenApplication {
/** /**
* @brief Canvas size * @brief Canvas size
* *
* Note that this method is named "windowSize" to be API compatible with * Canvas size to which all input event coordinates can be related.
* Application implementations on other platforms. * On HiDPI displays, canvas size can be different from
* * @ref framebufferSize(). See @ref Platform-Sdl2Application-dpi for
* Window size to which all input event coordinates can be related. * more information. Note that this method is named "window size" to be
* API-compatible with Application implementations on other platforms.
*/ */
Vector2i windowSize() const; 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 * @brief DPI scaling
* *
* How the content should be scaled relative to system defaults for * How the content should be scaled relative to system defaults for
* given @ref windowSize(). If a window is not created yet, returns * given @ref windowSize(). If a window is not created yet, returns
* zero vector, use @ref dpiScaling(const Configuration&) const for * zero vector, use @ref dpiScaling(const Configuration&) const for
* calculating a value independently. See @ref Platform-Sdl2Application-dpi * calculating a value depending on user configuration. By default set
* for more information. * to @cpp 1.0 @ce, see @ref Platform-EmscriptenApplication-dpi for
* @see @ref Sdl2Application::dpiScaling(), @ref framebufferSize() * more information.
* @see @ref framebufferSize(), @ref devicePixelRatio()
*/ */
Vector2 dpiScaling() const { return _dpiScaling; } Vector2 dpiScaling() const { return _dpiScaling; }
@ -343,27 +429,21 @@ class EmscriptenApplication {
* *
* Calculates DPI scaling that would be used when creating a window * Calculates DPI scaling that would be used when creating a window
* with given @p configuration. Takes into account DPI scaling policy * with given @p configuration. Takes into account DPI scaling policy
* and custom scaling specified on the command-line. See * and custom scaling specified via URL GET parameters. See
* @ref Platform-Sdl2Application-dpi for more information. * @ref Platform-EmscriptenApplication-dpi for more information.
* @ref devicePixelRatio()
*/ */
Vector2 dpiScaling(const Configuration& configuration) const; Vector2 dpiScaling(const Configuration& configuration) const;
#if defined(MAGNUM_TARGET_GL) || defined(DOXYGEN_GENERATING_OUTPUT)
/** /**
* @brief Framebuffer size * @brief Device pixel ratio
*
* Always the same as @ref windowSize() on
* @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". See
* @ref Platform-Sdl2Application-dpi for more information.
* *
* @note This function is available only if Magnum is compiled with * Crossplatform code shouldn't need to query this value because the
* @ref MAGNUM_TARGET_GL enabled (done by default). See * pixel ratio is already expressed in the ratio of @ref windowSize()
* @ref building-features for more information. * and @ref framebufferSize() values.
* * @see @ref dpiScaling()
* @see @ref Sdl2Application::framebufferSize()
*/ */
Vector2i framebufferSize() const { return windowSize(); } Vector2 devicePixelRatio() const { return _devicePixelRatio; }
#endif
protected: protected:
/** /**
@ -488,16 +568,16 @@ class EmscriptenApplication {
private: private:
enum class Flag: UnsignedByte { enum class Flag: UnsignedByte {
Redraw = 1 << 0, Redraw = 1 << 0,
Resizable = 1 << 1, TextInputActive = 1 << 1
TextInputActive = 1 << 2,
}; };
typedef Containers::EnumSet<Flag> Flags; typedef Containers::EnumSet<Flag> Flags;
CORRADE_ENUMSET_FRIEND_OPERATORS(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; Vector2i _lastKnownCanvasSize;
Flags _flags; Flags _flags;
@ -783,12 +863,11 @@ class EmscriptenApplication::Configuration {
* @param dpiScaling Custom DPI scaling value * @param dpiScaling Custom DPI scaling value
* *
* Default is a zero vector, meaning a value that matches the display * Default is a zero vector, meaning a value that matches the display
* or canvas size is autodetected. See @ref Platform-Sdl2Application-dpi * or canvas size is autodetected. See
* for more information. * @ref Platform-EmscriptenApplication-dpi for more information. When
* When @p dpiScaling is not a zero vector, this function sets the DPI * @p dpiScaling is not a zero vector, this function sets the DPI
* scaling directly. The resulting @ref EmscriptenApplication::windowSize() * scaling directly. The resulting @ref windowSize() is
* is @cpp size*dpiScaling @ce and @ref EmscriptenApplication::dpiScaling() * @cpp size*dpiScaling @ce and @ref dpiScaling() is @p dpiScaling.
* is @p dpiScaling.
*/ */
Configuration& setSize(const Vector2i& size, const Vector2& dpiScaling = {}) { Configuration& setSize(const Vector2i& size, const Vector2& dpiScaling = {}) {
_size = size; _size = size;
@ -800,8 +879,8 @@ class EmscriptenApplication::Configuration {
* @brief Custom DPI scaling * @brief Custom DPI scaling
* *
* If zero, the devices pixel ratio has a priority over this value. * If zero, the devices pixel ratio has a priority over this value.
* The `--magnum-dpi-scaling` command-line option has a priority * The `--magnum-dpi-scaling` option (specified via URL GET parameters)
* over any application-set value. * has a priority over any application-set value.
* @see @ref setSize(const Vector2i&, const Vector2&) * @see @ref setSize(const Vector2i&, const Vector2&)
*/ */
Vector2 dpiScaling() const { return _dpiScaling; } Vector2 dpiScaling() const { return _dpiScaling; }
@ -850,11 +929,11 @@ class EmscriptenApplication::ViewportEvent {
/** /**
* @brief Canvas size * @brief Canvas size
* *
* Note that this method is named "windowSize" to be API compatible with * On HiDPI displays, window size can be different from
* Application implementations on other platforms. * @ref framebufferSize(). See @ref Platform-EmscriptenApplication-dpi
* * for more information. Note that this method is named "window size"
* Equivalent to @ref framebufferSize(). See @ref Platform-Sdl2Application-dpi * to be API-compatible with Application implementations on other
* for more information. * platforms.
* @see @ref EmscriptenApplication::windowSize() * @see @ref EmscriptenApplication::windowSize()
*/ */
Vector2i windowSize() const { return _windowSize; } Vector2i windowSize() const { return _windowSize; }
@ -863,8 +942,9 @@ class EmscriptenApplication::ViewportEvent {
/** /**
* @brief Framebuffer size * @brief Framebuffer size
* *
* Equivalent to @ref windowSize(). See * On HiDPI displays, framebuffer size can be different from
* @ref Platform-Sdl2Application-dpi for more information. * @ref windowSize(). See @ref Platform-EmscriptenApplication-dpi for
* more information.
* *
* @note This function is available only if Magnum is compiled with * @note This function is available only if Magnum is compiled with
* @ref MAGNUM_TARGET_GL enabled (done by default). See * @ref MAGNUM_TARGET_GL enabled (done by default). See
@ -878,35 +958,47 @@ class EmscriptenApplication::ViewportEvent {
/** /**
* @brief DPI scaling * @brief DPI scaling
* *
* On some platforms moving an app between displays can result in DPI * On some platforms moving a browser window between displays can
* scaling value being changed in tandem with a canvas/framebuffer * result in DPI scaling value being changed in tandem with a
* size. Simply resizing a canvas doesn't change the DPI scaling value. * canvas/framebuffer size. Simply resizing the canvas doesn't change
* See @ref Platform-Sdl2Application-dpi for more information. * the DPI scaling value. See @ref Platform-EmscriptenApplication-dpi
* for more information.
* @see @ref EmscriptenApplication::dpiScaling() * @see @ref EmscriptenApplication::dpiScaling()
*/ */
Vector2 dpiScaling() const { return _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: private:
friend EmscriptenApplication; friend EmscriptenApplication;
explicit ViewportEvent( explicit ViewportEvent(const Vector2i& windowSize,
const Vector2i& windowSize,
#ifdef MAGNUM_TARGET_GL #ifdef MAGNUM_TARGET_GL
const Vector2i& framebufferSize, const Vector2i& framebufferSize,
#endif #endif
const Vector2& dpiScaling): const Vector2& dpiScaling, const Vector2& devicePixelRatio):
_windowSize{windowSize}, _windowSize{windowSize},
#ifdef MAGNUM_TARGET_GL #ifdef MAGNUM_TARGET_GL
_framebufferSize{framebufferSize}, _framebufferSize{framebufferSize},
#endif #endif
_dpiScaling{dpiScaling} {} _dpiScaling{dpiScaling}, _devicePixelRatio{devicePixelRatio} {}
const Vector2i _windowSize; const Vector2i _windowSize;
#ifdef MAGNUM_TARGET_GL #ifdef MAGNUM_TARGET_GL
const Vector2i _framebufferSize; const Vector2i _framebufferSize;
#endif #endif
const Vector2 _dpiScaling, _devicePixelRatio;
const Vector2 _dpiScaling;
}; };
/** /**

7
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 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 windowSize() and @ref framebufferSize() is always the same,
@ref dpiScaling() contains the queried DPI scaling value. The value can be @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 If your application is saving and restoring window size, it's advisable to take
@ref dpiScaling() into account: @ref dpiScaling() into account:

8
src/Magnum/Platform/Test/EmscriptenApplicationTest.cpp

@ -39,6 +39,12 @@ struct EmscriptenApplicationTest: Platform::Application {
//, GLConfiguration{}.setFlags({}) //, 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 /* This uses a VAO on WebGL 1, so it will crash in case GL flags are
missing EnableExtensionsByDefault (uncomment above) */ missing EnableExtensionsByDefault (uncomment above) */
GL::Mesh mesh; GL::Mesh mesh;
@ -54,7 +60,7 @@ struct EmscriptenApplicationTest: Platform::Application {
#ifdef MAGNUM_TARGET_GL #ifdef MAGNUM_TARGET_GL
/* For testing HiDPI resize events */ /* For testing HiDPI resize events */
void viewportEvent(ViewportEvent& event) override { 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 #endif

Loading…
Cancel
Save