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

View File

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

View File

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

View File

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