From a4ad78c33759372722198a4180e262e8b8208533 Mon Sep 17 00:00:00 2001 From: milad Date: Thu, 14 Nov 2019 11:28:37 -0800 Subject: [PATCH] comment updates and extra includes removed --- .../ScreenshareScriptingInterface.cpp | 90 +++++++++++++------ .../scripting/ScreenshareScriptingInterface.h | 8 +- 2 files changed, 65 insertions(+), 33 deletions(-) diff --git a/interface/src/scripting/ScreenshareScriptingInterface.cpp b/interface/src/scripting/ScreenshareScriptingInterface.cpp index 8348e96f10..0ac18769f7 100644 --- a/interface/src/scripting/ScreenshareScriptingInterface.cpp +++ b/interface/src/scripting/ScreenshareScriptingInterface.cpp @@ -9,37 +9,27 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include -#include #include #include +#include #include #include -#include -#include -#include -#include -#include #include #include -#include -#include +#include #include #include "EntityScriptingInterface.h" #include "ScreenshareScriptingInterface.h" -#include -#include -#include - ScreenshareScriptingInterface::ScreenshareScriptingInterface() { auto esi = DependencyManager::get(); if (!esi) { return; } + // This signal/slot connection is used when the screen share local web entity sends an event bridge message. QObject::connect(esi.data(), &EntityScriptingInterface::webEventReceived, this, &ScreenshareScriptingInterface::onWebEventReceived); }; @@ -55,27 +45,35 @@ static const glm::vec3 LOCAL_SCREENSHARE_WEB_ENTITY_LOCAL_POSITION(0.0f, -0.0862 static const glm::vec3 LOCAL_SCREENSHARE_WEB_ENTITY_DIMENSIONS(4.0419f, 2.2735f, 0.0100f); static const QString LOCAL_SCREENSHARE_WEB_ENTITY_URL = "https://hifi-content.s3.amazonaws.com/Experiences/Releases/usefulUtilities/smartBoard/screenshareViewer/screenshareClient.html"; +static const QString LOCAL_SCREENSHARE_WEB_ENTITY_HOST_TYPE ="local"; void ScreenshareScriptingInterface::startScreenshare(const QUuid& screenshareZoneID, const QUuid& smartboardEntityID, const bool& isPresenter) { + // We must start a new QProcess from the main thread. if (QThread::currentThread() != thread()) { - // We must start a new QProcess from the main thread. QMetaObject::invokeMethod(this, "startScreenshare", Q_ARG(const QUuid&, screenshareZoneID), Q_ARG(const QUuid&, smartboardEntityID), Q_ARG(const bool&, isPresenter)); return; } + // These three private member variables are set now so that they may be used later during asynchronous + // callbacks. _screenshareZoneID = screenshareZoneID; _smartboardEntityID = smartboardEntityID; _isPresenter = isPresenter; + // If we are presenting, and the screenshare process is already running, don't do anything else here. if (_isPresenter && _screenshareProcess && _screenshareProcess->state() != QProcess::NotRunning) { return; } + // If we're presenting... if (_isPresenter) { + // ...make sure we first reset this `std::unique_ptr`. _screenshareProcess.reset(new QProcess(this)); + // Ensure that the screenshare executable exists where we expect it to. + // Error out and reset the screen share state machine if the executable doesn't exist. QFileInfo screenshareExecutable(SCREENSHARE_EXE_PATH); if (!screenshareExecutable.exists() || !screenshareExecutable.isFile()) { qDebug() << "Screenshare executable doesn't exist at" << SCREENSHARE_EXE_PATH; @@ -85,6 +83,7 @@ void ScreenshareScriptingInterface::startScreenshare(const QUuid& screenshareZon } } + // Don't continue with any more of this logic if we can't get the `AccountManager` or `AddressManager`. auto accountManager = DependencyManager::get(); if (!accountManager) { return; @@ -94,9 +93,17 @@ void ScreenshareScriptingInterface::startScreenshare(const QUuid& screenshareZon return; } + // Construct and send a request to the Metaverse to obtain the information + // necessary to start the screen sharing process. + // This request requires: + // 1. The domain ID of the domain in which the user's avatar is present + // 2. User authentication information that is automatically included when `sendRequest()` is passed + // with the `AccountManagerAuth::Required` argument. + // Note that this request will only return successfully if the Domain Server has already registered + // the user paired with the current domain with the Metaverse. + // See `DomainServer::screensharePresence()` for more info about that. QString currentDomainID = uuidStringWithoutCurlyBraces(addressManager->getDomainID()); QString requestURLPath = "api/v1/domains/%1/screenshare"; - JSONCallbackParameters callbackParams; callbackParams.callbackReceiver = this; callbackParams.jsonCallbackMethod = "handleSuccessfulScreenshareInfoGet"; @@ -110,22 +117,29 @@ void ScreenshareScriptingInterface::startScreenshare(const QUuid& screenshareZon } void ScreenshareScriptingInterface::stopScreenshare() { + // We can only deal with our Screen Share `QProcess` on the main thread. if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "stopScreenshare"); return; } + // If the Screen Share process is running... if (_screenshareProcess && _screenshareProcess->state() != QProcess::NotRunning) { + //...terminate it and make sure that scripts know we terminated it by emitting + // `screenshareProcessTerminated()`. _screenshareProcess->terminate(); emit screenshareProcessTerminated(); } + // Delete the local web entity if we know about it here. if (!_screenshareViewerLocalWebEntityUUID.isNull()) { auto esi = DependencyManager::get(); if (esi) { esi->deleteEntity(_screenshareViewerLocalWebEntityUUID); } } + + // Reset all private member variables related to screen share here. _screenshareViewerLocalWebEntityUUID = "{00000000-0000-0000-0000-000000000000}"; _token = ""; _projectAPIKey = ""; @@ -133,12 +147,14 @@ void ScreenshareScriptingInterface::stopScreenshare() { _isPresenter = false; } +// Called when the Metaverse returns the information necessary to start/view a screen share. void ScreenshareScriptingInterface::handleSuccessfulScreenshareInfoGet(QNetworkReply* reply) { + // Read the reply and get it into a format we understand. QString answer = reply->readAll(); - QByteArray answerByteArray = answer.toUtf8(); QJsonDocument answerJSONObject = QJsonDocument::fromJson(answerByteArray); + // This Metaverse endpoint will always return a status key/value pair of "success" if things went well. QString status = answerJSONObject["status"].toString(); if (status != "success") { qDebug() << "Error when retrieving screenshare info via HTTP. Error:" << reply->errorString(); @@ -147,10 +163,12 @@ void ScreenshareScriptingInterface::handleSuccessfulScreenshareInfoGet(QNetworkR return; } + // Store the information necessary to start/view a screen share in these private member variables. _token = answerJSONObject["token"].toString(); _projectAPIKey = answerJSONObject["projectApiKey"].toString(); _sessionID = answerJSONObject["sessionID"].toString(); + // Make sure we have all of the info that we need. if (_token.isEmpty() || _projectAPIKey.isEmpty() || _sessionID.isEmpty()) { qDebug() << "Not all Screen Share information was retrieved from the backend. Stopping..."; stopScreenshare(); @@ -158,6 +176,10 @@ void ScreenshareScriptingInterface::handleSuccessfulScreenshareInfoGet(QNetworkR return; } + // If we're presenting: + // 1. Build a list of arguments that we're going to pass to the screen share Electron app. + // 2. Make sure we connect a signal/slot to know when the user quits the Electron app. + // 3. Start the screen share Electron app with the list of args from (1). if (_isPresenter) { QStringList arguments; arguments << " "; @@ -174,12 +196,7 @@ void ScreenshareScriptingInterface::handleSuccessfulScreenshareInfoGet(QNetworkR _screenshareProcess->start(SCREENSHARE_EXE_PATH, arguments); } - if (!_screenshareViewerLocalWebEntityUUID.isNull()) { - stopScreenshare(); - emit screenshareError(); - return; - } - + // Make sure we can grab the entity scripting interface. Error out if we can't. auto esi = DependencyManager::get(); if (!esi) { stopScreenshare(); @@ -187,6 +204,12 @@ void ScreenshareScriptingInterface::handleSuccessfulScreenshareInfoGet(QNetworkR return; } + // If, for some reason, we already have a record of a screen share local Web entity, delete it. + if (!_screenshareViewerLocalWebEntityUUID.isNull()) { + esi->deleteEntity(_screenshareViewerLocalWebEntityUUID); + } + + // Set up the entity properties associated with the screen share local Web entity. EntityItemProperties localScreenshareWebEntityProps; localScreenshareWebEntityProps.setType(LOCAL_SCREENSHARE_WEB_ENTITY_TYPE); localScreenshareWebEntityProps.setMaxFPS(LOCAL_SCREENSHARE_WEB_ENTITY_FPS); @@ -195,14 +218,16 @@ void ScreenshareScriptingInterface::handleSuccessfulScreenshareInfoGet(QNetworkR localScreenshareWebEntityProps.setParentID(_smartboardEntityID); localScreenshareWebEntityProps.setDimensions(LOCAL_SCREENSHARE_WEB_ENTITY_DIMENSIONS); - // The lines below will be used when writing the feature to ensure that the smartboard can be of any arbitrary size. + // The lines below will be used when writing the feature to support scaling the Smartboard entity to any arbitrary size. //EntityPropertyFlags desiredSmartboardProperties; //desiredSmartboardProperties += PROP_POSITION; //desiredSmartboardProperties += PROP_DIMENSIONS; - //EntityItemProperties smartboardProps = esi->getEntityProperties(smartboardEntityID, desiredSmartboardProperties); - - QString hostType = "local"; - _screenshareViewerLocalWebEntityUUID = esi->addEntity(localScreenshareWebEntityProps, hostType); + //EntityItemProperties smartboardProps = esi->getEntityProperties(_smartboardEntityID, desiredSmartboardProperties); + + // Add the screen share local Web entity to Interface's entity tree. + // When the Web entity loads the page specified by `LOCAL_SCREENSHARE_WEB_ENTITY_URL`, it will broadcast an Event Bridge + // message, which we will consume inside `ScreenshareScriptingInterface::onWebEventReceived()`. + _screenshareViewerLocalWebEntityUUID = esi->addEntity(localScreenshareWebEntityProps, LOCAL_SCREENSHARE_WEB_ENTITY_HOST_TYPE); } void ScreenshareScriptingInterface::handleFailedScreenshareInfoGet(QNetworkReply* reply) { @@ -211,21 +236,31 @@ void ScreenshareScriptingInterface::handleFailedScreenshareInfoGet(QNetworkReply emit screenshareError(); } +// This function will handle _all_ web events received via `EntityScriptingInterface::webEventReceived()`, including +// those not related to screen sharing. void ScreenshareScriptingInterface::onWebEventReceived(const QUuid& entityID, const QVariant& message) { + // Bail early if the entity that sent the Web event isn't the one we care about. if (entityID == _screenshareViewerLocalWebEntityUUID) { + // Bail early if we can't grab the Entity Scripting Interface. auto esi = DependencyManager::get(); if (!esi) { return; } + // Web events received from the screen share Web JS will always be in stringified JSON format. QByteArray jsonByteArray = QVariant(message).toString().toUtf8(); QJsonDocument jsonObject = QJsonDocument::fromJson(jsonByteArray); + // It should never happen where the screen share Web JS sends a message without the `app` key's value + // set to "screenshare". if (jsonObject["app"] != "screenshare") { return; } + // The screen share Web JS only sends a message with one method: "eventBridgeReady". Handle it here. if (jsonObject["method"] == "eventBridgeReady") { + // Stuff a JSON object full of information necessary for the screen share local Web entity + // to connect to the screen share session associated with the room in which the user's avatar is standing. QJsonObject responseObject; responseObject.insert("app", "screenshare"); responseObject.insert("method", "receiveConnectionInfo"); @@ -235,7 +270,6 @@ void ScreenshareScriptingInterface::onWebEventReceived(const QUuid& entityID, co responseObjectData.insert("sessionID", _sessionID); responseObject.insert("data", responseObjectData); - auto esi = DependencyManager::get(); esi->emitScriptEvent(_screenshareViewerLocalWebEntityUUID, responseObject.toVariantMap()); } } diff --git a/interface/src/scripting/ScreenshareScriptingInterface.h b/interface/src/scripting/ScreenshareScriptingInterface.h index b3d39f8f25..d64daa6c6b 100644 --- a/interface/src/scripting/ScreenshareScriptingInterface.h +++ b/interface/src/scripting/ScreenshareScriptingInterface.h @@ -12,13 +12,11 @@ #ifndef hifi_ScreenshareScriptingInterface_h #define hifi_ScreenshareScriptingInterface_h -// #include +#include #include #include -// #include -// #include -// #include -#include +#include + #include class ScreenshareScriptingInterface : public QObject, public Dependency {