Merge pull request #10860 from jherico/deadlock_guards

Don't use blocking connections on the main thread
This commit is contained in:
Seth Alves 2017-07-03 09:05:56 -07:00 committed by GitHub
commit 70a7b302fc
64 changed files with 816 additions and 428 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

@ -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

@ -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

@ -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>
@ -40,8 +41,6 @@ Q_LOGGING_CATEGORY(trace_render_overlays, "trace.render.overlays")
void Overlays::cleanupAllOverlays() {
{
QWriteLocker lock(&_lock);
QWriteLocker deleteLock(&_deleteLock);
foreach(Overlay::Pointer overlay, _overlaysHUD) {
_overlaysToDelete.push_back(overlay);
}
@ -50,19 +49,22 @@ void Overlays::cleanupAllOverlays() {
}
_overlaysHUD.clear();
_overlaysWorld.clear();
#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) {
thisOverlay->update(deltatime);
}
@ -80,8 +82,6 @@ void Overlays::cleanupOverlaysToDelete() {
render::Transaction transaction;
{
QWriteLocker lock(&_deleteLock);
do {
Overlay::Pointer overlay = _overlaysToDelete.takeLast();
@ -100,7 +100,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>();
@ -126,12 +125,10 @@ void Overlays::renderHUD(RenderArgs* renderArgs) {
}
void Overlays::disable() {
QWriteLocker lock(&_lock);
_enabled = false;
}
void Overlays::enable() {
QWriteLocker lock(&_lock);
_enabled = true;
}
@ -146,6 +143,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,8 +188,7 @@ 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++);
@ -205,14 +207,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 +230,29 @@ OverlayID Overlays::cloneOverlay(OverlayID id) {
}
bool Overlays::editOverlay(OverlayID id, const QVariant& properties) {
QWriteLocker lock(&_lock);
if (QThread::currentThread() != thread()) {
bool result;
BLOCKING_INVOKE_METHOD(this, "editOverlay", Q_RETURN_ARG(bool, result), Q_ARG(OverlayID, id), Q_ARG(QVariant, properties));
return result;
}
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()) {
bool result;
BLOCKING_INVOKE_METHOD(this, "editOverlays", Q_RETURN_ARG(bool, result), Q_ARG(QVariant, propertiesById));
return result;
}
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 +267,14 @@ 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);
if (_overlaysHUD.contains(id)) {
overlayToDelete = _overlaysHUD.take(id);
} else if (_overlaysWorld.contains(id)) {
@ -262,19 +284,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 +311,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 +324,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,10 +365,16 @@ void Overlays::setParentPanel(OverlayID childId, OverlayID panelId) {
}
}
}
#endif
OverlayID Overlays::getOverlayAtPoint(const glm::vec2& point) {
if (QThread::currentThread() != thread()) {
OverlayID result;
BLOCKING_INVOKE_METHOD(this, "getOverlayAtPoint", Q_RETURN_ARG(OverlayID, result), Q_ARG(glm::vec2, point));
return result;
}
glm::vec2 pointCopy = point;
QReadLocker lock(&_lock);
if (!_enabled) {
return UNKNOWN_OVERLAY_ID;
}
@ -365,9 +406,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,7 +451,18 @@ 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;
@ -448,16 +505,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 +578,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,7 +591,13 @@ bool Overlays::isLoaded(OverlayID id) {
return thisOverlay->isLoaded();
}
QSizeF Overlays::textSize(OverlayID id, const QString& text) const {
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 = _overlaysHUD[id];
if (thisOverlay) {
if (auto textOverlay = std::dynamic_pointer_cast<TextOverlay>(thisOverlay)) {
@ -554,6 +612,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 +666,15 @@ 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;
}
return _overlaysHUD.contains(id) || _overlaysWorld.contains(id);
}
@ -636,20 +702,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 +794,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,8 +942,13 @@ 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;
}
QMapIterator<OverlayID, Overlay::Pointer> i(_overlaysWorld);
int checked = 0;

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:
@ -316,13 +321,15 @@ private:
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 +338,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

@ -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

@ -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),

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

@ -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;
}