diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index 0d62b8dcc7..3bd38c4ae7 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include "AssignmentFactory.h" #include "AssignmentActionFactory.h" @@ -53,6 +54,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri auto scriptableAvatar = DependencyManager::set(); auto addressManager = DependencyManager::set(); + auto scriptEngines = DependencyManager::set(); // create a NodeList as an unassigned client, must be after addressManager auto nodeList = DependencyManager::set(NodeType::Unassigned, listenPort); @@ -174,6 +176,8 @@ AssignmentClient::~AssignmentClient() { void AssignmentClient::aboutToQuit() { stopAssignmentClient(); + DependencyManager::destroy(); + // clear the log handler so that Qt doesn't call the destructor on LogHandler qInstallMessageHandler(0); } diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 6c94f9d254..e8ebb5d5fb 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -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 diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2fdd778339..4f0b514827 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -103,6 +103,7 @@ #include #include #include +#include #include #include #include @@ -319,6 +320,7 @@ bool setupEssentials(int& argc, char** argv) { Setting::init(); // Set dependencies + DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -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()); _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().data(); + scriptEngines->registerScriptInitializer([this](ScriptEngine* engine){ + registerScriptEngineWithApplicationServices(engine); + }); + + connect(scriptEngines, &ScriptEngines::scriptCountChanged, scriptEngines, [this] { + auto scriptEngines = DependencyManager::get(); + 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()->saveScripts(); + DependencyManager::get()->shutdownScripting(); // stop all currently running global scripts + DependencyManager::destroy(); // 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().data()); + rootContext->setContextProperty("Audio", &AudioScriptingInterface::getInstance()); rootContext->setContextProperty("Controller", DependencyManager::get().data()); rootContext->setContextProperty("Entities", DependencyManager::get().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().data()); rootContext->setContextProperty("AudioDevice", AudioDeviceScriptingInterface::getInstance()); rootContext->setContextProperty("AnimationCache", DependencyManager::get().data()); rootContext->setContextProperty("SoundCache", DependencyManager::get().data()); @@ -1233,7 +1246,6 @@ void Application::initializeUi() { rootContext->setContextProperty("HMD", DependencyManager::get().data()); rootContext->setContextProperty("Scene", DependencyManager::get().data()); rootContext->setContextProperty("Render", DependencyManager::get().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()->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().data()); scriptEngine->registerGlobalObject("Render", DependencyManager::get().data()); - scriptEngine->registerGlobalObject("ScriptDiscoveryService", this->getRunningScriptsWidget()); + scriptEngine->registerGlobalObject("ScriptDiscoveryService", DependencyManager::get().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()->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(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(); - for (QHash::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::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->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()->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()->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()->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()); diff --git a/interface/src/Application.h b/interface/src/Application.h index 39e5453d1d..9237373778 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -106,10 +106,6 @@ public: void postLambdaEvent(std::function 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 _firstRun; - Setting::Handle _previousScriptLocation; - Setting::Handle _scriptsLocationHandle; Setting::Handle _fieldOfView; float _scaleMirror; @@ -499,12 +466,7 @@ private: TouchEvent _lastTouchEvent; - QReadWriteLock _scriptEnginesHashLock; - RunningScriptsWidget* _runningScriptsWidget; - QHash _scriptEnginesHash; - bool _runningScriptsWidgetWasVisible; - QString _scriptsLocation; - + RunningScriptsWidget* _runningScriptsWidget{ nullptr }; quint64 _lastNackTime; quint64 _lastSendDownstreamAudioStats; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 6373b3fdb2..eec04d4640 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #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(); // 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] diff --git a/interface/src/ui/HMDToolsDialog.cpp b/interface/src/ui/HMDToolsDialog.cpp index c1ea541864..f9fc444d4b 100644 --- a/interface/src/ui/HMDToolsDialog.cpp +++ b/interface/src/ui/HMDToolsDialog.cpp @@ -79,9 +79,6 @@ HMDToolsDialog::HMDToolsDialog(QWidget* parent) : // what screens we're allowed on watchWindow(windowHandle()); auto dialogsManager = DependencyManager::get(); - if (qApp->getRunningScriptsWidget()) { - watchWindow(qApp->getRunningScriptsWidget()->windowHandle()); - } if (qApp->getToolWindow()) { watchWindow(qApp->getToolWindow()->windowHandle()); } diff --git a/interface/src/ui/JSConsole.cpp b/interface/src/ui/JSConsole.cpp index 2d34ba5608..5d197f5ddc 100644 --- a/interface/src/ui/JSConsole.cpp +++ b/interface/src/ui/JSConsole.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #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()->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&))); diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 216c11a05c..b5e24fef1e 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #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()->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()->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()->setScriptsLocation(ui.scriptsLocationEdit->text()); } myAvatar->getHead()->setPupilDilation(ui.pupilDilationSlider->value() / (float)ui.pupilDilationSlider->maximum()); diff --git a/interface/src/ui/RunningScriptsWidget.cpp b/interface/src/ui/RunningScriptsWidget.cpp index 3752ea2176..483d467f01 100644 --- a/interface/src/ui/RunningScriptsWidget.cpp +++ b/interface/src/ui/RunningScriptsWidget.cpp @@ -22,8 +22,10 @@ #include #include +#include #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().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(&QSignalMapper::mapped), - qApp, &Application::reloadOneScript); - + [scriptEngines](const QString& scriptName) { scriptEngines->stopScript(scriptName, true); }); connect(&_stopSignalMapper, static_cast(&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()->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(); + 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()->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 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(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(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 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(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()->stopAllScripts(); } diff --git a/interface/src/ui/RunningScriptsWidget.h b/interface/src/ui/RunningScriptsWidget.h index 9029b13c56..b60586b5e7 100644 --- a/interface/src/ui/RunningScriptsWidget.h +++ b/interface/src/ui/RunningScriptsWidget.h @@ -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); }; diff --git a/interface/src/ui/ScriptEditorWidget.cpp b/interface/src/ui/ScriptEditorWidget.cpp index 380b645681..a261c208d7 100644 --- a/interface/src/ui/ScriptEditorWidget.cpp +++ b/interface/src/ui/ScriptEditorWidget.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -101,17 +102,18 @@ bool ScriptEditorWidget::setRunning(bool run) { disconnect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished); } + auto scriptEngines = DependencyManager::get(); 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()->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(); 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; diff --git a/interface/src/ui/ScriptEditorWindow.cpp b/interface/src/ui/ScriptEditorWindow.cpp index 3c6d0f73d5..919c8b64d3 100644 --- a/interface/src/ui/ScriptEditorWindow.cpp +++ b/interface/src/ui/ScriptEditorWindow.cpp @@ -16,6 +16,7 @@ #include "ScriptEditorWidget.h" #include +#include #include #include #include @@ -27,6 +28,7 @@ #include #include +#include #include "Application.h" #include "PathUtils.h" @@ -85,11 +87,12 @@ void ScriptEditorWindow::loadScriptMenu(const QString& scriptName) { } void ScriptEditorWindow::loadScriptClicked() { + auto scriptEngines = DependencyManager::get(); 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()->getRunningScripts();; if (runningScripts.count() > 0) { QSignalMapper* signalMapper = new QSignalMapper(this); foreach (const QString& runningScript, runningScripts) { diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 8026102478..e997879391 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -46,11 +46,13 @@ #include "TypedArrays.h" #include "XMLHttpRequestClass.h" #include "WebSocketClass.h" - #include "RecordingScriptingInterface.h" +#include "ScriptEngines.h" #include "MIDIEvent.h" +std::atomic 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()->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()->removeScriptEngine(this); } void ScriptEngine::disconnectNonEssentialSignals() { @@ -187,67 +180,6 @@ void ScriptEngine::runInThread() { workerThread->start(); } -QSet ScriptEngine::_allKnownScriptEngines; -QMutex ScriptEngine::_allScriptsMutex; -std::atomic ScriptEngine::_stoppingAllScripts { false }; - -void ScriptEngine::stopAllScripts(QObject* application) { - _allScriptsMutex.lock(); - _stoppingAllScripts = true; - - qCDebug(scriptengine) << "Stopping all scripts.... currently known scripts:" << _allKnownScriptEngines.size(); - - QMutableSetIterator 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) { diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 67a206d673..f89998d9f0 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -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 _allKnownScriptEngines; - static QMutex _allScriptsMutex; + friend class ScriptEngines; static std::atomic _stoppingAllScripts; }; diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp new file mode 100644 index 0000000000..a7894f1816 --- /dev/null +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -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 +#include + +#include +#include + +#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 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 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(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(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 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(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(); + for (QHash::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::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->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()->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(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); +} diff --git a/libraries/script-engine/src/ScriptEngines.h b/libraries/script-engine/src/ScriptEngines.h new file mode 100644 index 0000000000..6d78f3cfa0 --- /dev/null +++ b/libraries/script-engine/src/ScriptEngines.h @@ -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 +#include +#include + +#include +#include +#include + +#include +#include + +#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; + + 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 _firstRun { "firstRun", true }; + QReadWriteLock _scriptEnginesHashLock; + QHash _scriptEnginesHash; + QSet _allKnownScriptEngines; + QMutex _allScriptsMutex; + std::atomic _stoppingAllScripts { false }; + std::list _scriptInitializers; + mutable Setting::Handle _scriptsLocationHandle; + mutable Setting::Handle _previousScriptLocation; + ScriptsModel _scriptsModel; + ScriptsModelFilter _scriptsModelFilter; +}; + +#endif // hifi_ScriptEngine_h diff --git a/interface/src/ScriptsModel.cpp b/libraries/script-engine/src/ScriptsModel.cpp similarity index 96% rename from interface/src/ScriptsModel.cpp rename to libraries/script-engine/src/ScriptsModel.cpp index a59d499863..07518dded1 100644 --- a/interface/src/ScriptsModel.cpp +++ b/libraries/script-engine/src/ScriptsModel.cpp @@ -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 #include #include #include -#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(); 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; } diff --git a/interface/src/ScriptsModel.h b/libraries/script-engine/src/ScriptsModel.h similarity index 98% rename from interface/src/ScriptsModel.h rename to libraries/script-engine/src/ScriptsModel.h index ae75acfa3b..df9716d43b 100644 --- a/interface/src/ScriptsModel.h +++ b/libraries/script-engine/src/ScriptsModel.h @@ -92,6 +92,7 @@ protected: void rebuildTree(); private: + friend class ScriptEngines; bool _loadingScripts; QDir _localDirectory; QFileSystemWatcher _fsWatcher; diff --git a/interface/src/ScriptsModelFilter.cpp b/libraries/script-engine/src/ScriptsModelFilter.cpp similarity index 100% rename from interface/src/ScriptsModelFilter.cpp rename to libraries/script-engine/src/ScriptsModelFilter.cpp diff --git a/interface/src/ScriptsModelFilter.h b/libraries/script-engine/src/ScriptsModelFilter.h similarity index 100% rename from interface/src/ScriptsModelFilter.h rename to libraries/script-engine/src/ScriptsModelFilter.h