From c3422cdd6ba520ab6eeeeb6d4719c03837fb7d99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 4 Sep 2023 20:43:28 +0200 Subject: [PATCH] Platform: replace inline EM_ASM() with an external JS library. Same as the corresponding change in Corrade, this allows each function to explicitly specify its dependencies, making it no longer depending on what a particular Emscripten version decides to include by default, or forcing users to painstakingly fill the EXPORTED_FUNCTIONS array when linking the final executable. It also allows the code to eventually get conditionally included or not with preprocessor branches, for example to not include environment queries for code that won't ever access Node.js console. --- doc/changelog.dox | 8 +- modules/FindMagnum.cmake | 21 +- src/Magnum/Platform/CMakeLists.txt | 34 ++++ src/Magnum/Platform/EmscriptenApplication.cpp | 189 ++++++------------ src/Magnum/Platform/Platform.js.in | 137 +++++++++++++ src/Magnum/Platform/Sdl2Application.cpp | 53 +++-- 6 files changed, 277 insertions(+), 165 deletions(-) create mode 100644 src/Magnum/Platform/Platform.js.in diff --git a/doc/changelog.dox b/doc/changelog.dox index 8df52aa7c..86b4d494f 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -958,9 +958,11 @@ See also: - The Emscripten toolchain now looks for `lib*.a` files in addition to `*.bc` for better compatibility with 3rd party toolchains and package managers like Vcpkg (see [mosra/magnum#520](https://github.com/mosra/magnum/issues/520)). -- @ref Platform::Sdl2Application and @ref Platform::EmscriptenApplication now - use `UTF8ToString` instead of `AsciiToString` as the latter is no longer - included by default on Emscripten 3.1.21+ (see [mosra/magnum#619](https://github.com/mosra/magnum/issues/619)). +- The Emscripten build now uses `--js-library` instead of inline `EM_ASM()` + for calling into JavaScript. This allows it to properly specify its runtime + dependencies without risking breakages when new Emscripten versions make + more JS API functions optional, and circumvents the need for users to + specify `-s EXPORTED_FUNCTIONS` on their side. See [mosra/magnum#619](https://github.com/mosra/magnum/issues/619). @subsection changelog-latest-bugfixes Bug fixes diff --git a/modules/FindMagnum.cmake b/modules/FindMagnum.cmake index db701682f..fed3a7cdb 100644 --- a/modules/FindMagnum.cmake +++ b/modules/FindMagnum.cmake @@ -162,6 +162,7 @@ # MAGNUM_*_LIBRARY - Component libraries (w/o dependencies) # MAGNUM_*_LIBRARY_DEBUG - Debug version of given library, if found # MAGNUM_*_LIBRARY_RELEASE - Release version of given library, if found +# MAGNUM_PLATFORM_JS - Path to MagnumPlatform.js file # MAGNUM_BINARY_INSTALL_DIR - Binary installation directory # MAGNUM_LIBRARY_INSTALL_DIR - Library installation directory # MAGNUM_DATA_INSTALL_DIR - Data installation directory @@ -696,7 +697,17 @@ foreach(_component ${Magnum_FIND_COMPONENTS}) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES android EGL::EGL) - # EmscriptenApplication has no additional dependencies + # Emscripten application dependencies + elseif(_component STREQUAL EmscriptenApplication) + # Emscripten has various stuff implemented in JS + if(CORRADE_TARGET_EMSCRIPTEN) + find_file(MAGNUM_PLATFORM_JS MagnumPlatform.js + PATH_SUFFIXES lib) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + # TODO switch to INTERFACE_LINK_OPTIONS and SHELL: once + # we require CMake 3.13 unconditionally + INTERFACE_LINK_LIBRARIES "--js-library ${MAGNUM_PLATFORM_JS}") + endif() # GLFW application dependencies elseif(_component STREQUAL GlfwApplication) @@ -752,6 +763,14 @@ foreach(_component ${Magnum_FIND_COMPONENTS}) elseif(CORRADE_TARGET_UNIX) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${CMAKE_DL_LIBS}) + # Emscripten has various stuff implemented in JS + elseif(CORRADE_TARGET_EMSCRIPTEN) + find_file(MAGNUM_PLATFORM_JS MagnumPlatform.js + PATH_SUFFIXES lib) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + # TODO switch to INTERFACE_LINK_OPTIONS and SHELL: once + # we require CMake 3.13 unconditionally + INTERFACE_LINK_LIBRARIES "--js-library ${MAGNUM_PLATFORM_JS}") endif() # With GLVND (since CMake 3.11) we need to explicitly link to diff --git a/src/Magnum/Platform/CMakeLists.txt b/src/Magnum/Platform/CMakeLists.txt index dd481b163..75041d754 100644 --- a/src/Magnum/Platform/CMakeLists.txt +++ b/src/Magnum/Platform/CMakeLists.txt @@ -203,6 +203,24 @@ if(CORRADE_TARGET_EMSCRIPTEN AND (MAGNUM_WITH_EMSCRIPTENAPPLICATION OR MAGNUM_WI if(EMSCRIPTEN_VERSION VERSION_LESS 1.39.5) message(SEND_ERROR "Emscripten 1.39.5+, which enables DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR by default, is required to build EmscriptenApplication and Sdl2Application. Found version ${EMSCRIPTEN_VERSION}.") endif() + + # Patch the Platform.js file to contain only __deps that match given + # Emscripten version. No, there's no known better way. See the comment in + # the file itself for details. + if(EMSCRIPTEN_VERSION VERSION_LESS 3.1.35) + set(MagnumPlatform_EMSCRIPTEN_3135_ONLY "//") # Haha + else() + set(MagnumPlatform_EMSCRIPTEN_3135_ONLY "/* Emscripten 3.1.35+ */") + endif() + if(EMSCRIPTEN_VERSION VERSION_LESS 2.0.10) + set(MagnumPlatform_EMSCRIPTEN_2010_ONLY "//") # Haha + else() + set(MagnumPlatform_EMSCRIPTEN_2010_ONLY "/* Emscripten 2.0.10+ */") + endif() + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Platform.js.in + ${CMAKE_CURRENT_BINARY_DIR}/Platform.js) + + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/Platform.js DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR} RENAME MagnumPlatform.js) endif() # Emscripten application @@ -232,6 +250,14 @@ if(MAGNUM_WITH_EMSCRIPTENAPPLICATION) if(MAGNUM_TARGET_GL) target_link_libraries(MagnumEmscriptenApplication PUBLIC MagnumGL) endif() + if(CORRADE_TARGET_EMSCRIPTEN) + # TODO switch to target_link_options() and SHELL: once we require CMake + # 3.13 unconditionally + target_link_libraries(MagnumEmscriptenApplication PUBLIC "--js-library ${CMAKE_CURRENT_BINARY_DIR}/Platform.js") + # TODO this adds the dependency only on 3.13+ and only for Make/Ninja, + # any better option? + set_property(TARGET MagnumEmscriptenApplication APPEND PROPERTY INTERFACE_LINK_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/Platform.js) + endif() install(FILES ${MagnumEmscriptenApplication_HEADERS} DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Platform) install(TARGETS MagnumEmscriptenApplication @@ -344,6 +370,14 @@ if(MAGNUM_WITH_SDL2APPLICATION) MagnumGL ${MagnumSomeContext_LIBRARY}) endif() + if(CORRADE_TARGET_EMSCRIPTEN) + # TODO switch to target_link_options() and SHELL: once we require CMake + # 3.13 unconditionally + target_link_libraries(MagnumSdl2Application PUBLIC "--js-library ${CMAKE_CURRENT_BINARY_DIR}/Platform.js") + # TODO this adds the dependency only on 3.13+ and only for Make/Ninja, + # any better option? + set_property(TARGET MagnumSdl2Application APPEND PROPERTY INTERFACE_LINK_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/Platform.js) + endif() install(FILES ${MagnumSdl2Application_HEADERS} DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Platform) install(TARGETS MagnumSdl2Application diff --git a/src/Magnum/Platform/EmscriptenApplication.cpp b/src/Magnum/Platform/EmscriptenApplication.cpp index 20fc3d675..958813a8a 100644 --- a/src/Magnum/Platform/EmscriptenApplication.cpp +++ b/src/Magnum/Platform/EmscriptenApplication.cpp @@ -43,6 +43,18 @@ #include "Magnum/GL/Version.h" #endif +#ifdef CORRADE_TARGET_EMSCRIPTEN +/* Implemented in Platform.js.in */ +extern "C" { + char* magnumPlatformCanvasId(); + char* magnumPlatformKeyboardListeningElement(); + void magnumPlatformSetWindowTitle(const char* string, std::size_t size); + void magnumPlatformSetContainerCssClass(const char* string, std::size_t size); + void magnumPlatformSetCursor(const char* string, std::size_t size); + void magnumPlatformRequestAnimationFrame(int(*callback)(void*), void* state); +} +#endif + namespace Magnum { namespace Platform { using namespace Containers::Literals; @@ -178,20 +190,7 @@ namespace { } Containers::String canvasId() { - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" - /* Note: can't use let or const, as that breaks closure compiler: - ERROR - [JSC_LANGUAGE_FEATURE] This language feature is only - supported for ECMASCRIPT6 mode or better: const declaration. */ - char* id = reinterpret_cast(EM_ASM_INT({ - var id = '#' + Module['canvas'].id; - var bytes = lengthBytesUTF8(id) + 1; - var memory = _malloc(bytes); - stringToUTF8(id, memory, bytes); - return memory; - })); - #pragma GCC diagnostic pop - return Containers::String{id, [](char* data, std::size_t) { std::free(data); }}; + return Containers::String{magnumPlatformCanvasId(), [](char* data, std::size_t) { std::free(data); }}; } } @@ -434,23 +433,11 @@ Vector2i EmscriptenApplication::framebufferSize() const { #endif void EmscriptenApplication::setWindowTitle(const Containers::StringView title) { - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdollar-in-identifier-extension" - EM_ASM_({document.title = UTF8ToString($0, $1);}, title.data(), title.size()); - #pragma GCC diagnostic pop + magnumPlatformSetWindowTitle(title.data(), title.size()); } void EmscriptenApplication::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. */ - (Module['canvas'].closest('.mn-container') || - document.getElementById('container')).className = (['mn-container', UTF8ToString($0, $1)]).join(' '); - }, cssClass.data(), cssClass.size()); - #pragma GCC diagnostic pop + magnumPlatformSetContainerCssClass(cssClass.data(), cssClass.size()); /* Trigger a potential viewport event -- we don't poll the canvas size like Sdl2Application does, so it needs to be done explicitly */ @@ -539,32 +526,12 @@ void EmscriptenApplication::setupCallbacks(bool resizable) { return e.isAccepted(); })); - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" /* document and window are 'specialEventTargets' in emscripten, matching EMSCRIPTEN_EVENT_TARGET_DOCUMENT and EMSCRIPTEN_EVENT_TARGET_WINDOW. As the lookup happens with the passed parameter and arrays support element lookup via strings, we can unify the code by returning string of 1 or 2 if the target is document or window. */ - /* Note: can't use let or const, as that breaks closure compiler: - ERROR - [JSC_LANGUAGE_FEATURE] This language feature is only - supported for ECMASCRIPT6 mode or better: const declaration. */ - char* const keyboardListeningElement = reinterpret_cast(EM_ASM_INT({ - var element = Module['keyboardListeningElement'] || document; - - if(element === document) return 1; /* EMSCRIPTEN_EVENT_TARGET_DOCUMENT */ - if(element === window) return 2; /* EMSCRIPTEN_EVENT_TARGET_WINDOW */ - if('id' in element) { - var id = '#' + element.id; - var bytes = lengthBytesUTF8(id) + 1; - var memory = _malloc(bytes); - stringToUTF8(id, memory, bytes); - return memory; - } - - return 0; - })); - #pragma GCC diagnostic pop + char* const keyboardListeningElement = magnumPlatformKeyboardListeningElement(); /* If the element is a heap-allocated string, ensure it gets properly freed after */ @@ -650,43 +617,43 @@ void EmscriptenApplication::setupAnimationFrame(bool forceAnimationFrame) { namespace { -constexpr const char* CursorMap[] { - "auto", - "default", - "none", - "context-menu", - "help", - "pointer", - "progress", - "wait", - "cell", - "crosshair", - "text", - "vertical-text", - "alias", - "copy", - "move", - "no-drop", - "not-allowed", - "grab", - "grabbing", - "all-scroll", - "col-resize", - "row-resize", - "n-resize", - "e-resize", - "s-resize", - "w-resize", - "ne-resize", - "nw-resize", - "se-resize", - "sw-resize", - "ew-resize", - "ns-resize", - "nesw-resize", - "nwse-resize", - "zoom-in", - "zoom-out" +constexpr Containers::StringView CursorMap[]{ + "auto"_s, + "default"_s, + "none"_s, + "context-menu"_s, + "help"_s, + "pointer"_s, + "progress"_s, + "wait"_s, + "cell"_s, + "crosshair"_s, + "text"_s, + "vertical-text"_s, + "alias"_s, + "copy"_s, + "move"_s, + "no-drop"_s, + "not-allowed"_s, + "grab"_s, + "grabbing"_s, + "all-scroll"_s, + "col-resize"_s, + "row-resize"_s, + "n-resize"_s, + "e-resize"_s, + "s-resize"_s, + "w-resize"_s, + "ne-resize"_s, + "nw-resize"_s, + "se-resize"_s, + "sw-resize"_s, + "ew-resize"_s, + "ns-resize"_s, + "nesw-resize"_s, + "nwse-resize"_s, + "zoom-in"_s, + "zoom-out"_s }; } @@ -694,10 +661,8 @@ constexpr const char* CursorMap[] { void EmscriptenApplication::setCursor(Cursor cursor) { _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 = UTF8ToString($0);}, CursorMap[UnsignedInt(cursor)]); - #pragma GCC diagnostic pop + magnumPlatformSetCursor(CursorMap[UnsignedInt(cursor)].data(), + CursorMap[UnsignedInt(cursor)].size()); } EmscriptenApplication::Cursor EmscriptenApplication::cursor() { @@ -753,49 +718,7 @@ void EmscriptenApplication::redraw() { /* Start requestAnimationFrame loop */ _flags |= Flag::LoopActive; - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdollar-in-identifier-extension" - EM_ASM({ - /* Animation frame callback */ - var drawEvent = function() { - var id = window.requestAnimationFrame(drawEvent); - - /* Call our callback via function pointer taking an int and - returning int as well. This used to be - - if(!dynCall('ii', $0, [$1])) { - - but since 2.0.10 the dynCall isn't exported by default anymore: - https://github.com/emscripten-core/emscripten/commit/496967e00735c1523299e116dc692572d3d6d082 - and making it exported again doing so involves crazy shit with - CMake 3.13-only features and a ton of backslashes: - - target_link_options(MagnumEmscriptenApplication INTERFACE - "SHELL:-s \"DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=['\${xylophone}dynCall']\"" - "SHELL:-s \"EXPORTED_FUNCTIONS=['_main', 'dynCall']\"") - - (the ${xylophone} is needed because CMake expands that to - `\$${xylophone}dynCall` for some reason, which then the shell - collapses to `$dynCall` (assuming a $xylophone env var doesn't - exist, which it shoulndn't, but also it's 2021 and so everything - is possible), and no amount of \\\\$$$ helps avoiding that - xylophone); but doing so means we forever hardcode what - functions are exported and thus whatever extra Emscripten needs - to export will be overridden by this, causing only pain and - misery. - - So instead we rely on the implementation details of dynCall, - which are actually really simple: - https://github.com/emscripten-core/emscripten/blob/496967e00735c1523299e116dc692572d3d6d082/src/library.js#L3730-L3747 - and PRAY it doesn't change again in the future. */ - if(!wasmTable.get($0).apply(null, [$1])) { - window.cancelAnimationFrame(id); - } - }; - - window.requestAnimationFrame(drawEvent); - }, _callback, this); - #pragma GCC diagnostic pop + magnumPlatformRequestAnimationFrame(_callback, this); } void EmscriptenApplication::exit(int) { diff --git a/src/Magnum/Platform/Platform.js.in b/src/Magnum/Platform/Platform.js.in new file mode 100644 index 000000000..2664c3acc --- /dev/null +++ b/src/Magnum/Platform/Platform.js.in @@ -0,0 +1,137 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021, 2022, 2023 Vladimír Vondruš + + 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. +*/ + +/* JavaScript called by C++ code in EmscriptenApplication and Sdl2Application. + Doing it this way instead of having it inline with EM_ASM(), because this + provides an actually usable way of expressing dependencies. With EM_ASM() + one would instead have to pass + -s EXPORTED_FUNCTIONS=[...] + to the linker, and OF COURSE there still isn't a way to combine multiple of + those together: https://github.com/emscripten-core/emscripten/issues/19058 + Which would mean shifting the burden of specifying all possible exported + functions to the end user, which has no way to know if the list is correct + until the actual code needing given function gets called! + + Note: can't use let or const, as that breaks closure compiler: + ERROR - [JSC_LANGUAGE_FEATURE] This language feature is only supported for + ECMASCRIPT6 mode or better: const declaration. + + Also, lengthBytesUTF8, stringToUTF8 and UTF8ToString are only made library + functions as of https://github.com/emscripten-core/emscripten/pull/19097 + (Emscripten 3.1.35), before that they cause an error if included in __deps. + There's (of course, what else to expect) no way to query Emscripten version + from within the JS library file, and there's also no useful change in the + predefined setting variables between 3.1.34 and 35 I could rely on. Well, + except for STRUCT_INFO, which got removed from settings_internal.js in 35, + but the dumb-as-a-rock preprocessor in src/parseTools.js doesn't even + recognize #ifndef, tells me to use #if if I attempt to use #ifdef, and then + while it does what's expected on 3.1.34, on 3.1.35 it fails on + ReferenceError: STRUCT_INFO is not defined + Ourageously stupid. All of this. So what I do instead is passing this file + through configure_file() and replacing MagnumPlatform_EMSCRIPTEN_3135_ONLY + with a // on older versions. */ + +mergeInto(LibraryManager.library, { + /* Used by both EmscriptenApplication and Sdl2Application */ + magnumPlatformSetContainerCssClass__deps: [ + ${MagnumPlatform_EMSCRIPTEN_3135_ONLY} '$UTF8ToString' + ], + magnumPlatformSetContainerCssClass: function(string, size) { + /* 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. + UTF8ToString() is also used elsewhere while AsciiToString() isn't, + so even while AsciiToString() is significantly smaller it would + still cause the JS to get bigger compared to using UTF8ToString() + everywhere. */ + (Module['canvas'].closest('.mn-container') || + document.getElementById('container')).className = (['mn-container', UTF8ToString(string, size)]).join(' '); + }, + magnumPlatformSetCursor__deps: [ + ${MagnumPlatform_EMSCRIPTEN_3135_ONLY} '$UTF8ToString' + ], + magnumPlatformSetCursor: function(string, size) { + /* This could again use AsciiToString() but here we can pass explicit + size and avoid a length calculation */ + Module['canvas'].style.cursor = UTF8ToString(string, size); + }, + + /* Used by EmscriptenApplication only */ + magnumPlatformCanvasId__deps: ['malloc', + ${MagnumPlatform_EMSCRIPTEN_3135_ONLY} '$lengthBytesUTF8', '$stringToUTF8' + ], + magnumPlatformCanvasId: function() { + var id = '#' + Module['canvas'].id; + var bytes = lengthBytesUTF8(id) + 1; + var memory = _malloc(bytes); + stringToUTF8(id, memory, bytes); + return memory; + }, + magnumPlatformSetWindowTitle__deps: [ + ${MagnumPlatform_EMSCRIPTEN_3135_ONLY} '$UTF8ToString' + ], + magnumPlatformSetWindowTitle: function(string, size) { + document.title = UTF8ToString(string, size); + }, + magnumPlatformKeyboardListeningElement__deps: ['malloc', + ${MagnumPlatform_EMSCRIPTEN_3135_ONLY} '$lengthBytesUTF8', '$stringToUTF8' + ], + magnumPlatformKeyboardListeningElement: function() { + var element = Module['keyboardListeningElement'] || document; + + if(element === document) return 1; /* EMSCRIPTEN_EVENT_TARGET_DOCUMENT */ + if(element === window) return 2; /* EMSCRIPTEN_EVENT_TARGET_WINDOW */ + if('id' in element) { + var id = '#' + element.id; + /* This could theoretically also use just stringToAscii(), but + as stringToUTF8() is used elsewhere stringToAscii() would still + inflate the JS even though it's smaller. */ + var bytes = lengthBytesUTF8(id) + 1; + var memory = _malloc(bytes); + stringToUTF8(id, memory, bytes); + return memory; + } + + return 0; + }, + magnumPlatformRequestAnimationFrame__deps: [ + ${MagnumPlatform_EMSCRIPTEN_2010_ONLY} '$dynCall' + ], + magnumPlatformRequestAnimationFrame: function(callback, state) { + var drawEvent = function() { + var id = window.requestAnimationFrame(drawEvent); + if(!dynCall('ii', callback, [state])) + window.cancelAnimationFrame(id); + }; + window.requestAnimationFrame(drawEvent); + }, + +}); + +// kate: hl javascript diff --git a/src/Magnum/Platform/Sdl2Application.cpp b/src/Magnum/Platform/Sdl2Application.cpp index 1eff0e93c..cd63432c2 100644 --- a/src/Magnum/Platform/Sdl2Application.cpp +++ b/src/Magnum/Platform/Sdl2Application.cpp @@ -68,6 +68,14 @@ #include #endif +#ifdef CORRADE_TARGET_EMSCRIPTEN +/* Implemented in Platform.js.in */ +extern "C" { + void magnumPlatformSetContainerCssClass(const char* string, std::size_t size); + void magnumPlatformSetCursor(const char* string, std::size_t size); +} +#endif + namespace Magnum { namespace Platform { using namespace Containers::Literals; @@ -752,16 +760,7 @@ Vector2i Sdl2Application::framebufferSize() const { #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. */ - (Module['canvas'].closest('.mn-container') || - document.getElementById('container')).className = (['mn-container', UTF8ToString($0, $1)]).join(' '); - }, cssClass.data(), cssClass.size()); - #pragma GCC diagnostic pop + magnumPlatformSetContainerCssClass(cssClass.data(), cssClass.size()); } #endif @@ -1047,20 +1046,20 @@ constexpr SDL_SystemCursor CursorMap[] { 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" +constexpr Containers::StringView CursorMap[] { + "default"_s, + "text"_s, + "wait"_s, + "crosshair"_s, + "progress"_s, + "nwse-resize"_s, + "nesw-resize"_s, + "ew-resize"_s, + "ns-resize"_s, + "move"_s, + "not-allowed"_s, + "pointer"_s, + "none"_s /* Hidden & locked not supported yet */ }; #endif @@ -1099,10 +1098,8 @@ void Sdl2Application::setCursor(Cursor cursor) { 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 = UTF8ToString($0);}, CursorMap[UnsignedInt(cursor)]); - #pragma GCC diagnostic pop + magnumPlatformSetCursor(CursorMap[UnsignedInt(cursor)].data(), + CursorMap[UnsignedInt(cursor)].size()); #endif }