merge with master

This commit is contained in:
SamGondelman 2019-06-10 10:29:39 -07:00
commit 37feb7f027
198 changed files with 24689 additions and 764 deletions
BUILD_WIN.md
assignment-client/src/avatars
cmake
externals/wasapi
ports/bullet3
debian/patches
domain-server/src
hifi_vcpkg.py
interface
launchers

View file

@ -34,7 +34,18 @@ If you do not wish to use the Python installation bundled with Visual Studio, yo
Download and install the latest version of CMake 3.9.
Download the file named win64-x64 Installer from the [CMake Website](https://cmake.org/download/). You can access the installer on this [3.9 Version page](https://cmake.org/files/v3.9/). During installation, make sure to check "Add CMake to system PATH for all users" when prompted.
### Step 5. Running CMake to Generate Build Files
### Step 3. Create VCPKG environment variable
In the next step, you will use CMake to build High Fidelity. By default, the CMake process builds dependency files in Windows' `%TEMP%` directory, which is periodically cleared by the operating system. To prevent you from having to re-build the dependencies in the event that Windows clears that directory, we recommend that you create a `HIFI_VCPKG_BASE` environment variable linked to a directory somewhere on your machine. That directory will contain all dependency files until you manually remove them.
To create this variable:
* Naviagte to 'Edit the System Environment Variables' Through the start menu.
* Click on 'Environment Variables'
* Select 'New'
* Set "Variable name" to HIFI_VCPKG_BASE
* Set "Variable value" to any directory that you have control over.
### Step 4. Running CMake to Generate Build Files
Run Command Prompt from Start and run the following commands:
`cd "%HIFI_DIR%"`
@ -49,24 +60,28 @@ Run `cmake .. -G "Visual Studio 16 2019" -A x64`.
Where `%HIFI_DIR%` is the directory for the highfidelity repository.
### Step 6. Making a Build
### Step 5. Making a Build
Open `%HIFI_DIR%\build\hifi.sln` using Visual Studio.
Change the Solution Configuration (menu ribbon under the menu bar, next to the green play button) from "Debug" to "Release" for best performance.
Create another environment variable (see Step #4)
Create another environment variable (see Step #3)
* Set "Variable name": `PreferredToolArchitecture`
* Set "Variable value": `x64`
Restart Visual Studio for the new variable to take effect.
Run from the menu bar `Build > Build Solution`.
### Step 7. Testing Interface
### Step 6. Testing Interface
Create another environment variable (see Step #4)
Create another environment variable (see Step #3)
* Set "Variable name": `_NO_DEBUG_HEAP`
* Set "Variable value": `1`
Restart Visual Studio again.
In Visual Studio, right+click "interface" under the Apps folder in Solution Explorer and select "Set as Startup Project". Run from the menu bar `Debug > Start Debugging`.
Now, you should have a full build of High Fidelity and be able to run the Interface using Visual Studio. Please check our [Docs](https://wiki.highfidelity.com/wiki/Main_Page) for more information regarding the programming workflow.
@ -75,11 +90,11 @@ Note: You can also run Interface by launching it from command line or File Explo
## Troubleshooting
For any problems after Step #7, first try this:
For any problems after Step #6, first try this:
* Delete your locally cloned copy of the highfidelity repository
* Restart your computer
* Redownload the [repository](https://github.com/highfidelity/hifi)
* Restart directions from Step #7
* Restart directions from Step #6
#### CMake gives you the same error message repeatedly after the build fails

View file

@ -365,7 +365,7 @@ void AvatarMixer::manageIdentityData(const SharedNodePointer& node) {
// there is no need to manage identity data we haven't received yet
// so bail early if we've never received an identity packet for this avatar
if (!nodeData || !nodeData->getAvatar().hasProcessedFirstIdentity()) {
if (!nodeData || !nodeData->getAvatar().hasProcessedFirstIdentity() || !node->getActiveSocket()) {
return;
}

View file

@ -6,8 +6,8 @@ if (WIN32)
include(ExternalProject)
ExternalProject_Add(
${EXTERNAL_NAME}
URL https://public.highfidelity.com/dependencies/qtaudio_wasapi10.zip
URL_MD5 4f40e49715a420fb67b45b9cee19052c
URL https://public.highfidelity.com/dependencies/qtaudio_wasapi11.zip
URL_MD5 d0eb8489455e7f79d59155535a2c8861
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""

View file

@ -1,4 +1,7 @@
# Updated June 6th, 2019, to force new vckpg hash
#
# Common Ambient Variables:
#
# CURRENT_BUILDTREES_DIR = ${VCPKG_ROOT_DIR}\buildtrees\${PORT}
# CURRENT_PACKAGES_DIR = ${VCPKG_ROOT_DIR}\packages\${PORT}_${TARGET_TRIPLET}
# CURRENT_PORT_DIR = ${VCPKG_ROOT_DIR}\ports\${PORT}
@ -9,7 +12,6 @@
# VCPKG_ROOT_DIR = <C:\path\to\current\vcpkg>
# VCPKG_TARGET_ARCHITECTURE = target architecture (x64, x86, arm)
#
#
include(vcpkg_common_functions)

View file

@ -1,43 +1,41 @@
Index: hifi-interface-84/cmake/macros/SetupQt.cmake
Index: hifi/cmake/macros/SetupQt.cmake
===================================================================
--- hifi-interface-84.orig/cmake/macros/SetupQt.cmake
+++ hifi-interface-84/cmake/macros/SetupQt.cmake
@@ -18,19 +18,19 @@ function(calculate_default_qt_dir _QT_VE
elseif(WIN32)
set(QT_DEFAULT_ARCH "msvc2017_64")
else()
- set(QT_DEFAULT_ARCH "gcc_64")
+ set(QT_DEFAULT_ARCH "x86_64-linux-gnu")
endif()
--- hifi.orig/cmake/macros/SetupQt.cmake
+++ hifi/cmake/macros/SetupQt.cmake
@@ -27,7 +27,7 @@ function(calculate_qt5_version result _Q
set(_QT_CORE_DIR "${_QT_DIR}/include/QtCore")
endif()
if(NOT EXISTS "${_QT_CORE_DIR}")
- message(FATAL_ERROR "Could not find 'include/QtCore' in '${_QT_DIR}'")
+ message("Could not find 'include/QtCore' in '${_QT_DIR}'")
endif()
set(subdirs "")
get_sub_directories(subdirs ${_QT_CORE_DIR})
@@ -71,13 +71,13 @@ macro(setup_qt)
if (WIN32 OR (ANDROID AND ("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows")))
set(QT_DEFAULT_ROOT "c:/Qt")
else()
- set(QT_DEFAULT_ROOT "$ENV{HOME}/Qt")
+ set(QT_DEFAULT_ROOT "/usr/lib")
endif()
message("QT_CMAKE_PREFIX_PATH = " ${QT_CMAKE_PREFIX_PATH})
set_from_env(QT_ROOT QT_ROOT ${QT_DEFAULT_ROOT})
set_from_env(QT_ARCH QT_ARCH ${QT_DEFAULT_ARCH})
- # figure out where the qt dir is
- get_filename_component(QT_DIR "${QT_CMAKE_PREFIX_PATH}/../../" ABSOLUTE)
- set(QT_VERSION "unknown")
- calculate_qt5_version(QT_VERSION "${QT_DIR}")
- if (QT_VERSION STREQUAL "unknown")
- message(FATAL_ERROR "Could not determine QT_VERSION")
- endif()
+ # # figure out where the qt dir is
+ # get_filename_component(QT_DIR "${QT_CMAKE_PREFIX_PATH}/../../" ABSOLUTE)
+ # set(QT_VERSION "unknown")
+ # calculate_qt5_version(QT_VERSION "${QT_DIR}")
+ # if (QT_VERSION STREQUAL "unknown")
+ # message(FATAL_ERROR "Could not determine QT_VERSION")
+ # endif()
- set(${_RESULT_NAME} "${QT_ROOT}/${_QT_VERSION}/${QT_ARCH}" PARENT_SCOPE)
+ set(${_RESULT_NAME} "${QT_ROOT}/${QT_ARCH}" PARENT_SCOPE)
endfunction()
# Sets the QT_CMAKE_PREFIX_PATH and QT_DIR variables
@@ -44,7 +44,7 @@ macro(setup_qt)
endif()
if (("QT_CMAKE_PREFIX_PATH" STREQUAL "") OR (NOT EXISTS "${QT_CMAKE_PREFIX_PATH}"))
calculate_default_qt_dir(${QT_VERSION} QT_DIR)
- set(QT_CMAKE_PREFIX_PATH "${QT_DIR}/lib/cmake")
+ set(QT_CMAKE_PREFIX_PATH "${QT_DIR}/cmake")
else()
# figure out where the qt dir is
get_filename_component(QT_DIR "${QT_CMAKE_PREFIX_PATH}/../../" ABSOLUTE)
Index: hifi-interface-84/interface/CMakeLists.txt
if(WIN32)
# windows shell does not like backslashes expanded on the command line,
Index: hifi/interface/CMakeLists.txt
===================================================================
--- hifi-interface-84.orig/interface/CMakeLists.txt
+++ hifi-interface-84/interface/CMakeLists.txt
--- hifi.orig/interface/CMakeLists.txt
+++ hifi/interface/CMakeLists.txt
@@ -35,7 +35,7 @@ else ()
add_custom_command(
OUTPUT ${RESOURCES_RCC}
@ -60,11 +58,11 @@ Index: hifi-interface-84/interface/CMakeLists.txt
+
endif()
if (SCRIPTS_INSTALL_DIR)
Index: hifi-interface-84/tools/nitpick/CMakeLists.txt
if (DEV_BUILD AND (APPLE OR UNIX))
Index: hifi/tools/nitpick/CMakeLists.txt
===================================================================
--- hifi-interface-84.orig/tools/nitpick/CMakeLists.txt
+++ hifi-interface-84/tools/nitpick/CMakeLists.txt
--- hifi.orig/tools/nitpick/CMakeLists.txt
+++ hifi/tools/nitpick/CMakeLists.txt
@@ -12,7 +12,7 @@ generate_qrc(OUTPUT ${RESOURCES_QRC} PAT
add_custom_command(
OUTPUT ${RESOURCES_RCC}

View file

@ -276,7 +276,6 @@ void AssetsBackupHandler::createBackup(const QString& backupName, QuaZip& zip) {
return;
}
_backups.emplace_back(backupName, mappings, false);
qDebug() << "Created asset backup:" << backupName;
}
void AssetsBackupHandler::recoverBackup(const QString& backupName, QuaZip& zip) {

View file

@ -466,31 +466,20 @@ void DomainContentBackupManager::getAllBackupsAndStatus(MiniPromise::Promise pro
void DomainContentBackupManager::removeOldBackupVersions(const BackupRule& rule) {
QDir backupDir { _backupDirectory };
if (backupDir.exists() && rule.maxBackupVersions > 0) {
qCDebug(domain_server) << "Rolling old backup versions for rule" << rule.name;
auto matchingFiles =
backupDir.entryInfoList({ AUTOMATIC_BACKUP_PREFIX + rule.extensionFormat + "*.zip" }, QDir::Files | QDir::NoSymLinks, QDir::Name);
int backupsToDelete = matchingFiles.length() - rule.maxBackupVersions;
if (backupsToDelete <= 0) {
qCDebug(domain_server) << "Found" << matchingFiles.length() << "backups, no backups need to be deleted";
} else {
qCDebug(domain_server) << "Found" << matchingFiles.length() << "backups, deleting " << backupsToDelete << "backup(s)";
if (backupsToDelete > 0) {
for (int i = 0; i < backupsToDelete; ++i) {
auto fileInfo = matchingFiles[i].absoluteFilePath();
QFile backupFile(fileInfo);
if (backupFile.remove()) {
qCDebug(domain_server) << "Removed old backup: " << backupFile.fileName();
} else {
if (!backupFile.remove()) {
qCDebug(domain_server) << "Failed to remove old backup: " << backupFile.fileName();
}
}
qCDebug(domain_server) << "Done removing old backup versions";
}
} else {
qCDebug(domain_server) << "Rolling backups for rule" << rule.name << "."
<< " Max Rolled Backup Versions less than 1 [" << rule.maxBackupVersions << "]."
<< " No need to roll backups";
}
}
@ -501,13 +490,7 @@ void DomainContentBackupManager::backup() {
for (BackupRule& rule : _backupRules) {
auto secondsSinceLastBackup = nowSeconds - rule.lastBackupSeconds;
qCDebug(domain_server) << "Checking [" << rule.name << "] - Time since last backup [" << secondsSinceLastBackup
<< "] "
<< "compared to backup interval [" << rule.intervalSeconds << "]...";
if (secondsSinceLastBackup > rule.intervalSeconds) {
qCDebug(domain_server) << "Time since last backup [" << secondsSinceLastBackup << "] for rule [" << rule.name
<< "] exceeds backup interval [" << rule.intervalSeconds << "] doing backup now...";
bool success;
QString path;
@ -517,13 +500,9 @@ void DomainContentBackupManager::backup() {
continue;
}
qDebug() << "Created backup: " << path;
rule.lastBackupSeconds = nowSeconds;
removeOldBackupVersions(rule);
} else {
qCDebug(domain_server) << "Backup not needed for this rule [" << rule.name << "]...";
}
}
}

View file

@ -322,6 +322,11 @@ DomainServer::DomainServer(int argc, char* argv[]) :
_contentManager->initialize(true);
connect(_contentManager.get(), &DomainContentBackupManager::recoveryCompleted, this, &DomainServer::restart);
static const int NODE_PING_MONITOR_INTERVAL_MSECS = 1 * MSECS_PER_SECOND;
_nodePingMonitorTimer = new QTimer{ this };
connect(_nodePingMonitorTimer, &QTimer::timeout, this, &DomainServer::nodePingMonitor);
_nodePingMonitorTimer->start(NODE_PING_MONITOR_INTERVAL_MSECS);
}
void DomainServer::parseCommandLine(int argc, char* argv[]) {
@ -1722,6 +1727,18 @@ void DomainServer::sendHeartbeatToIceServer() {
}
}
void DomainServer::nodePingMonitor() {
auto nodeList = DependencyManager::get<LimitedNodeList>();
quint64 now = usecTimestampNow();
nodeList->eachNode([now](const SharedNodePointer& node) {
quint64 lastHeard = now - node->getLastHeardMicrostamp();
if (lastHeard > 2 * USECS_PER_SECOND) {
qCDebug(domain_server) << "Haven't heard from " << node->getPublicSocket() << " in " << lastHeard / USECS_PER_MSEC << " msec";
}
});
}
void DomainServer::processOctreeDataPersistMessage(QSharedPointer<ReceivedMessage> message) {
qDebug() << "Received octree data persist message";
auto data = message->readAll();

View file

@ -110,6 +110,7 @@ private slots:
void performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr);
void sendHeartbeatToMetaverse() { sendHeartbeatToMetaverse(QString()); }
void sendHeartbeatToIceServer();
void nodePingMonitor();
void handleConnectedNode(SharedNodePointer newNode, quint64 requestReceiveTime);
void handleTempDomainSuccess(QNetworkReply* requestReply);
@ -257,6 +258,7 @@ private:
QTimer* _iceHeartbeatTimer { nullptr };
QTimer* _metaverseHeartbeatTimer { nullptr };
QTimer* _metaverseGroupCacheTimer { nullptr };
QTimer* _nodePingMonitorTimer { nullptr };
QList<QHostAddress> _iceServerAddresses;
QSet<QHostAddress> _failedIceServerAddresses;

View file

@ -254,9 +254,9 @@ endif()
url = 'NOT DEFINED'
if platform.system() == 'Windows':
url = 'https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/qt5-install-5.12.3-windows2.tar.gz'
url = 'https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/qt5-install-5.12.3-windows3.tar.gz'
elif platform.system() == 'Darwin':
url = 'https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/qt5-install-5.12.3-macos2.tar.gz'
url = 'https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/qt5-install-5.12.3-macos3.tar.gz'
elif platform.system() == 'Linux':
if platform.linux_distribution()[1][:3] == '16.':
url = 'https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/qt5-install-5.12.3-ubuntu-16.04.tar.gz'

View file

@ -1,52 +1,171 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1600 100" style="enable-background:new 0 0 1600 100;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 23.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 1600 100"
style="enable-background:new 0 0 1600 100;"
xml:space="preserve"
sodipodi:docname="statusIconAtlas.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)"><metadata
id="metadata35"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs33" />
<style
type="text/css"
id="style2">
.st0{fill:#050505;}
</style>
<path class="st0" d="M92.8,69.3c-0.8-1.5-1.9-2.7-3.1-3.8c4.4-11.4,3.7-24.6-2.6-35.8c-9.2-16.3-28.3-24.4-46.3-20l-3.1,0.7l4.9,8.7
l1.7-0.3c13.7-2.7,27.6,3.6,34.5,15.7c4.9,8.7,5.5,18.9,1.9,27.9c-2.1,0.1-4.3,0.6-6.2,1.8c-6.5,3.7-8.8,12-5.1,18.5
c3.7,6.5,12,8.8,18.5,5.1C94.2,84.1,96.5,75.8,92.8,69.3z"/>
<path class="st0" d="M54.2,82.6l-1.5,0.1c-12.3,0.8-24.2-5.6-30.2-16.3c-3.8-6.6-4.9-14.2-3.7-21.3c2.8,0.4,5.9-0.1,8.6-1.6
c6.5-3.7,8.8-12,5.1-18.5s-12-8.8-18.5-5.1C7.7,23.7,5.4,32,9,38.5c0.3,0.6,0.7,1.2,1.2,1.7c-2.6,10.3-1.4,21.5,4.1,31.1
c7.5,13.2,21.5,21.2,36.5,21.2c1.8,0,3.5-0.1,5.2-0.3l3.6-0.4L54.2,82.6z"/>
<path class="st0" d="M67.2,63.4H33.8c-1,0-2.1-0.5-2.6-1.5c-0.5-0.9-0.5-2.1,0-3L47.8,30c0.5-0.9,1.6-1.5,2.6-1.5S52.5,29,53,30
l16.7,28.9c0.5,0.9,0.5,2.1,0,3C69.3,62.9,68.3,63.4,67.2,63.4z M39,57.4h23L50.4,37.5L39,57.4z"/>
<polygon class="st0" points="175.4,30.6 149.9,8 123.9,30.7 139,30.7 139.2,59.6 161,59.3 160.8,30.7 "/>
<polygon class="st0" points="225.6,39.8 251.1,62.5 277.1,39.8 261.9,39.8 261.7,8.9 240,9.2 240.2,39.8 "/>
<path class="st0" d="M174.3,42.8c1.8,3.7,2.8,7.8,2.8,12.1c0,15.2-12.3,27.5-27.5,27.5s-27.5-12.3-27.5-27.5c0-4.4,1-8.5,2.9-12.1
h-7.9c-1.4,3.8-2.2,7.8-2.2,12.1c0,19.2,15.6,34.7,34.7,34.7c19.2,0,34.7-15.6,34.7-34.7c0-4.3-0.8-8.3-2.2-12.1
C182.1,42.8,174.3,42.8,174.3,42.8z"/>
<path class="st0" d="M278.8,53c0.1,0.7,0.1,1.5,0.1,2.2c0,15.2-12.4,27.6-27.6,27.6c-15.2,0-27.6-12.4-27.6-27.6
c0-1.1,0.1-2.1,0.2-3.1c-2.1-2.1-4.1-4.1-6.2-6.2c-0.8,3-1.3,6.1-1.3,9.3c0,19.2,15.6,34.9,34.9,34.9s34.9-15.6,34.9-34.9
c0-2.9-0.4-5.8-1.1-8.5L278.8,53z"/>
<path class="st0" d="M353.3,91.2c-0.3,0-0.7,0-1,0c-1.8-0.2-3.5-0.4-5.3-0.7c-21.3-3.6-35.2-22.8-32-44.2
c2.7-18.2,17.7-31.4,36.8-32.5c17.2-0.9,33.8,11.4,38.2,28.5c0.8,3.1,1.1,6.3,1.6,9.5c0,0.3,0,0.7,0,1c-0.2,0.9-0.4,1.8-0.5,2.7
c-1.3,16.3-12.9,30.1-28.8,34C359.3,90.4,356.3,90.7,353.3,91.2z M353.7,83.9c8.3,0,16.1-3.4,22.6-9.9c2.2-2.2,2-3.1-0.7-4.5
c-3.9-1.9-7.8-3.7-11.7-5.6c-4-2-4.6-8.1-1.1-10.8c2-1.5,2.4-3.7,2.1-5.9c-0.2-1.8-1-3.5-1.2-5.3c-0.6-6-5.2-10.2-11.1-10.1
s-10.4,4.8-10.6,10.9c-0.1,1.4-0.4,2.8-0.9,4.1c-0.6,1.9,0.1,4.9,1.7,6.3c3.8,3.1,3.1,9-1.4,11.2c-3.6,1.7-7.2,3.4-10.8,5.2
c-3.4,1.6-3.6,2.5-0.8,5.1C336.2,80.6,343.8,83.9,353.7,83.9z"/>
<polygon class="st0" points="445.3,14.1 484.6,14.1 461.5,38.2 485.6,41.4 422.2,86.9 441.2,53.4 425.6,49.3 "/>
<path class="st0" d="M564.7,53.2l0.1-8.7l-22.9-0.2c0.1-0.5,0.2-1.1,0.2-1.6c0.2-6.1,4.7-10.8,10.6-10.9c5.9-0.1,10.5,4.1,11.1,10.1
c0.1,0.9,0.3,1.7,0.6,2.6l26.7,0.2c-3.3-17.9-19-31.4-37.9-31.4c-18.6,0-34.2,13.2-37.8,30.8l26.4,0.2l0,0l-0.9,0l-0.1,8.7
l-26.2-0.2c0.5,20.8,17.5,37.6,38.5,37.6c20.7,0,37.6-16.4,38.5-36.9L564.7,53.2z M576.3,74c-6.5,6.5-14.3,9.9-22.6,9.9
c-9.9,0-17.5-3.3-23.9-9.3c-2.8-2.6-2.6-3.5,0.8-5.1c3.6-1.8,7.2-3.5,10.8-5.2c4.5-2.2,5.2-8.1,1.4-11.2c0,0-0.1-0.1-0.1-0.1l20,0.2
c-3.4,2.7-2.8,8.7,1.2,10.7c3.9,1.9,7.8,3.7,11.7,5.6C578.3,70.9,578.5,71.8,576.3,74z"/>
<g>
<path class="st0" d="M638.5,48.2c-1.2-1-2.3-2.2-3.2-3.5h-16.7v9.2h29.1c-1.4-0.6-2.6-1.3-4-2.2C641.6,50.4,640.3,49.7,638.5,48.2z
"/>
<path class="st0" d="M684.9,44.7h-31.3c1,0.5,2.1,1,3.2,1.5c2.2,1,3.5,1.8,5.6,3.1c2.1,1.3,4.2,2.3,6,3.8c0.4,0.3,0.7,0.6,1,0.9
h15.5V44.7z"/>
<path class="st0" d="M651.5,44.2c-1.4-0.7-2.6-1.4-3.6-2.2c-1.1-0.8-1.9-1.7-2.5-2.8c-0.6-1-0.9-2.2-0.9-3.7c0-2.1,0.7-3.8,2-5.1
c1.3-1.3,3.2-2,5.7-2c1.1,0,2.2,0.1,3.2,0.4c1,0.3,1.9,0.8,2.7,1.4c0.8,0.7,1.5,1.6,2,2.7c0.5,1.1,0.9,2.5,1.1,4.2h7.4
c0.2,0,0.5,0,0.9,0c0.4,0,0.9,0,1.3,0c0.5,0,0.9,0,1.3,0c0.4,0,0.7,0,0.9,0c-0.1-3.3-0.7-6.1-1.8-8.5s-2.5-4.4-4.4-5.9
c-1.9-1.5-4-2.7-6.5-3.4c-2.5-0.7-5.1-1.1-8-1.1c-2.6,0-5,0.4-7.4,1.1c-2.4,0.7-4.4,1.8-6.3,3.2c-1.8,1.4-3.3,3.2-4.3,5.3
c-1.1,2.1-1.6,4.6-1.6,7.6c0,3,0.6,5.5,1.7,7.5c0.3,0.6,0.7,1.2,1.1,1.8h17.3C652.2,44.5,651.9,44.3,651.5,44.2z"/>
<path class="st0" d="M651.1,55c1.5,0.7,2.9,1.3,4.2,1.9s2.6,1.3,3.6,2.1c1,0.8,1.9,1.7,2.5,2.8s0.9,2.4,0.9,4c0,1.7-0.3,3.2-1,4.3
c-0.7,1.1-1.5,2-2.5,2.7c-1,0.7-2.1,1.1-3.3,1.4c-1.2,0.3-2.4,0.4-3.5,0.4c-1.3,0-2.5-0.2-3.7-0.7c-1.1-0.4-2.1-1.2-2.9-2.1
c-0.8-1-1.5-2.3-2-3.8c-0.5-1.6-0.8-3.5-0.8-5.7h-12.1c0.1,3.7,0.8,7,1.9,9.8c1.2,2.8,2.7,5.2,4.7,7c1.9,1.9,4.2,3.3,6.7,4.2
s5.3,1.4,8.1,1.4c2.9,0,5.8-0.4,8.4-1.1c2.7-0.7,5-1.9,7.1-3.5c2-1.6,3.7-3.6,4.9-6.1s1.8-5.5,1.8-9.1c0-2.6-0.5-4.9-1.6-6.8
c-0.9-1.5-2-2.9-3.3-4.1h-20.7C649.5,54.3,650.3,54.6,651.1,55z"/>
<sodipodi:namedview
bordercolor="#666666"
borderopacity="1"
gridtolerance="10"
guidetolerance="10"
id="namedview82"
inkscape:current-layer="Layer_1"
inkscape:cx="618.02971"
inkscape:cy="163.62366"
inkscape:pagecheckerboard="true"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-height="1017"
inkscape:window-maximized="1"
inkscape:window-width="1920"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:zoom="1.003678"
objecttolerance="10"
pagecolor="#000000"
showgrid="true">
<inkscape:grid
color="#3fffff"
empcolor="#3fffff"
empopacity="0.51764706"
empspacing="100"
id="grid3791"
opacity="0.62745098"
type="xygrid"
visible="true">
</inkscape:grid>
<inkscape:grid
enabled="false"
id="grid3793"
type="xygrid" />
</sodipodi:namedview>
<path
id="path51"
class="st0"
d="M92.8,69.3c-0.8-1.5-1.9-2.7-3.1-3.8c4.4-11.4,3.7-24.6-2.6-35.8c-9.2-16.3-28.3-24.4-46.3-20 l-3.1,0.7l4.9,8.7l1.7-0.3c13.7-2.7,27.6,3.6,34.5,15.7c4.9,8.7,5.5,18.9,1.9,27.9c-2.1,0.1-4.3,0.6-6.2,1.8 c-6.5,3.7-8.8,12-5.1,18.5c3.7,6.5,12,8.8,18.5,5.1C94.2,84.1,96.5,75.8,92.8,69.3z"
style="fill:#ffffff" />
<path
id="path53"
class="st0"
d="M54.2,82.6l-1.5,0.1c-12.3,0.8-24.2-5.6-30.2-16.3c-3.8-6.6-4.9-14.2-3.7-21.3 c2.8,0.4,5.9-0.1,8.6-1.6c6.5-3.7,8.8-12,5.1-18.5s-12-8.8-18.5-5.1C7.7,23.7,5.4,32,9,38.5c0.3,0.6,0.7,1.2,1.2,1.7 c-2.6,10.3-1.4,21.5,4.1,31.1c7.5,13.2,21.5,21.2,36.5,21.2c1.8,0,3.5-0.1,5.2-0.3l3.6-0.4L54.2,82.6z"
style="fill:#ffffff" />
<path
id="path55"
class="st0"
d="M67.2,63.4H33.8c-1,0-2.1-0.5-2.6-1.5c-0.5-0.9-0.5-2.1,0-3L47.8,30c0.5-0.9,1.6-1.5,2.6-1.5 S52.5,29,53,30l16.7,28.9c0.5,0.9,0.5,2.1,0,3C69.3,62.9,68.3,63.4,67.2,63.4z M39,57.4h23L50.4,37.5L39,57.4z"
style="fill:#ffffff" />
<polygon
id="polygon57"
class="st0"
points="175.4,30.6 149.9,8 123.9,30.7 139,30.7 139.2,59.6 161,59.3 160.8,30.7 "
style="fill:#ffffff" />
<polygon
id="polygon59"
class="st0"
points="225.6,39.8 251.1,62.5 277.1,39.8 261.9,39.8 261.7,8.9 240,9.2 240.2,39.8 "
style="fill:#ffffff" />
<path
id="path61"
class="st0"
d="M174.3,42.8c1.8,3.7,2.8,7.8,2.8,12.1c0,15.2-12.3,27.5-27.5,27.5s-27.5-12.3-27.5-27.5 c0-4.4,1-8.5,2.9-12.1h-7.9c-1.4,3.8-2.2,7.8-2.2,12.1c0,19.2,15.6,34.7,34.7,34.7c19.2,0,34.7-15.6,34.7-34.7 c0-4.3-0.8-8.3-2.2-12.1C182.1,42.8,174.3,42.8,174.3,42.8z"
style="fill:#ffffff" />
<path
id="path63"
class="st0"
d="M278.8,53c0.1,0.7,0.1,1.5,0.1,2.2c0,15.2-12.4,27.6-27.6,27.6c-15.2,0-27.6-12.4-27.6-27.6 c0-1.1,0.1-2.1,0.2-3.1c-2.1-2.1-4.1-4.1-6.2-6.2c-0.8,3-1.3,6.1-1.3,9.3c0,19.2,15.6,34.9,34.9,34.9s34.9-15.6,34.9-34.9 c0-2.9-0.4-5.8-1.1-8.5L278.8,53z"
style="fill:#ffffff" />
<path
id="path65"
class="st0"
d="M353.3,91.2c-0.3,0-0.7,0-1,0c-1.8-0.2-3.5-0.4-5.3-0.7c-21.3-3.6-35.2-22.8-32-44.2 c2.7-18.2,17.7-31.4,36.8-32.5c17.2-0.9,33.8,11.4,38.2,28.5c0.8,3.1,1.1,6.3,1.6,9.5c0,0.3,0,0.7,0,1c-0.2,0.9-0.4,1.8-0.5,2.7 c-1.3,16.3-12.9,30.1-28.8,34C359.3,90.4,356.3,90.7,353.3,91.2z M353.7,83.9c8.3,0,16.1-3.4,22.6-9.9c2.2-2.2,2-3.1-0.7-4.5 c-3.9-1.9-7.8-3.7-11.7-5.6c-4-2-4.6-8.1-1.1-10.8c2-1.5,2.4-3.7,2.1-5.9c-0.2-1.8-1-3.5-1.2-5.3c-0.6-6-5.2-10.2-11.1-10.1 s-10.4,4.8-10.6,10.9c-0.1,1.4-0.4,2.8-0.9,4.1c-0.6,1.9,0.1,4.9,1.7,6.3c3.8,3.1,3.1,9-1.4,11.2c-3.6,1.7-7.2,3.4-10.8,5.2 c-3.4,1.6-3.6,2.5-0.8,5.1C336.2,80.6,343.8,83.9,353.7,83.9z"
style="fill:#ffffff" />
<polygon
id="polygon67"
class="st0"
points="445.3,14.1 484.6,14.1 461.5,38.2 485.6,41.4 422.2,86.9 441.2,53.4 425.6,49.3 "
style="fill:#ffffff" />
<path
id="path69"
class="st0"
d="M564.7,53.2l0.1-8.7l-22.9-0.2c0.1-0.5,0.2-1.1,0.2-1.6c0.2-6.1,4.7-10.8,10.6-10.9 c5.9-0.1,10.5,4.1,11.1,10.1c0.1,0.9,0.3,1.7,0.6,2.6l26.7,0.2c-3.3-17.9-19-31.4-37.9-31.4c-18.6,0-34.2,13.2-37.8,30.8l26.4,0.2 l0,0h-0.9l-0.1,8.7l-26.2-0.2c0.5,20.8,17.5,37.6,38.5,37.6c20.7,0,37.6-16.4,38.5-36.9L564.7,53.2z M576.3,74 c-6.5,6.5-14.3,9.9-22.6,9.9c-9.9,0-17.5-3.3-23.9-9.3c-2.8-2.6-2.6-3.5,0.8-5.1c3.6-1.8,7.2-3.5,10.8-5.2c4.5-2.2,5.2-8.1,1.4-11.2 l-0.1-0.1l20,0.2c-3.4,2.7-2.8,8.7,1.2,10.7c3.9,1.9,7.8,3.7,11.7,5.6C578.3,70.9,578.5,71.8,576.3,74z"
style="fill:#ffffff" />
<g
id="g79"
style="fill:#ffffff">
<path
id="path71"
class="st0"
d="M638.5,48.2c-1.2-1-2.3-2.2-3.2-3.5h-16.7v9.2h29.1c-1.4-0.6-2.6-1.3-4-2.2 C641.6,50.4,640.3,49.7,638.5,48.2z"
style="fill:#ffffff" />
<path
id="path73"
class="st0"
d="M684.9,44.7h-31.3c1,0.5,2.1,1,3.2,1.5c2.2,1,3.5,1.8,5.6,3.1s4.2,2.3,6,3.8 c0.4,0.3,0.7,0.6,1,0.9h15.5V44.7z"
style="fill:#ffffff" />
<path
id="path75"
class="st0"
d="M651.5,44.2c-1.4-0.7-2.6-1.4-3.6-2.2c-1.1-0.8-1.9-1.7-2.5-2.8c-0.6-1-0.9-2.2-0.9-3.7 c0-2.1,0.7-3.8,2-5.1c1.3-1.3,3.2-2,5.7-2c1.1,0,2.2,0.1,3.2,0.4s1.9,0.8,2.7,1.4c0.8,0.7,1.5,1.6,2,2.7s0.9,2.5,1.1,4.2h7.4 c0.2,0,0.5,0,0.9,0s0.9,0,1.3,0c0.5,0,0.9,0,1.3,0c0.4,0,0.7,0,0.9,0c-0.1-3.3-0.7-6.1-1.8-8.5s-2.5-4.4-4.4-5.9s-4-2.7-6.5-3.4 c-2.5-0.7-5.1-1.1-8-1.1c-2.6,0-5,0.4-7.4,1.1s-4.4,1.8-6.3,3.2c-1.8,1.4-3.3,3.2-4.3,5.3c-1.1,2.1-1.6,4.6-1.6,7.6 s0.6,5.5,1.7,7.5c0.3,0.6,0.7,1.2,1.1,1.8h17.3C652.2,44.5,651.9,44.3,651.5,44.2z"
style="fill:#ffffff" />
<path
id="path77"
class="st0"
d="M651.1,55c1.5,0.7,2.9,1.3,4.2,1.9s2.6,1.3,3.6,2.1s1.9,1.7,2.5,2.8s0.9,2.4,0.9,4 c0,1.7-0.3,3.2-1,4.3c-0.7,1.1-1.5,2-2.5,2.7s-2.1,1.1-3.3,1.4s-2.4,0.4-3.5,0.4c-1.3,0-2.5-0.2-3.7-0.7c-1.1-0.4-2.1-1.2-2.9-2.1 c-0.8-1-1.5-2.3-2-3.8c-0.5-1.6-0.8-3.5-0.8-5.7h-12.1c0.1,3.7,0.8,7,1.9,9.8c1.2,2.8,2.7,5.2,4.7,7c1.9,1.9,4.2,3.3,6.7,4.2 s5.3,1.4,8.1,1.4c2.9,0,5.8-0.4,8.4-1.1c2.7-0.7,5-1.9,7.1-3.5c2-1.6,3.7-3.6,4.9-6.1s1.8-5.5,1.8-9.1c0-2.6-0.5-4.9-1.6-6.8 c-0.9-1.5-2-2.9-3.3-4.1h-20.7C649.5,54.3,650.3,54.6,651.1,55z"
style="fill:#ffffff" />
</g>
</svg>
<path
class="st0"
d="m 728.1,80.1 c -1.9,0 -3.6,0.9 -4.7,2.4 -5.3,-2 -9.1,-7.1 -9.1,-13.1 0,-7.8 6.3,-14.1 14.1,-14.1 1.3,0 2.4,-1.1 2.4,-2.4 0,-1.3 -1.1,-2.4 -2.4,-2.4 -10.4,0 -18.9,8.5 -18.9,18.9 0,8.4 5.5,15.5 13,17.9 0.6,2.5 2.9,4.3 5.6,4.3 3.2,0 5.8,-2.6 5.8,-5.8 0,-3.1 -2.6,-5.7 -5.8,-5.7 z m 50.1,-66.4 c -0.6,-2.5 -2.9,-4.3 -5.6,-4.3 -3.2,0 -5.8,2.6 -5.8,5.8 0,3.2 2.6,5.8 5.8,5.8 1.9,0 3.6,-0.9 4.7,-2.4 5.3,2 9.1,7.1 9.1,13.1 0,7.8 -6.3,14.1 -14.1,14.1 -1.3,0 -2.4,1.1 -2.4,2.4 0,1.3 1.1,2.4 2.4,2.4 10.4,0 18.9,-8.5 18.9,-18.9 0,-8.5 -5.5,-15.5 -13,-18 z M 750.4,34 c -9.1,0 -16.5,7.4 -16.5,16.5 0,9.1 7.4,16.5 16.5,16.5 9.1,0 16.5,-7.4 16.5,-16.5 0,-9.1 -7.4,-16.5 -16.5,-16.5 z"
id="path22"
inkscape:connector-curvature="0"
style="fill:#ffffff" />
<path
class="st0"
d="m 820.6,37.6 c 0,3.2 2.7,5.9 5.9,5.9 3.2,0 5.9,-2.7 5.9,-5.9 V 25.4 h 10.1 l -16,-16 -16,16 h 10.1 z m 8.4,42.5 c -1.9,0 -3.6,0.9 -4.7,2.4 -5.3,-2 -9.1,-7.1 -9.1,-13.1 0,-7.8 6.3,-14.1 14.1,-14.1 1.3,0 2.4,-1.1 2.4,-2.4 0,-1.3 -1.1,-2.4 -2.4,-2.4 -10.4,0 -18.9,8.5 -18.9,18.9 0,8.4 5.5,15.5 13,17.9 0.6,2.5 2.9,4.3 5.6,4.3 3.2,0 5.8,-2.6 5.8,-5.8 0,-3.2 -2.6,-5.7 -5.8,-5.7 z M 851.2,34 c -9.1,0 -16.5,7.4 -16.5,16.5 0,9.1 7.4,16.5 16.5,16.5 9.1,0 16.5,-7.4 16.5,-16.5 0,-9.1 -7.4,-16.5 -16.5,-16.5 z M 879,13.7 c -0.6,-2.5 -2.9,-4.3 -5.6,-4.3 -3.2,0 -5.8,2.6 -5.8,5.8 0,3.2 2.6,5.8 5.8,5.8 1.9,0 3.6,-0.9 4.7,-2.4 5.3,2 9.1,7.1 9.1,13.1 0,7.8 -6.3,14.1 -14.1,14.1 -1.3,0 -2.4,1.1 -2.4,2.4 0,1.3 1.1,2.4 2.4,2.4 10.4,0 18.9,-8.5 18.9,-18.9 0,-8.5 -5.4,-15.5 -13,-18 z"
id="path24"
inkscape:connector-curvature="0"
style="fill:#ffffff" />
<path
class="st0"
d="m 970.5,20.9 c 1.9,0 3.6,-0.9 4.7,-2.4 5.3,2 9.1,7.1 9.1,13.1 0,7.8 -6.3,14.1 -14.1,14.1 -1.3,0 -2.4,1.1 -2.4,2.4 0,1.3 1.1,2.4 2.4,2.4 10.4,0 18.9,-8.5 18.9,-18.9 0,-8.4 -5.5,-15.5 -13,-17.9 -0.6,-2.5 -2.9,-4.3 -5.6,-4.3 -3.2,0 -5.8,2.6 -5.8,5.8 0,3.2 2.6,5.7 5.8,5.7 z m -44.4,59.2 c -1.9,0 -3.6,0.9 -4.7,2.4 -5.3,-2 -9.1,-7.1 -9.1,-13.1 0,-7.8 6.3,-14.1 14.1,-14.1 1.3,0 2.4,-1.1 2.4,-2.4 0,-1.3 -1.1,-2.4 -2.4,-2.4 -10.4,0 -18.9,8.5 -18.9,18.9 0,8.4 5.5,15.5 13,17.9 0.6,2.5 2.9,4.3 5.6,4.3 3.2,0 5.8,-2.6 5.8,-5.8 -0.1,-3.1 -2.7,-5.7 -5.8,-5.7 z m 38.7,-29.6 c 0,-9.1 -7.4,-16.5 -16.5,-16.5 -9.1,0 -16.5,7.4 -16.5,16.5 0,9.1 7.4,16.5 16.5,16.5 9.1,0 16.5,-7.4 16.5,-16.5 z m 14.1,12.9 c 0,-3.2 -2.7,-5.9 -5.9,-5.9 -3.2,0 -5.9,2.7 -5.9,5.9 V 75.6 H 957 l 16,16 16,-16 h -10.1 z"
id="path26"
inkscape:connector-curvature="0"
style="fill:#ffffff" />
<path
class="st0"
d="m 1020.2,37.6 c 0,3.2 2.7,5.9 5.9,5.9 3.2,0 5.9,-2.7 5.9,-5.9 V 25.4 h 10.1 l -16,-16 -16,16 h 10.1 z m 47.1,12.9 c 0,-9.1 -7.4,-16.5 -16.5,-16.5 -9.1,0 -16.5,7.4 -16.5,16.5 0,6.3 3.5,11.8 8.7,14.5 -4,3.7 -6.6,10 -6.6,17.3 0,0 0.6,-6 14.3,-6 13.7,0 14.3,6 14.3,6 0,-7.3 -2.6,-13.6 -6.6,-17.3 5.4,-2.7 8.9,-8.2 8.9,-14.5 z m 11.3,-36.8 c -0.6,-2.5 -2.9,-4.3 -5.6,-4.3 -3.2,0 -5.8,2.6 -5.8,5.8 0,3.2 2.6,5.8 5.8,5.8 1.9,0 3.6,-0.9 4.7,-2.4 5.3,2 9.1,7.1 9.1,13.1 0,7.8 -6.3,14.1 -14.1,14.1 -1.3,0 -2.4,1.1 -2.4,2.4 0,1.3 1.1,2.4 2.4,2.4 10.4,0 18.9,-8.5 18.9,-18.9 0,-8.5 -5.4,-15.5 -13,-18 z m -50,66.4 c -1.9,0 -3.6,0.9 -4.7,2.4 -5.3,-2 -9.1,-7.1 -9.1,-13.1 0,-7.8 6.3,-14.1 14.1,-14.1 1.3,0 2.4,-1.1 2.4,-2.4 0,-1.3 -1.1,-2.4 -2.4,-2.4 -10.4,0 -18.9,8.5 -18.9,18.9 0,8.4 5.5,15.5 13,17.9 0.6,2.5 2.9,4.3 5.6,4.3 3.2,0 5.8,-2.6 5.8,-5.8 0,-3.2 -2.6,-5.7 -5.8,-5.7 z"
id="path28"
inkscape:connector-curvature="0"
style="fill:#ffffff" />
<path
class="st0"
d="m 1180.3,63.4 c 0,-3.2 -2.7,-5.9 -5.9,-5.9 -3.2,0 -5.9,2.7 -5.9,5.9 v 12.2 h -5.3 c -1.1,-4.4 -3.1,-8.1 -5.8,-10.5 5.2,-2.8 8.7,-8.2 8.7,-14.5 0,-9.1 -7.4,-16.5 -16.5,-16.5 -9.1,0 -16.5,7.4 -16.5,16.5 0,6.3 3.5,11.8 8.7,14.5 -4,3.7 -6.6,10 -6.6,17.3 0,0 0.6,-6 14.3,-6 13.7,0 14.3,6 14.3,6 0,-0.4 0,-0.8 0,-1.2 l 10.5,10.5 16,-16 h -10.1 V 63.4 Z m -8.4,-42.5 c 1.9,0 3.6,-0.9 4.7,-2.4 5.3,2 9.1,7.1 9.1,13.1 0,7.8 -6.3,14.1 -14.1,14.1 -1.3,0 -2.4,1.1 -2.4,2.4 0,1.3 1.1,2.4 2.4,2.4 10.4,0 18.9,-8.5 18.9,-18.9 0,-8.4 -5.5,-15.5 -13,-17.9 -0.6,-2.5 -2.9,-4.3 -5.6,-4.3 -3.2,0 -5.8,2.6 -5.8,5.8 0,3.2 2.7,5.7 5.8,5.7 z m -44.4,59.2 c -1.9,0 -3.6,0.9 -4.7,2.4 -5.3,-2 -9.1,-7.1 -9.1,-13.1 0,-7.8 6.3,-14.1 14.1,-14.1 1.3,0 2.4,-1.1 2.4,-2.4 0,-1.3 -1.1,-2.4 -2.4,-2.4 -10.4,0 -18.9,8.5 -18.9,18.9 0,8.4 5.5,15.5 13,17.9 0.6,2.5 2.9,4.3 5.6,4.3 3.2,0 5.8,-2.6 5.8,-5.8 -0.1,-3.1 -2.7,-5.7 -5.8,-5.7 z"
id="path30"
inkscape:connector-curvature="0"
style="fill:#ffffff" />
</svg>

Before

(image error) Size: 4.7 KiB

After

(image error) Size: 11 KiB

View file

@ -25,7 +25,6 @@ OriginalDesktop.Desktop {
Action {
text: "Open Browser"
shortcut: "Ctrl+B"
onTriggered: {
console.log("Open browser");
browserBuilder.createObject(desktop);

View file

@ -664,7 +664,7 @@ Rectangle {
}
RalewayRegular {
id: systemInjectorGainSliderText;
text: "System Sound volume";
text: "UI FX volume";
size: 16;
anchors.left: parent.left;
color: hifi.colors.white;

View file

@ -77,6 +77,21 @@ Rectangle {
Tablet.playSound(TabletEnums.ButtonClick);
muted = Qt.binding(function() { return AudioScriptingInterface.muted; }); // restore binding
}
onPressed: {
if (pushToTalk) {
AudioScriptingInterface.pushingToTalk = true;
Tablet.playSound(TabletEnums.ButtonClick);
}
}
onReleased: {
if (pushToTalk) {
AudioScriptingInterface.pushingToTalk = false;
Tablet.playSound(TabletEnums.ButtonClick);
}
}
onContainsMouseChanged: {
if (containsMouse) {
Tablet.playSound(TabletEnums.ButtonHover);

View file

@ -126,7 +126,7 @@ Flickable {
Layout.preferredWidth: parent.width
Layout.preferredHeight: 30
Layout.topMargin: 2
labelText: "System Sound Volume"
labelText: "UI FX Volume"
from: simplifiedUI.numericConstants.mutedValue
to: 20.0
defaultValue: 0.0

View file

@ -260,9 +260,7 @@ Rectangle {
id: statusButton
property string currentStatus
anchors.centerIn: parent
anchors.horizontalCenterOffset: 1
anchors.verticalCenterOffset: 2
width: 13
width: 15
height: width
radius: width/2
visible: false
@ -270,7 +268,7 @@ Rectangle {
ColorOverlay {
anchors.fill: statusButton
opacity: statusButton.currentStatus ? 1 : 0
opacity: statusButton.currentStatus ? (statusButtonMouseArea.containsMouse ? 1.0 : 0.7) : 0.7
source: statusButton
color: if (statusButton.currentStatus === "busy") {
"#ff001a"
@ -278,19 +276,11 @@ Rectangle {
"#009036"
} else if (statusButton.currentStatus) {
"#ffed00"
} else {
"#7e8c81"
}
}
Image {
id: focusIcon
source: "./images/focus.svg"
opacity: statusButtonMouseArea.containsMouse ? 1.0 : (statusButton.currentStatus === "busy" ? 0.7 : 0.3)
anchors.centerIn: parent
width: 36
height: 20
fillMode: Image.PreserveAspectFit
}
MouseArea {
id: statusButtonMouseArea
anchors.fill: parent

View file

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="32" height="19.001" fill="none" version="1.1" viewBox="0 0 32 19.001" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<metadata>
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<dc:title/>
</cc:Work>
</rdf:RDF>
</metadata>
<path d="m7 19h-3.542c-0.46115 0.0089-0.91905-0.0192-1.3443-0.2001-0.42525-0.1808-0.80846-0.4496-1.125-0.7892-0.3234-0.3355-0.57737-0.7329-0.74698-1.1691-0.16962-0.4362-0.25146-0.9023-0.24076-1.3709v-5.47c-0.0066251-0.46326 0.078429-0.92318 0.25019-1.3526 0.17176-0.42944 0.42674-0.81971 0.74988-1.1478 0.32313-0.32808 0.70786-0.58732 1.1316-0.76244 0.42368-0.17511 0.87772-0.26255 1.3353-0.25716h1.4718c0.03732-0.87492 0.24704-1.7335 0.61682-2.5251s0.89216-1.5005 1.5364-2.0849c1.2696-1.2214 3.1558-1.8924 4.9071-1.87l8 2.2471e-5c1.7402-0.025877 3.6023 0.64637 4.8546 1.87 1.2793 1.2015 2.0626 2.8482 2.1927 4.61h1.3178c0.946-0.01712 1.8609 0.3419 2.5484 1 0.3479 0.31321 0.625 0.69864 0.8125 1.13 0.1875 0.43142 0.2809 0.89867 0.274 1.37v5.47c0.0037 0.4767-0.091 0.9488-0.2781 1.3863-0.1871 0.4374-0.376 0.8397-0.7219 1.163-0.3346 0.3376-0.5167 0.5951-0.9542 0.7753-0.4376 0.1803-1.2083 0.2298-1.6807 0.2247h-3.3624v-12.049c0.0051-0.65734-0.1179-1.3092-0.362-1.9184-0.2441-0.60915-0.6044-1.1636-1.0603-1.6316-0.4213-0.48067-0.9384-0.86553-1.5173-1.1292-0.5788-0.26371-1.4283-0.27101-2.0631-0.27147h-8c-0.6475-0.00706-1.4451-0.00352-2.0378 0.26031-0.59274 0.26384-1.1231 0.65262-1.5556 1.1404-0.45594 0.46802-0.81626 1.0225-1.0603 1.6316-0.24406 0.60916-0.35129 1.261-0.34623 1.9184zm-3.4717-2h1.4717v-8.4793h-1.4717c-0.92847 0-1.5283 0.49-1.5283 1.48v5.47c0.01975 0.99 0.59981 1.5293 1.5283 1.5293zm26.472-6.9993c8e-3 -0.84434-0.6554-1.4883-1.5049-1.48h-1.4478v8.4793h1.4478c0.8319 0.0109 1.5175-0.722 1.5049-1.5293z" fill="#fff"/>
</svg>

Before

(image error) Size: 2 KiB

View file

@ -49,7 +49,10 @@ Item {
}
function pushSource(path) {
d.push(Qt.resolvedUrl("../../" + path));
// Workaround issue https://bugreports.qt.io/browse/QTBUG-75516 in Qt 5.12.3
// by creating the manually, instead of letting StackView do it for us.
var item = Qt.createComponent(Qt.resolvedUrl("../../" + path));
d.push(item);
if (d.currentItem.sendToScript !== undefined) {
d.currentItem.sendToScript.connect(tabletMenu.sendToScript);
}

View file

@ -868,7 +868,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<EyeTracker>();
DependencyManager::set<AudioClient>();
DependencyManager::set<AudioScope>();
@ -1312,12 +1316,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL());
// use our MyAvatar position and quat for address manager path
addressManager->setPositionGetter([this] {
addressManager->setPositionGetter([] {
auto avatarManager = DependencyManager::get<AvatarManager>();
auto myAvatar = avatarManager ? avatarManager->getMyAvatar() : nullptr;
return myAvatar ? myAvatar->getWorldFeetPosition() : Vectors::ZERO;
});
addressManager->setOrientationGetter([this] {
addressManager->setOrientationGetter([] {
auto avatarManager = DependencyManager::get<AvatarManager>();
auto myAvatar = avatarManager ? avatarManager->getMyAvatar() : nullptr;
return myAvatar ? myAvatar->getWorldOrientation() : glm::quat();
@ -2762,7 +2766,6 @@ void Application::cleanupBeforeQuit() {
// this must happen after QML, as there are unexplained audio crashes originating in qtwebengine
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "stop");
DependencyManager::destroy<AudioClient>();
DependencyManager::destroy<AudioInjectorManager>();
DependencyManager::destroy<AudioScriptingInterface>();
// The PointerManager must be destroyed before the PickManager because when a Pointer is deleted,
@ -2822,6 +2825,7 @@ Application::~Application() {
DependencyManager::destroy<SoundCacheScriptingInterface>();
DependencyManager::destroy<AudioInjectorManager>();
DependencyManager::destroy<AvatarManager>();
DependencyManager::destroy<AnimationCacheScriptingInterface>();
DependencyManager::destroy<AnimationCache>();
@ -3327,7 +3331,9 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) {
surfaceContext->setContextProperty("AccountServices", AccountServicesScriptingInterface::getInstance());
surfaceContext->setContextProperty("DialogsManager", _dialogsManagerScriptingInterface);
#ifdef HAVE_DDE
surfaceContext->setContextProperty("FaceTracker", DependencyManager::get<DdeFaceTracker>().data());
#endif
surfaceContext->setContextProperty("AvatarManager", DependencyManager::get<AvatarManager>().data());
surfaceContext->setContextProperty("LODManager", DependencyManager::get<LODManager>().data());
surfaceContext->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
@ -4306,10 +4312,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
break;
case Qt::Key_B:
if (isMeta) {
auto offscreenUi = getOffscreenUI();
offscreenUi->load("Browser.qml");
} else if (isOption) {
if (isOption) {
controller::InputRecorder* inputRecorder = controller::InputRecorder::getInstance();
inputRecorder->stopPlayback();
}
@ -4348,12 +4351,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
}
break;
case Qt::Key_N:
if (!isOption && !isShifted && isMeta) {
DependencyManager::get<NodeList>()->toggleIgnoreRadius();
}
break;
case Qt::Key_S:
if (isShifted && isMeta && !isOption) {
Menu::getInstance()->triggerOption(MenuOption::SuppressShortTimings);
@ -5177,9 +5174,15 @@ ivec2 Application::getMouse() const {
}
FaceTracker* Application::getActiveFaceTracker() {
#ifdef HAVE_DDE
auto dde = DependencyManager::get<DdeFaceTracker>();
return dde->isActive() ? static_cast<FaceTracker*>(dde.data()) : nullptr;
if (dde && dde->isActive()) {
return static_cast<FaceTracker*>(dde.data());
}
#endif
return nullptr;
}
FaceTracker* Application::getSelectedFaceTracker() {
@ -5378,25 +5381,13 @@ void Application::loadSettings() {
}
}
if (_firstRun.get()) {
// If this is our first run, evalute the Platform Tier and assign the matching Performance profile by default.
// A bunch of Performance, Simulation and Render settings will be set to a matching default value from this
// Load settings of the RenderScritpingInterface
// Do that explicitely before being used
RenderScriptingInterface::getInstance()->loadSettings();
// Here is the mapping between pelatformTIer and performance profile
const std::array<PerformanceManager::PerformancePreset, platform::Profiler::NumTiers> platformToPerformancePresetMap = {{
PerformanceManager::PerformancePreset::MID, // platform::Profiler::UNKNOWN
PerformanceManager::PerformancePreset::LOW, // platform::Profiler::LOW
PerformanceManager::PerformancePreset::MID, // platform::Profiler::MID
PerformanceManager::PerformancePreset::HIGH // platform::Profiler::HIGH
}};
// What is our profile?
auto platformTier = platform::Profiler::profilePlatform();
// Then let's assign the performance preset setting from it
getPerformanceManager().setPerformancePreset(platformToPerformancePresetMap[platformTier]);
}
// Setup the PerformanceManager which will enforce the several settings to match the Preset
// On the first run, the Preset is evaluated from the
getPerformanceManager().setupPerformancePresetSettings(_firstRun.get());
// finish initializing the camera, based on everything we checked above. Third person camera will be used if no settings
// dictated that we should be in first person
@ -7025,7 +7016,10 @@ void Application::copyDisplayViewFrustum(ViewFrustum& viewOut) const {
// feature. However, we still use this to reset face trackers, eye trackers, audio and to optionally re-load the avatar
// rig and animations from scratch.
void Application::resetSensors(bool andReload) {
#ifdef HAVE_DDE
DependencyManager::get<DdeFaceTracker>()->reset();
#endif
DependencyManager::get<EyeTracker>()->reset();
_overlayConductor.centerUI();
getActiveDisplayPlugin()->resetSensors();
@ -7225,7 +7219,7 @@ void Application::nodeKilled(SharedNodePointer node) {
_octreeProcessor.nodeKilled(node);
_entityEditSender.nodeKilled(node);
if (node->getType() == NodeType::AudioMixer) {
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "audioMixerKilled");
} else if (node->getType() == NodeType::EntityServer) {
@ -7413,8 +7407,10 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
scriptEngine->registerGlobalObject("AccountServices", AccountServicesScriptingInterface::getInstance());
qScriptRegisterMetaType(scriptEngine.data(), DownloadInfoResultToScriptValue, DownloadInfoResultFromScriptValue);
#ifdef HAVE_DDE
scriptEngine->registerGlobalObject("FaceTracker", DependencyManager::get<DdeFaceTracker>().data());
#endif
scriptEngine->registerGlobalObject("AvatarManager", DependencyManager::get<AvatarManager>().data());
scriptEngine->registerGlobalObject("LODManager", DependencyManager::get<LODManager>().data());

View file

@ -10,14 +10,38 @@
//
#include "PerformanceManager.h"
#include <platform/Profiler.h>
#include "scripting/RenderScriptingInterface.h"
PerformanceManager::PerformanceManager()
{
setPerformancePreset((PerformancePreset) _performancePresetSetting.get());
}
void PerformanceManager::setupPerformancePresetSettings(bool evaluatePlatformTier) {
if (evaluatePlatformTier || (getPerformancePreset() == UNKNOWN)) {
// If evaluatePlatformTier, evalute the Platform Tier and assign the matching Performance profile by default.
// A bunch of Performance, Simulation and Render settings will be set to a matching default value from this
// Here is the mapping between pelatformTIer and performance profile
const std::array<PerformanceManager::PerformancePreset, platform::Profiler::NumTiers> platformToPerformancePresetMap = { {
PerformanceManager::PerformancePreset::MID, // platform::Profiler::UNKNOWN
PerformanceManager::PerformancePreset::LOW, // platform::Profiler::LOW
PerformanceManager::PerformancePreset::MID, // platform::Profiler::MID
PerformanceManager::PerformancePreset::HIGH // platform::Profiler::HIGH
} };
// What is our profile?
auto platformTier = platform::Profiler::profilePlatform();
// Then let's assign the performance preset setting from it
setPerformancePreset(platformToPerformancePresetMap[platformTier]);
}
}
void PerformanceManager::setPerformancePreset(PerformanceManager::PerformancePreset preset) {
if (getPerformancePreset() != preset) {
if (isValidPerformancePreset(preset) && (getPerformancePreset() != preset)) {
_performancePresetSettingLock.withWriteLock([&] {
_performancePresetSetting.set((int)preset);
});
@ -27,7 +51,7 @@ void PerformanceManager::setPerformancePreset(PerformanceManager::PerformancePre
}
PerformanceManager::PerformancePreset PerformanceManager::getPerformancePreset() const {
PerformancePreset preset = PerformancePreset::MID;
PerformancePreset preset = PerformancePreset::UNKNOWN;
preset = (PerformancePreset) _performancePresetSettingLock.resultWithReadLock<int>([&] {
return _performancePresetSetting.get();
@ -42,7 +66,7 @@ void PerformanceManager::applyPerformancePreset(PerformanceManager::PerformanceP
case PerformancePreset::HIGH:
RenderScriptingInterface::getInstance()->setRenderMethod(RenderScriptingInterface::RenderMethod::DEFERRED);
RenderScriptingInterface::getInstance()->setShadowsEnabled(true);
qApp->getRefreshRateManager().setRefreshRateProfile(RefreshRateManager::RefreshRateProfile::INTERACTIVE);
qApp->getRefreshRateManager().setRefreshRateProfile(RefreshRateManager::RefreshRateProfile::REALTIME);
break;
case PerformancePreset::MID:
@ -57,7 +81,9 @@ void PerformanceManager::applyPerformancePreset(PerformanceManager::PerformanceP
qApp->getRefreshRateManager().setRefreshRateProfile(RefreshRateManager::RefreshRateProfile::ECO);
break;
case PerformancePreset::UNKNOWN:
default:
// Do nothing anymore
break;
}
}

View file

@ -20,21 +20,27 @@
class PerformanceManager {
public:
enum PerformancePreset {
LOW = 0,
UNKNOWN = 0, // Matching the platform Tier profiles enumeration for coherence
LOW,
MID,
HIGH,
PROFILE_COUNT
};
static bool isValidPerformancePreset(int value) { return (value >= PerformancePreset::UNKNOWN && value <= PerformancePreset::HIGH); }
PerformanceManager();
~PerformanceManager() = default;
// Setup the PerformanceManager which will enforce the several settings to match the Preset
// If evaluatePlatformTier is true, the Preset is evaluated from the Platform::Profiler::profilePlatform()
void setupPerformancePresetSettings(bool evaluatePlatformTier);
void setPerformancePreset(PerformancePreset performancePreset);
PerformancePreset getPerformancePreset() const;
private:
mutable ReadWriteLockable _performancePresetSettingLock;
Setting::Handle<int> _performancePresetSetting { "performancePreset", PerformanceManager::PerformancePreset::MID };
Setting::Handle<int> _performancePresetSetting { "performancePreset", PerformanceManager::PerformancePreset::UNKNOWN };
// The concrete performance preset changes
void applyPerformancePreset(PerformanceManager::PerformancePreset performancePreset);

View file

@ -94,7 +94,7 @@ void RefreshRateManager::toggleInactive() {
}
void RefreshRateManager::setRefreshRateProfile(RefreshRateManager::RefreshRateProfile refreshRateProfile) {
if (_refreshRateProfile != refreshRateProfile) {
if (isValidRefreshRateProfile(refreshRateProfile) && (_refreshRateProfile != refreshRateProfile)) {
_refreshRateProfileSettingLock.withWriteLock([&] {
_refreshRateProfile = refreshRateProfile;
_refreshRateProfileSetting.set((int) refreshRateProfile);
@ -124,7 +124,7 @@ RefreshRateManager::RefreshRateRegime RefreshRateManager::getRefreshRateRegime()
}
void RefreshRateManager::setRefreshRateRegime(RefreshRateManager::RefreshRateRegime refreshRateRegime) {
if (_refreshRateRegime != refreshRateRegime) {
if (isValidRefreshRateRegime(refreshRateRegime) && (_refreshRateRegime != refreshRateRegime)) {
_refreshRateRegime = refreshRateRegime;
updateRefreshRateController();
}
@ -132,7 +132,7 @@ void RefreshRateManager::setRefreshRateRegime(RefreshRateManager::RefreshRateReg
}
void RefreshRateManager::setUXMode(RefreshRateManager::UXMode uxMode) {
if (_uxMode != uxMode) {
if (isValidUXMode(uxMode) && (_uxMode != uxMode)) {
_uxMode = uxMode;
updateRefreshRateController();
}

View file

@ -29,6 +29,7 @@ public:
REALTIME,
PROFILE_NUM
};
static bool isValidRefreshRateProfile(RefreshRateProfile value) { return (value >= RefreshRateProfile::ECO && value <= RefreshRateProfile::REALTIME); }
enum RefreshRateRegime {
FOCUS_ACTIVE = 0,
@ -39,12 +40,14 @@ public:
SHUTDOWN,
REGIME_NUM
};
static bool isValidRefreshRateRegime(RefreshRateRegime value) { return (value >= RefreshRateRegime::FOCUS_ACTIVE && value <= RefreshRateRegime::SHUTDOWN); }
enum UXMode {
DESKTOP = 0,
VR,
UX_NUM
};
static bool isValidUXMode(UXMode value) { return (value >= UXMode::DESKTOP && value <= UXMode::VR); }
RefreshRateManager();
~RefreshRateManager() = default;

View file

@ -597,7 +597,7 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar
avatar->fadeOut(transaction, removalReason);
workload::SpacePointer space = _space;
transaction.transitionFinishedOperator(avatar->getRenderItemID(), [space, avatar]() {
transaction.setTransitionFinishedOperator(avatar->getRenderItemID(), [space, avatar]() {
if (avatar->getLastFadeRequested() != render::Transition::Type::USER_LEAVE_DOMAIN) {
// The avatar is using another transition besides the fade-out transition, which means it is still in use.
// Deleting the avatar now could cause state issues, so abort deletion and show message.

View file

@ -4947,35 +4947,55 @@ void MyAvatar::setWalkSpeed(float value) {
}
void MyAvatar::setWalkBackwardSpeed(float value) {
bool changed = true;
float prevVal;
switch (_controlSchemeIndex) {
case LocomotionControlsMode::CONTROLS_DEFAULT:
prevVal = _defaultWalkBackwardSpeed.get();
_defaultWalkBackwardSpeed.set(value);
break;
case LocomotionControlsMode::CONTROLS_ANALOG:
prevVal = _analogWalkBackwardSpeed.get();
_analogWalkBackwardSpeed.set(value);
break;
case LocomotionControlsMode::CONTROLS_ANALOG_PLUS:
prevVal = _analogPlusWalkBackwardSpeed.get();
_analogPlusWalkBackwardSpeed.set(value);
break;
default:
changed = false;
break;
}
if (changed && prevVal != value) {
emit walkBackwardSpeedChanged(value);
}
}
void MyAvatar::setSprintSpeed(float value) {
bool changed = true;
float prevVal;
switch (_controlSchemeIndex) {
case LocomotionControlsMode::CONTROLS_DEFAULT:
prevVal = _defaultSprintSpeed.get();
_defaultSprintSpeed.set(value);
break;
case LocomotionControlsMode::CONTROLS_ANALOG:
prevVal = _analogSprintSpeed.get();
_analogSprintSpeed.set(value);
break;
case LocomotionControlsMode::CONTROLS_ANALOG_PLUS:
prevVal = _analogPlusSprintSpeed.get();
_analogPlusSprintSpeed.set(value);
break;
default:
changed = false;
break;
}
if (changed && prevVal != value) {
emit analogPlusSprintSpeedChanged(value);
}
}
float MyAvatar::getSprintSpeed() const {
@ -5013,9 +5033,12 @@ float MyAvatar::getAnalogSprintSpeed() const {
}
void MyAvatar::setAnalogPlusWalkSpeed(float value) {
_analogPlusWalkSpeed.set(value);
// Sprint speed for Analog Plus should be double walk speed.
_analogPlusSprintSpeed.set(value * 2.0f);
if (_analogPlusWalkSpeed.get() != value) {
_analogPlusWalkSpeed.set(value);
emit analogPlusWalkSpeedChanged(value);
// Sprint speed for Analog Plus should be double walk speed.
_analogPlusSprintSpeed.set(value * 2.0f);
}
}
float MyAvatar::getAnalogPlusWalkSpeed() const {
@ -5023,7 +5046,10 @@ float MyAvatar::getAnalogPlusWalkSpeed() const {
}
void MyAvatar::setAnalogPlusSprintSpeed(float value) {
_analogPlusSprintSpeed.set(value);
if (_analogPlusSprintSpeed.get() != value) {
_analogPlusSprintSpeed.set(value);
emit analogPlusSprintSpeedChanged(value);
}
}
float MyAvatar::getAnalogPlusSprintSpeed() const {

42
interface/src/avatar/MyAvatar.h Executable file → Normal file
View file

@ -370,10 +370,10 @@ class MyAvatar : public Avatar {
Q_PROPERTY(QUuid SELF_ID READ getSelfID CONSTANT)
Q_PROPERTY(float walkSpeed READ getWalkSpeed WRITE setWalkSpeed);
Q_PROPERTY(float analogPlusWalkSpeed READ getAnalogPlusWalkSpeed WRITE setAnalogPlusWalkSpeed);
Q_PROPERTY(float analogPlusSprintSpeed READ getAnalogPlusSprintSpeed WRITE setAnalogPlusSprintSpeed);
Q_PROPERTY(float walkBackwardSpeed READ getWalkBackwardSpeed WRITE setWalkBackwardSpeed);
Q_PROPERTY(float sprintSpeed READ getSprintSpeed WRITE setSprintSpeed);
Q_PROPERTY(float analogPlusWalkSpeed READ getAnalogPlusWalkSpeed WRITE setAnalogPlusWalkSpeed NOTIFY analogPlusWalkSpeedChanged);
Q_PROPERTY(float analogPlusSprintSpeed READ getAnalogPlusSprintSpeed WRITE setAnalogPlusSprintSpeed NOTIFY analogPlusSprintSpeedChanged);
Q_PROPERTY(float walkBackwardSpeed READ getWalkBackwardSpeed WRITE setWalkBackwardSpeed NOTIFY walkBackwardSpeedChanged);
Q_PROPERTY(float sprintSpeed READ getSprintSpeed WRITE setSprintSpeed NOTIFY sprintSpeedChanged);
Q_PROPERTY(bool isInSittingState READ getIsInSittingState WRITE setIsInSittingState);
Q_PROPERTY(MyAvatar::SitStandModelType userRecenterModel READ getUserRecenterModel WRITE setUserRecenterModel);
Q_PROPERTY(bool isSitStandStateLocked READ getIsSitStandStateLocked WRITE setIsSitStandStateLocked);
@ -2161,6 +2161,38 @@ signals:
*/
void audioListenerModeChanged();
/**jsdoc
* Notifies when the analogPlusWalkSpeed value is changed.
* @function MyAvatar.analogPlusWalkSpeedChanged
* @param {float} value - the new avatar walk speed
* @returns {Signal}
*/
void analogPlusWalkSpeedChanged(float value);
/**jsdoc
* Notifies when the analogPlusSprintSpeed value is changed.
* @function MyAvatar.analogPlusSprintSpeedChanged
* @param {float} value - the new avatar sprint speed
* @returns {Signal}
*/
void analogPlusSprintSpeedChanged(float value);
/**jsdoc
* Notifies when the sprintSpeed value is changed.
* @function MyAvatar.sprintSpeedChanged
* @param {float} value - the new avatar sprint speed
* @returns {Signal}
*/
void sprintSpeedChanged(float value);
/**jsdoc
* Notifies when the walkBackwardSpeed value is changed.
* @function MyAvatar.walkBackwardSpeedChanged
* @param {float} value - the new avatar walk backward speed
* @returns {Signal}
*/
void walkBackwardSpeedChanged(float value);
/**jsdoc
* @function MyAvatar.transformChanged
* @returns {Signal}
@ -2603,7 +2635,7 @@ private:
bool _enableDebugDrawIKChains { false };
bool _enableDebugDrawDetailedCollision { false };
mutable bool _cauterizationNeedsUpdate; // do we need to scan children and update their "cauterized" state?
mutable bool _cauterizationNeedsUpdate { false }; // do we need to scan children and update their "cauterized" state?
AudioListenerMode _audioListenerMode;
glm::vec3 _customListenPosition;

View file

@ -219,7 +219,7 @@ bool OtherAvatar::isInPhysicsSimulation() const {
}
bool OtherAvatar::shouldBeInPhysicsSimulation() const {
return !isDead() && _workloadRegion < workload::Region::R3;
return !isDead() && _workloadRegion <= workload::Region::R3;
}
bool OtherAvatar::needsPhysicsUpdate() const {

View file

@ -14,8 +14,9 @@
#include <QtCore/QtGlobal>
//Disabling dde due to random crashes with closing the socket on macos. all the accompanying code is wrapped with the ifdef HAVE_DDE. uncomment the define below to enable
#if defined(Q_OS_WIN) || defined(Q_OS_OSX)
#define HAVE_DDE
//#define HAVE_DDE
#endif
#include <QProcess>

View file

@ -80,7 +80,7 @@ public:
class AudioInputDeviceList : public AudioDeviceList {
Q_OBJECT
Q_PROPERTY(bool peakValuesAvailable READ peakValuesAvailable)
Q_PROPERTY(bool peakValuesAvailable READ peakValuesAvailable CONSTANT)
Q_PROPERTY(bool peakValuesEnabled READ peakValuesEnabled WRITE setPeakValuesEnabled NOTIFY peakValuesEnabledChanged)
public:

View file

@ -21,6 +21,7 @@ PerformanceScriptingInterface::PerformanceScriptingInterface() {
void PerformanceScriptingInterface::setPerformancePreset(PerformancePreset performancePreset) {
qApp->getPerformanceManager().setPerformancePreset((PerformanceManager::PerformancePreset)performancePreset);
emit settingsChanged();
}
PerformanceScriptingInterface::PerformancePreset PerformanceScriptingInterface::getPerformancePreset() const {
@ -28,12 +29,13 @@ PerformanceScriptingInterface::PerformancePreset PerformanceScriptingInterface::
}
QStringList PerformanceScriptingInterface::getPerformancePresetNames() const {
static const QStringList performancePresetNames = { "Low", "Mid", "High" };
static const QStringList performancePresetNames = { "UNKNOWN", "LOW", "MID", "HIGH" };
return performancePresetNames;
}
void PerformanceScriptingInterface::setRefreshRateProfile(RefreshRateProfile refreshRateProfile) {
qApp->getRefreshRateManager().setRefreshRateProfile((RefreshRateManager::RefreshRateProfile)refreshRateProfile);
emit settingsChanged();
}
PerformanceScriptingInterface::RefreshRateProfile PerformanceScriptingInterface::getRefreshRateProfile() const {
@ -41,7 +43,7 @@ PerformanceScriptingInterface::RefreshRateProfile PerformanceScriptingInterface:
}
QStringList PerformanceScriptingInterface::getRefreshRateProfileNames() const {
static const QStringList refreshRateProfileNames = { "Eco", "Interactive", "Realtime" };
static const QStringList refreshRateProfileNames = { "ECO", "INTERACTIVE", "REALTIME" };
return refreshRateProfileNames;
}

View file

@ -20,10 +20,14 @@
class PerformanceScriptingInterface : public QObject {
Q_OBJECT
Q_PROPERTY(PerformancePreset performancePreset READ getPerformancePreset WRITE setPerformancePreset NOTIFY settingsChanged)
Q_PROPERTY(RefreshRateProfile refreshRateProfile READ getRefreshRateProfile WRITE setRefreshRateProfile NOTIFY settingsChanged)
public:
// PerformanceManager PerformancePreset tri state level enums
enum PerformancePreset {
UNKNOWN = PerformanceManager::PerformancePreset::UNKNOWN,
LOW = PerformanceManager::PerformancePreset::LOW,
MID = PerformanceManager::PerformancePreset::MID,
HIGH = PerformanceManager::PerformancePreset::HIGH,
@ -55,6 +59,9 @@ public slots:
RefreshRateManager::UXMode getUXMode() const;
RefreshRateManager::RefreshRateRegime getRefreshRateRegime() const;
signals:
void settingsChanged();
private:
static std::once_flag registry_flag;
};

View file

@ -186,8 +186,8 @@ public slots:
/**jsdoc
* Get the Platform TIer profiled on startup of the Computer
* Platform Tier is an ineger/enum value:
* LOW = 0, MID = 1, HIGH = 2
* Platform Tier is an integer/enum value:
* UNKNOWN = 0, LOW = 1, MID = 2, HIGH = 3
* @function PlatformInfo.getTierProfiled
* @returns {number} The Platform Tier profiled on startup.
*/
@ -195,8 +195,8 @@ public slots:
/**jsdoc
* Get the Platform Tier possible Names as an array of strings
* Platform Tier is an ineger/enum value:
* LOW = 0, MID = 1, HIGH = 2
* Platform Tier names are:
* [ "UNKNOWN", "LOW", "MID", "HIGH" ]
* @function PlatformInfo.getPlatformTierNames
* @returns {string} The array of names matching the number returned from PlatformInfo.getTierProfiled
*/

View file

@ -10,118 +10,138 @@
#include "LightingModel.h"
#include "AntialiasingEffect.h"
const QString DEFERRED = "deferred";
const QString FORWARD = "forward";
RenderScriptingInterface* RenderScriptingInterface::getInstance() {
static RenderScriptingInterface sharedInstance;
return &sharedInstance;
}
std::once_flag RenderScriptingInterface::registry_flag;
RenderScriptingInterface::RenderScriptingInterface() {
setRenderMethod((RenderMethod)_renderMethodSetting.get() == RenderMethod::DEFERRED ? RenderMethod::DEFERRED : RenderMethod::FORWARD);
setShadowsEnabled(_shadowsEnabledSetting.get());
setAmbientOcclusionEnabled(_ambientOcclusionEnabledSetting.get());
setAntialiasingEnabled(_antialiasingEnabledSetting.get());
std::call_once(registry_flag, [] {
qmlRegisterType<RenderScriptingInterface>("RenderEnums", 1, 0, "RenderEnums");
});
}
void RenderScriptingInterface::loadSettings() {
_renderSettingLock.withReadLock([&] {
_renderMethod = (_renderMethodSetting.get());
_shadowsEnabled = (_shadowsEnabledSetting.get());
_ambientOcclusionEnabled = (_ambientOcclusionEnabledSetting.get());
_antialiasingEnabled = (_antialiasingEnabledSetting.get());
});
forceRenderMethod((RenderMethod)_renderMethod);
forceShadowsEnabled(_shadowsEnabled);
forceAmbientOcclusionEnabled(_ambientOcclusionEnabled);
forceAntialiasingEnabled(_antialiasingEnabled);
}
RenderScriptingInterface::RenderMethod RenderScriptingInterface::getRenderMethod() {
return (RenderMethod)_renderMethodSetting.get() == RenderMethod::DEFERRED ? RenderMethod::DEFERRED : RenderMethod::FORWARD;
return (RenderMethod) _renderMethod;
}
void RenderScriptingInterface::setRenderMethod(RenderScriptingInterface::RenderMethod renderMethod) {
RenderMethod newMethod = renderMethod == RenderMethod::FORWARD ? RenderMethod::FORWARD : RenderMethod::DEFERRED;
if (_renderMethodSetting.get() == newMethod) {
return;
void RenderScriptingInterface::setRenderMethod(RenderMethod renderMethod) {
if (isValidRenderMethod(renderMethod) && (_renderMethod != (int) renderMethod)) {
forceRenderMethod(renderMethod);
emit settingsChanged();
}
}
void RenderScriptingInterface::forceRenderMethod(RenderMethod renderMethod) {
_renderSettingLock.withWriteLock([&] {
_renderMethod = (int)renderMethod;
_renderMethodSetting.set((int)renderMethod);
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setRenderMethod", Q_ARG(RenderScriptingInterface::RenderMethod, renderMethod));
return;
}
auto config = dynamic_cast<task::SwitchConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig("RenderMainView.DeferredForwardSwitch"));
if (config) {
_renderMethodSetting.set(newMethod);
config->setBranch(newMethod);
emit config->dirtyEnabled();
}
auto config = dynamic_cast<task::SwitchConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig("RenderMainView.DeferredForwardSwitch"));
if (config) {
config->setBranch((int)renderMethod);
}
});
}
QStringList RenderScriptingInterface::getRenderMethodNames() const {
static const QStringList refrenderMethodNames = { "Deferred", "Forward" };
static const QStringList refrenderMethodNames = { "DEFERRED", "FORWARD" };
return refrenderMethodNames;
}
bool RenderScriptingInterface::getShadowsEnabled() {
return _shadowsEnabledSetting.get();
return _shadowsEnabled;
}
void RenderScriptingInterface::setShadowsEnabled(bool enabled) {
if (_shadowsEnabledSetting.get() == enabled) {
return;
if (_shadowsEnabled != enabled) {
forceShadowsEnabled(enabled);
emit settingsChanged();
}
}
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setShadowsEnabled", Q_ARG(bool, enabled));
return;
}
auto lightingModelConfig = qApp->getRenderEngine()->getConfiguration()->getConfig<MakeLightingModel>("RenderMainView.LightingModel");
if (lightingModelConfig) {
Menu::getInstance()->setIsOptionChecked(MenuOption::Shadows, enabled);
void RenderScriptingInterface::forceShadowsEnabled(bool enabled) {
_renderSettingLock.withWriteLock([&] {
_shadowsEnabled = (enabled);
_shadowsEnabledSetting.set(enabled);
lightingModelConfig->setShadow(enabled);
}
auto lightingModelConfig = qApp->getRenderEngine()->getConfiguration()->getConfig<MakeLightingModel>("RenderMainView.LightingModel");
if (lightingModelConfig) {
Menu::getInstance()->setIsOptionChecked(MenuOption::Shadows, enabled);
lightingModelConfig->setShadow(enabled);
}
});
}
bool RenderScriptingInterface::getAmbientOcclusionEnabled() {
return _ambientOcclusionEnabledSetting.get();
return _ambientOcclusionEnabled;
}
void RenderScriptingInterface::setAmbientOcclusionEnabled(bool enabled) {
if (_ambientOcclusionEnabledSetting.get() == enabled) {
return;
if (_ambientOcclusionEnabled != enabled) {
forceAmbientOcclusionEnabled(enabled);
emit settingsChanged();
}
}
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setAmbientOcclusionEnabled", Q_ARG(bool, enabled));
return;
}
auto lightingModelConfig = qApp->getRenderEngine()->getConfiguration()->getConfig<MakeLightingModel>("RenderMainView.LightingModel");
if (lightingModelConfig) {
Menu::getInstance()->setIsOptionChecked(MenuOption::AmbientOcclusion, enabled);
void RenderScriptingInterface::forceAmbientOcclusionEnabled(bool enabled) {
_renderSettingLock.withWriteLock([&] {
_ambientOcclusionEnabled = (enabled);
_ambientOcclusionEnabledSetting.set(enabled);
lightingModelConfig->setAmbientOcclusion(enabled);
}
auto lightingModelConfig = qApp->getRenderEngine()->getConfiguration()->getConfig<MakeLightingModel>("RenderMainView.LightingModel");
if (lightingModelConfig) {
Menu::getInstance()->setIsOptionChecked(MenuOption::AmbientOcclusion, enabled);
lightingModelConfig->setAmbientOcclusion(enabled);
}
});
}
bool RenderScriptingInterface::getAntialiasingEnabled() {
return _antialiasingEnabledSetting.get();
return _antialiasingEnabled;
}
void RenderScriptingInterface::setAntialiasingEnabled(bool enabled) {
if (_antialiasingEnabledSetting.get() == enabled) {
return;
}
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setAntialiasingEnabled", Q_ARG(bool, enabled));
return;
}
auto mainViewJitterCamConfig = qApp->getRenderEngine()->getConfiguration()->getConfig<JitterSample>("RenderMainView.JitterCam");
auto mainViewAntialiasingConfig = qApp->getRenderEngine()->getConfiguration()->getConfig<Antialiasing>("RenderMainView.Antialiasing");
if (mainViewJitterCamConfig && mainViewAntialiasingConfig) {
Menu::getInstance()->setIsOptionChecked(MenuOption::AntiAliasing, enabled);
_antialiasingEnabledSetting.set(enabled);
if (enabled) {
mainViewJitterCamConfig->play();
mainViewAntialiasingConfig->setDebugFXAA(false);
} else {
mainViewJitterCamConfig->none();
mainViewAntialiasingConfig->setDebugFXAA(true);
}
if (_antialiasingEnabled != enabled) {
forceAntialiasingEnabled(enabled);
emit settingsChanged();
}
}
void RenderScriptingInterface::forceAntialiasingEnabled(bool enabled) {
_renderSettingLock.withWriteLock([&] {
_antialiasingEnabled = (enabled);
_antialiasingEnabledSetting.set(enabled);
auto mainViewJitterCamConfig = qApp->getRenderEngine()->getConfiguration()->getConfig<JitterSample>("RenderMainView.JitterCam");
auto mainViewAntialiasingConfig = qApp->getRenderEngine()->getConfiguration()->getConfig<Antialiasing>("RenderMainView.Antialiasing");
if (mainViewJitterCamConfig && mainViewAntialiasingConfig) {
Menu::getInstance()->setIsOptionChecked(MenuOption::AntiAliasing, enabled);
if (enabled) {
mainViewJitterCamConfig->play();
mainViewAntialiasingConfig->setDebugFXAA(false);
}
else {
mainViewJitterCamConfig->none();
mainViewAntialiasingConfig->setDebugFXAA(true);
}
}
});
}

View file

@ -25,10 +25,10 @@
*/
class RenderScriptingInterface : public QObject {
Q_OBJECT
Q_PROPERTY(RenderMethod renderMethod READ getRenderMethod WRITE setRenderMethod)
Q_PROPERTY(bool shadowsEnabled READ getShadowsEnabled WRITE setShadowsEnabled)
Q_PROPERTY(bool ambientOcclusionEnabled READ getAmbientOcclusionEnabled WRITE setAmbientOcclusionEnabled)
Q_PROPERTY(bool antialiasingEnabled READ getAntialiasingEnabled WRITE setAntialiasingEnabled)
Q_PROPERTY(RenderMethod renderMethod READ getRenderMethod WRITE setRenderMethod NOTIFY settingsChanged)
Q_PROPERTY(bool shadowsEnabled READ getShadowsEnabled WRITE setShadowsEnabled NOTIFY settingsChanged)
Q_PROPERTY(bool ambientOcclusionEnabled READ getAmbientOcclusionEnabled WRITE setAmbientOcclusionEnabled NOTIFY settingsChanged)
Q_PROPERTY(bool antialiasingEnabled READ getAntialiasingEnabled WRITE setAntialiasingEnabled NOTIFY settingsChanged)
public:
RenderScriptingInterface();
@ -36,11 +36,18 @@ public:
static RenderScriptingInterface* getInstance();
// RenderMethod enum type
enum RenderMethod {
enum class RenderMethod {
DEFERRED = render::Args::RenderMethod::DEFERRED,
FORWARD = render::Args::RenderMethod::FORWARD,
};
Q_ENUM(RenderMethod);
Q_ENUM(RenderMethod)
static bool isValidRenderMethod(RenderMethod value) { return (value >= RenderMethod::DEFERRED && value <= RenderMethod::FORWARD); }
// Load Settings
// Synchronize the runtime value to the actual setting
// Need to be called on start up to re-initialize the runtime to the saved setting states
void loadSettings();
public slots:
/**jsdoc
@ -132,12 +139,32 @@ public slots:
*/
// void setViewportResolutionScale(float resolutionScale);
signals:
void settingsChanged();
private:
// One lock to serialize and access safely all the settings
mutable ReadWriteLockable _renderSettingLock;
// Runtime value of each settings
int _renderMethod{ RENDER_FORWARD ? render::Args::RenderMethod::FORWARD : render::Args::RenderMethod::DEFERRED };
bool _shadowsEnabled{ true };
bool _ambientOcclusionEnabled{ false };
bool _antialiasingEnabled { true };
// Actual settings saved on disk
Setting::Handle<int> _renderMethodSetting { "renderMethod", RENDER_FORWARD ? render::Args::RenderMethod::FORWARD : render::Args::RenderMethod::DEFERRED };
Setting::Handle<bool> _shadowsEnabledSetting { "shadowsEnabled", true };
Setting::Handle<bool> _ambientOcclusionEnabledSetting { "ambientOcclusionEnabled", false };
Setting::Handle<bool> _antialiasingEnabledSetting { "antialiasingEnabled", true };
Setting::Handle<float> _viewportResolutionScaleSetting{ "viewportResolutionScale", 1.0f };
// Force assign both setting AND runtime value to the parameter value
void forceRenderMethod(RenderMethod renderMethod);
void forceShadowsEnabled(bool enabled);
void forceAmbientOcclusionEnabled(bool enabled);
void forceAntialiasingEnabled(bool enabled);
static std::once_flag registry_flag;
};
#endif // hifi_RenderScriptingInterface_h

View file

@ -150,7 +150,9 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap
if (status == QQuickView::Ready) {
QQuickItem* rootItem = _dockWidget->getRootItem();
_dockWidget->getQuickView()->rootContext()->setContextProperty(EVENT_BRIDGE_PROPERTY, this);
QObject::connect(rootItem, SIGNAL(sendToScript(QVariant)), this, SLOT(qmlToScript(const QVariant&)), Qt::QueuedConnection);
QObject::connect(rootItem, SIGNAL(sendToScript(QVariant)), this, SLOT(qmlToScript(const QVariant&)),
Qt::QueuedConnection);
emit mainWindow->windowGeometryChanged(qApp->getWindow()->geometry());
}
});
_dockWidget->setSource(QUrl(sourceUrl));

View file

@ -121,7 +121,7 @@ void setupPreferences() {
auto setter = [](bool value) { qApp->setSettingConstrainToolbarPosition(value); };
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Constrain Toolbar Position to Horizontal Center", getter, setter));
}
{
auto getter = []()->bool { return qApp->getAwayStateWhenFocusLostInVREnabled(); };
auto setter = [](bool value) { qApp->setAwayStateWhenFocusLostInVREnabled(value); };
@ -241,7 +241,7 @@ void setupPreferences() {
"installation and system details, and crash events. By allowing High Fidelity to collect "
"this information you are helping to improve the product. ", getter, setter));
}
static const QString AVATAR_TUNING { "Avatar Tuning" };
{
auto getter = [myAvatar]()->QString { return myAvatar->getDominantHand(); };
@ -257,8 +257,8 @@ void setupPreferences() {
preference->setStep(0.05f);
preference->setDecimals(2);
preferences->addPreference(preference);
// When the Interface is first loaded, this section setupPreferences(); is loaded -
// When the Interface is first loaded, this section setupPreferences(); is loaded -
// causing the myAvatar->getDomainMinScale() and myAvatar->getDomainMaxScale() to get set to incorrect values
// which can't be changed across domain switches. Having these values loaded up when you load the Dialog each time
// is a way around this, therefore they're not specified here but in the QML.
@ -281,10 +281,14 @@ void setupPreferences() {
static const QString FACE_TRACKING{ "Face Tracking" };
{
#ifdef HAVE_DDE
auto getter = []()->float { return DependencyManager::get<DdeFaceTracker>()->getEyeClosingThreshold(); };
auto setter = [](float value) { DependencyManager::get<DdeFaceTracker>()->setEyeClosingThreshold(value); };
preferences->addPreference(new SliderPreference(FACE_TRACKING, "Eye Closing Threshold", getter, setter));
#endif
}
{
auto getter = []()->float { return FaceTracker::getEyeDeflection(); };
auto setter = [](float value) { FaceTracker::setEyeDeflection(value); };
@ -317,14 +321,14 @@ void setupPreferences() {
preferences->addPreference(preference);
}
{
auto getter = [myAvatar]() -> bool { return myAvatar->hoverWhenUnsupported(); };
auto getter = [myAvatar]()->bool { return myAvatar->hoverWhenUnsupported(); };
auto setter = [myAvatar](bool value) { myAvatar->setHoverWhenUnsupported(value); };
auto preference = new CheckPreference(VR_MOVEMENT, "Hover When Unsupported", getter, setter);
preferences->addPreference(preference);
}
{
auto getter = [myAvatar]()->int { return myAvatar->getMovementReference(); };
auto setter = [myAvatar](int value) { myAvatar->setMovementReference(value); };
auto setter = [myAvatar](int value) { myAvatar->setMovementReference(value); };
//auto preference = new CheckPreference(VR_MOVEMENT, "Hand-Relative Movement", getter, setter);
auto preference = new RadioButtonsPreference(VR_MOVEMENT, "Movement Direction", getter, setter);
QStringList items;
@ -530,6 +534,5 @@ void setupPreferences() {
preference->setStep(10);
preferences->addPreference(preference);
}
}
}

View file

@ -77,7 +77,7 @@ public:
* @hifi-avatar
*
* @property {Uuid} keyboardFocusOverlay - Get or set the {@link Entities.EntityTypes|Web} entity that has keyboard focus.
* If no entity has keyboard focus, get returns <code>null</code>; set to <code>null</code> or {@link Uuid|Uuid.NULL} to
* If no entity has keyboard focus, get returns <code>null</code>; set to <code>null</code> or {@link Uuid(0)|Uuid.NULL} to
* clear keyboard focus.
*/
@ -122,7 +122,7 @@ public slots:
* @function Overlays.addOverlay
* @param {Overlays.OverlayType} type - The type of the overlay to add.
* @param {Overlays.OverlayProperties} properties - The properties of the overlay to add.
* @returns {Uuid} The ID of the newly created overlay if successful, otherwise {@link Uuid|Uuid.NULL}.
* @returns {Uuid} The ID of the newly created overlay if successful, otherwise {@link Uuid(0)|Uuid.NULL}.
* @example <caption>Add a cube overlay in front of your avatar.</caption>
* var overlay = Overlays.addOverlay("cube", {
* position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })),
@ -137,7 +137,7 @@ public slots:
* Create a clone of an existing entity (or 2D overlay).
* @function Overlays.cloneOverlay
* @param {Uuid} id - The ID of the entity/2D overlay to clone.
* @returns {Uuid} The ID of the new object if successful, otherwise {@link Uuid|Uuid.NULL}.
* @returns {Uuid} The ID of the new object if successful, otherwise {@link Uuid(0)|Uuid.NULL}.
*/
QUuid cloneOverlay(const QUuid& id);
@ -562,7 +562,7 @@ public slots:
* Set the Web3D entity that has keyboard focus.
* @function Overlays.setKeyboardFocusOverlay
* @param {Uuid} id - The ID of the {@link Entities.EntityTypes|Web} entity to set keyboard focus to. Use
* <code>null</code> or {@link Uuid|Uuid.NULL} to unset keyboard focus from an overlay.
* <code>null</code> or {@link Uuid(0)|Uuid.NULL} to unset keyboard focus from an overlay.
*/
void setKeyboardFocusOverlay(const QUuid& id) { DependencyManager::get<EntityScriptingInterface>()->setKeyboardFocusEntity(id); }

View file

@ -0,0 +1,114 @@
cmake_minimum_required(VERSION 3.0)
project(HQLauncher)
set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/")
set(src_files
src/Launcher.h
src/Launcher.m
src/SplashScreen.h
src/SplashScreen.m
src/LoginScreen.h
src/LoginScreen.m
src/DisplayNameScreen.h
src/DisplayNameScreen.m
src/ProcessScreen.h
src/ProcessScreen.m
src/Window.h
src/Window.m
src/DownloadInterface.h
src/DownloadInterface.m
src/DownloadDomainContent.h
src/DownloadDomainContent.m
src/DownloadScripts.h
src/DownloadScripts.m
src/CredentialsRequest.h
src/CredentialsRequest.m
src/LatestBuildRequest.h
src/LatestBuildRequest.m
src/OrganizationRequest.m
src/OrganizationRequest.h
src/ErrorViewController.h
src/ErrorViewController.m
src/Settings.h
src/Settings.m
src/LaunchInterface.h
src/CustomUI.h
src/CustomUI.m
src/main.mm
nib/Window.xib
nib/SplashScreen.xib
nib/ErrorScreen.xib
nib/LoginScreen.xib
nib/ProcessScreen.xib
nib/DisplayNameScreen.xib)
set(APP_NAME "HQ Launcher")
set(CMAKE_C_FLAGS "-x objective-c")
set(CMAKE_CXX_FLAGS "-x objective-c++")
set(CMAKE_EXE_LINKER_FLAGS "-framework Cocoa -framework AppKit -framework QuartzCore")
set_target_properties(${this_target} PROPERTIES
MACOSX_BUNDLE_INFO_PLIST MacOSXBundleInfo.plist.in)
set(MACOSX_BUNDLE_ICON_FILE "interface.icns")
function(set_from_env _RESULT_NAME _ENV_VAR_NAME _DEFAULT_VALUE)
if (NOT DEFINED ${_RESULT_NAME})
if ("$ENV{${_ENV_VAR_NAME}}" STREQUAL "")
set (${_RESULT_NAME} ${_DEFAULT_VALUE} PARENT_SCOPE)
else()
set (${_RESULT_NAME} $ENV{${_ENV_VAR_NAME}} PARENT_SCOPE)
endif()
endif()
endfunction()
add_executable(${PROJECT_NAME} MACOSX_BUNDLE ${src_files})
set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME ${APP_NAME})
set_from_env(LAUNCHER_HMAC_SECRET LAUNCHER_HMAC_SECRET "")
target_compile_definitions(${PROJECT_NAME} PRIVATE LAUNCHER_HMAC_SECRET="${LAUNCHER_HMAC_SECRET}")
file(GLOB NIB_FILES "nib/*.xib")
find_program(IBTOOL ibtool HINTS "/usr/bin" "${OSX_DEVELOPER_ROOT}/usr/bin")
foreach (nibFile ${NIB_FILES})
get_filename_component(fileWithExtension ${nibFile} NAME)
string(REGEX REPLACE "\\.[^.]*$" "" file ${fileWithExtension})
add_custom_command (TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${IBTOOL} --errors --warnings --notices
--output-format human-readable-text
--compile
"${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${APP_NAME}.app/Contents/Resources/${file}.nib"
${CMAKE_CURRENT_SOURCE_DIR}/nib/${file}.xib)
endforeach()
add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/images "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${APP_NAME}.app/Contents/Resources/")
install(
TARGETS HQLauncher
BUNDLE DESTINATION "."
COMPONENT applications
)
set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR})
include(CPackComponent)
set(CPACK_PACKAGE_NAME "HQ Launcher")
set(CPACK_PACKAGE_VENDOR "High Fidelity")
set(CPACK_PACKAGE_VERSION ${BUILD_VERSION})
set(CPACK_PACKAGE_FILE_NAME "HQ Launcher")
set(CPACK_NSIS_DISPLAY_NAME ${_DISPLAY_NAME})
set(DMG_SUBFOLDER_NAME "High Fidelity")
set(ESCAPED_DMG_SUBFOLDER_NAME "")
set(DMG_SUBFOLDER_ICON "${CMAKE_SOURCE_DIR}/cmake/installer/install-folder.rsrc")
set(CPACK_GENERATOR "DragNDrop")
include(CPack)

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

After

(image error) Size: 100 KiB

Binary file not shown.

After

(image error) Size: 299 KiB

Binary file not shown.

After

(image error) Size: 100 KiB

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
<key>CFBundleIdentifier</key>
<string>com.highfidelity.launcher</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>NSMainNibFile</key>
<string>Window</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>CFBundleDisplayName</key>
<string>HQ Launcher</string>
</dict>
</plist>

Binary file not shown.

After

(image error) Size: 5 KiB

Binary file not shown.

After

(image error) Size: 3.3 KiB

Binary file not shown.

After

(image error) Size: 2.9 KiB

Binary file not shown.

After

(image error) Size: 4.5 KiB

View file

@ -0,0 +1,14 @@
<svg width="100" height="18" viewBox="0 0 100 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.27077 0.0930176V6.06095H2.98187V0.0930176H0V13.8509H2.98187V8.41861H8.27077V13.8509H11.2526V0.0930176H8.27077Z" fill="white"/>
<path d="M12.982 4.21763H15.6348V13.8507H12.982V4.21763ZM12.7753 1.45509C12.7753 0.628432 13.4374 0 14.2852 0C15.1498 0 15.8162 0.628432 15.8162 1.45509C15.8162 2.28174 15.154 2.91018 14.2852 2.91018C13.4417 2.91018 12.7753 2.28596 12.7753 1.45509Z" fill="white"/>
<path d="M23.8844 8.78563V8.65488C23.8844 7.01422 23.0198 6.01886 21.5647 6.01886C20.0716 6.01886 19.2408 7.08591 19.2408 8.67174V8.81937C19.2408 10.4221 20.1813 11.4174 21.5056 11.4174C22.9059 11.4216 23.8844 10.4263 23.8844 8.78563ZM16.5879 14.0534H19.2408C19.4052 14.9012 20.0168 15.4537 21.3791 15.4537C22.9987 15.4537 23.8085 14.6059 23.8085 13.096V11.641C23.2728 12.5815 22.134 13.4292 20.7127 13.4292C18.3719 13.4292 16.512 11.6789 16.512 8.82357V8.69705C16.512 5.9345 18.3551 3.99863 20.7507 3.99863C22.3154 3.99863 23.2559 4.67766 23.8085 5.65615V4.21793H26.4613V13.096C26.4445 15.9514 24.5465 17.4065 21.3791 17.4065C18.2665 17.4065 16.8494 16.0442 16.5879 14.0534Z" fill="white"/>
<path d="M27.8997 0.092804H30.5694V5.74441C31.046 4.78701 32.1004 3.99411 33.7031 3.99411C35.601 3.99411 36.9423 5.13709 36.9423 7.71406V13.8465H34.2725V8.09786C34.2725 6.79039 33.7579 6.16618 32.577 6.16618C31.434 6.16618 30.5694 6.86631 30.5694 8.28343V13.8465H27.8997V0.092804V0.092804Z" fill="white"/>
<path d="M41.8979 0.0930176H50.3501V2.4338H44.8798V6.50379H49.8693V8.74758H44.8798V13.8509H41.8979V0.0930176Z" fill="white"/>
<path d="M51.5437 4.21763H54.1965V13.8507H51.5437V4.21763ZM51.3412 1.45509C51.3412 0.628432 52.0034 0 52.8511 0C53.7157 0 54.3821 0.628432 54.3821 1.45509C54.3821 2.28174 53.7199 2.91018 52.8511 2.91018C52.0034 2.91018 51.3412 2.28596 51.3412 1.45509Z" fill="white"/>
<path d="M62.4757 9.08057V8.93294C62.4757 7.01814 61.628 6.03965 60.1729 6.03965C58.6798 6.03965 57.8532 7.05188 57.8532 8.96669V9.11429C57.8532 11.0291 58.7726 11.9696 60.1012 11.9696C61.4972 11.9739 62.4757 11.0333 62.4757 9.08057ZM55.1244 9.17335V9.02574C55.1244 5.8583 56.9464 3.99833 59.3589 3.99833C60.9068 3.99833 61.8642 4.67737 62.3998 5.65586V0.092804H65.0527V13.8507H62.3998V12.248C61.9021 13.2223 60.7254 14.0531 59.304 14.0531C56.9675 14.0531 55.1244 12.3787 55.1244 9.17335Z" fill="white"/>
<path d="M73.0239 7.99693C72.9311 6.54185 72.1972 5.84173 70.9235 5.84173C69.7257 5.84173 68.9159 6.63464 68.7135 7.99693H73.0239ZM65.9678 9.15678V9.00915C65.9678 5.96824 68.123 3.99863 70.9235 3.99863C73.4077 3.99863 75.6219 5.4537 75.6219 8.89529V9.63337H68.6797C68.7556 11.2361 69.6203 12.1555 71.0753 12.1555C72.3111 12.1555 72.9185 11.6199 73.0829 10.8101H75.6051C75.293 12.8936 73.6354 14.0534 70.9994 14.0534C68.085 14.0534 65.9678 12.2314 65.9678 9.15678Z" fill="white"/>
<path d="M79.2661 0.0930176H76.6133V13.8509H79.2661V0.0930176Z" fill="white"/>
<path d="M80.7464 4.21763H83.3993V13.8507H80.7464V4.21763ZM80.5439 1.45509C80.5439 0.628432 81.2061 0 82.0539 0C82.9185 0 83.5849 0.628432 83.5849 1.45509C83.5849 2.28174 82.9227 2.91018 82.0539 2.91018C81.2104 2.91018 80.5439 2.28596 80.5439 1.45509Z" fill="white"/>
<path d="M84.8165 10.9618V2.10059H87.4694V4.17987H89.4939V6.07781H87.4694V10.7172C87.4694 11.4932 87.8575 11.8602 88.5365 11.8602C88.9414 11.8602 89.2366 11.8053 89.5487 11.6746V13.737C89.1818 13.8678 88.6461 13.9943 87.9291 13.9943C85.9047 13.9985 84.8165 12.9652 84.8165 10.9618Z" fill="white"/>
<path d="M97.4398 4.17957L95.2846 9.96614L92.9481 4.17957H90.0717L93.9055 12.8383L92.21 17.0939H94.7869L99.9999 4.17957H97.4398Z" fill="white"/>
</svg>

After

(image error) Size: 3.6 KiB

Binary file not shown.

After

(image error) Size: 16 KiB

Binary file not shown.

After

(image error) Size: 4.6 KiB

Binary file not shown.

After

(image error) Size: 7.6 KiB

Binary file not shown.

View file

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="DisplayNameScreen">
<connections>
<outlet property="view" destination="c22-O7-iKe" id="nCZ-J3-wG5"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="c22-O7-iKe">
<rect key="frame" x="0.0" y="0.0" width="515" height="390"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="aus-lo-eVi">
<rect key="frame" x="0.0" y="0.0" width="515" height="390"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="MYh-TA-w2A"/>
</imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="iAf-Ee-jJL">
<rect key="frame" x="95" y="344" width="325" height="33"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Choose a display name" id="GzZ-0d-2JH">
<font key="font" metaFont="systemBold" size="28"/>
<color key="textColor" name="selectedMenuItemTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="7dU-5o-Huh">
<rect key="frame" x="45" y="314" width="425" height="22"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="This is the name that your teammates will see." id="wNS-nK-rVp">
<font key="font" metaFont="system" size="14"/>
<color key="textColor" red="0.76862745098039209" green="0.76862745098039209" blue="0.76862745098039209" alpha="0.84705882352941175" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Vhg-rq-xUH" customClass="TextField">
<rect key="frame" x="104" y="187" width="306" height="26"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" focusRingType="none" baseWritingDirection="leftToRight" placeholderString="Display name" id="4ET-nT-5pD">
<font key="font" metaFont="system" size="18"/>
<color key="textColor" red="0.99999600649999998" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" name="systemGrayColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<box appearanceType="darkAqua" verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="wHR-wz-Fbe">
<rect key="frame" x="104" y="186" width="306" height="5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</box>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="j8K-TD-h7e">
<rect key="frame" x="381" y="0.0" width="134" height="46"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="0XM-bX-guC"/>
</imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ZRz-hZ-qj0" customClass="Hyperlink">
<rect key="frame" x="182" y="8" width="150" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Having trouble?" id="j4G-UG-IvP">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="linkColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="linkColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<action selector="hyperLink:" target="YVh-OH-vU8" id="JfS-ot-wAP"/>
</connections>
</textField>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="xeX-qc-ccB" customClass="HFButton">
<rect key="frame" x="205" y="84" width="104" height="42"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="square" title="LOG IN" bezelStyle="shadowlessSquare" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="6Ba-S9-5qW">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="systemBold" size="18"/>
</buttonCell>
<color key="contentTintColor" name="alternateSelectedControlTextColor" catalog="System" colorSpace="catalog"/>
<connections>
<action selector="login:" target="YVh-OH-vU8" id="uzF-yC-tu1"/>
</connections>
</button>
</subviews>
<point key="canvasLocation" x="138.5" y="154"/>
</customView>
<customObject id="YVh-OH-vU8" customClass="DisplayNameScreen">
<connections>
<outlet property="backgroundImage" destination="aus-lo-eVi" id="SRc-pV-lXG"/>
<outlet property="displayName" destination="Vhg-rq-xUH" id="Fb5-im-2hx"/>
<outlet property="smallLogo" destination="j8K-TD-h7e" id="OVd-p3-nu6"/>
</connections>
</customObject>
</objects>
</document>

View file

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="ErrorViewController">
<connections>
<outlet property="view" destination="c22-O7-iKe" id="gAZ-dU-ax1"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="c22-O7-iKe">
<rect key="frame" x="0.0" y="0.0" width="515" height="390"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="eih-a8-Pqa">
<rect key="frame" x="0.0" y="0.0" width="515" height="390"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="SuI-2T-YhQ"/>
</imageView>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="jmW-5q-Mf3">
<rect key="frame" x="181" y="195" width="152" height="122"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="CG4-WN-GsV"/>
</imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="rv7-io-Gy8">
<rect key="frame" x="209" y="122" width="93" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Uh oh." id="juz-EO-k3v">
<font key="font" metaFont="systemBold" size="28"/>
<color key="textColor" name="selectedTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="FkC-xi-3UI">
<rect key="frame" x="156" y="105" width="192" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="We seem to have a problem." id="bX3-v1-LLM">
<font key="font" metaFont="system" size="15"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tV0-Su-eLX">
<rect key="frame" x="156" y="80" width="192" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Please restart Launcher" id="aBw-o2-xZE">
<font key="font" metaFont="system" size="15"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Sds-MZ-qhT" customClass="HFButton">
<rect key="frame" x="200" y="21" width="110" height="35"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="squareTextured" title="Restart" bezelStyle="texturedSquare" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="x7d-uv-o8h">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="resartLauncher:" target="nWn-x7-LxT" id="IDF-qy-N8P"/>
</connections>
</button>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ugn-Hk-gWL">
<rect key="frame" x="381" y="0.0" width="134" height="46"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="CBD-Vk-Xd4"/>
</imageView>
</subviews>
<point key="canvasLocation" x="138.5" y="152"/>
</customView>
<customObject id="nWn-x7-LxT" customClass="ErrorViewController">
<connections>
<outlet property="backgroundImage" destination="eih-a8-Pqa" id="2xh-8r-1Qu"/>
<outlet property="smallLogo" destination="ugn-Hk-gWL" id="EVI-d3-mf5"/>
<outlet property="voxelImage" destination="jmW-5q-Mf3" id="NiI-cY-tAf"/>
</connections>
</customObject>
</objects>
</document>

View file

@ -0,0 +1,139 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="LoginScreen">
<connections>
<outlet property="view" destination="c22-O7-iKe" id="QBq-uY-czz"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView wantsLayer="YES" id="c22-O7-iKe">
<rect key="frame" x="0.0" y="0.0" width="515" height="390"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<imageView focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="L56-Jv-0N8">
<rect key="frame" x="0.0" y="0.0" width="515" height="390"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" focusRingType="none" alignment="left" imageScaling="proportionallyDown" id="ONf-d4-EDr"/>
<color key="contentTintColor" name="textColor" catalog="System" colorSpace="catalog"/>
</imageView>
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="L7W-3n-OKy" customClass="TextField">
<rect key="frame" x="79" y="254" width="353" height="22"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" focusRingType="none" baseWritingDirection="leftToRight" alignment="left" placeholderString="Organization name" id="Uiu-RX-Rp5">
<font key="font" metaFont="system" size="18"/>
<color key="textColor" red="0.99999600649999998" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" name="systemGrayColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<box focusRingType="none" appearanceType="darkAqua" verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="P9T-5L-LJf">
<rect key="frame" x="81" y="253" width="353" height="5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</box>
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="b9S-wR-tuB" customClass="TextField">
<rect key="frame" x="79" y="201" width="357" height="22"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" focusRingType="none" baseWritingDirection="leftToRight" alignment="left" placeholderString="Username" id="wju-XU-YvL">
<font key="font" metaFont="system" size="18"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" name="systemGrayColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<box appearanceType="darkAqua" verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="bW2-hT-DSz">
<rect key="frame" x="79" y="200" width="353" height="5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</box>
<secureTextField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="T2i-im-3ID" customClass="SecureTextField">
<rect key="frame" x="79" y="145" width="357" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<secureTextFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" focusRingType="none" baseWritingDirection="leftToRight" alignment="left" placeholderString="Password" usesSingleLineMode="YES" id="JRs-bw-waZ">
<font key="font" metaFont="system" size="18"/>
<color key="textColor" red="0.99999600649999998" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" name="systemGrayColor" catalog="System" colorSpace="catalog"/>
<allowedInputSourceLocales>
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
</allowedInputSourceLocales>
</secureTextFieldCell>
</secureTextField>
<box appearanceType="darkAqua" verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="4y1-Ud-HrR">
<rect key="frame" x="82" y="144" width="353" height="5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</box>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="TRA-Dd-qej">
<rect key="frame" x="38" y="307" width="425" height="22"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Be sure you've uploaded your Avatar before signing in." id="3q0-ys-GTR">
<font key="font" metaFont="system" size="14"/>
<color key="textColor" red="0.76862745098039209" green="0.76862745098039209" blue="0.76862745098039209" alpha="0.84705882352941175" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" name="alternateSelectedControlTextColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button focusRingType="exterior" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="jKE-fV-Tjv" customClass="HFButton">
<rect key="frame" x="197" y="57" width="110" height="35"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="square" title="NEXT" bezelStyle="shadowlessSquare" alignment="center" state="on" borderStyle="border" focusRingType="exterior" imageScaling="proportionallyUpOrDown" inset="2" id="RZM-vz-5eS">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="systemBold" size="18"/>
</buttonCell>
<color key="contentTintColor" name="alternateSelectedControlTextColor" catalog="System" colorSpace="catalog"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="CornerRadius">
<real key="value" value="1"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="color" keyPath="TextColorHover">
<color key="value" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
<connections>
<action selector="goToLogin:" target="NkF-nk-81S" id="e50-vF-R8Y"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="hIC-qf-Abj">
<rect key="frame" x="63" y="337" width="384" height="33"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Please log in" id="YuE-2K-HLT">
<font key="font" metaFont="systemBold" size="28"/>
<color key="textColor" name="alternateSelectedControlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="alternateSelectedControlTextColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="A59-kY-Mq3">
<rect key="frame" x="381" y="0.0" width="134" height="46"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="kZV-oM-dmS"/>
</imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qCS-xH-551" customClass="Hyperlink">
<rect key="frame" x="188" y="20" width="140" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Having trouble?" id="HA3-gC-CiL">
<font key="font" metaFont="system"/>
<color key="textColor" name="linkColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="linkColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<action selector="havingTrouble:" target="NkF-nk-81S" id="tsf-tC-aqq"/>
</connections>
</textField>
</subviews>
<point key="canvasLocation" x="138.5" y="154"/>
</customView>
<customObject id="NkF-nk-81S" customClass="LoginScreen">
<connections>
<outlet property="backgroundImage" destination="L56-Jv-0N8" id="INT-rB-YtG"/>
<outlet property="button" destination="jKE-fV-Tjv" id="or6-tG-r6R"/>
<outlet property="header" destination="hIC-qf-Abj" id="sVQ-rl-cvR"/>
<outlet property="orginization" destination="L7W-3n-OKy" id="TiL-wn-Z2b"/>
<outlet property="password" destination="T2i-im-3ID" id="uV1-RG-gfy"/>
<outlet property="smallHeader" destination="TRA-Dd-qej" id="9s6-gK-XCw"/>
<outlet property="smallLogo" destination="A59-kY-Mq3" id="XgD-NH-16J"/>
<outlet property="trouble" destination="qCS-xH-551" id="bQr-eg-dqc"/>
<outlet property="username" destination="b9S-wR-tuB" id="91a-SY-AL4"/>
</connections>
</customObject>
</objects>
</document>

View file

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="ProcessScreen">
<connections>
<outlet property="view" destination="c22-O7-iKe" id="Kzc-tu-LGt"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="c22-O7-iKe">
<rect key="frame" x="0.0" y="0.0" width="515" height="390"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kuY-e2-Hqb">
<rect key="frame" x="0.0" y="0.0" width="515" height="390"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="Abr-HV-cKq"/>
</imageView>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Nrc-t3-PSh">
<rect key="frame" x="192" y="190" width="131" height="112"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="LNv-HQ-3gb"/>
</imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="EMF-E4-qLL">
<rect key="frame" x="30" y="109" width="454" height="41"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Setup will take a moment" id="y9y-tH-HjJ">
<font key="font" metaFont="systemBold" size="28"/>
<color key="textColor" name="alternateSelectedControlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="BSg-lp-njL">
<rect key="frame" x="33" y="84" width="448" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="We're getting your headquaters ready" id="HeV-5p-9FI">
<font key="font" metaFont="system"/>
<color key="textColor" name="alternateSelectedControlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uh2-4K-n56">
<rect key="frame" x="381" y="0.0" width="134" height="46"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="6NP-7q-Mhj"/>
</imageView>
</subviews>
<point key="canvasLocation" x="138.5" y="154"/>
</customView>
<customObject id="utv-Iz-V5C" customClass="ProcessScreen">
<connections>
<outlet property="background" destination="kuY-e2-Hqb" id="CBc-bD-ux7"/>
<outlet property="boldStatus" destination="EMF-E4-qLL" id="udm-8B-7lt"/>
<outlet property="smallLogo" destination="uh2-4K-n56" id="pYg-hP-nr5"/>
<outlet property="smallStatus" destination="BSg-lp-njL" id="ziz-ek-Lq4"/>
<outlet property="voxelImage" destination="Nrc-t3-PSh" id="J81-ex-qbE"/>
</connections>
</customObject>
</objects>
</document>

View file

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="SplashScreen">
<connections>
<outlet property="view" destination="c22-O7-iKe" id="dKB-Pi-vXA"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="c22-O7-iKe">
<rect key="frame" x="0.0" y="0.0" width="515" height="390"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qtD-mb-qqq">
<rect key="frame" x="0.0" y="0.0" width="515" height="390"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="ixM-5m-vi6"/>
</imageView>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2i5-Zw-nH7">
<rect key="frame" x="145" y="72" width="225" height="225"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="qC6-tI-Uwf"/>
</imageView>
</subviews>
<point key="canvasLocation" x="119.5" y="134"/>
</customView>
<customObject id="iJ0-FI-XIf" customClass="SplashScreen">
<connections>
<outlet property="imageView" destination="qtD-mb-qqq" id="rCt-Gd-Uux"/>
<outlet property="logoImage" destination="2i5-Zw-nH7" id="7tM-sX-cvR"/>
</connections>
</customObject>
</objects>
</document>

View file

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner"/>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="Window">
<windowStyleMask key="styleMask" closable="YES"/>
<rect key="contentRect" x="505" y="583" width="515" height="390"/>
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1177"/>
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="515" height="390"/>
<autoresizingMask key="autoresizingMask"/>
</view>
<connections>
<outlet property="delegate" destination="PE9-qZ-I60" id="YL3-KK-gRc"/>
</connections>
<point key="canvasLocation" x="456.5" y="-138"/>
</window>
<customObject id="PE9-qZ-I60" userLabel="Launcher" customClass="Launcher">
<connections>
<outlet property="window" destination="QvC-M9-y7g" id="BJP-3M-XAB"/>
</connections>
</customObject>
</objects>
</document>

View file

@ -0,0 +1,10 @@
#import <Cocoa/Cocoa.h>
@interface CredentialsRequest : NSObject <NSURLConnectionDelegate> {
}
@property (nonatomic, retain) NSMutableData* webData;
@property (nonatomic, retain) NSString* jsonString;
- (void) confirmCredentials:(NSString*)username :(NSString*)password;
@end

View file

@ -0,0 +1,96 @@
#import "CredentialsRequest.h"
#import "Launcher.h"
#import "Settings.h"
@implementation CredentialsRequest
- (void) confirmCredentials:(NSString*)username :(NSString*)password {
NSLog(@"web request started");
NSString *post = [NSString stringWithFormat:@"grant_type=password&username=%@&password=%@&scope=owner",
[username stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet alphanumericCharacterSet]],
[password stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet alphanumericCharacterSet]]];
NSData *postData = [post dataUsingEncoding:NSUTF8StringEncoding];
NSString *postLength = [NSString stringWithFormat:@"%ld", (unsigned long)[postData length]];
NSMutableURLRequest *request = [NSMutableURLRequest new];
[request setURL:[NSURL URLWithString:@"https://metaverse.highfidelity.com/oauth/token"]];
[request setHTTPMethod:@"POST"];
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:postData];
NSURLSession* session = [NSURLSession sharedSession];
NSURLSessionDataTask* dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSLog(@"credentials request finished");
NSMutableData* webData = [NSMutableData data];
[webData appendData:data];
NSString* jsonString = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[data length] encoding:NSUTF8StringEncoding];
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
id json = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];
Launcher* sharedLauncher = [Launcher sharedLauncher];
if (json[@"error"] != nil) {
dispatch_async(dispatch_get_main_queue(), ^{
[[Settings sharedSettings] login:FALSE];
[sharedLauncher setLoginErrorState: CREDENTIALS];
[sharedLauncher credentialsAccepted:FALSE];
});
} else {
dispatch_async(dispatch_get_main_queue(), ^{
[[Settings sharedSettings] login:TRUE];
[sharedLauncher setTokenString:jsonString];
[sharedLauncher credentialsAccepted:TRUE];
});
}
NSLog(@"credentials: connectionDidFinished completed");
}];
[dataTask resume];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.webData appendData:data];
NSLog(@"credentials connection received data");
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(@"credentials connection received response");
NSHTTPURLResponse *ne = (NSHTTPURLResponse *)response;
if([ne statusCode] == 200) {
NSLog(@"connection state is 200 - all okay");
} else {
NSLog(@"connection state is NOT 200");
[[Launcher sharedLauncher] displayErrorPage];
}
}
-(void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(@"Conn Err: %@", [error localizedDescription]);
[[Launcher sharedLauncher] displayErrorPage];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(@"credentials request finished");
NSString* jsonString = [[NSString alloc] initWithBytes: [self.webData mutableBytes] length:[self.webData length] encoding:NSUTF8StringEncoding];
NSData *data = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
Launcher* sharedLauncher = [Launcher sharedLauncher];
if (json[@"error"] != nil) {
[[Settings sharedSettings] login:FALSE];
[sharedLauncher setLoginErrorState: CREDENTIALS];
[sharedLauncher credentialsAccepted:FALSE];
} else {
[[Settings sharedSettings] login:TRUE];
[sharedLauncher setTokenString:jsonString];
[sharedLauncher credentialsAccepted:TRUE];
}
NSLog(@"credentials: connectionDidFinished completed");
}
@end

View file

@ -0,0 +1,30 @@
#import <Cocoa/Cocoa.h>
#import <Quartz/Quartz.h>
#import <CoreGraphics/CoreGraphics.h>
extern NSString* hifiSmallLogoFilename;
extern NSString* hifiLargeLogoFilename;
extern NSString* hifiVoxelFilename;
extern NSString* hifiBackgroundFilename;
@interface TextField : NSTextField {
}
@end
@interface SecureTextField : NSSecureTextField {
}
@end
@interface HFButton : NSButton
@property (nonatomic, strong) CATextLayer *titleLayer;
@end
@interface Hyperlink : NSTextField {
}
@end

View file

@ -0,0 +1,118 @@
#import "CustomUI.h"
#import <QuartzCore/QuartzCore.h>
NSString* hifiSmallLogoFilename = @"hifi_logo_small";
NSString* hifiLargeLogoFilename = @"hifi_logo_large";
NSString* hifiVoxelFilename = @"HiFi_Voxel";
NSString* hifiBackgroundFilename = @"hifi_window";
@implementation TextField
- (void) textDidBeginEditing:(NSNotification *)notification
{
NSColor *insertionPointColor = [NSColor whiteColor];
NSTextView *fieldEditor = (NSTextView*)[self.window fieldEditor:YES
forObject:self];
fieldEditor.insertionPointColor = insertionPointColor;
}
- (void) mouseDown:(NSEvent *)event
{
NSColor *insertionPointColor = [NSColor whiteColor];
NSTextView *fieldEditor = (NSTextView*)[self.window fieldEditor:YES
forObject:self];
fieldEditor.insertionPointColor = insertionPointColor;
}
-(BOOL)becomeFirstResponder
{
BOOL status = [super becomeFirstResponder];
NSColor *insertionPointColor = [NSColor whiteColor];
NSTextView *fieldEditor = (NSTextView*)[self.window fieldEditor:YES
forObject:self];
fieldEditor.insertionPointColor = insertionPointColor;
return status;
}
@end
@implementation SecureTextField
- (void) textDidBeginEditing:(NSNotification *)notification
{
NSColor *insertionPointColor = [NSColor whiteColor];
NSTextView *fieldEditor = (NSTextView*)[self.window fieldEditor:YES
forObject:self];
fieldEditor.insertionPointColor = insertionPointColor;
}
- (void) mouseDown:(NSEvent *)event
{
NSColor *insertionPointColor = [NSColor whiteColor];
NSTextView *fieldEditor = (NSTextView*)[self.window fieldEditor:YES
forObject:self];
fieldEditor.insertionPointColor = insertionPointColor;
}
-(BOOL)becomeFirstResponder
{
BOOL status = [super becomeFirstResponder];
NSColor *insertionPointColor = [NSColor whiteColor];
NSTextView *fieldEditor = (NSTextView*)[self.window fieldEditor:YES
forObject:self];
fieldEditor.insertionPointColor = insertionPointColor;
return status;
}
@end
@implementation HFButton
- (void)drawRect:(NSRect)dirtyRect {
// The UI layers implemented in awakeFromNib will handle drawing
}
- (BOOL)layer:(CALayer *)layer shouldInheritContentsScale:(CGFloat)newScale fromWindow:(NSWindow *)window {
return YES;
}
- (void)awakeFromNib {
[super awakeFromNib];
self.wantsLayer = YES;
self.layer.backgroundColor = [NSColor blackColor].CGColor;
self.layer.borderColor = [NSColor whiteColor].CGColor;
self.layer.borderWidth = 2.0f;
self.layer.masksToBounds = YES;
_titleLayer = [[CATextLayer alloc] init];
CGSize buttonSize = self.frame.size;
CGSize titleSize = [self.title sizeWithAttributes:@{NSFontAttributeName: self.font}];
CGFloat x = (buttonSize.width - titleSize.width) / 2.0; // Title's origin x
CGFloat y = (buttonSize.height - titleSize.height) / 2.0; // Title's origin y
self.titleLayer.frame = NSMakeRect(round(x), round(y), ceil(titleSize.width), ceil(titleSize.height));
self.titleLayer.string = self.title;
self.titleLayer.foregroundColor = [NSColor whiteColor].CGColor;
// TODO(huffman) Fix this to be dynamic based on screen?
self.titleLayer.contentsScale = 2.0;
self.titleLayer.font = (__bridge CFTypeRef _Nullable)(self.font);
self.titleLayer.fontSize = self.font.pointSize;
//self.titleLayer.allowsEdgeAntialiasing = YES;
//self.titleLayer.allowsFontSubpixelQuantization = YES;
[self.layer addSublayer:self.titleLayer];
}
@end
@implementation Hyperlink
- (void) mouseDown:(NSEvent *)event
{
[self sendAction:[self action] to:[self target]];
}
@end

View file

@ -0,0 +1,5 @@
#import <Cocoa/Cocoa.h>
@interface DisplayNameScreen : NSViewController {
}
@end

View file

@ -0,0 +1,34 @@
#import "DisplayNameScreen.h"
#import "Launcher.h"
#import "CustomUI.h"
@interface DisplayNameScreen ()
@property (nonatomic, assign) IBOutlet NSImageView* backgroundImage;
@property (nonatomic, assign) IBOutlet NSImageView* smallLogo;
@property (nonatomic, assign) IBOutlet NSTextField* displayName;
@end
@implementation DisplayNameScreen
- (void) awakeFromNib {
[self.backgroundImage setImage: [NSImage imageNamed:hifiBackgroundFilename]];
[self.smallLogo setImage: [NSImage imageNamed:hifiSmallLogoFilename]];
NSMutableAttributedString* displayNameString = [[NSMutableAttributedString alloc] initWithString:@"Display Name"];
[displayNameString addAttribute:NSForegroundColorAttributeName value:[NSColor grayColor] range:NSMakeRange(0, displayNameString.length)];
[displayNameString addAttribute:NSFontAttributeName value:[NSFont systemFontOfSize:18] range:NSMakeRange(0,displayNameString.length)];
[self.displayName setPlaceholderAttributedString:displayNameString];
[self.displayName setTarget:self];
[self.displayName setAction:@selector(login:)];
}
- (IBAction)login:(id)sender {
Launcher* sharedLauncher = [Launcher sharedLauncher];
[sharedLauncher displayNameEntered: [self.displayName stringValue]];
}
- (IBAction)hyperLink:(id)sender
{
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://www.highfidelity.com/hq-support"]];
}
@end

View file

@ -0,0 +1,8 @@
#import <Cocoa/Cocoa.h>
@interface DownloadDomainContent : NSObject<NSURLSessionDataDelegate, NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLDownloadDelegate> {
}
- (void) downloadDomainContent:(NSString*) domainContentUrl;
@end

View file

@ -0,0 +1,60 @@
#import "DownloadDomainContent.h"
#import "Launcher.h"
@implementation DownloadDomainContent
- (void) downloadDomainContent:(NSString *)domainContentUrl
{
NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:domainContentUrl]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: self delegateQueue: [NSOperationQueue mainQueue]];
NSURLSessionDownloadTask *downloadTask = [defaultSession downloadTaskWithRequest:request];
[downloadTask resume];
}
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
CGFloat prog = (float)totalBytesWritten/totalBytesExpectedToWrite;
NSLog(@"domain content downloaded %d%%", (int)(100.0*prog));
}
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes {
// unused in this example
}
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
NSLog(@"Did finish downloading to url");
NSError *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *destinationFileName = downloadTask.originalRequest.URL.lastPathComponent;
NSString* finalFilePath = [[[Launcher sharedLauncher] getDownloadPathForContentAndScripts] stringByAppendingPathComponent:destinationFileName];
NSURL *destinationURL = [NSURL URLWithString: [finalFilePath stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]] relativeToURL: [NSURL URLWithString:@"file://"]];
if([fileManager fileExistsAtPath:[destinationURL path]])
{
[fileManager removeItemAtURL:destinationURL error:nil];
}
NSLog(@"%@", location.path);
NSLog(@"%@", destinationURL);
BOOL success = [fileManager moveItemAtURL:location toURL:destinationURL error:&error];
NSLog(success ? @"TRUE" : @"FALSE");
Launcher* sharedLauncher = [Launcher sharedLauncher];
[sharedLauncher setDownloadContextFilename:destinationFileName];
NSLog(@"extracting domain content file");
[sharedLauncher extractZipFileAtDestination:[[sharedLauncher getDownloadPathForContentAndScripts] stringByAppendingString:@"content"] :[[sharedLauncher getDownloadPathForContentAndScripts] stringByAppendingString:[sharedLauncher getDownloadContentFilename]]];
NSLog(@"finished extracting content file");
[sharedLauncher domainContentDownloadFinished];
}
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
NSLog(@"completed; error: %@", error);
}
@end

View file

@ -0,0 +1,8 @@
#import <Cocoa/Cocoa.h>
@interface DownloadInterface : NSObject<NSURLSessionDataDelegate, NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLDownloadDelegate> {
}
@property (nonatomic, assign) NSString* finalFilePath;
- (void) downloadInterface:(NSString*) downloadUrl;
@end

View file

@ -0,0 +1,72 @@
#import "DownloadInterface.h"
#import "Launcher.h"
#import "Settings.h"
@implementation DownloadInterface
- (void) downloadInterface:(NSString*) downloadUrl
{
NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:downloadUrl]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: self delegateQueue: [NSOperationQueue mainQueue]];
NSURLSessionDownloadTask *downloadTask = [defaultSession downloadTaskWithRequest:request];
[downloadTask resume];
}
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
CGFloat prog = (float)totalBytesWritten/totalBytesExpectedToWrite;
NSLog(@"interface downloaded %d%%", (int)(100.0*prog));
}
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes {
// unused in this example
}
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
NSLog(@"Did finish downloading to url");
NSError *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *destinationFileName = downloadTask.originalRequest.URL.lastPathComponent;
NSString* finalFilePath = [[[Launcher sharedLauncher] getAppPath] stringByAppendingPathComponent:destinationFileName];
NSURL *destinationURL = [NSURL URLWithString: [finalFilePath stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]] relativeToURL: [NSURL URLWithString:@"file://"]];
if([fileManager fileExistsAtPath:[destinationURL path]])
{
[fileManager removeItemAtURL:destinationURL error:nil];
}
[fileManager moveItemAtURL:location toURL:destinationURL error:&error];
Launcher* sharedLauncher = [Launcher sharedLauncher];
[sharedLauncher setDownloadFilename:destinationFileName];
NSString* appPath = [sharedLauncher getAppPath];
NSString* downloadFileName = [sharedLauncher getDownloadFilename];
NSLog(@"extract interface zip");
[sharedLauncher extractZipFileAtDestination:appPath :[appPath stringByAppendingString:downloadFileName]];
NSLog(@"finished extracting interface zip");
NSLog(@"starting xattr");
NSTask* quaratineTask = [[NSTask alloc] init];
quaratineTask.launchPath = @"/usr/bin/xattr";
quaratineTask.arguments = @[@"-d", @"com.apple.quarantine", [appPath stringByAppendingString:@"interface.app"]];
[quaratineTask launch];
[quaratineTask waitUntilExit];
NSLog(@"finished xattr");
NSString* launcherPath = [appPath stringByAppendingString:@"Launcher"];
[[Settings sharedSettings] setLauncherPath:launcherPath];
[sharedLauncher interfaceFinishedDownloading];
}
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
NSLog(@"completed; error: %@", error);
}
@end

View file

@ -0,0 +1,8 @@
#import <Cocoa/Cocoa.h>
@interface DownloadScripts : NSObject <NSURLDownloadDelegate> {
}
@property (nonatomic, assign) NSString* finalFilePath;
- (void) downloadScripts:(NSString*) scriptUrl;
@end

View file

@ -0,0 +1,39 @@
#import "DownloadScripts.h"
#import "Launcher.h"
@implementation DownloadScripts
- (void) downloadScripts:(NSString*) scriptsUrl
{
/*NSURLRequest* theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:scriptsUrl]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:6000.0];
NSURLDownload *theDownload = [[NSURLDownload alloc] initWithRequest:theRequest delegate:self];
if (!theDownload) {
NSLog(@"Download Failed");
}*/
}
- (void)download:(NSURLDownload *)download decideDestinationWithSuggestedFilename:(NSString *)filename
{
NSString* finalFilePath = [[[Launcher sharedLauncher] getDownloadPathForContentAndScripts] stringByAppendingPathComponent:filename];
[download setDestination:finalFilePath allowOverwrite:YES];
[[Launcher sharedLauncher] setDownloadScriptsFilename:filename];
}
- (void)downloadDidFinish:(NSURLDownload*)download
{
Launcher* sharedLauncher = [Launcher sharedLauncher];
[sharedLauncher extractZipFileAtDestination:[[sharedLauncher getDownloadPathForContentAndScripts] stringByAppendingString:@"scripts"] :[[sharedLauncher getDownloadPathForContentAndScripts] stringByAppendingString:[sharedLauncher getDownloadScriptsFilename]]];
[sharedLauncher domainScriptsDownloadFinished];
}
- (void)download:(NSURLDownload*)download didReceiveResponse:(NSURLResponse*)response
{
NSLog(@"DownloadScripts received a response");
}
@end

View file

@ -0,0 +1,7 @@
#import <Cocoa/Cocoa.h>
@interface ErrorViewController : NSViewController {
}
@end

View file

@ -0,0 +1,26 @@
#import "ErrorViewController.h"
#import "Launcher.h"
#import "CustomUI.h"
@interface ErrorViewController()
@property (nonatomic, assign) IBOutlet NSImageView* backgroundImage;
@property (nonatomic, assign) IBOutlet NSImageView* smallLogo;
@property (nonatomic, assign) IBOutlet NSImageView* voxelImage;
@end
@implementation ErrorViewController
- (void) awakeFromNib
{
[self.backgroundImage setImage:[NSImage imageNamed:hifiBackgroundFilename]];
[self.smallLogo setImage:[NSImage imageNamed:hifiSmallLogoFilename]];
[self.voxelImage setImage:[NSImage imageNamed:hifiVoxelFilename]];
}
-(IBAction)resartLauncher:(id)sender
{
[[Launcher sharedLauncher] showLoginScreen];
}
@end

View file

@ -0,0 +1,10 @@
#import <Cocoa/Cocoa.h>
@interface LatestBuildRequest : NSObject <NSURLConnectionDelegate> {
}
@property (nonatomic, retain) NSMutableData* webData;
@property (nonatomic, retain) NSString* jsonString;
- (void) requestLatestBuildInfo;
@end

View file

@ -0,0 +1,104 @@
#import "LatestBuildRequest.h"
#import "Launcher.h"
#import "Settings.h"
@implementation LatestBuildRequest
- (void) requestLatestBuildInfo {
NSMutableURLRequest *request = [NSMutableURLRequest new];
[request setURL:[NSURL URLWithString:@"https://thunder.highfidelity.com/builds/api/tags/latest?format=json"]];
[request setHTTPMethod:@"GET"];
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSURLSession* session = [NSURLSession sharedSession];
NSURLSessionDataTask* dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
Launcher* sharedLauncher = [Launcher sharedLauncher];
NSLog(@"credentials request finished");
NSMutableData* webData = [NSMutableData data];
[webData appendData:data];
NSString* jsonString = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[data length] encoding:NSUTF8StringEncoding];
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
id json = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];
NSFileManager* fileManager = [NSFileManager defaultManager];
NSArray *values = [json valueForKey:@"results"];
NSDictionary *value = [values objectAtIndex:0];
NSString* buildNumber = [value valueForKey:@"latest_version"];
NSDictionary* installers = [value objectForKey:@"installers"];
NSDictionary* macInstallerObject = [installers objectForKey:@"mac"];
NSString* macInstallerUrl = [macInstallerObject valueForKey:@"zip_url"];
BOOL appDirectoryExist = [fileManager fileExistsAtPath:[[sharedLauncher getAppPath] stringByAppendingString:@"interface.app"]];
dispatch_async(dispatch_get_main_queue(), ^{
Settings* settings = [Settings sharedSettings];
NSInteger currentVersion = [settings latestBuildVersion];
BOOL latestVersionAvailable = (currentVersion != buildNumber.integerValue);
[[Settings sharedSettings] buildVersion:buildNumber.integerValue];
BOOL shouldDownloadInterface = (latestVersionAvailable || !appDirectoryExist);
[sharedLauncher shouldDownloadLatestBuild:shouldDownloadInterface :macInstallerUrl];
});
}];
[dataTask resume];
//NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
/*if(theConnection) {
self.webData = [NSMutableData data];
NSLog(@"connection initiated");
}*/
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.webData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSHTTPURLResponse *ne = (NSHTTPURLResponse *)response;
if([ne statusCode] == 200) {
NSLog(@"connection state is 200 - all okay");
} else {
NSLog(@"connection state is NOT 200");
[[Launcher sharedLauncher] displayErrorPage];
}
}
-(void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(@"Conn Err: %@", [error localizedDescription]);
[[Launcher sharedLauncher] displayErrorPage];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
Launcher* sharedLauncher = [Launcher sharedLauncher];
self.jsonString = [[NSString alloc] initWithBytes: [self.webData mutableBytes] length:[self.webData length] encoding:NSUTF8StringEncoding];
NSData *data = [self.jsonString dataUsingEncoding:NSUTF8StringEncoding];
id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSFileManager* fileManager = [NSFileManager defaultManager];
NSArray *values = [json valueForKey:@"results"];
NSDictionary *value = [values objectAtIndex:0];
NSString* buildNumber = [value valueForKey:@"latest_version"];
NSDictionary* installers = [value objectForKey:@"installers"];
NSDictionary* macInstallerObject = [installers objectForKey:@"mac"];
NSString* macInstallerUrl = [macInstallerObject valueForKey:@"zip_url"];
BOOL appDirectoryExist = [fileManager fileExistsAtPath:[[sharedLauncher getAppPath] stringByAppendingString:@"interface.app"]];
Settings* settings = [Settings sharedSettings];
NSInteger currentVersion = [settings latestBuildVersion];
BOOL latestVersionAvailable = (currentVersion != buildNumber.integerValue);
[[Settings sharedSettings] buildVersion:buildNumber.integerValue];
BOOL shouldDownloadInterface = (latestVersionAvailable || !appDirectoryExist);
[sharedLauncher shouldDownloadLatestBuild:shouldDownloadInterface :macInstallerUrl];
}
@end

View file

@ -0,0 +1,5 @@
#pragma once
#include <string>
void launchInterface() {
}

View file

@ -0,0 +1,83 @@
#import <Cocoa/Cocoa.h>
#import "DownloadInterface.h"
#import "CredentialsRequest.h"
#import "DownloadDomainContent.h"
#import "LatestBuildRequest.h"
#import "OrganizationRequest.h"
#import "DownloadScripts.h"
typedef enum processStateTypes
{
DOWNLOADING_INTERFACE = 0,
RUNNING_INTERFACE_AFTER_DOWNLOAD,
CHECKING_UPDATE,
RUNNING_INTERFACE_AFTER_UPDATE
} ProcessState;
typedef enum LoginErrorTypes
{
NONE = 0,
ORGANIZATION,
CREDENTIALS
} LoginError;
@interface Launcher : NSObject <NSApplicationDelegate, NSWindowDelegate, NSURLDownloadDelegate> {
}
@property (nonatomic, retain) NSString* password;
@property (nonatomic, retain) NSString* username;
@property (nonatomic, retain) NSString* organization;
@property (nonatomic, retain) NSString* userToken;
@property (nonatomic, retain) NSString* displayName;
@property (nonatomic, retain) NSString* filename;
@property (nonatomic, retain) NSString* scriptsFilename;
@property (nonatomic, retain) NSString* contentFilename;
@property (nonatomic, retain) NSString* domainURL;
@property (nonatomic, retain) NSString* domainContentUrl;
@property (nonatomic, retain) NSString* domainScriptsUrl;
@property (nonatomic, retain) DownloadInterface* downloadInterface;
@property (nonatomic, retain) CredentialsRequest* credentialsRequest;
@property (nonatomic, retain) DownloadDomainContent* downloadDomainContent;
@property (nonatomic, retain) DownloadScripts* downloadScripts;
@property (nonatomic, retain) LatestBuildRequest* latestBuildRequest;
@property (nonatomic, retain) OrganizationRequest* organizationRequest;
@property (nonatomic) BOOL credentialsAccepted;
@property (nonatomic) BOOL waitingForCredentialReponse;
@property (nonatomic) BOOL gotCredentialResponse;
@property (nonatomic) BOOL waitingForInterfaceToTerminate;
@property (nonatomic, assign, readwrite) ProcessState processState;
@property (nonatomic, assign, readwrite) LoginError loginError;
- (void) displayNameEntered:(NSString*)aDisplayName;
- (void) credentialsEntered:(NSString*)aOrginization :(NSString*)aUsername :(NSString*)aPassword;
- (void) credentialsAccepted:(BOOL) aCredentialsAccepted;
- (void) domainContentDownloadFinished;
- (void) domainScriptsDownloadFinished;
- (void) setDomainURLInfo:(NSString*) aDomainURL :(NSString*) aDomainContentUrl :(NSString*) aDomainScriptsUrl;
- (void) organizationRequestFinished:(BOOL) aOriginzationAccepted;
- (BOOL) loginShouldSetErrorState;
- (void) displayErrorPage;
- (void) showLoginScreen;
- (ProcessState) currentProccessState;
- (void) setCurrentProcessState:(ProcessState) aProcessState;
- (void) setLoginErrorState:(LoginError) aLoginError;
- (LoginError) getLoginErrorState;
- (void) shouldDownloadLatestBuild:(BOOL) shouldDownload :(NSString*) downloadUrl;
- (void) interfaceFinishedDownloading;
- (NSString*) getDownloadPathForContentAndScripts;
- (void) launchInterface;
- (void) extractZipFileAtDestination:(NSString*) destination :(NSString*) file;
- (BOOL) isWaitingForInterfaceToTerminate;
- (void) setDownloadFilename:(NSString*) aFilename;
- (void) setDownloadContextFilename:(NSString*) aFilename;
- (void) setDownloadScriptsFilename:(NSString*) aFilename;
- (void) setTokenString:(NSString*) aToken;
- (NSString*) getTokenString;
- (NSString*) getDownloadContentFilename;
- (NSString*) getDownloadScriptsFilename;
- (NSString*) getDownloadFilename;
- (BOOL) isLoadedIn;
- (NSString*) getAppPath;
+ (id) sharedLauncher;
@end

View file

@ -0,0 +1,364 @@
#import "Launcher.h"
#import "Window.h"
#import "SplashScreen.h"
#import "LoginScreen.h"
#import "DisplayNameScreen.h"
#import "ProcessScreen.h"
#import "ErrorViewController.h"
#import "Settings.h"
@interface Launcher ()
@property (strong) IBOutlet Window* window;
@property (nonatomic, assign) NSString* finalFilePath;
@property (nonatomic, assign) NSButton* button;
@end
static BOOL const DELETE_ZIP_FILES = TRUE;
@implementation Launcher
+ (id) sharedLauncher {
static Launcher* sharedLauncher = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedLauncher = [[self alloc]init];
});
return sharedLauncher;
}
-(id)init {
if (self = [super init]) {
self.username = [[NSString alloc] initWithString:@"Default Property Value"];
self.downloadInterface = [DownloadInterface alloc];
self.downloadDomainContent = [DownloadDomainContent alloc];
self.credentialsRequest = [CredentialsRequest alloc];
self.latestBuildRequest = [LatestBuildRequest alloc];
self.organizationRequest = [OrganizationRequest alloc];
self.downloadScripts = [DownloadScripts alloc];
self.credentialsAccepted = TRUE;
self.gotCredentialResponse = FALSE;
self.waitingForCredentialReponse = FALSE;
self.waitingForInterfaceToTerminate = FALSE;
self.userToken = nil;
self.processState = DOWNLOADING_INTERFACE;
}
return self;
}
-(void)awakeFromNib {
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:@selector(didTerminateApp:)
name:NSWorkspaceDidTerminateApplicationNotification
object:nil];
SplashScreen* splashScreen = [[SplashScreen alloc] initWithNibName:@"SplashScreen" bundle:nil];
[self.window setContentViewController: splashScreen];
[self closeInterfaceIfRunning];
if (!self.waitingForInterfaceToTerminate) {
[self checkLoginStatus];
}
}
- (NSString*) getDownloadPathForContentAndScripts
{
NSString* filePath = [[NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) objectAtIndex:0]
stringByAppendingString:@"/Launcher/"];
if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
NSError * error = nil;
[[NSFileManager defaultManager] createDirectoryAtPath:filePath withIntermediateDirectories:TRUE attributes:nil error:&error];
}
return filePath;
}
- (void) extractZipFileAtDestination:(NSString *)destination :(NSString*)file
{
NSTask* task = [[NSTask alloc] init];
task.launchPath = @"/usr/bin/unzip";
task.arguments = @[@"-o", @"-d", destination, file];
[task launch];
[task waitUntilExit];
if (DELETE_ZIP_FILES) {
NSFileManager* fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtPath:file error:NULL];
}
}
- (void) displayErrorPage
{
ErrorViewController* errorPage = [[ErrorViewController alloc] initWithNibName:@"ErrorScreen" bundle:nil];
[[[[NSApplication sharedApplication] windows] objectAtIndex:0] setContentViewController: errorPage];
}
- (void) checkLoginStatus
{
if ([self isLoadedIn]) {
Launcher* sharedLauncher = [Launcher sharedLauncher];
[sharedLauncher setCurrentProcessState:CHECKING_UPDATE];
ProcessScreen* processScreen = [[ProcessScreen alloc] initWithNibName:@"ProcessScreen" bundle:nil];
[[[[NSApplication sharedApplication] windows] objectAtIndex:0] setContentViewController: processScreen];
[self.latestBuildRequest requestLatestBuildInfo];
} else {
[NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:@selector(onSplashScreenTimerFinished:)
userInfo:nil
repeats:NO];
}
}
- (void) setDownloadContextFilename:(NSString *)aFilename
{
self.contentFilename = aFilename;
}
- (void) setDownloadScriptsFilename:(NSString*)aFilename
{
self.scriptsFilename = aFilename;
}
- (NSString*) getDownloadContentFilename
{
return self.contentFilename;
}
- (NSString*) getDownloadScriptsFilename
{
return self.scriptsFilename;
}
- (void)didTerminateApp:(NSNotification *)notification {
if (self.waitingForInterfaceToTerminate) {
NSString* appName = [notification.userInfo valueForKey:@"NSApplicationName"];
if ([appName isEqualToString:@"interface"]) {
self.waitingForInterfaceToTerminate = FALSE;
[self checkLoginStatus];
}
}
}
- (void) closeInterfaceIfRunning
{
NSWorkspace* workspace = [NSWorkspace sharedWorkspace];
NSArray* apps = [workspace runningApplications];
for (NSRunningApplication* app in apps) {
if ([[app bundleIdentifier] isEqualToString:@"com.highfidelity.interface"] ||
[[app bundleIdentifier] isEqualToString:@"com.highfidelity.interface-pr"]) {
[app terminate];
self.waitingForInterfaceToTerminate = true;
}
}
}
- (BOOL) isWaitingForInterfaceToTerminate {
return self.waitingForInterfaceToTerminate;
}
- (BOOL) isLoadedIn
{
return [[Settings sharedSettings] isLoggedIn];
}
- (void) setDomainURLInfo:(NSString *)aDomainURL :(NSString *)aDomainContentUrl :(NSString *)aDomainScriptsUrl
{
self.domainURL = aDomainURL;
self.domainContentUrl = aDomainContentUrl;
self.domainScriptsUrl = aDomainScriptsUrl;
[[Settings sharedSettings] setDomainUrl:aDomainURL];
}
- (NSString*) getAppPath
{
return [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/Contents/MacOS/"];
}
- (BOOL) loginShouldSetErrorState
{
return !self.credentialsAccepted;
}
- (void) displayNameEntered:(NSString*)aDiplayName
{
self.processState = DOWNLOADING_INTERFACE;
ProcessScreen* processScreen = [[ProcessScreen alloc] initWithNibName:@"ProcessScreen" bundle:nil];
[[[[NSApplication sharedApplication] windows] objectAtIndex:0] setContentViewController: processScreen];
[self.downloadDomainContent downloadDomainContent:self.domainContentUrl];
self.displayName = aDiplayName;
}
- (void) domainContentDownloadFinished
{
//.[self.downloadScripts downloadScripts:self.domainScriptsUrl];
[self.latestBuildRequest requestLatestBuildInfo];
}
- (void) domainScriptsDownloadFinished
{
[self.latestBuildRequest requestLatestBuildInfo];
}
- (void) saveCredentialsAccepted:(BOOL)aCredentialsAccepted
{
self.credentialsAccepted = aCredentialsAccepted;
}
- (void) credentialsAccepted:(BOOL)aCredentialsAccepted
{
self.credentialsAccepted = aCredentialsAccepted;
if (aCredentialsAccepted) {
DisplayNameScreen* displayNameScreen = [[DisplayNameScreen alloc] initWithNibName:@"DisplayNameScreen" bundle:nil];
[[[[NSApplication sharedApplication] windows] objectAtIndex:0] setContentViewController: displayNameScreen];
} else {
LoginScreen* loginScreen = [[LoginScreen alloc] initWithNibName:@"LoginScreen" bundle:nil];
[[[[NSApplication sharedApplication] windows] objectAtIndex:0] setContentViewController: loginScreen];
}
}
- (void) interfaceFinishedDownloading
{
if (self.processState == DOWNLOADING_INTERFACE) {
self.processState = RUNNING_INTERFACE_AFTER_DOWNLOAD;
} else {
self.processState = RUNNING_INTERFACE_AFTER_UPDATE;
}
ProcessScreen* processScreen = [[ProcessScreen alloc] initWithNibName:@"ProcessScreen" bundle:nil];
[[[[NSApplication sharedApplication] windows] objectAtIndex:0] setContentViewController: processScreen];
[self launchInterface];
}
- (void) credentialsEntered:(NSString*)aOrginization :(NSString*)aUsername :(NSString*)aPassword
{
self.organization = aOrginization;
self.username = aUsername;
self.password = aPassword;
[self.organizationRequest confirmOrganization:aOrginization :aUsername];
}
- (void) organizationRequestFinished:(BOOL)aOriginzationAccepted
{
self.credentialsAccepted = aOriginzationAccepted;
if (aOriginzationAccepted) {
[self.credentialsRequest confirmCredentials:self.username : self.password];
} else {
LoginScreen* loginScreen = [[LoginScreen alloc] initWithNibName:@"LoginScreen" bundle:nil];
[[[[NSApplication sharedApplication] windows] objectAtIndex:0] setContentViewController: loginScreen];
}
}
- (BOOL)canBecomeKeyWindow
{
return YES;
}
-(void) showLoginScreen
{
LoginScreen* loginScreen = [[LoginScreen alloc] initWithNibName:@"LoginScreen" bundle:nil];
[[[[NSApplication sharedApplication] windows] objectAtIndex:0] setContentViewController: loginScreen];
}
- (void) shouldDownloadLatestBuild:(BOOL) shouldDownload :(NSString*) downloadUrl
{
if (shouldDownload) {
[self.downloadInterface downloadInterface: downloadUrl];
return;
}
[self launchInterface];
}
-(void)onSplashScreenTimerFinished:(NSTimer *)timer
{
[self showLoginScreen];
}
-(void)setCurrentProcessState:(ProcessState)aProcessState
{
self.processState = aProcessState;
}
- (void)applicationWillFinishLaunching:(NSNotification *)notification
{
[self.window makeKeyAndOrderFront:self];
}
- (void) setDownloadFilename:(NSString *)aFilename
{
self.filename = aFilename;
}
- (NSString*) getDownloadFilename
{
return self.filename;
}
- (void) setTokenString:(NSString *)aToken
{
self.userToken = aToken;
}
- (NSString*) getTokenString
{
return self.userToken;
}
- (void) setLoginErrorState:(LoginError)aLoginError
{
self.loginError = aLoginError;
}
- (LoginError) getLoginErrorState
{
return self.loginError;
}
- (void) launchInterface
{
NSString* launcherPath = [[self getAppPath] stringByAppendingString:@"HQ Launcher"];
[[Settings sharedSettings] setLauncherPath:launcherPath];
[[Settings sharedSettings] save];
NSWorkspace *workspace = [NSWorkspace sharedWorkspace];
NSURL *url = [NSURL fileURLWithPath:[workspace fullPathForApplication:[[self getAppPath] stringByAppendingString:@"interface.app/Contents/MacOS/interface"]]];
NSError *error = nil;
NSString* contentPath = [[self getDownloadPathForContentAndScripts] stringByAppendingString:@"content"];
NSString* displayName = [ self displayName];
NSString* scriptsPath = [[self getAppPath] stringByAppendingString:@"interface.app/Contents/Resources/scripts/simplifiedUI/"];
NSString* domainUrl = [[Settings sharedSettings] getDomainUrl];
NSString* userToken = [[Launcher sharedLauncher] getTokenString];
NSArray* arguments;
if (userToken != nil) {
arguments = [NSArray arrayWithObjects:
@"--url" , domainUrl ,
@"--tokens", userToken,
@"--cache", contentPath,
@"--displayName", displayName,
@"--script", scriptsPath,
@"--no-updater",
@"--no-launcher", nil];
} else {
arguments = [NSArray arrayWithObjects:
@"--url" , domainUrl,
@"--cache", contentPath,
@"--script", scriptsPath,
@"--no-updater",
@"--no-launcher", nil];
}
[workspace launchApplicationAtURL:url options:NSWorkspaceLaunchNewInstance configuration:[NSDictionary dictionaryWithObject:arguments forKey:NSWorkspaceLaunchConfigurationArguments] error:&error];
//NSLog(@"arguments %@", [NSDictionary dictionaryWithObject:arguments forKey:NSWorkspaceLaunchConfigurationArguments]);
[NSApp terminate:self];
}
- (ProcessState) currentProccessState
{
return self.processState;
}
@end

View file

@ -0,0 +1,5 @@
#import <Cocoa/Cocoa.h>
@interface LoginScreen : NSViewController {
}
@end

View file

@ -0,0 +1,76 @@
#import "LoginScreen.h"
#import "Launcher.h"
#import "CustomUI.h"
@interface LoginScreen ()
@property (nonatomic, assign) IBOutlet NSImageView* backgroundImage;
@property (nonatomic, assign) IBOutlet NSImageView* smallLogo;
@property (nonatomic, assign) IBOutlet NSTextField* username;
@property (nonatomic, assign) IBOutlet NSTextField* password;
@property (nonatomic, assign) IBOutlet NSTextField* orginization;
@property (nonatomic, assign) IBOutlet NSTextField* header;
@property (nonatomic, assign) IBOutlet NSTextField* smallHeader;
@property (nonatomic, assign) IBOutlet NSTextField* trouble;
@property (nonatomic, assign) IBOutlet NSButton* button;
@end
@implementation LoginScreen
- (void) awakeFromNib
{
Launcher* sharedLauncher = [Launcher sharedLauncher];
if ([sharedLauncher loginShouldSetErrorState]) {
[self.header setStringValue:@"Uh-oh, we have a problem"];
switch ([sharedLauncher getLoginErrorState]) {
case CREDENTIALS:
[self.smallHeader setStringValue:@"There is a problem with your credentials, please try again"];
break;
case ORGANIZATION:
[self.smallHeader setStringValue:@"There is a problem with your organization name, please try again"];
break;
case NONE:
break;
default:
break;
}
[self.button setTitle:@"TRY AGAIN"];
}
[self.backgroundImage setImage:[NSImage imageNamed:hifiBackgroundFilename]];
[self.smallLogo setImage:[NSImage imageNamed:hifiSmallLogoFilename]];
NSMutableAttributedString* usernameString = [[NSMutableAttributedString alloc] initWithString:@"Username"];
[usernameString addAttribute:NSForegroundColorAttributeName value:[NSColor grayColor] range:NSMakeRange(0,8)];
[usernameString addAttribute:NSFontAttributeName value:[NSFont systemFontOfSize:18] range:NSMakeRange(0,8)];
NSMutableAttributedString* orgName = [[NSMutableAttributedString alloc] initWithString:@"Organization Name"];
[orgName addAttribute:NSForegroundColorAttributeName value:[NSColor grayColor] range:NSMakeRange(0,17)];
[orgName addAttribute:NSFontAttributeName value:[NSFont systemFontOfSize:18] range:NSMakeRange(0,17)];
NSMutableAttributedString* passwordString = [[NSMutableAttributedString alloc] initWithString:@"Password"];
[passwordString addAttribute:NSForegroundColorAttributeName value:[NSColor grayColor] range:NSMakeRange(0,8)];
[passwordString addAttribute:NSFontAttributeName value:[NSFont systemFontOfSize:18] range:NSMakeRange(0,8)];
[self.username setPlaceholderAttributedString:usernameString];
[self.orginization setPlaceholderAttributedString:orgName];
[self.password setPlaceholderAttributedString:passwordString];
[self.password setTarget:self];
[self.password setAction:@selector(goToLogin:)];
}
- (IBAction)goToLogin:(id)sender
{
printf("In gotologin");
Launcher* sharedLauncher = [Launcher sharedLauncher];
[sharedLauncher credentialsEntered:[self.orginization stringValue] :[self.username stringValue] :[self.password stringValue]];
}
- (IBAction)havingTrouble:(id)sender
{
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://www.highfidelity.com/hq-support"]];
}
@end

View file

@ -0,0 +1,11 @@
#import <Cocoa/Cocoa.h>
@interface OrganizationRequest : NSObject <NSURLConnectionDelegate> {
}
@property (nonatomic, retain) NSMutableData* webData;
@property (nonatomic, retain) NSString* jsonString;
@property (nonatomic, retain) NSString* username;
- (void) confirmOrganization:(NSString*) aOrganization :(NSString*) aUsername;
@end

View file

@ -0,0 +1,92 @@
#import "OrganizationRequest.h"
#include <CommonCrypto/CommonDigest.h>
#include <CommonCrypto/CommonHMAC.h>
#import "Launcher.h"
static NSString* const organizationURL = @"https://s3.amazonaws.com/hifi-public/huffman/organizations/";
@implementation OrganizationRequest
- (void) confirmOrganization:(NSString*)aOrganization :(NSString*)aUsername {
self.username = aUsername;
const char *cKey = LAUNCHER_HMAC_SECRET;
const char *cData = [[aOrganization lowercaseString] cStringUsingEncoding:NSASCIIStringEncoding];
unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA256, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
NSData *HMACData = [NSData dataWithBytes:cHMAC length:sizeof(cHMAC)];
const unsigned char *buffer = (const unsigned char *)[HMACData bytes];
NSMutableString *hash = [NSMutableString stringWithCapacity:HMACData.length * 2];
for (int i = 0; i < HMACData.length; ++i){
[hash appendFormat:@"%02x", buffer[i]];
}
NSString* jsonFile = [hash stringByAppendingString:@".json"];
NSError *error;
NSData *data = [NSData dataWithContentsOfURL: [NSURL URLWithString:[organizationURL stringByAppendingString:jsonFile]]];
Launcher* sharedLauncher = [Launcher sharedLauncher];
if (data) {
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
[sharedLauncher setDomainURLInfo:[json valueForKey:@"domain"] :[json valueForKey:@"content_set_url"] :[json valueForKey:@"scripts_url"]];
[sharedLauncher setLoginErrorState: NONE];
return [sharedLauncher organizationRequestFinished:TRUE];
}
[sharedLauncher setLoginErrorState: ORGANIZATION];
return [sharedLauncher organizationRequestFinished:FALSE];
/*NSLog(@"URL: %@", [organizationURL stringByAppendingString:jsonFile]);
NSMutableURLRequest *request = [NSMutableURLRequest new];
[request setURL:[NSURL URLWithString: [organizationURL stringByAppendingString:@"High%20Fidelity"]]];
[request setHTTPMethod:@"GET"];
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if(theConnection) {
self.webData = [NSMutableData data];
NSLog(@"connection initiated");
}*/
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.webData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSHTTPURLResponse *ne = (NSHTTPURLResponse *)response;
if([ne statusCode] == 200) {
NSLog(@"connection state is 200 - all okay");
} else {
NSLog(@"connection state is NOT 200");
}
}
-(void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(@"Conn Err: %@", [error localizedDescription]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
/*NSString* jsonString = [[NSString alloc] initWithBytes: [self.webData mutableBytes] length:[self.webData length] encoding:NSUTF8StringEncoding];
NSData *data = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];*/
/*Launcher* sharedLauncher = [Launcher sharedLauncher];
if (json[@"error"] != nil) {
NSLog(@"Login in failed");
NSString* accessToken = [json objectForKey:@"access_token"];
NSLog(@"access token %@", accessToken);
[sharedLauncher credentialsAccepted:FALSE];
} else {
NSLog(@"Login successful");
NSString* accessToken = [json objectForKey:@"access_token"];
NSLog(@"access token %@", accessToken);
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setValue:accessToken forKey:@"access_token"];
[defaults synchronize];
[sharedLauncher credentialsAccepted:TRUE];
}*/
//NSLog(@"OUTPUT:: %@", self.jsonString);
}
@end

View file

@ -0,0 +1,7 @@
#import <Cocoa/Cocoa.h>
@interface ProcessScreen : NSViewController {
}
@property (nonatomic, assign) NSInteger imageRotation;
@end

View file

@ -0,0 +1,55 @@
#import "ProcessScreen.h"
#import "Launcher.h"
#import "CustomUI.h"
@interface ProcessScreen ()
@property (nonatomic, assign) IBOutlet NSImageView* background;
@property (nonatomic, assign) IBOutlet NSImageView* smallLogo;
@property (nonatomic, assign) IBOutlet NSImageView* voxelImage;
@property (nonatomic, assign) IBOutlet NSTextField* boldStatus;
@property (nonatomic, assign) IBOutlet NSTextField* smallStatus;
@end
@implementation ProcessScreen
- (void) awakeFromNib {
Launcher* sharedLauncher = [Launcher sharedLauncher];
switch ([sharedLauncher currentProccessState]) {
case DOWNLOADING_INTERFACE:
[self.boldStatus setStringValue:@"We're building your virtual HQ"];
[self.smallStatus setStringValue:@"Set up may take several minutes."];
break;
case RUNNING_INTERFACE_AFTER_DOWNLOAD:
[self.boldStatus setStringValue:@"Your new HQ is all setup"];
[self.smallStatus setStringValue:@"Thanks for being patient."];
break;
case CHECKING_UPDATE:
[self.boldStatus setStringValue:@"Getting updates..."];
[self.smallStatus setStringValue:@"We're getting the lastest and greatest for you, one sec."];
break;
case RUNNING_INTERFACE_AFTER_UPDATE:
[self.boldStatus setStringValue:@"You're good to go!"];
[self.smallStatus setStringValue:@"Thanks for being patient."];
break;
default:
break;
}
[self.background setImage: [NSImage imageNamed:hifiBackgroundFilename]];
[self.smallLogo setImage: [NSImage imageNamed:hifiSmallLogoFilename]];
[self.voxelImage setImage: [NSImage imageNamed:hifiVoxelFilename]];
self.imageRotation = 0;
//[self.voxelImage setFrameCenterRotation:90];
[NSTimer scheduledTimerWithTimeInterval:0.016
target:self
selector:@selector(rotateView:)
userInfo:nil
repeats:YES];
}
- (void) rotateView:(NSTimer *)timer{
self.imageRotation -= 1;
[self.voxelImage setFrameCenterRotation:self.imageRotation];
}
@end

View file

@ -0,0 +1,20 @@
#import <Cocoa/Cocoa.h>
@interface Settings : NSObject {
}
@property (nonatomic, assign) NSInteger build;
@property (nonatomic, assign) BOOL loggedIn;
@property (nonatomic, assign) NSString* domain;
@property (nonatomic, assign) NSString* launcher;
- (NSInteger) latestBuildVersion;
- (BOOL) isLoggedIn;
- (void) login:(BOOL)aLoggedIn;
- (void) buildVersion:(NSInteger) aBuildVersion;
- (void) setLauncherPath:(NSString*) aLauncherPath;
- (NSString*) getLaucnherPath;
- (void) setDomainUrl:(NSString*) aDomainUrl;
- (NSString*) getDomainUrl;
- (void) save;
+ (id) sharedSettings;
@end

View file

@ -0,0 +1,127 @@
#import "Settings.h"
#import "Launcher.h"
@implementation Settings
+ (id) sharedSettings
{
static Settings* sharedSettings = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedSettings = [[self alloc]init];
});
return sharedSettings;
}
- (NSString*) getFilePath
{
Launcher* sharedLauncher = [Launcher sharedLauncher];
NSString* appPath = [sharedLauncher getAppPath];
NSString* filePath = [appPath stringByAppendingString:@"interface.app/Contents/MacOS/"];
return filePath;
}
- (void) readDataFromJsonFile
{
NSString* filePath = [self getFilePath];
NSString* fileAtPath = [filePath stringByAppendingString:@"config.json"];
if ([[NSFileManager defaultManager] fileExistsAtPath:fileAtPath]) {
NSString* jsonString = [[NSString alloc] initWithData:[NSData dataWithContentsOfFile:fileAtPath] encoding:NSUTF8StringEncoding];
NSError * err;
NSData *data =[jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary * json;
if(data!=nil){
json = (NSDictionary *)[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&err];
self.loggedIn = [[json valueForKey:@"loggedIn"] boolValue];
self.build = [[json valueForKey:@"build_version"] integerValue];
self.launcher = [json valueForKey:@"luancherPath"];
self.domain = [json valueForKey:@"domain"];
return;
}
}
self.loggedIn = false;
self.build = 0;
self.launcher = nil;
self.domain = nil;
}
- (void) writeDataToFile {
NSDictionary* json = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSString stringWithFormat:@"%ld", self.build], @"build_version",
self.loggedIn ? @"TRUE" : @"FALSE", @"loggedIn",
self.domain, @"domain",
self.launcher, @"launcherPath", nil];
NSError * err;
NSData * jsonData = [NSJSONSerialization dataWithJSONObject:json options:0 error:&err];
NSString * jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSString* filePath = [self getFilePath];
NSString* fileAtPath = [filePath stringByAppendingString:@"config.json"];
if (![[NSFileManager defaultManager] fileExistsAtPath:fileAtPath]) {
NSError * error = nil;
[[NSFileManager defaultManager] createDirectoryAtPath:filePath withIntermediateDirectories:FALSE attributes:nil error:&error];
[[NSFileManager defaultManager] createFileAtPath:fileAtPath contents:nil attributes:nil];
}
[[jsonString dataUsingEncoding:NSUTF8StringEncoding] writeToFile:fileAtPath atomically:NO];
}
-(id)init
{
if (self = [super init]) {
[self readDataFromJsonFile];
}
return self;
}
- (BOOL) isLoggedIn
{
return self.loggedIn;
}
- (NSInteger) latestBuildVersion
{
return self.build;
}
- (void) buildVersion:(NSInteger)aBuildVersion
{
self.build = aBuildVersion;
}
- (void) login:(BOOL)aLoggedIn
{
self.loggedIn = aLoggedIn;
}
- (void) setLauncherPath:(NSString *)aLauncherPath
{
self.launcher = aLauncherPath;
}
- (void) setDomainUrl:(NSString *)aDomainUrl
{
self.domain = aDomainUrl;
}
- (NSString*) getDomainUrl
{
return self.domain;
}
- (NSString*) getLaucnherPath
{
return self.launcher;
}
- (void) save
{
[self writeDataToFile];
}
@end

View file

@ -0,0 +1,6 @@
#import <Cocoa/Cocoa.h>
#import "LoginScreen.h"
@interface SplashScreen: NSViewController {
}
@end

View file

@ -0,0 +1,19 @@
#import "SplashScreen.h"
#import "Launcher.h"
#import "CustomUI.h"
@interface SplashScreen ()
@property (nonatomic, assign) IBOutlet NSImageView* imageView;
@property (nonatomic, assign) IBOutlet NSImageView* logoImage;
@property (nonatomic, assign) IBOutlet NSButton* button;
@end
@implementation SplashScreen
- (void) viewDidLoad {
}
-(void)awakeFromNib {
[self.imageView setImage:[NSImage imageNamed:hifiBackgroundFilename]];
[self.logoImage setImage:[NSImage imageNamed:hifiLargeLogoFilename]];
}
@end

View file

@ -0,0 +1,6 @@
#import <Cocoa/Cocoa.h>
@interface Window : NSWindow {
}
@end

View file

@ -0,0 +1,7 @@
#import "Window.h"
@implementation Window
-(BOOL)canBecomeKeyWindow {
return YES;
}
@end

View file

@ -0,0 +1,45 @@
#import "Launcher.h"
#import "Settings.h"
void redirectLogToDocuments()
{
NSString* filePath = [[NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) objectAtIndex:0]
stringByAppendingString:@"/Launcher/"];
if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
NSError * error = nil;
[[NSFileManager defaultManager] createDirectoryAtPath:filePath withIntermediateDirectories:TRUE attributes:nil error:&error];
}
NSString *pathForLog = [filePath stringByAppendingPathComponent:@"log.txt"];
freopen([pathForLog cStringUsingEncoding:NSASCIIStringEncoding],"a+",stderr);
}
int main(int argc, const char* argv[]) {
//NSApp.appearance = [NSAppearance appearanceNamed: NSAppearanceNameAqua];
redirectLogToDocuments();
NSArray* apps = [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.highfidelity.launcher"];
if ([apps count] > 1) {
NSLog(@"launcher is already running");
return 0;
}
[NSApplication sharedApplication];
Launcher* sharedLauncher = [Launcher sharedLauncher];
[Settings sharedSettings];
[NSApp setDelegate: sharedLauncher];
// Referenced from https://stackoverflow.com/questions/9155015/handle-cmd-q-in-cocoa-application-and-menu-item-quit-application-programmatic
id menubar = [[NSMenu new] autorelease];
id appMenuItem = [[NSMenuItem new] autorelease];
[menubar addItem:appMenuItem];
[NSApp setMainMenu:menubar];
id appMenu = [[NSMenu new] autorelease];
id appName = [[NSProcessInfo processInfo] processName];
id quitTitle = [@"Quit " stringByAppendingString:appName];
id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:quitTitle action:@selector(terminate:) keyEquivalent:@"q"] autorelease];
[appMenu addItem:quitMenuItem];
[appMenuItem setSubmenu:appMenu];
return NSApplicationMain(argc, argv);
}

33
launchers/win32/BUILD.md Normal file
View file

@ -0,0 +1,33 @@
## Windows Build Guide
To build the launcher we need:
- Visual Studio Community 2017.
- CMake.
### Installing Visual Studio 2017
If you dont have Community or Professional edition of Visual Studio 2017, download [Visual Studio Community 2017](https://www.visualstudio.com/downloads/).
When selecting components, check "Desktop development with C++". Also on the right on the Summary toolbar, check "Windows 8.1 SDK and UCRT SDK" and "VC++ 2015.3 v140 toolset (x86,x64)".
If you already have Visual Studio installed and need to add python, open the "Add or remove programs" control panel and find the "Microsoft Visual Studio Installer".
### Installing CMake
Download and install the latest version of CMake 3.9.
Download the file named win64-x64 Installer from the [CMake Website](https://cmake.org/download/). You can access the installer on this [3.9 Version page](https://cmake.org/files/v3.9/). During installation, make sure to check "Add CMake to system PATH for all users" when prompted.
### Running CMake to Generate Build Files
Run Command Prompt from Start and run the following commands:
```
cd "%LAUNCHER_DIR%"
mkdir build
cd build
cmake .. -G "Visual Studio 15 Win64" -T v140
```
Where `%LAUNCHER_DIR%` is the directory for the PC light launcher repository.

View file

@ -0,0 +1,80 @@
cmake_minimum_required(VERSION 3.9.2)
project(HQLauncher)
set(CMAKE_CONFIGURATION_TYPES "Debug;Release"
CACHE STRING "Configuration types" FORCE)
# Use of MFC
set(CMAKE_MFC_FLAG 1)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
add_executable(HQLauncher
WIN32
libs/json/json.h
libs/json/json-forwards.h
libs/jsoncpp.cpp
LauncherApp.cpp
LauncherApp.h
Launcher.rc
LauncherDlg.cpp
LauncherDlg.h
LauncherManager.cpp
LauncherManager.h
LauncherUtils.cpp
LauncherUtils.h
libs/miniz.cpp
libs/miniz.h
res/HiFi_Logo_Large.png
res/HiFi_Logo_Small.png
res/HiFi_Voxel.png
res/HiFi_Window.png
res/interface.ico
res/Launcher.rc2
Resource.h
stdafx.cpp
stdafx.h
targetver.h
)
function(set_from_env _RESULT_NAME _ENV_VAR_NAME _DEFAULT_VALUE)
if (NOT DEFINED ${_RESULT_NAME})
if ("$ENV{${_ENV_VAR_NAME}}" STREQUAL "")
set (${_RESULT_NAME} ${_DEFAULT_VALUE} PARENT_SCOPE)
else()
set (${_RESULT_NAME} $ENV{${_ENV_VAR_NAME}} PARENT_SCOPE)
endif()
endif()
endfunction()
set_from_env(LAUNCHER_HMAC_SECRET LAUNCHER_HMAC_SECRET "")
target_compile_definitions(${PROJECT_NAME} PRIVATE LAUNCHER_HMAC_SECRET="${LAUNCHER_HMAC_SECRET}")
# Preprocessor definitions
target_compile_definitions(HQLauncher PRIVATE
$<$<CONFIG:Debug>:_UNICODE;_WINDOWS;_DEBUG>
$<$<CONFIG:Release>:_UNICODE;_WINDOWS;NDEBUG>
)
# Minimal rebuild
if (MSVC)
target_compile_options(HQLauncher PRIVATE
"$<$<CONFIG:Debug>:/Gm->"
"$<$<CONFIG:Release>:/Gm->"
)
endif ()
# Precompiled header files
if (MSVC)
target_compile_options(HQLauncher PRIVATE
"$<$<CONFIG:Debug>:/Yu>"
"$<$<CONFIG:Release>:/Yu>"
)
set_property(SOURCE stdafx.cpp
APPEND_STRING PROPERTY COMPILE_FLAGS
"$<$<CONFIG:Debug>:/Yc> \
$<$<CONFIG:Release>:/Yc>")
endif ()

214
launchers/win32/Launcher.rc Normal file
View file

@ -0,0 +1,214 @@
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#ifndef APSTUDIO_INVOKED
#include "targetver.h"
#endif
#include "afxres.h"
#include "verrsrc.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#ifndef APSTUDIO_INVOKED\r\n"
"#include ""targetver.h""\r\n"
"#endif\r\n"
"#include ""afxres.h""\r\n"
"#include ""verrsrc.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"#define _AFX_NO_SPLITTER_RESOURCES\r\n"
"#define _AFX_NO_OLE_RESOURCES\r\n"
"#define _AFX_NO_TRACKER_RESOURCES\r\n"
"#define _AFX_NO_PROPERTY_RESOURCES\r\n"
"\r\n"
"#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n"
"LANGUAGE 9, 1\r\n"
"#include ""res\\Launcher.rc2"" // non-Microsoft Visual C++ edited resources\r\n"
"#include ""afxres.rc"" // Standard components\r\n"
"#endif\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDR_MAINFRAME ICON "res\\interface.ico"
/////////////////////////////////////////////////////////////////////////////
//
// BINARY
//
IDR_FONT_REGULAR BINARY "res\\Graphikregular.ttf"
IDR_FONT_BOLD BINARY "res\\Graphikbold.ttf"
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_LAUNCHER_DIALOG DIALOGEX 0, 0, 300, 196
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION
EXSTYLE WS_EX_APPWINDOW
FONT 10, "MS Shell Dlg", 400, 0, 0x0
BEGIN
CONTROL "",IDC_VOXEL,"Static",SS_BLACKRECT,65,27,174,123
EDITTEXT IDC_ORGNAME,44,68,219,12,ES_AUTOHSCROLL | NOT WS_VISIBLE | NOT WS_BORDER
EDITTEXT IDC_USERNAME,44,95,219,12,ES_AUTOHSCROLL | NOT WS_VISIBLE | NOT WS_BORDER
EDITTEXT IDC_PASSWORD,44,122,219,12,ES_PASSWORD | ES_AUTOHSCROLL | NOT WS_VISIBLE | NOT WS_BORDER
LTEXT "Organization name",IDC_ORGNAME_BANNER,48,68,219,12,NOT WS_VISIBLE
LTEXT "Username",IDC_USERNAME_BANNER,48,95,219,12,NOT WS_VISIBLE
LTEXT "Password",IDC_PASSWORD_BANNER,48,122,219,12,NOT WS_VISIBLE
CTEXT "",IDC_MESSAGE_LABEL,5,39,299,23,NOT WS_VISIBLE
CTEXT "",IDC_ACTION_LABEL,10,15,286,25,NOT WS_VISIBLE
CTEXT "",IDC_MESSAGE2_LABEL,35,172,239,15,NOT WS_VISIBLE
CTEXT "",IDC_ACTION2_LABEL,15,147,278,25,NOT WS_VISIBLE
RTEXT "",IDC_TERMS,15,172,180,15,NOT WS_VISIBLE
LTEXT "",IDC_TERMS2,197,172,80,15,NOT WS_VISIBLE
CTEXT "",IDC_TROUBLE,65,203,174,15,NOT WS_VISIBLE
CONTROL "NEXT",IDC_BUTTON_NEXT,"Button",BS_OWNERDRAW | BS_FLAT | NOT WS_VISIBLE | WS_TABSTOP,107,158,94,16
CONTROL "Having Trouble?",IDC_TROUBLE_LINK,"Button",BS_OWNERDRAW | BS_FLAT | NOT WS_VISIBLE | WS_TABSTOP,126,203,56,11
END
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,1
PRODUCTVERSION 1,0,0,1
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904B0"
BEGIN
VALUE "CompanyName", "High Fidelity Inc."
VALUE "FileDescription", "HQ Launcher"
VALUE "FileVersion", "1.0.0.1"
VALUE "InternalName", "HQ Launcher"
VALUE "LegalCopyright", "High Fidelity Inc. All rights reserved."
VALUE "OriginalFilename", "HQ Launcher.exe"
VALUE "ProductName", "HQ Launcher"
VALUE "ProductVersion", "0.0.8.5"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
IDD_LAUNCHER_DIALOG, DIALOG
BEGIN
END
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// AFX_DIALOG_LAYOUT
//
IDD_LAUNCHER_DIALOG AFX_DIALOG_LAYOUT
BEGIN
0
END
/////////////////////////////////////////////////////////////////////////////
//
// PNG
//
IDB_PNG1 PNG "res\\HiFi_Window.png"
IDB_PNG2 PNG "res\\HiFi_Logo_Large.png"
IDB_PNG4 PNG "res\\HiFi_Voxel.png"
IDB_PNG5 PNG "res\\HiFi_Logo_Small.png"
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
#define _AFX_NO_SPLITTER_RESOURCES
#define _AFX_NO_OLE_RESOURCES
#define _AFX_NO_TRACKER_RESOURCES
#define _AFX_NO_PROPERTY_RESOURCES
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE 9, 1
#include "res\Launcher.rc2" // non-Microsoft Visual C++ edited resources
#include "afxres.rc" // Standard components
#endif
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

View file

@ -0,0 +1,91 @@
//
// LauncherApp.cpp
//
// Created by Luis Cuenca on 6/5/2019.
// 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 "stdafx.h"
#include "LauncherApp.h"
#include "LauncherDlg.h"
#include "LauncherManager.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CLauncherApp
BEGIN_MESSAGE_MAP(CLauncherApp, CWinApp)
ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
END_MESSAGE_MAP()
CLauncherApp::CLauncherApp(){}
// The one and only CLauncherApp object
CLauncherApp theApp;
// CLauncherApp initialization
BOOL CLauncherApp::InitInstance() {
// don't launch if already running
CreateMutex(NULL, TRUE, _T("HQ_Launcher_Mutex"));
if (GetLastError() == ERROR_ALREADY_EXISTS) {
return FALSE;
}
int iNumOfArgs;
LPWSTR* pArgs = CommandLineToArgvW(GetCommandLine(), &iNumOfArgs);
if (iNumOfArgs > 1 && CString(pArgs[1]).Compare(_T("--uninstall")) == 0) {
_manager.uninstall();
} else {
_manager.init();
}
if (!_manager.installLauncher()) {
return FALSE;
}
installFont(IDR_FONT_REGULAR);
installFont(IDR_FONT_BOLD);
CWinApp::InitInstance();
SetRegistryKey(_T("HQ High Fidelity"));
CLauncherDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
#if !defined(_AFXDLL) && !defined(_AFX_NO_MFC_CONTROLS_IN_DIALOGS)
ControlBarCleanUp();
#endif
// Since the dialog has been closed, return FALSE so that we exit the
// application, rather than start the application's message pump.
return FALSE;
}
BOOL CLauncherApp::installFont(int fontID) {
HINSTANCE hResInstance = AfxGetResourceHandle();
HRSRC res = FindResource(hResInstance,
MAKEINTRESOURCE(fontID), L"BINARY");
if (res) {
HGLOBAL mem = LoadResource(hResInstance, res);
void *data = LockResource(mem);
DWORD len = (DWORD)SizeofResource(hResInstance, res);
DWORD nFonts;
auto m_fonthandle = AddFontMemResourceEx(
data, // font resource
len, // number of bytes in font resource
NULL, // Reserved. Must be 0.
&nFonts // number of fonts installed
);
return (m_fonthandle != 0);
}
return FALSE;
}

View file

@ -0,0 +1,32 @@
//
// LauncherApp.h
//
// Created by Luis Cuenca on 6/5/2019.
// 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
//
#pragma once
#ifndef __AFXWIN_H__
#error "include 'stdafx.h' before including this file for PCH"
#endif
#include "resource.h" // main symbols
#include "LauncherManager.h"
class CLauncherApp : public CWinApp
{
public:
CLauncherApp();
virtual BOOL InitInstance();
void setDialogOnFront() { SetWindowPos(m_pMainWnd->GetSafeHwnd(), HWND_TOPMOST, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE); }
LauncherManager _manager;
private:
BOOL installFont(int fontID);
DECLARE_MESSAGE_MAP()
};
extern CLauncherApp theApp;

View file

@ -0,0 +1,668 @@
//
// LauncherDlg.cpp
//
// Created by Luis Cuenca on 6/5/2019.
// 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 "stdafx.h"
#include "LauncherApp.h"
#include "LauncherDlg.h"
#include <d2d1.h>
#pragma comment(lib, "d2d1")
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
static int ACTION_FONT_SIZE = 40;
static int MESSAGE_FONT_SIZE = 19;
static int FIELDS_FONT_SIZE = 24;
static int BUTTON_FONT_SIZE = 25;
static int TERMS_FONT_SIZE = 17;
static int TROUBLE_FONT_SIZE = 14;
static COLORREF COLOR_GREY = RGB(120, 120, 120);
static COLORREF COLOR_BLACK= RGB(0, 0, 0);
static COLORREF COLOR_WHITE = RGB(255, 255, 255);
static COLORREF COLOR_LIGHTER_GREY = RGB(230, 230, 230);
static COLORREF COLOR_LIGHT_GREY = RGB(200, 200, 200);
static COLORREF COLOR_BLUE = RGB(50, 160, 200);
static CString GRAPHIK_REGULAR = _T("Graphik-Regular");
static CString GRAPHIK_SEMIBOLD = _T("Graphik-Semibold");
static CString TROUBLE_URL = _T("https://www.highfidelity.com/hq-support");
CLauncherDlg::CLauncherDlg(CWnd* pParent)
: CDialog(IDD_LAUNCHER_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
EnableD2DSupport();
}
CLauncherDlg::~CLauncherDlg() {
theApp._manager.closeLog();
}
void CLauncherDlg::DoDataExchange(CDataExchange* pDX)
{
DDX_Control(pDX, IDC_BUTTON_NEXT, m_btnNext);
DDX_Control(pDX, IDC_TROUBLE_LINK, m_trouble_link);
DDX_Control(pDX, IDC_ORGNAME, m_orgname);
DDX_Control(pDX, IDC_USERNAME, m_username);
DDX_Control(pDX, IDC_PASSWORD, m_password);
CDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CLauncherDlg, CDialog)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_TIMER()
ON_EN_SETFOCUS(IDC_ORGNAME, &CLauncherDlg::OnOrgEditChangeFocus)
ON_EN_SETFOCUS(IDC_USERNAME, &CLauncherDlg::OnUserEditChangeFocus)
ON_EN_SETFOCUS(IDC_PASSWORD, &CLauncherDlg::OnPassEditChangeFocus)
ON_BN_CLICKED(IDC_BUTTON_NEXT, &CLauncherDlg::OnNextClicked)
ON_BN_CLICKED(IDC_TROUBLE_LINK, &CLauncherDlg::OnTroubleClicked)
ON_WM_CTLCOLOR()
ON_WM_DRAWITEM()
ON_WM_SETCURSOR()
END_MESSAGE_MAP()
// CLauncherDlg message handlers
BOOL CLauncherDlg::OnInitDialog() {
CDialog::OnInitDialog();
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
CFont editFont;
if (LauncherUtils::getFont(GRAPHIK_REGULAR, FIELDS_FONT_SIZE, true, editFont)) {
m_orgname.SetFont(&editFont);
m_username.SetFont(&editFont);
m_password.SetFont(&editFont);
}
CFont buttonFont;
if (LauncherUtils::getFont(_T("Graphik-Bold"), BUTTON_FONT_SIZE, true, buttonFont)) {
m_btnNext.SetFont(&editFont);
}
m_message_label = (CStatic *)GetDlgItem(IDC_MESSAGE_LABEL);
m_action_label = (CStatic *)GetDlgItem(IDC_ACTION_LABEL);
m_message2_label = (CStatic *)GetDlgItem(IDC_MESSAGE2_LABEL);
m_action2_label = (CStatic *)GetDlgItem(IDC_ACTION2_LABEL);
m_orgname_banner = (CStatic *)GetDlgItem(IDC_ORGNAME_BANNER);
m_username_banner = (CStatic *)GetDlgItem(IDC_USERNAME_BANNER);
m_password_banner = (CStatic *)GetDlgItem(IDC_PASSWORD_BANNER);
m_terms = (CStatic *)GetDlgItem(IDC_TERMS);
m_terms2 = (CStatic *)GetDlgItem(IDC_TERMS2);
m_trouble = (CStatic *)GetDlgItem(IDC_TROUBLE);
m_voxel = (CStatic *)GetDlgItem(IDC_VOXEL);
m_voxel->EnableD2DSupport();
m_pRenderTarget = GetRenderTarget();
SetTimer(1, 2, NULL);
return TRUE;
}
BOOL CLauncherDlg::PreTranslateMessage(MSG* pMsg) {
if ((pMsg->message == WM_KEYDOWN))
{
if (pMsg->wParam == VK_RETURN)
{
OnNextClicked();
return TRUE;
}
}
return CDialog::PreTranslateMessage(pMsg);
}
void CLauncherDlg::setCustomDialog() {
LONG lStyle = GetWindowLong(GetSafeHwnd(), GWL_STYLE);
lStyle &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU);
SetWindowLong(GetSafeHwnd(), GWL_STYLE, lStyle);
LONG lExStyle = GetWindowLong(GetSafeHwnd(), GWL_EXSTYLE);
lExStyle &= ~(WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE);
SetWindowLong(GetSafeHwnd(), GWL_EXSTYLE, lExStyle);
SetWindowPos(NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
// theApp.setDialogOnFront();
}
void CLauncherDlg::OnPaint()
{
CPaintDC dc(this);
setCustomDialog();
CDialog::OnPaint();
}
// The system calls this function to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CLauncherDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void CLauncherDlg::startProcess() {
if (theApp._manager.needsUpdate()) {
theApp._manager.addToLog(_T("Starting Process Update"));
setDrawDialog(DrawStep::DrawProcessUpdate);
} else {
theApp._manager.addToLog(_T("Starting Process Setup"));
setDrawDialog(DrawStep::DrawProcessSetup);
}
theApp._manager.addToLog(_T("Deleting directories before install"));
CString installDir;
theApp._manager.getAndCreatePaths(LauncherManager::PathType::Interface_Directory, installDir);
CString downloadDir;
theApp._manager.getAndCreatePaths(LauncherManager::PathType::Download_Directory, downloadDir);
LauncherUtils::deleteDirectoriesOnThread(installDir, downloadDir, [&](int error) {
LauncherUtils::DeleteDirError deleteError = (LauncherUtils::DeleteDirError)error;
switch(error) {
case LauncherUtils::DeleteDirError::NoErrorDeleting:
theApp._manager.addToLog(_T("Install directory deleted."));
theApp._manager.addToLog(_T("Downloads directory deleted."));
if (!theApp._manager.isLoggedIn()) {
theApp._manager.addToLog(_T("Downloading Content"));
theApp._manager.downloadContent();
} else {
theApp._manager.addToLog(_T("Downloading App"));
theApp._manager.downloadApplication();
}
break;
case LauncherUtils::DeleteDirError::ErrorDeletingBothDirs:
theApp._manager.addToLog(_T("Error deleting directories."));
break;
case LauncherUtils::DeleteDirError::ErrorDeletingApplicationDir:
theApp._manager.addToLog(_T("Error deleting application directory."));
break;
case LauncherUtils::DeleteDirError::ErrorDeletingDownloadsDir:
theApp._manager.addToLog(_T("Error deleting downloads directory."));
break;
default:
break;
}
});
}
BOOL CLauncherDlg::getHQInfo(const CString& orgname) {
CString hash;
CString lowerOrgName = orgname;
lowerOrgName.MakeLower();
LauncherUtils::hMac256(lowerOrgName, LAUNCHER_HMAC_SECRET, hash);
return theApp._manager.readOrganizationJSON(hash) == LauncherUtils::ResponseError::NoError;
}
afx_msg void CLauncherDlg::OnTroubleClicked() {
ShellExecute(0, NULL, TROUBLE_URL, NULL, NULL, SW_SHOWDEFAULT);
}
afx_msg void CLauncherDlg::OnNextClicked() {
if (_drawStep != DrawStep::DrawChoose) {
CString token;
CString username, password, orgname;
m_orgname.GetWindowTextW(orgname);
m_username.GetWindowTextW(username);
m_password.GetWindowTextW(password);
username = LauncherUtils::urlEncodeString(username);
password = LauncherUtils::urlEncodeString(password);
LauncherUtils::ResponseError error;
if (orgname.GetLength() > 0 && username.GetLength() > 0 && password.GetLength() > 0) {
theApp._manager.addToLog(_T("Trying to get organization data"));
if (getHQInfo(orgname)) {
theApp._manager.addToLog(_T("Organization data received."));
theApp._manager.addToLog(_T("Trying to log in with credentials"));
error = theApp._manager.getAccessTokenForCredentials(username, password);
if (error == LauncherUtils::ResponseError::NoError) {
theApp._manager.addToLog(_T("Logged in correctly."));
setDrawDialog(DrawStep::DrawChoose);
} else if (error == LauncherUtils::ResponseError::BadCredentials) {
theApp._manager.addToLog(_T("Bad credentials. Try again"));
setDrawDialog(DrawStep::DrawLoginErrorCred);
} else {
theApp._manager.addToLog(_T("Error Reading or retrieving response."));
MessageBox(L"Error Reading or retrieving response.", L"Network Error", MB_OK | MB_ICONERROR);
}
} else {
theApp._manager.addToLog(_T("Organization name does not exist."));
setDrawDialog(DrawStep::DrawLoginErrorOrg);
}
}
} else {
CString displayName;
m_username.GetWindowTextW(displayName);
theApp._manager.setDisplayName(displayName);
theApp._manager.addToLog(_T("Setting display name: " + displayName));
startProcess();
}
}
void CLauncherDlg::drawBackground(CHwndRenderTarget* pRenderTarget) {
CD2DBitmap m_pBitmamBackground(pRenderTarget, IDB_PNG1, _T("PNG"));
auto size = pRenderTarget->GetSize();
CD2DRectF backRec(0.0f, 0.0f, size.width, size.height);
pRenderTarget->DrawBitmap(&m_pBitmamBackground, backRec);
}
void CLauncherDlg::drawLogo(CHwndRenderTarget* pRenderTarget) {
CD2DBitmap m_pBitmamLogo(pRenderTarget, IDB_PNG2, _T("PNG"));
auto size = pRenderTarget->GetSize();
int logoWidth = 231;
int logoHeight = 181;
float logoPosX = 0.5f * (size.width - logoWidth);
float logoPosY = 0.95f * (size.height - logoHeight);
CD2DRectF logoRec(logoPosX, logoPosY, logoPosX + logoWidth, logoPosY + logoHeight);
pRenderTarget->DrawBitmap(&m_pBitmamLogo, logoRec);
}
void CLauncherDlg::drawSmallLogo(CHwndRenderTarget* pRenderTarget) {
CD2DBitmap m_pBitmamLogo(pRenderTarget, IDB_PNG5, _T("PNG"));
auto size = pRenderTarget->GetSize();
int padding = 6;
int logoWidth = 100;
int logoHeight = 18;
float logoPosX = size.width - logoWidth - padding;
float logoPosY = size.height - logoHeight - padding;
CD2DRectF logoRec(logoPosX, logoPosY, logoPosX + logoWidth, logoPosY + logoHeight);
pRenderTarget->DrawBitmap(&m_pBitmamLogo, logoRec);
}
void CLauncherDlg::drawVoxel(CHwndRenderTarget* pRenderTarget) {
CD2DBitmap m_pBitmamVoxel(pRenderTarget, IDB_PNG4, _T("PNG"));
auto size = pRenderTarget->GetSize();
int logoWidth = 132;
int logoHeight = 134;
float voxelPosX = 0.5f * (size.width - logoWidth);
float voxelPosY = 0.5f * (size.height - logoHeight);
CD2DRectF voxelRec(voxelPosX, voxelPosY, voxelPosX + logoWidth, voxelPosY + logoHeight);
auto midPoint = D2D1::Point2F(0.5f * size.width, 0.5f * size.height);
_logoRotation += 2.0f;
CD2DSolidColorBrush brush(pRenderTarget, D2D1::ColorF(0.0f, 0.0f, 0.0f));
pRenderTarget->SetTransform(D2D1::Matrix3x2F::Rotation(_logoRotation - 2.0f, midPoint));
pRenderTarget->FillRectangle(voxelRec, &brush);
pRenderTarget->SetTransform(D2D1::Matrix3x2F::Rotation(_logoRotation, midPoint));
pRenderTarget->DrawBitmap(&m_pBitmamVoxel, voxelRec);
pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
}
void CLauncherDlg::showWindows(std::vector<CStatic*> windows, bool show) {
for (auto window : windows) {
window->ShowWindow(show ? SW_SHOW : SW_HIDE);
}
}
void CLauncherDlg::prepareLogin(DrawStep step) {
m_voxel->ShowWindow(SW_HIDE);
m_orgname_banner->SetWindowTextW(_T("Organization Name"));
m_username_banner->SetWindowTextW(_T("Username"));
m_password_banner->SetWindowTextW(_T("Password"));
CString editText;
m_orgname.GetWindowTextW(editText);
m_orgname_banner->ShowWindow(editText.GetLength() == 0 ? SW_SHOW : SW_HIDE);
m_username.GetWindowTextW(editText);
m_username_banner->ShowWindow(editText.GetLength() == 0 ? SW_SHOW : SW_HIDE);
m_password.GetWindowTextW(editText);
m_password_banner->ShowWindow(editText.GetLength() == 0 ? SW_SHOW : SW_HIDE);
m_orgname.ShowWindow(SW_SHOW);
m_username.ShowWindow(SW_SHOW);
m_password.ShowWindow(SW_SHOW);
CString actionText = step == DrawStep::DrawLoginLogin ? _T("Please log in") : _T("Uh-oh, we have a problem");
CString messageText = step == DrawStep::DrawLoginLogin ? _T("Be sure you've uploaded your Avatar before signing in.") :
step == DrawStep::DrawLoginErrorCred ? _T("There is a problem with your credentials\n please try again.") : _T("There is a problem with your Organization name\n please try again.");
m_action_label->SetWindowTextW(actionText);
m_message_label->SetWindowTextW(messageText);
m_action_label->ShowWindow(SW_SHOW);
m_message_label->ShowWindow(SW_SHOW);
m_btnNext.ShowWindow(SW_SHOW);
m_trouble->SetWindowTextW(_T("Having Trouble?"));
m_trouble->ShowWindow(SW_SHOW);
m_trouble_link.ShowWindow(SW_SHOW);
}
void CLauncherDlg::prepareChoose() {
m_orgname.ShowWindow(SW_HIDE);
m_username.SetWindowTextW(_T(""));
m_username_banner->SetWindowTextW(_T("Display Name"));
CString editText;
m_username.GetWindowTextW(editText);
m_username_banner->ShowWindow(editText.GetLength() == 0 ? SW_SHOW : SW_HIDE);
m_password.ShowWindow(SW_HIDE);
m_orgname_banner->ShowWindow(SW_HIDE);
m_password_banner->ShowWindow(SW_HIDE);
m_action_label->SetWindowTextW(_T("Choose a display name"));
m_message_label->SetWindowTextW(_T("This is the name that your teammates will see."));
m_terms->ShowWindow(SW_SHOW);
m_terms2->ShowWindow(SW_SHOW);
m_terms->SetWindowTextW(_T("By signing in, you agree to the High Fidelity"));
m_terms2->SetWindowTextW(_T("Terms of Service"));
CRect rec;
m_btnNext.GetWindowRect(&rec);
ScreenToClient(&rec);
if (rec.top > 281) {
rec.bottom -= 35;
rec.top -= 35;
m_btnNext.MoveWindow(rec, FALSE);
}
m_btnNext.ShowWindow(SW_SHOW);
}
void CLauncherDlg::prepareProcess(DrawStep step) {
m_trouble->ShowWindow(SW_HIDE);
m_trouble_link.ShowWindow(SW_HIDE);
m_terms->ShowWindow(SW_HIDE);
m_terms2->ShowWindow(SW_HIDE);
m_orgname_banner->ShowWindow(SW_HIDE);
m_username_banner->ShowWindow(SW_HIDE);
m_password_banner->ShowWindow(SW_HIDE);
m_orgname.ShowWindow(SW_HIDE);
m_username.ShowWindow(SW_HIDE);
m_password.ShowWindow(SW_HIDE);
m_action_label->SetWindowTextW(_T(""));
m_message_label->SetWindowTextW(_T(""));
m_btnNext.ShowWindow(SW_HIDE);
m_action_label->ShowWindow(SW_HIDE);
m_message_label->ShowWindow(SW_HIDE);
m_voxel->ShowWindow(SW_SHOW);
CString actionText = _T("");
CString messageText = _T("");
switch (step) {
case DrawStep::DrawProcessSetup:
actionText = _T("We're building your virtual HQ");
messageText = _T("Set up may take several minutes.");
break;
case DrawStep::DrawProcessUpdate:
actionText = _T("Getting updates...");
messageText = _T("We're getting the latest and greatest for you, one sec.");
break;
case DrawStep::DrawProcessFinishHq:
actionText = _T("Your new HQ is all setup");
messageText = _T("Thanks for being patient.");
break;
case DrawStep::DrawProcessFinishUpdate:
actionText = _T("You're good to go!");
messageText = _T("Thanks for being patient.");
break;
case DrawStep::DrawProcessUninstall:
actionText = _T("Uninstalling...");
messageText = _T("It'll take one sec.");
break;
}
m_action2_label->SetWindowTextW(actionText);
m_message2_label->SetWindowTextW(messageText);
m_action2_label->ShowWindow(SW_SHOW);
m_message2_label->ShowWindow(SW_SHOW);
}
void CLauncherDlg::prepareError() {
}
BOOL CLauncherDlg::getTextFormat(int resID, TextFormat& formatOut) {
// Set default values for message
BOOL isText = TRUE;
formatOut.color = COLOR_LIGHT_GREY;
formatOut.isBold = false;
formatOut.isButton = false;
formatOut.size = MESSAGE_FONT_SIZE;
formatOut.underlined = false;
switch (resID) {
case IDC_VOXEL:
case IDD_LAUNCHER_DIALOG:
isText = FALSE;
case IDC_MESSAGE_LABEL:
case IDC_MESSAGE2_LABEL:
// Default values
break;
case IDC_ACTION_LABEL:
case IDC_ACTION2_LABEL:
formatOut.size = ACTION_FONT_SIZE;
formatOut.isBold = true;
formatOut.color = COLOR_LIGHTER_GREY;
break;
case IDC_USERNAME:
case IDC_PASSWORD:
case IDC_ORGNAME:
formatOut.color = COLOR_WHITE;
formatOut.size = FIELDS_FONT_SIZE;
formatOut.underlined = true;
break;
case IDC_USERNAME_BANNER:
case IDC_PASSWORD_BANNER:
case IDC_ORGNAME_BANNER:
formatOut.size = FIELDS_FONT_SIZE;
formatOut.color = COLOR_GREY;
break;
case IDC_TERMS:
formatOut.size = TERMS_FONT_SIZE;
break;
case IDC_TERMS2:
formatOut.size = TERMS_FONT_SIZE;
formatOut.isBold = true;
break;
case IDC_TROUBLE:
formatOut.size = TROUBLE_FONT_SIZE;
formatOut.color = COLOR_BLUE;
break;
}
return isText;
}
HBRUSH CLauncherDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
TextFormat textFormat;
int resId = pWnd->GetDlgCtrlID();
if (getTextFormat(resId, textFormat)) {
pDC->SetTextColor(textFormat.color);
pDC->SetBkMode(TRANSPARENT);
CFont textFont;
CString fontFamily = textFormat.isBold ? GRAPHIK_SEMIBOLD : GRAPHIK_REGULAR;
if (LauncherUtils::getFont(fontFamily, textFormat.size, textFormat.isBold, textFont)) {
pDC->SelectObject(&textFont);
}
if (textFormat.underlined) {
CRect rect;
pWnd->GetClientRect(&rect);
int borderThick = 1;
int padding = 4;
CRect lineRect = CRect(rect.left + padding, rect.bottom, rect.right - padding, rect.bottom + borderThick);
lineRect.MoveToY(lineRect.bottom + 1);
pDC->FillSolidRect(lineRect, COLOR_GREY);
}
}
return (HBRUSH)GetStockObject(BLACK_BRUSH);
}
void CLauncherDlg::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDC dc;
dc.Attach(lpDrawItemStruct->hDC);
CRect rect = lpDrawItemStruct->rcItem;
CRect defrect = rect;
CString btnName = _T("");
int xpan = 0;
if (nIDCtl == IDC_BUTTON_NEXT) {
if (_drawStep == DrawStep::DrawChoose || _drawStep == DrawStep::DrawLoginLogin) {
btnName += _drawStep == DrawStep::DrawLoginLogin ? _T("NEXT") : _T("LOG IN");
int xpan = -20;
defrect = CRect(rect.left - xpan, rect.top, rect.right + xpan, rect.bottom);
} else {
btnName += _T("TRY AGAIN");
}
int borderThick = 2;
dc.FillSolidRect(rect, COLOR_BLACK);
dc.FillSolidRect(defrect, COLOR_WHITE);
defrect.DeflateRect(borderThick, borderThick, borderThick, borderThick);
dc.FillSolidRect(defrect, COLOR_BLACK);
UINT state = lpDrawItemStruct->itemState;
dc.SetTextColor(COLOR_WHITE);
CFont buttonFont;
if (LauncherUtils::getFont(GRAPHIK_SEMIBOLD, BUTTON_FONT_SIZE, true, buttonFont)) {
dc.SelectObject(buttonFont);
}
dc.DrawText(btnName, CRect(rect.left, rect.top + 4, rect.right, rect.bottom - 8), DT_CENTER | DT_VCENTER | DT_SINGLELINE);
dc.Detach();
} else if (nIDCtl == IDC_TROUBLE_LINK) {
dc.FillSolidRect(rect, COLOR_BLACK);
dc.SetTextColor(COLOR_BLUE);
CFont buttonFont;
if (LauncherUtils::getFont(GRAPHIK_SEMIBOLD, TROUBLE_FONT_SIZE, true, buttonFont)) {
dc.SelectObject(buttonFont);
}
dc.DrawText(_T("Having Trouble"), CRect(rect.left, rect.top, rect.right, rect.bottom), DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}
}
void CLauncherDlg::redrawBanner(const CEdit& edit, CStatic* banner) {
CString editText;
edit.GetWindowTextW(editText);
if (editText.GetLength() == 0) {
banner->Invalidate();
}
}
void CLauncherDlg::OnOrgEditChangeFocus() {
redrawBanner(m_username, m_username_banner);
redrawBanner(m_password, m_password_banner);
}
void CLauncherDlg::OnUserEditChangeFocus() {
redrawBanner(m_orgname, m_orgname_banner);
redrawBanner(m_password, m_password_banner);
}
void CLauncherDlg::OnPassEditChangeFocus() {
redrawBanner(m_orgname, m_orgname_banner);
redrawBanner(m_username, m_username_banner);
}
BOOL CLauncherDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if (pWnd->IsKindOf(RUNTIME_CLASS(CButton))) {
::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_HAND));
return TRUE;
}
return CDialog::OnSetCursor(pWnd, nHitTest, message);
}
void CLauncherDlg::OnTimer(UINT_PTR nIDEvent) {
const int CONSOLE_MAX_SHUTDOWN_TRY_COUNT = 10;
const int CONSOLE_DELTATIME_BETWEEN_TRYS = 10;
if (_drawStep == DrawStep::DrawProcessSetup ||
_drawStep == DrawStep::DrawProcessUpdate ||
_drawStep == DrawStep::DrawProcessUninstall) {
// Refresh
setDrawDialog(_drawStep, true);
}
if (_showSplash) {
if (_splashStep == 0){
if (theApp._manager.needsUninstall()) {
theApp._manager.addToLog(_T("Waiting to uninstall"));
setDrawDialog(DrawStep::DrawProcessUninstall);
} else {
theApp._manager.addToLog(_T("Start splash screen"));
setDrawDialog(DrawStep::DrawLogo);
}
} else if (_splashStep > 100) {
_showSplash = false;
if (theApp._manager.shouldShutDown()) {
if (LauncherUtils::IsProcessRunning(L"interface.exe")) {
exit(0);
}
} else if (theApp._manager.needsUpdate()) {
startProcess();
} else if (theApp._manager.needsUninstall()) {
theApp._manager.uninstallApplication();
exit(0);
} else {
theApp._manager.addToLog(_T("Starting login"));
setDrawDialog(DrawStep::DrawLoginLogin);
}
}
_splashStep++;
} else if (theApp._manager.shouldShutDown()) {
if (LauncherUtils::IsProcessRunning(L"interface.exe")) {
exit(0);
}
}
}
void CLauncherDlg::setDrawDialog(DrawStep step, BOOL isUpdate) {
_drawStep = step;
auto m_pRenderTarget = GetRenderTarget();
auto m_voxelRenderTarget = m_voxel->GetRenderTarget();
switch (_drawStep) {
case DrawStep::DrawLogo:
m_pRenderTarget->BeginDraw();
drawBackground(m_pRenderTarget);
m_pRenderTarget->EndDraw();
m_voxelRenderTarget->BeginDraw();
drawLogo(m_voxelRenderTarget);
m_voxelRenderTarget->EndDraw();
break;
case DrawStep::DrawLoginLogin:
case DrawStep::DrawLoginErrorOrg:
case DrawStep::DrawLoginErrorCred:
prepareLogin(_drawStep);
m_pRenderTarget->BeginDraw();
drawBackground(m_pRenderTarget);
drawSmallLogo(m_pRenderTarget);
m_pRenderTarget->EndDraw();
RedrawWindow();
break;
case DrawStep::DrawChoose:
prepareChoose();
m_pRenderTarget->BeginDraw();
drawBackground(m_pRenderTarget);
drawSmallLogo(m_pRenderTarget);
m_pRenderTarget->EndDraw();
RedrawWindow();
break;
case DrawStep::DrawProcessFinishHq:
case DrawStep::DrawProcessFinishUpdate:
case DrawStep::DrawProcessUpdate:
case DrawStep::DrawProcessUninstall:
case DrawStep::DrawProcessSetup:
if (!isUpdate) {
m_voxelRenderTarget->BeginDraw();
m_voxelRenderTarget->Clear(D2D1::ColorF(0.0f, 0.0f, 0.0f, 1.0f));
m_voxelRenderTarget->EndDraw();
m_pRenderTarget->BeginDraw();
prepareProcess(_drawStep);
drawBackground(m_pRenderTarget);
drawSmallLogo(m_pRenderTarget);
m_pRenderTarget->EndDraw();
RedrawWindow();
}
m_voxelRenderTarget->BeginDraw();
drawVoxel(m_voxelRenderTarget);
m_voxelRenderTarget->EndDraw();
break;
default:
break;
}
}

View file

@ -0,0 +1,121 @@
//
// LauncherDlg.h
//
// Created by Luis Cuenca on 6/5/2019.
// 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
//
#pragma once
// CLauncherDlg dialog
class CLauncherDlg : public CDialog
{
// Construction
public:
enum DrawStep {
DrawLogo = 0,
DrawLoginLogin,
DrawLoginErrorOrg,
DrawLoginErrorCred,
DrawChoose,
DrawProcessSetup,
DrawProcessUpdate,
DrawProcessFinishHq,
DrawProcessFinishUpdate,
DrawProcessUninstall,
DrawError
};
struct TextFormat {
int size;
COLORREF color;
bool isButton;
bool isBold;
bool underlined;
};
CLauncherDlg(CWnd* pParent = nullptr);
~CLauncherDlg();
virtual BOOL PreTranslateMessage(MSG* pMsg);
void setDrawDialog(DrawStep step, BOOL isUpdate = FALSE);
// Dialog Data
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_LAUNCHER_DIALOG };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
void startProcess();
void setCustomDialog();
// Implementation
protected:
BOOL getHQInfo(const CString& orgname);
DrawStep _drawStep { DrawStep::DrawLogo };
BOOL getTextFormat(int ResID, TextFormat& formatOut);
void showWindows(std::vector<CStatic*> windows, bool show);
bool _isConsoleRunning{ false };
bool _isInstalling{ false };
bool _isFirstDraw{ false };
bool _showSplash{ true };
int _splashStep{ 0 };
float _logoRotation { 0.0f };
HICON m_hIcon;
CButton m_btnNext;
CButton m_trouble_link;
CStatic* m_message_label;
CStatic* m_action_label;
CStatic* m_message2_label;
CStatic* m_action2_label;
CStatic* m_terms;
CStatic* m_terms2;
CStatic* m_trouble;
CStatic* m_voxel;
CEdit m_orgname;
CEdit m_username;
CEdit m_password;
CStatic* m_orgname_banner;
CStatic* m_username_banner;
CStatic* m_password_banner;
void drawBackground(CHwndRenderTarget* pRenderTarget);
void drawLogo(CHwndRenderTarget* pRenderTarget);
void drawSmallLogo(CHwndRenderTarget* pRenderTarget);
void drawVoxel(CHwndRenderTarget* pRenderTarget);
void prepareLogin(DrawStep step);
void prepareProcess(DrawStep step);
void prepareChoose();
void prepareError();
void redrawBanner(const CEdit& edit, CStatic* banner);
// Generated message map functions
virtual BOOL OnInitDialog();
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnNextClicked();
afx_msg void OnTroubleClicked();
afx_msg void OnOrgEditChangeFocus();
afx_msg void OnUserEditChangeFocus();
afx_msg void OnPassEditChangeFocus();
afx_msg void OnTimer(UINT_PTR nIDEvent);
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct);
afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
};

View file

@ -0,0 +1,471 @@
//
// LauncherManager.cpp
//
// Created by Luis Cuenca on 6/5/2019.
// 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 "stdafx.h"
#include <time.h>
#include <fstream>
#include "LauncherManager.h"
LauncherManager::LauncherManager()
{
}
LauncherManager::~LauncherManager()
{
}
void LauncherManager::init() {
initLog();
addToLog(_T("Getting most recent build"));
getMostRecentBuild(_latestApplicationURL, _latestVersion);
addToLog(_T("Latest version: ") + _latestVersion);
CString currentVersion;
if (isApplicationInstalled(currentVersion, _domainURL, _contentURL, _loggedIn) && _loggedIn) {
addToLog(_T("Installed version: ") + currentVersion);
if (_latestVersion.Compare(currentVersion) == 0) {
addToLog(_T("Already running most recent build. Launching interface.exe"));
launchApplication();
_shouldShutdown = TRUE;
} else {
addToLog(_T("New build found. Updating"));
_shouldUpdate = TRUE;
}
}
}
BOOL LauncherManager::initLog() {
CString logPath;
auto result = getAndCreatePaths(PathType::Launcher_Directory, logPath);
if (result) {
logPath += _T("log.txt");
return result = _logFile.Open(logPath, CFile::modeCreate | CFile::modeReadWrite);
}
return FALSE;
}
BOOL LauncherManager::addToLog(const CString& line) {
if (_logFile.m_hFile != CStdioFile::hFileNull) {
char buff[100];
time_t now = time(0);
tm ltm;
localtime_s(&ltm, &now);
strftime(buff, 100, "%Y-%m-%d %H:%M:%S", &ltm);
CString timeStr = CString(buff);
_logFile.WriteString(timeStr + _T(" ") + line + _T("\n"));
return TRUE;
}
return FALSE;
}
void LauncherManager::closeLog() {
if (_logFile.m_hFile != CStdioFile::hFileNull) {
_logFile.Close();
}
}
BOOL LauncherManager::installLauncher() {
addToLog(_T("Installing Launcher."));
CString appPath;
BOOL result = getAndCreatePaths(PathType::Running_Path, appPath);
if (!result) {
MessageBox(NULL, L"Error getting app directory", L"Path Error", MB_OK | MB_ICONERROR);
}
CString installDirectory;
CString appDirectory = appPath.Left(appPath.ReverseFind('\\') + 1);
result = getAndCreatePaths(PathType::Launcher_Directory, installDirectory);
if (!result) {
MessageBox(NULL, L"Error getting app desired directory", L"Path Error", MB_OK | MB_ICONERROR);
}
CString instalationPath = installDirectory + LAUNCHER_EXE_FILENAME;
if (!installDirectory.Compare(appDirectory) == 0) {
if (!_shouldUninstall) {
// The installer is not running on the desired location and has to be installed
// Kill of running before self-copy
if (LauncherUtils::IsProcessRunning(LAUNCHER_EXE_FILENAME)) {
::ShellExecute(NULL, NULL, L"taskkill", L"/F /T /IM " + LAUNCHER_EXE_FILENAME, NULL, SW_HIDE);
}
CopyFile(appPath, instalationPath, FALSE);
}
} else if (_shouldUninstall) {
addToLog(_T("Launching uninstall mode."));
CString tempPath;
if (getAndCreatePaths(PathType::Temp_Directory, tempPath)) {
tempPath += _T("\\HQ_uninstaller_tmp.exe");
CopyFile(instalationPath, tempPath, false);
LauncherUtils::launchApplication(tempPath, _T(" --uninstall"));
exit(0);
}
}
return TRUE;
}
BOOL LauncherManager::createShortcuts() {
CString desktopLnkPath;
addToLog(_T("Creating shortcuts."));
getAndCreatePaths(PathType::Desktop_Directory, desktopLnkPath);
desktopLnkPath += _T("\\HQ Launcher.lnk");
CString installDir;
getAndCreatePaths(PathType::Launcher_Directory, installDir);
CString installPath = installDir + LAUNCHER_EXE_FILENAME;
if (!LauncherUtils::CreateLink(installPath, (LPCSTR)CStringA(desktopLnkPath), _T("CLick to Setup and Launch HQ."))) {
return FALSE;
}
CString startLinkPath;
getAndCreatePaths(PathType::StartMenu_Directory, startLinkPath);
CString appStartLinkPath = startLinkPath + _T("HQ Launcher.lnk");
CString uniStartLinkPath = startLinkPath + _T("Uninstall HQ.lnk");
CString uniLinkPath = installDir + _T("Uninstall HQ.lnk");
if (!LauncherUtils::CreateLink(installPath, (LPCSTR)CStringA(appStartLinkPath), _T("CLick to Setup and Launch HQ."))) {
return FALSE;
}
if (!LauncherUtils::CreateLink(installPath, (LPCSTR)CStringA(uniStartLinkPath), _T("CLick to Uninstall HQ."), _T("--uninstall"))) {
return FALSE;
}
if (!LauncherUtils::CreateLink(installPath, (LPCSTR)CStringA(uniLinkPath), _T("CLick to Uninstall HQ."), _T("--uninstall"))) {
return FALSE;
}
return TRUE;
}
BOOL LauncherManager::deleteShortcuts() {
CString desktopLnkPath;
addToLog(_T("Deleting shortcuts."));
getAndCreatePaths(PathType::Desktop_Directory, desktopLnkPath);
desktopLnkPath += _T("\\HQ Launcher.lnk");
BOOL success = LauncherUtils::deleteFileOrDirectory(desktopLnkPath);
CString startLinkPath;
getAndCreatePaths(PathType::StartMenu_Directory, startLinkPath);
return success && LauncherUtils::deleteFileOrDirectory(startLinkPath);
}
BOOL LauncherManager::isApplicationInstalled(CString& version, CString& domain,
CString& content, bool& loggedIn) {
CString applicationDir;
getAndCreatePaths(PathType::Launcher_Directory, applicationDir);
CString applicationPath = applicationDir + "interface\\interface.exe";
BOOL isApplicationInstalled = PathFileExistsW(applicationPath);
BOOL configFileExist = PathFileExistsW(applicationDir + _T("interface\\config.json"));
if (isApplicationInstalled && configFileExist) {
LauncherUtils::ResponseError status = readConfigJSON(version, domain, content, loggedIn);
return status == LauncherUtils::ResponseError::NoError;
}
return FALSE;
}
BOOL LauncherManager::getAndCreatePaths(PathType type, CString& outPath) {
if (type == PathType::Running_Path) {
char appPath[MAX_PATH];
DWORD size = GetModuleFileNameA(NULL, appPath, MAX_PATH);
if (size) {
outPath = CString(appPath);
return TRUE;
}
} else if (type == PathType::Desktop_Directory) {
TCHAR desktopPath[MAX_PATH];
auto success = SUCCEEDED(SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, 0, desktopPath));
outPath = CString(desktopPath);
return success;
} else if (type == PathType::Temp_Directory) {
TCHAR tempPath[MAX_PATH];
auto success = GetTempPath(MAX_PATH, tempPath);
outPath = CString(tempPath);
return success;
} else {
TCHAR localDataPath[MAX_PATH];
if (type == PathType::StartMenu_Directory) {
TCHAR startMenuPath[MAX_PATH];
auto success = SUCCEEDED(SHGetFolderPath(NULL, CSIDL_STARTMENU, NULL, 0, startMenuPath));
outPath = CString(startMenuPath) + _T("\\Programs\\HQ\\");
success = SHCreateDirectoryEx(NULL, outPath, NULL) || ERROR_ALREADY_EXISTS == GetLastError();
return success;
} else if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, localDataPath))) {
_tcscat_s(localDataPath, _T("\\") + DIRECTORY_NAME_APP + _T("\\"));
outPath = CString(localDataPath);
if (type == PathType::Download_Directory) {
outPath += DIRECTORY_NAME_DOWNLOADS + _T("\\");
} else if (type == PathType::Interface_Directory) {
outPath += DIRECTORY_NAME_INTERFACE;
} else if (type == PathType::Content_Directory) {
outPath += DIRECTORY_NAME_CONTENT;
}
return (CreateDirectory(outPath, NULL) || ERROR_ALREADY_EXISTS == GetLastError());
}
}
return FALSE;
}
BOOL LauncherManager::getInstalledVersion(const CString& path, CString& version) {
CStdioFile cfile;
BOOL success = cfile.Open(path, CFile::modeRead);
if (success) {
cfile.ReadString(version);
cfile.Close();
}
return success;
}
BOOL LauncherManager::launchApplication(const CString& tokensJSON) {
CString installDir;
LauncherManager::getAndCreatePaths(PathType::Interface_Directory, installDir);
CString interfaceExe = installDir + _T("\\interface.exe");
CString params1 = _T("--url \"") + _domainURL + ("\" ");
CString cacheDir;
LauncherManager::getAndCreatePaths(PathType::Content_Directory, cacheDir);
CString params3 = _T("--cache \"") + cacheDir + ("\" ");
CString params4 = !_displayName.IsEmpty() ? _T("--displayName \"") + _displayName + ("\" ") : _T("");
CString parsedTokens = tokensJSON;
parsedTokens.Replace(_T("\""), _T("\\\""));
CString params5 = !tokensJSON.IsEmpty() ? _T("--tokens \"") + parsedTokens + ("\"") : _T("");
CString params = params1 + params3 + params4 + params5 + EXTRA_PARAMETERS;
auto rs = ShellExecute(NULL, L"open", interfaceExe, params, NULL, SW_SHOW);
return (rs != NULL);
}
BOOL LauncherManager::createConfigJSON() {
CString configPath;
LauncherManager::getAndCreatePaths(PathType::Interface_Directory, configPath);
configPath += "\\config.json";
std::ofstream configFile(configPath, std::ofstream::binary);
if (configFile.fail()) {
return FALSE;
}
Json::Value config;
CString applicationPath;
LauncherManager::getAndCreatePaths(PathType::Launcher_Directory, applicationPath);
applicationPath += LAUNCHER_EXE_FILENAME;
config["loggedIn"] = _loggedIn;
config["launcherPath"] = LauncherUtils::cStringToStd(applicationPath);
config["version"] = LauncherUtils::cStringToStd(_latestVersion);
config["domain"] = LauncherUtils::cStringToStd(_domainURL);
CString content;
getAndCreatePaths(PathType::Content_Directory, content);
config["content"] = LauncherUtils::cStringToStd(content);
configFile << config;
configFile.close();
return TRUE;
}
LauncherUtils::ResponseError LauncherManager::readConfigJSON(CString& version, CString& domain, CString& content, bool& loggedIn) {
CString configPath;
getAndCreatePaths(PathType::Interface_Directory, configPath);
configPath += "\\config.json";
std::ifstream configFile(configPath, std::ifstream::binary);
if (configFile.fail()) {
return LauncherUtils::ResponseError::Open;
}
Json::Value config;
configFile >> config;
if (config["version"].isString() && config["domain"].isString() &&
config["content"].isString()) {
loggedIn = config["loggedIn"].asBool();
version = config["version"].asCString();
domain = config["domain"].asCString();
content = config["content"].asCString();
configFile.close();
return LauncherUtils::ResponseError::NoError;
}
configFile.close();
return LauncherUtils::ResponseError::ParsingJSON;
}
LauncherUtils::ResponseError LauncherManager::readOrganizationJSON(const CString& hash) {
CString contentTypeJson = L"content-type:application/json";
CString response;
CString url = _T("/hifi-public/huffman/organizations/") + hash + _T(".json");
LauncherUtils::ResponseError error = LauncherUtils::makeHTTPCall(L"HQ Launcher", L"s3.amazonaws.com", url,
contentTypeJson, CStringA(), response, false);
if (error != LauncherUtils::ResponseError::NoError) {
return error;
}
Json::Value json;
if (LauncherUtils::parseJSON(response, json)) {
if (json["content_set_url"].isString() && json["domain"].isString()) {
_contentURL = json["content_set_url"].asCString();
_domainURL = json["domain"].asCString();
return LauncherUtils::ResponseError::NoError;
}
}
return LauncherUtils::ResponseError::ParsingJSON;
}
LauncherUtils::ResponseError LauncherManager::getMostRecentBuild(CString& urlOut, CString& versionOut) {
CString contentTypeJson = L"content-type:application/json";
CString response;
LauncherUtils::ResponseError error = LauncherUtils::makeHTTPCall(L"HQ Launcher", L"thunder.highfidelity.com", L"/builds/api/tags/latest?format=json",
contentTypeJson, CStringA(), response, false);
if (error != LauncherUtils::ResponseError::NoError) {
return error;
}
Json::Value json;
if (LauncherUtils::parseJSON(response, json)) {
int count = json["count"].isInt() ? json["count"].asInt() : 0;
if (count > 0 && json["results"].isArray()) {
for (int i = 0; i < count; i++) {
if (json["results"][i].isObject()) {
Json::Value result = json["results"][i];
if (result["latest_version"].isInt()) {
std::string version = std::to_string(result["latest_version"].asInt());
versionOut = CString(version.c_str());
}
if (result["installers"].isObject() &&
result["installers"]["windows"].isObject() &&
result["installers"]["windows"]["zip_url"].isString()) {
urlOut = result["installers"]["windows"]["zip_url"].asCString();
return LauncherUtils::ResponseError::NoError;
}
}
}
}
}
return LauncherUtils::ResponseError::ParsingJSON;
}
LauncherUtils::ResponseError LauncherManager::getAccessTokenForCredentials(const CString& username, const CString& password) {
CStringA post = "grant_type=password&username=";
post += username;
post += "&password=";
post += password;
post += "&scope=owner";
CString contentTypeText = L"content-type:application/x-www-form-urlencoded";
CString response;
LauncherUtils::ResponseError error = LauncherUtils::makeHTTPCall(L"HQ Launcher", L"metaverse.highfidelity.com", L"/oauth/token",
contentTypeText, post, response, true);
if (error != LauncherUtils::ResponseError::NoError) {
return error;
}
Json::Value json;
if (LauncherUtils::parseJSON(response, json)) {
if (json["error"].isString()) {
return LauncherUtils::ResponseError::BadCredentials;
} else if (json["access_token"].isString()) {
_tokensJSON = response;
return LauncherUtils::ResponseError::NoError;
}
}
return LauncherUtils::ResponseError::ParsingJSON;
}
BOOL LauncherManager::createApplicationRegistryKeys(int size) {
const std::string REGISTRY_PATH = "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\HQ";
BOOL success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "DisplayName", "HQ");
CString installDir;
getAndCreatePaths(PathType::Launcher_Directory, installDir);
success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "InstallLocation", LauncherUtils::cStringToStd(installDir));
std::string applicationExe = LauncherUtils::cStringToStd(installDir + LAUNCHER_EXE_FILENAME);
std::string uninstallPath = '"' + applicationExe + '"' + " --uninstall";
success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "UninstallString", uninstallPath);
success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "DisplayVersion", LauncherUtils::cStringToStd(_latestVersion));
success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "DisplayIcon", applicationExe);
success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "Publisher", "High Fidelity");
success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "InstallDate", LauncherUtils::cStringToStd(CTime::GetCurrentTime().Format("%Y%m%d")));
success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "EstimatedSize", (DWORD)size);
success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "NoModify", (DWORD)1);
success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "NoRepair", (DWORD)1);
return success;
}
BOOL LauncherManager::deleteApplicationRegistryKeys() {
const CString REGISTRY_PATH = _T("Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\HQ");
return LauncherUtils::deleteRegistryKey(REGISTRY_PATH);
}
BOOL LauncherManager::uninstallApplication() {
CString installDir;
getAndCreatePaths(PathType::Launcher_Directory, installDir);
BOOL success = LauncherUtils::deleteFileOrDirectory(installDir);
success = success && (deleteShortcuts());
success = success && (deleteApplicationRegistryKeys());
return success;
}
void LauncherManager::onZipExtracted(ZipType type, int size) {
if (type == ZipType::ZipContent) {
addToLog(_T("Downloading application."));
downloadApplication();
} else if (type == ZipType::ZipApplication) {
createShortcuts();
CString versionPath;
getAndCreatePaths(LauncherManager::PathType::Launcher_Directory, versionPath);
addToLog(_T("Creating config.json"));
createConfigJSON();
addToLog(_T("Launching application."));
launchApplication(_tokensJSON);
addToLog(_T("Creating registry keys."));
createApplicationRegistryKeys(size);
_shouldShutdown = TRUE;
}
}
BOOL LauncherManager::extractApplication() {
CString installPath;
getAndCreatePaths(LauncherManager::PathType::Interface_Directory, installPath);
BOOL success = LauncherUtils::unzipFileOnThread(ZipType::ZipApplication, LauncherUtils::cStringToStd(_applicationZipPath),
LauncherUtils::cStringToStd(installPath), [&](int type, int size) {
onZipExtracted((ZipType)type, size);
});
return success;
}
void LauncherManager::onFileDownloaded(DownloadType type) {
if (type == DownloadType::DownloadContent) {
addToLog(_T("Installing content."));
installContent();
} else if (type == DownloadType::DownloadApplication) {
addToLog(_T("Installing application."));
extractApplication();
}
}
BOOL LauncherManager::installContent() {
std::string contentZipFile = LauncherUtils::cStringToStd(_contentZipPath);
CString contentPath;
getAndCreatePaths(LauncherManager::PathType::Content_Directory, contentPath);
BOOL success = LauncherUtils::unzipFileOnThread(ZipType::ZipContent, contentZipFile,
LauncherUtils::cStringToStd(contentPath), [&](int type, int size) {
onZipExtracted((ZipType)type, size);
});
return success;
}
BOOL LauncherManager::downloadFile(DownloadType type, const CString& url, CString& outPath) {
CString fileName = url.Mid(url.ReverseFind('/') + 1);
CString downloadDirectory;
BOOL success = getAndCreatePaths(LauncherManager::PathType::Download_Directory, downloadDirectory);
outPath = downloadDirectory + fileName;
if (success) {
if (!LauncherUtils::downloadFileOnThread(type, url, outPath, [&](int type) {
onFileDownloaded((DownloadType)type);
})) {
success = FALSE;
}
}
return success;
}
BOOL LauncherManager::downloadContent() {
addToLog(_T("Downloading content."));
CString contentURL = getContentURL();
return downloadFile(DownloadType::DownloadContent, contentURL, _contentZipPath);
}
BOOL LauncherManager::downloadApplication() {
CString applicationURL = getLatestInterfaceURL();
return downloadFile(DownloadType::DownloadApplication, applicationURL, _applicationZipPath);
}

View file

@ -0,0 +1,113 @@
//
// LauncherManager.h
//
// Created by Luis Cuenca on 6/5/2019.
// 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
//
#pragma once
#include "LauncherUtils.h"
const CString DIRECTORY_NAME_APP = _T("HQ");
const CString DIRECTORY_NAME_DOWNLOADS = _T("downloads");
const CString DIRECTORY_NAME_INTERFACE = _T("interface");
const CString DIRECTORY_NAME_CONTENT = _T("content");
const CString EXTRA_PARAMETERS = _T(" --suppress-settings-reset --no-launcher --no-updater");
const CString LAUNCHER_EXE_FILENAME = _T("HQ Launcher.exe");
const bool INSTALL_ZIP = true;
class LauncherManager
{
public:
enum PathType {
Running_Path = 0,
Launcher_Directory,
Download_Directory,
Interface_Directory,
Desktop_Directory,
Content_Directory,
StartMenu_Directory,
Temp_Directory
};
enum ZipType {
ZipContent = 0,
ZipApplication
};
enum DownloadType {
DownloadContent = 0,
DownloadApplication
};
enum ErrorType {
ErrorNetworkAuth,
ErrorNetworkUpdate,
ErrorNetworkHq,
ErrorDownloading,
ErrorUpdating,
ErrorInstall,
ErrorIOFiles
};
LauncherManager();
~LauncherManager();
void init();
BOOL initLog();
BOOL addToLog(const CString& line);
void closeLog();
BOOL getAndCreatePaths(PathType type, CString& outPath);
BOOL getInstalledVersion(const CString& path, CString& version);
BOOL isApplicationInstalled(CString& version, CString& domain,
CString& content, bool& loggedIn);
LauncherUtils::ResponseError getAccessTokenForCredentials(const CString& username, const CString& password);
LauncherUtils::ResponseError getMostRecentBuild(CString& urlOut, CString& versionOut);
LauncherUtils::ResponseError readOrganizationJSON(const CString& hash);
LauncherUtils::ResponseError readConfigJSON(CString& version, CString& domain,
CString& content, bool& loggedIn);
BOOL createConfigJSON();
BOOL createApplicationRegistryKeys(int size);
BOOL deleteApplicationRegistryKeys();
BOOL createShortcuts();
BOOL deleteShortcuts();
BOOL launchApplication(const CString& tokensJSON = _T(""));
BOOL uninstallApplication();
BOOL installLauncher();
// getters
const CString& getContentURL() const { return _contentURL; }
const CString& getdomainURL() const { return _domainURL; }
const CString& getVersion() const { return _version; }
BOOL shouldShutDown() const { return _shouldShutdown; }
BOOL needsUpdate() { return _shouldUpdate; }
BOOL needsUninstall() { return _shouldUninstall; }
void setDisplayName(const CString& displayName) { _displayName = displayName; }
bool isLoggedIn() { return _loggedIn; }
const CString& getLatestInterfaceURL() const { return _latestApplicationURL; }
void uninstall() { _shouldUninstall = true; };
BOOL downloadFile(DownloadType type, const CString& url, CString& localPath);
BOOL downloadContent();
BOOL downloadApplication();
BOOL installContent();
BOOL extractApplication();
void onZipExtracted(ZipType type, int size);
void onFileDownloaded(DownloadType type);
private:
CString _latestApplicationURL;
CString _latestVersion;
CString _contentURL;
CString _domainURL;
CString _version;
CString _displayName;
CString _tokensJSON;
CString _applicationZipPath;
CString _contentZipPath;
bool _loggedIn{ false };
BOOL _shouldUpdate{ FALSE };
BOOL _shouldUninstall{ FALSE };
BOOL _shouldShutdown{ FALSE };
CStdioFile _logFile;
};

Some files were not shown because too many files have changed in this diff Show more