From 6b9601604754cf9ade74114f82e8645092e42ac8 Mon Sep 17 00:00:00 2001 From: harry Date: Mon, 29 Jan 2024 23:17:15 -0500 Subject: [PATCH] JS scripting in work. --- src/drivers/Qt/QtScriptManager.cpp | 209 ++++++++++++++++++++++++----- src/drivers/Qt/QtScriptManager.h | 50 ++++++- src/drivers/Qt/fceuWrapper.cpp | 4 - 3 files changed, 219 insertions(+), 44 deletions(-) diff --git a/src/drivers/Qt/QtScriptManager.cpp b/src/drivers/Qt/QtScriptManager.cpp index 38958847..f772e227 100644 --- a/src/drivers/Qt/QtScriptManager.cpp +++ b/src/drivers/Qt/QtScriptManager.cpp @@ -41,6 +41,7 @@ #include "../../fceu.h" #include "../../movie.h" +#include "../../video.h" #include "../../x6502.h" #include "../../debug.h" @@ -55,6 +56,35 @@ #include "Qt/ConsoleUtilities.h" #include "Qt/ConsoleWindow.h" +// pix format for JS graphics +#define BUILD_PIXEL_ARGB8888(A,R,G,B) (((int) (A) << 24) | ((int) (R) << 16) | ((int) (G) << 8) | (int) (B)) +#define DECOMPOSE_PIXEL_ARGB8888(PIX,A,R,G,B) { (A) = ((PIX) >> 24) & 0xff; (R) = ((PIX) >> 16) & 0xff; (G) = ((PIX) >> 8) & 0xff; (B) = (PIX) & 0xff; } +#define JS_BUILD_PIXEL BUILD_PIXEL_ARGB8888 +#define JS_DECOMPOSE_PIXEL DECOMPOSE_PIXEL_ARGB8888 +#define JS_PIXEL_A(PIX) (((PIX) >> 24) & 0xff) +#define JS_PIXEL_R(PIX) (((PIX) >> 16) & 0xff) +#define JS_PIXEL_G(PIX) (((PIX) >> 8) & 0xff) +#define JS_PIXEL_B(PIX) ((PIX) & 0xff) + +namespace JS +{ +//---------------------------------------------------- +//---- Color Object +//---------------------------------------------------- +int ColorScriptObject::numInstances = 0; + +ColorScriptObject::ColorScriptObject(int r, int g, int b) + : QObject(), color(r, g, b), _palette(0) +{ + numInstances++; + //printf("ColorScriptObject(r,g,b) %p Constructor: %i\n", this, numInstances); +} +//---------------------------------------------------- +ColorScriptObject::~ColorScriptObject() +{ + numInstances--; + //printf("ColorScriptObject %p Destructor: %i\n", this, numInstances); +} //---------------------------------------------------- //---- EMU Script Object //---------------------------------------------------- @@ -196,11 +226,41 @@ bool EmuScriptObject::loadRom(const QString& romPath) return ret != 0; } //---------------------------------------------------- +bool EmuScriptObject::onEmulationThread() +{ + bool isEmuThread = (consoleWindow != nullptr) && + (QThread::currentThread() == consoleWindow->emulatorThread); + return isEmuThread; +} +//---------------------------------------------------- QString EmuScriptObject::getDir() { return QString(fceuExecutablePath()); } //---------------------------------------------------- +QJSValue EmuScriptObject::getScreenPixel(int x, int y, bool useBackup) +{ + int r,g,b,p; + uint32_t pixel = GetScreenPixel(x,y,useBackup); + r = JS_PIXEL_R(pixel); + g = JS_PIXEL_G(pixel); + b = JS_PIXEL_B(pixel); + + p = GetScreenPixelPalette(x,y,useBackup); + + ColorScriptObject* pixelObj = new ColorScriptObject(r, g, b); + + pixelObj->setPalette(p); + + pixelObj->moveToThread(QApplication::instance()->thread()); + + QJSValue jsVal = engine->newQObject(pixelObj); + + QJSEngine::setObjectOwnership( pixelObj, QJSEngine::JavaScriptOwnership); + + return jsVal; +} +//---------------------------------------------------- //---- Memory Script Object //---------------------------------------------------- //---------------------------------------------------- @@ -590,6 +650,7 @@ void MemoryScriptObject::unregisterAll() numWriteFuncsRegistered = 0; numExecFuncsRegistered = 0; } +} // JS //---------------------------------------------------- //---- Qt Script Instance //---------------------------------------------------- @@ -598,47 +659,64 @@ QtScriptInstance::QtScriptInstance(QObject* parent) { QScriptDialog_t* win = qobject_cast(parent); - emu = new EmuScriptObject(this); - mem = new MemoryScriptObject(this); - if (win != nullptr) { dialog = win; - emu->setDialog(dialog); - mem->setDialog(dialog); } - engine = new QJSEngine(nullptr); - emu->setEngine(engine); - mem->setEngine(engine); - - configEngine(); + initEngine(); QtScriptManager::getInstance()->addScriptInstance(this); } //---------------------------------------------------- QtScriptInstance::~QtScriptInstance() { - if (engine != nullptr) - { - engine->deleteLater(); - engine = nullptr; - } QtScriptManager::getInstance()->removeScriptInstance(this); + shutdownEngine(); + //printf("QtScriptInstance Destroyed\n"); } //---------------------------------------------------- -void QtScriptInstance::resetEngine() +void QtScriptInstance::shutdownEngine() { running = false; + if (onFrameBeginCallback != nullptr) + { + delete onFrameBeginCallback; + onFrameBeginCallback = nullptr; + } + if (onFrameFinishCallback != nullptr) + { + delete onFrameFinishCallback; + onFrameFinishCallback = nullptr; + } + if (onScriptStopCallback != nullptr) + { + delete onScriptStopCallback; + onScriptStopCallback = nullptr; + } + if (engine != nullptr) { - engine->deleteLater(); + engine->collectGarbage(); + //engine->deleteLater(); + delete engine; engine = nullptr; } - engine = new QJSEngine(nullptr); + + if (emu != nullptr) + { + delete emu; + emu = nullptr; + } + if (mem != nullptr) + { + mem->reset(); + delete mem; + mem = nullptr; + } if (ui_rootWidget != nullptr) { @@ -646,15 +724,29 @@ void QtScriptInstance::resetEngine() ui_rootWidget->deleteLater(); ui_rootWidget = nullptr; } - mem->reset(); - - configEngine(); } //---------------------------------------------------- -int QtScriptInstance::configEngine() +void QtScriptInstance::resetEngine() { + shutdownEngine(); + initEngine(); +} +//---------------------------------------------------- +int QtScriptInstance::initEngine() +{ + engine = new QJSEngine(this); + + emu = new JS::EmuScriptObject(this); + mem = new JS::MemoryScriptObject(this); + + emu->setDialog(dialog); + mem->setDialog(dialog); + engine->installExtensions(QJSEngine::ConsoleExtension); + emu->setEngine(engine); + mem->setEngine(engine); + QJSValue emuObject = engine->newQObject(emu); engine->globalObject().setProperty("emu", emuObject); @@ -667,9 +759,9 @@ int QtScriptInstance::configEngine() engine->globalObject().setProperty("gui", guiObject); - onFrameBeginCallback = QJSValue(); - onFrameFinishCallback = QJSValue(); - onScriptStopCallback = QJSValue(); + // Class Type Definitions for Script Use + QJSValue jsMetaObject = engine->newQMetaObject(&JS::ColorScriptObject::staticMetaObject); + engine->globalObject().setProperty("Color", jsMetaObject); return 0; } @@ -757,17 +849,29 @@ void QtScriptInstance::loadUI(const QString& uiFilePath) //---------------------------------------------------- void QtScriptInstance::registerBefore(const QJSValue& func) { - onFrameBeginCallback = func; + if (onFrameBeginCallback != nullptr) + { + delete onFrameBeginCallback; + } + onFrameBeginCallback = new QJSValue(func); } //---------------------------------------------------- void QtScriptInstance::registerAfter(const QJSValue& func) { - onFrameFinishCallback = func; + if (onFrameFinishCallback != nullptr) + { + delete onFrameFinishCallback; + } + onFrameFinishCallback = new QJSValue(func); } //---------------------------------------------------- void QtScriptInstance::registerStop(const QJSValue& func) { - onScriptStopCallback = func; + if (onScriptStopCallback != nullptr) + { + delete onScriptStopCallback; + } + onScriptStopCallback = new QJSValue(func); } //---------------------------------------------------- void QtScriptInstance::print(const QString& msg) @@ -776,6 +880,23 @@ void QtScriptInstance::print(const QString& msg) { dialog->logOutput(msg); } + else + { + qDebug() << msg; + } +} +//---------------------------------------------------- +bool QtScriptInstance::onEmulationThread() +{ + bool isEmuThread = (consoleWindow != nullptr) && + (QThread::currentThread() == consoleWindow->emulatorThread); + return isEmuThread; +} +//---------------------------------------------------- +bool QtScriptInstance::onGuiThread() +{ + bool isGuiThread = (QThread::currentThread() == QApplication::instance()->thread()); + return isGuiThread; } //---------------------------------------------------- int QtScriptInstance::throwError(QJSValue::ErrorType errorType, const QString &message) @@ -844,9 +965,14 @@ void QtScriptInstance::stopRunning() FCEU_WRAPPER_LOCK(); if (running) { - if (onScriptStopCallback.isCallable()) + if (onScriptStopCallback != nullptr && onScriptStopCallback->isCallable()) { - onScriptStopCallback.call(); + QJSValue callResult = onScriptStopCallback->call(); + + if (callResult.isError()) + { + print(callResult.toString()); + } } running = false; @@ -857,17 +983,29 @@ void QtScriptInstance::stopRunning() //---------------------------------------------------- void QtScriptInstance::onFrameBegin() { - if (running && onFrameBeginCallback.isCallable()) + if (running && onFrameBeginCallback != nullptr && onFrameBeginCallback->isCallable()) { - onFrameBeginCallback.call(); + QJSValue callResult = onFrameBeginCallback->call(); + + if (callResult.isError()) + { + print(callResult.toString()); + running = false; + } } } //---------------------------------------------------- void QtScriptInstance::onFrameFinish() { - if (running && onFrameFinishCallback.isCallable()) + if (running && onFrameFinishCallback != nullptr && onFrameFinishCallback->isCallable()) { - onFrameFinishCallback.call(); + QJSValue callResult = onFrameFinishCallback->call(); + + if (callResult.isError()) + { + print(callResult.toString()); + running = false; + } } } //---------------------------------------------------- @@ -1122,7 +1260,10 @@ QScriptDialog_t::~QScriptDialog_t(void) periodicTimer->stop(); + clearPropertyTree(); + scriptInstance->stopRunning(); + delete scriptInstance; settings.setValue("QScriptWindow/geometry", saveGeometry()); } diff --git a/src/drivers/Qt/QtScriptManager.h b/src/drivers/Qt/QtScriptManager.h index 82a0de16..c3d7712c 100644 --- a/src/drivers/Qt/QtScriptManager.h +++ b/src/drivers/Qt/QtScriptManager.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -32,6 +33,36 @@ class QScriptDialog_t; class QtScriptInstance; +namespace JS +{ + +class ColorScriptObject: public QObject +{ + Q_OBJECT + Q_PROPERTY(int red READ getRed WRITE setRed) + Q_PROPERTY(int green READ getGreen WRITE setGreen) + Q_PROPERTY(int blue READ getBlue WRITE setBlue) + Q_PROPERTY(int palette READ getPalette WRITE setPalette) +public: + Q_INVOKABLE ColorScriptObject(int r=0, int g=0, int b=0); + ~ColorScriptObject(); + +private: + QColor color; + int _palette; + static int numInstances; + +public slots: + Q_INVOKABLE int getRed(){ return color.red(); } + Q_INVOKABLE int getGreen(){ return color.green(); } + Q_INVOKABLE int getBlue(){ return color.blue(); } + Q_INVOKABLE void setRed(int r){ color.setRed(r); } + Q_INVOKABLE void setGreen(int g){ color.setGreen(g); } + Q_INVOKABLE void setBlue(int b){ color.setBlue(b); } + Q_INVOKABLE int getPalette(){ return _palette; } + Q_INVOKABLE void setPalette(int p){ _palette = p; } +}; + class EmuScriptObject: public QObject { Q_OBJECT @@ -65,7 +96,9 @@ public slots: Q_INVOKABLE void message(const QString& msg); Q_INVOKABLE void speedMode(const QString& mode); Q_INVOKABLE bool loadRom(const QString& romPath); + Q_INVOKABLE bool onEmulationThread(); Q_INVOKABLE QString getDir(); + Q_INVOKABLE QJSValue getScreenPixel(int x, int y, bool useBackup = false); }; @@ -126,6 +159,7 @@ public slots: Q_INVOKABLE void unregisterExec(const QJSValue& func, int address, int size = 1); Q_INVOKABLE void unregisterAll(); }; +} // JS class QtScriptInstance : public QObject { @@ -146,21 +180,23 @@ public: int throwError(QJSValue::ErrorType errorType, const QString &message = QString()); + QObject* getObjectParent(); QJSEngine* getEngine(){ return engine; }; private: - int configEngine(); + int initEngine(); + void shutdownEngine(); void printSymbols(QJSValue& val, int iter = 0); void loadObjectChildren(QJSValue& jsObject, QObject* obj); QJSEngine* engine = nullptr; QScriptDialog_t* dialog = nullptr; - EmuScriptObject* emu = nullptr; - MemoryScriptObject* mem = nullptr; + JS::EmuScriptObject* emu = nullptr; + JS::MemoryScriptObject* mem = nullptr; QWidget* ui_rootWidget = nullptr; - QJSValue onFrameBeginCallback; - QJSValue onFrameFinishCallback; - QJSValue onScriptStopCallback; + QJSValue *onFrameBeginCallback = nullptr; + QJSValue *onFrameFinishCallback = nullptr; + QJSValue *onScriptStopCallback = nullptr; bool running = false; public slots: @@ -170,6 +206,8 @@ public slots: Q_INVOKABLE void registerBefore(const QJSValue& func); Q_INVOKABLE void registerAfter(const QJSValue& func); Q_INVOKABLE void registerStop(const QJSValue& func); + Q_INVOKABLE bool onGuiThread(); + Q_INVOKABLE bool onEmulationThread(); }; class QtScriptManager : public QObject diff --git a/src/drivers/Qt/fceuWrapper.cpp b/src/drivers/Qt/fceuWrapper.cpp index 3f9f7272..5dc323b4 100644 --- a/src/drivers/Qt/fceuWrapper.cpp +++ b/src/drivers/Qt/fceuWrapper.cpp @@ -1980,10 +1980,6 @@ FCEUFILE* FCEUD_OpenArchiveIndex(ArchiveScanRecord& asr, std::string &fname, int } DUMMY(FCEUD_HideMenuToggle) DUMMY(FCEUD_MovieReplayFrom) -//DUMMY(FCEUD_AviRecordTo) -//DUMMY(FCEUD_AviStop) -//void FCEUI_AviVideoUpdate(const unsigned char* buffer) { } -//bool FCEUI_AviIsRecording(void) {return false;} void FCEUI_UseInputPreset(int preset) { } bool FCEUD_PauseAfterPlayback() { return pauseAfterPlayback; }