Merge branch 'screenshareElectronApp' of github.com:MiladNazeri/hifi into MiladNazeri-screenshareElectronApp

This commit is contained in:
Rebecca Stankus 2019-11-08 12:20:15 -08:00
commit 4af1ddf48b
21 changed files with 3310 additions and 6 deletions

View file

@ -256,6 +256,7 @@ endif()
if (BUILD_CLIENT)
add_subdirectory(interface)
add_subdirectory(screenshare)
set_target_properties(interface PROPERTIES FOLDER "Apps")
option(USE_SIXENSE "Build Interface with sixense library/plugin" OFF)

View file

@ -146,23 +146,27 @@ macro(SET_PACKAGING_PARAMETERS)
set(DMG_SUBFOLDER_ICON "${HF_CMAKE_DIR}/installer/install-folder.rsrc")
set(CONSOLE_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
set(INTERFACE_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
set(NITPICK_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
set(CONSOLE_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
set(INTERFACE_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
set(SCREENSHARE_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
set(NITPICK_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
if (CLIENT_ONLY)
set(CONSOLE_EXEC_NAME "Console.app")
else ()
set(CONSOLE_EXEC_NAME "Sandbox.app")
endif()
set(CONSOLE_INSTALL_APP_PATH "${CONSOLE_INSTALL_DIR}/${CONSOLE_EXEC_NAME}")
set(SCREENSHARE_EXEC_NAME "hifi-screenshare.app")
set(SCREENSHARE_INSTALL_APP_PATH "${SCREENSHARE_INSTALL_DIR}/${SCREENSHARE_EXEC_NAME}")
set(CONSOLE_APP_CONTENTS "${CONSOLE_INSTALL_APP_PATH}/Contents")
set(COMPONENT_APP_PATH "${CONSOLE_APP_CONTENTS}/MacOS/Components.app")
set(COMPONENT_INSTALL_DIR "${COMPONENT_APP_PATH}/Contents/MacOS")
set(CONSOLE_PLUGIN_INSTALL_DIR "${COMPONENT_APP_PATH}/Contents/PlugIns")
set(SCREENSHARE_APP_CONTENTS "${SCREENSHARE_INSTALL_APP_PATH}/Contents")
set(INTERFACE_INSTALL_APP_PATH "${CONSOLE_INSTALL_DIR}/${INTERFACE_BUNDLE_NAME}.app")
set(INTERFACE_ICON_FILENAME "${INTERFACE_ICON_PREFIX}.icns")
@ -170,9 +174,11 @@ macro(SET_PACKAGING_PARAMETERS)
else ()
if (WIN32)
set(CONSOLE_INSTALL_DIR "server-console")
set(SCREENSHARE_INSTALL_DIR "hifi-screenshare")
set(NITPICK_INSTALL_DIR "nitpick")
else ()
set(CONSOLE_INSTALL_DIR ".")
set(SCREENSHARE_INSTALL_DIR ".")
set(NITPICK_INSTALL_DIR ".")
endif ()
@ -186,6 +192,7 @@ macro(SET_PACKAGING_PARAMETERS)
set(NITPICK_ICON_FILENAME "${NITPICK_ICON_PREFIX}.ico")
set(CONSOLE_EXEC_NAME "server-console.exe")
set(SCREENSHARE_EXEC_NAME "hifi-screenshare.exe")
set(DS_EXEC_NAME "domain-server.exe")
set(AC_EXEC_NAME "assignment-client.exe")

View file

@ -49,4 +49,6 @@ Item {
Component.onCompleted: {
load(root.url, root.scriptUrl);
}
signal sendToScript(var message);
}

View file

@ -184,6 +184,7 @@
#include "scripting/AssetMappingsScriptingInterface.h"
#include "scripting/ClipboardScriptingInterface.h"
#include "scripting/DesktopScriptingInterface.h"
#include "scripting/ScreenshareScriptingInterface.h"
#include "scripting/AccountServicesScriptingInterface.h"
#include "scripting/HMDScriptingInterface.h"
#include "scripting/MenuScriptingInterface.h"
@ -888,6 +889,11 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
DependencyManager::set<ScriptCache>();
DependencyManager::set<SoundCache>();
DependencyManager::set<SoundCacheScriptingInterface>();
#ifdef HAVE_DDE
DependencyManager::set<DdeFaceTracker>();
#endif
DependencyManager::set<ScreenshareScriptingInterface>();
DependencyManager::set<AudioClient>();
DependencyManager::set<AudioScope>();
DependencyManager::set<DeferredLightingEffect>();
@ -2919,6 +2925,7 @@ Application::~Application() {
DependencyManager::destroy<SoundCache>();
DependencyManager::destroy<OctreeStatsProvider>();
DependencyManager::destroy<GeometryCache>();
DependencyManager::destroy<ScreenshareScriptingInterface>();
DependencyManager::get<ResourceManager>()->cleanup();
@ -3430,7 +3437,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) {
surfaceContext->setContextProperty("Users", DependencyManager::get<UsersScriptingInterface>().data());
surfaceContext->setContextProperty("UserActivityLogger", DependencyManager::get<UserActivityLoggerScriptingInterface>().data());
surfaceContext->setContextProperty("Screenshare", DependencyManager::get<ScreenshareScriptingInterface>().data());
surfaceContext->setContextProperty("Camera", &_myCamera);
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
@ -3536,6 +3543,7 @@ void Application::userKickConfirmation(const QUuid& nodeID) {
}
void Application::setupQmlSurface(QQmlContext* surfaceContext, bool setAdditionalContextProperties) {
surfaceContext->setContextProperty("Screenshare", DependencyManager::get<ScreenshareScriptingInterface>().data());
surfaceContext->setContextProperty("Users", DependencyManager::get<UsersScriptingInterface>().data());
surfaceContext->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
surfaceContext->setContextProperty("UserActivityLogger", DependencyManager::get<UserActivityLoggerScriptingInterface>().data());
@ -7314,6 +7322,7 @@ void Application::registerScriptEngineWithApplicationServices(const ScriptEngine
scriptEngine->registerGlobalObject("AvatarList", DependencyManager::get<AvatarManager>().data());
scriptEngine->registerGlobalObject("Camera", &_myCamera);
scriptEngine->registerGlobalObject("Screenshare", DependencyManager::get<ScreenshareScriptingInterface>().data());
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
scriptEngine->registerGlobalObject("SpeechRecognizer", DependencyManager::get<SpeechRecognizer>().data());

View file

@ -0,0 +1,174 @@
//
// ScreenshareScriptingInterface.cpp
// interface/src/scripting/
//
// Created by Milad Nazeri on 2019-10-23.
// Copyright 2019 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 <QCoreApplication>
#include <QDesktopServices>
#include <QJsonDocument>
#include <QThread>
#include <QUrl>
#include <AddressManager.h>
#include "EntityScriptingInterface.h"
#include "ScreenshareScriptingInterface.h"
ScreenshareScriptingInterface::ScreenshareScriptingInterface() {
};
ScreenshareScriptingInterface::~ScreenshareScriptingInterface() {
stopScreenshare();
}
static const EntityTypes::EntityType LOCAL_SCREENSHARE_WEB_ENTITY_TYPE = EntityTypes::Web;
static const uint8_t LOCAL_SCREENSHARE_WEB_ENTITY_FPS = 30;
static const glm::vec3 LOCAL_SCREENSHARE_WEB_ENTITY_LOCAL_POSITION(0.0f, 0.0f, 0.1f);
static const QString LOCAL_SCREENSHARE_WEB_ENTITY_URL = "https://hifi-content.s3.amazonaws.com/Experiences/Releases/usefulUtilities/smartBoard/screenshareViewer/screenshareClient.html?1";
void ScreenshareScriptingInterface::startScreenshare(const QUuid& screenshareZoneID, const QUuid& smartboardEntityID, const bool& isPresenter) {
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;
}
if (isPresenter && _screenshareProcess && _screenshareProcess->state() != QProcess::NotRunning) {
qDebug() << "Screenshare process already running. Aborting...";
return;
}
if (isPresenter) {
_screenshareProcess.reset(new QProcess(this));
QFileInfo screenshareExecutable(SCREENSHARE_EXE_PATH);
if (!screenshareExecutable.exists() || !screenshareExecutable.isFile()) {
qDebug() << "Screenshare executable doesn't exist at" << SCREENSHARE_EXE_PATH;
return;
}
}
QUuid currentDomainID = DependencyManager::get<AddressManager>()->getDomainID();
// Make HTTP GET request to:
// `https://metaverse.highfidelity.com/api/v1/domain/:domain_id/screenshare`,
// passing the Domain ID that the user is connected to, as well as the `roomName`.
// The server will respond with the relevant OpenTok Token, Session ID, and API Key.
// Upon error-free response, do the logic below, passing in that info as necessary.
QString token = "";
QString apiKey = "";
QString sessionID = "";
if (isPresenter) {
QStringList arguments;
arguments << "--token=" + token;
arguments << "--apiKey=" + apiKey;
arguments << "--sessionID=" + sessionID;
connect(_screenshareProcess.get(), &QProcess::errorOccurred,
[=](QProcess::ProcessError error) { qDebug() << "ZRF QProcess::errorOccurred. `error`:" << error; });
connect(_screenshareProcess.get(), &QProcess::started, [=]() { qDebug() << "ZRF QProcess::started"; });
connect(_screenshareProcess.get(), &QProcess::stateChanged,
[=](QProcess::ProcessState newState) { qDebug() << "ZRF QProcess::stateChanged. `newState`:" << newState; });
connect(_screenshareProcess.get(), QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
[=](int exitCode, QProcess::ExitStatus exitStatus) {
qDebug() << "ZRF QProcess::finished. `exitCode`:" << exitCode << "`exitStatus`:" << exitStatus;
emit screenshareStopped();
});
_screenshareProcess->start(SCREENSHARE_EXE_PATH, arguments);
}
if (!_screenshareViewerLocalWebEntityUUID.isNull()) {
return;
}
auto esi = DependencyManager::get<EntityScriptingInterface>();
if (!esi) {
return;
}
EntityItemProperties localScreenshareWebEntityProps;
localScreenshareWebEntityProps.setType(LOCAL_SCREENSHARE_WEB_ENTITY_TYPE);
localScreenshareWebEntityProps.setMaxFPS(LOCAL_SCREENSHARE_WEB_ENTITY_FPS);
localScreenshareWebEntityProps.setLocalPosition(LOCAL_SCREENSHARE_WEB_ENTITY_LOCAL_POSITION);
localScreenshareWebEntityProps.setSourceUrl(LOCAL_SCREENSHARE_WEB_ENTITY_URL);
localScreenshareWebEntityProps.setParentID(smartboardEntityID);
EntityPropertyFlags desiredSmartboardProperties;
desiredSmartboardProperties += PROP_POSITION;
desiredSmartboardProperties += PROP_DIMENSIONS;
EntityItemProperties smartboardProps = esi->getEntityProperties(smartboardEntityID, desiredSmartboardProperties);
localScreenshareWebEntityProps.setPosition(smartboardProps.getPosition());
localScreenshareWebEntityProps.setDimensions(smartboardProps.getDimensions());
QString hostType = "local";
_screenshareViewerLocalWebEntityUUID = esi->addEntity(localScreenshareWebEntityProps, hostType);
QObject::connect(esi.data(), &EntityScriptingInterface::webEventReceived, this, [&](const QUuid& entityID, const QVariant& message) {
if (entityID == _screenshareViewerLocalWebEntityUUID) {
qDebug() << "ZRF HERE! Inside `webEventReceived(). `entityID`:" << entityID << "`_screenshareViewerLocalWebEntityUUID`:" << _screenshareViewerLocalWebEntityUUID;
auto esi = DependencyManager::get<EntityScriptingInterface>();
if (!esi) {
return;
}
QJsonDocument jsonMessage = QJsonDocument::fromVariant(message);
QJsonObject jsonObject = jsonMessage.object();
qDebug() << "ZRF HERE! Inside `webEventReceived(). `message`:" << message << "`jsonMessage`:" << jsonMessage;
if (jsonObject["app"] != "screenshare") {
return;
}
qDebug() << "ZRF HERE! Inside `webEventReceived(). we're still here!";
if (jsonObject["method"] == "eventBridgeReady") {
QJsonObject responseObject;
responseObject.insert("app", "screenshare");
responseObject.insert("method", "receiveConnectionInfo");
QJsonObject responseObjectData;
responseObjectData.insert("token", token);
responseObjectData.insert("projectAPIKey", apiKey);
responseObjectData.insert("sessionID", sessionID);
responseObject.insert("data", responseObjectData);
qDebug() << "ZRF HERE! Inside `webEventReceived(). `responseObject.toVariantMap()`:" << responseObject.toVariantMap();
esi->emitScriptEvent(_screenshareViewerLocalWebEntityUUID, responseObject.toVariantMap());
}
}
});
};
void ScreenshareScriptingInterface::stopScreenshare() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "stopScreenshare");
return;
}
if (_screenshareProcess && _screenshareProcess->state() != QProcess::NotRunning) {
_screenshareProcess->terminate();
}
if (!_screenshareViewerLocalWebEntityUUID.isNull()) {
auto esi = DependencyManager::get<EntityScriptingInterface>();
if (esi) {
esi->deleteEntity(_screenshareViewerLocalWebEntityUUID);
}
}
_screenshareViewerLocalWebEntityUUID = "{00000000-0000-0000-0000-000000000000}";
}

View file

@ -0,0 +1,49 @@
#ifndef hifi_ScreenshareScriptingInterface_h
#define hifi_ScreenshareScriptingInterface_h
#include <QObject>
#include <QProcess>
#include <QtCore/QCoreApplication>
#include <DependencyManager.h>
#include <PathUtils.h>
class ScreenshareScriptingInterface : public QObject, public Dependency {
Q_OBJECT
public:
ScreenshareScriptingInterface();
~ScreenshareScriptingInterface();
Q_INVOKABLE void startScreenshare(const QUuid& screenshareZoneID, const QUuid& smartboardEntityID, const bool& isPresenter = false);
Q_INVOKABLE void stopScreenshare();
signals:
void screenshareStopped();
void startScreenshareViewer();
private:
#if DEV_BUILD
#ifdef Q_OS_WIN
const QString SCREENSHARE_EXE_PATH{ PathUtils::projectRootPath() + "/screenshare/hifi-screenshare-win32-x64/hifi-screenshare.exe" };
#elif defined(Q_OS_MAC)
const QString SCREENSHARE_EXE_PATH{ PathUtils::projectRootPath() + "/screenshare/screenshare-darwin-x64/hifi-screenshare.app" };
#else
// This path won't exist on other platforms, so the Screenshare Scripting Interface will exit early when invoked.
const QString SCREENSHARE_EXE_PATH{ PathUtils::projectRootPath() + "/screenshare/screenshare-other-os/hifi-screenshare" };
#endif
#else
#ifdef Q_OS_WIN
const QString SCREENSHARE_EXE_PATH{ QCoreApplication::applicationDirPath() + "/hifi-screenshare/hifi-screenshare.exe" };
#elif defined(Q_OS_MAC)
const QString SCREENSHARE_EXE_PATH{ QCoreApplication::applicationDirPath() + "/hifi-screenshare/hifi-screenshare.app" };
#else
// This path won't exist on other platforms, so the Screenshare Scripting Interface will exit early when invoked.
const QString SCREENSHARE_EXE_PATH{ QCoreApplication::applicationDirPath() + "/hifi-screenshare/hifi-screenshare" };
#endif
#endif
std::unique_ptr<QProcess> _screenshareProcess{ nullptr };
QUuid _screenshareViewerLocalWebEntityUUID;
};
#endif // hifi_ScreenshareScriptingInterface_h

4
screenshare/.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
hifi-screenshare-*/
hifi-screenshare*.zip
screenshare*.zip
screenshare-*/

View file

@ -0,0 +1,45 @@
set(TARGET_NAME screenshare)
add_custom_target(${TARGET_NAME}-npm-install
COMMAND npm install
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_custom_target(${TARGET_NAME}
COMMAND npm run packager -- --out ${CMAKE_CURRENT_BINARY_DIR}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS ${TARGET_NAME}-npm-install
)
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "screenshare")
set_target_properties(${TARGET_NAME}-npm-install PROPERTIES FOLDER "hidden/screenshare")
if (WIN32)
set(PACKAGED_SCREENSHARE_FOLDER "hifi-screenshare-win32-x64")
set(SCREENSHARE_DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/${PACKAGED_SCREENSHARE_FOLDER}")
install(
DIRECTORY "${SCREENSHARE_DESTINATION}/"
DESTINATION ${SCREENSHARE_INSTALL_DIR}
)
set(EXECUTABLE_PATH "${SCREENSHARE_DESTINATION}/${SCREENSHARE_EXEC_NAME}")
optional_win_executable_signing()
elseif (APPLE)
set(PACKAGED_SCREENSHARE_FOLDER "hifi-screenshare-darwin-x64/${SCREENSHARE_EXEC_NAME}")
install(
DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${PACKAGED_SCREENSHARE_FOLDER}"
USE_SOURCE_PERMISSIONS
DESTINATION ${SCREENSHARE_INSTALL_DIR}
)
endif()
if (PR_BUILD)
# DO build the Screenshare Electron app when building the `ALL_BUILD` target.
# DO build the Screenshare Electron app when a user selects "Build Solution" from within Visual Studio.
set_target_properties(${TARGET_NAME} PROPERTIES EXCLUDE_FROM_ALL FALSE EXCLUDE_FROM_DEFAULT_BUILD FALSE)
set_target_properties(${TARGET_NAME}-npm-install PROPERTIES EXCLUDE_FROM_ALL FALSE EXCLUDE_FROM_DEFAULT_BUILD FALSE)
else ()
# DO NOT build the Screenshare Electron app when building the `ALL_BUILD` target.
# DO NOT build the Screenshare Electron app when a user selects "Build Solution" from within Visual Studio.
set_target_properties(${TARGET_NAME} PROPERTIES EXCLUDE_FROM_ALL TRUE EXCLUDE_FROM_DEFAULT_BUILD TRUE)
set_target_properties(${TARGET_NAME}-npm-install PROPERTIES EXCLUDE_FROM_ALL TRUE EXCLUDE_FROM_DEFAULT_BUILD TRUE)
endif ()

22
screenshare/README.md Normal file
View file

@ -0,0 +1,22 @@
# Hifi-Screenshare
The Screenshare app will allow easy desktop sharing by being launced from within the highfidelity interface.
# Setup
Create the following environment variable the hifi-screenshare app will use to get the proper connection info:
hifiScreenshareURL="<URL for authentication>"
# Screenshare API
In order to launch the hifi-screenshare app from within interface, you will call the following:
Screenshare.startScreenshare(displayName, userName, token, sessionID, apiKey);
The app won't run without the correct info.
# Files included
packager.js :
Calling npm run packager will use this file to create the actual electron hifi-screenshare executable
src/main.js :
The main process file to configure the electron app
srce/app.js :
The render file to dispaly the screenshare UI

2289
screenshare/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

27
screenshare/package.json Normal file
View file

@ -0,0 +1,27 @@
{
"name": "highfidelity_screenshare",
"version": "1.0.0",
"description": "High Fidelity Screenshare",
"main": "src/main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"packager": "node packager.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/highfidelity/hifi.git"
},
"author": "High Fidelity",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/highfidelity/hifi/issues"
},
"homepage": "https://github.com/highfidelity/hifi#readme",
"devDependencies": {
"electron": "^6.0.12",
"electron-packager": "^14.0.6"
},
"dependencies": {
"yargs": "^14.2.0"
}
}

49
screenshare/packager.js Normal file
View file

@ -0,0 +1,49 @@
var packager = require('electron-packager');
var osType = require('os').type();
var argv = require('yargs').argv;
var platform = null;
if (osType == "Darwin" || osType == "Linux") {
platform = osType.toLowerCase();
} else if (osType == "Windows_NT") {
platform = "win32"
}
var NAME = "hifi-screenshare";
var options = {
dir: __dirname,
name: NAME,
version: "0.1.0",
overwrite: true,
prune: true,
arch: "x64",
platform: platform,
ignore: "electron-packager|README.md|CMakeLists.txt|packager.js|.gitignore"
};
// setup per OS options
if (osType == "Darwin") {
options["app-bundle-id"] = "com.highfidelity.hifi-screenshare";
} else if (osType == "Windows_NT") {
options["version-string"] = {
CompanyName: "High Fidelity, Inc.",
FileDescription: "High Fidelity Screenshare",
ProductName: NAME,
OriginalFilename: NAME + ".exe"
}
}
// check if we were passed a custom out directory, pass it along if so
if (argv.out) {
options.out = argv.out
}
// call the packager to produce the executable
packager(options)
.then(appPath => {
console.log("Wrote new app to " + appPath);
})
.catch(error => {
console.error("There was an error writing the packaged console: " + error.message);
process.exit(1);
});

285
screenshare/src/app.js Normal file
View file

@ -0,0 +1,285 @@
// Helpers
function handleError(error) {
if (error) {
console.error(error);
}
}
/* SOURCE EXAMPLE
[23584:1028/110448.237:INFO:CONSOLE(67)] "{
"id": "screen:0:0",
"name": "Screen 1",
"thumbnail": {},
"display_id": "2528732444",
"appIcon": null
}"
*/
var isBrowser = false;
const imageWidth = 265;
const imageHeight = 165;
var images = 10;
var testImage = "resources/test.jpg";
function MakeSource(name, thumbnail, id, newWidth, newHeight){
this.name = name;
this.thumbnail = thumbnail;
this.id = id;
this.width = newWidth;
this.height = newHeight;
}
let testSources = [];
for (let index = 0; index < images; index++) {
let test = new MakeSource("REALLY LONG LONG title" + index, testImage, index, imageWidth, imageHeight);
testSources.push(test);
}
// if (!isBrowser) {
const electron = require('electron');
// }
let currentScreensharePickID = "";
function screensharePicked(id){
currentScreensharePickID = id;
console.log(currentScreensharePickID);
document.getElementById("share_pick").innerHTML = "";
addSource(sourceMap[id], "share_pick");
togglePage();
}
function screenConfirmed(isConfirmed){
if (isConfirmed === true){
onAccessApproved(currentScreensharePickID);
}
togglePage();
}
let currentPage = "mainPage";
function togglePage(){
if (currentPage === "mainPage") {
currentPage = "confirmationPage";
document.getElementById("select_screen").style.display = "none";
document.getElementById("confirmation_screen").style.display = "block";
} else {
currentPage = "mainPage";
document.getElementById("select_screen").style.display = "block";
document.getElementById("confirmation_screen").style.display = "none";
}
}
// UI
function addSource(source, type) {
let sourceBody = document.createElement('div')
let thumbnail = isBrowser ? source.thumbnail : source.thumbnail.toDataURL();
sourceBody.classList.add("box")
if (type === "share_pick") {
sourceBody.style.marginLeft = "0px";
}
let circle = `<div class="circle" onclick="screensharePicked('${source.id}')"}></div>`
let image = "";
if (source.appIcon) {
image = `<img class="icon" src="${source.appIcon.toDataURL()}" />`;
}
sourceBody.innerHTML = `
<div class="heading">
${image}
<span class="screen_label">${source.name}</span>
</div>
<div class="${type === "share_pick" ? "image_no_hover" : "image"}" onclick="screensharePicked('${source.id}')">
<img src="${thumbnail}" />
</div>
`
// console.log(sourceBody.innerHTML);
if (type === "selects") {
document.getElementById("selects").appendChild(sourceBody);
} else {
document.getElementById("share_pick").appendChild(sourceBody);
document.getElementById("content_name").innerHTML = source.name;
}
}
let sourceMap = {};
function showSources() {
document.getElementById("selects").innerHTML="";
if (isBrowser) {
for (let source of testSources) {
sourceMap[source.id] = source;
addSource(source, "selects");
}
} else {
electron.desktopCapturer.getSources({
types:['window', 'screen'],
thumbnailSize: {
width: imageWidth,
height: imageHeight
},
fetchWindowIcons: true
}, (error, sources) => {
if (error) {
console.log("Error getting sources", error);
}
for (let source of sources) {
// console.log(JSON.stringify(sources,null,4));
sourceMap[source.id] = source;
//*if (source.id.indexOf("screen") > -1) {
// console.log("Adding:", source.id)
addSource(source, "selects");
//}
}
});
}
}
let localStream;
function stopSharing(){
desktopSharing = false;
if (localStream) {
localStream.getTracks()[0].stop();
localStream = null;
}
document.getElementById('screenshare').style.display = "none";
stopTokBoxPublisher();
}
function gotStream(stream) {
localStream = stream;
startTokboxPublisher(localStream);
stream.onended = () => {
if (desktopSharing) {
toggle();
}
};
}
function onAccessApproved(desktop_id) {
if (!desktop_id) {
console.log('Desktop Capture access rejected.');
return;
}
showSources();
document.getElementById('screenshare').style.visibility = "block";
desktopSharing = true;
console.log("Desktop sharing started.. desktop_id:" + desktop_id);
navigator.webkitGetUserMedia({
audio: false,
video: {
mandatory: {
chromeMediaSource: 'desktop',
chromeMediaSourceId: desktop_id,
minWidth: 1280,
maxWidth: 1280,
minHeight: 720,
maxHeight: 720
}
}
}, gotStream, handleError);
}
// Tokbox
function initializeTokboxSession() {
console.log("\n\n\n\n #$######\n TRYING TO START SESSION")
session = OT.initSession(apiKey, sessionId);
session.on('sessionDisconnected', (event) => {
console.log('You were disconnected from the session.', event.reason);
});
// Connect to the session
session.connect(token, (error) => {
if (error) {
handleError(error);
}
});
}
var publisher;
function startTokboxPublisher(stream){
publisher = document.createElement("div");
console.log("publisher pushed")
var publisherOptions = {
videoSource: stream.getVideoTracks()[0],
audioSource: null,
insertMode: 'append',
width: 1280,
height: 720
};
publisher = OT.initPublisher(publisher, publisherOptions, function(error){
if (error) {
console.log("ERROR: " + error);
} else {
session.publish(publisher, function(error) {
if (error) {
console.log("ERROR FROM Session.publish: " + error);
return;
}
console.log("MADE IT TO PUBLISH")
})
}
});
}
function stopTokBoxPublisher(){
console.log("TOK BOX STOPPED!")
publisher.destroy();
}
// main TODO:
// const {ipcRenderer} = ipcRenderer;
// let apiKey;
// let sessionId;
// let token;
// let session;
// ipcRenderer.on('connectionInfo', function(event, message){
// console.log("event", event);
// console.log("MESSAGE FROM MAIN", message);
// const connectionInfo = JSON.parse(message);
// apiKey = connectionInfo.apiKey;
// sessionId = connectionInfo.sessionId;
// token = connectionInfo.token;
// initializeTokboxSession();
// })
function startup(){
console.log("\n\n IN STARTUP \n\n")
// Make an Ajax request to get the OpenTok API key, session ID, and token from the server
// TODO:
fetch(process.env.hifiScreenshareURL)
.then(function(res) {
return res.json();
})
.then(function fetchJson(json) {
apiKey = json.apiKey;
sessionId = json.sessionId;
token = json.token;
initializeTokboxSession();
})
.catch(function catchErr(error) {
handleError(error);
alert('Failed to get opentok sessionId and token. Make sure you have updated the config.js file.');
});
}
document.addEventListener("DOMContentLoaded", () => {
startup();
showSources();
})

View file

@ -0,0 +1,55 @@
<html>
<head>
<link href="styles.css" rel="stylesheet">
</head>
<body id="main">
<div id="title" class="text_title">
<h1>Share your screen</h1>
<h3>Please select the content you'd like to share.</h3>
</div>
<button id="screenshare" onclick="stopSharing()" style="display: none;">Stop Screenshare</button>
<div id="select_screen">
<div class="scrollbar" id="style-1">
<div class="force-overflow">
<div id="selects">
<!-- <div class="box"> -->
<!-- <div class="heading"> -->
<!-- <div class="circle"></div> -->
<!-- <span class="screen_label">Screen 1</span> -->
<!-- </div> heading -->
<!-- <div class="image"> -->
<!-- </div> -->
<!-- </div> box -->
</div>
</div>
</div>
</div>
<div id="confirmation_screen" style="display: none;">
<div id="share_pick">
</div> <!-- share_pick -->
<div id="confirmation_text" style="color: white;">
<p>
Are you sure you'd like to share <span id="content_name">Content Name</span>?
</p>
<p>
Others will be able to see everything contained within this view.
</p>
</div>
<div id="button_selection">
<div id="yes" class="button_confirmation" style="color: #ffffff" onClick="screenConfirmed(true)">
Yes, share this content
</div>
<div id="no" class="button_confirmation" style="color: #009ee0" onClick="screenConfirmed(false)">
No, don't share this content
</div>
</div> button_selection
</div> <!-- confirmation screen -->
<script src="app.js"></script>
<script src="https://static.opentok.com/v2/js/opentok.min.js"></script>
</body>
</html>

56
screenshare/src/main.js Normal file
View file

@ -0,0 +1,56 @@
'use strict';
var userName, displayName, token, apiKey, sessionID;
const {app, BrowserWindow, ipcMain} = require('electron');
const gotTheLock = app.requestSingleInstanceLock()
const argv = require('yargs').argv;
// ./screenshare.exe --userName=miladN ...
const connectionInfo = {
userName: argv.userName || "testName",
displayName: argv.displayName || "displayName",
token: argv.token || "token",
apiKey: argv.apiKey || "apiKey",
sessionID: argv.sessionID || "sessionID"
}
if (!gotTheLock) {
// log.warn("Another instance of the screenshare is already running - this instance will quit.");
app.exit(0);
return;
}
var window;
function createWindow(){
const zoomFactor = 1.0;
window = new BrowserWindow({
backgroundColor: "#000000",
width: 1280 * zoomFactor,
height: 720 * zoomFactor,
center: true,
frame: true,
useContentSize: true,
zoomFactor: zoomFactor,
resizable: false,
alwaysOnTop: false, // TRY
webPreferences: {
nodeIntegration: true
}
});
window.loadURL('file://' + __dirname + '/index.html');
window.setMenu(null);
window.once('ready-to-show', () => {
window.show();
window.webContents.openDevTools()
})
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
app.on('ready', function() {
createWindow();
console.log("sending info");
window.webContents.send('connectionInfo', JSON.stringify(connectionInfo))
});

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

230
screenshare/src/styles.css Normal file
View file

@ -0,0 +1,230 @@
body {
background: black;
box-sizing: border-box;
/* display: -webkit-flex; */
/* -webkit-justify-content: center; */
/* -webkit-align-items: center; */
/* -webkit-flex-direction: column; */
font-family: "Graphik";
margin: 0px;
}
html::-webkit-scrollbar {
overflow: scroll;
overflow-x: hidden;
width: 0px;
}
#confirmation_screen {
/* background-color: orange; */
width: 100%;
display: flex;
text-align: center;
justify-content: center;
align-items: center;
}
#confirmation_text {
margin-top: 25px;
font-size: 25px;
line-height: 25px;
}
#confirmation_text p {
margin: 0px;
}
#button_selection {
margin-top: 25px;
width: 100%;
display: flex;
text-align: center;
justify-content: center;
align-items: center;
flex-direction: column;
}
#share_pick {
/* background-color: blue; */
}
.text_title {
color: white;
}
@font-face {
font-family: "Graphik";
src: url("./resources/Graphik-Regular.ttf");
}
#title {
margin-left: 21px;
margin-top: 21px;
}
#selects {
margin-left: 21px;
margin-top: 70px;
}
h1 {
line-height: 48px;
font-size: 48px;
margin: 0px 0px 0px 0px;
}
h3 {
line-height: 24px;
font-size: 24px;
margin: 9px 0px 0px 0px;
}
#publisher {
visibility: hidden;
width: 0px;
height: 0px;
bottom: 10px;
left: 10px;
z-index: 100;
border: 3px solid white;
border-radius: 3px;
}
.screen_label {
max-width: 220px;
font-size: 25px;
line-height: 25px;
margin-left: 15px;
color: white;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.icon {
display: inline-block;
background: #C4C4C4;
width: 20px;
height: 20px;
border-radius: 50%;
}
.circle:hover{
background-color: yellow;
cursor: pointer;
}
.button_confirmation {
margin: 4px;
cursor: pointer;
width: 250px;
height: 75px;
line-height: 75px;
text-align: center;
vertical-align: middle;
}
.button_confirmation:hover {
outline: solid white 2px;
width: 250px;
}
.box {
/* background-color: orange; */
height: 165px;
width: 265px;
display: inline-block;
margin-left: 35px;
margin-bottom: 40px;
margin-top: 20px;
}
.heading {
height: 35px;
display: flex;
align-items: center;
}
.image {
background-color: blue;
width: 265px;
height: 165px;
max-height: 165px;
max-width: 265px;
}
.image:hover {
outline: solid white 3px;
}
.image_no_hover {
background-color: blue;
width: 265px;
height: 165px;
max-height: 165px;
max-width: 265px;
}
img {
width: 265px;
height: 165px;
}
.scrollbar {
float: right;
height: 470px;
margin-right: 20px;
width: 100%;
overflow-y: scroll;
}
#style-1::-webkit-scrollbar {
width: 15px;
background-color: red;
}
#style-1::-webkit-scrollbar-thumb {
background-color: #0198CB;
}
#style-1::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
background-color: #848484;
margin-right: 22px;
}
/*
button {
display: inline-block;
background: -webkit-linear-gradient(#F9F9F9 40%, #E3E3E3 70%);
background: linear-gradient(#F9F9F9 40%, #E3E3E3 70%);
border: 1px solid #999;
-webkit-border-radius: 3px;
border-radius: 3px;
padding: 5px 8px;
outline: none;
white-space: nowrap;
-webkit-user-select: none;
user-select: none;
cursor: pointer;
text-shadow: 1px 1px #fff;
font-weight: 700;
font-size: 10pt;
}
button:hover,
button.active {
border-color: black;
}
button:active,
button.active {
background: -webkit-linear-gradient(#E3E3E3 40%, #F9F9F9 70%);
background: linear-gradient(#E3E3E3 40%, #F9F9F9 70%);
} */