From aef39ce6fa03147543af41e939b7eab37deccc51 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 10 Nov 2016 16:06:53 -0800 Subject: [PATCH 01/10] block until follow-on includes are finished but still avoid multiple evaluation of included urls --- libraries/script-engine/src/ScriptEngine.cpp | 25 +++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 776c7cfec6..c6a9874c38 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -1187,20 +1187,15 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac thisURL = resolvePath(file); } - if (!_includedURLs.contains(thisURL)) { - if (!isStandardLibrary && !currentSandboxURL.isEmpty() && (thisURL.scheme() == "file") && - (currentSandboxURL.scheme() != "file" || - !thisURL.toString(strippingFlags).startsWith(currentSandboxURL.toString(strippingFlags), getSensitivity()))) { - qCWarning(scriptengine) << "Script.include() ignoring file path" - << thisURL << "outside of original entity script" << currentSandboxURL; - } else { - // We could also check here for CORS, but we don't yet. - // It turns out that QUrl.resolve will not change hosts and copy authority, so we don't need to check that here. - urls.append(thisURL); - _includedURLs << thisURL; - } + if (!isStandardLibrary && !currentSandboxURL.isEmpty() && (thisURL.scheme() == "file") && + (currentSandboxURL.scheme() != "file" || + !thisURL.toString(strippingFlags).startsWith(currentSandboxURL.toString(strippingFlags), getSensitivity()))) { + qCWarning(scriptengine) << "Script.include() ignoring file path" + << thisURL << "outside of original entity script" << currentSandboxURL; } else { - qCDebug(scriptengine) << "Script.include() ignoring previously included url:" << thisURL; + // We could also check here for CORS, but we don't yet. + // It turns out that QUrl.resolve will not change hosts and copy authority, so we don't need to check that here. + urls.append(thisURL); } } @@ -1219,13 +1214,15 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac QString contents = data[url]; if (contents.isNull()) { qCDebug(scriptengine) << "Error loading file: " << url << "line:" << __LINE__; - } else { + } else if (!_includedURLs.contains(url)) { + _includedURLs << url; // Set the parent url so that path resolution will be relative // to this script's url during its initial evaluation _parentURL = url.toString(); auto operation = [&]() { evaluate(contents, url.toString()); }; + doWithEnvironment(capturedEntityIdentifier, capturedSandboxURL, operation); } } From 576eed9941cfae3bc03ee82cff50aa83c7758fde Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 10 Nov 2016 17:12:06 -0800 Subject: [PATCH 02/10] use a mutex to avoid a smaller race --- libraries/script-engine/src/ScriptEngine.cpp | 21 +++++++++++--------- libraries/script-engine/src/ScriptEngine.h | 1 + 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index c6a9874c38..1569a1b9f1 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -1214,16 +1214,19 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac QString contents = data[url]; if (contents.isNull()) { qCDebug(scriptengine) << "Error loading file: " << url << "line:" << __LINE__; - } else if (!_includedURLs.contains(url)) { - _includedURLs << url; - // Set the parent url so that path resolution will be relative - // to this script's url during its initial evaluation - _parentURL = url.toString(); - auto operation = [&]() { - evaluate(contents, url.toString()); - }; + } else { + std::lock_guard lock(_lock); + if (!_includedURLs.contains(url)) { + _includedURLs << url; + // Set the parent url so that path resolution will be relative + // to this script's url during its initial evaluation + _parentURL = url.toString(); + auto operation = [&]() { + evaluate(contents, url.toString()); + }; - doWithEnvironment(capturedEntityIdentifier, capturedSandboxURL, operation); + doWithEnvironment(capturedEntityIdentifier, capturedSandboxURL, operation); + } } } _parentURL = parentURL; diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 01088660ff..2b2cb3c81a 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -245,6 +245,7 @@ protected: std::function _emitScriptUpdates{ [](){ return true; } }; + std::recursive_mutex _lock; }; #endif // hifi_ScriptEngine_h From 5133ce054807019456403816d01dec090dec0507 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 12 Nov 2016 10:50:21 -0800 Subject: [PATCH 03/10] print a debug message when skipping evaluation of a previously included url --- libraries/script-engine/src/ScriptEngine.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 1569a1b9f1..00c48cc633 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -1226,6 +1226,8 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac }; doWithEnvironment(capturedEntityIdentifier, capturedSandboxURL, operation); + } else { + qCDebug(scriptengine) << "Script.include() skipping evaluation of previously included url:" << url; } } } From ec86b82079eed8da622560c98e011bbaa69ff879 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 14 Nov 2016 12:14:24 -0800 Subject: [PATCH 04/10] Add jsdoc to Camera --- interface/src/Camera.h | 57 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/interface/src/Camera.h b/interface/src/Camera.h index 792dcb4a40..27904242ab 100644 --- a/interface/src/Camera.h +++ b/interface/src/Camera.h @@ -35,9 +35,22 @@ __attribute__((unused)) #endif static int cameraModeId = qRegisterMetaType(); +/**jsdoc + * @global + */ class Camera : public QObject { Q_OBJECT - + + /**jsdoc + * @namespace Camera + * @property position {Vec3} The position of the camera. + * @property orientation {Quat} The orientation of the camera. + * @property mode {string} The current camera mode. + * @property cameraEntity {EntityID} The position and rotation properties of + * the entity specified by this ID are then used as the camera's position and + * orientation. Only works when mode is "entity". + * @property frustum {Object} The frustum of the camera. + */ Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition) Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation) Q_PROPERTY(QString mode READ getModeString WRITE setModeString) @@ -47,13 +60,13 @@ class Camera : public QObject { public: Camera(); - void initialize(); // instantly put the camera at the ideal position and orientation. + void initialize(); // instantly put the camera at the ideal position and orientation. void update( float deltaTime ); CameraMode getMode() const { return _mode; } void setMode(CameraMode m); - + void loadViewFrustum(ViewFrustum& frustum) const; ViewFrustum toViewFrustum() const; @@ -80,20 +93,44 @@ public slots: QUuid getCameraEntity() const; void setCameraEntity(QUuid entityID); + /**jsdoc + * Compute a {PickRay} based on the current camera configuration and the position x,y on the screen. + * @function Camera.computePickRay + * @param {float} x + * @param {float} y + * @return {PickRay} + */ PickRay computePickRay(float x, float y); - // These only work on independent cameras - /// one time change to what the camera is looking at - void lookAt(const glm::vec3& value); + /**jsdoc + * Set the camera to look at position position. Only works while in independent. + * camera mode. + * @function Camera.lookAt + * @param {Vec3} position position to look at + */ + void lookAt(const glm::vec3& position); - /// fix what the camera is looking at, and keep the camera looking at this even if position changes - void keepLookingAt(const glm::vec3& value); + /**jsdoc + * Set the camera to continue looking at position position. + * Only works while in `independent` camera mode. + * @function Camera.keepLookingAt + * @param {Vec3} position position to look at + */ + void keepLookingAt(const glm::vec3& position); - /// stops the keep looking at feature, doesn't change what's being looked at, but will stop camera from - /// continuing to update it's orientation to keep looking at the item + /**jsdoc + * Stops the camera from continually looking at a position that was set with + * `keepLookingAt` + * @function Camera.stopLookingAt + */ void stopLooking() { _isKeepLookingAt = false; } signals: + /**jsdoc + * Triggered when camera mode has changed. + * @function Camera.modeUpdated + * @return {Signal} + */ void modeUpdated(const QString& newMode); private: From 7e2bb0e5ed028e664d5cb709032016a99fba7933 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 14 Nov 2016 13:51:51 -0800 Subject: [PATCH 05/10] Cleanup camera docs --- interface/src/Camera.h | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/interface/src/Camera.h b/interface/src/Camera.h index 27904242ab..cedfb3f185 100644 --- a/interface/src/Camera.h +++ b/interface/src/Camera.h @@ -35,9 +35,6 @@ __attribute__((unused)) #endif static int cameraModeId = qRegisterMetaType(); -/**jsdoc - * @global - */ class Camera : public QObject { Q_OBJECT @@ -96,9 +93,9 @@ public slots: /**jsdoc * Compute a {PickRay} based on the current camera configuration and the position x,y on the screen. * @function Camera.computePickRay - * @param {float} x - * @param {float} y - * @return {PickRay} + * @param {float} x X-coordinate on screen. + * @param {float} y Y-coordinate on screen. + * @return {PickRay} The computed {PickRay}. */ PickRay computePickRay(float x, float y); @@ -106,7 +103,7 @@ public slots: * Set the camera to look at position position. Only works while in independent. * camera mode. * @function Camera.lookAt - * @param {Vec3} position position to look at + * @param {Vec3} Position Position to look at. */ void lookAt(const glm::vec3& position); @@ -114,7 +111,7 @@ public slots: * Set the camera to continue looking at position position. * Only works while in `independent` camera mode. * @function Camera.keepLookingAt - * @param {Vec3} position position to look at + * @param {Vec3} position Position to keep looking at. */ void keepLookingAt(const glm::vec3& position); From 3fcc2d3fa5d344a3f66cd40f979e021e56a89b92 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 14 Nov 2016 13:52:16 -0800 Subject: [PATCH 06/10] Add jsdoc to Clipboard --- .../scripting/ClipboardScriptingInterface.h | 51 ++++++++++++++++--- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/interface/src/scripting/ClipboardScriptingInterface.h b/interface/src/scripting/ClipboardScriptingInterface.h index 4737a194df..3c10475242 100644 --- a/interface/src/scripting/ClipboardScriptingInterface.h +++ b/interface/src/scripting/ClipboardScriptingInterface.h @@ -13,6 +13,9 @@ #include +/**jsdoc + * @namespace Clipboard + */ class ClipboardScriptingInterface : public QObject { Q_OBJECT public: @@ -21,13 +24,47 @@ public: signals: void readyToImport(); -public slots: - glm::vec3 getContentsDimensions(); /// returns the overall dimensions of everything on the blipboard - float getClipboardContentsLargestDimension(); /// returns the largest dimension of everything on the clipboard - bool importEntities(const QString& filename); - bool exportEntities(const QString& filename, const QVector& entityIDs); - bool exportEntities(const QString& filename, float x, float y, float z, float s); - QVector pasteEntities(glm::vec3 position); +public: + /**jsdoc + * @function Clipboard.getContentsDimensions + * @return {Vec3} The extents of the contents held in the clipboard. + */ + Q_INVOKABLE glm::vec3 getContentsDimensions(); + + /**jsdoc + * Compute largest dimension of the extents of the contents held in the clipboard + * @function Clipboard.getClipboardContentsLargestDimension + * @return {float} The largest dimension computed. + */ + Q_INVOKABLE float getClipboardContentsLargestDimension(); + + /**jsdoc + * Import entities from a .json file containing entity data into the clipboard. + * You can generate * a .json file using {Clipboard.exportEntities}. + * @function Clipboard.importEntities + * @param {string} filename Filename of file to import. + * @return {bool} True if the import was succesful, otherwise false. + */ + Q_INVOKABLE bool importEntities(const QString& filename); + + /**jsdoc + * Export the entities listed in `entityIDs` to the file `filename` + * @function Clipboard.exportEntities + * @param {string} filename Path to the file to export entities to. + * @param {EntityID[]} entityIDs IDs of entities to export. + * @return {bool} True if the export was succesful, otherwise false. + */ + Q_INVOKABLE bool exportEntities(const QString& filename, const QVector& entityIDs); + Q_INVOKABLE bool exportEntities(const QString& filename, float x, float y, float z, float s); + + /**jsdoc + * Paste the contents of the clipboard into the world. + * @function Clipboard.pasteEntities + * @param {Vec3} position Position to paste clipboard at. + * @return {EntityID[]} Array of entity IDs for the new entities that were + * created as a result of the paste operation. + */ + Q_INVOKABLE QVector pasteEntities(glm::vec3 position); }; #endif // hifi_ClipboardScriptingInterface_h From 5fd1ae64b0b2f124141e627a5d1eb61e7bf6aebe Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 14 Nov 2016 16:54:55 -0800 Subject: [PATCH 07/10] Begin adding entities jsdoc --- .../entities/src/EntityScriptingInterface.h | 69 ++++++++++++++++--- tools/jsdoc/plugins/hifi.js | 1 + 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 3d46113611..7cef753de9 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -56,6 +56,9 @@ QScriptValue RayToEntityIntersectionResultToScriptValue(QScriptEngine* engine, c void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, RayToEntityIntersectionResult& results); +/**jsdoc + * @namespace Entities + */ /// handles scripting of Entity commands from JS passed to assigned clients class EntityScriptingInterface : public OctreeScriptingInterface, public Dependency { Q_OBJECT @@ -87,40 +90,90 @@ public: ActivityTracking getActivityTracking() const { return _activityTracking; } public slots: - // returns true if the DomainServer will allow this Node/Avatar to make changes + /**jsdoc + * Returns `true` if the DomainServer will allow this Node/Avatar to make changes + * + * @function Entities.canAdjustLocks + * @return {bool} `true` if the client can adjust locks, `false` if not. + */ Q_INVOKABLE bool canAdjustLocks(); - // returns true if the DomainServer will allow this Node/Avatar to rez new entities + /**jsdoc + * @function Entities.canRez + * @return {bool} `true` if the DomainServer will allow this Node/Avatar to rez new entities + */ Q_INVOKABLE bool canRez(); + + /**jsdoc + * @function Entities.canRezTmp + * @return {bool} `true` if the DomainServer will allow this Node/Avatar to rez new temporary entities + */ Q_INVOKABLE bool canRezTmp(); - /// adds a model with the specific properties + /**jsdoc + * Add a new entity with the specified properties. If `clientOnly` is true, the entity will + * not be sent to the server and will only be visible/accessible on the local client. + * + * @function Entities.addEntity + * @param {EntityItemProperties} properties Properties of the entity to create. + * @param {bool} [clientOnly=false] Whether the entity should only exist locally or not. + * @return {EntityID} The entity ID of the newly created entity. The ID will be a null + * UUID (`{00000000-0000-0000-0000-000000000000}`) if the entity could not be created. + */ Q_INVOKABLE QUuid addEntity(const EntityItemProperties& properties, bool clientOnly = false); /// temporary method until addEntity can be used from QJSEngine + /// Deliberately not adding jsdoc, only used internally. Q_INVOKABLE QUuid addModelEntity(const QString& name, const QString& modelUrl, const QString& shapeType, bool dynamic, const glm::vec3& position, const glm::vec3& gravity); - /// gets the current model properties for a specific model - /// this function will not find return results in script engine contexts which don't have access to models + /**jsdoc + * Return the properties for the specified {EntityID}. + * not be sent to the server and will only be visible/accessible on the local client. + * @param {EntityItemProperties} properties Properties of the entity to create. + * @param {EntityPropertyFlags} [desiredProperties=[]] Array containing the names of the properties you + * would like to get. If the array is empty, all properties will be returned. + * @return {EntityItemProperties} The entity properties for the specified entity. + */ Q_INVOKABLE EntityItemProperties getEntityProperties(QUuid entityID); Q_INVOKABLE EntityItemProperties getEntityProperties(QUuid identity, EntityPropertyFlags desiredProperties); - /// edits a model updating only the included properties, will return the identified EntityItemID in case of - /// successful edit, if the input entityID is for an unknown model this function will have no effect + /**jsdoc + * Updates an entity with the specified properties. + * + * @function Entities.editEntity + * @return {EntityID} The EntityID of the entity if the edit was successful, otherwise the null {EntityID}. + */ Q_INVOKABLE QUuid editEntity(QUuid entityID, const EntityItemProperties& properties); - /// deletes a model + /**jsdoc + * Deletes an entity. + * + * @function Entities.deleteEntity + * @param {EntityID} entityID The ID of the entity to delete. + */ Q_INVOKABLE void deleteEntity(QUuid entityID); /// Allows a script to call a method on an entity's script. The method will execute in the entity script /// engine. If the entity does not have an entity script or the method does not exist, this call will have /// no effect. + /**jsdoc + * Call a method on an entity. If it is running an entity script (specified by the `script` property) + * and it exposes a property with the specified name `method`, it will be called + * using `params` as the list of arguments. + * + * @function Entities.callEntityMethod + * @param {EntityID} entityID The ID of the entity to call the method on. + * @param {string} method The name of the method to call. + * @param {string[]} params The list of parameters to call the specified method with. + */ Q_INVOKABLE void callEntityMethod(QUuid entityID, const QString& method, const QStringList& params = QStringList()); /// finds the closest model to the center point, within the radius /// will return a EntityItemID.isKnownID = false if no models are in the radius /// this function will not find any models in script engine contexts which don't have access to models + /**jsdoc + */ Q_INVOKABLE QUuid findClosestEntity(const glm::vec3& center, float radius) const; /// finds models within the search sphere specified by the center point and radius diff --git a/tools/jsdoc/plugins/hifi.js b/tools/jsdoc/plugins/hifi.js index 8016aa2ae5..084ff57fb5 100644 --- a/tools/jsdoc/plugins/hifi.js +++ b/tools/jsdoc/plugins/hifi.js @@ -19,6 +19,7 @@ exports.handlers = { '../../libraries/script-engine/src', '../../libraries/networking/src', '../../libraries/animation/src', + '../../libraries/entities/src', ]; var exts = ['.h', '.cpp']; From 1791a1c1fea0d7e4d8c3f403fee74863349087b7 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 15 Nov 2016 10:15:17 -0800 Subject: [PATCH 08/10] never send agents to other agents --- domain-server/src/DomainServer.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 5208cb2326..7d031c5be5 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -950,7 +950,9 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif if (nodeData->isAuthenticated()) { // if this authenticated node has any interest types, send back those nodes as well limitedNodeList->eachNode([&](const SharedNodePointer& otherNode){ - if (otherNode->getUUID() != node->getUUID() && nodeInterestSet.contains(otherNode->getType())) { + if (otherNode->getUUID() != node->getUUID() + && nodeInterestSet.contains(otherNode->getType()) + && (node->getType() != NodeType::Agent || otherNode->getType() != NodeType::Agent)) { // since we're about to add a node to the packet we start a segment domainListPackets->startSegment(); From 39afb24982865268cc51b5564c03ab847abfdcf2 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 15 Nov 2016 10:37:19 -0800 Subject: [PATCH 09/10] explictly remove Agent from NIS for other Agents --- domain-server/src/DomainGatekeeper.cpp | 9 ++++++++- domain-server/src/DomainServer.cpp | 12 +++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index abe7ed176a..051465efd2 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -109,7 +109,14 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer(node->getLinkedData()); nodeData->setSendingSockAddr(message->getSenderSockAddr()); - nodeData->setNodeInterestSet(nodeConnection.interestList.toSet()); + + // guard against patched agents asking to hear about other agents + auto safeInterestSet = nodeConnection.interestList.toSet(); + if (nodeConnection.nodeType == NodeType::Agent) { + safeInterestSet.remove(NodeType::Agent); + } + + nodeData->setNodeInterestSet(safeInterestSet); nodeData->setPlaceName(nodeConnection.placeName); // signal that we just connected a node so the DomainServer can get it a list diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 7d031c5be5..6668ed54dc 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -843,7 +843,14 @@ void DomainServer::processListRequestPacket(QSharedPointer mess // update the NodeInterestSet in case there have been any changes DomainServerNodeData* nodeData = reinterpret_cast(sendingNode->getLinkedData()); - nodeData->setNodeInterestSet(nodeRequestData.interestList.toSet()); + + // guard against patched agents asking to hear about other agents + auto safeInterestSet = nodeRequestData.interestList.toSet(); + if (sendingNode->getType() == NodeType::Agent) { + safeInterestSet.remove(NodeType::Agent); + } + + nodeData->setNodeInterestSet(safeInterestSet); // update the connecting hostname in case it has changed nodeData->setPlaceName(nodeRequestData.placeName); @@ -951,8 +958,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif // if this authenticated node has any interest types, send back those nodes as well limitedNodeList->eachNode([&](const SharedNodePointer& otherNode){ if (otherNode->getUUID() != node->getUUID() - && nodeInterestSet.contains(otherNode->getType()) - && (node->getType() != NodeType::Agent || otherNode->getType() != NodeType::Agent)) { + && nodeInterestSet.contains(otherNode->getType())) { // since we're about to add a node to the packet we start a segment domainListPackets->startSegment(); From 40914d979087db2d787a96c5d7cb3b6f60197dac Mon Sep 17 00:00:00 2001 From: David Kelly Date: Tue, 15 Nov 2016 12:58:51 -0800 Subject: [PATCH 10/10] Logging to help find audio device issue We sometimes show duplicate devices in Audio > Devices menu. But I cannot reproduce it, so maybe best thing is log timing and contents of input and output device arrays, to try to run down where the issue is. --- scripts/system/selectAudioDevice.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/system/selectAudioDevice.js b/scripts/system/selectAudioDevice.js index a44295fd3c..c618909e8c 100644 --- a/scripts/system/selectAudioDevice.js +++ b/scripts/system/selectAudioDevice.js @@ -63,6 +63,7 @@ function setupAudioMenus() { selectedOutputDevice = outputDeviceSetting; } } + print("audio output devices: " + outputDevices); for(var i = 0; i < outputDevices.length; i++) { var thisDeviceSelected = (outputDevices[i] == selectedOutputDevice); var menuItem = "Use " + outputDevices[i] + " for Output"; @@ -87,6 +88,7 @@ function setupAudioMenus() { selectedInputDevice = inputDeviceSetting; } } + print("audio input devices: " + inputDevices); for(var i = 0; i < inputDevices.length; i++) { var thisDeviceSelected = (inputDevices[i] == selectedInputDevice); var menuItem = "Use " + inputDevices[i] + " for Input"; @@ -103,13 +105,17 @@ function setupAudioMenus() { } function onDevicechanged() { + print("audio devices changed, removing Audio > Devices menu..."); Menu.removeMenu("Audio > Devices"); + print("now setting up Audio > Devices menu"); setupAudioMenus(); } // Have a small delay before the menu's get setup and the audio devices can switch to the last selected ones Script.setTimeout(function () { + print("connecting deviceChanged"); AudioDevice.deviceChanged.connect(onDevicechanged); + print("setting up Audio > Devices menu for first time"); setupAudioMenus(); }, 5000);