Qt JS script engine interface in work.

This commit is contained in:
harry 2024-01-15 14:17:13 -05:00
parent 8e7e5e8c05
commit 198cdafbf8
4 changed files with 137 additions and 61 deletions

View File

@ -186,6 +186,10 @@ consoleWin_t::consoleWin_t(QWidget *parent)
gameTimer->setTimerType( Qt::PreciseTimer ); gameTimer->setTimerType( Qt::PreciseTimer );
gameTimer->start( 8 ); // 120hz gameTimer->start( 8 ); // 120hz
#ifdef __FCEU_QSCRIPT_ENABLE__
QtScriptManager::create(nullptr);
#endif
emulatorThread->start(); emulatorThread->start();
g_config->getOption( "SDL.SetSchedParam", &opt ); g_config->getOption( "SDL.SetSchedParam", &opt );
@ -272,9 +276,6 @@ consoleWin_t::consoleWin_t(QWidget *parent)
// Create AVI Recording Disk Thread // Create AVI Recording Disk Thread
aviDiskThread = new AviRecordDiskThread_t(this); aviDiskThread = new AviRecordDiskThread_t(this);
#ifdef __FCEU_QSCRIPT_ENABLE__
QtScriptManager::create(this);
#endif
scrHandlerConnected = false; scrHandlerConnected = false;
} }
@ -316,6 +317,9 @@ consoleWin_t::~consoleWin_t(void)
closeGamePadConfWindow(); closeGamePadConfWindow();
#ifdef __FCEU_QSCRIPT_ENABLE__
QtScriptManager::destroy();
#endif
// The closeApp function call stops all threads. // The closeApp function call stops all threads.
// Calling quit on threads should not happen here. // Calling quit on threads should not happen here.
//printf("Thread Finished: %i \n", emulatorThread->isFinished() ); //printf("Thread Finished: %i \n", emulatorThread->isFinished() );

View File

@ -167,10 +167,29 @@ void EmuScriptObject::speedMode(const QString& mode)
FCEUD_SetEmulationSpeed(speed); FCEUD_SetEmulationSpeed(speed);
} }
//---------------------------------------------------- //----------------------------------------------------
void EmuScriptObject::registerBefore(const QJSValue& func)
{
script->registerBefore(func);
}
//----------------------------------------------------
void EmuScriptObject::registerAfter(const QJSValue& func)
{
script->registerAfter(func);
}
//----------------------------------------------------
void EmuScriptObject::registerStop(const QJSValue& func)
{
script->registerStop(func);
}
//----------------------------------------------------
bool EmuScriptObject::loadRom(const QString& romPath) bool EmuScriptObject::loadRom(const QString& romPath)
{ {
int ret = LoadGame(romPath.toLocal8Bit().constData()); int ret = 0;
if (!romPath.isEmpty())
{
ret = LoadGame(romPath.toLocal8Bit().constData());
}
return ret != 0; return ret != 0;
} }
//---------------------------------------------------- //----------------------------------------------------
@ -193,7 +212,7 @@ QtScriptInstance::QtScriptInstance(QObject* parent)
dialog = win; dialog = win;
emu->setDialog(dialog); emu->setDialog(dialog);
} }
engine = new QJSEngine(this); engine = new QJSEngine(nullptr);
emu->setEngine(engine); emu->setEngine(engine);
@ -223,7 +242,14 @@ void QtScriptInstance::resetEngine()
engine->deleteLater(); engine->deleteLater();
engine = nullptr; engine = nullptr;
} }
engine = new QJSEngine(this); engine = new QJSEngine(nullptr);
if (ui_rootWidget != nullptr)
{
ui_rootWidget->hide();
ui_rootWidget->deleteLater();
ui_rootWidget = nullptr;
}
configEngine(); configEngine();
} }
@ -240,8 +266,7 @@ int QtScriptInstance::configEngine()
engine->globalObject().setProperty("gui", guiObject); engine->globalObject().setProperty("gui", guiObject);
QtScriptManager::getInstance()->removeFrameFinishedConnection(this); onFrameBeginCallback = QJSValue();
onFrameFinishCallback = QJSValue(); onFrameFinishCallback = QJSValue();
onScriptStopCallback = QJSValue(); onScriptStopCallback = QJSValue();
@ -276,13 +301,10 @@ int QtScriptInstance::loadScriptFile( QString filepath )
running = true; running = true;
//printf("Script Evaluation Success!\n"); //printf("Script Evaluation Success!\n");
} }
onFrameFinishCallback = engine->globalObject().property("onFrameFinish"); //onFrameBeginCallback = engine->globalObject().property("onFrameBegin");
onScriptStopCallback = engine->globalObject().property("onScriptStop"); //onFrameFinishCallback = engine->globalObject().property("onFrameFinish");
//onScriptStopCallback = engine->globalObject().property("onScriptStop");
if (onFrameFinishCallback.isCallable())
{
QtScriptManager::getInstance()->addFrameFinishedConnection(this);
}
return 0; return 0;
} }
//---------------------------------------------------- //----------------------------------------------------
@ -313,24 +335,40 @@ void QtScriptInstance::loadUI(const QString& uiFilePath)
QFile uiFile(uiFilePath); QFile uiFile(uiFilePath);
QUiLoader uiLoader; QUiLoader uiLoader;
QWidget* rootWidget = uiLoader.load(&uiFile, dialog); ui_rootWidget = uiLoader.load(&uiFile, dialog);
if (rootWidget == nullptr) if (ui_rootWidget == nullptr)
{ {
return; return;
} }
QJSValue uiObject = engine->newQObject(rootWidget);
QJSValue uiObject = engine->newQObject(ui_rootWidget);
engine->globalObject().setProperty("ui", uiObject); engine->globalObject().setProperty("ui", uiObject);
loadObjectChildren( uiObject, rootWidget); loadObjectChildren( uiObject, ui_rootWidget);
rootWidget->show(); ui_rootWidget->show();
#else #else
throwError(QJSValue::GenericError, "Error: Application was not linked against Qt UI Tools"); throwError(QJSValue::GenericError, "Error: Application was not linked against Qt UI Tools");
#endif #endif
} }
//---------------------------------------------------- //----------------------------------------------------
void QtScriptInstance::registerBefore(const QJSValue& func)
{
onFrameBeginCallback = func;
}
//----------------------------------------------------
void QtScriptInstance::registerAfter(const QJSValue& func)
{
onFrameFinishCallback = func;
}
//----------------------------------------------------
void QtScriptInstance::registerStop(const QJSValue& func)
{
onScriptStopCallback = func;
}
//----------------------------------------------------
void QtScriptInstance::print(const QString& msg) void QtScriptInstance::print(const QString& msg)
{ {
if (dialog) if (dialog)
@ -416,6 +454,14 @@ void QtScriptInstance::stopRunning()
} }
} }
//---------------------------------------------------- //----------------------------------------------------
void QtScriptInstance::onFrameBegin()
{
if (running && onFrameBeginCallback.isCallable())
{
onFrameBeginCallback.call();
}
}
//----------------------------------------------------
void QtScriptInstance::onFrameFinish() void QtScriptInstance::onFrameFinish()
{ {
if (running && onFrameFinishCallback.isCallable()) if (running && onFrameFinishCallback.isCallable())
@ -499,6 +545,14 @@ QtScriptManager* QtScriptManager::create(QObject* parent)
return mgr; return mgr;
} }
//---------------------------------------------------- //----------------------------------------------------
void QtScriptManager::destroy(void)
{
if (_instance != nullptr)
{
delete _instance;
}
}
//----------------------------------------------------
void QtScriptManager::addScriptInstance(QtScriptInstance* script) void QtScriptManager::addScriptInstance(QtScriptInstance* script)
{ {
scriptList.push_back(script); scriptList.push_back(script);
@ -519,45 +573,22 @@ void QtScriptManager::removeScriptInstance(QtScriptInstance* script)
it++; it++;
} }
} }
removeFrameFinishedConnection(script);
} }
//---------------------------------------------------- //----------------------------------------------------
void QtScriptManager::addFrameFinishedConnection(QtScriptInstance* script) void QtScriptManager::frameBeginUpdate()
{ {
if (frameFinishConnectList.size() == 0) FCEU_WRAPPER_LOCK();
for (auto script : scriptList)
{ {
connect(consoleWindow->emulatorThread, SIGNAL(frameFinished(void)), this, SLOT(frameFinishedUpdate(void)), Qt::BlockingQueuedConnection); script->onFrameBegin();
}
frameFinishConnectList.push_back(script);
}
//----------------------------------------------------
void QtScriptManager::removeFrameFinishedConnection(QtScriptInstance* script)
{
auto it = frameFinishConnectList.begin();
while (it != frameFinishConnectList.end())
{
if (*it == script)
{
it = frameFinishConnectList.erase(it);
}
else
{
it++;
}
}
if (frameFinishConnectList.size() == 0)
{
consoleWindow->emulatorThread->disconnect( SIGNAL(frameFinished(void)), this, SLOT(frameFinishedUpdate(void)));
} }
FCEU_WRAPPER_UNLOCK();
} }
//---------------------------------------------------- //----------------------------------------------------
void QtScriptManager::frameFinishedUpdate() void QtScriptManager::frameFinishedUpdate()
{ {
FCEU_WRAPPER_LOCK(); FCEU_WRAPPER_LOCK();
for (auto script : frameFinishConnectList) for (auto script : scriptList)
{ {
script->onFrameFinish(); script->onFrameFinish();
} }
@ -649,6 +680,8 @@ QScriptDialog_t::QScriptDialog_t(QWidget *parent)
setLayout(mainLayout); setLayout(mainLayout);
emuThreadText.reserve(4096);
//winList.push_back(this); //winList.push_back(this);
periodicTimer = new QTimer(this); periodicTimer = new QTimer(this);
@ -694,13 +727,15 @@ void QScriptDialog_t::closeWindow(void)
//---------------------------------------------------- //----------------------------------------------------
void QScriptDialog_t::updatePeriodic(void) void QScriptDialog_t::updatePeriodic(void)
{ {
// TODO
//printf("Update JS\n"); //printf("Update JS\n");
//if (updateJSDisplay) FCEU_WRAPPER_LOCK();
//{ if (!emuThreadText.isEmpty())
// updateJSWindows(); {
// updateJSDisplay = false; jsOutput->insertPlainText(emuThreadText);
//} emuThreadText.clear();
}
refreshState();
FCEU_WRAPPER_UNLOCK();
} }
//---------------------------------------------------- //----------------------------------------------------
void QScriptDialog_t::openJSKillMessageBox(void) void QScriptDialog_t::openJSKillMessageBox(void)
@ -863,10 +898,12 @@ void QScriptDialog_t::openScriptFile(void)
//---------------------------------------------------- //----------------------------------------------------
void QScriptDialog_t::startScript(void) void QScriptDialog_t::startScript(void)
{ {
FCEU_WRAPPER_LOCK();
scriptInstance->resetEngine(); scriptInstance->resetEngine();
if (scriptInstance->loadScriptFile(scriptPath->text())) if (scriptInstance->loadScriptFile(scriptPath->text()))
{ {
// Script parsing error // Script parsing error
FCEU_WRAPPER_UNLOCK();
return; return;
} }
// TODO add option to pass options to script main. // TODO add option to pass options to script main.
@ -880,12 +917,16 @@ void QScriptDialog_t::startScript(void)
scriptInstance->call("main", argList); scriptInstance->call("main", argList);
refreshState(); refreshState();
FCEU_WRAPPER_UNLOCK();
} }
//---------------------------------------------------- //----------------------------------------------------
void QScriptDialog_t::stopScript(void) void QScriptDialog_t::stopScript(void)
{ {
FCEU_WRAPPER_LOCK();
scriptInstance->stopRunning(); scriptInstance->stopRunning();
refreshState(); refreshState();
FCEU_WRAPPER_UNLOCK();
} }
//---------------------------------------------------- //----------------------------------------------------
void QScriptDialog_t::refreshState(void) void QScriptDialog_t::refreshState(void)
@ -904,7 +945,14 @@ void QScriptDialog_t::refreshState(void)
//---------------------------------------------------- //----------------------------------------------------
void QScriptDialog_t::logOutput(const QString& text) void QScriptDialog_t::logOutput(const QString& text)
{ {
jsOutput->insertPlainText(text); if (QThread::currentThread() == consoleWindow->emulatorThread)
{
emuThreadText.append(text);
}
else
{
jsOutput->insertPlainText(text);
}
} }
//---------------------------------------------------- //----------------------------------------------------
#endif // __FCEU_QSCRIPT_ENABLE__ #endif // __FCEU_QSCRIPT_ENABLE__

View File

@ -55,6 +55,9 @@ public slots:
Q_INVOKABLE bool lagged(); Q_INVOKABLE bool lagged();
Q_INVOKABLE void setlagflag(bool flag); Q_INVOKABLE void setlagflag(bool flag);
Q_INVOKABLE bool emulating(); Q_INVOKABLE bool emulating();
Q_INVOKABLE void registerBefore(const QJSValue& func);
Q_INVOKABLE void registerAfter(const QJSValue& func);
Q_INVOKABLE void registerStop(const QJSValue& func);
Q_INVOKABLE void message(const QString& msg); Q_INVOKABLE void message(const QString& msg);
Q_INVOKABLE void speedMode(const QString& mode); Q_INVOKABLE void speedMode(const QString& mode);
Q_INVOKABLE bool loadRom(const QString& romPath); Q_INVOKABLE bool loadRom(const QString& romPath);
@ -76,6 +79,7 @@ public:
void stopRunning(); void stopRunning();
int call(const QString& funcName, const QJSValueList& args = QJSValueList()); int call(const QString& funcName, const QJSValueList& args = QJSValueList());
void onFrameBegin();
void onFrameFinish(); void onFrameFinish();
int throwError(QJSValue::ErrorType errorType, const QString &message = QString()); int throwError(QJSValue::ErrorType errorType, const QString &message = QString());
@ -90,6 +94,8 @@ private:
QJSEngine* engine = nullptr; QJSEngine* engine = nullptr;
QScriptDialog_t* dialog = nullptr; QScriptDialog_t* dialog = nullptr;
EmuScriptObject* emu = nullptr; EmuScriptObject* emu = nullptr;
QWidget* ui_rootWidget = nullptr;
QJSValue onFrameBeginCallback;
QJSValue onFrameFinishCallback; QJSValue onFrameFinishCallback;
QJSValue onScriptStopCallback; QJSValue onScriptStopCallback;
bool running = false; bool running = false;
@ -98,6 +104,9 @@ 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 void loadUI(const QString& uiFilePath);
Q_INVOKABLE QString openFileBrowser(const QString& initialPath = QString()); Q_INVOKABLE QString openFileBrowser(const QString& initialPath = QString());
Q_INVOKABLE void registerBefore(const QJSValue& func);
Q_INVOKABLE void registerAfter(const QJSValue& func);
Q_INVOKABLE void registerStop(const QJSValue& func);
}; };
class QtScriptManager : public QObject class QtScriptManager : public QObject
@ -110,21 +119,19 @@ public:
static QtScriptManager* getInstance(){ return _instance; } static QtScriptManager* getInstance(){ return _instance; }
static QtScriptManager* create(QObject* parent = nullptr); static QtScriptManager* create(QObject* parent = nullptr);
static void destroy();
int numScriptsLoaded(void){ return scriptList.size(); }
void addScriptInstance(QtScriptInstance* script); void addScriptInstance(QtScriptInstance* script);
void removeScriptInstance(QtScriptInstance* script); void removeScriptInstance(QtScriptInstance* script);
void addFrameFinishedConnection(QtScriptInstance* script);
void removeFrameFinishedConnection(QtScriptInstance* script);
private: private:
static QtScriptManager* _instance; static QtScriptManager* _instance;
QList<QtScriptInstance*> scriptList; QList<QtScriptInstance*> scriptList;
QList<QtScriptInstance*> frameFinishConnectList;
FCEU::timeStampRecord lastFrameUpdate; FCEU::timeStampRecord lastFrameUpdate;
int frameFinishedConnectCount = 0;
public slots: public slots:
void frameBeginUpdate();
void frameFinishedUpdate(); void frameFinishedUpdate();
}; };
@ -151,6 +158,7 @@ protected:
QPushButton *startButton; QPushButton *startButton;
QTextEdit *jsOutput; QTextEdit *jsOutput;
QtScriptInstance *scriptInstance; QtScriptInstance *scriptInstance;
QString emuThreadText;
private: private:
public slots: public slots:

View File

@ -42,6 +42,7 @@
#include "Qt/CheatsConf.h" #include "Qt/CheatsConf.h"
#include "Qt/SymbolicDebug.h" #include "Qt/SymbolicDebug.h"
#include "Qt/CodeDataLogger.h" #include "Qt/CodeDataLogger.h"
#include "Qt/QtScriptManager.h"
#include "Qt/ConsoleDebugger.h" #include "Qt/ConsoleDebugger.h"
#include "Qt/ConsoleWindow.h" #include "Qt/ConsoleWindow.h"
#include "Qt/ConsoleUtilities.h" #include "Qt/ConsoleUtilities.h"
@ -1477,17 +1478,32 @@ int fceuWrapperUpdate( void )
if ( GameInfo ) if ( GameInfo )
{ {
auto* qscriptMgr = QtScriptManager::getInstance();
bool scriptsLoaded = (qscriptMgr != nullptr) && (qscriptMgr->numScriptsLoaded() > 0);
if (scriptsLoaded)
{
qscriptMgr->frameBeginUpdate();
}
DoFun(frameskip, periodic_saves); DoFun(frameskip, periodic_saves);
if (scriptsLoaded)
{
qscriptMgr->frameFinishedUpdate();
}
hexEditorUpdateMemoryValues(); hexEditorUpdateMemoryValues();
fceuWrapperUnLock(); fceuWrapperUnLock();
emulatorHasMutex = 0;
if ( consoleWindow ) if ( consoleWindow )
{ {
consoleWindow->emulatorThread->signalFrameFinished(); consoleWindow->emulatorThread->signalFrameFinished();
} }
emulatorHasMutex = 0;
#ifdef __FCEU_PROFILER_ENABLE__ #ifdef __FCEU_PROFILER_ENABLE__
FCEU_profiler_log_thread_activity(); FCEU_profiler_log_thread_activity();