Merge branch 'master' into 21418

This commit is contained in:
David Rowe 2017-07-06 16:56:10 +12:00
commit cfd155c198
112 changed files with 3446 additions and 534 deletions

View file

@ -16,6 +16,7 @@
#include <QThread> #include <QThread>
#include <QTimer> #include <QTimer>
#include <shared/QtHelpers.h>
#include <AccountManager.h> #include <AccountManager.h>
#include <AddressManager.h> #include <AddressManager.h>
#include <Assignment.h> #include <Assignment.h>
@ -141,7 +142,7 @@ void AssignmentClient::stopAssignmentClient() {
QThread* currentAssignmentThread = _currentAssignment->thread(); QThread* currentAssignmentThread = _currentAssignment->thread();
// ask the current assignment to stop // ask the current assignment to stop
QMetaObject::invokeMethod(_currentAssignment, "stop", Qt::BlockingQueuedConnection); BLOCKING_INVOKE_METHOD(_currentAssignment, "stop");
// ask the current assignment to delete itself on its thread // ask the current assignment to delete itself on its thread
_currentAssignment->deleteLater(); _currentAssignment->deleteLater();

View file

@ -9,8 +9,12 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include <QCommandLineParser> #include "AssignmentClientApp.h"
#include <QThread>
#include <QtCore/QCommandLineParser>
#include <QtCore/QDir>
#include <QtCore/QStandardPaths>
#include <QtCore/QThread>
#include <LogHandler.h> #include <LogHandler.h>
#include <SharedUtil.h> #include <SharedUtil.h>
@ -20,10 +24,6 @@
#include "Assignment.h" #include "Assignment.h"
#include "AssignmentClient.h" #include "AssignmentClient.h"
#include "AssignmentClientMonitor.h" #include "AssignmentClientMonitor.h"
#include "AssignmentClientApp.h"
#include <QtCore/QDir>
#include <QtCore/QStandardPaths>
AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) : AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
QCoreApplication(argc, argv) QCoreApplication(argc, argv)
@ -87,6 +87,9 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
const QCommandLineOption logDirectoryOption(ASSIGNMENT_LOG_DIRECTORY, "directory to store logs", "log-directory"); const QCommandLineOption logDirectoryOption(ASSIGNMENT_LOG_DIRECTORY, "directory to store logs", "log-directory");
parser.addOption(logDirectoryOption); parser.addOption(logDirectoryOption);
const QCommandLineOption parentPIDOption(PARENT_PID_OPTION, "PID of the parent process", "parent-pid");
parser.addOption(parentPIDOption);
if (!parser.parse(QCoreApplication::arguments())) { if (!parser.parse(QCoreApplication::arguments())) {
qCritical() << parser.errorText() << endl; qCritical() << parser.errorText() << endl;
parser.showHelp(); parser.showHelp();
@ -203,6 +206,16 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
} }
} }
if (parser.isSet(parentPIDOption)) {
bool ok = false;
int parentPID = parser.value(parentPIDOption).toInt(&ok);
if (ok) {
qDebug() << "Parent process PID is" << parentPID;
watchParentProcess(parentPID);
}
}
QThread::currentThread()->setObjectName("main thread"); QThread::currentThread()->setObjectName("main thread");
DependencyManager::registerInheritance<LimitedNodeList, NodeList>(); DependencyManager::registerInheritance<LimitedNodeList, NodeList>();

View file

@ -131,7 +131,6 @@ void AssignmentClientMonitor::aboutToQuit() {
void AssignmentClientMonitor::spawnChildClient() { void AssignmentClientMonitor::spawnChildClient() {
QProcess* assignmentClient = new QProcess(this); QProcess* assignmentClient = new QProcess(this);
// unparse the parts of the command-line that the child cares about // unparse the parts of the command-line that the child cares about
QStringList _childArguments; QStringList _childArguments;
if (_assignmentPool != "") { if (_assignmentPool != "") {
@ -160,6 +159,9 @@ void AssignmentClientMonitor::spawnChildClient() {
_childArguments.append("--" + ASSIGNMENT_CLIENT_MONITOR_PORT_OPTION); _childArguments.append("--" + ASSIGNMENT_CLIENT_MONITOR_PORT_OPTION);
_childArguments.append(QString::number(DependencyManager::get<NodeList>()->getLocalSockAddr().getPort())); _childArguments.append(QString::number(DependencyManager::get<NodeList>()->getLocalSockAddr().getPort()));
_childArguments.append("--" + PARENT_PID_OPTION);
_childArguments.append(QString::number(QCoreApplication::applicationPid()));
QString nowString, stdoutFilenameTemp, stderrFilenameTemp, stdoutPathTemp, stderrPathTemp; QString nowString, stdoutFilenameTemp, stderrFilenameTemp, stdoutPathTemp, stderrPathTemp;

View file

@ -13,6 +13,7 @@
#include <QThread> #include <QThread>
#include <glm/gtx/transform.hpp> #include <glm/gtx/transform.hpp>
#include <shared/QtHelpers.h>
#include <GLMHelpers.h> #include <GLMHelpers.h>
#include <AnimUtil.h> #include <AnimUtil.h>
#include "ScriptableAvatar.h" #include "ScriptableAvatar.h"
@ -49,7 +50,7 @@ void ScriptableAvatar::stopAnimation() {
AnimationDetails ScriptableAvatar::getAnimationDetails() { AnimationDetails ScriptableAvatar::getAnimationDetails() {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
AnimationDetails result; AnimationDetails result;
QMetaObject::invokeMethod(this, "getAnimationDetails", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "getAnimationDetails",
Q_RETURN_ARG(AnimationDetails, result)); Q_RETURN_ARG(AnimationDetails, result));
return result; return result;
} }

View file

@ -56,19 +56,17 @@ elseif (${CMAKE_CXX_COMPILER_ID} MATCHES "GNU")
endif() endif()
function(_fbx_find_library _name _lib _suffix) function(_fbx_find_library _name _lib _suffix)
if (MSVC12) if (MSVC_VERSION EQUAL 1910)
set(VS_PREFIX vs2017)
elseif (MSVC_VERSION EQUAL 1900)
set(VS_PREFIX vs2015)
elseif (MSVC_VERSION EQUAL 1800)
set(VS_PREFIX vs2013) set(VS_PREFIX vs2013)
endif() elseif (MSVC_VERSION EQUAL 1700)
if (MSVC11)
set(VS_PREFIX vs2012) set(VS_PREFIX vs2012)
endif() elseif (MSVC_VERSION EQUAL 1600)
if (MSVC10)
set(VS_PREFIX vs2010) set(VS_PREFIX vs2010)
endif() elseif (MSVC_VERSION EQUAL 1500)
if (MSVC90)
set(VS_PREFIX vs2008) set(VS_PREFIX vs2008)
endif() endif()

View file

@ -221,6 +221,8 @@ void DomainServer::parseCommandLine() {
const QCommandLineOption masterConfigOption("master-config", "Deprecated config-file option"); const QCommandLineOption masterConfigOption("master-config", "Deprecated config-file option");
parser.addOption(masterConfigOption); parser.addOption(masterConfigOption);
const QCommandLineOption parentPIDOption(PARENT_PID_OPTION, "PID of the parent process", "parent-pid");
parser.addOption(parentPIDOption);
if (!parser.parse(QCoreApplication::arguments())) { if (!parser.parse(QCoreApplication::arguments())) {
qWarning() << parser.errorText() << endl; qWarning() << parser.errorText() << endl;
@ -249,6 +251,17 @@ void DomainServer::parseCommandLine() {
_overrideDomainID = true; _overrideDomainID = true;
qDebug() << "domain-server ID is" << _overridingDomainID; qDebug() << "domain-server ID is" << _overridingDomainID;
} }
if (parser.isSet(parentPIDOption)) {
bool ok = false;
int parentPID = parser.value(parentPIDOption).toInt(&ok);
if (ok) {
qDebug() << "Parent process PID is" << parentPID;
watchParentProcess(parentPID);
}
}
} }
DomainServer::~DomainServer() { DomainServer::~DomainServer() {

View file

@ -63,10 +63,7 @@ void RenderingClient::sendAvatarPacket() {
} }
void RenderingClient::cleanupBeforeQuit() { void RenderingClient::cleanupBeforeQuit() {
DependencyManager::get<AudioClient>()->cleanupBeforeQuit();
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(),
"stop", Qt::BlockingQueuedConnection);
// destroy the AudioClient so it and its thread will safely go down // destroy the AudioClient so it and its thread will safely go down
DependencyManager::destroy<AudioClient>(); DependencyManager::destroy<AudioClient>();
} }

View file

@ -25,11 +25,12 @@ Rectangle {
signal canceled() signal canceled()
signal restart() signal restart()
property int count: 3 property int count: 5
property string calibratingText: "CALIBRATING..." property string calibratingText: "CALIBRATING..."
property string calibratingCountText: "CALIBRATION STARTING IN" property string calibratingCountText: "CALIBRATION STARTING IN"
property string calibrationSuccess: "CALIBRATION COMPLETED" property string calibrationSuccess: "CALIBRATION COMPLETED"
property string calibrationFailed: "CALIBRATION FAILED" property string calibrationFailed: "CALIBRATION FAILED"
property string instructionText: "Please stand in a T-Pose during calibration"
HifiConstants { id: hifi } HifiConstants { id: hifi }
visible: true visible: true
@ -158,6 +159,15 @@ Rectangle {
onClicked: { onClicked: {
restart(); restart();
statusText.color = hifi.colors.blueHighlight;
statusText.text = info.calibratingCountText;
directions.text = instructionText;
countDown.visible = true;
busyIndicator.running = true;
busyRotation.from = 0
busyRotation.to = 360
busyIndicator.source = blueIndicator;
closeWindow.stop();
numberAnimation.stop(); numberAnimation.stop();
info.count = (timer.interval / 1000); info.count = (timer.interval / 1000);
numberAnimation.start(); numberAnimation.start();
@ -178,6 +188,7 @@ Rectangle {
} }
} }
function start(interval, countNumber) { function start(interval, countNumber) {
countDown.visible = true; countDown.visible = true;
statusText.color = hifi.colors.blueHighlight; statusText.color = hifi.colors.blueHighlight;
@ -201,6 +212,7 @@ Rectangle {
busyIndicator.running = false; busyIndicator.running = false;
statusText.text = info.calibrationSuccess statusText.text = info.calibrationSuccess
statusText.color = hifi.colors.greenHighlight statusText.color = hifi.colors.greenHighlight
directions.text = "SUCCESS"
closeWindow.start(); closeWindow.start();
} }

View file

@ -458,7 +458,7 @@ Rectangle {
width: glyphButton.width + calibrationText.width + padding width: glyphButton.width + calibrationText.width + padding
height: hifi.dimensions.controlLineHeight height: hifi.dimensions.controlLineHeight
anchors.top: bottomSeperator.bottom anchors.top: bottomSeperator.bottom
anchors.topMargin: 10 anchors.topMargin: 15
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: leftMargin anchors.leftMargin: leftMargin
@ -590,16 +590,24 @@ Rectangle {
lastConfiguration = composeConfigurationSettings(); lastConfiguration = composeConfigurationSettings();
} }
Component.onDestruction: {
var settings = InputConfiguration.configurationSettings(pluginName);
var data = {
"num_pucks": settings["puckCount"]
}
UserActivityLogger.logAction("mocap_ui_close_dialog", data);
}
HifiControls.SpinBox { HifiControls.SpinBox {
id: timeToCalibrate id: timeToCalibrate
width: 70 width: 70
anchors.top: calibrationButton.bottom anchors.top: calibrationButton.bottom
anchors.topMargin: 40 anchors.topMargin: 20
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: leftMargin anchors.leftMargin: leftMargin
minimumValue: 3 minimumValue: 5
value: 3 value: 5
colorScheme: hifi.colorSchemes.dark colorScheme: hifi.colorSchemes.dark
onEditingFinished: { onEditingFinished: {
@ -641,17 +649,39 @@ Rectangle {
to: 0 to: 0
} }
function logAction(action, status) {
console.log("calibrated from ui");
var data = {
"num_pucks": status["puckCount"],
"puck_configuration": status["configuration"],
"head_puck": status["head_puck"],
"hand_puck": status["hand_pucks"]
}
UserActivityLogger.logAction(action, data);
}
function calibrationStatusInfo(status) { function calibrationStatusInfo(status) {
var calibrationScreen = stack.currentItem; var calibrationScreen = stack.currentItem;
if (!status["UI"]) {
calibratingScreen = screen.createObject();
stack.push(calibratingScreen);
}
if (status["calibrated"]) { if (status["calibrated"]) {
calibrationScreen.success(); calibrationScreen.success();
if (status["UI"]) {
logAction("mocap_ui_success", status);
}
} else if (!status["calibrated"]) { } else if (!status["calibrated"]) {
var uncalibrated = status["success"]; calibrationScreen.failure();
if (!uncalibrated) {
calibrationScreen.failure(); if (status["UI"]) {
logAction("mocap_ui_failed", status);
} }
} }
updateCalibrationButton(); updateCalibrationButton();
} }
@ -717,6 +747,12 @@ Rectangle {
initializeButtonState(); initializeButtonState();
updateCalibrationText(); updateCalibrationText();
var data = {
"num_pucks": settings["puckCount"]
};
UserActivityLogger.logAction("mocap_ui_open_dialog", data);
} }
function displayTrackerConfiguration(type) { function displayTrackerConfiguration(type) {

View file

@ -48,6 +48,7 @@
#include <gl/QOpenGLContextWrapper.h> #include <gl/QOpenGLContextWrapper.h>
#include <shared/QtHelpers.h>
#include <shared/GlobalAppProperties.h> #include <shared/GlobalAppProperties.h>
#include <StatTracker.h> #include <StatTracker.h>
#include <Trace.h> #include <Trace.h>
@ -1207,15 +1208,26 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// Make sure we don't time out during slow operations at startup // Make sure we don't time out during slow operations at startup
updateHeartbeat(); updateHeartbeat();
int SAVE_SETTINGS_INTERVAL = 10 * MSECS_PER_SECOND; // Let's save every seconds for now
connect(&_settingsTimer, &QTimer::timeout, this, &Application::saveSettings); QTimer* settingsTimer = new QTimer();
connect(&_settingsThread, SIGNAL(started()), &_settingsTimer, SLOT(start())); moveToNewNamedThread(settingsTimer, "Settings Thread", [this, settingsTimer]{
connect(&_settingsThread, SIGNAL(finished()), &_settingsTimer, SLOT(stop())); connect(qApp, &Application::beforeAboutToQuit, [this, settingsTimer]{
_settingsTimer.moveToThread(&_settingsThread); // Disconnect the signal from the save settings
_settingsTimer.setSingleShot(false); QObject::disconnect(settingsTimer, &QTimer::timeout, this, &Application::saveSettings);
_settingsTimer.setInterval(SAVE_SETTINGS_INTERVAL); // 10s, Qt::CoarseTimer acceptable // Stop the settings timer
_settingsThread.setPriority(QThread::LowestPriority); settingsTimer->stop();
_settingsThread.start(); // Delete it (this will trigger the thread destruction
settingsTimer->deleteLater();
// Mark the settings thread as finished, so we know we can safely save in the main application
// shutdown code
_settingsGuard.trigger();
});
int SAVE_SETTINGS_INTERVAL = 10 * MSECS_PER_SECOND; // Let's save every seconds for now
settingsTimer->setSingleShot(false);
settingsTimer->setInterval(SAVE_SETTINGS_INTERVAL); // 10s, Qt::CoarseTimer acceptable
QObject::connect(settingsTimer, &QTimer::timeout, this, &Application::saveSettings);
}, QThread::LowestPriority);
if (Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson)) { if (Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson)) {
getMyAvatar()->setBoomLength(MyAvatar::ZOOM_MIN); // So that camera doesn't auto-switch to third person. getMyAvatar()->setBoomLength(MyAvatar::ZOOM_MIN); // So that camera doesn't auto-switch to third person.
@ -1643,7 +1655,7 @@ QString Application::getUserAgent() {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QString userAgent; QString userAgent;
QMetaObject::invokeMethod(this, "getUserAgent", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, userAgent)); BLOCKING_INVOKE_METHOD(this, "getUserAgent", Q_RETURN_ARG(QString, userAgent));
return userAgent; return userAgent;
} }
@ -1801,11 +1813,13 @@ void Application::cleanupBeforeQuit() {
locationUpdateTimer.stop(); locationUpdateTimer.stop();
identityPacketTimer.stop(); identityPacketTimer.stop();
pingTimer.stop(); pingTimer.stop();
QMetaObject::invokeMethod(&_settingsTimer, "stop", Qt::BlockingQueuedConnection);
// save state // Wait for the settings thread to shut down, and save the settings one last time when it's safe
_settingsThread.quit(); if (_settingsGuard.wait()) {
saveSettings(); // save state
saveSettings();
}
_window->saveGeometry(); _window->saveGeometry();
// Destroy third party processes after scripts have finished using them. // Destroy third party processes after scripts have finished using them.
@ -1829,8 +1843,7 @@ void Application::cleanupBeforeQuit() {
// FIXME: something else is holding a reference to AudioClient, // FIXME: something else is holding a reference to AudioClient,
// so it must be explicitly synchronously stopped here // so it must be explicitly synchronously stopped here
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), DependencyManager::get<AudioClient>()->cleanupBeforeQuit();
"cleanupBeforeQuit", Qt::BlockingQueuedConnection);
// destroy Audio so it and its threads have a chance to go down safely // destroy Audio so it and its threads have a chance to go down safely
// this must happen after QML, as there are unexplained audio crashes originating in qtwebengine // this must happen after QML, as there are unexplained audio crashes originating in qtwebengine

View file

@ -25,6 +25,7 @@
#include <QtWidgets/QApplication> #include <QtWidgets/QApplication>
#include <QtWidgets/QUndoStack> #include <QtWidgets/QUndoStack>
#include <ThreadHelpers.h>
#include <AbstractScriptingServicesInterface.h> #include <AbstractScriptingServicesInterface.h>
#include <AbstractViewStateInterface.h> #include <AbstractViewStateInterface.h>
#include <EntityEditPacketSender.h> #include <EntityEditPacketSender.h>
@ -596,8 +597,7 @@ private:
bool _notifiedPacketVersionMismatchThisDomain; bool _notifiedPacketVersionMismatchThisDomain;
QThread _settingsThread; ConditionalGuard _settingsGuard;
QTimer _settingsTimer;
GLCanvas* _glWidget{ nullptr }; GLCanvas* _glWidget{ nullptr };

View file

@ -25,6 +25,7 @@
#endif #endif
#include <shared/QtHelpers.h>
#include <AvatarData.h> #include <AvatarData.h>
#include <PerfStat.h> #include <PerfStat.h>
#include <RegisteredMetaTypes.h> #include <RegisteredMetaTypes.h>
@ -482,7 +483,7 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersection(const PickRay&
const QScriptValue& avatarIdsToDiscard) { const QScriptValue& avatarIdsToDiscard) {
RayToAvatarIntersectionResult result; RayToAvatarIntersectionResult result;
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(const_cast<AvatarManager*>(this), "findRayIntersection", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(const_cast<AvatarManager*>(this), "findRayIntersection",
Q_RETURN_ARG(RayToAvatarIntersectionResult, result), Q_RETURN_ARG(RayToAvatarIntersectionResult, result),
Q_ARG(const PickRay&, ray), Q_ARG(const PickRay&, ray),
Q_ARG(const QScriptValue&, avatarIdsToInclude), Q_ARG(const QScriptValue&, avatarIdsToInclude),

View file

@ -21,6 +21,7 @@
#include <QtCore/QTimer> #include <QtCore/QTimer>
#include <shared/QtHelpers.h>
#include <scripting/HMDScriptingInterface.h> #include <scripting/HMDScriptingInterface.h>
#include <AccountManager.h> #include <AccountManager.h>
#include <AddressManager.h> #include <AddressManager.h>
@ -897,7 +898,7 @@ void MyAvatar::restoreAnimation() {
QStringList MyAvatar::getAnimationRoles() { QStringList MyAvatar::getAnimationRoles() {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QStringList result; QStringList result;
QMetaObject::invokeMethod(this, "getAnimationRoles", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QStringList, result)); BLOCKING_INVOKE_METHOD(this, "getAnimationRoles", Q_RETURN_ARG(QStringList, result));
return result; return result;
} }
return _skeletonModel->getRig().getAnimationRoles(); return _skeletonModel->getRig().getAnimationRoles();
@ -1368,7 +1369,7 @@ void MyAvatar::resetFullAvatarURL() {
void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName) { void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "useFullAvatarURL", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "useFullAvatarURL",
Q_ARG(const QUrl&, fullAvatarURL), Q_ARG(const QUrl&, fullAvatarURL),
Q_ARG(const QString&, modelName)); Q_ARG(const QString&, modelName));
return; return;
@ -1394,7 +1395,7 @@ void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelN
void MyAvatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) { void MyAvatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setAttachmentData", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "setAttachmentData",
Q_ARG(const QVector<AttachmentData>, attachmentData)); Q_ARG(const QVector<AttachmentData>, attachmentData));
return; return;
} }
@ -2371,7 +2372,7 @@ bool MyAvatar::safeLanding(const glm::vec3& position) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
bool result; bool result;
QMetaObject::invokeMethod(this, "safeLanding", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result), Q_ARG(const glm::vec3&, position)); BLOCKING_INVOKE_METHOD(this, "safeLanding", Q_RETURN_ARG(bool, result), Q_ARG(const glm::vec3&, position));
return result; return result;
} }
glm::vec3 better; glm::vec3 better;

View file

@ -11,6 +11,8 @@
#include "Audio.h" #include "Audio.h"
#include <shared/QtHelpers.h>
#include "Application.h" #include "Application.h"
#include "AudioClient.h" #include "AudioClient.h"
#include "ui/AvatarInputs.h" #include "ui/AvatarInputs.h"
@ -49,27 +51,22 @@ float Audio::loudnessToLevel(float loudness) {
Audio::Audio() : _devices(_contextIsHMD) { Audio::Audio() : _devices(_contextIsHMD) {
auto client = DependencyManager::get<AudioClient>().data(); auto client = DependencyManager::get<AudioClient>().data();
connect(client, &AudioClient::muteToggled, this, &Audio::onMutedChanged); connect(client, &AudioClient::muteToggled, this, &Audio::onMutedChanged);
connect(client, &AudioClient::noiseReductionChanged, this, &Audio::onNoiseReductionChanged);
connect(client, &AudioClient::inputLoudnessChanged, this, &Audio::onInputLoudnessChanged); connect(client, &AudioClient::inputLoudnessChanged, this, &Audio::onInputLoudnessChanged);
connect(client, &AudioClient::inputVolumeChanged, this, &Audio::onInputVolumeChanged);
connect(this, &Audio::contextChanged, &_devices, &AudioDevices::onContextChanged); connect(this, &Audio::contextChanged, &_devices, &AudioDevices::onContextChanged);
connect(&_devices._inputs, &AudioDeviceList::deviceChanged, this, &Audio::onInputChanged);
enableNoiseReduction(enableNoiseReductionSetting.get()); enableNoiseReduction(enableNoiseReductionSetting.get());
} }
void Audio::setMuted(bool isMuted) { void Audio::setMuted(bool isMuted) {
if (_isMuted != isMuted) { if (_isMuted != isMuted) {
auto client = DependencyManager::get<AudioClient>().data(); auto client = DependencyManager::get<AudioClient>().data();
QMetaObject::invokeMethod(client, "toggleMute", Qt::BlockingQueuedConnection); QMetaObject::invokeMethod(client, "toggleMute");
_isMuted = isMuted;
emit mutedChanged(_isMuted);
} }
} }
void Audio::onMutedChanged() { void Audio::onMutedChanged() {
auto client = DependencyManager::get<AudioClient>().data(); bool isMuted = DependencyManager::get<AudioClient>()->isMuted();
bool isMuted;
QMetaObject::invokeMethod(client, "isMuted", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, isMuted));
if (_isMuted != isMuted) { if (_isMuted != isMuted) {
_isMuted = isMuted; _isMuted = isMuted;
emit mutedChanged(_isMuted); emit mutedChanged(_isMuted);
@ -79,11 +76,16 @@ void Audio::onMutedChanged() {
void Audio::enableNoiseReduction(bool enable) { void Audio::enableNoiseReduction(bool enable) {
if (_enableNoiseReduction != enable) { if (_enableNoiseReduction != enable) {
auto client = DependencyManager::get<AudioClient>().data(); auto client = DependencyManager::get<AudioClient>().data();
QMetaObject::invokeMethod(client, "setNoiseReduction", Qt::BlockingQueuedConnection, Q_ARG(bool, enable)); QMetaObject::invokeMethod(client, "setNoiseReduction", Q_ARG(bool, enable));
enableNoiseReductionSetting.set(enable); enableNoiseReductionSetting.set(enable);
_enableNoiseReduction = enable; }
emit noiseReductionChanged(enable); }
void Audio::onNoiseReductionChanged() {
bool noiseReductionEnabled = DependencyManager::get<AudioClient>()->isNoiseReductionEnabled();
if (_enableNoiseReduction != noiseReductionEnabled) {
_enableNoiseReduction = noiseReductionEnabled;
emit noiseReductionChanged(_enableNoiseReduction);
} }
} }
@ -93,19 +95,11 @@ void Audio::setInputVolume(float volume) {
if (_inputVolume != volume) { if (_inputVolume != volume) {
auto client = DependencyManager::get<AudioClient>().data(); auto client = DependencyManager::get<AudioClient>().data();
QMetaObject::invokeMethod(client, "setInputVolume", Qt::BlockingQueuedConnection, Q_ARG(float, volume)); QMetaObject::invokeMethod(client, "setInputVolume", Q_ARG(float, volume));
_inputVolume = volume;
emit inputVolumeChanged(_inputVolume);
} }
} }
// different audio input devices may have different volumes void Audio::onInputVolumeChanged(float volume) {
void Audio::onInputChanged() {
auto client = DependencyManager::get<AudioClient>().data();
float volume;
QMetaObject::invokeMethod(client, "getInputVolume", Qt::BlockingQueuedConnection, Q_RETURN_ARG(float, volume));
if (_inputVolume != volume) { if (_inputVolume != volume) {
_inputVolume = volume; _inputVolume = volume;
emit inputVolumeChanged(_inputVolume); emit inputVolumeChanged(_inputVolume);

View file

@ -62,9 +62,12 @@ signals:
void contextChanged(const QString& context); void contextChanged(const QString& context);
public slots: public slots:
void onMutedChanged();
void onContextChanged(); void onContextChanged();
void onInputChanged();
private slots:
void onMutedChanged();
void onNoiseReductionChanged();
void onInputVolumeChanged(float volume);
void onInputLoudnessChanged(float loudness); void onInputLoudnessChanged(float loudness);
protected: protected:

View file

@ -11,6 +11,8 @@
#include <map> #include <map>
#include <shared/QtHelpers.h>
#include "AudioDevices.h" #include "AudioDevices.h"
#include "Application.h" #include "Application.h"
@ -71,22 +73,14 @@ bool AudioDeviceList::setData(const QModelIndex& index, const QVariant& value, i
bool AudioDeviceList::setDevice(int row, bool fromUser) { bool AudioDeviceList::setDevice(int row, bool fromUser) {
bool success = false; bool success = false;
auto& device = _devices[row]; auto& device = _devices[row];
_userSelection = fromUser;
// skip if already selected // skip if already selected
if (!device.selected) { if (!device.selected) {
auto client = DependencyManager::get<AudioClient>(); auto client = DependencyManager::get<AudioClient>();
QMetaObject::invokeMethod(client.data(), "switchAudioDevice", Qt::BlockingQueuedConnection, QMetaObject::invokeMethod(client.data(), "switchAudioDevice",
Q_RETURN_ARG(bool, success),
Q_ARG(QAudio::Mode, _mode), Q_ARG(QAudio::Mode, _mode),
Q_ARG(const QAudioDeviceInfo&, device.info)); Q_ARG(const QAudioDeviceInfo&, device.info));
if (success) {
device.selected = true;
if (fromUser) {
emit deviceSelected(device.info, _selectedDevice);
}
emit deviceChanged(device.info);
}
} }
emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0)); emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0));
@ -135,12 +129,12 @@ void AudioDeviceList::resetDevice(bool contextIsHMD, const QString& device) {
} }
void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device) { void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device) {
auto oldDevice = _selectedDevice;
_selectedDevice = device; _selectedDevice = device;
QModelIndex index; QModelIndex index;
for (auto i = 0; i < _devices.size(); ++i) { for (auto i = 0; i < _devices.size(); ++i) {
AudioDevice& device = _devices[i]; AudioDevice& device = _devices[i];
if (device.selected && device.info != _selectedDevice) { if (device.selected && device.info != _selectedDevice) {
device.selected = false; device.selected = false;
} else if (device.info == _selectedDevice) { } else if (device.info == _selectedDevice) {
@ -149,6 +143,11 @@ void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device) {
} }
} }
if (_userSelection) {
_userSelection = false;
emit deviceSelected(_selectedDevice, oldDevice);
}
emit deviceChanged(_selectedDevice); emit deviceChanged(_selectedDevice);
emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0)); emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0));
} }

View file

@ -58,7 +58,7 @@ private:
static QHash<int, QByteArray> _roles; static QHash<int, QByteArray> _roles;
static Qt::ItemFlags _flags; static Qt::ItemFlags _flags;
bool _userSelection { false };
QAudio::Mode _mode; QAudio::Mode _mode;
QAudioDeviceInfo _selectedDevice; QAudioDeviceInfo _selectedDevice;
QList<AudioDevice> _devices; QList<AudioDevice> _devices;

View file

@ -8,9 +8,12 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include "Application.h"
#include "ClipboardScriptingInterface.h" #include "ClipboardScriptingInterface.h"
#include <shared/QtHelpers.h>
#include "Application.h"
ClipboardScriptingInterface::ClipboardScriptingInterface() { ClipboardScriptingInterface::ClipboardScriptingInterface() {
} }
@ -24,7 +27,7 @@ float ClipboardScriptingInterface::getClipboardContentsLargestDimension() {
bool ClipboardScriptingInterface::exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs) { bool ClipboardScriptingInterface::exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs) {
bool retVal; bool retVal;
QMetaObject::invokeMethod(qApp, "exportEntities", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(qApp, "exportEntities",
Q_RETURN_ARG(bool, retVal), Q_RETURN_ARG(bool, retVal),
Q_ARG(const QString&, filename), Q_ARG(const QString&, filename),
Q_ARG(const QVector<EntityItemID>&, entityIDs)); Q_ARG(const QVector<EntityItemID>&, entityIDs));
@ -33,7 +36,7 @@ bool ClipboardScriptingInterface::exportEntities(const QString& filename, const
bool ClipboardScriptingInterface::exportEntities(const QString& filename, float x, float y, float z, float s) { bool ClipboardScriptingInterface::exportEntities(const QString& filename, float x, float y, float z, float s) {
bool retVal; bool retVal;
QMetaObject::invokeMethod(qApp, "exportEntities", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(qApp, "exportEntities",
Q_RETURN_ARG(bool, retVal), Q_RETURN_ARG(bool, retVal),
Q_ARG(const QString&, filename), Q_ARG(const QString&, filename),
Q_ARG(float, x), Q_ARG(float, x),
@ -45,7 +48,7 @@ bool ClipboardScriptingInterface::exportEntities(const QString& filename, float
bool ClipboardScriptingInterface::importEntities(const QString& filename) { bool ClipboardScriptingInterface::importEntities(const QString& filename) {
bool retVal; bool retVal;
QMetaObject::invokeMethod(qApp, "importEntities", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(qApp, "importEntities",
Q_RETURN_ARG(bool, retVal), Q_RETURN_ARG(bool, retVal),
Q_ARG(const QString&, filename)); Q_ARG(const QString&, filename));
return retVal; return retVal;
@ -53,7 +56,7 @@ bool ClipboardScriptingInterface::importEntities(const QString& filename) {
QVector<EntityItemID> ClipboardScriptingInterface::pasteEntities(glm::vec3 position) { QVector<EntityItemID> ClipboardScriptingInterface::pasteEntities(glm::vec3 position) {
QVector<EntityItemID> retVal; QVector<EntityItemID> retVal;
QMetaObject::invokeMethod(qApp, "pasteEntities", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(qApp, "pasteEntities",
Q_RETURN_ARG(QVector<EntityItemID>, retVal), Q_RETURN_ARG(QVector<EntityItemID>, retVal),
Q_ARG(float, position.x), Q_ARG(float, position.x),
Q_ARG(float, position.y), Q_ARG(float, position.y),

View file

@ -13,6 +13,10 @@
#include <QObject> #include <QObject>
#include <glm/glm.hpp>
#include <EntityItemID.h>
/**jsdoc /**jsdoc
* @namespace Clipboard * @namespace Clipboard
*/ */

View file

@ -13,6 +13,7 @@
#include <QtScript/QScriptContext> #include <QtScript/QScriptContext>
#include <shared/QtHelpers.h>
#include <avatar/AvatarManager.h> #include <avatar/AvatarManager.h>
#include <display-plugins/DisplayPlugin.h> #include <display-plugins/DisplayPlugin.h>
#include <display-plugins/CompositorHelper.h> #include <display-plugins/CompositorHelper.h>
@ -152,22 +153,31 @@ QString HMDScriptingInterface::preferredAudioOutput() const {
return qApp->getActiveDisplayPlugin()->getPreferredAudioOutDevice(); return qApp->getActiveDisplayPlugin()->getPreferredAudioOutDevice();
} }
bool HMDScriptingInterface::setHandLasers(int hands, bool enabled, const glm::vec4& color, const glm::vec3& direction) const { bool HMDScriptingInterface::setHandLasers(int hands, bool enabled, const glm::vec4& color, const glm::vec3& direction) {
if (QThread::currentThread() != thread()) {
bool result;
BLOCKING_INVOKE_METHOD(this, "setHandLasers", Q_RETURN_ARG(bool, result),
Q_ARG(int, hands), Q_ARG(bool, enabled), Q_ARG(glm::vec4, color), Q_ARG(glm::vec3, direction));
return result;
}
auto offscreenUi = DependencyManager::get<OffscreenUi>(); auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->executeOnUiThread([offscreenUi, enabled] { offscreenUi->getDesktop()->setProperty("hmdHandMouseActive", enabled);
offscreenUi->getDesktop()->setProperty("hmdHandMouseActive", enabled);
});
return qApp->getActiveDisplayPlugin()->setHandLaser(hands, return qApp->getActiveDisplayPlugin()->setHandLaser(hands,
enabled ? DisplayPlugin::HandLaserMode::Overlay : DisplayPlugin::HandLaserMode::None, enabled ? DisplayPlugin::HandLaserMode::Overlay : DisplayPlugin::HandLaserMode::None,
color, direction); color, direction);
} }
bool HMDScriptingInterface::setExtraLaser(const glm::vec3& worldStart, bool enabled, const glm::vec4& color, const glm::vec3& direction) const { bool HMDScriptingInterface::setExtraLaser(const glm::vec3& worldStart, bool enabled, const glm::vec4& color, const glm::vec3& direction) {
auto offscreenUi = DependencyManager::get<OffscreenUi>(); if (QThread::currentThread() != thread()) {
offscreenUi->executeOnUiThread([offscreenUi, enabled] { bool result;
offscreenUi->getDesktop()->setProperty("hmdHandMouseActive", enabled); BLOCKING_INVOKE_METHOD(this, "setExtraLaser", Q_RETURN_ARG(bool, result),
}); Q_ARG(glm::vec3, worldStart), Q_ARG(bool, enabled), Q_ARG(glm::vec4, color), Q_ARG(glm::vec3, direction));
return result;
}
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->getDesktop()->setProperty("hmdHandMouseActive", enabled);
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
auto sensorToWorld = myAvatar->getSensorToWorldMatrix(); auto sensorToWorld = myAvatar->getSensorToWorldMatrix();
@ -179,11 +189,11 @@ bool HMDScriptingInterface::setExtraLaser(const glm::vec3& worldStart, bool enab
color, sensorStart, sensorDirection); color, sensorStart, sensorDirection);
} }
void HMDScriptingInterface::disableExtraLaser() const { void HMDScriptingInterface::disableExtraLaser() {
setExtraLaser(vec3(0), false, vec4(0), vec3(0)); setExtraLaser(vec3(0), false, vec4(0), vec3(0));
} }
void HMDScriptingInterface::disableHandLasers(int hands) const { void HMDScriptingInterface::disableHandLasers(int hands) {
setHandLasers(hands, false, vec4(0), vec3(0)); setHandLasers(hands, false, vec4(0), vec3(0));
} }

View file

@ -51,11 +51,11 @@ public:
Q_INVOKABLE void requestHideHandControllers(); Q_INVOKABLE void requestHideHandControllers();
Q_INVOKABLE bool shouldShowHandControllers() const; Q_INVOKABLE bool shouldShowHandControllers() const;
Q_INVOKABLE bool setHandLasers(int hands, bool enabled, const glm::vec4& color, const glm::vec3& direction) const; Q_INVOKABLE bool setHandLasers(int hands, bool enabled, const glm::vec4& color, const glm::vec3& direction);
Q_INVOKABLE void disableHandLasers(int hands) const; Q_INVOKABLE void disableHandLasers(int hands);
Q_INVOKABLE bool setExtraLaser(const glm::vec3& worldStart, bool enabled, const glm::vec4& color, const glm::vec3& direction) const; Q_INVOKABLE bool setExtraLaser(const glm::vec3& worldStart, bool enabled, const glm::vec4& color, const glm::vec3& direction);
Q_INVOKABLE void disableExtraLaser() const; Q_INVOKABLE void disableExtraLaser();
/// Suppress the activation of any on-screen keyboard so that a script operation will /// Suppress the activation of any on-screen keyboard so that a script operation will

View file

@ -14,6 +14,7 @@
#include <QtCore/QCoreApplication> #include <QtCore/QCoreApplication>
#include <QtCore/QThread> #include <QtCore/QThread>
#include <shared/QtHelpers.h>
#include <MenuItemProperties.h> #include <MenuItemProperties.h>
#include "Menu.h" #include "Menu.h"
@ -43,7 +44,7 @@ bool MenuScriptingInterface::menuExists(const QString& menu) {
return Menu::getInstance()->menuExists(menu); return Menu::getInstance()->menuExists(menu);
} }
bool result; bool result;
QMetaObject::invokeMethod(Menu::getInstance(), "menuExists", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(Menu::getInstance(), "menuExists",
Q_RETURN_ARG(bool, result), Q_RETURN_ARG(bool, result),
Q_ARG(const QString&, menu)); Q_ARG(const QString&, menu));
return result; return result;
@ -86,7 +87,7 @@ bool MenuScriptingInterface::menuItemExists(const QString& menu, const QString&
return Menu::getInstance()->menuItemExists(menu, menuitem); return Menu::getInstance()->menuItemExists(menu, menuitem);
} }
bool result; bool result;
QMetaObject::invokeMethod(Menu::getInstance(), "menuItemExists", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(Menu::getInstance(), "menuItemExists",
Q_RETURN_ARG(bool, result), Q_RETURN_ARG(bool, result),
Q_ARG(const QString&, menu), Q_ARG(const QString&, menu),
Q_ARG(const QString&, menuitem)); Q_ARG(const QString&, menuitem));
@ -114,7 +115,7 @@ bool MenuScriptingInterface::isOptionChecked(const QString& menuOption) {
return Menu::getInstance()->isOptionChecked(menuOption); return Menu::getInstance()->isOptionChecked(menuOption);
} }
bool result; bool result;
QMetaObject::invokeMethod(Menu::getInstance(), "isOptionChecked", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(Menu::getInstance(), "isOptionChecked",
Q_RETURN_ARG(bool, result), Q_RETURN_ARG(bool, result),
Q_ARG(const QString&, menuOption)); Q_ARG(const QString&, menuOption));
return result; return result;
@ -131,7 +132,7 @@ bool MenuScriptingInterface::isMenuEnabled(const QString& menuOption) {
return Menu::getInstance()->isOptionChecked(menuOption); return Menu::getInstance()->isOptionChecked(menuOption);
} }
bool result; bool result;
QMetaObject::invokeMethod(Menu::getInstance(), "isMenuEnabled", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(Menu::getInstance(), "isMenuEnabled",
Q_RETURN_ARG(bool, result), Q_RETURN_ARG(bool, result),
Q_ARG(const QString&, menuOption)); Q_ARG(const QString&, menuOption));
return result; return result;
@ -157,7 +158,7 @@ bool MenuScriptingInterface::isInfoViewVisible(const QString& path) {
} }
bool result; bool result;
QMetaObject::invokeMethod(Menu::getInstance(), "isInfoViewVisible", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(Menu::getInstance(), "isInfoViewVisible",
Q_RETURN_ARG(bool, result), Q_ARG(const QString&, path)); Q_RETURN_ARG(bool, result), Q_ARG(const QString&, path));
return result; return result;
} }

View file

@ -11,6 +11,7 @@
#include <QtCore/QLoggingCategory> #include <QtCore/QLoggingCategory>
#include <QtCore/QThread> #include <QtCore/QThread>
#include <shared/QtHelpers.h>
#include <DependencyManager.h> #include <DependencyManager.h>
#include <Trace.h> #include <Trace.h>
#include <StatTracker.h> #include <StatTracker.h>
@ -57,20 +58,25 @@ void TestScriptingInterface::waitIdle() {
} }
bool TestScriptingInterface::loadTestScene(QString scene) { bool TestScriptingInterface::loadTestScene(QString scene) {
if (QThread::currentThread() != thread()) {
bool result;
BLOCKING_INVOKE_METHOD(this, "loadTestScene", Q_RETURN_ARG(bool, result), Q_ARG(QString, scene));
return result;
}
static const QString TEST_ROOT = "https://raw.githubusercontent.com/highfidelity/hifi_tests/master/"; static const QString TEST_ROOT = "https://raw.githubusercontent.com/highfidelity/hifi_tests/master/";
static const QString TEST_BINARY_ROOT = "https://hifi-public.s3.amazonaws.com/test_scene_data/"; static const QString TEST_BINARY_ROOT = "https://hifi-public.s3.amazonaws.com/test_scene_data/";
static const QString TEST_SCRIPTS_ROOT = TEST_ROOT + "scripts/"; static const QString TEST_SCRIPTS_ROOT = TEST_ROOT + "scripts/";
static const QString TEST_SCENES_ROOT = TEST_ROOT + "scenes/"; static const QString TEST_SCENES_ROOT = TEST_ROOT + "scenes/";
return DependencyManager::get<OffscreenUi>()->returnFromUiThread([scene]()->QVariant {
DependencyManager::get<ResourceManager>()->setUrlPrefixOverride("atp:/", TEST_BINARY_ROOT + scene + ".atp/"); DependencyManager::get<ResourceManager>()->setUrlPrefixOverride("atp:/", TEST_BINARY_ROOT + scene + ".atp/");
auto tree = qApp->getEntities()->getTree(); auto tree = qApp->getEntities()->getTree();
auto treeIsClient = tree->getIsClient(); auto treeIsClient = tree->getIsClient();
// Force the tree to accept the load regardless of permissions // Force the tree to accept the load regardless of permissions
tree->setIsClient(false); tree->setIsClient(false);
auto result = tree->readFromURL(TEST_SCENES_ROOT + scene + ".json"); auto result = tree->readFromURL(TEST_SCENES_ROOT + scene + ".json");
tree->setIsClient(treeIsClient); tree->setIsClient(treeIsClient);
return result; return result;
}).toBool();
} }
bool TestScriptingInterface::startTracing(QString logrules) { bool TestScriptingInterface::startTracing(QString logrules) {

View file

@ -9,11 +9,14 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include "WindowScriptingInterface.h"
#include <QClipboard> #include <QClipboard>
#include <QtCore/QDir> #include <QtCore/QDir>
#include <QMessageBox> #include <QMessageBox>
#include <QScriptValue> #include <QScriptValue>
#include <shared/QtHelpers.h>
#include <SettingHandle.h> #include <SettingHandle.h>
#include <display-plugins/CompositorHelper.h> #include <display-plugins/CompositorHelper.h>
@ -24,8 +27,6 @@
#include "Menu.h" #include "Menu.h"
#include "OffscreenUi.h" #include "OffscreenUi.h"
#include "WindowScriptingInterface.h"
static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
static const QString LAST_BROWSE_LOCATION_SETTING = "LastBrowseLocation"; static const QString LAST_BROWSE_LOCATION_SETTING = "LastBrowseLocation";
static const QString LAST_BROWSE_ASSETS_LOCATION_SETTING = "LastBrowseAssetsLocation"; static const QString LAST_BROWSE_ASSETS_LOCATION_SETTING = "LastBrowseAssetsLocation";
@ -316,7 +317,7 @@ bool WindowScriptingInterface::isPhysicsEnabled() {
int WindowScriptingInterface::openMessageBox(QString title, QString text, int buttons, int defaultButton) { int WindowScriptingInterface::openMessageBox(QString title, QString text, int buttons, int defaultButton) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
int result; int result;
QMetaObject::invokeMethod(this, "openMessageBox", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "openMessageBox",
Q_RETURN_ARG(int, result), Q_RETURN_ARG(int, result),
Q_ARG(QString, title), Q_ARG(QString, title),
Q_ARG(QString, text), Q_ARG(QString, text),

View file

@ -18,6 +18,8 @@
#include <QtScript/QScriptValue> #include <QtScript/QScriptValue>
#include <QtWidgets/QMessageBox> #include <QtWidgets/QMessageBox>
#include <DependencyManager.h>
class CustomPromptResult { class CustomPromptResult {
public: public:
QVariant value; QVariant value;

View file

@ -15,6 +15,7 @@
#include <QScrollBar> #include <QScrollBar>
#include <QtConcurrent/QtConcurrentRun> #include <QtConcurrent/QtConcurrentRun>
#include <shared/QtHelpers.h>
#include <ScriptEngines.h> #include <ScriptEngines.h>
#include <PathUtils.h> #include <PathUtils.h>
@ -115,7 +116,7 @@ void JSConsole::executeCommand(const QString& command) {
QScriptValue JSConsole::executeCommandInWatcher(const QString& command) { QScriptValue JSConsole::executeCommandInWatcher(const QString& command) {
QScriptValue result; QScriptValue result;
QMetaObject::invokeMethod(_scriptEngine, "evaluate", Qt::ConnectionType::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(_scriptEngine, "evaluate",
Q_RETURN_ARG(QScriptValue, result), Q_RETURN_ARG(QScriptValue, result),
Q_ARG(const QString&, command), Q_ARG(const QString&, command),
Q_ARG(const QString&, _consoleFileName)); Q_ARG(const QString&, _consoleFileName));

View file

@ -45,11 +45,13 @@ Image3DOverlay::~Image3DOverlay() {
} }
void Image3DOverlay::update(float deltatime) { void Image3DOverlay::update(float deltatime) {
#if OVERLAY_PANELS
if (usecTimestampNow() > _transformExpiry) { if (usecTimestampNow() > _transformExpiry) {
Transform transform = getTransform(); Transform transform = getTransform();
applyTransformTo(transform); applyTransformTo(transform);
setTransform(transform); setTransform(transform);
} }
#endif
} }
void Image3DOverlay::render(RenderArgs* args) { void Image3DOverlay::render(RenderArgs* args) {

View file

@ -84,9 +84,9 @@ public:
void setColorPulse(float value) { _colorPulse = value; } void setColorPulse(float value) { _colorPulse = value; }
void setAlphaPulse(float value) { _alphaPulse = value; } void setAlphaPulse(float value) { _alphaPulse = value; }
virtual void setProperties(const QVariantMap& properties); Q_INVOKABLE virtual void setProperties(const QVariantMap& properties);
virtual Overlay* createClone() const = 0; Q_INVOKABLE virtual Overlay* createClone() const = 0;
virtual QVariant getProperty(const QString& property); Q_INVOKABLE virtual QVariant getProperty(const QString& property);
render::ItemID getRenderItemID() const { return _renderItemID; } render::ItemID getRenderItemID() const { return _renderItemID; }
void setRenderItemID(render::ItemID renderItemID) { _renderItemID = renderItemID; } void setRenderItemID(render::ItemID renderItemID) { _renderItemID = renderItemID; }

View file

@ -11,6 +11,8 @@
#include "OverlayPanel.h" #include "OverlayPanel.h"
#if OVERLAY_PANELS
#include <QVariant> #include <QVariant>
#include <RegisteredMetaTypes.h> #include <RegisteredMetaTypes.h>
#include <DependencyManager.h> #include <DependencyManager.h>
@ -185,3 +187,4 @@ void OverlayPanel::applyTransformTo(Transform& transform, bool force) {
pointTransformAtCamera(transform, getOffsetRotation()); pointTransformAtCamera(transform, getOffsetRotation());
} }
} }
#endif

View file

@ -22,6 +22,7 @@
#include "Billboardable.h" #include "Billboardable.h"
#include "Overlay.h" #include "Overlay.h"
#if OVERLAY_PANELS
class PropertyBinding { class PropertyBinding {
public: public:
PropertyBinding() {} PropertyBinding() {}
@ -80,4 +81,6 @@ private:
QScriptEngine* _scriptEngine; QScriptEngine* _scriptEngine;
}; };
#endif
#endif // hifi_OverlayPanel_h #endif // hifi_OverlayPanel_h

View file

@ -14,6 +14,7 @@
#include <QtScript/QScriptValueIterator> #include <QtScript/QScriptValueIterator>
#include <shared/QtHelpers.h>
#include <OffscreenUi.h> #include <OffscreenUi.h>
#include <render/Scene.h> #include <render/Scene.h>
#include <RegisteredMetaTypes.h> #include <RegisteredMetaTypes.h>
@ -39,34 +40,39 @@
Q_LOGGING_CATEGORY(trace_render_overlays, "trace.render.overlays") Q_LOGGING_CATEGORY(trace_render_overlays, "trace.render.overlays")
void Overlays::cleanupAllOverlays() { void Overlays::cleanupAllOverlays() {
QMap<OverlayID, Overlay::Pointer> overlaysHUD;
QMap<OverlayID, Overlay::Pointer> overlaysWorld;
{ {
QWriteLocker lock(&_lock); QMutexLocker locker(&_mutex);
QWriteLocker deleteLock(&_deleteLock); overlaysHUD.swap(_overlaysHUD);
foreach(Overlay::Pointer overlay, _overlaysHUD) { overlaysWorld.swap(_overlaysWorld);
_overlaysToDelete.push_back(overlay);
}
foreach(Overlay::Pointer overlay, _overlaysWorld) {
_overlaysToDelete.push_back(overlay);
}
_overlaysHUD.clear();
_overlaysWorld.clear();
_panels.clear();
} }
foreach(Overlay::Pointer overlay, overlaysHUD) {
_overlaysToDelete.push_back(overlay);
}
foreach(Overlay::Pointer overlay, overlaysWorld) {
_overlaysToDelete.push_back(overlay);
}
#if OVERLAY_PANELS
_panels.clear();
#endif
cleanupOverlaysToDelete(); cleanupOverlaysToDelete();
} }
void Overlays::init() { void Overlays::init() {
#if OVERLAY_PANELS
_scriptEngine = new QScriptEngine(); _scriptEngine = new QScriptEngine();
#endif
} }
void Overlays::update(float deltatime) { void Overlays::update(float deltatime) {
{ {
QWriteLocker lock(&_lock); QMutexLocker locker(&_mutex);
foreach(Overlay::Pointer thisOverlay, _overlaysHUD) { foreach(const auto& thisOverlay, _overlaysHUD) {
thisOverlay->update(deltatime); thisOverlay->update(deltatime);
} }
foreach(Overlay::Pointer thisOverlay, _overlaysWorld) { foreach(const auto& thisOverlay, _overlaysWorld) {
thisOverlay->update(deltatime); thisOverlay->update(deltatime);
} }
} }
@ -80,8 +86,6 @@ void Overlays::cleanupOverlaysToDelete() {
render::Transaction transaction; render::Transaction transaction;
{ {
QWriteLocker lock(&_deleteLock);
do { do {
Overlay::Pointer overlay = _overlaysToDelete.takeLast(); Overlay::Pointer overlay = _overlaysToDelete.takeLast();
@ -100,7 +104,6 @@ void Overlays::cleanupOverlaysToDelete() {
void Overlays::renderHUD(RenderArgs* renderArgs) { void Overlays::renderHUD(RenderArgs* renderArgs) {
PROFILE_RANGE(render_overlays, __FUNCTION__); PROFILE_RANGE(render_overlays, __FUNCTION__);
QReadLocker lock(&_lock);
gpu::Batch& batch = *renderArgs->_batch; gpu::Batch& batch = *renderArgs->_batch;
auto geometryCache = DependencyManager::get<GeometryCache>(); auto geometryCache = DependencyManager::get<GeometryCache>();
@ -111,7 +114,7 @@ void Overlays::renderHUD(RenderArgs* renderArgs) {
int height = size.y; int height = size.y;
mat4 legacyProjection = glm::ortho<float>(0, width, height, 0, -1000, 1000); mat4 legacyProjection = glm::ortho<float>(0, width, height, 0, -1000, 1000);
QMutexLocker locker(&_mutex);
foreach(Overlay::Pointer thisOverlay, _overlaysHUD) { foreach(Overlay::Pointer thisOverlay, _overlaysHUD) {
// Reset all batch pipeline settings between overlay // Reset all batch pipeline settings between overlay
@ -126,16 +129,17 @@ void Overlays::renderHUD(RenderArgs* renderArgs) {
} }
void Overlays::disable() { void Overlays::disable() {
QWriteLocker lock(&_lock);
_enabled = false; _enabled = false;
} }
void Overlays::enable() { void Overlays::enable() {
QWriteLocker lock(&_lock);
_enabled = true; _enabled = true;
} }
// Note, can't be invoked by scripts, but can be called by the InterfaceParentFinder
// class on packet processing threads
Overlay::Pointer Overlays::getOverlay(OverlayID id) const { Overlay::Pointer Overlays::getOverlay(OverlayID id) const {
QMutexLocker locker(&_mutex);
if (_overlaysHUD.contains(id)) { if (_overlaysHUD.contains(id)) {
return _overlaysHUD[id]; return _overlaysHUD[id];
} }
@ -146,6 +150,12 @@ Overlay::Pointer Overlays::getOverlay(OverlayID id) const {
} }
OverlayID Overlays::addOverlay(const QString& type, const QVariant& properties) { OverlayID Overlays::addOverlay(const QString& type, const QVariant& properties) {
if (QThread::currentThread() != thread()) {
OverlayID result;
BLOCKING_INVOKE_METHOD(this, "addOverlay", Q_RETURN_ARG(OverlayID, result), Q_ARG(QString, type), Q_ARG(QVariant, properties));
return result;
}
Overlay::Pointer thisOverlay = nullptr; Overlay::Pointer thisOverlay = nullptr;
if (type == ImageOverlay::TYPE) { if (type == ImageOverlay::TYPE) {
@ -185,19 +195,22 @@ OverlayID Overlays::addOverlay(const QString& type, const QVariant& properties)
return UNKNOWN_OVERLAY_ID; return UNKNOWN_OVERLAY_ID;
} }
OverlayID Overlays::addOverlay(Overlay::Pointer overlay) { OverlayID Overlays::addOverlay(const Overlay::Pointer& overlay) {
QWriteLocker lock(&_lock);
OverlayID thisID = OverlayID(QUuid::createUuid()); OverlayID thisID = OverlayID(QUuid::createUuid());
overlay->setOverlayID(thisID); overlay->setOverlayID(thisID);
overlay->setStackOrder(_stackOrder++); overlay->setStackOrder(_stackOrder++);
if (overlay->is3D()) { if (overlay->is3D()) {
_overlaysWorld[thisID] = overlay; {
QMutexLocker locker(&_mutex);
_overlaysWorld[thisID] = overlay;
}
render::ScenePointer scene = qApp->getMain3DScene(); render::ScenePointer scene = qApp->getMain3DScene();
render::Transaction transaction; render::Transaction transaction;
overlay->addToScene(overlay, scene, transaction); overlay->addToScene(overlay, scene, transaction);
scene->enqueueTransaction(transaction); scene->enqueueTransaction(transaction);
} else { } else {
QMutexLocker locker(&_mutex);
_overlaysHUD[thisID] = overlay; _overlaysHUD[thisID] = overlay;
} }
@ -205,14 +218,22 @@ OverlayID Overlays::addOverlay(Overlay::Pointer overlay) {
} }
OverlayID Overlays::cloneOverlay(OverlayID id) { OverlayID Overlays::cloneOverlay(OverlayID id) {
if (QThread::currentThread() != thread()) {
OverlayID result;
BLOCKING_INVOKE_METHOD(this, "cloneOverlay", Q_RETURN_ARG(OverlayID, result), Q_ARG(OverlayID, id));
return result;
}
Overlay::Pointer thisOverlay = getOverlay(id); Overlay::Pointer thisOverlay = getOverlay(id);
if (thisOverlay) { if (thisOverlay) {
OverlayID cloneId = addOverlay(Overlay::Pointer(thisOverlay->createClone())); OverlayID cloneId = addOverlay(Overlay::Pointer(thisOverlay->createClone()));
#if OVERLAY_PANELS
auto attachable = std::dynamic_pointer_cast<PanelAttachable>(thisOverlay); auto attachable = std::dynamic_pointer_cast<PanelAttachable>(thisOverlay);
if (attachable && attachable->getParentPanel()) { if (attachable && attachable->getParentPanel()) {
attachable->getParentPanel()->addChild(cloneId); attachable->getParentPanel()->addChild(cloneId);
} }
#endif
return cloneId; return cloneId;
} }
@ -220,21 +241,32 @@ OverlayID Overlays::cloneOverlay(OverlayID id) {
} }
bool Overlays::editOverlay(OverlayID id, const QVariant& properties) { bool Overlays::editOverlay(OverlayID id, const QVariant& properties) {
QWriteLocker lock(&_lock); if (QThread::currentThread() != thread()) {
// NOTE editOverlay can be called very frequently in scripts and can't afford to
// block waiting on the main thread. Additionally, no script actually
// examines the return value and does something useful with it, so use a non-blocking
// invoke and just always return true
QMetaObject::invokeMethod(this, "editOverlay", Q_ARG(OverlayID, id), Q_ARG(QVariant, properties));
return true;
}
Overlay::Pointer thisOverlay = getOverlay(id); Overlay::Pointer thisOverlay = getOverlay(id);
if (thisOverlay) { if (thisOverlay) {
thisOverlay->setProperties(properties.toMap()); thisOverlay->setProperties(properties.toMap());
return true; return true;
} }
return false; return false;
} }
bool Overlays::editOverlays(const QVariant& propertiesById) { bool Overlays::editOverlays(const QVariant& propertiesById) {
if (QThread::currentThread() != thread()) {
// NOTE see comment on editOverlay for why this is not a blocking call
QMetaObject::invokeMethod(this, "editOverlays", Q_ARG(QVariant, propertiesById));
return true;
}
QVariantMap map = propertiesById.toMap(); QVariantMap map = propertiesById.toMap();
bool success = true; bool success = true;
QWriteLocker lock(&_lock);
for (const auto& key : map.keys()) { for (const auto& key : map.keys()) {
OverlayID id = OverlayID(key); OverlayID id = OverlayID(key);
Overlay::Pointer thisOverlay = getOverlay(id); Overlay::Pointer thisOverlay = getOverlay(id);
@ -249,10 +281,15 @@ bool Overlays::editOverlays(const QVariant& propertiesById) {
} }
void Overlays::deleteOverlay(OverlayID id) { void Overlays::deleteOverlay(OverlayID id) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "deleteOverlay", Q_ARG(OverlayID, id));
return;
}
Overlay::Pointer overlayToDelete; Overlay::Pointer overlayToDelete;
{ {
QWriteLocker lock(&_lock); QMutexLocker locker(&_mutex);
if (_overlaysHUD.contains(id)) { if (_overlaysHUD.contains(id)) {
overlayToDelete = _overlaysHUD.take(id); overlayToDelete = _overlaysHUD.take(id);
} else if (_overlaysWorld.contains(id)) { } else if (_overlaysWorld.contains(id)) {
@ -262,19 +299,25 @@ void Overlays::deleteOverlay(OverlayID id) {
} }
} }
#if OVERLAY_PANELS
auto attachable = std::dynamic_pointer_cast<PanelAttachable>(overlayToDelete); auto attachable = std::dynamic_pointer_cast<PanelAttachable>(overlayToDelete);
if (attachable && attachable->getParentPanel()) { if (attachable && attachable->getParentPanel()) {
attachable->getParentPanel()->removeChild(id); attachable->getParentPanel()->removeChild(id);
attachable->setParentPanel(nullptr); attachable->setParentPanel(nullptr);
} }
#endif
QWriteLocker lock(&_deleteLock);
_overlaysToDelete.push_back(overlayToDelete); _overlaysToDelete.push_back(overlayToDelete);
emit overlayDeleted(id); emit overlayDeleted(id);
} }
QString Overlays::getOverlayType(OverlayID overlayId) const { QString Overlays::getOverlayType(OverlayID overlayId) {
if (QThread::currentThread() != thread()) {
QString result;
BLOCKING_INVOKE_METHOD(this, "getOverlayType", Q_RETURN_ARG(QString, result), Q_ARG(OverlayID, overlayId));
return result;
}
Overlay::Pointer overlay = getOverlay(overlayId); Overlay::Pointer overlay = getOverlay(overlayId);
if (overlay) { if (overlay) {
return overlay->getType(); return overlay->getType();
@ -283,6 +326,12 @@ QString Overlays::getOverlayType(OverlayID overlayId) const {
} }
QObject* Overlays::getOverlayObject(OverlayID id) { QObject* Overlays::getOverlayObject(OverlayID id) {
if (QThread::currentThread() != thread()) {
QObject* result;
BLOCKING_INVOKE_METHOD(this, "getOverlayObject", Q_RETURN_ARG(QObject*, result), Q_ARG(OverlayID, id));
return result;
}
Overlay::Pointer thisOverlay = getOverlay(id); Overlay::Pointer thisOverlay = getOverlay(id);
if (thisOverlay) { if (thisOverlay) {
return qobject_cast<QObject*>(&(*thisOverlay)); return qobject_cast<QObject*>(&(*thisOverlay));
@ -290,6 +339,7 @@ QObject* Overlays::getOverlayObject(OverlayID id) {
return nullptr; return nullptr;
} }
#if OVERLAY_PANELS
OverlayID Overlays::getParentPanel(OverlayID childId) const { OverlayID Overlays::getParentPanel(OverlayID childId) const {
Overlay::Pointer overlay = getOverlay(childId); Overlay::Pointer overlay = getOverlay(childId);
auto attachable = std::dynamic_pointer_cast<PanelAttachable>(overlay); auto attachable = std::dynamic_pointer_cast<PanelAttachable>(overlay);
@ -330,33 +380,31 @@ void Overlays::setParentPanel(OverlayID childId, OverlayID panelId) {
} }
} }
} }
#endif
OverlayID Overlays::getOverlayAtPoint(const glm::vec2& point) { OverlayID Overlays::getOverlayAtPoint(const glm::vec2& point) {
glm::vec2 pointCopy = point; if (QThread::currentThread() != thread()) {
QReadLocker lock(&_lock); OverlayID result;
BLOCKING_INVOKE_METHOD(this, "getOverlayAtPoint", Q_RETURN_ARG(OverlayID, result), Q_ARG(glm::vec2, point));
return result;
}
if (!_enabled) { if (!_enabled) {
return UNKNOWN_OVERLAY_ID; return UNKNOWN_OVERLAY_ID;
} }
QMapIterator<OverlayID, Overlay::Pointer> i(_overlaysHUD);
const float LARGE_NEGATIVE_FLOAT = -9999999; QMutexLocker locker(&_mutex);
glm::vec3 origin(pointCopy.x, pointCopy.y, LARGE_NEGATIVE_FLOAT); QMapIterator<OverlayID, Overlay::Pointer> i(_overlaysHUD);
glm::vec3 direction(0, 0, 1);
glm::vec3 thisSurfaceNormal;
unsigned int bestStackOrder = 0; unsigned int bestStackOrder = 0;
OverlayID bestOverlayID = UNKNOWN_OVERLAY_ID; OverlayID bestOverlayID = UNKNOWN_OVERLAY_ID;
while (i.hasNext()) { while (i.hasNext()) {
i.next(); i.next();
OverlayID thisID = i.key(); auto thisOverlay = std::dynamic_pointer_cast<Overlay2D>(i.value());
if (!i.value()->is3D()) { if (thisOverlay && thisOverlay->getVisible() && thisOverlay->isLoaded() &&
auto thisOverlay = std::dynamic_pointer_cast<Overlay2D>(i.value()); thisOverlay->getBoundingRect().contains(point.x, point.y, false)) {
if (thisOverlay && thisOverlay->getVisible() && thisOverlay->isLoaded() && if (thisOverlay->getStackOrder() > bestStackOrder) {
thisOverlay->getBoundingRect().contains(pointCopy.x, pointCopy.y, false)) { bestOverlayID = i.key();
if (thisOverlay->getStackOrder() > bestStackOrder) { bestStackOrder = thisOverlay->getStackOrder();
bestOverlayID = thisID;
bestStackOrder = thisOverlay->getStackOrder();
}
} }
} }
} }
@ -365,9 +413,14 @@ OverlayID Overlays::getOverlayAtPoint(const glm::vec2& point) {
} }
OverlayPropertyResult Overlays::getProperty(OverlayID id, const QString& property) { OverlayPropertyResult Overlays::getProperty(OverlayID id, const QString& property) {
if (QThread::currentThread() != thread()) {
OverlayPropertyResult result;
BLOCKING_INVOKE_METHOD(this, "getProperty", Q_RETURN_ARG(OverlayPropertyResult, result), Q_ARG(OverlayID, id), Q_ARG(QString, property));
return result;
}
OverlayPropertyResult result; OverlayPropertyResult result;
Overlay::Pointer thisOverlay = getOverlay(id); Overlay::Pointer thisOverlay = getOverlay(id);
QReadLocker lock(&_lock);
if (thisOverlay && thisOverlay->supportsGetProperty()) { if (thisOverlay && thisOverlay->supportsGetProperty()) {
result.value = thisOverlay->getProperty(property); result.value = thisOverlay->getProperty(property);
} }
@ -405,10 +458,22 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionInternal(const PickR
const QVector<OverlayID>& overlaysToInclude, const QVector<OverlayID>& overlaysToInclude,
const QVector<OverlayID>& overlaysToDiscard, const QVector<OverlayID>& overlaysToDiscard,
bool visibleOnly, bool collidableOnly) { bool visibleOnly, bool collidableOnly) {
QReadLocker lock(&_lock); if (QThread::currentThread() != thread()) {
RayToOverlayIntersectionResult result;
BLOCKING_INVOKE_METHOD(this, "findRayIntersectionInternal", Q_RETURN_ARG(RayToOverlayIntersectionResult, result),
Q_ARG(PickRay, ray),
Q_ARG(bool, precisionPicking),
Q_ARG(QVector<OverlayID>, overlaysToInclude),
Q_ARG(QVector<OverlayID>, overlaysToDiscard),
Q_ARG(bool, visibleOnly),
Q_ARG(bool, collidableOnly));
return result;
}
float bestDistance = std::numeric_limits<float>::max(); float bestDistance = std::numeric_limits<float>::max();
bool bestIsFront = false; bool bestIsFront = false;
QMutexLocker locker(&_mutex);
RayToOverlayIntersectionResult result; RayToOverlayIntersectionResult result;
QMapIterator<OverlayID, Overlay::Pointer> i(_overlaysWorld); QMapIterator<OverlayID, Overlay::Pointer> i(_overlaysWorld);
while (i.hasNext()) { while (i.hasNext()) {
@ -448,16 +513,6 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionInternal(const PickR
return result; return result;
} }
RayToOverlayIntersectionResult::RayToOverlayIntersectionResult() :
intersects(false),
overlayID(UNKNOWN_OVERLAY_ID),
distance(0),
face(),
intersection(),
extraInfo()
{
}
QScriptValue RayToOverlayIntersectionResultToScriptValue(QScriptEngine* engine, const RayToOverlayIntersectionResult& value) { QScriptValue RayToOverlayIntersectionResultToScriptValue(QScriptEngine* engine, const RayToOverlayIntersectionResult& value) {
auto obj = engine->newObject(); auto obj = engine->newObject();
obj.setProperty("intersects", value.intersects); obj.setProperty("intersects", value.intersects);
@ -531,7 +586,12 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& objectVar
} }
bool Overlays::isLoaded(OverlayID id) { bool Overlays::isLoaded(OverlayID id) {
QReadLocker lock(&_lock); if (QThread::currentThread() != thread()) {
bool result;
BLOCKING_INVOKE_METHOD(this, "isLoaded", Q_RETURN_ARG(bool, result), Q_ARG(OverlayID, id));
return result;
}
Overlay::Pointer thisOverlay = getOverlay(id); Overlay::Pointer thisOverlay = getOverlay(id);
if (!thisOverlay) { if (!thisOverlay) {
return false; // not found return false; // not found
@ -539,14 +599,27 @@ bool Overlays::isLoaded(OverlayID id) {
return thisOverlay->isLoaded(); return thisOverlay->isLoaded();
} }
QSizeF Overlays::textSize(OverlayID id, const QString& text) const { QSizeF Overlays::textSize(OverlayID id, const QString& text) {
Overlay::Pointer thisOverlay = _overlaysHUD[id]; if (QThread::currentThread() != thread()) {
QSizeF result;
BLOCKING_INVOKE_METHOD(this, "textSize", Q_RETURN_ARG(QSizeF, result), Q_ARG(OverlayID, id), Q_ARG(QString, text));
return result;
}
Overlay::Pointer thisOverlay;
{
QMutexLocker locker(&_mutex);
thisOverlay = _overlaysHUD[id];
}
if (thisOverlay) { if (thisOverlay) {
if (auto textOverlay = std::dynamic_pointer_cast<TextOverlay>(thisOverlay)) { if (auto textOverlay = std::dynamic_pointer_cast<TextOverlay>(thisOverlay)) {
return textOverlay->textSize(text); return textOverlay->textSize(text);
} }
} else { } else {
thisOverlay = _overlaysWorld[id]; {
QMutexLocker locker(&_mutex);
thisOverlay = _overlaysWorld[id];
}
if (auto text3dOverlay = std::dynamic_pointer_cast<Text3DOverlay>(thisOverlay)) { if (auto text3dOverlay = std::dynamic_pointer_cast<Text3DOverlay>(thisOverlay)) {
return text3dOverlay->textSize(text); return text3dOverlay->textSize(text);
} }
@ -554,6 +627,7 @@ QSizeF Overlays::textSize(OverlayID id, const QString& text) const {
return QSizeF(0.0f, 0.0f); return QSizeF(0.0f, 0.0f);
} }
#if OVERLAY_PANELS
OverlayID Overlays::addPanel(OverlayPanel::Pointer panel) { OverlayID Overlays::addPanel(OverlayPanel::Pointer panel) {
QWriteLocker lock(&_lock); QWriteLocker lock(&_lock);
@ -607,8 +681,16 @@ void Overlays::deletePanel(OverlayID panelId) {
emit panelDeleted(panelId); emit panelDeleted(panelId);
} }
#endif
bool Overlays::isAddedOverlay(OverlayID id) { bool Overlays::isAddedOverlay(OverlayID id) {
if (QThread::currentThread() != thread()) {
bool result;
BLOCKING_INVOKE_METHOD(this, "isAddedOverlay", Q_RETURN_ARG(bool, result), Q_ARG(OverlayID, id));
return result;
}
QMutexLocker locker(&_mutex);
return _overlaysHUD.contains(id) || _overlaysWorld.contains(id); return _overlaysHUD.contains(id) || _overlaysWorld.contains(id);
} }
@ -636,20 +718,43 @@ void Overlays::sendHoverLeaveOverlay(OverlayID id, PointerEvent event) {
emit hoverLeaveOverlay(id, event); emit hoverLeaveOverlay(id, event);
} }
OverlayID Overlays::getKeyboardFocusOverlay() const { OverlayID Overlays::getKeyboardFocusOverlay() {
if (QThread::currentThread() != thread()) {
OverlayID result;
BLOCKING_INVOKE_METHOD(this, "getKeyboardFocusOverlay", Q_RETURN_ARG(OverlayID, result));
return result;
}
return qApp->getKeyboardFocusOverlay(); return qApp->getKeyboardFocusOverlay();
} }
void Overlays::setKeyboardFocusOverlay(OverlayID id) { void Overlays::setKeyboardFocusOverlay(OverlayID id) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setKeyboardFocusOverlay", Q_ARG(OverlayID, id));
return;
}
qApp->setKeyboardFocusOverlay(id); qApp->setKeyboardFocusOverlay(id);
} }
float Overlays::width() const { float Overlays::width() {
if (QThread::currentThread() != thread()) {
float result;
BLOCKING_INVOKE_METHOD(this, "width", Q_RETURN_ARG(float, result));
return result;
}
auto offscreenUi = DependencyManager::get<OffscreenUi>(); auto offscreenUi = DependencyManager::get<OffscreenUi>();
return offscreenUi->getWindow()->size().width(); return offscreenUi->getWindow()->size().width();
} }
float Overlays::height() const { float Overlays::height() {
if (QThread::currentThread() != thread()) {
float result;
BLOCKING_INVOKE_METHOD(this, "height", Q_RETURN_ARG(float, result));
return result;
}
auto offscreenUi = DependencyManager::get<OffscreenUi>(); auto offscreenUi = DependencyManager::get<OffscreenUi>();
return offscreenUi->getWindow()->size().height(); return offscreenUi->getWindow()->size().height();
} }
@ -705,7 +810,6 @@ PointerEvent Overlays::calculatePointerEvent(Overlay::Pointer overlay, PickRay r
auto thisOverlay = std::dynamic_pointer_cast<Web3DOverlay>(overlay); auto thisOverlay = std::dynamic_pointer_cast<Web3DOverlay>(overlay);
QReadLocker lock(&_lock);
auto position = thisOverlay->getPosition(); auto position = thisOverlay->getPosition();
auto rotation = thisOverlay->getRotation(); auto rotation = thisOverlay->getRotation();
auto dimensions = thisOverlay->getSize(); auto dimensions = thisOverlay->getSize();
@ -854,9 +958,14 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) {
return false; return false;
} }
QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) const { QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) {
QVector<QUuid> result; QVector<QUuid> result;
if (QThread::currentThread() != thread()) {
BLOCKING_INVOKE_METHOD(this, "findOverlays", Q_RETURN_ARG(QVector<QUuid>, result), Q_ARG(glm::vec3, center), Q_ARG(float, radius));
return result;
}
QMutexLocker locker(&_mutex);
QMapIterator<OverlayID, Overlay::Pointer> i(_overlaysWorld); QMapIterator<OverlayID, Overlay::Pointer> i(_overlaysWorld);
int checked = 0; int checked = 0;
while (i.hasNext()) { while (i.hasNext()) {

View file

@ -25,8 +25,9 @@
#include <PointerEvent.h> #include <PointerEvent.h>
#include "Overlay.h" #include "Overlay.h"
#include "OverlayPanel.h"
#include "PanelAttachable.h" #include "PanelAttachable.h"
#include "OverlayPanel.h"
class PickRay; class PickRay;
@ -41,6 +42,8 @@ Q_DECLARE_METATYPE(OverlayPropertyResult);
QScriptValue OverlayPropertyResultToScriptValue(QScriptEngine* engine, const OverlayPropertyResult& value); QScriptValue OverlayPropertyResultToScriptValue(QScriptEngine* engine, const OverlayPropertyResult& value);
void OverlayPropertyResultFromScriptValue(const QScriptValue& object, OverlayPropertyResult& value); void OverlayPropertyResultFromScriptValue(const QScriptValue& object, OverlayPropertyResult& value);
const OverlayID UNKNOWN_OVERLAY_ID = OverlayID();
/**jsdoc /**jsdoc
* @typedef Overlays.RayToOverlayIntersectionResult * @typedef Overlays.RayToOverlayIntersectionResult
* @property {bool} intersects True if the PickRay intersected with a 3D overlay. * @property {bool} intersects True if the PickRay intersected with a 3D overlay.
@ -51,10 +54,9 @@ void OverlayPropertyResultFromScriptValue(const QScriptValue& object, OverlayPro
*/ */
class RayToOverlayIntersectionResult { class RayToOverlayIntersectionResult {
public: public:
RayToOverlayIntersectionResult(); bool intersects { false };
bool intersects; OverlayID overlayID { UNKNOWN_OVERLAY_ID };
OverlayID overlayID; float distance { 0 };
float distance;
BoxFace face; BoxFace face;
glm::vec3 surfaceNormal; glm::vec3 surfaceNormal;
glm::vec3 intersection; glm::vec3 intersection;
@ -77,8 +79,6 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, R
* @namespace Overlays * @namespace Overlays
*/ */
const OverlayID UNKNOWN_OVERLAY_ID = OverlayID();
class Overlays : public QObject { class Overlays : public QObject {
Q_OBJECT Q_OBJECT
@ -94,11 +94,13 @@ public:
void enable(); void enable();
Overlay::Pointer getOverlay(OverlayID id) const; Overlay::Pointer getOverlay(OverlayID id) const;
#if OVERLAY_PANELS
OverlayPanel::Pointer getPanel(OverlayID id) const { return _panels[id]; } OverlayPanel::Pointer getPanel(OverlayID id) const { return _panels[id]; }
#endif
/// adds an overlay that's already been created /// adds an overlay that's already been created
OverlayID addOverlay(Overlay* overlay) { return addOverlay(Overlay::Pointer(overlay)); } OverlayID addOverlay(Overlay* overlay) { return addOverlay(Overlay::Pointer(overlay)); }
OverlayID addOverlay(Overlay::Pointer overlay); OverlayID addOverlay(const Overlay::Pointer& overlay);
bool mousePressEvent(QMouseEvent* event); bool mousePressEvent(QMouseEvent* event);
bool mouseDoublePressEvent(QMouseEvent* event); bool mouseDoublePressEvent(QMouseEvent* event);
@ -156,7 +158,7 @@ public slots:
* @param {Overlays.OverlayID} overlayID The ID of the overlay to get the type of. * @param {Overlays.OverlayID} overlayID The ID of the overlay to get the type of.
* @return {string} The type of the overlay if found, otherwise the empty string. * @return {string} The type of the overlay if found, otherwise the empty string.
*/ */
QString getOverlayType(OverlayID overlayId) const; QString getOverlayType(OverlayID overlayId);
/**jsdoc /**jsdoc
* Get the overlay Script object. * Get the overlay Script object.
@ -215,7 +217,7 @@ public slots:
* @param {float} radius search radius * @param {float} radius search radius
* @return {Overlays.OverlayID[]} list of overlays withing the radius * @return {Overlays.OverlayID[]} list of overlays withing the radius
*/ */
QVector<QUuid> findOverlays(const glm::vec3& center, float radius) const; QVector<QUuid> findOverlays(const glm::vec3& center, float radius);
/**jsdoc /**jsdoc
* Check whether an overlay's assets have been loaded. For example, if the * Check whether an overlay's assets have been loaded. For example, if the
@ -237,7 +239,7 @@ public slots:
* @param {string} The string to measure. * @param {string} The string to measure.
* @return {Vec2} The size of the text. * @return {Vec2} The size of the text.
*/ */
QSizeF textSize(OverlayID id, const QString& text) const; QSizeF textSize(OverlayID id, const QString& text);
/**jsdoc /**jsdoc
* Get the width of the virtual 2D HUD. * Get the width of the virtual 2D HUD.
@ -245,7 +247,7 @@ public slots:
* @function Overlays.width * @function Overlays.width
* @return {float} The width of the 2D HUD. * @return {float} The width of the 2D HUD.
*/ */
float width() const; float width();
/**jsdoc /**jsdoc
* Get the height of the virtual 2D HUD. * Get the height of the virtual 2D HUD.
@ -253,11 +255,12 @@ public slots:
* @function Overlays.height * @function Overlays.height
* @return {float} The height of the 2D HUD. * @return {float} The height of the 2D HUD.
*/ */
float height() const; float height();
/// return true if there is an overlay with that id else false /// return true if there is an overlay with that id else false
bool isAddedOverlay(OverlayID id); bool isAddedOverlay(OverlayID id);
#if OVERLAY_PANELS
OverlayID getParentPanel(OverlayID childId) const; OverlayID getParentPanel(OverlayID childId) const;
void setParentPanel(OverlayID childId, OverlayID panelId); void setParentPanel(OverlayID childId, OverlayID panelId);
@ -279,6 +282,8 @@ public slots:
/// return true if there is a panel with that id else false /// return true if there is a panel with that id else false
bool isAddedPanel(OverlayID id) { return _panels.contains(id); } bool isAddedPanel(OverlayID id) { return _panels.contains(id); }
#endif
void sendMousePressOnOverlay(OverlayID overlayID, const PointerEvent& event); void sendMousePressOnOverlay(OverlayID overlayID, const PointerEvent& event);
void sendMouseReleaseOnOverlay(OverlayID overlayID, const PointerEvent& event); void sendMouseReleaseOnOverlay(OverlayID overlayID, const PointerEvent& event);
void sendMouseMoveOnOverlay(OverlayID overlayID, const PointerEvent& event); void sendMouseMoveOnOverlay(OverlayID overlayID, const PointerEvent& event);
@ -287,7 +292,7 @@ public slots:
void sendHoverOverOverlay(OverlayID id, PointerEvent event); void sendHoverOverOverlay(OverlayID id, PointerEvent event);
void sendHoverLeaveOverlay(OverlayID id, PointerEvent event); void sendHoverLeaveOverlay(OverlayID id, PointerEvent event);
OverlayID getKeyboardFocusOverlay() const; OverlayID getKeyboardFocusOverlay();
void setKeyboardFocusOverlay(OverlayID id); void setKeyboardFocusOverlay(OverlayID id);
signals: signals:
@ -314,15 +319,18 @@ signals:
private: private:
void cleanupOverlaysToDelete(); void cleanupOverlaysToDelete();
mutable QMutex _mutex;
QMap<OverlayID, Overlay::Pointer> _overlaysHUD; QMap<OverlayID, Overlay::Pointer> _overlaysHUD;
QMap<OverlayID, Overlay::Pointer> _overlaysWorld; QMap<OverlayID, Overlay::Pointer> _overlaysWorld;
#if OVERLAY_PANELS
QMap<OverlayID, OverlayPanel::Pointer> _panels; QMap<OverlayID, OverlayPanel::Pointer> _panels;
#endif
QList<Overlay::Pointer> _overlaysToDelete; QList<Overlay::Pointer> _overlaysToDelete;
unsigned int _stackOrder { 1 }; unsigned int _stackOrder { 1 };
QReadWriteLock _lock; #if OVERLAY_PANELS
QReadWriteLock _deleteLock;
QScriptEngine* _scriptEngine; QScriptEngine* _scriptEngine;
#endif
bool _enabled = true; bool _enabled = true;
PointerEvent calculatePointerEvent(Overlay::Pointer overlay, PickRay ray, RayToOverlayIntersectionResult rayPickResult, PointerEvent calculatePointerEvent(Overlay::Pointer overlay, PickRay ray, RayToOverlayIntersectionResult rayPickResult,
@ -331,7 +339,7 @@ private:
OverlayID _currentClickingOnOverlayID { UNKNOWN_OVERLAY_ID }; OverlayID _currentClickingOnOverlayID { UNKNOWN_OVERLAY_ID };
OverlayID _currentHoverOverOverlayID { UNKNOWN_OVERLAY_ID }; OverlayID _currentHoverOverOverlayID { UNKNOWN_OVERLAY_ID };
RayToOverlayIntersectionResult findRayIntersectionInternal(const PickRay& ray, bool precisionPicking, Q_INVOKABLE RayToOverlayIntersectionResult findRayIntersectionInternal(const PickRay& ray, bool precisionPicking,
const QVector<OverlayID>& overlaysToInclude, const QVector<OverlayID>& overlaysToInclude,
const QVector<OverlayID>& overlaysToDiscard, const QVector<OverlayID>& overlaysToDiscard,
bool visibleOnly = false, bool collidableOnly = false); bool visibleOnly = false, bool collidableOnly = false);

View file

@ -16,11 +16,15 @@
#include "OverlayPanel.h" #include "OverlayPanel.h"
bool PanelAttachable::getParentVisible() const { bool PanelAttachable::getParentVisible() const {
#if OVERLAY_PANELS
if (getParentPanel()) { if (getParentPanel()) {
return getParentPanel()->getVisible() && getParentPanel()->getParentVisible(); return getParentPanel()->getVisible() && getParentPanel()->getParentVisible();
} else { } else {
return true; return true;
} }
#else
return true;
#endif
} }
QVariant PanelAttachable::getProperty(const QString& property) { QVariant PanelAttachable::getProperty(const QString& property) {
@ -61,11 +65,13 @@ void PanelAttachable::applyTransformTo(Transform& transform, bool force) {
if (force || usecTimestampNow() > _transformExpiry) { if (force || usecTimestampNow() > _transformExpiry) {
const quint64 TRANSFORM_UPDATE_PERIOD = 100000; // frequency is 10 Hz const quint64 TRANSFORM_UPDATE_PERIOD = 100000; // frequency is 10 Hz
_transformExpiry = usecTimestampNow() + TRANSFORM_UPDATE_PERIOD; _transformExpiry = usecTimestampNow() + TRANSFORM_UPDATE_PERIOD;
#if OVERLAY_PANELS
if (getParentPanel()) { if (getParentPanel()) {
getParentPanel()->applyTransformTo(transform, true); getParentPanel()->applyTransformTo(transform, true);
transform.postTranslate(getOffsetPosition()); transform.postTranslate(getOffsetPosition());
transform.postRotate(getOffsetRotation()); transform.postRotate(getOffsetRotation());
transform.postScale(getOffsetScale()); transform.postScale(getOffsetScale());
} }
#endif
} }
} }

View file

@ -30,6 +30,8 @@
#ifndef hifi_PanelAttachable_h #ifndef hifi_PanelAttachable_h
#define hifi_PanelAttachable_h #define hifi_PanelAttachable_h
#define OVERLAY_PANELS 0
#include <memory> #include <memory>
#include <glm/glm.hpp> #include <glm/glm.hpp>
@ -39,18 +41,21 @@
#include <QUuid> #include <QUuid>
class OverlayPanel; class OverlayPanel;
class PanelAttachable { class PanelAttachable {
public: public:
// getters // getters
#if OVERLAY_PANELS
std::shared_ptr<OverlayPanel> getParentPanel() const { return _parentPanel; } std::shared_ptr<OverlayPanel> getParentPanel() const { return _parentPanel; }
#endif
glm::vec3 getOffsetPosition() const { return _offset.getTranslation(); } glm::vec3 getOffsetPosition() const { return _offset.getTranslation(); }
glm::quat getOffsetRotation() const { return _offset.getRotation(); } glm::quat getOffsetRotation() const { return _offset.getRotation(); }
glm::vec3 getOffsetScale() const { return _offset.getScale(); } glm::vec3 getOffsetScale() const { return _offset.getScale(); }
bool getParentVisible() const; bool getParentVisible() const;
// setters // setters
#if OVERLAY_PANELS
void setParentPanel(std::shared_ptr<OverlayPanel> panel) { _parentPanel = panel; } void setParentPanel(std::shared_ptr<OverlayPanel> panel) { _parentPanel = panel; }
#endif
void setOffsetPosition(const glm::vec3& position) { _offset.setTranslation(position); } void setOffsetPosition(const glm::vec3& position) { _offset.setTranslation(position); }
void setOffsetRotation(const glm::quat& rotation) { _offset.setRotation(rotation); } void setOffsetRotation(const glm::quat& rotation) { _offset.setRotation(rotation); }
void setOffsetScale(float scale) { _offset.setScale(scale); } void setOffsetScale(float scale) { _offset.setScale(scale); }
@ -66,7 +71,9 @@ protected:
quint64 _transformExpiry = 0; quint64 _transformExpiry = 0;
private: private:
#if OVERLAY_PANELS
std::shared_ptr<OverlayPanel> _parentPanel = nullptr; std::shared_ptr<OverlayPanel> _parentPanel = nullptr;
#endif
Transform _offset; Transform _offset;
}; };

View file

@ -32,21 +32,20 @@ QmlOverlay::QmlOverlay(const QUrl& url, const QmlOverlay* textOverlay)
} }
void QmlOverlay::buildQmlElement(const QUrl& url) { void QmlOverlay::buildQmlElement(const QUrl& url) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "buildQmlElement", Q_ARG(QUrl, url));
return;
}
auto offscreenUi = DependencyManager::get<OffscreenUi>(); auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->returnFromUiThread([=] { offscreenUi->load(url, [=](QQmlContext* context, QObject* object) {
offscreenUi->load(url, [=](QQmlContext* context, QObject* object) { QQuickItem* rawPtr = dynamic_cast<QQuickItem*>(object);
QQuickItem* rawPtr = dynamic_cast<QQuickItem*>(object); // Create a shared ptr with a custom deleter lambda, that calls deleteLater
// Create a shared ptr with a custom deleter lambda, that calls deleteLater _qmlElement = std::shared_ptr<QQuickItem>(rawPtr, [](QQuickItem* ptr) {
_qmlElement = std::shared_ptr<QQuickItem>(rawPtr, [](QQuickItem* ptr) { if (ptr) {
if (ptr) { ptr->deleteLater();
ptr->deleteLater(); }
}
});
}); });
while (!_qmlElement) {
qApp->processEvents();
}
return QVariant();
}); });
} }
@ -55,20 +54,23 @@ QmlOverlay::~QmlOverlay() {
} }
void QmlOverlay::setProperties(const QVariantMap& properties) { void QmlOverlay::setProperties(const QVariantMap& properties) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setProperties", Q_ARG(QVariantMap, properties));
return;
}
Overlay2D::setProperties(properties); Overlay2D::setProperties(properties);
auto bounds = _bounds; auto bounds = _bounds;
std::weak_ptr<QQuickItem> weakQmlElement = _qmlElement; std::weak_ptr<QQuickItem> weakQmlElement = _qmlElement;
DependencyManager::get<OffscreenUi>()->executeOnUiThread([weakQmlElement, bounds, properties] { // check to see if qmlElement still exists
// check to see if qmlElement still exists auto qmlElement = weakQmlElement.lock();
auto qmlElement = weakQmlElement.lock(); if (qmlElement) {
if (qmlElement) { qmlElement->setX(bounds.left());
qmlElement->setX(bounds.left()); qmlElement->setY(bounds.top());
qmlElement->setY(bounds.top()); qmlElement->setWidth(bounds.width());
qmlElement->setWidth(bounds.width()); qmlElement->setHeight(bounds.height());
qmlElement->setHeight(bounds.height()); QMetaObject::invokeMethod(qmlElement.get(), "updatePropertiesFromScript", Qt::DirectConnection, Q_ARG(QVariant, properties));
QMetaObject::invokeMethod(qmlElement.get(), "updatePropertiesFromScript", Qt::DirectConnection, Q_ARG(QVariant, properties)); }
}
});
} }
void QmlOverlay::render(RenderArgs* args) { void QmlOverlay::render(RenderArgs* args) {

View file

@ -32,7 +32,7 @@ public:
void render(RenderArgs* args) override; void render(RenderArgs* args) override;
private: private:
void buildQmlElement(const QUrl& url); Q_INVOKABLE void buildQmlElement(const QUrl& url);
protected: protected:
std::shared_ptr<QQuickItem> _qmlElement; std::shared_ptr<QQuickItem> _qmlElement;

View file

@ -9,15 +9,18 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include "AnimationCache.h"
#include <QRunnable> #include <QRunnable>
#include <QThreadPool> #include <QThreadPool>
#include "AnimationCache.h" #include <shared/QtHelpers.h>
#include "AnimationLogging.h"
#include <Trace.h> #include <Trace.h>
#include <StatTracker.h> #include <StatTracker.h>
#include <Profile.h> #include <Profile.h>
#include "AnimationLogging.h"
int animationPointerMetaTypeId = qRegisterMetaType<AnimationPointer>(); int animationPointerMetaTypeId = qRegisterMetaType<AnimationPointer>();
AnimationCache::AnimationCache(QObject* parent) : AnimationCache::AnimationCache(QObject* parent) :
@ -31,7 +34,7 @@ AnimationCache::AnimationCache(QObject* parent) :
AnimationPointer AnimationCache::getAnimation(const QUrl& url) { AnimationPointer AnimationCache::getAnimation(const QUrl& url) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
AnimationPointer result; AnimationPointer result;
QMetaObject::invokeMethod(this, "getAnimation", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "getAnimation",
Q_RETURN_ARG(AnimationPointer, result), Q_ARG(const QUrl&, url)); Q_RETURN_ARG(AnimationPointer, result), Q_ARG(const QUrl&, url));
return result; return result;
} }
@ -97,7 +100,7 @@ bool Animation::isLoaded() const {
QStringList Animation::getJointNames() const { QStringList Animation::getJointNames() const {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QStringList result; QStringList result;
QMetaObject::invokeMethod(const_cast<Animation*>(this), "getJointNames", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(const_cast<Animation*>(this), "getJointNames",
Q_RETURN_ARG(QStringList, result)); Q_RETURN_ARG(QStringList, result));
return result; return result;
} }
@ -111,7 +114,7 @@ QStringList Animation::getJointNames() const {
QVector<FBXAnimationFrame> Animation::getFrames() const { QVector<FBXAnimationFrame> Animation::getFrames() const {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QVector<FBXAnimationFrame> result; QVector<FBXAnimationFrame> result;
QMetaObject::invokeMethod(const_cast<Animation*>(this), "getFrames", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(const_cast<Animation*>(this), "getFrames",
Q_RETURN_ARG(QVector<FBXAnimationFrame>, result)); Q_RETURN_ARG(QVector<FBXAnimationFrame>, result));
return result; return result;
} }

View file

@ -259,10 +259,21 @@ void AudioClient::customDeleter() {
void AudioClient::cleanupBeforeQuit() { void AudioClient::cleanupBeforeQuit() {
// FIXME: this should be put in customDeleter, but there is still a reference to this when it is called, // FIXME: this should be put in customDeleter, but there is still a reference to this when it is called,
// so this must be explicitly, synchronously stopped // so this must be explicitly, synchronously stopped
static ConditionalGuard guard;
if (QThread::currentThread() != thread()) {
// This will likely be called from the main thread, but we don't want to do blocking queued calls
// from the main thread, so we use a normal auto-connection invoke, and then use a conditional to wait
// for completion
// The effect is the same, yes, but we actually want to avoid the use of Qt::BlockingQueuedConnection
// in the code
QMetaObject::invokeMethod(this, "cleanupBeforeQuit");
guard.wait();
return;
}
stop(); stop();
_checkDevicesTimer->stop(); _checkDevicesTimer->stop();
guard.trigger();
} }
void AudioClient::handleMismatchAudioFormat(SharedNodePointer node, const QString& currentCodec, const QString& recievedCodec) { void AudioClient::handleMismatchAudioFormat(SharedNodePointer node, const QString& currentCodec, const QString& recievedCodec) {
@ -1335,6 +1346,14 @@ void AudioClient::toggleMute() {
emit muteToggled(); emit muteToggled();
} }
void AudioClient::setNoiseReduction(bool enable) {
if (_isNoiseGateEnabled != enable) {
_isNoiseGateEnabled = enable;
emit noiseReductionChanged();
}
}
void AudioClient::setIsStereoInput(bool isStereoInput) { void AudioClient::setIsStereoInput(bool isStereoInput) {
if (isStereoInput != _isStereoInput) { if (isStereoInput != _isStereoInput) {
_isStereoInput = isStereoInput; _isStereoInput = isStereoInput;
@ -1446,6 +1465,8 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn
_audioInput = new QAudioInput(inputDeviceInfo, _inputFormat, this); _audioInput = new QAudioInput(inputDeviceInfo, _inputFormat, this);
_numInputCallbackBytes = calculateNumberOfInputCallbackBytes(_inputFormat); _numInputCallbackBytes = calculateNumberOfInputCallbackBytes(_inputFormat);
_audioInput->setBufferSize(_numInputCallbackBytes); _audioInput->setBufferSize(_numInputCallbackBytes);
// different audio input devices may have different volumes
emit inputVolumeChanged(_audioInput->volume());
// how do we want to handle input working, but output not working? // how do we want to handle input working, but output not working?
int numFrameSamples = calculateNumberOfFrameSamples(_numInputCallbackBytes); int numFrameSamples = calculateNumberOfFrameSamples(_numInputCallbackBytes);
@ -1703,7 +1724,7 @@ float AudioClient::azimuthForSource(const glm::vec3& relativePosition) {
// produce an oriented angle about the y-axis // produce an oriented angle about the y-axis
glm::vec3 direction = rotatedSourcePosition * (1.0f / fastSqrtf(rotatedSourcePositionLength2)); glm::vec3 direction = rotatedSourcePosition * (1.0f / fastSqrtf(rotatedSourcePositionLength2));
float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); // UNIT_NEG_Z is "forward" float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); // UNIT_NEG_Z is "forward"
return (direction.x < 0.0f) ? -angle : angle; return (direction.x < 0.0f) ? -angle : angle;
} else { } else {
@ -1847,3 +1868,10 @@ void AudioClient::setAvatarBoundingBoxParameters(glm::vec3 corner, glm::vec3 sca
void AudioClient::startThread() { void AudioClient::startThread() {
moveToNewNamedThread(this, "Audio Thread", [this] { start(); }); moveToNewNamedThread(this, "Audio Thread", [this] { start(); });
} }
void AudioClient::setInputVolume(float volume) {
if (_audioInput && volume != (float)_audioInput->volume()) {
_audioInput->setVolume(volume);
emit inputVolumeChanged(_audioInput->volume());
}
}

View file

@ -180,7 +180,8 @@ public slots:
virtual void setIsStereoInput(bool stereo) override; virtual void setIsStereoInput(bool stereo) override;
void setNoiseReduction(bool isNoiseGateEnabled) { _isNoiseGateEnabled = isNoiseGateEnabled; } void setNoiseReduction(bool isNoiseGateEnabled);
bool isNoiseReductionEnabled() const { return _isNoiseGateEnabled; }
void toggleLocalEcho() { _shouldEchoLocally = !_shouldEchoLocally; } void toggleLocalEcho() { _shouldEchoLocally = !_shouldEchoLocally; }
void toggleServerEcho() { _shouldEchoToServer = !_shouldEchoToServer; } void toggleServerEcho() { _shouldEchoToServer = !_shouldEchoToServer; }
@ -197,7 +198,7 @@ public slots:
bool switchAudioDevice(QAudio::Mode mode, const QString& deviceName); bool switchAudioDevice(QAudio::Mode mode, const QString& deviceName);
float getInputVolume() const { return (_audioInput) ? (float)_audioInput->volume() : 0.0f; } float getInputVolume() const { return (_audioInput) ? (float)_audioInput->volume() : 0.0f; }
void setInputVolume(float volume) { if (_audioInput) _audioInput->setVolume(volume); } void setInputVolume(float volume);
void setReverb(bool reverb); void setReverb(bool reverb);
void setReverbOptions(const AudioEffectOptions* options); void setReverbOptions(const AudioEffectOptions* options);
@ -207,7 +208,9 @@ public slots:
void saveSettings(); void saveSettings();
signals: signals:
bool muteToggled(); void inputVolumeChanged(float volume);
void muteToggled();
void noiseReductionChanged();
void mutedByMixer(); void mutedByMixer();
void inputReceived(const QByteArray& inputSamples); void inputReceived(const QByteArray& inputSamples);
void inputLoudnessChanged(float loudness); void inputLoudnessChanged(float loudness);

View file

@ -9,10 +9,13 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include <qthread.h> #include "SoundCache.h"
#include <QtCore/QThread>
#include <shared/QtHelpers.h>
#include "AudioLogging.h" #include "AudioLogging.h"
#include "SoundCache.h"
static const int SOUNDS_LOADING_PRIORITY { -7 }; // Make sure sounds load after the low rez texture mips static const int SOUNDS_LOADING_PRIORITY { -7 }; // Make sure sounds load after the low rez texture mips
@ -29,7 +32,7 @@ SoundCache::SoundCache(QObject* parent) :
SharedSoundPointer SoundCache::getSound(const QUrl& url) { SharedSoundPointer SoundCache::getSound(const QUrl& url) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
SharedSoundPointer result; SharedSoundPointer result;
QMetaObject::invokeMethod(this, "getSound", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "getSound",
Q_RETURN_ARG(SharedSoundPointer, result), Q_ARG(const QUrl&, url)); Q_RETURN_ARG(SharedSoundPointer, result), Q_ARG(const QUrl&, url));
return result; return result;
} }

View file

@ -15,6 +15,7 @@
#include <glm/gtx/transform.hpp> #include <glm/gtx/transform.hpp>
#include <glm/gtx/vector_query.hpp> #include <glm/gtx/vector_query.hpp>
#include <shared/QtHelpers.h>
#include <DeferredLightingEffect.h> #include <DeferredLightingEffect.h>
#include <EntityTreeRenderer.h> #include <EntityTreeRenderer.h>
#include <NodeList.h> #include <NodeList.h>
@ -1010,7 +1011,7 @@ glm::vec3 Avatar::getAbsoluteJointTranslationInObjectFrame(int index) const {
int Avatar::getJointIndex(const QString& name) const { int Avatar::getJointIndex(const QString& name) const {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
int result; int result;
QMetaObject::invokeMethod(const_cast<Avatar*>(this), "getJointIndex", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(const_cast<Avatar*>(this), "getJointIndex",
Q_RETURN_ARG(int, result), Q_ARG(const QString&, name)); Q_RETURN_ARG(int, result), Q_ARG(const QString&, name));
return result; return result;
} }
@ -1024,7 +1025,7 @@ int Avatar::getJointIndex(const QString& name) const {
QStringList Avatar::getJointNames() const { QStringList Avatar::getJointNames() const {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QStringList result; QStringList result;
QMetaObject::invokeMethod(const_cast<Avatar*>(this), "getJointNames", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(const_cast<Avatar*>(this), "getJointNames",
Q_RETURN_ARG(QStringList, result)); Q_RETURN_ARG(QStringList, result));
return result; return result;
} }
@ -1034,7 +1035,7 @@ QStringList Avatar::getJointNames() const {
glm::vec3 Avatar::getJointPosition(int index) const { glm::vec3 Avatar::getJointPosition(int index) const {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
glm::vec3 position; glm::vec3 position;
QMetaObject::invokeMethod(const_cast<Avatar*>(this), "getJointPosition", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(const_cast<Avatar*>(this), "getJointPosition",
Q_RETURN_ARG(glm::vec3, position), Q_ARG(const int, index)); Q_RETURN_ARG(glm::vec3, position), Q_ARG(const int, index));
return position; return position;
} }
@ -1046,7 +1047,7 @@ glm::vec3 Avatar::getJointPosition(int index) const {
glm::vec3 Avatar::getJointPosition(const QString& name) const { glm::vec3 Avatar::getJointPosition(const QString& name) const {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
glm::vec3 position; glm::vec3 position;
QMetaObject::invokeMethod(const_cast<Avatar*>(this), "getJointPosition", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(const_cast<Avatar*>(this), "getJointPosition",
Q_RETURN_ARG(glm::vec3, position), Q_ARG(const QString&, name)); Q_RETURN_ARG(glm::vec3, position), Q_ARG(const QString&, name));
return position; return position;
} }
@ -1105,7 +1106,7 @@ static std::shared_ptr<Model> allocateAttachmentModel(bool isSoft, const Rig& ri
void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) { void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setAttachmentData", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "setAttachmentData",
Q_ARG(const QVector<AttachmentData>, attachmentData)); Q_ARG(const QVector<AttachmentData>, attachmentData));
return; return;
} }

View file

@ -25,6 +25,7 @@
#include <QtNetwork/QNetworkReply> #include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest> #include <QtNetwork/QNetworkRequest>
#include <shared/QtHelpers.h>
#include <QVariantGLM.h> #include <QVariantGLM.h>
#include <Transform.h> #include <Transform.h>
#include <NetworkAccessManager.h> #include <NetworkAccessManager.h>
@ -1227,7 +1228,7 @@ bool AvatarData::isJointDataValid(int index) const {
} }
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
bool result; bool result;
QMetaObject::invokeMethod(const_cast<AvatarData*>(this), "isJointDataValid", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(const_cast<AvatarData*>(this), "isJointDataValid",
Q_RETURN_ARG(bool, result), Q_ARG(int, index)); Q_RETURN_ARG(bool, result), Q_ARG(int, index));
return result; return result;
} }
@ -1240,7 +1241,7 @@ glm::quat AvatarData::getJointRotation(int index) const {
} }
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
glm::quat result; glm::quat result;
QMetaObject::invokeMethod(const_cast<AvatarData*>(this), "getJointRotation", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(const_cast<AvatarData*>(this), "getJointRotation",
Q_RETURN_ARG(glm::quat, result), Q_ARG(int, index)); Q_RETURN_ARG(glm::quat, result), Q_ARG(int, index));
return result; return result;
} }
@ -1255,7 +1256,7 @@ glm::vec3 AvatarData::getJointTranslation(int index) const {
} }
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
glm::vec3 result; glm::vec3 result;
QMetaObject::invokeMethod(const_cast<AvatarData*>(this), "getJointTranslation", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(const_cast<AvatarData*>(this), "getJointTranslation",
Q_RETURN_ARG(glm::vec3, result), Q_ARG(int, index)); Q_RETURN_ARG(glm::vec3, result), Q_ARG(int, index));
return result; return result;
} }
@ -1266,7 +1267,7 @@ glm::vec3 AvatarData::getJointTranslation(int index) const {
glm::vec3 AvatarData::getJointTranslation(const QString& name) const { glm::vec3 AvatarData::getJointTranslation(const QString& name) const {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
glm::vec3 result; glm::vec3 result;
QMetaObject::invokeMethod(const_cast<AvatarData*>(this), "getJointTranslation", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(const_cast<AvatarData*>(this), "getJointTranslation",
Q_RETURN_ARG(glm::vec3, result), Q_ARG(const QString&, name)); Q_RETURN_ARG(glm::vec3, result), Q_ARG(const QString&, name));
return result; return result;
} }
@ -1344,7 +1345,7 @@ void AvatarData::clearJointData(const QString& name) {
bool AvatarData::isJointDataValid(const QString& name) const { bool AvatarData::isJointDataValid(const QString& name) const {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
bool result; bool result;
QMetaObject::invokeMethod(const_cast<AvatarData*>(this), "isJointDataValid", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(const_cast<AvatarData*>(this), "isJointDataValid",
Q_RETURN_ARG(bool, result), Q_ARG(const QString&, name)); Q_RETURN_ARG(bool, result), Q_ARG(const QString&, name));
return result; return result;
} }
@ -1354,7 +1355,7 @@ bool AvatarData::isJointDataValid(const QString& name) const {
glm::quat AvatarData::getJointRotation(const QString& name) const { glm::quat AvatarData::getJointRotation(const QString& name) const {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
glm::quat result; glm::quat result;
QMetaObject::invokeMethod(const_cast<AvatarData*>(this), "getJointRotation", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(const_cast<AvatarData*>(this), "getJointRotation",
Q_RETURN_ARG(glm::quat, result), Q_ARG(const QString&, name)); Q_RETURN_ARG(glm::quat, result), Q_ARG(const QString&, name));
return result; return result;
} }
@ -1364,8 +1365,7 @@ glm::quat AvatarData::getJointRotation(const QString& name) const {
QVector<glm::quat> AvatarData::getJointRotations() const { QVector<glm::quat> AvatarData::getJointRotations() const {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QVector<glm::quat> result; QVector<glm::quat> result;
QMetaObject::invokeMethod(const_cast<AvatarData*>(this), BLOCKING_INVOKE_METHOD(const_cast<AvatarData*>(this), "getJointRotations",
"getJointRotations", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QVector<glm::quat>, result)); Q_RETURN_ARG(QVector<glm::quat>, result));
return result; return result;
} }
@ -1380,8 +1380,7 @@ QVector<glm::quat> AvatarData::getJointRotations() const {
void AvatarData::setJointRotations(QVector<glm::quat> jointRotations) { void AvatarData::setJointRotations(QVector<glm::quat> jointRotations) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QVector<glm::quat> result; QVector<glm::quat> result;
QMetaObject::invokeMethod(const_cast<AvatarData*>(this), BLOCKING_INVOKE_METHOD(const_cast<AvatarData*>(this), "setJointRotations",
"setJointRotations", Qt::BlockingQueuedConnection,
Q_ARG(QVector<glm::quat>, jointRotations)); Q_ARG(QVector<glm::quat>, jointRotations));
} }
QWriteLocker writeLock(&_jointDataLock); QWriteLocker writeLock(&_jointDataLock);
@ -1398,8 +1397,7 @@ void AvatarData::setJointRotations(QVector<glm::quat> jointRotations) {
QVector<glm::vec3> AvatarData::getJointTranslations() const { QVector<glm::vec3> AvatarData::getJointTranslations() const {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QVector<glm::vec3> result; QVector<glm::vec3> result;
QMetaObject::invokeMethod(const_cast<AvatarData*>(this), BLOCKING_INVOKE_METHOD(const_cast<AvatarData*>(this), "getJointTranslations",
"getJointTranslations", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QVector<glm::vec3>, result)); Q_RETURN_ARG(QVector<glm::vec3>, result));
return result; return result;
} }
@ -1414,8 +1412,7 @@ QVector<glm::vec3> AvatarData::getJointTranslations() const {
void AvatarData::setJointTranslations(QVector<glm::vec3> jointTranslations) { void AvatarData::setJointTranslations(QVector<glm::vec3> jointTranslations) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QVector<glm::quat> result; QVector<glm::quat> result;
QMetaObject::invokeMethod(const_cast<AvatarData*>(this), BLOCKING_INVOKE_METHOD(const_cast<AvatarData*>(this), "setJointTranslations",
"setJointTranslations", Qt::BlockingQueuedConnection,
Q_ARG(QVector<glm::vec3>, jointTranslations)); Q_ARG(QVector<glm::vec3>, jointTranslations));
} }
QWriteLocker writeLock(&_jointDataLock); QWriteLocker writeLock(&_jointDataLock);
@ -1616,7 +1613,7 @@ void AvatarData::setDisplayName(const QString& displayName) {
QVector<AttachmentData> AvatarData::getAttachmentData() const { QVector<AttachmentData> AvatarData::getAttachmentData() const {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QVector<AttachmentData> result; QVector<AttachmentData> result;
QMetaObject::invokeMethod(const_cast<AvatarData*>(this), "getAttachmentData", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(const_cast<AvatarData*>(this), "getAttachmentData",
Q_RETURN_ARG(QVector<AttachmentData>, result)); Q_RETURN_ARG(QVector<AttachmentData>, result));
return result; return result;
} }
@ -2339,7 +2336,7 @@ void AvatarData::clearAvatarEntity(const QUuid& entityID) {
AvatarEntityMap AvatarData::getAvatarEntityData() const { AvatarEntityMap AvatarData::getAvatarEntityData() const {
AvatarEntityMap result; AvatarEntityMap result;
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(const_cast<AvatarData*>(this), "getAvatarEntityData", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(const_cast<AvatarData*>(this), "getAvatarEntityData",
Q_RETURN_ARG(AvatarEntityMap, result)); Q_RETURN_ARG(AvatarEntityMap, result));
return result; return result;
} }
@ -2380,7 +2377,7 @@ void AvatarData::setAvatarEntityData(const AvatarEntityMap& avatarEntityData) {
AvatarEntityIDs AvatarData::getAndClearRecentlyDetachedIDs() { AvatarEntityIDs AvatarData::getAndClearRecentlyDetachedIDs() {
AvatarEntityIDs result; AvatarEntityIDs result;
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(const_cast<AvatarData*>(this), "getAndClearRecentlyDetachedIDs", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(const_cast<AvatarData*>(this), "getAndClearRecentlyDetachedIDs",
Q_RETURN_ARG(AvatarEntityIDs, result)); Q_RETURN_ARG(AvatarEntityIDs, result));
return result; return result;
} }

View file

@ -11,14 +11,16 @@
#include <memory> #include <memory>
#include <math.h> #include <math.h>
#include <glm/gtc/type_ptr.hpp>
#include <QtCore/QTimer> #include <QtCore/QTimer>
#include <QtCore/QThread> #include <QtCore/QThread>
#include <QtWidgets/QApplication> #include <QtWidgets/QApplication>
#include <QtWidgets/QDesktopWidget> #include <QtWidgets/QDesktopWidget>
#include <glm/gtc/type_ptr.hpp>
#include <QtGui/QWindow> #include <QtGui/QWindow>
#include <QQuickWindow> #include <QQuickWindow>
#include <shared/QtHelpers.h>
#include <ui/Menu.h> #include <ui/Menu.h>
#include <NumericalConstants.h> #include <NumericalConstants.h>
#include <DependencyManager.h> #include <DependencyManager.h>
@ -289,7 +291,7 @@ glm::vec2 CompositorHelper::getReticleMaximumPosition() const {
void CompositorHelper::sendFakeMouseEvent() { void CompositorHelper::sendFakeMouseEvent() {
if (qApp->thread() != QThread::currentThread()) { if (qApp->thread() != QThread::currentThread()) {
QMetaObject::invokeMethod(this, "sendFakeMouseEvent", Qt::BlockingQueuedConnection); BLOCKING_INVOKE_METHOD(this, "sendFakeMouseEvent");
return; return;
} }

View file

@ -17,6 +17,7 @@
#include <QScriptSyntaxCheckResult> #include <QScriptSyntaxCheckResult>
#include <QThreadPool> #include <QThreadPool>
#include <shared/QtHelpers.h>
#include <ColorUtils.h> #include <ColorUtils.h>
#include <AbstractScriptingServicesInterface.h> #include <AbstractScriptingServicesInterface.h>
#include <AbstractViewStateInterface.h> #include <AbstractViewStateInterface.h>
@ -380,7 +381,7 @@ ModelPointer EntityTreeRenderer::allocateModel(const QString& url, float loading
// Only create and delete models on the thread that owns the EntityTreeRenderer // Only create and delete models on the thread that owns the EntityTreeRenderer
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "allocateModel", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "allocateModel",
Q_RETURN_ARG(ModelPointer, model), Q_RETURN_ARG(ModelPointer, model),
Q_ARG(const QString&, url)); Q_ARG(const QString&, url));
@ -397,7 +398,7 @@ ModelPointer EntityTreeRenderer::allocateModel(const QString& url, float loading
ModelPointer EntityTreeRenderer::updateModel(ModelPointer model, const QString& newUrl) { ModelPointer EntityTreeRenderer::updateModel(ModelPointer model, const QString& newUrl) {
// Only create and delete models on the thread that owns the EntityTreeRenderer // Only create and delete models on the thread that owns the EntityTreeRenderer
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "updateModel", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "updateModel",
Q_RETURN_ARG(ModelPointer, model), Q_RETURN_ARG(ModelPointer, model),
Q_ARG(ModelPointer, model), Q_ARG(ModelPointer, model),
Q_ARG(const QString&, newUrl)); Q_ARG(const QString&, newUrl));

View file

@ -9,19 +9,20 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include "EntityScriptingInterface.h"
#include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/transform.hpp> #include <glm/gtx/transform.hpp>
#include "EntityScriptingInterface.h"
#include <QFutureWatcher> #include <QFutureWatcher>
#include <QtConcurrent/QtConcurrentRun> #include <QtConcurrent/QtConcurrentRun>
#include "EntityItemID.h" #include <shared/QtHelpers.h>
#include <VariantMapToScriptValue.h> #include <VariantMapToScriptValue.h>
#include <SharedUtil.h> #include <SharedUtil.h>
#include <SpatialParentFinder.h> #include <SpatialParentFinder.h>
#include "EntityItemID.h"
#include "EntitiesLogging.h" #include "EntitiesLogging.h"
#include "EntityDynamicFactoryInterface.h" #include "EntityDynamicFactoryInterface.h"
#include "EntityDynamicInterface.h" #include "EntityDynamicInterface.h"
@ -1488,7 +1489,7 @@ int EntityScriptingInterface::getJointIndex(const QUuid& entityID, const QString
return -1; return -1;
} }
int result; int result;
QMetaObject::invokeMethod(_entityTree.get(), "getJointIndex", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(_entityTree.get(), "getJointIndex",
Q_RETURN_ARG(int, result), Q_ARG(QUuid, entityID), Q_ARG(QString, name)); Q_RETURN_ARG(int, result), Q_ARG(QUuid, entityID), Q_ARG(QString, name));
return result; return result;
} }
@ -1498,7 +1499,7 @@ QStringList EntityScriptingInterface::getJointNames(const QUuid& entityID) {
return QStringList(); return QStringList();
} }
QStringList result; QStringList result;
QMetaObject::invokeMethod(_entityTree.get(), "getJointNames", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(_entityTree.get(), "getJointNames",
Q_RETURN_ARG(QStringList, result), Q_ARG(QUuid, entityID)); Q_RETURN_ARG(QStringList, result), Q_ARG(QUuid, entityID));
return result; return result;
} }

View file

@ -198,11 +198,7 @@ gpu::TexturePointer TextureCache::getTextureByHash(const std::string& hash) {
std::unique_lock<std::mutex> lock(_texturesByHashesMutex); std::unique_lock<std::mutex> lock(_texturesByHashesMutex);
weakPointer = _texturesByHashes[hash]; weakPointer = _texturesByHashes[hash];
} }
auto result = weakPointer.lock(); return weakPointer.lock();
if (result) {
qCWarning(modelnetworking) << "QQQ Returning live texture for hash " << hash.c_str();
}
return result;
} }
gpu::TexturePointer TextureCache::cacheTextureByHash(const std::string& hash, const gpu::TexturePointer& texture) { gpu::TexturePointer TextureCache::cacheTextureByHash(const std::string& hash, const gpu::TexturePointer& texture) {

View file

@ -20,6 +20,7 @@
#include <QtNetwork/QHostInfo> #include <QtNetwork/QHostInfo>
#include <QtNetwork/QNetworkInterface> #include <QtNetwork/QNetworkInterface>
#include <shared/QtHelpers.h>
#include <ThreadHelpers.h> #include <ThreadHelpers.h>
#include <LogHandler.h> #include <LogHandler.h>
#include <UUID.h> #include <UUID.h>
@ -232,7 +233,7 @@ void NodeList::processICEPingPacket(QSharedPointer<ReceivedMessage> message) {
void NodeList::reset() { void NodeList::reset() {
if (thread() != QThread::currentThread()) { if (thread() != QThread::currentThread()) {
QMetaObject::invokeMethod(this, "reset", Qt::BlockingQueuedConnection); QMetaObject::invokeMethod(this, "reset");
return; return;
} }

View file

@ -9,22 +9,24 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include "ResourceCache.h"
#include <cfloat> #include <cfloat>
#include <cmath> #include <cmath>
#include <assert.h>
#include <QThread> #include <QThread>
#include <QTimer> #include <QTimer>
#include <SharedUtil.h> #include <SharedUtil.h>
#include <assert.h> #include <shared/QtHelpers.h>
#include <Trace.h>
#include <Profile.h>
#include "NetworkAccessManager.h" #include "NetworkAccessManager.h"
#include "NetworkLogging.h" #include "NetworkLogging.h"
#include "NodeList.h" #include "NodeList.h"
#include "ResourceCache.h"
#include <Trace.h>
#include <Profile.h>
#define clamp(x, min, max) (((x) < (min)) ? (min) :\ #define clamp(x, min, max) (((x) < (min)) ? (min) :\
(((x) > (max)) ? (max) :\ (((x) > (max)) ? (max) :\
@ -178,7 +180,7 @@ ScriptableResource* ResourceCache::prefetch(const QUrl& url, void* extra) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
// Must be called in thread to ensure getResource returns a valid pointer // Must be called in thread to ensure getResource returns a valid pointer
QMetaObject::invokeMethod(this, "prefetch", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "prefetch",
Q_RETURN_ARG(ScriptableResource*, result), Q_RETURN_ARG(ScriptableResource*, result),
Q_ARG(QUrl, url), Q_ARG(void*, extra)); Q_ARG(QUrl, url), Q_ARG(void*, extra));
return result; return result;
@ -301,7 +303,7 @@ QVariantList ResourceCache::getResourceList() {
QVariantList list; QVariantList list;
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
// NOTE: invokeMethod does not allow a const QObject* // NOTE: invokeMethod does not allow a const QObject*
QMetaObject::invokeMethod(this, "getResourceList", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "getResourceList",
Q_RETURN_ARG(QVariantList, list)); Q_RETURN_ARG(QVariantList, list));
} else { } else {
auto resources = _resources.uniqueKeys(); auto resources = _resources.uniqueKeys();

View file

@ -17,6 +17,7 @@
#include <QtCore/QThread> #include <QtCore/QThread>
#include <shared/QtHelpers.h>
#include <LogHandler.h> #include <LogHandler.h>
#include "../NetworkLogging.h" #include "../NetworkLogging.h"
@ -276,7 +277,7 @@ Connection* Socket::findOrCreateConnection(const HifiSockAddr& sockAddr) {
void Socket::clearConnections() { void Socket::clearConnections() {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "clearConnections", Qt::BlockingQueuedConnection); BLOCKING_INVOKE_METHOD(this, "clearConnections");
return; return;
} }

View file

@ -11,6 +11,8 @@
#include <QThread> #include <QThread>
#include <shared/QtHelpers.h>
#include "DisplayPlugin.h" #include "DisplayPlugin.h"
#include "InputPlugin.h" #include "InputPlugin.h"
#include "PluginManager.h" #include "PluginManager.h"
@ -21,7 +23,7 @@ InputConfiguration::InputConfiguration() {
QStringList InputConfiguration::inputPlugins() { QStringList InputConfiguration::inputPlugins() {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QStringList result; QStringList result;
QMetaObject::invokeMethod(this, "inputPlugins", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "inputPlugins",
Q_RETURN_ARG(QStringList, result)); Q_RETURN_ARG(QStringList, result));
return result; return result;
} }
@ -42,7 +44,7 @@ QStringList InputConfiguration::inputPlugins() {
QStringList InputConfiguration::activeInputPlugins() { QStringList InputConfiguration::activeInputPlugins() {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QStringList result; QStringList result;
QMetaObject::invokeMethod(this, "activeInputPlugins", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "activeInputPlugins",
Q_RETURN_ARG(QStringList, result)); Q_RETURN_ARG(QStringList, result));
return result; return result;
} }
@ -64,7 +66,7 @@ QStringList InputConfiguration::activeInputPlugins() {
QString InputConfiguration::configurationLayout(QString pluginName) { QString InputConfiguration::configurationLayout(QString pluginName) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QString result; QString result;
QMetaObject::invokeMethod(this, "configurationLayout", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "configurationLayout",
Q_RETURN_ARG(QString, result), Q_RETURN_ARG(QString, result),
Q_ARG(QString, pluginName)); Q_ARG(QString, pluginName));
return result; return result;
@ -81,7 +83,7 @@ QString InputConfiguration::configurationLayout(QString pluginName) {
void InputConfiguration::setConfigurationSettings(QJsonObject configurationSettings, QString pluginName) { void InputConfiguration::setConfigurationSettings(QJsonObject configurationSettings, QString pluginName) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setConfigurationSettings", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "setConfigurationSettings",
Q_ARG(QJsonObject, configurationSettings), Q_ARG(QJsonObject, configurationSettings),
Q_ARG(QString, pluginName)); Q_ARG(QString, pluginName));
return; return;
@ -97,7 +99,7 @@ void InputConfiguration::setConfigurationSettings(QJsonObject configurationSetti
QJsonObject InputConfiguration::configurationSettings(QString pluginName) { QJsonObject InputConfiguration::configurationSettings(QString pluginName) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QJsonObject result; QJsonObject result;
QMetaObject::invokeMethod(this, "configurationSettings", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "configurationSettings",
Q_RETURN_ARG(QJsonObject, result), Q_RETURN_ARG(QJsonObject, result),
Q_ARG(QString, pluginName)); Q_ARG(QString, pluginName));
return result; return result;
@ -113,7 +115,7 @@ QJsonObject InputConfiguration::configurationSettings(QString pluginName) {
void InputConfiguration::calibratePlugin(QString pluginName) { void InputConfiguration::calibratePlugin(QString pluginName) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "calibratePlugin", Qt::BlockingQueuedConnection); BLOCKING_INVOKE_METHOD(this, "calibratePlugin");
return; return;
} }
@ -128,7 +130,7 @@ void InputConfiguration::calibratePlugin(QString pluginName) {
bool InputConfiguration::uncalibratePlugin(QString pluginName) { bool InputConfiguration::uncalibratePlugin(QString pluginName) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
bool result; bool result;
QMetaObject::invokeMethod(this, "uncalibratePlugin", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "uncalibratePlugin",
Q_ARG(bool, result)); Q_ARG(bool, result));
return result; return result;
} }

View file

@ -8,6 +8,8 @@
#include <QThread> #include <QThread>
#include <shared/QtHelpers.h>
#include "ClipCache.h" #include "ClipCache.h"
#include "impl/PointerClip.h" #include "impl/PointerClip.h"
#include "Logging.h" #include "Logging.h"
@ -37,7 +39,7 @@ ClipCache::ClipCache(QObject* parent) :
NetworkClipLoaderPointer ClipCache::getClipLoader(const QUrl& url) { NetworkClipLoaderPointer ClipCache::getClipLoader(const QUrl& url) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
NetworkClipLoaderPointer result; NetworkClipLoaderPointer result;
QMetaObject::invokeMethod(this, "getClipLoader", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "getClipLoader",
Q_RETURN_ARG(NetworkClipLoaderPointer, result), Q_ARG(const QUrl&, url)); Q_RETURN_ARG(NetworkClipLoaderPointer, result), Q_ARG(const QUrl&, url));
return result; return result;
} }

View file

@ -18,6 +18,7 @@
#include <glm/gtx/transform.hpp> #include <glm/gtx/transform.hpp>
#include <glm/gtx/norm.hpp> #include <glm/gtx/norm.hpp>
#include <shared/QtHelpers.h>
#include <GeometryUtil.h> #include <GeometryUtil.h>
#include <PathUtils.h> #include <PathUtils.h>
#include <PerfStat.h> #include <PerfStat.h>
@ -870,7 +871,7 @@ bool Model::getRelativeDefaultJointTranslation(int jointIndex, glm::vec3& transl
QStringList Model::getJointNames() const { QStringList Model::getJointNames() const {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QStringList result; QStringList result;
QMetaObject::invokeMethod(const_cast<Model*>(this), "getJointNames", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(const_cast<Model*>(this), "getJointNames",
Q_RETURN_ARG(QStringList, result)); Q_RETURN_ARG(QStringList, result));
return result; return result;
} }

View file

@ -34,6 +34,8 @@ in vec2 _texCoord0;
out vec4 _fragColor; out vec4 _fragColor;
void main(void) { void main(void) {
_fragColor = vec4(0.0);
// Grab the fragment data from the uv // Grab the fragment data from the uv
vec2 texCoord = _texCoord0.st; vec2 texCoord = _texCoord0.st;

View file

@ -10,9 +10,12 @@
// //
#include "Config.h" #include "Config.h"
#include "Task.h"
#include <QtCore/QThread> #include <QtCore/QThread>
#include <shared/QtHelpers.h>
#include "Task.h"
using namespace task; using namespace task;
void JobConfig::setPresetList(const QJsonObject& object) { void JobConfig::setPresetList(const QJsonObject& object) {
@ -58,7 +61,7 @@ void TaskConfig::transferChildrenConfigs(QConfigPointer source) {
void TaskConfig::refresh() { void TaskConfig::refresh() {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "refresh", Qt::BlockingQueuedConnection); BLOCKING_INVOKE_METHOD(this, "refresh");
return; return;
} }

View file

@ -13,6 +13,8 @@
#include <QVector3D> #include <QVector3D>
#include <shared/QtHelpers.h>
#include "ScriptAudioInjector.h" #include "ScriptAudioInjector.h"
#include "ScriptEngineLogging.h" #include "ScriptEngineLogging.h"
@ -32,7 +34,7 @@ ScriptAudioInjector* AudioScriptingInterface::playSound(SharedSoundPointer sound
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
ScriptAudioInjector* injector = NULL; ScriptAudioInjector* injector = NULL;
QMetaObject::invokeMethod(this, "playSound", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "playSound",
Q_RETURN_ARG(ScriptAudioInjector*, injector), Q_RETURN_ARG(ScriptAudioInjector*, injector),
Q_ARG(SharedSoundPointer, sound), Q_ARG(SharedSoundPointer, sound),
Q_ARG(const AudioInjectorOptions&, injectorOptions)); Q_ARG(const AudioInjectorOptions&, injectorOptions));

View file

@ -14,6 +14,7 @@
#include <QtScript/QScriptValue> #include <QtScript/QScriptValue>
#include <QtWidgets/QFileDialog> #include <QtWidgets/QFileDialog>
#include <shared/QtHelpers.h>
#include <AssetClient.h> #include <AssetClient.h>
#include <AssetUpload.h> #include <AssetUpload.h>
#include <BuildInfo.h> #include <BuildInfo.h>
@ -98,7 +99,7 @@ void RecordingScriptingInterface::loadRecording(const QString& url, QScriptValue
void RecordingScriptingInterface::startPlaying() { void RecordingScriptingInterface::startPlaying() {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "startPlaying", Qt::BlockingQueuedConnection); BLOCKING_INVOKE_METHOD(this, "startPlaying");
return; return;
} }
@ -115,7 +116,7 @@ void RecordingScriptingInterface::setPlayerAudioOffset(float audioOffset) {
void RecordingScriptingInterface::setPlayerTime(float time) { void RecordingScriptingInterface::setPlayerTime(float time) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setPlayerTime", Qt::BlockingQueuedConnection, Q_ARG(float, time)); BLOCKING_INVOKE_METHOD(this, "setPlayerTime", Q_ARG(float, time));
return; return;
} }
_player->seek(time); _player->seek(time);
@ -147,7 +148,7 @@ void RecordingScriptingInterface::setPlayerUseSkeletonModel(bool useSkeletonMode
void RecordingScriptingInterface::pausePlayer() { void RecordingScriptingInterface::pausePlayer() {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "pausePlayer", Qt::BlockingQueuedConnection); BLOCKING_INVOKE_METHOD(this, "pausePlayer");
return; return;
} }
_player->pause(); _player->pause();
@ -155,7 +156,7 @@ void RecordingScriptingInterface::pausePlayer() {
void RecordingScriptingInterface::stopPlaying() { void RecordingScriptingInterface::stopPlaying() {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "stopPlaying", Qt::BlockingQueuedConnection); BLOCKING_INVOKE_METHOD(this, "stopPlaying");
return; return;
} }
_player->stop(); _player->stop();
@ -176,7 +177,7 @@ void RecordingScriptingInterface::startRecording() {
} }
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "startRecording", Qt::BlockingQueuedConnection); BLOCKING_INVOKE_METHOD(this, "startRecording");
return; return;
} }
@ -199,7 +200,7 @@ QString RecordingScriptingInterface::getDefaultRecordingSaveDirectory() {
void RecordingScriptingInterface::saveRecording(const QString& filename) { void RecordingScriptingInterface::saveRecording(const QString& filename) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "saveRecording", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "saveRecording",
Q_ARG(QString, filename)); Q_ARG(QString, filename));
return; return;
} }
@ -220,7 +221,7 @@ bool RecordingScriptingInterface::saveRecordingToAsset(QScriptValue getClipAtpUr
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
bool result; bool result;
QMetaObject::invokeMethod(this, "saveRecordingToAsset", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "saveRecordingToAsset",
Q_RETURN_ARG(bool, result), Q_RETURN_ARG(bool, result),
Q_ARG(QScriptValue, getClipAtpUrl)); Q_ARG(QScriptValue, getClipAtpUrl));
return result; return result;
@ -257,7 +258,7 @@ bool RecordingScriptingInterface::saveRecordingToAsset(QScriptValue getClipAtpUr
void RecordingScriptingInterface::loadLastRecording() { void RecordingScriptingInterface::loadLastRecording() {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "loadLastRecording", Qt::BlockingQueuedConnection); BLOCKING_INVOKE_METHOD(this, "loadLastRecording");
return; return;
} }

View file

@ -36,6 +36,7 @@
#include <QtScriptTools/QScriptEngineDebugger> #include <QtScriptTools/QScriptEngineDebugger>
#include <shared/QtHelpers.h>
#include <AudioConstants.h> #include <AudioConstants.h>
#include <AudioEffectOptions.h> #include <AudioEffectOptions.h>
#include <AvatarData.h> #include <AvatarData.h>
@ -436,12 +437,12 @@ void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) {
_fileNameString = url.toString(); _fileNameString = url.toString();
_isReloading = reload; _isReloading = reload;
// Check that script has a supported file extension // Check that script has a supported file extension
if (!hasValidScriptSuffix(_fileNameString)) { if (!hasValidScriptSuffix(_fileNameString)) {
scriptErrorMessage("File extension of file: " + _fileNameString + " is not a currently supported script type"); scriptErrorMessage("File extension of file: " + _fileNameString + " is not a currently supported script type");
emit errorLoadingScript(_fileNameString); emit errorLoadingScript(_fileNameString);
return; return;
} }
const auto maxRetries = 0; // for consistency with previous scriptCache->getScript() behavior const auto maxRetries = 0; // for consistency with previous scriptCache->getScript() behavior
auto scriptCache = DependencyManager::get<ScriptCache>(); auto scriptCache = DependencyManager::get<ScriptCache>();
@ -964,7 +965,7 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi
qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::evaluate() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " qCDebug(scriptengine) << "*** WARNING *** ScriptEngine::evaluate() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
"sourceCode:" << sourceCode << " fileName:" << fileName << "lineNumber:" << lineNumber; "sourceCode:" << sourceCode << " fileName:" << fileName << "lineNumber:" << lineNumber;
#endif #endif
QMetaObject::invokeMethod(this, "evaluate", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "evaluate",
Q_RETURN_ARG(QScriptValue, result), Q_RETURN_ARG(QScriptValue, result),
Q_ARG(const QString&, sourceCode), Q_ARG(const QString&, sourceCode),
Q_ARG(const QString&, fileName), Q_ARG(const QString&, fileName),
@ -1820,7 +1821,7 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac
clearExceptions(); clearExceptions();
} }
} else { } else {
scriptWarningMessage("Script.include() skipping evaluation of previously included url:" + url.toString()); scriptPrintedMessage("Script.include() skipping evaluation of previously included url:" + url.toString());
} }
} }
} }

View file

@ -12,6 +12,7 @@
#include <QtWidgets/QApplication> #include <QtWidgets/QApplication>
#include <shared/QtHelpers.h>
#include <SettingHandle.h> #include <SettingHandle.h>
#include <UserActivityLogger.h> #include <UserActivityLogger.h>
#include <PathUtils.h> #include <PathUtils.h>
@ -452,7 +453,7 @@ ScriptEngine* ScriptEngines::loadScript(const QUrl& scriptFilename, bool isUserL
bool activateMainWindow, bool reload) { bool activateMainWindow, bool reload) {
if (thread() != QThread::currentThread()) { if (thread() != QThread::currentThread()) {
ScriptEngine* result { nullptr }; ScriptEngine* result { nullptr };
QMetaObject::invokeMethod(this, "loadScript", Qt::BlockingQueuedConnection, Q_RETURN_ARG(ScriptEngine*, result), BLOCKING_INVOKE_METHOD(this, "loadScript", Q_RETURN_ARG(ScriptEngine*, result),
Q_ARG(QUrl, scriptFilename), Q_ARG(QUrl, scriptFilename),
Q_ARG(bool, isUserLoaded), Q_ARG(bool, isUserLoaded),
Q_ARG(bool, loadScriptFromEditor), Q_ARG(bool, loadScriptFromEditor),

View file

@ -1076,3 +1076,24 @@ void setMaxCores(uint8_t maxCores) {
SetProcessAffinityMask(process, newProcessAffinity); SetProcessAffinityMask(process, newProcessAffinity);
#endif #endif
} }
#ifdef Q_OS_WIN
VOID CALLBACK parentDiedCallback(PVOID lpParameter, BOOLEAN timerOrWaitFired) {
if (!timerOrWaitFired && qApp) {
qDebug() << "Parent process died, quitting";
qApp->quit();
}
}
void watchParentProcess(int parentPID) {
DWORD processID = parentPID;
HANDLE procHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID);
HANDLE newHandle;
RegisterWaitForSingleObject(&newHandle, procHandle, parentDiedCallback, NULL, INFINITE, WT_EXECUTEONLYONCE);
}
#else
void watchParentProcess(int parentPID) {
qWarning() << "Parent PID option not implemented on this plateform";
}
#endif // Q_OS_WIN

View file

@ -235,4 +235,7 @@ const QString& getInterfaceSharedMemoryName();
void setMaxCores(uint8_t maxCores); void setMaxCores(uint8_t maxCores);
const QString PARENT_PID_OPTION = "parent-pid";
void watchParentProcess(int parentPID);
#endif // hifi_SharedUtil_h #endif // hifi_SharedUtil_h

View file

@ -17,10 +17,6 @@ void moveToNewNamedThread(QObject* object, const QString& name, std::function<vo
QThread* thread = new QThread(); QThread* thread = new QThread();
thread->setObjectName(name); thread->setObjectName(name);
if (priority != QThread::InheritPriority) {
thread->setPriority(priority);
}
QString tempName = name; QString tempName = name;
QObject::connect(thread, &QThread::started, [startCallback] { QObject::connect(thread, &QThread::started, [startCallback] {
startCallback(); startCallback();
@ -32,6 +28,9 @@ void moveToNewNamedThread(QObject* object, const QString& name, std::function<vo
// put the object on the thread // put the object on the thread
object->moveToThread(thread); object->moveToThread(thread);
thread->start(); thread->start();
if (priority != QThread::InheritPriority) {
thread->setPriority(priority);
}
} }
void moveToNewNamedThread(QObject* object, const QString& name, QThread::Priority priority) { void moveToNewNamedThread(QObject* object, const QString& name, QThread::Priority priority) {

View file

@ -16,6 +16,7 @@
#include <QtCore/QMutex> #include <QtCore/QMutex>
#include <QtCore/QMutexLocker> #include <QtCore/QMutexLocker>
#include <QtCore/QWaitCondition>
#include <QtCore/QObject> #include <QtCore/QObject>
#include <QtCore/QString> #include <QtCore/QString>
#include <QtCore/QThread> #include <QtCore/QThread>
@ -34,4 +35,25 @@ void withLock(QMutex& lock, F function) {
void moveToNewNamedThread(QObject* object, const QString& name, std::function<void()> startCallback, QThread::Priority priority = QThread::InheritPriority); void moveToNewNamedThread(QObject* object, const QString& name, std::function<void()> startCallback, QThread::Priority priority = QThread::InheritPriority);
void moveToNewNamedThread(QObject* object, const QString& name, QThread::Priority priority = QThread::InheritPriority); void moveToNewNamedThread(QObject* object, const QString& name, QThread::Priority priority = QThread::InheritPriority);
class ConditionalGuard {
public:
void trigger() {
QMutexLocker locker(&_mutex);
_triggered = true;
_condition.wakeAll();
}
bool wait(unsigned long time = ULONG_MAX) {
QMutexLocker locker(&_mutex);
if (!_triggered) {
_condition.wait(&_mutex, time);
}
return _triggered;
}
private:
QMutex _mutex;
QWaitCondition _condition;
bool _triggered { false };
};
#endif #endif

View file

@ -0,0 +1,58 @@
//
// Created by Bradley Austin Davis on 2015/11/09
// Copyright 2013-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
//
#include "QtHelpers.h"
#include <QtCore/QThread>
#include <QtCore/QCoreApplication>
#include <QtCore/QLoggingCategory>
Q_LOGGING_CATEGORY(thread_safety, "hifi.thread_safety")
namespace hifi { namespace qt {
bool blockingInvokeMethod(
const char* function,
QObject *obj, const char *member,
QGenericReturnArgument ret,
QGenericArgument val0,
QGenericArgument val1,
QGenericArgument val2,
QGenericArgument val3,
QGenericArgument val4,
QGenericArgument val5,
QGenericArgument val6,
QGenericArgument val7,
QGenericArgument val8,
QGenericArgument val9) {
if (QThread::currentThread() == qApp->thread()) {
qCWarning(thread_safety) << "BlockingQueuedConnection invoked on main thread from " << function;
}
return QMetaObject::invokeMethod(obj, member,
Qt::BlockingQueuedConnection, ret, val0, val1, val2, val3, val4, val5, val6, val7, val8, val9);
}
bool blockingInvokeMethod(
const char* function,
QObject *obj, const char *member,
QGenericArgument val0,
QGenericArgument val1,
QGenericArgument val2,
QGenericArgument val3,
QGenericArgument val4,
QGenericArgument val5,
QGenericArgument val6,
QGenericArgument val7,
QGenericArgument val8,
QGenericArgument val9) {
return blockingInvokeMethod(function, obj, member, QGenericReturnArgument(), val0, val1, val2, val3, val4, val5, val6, val7, val8, val9);
}
} }

View file

@ -0,0 +1,52 @@
//
// Created by Bradley Austin Davis on 2017/06/29
// Copyright 2013-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
//
#pragma once
#ifndef hifi_Shared_QtHelpers_h
#define hifi_Shared_QtHelpers_h
#include <QtCore/QObject>
namespace hifi { namespace qt {
bool blockingInvokeMethod(
const char* function,
QObject *obj, const char *member,
QGenericReturnArgument ret,
QGenericArgument val0 = QGenericArgument(Q_NULLPTR),
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
QGenericArgument val4 = QGenericArgument(),
QGenericArgument val5 = QGenericArgument(),
QGenericArgument val6 = QGenericArgument(),
QGenericArgument val7 = QGenericArgument(),
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument());
bool blockingInvokeMethod(
const char* function,
QObject *obj, const char *member,
QGenericArgument val0 = QGenericArgument(Q_NULLPTR),
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
QGenericArgument val4 = QGenericArgument(),
QGenericArgument val5 = QGenericArgument(),
QGenericArgument val6 = QGenericArgument(),
QGenericArgument val7 = QGenericArgument(),
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument());
} }
#define BLOCKING_INVOKE_METHOD(obj, member, ...) \
hifi::qt::blockingInvokeMethod(__FUNCTION__, obj, member, ##__VA_ARGS__)
#endif

View file

@ -15,6 +15,7 @@
#include <QtQuick/QQuickWindow> #include <QtQuick/QQuickWindow>
#include <QtQml/QtQml> #include <QtQml/QtQml>
#include <shared/QtHelpers.h>
#include <gl/GLHelpers.h> #include <gl/GLHelpers.h>
#include <AbstractUriHandler.h> #include <AbstractUriHandler.h>
@ -249,7 +250,7 @@ int OffscreenUi::waitForMessageBoxResult(QQuickItem* messageBox) {
QMessageBox::StandardButton OffscreenUi::messageBox(Icon icon, const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { QMessageBox::StandardButton OffscreenUi::messageBox(Icon icon, const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMessageBox::StandardButton result = QMessageBox::StandardButton::NoButton; QMessageBox::StandardButton result = QMessageBox::StandardButton::NoButton;
QMetaObject::invokeMethod(this, "messageBox", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "messageBox",
Q_RETURN_ARG(QMessageBox::StandardButton, result), Q_RETURN_ARG(QMessageBox::StandardButton, result),
Q_ARG(Icon, icon), Q_ARG(Icon, icon),
Q_ARG(QString, title), Q_ARG(QString, title),
@ -351,7 +352,7 @@ QVariant OffscreenUi::getCustomInfo(const Icon icon, const QString& title, const
QVariant OffscreenUi::inputDialog(const Icon icon, const QString& title, const QString& label, const QVariant& current) { QVariant OffscreenUi::inputDialog(const Icon icon, const QString& title, const QString& label, const QVariant& current) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QVariant result; QVariant result;
QMetaObject::invokeMethod(this, "inputDialog", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "inputDialog",
Q_RETURN_ARG(QVariant, result), Q_RETURN_ARG(QVariant, result),
Q_ARG(Icon, icon), Q_ARG(Icon, icon),
Q_ARG(QString, title), Q_ARG(QString, title),
@ -366,7 +367,7 @@ QVariant OffscreenUi::inputDialog(const Icon icon, const QString& title, const Q
QVariant OffscreenUi::customInputDialog(const Icon icon, const QString& title, const QVariantMap& config) { QVariant OffscreenUi::customInputDialog(const Icon icon, const QString& title, const QVariantMap& config) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QVariant result; QVariant result;
QMetaObject::invokeMethod(this, "customInputDialog", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "customInputDialog",
Q_RETURN_ARG(QVariant, result), Q_RETURN_ARG(QVariant, result),
Q_ARG(Icon, icon), Q_ARG(Icon, icon),
Q_ARG(QString, title), Q_ARG(QString, title),
@ -640,7 +641,7 @@ QString OffscreenUi::fileDialog(const QVariantMap& properties) {
QString OffscreenUi::fileOpenDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { QString OffscreenUi::fileOpenDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QString result; QString result;
QMetaObject::invokeMethod(this, "fileOpenDialog", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "fileOpenDialog",
Q_RETURN_ARG(QString, result), Q_RETURN_ARG(QString, result),
Q_ARG(QString, caption), Q_ARG(QString, caption),
Q_ARG(QString, dir), Q_ARG(QString, dir),
@ -662,7 +663,7 @@ QString OffscreenUi::fileOpenDialog(const QString& caption, const QString& dir,
QString OffscreenUi::fileSaveDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { QString OffscreenUi::fileSaveDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QString result; QString result;
QMetaObject::invokeMethod(this, "fileSaveDialog", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "fileSaveDialog",
Q_RETURN_ARG(QString, result), Q_RETURN_ARG(QString, result),
Q_ARG(QString, caption), Q_ARG(QString, caption),
Q_ARG(QString, dir), Q_ARG(QString, dir),
@ -686,7 +687,7 @@ QString OffscreenUi::fileSaveDialog(const QString& caption, const QString& dir,
QString OffscreenUi::existingDirectoryDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) { QString OffscreenUi::existingDirectoryDialog(const QString& caption, const QString& dir, const QString& filter, QString* selectedFilter, QFileDialog::Options options) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QString result; QString result;
QMetaObject::invokeMethod(this, "existingDirectoryDialog", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "existingDirectoryDialog",
Q_RETURN_ARG(QString, result), Q_RETURN_ARG(QString, result),
Q_ARG(QString, caption), Q_ARG(QString, caption),
Q_ARG(QString, dir), Q_ARG(QString, dir),
@ -773,7 +774,7 @@ QString OffscreenUi::assetOpenDialog(const QString& caption, const QString& dir,
// ATP equivalent of fileOpenDialog(). // ATP equivalent of fileOpenDialog().
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QString result; QString result;
QMetaObject::invokeMethod(this, "assetOpenDialog", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "assetOpenDialog",
Q_RETURN_ARG(QString, result), Q_RETURN_ARG(QString, result),
Q_ARG(QString, caption), Q_ARG(QString, caption),
Q_ARG(QString, dir), Q_ARG(QString, dir),

View file

@ -13,6 +13,7 @@
#include <QtScript/QScriptContext> #include <QtScript/QScriptContext>
#include <QtScript/QScriptEngine> #include <QtScript/QScriptEngine>
#include <shared/QtHelpers.h>
#include "OffscreenUi.h" #include "OffscreenUi.h"
static const char* const URL_PROPERTY = "source"; static const char* const URL_PROPERTY = "source";
@ -21,39 +22,51 @@ static const char* const SCRIPT_PROPERTY = "scriptUrl";
// Method called by Qt scripts to create a new web window in the overlay // Method called by Qt scripts to create a new web window in the overlay
QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) { QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) {
auto properties = parseArguments(context); auto properties = parseArguments(context);
QmlWebWindowClass* retVal { nullptr };
auto offscreenUi = DependencyManager::get<OffscreenUi>(); auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->executeOnUiThread([&] { QmlWebWindowClass* retVal = new QmlWebWindowClass();
retVal = new QmlWebWindowClass();
retVal->initQml(properties);
}, true);
Q_ASSERT(retVal); Q_ASSERT(retVal);
if (QThread::currentThread() != qApp->thread()) {
retVal->moveToThread(qApp->thread());
QMetaObject::invokeMethod(retVal, "initQml", Q_ARG(QVariantMap, properties));
} else {
retVal->initQml(properties);
}
connect(engine, &QScriptEngine::destroyed, retVal, &QmlWindowClass::deleteLater); connect(engine, &QScriptEngine::destroyed, retVal, &QmlWindowClass::deleteLater);
return engine->newQObject(retVal); return engine->newQObject(retVal);
} }
QString QmlWebWindowClass::getURL() const { QString QmlWebWindowClass::getURL() {
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant { if (QThread::currentThread() != thread()) {
if (_qmlWindow.isNull()) { QString result;
return QVariant(); BLOCKING_INVOKE_METHOD(this, "getURL", Q_RETURN_ARG(QString, result));
} return result;
return _qmlWindow->property(URL_PROPERTY); }
});
return result.toString(); if (_qmlWindow.isNull()) {
return QString();
}
return _qmlWindow->property(URL_PROPERTY).toString();
} }
void QmlWebWindowClass::setURL(const QString& urlString) { void QmlWebWindowClass::setURL(const QString& urlString) {
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] { if (QThread::currentThread() != thread()) {
if (!_qmlWindow.isNull()) { QMetaObject::invokeMethod(this, "setURL", Q_ARG(QString, urlString));
_qmlWindow->setProperty(URL_PROPERTY, urlString); return;
} }
});
if (!_qmlWindow.isNull()) {
_qmlWindow->setProperty(URL_PROPERTY, urlString);
}
} }
void QmlWebWindowClass::setScriptURL(const QString& script) { void QmlWebWindowClass::setScriptURL(const QString& script) {
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] { if (QThread::currentThread() != thread()) {
if (!_qmlWindow.isNull()) { QMetaObject::invokeMethod(this, "setScriptURL", Q_ARG(QString, script));
_qmlWindow->setProperty(SCRIPT_PROPERTY, script); return;
} }
});
if (!_qmlWindow.isNull()) {
_qmlWindow->setProperty(SCRIPT_PROPERTY, script);
}
} }

View file

@ -20,7 +20,7 @@ public:
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine); static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
public slots: public slots:
QString getURL() const; QString getURL();
void setURL(const QString& url); void setURL(const QString& url);
void setScriptURL(const QString& script); void setScriptURL(const QString& script);

View file

@ -23,6 +23,7 @@
#include <QtCore/QJsonDocument> #include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject> #include <QtCore/QJsonObject>
#include <shared/QtHelpers.h>
#include "OffscreenUi.h" #include "OffscreenUi.h"
static const char* const SOURCE_PROPERTY = "source"; static const char* const SOURCE_PROPERTY = "source";
@ -73,13 +74,15 @@ QVariantMap QmlWindowClass::parseArguments(QScriptContext* context) {
// Method called by Qt scripts to create a new web window in the overlay // Method called by Qt scripts to create a new web window in the overlay
QScriptValue QmlWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) { QScriptValue QmlWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) {
auto properties = parseArguments(context); auto properties = parseArguments(context);
QmlWindowClass* retVal { nullptr };
auto offscreenUi = DependencyManager::get<OffscreenUi>(); auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->executeOnUiThread([&] { QmlWindowClass* retVal = new QmlWindowClass();
retVal = new QmlWindowClass();
retVal->initQml(properties);
}, true);
Q_ASSERT(retVal); Q_ASSERT(retVal);
if (QThread::currentThread() != qApp->thread()) {
retVal->moveToThread(qApp->thread());
BLOCKING_INVOKE_METHOD(retVal, "initQml", Q_ARG(QVariantMap, properties));
} else {
retVal->initQml(properties);
}
connect(engine, &QScriptEngine::destroyed, retVal, &QmlWindowClass::deleteLater); connect(engine, &QScriptEngine::destroyed, retVal, &QmlWindowClass::deleteLater);
return engine->newQObject(retVal); return engine->newQObject(retVal);
} }
@ -90,9 +93,10 @@ QmlWindowClass::QmlWindowClass() {
void QmlWindowClass::initQml(QVariantMap properties) { void QmlWindowClass::initQml(QVariantMap properties) {
auto offscreenUi = DependencyManager::get<OffscreenUi>(); auto offscreenUi = DependencyManager::get<OffscreenUi>();
_toolWindow = properties.contains(TOOLWINDOW_PROPERTY) && properties[TOOLWINDOW_PROPERTY].toBool();
_source = properties[SOURCE_PROPERTY].toString(); _source = properties[SOURCE_PROPERTY].toString();
#if QML_TOOL_WINDOW
_toolWindow = properties.contains(TOOLWINDOW_PROPERTY) && properties[TOOLWINDOW_PROPERTY].toBool();
if (_toolWindow) { if (_toolWindow) {
// Build the event bridge and wrapper on the main thread // Build the event bridge and wrapper on the main thread
_qmlWindow = offscreenUi->getToolWindow(); _qmlWindow = offscreenUi->getToolWindow();
@ -103,10 +107,11 @@ void QmlWindowClass::initQml(QVariantMap properties) {
Q_ARG(QVariant, QVariant::fromValue(properties))); Q_ARG(QVariant, QVariant::fromValue(properties)));
Q_ASSERT(invokeResult); Q_ASSERT(invokeResult);
} else { } else {
#endif
// Build the event bridge and wrapper on the main thread // Build the event bridge and wrapper on the main thread
offscreenUi->loadInNewContext(qmlSource(), [&](QQmlContext* context, QObject* object) { offscreenUi->loadInNewContext(qmlSource(), [&](QQmlContext* context, QObject* object) {
_qmlWindow = object; _qmlWindow = object;
context->setContextProperty("eventBridge", this); context->setContextProperty(EVENT_BRIDGE_PROPERTY, this);
context->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership); context->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership);
context->engine()->setObjectOwnership(object, QQmlEngine::CppOwnership); context->engine()->setObjectOwnership(object, QQmlEngine::CppOwnership);
if (properties.contains(TITLE_PROPERTY)) { if (properties.contains(TITLE_PROPERTY)) {
@ -133,7 +138,9 @@ void QmlWindowClass::initQml(QVariantMap properties) {
connect(_qmlWindow, SIGNAL(moved(QVector2D)), this, SLOT(hasMoved(QVector2D)), Qt::QueuedConnection); connect(_qmlWindow, SIGNAL(moved(QVector2D)), this, SLOT(hasMoved(QVector2D)), Qt::QueuedConnection);
connect(_qmlWindow, SIGNAL(windowClosed()), this, SLOT(hasClosed()), Qt::QueuedConnection); connect(_qmlWindow, SIGNAL(windowClosed()), this, SLOT(hasClosed()), Qt::QueuedConnection);
}); });
#if QML_TOOL_WINDOW
} }
#endif
Q_ASSERT(_qmlWindow); Q_ASSERT(_qmlWindow);
Q_ASSERT(dynamic_cast<const QQuickItem*>(_qmlWindow.data())); Q_ASSERT(dynamic_cast<const QQuickItem*>(_qmlWindow.data()));
} }
@ -207,56 +214,77 @@ QmlWindowClass::~QmlWindowClass() {
} }
QQuickItem* QmlWindowClass::asQuickItem() const { QQuickItem* QmlWindowClass::asQuickItem() const {
#if QML_TOOL_WINDOW
if (_toolWindow) { if (_toolWindow) {
return DependencyManager::get<OffscreenUi>()->getToolWindow(); return DependencyManager::get<OffscreenUi>()->getToolWindow();
} }
#endif
return _qmlWindow.isNull() ? nullptr : dynamic_cast<QQuickItem*>(_qmlWindow.data()); return _qmlWindow.isNull() ? nullptr : dynamic_cast<QQuickItem*>(_qmlWindow.data());
} }
void QmlWindowClass::setVisible(bool visible) { void QmlWindowClass::setVisible(bool visible) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setVisible", Q_ARG(bool, visible));
return;
}
QQuickItem* targetWindow = asQuickItem(); QQuickItem* targetWindow = asQuickItem();
#if QML_TOOL_WINDOW
if (_toolWindow) { if (_toolWindow) {
// For tool window tabs we special case visibility as a function call on the tab parent // For tool window tabs we special case visibility as a function call on the tab parent
// The tool window itself has special logic based on whether any tabs are visible // The tool window itself has special logic based on whether any tabs are visible
QMetaObject::invokeMethod(targetWindow, "showTabForUrl", Qt::QueuedConnection, Q_ARG(QVariant, _source), Q_ARG(QVariant, visible)); QMetaObject::invokeMethod(targetWindow, "showTabForUrl", Qt::QueuedConnection, Q_ARG(QVariant, _source), Q_ARG(QVariant, visible));
} else { return;
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] { }
targetWindow->setProperty(OFFSCREEN_VISIBILITY_PROPERTY, visible); #endif
}); targetWindow->setProperty(OFFSCREEN_VISIBILITY_PROPERTY, visible);
}
bool QmlWindowClass::isVisible() {
if (QThread::currentThread() != thread()) {
bool result = false;
BLOCKING_INVOKE_METHOD(this, "isVisible", Q_RETURN_ARG(bool, result));
return result;
} }
}
bool QmlWindowClass::isVisible() const {
// The tool window itself has special logic based on whether any tabs are enabled // The tool window itself has special logic based on whether any tabs are enabled
return DependencyManager::get<OffscreenUi>()->returnFromUiThread([&] { if (_qmlWindow.isNull()) {
if (_qmlWindow.isNull()) { return false;
return QVariant::fromValue(false); }
}
if (_toolWindow) { #if QML_TOOL_WINDOW
return QVariant::fromValue(dynamic_cast<QQuickItem*>(_qmlWindow.data())->isEnabled()); if (_toolWindow) {
} else { return dynamic_cast<QQuickItem*>(_qmlWindow.data())->isEnabled();
return QVariant::fromValue(asQuickItem()->isVisible()); }
} #endif
}).toBool();
return asQuickItem()->isVisible();
} }
glm::vec2 QmlWindowClass::getPosition() const { glm::vec2 QmlWindowClass::getPosition() {
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant { if (QThread::currentThread() != thread()) {
if (_qmlWindow.isNull()) { vec2 result;
return QVariant(QPointF(0, 0)); BLOCKING_INVOKE_METHOD(this, "getPosition", Q_RETURN_ARG(vec2, result));
} return result;
return asQuickItem()->position(); }
});
return toGlm(result.toPointF()); if (_qmlWindow.isNull()) {
return {};
}
return toGlm(asQuickItem()->position());
} }
void QmlWindowClass::setPosition(const glm::vec2& position) { void QmlWindowClass::setPosition(const glm::vec2& position) {
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] { if (QThread::currentThread() != thread()) {
if (!_qmlWindow.isNull()) { QMetaObject::invokeMethod(this, "setPosition", Q_ARG(vec2, position));
asQuickItem()->setPosition(QPointF(position.x, position.y)); return;
} }
});
if (!_qmlWindow.isNull()) {
asQuickItem()->setPosition(QPointF(position.x, position.y));
}
} }
void QmlWindowClass::setPosition(int x, int y) { void QmlWindowClass::setPosition(int x, int y) {
@ -268,23 +296,29 @@ glm::vec2 toGlm(const QSizeF& size) {
return glm::vec2(size.width(), size.height()); return glm::vec2(size.width(), size.height());
} }
glm::vec2 QmlWindowClass::getSize() const { glm::vec2 QmlWindowClass::getSize() {
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant { if (QThread::currentThread() != thread()) {
if (_qmlWindow.isNull()) { vec2 result;
return QVariant(QSizeF(0, 0)); BLOCKING_INVOKE_METHOD(this, "getSize", Q_RETURN_ARG(vec2, result));
} return result;
QQuickItem* targetWindow = asQuickItem(); }
return QSizeF(targetWindow->width(), targetWindow->height());
}); if (_qmlWindow.isNull()) {
return toGlm(result.toSizeF()); return {};
}
QQuickItem* targetWindow = asQuickItem();
return vec2(targetWindow->width(), targetWindow->height());
} }
void QmlWindowClass::setSize(const glm::vec2& size) { void QmlWindowClass::setSize(const glm::vec2& size) {
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] { if (QThread::currentThread() != thread()) {
if (!_qmlWindow.isNull()) { QMetaObject::invokeMethod(this, "setSize", Q_ARG(vec2, size));
asQuickItem()->setSize(QSizeF(size.x, size.y)); return;
} }
});
if (!_qmlWindow.isNull()) {
asQuickItem()->setSize(QSizeF(size.x, size.y));
}
} }
void QmlWindowClass::setSize(int width, int height) { void QmlWindowClass::setSize(int width, int height) {
@ -292,28 +326,37 @@ void QmlWindowClass::setSize(int width, int height) {
} }
void QmlWindowClass::setTitle(const QString& title) { void QmlWindowClass::setTitle(const QString& title) {
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] { if (QThread::currentThread() != thread()) {
if (!_qmlWindow.isNull()) { QMetaObject::invokeMethod(this, "setTitle", Q_ARG(QString, title));
asQuickItem()->setProperty(TITLE_PROPERTY, title); return;
} }
});
if (!_qmlWindow.isNull()) {
asQuickItem()->setProperty(TITLE_PROPERTY, title);
}
} }
void QmlWindowClass::close() { void QmlWindowClass::close() {
if (_qmlWindow) { if (QThread::currentThread() != thread()) {
if (_toolWindow) { QMetaObject::invokeMethod(this, "close");
auto offscreenUi = DependencyManager::get<OffscreenUi>(); return;
offscreenUi->executeOnUiThread([=] {
auto toolWindow = offscreenUi->getToolWindow();
auto invokeResult = QMetaObject::invokeMethod(toolWindow, "removeTabForUrl", Qt::DirectConnection,
Q_ARG(QVariant, _source));
Q_ASSERT(invokeResult);
});
} else {
_qmlWindow->deleteLater();
}
_qmlWindow = nullptr;
} }
#if QML_TOOL_WINDOW
if (_toolWindow) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
auto toolWindow = offscreenUi->getToolWindow();
auto invokeResult = QMetaObject::invokeMethod(toolWindow, "removeTabForUrl", Qt::DirectConnection,
Q_ARG(QVariant, _source));
Q_ASSERT(invokeResult);
return;
}
#endif
if (_qmlWindow) {
_qmlWindow->deleteLater();
}
_qmlWindow = nullptr;
} }
void QmlWindowClass::hasMoved(QVector2D position) { void QmlWindowClass::hasMoved(QVector2D position) {
@ -325,10 +368,13 @@ void QmlWindowClass::hasClosed() {
} }
void QmlWindowClass::raise() { void QmlWindowClass::raise() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "raise");
return;
}
auto offscreenUi = DependencyManager::get<OffscreenUi>(); auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->executeOnUiThread([=] { if (_qmlWindow) {
if (!_qmlWindow.isNull()) { QMetaObject::invokeMethod(asQuickItem(), "raise", Qt::DirectConnection);
QMetaObject::invokeMethod(asQuickItem(), "raise", Qt::DirectConnection); }
}
});
} }

View file

@ -19,6 +19,8 @@
class QScriptEngine; class QScriptEngine;
class QScriptContext; class QScriptContext;
#define QML_TOOL_WINDOW 0
// FIXME refactor this class to be a QQuickItem derived type and eliminate the needless wrapping // FIXME refactor this class to be a QQuickItem derived type and eliminate the needless wrapping
class QmlWindowClass : public QObject { class QmlWindowClass : public QObject {
Q_OBJECT Q_OBJECT
@ -31,18 +33,18 @@ public:
QmlWindowClass(); QmlWindowClass();
~QmlWindowClass(); ~QmlWindowClass();
virtual void initQml(QVariantMap properties); Q_INVOKABLE virtual void initQml(QVariantMap properties);
QQuickItem* asQuickItem() const; QQuickItem* asQuickItem() const;
public slots: public slots:
bool isVisible() const; bool isVisible();
void setVisible(bool visible); void setVisible(bool visible);
glm::vec2 getPosition() const; glm::vec2 getPosition();
void setPosition(const glm::vec2& position); void setPosition(const glm::vec2& position);
void setPosition(int x, int y); void setPosition(int x, int y);
glm::vec2 getSize() const; glm::vec2 getSize();
void setSize(const glm::vec2& size); void setSize(const glm::vec2& size);
void setSize(int width, int height); void setSize(int width, int height);
void setTitle(const QString& title); void setTitle(const QString& title);
@ -85,9 +87,12 @@ protected:
virtual QString qmlSource() const { return "QmlWindow.qml"; } virtual QString qmlSource() const { return "QmlWindow.qml"; }
#if QML_TOOL_WINDOW
// FIXME needs to be initialized in the ctor once we have support // FIXME needs to be initialized in the ctor once we have support
// for tool window panes in QML // for tool window panes in QML
bool _toolWindow { false }; bool _toolWindow { false };
#endif
QPointer<QObject> _qmlWindow; QPointer<QObject> _qmlWindow;
QString _source; QString _source;

View file

@ -13,12 +13,12 @@
#include <QtWidgets/QShortcut> #include <QtWidgets/QShortcut>
#include <SettingHandle.h> #include <SettingHandle.h>
#include <shared/QtHelpers.h>
#include "../VrMenu.h" #include "../VrMenu.h"
#include "../OffscreenUi.h" #include "../OffscreenUi.h"
#include "Logging.h" #include "Logging.h"
using namespace ui; using namespace ui;
static QList<QString> groups; static QList<QString> groups;
@ -246,7 +246,7 @@ void Menu::removeAction(MenuWrapper* menu, const QString& actionName) {
void Menu::setIsOptionChecked(const QString& menuOption, bool isChecked) { void Menu::setIsOptionChecked(const QString& menuOption, bool isChecked) {
if (thread() != QThread::currentThread()) { if (thread() != QThread::currentThread()) {
QMetaObject::invokeMethod(this, "setIsOptionChecked", Qt::BlockingQueuedConnection, BLOCKING_INVOKE_METHOD(this, "setIsOptionChecked",
Q_ARG(const QString&, menuOption), Q_ARG(const QString&, menuOption),
Q_ARG(bool, isChecked)); Q_ARG(bool, isChecked));
return; return;

View file

@ -25,6 +25,8 @@
#include <QtCore/QWaitCondition> #include <QtCore/QWaitCondition>
#include <shared/NsightHelpers.h> #include <shared/NsightHelpers.h>
#include <shared/GlobalAppProperties.h>
#include <shared/QtHelpers.h>
#include <PerfStat.h> #include <PerfStat.h>
#include <DependencyManager.h> #include <DependencyManager.h>
#include <NumericalConstants.h> #include <NumericalConstants.h>
@ -34,7 +36,6 @@
#include <AccountManager.h> #include <AccountManager.h>
#include <NetworkAccessManager.h> #include <NetworkAccessManager.h>
#include <GLMHelpers.h> #include <GLMHelpers.h>
#include <shared/GlobalAppProperties.h>
#include <gl/OffscreenGLCanvas.h> #include <gl/OffscreenGLCanvas.h>
#include <gl/GLHelpers.h> #include <gl/GLHelpers.h>
@ -886,28 +887,6 @@ QQmlContext* OffscreenQmlSurface::getSurfaceContext() {
return _qmlContext; return _qmlContext;
} }
void OffscreenQmlSurface::executeOnUiThread(std::function<void()> function, bool blocking ) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "executeOnUiThread", blocking ? Qt::BlockingQueuedConnection : Qt::QueuedConnection,
Q_ARG(std::function<void()>, function));
return;
}
function();
}
QVariant OffscreenQmlSurface::returnFromUiThread(std::function<QVariant()> function) {
if (QThread::currentThread() != thread()) {
QVariant result;
QMetaObject::invokeMethod(this, "returnFromUiThread", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QVariant, result),
Q_ARG(std::function<QVariant()>, function));
return result;
}
return function();
}
void OffscreenQmlSurface::focusDestroyed(QObject *obj) { void OffscreenQmlSurface::focusDestroyed(QObject *obj) {
_currentFocusItem = nullptr; _currentFocusItem = nullptr;
} }

View file

@ -55,10 +55,6 @@ public:
return load(QUrl(qmlSourceFile), f); return load(QUrl(qmlSourceFile), f);
} }
void clearCache(); void clearCache();
Q_INVOKABLE void executeOnUiThread(std::function<void()> function, bool blocking = false);
Q_INVOKABLE QVariant returnFromUiThread(std::function<QVariant()> function);
void setMaxFps(uint8_t maxFps) { _maxFps = maxFps; } void setMaxFps(uint8_t maxFps) { _maxFps = maxFps; }
// Optional values for event handling // Optional values for event handling
void setProxyWindow(QWindow* window); void setProxyWindow(QWindow* window);

View file

@ -11,6 +11,8 @@
#include <QtCore/QThread> #include <QtCore/QThread>
#include <QtCore/QCoreApplication> #include <QtCore/QCoreApplication>
#include <shared/QtHelpers.h>
QmlWrapper::QmlWrapper(QObject* qmlObject, QObject* parent) QmlWrapper::QmlWrapper(QObject* qmlObject, QObject* parent)
: QObject(parent), _qmlObject(qmlObject) { : QObject(parent), _qmlObject(qmlObject) {
Q_ASSERT(QThread::currentThread() == qApp->thread()); Q_ASSERT(QThread::currentThread() == qApp->thread());
@ -36,7 +38,7 @@ void QmlWrapper::writeProperties(QVariant propertyMap) {
QVariant QmlWrapper::readProperty(const QString& propertyName) { QVariant QmlWrapper::readProperty(const QString& propertyName) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QVariant result; QVariant result;
QMetaObject::invokeMethod(this, "readProperty", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVariant, result), Q_ARG(QString, propertyName)); BLOCKING_INVOKE_METHOD(this, "readProperty", Q_RETURN_ARG(QVariant, result), Q_ARG(QString, propertyName));
return result; return result;
} }
@ -46,7 +48,7 @@ QVariant QmlWrapper::readProperty(const QString& propertyName) {
QVariant QmlWrapper::readProperties(const QVariant& propertyList) { QVariant QmlWrapper::readProperties(const QVariant& propertyList) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QVariant result; QVariant result;
QMetaObject::invokeMethod(this, "readProperties", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, propertyList)); BLOCKING_INVOKE_METHOD(this, "readProperties", Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, propertyList));
return result; return result;
} }

View file

@ -11,6 +11,7 @@
#include <QtCore/QThread> #include <QtCore/QThread>
#include <QtQml/QQmlProperty> #include <QtQml/QQmlProperty>
#include <shared/QtHelpers.h>
#include <PathUtils.h> #include <PathUtils.h>
#include <DependencyManager.h> #include <DependencyManager.h>
#include <AccountManager.h> #include <AccountManager.h>
@ -42,7 +43,7 @@ ToolbarProxy* TabletScriptingInterface::getSystemToolbarProxy() {
TabletProxy* TabletScriptingInterface::getTablet(const QString& tabletId) { TabletProxy* TabletScriptingInterface::getTablet(const QString& tabletId) {
TabletProxy* tabletProxy = nullptr; TabletProxy* tabletProxy = nullptr;
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "getTablet", Qt::BlockingQueuedConnection, Q_RETURN_ARG(TabletProxy*, tabletProxy), Q_ARG(QString, tabletId)); BLOCKING_INVOKE_METHOD(this, "getTablet", Q_RETURN_ARG(TabletProxy*, tabletProxy), Q_ARG(QString, tabletId));
return tabletProxy; return tabletProxy;
} }
@ -213,20 +214,18 @@ void TabletProxy::setToolbarMode(bool toolbarMode) {
// create new desktop window // create new desktop window
auto offscreenUi = DependencyManager::get<OffscreenUi>(); auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->executeOnUiThread([=] { auto tabletRootWindow = new TabletRootWindow();
auto tabletRootWindow = new TabletRootWindow(); tabletRootWindow->initQml(QVariantMap());
tabletRootWindow->initQml(QVariantMap()); auto quickItem = tabletRootWindow->asQuickItem();
auto quickItem = tabletRootWindow->asQuickItem(); _desktopWindow = tabletRootWindow;
_desktopWindow = tabletRootWindow; QMetaObject::invokeMethod(quickItem, "setShown", Q_ARG(const QVariant&, QVariant(false)));
QMetaObject::invokeMethod(quickItem, "setShown", Q_ARG(const QVariant&, QVariant(false)));
QObject::connect(quickItem, SIGNAL(windowClosed()), this, SLOT(desktopWindowClosed())); QObject::connect(quickItem, SIGNAL(windowClosed()), this, SLOT(desktopWindowClosed()));
QObject::connect(tabletRootWindow, SIGNAL(webEventReceived(QVariant)), this, SLOT(emitWebEvent(QVariant)), Qt::DirectConnection); QObject::connect(tabletRootWindow, SIGNAL(webEventReceived(QVariant)), this, SLOT(emitWebEvent(QVariant)), Qt::DirectConnection);
// forward qml surface events to interface js // forward qml surface events to interface js
connect(tabletRootWindow, &QmlWindowClass::fromQml, this, &TabletProxy::fromQml); connect(tabletRootWindow, &QmlWindowClass::fromQml, this, &TabletProxy::fromQml);
});
} else { } else {
_state = State::Home; _state = State::Home;
removeButtonsFromToolbar(); removeButtonsFromToolbar();
@ -292,7 +291,7 @@ void TabletProxy::initialScreen(const QVariant& url) {
bool TabletProxy::isMessageDialogOpen() { bool TabletProxy::isMessageDialogOpen() {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
bool result = false; bool result = false;
QMetaObject::invokeMethod(this, "isMessageDialogOpen", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result)); BLOCKING_INVOKE_METHOD(this, "isMessageDialogOpen", Q_RETURN_ARG(bool, result));
return result; return result;
} }
@ -317,7 +316,7 @@ void TabletProxy::emitWebEvent(const QVariant& msg) {
bool TabletProxy::isPathLoaded(const QVariant& path) { bool TabletProxy::isPathLoaded(const QVariant& path) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
bool result = false; bool result = false;
QMetaObject::invokeMethod(this, "isPathLoaded", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result), Q_ARG(QVariant, path)); BLOCKING_INVOKE_METHOD(this, "isPathLoaded", Q_RETURN_ARG(bool, result), Q_ARG(QVariant, path));
return result; return result;
} }
@ -480,7 +479,7 @@ void TabletProxy::loadQMLSource(const QVariant& path) {
bool TabletProxy::pushOntoStack(const QVariant& path) { bool TabletProxy::pushOntoStack(const QVariant& path) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
bool result = false; bool result = false;
QMetaObject::invokeMethod(this, "pushOntoStack", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result), Q_ARG(QVariant, path)); BLOCKING_INVOKE_METHOD(this, "pushOntoStack", Q_RETURN_ARG(bool, result), Q_ARG(QVariant, path));
return result; return result;
} }
@ -606,7 +605,7 @@ void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaS
TabletButtonProxy* TabletProxy::addButton(const QVariant& properties) { TabletButtonProxy* TabletProxy::addButton(const QVariant& properties) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
TabletButtonProxy* result = nullptr; TabletButtonProxy* result = nullptr;
QMetaObject::invokeMethod(this, "addButton", Qt::BlockingQueuedConnection, Q_RETURN_ARG(TabletButtonProxy*, result), Q_ARG(QVariant, properties)); BLOCKING_INVOKE_METHOD(this, "addButton", Q_RETURN_ARG(TabletButtonProxy*, result), Q_ARG(QVariant, properties));
return result; return result;
} }
@ -633,7 +632,7 @@ TabletButtonProxy* TabletProxy::addButton(const QVariant& properties) {
bool TabletProxy::onHomeScreen() { bool TabletProxy::onHomeScreen() {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
bool result = false; bool result = false;
QMetaObject::invokeMethod(this, "onHomeScreen", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result)); BLOCKING_INVOKE_METHOD(this, "onHomeScreen", Q_RETURN_ARG(bool, result));
return result; return result;
} }
@ -840,7 +839,7 @@ void TabletButtonProxy::setToolbarButtonProxy(QObject* toolbarButtonProxy) {
QVariantMap TabletButtonProxy::getProperties() { QVariantMap TabletButtonProxy::getProperties() {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QVariantMap result; QVariantMap result;
QMetaObject::invokeMethod(this, "getProperties", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVariantMap, result)); BLOCKING_INVOKE_METHOD(this, "getProperties", Q_RETURN_ARG(QVariantMap, result));
return result; return result;
} }

View file

@ -12,6 +12,8 @@
#include <QtQuick/QQuickItem> #include <QtQuick/QQuickItem>
#include <QtScript/QScriptValue> #include <QtScript/QScriptValue>
#include <QtScript/QScriptEngine> #include <QtScript/QScriptEngine>
#include <shared/QtHelpers.h>
#include "../OffscreenUi.h" #include "../OffscreenUi.h"
QScriptValue toolbarToScriptValue(QScriptEngine* engine, ToolbarProxy* const &in) { QScriptValue toolbarToScriptValue(QScriptEngine* engine, ToolbarProxy* const &in) {
@ -68,7 +70,7 @@ ToolbarProxy::ToolbarProxy(QObject* qmlObject, QObject* parent) : QmlWrapper(qml
ToolbarButtonProxy* ToolbarProxy::addButton(const QVariant& properties) { ToolbarButtonProxy* ToolbarProxy::addButton(const QVariant& properties) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
ToolbarButtonProxy* result = nullptr; ToolbarButtonProxy* result = nullptr;
QMetaObject::invokeMethod(this, "addButton", Qt::BlockingQueuedConnection, Q_RETURN_ARG(ToolbarButtonProxy*, result), Q_ARG(QVariant, properties)); BLOCKING_INVOKE_METHOD(this, "addButton", Q_RETURN_ARG(ToolbarButtonProxy*, result), Q_ARG(QVariant, properties));
return result; return result;
} }
@ -99,18 +101,14 @@ void ToolbarProxy::removeButton(const QVariant& name) {
ToolbarProxy* ToolbarScriptingInterface::getToolbar(const QString& toolbarId) { ToolbarProxy* ToolbarScriptingInterface::getToolbar(const QString& toolbarId) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
ToolbarProxy* result = nullptr; ToolbarProxy* result = nullptr;
QMetaObject::invokeMethod(this, "getToolbar", Qt::BlockingQueuedConnection, Q_RETURN_ARG(ToolbarProxy*, result), Q_ARG(QString, toolbarId)); BLOCKING_INVOKE_METHOD(this, "getToolbar", Q_RETURN_ARG(ToolbarProxy*, result), Q_ARG(QString, toolbarId));
return result; return result;
} }
auto offscreenUi = DependencyManager::get<OffscreenUi>(); auto offscreenUi = DependencyManager::get<OffscreenUi>();
auto desktop = offscreenUi->getDesktop(); auto desktop = offscreenUi->getDesktop();
Qt::ConnectionType connectionType = Qt::AutoConnection;
if (QThread::currentThread() != desktop->thread()) {
connectionType = Qt::BlockingQueuedConnection;
}
QVariant resultVar; QVariant resultVar;
bool invokeResult = QMetaObject::invokeMethod(desktop, "getToolbar", connectionType, Q_RETURN_ARG(QVariant, resultVar), Q_ARG(QVariant, toolbarId)); bool invokeResult = QMetaObject::invokeMethod(desktop, "getToolbar", Q_RETURN_ARG(QVariant, resultVar), Q_ARG(QVariant, toolbarId));
if (!invokeResult) { if (!invokeResult) {
return nullptr; return nullptr;
} }

View file

@ -249,6 +249,7 @@ ViveControllerManager::InputDevice::InputDevice(vr::IVRSystem*& system) : contro
_configStringMap[Config::FeetAndHips] = QString("FeetAndHips"); _configStringMap[Config::FeetAndHips] = QString("FeetAndHips");
_configStringMap[Config::FeetHipsAndChest] = QString("FeetHipsAndChest"); _configStringMap[Config::FeetHipsAndChest] = QString("FeetHipsAndChest");
_configStringMap[Config::FeetHipsAndShoulders] = QString("FeetHipsAndShoulders"); _configStringMap[Config::FeetHipsAndShoulders] = QString("FeetHipsAndShoulders");
_configStringMap[Config::FeetHipsChestAndShoulders] = QString("FeetHipsChestAndShoulders");
} }
void ViveControllerManager::InputDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { void ViveControllerManager::InputDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) {
@ -325,6 +326,7 @@ void ViveControllerManager::InputDevice::calibrateFromUI(const controller::Input
if (_calibrate) { if (_calibrate) {
uncalibrate(); uncalibrate();
calibrate(inputCalibrationData); calibrate(inputCalibrationData);
emitCalibrationStatus();
_calibrate = false; _calibrate = false;
} }
} }
@ -372,29 +374,23 @@ QJsonObject ViveControllerManager::InputDevice::configurationSettings() {
Locker locker(_lock); Locker locker(_lock);
QJsonObject configurationSettings; QJsonObject configurationSettings;
configurationSettings["trackerConfiguration"] = configToString(_preferedConfig); configurationSettings["trackerConfiguration"] = configToString(_preferedConfig);
configurationSettings["HMDHead"] = (_headConfig == HeadConfig::HMD) ? true : false; configurationSettings["HMDHead"] = (_headConfig == HeadConfig::HMD);
configurationSettings["handController"] = (_handConfig == HandConfig::HandController) ? true : false; configurationSettings["handController"] = (_handConfig == HandConfig::HandController);
configurationSettings["puckCount"] = (int)_validTrackedObjects.size();
return configurationSettings; return configurationSettings;
} }
void ViveControllerManager::InputDevice::emitCalibrationStatus(const bool success) { void ViveControllerManager::InputDevice::emitCalibrationStatus() {
auto inputConfiguration = DependencyManager::get<InputConfiguration>(); auto inputConfiguration = DependencyManager::get<InputConfiguration>();
QJsonObject status = QJsonObject(); QJsonObject status = QJsonObject();
status["calibrated"] = _calibrated;
if (_calibrated && success) { status["configuration"] = configToString(_preferedConfig);
status["calibrated"] = _calibrated; status["head_puck"] = (_headConfig == HeadConfig::Puck);
status["configuration"] = configToString(_preferedConfig); status["hand_pucks"] = (_handConfig == HandConfig::Pucks);
} else if (!_calibrated && !success) { status["puckCount"] = (int)_validTrackedObjects.size();
status["calibrated"] = _calibrated; status["UI"] = _calibrate;
status["success"] = success;
} else if (!_calibrated && success) {
status["calibrated"] = _calibrated;
status["success"] = success;
status["configuration"] = configToString(_preferedConfig);
status["puckCount"] = (int)_validTrackedObjects.size();
}
emit inputConfiguration->calibrationStatus(status); //inputConfiguration->calibrated(status); emit inputConfiguration->calibrationStatus(status);
} }
void ViveControllerManager::InputDevice::handleTrackedObject(uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData) { void ViveControllerManager::InputDevice::handleTrackedObject(uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData) {
@ -437,12 +433,29 @@ void ViveControllerManager::InputDevice::handleTrackedObject(uint32_t deviceInde
} }
} }
void ViveControllerManager::InputDevice::sendUserActivityData(QString activity) {
QJsonObject jsonData = {
{"num_pucks", (int)_validTrackedObjects.size()},
{"configuration", configToString(_preferedConfig)},
{"head_puck", (_headConfig == HeadConfig::Puck) ? true : false},
{"hand_pucks", (_handConfig == HandConfig::Pucks) ? true : false}
};
UserActivityLogger::getInstance().logAction(activity, jsonData);
}
void ViveControllerManager::InputDevice::calibrateOrUncalibrate(const controller::InputCalibrationData& inputCalibration) { void ViveControllerManager::InputDevice::calibrateOrUncalibrate(const controller::InputCalibrationData& inputCalibration) {
if (!_calibrated) { if (!_calibrated) {
calibrate(inputCalibration); calibrate(inputCalibration);
if (_calibrated) {
sendUserActivityData("mocap_button_success");
} else {
sendUserActivityData("mocap_button_fail");
}
emitCalibrationStatus();
} else { } else {
uncalibrate(); uncalibrate();
emitCalibrationStatus(true); sendUserActivityData("mocap_button_uncalibrate");
} }
} }
@ -454,7 +467,6 @@ void ViveControllerManager::InputDevice::calibrate(const controller::InputCalibr
if (puckCount == 0) { if (puckCount == 0) {
uncalibrate(); uncalibrate();
emitCalibrationStatus(false);
return; return;
} }
@ -473,10 +485,8 @@ void ViveControllerManager::InputDevice::calibrate(const controller::InputCalibr
if (!headConfigured || !handsConfigured || !bodyConfigured) { if (!headConfigured || !handsConfigured || !bodyConfigured) {
uncalibrate(); uncalibrate();
emitCalibrationStatus(false);
} else { } else {
_calibrated = true; _calibrated = true;
emitCalibrationStatus(true);
qDebug() << "PuckCalibration: " << configToString(_config) << " Configuration Successful"; qDebug() << "PuckCalibration: " << configToString(_config) << " Configuration Successful";
} }
} }
@ -566,8 +576,6 @@ bool ViveControllerManager::InputDevice::configureBody(glm::mat4& defaultToRefer
return true; return true;
} }
qDebug() << "Puck Calibration: " << configToString(_config) << " Config Failed: Could not meet the minimal # of pucks"; qDebug() << "Puck Calibration: " << configToString(_config) << " Config Failed: Could not meet the minimal # of pucks";
uncalibrate();
emitCalibrationStatus(false);
return false; return false;
} }

View file

@ -73,6 +73,7 @@ private:
void calibrateOrUncalibrate(const controller::InputCalibrationData& inputCalibration); void calibrateOrUncalibrate(const controller::InputCalibrationData& inputCalibration);
void calibrate(const controller::InputCalibrationData& inputCalibration); void calibrate(const controller::InputCalibrationData& inputCalibration);
void uncalibrate(); void uncalibrate();
void sendUserActivityData(QString activity);
void configureCalibrationSettings(const QJsonObject configurationSettings); void configureCalibrationSettings(const QJsonObject configurationSettings);
QJsonObject configurationSettings(); QJsonObject configurationSettings();
controller::Pose addOffsetToPuckPose(int joint) const; controller::Pose addOffsetToPuckPose(int joint) const;
@ -106,7 +107,7 @@ private:
void calibrateHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); void calibrateHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration);
void calibrateFromHandController(const controller::InputCalibrationData& inputCalibrationData); void calibrateFromHandController(const controller::InputCalibrationData& inputCalibrationData);
void calibrateFromUI(const controller::InputCalibrationData& inputCalibrationData); void calibrateFromUI(const controller::InputCalibrationData& inputCalibrationData);
void emitCalibrationStatus(const bool success); void emitCalibrationStatus();
void calibrateNextFrame(); void calibrateNextFrame();
@ -139,7 +140,7 @@ private:
FeetAndHips, FeetAndHips,
FeetHipsAndChest, FeetHipsAndChest,
FeetHipsAndShoulders, FeetHipsAndShoulders,
FeetHipsChestAndShoulders, FeetHipsChestAndShoulders
}; };
enum class HeadConfig { enum class HeadConfig {

View file

@ -20,7 +20,9 @@ var blastShareText = "Blast to my Connections",
hifiShareText = "Share to Snaps Feed", hifiShareText = "Share to Snaps Feed",
hifiAlreadySharedText = "Already Shared to Snaps Feed", hifiAlreadySharedText = "Already Shared to Snaps Feed",
facebookShareText = "Share to Facebook", facebookShareText = "Share to Facebook",
twitterShareText = "Share to Twitter"; twitterShareText = "Share to Twitter",
shareButtonLabelTextActive = "SHARE&#58;",
shareButtonLabelTextInactive = "SHARE";
function fileExtensionMatches(filePath, extension) { function fileExtensionMatches(filePath, extension) {
return filePath.split('.').pop().toLowerCase() === extension; return filePath.split('.').pop().toLowerCase() === extension;
@ -138,6 +140,8 @@ function selectImageToShare(selectedID, isSelected) {
var imageContainer = document.getElementById(selectedID), var imageContainer = document.getElementById(selectedID),
image = document.getElementById(selectedID + 'img'), image = document.getElementById(selectedID + 'img'),
shareBar = document.getElementById(selectedID + "shareBar"), shareBar = document.getElementById(selectedID + "shareBar"),
showShareButtonsDots = document.getElementById(selectedID + "showShareButtonsDots"),
showShareButtonsLabel = document.getElementById(selectedID + "showShareButtonsLabel"),
shareButtonsDiv = document.getElementById(selectedID + "shareButtonsDiv"), shareButtonsDiv = document.getElementById(selectedID + "shareButtonsDiv"),
shareBarHelp = document.getElementById(selectedID + "shareBarHelp"), shareBarHelp = document.getElementById(selectedID + "shareBarHelp"),
showShareButtonsButtonDiv = document.getElementById(selectedID + "showShareButtonsButtonDiv"), showShareButtonsButtonDiv = document.getElementById(selectedID + "showShareButtonsButtonDiv"),
@ -156,6 +160,9 @@ function selectImageToShare(selectedID, isSelected) {
shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.45)"; shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.45)";
shareBar.style.pointerEvents = "initial"; shareBar.style.pointerEvents = "initial";
showShareButtonsDots.style.visibility = "hidden";
showShareButtonsLabel.innerHTML = shareButtonLabelTextActive;
shareButtonsDiv.style.visibility = "visible"; shareButtonsDiv.style.visibility = "visible";
shareBarHelp.style.visibility = "visible"; shareBarHelp.style.visibility = "visible";
@ -176,6 +183,9 @@ function selectImageToShare(selectedID, isSelected) {
shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.0)"; shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.0)";
shareBar.style.pointerEvents = "none"; shareBar.style.pointerEvents = "none";
showShareButtonsDots.style.visibility = "visible";
showShareButtonsLabel.innerHTML = shareButtonLabelTextInactive;
shareButtonsDiv.style.visibility = "hidden"; shareButtonsDiv.style.visibility = "hidden";
shareBarHelp.style.visibility = "hidden"; shareBarHelp.style.visibility = "hidden";
} }
@ -185,6 +195,7 @@ function createShareBar(parentID, isLoggedIn, canShare, isGif, blastButtonDisabl
shareBarHelpID = parentID + "shareBarHelp", shareBarHelpID = parentID + "shareBarHelp",
shareButtonsDivID = parentID + "shareButtonsDiv", shareButtonsDivID = parentID + "shareButtonsDiv",
showShareButtonsButtonDivID = parentID + "showShareButtonsButtonDiv", showShareButtonsButtonDivID = parentID + "showShareButtonsButtonDiv",
showShareButtonsDotsID = parentID + "showShareButtonsDots",
showShareButtonsLabelID = parentID + "showShareButtonsLabel", showShareButtonsLabelID = parentID + "showShareButtonsLabel",
blastToConnectionsButtonID = parentID + "blastToConnectionsButton", blastToConnectionsButtonID = parentID + "blastToConnectionsButton",
shareWithEveryoneButtonID = parentID + "shareWithEveryoneButton", shareWithEveryoneButtonID = parentID + "shareWithEveryoneButton",
@ -199,8 +210,8 @@ function createShareBar(parentID, isLoggedIn, canShare, isGif, blastButtonDisabl
if (canShare) { if (canShare) {
shareBarInnerHTML = '<div class="shareControlsHelp" id="' + shareBarHelpID + '" style="visibility:hidden;' + ((canBlast && blastButtonDisabled || !canBlast && hifiButtonDisabled) ? "background-color:#000;opacity:0.5;" : "") + '"></div>' + shareBarInnerHTML = '<div class="shareControlsHelp" id="' + shareBarHelpID + '" style="visibility:hidden;' + ((canBlast && blastButtonDisabled || !canBlast && hifiButtonDisabled) ? "background-color:#000;opacity:0.5;" : "") + '"></div>' +
'<div class="showShareButtonsButtonDiv inactive" id="' + showShareButtonsButtonDivID + '" onclick="selectImageToShare(' + parentID + ', true)">' + '<div class="showShareButtonsButtonDiv inactive" id="' + showShareButtonsButtonDivID + '" onclick="selectImageToShare(' + parentID + ', true)">' +
'<label id="' + showShareButtonsLabelID + '">SHARE</label>' + '<label id="' + showShareButtonsLabelID + '">' + shareButtonLabelTextInactive + '</label>' +
'<span class="showShareButtonDots">' + '<span id="' + showShareButtonsDotsID + '" class="showShareButtonDots">' +
'&#xe019;' + '&#xe019;' +
'</div>' + '</div>' +
'</div>' + '</div>' +
@ -217,7 +228,7 @@ function createShareBar(parentID, isLoggedIn, canShare, isGif, blastButtonDisabl
document.getElementById(parentID + 'img').onclick = function () { selectImageToShare(parentID, true); }; document.getElementById(parentID + 'img').onclick = function () { selectImageToShare(parentID, true); };
} else { } else {
shareBarInnerHTML = '<div class="showShareButtonsButtonDiv inactive" id="' + showShareButtonsButtonDivID + '" onclick="selectImageToShare(' + parentID + ', true)">' + shareBarInnerHTML = '<div class="showShareButtonsButtonDiv inactive" id="' + showShareButtonsButtonDivID + '" onclick="selectImageToShare(' + parentID + ', true)">' +
'<label id="' + showShareButtonsLabelID + '">SHARE</label>' + '<label id="' + showShareButtonsLabelID + '">' + shareButtonLabelTextInactive + '</label>' +
'<span class="showShareButtonDots">' + '<span class="showShareButtonDots">' +
'&#xe019;' + '&#xe019;' +
'</div>' + '</div>' +
@ -230,7 +241,7 @@ function createShareBar(parentID, isLoggedIn, canShare, isGif, blastButtonDisabl
} }
} else { } else {
shareBarInnerHTML = '<div class="showShareButtonsButtonDiv inactive" id="' + showShareButtonsButtonDivID + '" onclick="selectImageToShare(' + parentID + ', true)">' + shareBarInnerHTML = '<div class="showShareButtonsButtonDiv inactive" id="' + showShareButtonsButtonDivID + '" onclick="selectImageToShare(' + parentID + ', true)">' +
'<label id="' + showShareButtonsLabelID + '">SHARE</label>' + '<label id="' + showShareButtonsLabelID + '">' + shareButtonLabelTextInactive + '</label>' +
'<span class="showShareButtonDots">' + '<span class="showShareButtonDots">' +
'&#xe019;' + '&#xe019;' +
'</div>' + '</div>' +

View file

@ -334,6 +334,7 @@ function startInterface(url) {
// create a new Interface instance - Interface makes sure only one is running at a time // create a new Interface instance - Interface makes sure only one is running at a time
var pInterface = new Process('interface', interfacePath, argArray); var pInterface = new Process('interface', interfacePath, argArray);
pInterface.detached = true;
pInterface.start(); pInterface.start();
} }
@ -892,10 +893,19 @@ function onContentLoaded() {
deleteOldFiles(logPath, DELETE_LOG_FILES_OLDER_THAN_X_SECONDS, LOG_FILE_REGEX); deleteOldFiles(logPath, DELETE_LOG_FILES_OLDER_THAN_X_SECONDS, LOG_FILE_REGEX);
if (dsPath && acPath) { if (dsPath && acPath) {
domainServer = new Process('domain-server', dsPath, ["--get-temp-name"], logPath); var dsArguments = ['--get-temp-name',
acMonitor = new ACMonitorProcess('ac-monitor', acPath, ['-n7', '--parent-pid', process.pid];
'--log-directory', logPath, domainServer = new Process('domain-server', dsPath, dsArguments, logPath);
'--http-status-port', httpStatusPort], httpStatusPort, logPath); domainServer.restartOnCrash = true;
var acArguments = ['-n7',
'--log-directory', logPath,
'--http-status-port', httpStatusPort,
'--parent-pid', process.pid];
acMonitor = new ACMonitorProcess('ac-monitor', acPath, acArguments,
httpStatusPort, logPath);
acMonitor.restartOnCrash = true;
homeServer = new ProcessGroup('home', [domainServer, acMonitor]); homeServer = new ProcessGroup('home', [domainServer, acMonitor]);
logWindow = new LogWindow(acMonitor, domainServer); logWindow = new LogWindow(acMonitor, domainServer);

View file

@ -113,6 +113,10 @@ function Process(name, command, commandArgs, logDirectory) {
this.logDirectory = logDirectory; this.logDirectory = logDirectory;
this.logStdout = null; this.logStdout = null;
this.logStderr = null; this.logStderr = null;
this.detached = false;
this.restartOnCrash = false;
this.restartCount = 0;
this.firstRestartTimestamp = Date.now();
this.state = ProcessStates.STOPPED; this.state = ProcessStates.STOPPED;
}; };
@ -165,9 +169,10 @@ Process.prototype = extend(Process.prototype, {
try { try {
this.child = childProcess.spawn(this.command, this.commandArgs, { this.child = childProcess.spawn(this.command, this.commandArgs, {
detached: false, detached: this.detached,
stdio: ['ignore', logStdout, logStderr] stdio: ['ignore', logStdout, logStderr]
}); });
log.debug("Spawned " + this.command + " with pid " + this.child.pid);
} catch (e) { } catch (e) {
log.debug("Got error starting child process for " + this.name, e); log.debug("Got error starting child process for " + this.name, e);
this.child = null; this.child = null;
@ -266,7 +271,30 @@ Process.prototype = extend(Process.prototype, {
clearTimeout(this.stoppingTimeoutID); clearTimeout(this.stoppingTimeoutID);
this.stoppingTimeoutID = null; this.stoppingTimeoutID = null;
} }
// Grab current state before updating it.
var unexpectedShutdown = this.state != ProcessStates.STOPPING;
this.updateState(ProcessStates.STOPPED); this.updateState(ProcessStates.STOPPED);
if (unexpectedShutdown && this.restartOnCrash) {
var MAX_RESTARTS = 10;
var MAX_RESTARTS_PERIOD = 10; // 10 min
var MSEC_PER_MIN = 1000 * 60;
var now = Date.now();
var timeDiff = (now - this.firstRestartTimestamp) / MSEC_PER_MIN;
if (timeDiff > MAX_RESTARTS_PERIOD) {
this.firstRestartTimestamp = now;
this.restartCount = 0;
}
if (this.restartCount < 10) {
this.restartCount++;
log.warn("Child stopped unexpectedly, restarting.");
this.start();
} else {
log.warn("Child stopped unexpectedly too many times, not restarting.");
}
}
} }
}); });

View file

@ -90,14 +90,16 @@ ACClientApp::ACClientApp(int argc, char* argv[]) :
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
// start the nodeThread so its event loop is running
nodeList->startThread();
// setup a timer for domain-server check ins // setup a timer for domain-server check ins
QTimer* domainCheckInTimer = new QTimer(nodeList.data()); QTimer* domainCheckInTimer = new QTimer(nodeList.data());
connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn); connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn);
domainCheckInTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS); domainCheckInTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS);
// start the nodeThread so its event loop is running
// (must happen after the checkin timer is created with the nodelist as it's parent)
nodeList->startThread();
const DomainHandler& domainHandler = nodeList->getDomainHandler(); const DomainHandler& domainHandler = nodeList->getDomainHandler();
connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&))); connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));

View file

@ -61,7 +61,9 @@ Oven::Oven(int argc, char* argv[]) :
if (parser.isSet(CLI_INPUT_PARAMETER) || parser.isSet(CLI_OUTPUT_PARAMETER)) { if (parser.isSet(CLI_INPUT_PARAMETER) || parser.isSet(CLI_OUTPUT_PARAMETER)) {
if (parser.isSet(CLI_INPUT_PARAMETER) && parser.isSet(CLI_OUTPUT_PARAMETER)) { if (parser.isSet(CLI_INPUT_PARAMETER) && parser.isSet(CLI_OUTPUT_PARAMETER)) {
BakerCLI* cli = new BakerCLI(this); BakerCLI* cli = new BakerCLI(this);
cli->bakeFile(parser.value(CLI_INPUT_PARAMETER), parser.value(CLI_OUTPUT_PARAMETER)); QUrl inputUrl(QDir::fromNativeSeparators(parser.value(CLI_INPUT_PARAMETER)));
QUrl outputUrl(QDir::fromNativeSeparators(parser.value(CLI_OUTPUT_PARAMETER)));
cli->bakeFile(inputUrl, outputUrl.toString());
} else { } else {
parser.showHelp(); parser.showHelp();
QApplication::quit(); QApplication::quit();

View file

@ -0,0 +1,59 @@
"use strict";
/*
rockethands.js
unpublishedScripts/marketplace/rocketHands/rockethands.js
Created by Cain Kilgore on 30/06/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
*/
(function() {
var isRocketing = false;
function checkRocketing() {
if (HMD.active && (Controller.Hardware.Vive || Controller.Hardware.OculusTouch) && canRocket()) {
isRocketing = true;
MyAvatar.motorReferenceFrame = "world";
var moveVector = Vec3.multiply(Quat.getFront(Camera.getOrientation()), 10);
if (!MyAvatar.isFlying()) {
moveVector = Vec3.sum(moveVector, {x: 0, y: 1, z: 0});
}
MyAvatar.motorVelocity = moveVector;
MyAvatar.motorTimescale = 1.0;
} else {
checkCanStopRocketing();
}
};
function checkCanStopRocketing() {
if (isRocketing) {
MyAvatar.motorVelocity = 0;
isRocketing = false;
}
}
function canRocket() {
var leftHand = Controller.getPoseValue(Controller.Standard.LeftHand);
var rightHand = Controller.getPoseValue(Controller.Standard.RightHand);
var leftWorldControllerPos = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, leftHand.translation));
var rightWorldControllerPos = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, rightHand.translation));
var hipPosition = MyAvatar.getJointPosition("Hips");
var controllerHipThreshold = 0.1; // In Meters. Experimentally determined. Used to figure out if user's hands are "close enough" to their hips.
var controllerRotationThreshold = 0.25; // In Radians. Experimentally determined. Used to figure out if user's hands are within a rotation threshold.
return ((leftWorldControllerPos.y > (hipPosition.y - controllerHipThreshold)) &&
(leftWorldControllerPos.y < (hipPosition.y + controllerHipThreshold)) &&
(rightWorldControllerPos.y > (hipPosition.y - controllerHipThreshold)) &&
(rightWorldControllerPos.y < (hipPosition.y + controllerHipThreshold)) &&
leftHand.rotation.y < controllerRotationThreshold &&
leftHand.rotation.y > -controllerRotationThreshold &&
rightHand.rotation.y < controllerRotationThreshold &&
rightHand.rotation.y > -controllerRotationThreshold);
}
Script.update.connect(checkRocketing);
}());

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,34 @@
//
// pUtils.js
//
// Created by Patrick Gosch on 03/28/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
//
getEntityTextures = function(id) {
var results = null;
var properties = Entities.getEntityProperties(id, "textures");
if (properties.textures) {
try {
results = JSON.parse(properties.textures);
} catch (err) {
logDebug(err);
logDebug(properties.textures);
}
}
return results ? results : {};
};
setEntityTextures = function(id, textureList) {
var json = JSON.stringify(textureList);
Entities.editEntity(id, {textures: json});
};
editEntityTextures = function(id, textureName, textureURL) {
var textureList = getEntityTextures(id);
textureList[textureName] = textureURL;
setEntityTextures(id, textureList);
};

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

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