diff --git a/src/corelib/Qt6WasmMacros.cmake b/src/corelib/Qt6WasmMacros.cmake index 6153c8d617..2276fcf6da 100644 --- a/src/corelib/Qt6WasmMacros.cmake +++ b/src/corelib/Qt6WasmMacros.cmake @@ -85,18 +85,20 @@ endfunction() function(_qt_internal_add_wasm_extra_exported_methods target) get_target_property(wasm_extra_exported_methods "${target}" QT_WASM_EXTRA_EXPORTED_METHODS) + set(wasm_default_exported_methods "UTF16ToString,stringToUTF16,JSEvents") + if(NOT wasm_extra_exported_methods) set(wasm_extra_exported_methods ${QT_WASM_EXTRA_EXPORTED_METHODS}) endif() if(wasm_extra_exported_methods) target_link_options("${target}" PRIVATE - "SHELL:-s EXPORTED_RUNTIME_METHODS=UTF16ToString,stringToUTF16,${wasm_extra_exported_methods}" + "SHELL:-s EXPORTED_RUNTIME_METHODS=${wasm_default_exported_methods},${wasm_extra_exported_methods}" ) else() # an errant dangling comma will break this target_link_options("${target}" PRIVATE - "SHELL:-s EXPORTED_RUNTIME_METHODS=UTF16ToString,stringToUTF16" + "SHELL:-s EXPORTED_RUNTIME_METHODS=${wasm_default_exported_methods}" ) endif() endfunction() diff --git a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp index da9219bd6e..df72397211 100644 --- a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp +++ b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp @@ -4,8 +4,18 @@ #include "qwasmopenglcontext.h" #include "qwasmintegration.h" #include +#include #include +namespace { +void qtDoNothing(emscripten::val) { } +} // namespace + +EMSCRIPTEN_BINDINGS(qwasmopenglcontext) +{ + function("qtDoNothing", &qtDoNothing); +} + QT_BEGIN_NAMESPACE QWasmOpenGLContext::QWasmOpenGLContext(const QSurfaceFormat &format) @@ -26,12 +36,14 @@ QWasmOpenGLContext::~QWasmOpenGLContext() { if (m_context) { // Destroy GL context. Work around bug in emscripten_webgl_destroy_context - // which removes all event handlers on the canvas by temporarily removing - // emscripten's JSEvents global object. - emscripten::val jsEvents = emscripten::val::global("window")["JSEvents"]; - emscripten::val::global("window").set("JSEvents", emscripten::val::undefined()); + // which removes all event handlers on the canvas by temporarily replacing the function + // that does the removal with a function that does nothing. + emscripten::val jsEvents = emscripten::val::module_property("JSEvents"); + emscripten::val savedRemoveAllHandlersOnTargetFunction = + jsEvents["removeAllHandlersOnTarget"]; + jsEvents.set("removeAllHandlersOnTarget", emscripten::val::module_property("qtDoNothing")); emscripten_webgl_destroy_context(m_context); - emscripten::val::global("window").set("JSEvents", jsEvents); + jsEvents.set("removeAllHandlersOnTarget", savedRemoveAllHandlersOnTargetFunction); m_context = 0; } } diff --git a/tests/manual/wasm/qstdweb/CMakeLists.txt b/tests/manual/wasm/qstdweb/CMakeLists.txt index 093fe54663..234b12bab9 100644 --- a/tests/manual/wasm/qstdweb/CMakeLists.txt +++ b/tests/manual/wasm/qstdweb/CMakeLists.txt @@ -44,3 +44,29 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/../qtwasmtestlib/qtwasmtestlib.js ${CMAKE_CURRENT_BINARY_DIR}/qtwasmtestlib.js) + +qt_internal_add_manual_test(qwasmcompositor_auto + SOURCES + qwasmcompositor_main.cpp + ../qtwasmtestlib/qtwasmtestlib.cpp + PUBLIC_LIBRARIES + Qt::Core + Qt::CorePrivate + Qt::GuiPrivate +) + +include_directories(../qtwasmtestlib/) + +add_custom_command( + TARGET qwasmcompositor_auto POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/qwasmcompositor_auto.html + ${CMAKE_CURRENT_BINARY_DIR}/qwasmcompositor_auto.html) + +add_custom_command( + TARGET qwasmcompositor_auto POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/../qtwasmtestlib/qtwasmtestlib.js + ${CMAKE_CURRENT_BINARY_DIR}/qtwasmtestlib.js) + +target_link_options(qwasmcompositor_auto PRIVATE -sASYNCIFY -Os) diff --git a/tests/manual/wasm/qstdweb/qwasmcompositor_auto.html b/tests/manual/wasm/qstdweb/qwasmcompositor_auto.html new file mode 100644 index 0000000000..26daecdf41 --- /dev/null +++ b/tests/manual/wasm/qstdweb/qwasmcompositor_auto.html @@ -0,0 +1,10 @@ + + + + +

Running files auto test.

+
diff --git a/tests/manual/wasm/qstdweb/qwasmcompositor_main.cpp b/tests/manual/wasm/qstdweb/qwasmcompositor_main.cpp new file mode 100644 index 0000000000..17ab7a90e8 --- /dev/null +++ b/tests/manual/wasm/qstdweb/qwasmcompositor_main.cpp @@ -0,0 +1,172 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +namespace tst_qwasmcompositor_internal { +namespace { +class Window : public QWindow +{ + Q_OBJECT +public: + Window(); + ~Window() override { qDebug() << "dtor Window"; } + + void keyPressEvent(QKeyEvent *) final; + +signals: + void exposed(); + void keyEventReceived(); + void initFailed(); + +protected: +private: + void init(); + + void exposeEvent(QExposeEvent *) override; + bool m_exposedOnce = false; + + std::unique_ptr m_fallbackSurface; + std::unique_ptr m_rhi; +}; + +Window::Window() +{ + setSurfaceType(OpenGLSurface); +} + +void Window::exposeEvent(QExposeEvent *) +{ + if (isExposed() && !m_exposedOnce) { + m_exposedOnce = true; + init(); + emit exposed(); + } +} + +void Window::keyPressEvent(QKeyEvent *) +{ + emit keyEventReceived(); +} + +void Window::init() +{ + QRhi::Flags rhiFlags = QRhi::EnableDebugMarkers | QRhi::EnableProfiling; + + m_fallbackSurface.reset(QRhiGles2InitParams::newFallbackSurface()); + QRhiGles2InitParams params; + params.fallbackSurface = m_fallbackSurface.get(); + params.window = this; + + // Double init of RHI causes the OpenGL context to be destroyed, which causes a bug with input. + m_rhi.reset(QRhi::create(QRhi::OpenGLES2, ¶ms, rhiFlags)); + m_rhi.reset(QRhi::create(QRhi::OpenGLES2, ¶ms, rhiFlags)); + + if (!m_rhi) + emit initFailed(); +} + +} // namespace +} // namespace tst_qwasmcompositor_internal + +using namespace emscripten; + +class QWasmCompositorTest : public QObject +{ + Q_OBJECT + +public: + QWasmCompositorTest() : m_window(val::global("window")), m_testSupport(val::object()) + { + m_window.set("testSupport", m_testSupport); + m_testSupport.set("qtAddContainerElement", + emscripten::val::module_property("qtAddContainerElement")); + m_testSupport.set("qtRemoveContainerElement", + emscripten::val::module_property("qtRemoveContainerElement")); + } + + ~QWasmCompositorTest() noexcept + { + qDebug() << this << "In dtor"; + for (auto it = m_cleanup.rbegin(); it != m_cleanup.rend(); ++it) + (*it)(); + m_window.set("testSupport", emscripten::val::undefined()); + } + +private: + void init() + { + EM_ASM({ + const canvas = document.createElement("canvas"); + canvas.id = "test-canvas-qwasmcompositor"; + testSupport.canvas = canvas; + document.body.appendChild(canvas); + }); + m_cleanup.emplace_back([]() mutable { + EM_ASM({ + testSupport.qtRemoveContainerElement(testSupport.canvas); + testSupport.canvas.parentElement.removeChild(testSupport.canvas); + }); + }); + + EM_ASM({ testSupport.qtAddContainerElement(testSupport.canvas); }); + } + + template + T *Own(T *plainPtr) + { + m_cleanup.emplace_back([plainPtr]() mutable { delete plainPtr; }); + return plainPtr; + } + + val m_window; + val m_testSupport; + + std::vector> m_cleanup; + +private slots: + void testReceivingKeyboardEventsAfterOpenGLContextReset(); +}; + +void QWasmCompositorTest::testReceivingKeyboardEventsAfterOpenGLContextReset() +{ + init(); + + using Window = tst_qwasmcompositor_internal::Window; + Window *window = Own(new Window); + window->show(); + window->requestActivate(); + + QWindowSystemInterface::flushWindowSystemEvents(); + + QObject::connect(window, &Window::keyEventReceived, []() { QWASMSUCCESS(); }); + QObject::connect(window, &Window::initFailed, + []() { QWASMFAIL("Cannot initialize test window"); }); + QObject::connect(window, &Window::exposed, []() { + EM_ASM({ testSupport.canvas.dispatchEvent(new KeyboardEvent('keydown', { key : 'a' })); }); + }); +} + +int main(int argc, char **argv) +{ + auto testObject = std::make_shared(); + QtWasmTest::initTestCase(argc, argv, testObject); + return 0; +} + +#include "qwasmcompositor_main.moc"