From ecda95ed70771ea6332fb770960ee036d5f17123 Mon Sep 17 00:00:00 2001 From: harry Date: Mon, 15 Jan 2024 04:33:27 -0500 Subject: [PATCH] Qt JS engine in work. --- src/CMakeLists.txt | 31 +++- src/drivers/Qt/QtScriptManager.cpp | 248 +++++++++++++++++++++++++++-- src/drivers/Qt/QtScriptManager.h | 27 +++- 3 files changed, 282 insertions(+), 24 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 908b102a..2cbd763f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -15,13 +15,13 @@ endif() if (NOT DEFINED QT) message( STATUS "Attempting to determine Qt Version...") - find_package( Qt6 COMPONENTS Core) + find_package( Qt6 COMPONENTS Core QUIET) if (${Qt6Core_FOUND}) message( STATUS "Found Qt Version: ${Qt6Core_VERSION}") set( QT 6 ) else() - find_package( Qt5 COMPONENTS Core) + find_package( Qt5 COMPONENTS Core QUIET) if (${Qt5Core_FOUND}) message( STATUS "Found Qt Version: ${Qt5Core_VERSION}") @@ -41,8 +41,9 @@ if ( ${QT} EQUAL 6 ) find_package( Qt6 REQUIRED COMPONENTS Widgets OpenGL OpenGLWidgets) find_package( Qt6 COMPONENTS Help QUIET) find_package( Qt6 COMPONENTS Qml) - add_definitions( ${Qt6Widgets_DEFINITIONS} ${Qt6Qml_DEFINITIONS} ${Qt6Help_DEFINITIONS} ${Qt6OpenGLWidgets_DEFINITIONS} ) - include_directories( ${Qt6Widgets_INCLUDE_DIRS} ${Qt6Qml_INCLUDE_DIRS} ${Qt6Help_INCLUDE_DIRS} ${Qt6OpenGLWidgets_INCLUDE_DIRS} ) + find_package( Qt6 COMPONENTS UiTools) + add_definitions( ${Qt6Widgets_DEFINITIONS} ${Qt6Qml_DEFINITIONS} ${Qt6UiTools_DEFINITIONS} ${Qt6Help_DEFINITIONS} ${Qt6OpenGLWidgets_DEFINITIONS} ) + include_directories( ${Qt6Widgets_INCLUDE_DIRS} ${Qt6Qml_INCLUDE_DIRS} ${Qt6UiTools_INCLUDE_DIRS} ${Qt6Help_INCLUDE_DIRS} ${Qt6OpenGLWidgets_INCLUDE_DIRS} ) if (${Qt6Help_FOUND}) message( STATUS "Qt6 Help Module Found") @@ -55,18 +56,26 @@ if ( ${QT} EQUAL 6 ) if (${Qt6Qml_FOUND}) message( STATUS "Qt6 Qml Module Found") - #add_definitions( -D__FCEU_QSCRIPT_ENABLE__ ) + add_definitions( -D__FCEU_QSCRIPT_ENABLE__ ) else() message( STATUS "Qt6 Qml Module Not Found") endif() + + if (${Qt6UiTools_FOUND}) + message( STATUS "Qt6 UiTools Module Found") + add_definitions( -D__QT_UI_TOOLS__ ) + else() + message( STATUS "Qt6 UiTools Module Not Found") + endif() else() message( STATUS "GUI Frontend: Qt5") set( Qt Qt5 ) find_package( Qt5 REQUIRED COMPONENTS Widgets OpenGL) find_package( Qt5 COMPONENTS Help QUIET) find_package( Qt5 COMPONENTS Qml) - add_definitions( ${Qt5Widgets_DEFINITIONS} ${Qt5Qml_DEFINITIONS} ${Qt5Help_DEFINITIONS} ) - include_directories( ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Qml_INCLUDE_DIRS} ${Qt5Help_INCLUDE_DIRS} ) + find_package( Qt5 COMPONENTS UiTools) + add_definitions( ${Qt5Widgets_DEFINITIONS} ${Qt5Qml_DEFINITIONS} ${Qt5UiTools_DEFINITIONS} ${Qt5Help_DEFINITIONS} ) + include_directories( ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Qml_INCLUDE_DIRS} ${Qt5UiTools_INCLUDE_DIRS} ${Qt5Help_INCLUDE_DIRS} ) if (${Qt5Help_FOUND}) message( STATUS "Qt5 Help Module Found") @@ -83,6 +92,13 @@ else() else() message( STATUS "Qt5 Qml Module Not Found") endif() + + if (${Qt5UiTools_FOUND}) + message( STATUS "Qt5 UiTools Module Found") + add_definitions( -D__QT_UI_TOOLS__ ) + else() + message( STATUS "Qt5 UiTools Module Not Found") + endif() endif() if(WIN32) @@ -714,6 +730,7 @@ target_link_libraries( ${APP_NAME} ${${Qt}Widgets_LIBRARIES} ${${Qt}Help_LIBRARIES} ${${Qt}Qml_LIBRARIES} + ${${Qt}UiTools_LIBRARIES} ${${Qt}OpenGL_LIBRARIES} ${${Qt}OpenGLWidgets_LIBRARIES} ${OPENGL_LDFLAGS} diff --git a/src/drivers/Qt/QtScriptManager.cpp b/src/drivers/Qt/QtScriptManager.cpp index 888edcea..bf053827 100644 --- a/src/drivers/Qt/QtScriptManager.cpp +++ b/src/drivers/Qt/QtScriptManager.cpp @@ -34,6 +34,10 @@ #include #include +#ifdef __QT_UI_TOOLS__ +#include +#endif + #include "../../fceu.h" #include "../../movie.h" @@ -54,6 +58,7 @@ EmuScriptObject::EmuScriptObject(QObject* parent) : QObject(parent) { + script = qobject_cast(parent); } //---------------------------------------------------- EmuScriptObject::~EmuScriptObject() @@ -68,7 +73,12 @@ void EmuScriptObject::print(const QString& msg) } } //---------------------------------------------------- -void EmuScriptObject::softreset() +void EmuScriptObject::powerOn() +{ + fceuWrapperHardReset(); +} +//---------------------------------------------------- +void EmuScriptObject::softReset() { fceuWrapperSoftReset(); } @@ -83,6 +93,92 @@ void EmuScriptObject::unpause() FCEUI_SetEmulationPaused(0); } //---------------------------------------------------- +bool EmuScriptObject::paused() +{ + return FCEUI_EmulationPaused() != 0; +} +//---------------------------------------------------- +int EmuScriptObject::framecount() +{ + return FCEUMOV_GetFrame(); +} +//---------------------------------------------------- +int EmuScriptObject::lagcount() +{ + return FCEUI_GetLagCount(); +} +//---------------------------------------------------- +bool EmuScriptObject::lagged() +{ + return FCEUI_GetLagged(); +} +//---------------------------------------------------- +void EmuScriptObject::setlagflag(bool flag) +{ + FCEUI_SetLagFlag(flag); +} +//---------------------------------------------------- +bool EmuScriptObject::emulating() +{ + return (GameInfo != nullptr); +} +//---------------------------------------------------- +void EmuScriptObject::message(const QString& msg) +{ + FCEU_DispMessage("%s",0, msg.toStdString().c_str()); +} +//---------------------------------------------------- +void EmuScriptObject::speedMode(const QString& mode) +{ + int speed = EMUSPEED_NORMAL; + bool useTurbo = false; + + if (mode.contains("normal", Qt::CaseInsensitive)) + { + speed = EMUSPEED_NORMAL; + } + else if (mode.contains("nothrottle", Qt::CaseInsensitive)) + { + useTurbo = true; + } + else if (mode.contains("turbo", Qt::CaseInsensitive)) + { + useTurbo = true; + } + else if (mode.contains("maximum", Qt::CaseInsensitive)) + { + speed = EMUSPEED_FASTEST; + } + else + { + QString msg = "Invalid mode argument \"" + mode + "\" to emu.speedmode\n"; + script->throwError(QJSValue::TypeError, msg); + return; + } + + if (useTurbo) + { + FCEUD_TurboOn(); + } + else + { + FCEUD_TurboOff(); + } + FCEUD_SetEmulationSpeed(speed); +} +//---------------------------------------------------- +bool EmuScriptObject::loadRom(const QString& romPath) +{ + int ret = LoadGame(romPath.toLocal8Bit().constData()); + + return ret != 0; +} +//---------------------------------------------------- +QString EmuScriptObject::getDir() +{ + return QString(fceuExecutablePath()); +} +//---------------------------------------------------- //---- Qt Script Instance //---------------------------------------------------- QtScriptInstance::QtScriptInstance(QObject* parent) @@ -99,6 +195,8 @@ QtScriptInstance::QtScriptInstance(QObject* parent) } engine = new QJSEngine(this); + emu->setEngine(engine); + configEngine(); QtScriptManager::getInstance()->addScriptInstance(this); @@ -118,6 +216,8 @@ QtScriptInstance::~QtScriptInstance() //---------------------------------------------------- void QtScriptInstance::resetEngine() { + running = false; + if (engine != nullptr) { engine->deleteLater(); @@ -136,7 +236,12 @@ int QtScriptInstance::configEngine() engine->globalObject().setProperty("emu", emuObject); + QJSValue guiObject = engine->newQObject(this); + + engine->globalObject().setProperty("gui", guiObject); + onFrameFinishCallback = QJSValue(); + onScriptStopCallback = QJSValue(); return 0; } @@ -145,6 +250,8 @@ int QtScriptInstance::loadScriptFile( QString filepath ) { QFile scriptFile(filepath); + running = false; + if (!scriptFile.open(QIODevice::ReadOnly)) { return -1; @@ -164,13 +271,60 @@ int QtScriptInstance::loadScriptFile( QString filepath ) } else { + running = true; //printf("Script Evaluation Success!\n"); } onFrameFinishCallback = engine->globalObject().property("onFrameFinish"); + onScriptStopCallback = engine->globalObject().property("onScriptStop"); return 0; } //---------------------------------------------------- +void QtScriptInstance::loadObjectChildren(QJSValue& jsObject, QObject* obj) +{ + const QObjectList& childList = obj->children(); + + for (auto& child : childList) + { + QString name = child->objectName(); + + if (!name.isEmpty()) + { + printf("Object: %s.%s\n", obj->objectName().toStdString().c_str(), child->objectName().toStdString().c_str()); + + QJSValue newJsObj = engine->newQObject(child); + + jsObject.setProperty(child->objectName(), newJsObj); + + loadObjectChildren( newJsObj, child); + } + } +} +//---------------------------------------------------- +void QtScriptInstance::loadUI(const QString& uiFilePath) +{ +#ifdef __QT_UI_TOOLS__ + QFile uiFile(uiFilePath); + QUiLoader uiLoader; + + QWidget* rootWidget = uiLoader.load(&uiFile, dialog); + + if (rootWidget == nullptr) + { + return; + } + QJSValue uiObject = engine->newQObject(rootWidget); + + engine->globalObject().setProperty("ui", uiObject); + + loadObjectChildren( uiObject, rootWidget); + + rootWidget->show(); +#else + throwError(QJSValue::GenericError, "Error: Application was not linked against Qt UI Tools"); +#endif +} +//---------------------------------------------------- void QtScriptInstance::print(const QString& msg) { if (dialog) @@ -179,6 +333,14 @@ void QtScriptInstance::print(const QString& msg) } } //---------------------------------------------------- +int QtScriptInstance::throwError(QJSValue::ErrorType errorType, const QString &message) +{ + running = false; + print(message); + engine->setInterrupted(true); + return 0; +} +//---------------------------------------------------- void QtScriptInstance::printSymbols(QJSValue& val, int iter) { int i=0; @@ -236,17 +398,74 @@ int QtScriptInstance::call(const QString& funcName, const QJSValueList& args) return 0; } //---------------------------------------------------- +void QtScriptInstance::stopRunning() +{ + if (running) + { + if (onScriptStopCallback.isCallable()) + { + onScriptStopCallback.call(); + } + running = false; + } +} +//---------------------------------------------------- void QtScriptInstance::onFrameFinish() { - if (onFrameFinishCallback.isCallable()) + if (running && onFrameFinishCallback.isCallable()) { onFrameFinishCallback.call(); } } //---------------------------------------------------- -bool QtScriptInstance::isRunning() +QString QtScriptInstance::openFileBrowser(const QString& initialPath) { - return false; + QString selectedFile; + QFileDialog dialog(this->dialog, tr("Open File") ); + QList urls; + bool useNativeFileDialogVal = false; + + g_config->getOption("SDL.UseNativeFileDialog", &useNativeFileDialogVal); + + const QStringList filters({ + "Any files (*)" + }); + + urls << QUrl::fromLocalFile( QDir::rootPath() ); + urls << QUrl::fromLocalFile(QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first()); + urls << QUrl::fromLocalFile(QStandardPaths::standardLocations(QStandardPaths::DesktopLocation).first()); + urls << QUrl::fromLocalFile(QStandardPaths::standardLocations(QStandardPaths::DownloadLocation).first()); + urls << QUrl::fromLocalFile( QDir( FCEUI_GetBaseDirectory() ).absolutePath() ); + + dialog.setFileMode(QFileDialog::ExistingFile); + + dialog.setNameFilters( filters ); + + dialog.setViewMode(QFileDialog::List); + dialog.setFilter( QDir::AllEntries | QDir::AllDirs | QDir::Hidden ); + dialog.setLabelText( QFileDialog::Accept, tr("Open") ); + + if (!initialPath.isEmpty() ) + { + dialog.setDirectory( initialPath ); + } + + dialog.setOption(QFileDialog::DontUseNativeDialog, !useNativeFileDialogVal); + dialog.setSidebarUrls(urls); + + int ret = dialog.exec(); + + if ( ret ) + { + QStringList fileList; + fileList = dialog.selectedFiles(); + + if ( fileList.size() > 0 ) + { + selectedFile = fileList[0]; + } + } + return selectedFile; } //---------------------------------------------------- //---- Qt Script Manager @@ -401,31 +620,24 @@ QScriptDialog_t::QScriptDialog_t(QWidget *parent) restoreGeometry(settings.value("QScriptWindow/geometry").toByteArray()); } - //---------------------------------------------------- QScriptDialog_t::~QScriptDialog_t(void) { QSettings settings; - std::list::iterator it; //printf("Destroy JS Control Window\n"); periodicTimer->stop(); - //for (it = winList.begin(); it != winList.end(); it++) - //{ - // if ((*it) == this) - // { - // winList.erase(it); - // //printf("Removing JS Window\n"); - // break; - // } - //} + scriptInstance->stopRunning(); + settings.setValue("QScriptWindow/geometry", saveGeometry()); } //---------------------------------------------------- void QScriptDialog_t::closeEvent(QCloseEvent *event) { + scriptInstance->stopRunning(); + //printf("JS Control Close Window Event\n"); done(0); deleteLater(); @@ -434,6 +646,8 @@ void QScriptDialog_t::closeEvent(QCloseEvent *event) //---------------------------------------------------- void QScriptDialog_t::closeWindow(void) { + scriptInstance->stopRunning(); + //printf("JS Control Close Window\n"); done(0); deleteLater(); @@ -625,10 +839,14 @@ void QScriptDialog_t::startScript(void) QJSValueList argList = { argArray }; scriptInstance->call("main", argList); + + refreshState(); } //---------------------------------------------------- void QScriptDialog_t::stopScript(void) { + scriptInstance->stopRunning(); + refreshState(); } //---------------------------------------------------- void QScriptDialog_t::refreshState(void) diff --git a/src/drivers/Qt/QtScriptManager.h b/src/drivers/Qt/QtScriptManager.h index 2844fcb3..34cf34c8 100644 --- a/src/drivers/Qt/QtScriptManager.h +++ b/src/drivers/Qt/QtScriptManager.h @@ -26,6 +26,7 @@ #include "utils/timeStamp.h" class QScriptDialog_t; +class QtScriptInstance; class EmuScriptObject: public QObject { @@ -34,16 +35,30 @@ public: EmuScriptObject(QObject* parent = nullptr); ~EmuScriptObject(); + void setEngine(QJSEngine* _engine){ engine = _engine; } void setDialog(QScriptDialog_t* _dialog){ dialog = _dialog; } private: + QJSEngine* engine = nullptr; QScriptDialog_t* dialog = nullptr; + QtScriptInstance* script = nullptr; public slots: Q_INVOKABLE void print(const QString& msg); - Q_INVOKABLE void softreset(); + Q_INVOKABLE void powerOn(); + Q_INVOKABLE void softReset(); Q_INVOKABLE void pause(); Q_INVOKABLE void unpause(); + Q_INVOKABLE bool paused(); + Q_INVOKABLE int framecount(); + Q_INVOKABLE int lagcount(); + Q_INVOKABLE bool lagged(); + Q_INVOKABLE void setlagflag(bool flag); + Q_INVOKABLE bool emulating(); + Q_INVOKABLE void message(const QString& msg); + Q_INVOKABLE void speedMode(const QString& mode); + Q_INVOKABLE bool loadRom(const QString& romPath); + Q_INVOKABLE QString getDir(); }; @@ -57,24 +72,32 @@ public: void resetEngine(); int loadScriptFile(QString filepath); - bool isRunning(); + bool isRunning(){ return running; }; + void stopRunning(); int call(const QString& funcName, const QJSValueList& args = QJSValueList()); void onFrameFinish(); + int throwError(QJSValue::ErrorType errorType, const QString &message = QString()); + QJSEngine* getEngine(){ return engine; }; private: int configEngine(); void printSymbols(QJSValue& val, int iter = 0); + void loadObjectChildren(QJSValue& jsObject, QObject* obj); QJSEngine* engine = nullptr; QScriptDialog_t* dialog = nullptr; EmuScriptObject* emu = nullptr; QJSValue onFrameFinishCallback; + QJSValue onScriptStopCallback; + bool running = false; public slots: Q_INVOKABLE void print(const QString& msg); + Q_INVOKABLE void loadUI(const QString& uiFilePath); + Q_INVOKABLE QString openFileBrowser(const QString& initialPath = QString()); }; class QtScriptManager : public QObject