Fix the workaround in ~QWasmOpenGLContext
The workaround stopped working because JSEvents is now not a global object. Update the workaround by exporting the JSEvents object from emscripten runtime and replacing the function that removes the event handlers to a dummy function that does nothing temporarily, only to revert it when the context is destroyed. Fixes: QTBUG-107197 Pick-to: 6.4 Change-Id: Icceae884c85e04fdafcca6cf3c563094d3f6f0dc Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>bb10
parent
e487b07e18
commit
2c8cf8eb42
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -4,8 +4,18 @@
|
|||
#include "qwasmopenglcontext.h"
|
||||
#include "qwasmintegration.h"
|
||||
#include <EGL/egl.h>
|
||||
#include <emscripten/bind.h>
|
||||
#include <emscripten/val.h>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
<!doctype html>
|
||||
<script type="text/javascript" src="qtwasmtestlib.js"></script>
|
||||
<script type="text/javascript" src="qwasmcompositor_auto.js"></script>
|
||||
<script>
|
||||
window.onload = () => {
|
||||
runTestCase(document.getElementById("log"));
|
||||
};
|
||||
</script>
|
||||
<p>Running files auto test.</p>
|
||||
<div id="log"></div>
|
||||
|
|
@ -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 <QtCore/QEvent>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtGui/qwindow.h>
|
||||
#include <QtGui/qguiapplication.h>
|
||||
#include <QtGui/qoffscreensurface.h>
|
||||
#include <QtGui/qpa/qwindowsysteminterface.h>
|
||||
#include <QtGui/private/qrhigles2_p.h>
|
||||
|
||||
#include <qtwasmtestlib.h>
|
||||
|
||||
#include <emscripten.h>
|
||||
#include <emscripten/val.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
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<QOffscreenSurface> m_fallbackSurface;
|
||||
std::unique_ptr<QRhi> 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<class T>
|
||||
T *Own(T *plainPtr)
|
||||
{
|
||||
m_cleanup.emplace_back([plainPtr]() mutable { delete plainPtr; });
|
||||
return plainPtr;
|
||||
}
|
||||
|
||||
val m_window;
|
||||
val m_testSupport;
|
||||
|
||||
std::vector<std::function<void()>> 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<QWasmCompositorTest>();
|
||||
QtWasmTest::initTestCase<QGuiApplication>(argc, argv, testObject);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "qwasmcompositor_main.moc"
|
||||
Loading…
Reference in New Issue