History

comment updates and extra includes removed

milad committed 7 years ago

Commit a4ad78c1 parent 82c85b52 files · +65 −33Browse files

Showing the changes to interface/src/scripting/ScreenshareScriptingInterface.cpp (1 of 2 files). Show all

Unified · Split

interface/src/scripting/ScreenshareScriptingInterface.cpp+62−28
@@ -9,37 +9,27 @@ · 8 unmodified lines
99 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
1010 //
1111
12−#include <QCoreApplication>
13−#include <QDesktopServices>
1412 #include <QJsonDocument>
1513 #include <QJsonObject>
14+#include <QNetworkAccessManager>
1615 #include <QThread>
1716 #include <QUrl>
18−#include <QObject>
19−#include <QNetworkAccessManager>
20−#include <QNetworkReply>
21−#include <QNetworkRequest>
22−#include <QProcessEnvironment>
2317
2418 #include <AccountManager.h>
2519 #include <AddressManager.h>
26−#include <EntityTreeRenderer.h>
27−#include <EntityTree.h>
20+#include <DependencyManager.h>
2821 #include <UUID.h>
2922
3023 #include "EntityScriptingInterface.h"
3124 #include "ScreenshareScriptingInterface.h"
3225
33−#include <RenderableEntityItem.h>
34−#include <RenderableTextEntityItem.h>
35−#include <RenderableWebEntityItem.h>
36−
3726 ScreenshareScriptingInterface::ScreenshareScriptingInterface() {
3827 auto esi = DependencyManager::get<EntityScriptingInterface>();
3928 if (!esi) {
4029 return;
4130 }
4231
32+ // This signal/slot connection is used when the screen share local web entity sends an event bridge message.
4333 QObject::connect(esi.data(), &EntityScriptingInterface::webEventReceived, this, &ScreenshareScriptingInterface::onWebEventReceived);
4434 };
4535
@@ -55,27 +45,35 @@ static const glm::vec3 LOCAL_SCREENSHARE_WEB_ENTITY_LOCAL_POSITION(0.0f, -0.0862 · 9 unmodified lines
5545 static const glm::vec3 LOCAL_SCREENSHARE_WEB_ENTITY_DIMENSIONS(4.0419f, 2.2735f, 0.0100f);
5646 static const QString LOCAL_SCREENSHARE_WEB_ENTITY_URL =
5747 "https://hifi-content.s3.amazonaws.com/Experiences/Releases/usefulUtilities/smartBoard/screenshareViewer/screenshareClient.html";
48+static const QString LOCAL_SCREENSHARE_WEB_ENTITY_HOST_TYPE ="local";
5849 void ScreenshareScriptingInterface::startScreenshare(const QUuid& screenshareZoneID,
5950 const QUuid& smartboardEntityID,
6051 const bool& isPresenter) {
52+ // We must start a new QProcess from the main thread.
6153 if (QThread::currentThread() != thread()) {
62− // We must start a new QProcess from the main thread.
6354 QMetaObject::invokeMethod(this, "startScreenshare", Q_ARG(const QUuid&, screenshareZoneID),
6455 Q_ARG(const QUuid&, smartboardEntityID), Q_ARG(const bool&, isPresenter));
6556 return;
6657 }
6758
59+ // These three private member variables are set now so that they may be used later during asynchronous
60+ // callbacks.
6861 _screenshareZoneID = screenshareZoneID;
6962 _smartboardEntityID = smartboardEntityID;
7063 _isPresenter = isPresenter;
7164
65+ // If we are presenting, and the screenshare process is already running, don't do anything else here.
7266 if (_isPresenter && _screenshareProcess && _screenshareProcess->state() != QProcess::NotRunning) {
7367 return;
7468 }
7569
70+ // If we're presenting...
7671 if (_isPresenter) {
72+ // ...make sure we first reset this `std::unique_ptr`.
7773 _screenshareProcess.reset(new QProcess(this));
7874
75+ // Ensure that the screenshare executable exists where we expect it to.
76+ // Error out and reset the screen share state machine if the executable doesn't exist.
7977 QFileInfo screenshareExecutable(SCREENSHARE_EXE_PATH);
8078 if (!screenshareExecutable.exists() || !screenshareExecutable.isFile()) {
8179 qDebug() << "Screenshare executable doesn't exist at" << SCREENSHARE_EXE_PATH;
@@ -85,6 +83,7 @@ void ScreenshareScriptingInterface::startScreenshare(const QUuid& screenshareZon · 3 unmodified lines
8583 }
8684 }
8785
86+ // Don't continue with any more of this logic if we can't get the `AccountManager` or `AddressManager`.
8887 auto accountManager = DependencyManager::get<AccountManager>();
8988 if (!accountManager) {
9089 return;
@@ -94,9 +93,17 @@ void ScreenshareScriptingInterface::startScreenshare(const QUuid& screenshareZon · 3 unmodified lines
9493 return;
9594 }
9695
96+ // Construct and send a request to the Metaverse to obtain the information
97+ // necessary to start the screen sharing process.
98+ // This request requires:
99+ // 1. The domain ID of the domain in which the user's avatar is present
100+ // 2. User authentication information that is automatically included when `sendRequest()` is passed
101+ // with the `AccountManagerAuth::Required` argument.
102+ // Note that this request will only return successfully if the Domain Server has already registered
103+ // the user paired with the current domain with the Metaverse.
104+ // See `DomainServer::screensharePresence()` for more info about that.
97105 QString currentDomainID = uuidStringWithoutCurlyBraces(addressManager->getDomainID());
98106 QString requestURLPath = "api/v1/domains/%1/screenshare";
99−
100107 JSONCallbackParameters callbackParams;
101108 callbackParams.callbackReceiver = this;
102109 callbackParams.jsonCallbackMethod = "handleSuccessfulScreenshareInfoGet";
@@ -110,22 +117,29 @@ void ScreenshareScriptingInterface::startScreenshare(const QUuid& screenshareZon · 7 unmodified lines
110117 }
111118
112119 void ScreenshareScriptingInterface::stopScreenshare() {
120+ // We can only deal with our Screen Share `QProcess` on the main thread.
113121 if (QThread::currentThread() != thread()) {
114122 QMetaObject::invokeMethod(this, "stopScreenshare");
115123 return;
116124 }
117125
126+ // If the Screen Share process is running...
118127 if (_screenshareProcess && _screenshareProcess->state() != QProcess::NotRunning) {
128+ //...terminate it and make sure that scripts know we terminated it by emitting
129+ // `screenshareProcessTerminated()`.
119130 _screenshareProcess->terminate();
120131 emit screenshareProcessTerminated();
121132 }
122133
134+ // Delete the local web entity if we know about it here.
123135 if (!_screenshareViewerLocalWebEntityUUID.isNull()) {
124136 auto esi = DependencyManager::get<EntityScriptingInterface>();
125137 if (esi) {
126138 esi->deleteEntity(_screenshareViewerLocalWebEntityUUID);
127139 }
128140 }
141+
142+ // Reset all private member variables related to screen share here.
129143 _screenshareViewerLocalWebEntityUUID = "{00000000-0000-0000-0000-000000000000}";
130144 _token = "";
131145 _projectAPIKey = "";
@@ -133,12 +147,14 @@ void ScreenshareScriptingInterface::stopScreenshare() { · 1 unmodified line
133147 _isPresenter = false;
134148 }
135149
150+// Called when the Metaverse returns the information necessary to start/view a screen share.
136151 void ScreenshareScriptingInterface::handleSuccessfulScreenshareInfoGet(QNetworkReply* reply) {
152+ // Read the reply and get it into a format we understand.
137153 QString answer = reply->readAll();
138−
139154 QByteArray answerByteArray = answer.toUtf8();
140155 QJsonDocument answerJSONObject = QJsonDocument::fromJson(answerByteArray);
141156
157+ // This Metaverse endpoint will always return a status key/value pair of "success" if things went well.
142158 QString status = answerJSONObject["status"].toString();
143159 if (status != "success") {
144160 qDebug() << "Error when retrieving screenshare info via HTTP. Error:" << reply->errorString();
@@ -147,10 +163,12 @@ void ScreenshareScriptingInterface::handleSuccessfulScreenshareInfoGet(QNetworkR · 2 unmodified lines
147163 return;
148164 }
149165
166+ // Store the information necessary to start/view a screen share in these private member variables.
150167 _token = answerJSONObject["token"].toString();
151168 _projectAPIKey = answerJSONObject["projectApiKey"].toString();
152169 _sessionID = answerJSONObject["sessionID"].toString();
153170
171+ // Make sure we have all of the info that we need.
154172 if (_token.isEmpty() || _projectAPIKey.isEmpty() || _sessionID.isEmpty()) {
155173 qDebug() << "Not all Screen Share information was retrieved from the backend. Stopping...";
156174 stopScreenshare();
@@ -158,6 +176,10 @@ void ScreenshareScriptingInterface::handleSuccessfulScreenshareInfoGet(QNetworkR · 1 unmodified line
158176 return;
159177 }
160178
179+ // If we're presenting:
180+ // 1. Build a list of arguments that we're going to pass to the screen share Electron app.
181+ // 2. Make sure we connect a signal/slot to know when the user quits the Electron app.
182+ // 3. Start the screen share Electron app with the list of args from (1).
161183 if (_isPresenter) {
162184 QStringList arguments;
163185 arguments << " ";
@@ -174,12 +196,7 @@ void ScreenshareScriptingInterface::handleSuccessfulScreenshareInfoGet(QNetworkR · 10 unmodified lines
174196 _screenshareProcess->start(SCREENSHARE_EXE_PATH, arguments);
175197 }
176198
177− if (!_screenshareViewerLocalWebEntityUUID.isNull()) {
178− stopScreenshare();
179− emit screenshareError();
180− return;
181− }
182−
199+ // Make sure we can grab the entity scripting interface. Error out if we can't.
183200 auto esi = DependencyManager::get<EntityScriptingInterface>();
184201 if (!esi) {
185202 stopScreenshare();
@@ -187,6 +204,12 @@ void ScreenshareScriptingInterface::handleSuccessfulScreenshareInfoGet(QNetworkR · 1 unmodified line
187204 return;
188205 }
189206
207+ // If, for some reason, we already have a record of a screen share local Web entity, delete it.
208+ if (!_screenshareViewerLocalWebEntityUUID.isNull()) {
209+ esi->deleteEntity(_screenshareViewerLocalWebEntityUUID);
210+ }
211+
212+ // Set up the entity properties associated with the screen share local Web entity.
190213 EntityItemProperties localScreenshareWebEntityProps;
191214 localScreenshareWebEntityProps.setType(LOCAL_SCREENSHARE_WEB_ENTITY_TYPE);
192215 localScreenshareWebEntityProps.setMaxFPS(LOCAL_SCREENSHARE_WEB_ENTITY_FPS);
@@ -195,14 +218,16 @@ void ScreenshareScriptingInterface::handleSuccessfulScreenshareInfoGet(QNetworkR · 2 unmodified lines
195218 localScreenshareWebEntityProps.setParentID(_smartboardEntityID);
196219 localScreenshareWebEntityProps.setDimensions(LOCAL_SCREENSHARE_WEB_ENTITY_DIMENSIONS);
197220
198− // The lines below will be used when writing the feature to ensure that the smartboard can be of any arbitrary size.
221+ // The lines below will be used when writing the feature to support scaling the Smartboard entity to any arbitrary size.
199222 //EntityPropertyFlags desiredSmartboardProperties;
200223 //desiredSmartboardProperties += PROP_POSITION;
201224 //desiredSmartboardProperties += PROP_DIMENSIONS;
202− //EntityItemProperties smartboardProps = esi->getEntityProperties(smartboardEntityID, desiredSmartboardProperties);
203−
204− QString hostType = "local";
205− _screenshareViewerLocalWebEntityUUID = esi->addEntity(localScreenshareWebEntityProps, hostType);
225+ //EntityItemProperties smartboardProps = esi->getEntityProperties(_smartboardEntityID, desiredSmartboardProperties);
226+
227+ // Add the screen share local Web entity to Interface's entity tree.
228+ // When the Web entity loads the page specified by `LOCAL_SCREENSHARE_WEB_ENTITY_URL`, it will broadcast an Event Bridge
229+ // message, which we will consume inside `ScreenshareScriptingInterface::onWebEventReceived()`.
230+ _screenshareViewerLocalWebEntityUUID = esi->addEntity(localScreenshareWebEntityProps, LOCAL_SCREENSHARE_WEB_ENTITY_HOST_TYPE);
206231 }
207232
208233 void ScreenshareScriptingInterface::handleFailedScreenshareInfoGet(QNetworkReply* reply) {
@@ -211,21 +236,31 @@ void ScreenshareScriptingInterface::handleFailedScreenshareInfoGet(QNetworkReply · 2 unmodified lines
211236 emit screenshareError();
212237 }
213238
239+// This function will handle _all_ web events received via `EntityScriptingInterface::webEventReceived()`, including
240+// those not related to screen sharing.
214241 void ScreenshareScriptingInterface::onWebEventReceived(const QUuid& entityID, const QVariant& message) {
242+ // Bail early if the entity that sent the Web event isn't the one we care about.
215243 if (entityID == _screenshareViewerLocalWebEntityUUID) {
244+ // Bail early if we can't grab the Entity Scripting Interface.
216245 auto esi = DependencyManager::get<EntityScriptingInterface>();
217246 if (!esi) {
218247 return;
219248 }
220249
250+ // Web events received from the screen share Web JS will always be in stringified JSON format.
221251 QByteArray jsonByteArray = QVariant(message).toString().toUtf8();
222252 QJsonDocument jsonObject = QJsonDocument::fromJson(jsonByteArray);
223253
254+ // It should never happen where the screen share Web JS sends a message without the `app` key's value
255+ // set to "screenshare".
224256 if (jsonObject["app"] != "screenshare") {
225257 return;
226258 }
227259
260+ // The screen share Web JS only sends a message with one method: "eventBridgeReady". Handle it here.
228261 if (jsonObject["method"] == "eventBridgeReady") {
262+ // Stuff a JSON object full of information necessary for the screen share local Web entity
263+ // to connect to the screen share session associated with the room in which the user's avatar is standing.
229264 QJsonObject responseObject;
230265 responseObject.insert("app", "screenshare");
231266 responseObject.insert("method", "receiveConnectionInfo");
@@ -235,7 +270,6 @@ void ScreenshareScriptingInterface::onWebEventReceived(const QUuid& entityID, co · 3 unmodified lines
235270 responseObjectData.insert("sessionID", _sessionID);
236271 responseObject.insert("data", responseObjectData);
237272
238− auto esi = DependencyManager::get<EntityScriptingInterface>();
239273 esi->emitScriptEvent(_screenshareViewerLocalWebEntityUUID, responseObject.toVariantMap());
240274 }
241275 }