JS scripting in work.

This commit is contained in:
harry 2024-01-29 23:17:15 -05:00
parent 813d4c0c4b
commit 6b96016047
3 changed files with 219 additions and 44 deletions

View File

@ -41,6 +41,7 @@
#include "../../fceu.h" #include "../../fceu.h"
#include "../../movie.h" #include "../../movie.h"
#include "../../video.h"
#include "../../x6502.h" #include "../../x6502.h"
#include "../../debug.h" #include "../../debug.h"
@ -55,6 +56,35 @@
#include "Qt/ConsoleUtilities.h" #include "Qt/ConsoleUtilities.h"
#include "Qt/ConsoleWindow.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 //---- EMU Script Object
//---------------------------------------------------- //----------------------------------------------------
@ -196,11 +226,41 @@ bool EmuScriptObject::loadRom(const QString& romPath)
return ret != 0; return ret != 0;
} }
//---------------------------------------------------- //----------------------------------------------------
bool EmuScriptObject::onEmulationThread()
{
bool isEmuThread = (consoleWindow != nullptr) &&
(QThread::currentThread() == consoleWindow->emulatorThread);
return isEmuThread;
}
//----------------------------------------------------
QString EmuScriptObject::getDir() QString EmuScriptObject::getDir()
{ {
return QString(fceuExecutablePath()); 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 //---- Memory Script Object
//---------------------------------------------------- //----------------------------------------------------
//---------------------------------------------------- //----------------------------------------------------
@ -590,6 +650,7 @@ void MemoryScriptObject::unregisterAll()
numWriteFuncsRegistered = 0; numWriteFuncsRegistered = 0;
numExecFuncsRegistered = 0; numExecFuncsRegistered = 0;
} }
} // JS
//---------------------------------------------------- //----------------------------------------------------
//---- Qt Script Instance //---- Qt Script Instance
//---------------------------------------------------- //----------------------------------------------------
@ -598,47 +659,64 @@ QtScriptInstance::QtScriptInstance(QObject* parent)
{ {
QScriptDialog_t* win = qobject_cast<QScriptDialog_t*>(parent); QScriptDialog_t* win = qobject_cast<QScriptDialog_t*>(parent);
emu = new EmuScriptObject(this);
mem = new MemoryScriptObject(this);
if (win != nullptr) if (win != nullptr)
{ {
dialog = win; dialog = win;
emu->setDialog(dialog);
mem->setDialog(dialog);
} }
engine = new QJSEngine(nullptr);
emu->setEngine(engine); initEngine();
mem->setEngine(engine);
configEngine();
QtScriptManager::getInstance()->addScriptInstance(this); QtScriptManager::getInstance()->addScriptInstance(this);
} }
//---------------------------------------------------- //----------------------------------------------------
QtScriptInstance::~QtScriptInstance() QtScriptInstance::~QtScriptInstance()
{ {
if (engine != nullptr)
{
engine->deleteLater();
engine = nullptr;
}
QtScriptManager::getInstance()->removeScriptInstance(this); QtScriptManager::getInstance()->removeScriptInstance(this);
shutdownEngine();
//printf("QtScriptInstance Destroyed\n"); //printf("QtScriptInstance Destroyed\n");
} }
//---------------------------------------------------- //----------------------------------------------------
void QtScriptInstance::resetEngine() void QtScriptInstance::shutdownEngine()
{ {
running = false; 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) if (engine != nullptr)
{ {
engine->deleteLater(); engine->collectGarbage();
//engine->deleteLater();
delete engine;
engine = nullptr; 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) if (ui_rootWidget != nullptr)
{ {
@ -646,15 +724,29 @@ void QtScriptInstance::resetEngine()
ui_rootWidget->deleteLater(); ui_rootWidget->deleteLater();
ui_rootWidget = nullptr; 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); engine->installExtensions(QJSEngine::ConsoleExtension);
emu->setEngine(engine);
mem->setEngine(engine);
QJSValue emuObject = engine->newQObject(emu); QJSValue emuObject = engine->newQObject(emu);
engine->globalObject().setProperty("emu", emuObject); engine->globalObject().setProperty("emu", emuObject);
@ -667,9 +759,9 @@ int QtScriptInstance::configEngine()
engine->globalObject().setProperty("gui", guiObject); engine->globalObject().setProperty("gui", guiObject);
onFrameBeginCallback = QJSValue(); // Class Type Definitions for Script Use
onFrameFinishCallback = QJSValue(); QJSValue jsMetaObject = engine->newQMetaObject(&JS::ColorScriptObject::staticMetaObject);
onScriptStopCallback = QJSValue(); engine->globalObject().setProperty("Color", jsMetaObject);
return 0; return 0;
} }
@ -757,17 +849,29 @@ void QtScriptInstance::loadUI(const QString& uiFilePath)
//---------------------------------------------------- //----------------------------------------------------
void QtScriptInstance::registerBefore(const QJSValue& func) void QtScriptInstance::registerBefore(const QJSValue& func)
{ {
onFrameBeginCallback = func; if (onFrameBeginCallback != nullptr)
{
delete onFrameBeginCallback;
}
onFrameBeginCallback = new QJSValue(func);
} }
//---------------------------------------------------- //----------------------------------------------------
void QtScriptInstance::registerAfter(const 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) void QtScriptInstance::registerStop(const QJSValue& func)
{ {
onScriptStopCallback = func; if (onScriptStopCallback != nullptr)
{
delete onScriptStopCallback;
}
onScriptStopCallback = new QJSValue(func);
} }
//---------------------------------------------------- //----------------------------------------------------
void QtScriptInstance::print(const QString& msg) void QtScriptInstance::print(const QString& msg)
@ -776,6 +880,23 @@ void QtScriptInstance::print(const QString& msg)
{ {
dialog->logOutput(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) int QtScriptInstance::throwError(QJSValue::ErrorType errorType, const QString &message)
@ -844,9 +965,14 @@ void QtScriptInstance::stopRunning()
FCEU_WRAPPER_LOCK(); FCEU_WRAPPER_LOCK();
if (running) if (running)
{ {
if (onScriptStopCallback.isCallable()) if (onScriptStopCallback != nullptr && onScriptStopCallback->isCallable())
{ {
onScriptStopCallback.call(); QJSValue callResult = onScriptStopCallback->call();
if (callResult.isError())
{
print(callResult.toString());
}
} }
running = false; running = false;
@ -857,17 +983,29 @@ void QtScriptInstance::stopRunning()
//---------------------------------------------------- //----------------------------------------------------
void QtScriptInstance::onFrameBegin() 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() 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(); periodicTimer->stop();
clearPropertyTree();
scriptInstance->stopRunning(); scriptInstance->stopRunning();
delete scriptInstance;
settings.setValue("QScriptWindow/geometry", saveGeometry()); settings.setValue("QScriptWindow/geometry", saveGeometry());
} }

View File

@ -8,6 +8,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdarg.h> #include <stdarg.h>
#include <QColor>
#include <QWidget> #include <QWidget>
#include <QDialog> #include <QDialog>
#include <QTabWidget> #include <QTabWidget>
@ -32,6 +33,36 @@
class QScriptDialog_t; class QScriptDialog_t;
class QtScriptInstance; 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 class EmuScriptObject: public QObject
{ {
Q_OBJECT Q_OBJECT
@ -65,7 +96,9 @@ public slots:
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);
Q_INVOKABLE bool onEmulationThread();
Q_INVOKABLE QString getDir(); 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 unregisterExec(const QJSValue& func, int address, int size = 1);
Q_INVOKABLE void unregisterAll(); Q_INVOKABLE void unregisterAll();
}; };
} // JS
class QtScriptInstance : public QObject class QtScriptInstance : public QObject
{ {
@ -146,21 +180,23 @@ public:
int throwError(QJSValue::ErrorType errorType, const QString &message = QString()); int throwError(QJSValue::ErrorType errorType, const QString &message = QString());
QObject* getObjectParent();
QJSEngine* getEngine(){ return engine; }; QJSEngine* getEngine(){ return engine; };
private: private:
int configEngine(); int initEngine();
void shutdownEngine();
void printSymbols(QJSValue& val, int iter = 0); void printSymbols(QJSValue& val, int iter = 0);
void loadObjectChildren(QJSValue& jsObject, QObject* obj); void loadObjectChildren(QJSValue& jsObject, QObject* obj);
QJSEngine* engine = nullptr; QJSEngine* engine = nullptr;
QScriptDialog_t* dialog = nullptr; QScriptDialog_t* dialog = nullptr;
EmuScriptObject* emu = nullptr; JS::EmuScriptObject* emu = nullptr;
MemoryScriptObject* mem = nullptr; JS::MemoryScriptObject* mem = nullptr;
QWidget* ui_rootWidget = nullptr; QWidget* ui_rootWidget = nullptr;
QJSValue onFrameBeginCallback; QJSValue *onFrameBeginCallback = nullptr;
QJSValue onFrameFinishCallback; QJSValue *onFrameFinishCallback = nullptr;
QJSValue onScriptStopCallback; QJSValue *onScriptStopCallback = nullptr;
bool running = false; bool running = false;
public slots: public slots:
@ -170,6 +206,8 @@ public slots:
Q_INVOKABLE void registerBefore(const QJSValue& func); Q_INVOKABLE void registerBefore(const QJSValue& func);
Q_INVOKABLE void registerAfter(const QJSValue& func); Q_INVOKABLE void registerAfter(const QJSValue& func);
Q_INVOKABLE void registerStop(const QJSValue& func); Q_INVOKABLE void registerStop(const QJSValue& func);
Q_INVOKABLE bool onGuiThread();
Q_INVOKABLE bool onEmulationThread();
}; };
class QtScriptManager : public QObject class QtScriptManager : public QObject

View File

@ -1980,10 +1980,6 @@ FCEUFILE* FCEUD_OpenArchiveIndex(ArchiveScanRecord& asr, std::string &fname, int
} }
DUMMY(FCEUD_HideMenuToggle) DUMMY(FCEUD_HideMenuToggle)
DUMMY(FCEUD_MovieReplayFrom) 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) { } void FCEUI_UseInputPreset(int preset) { }
bool FCEUD_PauseAfterPlayback() { return pauseAfterPlayback; } bool FCEUD_PauseAfterPlayback() { return pauseAfterPlayback; }