diff --git a/BUILD_WIN.md b/BUILD_WIN.md index 1a33088237..eea1f85e5b 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -27,11 +27,20 @@ Go to `Control Panel > System > Advanced System Settings > Environment Variables * Set "Variable name": `QT_CMAKE_PREFIX_PATH` * Set "Variable value": `C:\Qt\5.9.1\msvc2017_64\lib\cmake` -### Step 5. Installing OpenSSL +### Step 5. Installing [vcpkg](https://github.com/Microsoft/vcpkg) -Download and install the Win64 OpenSSL v1.0.2 Installer[https://slproweb.com/products/Win32OpenSSL.html]. + * Clone the VCPKG [repository](https://github.com/Microsoft/vcpkg) + * Follow the instructions in the [readme](https://github.com/Microsoft/vcpkg/blob/master/README.md) to bootstrap vcpkg + * Note, you may need to do these in a _Developer Command Prompt_ + * Set an environment variable VCPKG_ROOT to the location of the cloned repository + * Close and re-open any command prompts after setting the environment variable so that they will pick up the change -### Step 6. Running CMake to Generate Build Files +### Step 6. Installing OpenSSL via vcpkg + + * In the vcpkg directory, install the 64 bit OpenSSL package with the command `vcpkg install openssl:x64-windows` + * Once the build completes you should have a file `ssl.h` in `${VCPKG_ROOT}/installed/x64-windows/include/openssl` + +### Step 7. Running CMake to Generate Build Files Run Command Prompt from Start and run the following commands: ``` @@ -43,7 +52,7 @@ cmake .. -G "Visual Studio 15 Win64" Where `%HIFI_DIR%` is the directory for the highfidelity repository. -### Step 7. Making a Build +### Step 8. Making a Build Open `%HIFI_DIR%\build\hifi.sln` using Visual Studio. @@ -51,7 +60,7 @@ Change the Solution Configuration (next to the green play button) from "Debug" t Run `Build > Build Solution`. -### Step 8. Testing Interface +### Step 9. Testing Interface Create another environment variable (see Step #4) * Set "Variable name": `_NO_DEBUG_HEAP` @@ -65,16 +74,20 @@ Note: You can also run Interface by launching it from command line or File Explo ## Troubleshooting -For any problems after Step #6, first try this: +For any problems after Step #7, first try this: * Delete your locally cloned copy of the highfidelity repository * Restart your computer * Redownload the [repository](https://github.com/highfidelity/hifi) -* Restart directions from Step #6 +* Restart directions from Step #7 #### CMake gives you the same error message repeatedly after the build fails Remove `CMakeCache.txt` found in the `%HIFI_DIR%\build` directory. +#### CMake can't find OpenSSL + +Remove `CMakeCache.txt` found in the `%HIFI_DIR%\build` directory. Verify that your VCPKG_ROOT environment variable is set and pointing to the correct location. Verify that the file `${VCPKG_ROOT}/installed/x64-windows/include/openssl/ssl.h` exists. + #### Qt is throwing an error Make sure you have the correct version (5.9.1) installed and `QT_CMAKE_PREFIX_PATH` environment variable is set correctly. diff --git a/Test Plan 2.docx b/Test Plan 2.docx deleted file mode 100644 index da60821b53..0000000000 Binary files a/Test Plan 2.docx and /dev/null differ diff --git a/assignment-client/src/audio/AudioMixerSlave.cpp b/assignment-client/src/audio/AudioMixerSlave.cpp index ed63bbc298..a131e266d2 100644 --- a/assignment-client/src/audio/AudioMixerSlave.cpp +++ b/assignment-client/src/audio/AudioMixerSlave.cpp @@ -558,7 +558,7 @@ float computeAzimuth(const AvatarAudioStream& listeningNodeStream, const Positio // produce an oriented angle about the y-axis glm::vec3 direction = rotatedSourcePosition * (1.0f / fastSqrtf(rotatedSourcePositionLength2)); - float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); // UNIT_NEG_Z is "forward" + float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); // UNIT_NEG_Z is "forward" return (direction.x < 0.0f) ? -angle : angle; } else { diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 868b377ced..9345f96b1d 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -458,84 +458,80 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode* return _truePacketsSent; } +bool OctreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params) { + bool somethingToSend = false; + OctreeQueryNode* nodeData = static_cast(params.nodeData); + if (!nodeData->elementBag.isEmpty()) { + quint64 encodeStart = usecTimestampNow(); + quint64 lockWaitStart = encodeStart; + + _myServer->getOctree()->withReadLock([&]{ + OctreeServer::trackTreeWaitTime((float)(usecTimestampNow() - lockWaitStart)); + + OctreeElementPointer subTree = nodeData->elementBag.extract(); + if (subTree) { + // NOTE: this is where the tree "contents" are actually packed + nodeData->stats.encodeStarted(); + _myServer->getOctree()->encodeTreeBitstream(subTree, &_packetData, nodeData->elementBag, params); + nodeData->stats.encodeStopped(); + + somethingToSend = true; + } + }); + + OctreeServer::trackEncodeTime((float)(usecTimestampNow() - encodeStart)); + } else { + OctreeServer::trackTreeWaitTime(OctreeServer::SKIP_TIME); + OctreeServer::trackEncodeTime(OctreeServer::SKIP_TIME); + } + return somethingToSend; +} + void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged, bool isFullScene) { // calculate max number of packets that can be sent during this interval int clientMaxPacketsPerInterval = std::max(1, (nodeData->getMaxQueryPacketsPerSecond() / INTERVALS_PER_SECOND)); int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval()); int extraPackingAttempts = 0; - bool completedScene = false; + + // init params once outside the while loop + int boundaryLevelAdjustClient = nodeData->getBoundaryLevelAdjust(); + int boundaryLevelAdjust = boundaryLevelAdjustClient + + (viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST); + float octreeSizeScale = nodeData->getOctreeSizeScale(); + EncodeBitstreamParams params(INT_MAX, WANT_EXISTS_BITS, DONT_CHOP, + viewFrustumChanged, boundaryLevelAdjust, octreeSizeScale, + isFullScene, _myServer->getJurisdiction(), nodeData); + // Our trackSend() function is implemented by the server subclass, and will be called back + // during the encodeTreeBitstream() as new entities/data elements are sent + params.trackSend = [this](const QUuid& dataID, quint64 dataEdited) { + _myServer->trackSend(dataID, dataEdited, _nodeUuid); + }; + nodeData->copyCurrentViewFrustum(params.viewFrustum); + if (viewFrustumChanged) { + nodeData->copyLastKnownViewFrustum(params.lastViewFrustum); + } bool somethingToSend = true; // assume we have something + bool bagHadSomething = !nodeData->elementBag.isEmpty(); while (somethingToSend && _packetsSentThisInterval < maxPacketsPerInterval && !nodeData->isShuttingDown()) { - float lockWaitElapsedUsec = OctreeServer::SKIP_TIME; - float encodeElapsedUsec = OctreeServer::SKIP_TIME; float compressAndWriteElapsedUsec = OctreeServer::SKIP_TIME; float packetSendingElapsedUsec = OctreeServer::SKIP_TIME; quint64 startInside = usecTimestampNow(); bool lastNodeDidntFit = false; // assume each node fits - if (!nodeData->elementBag.isEmpty()) { + params.stopReason = EncodeBitstreamParams::UNKNOWN; // reset params.stopReason before traversal - quint64 lockWaitStart = usecTimestampNow(); - _myServer->getOctree()->withReadLock([&]{ - quint64 lockWaitEnd = usecTimestampNow(); - lockWaitElapsedUsec = (float)(lockWaitEnd - lockWaitStart); - quint64 encodeStart = usecTimestampNow(); + somethingToSend = traverseTreeAndBuildNextPacketPayload(params); - OctreeElementPointer subTree = nodeData->elementBag.extract(); - if (!subTree) { - return; - } - - float octreeSizeScale = nodeData->getOctreeSizeScale(); - int boundaryLevelAdjustClient = nodeData->getBoundaryLevelAdjust(); - - int boundaryLevelAdjust = boundaryLevelAdjustClient + - (viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST); - - EncodeBitstreamParams params(INT_MAX, WANT_EXISTS_BITS, DONT_CHOP, - viewFrustumChanged, boundaryLevelAdjust, octreeSizeScale, - isFullScene, _myServer->getJurisdiction(), nodeData); - nodeData->copyCurrentViewFrustum(params.viewFrustum); - if (viewFrustumChanged) { - nodeData->copyLastKnownViewFrustum(params.lastViewFrustum); - } - - // Our trackSend() function is implemented by the server subclass, and will be called back - // during the encodeTreeBitstream() as new entities/data elements are sent - params.trackSend = [this](const QUuid& dataID, quint64 dataEdited) { - _myServer->trackSend(dataID, dataEdited, _nodeUuid); - }; - - // TODO: should this include the lock time or not? This stat is sent down to the client, - // it seems like it may be a good idea to include the lock time as part of the encode time - // are reported to client. Since you can encode without the lock - nodeData->stats.encodeStarted(); - - // NOTE: this is where the tree "contents" are actaully packed - _myServer->getOctree()->encodeTreeBitstream(subTree, &_packetData, nodeData->elementBag, params); - - quint64 encodeEnd = usecTimestampNow(); - encodeElapsedUsec = (float)(encodeEnd - encodeStart); - - // If after calling encodeTreeBitstream() there are no nodes left to send, then we know we've - // sent the entire scene. We want to know this below so we'll actually write this content into - // the packet and send it - completedScene = nodeData->elementBag.isEmpty(); - - if (params.stopReason == EncodeBitstreamParams::DIDNT_FIT) { - lastNodeDidntFit = true; - extraPackingAttempts++; - } - - nodeData->stats.encodeStopped(); - }); - } else { - somethingToSend = false; // this will cause us to drop out of the loop... + if (params.stopReason == EncodeBitstreamParams::DIDNT_FIT) { + lastNodeDidntFit = true; + extraPackingAttempts++; } + // If the bag had contents but is now empty then we know we've sent the entire scene. + bool completedScene = bagHadSomething && nodeData->elementBag.isEmpty(); if (completedScene || lastNodeDidntFit) { // we probably want to flush what has accumulated in nodeData but: // do we have more data to send? and is there room? @@ -562,8 +558,7 @@ void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, Octre if (sendNow) { quint64 packetSendingStart = usecTimestampNow(); _packetsSentThisInterval += handlePacketSend(node, nodeData); - quint64 packetSendingEnd = usecTimestampNow(); - packetSendingElapsedUsec = (float)(packetSendingEnd - packetSendingStart); + packetSendingElapsedUsec = (float)(usecTimestampNow() - packetSendingStart); targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE); extraPackingAttempts = 0; @@ -576,14 +571,9 @@ void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, Octre } _packetData.changeSettings(true, targetSize); // will do reset - NOTE: Always compressed } - OctreeServer::trackTreeWaitTime(lockWaitElapsedUsec); - OctreeServer::trackEncodeTime(encodeElapsedUsec); OctreeServer::trackCompressAndWriteTime(compressAndWriteElapsedUsec); OctreeServer::trackPacketSendingTime(packetSendingElapsedUsec); - - quint64 endInside = usecTimestampNow(); - quint64 elapsedInsideUsecs = endInside - startInside; - OctreeServer::trackInsideTime((float)elapsedInsideUsecs); + OctreeServer::trackInsideTime((float)(usecTimestampNow() - startInside)); } if (somethingToSend && _myServer->wantsVerboseDebug()) { diff --git a/assignment-client/src/octree/OctreeSendThread.h b/assignment-client/src/octree/OctreeSendThread.h index d158539f57..8f75092528 100644 --- a/assignment-client/src/octree/OctreeSendThread.h +++ b/assignment-client/src/octree/OctreeSendThread.h @@ -53,7 +53,9 @@ protected: /// Called before a packetDistributor pass to allow for pre-distribution processing virtual void preDistributionProcessing() {}; - virtual void traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged, bool isFullScene); + virtual void traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData, + bool viewFrustumChanged, bool isFullScene); + virtual bool traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params); OctreeServer* _myServer { nullptr }; QWeakPointer _node; diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index db97da751f..974f00326b 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -35,7 +35,7 @@ #include int OctreeServer::_clientCount = 0; -const int MOVING_AVERAGE_SAMPLE_COUNTS = 1000000; +const int MOVING_AVERAGE_SAMPLE_COUNTS = 1000; float OctreeServer::SKIP_TIME = -1.0f; // use this for trackXXXTime() calls for non-times @@ -136,18 +136,19 @@ void OctreeServer::trackEncodeTime(float time) { if (time == SKIP_TIME) { _noEncode++; - time = 0.0f; - } else if (time <= MAX_SHORT_TIME) { - _shortEncode++; - _averageShortEncodeTime.updateAverage(time); - } else if (time <= MAX_LONG_TIME) { - _longEncode++; - _averageLongEncodeTime.updateAverage(time); } else { - _extraLongEncode++; - _averageExtraLongEncodeTime.updateAverage(time); + if (time <= MAX_SHORT_TIME) { + _shortEncode++; + _averageShortEncodeTime.updateAverage(time); + } else if (time <= MAX_LONG_TIME) { + _longEncode++; + _averageLongEncodeTime.updateAverage(time); + } else { + _extraLongEncode++; + _averageExtraLongEncodeTime.updateAverage(time); + } + _averageEncodeTime.updateAverage(time); } - _averageEncodeTime.updateAverage(time); } void OctreeServer::trackTreeWaitTime(float time) { @@ -155,18 +156,19 @@ void OctreeServer::trackTreeWaitTime(float time) { const float MAX_LONG_TIME = 100.0f; if (time == SKIP_TIME) { _noTreeWait++; - time = 0.0f; - } else if (time <= MAX_SHORT_TIME) { - _shortTreeWait++; - _averageTreeShortWaitTime.updateAverage(time); - } else if (time <= MAX_LONG_TIME) { - _longTreeWait++; - _averageTreeLongWaitTime.updateAverage(time); } else { - _extraLongTreeWait++; - _averageTreeExtraLongWaitTime.updateAverage(time); + if (time <= MAX_SHORT_TIME) { + _shortTreeWait++; + _averageTreeShortWaitTime.updateAverage(time); + } else if (time <= MAX_LONG_TIME) { + _longTreeWait++; + _averageTreeLongWaitTime.updateAverage(time); + } else { + _extraLongTreeWait++; + _averageTreeExtraLongWaitTime.updateAverage(time); + } + _averageTreeWaitTime.updateAverage(time); } - _averageTreeWaitTime.updateAverage(time); } void OctreeServer::trackCompressAndWriteTime(float time) { @@ -174,26 +176,27 @@ void OctreeServer::trackCompressAndWriteTime(float time) { const float MAX_LONG_TIME = 100.0f; if (time == SKIP_TIME) { _noCompress++; - time = 0.0f; - } else if (time <= MAX_SHORT_TIME) { - _shortCompress++; - _averageShortCompressTime.updateAverage(time); - } else if (time <= MAX_LONG_TIME) { - _longCompress++; - _averageLongCompressTime.updateAverage(time); } else { - _extraLongCompress++; - _averageExtraLongCompressTime.updateAverage(time); + if (time <= MAX_SHORT_TIME) { + _shortCompress++; + _averageShortCompressTime.updateAverage(time); + } else if (time <= MAX_LONG_TIME) { + _longCompress++; + _averageLongCompressTime.updateAverage(time); + } else { + _extraLongCompress++; + _averageExtraLongCompressTime.updateAverage(time); + } + _averageCompressAndWriteTime.updateAverage(time); } - _averageCompressAndWriteTime.updateAverage(time); } void OctreeServer::trackPacketSendingTime(float time) { if (time == SKIP_TIME) { _noSend++; - time = 0.0f; + } else { + _averagePacketSendingTime.updateAverage(time); } - _averagePacketSendingTime.updateAverage(time); } @@ -202,18 +205,19 @@ void OctreeServer::trackProcessWaitTime(float time) { const float MAX_LONG_TIME = 100.0f; if (time == SKIP_TIME) { _noProcessWait++; - time = 0.0f; - } else if (time <= MAX_SHORT_TIME) { - _shortProcessWait++; - _averageProcessShortWaitTime.updateAverage(time); - } else if (time <= MAX_LONG_TIME) { - _longProcessWait++; - _averageProcessLongWaitTime.updateAverage(time); } else { - _extraLongProcessWait++; - _averageProcessExtraLongWaitTime.updateAverage(time); + if (time <= MAX_SHORT_TIME) { + _shortProcessWait++; + _averageProcessShortWaitTime.updateAverage(time); + } else if (time <= MAX_LONG_TIME) { + _longProcessWait++; + _averageProcessLongWaitTime.updateAverage(time); + } else { + _extraLongProcessWait++; + _averageProcessExtraLongWaitTime.updateAverage(time); + } + _averageProcessWaitTime.updateAverage(time); } - _averageProcessWaitTime.updateAverage(time); } OctreeServer::OctreeServer(ReceivedMessage& message) : diff --git a/cmake/macros/SetPackagingParameters.cmake b/cmake/macros/SetPackagingParameters.cmake index fea244873c..8458d53f68 100644 --- a/cmake/macros/SetPackagingParameters.cmake +++ b/cmake/macros/SetPackagingParameters.cmake @@ -133,7 +133,7 @@ macro(SET_PACKAGING_PARAMETERS) else() message( FATAL_ERROR "Visual Studio 2013 or higher required." ) endif() - + if (NOT SIGNTOOL_EXECUTABLE) message(FATAL_ERROR "Code signing of executables was requested but signtool.exe could not be found.") endif () @@ -147,6 +147,7 @@ macro(SET_PACKAGING_PARAMETERS) set(CONSOLE_STARTUP_REG_KEY "ConsoleStartupShortcut") set(CLIENT_LAUNCH_NOW_REG_KEY "ClientLaunchAfterInstall") set(SERVER_LAUNCH_NOW_REG_KEY "ServerLaunchAfterInstall") + set(CUSTOM_INSTALL_REG_KEY "CustomInstall") endif () # setup component categories for installer diff --git a/cmake/modules/FindOpenSSL.cmake b/cmake/modules/FindOpenSSL.cmake index 69b6c367ca..338dee7bc8 100644 --- a/cmake/modules/FindOpenSSL.cmake +++ b/cmake/modules/FindOpenSSL.cmake @@ -34,26 +34,11 @@ if (UNIX) endif () if (WIN32) - - file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles) - - if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") - # http://www.slproweb.com/products/Win32OpenSSL.html - set(_OPENSSL_ROOT_HINTS ${OPENSSL_ROOT_DIR} $ENV{OPENSSL_ROOT_DIR} $ENV{HIFI_LIB_DIR}/openssl - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1;Inno Setup: App Path]" - ) - set(_OPENSSL_ROOT_PATHS "${_programfiles}/OpenSSL" "${_programfiles}/OpenSSL-Win64" "C:/OpenSSL/" "C:/OpenSSL-Win64/") + if (("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")) + set(_OPENSSL_ROOT_HINTS_AND_PATHS $ENV{VCPKG_ROOT}/installed/x64-windows) else() - # http://www.slproweb.com/products/Win32OpenSSL.html - set(_OPENSSL_ROOT_HINTS ${OPENSSL_ROOT_DIR} $ENV{OPENSSL_ROOT_DIR} $ENV{HIFI_LIB_DIR}/openssl - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]" - ) - set(_OPENSSL_ROOT_PATHS "${_programfiles}/OpenSSL" "${_programfiles}/OpenSSL-Win32" "C:/OpenSSL/" "C:/OpenSSL-Win32/") + set(_OPENSSL_ROOT_HINTS_AND_PATHS $ENV{VCPKG_ROOT}/installed/x86-windows) endif() - - unset(_programfiles) - set(_OPENSSL_ROOT_HINTS_AND_PATHS HINTS ${_OPENSSL_ROOT_HINTS} PATHS ${_OPENSSL_ROOT_PATHS}) - else () include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") hifi_library_search_hints("openssl") @@ -67,47 +52,14 @@ find_path(OPENSSL_INCLUDE_DIR NAMES openssl/ssl.h HINTS ${_OPENSSL_ROOT_HINTS_AN if (WIN32 AND NOT CYGWIN) if (MSVC) - - # In Visual C++ naming convention each of these four kinds of Windows libraries has it's standard suffix: - # * MD for dynamic-release - # * MDd for dynamic-debug - # * MT for static-release - # * MTd for static-debug - - # Implementation details: - # We are using the libraries located in the VC subdir instead of the parent directory eventhough : - # libeay32MD.lib is identical to ../libeay32.lib, and - # ssleay32MD.lib is identical to ../ssleay32.lib - - # The Kitware FindOpenSSL module has been modified here by High Fidelity to look specifically for static libraries - - find_library(LIB_EAY_DEBUG NAMES libeay32MTd - ${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES "lib/VC/static" - ) - - find_library(LIB_EAY_RELEASE NAMES libeay32MT - ${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES "lib/VC/static" - ) - - find_library(SSL_EAY_DEBUG NAMES ssleay32MTd - ${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES "lib/VC/static" - ) - - find_library(SSL_EAY_RELEASE NAMES ssleay32MT - ${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES "lib/VC/static" - ) - - set(LIB_EAY_LIBRARY_DEBUG "${LIB_EAY_DEBUG}") - set(LIB_EAY_LIBRARY_RELEASE "${LIB_EAY_RELEASE}") - set(SSL_EAY_LIBRARY_DEBUG "${SSL_EAY_DEBUG}") - set(SSL_EAY_LIBRARY_RELEASE "${SSL_EAY_RELEASE}") + # Using vcpkg builds of openssl + find_library(LIB_EAY_LIBRARY_RELEASE NAMES libeay32 HINTS ${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES "lib") + find_library(SSL_EAY_LIBRARY_RELEASE NAMES ssleay32 HINTS ${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES "lib") include(SelectLibraryConfigurations) select_library_configurations(LIB_EAY) select_library_configurations(SSL_EAY) - set(OPENSSL_LIBRARIES ${SSL_EAY_LIBRARY} ${LIB_EAY_LIBRARY}) - find_path(OPENSSL_DLL_PATH NAMES ssleay32.dll PATH_SUFFIXES "bin" ${_OPENSSL_ROOT_HINTS_AND_PATHS}) endif() else() diff --git a/cmake/templates/CPackProperties.cmake.in b/cmake/templates/CPackProperties.cmake.in index c1e3d9d773..b91d78f628 100644 --- a/cmake/templates/CPackProperties.cmake.in +++ b/cmake/templates/CPackProperties.cmake.in @@ -40,6 +40,7 @@ set(CONSOLE_DESKTOP_SHORTCUT_REG_KEY "@CONSOLE_DESKTOP_SHORTCUT_REG_KEY@") set(CONSOLE_STARTUP_REG_KEY "@CONSOLE_STARTUP_REG_KEY@") set(SERVER_LAUNCH_NOW_REG_KEY "@SERVER_LAUNCH_NOW_REG_KEY@") set(CLIENT_LAUNCH_NOW_REG_KEY "@CLIENT_LAUNCH_NOW_REG_KEY@") +set(CUSTOM_INSTALL_REG_KEY "@CUSTOM_INSTALL_REG_KEY@") set(INSTALLER_HEADER_IMAGE "@INSTALLER_HEADER_IMAGE@") set(UNINSTALLER_HEADER_IMAGE "@UNINSTALLER_HEADER_IMAGE@") set(ADD_REMOVE_ICON_PATH "@ADD_REMOVE_ICON_PATH@") diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index de79b49a74..5eedbb06ed 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -49,7 +49,7 @@ Var STR_CONTAINS_VAR_3 Var STR_CONTAINS_VAR_4 Var STR_RETURN_VAR - + Function StrContains Exch $STR_NEEDLE Exch 1 @@ -343,29 +343,29 @@ SectionEnd ;-------------------------------- ;Pages !insertmacro MUI_PAGE_WELCOME - + !insertmacro MUI_PAGE_LICENSE "@CPACK_RESOURCE_FILE_LICENSE@" - + Page custom InstallTypesPage ReadInstallTypes - + !define MUI_PAGE_CUSTOMFUNCTION_PRE AbortFunction !insertmacro MUI_PAGE_DIRECTORY - + ;Start Menu Folder Page Configuration !define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKLM" !define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" - + !define MUI_PAGE_CUSTOMFUNCTION_PRE AbortFunction !insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER - + !define MUI_PAGE_CUSTOMFUNCTION_PRE AbortFunction @CPACK_NSIS_PAGE_COMPONENTS@ - + Page custom PostInstallOptionsPage ReadPostInstallOptions !insertmacro MUI_PAGE_INSTFILES - + !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES @@ -453,9 +453,10 @@ Var ExpressInstallRadioButton Var CustomInstallRadioButton Var InstallTypeDialog Var Express +Var CustomInstallTemporaryState -!macro SetPostInstallOption Checkbox OptionName Default - ; reads the value for the given post install option to the registry +!macro SetInstallOption Checkbox OptionName Default + ; reads the value for the given install option to the registry ReadRegStr $0 HKLM "@REGISTRY_HKLM_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@\@POST_INSTALL_OPTIONS_REG_GROUP@" "${OptionName}" ${If} $0 == "NO" @@ -472,31 +473,39 @@ Var Express Function InstallTypesPage !insertmacro MUI_HEADER_TEXT "Choose Installation Type" "Express or Custom Install" - + nsDialogs::Create 1018 Pop $InstallTypeDialog - + ${If} $InstallTypeDialog == error Abort ${EndIf} - + StrCpy $CurrentOffset 0 StrCpy $OffsetUnits u - StrCpy $Express "0" - + StrCpy $Express "0" + ${NSD_CreateRadioButton} 30% $CurrentOffset$OffsetUnits 100% 10u "Express Install (Recommended)"; $\nInstalls High Fidelity Interface and High Fidelity Sandbox" pop $ExpressInstallRadioButton ${NSD_OnClick} $ExpressInstallRadioButton ChangeExpressLabel IntOp $CurrentOffset $CurrentOffset + 15 - + ${NSD_CreateRadiobutton} 30% $CurrentOffset$OffsetUnits 100% 10u "Custom Install (Advanced)" pop $CustomInstallRadioButton - ${NSD_OnClick} $CustomInstallRadioButton ChangeCustomLabel - - ; Express Install selected by default - ${NSD_Check} $ExpressInstallRadioButton + ${NSD_OnClick} $CustomInstallRadioButton ChangeCustomLabel + + ; check install type from the registry, express install by default + !insertmacro SetInstallOption $CustomInstallRadioButton @CUSTOM_INSTALL_REG_KEY@ ${BST_UNCHECKED} + + ; set the express install value based on the custom install value from registry + ${NSD_GetState} $CustomInstallRadioButton $CustomInstallTemporaryState + + ${If} $CustomInstallTemporaryState == ${BST_UNCHECKED} + ${NSD_Check} $ExpressInstallRadioButton + ${EndIf} + Call ChangeExpressLabel - + nsDialogs::Show FunctionEnd @@ -519,18 +528,18 @@ Function AbortFunction StrCmp $Express "1" 0 end Abort end: -FunctionEnd +FunctionEnd Function PostInstallOptionsPage !insertmacro MUI_HEADER_TEXT "Setup Options" "" nsDialogs::Create 1018 Pop $PostInstallDialog - + ${If} $PostInstallDialog == error Abort ${EndIf} - + ; Check if Express is set, if so, abort the post install options page StrCmp $Express "1" 0 end Abort @@ -543,18 +552,18 @@ Function PostInstallOptionsPage ${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Create a desktop shortcut for @INTERFACE_HF_SHORTCUT_NAME@" Pop $DesktopClientCheckbox IntOp $CurrentOffset $CurrentOffset + 15 - + ; set the checkbox state depending on what is present in the registry - !insertmacro SetPostInstallOption $DesktopClientCheckbox @CLIENT_DESKTOP_SHORTCUT_REG_KEY@ ${BST_CHECKED} + !insertmacro SetInstallOption $DesktopClientCheckbox @CLIENT_DESKTOP_SHORTCUT_REG_KEY@ ${BST_CHECKED} ${EndIf} - + ${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@} ${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Create a desktop shortcut for @CONSOLE_HF_SHORTCUT_NAME@" Pop $DesktopServerCheckbox IntOp $CurrentOffset $CurrentOffset + 15 - + ; set the checkbox state depending on what is present in the registry - !insertmacro SetPostInstallOption $DesktopServerCheckbox @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ ${BST_UNCHECKED} + !insertmacro SetInstallOption $DesktopServerCheckbox @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ ${BST_UNCHECKED} ${EndIf} ${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@} @@ -562,7 +571,7 @@ Function PostInstallOptionsPage Pop $LaunchServerNowCheckbox ; set the checkbox state depending on what is present in the registry - !insertmacro SetPostInstallOption $LaunchServerNowCheckbox @SERVER_LAUNCH_NOW_REG_KEY@ ${BST_CHECKED} + !insertmacro SetInstallOption $LaunchServerNowCheckbox @SERVER_LAUNCH_NOW_REG_KEY@ ${BST_CHECKED} ${StrContains} $substringResult "/forceNoLaunchServer" $CMDLINE ${IfNot} $substringResult == "" ${NSD_SetState} $LaunchServerNowCheckbox ${BST_UNCHECKED} @@ -570,29 +579,29 @@ Function PostInstallOptionsPage IntOp $CurrentOffset $CurrentOffset + 15 ${EndIf} - + ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} ${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Launch @INTERFACE_HF_SHORTCUT_NAME@ after install" Pop $LaunchClientNowCheckbox IntOp $CurrentOffset $CurrentOffset + 30 - + ; set the checkbox state depending on what is present in the registry - !insertmacro SetPostInstallOption $LaunchClientNowCheckbox @CLIENT_LAUNCH_NOW_REG_KEY@ ${BST_CHECKED} + !insertmacro SetInstallOption $LaunchClientNowCheckbox @CLIENT_LAUNCH_NOW_REG_KEY@ ${BST_CHECKED} ${StrContains} $substringResult "/forceNoLaunchClient" $CMDLINE ${IfNot} $substringResult == "" ${NSD_SetState} $LaunchClientNowCheckbox ${BST_UNCHECKED} ${EndIf} ${EndIf} - + ${If} ${SectionIsSelected} ${@SERVER_COMPONENT_NAME@} ${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Launch @CONSOLE_HF_SHORTCUT_NAME@ on startup" Pop $ServerStartupCheckbox IntOp $CurrentOffset $CurrentOffset + 15 - + ; set the checkbox state depending on what is present in the registry - !insertmacro SetPostInstallOption $ServerStartupCheckbox @CONSOLE_STARTUP_REG_KEY@ ${BST_CHECKED} + !insertmacro SetInstallOption $ServerStartupCheckbox @CONSOLE_STARTUP_REG_KEY@ ${BST_CHECKED} ${EndIf} - + ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} ${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Perform a clean install (Delete older settings and content)" Pop $CleanInstallCheckbox @@ -618,12 +627,12 @@ Function PostInstallOptionsPage ${NSD_SetState} $CopyFromProductionCheckbox ${BST_UNCHECKED} ${EndIf} - + nsDialogs::Show FunctionEnd -!macro WritePostInstallOption OptionName Option - ; writes the value for the given post install option to the registry +!macro WriteInstallOption OptionName Option + ; writes the value for the given install option to the registry WriteRegStr HKLM "@REGISTRY_HKLM_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@\@POST_INSTALL_OPTIONS_REG_GROUP@" "${OptionName}" ${Option} !macroend @@ -641,12 +650,12 @@ Function ReadInstallTypes ; check if the user asked for express/custom install ${NSD_GetState} $ExpressInstallRadioButton $ExpressInstallState ${NSD_GetState} $CustomInstallRadioButton $CustomInstallState - + ${If} $ExpressInstallState == ${BST_CHECKED} StrCpy $Express "1" - - StrCpy $DesktopClientState ${BST_CHECKED} - StrCpy $ServerStartupState ${BST_CHECKED} + + StrCpy $DesktopClientState ${BST_CHECKED} + StrCpy $ServerStartupState ${BST_CHECKED} StrCpy $LaunchServerNowState ${BST_CHECKED} StrCpy $LaunchClientNowState ${BST_CHECKED} StrCpy $CleanInstallState ${BST_UNCHECKED} @@ -655,9 +664,12 @@ Function ReadInstallTypes ${If} @PR_BUILD@ == 1 StrCpy $CopyFromProductionState ${BST_UNCHECKED} ${EndIf} - + + !insertmacro WriteInstallOption "@CUSTOM_INSTALL_REG_KEY@" NO + ${Else} + !insertmacro WriteInstallOption "@CUSTOM_INSTALL_REG_KEY@" YES ${EndIf} - + FunctionEnd Function ReadPostInstallOptions @@ -683,12 +695,12 @@ Function ReadPostInstallOptions ; check if we need to launch the server post-install ${NSD_GetState} $LaunchServerNowCheckbox $LaunchServerNowState ${EndIf} - + ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} ; check if we need to launch the client post-install ${NSD_GetState} $LaunchClientNowCheckbox $LaunchClientNowState ${EndIf} - + ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} ; check if the user asked for a clean install ${NSD_GetState} $CleanInstallCheckbox $CleanInstallState @@ -700,9 +712,9 @@ Function HandlePostInstallOptions ; check if the user asked for a desktop shortcut to High Fidelity ${If} $DesktopClientState == ${BST_CHECKED} CreateShortCut "$DESKTOP\@INTERFACE_HF_SHORTCUT_NAME@.lnk" "$INSTDIR\@INTERFACE_WIN_EXEC_NAME@" - !insertmacro WritePostInstallOption "@CLIENT_DESKTOP_SHORTCUT_REG_KEY@" YES + !insertmacro WriteInstallOption "@CLIENT_DESKTOP_SHORTCUT_REG_KEY@" YES ${Else} - !insertmacro WritePostInstallOption @CLIENT_DESKTOP_SHORTCUT_REG_KEY@ NO + !insertmacro WriteInstallOption @CLIENT_DESKTOP_SHORTCUT_REG_KEY@ NO ${EndIf} ${EndIf} @@ -711,12 +723,12 @@ Function HandlePostInstallOptions ; check if the user asked for a desktop shortcut to Sandbox ${If} $DesktopServerState == ${BST_CHECKED} CreateShortCut "$DESKTOP\@CONSOLE_HF_SHORTCUT_NAME@.lnk" "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@" - !insertmacro WritePostInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ YES + !insertmacro WriteInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ YES ${Else} - !insertmacro WritePostInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ NO + !insertmacro WriteInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ NO ${EndIf} - + ; check if the user asked to have Sandbox launched every startup ${If} $ServerStartupState == ${BST_CHECKED} ; in case we added a shortcut in the global context, pull that now @@ -730,12 +742,12 @@ Function HandlePostInstallOptions ; reset the shell var context back SetShellVarContext all - !insertmacro WritePostInstallOption @CONSOLE_STARTUP_REG_KEY@ YES + !insertmacro WriteInstallOption @CONSOLE_STARTUP_REG_KEY@ YES ${Else} - !insertmacro WritePostInstallOption @CONSOLE_STARTUP_REG_KEY@ NO + !insertmacro WriteInstallOption @CONSOLE_STARTUP_REG_KEY@ NO ${EndIf} ${EndIf} - + ${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@} ; check if the user asked for a clean install ${If} $CleanInstallState == ${BST_CHECKED} @@ -774,28 +786,28 @@ Function HandlePostInstallOptions ${EndIf} ${If} $LaunchServerNowState == ${BST_CHECKED} - !insertmacro WritePostInstallOption @SERVER_LAUNCH_NOW_REG_KEY@ YES + !insertmacro WriteInstallOption @SERVER_LAUNCH_NOW_REG_KEY@ YES ; both launches use the explorer trick in case the user has elevated permissions for the installer ${If} $LaunchClientNowState == ${BST_CHECKED} - !insertmacro WritePostInstallOption @CLIENT_LAUNCH_NOW_REG_KEY@ YES + !insertmacro WriteInstallOption @CLIENT_LAUNCH_NOW_REG_KEY@ YES ; create shortcut with ARGUMENTS CreateShortCut "$TEMP\SandboxShortcut.lnk" "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@" "-- --launchInterface" Exec '"$WINDIR\explorer.exe" "$TEMP\SandboxShortcut.lnk"' ${Else} - !insertmacro WritePostInstallOption @CLIENT_LAUNCH_NOW_REG_KEY@ NO + !insertmacro WriteInstallOption @CLIENT_LAUNCH_NOW_REG_KEY@ NO Exec '"$WINDIR\explorer.exe" "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@"' ${EndIf} ${Else} - !insertmacro WritePostInstallOption @SERVER_LAUNCH_NOW_REG_KEY@ NO + !insertmacro WriteInstallOption @SERVER_LAUNCH_NOW_REG_KEY@ NO ; launch uses the explorer trick in case the user has elevated permissions for the installer ${If} $LaunchClientNowState == ${BST_CHECKED} - !insertmacro WritePostInstallOption @CLIENT_LAUNCH_NOW_REG_KEY@ YES + !insertmacro WriteInstallOption @CLIENT_LAUNCH_NOW_REG_KEY@ YES Exec '"$WINDIR\explorer.exe" "$INSTDIR\@INTERFACE_WIN_EXEC_NAME@"' ${Else} - !insertmacro WritePostInstallOption @CLIENT_LAUNCH_NOW_REG_KEY@ NO + !insertmacro WriteInstallOption @CLIENT_LAUNCH_NOW_REG_KEY@ NO ${EndIf} ${EndIf} @@ -843,7 +855,7 @@ Section "-Core installation" Rename "$INSTDIR\resources\qml\styles-uit\RalewaySemibold.qml" "$INSTDIR\resources\qml\styles-uit\RalewaySemiBold.qml" ExecWait "$INSTDIR\vcredist_x64.exe /install /q /norestart" - + ; Remove the Old Interface directory and vcredist_x64.exe (from installs prior to Server Console) RMDir /r "$INSTDIR\Interface" Delete "$INSTDIR\vcredist_x64.exe" @@ -943,9 +955,9 @@ Section "-Core installation" Call ConditionalAddToRegisty !insertmacro MUI_STARTMENU_WRITE_END - + @CPACK_NSIS_EXTRA_INSTALL_COMMANDS@ - + ; Handle whichever post install options were set Call HandlePostInstallOptions @@ -963,9 +975,18 @@ SectionEnd ${If} $R0 == 0 ; the process is running, ask the user to close it + + ${If} "${displayName}" == "@CONSOLE_DISPLAY_NAME@" MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION \ - "${displayName} cannot be ${action} while ${displayName} is running.$\r$\nPlease close it and click Retry to continue." \ + "${displayName} cannot be ${action} while ${displayName} is running.$\r$\nPlease close it in the system tray and click Retry to continue." \ /SD IDCANCEL IDRETRY Prompt_${UniqueID} IDCANCEL 0 + ${EndIf} + + ${If} "${displayName}" == "@INTERFACE_DISPLAY_NAME@" + MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION \ + "${displayName} cannot be ${action} while ${displayName} is running.$\r$\nPlease close it in the task bar and click Retry to continue." \ + /SD IDCANCEL IDRETRY Prompt_${UniqueID} IDCANCEL 0 + ${EndIf} ; If the user decided to cancel, stop the current installer/uninstaller Abort diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 5bd6020c92..b7336e1505 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -752,8 +752,28 @@ void DomainServer::setupICEHeartbeatForFullNetworking() { } void DomainServer::updateICEServerAddresses() { - if (_iceAddressLookupID == -1) { + if (_iceAddressLookupID == INVALID_ICE_LOOKUP_ID) { _iceAddressLookupID = QHostInfo::lookupHost(_iceServerAddr, this, SLOT(handleICEHostInfo(QHostInfo))); + + // there seems to be a 5.9 bug where lookupHost never calls our slot + // so we add a single shot manual "timeout" to fire it off again if it hasn't called back yet + static const int ICE_ADDRESS_LOOKUP_TIMEOUT_MS = 5000; + QTimer::singleShot(ICE_ADDRESS_LOOKUP_TIMEOUT_MS, this, &DomainServer::timeoutICEAddressLookup); + } +} + +void DomainServer::timeoutICEAddressLookup() { + if (_iceAddressLookupID != INVALID_ICE_LOOKUP_ID) { + // we waited 5s and didn't hear back for our ICE DNS lookup + // so time that one out and kick off another + + qDebug() << "IP address lookup timed out for" << _iceServerAddr << "- retrying"; + + QHostInfo::abortHostLookup(_iceAddressLookupID); + + _iceAddressLookupID = INVALID_ICE_LOOKUP_ID; + + updateICEServerAddresses(); } } diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 03ad76d313..52ac435517 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -39,6 +39,8 @@ typedef QMultiHash TransactionHash; using Subnet = QPair; using SubnetList = std::vector; +const int INVALID_ICE_LOOKUP_ID = -1; + enum ReplicationServerDirection { Upstream, Downstream @@ -114,6 +116,8 @@ private slots: void tokenGrantFinished(); void profileRequestFinished(); + void timeoutICEAddressLookup(); + signals: void iceServerChanged(); void userConnected(); @@ -223,7 +227,7 @@ private: QList _iceServerAddresses; QSet _failedIceServerAddresses; - int _iceAddressLookupID { -1 }; + int _iceAddressLookupID { INVALID_ICE_LOOKUP_ID }; int _noReplyICEHeartbeats { 0 }; int _numHeartbeatDenials { 0 }; bool _connectedToICEServer { false }; diff --git a/interface/resources/qml/TabletBrowser.qml b/interface/resources/qml/TabletBrowser.qml index 0b06a6e2a1..8dbcc8f4f8 100644 --- a/interface/resources/qml/TabletBrowser.qml +++ b/interface/resources/qml/TabletBrowser.qml @@ -1,7 +1,7 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 import QtWebChannel 1.0 -import QtWebEngine 1.2 +import QtWebEngine 1.5 import "controls" import "controls-uit" as HifiControls @@ -13,11 +13,11 @@ Item { id: root HifiConstants { id: hifi } HifiStyles.HifiConstants { id: hifistyles } - //width: parent.width + height: 600 property variant permissionsBar: {'securityOrigin':'none','feature':'none'} property alias url: webview.url - property WebEngineView webView: webview + property bool canGoBack: webview.canGoBack property bool canGoForward: webview.canGoForward @@ -123,5 +123,4 @@ Item { break; } } - } diff --git a/interface/resources/qml/controls-uit/BaseWebView.qml b/interface/resources/qml/controls-uit/BaseWebView.qml index 3ca57f03bf..660f52d529 100644 --- a/interface/resources/qml/controls-uit/BaseWebView.qml +++ b/interface/resources/qml/controls-uit/BaseWebView.qml @@ -9,7 +9,7 @@ // import QtQuick 2.5 -import QtWebEngine 1.2 +import QtWebEngine 1.5 WebEngineView { id: root diff --git a/interface/resources/qml/controls-uit/WebSpinner.qml b/interface/resources/qml/controls-uit/WebSpinner.qml index 6323bff1a7..e8e01c4865 100644 --- a/interface/resources/qml/controls-uit/WebSpinner.qml +++ b/interface/resources/qml/controls-uit/WebSpinner.qml @@ -9,10 +9,13 @@ // import QtQuick 2.5 +import QtWebEngine 1.5 AnimatedImage { + property WebEngineView webview: parent source: "../../icons/loader-snake-64-w.gif" - visible: parent.loading && /^(http.*|)$/i.test(parent.url.toString()) + visible: webview.loading && /^(http.*|)$/i.test(webview.url.toString()) + playing: visible z: 10000 anchors { horizontalCenter: parent.horizontalCenter diff --git a/interface/resources/qml/controls/FlickableWebViewCore.qml b/interface/resources/qml/controls/FlickableWebViewCore.qml new file mode 100644 index 0000000000..0460b5b16a --- /dev/null +++ b/interface/resources/qml/controls/FlickableWebViewCore.qml @@ -0,0 +1,170 @@ +import QtQuick 2.7 +import QtWebEngine 1.5 +import QtWebChannel 1.0 + +import QtQuick.Controls 2.2 + +import "../styles-uit" as StylesUIt + +Flickable { + id: flick + + property alias url: _webview.url + property alias canGoBack: _webview.canGoBack + property alias webViewCore: _webview + property alias webViewCoreProfile: _webview.profile + + property string userScriptUrl: "" + property string urlTag: "noDownload=false"; + + signal newViewRequestedCallback(var request) + signal loadingChangedCallback(var loadRequest) + + boundsBehavior: Flickable.StopAtBounds + + StylesUIt.HifiConstants { + id: hifi + } + + onHeightChanged: { + if (height > 0) { + //reload page since window dimentions changed, + //so web engine should recalculate page render dimentions + reloadTimer.start() + } + } + + ScrollBar.vertical: ScrollBar { + id: scrollBar + visible: flick.contentHeight > flick.height + + contentItem: Rectangle { + opacity: 0.75 + implicitWidth: hifi.dimensions.scrollbarHandleWidth + radius: height / 2 + color: hifi.colors.tableScrollHandleDark + } + } + + function onLoadingChanged(loadRequest) { + if (WebEngineView.LoadStartedStatus === loadRequest.status) { + flick.contentWidth = flick.width + flick.contentHeight = flick.height + + // Required to support clicking on "hifi://" links + var url = loadRequest.url.toString(); + if (urlHandler.canHandleUrl(url)) { + if (urlHandler.handleUrl(url)) { + _webview.stop(); + } + } + } + + if (WebEngineView.LoadFailedStatus === loadRequest.status) { + console.log(" Tablet WebEngineView failed to load url: " + loadRequest.url.toString()); + } + + if (WebEngineView.LoadSucceededStatus === loadRequest.status) { + //disable Chromium's scroll bars + _webview.runJavaScript("document.body.style.overflow = 'hidden';"); + //calculate page height + _webview.runJavaScript("document.body.scrollHeight;", function (i_actualPageHeight) { + if (i_actualPageHeight !== undefined) { + flick.contentHeight = i_actualPageHeight + } else { + flick.contentHeight = flick.height; + } + }) + flick.contentWidth = flick.width + } + } + + Timer { + id: reloadTimer + interval: 100 + repeat: false + onTriggered: { + _webview.reload() + } + } + + WebEngineView { + id: _webview + + height: parent.height + + profile: HFWebEngineProfile; + + // creates a global EventBridge object. + WebEngineScript { + id: createGlobalEventBridge + sourceCode: eventBridgeJavaScriptToInject + injectionPoint: WebEngineScript.DocumentCreation + worldId: WebEngineScript.MainWorld + } + + // detects when to raise and lower virtual keyboard + WebEngineScript { + id: raiseAndLowerKeyboard + injectionPoint: WebEngineScript.Deferred + sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js" + worldId: WebEngineScript.MainWorld + } + + // User script. + WebEngineScript { + id: userScript + sourceUrl: flick.userScriptUrl + injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished. + worldId: WebEngineScript.MainWorld + } + + userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ] + + property string newUrl: "" + + Component.onCompleted: { + width = Qt.binding(function() { return flick.width; }); + webChannel.registerObject("eventBridge", eventBridge); + webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper); + // Ensure the JS from the web-engine makes it to our logging + _webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) { + console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message); + }); + _webview.profile.httpUserAgent = "Mozilla/5.0 Chrome (HighFidelityInterface)"; + + } + + onFeaturePermissionRequested: { + grantFeaturePermission(securityOrigin, feature, true); + } + + onContentsSizeChanged: { + flick.contentHeight = Math.max(contentsSize.height, flick.height); + flick.contentWidth = flick.width + } + //disable popup + onContextMenuRequested: { + request.accepted = true; + } + + onNewViewRequested: { + newViewRequestedCallback(request) + } + + onLoadingChanged: { + flick.onLoadingChanged(loadRequest) + loadingChangedCallback(loadRequest) + } + } + + AnimatedImage { + //anchoring doesnt works here when changing content size + x: flick.width/2 - width/2 + y: flick.height/2 - height/2 + source: "../../icons/loader-snake-64-w.gif" + visible: _webview.loading && /^(http.*|)$/i.test(_webview.url.toString()) + playing: visible + z: 10000 + } +} diff --git a/interface/resources/qml/controls/TabletWebScreen.qml b/interface/resources/qml/controls/TabletWebScreen.qml index 1de31aba41..e06ff51569 100644 --- a/interface/resources/qml/controls/TabletWebScreen.qml +++ b/interface/resources/qml/controls/TabletWebScreen.qml @@ -1,14 +1,13 @@ -import QtQuick 2.5 -import QtWebEngine 1.1 -import QtWebChannel 1.0 +import QtQuick 2.7 import "../controls-uit" as HiFiControls Item { - property alias url: root.url - property alias scriptURL: root.userScriptUrl - property alias canGoBack: root.canGoBack; - property var goBack: root.goBack; - property alias urlTag: root.urlTag + id: root + property alias url: webroot.url + property alias scriptURL: webroot.userScriptUrl + property alias canGoBack: webroot.canGoBack; + property var goBack: webroot.webViewCore.goBack; + property alias urlTag: webroot.urlTag property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false property bool keyboardRaised: false property bool punctuationMode: false @@ -21,84 +20,20 @@ Item { } */ - property alias viewProfile: root.profile + property alias viewProfile: webroot.webViewCoreProfile - WebEngineView { - id: root - objectName: "webEngineView" - x: 0 - y: 0 + FlickableWebViewCore { + id: webroot width: parent.width height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height - profile: HFWebEngineProfile; - - property string userScriptUrl: "" - - // creates a global EventBridge object. - WebEngineScript { - id: createGlobalEventBridge - sourceCode: eventBridgeJavaScriptToInject - injectionPoint: WebEngineScript.DocumentCreation - worldId: WebEngineScript.MainWorld - } - - // detects when to raise and lower virtual keyboard - WebEngineScript { - id: raiseAndLowerKeyboard - injectionPoint: WebEngineScript.Deferred - sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js" - worldId: WebEngineScript.MainWorld - } - - // User script. - WebEngineScript { - id: userScript - sourceUrl: root.userScriptUrl - injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished. - worldId: WebEngineScript.MainWorld - } - - property string urlTag: "noDownload=false"; - - userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ] - - property string newUrl: "" - - - Component.onCompleted: { - webChannel.registerObject("eventBridge", eventBridge); - webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper); - // Ensure the JS from the web-engine makes it to our logging - root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) { - console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message); - }); - - root.profile.httpUserAgent = "Mozilla/5.0 Chrome (HighFidelityInterface)"; - } - - onFeaturePermissionRequested: { - grantFeaturePermission(securityOrigin, feature, true); - } - - onLoadingChanged: { + onLoadingChangedCallback: { keyboardRaised = false; punctuationMode = false; keyboard.resetShiftMode(false); - - // Required to support clicking on "hifi://" links - if (WebEngineView.LoadStartedStatus == loadRequest.status) { - var url = loadRequest.url.toString(); - url = (url.indexOf("?") >= 0) ? url + urlTag : url + "?" + urlTag; - if (urlHandler.canHandleUrl(url)) { - if (urlHandler.handleUrl(url)) { - root.stop(); - } - } - } } - onNewViewRequested:{ + onNewViewRequestedCallback: { // desktop is not defined for web-entities or tablet if (typeof desktop !== "undefined") { desktop.openBrowserWindow(request, profile); @@ -107,7 +42,6 @@ Item { } } - HiFiControls.WebSpinner { } } HiFiControls.Keyboard { @@ -120,5 +54,4 @@ Item { bottom: parent.bottom } } - } diff --git a/interface/resources/qml/controls/TabletWebView.qml b/interface/resources/qml/controls/TabletWebView.qml index 8547f67468..23143db3ba 100644 --- a/interface/resources/qml/controls/TabletWebView.qml +++ b/interface/resources/qml/controls/TabletWebView.qml @@ -1,18 +1,14 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtWebEngine 1.2 -import QtWebChannel 1.0 +import QtQuick 2.7 +import QtWebEngine 1.5 import "../controls-uit" as HiFiControls import "../styles" as HifiStyles import "../styles-uit" -import "../" -import "." Item { - id: web + id: root HifiConstants { id: hifi } - width: parent.width - height: parent.height + width: parent !== null ? parent.width : undefined + height: parent !== null ? parent.height : undefined property var parentStackItem: null property int headerHeight: 70 property string url @@ -21,8 +17,8 @@ Item { property bool keyboardRaised: false property bool punctuationMode: false property bool isDesktop: false - property alias webView: webview - property alias profile: webview.profile + property alias webView: web.webViewCore + property alias profile: web.webViewCoreProfile property bool remove: false property bool closeButtonVisible: true @@ -79,7 +75,7 @@ Item { color: hifi.colors.baseGray font.pixelSize: 12 verticalAlignment: Text.AlignLeft - text: webview.url + text: root.url anchors { top: nav.bottom horizontalCenter: parent.horizontalCenter; @@ -104,13 +100,13 @@ Item { function closeWebEngine() { if (remove) { - web.destroy(); + root.destroy(); return; } if (parentStackItem) { parentStackItem.pop(); } else { - web.visible = false; + root.visible = false; } } @@ -128,67 +124,19 @@ Item { } function loadUrl(url) { - webview.url = url - web.url = webview.url; + web.webViewCore.url = url + root.url = web.webViewCore.url; } onUrlChanged: { loadUrl(url); } - WebEngineView { - id: webview - objectName: "webEngineView" - x: 0 - y: 0 + FlickableWebViewCore { + id: web width: parent.width - height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height - web.headerHeight : parent.height - web.headerHeight + height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height - root.headerHeight : parent.height - root.headerHeight anchors.top: buttons.bottom - profile: HFWebEngineProfile; - - property string userScriptUrl: "" - - // creates a global EventBridge object. - WebEngineScript { - id: createGlobalEventBridge - sourceCode: eventBridgeJavaScriptToInject - injectionPoint: WebEngineScript.DocumentCreation - worldId: WebEngineScript.MainWorld - } - - // detects when to raise and lower virtual keyboard - WebEngineScript { - id: raiseAndLowerKeyboard - injectionPoint: WebEngineScript.Deferred - sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js" - worldId: WebEngineScript.MainWorld - } - - // User script. - WebEngineScript { - id: userScript - sourceUrl: webview.userScriptUrl - injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished. - worldId: WebEngineScript.MainWorld - } - - property string urlTag: "noDownload=false"; - userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ] - - property string newUrl: "" - - Component.onCompleted: { - webChannel.registerObject("eventBridge", eventBridge); - webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper); - // Ensure the JS from the web-engine makes it to our logging - webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) { - console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message); - }); - } - - onFeaturePermissionRequested: { - grantFeaturePermission(securityOrigin, feature, true); - } onUrlChanged: { // Record history, skipping null and duplicate items. @@ -201,34 +149,16 @@ Item { } } - onLoadingChanged: { + onLoadingChangedCallback: { keyboardRaised = false; punctuationMode = false; keyboard.resetShiftMode(false); - // Required to support clicking on "hifi://" links - if (WebEngineView.LoadStartedStatus == loadRequest.status) { - var url = loadRequest.url.toString(); - if (urlHandler.canHandleUrl(url)) { - if (urlHandler.handleUrl(url)) { - root.stop(); - } - } - } - - if (WebEngineView.LoadFailedStatus == loadRequest.status) { - console.log(" Tablet WebEngineView failed to load url: " + loadRequest.url.toString()); - } - - if (WebEngineView.LoadSucceededStatus == loadRequest.status) { - webview.forceActiveFocus(); - } - } - - onNewViewRequested: { - request.openIn(webview); + webViewCore.forceActiveFocus(); } - HiFiControls.WebSpinner { } + onNewViewRequestedCallback: { + request.openIn(webViewCore); + } } HiFiControls.Keyboard { @@ -244,7 +174,7 @@ Item { } Component.onCompleted: { - web.isDesktop = (typeof desktop !== "undefined"); + root.isDesktop = (typeof desktop !== "undefined"); keyboardEnabled = HMD.active; } diff --git a/interface/resources/qml/controls/WebView.qml b/interface/resources/qml/controls/WebView.qml index 3f2ac6363b..c38c5df9cf 100644 --- a/interface/resources/qml/controls/WebView.qml +++ b/interface/resources/qml/controls/WebView.qml @@ -1,17 +1,19 @@ -import QtQuick 2.5 -import QtWebEngine 1.1 -import QtWebChannel 1.0 +import QtQuick 2.7 import "../controls-uit" as HiFiControls Item { - property alias url: root.url - property alias scriptURL: root.userScriptUrl - property alias canGoBack: root.canGoBack; - property var goBack: root.goBack; - property alias urlTag: root.urlTag + width: parent !== null ? parent.width : undefined + height: parent !== null ? parent.height : undefined + + property alias url: webroot.url + property alias scriptURL: webroot.userScriptUrl + property alias canGoBack: webroot.canGoBack; + property var goBack: webroot.webViewCore.goBack; + property alias urlTag: webroot.urlTag property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false property bool keyboardRaised: false property bool punctuationMode: false + property alias flickable: webroot.interactive // FIXME - Keyboard HMD only: Make Interface either set keyboardRaised property directly in OffscreenQmlSurface // or provide HMDinfo object to QML in RenderableWebEntityItem and do the following. @@ -21,82 +23,20 @@ Item { } */ - property alias viewProfile: root.profile + property alias viewProfile: webroot.webViewCoreProfile - WebEngineView { - id: root - objectName: "webEngineView" - x: 0 - y: 0 + FlickableWebViewCore { + id: webroot width: parent.width height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height - profile: HFWebEngineProfile; - - property string userScriptUrl: "" - - // creates a global EventBridge object. - WebEngineScript { - id: createGlobalEventBridge - sourceCode: eventBridgeJavaScriptToInject - injectionPoint: WebEngineScript.DocumentCreation - worldId: WebEngineScript.MainWorld - } - - // detects when to raise and lower virtual keyboard - WebEngineScript { - id: raiseAndLowerKeyboard - injectionPoint: WebEngineScript.Deferred - sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js" - worldId: WebEngineScript.MainWorld - } - - // User script. - WebEngineScript { - id: userScript - sourceUrl: root.userScriptUrl - injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished. - worldId: WebEngineScript.MainWorld - } - - property string urlTag: "noDownload=false"; - - userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ] - - property string newUrl: "" - - Component.onCompleted: { - webChannel.registerObject("eventBridge", eventBridge); - webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper); - // Ensure the JS from the web-engine makes it to our logging - root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) { - console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message); - }); - - } - - onFeaturePermissionRequested: { - grantFeaturePermission(securityOrigin, feature, true); - } - - onLoadingChanged: { + onLoadingChangedCallback: { keyboardRaised = false; punctuationMode = false; keyboard.resetShiftMode(false); - - // Required to support clicking on "hifi://" links - if (WebEngineView.LoadStartedStatus == loadRequest.status) { - var url = loadRequest.url.toString(); - url = (url.indexOf("?") >= 0) ? url + urlTag : url + "?" + urlTag; - if (urlHandler.canHandleUrl(url)) { - if (urlHandler.handleUrl(url)) { - root.stop(); - } - } - } } - onNewViewRequested:{ + onNewViewRequestedCallback: { // desktop is not defined for web-entities or tablet if (typeof desktop !== "undefined") { desktop.openBrowserWindow(request, profile); @@ -104,8 +44,6 @@ Item { tabletRoot.openBrowserWindow(request, profile); } } - - HiFiControls.WebSpinner { } } HiFiControls.Keyboard { @@ -118,5 +56,4 @@ Item { bottom: parent.bottom } } - } diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 93742e39a5..a819e032eb 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -36,6 +36,24 @@ Rectangle { return (root.parent !== null) && root.parent.objectName == "loader"; } + property bool showPeaks: true; + function enablePeakValues() { + Audio.devices.input.peakValuesEnabled = true; + Audio.devices.input.peakValuesEnabledChanged.connect(function(enabled) { + if (!enabled && root.showPeaks) { + Audio.devices.input.peakValuesEnabled = true; + } + }); + } + function disablePeakValues() { + root.showPeaks = false; + Audio.devices.input.peakValuesEnabled = false; + } + + Component.onCompleted: enablePeakValues(); + Component.onDestruction: disablePeakValues(); + onVisibleChanged: visible ? enablePeakValues() : disablePeakValues(); + Column { y: 16; // padding does not work spacing: 16; @@ -133,12 +151,13 @@ Rectangle { onClicked: Audio.setInputDevice(info); } - InputLevel { - id: level; + InputPeak { + id: inputPeak; + visible: Audio.devices.input.peakValuesAvailable; + peak: model.peak; anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right anchors.rightMargin: 30 - visible: selected; } } } diff --git a/interface/resources/qml/hifi/audio/InputLevel.qml b/interface/resources/qml/hifi/audio/InputPeak.qml similarity index 95% rename from interface/resources/qml/hifi/audio/InputLevel.qml rename to interface/resources/qml/hifi/audio/InputPeak.qml index 58ce3a1ec7..4ff49091b1 100644 --- a/interface/resources/qml/hifi/audio/InputLevel.qml +++ b/interface/resources/qml/hifi/audio/InputPeak.qml @@ -1,5 +1,5 @@ // -// InputLevel.qml +// InputPeak.qml // qml/hifi/audio // // Created by Zach Pomerantz on 6/20/2017 @@ -15,7 +15,7 @@ import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.0 Rectangle { - readonly property var level: Audio.inputLevel; + property var peak; width: 70; height: 8; @@ -65,7 +65,7 @@ Rectangle { Rectangle { // mask id: mask; - width: parent.width * level; + width: parent.width * peak; radius: 5; anchors { bottom: parent.bottom; diff --git a/interface/resources/qml/hifi/commerce/wallet/Help.qml b/interface/resources/qml/hifi/commerce/wallet/Help.qml index 402209f38d..19273298b6 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Help.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Help.qml @@ -55,6 +55,7 @@ Item { text: "DEBUG: Clear Cached Passphrase"; onClicked: { commerce.setPassphrase(""); + sendSignalToWallet({method: 'passphraseReset'}); } } HifiControlsUit.Button { diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index 4fcfc0c93b..87cde5f43d 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -33,12 +33,19 @@ Rectangle { Hifi.QmlCommerce { id: commerce; + onAccountResult: { + if (result.status === "success") { + commerce.getKeyFilePathIfExists(); + } else { + // unsure how to handle a failure here. We definitely cannot proceed. + } + } onLoginStatusResult: { if (!isLoggedIn && root.activeView !== "needsLogIn") { root.activeView = "needsLogIn"; } else if (isLoggedIn) { root.activeView = "initialize"; - commerce.getKeyFilePathIfExists(); + commerce.account(); } } @@ -318,7 +325,7 @@ Rectangle { Connections { onSendSignalToWallet: { - if (msg.method === 'walletReset') { + if (msg.method === 'walletReset' || msg.method === 'passphraseReset') { sendToScript(msg); } } diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletSetupLightbox.qml b/interface/resources/qml/hifi/commerce/wallet/WalletSetupLightbox.qml index 2956dfb518..a623c2bcf7 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletSetupLightbox.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletSetupLightbox.qml @@ -325,7 +325,7 @@ Rectangle { onClicked: { if (passphraseSelection.validateAndSubmitPassphrase()) { root.lastPage = "choosePassphrase"; - commerce.balance(); // Do this here so that keys are generated. Order might change as backend changes? + commerce.generateKeyPair(); choosePassphraseContainer.visible = false; privateKeysReadyContainer.visible = true; } diff --git a/interface/resources/qml/hifi/tablet/TabletButton.qml b/interface/resources/qml/hifi/tablet/TabletButton.qml index a45390b3d0..58091d9fab 100644 --- a/interface/resources/qml/hifi/tablet/TabletButton.qml +++ b/interface/resources/qml/hifi/tablet/TabletButton.qml @@ -3,6 +3,8 @@ import QtGraphicalEffects 1.0 Item { id: tabletButton + + property string captionColorOverride: "" property var uuid; property string icon: "icons/tablet-icons/edit-i.svg" property string hoverIcon: tabletButton.icon @@ -102,7 +104,7 @@ Item { Text { id: text - color: "#ffffff" + color: captionColorOverride !== "" ? captionColorOverride: "#ffffff" text: tabletButton.text font.bold: true font.pixelSize: 18 diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d2a7014164..de1269c3ae 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -604,6 +604,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::registerInheritance(); // Set dependencies + DependencyManager::set(); DependencyManager::set(std::bind(&Application::getUserAgent, qApp)); DependencyManager::set(); DependencyManager::set(ScriptEngine::CLIENT_SCRIPT); @@ -2819,35 +2820,18 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { // Get controller availability bool hasHandControllers = false; - HandControllerType handControllerType = Vive; - if (PluginUtils::isViveControllerAvailable()) { + if (PluginUtils::isViveControllerAvailable() || PluginUtils::isOculusTouchControllerAvailable()) { hasHandControllers = true; - handControllerType = Vive; - } else if (PluginUtils::isOculusTouchControllerAvailable()) { - hasHandControllers = true; - handControllerType = Oculus; } - // Check tutorial content versioning - bool hasTutorialContent = contentVersion >= MIN_CONTENT_VERSION.at(handControllerType); - // Check HMD use (may be technically available without being in use) bool hasHMD = PluginUtils::isHMDAvailable(); bool isUsingHMD = _displayPlugin->isHmd(); bool isUsingHMDAndHandControllers = hasHMD && hasHandControllers && isUsingHMD; - Setting::Handle tutorialComplete{ "tutorialComplete", false }; Setting::Handle firstRun{ Settings::firstRun, true }; - const QString HIFI_SKIP_TUTORIAL_COMMAND_LINE_KEY = "--skipTutorial"; - // Skips tutorial/help behavior, and does NOT clear firstRun setting. - bool skipTutorial = arguments().contains(HIFI_SKIP_TUTORIAL_COMMAND_LINE_KEY); - bool isTutorialComplete = tutorialComplete.get(); - bool shouldGoToTutorial = isUsingHMDAndHandControllers && hasTutorialContent && !isTutorialComplete && !skipTutorial; - qCDebug(interfaceapp) << "HMD:" << hasHMD << ", Hand Controllers: " << hasHandControllers << ", Using HMD: " << isUsingHMDAndHandControllers; - qCDebug(interfaceapp) << "Tutorial version:" << contentVersion << ", sufficient:" << hasTutorialContent << - ", complete:" << isTutorialComplete << ", should go:" << shouldGoToTutorial; // when --url in command line, teleport to location const QString HIFI_URL_COMMAND_LINE_KEY = "--url"; @@ -2857,58 +2841,31 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { addressLookupString = arguments().value(urlIndex + 1); } - const QString TUTORIAL_PATH = "/tutorial_begin"; - - static const QString SENT_TO_TUTORIAL = "tutorial"; static const QString SENT_TO_PREVIOUS_LOCATION = "previous_location"; static const QString SENT_TO_ENTRY = "entry"; static const QString SENT_TO_SANDBOX = "sandbox"; QString sentTo; - if (shouldGoToTutorial) { - if (sandboxIsRunning) { - qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home."; - DependencyManager::get()->goToLocalSandbox(TUTORIAL_PATH); - sentTo = SENT_TO_TUTORIAL; - } else { - qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry."; - if (firstRun.get()) { - showHelp(); - } - if (addressLookupString.isEmpty()) { - DependencyManager::get()->goToEntry(); - sentTo = SENT_TO_ENTRY; - } else { - DependencyManager::get()->loadSettings(addressLookupString); - sentTo = SENT_TO_PREVIOUS_LOCATION; - } - } - } else { - // If this is a first run we short-circuit the address passed in - if (firstRun.get() && !skipTutorial) { + if (firstRun.get()) { showHelp(); - if (isUsingHMDAndHandControllers) { - if (sandboxIsRunning) { - qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home."; - DependencyManager::get()->goToLocalSandbox(); - sentTo = SENT_TO_SANDBOX; - } else { - qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry."; - DependencyManager::get()->goToEntry(); - sentTo = SENT_TO_ENTRY; - } + if (sandboxIsRunning) { + qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home."; + DependencyManager::get()->goToLocalSandbox(); + sentTo = SENT_TO_SANDBOX; } else { + qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry."; DependencyManager::get()->goToEntry(); sentTo = SENT_TO_ENTRY; } + firstRun.set(false); + } else { qCDebug(interfaceapp) << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString); DependencyManager::get()->loadSettings(addressLookupString); sentTo = SENT_TO_PREVIOUS_LOCATION; } - } UserActivityLogger::getInstance().logAction("startup_sent_to", { { "sent_to", sentTo }, @@ -2917,18 +2874,10 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { { "has_hand_controllers", hasHandControllers }, { "is_using_hmd", isUsingHMD }, { "is_using_hmd_and_hand_controllers", isUsingHMDAndHandControllers }, - { "content_version", contentVersion }, - { "is_tutorial_complete", isTutorialComplete }, - { "has_tutorial_content", hasTutorialContent }, - { "should_go_to_tutorial", shouldGoToTutorial } + { "content_version", contentVersion } }); _connectionMonitor.init(); - - // After all of the constructor is completed, then set firstRun to false. - if (!skipTutorial) { - firstRun.set(false); - } } bool Application::importJSONFromURL(const QString& urlString) { @@ -7459,7 +7408,10 @@ void Application::updateDisplayMode() { getApplicationCompositor().setDisplayPlugin(newDisplayPlugin); _displayPlugin = newDisplayPlugin; connect(_displayPlugin.get(), &DisplayPlugin::presented, this, &Application::onPresent); - offscreenUi->getDesktop()->setProperty("repositionLocked", wasRepositionLocked); + auto desktop = offscreenUi->getDesktop(); + if (desktop) { + desktop->setProperty("repositionLocked", wasRepositionLocked); + } } bool isHmd = _displayPlugin->isHmd(); diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index 2055229059..a1488af1fe 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -176,3 +176,28 @@ void Ledger::resetFailure(QNetworkReply& reply) { failResponse("reset", reply); void Ledger::reset() { send("reset_user_hfc_account", "resetSuccess", "resetFailure", QNetworkAccessManager::PutOperation, QJsonObject()); } + +void Ledger::accountSuccess(QNetworkReply& reply) { + // lets set the appropriate stuff in the wallet now + auto wallet = DependencyManager::get(); + QByteArray response = reply.readAll(); + QJsonObject data = QJsonDocument::fromJson(response).object()["data"].toObject(); + + auto salt = QByteArray::fromBase64(data["salt"].toString().toUtf8()); + auto iv = QByteArray::fromBase64(data["iv"].toString().toUtf8()); + auto ckey = QByteArray::fromBase64(data["ckey"].toString().toUtf8()); + + wallet->setSalt(salt); + wallet->setIv(iv); + wallet->setCKey(ckey); + + // none of the hfc account info should be emitted + emit accountResult(QJsonObject{ {"status", "success"} }); +} + +void Ledger::accountFailure(QNetworkReply& reply) { + failResponse("account", reply); +} +void Ledger::account() { + send("hfc_account", "accountSuccess", "accountFailure", QNetworkAccessManager::PutOperation, QJsonObject()); +} diff --git a/interface/src/commerce/Ledger.h b/interface/src/commerce/Ledger.h index 55b648aa4c..6aadf5afb0 100644 --- a/interface/src/commerce/Ledger.h +++ b/interface/src/commerce/Ledger.h @@ -29,6 +29,7 @@ public: void balance(const QStringList& keys); void inventory(const QStringList& keys); void history(const QStringList& keys); + void account(); void reset(); signals: @@ -37,6 +38,7 @@ signals: void balanceResult(QJsonObject result); void inventoryResult(QJsonObject result); void historyResult(QJsonObject result); + void accountResult(QJsonObject result); public slots: void buySuccess(QNetworkReply& reply); @@ -51,6 +53,8 @@ public slots: void historyFailure(QNetworkReply& reply); void resetSuccess(QNetworkReply& reply); void resetFailure(QNetworkReply& reply); + void accountSuccess(QNetworkReply& reply); + void accountFailure(QNetworkReply& reply); private: QJsonObject apiResponse(const QString& label, QNetworkReply& reply); diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index 96f2a02f31..9315a2e9c5 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -27,6 +27,7 @@ QmlCommerce::QmlCommerce(QQuickItem* parent) : OffscreenQmlDialog(parent) { connect(wallet.data(), &Wallet::securityImageResult, this, &QmlCommerce::securityImageResult); connect(ledger.data(), &Ledger::historyResult, this, &QmlCommerce::historyResult); connect(wallet.data(), &Wallet::keyFilePathIfExistsResult, this, &QmlCommerce::keyFilePathIfExistsResult); + connect(ledger.data(), &Ledger::accountResult, this, &QmlCommerce::accountResult); } void QmlCommerce::getLoginStatus() { @@ -86,7 +87,7 @@ void QmlCommerce::history() { void QmlCommerce::setPassphrase(const QString& passphrase) { auto wallet = DependencyManager::get(); - if (wallet->getPassphrase() && !wallet->getPassphrase()->isEmpty()) { + if(wallet->getPassphrase() && !wallet->getPassphrase()->isEmpty() && !passphrase.isEmpty()) { wallet->changePassphrase(passphrase); } else { wallet->setPassphrase(passphrase); @@ -94,9 +95,20 @@ void QmlCommerce::setPassphrase(const QString& passphrase) { getWalletAuthenticatedStatus(); } +void QmlCommerce::generateKeyPair() { + auto wallet = DependencyManager::get(); + wallet->generateKeyPair(); + getWalletAuthenticatedStatus(); +} + void QmlCommerce::reset() { auto ledger = DependencyManager::get(); auto wallet = DependencyManager::get(); ledger->reset(); wallet->reset(); } + +void QmlCommerce::account() { + auto ledger = DependencyManager::get(); + ledger->account(); +} diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h index f66bf518f5..7b0bab95a6 100644 --- a/interface/src/commerce/QmlCommerce.h +++ b/interface/src/commerce/QmlCommerce.h @@ -39,6 +39,7 @@ signals: void balanceResult(QJsonObject result); void inventoryResult(QJsonObject result); void historyResult(QJsonObject result); + void accountResult(QJsonObject result); protected: Q_INVOKABLE void getLoginStatus(); @@ -53,8 +54,9 @@ protected: Q_INVOKABLE void balance(); Q_INVOKABLE void inventory(); Q_INVOKABLE void history(); - + Q_INVOKABLE void generateKeyPair(); Q_INVOKABLE void reset(); + Q_INVOKABLE void account(); }; #endif // hifi_QmlCommerce_h diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 69327b09a6..2faa08064d 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -55,9 +55,12 @@ QString imageFilePath() { // use the cached _passphrase if it exists, otherwise we need to prompt int passwordCallback(char* password, int maxPasswordSize, int rwFlag, void* u) { // just return a hardcoded pwd for now - auto passphrase = DependencyManager::get()->getPassphrase(); + auto wallet = DependencyManager::get(); + auto passphrase = wallet->getPassphrase(); if (passphrase && !passphrase->isEmpty()) { - strcpy(password, passphrase->toLocal8Bit().constData()); + QString saltedPassphrase(*passphrase); + saltedPassphrase.append(wallet->getSalt()); + strcpy(password, saltedPassphrase.toUtf8().constData()); return static_cast(passphrase->size()); } else { // this shouldn't happen - so lets log it to tell us we have @@ -245,6 +248,8 @@ RSA* readPrivateKey(const char* filename) { } else { qCDebug(commerce) << "couldn't parse" << filename; + // if the passphrase is wrong, then let's not cache it + DependencyManager::get()->setPassphrase(""); } fclose(fp); } else { @@ -252,13 +257,12 @@ RSA* readPrivateKey(const char* filename) { } return key; } -static const unsigned char IVEC[16] = "IAmAnIVecYay123"; void initializeAESKeys(unsigned char* ivec, unsigned char* ckey, const QByteArray& salt) { - // first ivec - memcpy(ivec, IVEC, 16); - auto hash = QCryptographicHash::hash(salt, QCryptographicHash::Sha256); - memcpy(ckey, hash.data(), 32); + // use the ones in the wallet + auto wallet = DependencyManager::get(); + memcpy(ivec, wallet->getIv(), 16); + memcpy(ckey, wallet->getCKey(), 32); } Wallet::~Wallet() { @@ -273,8 +277,6 @@ void Wallet::setPassphrase(const QString& passphrase) { } _passphrase = new QString(passphrase); - // no matter what, we now need to clear the keys as they - // need to be read using this passphrase _publicKeys.clear(); } @@ -413,28 +415,10 @@ bool Wallet::walletIsAuthenticatedWithPassphrase() { return false; } -bool Wallet::createIfNeeded() { - if (_publicKeys.count() > 0) return false; - +bool Wallet::generateKeyPair() { // FIXME: initialize OpenSSL elsewhere soon initialize(); - // try to read existing keys if they exist... - auto publicKey = readPublicKey(keyFilePath().toStdString().c_str()); - if (publicKey.size() > 0) { - if (auto key = readPrivateKey(keyFilePath().toStdString().c_str()) ) { - qCDebug(commerce) << "read private key"; - RSA_free(key); - // K -- add the public key since we have a legit private key associated with it - _publicKeys.push_back(publicKey.toBase64()); - return false; - } - } - qCInfo(commerce) << "Creating wallet."; - return generateKeyPair(); -} - -bool Wallet::generateKeyPair() { qCInfo(commerce) << "Generating keypair."; auto keyPair = generateRSAKeypair(); sendKeyFilePathIfExists(); @@ -453,7 +437,6 @@ bool Wallet::generateKeyPair() { QStringList Wallet::listPublicKeys() { qCInfo(commerce) << "Enumerating public keys."; - createIfNeeded(); return _publicKeys; } @@ -572,12 +555,8 @@ void Wallet::reset() { // tell the provider we got nothing updateImageProvider(); - delete _passphrase; + _passphrase->clear(); - // for now we need to maintain the hard-coded passphrase. - // FIXME: remove this line as part of wiring up the passphrase - // and probably set it to nullptr - _passphrase = new QString("pwd"); QFile keyFile(keyFilePath()); QFile imageFile(imageFilePath()); @@ -608,6 +587,7 @@ bool Wallet::changePassphrase(const QString& newPassphrase) { return false; } } - qCDebug(commerce) << "couldn't read keys"; + qCDebug(commerce) << "couldn't decrypt keys with current passphrase, clearing"; + setPassphrase(QString("")); return false; } diff --git a/interface/src/commerce/Wallet.h b/interface/src/commerce/Wallet.h index 3b470210de..f3c2ac399b 100644 --- a/interface/src/commerce/Wallet.h +++ b/interface/src/commerce/Wallet.h @@ -26,7 +26,6 @@ public: ~Wallet(); // These are currently blocking calls, although they might take a moment. - bool createIfNeeded(); bool generateKeyPair(); QStringList listPublicKeys(); QString signWithKey(const QByteArray& text, const QString& key); @@ -36,6 +35,10 @@ public: void setSalt(const QByteArray& salt) { _salt = salt; } QByteArray getSalt() { return _salt; } + void setIv(const QByteArray& iv) { _iv = iv; } + QByteArray getIv() { return _iv; } + void setCKey(const QByteArray& ckey) { _ckey = ckey; } + QByteArray getCKey() { return _ckey; } void setPassphrase(const QString& passphrase); QString* getPassphrase() { return _passphrase; } @@ -53,6 +56,8 @@ private: QStringList _publicKeys{}; QPixmap* _securityImage { nullptr }; QByteArray _salt {"iamsalt!"}; + QByteArray _iv; + QByteArray _ckey; QString* _passphrase { new QString("") }; void updateImageProvider(); diff --git a/interface/src/scripting/AudioDevices.cpp b/interface/src/scripting/AudioDevices.cpp index f2e6dbf4d7..e5cc43f9df 100644 --- a/interface/src/scripting/AudioDevices.cpp +++ b/interface/src/scripting/AudioDevices.cpp @@ -37,6 +37,20 @@ Setting::Handle& getSetting(bool contextIsHMD, QAudio::Mode mode) { } } +enum AudioDeviceRole { + DisplayRole = Qt::DisplayRole, + CheckStateRole = Qt::CheckStateRole, + PeakRole = Qt::UserRole, + InfoRole = Qt::UserRole + 1 +}; + +QHash AudioDeviceList::_roles { + { DisplayRole, "display" }, + { CheckStateRole, "selected" }, + { PeakRole, "peak" }, + { InfoRole, "info" } +}; + static QString getTargetDevice(bool hmd, QAudio::Mode mode) { QString deviceName; auto& setting = getSetting(hmd, mode); @@ -52,29 +66,36 @@ static QString getTargetDevice(bool hmd, QAudio::Mode mode) { return deviceName; } -QHash AudioDeviceList::_roles { - { Qt::DisplayRole, "display" }, - { Qt::CheckStateRole, "selected" }, - { Qt::UserRole, "info" } -}; Qt::ItemFlags AudioDeviceList::_flags { Qt::ItemIsSelectable | Qt::ItemIsEnabled }; QVariant AudioDeviceList::data(const QModelIndex& index, int role) const { - if (!index.isValid() || index.row() >= _devices.size()) { + if (!index.isValid() || index.row() >= rowCount()) { return QVariant(); } - if (role == Qt::DisplayRole) { - return _devices.at(index.row()).display; - } else if (role == Qt::CheckStateRole) { - return _devices.at(index.row()).selected; - } else if (role == Qt::UserRole) { - return QVariant::fromValue(_devices.at(index.row()).info); + if (role == DisplayRole) { + return _devices.at(index.row())->display; + } else if (role == CheckStateRole) { + return _devices.at(index.row())->selected; + } else if (role == InfoRole) { + return QVariant::fromValue(_devices.at(index.row())->info); } else { return QVariant(); } } +QVariant AudioInputDeviceList::data(const QModelIndex& index, int role) const { + if (!index.isValid() || index.row() >= rowCount()) { + return QVariant(); + } + + if (role == PeakRole) { + return std::static_pointer_cast(_devices.at(index.row()))->peak; + } else { + return AudioDeviceList::data(index, role); + } +} + void AudioDeviceList::resetDevice(bool contextIsHMD) { auto client = DependencyManager::get().data(); QString deviceName = getTargetDevice(contextIsHMD, _mode); @@ -113,8 +134,9 @@ void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device) { auto oldDevice = _selectedDevice; _selectedDevice = device; - for (auto i = 0; i < _devices.size(); ++i) { - AudioDevice& device = _devices[i]; + for (auto i = 0; i < rowCount(); ++i) { + AudioDevice& device = *_devices[i]; + if (device.selected && device.info != _selectedDevice) { device.selected = false; } else if (device.info == _selectedDevice) { @@ -139,17 +161,47 @@ void AudioDeviceList::onDevicesChanged(const QList& devices) { .remove("Device") .replace(" )", ")"); device.selected = (device.info == _selectedDevice); - _devices.push_back(device); + _devices.push_back(newDevice(device)); } endResetModel(); } +bool AudioInputDeviceList::peakValuesAvailable() { + std::call_once(_peakFlag, [&] { + _peakValuesAvailable = DependencyManager::get()->peakValuesAvailable(); + }); + return _peakValuesAvailable; +} + +void AudioInputDeviceList::setPeakValuesEnabled(bool enable) { + if (peakValuesAvailable() && (enable != _peakValuesEnabled)) { + DependencyManager::get()->enablePeakValues(enable); + _peakValuesEnabled = enable; + emit peakValuesEnabledChanged(_peakValuesEnabled); + } +} + +void AudioInputDeviceList::onPeakValueListChanged(const QList& peakValueList) { + assert(_mode == QAudio::AudioInput); + + if (peakValueList.length() != rowCount()) { + qWarning() << "AudioDeviceList" << __FUNCTION__ << "length mismatch"; + } + + for (auto i = 0; i < rowCount(); ++i) { + std::static_pointer_cast(_devices[i])->peak = peakValueList[i]; + } + + emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0), { PeakRole }); +} + AudioDevices::AudioDevices(bool& contextIsHMD) : _contextIsHMD(contextIsHMD) { auto client = DependencyManager::get(); connect(client.data(), &AudioClient::deviceChanged, this, &AudioDevices::onDeviceChanged, Qt::QueuedConnection); connect(client.data(), &AudioClient::devicesChanged, this, &AudioDevices::onDevicesChanged, Qt::QueuedConnection); + connect(client.data(), &AudioClient::peakValueListChanged, &_inputs, &AudioInputDeviceList::onPeakValueListChanged, Qt::QueuedConnection); // connections are made after client is initialized, so we must also fetch the devices _inputs.onDeviceChanged(client->getActiveAudioDevice(QAudio::AudioInput)); diff --git a/interface/src/scripting/AudioDevices.h b/interface/src/scripting/AudioDevices.h index 3278a53374..4c1820e9c8 100644 --- a/interface/src/scripting/AudioDevices.h +++ b/interface/src/scripting/AudioDevices.h @@ -12,6 +12,9 @@ #ifndef hifi_scripting_AudioDevices_h #define hifi_scripting_AudioDevices_h +#include +#include + #include #include #include @@ -29,7 +32,11 @@ class AudioDeviceList : public QAbstractListModel { Q_OBJECT public: - AudioDeviceList(QAudio::Mode mode) : _mode(mode) {} + AudioDeviceList(QAudio::Mode mode = QAudio::AudioOutput) : _mode(mode) {} + ~AudioDeviceList() = default; + + virtual std::shared_ptr newDevice(const AudioDevice& device) + { return std::make_shared(device); } int rowCount(const QModelIndex& parent = QModelIndex()) const override { Q_UNUSED(parent); return _devices.size(); } QHash roleNames() const override { return _roles; } @@ -44,25 +51,63 @@ public: signals: void deviceChanged(const QAudioDeviceInfo& device); -private slots: +protected slots: void onDeviceChanged(const QAudioDeviceInfo& device); void onDevicesChanged(const QList& devices); -private: +protected: friend class AudioDevices; static QHash _roles; static Qt::ItemFlags _flags; const QAudio::Mode _mode; QAudioDeviceInfo _selectedDevice; - QList _devices; + QList> _devices; +}; + +class AudioInputDevice : public AudioDevice { +public: + AudioInputDevice(const AudioDevice& device) : AudioDevice(device) {} + float peak { 0.0f }; +}; + +class AudioInputDeviceList : public AudioDeviceList { + Q_OBJECT + Q_PROPERTY(bool peakValuesAvailable READ peakValuesAvailable) + Q_PROPERTY(bool peakValuesEnabled READ peakValuesEnabled WRITE setPeakValuesEnabled NOTIFY peakValuesEnabledChanged) + +public: + AudioInputDeviceList() : AudioDeviceList(QAudio::AudioInput) {} + virtual ~AudioInputDeviceList() = default; + + virtual std::shared_ptr newDevice(const AudioDevice& device) override + { return std::make_shared(device); } + + QVariant data(const QModelIndex& index, int role) const override; + +signals: + void peakValuesEnabledChanged(bool enabled); + +protected slots: + void onPeakValueListChanged(const QList& peakValueList); + +protected: + friend class AudioDevices; + + bool peakValuesAvailable(); + std::once_flag _peakFlag; + bool _peakValuesAvailable; + + bool peakValuesEnabled() const { return _peakValuesEnabled; } + void setPeakValuesEnabled(bool enable); + bool _peakValuesEnabled { false }; }; class Audio; class AudioDevices : public QObject { Q_OBJECT - Q_PROPERTY(AudioDeviceList* input READ getInputList NOTIFY nop) + Q_PROPERTY(AudioInputDeviceList* input READ getInputList NOTIFY nop) Q_PROPERTY(AudioDeviceList* output READ getOutputList NOTIFY nop) public: @@ -82,10 +127,10 @@ private slots: private: friend class Audio; - AudioDeviceList* getInputList() { return &_inputs; } + AudioInputDeviceList* getInputList() { return &_inputs; } AudioDeviceList* getOutputList() { return &_outputs; } - AudioDeviceList _inputs { QAudio::AudioInput }; + AudioInputDeviceList _inputs; AudioDeviceList _outputs { QAudio::AudioOutput }; QAudioDeviceInfo _requestedOutputDevice; QAudioDeviceInfo _requestedInputDevice; diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp index c9341fd3d9..515215a8d3 100644 --- a/interface/src/ui/LoginDialog.cpp +++ b/interface/src/ui/LoginDialog.cpp @@ -169,12 +169,14 @@ void LoginDialog::openUrl(const QString& url) const { auto offscreenUi = DependencyManager::get(); if (tablet->getToolbarMode()) { - auto browser = offscreenUi->load("Browser.qml"); - browser->setProperty("url", url); + offscreenUi->load("Browser.qml", [=](QQmlContext* context, QObject* newObject) { + newObject->setProperty("url", url); + }); } else { if (!hmd->getShouldShowTablet() && !qApp->isHMDMode()) { - auto browser = offscreenUi->load("Browser.qml"); - browser->setProperty("url", url); + offscreenUi->load("Browser.qml", [=](QQmlContext* context, QObject* newObject) { + newObject->setProperty("url", url); + }); } else { tablet->gotoWebScreen(url); } diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 27bab687d5..4e7cc08919 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -78,7 +78,7 @@ Setting::Handle staticJitterBufferFrames("staticJitterBufferFrames", // protect the Qt internal device list using Mutex = std::mutex; using Lock = std::unique_lock; -static Mutex _deviceMutex; +Mutex _deviceMutex; // thread-safe QList getAvailableDevices(QAudio::Mode mode) { @@ -227,13 +227,18 @@ AudioClient::AudioClient() : // start a thread to detect any device changes _checkDevicesTimer = new QTimer(this); connect(_checkDevicesTimer, &QTimer::timeout, [this] { - QtConcurrent::run(QThreadPool::globalInstance(), [this] { - checkDevices(); - }); + QtConcurrent::run(QThreadPool::globalInstance(), [this] { checkDevices(); }); }); const unsigned long DEVICE_CHECK_INTERVAL_MSECS = 2 * 1000; _checkDevicesTimer->start(DEVICE_CHECK_INTERVAL_MSECS); + // start a thread to detect peak value changes + _checkPeakValuesTimer = new QTimer(this); + connect(_checkPeakValuesTimer, &QTimer::timeout, [this] { + QtConcurrent::run(QThreadPool::globalInstance(), [this] { checkPeakValues(); }); + }); + const unsigned long PEAK_VALUES_CHECK_INTERVAL_MSECS = 50; + _checkPeakValuesTimer->start(PEAK_VALUES_CHECK_INTERVAL_MSECS); configureReverb(); @@ -275,6 +280,7 @@ void AudioClient::cleanupBeforeQuit() { stop(); _checkDevicesTimer->stop(); + _checkPeakValuesTimer->stop(); guard.trigger(); } @@ -316,8 +322,6 @@ QString getWinDeviceName(IMMDevice* pEndpoint) { QString deviceName; IPropertyStore* pPropertyStore; pEndpoint->OpenPropertyStore(STGM_READ, &pPropertyStore); - pEndpoint->Release(); - pEndpoint = nullptr; PROPVARIANT pv; PropVariantInit(&pv); HRESULT hr = pPropertyStore->GetValue(PKEY_Device_FriendlyName, &pv); @@ -346,6 +350,8 @@ QString AudioClient::getWinDeviceName(wchar_t* guid) { deviceName = QString("NONE"); } else { deviceName = ::getWinDeviceName(pEndpoint); + pEndpoint->Release(); + pEndpoint = nullptr; } pMMDeviceEnumerator->Release(); pMMDeviceEnumerator = nullptr; @@ -429,6 +435,8 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { deviceName = QString("NONE"); } else { deviceName = getWinDeviceName(pEndpoint); + pEndpoint->Release(); + pEndpoint = nullptr; } pMMDeviceEnumerator->Release(); pMMDeviceEnumerator = NULL; diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 26a07235d5..ff0ea968a8 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -148,6 +148,9 @@ public: QAudioDeviceInfo getActiveAudioDevice(QAudio::Mode mode) const; QList getAudioDevices(QAudio::Mode mode) const; + void enablePeakValues(bool enable) { _enablePeakValues = enable; } + bool peakValuesAvailable() const; + static const float CALLBACK_ACCELERATOR_RATIO; bool getNamedAudioDeviceForModeExists(QAudio::Mode mode, const QString& deviceName); @@ -224,6 +227,7 @@ signals: void deviceChanged(QAudio::Mode mode, const QAudioDeviceInfo& device); void devicesChanged(QAudio::Mode mode, const QList& devices); + void peakValueListChanged(const QList peakValueList); void receivedFirstPacket(); void disconnected(); @@ -242,9 +246,12 @@ private: friend class CheckDevicesThread; friend class LocalInjectorsThread; + // background tasks + void checkDevices(); + void checkPeakValues(); + void outputFormatChanged(); void handleAudioInput(QByteArray& audioBuffer); - void checkDevices(); void prepareLocalAudioInjectors(std::unique_ptr localAudioLock = nullptr); bool mixLocalAudioInjectors(float* mixBuffer); float azimuthForSource(const glm::vec3& relativePosition); @@ -298,6 +305,7 @@ private: std::atomic _localInjectorsAvailable { false }; MixedProcessedAudioStream _receivedAudioStream; bool _isStereoInput; + std::atomic _enablePeakValues { false }; quint64 _outputStarveDetectionStartTimeMsec; int _outputStarveDetectionCount; @@ -396,6 +404,7 @@ private: RateCounter<> _audioInbound; QTimer* _checkDevicesTimer { nullptr }; + QTimer* _checkPeakValuesTimer { nullptr }; }; diff --git a/libraries/audio-client/src/AudioPeakValues.cpp b/libraries/audio-client/src/AudioPeakValues.cpp new file mode 100644 index 0000000000..3df469b830 --- /dev/null +++ b/libraries/audio-client/src/AudioPeakValues.cpp @@ -0,0 +1,176 @@ +// +// AudioPeakValues.cpp +// interface/src +// +// Created by Zach Pomerantz on 6/26/2017 +// Copyright 2013 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 "AudioClient.h" + +#ifdef Q_OS_WIN + +#include +#include +#include +#include + +#include + +#define RETURN_ON_FAIL(result) if (FAILED(result)) { return; } +#define CONTINUE_ON_FAIL(result) if (FAILED(result)) { continue; } + +extern QString getWinDeviceName(IMMDevice* pEndpoint); +extern std::mutex _deviceMutex; + +std::map> activeClients; + +template +void release(T* t) { + t->Release(); +} + +template <> +void release(IAudioClient* audioClient) { + audioClient->Stop(); + audioClient->Release(); +} + +void AudioClient::checkPeakValues() { + // prepare the windows environment + CoInitialize(NULL); + + // if disabled, clean up active clients + if (!_enablePeakValues) { + activeClients.clear(); + return; + } + + // lock the devices so the _inputDevices list is static + std::unique_lock lock(_deviceMutex); + HRESULT result; + + // initialize the payload + QList peakValueList; + for (int i = 0; i < _inputDevices.size(); ++i) { + peakValueList.push_back(0.0f); + } + + std::shared_ptr enumerator; + { + IMMDeviceEnumerator* pEnumerator; + result = CoCreateInstance( + __uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, + __uuidof(IMMDeviceEnumerator), (void**)&pEnumerator); + RETURN_ON_FAIL(result); + enumerator = std::shared_ptr(pEnumerator, &release); + } + + std::shared_ptr endpoints; + { + IMMDeviceCollection* pEndpoints; + result = enumerator->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE, &pEndpoints); + RETURN_ON_FAIL(result); + endpoints = std::shared_ptr(pEndpoints, &release); + } + + UINT count; + { + result = endpoints->GetCount(&count); + RETURN_ON_FAIL(result); + } + + IMMDevice* pDevice; + std::shared_ptr device; + IAudioMeterInformation* pMeterInfo; + std::shared_ptr meterInfo; + IAudioClient* pAudioClient; + std::shared_ptr audioClient; + DWORD hardwareSupport; + LPWSTR pDeviceId = NULL; + LPWAVEFORMATEX format; + float peakValue; + QString deviceName; + int deviceIndex; + for (UINT i = 0; i < count; ++i) { + result = endpoints->Item(i, &pDevice); + CONTINUE_ON_FAIL(result); + device = std::shared_ptr(pDevice, &release); + + // if the device isn't listed through Qt, skip it + deviceName = ::getWinDeviceName(pDevice); + deviceIndex = 0; + for (; deviceIndex < _inputDevices.size(); ++deviceIndex) { + if (deviceName == _inputDevices[deviceIndex].deviceName()) { + break; + } + } + if (deviceIndex >= _inputDevices.size()) { + continue; + } + + //continue; + + result = device->Activate(__uuidof(IAudioMeterInformation), CLSCTX_ALL, NULL, (void**)&pMeterInfo); + CONTINUE_ON_FAIL(result); + meterInfo = std::shared_ptr(pMeterInfo, &release); + + //continue; + + hardwareSupport; + result = meterInfo->QueryHardwareSupport(&hardwareSupport); + CONTINUE_ON_FAIL(result); + + //continue; + + // if the device has no hardware support (USB)... + if (!(hardwareSupport & ENDPOINT_HARDWARE_SUPPORT_METER)) { + result = device->GetId(&pDeviceId); + CONTINUE_ON_FAIL(result); + std::wstring deviceId(pDeviceId); + CoTaskMemFree(pDeviceId); + + //continue; + + // ...and no active client... + if (activeClients.find(deviceId) == activeClients.end()) { + result = device->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pAudioClient); + CONTINUE_ON_FAIL(result); + audioClient = std::shared_ptr(pAudioClient, &release); + + //continue; + + // ...activate a client + audioClient->GetMixFormat(&format); + audioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, 0, 0, format, NULL); + audioClient->Start(); + + //continue; + + activeClients[deviceId] = audioClient; + } + } + + // get the peak value and put it in the payload + meterInfo->GetPeakValue(&peakValue); + peakValueList[deviceIndex] = peakValue; + } + + emit peakValueListChanged(peakValueList); +} + +bool AudioClient::peakValuesAvailable() const { + return true; +} + +#else +void AudioClient::checkPeakValues() { +} + +bool AudioClient::peakValuesAvailable() const { + return false; +} +#endif \ No newline at end of file diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index cceecaeed9..ad16a97bf6 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -431,7 +431,7 @@ int Octree::readElementData(const OctreeElementPointer& destinationElement, cons return bytesRead; } -void Octree::readBitstreamToTree(const unsigned char * bitstream, unsigned long int bufferSizeBytes, +void Octree::readBitstreamToTree(const unsigned char * bitstream, uint64_t bufferSizeBytes, ReadBitstreamToTreeParams& args) { int bytesRead = 0; const unsigned char* bitstreamAt = bitstream; @@ -925,8 +925,8 @@ int Octree::encodeTreeBitstream(const OctreeElementPointer& element, roomForOctalCode = packetData->startSubTree(newCode); if (newCode) { - delete[] newCode; codeLength = numberOfThreeBitSectionsInCode(newCode); + delete[] newCode; } else { codeLength = 1; } @@ -1152,7 +1152,6 @@ int Octree::encodeTreeBitstreamRecursion(const OctreeElementPointer& element, OctreeElementPointer sortedChildren[NUMBER_OF_CHILDREN] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; float distancesToChildren[NUMBER_OF_CHILDREN] = { 0, 0, 0, 0, 0, 0, 0, 0 }; int indexOfChildren[NUMBER_OF_CHILDREN] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - int currentCount = 0; for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { OctreeElementPointer childElement = element->getChildAtIndex(i); @@ -1174,7 +1173,6 @@ int Octree::encodeTreeBitstreamRecursion(const OctreeElementPointer& element, sortedChildren[i] = childElement; indexOfChildren[i] = i; distancesToChildren[i] = 0.0f; - currentCount++; // track stats // must check childElement here, because it could be we got here with no childElement @@ -1185,7 +1183,7 @@ int Octree::encodeTreeBitstreamRecursion(const OctreeElementPointer& element, // for each child element in Distance sorted order..., check to see if they exist, are colored, and in view, and if so // add them to our distance ordered array of children - for (int i = 0; i < currentCount; i++) { + for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { OctreeElementPointer childElement = sortedChildren[i]; int originalIndex = indexOfChildren[i]; @@ -1276,7 +1274,7 @@ int Octree::encodeTreeBitstreamRecursion(const OctreeElementPointer& element, } // NOTE: the childrenDataBits indicates that there is an array of child element data included in this packet. - // We wil write this bit mask but we may come back later and update the bits that are actually included + // We will write this bit mask but we may come back later and update the bits that are actually included packetData->releaseReservedBytes(sizeof(childrenDataBits)); continueThisLevel = packetData->appendBitMask(childrenDataBits); @@ -1441,7 +1439,7 @@ int Octree::encodeTreeBitstreamRecursion(const OctreeElementPointer& element, // for each child element in Distance sorted order..., check to see if they exist, are colored, and in view, and if so // add them to our distance ordered array of children - for (int indexByDistance = 0; indexByDistance < currentCount; indexByDistance++) { + for (int indexByDistance = 0; indexByDistance < NUMBER_OF_CHILDREN; indexByDistance++) { OctreeElementPointer childElement = sortedChildren[indexByDistance]; int originalIndex = indexOfChildren[indexByDistance]; @@ -1638,7 +1636,7 @@ bool Octree::readFromFile(const char* fileName) { QDataStream fileInputStream(&file); QFileInfo fileInfo(qFileName); - unsigned long fileLength = fileInfo.size(); + uint64_t fileLength = fileInfo.size(); emit importSize(1.0f, 1.0f, 1.0f); emit importProgress(0); @@ -1715,7 +1713,7 @@ bool Octree::readFromURL(const QString& urlString) { } -bool Octree::readFromStream(unsigned long streamLength, QDataStream& inputStream, const QString& marketplaceID) { +bool Octree::readFromStream(uint64_t streamLength, QDataStream& inputStream, const QString& marketplaceID) { // decide if this is binary SVO or JSON-formatted SVO QIODevice *device = inputStream.device(); char firstChar; @@ -1732,14 +1730,14 @@ bool Octree::readFromStream(unsigned long streamLength, QDataStream& inputStream } -bool Octree::readSVOFromStream(unsigned long streamLength, QDataStream& inputStream) { +bool Octree::readSVOFromStream(uint64_t streamLength, QDataStream& inputStream) { qWarning() << "SVO file format depricated. Support for reading SVO files is no longer support and will be removed soon."; bool fileOk = false; PacketVersion gotVersion = 0; - unsigned long headerLength = 0; // bytes in the header + uint64_t headerLength = 0; // bytes in the header bool wantImportProgress = true; @@ -1751,14 +1749,14 @@ bool Octree::readSVOFromStream(unsigned long streamLength, QDataStream& inputStr if (getWantSVOfileVersions()) { // read just enough of the file to parse the header... - const unsigned long HEADER_LENGTH = sizeof(int) + sizeof(PacketVersion); + const uint64_t HEADER_LENGTH = sizeof(int) + sizeof(PacketVersion); unsigned char fileHeader[HEADER_LENGTH]; inputStream.readRawData((char*)&fileHeader, HEADER_LENGTH); headerLength = HEADER_LENGTH; // we need this later to skip to the data unsigned char* dataAt = (unsigned char*)&fileHeader; - unsigned long dataLength = HEADER_LENGTH; + uint64_t dataLength = HEADER_LENGTH; // if so, read the first byte of the file and see if it matches the expected version code int intPacketType; @@ -1804,7 +1802,7 @@ bool Octree::readSVOFromStream(unsigned long streamLength, QDataStream& inputStr if (!hasBufferBreaks) { // read the entire file into a buffer, WHAT!? Why not. - unsigned long dataLength = streamLength - headerLength; + uint64_t dataLength = streamLength - headerLength; unsigned char* entireFileDataSection = new unsigned char[dataLength]; inputStream.readRawData((char*)entireFileDataSection, dataLength); @@ -1819,9 +1817,9 @@ bool Octree::readSVOFromStream(unsigned long streamLength, QDataStream& inputStr } else { - unsigned long dataLength = streamLength - headerLength; - unsigned long remainingLength = dataLength; - const unsigned long MAX_CHUNK_LENGTH = MAX_OCTREE_PACKET_SIZE * 2; + uint64_t dataLength = streamLength - headerLength; + uint64_t remainingLength = dataLength; + const uint64_t MAX_CHUNK_LENGTH = MAX_OCTREE_PACKET_SIZE * 2; unsigned char* fileChunk = new unsigned char[MAX_CHUNK_LENGTH]; while (remainingLength > 0) { @@ -1847,7 +1845,7 @@ bool Octree::readSVOFromStream(unsigned long streamLength, QDataStream& inputStr remainingLength -= chunkLength; unsigned char* dataAt = fileChunk; - unsigned long dataLength = chunkLength; + uint64_t dataLength = chunkLength; ReadBitstreamToTreeParams args(NO_EXISTS_BITS, NULL, 0, SharedNodePointer(), wantImportProgress, gotVersion); @@ -1887,7 +1885,7 @@ QJsonDocument addMarketplaceIDToDocumentEntities(QJsonDocument& doc, const QStri const int READ_JSON_BUFFER_SIZE = 2048; -bool Octree::readJSONFromStream(unsigned long streamLength, QDataStream& inputStream, const QString& marketplaceID /*=""*/) { +bool Octree::readJSONFromStream(uint64_t streamLength, QDataStream& inputStream, const QString& marketplaceID /*=""*/) { // if the data is gzipped we may not have a useful bytesAvailable() result, so just keep reading until // we get an eof. Leave streamLength parameter for consistency. @@ -1982,14 +1980,14 @@ bool Octree::writeToJSONFile(const char* fileName, const OctreeElementPointer& e return success; } -unsigned long Octree::getOctreeElementsCount() { - unsigned long nodeCount = 0; +uint64_t Octree::getOctreeElementsCount() { + uint64_t nodeCount = 0; recurseTreeWithOperation(countOctreeElementsOperation, &nodeCount); return nodeCount; } bool Octree::countOctreeElementsOperation(const OctreeElementPointer& element, void* extraData) { - (*(unsigned long*)extraData)++; + (*(uint64_t*)extraData)++; return true; // keep going } diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 38454d20b5..3b84618a56 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -231,7 +232,7 @@ public: virtual void eraseAllOctreeElements(bool createNewRoot = true); - void readBitstreamToTree(const unsigned char* bitstream, unsigned long int bufferSizeBytes, ReadBitstreamToTreeParams& args); + void readBitstreamToTree(const unsigned char* bitstream, uint64_t bufferSizeBytes, ReadBitstreamToTreeParams& args); void deleteOctalCodeFromTree(const unsigned char* codeBuffer, bool collapseEmptyTrees = DONT_COLLAPSE); void reaverageOctreeElements(OctreeElementPointer startElement = OctreeElementPointer()); @@ -301,13 +302,13 @@ public: // Octree importers bool readFromFile(const char* filename); bool readFromURL(const QString& url); // will support file urls as well... - bool readFromStream(unsigned long streamLength, QDataStream& inputStream, const QString& marketplaceID=""); - bool readSVOFromStream(unsigned long streamLength, QDataStream& inputStream); - bool readJSONFromStream(unsigned long streamLength, QDataStream& inputStream, const QString& marketplaceID=""); + bool readFromStream(uint64_t streamLength, QDataStream& inputStream, const QString& marketplaceID=""); + bool readSVOFromStream(uint64_t streamLength, QDataStream& inputStream); + bool readJSONFromStream(uint64_t streamLength, QDataStream& inputStream, const QString& marketplaceID=""); bool readJSONFromGzippedFile(QString qFileName); virtual bool readFromMap(QVariantMap& entityDescription) = 0; - unsigned long getOctreeElementsCount(); + uint64_t getOctreeElementsCount(); bool getShouldReaverage() const { return _shouldReaverage; } diff --git a/libraries/ui/src/CursorManager.cpp b/libraries/ui/src/CursorManager.cpp index 106574ff4f..8eb4989ab6 100644 --- a/libraries/ui/src/CursorManager.cpp +++ b/libraries/ui/src/CursorManager.cpp @@ -24,13 +24,6 @@ namespace Cursor { return _icon; } - - class MouseInstance : public Instance { - Source getType() const override { - return Source::MOUSE; - } - }; - QMap Manager::ICON_NAMES { { Icon::SYSTEM, "SYSTEM", }, { Icon::DEFAULT, "DEFAULT", }, @@ -38,7 +31,7 @@ namespace Cursor { { Icon::ARROW, "ARROW", }, { Icon::RETICLE, "RETICLE", }, }; - QMap Manager::ICONS; + static uint16_t _customIconId = Icon::USER_BASE; Manager::Manager() { @@ -62,8 +55,8 @@ namespace Cursor { } Manager& Manager::instance() { - static Manager instance; - return instance; + static QSharedPointer instance = DependencyManager::get(); + return *instance; } QList Manager::registeredIcons() const { @@ -76,7 +69,6 @@ namespace Cursor { Instance* Manager::getCursor(uint8_t index) { Q_ASSERT(index < getCount()); - static MouseInstance mouseInstance; if (index == 0) { return &mouseInstance; } diff --git a/libraries/ui/src/CursorManager.h b/libraries/ui/src/CursorManager.h index 2a92cc7f45..02ab99ee4b 100644 --- a/libraries/ui/src/CursorManager.h +++ b/libraries/ui/src/CursorManager.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include @@ -39,7 +40,15 @@ namespace Cursor { uint16_t _icon; }; - class Manager { + class MouseInstance : public Instance { + Source getType() const override { + return Source::MOUSE; + } + }; + + class Manager : public QObject, public Dependency { + SINGLETON_DEPENDENCY + Manager(); Manager(const Manager& other) = delete; public: @@ -52,12 +61,13 @@ namespace Cursor { QList registeredIcons() const; const QString& getIconImage(uint16_t icon); - static QMap ICONS; static QMap ICON_NAMES; static Icon lookupIcon(const QString& name); static const QString& getIconName(const Icon& icon); private: + MouseInstance mouseInstance; float _scale{ 1.0f }; + QMap ICONS; }; } diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index c1120f1775..b4a07ecd47 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -554,20 +554,19 @@ void OffscreenUi::createDesktop(const QUrl& url) { getSurfaceContext()->setContextProperty("DebugQML", QVariant(false)); #endif - _desktop = dynamic_cast(load(url)); - Q_ASSERT(_desktop); - getSurfaceContext()->setContextProperty("desktop", _desktop); + load(url, [=](QQmlContext* context, QObject* newObject) { + _desktop = static_cast(newObject); + getSurfaceContext()->setContextProperty("desktop", _desktop); + _toolWindow = _desktop->findChild("ToolWindow"); - _toolWindow = _desktop->findChild("ToolWindow"); + _vrMenu = new VrMenu(this); + for (const auto& menuInitializer : _queuedMenuInitializers) { + menuInitializer(_vrMenu); + } - _vrMenu = new VrMenu(this); - for (const auto& menuInitializer : _queuedMenuInitializers) { - menuInitializer(_vrMenu); - } - - new KeyboardFocusHack(); - - connect(_desktop, SIGNAL(showDesktop()), this, SIGNAL(showDesktop())); + new KeyboardFocusHack(); + connect(_desktop, SIGNAL(showDesktop()), this, SIGNAL(showDesktop())); + }); } QQuickItem* OffscreenUi::getDesktop() { diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 84466f41b0..d29dc7a0f9 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -639,7 +639,7 @@ void OffscreenQmlSurface::setBaseUrl(const QUrl& baseUrl) { _qmlContext->setBaseUrl(baseUrl); } -QObject* OffscreenQmlSurface::load(const QUrl& qmlSource, bool createNewContext, std::function f) { +void OffscreenQmlSurface::load(const QUrl& qmlSource, bool createNewContext, std::function f) { if (QThread::currentThread() != thread()) { qCWarning(uiLogging) << "Called load on a non-surface thread"; } @@ -662,32 +662,32 @@ QObject* OffscreenQmlSurface::load(const QUrl& qmlSource, bool createNewContext, [this, qmlComponent, targetContext, f](QQmlComponent::Status) { finishQmlLoad(qmlComponent, targetContext, f); }); - return nullptr; + return; } - return finishQmlLoad(qmlComponent, targetContext, f); + finishQmlLoad(qmlComponent, targetContext, f); } -QObject* OffscreenQmlSurface::loadInNewContext(const QUrl& qmlSource, std::function f) { - return load(qmlSource, true, f); +void OffscreenQmlSurface::loadInNewContext(const QUrl& qmlSource, std::function f) { + load(qmlSource, true, f); } -QObject* OffscreenQmlSurface::load(const QUrl& qmlSource, std::function f) { - return load(qmlSource, false, f); +void OffscreenQmlSurface::load(const QUrl& qmlSource, std::function f) { + load(qmlSource, false, f); } void OffscreenQmlSurface::clearCache() { _qmlContext->engine()->clearComponentCache(); } -QObject* OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, std::function f) { +void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, std::function f) { disconnect(qmlComponent, &QQmlComponent::statusChanged, this, 0); if (qmlComponent->isError()) { for (const auto& error : qmlComponent->errors()) { qCWarning(uiLogging) << error.url() << error.line() << error; } qmlComponent->deleteLater(); - return nullptr; + return; } @@ -700,7 +700,7 @@ QObject* OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlCon qFatal("Unable to finish loading QML root"); } qmlComponent->deleteLater(); - return nullptr; + return; } qmlContext->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership); @@ -726,19 +726,19 @@ QObject* OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlCon } // If we already have a root, just set a couple of flags and the ancestry - if (_rootItem) { + if (newItem && _rootItem) { // Allow child windows to be destroyed from JS QQmlEngine::setObjectOwnership(newObject, QQmlEngine::JavaScriptOwnership); newObject->setParent(_rootItem); if (newItem) { newItem->setParentItem(_rootItem); } - return newObject; + return; } if (!newItem) { qFatal("Could not load object as root item"); - return nullptr; + return; } connect(newItem, SIGNAL(sendToScript(QVariant)), this, SIGNAL(fromQml(QVariant))); @@ -747,7 +747,6 @@ QObject* OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlCon _rootItem = newItem; _rootItem->setParentItem(_quickWindow->contentItem()); _rootItem->setSize(_quickWindow->renderTargetSize()); - return _rootItem; } void OffscreenQmlSurface::updateQuick() { diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.h b/libraries/ui/src/ui/OffscreenQmlSurface.h index 3b53f6a185..7da929723c 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.h +++ b/libraries/ui/src/ui/OffscreenQmlSurface.h @@ -50,10 +50,10 @@ public: void resize(const QSize& size, bool forceResize = false); QSize size() const; - Q_INVOKABLE QObject* load(const QUrl& qmlSource, bool createNewContext, std::function f = [](QQmlContext*, QObject*) {}); - Q_INVOKABLE QObject* loadInNewContext(const QUrl& qmlSource, std::function f = [](QQmlContext*, QObject*) {}); - Q_INVOKABLE QObject* load(const QUrl& qmlSource, std::function f = [](QQmlContext*, QObject*) {}); - Q_INVOKABLE QObject* load(const QString& qmlSourceFile, std::function f = [](QQmlContext*, QObject*) {}) { + Q_INVOKABLE void load(const QUrl& qmlSource, bool createNewContext, std::function f = [](QQmlContext*, QObject*) {}); + Q_INVOKABLE void loadInNewContext(const QUrl& qmlSource, std::function f = [](QQmlContext*, QObject*) {}); + Q_INVOKABLE void load(const QUrl& qmlSource, std::function f = [](QQmlContext*, QObject*) {}); + Q_INVOKABLE void load(const QString& qmlSourceFile, std::function f = [](QQmlContext*, QObject*) {}) { return load(QUrl(qmlSourceFile), f); } void clearCache(); @@ -120,7 +120,7 @@ protected: private: static QOpenGLContext* getSharedContext(); - QObject* finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, std::function f); + void finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, std::function f); QPointF mapWindowToUi(const QPointF& sourcePosition, QObject* sourceObject); void setupFbo(); bool allowNewFrame(uint8_t fps); diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 5f07c4cbe7..5a668a3d6e 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -71,6 +71,10 @@ case 'maybeEnableHmdPreview': Menu.setIsOptionChecked("Disable Preview", isHmdPreviewDisabled); break; + case 'passphraseReset': + onButtonClicked(); + onButtonClicked(); + break; case 'walletReset': onButtonClicked(); onButtonClicked(); diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index 538fe0b1e4..832fe10d5f 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -539,7 +539,7 @@ clickMapping.from(Controller.Standard.Start).peek().to(function (clicked) { if (clicked) { activeHudPoint2dGamePad(); var noHands = -1; - Messages.sendLocalMessage("toggleHand", noHands); + Messages.sendLocalMessage("toggleHand", Controller.Standard.LeftHand); } wantsMenu = clicked; diff --git a/scripts/system/hmd.js b/scripts/system/hmd.js index c9c3dbe493..b9fd7f725c 100644 --- a/scripts/system/hmd.js +++ b/scripts/system/hmd.js @@ -68,6 +68,9 @@ function onHmdChanged(isHmd) { function onClicked() { var isDesktop = Menu.isOptionChecked(desktopMenuItemName); Menu.setIsOptionChecked(isDesktop ? headset : desktopMenuItemName, true); + if (!isDesktop) { + UserActivityLogger.logAction("exit_vr"); + } } if (headset) {