Merge branch 'master' of https://github.com/highfidelity/hifi into unit-scaled-address-bar

This commit is contained in:
howard-stearns 2016-10-18 09:36:09 -07:00
commit 3ce3e57c38
65 changed files with 1143 additions and 1052 deletions

View file

@ -519,7 +519,7 @@ void DomainServer::setupNodeListAndAssignments() {
// add whatever static assignments that have been parsed to the queue
addStaticAssignmentsToQueue();
// set a custum packetVersionMatch as the verify packet operator for the udt::Socket
// set a custom packetVersionMatch as the verify packet operator for the udt::Socket
nodeList->setPacketFilterOperator(&DomainServer::packetVersionMatch);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 246 KiB

View file

@ -11,6 +11,7 @@
var POLL_FREQUENCY = 500; // ms
var MAX_WARNINGS = 3;
var numWarnings = 0;
var isWindowFocused = true;
var isKeyboardRaised = false;
var isNumericKeyboard = false;
var KEYBOARD_HEIGHT = 200;
@ -38,15 +39,15 @@
var keyboardRaised = shouldRaiseKeyboard();
var numericKeyboard = shouldSetNumeric();
if (keyboardRaised !== isKeyboardRaised || numericKeyboard !== isNumericKeyboard) {
if (isWindowFocused && (keyboardRaised !== isKeyboardRaised || numericKeyboard !== isNumericKeyboard)) {
if (typeof EventBridge !== "undefined") {
if (typeof EventBridge !== "undefined" && EventBridge !== null) {
EventBridge.emitWebEvent(
keyboardRaised ? ("_RAISE_KEYBOARD" + (numericKeyboard ? "_NUMERIC" : "")) : "_LOWER_KEYBOARD"
);
} else {
if (numWarnings < MAX_WARNINGS) {
console.log("WARNING: no global EventBridge object found");
console.log("WARNING: No global EventBridge object found");
numWarnings++;
}
}
@ -65,4 +66,14 @@
isNumericKeyboard = numericKeyboard;
}
}, POLL_FREQUENCY);
window.addEventListener("focus", function () {
isWindowFocused = true;
});
window.addEventListener("blur", function () {
isWindowFocused = false;
isKeyboardRaised = false;
isNumericKeyboard = false;
});
})();

View file

@ -33,7 +33,10 @@ Window {
width: addressBarDialog.implicitWidth
height: addressBarDialog.implicitHeight
onShownChanged: addressBarDialog.observeShownChanged(shown);
onShownChanged: {
addressBarDialog.keyboardEnabled = HMD.active;
addressBarDialog.observeShownChanged(shown);
}
Component.onCompleted: {
root.parentChanged.connect(center);
center();
@ -61,7 +64,6 @@ Window {
clearAddressLineTimer.start();
}
property var allStories: [];
property int keyboardHeight: 200;
property int cardWidth: 200;
property int cardHeight: 152;
property string metaverseBase: addressBarDialog.metaverseServerUrl + "/api/v1/";
@ -70,10 +72,12 @@ Window {
AddressBarDialog {
id: addressBarDialog
property bool keyboardEnabled: false
property bool keyboardRaised: false
property bool punctuationMode: false
implicitWidth: backgroundImage.width
implicitHeight: backgroundImage.height + keyboardHeight + cardHeight;
implicitHeight: backgroundImage.height + (keyboardEnabled ? keyboard.raisedHeight : keyboardHeight) + cardHeight;
// The buttons have their button state changed on hover, so we have to manually fix them up here
onBackEnabledChanged: backArrow.buttonState = addressBarDialog.backEnabled ? 1 : 0;
@ -92,7 +96,7 @@ Window {
spacing: hifi.layout.spacing;
clip: true;
anchors {
bottom: backgroundImage.top;
top: parent.top
horizontalCenter: backgroundImage.horizontalCenter
}
model: suggestions;
@ -276,33 +280,15 @@ Window {
}
}
// virtual keyboard, letters
HifiControls.Keyboard {
id: keyboard1
y: parent.height
height: keyboardHeight
visible: !parent.punctuationMode
enabled: !parent.punctuationMode
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
anchors.top: backgroundImage.bottom
anchors.bottomMargin: 0
}
HifiControls.KeyboardPunctuation {
id: keyboard2
y: parent.height
height: keyboardHeight
visible: parent.punctuationMode
enabled: parent.punctuationMode
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
anchors.top: backgroundImage.bottom
anchors.bottomMargin: 0
id: keyboard
raised: parent.keyboardEnabled // Ignore keyboardRaised; keep keyboard raised if enabled (i.e., in HMD).
numeric: parent.punctuationMode
anchors {
top: backgroundImage.bottom
left: parent.left
right: parent.right
}
}
}

View file

@ -31,13 +31,6 @@ ScrollingWindow {
addressBar.text = webview.url
}
onParentChanged: {
if (visible) {
addressBar.forceActiveFocus();
addressBar.selectAll()
}
}
function showPermissionsBar(){
permissionsContainer.visible=true;
}

View file

@ -33,6 +33,8 @@ ModalWindow {
property string title: ""
property int titleWidth: 0
keyboardOverride: true // Disable ModalWindow's keyboard.
LoginDialog {
id: loginDialog

View file

@ -27,6 +27,7 @@ Item {
loginDialog.login(usernameField.text, passwordField.text)
}
property bool keyboardEnabled: false
property bool keyboardRaised: false
property bool punctuationMode: false
@ -46,7 +47,7 @@ Item {
root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth));
root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
+ (linkAccountBody.keyboardRaised ? (200 + 2 * hifi.dimensions.contentSpacing.y) : hifi.dimensions.contentSpacing.y);
+ (keyboardEnabled && keyboardRaised ? (200 + 2 * hifi.dimensions.contentSpacing.y) : hifi.dimensions.contentSpacing.y);
}
}
@ -137,28 +138,13 @@ Item {
// Override ScrollingWindow's keyboard that would be at very bottom of dialog.
Keyboard {
y: parent.keyboardRaised ? parent.height : 0
height: parent.keyboardRaised ? 200 : 0
visible: parent.keyboardRaised && !parent.punctuationMode
enabled: parent.keyboardRaised && !parent.punctuationMode
raised: keyboardEnabled && keyboardRaised
numeric: punctuationMode
anchors {
left: parent.left
right: parent.right
bottom: buttons.top
bottomMargin: parent.keyboardRaised ? 2 * hifi.dimensions.contentSpacing.y : 0
}
}
KeyboardPunctuation {
y: parent.keyboardRaised ? parent.height : 0
height: parent.keyboardRaised ? 200 : 0
visible: parent.keyboardRaised && parent.punctuationMode
enabled: parent.keyboardRaised && parent.punctuationMode
anchors {
left: parent.left
right: parent.right
bottom: buttons.top
bottomMargin: parent.keyboardRaised ? 2 * hifi.dimensions.contentSpacing.y : 0
bottomMargin: keyboardRaised ? 2 * hifi.dimensions.contentSpacing.y : 0
}
}
@ -195,9 +181,10 @@ Item {
Component.onCompleted: {
root.title = qsTr("Sign Into High Fidelity")
root.iconText = "<"
keyboardEnabled = HMD.active;
d.resize();
usernameField.forceActiveFocus()
usernameField.forceActiveFocus();
}
Connections {

View file

@ -42,6 +42,10 @@ ScrollingWindow {
}
}
onShownChanged: {
keyboardEnabled = HMD.active;
}
Settings {
category: "ToolWindow.Position"
property alias x: toolWindow.x

View file

@ -25,8 +25,6 @@ WebEngineView {
});
}
// FIXME hack to get the URL with the auth token included. Remove when we move to Qt 5.6
Timer {
id: urlReplacementTimer

View file

@ -1,10 +1,28 @@
//
// FileDialog.qml
//
// Created by Anthony Thibault on 31 Oct 2016
// Copyright 2016 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.0
Item {
id: keyboardBase
height: 200
property alias shiftKey: key27
property bool raised: false
property bool numeric: false
readonly property int raisedHeight: 200
height: enabled && raised ? raisedHeight : 0
visible: enabled && raised
property bool shiftMode: false
property bool numericShiftMode: false
function resetShiftMode(mode) {
shiftMode = mode;
@ -37,8 +55,8 @@ Item {
function forEachKey(func) {
var i, j;
for (i = 0; i < column1.children.length; i++) {
var row = column1.children[i];
for (i = 0; i < columnAlpha.children.length; i++) {
var row = columnAlpha.children[i];
for (j = 0; j < row.children.length; j++) {
var key = row.children[j];
func(key);
@ -48,10 +66,12 @@ Item {
onShiftModeChanged: {
forEachKey(function (key) {
if (shiftMode) {
key.glyph = keyboardBase.toUpper(key.glyph);
} else {
key.glyph = keyboardBase.toLower(key.glyph);
if (/[a-z]/i.test(key.glyph)) {
if (shiftMode) {
key.glyph = keyboardBase.toUpper(key.glyph);
} else {
key.glyph = keyboardBase.toLower(key.glyph);
}
}
});
}
@ -97,265 +117,177 @@ Item {
anchors.bottomMargin: 0
Column {
id: column1
id: columnAlpha
width: 480
height: 200
visible: !numeric
Row {
id: row1
width: 480
height: 50
anchors.left: parent.left
anchors.leftMargin: 0
anchors.leftMargin: 4
Key {
id: key1
width: 44
glyph: "q"
}
Key {
id: key2
width: 44
glyph: "w"
}
Key {
id: key3
width: 44
glyph: "e"
}
Key {
id: key4
width: 43
glyph: "r"
}
Key {
id: key5
width: 43
glyph: "t"
}
Key {
id: key6
width: 44
glyph: "y"
}
Key {
id: key7
width: 44
glyph: "u"
}
Key {
id: key8
width: 43
glyph: "i"
}
Key {
id: key9
width: 42
glyph: "o"
}
Key {
id: key10
width: 44
glyph: "p"
}
Key {
id: key28
width: 45
glyph: "←"
}
Key { width: 43; glyph: "q"; }
Key { width: 43; glyph: "w"; }
Key { width: 43; glyph: "e"; }
Key { width: 43; glyph: "r"; }
Key { width: 43; glyph: "t"; }
Key { width: 43; glyph: "y"; }
Key { width: 43; glyph: "u"; }
Key { width: 43; glyph: "i"; }
Key { width: 43; glyph: "o"; }
Key { width: 43; glyph: "p"; }
Key { width: 43; glyph: "←"; }
}
Row {
id: row2
width: 480
height: 50
anchors.left: parent.left
anchors.leftMargin: 18
anchors.leftMargin: 20
Key {
id: key11
width: 43
}
Key {
id: key12
width: 43
glyph: "s"
}
Key {
id: key13
width: 43
glyph: "d"
}
Key {
id: key14
width: 43
glyph: "f"
}
Key {
id: key15
width: 43
glyph: "g"
}
Key {
id: key16
width: 43
glyph: "h"
}
Key {
id: key17
width: 43
glyph: "j"
}
Key {
id: key18
width: 43
glyph: "k"
}
Key {
id: key19
width: 43
glyph: "l"
}
Key {
id: key32
width: 75
glyph: "⏎"
}
Key { width: 43; glyph: "a"; }
Key { width: 43; glyph: "s"; }
Key { width: 43; glyph: "d"; }
Key { width: 43; glyph: "f"; }
Key { width: 43; glyph: "g"; }
Key { width: 43; glyph: "h"; }
Key { width: 43; glyph: "j"; }
Key { width: 43; glyph: "k"; }
Key { width: 43; glyph: "l"; }
Key { width: 70; glyph: "⏎"; }
}
Row {
id: row3
width: 480
height: 50
anchors.left: parent.left
anchors.leftMargin: 0
anchors.leftMargin: 4
Key {
id: key27
width: 46
id: shiftKey
width: 43
glyph: "⇪"
toggle: true
onToggledChanged: {
shiftMode = toggled;
}
onToggledChanged: shiftMode = toggled
}
Key {
id: key20
width: 43
glyph: "z"
}
Key {
id: key21
width: 43
glyph: "x"
}
Key {
id: key22
width: 43
glyph: "c"
}
Key {
id: key23
width: 43
glyph: "v"
}
Key {
id: key24
width: 43
glyph: "b"
}
Key {
id: key25
width: 43
glyph: "n"
}
Key {
id: key26
width: 44
glyph: "m"
}
Key {
id: key31
width: 43
glyph: "_"
}
Key {
id: key33
width: 43
glyph: "?"
}
Key {
id: key36
width: 46
glyph: "/"
}
Key { width: 43; glyph: "z"; }
Key { width: 43; glyph: "x"; }
Key { width: 43; glyph: "c"; }
Key { width: 43; glyph: "v"; }
Key { width: 43; glyph: "b"; }
Key { width: 43; glyph: "n"; }
Key { width: 43; glyph: "m"; }
Key { width: 43; glyph: "_"; }
Key { width: 43; glyph: "/"; }
Key { width: 43; glyph: "?"; }
}
Row {
id: row4
width: 480
height: 50
anchors.left: parent.left
anchors.leftMargin: 19
anchors.leftMargin: 4
Key {
id: key30
width: 89
glyph: "&123"
mouseArea.onClicked: {
keyboardBase.parent.punctuationMode = true;
}
width: 70
glyph: "123"
mouseArea.onClicked: keyboardBase.parent.punctuationMode = true
}
Key { width: 231; glyph: " "; }
Key { width: 43; glyph: ","; }
Key { width: 43; glyph: "."; }
Key { width: 43; glyph: "\u276C"; }
Key { width: 43; glyph: "\u276D"; }
}
}
Column {
id: columnNumeric
width: 480
height: 200
visible: numeric
Row {
width: 480
height: 50
anchors.left: parent.left
anchors.leftMargin: 4
Key { width: 43; glyph: "1"; }
Key { width: 43; glyph: "2"; }
Key { width: 43; glyph: "3"; }
Key { width: 43; glyph: "4"; }
Key { width: 43; glyph: "5"; }
Key { width: 43; glyph: "6"; }
Key { width: 43; glyph: "7"; }
Key { width: 43; glyph: "8"; }
Key { width: 43; glyph: "9"; }
Key { width: 43; glyph: "0"; }
Key { width: 43; glyph: "←"; }
}
Row {
width: 480
height: 50
anchors.left: parent.left
anchors.leftMargin: 4
Key { width: 43; glyph: "!"; }
Key { width: 43; glyph: "@"; }
Key { width: 43; glyph: "#"; }
Key { width: 43; glyph: "$"; }
Key { width: 43; glyph: "%"; }
Key { width: 43; glyph: "^"; }
Key { width: 43; glyph: "&"; }
Key { width: 43; glyph: "*"; }
Key { width: 43; glyph: "("; }
Key { width: 43; glyph: ")"; }
Key { width: 43; glyph: "⏎"; }
}
Row {
width: 480
height: 50
anchors.left: parent.left
anchors.leftMargin: 4
Key {
id: key29
width: 285
glyph: " "
}
Key {
id: key34
id: numericShiftKey
width: 43
glyph: "⇦"
glyph: "\u21E8"
toggle: true
onToggledChanged: numericShiftMode = toggled
}
Key { width: 43; glyph: numericShiftMode ? "`" : "+"; }
Key { width: 43; glyph: numericShiftMode ? "~" : "-"; }
Key { width: 43; glyph: numericShiftMode ? "\u00A3" : "="; }
Key { width: 43; glyph: numericShiftMode ? "\u20AC" : ";"; }
Key { width: 43; glyph: numericShiftMode ? "\u00A5" : ":"; }
Key { width: 43; glyph: numericShiftMode ? "<" : "'"; }
Key { width: 43; glyph: numericShiftMode ? ">" : "\""; }
Key { width: 43; glyph: numericShiftMode ? "[" : "{"; }
Key { width: 43; glyph: numericShiftMode ? "]" : "}"; }
Key { width: 43; glyph: numericShiftMode ? "\\" : "|"; }
}
Row {
width: 480
height: 50
anchors.left: parent.left
anchors.leftMargin: 4
Key {
id: key35
x: 343
width: 43
glyph: "⇨"
width: 70
glyph: "abc"
mouseArea.onClicked: keyboardBase.parent.punctuationMode = false
}
Key { width: 231; glyph: " "; }
Key { width: 43; glyph: ","; }
Key { width: 43; glyph: "."; }
Key { width: 43; glyph: "\u276C"; }
Key { width: 43; glyph: "\u276D"; }
}
}
}
@ -386,5 +318,4 @@ Item {
anchors.top: parent.top
anchors.topMargin: 0
}
}

View file

@ -1,324 +0,0 @@
import QtQuick 2.0
Item {
id: keyboardBase
height: 200
Rectangle {
id: leftRect
y: 0
height: 200
color: "#252525"
anchors.right: keyboardRect.left
anchors.rightMargin: 0
anchors.bottom: parent.bottom
anchors.bottomMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
}
Rectangle {
id: keyboardRect
x: 206
y: 0
width: 480
height: 200
color: "#252525"
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: 0
Column {
id: column1
width: 480
height: 200
Row {
id: row1
width: 480
height: 50
anchors.left: parent.left
anchors.leftMargin: 0
Key {
id: key1
width: 43
glyph: "1"
}
Key {
id: key2
width: 43
glyph: "2"
}
Key {
id: key3
width: 43
glyph: "3"
}
Key {
id: key4
width: 43
glyph: "4"
}
Key {
id: key5
width: 43
glyph: "5"
}
Key {
id: key6
width: 43
glyph: "6"
}
Key {
id: key7
width: 43
glyph: "7"
}
Key {
id: key8
width: 43
glyph: "8"
}
Key {
id: key9
width: 43
glyph: "9"
}
Key {
id: key10
width: 43
glyph: "0"
}
Key {
id: key28
width: 50
glyph: "←"
}
}
Row {
id: row2
width: 480
height: 50
anchors.left: parent.left
anchors.leftMargin: 0
Key {
id: key11
width: 43
glyph: "!"
}
Key {
id: key12
width: 43
glyph: "@"
}
Key {
id: key13
width: 43
glyph: "#"
}
Key {
id: key14
width: 43
glyph: "$"
}
Key {
id: key15
width: 43
glyph: "%"
}
Key {
id: key16
width: 43
glyph: "^"
}
Key {
id: key17
width: 43
glyph: "&"
}
Key {
id: key18
width: 43
glyph: "*"
}
Key {
id: key19
width: 43
glyph: "("
}
Key {
id: key32
width: 43
glyph: ")"
}
Key {
id: key37
width: 50
glyph: "⏎"
}
}
Row {
id: row3
width: 480
height: 50
anchors.left: parent.left
anchors.leftMargin: 4
Key {
id: key27
width: 43
glyph: "="
}
Key {
id: key20
width: 43
glyph: "+"
}
Key {
id: key21
width: 43
glyph: "-"
}
Key {
id: key22
width: 43
glyph: ","
}
Key {
id: key23
width: 43
glyph: "."
}
Key {
id: key24
width: 43
glyph: ";"
}
Key {
id: key25
width: 43
glyph: ":"
}
Key {
id: key26
width: 43
glyph: "'"
}
Key {
id: key31
width: 43
glyph: "\""
}
Key {
id: key33
width: 43
glyph: "<"
}
Key {
id: key36
width: 43
glyph: ">"
}
}
Row {
id: row4
width: 480
height: 50
anchors.left: parent.left
anchors.leftMargin: 19
Key {
id: key30
width: 65
glyph: "abc"
mouseArea.onClicked: {
keyboardBase.parent.punctuationMode = false
}
}
Key {
id: key29
width: 285
glyph: " "
}
Key {
id: key34
width: 43
glyph: "⇦"
}
Key {
id: key35
x: 343
width: 43
glyph: "⇨"
}
}
}
}
Rectangle {
id: rightRect
y: 280
height: 200
color: "#252525"
border.width: 0
anchors.left: keyboardRect.right
anchors.leftMargin: 0
anchors.right: parent.right
anchors.rightMargin: 0
anchors.bottom: parent.bottom
anchors.bottomMargin: 0
}
Rectangle {
id: rectangle1
color: "#ffffff"
anchors.bottom: keyboardRect.top
anchors.bottomMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
anchors.right: parent.right
anchors.rightMargin: 0
anchors.top: parent.top
anchors.topMargin: 0
}
}

View file

@ -199,6 +199,11 @@ TreeView {
unfocusHelper.forceActiveFocus();
}
}
onReadOnlyChanged: {
// Have to explicily set keyboardRaised because automatic setting fails because readOnly is true at the time.
keyboardRaised = activeFocus;
}
}
}
}

View file

@ -13,8 +13,9 @@ import "."
BaseWebView {
onNewViewRequested: {
var component = Qt.createComponent("../Browser.qml");
var newWindow = component.createObject(desktop);
request.openIn(newWindow.webView)
// Load dialog via OffscreenUi so that JavaScript EventBridge is available.
var browser = OffscreenUi.load("Browser.qml");
request.openIn(browser.webView);
browser.webView.forceActiveFocus();
}
}

View file

@ -6,9 +6,18 @@ import "../controls-uit" as HiFiControls
Item {
property alias url: root.url
property alias eventBridge: eventBridgeWrapper.eventBridge
property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false
property bool keyboardRaised: false
property bool punctuationMode: false
// FIXME - Keyboard HMD only: Make Interface either set keyboardRaised property directly in OffscreenQmlSurface
// or provide HMDinfo object to QML in RenderableWebEntityItem and do the following.
/*
onKeyboardRaisedChanged: {
keyboardEnabled = HMDinfo.active;
}
*/
QtObject {
id: eventBridgeWrapper
WebChannel.id: "eventBridgeWrapper"
@ -20,7 +29,7 @@ Item {
x: 0
y: 0
width: parent.width
height: keyboardRaised ? parent.height - keyboard1.height : parent.height
height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height
// creates a global EventBridge object.
WebEngineScript {
@ -82,7 +91,7 @@ Item {
onLoadingChanged: {
keyboardRaised = false;
punctuationMode = false;
keyboard1.resetShiftMode(false);
keyboard.resetShiftMode(false);
// Required to support clicking on "hifi://" links
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
@ -105,32 +114,15 @@ Item {
}
}
// virtual keyboard, letters
HiFiControls.Keyboard {
id: keyboard1
y: keyboardRaised ? parent.height : 0
height: keyboardRaised ? 200 : 0
visible: keyboardRaised && !punctuationMode
enabled: keyboardRaised && !punctuationMode
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
anchors.bottom: parent.bottom
anchors.bottomMargin: 0
id: keyboard
raised: parent.keyboardEnabled && parent.keyboardRaised
numeric: parent.punctuationMode
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
}
}
HiFiControls.KeyboardPunctuation {
id: keyboard2
y: keyboardRaised ? parent.height : 0
height: keyboardRaised ? 200 : 0
visible: keyboardRaised && punctuationMode
enabled: keyboardRaised && punctuationMode
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
anchors.bottom: parent.bottom
anchors.bottomMargin: 0
}
}

View file

@ -22,7 +22,7 @@ ModalWindow {
implicitWidth: 640;
implicitHeight: 320;
visible: true;
keyboardEnabled: false // Disable ModalWindow's keyboard.
keyboardOverride: true // Disable ModalWindow's keyboard.
signal selected(var result);
signal canceled();
@ -51,6 +51,7 @@ ModalWindow {
}
}
property bool keyboardEnabled: false
property bool keyboardRaised: false
property bool punctuationMode: false
onKeyboardRaisedChanged: d.resize();
@ -116,7 +117,7 @@ ModalWindow {
var targetHeight = (textField.visible ? textField.controlHeight + hifi.dimensions.contentSpacing.y : 0) +
(extraInputs.visible ? extraInputs.height + hifi.dimensions.contentSpacing.y : 0) +
(buttons.height + 3 * hifi.dimensions.contentSpacing.y) +
(root.keyboardRaised ? (200 + hifi.dimensions.contentSpacing.y) : 0);
((keyboardEnabled && keyboardRaised) ? (keyboard.raisedHeight + hifi.dimensions.contentSpacing.y) : 0);
root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWdith) ? d.maxWidth : targetWidth);
root.height = (targetHeight < d.minHeight) ? d.minHeight : ((targetHeight > d.maxHeight) ?
@ -153,38 +154,15 @@ ModalWindow {
}
}
Item {
Keyboard {
id: keyboard
height: keyboardRaised ? 200 : 0
raised: keyboardEnabled && keyboardRaised
numeric: punctuationMode
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
bottomMargin: keyboardRaised ? hifi.dimensions.contentSpacing.y : 0
}
Keyboard {
id: keyboard1
visible: keyboardRaised && !punctuationMode
enabled: keyboardRaised && !punctuationMode
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
}
}
KeyboardPunctuation {
id: keyboard2
visible: keyboardRaised && punctuationMode
enabled: keyboardRaised && punctuationMode
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
}
bottomMargin: raised ? hifi.dimensions.contentSpacing.y : 0
}
}
}
@ -339,6 +317,7 @@ ModalWindow {
}
Component.onCompleted: {
keyboardEnabled = HMD.active;
updateIcon();
updateCheckbox();
d.resize();

View file

@ -27,7 +27,7 @@ ModalWindow {
id: root
resizable: true
implicitWidth: 480
implicitHeight: 360 + (fileDialogItem.keyboardRaised ? 200 + hifi.dimensions.contentSpacing.y : 0)
implicitHeight: 360 + (fileDialogItem.keyboardEnabled && fileDialogItem.keyboardRaised ? keyboard.raisedHeight + hifi.dimensions.contentSpacing.y : 0)
minSize: Qt.vector2d(360, 240)
draggable: true
@ -70,7 +70,9 @@ ModalWindow {
signal canceled();
Component.onCompleted: {
console.log("Helper " + helper + " drives " + drives)
console.log("Helper " + helper + " drives " + drives);
fileDialogItem.keyboardEnabled = HMD.active;
// HACK: The following lines force the model to initialize properly such that the go-up button
// works properly from the initial screen.
@ -106,6 +108,7 @@ ModalWindow {
height: pane.height
anchors.margins: 0
property bool keyboardEnabled: false
property bool keyboardRaised: false
property bool punctuationMode: false
@ -626,7 +629,7 @@ ModalWindow {
left: parent.left
right: selectionType.visible ? selectionType.left: parent.right
rightMargin: selectionType.visible ? hifi.dimensions.contentSpacing.x : 0
bottom: keyboard1.top
bottom: keyboard.top
bottomMargin: hifi.dimensions.contentSpacing.y
}
readOnly: !root.saveDialog
@ -648,25 +651,15 @@ ModalWindow {
}
Keyboard {
id: keyboard1
height: parent.keyboardRaised ? 200 : 0
visible: parent.keyboardRaised && !parent.punctuationMode
enabled: visible
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: buttonRow.top
anchors.bottomMargin: visible ? hifi.dimensions.contentSpacing.y : 0
}
KeyboardPunctuation {
id: keyboard2
height: parent.keyboardRaised ? 200 : 0
visible: parent.keyboardRaised && parent.punctuationMode
enabled: visible
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: buttonRow.top
anchors.bottomMargin: visible ? hifi.dimensions.contentSpacing.y : 0
id: keyboard
raised: parent.keyboardEnabled && parent.keyboardRaised
numeric: parent.punctuationMode
anchors {
left: parent.left
right: parent.right
bottom: buttonRow.top
bottomMargin: visible ? hifi.dimensions.contentSpacing.y : 0
}
}
Row {

View file

@ -22,6 +22,7 @@ ModalWindow {
implicitWidth: 640
implicitHeight: 320
visible: true
keyboardOverride: true // Disable ModalWindow's keyboard.
signal selected(var result);
signal canceled();
@ -45,6 +46,12 @@ ModalWindow {
property int titleWidth: 0
onTitleWidthChanged: d.resize();
property bool keyboardEnabled: false
property bool keyboardRaised: false
property bool punctuationMode: false
onKeyboardRaisedChanged: d.resize();
function updateIcon() {
if (!root) {
return;
@ -59,11 +66,6 @@ ModalWindow {
height: pane.height
anchors.margins: 0
property bool keyboardRaised: false
property bool punctuationMode: false
onKeyboardRaisedChanged: d.resize();
QtObject {
id: d
readonly property int minWidth: 480
@ -74,15 +76,15 @@ ModalWindow {
function resize() {
var targetWidth = Math.max(titleWidth, pane.width)
var targetHeight = (items ? comboBox.controlHeight : textResult.controlHeight) + 5 * hifi.dimensions.contentSpacing.y + buttons.height
root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWdith) ? d.maxWidth : targetWidth)
root.height = ((targetHeight < d.minHeight) ? d.minHeight: ((targetHeight > d.maxHeight) ? d.maxHeight : targetHeight)) + (modalWindowItem.keyboardRaised ? (200 + 2 * hifi.dimensions.contentSpacing.y) : 0)
root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWdith) ? d.maxWidth : targetWidth);
root.height = ((targetHeight < d.minHeight) ? d.minHeight : ((targetHeight > d.maxHeight) ? d.maxHeight : targetHeight)) + ((keyboardEnabled && keyboardRaised) ? (keyboard.raisedHeight + 2 * hifi.dimensions.contentSpacing.y) : 0)
}
}
Item {
anchors {
top: parent.top
bottom: keyboard1.top;
bottom: keyboard.top;
left: parent.left;
right: parent.right;
margins: 0
@ -116,33 +118,16 @@ ModalWindow {
}
}
// virtual keyboard, letters
Keyboard {
id: keyboard1
y: parent.keyboardRaised ? parent.height : 0
height: parent.keyboardRaised ? 200 : 0
visible: parent.keyboardRaised && !parent.punctuationMode
enabled: parent.keyboardRaised && !parent.punctuationMode
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
anchors.bottom: buttons.top
anchors.bottomMargin: parent.keyboardRaised ? 2 * hifi.dimensions.contentSpacing.y : 0
}
KeyboardPunctuation {
id: keyboard2
y: parent.keyboardRaised ? parent.height : 0
height: parent.keyboardRaised ? 200 : 0
visible: parent.keyboardRaised && parent.punctuationMode
enabled: parent.keyboardRaised && parent.punctuationMode
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
anchors.bottom: buttons.top
anchors.bottomMargin: parent.keyboardRaised ? 2 * hifi.dimensions.contentSpacing.y : 0
id: keyboard
raised: keyboardEnabled && keyboardRaised
numeric: punctuationMode
anchors {
left: parent.left
right: parent.right
bottom: buttons.top
bottomMargin: raised ? 2 * hifi.dimensions.contentSpacing.y : 0
}
}
Flow {
@ -203,6 +188,7 @@ ModalWindow {
}
Component.onCompleted: {
keyboardEnabled = HMD.active;
updateIcon();
d.resize();
textResult.forceActiveFocus();

View file

@ -10,23 +10,84 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtWebEngine 1.1
import QtWebChannel 1.0
import QtWebEngine 1.2
import "../../windows" as Windows
import "../../controls-uit" as Controls
import "../../windows"
import "../../controls-uit"
import "../../styles-uit"
Windows.Window {
Window {
id: root
HifiConstants { id: hifi }
width: 900; height: 700
resizable: true
modality: Qt.ApplicationModal
Controls.WebView {
id: webview
property alias eventBridge: eventBridgeWrapper.eventBridge
Item {
anchors.fill: parent
url: "https://metaverse.highfidelity.com/marketplace?category=avatars"
focus: true
property bool keyboardEnabled: false
property bool keyboardRaised: true
property bool punctuationMode: false
BaseWebView {
id: webview
url: "https://metaverse.highfidelity.com/marketplace?category=avatars"
focus: true
anchors {
top: parent.top
left: parent.left
right: parent.right
bottom: keyboard.top
}
property alias eventBridgeWrapper: eventBridgeWrapper
QtObject {
id: eventBridgeWrapper
WebChannel.id: "eventBridgeWrapper"
property var eventBridge;
}
webChannel.registeredObjects: [eventBridgeWrapper]
// Create a global EventBridge object for raiseAndLowerKeyboard.
WebEngineScript {
id: createGlobalEventBridge
sourceCode: eventBridgeJavaScriptToInject
injectionPoint: WebEngineScript.DocumentCreation
worldId: WebEngineScript.MainWorld
}
// Detect when may want to raise and lower keyboard.
WebEngineScript {
id: raiseAndLowerKeyboard
injectionPoint: WebEngineScript.Deferred
sourceUrl: resourceDirectoryUrl + "html/raiseAndLowerKeyboard.js"
worldId: WebEngineScript.MainWorld
}
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ]
}
Keyboard {
id: keyboard
raised: parent.keyboardEnabled && parent.keyboardRaised
numeric: parent.punctuationMode
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
}
}
Component.onCompleted: {
keyboardEnabled = HMD.active;
}
}
}

View file

@ -74,11 +74,6 @@ Preference {
colorScheme: hifi.colorSchemes.dark
}
Component {
id: avatarBrowserBuilder;
AvatarBrowser { }
}
Button {
id: button
text: "Browse"
@ -87,12 +82,12 @@ Preference {
verticalCenter: dataTextField.verticalCenter
}
onClicked: {
root.browser = avatarBrowserBuilder.createObject(desktop);
// Load dialog via OffscreenUi so that JavaScript EventBridge is available.
root.browser = OffscreenUi.load("dialogs/preferences/AvatarBrowser.qml");
root.browser.windowDestroyed.connect(function(){
root.browser = null;
})
});
}
}
}
}

View file

@ -45,7 +45,7 @@ ScrollingWindow {
Rectangle {
width: parent.width
height: root.height - (keyboardRaised ? 200 : 0)
height: root.height - (keyboardEnabled && keyboardRaised ? 200 : 0)
radius: 4
color: hifi.colors.baseGray
@ -210,7 +210,7 @@ ScrollingWindow {
}
onKeyboardRaisedChanged: {
if (keyboardRaised) {
if (keyboardEnabled && keyboardRaised) {
// Scroll to item with focus if necessary.
var footerHeight = newAttachmentButton.height + buttonRow.height + 3 * hifi.dimensions.contentSpacing.y;
var delta = activator.mouseY

View file

@ -30,7 +30,7 @@ ScrollingWindow {
Rectangle {
width: parent.width
height: root.height - (keyboardRaised ? 200 : 0)
height: root.height - (keyboardEnabled && keyboardRaised ? 200 : 0)
radius: 4
color: hifi.colors.baseGray

View file

@ -34,7 +34,8 @@ Window {
property var footer: Item { } // Optional static footer at the bottom of the dialog.
readonly property var footerContentHeight: footer.height > 0 ? (footer.height + 2 * hifi.dimensions.contentSpacing.y + 3) : 0
property bool keyboardEnabled: true // Set false if derived control implements its own keyboard.
property bool keyboardOverride: false // Set true in derived control if it implements its own keyboard.
property bool keyboardEnabled: false
property bool keyboardRaised: false
property bool punctuationMode: false
@ -132,7 +133,7 @@ Window {
// Optional non-scrolling footer.
id: footerPane
property alias keyboardEnabled: window.keyboardEnabled
property alias keyboardOverride: window.keyboardOverride
property alias keyboardRaised: window.keyboardRaised
property alias punctuationMode: window.punctuationMode
@ -141,9 +142,9 @@ Window {
bottom: parent.bottom
}
width: parent.contentWidth
height: footerContentHeight + (keyboardEnabled && keyboardRaised ? 200 : 0)
height: footerContentHeight + (keyboard.enabled && keyboard.raised ? keyboard.height : 0)
color: hifi.colors.baseGray
visible: footer.height > 0 || keyboardEnabled && keyboardRaised
visible: footer.height > 0 || keyboard.enabled && keyboard.raised
Item {
// Horizontal rule.
@ -181,22 +182,10 @@ Window {
}
HiFiControls.Keyboard {
id: keyboard1
height: parent.keyboardEnabled && parent.keyboardRaised ? 200 : 0
visible: parent.keyboardEnabled && parent.keyboardRaised && !parent.punctuationMode
enabled: parent.keyboardEnabled && parent.keyboardRaised && !parent.punctuationMode
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
}
}
HiFiControls.KeyboardPunctuation {
id: keyboard2
height: parent.keyboardEnabled && parent.keyboardRaised ? 200 : 0
visible: parent.keyboardEnabled && parent.keyboardRaised && parent.punctuationMode
enabled: parent.keyboardEnabled && parent.keyboardRaised && parent.punctuationMode
id: keyboard
enabled: !keyboardOverride
raised: keyboardEnabled && keyboardRaised
numeric: punctuationMode
anchors {
left: parent.left
right: parent.right
@ -207,9 +196,9 @@ Window {
}
onKeyboardRaisedChanged: {
if (keyboardEnabled && keyboardRaised) {
if (!keyboardOverride && keyboardEnabled && keyboardRaised) {
var delta = activator.mouseY
- (activator.height + activator.y - 200 - footerContentHeight - hifi.dimensions.controlLineHeight);
- (activator.height + activator.y - keyboard.raisedHeight - footerContentHeight - hifi.dimensions.controlLineHeight);
if (delta > 0) {
pane.scrollBy(delta);
@ -220,4 +209,10 @@ Window {
}
}
}
Component.onCompleted: {
if (typeof HMD !== "undefined") {
keyboardEnabled = HMD.active;
}
}
}

View file

@ -94,6 +94,7 @@
#include <RenderShadowTask.h>
#include <RenderDeferredTask.h>
#include <ResourceCache.h>
#include <SandboxUtils.h>
#include <SceneScriptingInterface.h>
#include <ScriptEngines.h>
#include <ScriptCache.h>
@ -415,8 +416,6 @@ bool setupEssentials(int& argc, char** argv) {
static const auto SUPPRESS_SETTINGS_RESET = "--suppress-settings-reset";
bool suppressPrompt = cmdOptionExists(argc, const_cast<const char**>(argv), SUPPRESS_SETTINGS_RESET);
bool previousSessionCrashed = CrashHandler::checkForResetSettings(suppressPrompt);
CrashHandler::writeRunningMarkerFiler();
qAddPostRoutine(CrashHandler::deleteRunningMarkerFile);
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
DependencyManager::registerInheritance<AvatarHashMap, AvatarManager>();
@ -504,8 +503,11 @@ Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context);
Setting::Handle<int> sessionRunTime{ "sessionRunTime", 0 };
Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bool runServer, QString runServerPathOption) :
QApplication(argc, argv),
_shouldRunServer(runServer),
_runServerPath(runServerPathOption),
_runningMarker(this, RUNNING_MARKER_FILENAME),
_window(new MainWindow(desktop())),
_sessionRunTimer(startupTimer),
_previousSessionCrashed(setupEssentials(argc, argv)),
@ -529,7 +531,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
_maxOctreePPS(maxOctreePacketsPerSecond.get()),
_lastFaceTrackerUpdate(0)
{
_runningMarker.startRunningMarker();
PluginContainer* pluginContainer = dynamic_cast<PluginContainer*>(this); // set the container for any plugins that care
PluginManager::getInstance()->setContainer(pluginContainer);
@ -575,6 +577,28 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
qCDebug(interfaceapp) << "[VERSION] We will use DEVELOPMENT global services.";
#endif
bool wantsSandboxRunning = shouldRunServer();
static bool determinedSandboxState = false;
SandboxUtils sandboxUtils;
sandboxUtils.ifLocalSandboxRunningElse([&]() {
qCDebug(interfaceapp) << "Home sandbox appears to be running.....";
determinedSandboxState = true;
}, [&, wantsSandboxRunning]() {
qCDebug(interfaceapp) << "Home sandbox does not appear to be running....";
determinedSandboxState = true;
if (wantsSandboxRunning) {
QString contentPath = getRunServerPath();
SandboxUtils::runLocalSandbox(contentPath, true, RUNNING_MARKER_FILENAME);
}
});
quint64 MAX_WAIT_TIME = USECS_PER_SECOND * 4;
auto startWaiting = usecTimestampNow();
while (!determinedSandboxState && (usecTimestampNow() - startWaiting <= MAX_WAIT_TIME)) {
QCoreApplication::processEvents();
usleep(USECS_PER_MSEC * 50); // 20hz
}
_bookmarks = new Bookmarks(); // Before setting up the menu
@ -1265,7 +1289,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
// Get sandbox content set version, if available
auto acDirPath = PathUtils::getRootDataDirectory() + BuildInfo::MODIFIED_ORGANIZATION + "/assignment-client/";
auto contentVersionPath = acDirPath + "content-version.txt";
qDebug() << "Checking " << contentVersionPath << " for content version";
qCDebug(interfaceapp) << "Checking " << contentVersionPath << " for content version";
auto contentVersion = 0;
QFile contentVersionFile(contentVersionPath);
if (contentVersionFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
@ -1273,7 +1297,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
// toInt() returns 0 if the conversion fails, so we don't need to specifically check for failure
contentVersion = line.toInt();
}
qDebug() << "Server content version: " << contentVersion;
qCDebug(interfaceapp) << "Server content version: " << contentVersion;
bool hasTutorialContent = contentVersion >= 1;
@ -1283,10 +1307,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
bool shouldGoToTutorial = hasHMDAndHandControllers && hasTutorialContent && !tutorialComplete.get();
qDebug() << "Has HMD + Hand Controllers: " << hasHMDAndHandControllers << ", current plugin: " << _displayPlugin->getName();
qDebug() << "Has tutorial content: " << hasTutorialContent;
qDebug() << "Tutorial complete: " << tutorialComplete.get();
qDebug() << "Should go to tutorial: " << shouldGoToTutorial;
qCDebug(interfaceapp) << "Has HMD + Hand Controllers: " << hasHMDAndHandControllers << ", current plugin: " << _displayPlugin->getName();
qCDebug(interfaceapp) << "Has tutorial content: " << hasTutorialContent;
qCDebug(interfaceapp) << "Tutorial complete: " << tutorialComplete.get();
qCDebug(interfaceapp) << "Should go to tutorial: " << shouldGoToTutorial;
// when --url in command line, teleport to location
const QString HIFI_URL_COMMAND_LINE_KEY = "--url";
@ -1299,11 +1323,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
const QString TUTORIAL_PATH = "/tutorial_begin";
if (shouldGoToTutorial) {
DependencyManager::get<AddressManager>()->ifLocalSandboxRunningElse([=]() {
qDebug() << "Home sandbox appears to be running, going to Home.";
sandboxUtils.ifLocalSandboxRunningElse([=]() {
qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home.";
DependencyManager::get<AddressManager>()->goToLocalSandbox(TUTORIAL_PATH);
}, [=]() {
qDebug() << "Home sandbox does not appear to be running, going to Entry.";
qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry.";
if (firstRun.get()) {
showHelp();
}
@ -1324,18 +1348,18 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
// If this is a first run we short-circuit the address passed in
if (isFirstRun) {
if (hasHMDAndHandControllers) {
DependencyManager::get<AddressManager>()->ifLocalSandboxRunningElse([=]() {
qDebug() << "Home sandbox appears to be running, going to Home.";
sandboxUtils.ifLocalSandboxRunningElse([=]() {
qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home.";
DependencyManager::get<AddressManager>()->goToLocalSandbox();
}, [=]() {
qDebug() << "Home sandbox does not appear to be running, going to Entry.";
qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry.";
DependencyManager::get<AddressManager>()->goToEntry();
});
} else {
DependencyManager::get<AddressManager>()->goToEntry();
}
} else {
qDebug() << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString);
qCDebug(interfaceapp) << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString);
DependencyManager::get<AddressManager>()->loadSettings(addressLookupString);
}
}

View file

@ -46,6 +46,8 @@
#include <ThreadSafeValueCache.h>
#include <shared/FileLogger.h>
#include <RunningMarker.h>
#include "avatar/MyAvatar.h"
#include "Bookmarks.h"
#include "Camera.h"
@ -87,6 +89,8 @@ static const UINT UWM_SHOW_APPLICATION =
RegisterWindowMessage("UWM_SHOW_APPLICATION_{71123FD6-3DA8-4DC1-9C27-8A12A6250CBA}_" + qgetenv("USERNAME"));
#endif
static const QString RUNNING_MARKER_FILENAME = "Interface.running";
class Application;
#if defined(qApp)
#undef qApp
@ -103,7 +107,16 @@ class Application : public QApplication,
// TODO? Get rid of those
friend class OctreePacketProcessor;
private:
bool _shouldRunServer { false };
QString _runServerPath;
RunningMarker _runningMarker;
public:
// startup related getter/setters
bool shouldRunServer() const { return _shouldRunServer; }
bool hasRunServerPath() const { return !_runServerPath.isEmpty(); }
QString getRunServerPath() const { return _runServerPath; }
// virtual functions required for PluginContainer
virtual ui::Menu* getPrimaryMenu() override;
@ -127,7 +140,7 @@ public:
static void initPlugins(const QStringList& arguments);
static void shutdownPlugins();
Application(int& argc, char** argv, QElapsedTimer& startup_time);
Application(int& argc, char** argv, QElapsedTimer& startup_time, bool runServer, QString runServerPathOption);
~Application();
void postLambdaEvent(std::function<void()> f) override;

View file

@ -23,9 +23,10 @@
#include <QVBoxLayout>
#include <QtCore/QUrl>
#include "Application.h"
#include "Menu.h"
static const QString RUNNING_MARKER_FILENAME = "Interface.running";
#include <RunningMarker.h>
bool CrashHandler::checkForResetSettings(bool suppressPrompt) {
QSettings::setDefaultFormat(QSettings::IniFormat);
@ -39,7 +40,7 @@ bool CrashHandler::checkForResetSettings(bool suppressPrompt) {
// If option does not exist in Interface.ini so assume default behavior.
bool displaySettingsResetOnCrash = !displayCrashOptions.isValid() || displayCrashOptions.toBool();
QFile runningMarkerFile(runningMarkerFilePath());
QFile runningMarkerFile(RunningMarker::getMarkerFilePath(RUNNING_MARKER_FILENAME));
bool wasLikelyCrash = runningMarkerFile.exists();
if (suppressPrompt) {
@ -161,20 +162,3 @@ void CrashHandler::handleCrash(CrashHandler::Action action) {
}
}
void CrashHandler::writeRunningMarkerFiler() {
QFile runningMarkerFile(runningMarkerFilePath());
if (!runningMarkerFile.exists()) {
runningMarkerFile.open(QIODevice::WriteOnly);
runningMarkerFile.close();
}
}
void CrashHandler::deleteRunningMarkerFile() {
QFile runningMarkerFile(runningMarkerFilePath());
if (runningMarkerFile.exists()) {
runningMarkerFile.remove();
}
}
const QString CrashHandler::runningMarkerFilePath() {
return QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + RUNNING_MARKER_FILENAME;
}

View file

@ -19,9 +19,6 @@ class CrashHandler {
public:
static bool checkForResetSettings(bool suppressPrompt = false);
static void writeRunningMarkerFiler();
static void deleteRunningMarkerFile();
private:
enum Action {
DELETE_INTERFACE_INI,
@ -31,8 +28,6 @@ private:
static Action promptUserForAction(bool showCrashMessage);
static void handleCrash(Action action);
static const QString runningMarkerFilePath();
};
#endif // hifi_CrashHandler_h

View file

@ -37,6 +37,8 @@
#include <CrashReporter.h>
#endif
int main(int argc, const char* argv[]) {
#if HAS_BUGSPLAT
static QString BUG_SPLAT_DATABASE = "interface_alpha";
@ -128,22 +130,9 @@ int main(int argc, const char* argv[]) {
parser.addOption(runServerOption);
parser.addOption(serverContentPathOption);
parser.parse(arguments);
if (parser.isSet(runServerOption)) {
QString applicationDirPath = QFileInfo(arguments[0]).path();
QString serverPath = applicationDirPath + "/server-console/server-console.exe";
qDebug() << "Application dir path is: " << applicationDirPath;
qDebug() << "Server path is: " << serverPath;
QStringList args;
if (parser.isSet(serverContentPathOption)) {
QString serverContentPath = QFileInfo(arguments[0]).path() + "/" + parser.value(serverContentPathOption);
args << "--" << "--contentPath" << serverContentPath;
}
qDebug() << QFileInfo(arguments[0]).path();
qDebug() << QProcess::startDetached(serverPath, args);
// Sleep a short amount of time to give the server a chance to start
usleep(2000000);
}
bool runServer = parser.isSet(runServerOption);
bool serverContentPathOptionIsSet = parser.isSet(serverContentPathOption);
QString serverContentPathOptionValue = serverContentPathOptionIsSet ? parser.value(serverContentPathOption) : QString();
QElapsedTimer startupTime;
startupTime.start();
@ -166,10 +155,11 @@ int main(int argc, const char* argv[]) {
SteamClient::init();
int exitCode;
{
QSettings::setDefaultFormat(QSettings::IniFormat);
Application app(argc, const_cast<char**>(argv), startupTime);
Application app(argc, const_cast<char**>(argv), startupTime, runServer, serverContentPathOptionValue);
// If we failed the OpenGLVersion check, log it.
if (override) {
@ -223,7 +213,6 @@ int main(int argc, const char* argv[]) {
QTranslator translator;
translator.load("i18n/interface_en");
app.installTranslator(&translator);
qCDebug(interfaceapp, "Created QT Application.");
exitCode = app.exec();
server.close();

View file

@ -43,9 +43,7 @@ const QString SNAPSHOTS_DIRECTORY = "Snapshots";
const QString URL = "highfidelity_url";
Setting::Handle<QString> Snapshot::snapshotsLocation("snapshotsLocation",
QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
Setting::Handle<bool> Snapshot::hasSetSnapshotsLocation("hasSetSnapshotsLocation", false);
Setting::Handle<QString> Snapshot::snapshotsLocation("snapshotsLocation");
SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) {
@ -105,42 +103,44 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary) {
const int IMAGE_QUALITY = 100;
if (!isTemporary) {
QString snapshotFullPath;
if (!hasSetSnapshotsLocation.get()) {
snapshotFullPath = QFileDialog::getExistingDirectory(nullptr, "Choose Snapshots Directory", snapshotsLocation.get());
hasSetSnapshotsLocation.set(true);
QString snapshotFullPath = snapshotsLocation.get();
if (snapshotFullPath.isEmpty()) {
snapshotFullPath = OffscreenUi::getExistingDirectory(nullptr, "Choose Snapshots Directory", QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
snapshotsLocation.set(snapshotFullPath);
} else {
snapshotFullPath = snapshotsLocation.get();
}
if (!snapshotFullPath.endsWith(QDir::separator())) {
snapshotFullPath.append(QDir::separator());
if (!snapshotFullPath.isEmpty()) { // not cancelled
if (!snapshotFullPath.endsWith(QDir::separator())) {
snapshotFullPath.append(QDir::separator());
}
snapshotFullPath.append(filename);
QFile* imageFile = new QFile(snapshotFullPath);
imageFile->open(QIODevice::WriteOnly);
shot.save(imageFile, 0, IMAGE_QUALITY);
imageFile->close();
return imageFile;
}
snapshotFullPath.append(filename);
QFile* imageFile = new QFile(snapshotFullPath);
imageFile->open(QIODevice::WriteOnly);
shot.save(imageFile, 0, IMAGE_QUALITY);
imageFile->close();
return imageFile;
} else {
QTemporaryFile* imageTempFile = new QTemporaryFile(QDir::tempPath() + "/XXXXXX-" + filename);
if (!imageTempFile->open()) {
qDebug() << "Unable to open QTemporaryFile for temp snapshot. Will not save.";
return NULL;
}
shot.save(imageTempFile, 0, IMAGE_QUALITY);
imageTempFile->close();
return imageTempFile;
}
// Either we were asked for a tempororary, or the user didn't set a directory.
QTemporaryFile* imageTempFile = new QTemporaryFile(QDir::tempPath() + "/XXXXXX-" + filename);
if (!imageTempFile->open()) {
qDebug() << "Unable to open QTemporaryFile for temp snapshot. Will not save.";
return NULL;
}
imageTempFile->setAutoRemove(isTemporary);
shot.save(imageTempFile, 0, IMAGE_QUALITY);
imageTempFile->close();
return imageTempFile;
}
void Snapshot::uploadSnapshot(const QString& filename) {

View file

@ -39,7 +39,6 @@ public:
static SnapshotMetaData* parseSnapshotData(QString snapshotPath);
static Setting::Handle<QString> snapshotsLocation;
static Setting::Handle<bool> hasSetSnapshotsLocation;
static void uploadSnapshot(const QString& filename);
private:
static QFile* savedFileForSnapshot(QImage & image, bool isTemporary);

View file

@ -108,6 +108,7 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) {
_webSurface->resume();
_webSurface->getRootItem()->setProperty("url", _sourceUrl);
_webSurface->getRootContext()->setContextProperty("desktop", QVariant());
// FIXME - Keyboard HMD only: Possibly add "HMDinfo" object to context for WebView.qml.
// forward web events to EntityScriptingInterface
auto entities = DependencyManager::get<EntityScriptingInterface>();

View file

@ -309,6 +309,7 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) {
}
// FIXME
_glData = ::getGLContextData(); // Initialize JSON structure so that it can be filled in later and then used in QML.
_qmlEngine->rootContext()->setContextProperty("GL", _glData);
_qmlEngine->rootContext()->setContextProperty("offscreenWindow", QVariant::fromValue(getWindow()));
_qmlComponent = new QQmlComponent(_qmlEngine);
@ -726,13 +727,13 @@ void OffscreenQmlSurface::setFocusText(bool newFocusText) {
}
// UTF-8 encoded symbols
static const uint8_t UPWARDS_WHITE_ARROW_FROM_BAR[] = { 0xE2, 0x87, 0xAA, 0x00 }; // shift
static const uint8_t LEFT_ARROW[] = { 0xE2, 0x86, 0x90, 0x00 }; // backspace
static const uint8_t LEFTWARD_WHITE_ARROW[] = { 0xE2, 0x87, 0xA6, 0x00 }; // left arrow
static const uint8_t RIGHTWARD_WHITE_ARROW[] = { 0xE2, 0x87, 0xA8, 0x00 }; // right arrow
static const uint8_t ASTERISIM[] = { 0xE2, 0x81, 0x82, 0x00 }; // symbols
static const uint8_t RETURN_SYMBOL[] = { 0xE2, 0x8F, 0x8E, 0x00 }; // return
static const char PUNCTUATION_STRING[] = "&123";
static const uint8_t SHIFT_ARROW[] = { 0xE2, 0x87, 0xAA, 0x00 };
static const uint8_t NUMERIC_SHIFT_ARROW[] = { 0xE2, 0x87, 0xA8, 0x00 };
static const uint8_t BACKSPACE_SYMBOL[] = { 0xE2, 0x86, 0x90, 0x00 };
static const uint8_t LEFT_ARROW[] = { 0xE2, 0x9D, 0xAC, 0x00 };
static const uint8_t RIGHT_ARROW[] = { 0xE2, 0x9D, 0xAD, 0x00 };
static const uint8_t RETURN_SYMBOL[] = { 0xE2, 0x8F, 0x8E, 0x00 };
static const char PUNCTUATION_STRING[] = "123";
static const char ALPHABET_STRING[] = "abc";
static bool equals(const QByteArray& byteArray, const uint8_t* ptr) {
@ -752,19 +753,19 @@ void OffscreenQmlSurface::synthesizeKeyPress(QString key) {
int scanCode = (int)utf8Key[0];
QString keyString = key;
if (equals(utf8Key, UPWARDS_WHITE_ARROW_FROM_BAR) || equals(utf8Key, ASTERISIM) ||
if (equals(utf8Key, SHIFT_ARROW) || equals(utf8Key, NUMERIC_SHIFT_ARROW) ||
equals(utf8Key, (uint8_t*)PUNCTUATION_STRING) || equals(utf8Key, (uint8_t*)ALPHABET_STRING)) {
return; // ignore
} else if (equals(utf8Key, LEFT_ARROW)) {
} else if (equals(utf8Key, BACKSPACE_SYMBOL)) {
scanCode = Qt::Key_Backspace;
keyString = "\x08";
} else if (equals(utf8Key, RETURN_SYMBOL)) {
scanCode = Qt::Key_Return;
keyString = "\x0d";
} else if (equals(utf8Key, LEFTWARD_WHITE_ARROW)) {
} else if (equals(utf8Key, LEFT_ARROW)) {
scanCode = Qt::Key_Left;
keyString = "";
} else if (equals(utf8Key, RIGHTWARD_WHITE_ARROW)) {
} else if (equals(utf8Key, RIGHT_ARROW)) {
scanCode = Qt::Key_Right;
keyString = "";
}
@ -787,6 +788,7 @@ void OffscreenQmlSurface::setKeyboardRaised(QObject* object, bool raised, bool n
numeric = numeric || QString(item->metaObject()->className()).left(7) == "SpinBox";
if (item->property("keyboardRaised").isValid()) {
// FIXME - HMD only: Possibly set value of "keyboardEnabled" per isHMDMode() for use in WebView.qml.
if (item->property("punctuationMode").isValid()) {
item->setProperty("punctuationMode", QVariant(numeric));
}

View file

@ -37,27 +37,35 @@ void TextureRecycler::clear() {
_allTextures.clear();
}
void TextureRecycler::addTexture() {
uint32_t newTexture;
glGenTextures(1, &newTexture);
glBindTexture(GL_TEXTURE_2D, newTexture);
if (_useMipmaps) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8.0f);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -0.2f);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8.0f);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, _size.x, _size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
_allTextures.emplace(std::piecewise_construct, std::forward_as_tuple(newTexture), std::forward_as_tuple(newTexture, _size));
_readyTextures.push(newTexture);
}
uint32_t TextureRecycler::getNextTexture() {
while (_allTextures.size() < _textureCount) {
addTexture();
}
if (_readyTextures.empty()) {
uint32_t newTexture;
glGenTextures(1, &newTexture);
glBindTexture(GL_TEXTURE_2D, newTexture);
if (_useMipmaps) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8.0f);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -0.2f);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8.0f);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, _size.x, _size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
_allTextures.emplace(std::piecewise_construct, std::forward_as_tuple(newTexture), std::forward_as_tuple(newTexture, _size));
_readyTextures.push(newTexture);
addTexture();
}
uint32_t result = _readyTextures.front();

View file

@ -15,15 +15,21 @@
#include <GLMHelpers.h>
// GPU resources are typically buffered for one copy being used by the renderer,
// one copy in flight, and one copy being used by the receiver
#define GPU_RESOURCE_BUFFER_SIZE 3
class TextureRecycler {
public:
TextureRecycler(bool useMipmaps) : _useMipmaps(useMipmaps) {}
void setSize(const uvec2& size);
void setTextureCount(uint8_t textureCount);
void clear();
uint32_t getNextTexture();
void recycleTexture(uint32_t texture);
private:
void addTexture();
struct TexInfo {
const uint32_t _tex{ 0 };
@ -42,6 +48,7 @@ private:
Queue _readyTextures;
uvec2 _size{ 1920, 1080 };
bool _useMipmaps;
uint8_t _textureCount { GPU_RESOURCE_BUFFER_SIZE };
};
#endif

View file

@ -663,9 +663,19 @@ void GLBackend::recycle() const {
Lock lock(_trashMutex);
std::swap(_externalTexturesTrash, externalTexturesTrash);
}
for (auto pair : externalTexturesTrash) {
auto fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
pair.second(pair.first, fence);
if (!externalTexturesTrash.empty()) {
std::vector<GLsync> fences;
fences.resize(externalTexturesTrash.size());
for (size_t i = 0; i < externalTexturesTrash.size(); ++i) {
fences[i] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
}
// External texture fences will be read in another thread/context, so we need a flush
glFlush();
size_t index = 0;
for (auto pair : externalTexturesTrash) {
auto fence = fences[index++];
pair.second(pair.first, fence);
}
}
}

View file

@ -831,32 +831,3 @@ void AddressManager::addCurrentAddressToHistory(LookupTrigger trigger) {
}
}
void AddressManager::ifLocalSandboxRunningElse(std::function<void()> localSandboxRunningDoThis,
std::function<void()> localSandboxNotRunningDoThat) {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest sandboxStatus(SANDBOX_STATUS_URL);
sandboxStatus.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
sandboxStatus.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
QNetworkReply* reply = networkAccessManager.get(sandboxStatus);
connect(reply, &QNetworkReply::finished, this, [reply, localSandboxRunningDoThis, localSandboxNotRunningDoThat]() {
auto statusData = reply->readAll();
auto statusJson = QJsonDocument::fromJson(statusData);
if (!statusJson.isEmpty()) {
auto statusObject = statusJson.object();
auto serversValue = statusObject.value("servers");
if (!serversValue.isUndefined() && serversValue.isObject()) {
auto serversObject = serversValue.toObject();
auto serversCount = serversObject.size();
const int MINIMUM_EXPECTED_SERVER_COUNT = 5;
if (serversCount >= MINIMUM_EXPECTED_SERVER_COUNT) {
localSandboxRunningDoThis();
return;
}
}
}
localSandboxNotRunningDoThat();
});
}

View file

@ -25,7 +25,6 @@
const QString HIFI_URL_SCHEME = "hifi";
const QString DEFAULT_HIFI_ADDRESS = "hifi://welcome";
const QString SANDBOX_HIFI_ADDRESS = "hifi://localhost";
const QString SANDBOX_STATUS_URL = "http://localhost:60332/status";
const QString INDEX_PATH = "/";
const QString GET_PLACE = "/api/v1/places/%1";
@ -78,11 +77,6 @@ public:
const QStack<QUrl>& getBackStack() const { return _backStack; }
const QStack<QUrl>& getForwardStack() const { return _forwardStack; }
/// determines if the local sandbox is likely running. It does not account for custom setups, and is only
/// intended to detect the standard local sandbox install.
void ifLocalSandboxRunningElse(std::function<void()> localSandboxRunningDoThis,
std::function<void()> localSandboxNotRunningDoThat);
public slots:
void handleLookupString(const QString& lookupString, bool fromSuggestions = false);

View file

@ -45,6 +45,8 @@ AssetClient::AssetClient() {
packetReceiver.registerListener(PacketType::AssetUploadReply, this, "handleAssetUploadReply");
connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &AssetClient::handleNodeKilled);
connect(nodeList.data(), &LimitedNodeList::clientConnectionToNodeReset,
this, &AssetClient::handleNodeClientConnectionReset);
}
void AssetClient::init() {
@ -233,15 +235,15 @@ MessageID AssetClient::getAsset(const QString& hash, DataOffset start, DataOffse
packet->writePrimitive(start);
packet->writePrimitive(end);
nodeList->sendPacket(std::move(packet), *assetServer);
if (nodeList->sendPacket(std::move(packet), *assetServer) != -1) {
_pendingRequests[assetServer][messageID] = { QSharedPointer<ReceivedMessage>(), callback, progressCallback };
_pendingRequests[assetServer][messageID] = { QSharedPointer<ReceivedMessage>(), callback, progressCallback };
return messageID;
} else {
callback(false, AssetServerError::NoError, QByteArray());
return INVALID_MESSAGE_ID;
return messageID;
}
}
callback(false, AssetServerError::NoError, QByteArray());
return INVALID_MESSAGE_ID;
}
MessageID AssetClient::getAssetInfo(const QString& hash, GetInfoCallback callback) {
@ -259,15 +261,15 @@ MessageID AssetClient::getAssetInfo(const QString& hash, GetInfoCallback callbac
packet->writePrimitive(messageID);
packet->write(QByteArray::fromHex(hash.toLatin1()));
nodeList->sendPacket(std::move(packet), *assetServer);
if (nodeList->sendPacket(std::move(packet), *assetServer) != -1) {
_pendingInfoRequests[assetServer][messageID] = callback;
_pendingInfoRequests[assetServer][messageID] = callback;
return messageID;
} else {
callback(false, AssetServerError::NoError, { "", 0 });
return INVALID_MESSAGE_ID;
return messageID;
}
}
callback(false, AssetServerError::NoError, { "", 0 });
return INVALID_MESSAGE_ID;
}
void AssetClient::handleAssetGetInfoReply(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
@ -453,15 +455,15 @@ MessageID AssetClient::getAssetMapping(const AssetPath& path, MappingOperationCa
packetList->writeString(path);
nodeList->sendPacketList(std::move(packetList), *assetServer);
if (nodeList->sendPacketList(std::move(packetList), *assetServer) != -1) {
_pendingMappingRequests[assetServer][messageID] = callback;
_pendingMappingRequests[assetServer][messageID] = callback;
return messageID;
} else {
callback(false, AssetServerError::NoError, QSharedPointer<ReceivedMessage>());
return INVALID_MESSAGE_ID;
return messageID;
}
}
callback(false, AssetServerError::NoError, QSharedPointer<ReceivedMessage>());
return INVALID_MESSAGE_ID;
}
MessageID AssetClient::getAllAssetMappings(MappingOperationCallback callback) {
@ -478,15 +480,15 @@ MessageID AssetClient::getAllAssetMappings(MappingOperationCallback callback) {
packetList->writePrimitive(AssetMappingOperationType::GetAll);
nodeList->sendPacketList(std::move(packetList), *assetServer);
if (nodeList->sendPacketList(std::move(packetList), *assetServer) != -1) {
_pendingMappingRequests[assetServer][messageID] = callback;
_pendingMappingRequests[assetServer][messageID] = callback;
return messageID;
} else {
callback(false, AssetServerError::NoError, QSharedPointer<ReceivedMessage>());
return INVALID_MESSAGE_ID;
return messageID;
}
}
callback(false, AssetServerError::NoError, QSharedPointer<ReceivedMessage>());
return INVALID_MESSAGE_ID;
}
MessageID AssetClient::deleteAssetMappings(const AssetPathList& paths, MappingOperationCallback callback) {
@ -507,15 +509,15 @@ MessageID AssetClient::deleteAssetMappings(const AssetPathList& paths, MappingOp
packetList->writeString(path);
}
nodeList->sendPacketList(std::move(packetList), *assetServer);
if (nodeList->sendPacketList(std::move(packetList), *assetServer) != -1) {
_pendingMappingRequests[assetServer][messageID] = callback;
_pendingMappingRequests[assetServer][messageID] = callback;
return messageID;
} else {
callback(false, AssetServerError::NoError, QSharedPointer<ReceivedMessage>());
return INVALID_MESSAGE_ID;
return messageID;
}
}
callback(false, AssetServerError::NoError, QSharedPointer<ReceivedMessage>());
return INVALID_MESSAGE_ID;
}
MessageID AssetClient::setAssetMapping(const QString& path, const AssetHash& hash, MappingOperationCallback callback) {
@ -535,15 +537,15 @@ MessageID AssetClient::setAssetMapping(const QString& path, const AssetHash& has
packetList->writeString(path);
packetList->write(QByteArray::fromHex(hash.toUtf8()));
nodeList->sendPacketList(std::move(packetList), *assetServer);
if (nodeList->sendPacketList(std::move(packetList), *assetServer) != -1) {
_pendingMappingRequests[assetServer][messageID] = callback;
_pendingMappingRequests[assetServer][messageID] = callback;
return messageID;
} else {
callback(false, AssetServerError::NoError, QSharedPointer<ReceivedMessage>());
return INVALID_MESSAGE_ID;
return messageID;
}
}
callback(false, AssetServerError::NoError, QSharedPointer<ReceivedMessage>());
return INVALID_MESSAGE_ID;
}
MessageID AssetClient::renameAssetMapping(const AssetPath& oldPath, const AssetPath& newPath, MappingOperationCallback callback) {
@ -561,16 +563,16 @@ MessageID AssetClient::renameAssetMapping(const AssetPath& oldPath, const AssetP
packetList->writeString(oldPath);
packetList->writeString(newPath);
nodeList->sendPacketList(std::move(packetList), *assetServer);
if (nodeList->sendPacketList(std::move(packetList), *assetServer) != -1) {
_pendingMappingRequests[assetServer][messageID] = callback;
_pendingMappingRequests[assetServer][messageID] = callback;
return messageID;
return messageID;
} else {
callback(false, AssetServerError::NoError, QSharedPointer<ReceivedMessage>());
return INVALID_MESSAGE_ID;
}
}
callback(false, AssetServerError::NoError, QSharedPointer<ReceivedMessage>());
return INVALID_MESSAGE_ID;
}
bool AssetClient::cancelMappingRequest(MessageID id) {
@ -646,15 +648,15 @@ MessageID AssetClient::uploadAsset(const QByteArray& data, UploadResultCallback
packetList->writePrimitive(size);
packetList->write(data.constData(), size);
nodeList->sendPacketList(std::move(packetList), *assetServer);
if (nodeList->sendPacketList(std::move(packetList), *assetServer) != -1) {
_pendingUploads[assetServer][messageID] = callback;
_pendingUploads[assetServer][messageID] = callback;
return messageID;
} else {
callback(false, AssetServerError::NoError, QString());
return INVALID_MESSAGE_ID;
return messageID;
}
}
callback(false, AssetServerError::NoError, QString());
return INVALID_MESSAGE_ID;
}
void AssetClient::handleAssetUploadReply(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
@ -704,6 +706,34 @@ void AssetClient::handleNodeKilled(SharedNodePointer node) {
return;
}
forceFailureOfPendingRequests(node);
{
auto messageMapIt = _pendingUploads.find(node);
if (messageMapIt != _pendingUploads.end()) {
for (const auto& value : messageMapIt->second) {
value.second(false, AssetServerError::NoError, "");
}
messageMapIt->second.clear();
}
}
}
void AssetClient::handleNodeClientConnectionReset(SharedNodePointer node) {
// a client connection to a Node was reset
// if it was an AssetServer we need to cause anything pending to fail so it is re-attempted
if (node->getType() != NodeType::AssetServer) {
return;
}
qCDebug(asset_client) << "AssetClient detected client connection reset handshake with Asset Server - failing any pending requests";
forceFailureOfPendingRequests(node);
}
void AssetClient::forceFailureOfPendingRequests(SharedNodePointer node) {
{
auto messageMapIt = _pendingRequests.find(node);
if (messageMapIt != _pendingRequests.end()) {
@ -731,16 +761,6 @@ void AssetClient::handleNodeKilled(SharedNodePointer node) {
}
}
{
auto messageMapIt = _pendingUploads.find(node);
if (messageMapIt != _pendingUploads.end()) {
for (const auto& value : messageMapIt->second) {
value.second(false, AssetServerError::NoError, "");
}
messageMapIt->second.clear();
}
}
{
auto messageMapIt = _pendingMappingRequests.find(node);
if (messageMapIt != _pendingMappingRequests.end()) {

View file

@ -75,6 +75,7 @@ private slots:
void handleAssetUploadReply(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleNodeKilled(SharedNodePointer node);
void handleNodeClientConnectionReset(SharedNodePointer node);
private:
MessageID getAssetMapping(const AssetHash& hash, MappingOperationCallback callback);
@ -96,6 +97,8 @@ private:
void handleProgressCallback(const QWeakPointer<Node>& node, MessageID messageID, qint64 size, DataOffset length);
void handleCompleteCallback(const QWeakPointer<Node>& node, MessageID messageID);
void forceFailureOfPendingRequests(SharedNodePointer node);
struct GetAssetRequestData {
QSharedPointer<ReceivedMessage> message;
ReceivedAssetCallback completeCallback;

View file

@ -32,6 +32,7 @@
#include <UUID.h>
#include "AccountManager.h"
#include "AssetClient.h"
#include "Assignment.h"
#include "HifiSockAddr.h"
#include "NetworkLogging.h"
@ -41,7 +42,8 @@ static Setting::Handle<quint16> LIMITED_NODELIST_LOCAL_PORT("LimitedNodeList.Loc
const std::set<NodeType_t> SOLO_NODE_TYPES = {
NodeType::AvatarMixer,
NodeType::AudioMixer
NodeType::AudioMixer,
NodeType::AssetServer
};
LimitedNodeList::LimitedNodeList(int socketListenPort, int dtlsListenPort) :
@ -113,6 +115,12 @@ LimitedNodeList::LimitedNodeList(int socketListenPort, int dtlsListenPort) :
using std::placeholders::_1;
_nodeSocket.setPacketFilterOperator(std::bind(&LimitedNodeList::isPacketVerified, this, _1));
// set our socketBelongsToNode method as the connection creation filter operator for the udt::Socket
_nodeSocket.setConnectionCreationFilterOperator(std::bind(&LimitedNodeList::sockAddrBelongsToNode, this, _1));
// handle when a socket connection has its receiver side reset - might need to emit clientConnectionToNodeReset
connect(&_nodeSocket, &udt::Socket::clientHandshakeRequestComplete, this, &LimitedNodeList::clientConnectionToSockAddrReset);
_packetStatTimer.start();
if (_stunSockAddr.getAddress().isNull()) {
@ -317,6 +325,8 @@ void LimitedNodeList::fillPacketHeader(const NLPacket& packet, const QUuid& conn
}
}
static const qint64 ERROR_SENDING_PACKET_BYTES = -1;
qint64 LimitedNodeList::sendUnreliablePacket(const NLPacket& packet, const Node& destinationNode) {
Q_ASSERT(!packet.isPartOfMessage());
@ -353,7 +363,7 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const Node&
return sendPacket(std::move(packet), *activeSocket, destinationNode.getConnectionSecret());
} else {
qCDebug(networking) << "LimitedNodeList::sendPacket called without active socket for node" << destinationNode << "- not sending";
return 0;
return ERROR_SENDING_PACKET_BYTES;
}
}
@ -392,7 +402,7 @@ qint64 LimitedNodeList::sendPacketList(NLPacketList& packetList, const Node& des
} else {
qCDebug(networking) << "LimitedNodeList::sendPacketList called without active socket for node" << destinationNode
<< " - not sending.";
return 0;
return ERROR_SENDING_PACKET_BYTES;
}
}
@ -438,7 +448,7 @@ qint64 LimitedNodeList::sendPacketList(std::unique_ptr<NLPacketList> packetList,
return _nodeSocket.writePacketList(std::move(packetList), *activeSocket);
} else {
qCDebug(networking) << "LimitedNodeList::sendPacketList called without active socket for node. Not sending.";
return 0;
return ERROR_SENDING_PACKET_BYTES;
}
}
@ -446,7 +456,7 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const Node&
const HifiSockAddr& overridenSockAddr) {
if (overridenSockAddr.isNull() && !destinationNode.getActiveSocket()) {
qCDebug(networking) << "LimitedNodeList::sendPacket called without active socket for node. Not sending.";
return 0;
return ERROR_SENDING_PACKET_BYTES;
}
// use the node's active socket as the destination socket if there is no overriden socket address
@ -1136,3 +1146,23 @@ void LimitedNodeList::flagTimeForConnectionStep(ConnectionStep connectionStep, q
}
}
}
void LimitedNodeList::clientConnectionToSockAddrReset(const HifiSockAddr& sockAddr) {
// for certain reliable channels higher level classes may need to know if the udt::Connection has been reset
auto matchingNode = findNodeWithAddr(sockAddr);
if (matchingNode) {
emit clientConnectionToNodeReset(matchingNode);
}
}
#if (PR_BUILD || DEV_BUILD)
void LimitedNodeList::sendFakedHandshakeRequestToNode(SharedNodePointer node) {
if (node && node->getActiveSocket()) {
_nodeSocket.sendFakedHandshakeRequest(*node->getActiveSocket());
}
}
#endif

View file

@ -41,6 +41,7 @@
#include "NLPacketList.h"
#include "PacketReceiver.h"
#include "ReceivedMessage.h"
#include "udt/ControlPacket.h"
#include "udt/PacketHeaders.h"
#include "udt/Socket.h"
#include "UUIDHasher.h"
@ -235,6 +236,9 @@ public:
static void makeSTUNRequestPacket(char* stunRequestPacket);
#if (PR_BUILD || DEV_BUILD)
void sendFakedHandshakeRequestToNode(SharedNodePointer node);
#endif
public slots:
void reset();
@ -262,6 +266,8 @@ signals:
void nodeKilled(SharedNodePointer);
void nodeActivated(SharedNodePointer);
void clientConnectionToNodeReset(SharedNodePointer);
void localSockAddrChanged(const HifiSockAddr& localSockAddr);
void publicSockAddrChanged(const HifiSockAddr& publicSockAddr);
@ -274,6 +280,8 @@ signals:
protected slots:
void connectedForLocalSocketTest();
void errorTestingLocalSocket();
void clientConnectionToSockAddrReset(const HifiSockAddr& sockAddr);
protected:
LimitedNodeList(int socketListenPort = INVALID_PORT, int dtlsListenPort = INVALID_PORT);
@ -299,6 +307,8 @@ protected:
void sendPacketToIceServer(PacketType packetType, const HifiSockAddr& iceServerSockAddr, const QUuid& clientID,
const QUuid& peerRequestID = QUuid());
bool sockAddrBelongsToNode(const HifiSockAddr& sockAddr) { return findNodeWithAddr(sockAddr) != SharedNodePointer(); }
QUuid _sessionUUID;
NodeHash _nodeHash;
mutable QReadWriteLock _nodeMutex;

View file

@ -104,6 +104,10 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort)
connect(&_domainHandler, SIGNAL(connectedToDomain(QString)), &_keepAlivePingTimer, SLOT(start()));
connect(&_domainHandler, &DomainHandler::disconnectedFromDomain, &_keepAlivePingTimer, &QTimer::stop);
// 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));
// we definitely want STUN to update our public socket, so call the LNL to kick that off
startSTUNPublicSocketUpdate();
@ -703,6 +707,10 @@ void NodeList::sendKeepAlivePings() {
});
}
bool NodeList::sockAddrBelongsToDomainOrNode(const HifiSockAddr& sockAddr) {
return _domainHandler.getSockAddr() == sockAddr || LimitedNodeList::sockAddrBelongsToNode(sockAddr);
}
void NodeList::ignoreNodeBySessionID(const QUuid& nodeID) {
// enumerate the nodes to send a reliable ignore packet to each that can leverage it

View file

@ -132,6 +132,8 @@ private:
void pingPunchForInactiveNode(const SharedNodePointer& node);
bool sockAddrBelongsToDomainOrNode(const HifiSockAddr& sockAddr);
NodeType_t _ownerType;
NodeSet _nodeTypesOfInterest;
DomainHandler _domainHandler;

View file

@ -0,0 +1,96 @@
//
// SandboxUtils.cpp
// libraries/networking/src
//
// Created by Brad Hefta-Gaub on 2016-10-15.
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QDataStream>
#include <QDebug>
#include <QFile>
#include <QFileInfo>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkReply>
#include <QProcess>
#include <QStandardPaths>
#include <QThread>
#include <QTimer>
#include <NumericalConstants.h>
#include <SharedUtil.h>
#include <RunningMarker.h>
#include "SandboxUtils.h"
#include "NetworkAccessManager.h"
void SandboxUtils::ifLocalSandboxRunningElse(std::function<void()> localSandboxRunningDoThis,
std::function<void()> localSandboxNotRunningDoThat) {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest sandboxStatus(SANDBOX_STATUS_URL);
sandboxStatus.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
sandboxStatus.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
QNetworkReply* reply = networkAccessManager.get(sandboxStatus);
connect(reply, &QNetworkReply::finished, this, [reply, localSandboxRunningDoThis, localSandboxNotRunningDoThat]() {
if (reply->error() == QNetworkReply::NoError) {
auto statusData = reply->readAll();
auto statusJson = QJsonDocument::fromJson(statusData);
if (!statusJson.isEmpty()) {
auto statusObject = statusJson.object();
auto serversValue = statusObject.value("servers");
if (!serversValue.isUndefined() && serversValue.isObject()) {
auto serversObject = serversValue.toObject();
auto serversCount = serversObject.size();
const int MINIMUM_EXPECTED_SERVER_COUNT = 5;
if (serversCount >= MINIMUM_EXPECTED_SERVER_COUNT) {
localSandboxRunningDoThis();
return;
}
}
}
}
localSandboxNotRunningDoThat();
});
}
void SandboxUtils::runLocalSandbox(QString contentPath, bool autoShutdown, QString runningMarkerName) {
QString applicationDirPath = QFileInfo(QCoreApplication::applicationFilePath()).path();
QString serverPath = applicationDirPath + "/server-console/server-console.exe";
qDebug() << "Application dir path is: " << applicationDirPath;
qDebug() << "Server path is: " << serverPath;
qDebug() << "autoShutdown: " << autoShutdown;
bool hasContentPath = !contentPath.isEmpty();
bool passArgs = autoShutdown || hasContentPath;
QStringList args;
if (passArgs) {
args << "--";
}
if (hasContentPath) {
QString serverContentPath = applicationDirPath + "/" + contentPath;
args << "--contentPath" << serverContentPath;
}
if (autoShutdown) {
QString interfaceRunningStateFile = RunningMarker::getMarkerFilePath(runningMarkerName);
args << "--shutdownWatcher" << interfaceRunningStateFile;
}
qDebug() << applicationDirPath;
qDebug() << "Launching sandbox with:" << args;
qDebug() << QProcess::startDetached(serverPath, args);
// Sleep a short amount of time to give the server a chance to start
usleep(2000000); /// do we really need this??
}

View file

@ -0,0 +1,32 @@
//
// SandboxUtils.h
// libraries/networking/src
//
// Created by Brad Hefta-Gaub on 2016-10-15.
// Copyright 2016 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
//
#ifndef hifi_SandboxUtils_h
#define hifi_SandboxUtils_h
#include <functional>
#include <QtCore/QObject>
const QString SANDBOX_STATUS_URL = "http://localhost:60332/status";
class SandboxUtils : public QObject {
Q_OBJECT
public:
/// determines if the local sandbox is likely running. It does not account for custom setups, and is only
/// intended to detect the standard local sandbox install.
void ifLocalSandboxRunningElse(std::function<void()> localSandboxRunningDoThis,
std::function<void()> localSandboxNotRunningDoThat);
static void runLocalSandbox(QString contentPath, bool autoShutdown, QString runningMarkerName);
};
#endif // hifi_SandboxUtils_h

View file

@ -426,18 +426,25 @@ SequenceNumber Connection::nextACK() const {
}
}
void Connection::sendHandshakeRequest() {
auto handshakeRequestPacket = ControlPacket::create(ControlPacket::HandshakeRequest, 0);
_parentSocket->writeBasePacket(*handshakeRequestPacket, _destination);
_didRequestHandshake = true;
}
bool Connection::processReceivedSequenceNumber(SequenceNumber sequenceNumber, int packetSize, int payloadSize) {
if (!_hasReceivedHandshake) {
// Refuse to process any packets until we've received the handshake
// Send handshake request to re-request a handshake
auto handshakeRequestPacket = ControlPacket::create(ControlPacket::HandshakeRequest, 0);
_parentSocket->writeBasePacket(*handshakeRequestPacket, _destination);
#ifdef UDT_CONNECTION_DEBUG
qCDebug(networking) << "Received packet before receiving handshake, sending HandshakeRequest";
#endif
sendHandshakeRequest();
return false;
}
@ -789,6 +796,11 @@ void Connection::processHandshake(ControlPacketPointer controlPacket) {
// indicate that handshake has been received
_hasReceivedHandshake = true;
if (_didRequestHandshake) {
emit receiverHandshakeRequestComplete(_destination);
_didRequestHandshake = false;
}
}
void Connection::processHandshakeACK(ControlPacketPointer controlPacket) {

View file

@ -79,10 +79,13 @@ public:
void setMaxBandwidth(int maxBandwidth);
void sendHandshakeRequest();
signals:
void packetSent();
void connectionInactive(const HifiSockAddr& sockAddr);
void receiverHandshakeRequestComplete(const HifiSockAddr& sockAddr);
private slots:
void recordSentPackets(int payload, int total);
void recordRetransmission();
@ -129,6 +132,7 @@ private:
bool _hasReceivedHandshake { false }; // flag for receipt of handshake from server
bool _hasReceivedHandshakeACK { false }; // flag for receipt of handshake ACK from client
bool _didRequestHandshake { false }; // flag for request of handshake from server
p_high_resolution_clock::time_point _connectionStart = p_high_resolution_clock::now(); // holds the time_point for creation of this connection
p_high_resolution_clock::time_point _lastReceiveTime; // holds the last time we received anything from sender

View file

@ -97,6 +97,9 @@ SendQueue::SendQueue(Socket* socket, HifiSockAddr dest) :
_currentSequenceNumber = _initialSequenceNumber - 1;
_atomicCurrentSequenceNumber = uint32_t(_currentSequenceNumber);
_lastACKSequenceNumber = uint32_t(_currentSequenceNumber) - 1;
// default the last receiver response to the current time
_lastReceiverResponse = QDateTime::currentMSecsSinceEpoch();
}
void SendQueue::queuePacket(std::unique_ptr<Packet> packet) {
@ -166,7 +169,7 @@ void SendQueue::ack(SequenceNumber ack) {
void SendQueue::nak(SequenceNumber start, SequenceNumber end) {
// this is a response from the client, re-set our timeout expiry
_lastReceiverResponse = QDateTime::currentMSecsSinceEpoch();
_lastReceiverResponse = QDateTime::currentMSecsSinceEpoch();
{
std::lock_guard<std::mutex> nakLocker(_naksLock);
@ -520,7 +523,6 @@ bool SendQueue::isInactive(bool attemptedToSendPacket) {
if (sinceLastResponse > 0 &&
sinceLastResponse >= int64_t(NUM_TIMEOUTS_BEFORE_INACTIVE * (_estimatedTimeout / USECS_PER_MSEC)) &&
_lastReceiverResponse > 0 &&
sinceLastResponse > MIN_MS_BEFORE_INACTIVE) {
// If the flow window has been full for over CONSIDER_INACTIVE_AFTER,
// then signal the queue is inactive and return so it can be cleaned up

View file

@ -171,11 +171,28 @@ qint64 Socket::writePacketList(std::unique_ptr<PacketList> packetList, const Hif
}
void Socket::writeReliablePacket(Packet* packet, const HifiSockAddr& sockAddr) {
findOrCreateConnection(sockAddr).sendReliablePacket(std::unique_ptr<Packet>(packet));
auto connection = findOrCreateConnection(sockAddr);
if (connection) {
connection->sendReliablePacket(std::unique_ptr<Packet>(packet));
}
#ifdef UDT_CONNECTION_DEBUG
else {
qCDebug(networking) << "Socket::writeReliablePacket refusing to send packet - no connection was created";
}
#endif
}
void Socket::writeReliablePacketList(PacketList* packetList, const HifiSockAddr& sockAddr) {
findOrCreateConnection(sockAddr).sendReliablePacketList(std::unique_ptr<PacketList>(packetList));
auto connection = findOrCreateConnection(sockAddr);
if (connection) {
connection->sendReliablePacketList(std::unique_ptr<PacketList>(packetList));
}
#ifdef UDT_CONNECTION_DEBUG
else {
qCDebug(networking) << "Socket::writeReliablePacketList refusing to send packet list - no connection was created";
}
#endif
}
qint64 Socket::writeDatagram(const char* data, qint64 size, const HifiSockAddr& sockAddr) {
@ -198,25 +215,40 @@ qint64 Socket::writeDatagram(const QByteArray& datagram, const HifiSockAddr& soc
return bytesWritten;
}
Connection& Socket::findOrCreateConnection(const HifiSockAddr& sockAddr) {
Connection* Socket::findOrCreateConnection(const HifiSockAddr& sockAddr) {
auto it = _connectionsHash.find(sockAddr);
if (it == _connectionsHash.end()) {
auto congestionControl = _ccFactory->create();
congestionControl->setMaxBandwidth(_maxBandwidth);
auto connection = std::unique_ptr<Connection>(new Connection(this, sockAddr, std::move(congestionControl)));
// we did not have a matching connection, time to see if we should make one
// we queue the connection to cleanup connection in case it asks for it during its own rate control sync
QObject::connect(connection.get(), &Connection::connectionInactive, this, &Socket::cleanupConnection);
if (_connectionCreationFilterOperator && !_connectionCreationFilterOperator(sockAddr)) {
// the connection creation filter did not allow us to create a new connection
#ifdef UDT_CONNECTION_DEBUG
qCDebug(networking) << "Socket::findOrCreateConnection refusing to create connection for" << sockAddr
<< "due to connection creation filter";
#endif
return nullptr;
} else {
auto congestionControl = _ccFactory->create();
congestionControl->setMaxBandwidth(_maxBandwidth);
auto connection = std::unique_ptr<Connection>(new Connection(this, sockAddr, std::move(congestionControl)));
// we queue the connection to cleanup connection in case it asks for it during its own rate control sync
QObject::connect(connection.get(), &Connection::connectionInactive, this, &Socket::cleanupConnection);
// allow higher-level classes to find out when connections have completed a handshake
QObject::connect(connection.get(), &Connection::receiverHandshakeRequestComplete,
this, &Socket::clientHandshakeRequestComplete);
#ifdef UDT_CONNECTION_DEBUG
qCDebug(networking) << "Creating new connection to" << sockAddr;
qCDebug(networking) << "Creating new connection to" << sockAddr;
#endif
it = _connectionsHash.insert(it, std::make_pair(sockAddr, std::move(connection)));
it = _connectionsHash.insert(it, std::make_pair(sockAddr, std::move(connection)));
}
}
return *it->second;
return it->second.get();
}
void Socket::clearConnections() {
@ -292,9 +324,12 @@ void Socket::readPendingDatagrams() {
// setup a control packet from the data we just read
auto controlPacket = ControlPacket::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, senderSockAddr);
// move this control packet to the matching connection
auto& connection = findOrCreateConnection(senderSockAddr);
connection.processControl(move(controlPacket));
// move this control packet to the matching connection, if there is one
auto connection = findOrCreateConnection(senderSockAddr);
if (connection) {
connection->processControl(move(controlPacket));
}
} else {
// setup a Packet from the data we just read
@ -304,19 +339,21 @@ void Socket::readPendingDatagrams() {
if (!_packetFilterOperator || _packetFilterOperator(*packet)) {
if (packet->isReliable()) {
// if this was a reliable packet then signal the matching connection with the sequence number
auto& connection = findOrCreateConnection(senderSockAddr);
auto connection = findOrCreateConnection(senderSockAddr);
if (!connection.processReceivedSequenceNumber(packet->getSequenceNumber(),
packet->getDataSize(),
packet->getPayloadSize())) {
// the connection indicated that we should not continue processing this packet
if (!connection || !connection->processReceivedSequenceNumber(packet->getSequenceNumber(),
packet->getDataSize(),
packet->getPayloadSize())) {
// the connection could not be created or indicated that we should not continue processing this packet
continue;
}
}
if (packet->isPartOfMessage()) {
auto& connection = findOrCreateConnection(senderSockAddr);
connection.queueReceivedMessagePacket(std::move(packet));
auto connection = findOrCreateConnection(senderSockAddr);
if (connection) {
connection->queueReceivedMessagePacket(std::move(packet));
}
} else if (_packetHandler) {
// call the verified packet callback to let it handle this packet
_packetHandler(std::move(packet));
@ -427,3 +464,14 @@ void Socket::handleStateChanged(QAbstractSocket::SocketState socketState) {
qCWarning(networking) << "udt::Socket state changed - state is now" << socketState;
}
}
#if (PR_BUILD || DEV_BUILD)
void Socket::sendFakedHandshakeRequest(const HifiSockAddr& sockAddr) {
auto connection = findOrCreateConnection(sockAddr);
if (connection) {
connection->sendHandshakeRequest();
}
}
#endif

View file

@ -37,6 +37,7 @@ class PacketList;
class SequenceNumber;
using PacketFilterOperator = std::function<bool(const Packet&)>;
using ConnectionCreationFilterOperator = std::function<bool(const HifiSockAddr&)>;
using BasePacketHandler = std::function<void(std::unique_ptr<BasePacket>)>;
using PacketHandler = std::function<void(std::unique_ptr<Packet>)>;
@ -68,6 +69,8 @@ public:
void setPacketHandler(PacketHandler handler) { _packetHandler = handler; }
void setMessageHandler(MessageHandler handler) { _messageHandler = handler; }
void setMessageFailureHandler(MessageFailureHandler handler) { _messageFailureHandler = handler; }
void setConnectionCreationFilterOperator(ConnectionCreationFilterOperator filterOperator)
{ _connectionCreationFilterOperator = filterOperator; }
void addUnfilteredHandler(const HifiSockAddr& senderSockAddr, BasePacketHandler handler)
{ _unfilteredHandlers[senderSockAddr] = handler; }
@ -80,6 +83,13 @@ public:
StatsVector sampleStatsForAllConnections();
#if (PR_BUILD || DEV_BUILD)
void sendFakedHandshakeRequest(const HifiSockAddr& sockAddr);
#endif
signals:
void clientHandshakeRequestComplete(const HifiSockAddr& sockAddr);
public slots:
void cleanupConnection(HifiSockAddr sockAddr);
void clearConnections();
@ -93,7 +103,8 @@ private slots:
private:
void setSystemBufferSizes();
Connection& findOrCreateConnection(const HifiSockAddr& sockAddr);
Connection* findOrCreateConnection(const HifiSockAddr& sockAddr);
bool socketMatchesNodeOrDomain(const HifiSockAddr& sockAddr);
// privatized methods used by UDTTest - they are private since they must be called on the Socket thread
ConnectionStats::Stats sampleStatsForConnection(const HifiSockAddr& destination);
@ -109,6 +120,7 @@ private:
PacketHandler _packetHandler;
MessageHandler _messageHandler;
MessageFailureHandler _messageFailureHandler;
ConnectionCreationFilterOperator _connectionCreationFilterOperator;
std::unordered_map<HifiSockAddr, BasePacketHandler> _unfilteredHandlers;
std::unordered_map<HifiSockAddr, SequenceNumber> _unreliableSequenceNumbers;

View file

@ -17,6 +17,7 @@
#include <AssetUpload.h>
#include <MappingRequest.h>
#include <NetworkLogging.h>
#include <NodeList.h>
AssetScriptingInterface::AssetScriptingInterface(QScriptEngine* engine) :
_engine(engine)
@ -86,3 +87,13 @@ void AssetScriptingInterface::downloadData(QString urlString, QScriptValue callb
assetRequest->start();
}
#if (PR_BUILD || DEV_BUILD)
void AssetScriptingInterface::sendFakedHandshake() {
auto nodeList = DependencyManager::get<NodeList>();
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
nodeList->sendFakedHandshakeRequestToNode(assetServer);
}
#endif

View file

@ -28,6 +28,10 @@ public:
Q_INVOKABLE void downloadData(QString url, QScriptValue downloadComplete);
Q_INVOKABLE void setMapping(QString path, QString hash, QScriptValue callback);
#if (PR_BUILD || DEV_BUILD)
Q_INVOKABLE void sendFakedHandshake();
#endif
protected:
QSet<AssetRequest*> _pendingRequests;
QScriptEngine* _engine;

View file

@ -0,0 +1,76 @@
//
// RunningMarker.cpp
// libraries/shared/src
//
// Created by Brad Hefta-Gaub on 2016-10-16
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "RunningMarker.h"
#include <QFile>
#include <QStandardPaths>
#include <QThread>
#include <QTimer>
#include "NumericalConstants.h"
#include "PathUtils.h"
RunningMarker::RunningMarker(QObject* parent, QString name) :
_parent(parent),
_name(name)
{
}
void RunningMarker::startRunningMarker() {
static const int RUNNING_STATE_CHECK_IN_MSECS = MSECS_PER_SECOND;
// start the nodeThread so its event loop is running
QThread* runningMarkerThread = new QThread(_parent);
runningMarkerThread->setObjectName("Running Marker Thread");
runningMarkerThread->start();
writeRunningMarkerFiler(); // write the first file, even before timer
QTimer* runningMarkerTimer = new QTimer(_parent);
QObject::connect(runningMarkerTimer, &QTimer::timeout, [=](){
writeRunningMarkerFiler();
});
runningMarkerTimer->start(RUNNING_STATE_CHECK_IN_MSECS);
// put the time on the thread
runningMarkerTimer->moveToThread(runningMarkerThread);
}
RunningMarker::~RunningMarker() {
deleteRunningMarkerFile();
}
void RunningMarker::writeRunningMarkerFiler() {
QFile runningMarkerFile(getFilePath());
// always write, even it it exists, so that it touches the files
if (runningMarkerFile.open(QIODevice::WriteOnly)) {
runningMarkerFile.close();
}
}
void RunningMarker::deleteRunningMarkerFile() {
QFile runningMarkerFile(getFilePath());
if (runningMarkerFile.exists()) {
runningMarkerFile.remove();
}
}
QString RunningMarker::getFilePath() {
return QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + _name;
}
QString RunningMarker::getMarkerFilePath(QString name) {
return QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + name;
}

View file

@ -0,0 +1,35 @@
//
// RunningMarker.h
// interface/src
//
// Created by Brad Hefta-Gaub on 2016-10-15.
// Copyright 2016 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
//
#ifndef hifi_RunningMarker_h
#define hifi_RunningMarker_h
#include <QObject>
#include <QString>
class RunningMarker {
public:
RunningMarker(QObject* parent, QString name);
~RunningMarker();
void startRunningMarker();
QString getFilePath();
static QString getMarkerFilePath(QString name);
protected:
void writeRunningMarkerFiler();
void deleteRunningMarkerFile();
QObject* _parent { nullptr };
QString _name;
};
#endif // hifi_RunningMarker_h

View file

@ -616,6 +616,28 @@ QString OffscreenUi::fileSaveDialog(const QString& caption, const QString& dir,
return fileDialog(map);
}
QString OffscreenUi::existingDirectoryDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) {
if (QThread::currentThread() != thread()) {
QString result;
QMetaObject::invokeMethod(this, "existingDirectoryDialog", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QString, result),
Q_ARG(QString, caption),
Q_ARG(QString, dir),
Q_ARG(QString, filter),
Q_ARG(QString*, selectedFilter),
Q_ARG(QFileDialog::Options, options));
return result;
}
QVariantMap map;
map.insert("caption", caption);
map.insert("dir", QUrl::fromLocalFile(dir));
map.insert("filter", filter);
map.insert("options", static_cast<int>(options));
map.insert("selectDirectory", true);
return fileDialog(map);
}
QString OffscreenUi::getOpenFileName(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) {
return DependencyManager::get<OffscreenUi>()->fileOpenDialog(caption, dir, filter, selectedFilter, options);
}
@ -624,6 +646,10 @@ QString OffscreenUi::getSaveFileName(void* ignored, const QString &caption, cons
return DependencyManager::get<OffscreenUi>()->fileSaveDialog(caption, dir, filter, selectedFilter, options);
}
QString OffscreenUi::getExistingDirectory(void* ignored, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) {
return DependencyManager::get<OffscreenUi>()->existingDirectoryDialog(caption, dir, filter, selectedFilter, options);
}
bool OffscreenUi::eventFilter(QObject* originalDestination, QEvent* event) {
if (!filterEnabled(originalDestination, event)) {
return false;

View file

@ -115,11 +115,14 @@ public:
Q_INVOKABLE QString fileOpenDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
Q_INVOKABLE QString fileSaveDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
Q_INVOKABLE QString existingDirectoryDialog(const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
// Compatibility with QFileDialog::getOpenFileName
static QString getOpenFileName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
// Compatibility with QFileDialog::getSaveFileName
static QString getSaveFileName(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
// Compatibility with QFileDialog::getExistingDirectory
static QString getExistingDirectory(void* ignored, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0);
Q_INVOKABLE QVariant inputDialog(const Icon icon, const QString& title, const QString& label = QString(), const QVariant& current = QVariant());
Q_INVOKABLE QVariant customInputDialog(const Icon icon, const QString& title, const QVariantMap& config);

View file

@ -59,13 +59,15 @@ void OculusDisplayPlugin::customizeContext() {
ovrResult result = ovr_CreateTextureSwapChainGL(_session, &desc, &_textureSwapChain);
if (!OVR_SUCCESS(result)) {
logFatal("Failed to create swap textures");
logCritical("Failed to create swap textures");
return;
}
int length = 0;
result = ovr_GetTextureSwapChainLength(_session, _textureSwapChain, &length);
if (!OVR_SUCCESS(result) || !length) {
qFatal("Unable to count swap chain textures");
logCritical("Unable to count swap chain textures");
return;
}
for (int i = 0; i < length; ++i) {
GLuint chainTexId;
@ -83,6 +85,7 @@ void OculusDisplayPlugin::customizeContext() {
_sceneLayer.ColorTexture[0] = _textureSwapChain;
// not needed since the structure was zeroed on init, but explicit
_sceneLayer.ColorTexture[1] = nullptr;
_customized = true;
}
void OculusDisplayPlugin::uncustomizeContext() {
@ -98,10 +101,14 @@ void OculusDisplayPlugin::uncustomizeContext() {
ovr_DestroyTextureSwapChain(_session, _textureSwapChain);
_textureSwapChain = nullptr;
_outputFramebuffer.reset();
_customized = false;
Parent::uncustomizeContext();
}
void OculusDisplayPlugin::hmdPresent() {
if (!_customized) {
return;
}
PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)_currentFrame->frameIndex)

View file

@ -32,5 +32,6 @@ private:
static const QString NAME;
ovrTextureSwapChain _textureSwapChain;
gpu::FramebufferPointer _outputFramebuffer;
bool _customized { false };
};

View file

@ -39,12 +39,12 @@ void logWarning(const char* what) {
qWarning(oculus) << what << ":" << getError().ErrorString;
}
void logFatal(const char* what) {
void logCritical(const char* what) {
std::string error("[oculus] ");
error += what;
error += ": ";
error += getError().ErrorString;
qFatal(error.c_str());
qCritical(error.c_str());
}

View file

@ -15,7 +15,7 @@
#include <controllers/Forward.h>
void logWarning(const char* what);
void logFatal(const char* what);
void logCritical(const char* what);
bool oculusAvailable();
ovrSession acquireOculusSession();
void releaseOculusSession();

View file

@ -15,9 +15,9 @@
(function() { // BEGIN LOCAL_SCOPE
Script.include("/~/system/libraries/utils.js");
Script.include("/~/system/libraries/Xform.js");
Script.include("/~/system/libraries/controllers.js");
Script.include("../libraries/utils.js");
Script.include("../libraries/Xform.js");
Script.include("../libraries/controllers.js");
//
// add lines where the hand ray picking is happening
@ -792,7 +792,7 @@ function MyController(hand) {
};
this.setState = function(newState, reason) {
setGrabCommunications((newState === STATE_DISTANCE_HOLDING) || (newState === STATE_NEAR_GRABBING));
if (WANT_DEBUG || WANT_DEBUG_STATE) {
var oldStateName = stateToName(this.state);
var newStateName = stateToName(newState);
@ -2770,10 +2770,10 @@ var handleHandMessages = function(channel, message, sender) {
Messages.messageReceived.connect(handleHandMessages);
var BASIC_TIMER_INTERVAL = 20; // 20ms = 50hz good enough
var BASIC_TIMER_INTERVAL_MS = 20; // 20ms = 50hz good enough
var updateIntervalTimer = Script.setInterval(function(){
update();
}, BASIC_TIMER_INTERVAL);
update(BASIC_TIMER_INTERVAL_MS / 1000);
}, BASIC_TIMER_INTERVAL_MS);
function cleanup() {
Script.clearInterval(updateIntervalTimer);

View file

@ -20,7 +20,7 @@
// When partially squeezing over a HUD element, a laser or the reticle is shown where the active hand
// controller beam intersects the HUD.
Script.include("/~/system/libraries/controllers.js");
Script.include("../libraries/controllers.js");
// UTILITIES -------------
//
@ -484,6 +484,9 @@ function update() {
if (!activeTrigger.state) {
return off(); // No trigger
}
if (getGrabCommunications()) {
return off();
}
var hudPoint2d = activeHudPoint2d(activeHand);
if (!hudPoint2d) {
return off();

View file

@ -7,6 +7,14 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
/* global MyAvatar, Vec3, Controller, Quat */
var GRAB_COMMUNICATIONS_SETTING = "io.highfidelity.isFarGrabbing";
setGrabCommunications = function setFarGrabCommunications(on) {
Settings.setValue(GRAB_COMMUNICATIONS_SETTING, on ? "on" : "");
}
getGrabCommunications = function getFarGrabCommunications() {
return !!Settings.getValue(GRAB_COMMUNICATIONS_SETTING, "");
}
// var GRAB_POINT_SPHERE_OFFSET = { x: 0, y: 0.2, z: 0 };
// var GRAB_POINT_SPHERE_OFFSET = { x: 0.1, y: 0.175, z: 0.04 };

View file

@ -128,6 +128,12 @@ function shutdown() {
}
}
function forcedShutdown() {
if (!isShuttingDown) {
shutdownCallback(0);
}
}
function shutdownCallback(idx) {
if (idx == 0 && !isShuttingDown) {
isShuttingDown = true;
@ -226,6 +232,7 @@ if (shouldQuit) {
// Check command line arguments to see how to find binaries
var argv = require('yargs').argv;
var pathFinder = require('./modules/path-finder.js');
var interfacePath = null;
@ -774,6 +781,7 @@ function maybeShowSplash() {
}
}
const trayIconOS = (osType == "Darwin") ? "osx" : "win";
var trayIcons = {};
trayIcons[ProcessGroupStates.STARTED] = "console-tray-" + trayIconOS + ".png";
@ -842,6 +850,34 @@ function onContentLoaded() {
// start the home server
homeServer.start();
}
// If we were launched with the shutdownWatcher option, then we need to watch for the interface app
// shutting down. The interface app will regularly update a running state file which we will check.
// If the file doesn't exist or stops updating for a significant amount of time, we will shut down.
if (argv.shutdownWatcher) {
console.log("Shutdown watcher requested... argv.shutdownWatcher:", argv.shutdownWatcher);
var MAX_TIME_SINCE_EDIT = 5000; // 5 seconds between updates
var firstAttemptToCheck = new Date().getTime();
var shutdownWatchInterval = setInterval(function(){
var stats = fs.stat(argv.shutdownWatcher, function(err, stats) {
if (err) {
var sinceFirstCheck = new Date().getTime() - firstAttemptToCheck;
if (sinceFirstCheck > MAX_TIME_SINCE_EDIT) {
console.log("Running state file is missing, assume interface has shutdown... shutting down snadbox.");
forcedShutdown();
clearTimeout(shutdownWatchInterval);
}
} else {
var sinceEdit = new Date().getTime() - stats.mtime.getTime();
if (sinceEdit > MAX_TIME_SINCE_EDIT) {
console.log("Running state of interface hasn't updated in MAX time... shutting down.");
forcedShutdown();
clearTimeout(shutdownWatchInterval);
}
}
});
}, 1000);
}
}

View file

@ -39,11 +39,13 @@ var THROTTLE = true;
var THROTTLE_RATE = 5000;
var sinceLastUpdate = 0;
var entitiesToMove = [];
//print('cells script')
function findCells() {
var results = Entities.findEntities(basePosition, 60000);
Script.clearInterval(octreeQueryInterval); // we don't need it any more
if (results.length === 0) {
// print('no entities found')
@ -55,9 +57,7 @@ function findCells() {
// print('name is:: ' + name)
if (name === 'Cell') {
// print('found a cell!!' + v)
Script.setTimeout(function() {
moveCell(v);
}, Math.random() * THROTTLE_RATE);
entitiesToMove.push(v);
}
});
}
@ -93,6 +93,7 @@ function update(deltaTime) {
Entities.setPacketsPerSecond(6000);
print("PPS:" + Entities.getPacketsPerSecond());
initialized = true;
Script.setTimeout(findCells, 20 * 1000); // After 20 seconds of getting entities, look for cells.
}
return;
}
@ -102,7 +103,11 @@ function update(deltaTime) {
if (sinceLastUpdate > THROTTLE_RATE) {
// print('SHOULD FIND CELLS!!!')
sinceLastUpdate = 0;
findCells();
entitiesToMove.forEach(function (v) {
Script.setTimeout(function() {
moveCell(v);
}, Math.random() * THROTTLE_RATE); // don't move all of them every five seconds, but at random times over interval
});
} else {
// print('returning in update ' + sinceLastUpdate)
return;
@ -116,4 +121,4 @@ function unload() {
}
Script.update.connect(update);
Script.scriptEnding.connect(unload);
Script.scriptEnding.connect(unload);

View file

@ -39,11 +39,13 @@ var THROTTLE = true;
var THROTTLE_RATE = 5000;
var sinceLastUpdate = 0;
var entitiesToMove = [];
//print('vesicle script')
function findVesicles() {
var results = Entities.findEntities(basePosition, 60000);
Script.clearInterval(octreeQueryInterval); // we don't need it any more
if (results.length === 0) {
// print('no entities found');
@ -54,9 +56,7 @@ function findVesicles() {
var name = Entities.getEntityProperties(v, 'name').name;
if (name === 'vesicle') {
//print('found a vesicle!!' + v)
Script.setTimeout(function() {
moveVesicle(v);
}, Math.random() * THROTTLE_RATE);
entitiesToMove.push(v);
}
});
}
@ -100,6 +100,7 @@ function update(deltaTime) {
Entities.setPacketsPerSecond(6000);
print("PPS:" + Entities.getPacketsPerSecond());
initialized = true;
Script.setTimeout(findVesicles, 20 * 1000); // After 20 seconds of getting entities, look for cells.
}
return;
}
@ -108,7 +109,11 @@ function update(deltaTime) {
sinceLastUpdate = sinceLastUpdate + deltaTime * 1000;
if (sinceLastUpdate > THROTTLE_RATE) {
sinceLastUpdate = 0;
findVesicles();
entitiesToMove.forEach(function (v) {
Script.setTimeout(function() {
moveVesicle(v);
}, Math.random() * THROTTLE_RATE); // don't move all of them every five seconds, but at random times over interval
});
} else {
return;
}
@ -121,4 +126,4 @@ function unload() {
}
Script.update.connect(update);
Script.scriptEnding.connect(unload);
Script.scriptEnding.connect(unload);