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 <lorn.potter@gmail.com>
This commit is contained in:
Morten Sørvig 2023-12-07 12:11:23 +01:00
parent 37b3cc6549
commit f2e227742f
3 changed files with 47 additions and 2 deletions

View File

@ -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<std::mutex> 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<void>("onLoaded");
});
}
namespace {
void trampoline(void *context) {

View File

@ -56,6 +56,11 @@ public:
static void runOnMainThreadAsync(std::function<void(void)> 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();

View File

@ -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 =>
{