From f2e227742fe90d2420ab433ae18449f46a210ae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20S=C3=B8rvig?= Date: Thu, 7 Dec 2023 12:11:23 +0100 Subject: [PATCH] wasm: delay "qtLoaded" qtloader.js event Currently we send qtLoaded in response to Emscripten onRuntimeInitialized, which is sent just before or at the time main() is called. This can be too early if Qt (app) initialization is not instantaneous, in which case we risk displaying a blank page in between hiding the loading screen and showing the application. Change this to send qtLoaded when QCoreApplication has finished its constructor and the event loop is running. Also add the possibility of registering startup tasks which can delay sending qtLoaded further. We now require a QCoreApplication (or a subclass) instance for the qtLoaded event. onRuntimeInitialized can be used as a replacement if anyone needs the previous behavior. Change-Id: I9298e881d2909aac4040c5f2068e6c7d196196cc Reviewed-by: Lorn Potter --- src/corelib/kernel/qeventdispatcher_wasm.cpp | 42 ++++++++++++++++++++ src/corelib/kernel/qeventdispatcher_wasm_p.h | 5 +++ src/plugins/platforms/wasm/qtloader.js | 2 - 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/corelib/kernel/qeventdispatcher_wasm.cpp b/src/corelib/kernel/qeventdispatcher_wasm.cpp index cc24b5ff7c1..aa441585519 100644 --- a/src/corelib/kernel/qeventdispatcher_wasm.cpp +++ b/src/corelib/kernel/qeventdispatcher_wasm.cpp @@ -217,6 +217,9 @@ QEventDispatcherWasm::QEventDispatcherWasm() #if QT_CONFIG(thread) g_mainThread = pthread_self(); #endif + // Call the "onLoaded" JavaScript callback, unless startup tasks + // have been registered which should complete first. + checkCallQtLoaded(); } else { #if QT_CONFIG(thread) std::lock_guard lock(g_staticDataMutex); @@ -894,6 +897,45 @@ void QEventDispatcherWasm::socketSelect(int timeout, int socket, bool waitForRea selectForRead, selectForWrite, socketDisconnect); } +namespace { + int g_startupTasks = 0; +} + +// The following functions manages sending the "qtLoaded" event/callback +// from qtloader.js on startup, once Qt initialization has been completed +// and the application is ready to display the first frame. This can be +// either as soon as the event loop is running, or later, if additional +// startup tasks (e.g. local font loading) have been registered. + +void QEventDispatcherWasm::registerStartupTask() +{ + ++g_startupTasks; +} + +void QEventDispatcherWasm::completeStarupTask() +{ + --g_startupTasks; + checkCallQtLoaded(); +} + +void QEventDispatcherWasm::checkCallQtLoaded() +{ + if (g_startupTasks > 0) + return; + + static bool qtLoadedCalled = false; + if (qtLoadedCalled) + return; + qtLoadedCalled = true; + + QTimer::singleShot(0, [](){ + emscripten::val qt = emscripten::val::module_property("qt"); + if (qt.isUndefined()) + return; + qt.call("onLoaded"); + }); +} + namespace { void trampoline(void *context) { diff --git a/src/corelib/kernel/qeventdispatcher_wasm_p.h b/src/corelib/kernel/qeventdispatcher_wasm_p.h index 564bbffa47e..afffc8a813b 100644 --- a/src/corelib/kernel/qeventdispatcher_wasm_p.h +++ b/src/corelib/kernel/qeventdispatcher_wasm_p.h @@ -56,6 +56,11 @@ public: static void runOnMainThreadAsync(std::function fn); static void socketSelect(int timeout, int socket, bool waitForRead, bool waitForWrite, bool *selectForRead, bool *selectForWrite, bool *socketDisconnect); + + static void registerStartupTask(); + static void completeStarupTask(); + static void checkCallQtLoaded(); + protected: virtual bool processPostedEvents(); diff --git a/src/plugins/platforms/wasm/qtloader.js b/src/plugins/platforms/wasm/qtloader.js index 7b9288cd30a..1789ad70e33 100644 --- a/src/plugins/platforms/wasm/qtloader.js +++ b/src/plugins/platforms/wasm/qtloader.js @@ -160,8 +160,6 @@ async function qtLoad(config) config.preRun = []; config.preRun.push(qtPreRun); - config.onRuntimeInitialized = () => config.qt.onLoaded?.(); - const originalLocateFile = config.locateFile; config.locateFile = filename => {