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
9// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html9// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10//10//
11 11
12#include <QCoreApplication>
13#include <QDesktopServices>
14#include <QJsonDocument>12#include <QJsonDocument>
15#include <QJsonObject>13#include <QJsonObject>
14#include <QNetworkAccessManager>
16#include <QThread>15#include <QThread>
17#include <QUrl>16#include <QUrl>
18#include <QObject>
19#include <QNetworkAccessManager>
20#include <QNetworkReply>
21#include <QNetworkRequest>
22#include <QProcessEnvironment>
23 17
24#include <AccountManager.h>18#include <AccountManager.h>
25#include <AddressManager.h>19#include <AddressManager.h>
26#include <EntityTreeRenderer.h>20#include <DependencyManager.h>
27#include <EntityTree.h>
28#include <UUID.h>21#include <UUID.h>
29 22
30#include "EntityScriptingInterface.h"23#include "EntityScriptingInterface.h"
31#include "ScreenshareScriptingInterface.h"24#include "ScreenshareScriptingInterface.h"
32 25
33#include <RenderableEntityItem.h>
34#include <RenderableTextEntityItem.h>
35#include <RenderableWebEntityItem.h>
36
37ScreenshareScriptingInterface::ScreenshareScriptingInterface() {26ScreenshareScriptingInterface::ScreenshareScriptingInterface() {
38 auto esi = DependencyManager::get<EntityScriptingInterface>();27 auto esi = DependencyManager::get<EntityScriptingInterface>();
39 if (!esi) {28 if (!esi) {
40 return;29 return;
41 }30 }
42 31
32 // This signal/slot connection is used when the screen share local web entity sends an event bridge message.
43 QObject::connect(esi.data(), &EntityScriptingInterface::webEventReceived, this, &ScreenshareScriptingInterface::onWebEventReceived);33 QObject::connect(esi.data(), &EntityScriptingInterface::webEventReceived, this, &ScreenshareScriptingInterface::onWebEventReceived);
44};34};
45 35
@@ -55,27 +45,35 @@ static const glm::vec3 LOCAL_SCREENSHARE_WEB_ENTITY_LOCAL_POSITION(0.0f, -0.0862 · 9 unmodified lines
55static const glm::vec3 LOCAL_SCREENSHARE_WEB_ENTITY_DIMENSIONS(4.0419f, 2.2735f, 0.0100f);45static const glm::vec3 LOCAL_SCREENSHARE_WEB_ENTITY_DIMENSIONS(4.0419f, 2.2735f, 0.0100f);
56static const QString LOCAL_SCREENSHARE_WEB_ENTITY_URL =46static const QString LOCAL_SCREENSHARE_WEB_ENTITY_URL =
57 "https://hifi-content.s3.amazonaws.com/Experiences/Releases/usefulUtilities/smartBoard/screenshareViewer/screenshareClient.html";47 "https://hifi-content.s3.amazonaws.com/Experiences/Releases/usefulUtilities/smartBoard/screenshareViewer/screenshareClient.html";
48static const QString LOCAL_SCREENSHARE_WEB_ENTITY_HOST_TYPE ="local";
58void ScreenshareScriptingInterface::startScreenshare(const QUuid& screenshareZoneID,49void ScreenshareScriptingInterface::startScreenshare(const QUuid& screenshareZoneID,
59 const QUuid& smartboardEntityID,50 const QUuid& smartboardEntityID,
60 const bool& isPresenter) {51 const bool& isPresenter) {
52 // We must start a new QProcess from the main thread.
61 if (QThread::currentThread() != thread()) {53 if (QThread::currentThread() != thread()) {
62 // We must start a new QProcess from the main thread.
63 QMetaObject::invokeMethod(this, "startScreenshare", Q_ARG(const QUuid&, screenshareZoneID),54 QMetaObject::invokeMethod(this, "startScreenshare", Q_ARG(const QUuid&, screenshareZoneID),
64 Q_ARG(const QUuid&, smartboardEntityID), Q_ARG(const bool&, isPresenter));55 Q_ARG(const QUuid&, smartboardEntityID), Q_ARG(const bool&, isPresenter));
65 return;56 return;
66 }57 }
67 58
59 // These three private member variables are set now so that they may be used later during asynchronous
60 // callbacks.
68 _screenshareZoneID = screenshareZoneID;61 _screenshareZoneID = screenshareZoneID;
69 _smartboardEntityID = smartboardEntityID;62 _smartboardEntityID = smartboardEntityID;
70 _isPresenter = isPresenter;63 _isPresenter = isPresenter;
71 64
65 // If we are presenting, and the screenshare process is already running, don't do anything else here.
72 if (_isPresenter && _screenshareProcess && _screenshareProcess->state() != QProcess::NotRunning) {66 if (_isPresenter && _screenshareProcess && _screenshareProcess->state() != QProcess::NotRunning) {
73 return;67 return;
74 }68 }
75 69
70 // If we're presenting...
76 if (_isPresenter) {71 if (_isPresenter) {
72 // ...make sure we first reset this `std::unique_ptr`.
77 _screenshareProcess.reset(new QProcess(this));73 _screenshareProcess.reset(new QProcess(this));
78 74
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.
79 QFileInfo screenshareExecutable(SCREENSHARE_EXE_PATH);77 QFileInfo screenshareExecutable(SCREENSHARE_EXE_PATH);
80 if (!screenshareExecutable.exists() || !screenshareExecutable.isFile()) {78 if (!screenshareExecutable.exists() || !screenshareExecutable.isFile()) {
81 qDebug() << "Screenshare executable doesn't exist at" << SCREENSHARE_EXE_PATH;79 qDebug() << "Screenshare executable doesn't exist at" << SCREENSHARE_EXE_PATH;
@@ -85,6 +83,7 @@ void ScreenshareScriptingInterface::startScreenshare(const QUuid& screenshareZon · 3 unmodified lines
85 }83 }
86 }84 }
87 85
86 // Don't continue with any more of this logic if we can't get the `AccountManager` or `AddressManager`.
88 auto accountManager = DependencyManager::get<AccountManager>();87 auto accountManager = DependencyManager::get<AccountManager>();
89 if (!accountManager) {88 if (!accountManager) {
90 return;89 return;
@@ -94,9 +93,17 @@ void ScreenshareScriptingInterface::startScreenshare(const QUuid& screenshareZon · 3 unmodified lines
94 return;93 return;
95 }94 }
96 95
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.
97 QString currentDomainID = uuidStringWithoutCurlyBraces(addressManager->getDomainID());105 QString currentDomainID = uuidStringWithoutCurlyBraces(addressManager->getDomainID());
98 QString requestURLPath = "api/v1/domains/%1/screenshare";106 QString requestURLPath = "api/v1/domains/%1/screenshare";
99
100 JSONCallbackParameters callbackParams;107 JSONCallbackParameters callbackParams;
101 callbackParams.callbackReceiver = this;108 callbackParams.callbackReceiver = this;
102 callbackParams.jsonCallbackMethod = "handleSuccessfulScreenshareInfoGet";109 callbackParams.jsonCallbackMethod = "handleSuccessfulScreenshareInfoGet";
@@ -110,22 +117,29 @@ void ScreenshareScriptingInterface::startScreenshare(const QUuid& screenshareZon · 7 unmodified lines
110}117}
111 118
112void ScreenshareScriptingInterface::stopScreenshare() {119void ScreenshareScriptingInterface::stopScreenshare() {
120 // We can only deal with our Screen Share `QProcess` on the main thread.
113 if (QThread::currentThread() != thread()) {121 if (QThread::currentThread() != thread()) {
114 QMetaObject::invokeMethod(this, "stopScreenshare");122 QMetaObject::invokeMethod(this, "stopScreenshare");
115 return;123 return;
116 }124 }
117 125
126 // If the Screen Share process is running...
118 if (_screenshareProcess && _screenshareProcess->state() != QProcess::NotRunning) {127 if (_screenshareProcess && _screenshareProcess->state() != QProcess::NotRunning) {
128 //...terminate it and make sure that scripts know we terminated it by emitting
129 // `screenshareProcessTerminated()`.
119 _screenshareProcess->terminate();130 _screenshareProcess->terminate();
120 emit screenshareProcessTerminated();131 emit screenshareProcessTerminated();
121 }132 }
122 133
134 // Delete the local web entity if we know about it here.
123 if (!_screenshareViewerLocalWebEntityUUID.isNull()) {135 if (!_screenshareViewerLocalWebEntityUUID.isNull()) {
124 auto esi = DependencyManager::get<EntityScriptingInterface>();136 auto esi = DependencyManager::get<EntityScriptingInterface>();
125 if (esi) {137 if (esi) {
126 esi->deleteEntity(_screenshareViewerLocalWebEntityUUID);138 esi->deleteEntity(_screenshareViewerLocalWebEntityUUID);
127 }139 }
128 }140 }
141
142 // Reset all private member variables related to screen share here.
129 _screenshareViewerLocalWebEntityUUID = "{00000000-0000-0000-0000-000000000000}";143 _screenshareViewerLocalWebEntityUUID = "{00000000-0000-0000-0000-000000000000}";
130 _token = "";144 _token = "";
131 _projectAPIKey = "";145 _projectAPIKey = "";
@@ -133,12 +147,14 @@ void ScreenshareScriptingInterface::stopScreenshare() { · 1 unmodified line
133 _isPresenter = false;147 _isPresenter = false;
134}148}
135 149
150// Called when the Metaverse returns the information necessary to start/view a screen share.
136void ScreenshareScriptingInterface::handleSuccessfulScreenshareInfoGet(QNetworkReply* reply) {151void ScreenshareScriptingInterface::handleSuccessfulScreenshareInfoGet(QNetworkReply* reply) {
152 // Read the reply and get it into a format we understand.
137 QString answer = reply->readAll();153 QString answer = reply->readAll();
138
139 QByteArray answerByteArray = answer.toUtf8();154 QByteArray answerByteArray = answer.toUtf8();
140 QJsonDocument answerJSONObject = QJsonDocument::fromJson(answerByteArray);155 QJsonDocument answerJSONObject = QJsonDocument::fromJson(answerByteArray);
141 156
157 // This Metaverse endpoint will always return a status key/value pair of "success" if things went well.
142 QString status = answerJSONObject["status"].toString();158 QString status = answerJSONObject["status"].toString();
143 if (status != "success") {159 if (status != "success") {
144 qDebug() << "Error when retrieving screenshare info via HTTP. Error:" << reply->errorString();160 qDebug() << "Error when retrieving screenshare info via HTTP. Error:" << reply->errorString();
@@ -147,10 +163,12 @@ void ScreenshareScriptingInterface::handleSuccessfulScreenshareInfoGet(QNetworkR · 2 unmodified lines
147 return;163 return;
148 }164 }
149 165
166 // Store the information necessary to start/view a screen share in these private member variables.
150 _token = answerJSONObject["token"].toString();167 _token = answerJSONObject["token"].toString();
151 _projectAPIKey = answerJSONObject["projectApiKey"].toString();168 _projectAPIKey = answerJSONObject["projectApiKey"].toString();
152 _sessionID = answerJSONObject["sessionID"].toString();169 _sessionID = answerJSONObject["sessionID"].toString();
153 170
171 // Make sure we have all of the info that we need.
154 if (_token.isEmpty() || _projectAPIKey.isEmpty() || _sessionID.isEmpty()) {172 if (_token.isEmpty() || _projectAPIKey.isEmpty() || _sessionID.isEmpty()) {
155 qDebug() << "Not all Screen Share information was retrieved from the backend. Stopping...";173 qDebug() << "Not all Screen Share information was retrieved from the backend. Stopping...";
156 stopScreenshare();174 stopScreenshare();
@@ -158,6 +176,10 @@ void ScreenshareScriptingInterface::handleSuccessfulScreenshareInfoGet(QNetworkR · 1 unmodified line
158 return;176 return;
159 }177 }
160 178
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).
161 if (_isPresenter) {183 if (_isPresenter) {
162 QStringList arguments;184 QStringList arguments;
163 arguments << " ";185 arguments << " ";
@@ -174,12 +196,7 @@ void ScreenshareScriptingInterface::handleSuccessfulScreenshareInfoGet(QNetworkR · 10 unmodified lines
174 _screenshareProcess->start(SCREENSHARE_EXE_PATH, arguments);196 _screenshareProcess->start(SCREENSHARE_EXE_PATH, arguments);
175 }197 }
176 198
177 if (!_screenshareViewerLocalWebEntityUUID.isNull()) {199 // Make sure we can grab the entity scripting interface. Error out if we can't.
178 stopScreenshare();
179 emit screenshareError();
180 return;
181 }
182
183 auto esi = DependencyManager::get<EntityScriptingInterface>();200 auto esi = DependencyManager::get<EntityScriptingInterface>();
184 if (!esi) {201 if (!esi) {
185 stopScreenshare();202 stopScreenshare();
@@ -187,6 +204,12 @@ void ScreenshareScriptingInterface::handleSuccessfulScreenshareInfoGet(QNetworkR · 1 unmodified line
187 return;204 return;
188 }205 }
189 206
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.
190 EntityItemProperties localScreenshareWebEntityProps;213 EntityItemProperties localScreenshareWebEntityProps;
191 localScreenshareWebEntityProps.setType(LOCAL_SCREENSHARE_WEB_ENTITY_TYPE);214 localScreenshareWebEntityProps.setType(LOCAL_SCREENSHARE_WEB_ENTITY_TYPE);
192 localScreenshareWebEntityProps.setMaxFPS(LOCAL_SCREENSHARE_WEB_ENTITY_FPS);215 localScreenshareWebEntityProps.setMaxFPS(LOCAL_SCREENSHARE_WEB_ENTITY_FPS);
@@ -195,14 +218,16 @@ void ScreenshareScriptingInterface::handleSuccessfulScreenshareInfoGet(QNetworkR · 2 unmodified lines
195 localScreenshareWebEntityProps.setParentID(_smartboardEntityID);218 localScreenshareWebEntityProps.setParentID(_smartboardEntityID);
196 localScreenshareWebEntityProps.setDimensions(LOCAL_SCREENSHARE_WEB_ENTITY_DIMENSIONS);219 localScreenshareWebEntityProps.setDimensions(LOCAL_SCREENSHARE_WEB_ENTITY_DIMENSIONS);
197 220
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.
199 //EntityPropertyFlags desiredSmartboardProperties;222 //EntityPropertyFlags desiredSmartboardProperties;
200 //desiredSmartboardProperties += PROP_POSITION;223 //desiredSmartboardProperties += PROP_POSITION;
201 //desiredSmartboardProperties += PROP_DIMENSIONS;224 //desiredSmartboardProperties += PROP_DIMENSIONS;
202 //EntityItemProperties smartboardProps = esi->getEntityProperties(smartboardEntityID, desiredSmartboardProperties);225 //EntityItemProperties smartboardProps = esi->getEntityProperties(_smartboardEntityID, desiredSmartboardProperties);
203 226
204 QString hostType = "local";227 // Add the screen share local Web entity to Interface's entity tree.
205 _screenshareViewerLocalWebEntityUUID = esi->addEntity(localScreenshareWebEntityProps, hostType);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);
206}231}
207 232
208void ScreenshareScriptingInterface::handleFailedScreenshareInfoGet(QNetworkReply* reply) {233void ScreenshareScriptingInterface::handleFailedScreenshareInfoGet(QNetworkReply* reply) {
@@ -211,21 +236,31 @@ void ScreenshareScriptingInterface::handleFailedScreenshareInfoGet(QNetworkReply · 2 unmodified lines
211 emit screenshareError();236 emit screenshareError();
212}237}
213 238
239// This function will handle _all_ web events received via `EntityScriptingInterface::webEventReceived()`, including
240// those not related to screen sharing.
214void ScreenshareScriptingInterface::onWebEventReceived(const QUuid& entityID, const QVariant& message) {241void 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.
215 if (entityID == _screenshareViewerLocalWebEntityUUID) {243 if (entityID == _screenshareViewerLocalWebEntityUUID) {
244 // Bail early if we can't grab the Entity Scripting Interface.
216 auto esi = DependencyManager::get<EntityScriptingInterface>();245 auto esi = DependencyManager::get<EntityScriptingInterface>();
217 if (!esi) {246 if (!esi) {
218 return;247 return;
219 }248 }
220 249
250 // Web events received from the screen share Web JS will always be in stringified JSON format.
221 QByteArray jsonByteArray = QVariant(message).toString().toUtf8();251 QByteArray jsonByteArray = QVariant(message).toString().toUtf8();
222 QJsonDocument jsonObject = QJsonDocument::fromJson(jsonByteArray);252 QJsonDocument jsonObject = QJsonDocument::fromJson(jsonByteArray);
223 253
254 // It should never happen where the screen share Web JS sends a message without the `app` key's value
255 // set to "screenshare".
224 if (jsonObject["app"] != "screenshare") {256 if (jsonObject["app"] != "screenshare") {
225 return;257 return;
226 }258 }
227 259
260 // The screen share Web JS only sends a message with one method: "eventBridgeReady". Handle it here.
228 if (jsonObject["method"] == "eventBridgeReady") {261 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.
229 QJsonObject responseObject;264 QJsonObject responseObject;
230 responseObject.insert("app", "screenshare");265 responseObject.insert("app", "screenshare");
231 responseObject.insert("method", "receiveConnectionInfo");266 responseObject.insert("method", "receiveConnectionInfo");
@@ -235,7 +270,6 @@ void ScreenshareScriptingInterface::onWebEventReceived(const QUuid& entityID, co · 3 unmodified lines
235 responseObjectData.insert("sessionID", _sessionID);270 responseObjectData.insert("sessionID", _sessionID);
236 responseObject.insert("data", responseObjectData);271 responseObject.insert("data", responseObjectData);
237 272
238 auto esi = DependencyManager::get<EntityScriptingInterface>();
239 esi->emitScriptEvent(_screenshareViewerLocalWebEntityUUID, responseObject.toVariantMap());273 esi->emitScriptEvent(_screenshareViewerLocalWebEntityUUID, responseObject.toVariantMap());
240 }274 }
241 }275 }