mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 15:49:24 +02:00
Merge pull request #7845 from jherico/script_debug
Enable script debugging
This commit is contained in:
commit
af6be7f5f3
6 changed files with 143 additions and 17 deletions
|
@ -44,7 +44,7 @@ else ()
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
find_package(Qt5 COMPONENTS
|
find_package(Qt5 COMPONENTS
|
||||||
Gui Multimedia Network OpenGL Qml Quick Script Svg
|
Gui Multimedia Network OpenGL Qml Quick Script ScriptTools Svg
|
||||||
WebChannel WebEngine WebEngineWidgets WebKitWidgets WebSockets)
|
WebChannel WebEngine WebEngineWidgets WebKitWidgets WebSockets)
|
||||||
|
|
||||||
# grab the ui files in resources/ui
|
# grab the ui files in resources/ui
|
||||||
|
@ -201,7 +201,7 @@ include_directories("${PROJECT_SOURCE_DIR}/src")
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
${TARGET_NAME}
|
${TARGET_NAME}
|
||||||
Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL
|
Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL
|
||||||
Qt5::Qml Qt5::Quick Qt5::Script Qt5::Svg
|
Qt5::Qml Qt5::Quick Qt5::Script Qt5::ScriptTools Qt5::Svg
|
||||||
Qt5::WebChannel Qt5::WebEngine Qt5::WebEngineWidgets Qt5::WebKitWidgets
|
Qt5::WebChannel Qt5::WebEngine Qt5::WebEngineWidgets Qt5::WebKitWidgets
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,11 @@
|
||||||
|
|
||||||
#include "MenuScriptingInterface.h"
|
#include "MenuScriptingInterface.h"
|
||||||
|
|
||||||
#include "Menu.h"
|
#include <QtCore/QCoreApplication>
|
||||||
|
#include <QtCore/QThread>
|
||||||
|
|
||||||
#include <MenuItemProperties.h>
|
#include <MenuItemProperties.h>
|
||||||
|
#include "Menu.h"
|
||||||
|
|
||||||
MenuScriptingInterface* MenuScriptingInterface::getInstance() {
|
MenuScriptingInterface* MenuScriptingInterface::getInstance() {
|
||||||
static MenuScriptingInterface sharedInstance;
|
static MenuScriptingInterface sharedInstance;
|
||||||
|
@ -36,6 +39,9 @@ void MenuScriptingInterface::removeMenu(const QString& menu) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MenuScriptingInterface::menuExists(const QString& menu) {
|
bool MenuScriptingInterface::menuExists(const QString& menu) {
|
||||||
|
if (QThread::currentThread() == qApp->thread()) {
|
||||||
|
return Menu::getInstance()->menuExists(menu);
|
||||||
|
}
|
||||||
bool result;
|
bool result;
|
||||||
QMetaObject::invokeMethod(Menu::getInstance(), "menuExists", Qt::BlockingQueuedConnection,
|
QMetaObject::invokeMethod(Menu::getInstance(), "menuExists", Qt::BlockingQueuedConnection,
|
||||||
Q_RETURN_ARG(bool, result),
|
Q_RETURN_ARG(bool, result),
|
||||||
|
@ -76,11 +82,14 @@ void MenuScriptingInterface::removeMenuItem(const QString& menu, const QString&
|
||||||
};
|
};
|
||||||
|
|
||||||
bool MenuScriptingInterface::menuItemExists(const QString& menu, const QString& menuitem) {
|
bool MenuScriptingInterface::menuItemExists(const QString& menu, const QString& menuitem) {
|
||||||
|
if (QThread::currentThread() == qApp->thread()) {
|
||||||
|
return Menu::getInstance()->menuItemExists(menu, menuitem);
|
||||||
|
}
|
||||||
bool result;
|
bool result;
|
||||||
QMetaObject::invokeMethod(Menu::getInstance(), "menuItemExists", Qt::BlockingQueuedConnection,
|
QMetaObject::invokeMethod(Menu::getInstance(), "menuItemExists", Qt::BlockingQueuedConnection,
|
||||||
Q_RETURN_ARG(bool, result),
|
Q_RETURN_ARG(bool, result),
|
||||||
Q_ARG(const QString&, menu),
|
Q_ARG(const QString&, menu),
|
||||||
Q_ARG(const QString&, menuitem));
|
Q_ARG(const QString&, menuitem));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,6 +110,9 @@ void MenuScriptingInterface::removeActionGroup(const QString& groupName) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MenuScriptingInterface::isOptionChecked(const QString& menuOption) {
|
bool MenuScriptingInterface::isOptionChecked(const QString& menuOption) {
|
||||||
|
if (QThread::currentThread() == qApp->thread()) {
|
||||||
|
return Menu::getInstance()->isOptionChecked(menuOption);
|
||||||
|
}
|
||||||
bool result;
|
bool result;
|
||||||
QMetaObject::invokeMethod(Menu::getInstance(), "isOptionChecked", Qt::BlockingQueuedConnection,
|
QMetaObject::invokeMethod(Menu::getInstance(), "isOptionChecked", Qt::BlockingQueuedConnection,
|
||||||
Q_RETURN_ARG(bool, result),
|
Q_RETURN_ARG(bool, result),
|
||||||
|
@ -109,7 +121,7 @@ bool MenuScriptingInterface::isOptionChecked(const QString& menuOption) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuScriptingInterface::setIsOptionChecked(const QString& menuOption, bool isChecked) {
|
void MenuScriptingInterface::setIsOptionChecked(const QString& menuOption, bool isChecked) {
|
||||||
QMetaObject::invokeMethod(Menu::getInstance(), "setIsOptionChecked", Qt::BlockingQueuedConnection,
|
QMetaObject::invokeMethod(Menu::getInstance(), "setIsOptionChecked",
|
||||||
Q_ARG(const QString&, menuOption),
|
Q_ARG(const QString&, menuOption),
|
||||||
Q_ARG(bool, isChecked));
|
Q_ARG(bool, isChecked));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
set(TARGET_NAME script-engine)
|
set(TARGET_NAME script-engine)
|
||||||
setup_hifi_library(Gui Network Script WebSockets Widgets)
|
setup_hifi_library(Gui Network Script ScriptTools WebSockets Widgets)
|
||||||
link_hifi_libraries(shared networking octree gpu ui procedural model model-networking recording avatars fbx entities controllers animation audio physics)
|
link_hifi_libraries(shared networking octree gpu ui procedural model model-networking recording avatars fbx entities controllers animation audio physics)
|
||||||
|
|
|
@ -17,12 +17,18 @@
|
||||||
#include <QtCore/QFileInfo>
|
#include <QtCore/QFileInfo>
|
||||||
#include <QtCore/QTimer>
|
#include <QtCore/QTimer>
|
||||||
#include <QtCore/QThread>
|
#include <QtCore/QThread>
|
||||||
|
#include <QtCore/QRegularExpression>
|
||||||
|
|
||||||
|
#include <QtWidgets/QMainWindow>
|
||||||
|
#include <QtWidgets/QApplication>
|
||||||
|
|
||||||
#include <QtNetwork/QNetworkRequest>
|
#include <QtNetwork/QNetworkRequest>
|
||||||
#include <QtNetwork/QNetworkReply>
|
#include <QtNetwork/QNetworkReply>
|
||||||
#include <QtScript/QScriptEngine>
|
|
||||||
#include <QtScript/QScriptValue>
|
#include <QtScript/QScriptValue>
|
||||||
#include <QtScript/QScriptValueIterator>
|
#include <QtScript/QScriptValueIterator>
|
||||||
#include <QtCore/QStringList>
|
|
||||||
|
#include <QtScriptTools/QScriptEngineDebugger>
|
||||||
|
|
||||||
#include <AudioConstants.h>
|
#include <AudioConstants.h>
|
||||||
#include <AudioEffectOptions.h>
|
#include <AudioEffectOptions.h>
|
||||||
|
@ -34,6 +40,7 @@
|
||||||
#include <NodeList.h>
|
#include <NodeList.h>
|
||||||
#include <udt/PacketHeaders.h>
|
#include <udt/PacketHeaders.h>
|
||||||
#include <UUID.h>
|
#include <UUID.h>
|
||||||
|
#include <ui/Menu.h>
|
||||||
|
|
||||||
#include <controllers/ScriptingInterface.h>
|
#include <controllers/ScriptingInterface.h>
|
||||||
#include <AnimationObject.h>
|
#include <AnimationObject.h>
|
||||||
|
@ -169,6 +176,93 @@ void ScriptEngine::disconnectNonEssentialSignals() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScriptEngine::runDebuggable() {
|
||||||
|
static QMenuBar* menuBar { nullptr };
|
||||||
|
static QMenu* scriptDebugMenu { nullptr };
|
||||||
|
static size_t scriptMenuCount { 0 };
|
||||||
|
if (!scriptDebugMenu) {
|
||||||
|
for (auto window : qApp->topLevelWidgets()) {
|
||||||
|
auto mainWindow = qobject_cast<QMainWindow*>(window);
|
||||||
|
if (mainWindow) {
|
||||||
|
menuBar = mainWindow->menuBar();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (menuBar) {
|
||||||
|
scriptDebugMenu = menuBar->addMenu("Script Debug");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
|
_isRunning = true;
|
||||||
|
_debuggable = true;
|
||||||
|
_debugger = new QScriptEngineDebugger(this);
|
||||||
|
_debugger->attachTo(this);
|
||||||
|
|
||||||
|
QMenu* parentMenu = scriptDebugMenu;
|
||||||
|
QMenu* scriptMenu { nullptr };
|
||||||
|
if (parentMenu) {
|
||||||
|
++scriptMenuCount;
|
||||||
|
scriptMenu = parentMenu->addMenu(_fileNameString);
|
||||||
|
scriptMenu->addMenu(_debugger->createStandardMenu(qApp->activeWindow()));
|
||||||
|
} else {
|
||||||
|
qWarning() << "Unable to add script debug menu";
|
||||||
|
}
|
||||||
|
|
||||||
|
QScriptValue result = evaluate(_scriptContents, _fileNameString);
|
||||||
|
|
||||||
|
_lastUpdate = usecTimestampNow();
|
||||||
|
QTimer* timer = new QTimer(this);
|
||||||
|
connect(this, &ScriptEngine::finished, [this, timer, parentMenu, scriptMenu] {
|
||||||
|
if (scriptMenu) {
|
||||||
|
parentMenu->removeAction(scriptMenu->menuAction());
|
||||||
|
--scriptMenuCount;
|
||||||
|
if (0 == scriptMenuCount) {
|
||||||
|
menuBar->removeAction(scriptDebugMenu->menuAction());
|
||||||
|
scriptDebugMenu = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
disconnect(timer);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(timer, &QTimer::timeout, [this, timer] {
|
||||||
|
if (_isFinished) {
|
||||||
|
if (!_isRunning) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
stopAllTimers(); // make sure all our timers are stopped if the script is ending
|
||||||
|
if (_wantSignals) {
|
||||||
|
emit scriptEnding();
|
||||||
|
emit finished(_fileNameString, this);
|
||||||
|
}
|
||||||
|
_isRunning = false;
|
||||||
|
if (_wantSignals) {
|
||||||
|
emit runningStateChanged();
|
||||||
|
emit doneRunning();
|
||||||
|
}
|
||||||
|
timer->deleteLater();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 now = usecTimestampNow();
|
||||||
|
// we check for 'now' in the past in case people set their clock back
|
||||||
|
if (_lastUpdate < now) {
|
||||||
|
float deltaTime = (float)(now - _lastUpdate) / (float)USECS_PER_SECOND;
|
||||||
|
if (!_isFinished) {
|
||||||
|
if (_wantSignals) {
|
||||||
|
emit update(deltaTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_lastUpdate = now;
|
||||||
|
// Debug and clear exceptions
|
||||||
|
hadUncaughtExceptions(*this, _fileNameString);
|
||||||
|
});
|
||||||
|
|
||||||
|
timer->start(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void ScriptEngine::runInThread() {
|
void ScriptEngine::runInThread() {
|
||||||
Q_ASSERT_X(!_isThreaded, "ScriptEngine::runInThread()", "runInThread should not be called more than once");
|
Q_ASSERT_X(!_isThreaded, "ScriptEngine::runInThread()", "runInThread should not be called more than once");
|
||||||
|
|
||||||
|
@ -260,6 +354,10 @@ void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) {
|
||||||
// FIXME - switch this to the new model of ScriptCache callbacks
|
// FIXME - switch this to the new model of ScriptCache callbacks
|
||||||
void ScriptEngine::scriptContentsAvailable(const QUrl& url, const QString& scriptContents) {
|
void ScriptEngine::scriptContentsAvailable(const QUrl& url, const QString& scriptContents) {
|
||||||
_scriptContents = scriptContents;
|
_scriptContents = scriptContents;
|
||||||
|
static const QString DEBUG_FLAG("#debug");
|
||||||
|
if (QRegularExpression(DEBUG_FLAG).match(scriptContents).hasMatch()) {
|
||||||
|
_debuggable = true;
|
||||||
|
}
|
||||||
if (_wantSignals) {
|
if (_wantSignals) {
|
||||||
emit scriptLoaded(url.toString());
|
emit scriptLoaded(url.toString());
|
||||||
}
|
}
|
||||||
|
@ -723,7 +821,7 @@ void ScriptEngine::run() {
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||||
|
|
||||||
qint64 lastUpdate = usecTimestampNow();
|
_lastUpdate = usecTimestampNow();
|
||||||
|
|
||||||
// TODO: Integrate this with signals/slots instead of reimplementing throttling for ScriptEngine
|
// TODO: Integrate this with signals/slots instead of reimplementing throttling for ScriptEngine
|
||||||
while (!_isFinished) {
|
while (!_isFinished) {
|
||||||
|
@ -771,15 +869,15 @@ void ScriptEngine::run() {
|
||||||
qint64 now = usecTimestampNow();
|
qint64 now = usecTimestampNow();
|
||||||
|
|
||||||
// we check for 'now' in the past in case people set their clock back
|
// we check for 'now' in the past in case people set their clock back
|
||||||
if (lastUpdate < now) {
|
if (_lastUpdate < now) {
|
||||||
float deltaTime = (float) (now - lastUpdate) / (float) USECS_PER_SECOND;
|
float deltaTime = (float) (now - _lastUpdate) / (float) USECS_PER_SECOND;
|
||||||
if (!_isFinished) {
|
if (!_isFinished) {
|
||||||
if (_wantSignals) {
|
if (_wantSignals) {
|
||||||
emit update(deltaTime);
|
emit update(deltaTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastUpdate = now;
|
_lastUpdate = now;
|
||||||
|
|
||||||
// Debug and clear exceptions
|
// Debug and clear exceptions
|
||||||
hadUncaughtExceptions(*this, _fileNameString);
|
hadUncaughtExceptions(*this, _fileNameString);
|
||||||
|
|
|
@ -18,9 +18,10 @@
|
||||||
#include <QtCore/QUrl>
|
#include <QtCore/QUrl>
|
||||||
#include <QtCore/QSet>
|
#include <QtCore/QSet>
|
||||||
#include <QtCore/QWaitCondition>
|
#include <QtCore/QWaitCondition>
|
||||||
#include <QtScript/QScriptEngine>
|
|
||||||
#include <QtCore/QStringList>
|
#include <QtCore/QStringList>
|
||||||
|
|
||||||
|
#include <QtScript/QScriptEngine>
|
||||||
|
|
||||||
#include <AnimationCache.h>
|
#include <AnimationCache.h>
|
||||||
#include <AnimVariant.h>
|
#include <AnimVariant.h>
|
||||||
#include <AvatarData.h>
|
#include <AvatarData.h>
|
||||||
|
@ -39,6 +40,8 @@
|
||||||
#include "ScriptUUID.h"
|
#include "ScriptUUID.h"
|
||||||
#include "Vec3.h"
|
#include "Vec3.h"
|
||||||
|
|
||||||
|
class QScriptEngineDebugger;
|
||||||
|
|
||||||
static const QString NO_SCRIPT("");
|
static const QString NO_SCRIPT("");
|
||||||
|
|
||||||
static const int SCRIPT_FPS = 60;
|
static const int SCRIPT_FPS = 60;
|
||||||
|
@ -75,6 +78,8 @@ public:
|
||||||
/// services before calling this.
|
/// services before calling this.
|
||||||
void runInThread();
|
void runInThread();
|
||||||
|
|
||||||
|
void runDebuggable();
|
||||||
|
|
||||||
/// run the script in the callers thread, exit when stop() is called.
|
/// run the script in the callers thread, exit when stop() is called.
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
|
@ -140,6 +145,8 @@ public:
|
||||||
bool isFinished() const { return _isFinished; } // used by Application and ScriptWidget
|
bool isFinished() const { return _isFinished; } // used by Application and ScriptWidget
|
||||||
bool isRunning() const { return _isRunning; } // used by ScriptWidget
|
bool isRunning() const { return _isRunning; } // used by ScriptWidget
|
||||||
|
|
||||||
|
bool isDebuggable() const { return _debuggable; }
|
||||||
|
|
||||||
void disconnectNonEssentialSignals();
|
void disconnectNonEssentialSignals();
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -187,6 +194,9 @@ protected:
|
||||||
bool _wantSignals { true };
|
bool _wantSignals { true };
|
||||||
QHash<EntityItemID, EntityScriptDetails> _entityScripts;
|
QHash<EntityItemID, EntityScriptDetails> _entityScripts;
|
||||||
bool _isThreaded { false };
|
bool _isThreaded { false };
|
||||||
|
QScriptEngineDebugger* _debugger { nullptr };
|
||||||
|
bool _debuggable { false };
|
||||||
|
qint64 _lastUpdate;
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
QString getFilename() const;
|
QString getFilename() const;
|
||||||
|
|
|
@ -9,7 +9,8 @@
|
||||||
#include "ScriptEngines.h"
|
#include "ScriptEngines.h"
|
||||||
|
|
||||||
#include <QtCore/QStandardPaths>
|
#include <QtCore/QStandardPaths>
|
||||||
#include <QtCore/QCoreApplication>
|
|
||||||
|
#include <QtWidgets/QApplication>
|
||||||
|
|
||||||
#include <SettingHandle.h>
|
#include <SettingHandle.h>
|
||||||
#include <UserActivityLogger.h>
|
#include <UserActivityLogger.h>
|
||||||
|
@ -490,7 +491,12 @@ void ScriptEngines::launchScriptEngine(ScriptEngine* scriptEngine) {
|
||||||
for (auto initializer : _scriptInitializers) {
|
for (auto initializer : _scriptInitializers) {
|
||||||
initializer(scriptEngine);
|
initializer(scriptEngine);
|
||||||
}
|
}
|
||||||
scriptEngine->runInThread();
|
|
||||||
|
if (scriptEngine->isDebuggable() || (qApp->queryKeyboardModifiers() & Qt::ShiftModifier)) {
|
||||||
|
scriptEngine->runDebuggable();
|
||||||
|
} else {
|
||||||
|
scriptEngine->runInThread();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue