mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 13:49:12 +02:00
Merge branch '20805' into 20809
This commit is contained in:
commit
7ee1edac3a
43 changed files with 375 additions and 114 deletions
|
@ -1588,7 +1588,9 @@ function MyController(hand) {
|
||||||
|
|
||||||
ids.forEach(function(id) {
|
ids.forEach(function(id) {
|
||||||
var props = Entities.getEntityProperties(id, ["boundingBox", "name"]);
|
var props = Entities.getEntityProperties(id, ["boundingBox", "name"]);
|
||||||
if (props.name === 'pointer') {
|
if (!props ||
|
||||||
|
!props.boundingBox ||
|
||||||
|
props.name === 'pointer') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var entityMinPoint = props.boundingBox.brn;
|
var entityMinPoint = props.boundingBox.brn;
|
||||||
|
|
|
@ -75,7 +75,7 @@
|
||||||
function createEmitNumberPropertyUpdateFunction(propertyName) {
|
function createEmitNumberPropertyUpdateFunction(propertyName) {
|
||||||
return function() {
|
return function() {
|
||||||
EventBridge.emitWebEvent(
|
EventBridge.emitWebEvent(
|
||||||
'{ "type":"update", "properties":{"' + propertyName + '":' + Number(this.value.toFixed(4)) + '}}'
|
'{ "type":"update", "properties":{"' + propertyName + '":' + parseFloat(this.value).toFixed(4) + '}}'
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,11 +32,9 @@ ParticleExplorerTool = function() {
|
||||||
|
|
||||||
that.destroyWebView = function() {
|
that.destroyWebView = function() {
|
||||||
if (!that.webView) {
|
if (!that.webView) {
|
||||||
print("EBL CAN'ZT CLOSE WEB VIEW- IT DOESNT EXISTS!")
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
print("EBL CLOSING WEB VIEW")
|
|
||||||
that.webView.close();
|
that.webView.close();
|
||||||
that.webView = null;
|
that.webView = null;
|
||||||
that.activeParticleEntity = 0;
|
that.activeParticleEntity = 0;
|
||||||
|
|
|
@ -132,7 +132,7 @@ TableView {
|
||||||
// FIXME: Put reload item in tableModel passed in from RunningScripts.
|
// FIXME: Put reload item in tableModel passed in from RunningScripts.
|
||||||
HiFiGlyphs {
|
HiFiGlyphs {
|
||||||
id: reloadButton
|
id: reloadButton
|
||||||
text: "a"
|
text: hifi.glyphs.reloadSmall
|
||||||
color: parent.color
|
color: parent.color
|
||||||
anchors {
|
anchors {
|
||||||
top: parent.top
|
top: parent.top
|
||||||
|
@ -148,7 +148,7 @@ TableView {
|
||||||
// FIXME: Put stop item in tableModel passed in from RunningScripts.
|
// FIXME: Put stop item in tableModel passed in from RunningScripts.
|
||||||
HiFiGlyphs {
|
HiFiGlyphs {
|
||||||
id: stopButton
|
id: stopButton
|
||||||
text: "C"
|
text: hifi.glyphs.closeSmall
|
||||||
color: parent.color
|
color: parent.color
|
||||||
anchors {
|
anchors {
|
||||||
top: parent.top
|
top: parent.top
|
||||||
|
|
|
@ -22,7 +22,7 @@ TextField {
|
||||||
|
|
||||||
FontLoader { id: firaSansSemiBold; source: "../../fonts/FiraSans-SemiBold.ttf"; }
|
FontLoader { id: firaSansSemiBold; source: "../../fonts/FiraSans-SemiBold.ttf"; }
|
||||||
font.family: firaSansSemiBold.name
|
font.family: firaSansSemiBold.name
|
||||||
font.pointSize: hifi.fontSizes.textFieldInput
|
font.pixelSize: hifi.fontSizes.textFieldInput
|
||||||
height: implicitHeight + 4 // Make surrounding box higher so that highlight is vertically centered.
|
height: implicitHeight + 4 // Make surrounding box higher so that highlight is vertically centered.
|
||||||
placeholderText: textField.label // Instead of separate label (see below).
|
placeholderText: textField.label // Instead of separate label (see below).
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,18 @@ TreeView {
|
||||||
backgroundColor: parent.isLightColorScheme ? hifi.colors.tableRowLightEven : hifi.colors.tableRowDarkEven
|
backgroundColor: parent.isLightColorScheme ? hifi.colors.tableRowLightEven : hifi.colors.tableRowDarkEven
|
||||||
alternateBackgroundColor: parent.isLightColorScheme ? hifi.colors.tableRowLightOdd : hifi.colors.tableRowDarkOdd
|
alternateBackgroundColor: parent.isLightColorScheme ? hifi.colors.tableRowLightOdd : hifi.colors.tableRowDarkOdd
|
||||||
|
|
||||||
|
branchDelegate: HiFiGlyphs {
|
||||||
|
text: styleData.isExpanded ? hifi.glyphs.disclosureCollapse : hifi.glyphs.disclosureExpand
|
||||||
|
size: hifi.fontSizes.tableText * 2.5 // tableText is in points; proportionately scale to pixels
|
||||||
|
color: colorScheme == hifi.colorSchemes.light
|
||||||
|
? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight)
|
||||||
|
: (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText)
|
||||||
|
anchors {
|
||||||
|
left: parent ? parent.left : undefined
|
||||||
|
leftMargin: hifi.dimensions.tablePadding / 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handle: Item {
|
handle: Item {
|
||||||
id: scrollbarHandle
|
id: scrollbarHandle
|
||||||
implicitWidth: 6
|
implicitWidth: 6
|
||||||
|
|
|
@ -18,12 +18,19 @@ import "../js/Utils.js" as Utils
|
||||||
// This is our primary 'desktop' object to which all VR dialogs and windows are childed.
|
// This is our primary 'desktop' object to which all VR dialogs and windows are childed.
|
||||||
FocusScope {
|
FocusScope {
|
||||||
id: desktop
|
id: desktop
|
||||||
anchors.fill: parent;
|
|
||||||
objectName: "desktop"
|
objectName: "desktop"
|
||||||
|
|
||||||
|
// Allow the scale of the desktop to be changed without screwing up the size relative to the parent.
|
||||||
|
height: parent.height / scale
|
||||||
|
width: parent.width / scale
|
||||||
|
|
||||||
onHeightChanged: d.repositionAll();
|
onHeightChanged: d.repositionAll();
|
||||||
onWidthChanged: d.repositionAll();
|
onWidthChanged: d.repositionAll();
|
||||||
|
|
||||||
|
// Controls and windows can trigger this signal to ensure the desktop becomes visible
|
||||||
|
// when they're opened.
|
||||||
|
signal showDesktop();
|
||||||
|
|
||||||
// Allows QML/JS to find the desktop through the parent chain
|
// Allows QML/JS to find the desktop through the parent chain
|
||||||
property bool desktopRoot: true
|
property bool desktopRoot: true
|
||||||
|
|
||||||
|
@ -225,6 +232,8 @@ FocusScope {
|
||||||
}
|
}
|
||||||
|
|
||||||
reposition(targetWindow);
|
reposition(targetWindow);
|
||||||
|
|
||||||
|
showDesktop();
|
||||||
}
|
}
|
||||||
|
|
||||||
function reposition(item) {
|
function reposition(item) {
|
||||||
|
|
|
@ -24,7 +24,7 @@ Window {
|
||||||
resizable: true
|
resizable: true
|
||||||
destroyOnInvisible: true
|
destroyOnInvisible: true
|
||||||
x: 40; y: 40
|
x: 40; y: 40
|
||||||
implicitWidth: 384; implicitHeight: 650
|
implicitWidth: 384; implicitHeight: 640
|
||||||
minSize: Qt.vector2d(200, 300)
|
minSize: Qt.vector2d(200, 300)
|
||||||
|
|
||||||
HifiConstants { id: hifi }
|
HifiConstants { id: hifi }
|
||||||
|
|
|
@ -16,7 +16,7 @@ Text {
|
||||||
id: root
|
id: root
|
||||||
FontLoader { id: anonymousProRegular; source: "../../fonts/AnonymousPro-Regular.ttf"; }
|
FontLoader { id: anonymousProRegular; source: "../../fonts/AnonymousPro-Regular.ttf"; }
|
||||||
property real size: 32
|
property real size: 32
|
||||||
font.pointSize: size
|
font.pixelSize: size
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
horizontalAlignment: Text.AlignLeft
|
horizontalAlignment: Text.AlignLeft
|
||||||
font.family: anonymousProRegular.name
|
font.family: anonymousProRegular.name
|
||||||
|
|
|
@ -16,7 +16,7 @@ Text {
|
||||||
id: root
|
id: root
|
||||||
FontLoader { id: firaSansSemiBold; source: "../../fonts/FiraSans-SemiBold.ttf"; }
|
FontLoader { id: firaSansSemiBold; source: "../../fonts/FiraSans-SemiBold.ttf"; }
|
||||||
property real size: 32
|
property real size: 32
|
||||||
font.pointSize: size
|
font.pixelSize: size
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
horizontalAlignment: Text.AlignLeft
|
horizontalAlignment: Text.AlignLeft
|
||||||
font.family: firaSansSemiBold.name
|
font.family: firaSansSemiBold.name
|
||||||
|
|
|
@ -16,7 +16,7 @@ Text {
|
||||||
id: root
|
id: root
|
||||||
FontLoader { id: hiFiGlyphs; source: "../../fonts/hifi-glyphs.ttf"; }
|
FontLoader { id: hiFiGlyphs; source: "../../fonts/hifi-glyphs.ttf"; }
|
||||||
property int size: 32
|
property int size: 32
|
||||||
font.pixelSize: size // Size this font in pixels because it's UI widgets.
|
font.pixelSize: size
|
||||||
width: size
|
width: size
|
||||||
height: size
|
height: size
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
|
|
@ -16,6 +16,7 @@ Item {
|
||||||
readonly property alias colorSchemes: colorSchemes
|
readonly property alias colorSchemes: colorSchemes
|
||||||
readonly property alias dimensions: dimensions
|
readonly property alias dimensions: dimensions
|
||||||
readonly property alias fontSizes: fontSizes
|
readonly property alias fontSizes: fontSizes
|
||||||
|
readonly property alias glyphs: glyphs
|
||||||
readonly property alias buttons: buttons
|
readonly property alias buttons: buttons
|
||||||
readonly property alias effects: effects
|
readonly property alias effects: effects
|
||||||
|
|
||||||
|
@ -83,22 +84,35 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: fontSizes
|
id: fontSizes // In pixels
|
||||||
readonly property real overlayTitle: dimensions.largeScreen? 16 : 12
|
readonly property real overlayTitle: dimensions.largeScreen? 18 : 14
|
||||||
readonly property real tabName: dimensions.largeScreen? 11 : 9
|
readonly property real tabName: dimensions.largeScreen? 12 : 10
|
||||||
readonly property real sectionName: dimensions.largeScreen? 11 : 9
|
readonly property real sectionName: dimensions.largeScreen? 12 : 10
|
||||||
readonly property real inputLabel: dimensions.largeScreen? 13.5 : 9
|
readonly property real inputLabel: dimensions.largeScreen? 14 : 10
|
||||||
readonly property real textFieldInput: dimensions.largeScreen? 13.5 : 11
|
readonly property real textFieldInput: dimensions.largeScreen? 15 : 12
|
||||||
readonly property real tableText: dimensions.largeScreen? 13.5 : 11
|
readonly property real tableText: dimensions.largeScreen? 15 : 12
|
||||||
readonly property real buttonLabel: dimensions.largeScreen? 12 : 8
|
readonly property real buttonLabel: dimensions.largeScreen? 13 : 9
|
||||||
readonly property real iconButton: dimensions.largeScreen? 12: 8
|
readonly property real iconButton: dimensions.largeScreen? 13 : 9
|
||||||
readonly property real listItem: dimensions.largeScreen? 13.5 : 9
|
readonly property real listItem: dimensions.largeScreen? 15 : 11
|
||||||
readonly property real tabularData: dimensions.largeScreen? 11 : 9
|
readonly property real tabularData: dimensions.largeScreen? 15 : 11
|
||||||
readonly property real logs: dimensions.largeScreen? 15 : 10
|
readonly property real logs: dimensions.largeScreen? 16 : 12
|
||||||
readonly property real code: dimensions.largeScreen? 15 : 10
|
readonly property real code: dimensions.largeScreen? 16 : 12
|
||||||
readonly property real rootMenu: dimensions.largeScreen? 13.5 : 9
|
readonly property real rootMenu: dimensions.largeScreen? 15 : 11
|
||||||
readonly property real menuItem: dimensions.largeScreen? 13.5 : 9
|
readonly property real menuItem: dimensions.largeScreen? 15 : 11
|
||||||
readonly property real shortcutText: dimensions.largeScreen? 12 : 8
|
readonly property real shortcutText: dimensions.largeScreen? 13 : 9
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: glyphs
|
||||||
|
readonly property string close: "w"
|
||||||
|
readonly property string closeInverted: "x"
|
||||||
|
readonly property string closeSmall: "C"
|
||||||
|
readonly property string disclosureCollapse: "Z"
|
||||||
|
readonly property string disclosureExpand: "B"
|
||||||
|
readonly property string pin: "y"
|
||||||
|
readonly property string pinInverted: "z"
|
||||||
|
readonly property string reloadSmall: "a"
|
||||||
|
readonly property string resizeHandle: "A"
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
|
|
@ -16,7 +16,7 @@ Text {
|
||||||
id: root
|
id: root
|
||||||
FontLoader { id: ralewayBold; source: "../../fonts/Raleway-Bold.ttf"; }
|
FontLoader { id: ralewayBold; source: "../../fonts/Raleway-Bold.ttf"; }
|
||||||
property real size: 32
|
property real size: 32
|
||||||
font.pointSize: size
|
font.pixelSize: size
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
horizontalAlignment: Text.AlignLeft
|
horizontalAlignment: Text.AlignLeft
|
||||||
font.family: ralewayBold.name
|
font.family: ralewayBold.name
|
||||||
|
|
|
@ -16,7 +16,7 @@ Text {
|
||||||
id: root
|
id: root
|
||||||
FontLoader { id: ralewayLight; source: "../../fonts/Raleway-Light.ttf"; }
|
FontLoader { id: ralewayLight; source: "../../fonts/Raleway-Light.ttf"; }
|
||||||
property real size: 32
|
property real size: 32
|
||||||
font.pointSize: size
|
font.pixelSize: size
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
horizontalAlignment: Text.AlignLeft
|
horizontalAlignment: Text.AlignLeft
|
||||||
font.family: ralewayLight.name
|
font.family: ralewayLight.name
|
||||||
|
|
|
@ -16,7 +16,7 @@ Text {
|
||||||
id: root
|
id: root
|
||||||
FontLoader { id: ralewayRegular; source: "../../fonts/Raleway-Regular.ttf"; }
|
FontLoader { id: ralewayRegular; source: "../../fonts/Raleway-Regular.ttf"; }
|
||||||
property real size: 32
|
property real size: 32
|
||||||
font.pointSize: size
|
font.pixelSize: size
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
horizontalAlignment: Text.AlignLeft
|
horizontalAlignment: Text.AlignLeft
|
||||||
font.family: ralewayRegular.name
|
font.family: ralewayRegular.name
|
||||||
|
|
|
@ -16,7 +16,7 @@ Text {
|
||||||
id: root
|
id: root
|
||||||
FontLoader { id: ralewaySemibold; source: "../../fonts/Raleway-Semibold.ttf"; }
|
FontLoader { id: ralewaySemibold; source: "../../fonts/Raleway-Semibold.ttf"; }
|
||||||
property real size: 32
|
property real size: 32
|
||||||
font.pointSize: size
|
font.pixelSize: size
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
horizontalAlignment: Text.AlignLeft
|
horizontalAlignment: Text.AlignLeft
|
||||||
font.family: ralewaySemibold.name
|
font.family: ralewaySemibold.name
|
||||||
|
|
|
@ -53,7 +53,7 @@ Frame {
|
||||||
HiFiGlyphs {
|
HiFiGlyphs {
|
||||||
// "Pin" button
|
// "Pin" button
|
||||||
visible: false
|
visible: false
|
||||||
text: (frame.pinned && !pinClickArea.containsMouse) || (!frame.pinned && pinClickArea.containsMouse) ? "z" : "y"
|
text: (frame.pinned && !pinClickArea.containsMouse) || (!frame.pinned && pinClickArea.containsMouse) ? hifi.glyphs.pinInverted : hifi.glyphs.pin
|
||||||
color: pinClickArea.containsMouse && !pinClickArea.pressed ? hifi.colors.redHighlight : hifi.colors.white
|
color: pinClickArea.containsMouse && !pinClickArea.pressed ? hifi.colors.redHighlight : hifi.colors.white
|
||||||
size: iconSize
|
size: iconSize
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
@ -68,7 +68,7 @@ Frame {
|
||||||
HiFiGlyphs {
|
HiFiGlyphs {
|
||||||
// "Close" button
|
// "Close" button
|
||||||
visible: window ? window.closable : false
|
visible: window ? window.closable : false
|
||||||
text: closeClickArea.containsPress ? "x" : "w"
|
text: closeClickArea.containsPress ? hifi.glyphs.closeInverted : hifi.glyphs.close
|
||||||
color: closeClickArea.containsMouse ? hifi.colors.redHighlight : hifi.colors.white
|
color: closeClickArea.containsMouse ? hifi.colors.redHighlight : hifi.colors.white
|
||||||
size: iconSize
|
size: iconSize
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
|
|
@ -125,7 +125,7 @@ Item {
|
||||||
visible: sizeDrag.enabled
|
visible: sizeDrag.enabled
|
||||||
x: -11 // Move a little to visually align
|
x: -11 // Move a little to visually align
|
||||||
y: -4 // ""
|
y: -4 // ""
|
||||||
text: "A"
|
text: hifi.glyphs.resizeHandle
|
||||||
size: iconSize + 10
|
size: iconSize + 10
|
||||||
color: sizeDrag.containsMouse || sizeDrag.pressed ? hifi.colors.white : hifi.colors.white50
|
color: sizeDrag.containsMouse || sizeDrag.pressed ? hifi.colors.white : hifi.colors.white50
|
||||||
}
|
}
|
||||||
|
|
|
@ -1198,6 +1198,7 @@ void Application::initializeUi() {
|
||||||
// OffscreenUi is a subclass of OffscreenQmlSurface specifically designed to
|
// OffscreenUi is a subclass of OffscreenQmlSurface specifically designed to
|
||||||
// support the window management and scripting proxies for VR use
|
// support the window management and scripting proxies for VR use
|
||||||
offscreenUi->createDesktop(QString("hifi/Desktop.qml"));
|
offscreenUi->createDesktop(QString("hifi/Desktop.qml"));
|
||||||
|
connect(offscreenUi.data(), &OffscreenUi::showDesktop, this, &Application::showDesktop);
|
||||||
|
|
||||||
// FIXME either expose so that dialogs can set this themselves or
|
// FIXME either expose so that dialogs can set this themselves or
|
||||||
// do better detection in the offscreen UI of what has focus
|
// do better detection in the offscreen UI of what has focus
|
||||||
|
@ -3772,8 +3773,9 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
||||||
// The pending changes collecting the changes here
|
// The pending changes collecting the changes here
|
||||||
render::PendingChanges pendingChanges;
|
render::PendingChanges pendingChanges;
|
||||||
|
|
||||||
|
// FIXME: Move this out of here!, Background / skybox should be driven by the enityt content just like the other entities
|
||||||
// Background rendering decision
|
// Background rendering decision
|
||||||
if (BackgroundRenderData::_item == 0) {
|
if (!render::Item::isValidID(BackgroundRenderData::_item)) {
|
||||||
auto backgroundRenderData = make_shared<BackgroundRenderData>();
|
auto backgroundRenderData = make_shared<BackgroundRenderData>();
|
||||||
auto backgroundRenderPayload = make_shared<BackgroundRenderData::Payload>(backgroundRenderData);
|
auto backgroundRenderPayload = make_shared<BackgroundRenderData::Payload>(backgroundRenderData);
|
||||||
BackgroundRenderData::_item = _main3DScene->allocateID();
|
BackgroundRenderData::_item = _main3DScene->allocateID();
|
||||||
|
@ -3798,8 +3800,9 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: Move this out of here!, WorldBox should be driven by the entity content just like the other entities
|
||||||
// Make sure the WorldBox is in the scene
|
// Make sure the WorldBox is in the scene
|
||||||
if (WorldBoxRenderData::_item == 0) {
|
if (!render::Item::isValidID(WorldBoxRenderData::_item)) {
|
||||||
auto worldBoxRenderData = make_shared<WorldBoxRenderData>();
|
auto worldBoxRenderData = make_shared<WorldBoxRenderData>();
|
||||||
auto worldBoxRenderPayload = make_shared<WorldBoxRenderData::Payload>(worldBoxRenderData);
|
auto worldBoxRenderPayload = make_shared<WorldBoxRenderData::Payload>(worldBoxRenderData);
|
||||||
|
|
||||||
|
@ -5128,3 +5131,9 @@ void Application::readArgumentsFromLocalSocket() {
|
||||||
qApp->openUrl(QString::fromUtf8(message));
|
qApp->openUrl(QString::fromUtf8(message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Application::showDesktop() {
|
||||||
|
if (!_overlayConductor.getEnabled()) {
|
||||||
|
_overlayConductor.setEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -280,6 +280,7 @@ public slots:
|
||||||
void runTests();
|
void runTests();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void showDesktop();
|
||||||
void clearDomainOctreeDetails();
|
void clearDomainOctreeDetails();
|
||||||
void idle(uint64_t now);
|
void idle(uint64_t now);
|
||||||
void aboutToQuit();
|
void aboutToQuit();
|
||||||
|
|
|
@ -315,6 +315,7 @@ bool Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr<render::Scene>
|
||||||
|
|
||||||
void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
|
void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
|
||||||
pendingChanges.removeItem(_renderItemID);
|
pendingChanges.removeItem(_renderItemID);
|
||||||
|
render::Item::clearID(_renderItemID);
|
||||||
_skeletonModel.removeFromScene(scene, pendingChanges);
|
_skeletonModel.removeFromScene(scene, pendingChanges);
|
||||||
getHead()->getFaceModel().removeFromScene(scene, pendingChanges);
|
getHead()->getFaceModel().removeFromScene(scene, pendingChanges);
|
||||||
for (auto& attachmentModel : _attachmentModels) {
|
for (auto& attachmentModel : _attachmentModels) {
|
||||||
|
@ -323,7 +324,7 @@ void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::S
|
||||||
}
|
}
|
||||||
|
|
||||||
void Avatar::updateRenderItem(render::PendingChanges& pendingChanges) {
|
void Avatar::updateRenderItem(render::PendingChanges& pendingChanges) {
|
||||||
if (_renderItemID != render::Item::INVALID_ITEM_ID) {
|
if (render::Item::isValidID(_renderItemID)) {
|
||||||
pendingChanges.updateItem<render::Payload<AvatarData>>(_renderItemID, [](render::Payload<AvatarData>& p) {});
|
pendingChanges.updateItem<render::Payload<AvatarData>>(_renderItemID, [](render::Payload<AvatarData>& p) {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -229,6 +229,6 @@ bool Overlay::addToScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene
|
||||||
|
|
||||||
void Overlay::removeFromScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
|
void Overlay::removeFromScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
|
||||||
pendingChanges.removeItem(_renderItemID);
|
pendingChanges.removeItem(_renderItemID);
|
||||||
_renderItemID = render::Item::INVALID_ITEM_ID;
|
render::Item::clearID(_renderItemID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -94,7 +94,7 @@ void Overlays::cleanupOverlaysToDelete() {
|
||||||
Overlay::Pointer overlay = _overlaysToDelete.takeLast();
|
Overlay::Pointer overlay = _overlaysToDelete.takeLast();
|
||||||
|
|
||||||
auto itemID = overlay->getRenderItemID();
|
auto itemID = overlay->getRenderItemID();
|
||||||
if (itemID != render::Item::INVALID_ITEM_ID) {
|
if (render::Item::isValidID(itemID)) {
|
||||||
overlay->removeFromScene(overlay, scene, pendingChanges);
|
overlay->removeFromScene(overlay, scene, pendingChanges);
|
||||||
}
|
}
|
||||||
} while (!_overlaysToDelete.isEmpty());
|
} while (!_overlaysToDelete.isEmpty());
|
||||||
|
@ -241,7 +241,7 @@ bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) {
|
||||||
render::ItemKey itemKey = render::payloadGetKey(thisOverlay);
|
render::ItemKey itemKey = render::payloadGetKey(thisOverlay);
|
||||||
if (itemKey != oldItemKey) {
|
if (itemKey != oldItemKey) {
|
||||||
auto itemID = thisOverlay->getRenderItemID();
|
auto itemID = thisOverlay->getRenderItemID();
|
||||||
if (itemID != render::Item::INVALID_ITEM_ID) {
|
if (render::Item::isValidID(itemID)) {
|
||||||
render::ScenePointer scene = qApp->getMain3DScene();
|
render::ScenePointer scene = qApp->getMain3DScene();
|
||||||
render::PendingChanges pendingChanges;
|
render::PendingChanges pendingChanges;
|
||||||
pendingChanges.resortItem(itemID, oldItemKey, itemKey);
|
pendingChanges.resortItem(itemID, oldItemKey, itemKey);
|
||||||
|
|
|
@ -104,7 +104,9 @@ void AudioInjector::restart() {
|
||||||
|
|
||||||
// reset state to start sending from beginning again
|
// reset state to start sending from beginning again
|
||||||
_nextFrame = 0;
|
_nextFrame = 0;
|
||||||
|
if (_frameTimer) {
|
||||||
_frameTimer->invalidate();
|
_frameTimer->invalidate();
|
||||||
|
}
|
||||||
_hasSentFirstFrame = false;
|
_hasSentFirstFrame = false;
|
||||||
|
|
||||||
// check our state to decide if we need extra handling for the restart request
|
// check our state to decide if we need extra handling for the restart request
|
||||||
|
@ -122,7 +124,9 @@ void AudioInjector::restart() {
|
||||||
injectLocally();
|
injectLocally();
|
||||||
} else {
|
} else {
|
||||||
// wake the AudioInjectorManager back up if it's stuck waiting
|
// wake the AudioInjectorManager back up if it's stuck waiting
|
||||||
injectorManager->restartFinishedInjector(this);
|
if (!injectorManager->restartFinishedInjector(this)) {
|
||||||
|
_state = State::Finished; // we're not playing, so reset the state used by isPlaying.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,6 +129,15 @@ void AudioInjectorManager::run() {
|
||||||
|
|
||||||
static const int MAX_INJECTORS_PER_THREAD = 40; // calculated based on AudioInjector time to send frame, with sufficient padding
|
static const int MAX_INJECTORS_PER_THREAD = 40; // calculated based on AudioInjector time to send frame, with sufficient padding
|
||||||
|
|
||||||
|
bool AudioInjectorManager::wouldExceedLimits() { // Should be called inside of a lock.
|
||||||
|
if (_injectors.size() >= MAX_INJECTORS_PER_THREAD) {
|
||||||
|
qDebug() << "AudioInjectorManager::threadInjector could not thread AudioInjector - at max of"
|
||||||
|
<< MAX_INJECTORS_PER_THREAD << "current audio injectors.";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool AudioInjectorManager::threadInjector(AudioInjector* injector) {
|
bool AudioInjectorManager::threadInjector(AudioInjector* injector) {
|
||||||
if (_shouldStop) {
|
if (_shouldStop) {
|
||||||
qDebug() << "AudioInjectorManager::threadInjector asked to thread injector but is shutting down.";
|
qDebug() << "AudioInjectorManager::threadInjector asked to thread injector but is shutting down.";
|
||||||
|
@ -138,8 +147,9 @@ bool AudioInjectorManager::threadInjector(AudioInjector* injector) {
|
||||||
// guard the injectors vector with a mutex
|
// guard the injectors vector with a mutex
|
||||||
Lock lock(_injectorsMutex);
|
Lock lock(_injectorsMutex);
|
||||||
|
|
||||||
// check if we'll be able to thread this injector (do we have < max concurrent injectors)
|
if (wouldExceedLimits()) {
|
||||||
if (_injectors.size() < MAX_INJECTORS_PER_THREAD) {
|
return false;
|
||||||
|
} else {
|
||||||
if (!_thread) {
|
if (!_thread) {
|
||||||
createThread();
|
createThread();
|
||||||
}
|
}
|
||||||
|
@ -156,23 +166,22 @@ bool AudioInjectorManager::threadInjector(AudioInjector* injector) {
|
||||||
_injectorReady.notify_one();
|
_injectorReady.notify_one();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
|
||||||
// unable to thread this injector, at the max
|
|
||||||
qDebug() << "AudioInjectorManager::threadInjector could not thread AudioInjector - at max of"
|
|
||||||
<< MAX_INJECTORS_PER_THREAD << "current audio injectors.";
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioInjectorManager::restartFinishedInjector(AudioInjector* injector) {
|
bool AudioInjectorManager::restartFinishedInjector(AudioInjector* injector) {
|
||||||
if (!_shouldStop) {
|
if (!_shouldStop) {
|
||||||
// guard the injectors vector with a mutex
|
// guard the injectors vector with a mutex
|
||||||
Lock lock(_injectorsMutex);
|
Lock lock(_injectorsMutex);
|
||||||
|
|
||||||
|
if (wouldExceedLimits()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
// add the injector to the queue with a send timestamp of now
|
// add the injector to the queue with a send timestamp of now
|
||||||
_injectors.emplace(usecTimestampNow(), InjectorQPointer { injector });
|
_injectors.emplace(usecTimestampNow(), InjectorQPointer { injector });
|
||||||
|
|
||||||
// notify our wait condition so we can inject two frames for this injector immediately
|
// notify our wait condition so we can inject two frames for this injector immediately
|
||||||
_injectorReady.notify_one();
|
_injectorReady.notify_one();
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,8 +50,9 @@ private:
|
||||||
using Lock = std::unique_lock<Mutex>;
|
using Lock = std::unique_lock<Mutex>;
|
||||||
|
|
||||||
bool threadInjector(AudioInjector* injector);
|
bool threadInjector(AudioInjector* injector);
|
||||||
void restartFinishedInjector(AudioInjector* injector);
|
bool restartFinishedInjector(AudioInjector* injector);
|
||||||
void notifyInjectorReadyCondition() { _injectorReady.notify_one(); }
|
void notifyInjectorReadyCondition() { _injectorReady.notify_one(); }
|
||||||
|
bool wouldExceedLimits();
|
||||||
|
|
||||||
AudioInjectorManager() {};
|
AudioInjectorManager() {};
|
||||||
AudioInjectorManager(const AudioInjectorManager&) = delete;
|
AudioInjectorManager(const AudioInjectorManager&) = delete;
|
||||||
|
|
|
@ -66,10 +66,11 @@ public:
|
||||||
|
|
||||||
void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
|
void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
|
||||||
pendingChanges.removeItem(_myItem);
|
pendingChanges.removeItem(_myItem);
|
||||||
|
render::Item::clearID(_myItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
void notifyChanged() {
|
void notifyChanged() {
|
||||||
if (_myItem == render::Item::INVALID_ITEM_ID) {
|
if (!render::Item::isValidID(_myItem)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -251,6 +251,7 @@ bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_p
|
||||||
void RenderableModelEntityItem::removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,
|
void RenderableModelEntityItem::removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,
|
||||||
render::PendingChanges& pendingChanges) {
|
render::PendingChanges& pendingChanges) {
|
||||||
pendingChanges.removeItem(_myMetaItem);
|
pendingChanges.removeItem(_myMetaItem);
|
||||||
|
render::Item::clearID(_myMetaItem);
|
||||||
if (_model) {
|
if (_model) {
|
||||||
_model->removeFromScene(scene, pendingChanges);
|
_model->removeFromScene(scene, pendingChanges);
|
||||||
}
|
}
|
||||||
|
|
|
@ -179,6 +179,7 @@ void RenderableParticleEffectEntityItem::removeFromScene(EntityItemPointer self,
|
||||||
render::PendingChanges& pendingChanges) {
|
render::PendingChanges& pendingChanges) {
|
||||||
pendingChanges.removeItem(_renderItemId);
|
pendingChanges.removeItem(_renderItemId);
|
||||||
_scene = nullptr;
|
_scene = nullptr;
|
||||||
|
render::Item::clearID(_renderItemId);
|
||||||
};
|
};
|
||||||
|
|
||||||
void RenderableParticleEffectEntityItem::update(const quint64& now) {
|
void RenderableParticleEffectEntityItem::update(const quint64& now) {
|
||||||
|
@ -199,7 +200,8 @@ void RenderableParticleEffectEntityItem::update(const quint64& now) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderableParticleEffectEntityItem::updateRenderItem() {
|
void RenderableParticleEffectEntityItem::updateRenderItem() {
|
||||||
if (!_scene) {
|
// this 2 tests are synonyms for this class, but we would like to get rid of the _scene pointer ultimately
|
||||||
|
if (!_scene || !render::Item::isValidID(_renderItemId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!getVisible()) {
|
if (!getVisible()) {
|
||||||
|
@ -312,7 +314,7 @@ void RenderableParticleEffectEntityItem::createPipelines() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderableParticleEffectEntityItem::notifyBoundChanged() {
|
void RenderableParticleEffectEntityItem::notifyBoundChanged() {
|
||||||
if (_renderItemId == render::Item::INVALID_ITEM_ID) {
|
if (!render::Item::isValidID(_renderItemId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
render::PendingChanges pendingChanges;
|
render::PendingChanges pendingChanges;
|
||||||
|
|
|
@ -572,6 +572,7 @@ void RenderablePolyVoxEntityItem::removeFromScene(EntityItemPointer self,
|
||||||
std::shared_ptr<render::Scene> scene,
|
std::shared_ptr<render::Scene> scene,
|
||||||
render::PendingChanges& pendingChanges) {
|
render::PendingChanges& pendingChanges) {
|
||||||
pendingChanges.removeItem(_myItem);
|
pendingChanges.removeItem(_myItem);
|
||||||
|
render::Item::clearID(_myItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace render {
|
namespace render {
|
||||||
|
|
|
@ -230,6 +230,7 @@ bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, std::shared_pt
|
||||||
void RenderableZoneEntityItem::removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,
|
void RenderableZoneEntityItem::removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,
|
||||||
render::PendingChanges& pendingChanges) {
|
render::PendingChanges& pendingChanges) {
|
||||||
pendingChanges.removeItem(_myMetaItem);
|
pendingChanges.removeItem(_myMetaItem);
|
||||||
|
render::Item::clearID(_myMetaItem);
|
||||||
if (_model) {
|
if (_model) {
|
||||||
_model->removeFromScene(scene, pendingChanges);
|
_model->removeFromScene(scene, pendingChanges);
|
||||||
}
|
}
|
||||||
|
@ -237,7 +238,7 @@ void RenderableZoneEntityItem::removeFromScene(EntityItemPointer self, std::shar
|
||||||
|
|
||||||
|
|
||||||
void RenderableZoneEntityItem::notifyBoundChanged() {
|
void RenderableZoneEntityItem::notifyBoundChanged() {
|
||||||
if (_myMetaItem == render::Item::INVALID_ITEM_ID) {
|
if (!render::Item::isValidID(_myMetaItem)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
render::PendingChanges pendingChanges;
|
render::PendingChanges pendingChanges;
|
||||||
|
|
|
@ -21,6 +21,17 @@
|
||||||
class NLPacket : public udt::Packet {
|
class NLPacket : public udt::Packet {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
// 0 1 2 3
|
||||||
|
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | Packet Type | Packet Version |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | Node UUID | Hash (only if verified) | Optional (only if sourced)
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
//
|
||||||
|
// NLPacket Header Format
|
||||||
|
|
||||||
// this is used by the Octree classes - must be known at compile time
|
// this is used by the Octree classes - must be known at compile time
|
||||||
static const int MAX_PACKET_HEADER_SIZE =
|
static const int MAX_PACKET_HEADER_SIZE =
|
||||||
sizeof(udt::Packet::SequenceNumberAndBitField) + sizeof(udt::Packet::MessageNumberAndBitField) +
|
sizeof(udt::Packet::SequenceNumberAndBitField) + sizeof(udt::Packet::MessageNumberAndBitField) +
|
||||||
|
|
|
@ -26,10 +26,65 @@ namespace udt {
|
||||||
static const int UDP_SEND_BUFFER_SIZE_BYTES = 1048576;
|
static const int UDP_SEND_BUFFER_SIZE_BYTES = 1048576;
|
||||||
static const int UDP_RECEIVE_BUFFER_SIZE_BYTES = 1048576;
|
static const int UDP_RECEIVE_BUFFER_SIZE_BYTES = 1048576;
|
||||||
static const int DEFAULT_SYN_INTERVAL_USECS = 10 * 1000;
|
static const int DEFAULT_SYN_INTERVAL_USECS = 10 * 1000;
|
||||||
static const int SEQUENCE_NUMBER_BITS = sizeof(SequenceNumber) * 8;
|
|
||||||
static const int MESSAGE_LINE_NUMBER_BITS = 32;
|
|
||||||
static const int MESSAGE_NUMBER_BITS = 30;
|
// Header constants
|
||||||
static const uint32_t CONTROL_BIT_MASK = uint32_t(1) << (SEQUENCE_NUMBER_BITS - 1);
|
|
||||||
|
// Bit sizes (in order)
|
||||||
|
static const int CONTROL_BIT_SIZE = 1;
|
||||||
|
static const int RELIABILITY_BIT_SIZE = 1;
|
||||||
|
static const int MESSAGE_BIT_SIZE = 1;
|
||||||
|
static const int OBFUSCATION_LEVEL_SIZE = 2;
|
||||||
|
static const int SEQUENCE_NUMBER_SIZE= 27;
|
||||||
|
|
||||||
|
static const int PACKET_POSITION_SIZE = 2;
|
||||||
|
static const int MESSAGE_NUMBER_SIZE = 30;
|
||||||
|
|
||||||
|
static const int MESSAGE_PART_NUMBER_SIZE = 32;
|
||||||
|
|
||||||
|
// Offsets
|
||||||
|
static const int SEQUENCE_NUMBER_OFFSET = 0;
|
||||||
|
static const int OBFUSCATION_LEVEL_OFFSET = SEQUENCE_NUMBER_OFFSET + SEQUENCE_NUMBER_SIZE;
|
||||||
|
static const int MESSAGE_BIT_OFFSET = OBFUSCATION_LEVEL_OFFSET + OBFUSCATION_LEVEL_SIZE;
|
||||||
|
static const int RELIABILITY_BIT_OFFSET = MESSAGE_BIT_OFFSET + MESSAGE_BIT_SIZE;
|
||||||
|
static const int CONTROL_BIT_OFFSET = RELIABILITY_BIT_OFFSET + RELIABILITY_BIT_SIZE;
|
||||||
|
|
||||||
|
static const int MESSAGE_NUMBER_OFFSET = 0;
|
||||||
|
static const int PACKET_POSITION_OFFSET = MESSAGE_NUMBER_OFFSET + MESSAGE_NUMBER_SIZE;
|
||||||
|
|
||||||
|
static const int MESSAGE_PART_NUMBER_OFFSET = 0;
|
||||||
|
|
||||||
|
// Masks
|
||||||
|
static const uint32_t CONTROL_BIT_MASK = uint32_t(1) << CONTROL_BIT_OFFSET;
|
||||||
|
static const uint32_t RELIABILITY_BIT_MASK = uint32_t(1) << RELIABILITY_BIT_OFFSET;
|
||||||
|
static const uint32_t MESSAGE_BIT_MASK = uint32_t(1) << MESSAGE_BIT_OFFSET;
|
||||||
|
static const uint32_t OBFUSCATION_LEVEL_MASK = uint32_t(3) << OBFUSCATION_LEVEL_OFFSET;
|
||||||
|
static const uint32_t BIT_FIELD_MASK = CONTROL_BIT_MASK | RELIABILITY_BIT_MASK | MESSAGE_BIT_MASK | OBFUSCATION_LEVEL_MASK;
|
||||||
|
static const uint32_t SEQUENCE_NUMBER_MASK = ~BIT_FIELD_MASK;
|
||||||
|
|
||||||
|
static const uint32_t PACKET_POSITION_MASK = uint32_t(3) << PACKET_POSITION_OFFSET;
|
||||||
|
static const uint32_t MESSAGE_NUMBER_MASK = ~PACKET_POSITION_MASK;
|
||||||
|
|
||||||
|
static const uint32_t MESSAGE_PART_NUMBER_MASK = ~uint32_t(0);
|
||||||
|
|
||||||
|
|
||||||
|
// Static checks
|
||||||
|
static_assert(CONTROL_BIT_SIZE + RELIABILITY_BIT_SIZE + MESSAGE_BIT_SIZE +
|
||||||
|
OBFUSCATION_LEVEL_SIZE + SEQUENCE_NUMBER_SIZE == 32, "Sequence number line size incorrect");
|
||||||
|
static_assert(PACKET_POSITION_SIZE + MESSAGE_NUMBER_SIZE == 32, "Message number line size incorrect");
|
||||||
|
static_assert(MESSAGE_PART_NUMBER_SIZE == 32, "Message part number line size incorrect");
|
||||||
|
|
||||||
|
static_assert(CONTROL_BIT_MASK == 0x80000000, "CONTROL_BIT_MASK incorrect");
|
||||||
|
static_assert(RELIABILITY_BIT_MASK == 0x40000000, "RELIABILITY_BIT_MASK incorrect");
|
||||||
|
static_assert(MESSAGE_BIT_MASK == 0x20000000, "MESSAGE_BIT_MASK incorrect");
|
||||||
|
static_assert(OBFUSCATION_LEVEL_MASK == 0x18000000, "OBFUSCATION_LEVEL_MASK incorrect");
|
||||||
|
static_assert(BIT_FIELD_MASK == 0xF8000000, "BIT_FIELD_MASK incorrect");
|
||||||
|
static_assert(SEQUENCE_NUMBER_MASK == 0x07FFFFFF, "SEQUENCE_NUMBER_MASK incorrect");
|
||||||
|
|
||||||
|
static_assert(PACKET_POSITION_MASK == 0xC0000000, "PACKET_POSITION_MASK incorrect");
|
||||||
|
static_assert(MESSAGE_NUMBER_MASK == 0x3FFFFFFF, "MESSAGE_NUMBER_MASK incorrect");
|
||||||
|
|
||||||
|
static_assert(MESSAGE_PART_NUMBER_MASK == 0xFFFFFFFF, "MESSAGE_PART_NUMBER_MASK incorrect");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // hifi_udt_Constants_h
|
#endif // hifi_udt_Constants_h
|
||||||
|
|
|
@ -11,10 +11,35 @@
|
||||||
|
|
||||||
#include "Packet.h"
|
#include "Packet.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include <LogHandler.h>
|
||||||
|
|
||||||
using namespace udt;
|
using namespace udt;
|
||||||
|
|
||||||
static int packetMetaTypeId = qRegisterMetaType<Packet*>("Packet*");
|
static int packetMetaTypeId = qRegisterMetaType<Packet*>("Packet*");
|
||||||
|
|
||||||
|
using Key = uint64_t;
|
||||||
|
static const std::array<Key, 4> KEYS {{
|
||||||
|
0x0,
|
||||||
|
0x6362726973736574,
|
||||||
|
0x7362697261726461,
|
||||||
|
0x72687566666d616e,
|
||||||
|
}};
|
||||||
|
|
||||||
|
void xorHelper(char* start, int size, Key key) {
|
||||||
|
const auto end = start + size;
|
||||||
|
|
||||||
|
auto p = start;
|
||||||
|
for (; p + sizeof(Key) < end; p += sizeof(Key)) {
|
||||||
|
*reinterpret_cast<Key*>(p) ^= key;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; p < end; ++p || ++i) {
|
||||||
|
*p ^= *(reinterpret_cast<const char*>(&key) + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int Packet::localHeaderSize(bool isPartOfMessage) {
|
int Packet::localHeaderSize(bool isPartOfMessage) {
|
||||||
return sizeof(Packet::SequenceNumberAndBitField) +
|
return sizeof(Packet::SequenceNumberAndBitField) +
|
||||||
(isPartOfMessage ? sizeof(Packet::MessageNumberAndBitField) + sizeof(MessagePartNumber) : 0);
|
(isPartOfMessage ? sizeof(Packet::MessageNumberAndBitField) + sizeof(MessagePartNumber) : 0);
|
||||||
|
@ -69,44 +94,48 @@ Packet::Packet(std::unique_ptr<char[]> data, qint64 size, const HifiSockAddr& se
|
||||||
readHeader();
|
readHeader();
|
||||||
|
|
||||||
adjustPayloadStartAndCapacity(Packet::localHeaderSize(_isPartOfMessage), _payloadSize > 0);
|
adjustPayloadStartAndCapacity(Packet::localHeaderSize(_isPartOfMessage), _payloadSize > 0);
|
||||||
|
|
||||||
|
if (getObfuscationLevel() != Packet::NoObfuscation) {
|
||||||
|
#ifdef UDT_CONNECTION_DEBUG
|
||||||
|
QString debugString = "Unobfuscating packet %1 with level %2";
|
||||||
|
debugString = debugString.arg(QString::number((uint32_t)getSequenceNumber()),
|
||||||
|
QString::number(getObfuscationLevel()));
|
||||||
|
|
||||||
|
if (isPartOfMessage()) {
|
||||||
|
debugString += "\n";
|
||||||
|
debugString += " Message Number: %1, Part Number: %2.";
|
||||||
|
debugString = debugString.arg(QString::number(getMessageNumber()),
|
||||||
|
QString::number(getMessagePartNumber()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex("^Unobfuscating packet .*");
|
||||||
|
qDebug() << qPrintable(debugString);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
obfuscate(NoObfuscation); // Undo obfuscation
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Packet::Packet(const Packet& other) :
|
Packet::Packet(const Packet& other) : BasePacket(other) {
|
||||||
BasePacket(other)
|
copyMembers(other);
|
||||||
{
|
|
||||||
_isReliable = other._isReliable;
|
|
||||||
_isPartOfMessage = other._isPartOfMessage;
|
|
||||||
_sequenceNumber = other._sequenceNumber;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Packet& Packet::operator=(const Packet& other) {
|
Packet& Packet::operator=(const Packet& other) {
|
||||||
BasePacket::operator=(other);
|
BasePacket::operator=(other);
|
||||||
|
|
||||||
_isReliable = other._isReliable;
|
copyMembers(other);
|
||||||
_isPartOfMessage = other._isPartOfMessage;
|
|
||||||
_sequenceNumber = other._sequenceNumber;
|
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Packet::Packet(Packet&& other) :
|
Packet::Packet(Packet&& other) : BasePacket(std::move(other)) {
|
||||||
BasePacket(std::move(other))
|
copyMembers(other);
|
||||||
{
|
|
||||||
_isReliable = other._isReliable;
|
|
||||||
_isPartOfMessage = other._isPartOfMessage;
|
|
||||||
_sequenceNumber = other._sequenceNumber;
|
|
||||||
_packetPosition = other._packetPosition;
|
|
||||||
_messageNumber = other._messageNumber;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Packet& Packet::operator=(Packet&& other) {
|
Packet& Packet::operator=(Packet&& other) {
|
||||||
BasePacket::operator=(std::move(other));
|
BasePacket::operator=(std::move(other));
|
||||||
|
|
||||||
_isReliable = other._isReliable;
|
copyMembers(other);
|
||||||
_isPartOfMessage = other._isPartOfMessage;
|
|
||||||
_sequenceNumber = other._sequenceNumber;
|
|
||||||
_packetPosition = other._packetPosition;
|
|
||||||
_messageNumber = other._messageNumber;
|
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
@ -124,13 +153,27 @@ void Packet::writeSequenceNumber(SequenceNumber sequenceNumber) const {
|
||||||
writeHeader();
|
writeHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
static const uint32_t RELIABILITY_BIT_MASK = uint32_t(1) << (SEQUENCE_NUMBER_BITS - 2);
|
void Packet::obfuscate(ObfuscationLevel level) {
|
||||||
static const uint32_t MESSAGE_BIT_MASK = uint32_t(1) << (SEQUENCE_NUMBER_BITS - 3);
|
auto obfuscationKey = KEYS[getObfuscationLevel()] ^ KEYS[level]; // Undo old and apply new one.
|
||||||
static const uint32_t BIT_FIELD_MASK = CONTROL_BIT_MASK | RELIABILITY_BIT_MASK | MESSAGE_BIT_MASK;
|
if (obfuscationKey != 0) {
|
||||||
|
xorHelper(getData() + localHeaderSize(isPartOfMessage()),
|
||||||
|
getDataSize() - localHeaderSize(isPartOfMessage()), obfuscationKey);
|
||||||
|
|
||||||
static const uint8_t PACKET_POSITION_OFFSET = 30;
|
// Update members and header
|
||||||
static const uint32_t PACKET_POSITION_MASK = uint32_t(0x03) << PACKET_POSITION_OFFSET;
|
_obfuscationLevel = level;
|
||||||
static const uint32_t MESSAGE_NUMBER_MASK = ~PACKET_POSITION_MASK;
|
writeHeader();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Packet::copyMembers(const Packet& other) {
|
||||||
|
_isReliable = other._isReliable;
|
||||||
|
_isPartOfMessage = other._isPartOfMessage;
|
||||||
|
_obfuscationLevel = other._obfuscationLevel;
|
||||||
|
_sequenceNumber = other._sequenceNumber;
|
||||||
|
_packetPosition = other._packetPosition;
|
||||||
|
_messageNumber = other._messageNumber;
|
||||||
|
_messagePartNumber = other._messagePartNumber;
|
||||||
|
}
|
||||||
|
|
||||||
void Packet::readHeader() const {
|
void Packet::readHeader() const {
|
||||||
SequenceNumberAndBitField* seqNumBitField = reinterpret_cast<SequenceNumberAndBitField*>(_packet.get());
|
SequenceNumberAndBitField* seqNumBitField = reinterpret_cast<SequenceNumberAndBitField*>(_packet.get());
|
||||||
|
@ -139,10 +182,12 @@ void Packet::readHeader() const {
|
||||||
|
|
||||||
_isReliable = (bool) (*seqNumBitField & RELIABILITY_BIT_MASK); // Only keep reliability bit
|
_isReliable = (bool) (*seqNumBitField & RELIABILITY_BIT_MASK); // Only keep reliability bit
|
||||||
_isPartOfMessage = (bool) (*seqNumBitField & MESSAGE_BIT_MASK); // Only keep message bit
|
_isPartOfMessage = (bool) (*seqNumBitField & MESSAGE_BIT_MASK); // Only keep message bit
|
||||||
_sequenceNumber = SequenceNumber{ *seqNumBitField & ~BIT_FIELD_MASK }; // Remove the bit field
|
_obfuscationLevel = (ObfuscationLevel)((*seqNumBitField & OBFUSCATION_LEVEL_MASK) >> OBFUSCATION_LEVEL_OFFSET);
|
||||||
|
_sequenceNumber = SequenceNumber{ *seqNumBitField & SEQUENCE_NUMBER_MASK }; // Remove the bit field
|
||||||
|
|
||||||
if (_isPartOfMessage) {
|
if (_isPartOfMessage) {
|
||||||
MessageNumberAndBitField* messageNumberAndBitField = seqNumBitField + 1;
|
MessageNumberAndBitField* messageNumberAndBitField = seqNumBitField + 1;
|
||||||
|
|
||||||
_messageNumber = *messageNumberAndBitField & MESSAGE_NUMBER_MASK;
|
_messageNumber = *messageNumberAndBitField & MESSAGE_NUMBER_MASK;
|
||||||
_packetPosition = static_cast<PacketPosition>(*messageNumberAndBitField >> PACKET_POSITION_OFFSET);
|
_packetPosition = static_cast<PacketPosition>(*messageNumberAndBitField >> PACKET_POSITION_OFFSET);
|
||||||
|
|
||||||
|
@ -164,6 +209,10 @@ void Packet::writeHeader() const {
|
||||||
*seqNumBitField |= RELIABILITY_BIT_MASK;
|
*seqNumBitField |= RELIABILITY_BIT_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_obfuscationLevel != NoObfuscation) {
|
||||||
|
*seqNumBitField |= (_obfuscationLevel << OBFUSCATION_LEVEL_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
if (_isPartOfMessage) {
|
if (_isPartOfMessage) {
|
||||||
*seqNumBitField |= MESSAGE_BIT_MASK;
|
*seqNumBitField |= MESSAGE_BIT_MASK;
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,25 @@ namespace udt {
|
||||||
class Packet : public BasePacket {
|
class Packet : public BasePacket {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
// Packet Header Format
|
||||||
|
//
|
||||||
|
// 0 1 2 3
|
||||||
|
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// |C|R|M| O | Sequence Number |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | P | Message Number | Optional (only if M = 1)
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | Message Part Number | Optional (only if M = 1)
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
//
|
||||||
|
// C: Control bit
|
||||||
|
// R: Reliable bit
|
||||||
|
// M: Message bit
|
||||||
|
// O: Obfuscation level
|
||||||
|
// P: Position bits
|
||||||
|
|
||||||
|
|
||||||
// NOTE: The SequenceNumber is only actually 29 bits to leave room for a bit field
|
// NOTE: The SequenceNumber is only actually 29 bits to leave room for a bit field
|
||||||
using SequenceNumberAndBitField = uint32_t;
|
using SequenceNumberAndBitField = uint32_t;
|
||||||
|
|
||||||
|
@ -35,10 +54,18 @@ public:
|
||||||
|
|
||||||
// Use same size as MessageNumberAndBitField so we can use the enum with bitwise operations
|
// Use same size as MessageNumberAndBitField so we can use the enum with bitwise operations
|
||||||
enum PacketPosition : MessageNumberAndBitField {
|
enum PacketPosition : MessageNumberAndBitField {
|
||||||
ONLY = 0x0,
|
ONLY = 0x0, // 00
|
||||||
FIRST = 0x2,
|
FIRST = 0x2, // 10
|
||||||
MIDDLE = 0x3,
|
MIDDLE = 0x3, // 11
|
||||||
LAST = 0x1
|
LAST = 0x1 // 01
|
||||||
|
};
|
||||||
|
|
||||||
|
// Use same size as SequenceNumberAndBitField so we can use the enum with bitwise operations
|
||||||
|
enum ObfuscationLevel : SequenceNumberAndBitField {
|
||||||
|
NoObfuscation = 0x0, // 00
|
||||||
|
ObfuscationL1 = 0x1, // 01
|
||||||
|
ObfuscationL2 = 0x2, // 10
|
||||||
|
ObfuscationL3 = 0x3, // 11
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::unique_ptr<Packet> create(qint64 size = -1, bool isReliable = false, bool isPartOfMessage = false);
|
static std::unique_ptr<Packet> create(qint64 size = -1, bool isReliable = false, bool isPartOfMessage = false);
|
||||||
|
@ -57,6 +84,7 @@ public:
|
||||||
bool isPartOfMessage() const { return _isPartOfMessage; }
|
bool isPartOfMessage() const { return _isPartOfMessage; }
|
||||||
bool isReliable() const { return _isReliable; }
|
bool isReliable() const { return _isReliable; }
|
||||||
|
|
||||||
|
ObfuscationLevel getObfuscationLevel() const { return _obfuscationLevel; }
|
||||||
SequenceNumber getSequenceNumber() const { return _sequenceNumber; }
|
SequenceNumber getSequenceNumber() const { return _sequenceNumber; }
|
||||||
MessageNumber getMessageNumber() const { return _messageNumber; }
|
MessageNumber getMessageNumber() const { return _messageNumber; }
|
||||||
PacketPosition getPacketPosition() const { return _packetPosition; }
|
PacketPosition getPacketPosition() const { return _packetPosition; }
|
||||||
|
@ -64,6 +92,7 @@ public:
|
||||||
|
|
||||||
void writeMessageNumber(MessageNumber messageNumber, PacketPosition position, MessagePartNumber messagePartNumber);
|
void writeMessageNumber(MessageNumber messageNumber, PacketPosition position, MessagePartNumber messagePartNumber);
|
||||||
void writeSequenceNumber(SequenceNumber sequenceNumber) const;
|
void writeSequenceNumber(SequenceNumber sequenceNumber) const;
|
||||||
|
void obfuscate(ObfuscationLevel level);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Packet(qint64 size, bool isReliable = false, bool isPartOfMessage = false);
|
Packet(qint64 size, bool isReliable = false, bool isPartOfMessage = false);
|
||||||
|
@ -76,6 +105,8 @@ protected:
|
||||||
Packet& operator=(Packet&& other);
|
Packet& operator=(Packet&& other);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void copyMembers(const Packet& other);
|
||||||
|
|
||||||
// Header readers - these read data to member variables after pulling packet off wire
|
// Header readers - these read data to member variables after pulling packet off wire
|
||||||
void readHeader() const;
|
void readHeader() const;
|
||||||
void writeHeader() const;
|
void writeHeader() const;
|
||||||
|
@ -83,6 +114,7 @@ private:
|
||||||
// Simple holders to prevent multiple reading and bitwise ops
|
// Simple holders to prevent multiple reading and bitwise ops
|
||||||
mutable bool _isReliable { false };
|
mutable bool _isReliable { false };
|
||||||
mutable bool _isPartOfMessage { false };
|
mutable bool _isPartOfMessage { false };
|
||||||
|
mutable ObfuscationLevel _obfuscationLevel { NoObfuscation };
|
||||||
mutable SequenceNumber _sequenceNumber { 0 };
|
mutable SequenceNumber _sequenceNumber { 0 };
|
||||||
mutable MessageNumber _messageNumber { 0 };
|
mutable MessageNumber _messageNumber { 0 };
|
||||||
mutable PacketPosition _packetPosition { PacketPosition::ONLY };
|
mutable PacketPosition _packetPosition { PacketPosition::ONLY };
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
using namespace udt;
|
using namespace udt;
|
||||||
|
|
||||||
MessageNumber PacketQueue::getNextMessageNumber() {
|
MessageNumber PacketQueue::getNextMessageNumber() {
|
||||||
static const MessageNumber MAX_MESSAGE_NUMBER = MessageNumber(1) << MESSAGE_NUMBER_BITS;
|
static const MessageNumber MAX_MESSAGE_NUMBER = MessageNumber(1) << MESSAGE_NUMBER_SIZE;
|
||||||
_currentMessageNumber = (_currentMessageNumber + 1) % MAX_MESSAGE_NUMBER;
|
_currentMessageNumber = (_currentMessageNumber + 1) % MAX_MESSAGE_NUMBER;
|
||||||
return _currentMessageNumber;
|
return _currentMessageNumber;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <QtCore/QDateTime>
|
#include <QtCore/QDateTime>
|
||||||
#include <QtCore/QThread>
|
#include <QtCore/QThread>
|
||||||
|
|
||||||
|
#include <LogHandler.h>
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
|
|
||||||
#include "../NetworkLogging.h"
|
#include "../NetworkLogging.h"
|
||||||
|
@ -225,7 +226,9 @@ void SendQueue::sendNewPacketAndAddToSentList(std::unique_ptr<Packet> newPacket,
|
||||||
{
|
{
|
||||||
// Insert the packet we have just sent in the sent list
|
// Insert the packet we have just sent in the sent list
|
||||||
QWriteLocker locker(&_sentLock);
|
QWriteLocker locker(&_sentLock);
|
||||||
_sentPackets[newPacket->getSequenceNumber()].swap(newPacket);
|
auto& entry = _sentPackets[newPacket->getSequenceNumber()];
|
||||||
|
entry.first = 0; // No resend
|
||||||
|
entry.second.swap(newPacket);
|
||||||
}
|
}
|
||||||
Q_ASSERT_X(!newPacket, "SendQueue::sendNewPacketAndAddToSentList()", "Overriden packet in sent list");
|
Q_ASSERT_X(!newPacket, "SendQueue::sendNewPacketAndAddToSentList()", "Overriden packet in sent list");
|
||||||
|
|
||||||
|
@ -354,14 +357,46 @@ bool SendQueue::maybeResendPacket() {
|
||||||
auto it = _sentPackets.find(resendNumber);
|
auto it = _sentPackets.find(resendNumber);
|
||||||
|
|
||||||
if (it != _sentPackets.end()) {
|
if (it != _sentPackets.end()) {
|
||||||
|
auto& entry = it->second;
|
||||||
// we found the packet - grab it
|
// we found the packet - grab it
|
||||||
auto& resendPacket = *(it->second);
|
auto& resendPacket = *(entry.second);
|
||||||
|
++entry.first; // Add 1 resend
|
||||||
|
|
||||||
|
Packet::ObfuscationLevel level = (Packet::ObfuscationLevel)(entry.first < 2 ? 0 : (entry.first - 2) % 4);
|
||||||
|
|
||||||
|
if (level != Packet::NoObfuscation) {
|
||||||
|
#ifdef UDT_CONNECTION_DEBUG
|
||||||
|
QString debugString = "Obfuscating packet %1 with level %2";
|
||||||
|
debugString = debugString.arg(QString::number((uint32_t)resendPacket.getSequenceNumber()),
|
||||||
|
QString::number(level));
|
||||||
|
if (resendPacket.isPartOfMessage()) {
|
||||||
|
debugString += "\n";
|
||||||
|
debugString += " Message Number: %1, Part Number: %2.";
|
||||||
|
debugString = debugString.arg(QString::number(resendPacket.getMessageNumber()),
|
||||||
|
QString::number(resendPacket.getMessagePartNumber()));
|
||||||
|
}
|
||||||
|
static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex("^Obfuscating packet .*");
|
||||||
|
qCritical() << qPrintable(debugString);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Create copy of the packet
|
||||||
|
auto packet = Packet::createCopy(resendPacket);
|
||||||
|
|
||||||
|
// unlock the sent packets
|
||||||
|
sentLocker.unlock();
|
||||||
|
|
||||||
|
// Obfuscate packet
|
||||||
|
packet->obfuscate(level);
|
||||||
|
|
||||||
|
// send it off
|
||||||
|
sendPacket(*packet);
|
||||||
|
} else {
|
||||||
// send it off
|
// send it off
|
||||||
sendPacket(resendPacket);
|
sendPacket(resendPacket);
|
||||||
|
|
||||||
// unlock the sent packets
|
// unlock the sent packets
|
||||||
sentLocker.unlock();
|
sentLocker.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
emit packetRetransmitted();
|
emit packetRetransmitted();
|
||||||
|
|
||||||
|
|
|
@ -126,7 +126,8 @@ private:
|
||||||
LossList _naks; // Sequence numbers of packets to resend
|
LossList _naks; // Sequence numbers of packets to resend
|
||||||
|
|
||||||
mutable QReadWriteLock _sentLock; // Protects the sent packet list
|
mutable QReadWriteLock _sentLock; // Protects the sent packet list
|
||||||
std::unordered_map<SequenceNumber, std::unique_ptr<Packet>> _sentPackets; // Packets waiting for ACK.
|
using PacketResendPair = std::pair<uint8_t, std::unique_ptr<Packet>>; // Number of resend + packet ptr
|
||||||
|
std::unordered_map<SequenceNumber, PacketResendPair> _sentPackets; // Packets waiting for ACK.
|
||||||
|
|
||||||
std::mutex _handshakeMutex; // Protects the handshake ACK condition_variable
|
std::mutex _handshakeMutex; // Protects the handshake ACK condition_variable
|
||||||
std::atomic<bool> _hasReceivedHandshakeACK { false }; // flag for receipt of handshake ACK from client
|
std::atomic<bool> _hasReceivedHandshakeACK { false }; // flag for receipt of handshake ACK from client
|
||||||
|
|
|
@ -24,9 +24,9 @@ public:
|
||||||
using Type = int32_t;
|
using Type = int32_t;
|
||||||
using UType = uint32_t;
|
using UType = uint32_t;
|
||||||
|
|
||||||
// Values are for 29 bit SequenceNumber
|
// Values are for 27 bit SequenceNumber
|
||||||
static const Type THRESHOLD = 0x0FFFFFFF; // threshold for comparing sequence numbers
|
static const Type THRESHOLD = 0x03FFFFFF; // threshold for comparing sequence numbers
|
||||||
static const Type MAX = 0x1FFFFFFF; // maximum sequence number used in UDT
|
static const Type MAX = 0x07FFFFFF; // maximum sequence number used in UDT
|
||||||
|
|
||||||
SequenceNumber() = default;
|
SequenceNumber() = default;
|
||||||
SequenceNumber(const SequenceNumber& other) : _value(other._value) {}
|
SequenceNumber(const SequenceNumber& other) : _value(other._value) {}
|
||||||
|
|
|
@ -217,6 +217,10 @@ public:
|
||||||
static const ID INVALID_ITEM_ID = 0;
|
static const ID INVALID_ITEM_ID = 0;
|
||||||
static const ItemCell INVALID_CELL = -1;
|
static const ItemCell INVALID_CELL = -1;
|
||||||
|
|
||||||
|
// Convenient function to clear an ID or check it s valid
|
||||||
|
static void clearID(ID& id) { id = INVALID_ITEM_ID; }
|
||||||
|
static bool isValidID(const ID id) { return id != INVALID_ITEM_ID; }
|
||||||
|
|
||||||
// Bound is the AABBox fully containing this item
|
// Bound is the AABBox fully containing this item
|
||||||
typedef AABox Bound;
|
typedef AABox Bound;
|
||||||
|
|
||||||
|
|
|
@ -112,6 +112,7 @@ void OffscreenUi::create(QOpenGLContext* context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void OffscreenUi::show(const QUrl& url, const QString& name, std::function<void(QQmlContext*, QObject*)> f) {
|
void OffscreenUi::show(const QUrl& url, const QString& name, std::function<void(QQmlContext*, QObject*)> f) {
|
||||||
|
emit showDesktop();
|
||||||
QQuickItem* item = getRootItem()->findChild<QQuickItem*>(name);
|
QQuickItem* item = getRootItem()->findChild<QQuickItem*>(name);
|
||||||
// First load?
|
// First load?
|
||||||
if (!item) {
|
if (!item) {
|
||||||
|
@ -127,6 +128,7 @@ void OffscreenUi::toggle(const QUrl& url, const QString& name, std::function<voi
|
||||||
QQuickItem* item = getRootItem()->findChild<QQuickItem*>(name);
|
QQuickItem* item = getRootItem()->findChild<QQuickItem*>(name);
|
||||||
// Already loaded?
|
// Already loaded?
|
||||||
if (item) {
|
if (item) {
|
||||||
|
emit showDesktop();
|
||||||
item->setVisible(!item->isVisible());
|
item->setVisible(!item->isVisible());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -134,6 +136,7 @@ void OffscreenUi::toggle(const QUrl& url, const QString& name, std::function<voi
|
||||||
load(url, f);
|
load(url, f);
|
||||||
item = getRootItem()->findChild<QQuickItem*>(name);
|
item = getRootItem()->findChild<QQuickItem*>(name);
|
||||||
if (item && !item->isVisible()) {
|
if (item && !item->isVisible()) {
|
||||||
|
emit showDesktop();
|
||||||
item->setVisible(true);
|
item->setVisible(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -439,6 +442,8 @@ void OffscreenUi::createDesktop(const QUrl& url) {
|
||||||
new VrMenu(this);
|
new VrMenu(this);
|
||||||
|
|
||||||
new KeyboardFocusHack();
|
new KeyboardFocusHack();
|
||||||
|
|
||||||
|
connect(_desktop, SIGNAL(showDesktop()), this, SLOT(showDesktop()));
|
||||||
}
|
}
|
||||||
|
|
||||||
QQuickItem* OffscreenUi::getDesktop() {
|
QQuickItem* OffscreenUi::getDesktop() {
|
||||||
|
|
|
@ -106,6 +106,9 @@ public:
|
||||||
// Compatibility with QInputDialog::getItem
|
// Compatibility with QInputDialog::getItem
|
||||||
static QString getItem(void *ignored, const QString & title, const QString & label, const QStringList & items, int current = 0, bool editable = true, bool * ok = 0, Qt::WindowFlags flags = 0, Qt::InputMethodHints inputMethodHints = Qt::ImhNone);
|
static QString getItem(void *ignored, const QString & title, const QString & label, const QStringList & items, int current = 0, bool editable = true, bool * ok = 0, Qt::WindowFlags flags = 0, Qt::InputMethodHints inputMethodHints = Qt::ImhNone);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void showDesktop();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString fileDialog(const QVariantMap& properties);
|
QString fileDialog(const QVariantMap& properties);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue