From 2662dc9b4e5ba665556ed60d791b3803ff5eb18f Mon Sep 17 00:00:00 2001
From: Brad Davis
Date: Fri, 27 Apr 2018 08:03:48 -0700
Subject: [PATCH 01/17] Suppress tracing log spam when tracing is not active
---
libraries/shared/src/Trace.h | 3 +++
1 file changed, 3 insertions(+)
diff --git a/libraries/shared/src/Trace.h b/libraries/shared/src/Trace.h
index 93e2c6c4c2..1e1326968f 100644
--- a/libraries/shared/src/Trace.h
+++ b/libraries/shared/src/Trace.h
@@ -102,6 +102,9 @@ private:
};
inline void traceEvent(const QLoggingCategory& category, const QString& name, EventType type, const QString& id = "", const QVariantMap& args = {}, const QVariantMap& extra = {}) {
+ if (!DependencyManager::isSet()) {
+ return;
+ }
const auto& tracer = DependencyManager::get();
if (tracer) {
tracer->traceEvent(category, name, type, id, args, extra);
From 4db83230ce94b966efdfe07553d368be68a6e893 Mon Sep 17 00:00:00 2001
From: Brad Davis
Date: Fri, 27 Apr 2018 08:09:31 -0700
Subject: [PATCH 02/17] Fix bad virtual cast on qml surface destruction
---
libraries/ui/src/ui/OffscreenQmlSurface.cpp | 23 +++++++++++++--------
libraries/ui/src/ui/OffscreenQmlSurface.h | 4 +++-
2 files changed, 17 insertions(+), 10 deletions(-)
diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp
index 43b573a169..b3c1c486e9 100644
--- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp
+++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp
@@ -223,6 +223,17 @@ void AudioHandler::run() {
qDebug() << "QML Audio changed to " << _newTargetDevice;
}
+OffscreenQmlSurface::~OffscreenQmlSurface() {
+ clearFocusItem();
+}
+
+void OffscreenQmlSurface::clearFocusItem() {
+ if (_currentFocusItem) {
+ disconnect(_currentFocusItem, &QObject::destroyed, this, &OffscreenQmlSurface::focusDestroyed);
+ }
+ _currentFocusItem = nullptr;
+}
+
void OffscreenQmlSurface::initializeEngine(QQmlEngine* engine) {
Parent::initializeEngine(engine);
new QQmlFileSelector(engine);
@@ -545,17 +556,15 @@ bool OffscreenQmlSurface::handlePointerEvent(const PointerEvent& event, class QT
}
void OffscreenQmlSurface::focusDestroyed(QObject* obj) {
- if (_currentFocusItem) {
- disconnect(_currentFocusItem, &QObject::destroyed, this, &OffscreenQmlSurface::focusDestroyed);
- }
- _currentFocusItem = nullptr;
+ clearFocusItem();
}
void OffscreenQmlSurface::onFocusObjectChanged(QObject* object) {
+ clearFocusItem();
+
QQuickItem* item = static_cast(object);
if (!item) {
setFocusText(false);
- _currentFocusItem = nullptr;
return;
}
@@ -563,10 +572,6 @@ void OffscreenQmlSurface::onFocusObjectChanged(QObject* object) {
qApp->sendEvent(object, &query);
setFocusText(query.value(Qt::ImEnabled).toBool());
- if (_currentFocusItem) {
- disconnect(_currentFocusItem, &QObject::destroyed, this, 0);
- }
-
// Raise and lower keyboard for QML text fields.
// HTML text fields are handled in emitWebEvent() methods - testing READ_ONLY_PROPERTY prevents action for HTML files.
const char* READ_ONLY_PROPERTY = "readOnly";
diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.h b/libraries/ui/src/ui/OffscreenQmlSurface.h
index 9fa86f12a3..b95a8f117d 100644
--- a/libraries/ui/src/ui/OffscreenQmlSurface.h
+++ b/libraries/ui/src/ui/OffscreenQmlSurface.h
@@ -22,7 +22,8 @@ class OffscreenQmlSurface : public hifi::qml::OffscreenSurface {
Q_OBJECT
Q_PROPERTY(bool focusText READ isFocusText NOTIFY focusTextChanged)
public:
-
+ ~OffscreenQmlSurface();
+
static void addWhitelistContextHandler(const std::initializer_list& urls, const QmlContextCallback& callback);
static void addWhitelistContextHandler(const QUrl& url, const QmlContextCallback& callback) { addWhitelistContextHandler({ { url } }, callback); };
@@ -58,6 +59,7 @@ public slots:
void sendToQml(const QVariant& message);
protected:
+ void clearFocusItem();
void setFocusText(bool newFocusText);
void initializeEngine(QQmlEngine* engine) override;
void onRootContextCreated(QQmlContext* qmlContext) override;
From 6640313256e0fadc6401628f6e0316518a01119a Mon Sep 17 00:00:00 2001
From: Brad Davis
Date: Fri, 27 Apr 2018 08:10:22 -0700
Subject: [PATCH 03/17] Fix memory leak in QML surfaces
---
libraries/ui/src/ui/OffscreenQmlSurface.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp
index b3c1c486e9..0d8e22cebb 100644
--- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp
+++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp
@@ -115,6 +115,7 @@ private:
class UrlHandler : public QObject {
Q_OBJECT
public:
+ UrlHandler(QObject* parent = nullptr) : QObject(parent) {}
Q_INVOKABLE bool canHandleUrl(const QString& url) {
static auto handler = dynamic_cast(qApp);
return handler && handler->canAcceptURL(url);
@@ -257,7 +258,7 @@ void OffscreenQmlSurface::initializeEngine(QQmlEngine* engine) {
auto rootContext = engine->rootContext();
rootContext->setContextProperty("GL", ::getGLContextData());
- rootContext->setContextProperty("urlHandler", new UrlHandler());
+ rootContext->setContextProperty("urlHandler", new UrlHandler(rootContext));
rootContext->setContextProperty("resourceDirectoryUrl", QUrl::fromLocalFile(PathUtils::resourcesPath()));
rootContext->setContextProperty("ApplicationInterface", qApp);
auto javaScriptToInject = getEventBridgeJavascript();
From 1b612d373f845ea4dab1698fb36b12d5814d88ff Mon Sep 17 00:00:00 2001
From: Brad Davis
Date: Fri, 27 Apr 2018 08:11:11 -0700
Subject: [PATCH 04/17] Explicitly delete QML context before releasing engine
---
libraries/qml/src/qml/impl/SharedObject.cpp | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/libraries/qml/src/qml/impl/SharedObject.cpp b/libraries/qml/src/qml/impl/SharedObject.cpp
index 9253c41b39..7bcb216ba8 100644
--- a/libraries/qml/src/qml/impl/SharedObject.cpp
+++ b/libraries/qml/src/qml/impl/SharedObject.cpp
@@ -97,12 +97,13 @@ SharedObject::~SharedObject() {
}
// _rootItem is parented to the quickWindow, so needs no explicit destruction
- //if (_rootItem) {
- // delete _rootItem;
- // _rootItem = nullptr;
- //}
- releaseEngine(_qmlContext->engine());
+ if (_qmlContext) {
+ auto engine = _qmlContext->engine();
+ delete _qmlContext;
+ _qmlContext = nullptr;
+ releaseEngine(engine);
+ }
}
void SharedObject::create(OffscreenSurface* surface) {
@@ -210,9 +211,9 @@ QQmlEngine* SharedObject::acquireEngine(OffscreenSurface* surface) {
if (!globalEngine) {
Q_ASSERT(0 == globalEngineRefCount);
globalEngine = new QQmlEngine();
- surface->initializeQmlEngine(result);
- ++globalEngineRefCount;
+ surface->initializeEngine(result);
}
+ ++globalEngineRefCount;
result = globalEngine;
#else
result = new QQmlEngine();
From e892694bf5bdcf56e5892a71fe7fb2492f7d42ec Mon Sep 17 00:00:00 2001
From: Brad Davis
Date: Fri, 27 Apr 2018 08:13:35 -0700
Subject: [PATCH 05/17] Don't modify URL on web entity on QML shutdown, remove
tablet related code from web entity
---
.../src/RenderableWebEntityItem.cpp | 17 +----------------
1 file changed, 1 insertion(+), 16 deletions(-)
diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp
index f333e805ce..7ad74d1eee 100644
--- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp
@@ -308,12 +308,7 @@ bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
item->setProperty(URL_PROPERTY, _lastSourceUrl);
});
} else if (_contentType == ContentType::QmlContent) {
- _webSurface->load(_lastSourceUrl, [this](QQmlContext* context, QObject* item) {
- if (item && item->objectName() == "tabletRoot") {
- auto tabletScriptingInterface = DependencyManager::get();
- tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface.data());
- }
- });
+ _webSurface->load(_lastSourceUrl);
}
_fadeStartTime = usecTimestampNow();
_webSurface->resume();
@@ -330,16 +325,6 @@ void WebEntityRenderer::destroyWebSurface() {
if (webSurface) {
--_currentWebCount;
QQuickItem* rootItem = webSurface->getRootItem();
- // Explicitly set the web URL to an empty string, in an effort to get a
- // faster shutdown of any chromium processes interacting with audio
- if (rootItem && _contentType == ContentType::HtmlContent) {
- rootItem->setProperty(URL_PROPERTY, "");
- }
-
- if (rootItem && rootItem->objectName() == "tabletRoot") {
- auto tabletScriptingInterface = DependencyManager::get();
- tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", nullptr);
- }
// Fix for crash in QtWebEngineCore when rapidly switching domains
// Call stop on the QWebEngineView before destroying OffscreenQMLSurface.
From f2172d84a00d8add357029821c45f4d74d15d412 Mon Sep 17 00:00:00 2001
From: Brad Davis
Date: Fri, 27 Apr 2018 08:35:20 -0700
Subject: [PATCH 06/17] Add stress test for QML web content
---
tests/qml/qml/controls/WebEntityView.qml | 32 ++++
tests/qml/src/main.cpp | 198 ++++++++++++++++++-----
2 files changed, 187 insertions(+), 43 deletions(-)
create mode 100644 tests/qml/qml/controls/WebEntityView.qml
diff --git a/tests/qml/qml/controls/WebEntityView.qml b/tests/qml/qml/controls/WebEntityView.qml
new file mode 100644
index 0000000000..a6c38f4abd
--- /dev/null
+++ b/tests/qml/qml/controls/WebEntityView.qml
@@ -0,0 +1,32 @@
+//
+// WebEntityView.qml
+//
+// Created by Kunal Gosar on 16 March 2017
+// Copyright 2017 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 QtWebEngine 1.5
+
+WebEngineView {
+ id: webViewCore
+ objectName: "webEngineView"
+ width: parent !== null ? parent.width : undefined
+ height: parent !== null ? parent.height : undefined
+
+ onFeaturePermissionRequested: {
+ grantFeaturePermission(securityOrigin, feature, true);
+ }
+
+ //disable popup
+ onContextMenuRequested: {
+ request.accepted = true;
+ }
+
+ onNewViewRequested: {
+ newViewRequestedCallback(request)
+ }
+}
diff --git a/tests/qml/src/main.cpp b/tests/qml/src/main.cpp
index 022f7290f4..c23a958da7 100644
--- a/tests/qml/src/main.cpp
+++ b/tests/qml/src/main.cpp
@@ -28,52 +28,82 @@
#include
#include
#include
-
+#include
#include
#include
+#include
+
#include
-#include
#include
#include
#include
#include
#include
#include
+#include
+#include
+
+#pragma optimize("", off)
+
+namespace gl {
+ extern void initModuleGl();
+}
-class OffscreenQmlSurface : public hifi::qml::OffscreenSurface {
+QUrl getTestResource(const QString& relativePath) {
+ static QString dir;
+ if (dir.isEmpty()) {
+ QDir path(__FILE__);
+ path.cdUp();
+ dir = path.cleanPath(path.absoluteFilePath("../")) + "/";
+ qDebug() << "Resources Path: " << dir;
+ }
+ return QUrl::fromLocalFile(dir + relativePath);
+}
+
+#define DIVISIONS_X 5
+#define DIVISIONS_Y 5
+
+using QmlPtr = QSharedPointer;
+using TextureAndFence = hifi::qml::OffscreenSurface::TextureAndFence;
+
+struct QmlInfo {
+ QmlPtr surface;
+ GLuint texture{ 0 };
+ uint64_t lifetime{ 0 };
};
class TestWindow : public QWindow {
-
public:
TestWindow();
-
private:
- using TextureAndFence = hifi::qml::OffscreenSurface::TextureAndFence;
QOpenGLContext _glContext;
OffscreenGLCanvas _sharedContext;
- OffscreenQmlSurface _offscreenQml;
+ std::array, DIVISIONS_X> _surfaces;
+
QOpenGLFunctions_4_5_Core _glf;
- uint32_t _currentTexture{ 0 };
- GLsync _readFence{ 0 };
std::function _discardLamdba;
QSize _size;
+ size_t _surfaceCount{ 0 };
GLuint _fbo{ 0 };
const QSize _qmlSize{ 640, 480 };
bool _aboutToQuit{ false };
void initGl();
+ void updateSurfaces();
+ void buildSurface(QmlInfo& qmlInfo);
+ void destroySurface(QmlInfo& qmlInfo);
void resizeWindow(const QSize& size);
void draw();
void resizeEvent(QResizeEvent* ev) override;
};
TestWindow::TestWindow() {
- setSurfaceType(QSurface::OpenGLSurface);
+ Setting::init();
+ setSurfaceType(QSurface::OpenGLSurface);
QSurfaceFormat format;
format.setDepthBufferSize(24);
format.setStencilBufferSize(8);
@@ -85,11 +115,12 @@ TestWindow::TestWindow() {
show();
+
resize(QSize(800, 600));
auto timer = new QTimer(this);
timer->setTimerType(Qt::PreciseTimer);
- timer->setInterval(5);
+ timer->setInterval(30);
connect(timer, &QTimer::timeout, [&] { draw(); });
timer->start();
@@ -97,7 +128,6 @@ TestWindow::TestWindow() {
timer->stop();
_aboutToQuit = true;
});
-
}
void TestWindow::initGl() {
@@ -105,6 +135,7 @@ void TestWindow::initGl() {
if (!_glContext.create() || !_glContext.makeCurrent(this)) {
qFatal("Unable to intialize Window GL context");
}
+ gl::initModuleGl();
_glf.initializeOpenGLFunctions();
_glf.glCreateFramebuffers(1, &_fbo);
@@ -113,15 +144,104 @@ void TestWindow::initGl() {
qFatal("Unable to intialize Shared GL context");
}
hifi::qml::OffscreenSurface::setSharedContext(_sharedContext.getContext());
- _discardLamdba = _offscreenQml.getDiscardLambda();
- _offscreenQml.resize({ 640, 480 });
- _offscreenQml.load(QUrl::fromLocalFile("C:/Users/bdavi/Git/hifi/tests/qml/qml/main.qml"));
+ _discardLamdba = hifi::qml::OffscreenSurface::getDiscardLambda();
}
void TestWindow::resizeWindow(const QSize& size) {
_size = size;
}
+static const int DEFAULT_MAX_FPS = 10;
+static const int YOUTUBE_MAX_FPS = 30;
+static const QString CONTROL_URL{ "/qml/controls/WebEntityView.qml" };
+static const char* URL_PROPERTY{ "url" };
+
+QString getSourceUrl() {
+ static const std::vector SOURCE_URLS{
+ "https://www.reddit.com/wiki/random",
+ "https://en.wikipedia.org/wiki/Wikipedia:Random",
+ "https://slashdot.org/",
+ //"https://www.youtube.com/watch?v=gDXwhHm4GhM",
+ //"https://www.youtube.com/watch?v=Ch_hoYPPeGc",
+ };
+
+ auto index = rand() % SOURCE_URLS.size();
+ return SOURCE_URLS[index];
+}
+
+
+
+void TestWindow::buildSurface(QmlInfo& qmlInfo) {
+ ++_surfaceCount;
+ auto lifetimeSecs = (uint32_t)(2.0f + (randFloat() * 10.0f));
+ auto lifetimeUsecs = (USECS_PER_SECOND * lifetimeSecs);
+ qmlInfo.lifetime = lifetimeUsecs + usecTimestampNow();
+ qmlInfo.texture = 0;
+ auto& surface = qmlInfo.surface;
+ surface.reset(new hifi::qml::OffscreenSurface());
+ surface->setMaxFps(DEFAULT_MAX_FPS);
+ surface->resize(_qmlSize);
+ surface->setMaxFps(DEFAULT_MAX_FPS);
+ hifi::qml::QmlContextObjectCallback callback = [](QQmlContext* context, QQuickItem* item) {
+ item->setProperty(URL_PROPERTY, getSourceUrl());
+ };
+ surface->load(getTestResource(CONTROL_URL), callback);
+ surface->resume();
+}
+
+void TestWindow::destroySurface(QmlInfo& qmlInfo) {
+ auto& surface = qmlInfo.surface;
+ QQuickItem* rootItem = surface->getRootItem();
+ if (rootItem) {
+ QObject* obj = rootItem->findChild("webEngineView");
+ if (!obj && rootItem->objectName() == "webEngineView") {
+ obj = rootItem;
+ }
+ if (obj) {
+ // stop loading
+ QMetaObject::invokeMethod(obj, "stop");
+ }
+ }
+ surface->pause();
+ surface.reset();
+}
+
+void TestWindow::updateSurfaces() {
+ auto now = usecTimestampNow();
+ // Fetch any new textures
+ for (size_t x = 0; x < DIVISIONS_X; ++x) {
+ for (size_t y = 0; y < DIVISIONS_Y; ++y) {
+ auto& qmlInfo = _surfaces[x][y];
+ if (!qmlInfo.surface) {
+ if (randFloat() > 0.99f) {
+ buildSurface(qmlInfo);
+ } else {
+ continue;
+ }
+ }
+
+ if (now > qmlInfo.lifetime) {
+ destroySurface(qmlInfo);
+ continue;
+ }
+
+ auto& surface = qmlInfo.surface;
+ auto& currentTexture = qmlInfo.texture;
+
+ TextureAndFence newTextureAndFence;
+ if (surface->fetchTexture(newTextureAndFence)) {
+ if (currentTexture != 0) {
+ auto readFence = _glf.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+ glFlush();
+ _discardLamdba(currentTexture, readFence);
+ }
+ currentTexture = newTextureAndFence.first;
+ _glf.glWaitSync((GLsync)newTextureAndFence.second, 0, GL_TIMEOUT_IGNORED);
+ }
+ }
+ }
+}
+
void TestWindow::draw() {
if (_aboutToQuit) {
return;
@@ -140,36 +260,31 @@ void TestWindow::draw() {
return;
}
+ updateSurfaces();
+
+ auto size = this->geometry().size();
+ auto incrementX = size.width() / DIVISIONS_X;
+ auto incrementY = size.height() / DIVISIONS_Y;
+ _glf.glViewport(0, 0, size.width(), size.height());
_glf.glClearColor(1, 0, 0, 1);
_glf.glClear(GL_COLOR_BUFFER_BIT);
- TextureAndFence newTextureAndFence;
- if (_offscreenQml.fetchTexture(newTextureAndFence)) {
- if (_currentTexture) {
- _discardLamdba(_currentTexture, _readFence);
- _readFence = 0;
+ for (uint32_t x = 0; x < DIVISIONS_X; ++x) {
+ for (uint32_t y = 0; y < DIVISIONS_Y; ++y) {
+ auto& qmlInfo = _surfaces[x][y];
+ if (!qmlInfo.surface || !qmlInfo.texture) {
+ continue;
+ }
+ _glf.glNamedFramebufferTexture(_fbo, GL_COLOR_ATTACHMENT0, qmlInfo.texture, 0);
+ _glf.glBlitNamedFramebuffer(_fbo, 0,
+ // src coordinates
+ 0, 0, _qmlSize.width() - 1, _qmlSize.height() - 1,
+ // dst coordinates
+ incrementX * x, incrementY * y, incrementX * (x + 1), incrementY * (y + 1),
+ // blit mask and filter
+ GL_COLOR_BUFFER_BIT, GL_NEAREST);
}
-
- _currentTexture = newTextureAndFence.first;
- _glf.glWaitSync((GLsync)newTextureAndFence.second, 0, GL_TIMEOUT_IGNORED);
- _glf.glNamedFramebufferTexture(_fbo, GL_COLOR_ATTACHMENT0, _currentTexture, 0);
}
-
- auto diff = _size - _qmlSize;
- diff /= 2;
- auto qmlExtent = diff + _qmlSize;
-
- if (_currentTexture) {
- _glf.glBlitNamedFramebuffer(_fbo, 0,
- 0, 0, _qmlSize.width() - 1, _qmlSize.height() - 1,
- diff.width(), diff.height(), qmlExtent.width() - 1, qmlExtent.height() - 2,
- GL_COLOR_BUFFER_BIT, GL_NEAREST);
- }
-
- if (_readFence) {
- _glf.glDeleteSync(_readFence);
- }
- _readFence = _glf.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
_glf.glFlush();
_glContext.swapBuffers(this);
@@ -180,11 +295,8 @@ void TestWindow::resizeEvent(QResizeEvent* ev) {
}
int main(int argc, char** argv) {
- setupHifiApplication("QML Test");
-
QGuiApplication app(argc, argv);
TestWindow window;
app.exec();
return 0;
}
-
From 5f8149fd6873ca6ad1163927826283f1809a10c8 Mon Sep 17 00:00:00 2001
From: Brad Davis
Date: Fri, 27 Apr 2018 13:57:22 -0700
Subject: [PATCH 07/17] Remove debugging pragma
---
tests/qml/src/main.cpp | 2 --
1 file changed, 2 deletions(-)
diff --git a/tests/qml/src/main.cpp b/tests/qml/src/main.cpp
index c23a958da7..cd5a567e95 100644
--- a/tests/qml/src/main.cpp
+++ b/tests/qml/src/main.cpp
@@ -44,8 +44,6 @@
#include
#include
-#pragma optimize("", off)
-
namespace gl {
extern void initModuleGl();
}
From 575520fe3063f3d6deac5e8266b3e886ac25d88a Mon Sep 17 00:00:00 2001
From: Brad Davis
Date: Fri, 27 Apr 2018 15:08:10 -0700
Subject: [PATCH 08/17] Ensure we actually delete the QML scenegraph!
---
libraries/qml/src/qml/impl/SharedObject.cpp | 7 +-
tests/qml/qml/controls/WebEntityView.qml | 15 ++++
tests/qml/src/main.cpp | 84 ++++++++++++++-------
3 files changed, 78 insertions(+), 28 deletions(-)
diff --git a/libraries/qml/src/qml/impl/SharedObject.cpp b/libraries/qml/src/qml/impl/SharedObject.cpp
index 7bcb216ba8..2fde057ca8 100644
--- a/libraries/qml/src/qml/impl/SharedObject.cpp
+++ b/libraries/qml/src/qml/impl/SharedObject.cpp
@@ -90,14 +90,17 @@ SharedObject::~SharedObject() {
_renderControl = nullptr;
}
+ if (_rootItem) {
+ delete _rootItem;
+ _rootItem = nullptr;
+ }
+
if (_quickWindow) {
_quickWindow->destroy();
delete _quickWindow;
_quickWindow = nullptr;
}
- // _rootItem is parented to the quickWindow, so needs no explicit destruction
-
if (_qmlContext) {
auto engine = _qmlContext->engine();
delete _qmlContext;
diff --git a/tests/qml/qml/controls/WebEntityView.qml b/tests/qml/qml/controls/WebEntityView.qml
index a6c38f4abd..5bd29ef457 100644
--- a/tests/qml/qml/controls/WebEntityView.qml
+++ b/tests/qml/qml/controls/WebEntityView.qml
@@ -10,6 +10,21 @@
import QtQuick 2.5
import QtWebEngine 1.5
+import Hifi 1.0
+
+/*
+TestItem {
+ Rectangle {
+ anchors.fill: parent
+ anchors.margins: 10
+ color: "blue"
+ property string url: ""
+ ColorAnimation on color {
+ loops: Animation.Infinite; from: "blue"; to: "yellow"; duration: 1000
+ }
+ }
+}
+*/
WebEngineView {
id: webViewCore
diff --git a/tests/qml/src/main.cpp b/tests/qml/src/main.cpp
index cd5a567e95..ec4012898f 100644
--- a/tests/qml/src/main.cpp
+++ b/tests/qml/src/main.cpp
@@ -48,6 +48,17 @@ namespace gl {
extern void initModuleGl();
}
+class QTestItem : public QQuickItem {
+ Q_OBJECT
+public:
+ QTestItem(QQuickItem* parent = nullptr) : QQuickItem(parent) {
+ qDebug() << __FUNCTION__;
+ }
+
+ ~QTestItem() {
+ qDebug() << __FUNCTION__;
+ }
+};
QUrl getTestResource(const QString& relativePath) {
static QString dir;
@@ -89,9 +100,10 @@ private:
GLuint _fbo{ 0 };
const QSize _qmlSize{ 640, 480 };
bool _aboutToQuit{ false };
+ uint64_t _createStopTime;
void initGl();
void updateSurfaces();
- void buildSurface(QmlInfo& qmlInfo);
+ void buildSurface(QmlInfo& qmlInfo, bool allowVideo);
void destroySurface(QmlInfo& qmlInfo);
void resizeWindow(const QSize& size);
void draw();
@@ -111,8 +123,10 @@ TestWindow::TestWindow() {
QSurfaceFormat::setDefaultFormat(format);
setFormat(format);
- show();
+ qmlRegisterType("Hifi", 1, 0, "TestItem");
+ show();
+ _createStopTime = usecTimestampNow() + (3000u * USECS_PER_SECOND);
resize(QSize(800, 600));
@@ -167,40 +181,56 @@ QString getSourceUrl() {
return SOURCE_URLS[index];
}
+#define CACHE_WEBVIEWS 0
+#if CACHE_WEBVIEWS
+static std::list _cache;
+#endif
-void TestWindow::buildSurface(QmlInfo& qmlInfo) {
+hifi::qml::QmlContextObjectCallback callback = [](QQmlContext* context, QQuickItem* item) {
+ item->setProperty(URL_PROPERTY, getSourceUrl());
+};
+
+void TestWindow::buildSurface(QmlInfo& qmlInfo, bool allowVideo) {
++_surfaceCount;
- auto lifetimeSecs = (uint32_t)(2.0f + (randFloat() * 10.0f));
+ auto lifetimeSecs = (uint32_t)(5.0f + (randFloat() * 10.0f));
auto lifetimeUsecs = (USECS_PER_SECOND * lifetimeSecs);
qmlInfo.lifetime = lifetimeUsecs + usecTimestampNow();
qmlInfo.texture = 0;
- auto& surface = qmlInfo.surface;
- surface.reset(new hifi::qml::OffscreenSurface());
- surface->setMaxFps(DEFAULT_MAX_FPS);
- surface->resize(_qmlSize);
- surface->setMaxFps(DEFAULT_MAX_FPS);
- hifi::qml::QmlContextObjectCallback callback = [](QQmlContext* context, QQuickItem* item) {
- item->setProperty(URL_PROPERTY, getSourceUrl());
- };
- surface->load(getTestResource(CONTROL_URL), callback);
- surface->resume();
+#if CACHE_WEBVIEWS
+ if (_cache.empty()) {
+ _cache.emplace_back(new hifi::qml::OffscreenSurface());
+ auto& surface = _cache.back();
+ surface->load(getTestResource(CONTROL_URL));
+ surface->setMaxFps(DEFAULT_MAX_FPS);
+ }
+ qmlInfo.surface = _cache.front();
+ _cache.pop_front();
+#else
+ qmlInfo.surface.reset(new hifi::qml::OffscreenSurface());
+ qmlInfo.surface->load(getTestResource(CONTROL_URL));
+ qmlInfo.surface->setMaxFps(DEFAULT_MAX_FPS);
+#endif
+
+ qmlInfo.surface->resize(_qmlSize);
+ auto url = allowVideo ? "https://www.youtube.com/watch?v=gDXwhHm4GhM" : getSourceUrl();
+ qmlInfo.surface->getRootItem()->setProperty(URL_PROPERTY, url);
+ qmlInfo.surface->resume();
}
+
void TestWindow::destroySurface(QmlInfo& qmlInfo) {
auto& surface = qmlInfo.surface;
- QQuickItem* rootItem = surface->getRootItem();
- if (rootItem) {
- QObject* obj = rootItem->findChild("webEngineView");
- if (!obj && rootItem->objectName() == "webEngineView") {
- obj = rootItem;
- }
- if (obj) {
- // stop loading
- QMetaObject::invokeMethod(obj, "stop");
- }
+ auto webView = surface->getRootItem();
+ if (webView) {
+ // stop loading
+ QMetaObject::invokeMethod(webView, "stop");
+ webView->setProperty(URL_PROPERTY, "about:blank");
}
surface->pause();
+#if CACHE_WEBVIEWS
+ _cache.push_back(surface);
+#endif
surface.reset();
}
@@ -211,8 +241,8 @@ void TestWindow::updateSurfaces() {
for (size_t y = 0; y < DIVISIONS_Y; ++y) {
auto& qmlInfo = _surfaces[x][y];
if (!qmlInfo.surface) {
- if (randFloat() > 0.99f) {
- buildSurface(qmlInfo);
+ if (now < _createStopTime && randFloat() > 0.99f) {
+ buildSurface(qmlInfo, x == 0 && y == 0);
} else {
continue;
}
@@ -298,3 +328,5 @@ int main(int argc, char** argv) {
app.exec();
return 0;
}
+
+#include "main.moc"
\ No newline at end of file
From 8e42bb8c877e693cdf29bdcb2e206316a2bc8a85 Mon Sep 17 00:00:00 2001
From: Brad Davis
Date: Fri, 27 Apr 2018 16:04:07 -0700
Subject: [PATCH 09/17] Restore the stop functionality for a browser view when
it's being destroyed
---
.../qml/controls/FlickableWebViewCore.qml | 5 ++
interface/resources/qml/controls/WebView.qml | 4 ++
.../src/RenderableWebEntityItem.cpp | 11 ++-
tests/qml/src/main.cpp | 68 +++++--------------
4 files changed, 32 insertions(+), 56 deletions(-)
diff --git a/interface/resources/qml/controls/FlickableWebViewCore.qml b/interface/resources/qml/controls/FlickableWebViewCore.qml
index 8e7db44b7d..943f15e1de 100644
--- a/interface/resources/qml/controls/FlickableWebViewCore.qml
+++ b/interface/resources/qml/controls/FlickableWebViewCore.qml
@@ -21,6 +21,7 @@ Item {
signal newViewRequestedCallback(var request)
signal loadingChangedCallback(var loadRequest)
+
width: parent.width
property bool interactive: false
@@ -29,6 +30,10 @@ Item {
id: hifi
}
+ function stop() {
+ webViewCore.stop();
+ }
+
function unfocus() {
webViewCore.runJavaScript("if (document.activeElement) document.activeElement.blur();", function(result) {
console.log('unfocus completed: ', result);
diff --git a/interface/resources/qml/controls/WebView.qml b/interface/resources/qml/controls/WebView.qml
index 931c64e1ef..71bf69fdc8 100644
--- a/interface/resources/qml/controls/WebView.qml
+++ b/interface/resources/qml/controls/WebView.qml
@@ -21,6 +21,10 @@ Item {
property bool passwordField: false
property alias flickable: webroot.interactive
+ function stop() {
+ webroot.stop();
+ }
+
// FIXME - Keyboard HMD only: Make Interface either set keyboardRaised property directly in OffscreenQmlSurface
// or provide HMDinfo object to QML in RenderableWebEntityItem and do the following.
/*
diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp
index 7ad74d1eee..693e3d0cf4 100644
--- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp
@@ -318,8 +318,10 @@ bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
void WebEntityRenderer::destroyWebSurface() {
QSharedPointer webSurface;
+ ContentType contentType{ ContentType::NoContent };
withWriteLock([&] {
webSurface.swap(_webSurface);
+ std::swap(contentType, _contentType);
});
if (webSurface) {
@@ -328,12 +330,9 @@ void WebEntityRenderer::destroyWebSurface() {
// Fix for crash in QtWebEngineCore when rapidly switching domains
// Call stop on the QWebEngineView before destroying OffscreenQMLSurface.
- if (rootItem) {
- QObject* obj = rootItem->findChild("webEngineView");
- if (obj) {
- // stop loading
- QMetaObject::invokeMethod(obj, "stop");
- }
+ if (rootItem && contentType == ContentType::HtmlContent) {
+ // stop loading
+ QMetaObject::invokeMethod(rootItem, "stop");
}
webSurface->pause();
diff --git a/tests/qml/src/main.cpp b/tests/qml/src/main.cpp
index ec4012898f..349ac55d88 100644
--- a/tests/qml/src/main.cpp
+++ b/tests/qml/src/main.cpp
@@ -45,19 +45,15 @@
#include
namespace gl {
- extern void initModuleGl();
+extern void initModuleGl();
}
class QTestItem : public QQuickItem {
Q_OBJECT
public:
- QTestItem(QQuickItem* parent = nullptr) : QQuickItem(parent) {
- qDebug() << __FUNCTION__;
- }
+ QTestItem(QQuickItem* parent = nullptr) : QQuickItem(parent) { qDebug() << __FUNCTION__; }
- ~QTestItem() {
- qDebug() << __FUNCTION__;
- }
+ ~QTestItem() { qDebug() << __FUNCTION__; }
};
QUrl getTestResource(const QString& relativePath) {
@@ -71,7 +67,6 @@ QUrl getTestResource(const QString& relativePath) {
return QUrl::fromLocalFile(dir + relativePath);
}
-
#define DIVISIONS_X 5
#define DIVISIONS_Y 5
@@ -164,61 +159,41 @@ void TestWindow::resizeWindow(const QSize& size) {
}
static const int DEFAULT_MAX_FPS = 10;
-static const int YOUTUBE_MAX_FPS = 30;
static const QString CONTROL_URL{ "/qml/controls/WebEntityView.qml" };
static const char* URL_PROPERTY{ "url" };
-QString getSourceUrl() {
+QString getSourceUrl(bool video) {
static const std::vector SOURCE_URLS{
"https://www.reddit.com/wiki/random",
"https://en.wikipedia.org/wiki/Wikipedia:Random",
"https://slashdot.org/",
- //"https://www.youtube.com/watch?v=gDXwhHm4GhM",
- //"https://www.youtube.com/watch?v=Ch_hoYPPeGc",
};
- auto index = rand() % SOURCE_URLS.size();
- return SOURCE_URLS[index];
+ static const std::vector VIDEO_SOURCE_URLS{
+ "https://www.youtube.com/watch?v=gDXwhHm4GhM",
+ "https://www.youtube.com/watch?v=Ch_hoYPPeGc",
+ };
+
+ const auto& sourceUrls = video ? VIDEO_SOURCE_URLS : SOURCE_URLS;
+ auto index = rand() % sourceUrls.size();
+ return sourceUrls[index];
}
-#define CACHE_WEBVIEWS 0
-
-#if CACHE_WEBVIEWS
-static std::list _cache;
-#endif
-
-hifi::qml::QmlContextObjectCallback callback = [](QQmlContext* context, QQuickItem* item) {
- item->setProperty(URL_PROPERTY, getSourceUrl());
-};
-
-void TestWindow::buildSurface(QmlInfo& qmlInfo, bool allowVideo) {
+void TestWindow::buildSurface(QmlInfo& qmlInfo, bool video) {
++_surfaceCount;
auto lifetimeSecs = (uint32_t)(5.0f + (randFloat() * 10.0f));
auto lifetimeUsecs = (USECS_PER_SECOND * lifetimeSecs);
qmlInfo.lifetime = lifetimeUsecs + usecTimestampNow();
qmlInfo.texture = 0;
-#if CACHE_WEBVIEWS
- if (_cache.empty()) {
- _cache.emplace_back(new hifi::qml::OffscreenSurface());
- auto& surface = _cache.back();
- surface->load(getTestResource(CONTROL_URL));
- surface->setMaxFps(DEFAULT_MAX_FPS);
- }
- qmlInfo.surface = _cache.front();
- _cache.pop_front();
-#else
qmlInfo.surface.reset(new hifi::qml::OffscreenSurface());
- qmlInfo.surface->load(getTestResource(CONTROL_URL));
+ qmlInfo.surface->load(getTestResource(CONTROL_URL), [video](QQmlContext* context, QQuickItem* item) {
+ item->setProperty(URL_PROPERTY, getSourceUrl(video));
+ });
qmlInfo.surface->setMaxFps(DEFAULT_MAX_FPS);
-#endif
-
qmlInfo.surface->resize(_qmlSize);
- auto url = allowVideo ? "https://www.youtube.com/watch?v=gDXwhHm4GhM" : getSourceUrl();
- qmlInfo.surface->getRootItem()->setProperty(URL_PROPERTY, url);
qmlInfo.surface->resume();
}
-
void TestWindow::destroySurface(QmlInfo& qmlInfo) {
auto& surface = qmlInfo.surface;
auto webView = surface->getRootItem();
@@ -228,9 +203,6 @@ void TestWindow::destroySurface(QmlInfo& qmlInfo) {
webView->setProperty(URL_PROPERTY, "about:blank");
}
surface->pause();
-#if CACHE_WEBVIEWS
- _cache.push_back(surface);
-#endif
surface.reset();
}
@@ -296,7 +268,6 @@ void TestWindow::draw() {
_glf.glViewport(0, 0, size.width(), size.height());
_glf.glClearColor(1, 0, 0, 1);
_glf.glClear(GL_COLOR_BUFFER_BIT);
-
for (uint32_t x = 0; x < DIVISIONS_X; ++x) {
for (uint32_t y = 0; y < DIVISIONS_Y; ++y) {
auto& qmlInfo = _surfaces[x][y];
@@ -313,8 +284,6 @@ void TestWindow::draw() {
GL_COLOR_BUFFER_BIT, GL_NEAREST);
}
}
- _glf.glFlush();
-
_glContext.swapBuffers(this);
}
@@ -325,8 +294,7 @@ void TestWindow::resizeEvent(QResizeEvent* ev) {
int main(int argc, char** argv) {
QGuiApplication app(argc, argv);
TestWindow window;
- app.exec();
- return 0;
+ return app.exec();
}
-#include "main.moc"
\ No newline at end of file
+#include "main.moc"
From 7db07db9e2bdfd4bb92544a951e8e94b38f6ac97 Mon Sep 17 00:00:00 2001
From: Liv Erickson
Date: Mon, 30 Apr 2018 13:55:01 -0700
Subject: [PATCH 10/17] remove menu item at cleanup
---
scripts/system/edit.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/scripts/system/edit.js b/scripts/system/edit.js
index 6d2b1f129b..c99c8d401a 100644
--- a/scripts/system/edit.js
+++ b/scripts/system/edit.js
@@ -1291,6 +1291,7 @@ function cleanupModelMenus() {
Menu.removeMenuItem("Edit", MENU_EASE_ON_FOCUS);
Menu.removeMenuItem("Edit", MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE);
Menu.removeMenuItem("Edit", MENU_SHOW_ZONES_IN_EDIT_MODE);
+ Menu.removeMenuItem("Edit", MENU_CREATE_ENTITIES_GRABBABLE);
}
Script.scriptEnding.connect(function () {
From f9bda6df15f125b2352dc19a1f6a70b38cea10a2 Mon Sep 17 00:00:00 2001
From: David Rowe
Date: Tue, 1 May 2018 11:36:37 +1200
Subject: [PATCH 11/17] Fix LODManager JSDoc
---
interface/src/LODManager.h | 49 +++++++++++++++++---------------------
1 file changed, 22 insertions(+), 27 deletions(-)
diff --git a/interface/src/LODManager.h b/interface/src/LODManager.h
index e8737d92ae..96d92e91e9 100644
--- a/interface/src/LODManager.h
+++ b/interface/src/LODManager.h
@@ -9,11 +9,6 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
-/**jsdoc
- * The LOD class manages your Level of Detail functions within interface
- * @namespace LODManager
- */
-
#ifndef hifi_LODManager_h
#define hifi_LODManager_h
@@ -39,10 +34,32 @@ const float ADJUST_LOD_MIN_SIZE_SCALE = DEFAULT_OCTREE_SIZE_SCALE * 0.04f;
class AABox;
+/**jsdoc
+ * The LOD class manages your Level of Detail functions within Interface.
+ * @namespace LODManager
+ * @property {number} presentTime Read-only.
+ * @property {number} engineRunTime Read-only.
+ * @property {number} gpuTime Read-only.
+ * @property {number} avgRenderTime Read-only.
+ * @property {number} fps Read-only.
+ * @property {number} lodLevel Read-only.
+ * @property {number} lodDecreaseFPS Read-only.
+ * @property {number} lodIncreaseFPS Read-only.
+ */
+
class LODManager : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
+ Q_PROPERTY(float presentTime READ getPresentTime)
+ Q_PROPERTY(float engineRunTime READ getEngineRunTime)
+ Q_PROPERTY(float gpuTime READ getGPUTime)
+ Q_PROPERTY(float avgRenderTime READ getAverageRenderTime)
+ Q_PROPERTY(float fps READ getMaxTheoreticalFPS)
+ Q_PROPERTY(float lodLevel READ getLODLevel)
+ Q_PROPERTY(float lodDecreaseFPS READ getLODDecreaseFPS)
+ Q_PROPERTY(float lodIncreaseFPS READ getLODIncreaseFPS)
+
public:
/**jsdoc
@@ -138,28 +155,6 @@ public:
*/
Q_INVOKABLE float getLODIncreaseFPS() const;
- /**jsdoc
- * @namespace LODManager
- * @property {number} presentTime Read-only.
- * @property {number} engineRunTime Read-only.
- * @property {number} gpuTime Read-only.
- * @property {number} avgRenderTime Read-only.
- * @property {number} fps Read-only.
- * @property {number} lodLevel Read-only.
- * @property {number} lodDecreaseFPS Read-only.
- * @property {number} lodIncreaseFPS Read-only.
- */
-
- Q_PROPERTY(float presentTime READ getPresentTime)
- Q_PROPERTY(float engineRunTime READ getEngineRunTime)
- Q_PROPERTY(float gpuTime READ getGPUTime)
- Q_PROPERTY(float avgRenderTime READ getAverageRenderTime)
- Q_PROPERTY(float fps READ getMaxTheoreticalFPS)
- Q_PROPERTY(float lodLevel READ getLODLevel)
-
- Q_PROPERTY(float lodDecreaseFPS READ getLODDecreaseFPS)
- Q_PROPERTY(float lodIncreaseFPS READ getLODIncreaseFPS)
-
float getPresentTime() const { return _presentTime; }
float getEngineRunTime() const { return _engineRunTime; }
float getGPUTime() const { return _gpuTime; }
From f69ee410748700e547c7fffb5e5a5ee2021b20f6 Mon Sep 17 00:00:00 2001
From: Zach Fox
Date: Mon, 30 Apr 2018 17:13:44 -0700
Subject: [PATCH 12/17] Prevent crash in ImageProvider when tablet isn't yet
initialized
---
interface/src/commerce/Wallet.cpp | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp
index 3e0e4adf18..42c8b9973d 100644
--- a/interface/src/commerce/Wallet.cpp
+++ b/interface/src/commerce/Wallet.cpp
@@ -615,9 +615,12 @@ void Wallet::updateImageProvider() {
securityImageProvider->setSecurityImage(_securityImage);
// inform tablet security image provider
- QQmlEngine* tabletEngine = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system")->getTabletSurface()->getSurfaceContext()->engine();
- securityImageProvider = reinterpret_cast(tabletEngine->imageProvider(SecurityImageProvider::PROVIDER_NAME));
- securityImageProvider->setSecurityImage(_securityImage);
+ auto tablet = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system");
+ if (tablet) {
+ QQmlEngine* tabletEngine = tablet->getTabletSurface()->getSurfaceContext()->engine();
+ securityImageProvider = reinterpret_cast(tabletEngine->imageProvider(SecurityImageProvider::PROVIDER_NAME));
+ securityImageProvider->setSecurityImage(_securityImage);
+ }
}
void Wallet::chooseSecurityImage(const QString& filename) {
From a1fc1c4810df8eec32b52969b45fd2e0d5c7448b Mon Sep 17 00:00:00 2001
From: David Rowe
Date: Tue, 1 May 2018 21:55:14 +1200
Subject: [PATCH 13/17] Add JSDoc for Entity preload and unload signals
---
libraries/script-engine/src/ScriptEngine.cpp | 33 ++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp
index c79ffffec7..f0a13cc62b 100644
--- a/libraries/script-engine/src/ScriptEngine.cpp
+++ b/libraries/script-engine/src/ScriptEngine.cpp
@@ -2161,6 +2161,32 @@ void ScriptEngine::loadEntityScript(const EntityItemID& entityID, const QString&
}, forceRedownload);
}
+/**jsdoc
+ * Triggered when the script starts for a user.
+ * Note: Can only be connected to via this.preload = function (...) { ... }
in the entity script.
+ * Available in: | Client Entity Scripts | Server Entity Scripts |
---|
+ * @function Entities.preload
+ * @param {Uuid} entityID - The ID of the entity that the script is running in.
+ * @returns {Signal}
+ * @example Get the ID of the entity that a client entity script is running in.
+ * var entityScript = (function () {
+ * this.entityID = Uuid.NULL;
+ *
+ * this.preload = function (entityID) {
+ * this.entityID = entityID;
+ * print("Entity ID: " + this.entityID);
+ * };
+ * );
+ *
+ * var entityID = Entities.addEntity({
+ * type: "Box",
+ * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })),
+ * dimensions: { x: 0.5, y: 0.5, z: 0.5 },
+ * color: { red: 255, green: 0, blue: 0 },
+ * script: "(" + entityScript + ")", // Could host the script on a Web server instead.
+ * lifetime: 300 // Delete after 5 minutes.
+ * });
+ */
// since all of these operations can be asynch we will always do the actual work in the response handler
// for the download
void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success , const QString& status) {
@@ -2345,6 +2371,13 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co
processDeferredEntityLoads(entityScript, entityID);
}
+/**jsdoc
+ * Triggered when the script terminates for a user.
+ * Note: Can only be connected to via this.unoad = function () { ... }
in the entity script.
+ * Available in: | Client Entity Scripts | Server Entity Scripts |
---|
+ * @function Entities.unload
+ * @returns {Signal}
+ */
void ScriptEngine::unloadEntityScript(const EntityItemID& entityID, bool shouldRemoveFromMap) {
if (QThread::currentThread() != thread()) {
#ifdef THREAD_DEBUGGING
From 65f5915372912077c51319937415f7396df5d923 Mon Sep 17 00:00:00 2001
From: David Rowe
Date: Tue, 1 May 2018 21:55:27 +1200
Subject: [PATCH 14/17] Add JSDoc for startFarTrigger etc. calls on entity
scripts
---
.../scripting/ControllerScriptingInterface.h | 57 ++++++++++++++++++-
1 file changed, 56 insertions(+), 1 deletion(-)
diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h
index f19caa8478..4fceda3b04 100644
--- a/interface/src/scripting/ControllerScriptingInterface.h
+++ b/interface/src/scripting/ControllerScriptingInterface.h
@@ -28,7 +28,7 @@ class ScriptEngine;
/**jsdoc
* The Controller API provides facilities to interact with computer and controller hardware.
*
- * Functions:
+ * Functions
*
* Properties
*
@@ -143,6 +143,61 @@ class ScriptEngine;
* - {@link Controller.stopInputPlayback|stopInputPlayback}
*
*
+ * Entity Methods:
+ *
+ * The default scripts implement hand controller actions that use {@link Entities.callEntityMethod} to call entity script
+ * methods, if present in the entity being interacted with.
+ *
+ *
+ *
+ * Method Name | Description | Example |
+ *
+ *
+ *
+ * startFarTrigger
continueFarTrigger
stopFarTrigger |
+ * These methods are called when a user is more than 0.3m away from the entity, the entity is triggerable, and the
+ * user starts, continues, or stops squeezing the trigger. |
+ *
+ * A light switch that can be toggled on and off from a distance. |
+ *
+ *
+ * startNearTrigger
continueNearTrigger
stopNearTrigger |
+ * These methods are called when a user is less than 0.3m away from the entity, the entity is triggerable, and the
+ * user starts, continues, or stops squeezing the trigger. |
+ * A doorbell that can be rung when a user is near. |
+ *
+ *
+ * startDistanceGrab
continueDistanceGrab
|
+ * These methods are called when a user is more than 0.3m away from the entity, the entity is either cloneable, or
+ * grabbable and not locked, and the user starts or continues to squeeze the trigger. |
+ * A comet that emits icy particle trails when a user is dragging it through the sky. |
+ *
+ *
+ * startNearGrab
continueNearGrab
|
+ * These methods are called when a user is less than 0.3m away from the entity, the entity is either cloneable, or
+ * grabbable and not locked, and the user starts or continues to squeeze the trigger. |
+ * A ball that glows when it's being held close. |
+ *
+ *
+ * releaseGrab |
+ * This method is called when a user releases the trigger when having been either distance or near grabbing an
+ * entity. |
+ * Turn off the ball glow or comet trail with the user finishes grabbing it. |
+ *
+ *
+ * startEquip
continueEquip
releaseEquip |
+ * These methods are called when a user starts, continues, or stops equipping an entity. |
+ * A glass that stays in the user's hand after the trigger is clicked. |
+ *
+ *
+ *
+ * All the entity methods are called with the following two arguments:
+ *
+ * - The entity ID.
+ * - A string,
"hand,userID"
— where "hand" is "left"
or "right"
, and "userID"
+ * is the user's {@link MyAvatar|MyAvatar.sessionUUID}.
+ *
+ *
* @namespace Controller
*
* @property {Controller.Actions} Actions - Predefined actions on Interface and the user's avatar. These can be used as end
From 2482da3c259752b670fc4f2acbf3c087073b80ad Mon Sep 17 00:00:00 2001
From: David Rowe
Date: Tue, 1 May 2018 21:55:45 +1200
Subject: [PATCH 15/17] Miscellaneous JSDoc fixes noticed in passing
---
.../controllers/src/controllers/impl/MappingBuilderProxy.h | 6 ++++--
.../controllers/src/controllers/impl/RouteBuilderProxy.h | 3 ++-
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h
index 86a43c0c13..3c3858e2ba 100644
--- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h
+++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h
@@ -39,7 +39,9 @@ class UserInputMapper;
* methods.
* Use {@link Controller.parseMapping} or {@link Controller.loadMapping} to load a {@link Controller.MappingJSON}.
*
- * Enable the mapping using {@link MappingObject#enable|enable} or {@link Controller.enableMapping} for it to take effect.
+ *
+ *
Enable the mapping using {@link MappingObject#enable|enable} or {@link Controller.enableMapping} for it to take
+ * effect.
*
* Mappings and their routes are applied according to the following rules:
*
@@ -49,7 +51,7 @@ class UserInputMapper;
* output that already has a route the new route is ignored.
* - New mappings override previous mappings: each output is processed using the route in the most recently enabled
* mapping that contains that output.
- *
+ *
*
* @class MappingObject
*/
diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h
index 0336638068..d33f3e3383 100644
--- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h
+++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h
@@ -29,7 +29,8 @@ class ScriptingInterface;
* A route in a {@link MappingObject} used by the {@link Controller} API.
*
* Create a route using {@link MappingObject} methods and apply this object's methods to process it, terminating with
- * {@link RouteObject#to} to apply it to a Standard
control, action, or script function.
+ * {@link RouteObject#to} to apply it to a Standard
control, action, or script function. Note: Loops are not
+ * permitted.
*
* Some methods apply to routes with number data, some apply routes with {@link Pose} data, and some apply to both route
* types.
From f1e1c6a34817563ac0a9da876a968561361b9ee6 Mon Sep 17 00:00:00 2001
From: Seth Alves
Date: Tue, 1 May 2018 09:15:44 -0700
Subject: [PATCH 16/17] fix physics related crash-on-exit
---
libraries/physics/src/PhysicalEntitySimulation.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp
index ab7c2ec252..3ea3616b15 100644
--- a/libraries/physics/src/PhysicalEntitySimulation.cpp
+++ b/libraries/physics/src/PhysicalEntitySimulation.cpp
@@ -153,6 +153,8 @@ void PhysicalEntitySimulation::clearEntitiesInternal() {
// remove the objects (aka MotionStates) from physics
_physicsEngine->removeSetOfObjects(_physicalObjects);
+ clearOwnershipData();
+
// delete the MotionStates
for (auto stateItr : _physicalObjects) {
EntityMotionState* motionState = static_cast(&(*stateItr));
@@ -165,7 +167,6 @@ void PhysicalEntitySimulation::clearEntitiesInternal() {
_physicalObjects.clear();
// clear all other lists specific to this derived class
- clearOwnershipData();
_entitiesToRemoveFromPhysics.clear();
_entitiesToAddToPhysics.clear();
_incomingChanges.clear();
From cd76336e6a937128a5b8f227ae57a3e856431914 Mon Sep 17 00:00:00 2001
From: Zach Fox
Date: Tue, 1 May 2018 09:54:23 -0700
Subject: [PATCH 17/17] Actually fix
---
interface/src/commerce/Wallet.cpp | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp
index 42c8b9973d..35e6ca1c92 100644
--- a/interface/src/commerce/Wallet.cpp
+++ b/interface/src/commerce/Wallet.cpp
@@ -615,11 +615,14 @@ void Wallet::updateImageProvider() {
securityImageProvider->setSecurityImage(_securityImage);
// inform tablet security image provider
- auto tablet = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system");
+ TabletProxy* tablet = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system");
if (tablet) {
- QQmlEngine* tabletEngine = tablet->getTabletSurface()->getSurfaceContext()->engine();
- securityImageProvider = reinterpret_cast(tabletEngine->imageProvider(SecurityImageProvider::PROVIDER_NAME));
- securityImageProvider->setSecurityImage(_securityImage);
+ OffscreenQmlSurface* tabletSurface = tablet->getTabletSurface();
+ if (tabletSurface) {
+ QQmlEngine* tabletEngine = tabletSurface->getSurfaceContext()->engine();
+ securityImageProvider = reinterpret_cast(tabletEngine->imageProvider(SecurityImageProvider::PROVIDER_NAME));
+ securityImageProvider->setSecurityImage(_securityImage);
+ }
}
}