Merge branch 'master' of github.com:highfidelity/hifi into nut

This commit is contained in:
Sam Gateau 2019-06-05 20:15:27 -07:00
commit 49c006e5af
91 changed files with 23384 additions and 97 deletions

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

@ -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

@ -0,0 +1,104 @@
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")
add_executable(${PROJECT_NAME} MACOSX_BUNDLE ${src_files})
set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME ${APP_NAME})
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

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 KiB

Binary file not shown.

After

Width:  |  Height:  |  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

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  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

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  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,95 @@
#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, password];
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];
//NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
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,71 @@
#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];
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,96 @@
#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/";
#define str(s) #s
#define LAUNCHER_HMAC_SECRET_STRING str(LAUNCHER_HMAC_SECRET)
@implementation OrganizationRequest
- (void) confirmOrganization:(NSString*)aOrganization :(NSString*)aUsername {
self.username = aUsername;
const char *cKey = LAUNCHER_HMAC_SECRET_STRING;
const char *cData = [aOrganization 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];
}
NSLog(@"FAAAAILLLLLLLEEEEEEEDDDDDD");
[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,68 @@
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
)
target_compile_definitions(HQLauncher PRIVATE LAUNCHER_HMAC_SECRET=$ENV{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,624 @@
//
// 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();
}
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()) {
setDrawDialog(DrawStep::DrawProcessUpdate);
} else {
setDrawDialog(DrawStep::DrawProcessSetup);
}
CString installDir;
theApp._manager.getAndCreatePaths(LauncherManager::PathType::Interface_Directory, installDir);
CString interfaceExe = installDir += "\\interface.exe";
if (!theApp._manager.isLoggedIn()) {
theApp._manager.downloadContent();
} else {
theApp._manager.downloadApplication();
}
}
#define str(s) #s
#define LAUNCHER_HMAC_SECRET_STRING str(LAUNCHER_HMAC_SECRET)
BOOL CLauncherDlg::getHQInfo(const CString& orgname) {
CString hash;
LauncherUtils::hMac256(orgname, LAUNCHER_HMAC_SECRET_STRING, 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);
LauncherUtils::ResponseError error;
if (orgname.GetLength() > 0 && username.GetLength() > 0 && password.GetLength() > 0) {
if (getHQInfo(orgname)) {
error = theApp._manager.getAccessTokenForCredentials(username, password);
if (error == LauncherUtils::ResponseError::NoError) {
setDrawDialog(DrawStep::DrawChoose);
} else if (error == LauncherUtils::ResponseError::BadCredentials) {
setDrawDialog(DrawStep::DrawLoginErrorCred);
} else {
MessageBox(L"Error Reading or retreaving response.", L"Network Error", MB_OK | MB_ICONERROR);
}
} else {
setDrawDialog(DrawStep::DrawLoginErrorOrg);
}
}
} else {
CString displayName;
m_username.GetWindowTextW(displayName);
theApp._manager.setDisplayName(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()) {
setDrawDialog(DrawStep::DrawProcessUninstall);
} else {
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 {
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,120 @@
//
// 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);
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,422 @@
//
// 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 <fstream>
#include "LauncherManager.h"
LauncherManager::LauncherManager()
{
}
LauncherManager::~LauncherManager()
{
}
void LauncherManager::init() {
getMostRecentBuild(_latestApplicationURL, _latestVersion);
CString currentVersion;
if (isApplicationInstalled(currentVersion, _domainURL, _contentURL, _loggedIn) && _loggedIn) {
if (_latestVersion.Compare(currentVersion) == 0) {
launchApplication();
_shouldShutdown = TRUE;
} else {
_shouldUpdate = TRUE;
}
}
}
BOOL LauncherManager::installLauncher() {
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) {
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;
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;
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) {
downloadApplication();
} else if (type == ZipType::ZipApplication) {
createShortcuts();
CString versionPath;
getAndCreatePaths(LauncherManager::PathType::Launcher_Directory, versionPath);
createConfigJSON();
launchApplication(_tokensJSON);
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) {
installContent();
} else if (type == DownloadType::DownloadApplication) {
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() {
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,109 @@
//
// 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 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 };
};

View file

@ -0,0 +1,405 @@
//
// LauncherUtils.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 <wincrypt.h>
#include <tlhelp32.h>
#include <strsafe.h>
#include <winhttp.h>
#pragma comment(lib, "winhttp")
#include "LauncherUtils.h"
BOOL LauncherUtils::IsProcessRunning(const wchar_t *processName) {
bool exists = false;
PROCESSENTRY32 entry;
entry.dwSize = sizeof(PROCESSENTRY32);
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (Process32First(snapshot, &entry)) {
while (Process32Next(snapshot, &entry)) {
if (!_wcsicmp(entry.szExeFile, processName)) {
exists = true;
break;
}
}
}
CloseHandle(snapshot);
return exists;
}
HRESULT LauncherUtils::CreateLink(LPCWSTR lpszPathObj, LPCSTR lpszPathLink, LPCWSTR lpszDesc, LPCWSTR lpszArgs) {
IShellLink* psl;
// Get a pointer to the IShellLink interface. It is assumed that CoInitialize
// has already been called.
CoInitialize(NULL);
HRESULT hres = E_INVALIDARG;
if ((lpszPathObj != NULL) && (wcslen(lpszPathObj) > 0) &&
(lpszDesc != NULL) &&
(lpszPathLink != NULL) && (strlen(lpszPathLink) > 0)) {
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
if (SUCCEEDED(hres)) {
IPersistFile* ppf;
// Set the path to the shortcut target and add the description.
psl->SetPath(lpszPathObj);
psl->SetDescription(lpszDesc);
psl->SetArguments(lpszArgs);
// Query IShellLink for the IPersistFile interface, used for saving the
// shortcut in persistent storage.
hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
if (SUCCEEDED(hres)) {
WCHAR wsz[MAX_PATH];
// Ensure that the string is Unicode.
MultiByteToWideChar(CP_ACP, 0, lpszPathLink, -1, wsz, MAX_PATH);
// Add code here to check return value from MultiByteWideChar
// for success.
// Save the link by calling IPersistFile::Save.
hres = ppf->Save(wsz, TRUE);
ppf->Release();
}
psl->Release();
}
}
CoUninitialize();
return SUCCEEDED(hres);
}
std::string LauncherUtils::cStringToStd(CString cstring) {
CT2CA convertedString(cstring);
std::string strStd(convertedString);
return strStd;
}
BOOL LauncherUtils::launchApplication(LPCWSTR lpApplicationName, LPTSTR cmdArgs) {
// additional information
STARTUPINFO si;
PROCESS_INFORMATION pi;
// set the size of the structures
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// start the program up
BOOL success = CreateProcess(
lpApplicationName, // the path
cmdArgs, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
CREATE_NEW_CONSOLE, // Opens file in a separate console
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi // Pointer to PROCESS_INFORMATION structure
);
// Close process and thread handles.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return success;
}
BOOL LauncherUtils::deleteRegistryKey(const CString& registryPath) {
TCHAR szDelKey[MAX_PATH * 2];
StringCchCopy(szDelKey, MAX_PATH * 2, registryPath);
auto status = RegDeleteKey(HKEY_CURRENT_USER, szDelKey);
if (status == ERROR_SUCCESS) {
return TRUE;
}
return FALSE;
}
LauncherUtils::ResponseError LauncherUtils::makeHTTPCall(const CString& callerName,
const CString& mainUrl, const CString& dirUrl,
const CString& contentType, CStringA& postData,
CString& response, bool isPost = false) {
HINTERNET hopen = WinHttpOpen(callerName, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (!hopen) {
return ResponseError::Open;
}
HINTERNET hconnect = WinHttpConnect(hopen, mainUrl, INTERNET_DEFAULT_HTTPS_PORT, 0);
if (!hconnect) {
return ResponseError::Connect;
}
HINTERNET hrequest = WinHttpOpenRequest(hconnect, isPost ? L"POST" : L"GET", dirUrl,
NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
if (!hrequest) {
return ResponseError::OpenRequest;
}
if (isPost) {
if (!WinHttpSendRequest(hrequest, contentType, -1,
postData.GetBuffer(postData.GetLength()), (DWORD)strlen(postData), (DWORD)strlen(postData), NULL)) {
return ResponseError::SendRequest;
}
} else {
if (!WinHttpSendRequest(hrequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0)) {
return ResponseError::SendRequest;
}
}
if (!WinHttpReceiveResponse(hrequest, 0)) {
return ResponseError::ReceiveRequest;
}
// query remote file size, set haveContentLength on success and dwContentLength to the length
wchar_t szContentLength[32];
DWORD bufferBytes = 4096;
DWORD dwHeaderIndex = WINHTTP_NO_HEADER_INDEX;
BOOL haveContentLength = WinHttpQueryHeaders(hrequest, WINHTTP_QUERY_CONTENT_LENGTH, NULL,
&szContentLength, &bufferBytes, &dwHeaderIndex);
DWORD dwContentLength;
if (haveContentLength) {
dwContentLength = _wtoi(szContentLength);
}
byte p[4096];
if (!WinHttpQueryDataAvailable(hrequest, &bufferBytes)) {
return ResponseError::ReadResponse;
}
WinHttpReadData(hrequest, p, bufferBytes, &bufferBytes);
response = CString((const char*)p, (int)bufferBytes);
return ResponseError::NoError;
}
BOOL LauncherUtils::parseJSON(const CString& jsonTxt, Json::Value& root) {
Json::CharReaderBuilder CharBuilder;
std::string jsonString = cStringToStd(jsonTxt);
std::string errs;
Json::CharReader* nreader = CharBuilder.newCharReader();
bool parsingSuccessful = false;
if (nreader != NULL) {
parsingSuccessful = nreader->parse(jsonString.c_str(), jsonString.c_str() + jsonString.length(), &root, &errs);
delete nreader;
}
return parsingSuccessful;
}
BOOL LauncherUtils::getFont(const CString& fontName, int fontSize, bool isBold, CFont& fontOut) {
LOGFONT lf;
memset(&lf, 0, sizeof(lf));
lf.lfHeight = fontSize;
lf.lfWeight = isBold ? FW_BOLD : FW_NORMAL;
lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;
lf.lfQuality = ANTIALIASED_QUALITY;
wcscpy_s(lf.lfFaceName, fontName);
if (!fontOut.CreateFontIndirect(&lf)) {
return FALSE;
}
return TRUE;
}
uint64_t LauncherUtils::extractZip(const std::string& zipFile, const std::string& path, std::vector<std::string>& files) {
mz_zip_archive zip_archive;
memset(&zip_archive, 0, sizeof(zip_archive));
auto status = mz_zip_reader_init_file(&zip_archive, zipFile.c_str(), 0);
if (!status) return 0;
int fileCount = (int)mz_zip_reader_get_num_files(&zip_archive);
if (fileCount == 0) {
mz_zip_reader_end(&zip_archive);
return 0;
}
mz_zip_archive_file_stat file_stat;
if (!mz_zip_reader_file_stat(&zip_archive, 0, &file_stat)) {
mz_zip_reader_end(&zip_archive);
return 0;
}
// Get root folder
CString lastDir = _T("");
uint64_t totalSize = 0;
for (int i = 0; i < fileCount; i++) {
if (!mz_zip_reader_file_stat(&zip_archive, i, &file_stat)) continue;
std::string filename = file_stat.m_filename;
std::replace(filename.begin(), filename.end(), '/', '\\');
CString fullFilename = CString(path.c_str()) + "\\" + CString(filename.c_str());
if (mz_zip_reader_is_file_a_directory(&zip_archive, i)) {
if (SHCreateDirectoryEx(NULL, fullFilename, NULL) || ERROR_ALREADY_EXISTS == GetLastError()) {
break;
} else {
continue;
}
}
CT2A destFile(fullFilename);
if (mz_zip_reader_extract_to_file(&zip_archive, i, destFile, 0)) {
totalSize += (uint64_t)file_stat.m_uncomp_size;
files.emplace_back(destFile);
}
}
// Close the archive, freeing any resources it was using
mz_zip_reader_end(&zip_archive);
return totalSize;
}
BOOL LauncherUtils::insertRegistryKey(const std::string& regPath, const std::string& name, const std::string& value) {
HKEY key;
auto status = RegCreateKeyExA(HKEY_CURRENT_USER, regPath.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE | KEY_QUERY_VALUE, NULL, &key, NULL);
if (status == ERROR_SUCCESS) {
status = RegSetValueExA(key, name.c_str(), 0, REG_SZ, (const BYTE*)value.c_str(), (DWORD)(value.size() + 1));
return status == ERROR_SUCCESS;
}
RegCloseKey(key);
return FALSE;
}
BOOL LauncherUtils::insertRegistryKey(const std::string& regPath, const std::string& name, DWORD value) {
HKEY key;
auto status = RegCreateKeyExA(HKEY_CURRENT_USER, regPath.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE | KEY_QUERY_VALUE, NULL, &key, NULL);
if (status == ERROR_SUCCESS) {
status = RegSetValueExA(key, name.c_str(), 0, REG_DWORD, (const BYTE*)&value, sizeof(value));
return TRUE;
}
RegCloseKey(key);
return FALSE;
}
BOOL LauncherUtils::deleteFileOrDirectory(const CString& dirPath, bool noRecycleBin) {
CString dir = dirPath;
// Add extra null to string
dir.AppendChar(0);
SHFILEOPSTRUCT fileop;
fileop.hwnd = NULL; // no status display
fileop.wFunc = FO_DELETE; // delete operation
fileop.pFrom = dir; // source file name as double null terminated string
fileop.pTo = NULL; // no destination needed
fileop.fFlags = FOF_NOCONFIRMATION | FOF_SILENT; // do not prompt the user
if (!noRecycleBin) {
fileop.fFlags |= FOF_ALLOWUNDO;
}
fileop.fAnyOperationsAborted = FALSE;
fileop.lpszProgressTitle = NULL;
fileop.hNameMappings = NULL;
int ret = SHFileOperation(&fileop);
return (ret == 0);
}
BOOL LauncherUtils::hMac256(const CString& cmessage, const char* keystr, CString& hashOut) {
char message[256];
strcpy_s(message, CStringA(cmessage).GetString());
char key[256];
strcpy_s(key, keystr);
HCRYPTPROV hProv = 0;
HCRYPTHASH hHash = 0;
HCRYPTKEY hKey = 0;
HCRYPTHASH hHmacHash = 0;
BYTE pbHash[32];
HMAC_INFO HmacInfo;
int err = 0;
typedef struct blob {
BLOBHEADER header;
DWORD len;
BYTE key[1];
} m_blob;
ZeroMemory(&HmacInfo, sizeof(HmacInfo));
HmacInfo.HashAlgid = CALG_SHA_256;
ZeroMemory(&pbHash, 32);
m_blob* kb = NULL;
DWORD kbSize = DWORD(sizeof(m_blob) + strlen(key));
kb = (m_blob*)malloc(kbSize);
kb->header.bType = PLAINTEXTKEYBLOB;
kb->header.bVersion = CUR_BLOB_VERSION;
kb->header.reserved = 0;
kb->header.aiKeyAlg = CALG_RC2;
memcpy(&kb->key, key, strlen(key));
kb->len = (DWORD)strlen(key);
BOOL success = false;
DWORD datalen = (DWORD)32;
if (CryptAcquireContext(&hProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_NEWKEYSET)
&& CryptImportKey(hProv, (BYTE*)kb, kbSize, 0, CRYPT_IPSEC_HMAC_KEY, &hKey)
&& CryptCreateHash(hProv, CALG_HMAC, hKey, 0, &hHmacHash)
&& CryptSetHashParam(hHmacHash, HP_HMAC_INFO, (BYTE*)&HmacInfo, 0)
&& CryptHashData(hHmacHash, (BYTE*)message, (DWORD)strlen(message), 0)
&& CryptGetHashParam(hHmacHash, HP_HASHVAL, pbHash, &datalen, 0)) {
char *Hex = "0123456789abcdef";
char HashString[65] = { 0 };
for (int Count = 0; Count < 32; Count++)
{
HashString[Count * 2] = Hex[pbHash[Count] >> 4];
HashString[(Count * 2) + 1] = Hex[pbHash[Count] & 0xF];
}
hashOut = CString(HashString);
success = true;
}
free(kb);
if (hHmacHash)
CryptDestroyHash(hHmacHash);
if (hKey)
CryptDestroyKey(hKey);
if (hHash)
CryptDestroyHash(hHash);
if (hProv)
CryptReleaseContext(hProv, 0);
return success;
}
DWORD WINAPI LauncherUtils::unzipThread(LPVOID lpParameter) {
UnzipThreadData& data = *((UnzipThreadData*)lpParameter);
uint64_t size = LauncherUtils::extractZip(data._zipFile, data._path, std::vector<std::string>());
int mb_size = (int)(size * 0.001f);
data.callback(data._type, mb_size);
delete &data;
return 0;
}
DWORD WINAPI LauncherUtils::downloadThread(LPVOID lpParameter)
{
DownloadThreadData& data = *((DownloadThreadData*)lpParameter);
auto hr = URLDownloadToFile(0, data._url, data._file, 0, NULL);
data.callback(data._type);
return 0;
}
BOOL LauncherUtils::unzipFileOnThread(int type, const std::string& zipFile, const std::string& path, std::function<void(int, int)> callback) {
DWORD myThreadID;
UnzipThreadData* unzipThreadData = new UnzipThreadData();
unzipThreadData->_type = type;
unzipThreadData->_zipFile = zipFile;
unzipThreadData->_path = path;
unzipThreadData->setCallback(callback);
HANDLE myHandle = CreateThread(0, 0, unzipThread, unzipThreadData, 0, &myThreadID);
if (myHandle) {
CloseHandle(myHandle);
return TRUE;
}
return FALSE;
}
BOOL LauncherUtils::downloadFileOnThread(int type, const CString& url, const CString& file, std::function<void(int)> callback) {
DWORD myThreadID;
DownloadThreadData* downloadThreadData = new DownloadThreadData();
downloadThreadData->_type = type;
downloadThreadData->_url = url;
downloadThreadData->_file = file;
downloadThreadData->setCallback(callback);
HANDLE myHandle = CreateThread(0, 0, downloadThread, downloadThreadData, 0, &myThreadID);
if (myHandle) {
CloseHandle(myHandle);
return TRUE;
}
return FALSE;
}

View file

@ -0,0 +1,77 @@
//
// LauncherUtils.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 <functional>
#include "libs/json/json.h"
#include "libs/miniz.h"
class LauncherUtils
{
public:
enum ResponseError {
Open = 0,
Connect,
OpenRequest,
SendRequest,
ReceiveRequest,
ReadResponse,
ParsingJSON,
BadCredentials,
NoError
};
struct DownloadThreadData {
int _type;
CString _url;
CString _file;
std::function<void(int)> callback;
// function(type)
void setCallback(std::function<void(int)> fn) {
callback = std::bind(fn, std::placeholders::_1);
}
};
struct UnzipThreadData {
int _type;
std::string _zipFile;
std::string _path;
// function(type, size)
std::function<void(int, int)> callback;
void setCallback(std::function<void(int, int)> fn) {
callback = std::bind(fn, std::placeholders::_1, std::placeholders::_2);
}
};
static BOOL parseJSON(const CString& jsonTxt, Json::Value& jsonObject);
static ResponseError makeHTTPCall(const CString& callerName, const CString& mainUrl,
const CString& dirUrl, const CString& contentType,
CStringA& postData, CString& response, bool isPost);
static std::string cStringToStd(CString cstring);
static BOOL getFont(const CString& fontName, int fontSize, bool isBold, CFont& fontOut);
static BOOL launchApplication(LPCWSTR lpApplicationName, LPTSTR cmdArgs = _T(""));
static BOOL IsProcessRunning(const wchar_t *processName);
static BOOL insertRegistryKey(const std::string& regPath, const std::string& name, const std::string& value);
static BOOL insertRegistryKey(const std::string& regPath, const std::string& name, DWORD value);
static BOOL deleteFileOrDirectory(const CString& dirPath, bool noRecycleBin = true);
static HRESULT CreateLink(LPCWSTR lpszPathObj, LPCSTR lpszPathLink, LPCWSTR lpszDesc, LPCWSTR lpszArgs = _T(""));
static BOOL hMac256(const CString& message, const char* key, CString& hashOut);
static uint64_t extractZip(const std::string& zipFile, const std::string& path, std::vector<std::string>& files);
static BOOL deleteRegistryKey(const CString& registryPath);
static BOOL unzipFileOnThread(int type, const std::string& zipFile, const std::string& path, std::function<void(int, int)> callback);
static BOOL downloadFileOnThread(int type, const CString& url, const CString& file, std::function<void(int)> callback);
private:
// Threads
static DWORD WINAPI unzipThread(LPVOID lpParameter);
static DWORD WINAPI downloadThread(LPVOID lpParameter);
};

View file

@ -0,0 +1,320 @@
/// Json-cpp amalgamated forward header (http://jsoncpp.sourceforge.net/).
/// It is intended to be used with #include "json/json-forwards.h"
/// This header provides forward declaration for all JsonCpp types.
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: LICENSE
// //////////////////////////////////////////////////////////////////////
/*
The JsonCpp library's source code, including accompanying documentation,
tests and demonstration applications, are licensed under the following
conditions...
Baptiste Lepilleur and The JsonCpp Authors explicitly disclaim copyright in all
jurisdictions which recognize such a disclaimer. In such jurisdictions,
this software is released into the Public Domain.
In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur and
The JsonCpp Authors, and is released under the terms of the MIT License (see below).
In jurisdictions which recognize Public Domain property, the user of this
software may choose to accept it either as 1) Public Domain, 2) under the
conditions of the MIT License (see below), or 3) under the terms of dual
Public Domain/MIT License conditions described here, as they choose.
The MIT License is about as close to Public Domain as a license can get, and is
described in clear, concise terms at:
http://en.wikipedia.org/wiki/MIT_License
The full text of the MIT License follows:
========================================================================
Copyright (c) 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
(END LICENSE TEXT)
The MIT license is compatible with both the GPL and commercial
software, affording one all of the rights of Public Domain with the
minor nuisance of being required to keep the above copyright notice
and license text in the source code. Note also that by accepting the
Public Domain "license" you can re-license your copy using whatever
license you like.
*/
// //////////////////////////////////////////////////////////////////////
// End of content of file: LICENSE
// //////////////////////////////////////////////////////////////////////
#ifndef JSON_FORWARD_AMALGAMATED_H_INCLUDED
# define JSON_FORWARD_AMALGAMATED_H_INCLUDED
/// If defined, indicates that the source file is amalgamated
/// to prevent private header inclusion.
#define JSON_IS_AMALGAMATION
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: include/json/config.h
// //////////////////////////////////////////////////////////////////////
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_CONFIG_H_INCLUDED
#define JSON_CONFIG_H_INCLUDED
#include <cstddef>
#include <cstdint>
#include <istream>
#include <memory>
#include <ostream>
#include <sstream>
#include <string>
#include <type_traits>
/// If defined, indicates that json library is embedded in CppTL library.
//# define JSON_IN_CPPTL 1
/// If defined, indicates that json may leverage CppTL library
//# define JSON_USE_CPPTL 1
/// If defined, indicates that cpptl vector based map should be used instead of
/// std::map
/// as Value container.
//# define JSON_USE_CPPTL_SMALLMAP 1
// If non-zero, the library uses exceptions to report bad input instead of C
// assertion macros. The default is to use exceptions.
#ifndef JSON_USE_EXCEPTION
#define JSON_USE_EXCEPTION 1
#endif
/// If defined, indicates that the source file is amalgamated
/// to prevent private header inclusion.
/// Remarks: it is automatically defined in the generated amalgamated header.
// #define JSON_IS_AMALGAMATION
#ifdef JSON_IN_CPPTL
#include <cpptl/config.h>
#ifndef JSON_USE_CPPTL
#define JSON_USE_CPPTL 1
#endif
#endif
#ifdef JSON_IN_CPPTL
#define JSON_API CPPTL_API
#elif defined(JSON_DLL_BUILD)
#if defined(_MSC_VER) || defined(__MINGW32__)
#define JSON_API __declspec(dllexport)
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
#elif defined(__GNUC__) || defined(__clang__)
#define JSON_API __attribute__((visibility("default")))
#endif // if defined(_MSC_VER)
#elif defined(JSON_DLL)
#if defined(_MSC_VER) || defined(__MINGW32__)
#define JSON_API __declspec(dllimport)
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
#endif // if defined(_MSC_VER)
#endif // ifdef JSON_IN_CPPTL
#if !defined(JSON_API)
#define JSON_API
#endif
#if defined(_MSC_VER) && _MSC_VER < 1800
#error \
"ERROR: Visual Studio 12 (2013) with _MSC_VER=1800 is the oldest supported compiler with sufficient C++11 capabilities"
#endif
#if defined(_MSC_VER) && _MSC_VER < 1900
// As recommended at
// https://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010
extern JSON_API int
msvc_pre1900_c99_snprintf(char* outBuf, size_t size, const char* format, ...);
#define jsoncpp_snprintf msvc_pre1900_c99_snprintf
#else
#define jsoncpp_snprintf std::snprintf
#endif
// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for
// integer
// Storages, and 64 bits integer support is disabled.
// #define JSON_NO_INT64 1
// JSONCPP_OVERRIDE is maintained for backwards compatibility of external tools.
// C++11 should be used directly in JSONCPP.
#define JSONCPP_OVERRIDE override
#if __cplusplus >= 201103L
#define JSONCPP_NOEXCEPT noexcept
#define JSONCPP_OP_EXPLICIT explicit
#elif defined(_MSC_VER) && _MSC_VER < 1900
#define JSONCPP_NOEXCEPT throw()
#define JSONCPP_OP_EXPLICIT explicit
#elif defined(_MSC_VER) && _MSC_VER >= 1900
#define JSONCPP_NOEXCEPT noexcept
#define JSONCPP_OP_EXPLICIT explicit
#else
#define JSONCPP_NOEXCEPT throw()
#define JSONCPP_OP_EXPLICIT
#endif
#ifdef __clang__
#if __has_extension(attribute_deprecated_with_message)
#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
#endif
#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
#elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__))
#endif // GNUC version
#elif defined(_MSC_VER) // MSVC (after clang because clang on Windows emulates MSVC)
#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
#endif // __clang__ || __GNUC__ || _MSC_VER
#if !defined(JSONCPP_DEPRECATED)
#define JSONCPP_DEPRECATED(message)
#endif // if !defined(JSONCPP_DEPRECATED)
#if __GNUC__ >= 6
#define JSON_USE_INT64_DOUBLE_CONVERSION 1
#endif
#if !defined(JSON_IS_AMALGAMATION)
#include "allocator.h"
#include "version.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
namespace Json {
typedef int Int;
typedef unsigned int UInt;
#if defined(JSON_NO_INT64)
typedef int LargestInt;
typedef unsigned int LargestUInt;
#undef JSON_HAS_INT64
#else // if defined(JSON_NO_INT64)
// For Microsoft Visual use specific types as long long is not supported
#if defined(_MSC_VER) // Microsoft Visual Studio
typedef __int64 Int64;
typedef unsigned __int64 UInt64;
#else // if defined(_MSC_VER) // Other platforms, use long long
typedef int64_t Int64;
typedef uint64_t UInt64;
#endif // if defined(_MSC_VER)
typedef Int64 LargestInt;
typedef UInt64 LargestUInt;
#define JSON_HAS_INT64
#endif // if defined(JSON_NO_INT64)
template <typename T>
using Allocator = typename std::conditional<JSONCPP_USING_SECURE_MEMORY,
SecureAllocator<T>,
std::allocator<T>>::type;
using String = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
using IStringStream = std::basic_istringstream<String::value_type,
String::traits_type,
String::allocator_type>;
using OStringStream = std::basic_ostringstream<String::value_type,
String::traits_type,
String::allocator_type>;
using IStream = std::istream;
using OStream = std::ostream;
} // namespace Json
// Legacy names (formerly macros).
using JSONCPP_STRING = Json::String;
using JSONCPP_ISTRINGSTREAM = Json::IStringStream;
using JSONCPP_OSTRINGSTREAM = Json::OStringStream;
using JSONCPP_ISTREAM = Json::IStream;
using JSONCPP_OSTREAM = Json::OStream;
#endif // JSON_CONFIG_H_INCLUDED
// //////////////////////////////////////////////////////////////////////
// End of content of file: include/json/config.h
// //////////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: include/json/forwards.h
// //////////////////////////////////////////////////////////////////////
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_FORWARDS_H_INCLUDED
#define JSON_FORWARDS_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "config.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
namespace Json {
// writer.h
class FastWriter;
class StyledWriter;
// reader.h
class Reader;
// features.h
class Features;
// value.h
typedef unsigned int ArrayIndex;
class StaticString;
class Path;
class PathArgument;
class Value;
class ValueIteratorBase;
class ValueIterator;
class ValueConstIterator;
} // namespace Json
#endif // JSON_FORWARDS_H_INCLUDED
// //////////////////////////////////////////////////////////////////////
// End of content of file: include/json/forwards.h
// //////////////////////////////////////////////////////////////////////
#endif //ifndef JSON_FORWARD_AMALGAMATED_H_INCLUDED

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

1338
launchers/win32/libs/miniz.h Normal file

File diff suppressed because it is too large Load diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View file

@ -0,0 +1,13 @@
//
// Launcher.rc2 - resources Microsoft Visual C++ does not edit directly
//
#ifdef APSTUDIO_INVOKED
#error this file is not editable by Microsoft Visual C++
#endif //APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
// Add manually edited resources here...
/////////////////////////////////////////////////////////////////////////////

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View file

@ -0,0 +1,40 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by Launcher.rc
//
#define IDD_LAUNCHER_DIALOG 102
#define IDR_MAINFRAME 128
#define IDB_PNG1 133
#define IDB_PNG2 134
#define IDB_PNG4 136
#define IDR_FONT_REGULAR 140
#define IDR_FONT_BOLD 141
#define IDB_PNG5 144
#define IDC_BACKGROUND 1009
#define IDC_ORGNAME 1010
#define IDC_USERNAME 1011
#define IDC_PASSWORD 1012
#define IDC_MESSAGE_LABEL 1013
#define IDC_ACTION_LABEL 1014
#define IDC_BUTTON_NEXT 1015
#define IDC_MESSAGE2_LABEL 1016
#define IDC_ACTION2_LABEL 1017
#define IDC_ORGNAME_BANNER 1018
#define IDC_USERNAME_BANNER 1019
#define IDC_PASSWORD_BANNER 1020
#define IDC_TERMS 1021
#define IDC_TERMS2 1022
#define IDC_TROUBLE 1023
#define IDC_VOXEL 1024
#define IDC_TROUBLE_LINK 1027
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 147
#define _APS_NEXT_COMMAND_VALUE 32771
#define _APS_NEXT_CONTROL_VALUE 1028
#define _APS_NEXT_SYMED_VALUE 103
#endif
#endif

View file

@ -0,0 +1,13 @@
//
// stdafx.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"

35
launchers/win32/stdafx.h Normal file
View file

@ -0,0 +1,35 @@
//
// stdafx.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 VC_EXTRALEAN
#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers
#endif
#include "targetver.h"
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit
#define _AFX_NO_MFC_CONTROLS_IN_DIALOGS // remove support for MFC controls in dialogs
// turns off MFC's hiding of some common and often safely ignored warning messages
#define _AFX_ALL_WARNINGS
#include <afxwin.h> // MFC core and standard components
#include <afxext.h> // MFC extensions
#ifndef _AFX_NO_OLE_SUPPORT
#include <afxdtctl.h> // MFC support for Internet Explorer 4 Common Controls
#endif
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h> // MFC support for Windows Common Controls
#endif // _AFX_NO_AFXCMN_SUPPORT
#include <afxcontrolbars.h> // MFC support for ribbons and control bars

View file

@ -0,0 +1,18 @@
//
// targetver.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
// Including SDKDDKVer.h defines the highest available Windows platform.
// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
#include <SDKDDKVer.h>

View file

@ -437,7 +437,18 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const HifiS
return size;
} else {
return sendUnreliablePacket(*packet, sockAddr, hmacAuth);
auto size = sendUnreliablePacket(*packet, sockAddr, hmacAuth);
if (size < 0) {
auto now = usecTimestampNow();
eachNode([now](const SharedNodePointer & node) {
qCDebug(networking) << "Stats for " << node->getPublicSocket() << "\n"
<< " Last Heard Microstamp: " << node->getLastHeardMicrostamp() << " (" << (now - node->getLastHeardMicrostamp()) << "usec ago)\n"
<< " Outbound Kbps: " << node->getOutboundKbps() << "\n"
<< " Inbound Kbps: " << node->getInboundKbps() << "\n"
<< " Ping: " << node->getPingMs();
});
}
return size;
}
}

View file

@ -226,8 +226,17 @@ qint64 Socket::writeDatagram(const QByteArray& datagram, const HifiSockAddr& soc
qint64 bytesWritten = _udpSocket.writeDatagram(datagram, sockAddr.getAddress(), sockAddr.getPort());
if (bytesWritten < 0) {
// when saturating a link this isn't an uncommon message - suppress it so it doesn't bomb the debug
qCDebug(networking) << "Socket::writeDatagram : " << sockAddr << " " << _udpSocket.error();
qCDebug(networking) << "udt::writeDatagram (" << _udpSocket.state() << ") error - " << _udpSocket.error() << "(" << _udpSocket.errorString() << ")";
#ifdef WIN32
int wsaError = WSAGetLastError();
qCDebug(networking) << "windows socket error " << wsaError;
#endif
#ifdef DEBUG_EVENT_QUEUE
int nodeListQueueSize = ::hifi::qt::getEventQueueSize(thread());
qCDebug(networking) << "Networking queue size - " << nodeListQueueSize;
#endif // DEBUG_EVENT_QUEUE
}
return bytesWritten;
@ -490,7 +499,11 @@ std::vector<HifiSockAddr> Socket::getConnectionSockAddrs() {
}
void Socket::handleSocketError(QAbstractSocket::SocketError socketError) {
qCDebug(networking) << "udt::Socket error - " << socketError;
qCDebug(networking) << "udt::Socket (" << _udpSocket.state() << ") error - " << socketError << "(" << _udpSocket.errorString() << ")";
#ifdef DEBUG_EVENT_QUEUE
int nodeListQueueSize = ::hifi::qt::getEventQueueSize(thread());
qCDebug(networking) << "Networking queue size - " << nodeListQueueSize;
#endif // DEBUG_EVENT_QUEUE
}
void Socket::handleStateChanged(QAbstractSocket::SocketState socketState) {

View file

@ -18,6 +18,8 @@
#include <shared/ReadWriteLockable.h>
/**jsdoc
* The <code>Users</code> API provides features to regulate your interaction with other users.
*
* @namespace Users
*
* @hifi-interface
@ -25,10 +27,10 @@
* @hifi-avatar
* @hifi-assignment-client
*
* @property {boolean} canKick - <code>true</code> if the domain server allows the node or avatar to kick (ban) avatars,
* otherwise <code>false</code>. <em>Read-only.</em>
* @property {boolean} requestsDomainListData - <code>true</code> if the avatar requests extra data from the mixers (such as
* positional data of an avatar you've ignored). <em>Read-only.</em>
* @property {boolean} canKick - <code>true</code> if the domain server allows the client to kick (ban) avatars, otherwise
* <code>false</code>. <em>Read-only.</em>
* @property {boolean} requestsDomainListData - <code>true</code> if the client requests extra data from the mixers (such as
* positional data of an avatar they've ignored). <em>Read-only.</em>
*/
class UsersScriptingInterface : public QObject, public Dependency {
Q_OBJECT
@ -49,130 +51,161 @@ public:
public slots:
/**jsdoc
* Personally ignore another user, making them disappear for you and you disappear for them.
* Ignores or un-ignores another avatar. Ignoring an avatar makes them disappear for you and you disappear for them.
* @function Users.ignore
* @param {Uuid} nodeID The node or session ID of the user you want to ignore.
* @param {boolean} enable True for ignored; false for un-ignored.
* @param {Uuid} sessionID - The session ID of the avatar to ignore.
* @param {boolean} [enable=true] - <code>true</code> to ignore, <code>false</code> to un-ignore.
* @example <caption>Ignore a nearby avatar for a few seconds.</caption>
* var avatars = AvatarList.getAvatarsInRange(MyAvatar.position, 1000);
* if (avatars.length > 1) { // Skip own avatar which is provided in position 0.
* print("Ignore: " + avatars[1]);
* Users.ignore(avatars[1], true);
* Script.setTimeout(function () {
* print("Un-ignore: " + avatars[1]);
* Users.ignore(avatars[1], false);
* }, 5000);
* } else {
* print("No avatars");
* }
*/
void ignore(const QUuid& nodeID, bool ignoreEnabled = true);
/**jsdoc
* Get whether or not you have ignored the node with the given UUID.
* Gets whether or not you have ignored a particular avatar.
* @function Users.getIgnoreStatus
* @param {Uuid} nodeID The node or session ID of the user whose ignore status you want.
* @returns {boolean}
* @param {Uuid} sessionID - The session ID of the avatar to get the ignore status of.
* @returns {boolean} <code>true</code> if the avatar is being ignored, <code>false</code> if it isn't.
*/
bool getIgnoreStatus(const QUuid& nodeID);
/**jsdoc
* Mute another user for you and you only. They won't be able to hear you, and you won't be able to hear them.
* Mutes or un-mutes another avatar. Muting makes you unable to hear them and them unable to hear you.
* @function Users.personalMute
* @param {Uuid} nodeID The node or session ID of the user you want to mute.
* @param {boolean} muteEnabled True for enabled; false for disabled.
* @param {Uuid} sessionID - The session ID of the avatar to mute.
* @param {boolean} [muteEnabled=true] - <code>true</code> to mute, <code>false</code> to un-mute.
*/
void personalMute(const QUuid& nodeID, bool muteEnabled = true);
/**jsdoc
* Get whether or not you have personally muted the node with the given UUID.
* @function Users.requestPersonalMuteStatus
* @param {Uuid} nodeID The node or session ID of the user whose personal mute status you want.
* @returns {boolean}
* Gets whether or not you have muted a particular avatar.
* @function Users.getPersonalMuteStatus
* @param {Uuid} sessionID - The session ID of the avatar to get the mute status of.
* @returns {boolean} <code>true</code> if the avatar is muted, <code>false</code> if it isn't.
*/
bool getPersonalMuteStatus(const QUuid& nodeID);
/**jsdoc
* Sets an avatar's gain for you and you only.
* Units are Decibels (dB)
* Sets an avatar's gain (volume) for you and you only, or sets the master gain.
* @function Users.setAvatarGain
* @param {Uuid} nodeID The node or session ID of the user whose gain you want to modify, or null to set the master gain.
* @param {number} gain The gain of the avatar you'd like to set. Units are dB.
* @param {Uuid} nodeID - The session ID of the avatar to set the gain for, or <code>null</code> to set the master gain.
* @param {number} gain - The gain to set, in dB.
*/
void setAvatarGain(const QUuid& nodeID, float gain);
/**jsdoc
* Gets an avatar's gain for you and you only.
* Gets an avatar's gain (volume) for you and you only, or gets the master gain.
* @function Users.getAvatarGain
* @param {Uuid} nodeID The node or session ID of the user whose gain you want to get, or null to get the master gain.
* @returns {number} gain (in dB)
* @param {Uuid} nodeID - The session ID of the avatar to get the gain for, or <code>null</code> to get the master gain.
* @returns {number} The gain, in dB.
*/
float getAvatarGain(const QUuid& nodeID);
/**jsdoc
* Kick/ban another user. Removes them from the server and prevents them from returning. Bans by either user name (if
* available) or machine fingerprint otherwise. This will only do anything if you're an admin of the domain you're in.
* Kicks and bans a user. This removes them from the server and prevents them from returning. The ban is by user name if
* available, or machine fingerprint otherwise.
* <p>This function only works if you're an administrator of the domain you're in.</p>
* @function Users.kick
* @param {Uuid} nodeID The node or session ID of the user you want to kick.
* @param {Uuid} sessionID - The session ID of the user to kick and ban.
*/
void kick(const QUuid& nodeID);
/**jsdoc
* Mutes another user's microphone for everyone. Not permanent; the silenced user can unmute themselves with the UNMUTE
* button in their HUD. This will only do anything if you're an admin of the domain you're in.
* Mutes a user's microphone for everyone. The mute is not permanent: the user can unmute themselves.
* <p>This function only works if you're an administrator of the domain you're in.</p>
* @function Users.mute
* @param {Uuid} nodeID The node or session ID of the user you want to mute.
* @param {Uuid} sessionID - The session ID of the user to mute.
*/
void mute(const QUuid& nodeID);
/**jsdoc
* Request the user name and machine fingerprint associated with the given UUID. The user name will be returned in a
* {@link Users.usernameFromIDReply|usernameFromIDReply} signal. This will only do anything if you're an admin of the domain
* you're in.
* @function Users.requestUsernameFromID
* @param {Uuid} nodeID The node or session ID of the user whose user name you want.
*/
* Requests the user name and machine fingerprint associated with the given UUID. The user name is returned via a
* {@link Users.usernameFromIDReply|usernameFromIDReply} signal.
* <p>This function only works if you're an administrator of the domain you're in.</p>
* @function Users.requestUsernameFromID
* @param {Uuid} sessionID - The session ID of the user to get the user name and machine fingerprint of.
* @example <caption>Report the user name and fingerprint of a nearby user.</caption>
* function onUsernameFromIDReply(sessionID, userName, machineFingerprint, isAdmin) {
* print("Session: " + sessionID);
* print("User name: " + userName);
* print("Fingerprint: " + machineFingerprint);
* print("Is admin: " + isAdmin);
* }
*
* Users.usernameFromIDReply.connect(onUsernameFromIDReply);
*
* var avatars = AvatarList.getAvatarsInRange(MyAvatar.position, 1000);
* if (avatars.length > 1) { // Skip own avatar which is provided in position 0.
* print("Request data for: " + avatars[1]);
* Users.requestUsernameFromID(avatars[1]);
* } else {
* print("No avatars");
* }
*/
void requestUsernameFromID(const QUuid& nodeID);
/**jsdoc
* Returns `true` if the DomainServer will allow this Node/Avatar to make kick.
* Gets whether the client can kick and ban users in the domain.
* @function Users.getCanKick
* @returns {boolean} <code>true</code> if the domain server allows the client to kick (ban) other users, otherwise
* @returns {boolean} <code>true</code> if the domain server allows the client to kick and ban users, otherwise
* <code>false</code>.
*/
bool getCanKick();
/**jsdoc
* Toggle the state of the space bubble feature.
* Toggles the state of the privacy shield.
* @function Users.toggleIgnoreRadius
*/
void toggleIgnoreRadius();
/**jsdoc
* Enables the space bubble feature.
* Enables the privacy shield.
* @function Users.enableIgnoreRadius
*/
void enableIgnoreRadius();
/**jsdoc
* Disables the space bubble feature.
* Disables the privacy shield.
* @function Users.disableIgnoreRadius
*/
void disableIgnoreRadius();
/**jsdoc
* Returns `true` if the space bubble feature is enabled.
* Gets the status of the privacy shield.
* @function Users.getIgnoreRadiusEnabled
* @returns {boolean} <code>true</code> if the space bubble is enabled, otherwise <code>false</code>.
* @returns {boolean} <code>true</code> if the privacy shield is enabled, <code>false</code> if it is disabled.
*/
bool getIgnoreRadiusEnabled();
signals:
/**jsdoc
* Triggered when your ability to kick and ban users changes.
* @function Users.canKickChanged
* @param {boolean} canKick
* @param {boolean} canKick - <code>true</code> if you can kick and ban users, <code>false</code> if you can't.
* @returns {Signal}
*/
void canKickChanged(bool canKick);
/**jsdoc
* Triggered when the privacy shield status changes.
* @function Users.ignoreRadiusEnabledChanged
* @param {boolean} isEnabled
* @param {boolean} isEnabled - <code>true</code> if the privacy shield is enabled, <code>false</code> if it isn't.
* @returns {Signal}
*/
void ignoreRadiusEnabledChanged(bool isEnabled);
/**jsdoc
* Notifies scripts that another user has entered the ignore radius.
* Triggered when another user enters the privacy shield.
* @function Users.enteredIgnoreRadius
* @returns {Signal}
*/
@ -181,20 +214,21 @@ signals:
/**jsdoc
* Triggered in response to a {@link Users.requestUsernameFromID|requestUsernameFromID} call. Provides the user name and
* machine fingerprint associated with a UUID.
* Username and machineFingerprint will be their default constructor output if the requesting user isn't an admin.
* @function Users.usernameFromIDReply
* @param {Uuid} nodeID
* @param {string} userName
* @param {string} machineFingerprint
* @param {boolean} isAdmin
* @param {Uuid} sessionID - The session ID of the client that the data is for.
* @param {string} userName - The user name of the client, if the requesting client is an administrator in the domain or
* the <code>sessionID</code> is that of the client, otherwise <code>""</code>.
* @param {Uuid} machineFingerprint - The machine fingerprint of the client, if the requesting client is an administrator
* in the domain or the <code>sessionID</code> is that of the client, otherwise {@link Uuid|Uuid.NULL}.
* @param {boolean} isAdmin - <code>true</code> if the client is an administrator in the domain, <code>false</code> if not.
* @returns {Signal}
*/
void usernameFromIDReply(const QString& nodeID, const QString& username, const QString& machineFingerprint, bool isAdmin);
/**jsdoc
* Notifies scripts that a user has disconnected from the domain.
* Triggered when a client has disconnected from the domain.
* @function Users.avatarDisconnected
* @param {Uuid} nodeID The session ID of the avatar that has disconnected.
* @param {Uuid} sessionID - The session ID of the client that has disconnected.
* @returns {Signal}
*/
void avatarDisconnected(const QUuid& nodeID);

View file

@ -22,8 +22,8 @@
#include "GLMHelpers.h"
/**jsdoc
* The Vec3 API facilities for generating and manipulating 3-dimensional vectors. High Fidelity uses a right-handed
* Cartesian coordinate system where the y-axis is the "up" and the negative z-axis is the "front" direction.
* The <code>Vec3</code> API provides facilities for generating and manipulating 3-dimensional vectors. High Fidelity uses a
* right-handed Cartesian coordinate system where the y-axis is the "up" and the negative z-axis is the "front" direction.
* <img alt="High Fidelity coordinate system" src="https://docs.highfidelity.com/images/opengl-coord-system.jpg" />
*
* @namespace Vec3
@ -98,7 +98,7 @@ class Vec3 : public QObject, protected QScriptable {
public slots:
/**jsdoc
* Calculate the reflection of a vector in a plane.
* Calculates the reflection of a vector in a plane.
* @function Vec3(0).reflect
* @param {Vec3} v - The vector to reflect.
* @param {Vec3} normal - The normal of the plane.
@ -112,7 +112,7 @@ public slots:
glm::vec3 reflect(const glm::vec3& v1, const glm::vec3& v2) { return glm::reflect(v1, v2); }
/**jsdoc
* Calculate the cross product of two vectors.
* Calculates the cross product of two vectors.
* @function Vec3(0).cross
* @param {Vec3} v1 - The first vector.
* @param {Vec3} v2 - The second vector.
@ -126,7 +126,7 @@ public slots:
glm::vec3 cross(const glm::vec3& v1, const glm::vec3& v2) { return glm::cross(v1, v2); }
/**jsdoc
* Calculate the dot product of two vectors.
* Calculates the dot product of two vectors.
* @function Vec3(0).dot
* @param {Vec3} v1 - The first vector.
* @param {Vec3} v2 - The second vector.
@ -140,7 +140,7 @@ public slots:
float dot(const glm::vec3& v1, const glm::vec3& v2) { return glm::dot(v1, v2); }
/**jsdoc
* Multiply a vector by a scale factor.
* Multiplies a vector by a scale factor.
* @function Vec3(0).multiply
* @param {Vec3} v - The vector.
* @param {number} scale - The scale factor.
@ -149,7 +149,7 @@ public slots:
glm::vec3 multiply(const glm::vec3& v1, float f) { return v1 * f; }
/**jsdoc
* Multiply a vector by a scale factor.
* Multiplies a vector by a scale factor.
* @function Vec3(0).multiply
* @param {number} scale - The scale factor.
* @param {Vec3} v - The vector.
@ -158,7 +158,7 @@ public slots:
glm::vec3 multiply(float f, const glm::vec3& v1) { return v1 * f; }
/**jsdoc
* Multiply two vectors.
* Multiplies two vectors.
* @function Vec3(0).multiplyVbyV
* @param {Vec3} v1 - The first vector.
* @param {Vec3} v2 - The second vector.
@ -173,7 +173,7 @@ public slots:
glm::vec3 multiplyVbyV(const glm::vec3& v1, const glm::vec3& v2) { return v1 * v2; }
/**jsdoc
* Rotate a vector.
* Rotates a vector.
* @function Vec3(0).multiplyQbyV
* @param {Quat} q - The rotation to apply.
* @param {Vec3} v - The vector to rotate.
@ -187,7 +187,7 @@ public slots:
glm::vec3 multiplyQbyV(const glm::quat& q, const glm::vec3& v) { return q * v; }
/**jsdoc
* Calculate the sum of two vectors.
* Calculates the sum of two vectors.
* @function Vec3(0).sum
* @param {Vec3} v1 - The first vector.
* @param {Vec3} v2 - The second vector.
@ -196,7 +196,7 @@ public slots:
glm::vec3 sum(const glm::vec3& v1, const glm::vec3& v2) { return v1 + v2; }
/**jsdoc
* Calculate one vector subtracted from another.
* Calculates one vector subtracted from another.
* @function Vec3(0).subtract
* @param {Vec3} v1 - The first vector.
* @param {Vec3} v2 - The second vector.
@ -205,7 +205,7 @@ public slots:
glm::vec3 subtract(const glm::vec3& v1, const glm::vec3& v2) { return v1 - v2; }
/**jsdoc
* Calculate the length of a vector
* Calculates the length of a vector
* @function Vec3(0).length
* @param {Vec3} v - The vector.
* @returns {number} The length of the vector.
@ -213,7 +213,7 @@ public slots:
float length(const glm::vec3& v) { return glm::length(v); }
/**jsdoc
* Calculate the distance between two points.
* Calculates the distance between two points.
* @function Vec3(0).distance
* @param {Vec3} p1 - The first point.
* @param {Vec3} p2 - The second point.
@ -231,16 +231,17 @@ public slots:
float distance(const glm::vec3& v1, const glm::vec3& v2) { return glm::distance(v1, v2); }
/**jsdoc
* Calculate the angle of rotation from one vector onto another, with the sign depending on a reference vector.
* Calculates the angle of rotation from one vector onto another, with the sign depending on a reference vector.
* @function Vec3(0).orientedAngle
* @param {Vec3} v1 - The first vector.
* @param {Vec3} v2 - The second vector.
* @param {Vec3} ref - Reference vector.
* @returns {number} The angle of rotation from the first vector to the second, in degrees, with a positive sign if the
* rotation axis aligns with the reference vector (has a positive dot product) otherwise a negative sign.
* @example <caption>Compare <code>Vec3.angle()</code> and <code>Vec3.orientedAngle()</code>.</caption>
* @returns {number} The angle of rotation from the first vector to the second, in degrees. The value is positive if the
* rotation axis aligns with the reference vector (has a positive dot product), otherwise the value is negative.
* @example <caption>Compare <code>Vec3.getAngle()</code> and <code>Vec3.orientedAngle()</code>.</caption>
* var v1 = { x: 5, y: 0, z: 0 };
* var v2 = { x: 5, y: 0, z: 5 };
*
* var angle = Vec3.getAngle(v1, v2);
* print(angle * 180 / Math.PI); // 45
*
@ -252,7 +253,7 @@ public slots:
float orientedAngle(const glm::vec3& v1, const glm::vec3& v2, const glm::vec3& v3);
/**jsdoc
* Normalize a vector so that its length is <code>1</code>.
* Normalizes a vector so that its length is <code>1</code>.
* @function Vec3(0).normalize
* @param {Vec3} v - The vector to normalize.
* @returns {Vec3} The vector normalized to have a length of <code>1</code>.
@ -265,11 +266,11 @@ public slots:
glm::vec3 normalize(const glm::vec3& v) { return glm::normalize(v); };
/**jsdoc
* Calculate a linear interpolation between two vectors.
* Calculates a linear interpolation between two vectors.
* @function Vec3(0).mix
* @param {Vec3} v1 - The first vector.
* @param {Vec3} v2 - The second vector.
* @param {number} factor - The interpolation factor in the range <code>0.0</code> to <code>1.0</code>.
* @param {number} factor - The interpolation factor, range <code>0.0</code> &ndash; <code>1.0</code>.
* @returns {Vec3} The linear interpolation between the two vectors: <code>(1 - factor) * v1 + factor * v2</code>.
* @example <caption>Linear interpolation between two vectors.</caption>
* var v1 = { x: 10, y: 0, z: 0 };
@ -280,7 +281,7 @@ public slots:
glm::vec3 mix(const glm::vec3& v1, const glm::vec3& v2, float m) { return glm::mix(v1, v2, m); }
/**jsdoc
* Print to the program log a text label followed by a vector value.
* Prints the vector to the program log, as a text label followed by the vector value.
* @function Vec3(0).print
* @param {string} label - The label to print.
* @param {Vec3} v - The vector value to print.
@ -292,8 +293,9 @@ public slots:
void print(const QString& label, const glm::vec3& v);
/**jsdoc
* Test whether two vectors are equal. <strong>Note:</strong> The vectors must be exactly equal in order for
* <code>true</code> to be returned; it is often better to use {@link Vec3(0).withinEpsilon|withinEpsilon}.
* Tests whether two vectors are equal.
* <p><strong>Note:</strong> The vectors must be exactly equal in order for <code>true</code> to be returned; it is often
* better to use {@link Vec3(0).withinEpsilon|withinEpsilon}.</p>
* @function Vec3(0).equal
* @param {Vec3} v1 - The first vector.
* @param {Vec3} v2 - The second vector.
@ -301,6 +303,7 @@ public slots:
* @example <caption> Vectors are only equal if exactly the same.</caption>
* var v1 = { x: 10, y: 10, z: 10 };
* var v2 = { x: 10, y: 10, z: 10 };
*
* var equal = Vec3.equal(v1, v2);
* print(equal); // true
*
@ -311,17 +314,18 @@ public slots:
bool equal(const glm::vec3& v1, const glm::vec3& v2) { return v1 == v2; }
/**jsdoc
* Test whether two vectors are equal within a tolerance. <strong>Note:</strong> It is often better to use this function
* than {@link Vec3(0).equal|equal}.
* Tests whether two vectors are equal within a tolerance.
* <p><strong>Note:</strong> It is often better to use this function than {@link Vec3(0).equal|equal}.</p>
* @function Vec3(0).withinEpsilon
* @param {Vec3} v1 - The first vector.
* @param {Vec3} v2 - The second vector.
* @param {number} epsilon - The maximum distance between the two vectors.
* @returns {boolean} <code>true</code> if the distance between the points represented by the vectors is less than or equal
* to the <code>epsilon</code>, otherwise <code>false</code>.
* to <code>epsilon</code>, otherwise <code>false</code>.
* @example <caption>Testing vectors for near equality.</caption>
* var v1 = { x: 10, y: 10, z: 10 };
* var v2 = { x: 10, y: 10, z: 10.0005 };
*
* var equal = Vec3.equal(v1, v2);
* print(equal); // false
*
@ -331,11 +335,11 @@ public slots:
bool withinEpsilon(const glm::vec3& v1, const glm::vec3& v2, float epsilon);
/**jsdoc
* Calculate polar coordinates (elevation, azimuth, radius) that transform the unit z-axis vector onto a point.
* Calculates polar coordinates (elevation, azimuth, radius) that transform the unit z-axis vector onto a point.
* @function Vec3(0).toPolar
* @param {Vec3} p - The point to calculate the polar coordinates for.
* @returns {Vec3} Vector of polar coordinates for the point: <code>x</code> elevation rotation about the x-axis in
* radians, <code>y</code> azimuth rotation about the y-axis in radians, and <code>z</code> scale.
* radians, <code>y</code> azimuth rotation about the y-axis in radians, and <code>z</code> radius.
* @example <caption>Polar coordinates for a point.</caption>
* var v = { x: 5, y: 2.5, z: 5 };
* var polar = Vec3.toPolar(v);
@ -347,10 +351,10 @@ public slots:
glm::vec3 toPolar(const glm::vec3& v);
/**jsdoc
* Calculate the coordinates of a point from polar coordinate transformation of the unit z-axis vector.
* Calculates the coordinates of a point from polar coordinate transformation of the unit z-axis vector.
* @function Vec3(0).fromPolar
* @param {Vec3} polar - The polar coordinates of a point: <code>x</code> elevation rotation about the x-axis in radians,
* <code>y</code> azimuth rotation about the y-axis in radians, and <code>z</code> scale.
* <code>y</code> azimuth rotation about the y-axis in radians, and <code>z</code> radius.
* @returns {Vec3} The coordinates of the point.
* @example <caption>Polar coordinates to Cartesian.</caption>
* var polar = { x: -19.471 * Math.PI / 180, y: 45 * Math.PI / 180, z: 7.5 };
@ -361,18 +365,17 @@ public slots:
glm::vec3 fromPolar(const glm::vec3& polar);
/**jsdoc
* Calculate the unit vector corresponding to polar coordinates elevation and azimuth transformation of the unit z-axis
* Calculates the unit vector corresponding to polar coordinates elevation and azimuth transformation of the unit z-axis
* vector.
* @function Vec3(0).fromPolar
* @param {number} elevation - Rotation about the x-axis, in radians.
* @param {number} azimuth - Rotation about the y-axis, in radians.
* @returns {Vec3} Unit vector for the direction specified by <code>elevation</code> and <code>azimuth</code>.
* @example <caption>Polar coordinates to Cartesian.</caption>
* @example <caption>Polar coordinates to Cartesian coordinates.</caption>
* var elevation = -19.471 * Math.PI / 180;
* var rotation = 45 * Math.PI / 180;
* var p = Vec3.fromPolar(elevation, rotation);
* print(JSON.stringify(p)); // {"x":0.667,"y":0.333,"z":0.667}
*
* p = Vec3.multiply(7.5, p);
* print(JSON.stringify(p)); // {"x":5,"y":2.5,"z":5}
*/
@ -380,7 +383,7 @@ public slots:
glm::vec3 fromPolar(float elevation, float azimuth);
/**jsdoc
* Calculate the angle between two vectors.
* Calculates the angle between two vectors.
* @function Vec3(0).getAngle
* @param {Vec3} v1 - The first vector.
* @param {Vec3} v2 - The second vector.

View file

@ -100,7 +100,8 @@ glm::vec2 vec2FromVariant(const QVariant& object);
* @property {number} x - X-coordinate of the vector. Synonyms: <code>r</code>, <code>red</code>.
* @property {number} y - Y-coordinate of the vector. Synonyms: <code>g</code>, <code>green</code>.
* @property {number} z - Z-coordinate of the vector. Synonyms: <code>b</code>, <code>blue</code>.
* @example <caption>Vec3s can be set in multiple ways and modified with their aliases, but still stringify in the same way</caption>
* @example <caption>Vec3 values can be set in multiple ways and modified with their aliases, but still stringify in the same
* way.</caption>
* Entities.editEntity(<id>, { position: { x: 1, y: 2, z: 3 }}); // { x: 1, y: 2, z: 3 }
* Entities.editEntity(<id>, { position: { r: 4, g: 5, b: 6 }}); // { x: 4, y: 5, z: 6 }
* Entities.editEntity(<id>, { position: { red: 7, green: 8, blue: 9 }}); // { x: 7, y: 8, z: 9 }

View file

@ -29,10 +29,12 @@ elif sys.platform == "darwin":
# Customize the output filename
def computeArchiveName():
def computeArchiveName(prefix):
RELEASE_TYPE = os.getenv("RELEASE_TYPE", "DEV")
RELEASE_NUMBER = os.getenv("RELEASE_NUMBER", "")
GIT_PR_COMMIT_SHORT = os.getenv("SHA7", "")
if GIT_PR_COMMIT_SHORT == '':
GIT_PR_COMMIT_SHORT = os.getenv("GIT_PR_COMMIT_SHORT", "")
if RELEASE_TYPE == "PRODUCTION":
BUILD_VERSION = "{}-{}".format(RELEASE_NUMBER, GIT_PR_COMMIT_SHORT)
@ -48,7 +50,7 @@ def computeArchiveName():
else:
PLATFORM = "other"
ARCHIVE_NAME = "HighFidelity-Beta-Interface-{}-{}".format(BUILD_VERSION, PLATFORM)
ARCHIVE_NAME = "{}-{}-{}".format(prefix, BUILD_VERSION, PLATFORM)
return ARCHIVE_NAME
def wipeClientBuildPath(relativePath):
@ -109,12 +111,48 @@ def fixupWinZip(filename):
print("Replacing {} with fixed {}".format(fullPath, outFullPath))
shutil.move(outFullPath, fullPath)
def buildLightLauncher():
launcherSourcePath = os.path.join(SOURCE_PATH, 'launchers', sys.platform)
launcherBuildPath = os.path.join(BUILD_PATH, 'launcher')
if not os.path.exists(launcherBuildPath):
os.makedirs(launcherBuildPath)
# configure launcher build
cmakeArgs = [ 'cmake', '-S', launcherSourcePath ]
if sys.platform == 'darwin':
cmakeArgs.append('-G')
cmakeArgs.append('Xcode')
elif sys.platform == 'win32':
cmakeArgs.append('-A')
cmakeArgs.append('x64')
hifi_utils.executeSubprocess(cmakeArgs, folder=launcherBuildPath)
buildTarget = 'package'
if sys.platform == 'win32':
buildTarget = 'ALL_BUILD'
hifi_utils.executeSubprocess([
'cmake',
'--build', launcherBuildPath,
'--config', 'Release',
'--target', buildTarget
], folder=launcherBuildPath)
if sys.platform == 'darwin':
launcherDestFile = os.path.join(BUILD_PATH, "{}.dmg".format(computeArchiveName('Launcher')))
launcherSourceFile = os.path.join(launcherBuildPath, "HQ Launcher.dmg")
elif sys.platform == 'win32':
# FIXME
launcherDestFile = os.path.join(BUILD_PATH, "{}.exe".format(computeArchiveName('Launcher')))
launcherSourceFile = os.path.join(launcherBuildPath, "Launcher.exe")
print("Moving {} to {}".format(launcherSourceFile, launcherDestFile))
shutil.move(launcherSourceFile, launcherDestFile)
for wipePath in WIPE_PATHS:
wipeClientBuildPath(wipePath)
# Need the archive name for ad-hoc zip file manipulation
archiveName = computeArchiveName()
archiveName = computeArchiveName('HighFidelity-Beta-Interface')
cpackCommand = [
'cpack',
@ -132,3 +170,5 @@ if sys.platform == "win32":
elif sys.platform == "darwin":
fixupMacZip(archiveName)
# FIXME enable when MFC is on the Windows build hosts
# buildLightLauncher()