diff --git a/CMakeLists.txt b/CMakeLists.txt index 286fe4c7..0562a733 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,8 +2,10 @@ cmake_minimum_required(VERSION 3.16) project(FreeKill VERSION 0.0.1) +if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") include_directories(fkparse/src) add_subdirectory(fkparse) +endif () find_package(Qt6 REQUIRED COMPONENTS Gui @@ -31,15 +33,38 @@ include_directories(src/network) include_directories(src/server) include_directories(src/ui) +if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") +# Fix include problem +include_directories("/usr/include/openssl-1.1/") +endif() + file(GLOB SWIG_FILES "${PROJECT_SOURCE_DIR}/src/swig/*.i") +if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") + set(SWIG_SOURCE ${PROJECT_SOURCE_DIR}/src/swig/freekill-wasm.i) +else () + set(SWIG_SOURCE ${PROJECT_SOURCE_DIR}/src/swig/freekill.i) +endif () + add_custom_command( OUTPUT ${PROJECT_SOURCE_DIR}/src/swig/freekill-wrap.cxx DEPENDS ${SWIG_FILES} COMMENT "Generating freekill-wrap.cxx" COMMAND swig -c++ -lua -Wall -o ${PROJECT_SOURCE_DIR}/src/swig/freekill-wrap.cxx - ${PROJECT_SOURCE_DIR}/src/swig/freekill.i + ${SWIG_SOURCE} ) qt_add_executable(FreeKill) + +if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") +file(GLOB_RECURSE FK_RESOURCE_FILES + RELATIVE ${PROJECT_SOURCE_DIR} + *.lua *.qml *.js *.png *.jpg *.mp3 +) +list(APPEND FK_RESOURCE_FILES "fonts/FZLBGBK.ttf") +qt_add_resources(FreeKill "qrc" + PREFIX "/" + FILES ${FK_RESOURCE_FILES} +) +endif() add_subdirectory(src) diff --git a/doc/dev/compile.md b/doc/dev/compile.md index dece14b1..b70cb55a 100644 --- a/doc/dev/compile.md +++ b/doc/dev/compile.md @@ -91,3 +91,30 @@ ___ ## 编译安卓版 用Qt安装器装好Android库,然后配置一下android-sdk就能编译了。 + +___ + +## WASM下编译 + +WASM大概就是能在浏览器中跑C++。编译用Qt Creator即可。 + +### 1. 条件与局限性 + +如果程序运行在网页上的话,那么理应只有客户端,然后提供网页的服务器上自然也运行着一个后端服务器。所以说在编译时应该舍弃掉服务端相关的代码。因此依赖库就不再需要sqlite3。 + +总之是编译个纯客户端的FK。 + +### 2. 编译OpenSSL + +进入OpenSSL的src目录,然后 + + $ ./config -no-asm -no-engine -no-dso + $ emmake make -j8 build_generated libssl.a libcrypto.a + +编译Lua的话直接emmake make就行了,总之库已经传到仓库了。 + +### 3. 部署资源文件 + +由于CMake中`file(GLOB_RECURSE)`所带来的缺陷,每当资源文件变动时,需要手动更新。 + +把构建目录中的.rcc目录删掉然后重新执行CMake->make即可。每次编译资源文件总要消耗相当多的时间。 diff --git a/lib/wasm/libcrypto.a b/lib/wasm/libcrypto.a new file mode 100644 index 00000000..83116dfb Binary files /dev/null and b/lib/wasm/libcrypto.a differ diff --git a/lib/wasm/liblua.a b/lib/wasm/liblua.a new file mode 100644 index 00000000..407b56b1 Binary files /dev/null and b/lib/wasm/liblua.a differ diff --git a/qml/Pages/RoomElement/PixmapAnimation.qml b/qml/Pages/RoomElement/PixmapAnimation.qml index 93af9ed0..b4b44545 100644 --- a/qml/Pages/RoomElement/PixmapAnimation.qml +++ b/qml/Pages/RoomElement/PixmapAnimation.qml @@ -1,5 +1,4 @@ import QtQuick -import Qt.labs.folderlistmodel import "../skin-bank.js" as SkinBank Item { @@ -18,12 +17,8 @@ Item { width: childrenRect.width height: childrenRect.height - FolderListModel { - id: fileModel - folder: SkinBank.PIXANIM_DIR + source - nameFilters: ["*.png"] - showDirs: false - } + property string folder: SkinBank.PIXANIM_DIR + source + property int fileModel Repeater { id: frames @@ -35,7 +30,7 @@ Item { onStatusChanged: { if (status == Image.Ready) { loadedFrameCount++; - if (loadedFrameCount == fileModel.count) + if (loadedFrameCount == fileModel) root.loaded(); } } @@ -52,8 +47,8 @@ Item { interval: 50 repeat: true onTriggered: { - if (currentFrame >= fileModel.count) { - frames.itemAt(fileModel.count - 1).visible = false; + if (currentFrame >= fileModel) { + frames.itemAt(fileModel - 1).visible = false; if (loop) { currentFrame = 0; } else { @@ -73,7 +68,7 @@ Item { function start() { - if (loadedFrameCount == fileModel.count) { + if (loadedFrameCount == fileModel) { timer.start(); } else { root.loaded.connect(function(){ @@ -86,4 +81,8 @@ Item { { timer.stop(); } + + Component.onCompleted: { + fileModel = Backend.ls(folder).length; + } } diff --git a/qml/Pages/RoomLogic.js b/qml/Pages/RoomLogic.js index 6a51e72e..46a3c336 100644 --- a/qml/Pages/RoomLogic.js +++ b/qml/Pages/RoomLogic.js @@ -170,7 +170,14 @@ function moveCards(moves) { } function setEmotion(id, emotion) { - let path = (SkinBank.PIXANIM_DIR + emotion).replace("file://", ""); + let path; + if (OS === "Win") { + // Windows: file:/C:/xxx/xxxx + path = (SkinBank.PIXANIM_DIR + emotion).replace("file:/", ""); + } else { + path = (SkinBank.PIXANIM_DIR + emotion).replace("file://", ""); + } + if (!Backend.exists(path)) { return; } diff --git a/qml/Pages/WebInit.qml b/qml/Pages/WebInit.qml new file mode 100644 index 00000000..bd50f3d4 --- /dev/null +++ b/qml/Pages/WebInit.qml @@ -0,0 +1,61 @@ +import QtQuick +import QtQuick.Controls + +Item { + id: root + scale: 2 + + // Change this to your server's IP or domain name + property string server_addr: "127.0.0.1:9530" + + Frame { + id: join_server + anchors.centerIn: parent + background: Rectangle { + color: "#88888888" + radius: 2 + } + + Column { + spacing: 8 + TextField { + id: screenNameEdit + text: "player" + onTextChanged: { + passwordEdit.text = ""; + let data = config.savedPassword[server_addr.editText]; + if (data) { + if (text === data.username) { + passwordEdit.text = data.shorten_password; + } + } + } + } + TextField { + id: passwordEdit + text: "" + echoMode: TextInput.Password + passwordCharacter: "*" + } + Button { + text: "Login" + enabled: passwordEdit.text !== "" + onClicked: { + config.serverAddr = server_addr; + config.screenName = screenNameEdit.text; + config.password = passwordEdit.text; + mainWindow.busy = true; + Backend.joinServer(server_addr); + } + } + } + } + + Component.onCompleted: { + config.loadConf(); + + let data = config.savedPassword[config.lastLoginServer]; + screenNameEdit.text = data.username; + passwordEdit.text = data.shorten_password; + } +} diff --git a/qml/main.qml b/qml/main.qml index 12d20d14..813618ab 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -37,11 +37,12 @@ Item { StackView { id: mainStack visible: !mainWindow.busy - initialItem: init + initialItem: OS !== "Web" ? init : webinit anchors.fill: parent } Component { id: init; Init {} } + Component { id: webinit; WebInit {} } Component { id: lobby; Lobby {} } Component { id: generalsOverview; GeneralsOverview {} } Component { id: cardsOverview; CardsOverview {} } @@ -152,7 +153,7 @@ Item { } Component.onCompleted: { - if (!Android) { + if (OS !== "Android" && OS !== "Web") { width = config.winWidth; height = config.winHeight; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 41512ba3..c5ae4bc5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -28,6 +28,7 @@ set(freekill_HEADERS "ui/qmlbackend.h" ) +set(FKP_LIB fkparse) if (WIN32) set(LUA_LIB ${PROJECT_SOURCE_DIR}/lib/win/lua54.dll) set(SQLITE3_LIB ${PROJECT_SOURCE_DIR}/lib/win/sqlite3.dll) @@ -40,6 +41,19 @@ elseif (ANDROID) QT_ANDROID_PACKAGE_SOURCE_DIR ${PROJECT_SOURCE_DIR}/android QT_ANDROID_EXTRA_LIBS "${LUA_LIB};${SQLITE3_LIB};${CRYPTO_LIB}" ) +elseif (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") + # WASM + list(REMOVE_ITEM freekill_SRCS + "network/server_socket.cpp" + #"network/client_socket.cpp" + #"network/router.cpp" + "server/server.cpp" + "server/serverplayer.cpp" + "server/room.cpp" + ) + set(FKP_LIB "") + set(LUA_LIB ${PROJECT_SOURCE_DIR}/lib/wasm/liblua.a) + set(CRYPTO_LIB ${PROJECT_SOURCE_DIR}/lib/wasm/libcrypto.a) else () set(LUA_LIB lua5.4) set(SQLITE3_LIB sqlite3) @@ -57,7 +71,7 @@ target_link_libraries(FreeKill PRIVATE ${SQLITE3_LIB} ${CRYPTO_LIB} ${READLINE_LIB} - fkparse + ${FKP_LIB} Qt6::Qml Qt6::Gui Qt6::Widgets diff --git a/src/core/util.cpp b/src/core/util.cpp index 75782c50..a4e76ddc 100644 --- a/src/core/util.cpp +++ b/src/core/util.cpp @@ -61,6 +61,7 @@ void Dumpstack(lua_State *L) } } +#ifndef Q_OS_WASM sqlite3 *OpenDatabase(const QString &filename) { sqlite3 *ret; @@ -151,6 +152,7 @@ RSA *InitServerRSA() { fclose(keyFile); return rsa; } +#endif static void writeFileMD5(QFile &dest, const QString &fname) { QFile f(fname); diff --git a/src/core/util.h b/src/core/util.h index f8257d92..767497b3 100644 --- a/src/core/util.h +++ b/src/core/util.h @@ -6,6 +6,7 @@ lua_State *CreateLuaState(); bool DoLuaScript(lua_State *L, const char *script); +#ifndef Q_OS_WASM sqlite3 *OpenDatabase(const QString &filename = "./server/users.db"); QJsonObject SelectFromDatabase(sqlite3 *db, const QString &sql); // For Lua @@ -14,6 +15,7 @@ void ExecSQL(sqlite3 *db, const QString &sql); void CloseDatabase(sqlite3 *db); RSA *InitServerRSA(); +#endif QString calcFileMD5(); diff --git a/src/main.cpp b/src/main.cpp index 73013009..ebc05141 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,7 @@ #include "qmlbackend.h" +#ifndef Q_OS_WASM #include "server.h" +#endif #if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) #include "shell.h" @@ -11,8 +13,9 @@ #include #include +#include -#ifdef Q_OS_ANDROID +#if defined(Q_OS_ANDROID) || defined(Q_OS_WASM) static bool copyPath(const QString &srcFilePath, const QString &tgtFilePath) { QFileInfo srcFileInfo(srcFilePath); @@ -48,19 +51,19 @@ void fkMsgHandler(QtMsgType type, const QMessageLogContext &context, const QStri auto threadName = QThread::currentThread()->objectName().toLatin1().constData(); switch (type) { case QtDebugMsg: - fprintf(stderr, "[%s/\e[1;30mDEBUG\e[0m] %s\n", threadName, localMsg.constData()); + fprintf(stderr, "[%s/DEBUG] %s\n", threadName, localMsg.constData()); break; case QtInfoMsg: - fprintf(stderr, "[%s/\e[1;32mINFO\e[0m] %s\n", threadName, localMsg.constData()); + fprintf(stderr, "[%s/INFO] %s\n", threadName, localMsg.constData()); break; case QtWarningMsg: - fprintf(stderr, "[%s/\e[1;33mWARNING\e[0m] %s\n", threadName, localMsg.constData()); + fprintf(stderr, "[%s/WARNING] %s\n", threadName, localMsg.constData()); break; case QtCriticalMsg: - fprintf(stderr, "[%s/\e[1;31mCRITICAL\e[0m] %s\n", threadName, localMsg.constData()); + fprintf(stderr, "[%s/CRITICAL] %s\n", threadName, localMsg.constData()); break; case QtFatalMsg: - fprintf(stderr, "[%s/\e[1;31mFATAL\e[0m] %s\n", threadName, localMsg.constData()); + fprintf(stderr, "[%s/FATAL] %s\n", threadName, localMsg.constData()); break; } } @@ -73,6 +76,7 @@ int main(int argc, char *argv[]) QCoreApplication::setApplicationName("FreeKill"); QCoreApplication::setApplicationVersion("Alpha 0.0.1"); +#ifndef Q_OS_WASM QCommandLineParser parser; parser.setApplicationDescription("FreeKill server"); parser.addHelpOption(); @@ -105,6 +109,9 @@ int main(int argc, char *argv[]) } return app->exec(); } +#else + copyPath(":/", QDir::currentPath()); +#endif app = new QApplication(argc, argv); @@ -132,19 +139,32 @@ int main(int argc, char *argv[]) backend.setEngine(engine); engine->rootContext()->setContextProperty("Backend", &backend); - engine->rootContext()->setContextProperty("AppPath", QUrl::fromLocalFile(QDir::currentPath())); + #ifdef QT_DEBUG bool debugging = true; #else bool debugging = false; #endif engine->rootContext()->setContextProperty("Debugging", debugging); -#ifdef Q_OS_ANDROID - engine->rootContext()->setContextProperty("Android", true); + + + QString system; +#if defined(Q_OS_ANDROID) + system = "Android"; +#elif defined(Q_OS_WASM) + system = "Web"; +#elif defined(Q_OS_WIN32) + system = "Win"; +#elif defined(Q_OS_LINUX) + system = "Linux"; #else - engine->rootContext()->setContextProperty("Android", false); + system = "Other"; #endif + engine->rootContext()->setContextProperty("OS", system); + + engine->rootContext()->setContextProperty("AppPath", QUrl::fromLocalFile(QDir::currentPath())); engine->load("qml/main.qml"); + if (engine->rootObjects().isEmpty()) return -1; diff --git a/src/network/router.cpp b/src/network/router.cpp index ab1db819..221d82e4 100644 --- a/src/network/router.cpp +++ b/src/network/router.cpp @@ -1,8 +1,10 @@ #include "router.h" #include "client.h" #include "client_socket.h" +#ifndef Q_OS_WASM #include "server.h" #include "serverplayer.h" +#endif #include "util.h" Router::Router(QObject *parent, ClientSocket *socket, RouterType type) @@ -13,7 +15,9 @@ Router::Router(QObject *parent, ClientSocket *socket, RouterType type) setSocket(socket); expectedReplyId = -1; replyTimeout = 0; +#ifndef Q_OS_WASM extraReplyReadySemaphore = nullptr; +#endif } Router::~Router() @@ -44,14 +48,17 @@ void Router::setSocket(ClientSocket *socket) } } +#ifndef Q_OS_WASM void Router::setReplyReadySemaphore(QSemaphore *semaphore) { extraReplyReadySemaphore = semaphore; } +#endif void Router::request(int type, const QString& command, const QString& jsonData, int timeout) { +#ifndef Q_OS_WASM // In case a request is called without a following waitForReply call if (replyReadySemaphore.available() > 0) replyReadySemaphore.acquire(replyReadySemaphore.available()); @@ -74,6 +81,7 @@ void Router::request(int type, const QString& command, body << timeout; emit messageReady(QJsonDocument(body).toJson(QJsonDocument::Compact)); +#endif } void Router::reply(int type, const QString& command, const QString& jsonData) @@ -106,6 +114,7 @@ int Router::getTimeout() const // cancel last request from the sender void Router::cancelRequest() { +#ifndef Q_OS_WASM replyMutex.lock(); expectedReplyId = -1; replyTimeout = 0; @@ -114,22 +123,28 @@ void Router::cancelRequest() if (replyReadySemaphore.available() > 0) replyReadySemaphore.acquire(replyReadySemaphore.available()); +#endif } QString Router::waitForReply() { +#ifndef Q_OS_WASM replyReadySemaphore.acquire(); return m_reply; +#endif } QString Router::waitForReply(int timeout) { +#ifndef Q_OS_WASM replyReadySemaphore.tryAcquire(1, timeout * 1000); return m_reply; +#endif } void Router::abortRequest() { +#ifndef Q_OS_WASM replyMutex.lock(); if (expectedReplyId != -1) { replyReadySemaphore.release(); @@ -139,10 +154,12 @@ void Router::abortRequest() extraReplyReadySemaphore = nullptr; } replyMutex.unlock(); +#endif } void Router::handlePacket(const QByteArray& rawPacket) { +#ifndef Q_OS_WASM static QMap lobby_actions; if (lobby_actions.size() <= 0) { lobby_actions["UpdateAvatar"] = [](ServerPlayer *sender, const QString &jsonData){ @@ -260,5 +277,28 @@ void Router::handlePacket(const QByteArray& rawPacket) locker.unlock(); emit replyReady(); } +#else + QJsonDocument packet = QJsonDocument::fromJson(rawPacket); + if (packet.isNull() || !packet.isArray()) + return; + + int requestId = packet[0].toInt(); + int type = packet[1].toInt(); + QString command = packet[2].toString(); + QString jsonData = packet[3].toString(); + + if (type & TYPE_NOTIFICATION) { + if (type & DEST_CLIENT) { + ClientInstance->callLua(command, jsonData); + } + } else if (type & TYPE_REQUEST) { + this->requestId = requestId; + this->requestTimeout = packet[4].toInt(); + + if (type & DEST_CLIENT) { + qobject_cast(parent())->callLua(command, jsonData); + } + } +#endif } diff --git a/src/network/router.h b/src/network/router.h index 406d81cc..ff575cb2 100644 --- a/src/network/router.h +++ b/src/network/router.h @@ -29,7 +29,9 @@ public: ClientSocket *getSocket() const; void setSocket(ClientSocket *socket); +#ifndef Q_OS_WASM void setReplyReadySemaphore(QSemaphore *semaphore); +#endif void request(int type, const QString &command, const QString &jsonData, int timeout); @@ -66,8 +68,10 @@ private: int expectedReplyId; int replyTimeout; QString m_reply; // should be json string +#ifndef Q_OS_WASM QSemaphore replyReadySemaphore; QSemaphore *extraReplyReadySemaphore; +#endif // Two Lua global table for callbacks and interactions // stored in the lua_State of the sender diff --git a/src/swig/freekill-wasm.i b/src/swig/freekill-wasm.i new file mode 100644 index 00000000..6f7832ee --- /dev/null +++ b/src/swig/freekill-wasm.i @@ -0,0 +1,15 @@ +%module fk + +%{ +#include "client.h" +#include "serverplayer.h" +#include "clientplayer.h" +#include "room.h" +#include "qmlbackend.h" +#include "util.h" +%} + +%include "naturalvar.i" +%include "qt.i" +%include "player.i" +%include "client.i" diff --git a/src/swig/player.i b/src/swig/player.i index cd0ad9e2..ecd684bc 100644 --- a/src/swig/player.i +++ b/src/swig/player.i @@ -34,22 +34,3 @@ public: }; extern ClientPlayer *Self; - -%nodefaultctor ServerPlayer; -%nodefaultdtor ServerPlayer; -class ServerPlayer : public Player { -public: - Server *getServer() const; - Room *getRoom() const; - void setRoom(Room *room); - - void speak(const QString &message); - - void doRequest(const QString &command, - const QString &json_data, int timeout); - QString waitForReply(); - QString waitForReply(int timeout); - void doNotify(const QString &command, const QString &json_data); - - void prepareForRequest(const QString &command, const QString &data); -}; diff --git a/src/swig/server.i b/src/swig/server.i index d3344b4e..e3e75aa0 100644 --- a/src/swig/server.i +++ b/src/swig/server.i @@ -93,3 +93,21 @@ void Room::roomStart() { %} +%nodefaultctor ServerPlayer; +%nodefaultdtor ServerPlayer; +class ServerPlayer : public Player { +public: + Server *getServer() const; + Room *getRoom() const; + void setRoom(Room *room); + + void speak(const QString &message); + + void doRequest(const QString &command, + const QString &json_data, int timeout); + QString waitForReply(); + QString waitForReply(int timeout); + void doNotify(const QString &command, const QString &json_data); + + void prepareForRequest(const QString &command, const QString &data); +}; diff --git a/src/ui/qmlbackend.cpp b/src/ui/qmlbackend.cpp index 2d96ab90..0f127846 100644 --- a/src/ui/qmlbackend.cpp +++ b/src/ui/qmlbackend.cpp @@ -1,5 +1,7 @@ #include "qmlbackend.h" +#ifndef Q_OS_WASM #include "server.h" +#endif #include "client.h" #include "util.h" @@ -11,14 +13,18 @@ QmlBackend::QmlBackend(QObject* parent) Backend = this; engine = nullptr; rsa = RSA_new(); +#ifndef Q_OS_WASM parser = fkp_new_parser(); +#endif } QmlBackend::~QmlBackend() { Backend = nullptr; RSA_free(rsa); +#ifndef Q_OS_WASM fkp_close(parser); +#endif } QQmlApplicationEngine *QmlBackend::getEngine() const @@ -33,6 +39,7 @@ void QmlBackend::setEngine(QQmlApplicationEngine *engine) void QmlBackend::startServer(ushort port) { +#ifndef Q_OS_WASM if (!ServerInstance) { Server *server = new Server(this); @@ -41,6 +48,7 @@ void QmlBackend::startServer(ushort port) emit notifyUI("ErrorMsg", tr("Cannot start server!")); } } +#endif } void QmlBackend::joinServer(QString address) @@ -81,7 +89,7 @@ void QmlBackend::cd(const QString &path) { } QStringList QmlBackend::ls(const QString &dir) { - return QDir(dir).entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); + return QDir(QUrl(dir).path()).entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); } QString QmlBackend::pwd() { @@ -169,12 +177,14 @@ QString QmlBackend::callLuaFunction(const QString &func_name, } QString QmlBackend::pubEncrypt(const QString &key, const QString &data) { - BIO *keyio = BIO_new_mem_buf(key.toLatin1().data(), -1); + auto key_bytes = key.toLatin1(); + BIO *keyio = BIO_new_mem_buf(key_bytes.constData(), -1); PEM_read_bio_RSAPublicKey(keyio, &rsa, NULL, NULL); BIO_free_all(keyio); + auto data_bytes = data.toUtf8(); unsigned char buf[RSA_size(rsa)]; - RSA_public_encrypt(data.length(), (const unsigned char *)data.toUtf8().data(), + RSA_public_encrypt(data.length(), (const unsigned char *)data_bytes.constData(), buf, rsa, RSA_PKCS1_PADDING); return QByteArray::fromRawData((const char *)buf, RSA_size(rsa)).toBase64(); } @@ -209,6 +219,7 @@ void QmlBackend::saveConf(const QString &conf) { } void QmlBackend::parseFkp(const QString &fileName) { +#ifndef Q_OS_WASM if (!QFile::exists(fileName)) { // errorEdit->setText(tr("File does not exist!")); return; @@ -245,8 +256,10 @@ void QmlBackend::parseFkp(const QString &fileName) { } */ QDir::setCurrent(cwd); +#endif } +#ifndef Q_OS_WASM static void copyFkpHash2QHash(QHash &dst, fkp_hash *from) { dst.clear(); for (size_t i = 0; i < from->capacity; i++) { @@ -261,6 +274,7 @@ void QmlBackend::readHashFromParser() { copyFkpHash2QHash(skills, parser->skills); copyFkpHash2QHash(marks, parser->marks); } +#endif QString QmlBackend::calcFileMD5() { return ::calcFileMD5(); diff --git a/src/ui/qmlbackend.h b/src/ui/qmlbackend.h index fd573301..01e07ad9 100644 --- a/src/ui/qmlbackend.h +++ b/src/ui/qmlbackend.h @@ -1,7 +1,9 @@ #ifndef _QMLBACKEND_H #define _QMLBACKEND_H +#ifndef Q_OS_WASM #include "fkparse.h" +#endif #include class QmlBackend : public QObject { @@ -48,7 +50,9 @@ signals: private: QQmlApplicationEngine *engine; RSA *rsa; +#ifndef Q_OS_WASM fkp_parser *parser; +#endif QHash generals; QHash skills; QHash marks;