Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ClusterM/fceux.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAutomerge Bot <bot@example.com>2024-01-15 03:28:52 +0300
committerAutomerge Bot <bot@example.com>2024-01-15 03:28:52 +0300
commitee59115ac9f4a974d2addf952e824cf4c81f4350 (patch)
tree67b7e1a6db72a6f63b66af7ce4fe567eb8d37fb2
parentde916bbd112ea333f85c02e42b0748c93664de22 (diff)
parent3436e221dec1fffbaa835a9107efa01ae79f3406 (diff)
Merge branch 'master' of https://github.com/TASVideos/fceux into cluster
-rw-r--r--src/CMakeLists.txt82
-rw-r--r--src/drivers/Qt/ConsoleWindow.cpp80
-rw-r--r--src/drivers/Qt/ConsoleWindow.h2
-rw-r--r--src/drivers/Qt/FrameTimingStats.cpp18
-rw-r--r--src/drivers/Qt/FrameTimingStats.h1
-rw-r--r--src/drivers/Qt/QtScriptManager.cpp653
-rw-r--r--src/drivers/Qt/QtScriptManager.h144
-rw-r--r--src/drivers/Qt/config.cpp1
-rw-r--r--src/drivers/Qt/sdl-throttle.cpp39
-rw-r--r--src/drivers/Qt/throttle.h9
10 files changed, 994 insertions, 35 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 12448edd..5b8bd59a 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -10,34 +10,80 @@ if (${PUBLIC_RELEASE})
endif()
if ( ${QT6} )
- message( STATUS "GUI Frontend: Qt6")
- set( Qt Qt6 )
-else()
- message( STATUS "GUI Frontend: Qt5")
- set( Qt Qt5 )
+ set( QT 6 )
endif()
-if ( ${QHELP} )
- set(QtHelpModule Help)
- add_definitions( -D_USE_QHELP )
+if (NOT DEFINED QT)
+ message( STATUS "Attempting to determine Qt Version...")
+ find_package( Qt6 COMPONENTS Core)
+
+ if (${Qt6Core_FOUND})
+ message( STATUS "Found Qt Version: ${Qt6Core_VERSION}")
+ set( QT 6 )
+ else()
+ find_package( Qt5 COMPONENTS Core)
+
+ if (${Qt5Core_FOUND})
+ message( STATUS "Found Qt Version: ${Qt5Core_VERSION}")
+ set( QT 5 )
+ endif()
+ endif()
endif()
if ( ${FCEU_PROFILER_ENABLE} )
message( STATUS "FCEU Profiler Enabled")
add_definitions( -D__FCEU_PROFILER_ENABLE__ )
endif()
-
-if ( ${QT6} )
- find_package( Qt6 REQUIRED COMPONENTS Widgets OpenGL OpenGLWidgets ${QtHelpModule})
- add_definitions( ${Qt6Widgets_DEFINITIONS} ${Qt6Help_DEFINITIONS} ${Qt6OpenGLWidgets_DEFINITIONS} )
- include_directories( ${Qt6Widgets_INCLUDE_DIRS} ${Qt6Help_INCLUDE_DIRS} ${Qt6OpenGLWidgets_INCLUDE_DIRS} )
+if ( ${QT} EQUAL 6 )
+ message( STATUS "GUI Frontend: Qt6")
+ set( Qt Qt6 )
+ find_package( Qt6 REQUIRED COMPONENTS Widgets OpenGL OpenGLWidgets)
+ find_package( Qt6 COMPONENTS Help QUIET)
+ find_package( Qt6 COMPONENTS Qml)
+ add_definitions( ${Qt6Widgets_DEFINITIONS} ${Qt6Qml_DEFINITIONS} ${Qt6Help_DEFINITIONS} ${Qt6OpenGLWidgets_DEFINITIONS} )
+ include_directories( ${Qt6Widgets_INCLUDE_DIRS} ${Qt6Qml_INCLUDE_DIRS} ${Qt6Help_INCLUDE_DIRS} ${Qt6OpenGLWidgets_INCLUDE_DIRS} )
+
+ if (${Qt6Help_FOUND})
+ message( STATUS "Qt6 Help Module Found")
+ if (${QHELP})
+ add_definitions( -D_USE_QHELP )
+ endif()
+ else()
+ message( STATUS "Qt6 Help Module Not Found")
+ endif()
+
+ if (${Qt6Qml_FOUND})
+ message( STATUS "Qt6 Qml Module Found")
+ #add_definitions( -D__FCEU_QSCRIPT_ENABLE__ )
+ else()
+ message( STATUS "Qt6 Qml Module Not Found")
+ endif()
else()
- find_package( Qt5 REQUIRED COMPONENTS Widgets OpenGL ${QtHelpModule})
- add_definitions( ${Qt5Widgets_DEFINITIONS} ${Qt5Help_DEFINITIONS} )
- include_directories( ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Help_INCLUDE_DIRS} )
-endif()
+ message( STATUS "GUI Frontend: Qt5")
+ set( Qt Qt5 )
+ find_package( Qt5 REQUIRED COMPONENTS Widgets OpenGL)
+ find_package( Qt5 COMPONENTS Help QUIET)
+ find_package( Qt5 COMPONENTS Qml)
+ add_definitions( ${Qt5Widgets_DEFINITIONS} ${Qt5Qml_DEFINITIONS} ${Qt5Help_DEFINITIONS} )
+ include_directories( ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Qml_INCLUDE_DIRS} ${Qt5Help_INCLUDE_DIRS} )
+
+ if (${Qt5Help_FOUND})
+ message( STATUS "Qt5 Help Module Found")
+ if (${QHELP})
+ add_definitions( -D_USE_QHELP )
+ endif()
+ else()
+ message( STATUS "Qt5 Help Module Not Found")
+ endif()
+ if (${Qt5Qml_FOUND})
+ message( STATUS "Qt5 Qml Module Found")
+ add_definitions( -D__FCEU_QSCRIPT_ENABLE__ )
+ else()
+ message( STATUS "Qt5 Qml Module Not Found")
+ endif()
+endif()
if(WIN32)
find_package(OpenGL REQUIRED)
@@ -580,6 +626,7 @@ set(SRC_DRIVERS_SDL
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleSoundConf.cpp
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/StateRecorderConf.cpp
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/iNesHeaderEditor.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/QtScriptManager.cpp
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/SplashScreen.cpp
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/TraceLogger.cpp
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/AboutWindow.cpp
@@ -667,6 +714,7 @@ target_link_libraries( ${APP_NAME}
${ASAN_LDFLAGS} ${GPROF_LDFLAGS}
${${Qt}Widgets_LIBRARIES}
${${Qt}Help_LIBRARIES}
+ ${${Qt}Qml_LIBRARIES}
${${Qt}OpenGL_LIBRARIES}
${${Qt}OpenGLWidgets_LIBRARIES}
${OPENGL_LDFLAGS}
diff --git a/src/drivers/Qt/ConsoleWindow.cpp b/src/drivers/Qt/ConsoleWindow.cpp
index 2b4ee2ec..b7030da2 100644
--- a/src/drivers/Qt/ConsoleWindow.cpp
+++ b/src/drivers/Qt/ConsoleWindow.cpp
@@ -67,6 +67,7 @@
#include "Qt/main.h"
#include "Qt/dface.h"
#include "Qt/input.h"
+#include "Qt/throttle.h"
#include "Qt/ColorMenu.h"
#include "Qt/ConsoleWindow.h"
#include "Qt/InputConf.h"
@@ -86,6 +87,7 @@
#include "Qt/TimingConf.h"
#include "Qt/FrameTimingStats.h"
#include "Qt/LuaControl.h"
+#include "Qt/QtScriptManager.h"
#include "Qt/CheatsConf.h"
#include "Qt/GameGenie.h"
#include "Qt/HexEditor.h"
@@ -270,6 +272,9 @@ 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;
}
@@ -1079,7 +1084,7 @@ void consoleWin_t::createMainMenu(void)
connect( Hotkeys[ HK_SELECT_STATE_NEXT ].getShortcut(), SIGNAL(activated()), this, SLOT(incrementState(void)) );
#ifdef _S9XLUA_H
- // File -> Quick Save
+ // File -> Load Lua
loadLuaAct = new QAction(tr("Load &Lua Script"), this);
//loadLuaAct->setShortcut( QKeySequence(tr("F5")));
loadLuaAct->setStatusTip(tr("Load Lua Script"));
@@ -1090,7 +1095,20 @@ void consoleWin_t::createMainMenu(void)
fileMenu->addSeparator();
#else
- loadLuaAct = NULL;
+ loadLuaAct = nullptr;
+#endif
+
+#ifdef _S9XLUA_H
+ // File -> Load QScript
+ loadJsAct = new QAction(tr("Load &Qt Script"), this);
+ loadJsAct->setStatusTip(tr("Load Qt Script"));
+ connect(loadJsAct, SIGNAL(triggered()), this, SLOT(loadJs(void)) );
+
+ fileMenu->addAction(loadJsAct);
+
+ fileMenu->addSeparator();
+#else
+ loadJsAct = NULL;
#endif
// File -> Screenshot
@@ -2981,6 +2999,19 @@ void consoleWin_t::loadLua(void)
#endif
}
+void consoleWin_t::loadJs(void)
+{
+#ifdef __FCEU_QSCRIPT_ENABLE__
+ QScriptDialog_t *jsCtrlWin;
+
+ //printf("Open JS Control Window\n");
+
+ jsCtrlWin = new QScriptDialog_t(this);
+
+ jsCtrlWin->show();
+#endif
+}
+
void consoleWin_t::openInputConfWin(void)
{
//printf("Open Input Config Window\n");
@@ -4546,21 +4577,30 @@ void consoleWin_t::emuFrameFinish(void)
{
static bool eventProcessingInProg = false;
- if ( eventProcessingInProg )
- { // Prevent recursion as processEvents function can double back on us
- return;
- }
- eventProcessingInProg = true;
- // Process all events before attempting to render viewport
- QCoreApplication::processEvents();
+ guiSignalRecvMark();
- eventProcessingInProg = false;
+ //if ( eventProcessingInProg )
+ //{ // Prevent recursion as processEvents function can double back on us
+ // return;
+ //}
+ // Prevent recursion as processEvents function can double back on us
+ if ( !eventProcessingInProg )
+ {
+ eventProcessingInProg = true;
+ // Process all events before attempting to render viewport
+ QCoreApplication::processEvents();
+ eventProcessingInProg = false;
+ }
// Update Input Devices
FCEUD_UpdateInput();
//printf("EMU Frame Finish\n");
+#ifdef __FCEU_QSCRIPT_ENABLE__
+ QtScriptManager::getInstance()->frameFinishedUpdate();
+#endif
+
transferVideoBuffer();
}
@@ -4569,15 +4609,18 @@ void consoleWin_t::updatePeriodic(void)
FCEU_PROFILE_FUNC(prof, "updatePeriodic");
static bool eventProcessingInProg = false;
- if ( eventProcessingInProg )
- { // Prevent recursion as processEvents function can double back on us
- return;
+ //if ( eventProcessingInProg )
+ //{ // Prevent recursion as processEvents function can double back on us
+ // return;
+ //}
+ // Prevent recursion as processEvents function can double back on us
+ if ( !eventProcessingInProg )
+ {
+ eventProcessingInProg = true;
+ // Process all events before attempting to render viewport
+ QCoreApplication::processEvents();
+ eventProcessingInProg = false;
}
- eventProcessingInProg = true;
- // Process all events before attempting to render viewport
- QCoreApplication::processEvents();
-
- eventProcessingInProg = false;
// Update Input Devices
FCEUD_UpdateInput();
@@ -4863,6 +4906,7 @@ void emulatorThread_t::run(void)
void emulatorThread_t::signalFrameFinished(void)
{
+ emuSignalSendMark();
emit frameFinished();
}
diff --git a/src/drivers/Qt/ConsoleWindow.h b/src/drivers/Qt/ConsoleWindow.h
index 1fc69749..d0fe59fe 100644
--- a/src/drivers/Qt/ConsoleWindow.h
+++ b/src/drivers/Qt/ConsoleWindow.h
@@ -206,6 +206,7 @@ class consoleWin_t : public QMainWindow
QAction *quickLoadAct;
QAction *quickSaveAct;
QAction *loadLuaAct;
+ QAction *loadJsAct;
QAction *scrShotAct;
QAction *quitAct;
QAction *inputConfig;
@@ -372,6 +373,7 @@ class consoleWin_t : public QMainWindow
void incrementState(void);
void decrementState(void);
void loadLua(void);
+ void loadJs(void);
void takeScreenShot(void);
void prepareScreenShot(void);
void powerConsoleCB(void);
diff --git a/src/drivers/Qt/FrameTimingStats.cpp b/src/drivers/Qt/FrameTimingStats.cpp
index 35878850..e2551da8 100644
--- a/src/drivers/Qt/FrameTimingStats.cpp
+++ b/src/drivers/Qt/FrameTimingStats.cpp
@@ -89,6 +89,7 @@ FrameTimingDialog_t::FrameTimingDialog_t(QWidget *parent)
frameTimeIdlePct = new QTreeWidgetItem();
frameLateCount = new QTreeWidgetItem();
videoTimeAbs = new QTreeWidgetItem();
+ emuSignalDelay = new QTreeWidgetItem();
tree->addTopLevelItem(frameTimeAbs);
tree->addTopLevelItem(frameTimeDel);
@@ -97,6 +98,7 @@ FrameTimingDialog_t::FrameTimingDialog_t(QWidget *parent)
tree->addTopLevelItem(frameTimeWorkPct);
tree->addTopLevelItem(frameTimeIdlePct);
tree->addTopLevelItem(videoTimeAbs);
+ tree->addTopLevelItem(emuSignalDelay);
tree->addTopLevelItem(frameLateCount);
frameTimeAbs->setFlags(Qt::ItemIsEnabled | Qt::ItemNeverHasChildren);
@@ -109,6 +111,7 @@ FrameTimingDialog_t::FrameTimingDialog_t(QWidget *parent)
frameTimeWorkPct->setText(0, tr("Frame Work %"));
frameTimeIdlePct->setText(0, tr("Frame Idle %"));
frameLateCount->setText(0, tr("Frame Late Count"));
+ emuSignalDelay->setText(0, tr("EMU Signal Delay ms"));
videoTimeAbs->setText(0, tr("Video Period ms"));
frameTimeAbs->setTextAlignment(0, Qt::AlignLeft);
@@ -119,6 +122,7 @@ FrameTimingDialog_t::FrameTimingDialog_t(QWidget *parent)
frameTimeIdlePct->setTextAlignment(0, Qt::AlignLeft);
frameLateCount->setTextAlignment(0, Qt::AlignLeft);
videoTimeAbs->setTextAlignment(0, Qt::AlignLeft);
+ emuSignalDelay->setTextAlignment(0, Qt::AlignLeft);
for (int i = 0; i < 4; i++)
{
@@ -130,6 +134,7 @@ FrameTimingDialog_t::FrameTimingDialog_t(QWidget *parent)
frameTimeIdlePct->setTextAlignment(i + 1, Qt::AlignCenter);
frameLateCount->setTextAlignment(i + 1, Qt::AlignCenter);
videoTimeAbs->setTextAlignment(i + 1, Qt::AlignCenter);
+ emuSignalDelay->setTextAlignment(i + 1, Qt::AlignCenter);
}
hbox = new QHBoxLayout();
@@ -294,6 +299,19 @@ void FrameTimingDialog_t::updateTimingStats(void)
sprintf(stmp, "%.3f", stats.videoTimeDel.max * 1e3);
videoTimeAbs->setText(4, tr(stmp));
+ // Emulator to GUI Thread Signal Delay
+ sprintf(stmp, "%.3f", stats.emuSignalDelay.tgt * 1e3);
+ emuSignalDelay->setText(1, tr(stmp));
+
+ sprintf(stmp, "%.3f", stats.emuSignalDelay.cur * 1e3);
+ emuSignalDelay->setText(2, tr(stmp));
+
+ sprintf(stmp, "%.3f", stats.emuSignalDelay.min * 1e3);
+ emuSignalDelay->setText(3, tr(stmp));
+
+ sprintf(stmp, "%.3f", stats.emuSignalDelay.max * 1e3);
+ emuSignalDelay->setText(4, tr(stmp));
+
// Late Count
sprintf(stmp, "%u", stats.lateCount);
frameLateCount->setText(1, tr("0"));
diff --git a/src/drivers/Qt/FrameTimingStats.h b/src/drivers/Qt/FrameTimingStats.h
index 4131430e..79cae941 100644
--- a/src/drivers/Qt/FrameTimingStats.h
+++ b/src/drivers/Qt/FrameTimingStats.h
@@ -40,6 +40,7 @@ protected:
QTreeWidgetItem *frameTimeIdlePct;
QTreeWidgetItem *frameLateCount;
QTreeWidgetItem *videoTimeAbs;
+ QTreeWidgetItem *emuSignalDelay;
QGroupBox *statFrame;
QTreeWidget *tree;
diff --git a/src/drivers/Qt/QtScriptManager.cpp b/src/drivers/Qt/QtScriptManager.cpp
new file mode 100644
index 00000000..888edcea
--- /dev/null
+++ b/src/drivers/Qt/QtScriptManager.cpp
@@ -0,0 +1,653 @@
+/* FCE Ultra - NES/Famicom Emulator
+ *
+ * Copyright notice for this file:
+ * Copyright (C) 2020 thor
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+// QtScriptManager.cpp
+//
+#ifdef __FCEU_QSCRIPT_ENABLE__
+#include <stdio.h>
+#include <string.h>
+#include <list>
+
+#ifdef WIN32
+#include <Windows.h>
+#endif
+
+#include <QTextEdit>
+#include <QFileDialog>
+#include <QMessageBox>
+#include <QSettings>
+#include <QJSValueIterator>
+
+#include "../../fceu.h"
+#include "../../movie.h"
+
+#include "common/os_utils.h"
+
+#include "Qt/QtScriptManager.h"
+#include "Qt/main.h"
+#include "Qt/input.h"
+#include "Qt/config.h"
+#include "Qt/keyscan.h"
+#include "Qt/fceuWrapper.h"
+#include "Qt/ConsoleUtilities.h"
+#include "Qt/ConsoleWindow.h"
+
+//----------------------------------------------------
+//---- EMU Script Object
+//----------------------------------------------------
+EmuScriptObject::EmuScriptObject(QObject* parent)
+ : QObject(parent)
+{
+}
+//----------------------------------------------------
+EmuScriptObject::~EmuScriptObject()
+{
+}
+//----------------------------------------------------
+void EmuScriptObject::print(const QString& msg)
+{
+ if (dialog != nullptr)
+ {
+ dialog->logOutput(msg);
+ }
+}
+//----------------------------------------------------
+void EmuScriptObject::softreset()
+{
+ fceuWrapperSoftReset();
+}
+//----------------------------------------------------
+void EmuScriptObject::pause()
+{
+ FCEUI_SetEmulationPaused( EMULATIONPAUSED_PAUSED );
+}
+//----------------------------------------------------
+void EmuScriptObject::unpause()
+{
+ FCEUI_SetEmulationPaused(0);
+}
+//----------------------------------------------------
+//---- Qt Script Instance
+//----------------------------------------------------
+QtScriptInstance::QtScriptInstance(QObject* parent)
+ : QObject(parent)
+{
+ QScriptDialog_t* win = qobject_cast<QScriptDialog_t*>(parent);
+
+ emu = new EmuScriptObject(this);
+
+ if (win != nullptr)
+ {
+ dialog = win;
+ emu->setDialog(dialog);
+ }
+ engine = new QJSEngine(this);
+
+ configEngine();
+
+ QtScriptManager::getInstance()->addScriptInstance(this);
+}
+//----------------------------------------------------
+QtScriptInstance::~QtScriptInstance()
+{
+ if (engine != nullptr)
+ {
+ engine->deleteLater();
+ engine = nullptr;
+ }
+ QtScriptManager::getInstance()->removeScriptInstance(this);
+
+ //printf("QtScriptInstance Destroyed\n");
+}
+//----------------------------------------------------
+void QtScriptInstance::resetEngine()
+{
+ if (engine != nullptr)
+ {
+ engine->deleteLater();
+ engine = nullptr;
+ }
+ engine = new QJSEngine(this);
+
+ configEngine();
+}
+//----------------------------------------------------
+int QtScriptInstance::configEngine()
+{
+ engine->installExtensions(QJSEngine::ConsoleExtension);
+
+ QJSValue emuObject = engine->newQObject(emu);
+
+ engine->globalObject().setProperty("emu", emuObject);
+
+ onFrameFinishCallback = QJSValue();
+
+ return 0;
+}
+//----------------------------------------------------
+int QtScriptInstance::loadScriptFile( QString filepath )
+{
+ QFile scriptFile(filepath);
+
+ if (!scriptFile.open(QIODevice::ReadOnly))
+ {
+ return -1;
+ }
+ QTextStream stream(&scriptFile);
+ QString fileText = stream.readAll();
+ scriptFile.close();
+
+ FCEU_WRAPPER_LOCK();
+ QJSValue evalResult = engine->evaluate(fileText, filepath);
+ FCEU_WRAPPER_UNLOCK();
+
+ if (evalResult.isError())
+ {
+ print(evalResult.toString());
+ return -1;
+ }
+ else
+ {
+ //printf("Script Evaluation Success!\n");
+ }
+ onFrameFinishCallback = engine->globalObject().property("onFrameFinish");
+
+ return 0;
+}
+//----------------------------------------------------
+void QtScriptInstance::print(const QString& msg)
+{
+ if (dialog)
+ {
+ dialog->logOutput(msg);
+ }
+}
+//----------------------------------------------------
+void QtScriptInstance::printSymbols(QJSValue& val, int iter)
+{
+ int i=0;
+ if (iter > 10)
+ {
+ return;
+ }
+ QJSValueIterator it(val);
+ while (it.hasNext())
+ {
+ it.next();
+ QJSValue child = it.value();
+ qDebug() << iter << ":" << i << " " << it.name() << ": " << child.toString();
+
+ bool isPrototype = it.name() == "prototype";
+
+ if (!isPrototype)
+ {
+ printSymbols(child, iter + 1);
+ }
+ i++;
+ }
+}
+//----------------------------------------------------
+int QtScriptInstance::call(const QString& funcName, const QJSValueList& args)
+{
+ if (engine == nullptr)
+ {
+ return -1;
+ }
+ if (!engine->globalObject().hasProperty(funcName))
+ {
+ print(QString("No function exists: ") + funcName);
+ return -1;
+ }
+ QJSValue func = engine->globalObject().property(funcName);
+
+ FCEU_WRAPPER_LOCK();
+ QJSValue callResult = func.call(args);
+ FCEU_WRAPPER_UNLOCK();
+
+ if (callResult.isError())
+ {
+ print(callResult.toString());
+ }
+ else
+ {
+ //printf("Script Call Success!\n");
+ }
+
+ QJSValue global = engine->globalObject();
+
+ printSymbols( global );
+
+ return 0;
+}
+//----------------------------------------------------
+void QtScriptInstance::onFrameFinish()
+{
+ if (onFrameFinishCallback.isCallable())
+ {
+ onFrameFinishCallback.call();
+ }
+}
+//----------------------------------------------------
+bool QtScriptInstance::isRunning()
+{
+ return false;
+}
+//----------------------------------------------------
+//---- Qt Script Manager
+//----------------------------------------------------
+QtScriptManager* QtScriptManager::_instance = nullptr;
+
+QtScriptManager::QtScriptManager(QObject* parent)
+ : QObject(parent)
+{
+ _instance = this;
+}
+//----------------------------------------------------
+QtScriptManager::~QtScriptManager()
+{
+ _instance = nullptr;
+ //printf("QtScriptManager destroyed\n");
+}
+//----------------------------------------------------
+QtScriptManager* QtScriptManager::create(QObject* parent)
+{
+ QtScriptManager* mgr = new QtScriptManager(parent);
+
+ //printf("QtScriptManager created\n");
+
+ return mgr;
+}
+//----------------------------------------------------
+void QtScriptManager::addScriptInstance(QtScriptInstance* script)
+{
+ scriptList.push_back(script);
+}
+//----------------------------------------------------
+void QtScriptManager::removeScriptInstance(QtScriptInstance* script)
+{
+ auto it = scriptList.begin();
+
+ while (it != scriptList.end())
+ {
+ if (*it == script)
+ {
+ it = scriptList.erase(it);
+ }
+ else
+ {
+ it++;
+ }
+ }
+}
+//----------------------------------------------------
+void QtScriptManager::frameFinishedUpdate()
+{
+ FCEU_WRAPPER_LOCK();
+ for (auto script : scriptList)
+ {
+ script->onFrameFinish();
+ }
+ FCEU_WRAPPER_UNLOCK();
+}
+//----------------------------------------------------
+//---- Qt Script Dialog Window
+//----------------------------------------------------
+QScriptDialog_t::QScriptDialog_t(QWidget *parent)
+ : QDialog(parent, Qt::Window)
+{
+ QVBoxLayout *mainLayout;
+ QHBoxLayout *hbox;
+ QPushButton *closeButton;
+ QLabel *lbl;
+ std::string filename;
+ QSettings settings;
+
+ resize(512, 512);
+
+ setWindowTitle(tr("Qt Java Script Control"));
+
+ mainLayout = new QVBoxLayout();
+
+ lbl = new QLabel(tr("Script File:"));
+
+ scriptPath = new QLineEdit();
+ scriptArgs = new QLineEdit();
+
+ g_config->getOption("SDL.LastLoadJs", &filename);
+
+ scriptPath->setText( tr(filename.c_str()) );
+ scriptPath->setClearButtonEnabled(true);
+ scriptArgs->setClearButtonEnabled(true);
+
+ jsOutput = new QTextEdit();
+ jsOutput->setReadOnly(true);
+
+ hbox = new QHBoxLayout();
+
+ browseButton = new QPushButton(tr("Browse"));
+ stopButton = new QPushButton(tr("Stop"));
+
+ scriptInstance = new QtScriptInstance(this);
+
+ if (scriptInstance->isRunning())
+ {
+ startButton = new QPushButton(tr("Restart"));
+ }
+ else
+ {
+ startButton = new QPushButton(tr("Start"));
+ }
+
+ stopButton->setEnabled(scriptInstance->isRunning());
+
+ connect(browseButton, SIGNAL(clicked()), this, SLOT(openScriptFile(void)));
+ connect(stopButton, SIGNAL(clicked()), this, SLOT(stopScript(void)));
+ connect(startButton, SIGNAL(clicked()), this, SLOT(startScript(void)));
+
+ hbox->addWidget(browseButton);
+ hbox->addWidget(stopButton);
+ hbox->addWidget(startButton);
+
+ mainLayout->addWidget(lbl);
+ mainLayout->addWidget(scriptPath);
+ mainLayout->addLayout(hbox);
+
+ hbox = new QHBoxLayout();
+ lbl = new QLabel(tr("Arguments:"));
+
+ hbox->addWidget(lbl);
+ hbox->addWidget(scriptArgs);
+
+ mainLayout->addLayout(hbox);
+
+ lbl = new QLabel(tr("Output Console:"));
+ mainLayout->addWidget(lbl);
+ mainLayout->addWidget(jsOutput);
+
+ closeButton = new QPushButton( tr("Close") );
+ closeButton->setIcon(style()->standardIcon(QStyle::SP_DialogCloseButton));
+ connect(closeButton, SIGNAL(clicked(void)), this, SLOT(closeWindow(void)));
+
+ hbox = new QHBoxLayout();
+ hbox->addStretch(5);
+ hbox->addWidget( closeButton, 1 );
+ mainLayout->addLayout( hbox );
+
+ setLayout(mainLayout);
+
+ //winList.push_back(this);
+
+ periodicTimer = new QTimer(this);
+
+ connect(periodicTimer, &QTimer::timeout, this, &QScriptDialog_t::updatePeriodic);
+
+ periodicTimer->start(200); // 5hz
+
+ restoreGeometry(settings.value("QScriptWindow/geometry").toByteArray());
+}
+
+//----------------------------------------------------
+QScriptDialog_t::~QScriptDialog_t(void)
+{
+ QSettings settings;
+ std::list<QScriptDialog_t *>::iterator it;
+
+ //printf("Destroy JS Control Window\n");
+
+ periodicTimer->stop();
+
+ //for (it = winList.begin(); it != winList.end(); it++)
+ //{
+ // if ((*it) == this)
+ // {
+ // winList.erase(it);
+ // //printf("Removing JS Window\n");
+ // break;
+ // }
+ //}
+ settings.setValue("QScriptWindow/geometry", saveGeometry());
+}
+//----------------------------------------------------
+void QScriptDialog_t::closeEvent(QCloseEvent *event)
+{
+ //printf("JS Control Close Window Event\n");
+ done(0);
+ deleteLater();
+ event->accept();
+}
+//----------------------------------------------------
+void QScriptDialog_t::closeWindow(void)
+{
+ //printf("JS Control Close Window\n");
+ done(0);
+ deleteLater();
+}
+//----------------------------------------------------
+void QScriptDialog_t::updatePeriodic(void)
+{
+ // TODO
+ //printf("Update JS\n");
+ //if (updateJSDisplay)
+ //{
+ // updateJSWindows();
+ // updateJSDisplay = false;
+ //}
+}
+//----------------------------------------------------
+void QScriptDialog_t::openJSKillMessageBox(void)
+{
+ int ret;
+ QMessageBox msgBox(this);
+
+ msgBox.setIcon(QMessageBox::Warning);
+ msgBox.setText(tr("The JS script running has been running a long time.\nIt may have gone crazy. Kill it? (I won't ask again if you say No)\n"));
+ msgBox.setStandardButtons(QMessageBox::Yes);
+ msgBox.addButton(QMessageBox::No);
+ msgBox.setDefaultButton(QMessageBox::No);
+
+ ret = msgBox.exec();
+
+ if (ret == QMessageBox::Yes)
+ {
+ }
+}
+//----------------------------------------------------
+void QScriptDialog_t::openScriptFile(void)
+{
+ int ret, useNativeFileDialogVal;
+ QString filename;
+ std::string last;
+ std::string dir;
+ const char *exePath = nullptr;
+ const char *jsPath = nullptr;
+ QFileDialog dialog(this, tr("Open JS Script"));
+ QList<QUrl> urls;
+ QDir d;
+
+ exePath = fceuExecutablePath();
+
+ //urls = dialog.sidebarUrls();
+ urls << QUrl::fromLocalFile(QDir::rootPath());
+ urls << QUrl::fromLocalFile(QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first());
+ urls << QUrl::fromLocalFile(QStandardPaths::standardLocations(QStandardPaths::DesktopLocation).first());
+ urls << QUrl::fromLocalFile(QStandardPaths::standardLocations(QStandardPaths::DownloadLocation).first());
+ urls << QUrl::fromLocalFile(QDir(FCEUI_GetBaseDirectory()).absolutePath());
+
+ if (exePath[0] != 0)
+ {
+ d.setPath(QString(exePath) + "/../jsScripts");
+
+ if (d.exists())
+ {
+ urls << QUrl::fromLocalFile(d.absolutePath());
+ }
+ }
+#ifndef WIN32
+ d.setPath("/usr/share/fceux/jsScripts");
+
+ if (d.exists())
+ {
+ urls << QUrl::fromLocalFile(d.absolutePath());
+ }
+#endif
+
+ jsPath = getenv("FCEU_QSCRIPT_PATH");
+
+ // Parse LUA_PATH and add to urls
+ if (jsPath)
+ {
+ int i, j;
+ char stmp[2048];
+
+ i = j = 0;
+ while (jsPath[i] != 0)
+ {
+ if (jsPath[i] == ';')
+ {
+ stmp[j] = 0;
+
+ if (j > 0)
+ {
+ d.setPath(stmp);
+
+ if (d.exists())
+ {
+ urls << QUrl::fromLocalFile(d.absolutePath());
+ }
+ }
+ j = 0;
+ }
+ else
+ {
+ stmp[j] = jsPath[i];
+ j++;
+ }
+ i++;
+ }
+
+ stmp[j] = 0;
+
+ if (j > 0)
+ {
+ d.setPath(stmp);
+
+ if (d.exists())
+ {
+ urls << QUrl::fromLocalFile(d.absolutePath());
+ }
+ }
+ }
+
+ dialog.setFileMode(QFileDialog::ExistingFile);
+
+ dialog.setNameFilter(tr("JS Scripts (*.js *.JS) ;; All files (*)"));
+
+ dialog.setViewMode(QFileDialog::List);
+ dialog.setFilter(QDir::AllEntries | QDir::AllDirs | QDir::Hidden);
+ dialog.setLabelText(QFileDialog::Accept, tr("Load"));
+
+ g_config->getOption("SDL.LastLoadJs", &last);
+
+ if (last.size() == 0)
+ {
+#ifdef WIN32
+ last.assign(FCEUI_GetBaseDirectory());
+#else
+ last.assign("/usr/share/fceux/jsScripts");
+#endif
+ }
+
+ getDirFromFile(last.c_str(), dir);
+
+ dialog.setDirectory(tr(dir.c_str()));
+
+ // Check config option to use native file dialog or not
+ g_config->getOption("SDL.UseNativeFileDialog", &useNativeFileDialogVal);
+
+ dialog.setOption(QFileDialog::DontUseNativeDialog, !useNativeFileDialogVal);
+ dialog.setSidebarUrls(urls);
+
+ ret = dialog.exec();
+
+ if (ret)
+ {
+ QStringList fileList;
+ fileList = dialog.selectedFiles();
+
+ if (fileList.size() > 0)
+ {
+ filename = fileList[0];
+ }
+ }
+
+ if (filename.isNull())
+ {
+ return;
+ }
+ qDebug() << "selected file path : " << filename.toUtf8();
+
+ g_config->setOption("SDL.LastLoadJs", filename.toStdString().c_str());
+
+ scriptPath->setText(filename);
+
+}
+//----------------------------------------------------
+void QScriptDialog_t::startScript(void)
+{
+ scriptInstance->resetEngine();
+ if (scriptInstance->loadScriptFile(scriptPath->text()))
+ {
+ // Script parsing error
+ return;
+ }
+ // TODO add option to pass options to script main.
+ QJSValue argArray = scriptInstance->getEngine()->newArray(4);
+ argArray.setProperty(0, "arg1");
+ argArray.setProperty(1, "arg2");
+ argArray.setProperty(2, "arg3");
+
+ QJSValueList argList = { argArray };
+
+ scriptInstance->call("main", argList);
+}
+//----------------------------------------------------
+void QScriptDialog_t::stopScript(void)
+{
+}
+//----------------------------------------------------
+void QScriptDialog_t::refreshState(void)
+{
+ if (scriptInstance->isRunning())
+ {
+ stopButton->setEnabled(true);
+ startButton->setText(tr("Restart"));
+ }
+ else
+ {
+ stopButton->setEnabled(false);
+ startButton->setText(tr("Start"));
+ }
+}
+//----------------------------------------------------
+void QScriptDialog_t::logOutput(const QString& text)
+{
+ jsOutput->insertPlainText(text);
+}
+//----------------------------------------------------
+#endif // __FCEU_QSCRIPT_ENABLE__
diff --git a/src/drivers/Qt/QtScriptManager.h b/src/drivers/Qt/QtScriptManager.h
new file mode 100644
index 00000000..2844fcb3
--- /dev/null
+++ b/src/drivers/Qt/QtScriptManager.h
@@ -0,0 +1,144 @@
+// QtScriptManager.h
+//
+
+#pragma once
+
+#ifdef __FCEU_QSCRIPT_ENABLE__
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#include <QWidget>
+#include <QDialog>
+#include <QVBoxLayout>
+#include <QHBoxLayout>
+#include <QComboBox>
+#include <QCheckBox>
+#include <QPushButton>
+#include <QLabel>
+#include <QFrame>
+#include <QGroupBox>
+#include <QLineEdit>
+#include <QTextEdit>
+#include <QJSEngine>
+
+#include "Qt/main.h"
+#include "utils/timeStamp.h"
+
+class QScriptDialog_t;
+
+class EmuScriptObject: public QObject
+{
+ Q_OBJECT
+public:
+ EmuScriptObject(QObject* parent = nullptr);
+ ~EmuScriptObject();
+
+ void setDialog(QScriptDialog_t* _dialog){ dialog = _dialog; }
+
+private:
+ QScriptDialog_t* dialog = nullptr;
+
+public slots:
+ Q_INVOKABLE void print(const QString& msg);
+ Q_INVOKABLE void softreset();
+ Q_INVOKABLE void pause();
+ Q_INVOKABLE void unpause();
+
+};
+
+class QtScriptInstance : public QObject
+{
+ Q_OBJECT
+public:
+ QtScriptInstance(QObject* parent = nullptr);
+ ~QtScriptInstance();
+
+ void resetEngine();
+ int loadScriptFile(QString filepath);
+
+ bool isRunning();
+
+ int call(const QString& funcName, const QJSValueList& args = QJSValueList());
+ void onFrameFinish();
+
+ QJSEngine* getEngine(){ return engine; };
+private:
+
+ int configEngine();
+ void printSymbols(QJSValue& val, int iter = 0);
+
+ QJSEngine* engine = nullptr;
+ QScriptDialog_t* dialog = nullptr;
+ EmuScriptObject* emu = nullptr;
+ QJSValue onFrameFinishCallback;
+
+public slots:
+ Q_INVOKABLE void print(const QString& msg);
+};
+
+class QtScriptManager : public QObject
+{
+ Q_OBJECT
+
+public:
+ QtScriptManager(QObject* parent = nullptr);
+ ~QtScriptManager();
+
+ static QtScriptManager* getInstance(){ return _instance; }
+ static QtScriptManager* create(QObject* parent = nullptr);
+
+ void addScriptInstance(QtScriptInstance* script);
+ void removeScriptInstance(QtScriptInstance* script);
+private:
+ static QtScriptManager* _instance;
+
+ QList<QtScriptInstance*> scriptList;
+ FCEU::timeStampRecord lastFrameUpdate;
+
+public slots:
+ void frameFinishedUpdate();
+};
+
+class QScriptDialog_t : public QDialog
+{
+ Q_OBJECT
+
+public:
+ QScriptDialog_t(QWidget *parent = nullptr);
+ ~QScriptDialog_t(void);
+
+ void refreshState(void);
+ void logOutput(const QString& text);
+
+protected:
+ void closeEvent(QCloseEvent *bar);
+ void openJSKillMessageBox(void);
+
+ QTimer *periodicTimer;
+ QLineEdit *scriptPath;
+ QLineEdit *scriptArgs;
+ QPushButton *browseButton;
+ QPushButton *stopButton;
+ QPushButton *startButton;
+ QTextEdit *jsOutput;
+ QtScriptInstance *scriptInstance;
+
+private:
+public slots:
+ void closeWindow(void);
+private slots:
+ void updatePeriodic(void);
+ void openScriptFile(void);
+ void startScript(void);
+ void stopScript(void);
+};
+
+// Formatted print
+//int LuaPrintfToWindowConsole( __FCEU_PRINTF_FORMAT const char *format, ...) __FCEU_PRINTF_ATTRIBUTE( 1, 2 );
+
+//void PrintToWindowConsole(intptr_t hDlgAsInt, const char *str);
+
+//int LuaKillMessageBox(void);
+
+#endif // __FCEU_QSCRIPT_ENABLE__
diff --git a/src/drivers/Qt/config.cpp b/src/drivers/Qt/config.cpp
index ded54a16..50442ffb 100644
--- a/src/drivers/Qt/config.cpp
+++ b/src/drivers/Qt/config.cpp
@@ -801,6 +801,7 @@ InitConfig()
config->addOption("_lastsavestateas", "SDL.LastSaveStateAs", savPath );
config->addOption("_lastopenmovie", "SDL.LastOpenMovie", movPath);
config->addOption("_lastloadlua", "SDL.LastLoadLua", "");
+ config->addOption("_lastloadjs", "SDL.LastLoadJs", "");
config->addOption("SDL.HelpFilePath", "");
config->addOption("SDL.AviFilePath", "");
config->addOption("SDL.WavFilePath", "");
diff --git a/src/drivers/Qt/sdl-throttle.cpp b/src/drivers/Qt/sdl-throttle.cpp
index 800ad121..8d5daece 100644
--- a/src/drivers/Qt/sdl-throttle.cpp
+++ b/src/drivers/Qt/sdl-throttle.cpp
@@ -39,6 +39,7 @@ static const double Normal = 1.0; // 1x speed (around 60 fps on NTSC)
static uint32 frameLateCounter = 0;
static FCEU::timeStampRecord Lasttime, Nexttime, Latetime;
static FCEU::timeStampRecord DesiredFrameTime, HalfFrameTime, QuarterFrameTime, DoubleFrameTime;
+static FCEU::timeStampRecord emuSignalTx, guiSignalRx, emuSignalLatency;
static double desired_frametime = (1.0 / 60.099823);
static double desired_frameRate = (60.099823);
static double baseframeRate = (60.099823);
@@ -52,6 +53,9 @@ static double videoLastTs = 0.0;
static double videoPeriodCur = 0.0;
static double videoPeriodMin = 1.0;
static double videoPeriodMax = 0.0;
+static double emuLatencyCur = 0.0;
+static double emuLatencyMin = 1.0;
+static double emuLatencyMax = 0.0;
static bool keepFrameTimeStats = false;
static int InFrame = 0;
double g_fpsScale = Normal; // used by sdl.cpp
@@ -193,6 +197,11 @@ int getFrameTimingStats( struct frameTimingStat_t *stats )
stats->videoTimeDel.min = videoPeriodMin;
stats->videoTimeDel.max = videoPeriodMax;
+ stats->emuSignalDelay.tgt = 0.0;
+ stats->emuSignalDelay.cur = emuLatencyCur;
+ stats->emuSignalDelay.min = emuLatencyMin;
+ stats->emuSignalDelay.max = emuLatencyMax;
+
return 0;
}
@@ -216,6 +225,34 @@ void videoBufferSwapMark(void)
}
}
+void emuSignalSendMark(void)
+{
+ if ( keepFrameTimeStats )
+ {
+ emuSignalTx.readNew();
+ }
+}
+
+void guiSignalRecvMark(void)
+{
+ if ( keepFrameTimeStats )
+ {
+ guiSignalRx.readNew();
+ emuSignalLatency = guiSignalRx - emuSignalTx;
+
+ emuLatencyCur = emuSignalLatency.toSeconds();
+
+ if ( emuLatencyCur < emuLatencyMin )
+ {
+ emuLatencyMin = emuLatencyCur;
+ }
+ if ( emuLatencyCur > emuLatencyMax )
+ {
+ emuLatencyMax = emuLatencyCur;
+ }
+ }
+}
+
void resetFrameTiming(void)
{
frameLateCounter = 0;
@@ -225,6 +262,8 @@ void resetFrameTiming(void)
frameIdleMin = 1.0;
videoPeriodMin = 1.0;
videoPeriodMax = 0.0;
+ emuLatencyMin = 1.0;
+ emuLatencyMax = 0.0;
}
/* LOGMUL = exp(log(2) / 3)
diff --git a/src/drivers/Qt/throttle.h b/src/drivers/Qt/throttle.h
index 7cffdbe6..64f4baa4 100644
--- a/src/drivers/Qt/throttle.h
+++ b/src/drivers/Qt/throttle.h
@@ -43,6 +43,13 @@ struct frameTimingStat_t
double max;
} videoTimeDel;
+ struct {
+ double tgt;
+ double cur;
+ double min;
+ double max;
+ } emuSignalDelay;
+
unsigned int lateCount;
bool enabled;
@@ -52,6 +59,8 @@ void resetFrameTiming(void);
void setFrameTimingEnable( bool enable );
int getFrameTimingStats( struct frameTimingStat_t *stats );
void videoBufferSwapMark(void);
+void emuSignalSendMark(void);
+void guiSignalRecvMark(void);
double getHighPrecTimeStamp(void);
double getFrameRate(void);
double getFrameRateAdjustmentRatio(void);