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

This commit is contained in:
samcake 2017-09-08 10:17:03 -07:00
commit 754f698349
58 changed files with 1295 additions and 726 deletions

View file

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

Binary file not shown.

View file

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

View file

@ -458,84 +458,80 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
return _truePacketsSent;
}
bool OctreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params) {
bool somethingToSend = false;
OctreeQueryNode* nodeData = static_cast<OctreeQueryNode*>(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()) {

View file

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

View file

@ -35,7 +35,7 @@
#include <QtCore/QDir>
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) :

View file

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

View file

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

View file

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

View file

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

View file

@ -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();
}
}

View file

@ -39,6 +39,8 @@ typedef QMultiHash<QUuid, WalletTransaction*> TransactionHash;
using Subnet = QPair<QHostAddress, int>;
using SubnetList = std::vector<Subnet>;
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<QHostAddress> _iceServerAddresses;
QSet<QHostAddress> _failedIceServerAddresses;
int _iceAddressLookupID { -1 };
int _iceAddressLookupID { INVALID_ICE_LOOKUP_ID };
int _noReplyICEHeartbeats { 0 };
int _numHeartbeatDenials { 0 };
bool _connectedToICEServer { false };

View file

@ -6,7 +6,14 @@
<title>Welcome to Interface</title>
<style>
body {
@font-face {
font-family: 'Raleway Light';
src: url('../fonts/Raleway-Light.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
body {
background: black;
width: 100%;
overflow-x: hidden;
@ -15,6 +22,14 @@
padding: 0;
}
a:link {color:inherit}
a:active {color:inherit}
a:visited {color:inherit}
a:hover {
color:inherit;
cursor: pointer;
}
#left_button {
position: absolute;
left: 70;
@ -38,6 +53,15 @@
position: absolute;
top: 0; left: 0; bottom: 0; right: 0;
}
#report_problem {
position: fixed;
top: 10;
right: 10;
font-family: "Raleway Light", sans-serif;
text-decoration: none;
color: #ddd;
}
</style>
<script>
var handControllerImageURL = null;
@ -152,6 +176,7 @@
<a href="#" id="left_button" onmousedown="cycleLeft()"></a>
<a href="#" id="right_button" onmousedown="cycleRight()"></a>
</div>
<a href="mailto:support@highfidelity.com" id="report_problem">Report Problem</a>
</body>
</html>

View file

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

View file

@ -9,7 +9,7 @@
//
import QtQuick 2.5
import QtWebEngine 1.2
import QtWebEngine 1.5
WebEngineView {
id: root

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -55,6 +55,7 @@ Item {
text: "DEBUG: Clear Cached Passphrase";
onClicked: {
commerce.setPassphrase("");
sendSignalToWallet({method: 'passphraseReset'});
}
}
HifiControlsUit.Button {

View file

@ -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);
}
}

View file

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

View file

@ -1,5 +1,6 @@
import QtQuick 2.5
import QtGraphicalEffects 1.0
import QtQuick.Layouts 1.3
import "../../styles-uit"
import "../audio" as HifiAudio
@ -109,15 +110,45 @@ Item {
}
}
RalewaySemiBold {
id: usernameText
text: tabletRoot.username
anchors.verticalCenter: parent.verticalCenter
Item {
width: 150
height: 50
anchors.right: parent.right
anchors.rightMargin: 20
horizontalAlignment: Text.AlignRight
font.pixelSize: 20
color: "#afafaf"
anchors.rightMargin: 30
anchors.verticalCenter: parent.verticalCenter
ColumnLayout {
anchors.fill: parent
RalewaySemiBold {
text: Account.loggedIn ? qsTr("Log out") : qsTr("Log in")
horizontalAlignment: Text.AlignRight
anchors.right: parent.right
font.pixelSize: 20
color: "#afafaf"
}
RalewaySemiBold {
visible: Account.loggedIn
height: Account.loggedIn ? parent.height/2 - parent.spacing/2 : 0
text: Account.loggedIn ? "[" + tabletRoot.usernameShort + "]" : ""
horizontalAlignment: Text.AlignRight
anchors.right: parent.right
font.pixelSize: 20
color: "#afafaf"
}
}
MouseArea {
anchors.fill: parent
onClicked: {
if (!Account.loggedIn) {
DialogsManager.showLoginDialog()
} else {
Account.logOut()
}
}
}
}
}

View file

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

View file

@ -8,6 +8,7 @@ Item {
id: tabletRoot
objectName: "tabletRoot"
property string username: "Unknown user"
property string usernameShort: "Unknown user"
property var rootMenu;
property var openModal: null;
property var openMessage: null;
@ -157,6 +158,11 @@ Item {
function setUsername(newUsername) {
username = newUsername;
usernameShort = newUsername.substring(0, 8);
if (newUsername.length > 8) {
usernameShort = usernameShort + "..."
}
}
ListModel {

View file

@ -604,6 +604,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
DependencyManager::registerInheritance<SpatialParentFinder, InterfaceParentFinder>();
// Set dependencies
DependencyManager::set<Cursor::Manager>();
DependencyManager::set<AccountManager>(std::bind(&Application::getUserAgent, qApp));
DependencyManager::set<StatTracker>();
DependencyManager::set<ScriptEngines>(ScriptEngine::CLIENT_SCRIPT);
@ -2802,35 +2803,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<bool> tutorialComplete{ "tutorialComplete", false };
Setting::Handle<bool> 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";
@ -2840,58 +2824,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<AddressManager>()->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<AddressManager>()->goToEntry();
sentTo = SENT_TO_ENTRY;
} else {
DependencyManager::get<AddressManager>()->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<AddressManager>()->goToLocalSandbox();
sentTo = SENT_TO_SANDBOX;
} else {
qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry.";
DependencyManager::get<AddressManager>()->goToEntry();
sentTo = SENT_TO_ENTRY;
}
if (sandboxIsRunning) {
qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home.";
DependencyManager::get<AddressManager>()->goToLocalSandbox();
sentTo = SENT_TO_SANDBOX;
} else {
qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry.";
DependencyManager::get<AddressManager>()->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<AddressManager>()->loadSettings(addressLookupString);
sentTo = SENT_TO_PREVIOUS_LOCATION;
}
}
UserActivityLogger::getInstance().logAction("startup_sent_to", {
{ "sent_to", sentTo },
@ -2900,18 +2857,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) {
@ -6320,11 +6269,11 @@ bool Application::askToWearAvatarAttachmentUrl(const QString& url) {
bool Application::askToReplaceDomainContent(const QString& url) {
QString methodDetails;
const int MAX_CHARACTERS_PER_LINE = 90;
if (DependencyManager::get<NodeList>()->getThisNodeCanReplaceContent()) {
QUrl originURL { url };
if (originURL.host().endsWith(MARKETPLACE_CDN_HOSTNAME)) {
// Create a confirmation dialog when this call is made
const int MAX_CHARACTERS_PER_LINE = 90;
static const QString infoText = simpleWordWrap("Your domain's content will be replaced with a new content set. "
"If you want to save what you have now, create a backup before proceeding. For more information about backing up "
"and restoring content, visit the documentation page at: ", MAX_CHARACTERS_PER_LINE) +
@ -6360,7 +6309,9 @@ bool Application::askToReplaceDomainContent(const QString& url) {
}
} else {
methodDetails = "UserDoesNotHavePermissionToReplaceContent";
OffscreenUi::warning("Unable to replace content", "You do not have permissions to replace domain content",
static const QString warningMessage = simpleWordWrap("The domain owner must enable 'Replace Content' "
"permissions for you in this domain's server settings before you can continue.", MAX_CHARACTERS_PER_LINE);
OffscreenUi::warning("You do not have permissions to replace domain content", warningMessage,
QMessageBox::Ok, QMessageBox::Ok);
}
QJsonObject messageProperties = {
@ -7438,7 +7389,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();

View file

@ -1912,6 +1912,17 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) {
const bool shouldDrawHead = shouldRenderHead(renderArgs);
if (shouldDrawHead != _prevShouldDrawHead) {
_skeletonModel->setEnableCauterization(!shouldDrawHead);
for (int i = 0; i < _attachmentData.size(); i++) {
if (_attachmentData[i].jointName.compare("Head", Qt::CaseInsensitive) == 0 ||
_attachmentData[i].jointName.compare("Neck", Qt::CaseInsensitive) == 0 ||
_attachmentData[i].jointName.compare("LeftEye", Qt::CaseInsensitive) == 0 ||
_attachmentData[i].jointName.compare("RightEye", Qt::CaseInsensitive) == 0 ||
_attachmentData[i].jointName.compare("HeadTop_End", Qt::CaseInsensitive) == 0 ||
_attachmentData[i].jointName.compare("Face", Qt::CaseInsensitive) == 0) {
_attachmentModels[i]->setVisibleInScene(shouldDrawHead, qApp->getMain3DScene());
}
}
}
_prevShouldDrawHead = shouldDrawHead;
}

View file

@ -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<Wallet>();
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());
}

View file

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

View file

@ -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<Wallet>();
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>();
wallet->generateKeyPair();
getWalletAuthenticatedStatus();
}
void QmlCommerce::reset() {
auto ledger = DependencyManager::get<Ledger>();
auto wallet = DependencyManager::get<Wallet>();
ledger->reset();
wallet->reset();
}
void QmlCommerce::account() {
auto ledger = DependencyManager::get<Ledger>();
ledger->account();
}

View file

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

View file

@ -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<Wallet>()->getPassphrase();
auto wallet = DependencyManager::get<Wallet>();
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<int>(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<Wallet>()->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<Wallet>();
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;
}

View file

@ -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();

View file

@ -18,6 +18,8 @@ AccountScriptingInterface* AccountScriptingInterface::getInstance() {
auto accountManager = DependencyManager::get<AccountManager>();
QObject::connect(accountManager.data(), &AccountManager::profileChanged,
&sharedInstance, &AccountScriptingInterface::usernameChanged);
QObject::connect(accountManager.data(), &AccountManager::usernameChanged,
&sharedInstance, &AccountScriptingInterface::onUsernameChanged);
return &sharedInstance;
}
@ -31,6 +33,21 @@ bool AccountScriptingInterface::checkAndSignalForAccessToken() {
return accountManager->checkAndSignalForAccessToken();
}
void AccountScriptingInterface::logOut() {
auto accountManager = DependencyManager::get<AccountManager>();
return accountManager->logout();
}
AccountScriptingInterface::AccountScriptingInterface(QObject *parent): QObject(parent) {
m_loggedIn = isLoggedIn();
emit loggedInChanged(m_loggedIn);
}
void AccountScriptingInterface::onUsernameChanged(QString username) {
m_loggedIn = (username != QString());
emit loggedInChanged(m_loggedIn);
}
QString AccountScriptingInterface::getUsername() {
auto accountManager = DependencyManager::get<AccountManager>();
if (accountManager->isLoggedIn()) {

View file

@ -18,6 +18,7 @@ class AccountScriptingInterface : public QObject {
Q_OBJECT
Q_PROPERTY(QString username READ getUsername NOTIFY usernameChanged)
Q_PROPERTY(bool loggedIn READ loggedIn NOTIFY loggedInChanged)
/**jsdoc
* @namespace Account
@ -32,6 +33,7 @@ signals:
* @return {Signal}
*/
void usernameChanged();
void loggedInChanged(bool loggedIn);
public slots:
static AccountScriptingInterface* getInstance();
@ -50,6 +52,20 @@ public slots:
*/
bool isLoggedIn();
bool checkAndSignalForAccessToken();
void logOut();
public:
AccountScriptingInterface(QObject* parent = nullptr);
bool loggedIn() const {
return m_loggedIn;
}
private slots:
void onUsernameChanged(QString username);
private:
bool m_loggedIn { false };
};
#endif // hifi_AccountScriptingInterface_h

View file

@ -37,6 +37,20 @@ Setting::Handle<QString>& getSetting(bool contextIsHMD, QAudio::Mode mode) {
}
}
enum AudioDeviceRole {
DisplayRole = Qt::DisplayRole,
CheckStateRole = Qt::CheckStateRole,
PeakRole = Qt::UserRole,
InfoRole = Qt::UserRole + 1
};
QHash<int, QByteArray> 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<int, QByteArray> 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<QAudioDeviceInfo>(_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<QAudioDeviceInfo>(_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<AudioInputDevice>(_devices.at(index.row()))->peak;
} else {
return AudioDeviceList::data(index, role);
}
}
void AudioDeviceList::resetDevice(bool contextIsHMD) {
auto client = DependencyManager::get<AudioClient>().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<QAudioDeviceInfo>& 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<AudioClient>()->peakValuesAvailable();
});
return _peakValuesAvailable;
}
void AudioInputDeviceList::setPeakValuesEnabled(bool enable) {
if (peakValuesAvailable() && (enable != _peakValuesEnabled)) {
DependencyManager::get<AudioClient>()->enablePeakValues(enable);
_peakValuesEnabled = enable;
emit peakValuesEnabledChanged(_peakValuesEnabled);
}
}
void AudioInputDeviceList::onPeakValueListChanged(const QList<float>& 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<AudioInputDevice>(_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<AudioClient>();
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));

View file

@ -12,6 +12,9 @@
#ifndef hifi_scripting_AudioDevices_h
#define hifi_scripting_AudioDevices_h
#include <memory>
#include <mutex>
#include <QObject>
#include <QAbstractListModel>
#include <QAudioDeviceInfo>
@ -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<AudioDevice> newDevice(const AudioDevice& device)
{ return std::make_shared<AudioDevice>(device); }
int rowCount(const QModelIndex& parent = QModelIndex()) const override { Q_UNUSED(parent); return _devices.size(); }
QHash<int, QByteArray> 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<QAudioDeviceInfo>& devices);
private:
protected:
friend class AudioDevices;
static QHash<int, QByteArray> _roles;
static Qt::ItemFlags _flags;
const QAudio::Mode _mode;
QAudioDeviceInfo _selectedDevice;
QList<AudioDevice> _devices;
QList<std::shared_ptr<AudioDevice>> _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<AudioDevice> newDevice(const AudioDevice& device) override
{ return std::make_shared<AudioInputDevice>(device); }
QVariant data(const QModelIndex& index, int role) const override;
signals:
void peakValuesEnabledChanged(bool enabled);
protected slots:
void onPeakValueListChanged(const QList<float>& 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;

View file

@ -33,7 +33,12 @@ void DialogsManagerScriptingInterface::showAddressBar() {
void DialogsManagerScriptingInterface::hideAddressBar() {
QMetaObject::invokeMethod(DependencyManager::get<DialogsManager>().data(),
"hideAddressBar", Qt::QueuedConnection);
"hideAddressBar", Qt::QueuedConnection);
}
void DialogsManagerScriptingInterface::showLoginDialog() {
QMetaObject::invokeMethod(DependencyManager::get<DialogsManager>().data(),
"showLoginDialog", Qt::QueuedConnection);
}
void DialogsManagerScriptingInterface::showFeed() {

View file

@ -24,6 +24,7 @@ public:
public slots:
void showAddressBar();
void hideAddressBar();
void showLoginDialog();
signals:
void addressBarShown(bool visible);

View file

@ -169,12 +169,14 @@ void LoginDialog::openUrl(const QString& url) const {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
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);
}

View file

@ -78,7 +78,7 @@ Setting::Handle<int> staticJitterBufferFrames("staticJitterBufferFrames",
// protect the Qt internal device list
using Mutex = std::mutex;
using Lock = std::unique_lock<Mutex>;
static Mutex _deviceMutex;
Mutex _deviceMutex;
// thread-safe
QList<QAudioDeviceInfo> 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;

View file

@ -148,6 +148,9 @@ public:
QAudioDeviceInfo getActiveAudioDevice(QAudio::Mode mode) const;
QList<QAudioDeviceInfo> 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<QAudioDeviceInfo>& devices);
void peakValueListChanged(const QList<float> 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<Lock> localAudioLock = nullptr);
bool mixLocalAudioInjectors(float* mixBuffer);
float azimuthForSource(const glm::vec3& relativePosition);
@ -298,6 +305,7 @@ private:
std::atomic<bool> _localInjectorsAvailable { false };
MixedProcessedAudioStream _receivedAudioStream;
bool _isStereoInput;
std::atomic<bool> _enablePeakValues { false };
quint64 _outputStarveDetectionStartTimeMsec;
int _outputStarveDetectionCount;
@ -396,6 +404,7 @@ private:
RateCounter<> _audioInbound;
QTimer* _checkDevicesTimer { nullptr };
QTimer* _checkPeakValuesTimer { nullptr };
};

View file

@ -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 <windows.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <audioclient.h>
#include <QString>
#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<std::wstring, std::shared_ptr<IAudioClient>> activeClients;
template <class T>
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<std::mutex> lock(_deviceMutex);
HRESULT result;
// initialize the payload
QList<float> peakValueList;
for (int i = 0; i < _inputDevices.size(); ++i) {
peakValueList.push_back(0.0f);
}
std::shared_ptr<IMMDeviceEnumerator> enumerator;
{
IMMDeviceEnumerator* pEnumerator;
result = CoCreateInstance(
__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER,
__uuidof(IMMDeviceEnumerator), (void**)&pEnumerator);
RETURN_ON_FAIL(result);
enumerator = std::shared_ptr<IMMDeviceEnumerator>(pEnumerator, &release<IMMDeviceEnumerator>);
}
std::shared_ptr<IMMDeviceCollection> endpoints;
{
IMMDeviceCollection* pEndpoints;
result = enumerator->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE, &pEndpoints);
RETURN_ON_FAIL(result);
endpoints = std::shared_ptr<IMMDeviceCollection>(pEndpoints, &release<IMMDeviceCollection>);
}
UINT count;
{
result = endpoints->GetCount(&count);
RETURN_ON_FAIL(result);
}
IMMDevice* pDevice;
std::shared_ptr<IMMDevice> device;
IAudioMeterInformation* pMeterInfo;
std::shared_ptr<IAudioMeterInformation> meterInfo;
IAudioClient* pAudioClient;
std::shared_ptr<IAudioClient> 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<IMMDevice>(pDevice, &release<IMMDevice>);
// 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<IAudioMeterInformation>(pMeterInfo, &release<IAudioMeterInformation>);
//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<IAudioClient>(pAudioClient, &release<IAudioClient>);
//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

View file

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

View file

@ -14,6 +14,7 @@
#include <memory>
#include <set>
#include <stdint.h>
#include <QHash>
#include <QObject>
@ -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; }

View file

@ -24,13 +24,6 @@ namespace Cursor {
return _icon;
}
class MouseInstance : public Instance {
Source getType() const override {
return Source::MOUSE;
}
};
QMap<uint16_t, QString> Manager::ICON_NAMES {
{ Icon::SYSTEM, "SYSTEM", },
{ Icon::DEFAULT, "DEFAULT", },
@ -38,7 +31,7 @@ namespace Cursor {
{ Icon::ARROW, "ARROW", },
{ Icon::RETICLE, "RETICLE", },
};
QMap<uint16_t, QString> 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<Manager> instance = DependencyManager::get<Cursor::Manager>();
return *instance;
}
QList<uint16_t> 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;
}

View file

@ -8,6 +8,7 @@
#pragma once
#include <stdint.h>
#include <DependencyManager.h>
#include <GLMHelpers.h>
@ -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<uint16_t> registeredIcons() const;
const QString& getIconImage(uint16_t icon);
static QMap<uint16_t, QString> ICONS;
static QMap<uint16_t, QString> ICON_NAMES;
static Icon lookupIcon(const QString& name);
static const QString& getIconName(const Icon& icon);
private:
MouseInstance mouseInstance;
float _scale{ 1.0f };
QMap<uint16_t, QString> ICONS;
};
}

View file

@ -554,20 +554,19 @@ void OffscreenUi::createDesktop(const QUrl& url) {
getSurfaceContext()->setContextProperty("DebugQML", QVariant(false));
#endif
_desktop = dynamic_cast<QQuickItem*>(load(url));
Q_ASSERT(_desktop);
getSurfaceContext()->setContextProperty("desktop", _desktop);
load(url, [=](QQmlContext* context, QObject* newObject) {
_desktop = static_cast<QQuickItem*>(newObject);
getSurfaceContext()->setContextProperty("desktop", _desktop);
_toolWindow = _desktop->findChild<QQuickItem*>("ToolWindow");
_toolWindow = _desktop->findChild<QQuickItem*>("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() {

View file

@ -639,7 +639,7 @@ void OffscreenQmlSurface::setBaseUrl(const QUrl& baseUrl) {
_qmlContext->setBaseUrl(baseUrl);
}
QObject* OffscreenQmlSurface::load(const QUrl& qmlSource, bool createNewContext, std::function<void(QQmlContext*, QObject*)> f) {
void OffscreenQmlSurface::load(const QUrl& qmlSource, bool createNewContext, std::function<void(QQmlContext*, QObject*)> 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<void(QQmlContext*, QObject*)> f) {
return load(qmlSource, true, f);
void OffscreenQmlSurface::loadInNewContext(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f) {
load(qmlSource, true, f);
}
QObject* OffscreenQmlSurface::load(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f) {
return load(qmlSource, false, f);
void OffscreenQmlSurface::load(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f) {
load(qmlSource, false, f);
}
void OffscreenQmlSurface::clearCache() {
_qmlContext->engine()->clearComponentCache();
}
QObject* OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, std::function<void(QQmlContext*, QObject*)> f) {
void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, std::function<void(QQmlContext*, QObject*)> 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() {

View file

@ -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<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
Q_INVOKABLE QObject* loadInNewContext(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
Q_INVOKABLE QObject* load(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
Q_INVOKABLE QObject* load(const QString& qmlSourceFile, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {}) {
Q_INVOKABLE void load(const QUrl& qmlSource, bool createNewContext, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
Q_INVOKABLE void loadInNewContext(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
Q_INVOKABLE void load(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
Q_INVOKABLE void load(const QString& qmlSourceFile, std::function<void(QQmlContext*, QObject*)> 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<void(QQmlContext*, QObject*)> f);
void finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, std::function<void(QQmlContext*, QObject*)> f);
QPointF mapWindowToUi(const QPointF& sourcePosition, QObject* sourceObject);
void setupFbo();
bool allowNewFrame(uint8_t fps);

View file

@ -71,6 +71,10 @@
case 'maybeEnableHmdPreview':
Menu.setIsOptionChecked("Disable Preview", isHmdPreviewDisabled);
break;
case 'passphraseReset':
onButtonClicked();
onButtonClicked();
break;
case 'walletReset':
onButtonClicked();
onButtonClicked();

View file

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

View file

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

View file

@ -134,7 +134,7 @@
if (parseInt(cost) > 0) {
var priceElement = $(this).find('.price')
priceElement.css({ "width": "auto", "padding": "3px 5px", "height": "26px" });
priceElement.text(parseFloat(cost / 100).toFixed(2) + ' HFC');
priceElement.text(cost + ' HFC');
priceElement.css({ "min-width": priceElement.width() + 10 });
}
});

View file

@ -0,0 +1,168 @@
(function () {
var FORCE_DROP_CHANNEL = "Hifi-Hand-Drop";
var proxInterval,
proxTimeout;
var _entityID;
this.preload = function (entityID) {
_entityID = entityID;
Entities.editEntity(_entityID, {
userData: '{"grabbableKey": {"grabbable": true}'
});
};
var particleTrailEntity = null;
function particleTrail() {
var props = {
type: 'ParticleEffect',
name: 'Particle',
parentID: _entityID,
isEmitting: true,
lifespan: 2.0,
maxParticles: 100,
textures: 'https://content.highfidelity.com/DomainContent/production/Particles/wispy-smoke.png',
emitRate: 50,
emitSpeed: 0,
emitterShouldTrail: true,
particleRadius: 0,
radiusSpread: 0,
radiusStart: .2,
radiusFinish: 0.1,
color: {
red: 201,
blue: 201,
green: 34
},
accelerationSpread: {
x: 0,
y: 0,
z: 0
},
alpha: 0,
alphaSpread: 0,
alphaStart: 1,
alphaFinish: 0,
polarStart: 0,
polarFinish: 0,
azimuthStart: -180,
azimuthFinish: 180
};
particleTrailEntity = Entities.addEntity(props);
}
function particleExplode() {
var entPos = Entities.getEntityProperties(_entityID, 'position').position;
var props = {
type: 'ParticleEffect',
name: 'Particle',
parentID: _entityID,
isEmitting: true,
lifespan: 2,
maxParticles: 10,
position: entPos,
textures: 'https://content.highfidelity.com/DomainContent/production/Particles/wispy-smoke.png',
emitRate: 1,
emitSpeed: 0,
emitterShouldTrail: false,
particleRadius: 1,
radiusSpread: 0,
radiusStart: 0,
radiusFinish: 1,
color: {
red: 232,
blue: 232,
green: 26
},
emitAcceleration: {
x: 0,
y: 0,
z: 0
},
alpha: 0,
alphaSpread: 0,
alphaStart: 1,
alphaFinish: .5,
polarStart: 0,
polarFinish: 0,
azimuthStart: -180,
azimuthFinish: 180
};
var explosionParticles = Entities.addEntity(props);
Entities.editEntity(_entityID, {
velocity: Vec3.ZERO,
dynamic: false
});
Script.setTimeout(function () {
Entities.deleteEntity(explosionParticles);
Entities.editEntity(_entityID, {
dynamic: true
})
}, 500);
}
function clearProxCheck() {
if (proxInterval) {
Script.clearInterval(proxInterval);
Entities.deleteEntity(particleTrailEntity);
particleTrailEntity = null;
}
if (proxTimeout) {
Script.clearTimeout(proxTimeout);
}
}
function proxCheck() {
var ballPos = Entities.getEntityProperties(_entityID, ['position']).position;
var isAnyAvatarInRange = AvatarList.isAvatarInRange(ballPos, 1);
if (isAnyAvatarInRange) {
clearProxCheck();
particleExplode();
}
}
this.startDistanceGrab = function (thisEntityID, triggerHandAndAvatarUUIDArray) {
clearProxCheck();
var triggerHand = triggerHandAndAvatarUUIDArray[0];
var avatarUUID = triggerHandAndAvatarUUIDArray[1];
var ballPos = Entities.getEntityProperties(_entityID, ['position']).position;
var MAX_DISTANCE_GRAB = 2; //meter
if (Vec3.distance(ballPos, AvatarList.getAvatar(avatarUUID).position) > MAX_DISTANCE_GRAB) {
Messages.sendMessage(FORCE_DROP_CHANNEL, triggerHand, true);
}
};
this.startNearGrab = function (thisEntityID, triggerHandAndAvatarUUIDArray) {
clearProxCheck();
};
this.releaseGrab = function (thisEntityID) {
if (particleTrailEntity === null) {
particleTrail();
}
Script.setTimeout(function () {
proxInterval = Script.setInterval(proxCheck, 50);
}, 200); // Setting a delay to give it time to leave initial avatar without proc.
proxTimeout = Script.setTimeout(function () {
clearProxCheck();
}, 10000)
};
this.collisionWithEntity = function (thisEntityID, collisionEntityID, collisionInfo) {
clearProxCheck();
};
});