Qt JS engine in work.

This commit is contained in:
harry 2024-01-15 04:33:27 -05:00
parent 3436e221de
commit ecda95ed70
3 changed files with 282 additions and 24 deletions

View File

@ -15,13 +15,13 @@ endif()
if (NOT DEFINED QT) if (NOT DEFINED QT)
message( STATUS "Attempting to determine Qt Version...") message( STATUS "Attempting to determine Qt Version...")
find_package( Qt6 COMPONENTS Core) find_package( Qt6 COMPONENTS Core QUIET)
if (${Qt6Core_FOUND}) if (${Qt6Core_FOUND})
message( STATUS "Found Qt Version: ${Qt6Core_VERSION}") message( STATUS "Found Qt Version: ${Qt6Core_VERSION}")
set( QT 6 ) set( QT 6 )
else() else()
find_package( Qt5 COMPONENTS Core) find_package( Qt5 COMPONENTS Core QUIET)
if (${Qt5Core_FOUND}) if (${Qt5Core_FOUND})
message( STATUS "Found Qt Version: ${Qt5Core_VERSION}") 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 REQUIRED COMPONENTS Widgets OpenGL OpenGLWidgets)
find_package( Qt6 COMPONENTS Help QUIET) find_package( Qt6 COMPONENTS Help QUIET)
find_package( Qt6 COMPONENTS Qml) find_package( Qt6 COMPONENTS Qml)
add_definitions( ${Qt6Widgets_DEFINITIONS} ${Qt6Qml_DEFINITIONS} ${Qt6Help_DEFINITIONS} ${Qt6OpenGLWidgets_DEFINITIONS} ) find_package( Qt6 COMPONENTS UiTools)
include_directories( ${Qt6Widgets_INCLUDE_DIRS} ${Qt6Qml_INCLUDE_DIRS} ${Qt6Help_INCLUDE_DIRS} ${Qt6OpenGLWidgets_INCLUDE_DIRS} ) 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}) if (${Qt6Help_FOUND})
message( STATUS "Qt6 Help Module Found") message( STATUS "Qt6 Help Module Found")
@ -55,18 +56,26 @@ if ( ${QT} EQUAL 6 )
if (${Qt6Qml_FOUND}) if (${Qt6Qml_FOUND})
message( STATUS "Qt6 Qml Module Found") message( STATUS "Qt6 Qml Module Found")
#add_definitions( -D__FCEU_QSCRIPT_ENABLE__ ) add_definitions( -D__FCEU_QSCRIPT_ENABLE__ )
else() else()
message( STATUS "Qt6 Qml Module Not Found") message( STATUS "Qt6 Qml Module Not Found")
endif() 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() else()
message( STATUS "GUI Frontend: Qt5") message( STATUS "GUI Frontend: Qt5")
set( Qt Qt5 ) set( Qt Qt5 )
find_package( Qt5 REQUIRED COMPONENTS Widgets OpenGL) find_package( Qt5 REQUIRED COMPONENTS Widgets OpenGL)
find_package( Qt5 COMPONENTS Help QUIET) find_package( Qt5 COMPONENTS Help QUIET)
find_package( Qt5 COMPONENTS Qml) find_package( Qt5 COMPONENTS Qml)
add_definitions( ${Qt5Widgets_DEFINITIONS} ${Qt5Qml_DEFINITIONS} ${Qt5Help_DEFINITIONS} ) find_package( Qt5 COMPONENTS UiTools)
include_directories( ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Qml_INCLUDE_DIRS} ${Qt5Help_INCLUDE_DIRS} ) 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}) if (${Qt5Help_FOUND})
message( STATUS "Qt5 Help Module Found") message( STATUS "Qt5 Help Module Found")
@ -83,6 +92,13 @@ else()
else() else()
message( STATUS "Qt5 Qml Module Not Found") message( STATUS "Qt5 Qml Module Not Found")
endif() 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() endif()
if(WIN32) if(WIN32)
@ -714,6 +730,7 @@ target_link_libraries( ${APP_NAME}
${${Qt}Widgets_LIBRARIES} ${${Qt}Widgets_LIBRARIES}
${${Qt}Help_LIBRARIES} ${${Qt}Help_LIBRARIES}
${${Qt}Qml_LIBRARIES} ${${Qt}Qml_LIBRARIES}
${${Qt}UiTools_LIBRARIES}
${${Qt}OpenGL_LIBRARIES} ${${Qt}OpenGL_LIBRARIES}
${${Qt}OpenGLWidgets_LIBRARIES} ${${Qt}OpenGLWidgets_LIBRARIES}
${OPENGL_LDFLAGS} ${OPENGL_LDFLAGS}

View File

@ -34,6 +34,10 @@
#include <QSettings> #include <QSettings>
#include <QJSValueIterator> #include <QJSValueIterator>
#ifdef __QT_UI_TOOLS__
#include <QUiLoader>
#endif
#include "../../fceu.h" #include "../../fceu.h"
#include "../../movie.h" #include "../../movie.h"
@ -54,6 +58,7 @@
EmuScriptObject::EmuScriptObject(QObject* parent) EmuScriptObject::EmuScriptObject(QObject* parent)
: QObject(parent) : QObject(parent)
{ {
script = qobject_cast<QtScriptInstance*>(parent);
} }
//---------------------------------------------------- //----------------------------------------------------
EmuScriptObject::~EmuScriptObject() EmuScriptObject::~EmuScriptObject()
@ -68,7 +73,12 @@ void EmuScriptObject::print(const QString& msg)
} }
} }
//---------------------------------------------------- //----------------------------------------------------
void EmuScriptObject::softreset() void EmuScriptObject::powerOn()
{
fceuWrapperHardReset();
}
//----------------------------------------------------
void EmuScriptObject::softReset()
{ {
fceuWrapperSoftReset(); fceuWrapperSoftReset();
} }
@ -83,6 +93,92 @@ void EmuScriptObject::unpause()
FCEUI_SetEmulationPaused(0); 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 //---- Qt Script Instance
//---------------------------------------------------- //----------------------------------------------------
QtScriptInstance::QtScriptInstance(QObject* parent) QtScriptInstance::QtScriptInstance(QObject* parent)
@ -99,6 +195,8 @@ QtScriptInstance::QtScriptInstance(QObject* parent)
} }
engine = new QJSEngine(this); engine = new QJSEngine(this);
emu->setEngine(engine);
configEngine(); configEngine();
QtScriptManager::getInstance()->addScriptInstance(this); QtScriptManager::getInstance()->addScriptInstance(this);
@ -118,6 +216,8 @@ QtScriptInstance::~QtScriptInstance()
//---------------------------------------------------- //----------------------------------------------------
void QtScriptInstance::resetEngine() void QtScriptInstance::resetEngine()
{ {
running = false;
if (engine != nullptr) if (engine != nullptr)
{ {
engine->deleteLater(); engine->deleteLater();
@ -136,7 +236,12 @@ int QtScriptInstance::configEngine()
engine->globalObject().setProperty("emu", emuObject); engine->globalObject().setProperty("emu", emuObject);
QJSValue guiObject = engine->newQObject(this);
engine->globalObject().setProperty("gui", guiObject);
onFrameFinishCallback = QJSValue(); onFrameFinishCallback = QJSValue();
onScriptStopCallback = QJSValue();
return 0; return 0;
} }
@ -145,6 +250,8 @@ int QtScriptInstance::loadScriptFile( QString filepath )
{ {
QFile scriptFile(filepath); QFile scriptFile(filepath);
running = false;
if (!scriptFile.open(QIODevice::ReadOnly)) if (!scriptFile.open(QIODevice::ReadOnly))
{ {
return -1; return -1;
@ -164,13 +271,60 @@ int QtScriptInstance::loadScriptFile( QString filepath )
} }
else else
{ {
running = true;
//printf("Script Evaluation Success!\n"); //printf("Script Evaluation Success!\n");
} }
onFrameFinishCallback = engine->globalObject().property("onFrameFinish"); onFrameFinishCallback = engine->globalObject().property("onFrameFinish");
onScriptStopCallback = engine->globalObject().property("onScriptStop");
return 0; 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) void QtScriptInstance::print(const QString& msg)
{ {
if (dialog) 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) void QtScriptInstance::printSymbols(QJSValue& val, int iter)
{ {
int i=0; int i=0;
@ -236,17 +398,74 @@ int QtScriptInstance::call(const QString& funcName, const QJSValueList& args)
return 0; return 0;
} }
//---------------------------------------------------- //----------------------------------------------------
void QtScriptInstance::stopRunning()
{
if (running)
{
if (onScriptStopCallback.isCallable())
{
onScriptStopCallback.call();
}
running = false;
}
}
//----------------------------------------------------
void QtScriptInstance::onFrameFinish() void QtScriptInstance::onFrameFinish()
{ {
if (onFrameFinishCallback.isCallable()) if (running && onFrameFinishCallback.isCallable())
{ {
onFrameFinishCallback.call(); onFrameFinishCallback.call();
} }
} }
//---------------------------------------------------- //----------------------------------------------------
bool QtScriptInstance::isRunning() QString QtScriptInstance::openFileBrowser(const QString& initialPath)
{ {
return false; QString selectedFile;
QFileDialog dialog(this->dialog, tr("Open File") );
QList<QUrl> 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 //---- Qt Script Manager
@ -401,31 +620,24 @@ QScriptDialog_t::QScriptDialog_t(QWidget *parent)
restoreGeometry(settings.value("QScriptWindow/geometry").toByteArray()); restoreGeometry(settings.value("QScriptWindow/geometry").toByteArray());
} }
//---------------------------------------------------- //----------------------------------------------------
QScriptDialog_t::~QScriptDialog_t(void) QScriptDialog_t::~QScriptDialog_t(void)
{ {
QSettings settings; QSettings settings;
std::list<QScriptDialog_t *>::iterator it;
//printf("Destroy JS Control Window\n"); //printf("Destroy JS Control Window\n");
periodicTimer->stop(); periodicTimer->stop();
//for (it = winList.begin(); it != winList.end(); it++) scriptInstance->stopRunning();
//{
// if ((*it) == this)
// {
// winList.erase(it);
// //printf("Removing JS Window\n");
// break;
// }
//}
settings.setValue("QScriptWindow/geometry", saveGeometry()); settings.setValue("QScriptWindow/geometry", saveGeometry());
} }
//---------------------------------------------------- //----------------------------------------------------
void QScriptDialog_t::closeEvent(QCloseEvent *event) void QScriptDialog_t::closeEvent(QCloseEvent *event)
{ {
scriptInstance->stopRunning();
//printf("JS Control Close Window Event\n"); //printf("JS Control Close Window Event\n");
done(0); done(0);
deleteLater(); deleteLater();
@ -434,6 +646,8 @@ void QScriptDialog_t::closeEvent(QCloseEvent *event)
//---------------------------------------------------- //----------------------------------------------------
void QScriptDialog_t::closeWindow(void) void QScriptDialog_t::closeWindow(void)
{ {
scriptInstance->stopRunning();
//printf("JS Control Close Window\n"); //printf("JS Control Close Window\n");
done(0); done(0);
deleteLater(); deleteLater();
@ -625,10 +839,14 @@ void QScriptDialog_t::startScript(void)
QJSValueList argList = { argArray }; QJSValueList argList = { argArray };
scriptInstance->call("main", argList); scriptInstance->call("main", argList);
refreshState();
} }
//---------------------------------------------------- //----------------------------------------------------
void QScriptDialog_t::stopScript(void) void QScriptDialog_t::stopScript(void)
{ {
scriptInstance->stopRunning();
refreshState();
} }
//---------------------------------------------------- //----------------------------------------------------
void QScriptDialog_t::refreshState(void) void QScriptDialog_t::refreshState(void)

View File

@ -26,6 +26,7 @@
#include "utils/timeStamp.h" #include "utils/timeStamp.h"
class QScriptDialog_t; class QScriptDialog_t;
class QtScriptInstance;
class EmuScriptObject: public QObject class EmuScriptObject: public QObject
{ {
@ -34,16 +35,30 @@ public:
EmuScriptObject(QObject* parent = nullptr); EmuScriptObject(QObject* parent = nullptr);
~EmuScriptObject(); ~EmuScriptObject();
void setEngine(QJSEngine* _engine){ engine = _engine; }
void setDialog(QScriptDialog_t* _dialog){ dialog = _dialog; } void setDialog(QScriptDialog_t* _dialog){ dialog = _dialog; }
private: private:
QJSEngine* engine = nullptr;
QScriptDialog_t* dialog = nullptr; QScriptDialog_t* dialog = nullptr;
QtScriptInstance* script = nullptr;
public slots: public slots:
Q_INVOKABLE void print(const QString& msg); 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 pause();
Q_INVOKABLE void unpause(); 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(); void resetEngine();
int loadScriptFile(QString filepath); int loadScriptFile(QString filepath);
bool isRunning(); bool isRunning(){ return running; };
void stopRunning();
int call(const QString& funcName, const QJSValueList& args = QJSValueList()); int call(const QString& funcName, const QJSValueList& args = QJSValueList());
void onFrameFinish(); void onFrameFinish();
int throwError(QJSValue::ErrorType errorType, const QString &message = QString());
QJSEngine* getEngine(){ return engine; }; QJSEngine* getEngine(){ return engine; };
private: private:
int configEngine(); int configEngine();
void printSymbols(QJSValue& val, int iter = 0); void printSymbols(QJSValue& val, int iter = 0);
void loadObjectChildren(QJSValue& jsObject, QObject* obj);
QJSEngine* engine = nullptr; QJSEngine* engine = nullptr;
QScriptDialog_t* dialog = nullptr; QScriptDialog_t* dialog = nullptr;
EmuScriptObject* emu = nullptr; EmuScriptObject* emu = nullptr;
QJSValue onFrameFinishCallback; QJSValue onFrameFinishCallback;
QJSValue onScriptStopCallback;
bool running = false;
public slots: public slots:
Q_INVOKABLE void print(const QString& msg); 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 class QtScriptManager : public QObject