From 1d93b6ada0fa63f4c40ee8bec98125b0ec0187e5 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 17 Jan 2019 17:51:12 -0800 Subject: [PATCH 01/12] Support gpu timing on systems with disjoint timer extension --- .../src/gpu/gl/GLBackendQuery.cpp | 42 ++++++++++++++++--- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackendQuery.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendQuery.cpp index 61423bf970..7f61ca78f6 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackendQuery.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendQuery.cpp @@ -24,6 +24,17 @@ const uint32_t MAX_RANGE_QUERY_DEPTH = 10000; static bool timeElapsed = false; #endif +#if defined(USE_GLES) +static bool hasTimerExtension() { + static std::once_flag once; + static bool result = false; + std::call_once(once, [&] { + result = glGetQueryObjectui64vEXT != nullptr; + }); + return result; +} +#endif + void GLBackend::do_beginQuery(const Batch& batch, size_t paramOffset) { auto query = batch._queries.get(batch._params[paramOffset]._uint); GLQuery* glquery = syncGPUObject(*query); @@ -33,7 +44,11 @@ void GLBackend::do_beginQuery(const Batch& batch, size_t paramOffset) { ++_queryStage._rangeQueryDepth; glquery->_batchElapsedTimeBegin = std::chrono::high_resolution_clock::now(); -#if !defined(USE_GLES) +#if defined(USE_GLES) + if (hasTimerExtension()) { + glQueryCounterEXT(glquery->_beginqo, GL_TIMESTAMP_EXT); + } +#else if (timeElapsed) { if (_queryStage._rangeQueryDepth <= MAX_RANGE_QUERY_DEPTH) { glBeginQuery(GL_TIME_ELAPSED, glquery->_endqo); @@ -52,7 +67,11 @@ void GLBackend::do_endQuery(const Batch& batch, size_t paramOffset) { auto query = batch._queries.get(batch._params[paramOffset]._uint); GLQuery* glquery = syncGPUObject(*query); if (glquery) { -#if !defined(USE_GLES) +#if defined(USE_GLES) + if (hasTimerExtension()) { + glQueryCounterEXT(glquery->_endqo, GL_TIMESTAMP_EXT); + } +#else if (timeElapsed) { if (_queryStage._rangeQueryDepth <= MAX_RANGE_QUERY_DEPTH) { glEndQuery(GL_TIME_ELAPSED); @@ -79,7 +98,21 @@ void GLBackend::do_getQuery(const Batch& batch, size_t paramOffset) { if (glquery->_rangeQueryDepth > MAX_RANGE_QUERY_DEPTH) { query->triggerReturnHandler(glquery->_result, glquery->_batchElapsedTime); } else { -#if !defined(USE_GLES) +#if defined(USE_GLES) + glquery->_result = 0; + if (hasTimerExtension()) { + glGetQueryObjectui64vEXT(glquery->_endqo, GL_QUERY_RESULT_AVAILABLE, &glquery->_result); + if (glquery->_result == GL_TRUE) { + GLuint64 start, end; + glGetQueryObjectui64vEXT(glquery->_beginqo, GL_QUERY_RESULT, &start); + glGetQueryObjectui64vEXT(glquery->_endqo, GL_QUERY_RESULT, &end); + glquery->_result = end - start; + query->triggerReturnHandler(glquery->_result, glquery->_batchElapsedTime); + } + } else { + query->triggerReturnHandler(0, glquery->_batchElapsedTime); + } +#else glGetQueryObjectui64v(glquery->_endqo, GL_QUERY_RESULT_AVAILABLE, &glquery->_result); if (glquery->_result == GL_TRUE) { if (timeElapsed) { @@ -92,9 +125,6 @@ void GLBackend::do_getQuery(const Batch& batch, size_t paramOffset) { } query->triggerReturnHandler(glquery->_result, glquery->_batchElapsedTime); } -#else - // gles3 is not supporting true time query returns just the batch elapsed time - query->triggerReturnHandler(0, glquery->_batchElapsedTime); #endif (void)CHECK_GL_ERROR(); } From 93a91cdba2802a0b0f9fd6da063ac8bd0749e4eb Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 29 Jan 2019 15:40:45 -0800 Subject: [PATCH 02/12] webengine fileselector --- .../resources/qml/+webengine/Browser.qml | 275 ++++++++++++++++++ .../resources/qml/+webengine/InfoView.qml | 50 ++++ .../qml/+webengine/TabletBrowser.qml | 125 ++++++++ interface/resources/qml/Browser.qml | 69 +---- interface/resources/qml/InfoView.qml | 3 +- interface/resources/qml/TabletBrowser.qml | 97 +----- .../+webengine/FlickableWebViewCore.qml | 189 ++++++++++++ .../qml/controls/FlickableWebViewCore.qml | 141 +-------- .../resources/qml/controls/TabletWebView.qml | 1 - .../controlsUit/+webengine/BaseWebView.qml | 38 +++ .../resources/qml/controlsUit/BaseWebView.qml | 27 +- .../qml/controlsUit/ProxyWebView.qml | 30 ++ interface/resources/qml/controlsUit/qmldir | 1 + libraries/shared/src/shared/FileUtils.cpp | 4 + libraries/ui/src/ui/OffscreenQmlSurface.cpp | 5 +- 15 files changed, 730 insertions(+), 325 deletions(-) create mode 100644 interface/resources/qml/+webengine/Browser.qml create mode 100644 interface/resources/qml/+webengine/InfoView.qml create mode 100644 interface/resources/qml/+webengine/TabletBrowser.qml create mode 100644 interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml create mode 100644 interface/resources/qml/controlsUit/+webengine/BaseWebView.qml create mode 100644 interface/resources/qml/controlsUit/ProxyWebView.qml diff --git a/interface/resources/qml/+webengine/Browser.qml b/interface/resources/qml/+webengine/Browser.qml new file mode 100644 index 0000000000..52157bdf42 --- /dev/null +++ b/interface/resources/qml/+webengine/Browser.qml @@ -0,0 +1,275 @@ +import QtQuick 2.5 +import QtWebChannel 1.0 +import QtWebEngine 1.5 + +import controlsUit 1.0 +import stylesUit 1.0 +import "qrc:////qml//windows" + +ScrollingWindow { + id: root + HifiConstants { id: hifi } + //HifiStyles.HifiConstants { id: hifistyles } + title: "Browser" + resizable: true + destroyOnHidden: true + width: 800 + height: 600 + property variant permissionsBar: {'securityOrigin':'none','feature':'none'} + property alias url: webview.url + property alias webView: webview + + signal loadingChanged(int status) + + x: 100 + y: 100 + + Component.onCompleted: { + focus = true + shown = true + addressBar.text = webview.url + } + + function setProfile(profile) { + webview.profile = profile; + } + + function showPermissionsBar(){ + permissionsContainer.visible=true; + } + + function hidePermissionsBar(){ + permissionsContainer.visible=false; + } + + function allowPermissions(){ + webview.grantFeaturePermission(permissionsBar.securityOrigin, permissionsBar.feature, true); + hidePermissionsBar(); + } + + function setAutoAdd(auto) { + desktop.setAutoAdd(auto); + } + + Item { + id:item + width: pane.contentWidth + implicitHeight: pane.scrollHeight + + Row { + id: buttons + spacing: 4 + anchors.top: parent.top + anchors.topMargin: 8 + anchors.left: parent.left + anchors.leftMargin: 8 + HiFiGlyphs { + id: back; + enabled: webview.canGoBack; + text: hifi.glyphs.backward + color: enabled ? hifi.colors.text : hifi.colors.disabledText + size: 48 + MouseArea { anchors.fill: parent; onClicked: webview.goBack() } + } + + HiFiGlyphs { + id: forward; + enabled: webview.canGoForward; + text: hifi.glyphs.forward + color: enabled ? hifi.colors.text : hifi.colors.disabledText + size: 48 + MouseArea { anchors.fill: parent; onClicked: webview.goForward() } + } + + HiFiGlyphs { + id: reload; + enabled: webview.canGoForward; + text: webview.loading ? hifi.glyphs.close : hifi.glyphs.reload + color: enabled ? hifi.colors.text : hifi.colors.disabledText + size: 48 + MouseArea { anchors.fill: parent; onClicked: webview.goForward() } + } + + } + + Item { + id: border + height: 48 + anchors.top: parent.top + anchors.topMargin: 8 + anchors.right: parent.right + anchors.rightMargin: 8 + anchors.left: buttons.right + anchors.leftMargin: 8 + + Item { + id: barIcon + width: parent.height + height: parent.height + Image { + source: webview.icon; + x: (parent.height - height) / 2 + y: (parent.width - width) / 2 + sourceSize: Qt.size(width, height); + verticalAlignment: Image.AlignVCenter; + horizontalAlignment: Image.AlignHCenter + } + } + + TextField { + id: addressBar + anchors.right: parent.right + anchors.rightMargin: 8 + anchors.left: barIcon.right + anchors.leftMargin: 0 + anchors.verticalCenter: parent.verticalCenter + focus: true + colorScheme: hifi.colorSchemes.dark + placeholderText: "Enter URL" + Component.onCompleted: ScriptDiscoveryService.scriptsModelFilter.filterRegExp = new RegExp("^.*$", "i") + Keys.onPressed: { + switch(event.key) { + case Qt.Key_Enter: + case Qt.Key_Return: + event.accepted = true + if (text.indexOf("http") != 0) { + text = "http://" + text; + } + root.hidePermissionsBar(); + root.keyboardRaised = false; + webview.url = text; + break; + } + } + } + } + + Rectangle { + id:permissionsContainer + visible:false + color: "#000000" + width: parent.width + anchors.top: buttons.bottom + height:40 + z:100 + gradient: Gradient { + GradientStop { position: 0.0; color: "black" } + GradientStop { position: 1.0; color: "grey" } + } + + RalewayLight { + id: permissionsInfo + anchors.right:permissionsRow.left + anchors.rightMargin: 32 + anchors.topMargin:8 + anchors.top:parent.top + text: "This site wants to use your microphone/camera" + size: 18 + color: hifi.colors.white + } + + Row { + id: permissionsRow + spacing: 4 + anchors.top:parent.top + anchors.topMargin: 8 + anchors.right: parent.right + visible: true + z:101 + + Button { + id:allow + text: "Allow" + color: hifi.buttons.blue + colorScheme: root.colorScheme + width: 120 + enabled: true + onClicked: root.allowPermissions(); + z:101 + } + + Button { + id:block + text: "Block" + color: hifi.buttons.red + colorScheme: root.colorScheme + width: 120 + enabled: true + onClicked: root.hidePermissionsBar(); + z:101 + } + } + } + + WebView { + id: webview + url: "https://highfidelity.com/" + profile: FileTypeProfile; + + // Create a global EventBridge object for raiseAndLowerKeyboard. + WebEngineScript { + id: createGlobalEventBridge + sourceCode: eventBridgeJavaScriptToInject + injectionPoint: WebEngineScript.Deferred + 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 ] + + anchors.top: buttons.bottom + anchors.topMargin: 8 + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + + onFeaturePermissionRequested: { + if (feature == 2) { // QWebEnginePage::MediaAudioCapture + grantFeaturePermission(securityOrigin, feature, true); + } else { + permissionsBar.securityOrigin = securityOrigin; + permissionsBar.feature = feature; + root.showPermissionsBar(); + } + } + + onLoadingChanged: { + if (loadRequest.status === WebEngineView.LoadSucceededStatus) { + addressBar.text = loadRequest.url + } + root.loadingChanged(loadRequest.status); + } + + onWindowCloseRequested: { + root.destroy(); + } + + Component.onCompleted: { + webChannel.registerObject("eventBridge", eventBridge); + webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper); + desktop.initWebviewProfileHandlers(webview.profile); + } + } + + } // item + + + Keys.onPressed: { + switch(event.key) { + case Qt.Key_L: + if (event.modifiers == Qt.ControlModifier) { + event.accepted = true + addressBar.selectAll() + addressBar.forceActiveFocus() + } + break; + } + } +} // dialog diff --git a/interface/resources/qml/+webengine/InfoView.qml b/interface/resources/qml/+webengine/InfoView.qml new file mode 100644 index 0000000000..eb190c3c45 --- /dev/null +++ b/interface/resources/qml/+webengine/InfoView.qml @@ -0,0 +1,50 @@ +// +// InfoView.qml +// +// Created by Bradley Austin Davis on 27 Apr 2015 +// Copyright 2015 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.5 +import Hifi 1.0 as Hifi + +import controlsUit 1.0 +import "qrc:////qml//windows" as Windows + +Windows.ScrollingWindow { + id: root + width: 800 + height: 800 + resizable: true + + Hifi.InfoView { + id: infoView + width: pane.contentWidth + implicitHeight: pane.scrollHeight + + WebView { + id: webview + objectName: "WebView" + anchors.fill: parent + url: infoView.url + } + } + + Component.onCompleted: { + centerWindow(root); + } + + onVisibleChanged: { + if (visible) { + centerWindow(root); + } + } + + function centerWindow() { + desktop.centerOnVisible(root); + } + +} diff --git a/interface/resources/qml/+webengine/TabletBrowser.qml b/interface/resources/qml/+webengine/TabletBrowser.qml new file mode 100644 index 0000000000..720a904231 --- /dev/null +++ b/interface/resources/qml/+webengine/TabletBrowser.qml @@ -0,0 +1,125 @@ +import QtQuick 2.5 +import QtWebChannel 1.0 +import QtWebEngine 1.5 + +import "controls" +import controlsUit 1.0 as HifiControls +import "styles" as HifiStyles +import stylesUit 1.0 +import "windows" + +Item { + id: root + HifiConstants { id: hifi } + HifiStyles.HifiConstants { id: hifistyles } + + height: 600 + property variant permissionsBar: {'securityOrigin':'none','feature':'none'} + property alias url: webview.url + + property bool canGoBack: webview.canGoBack + property bool canGoForward: webview.canGoForward + + + signal loadingChanged(int status) + + x: 0 + y: 0 + + function setProfile(profile) { + webview.profile = profile; + } + + WebEngineView { + id: webview + objectName: "webEngineView" + x: 0 + y: 0 + width: parent.width + height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height + + profile: HFWebEngineProfile; + + property string userScriptUrl: "" + + // creates a global EventBridge object. + WebEngineScript { + id: createGlobalEventBridge + sourceCode: eventBridgeJavaScriptToInject + injectionPoint: WebEngineScript.DocumentCreation + worldId: WebEngineScript.MainWorld + } + + // detects when to raise and lower virtual keyboard + WebEngineScript { + id: raiseAndLowerKeyboard + injectionPoint: WebEngineScript.Deferred + sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js" + worldId: WebEngineScript.MainWorld + } + + // User script. + WebEngineScript { + id: userScript + sourceUrl: webview.userScriptUrl + injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished. + worldId: WebEngineScript.MainWorld + } + + userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ] + + property string newUrl: "" + + Component.onCompleted: { + webChannel.registerObject("eventBridge", eventBridge); + webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper); + + // Ensure the JS from the web-engine makes it to our logging + webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) { + console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message); + }); + + webview.profile.httpUserAgent = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36"; + web.address = url; + } + + onFeaturePermissionRequested: { + grantFeaturePermission(securityOrigin, feature, true); + } + + onLoadingChanged: { + keyboardRaised = false; + punctuationMode = false; + keyboard.resetShiftMode(false); + + // Required to support clicking on "hifi://" links + if (WebEngineView.LoadStartedStatus == loadRequest.status) { + urlAppend(loadRequest.url.toString()) + var url = loadRequest.url.toString(); + if (urlHandler.canHandleUrl(url)) { + if (urlHandler.handleUrl(url)) { + root.stop(); + } + } + } + } + + onNewViewRequested: { + request.openIn(webView); + } + + HifiControls.WebSpinner { } + } + + Keys.onPressed: { + switch(event.key) { + case Qt.Key_L: + if (event.modifiers == Qt.ControlModifier) { + event.accepted = true + addressBar.selectAll() + addressBar.forceActiveFocus() + } + break; + } + } +} diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index 4ddee15a10..78ffb51e67 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -1,16 +1,14 @@ import QtQuick 2.5 -import QtWebChannel 1.0 -import QtWebEngine 1.5 import controlsUit 1.0 -import "styles" as HifiStyles import stylesUit 1.0 + import "windows" ScrollingWindow { id: root HifiConstants { id: hifi } - HifiStyles.HifiConstants { id: hifistyles } + //HifiStyles.HifiConstants { id: hifistyles } title: "Browser" resizable: true destroyOnHidden: true @@ -32,7 +30,6 @@ ScrollingWindow { } function setProfile(profile) { - webview.profile = profile; } function showPermissionsBar(){ @@ -44,7 +41,6 @@ ScrollingWindow { } function allowPermissions(){ - webview.grantFeaturePermission(permissionsBar.securityOrigin, permissionsBar.feature, true); hidePermissionsBar(); } @@ -68,7 +64,7 @@ ScrollingWindow { id: back; enabled: webview.canGoBack; text: hifi.glyphs.backward - color: enabled ? hifistyles.colors.text : hifistyles.colors.disabledText + color: enabled ? hifi.colors.text : hifi.colors.disabledText size: 48 MouseArea { anchors.fill: parent; onClicked: webview.goBack() } } @@ -77,7 +73,7 @@ ScrollingWindow { id: forward; enabled: webview.canGoForward; text: hifi.glyphs.forward - color: enabled ? hifistyles.colors.text : hifistyles.colors.disabledText + color: enabled ? hifi.colors.text : hifi.colors.disabledText size: 48 MouseArea { anchors.fill: parent; onClicked: webview.goForward() } } @@ -86,7 +82,7 @@ ScrollingWindow { id: reload; enabled: webview.canGoForward; text: webview.loading ? hifi.glyphs.close : hifi.glyphs.reload - color: enabled ? hifistyles.colors.text : hifistyles.colors.disabledText + color: enabled ? hifi.colors.text : hifi.colors.disabledText size: 48 MouseArea { anchors.fill: parent; onClicked: webview.goForward() } } @@ -202,61 +198,10 @@ ScrollingWindow { } } - WebView { + ProxyWebView { id: webview + anchors.centerIn: parent url: "https://highfidelity.com/" - profile: FileTypeProfile; - - // Create a global EventBridge object for raiseAndLowerKeyboard. - WebEngineScript { - id: createGlobalEventBridge - sourceCode: eventBridgeJavaScriptToInject - injectionPoint: WebEngineScript.Deferred - 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 ] - - anchors.top: buttons.bottom - anchors.topMargin: 8 - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.right: parent.right - - onFeaturePermissionRequested: { - if (feature == 2) { // QWebEnginePage::MediaAudioCapture - grantFeaturePermission(securityOrigin, feature, true); - } else { - permissionsBar.securityOrigin = securityOrigin; - permissionsBar.feature = feature; - root.showPermissionsBar(); - } - } - - onLoadingChanged: { - if (loadRequest.status === WebEngineView.LoadSucceededStatus) { - addressBar.text = loadRequest.url - } - root.loadingChanged(loadRequest.status); - } - - onWindowCloseRequested: { - root.destroy(); - } - - Component.onCompleted: { - webChannel.registerObject("eventBridge", eventBridge); - webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper); - desktop.initWebviewProfileHandlers(webview.profile); - } } } // item diff --git a/interface/resources/qml/InfoView.qml b/interface/resources/qml/InfoView.qml index 8c5900b4c3..5c2c7fcff9 100644 --- a/interface/resources/qml/InfoView.qml +++ b/interface/resources/qml/InfoView.qml @@ -19,13 +19,12 @@ Windows.ScrollingWindow { width: 800 height: 800 resizable: true - Hifi.InfoView { id: infoView width: pane.contentWidth implicitHeight: pane.scrollHeight - WebView { + ProxyWebView { id: webview objectName: "WebView" anchors.fill: parent diff --git a/interface/resources/qml/TabletBrowser.qml b/interface/resources/qml/TabletBrowser.qml index 720a904231..f83a9c81a5 100644 --- a/interface/resources/qml/TabletBrowser.qml +++ b/interface/resources/qml/TabletBrowser.qml @@ -1,17 +1,11 @@ import QtQuick 2.5 -import QtWebChannel 1.0 -import QtWebEngine 1.5 -import "controls" import controlsUit 1.0 as HifiControls -import "styles" as HifiStyles import stylesUit 1.0 -import "windows" Item { id: root HifiConstants { id: hifi } - HifiStyles.HifiConstants { id: hifistyles } height: 600 property variant permissionsBar: {'securityOrigin':'none','feature':'none'} @@ -30,96 +24,9 @@ Item { webview.profile = profile; } - WebEngineView { + HifiControls.ProxyWebView { id: webview - objectName: "webEngineView" - x: 0 - y: 0 width: parent.width - height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height - - profile: HFWebEngineProfile; - - property string userScriptUrl: "" - - // creates a global EventBridge object. - WebEngineScript { - id: createGlobalEventBridge - sourceCode: eventBridgeJavaScriptToInject - injectionPoint: WebEngineScript.DocumentCreation - worldId: WebEngineScript.MainWorld - } - - // detects when to raise and lower virtual keyboard - WebEngineScript { - id: raiseAndLowerKeyboard - injectionPoint: WebEngineScript.Deferred - sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js" - worldId: WebEngineScript.MainWorld - } - - // User script. - WebEngineScript { - id: userScript - sourceUrl: webview.userScriptUrl - injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished. - worldId: WebEngineScript.MainWorld - } - - userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ] - - property string newUrl: "" - - Component.onCompleted: { - webChannel.registerObject("eventBridge", eventBridge); - webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper); - - // Ensure the JS from the web-engine makes it to our logging - webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) { - console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message); - }); - - webview.profile.httpUserAgent = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36"; - web.address = url; - } - - onFeaturePermissionRequested: { - grantFeaturePermission(securityOrigin, feature, true); - } - - onLoadingChanged: { - keyboardRaised = false; - punctuationMode = false; - keyboard.resetShiftMode(false); - - // Required to support clicking on "hifi://" links - if (WebEngineView.LoadStartedStatus == loadRequest.status) { - urlAppend(loadRequest.url.toString()) - var url = loadRequest.url.toString(); - if (urlHandler.canHandleUrl(url)) { - if (urlHandler.handleUrl(url)) { - root.stop(); - } - } - } - } - - onNewViewRequested: { - request.openIn(webView); - } - - HifiControls.WebSpinner { } - } - - Keys.onPressed: { - switch(event.key) { - case Qt.Key_L: - if (event.modifiers == Qt.ControlModifier) { - event.accepted = true - addressBar.selectAll() - addressBar.forceActiveFocus() - } - break; - } + height: parent.height } } diff --git a/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml b/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml new file mode 100644 index 0000000000..823d0107a2 --- /dev/null +++ b/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml @@ -0,0 +1,189 @@ +import QtQuick 2.7 +import QtWebEngine 1.5 +import QtWebChannel 1.0 + +import QtQuick.Controls 2.2 + +import stylesUit 1.0 as StylesUIt + +Item { + id: flick + + property alias url: webViewCore.url + property alias canGoBack: webViewCore.canGoBack + property alias webViewCore: webViewCore + property alias webViewCoreProfile: webViewCore.profile + property string webViewCoreUserAgent + + property string userScriptUrl: "" + property string urlTag: "noDownload=false"; + + signal newViewRequestedCallback(var request) + signal loadingChangedCallback(var loadRequest) + + + width: parent.width + + property bool interactive: false + + property bool blurOnCtrlShift: true + + StylesUIt.HifiConstants { + id: hifi + } + + function stop() { + webViewCore.stop(); + } + + Timer { + id: delayedUnfocuser + repeat: false + interval: 200 + onTriggered: { + + // The idea behind this is to delay unfocusing, so that fast lower/raise will not result actual unfocusing. + // Fast lower/raise happens every time keyboard is being re-raised (see the code below in OffscreenQmlSurface::setKeyboardRaised) + // + // if (raised) { + // item->setProperty("keyboardRaised", QVariant(!raised)); + // } + // + // item->setProperty("keyboardRaised", QVariant(raised)); + // + + webViewCore.runJavaScript("if (document.activeElement) document.activeElement.blur();", function(result) { + console.log('unfocus completed: ', result); + }); + } + } + + function unfocus() { + delayedUnfocuser.start(); + } + + function stopUnfocus() { + delayedUnfocuser.stop(); + } + + function onLoadingChanged(loadRequest) { + if (WebEngineView.LoadStartedStatus === loadRequest.status) { + + // Required to support clicking on "hifi://" links + var url = loadRequest.url.toString(); + url = (url.indexOf("?") >= 0) ? url + urlTag : url + "?" + urlTag; + if (urlHandler.canHandleUrl(url)) { + if (urlHandler.handleUrl(url)) { + webViewCore.stop(); + } + } + } + + if (WebEngineView.LoadFailedStatus === loadRequest.status) { + console.log("Tablet WebEngineView failed to load url: " + loadRequest.url.toString()); + } + + if (WebEngineView.LoadSucceededStatus === loadRequest.status) { + //disable Chromium's scroll bars + } + } + + WebEngineView { + id: webViewCore + + width: parent.width + height: parent.height + + profile: HFWebEngineProfile; + settings.pluginsEnabled: true + settings.touchIconsEnabled: true + settings.allowRunningInsecureContent: true + + // creates a global EventBridge object. + WebEngineScript { + id: createGlobalEventBridge + sourceCode: eventBridgeJavaScriptToInject + injectionPoint: WebEngineScript.DocumentCreation + worldId: WebEngineScript.MainWorld + } + + // detects when to raise and lower virtual keyboard + WebEngineScript { + id: raiseAndLowerKeyboard + injectionPoint: WebEngineScript.Deferred + sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js" + worldId: WebEngineScript.MainWorld + } + + // User script. + WebEngineScript { + id: userScript + sourceUrl: flick.userScriptUrl + injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished. + worldId: WebEngineScript.MainWorld + } + + userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ] + + Component.onCompleted: { + webChannel.registerObject("eventBridge", eventBridge); + webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper); + + if (webViewCoreUserAgent !== undefined) { + webViewCore.profile.httpUserAgent = webViewCoreUserAgent + } else { + webViewCore.profile.httpUserAgent += " (HighFidelityInterface)"; + } + // Ensure the JS from the web-engine makes it to our logging + webViewCore.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) { + console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message); + }); + } + + onFeaturePermissionRequested: { + grantFeaturePermission(securityOrigin, feature, true); + } + + //disable popup + onContextMenuRequested: { + request.accepted = true; + } + + onNewViewRequested: { + newViewRequestedCallback(request) + } + + // Prior to 5.10, the WebEngineView loading property is true during initial page loading and then stays false + // as in-page javascript adds more html content. However, in 5.10 there is a bug such that adding html turns + // loading true, and never turns it false again. safeLoading provides a workaround, but it should be removed + // when QT fixes this. + property bool safeLoading: false + property bool loadingLatched: false + property var loadingRequest: null + onLoadingChanged: { + webViewCore.loadingRequest = loadRequest; + webViewCore.safeLoading = webViewCore.loading && !loadingLatched; + webViewCore.loadingLatched |= webViewCore.loading; + } + onSafeLoadingChanged: { + flick.onLoadingChanged(webViewCore.loadingRequest) + loadingChangedCallback(webViewCore.loadingRequest) + } + } + + AnimatedImage { + //anchoring doesnt works here when changing content size + x: flick.width/2 - width/2 + y: flick.height/2 - height/2 + source: "../../icons/loader-snake-64-w.gif" + visible: webViewCore.safeLoading && /^(http.*|)$/i.test(webViewCore.url.toString()) + playing: visible + z: 10000 + } + + Keys.onPressed: { + if (blurOnCtrlShift && (event.modifiers & Qt.ShiftModifier) && (event.modifiers & Qt.ControlModifier)) { + webViewCore.focus = false; + } + } +} diff --git a/interface/resources/qml/controls/FlickableWebViewCore.qml b/interface/resources/qml/controls/FlickableWebViewCore.qml index 823d0107a2..a844c8b624 100644 --- a/interface/resources/qml/controls/FlickableWebViewCore.qml +++ b/interface/resources/qml/controls/FlickableWebViewCore.qml @@ -1,10 +1,9 @@ import QtQuick 2.7 -import QtWebEngine 1.5 -import QtWebChannel 1.0 import QtQuick.Controls 2.2 import stylesUit 1.0 as StylesUIt +import controlsUit 1.0 as ControlsUit Item { id: flick @@ -33,157 +32,21 @@ Item { } function stop() { - webViewCore.stop(); - } - Timer { - id: delayedUnfocuser - repeat: false - interval: 200 - onTriggered: { - - // The idea behind this is to delay unfocusing, so that fast lower/raise will not result actual unfocusing. - // Fast lower/raise happens every time keyboard is being re-raised (see the code below in OffscreenQmlSurface::setKeyboardRaised) - // - // if (raised) { - // item->setProperty("keyboardRaised", QVariant(!raised)); - // } - // - // item->setProperty("keyboardRaised", QVariant(raised)); - // - - webViewCore.runJavaScript("if (document.activeElement) document.activeElement.blur();", function(result) { - console.log('unfocus completed: ', result); - }); - } } function unfocus() { - delayedUnfocuser.start(); } function stopUnfocus() { - delayedUnfocuser.stop(); } function onLoadingChanged(loadRequest) { - if (WebEngineView.LoadStartedStatus === loadRequest.status) { - - // Required to support clicking on "hifi://" links - var url = loadRequest.url.toString(); - url = (url.indexOf("?") >= 0) ? url + urlTag : url + "?" + urlTag; - if (urlHandler.canHandleUrl(url)) { - if (urlHandler.handleUrl(url)) { - webViewCore.stop(); - } - } - } - - if (WebEngineView.LoadFailedStatus === loadRequest.status) { - console.log("Tablet WebEngineView failed to load url: " + loadRequest.url.toString()); - } - - if (WebEngineView.LoadSucceededStatus === loadRequest.status) { - //disable Chromium's scroll bars - } } - WebEngineView { + ControlsUit.ProxyWebView { id: webViewCore - width: parent.width height: parent.height - - profile: HFWebEngineProfile; - settings.pluginsEnabled: true - settings.touchIconsEnabled: true - settings.allowRunningInsecureContent: true - - // creates a global EventBridge object. - WebEngineScript { - id: createGlobalEventBridge - sourceCode: eventBridgeJavaScriptToInject - injectionPoint: WebEngineScript.DocumentCreation - worldId: WebEngineScript.MainWorld - } - - // detects when to raise and lower virtual keyboard - WebEngineScript { - id: raiseAndLowerKeyboard - injectionPoint: WebEngineScript.Deferred - sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js" - worldId: WebEngineScript.MainWorld - } - - // User script. - WebEngineScript { - id: userScript - sourceUrl: flick.userScriptUrl - injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished. - worldId: WebEngineScript.MainWorld - } - - userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ] - - Component.onCompleted: { - webChannel.registerObject("eventBridge", eventBridge); - webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper); - - if (webViewCoreUserAgent !== undefined) { - webViewCore.profile.httpUserAgent = webViewCoreUserAgent - } else { - webViewCore.profile.httpUserAgent += " (HighFidelityInterface)"; - } - // Ensure the JS from the web-engine makes it to our logging - webViewCore.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) { - console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message); - }); - } - - onFeaturePermissionRequested: { - grantFeaturePermission(securityOrigin, feature, true); - } - - //disable popup - onContextMenuRequested: { - request.accepted = true; - } - - onNewViewRequested: { - newViewRequestedCallback(request) - } - - // Prior to 5.10, the WebEngineView loading property is true during initial page loading and then stays false - // as in-page javascript adds more html content. However, in 5.10 there is a bug such that adding html turns - // loading true, and never turns it false again. safeLoading provides a workaround, but it should be removed - // when QT fixes this. - property bool safeLoading: false - property bool loadingLatched: false - property var loadingRequest: null - onLoadingChanged: { - webViewCore.loadingRequest = loadRequest; - webViewCore.safeLoading = webViewCore.loading && !loadingLatched; - webViewCore.loadingLatched |= webViewCore.loading; - } - onSafeLoadingChanged: { - flick.onLoadingChanged(webViewCore.loadingRequest) - loadingChangedCallback(webViewCore.loadingRequest) - } - } - - AnimatedImage { - //anchoring doesnt works here when changing content size - x: flick.width/2 - width/2 - y: flick.height/2 - height/2 - source: "../../icons/loader-snake-64-w.gif" - visible: webViewCore.safeLoading && /^(http.*|)$/i.test(webViewCore.url.toString()) - playing: visible - z: 10000 - } - - Keys.onPressed: { - if (blurOnCtrlShift && (event.modifiers & Qt.ShiftModifier) && (event.modifiers & Qt.ControlModifier)) { - webViewCore.focus = false; - } } } diff --git a/interface/resources/qml/controls/TabletWebView.qml b/interface/resources/qml/controls/TabletWebView.qml index 3959dbf01b..9cbbd48a22 100644 --- a/interface/resources/qml/controls/TabletWebView.qml +++ b/interface/resources/qml/controls/TabletWebView.qml @@ -1,5 +1,4 @@ import QtQuick 2.7 -import QtWebEngine 1.5 import controlsUit 1.0 as HiFiControls import "../styles" as HifiStyles import stylesUit 1.0 diff --git a/interface/resources/qml/controlsUit/+webengine/BaseWebView.qml b/interface/resources/qml/controlsUit/+webengine/BaseWebView.qml new file mode 100644 index 0000000000..fdd9c12220 --- /dev/null +++ b/interface/resources/qml/controlsUit/+webengine/BaseWebView.qml @@ -0,0 +1,38 @@ +// +// WebView.qml +// +// Created by Bradley Austin Davis on 12 Jan 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.7 +import QtWebEngine 1.5 + +WebEngineView { + id: root + + Component.onCompleted: { + console.log("Connecting JS messaging to Hifi Logging") + // Ensure the JS from the web-engine makes it to our logging + root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) { + console.log("Web Window JS message: " + sourceID + " " + lineNumber + " " + message); + }); + } + + onLoadingChanged: { + // Required to support clicking on "hifi://" links + if (WebEngineView.LoadStartedStatus == loadRequest.status) { + var url = loadRequest.url.toString(); + if (urlHandler.canHandleUrl(url)) { + if (urlHandler.handleUrl(url)) { + root.stop(); + } + } + } + } + + WebSpinner { } +} diff --git a/interface/resources/qml/controlsUit/BaseWebView.qml b/interface/resources/qml/controlsUit/BaseWebView.qml index fdd9c12220..52b2d1f3db 100644 --- a/interface/resources/qml/controlsUit/BaseWebView.qml +++ b/interface/resources/qml/controlsUit/BaseWebView.qml @@ -8,31 +8,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.7 -import QtWebEngine 1.5 +import "." -WebEngineView { +ProxyWebView { id: root - - Component.onCompleted: { - console.log("Connecting JS messaging to Hifi Logging") - // Ensure the JS from the web-engine makes it to our logging - root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) { - console.log("Web Window JS message: " + sourceID + " " + lineNumber + " " + message); - }); - } - - onLoadingChanged: { - // Required to support clicking on "hifi://" links - if (WebEngineView.LoadStartedStatus == loadRequest.status) { - var url = loadRequest.url.toString(); - if (urlHandler.canHandleUrl(url)) { - if (urlHandler.handleUrl(url)) { - root.stop(); - } - } - } - } - - WebSpinner { } } diff --git a/interface/resources/qml/controlsUit/ProxyWebView.qml b/interface/resources/qml/controlsUit/ProxyWebView.qml new file mode 100644 index 0000000000..adcc472831 --- /dev/null +++ b/interface/resources/qml/controlsUit/ProxyWebView.qml @@ -0,0 +1,30 @@ +import QtQuick 2.7 +import stylesUit 1.0 + +Rectangle { + HifiConstants { + id: hifi + } + + color: hifi.colors.darkGray + + signal onNewViewRequested(); + + property string url: ""; + property bool canGoBack: false + property bool canGoForward: false + property string icon: "" + property var profile: {} + + property bool safeLoading: false + property bool loadingLatched: false + property var loadingRequest: null + + + Text { + anchors.centerIn: parent + text: "This feature is not supported" + font.pixelSize: 32 + color: hifi.colors.white + } +} diff --git a/interface/resources/qml/controlsUit/qmldir b/interface/resources/qml/controlsUit/qmldir index d0577f5575..e1665df40e 100644 --- a/interface/resources/qml/controlsUit/qmldir +++ b/interface/resources/qml/controlsUit/qmldir @@ -29,6 +29,7 @@ TextEdit 1.0 TextEdit.qml TextField 1.0 TextField.qml ToolTip 1.0 ToolTip.qml Tree 1.0 Tree.qml +ProxyWebView 1.0 ProxyWebView.qml VerticalSpacer 1.0 VerticalSpacer.qml WebGlyphButton 1.0 WebGlyphButton.qml WebSpinner 1.0 WebSpinner.qml diff --git a/libraries/shared/src/shared/FileUtils.cpp b/libraries/shared/src/shared/FileUtils.cpp index 0709a53602..f65acccfa1 100644 --- a/libraries/shared/src/shared/FileUtils.cpp +++ b/libraries/shared/src/shared/FileUtils.cpp @@ -33,6 +33,10 @@ const QStringList& FileUtils::getFileSelectors() { #if defined(USE_GLES) extraSelectors << "gles"; #endif + +#ifndef Q_OS_ANDROID + extraSelectors << "webengine"; +#endif }); return extraSelectors; diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index f67a356078..71bb65509f 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -49,6 +49,7 @@ #include #include "SecurityImageProvider.h" +#include "shared/FileUtils.h" #include "types/FileTypeProfile.h" #include "types/HFWebEngineProfile.h" #include "types/SoundEffect.h" @@ -237,7 +238,9 @@ void OffscreenQmlSurface::clearFocusItem() { void OffscreenQmlSurface::initializeEngine(QQmlEngine* engine) { Parent::initializeEngine(engine); - new QQmlFileSelector(engine); + QQmlFileSelector* fileSelector = new QQmlFileSelector(engine); + fileSelector->setExtraSelectors(FileUtils::getFileSelectors()); + static std::once_flag once; std::call_once(once, [] { qRegisterMetaType(); From 0846eb8ec68730af06629a6cea7ab7cffb39d86c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 30 Jan 2019 13:59:37 -0800 Subject: [PATCH 03/12] attempt to allow position edits in releaseGrab entity-method --- interface/src/avatar/MyAvatar.cpp | 10 ++++ .../src/avatars-renderer/Avatar.cpp | 3 ++ libraries/physics/src/EntityMotionState.cpp | 49 ++++++++++++++----- libraries/shared/src/Grab.h | 14 ++++-- libraries/shared/src/SpatiallyNestable.cpp | 7 ++- libraries/shared/src/SpatiallyNestable.h | 2 +- 6 files changed, 68 insertions(+), 17 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 92d9270d20..a0dd817742 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -5300,6 +5300,16 @@ void MyAvatar::releaseGrab(const QUuid& grabID) { bool tellHandler { false }; _avatarGrabsLock.withWriteLock([&] { + + std::map::iterator itr; + itr = _avatarGrabs.find(grabID); + if (itr != _avatarGrabs.end()) { + GrabPointer grab = itr->second; + if (grab) { + grab->setDeleted(true); + } + } + if (_avatarGrabData.remove(grabID)) { _grabsToDelete.push_back(grabID); tellHandler = true; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 07c1ca9a32..4bfaea0617 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -412,6 +412,9 @@ void Avatar::accumulateGrabPositions(std::map& g if (!grab || !grab->getActionID().isNull()) { continue; // the accumulated value isn't used, in this case. } + if (grab->getDeleted()) { + continue; + } glm::vec3 jointTranslation = getAbsoluteJointTranslationInObjectFrame(grab->getParentJointIndex()); glm::quat jointRotation = getAbsoluteJointRotationInObjectFrame(grab->getParentJointIndex()); diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index a82931064a..5a658e1f01 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -114,18 +114,45 @@ void EntityMotionState::updateServerPhysicsVariables() { } } +// void EntityMotionState::handleDeactivation() { +// // copy _server data to entity +// Transform localTransform = _entity->getLocalTransform(); +// // localTransform.setTranslation(_serverPosition); +// // localTransform.setRotation(_serverRotation); +// _entity->setLocalTransformAndVelocities(localTransform, ENTITY_ITEM_ZERO_VEC3, ENTITY_ITEM_ZERO_VEC3); +// // and also to RigidBody +// btTransform worldTrans; +// worldTrans.setOrigin(glmToBullet(_entity->getWorldPosition())); +// worldTrans.setRotation(glmToBullet(_entity->getWorldOrientation())); +// _body->setWorldTransform(worldTrans); +// // no need to update velocities... should already be zero +// } + void EntityMotionState::handleDeactivation() { - // copy _server data to entity - Transform localTransform = _entity->getLocalTransform(); - localTransform.setTranslation(_serverPosition); - localTransform.setRotation(_serverRotation); - _entity->setLocalTransformAndVelocities(localTransform, ENTITY_ITEM_ZERO_VEC3, ENTITY_ITEM_ZERO_VEC3); - // and also to RigidBody - btTransform worldTrans; - worldTrans.setOrigin(glmToBullet(_entity->getWorldPosition())); - worldTrans.setRotation(glmToBullet(_entity->getWorldOrientation())); - _body->setWorldTransform(worldTrans); - // no need to update velocities... should already be zero + if (_entity->getDirtyFlags() & (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES)) { + // Some non-physical event (script-call or network-packet) has modified the entity's transform and/or velocities + // at the last minute before deactivation --> the values stored in _server* and _body are stale. + // We assume the EntityMotionState is the last to know, so we copy from EntityItem and let things sort themselves out. + Transform localTransform; + _entity->getLocalTransformAndVelocities(localTransform, _serverVelocity, _serverAngularVelocity); + _serverPosition = localTransform.getTranslation(); + _serverRotation = localTransform.getRotation(); + _serverAcceleration = _entity->getAcceleration(); + _serverActionData = _entity->getDynamicData(); + _lastStep = ObjectMotionState::getWorldSimulationStep(); + } else { + // copy _server data to entity + Transform localTransform = _entity->getLocalTransform(); + localTransform.setTranslation(_serverPosition); + localTransform.setRotation(_serverRotation); + _entity->setLocalTransformAndVelocities(localTransform, ENTITY_ITEM_ZERO_VEC3, ENTITY_ITEM_ZERO_VEC3); + // and also to RigidBody + btTransform worldTrans; + worldTrans.setOrigin(glmToBullet(_entity->getWorldPosition())); + worldTrans.setRotation(glmToBullet(_entity->getWorldOrientation())); + _body->setWorldTransform(worldTrans); + // no need to update velocities... should already be zero + } } // virtual diff --git a/libraries/shared/src/Grab.h b/libraries/shared/src/Grab.h index 5765d6fd0e..59602439f7 100644 --- a/libraries/shared/src/Grab.h +++ b/libraries/shared/src/Grab.h @@ -26,16 +26,16 @@ public: void accumulate(glm::vec3 position, glm::quat orientation) { _position += position; _orientation = orientation; // XXX - count++; + _count++; } - glm::vec3 finalizePosition() { return count > 0 ? _position * (1.0f / count) : glm::vec3(0.0f); } + glm::vec3 finalizePosition() { return _count > 0 ? _position * (1.0f / _count) : glm::vec3(0.0f); } glm::quat finalizeOrientation() { return _orientation; } // XXX protected: glm::vec3 _position; glm::quat _orientation; - int count { 0 }; + int _count { 0 }; }; class Grab { @@ -48,7 +48,8 @@ public: _parentJointIndex(newParentJointIndex), _hand(newHand), _positionalOffset(newPositionalOffset), - _rotationalOffset(newRotationalOffset) {} + _rotationalOffset(newRotationalOffset), + _deleted(false) {} QByteArray toByteArray(); bool fromByteArray(const QByteArray& grabData); @@ -61,6 +62,7 @@ public: _positionalOffset = other->_positionalOffset; _rotationalOffset = other->_rotationalOffset; _actionID = other->_actionID; + _deleted = other->_deleted; return *this; } @@ -85,6 +87,9 @@ public: glm::quat getRotationalOffset() const { return _rotationalOffset; } void setRotationalOffset(glm::quat rotationalOffset) { _rotationalOffset = rotationalOffset; } + bool getDeleted() const { return _deleted; } + void setDeleted(bool value) { _deleted = value; } + protected: QUuid _actionID; // if an action is created in bullet for this grab, this is the ID QUuid _ownerID; // avatar ID of grabber @@ -93,6 +98,7 @@ protected: QString _hand; // "left" or "right" glm::vec3 _positionalOffset; // relative to joint glm::quat _rotationalOffset; // relative to joint + bool _deleted { false }; // scheduled for deletion }; diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index c524e3183b..dd0d749919 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -1390,7 +1390,12 @@ void SpatiallyNestable::removeGrab(GrabPointer grab) { bool SpatiallyNestable::hasGrabs() { bool result { false }; _grabsLock.withReadLock([&] { - result = !_grabs.isEmpty(); + foreach (const GrabPointer &grab, _grabs) { + if (grab && !grab->getDeleted()) { + result = true; + break; + } + } }); return result; } diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index 319f07236b..ed432647fd 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -241,7 +241,7 @@ protected: quint64 _rotationChanged { 0 }; mutable ReadWriteLockable _grabsLock; - QSet _grabs; + QSet _grabs; // upon this thing private: SpatiallyNestable() = delete; From 3ab2db96b6ecd0d43d15dad43143f81a52c93218 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 30 Jan 2019 14:42:47 -0800 Subject: [PATCH 04/12] deactivate grab action when grab is released --- interface/src/avatar/MyAvatar.cpp | 5 ++++- .../avatars-renderer/src/avatars-renderer/Avatar.cpp | 2 +- libraries/entities/src/EntityDynamicInterface.h | 1 + libraries/entities/src/EntityItem.cpp | 10 ++++++++++ libraries/entities/src/EntityItem.h | 1 + libraries/shared/src/Grab.h | 10 +++++----- libraries/shared/src/SpatiallyNestable.cpp | 2 +- libraries/shared/src/SpatiallyNestable.h | 1 + 8 files changed, 24 insertions(+), 8 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index a0dd817742..0530ba8eb2 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -5306,7 +5306,10 @@ void MyAvatar::releaseGrab(const QUuid& grabID) { if (itr != _avatarGrabs.end()) { GrabPointer grab = itr->second; if (grab) { - grab->setDeleted(true); + grab->setReleased(true); + bool success; + SpatiallyNestablePointer target = SpatiallyNestable::findByID(grab->getTargetID(), success); + target->disableGrab(grab); } } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 4bfaea0617..b8626c813e 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -412,7 +412,7 @@ void Avatar::accumulateGrabPositions(std::map& g if (!grab || !grab->getActionID().isNull()) { continue; // the accumulated value isn't used, in this case. } - if (grab->getDeleted()) { + if (grab->getReleased()) { continue; } diff --git a/libraries/entities/src/EntityDynamicInterface.h b/libraries/entities/src/EntityDynamicInterface.h index 836dae2057..c911eda471 100644 --- a/libraries/entities/src/EntityDynamicInterface.h +++ b/libraries/entities/src/EntityDynamicInterface.h @@ -59,6 +59,7 @@ public: virtual bool isReadyForAdd() const { return true; } bool isActive() { return _active; } + void deactivate() { _active = false; } virtual void removeFromSimulation(EntitySimulationPointer simulation) const = 0; virtual EntityItemWeakPointer getOwnerEntity() const = 0; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 41e4f43a5d..1640e97ff4 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -3506,3 +3506,13 @@ void EntityItem::removeGrab(GrabPointer grab) { } disableNoBootstrap(); } + +void EntityItem::disableGrab(GrabPointer grab) { + QUuid actionID = grab->getActionID(); + if (!actionID.isNull()) { + EntityDynamicPointer action = _grabActions.value(actionID); + if (action) { + action->deactivate(); + } + } +} diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index ec7ad78313..27b207b6f3 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -561,6 +561,7 @@ public: virtual void addGrab(GrabPointer grab) override; virtual void removeGrab(GrabPointer grab) override; + virtual void disableGrab(GrabPointer grab) override; signals: void requestRenderUpdate(); diff --git a/libraries/shared/src/Grab.h b/libraries/shared/src/Grab.h index 59602439f7..f16a80befa 100644 --- a/libraries/shared/src/Grab.h +++ b/libraries/shared/src/Grab.h @@ -49,7 +49,7 @@ public: _hand(newHand), _positionalOffset(newPositionalOffset), _rotationalOffset(newRotationalOffset), - _deleted(false) {} + _released(false) {} QByteArray toByteArray(); bool fromByteArray(const QByteArray& grabData); @@ -62,7 +62,7 @@ public: _positionalOffset = other->_positionalOffset; _rotationalOffset = other->_rotationalOffset; _actionID = other->_actionID; - _deleted = other->_deleted; + _released = other->_released; return *this; } @@ -87,8 +87,8 @@ public: glm::quat getRotationalOffset() const { return _rotationalOffset; } void setRotationalOffset(glm::quat rotationalOffset) { _rotationalOffset = rotationalOffset; } - bool getDeleted() const { return _deleted; } - void setDeleted(bool value) { _deleted = value; } + bool getReleased() const { return _released; } + void setReleased(bool value) { _released = value; } protected: QUuid _actionID; // if an action is created in bullet for this grab, this is the ID @@ -98,7 +98,7 @@ protected: QString _hand; // "left" or "right" glm::vec3 _positionalOffset; // relative to joint glm::quat _rotationalOffset; // relative to joint - bool _deleted { false }; // scheduled for deletion + bool _released { false }; // released and scheduled for deletion }; diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index dd0d749919..d3ed79faf4 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -1391,7 +1391,7 @@ bool SpatiallyNestable::hasGrabs() { bool result { false }; _grabsLock.withReadLock([&] { foreach (const GrabPointer &grab, _grabs) { - if (grab && !grab->getDeleted()) { + if (grab && !grab->getReleased()) { result = true; break; } diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index ed432647fd..e7a449f73f 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -218,6 +218,7 @@ public: virtual void addGrab(GrabPointer grab); virtual void removeGrab(GrabPointer grab); + virtual void disableGrab(GrabPointer grab) {}; bool hasGrabs(); virtual QUuid getEditSenderID(); From f33e5ba5ae5d0df55018128489b4c115ef6b8111 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 30 Jan 2019 14:47:38 -0800 Subject: [PATCH 05/12] cleanup --- libraries/physics/src/EntityMotionState.cpp | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 5a658e1f01..ce9cb20c21 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -114,20 +114,6 @@ void EntityMotionState::updateServerPhysicsVariables() { } } -// void EntityMotionState::handleDeactivation() { -// // copy _server data to entity -// Transform localTransform = _entity->getLocalTransform(); -// // localTransform.setTranslation(_serverPosition); -// // localTransform.setRotation(_serverRotation); -// _entity->setLocalTransformAndVelocities(localTransform, ENTITY_ITEM_ZERO_VEC3, ENTITY_ITEM_ZERO_VEC3); -// // and also to RigidBody -// btTransform worldTrans; -// worldTrans.setOrigin(glmToBullet(_entity->getWorldPosition())); -// worldTrans.setRotation(glmToBullet(_entity->getWorldOrientation())); -// _body->setWorldTransform(worldTrans); -// // no need to update velocities... should already be zero -// } - void EntityMotionState::handleDeactivation() { if (_entity->getDirtyFlags() & (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES)) { // Some non-physical event (script-call or network-packet) has modified the entity's transform and/or velocities From c6f44234f8afdc1f30f5c4ac2b1b317c8027ef75 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 30 Jan 2019 14:56:02 -0800 Subject: [PATCH 06/12] avoid possible crash --- interface/src/avatar/MyAvatar.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0530ba8eb2..2eae7aa181 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -5309,7 +5309,9 @@ void MyAvatar::releaseGrab(const QUuid& grabID) { grab->setReleased(true); bool success; SpatiallyNestablePointer target = SpatiallyNestable::findByID(grab->getTargetID(), success); - target->disableGrab(grab); + if (target) { + target->disableGrab(grab); + } } } From 30d9fe705ebee66d6d30a3233959a699c3ad0010 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 30 Jan 2019 14:56:50 -0800 Subject: [PATCH 07/12] avoid possible crash --- interface/src/avatar/MyAvatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 2eae7aa181..3f32f96795 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -5309,7 +5309,7 @@ void MyAvatar::releaseGrab(const QUuid& grabID) { grab->setReleased(true); bool success; SpatiallyNestablePointer target = SpatiallyNestable::findByID(grab->getTargetID(), success); - if (target) { + if (target && success) { target->disableGrab(grab); } } From 03789e01da412cc50cb8ecb86d1a5f03833b0509 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 30 Jan 2019 15:53:21 -0800 Subject: [PATCH 08/12] adding fix for oculus store argument check --- interface/src/Application.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index daf2dd6363..3b9fe15f25 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -761,6 +761,11 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { static const auto SUPPRESS_SETTINGS_RESET = "--suppress-settings-reset"; bool suppressPrompt = cmdOptionExists(argc, const_cast(argv), SUPPRESS_SETTINGS_RESET); + // set the OCULUS_STORE property so the oculus plugin can know if we ran from the Oculus Store + static const auto OCULUS_STORE_ARG = "--oculus-store"; + bool isStore = cmdOptionExists(argc, const_cast(argv), OCULUS_STORE_ARG); + qApp->setProperty(hifi::properties::OCULUS_STORE, isStore); + // Ignore any previous crashes if running from command line with a test script. bool inTestMode { false }; for (int i = 0; i < argc; ++i) { @@ -1138,10 +1143,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo qCDebug(interfaceapp) << "[VERSION] We will use DEVELOPMENT global services."; #endif - // set the OCULUS_STORE property so the oculus plugin can know if we ran from the Oculus Store - static const QString OCULUS_STORE_ARG = "--oculus-store"; - bool isStore = arguments().indexOf(OCULUS_STORE_ARG) != -1; - setProperty(hifi::properties::OCULUS_STORE, isStore); + bool isStore = property(hifi::properties::OCULUS_STORE).toBool(); + DependencyManager::get()->setLimitedCommerce(isStore); // Or we could make it a separate arg, or if either arg is set, etc. And should this instead by a hifi::properties? updateHeartbeat(); From 2c40b120ca98dff84a9c660af97b665643fbc43a Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Wed, 30 Jan 2019 17:26:52 -0800 Subject: [PATCH 09/12] Make Animation Tests Pass Again --- .../animation/src/AnimBlendLinearMove.cpp | 4 +- libraries/animation/src/AnimVariant.h | 7 +-- libraries/animation/src/Rig.cpp | 2 - tests/animation/CMakeLists.txt | 2 +- .../src/AnimInverseKinematicsTests.cpp | 4 +- tests/animation/src/AnimTests.cpp | 62 ++++++++++--------- 6 files changed, 40 insertions(+), 41 deletions(-) diff --git a/libraries/animation/src/AnimBlendLinearMove.cpp b/libraries/animation/src/AnimBlendLinearMove.cpp index 07e1c17f77..28b8bb4a9f 100644 --- a/libraries/animation/src/AnimBlendLinearMove.cpp +++ b/libraries/animation/src/AnimBlendLinearMove.cpp @@ -151,10 +151,10 @@ void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIn if (_phase < 0.0f) { _phase = 0.0f; } - + // detect loop trigger events if (_phase >= 1.0f) { - triggersOut.setTrigger(_id + "Loop"); + triggersOut.setTrigger(_id + "OnLoop"); _phase = glm::fract(_phase); } diff --git a/libraries/animation/src/AnimVariant.h b/libraries/animation/src/AnimVariant.h index 0f921984b1..eb9ebd33dd 100644 --- a/libraries/animation/src/AnimVariant.h +++ b/libraries/animation/src/AnimVariant.h @@ -209,15 +209,14 @@ public: void set(const QString& key, const QString& value) { _map[key] = AnimVariant(value); } void unset(const QString& key) { _map.erase(key); } - void setTrigger(const QString& key) { _triggers.insert(key); } - void clearTriggers() { _triggers.clear(); } + void setTrigger(const QString& key) { _map[key] = AnimVariant(true); } void setRigToGeometryTransform(const glm::mat4& rigToGeometry) { _rigToGeometryMat = rigToGeometry; _rigToGeometryRot = glmExtractRotation(rigToGeometry); } - void clearMap() { _map.clear(); } + void clearMap() { _map.clear(); _triggers.clear(); } bool hasKey(const QString& key) const { return _map.find(key) != _map.end(); } const AnimVariant& get(const QString& key) const { @@ -238,7 +237,7 @@ public: // For stat debugging. std::map toDebugMap() const; -#ifdef NDEBUG +#ifndef NDEBUG void dump() const { qCDebug(animation) << "AnimVariantMap ="; for (auto& pair : _map) { diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index bc4dca54f2..7842ec0804 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1207,9 +1207,7 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons _networkPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); } _lastAnimVars = _animVars; - _animVars.clearTriggers(); _animVars = triggersOut; - _networkVars.clearTriggers(); _networkVars = networkTriggersOut; _lastContext = context; } diff --git a/tests/animation/CMakeLists.txt b/tests/animation/CMakeLists.txt index 17999c4d8e..2af4d5f2cd 100644 --- a/tests/animation/CMakeLists.txt +++ b/tests/animation/CMakeLists.txt @@ -1,7 +1,7 @@ # Declare dependencies macro (setup_testcase_dependencies) # link in the shared libraries - link_hifi_libraries(shared animation gpu fbx graphics networking test-utils) + link_hifi_libraries(shared animation gpu fbx hfm graphics networking test-utils) package_libraries_for_deployment() endmacro () diff --git a/tests/animation/src/AnimInverseKinematicsTests.cpp b/tests/animation/src/AnimInverseKinematicsTests.cpp index 910770bb0d..708b1b2d2a 100644 --- a/tests/animation/src/AnimInverseKinematicsTests.cpp +++ b/tests/animation/src/AnimInverseKinematicsTests.cpp @@ -143,7 +143,7 @@ void AnimInverseKinematicsTests::testSingleChain() { ikDoll.setTargetVars(QString("D"), QString("positionD"), QString("rotationD"), QString("targetTypeD"), QString("weightD"), 1.0f, flexCoefficients, QString("poleVectorEnabledD"), QString("poleReferenceVectorD"), QString("poleVectorD")); - AnimNode::Triggers triggers; + AnimVariantMap triggers; // the IK solution should be: // @@ -236,7 +236,7 @@ void AnimInverseKinematicsTests::testSingleChain() { ikDoll.setTargetVars(QString("D"), QString("positionD"), QString("rotationD"), QString("targetTypeD"), QString("weightD"), 1.0f, flexCoefficients, QString("poleVectorEnabledD"), QString("poleReferenceVectorD"), QString("poleVectorD")); - AnimNode::Triggers triggers; + AnimVariantMap triggers; // the IK solution should be: // diff --git a/tests/animation/src/AnimTests.cpp b/tests/animation/src/AnimTests.cpp index 01c8d1c1b6..0cd9571e22 100644 --- a/tests/animation/src/AnimTests.cpp +++ b/tests/animation/src/AnimTests.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -33,6 +34,7 @@ void AnimTests::initTestCase() { DependencyManager::set(NodeType::Agent); DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); } @@ -84,26 +86,26 @@ void AnimTests::testClipEvaulate() { AnimClip clip(id, url, startFrame, endFrame, timeScale, loopFlag, mirrorFlag); - AnimNode::Triggers triggers; + AnimVariantMap triggers; clip.evaluate(vars, context, framesToSec(10.0f), triggers); QCOMPARE_WITH_ABS_ERROR(clip._frame, 12.0f, TEST_EPSILON); // does it loop? - triggers.clear(); + triggers.clearMap(); clip.evaluate(vars, context, framesToSec(12.0f), triggers); QCOMPARE_WITH_ABS_ERROR(clip._frame, 3.0f, TEST_EPSILON); // Note: frame 3 and not 4, because extra frame between start and end. // did we receive a loop trigger? - QVERIFY(std::find(triggers.begin(), triggers.end(), "myClipNodeOnLoop") != triggers.end()); + QVERIFY(triggers.hasKey("myClipNodeOnLoop")); // does it pause at end? - triggers.clear(); + triggers.clearMap(); clip.setLoopFlagVar("FalseVar"); clip.evaluate(vars, context, framesToSec(20.0f), triggers); QCOMPARE_WITH_ABS_ERROR(clip._frame, 22.0f, TEST_EPSILON); // did we receive a done trigger? - QVERIFY(std::find(triggers.begin(), triggers.end(), "myClipNodeOnDone") != triggers.end()); + QVERIFY(triggers.hasKey("myClipNodeOnDone")); } void AnimTests::testClipEvaulateWithVars() { @@ -133,7 +135,7 @@ void AnimTests::testClipEvaulateWithVars() { clip.setTimeScaleVar("timeScale2"); clip.setLoopFlagVar("loopFlag2"); - AnimNode::Triggers triggers; + AnimVariantMap triggers; clip.evaluate(vars, context, framesToSec(0.1f), triggers); // verify that the values from the AnimVariantMap made it into the clipNode's @@ -284,11 +286,11 @@ void AnimTests::testAccumulateTime() { timeScale = 1.0f; float dt = 1.0f; QString id = "testNode"; - AnimNode::Triggers triggers; + AnimVariantMap triggers; float loopFlag = true; float resultFrame = accumulateTime(startFrame, endFrame, timeScale, startFrame, dt, loopFlag, id, triggers); // a one frame looping animation should NOT trigger onLoop events - QVERIFY(triggers.empty()); + QVERIFY(!triggers.hasKey("testNodeOnLoop")); const uint32_t MAX_TRIGGER_COUNT = 3; @@ -296,45 +298,45 @@ void AnimTests::testAccumulateTime() { endFrame = 1.1f; timeScale = 10.0f; dt = 10.0f; - triggers.clear(); + triggers.clearMap(); loopFlag = true; resultFrame = accumulateTime(startFrame, endFrame, timeScale, startFrame, dt, loopFlag, id, triggers); - // a short animation with a large dt & a large timescale, should only create a MAXIMUM of 3 loop events. - QVERIFY(triggers.size() <= MAX_TRIGGER_COUNT); + // a short animation with a large dt & a large timescale, should generate a onLoop event. + QVERIFY(triggers.hasKey("testNodeOnLoop")); } void AnimTests::testAccumulateTimeWithParameters(float startFrame, float endFrame, float timeScale) const { float dt = (1.0f / 30.0f) / timeScale; // sec QString id = "testNode"; - AnimNode::Triggers triggers; + AnimVariantMap triggers; bool loopFlag = false; float resultFrame = accumulateTime(startFrame, endFrame, timeScale, startFrame, dt, loopFlag, id, triggers); QVERIFY(resultFrame == startFrame + 1.0f); - QVERIFY(triggers.empty()); - triggers.clear(); + QVERIFY(!triggers.hasKey("testNodeOnLoop")); + triggers.clearMap(); resultFrame = accumulateTime(startFrame, endFrame, timeScale, resultFrame, dt, loopFlag, id, triggers); QVERIFY(resultFrame == startFrame + 2.0f); - QVERIFY(triggers.empty()); - triggers.clear(); + QVERIFY(!triggers.hasKey("testNodeOnLoop")); + triggers.clearMap(); resultFrame = accumulateTime(startFrame, endFrame, timeScale, resultFrame, dt, loopFlag, id, triggers); QVERIFY(resultFrame == startFrame + 3.0f); - QVERIFY(triggers.empty()); - triggers.clear(); + QVERIFY(!triggers.hasKey("testNodeOnLoop")); + triggers.clearMap(); // test onDone trigger and frame clamping. resultFrame = accumulateTime(startFrame, endFrame, timeScale, endFrame - 1.0f, dt, loopFlag, id, triggers); QVERIFY(resultFrame == endFrame); - QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnDone"); - triggers.clear(); + QVERIFY(triggers.hasKey("testNodeOnDone")); + triggers.clearMap(); resultFrame = accumulateTime(startFrame, endFrame, timeScale, endFrame - 0.5f, dt, loopFlag, id, triggers); QVERIFY(resultFrame == endFrame); - QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnDone"); - triggers.clear(); + QVERIFY(triggers.hasKey("testNodeOnDone")); + triggers.clearMap(); // test onLoop trigger and looping frame logic loopFlag = true; @@ -342,26 +344,26 @@ void AnimTests::testAccumulateTimeWithParameters(float startFrame, float endFram // should NOT trigger loop even though we stop at last frame, because there is an extra frame between end and start frames. resultFrame = accumulateTime(startFrame, endFrame, timeScale, endFrame - 1.0f, dt, loopFlag, id, triggers); QVERIFY(resultFrame == endFrame); - QVERIFY(triggers.empty()); - triggers.clear(); + QVERIFY(!triggers.hasKey("testNodeOnLoop")); + triggers.clearMap(); // now we should hit loop trigger resultFrame = accumulateTime(startFrame, endFrame, timeScale, resultFrame, dt, loopFlag, id, triggers); QVERIFY(resultFrame == startFrame); - QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnLoop"); - triggers.clear(); + QVERIFY(triggers.hasKey("testNodeOnLoop")); + triggers.clearMap(); // should NOT trigger loop, even though we move past the end frame, because of extra frame between end and start. resultFrame = accumulateTime(startFrame, endFrame, timeScale, endFrame - 0.5f, dt, loopFlag, id, triggers); QVERIFY(resultFrame == endFrame + 0.5f); - QVERIFY(triggers.empty()); - triggers.clear(); + QVERIFY(!triggers.hasKey("testNodeOnLoop")); + triggers.clearMap(); // now we should hit loop trigger resultFrame = accumulateTime(startFrame, endFrame, timeScale, resultFrame, dt, loopFlag, id, triggers); QVERIFY(resultFrame == startFrame + 0.5f); - QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnLoop"); - triggers.clear(); + QVERIFY(triggers.hasKey("testNodeOnLoop")); + triggers.clearMap(); } void AnimTests::testAnimPose() { From 283dabc62296ce314257427e0f2cd181033e61a0 Mon Sep 17 00:00:00 2001 From: Clement Date: Wed, 30 Jan 2019 17:03:16 -0800 Subject: [PATCH 10/12] Fix reload mechanic for entity server scripts --- .../src/scripts/EntityScriptServer.cpp | 16 ++++++++-------- .../src/scripts/EntityScriptServer.h | 2 +- .../entities-renderer/src/EntityTreeRenderer.cpp | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index ef0c807bc4..f1a6c97831 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -112,7 +112,6 @@ void EntityScriptServer::handleReloadEntityServerScriptPacket(QSharedPointerunloadEntityScript(entityID); checkAndCallPreload(entityID, true); } } @@ -184,7 +183,6 @@ void EntityScriptServer::updateEntityPPS() { pps = std::min(_maxEntityPPS, pps); } _entityEditSender.setPacketsPerSecond(pps); - qDebug() << QString("Updating entity PPS to: %1 @ %2 PPS per script = %3 PPS").arg(numRunningScripts).arg(_entityPPSPerScript).arg(pps); } void EntityScriptServer::handleEntityServerScriptLogPacket(QSharedPointer message, SharedNodePointer senderNode) { @@ -525,23 +523,25 @@ void EntityScriptServer::deletingEntity(const EntityItemID& entityID) { void EntityScriptServer::entityServerScriptChanging(const EntityItemID& entityID, bool reload) { if (_entityViewer.getTree() && !_shuttingDown) { - _entitiesScriptEngine->unloadEntityScript(entityID, true); checkAndCallPreload(entityID, reload); } } -void EntityScriptServer::checkAndCallPreload(const EntityItemID& entityID, bool reload) { +void EntityScriptServer::checkAndCallPreload(const EntityItemID& entityID, bool forceRedownload) { if (_entityViewer.getTree() && !_shuttingDown && _entitiesScriptEngine) { EntityItemPointer entity = _entityViewer.getTree()->findEntityByEntityItemID(entityID); EntityScriptDetails details; - bool notRunning = !_entitiesScriptEngine->getEntityScriptDetails(entityID, details); - if (entity && (reload || notRunning || details.scriptText != entity->getServerScripts())) { + bool isRunning = _entitiesScriptEngine->getEntityScriptDetails(entityID, details); + if (entity && (forceRedownload || !isRunning || details.scriptText != entity->getServerScripts())) { + if (isRunning) { + _entitiesScriptEngine->unloadEntityScript(entityID, true); + } + QString scriptUrl = entity->getServerScripts(); if (!scriptUrl.isEmpty()) { scriptUrl = DependencyManager::get()->normalizeURL(scriptUrl); - qCDebug(entity_script_server) << "Loading entity server script" << scriptUrl << "for" << entityID; - _entitiesScriptEngine->loadEntityScript(entityID, scriptUrl, reload); + _entitiesScriptEngine->loadEntityScript(entityID, scriptUrl, forceRedownload); } } } diff --git a/assignment-client/src/scripts/EntityScriptServer.h b/assignment-client/src/scripts/EntityScriptServer.h index 9c6c4c752e..944fee36a3 100644 --- a/assignment-client/src/scripts/EntityScriptServer.h +++ b/assignment-client/src/scripts/EntityScriptServer.h @@ -67,7 +67,7 @@ private: void addingEntity(const EntityItemID& entityID); void deletingEntity(const EntityItemID& entityID); void entityServerScriptChanging(const EntityItemID& entityID, bool reload); - void checkAndCallPreload(const EntityItemID& entityID, bool reload = false); + void checkAndCallPreload(const EntityItemID& entityID, bool forceRedownload = false); void cleanupOldKilledListeners(); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index c71b296a74..44025fc8f4 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -1048,7 +1048,7 @@ void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, bool QString scriptUrl = entity->getScript(); if ((shouldLoad && unloadFirst) || scriptUrl.isEmpty()) { if (_entitiesScriptEngine) { - _entitiesScriptEngine->unloadEntityScript(entityID); + _entitiesScriptEngine->unloadEntityScript(entityID); } entity->scriptHasUnloaded(); } From f4118213b15fa12082a73990ed70e9af0678d0bf Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Fri, 1 Feb 2019 10:12:04 -0800 Subject: [PATCH 11/12] give tablet root color --- .../qml/hifi/commerce/common/sendAsset/SendAsset.qml | 3 +-- interface/resources/qml/hifi/tablet/TabletRoot.qml | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml index bc8816e0ea..68d437a346 100644 --- a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml +++ b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml @@ -21,11 +21,10 @@ import "../../../../controls" as HifiControls import "../" as HifiCommerceCommon import "qrc:////qml//hifi//models" as HifiModels // Absolute path so the same code works everywhere. -Rectangle { +Item { HifiConstants { id: hifi; } id: root; - color: hifi.colors.baseGray property int parentAppTitleBarHeight; property int parentAppNavBarHeight; property string currentActiveView: "sendAssetHome"; diff --git a/interface/resources/qml/hifi/tablet/TabletRoot.qml b/interface/resources/qml/hifi/tablet/TabletRoot.qml index b19dcbb919..93a23f1b9d 100644 --- a/interface/resources/qml/hifi/tablet/TabletRoot.qml +++ b/interface/resources/qml/hifi/tablet/TabletRoot.qml @@ -3,10 +3,13 @@ import Hifi 1.0 import "../../dialogs" import "../../controls" +import stylesUit 1.0 -Item { +Rectangle { + HifiConstants { id: hifi; } id: tabletRoot objectName: "tabletRoot" + color: hifi.colors.baseGray property string username: "Unknown user" property string usernameShort: "Unknown user" property var rootMenu; From 30bd9774b2d9127db8ddc1b857f689bab83d169a Mon Sep 17 00:00:00 2001 From: danteruiz Date: Tue, 5 Feb 2019 13:11:54 -0800 Subject: [PATCH 12/12] script engine to load platform specific files --- libraries/script-engine/src/ScriptEngines.cpp | 3 + libraries/shared/src/shared/FileUtils.cpp | 5 + .../+android_questInterface/defaultScripts.js | 118 ++++++++++++++++++ 3 files changed, 126 insertions(+) create mode 100644 scripts/+android_questInterface/defaultScripts.js diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index 8ecfb84633..3963ad5593 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include "ScriptEngine.h" #include "ScriptEngineLogging.h" @@ -476,6 +477,8 @@ ScriptEnginePointer ScriptEngines::loadScript(const QUrl& scriptFilename, bool i scriptUrl = normalizeScriptURL(scriptFilename); } + scriptUrl = QUrl(FileUtils::selectFile(scriptUrl.toString())); + auto scriptEngine = getScriptEngine(scriptUrl); if (scriptEngine && !scriptEngine->isStopping()) { return scriptEngine; diff --git a/libraries/shared/src/shared/FileUtils.cpp b/libraries/shared/src/shared/FileUtils.cpp index 0709a53602..041fca5459 100644 --- a/libraries/shared/src/shared/FileUtils.cpp +++ b/libraries/shared/src/shared/FileUtils.cpp @@ -30,6 +30,11 @@ const QStringList& FileUtils::getFileSelectors() { static std::once_flag once; static QStringList extraSelectors; std::call_once(once, [] { + +#if defined(Q_OS_ANDROID) + //extraSelectors << "android_" HIFI_ANDROID_APP; +#endif + #if defined(USE_GLES) extraSelectors << "gles"; #endif diff --git a/scripts/+android_questInterface/defaultScripts.js b/scripts/+android_questInterface/defaultScripts.js new file mode 100644 index 0000000000..da50e4a182 --- /dev/null +++ b/scripts/+android_questInterface/defaultScripts.js @@ -0,0 +1,118 @@ +"use strict"; +/* jslint vars: true, plusplus: true */ + +// +// defaultScripts.js +// examples +// +// Copyright 2014 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 +// + +var DEFAULT_SCRIPTS_COMBINED = [ + "system/request-service.js", + "system/progress.js", + //"system/away.js", + "system/hmd.js", + "system/menu.js", + "system/bubble.js", + "system/pal.js", // "system/mod.js", // older UX, if you prefer + "system/avatarapp.js", + "system/makeUserConnection.js", + "system/tablet-goto.js", + "system/notifications.js", + "system/commerce/wallet.js", + "system/dialTone.js", + "system/firstPersonHMD.js", + "system/tablet-ui/tabletUI.js", + "system/miniTablet.js" +]; +var DEFAULT_SCRIPTS_SEPARATE = [ + "system/controllers/controllerScripts.js", + //"system/chat.js" +]; + +if (Window.interstitialModeEnabled) { + // Insert interstitial scripts at front so that they're started first. + DEFAULT_SCRIPTS_COMBINED.splice(0, 0, "system/interstitialPage.js", "system/redirectOverlays.js"); +} + +// add a menu item for debugging +var MENU_CATEGORY = "Developer > Scripting"; +var MENU_ITEM = "Debug defaultScripts.js"; + +var SETTINGS_KEY = '_debugDefaultScriptsIsChecked'; +var previousSetting = Settings.getValue(SETTINGS_KEY); + +if (previousSetting === '' || previousSetting === false || previousSetting === 'false') { + previousSetting = false; +} + +if (previousSetting === true || previousSetting === 'true') { + previousSetting = true; +} + +if (Menu.menuExists(MENU_CATEGORY) && !Menu.menuItemExists(MENU_CATEGORY, MENU_ITEM)) { + Menu.addMenuItem({ + menuName: MENU_CATEGORY, + menuItemName: MENU_ITEM, + isCheckable: true, + isChecked: previousSetting, + }); +} + +function loadSeparateDefaults() { + for (var i in DEFAULT_SCRIPTS_SEPARATE) { + Script.load(DEFAULT_SCRIPTS_SEPARATE[i]); + } +} + +function runDefaultsTogether() { + for (var i in DEFAULT_SCRIPTS_COMBINED) { + Script.include(DEFAULT_SCRIPTS_COMBINED[i]); + } + loadSeparateDefaults(); +} + +function runDefaultsSeparately() { + for (var i in DEFAULT_SCRIPTS_COMBINED) { + Script.load(DEFAULT_SCRIPTS_COMBINED[i]); + } + loadSeparateDefaults(); +} + +// start all scripts +if (Menu.isOptionChecked(MENU_ITEM)) { + // we're debugging individual default scripts + // so we load each into its own ScriptEngine instance + runDefaultsSeparately(); +} else { + // include all default scripts into this ScriptEngine + runDefaultsTogether(); +} + +function menuItemEvent(menuItem) { + if (menuItem === MENU_ITEM) { + var isChecked = Menu.isOptionChecked(MENU_ITEM); + if (isChecked === true) { + Settings.setValue(SETTINGS_KEY, true); + } else if (isChecked === false) { + Settings.setValue(SETTINGS_KEY, false); + } + Menu.triggerOption("Reload All Scripts"); + } +} + +function removeMenuItem() { + if (!Menu.isOptionChecked(MENU_ITEM)) { + Menu.removeMenuItem(MENU_CATEGORY, MENU_ITEM); + } +} + +Script.scriptEnding.connect(function() { + removeMenuItem(); +}); + +Menu.menuItemEvent.connect(menuItemEvent);