mirror of
https://github.com/overte-org/overte.git
synced 2025-04-08 07:12:40 +02:00
Moving running scripts functionality to scripting library
This commit is contained in:
parent
ddd9b0826b
commit
d28f80aa07
20 changed files with 664 additions and 569 deletions
|
@ -31,6 +31,7 @@
|
|||
#include <ShutdownEventListener.h>
|
||||
#include <SoundCache.h>
|
||||
#include <ResourceScriptingInterface.h>
|
||||
#include <ScriptEngines.h>
|
||||
|
||||
#include "AssignmentFactory.h"
|
||||
#include "AssignmentActionFactory.h"
|
||||
|
@ -53,6 +54,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
|
|||
|
||||
auto scriptableAvatar = DependencyManager::set<ScriptableAvatar>();
|
||||
auto addressManager = DependencyManager::set<AddressManager>();
|
||||
auto scriptEngines = DependencyManager::set<ScriptEngines>();
|
||||
|
||||
// create a NodeList as an unassigned client, must be after addressManager
|
||||
auto nodeList = DependencyManager::set<NodeList>(NodeType::Unassigned, listenPort);
|
||||
|
@ -174,6 +176,8 @@ AssignmentClient::~AssignmentClient() {
|
|||
void AssignmentClient::aboutToQuit() {
|
||||
stopAssignmentClient();
|
||||
|
||||
DependencyManager::destroy<ScriptEngines>();
|
||||
|
||||
// clear the log handler so that Qt doesn't call the destructor on LogHandler
|
||||
qInstallMessageHandler(0);
|
||||
}
|
||||
|
|
|
@ -32,9 +32,14 @@ DialogContainer {
|
|||
property int maximumY: parent ? parent.height - height : 0
|
||||
|
||||
|
||||
Rectangle {
|
||||
id: dragRegion
|
||||
visible: dragMouseArea.containsMouse
|
||||
}
|
||||
|
||||
AddressBarDialog {
|
||||
id: addressBarDialog
|
||||
|
||||
z: dragRegion.z + 1
|
||||
implicitWidth: backgroundImage.width
|
||||
implicitHeight: backgroundImage.height
|
||||
|
||||
|
@ -48,11 +53,13 @@ DialogContainer {
|
|||
property int inputAreaStep: (height - inputAreaHeight) / 2
|
||||
|
||||
MouseArea {
|
||||
id: dragMouseArea
|
||||
// Drag the icon
|
||||
width: parent.height
|
||||
height: parent.height
|
||||
x: 0
|
||||
y: 0
|
||||
hoverEnabled: true
|
||||
drag {
|
||||
target: root
|
||||
minimumX: -parent.inputAreaStep
|
||||
|
@ -61,6 +68,8 @@ DialogContainer {
|
|||
maximumY: root.parent ? root.maximumY + parent.inputAreaStep : 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
MouseArea {
|
||||
// Drag the input rectangle
|
||||
|
|
|
@ -103,6 +103,7 @@
|
|||
#include <RecordingScriptingInterface.h>
|
||||
#include <ScriptCache.h>
|
||||
#include <SoundCache.h>
|
||||
#include <ScriptEngines.h>
|
||||
#include <TextureCache.h>
|
||||
#include <Tooltip.h>
|
||||
#include <udt/PacketHeaders.h>
|
||||
|
@ -319,6 +320,7 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
Setting::init();
|
||||
|
||||
// Set dependencies
|
||||
DependencyManager::set<ScriptEngines>();
|
||||
DependencyManager::set<recording::Deck>();
|
||||
DependencyManager::set<recording::Recorder>();
|
||||
DependencyManager::set<AddressManager>();
|
||||
|
@ -402,16 +404,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
_entityClipboard(new EntityTree()),
|
||||
_lastQueriedTime(usecTimestampNow()),
|
||||
_mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)),
|
||||
_firstRun("firstRun", true),
|
||||
_previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION),
|
||||
_scriptsLocationHandle("scriptsLocation", DESKTOP_LOCATION),
|
||||
_fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES),
|
||||
_scaleMirror(1.0f),
|
||||
_rotateMirror(0.0f),
|
||||
_raiseMirror(0.0f),
|
||||
_enableProcessOctreeThread(true),
|
||||
_runningScriptsWidget(NULL),
|
||||
_runningScriptsWidgetWasVisible(false),
|
||||
_lastNackTime(usecTimestampNow()),
|
||||
_lastSendDownstreamAudioStats(usecTimestampNow()),
|
||||
_aboutToQuit(false),
|
||||
|
@ -451,7 +448,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
|
||||
_bookmarks = new Bookmarks(); // Before setting up the menu
|
||||
|
||||
_runningScriptsWidget = new RunningScriptsWidget(_window);
|
||||
_renderEngine->addTask(make_shared<RenderDeferredTask>());
|
||||
_renderEngine->registerScene(_main3DScene);
|
||||
|
||||
|
@ -598,6 +594,30 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
connect(addressManager.data(), &AddressManager::hostChanged, this, &Application::updateWindowTitle);
|
||||
connect(this, &QCoreApplication::aboutToQuit, addressManager.data(), &AddressManager::storeCurrentAddress);
|
||||
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>().data();
|
||||
scriptEngines->registerScriptInitializer([this](ScriptEngine* engine){
|
||||
registerScriptEngineWithApplicationServices(engine);
|
||||
});
|
||||
|
||||
connect(scriptEngines, &ScriptEngines::scriptCountChanged, scriptEngines, [this] {
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
||||
if (scriptEngines->getRunningScripts().isEmpty()) {
|
||||
getMyAvatar()->clearScriptableSettings();
|
||||
}
|
||||
}, Qt::QueuedConnection);
|
||||
|
||||
connect(scriptEngines, &ScriptEngines::scriptsReloading, scriptEngines, [this] {
|
||||
getEntities()->reloadEntityScripts();
|
||||
}, Qt::QueuedConnection);
|
||||
|
||||
connect(scriptEngines, &ScriptEngines::scriptLoadError,
|
||||
scriptEngines, [](const QString& filename, const QString& error){
|
||||
OffscreenUi::warning(nullptr, "Error Loading Script", filename + " failed to load.");
|
||||
}, Qt::QueuedConnection);
|
||||
|
||||
_runningScriptsWidget = new RunningScriptsWidget(_window);
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
WSADATA WsaData;
|
||||
int wsaresult = WSAStartup(MAKEWORD(2, 2), &WsaData);
|
||||
|
@ -677,9 +697,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
|
||||
_overlays.init(); // do this before scripts load
|
||||
|
||||
_runningScriptsWidget->setRunningScripts(getRunningScripts());
|
||||
|
||||
connect(this, SIGNAL(aboutToQuit()), this, SLOT(saveScripts()));
|
||||
connect(this, SIGNAL(aboutToQuit()), this, SLOT(aboutToQuit()));
|
||||
|
||||
// hook up bandwidth estimator
|
||||
|
@ -846,18 +863,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
userInputMapper->registerDevice(_keyboardMouseDevice->getInputDevice());
|
||||
userInputMapper->loadDefaultMapping(userInputMapper->getStandardDeviceID());
|
||||
|
||||
// check first run...
|
||||
if (_firstRun.get()) {
|
||||
qCDebug(interfaceapp) << "This is a first run...";
|
||||
// clear the scripts, and set out script to our default scripts
|
||||
clearScriptsBeforeRunning();
|
||||
loadScript(DEFAULT_SCRIPTS_JS_URL);
|
||||
|
||||
_firstRun.set(false);
|
||||
} else {
|
||||
// do this as late as possible so that all required subsystems are initialized
|
||||
loadScripts();
|
||||
}
|
||||
// force the model the look at the correct directory (weird order of operations issue)
|
||||
scriptEngines->setScriptsLocation(scriptEngines->getScriptsLocation());
|
||||
// do this as late as possible so that all required subsystems are initialized
|
||||
scriptEngines->loadScripts();
|
||||
|
||||
loadSettings();
|
||||
int SAVE_SETTINGS_INTERVAL = 10 * MSECS_PER_SECOND; // Let's save every seconds for now
|
||||
|
@ -1016,7 +1025,9 @@ void Application::cleanupBeforeQuit() {
|
|||
nodeList->getPacketReceiver().setShouldDropPackets(true);
|
||||
|
||||
getEntities()->shutdown(); // tell the entities system we're shutting down, so it will stop running scripts
|
||||
ScriptEngine::stopAllScripts(this); // stop all currently running global scripts
|
||||
DependencyManager::get<ScriptEngines>()->saveScripts();
|
||||
DependencyManager::get<ScriptEngines>()->shutdownScripting(); // stop all currently running global scripts
|
||||
DependencyManager::destroy<ScriptEngines>();
|
||||
|
||||
// first stop all timers directly or by invokeMethod
|
||||
// depending on what thread they run in
|
||||
|
@ -1191,8 +1202,9 @@ void Application::initializeUi() {
|
|||
connect(engine, &QQmlEngine::quit, [] {
|
||||
qApp->quit();
|
||||
});
|
||||
rootContext->setContextProperty("Audio", &AudioScriptingInterface::getInstance());
|
||||
|
||||
rootContext->setContextProperty("AnimationCache", DependencyManager::get<AnimationCache>().data());
|
||||
rootContext->setContextProperty("Audio", &AudioScriptingInterface::getInstance());
|
||||
rootContext->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());
|
||||
rootContext->setContextProperty("Entities", DependencyManager::get<EntityScriptingInterface>().data());
|
||||
rootContext->setContextProperty("MyAvatar", getMyAvatar());
|
||||
|
@ -1219,6 +1231,7 @@ void Application::initializeUi() {
|
|||
rootContext->setContextProperty("Menu", MenuScriptingInterface::getInstance());
|
||||
rootContext->setContextProperty("Stats", Stats::getInstance());
|
||||
rootContext->setContextProperty("Settings", SettingsScriptingInterface::getInstance());
|
||||
rootContext->setContextProperty("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());
|
||||
rootContext->setContextProperty("AudioDevice", AudioDeviceScriptingInterface::getInstance());
|
||||
rootContext->setContextProperty("AnimationCache", DependencyManager::get<AnimationCache>().data());
|
||||
rootContext->setContextProperty("SoundCache", DependencyManager::get<SoundCache>().data());
|
||||
|
@ -1233,7 +1246,6 @@ void Application::initializeUi() {
|
|||
rootContext->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
|
||||
rootContext->setContextProperty("Scene", DependencyManager::get<SceneScriptingInterface>().data());
|
||||
rootContext->setContextProperty("Render", DependencyManager::get<RenderScriptingInterface>().data());
|
||||
rootContext->setContextProperty("ScriptDiscoveryService", this->getRunningScriptsWidget());
|
||||
|
||||
_glWidget->installEventFilter(offscreenUi.data());
|
||||
VrMenu::load();
|
||||
|
@ -3437,29 +3449,6 @@ bool Application::isHMDMode() const {
|
|||
}
|
||||
float Application::getTargetFrameRate() { return getActiveDisplayPlugin()->getTargetFrameRate(); }
|
||||
|
||||
QRect Application::getDesirableApplicationGeometry() {
|
||||
QRect applicationGeometry = getWindow()->geometry();
|
||||
|
||||
// If our parent window is on the HMD, then don't use its geometry, instead use
|
||||
// the "main screen" geometry.
|
||||
HMDToolsDialog* hmdTools = DependencyManager::get<DialogsManager>()->getHMDToolsDialog();
|
||||
if (hmdTools && hmdTools->hasHMDScreen()) {
|
||||
QScreen* hmdScreen = hmdTools->getHMDScreen();
|
||||
QWindow* appWindow = getWindow()->windowHandle();
|
||||
QScreen* appScreen = appWindow->screen();
|
||||
|
||||
// if our app's screen is the hmd screen, we don't want to place the
|
||||
// running scripts widget on it. So we need to pick a better screen.
|
||||
// we will use the screen for the HMDTools since it's a guarenteed
|
||||
// better screen.
|
||||
if (appScreen == hmdScreen) {
|
||||
QScreen* betterScreen = hmdTools->windowHandle()->screen();
|
||||
applicationGeometry = betterScreen->geometry();
|
||||
}
|
||||
}
|
||||
return applicationGeometry;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// loadViewFrustum()
|
||||
//
|
||||
|
@ -4163,48 +4152,6 @@ int Application::processOctreeStats(ReceivedMessage& message, SharedNodePointer
|
|||
void Application::packetSent(quint64 length) {
|
||||
}
|
||||
|
||||
const QString SETTINGS_KEY = "Settings";
|
||||
|
||||
void Application::loadScripts() {
|
||||
// loads all saved scripts
|
||||
Settings settings;
|
||||
int size = settings.beginReadArray(SETTINGS_KEY);
|
||||
for (int i = 0; i < size; ++i){
|
||||
settings.setArrayIndex(i);
|
||||
QString string = settings.value("script").toString();
|
||||
if (!string.isEmpty()) {
|
||||
loadScript(string);
|
||||
}
|
||||
}
|
||||
settings.endArray();
|
||||
}
|
||||
|
||||
void Application::clearScriptsBeforeRunning() {
|
||||
// clears all scripts from the settingsSettings settings;
|
||||
Settings settings;
|
||||
settings.beginWriteArray(SETTINGS_KEY);
|
||||
settings.remove("");
|
||||
}
|
||||
|
||||
void Application::saveScripts() {
|
||||
// Saves all currently running user-loaded scripts
|
||||
Settings settings;
|
||||
settings.beginWriteArray(SETTINGS_KEY);
|
||||
settings.remove("");
|
||||
|
||||
QStringList runningScripts = getRunningScripts();
|
||||
int i = 0;
|
||||
for (auto it = runningScripts.begin(); it != runningScripts.end(); ++it) {
|
||||
if (getScriptEngine(*it)->isUserLoaded()) {
|
||||
settings.setArrayIndex(i);
|
||||
settings.setValue("script", *it);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
settings.endArray();
|
||||
}
|
||||
|
||||
|
||||
void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine) {
|
||||
// setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so
|
||||
// we can use the same ones from the application.
|
||||
|
@ -4231,11 +4178,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
scriptEngine->registerGlobalObject("Clipboard", clipboardScriptable);
|
||||
connect(scriptEngine, &ScriptEngine::finished, clipboardScriptable, &ClipboardScriptingInterface::deleteLater);
|
||||
|
||||
connect(scriptEngine, &ScriptEngine::finished, this, &Application::scriptFinished, Qt::DirectConnection);
|
||||
|
||||
connect(scriptEngine, SIGNAL(loadScript(const QString&, bool)), this, SLOT(loadScript(const QString&, bool)));
|
||||
connect(scriptEngine, SIGNAL(reloadScript(const QString&, bool)), this, SLOT(reloadScript(const QString&, bool)));
|
||||
|
||||
scriptEngine->registerGlobalObject("Overlays", &_overlays);
|
||||
qScriptRegisterMetaType(scriptEngine, OverlayPropertyResultToScriptValue, OverlayPropertyResultFromScriptValue);
|
||||
qScriptRegisterMetaType(scriptEngine, RayToOverlayIntersectionResultToScriptValue,
|
||||
|
@ -4283,7 +4225,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
scriptEngine->registerGlobalObject("Scene", DependencyManager::get<SceneScriptingInterface>().data());
|
||||
scriptEngine->registerGlobalObject("Render", DependencyManager::get<RenderScriptingInterface>().data());
|
||||
|
||||
scriptEngine->registerGlobalObject("ScriptDiscoveryService", this->getRunningScriptsWidget());
|
||||
scriptEngine->registerGlobalObject("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());
|
||||
}
|
||||
|
||||
bool Application::canAcceptURL(const QString& urlString) const {
|
||||
|
@ -4389,7 +4331,7 @@ bool Application::askToLoadScript(const QString& scriptFilenameOrURL) {
|
|||
|
||||
if (reply == QMessageBox::Yes) {
|
||||
qCDebug(interfaceapp) << "Chose to run the script: " << scriptFilenameOrURL;
|
||||
loadScript(scriptFilenameOrURL);
|
||||
DependencyManager::get<ScriptEngines>()->loadScript(scriptFilenameOrURL);
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Declined to run the script: " << scriptFilenameOrURL;
|
||||
}
|
||||
|
@ -4562,186 +4504,6 @@ bool Application::displayAvatarAttachmentConfirmationDialog(const QString& name)
|
|||
}
|
||||
}
|
||||
|
||||
ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUserLoaded,
|
||||
bool loadScriptFromEditor, bool activateMainWindow, bool reload) {
|
||||
|
||||
if (isAboutToQuit()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
QUrl scriptUrl(scriptFilename);
|
||||
const QString& scriptURLString = scriptUrl.toString();
|
||||
{
|
||||
QReadLocker lock(&_scriptEnginesHashLock);
|
||||
if (_scriptEnginesHash.contains(scriptURLString) && loadScriptFromEditor
|
||||
&& !_scriptEnginesHash[scriptURLString]->isFinished()) {
|
||||
|
||||
return _scriptEnginesHash[scriptURLString];
|
||||
}
|
||||
}
|
||||
|
||||
ScriptEngine* scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface);
|
||||
scriptEngine->setUserLoaded(isUserLoaded);
|
||||
|
||||
if (scriptFilename.isNull()) {
|
||||
// This appears to be the script engine used by the script widget's evaluation window before the file has been saved...
|
||||
|
||||
// this had better be the script editor (we should de-couple so somebody who thinks they are loading a script
|
||||
// doesn't just get an empty script engine)
|
||||
|
||||
// we can complete setup now since there isn't a script we have to load
|
||||
registerScriptEngineWithApplicationServices(scriptEngine);
|
||||
scriptEngine->runInThread();
|
||||
} else {
|
||||
// connect to the appropriate signals of this script engine
|
||||
connect(scriptEngine, &ScriptEngine::scriptLoaded, this, &Application::handleScriptEngineLoaded);
|
||||
connect(scriptEngine, &ScriptEngine::errorLoadingScript, this, &Application::handleScriptLoadError);
|
||||
|
||||
// get the script engine object to load the script at the designated script URL
|
||||
scriptEngine->loadURL(scriptUrl, reload);
|
||||
}
|
||||
|
||||
// restore the main window's active state
|
||||
if (activateMainWindow && !loadScriptFromEditor) {
|
||||
_window->activateWindow();
|
||||
}
|
||||
|
||||
return scriptEngine;
|
||||
}
|
||||
|
||||
void Application::reloadScript(const QString& scriptName, bool isUserLoaded) {
|
||||
loadScript(scriptName, isUserLoaded, false, false, true);
|
||||
}
|
||||
|
||||
// FIXME - change to new version of ScriptCache loading notification
|
||||
void Application::handleScriptEngineLoaded(const QString& scriptFilename) {
|
||||
ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(sender());
|
||||
|
||||
{
|
||||
QWriteLocker lock(&_scriptEnginesHashLock);
|
||||
_scriptEnginesHash.insertMulti(scriptFilename, scriptEngine);
|
||||
}
|
||||
|
||||
_runningScriptsWidget->setRunningScripts(getRunningScripts());
|
||||
UserActivityLogger::getInstance().loadedScript(scriptFilename);
|
||||
|
||||
// register our application services and set it off on its own thread
|
||||
registerScriptEngineWithApplicationServices(scriptEngine);
|
||||
scriptEngine->runInThread();
|
||||
}
|
||||
|
||||
// FIXME - change to new version of ScriptCache loading notification
|
||||
void Application::handleScriptLoadError(const QString& scriptFilename) {
|
||||
qCDebug(interfaceapp) << "Application::loadScript(), script failed to load...";
|
||||
OffscreenUi::warning(getWindow(), "Error Loading Script", scriptFilename + " failed to load.");
|
||||
}
|
||||
|
||||
QStringList Application::getRunningScripts() {
|
||||
QReadLocker lock(&_scriptEnginesHashLock);
|
||||
return _scriptEnginesHash.keys();
|
||||
}
|
||||
|
||||
ScriptEngine* Application::getScriptEngine(const QString& scriptHash) {
|
||||
QReadLocker lock(&_scriptEnginesHashLock);
|
||||
return _scriptEnginesHash.value(scriptHash, nullptr);
|
||||
}
|
||||
|
||||
void Application::scriptFinished(const QString& scriptName, ScriptEngine* engine) {
|
||||
bool removed = false;
|
||||
{
|
||||
QWriteLocker lock(&_scriptEnginesHashLock);
|
||||
const QString& scriptURLString = QUrl(scriptName).toString();
|
||||
for (auto it = _scriptEnginesHash.find(scriptURLString); it != _scriptEnginesHash.end(); ++it) {
|
||||
if (it.value() == engine) {
|
||||
_scriptEnginesHash.erase(it);
|
||||
removed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (removed) {
|
||||
postLambdaEvent([this, scriptName]() {
|
||||
_runningScriptsWidget->scriptStopped(scriptName);
|
||||
_runningScriptsWidget->setRunningScripts(getRunningScripts());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Application::stopAllScripts(bool restart) {
|
||||
{
|
||||
QReadLocker lock(&_scriptEnginesHashLock);
|
||||
|
||||
if (restart) {
|
||||
// Delete all running scripts from cache so that they are re-downloaded when they are restarted
|
||||
auto scriptCache = DependencyManager::get<ScriptCache>();
|
||||
for (QHash<QString, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
|
||||
it != _scriptEnginesHash.constEnd(); it++) {
|
||||
if (!it.value()->isFinished()) {
|
||||
scriptCache->deleteScript(it.key());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stop and possibly restart all currently running scripts
|
||||
for (QHash<QString, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
|
||||
it != _scriptEnginesHash.constEnd(); it++) {
|
||||
if (it.value()->isFinished()) {
|
||||
continue;
|
||||
}
|
||||
if (restart && it.value()->isUserLoaded()) {
|
||||
connect(it.value(), &ScriptEngine::finished, this, [this](QString scriptName, ScriptEngine* engine) {
|
||||
reloadScript(scriptName);
|
||||
});
|
||||
}
|
||||
QMetaObject::invokeMethod(it.value(), "stop");
|
||||
//it.value()->stop();
|
||||
qCDebug(interfaceapp) << "stopping script..." << it.key();
|
||||
}
|
||||
}
|
||||
getMyAvatar()->clearScriptableSettings();
|
||||
}
|
||||
|
||||
bool Application::stopScript(const QString& scriptHash, bool restart) {
|
||||
bool stoppedScript = false;
|
||||
{
|
||||
QReadLocker lock(&_scriptEnginesHashLock);
|
||||
if (_scriptEnginesHash.contains(scriptHash)) {
|
||||
ScriptEngine* scriptEngine = _scriptEnginesHash[scriptHash];
|
||||
if (restart) {
|
||||
auto scriptCache = DependencyManager::get<ScriptCache>();
|
||||
scriptCache->deleteScript(QUrl(scriptHash));
|
||||
connect(scriptEngine, &ScriptEngine::finished, this, [this](QString scriptName, ScriptEngine* engine) {
|
||||
reloadScript(scriptName);
|
||||
});
|
||||
}
|
||||
scriptEngine->stop();
|
||||
stoppedScript = true;
|
||||
qCDebug(interfaceapp) << "stopping script..." << scriptHash;
|
||||
}
|
||||
}
|
||||
if (_scriptEnginesHash.empty()) {
|
||||
getMyAvatar()->clearScriptableSettings();
|
||||
}
|
||||
return stoppedScript;
|
||||
}
|
||||
|
||||
void Application::reloadAllScripts() {
|
||||
DependencyManager::get<ScriptCache>()->clearCache();
|
||||
getEntities()->reloadEntityScripts();
|
||||
stopAllScripts(true);
|
||||
}
|
||||
|
||||
void Application::reloadOneScript(const QString& scriptName) {
|
||||
stopScript(scriptName, true);
|
||||
}
|
||||
|
||||
void Application::loadDefaultScripts() {
|
||||
QReadLocker lock(&_scriptEnginesHashLock);
|
||||
if (!_scriptEnginesHash.contains(DEFAULT_SCRIPTS_JS_URL)) {
|
||||
loadScript(DEFAULT_SCRIPTS_JS_URL);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::toggleRunningScriptsWidget() {
|
||||
if (_runningScriptsWidget->isVisible()) {
|
||||
if (_runningScriptsWidget->hasFocus()) {
|
||||
|
@ -4799,27 +4561,17 @@ void Application::domainSettingsReceived(const QJsonObject& domainSettingsObject
|
|||
qCDebug(interfaceapp) << "Destination wallet UUID for edit payments is" << voxelWalletUUID;
|
||||
}
|
||||
|
||||
QString Application::getPreviousScriptLocation() {
|
||||
return _previousScriptLocation.get();
|
||||
}
|
||||
|
||||
void Application::setPreviousScriptLocation(const QString& previousScriptLocation) {
|
||||
_previousScriptLocation.set(previousScriptLocation);
|
||||
}
|
||||
|
||||
void Application::loadDialog() {
|
||||
|
||||
QString fileNameString = QFileDialog::getOpenFileName(_glWidget,
|
||||
tr("Open Script"),
|
||||
getPreviousScriptLocation(),
|
||||
tr("JavaScript Files (*.js)"));
|
||||
// To be migratd to QML
|
||||
QString fileNameString = QFileDialog::getOpenFileName(
|
||||
_glWidget, tr("Open Script"), "", tr("JavaScript Files (*.js)"));
|
||||
if (!fileNameString.isEmpty()) {
|
||||
setPreviousScriptLocation(fileNameString);
|
||||
loadScript(fileNameString);
|
||||
DependencyManager::get<ScriptEngines>()->loadScript(fileNameString);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::loadScriptURLDialog() {
|
||||
// To be migratd to QML
|
||||
QInputDialog scriptURLDialog(getWindow());
|
||||
scriptURLDialog.setWindowTitle("Open and Run Script URL");
|
||||
scriptURLDialog.setLabelText("Script:");
|
||||
|
@ -4835,19 +4587,10 @@ void Application::loadScriptURLDialog() {
|
|||
// the user input a new hostname, use that
|
||||
newScript = scriptURLDialog.textValue();
|
||||
}
|
||||
loadScript(newScript);
|
||||
DependencyManager::get<ScriptEngines>()->loadScript(newScript);
|
||||
}
|
||||
}
|
||||
|
||||
QString Application::getScriptsLocation() {
|
||||
return _scriptsLocationHandle.get();
|
||||
}
|
||||
|
||||
void Application::setScriptsLocation(const QString& scriptsLocation) {
|
||||
_scriptsLocationHandle.set(scriptsLocation);
|
||||
emit scriptLocationChanged(scriptsLocation);
|
||||
}
|
||||
|
||||
void Application::toggleLogDialog() {
|
||||
if (! _logDialog) {
|
||||
_logDialog = new LogDialog(_glWidget, getLogger());
|
||||
|
|
|
@ -106,10 +106,6 @@ public:
|
|||
|
||||
void postLambdaEvent(std::function<void()> f);
|
||||
|
||||
void loadScripts();
|
||||
QString getPreviousScriptLocation();
|
||||
void setPreviousScriptLocation(const QString& previousScriptLocation);
|
||||
void clearScriptsBeforeRunning();
|
||||
void initializeGL();
|
||||
void initializeUi();
|
||||
void paintGL();
|
||||
|
@ -197,9 +193,6 @@ public:
|
|||
|
||||
NodeToJurisdictionMap& getEntityServerJurisdictions() { return _entityServerJurisdictions; }
|
||||
|
||||
QStringList getRunningScripts();
|
||||
ScriptEngine* getScriptEngine(const QString& scriptHash);
|
||||
|
||||
float getRenderResolutionScale() const;
|
||||
|
||||
bool isAboutToQuit() const { return _aboutToQuit; }
|
||||
|
@ -212,14 +205,8 @@ public:
|
|||
glm::mat4 getEyeOffset(int eye) const;
|
||||
glm::mat4 getEyeProjection(int eye) const;
|
||||
|
||||
QRect getDesirableApplicationGeometry();
|
||||
RunningScriptsWidget* getRunningScriptsWidget() { return _runningScriptsWidget; }
|
||||
|
||||
Bookmarks* getBookmarks() const { return _bookmarks; }
|
||||
|
||||
QString getScriptsLocation();
|
||||
void setScriptsLocation(const QString& scriptsLocation);
|
||||
|
||||
virtual bool canAcceptURL(const QString& url) const override;
|
||||
virtual bool acceptURL(const QString& url, bool defaultUpload = false) override;
|
||||
|
||||
|
@ -241,8 +228,6 @@ public:
|
|||
float getAverageSimsPerSecond();
|
||||
|
||||
signals:
|
||||
void scriptLocationChanged(const QString& newPath);
|
||||
|
||||
void svoImportRequested(const QString& url);
|
||||
|
||||
void checkBackgroundDownloads();
|
||||
|
@ -263,14 +248,6 @@ public slots:
|
|||
void loadDialog();
|
||||
void loadScriptURLDialog();
|
||||
void toggleLogDialog();
|
||||
|
||||
ScriptEngine* loadScript(const QString& scriptFilename = QString(), bool isUserLoaded = true,
|
||||
bool loadScriptFromEditor = false, bool activateMainWindow = false, bool reload = false);
|
||||
void stopAllScripts(bool restart = false);
|
||||
bool stopScript(const QString& scriptHash, bool restart = false);
|
||||
void reloadAllScripts();
|
||||
void reloadOneScript(const QString& scriptName);
|
||||
void loadDefaultScripts();
|
||||
void toggleRunningScriptsWidget();
|
||||
|
||||
void showFriendsWindow();
|
||||
|
@ -313,9 +290,6 @@ private slots:
|
|||
void idle(uint64_t now);
|
||||
void aboutToQuit();
|
||||
|
||||
void handleScriptEngineLoaded(const QString& scriptFilename);
|
||||
void handleScriptLoadError(const QString& scriptFilename);
|
||||
|
||||
void connectedToDomain(const QString& hostname);
|
||||
|
||||
void audioMuteToggled();
|
||||
|
@ -331,10 +305,6 @@ private slots:
|
|||
void loadSettings();
|
||||
void saveSettings();
|
||||
|
||||
void scriptFinished(const QString& scriptName, ScriptEngine* engine);
|
||||
void saveScripts();
|
||||
void reloadScript(const QString& scriptName, bool isUserLoaded = true);
|
||||
|
||||
bool acceptSnapshot(const QString& urlString);
|
||||
bool askToSetAvatarUrl(const QString& url);
|
||||
bool askToLoadScript(const QString& scriptFilenameOrURL);
|
||||
|
@ -468,9 +438,6 @@ private:
|
|||
Camera _mirrorCamera; // Cammera for mirror view
|
||||
QRect _mirrorViewRect;
|
||||
|
||||
Setting::Handle<bool> _firstRun;
|
||||
Setting::Handle<QString> _previousScriptLocation;
|
||||
Setting::Handle<QString> _scriptsLocationHandle;
|
||||
Setting::Handle<float> _fieldOfView;
|
||||
|
||||
float _scaleMirror;
|
||||
|
@ -499,12 +466,7 @@ private:
|
|||
|
||||
TouchEvent _lastTouchEvent;
|
||||
|
||||
QReadWriteLock _scriptEnginesHashLock;
|
||||
RunningScriptsWidget* _runningScriptsWidget;
|
||||
QHash<QString, ScriptEngine*> _scriptEnginesHash;
|
||||
bool _runningScriptsWidgetWasVisible;
|
||||
QString _scriptsLocation;
|
||||
|
||||
RunningScriptsWidget* _runningScriptsWidget{ nullptr };
|
||||
quint64 _lastNackTime;
|
||||
quint64 _lastSendDownstreamAudioStats;
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <SettingHandle.h>
|
||||
#include <UserActivityLogger.h>
|
||||
#include <VrMenu.h>
|
||||
#include <ScriptEngines.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "AccountManager.h"
|
||||
|
@ -114,13 +115,15 @@ Menu::Menu() {
|
|||
Qt::CTRL | Qt::SHIFT | Qt::Key_O, qApp, SLOT(loadScriptURLDialog()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
||||
// Edit > Stop All Scripts... [advanced]
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::StopAllScripts, 0, qApp, SLOT(stopAllScripts()),
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::StopAllScripts, 0,
|
||||
scriptEngines.data(), SLOT(stopAllScripts()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
// Edit > Reload All Scripts... [advanced]
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::ReloadAllScripts, Qt::CTRL | Qt::Key_R,
|
||||
qApp, SLOT(reloadAllScripts()),
|
||||
scriptEngines.data(), SLOT(reloadAllScripts()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
// Edit > Scripts Editor... [advanced]
|
||||
|
|
|
@ -79,9 +79,6 @@ HMDToolsDialog::HMDToolsDialog(QWidget* parent) :
|
|||
// what screens we're allowed on
|
||||
watchWindow(windowHandle());
|
||||
auto dialogsManager = DependencyManager::get<DialogsManager>();
|
||||
if (qApp->getRunningScriptsWidget()) {
|
||||
watchWindow(qApp->getRunningScriptsWidget()->windowHandle());
|
||||
}
|
||||
if (qApp->getToolWindow()) {
|
||||
watchWindow(qApp->getToolWindow()->windowHandle());
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <QScrollBar>
|
||||
#include <QtConcurrent/QtConcurrentRun>
|
||||
|
||||
#include <ScriptEngines.h>
|
||||
#include <PathUtils.h>
|
||||
|
||||
#include "Application.h"
|
||||
|
@ -84,7 +85,7 @@ void JSConsole::setScriptEngine(ScriptEngine* scriptEngine) {
|
|||
|
||||
// if scriptEngine is NULL then create one and keep track of it using _ownScriptEngine
|
||||
_ownScriptEngine = scriptEngine == NULL;
|
||||
_scriptEngine = _ownScriptEngine ? qApp->loadScript(QString(), false) : scriptEngine;
|
||||
_scriptEngine = _ownScriptEngine ? DependencyManager::get<ScriptEngines>()->loadScript(QString(), false) : scriptEngine;
|
||||
|
||||
connect(_scriptEngine, SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&)));
|
||||
connect(_scriptEngine, SIGNAL(errorMessage(const QString&)), this, SLOT(handleError(const QString&)));
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <devices/DdeFaceTracker.h>
|
||||
#include <devices/Faceshift.h>
|
||||
#include <NetworkingConstants.h>
|
||||
#include <ScriptEngines.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "DialogsManager.h"
|
||||
|
@ -44,7 +45,9 @@ PreferencesDialog::PreferencesDialog(QWidget* parent) :
|
|||
|
||||
connect(ui.buttonBrowseLocation, &QPushButton::clicked, this, &PreferencesDialog::openSnapshotLocationBrowser);
|
||||
connect(ui.buttonBrowseScriptsLocation, &QPushButton::clicked, this, &PreferencesDialog::openScriptsLocationBrowser);
|
||||
connect(ui.buttonReloadDefaultScripts, &QPushButton::clicked, qApp, &Application::loadDefaultScripts);
|
||||
connect(ui.buttonReloadDefaultScripts, &QPushButton::clicked, [] {
|
||||
DependencyManager::get<ScriptEngines>()->loadDefaultScripts();
|
||||
});
|
||||
|
||||
connect(ui.buttonChangeAppearance, &QPushButton::clicked, this, &PreferencesDialog::openFullAvatarModelBrowser);
|
||||
connect(ui.appearanceDescription, &QLineEdit::editingFinished, this, &PreferencesDialog::changeFullAvatarURL);
|
||||
|
@ -171,7 +174,7 @@ void PreferencesDialog::loadPreferences() {
|
|||
|
||||
ui.snapshotLocationEdit->setText(Snapshot::snapshotsLocation.get());
|
||||
|
||||
ui.scriptsLocationEdit->setText(qApp->getScriptsLocation());
|
||||
ui.scriptsLocationEdit->setText(DependencyManager::get<ScriptEngines>()->getScriptsLocation());
|
||||
|
||||
ui.pupilDilationSlider->setValue(myAvatar->getHead()->getPupilDilation() *
|
||||
ui.pupilDilationSlider->maximum());
|
||||
|
@ -265,7 +268,7 @@ void PreferencesDialog::savePreferences() {
|
|||
}
|
||||
|
||||
if (!ui.scriptsLocationEdit->text().isEmpty() && QDir(ui.scriptsLocationEdit->text()).exists()) {
|
||||
qApp->setScriptsLocation(ui.scriptsLocationEdit->text());
|
||||
DependencyManager::get<ScriptEngines>()->setScriptsLocation(ui.scriptsLocationEdit->text());
|
||||
}
|
||||
|
||||
myAvatar->getHead()->setPupilDilation(ui.pupilDilationSlider->value() / (float)ui.pupilDilationSlider->maximum());
|
||||
|
|
|
@ -22,8 +22,10 @@
|
|||
#include <QWindow>
|
||||
|
||||
#include <PathUtils.h>
|
||||
#include <ScriptEngines.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "MainWindow.h"
|
||||
#include "Menu.h"
|
||||
#include "ScriptsModel.h"
|
||||
#include "UIUtil.h"
|
||||
|
@ -33,57 +35,55 @@ RunningScriptsWidget::RunningScriptsWidget(QWidget* parent) :
|
|||
Qt::WindowCloseButtonHint),
|
||||
ui(new Ui::RunningScriptsWidget),
|
||||
_reloadSignalMapper(this),
|
||||
_stopSignalMapper(this),
|
||||
_scriptsModelFilter(this),
|
||||
_scriptsModel(this) {
|
||||
_stopSignalMapper(this) {
|
||||
ui->setupUi(this);
|
||||
|
||||
setAttribute(Qt::WA_DeleteOnClose, false);
|
||||
|
||||
ui->filterLineEdit->installEventFilter(this);
|
||||
|
||||
connect(&_scriptsModelFilter, &QSortFilterProxyModel::modelReset,
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>().data();
|
||||
connect(scriptEngines->scriptsModelFilter(), &QSortFilterProxyModel::modelReset,
|
||||
this, &RunningScriptsWidget::selectFirstInList);
|
||||
|
||||
// FIXME: menu isn't prepared at this point.
|
||||
//QString shortcutText = Menu::getInstance()->getActionForOption(MenuOption::ReloadAllScripts)->shortcut().toString(QKeySequence::NativeText);
|
||||
//ui->tipLabel->setText("Tip: Use " + shortcutText + " to reload all scripts.");
|
||||
|
||||
_scriptsModelFilter.setSourceModel(&_scriptsModel);
|
||||
_scriptsModelFilter.sort(0, Qt::AscendingOrder);
|
||||
_scriptsModelFilter.setDynamicSortFilter(true);
|
||||
ui->scriptTreeView->setModel(&_scriptsModelFilter);
|
||||
ui->scriptTreeView->setModel(scriptEngines->scriptsModelFilter());
|
||||
|
||||
connect(ui->filterLineEdit, &QLineEdit::textChanged, this, &RunningScriptsWidget::updateFileFilter);
|
||||
connect(ui->scriptTreeView, &QTreeView::doubleClicked, this, &RunningScriptsWidget::loadScriptFromList);
|
||||
|
||||
connect(ui->reloadAllButton, &QPushButton::clicked, qApp, &Application::reloadAllScripts);
|
||||
connect(ui->stopAllButton, &QPushButton::clicked, this, &RunningScriptsWidget::allScriptsStopped);
|
||||
connect(ui->reloadAllButton, &QPushButton::clicked, scriptEngines, &ScriptEngines::reloadAllScripts);
|
||||
connect(ui->stopAllButton, &QPushButton::clicked, scriptEngines, &ScriptEngines::stopAllScripts);
|
||||
connect(ui->loadScriptFromDiskButton, &QPushButton::clicked, qApp, &Application::loadDialog);
|
||||
connect(ui->loadScriptFromURLButton, &QPushButton::clicked, qApp, &Application::loadScriptURLDialog);
|
||||
connect(&_reloadSignalMapper, static_cast<void(QSignalMapper::*)(const QString&)>(&QSignalMapper::mapped),
|
||||
qApp, &Application::reloadOneScript);
|
||||
|
||||
[scriptEngines](const QString& scriptName) { scriptEngines->stopScript(scriptName, true); });
|
||||
connect(&_stopSignalMapper, static_cast<void(QSignalMapper::*)(const QString&)>(&QSignalMapper::mapped),
|
||||
[](const QString& script) { qApp->stopScript(script); });
|
||||
[scriptEngines](const QString& scriptName) { scriptEngines->stopScript(scriptName); });
|
||||
|
||||
setRunningScripts(scriptEngines->getRunningScripts());
|
||||
connect(scriptEngines, &ScriptEngines::scriptCountChanged, scriptEngines, [this, scriptEngines] {
|
||||
setRunningScripts(scriptEngines->getRunningScripts());
|
||||
}, Qt::QueuedConnection);
|
||||
UIUtil::scaleWidgetFontSizes(this);
|
||||
}
|
||||
|
||||
RunningScriptsWidget::~RunningScriptsWidget() {
|
||||
delete ui;
|
||||
_scriptsModel.deleteLater();
|
||||
}
|
||||
|
||||
void RunningScriptsWidget::updateFileFilter(const QString& filter) {
|
||||
QRegExp regex("^.*" + QRegExp::escape(filter) + ".*$", Qt::CaseInsensitive);
|
||||
_scriptsModelFilter.setFilterRegExp(regex);
|
||||
DependencyManager::get<ScriptEngines>()->scriptsModelFilter()->setFilterRegExp(regex);
|
||||
selectFirstInList();
|
||||
}
|
||||
|
||||
void RunningScriptsWidget::loadScriptFromList(const QModelIndex& index) {
|
||||
QVariant scriptFile = _scriptsModelFilter.data(index, ScriptsModel::ScriptPath);
|
||||
qApp->loadScript(scriptFile.toString());
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
||||
QVariant scriptFile = scriptEngines->scriptsModelFilter()->data(index, ScriptsModel::ScriptPath);
|
||||
scriptEngines->loadScript(scriptFile.toString());
|
||||
}
|
||||
|
||||
void RunningScriptsWidget::loadSelectedScript() {
|
||||
|
@ -172,7 +172,7 @@ void RunningScriptsWidget::showEvent(QShowEvent* event) {
|
|||
ui->filterLineEdit->setFocus();
|
||||
}
|
||||
|
||||
QRect parentGeometry = qApp->getDesirableApplicationGeometry();
|
||||
QRect parentGeometry = qApp->getWindow()->geometry();
|
||||
int titleBarHeight = UIUtil::getWindowTitleBarHeight(this);
|
||||
int topMargin = titleBarHeight;
|
||||
|
||||
|
@ -183,8 +183,9 @@ void RunningScriptsWidget::showEvent(QShowEvent* event) {
|
|||
}
|
||||
|
||||
void RunningScriptsWidget::selectFirstInList() {
|
||||
if (_scriptsModelFilter.rowCount() > 0) {
|
||||
ui->scriptTreeView->setCurrentIndex(_scriptsModelFilter.index(0, 0));
|
||||
auto model = DependencyManager::get<ScriptEngines>()->scriptsModelFilter();
|
||||
if (model->rowCount() > 0) {
|
||||
ui->scriptTreeView->setCurrentIndex(model->index(0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -217,90 +218,5 @@ void RunningScriptsWidget::keyPressEvent(QKeyEvent *keyEvent) {
|
|||
}
|
||||
|
||||
void RunningScriptsWidget::allScriptsStopped() {
|
||||
qApp->stopAllScripts();
|
||||
}
|
||||
|
||||
QVariantList RunningScriptsWidget::getRunning() {
|
||||
const int WINDOWS_DRIVE_LETTER_SIZE = 1;
|
||||
QVariantList result;
|
||||
foreach(const QString& runningScript, qApp->getRunningScripts()) {
|
||||
QUrl runningScriptURL = QUrl(runningScript);
|
||||
if (runningScriptURL.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) {
|
||||
runningScriptURL = QUrl::fromLocalFile(runningScriptURL.toDisplayString(QUrl::FormattingOptions(QUrl::FullyEncoded)));
|
||||
}
|
||||
QVariantMap resultNode;
|
||||
resultNode.insert("name", runningScriptURL.fileName());
|
||||
resultNode.insert("url", runningScriptURL.toDisplayString(QUrl::FormattingOptions(QUrl::FullyEncoded)));
|
||||
// The path contains the exact path/URL of the script, which also is used in the stopScript function.
|
||||
resultNode.insert("path", runningScript);
|
||||
resultNode.insert("local", runningScriptURL.isLocalFile());
|
||||
result.append(resultNode);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QVariantList RunningScriptsWidget::getPublic() {
|
||||
return getPublicChildNodes(NULL);
|
||||
}
|
||||
|
||||
QVariantList RunningScriptsWidget::getPublicChildNodes(TreeNodeFolder* parent) {
|
||||
QVariantList result;
|
||||
QList<TreeNodeBase*> treeNodes = qApp->getRunningScriptsWidget()->getScriptsModel()
|
||||
->getFolderNodes(parent);
|
||||
for (int i = 0; i < treeNodes.size(); i++) {
|
||||
TreeNodeBase* node = treeNodes.at(i);
|
||||
if (node->getType() == TREE_NODE_TYPE_FOLDER) {
|
||||
TreeNodeFolder* folder = static_cast<TreeNodeFolder*>(node);
|
||||
QVariantMap resultNode;
|
||||
resultNode.insert("name", node->getName());
|
||||
resultNode.insert("type", "folder");
|
||||
resultNode.insert("children", getPublicChildNodes(folder));
|
||||
result.append(resultNode);
|
||||
continue;
|
||||
}
|
||||
TreeNodeScript* script = static_cast<TreeNodeScript*>(node);
|
||||
if (script->getOrigin() == ScriptOrigin::SCRIPT_ORIGIN_LOCAL) {
|
||||
continue;
|
||||
}
|
||||
QVariantMap resultNode;
|
||||
resultNode.insert("name", node->getName());
|
||||
resultNode.insert("type", "script");
|
||||
resultNode.insert("url", script->getFullPath());
|
||||
result.append(resultNode);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QVariantList RunningScriptsWidget::getLocal() {
|
||||
QVariantList result;
|
||||
QList<TreeNodeBase*> treeNodes = qApp->getRunningScriptsWidget()->getScriptsModel()
|
||||
->getFolderNodes(NULL);
|
||||
for (int i = 0; i < treeNodes.size(); i++) {
|
||||
TreeNodeBase* node = treeNodes.at(i);
|
||||
if (node->getType() != TREE_NODE_TYPE_SCRIPT) {
|
||||
continue;
|
||||
}
|
||||
TreeNodeScript* script = static_cast<TreeNodeScript*>(node);
|
||||
if (script->getOrigin() != ScriptOrigin::SCRIPT_ORIGIN_LOCAL) {
|
||||
continue;
|
||||
}
|
||||
QVariantMap resultNode;
|
||||
resultNode.insert("name", node->getName());
|
||||
resultNode.insert("path", script->getFullPath());
|
||||
result.append(resultNode);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool RunningScriptsWidget::stopScriptByName(const QString& name) {
|
||||
foreach (const QString& runningScript, qApp->getRunningScripts()) {
|
||||
if (QUrl(runningScript).fileName().toLower() == name.trimmed().toLower()) {
|
||||
return qApp->stopScript(runningScript, false);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RunningScriptsWidget::stopScript(const QString& name, bool restart) {
|
||||
return qApp->stopScript(name, restart);
|
||||
DependencyManager::get<ScriptEngines>()->stopAllScripts();
|
||||
}
|
||||
|
|
|
@ -33,24 +33,12 @@ public:
|
|||
|
||||
void setRunningScripts(const QStringList& list);
|
||||
|
||||
const ScriptsModel* getScriptsModel() { return &_scriptsModel; }
|
||||
|
||||
signals:
|
||||
void scriptStopped(const QString& scriptName);
|
||||
|
||||
protected:
|
||||
virtual bool eventFilter(QObject* sender, QEvent* event);
|
||||
|
||||
virtual void keyPressEvent(QKeyEvent* event);
|
||||
virtual void showEvent(QShowEvent* event);
|
||||
|
||||
public slots:
|
||||
QVariantList getRunning();
|
||||
QVariantList getPublic();
|
||||
QVariantList getLocal();
|
||||
bool stopScript(const QString& name, bool restart = false);
|
||||
bool stopScriptByName(const QString& name);
|
||||
|
||||
private slots:
|
||||
void allScriptsStopped();
|
||||
void updateFileFilter(const QString& filter);
|
||||
|
@ -62,9 +50,6 @@ private:
|
|||
Ui::RunningScriptsWidget* ui;
|
||||
QSignalMapper _reloadSignalMapper;
|
||||
QSignalMapper _stopSignalMapper;
|
||||
ScriptsModelFilter _scriptsModelFilter;
|
||||
ScriptsModel _scriptsModel;
|
||||
|
||||
QVariantList getPublicChildNodes(TreeNodeFolder* parent);
|
||||
};
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
#include <ScriptEngines.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <OffscreenUi.h>
|
||||
|
||||
|
@ -101,17 +102,18 @@ bool ScriptEditorWidget::setRunning(bool run) {
|
|||
disconnect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished);
|
||||
}
|
||||
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
||||
if (run) {
|
||||
const QString& scriptURLString = QUrl(_currentScript).toString();
|
||||
// Reload script so that an out of date copy is not retrieved from the cache
|
||||
_scriptEngine = qApp->loadScript(scriptURLString, true, true, false, true);
|
||||
_scriptEngine = scriptEngines->loadScript(scriptURLString, true, true, false, true);
|
||||
connect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged);
|
||||
connect(_scriptEngine, &ScriptEngine::update, this, &ScriptEditorWidget::onScriptModified);
|
||||
connect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished);
|
||||
} else {
|
||||
connect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished);
|
||||
const QString& scriptURLString = QUrl(_currentScript).toString();
|
||||
qApp->stopScript(scriptURLString);
|
||||
scriptEngines->stopScript(scriptURLString);
|
||||
_scriptEngine = NULL;
|
||||
}
|
||||
_console->setScriptEngine(_scriptEngine);
|
||||
|
@ -173,7 +175,7 @@ void ScriptEditorWidget::loadFile(const QString& scriptPath) {
|
|||
}
|
||||
}
|
||||
const QString& scriptURLString = QUrl(_currentScript).toString();
|
||||
_scriptEngine = qApp->getScriptEngine(scriptURLString);
|
||||
_scriptEngine = DependencyManager::get<ScriptEngines>()->getScriptEngine(scriptURLString);
|
||||
if (_scriptEngine != NULL) {
|
||||
connect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged);
|
||||
connect(_scriptEngine, &ScriptEngine::update, this, &ScriptEditorWidget::onScriptModified);
|
||||
|
@ -187,11 +189,12 @@ bool ScriptEditorWidget::save() {
|
|||
}
|
||||
|
||||
bool ScriptEditorWidget::saveAs() {
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
||||
QString fileName = QFileDialog::getSaveFileName(this, tr("Save script"),
|
||||
qApp->getPreviousScriptLocation(),
|
||||
scriptEngines->getPreviousScriptLocation(),
|
||||
tr("JavaScript Files (*.js)"));
|
||||
if (!fileName.isEmpty()) {
|
||||
qApp->setPreviousScriptLocation(fileName);
|
||||
scriptEngines->setPreviousScriptLocation(fileName);
|
||||
return saveFile(fileName);
|
||||
} else {
|
||||
return false;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "ScriptEditorWidget.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QtCore/QSignalMapper>
|
||||
#include <QFrame>
|
||||
#include <QLayoutItem>
|
||||
#include <QMainWindow>
|
||||
|
@ -27,6 +28,7 @@
|
|||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
#include <ScriptEngines.h>
|
||||
#include "Application.h"
|
||||
#include "PathUtils.h"
|
||||
|
||||
|
@ -85,11 +87,12 @@ void ScriptEditorWindow::loadScriptMenu(const QString& scriptName) {
|
|||
}
|
||||
|
||||
void ScriptEditorWindow::loadScriptClicked() {
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
||||
QString scriptName = QFileDialog::getOpenFileName(this, tr("Interface"),
|
||||
qApp->getPreviousScriptLocation(),
|
||||
scriptEngines->getPreviousScriptLocation(),
|
||||
tr("JavaScript Files (*.js)"));
|
||||
if (!scriptName.isEmpty()) {
|
||||
qApp->setPreviousScriptLocation(scriptName);
|
||||
scriptEngines->setPreviousScriptLocation(scriptName);
|
||||
addScriptEditorWidget("loading...")->loadFile(scriptName);
|
||||
updateButtons();
|
||||
}
|
||||
|
@ -97,7 +100,7 @@ void ScriptEditorWindow::loadScriptClicked() {
|
|||
|
||||
void ScriptEditorWindow::loadMenuAboutToShow() {
|
||||
_loadMenu->clear();
|
||||
QStringList runningScripts = qApp->getRunningScripts();
|
||||
QStringList runningScripts = DependencyManager::get<ScriptEngines>()->getRunningScripts();;
|
||||
if (runningScripts.count() > 0) {
|
||||
QSignalMapper* signalMapper = new QSignalMapper(this);
|
||||
foreach (const QString& runningScript, runningScripts) {
|
||||
|
|
|
@ -46,11 +46,13 @@
|
|||
#include "TypedArrays.h"
|
||||
#include "XMLHttpRequestClass.h"
|
||||
#include "WebSocketClass.h"
|
||||
|
||||
#include "RecordingScriptingInterface.h"
|
||||
#include "ScriptEngines.h"
|
||||
|
||||
#include "MIDIEvent.h"
|
||||
|
||||
std::atomic<bool> ScriptEngine::_stoppingAllScripts { false };
|
||||
|
||||
static const QString SCRIPT_EXCEPTION_FORMAT = "[UncaughtException] %1 in %2:%3";
|
||||
|
||||
Q_DECLARE_METATYPE(QScriptEngine::FunctionSignature)
|
||||
|
@ -132,9 +134,7 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam
|
|||
_fileNameString(fileNameString),
|
||||
_arrayBufferClass(new ArrayBufferClass(this))
|
||||
{
|
||||
_allScriptsMutex.lock();
|
||||
_allKnownScriptEngines.insert(this);
|
||||
_allScriptsMutex.unlock();
|
||||
DependencyManager::get<ScriptEngines>()->addScriptEngine(this);
|
||||
|
||||
connect(this, &QScriptEngine::signalHandlerException, this, [this](const QScriptValue& exception) {
|
||||
hadUncaughtExceptions(*this, _fileNameString);
|
||||
|
@ -144,14 +144,7 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam
|
|||
ScriptEngine::~ScriptEngine() {
|
||||
qCDebug(scriptengine) << "Script Engine shutting down (destructor) for script:" << getFilename();
|
||||
|
||||
// If we're not already in the middle of stopping all scripts, then we should remove ourselves
|
||||
// from the list of running scripts. We don't do this if we're in the process of stopping all scripts
|
||||
// because that method removes scripts from its list as it iterates them
|
||||
if (!_stoppingAllScripts) {
|
||||
_allScriptsMutex.lock();
|
||||
_allKnownScriptEngines.remove(this);
|
||||
_allScriptsMutex.unlock();
|
||||
}
|
||||
DependencyManager::get<ScriptEngines>()->removeScriptEngine(this);
|
||||
}
|
||||
|
||||
void ScriptEngine::disconnectNonEssentialSignals() {
|
||||
|
@ -187,67 +180,6 @@ void ScriptEngine::runInThread() {
|
|||
workerThread->start();
|
||||
}
|
||||
|
||||
QSet<ScriptEngine*> ScriptEngine::_allKnownScriptEngines;
|
||||
QMutex ScriptEngine::_allScriptsMutex;
|
||||
std::atomic<bool> ScriptEngine::_stoppingAllScripts { false };
|
||||
|
||||
void ScriptEngine::stopAllScripts(QObject* application) {
|
||||
_allScriptsMutex.lock();
|
||||
_stoppingAllScripts = true;
|
||||
|
||||
qCDebug(scriptengine) << "Stopping all scripts.... currently known scripts:" << _allKnownScriptEngines.size();
|
||||
|
||||
QMutableSetIterator<ScriptEngine*> i(_allKnownScriptEngines);
|
||||
while (i.hasNext()) {
|
||||
ScriptEngine* scriptEngine = i.next();
|
||||
|
||||
QString scriptName = scriptEngine->getFilename();
|
||||
|
||||
// NOTE: typically all script engines are running. But there's at least one known exception to this, the
|
||||
// "entities sandbox" which is only used to evaluate entities scripts to test their validity before using
|
||||
// them. We don't need to stop scripts that aren't running.
|
||||
if (scriptEngine->isRunning()) {
|
||||
|
||||
// If the script is running, but still evaluating then we need to wait for its evaluation step to
|
||||
// complete. After that we can handle the stop process appropriately
|
||||
if (scriptEngine->evaluatePending()) {
|
||||
while (scriptEngine->evaluatePending()) {
|
||||
|
||||
// This event loop allows any started, but not yet finished evaluate() calls to complete
|
||||
// we need to let these complete so that we can be guaranteed that the script engine isn't
|
||||
// in a partially setup state, which can confuse our shutdown unwinding.
|
||||
QEventLoop loop;
|
||||
QObject::connect(scriptEngine, &ScriptEngine::evaluationFinished, &loop, &QEventLoop::quit);
|
||||
loop.exec();
|
||||
}
|
||||
}
|
||||
|
||||
// We disconnect any script engine signals from the application because we don't want to do any
|
||||
// extra stopScript/loadScript processing that the Application normally does when scripts start
|
||||
// and stop. We can safely short circuit this because we know we're in the "quitting" process
|
||||
scriptEngine->disconnect(application);
|
||||
|
||||
// Calling stop on the script engine will set it's internal _isFinished state to true, and result
|
||||
// in the ScriptEngine gracefully ending it's run() method.
|
||||
scriptEngine->stop();
|
||||
|
||||
// We need to wait for the engine to be done running before we proceed, because we don't
|
||||
// want any of the scripts final "scriptEnding()" or pending "update()" methods from accessing
|
||||
// any application state after we leave this stopAllScripts() method
|
||||
qCDebug(scriptengine) << "waiting on script:" << scriptName;
|
||||
scriptEngine->waitTillDoneRunning();
|
||||
qCDebug(scriptengine) << "done waiting on script:" << scriptName;
|
||||
|
||||
// If the script is stopped, we can remove it from our set
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
_stoppingAllScripts = false;
|
||||
_allScriptsMutex.unlock();
|
||||
qCDebug(scriptengine) << "DONE Stopping all scripts....";
|
||||
}
|
||||
|
||||
|
||||
void ScriptEngine::waitTillDoneRunning() {
|
||||
// If the script never started running or finished running before we got here, we don't need to wait for it
|
||||
if (_isRunning && _isThreaded) {
|
||||
|
|
|
@ -129,7 +129,6 @@ public:
|
|||
bool isRunning() const { return _isRunning; } // used by ScriptWidget
|
||||
|
||||
void disconnectNonEssentialSignals();
|
||||
static void stopAllScripts(QObject* application); // used by Application on shutdown
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// NOTE - These are the callback implementations for ScriptUser the get called by ScriptCache when the contents
|
||||
|
@ -204,8 +203,7 @@ protected:
|
|||
void forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, QScriptValueList eventHanderArgs);
|
||||
Q_INVOKABLE void entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success);
|
||||
|
||||
static QSet<ScriptEngine*> _allKnownScriptEngines;
|
||||
static QMutex _allScriptsMutex;
|
||||
friend class ScriptEngines;
|
||||
static std::atomic<bool> _stoppingAllScripts;
|
||||
};
|
||||
|
||||
|
|
434
libraries/script-engine/src/ScriptEngines.cpp
Normal file
434
libraries/script-engine/src/ScriptEngines.cpp
Normal file
|
@ -0,0 +1,434 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2016/01/08
|
||||
// Copyright 2013-2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "ScriptEngines.h"
|
||||
|
||||
#include <QtCore/QStandardPaths>
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
#include <SettingHandle.h>
|
||||
#include <UserActivityLogger.h>
|
||||
|
||||
#include "ScriptEngine.h"
|
||||
#include "ScriptEngineLogging.h"
|
||||
|
||||
#define __STR2__(x) #x
|
||||
#define __STR1__(x) __STR2__(x)
|
||||
#define __LOC__ __FILE__ "("__STR1__(__LINE__)") : Warning Msg: "
|
||||
|
||||
#ifndef __APPLE__
|
||||
static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
|
||||
#else
|
||||
// Temporary fix to Qt bug: http://stackoverflow.com/questions/16194475
|
||||
static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).append("/script.js");
|
||||
#endif
|
||||
|
||||
ScriptsModel& getScriptsModel() {
|
||||
static ScriptsModel scriptsModel;
|
||||
return scriptsModel;
|
||||
}
|
||||
|
||||
ScriptEngines::ScriptEngines()
|
||||
: _scriptsLocationHandle("scriptsLocation", DESKTOP_LOCATION),
|
||||
_previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION){
|
||||
_scriptsModelFilter.setSourceModel(&_scriptsModel);
|
||||
_scriptsModelFilter.sort(0, Qt::AscendingOrder);
|
||||
_scriptsModelFilter.setDynamicSortFilter(true);
|
||||
}
|
||||
|
||||
|
||||
QObject* scriptsModel();
|
||||
|
||||
void ScriptEngines::registerScriptInitializer(ScriptInitializer initializer) {
|
||||
_scriptInitializers.push_back(initializer);
|
||||
}
|
||||
|
||||
void ScriptEngines::addScriptEngine(ScriptEngine* engine) {
|
||||
_allScriptsMutex.lock();
|
||||
_allKnownScriptEngines.insert(engine);
|
||||
_allScriptsMutex.unlock();
|
||||
}
|
||||
|
||||
void ScriptEngines::removeScriptEngine(ScriptEngine* engine) {
|
||||
// If we're not already in the middle of stopping all scripts, then we should remove ourselves
|
||||
// from the list of running scripts. We don't do this if we're in the process of stopping all scripts
|
||||
// because that method removes scripts from its list as it iterates them
|
||||
if (!_stoppingAllScripts) {
|
||||
_allScriptsMutex.lock();
|
||||
_allKnownScriptEngines.remove(engine);
|
||||
_allScriptsMutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEngines::shutdownScripting() {
|
||||
_allScriptsMutex.lock();
|
||||
_stoppingAllScripts = true;
|
||||
ScriptEngine::_stoppingAllScripts = true;
|
||||
qCDebug(scriptengine) << "Stopping all scripts.... currently known scripts:" << _allKnownScriptEngines.size();
|
||||
|
||||
QMutableSetIterator<ScriptEngine*> i(_allKnownScriptEngines);
|
||||
while (i.hasNext()) {
|
||||
ScriptEngine* scriptEngine = i.next();
|
||||
QString scriptName = scriptEngine->getFilename();
|
||||
|
||||
// NOTE: typically all script engines are running. But there's at least one known exception to this, the
|
||||
// "entities sandbox" which is only used to evaluate entities scripts to test their validity before using
|
||||
// them. We don't need to stop scripts that aren't running.
|
||||
if (scriptEngine->isRunning()) {
|
||||
|
||||
// If the script is running, but still evaluating then we need to wait for its evaluation step to
|
||||
// complete. After that we can handle the stop process appropriately
|
||||
if (scriptEngine->evaluatePending()) {
|
||||
while (scriptEngine->evaluatePending()) {
|
||||
|
||||
// This event loop allows any started, but not yet finished evaluate() calls to complete
|
||||
// we need to let these complete so that we can be guaranteed that the script engine isn't
|
||||
// in a partially setup state, which can confuse our shutdown unwinding.
|
||||
QEventLoop loop;
|
||||
QObject::connect(scriptEngine, &ScriptEngine::evaluationFinished, &loop, &QEventLoop::quit);
|
||||
loop.exec();
|
||||
}
|
||||
}
|
||||
|
||||
// We disconnect any script engine signals from the application because we don't want to do any
|
||||
// extra stopScript/loadScript processing that the Application normally does when scripts start
|
||||
// and stop. We can safely short circuit this because we know we're in the "quitting" process
|
||||
scriptEngine->disconnect(this);
|
||||
|
||||
// Calling stop on the script engine will set it's internal _isFinished state to true, and result
|
||||
// in the ScriptEngine gracefully ending it's run() method.
|
||||
scriptEngine->stop();
|
||||
|
||||
// We need to wait for the engine to be done running before we proceed, because we don't
|
||||
// want any of the scripts final "scriptEnding()" or pending "update()" methods from accessing
|
||||
// any application state after we leave this stopAllScripts() method
|
||||
qCDebug(scriptengine) << "waiting on script:" << scriptName;
|
||||
scriptEngine->waitTillDoneRunning();
|
||||
qCDebug(scriptengine) << "done waiting on script:" << scriptName;
|
||||
|
||||
// If the script is stopped, we can remove it from our set
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
_stoppingAllScripts = false;
|
||||
_allScriptsMutex.unlock();
|
||||
qCDebug(scriptengine) << "DONE Stopping all scripts....";
|
||||
}
|
||||
|
||||
QVariantList getPublicChildNodes(TreeNodeFolder* parent) {
|
||||
QVariantList result;
|
||||
QList<TreeNodeBase*> treeNodes = getScriptsModel().getFolderNodes(parent);
|
||||
for (int i = 0; i < treeNodes.size(); i++) {
|
||||
TreeNodeBase* node = treeNodes.at(i);
|
||||
if (node->getType() == TREE_NODE_TYPE_FOLDER) {
|
||||
TreeNodeFolder* folder = static_cast<TreeNodeFolder*>(node);
|
||||
QVariantMap resultNode;
|
||||
resultNode.insert("name", node->getName());
|
||||
resultNode.insert("type", "folder");
|
||||
resultNode.insert("children", getPublicChildNodes(folder));
|
||||
result.append(resultNode);
|
||||
continue;
|
||||
}
|
||||
TreeNodeScript* script = static_cast<TreeNodeScript*>(node);
|
||||
if (script->getOrigin() == ScriptOrigin::SCRIPT_ORIGIN_LOCAL) {
|
||||
continue;
|
||||
}
|
||||
QVariantMap resultNode;
|
||||
resultNode.insert("name", node->getName());
|
||||
resultNode.insert("type", "script");
|
||||
resultNode.insert("url", script->getFullPath());
|
||||
result.append(resultNode);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QVariantList ScriptEngines::getPublic() {
|
||||
return getPublicChildNodes(NULL);
|
||||
}
|
||||
|
||||
QVariantList ScriptEngines::getLocal() {
|
||||
QVariantList result;
|
||||
QList<TreeNodeBase*> treeNodes = getScriptsModel().getFolderNodes(NULL);
|
||||
for (int i = 0; i < treeNodes.size(); i++) {
|
||||
TreeNodeBase* node = treeNodes.at(i);
|
||||
if (node->getType() != TREE_NODE_TYPE_SCRIPT) {
|
||||
continue;
|
||||
}
|
||||
TreeNodeScript* script = static_cast<TreeNodeScript*>(node);
|
||||
if (script->getOrigin() != ScriptOrigin::SCRIPT_ORIGIN_LOCAL) {
|
||||
continue;
|
||||
}
|
||||
QVariantMap resultNode;
|
||||
resultNode.insert("name", node->getName());
|
||||
resultNode.insert("path", script->getFullPath());
|
||||
result.append(resultNode);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QVariantList ScriptEngines::getRunning() {
|
||||
const int WINDOWS_DRIVE_LETTER_SIZE = 1;
|
||||
QVariantList result;
|
||||
auto runningScripts = getRunningScripts();
|
||||
foreach(const QString& runningScript, runningScripts) {
|
||||
QUrl runningScriptURL = QUrl(runningScript);
|
||||
if (runningScriptURL.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) {
|
||||
runningScriptURL = QUrl::fromLocalFile(runningScriptURL.toDisplayString(QUrl::FormattingOptions(QUrl::FullyEncoded)));
|
||||
}
|
||||
QVariantMap resultNode;
|
||||
resultNode.insert("name", runningScriptURL.fileName());
|
||||
resultNode.insert("url", runningScriptURL.toDisplayString(QUrl::FormattingOptions(QUrl::FullyEncoded)));
|
||||
// The path contains the exact path/URL of the script, which also is used in the stopScript function.
|
||||
resultNode.insert("path", runningScript);
|
||||
resultNode.insert("local", runningScriptURL.isLocalFile());
|
||||
result.append(resultNode);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static const QString SETTINGS_KEY = "Settings";
|
||||
static const QString DEFAULT_SCRIPTS_JS_URL = "http://s3.amazonaws.com/hifi-public/scripts/defaultScripts.js";
|
||||
|
||||
void ScriptEngines::loadDefaultScripts() {
|
||||
loadScript(DEFAULT_SCRIPTS_JS_URL);
|
||||
}
|
||||
|
||||
void ScriptEngines::loadOneScript(const QString& scriptFilename) {
|
||||
loadScript(scriptFilename);
|
||||
}
|
||||
|
||||
void ScriptEngines::loadScripts() {
|
||||
// check first run...
|
||||
if (_firstRun.get()) {
|
||||
qCDebug(scriptengine) << "This is a first run...";
|
||||
// clear the scripts, and set out script to our default scripts
|
||||
clearScripts();
|
||||
loadDefaultScripts();
|
||||
_firstRun.set(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// loads all saved scripts
|
||||
Settings settings;
|
||||
int size = settings.beginReadArray(SETTINGS_KEY);
|
||||
for (int i = 0; i < size; ++i) {
|
||||
settings.setArrayIndex(i);
|
||||
QString string = settings.value("script").toString();
|
||||
if (!string.isEmpty()) {
|
||||
loadScript(string);
|
||||
}
|
||||
}
|
||||
settings.endArray();
|
||||
}
|
||||
|
||||
void ScriptEngines::clearScripts() {
|
||||
// clears all scripts from the settingsSettings settings;
|
||||
Settings settings;
|
||||
settings.beginWriteArray(SETTINGS_KEY);
|
||||
settings.remove("");
|
||||
}
|
||||
|
||||
void ScriptEngines::saveScripts() {
|
||||
// Saves all currently running user-loaded scripts
|
||||
Settings settings;
|
||||
settings.beginWriteArray(SETTINGS_KEY);
|
||||
settings.remove("");
|
||||
|
||||
QStringList runningScripts = getRunningScripts();
|
||||
int i = 0;
|
||||
for (auto it = runningScripts.begin(); it != runningScripts.end(); ++it) {
|
||||
if (getScriptEngine(*it)->isUserLoaded()) {
|
||||
settings.setArrayIndex(i);
|
||||
settings.setValue("script", *it);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
settings.endArray();
|
||||
}
|
||||
|
||||
QStringList ScriptEngines::getRunningScripts() {
|
||||
QReadLocker lock(&_scriptEnginesHashLock);
|
||||
return _scriptEnginesHash.keys();
|
||||
}
|
||||
|
||||
void ScriptEngines::stopAllScripts(bool restart) {
|
||||
QReadLocker lock(&_scriptEnginesHashLock);
|
||||
if (restart) {
|
||||
// Delete all running scripts from cache so that they are re-downloaded when they are restarted
|
||||
auto scriptCache = DependencyManager::get<ScriptCache>();
|
||||
for (QHash<QString, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
|
||||
it != _scriptEnginesHash.constEnd(); it++) {
|
||||
if (!it.value()->isFinished()) {
|
||||
scriptCache->deleteScript(it.key());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stop and possibly restart all currently running scripts
|
||||
for (QHash<QString, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
|
||||
it != _scriptEnginesHash.constEnd(); it++) {
|
||||
if (it.value()->isFinished()) {
|
||||
continue;
|
||||
}
|
||||
if (restart && it.value()->isUserLoaded()) {
|
||||
connect(it.value(), &ScriptEngine::finished, this, [this](QString scriptName, ScriptEngine* engine) {
|
||||
reloadScript(scriptName);
|
||||
});
|
||||
}
|
||||
QMetaObject::invokeMethod(it.value(), "stop");
|
||||
//it.value()->stop();
|
||||
qCDebug(scriptengine) << "stopping script..." << it.key();
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptEngines::stopScript(const QString& scriptHash, bool restart) {
|
||||
bool stoppedScript = false;
|
||||
{
|
||||
QReadLocker lock(&_scriptEnginesHashLock);
|
||||
if (_scriptEnginesHash.contains(scriptHash)) {
|
||||
ScriptEngine* scriptEngine = _scriptEnginesHash[scriptHash];
|
||||
if (restart) {
|
||||
auto scriptCache = DependencyManager::get<ScriptCache>();
|
||||
scriptCache->deleteScript(QUrl(scriptHash));
|
||||
connect(scriptEngine, &ScriptEngine::finished, this, [this](QString scriptName, ScriptEngine* engine) {
|
||||
reloadScript(scriptName);
|
||||
});
|
||||
}
|
||||
scriptEngine->stop();
|
||||
stoppedScript = true;
|
||||
qCDebug(scriptengine) << "stopping script..." << scriptHash;
|
||||
}
|
||||
}
|
||||
return stoppedScript;
|
||||
}
|
||||
|
||||
QString ScriptEngines::getScriptsLocation() const {
|
||||
return _scriptsLocationHandle.get();
|
||||
}
|
||||
|
||||
void ScriptEngines::setScriptsLocation(const QString& scriptsLocation) {
|
||||
_scriptsLocationHandle.set(scriptsLocation);
|
||||
_scriptsModel.updateScriptsLocation(scriptsLocation);
|
||||
}
|
||||
|
||||
void ScriptEngines::reloadAllScripts() {
|
||||
DependencyManager::get<ScriptCache>()->clearCache();
|
||||
emit scriptsReloading();
|
||||
stopAllScripts(true);
|
||||
}
|
||||
|
||||
ScriptEngine* ScriptEngines::loadScript(const QString& scriptFilename, bool isUserLoaded, bool loadScriptFromEditor, bool activateMainWindow, bool reload) {
|
||||
if (thread() != QThread::currentThread()) {
|
||||
ScriptEngine* result { nullptr };
|
||||
QMetaObject::invokeMethod(this, "loadScript", Qt::BlockingQueuedConnection, Q_RETURN_ARG(ScriptEngine*, result),
|
||||
Q_ARG(QString, scriptFilename),
|
||||
Q_ARG(bool, isUserLoaded),
|
||||
Q_ARG(bool, loadScriptFromEditor),
|
||||
Q_ARG(bool, activateMainWindow),
|
||||
Q_ARG(bool, reload));
|
||||
return result;
|
||||
}
|
||||
QUrl scriptUrl(scriptFilename);
|
||||
auto scriptEngine = getScriptEngine(scriptUrl.toString());
|
||||
if (scriptEngine) {
|
||||
return scriptEngine;
|
||||
}
|
||||
|
||||
scriptEngine = new ScriptEngine(NO_SCRIPT, "", true);
|
||||
scriptEngine->setUserLoaded(isUserLoaded);
|
||||
|
||||
if (scriptFilename.isNull()) {
|
||||
launchScriptEngine(scriptEngine);
|
||||
} else {
|
||||
// connect to the appropriate signals of this script engine
|
||||
connect(scriptEngine, &ScriptEngine::scriptLoaded, this, &ScriptEngines::onScriptEngineLoaded);
|
||||
connect(scriptEngine, &ScriptEngine::errorLoadingScript, this, &ScriptEngines::onScriptEngineError);
|
||||
|
||||
// get the script engine object to load the script at the designated script URL
|
||||
scriptEngine->loadURL(scriptUrl, reload);
|
||||
}
|
||||
|
||||
return scriptEngine;
|
||||
}
|
||||
|
||||
ScriptEngine* ScriptEngines::getScriptEngine(const QString& scriptHash) {
|
||||
ScriptEngine* result = nullptr;
|
||||
{
|
||||
QReadLocker lock(&_scriptEnginesHashLock);
|
||||
auto it = _scriptEnginesHash.find(scriptHash);
|
||||
if (it != _scriptEnginesHash.end()) {
|
||||
result = it.value();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// FIXME - change to new version of ScriptCache loading notification
|
||||
void ScriptEngines::onScriptEngineLoaded(const QString& scriptFilename) {
|
||||
UserActivityLogger::getInstance().loadedScript(scriptFilename);
|
||||
ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(sender());
|
||||
|
||||
launchScriptEngine(scriptEngine);
|
||||
|
||||
{
|
||||
QWriteLocker lock(&_scriptEnginesHashLock);
|
||||
_scriptEnginesHash.insertMulti(scriptFilename, scriptEngine);
|
||||
}
|
||||
emit scriptCountChanged();
|
||||
}
|
||||
|
||||
void ScriptEngines::launchScriptEngine(ScriptEngine* scriptEngine) {
|
||||
connect(scriptEngine, &ScriptEngine::finished, this, &ScriptEngines::onScriptFinished, Qt::DirectConnection);
|
||||
connect(scriptEngine, &ScriptEngine::loadScript, [&](const QString& scriptName, bool userLoaded) {
|
||||
loadScript(scriptName, userLoaded);
|
||||
});
|
||||
connect(scriptEngine, &ScriptEngine::reloadScript, [&](const QString& scriptName, bool userLoaded) {
|
||||
loadScript(scriptName, userLoaded, false, false, true);
|
||||
});
|
||||
|
||||
// register our application services and set it off on its own thread
|
||||
for (auto initializer : _scriptInitializers) {
|
||||
initializer(scriptEngine);
|
||||
}
|
||||
scriptEngine->runInThread();
|
||||
}
|
||||
|
||||
|
||||
void ScriptEngines::onScriptFinished(const QString& scriptName, ScriptEngine* engine) {
|
||||
bool removed = false;
|
||||
{
|
||||
QWriteLocker lock(&_scriptEnginesHashLock);
|
||||
const QString& scriptURLString = QUrl(scriptName).toString();
|
||||
for (auto it = _scriptEnginesHash.find(scriptURLString); it != _scriptEnginesHash.end(); ++it) {
|
||||
if (it.value() == engine) {
|
||||
_scriptEnginesHash.erase(it);
|
||||
removed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (removed) {
|
||||
emit scriptCountChanged();
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME - change to new version of ScriptCache loading notification
|
||||
void ScriptEngines::onScriptEngineError(const QString& scriptFilename) {
|
||||
qCDebug(scriptengine) << "Application::loadScript(), script failed to load...";
|
||||
emit scriptLoadError(scriptFilename, "");
|
||||
}
|
||||
|
||||
QString ScriptEngines::getPreviousScriptLocation() const {
|
||||
return _previousScriptLocation.get();
|
||||
}
|
||||
|
||||
void ScriptEngines::setPreviousScriptLocation(const QString& previousScriptLocation) {
|
||||
_previousScriptLocation.set(previousScriptLocation);
|
||||
}
|
103
libraries/script-engine/src/ScriptEngines.h
Normal file
103
libraries/script-engine/src/ScriptEngines.h
Normal file
|
@ -0,0 +1,103 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2016/01/08
|
||||
// Copyright 2013-2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_ScriptEngines_h
|
||||
#define hifi_ScriptEngines_h
|
||||
|
||||
#include <functional>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QMutex>
|
||||
#include <QtCore/QReadWriteLock>
|
||||
|
||||
#include <SettingHandle.h>
|
||||
#include <DependencyManager.h>
|
||||
|
||||
#include "ScriptsModel.h"
|
||||
#include "ScriptsModelFilter.h"
|
||||
|
||||
class ScriptEngine;
|
||||
|
||||
class ScriptEngines : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(ScriptsModel* scriptsModel READ scriptsModel CONSTANT)
|
||||
Q_PROPERTY(ScriptsModelFilter* scriptsModelFilter READ scriptsModelFilter CONSTANT)
|
||||
|
||||
public:
|
||||
using ScriptInitializer = std::function<void(ScriptEngine*)>;
|
||||
|
||||
ScriptEngines();
|
||||
void registerScriptInitializer(ScriptInitializer initializer);
|
||||
|
||||
void loadScripts();
|
||||
void saveScripts();
|
||||
void clearScripts();
|
||||
|
||||
QString getScriptsLocation() const;
|
||||
void loadDefaultScripts();
|
||||
void setScriptsLocation(const QString& scriptsLocation);
|
||||
QStringList getRunningScripts();
|
||||
ScriptEngine* getScriptEngine(const QString& scriptHash);
|
||||
|
||||
QString getPreviousScriptLocation() const;
|
||||
void setPreviousScriptLocation(const QString& previousScriptLocation);
|
||||
|
||||
ScriptsModel* scriptsModel() { return &_scriptsModel; };
|
||||
ScriptsModelFilter* scriptsModelFilter() { return &_scriptsModelFilter; };
|
||||
|
||||
Q_INVOKABLE void loadOneScript(const QString& scriptFilename);
|
||||
Q_INVOKABLE ScriptEngine* loadScript(const QString& scriptFilename = QString(),
|
||||
bool isUserLoaded = true, bool loadScriptFromEditor = false, bool activateMainWindow = false, bool reload = false);
|
||||
Q_INVOKABLE bool stopScript(const QString& scriptHash, bool restart = false);
|
||||
|
||||
Q_INVOKABLE void reloadAllScripts();
|
||||
Q_INVOKABLE void stopAllScripts(bool restart = false);
|
||||
|
||||
Q_INVOKABLE QVariantList getRunning();
|
||||
Q_INVOKABLE QVariantList getPublic();
|
||||
Q_INVOKABLE QVariantList getLocal();
|
||||
|
||||
// Called at shutdown time
|
||||
void shutdownScripting();
|
||||
|
||||
signals:
|
||||
void scriptCountChanged();
|
||||
void scriptsReloading();
|
||||
void scriptLoadError(const QString& filename, const QString& error);
|
||||
|
||||
protected slots:
|
||||
void onScriptFinished(const QString& fileNameString, ScriptEngine* engine);
|
||||
|
||||
protected:
|
||||
friend class ScriptEngine;
|
||||
|
||||
void reloadScript(const QString& scriptName) { loadScript(scriptName, true, false, false, true); }
|
||||
void addScriptEngine(ScriptEngine* engine);
|
||||
void removeScriptEngine(ScriptEngine* engine);
|
||||
void onScriptEngineLoaded(const QString& scriptFilename);
|
||||
void onScriptEngineError(const QString& scriptFilename);
|
||||
void launchScriptEngine(ScriptEngine* engine);
|
||||
|
||||
|
||||
Setting::Handle<bool> _firstRun { "firstRun", true };
|
||||
QReadWriteLock _scriptEnginesHashLock;
|
||||
QHash<QString, ScriptEngine*> _scriptEnginesHash;
|
||||
QSet<ScriptEngine*> _allKnownScriptEngines;
|
||||
QMutex _allScriptsMutex;
|
||||
std::atomic<bool> _stoppingAllScripts { false };
|
||||
std::list<ScriptInitializer> _scriptInitializers;
|
||||
mutable Setting::Handle<QString> _scriptsLocationHandle;
|
||||
mutable Setting::Handle<QString> _previousScriptLocation;
|
||||
ScriptsModel _scriptsModel;
|
||||
ScriptsModelFilter _scriptsModelFilter;
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptEngine_h
|
|
@ -1,7 +1,4 @@
|
|||
//
|
||||
// ScriptsModel.cpp
|
||||
// interface/src
|
||||
//
|
||||
// Created by Ryan Huffman on 05/12/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
|
@ -11,17 +8,21 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "ScriptsModel.h"
|
||||
|
||||
#include <QUrl>
|
||||
#include <QUrlQuery>
|
||||
#include <QXmlStreamReader>
|
||||
|
||||
#include <NetworkAccessManager.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Menu.h"
|
||||
#include "InterfaceLogging.h"
|
||||
#include "ScriptEngine.h"
|
||||
#include "ScriptEngines.h"
|
||||
#include "ScriptEngineLogging.h"
|
||||
#define __STR2__(x) #x
|
||||
#define __STR1__(x) __STR2__(x)
|
||||
#define __LOC__ __FILE__ "("__STR1__(__LINE__)") : Warning Msg: "
|
||||
|
||||
#include "ScriptsModel.h"
|
||||
|
||||
static const QString S3_URL = "http://s3.amazonaws.com/hifi-public";
|
||||
static const QString PUBLIC_URL = "http://public.highfidelity.io";
|
||||
|
@ -60,11 +61,8 @@ ScriptsModel::ScriptsModel(QObject* parent) :
|
|||
_localDirectory.setFilter(QDir::Files | QDir::Readable);
|
||||
_localDirectory.setNameFilters(QStringList("*.js"));
|
||||
|
||||
updateScriptsLocation(qApp->getScriptsLocation());
|
||||
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
||||
connect(&_fsWatcher, &QFileSystemWatcher::directoryChanged, this, &ScriptsModel::reloadLocalFiles);
|
||||
connect(qApp, &Application::scriptLocationChanged, this, &ScriptsModel::updateScriptsLocation);
|
||||
|
||||
reloadLocalFiles();
|
||||
reloadRemoteFiles();
|
||||
}
|
||||
|
@ -182,7 +180,7 @@ void ScriptsModel::downloadFinished() {
|
|||
if (!data.isEmpty()) {
|
||||
finished = parseXML(data);
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Error: Received no data when loading remote scripts";
|
||||
qCDebug(scriptengine) << "Error: Received no data when loading remote scripts";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -231,7 +229,7 @@ bool ScriptsModel::parseXML(QByteArray xmlFile) {
|
|||
|
||||
// Error handling
|
||||
if (xml.hasError()) {
|
||||
qCDebug(interfaceapp) << "Error loading remote scripts: " << xml.errorString();
|
||||
qCDebug(scriptengine) << "Error loading remote scripts: " << xml.errorString();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -92,6 +92,7 @@ protected:
|
|||
void rebuildTree();
|
||||
|
||||
private:
|
||||
friend class ScriptEngines;
|
||||
bool _loadingScripts;
|
||||
QDir _localDirectory;
|
||||
QFileSystemWatcher _fsWatcher;
|
Loading…
Reference in a new issue