Browse Source

Platform: get EmscriptenApplication canvas target from Module['canvas'].

And properly account for -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR.
Module['canvas'] can be read even from code compiled with -s MODULARIZE
so it's a preferrable option to hardcoding it in Configuration. The
target strings in Emscripten depend on whether we're compiled with
DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR (see
https://github.com/emscripten-core/emscripten/pull/7977). This is now
detected and handled at runtime to prepend element IDs with and use
the correct window and document magic targets.
pull/482/head
Pablo Escobar 6 years ago committed by Vladimír Vondruš
parent
commit
01b4d8d171
  1. 135
      src/Magnum/Platform/EmscriptenApplication.cpp
  2. 3
      src/Magnum/Platform/EmscriptenApplication.h

135
src/Magnum/Platform/EmscriptenApplication.cpp

@ -43,6 +43,13 @@
#include "Magnum/Platform/GLContext.h"
#endif
/** @todo drop once we don't support < 1.38.27 anymore */
#ifndef EMSCRIPTEN_EVENT_TARGET_DOCUMENT
#define EMSCRIPTEN_EVENT_TARGET_DOCUMENT reinterpret_cast<const char*>(1)
#define EMSCRIPTEN_EVENT_TARGET_WINDOW reinterpret_cast<const char*>(2)
#define EMSCRIPTEN_EVENT_TARGET_SCREEN reinterpret_cast<const char*>(3)
#endif
namespace Magnum { namespace Platform {
namespace {
@ -174,6 +181,36 @@ namespace {
return Key::Unknown;
}
std::string canvasId() {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
char* id = reinterpret_cast<char*>(EM_ASM_INT({
return allocate(intArrayFromString(Module['canvas'].id), 'i8', ALLOC_NORMAL);
}));
#pragma GCC diagnostic pop
std::string str = id;
std::free(id);
return str;
}
bool checkForDeprecatedEmscriptenTargetBehavior() {
/* Emscripten 1.38.27 changed to generic CSS selectors from element IDs
depending on -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1 being
set.
https://github.com/emscripten-core/emscripten/pull/7977
There is no simple way to check for compiler options so check
whether the new CSS selectors are being used. If so, it should find
canvas#[id] which is any canvas with the ID of Module.canvas.
The old target behavior will look for an element with id="canvas#[id]"
which could theoretically exist but that's highly unlikely. */
bool deprecated = true;
Vector2d tempSize;
if(emscripten_get_element_css_size(("canvas#" + canvasId()).data(), &tempSize.x(), &tempSize.y()) >= 0) {
deprecated = false;
}
return deprecated;
}
}
EmscriptenApplication::EmscriptenApplication(const Arguments& arguments): EmscriptenApplication{arguments, Configuration{}} {}
@ -266,10 +303,19 @@ bool EmscriptenApplication::tryCreate(const Configuration& configuration) {
}
#endif
std::ostream* verbose = _verboseLog ? Debug::output() : nullptr;
_deprecatedTargetBehavior = checkForDeprecatedEmscriptenTargetBehavior();
if(_deprecatedTargetBehavior) {
Debug{verbose} << "Platform::EmscriptenApplication::tryCreate(): using old Emscripten target behavior";
}
_canvasTarget = (_deprecatedTargetBehavior ? "" : "#") + canvasId();
_dpiScaling = dpiScaling(configuration);
if(!configuration.size().isZero()) {
const Vector2i scaledCanvasSize = configuration.size()*_dpiScaling;
emscripten_set_canvas_element_size("#canvas", scaledCanvasSize.x(), scaledCanvasSize.y());
emscripten_set_canvas_element_size(_canvasTarget.data(), scaledCanvasSize.x(), scaledCanvasSize.y());
}
setupCallbacks(!!(configuration.windowFlags() & Configuration::WindowFlag::Resizable));
@ -327,6 +373,17 @@ bool EmscriptenApplication::tryCreate(const Configuration& configuration, const
_devicePixelRatio = Vector2{Float(emscripten_get_device_pixel_ratio())};
Debug{verbose} << "Platform::EmscriptenApplication: device pixel ratio" << _devicePixelRatio.x();
/* Find out which element target strings Emscripten expects. This depends on
the DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR compiler option. */
_deprecatedTargetBehavior = checkForDeprecatedEmscriptenTargetBehavior();
if(_deprecatedTargetBehavior) {
Debug{verbose} << "Platform::EmscriptenApplication::tryCreate(): using old Emscripten target behavior";
}
/* Get the canvas ID from Module.canvas, either set by EmscriptenApplication.js
or overridden/manually set by the user. */
_canvasTarget = (_deprecatedTargetBehavior ? "" : "#") + canvasId();
/* 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
@ -346,10 +403,10 @@ bool EmscriptenApplication::tryCreate(const Configuration& configuration, const
}
_dpiScaling = dpiScaling(configuration);
const Vector2i scaledCanvasSize = canvasSize*_dpiScaling*_devicePixelRatio;
emscripten_set_canvas_element_size("#canvas", scaledCanvasSize.x(), scaledCanvasSize.y());
emscripten_set_canvas_element_size(_canvasTarget.data(), scaledCanvasSize.x(), scaledCanvasSize.y());
/* Create WebGL context */
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context = emscripten_webgl_create_context("#canvas", &attrs);
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context = emscripten_webgl_create_context(_canvasTarget.data(), &attrs);
if(!context) {
/* When context creation fails, `context` is a negative integer
matching EMSCRIPTEN_RESULT_* defines */
@ -371,23 +428,14 @@ bool EmscriptenApplication::tryCreate(const Configuration& configuration, const
Vector2i EmscriptenApplication::windowSize() const {
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());
emscripten_get_element_css_size(_canvasTarget.data(), &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());
emscripten_get_canvas_element_size(_canvasTarget.data(), &size.x(), &size.y());
return size;
}
#endif
@ -423,12 +471,11 @@ void EmscriptenApplication::swapBuffers() {
/* Called from window resize event but also explicitly from
setContainerCssClass() */
void EmscriptenApplication::handleCanvasResize(const EmscriptenUiEvent* event) {
/* See windowSize() for why we hardcode "#canvas" here */
const Vector2i canvasSize{windowSize()};
if(canvasSize != _lastKnownCanvasSize) {
_lastKnownCanvasSize = canvasSize;
const Vector2i size = canvasSize*_dpiScaling*_devicePixelRatio;
emscripten_set_canvas_element_size("#canvas", size.x(), size.y());
emscripten_set_canvas_element_size(_canvasTarget.data(), size.x(), size.y());
ViewportEvent e{event, canvasSize,
#ifdef MAGNUM_TARGET_GL
framebufferSize(),
@ -454,11 +501,7 @@ void EmscriptenApplication::setupCallbacks(bool resizable) {
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
const char* target = _deprecatedTargetBehavior ? "#window" : EMSCRIPTEN_EVENT_TARGET_WINDOW;
auto cb = [](int, const EmscriptenUiEvent* event, void* userData) -> Int {
static_cast<EmscriptenApplication*>(userData)->handleCanvasResize(event);
return false; /** @todo what does ignoring a resize event mean? */
@ -466,23 +509,21 @@ void EmscriptenApplication::setupCallbacks(bool resizable) {
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(_canvasTarget.data(), this, false,
([](int, const EmscriptenMouseEvent* event, void* userData) -> Int {
MouseEvent e{*event};
static_cast<EmscriptenApplication*>(userData)->mousePressEvent(e);
return e.isAccepted();
}));
emscripten_set_mouseup_callback("#canvas", this, false,
emscripten_set_mouseup_callback(_canvasTarget.data(), this, false,
([](int, const EmscriptenMouseEvent* event, void* userData) -> Int {
MouseEvent e{*event};
static_cast<EmscriptenApplication*>(userData)->mouseReleaseEvent(e);
return e.isAccepted();
}));
emscripten_set_mousemove_callback("#canvas", this, false,
emscripten_set_mousemove_callback(_canvasTarget.data(), this, false,
([](int, const EmscriptenMouseEvent* event, void* userData) -> Int {
auto& app = *static_cast<EmscriptenApplication*>(userData);
/* With DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR, canvasX/Y is
@ -499,7 +540,7 @@ void EmscriptenApplication::setupCallbacks(bool resizable) {
return e.isAccepted();
}));
emscripten_set_wheel_callback("#canvas", this, false,
emscripten_set_wheel_callback(_canvasTarget.data(), this, false,
([](int, const EmscriptenWheelEvent* event, void* userData) -> Int {
MouseScrollEvent e{*event};
static_cast<EmscriptenApplication*>(userData)->mouseScrollEvent(e);
@ -516,31 +557,31 @@ void EmscriptenApplication::setupCallbacks(bool resizable) {
1.38.27 depending on -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1
but we don't want to force this flag on the users so the behavior
handles both. */
#ifdef EMSCRIPTEN_EVENT_TARGET_DOCUMENT
char* const keyboardListeningElement = reinterpret_cast<char*>(EM_ASM_INT({
const char* keyboardListeningElement = reinterpret_cast<const char*>(EM_ASM_INT({
var element = Module['keyboardListeningElement'] || document;
if(element === document) return 1;
if(element === window) return 2;
if(element === document) return 1; /* EMSCRIPTEN_EVENT_TARGET_DOCUMENT */
if(element === window) return 2; /* EMSCRIPTEN_EVENT_TARGET_WINDOW */
if('id' in element)
return allocate(intArrayFromString(element.id), 'i8', ALLOC_NORMAL);
return 0;
}));
#else
char* const keyboardListeningElement = reinterpret_cast<char*>(EM_ASM_INT({
var element = Module['keyboardListeningElement'] || document;
if(element === document) element = {id: '#document'};
if(element === window) element = {id: '#window'};
if('id' in element)
return allocate(intArrayFromString(element.id), 'i8', ALLOC_NORMAL);
return 0;
}));
#endif
#pragma GCC diagnostic pop
std::string keyboardListeningElementString;
if(keyboardListeningElement == EMSCRIPTEN_EVENT_TARGET_DOCUMENT) {
keyboardListeningElement = _deprecatedTargetBehavior ? "#document" : keyboardListeningElement;
} else if(keyboardListeningElement == EMSCRIPTEN_EVENT_TARGET_WINDOW) {
keyboardListeningElement = _deprecatedTargetBehavior ? "#window" : keyboardListeningElement;
} else if(keyboardListeningElement) {
if(!_deprecatedTargetBehavior)
keyboardListeningElementString = "#";
keyboardListeningElementString += keyboardListeningElement;
std::free(const_cast<char*>(keyboardListeningElement));
keyboardListeningElement = keyboardListeningElementString.data();
}
/* Happens only if keyboardListeningElement was set, but did not have an
`id` attribute. Instead it should be either null or undefined, a DOM
element, `window` or `document`. */
@ -571,14 +612,6 @@ void EmscriptenApplication::setupCallbacks(bool resizable) {
static_cast<EmscriptenApplication*>(userData)->keyReleaseEvent(e);
return e.isAccepted();
}));
#ifdef EMSCRIPTEN_EVENT_TARGET_DOCUMENT
if(keyboardListeningElement != EMSCRIPTEN_EVENT_TARGET_DOCUMENT &&
keyboardListeningElement != EMSCRIPTEN_EVENT_TARGET_WINDOW)
#endif
{
std::free(keyboardListeningElement);
}
}
void EmscriptenApplication::setupAnimationFrame(bool forceAnimationFrame) {

3
src/Magnum/Platform/EmscriptenApplication.h

@ -896,6 +896,9 @@ class EmscriptenApplication {
Flags _flags;
Cursor _cursor;
bool _deprecatedTargetBehavior{};
std::string _canvasTarget;
#ifdef MAGNUM_TARGET_GL
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE _glContext{};
Containers::Pointer<Platform::GLContext> _context;

Loading…
Cancel
Save