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 <QTimer>
#include <shared/QtHelpers.h>
#include <AccountManager.h>
#include <AddressManager.h>
#include <Assignment.h>
@ -141,7 +142,7 @@ void AssignmentClient::stopAssignmentClient() {
QThread* currentAssignmentThread = _currentAssignment->thread();
// 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
_currentAssignment->deleteLater();

View file

@ -9,8 +9,12 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QCommandLineParser>
#include <QThread>
#include "AssignmentClientApp.h"
#include <QtCore/QCommandLineParser>
#include <QtCore/QDir>
#include <QtCore/QStandardPaths>
#include <QtCore/QThread>
#include <LogHandler.h>
#include <SharedUtil.h>
@ -20,10 +24,6 @@
#include "Assignment.h"
#include "AssignmentClient.h"
#include "AssignmentClientMonitor.h"
#include "AssignmentClientApp.h"
#include <QtCore/QDir>
#include <QtCore/QStandardPaths>
AssignmentClientApp::AssignmentClientApp(int argc, char* 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");
parser.addOption(logDirectoryOption);
const QCommandLineOption parentPIDOption(PARENT_PID_OPTION, "PID of the parent process", "parent-pid");
parser.addOption(parentPIDOption);
if (!parser.parse(QCoreApplication::arguments())) {
qCritical() << parser.errorText() << endl;
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");
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();

View file

@ -131,7 +131,6 @@ void AssignmentClientMonitor::aboutToQuit() {
void AssignmentClientMonitor::spawnChildClient() {
QProcess* assignmentClient = new QProcess(this);
// unparse the parts of the command-line that the child cares about
QStringList _childArguments;
if (_assignmentPool != "") {
@ -160,6 +159,9 @@ void AssignmentClientMonitor::spawnChildClient() {
_childArguments.append("--" + ASSIGNMENT_CLIENT_MONITOR_PORT_OPTION);
_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;

View file

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

View file

@ -56,19 +56,17 @@ elseif (${CMAKE_CXX_COMPILER_ID} MATCHES "GNU")
endif()
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)
endif()
if (MSVC11)
elseif (MSVC_VERSION EQUAL 1700)
set(VS_PREFIX vs2012)
endif()
if (MSVC10)
elseif (MSVC_VERSION EQUAL 1600)
set(VS_PREFIX vs2010)
endif()
if (MSVC90)
elseif (MSVC_VERSION EQUAL 1500)
set(VS_PREFIX vs2008)
endif()

View file

@ -221,6 +221,8 @@ void DomainServer::parseCommandLine() {
const QCommandLineOption masterConfigOption("master-config", "Deprecated config-file option");
parser.addOption(masterConfigOption);
const QCommandLineOption parentPIDOption(PARENT_PID_OPTION, "PID of the parent process", "parent-pid");
parser.addOption(parentPIDOption);
if (!parser.parse(QCoreApplication::arguments())) {
qWarning() << parser.errorText() << endl;
@ -249,6 +251,17 @@ void DomainServer::parseCommandLine() {
_overrideDomainID = true;
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() {

View file

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

View file

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

View file

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

View file

@ -48,6 +48,7 @@
#include <gl/QOpenGLContextWrapper.h>
#include <shared/QtHelpers.h>
#include <shared/GlobalAppProperties.h>
#include <StatTracker.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
updateHeartbeat();
int SAVE_SETTINGS_INTERVAL = 10 * MSECS_PER_SECOND; // Let's save every seconds for now
connect(&_settingsTimer, &QTimer::timeout, this, &Application::saveSettings);
connect(&_settingsThread, SIGNAL(started()), &_settingsTimer, SLOT(start()));
connect(&_settingsThread, SIGNAL(finished()), &_settingsTimer, SLOT(stop()));
_settingsTimer.moveToThread(&_settingsThread);
_settingsTimer.setSingleShot(false);
_settingsTimer.setInterval(SAVE_SETTINGS_INTERVAL); // 10s, Qt::CoarseTimer acceptable
_settingsThread.setPriority(QThread::LowestPriority);
_settingsThread.start();
QTimer* settingsTimer = new QTimer();
moveToNewNamedThread(settingsTimer, "Settings Thread", [this, settingsTimer]{
connect(qApp, &Application::beforeAboutToQuit, [this, settingsTimer]{
// Disconnect the signal from the save settings
QObject::disconnect(settingsTimer, &QTimer::timeout, this, &Application::saveSettings);
// Stop the settings timer
settingsTimer->stop();
// 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)) {
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()) {
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;
}
@ -1801,11 +1813,13 @@ void Application::cleanupBeforeQuit() {
locationUpdateTimer.stop();
identityPacketTimer.stop();
pingTimer.stop();
QMetaObject::invokeMethod(&_settingsTimer, "stop", Qt::BlockingQueuedConnection);
// save state
_settingsThread.quit();
saveSettings();
// Wait for the settings thread to shut down, and save the settings one last time when it's safe
if (_settingsGuard.wait()) {
// save state
saveSettings();
}
_window->saveGeometry();
// 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,
// so it must be explicitly synchronously stopped here
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(),
"cleanupBeforeQuit", Qt::BlockingQueuedConnection);
DependencyManager::get<AudioClient>()->cleanupBeforeQuit();
// 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

View file

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

View file

@ -25,6 +25,7 @@
#endif
#include <shared/QtHelpers.h>
#include <AvatarData.h>
#include <PerfStat.h>
#include <RegisteredMetaTypes.h>
@ -482,7 +483,7 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersection(const PickRay&
const QScriptValue& avatarIdsToDiscard) {
RayToAvatarIntersectionResult result;
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_ARG(const PickRay&, ray),
Q_ARG(const QScriptValue&, avatarIdsToInclude),

View file

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

View file

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

View file

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

View file

@ -11,6 +11,8 @@
#include <map>
#include <shared/QtHelpers.h>
#include "AudioDevices.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 success = false;
auto& device = _devices[row];
_userSelection = fromUser;
// skip if already selected
if (!device.selected) {
auto client = DependencyManager::get<AudioClient>();
QMetaObject::invokeMethod(client.data(), "switchAudioDevice", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, success),
QMetaObject::invokeMethod(client.data(), "switchAudioDevice",
Q_ARG(QAudio::Mode, _mode),
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));
@ -135,12 +129,12 @@ void AudioDeviceList::resetDevice(bool contextIsHMD, const QString& device) {
}
void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device) {
auto oldDevice = _selectedDevice;
_selectedDevice = device;
QModelIndex index;
for (auto i = 0; i < _devices.size(); ++i) {
AudioDevice& device = _devices[i];
if (device.selected && device.info != _selectedDevice) {
device.selected = false;
} 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 dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0));
}

View file

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

View file

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

View file

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

View file

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

View file

@ -51,11 +51,11 @@ public:
Q_INVOKABLE void requestHideHandControllers();
Q_INVOKABLE bool shouldShowHandControllers() const;
Q_INVOKABLE bool setHandLasers(int hands, bool enabled, const glm::vec4& color, const glm::vec3& direction) const;
Q_INVOKABLE void disableHandLasers(int hands) const;
Q_INVOKABLE bool setHandLasers(int hands, bool enabled, const glm::vec4& color, const glm::vec3& direction);
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 void disableExtraLaser() const;
Q_INVOKABLE bool setExtraLaser(const glm::vec3& worldStart, bool enabled, const glm::vec4& color, const glm::vec3& direction);
Q_INVOKABLE void disableExtraLaser();
/// 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/QThread>
#include <shared/QtHelpers.h>
#include <MenuItemProperties.h>
#include "Menu.h"
@ -43,7 +44,7 @@ bool MenuScriptingInterface::menuExists(const QString& menu) {
return Menu::getInstance()->menuExists(menu);
}
bool result;
QMetaObject::invokeMethod(Menu::getInstance(), "menuExists", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(Menu::getInstance(), "menuExists",
Q_RETURN_ARG(bool, result),
Q_ARG(const QString&, menu));
return result;
@ -86,7 +87,7 @@ bool MenuScriptingInterface::menuItemExists(const QString& menu, const QString&
return Menu::getInstance()->menuItemExists(menu, menuitem);
}
bool result;
QMetaObject::invokeMethod(Menu::getInstance(), "menuItemExists", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(Menu::getInstance(), "menuItemExists",
Q_RETURN_ARG(bool, result),
Q_ARG(const QString&, menu),
Q_ARG(const QString&, menuitem));
@ -114,7 +115,7 @@ bool MenuScriptingInterface::isOptionChecked(const QString& menuOption) {
return Menu::getInstance()->isOptionChecked(menuOption);
}
bool result;
QMetaObject::invokeMethod(Menu::getInstance(), "isOptionChecked", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(Menu::getInstance(), "isOptionChecked",
Q_RETURN_ARG(bool, result),
Q_ARG(const QString&, menuOption));
return result;
@ -131,7 +132,7 @@ bool MenuScriptingInterface::isMenuEnabled(const QString& menuOption) {
return Menu::getInstance()->isOptionChecked(menuOption);
}
bool result;
QMetaObject::invokeMethod(Menu::getInstance(), "isMenuEnabled", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(Menu::getInstance(), "isMenuEnabled",
Q_RETURN_ARG(bool, result),
Q_ARG(const QString&, menuOption));
return result;
@ -157,7 +158,7 @@ bool MenuScriptingInterface::isInfoViewVisible(const QString& path) {
}
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));
return result;
}

View file

@ -11,6 +11,7 @@
#include <QtCore/QLoggingCategory>
#include <QtCore/QThread>
#include <shared/QtHelpers.h>
#include <DependencyManager.h>
#include <Trace.h>
#include <StatTracker.h>
@ -57,20 +58,25 @@ void TestScriptingInterface::waitIdle() {
}
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_BINARY_ROOT = "https://hifi-public.s3.amazonaws.com/test_scene_data/";
static const QString TEST_SCRIPTS_ROOT = TEST_ROOT + "scripts/";
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/");
auto tree = qApp->getEntities()->getTree();
auto treeIsClient = tree->getIsClient();
// Force the tree to accept the load regardless of permissions
tree->setIsClient(false);
auto result = tree->readFromURL(TEST_SCENES_ROOT + scene + ".json");
tree->setIsClient(treeIsClient);
return result;
}).toBool();
DependencyManager::get<ResourceManager>()->setUrlPrefixOverride("atp:/", TEST_BINARY_ROOT + scene + ".atp/");
auto tree = qApp->getEntities()->getTree();
auto treeIsClient = tree->getIsClient();
// Force the tree to accept the load regardless of permissions
tree->setIsClient(false);
auto result = tree->readFromURL(TEST_SCENES_ROOT + scene + ".json");
tree->setIsClient(treeIsClient);
return result;
}
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
//
#include "WindowScriptingInterface.h"
#include <QClipboard>
#include <QtCore/QDir>
#include <QMessageBox>
#include <QScriptValue>
#include <shared/QtHelpers.h>
#include <SettingHandle.h>
#include <display-plugins/CompositorHelper.h>
@ -24,8 +27,6 @@
#include "Menu.h"
#include "OffscreenUi.h"
#include "WindowScriptingInterface.h"
static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
static const QString LAST_BROWSE_LOCATION_SETTING = "LastBrowseLocation";
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) {
if (QThread::currentThread() != thread()) {
int result;
QMetaObject::invokeMethod(this, "openMessageBox", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "openMessageBox",
Q_RETURN_ARG(int, result),
Q_ARG(QString, title),
Q_ARG(QString, text),

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -14,6 +14,7 @@
#include <QtScript/QScriptValueIterator>
#include <shared/QtHelpers.h>
#include <OffscreenUi.h>
#include <render/Scene.h>
#include <RegisteredMetaTypes.h>
@ -39,34 +40,39 @@
Q_LOGGING_CATEGORY(trace_render_overlays, "trace.render.overlays")
void Overlays::cleanupAllOverlays() {
QMap<OverlayID, Overlay::Pointer> overlaysHUD;
QMap<OverlayID, Overlay::Pointer> overlaysWorld;
{
QWriteLocker lock(&_lock);
QWriteLocker deleteLock(&_deleteLock);
foreach(Overlay::Pointer overlay, _overlaysHUD) {
_overlaysToDelete.push_back(overlay);
}
foreach(Overlay::Pointer overlay, _overlaysWorld) {
_overlaysToDelete.push_back(overlay);
}
_overlaysHUD.clear();
_overlaysWorld.clear();
_panels.clear();
QMutexLocker locker(&_mutex);
overlaysHUD.swap(_overlaysHUD);
overlaysWorld.swap(_overlaysWorld);
}
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();
}
void Overlays::init() {
#if OVERLAY_PANELS
_scriptEngine = new QScriptEngine();
#endif
}
void Overlays::update(float deltatime) {
{
QWriteLocker lock(&_lock);
foreach(Overlay::Pointer thisOverlay, _overlaysHUD) {
QMutexLocker locker(&_mutex);
foreach(const auto& thisOverlay, _overlaysHUD) {
thisOverlay->update(deltatime);
}
foreach(Overlay::Pointer thisOverlay, _overlaysWorld) {
foreach(const auto& thisOverlay, _overlaysWorld) {
thisOverlay->update(deltatime);
}
}
@ -80,8 +86,6 @@ void Overlays::cleanupOverlaysToDelete() {
render::Transaction transaction;
{
QWriteLocker lock(&_deleteLock);
do {
Overlay::Pointer overlay = _overlaysToDelete.takeLast();
@ -100,7 +104,6 @@ void Overlays::cleanupOverlaysToDelete() {
void Overlays::renderHUD(RenderArgs* renderArgs) {
PROFILE_RANGE(render_overlays, __FUNCTION__);
QReadLocker lock(&_lock);
gpu::Batch& batch = *renderArgs->_batch;
auto geometryCache = DependencyManager::get<GeometryCache>();
@ -111,7 +114,7 @@ void Overlays::renderHUD(RenderArgs* renderArgs) {
int height = size.y;
mat4 legacyProjection = glm::ortho<float>(0, width, height, 0, -1000, 1000);
QMutexLocker locker(&_mutex);
foreach(Overlay::Pointer thisOverlay, _overlaysHUD) {
// Reset all batch pipeline settings between overlay
@ -126,16 +129,17 @@ void Overlays::renderHUD(RenderArgs* renderArgs) {
}
void Overlays::disable() {
QWriteLocker lock(&_lock);
_enabled = false;
}
void Overlays::enable() {
QWriteLocker lock(&_lock);
_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 {
QMutexLocker locker(&_mutex);
if (_overlaysHUD.contains(id)) {
return _overlaysHUD[id];
}
@ -146,6 +150,12 @@ Overlay::Pointer Overlays::getOverlay(OverlayID id) const {
}
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;
if (type == ImageOverlay::TYPE) {
@ -185,19 +195,22 @@ OverlayID Overlays::addOverlay(const QString& type, const QVariant& properties)
return UNKNOWN_OVERLAY_ID;
}
OverlayID Overlays::addOverlay(Overlay::Pointer overlay) {
QWriteLocker lock(&_lock);
OverlayID Overlays::addOverlay(const Overlay::Pointer& overlay) {
OverlayID thisID = OverlayID(QUuid::createUuid());
overlay->setOverlayID(thisID);
overlay->setStackOrder(_stackOrder++);
if (overlay->is3D()) {
_overlaysWorld[thisID] = overlay;
{
QMutexLocker locker(&_mutex);
_overlaysWorld[thisID] = overlay;
}
render::ScenePointer scene = qApp->getMain3DScene();
render::Transaction transaction;
overlay->addToScene(overlay, scene, transaction);
scene->enqueueTransaction(transaction);
} else {
QMutexLocker locker(&_mutex);
_overlaysHUD[thisID] = overlay;
}
@ -205,14 +218,22 @@ OverlayID Overlays::addOverlay(Overlay::Pointer overlay) {
}
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);
if (thisOverlay) {
OverlayID cloneId = addOverlay(Overlay::Pointer(thisOverlay->createClone()));
#if OVERLAY_PANELS
auto attachable = std::dynamic_pointer_cast<PanelAttachable>(thisOverlay);
if (attachable && attachable->getParentPanel()) {
attachable->getParentPanel()->addChild(cloneId);
}
#endif
return cloneId;
}
@ -220,21 +241,32 @@ OverlayID Overlays::cloneOverlay(OverlayID id) {
}
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);
if (thisOverlay) {
thisOverlay->setProperties(properties.toMap());
return true;
}
return false;
}
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();
bool success = true;
QWriteLocker lock(&_lock);
for (const auto& key : map.keys()) {
OverlayID id = OverlayID(key);
Overlay::Pointer thisOverlay = getOverlay(id);
@ -249,10 +281,15 @@ bool Overlays::editOverlays(const QVariant& propertiesById) {
}
void Overlays::deleteOverlay(OverlayID id) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "deleteOverlay", Q_ARG(OverlayID, id));
return;
}
Overlay::Pointer overlayToDelete;
{
QWriteLocker lock(&_lock);
QMutexLocker locker(&_mutex);
if (_overlaysHUD.contains(id)) {
overlayToDelete = _overlaysHUD.take(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);
if (attachable && attachable->getParentPanel()) {
attachable->getParentPanel()->removeChild(id);
attachable->setParentPanel(nullptr);
}
#endif
QWriteLocker lock(&_deleteLock);
_overlaysToDelete.push_back(overlayToDelete);
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);
if (overlay) {
return overlay->getType();
@ -283,6 +326,12 @@ QString Overlays::getOverlayType(OverlayID overlayId) const {
}
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);
if (thisOverlay) {
return qobject_cast<QObject*>(&(*thisOverlay));
@ -290,6 +339,7 @@ QObject* Overlays::getOverlayObject(OverlayID id) {
return nullptr;
}
#if OVERLAY_PANELS
OverlayID Overlays::getParentPanel(OverlayID childId) const {
Overlay::Pointer overlay = getOverlay(childId);
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) {
glm::vec2 pointCopy = point;
QReadLocker lock(&_lock);
if (QThread::currentThread() != thread()) {
OverlayID result;
BLOCKING_INVOKE_METHOD(this, "getOverlayAtPoint", Q_RETURN_ARG(OverlayID, result), Q_ARG(glm::vec2, point));
return result;
}
if (!_enabled) {
return UNKNOWN_OVERLAY_ID;
}
QMapIterator<OverlayID, Overlay::Pointer> i(_overlaysHUD);
const float LARGE_NEGATIVE_FLOAT = -9999999;
glm::vec3 origin(pointCopy.x, pointCopy.y, LARGE_NEGATIVE_FLOAT);
glm::vec3 direction(0, 0, 1);
glm::vec3 thisSurfaceNormal;
QMutexLocker locker(&_mutex);
QMapIterator<OverlayID, Overlay::Pointer> i(_overlaysHUD);
unsigned int bestStackOrder = 0;
OverlayID bestOverlayID = UNKNOWN_OVERLAY_ID;
while (i.hasNext()) {
i.next();
OverlayID thisID = i.key();
if (!i.value()->is3D()) {
auto thisOverlay = std::dynamic_pointer_cast<Overlay2D>(i.value());
if (thisOverlay && thisOverlay->getVisible() && thisOverlay->isLoaded() &&
thisOverlay->getBoundingRect().contains(pointCopy.x, pointCopy.y, false)) {
if (thisOverlay->getStackOrder() > bestStackOrder) {
bestOverlayID = thisID;
bestStackOrder = thisOverlay->getStackOrder();
}
auto thisOverlay = std::dynamic_pointer_cast<Overlay2D>(i.value());
if (thisOverlay && thisOverlay->getVisible() && thisOverlay->isLoaded() &&
thisOverlay->getBoundingRect().contains(point.x, point.y, false)) {
if (thisOverlay->getStackOrder() > bestStackOrder) {
bestOverlayID = i.key();
bestStackOrder = thisOverlay->getStackOrder();
}
}
}
@ -365,9 +413,14 @@ OverlayID Overlays::getOverlayAtPoint(const glm::vec2& point) {
}
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;
Overlay::Pointer thisOverlay = getOverlay(id);
QReadLocker lock(&_lock);
if (thisOverlay && thisOverlay->supportsGetProperty()) {
result.value = thisOverlay->getProperty(property);
}
@ -405,10 +458,22 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionInternal(const PickR
const QVector<OverlayID>& overlaysToInclude,
const QVector<OverlayID>& overlaysToDiscard,
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();
bool bestIsFront = false;
QMutexLocker locker(&_mutex);
RayToOverlayIntersectionResult result;
QMapIterator<OverlayID, Overlay::Pointer> i(_overlaysWorld);
while (i.hasNext()) {
@ -448,16 +513,6 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionInternal(const PickR
return result;
}
RayToOverlayIntersectionResult::RayToOverlayIntersectionResult() :
intersects(false),
overlayID(UNKNOWN_OVERLAY_ID),
distance(0),
face(),
intersection(),
extraInfo()
{
}
QScriptValue RayToOverlayIntersectionResultToScriptValue(QScriptEngine* engine, const RayToOverlayIntersectionResult& value) {
auto obj = engine->newObject();
obj.setProperty("intersects", value.intersects);
@ -531,7 +586,12 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& objectVar
}
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);
if (!thisOverlay) {
return false; // not found
@ -539,14 +599,27 @@ bool Overlays::isLoaded(OverlayID id) {
return thisOverlay->isLoaded();
}
QSizeF Overlays::textSize(OverlayID id, const QString& text) const {
Overlay::Pointer thisOverlay = _overlaysHUD[id];
QSizeF Overlays::textSize(OverlayID id, const QString& text) {
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 (auto textOverlay = std::dynamic_pointer_cast<TextOverlay>(thisOverlay)) {
return textOverlay->textSize(text);
}
} else {
thisOverlay = _overlaysWorld[id];
{
QMutexLocker locker(&_mutex);
thisOverlay = _overlaysWorld[id];
}
if (auto text3dOverlay = std::dynamic_pointer_cast<Text3DOverlay>(thisOverlay)) {
return text3dOverlay->textSize(text);
}
@ -554,6 +627,7 @@ QSizeF Overlays::textSize(OverlayID id, const QString& text) const {
return QSizeF(0.0f, 0.0f);
}
#if OVERLAY_PANELS
OverlayID Overlays::addPanel(OverlayPanel::Pointer panel) {
QWriteLocker lock(&_lock);
@ -607,8 +681,16 @@ void Overlays::deletePanel(OverlayID panelId) {
emit panelDeleted(panelId);
}
#endif
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);
}
@ -636,20 +718,43 @@ void Overlays::sendHoverLeaveOverlay(OverlayID id, PointerEvent 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();
}
void Overlays::setKeyboardFocusOverlay(OverlayID id) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setKeyboardFocusOverlay", Q_ARG(OverlayID, id));
return;
}
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>();
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>();
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);
QReadLocker lock(&_lock);
auto position = thisOverlay->getPosition();
auto rotation = thisOverlay->getRotation();
auto dimensions = thisOverlay->getSize();
@ -854,9 +958,14 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) {
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;
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);
int checked = 0;
while (i.hasNext()) {

View file

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

View file

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

View file

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

View file

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

View file

@ -32,7 +32,7 @@ public:
void render(RenderArgs* args) override;
private:
void buildQmlElement(const QUrl& url);
Q_INVOKABLE void buildQmlElement(const QUrl& url);
protected:
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
//
#include "AnimationCache.h"
#include <QRunnable>
#include <QThreadPool>
#include "AnimationCache.h"
#include "AnimationLogging.h"
#include <shared/QtHelpers.h>
#include <Trace.h>
#include <StatTracker.h>
#include <Profile.h>
#include "AnimationLogging.h"
int animationPointerMetaTypeId = qRegisterMetaType<AnimationPointer>();
AnimationCache::AnimationCache(QObject* parent) :
@ -31,7 +34,7 @@ AnimationCache::AnimationCache(QObject* parent) :
AnimationPointer AnimationCache::getAnimation(const QUrl& url) {
if (QThread::currentThread() != thread()) {
AnimationPointer result;
QMetaObject::invokeMethod(this, "getAnimation", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "getAnimation",
Q_RETURN_ARG(AnimationPointer, result), Q_ARG(const QUrl&, url));
return result;
}
@ -97,7 +100,7 @@ bool Animation::isLoaded() const {
QStringList Animation::getJointNames() const {
if (QThread::currentThread() != thread()) {
QStringList result;
QMetaObject::invokeMethod(const_cast<Animation*>(this), "getJointNames", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(const_cast<Animation*>(this), "getJointNames",
Q_RETURN_ARG(QStringList, result));
return result;
}
@ -111,7 +114,7 @@ QStringList Animation::getJointNames() const {
QVector<FBXAnimationFrame> Animation::getFrames() const {
if (QThread::currentThread() != thread()) {
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));
return result;
}

View file

@ -259,10 +259,21 @@ void AudioClient::customDeleter() {
void AudioClient::cleanupBeforeQuit() {
// 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
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();
_checkDevicesTimer->stop();
guard.trigger();
}
void AudioClient::handleMismatchAudioFormat(SharedNodePointer node, const QString& currentCodec, const QString& recievedCodec) {
@ -1335,6 +1346,14 @@ void AudioClient::toggleMute() {
emit muteToggled();
}
void AudioClient::setNoiseReduction(bool enable) {
if (_isNoiseGateEnabled != enable) {
_isNoiseGateEnabled = enable;
emit noiseReductionChanged();
}
}
void AudioClient::setIsStereoInput(bool isStereoInput) {
if (isStereoInput != _isStereoInput) {
_isStereoInput = isStereoInput;
@ -1446,6 +1465,8 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn
_audioInput = new QAudioInput(inputDeviceInfo, _inputFormat, this);
_numInputCallbackBytes = calculateNumberOfInputCallbackBytes(_inputFormat);
_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?
int numFrameSamples = calculateNumberOfFrameSamples(_numInputCallbackBytes);
@ -1703,7 +1724,7 @@ float AudioClient::azimuthForSource(const glm::vec3& relativePosition) {
// produce an oriented angle about the y-axis
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;
} else {
@ -1847,3 +1868,10 @@ void AudioClient::setAvatarBoundingBoxParameters(glm::vec3 corner, glm::vec3 sca
void AudioClient::startThread() {
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;
void setNoiseReduction(bool isNoiseGateEnabled) { _isNoiseGateEnabled = isNoiseGateEnabled; }
void setNoiseReduction(bool isNoiseGateEnabled);
bool isNoiseReductionEnabled() const { return _isNoiseGateEnabled; }
void toggleLocalEcho() { _shouldEchoLocally = !_shouldEchoLocally; }
void toggleServerEcho() { _shouldEchoToServer = !_shouldEchoToServer; }
@ -197,7 +198,7 @@ public slots:
bool switchAudioDevice(QAudio::Mode mode, const QString& deviceName);
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 setReverbOptions(const AudioEffectOptions* options);
@ -207,7 +208,9 @@ public slots:
void saveSettings();
signals:
bool muteToggled();
void inputVolumeChanged(float volume);
void muteToggled();
void noiseReductionChanged();
void mutedByMixer();
void inputReceived(const QByteArray& inputSamples);
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
//
#include <qthread.h>
#include "SoundCache.h"
#include <QtCore/QThread>
#include <shared/QtHelpers.h>
#include "AudioLogging.h"
#include "SoundCache.h"
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) {
if (QThread::currentThread() != thread()) {
SharedSoundPointer result;
QMetaObject::invokeMethod(this, "getSound", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "getSound",
Q_RETURN_ARG(SharedSoundPointer, result), Q_ARG(const QUrl&, url));
return result;
}

View file

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

View file

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

View file

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

View file

@ -17,6 +17,7 @@
#include <QScriptSyntaxCheckResult>
#include <QThreadPool>
#include <shared/QtHelpers.h>
#include <ColorUtils.h>
#include <AbstractScriptingServicesInterface.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
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "allocateModel", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "allocateModel",
Q_RETURN_ARG(ModelPointer, model),
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) {
// Only create and delete models on the thread that owns the EntityTreeRenderer
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "updateModel", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "updateModel",
Q_RETURN_ARG(ModelPointer, model),
Q_ARG(ModelPointer, model),
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
//
#include "EntityScriptingInterface.h"
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/transform.hpp>
#include "EntityScriptingInterface.h"
#include <QFutureWatcher>
#include <QtConcurrent/QtConcurrentRun>
#include "EntityItemID.h"
#include <shared/QtHelpers.h>
#include <VariantMapToScriptValue.h>
#include <SharedUtil.h>
#include <SpatialParentFinder.h>
#include "EntityItemID.h"
#include "EntitiesLogging.h"
#include "EntityDynamicFactoryInterface.h"
#include "EntityDynamicInterface.h"
@ -1488,7 +1489,7 @@ int EntityScriptingInterface::getJointIndex(const QUuid& entityID, const QString
return -1;
}
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));
return result;
}
@ -1498,7 +1499,7 @@ QStringList EntityScriptingInterface::getJointNames(const QUuid& entityID) {
return QStringList();
}
QStringList result;
QMetaObject::invokeMethod(_entityTree.get(), "getJointNames", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(_entityTree.get(), "getJointNames",
Q_RETURN_ARG(QStringList, result), Q_ARG(QUuid, entityID));
return result;
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -36,6 +36,7 @@
#include <QtScriptTools/QScriptEngineDebugger>
#include <shared/QtHelpers.h>
#include <AudioConstants.h>
#include <AudioEffectOptions.h>
#include <AvatarData.h>
@ -436,12 +437,12 @@ void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) {
_fileNameString = url.toString();
_isReloading = reload;
// Check that script has a supported file extension
// Check that script has a supported file extension
if (!hasValidScriptSuffix(_fileNameString)) {
scriptErrorMessage("File extension of file: " + _fileNameString + " is not a currently supported script type");
emit errorLoadingScript(_fileNameString);
return;
}
}
const auto maxRetries = 0; // for consistency with previous scriptCache->getScript() behavior
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() << "] "
"sourceCode:" << sourceCode << " fileName:" << fileName << "lineNumber:" << lineNumber;
#endif
QMetaObject::invokeMethod(this, "evaluate", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "evaluate",
Q_RETURN_ARG(QScriptValue, result),
Q_ARG(const QString&, sourceCode),
Q_ARG(const QString&, fileName),
@ -1820,7 +1821,7 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac
clearExceptions();
}
} 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 <shared/QtHelpers.h>
#include <SettingHandle.h>
#include <UserActivityLogger.h>
#include <PathUtils.h>
@ -452,7 +453,7 @@ ScriptEngine* ScriptEngines::loadScript(const QUrl& scriptFilename, bool isUserL
bool activateMainWindow, bool reload) {
if (thread() != QThread::currentThread()) {
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(bool, isUserLoaded),
Q_ARG(bool, loadScriptFromEditor),

View file

@ -1076,3 +1076,24 @@ void setMaxCores(uint8_t maxCores) {
SetProcessAffinityMask(process, newProcessAffinity);
#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);
const QString PARENT_PID_OPTION = "parent-pid";
void watchParentProcess(int parentPID);
#endif // hifi_SharedUtil_h

View file

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

View file

@ -16,6 +16,7 @@
#include <QtCore/QMutex>
#include <QtCore/QMutexLocker>
#include <QtCore/QWaitCondition>
#include <QtCore/QObject>
#include <QtCore/QString>
#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, 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

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 <QtQml/QtQml>
#include <shared/QtHelpers.h>
#include <gl/GLHelpers.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) {
if (QThread::currentThread() != thread()) {
QMessageBox::StandardButton result = QMessageBox::StandardButton::NoButton;
QMetaObject::invokeMethod(this, "messageBox", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "messageBox",
Q_RETURN_ARG(QMessageBox::StandardButton, result),
Q_ARG(Icon, icon),
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) {
if (QThread::currentThread() != thread()) {
QVariant result;
QMetaObject::invokeMethod(this, "inputDialog", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "inputDialog",
Q_RETURN_ARG(QVariant, result),
Q_ARG(Icon, icon),
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) {
if (QThread::currentThread() != thread()) {
QVariant result;
QMetaObject::invokeMethod(this, "customInputDialog", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "customInputDialog",
Q_RETURN_ARG(QVariant, result),
Q_ARG(Icon, icon),
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) {
if (QThread::currentThread() != thread()) {
QString result;
QMetaObject::invokeMethod(this, "fileOpenDialog", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "fileOpenDialog",
Q_RETURN_ARG(QString, result),
Q_ARG(QString, caption),
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) {
if (QThread::currentThread() != thread()) {
QString result;
QMetaObject::invokeMethod(this, "fileSaveDialog", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "fileSaveDialog",
Q_RETURN_ARG(QString, result),
Q_ARG(QString, caption),
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) {
if (QThread::currentThread() != thread()) {
QString result;
QMetaObject::invokeMethod(this, "existingDirectoryDialog", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "existingDirectoryDialog",
Q_RETURN_ARG(QString, result),
Q_ARG(QString, caption),
Q_ARG(QString, dir),
@ -773,7 +774,7 @@ QString OffscreenUi::assetOpenDialog(const QString& caption, const QString& dir,
// ATP equivalent of fileOpenDialog().
if (QThread::currentThread() != thread()) {
QString result;
QMetaObject::invokeMethod(this, "assetOpenDialog", Qt::BlockingQueuedConnection,
BLOCKING_INVOKE_METHOD(this, "assetOpenDialog",
Q_RETURN_ARG(QString, result),
Q_ARG(QString, caption),
Q_ARG(QString, dir),

View file

@ -13,6 +13,7 @@
#include <QtScript/QScriptContext>
#include <QtScript/QScriptEngine>
#include <shared/QtHelpers.h>
#include "OffscreenUi.h"
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
QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) {
auto properties = parseArguments(context);
QmlWebWindowClass* retVal { nullptr };
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->executeOnUiThread([&] {
retVal = new QmlWebWindowClass();
retVal->initQml(properties);
}, true);
QmlWebWindowClass* retVal = new QmlWebWindowClass();
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);
return engine->newQObject(retVal);
}
QString QmlWebWindowClass::getURL() const {
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant {
if (_qmlWindow.isNull()) {
return QVariant();
}
return _qmlWindow->property(URL_PROPERTY);
});
return result.toString();
QString QmlWebWindowClass::getURL() {
if (QThread::currentThread() != thread()) {
QString result;
BLOCKING_INVOKE_METHOD(this, "getURL", Q_RETURN_ARG(QString, result));
return result;
}
if (_qmlWindow.isNull()) {
return QString();
}
return _qmlWindow->property(URL_PROPERTY).toString();
}
void QmlWebWindowClass::setURL(const QString& urlString) {
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
if (!_qmlWindow.isNull()) {
_qmlWindow->setProperty(URL_PROPERTY, urlString);
}
});
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setURL", Q_ARG(QString, urlString));
return;
}
if (!_qmlWindow.isNull()) {
_qmlWindow->setProperty(URL_PROPERTY, urlString);
}
}
void QmlWebWindowClass::setScriptURL(const QString& script) {
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
if (!_qmlWindow.isNull()) {
_qmlWindow->setProperty(SCRIPT_PROPERTY, script);
}
});
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setScriptURL", Q_ARG(QString, script));
return;
}
if (!_qmlWindow.isNull()) {
_qmlWindow->setProperty(SCRIPT_PROPERTY, script);
}
}

View file

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

View file

@ -23,6 +23,7 @@
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <shared/QtHelpers.h>
#include "OffscreenUi.h"
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
QScriptValue QmlWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) {
auto properties = parseArguments(context);
QmlWindowClass* retVal { nullptr };
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->executeOnUiThread([&] {
retVal = new QmlWindowClass();
retVal->initQml(properties);
}, true);
QmlWindowClass* retVal = new QmlWindowClass();
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);
return engine->newQObject(retVal);
}
@ -90,9 +93,10 @@ QmlWindowClass::QmlWindowClass() {
void QmlWindowClass::initQml(QVariantMap properties) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
_toolWindow = properties.contains(TOOLWINDOW_PROPERTY) && properties[TOOLWINDOW_PROPERTY].toBool();
_source = properties[SOURCE_PROPERTY].toString();
#if QML_TOOL_WINDOW
_toolWindow = properties.contains(TOOLWINDOW_PROPERTY) && properties[TOOLWINDOW_PROPERTY].toBool();
if (_toolWindow) {
// Build the event bridge and wrapper on the main thread
_qmlWindow = offscreenUi->getToolWindow();
@ -103,10 +107,11 @@ void QmlWindowClass::initQml(QVariantMap properties) {
Q_ARG(QVariant, QVariant::fromValue(properties)));
Q_ASSERT(invokeResult);
} else {
#endif
// Build the event bridge and wrapper on the main thread
offscreenUi->loadInNewContext(qmlSource(), [&](QQmlContext* context, QObject* object) {
_qmlWindow = object;
context->setContextProperty("eventBridge", this);
context->setContextProperty(EVENT_BRIDGE_PROPERTY, this);
context->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership);
context->engine()->setObjectOwnership(object, QQmlEngine::CppOwnership);
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(windowClosed()), this, SLOT(hasClosed()), Qt::QueuedConnection);
});
#if QML_TOOL_WINDOW
}
#endif
Q_ASSERT(_qmlWindow);
Q_ASSERT(dynamic_cast<const QQuickItem*>(_qmlWindow.data()));
}
@ -207,56 +214,77 @@ QmlWindowClass::~QmlWindowClass() {
}
QQuickItem* QmlWindowClass::asQuickItem() const {
#if QML_TOOL_WINDOW
if (_toolWindow) {
return DependencyManager::get<OffscreenUi>()->getToolWindow();
}
#endif
return _qmlWindow.isNull() ? nullptr : dynamic_cast<QQuickItem*>(_qmlWindow.data());
}
void QmlWindowClass::setVisible(bool visible) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setVisible", Q_ARG(bool, visible));
return;
}
QQuickItem* targetWindow = asQuickItem();
#if QML_TOOL_WINDOW
if (_toolWindow) {
// 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
QMetaObject::invokeMethod(targetWindow, "showTabForUrl", Qt::QueuedConnection, Q_ARG(QVariant, _source), Q_ARG(QVariant, visible));
} else {
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
targetWindow->setProperty(OFFSCREEN_VISIBILITY_PROPERTY, visible);
});
return;
}
#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
return DependencyManager::get<OffscreenUi>()->returnFromUiThread([&] {
if (_qmlWindow.isNull()) {
return QVariant::fromValue(false);
}
if (_toolWindow) {
return QVariant::fromValue(dynamic_cast<QQuickItem*>(_qmlWindow.data())->isEnabled());
} else {
return QVariant::fromValue(asQuickItem()->isVisible());
}
}).toBool();
if (_qmlWindow.isNull()) {
return false;
}
#if QML_TOOL_WINDOW
if (_toolWindow) {
return dynamic_cast<QQuickItem*>(_qmlWindow.data())->isEnabled();
}
#endif
return asQuickItem()->isVisible();
}
glm::vec2 QmlWindowClass::getPosition() const {
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant {
if (_qmlWindow.isNull()) {
return QVariant(QPointF(0, 0));
}
return asQuickItem()->position();
});
return toGlm(result.toPointF());
glm::vec2 QmlWindowClass::getPosition() {
if (QThread::currentThread() != thread()) {
vec2 result;
BLOCKING_INVOKE_METHOD(this, "getPosition", Q_RETURN_ARG(vec2, result));
return result;
}
if (_qmlWindow.isNull()) {
return {};
}
return toGlm(asQuickItem()->position());
}
void QmlWindowClass::setPosition(const glm::vec2& position) {
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
if (!_qmlWindow.isNull()) {
asQuickItem()->setPosition(QPointF(position.x, position.y));
}
});
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setPosition", Q_ARG(vec2, position));
return;
}
if (!_qmlWindow.isNull()) {
asQuickItem()->setPosition(QPointF(position.x, position.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());
}
glm::vec2 QmlWindowClass::getSize() const {
QVariant result = DependencyManager::get<OffscreenUi>()->returnFromUiThread([&]()->QVariant {
if (_qmlWindow.isNull()) {
return QVariant(QSizeF(0, 0));
}
QQuickItem* targetWindow = asQuickItem();
return QSizeF(targetWindow->width(), targetWindow->height());
});
return toGlm(result.toSizeF());
glm::vec2 QmlWindowClass::getSize() {
if (QThread::currentThread() != thread()) {
vec2 result;
BLOCKING_INVOKE_METHOD(this, "getSize", Q_RETURN_ARG(vec2, result));
return result;
}
if (_qmlWindow.isNull()) {
return {};
}
QQuickItem* targetWindow = asQuickItem();
return vec2(targetWindow->width(), targetWindow->height());
}
void QmlWindowClass::setSize(const glm::vec2& size) {
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
if (!_qmlWindow.isNull()) {
asQuickItem()->setSize(QSizeF(size.x, size.y));
}
});
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setSize", Q_ARG(vec2, size));
return;
}
if (!_qmlWindow.isNull()) {
asQuickItem()->setSize(QSizeF(size.x, size.y));
}
}
void QmlWindowClass::setSize(int width, int height) {
@ -292,28 +326,37 @@ void QmlWindowClass::setSize(int width, int height) {
}
void QmlWindowClass::setTitle(const QString& title) {
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
if (!_qmlWindow.isNull()) {
asQuickItem()->setProperty(TITLE_PROPERTY, title);
}
});
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setTitle", Q_ARG(QString, title));
return;
}
if (!_qmlWindow.isNull()) {
asQuickItem()->setProperty(TITLE_PROPERTY, title);
}
}
void QmlWindowClass::close() {
if (_qmlWindow) {
if (_toolWindow) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
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 (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "close");
return;
}
#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) {
@ -325,10 +368,13 @@ void QmlWindowClass::hasClosed() {
}
void QmlWindowClass::raise() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "raise");
return;
}
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->executeOnUiThread([=] {
if (!_qmlWindow.isNull()) {
QMetaObject::invokeMethod(asQuickItem(), "raise", Qt::DirectConnection);
}
});
if (_qmlWindow) {
QMetaObject::invokeMethod(asQuickItem(), "raise", Qt::DirectConnection);
}
}

View file

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

View file

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

View file

@ -25,6 +25,8 @@
#include <QtCore/QWaitCondition>
#include <shared/NsightHelpers.h>
#include <shared/GlobalAppProperties.h>
#include <shared/QtHelpers.h>
#include <PerfStat.h>
#include <DependencyManager.h>
#include <NumericalConstants.h>
@ -34,7 +36,6 @@
#include <AccountManager.h>
#include <NetworkAccessManager.h>
#include <GLMHelpers.h>
#include <shared/GlobalAppProperties.h>
#include <gl/OffscreenGLCanvas.h>
#include <gl/GLHelpers.h>
@ -886,28 +887,6 @@ QQmlContext* OffscreenQmlSurface::getSurfaceContext() {
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) {
_currentFocusItem = nullptr;
}

View file

@ -55,10 +55,6 @@ public:
return load(QUrl(qmlSourceFile), f);
}
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; }
// Optional values for event handling
void setProxyWindow(QWindow* window);

View file

@ -11,6 +11,8 @@
#include <QtCore/QThread>
#include <QtCore/QCoreApplication>
#include <shared/QtHelpers.h>
QmlWrapper::QmlWrapper(QObject* qmlObject, QObject* parent)
: QObject(parent), _qmlObject(qmlObject) {
Q_ASSERT(QThread::currentThread() == qApp->thread());
@ -36,7 +38,7 @@ void QmlWrapper::writeProperties(QVariant propertyMap) {
QVariant QmlWrapper::readProperty(const QString& propertyName) {
if (QThread::currentThread() != thread()) {
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;
}
@ -46,7 +48,7 @@ QVariant QmlWrapper::readProperty(const QString& propertyName) {
QVariant QmlWrapper::readProperties(const QVariant& propertyList) {
if (QThread::currentThread() != thread()) {
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;
}

View file

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

View file

@ -12,6 +12,8 @@
#include <QtQuick/QQuickItem>
#include <QtScript/QScriptValue>
#include <QtScript/QScriptEngine>
#include <shared/QtHelpers.h>
#include "../OffscreenUi.h"
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) {
if (QThread::currentThread() != thread()) {
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;
}
@ -99,18 +101,14 @@ void ToolbarProxy::removeButton(const QVariant& name) {
ToolbarProxy* ToolbarScriptingInterface::getToolbar(const QString& toolbarId) {
if (QThread::currentThread() != thread()) {
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;
}
auto offscreenUi = DependencyManager::get<OffscreenUi>();
auto desktop = offscreenUi->getDesktop();
Qt::ConnectionType connectionType = Qt::AutoConnection;
if (QThread::currentThread() != desktop->thread()) {
connectionType = Qt::BlockingQueuedConnection;
}
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) {
return nullptr;
}

View file

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

View file

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

View file

@ -20,7 +20,9 @@ var blastShareText = "Blast to my Connections",
hifiShareText = "Share to Snaps Feed",
hifiAlreadySharedText = "Already Shared to Snaps Feed",
facebookShareText = "Share to Facebook",
twitterShareText = "Share to Twitter";
twitterShareText = "Share to Twitter",
shareButtonLabelTextActive = "SHARE&#58;",
shareButtonLabelTextInactive = "SHARE";
function fileExtensionMatches(filePath, extension) {
return filePath.split('.').pop().toLowerCase() === extension;
@ -138,6 +140,8 @@ function selectImageToShare(selectedID, isSelected) {
var imageContainer = document.getElementById(selectedID),
image = document.getElementById(selectedID + 'img'),
shareBar = document.getElementById(selectedID + "shareBar"),
showShareButtonsDots = document.getElementById(selectedID + "showShareButtonsDots"),
showShareButtonsLabel = document.getElementById(selectedID + "showShareButtonsLabel"),
shareButtonsDiv = document.getElementById(selectedID + "shareButtonsDiv"),
shareBarHelp = document.getElementById(selectedID + "shareBarHelp"),
showShareButtonsButtonDiv = document.getElementById(selectedID + "showShareButtonsButtonDiv"),
@ -156,6 +160,9 @@ function selectImageToShare(selectedID, isSelected) {
shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.45)";
shareBar.style.pointerEvents = "initial";
showShareButtonsDots.style.visibility = "hidden";
showShareButtonsLabel.innerHTML = shareButtonLabelTextActive;
shareButtonsDiv.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.pointerEvents = "none";
showShareButtonsDots.style.visibility = "visible";
showShareButtonsLabel.innerHTML = shareButtonLabelTextInactive;
shareButtonsDiv.style.visibility = "hidden";
shareBarHelp.style.visibility = "hidden";
}
@ -185,6 +195,7 @@ function createShareBar(parentID, isLoggedIn, canShare, isGif, blastButtonDisabl
shareBarHelpID = parentID + "shareBarHelp",
shareButtonsDivID = parentID + "shareButtonsDiv",
showShareButtonsButtonDivID = parentID + "showShareButtonsButtonDiv",
showShareButtonsDotsID = parentID + "showShareButtonsDots",
showShareButtonsLabelID = parentID + "showShareButtonsLabel",
blastToConnectionsButtonID = parentID + "blastToConnectionsButton",
shareWithEveryoneButtonID = parentID + "shareWithEveryoneButton",
@ -199,8 +210,8 @@ function createShareBar(parentID, isLoggedIn, canShare, isGif, blastButtonDisabl
if (canShare) {
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)">' +
'<label id="' + showShareButtonsLabelID + '">SHARE</label>' +
'<span class="showShareButtonDots">' +
'<label id="' + showShareButtonsLabelID + '">' + shareButtonLabelTextInactive + '</label>' +
'<span id="' + showShareButtonsDotsID + '" class="showShareButtonDots">' +
'&#xe019;' +
'</div>' +
'</div>' +
@ -217,7 +228,7 @@ function createShareBar(parentID, isLoggedIn, canShare, isGif, blastButtonDisabl
document.getElementById(parentID + 'img').onclick = function () { selectImageToShare(parentID, true); };
} else {
shareBarInnerHTML = '<div class="showShareButtonsButtonDiv inactive" id="' + showShareButtonsButtonDivID + '" onclick="selectImageToShare(' + parentID + ', true)">' +
'<label id="' + showShareButtonsLabelID + '">SHARE</label>' +
'<label id="' + showShareButtonsLabelID + '">' + shareButtonLabelTextInactive + '</label>' +
'<span class="showShareButtonDots">' +
'&#xe019;' +
'</div>' +
@ -230,7 +241,7 @@ function createShareBar(parentID, isLoggedIn, canShare, isGif, blastButtonDisabl
}
} else {
shareBarInnerHTML = '<div class="showShareButtonsButtonDiv inactive" id="' + showShareButtonsButtonDivID + '" onclick="selectImageToShare(' + parentID + ', true)">' +
'<label id="' + showShareButtonsLabelID + '">SHARE</label>' +
'<label id="' + showShareButtonsLabelID + '">' + shareButtonLabelTextInactive + '</label>' +
'<span class="showShareButtonDots">' +
'&#xe019;' +
'</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
var pInterface = new Process('interface', interfacePath, argArray);
pInterface.detached = true;
pInterface.start();
}
@ -892,10 +893,19 @@ function onContentLoaded() {
deleteOldFiles(logPath, DELETE_LOG_FILES_OLDER_THAN_X_SECONDS, LOG_FILE_REGEX);
if (dsPath && acPath) {
domainServer = new Process('domain-server', dsPath, ["--get-temp-name"], logPath);
acMonitor = new ACMonitorProcess('ac-monitor', acPath, ['-n7',
'--log-directory', logPath,
'--http-status-port', httpStatusPort], httpStatusPort, logPath);
var dsArguments = ['--get-temp-name',
'--parent-pid', process.pid];
domainServer = new Process('domain-server', dsPath, dsArguments, 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]);
logWindow = new LogWindow(acMonitor, domainServer);

View file

@ -113,6 +113,10 @@ function Process(name, command, commandArgs, logDirectory) {
this.logDirectory = logDirectory;
this.logStdout = null;
this.logStderr = null;
this.detached = false;
this.restartOnCrash = false;
this.restartCount = 0;
this.firstRestartTimestamp = Date.now();
this.state = ProcessStates.STOPPED;
};
@ -165,9 +169,10 @@ Process.prototype = extend(Process.prototype, {
try {
this.child = childProcess.spawn(this.command, this.commandArgs, {
detached: false,
detached: this.detached,
stdio: ['ignore', logStdout, logStderr]
});
log.debug("Spawned " + this.command + " with pid " + this.child.pid);
} catch (e) {
log.debug("Got error starting child process for " + this.name, e);
this.child = null;
@ -266,7 +271,30 @@ Process.prototype = extend(Process.prototype, {
clearTimeout(this.stoppingTimeoutID);
this.stoppingTimeoutID = null;
}
// Grab current state before updating it.
var unexpectedShutdown = this.state != ProcessStates.STOPPING;
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>();
// start the nodeThread so its event loop is running
nodeList->startThread();
// setup a timer for domain-server check ins
QTimer* domainCheckInTimer = new QTimer(nodeList.data());
connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn);
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();
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)) {
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 {
parser.showHelp();
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