Morten Sørvig 64007c7497 wasm: add "preload" qtloader config property
Add support for downloading files from the web server
to the in-memory file system at application load time.

See included documentation for usage.

This preload functionality is different from Emscripten's
--preload-file and --embed-file in that the files are
not packed to a single data file or embedded in the
JavaScript runtime. Instead, the files are downloaded
individually from the web server, which means that they
can be cached individually, and also updated individually
without rebuilding the application.

Any file type can be preloaded. The primary use case
(at the moment) is preloading Qt plugins and QML imports.

Pick-to: 6.6
Task-number: QTBUG-63925
Change-Id: I2b71b0d6a2c12ecd3ec58e319c679cd3f6b16631
Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
2023-07-04 13:42:01 +00:00

172 lines
4.4 KiB
C++

// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QtWidgets/QtWidgets>
#include <iostream>
#include <sstream>
#include <emscripten/bind.h>
#include <emscripten/val.h>
#include <emscripten.h>
#include <QtGui/qpa/qplatformscreen.h>
namespace {
constexpr int ExitValueImmediateReturn = 42;
constexpr int ExitValueFromExitApp = 22;
std::string screenInformation()
{
auto screens = qGuiApp->screens();
std::ostringstream out;
out << "[";
const char *separator = "";
for (const auto &screen : screens) {
out << separator;
out << "[" << std::to_string(screen->geometry().x()) << ","
<< std::to_string(screen->geometry().y()) << ","
<< std::to_string(screen->geometry().width()) << ","
<< std::to_string(screen->geometry().height()) << "]";
separator = ",";
}
out << "]";
return out.str();
}
std::string logicalDpi()
{
auto screens = qGuiApp->screens();
std::ostringstream out;
out << "[";
const char *separator = "";
for (const auto &screen : screens) {
out << separator;
out << "[" << std::to_string(screen->handle()->logicalDpi().first) << ", "
<< std::to_string(screen->handle()->logicalDpi().second) << "]";
separator = ",";
}
out << "]";
return out.str();
}
std::string preloadedFiles()
{
QStringList files = QDir("/preload").entryList(QDir::Files);
std::ostringstream out;
out << "[";
const char *separator = "";
for (const auto &file : files) {
out << separator;
out << file.toStdString();
separator = ",";
}
out << "]";
return out.str();
}
void crash()
{
std::abort();
}
void exitApp()
{
exit(ExitValueFromExitApp);
}
void produceOutput()
{
fprintf(stdout, "Sample output!\n");
}
std::string retrieveArguments()
{
auto arguments = QApplication::arguments();
std::ostringstream out;
out << "[";
const char *separator = "";
for (const auto &argument : arguments) {
out << separator;
out << "'" << argument.toStdString() << "'";
separator = ",";
}
out << "]";
return out.str();
}
std::string getEnvironmentVariable(std::string name) {
return QString::fromLatin1(qgetenv(name.c_str())).toStdString();
}
} // namespace
class AppWindow : public QObject
{
Q_OBJECT
public:
AppWindow() : m_layout(new QVBoxLayout(&m_ui))
{
addWidget<QLabel>("Qt Loader integration tests");
m_ui.setLayout(m_layout);
}
void show() { m_ui.show(); }
~AppWindow() = default;
private:
template<class T, class... Args>
T *addWidget(Args... args)
{
T *widget = new T(std::forward<Args>(args)..., &m_ui);
m_layout->addWidget(widget);
return widget;
}
QWidget m_ui;
QVBoxLayout *m_layout;
};
int main(int argc, char **argv)
{
QApplication application(argc, argv);
const auto arguments = application.arguments();
const bool exitImmediately =
std::find(arguments.begin(), arguments.end(), QStringLiteral("--exit-immediately"))
!= arguments.end();
if (exitImmediately)
return ExitValueImmediateReturn;
const bool crashImmediately =
std::find(arguments.begin(), arguments.end(), QStringLiteral("--crash-immediately"))
!= arguments.end();
if (crashImmediately)
crash();
const bool noGui = std::find(arguments.begin(), arguments.end(), QStringLiteral("--no-gui"))
!= arguments.end();
if (!noGui) {
AppWindow window;
window.show();
return application.exec();
}
return application.exec();
}
EMSCRIPTEN_BINDINGS(qtLoaderIntegrationTest)
{
emscripten::constant("EXIT_VALUE_IMMEDIATE_RETURN", ExitValueImmediateReturn);
emscripten::constant("EXIT_VALUE_FROM_EXIT_APP", ExitValueFromExitApp);
emscripten::function("screenInformation", &screenInformation);
emscripten::function("logicalDpi", &logicalDpi);
emscripten::function("preloadedFiles", &preloadedFiles);
emscripten::function("crash", &crash);
emscripten::function("exitApp", &exitApp);
emscripten::function("produceOutput", &produceOutput);
emscripten::function("retrieveArguments", &retrieveArguments);
emscripten::function("getEnvironmentVariable", &getEnvironmentVariable);
}
#include "main.moc"