History

Merge branch 'master' of https://github.com/highfidelity/hifi into console

Stephen Birarda committed 10 years ago

Commit 404fb2f2 parents 184bae2 3a7ec33136 files · +3417 −1538Browse files

Showing the changes to interface/src/Application.cpp (1 of 136 files). Show all

Unified · Split

interface/src/Application.cpp+127−26
@@ -20,6 +20,8 @@ · 19 unmodified lines
20#include <glm/gtc/type_ptr.hpp>20#include <glm/gtc/type_ptr.hpp>
21 21
22#include <QtCore/QDebug>22#include <QtCore/QDebug>
23#include <QtCore/QJsonDocument>
24#include <QtCore/QJsonObject>
23#include <QtCore/QObject>25#include <QtCore/QObject>
24#include <QtCore/QUrl>26#include <QtCore/QUrl>
25#include <QtCore/QTimer>27#include <QtCore/QTimer>
@@ -89,6 +91,7 @@ · 63 unmodified lines
89#include <ObjectMotionState.h>91#include <ObjectMotionState.h>
90#include <OctalCode.h>92#include <OctalCode.h>
91#include <OctreeSceneStats.h>93#include <OctreeSceneStats.h>
94#include <OffscreenUi.h>
92#include <gl/OffscreenGLCanvas.h>95#include <gl/OffscreenGLCanvas.h>
93#include <PathUtils.h>96#include <PathUtils.h>
94#include <PerfStat.h>97#include <PerfStat.h>
@@ -187,6 +190,7 @@ static const QString JS_EXTENSION = ".js"; · 92 unmodified lines
187static const QString FST_EXTENSION = ".fst";190static const QString FST_EXTENSION = ".fst";
188static const QString FBX_EXTENSION = ".fbx";191static const QString FBX_EXTENSION = ".fbx";
189static const QString OBJ_EXTENSION = ".obj";192static const QString OBJ_EXTENSION = ".obj";
193static const QString AVA_JSON_EXTENSION = ".ava.json";
190 194
191static const int MIRROR_VIEW_TOP_PADDING = 5;195static const int MIRROR_VIEW_TOP_PADDING = 5;
192static const int MIRROR_VIEW_LEFT_PADDING = 10;196static const int MIRROR_VIEW_LEFT_PADDING = 10;
@@ -222,7 +226,8 @@ const QHash<QString, Application::AcceptURLMethod> Application::_acceptedExtensi · 29 unmodified lines
222 { SVO_EXTENSION, &Application::importSVOFromURL },226 { SVO_EXTENSION, &Application::importSVOFromURL },
223 { SVO_JSON_EXTENSION, &Application::importSVOFromURL },227 { SVO_JSON_EXTENSION, &Application::importSVOFromURL },
224 { JS_EXTENSION, &Application::askToLoadScript },228 { JS_EXTENSION, &Application::askToLoadScript },
225 { FST_EXTENSION, &Application::askToSetAvatarUrl }229 { FST_EXTENSION, &Application::askToSetAvatarUrl },
230 { AVA_JSON_EXTENSION, &Application::askToWearAvatarAttachmentUrl }
226};231};
227 232
228#ifdef Q_OS_WIN233#ifdef Q_OS_WIN
@@ -347,7 +352,7 @@ bool setupEssentials(int& argc, char** argv) { · 118 unmodified lines
347 DependencyManager::set<WindowScriptingInterface>();352 DependencyManager::set<WindowScriptingInterface>();
348 DependencyManager::set<HMDScriptingInterface>();353 DependencyManager::set<HMDScriptingInterface>();
349 DependencyManager::set<ResourceScriptingInterface>();354 DependencyManager::set<ResourceScriptingInterface>();
350 355
351 356
352#if defined(Q_OS_MAC) || defined(Q_OS_WIN)357#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
353 DependencyManager::set<SpeechRecognizer>();358 DependencyManager::set<SpeechRecognizer>();
@@ -378,7 +383,7 @@ PluginContainer* _pluginContainer; · 24 unmodified lines
378 383
379 384
380// FIXME hack access to the internal share context for the Chromium helper385// FIXME hack access to the internal share context for the Chromium helper
381// Normally we'd want to use QWebEngine::initialize(), but we can't because 386// Normally we'd want to use QWebEngine::initialize(), but we can't because
382// our primary context is a QGLWidget, which can't easily be initialized to share387// our primary context is a QGLWidget, which can't easily be initialized to share
383// from a QOpenGLContext.388// from a QOpenGLContext.
384//389//
@@ -694,7 +699,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 309 unmodified lines
694 auto userInputMapper = DependencyManager::get<UserInputMapper>();699 auto userInputMapper = DependencyManager::get<UserInputMapper>();
695 connect(userInputMapper.data(), &UserInputMapper::actionEvent, [this](int action, float state) {700 connect(userInputMapper.data(), &UserInputMapper::actionEvent, [this](int action, float state) {
696 using namespace controller;701 using namespace controller;
697 static auto offscreenUi = DependencyManager::get<OffscreenUi>();702 auto offscreenUi = DependencyManager::get<OffscreenUi>();
698 if (offscreenUi->navigationFocused()) {703 if (offscreenUi->navigationFocused()) {
699 auto actionEnum = static_cast<Action>(action);704 auto actionEnum = static_cast<Action>(action);
700 int key = Qt::Key_unknown;705 int key = Qt::Key_unknown;
@@ -735,6 +740,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 34 unmodified lines
735 case Action::UI_NAV_SELECT:740 case Action::UI_NAV_SELECT:
736 key = Qt::Key_Return;741 key = Qt::Key_Return;
737 break;742 break;
743 default:
744 break;
738 }745 }
739 746
740 if (navAxis) {747 if (navAxis) {
@@ -832,7 +839,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 91 unmodified lines
832 return (float)qApp->getMyAvatar()->getCharacterController()->onGround();839 return (float)qApp->getMyAvatar()->getCharacterController()->onGround();
833 }));840 }));
834 _applicationStateDevice->addInputVariant(QString("NavigationFocused"), controller::StateController::ReadLambda([]() -> float {841 _applicationStateDevice->addInputVariant(QString("NavigationFocused"), controller::StateController::ReadLambda([]() -> float {
835 static auto offscreenUi = DependencyManager::get<OffscreenUi>();842 auto offscreenUi = DependencyManager::get<OffscreenUi>();
836 return offscreenUi->navigationFocused() ? 1.0 : 0.0;843 return offscreenUi->navigationFocused() ? 1.0 : 0.0;
837 }));844 }));
838 845
@@ -1035,7 +1042,7 @@ void Application::cleanupBeforeQuit() { · 196 unmodified lines
1035 1042
1036 // destroy the AudioClient so it and its thread have a chance to go down safely1043 // destroy the AudioClient so it and its thread have a chance to go down safely
1037 DependencyManager::destroy<AudioClient>();1044 DependencyManager::destroy<AudioClient>();
1038 1045
1039 // destroy the AudioInjectorManager so it and its thread have a chance to go down safely1046 // destroy the AudioInjectorManager so it and its thread have a chance to go down safely
1040 // this will also stop any ongoing network injectors1047 // this will also stop any ongoing network injectors
1041 DependencyManager::destroy<AudioInjectorManager>();1048 DependencyManager::destroy<AudioInjectorManager>();
@@ -1047,6 +1054,8 @@ void Application::cleanupBeforeQuit() { · 5 unmodified lines
1047#ifdef HAVE_IVIEWHMD1054#ifdef HAVE_IVIEWHMD
1048 DependencyManager::destroy<EyeTracker>();1055 DependencyManager::destroy<EyeTracker>();
1049#endif1056#endif
1057
1058 DependencyManager::destroy<OffscreenUi>();
1050}1059}
1051 1060
1052void Application::emptyLocalCache() {1061void Application::emptyLocalCache() {
@@ -1080,8 +1089,8 @@ Application::~Application() { · 27 unmodified lines
1080 // remove avatars from physics engine1089 // remove avatars from physics engine
1081 DependencyManager::get<AvatarManager>()->clearOtherAvatars();1090 DependencyManager::get<AvatarManager>()->clearOtherAvatars();
1082 VectorOfMotionStates motionStates;1091 VectorOfMotionStates motionStates;
1083 DependencyManager::get<AvatarManager>()->getObjectsToDelete(motionStates);1092 DependencyManager::get<AvatarManager>()->getObjectsToRemoveFromPhysics(motionStates);
1084 _physicsEngine->deleteObjects(motionStates);1093 _physicsEngine->removeObjects(motionStates);
1085 1094
1086 DependencyManager::destroy<OffscreenUi>();1095 DependencyManager::destroy<OffscreenUi>();
1087 DependencyManager::destroy<AvatarManager>();1096 DependencyManager::destroy<AvatarManager>();
@@ -1176,7 +1185,6 @@ void Application::initializeUi() { · 88 unmodified lines
1176 offscreenUi->setProxyWindow(_window->windowHandle());1185 offscreenUi->setProxyWindow(_window->windowHandle());
1177 offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));1186 offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));
1178 offscreenUi->load("Root.qml");1187 offscreenUi->load("Root.qml");
1179 offscreenUi->load("RootMenu.qml");
1180 // FIXME either expose so that dialogs can set this themselves or1188 // FIXME either expose so that dialogs can set this themselves or
1181 // do better detection in the offscreen UI of what has focus1189 // do better detection in the offscreen UI of what has focus
1182 offscreenUi->setNavigationFocused(false);1190 offscreenUi->setNavigationFocused(false);
@@ -1265,7 +1273,7 @@ void Application::initializeUi() { · 82 unmodified lines
1265}1273}
1266 1274
1267void Application::paintGL() {1275void Application::paintGL() {
1268 // paintGL uses a queued connection, so we can get messages from the queue even after we've quit 1276 // paintGL uses a queued connection, so we can get messages from the queue even after we've quit
1269 // and the plugins have shutdown1277 // and the plugins have shutdown
1270 if (_aboutToQuit) {1278 if (_aboutToQuit) {
1271 return;1279 return;
@@ -1335,7 +1343,7 @@ void Application::paintGL() { · 63 unmodified lines
1335 renderArgs._context->syncCache();1343 renderArgs._context->syncCache();
1336 }1344 }
1337 1345
1338 if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {1346 if (Menu::getInstance()->isOptionChecked(MenuOption::MiniMirror)) {
1339 PerformanceTimer perfTimer("Mirror");1347 PerformanceTimer perfTimer("Mirror");
1340 auto primaryFbo = DependencyManager::get<FramebufferCache>()->getPrimaryFramebuffer();1348 auto primaryFbo = DependencyManager::get<FramebufferCache>()->getPrimaryFramebuffer();
1341 1349
@@ -1624,7 +1632,7 @@ void Application::aboutApp() { · 282 unmodified lines
1624 InfoView::show(INFO_HELP_PATH);1632 InfoView::show(INFO_HELP_PATH);
1625}1633}
1626 1634
1627void Application::showEditEntitiesHelp() {1635void Application::showHelp() {
1628 InfoView::show(INFO_EDIT_ENTITIES_PATH);1636 InfoView::show(INFO_EDIT_ENTITIES_PATH);
1629}1637}
1630 1638
@@ -1982,7 +1990,7 @@ void Application::keyPressEvent(QKeyEvent* event) { · 351 unmodified lines
1982 1990
1983 case Qt::Key_H:1991 case Qt::Key_H:
1984 if (isShifted) {1992 if (isShifted) {
1985 Menu::getInstance()->triggerOption(MenuOption::Mirror);1993 Menu::getInstance()->triggerOption(MenuOption::MiniMirror);
1986 } else {1994 } else {
1987 Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror));1995 Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror));
1988 if (!Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {1996 if (!Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
@@ -3082,11 +3090,11 @@ void Application::update(float deltaTime) { · 1093 unmodified lines
3082 PerformanceTimer perfTimer("physics");3090 PerformanceTimer perfTimer("physics");
3083 3091
3084 static VectorOfMotionStates motionStates;3092 static VectorOfMotionStates motionStates;
3085 _entitySimulation.getObjectsToDelete(motionStates);3093 _entitySimulation.getObjectsToRemoveFromPhysics(motionStates);
3086 _physicsEngine->deleteObjects(motionStates);3094 _physicsEngine->removeObjects(motionStates);
3087 3095
3088 getEntities()->getTree()->withWriteLock([&] {3096 getEntities()->getTree()->withWriteLock([&] {
3089 _entitySimulation.getObjectsToAdd(motionStates);3097 _entitySimulation.getObjectsToAddToPhysics(motionStates);
3090 _physicsEngine->addObjects(motionStates);3098 _physicsEngine->addObjects(motionStates);
3091 3099
3092 });3100 });
@@ -3099,9 +3107,9 @@ void Application::update(float deltaTime) { · 6 unmodified lines
3099 _entitySimulation.applyActionChanges();3107 _entitySimulation.applyActionChanges();
3100 3108
3101 AvatarManager* avatarManager = DependencyManager::get<AvatarManager>().data();3109 AvatarManager* avatarManager = DependencyManager::get<AvatarManager>().data();
3102 avatarManager->getObjectsToDelete(motionStates);3110 avatarManager->getObjectsToRemoveFromPhysics(motionStates);
3103 _physicsEngine->deleteObjects(motionStates);3111 _physicsEngine->removeObjects(motionStates);
3104 avatarManager->getObjectsToAdd(motionStates);3112 avatarManager->getObjectsToAddToPhysics(motionStates);
3105 _physicsEngine->addObjects(motionStates);3113 _physicsEngine->addObjects(motionStates);
3106 avatarManager->getObjectsToChange(motionStates);3114 avatarManager->getObjectsToChange(motionStates);
3107 _physicsEngine->changeObjects(motionStates);3115 _physicsEngine->changeObjects(motionStates);
@@ -3738,7 +3746,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se · 630 unmodified lines
3738 auto backgroundRenderData = make_shared<BackgroundRenderData>(&_environment);3746 auto backgroundRenderData = make_shared<BackgroundRenderData>(&_environment);
3739 auto backgroundRenderPayload = make_shared<BackgroundRenderData::Payload>(backgroundRenderData);3747 auto backgroundRenderPayload = make_shared<BackgroundRenderData::Payload>(backgroundRenderData);
3740 BackgroundRenderData::_item = _main3DScene->allocateID();3748 BackgroundRenderData::_item = _main3DScene->allocateID();
3741 pendingChanges.resetItem(WorldBoxRenderData::_item, backgroundRenderPayload);3749 pendingChanges.resetItem(BackgroundRenderData::_item, backgroundRenderPayload);
3742 } else {3750 } else {
3743 3751
3744 }3752 }
@@ -4080,7 +4088,7 @@ bool Application::nearbyEntitiesAreReadyForPhysics() { · 335 unmodified lines
4080 });4088 });
4081 4089
4082 foreach (EntityItemPointer entity, entities) {4090 foreach (EntityItemPointer entity, entities) {
4083 if (!entity->isReadyToComputeShape()) {4091 if (entity->shouldBePhysical() && !entity->isReadyToComputeShape()) {
4084 static QString repeatedMessage =4092 static QString repeatedMessage =
4085 LogHandler::getInstance().addRepeatedMessageRegex("Physics disabled until entity loads: .*");4093 LogHandler::getInstance().addRepeatedMessageRegex("Physics disabled until entity loads: .*");
4086 qCDebug(interfaceapp) << "Physics disabled until entity loads: " << entity->getID() << entity->getName();4094 qCDebug(interfaceapp) << "Physics disabled until entity loads: " << entity->getID() << entity->getName();
@@ -4380,7 +4388,7 @@ bool Application::askToSetAvatarUrl(const QString& url) { · 293 unmodified lines
4380bool Application::askToLoadScript(const QString& scriptFilenameOrURL) {4388bool Application::askToLoadScript(const QString& scriptFilenameOrURL) {
4381 QMessageBox::StandardButton reply;4389 QMessageBox::StandardButton reply;
4382 QString message = "Would you like to run this script:\n" + scriptFilenameOrURL;4390 QString message = "Would you like to run this script:\n" + scriptFilenameOrURL;
4383 reply = QMessageBox::question(getWindow(), "Run Script", message, QMessageBox::Yes|QMessageBox::No);4391 reply = OffscreenUi::question(getWindow(), "Run Script", message, QMessageBox::Yes | QMessageBox::No);
4384 4392
4385 if (reply == QMessageBox::Yes) {4393 if (reply == QMessageBox::Yes) {
4386 qCDebug(interfaceapp) << "Chose to run the script: " << scriptFilenameOrURL;4394 qCDebug(interfaceapp) << "Chose to run the script: " << scriptFilenameOrURL;
@@ -4393,7 +4401,7 @@ bool Application::askToLoadScript(const QString& scriptFilenameOrURL) { · 6 unmodified lines
4393 4401
4394bool Application::askToUploadAsset(const QString& filename) {4402bool Application::askToUploadAsset(const QString& filename) {
4395 if (!DependencyManager::get<NodeList>()->getThisNodeCanRez()) {4403 if (!DependencyManager::get<NodeList>()->getThisNodeCanRez()) {
4396 QMessageBox::warning(_window, "Failed Upload",4404 OffscreenUi::warning(_window, "Failed Upload",
4397 QString("You don't have upload rights on that domain.\n\n"));4405 QString("You don't have upload rights on that domain.\n\n"));
4398 return false;4406 return false;
4399 }4407 }
@@ -4437,7 +4445,7 @@ bool Application::askToUploadAsset(const QString& filename) { · 37 unmodified lines
4437 }4445 }
4438 4446
4439 // display a message box with the error4447 // display a message box with the error
4440 QMessageBox::warning(_window, "Failed Upload", QString("Failed to upload %1.\n\n").arg(filename));4448 OffscreenUi::warning(_window, "Failed Upload", QString("Failed to upload %1.\n\n").arg(filename));
4441 return false;4449 return false;
4442}4450}
4443 4451
@@ -4464,6 +4472,99 @@ void Application::modelUploadFinished(AssetUpload* upload, const QString& hash) · 20 unmodified lines
4464 }4472 }
4465}4473}
4466 4474
4475bool Application::askToWearAvatarAttachmentUrl(const QString& url) {
4476
4477 QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
4478 QNetworkRequest networkRequest = QNetworkRequest(url);
4479 networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
4480 QNetworkReply* reply = networkAccessManager.get(networkRequest);
4481 int requestNumber = ++_avatarAttachmentRequest;
4482 connect(reply, &QNetworkReply::finished, [this, reply, url, requestNumber]() {
4483
4484 if (requestNumber != _avatarAttachmentRequest) {
4485 // this request has been superseded by another more recent request
4486 reply->deleteLater();
4487 return;
4488 }
4489
4490 QNetworkReply::NetworkError networkError = reply->error();
4491 if (networkError == QNetworkReply::NoError) {
4492 // download success
4493 QByteArray contents = reply->readAll();
4494
4495 QJsonParseError jsonError;
4496 auto doc = QJsonDocument::fromJson(contents, &jsonError);
4497 if (jsonError.error == QJsonParseError::NoError) {
4498
4499 auto jsonObject = doc.object();
4500
4501 // retrieve optional name field from JSON
4502 QString name = tr("Unnamed Attachment");
4503 auto nameValue = jsonObject.value("name");
4504 if (nameValue.isString()) {
4505 name = nameValue.toString();
4506 }
4507
4508 // display confirmation dialog
4509 if (displayAvatarAttachmentConfirmationDialog(name)) {
4510
4511 // add attachment to avatar
4512 auto myAvatar = getMyAvatar();
4513 assert(myAvatar);
4514 auto attachmentDataVec = myAvatar->getAttachmentData();
4515 AttachmentData attachmentData;
4516 attachmentData.fromJson(jsonObject);
4517 attachmentDataVec.push_back(attachmentData);
4518 myAvatar->setAttachmentData(attachmentDataVec);
4519
4520 } else {
4521 qCDebug(interfaceapp) << "User declined to wear the avatar attachment: " << url;
4522 }
4523
4524 } else {
4525 // json parse error
4526 auto avatarAttachmentParseErrorString = tr("Error parsing attachment JSON from url: \"%1\"");
4527 displayAvatarAttachmentWarning(avatarAttachmentParseErrorString.arg(url));
4528 }
4529 } else {
4530 // download failure
4531 auto avatarAttachmentDownloadErrorString = tr("Error downloading attachment JSON from url: \"%1\"");
4532 displayAvatarAttachmentWarning(avatarAttachmentDownloadErrorString.arg(url));
4533 }
4534 reply->deleteLater();
4535 });
4536 return true;
4537}
4538
4539void Application::displayAvatarAttachmentWarning(const QString& message) const {
4540 auto avatarAttachmentWarningTitle = tr("Avatar Attachment Failure");
4541 QMessageBox msgBox;
4542 msgBox.setIcon(QMessageBox::Warning);
4543 msgBox.setWindowTitle(avatarAttachmentWarningTitle);
4544 msgBox.setText(message);
4545 msgBox.exec();
4546 msgBox.addButton(QMessageBox::Ok);
4547 msgBox.exec();
4548}
4549
4550bool Application::displayAvatarAttachmentConfirmationDialog(const QString& name) const {
4551 auto avatarAttachmentConfirmationTitle = tr("Avatar Attachment Confirmation");
4552 auto avatarAttachmentConfirmationMessage = tr("Would you like to wear '%1' on your avatar?");
4553 QMessageBox msgBox;
4554 msgBox.setIcon(QMessageBox::Question);
4555 msgBox.setWindowTitle(avatarAttachmentConfirmationTitle);
4556 QPushButton* button = msgBox.addButton(tr("Yes"), QMessageBox::ActionRole);
4557 QString message = avatarAttachmentConfirmationMessage.arg(name);
4558 msgBox.setText(message);
4559 msgBox.addButton(QMessageBox::Cancel);
4560 msgBox.exec();
4561 if (msgBox.clickedButton() == button) {
4562 return true;
4563 } else {
4564 return false;
4565 }
4566}
4567
4467ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUserLoaded,4568ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUserLoaded,
4468 bool loadScriptFromEditor, bool activateMainWindow, bool reload) {4569 bool loadScriptFromEditor, bool activateMainWindow, bool reload) {
4469 4570
@@ -4535,7 +4636,7 @@ void Application::handleScriptEngineLoaded(const QString& scriptFilename) { · 65 unmodified lines
4535// FIXME - change to new version of ScriptCache loading notification4636// FIXME - change to new version of ScriptCache loading notification
4536void Application::handleScriptLoadError(const QString& scriptFilename) {4637void Application::handleScriptLoadError(const QString& scriptFilename) {
4537 qCDebug(interfaceapp) << "Application::loadScript(), script failed to load...";4638 qCDebug(interfaceapp) << "Application::loadScript(), script failed to load...";
4538 QMessageBox::warning(getWindow(), "Error Loading Script", scriptFilename + " failed to load.");4639 OffscreenUi::warning(getWindow(), "Error Loading Script", scriptFilename + " failed to load.");
4539}4640}
4540 4641
4541QStringList Application::getRunningScripts() {4642QStringList Application::getRunningScripts() {
@@ -4991,7 +5092,7 @@ void Application::updateDisplayMode() { · 449 unmodified lines
4991 foreach(auto displayPlugin, displayPlugins) {5092 foreach(auto displayPlugin, displayPlugins) {
4992 addDisplayPluginToMenu(displayPlugin, first);5093 addDisplayPluginToMenu(displayPlugin, first);
4993 // This must be a queued connection to avoid a deadlock5094 // This must be a queued connection to avoid a deadlock
4994 QObject::connect(displayPlugin.get(), &DisplayPlugin::requestRender, 5095 QObject::connect(displayPlugin.get(), &DisplayPlugin::requestRender,
4995 this, &Application::paintGL, Qt::QueuedConnection);5096 this, &Application::paintGL, Qt::QueuedConnection);
4996 5097
4997 QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged, [this](const QSize & size) {5098 QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged, [this](const QSize & size) {