Merge branch 'master' into stun-on-iface-change

including PR15765, don't rebind.
This commit is contained in:
Simon Walton 2019-06-14 09:32:06 -07:00
commit 0804ffe5eb
113 changed files with 2681 additions and 1750 deletions

View file

@ -106,3 +106,4 @@ The following build options can be used when running CMake
#### Devices
You can support external input/output devices such as Leap Motion, MIDI, and more by adding each individual SDK in the visible building path. Refer to the readme file available in each device folder in [interface/external/](interface/external) for the detailed explanation of the requirements to use the device.

View file

@ -74,7 +74,8 @@
* avatar. <em>Read-only.</em>
* @property {number} sensorToWorldScale - The scale that transforms dimensions in the user's real world to the avatar's
* size in the virtual world. <em>Read-only.</em>
* @property {boolean} hasPriority - is the avatar in a Hero zone? <em>Read-only.</em>
* @property {boolean} hasPriority - <code>true</code> if the avatar is in a "hero" zone, <code>false</code> if it isn't.
* <em>Read-only.</em>
*
* @example <caption>Create a scriptable avatar.</caption>
* (function () {
@ -138,6 +139,9 @@ public:
/// Returns the index of the joint with the specified name, or -1 if not found/unknown.
Q_INVOKABLE virtual int getJointIndex(const QString& name) const override;
/**jsdoc
* @comment Uses the base class's JSDoc.
*/
Q_INVOKABLE virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override;
/**jsdoc

View file

@ -57,7 +57,7 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessag
if (message->getSize() == 0) {
return;
}
QDataStream packetStream(message->getMessage());
// read a NodeConnectionData object from the packet so we can pass around this data while we're inspecting it
@ -88,11 +88,10 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessag
auto pendingAssignment = _pendingAssignedNodes.find(nodeConnection.connectUUID);
SharedNodePointer node;
QString username;
if (pendingAssignment != _pendingAssignedNodes.end()) {
node = processAssignmentConnectRequest(nodeConnection, pendingAssignment->second);
} else if (!STATICALLY_ASSIGNED_NODES.contains(nodeConnection.nodeType)) {
QString username;
QByteArray usernameSignature;
if (message->getBytesLeftToRead() > 0) {
@ -122,9 +121,13 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessag
nodeData->setNodeInterestSet(safeInterestSet);
nodeData->setPlaceName(nodeConnection.placeName);
qDebug() << "Allowed connection from node" << uuidStringWithoutCurlyBraces(node->getUUID())
<< "on" << message->getSenderSockAddr() << "with MAC" << nodeConnection.hardwareAddress
<< "and machine fingerprint" << nodeConnection.machineFingerprint;
qDebug() << "Allowed connection from node" << uuidStringWithoutCurlyBraces(node->getUUID())
<< "on" << message->getSenderSockAddr()
<< "with MAC" << nodeConnection.hardwareAddress
<< "and machine fingerprint" << nodeConnection.machineFingerprint
<< "user" << username
<< "reason" << QString(nodeConnection.connectReason ? "SilentDomainDisconnect" : "Connect")
<< "previous connection uptime" << nodeConnection.previousConnectionUpTime/USECS_PER_MSEC << "msec";
// signal that we just connected a node so the DomainServer can get it a list
// and broadcast its presence right away
@ -468,7 +471,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
if (node->getPublicSocket() == nodeConnection.publicSockAddr && node->getLocalSocket() == nodeConnection.localSockAddr) {
// we have a node that already has these exact sockets
// this can occur if a node is failing to connect to the domain
// remove the old node before adding the new node
qDebug() << "Deleting existing connection from same sockaddr: " << node->getUUID();
existingNodeID = node->getUUID();
@ -842,7 +845,7 @@ void DomainGatekeeper::processICEPingPacket(QSharedPointer<ReceivedMessage> mess
// before we respond to this ICE ping packet, make sure we have a peer in the list that matches
QUuid icePeerID = QUuid::fromRfc4122({ message->getRawMessage(), NUM_BYTES_RFC4122_UUID });
if (_icePeers.contains(icePeerID)) {
auto pingReplyPacket = limitedNodeList->constructICEPingReplyPacket(*message, limitedNodeList->getSessionUUID());
@ -882,7 +885,6 @@ void DomainGatekeeper::getGroupMemberships(const QString& username) {
QJsonArray groupIDs = QJsonArray::fromStringList(groupIDSet.toList());
json["groups"] = groupIDs;
// if we've already asked, wait for the answer before asking again
QString lowerUsername = username.toLower();
if (_inFlightGroupMembershipsRequests.contains(lowerUsername)) {
@ -969,7 +971,7 @@ void DomainGatekeeper::getDomainOwnerFriendsList() {
QNetworkAccessManager::GetOperation, callbackParams, QByteArray(),
NULL, QVariantMap());
}
}
void DomainGatekeeper::getDomainOwnerFriendsListJSONCallback(QNetworkReply* requestReply) {

View file

@ -1079,7 +1079,7 @@ void DomainServer::processListRequestPacket(QSharedPointer<ReceivedMessage> mess
// client-side send time of last connect/domain list request
nodeData->setLastDomainCheckinTimestamp(nodeRequestData.lastPingTimestamp);
sendDomainListToNode(sendingNode, message->getFirstPacketReceiveTime(), message->getSenderSockAddr());
sendDomainListToNode(sendingNode, message->getFirstPacketReceiveTime(), message->getSenderSockAddr(), false);
}
bool DomainServer::isInInterestSet(const SharedNodePointer& nodeA, const SharedNodePointer& nodeB) {
@ -1145,7 +1145,7 @@ void DomainServer::handleConnectedNode(SharedNodePointer newNode, quint64 reques
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(newNode->getLinkedData());
// reply back to the user with a PacketType::DomainList
sendDomainListToNode(newNode, requestReceiveTime, nodeData->getSendingSockAddr());
sendDomainListToNode(newNode, requestReceiveTime, nodeData->getSendingSockAddr(), true);
// if this node is a user (unassigned Agent), signal
if (newNode->getType() == NodeType::Agent && !nodeData->wasAssigned()) {
@ -1161,7 +1161,7 @@ void DomainServer::handleConnectedNode(SharedNodePointer newNode, quint64 reques
broadcastNewNode(newNode);
}
void DomainServer::sendDomainListToNode(const SharedNodePointer& node, quint64 requestPacketReceiveTime, const HifiSockAddr &senderSockAddr) {
void DomainServer::sendDomainListToNode(const SharedNodePointer& node, quint64 requestPacketReceiveTime, const HifiSockAddr &senderSockAddr, bool newConnection) {
const int NUM_DOMAIN_LIST_EXTENDED_HEADER_BYTES = NUM_BYTES_RFC4122_UUID + NLPacket::NUM_BYTES_LOCALID +
NUM_BYTES_RFC4122_UUID + NLPacket::NUM_BYTES_LOCALID + 4;
@ -1181,6 +1181,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, quint64 r
extendedHeaderStream << nodeData->getLastDomainCheckinTimestamp();
extendedHeaderStream << quint64(duration_cast<microseconds>(system_clock::now().time_since_epoch()).count());
extendedHeaderStream << quint64(duration_cast<microseconds>(p_high_resolution_clock::now().time_since_epoch()).count()) - requestPacketReceiveTime;
extendedHeaderStream << newConnection;
auto domainListPackets = NLPacketList::create(PacketType::DomainList, extendedHeader);
// always send the node their own UUID back

View file

@ -173,7 +173,7 @@ private:
void handleKillNode(SharedNodePointer nodeToKill);
void broadcastNodeDisconnect(const SharedNodePointer& disconnnectedNode);
void sendDomainListToNode(const SharedNodePointer& node, quint64 requestPacketReceiveTime, const HifiSockAddr& senderSockAddr);
void sendDomainListToNode(const SharedNodePointer& node, quint64 requestPacketReceiveTime, const HifiSockAddr& senderSockAddr, bool newConnection);
bool isInInterestSet(const SharedNodePointer& nodeA, const SharedNodePointer& nodeB);

View file

@ -35,6 +35,10 @@ NodeConnectionData NodeConnectionData::fromDataStream(QDataStream& dataStream, c
// now the machine fingerprint
dataStream >> newHeader.machineFingerprint;
dataStream >> newHeader.connectReason;
dataStream >> newHeader.previousConnectionUpTime;
}
dataStream >> newHeader.lastPingTimestamp;

View file

@ -31,6 +31,8 @@ public:
QString placeName;
QString hardwareAddress;
QUuid machineFingerprint;
quint32 connectReason;
quint64 previousConnectionUpTime;
QByteArray protocolVersion;
};

View file

@ -1463,7 +1463,7 @@
"data": {
"alpha": 0.0,
"desiredSpeed": 1.4,
"characteristicSpeeds": [0.5, 1.8, 2.3, 3.2, 4.5],
"characteristicSpeeds": [0.5, 1.8, 2.3, 3.0, 5.0],
"alphaVar": "moveForwardAlpha",
"desiredSpeedVar": "moveForwardSpeed"
},
@ -1509,8 +1509,8 @@
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/jog_fwd.fbx",
"startFrame": 0.0,
"endFrame": 25.0,
"startFrame": 1.0,
"endFrame": 22.0,
"timeScale": 1.0,
"loopFlag": true
},
@ -1522,7 +1522,7 @@
"data": {
"url": "qrc:///avatar/animations/run_fwd.fbx",
"startFrame": 1.0,
"endFrame": 22.0,
"endFrame": 23.0,
"timeScale": 1.0,
"loopFlag": true
},
@ -2099,4 +2099,4 @@
}
]
}
}
}

View file

@ -269,6 +269,9 @@ Item {
StatText {
text: "GPU: " + root.gpuFrameTime.toFixed(1) + " ms"
}
StatText {
text: "LOD Target: " + root.lodTargetFramerate + " Hz Angle: " + root.lodAngle + " deg"
}
StatText {
text: "Drawcalls: " + root.drawcalls
}

View file

@ -13,15 +13,14 @@ import QtQuick 2.4
import controlsUit 1.0 as HifiControlsUit
import stylesUit 1.0 as HifiStylesUit
import "LoginDialog"
FocusScope {
id: root
HifiStylesUit.HifiConstants { id: hifi }
objectName: "LoginDialog"
property bool shown: true
visible: shown
HifiStylesUit.HifiConstants { id: hifi }
anchors.fill: parent
readonly property bool isTablet: false
@ -33,12 +32,17 @@ FocusScope {
property bool keyboardRaised: false
property bool punctuationMode: false
property bool isPassword: false
property string title: ""
property string text: ""
property int titleWidth: 0
property alias bannerWidth: banner.width
property alias bannerHeight: banner.height
property string title: ""
property string text: ""
property int titleWidth: 0
property bool isHMD: HMD.active
function tryDestroy() {
root.destroy()
}

View file

@ -259,6 +259,35 @@ Item {
visible: root.expanded;
text: "Entity Servers In: " + root.entityPacketsInKbps + " kbps";
}
StatText {
visible: !root.expanded
text: "Octree Elements Server: " + root.serverElements +
" Local: " + root.localElements;
}
StatText {
visible: root.expanded
text: "Octree Sending Mode: " + root.sendingMode;
}
StatText {
visible: root.expanded
text: "Octree Packets to Process: " + root.packetStats;
}
StatText {
visible: root.expanded
text: "Octree Elements - ";
}
StatText {
visible: root.expanded
text: "\tServer: " + root.serverElements +
" Internal: " + root.serverInternal +
" Leaves: " + root.serverLeaves;
}
StatText {
visible: root.expanded
text: "\tLocal: " + root.localElements +
" Internal: " + root.localInternal +
" Leaves: " + root.localLeaves;
}
StatText {
visible: root.expanded;
text: "Downloads: " + root.downloads + "/" + root.downloadLimit +
@ -316,6 +345,9 @@ Item {
StatText {
text: "GPU frame size: " + root.gpuFrameSize.x + " x " + root.gpuFrameSize.y
}
StatText {
text: "LOD Target: " + root.lodTargetFramerate + " Hz Angle: " + root.lodAngle + " deg"
}
StatText {
text: "Drawcalls: " + root.drawcalls
}
@ -401,35 +433,6 @@ Item {
text: " out of view: " + root.shadowOutOfView +
" too small: " + root.shadowTooSmall;
}
StatText {
visible: !root.expanded
text: "Octree Elements Server: " + root.serverElements +
" Local: " + root.localElements;
}
StatText {
visible: root.expanded
text: "Octree Sending Mode: " + root.sendingMode;
}
StatText {
visible: root.expanded
text: "Octree Packets to Process: " + root.packetStats;
}
StatText {
visible: root.expanded
text: "Octree Elements - ";
}
StatText {
visible: root.expanded
text: "\tServer: " + root.serverElements +
" Internal: " + root.serverInternal +
" Leaves: " + root.serverLeaves;
}
StatText {
visible: root.expanded
text: "\tLocal: " + root.localElements +
" Internal: " + root.localInternal +
" Leaves: " + root.localLeaves;
}
StatText {
visible: root.expanded
text: "LOD: " + root.lodStatus;

View file

@ -23,43 +23,36 @@ FocusScope {
objectName: "LoginDialog"
visible: true
HifiStylesUit.HifiConstants { id: hifi }
anchors.fill: parent
width: parent.width
height: parent.height
property var tabletProxy: Tablet.getTablet("com.highfidelity.interface.tablet.system");
property bool isHMD: HMD.active
property bool gotoPreviousApp: false;
readonly property bool isTablet: true
readonly property bool isOverlay: false
property string iconText: hifi.glyphs.avatar
property int iconSize: 35
property bool keyboardEnabled: false
property bool keyboardRaised: false
property bool punctuationMode: false
property bool isPassword: false
readonly property bool isTablet: true
readonly property bool isOverlay: false
property alias text: loginKeyboard.mirroredText
property int titleWidth: 0
property alias bannerWidth: banner.width
property alias bannerHeight: banner.height
property string iconText: hifi.glyphs.avatar
property int iconSize: 35
property var pane: QtObject {
property real width: root.width
property real height: root.height
}
property int titleWidth: 0
function tryDestroy() {
tabletProxy.gotoHomeScreen();
}
property bool isHMD: HMD.active
MouseArea {
width: root.width
height: root.height
}
// TABLET SPECIFIC PROPERTIES START //
property alias text: loginKeyboard.mirroredText
width: parent.width
height: parent.height
property var tabletProxy: Tablet.getTablet("com.highfidelity.interface.tablet.system")
property bool gotoPreviousApp: false
property bool keyboardOverride: true
@ -70,7 +63,20 @@ FocusScope {
property alias loginDialog: loginDialog
property alias hifi: hifi
HifiStylesUit.HifiConstants { id: hifi }
property var pane: QtObject {
property real width: root.width
property real height: root.height
}
MouseArea {
width: root.width
height: root.height
}
// TABLET SPECIFIC PROPERTIES END //
function tryDestroy() {
tabletProxy.gotoHomeScreen();
}
Timer {
id: keyboardTimer
@ -102,6 +108,15 @@ FocusScope {
anchors.fill: parent
}
Rectangle {
z: -6
id: opaqueRect
height: parent.height
width: parent.width
opacity: 0.65
color: "black"
}
Item {
z: -5
id: bannerContainer
@ -119,15 +134,6 @@ FocusScope {
}
}
Rectangle {
z: -6
id: opaqueRect
height: parent.height
width: parent.width
opacity: 0.65
color: "black"
}
HifiControlsUit.Keyboard {
id: loginKeyboard
raised: root.keyboardEnabled && root.keyboardRaised

View file

@ -12,6 +12,8 @@ import controlsUit 1.0
OriginalDesktop.Desktop {
id: desktop
property alias toolbarObjectName: sysToolbar.objectName
MouseArea {
id: hoverWatch
anchors.fill: parent
@ -70,7 +72,12 @@ OriginalDesktop.Desktop {
x: sysToolbar.x
buttonModel: tablet ? tablet.buttons : null;
shown: tablet ? tablet.toolbarMode : false;
onVisibleChanged: {
desktop.toolbarVisibleChanged(visible, sysToolbar.objectName);
}
}
signal toolbarVisibleChanged(bool isVisible, string toolbarName);
QtSettings.Settings {
id: settings;

View file

@ -88,7 +88,7 @@ Rectangle {
Image {
id: accent
source: "../images/accent.svg"
source: "images/accent.svg"
anchors.top: parent.top
anchors.right: parent.right
width: 60
@ -122,7 +122,7 @@ Rectangle {
Tablet.playSound(TabletEnums.ButtonClick);
// Can't use `Window.location` in QML, so just use what setting `Window.location` actually calls under the hood:
// AddressManager.handleLookupString().
AddressManager.handleLookupString(LocationBookmarks.getHomeLocationAddress());
AddressManager.handleLookupString(LocationBookmarks.getAddress("hqhome"));
}
}
}

View file

Before

Width:  |  Height:  |  Size: 268 B

After

Width:  |  Height:  |  Size: 268 B

View file

@ -14,16 +14,41 @@ import stylesUit 1.0 as HifiStylesUit
import "./audio" as AudioSettings
import "./general" as GeneralSettings
import "./vr" as VrSettings
import "./dev" as DevSettings
Rectangle {
property string activeTabView: "generalTabView"
id: root
color: simplifiedUI.colors.darkBackground
anchors.fill: parent
property bool developerModeEnabled: Settings.getValue("simplifiedUI/developerModeEnabled", false)
SimplifiedConstants.SimplifiedConstants {
id: simplifiedUI
}
focus: true
Keys.onPressed: {
if ((event.key == Qt.Key_D) && (event.modifiers & Qt.ControlModifier && event.modifiers & Qt.AltModifier && event.modifiers & Qt.ShiftModifier)) {
var currentSetting = Settings.getValue("simplifiedUI/developerModeEnabled", false);
var newSetting = !currentSetting;
Settings.setValue("simplifiedUI/developerModeEnabled", newSetting);
root.developerModeEnabled = newSetting;
if (newSetting) {
console.log("Developer mode ON. You are now a developer!");
} else {
console.log("Developer mode OFF.");
if (root.activeTabView === "devTabView") {
tabListView.currentIndex = 2;
root.activeTabView = "vrTabView";
}
}
}
}
Component.onCompleted: {
root.forceActiveFocus();
}
Rectangle {
@ -49,6 +74,10 @@ Rectangle {
tabTitle: "VR"
tabViewName: "vrTabView"
}
ListElement {
tabTitle: "Dev"
tabViewName: "devTabView"
}
}
@ -70,6 +99,8 @@ Rectangle {
highlight: highlightBar
interactive: contentItem.width > width
delegate: Item {
visible: model.tabTitle !== "Dev" || (model.tabTitle === "Dev" && root.developerModeEnabled)
width: tabTitleText.paintedWidth + 64
height: parent.height
@ -125,14 +156,30 @@ Rectangle {
visible: activeTabView === "vrTabView"
anchors.fill: parent
}
DevSettings.Dev {
id: devTabViewContainer
visible: activeTabView === "devTabView"
anchors.fill: parent
}
}
Image {
source: "../images/accent.svg"
source: {
if (root.activeTabView === "generalTabView") {
"images/accent1.svg"
} else if (root.activeTabView === "audioTabView") {
"images/accent2.svg"
} else if (root.activeTabView === "vrTabView") {
"images/accent3.svg"
} else {
"images/accent3.svg"
}
}
anchors.right: parent.right
anchors.bottom: parent.bottom
width: 94
height: 175
anchors.top: tabContainer.bottom
width: 106
height: 200
}

View file

@ -57,7 +57,7 @@ Flickable {
Layout.preferredWidth: parent.width
spacing: 0
HifiStylesUit.GraphikRegular {
HifiStylesUit.GraphikSemiBold {
id: volumeControlsTitle
text: "Volume Controls"
Layout.preferredWidth: parent.width
@ -154,7 +154,7 @@ Flickable {
Layout.preferredWidth: parent.width
spacing: 0
HifiStylesUit.GraphikRegular {
HifiStylesUit.GraphikSemiBold {
id: micControlsTitle
text: "Default Mute Controls"
Layout.maximumWidth: parent.width
@ -196,7 +196,7 @@ Flickable {
Layout.preferredWidth: parent.width
spacing: 0
HifiStylesUit.GraphikRegular {
HifiStylesUit.GraphikSemiBold {
id: inputDeviceTitle
text: "Which input device?"
Layout.maximumWidth: parent.width
@ -291,7 +291,7 @@ Flickable {
Layout.preferredWidth: parent.width
spacing: 0
HifiStylesUit.GraphikRegular {
HifiStylesUit.GraphikSemiBold {
id: outputDeviceTitle
text: "Which output device?"
Layout.maximumWidth: parent.width

View file

@ -0,0 +1,97 @@
//
// Dev.qml
//
// Created by Zach Fox on 2019-06-11
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.10
import QtQuick.Controls 2.3
import "../../simplifiedConstants" as SimplifiedConstants
import "../../simplifiedControls" as SimplifiedControls
import stylesUit 1.0 as HifiStylesUit
import QtQuick.Layouts 1.3
Flickable {
id: root
contentWidth: parent.width
contentHeight: devColumnLayout.height
topMargin: 24
bottomMargin: 24
clip: true
onVisibleChanged: {
if (visible) {
root.contentX = 0;
root.contentY = -root.topMargin;
}
}
SimplifiedConstants.SimplifiedConstants {
id: simplifiedUI
}
ColumnLayout {
id: devColumnLayout
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
spacing: simplifiedUI.margins.settings.spacingBetweenSettings
ColumnLayout {
id: uiControlsContainer
Layout.preferredWidth: parent.width
spacing: 0
HifiStylesUit.GraphikSemiBold {
id: uiControlsTitle
text: "User Interface"
Layout.maximumWidth: parent.width
height: paintedHeight
size: 22
color: simplifiedUI.colors.text.white
}
HifiStylesUit.GraphikRegular {
id: uiControlsSubtitle
text: "You'll have to restart Interface after changing either of these settings. If you don't get any Toolbar apps back after restarting, run defaultScripts.js manually."
Layout.maximumWidth: parent.width
height: paintedHeight
size: 16
color: simplifiedUI.colors.text.white
wrapMode: Text.Wrap
}
ColumnLayout {
id: uiControlsSwitchGroup
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
SimplifiedControls.Switch {
id: keepMenusSwitch
width: parent.width
height: 18
labelTextOn: "Keep Old Menus (File, Edit, etc)"
checked: Settings.getValue("simplifiedUI/keepMenus", false);
onClicked: {
Settings.setValue("simplifiedUI/keepMenus", !Settings.getValue("simplifiedUI/keepMenus", false));
}
}
SimplifiedControls.Switch {
id: keepOldUIAndScriptsSwitch
width: parent.width
height: 18
labelTextOn: "Keep Old UI and Scripts"
checked: Settings.getValue("simplifiedUI/keepExistingUIAndScripts", false);
onClicked: {
Settings.setValue("simplifiedUI/keepExistingUIAndScripts", !Settings.getValue("simplifiedUI/keepExistingUIAndScripts", false));
}
}
}
}
}
}

View file

@ -51,7 +51,7 @@ Flickable {
Layout.preferredWidth: parent.width
spacing: 0
HifiStylesUit.GraphikRegular {
HifiStylesUit.GraphikSemiBold {
id: avatarNameTagsTitle
text: "Avatar Name Tags"
Layout.maximumWidth: parent.width
@ -99,9 +99,9 @@ Flickable {
Layout.preferredWidth: parent.width
spacing: 0
HifiStylesUit.GraphikRegular {
HifiStylesUit.GraphikSemiBold {
id: performanceTitle
text: "Graphics Preset"
text: "Graphics Settings"
Layout.maximumWidth: parent.width
height: paintedHeight
size: 22
@ -115,7 +115,7 @@ Flickable {
SimplifiedControls.RadioButton {
id: performanceLow
text: "Low"
text: "Low Quality" + (PlatformInfo.getTierProfiled() === PerformanceEnums.LOW ? " (Recommended)" : "")
checked: Performance.getPerformancePreset() === PerformanceEnums.LOW
onClicked: {
Performance.setPerformancePreset(PerformanceEnums.LOW);
@ -124,7 +124,7 @@ Flickable {
SimplifiedControls.RadioButton {
id: performanceMedium
text: "Medium"
text: "Medium Quality" + (PlatformInfo.getTierProfiled() === PerformanceEnums.MID ? " (Recommended)" : "")
checked: Performance.getPerformancePreset() === PerformanceEnums.MID
onClicked: {
Performance.setPerformancePreset(PerformanceEnums.MID);
@ -133,7 +133,7 @@ Flickable {
SimplifiedControls.RadioButton {
id: performanceHigh
text: "High"
text: "High Quality" + (PlatformInfo.getTierProfiled() === PerformanceEnums.HIGH ? " (Recommended)" : "")
checked: Performance.getPerformancePreset() === PerformanceEnums.HIGH
onClicked: {
Performance.setPerformancePreset(PerformanceEnums.HIGH);
@ -147,7 +147,7 @@ Flickable {
Layout.preferredWidth: parent.width
spacing: 0
HifiStylesUit.GraphikRegular {
HifiStylesUit.GraphikSemiBold {
id: cameraTitle
text: "Camera View"
Layout.maximumWidth: parent.width
@ -199,7 +199,7 @@ Flickable {
wrapMode: Text.Wrap
width: paintedWidth
height: paintedHeight
size: 22
size: 14
color: simplifiedUI.colors.text.lightBlue
MouseArea {

View file

@ -0,0 +1,4 @@
<svg width="106" height="200" viewBox="0 0 106 200" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0L106 -9.26681e-06L83.1739 10.0481L0 0Z" fill="#FFED00"/>
<path d="M83.1738 10.0481L106 -1.99552e-06L106 200L83.1738 10.0481Z" fill="#FF42A7"/>
</svg>

After

Width:  |  Height:  |  Size: 263 B

View file

@ -0,0 +1,4 @@
<svg width="106" height="200" viewBox="0 0 106 200" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0L106 -9.26681e-06L83.1739 10.0481L0 0Z" fill="#FF42A7"/>
<path d="M83.1738 10.0481L106 -1.99552e-06L106 200L83.1738 10.0481Z" fill="#009EE0"/>
</svg>

After

Width:  |  Height:  |  Size: 263 B

View file

@ -0,0 +1,4 @@
<svg width="106" height="200" viewBox="0 0 106 200" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0L106 -9.26681e-06L83.1739 10.0481L0 0Z" fill="#009EE0"/>
<path d="M83.1738 10.0481L106 -1.99552e-06L106 200L83.1738 10.0481Z" fill="#FFED00"/>
</svg>

After

Width:  |  Height:  |  Size: 263 B

View file

@ -57,7 +57,7 @@ Flickable {
Layout.preferredWidth: parent.width
spacing: 0
HifiStylesUit.GraphikRegular {
HifiStylesUit.GraphikSemiBold {
id: controlsTitle
text: "VR Movement Controls"
Layout.maximumWidth: parent.width
@ -143,7 +143,7 @@ Flickable {
Layout.preferredWidth: parent.width
spacing: 0
HifiStylesUit.GraphikRegular {
HifiStylesUit.GraphikSemiBold {
id: micControlsTitle
text: "Default Mute Controls"
Layout.maximumWidth: parent.width
@ -185,7 +185,7 @@ Flickable {
Layout.preferredWidth: parent.width
spacing: 0
HifiStylesUit.GraphikRegular {
HifiStylesUit.GraphikSemiBold {
id: inputDeviceTitle
text: "Which input device?"
Layout.maximumWidth: parent.width
@ -280,7 +280,7 @@ Flickable {
Layout.preferredWidth: parent.width
spacing: 0
HifiStylesUit.GraphikRegular {
HifiStylesUit.GraphikSemiBold {
id: outputDeviceTitle
text: "Which output device?"
Layout.maximumWidth: parent.width

View file

@ -183,7 +183,7 @@ QtObject {
readonly property QtObject settings: QtObject {
property int subtitleTopMargin: 2
property int settingsGroupTopMargin: 24
property int settingsGroupTopMargin: 14
property int spacingBetweenSettings: 48
property int spacingBetweenRadiobuttons: 14
}

View file

@ -18,6 +18,16 @@ import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same c
Rectangle {
id: root
focus: true
signal keyPressEvent(int key, int modifiers)
Keys.onPressed: {
keyPressEvent(event.key, event.modifiers);
}
signal keyReleaseEvent(int key, int modifiers)
Keys.onReleased: {
keyReleaseEvent(event.key, event.modifiers);
}
SimplifiedConstants.SimplifiedConstants {
id: simplifiedUI
@ -37,6 +47,12 @@ Rectangle {
onSkeletonModelURLChanged: {
root.updatePreviewUrl();
if ((MyAvatar.skeletonModelURL.indexOf("defaultAvatar") > -1 || MyAvatar.skeletonModelURL.indexOf("fst") === -1) &&
topBarInventoryModel.count > 0) {
Settings.setValue("simplifiedUI/alreadyAutoSelectedAvatar", true);
MyAvatar.skeletonModelURL = topBarInventoryModel.get(0).download_url;
}
}
}
@ -83,6 +99,13 @@ Rectangle {
topBarInventoryModel.getNextPage();
} else {
inventoryFullyReceived = true;
// If we have an avatar in our inventory AND we haven't already auto-selected an avatar...
if ((!Settings.getValue("simplifiedUI/alreadyAutoSelectedAvatar", false) ||
MyAvatar.skeletonModelURL.indexOf("defaultAvatar") > -1 || MyAvatar.skeletonModelURL.indexOf("fst") === -1) && topBarInventoryModel.count > 0) {
Settings.setValue("simplifiedUI/alreadyAutoSelectedAvatar", true);
MyAvatar.skeletonModelURL = topBarInventoryModel.get(0).download_url;
}
}
}
}
@ -455,5 +478,5 @@ Rectangle {
break;
}
}
signal sendToScript(var message);
signal sendToScript(var message)
}

View file

@ -34,7 +34,8 @@ StackView {
}
function pushSource(path) {
editRoot.push(Qt.resolvedUrl("../../" + path), itemProperties,
var item = Qt.createComponent(Qt.resolvedUrl("../../" + path));
editRoot.push(item, itemProperties,
StackView.Immediate);
editRoot.currentItem.sendToScript.connect(editRoot.sendToScript);
}

View file

@ -37,7 +37,8 @@ StackView {
}
function pushSource(path) {
editRoot.push(Qt.resolvedUrl("../../" + path), itemProperties,
var item = Qt.createComponent(Qt.resolvedUrl("../../" + path));
editRoot.push(item, itemProperties,
StackView.Immediate);
editRoot.currentItem.sendToScript.connect(editRoot.sendToScript);
}

View file

@ -24,7 +24,8 @@ StackView {
signal sendToScript(var message);
function pushSource(path) {
profileRoot.push(Qt.resolvedUrl(path));
var item = Qt.createComponent(Qt.resolvedUrl(path));
profileRoot.push(item);
}
function popSource() {

View file

@ -22,7 +22,8 @@ StackView {
signal sendToScript(var message);
function pushSource(path) {
profileRoot.push(Qt.reslovedUrl(path));
var item = Qt.createComponent(Qt.resolvedUrl(path));
profileRoot.push(item);
}
function popSource() {

View file

@ -5,6 +5,9 @@ import TabletScriptingInterface 1.0
Item {
id: tabletButton
// NOTE: These properties form part of the "TabletButtonProxy.ButtonProperties" type.
// Keep the type's JSDoc up to date with any changes.
property color defaultCaptionColor: "#ffffff"
property color captionColor: defaultCaptionColor
@ -18,15 +21,15 @@ Item {
property string activeText: tabletButton.text
property string activeHoverText: tabletButton.activeText
property bool isActive: false
property bool inDebugMode: false
property bool inDebugMode: false // tablet only
property bool isEntered: false
property double sortOrder: 100
property int stableOrder: 0
property var tabletRoot;
property var flickable: null
property var gridView: null
property var tabletRoot; // tablet only
property var flickable: null // tablet only
property var gridView: null // tablet only
property int buttonIndex: -1
property int buttonIndex: -1 // tablet only
width: 129
height: 129

View file

@ -22,7 +22,8 @@ StackView {
signal sendToScript(var message);
function pushSource(path) {
profileRoot.push(Qt.resolvedUrl(path));
var item = Qt.createComponent(Qt.resolvedUrl(path));
profileRoot.push(item);
}
function popSource() {

View file

@ -22,7 +22,8 @@ StackView {
signal sendToScript(var message);
function pushSource(path) {
profileRoot.push(Qt.resolvedUrl(path));
var item = Qt.createComponent(Qt.resolvedUrl(path));
profileRoot.push(item);
}
function popSource() {

View file

@ -22,7 +22,8 @@ StackView {
signal sendToScript(var message);
function pushSource(path) {
profileRoot.push(Qt.resolvedUrl(path));
var item = Qt.createComponent(Qt.resolvedUrl(path));
profileRoot.push(item);
}
function popSource() {

View file

@ -22,7 +22,8 @@ StackView {
signal sendToScript(var message);
function pushSource(path) {
profileRoot.push(Qt.resolvedUrl(path));
var item = Qt.createComponent(Qt.resolvedUrl(path));
profileRoot.push(item);
}
function popSource() {

View file

@ -3,6 +3,9 @@ import QtQuick 2.5
StateImage {
id: button
// NOTE: These properties form part of the "TabletButtonProxy.ButtonProperties" type.
// Keep the type's JSDoc up to date with any changes.
property color defaultCaptionColor: "#ffffff"
property color captionColor: defaultCaptionColor

View file

@ -950,6 +950,19 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
DependencyManager::set<GrabManager>();
DependencyManager::set<AvatarPackager>();
QString setBookmarkValue = getCmdOption(argc, constArgv, "--setBookmark");
if (!setBookmarkValue.isEmpty()) {
// Bookmarks are expected to be in a name=url form.
// An `=` character in the name or url is unsupported.
auto parts = setBookmarkValue.split("=");
if (parts.length() != 2) {
qWarning() << "Malformed setBookmark argument: " << setBookmarkValue;
} else {
qDebug() << "Setting bookmark" << parts[0] << "to" << parts[1];
DependencyManager::get<LocationBookmarks>()->insert(parts[0], parts[1]);
}
}
return previousSessionCrashed;
}
@ -2695,6 +2708,7 @@ void Application::cleanupBeforeQuit() {
}
getEntities()->shutdown(); // tell the entities system we're shutting down, so it will stop running scripts
getEntities()->clear();
// Clear any queued processing (I/O, FBX/OBJ/Texture parsing)
QThreadPool::globalInstance()->clear();
@ -5611,7 +5625,7 @@ void Application::resumeAfterLoginDialogActionTaken() {
scriptEngines->reloadLocalFiles();
// if the --scripts command-line argument was used.
if (!_defaultScriptsLocation.exists() && (arguments().indexOf(QString("--").append(SCRIPTS_SWITCH))) != -1) {
if (_defaultScriptsLocation.exists() && (arguments().indexOf(QString("--").append(SCRIPTS_SWITCH))) != -1) {
scriptEngines->loadDefaultScripts();
scriptEngines->defaultScriptsLocationOverridden(true);
} else {

View file

@ -28,6 +28,7 @@ public:
Bookmarks();
virtual void setupMenus(Menu* menubar, MenuWrapper* menu) = 0;
void insert(const QString& name, const QVariant& address); // Overwrites any existing entry with same name.
QString addressForBookmark(const QString& name) const;
protected:
@ -37,7 +38,6 @@ protected:
virtual void addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) = 0;
void enableMenuItems(bool enabled);
virtual void readFromFile();
void insert(const QString& name, const QVariant& address); // Overwrites any existing entry with same name.
void sortActions(Menu* menubar, MenuWrapper* menu);
int getMenuItemLocation(QList<QAction*> actions, const QString& name) const;
void removeBookmarkFromMenu(Menu* menubar, const QString& name);

View file

@ -351,7 +351,18 @@ float LODManager::getHMDLODTargetFPS() const {
}
float LODManager::getLODTargetFPS() const {
auto refreshRateFPS = qApp->getRefreshRateManager().getActiveRefreshRate();
// Use the current refresh rate as the recommended rate target used to cap the LOD manager control value.
// When focused, Use the Focus Inactive as the targget LOD to void abrupt changes from the lod controller.
auto& refreshRateManager = qApp->getRefreshRateManager();
auto refreshRateRegime = refreshRateManager.getRefreshRateRegime();
auto refreshRateProfile = refreshRateManager.getRefreshRateProfile();
auto refreshRateUXMode = refreshRateManager.getUXMode();
auto refreshRateFPS = refreshRateManager.getActiveRefreshRate();
if (refreshRateRegime == RefreshRateManager::RefreshRateRegime::FOCUS_ACTIVE) {
refreshRateFPS = refreshRateManager.queryRefreshRateTarget(refreshRateProfile, RefreshRateManager::RefreshRateRegime::FOCUS_INACTIVE, refreshRateUXMode);
}
auto lodTargetFPS = getDesktopLODTargetFPS();
if (qApp->isHMDMode()) {
lodTargetFPS = getHMDLODTargetFPS();

View file

@ -67,6 +67,10 @@ QString LocationBookmarks::getHomeLocationAddress() {
return addressForBookmark(HOME_BOOKMARK);
}
QString LocationBookmarks::getAddress(const QString& bookmarkName) {
return addressForBookmark(bookmarkName);
}
void LocationBookmarks::teleportToBookmark() {
QAction* action = qobject_cast<QAction*>(sender());
QString address = action->data().toString();

View file

@ -34,6 +34,13 @@ public:
void setupMenus(Menu* menubar, MenuWrapper* menu) override;
static const QString HOME_BOOKMARK;
/**jsdoc
* @function LocationBookmarks.getAddress
* @param {string} bookmarkName Name of the bookmark to get the address for.
* @returns {string} The url for the specified bookmark. If the bookmark does not exist, the empty string will be returned.
*/
Q_INVOKABLE QString getAddress(const QString& bookmarkName);
public slots:
/**jsdoc
@ -48,7 +55,7 @@ public slots:
void setHomeLocationToAddress(const QVariant& address);
/**jsdoc
* @function LocationBookmarksgetHomeLocationAddress
* @function LocationBookmarks.getHomeLocationAddress
* @returns {string} The url for the home location bookmark
*/
QString getHomeLocationAddress();

View file

@ -13,6 +13,7 @@
#include <platform/Profiler.h>
#include "scripting/RenderScriptingInterface.h"
#include "LODManager.h"
PerformanceManager::PerformanceManager()
{
@ -62,17 +63,29 @@ PerformanceManager::PerformancePreset PerformanceManager::getPerformancePreset()
void PerformanceManager::applyPerformancePreset(PerformanceManager::PerformancePreset preset) {
// Ugly case that prevent us to run deferred everywhere...
bool isDeferredCapable = platform::Profiler::isRenderMethodDeferredCapable();
switch (preset) {
case PerformancePreset::HIGH:
RenderScriptingInterface::getInstance()->setRenderMethod(RenderScriptingInterface::RenderMethod::DEFERRED);
RenderScriptingInterface::getInstance()->setRenderMethod( ( isDeferredCapable ?
RenderScriptingInterface::RenderMethod::DEFERRED :
RenderScriptingInterface::RenderMethod::FORWARD ) );
RenderScriptingInterface::getInstance()->setShadowsEnabled(true);
qApp->getRefreshRateManager().setRefreshRateProfile(RefreshRateManager::RefreshRateProfile::REALTIME);
DependencyManager::get<LODManager>()->setWorldDetailQuality(0.5f);
break;
case PerformancePreset::MID:
RenderScriptingInterface::getInstance()->setRenderMethod(RenderScriptingInterface::RenderMethod::DEFERRED);
RenderScriptingInterface::getInstance()->setRenderMethod((isDeferredCapable ?
RenderScriptingInterface::RenderMethod::DEFERRED :
RenderScriptingInterface::RenderMethod::FORWARD));
RenderScriptingInterface::getInstance()->setShadowsEnabled(false);
qApp->getRefreshRateManager().setRefreshRateProfile(RefreshRateManager::RefreshRateProfile::INTERACTIVE);
DependencyManager::get<LODManager>()->setWorldDetailQuality(0.5f);
break;
case PerformancePreset::LOW:
@ -80,6 +93,8 @@ void PerformanceManager::applyPerformancePreset(PerformanceManager::PerformanceP
RenderScriptingInterface::getInstance()->setShadowsEnabled(false);
qApp->getRefreshRateManager().setRefreshRateProfile(RefreshRateManager::RefreshRateProfile::ECO);
DependencyManager::get<LODManager>()->setWorldDetailQuality(0.75f);
break;
case PerformancePreset::UNKNOWN:
default:

View file

@ -107,9 +107,7 @@ RefreshRateManager::RefreshRateProfile RefreshRateManager::getRefreshRateProfile
RefreshRateManager::RefreshRateProfile profile = RefreshRateManager::RefreshRateProfile::REALTIME;
if (getUXMode() != RefreshRateManager::UXMode::VR) {
profile =(RefreshRateManager::RefreshRateProfile) _refreshRateProfileSettingLock.resultWithReadLock<int>([&] {
return _refreshRateProfileSetting.get();
});
return _refreshRateProfile;
}
return profile;
@ -138,15 +136,17 @@ void RefreshRateManager::setUXMode(RefreshRateManager::UXMode uxMode) {
}
}
int RefreshRateManager::queryRefreshRateTarget(RefreshRateProfile profile, RefreshRateRegime regime, UXMode uxMode) const {
int targetRefreshRate = VR_TARGET_RATE;
if (uxMode == RefreshRateManager::UXMode::DESKTOP) {
targetRefreshRate = REFRESH_RATE_PROFILES[profile][regime];
}
return targetRefreshRate;
}
void RefreshRateManager::updateRefreshRateController() const {
if (_refreshRateOperator) {
int targetRefreshRate;
if (_uxMode == RefreshRateManager::UXMode::DESKTOP) {
targetRefreshRate = REFRESH_RATE_PROFILES[_refreshRateProfile][_refreshRateRegime];
} else {
targetRefreshRate = VR_TARGET_RATE;
}
int targetRefreshRate = queryRefreshRateTarget(_refreshRateProfile, _refreshRateRegime, _uxMode);
_refreshRateOperator(targetRefreshRate);
_activeRefreshRate = targetRefreshRate;
}

View file

@ -65,6 +65,9 @@ public:
int getActiveRefreshRate() const { return _activeRefreshRate; }
void updateRefreshRateController() const;
// query the refresh rate target at the specified combination
int queryRefreshRateTarget(RefreshRateProfile profile, RefreshRateRegime regime, UXMode uxMode) const;
void resetInactiveTimer();
void toggleInactive();

View file

@ -490,7 +490,7 @@ void AvatarManager::buildPhysicsTransaction(PhysicsEngine::Transaction& transact
_myAvatar->getCharacterController()->buildPhysicsTransaction(transaction);
for (auto avatar : _otherAvatarsToChangeInPhysics) {
bool isInPhysics = avatar->isInPhysicsSimulation();
if (isInPhysics != avatar->shouldBeInPhysicsSimulation()) {
if (isInPhysics != avatar->shouldBeInPhysicsSimulation() || avatar->_needsReinsertion) {
if (isInPhysics) {
transaction.objectsToRemove.push_back(avatar->_motionState);
avatar->_motionState = nullptr;

View file

@ -2488,12 +2488,12 @@ QVariantList MyAvatar::getAvatarEntitiesVariant() {
QVariantMap avatarEntityData;
avatarEntityData["id"] = entityID;
EntityItemProperties entityProperties = entity->getProperties(desiredProperties);
QScriptValue scriptProperties;
{
std::lock_guard<std::mutex> guard(_scriptEngineLock);
QScriptValue scriptProperties;
scriptProperties = EntityItemPropertiesToScriptValue(_scriptEngine, entityProperties);
avatarEntityData["properties"] = scriptProperties.toVariant();
}
avatarEntityData["properties"] = scriptProperties.toVariant();
avatarEntitiesData.append(QVariant(avatarEntityData));
}
}

View file

@ -39,6 +39,23 @@ class ModelItemID;
class MyHead;
class DetailedMotionState;
/**jsdoc
* <p>Locomotion control types.</p>
* <table>
* <thead>
* <tr><th>Value</th><th>Name</th><th>Description</th></tr>
* </thead>
* <tbody>
* <tr><td><code>0</code></td><td>Default</td><td>Your walking speed is constant; it doesn't change depending on how far
* forward you push your controller's joystick. Fully pushing your joystick forward makes your avatar run.</td></tr>
* <tr><td><code>1</code></td><td>Analog</td><td>Your walking speed changes in steps based on how far forward you push your
* controller's joystick. Fully pushing your joystick forward makes your avatar run.</td></tr>
* <tr><td><code>2</code></td><td>AnalogPlus</td><td>Your walking speed changes proportionally to how far forward you push
* your controller's joystick. Fully pushing your joystick forward makes your avatar run.</td></tr>
* </tbody>
* </table>
* @typedef {number} MyAvatar.LocomotionControlsMode
*/
enum LocomotionControlsMode {
CONTROLS_DEFAULT = 0,
CONTROLS_ANALOG,
@ -128,6 +145,8 @@ class MyAvatar : public Avatar {
* avatar. <em>Read-only.</em>
* @property {number} sensorToWorldScale - The scale that transforms dimensions in the user's real world to the avatar's
* size in the virtual world. <em>Read-only.</em>
* @property {boolean} hasPriority - <code>true</code> if the avatar is in a "hero" zone, <code>false</code> if it isn't.
* <em>Read-only.</em>
*
* @comment IMPORTANT: This group of properties is copied from Avatar.h; they should NOT be edited here.
* @property {Vec3} skeletonOffset - Can be used to apply a translation offset between the avatar's position and the
@ -239,9 +258,16 @@ class MyAvatar : public Avatar {
* where MyAvatar.sessionUUID is not available (e.g., if not connected to a domain). Note: Likely to be deprecated.
* <em>Read-only.</em>
*
* @property {number} walkSpeed - The walk speed of your avatar.
* @property {number} walkBackwardSpeed - The walk backward speed of your avatar.
* @property {number} sprintSpeed - The sprint speed of your avatar.
* @property {number} walkSpeed - The walk speed of your avatar for the current control scheme (see
* {@link MyAvatar.getControlScheme|getControlScheme}).
* @property {number} walkBackwardSpeed - The walk backward speed of your avatar for the current control scheme (see
* {@link MyAvatar.getControlScheme|getControlScheme}).
* @property {number} sprintSpeed - The sprint (run) speed of your avatar for the current control scheme (see
* {@link MyAvatar.getControlScheme|getControlScheme}).
* @property {number} analogPlusWalkSpeed - The walk speed of your avatar for the "AnalogPlus" control scheme.
* <p><strong>Warning:</strong> Setting this value also sets the value of <code>analogPlusSprintSpeed</code> to twice
* the value.</p>
* @property {number} analogPlusSprintSpeed - The sprint speed of your avatar for the "AnalogPlus" control scheme.
* @property {MyAvatar.SitStandModelType} userRecenterModel - Controls avatar leaning and recentering behavior.
* @property {number} isInSittingState - <code>true</code> if your avatar is sitting (avatar leaning is disabled,
* recenntering is enabled), <code>false</code> if it is standing (avatar leaning is enabled, and avatar recenters if it
@ -281,6 +307,7 @@ class MyAvatar : public Avatar {
* @borrows Avatar.updateAvatarEntity as updateAvatarEntity
* @borrows Avatar.clearAvatarEntity as clearAvatarEntity
* @borrows Avatar.setForceFaceTrackerConnected as setForceFaceTrackerConnected
* @borrows Avatar.setSkeletonModelURL as setSkeletonModelURL
* @borrows Avatar.getAttachmentData as getAttachmentData
* @borrows Avatar.setAttachmentData as setAttachmentData
* @borrows Avatar.attach as attach
@ -308,7 +335,6 @@ class MyAvatar : public Avatar {
* @comment Avatar.setAbsoluteJointTranslationInObjectFrame as setAbsoluteJointTranslationInObjectFrame - Don't borrow because implementation is different.
* @borrows Avatar.getTargetScale as getTargetScale
* @borrows Avatar.resetLastSent as resetLastSent
* @borrows Avatar.hasPriority as hasPriority
*/
// FIXME: `glm::vec3 position` is not accessible from QML, so this exposes position in a QML-native type
Q_PROPERTY(QVector3D qmlPosition READ getQmlPosition)
@ -583,14 +609,13 @@ public:
* the avatar will move in unpredictable ways. For more information about avatar joint orientation standards, see
* <a href="https://docs.highfidelity.com/create/avatars/avatar-standards">Avatar Standards</a>.</p>
* @function MyAvatar.overrideAnimation
* @param url {string} The URL to the animation file. Animation files need to be FBX format, but only need to contain the
* @param {string} url - The URL to the animation file. Animation files need to be FBX format, but only need to contain the
* avatar skeleton and animation data.
* @param fps {number} The frames per second (FPS) rate for the animation playback. 30 FPS is normal speed.
* @param loop {boolean} Set to true if the animation should loop.
* @param firstFrame {number} The frame the animation should start at.
* @param lastFrame {number} The frame the animation should end at.
* @param {number} fps - The frames per second (FPS) rate for the animation playback. 30 FPS is normal speed.
* @param {boolean} loop - <code>true</code> if the animation should loop, <code>false</code> if it shouldn't.
* @param {number} firstFrame - The frame to start the animation at.
* @param {number} lastFrame - The frame to end the animation at.
* @example <caption> Play a clapping animation on your avatar for three seconds. </caption>
* // Clap your hands for 3 seconds then restore animation back to the avatar.
* var ANIM_URL = "https://s3.amazonaws.com/hifi-public/animations/ClapAnimations/ClapHands_Standing.fbx";
* MyAvatar.overrideAnimation(ANIM_URL, 30, true, 0, 53);
* Script.setTimeout(function () {
@ -601,18 +626,18 @@ public:
Q_INVOKABLE void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame);
/**jsdoc
* <code>overrideHandAnimation()</code> Gets the overrides the default hand poses that are triggered with controller buttons.
* use {@link MyAvatar.restoreHandAnimation}.</p> to restore the default poses.
* Overrides the default hand poses that are triggered with controller buttons.
* Use {@link MyAvatar.restoreHandAnimation} to restore the default poses.
* @function MyAvatar.overrideHandAnimation
* @param isLeft {boolean} Set true if using the left hand
* @param url {string} The URL to the animation file. Animation files need to be FBX format, but only need to contain the
* @param isLeft {boolean} <code>true</code> to override the left hand, <code>false</code> to override the right hand.
* @param {string} url - The URL of the animation file. Animation files need to be FBX format, but only need to contain the
* avatar skeleton and animation data.
* @param fps {number} The frames per second (FPS) rate for the animation playback. 30 FPS is normal speed.
* @param loop {boolean} Set to true if the animation should loop.
* @param firstFrame {number} The frame the animation should start at.
* @param lastFrame {number} The frame the animation should end at
* @example <caption> Override left hand animation for three seconds. </caption>
* // Override the left hand pose then restore the default pose.
* @param {number} fps - The frames per second (FPS) rate for the animation playback. 30 FPS is normal speed.
* @param {boolean} loop - <code>true</code> if the animation should loop, <code>false</code> if it shouldn't.
* @param {number} firstFrame - The frame to start the animation at.
* @param {number} lastFrame - The frame to end the animation at.
* @example <caption> Override left hand animation for three seconds.</caption>
* var ANIM_URL = "https://s3.amazonaws.com/hifi-public/animations/ClapAnimations/ClapHands_Standing.fbx";
* MyAvatar.overrideHandAnimation(isLeft, ANIM_URL, 30, true, 0, 53);
* Script.setTimeout(function () {
* MyAvatar.restoreHandAnimation();
@ -629,7 +654,6 @@ public:
* animation, this function has no effect.</p>
* @function MyAvatar.restoreAnimation
* @example <caption> Play a clapping animation on your avatar for three seconds. </caption>
* // Clap your hands for 3 seconds then restore animation back to the avatar.
* var ANIM_URL = "https://s3.amazonaws.com/hifi-public/animations/ClapAnimations/ClapHands_Standing.fbx";
* MyAvatar.overrideAnimation(ANIM_URL, 30, true, 0, 53);
* Script.setTimeout(function () {
@ -639,16 +663,15 @@ public:
Q_INVOKABLE void restoreAnimation();
/**jsdoc
* Restores the default hand animation state machine that is driven by the state machine in the avatar-animation json.
* Restores the default hand animation state machine that is driven by the state machine in the avatar-animation JSON.
* <p>The avatar animation system includes a set of default animations along with rules for how those animations are blended
* together with procedural data (such as look at vectors, hand sensors etc.). Playing your own custom animations will
* override the default animations. <code>restoreHandAnimation()</code> is used to restore the default hand poses
* If you aren't currently playing an override hand
* animation, this function has no effect.</p>
* override the default animations. <code>restoreHandAnimation()</code> is used to restore the default hand poses.
* If you aren't currently playing an override hand animation, this function has no effect.</p>
* @function MyAvatar.restoreHandAnimation
* @param isLeft {boolean} Set to true if using the left hand
* @example <caption> Override left hand animation for three seconds. </caption>
* // Override the left hand pose then restore the default pose.
* var ANIM_URL = "https://s3.amazonaws.com/hifi-public/animations/ClapAnimations/ClapHands_Standing.fbx";
* MyAvatar.overrideHandAnimation(isLeft, ANIM_URL, 30, true, 0, 53);
* Script.setTimeout(function () {
* MyAvatar.restoreHandAnimation();
@ -689,12 +712,13 @@ public:
* the avatar will move in unpredictable ways. For more information about avatar joint orientation standards, see
* <a href="https://docs.highfidelity.com/create/avatars/avatar-standards">Avatar Standards</a>.
* @function MyAvatar.overrideRoleAnimation
* @param role {string} The animation role to override
* @param url {string} The URL to the animation file. Animation files need to be in FBX format, but only need to contain the avatar skeleton and animation data.
* @param fps {number} The frames per second (FPS) rate for the animation playback. 30 FPS is normal speed.
* @param loop {boolean} Set to true if the animation should loop
* @param firstFrame {number} The frame the animation should start at
* @param lastFrame {number} The frame the animation should end at
* @param {string} role - The animation role to override
* @param {string} url - The URL to the animation file. Animation files need to be in FBX format, but only need to contain
* the avatar skeleton and animation data.
* @param {number} fps - The frames per second (FPS) rate for the animation playback. 30 FPS is normal speed.
* @param {boolean} loop - <code>true</code> if the animation should loop, <code>false</code> if it shouldn't.
* @param {number} firstFrame - The frame the animation should start at.
* @param {number} lastFrame - The frame the animation should end at.
* @example <caption>The default avatar-animation.json defines an "idleStand" animation role. This role specifies that when the avatar is not moving,
* an animation clip of the avatar idling with hands hanging at its side will be used. It also specifies that when the avatar moves, the animation
* will smoothly blend to the walking animation used by the "walkFwd" animation role.
@ -782,33 +806,42 @@ public:
* mode.
*/
Q_INVOKABLE bool getSnapTurn() const { return _useSnapTurn; }
/**jsdoc
* Sets whether your should do snap turns or smooth turns in HMD mode.
* Sets whether you do snap turns or smooth turns in HMD mode.
* @function MyAvatar.setSnapTurn
* @param {boolean} on - <code>true</code> to do snap turns in HMD mode; <code>false</code> to do smooth turns in HMD mode.
*/
Q_INVOKABLE void setSnapTurn(bool on) { _useSnapTurn = on; }
/**
/**jsdoc
* Gets the control scheme that is in use.
* @function MyAvatar.getControlScheme
* @returns {number}
*/
* @returns {MyAvatar.LocomotionControlsMode} The control scheme that is in use.
*/
Q_INVOKABLE int getControlScheme() const { return _controlSchemeIndex; }
/**
/**jsdoc
* Sets the control scheme to use.
* @function MyAvatar.setControlScheme
* @param {number} index
*/
* @param {MyAvatar.LocomotionControlsMode} controlScheme - The control scheme to use.
*/
Q_INVOKABLE void setControlScheme(int index) { _controlSchemeIndex = (index >= 0 && index <= 2) ? index : 0; }
/**jsdoc
* Gets whether your avatar hovers when its feet are not on the ground.
* @function MyAvatar.hoverWhenUnsupported
* @returns {boolean}
* @returns {boolean} <code>true</code> if your avatar hovers when its feet are not on the ground, <code>false</code> if it
* falls.
*/
// FIXME: Should be named, getHoverWhenUnsupported().
Q_INVOKABLE bool hoverWhenUnsupported() const { return _hoverWhenUnsupported; }
/**jsdoc
* Sets whether your avatar hovers when its feet are not on the ground.
* @function MyAvatar.setHoverWhenUnsupported
* @param {boolean} on
* @param {boolean} hover - <code>true</code> if your avatar hovers when its feet are not on the ground, <code>false</code>
* if it falls.
*/
Q_INVOKABLE void setHoverWhenUnsupported(bool on) { _hoverWhenUnsupported = on; }
@ -826,26 +859,31 @@ public:
* @returns {string} <code>"left"</code> for the left hand, <code>"right"</code> for the right hand.
*/
Q_INVOKABLE QString getDominantHand() const;
/**jsdoc
* @function MyAVatar.setStrafeEnabled
* @param {bool} enabled
*/
* Sets whether strafing is enabled.
* @function MyAvatar.setStrafeEnabled
* @param {boolean} enabled - <code>true</code> if strafing is enabled, <code>false</code> if it isn't.
*/
Q_INVOKABLE void setStrafeEnabled(bool enabled);
/**jsdoc
* @function MyAvatar.getStrafeEnabled
* @returns {bool}
*/
* Gets whether strafing is enabled.
* @function MyAvatar.getStrafeEnabled
* @returns {boolean} <code>true</code> if strafing is enabled, <code>false</code> if it isn't.
*/
Q_INVOKABLE bool getStrafeEnabled() const;
/**jsdoc
* Sets the HMD alignment relative to your avatar.
* @function MyAvatar.setHmdAvatarAlignmentType
* @param {string} type - <code>"head"</code> to align your head and your avatar's head, <code>"eyes"</code> to align your
* eyes and your avatar's eyes.
*
*/
Q_INVOKABLE void setHmdAvatarAlignmentType(const QString& type);
/**jsdoc
* Gets the HMD alignment for your avatar.
* Gets the HMD alignment relative to your avatar.
* @function MyAvatar.getHmdAvatarAlignmentType
* @returns {string} <code>"head"</code> if aligning your head and your avatar's head, <code>"eyes"</code> if aligning your
* eyes and your avatar's eyes.
@ -1495,18 +1533,8 @@ public:
*/
Q_INVOKABLE float getDriveGear5();
/**jsdoc
* Choose the control scheme.
* @function MyAvatar.setControlSchemeIndex
* @param {number} Choose the control scheme to be used.
*/
void setControlSchemeIndex(int index);
/**jsdoc
* Check what control scheme is in use.
* @function MyAvatar.getControlSchemeIndex
* @returns {number} Returns the index associated with a given control scheme.
*/
int getControlSchemeIndex();
/**jsdoc
@ -1584,8 +1612,8 @@ public:
Q_INVOKABLE bool getCharacterControllerEnabled(); // deprecated
/**jsdoc
* @comment Different behavior to the Avatar version of this method.
* Gets the rotation of a joint relative to the avatar.
* @comment Different behavior to the Avatar version of this method.
* @function MyAvatar.getAbsoluteJointRotationInObjectFrame
* @param {number} index - The index of the joint.
* @returns {Quat} The rotation of the joint relative to the avatar.
@ -1597,8 +1625,8 @@ public:
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override;
/**jsdoc
* @comment Different behavior to the Avatar version of this method.
* Gets the translation of a joint relative to the avatar.
* @comment Different behavior to the Avatar version of this method.
* @function MyAvatar.getAbsoluteJointTranslationInObjectFrame
* @param {number} index - The index of the joint.
* @returns {Vec3} The translation of the joint relative to the avatar.
@ -2441,6 +2469,9 @@ private:
void updateEyeContactTarget(float deltaTime);
// These are made private for MyAvatar so that you will use the "use" methods instead
/**jsdoc
* @comment Borrows the base class's JSDoc.
*/
Q_INVOKABLE virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override;
virtual void updatePalms() override {}

View file

@ -132,6 +132,10 @@ static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SI
bool GraphicsEngine::shouldPaint() const {
auto displayPlugin = qApp->getActiveDisplayPlugin();
if (!displayPlugin) {
// We're shutting down
return false;
}
#ifdef DEBUG_PAINT_DELAY
static uint64_t paintDelaySamples{ 0 };
@ -175,6 +179,10 @@ void GraphicsEngine::render_performFrame() {
{
PROFILE_RANGE(render, "/getActiveDisplayPlugin");
displayPlugin = qApp->getActiveDisplayPlugin();
if (!displayPlugin) {
// We're shutting down
return;
}
}
{

View file

@ -85,6 +85,7 @@ int main(int argc, const char* argv[]) {
QCommandLineOption overrideScriptsPathOption(SCRIPTS_SWITCH, "set scripts <path>", "path");
QCommandLineOption responseTokensOption("tokens", "set response tokens <json>", "json");
QCommandLineOption displayNameOption("displayName", "set user display name <string>", "string");
QCommandLineOption setBookmarkOption("setBookmark", "set bookmark key=value pair", "string");
parser.addOption(urlOption);
parser.addOption(noLauncherOption);
@ -97,6 +98,7 @@ int main(int argc, const char* argv[]) {
parser.addOption(allowMultipleInstancesOption);
parser.addOption(responseTokensOption);
parser.addOption(displayNameOption);
parser.addOption(setBookmarkOption);
if (!parser.parse(arguments)) {
std::cout << parser.errorText().toStdString() << std::endl; // Avoid Qt log spam

View file

@ -68,6 +68,16 @@ void interactiveWindowPointerFromScriptValue(const QScriptValue& object, Interac
}
}
void InteractiveWindow::forwardKeyPressEvent(int key, int modifiers) {
QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, static_cast<Qt::KeyboardModifiers>(modifiers));
QCoreApplication::postEvent(QCoreApplication::instance(), event);
}
void InteractiveWindow::forwardKeyReleaseEvent(int key, int modifiers) {
QKeyEvent* event = new QKeyEvent(QEvent::KeyRelease, key, static_cast<Qt::KeyboardModifiers>(modifiers));
QCoreApplication::postEvent(QCoreApplication::instance(), event);
}
/**jsdoc
* A set of properties used when creating an <code>InteractiveWindow</code>.
* @typedef {object} InteractiveWindow.Properties
@ -152,12 +162,16 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap
_dockWidget->getQuickView()->rootContext()->setContextProperty(EVENT_BRIDGE_PROPERTY, this);
QObject::connect(rootItem, SIGNAL(sendToScript(QVariant)), this, SLOT(qmlToScript(const QVariant&)),
Qt::QueuedConnection);
QObject::connect(rootItem, SIGNAL(keyPressEvent(int, int)), this, SLOT(forwardKeyPressEvent(int, int)),
Qt::QueuedConnection);
QObject::connect(rootItem, SIGNAL(keyReleaseEvent(int, int)), this, SLOT(forwardKeyReleaseEvent(int, int)),
Qt::QueuedConnection);
emit mainWindow->windowGeometryChanged(qApp->getWindow()->geometry());
}
});
_dockWidget->setSource(QUrl(sourceUrl));
mainWindow->addDockWidget(dockArea, _dockWidget.get());
} else {
auto offscreenUi = DependencyManager::get<OffscreenUi>();

View file

@ -287,6 +287,9 @@ protected slots:
*/
void qmlToScript(const QVariant& message);
void forwardKeyPressEvent(int key, int modifiers);
void forwardKeyReleaseEvent(int key, int modifiers);
private:
QPointer<QObject> _qmlWindow;
std::shared_ptr<DockWidget> _dockWidget { nullptr };

View file

@ -416,6 +416,8 @@ void Stats::updateStats(bool force) {
gpuContext->getFrameStats(gpuFrameStats);
STAT_UPDATE(drawcalls, gpuFrameStats._DSNumDrawcalls);
STAT_UPDATE(lodTargetFramerate, DependencyManager::get<LODManager>()->getLODTargetFPS());
STAT_UPDATE(lodAngle, DependencyManager::get<LODManager>()->getLODAngleDeg());
// Incoming packets

View file

@ -109,6 +109,8 @@ private: \
* @property {number} shadowRendered - <em>Read-only.</em>
* @property {string} sendingMode - <em>Read-only.</em>
* @property {string} packetStats - <em>Read-only.</em>
* @property {number} lodAngle - <em>Read-only.</em>
* @property {number} lodTargetFramerate - <em>Read-only.</em>
* @property {string} lodStatus - <em>Read-only.</em>
* @property {string} timingStats - <em>Read-only.</em>
* @property {string} gameUpdateStats - <em>Read-only.</em>
@ -260,7 +262,7 @@ class Stats : public QQuickItem {
STATS_PROPERTY(int, processing, 0)
STATS_PROPERTY(int, processingPending, 0)
STATS_PROPERTY(int, triangles, 0)
STATS_PROPERTY(int, drawcalls, 0)
STATS_PROPERTY(quint32 , drawcalls, 0)
STATS_PROPERTY(int, materialSwitches, 0)
STATS_PROPERTY(int, itemConsidered, 0)
STATS_PROPERTY(int, itemOutOfView, 0)
@ -272,6 +274,8 @@ class Stats : public QQuickItem {
STATS_PROPERTY(int, shadowRendered, 0)
STATS_PROPERTY(QString, sendingMode, QString())
STATS_PROPERTY(QString, packetStats, QString())
STATS_PROPERTY(int, lodAngle, 0)
STATS_PROPERTY(int, lodTargetFramerate, 0)
STATS_PROPERTY(QString, lodStatus, QString())
STATS_PROPERTY(QString, timingStats, QString())
STATS_PROPERTY(QString, gameUpdateStats, QString())
@ -858,6 +862,20 @@ signals:
*/
void packetStatsChanged();
/**jsdoc
* Triggered when the value of the <code>lodAngle</code> property changes.
* @function Stats.lodAngleChanged
* @returns {Signal}
*/
void lodAngleChanged();
/**jsdoc
* Triggered when the value of the <code>lodTargetFramerate</code> property changes.
* @function Stats.lodTargetFramerateChanged
* @returns {Signal}
*/
void lodTargetFramerateChanged();
/**jsdoc
* Triggered when the value of the <code>lodStatus</code> property changes.
* @function Stats.lodStatusChanged

View file

@ -66,6 +66,10 @@ endfunction()
add_executable(${PROJECT_NAME} MACOSX_BUNDLE ${src_files})
set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME ${APP_NAME})
set_from_env(LAUNCHER_HMAC_SECRET LAUNCHER_HMAC_SECRET "")
if (LAUNCHER_HMAC_SECRET STREQUAL "")
message(FATAL_ERROR "LAUNCHER_HMAC_SECRET is not set")
endif()
target_compile_definitions(${PROJECT_NAME} PRIVATE LAUNCHER_HMAC_SECRET="${LAUNCHER_HMAC_SECRET}")
file(GLOB NIB_FILES "nib/*.xib")

View file

@ -11,7 +11,7 @@
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="Window">
<windowStyleMask key="styleMask" closable="YES"/>
<rect key="contentRect" x="505" y="583" width="515" height="390"/>
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1177"/>
<rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="515" height="390"/>
<autoresizingMask key="autoresizingMask"/>

View file

@ -7,8 +7,9 @@
- (void) confirmCredentials:(NSString*)username :(NSString*)password {
NSLog(@"web request started");
NSString* trimmedUsername = [username stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSString *post = [NSString stringWithFormat:@"grant_type=password&username=%@&password=%@&scope=owner",
[username stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet alphanumericCharacterSet]],
[trimmedUsername stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet alphanumericCharacterSet]],
[password stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet alphanumericCharacterSet]]];
NSData *postData = [post dataUsingEncoding:NSUTF8StringEncoding];
NSString *postLength = [NSString stringWithFormat:@"%ld", (unsigned long)[postData length]];

View file

@ -15,6 +15,31 @@ NSString* hifiBackgroundFilename = @"hifi_window";
forObject:self];
fieldEditor.insertionPointColor = insertionPointColor;
}
- (BOOL) performKeyEquivalent:(NSEvent *)event
{
if ([event type] == NSEventTypeKeyDown) {
if ([event modifierFlags] & NSEventModifierFlagCommand) {
if ([[event charactersIgnoringModifiers] isEqualToString:@"v"]) {
[NSApp sendAction:(NSSelectorFromString(@"paste:")) to:nil from:self];
return TRUE;
}
if ([[event charactersIgnoringModifiers] isEqualToString:@"c"]) {
[NSApp sendAction:(NSSelectorFromString(@"copy:")) to:nil from:self];
return TRUE;
}
if ([[event charactersIgnoringModifiers] isEqualToString:@"a"]) {
[NSApp sendAction:(NSSelectorFromString(@"selectAll:")) to:nil from:self];
return TRUE;
}
}
}
return [super performKeyEquivalent:event];
}
- (void) mouseDown:(NSEvent *)event
{
NSColor *insertionPointColor = [NSColor whiteColor];
@ -63,6 +88,30 @@ NSString* hifiBackgroundFilename = @"hifi_window";
fieldEditor.insertionPointColor = insertionPointColor;
return status;
}
- (BOOL) performKeyEquivalent:(NSEvent *)event
{
if ([event type] == NSEventTypeKeyDown) {
if ([event modifierFlags] & NSEventModifierFlagCommand) {
if ([[event charactersIgnoringModifiers] isEqualToString:@"v"]) {
[NSApp sendAction:(NSSelectorFromString(@"paste:")) to:nil from:self];
return TRUE;
}
if ([[event charactersIgnoringModifiers] isEqualToString:@"c"]) {
[NSApp sendAction:(NSSelectorFromString(@"copy:")) to:nil from:self];
return TRUE;
}
if ([[event charactersIgnoringModifiers] isEqualToString:@"a"]) {
[NSApp sendAction:(NSSelectorFromString(@"selectAll:")) to:nil from:self];
return TRUE;
}
}
}
return [super performKeyEquivalent:event];
}
@end

View file

@ -54,7 +54,9 @@
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
NSLog(@"completed; error: %@", error);
if (error) {
[[Launcher sharedLauncher] displayErrorPage];
}
}
@end

View file

@ -66,6 +66,9 @@
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
NSLog(@"completed; error: %@", error);
if (error) {
[[Launcher sharedLauncher] displayErrorPage];
}
}

View file

@ -57,6 +57,7 @@ typedef enum LoginErrorTypes
- (BOOL) loginShouldSetErrorState;
- (void) displayErrorPage;
- (void) showLoginScreen;
- (NSString*) getLauncherPath;
- (ProcessState) currentProccessState;
- (void) setCurrentProcessState:(ProcessState) aProcessState;
- (void) setLoginErrorState:(LoginError) aLoginError;

View file

@ -46,6 +46,7 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
}
-(void)awakeFromNib {
[[NSApplication sharedApplication] activateIgnoringOtherApps:TRUE];
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:@selector(didTerminateApp:)
name:NSWorkspaceDidTerminateApplicationNotification
@ -73,6 +74,11 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
return filePath;
}
- (NSString*) getLauncherPath
{
return [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/Contents/MacOS/"];
}
- (void) extractZipFileAtDestination:(NSString *)destination :(NSString*)file
{
NSTask* task = [[NSTask alloc] init];
@ -109,6 +115,7 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
userInfo:nil
repeats:NO];
}
[[NSApplication sharedApplication] activateIgnoringOtherApps:TRUE];
}
- (void) setDownloadContextFilename:(NSString *)aFilename
@ -174,7 +181,7 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
- (NSString*) getAppPath
{
return [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/Contents/MacOS/"];
return [self getDownloadPathForContentAndScripts];
}
- (BOOL) loginShouldSetErrorState
@ -272,6 +279,7 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
-(void)onSplashScreenTimerFinished:(NSTimer *)timer
{
[[NSApplication sharedApplication] activateIgnoringOtherApps:TRUE];
[self showLoginScreen];
}
@ -317,7 +325,7 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
- (void) launchInterface
{
NSString* launcherPath = [[self getAppPath] stringByAppendingString:@"HQ Launcher"];
NSString* launcherPath = [[self getLauncherPath] stringByAppendingString:@"HQ Launcher"];
[[Settings sharedSettings] setLauncherPath:launcherPath];
[[Settings sharedSettings] save];
@ -331,6 +339,7 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
NSString* scriptsPath = [[self getAppPath] stringByAppendingString:@"interface.app/Contents/Resources/scripts/simplifiedUI/"];
NSString* domainUrl = [[Settings sharedSettings] getDomainUrl];
NSString* userToken = [[Launcher sharedLauncher] getTokenString];
NSString* homeBookmark = [[NSString stringWithFormat:@"hqhome="] stringByAppendingString:domainUrl];
NSArray* arguments;
if (userToken != nil) {
arguments = [NSArray arrayWithObjects:
@ -338,21 +347,21 @@ static BOOL const DELETE_ZIP_FILES = TRUE;
@"--tokens", userToken,
@"--cache", contentPath,
@"--displayName", displayName,
@"--script", scriptsPath,
@"--scripts", scriptsPath,
@"--setBookmark", homeBookmark,
@"--no-updater",
@"--no-launcher", nil];
} else {
arguments = [NSArray arrayWithObjects:
@"--url" , domainUrl,
@"--cache", contentPath,
@"--script", scriptsPath,
@"--scripts", scriptsPath,
@"--setBookmark", homeBookmark,
@"--no-updater",
@"--no-launcher", nil];
}
[workspace launchApplicationAtURL:url options:NSWorkspaceLaunchNewInstance configuration:[NSDictionary dictionaryWithObject:arguments forKey:NSWorkspaceLaunchConfigurationArguments] error:&error];
//NSLog(@"arguments %@", [NSDictionary dictionaryWithObject:arguments forKey:NSWorkspaceLaunchConfigurationArguments]);
[NSApp terminate:self];
}

View file

@ -4,15 +4,15 @@
#import "Launcher.h"
static NSString* const organizationURL = @"https://s3.amazonaws.com/hifi-public/huffman/organizations/";
static NSString* const organizationURL = @"https://orgs.highfidelity.com/organizations/";
@implementation OrganizationRequest
- (void) confirmOrganization:(NSString*)aOrganization :(NSString*)aUsername {
self.username = aUsername;
NSString* trimmedOrgString = [aOrganization stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
const char *cKey = LAUNCHER_HMAC_SECRET;
const char *cData = [[aOrganization lowercaseString] cStringUsingEncoding:NSASCIIStringEncoding];
const char *cData = [[trimmedOrgString lowercaseString] cStringUsingEncoding:NSASCIIStringEncoding];
unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA256, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
NSData *HMACData = [NSData dataWithBytes:cHMAC length:sizeof(cHMAC)];

View file

@ -41,5 +41,6 @@ int main(int argc, const char* argv[]) {
[appMenu addItem:quitMenuItem];
[appMenuItem setSubmenu:appMenu];
[[NSApplication sharedApplication] activateIgnoringOtherApps:TRUE];
return NSApplicationMain(argc, argv);
}

View file

@ -50,6 +50,11 @@ function(set_from_env _RESULT_NAME _ENV_VAR_NAME _DEFAULT_VALUE)
endfunction()
set_from_env(LAUNCHER_HMAC_SECRET LAUNCHER_HMAC_SECRET "")
if (LAUNCHER_HMAC_SECRET STREQUAL "")
message(FATAL_ERROR "LAUNCHER_HMAC_SECRET is not set")
endif()
target_compile_definitions(${PROJECT_NAME} PRIVATE LAUNCHER_HMAC_SECRET="${LAUNCHER_HMAC_SECRET}")

View file

@ -20,7 +20,7 @@
// CLauncherApp
BEGIN_MESSAGE_MAP(CLauncherApp, CWinApp)
ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
END_MESSAGE_MAP()
CLauncherApp::CLauncherApp(){}
@ -32,60 +32,60 @@ CLauncherApp theApp;
// CLauncherApp initialization
BOOL CLauncherApp::InitInstance() {
// don't launch if already running
CreateMutex(NULL, TRUE, _T("HQ_Launcher_Mutex"));
if (GetLastError() == ERROR_ALREADY_EXISTS) {
return FALSE;
}
int iNumOfArgs;
LPWSTR* pArgs = CommandLineToArgvW(GetCommandLine(), &iNumOfArgs);
if (iNumOfArgs > 1 && CString(pArgs[1]).Compare(_T("--uninstall")) == 0) {
_manager.uninstall();
} else {
_manager.init();
}
if (!_manager.installLauncher()) {
return FALSE;
}
installFont(IDR_FONT_REGULAR);
installFont(IDR_FONT_BOLD);
CWinApp::InitInstance();
// don't launch if already running
CreateMutex(NULL, TRUE, _T("HQ_Launcher_Mutex"));
if (GetLastError() == ERROR_ALREADY_EXISTS) {
return FALSE;
}
int iNumOfArgs;
LPWSTR* pArgs = CommandLineToArgvW(GetCommandLine(), &iNumOfArgs);
if (iNumOfArgs > 1 && CString(pArgs[1]).Compare(_T("--uninstall")) == 0) {
_manager.uninstall();
} else {
_manager.init();
}
if (!_manager.installLauncher()) {
return FALSE;
}
installFont(IDR_FONT_REGULAR);
installFont(IDR_FONT_BOLD);
CWinApp::InitInstance();
SetRegistryKey(_T("HQ High Fidelity"));
SetRegistryKey(_T("HQ High Fidelity"));
CLauncherDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
CLauncherDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
#if !defined(_AFXDLL) && !defined(_AFX_NO_MFC_CONTROLS_IN_DIALOGS)
ControlBarCleanUp();
ControlBarCleanUp();
#endif
// Since the dialog has been closed, return FALSE so that we exit the
// application, rather than start the application's message pump.
return FALSE;
// Since the dialog has been closed, return FALSE so that we exit the
// application, rather than start the application's message pump.
return FALSE;
}
BOOL CLauncherApp::installFont(int fontID) {
HINSTANCE hResInstance = AfxGetResourceHandle();
HRSRC res = FindResource(hResInstance,
MAKEINTRESOURCE(fontID), L"BINARY");
if (res) {
HGLOBAL mem = LoadResource(hResInstance, res);
void *data = LockResource(mem);
DWORD len = (DWORD)SizeofResource(hResInstance, res);
HINSTANCE hResInstance = AfxGetResourceHandle();
HRSRC res = FindResource(hResInstance,
MAKEINTRESOURCE(fontID), L"BINARY");
if (res) {
HGLOBAL mem = LoadResource(hResInstance, res);
void *data = LockResource(mem);
DWORD len = (DWORD)SizeofResource(hResInstance, res);
DWORD nFonts;
auto m_fonthandle = AddFontMemResourceEx(
data, // font resource
len, // number of bytes in font resource
NULL, // Reserved. Must be 0.
&nFonts // number of fonts installed
);
DWORD nFonts;
auto m_fonthandle = AddFontMemResourceEx(
data, // font resource
len, // number of bytes in font resource
NULL, // Reserved. Must be 0.
&nFonts // number of fonts installed
);
return (m_fonthandle != 0);
}
return FALSE;
return (m_fonthandle != 0);
}
return FALSE;
}

View file

@ -11,22 +11,22 @@
#pragma once
#ifndef __AFXWIN_H__
#error "include 'stdafx.h' before including this file for PCH"
#error "include 'stdafx.h' before including this file for PCH"
#endif
#include "resource.h" // main symbols
#include "resource.h" // main symbols
#include "LauncherManager.h"
class CLauncherApp : public CWinApp
{
public:
CLauncherApp();
virtual BOOL InitInstance();
void setDialogOnFront() { SetWindowPos(m_pMainWnd->GetSafeHwnd(), HWND_TOPMOST, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE); }
LauncherManager _manager;
CLauncherApp();
virtual BOOL InitInstance();
void setDialogOnFront() { SetWindowPos(m_pMainWnd->GetSafeHwnd(), HWND_TOPMOST, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE); }
LauncherManager _manager;
private:
BOOL installFont(int fontID);
DECLARE_MESSAGE_MAP()
BOOL installFont(int fontID);
DECLARE_MESSAGE_MAP()
};
extern CLauncherApp theApp;

View file

@ -40,10 +40,10 @@ static CString TROUBLE_URL = _T("https://www.highfidelity.com/hq-support");
CLauncherDlg::CLauncherDlg(CWnd* pParent)
: CDialog(IDD_LAUNCHER_DIALOG, pParent)
: CDialog(IDD_LAUNCHER_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
EnableD2DSupport();
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
EnableD2DSupport();
}
CLauncherDlg::~CLauncherDlg() {
@ -52,109 +52,116 @@ CLauncherDlg::~CLauncherDlg() {
void CLauncherDlg::DoDataExchange(CDataExchange* pDX)
{
DDX_Control(pDX, IDC_BUTTON_NEXT, m_btnNext);
DDX_Control(pDX, IDC_TROUBLE_LINK, m_trouble_link);
DDX_Control(pDX, IDC_ORGNAME, m_orgname);
DDX_Control(pDX, IDC_USERNAME, m_username);
DDX_Control(pDX, IDC_PASSWORD, m_password);
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_BUTTON_NEXT, m_btnNext);
DDX_Control(pDX, IDC_TROUBLE_LINK, m_trouble_link);
DDX_Control(pDX, IDC_ORGNAME, m_orgname);
DDX_Control(pDX, IDC_USERNAME, m_username);
DDX_Control(pDX, IDC_PASSWORD, m_password);
CDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CLauncherDlg, CDialog)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_TIMER()
ON_EN_SETFOCUS(IDC_ORGNAME, &CLauncherDlg::OnOrgEditChangeFocus)
ON_EN_SETFOCUS(IDC_USERNAME, &CLauncherDlg::OnUserEditChangeFocus)
ON_EN_SETFOCUS(IDC_PASSWORD, &CLauncherDlg::OnPassEditChangeFocus)
ON_BN_CLICKED(IDC_BUTTON_NEXT, &CLauncherDlg::OnNextClicked)
ON_BN_CLICKED(IDC_TROUBLE_LINK, &CLauncherDlg::OnTroubleClicked)
ON_WM_CTLCOLOR()
ON_WM_DRAWITEM()
ON_WM_SETCURSOR()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_TIMER()
ON_EN_SETFOCUS(IDC_ORGNAME, &CLauncherDlg::OnOrgEditChangeFocus)
ON_EN_SETFOCUS(IDC_USERNAME, &CLauncherDlg::OnUserEditChangeFocus)
ON_EN_SETFOCUS(IDC_PASSWORD, &CLauncherDlg::OnPassEditChangeFocus)
ON_BN_CLICKED(IDC_BUTTON_NEXT, &CLauncherDlg::OnNextClicked)
ON_BN_CLICKED(IDC_TROUBLE_LINK, &CLauncherDlg::OnTroubleClicked)
ON_WM_CTLCOLOR()
ON_WM_DRAWITEM()
ON_WM_SETCURSOR()
END_MESSAGE_MAP()
// CLauncherDlg message handlers
BOOL CLauncherDlg::OnInitDialog() {
CDialog::OnInitDialog();
CDialog::OnInitDialog();
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
CFont editFont;
if (LauncherUtils::getFont(GRAPHIK_REGULAR, FIELDS_FONT_SIZE, true, editFont)) {
m_orgname.SetFont(&editFont);
m_username.SetFont(&editFont);
m_password.SetFont(&editFont);
}
CFont buttonFont;
if (LauncherUtils::getFont(_T("Graphik-Bold"), BUTTON_FONT_SIZE, true, buttonFont)) {
m_btnNext.SetFont(&editFont);
}
CFont editFont;
if (LauncherUtils::getFont(GRAPHIK_REGULAR, FIELDS_FONT_SIZE, true, editFont)) {
m_orgname.SetFont(&editFont);
m_username.SetFont(&editFont);
m_password.SetFont(&editFont);
}
CFont buttonFont;
if (LauncherUtils::getFont(_T("Graphik-Bold"), BUTTON_FONT_SIZE, true, buttonFont)) {
m_btnNext.SetFont(&editFont);
}
m_message_label = (CStatic *)GetDlgItem(IDC_MESSAGE_LABEL);
m_action_label = (CStatic *)GetDlgItem(IDC_ACTION_LABEL);
m_message2_label = (CStatic *)GetDlgItem(IDC_MESSAGE2_LABEL);
m_action2_label = (CStatic *)GetDlgItem(IDC_ACTION2_LABEL);
m_message_label = (CStatic *)GetDlgItem(IDC_MESSAGE_LABEL);
m_action_label = (CStatic *)GetDlgItem(IDC_ACTION_LABEL);
m_message2_label = (CStatic *)GetDlgItem(IDC_MESSAGE2_LABEL);
m_action2_label = (CStatic *)GetDlgItem(IDC_ACTION2_LABEL);
m_orgname_banner = (CStatic *)GetDlgItem(IDC_ORGNAME_BANNER);
m_username_banner = (CStatic *)GetDlgItem(IDC_USERNAME_BANNER);
m_password_banner = (CStatic *)GetDlgItem(IDC_PASSWORD_BANNER);
m_orgname_banner = (CStatic *)GetDlgItem(IDC_ORGNAME_BANNER);
m_username_banner = (CStatic *)GetDlgItem(IDC_USERNAME_BANNER);
m_password_banner = (CStatic *)GetDlgItem(IDC_PASSWORD_BANNER);
m_terms = (CStatic *)GetDlgItem(IDC_TERMS);
m_terms2 = (CStatic *)GetDlgItem(IDC_TERMS2);
m_trouble = (CStatic *)GetDlgItem(IDC_TROUBLE);
m_terms = (CStatic *)GetDlgItem(IDC_TERMS);
m_terms2 = (CStatic *)GetDlgItem(IDC_TERMS2);
m_trouble = (CStatic *)GetDlgItem(IDC_TROUBLE);
m_voxel = (CStatic *)GetDlgItem(IDC_VOXEL);
m_voxel = (CStatic *)GetDlgItem(IDC_VOXEL);
m_voxel->EnableD2DSupport();
m_voxel->EnableD2DSupport();
m_pRenderTarget = GetRenderTarget();
m_pRenderTarget = GetRenderTarget();
SetTimer(1, 2, NULL);
return TRUE;
SetTimer(1, 2, NULL);
return TRUE;
}
BOOL CLauncherDlg::PreTranslateMessage(MSG* pMsg) {
if ((pMsg->message == WM_KEYDOWN))
{
if (pMsg->wParam == VK_RETURN)
{
OnNextClicked();
return TRUE;
}
}
return CDialog::PreTranslateMessage(pMsg);
if ((pMsg->message == WM_KEYDOWN))
{
if (pMsg->wParam == 'A' && GetKeyState(VK_CONTROL) < 0) {
CWnd* wnd = GetFocus();
CWnd* myWnd = this->GetDlgItem(IDC_ORGNAME);
if (wnd && (wnd == this->GetDlgItem(IDC_ORGNAME) ||
wnd == this->GetDlgItem(IDC_USERNAME) ||
wnd == this->GetDlgItem(IDC_PASSWORD))) {
((CEdit*)wnd)->SetSel(0, -1);
}
return TRUE;
} else if (pMsg->wParam == VK_RETURN) {
OnNextClicked();
return TRUE;
}
}
return CDialog::PreTranslateMessage(pMsg);
}
void CLauncherDlg::setCustomDialog() {
LONG lStyle = GetWindowLong(GetSafeHwnd(), GWL_STYLE);
lStyle &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU);
SetWindowLong(GetSafeHwnd(), GWL_STYLE, lStyle);
LONG lStyle = GetWindowLong(GetSafeHwnd(), GWL_STYLE);
lStyle &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU);
SetWindowLong(GetSafeHwnd(), GWL_STYLE, lStyle);
LONG lExStyle = GetWindowLong(GetSafeHwnd(), GWL_EXSTYLE);
lExStyle &= ~(WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE);
SetWindowLong(GetSafeHwnd(), GWL_EXSTYLE, lExStyle);
LONG lExStyle = GetWindowLong(GetSafeHwnd(), GWL_EXSTYLE);
lExStyle &= ~(WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE);
SetWindowLong(GetSafeHwnd(), GWL_EXSTYLE, lExStyle);
SetWindowPos(NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
// theApp.setDialogOnFront();
SetWindowPos(NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
}
void CLauncherDlg::OnPaint()
{
CPaintDC dc(this);
setCustomDialog();
CDialog::OnPaint();
CPaintDC dc(this);
setCustomDialog();
CDialog::OnPaint();
}
// The system calls this function to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CLauncherDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
return static_cast<HCURSOR>(m_hIcon);
}
void CLauncherDlg::startProcess() {
@ -202,15 +209,15 @@ void CLauncherDlg::startProcess() {
}
BOOL CLauncherDlg::getHQInfo(const CString& orgname) {
CString hash;
CString hash;
CString lowerOrgName = orgname;
lowerOrgName.MakeLower();
LauncherUtils::hMac256(lowerOrgName, LAUNCHER_HMAC_SECRET, hash);
return theApp._manager.readOrganizationJSON(hash) == LauncherUtils::ResponseError::NoError;
return theApp._manager.readOrganizationJSON(hash) == LauncherUtils::ResponseError::NoError;
}
afx_msg void CLauncherDlg::OnTroubleClicked() {
ShellExecute(0, NULL, TROUBLE_URL, NULL, NULL, SW_SHOWDEFAULT);
LauncherUtils::executeOnForeground(TROUBLE_URL, _T(""));
}
afx_msg void CLauncherDlg::OnNextClicked() {
@ -255,414 +262,421 @@ afx_msg void CLauncherDlg::OnNextClicked() {
}
void CLauncherDlg::drawBackground(CHwndRenderTarget* pRenderTarget) {
CD2DBitmap m_pBitmamBackground(pRenderTarget, IDB_PNG1, _T("PNG"));
auto size = pRenderTarget->GetSize();
CD2DRectF backRec(0.0f, 0.0f, size.width, size.height);
pRenderTarget->DrawBitmap(&m_pBitmamBackground, backRec);
CD2DBitmap m_pBitmamBackground(pRenderTarget, IDB_PNG1, _T("PNG"));
auto size = pRenderTarget->GetSize();
CD2DRectF backRec(0.0f, 0.0f, size.width, size.height);
pRenderTarget->DrawBitmap(&m_pBitmamBackground, backRec);
}
void CLauncherDlg::drawLogo(CHwndRenderTarget* pRenderTarget) {
CD2DBitmap m_pBitmamLogo(pRenderTarget, IDB_PNG2, _T("PNG"));
auto size = pRenderTarget->GetSize();
int logoWidth = 231;
int logoHeight = 181;
float logoPosX = 0.5f * (size.width - logoWidth);
float logoPosY = 0.95f * (size.height - logoHeight);
CD2DRectF logoRec(logoPosX, logoPosY, logoPosX + logoWidth, logoPosY + logoHeight);
pRenderTarget->DrawBitmap(&m_pBitmamLogo, logoRec);
CD2DBitmap m_pBitmamLogo(pRenderTarget, IDB_PNG2, _T("PNG"));
auto size = pRenderTarget->GetSize();
int logoWidth = 231;
int logoHeight = 181;
float logoPosX = 0.5f * (size.width - logoWidth);
float logoPosY = 0.95f * (size.height - logoHeight);
CD2DRectF logoRec(logoPosX, logoPosY, logoPosX + logoWidth, logoPosY + logoHeight);
pRenderTarget->DrawBitmap(&m_pBitmamLogo, logoRec);
}
void CLauncherDlg::drawSmallLogo(CHwndRenderTarget* pRenderTarget) {
CD2DBitmap m_pBitmamLogo(pRenderTarget, IDB_PNG5, _T("PNG"));
auto size = pRenderTarget->GetSize();
int padding = 6;
int logoWidth = 100;
int logoHeight = 18;
float logoPosX = size.width - logoWidth - padding;
float logoPosY = size.height - logoHeight - padding;
CD2DRectF logoRec(logoPosX, logoPosY, logoPosX + logoWidth, logoPosY + logoHeight);
pRenderTarget->DrawBitmap(&m_pBitmamLogo, logoRec);
CD2DBitmap m_pBitmamLogo(pRenderTarget, IDB_PNG5, _T("PNG"));
auto size = pRenderTarget->GetSize();
int padding = 6;
int logoWidth = 100;
int logoHeight = 18;
float logoPosX = size.width - logoWidth - padding;
float logoPosY = size.height - logoHeight - padding;
CD2DRectF logoRec(logoPosX, logoPosY, logoPosX + logoWidth, logoPosY + logoHeight);
pRenderTarget->DrawBitmap(&m_pBitmamLogo, logoRec);
}
void CLauncherDlg::drawVoxel(CHwndRenderTarget* pRenderTarget) {
CD2DBitmap m_pBitmamVoxel(pRenderTarget, IDB_PNG4, _T("PNG"));
auto size = pRenderTarget->GetSize();
int logoWidth = 132;
int logoHeight = 134;
float voxelPosX = 0.5f * (size.width - logoWidth);
float voxelPosY = 0.5f * (size.height - logoHeight);
CD2DRectF voxelRec(voxelPosX, voxelPosY, voxelPosX + logoWidth, voxelPosY + logoHeight);
auto midPoint = D2D1::Point2F(0.5f * size.width, 0.5f * size.height);
_logoRotation += 2.0f;
CD2DSolidColorBrush brush(pRenderTarget, D2D1::ColorF(0.0f, 0.0f, 0.0f));
pRenderTarget->SetTransform(D2D1::Matrix3x2F::Rotation(_logoRotation - 2.0f, midPoint));
pRenderTarget->FillRectangle(voxelRec, &brush);
pRenderTarget->SetTransform(D2D1::Matrix3x2F::Rotation(_logoRotation, midPoint));
pRenderTarget->DrawBitmap(&m_pBitmamVoxel, voxelRec);
pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
CD2DBitmap m_pBitmamVoxel(pRenderTarget, IDB_PNG4, _T("PNG"));
auto size = pRenderTarget->GetSize();
int logoWidth = 132;
int logoHeight = 134;
float voxelPosX = 0.5f * (size.width - logoWidth);
float voxelPosY = 0.5f * (size.height - logoHeight);
CD2DRectF voxelRec(voxelPosX, voxelPosY, voxelPosX + logoWidth, voxelPosY + logoHeight);
auto midPoint = D2D1::Point2F(0.5f * size.width, 0.5f * size.height);
_logoRotation += 2.0f;
CD2DSolidColorBrush brush(pRenderTarget, D2D1::ColorF(0.0f, 0.0f, 0.0f));
pRenderTarget->SetTransform(D2D1::Matrix3x2F::Rotation(_logoRotation - 2.0f, midPoint));
pRenderTarget->FillRectangle(voxelRec, &brush);
pRenderTarget->SetTransform(D2D1::Matrix3x2F::Rotation(_logoRotation, midPoint));
pRenderTarget->DrawBitmap(&m_pBitmamVoxel, voxelRec);
pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
}
void CLauncherDlg::showWindows(std::vector<CStatic*> windows, bool show) {
for (auto window : windows) {
window->ShowWindow(show ? SW_SHOW : SW_HIDE);
}
for (auto window : windows) {
window->ShowWindow(show ? SW_SHOW : SW_HIDE);
}
}
void CLauncherDlg::prepareLogin(DrawStep step) {
m_voxel->ShowWindow(SW_HIDE);
m_orgname_banner->SetWindowTextW(_T("Organization Name"));
m_username_banner->SetWindowTextW(_T("Username"));
m_password_banner->SetWindowTextW(_T("Password"));
CString editText;
m_orgname.GetWindowTextW(editText);
m_orgname_banner->ShowWindow(editText.GetLength() == 0 ? SW_SHOW : SW_HIDE);
m_username.GetWindowTextW(editText);
m_username_banner->ShowWindow(editText.GetLength() == 0 ? SW_SHOW : SW_HIDE);
m_password.GetWindowTextW(editText);
m_password_banner->ShowWindow(editText.GetLength() == 0 ? SW_SHOW : SW_HIDE);
m_orgname.ShowWindow(SW_SHOW);
m_username.ShowWindow(SW_SHOW);
m_password.ShowWindow(SW_SHOW);
CString actionText = step == DrawStep::DrawLoginLogin ? _T("Please log in") : _T("Uh-oh, we have a problem");
CString messageText = step == DrawStep::DrawLoginLogin ? _T("Be sure you've uploaded your Avatar before signing in.") :
step == DrawStep::DrawLoginErrorCred ? _T("There is a problem with your credentials\n please try again.") : _T("There is a problem with your Organization name\n please try again.");
m_action_label->SetWindowTextW(actionText);
m_message_label->SetWindowTextW(messageText);
m_action_label->ShowWindow(SW_SHOW);
m_message_label->ShowWindow(SW_SHOW);
m_btnNext.ShowWindow(SW_SHOW);
m_trouble->SetWindowTextW(_T("Having Trouble?"));
m_trouble->ShowWindow(SW_SHOW);
m_trouble_link.ShowWindow(SW_SHOW);
m_voxel->ShowWindow(SW_HIDE);
m_orgname_banner->SetWindowTextW(_T("Organization Name"));
m_username_banner->SetWindowTextW(_T("Username"));
m_password_banner->SetWindowTextW(_T("Password"));
CString editText;
m_orgname.GetWindowTextW(editText);
m_orgname_banner->ShowWindow(editText.GetLength() == 0 ? SW_SHOW : SW_HIDE);
m_username.GetWindowTextW(editText);
m_username_banner->ShowWindow(editText.GetLength() == 0 ? SW_SHOW : SW_HIDE);
m_password.GetWindowTextW(editText);
m_password_banner->ShowWindow(editText.GetLength() == 0 ? SW_SHOW : SW_HIDE);
m_orgname.ShowWindow(SW_SHOW);
m_username.ShowWindow(SW_SHOW);
m_password.ShowWindow(SW_SHOW);
CString actionText = step == DrawStep::DrawLoginLogin ? _T("Please log in") : _T("Uh-oh, we have a problem");
CString messageText = step == DrawStep::DrawLoginLogin ? _T("Be sure you've uploaded your Avatar before signing in.") :
step == DrawStep::DrawLoginErrorCred ? _T("There is a problem with your credentials\n please try again.") : _T("There is a problem with your Organization name\n please try again.");
m_action_label->SetWindowTextW(actionText);
m_message_label->SetWindowTextW(messageText);
m_action_label->ShowWindow(SW_SHOW);
m_message_label->ShowWindow(SW_SHOW);
m_btnNext.ShowWindow(SW_SHOW);
m_trouble->SetWindowTextW(_T("Having Trouble?"));
m_trouble->ShowWindow(SW_SHOW);
m_trouble_link.ShowWindow(SW_SHOW);
}
void CLauncherDlg::prepareChoose() {
m_orgname.ShowWindow(SW_HIDE);
m_username.SetWindowTextW(_T(""));
m_username_banner->SetWindowTextW(_T("Display Name"));
CString editText;
m_username.GetWindowTextW(editText);
m_username_banner->ShowWindow(editText.GetLength() == 0 ? SW_SHOW : SW_HIDE);
m_password.ShowWindow(SW_HIDE);
m_orgname_banner->ShowWindow(SW_HIDE);
m_password_banner->ShowWindow(SW_HIDE);
m_action_label->SetWindowTextW(_T("Choose a display name"));
m_message_label->SetWindowTextW(_T("This is the name that your teammates will see."));
m_terms->ShowWindow(SW_SHOW);
m_terms2->ShowWindow(SW_SHOW);
m_terms->SetWindowTextW(_T("By signing in, you agree to the High Fidelity"));
m_terms2->SetWindowTextW(_T("Terms of Service"));
CRect rec;
m_btnNext.GetWindowRect(&rec);
ScreenToClient(&rec);
if (rec.top > 281) {
rec.bottom -= 35;
rec.top -= 35;
m_btnNext.MoveWindow(rec, FALSE);
}
m_btnNext.ShowWindow(SW_SHOW);
m_orgname.ShowWindow(SW_HIDE);
m_username.SetWindowTextW(_T(""));
m_username_banner->SetWindowTextW(_T("Display Name"));
CString editText;
m_username.GetWindowTextW(editText);
m_username_banner->ShowWindow(editText.GetLength() == 0 ? SW_SHOW : SW_HIDE);
m_password.ShowWindow(SW_HIDE);
m_orgname_banner->ShowWindow(SW_HIDE);
m_password_banner->ShowWindow(SW_HIDE);
m_action_label->SetWindowTextW(_T("Choose a display name"));
m_message_label->SetWindowTextW(_T("This is the name that your teammates will see."));
m_terms->ShowWindow(SW_SHOW);
m_terms2->ShowWindow(SW_SHOW);
m_terms->SetWindowTextW(_T("By signing in, you agree to the High Fidelity"));
m_terms2->SetWindowTextW(_T("Terms of Service"));
CRect rec;
m_btnNext.GetWindowRect(&rec);
ScreenToClient(&rec);
if (rec.top > 281) {
rec.bottom -= 35;
rec.top -= 35;
m_btnNext.MoveWindow(rec, FALSE);
}
m_btnNext.ShowWindow(SW_SHOW);
}
void CLauncherDlg::prepareProcess(DrawStep step) {
m_trouble->ShowWindow(SW_HIDE);
m_trouble_link.ShowWindow(SW_HIDE);
m_terms->ShowWindow(SW_HIDE);
m_terms2->ShowWindow(SW_HIDE);
m_orgname_banner->ShowWindow(SW_HIDE);
m_username_banner->ShowWindow(SW_HIDE);
m_password_banner->ShowWindow(SW_HIDE);
m_orgname.ShowWindow(SW_HIDE);
m_username.ShowWindow(SW_HIDE);
m_password.ShowWindow(SW_HIDE);
m_action_label->SetWindowTextW(_T(""));
m_message_label->SetWindowTextW(_T(""));
m_btnNext.ShowWindow(SW_HIDE);
m_action_label->ShowWindow(SW_HIDE);
m_message_label->ShowWindow(SW_HIDE);
m_voxel->ShowWindow(SW_SHOW);
CString actionText = _T("");
CString messageText = _T("");
switch (step) {
case DrawStep::DrawProcessSetup:
actionText = _T("We're building your virtual HQ");
messageText = _T("Set up may take several minutes.");
break;
case DrawStep::DrawProcessUpdate:
actionText = _T("Getting updates...");
messageText = _T("We're getting the latest and greatest for you, one sec.");
break;
case DrawStep::DrawProcessFinishHq:
actionText = _T("Your new HQ is all setup");
messageText = _T("Thanks for being patient.");
break;
case DrawStep::DrawProcessFinishUpdate:
actionText = _T("You're good to go!");
messageText = _T("Thanks for being patient.");
break;
case DrawStep::DrawProcessUninstall:
actionText = _T("Uninstalling...");
messageText = _T("It'll take one sec.");
break;
}
m_action2_label->SetWindowTextW(actionText);
m_message2_label->SetWindowTextW(messageText);
m_action2_label->ShowWindow(SW_SHOW);
m_message2_label->ShowWindow(SW_SHOW);
m_trouble->ShowWindow(SW_HIDE);
m_trouble_link.ShowWindow(SW_HIDE);
m_terms->ShowWindow(SW_HIDE);
m_terms2->ShowWindow(SW_HIDE);
m_orgname_banner->ShowWindow(SW_HIDE);
m_username_banner->ShowWindow(SW_HIDE);
m_password_banner->ShowWindow(SW_HIDE);
m_orgname.ShowWindow(SW_HIDE);
m_username.ShowWindow(SW_HIDE);
m_password.ShowWindow(SW_HIDE);
m_action_label->SetWindowTextW(_T(""));
m_message_label->SetWindowTextW(_T(""));
m_btnNext.ShowWindow(SW_HIDE);
m_action_label->ShowWindow(SW_HIDE);
m_message_label->ShowWindow(SW_HIDE);
m_voxel->ShowWindow(SW_SHOW);
CString actionText = _T("");
CString messageText = _T("");
switch (step) {
case DrawStep::DrawProcessSetup:
actionText = _T("We're building your virtual HQ");
messageText = _T("Set up may take several minutes.");
break;
case DrawStep::DrawProcessUpdate:
actionText = _T("Getting updates...");
messageText = _T("We're getting the latest and greatest for you, one sec.");
break;
case DrawStep::DrawProcessFinishHq:
actionText = _T("Your new HQ is all setup");
messageText = _T("Thanks for being patient.");
break;
case DrawStep::DrawProcessFinishUpdate:
actionText = _T("You're good to go!");
messageText = _T("Thanks for being patient.");
break;
case DrawStep::DrawProcessUninstall:
actionText = _T("Uninstalling...");
messageText = _T("It'll take one sec.");
break;
}
m_action2_label->SetWindowTextW(actionText);
m_message2_label->SetWindowTextW(messageText);
m_action2_label->ShowWindow(SW_SHOW);
m_message2_label->ShowWindow(SW_SHOW);
}
void CLauncherDlg::prepareError() {
}
BOOL CLauncherDlg::getTextFormat(int resID, TextFormat& formatOut) {
// Set default values for message
BOOL isText = TRUE;
formatOut.color = COLOR_LIGHT_GREY;
formatOut.isBold = false;
formatOut.isButton = false;
formatOut.size = MESSAGE_FONT_SIZE;
formatOut.underlined = false;
switch (resID) {
case IDC_VOXEL:
case IDD_LAUNCHER_DIALOG:
isText = FALSE;
case IDC_MESSAGE_LABEL:
case IDC_MESSAGE2_LABEL:
// Default values
break;
case IDC_ACTION_LABEL:
case IDC_ACTION2_LABEL:
formatOut.size = ACTION_FONT_SIZE;
formatOut.isBold = true;
formatOut.color = COLOR_LIGHTER_GREY;
break;
case IDC_USERNAME:
case IDC_PASSWORD:
case IDC_ORGNAME:
formatOut.color = COLOR_WHITE;
formatOut.size = FIELDS_FONT_SIZE;
formatOut.underlined = true;
break;
case IDC_USERNAME_BANNER:
case IDC_PASSWORD_BANNER:
case IDC_ORGNAME_BANNER:
formatOut.size = FIELDS_FONT_SIZE;
formatOut.color = COLOR_GREY;
break;
case IDC_TERMS:
formatOut.size = TERMS_FONT_SIZE;
break;
case IDC_TERMS2:
formatOut.size = TERMS_FONT_SIZE;
formatOut.isBold = true;
break;
case IDC_TROUBLE:
formatOut.size = TROUBLE_FONT_SIZE;
formatOut.color = COLOR_BLUE;
break;
}
return isText;
// Set default values for message
BOOL isText = TRUE;
formatOut.color = COLOR_LIGHT_GREY;
formatOut.isBold = false;
formatOut.isButton = false;
formatOut.size = MESSAGE_FONT_SIZE;
formatOut.underlined = false;
switch (resID) {
case IDC_VOXEL:
case IDD_LAUNCHER_DIALOG:
isText = FALSE;
case IDC_MESSAGE_LABEL:
case IDC_MESSAGE2_LABEL:
// Default values
break;
case IDC_ACTION_LABEL:
case IDC_ACTION2_LABEL:
formatOut.size = ACTION_FONT_SIZE;
formatOut.isBold = true;
formatOut.color = COLOR_LIGHTER_GREY;
break;
case IDC_USERNAME:
case IDC_PASSWORD:
case IDC_ORGNAME:
formatOut.color = COLOR_WHITE;
formatOut.size = FIELDS_FONT_SIZE;
formatOut.underlined = true;
break;
case IDC_USERNAME_BANNER:
case IDC_PASSWORD_BANNER:
case IDC_ORGNAME_BANNER:
formatOut.size = FIELDS_FONT_SIZE;
formatOut.color = COLOR_GREY;
break;
case IDC_TERMS:
formatOut.size = TERMS_FONT_SIZE;
break;
case IDC_TERMS2:
formatOut.size = TERMS_FONT_SIZE;
formatOut.isBold = true;
break;
case IDC_TROUBLE:
formatOut.size = TROUBLE_FONT_SIZE;
formatOut.color = COLOR_BLUE;
break;
}
return isText;
}
HBRUSH CLauncherDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
TextFormat textFormat;
int resId = pWnd->GetDlgCtrlID();
if (getTextFormat(resId, textFormat)) {
pDC->SetTextColor(textFormat.color);
pDC->SetBkMode(TRANSPARENT);
CFont textFont;
CString fontFamily = textFormat.isBold ? GRAPHIK_SEMIBOLD : GRAPHIK_REGULAR;
if (LauncherUtils::getFont(fontFamily, textFormat.size, textFormat.isBold, textFont)) {
pDC->SelectObject(&textFont);
}
if (textFormat.underlined) {
CRect rect;
pWnd->GetClientRect(&rect);
int borderThick = 1;
int padding = 4;
CRect lineRect = CRect(rect.left + padding, rect.bottom, rect.right - padding, rect.bottom + borderThick);
lineRect.MoveToY(lineRect.bottom + 1);
pDC->FillSolidRect(lineRect, COLOR_GREY);
}
}
return (HBRUSH)GetStockObject(BLACK_BRUSH);
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
TextFormat textFormat;
int resId = pWnd->GetDlgCtrlID();
if (getTextFormat(resId, textFormat)) {
pDC->SetTextColor(textFormat.color);
pDC->SetBkMode(TRANSPARENT);
CFont textFont;
CString fontFamily = textFormat.isBold ? GRAPHIK_SEMIBOLD : GRAPHIK_REGULAR;
if (LauncherUtils::getFont(fontFamily, textFormat.size, textFormat.isBold, textFont)) {
pDC->SelectObject(&textFont);
}
if (textFormat.underlined) {
CRect rect;
pWnd->GetClientRect(&rect);
int borderThick = 1;
int padding = 4;
CRect lineRect = CRect(rect.left + padding, rect.bottom, rect.right - padding, rect.bottom + borderThick);
lineRect.MoveToY(lineRect.bottom + 1);
pDC->FillSolidRect(lineRect, COLOR_GREY);
}
}
return (HBRUSH)GetStockObject(BLACK_BRUSH);
}
void CLauncherDlg::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDC dc;
dc.Attach(lpDrawItemStruct->hDC);
CRect rect = lpDrawItemStruct->rcItem;
CRect defrect = rect;
CString btnName = _T("");
int xpan = 0;
if (nIDCtl == IDC_BUTTON_NEXT) {
if (_drawStep == DrawStep::DrawChoose || _drawStep == DrawStep::DrawLoginLogin) {
btnName += _drawStep == DrawStep::DrawLoginLogin ? _T("NEXT") : _T("LOG IN");
int xpan = -20;
defrect = CRect(rect.left - xpan, rect.top, rect.right + xpan, rect.bottom);
} else {
btnName += _T("TRY AGAIN");
}
int borderThick = 2;
dc.FillSolidRect(rect, COLOR_BLACK);
dc.FillSolidRect(defrect, COLOR_WHITE);
defrect.DeflateRect(borderThick, borderThick, borderThick, borderThick);
dc.FillSolidRect(defrect, COLOR_BLACK);
UINT state = lpDrawItemStruct->itemState;
dc.SetTextColor(COLOR_WHITE);
CDC dc;
dc.Attach(lpDrawItemStruct->hDC);
CRect rect = lpDrawItemStruct->rcItem;
CRect defrect = rect;
CString btnName = _T("");
int xpan = 0;
if (nIDCtl == IDC_BUTTON_NEXT) {
if (_drawStep == DrawStep::DrawChoose || _drawStep == DrawStep::DrawLoginLogin) {
btnName += _drawStep == DrawStep::DrawLoginLogin ? _T("NEXT") : _T("LOG IN");
int xpan = -20;
defrect = CRect(rect.left - xpan, rect.top, rect.right + xpan, rect.bottom);
} else {
btnName += _T("TRY AGAIN");
}
int borderThick = 2;
dc.FillSolidRect(rect, COLOR_BLACK);
dc.FillSolidRect(defrect, COLOR_WHITE);
defrect.DeflateRect(borderThick, borderThick, borderThick, borderThick);
dc.FillSolidRect(defrect, COLOR_BLACK);
UINT state = lpDrawItemStruct->itemState;
dc.SetTextColor(COLOR_WHITE);
CFont buttonFont;
if (LauncherUtils::getFont(GRAPHIK_SEMIBOLD, BUTTON_FONT_SIZE, true, buttonFont)) {
dc.SelectObject(buttonFont);
}
dc.DrawText(btnName, CRect(rect.left, rect.top + 4, rect.right, rect.bottom - 8), DT_CENTER | DT_VCENTER | DT_SINGLELINE);
dc.Detach();
} else if (nIDCtl == IDC_TROUBLE_LINK) {
dc.FillSolidRect(rect, COLOR_BLACK);
dc.SetTextColor(COLOR_BLUE);
CFont buttonFont;
if (LauncherUtils::getFont(GRAPHIK_SEMIBOLD, TROUBLE_FONT_SIZE, true, buttonFont)) {
dc.SelectObject(buttonFont);
}
dc.DrawText(_T("Having Trouble"), CRect(rect.left, rect.top, rect.right, rect.bottom), DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}
CFont buttonFont;
if (LauncherUtils::getFont(GRAPHIK_SEMIBOLD, BUTTON_FONT_SIZE, true, buttonFont)) {
dc.SelectObject(buttonFont);
}
dc.DrawText(btnName, CRect(rect.left, rect.top + 4, rect.right, rect.bottom - 8), DT_CENTER | DT_VCENTER | DT_SINGLELINE);
dc.Detach();
} else if (nIDCtl == IDC_TROUBLE_LINK) {
dc.FillSolidRect(rect, COLOR_BLACK);
dc.SetTextColor(COLOR_BLUE);
CFont buttonFont;
if (LauncherUtils::getFont(GRAPHIK_SEMIBOLD, TROUBLE_FONT_SIZE, true, buttonFont)) {
dc.SelectObject(buttonFont);
}
dc.DrawText(_T("Having Trouble"), CRect(rect.left, rect.top, rect.right, rect.bottom), DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}
}
void CLauncherDlg::redrawBanner(const CEdit& edit, CStatic* banner) {
CString editText;
edit.GetWindowTextW(editText);
if (editText.GetLength() == 0) {
banner->Invalidate();
}
CString editText;
edit.GetWindowTextW(editText);
if (editText.GetLength() == 0) {
banner->Invalidate();
}
}
void CLauncherDlg::OnOrgEditChangeFocus() {
redrawBanner(m_username, m_username_banner);
redrawBanner(m_password, m_password_banner);
redrawBanner(m_username, m_username_banner);
redrawBanner(m_password, m_password_banner);
}
void CLauncherDlg::OnUserEditChangeFocus() {
redrawBanner(m_orgname, m_orgname_banner);
redrawBanner(m_password, m_password_banner);
redrawBanner(m_orgname, m_orgname_banner);
redrawBanner(m_password, m_password_banner);
}
void CLauncherDlg::OnPassEditChangeFocus() {
redrawBanner(m_orgname, m_orgname_banner);
redrawBanner(m_username, m_username_banner);
redrawBanner(m_orgname, m_orgname_banner);
redrawBanner(m_username, m_username_banner);
}
BOOL CLauncherDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if (pWnd->IsKindOf(RUNTIME_CLASS(CButton))) {
::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_HAND));
return TRUE;
}
return CDialog::OnSetCursor(pWnd, nHitTest, message);
if (pWnd->IsKindOf(RUNTIME_CLASS(CButton))) {
::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_HAND));
return TRUE;
}
return CDialog::OnSetCursor(pWnd, nHitTest, message);
}
void CLauncherDlg::OnTimer(UINT_PTR nIDEvent) {
const int CONSOLE_MAX_SHUTDOWN_TRY_COUNT = 10;
const int CONSOLE_DELTATIME_BETWEEN_TRYS = 10;
if (_drawStep == DrawStep::DrawProcessSetup ||
_drawStep == DrawStep::DrawProcessUpdate ||
_drawStep == DrawStep::DrawProcessUninstall) {
// Refresh
setDrawDialog(_drawStep, true);
}
if (_showSplash) {
if (_splashStep == 0){
const int CONSOLE_MAX_SHUTDOWN_TRY_COUNT = 10;
const int CONSOLE_DELTATIME_BETWEEN_TRYS = 10;
if (_drawStep == DrawStep::DrawProcessSetup ||
_drawStep == DrawStep::DrawProcessUpdate ||
_drawStep == DrawStep::DrawProcessUninstall) {
// Refresh
setDrawDialog(_drawStep, true);
}
if (_showSplash) {
if (_splashStep == 0){
if (theApp._manager.needsUninstall()) {
theApp._manager.addToLog(_T("Waiting to uninstall"));
setDrawDialog(DrawStep::DrawProcessUninstall);
} else {
theApp._manager.addToLog(_T("Start splash screen"));
setDrawDialog(DrawStep::DrawLogo);
}
} else if (_splashStep > 100) {
_showSplash = false;
if (theApp._manager.shouldShutDown()) {
if (LauncherUtils::IsProcessRunning(L"interface.exe")) {
exit(0);
}
} else if (theApp._manager.needsUpdate()) {
startProcess();
} else if (theApp._manager.needsUninstall()) {
theApp._manager.uninstallApplication();
exit(0);
} else {
}
} else if (_splashStep > 100) {
_showSplash = false;
if (theApp._manager.shouldShutDown()) {
if (_applicationWND != NULL) {
::SetForegroundWindow(_applicationWND);
::SetActiveWindow(_applicationWND);
}
if (LauncherUtils::IsProcessRunning(L"interface.exe")) {
exit(0);
}
} else if (theApp._manager.needsUpdate()) {
startProcess();
} else if (theApp._manager.needsUninstall()) {
theApp._manager.uninstallApplication();
exit(0);
} else {
theApp._manager.addToLog(_T("Starting login"));
setDrawDialog(DrawStep::DrawLoginLogin);
}
}
_splashStep++;
} else if (theApp._manager.shouldShutDown()) {
if (LauncherUtils::IsProcessRunning(L"interface.exe")) {
exit(0);
}
}
setDrawDialog(DrawStep::DrawLoginLogin);
}
}
_splashStep++;
} else if (theApp._manager.shouldShutDown()) {
if (LauncherUtils::IsProcessRunning(L"interface.exe")) {
exit(0);
}
}
if (theApp._manager.shouldLaunch()) {
_applicationWND = theApp._manager.launchApplication();
}
}
void CLauncherDlg::setDrawDialog(DrawStep step, BOOL isUpdate) {
_drawStep = step;
auto m_pRenderTarget = GetRenderTarget();
auto m_voxelRenderTarget = m_voxel->GetRenderTarget();
switch (_drawStep) {
case DrawStep::DrawLogo:
m_pRenderTarget->BeginDraw();
drawBackground(m_pRenderTarget);
m_pRenderTarget->EndDraw();
m_voxelRenderTarget->BeginDraw();
drawLogo(m_voxelRenderTarget);
m_voxelRenderTarget->EndDraw();
break;
case DrawStep::DrawLoginLogin:
case DrawStep::DrawLoginErrorOrg:
case DrawStep::DrawLoginErrorCred:
prepareLogin(_drawStep);
m_pRenderTarget->BeginDraw();
drawBackground(m_pRenderTarget);
drawSmallLogo(m_pRenderTarget);
m_pRenderTarget->EndDraw();
RedrawWindow();
break;
case DrawStep::DrawChoose:
prepareChoose();
m_pRenderTarget->BeginDraw();
drawBackground(m_pRenderTarget);
drawSmallLogo(m_pRenderTarget);
m_pRenderTarget->EndDraw();
RedrawWindow();
break;
case DrawStep::DrawProcessFinishHq:
case DrawStep::DrawProcessFinishUpdate:
case DrawStep::DrawProcessUpdate:
case DrawStep::DrawProcessUninstall:
case DrawStep::DrawProcessSetup:
if (!isUpdate) {
m_voxelRenderTarget->BeginDraw();
m_voxelRenderTarget->Clear(D2D1::ColorF(0.0f, 0.0f, 0.0f, 1.0f));
m_voxelRenderTarget->EndDraw();
m_pRenderTarget->BeginDraw();
prepareProcess(_drawStep);
drawBackground(m_pRenderTarget);
drawSmallLogo(m_pRenderTarget);
m_pRenderTarget->EndDraw();
RedrawWindow();
}
m_voxelRenderTarget->BeginDraw();
drawVoxel(m_voxelRenderTarget);
m_voxelRenderTarget->EndDraw();
break;
default:
break;
}
_drawStep = step;
auto m_pRenderTarget = GetRenderTarget();
auto m_voxelRenderTarget = m_voxel->GetRenderTarget();
switch (_drawStep) {
case DrawStep::DrawLogo:
m_pRenderTarget->BeginDraw();
drawBackground(m_pRenderTarget);
m_pRenderTarget->EndDraw();
m_voxelRenderTarget->BeginDraw();
drawLogo(m_voxelRenderTarget);
m_voxelRenderTarget->EndDraw();
break;
case DrawStep::DrawLoginLogin:
case DrawStep::DrawLoginErrorOrg:
case DrawStep::DrawLoginErrorCred:
prepareLogin(_drawStep);
m_pRenderTarget->BeginDraw();
drawBackground(m_pRenderTarget);
drawSmallLogo(m_pRenderTarget);
m_pRenderTarget->EndDraw();
RedrawWindow();
break;
case DrawStep::DrawChoose:
prepareChoose();
m_pRenderTarget->BeginDraw();
drawBackground(m_pRenderTarget);
drawSmallLogo(m_pRenderTarget);
m_pRenderTarget->EndDraw();
RedrawWindow();
break;
case DrawStep::DrawProcessFinishHq:
case DrawStep::DrawProcessFinishUpdate:
case DrawStep::DrawProcessUpdate:
case DrawStep::DrawProcessUninstall:
case DrawStep::DrawProcessSetup:
if (!isUpdate) {
m_voxelRenderTarget->BeginDraw();
m_voxelRenderTarget->Clear(D2D1::ColorF(0.0f, 0.0f, 0.0f, 1.0f));
m_voxelRenderTarget->EndDraw();
m_pRenderTarget->BeginDraw();
prepareProcess(_drawStep);
drawBackground(m_pRenderTarget);
drawSmallLogo(m_pRenderTarget);
m_pRenderTarget->EndDraw();
RedrawWindow();
}
m_voxelRenderTarget->BeginDraw();
drawVoxel(m_voxelRenderTarget);
m_voxelRenderTarget->EndDraw();
break;
default:
break;
}
}

View file

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

View file

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

View file

@ -23,91 +23,93 @@ const bool INSTALL_ZIP = true;
class LauncherManager
{
public:
enum PathType {
Running_Path = 0,
Launcher_Directory,
Download_Directory,
Interface_Directory,
Desktop_Directory,
Content_Directory,
StartMenu_Directory,
Temp_Directory
};
enum ZipType {
ZipContent = 0,
ZipApplication
};
enum DownloadType {
DownloadContent = 0,
DownloadApplication
};
enum ErrorType {
ErrorNetworkAuth,
ErrorNetworkUpdate,
ErrorNetworkHq,
ErrorDownloading,
ErrorUpdating,
ErrorInstall,
ErrorIOFiles
};
LauncherManager();
~LauncherManager();
void init();
enum PathType {
Running_Path = 0,
Launcher_Directory,
Download_Directory,
Interface_Directory,
Desktop_Directory,
Content_Directory,
StartMenu_Directory,
Temp_Directory
};
enum ZipType {
ZipContent = 0,
ZipApplication
};
enum DownloadType {
DownloadContent = 0,
DownloadApplication
};
enum ErrorType {
ErrorNetworkAuth,
ErrorNetworkUpdate,
ErrorNetworkHq,
ErrorDownloading,
ErrorUpdating,
ErrorInstall,
ErrorIOFiles
};
LauncherManager();
~LauncherManager();
void init();
BOOL initLog();
BOOL addToLog(const CString& line);
void closeLog();
BOOL getAndCreatePaths(PathType type, CString& outPath);
BOOL getInstalledVersion(const CString& path, CString& version);
BOOL isApplicationInstalled(CString& version, CString& domain,
CString& content, bool& loggedIn);
LauncherUtils::ResponseError getAccessTokenForCredentials(const CString& username, const CString& password);
LauncherUtils::ResponseError getMostRecentBuild(CString& urlOut, CString& versionOut);
LauncherUtils::ResponseError readOrganizationJSON(const CString& hash);
LauncherUtils::ResponseError readConfigJSON(CString& version, CString& domain,
CString& content, bool& loggedIn);
BOOL createConfigJSON();
BOOL createApplicationRegistryKeys(int size);
BOOL deleteApplicationRegistryKeys();
BOOL createShortcuts();
BOOL deleteShortcuts();
BOOL launchApplication(const CString& tokensJSON = _T(""));
BOOL uninstallApplication();
BOOL installLauncher();
BOOL getAndCreatePaths(PathType type, CString& outPath);
BOOL getInstalledVersion(const CString& path, CString& version);
BOOL isApplicationInstalled(CString& version, CString& domain,
CString& content, bool& loggedIn);
LauncherUtils::ResponseError getAccessTokenForCredentials(const CString& username, const CString& password);
LauncherUtils::ResponseError getMostRecentBuild(CString& urlOut, CString& versionOut);
LauncherUtils::ResponseError readOrganizationJSON(const CString& hash);
LauncherUtils::ResponseError readConfigJSON(CString& version, CString& domain,
CString& content, bool& loggedIn);
BOOL createConfigJSON();
BOOL createApplicationRegistryKeys(int size);
BOOL deleteApplicationRegistryKeys();
BOOL createShortcuts();
BOOL deleteShortcuts();
HWND launchApplication();
BOOL uninstallApplication();
BOOL installLauncher();
// getters
const CString& getContentURL() const { return _contentURL; }
const CString& getdomainURL() const { return _domainURL; }
const CString& getVersion() const { return _version; }
BOOL shouldShutDown() const { return _shouldShutdown; }
BOOL needsUpdate() { return _shouldUpdate; }
BOOL needsUninstall() { return _shouldUninstall; }
void setDisplayName(const CString& displayName) { _displayName = displayName; }
bool isLoggedIn() { return _loggedIn; }
const CString& getLatestInterfaceURL() const { return _latestApplicationURL; }
void uninstall() { _shouldUninstall = true; };
// getters
const CString& getContentURL() const { return _contentURL; }
const CString& getdomainURL() const { return _domainURL; }
const CString& getVersion() const { return _version; }
BOOL shouldShutDown() const { return _shouldShutdown; }
BOOL shouldLaunch() const { return _shouldLaunch; }
BOOL needsUpdate() { return _shouldUpdate; }
BOOL needsUninstall() { return _shouldUninstall; }
void setDisplayName(const CString& displayName) { _displayName = displayName; }
bool isLoggedIn() { return _loggedIn; }
const CString& getLatestInterfaceURL() const { return _latestApplicationURL; }
void uninstall() { _shouldUninstall = true; };
BOOL downloadFile(DownloadType type, const CString& url, CString& localPath);
BOOL downloadContent();
BOOL downloadApplication();
BOOL installContent();
BOOL extractApplication();
void onZipExtracted(ZipType type, int size);
void onFileDownloaded(DownloadType type);
BOOL downloadFile(DownloadType type, const CString& url, CString& localPath);
BOOL downloadContent();
BOOL downloadApplication();
BOOL installContent();
BOOL extractApplication();
void onZipExtracted(ZipType type, int size);
void onFileDownloaded(DownloadType type);
private:
CString _latestApplicationURL;
CString _latestVersion;
CString _contentURL;
CString _domainURL;
CString _version;
CString _displayName;
CString _tokensJSON;
CString _applicationZipPath;
CString _contentZipPath;
bool _loggedIn{ false };
BOOL _shouldUpdate{ FALSE };
BOOL _shouldUninstall{ FALSE };
BOOL _shouldShutdown{ FALSE };
CString _latestApplicationURL;
CString _latestVersion;
CString _contentURL;
CString _domainURL;
CString _version;
CString _displayName;
CString _tokensJSON;
CString _applicationZipPath;
CString _contentZipPath;
bool _loggedIn{ false };
BOOL _shouldUpdate{ FALSE };
BOOL _shouldUninstall{ FALSE };
BOOL _shouldShutdown{ FALSE };
BOOL _shouldLaunch{ FALSE };
CStdioFile _logFile;
};

View file

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

View file

@ -18,17 +18,17 @@
class LauncherUtils
{
public:
enum ResponseError {
Open = 0,
Connect,
OpenRequest,
SendRequest,
ReceiveRequest,
ReadResponse,
ParsingJSON,
BadCredentials,
NoError
};
enum ResponseError {
Open = 0,
Connect,
OpenRequest,
SendRequest,
ReceiveRequest,
ReadResponse,
ParsingJSON,
BadCredentials,
NoError
};
enum DeleteDirError {
NoErrorDeleting = 0,
@ -37,27 +37,27 @@ public:
ErrorDeletingBothDirs
};
struct DownloadThreadData {
int _type;
CString _url;
CString _file;
std::function<void(int)> callback;
// function(type)
void setCallback(std::function<void(int)> fn) {
callback = std::bind(fn, std::placeholders::_1);
}
};
struct DownloadThreadData {
int _type;
CString _url;
CString _file;
std::function<void(int)> callback;
// function(type)
void setCallback(std::function<void(int)> fn) {
callback = std::bind(fn, std::placeholders::_1);
}
};
struct UnzipThreadData {
int _type;
std::string _zipFile;
std::string _path;
// function(type, size)
std::function<void(int, int)> callback;
void setCallback(std::function<void(int, int)> fn) {
callback = std::bind(fn, std::placeholders::_1, std::placeholders::_2);
}
};
struct UnzipThreadData {
int _type;
std::string _zipFile;
std::string _path;
// function(type, size)
std::function<void(int, int)> callback;
void setCallback(std::function<void(int, int)> fn) {
callback = std::bind(fn, std::placeholders::_1, std::placeholders::_2);
}
};
struct DeleteThreadData {
CString _applicationDir;
@ -66,31 +66,32 @@ public:
void setCallback(std::function<void(int)> fn) { callback = std::bind(fn, std::placeholders::_1); }
};
static BOOL parseJSON(const CString& jsonTxt, Json::Value& jsonObject);
static ResponseError makeHTTPCall(const CString& callerName, const CString& mainUrl,
const CString& dirUrl, const CString& contentType,
CStringA& postData, CString& response, bool isPost);
static std::string cStringToStd(CString cstring);
static BOOL getFont(const CString& fontName, int fontSize, bool isBold, CFont& fontOut);
static BOOL launchApplication(LPCWSTR lpApplicationName, LPTSTR cmdArgs = _T(""));
static BOOL IsProcessRunning(const wchar_t *processName);
static BOOL insertRegistryKey(const std::string& regPath, const std::string& name, const std::string& value);
static BOOL insertRegistryKey(const std::string& regPath, const std::string& name, DWORD value);
static BOOL deleteFileOrDirectory(const CString& dirPath, bool noRecycleBin = true);
static HRESULT CreateLink(LPCWSTR lpszPathObj, LPCSTR lpszPathLink, LPCWSTR lpszDesc, LPCWSTR lpszArgs = _T(""));
static BOOL hMac256(const CString& message, const char* key, CString& hashOut);
static uint64_t extractZip(const std::string& zipFile, const std::string& path, std::vector<std::string>& files);
static BOOL deleteRegistryKey(const CString& registryPath);
static BOOL unzipFileOnThread(int type, const std::string& zipFile, const std::string& path, std::function<void(int, int)> callback);
static BOOL downloadFileOnThread(int type, const CString& url, const CString& file, std::function<void(int)> callback);
static BOOL parseJSON(const CString& jsonTxt, Json::Value& jsonObject);
static ResponseError makeHTTPCall(const CString& callerName, const CString& mainUrl,
const CString& dirUrl, const CString& contentType,
CStringA& postData, CString& response, bool isPost);
static std::string cStringToStd(CString cstring);
static BOOL getFont(const CString& fontName, int fontSize, bool isBold, CFont& fontOut);
static BOOL launchApplication(LPCWSTR lpApplicationName, LPTSTR cmdArgs = _T(""));
static BOOL IsProcessRunning(const wchar_t *processName);
static BOOL insertRegistryKey(const std::string& regPath, const std::string& name, const std::string& value);
static BOOL insertRegistryKey(const std::string& regPath, const std::string& name, DWORD value);
static BOOL deleteFileOrDirectory(const CString& dirPath, bool noRecycleBin = true);
static HRESULT CreateLink(LPCWSTR lpszPathObj, LPCSTR lpszPathLink, LPCWSTR lpszDesc, LPCWSTR lpszArgs = _T(""));
static BOOL hMac256(const CString& message, const char* key, CString& hashOut);
static uint64_t extractZip(const std::string& zipFile, const std::string& path, std::vector<std::string>& files);
static BOOL deleteRegistryKey(const CString& registryPath);
static BOOL unzipFileOnThread(int type, const std::string& zipFile, const std::string& path, std::function<void(int, int)> callback);
static BOOL downloadFileOnThread(int type, const CString& url, const CString& file, std::function<void(int)> callback);
static BOOL deleteDirectoriesOnThread(const CString& applicationDir,
const CString& downloadsDir,
std::function<void(int)> callback);
static CString urlEncodeString(const CString& url);
static HWND executeOnForeground(const CString& path, const CString& params);
private:
// Threads
static DWORD WINAPI unzipThread(LPVOID lpParameter);
static DWORD WINAPI downloadThread(LPVOID lpParameter);
// Threads
static DWORD WINAPI unzipThread(LPVOID lpParameter);
static DWORD WINAPI downloadThread(LPVOID lpParameter);
static DWORD WINAPI deleteDirectoriesThread(LPVOID lpParameter);
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View file

@ -396,9 +396,10 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
}
const size_t byteArraySize = AvatarDataPacket::MAX_CONSTANT_HEADER_SIZE + NUM_BYTES_RFC4122_UUID +
AvatarDataPacket::maxFaceTrackerInfoSize(_headData->getBlendshapeCoefficients().size()) +
AvatarDataPacket::maxJointDataSize(_jointData.size()) +
AvatarDataPacket::maxJointDefaultPoseFlagsSize(_jointData.size());
AvatarDataPacket::maxFaceTrackerInfoSize(_headData->getBlendshapeCoefficients().size()) +
AvatarDataPacket::maxJointDataSize(_jointData.size()) +
AvatarDataPacket::maxJointDefaultPoseFlagsSize(_jointData.size()) +
AvatarDataPacket::FAR_GRAB_JOINTS_SIZE;
if (maxDataSize == 0) {
maxDataSize = (int)byteArraySize;

View file

@ -302,6 +302,15 @@ namespace AvatarDataPacket {
const size_t AVATAR_LOCAL_POSITION_SIZE = 12;
static_assert(sizeof(AvatarLocalPosition) == AVATAR_LOCAL_POSITION_SIZE, "AvatarDataPacket::AvatarLocalPosition size doesn't match.");
PACKED_BEGIN struct HandControllers {
SixByteQuat leftHandRotation;
SixByteTrans leftHandTranslation;
SixByteQuat rightHandRotation;
SixByteTrans rightHandTranslation;
} PACKED_END;
static const size_t HAND_CONTROLLERS_SIZE = 24;
static_assert(sizeof(HandControllers) == HAND_CONTROLLERS_SIZE, "AvatarDataPacket::HandControllers size doesn't match.");
const size_t MAX_CONSTANT_HEADER_SIZE = HEADER_SIZE +
AVATAR_GLOBAL_POSITION_SIZE +
AVATAR_BOUNDING_BOX_SIZE +
@ -312,17 +321,8 @@ namespace AvatarDataPacket {
SENSOR_TO_WORLD_SIZE +
ADDITIONAL_FLAGS_SIZE +
PARENT_INFO_SIZE +
AVATAR_LOCAL_POSITION_SIZE;
PACKED_BEGIN struct HandControllers {
SixByteQuat leftHandRotation;
SixByteTrans leftHandTranslation;
SixByteQuat rightHandRotation;
SixByteTrans rightHandTranslation;
} PACKED_END;
static const size_t HAND_CONTROLLERS_SIZE = 24;
static_assert(sizeof(HandControllers) == HAND_CONTROLLERS_SIZE, "AvatarDataPacket::HandControllers size doesn't match.");
AVATAR_LOCAL_POSITION_SIZE +
HAND_CONTROLLERS_SIZE;
// variable length structure follows
@ -1211,6 +1211,12 @@ public:
const QString& getDisplayName() const { return _displayName; }
const QString& getSessionDisplayName() const { return _sessionDisplayName; }
bool getLookAtSnappingEnabled() const { return _lookAtSnappingEnabled; }
/**jsdoc
* Sets the avatar's skeleton model.
* @function Avatar.setSkeletonModelURL
* @param {string} url - The avatar's FST file.
*/
Q_INVOKABLE virtual void setSkeletonModelURL(const QUrl& skeletonModelURL);
virtual void setDisplayName(const QString& displayName);

View file

@ -55,7 +55,7 @@ protected:
bool _faceCamera { false };
bool _glow { false };
size_t _numVertices;
size_t _numVertices { 0 };
gpu::BufferPointer _polylineDataBuffer;
gpu::BufferPointer _polylineGeometryBuffer;
static std::map<std::pair<render::Args::RenderMethod, bool>, gpu::PipelinePointer> _pipelines;

View file

@ -343,4 +343,4 @@ PulsePropertyGroup WebEntityItem::getPulseProperties() const {
return resultWithReadLock<PulsePropertyGroup>([&] {
return _pulseProperties;
});
}
}

View file

@ -309,7 +309,7 @@ void GLBackend::setResourceTexture(unsigned int slot, const TexturePointer& reso
glActiveTexture(GL_TEXTURE0 + slot);
glBindTexture(textureState._target, to);
(void)CHECK_GL_ERROR();
_stats._RSAmountTextureMemoryBounded += (int)object->size();
_stats._RSAmountTextureMemoryBounded += (uint64_t)object->size();
} else {
releaseResourceTexture(slot);

View file

@ -8,6 +8,7 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <limits>
#include "Context.h"
#include <shared/GlobalAppProperties.h>
@ -18,19 +19,28 @@
using namespace gpu;
template<typename T>
T subWrap(T endValue, T beginValue) {
if (endValue >= beginValue) {
return endValue - beginValue;
} else {
return endValue + ((std::numeric_limits<T>::max() - beginValue) + 1);
}
}
void ContextStats::evalDelta(const ContextStats& begin, const ContextStats& end) {
_ISNumFormatChanges = end._ISNumFormatChanges - begin._ISNumFormatChanges;
_ISNumInputBufferChanges = end._ISNumInputBufferChanges - begin._ISNumInputBufferChanges;
_ISNumIndexBufferChanges = end._ISNumIndexBufferChanges - begin._ISNumIndexBufferChanges;
_ISNumFormatChanges = subWrap<uint32_t>(end._ISNumFormatChanges, begin._ISNumFormatChanges);
_ISNumInputBufferChanges = subWrap<uint32_t>(end._ISNumInputBufferChanges, begin._ISNumInputBufferChanges);
_ISNumIndexBufferChanges = subWrap<uint32_t>(end._ISNumIndexBufferChanges, begin._ISNumIndexBufferChanges);
_RSNumTextureBounded = end._RSNumTextureBounded - begin._RSNumTextureBounded;
_RSAmountTextureMemoryBounded = end._RSAmountTextureMemoryBounded - begin._RSAmountTextureMemoryBounded;
_RSNumTextureBounded = subWrap<uint32_t>(end._RSNumTextureBounded, begin._RSNumTextureBounded);
_RSAmountTextureMemoryBounded = subWrap<uint64_t>(end._RSAmountTextureMemoryBounded, begin._RSAmountTextureMemoryBounded);
_DSNumAPIDrawcalls = end._DSNumAPIDrawcalls - begin._DSNumAPIDrawcalls;
_DSNumDrawcalls = end._DSNumDrawcalls - begin._DSNumDrawcalls;
_DSNumTriangles= end._DSNumTriangles - begin._DSNumTriangles;
_DSNumAPIDrawcalls = subWrap<uint32_t>(end._DSNumAPIDrawcalls, begin._DSNumAPIDrawcalls);
_DSNumDrawcalls = subWrap<uint32_t>(end._DSNumDrawcalls, begin._DSNumDrawcalls);
_DSNumTriangles= subWrap<uint32_t>(end._DSNumTriangles, begin._DSNumTriangles);
_PSNumSetPipelines = end._PSNumSetPipelines - begin._PSNumSetPipelines;
_PSNumSetPipelines = subWrap<uint32_t>(end._PSNumSetPipelines, begin._PSNumSetPipelines);
}

View file

@ -32,19 +32,19 @@ namespace gpu {
struct ContextStats {
public:
int _ISNumFormatChanges = 0;
int _ISNumInputBufferChanges = 0;
int _ISNumIndexBufferChanges = 0;
uint32_t _ISNumFormatChanges { 0 };
uint32_t _ISNumInputBufferChanges { 0 };
uint32_t _ISNumIndexBufferChanges { 0 };
int _RSNumResourceBufferBounded = 0;
int _RSNumTextureBounded = 0;
int _RSAmountTextureMemoryBounded = 0;
uint32_t _RSNumResourceBufferBounded { 0 };
uint32_t _RSNumTextureBounded { 0 };
uint64_t _RSAmountTextureMemoryBounded { 0 };
int _DSNumAPIDrawcalls = 0;
int _DSNumDrawcalls = 0;
int _DSNumTriangles = 0;
uint32_t _DSNumAPIDrawcalls { 0 };
uint32_t _DSNumDrawcalls { 0 };
uint32_t _DSNumTriangles { 0 };
int _PSNumSetPipelines = 0;
uint32_t _PSNumSetPipelines { 0 };
ContextStats() {}
ContextStats(const ContextStats& stats) = default;

View file

@ -632,6 +632,7 @@ void LimitedNodeList::processKillNode(ReceivedMessage& message) {
}
void LimitedNodeList::handleNodeKill(const SharedNodePointer& node, ConnectionID nextConnectionID) {
_nodeDisconnectTimestamp = usecTimestampNow();
qCDebug(networking) << "Killed" << *node;
node->stopPingTimer();
emit nodeKilled(node);

View file

@ -108,6 +108,13 @@ public:
};
Q_ENUM(ConnectionStep);
enum ConnectReason : quint32 {
Connect = 0,
SilentDomainDisconnect
};
Q_ENUM(ConnectReason);
QUuid getSessionUUID() const;
void setSessionUUID(const QUuid& sessionUUID);
Node::LocalID getSessionLocalID() const;
@ -461,6 +468,9 @@ protected:
}
std::unordered_map<QUuid, ConnectionID> _connectionIDs;
quint64 _nodeConnectTimestamp{ 0 };
quint64 _nodeDisconnectTimestamp{ 0 };
ConnectReason _connectReason { Connect };
private slots:
void flagTimeForConnectionStep(ConnectionStep connectionStep, quint64 timestamp);

View file

@ -113,6 +113,8 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort)
connect(&_domainHandler, SIGNAL(connectedToDomain(QUrl)), &_keepAlivePingTimer, SLOT(start()));
connect(&_domainHandler, &DomainHandler::disconnectedFromDomain, &_keepAlivePingTimer, &QTimer::stop);
connect(&_domainHandler, &DomainHandler::limitOfSilentDomainCheckInsReached, this, [this]() { _connectReason = LimitedNodeList::SilentDomainDisconnect; });
// set our sockAddrBelongsToDomainOrNode method as the connection creation filter for the udt::Socket
using std::placeholders::_1;
_nodeSocket.setConnectionCreationFilterOperator(std::bind(&NodeList::sockAddrBelongsToDomainOrNode, this, _1));
@ -304,7 +306,8 @@ void NodeList::sendDomainServerCheckIn() {
// may be called by multiple threads.
if (!_sendDomainServerCheckInEnabled) {
qCDebug(networking_ice) << "Refusing to send a domain-server check in while it is disabled.";
static const QString DISABLED_CHECKIN_DEBUG{ "Refusing to send a domain-server check in while it is disabled." };
HIFI_FCDEBUG(networking_ice(), DISABLED_CHECKIN_DEBUG);
return;
}
@ -414,6 +417,16 @@ void NodeList::sendDomainServerCheckIn() {
// now add the machine fingerprint
auto accountManager = DependencyManager::get<AccountManager>();
packetStream << FingerprintUtils::getMachineFingerprint();
packetStream << _connectReason;
if (_nodeDisconnectTimestamp < _nodeConnectTimestamp) {
_nodeDisconnectTimestamp = usecTimestampNow();
}
quint64 previousConnectionUptime = _nodeConnectTimestamp ? _nodeDisconnectTimestamp - _nodeConnectTimestamp : 0;
packetStream << previousConnectionUptime;
}
packetStream << quint64(duration_cast<microseconds>(system_clock::now().time_since_epoch()).count());
@ -439,6 +452,16 @@ void NodeList::sendDomainServerCheckIn() {
// Send duplicate check-ins in the exponentially increasing sequence 1, 1, 2, 4, ...
static const int MAX_CHECKINS_TOGETHER = 20;
int outstandingCheckins = _domainHandler.getCheckInPacketsSinceLastReply();
/*
static const int WARNING_CHECKIN_COUNT = 2;
if (outstandingCheckins > WARNING_CHECKIN_COUNT) {
// We may be headed for a disconnect, as we've written two DomainListRequests without getting anything back.
// In some cases, we've found that nothing is going out on the wire despite not getting any errors from
// sendPacket => writeDatagram, below. In at least some such cases, we've found that the DomainDisconnectRequest
// does go through, so let's at least try to mix it up with a different safe packet.
// TODO: send ICEPing, and later on tell the other nodes to shut up for a moment.
}*/
int checkinCount = outstandingCheckins > 1 ? std::pow(2, outstandingCheckins - 2) : 1;
checkinCount = std::min(checkinCount, MAX_CHECKINS_TOGETHER);
for (int i = 1; i < checkinCount; ++i) {
@ -660,6 +683,14 @@ void NodeList::processDomainServerList(QSharedPointer<ReceivedMessage> message)
quint64 domainServerCheckinProcessingTime;
packetStream >> domainServerCheckinProcessingTime;
bool newConnection;
packetStream >> newConnection;
if (newConnection) {
_nodeConnectTimestamp = usecTimestampNow();
_connectReason = Connect;
}
qint64 pingLagTime = (now - qint64(connectRequestTimestamp)) / qint64(USECS_PER_MSEC);
qint64 domainServerRequestLag = (qint64(domainServerPingSendTime - domainServerCheckinProcessingTime) - qint64(connectRequestTimestamp)) / qint64(USECS_PER_MSEC);;
@ -673,13 +704,6 @@ void NodeList::processDomainServerList(QSharedPointer<ReceivedMessage> message)
// refuse to process this packet if we aren't currently connected to the DS
return;
}
#ifdef DEBUG_EVENT_QUEUE
{
int nodeListQueueSize = ::hifi::qt::getEventQueueSize(thread());
qCDebug(networking) << "DomainList received, pending count =" << _domainHandler.getCheckInPacketsSinceLastReply()
<< "NodeList thread event queue size =" << nodeListQueueSize;
}
#endif
// warn if ping lag is getting long
if (pingLagTime > qint64(MSECS_PER_SECOND)) {

View file

@ -346,7 +346,7 @@ void ResourceCache::setRequestLimit(uint32_t limit) {
QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl& fallback, void* extra, size_t extraHash) {
QSharedPointer<Resource> resource;
{
QReadLocker locker(&_resourcesLock);
QWriteLocker locker(&_resourcesLock);
auto& resourcesWithExtraHash = _resources[url];
auto resourcesWithExtraHashIter = resourcesWithExtraHash.find(extraHash);
if (resourcesWithExtraHashIter != resourcesWithExtraHash.end()) {

View file

@ -27,7 +27,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::StunResponse:
return 17;
case PacketType::DomainList:
return static_cast<PacketVersion>(DomainListVersion::HasTimestamp);
return static_cast<PacketVersion>(DomainListVersion::HasConnectReason);
case PacketType::EntityAdd:
case PacketType::EntityClone:
case PacketType::EntityEdit:
@ -72,7 +72,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
return static_cast<PacketVersion>(DomainConnectionDeniedVersion::IncludesExtraInfo);
case PacketType::DomainConnectRequest:
return static_cast<PacketVersion>(DomainConnectRequestVersion::HasTimestamp);
return static_cast<PacketVersion>(DomainConnectRequestVersion::HasReason);
case PacketType::DomainServerAddedNode:
return static_cast<PacketVersion>(DomainServerAddedNodeVersion::PermissionsGrid);

View file

@ -345,7 +345,8 @@ enum class DomainConnectRequestVersion : PacketVersion {
HasMACAddress,
HasMachineFingerprint,
AlwaysHasMachineFingerprint,
HasTimestamp
HasTimestamp,
HasReason
};
enum class DomainConnectionDeniedVersion : PacketVersion {
@ -365,7 +366,8 @@ enum class DomainListVersion : PacketVersion {
GetUsernameFromUUIDSupport,
GetMachineFingerprintFromUUIDSupport,
AuthenticationOptional,
HasTimestamp
HasTimestamp,
HasConnectReason
};
enum class AudioVersion : PacketVersion {

View file

@ -61,10 +61,13 @@ void Socket::bind(const QHostAddress& address, quint16 port) {
auto sd = _udpSocket.socketDescriptor();
int val = IP_PMTUDISC_DONT;
setsockopt(sd, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val));
#elif defined(Q_OS_WINDOWS)
#elif defined(Q_OS_WIN)
auto sd = _udpSocket.socketDescriptor();
int val = 0; // false
setsockopt(sd, IPPROTO_IP, IP_DONTFRAGMENT, &val, sizeof(val));
if (setsockopt(sd, IPPROTO_IP, IP_DONTFRAGMENT, (const char *)&val, sizeof(val))) {
auto wsaErr = WSAGetLastError();
qCWarning(networking) << "Socket::bind Cannot setsockopt IP_DONTFRAGMENT" << wsaErr;
}
#endif
}
}
@ -225,19 +228,25 @@ qint64 Socket::writeDatagram(const char* data, qint64 size, const HifiSockAddr&
qint64 Socket::writeDatagram(const QByteArray& datagram, const HifiSockAddr& sockAddr) {
// don't attempt to write the datagram if we're unbound. Just drop it.
// _udpSocket.writeDatagram will return an error anyway, but there are
// potential crashes in Qt when that happens.
if (_udpSocket.state() != QAbstractSocket::BoundState) {
qCDebug(networking) << "Attempt to writeDatagram when in unbound state to" << sockAddr;
return -1;
}
qint64 bytesWritten = _udpSocket.writeDatagram(datagram, sockAddr.getAddress(), sockAddr.getPort());
if (bytesWritten < 0) {
qCDebug(networking) << "udt::writeDatagram (" << _udpSocket.state() << ") error - " << _udpSocket.error() << "(" << _udpSocket.errorString() << ")";
int pending = _udpSocket.bytesToWrite();
if (bytesWritten < 0 || pending) {
int wsaError = 0;
#ifdef WIN32
int wsaError = WSAGetLastError();
qCDebug(networking) << "windows socket error " << wsaError;
wsaError = WSAGetLastError();
#endif
qCDebug(networking) << "udt::writeDatagram (" << _udpSocket.state() << sockAddr << ") error - " << wsaError << _udpSocket.error() << "(" << _udpSocket.errorString() << ")"
<< (pending ? "pending bytes:" : "pending:") << pending;
#ifdef DEBUG_EVENT_QUEUE
int nodeListQueueSize = ::hifi::qt::getEventQueueSize(thread());
qCDebug(networking) << "Networking queue size - " << nodeListQueueSize;
qCDebug(networking) << "Networking queue size - " << nodeListQueueSize << "writing datagram to" << sockAddr;
#endif // DEBUG_EVENT_QUEUE
}
@ -245,6 +254,7 @@ qint64 Socket::writeDatagram(const QByteArray& datagram, const HifiSockAddr& soc
}
Connection* Socket::findOrCreateConnection(const HifiSockAddr& sockAddr, bool filterCreate) {
Lock connectionsLock(_connectionsHashMutex);
auto it = _connectionsHash.find(sockAddr);
if (it == _connectionsHash.end()) {
@ -284,6 +294,7 @@ void Socket::clearConnections() {
return;
}
Lock connectionsLock(_connectionsHashMutex);
if (_connectionsHash.size() > 0) {
// clear all of the current connections in the socket
qCDebug(networking) << "Clearing all remaining connections in Socket.";
@ -292,6 +303,7 @@ void Socket::clearConnections() {
}
void Socket::cleanupConnection(HifiSockAddr sockAddr) {
Lock connectionsLock(_connectionsHashMutex);
auto numErased = _connectionsHash.erase(sockAddr);
if (numErased > 0) {
@ -449,6 +461,7 @@ void Socket::readPendingDatagrams() {
}
void Socket::connectToSendSignal(const HifiSockAddr& destinationAddr, QObject* receiver, const char* slot) {
Lock connectionsLock(_connectionsHashMutex);
auto it = _connectionsHash.find(destinationAddr);
if (it != _connectionsHash.end()) {
connect(it->second.get(), SIGNAL(packetSent()), receiver, slot);
@ -465,6 +478,7 @@ void Socket::setConnectionMaxBandwidth(int maxBandwidth) {
qInfo() << "Setting socket's maximum bandwith to" << maxBandwidth << "bps. ("
<< _connectionsHash.size() << "live connections)";
_maxBandwidth = maxBandwidth;
Lock connectionsLock(_connectionsHashMutex);
for (auto& pair : _connectionsHash) {
auto& connection = pair.second;
connection->setMaxBandwidth(_maxBandwidth);
@ -482,6 +496,8 @@ ConnectionStats::Stats Socket::sampleStatsForConnection(const HifiSockAddr& dest
Socket::StatsVector Socket::sampleStatsForAllConnections() {
StatsVector result;
Lock connectionsLock(_connectionsHashMutex);
result.reserve(_connectionsHash.size());
for (const auto& connectionPair : _connectionsHash) {
result.emplace_back(connectionPair.first, connectionPair.second->sampleStats());
@ -492,6 +508,8 @@ Socket::StatsVector Socket::sampleStatsForAllConnections() {
std::vector<HifiSockAddr> Socket::getConnectionSockAddrs() {
std::vector<HifiSockAddr> addr;
Lock connectionsLock(_connectionsHashMutex);
addr.reserve(_connectionsHash.size());
for (const auto& connectionPair : _connectionsHash) {
@ -501,7 +519,13 @@ std::vector<HifiSockAddr> Socket::getConnectionSockAddrs() {
}
void Socket::handleSocketError(QAbstractSocket::SocketError socketError) {
qCDebug(networking) << "udt::Socket (" << _udpSocket.state() << ") error - " << socketError << "(" << _udpSocket.errorString() << ")";
int wsaError = 0;
#ifdef WIN32
wsaError = WSAGetLastError();
#endif
int pending = _udpSocket.bytesToWrite();
qCDebug(networking) << "udt::Socket (" << _udpSocket.state() << ") error - " << wsaError << socketError << "(" << _udpSocket.errorString() << ")"
<< (pending ? "pending bytes:" : "pending:") << pending;
#ifdef DEBUG_EVENT_QUEUE
int nodeListQueueSize = ::hifi::qt::getEventQueueSize(thread());
qCDebug(networking) << "Networking queue size - " << nodeListQueueSize;

View file

@ -129,6 +129,7 @@ private:
ConnectionCreationFilterOperator _connectionCreationFilterOperator;
Mutex _unreliableSequenceNumbersMutex;
Mutex _connectionsHashMutex;
std::unordered_map<HifiSockAddr, BasePacketHandler> _unfilteredHandlers;
std::unordered_map<HifiSockAddr, SequenceNumber> _unreliableSequenceNumbers;

View file

@ -35,7 +35,7 @@ static bool flipNormalsMyAvatarVsBackfacingTriangles(btManifoldPoint& cp,
const btVector3* v = triShape->m_vertices1;
btVector3 faceNormal = colObj1Wrap->getWorldTransform().getBasis() * btCross(v[1] - v[0], v[2] - v[0]);
float nDotF = btDot(faceNormal, cp.m_normalWorldOnB);
if (nDotF <= 0.0f) {
if (nDotF <= 0.0f && faceNormal.length2() > EPSILON) {
faceNormal.normalize();
// flip the contact normal to be aligned with the face normal
cp.m_normalWorldOnB += -2.0f * nDotF * faceNormal;

View file

@ -12,6 +12,7 @@
#include "Platform.h"
#include "PlatformKeys.h"
#include <qglobal.h>
using namespace platform;
@ -124,4 +125,35 @@ bool filterOnProcessors(const platform::json& computer, const platform::json& cp
// Not able to profile
return false;
}
}
// Ugly very adhoc capability check to know if a particular hw can REnder with Deferred method or not
// YES for PC windows and linux
// NO for android
// YES on macos EXCEPT for macbookair with gpu intel iris or intel HD 6000
bool Profiler::isRenderMethodDeferredCapable() {
#if defined(Q_OS_MAC)
auto computer = platform::getComputer();
const auto computerModel = (computer.count(keys::computer::model) ? computer[keys::computer::model].get<std::string>() : "");
auto gpuInfo = platform::getGPU(0);
const auto gpuModel = (gpuInfo.count(keys::gpu::model) ? gpuInfo[keys::gpu::model].get<std::string>() : "");
// Macbook air 2018 are a problem
if ((computerModel.find("MacBookAir") != std::string::npos) && (gpuModel.find("Intel HD Graphics 6000") != std::string::npos)) {
return false;
}
// We know for fact that one INtel Iris is problematic, not enough info yet for sure
// if ((gpuModel.find("Intel Iris ....") != std::string::npos)) {
// return false;
//}
return true;
#elif defined(Q_OS_ANDROID)
return false;
#else
return true;
#endif
}

View file

@ -28,6 +28,9 @@ public:
static const std::array<const char*, Tier::NumTiers> TierNames;
static Tier profilePlatform();
// Ugly very adhoc capability check to know if a particular hw can REnder with Deferred method or not
static bool isRenderMethodDeferredCapable();
};
}

View file

@ -18,6 +18,9 @@
#include <unistd.h>
#include <cpuid.h>
#include <sys/sysctl.h>
#include <CoreFoundation/CoreFoundation.h>
#include <ApplicationServices/ApplicationServices.h>
#endif
using namespace platform;
@ -33,18 +36,75 @@ void MACOSInstance::enumerateCpu() {
}
void MACOSInstance::enumerateGpu() {
#ifdef Q_OS_MAC
GPUIdent* ident = GPUIdent::getInstance();
json gpu = {};
gpu[keys::gpu::vendor] = ident->getName().toUtf8().constData();
gpu[keys::gpu::model] = ident->getName().toUtf8().constData();
gpu[keys::gpu::videoMemory] = ident->getMemory();
gpu[keys::gpu::driver] = ident->getDriver().toUtf8().constData();
_gpu.push_back(gpu);
_display = ident->getOutput();
#endif
}
void MACOSInstance::enumerateDisplays() {
#ifdef Q_OS_MAC
auto displayID = CGMainDisplayID();
auto displaySize = CGDisplayScreenSize(displayID);
const auto MM_TO_IN = 0.0393701;
auto displaySizeWidthInches = displaySize.width * MM_TO_IN;
auto displaySizeHeightInches = displaySize.height * MM_TO_IN;
auto displaySizeDiagonalInches = sqrt(displaySizeWidthInches * displaySizeWidthInches + displaySizeHeightInches * displaySizeHeightInches);
auto displayBounds = CGDisplayBounds(displayID);
auto displayMaster =CGDisplayIsMain(displayID);
auto displayUnit =CGDisplayUnitNumber(displayID);
auto displayModel =CGDisplayModelNumber(displayID);
auto displayVendor = CGDisplayVendorNumber(displayID);
auto displaySerial = CGDisplaySerialNumber(displayID);
auto displayMode = CGDisplayCopyDisplayMode(displayID);
auto displayModeWidth = CGDisplayModeGetPixelWidth(displayMode);
auto displayModeHeight = CGDisplayModeGetPixelHeight(displayMode);
auto displayRefreshrate = CGDisplayModeGetRefreshRate(displayMode);
CGDisplayModeRelease(displayMode);
json display = {};
display["physicalWidth"] = displaySizeWidthInches;
display["physicalHeight"] = displaySizeHeightInches;
display["physicalDiagonal"] = displaySizeDiagonalInches;
display["ppi"] = sqrt(displayModeHeight * displayModeHeight + displayModeWidth * displayModeWidth) / displaySizeDiagonalInches;
display["coordLeft"] = displayBounds.origin.x;
display["coordRight"] = displayBounds.origin.x + displayBounds.size.width;
display["coordTop"] = displayBounds.origin.y;
display["coordBottom"] = displayBounds.origin.y + displayBounds.size.height;
display["isMaster"] = displayMaster;
display["unit"] = displayUnit;
display["vendor"] = displayVendor;
display["model"] = displayModel;
display["serial"] = displaySerial;
display["refreshrate"] =displayRefreshrate;
display["modeWidth"] = displayModeWidth;
display["modeHeight"] = displayModeHeight;
_display.push_back(display);
#endif
}
void MACOSInstance::enumerateMemory() {
json ram = {};

View file

@ -18,6 +18,7 @@ namespace platform {
void enumerateCpu() override;
void enumerateMemory() override;
void enumerateGpu() override;
void enumerateDisplays() override;
void enumerateComputer () override;
};

View file

@ -18,6 +18,7 @@ bool Instance::enumeratePlatform() {
enumerateComputer();
enumerateCpu();
enumerateGpu();
enumerateDisplays();
enumerateMemory();
// And profile the platform and put the tier in "computer"

View file

@ -37,6 +37,7 @@ public:
void virtual enumerateCpu()=0;
void virtual enumerateMemory()=0;
void virtual enumerateGpu()=0;
void virtual enumerateDisplays() {}
void virtual enumerateComputer()=0;
virtual ~Instance();

View file

@ -939,6 +939,11 @@ void GeometryCache::renderWireSphere(gpu::Batch& batch, const glm::vec4& color)
void GeometryCache::renderGrid(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner,
int majorRows, int majorCols, float majorEdge, int minorRows, int minorCols, float minorEdge,
const glm::vec4& color, bool forward, int id) {
if (majorRows == 0 || majorCols == 0) {
return;
}
Vec2FloatPair majorKey(glm::vec2(majorRows, majorCols), majorEdge);
Vec2FloatPair minorKey(glm::vec2(minorRows, minorCols), minorEdge);
Vec2FloatPairPair key(majorKey, minorKey);
@ -962,8 +967,8 @@ void GeometryCache::renderGrid(gpu::Batch& batch, const glm::vec2& minCorner, co
gridBuffer.edit<GridSchema>().period = glm::vec4(majorRows, majorCols, minorRows, minorCols);
gridBuffer.edit<GridSchema>().offset.x = -(majorEdge / majorRows) / 2;
gridBuffer.edit<GridSchema>().offset.y = -(majorEdge / majorCols) / 2;
gridBuffer.edit<GridSchema>().offset.z = -(minorEdge / minorRows) / 2;
gridBuffer.edit<GridSchema>().offset.w = -(minorEdge / minorCols) / 2;
gridBuffer.edit<GridSchema>().offset.z = minorRows == 0 ? 0 : -(minorEdge / minorRows) / 2;
gridBuffer.edit<GridSchema>().offset.w = minorCols == 0 ? 0 : -(minorEdge / minorCols) / 2;
gridBuffer.edit<GridSchema>().edge = glm::vec4(glm::vec2(majorEdge),
// If rows or columns are not set, do not draw minor gridlines
glm::vec2((minorRows != 0 && minorCols != 0) ? minorEdge : 0.0f));

View file

@ -56,7 +56,7 @@ namespace render {
Q_PROPERTY(quint32 frameTextureCount MEMBER frameTextureCount NOTIFY dirty)
Q_PROPERTY(quint32 frameTextureRate MEMBER frameTextureRate NOTIFY dirty)
Q_PROPERTY(quint32 frameTextureMemoryUsage MEMBER frameTextureMemoryUsage NOTIFY dirty)
Q_PROPERTY(quint64 frameTextureMemoryUsage MEMBER frameTextureMemoryUsage NOTIFY dirty)
Q_PROPERTY(quint32 frameSetPipelineCount MEMBER frameSetPipelineCount NOTIFY dirty)
Q_PROPERTY(quint32 frameSetInputFormatCount MEMBER frameSetInputFormatCount NOTIFY dirty)
@ -96,7 +96,7 @@ namespace render {
quint32 frameTextureCount{ 0 };
quint32 frameTextureRate{ 0 };
qint64 frameTextureMemoryUsage{ 0 };
quint64 frameTextureMemoryUsage{ 0 };
quint32 frameSetPipelineCount{ 0 };
@ -124,4 +124,4 @@ namespace render {
};
}
#endif
#endif

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