diff --git a/src/drivers/Qt/GuiConf.cpp b/src/drivers/Qt/GuiConf.cpp index 3bc6b7a9..f5fd6526 100644 --- a/src/drivers/Qt/GuiConf.cpp +++ b/src/drivers/Qt/GuiConf.cpp @@ -43,7 +43,7 @@ static int writeQPaletteToFile( const char *path, QPalette *pal ); static int readQPaletteFromFile( const char *path, QPalette *pal ); -static GuiPaletteEditDialog_t *editDialog = NULL; +static GuiPaletteEditDialog_t *editDialog = nullptr; //---------------------------------------------------- GuiConfDialog_t::GuiConfDialog_t(QWidget *parent) : QDialog(parent) @@ -590,7 +590,14 @@ fceuStyle::fceuStyle(QStyle *style) : QProxyStyle(style) { //printf("New Style!!!\n"); - setObjectName( style->objectName() ); + if (style != nullptr) + { + setObjectName( style->objectName() ); + } + else + { + setObjectName("fusion"); + } } fceuStyle::~fceuStyle(void) @@ -609,25 +616,21 @@ fceuStyle::~fceuStyle(void) QStyle *fceuStyle::styleBase(QStyle *style) const { - std::string s; - static QStyle *base; + static QStyle *base = nullptr; +#ifdef WIN32 + //QString defaultStyle("windows"); // fusion is much more stable and consistent. + QString styleName("fusion"); +#else + QString styleName("fusion"); +#endif - if ( g_config != NULL ) + if ( g_config != nullptr ) { - g_config->getOption("SDL.GuiStyle", &s ); + g_config->getOption("SDL.GuiStyle", &styleName ); } - if ( s.size() == 0 ) { int i, idx = -1; -#ifdef WIN32 - //QString defaultStyle("windows"); // fusion is much more stable and consistent. - QString defaultStyle("fusion"); -#elif __APPLE__ - QString defaultStyle("fusion"); -#else - QString defaultStyle("fusion"); -#endif QStringList styleKeys = QStyleFactory::keys(); @@ -635,7 +638,7 @@ QStyle *fceuStyle::styleBase(QStyle *style) const { //printf("Style: '%s'\n", styleKeys[i].toStdString().c_str() ); - if ( defaultStyle.compare( styleKeys[i], Qt::CaseInsensitive ) == 0 ) + if ( styleName.compare( styleKeys[i], Qt::CaseInsensitive ) == 0 ) { //printf("Style Match: %s\n", styleKeys[i].toStdString().c_str() ); idx = i; @@ -645,17 +648,13 @@ QStyle *fceuStyle::styleBase(QStyle *style) const if ( (idx >= 0) && (idx < styleKeys.size()) ) { - s = styleKeys[idx].toStdString(); - } - else - { - s.assign("fusion"); + styleName = styleKeys[idx]; } } - if ( style == NULL ) + if ( style == nullptr ) { - base = QStyleFactory::create(QString::fromStdString(s)); + base = QStyleFactory::create(styleName); } else { @@ -772,7 +771,7 @@ void fceuStyle::polish(QApplication *app) } else { - app->setStyleSheet(NULL); + app->setStyleSheet(nullptr); } } //---------------------------------------------------- @@ -852,7 +851,7 @@ static const char *getRoleText( QPalette::ColorRole role ) rTxt = "NoRole"; break; default: - rTxt = NULL; + rTxt = nullptr; break; } return rTxt; @@ -869,7 +868,7 @@ static int writeQPaletteToFile( const char *path, QPalette *pal ) fp = fopen( path, "w"); - if ( fp == NULL ) + if ( fp == nullptr ) { printf("Error: Failed to Open File for writing: '%s'\n", path ); return -1; @@ -928,7 +927,7 @@ static int readQPaletteFromFile( const char *path, QPalette *pal ) fp = fopen( path, "r"); - if ( fp == NULL ) + if ( fp == nullptr ) { printf("Error: Failed to Open File for writing: '%s'\n", path ); return -1; @@ -987,7 +986,7 @@ static int readQPaletteFromFile( const char *path, QPalette *pal ) continue; } - rTxtMatch = NULL; + rTxtMatch = nullptr; r = QPalette::WindowText; @@ -1006,7 +1005,7 @@ static int readQPaletteFromFile( const char *path, QPalette *pal ) } } - if ( rTxtMatch == NULL ) continue; + if ( rTxtMatch == nullptr ) continue; color = pal->color( g, r ); @@ -1156,7 +1155,7 @@ GuiPaletteEditDialog_t::~GuiPaletteEditDialog_t(void) { if ( editDialog == this ) { - editDialog = NULL; + editDialog = nullptr; } } //---------------------------------------------------- @@ -1328,13 +1327,13 @@ void GuiPaletteColorSelect::setText(void) gTxt = "Inactive"; break; default: - gTxt = NULL; + gTxt = nullptr; break; } rTxt = getRoleText( role ); - if ( (gTxt == NULL) || (rTxt == NULL) ) + if ( (gTxt == nullptr) || (rTxt == nullptr) ) { return; } diff --git a/src/drivers/Qt/GuiConf.h b/src/drivers/Qt/GuiConf.h index 9aa0acbc..0814b754 100644 --- a/src/drivers/Qt/GuiConf.h +++ b/src/drivers/Qt/GuiConf.h @@ -33,8 +33,6 @@ public: ~fceuStyle(void); - QStyle *baseStyle() const; - void polish(QPalette &palette) override; void polish(QApplication *app) override; 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; } diff --git a/src/drivers/common/configSys.cpp b/src/drivers/common/configSys.cpp index 78048384..46270601 100644 --- a/src/drivers/common/configSys.cpp +++ b/src/drivers/common/configSys.cpp @@ -413,6 +413,25 @@ Config::getOption(const std::string &name, return 0; } +#ifdef __QT_DRIVER__ +int +Config::getOption(const std::string &name, + QString *value) const +{ + std::map::const_iterator opt_i; + + // confirm that the option exists + opt_i = _strOptMap.find(name); + if(opt_i == _strOptMap.end()) { + return -1; + } + + // get the option + (*value) = QString::fromStdString(opt_i->second); + return 0; +} +#endif + int Config::getOption(const std::string &name, const char **value) const diff --git a/src/drivers/common/configSys.h b/src/drivers/common/configSys.h index 0e41aa04..5beda053 100644 --- a/src/drivers/common/configSys.h +++ b/src/drivers/common/configSys.h @@ -4,6 +4,10 @@ #include #include +#ifdef __QT_DRIVER__ +#include +#endif + class Config { private: std::string _dir; @@ -66,6 +70,9 @@ public: int setOption(const std::string &, double); int setOption(const std::string &, void (*)(const std::string &)); +#ifdef __QT_DRIVER__ + int getOption(const std::string &, QString *) const; +#endif int getOption(const std::string &, std::string *) const; int getOption(const std::string &, const char **) const; int getOption(const std::string &, int *) const;