Enable script debugging

This commit is contained in:
Brad Davis 2016-05-08 10:40:30 -07:00
parent 0de721e8f4
commit 40316498a4
6 changed files with 111 additions and 17 deletions

View file

@ -44,7 +44,7 @@ else ()
endif ()
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)
# grab the ui files in resources/ui
@ -201,7 +201,7 @@ include_directories("${PROJECT_SOURCE_DIR}/src")
target_link_libraries(
${TARGET_NAME}
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
)

View file

@ -11,8 +11,11 @@
#include "MenuScriptingInterface.h"
#include "Menu.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QThread>
#include <MenuItemProperties.h>
#include "Menu.h"
MenuScriptingInterface* MenuScriptingInterface::getInstance() {
static MenuScriptingInterface sharedInstance;
@ -36,6 +39,9 @@ void MenuScriptingInterface::removeMenu(const QString& menu) {
}
bool MenuScriptingInterface::menuExists(const QString& menu) {
if (QThread::currentThread() == qApp->thread()) {
return Menu::getInstance()->menuExists(menu);
}
bool result;
QMetaObject::invokeMethod(Menu::getInstance(), "menuExists", Qt::BlockingQueuedConnection,
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) {
if (QThread::currentThread() == qApp->thread()) {
return Menu::getInstance()->menuItemExists(menu, menuitem);
}
bool result;
QMetaObject::invokeMethod(Menu::getInstance(), "menuItemExists", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, result),
Q_ARG(const QString&, menu),
Q_ARG(const QString&, menuitem));
Q_RETURN_ARG(bool, result),
Q_ARG(const QString&, menu),
Q_ARG(const QString&, menuitem));
return result;
}
@ -101,6 +110,9 @@ void MenuScriptingInterface::removeActionGroup(const QString& groupName) {
}
bool MenuScriptingInterface::isOptionChecked(const QString& menuOption) {
if (QThread::currentThread() == qApp->thread()) {
return Menu::getInstance()->isOptionChecked(menuOption);
}
bool result;
QMetaObject::invokeMethod(Menu::getInstance(), "isOptionChecked", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, result),
@ -109,7 +121,7 @@ bool MenuScriptingInterface::isOptionChecked(const QString& menuOption) {
}
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(bool, isChecked));
}

View file

@ -1,3 +1,3 @@
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)

View file

@ -17,12 +17,18 @@
#include <QtCore/QFileInfo>
#include <QtCore/QTimer>
#include <QtCore/QThread>
#include <QtCore/QRegularExpression>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QApplication>
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply>
#include <QtScript/QScriptEngine>
#include <QtScript/QScriptValue>
#include <QtScript/QScriptValueIterator>
#include <QtCore/QStringList>
#include <QtScriptTools/QScriptEngineDebugger>
#include <AudioConstants.h>
#include <AudioEffectOptions.h>
@ -34,6 +40,7 @@
#include <NodeList.h>
#include <udt/PacketHeaders.h>
#include <UUID.h>
#include <ui/Menu.h>
#include <controllers/ScriptingInterface.h>
#include <AnimationObject.h>
@ -169,6 +176,61 @@ void ScriptEngine::disconnectNonEssentialSignals() {
}
}
void ScriptEngine::runDebuggable() {
init();
_isRunning = true;
_debuggable = true;
_debugger = new QScriptEngineDebugger(this);
_debugger->attachTo(this);
static QMenu* scriptDebugMenu = qobject_cast<QMainWindow*>(qApp->activeWindow())->menuBar()->addMenu("Script Debug");
scriptDebugMenu->addMenu(_fileNameString)->addMenu(_debugger->createStandardMenu(qApp->activeWindow()));
_debugger->action(QScriptEngineDebugger::InterruptAction)->trigger();
QScriptValue result = evaluate(_scriptContents, _fileNameString);
_lastUpdate = usecTimestampNow();
QTimer* timer = new QTimer(this);
connect(this, &ScriptEngine::finished, [this, timer] {
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() {
Q_ASSERT_X(!_isThreaded, "ScriptEngine::runInThread()", "runInThread should not be called more than once");
@ -260,6 +322,10 @@ void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) {
// FIXME - switch this to the new model of ScriptCache callbacks
void ScriptEngine::scriptContentsAvailable(const QUrl& url, const QString& scriptContents) {
_scriptContents = scriptContents;
static const QString DEBUG_FLAG("#debug");
if (QRegularExpression(DEBUG_FLAG).match(scriptContents).hasMatch()) {
_debuggable = true;
}
if (_wantSignals) {
emit scriptLoaded(url.toString());
}
@ -723,7 +789,7 @@ void ScriptEngine::run() {
auto nodeList = DependencyManager::get<NodeList>();
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
qint64 lastUpdate = usecTimestampNow();
_lastUpdate = usecTimestampNow();
// TODO: Integrate this with signals/slots instead of reimplementing throttling for ScriptEngine
while (!_isFinished) {
@ -771,15 +837,15 @@ void ScriptEngine::run() {
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 (_lastUpdate < now) {
float deltaTime = (float) (now - _lastUpdate) / (float) USECS_PER_SECOND;
if (!_isFinished) {
if (_wantSignals) {
emit update(deltaTime);
}
}
}
lastUpdate = now;
_lastUpdate = now;
// Debug and clear exceptions
hadUncaughtExceptions(*this, _fileNameString);

View file

@ -18,9 +18,10 @@
#include <QtCore/QUrl>
#include <QtCore/QSet>
#include <QtCore/QWaitCondition>
#include <QtScript/QScriptEngine>
#include <QtCore/QStringList>
#include <QtScript/QScriptEngine>
#include <AnimationCache.h>
#include <AnimVariant.h>
#include <AvatarData.h>
@ -39,6 +40,8 @@
#include "ScriptUUID.h"
#include "Vec3.h"
class QScriptEngineDebugger;
static const QString NO_SCRIPT("");
static const int SCRIPT_FPS = 60;
@ -75,6 +78,8 @@ public:
/// services before calling this.
void runInThread();
void runDebuggable();
/// run the script in the callers thread, exit when stop() is called.
void run();
@ -140,6 +145,8 @@ public:
bool isFinished() const { return _isFinished; } // used by Application and ScriptWidget
bool isRunning() const { return _isRunning; } // used by ScriptWidget
bool isDebuggable() const { return _debuggable; }
void disconnectNonEssentialSignals();
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -187,6 +194,9 @@ protected:
bool _wantSignals { true };
QHash<EntityItemID, EntityScriptDetails> _entityScripts;
bool _isThreaded { false };
QScriptEngineDebugger* _debugger { nullptr };
bool _debuggable { false };
qint64 _lastUpdate;
void init();
QString getFilename() const;

View file

@ -9,7 +9,8 @@
#include "ScriptEngines.h"
#include <QtCore/QStandardPaths>
#include <QtCore/QCoreApplication>
#include <QtWidgets/QApplication>
#include <SettingHandle.h>
#include <UserActivityLogger.h>
@ -490,7 +491,12 @@ void ScriptEngines::launchScriptEngine(ScriptEngine* scriptEngine) {
for (auto initializer : _scriptInitializers) {
initializer(scriptEngine);
}
scriptEngine->runInThread();
if (scriptEngine->isDebuggable() || (qApp->queryKeyboardModifiers() & Qt::ShiftModifier)) {
scriptEngine->runDebuggable();
} else {
scriptEngine->runInThread();
}
}