//// Application.cpp// interface/src//// Created by Andrzej Kapolka on 5/10/13.// Copyright 2013 High Fidelity, Inc.// Copyright 2020 Vircadia contributors.// Copyright 2022-2023 Overte e.V.//// Distributed under the Apache License, Version 2.0.// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// SPDX-License-Identifier: Apache-2.0// #include "Application.h" #include <QDesktopWidget>#include <QDesktopServices>#include <QtGui/QClipboard>#include <QtNetwork/QLocalSocket>#include <QtNetwork/QLocalServer>#include <QtQuick/QQuickWindow>#include <QWidget>#include <string_view> #include <AccountManager.h>#include <AddressManager.h>#include <AnimationCacheScriptingInterface.h>#include <AnimDebugDraw.h>#include <AvatarBookmarks.h>#include <audio/AudioScope.h>#include <avatar/AvatarManager.h>#include <avatar/AvatarPackager.h>#include <avatar/GrabManager.h>#include <BuildInfo.h>#include <controllers/ScriptingInterface.h>#include <controllers/UserInputMapper.h>#include <CrashHelpers.h>#include <DebugDraw.h>#include <DesktopPreviewProvider.h>#include <display-plugins/CompositorHelper.h>#include <display-plugins/DisplayPlugin.h>#include <DomainAccountManager.h>#include <EntityScriptServerLogClient.h>#include <FramebufferCache.h>#include <gl/GLHelpers.h>#include <GPUIdent.h>#include <graphics-scripting/GraphicsScriptingInterface.h>#include <hfm/ModelFormatRegistry.h>#include <input-plugins/InputPlugin.h>#include <input-plugins/KeyboardMouseDevice.h>#include <LocationScriptingInterface.h>#include <LogHandler.h>#include <MainWindow.h>#include <MessagesClient.h>#include <material-networking/TextureCacheScriptingInterface.h>#include <model-networking/ModelCacheScriptingInterface.h>#include <networking/CloseEventSender.h>#include <OffscreenUi.h>#include <PickManager.h>#include <platform/Platform.h>#include <platform/PlatformKeys.h>#include <platform/backend/PlatformInstance.h>#include <plugins/OculusPlatformPlugin.h>#include <plugins/PluginManager.h>#include <plugins/PluginUtils.h>#include <plugins/SteamClientPlugin.h>#include <Preferences.h>#include <procedural/MaterialCacheScriptingInterface.h>#include <QmlFragmentClass.h>#include <QmlWebWindowClass.h>#include <QmlWindowClass.h>#include <raypick/PickScriptingInterface.h>#include <raypick/PointerScriptingInterface.h>#include <raypick/RayPickScriptingInterface.h>#include <recording/RecordingScriptingInterface.h>#include <render/EngineStats.h>#include <ResourceScriptingInterface.h>#include <SandboxUtils.h>#include <SceneScriptingInterface.h>#include <ScriptEngines.h>#include <scripting/AccountServicesScriptingInterface.h>#include <scripting/Audio.h>#include <scripting/ClipboardScriptingInterface.h>#include <scripting/ControllerScriptingInterface.h>#include <scripting/DesktopScriptingInterface.h>#include <scripting/HMDScriptingInterface.h>#include <scripting/KeyboardScriptingInterface.h>#include <scripting/MenuScriptingInterface.h>#include <scripting/PerformanceScriptingInterface.h>#include <scripting/PlatformInfoScriptingInterface.h>#include <scripting/RatesScriptingInterface.h>#include <scripting/RenderScriptingInterface.h>#include <scripting/SelectionScriptingInterface.h>#include <scripting/SettingsScriptingInterface.h>#include <scripting/TestScriptingInterface.h>#include <scripting/WindowScriptingInterface.h>#include <scripting/OSCScriptingInterface.h>#ifndef Q_OS_ANDROID#include <shared/FileLogger.h>#endif#include <shared/GlobalAppProperties.h>#include <shared/PlatformHelper.h>#include <shared/QtHelpers.h>#include <SoundCacheScriptingInterface.h>#include <StatTracker.h>#include <ui/AvatarInputs.h>#include <ui/AnimStats.h>#include <ui/TabletScriptingInterface.h>#include <ui/Keyboard.h>#include <ui/OctreeStatsProvider.h>#include <ui/OffscreenQmlSurfaceCache.h>#include <ui/Snapshot.h>#include <ui/SnapshotAnimated.h>#include <ui/StandAloneJSConsole.h>#include <ui/Stats.h>#include <ui/ToolbarScriptingInterface.h>#include <UserActivityLogger.h>#include <UserActivityLoggerScriptingInterface.h>#include <UsersScriptingInterface.h> #include "AboutUtil.h"#include "ApplicationEventHandler.h"#include "AudioClient.h"#include "DeadlockWatchdog.h"#include "GLCanvas.h"#include "LocationBookmarks.h"#include "LODManager.h"#include "Menu.h"#include "ResourceRequestObserver.h"#if defined(Q_OS_MAC) || defined(Q_OS_WIN)#include "SpeechRecognizer.h"#endif#include "Util.h"#ifndef USE_GL#include "vk/VKWindow.h"#endif #if defined(Q_OS_WIN)#include "WindowsSystemInfo.h" // On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU// FIXME seems to be broken.extern "C" { _declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;}#endif #if defined(Q_OS_MAC)// On Mac OS, disable App Nap to prevent audio glitches while running in the background#include "AppNapDisabler.h"static AppNapDisabler appNapDisabler; // disabled, while in scope#endif #if defined(Q_OS_ANDROID)#include <android/log.h>#endif // For processing on QThreadPool, we target a number of threads after reserving some// based on how many are being consumed by the application and the display plugin. However,// we will never drop below the 'min' valuestatic const int MIN_PROCESSING_THREAD_POOL_SIZE = 2;static const int ENTITY_SERVER_CONNECTION_TIMEOUT = 5000; const float DEFAULT_HMD_TABLET_SCALE_PERCENT = 60.0f;const float DEFAULT_DESKTOP_TABLET_SCALE_PERCENT = 75.0f;const bool DEFAULT_DESKTOP_TABLET_BECOMES_TOOLBAR = true;const bool DEFAULT_HMD_TABLET_BECOMES_TOOLBAR = false;const bool DEFAULT_PREFER_STYLUS_OVER_LASER = false;const bool DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS = false;const bool DEFAULT_MOUSE_CAPTURE_VR = false;const bool DEFAULT_SHOW_GRAPHICS_ICON = true;const bool DEFAULT_MINI_TABLET_ENABLED = false;const bool DEFAULT_AWAY_STATE_WHEN_FOCUS_LOST_IN_VR_ENABLED = true; static const quint64 TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS = 1 * USECS_PER_SECOND; static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);static const QString KEEP_ME_LOGGED_IN_SETTING_NAME = "keepMeLoggedIn";const QString DEFAULT_CURSOR_NAME = "SYSTEM"; Setting::Handle<int> sessionRunTime { "sessionRunTime", 0 }; void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { QString logMessage = LogHandler::getInstance().printMessage((LogMsgType) type, context, message); if (!logMessage.isEmpty()) {#ifdef Q_OS_ANDROID const char * local=logMessage.toStdString().c_str(); switch (type) { case QtDebugMsg: __android_log_write(ANDROID_LOG_DEBUG,"Interface",local); break; case QtInfoMsg: __android_log_write(ANDROID_LOG_INFO,"Interface",local); break; case QtWarningMsg: __android_log_write(ANDROID_LOG_WARN,"Interface",local); break; case QtCriticalMsg: __android_log_write(ANDROID_LOG_ERROR,"Interface",local); break; case QtFatalMsg: default: __android_log_write(ANDROID_LOG_FATAL,"Interface",local); abort(); }#else qApp->getLogger()->addMessage(qPrintable(logMessage));#endif }} Application::Application( int& argc, char** argv, QElapsedTimer& startupTimer) : QApplication(argc, argv),#ifdef USE_GL _window(new MainWindow(desktop())),#else _vkWindow(new VKWindow()), _vkWindowWrapper(QWidget::createWindowContainer(_vkWindow)), _window(new MainWindow(_vkWindowWrapper)),#endif // Menu needs to be initialized before other initializers. Otherwise deadlock happens on qApp->getWindow()->menuBar(). _isMenuInitialized(initMenu()),#ifndef Q_OS_ANDROID _logger(new FileLogger(this)),#endif _sessionRunTimer(startupTimer), _lastNackTime(usecTimestampNow()), _lastSendDownstreamAudioStats(usecTimestampNow()), _useDiscordPresence("useDiscordPresence", true), _firstRun(Settings::firstRun, true), _previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION), _previousPreferredDisplayMode("previousPreferredDisplayMode", 0), // UI _hmdTabletScale("hmdTabletScale", DEFAULT_HMD_TABLET_SCALE_PERCENT), _desktopTabletScale("desktopTabletScale", DEFAULT_DESKTOP_TABLET_SCALE_PERCENT), _desktopTabletBecomesToolbarSetting("desktopTabletBecomesToolbar", DEFAULT_DESKTOP_TABLET_BECOMES_TOOLBAR), _hmdTabletBecomesToolbarSetting("hmdTabletBecomesToolbar", DEFAULT_HMD_TABLET_BECOMES_TOOLBAR), _preferStylusOverLaserSetting("preferStylusOverLaser", DEFAULT_PREFER_STYLUS_OVER_LASER), _preferAvatarFingerOverStylusSetting("preferAvatarFingerOverStylus", DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS), _defaultMouseCaptureVR("defaultMouseCaptureVR", DEFAULT_MOUSE_CAPTURE_VR), _constrainToolbarPosition("toolbar/constrainToolbarToCenterX", true), _awayStateWhenFocusLostInVREnabled("awayStateWhenFocusLostInVREnabled", DEFAULT_AWAY_STATE_WHEN_FOCUS_LOST_IN_VR_ENABLED), _preferredCursor("preferredCursor", DEFAULT_CURSOR_NAME), _darkTheme("darkTheme", true), _miniTabletEnabledSetting("miniTabletEnabled", DEFAULT_MINI_TABLET_ENABLED), // Entities _maxOctreePacketsPerSecond("maxOctreePPS", DEFAULT_MAX_OCTREE_PPS), _maxOctreePPS(_maxOctreePacketsPerSecond.get()), // Camera _fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES), _cameraClippingEnabled("cameraClippingEnabled", false){ setProperty(hifi::properties::CRASHED, _previousSessionCrashed); LogHandler::getInstance().moveToThread(thread()); LogHandler::getInstance().setupRepeatedMessageFlusher(); qInstallMessageHandler(messageHandler); DependencyManager::set<PathUtils>();} Application::~Application() { // remove avatars from physics engine if (auto avatarManager = DependencyManager::get<AvatarManager>()) { // AvatarManager may not yet exist in case of an early exit avatarManager->clearOtherAvatars(); auto myCharacterController = getMyAvatar()->getCharacterController(); myCharacterController->clearDetailedMotionStates(); PhysicsEngine::Transaction transaction; avatarManager->buildPhysicsTransaction(transaction); _physicsEngine->processTransaction(transaction); avatarManager->handleProcessedPhysicsTransaction(transaction); avatarManager->deleteAllAvatars(); } if (_physicsEngine) { _physicsEngine->setCharacterController(nullptr); } // the _shapeManager should have zero references _shapeManager.collectGarbage(); assert(_shapeManager.getNumShapes() == 0); if (_graphicsEngine) { // shutdown graphics engine _graphicsEngine->shutdown(); } _gameWorkload.shutdown(); DependencyManager::destroy<Preferences>(); PlatformHelper::shutdown(); if (_entityClipboard) { _entityClipboard->eraseAllOctreeElements(); _entityClipboard.reset(); } if (_octreeProcessor) { _octreeProcessor->terminate(); } if (_entityEditSender) { _entityEditSender->terminate(); } if (auto pluginManager = PluginManager::getInstance()) { if (auto steamClient = pluginManager->getSteamClientPlugin()) { steamClient->shutdown(); } if (auto oculusPlatform = pluginManager->getOculusPlatformPlugin()) { oculusPlatform->shutdown(); } } DependencyManager::destroy<PluginManager>(); DependencyManager::destroy<CompositorHelper>(); // must be destroyed before the FramebufferCache DependencyManager::destroy<SoundCacheScriptingInterface>(); DependencyManager::destroy<AudioInjectorManager>(); DependencyManager::destroy<AvatarManager>(); DependencyManager::destroy<AnimationCacheScriptingInterface>(); DependencyManager::destroy<AnimationCache>(); DependencyManager::destroy<FramebufferCache>(); DependencyManager::destroy<MaterialCacheScriptingInterface>(); DependencyManager::destroy<MaterialCache>(); DependencyManager::destroy<TextureCacheScriptingInterface>(); DependencyManager::destroy<TextureCache>(); DependencyManager::destroy<ModelCacheScriptingInterface>(); DependencyManager::destroy<ModelCache>(); DependencyManager::destroy<ModelFormatRegistry>(); DependencyManager::destroy<ScriptCache>(); DependencyManager::destroy<SoundCacheScriptingInterface>(); DependencyManager::destroy<SoundCache>(); DependencyManager::destroy<OctreeStatsProvider>(); DependencyManager::destroy<GeometryCache>(); if (auto resourceManager = DependencyManager::get<ResourceManager>()) { resourceManager->cleanup(); } // remove the NodeList from the DependencyManager DependencyManager::destroy<NodeList>(); #if 0 ConnexionClient::getInstance().destroy();#endif // The window takes ownership of the menu, so this has the side effect of destroying it. _window->setMenuBar(nullptr); _window->deleteLater(); // make sure that the quit event has finished sending before we take the application down if (auto closeEventSender = DependencyManager::get<CloseEventSender>()) { while (!closeEventSender->hasFinishedQuitEvent() && !closeEventSender->hasTimedOutQuitEvent()) { // sleep a little so we're not spinning at 100% std::this_thread::sleep_for(std::chrono::milliseconds(10)); } // quit the thread used by the closure event sender closeEventSender->thread()->quit(); } // Can't log to file past this point, FileLogger about to be deleted qInstallMessageHandler(LogHandler::verboseMessageHandler); #ifdef Q_OS_MAC // 26 Feb 2021 - Tried re-enabling this call but OSX still crashes on exit. // // 10/16/2019 - Disabling this call. This causes known crashes (A), and it is not // fully understood whether it might cause other unknown crashes (B). // // (A) Although we try to shutdown the ScriptEngine threads in onAboutToQuit, there is // currently no guarantee that they have stopped. Waiting on them to stop has so far appeared to // never return on Mac, causing the application to hang on shutdown. Because ScriptEngines // may still be running, they may end up receiving events that are triggered from this processEvents call, // and then try to access resources that are no longer available at this point in time. // If the ScriptEngine threads were fully destroyed before getting here, this would // not be an issue. // // (B) It seems likely that a bunch of potential event handlers are dependent on Application // and other common dependencies to be available and not destroyed or in the middle of being // destroyed. // Clear the event queue before application is totally destructed. // This will drain the messasge queue of pending "deleteLaters" queued up // during shutdown of the script engines. // We do this here because there is a possiblty that [NSApplication terminate:] // will be called during processEvents which will invoke all static destructors. // We want to postpone this utill the last possible moment. //QCoreApplication::processEvents();#endif} bool Application::isServerlessMode() const { auto tree = getEntities()->getTree(); if (tree) { return tree->isServerlessMode(); } return false;} QString Application::getUserAgent() { if (QThread::currentThread() != thread()) { QString userAgent; BLOCKING_INVOKE_METHOD(this, "getUserAgent", Q_RETURN_ARG(QString, userAgent)); return userAgent; } QString userAgent = NetworkingConstants::OVERTE_USER_AGENT + "/" + BuildInfo::VERSION + "; " + QSysInfo::productType() + " " + QSysInfo::productVersion() + ")"; auto formatPluginName = [](QString name) -> QString { return name.trimmed().replace(" ", "-"); }; // For each plugin, add to userAgent const auto& displayPlugins = PluginManager::getInstance()->getDisplayPlugins(); for (const auto& dp : displayPlugins) { if (dp->isActive() && dp->isHmd()) { userAgent += " " + formatPluginName(dp->getName()); } } const auto& inputPlugins = PluginManager::getInstance()->getInputPlugins(); for (const auto& ip : inputPlugins) { if (ip->isActive()) { userAgent += " " + formatPluginName(ip->getName()); } } // for codecs, we include all of them, even if not active const auto& codecPlugins = PluginManager::getInstance()->getCodecPlugins(); for (const auto& cp : codecPlugins) { userAgent += " " + formatPluginName(cp->getName()); } return userAgent;} static const QString CONTENT_SET_NAME_QUERY_PARAM = "name";void Application::replaceDomainContent(const QString& url, const QString& itemName) { qCDebug(interfaceapp) << "Attempting to replace domain content"; QUrl msgUrl(url); QUrlQuery urlQuery(msgUrl.query()); urlQuery.addQueryItem(CONTENT_SET_NAME_QUERY_PARAM, itemName); msgUrl.setQuery(urlQuery.query(QUrl::QUrl::FullyEncoded)); QByteArray urlData(msgUrl.toString(QUrl::QUrl::FullyEncoded).toUtf8()); auto limitedNodeList = DependencyManager::get<NodeList>(); const auto& domainHandler = limitedNodeList->getDomainHandler(); auto octreeFilePacket = NLPacket::create(PacketType::DomainContentReplacementFromUrl, urlData.size(), true); octreeFilePacket->write(urlData); limitedNodeList->sendPacket(std::move(octreeFilePacket), domainHandler.getSockAddr()); auto addressManager = DependencyManager::get<AddressManager>(); addressManager->handleLookupString(DOMAIN_SPAWNING_POINT); QString newHomeAddress = addressManager->getHost() + DOMAIN_SPAWNING_POINT; qCDebug(interfaceapp) << "Setting new home bookmark to: " << newHomeAddress; DependencyManager::get<LocationBookmarks>()->setHomeLocationToAddress(newHomeAddress);} void Application::openDirectory(const QString& path) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "openDirectory", Q_ARG(const QString&, path)); return; } QString dirPath = path;#if defined(Q_OS_WIN) const QString FILE_SCHEME = "file:///";#else const QString FILE_SCHEME = "file://";#endif if (dirPath.startsWith(FILE_SCHEME)) { dirPath.remove(0, FILE_SCHEME.length()); } QFileInfo fileInfo(dirPath); if (fileInfo.isDir()) { auto scheme = QUrl(path).scheme(); QDesktopServices::unsetUrlHandler(scheme); QDesktopServices::openUrl(path); QDesktopServices::setUrlHandler(scheme, this, "showUrlHandler"); }} void Application::forceLoginWithTokens(const QString& tokens) { DependencyManager::get<AccountManager>()->setAccessTokens(tokens); Setting::Handle<bool>(KEEP_ME_LOGGED_IN_SETTING_NAME, true).set(true);}Lines 1–500 of 2578. Load more
// // Application.cpp // interface/src // // Created by Andrzej Kapolka on 5/10/13. // Copyright 2013 High Fidelity, Inc. // Copyright 2020 Vircadia contributors. // Copyright 2022-2023 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // SPDX-License-Identifier: Apache-2.0 // #include "Application.h" #include <QDesktopWidget> #include <QDesktopServices> #include <QtGui/QClipboard> #include <QtNetwork/QLocalSocket> #include <QtNetwork/QLocalServer> #include <QtQuick/QQuickWindow> #include <QWidget> #include <string_view> #include <AccountManager.h> #include <AddressManager.h> #include <AnimationCacheScriptingInterface.h> #include <AnimDebugDraw.h> #include <AvatarBookmarks.h> #include <audio/AudioScope.h> #include <avatar/AvatarManager.h> #include <avatar/AvatarPackager.h> #include <avatar/GrabManager.h> #include <BuildInfo.h> #include <controllers/ScriptingInterface.h> #include <controllers/UserInputMapper.h> #include <CrashHelpers.h> #include <DebugDraw.h> #include <DesktopPreviewProvider.h> #include <display-plugins/CompositorHelper.h> #include <display-plugins/DisplayPlugin.h> #include <DomainAccountManager.h> #include <EntityScriptServerLogClient.h> #include <FramebufferCache.h> #include <gl/GLHelpers.h> #include <GPUIdent.h> #include <graphics-scripting/GraphicsScriptingInterface.h> #include <hfm/ModelFormatRegistry.h> #include <input-plugins/InputPlugin.h> #include <input-plugins/KeyboardMouseDevice.h> #include <LocationScriptingInterface.h> #include <LogHandler.h> #include <MainWindow.h> #include <MessagesClient.h> #include <material-networking/TextureCacheScriptingInterface.h> #include <model-networking/ModelCacheScriptingInterface.h> #include <networking/CloseEventSender.h> #include <OffscreenUi.h> #include <PickManager.h> #include <platform/Platform.h> #include <platform/PlatformKeys.h> #include <platform/backend/PlatformInstance.h> #include <plugins/OculusPlatformPlugin.h> #include <plugins/PluginManager.h> #include <plugins/PluginUtils.h> #include <plugins/SteamClientPlugin.h> #include <Preferences.h> #include <procedural/MaterialCacheScriptingInterface.h> #include <QmlFragmentClass.h> #include <QmlWebWindowClass.h> #include <QmlWindowClass.h> #include <raypick/PickScriptingInterface.h> #include <raypick/PointerScriptingInterface.h> #include <raypick/RayPickScriptingInterface.h> #include <recording/RecordingScriptingInterface.h> #include <render/EngineStats.h> #include <ResourceScriptingInterface.h> #include <SandboxUtils.h> #include <SceneScriptingInterface.h> #include <ScriptEngines.h> #include <scripting/AccountServicesScriptingInterface.h> #include <scripting/Audio.h> #include <scripting/ClipboardScriptingInterface.h> #include <scripting/ControllerScriptingInterface.h> #include <scripting/DesktopScriptingInterface.h> #include <scripting/HMDScriptingInterface.h> #include <scripting/KeyboardScriptingInterface.h> #include <scripting/MenuScriptingInterface.h> #include <scripting/PerformanceScriptingInterface.h> #include <scripting/PlatformInfoScriptingInterface.h> #include <scripting/RatesScriptingInterface.h> #include <scripting/RenderScriptingInterface.h> #include <scripting/SelectionScriptingInterface.h> #include <scripting/SettingsScriptingInterface.h> #include <scripting/TestScriptingInterface.h> #include <scripting/WindowScriptingInterface.h> #include <scripting/OSCScriptingInterface.h> #ifndef Q_OS_ANDROID #include <shared/FileLogger.h> #endif #include <shared/GlobalAppProperties.h> #include <shared/PlatformHelper.h> #include <shared/QtHelpers.h> #include <SoundCacheScriptingInterface.h> #include <StatTracker.h> #include <ui/AvatarInputs.h> #include <ui/AnimStats.h> #include <ui/TabletScriptingInterface.h> #include <ui/Keyboard.h> #include <ui/OctreeStatsProvider.h> #include <ui/OffscreenQmlSurfaceCache.h> #include <ui/Snapshot.h> #include <ui/SnapshotAnimated.h> #include <ui/StandAloneJSConsole.h> #include <ui/Stats.h> #include <ui/ToolbarScriptingInterface.h> #include <UserActivityLogger.h> #include <UserActivityLoggerScriptingInterface.h> #include <UsersScriptingInterface.h> #include "AboutUtil.h" #include "ApplicationEventHandler.h" #include "AudioClient.h" #include "DeadlockWatchdog.h" #include "GLCanvas.h" #include "LocationBookmarks.h" #include "LODManager.h" #include "Menu.h" #include "ResourceRequestObserver.h" #if defined(Q_OS_MAC) || defined(Q_OS_WIN) #include "SpeechRecognizer.h" #endif #include "Util.h" #ifndef USE_GL #include "vk/VKWindow.h" #endif #if defined(Q_OS_WIN) #include "WindowsSystemInfo.h" // On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU // FIXME seems to be broken. extern "C" { _declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; } #endif #if defined(Q_OS_MAC) // On Mac OS, disable App Nap to prevent audio glitches while running in the background #include "AppNapDisabler.h" static AppNapDisabler appNapDisabler; // disabled, while in scope #endif #if defined(Q_OS_ANDROID) #include <android/log.h> #endif // For processing on QThreadPool, we target a number of threads after reserving some // based on how many are being consumed by the application and the display plugin. However, // we will never drop below the 'min' value static const int MIN_PROCESSING_THREAD_POOL_SIZE = 2; static const int ENTITY_SERVER_CONNECTION_TIMEOUT = 5000; const float DEFAULT_HMD_TABLET_SCALE_PERCENT = 60.0f; const float DEFAULT_DESKTOP_TABLET_SCALE_PERCENT = 75.0f; const bool DEFAULT_DESKTOP_TABLET_BECOMES_TOOLBAR = true; const bool DEFAULT_HMD_TABLET_BECOMES_TOOLBAR = false; const bool DEFAULT_PREFER_STYLUS_OVER_LASER = false; const bool DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS = false; const bool DEFAULT_MOUSE_CAPTURE_VR = false; const bool DEFAULT_SHOW_GRAPHICS_ICON = true; const bool DEFAULT_MINI_TABLET_ENABLED = false; const bool DEFAULT_AWAY_STATE_WHEN_FOCUS_LOST_IN_VR_ENABLED = true; static const quint64 TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS = 1 * USECS_PER_SECOND; static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); static const QString KEEP_ME_LOGGED_IN_SETTING_NAME = "keepMeLoggedIn"; const QString DEFAULT_CURSOR_NAME = "SYSTEM"; Setting::Handle<int> sessionRunTime { "sessionRunTime", 0 }; void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { QString logMessage = LogHandler::getInstance().printMessage((LogMsgType) type, context, message); if (!logMessage.isEmpty()) { #ifdef Q_OS_ANDROID const char * local=logMessage.toStdString().c_str(); switch (type) { case QtDebugMsg: __android_log_write(ANDROID_LOG_DEBUG,"Interface",local); break; case QtInfoMsg: __android_log_write(ANDROID_LOG_INFO,"Interface",local); break; case QtWarningMsg: __android_log_write(ANDROID_LOG_WARN,"Interface",local); break; case QtCriticalMsg: __android_log_write(ANDROID_LOG_ERROR,"Interface",local); break; case QtFatalMsg: default: __android_log_write(ANDROID_LOG_FATAL,"Interface",local); abort(); } #else qApp->getLogger()->addMessage(qPrintable(logMessage)); #endif } } Application::Application( int& argc, char** argv, QElapsedTimer& startupTimer ) : QApplication(argc, argv), #ifdef USE_GL _window(new MainWindow(desktop())), #else _vkWindow(new VKWindow()), _vkWindowWrapper(QWidget::createWindowContainer(_vkWindow)), _window(new MainWindow(_vkWindowWrapper)), #endif // Menu needs to be initialized before other initializers. Otherwise deadlock happens on qApp->getWindow()->menuBar(). _isMenuInitialized(initMenu()), #ifndef Q_OS_ANDROID _logger(new FileLogger(this)), #endif _sessionRunTimer(startupTimer), _lastNackTime(usecTimestampNow()), _lastSendDownstreamAudioStats(usecTimestampNow()), _useDiscordPresence("useDiscordPresence", true), _firstRun(Settings::firstRun, true), _previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION), _previousPreferredDisplayMode("previousPreferredDisplayMode", 0), // UI _hmdTabletScale("hmdTabletScale", DEFAULT_HMD_TABLET_SCALE_PERCENT), _desktopTabletScale("desktopTabletScale", DEFAULT_DESKTOP_TABLET_SCALE_PERCENT), _desktopTabletBecomesToolbarSetting("desktopTabletBecomesToolbar", DEFAULT_DESKTOP_TABLET_BECOMES_TOOLBAR), _hmdTabletBecomesToolbarSetting("hmdTabletBecomesToolbar", DEFAULT_HMD_TABLET_BECOMES_TOOLBAR), _preferStylusOverLaserSetting("preferStylusOverLaser", DEFAULT_PREFER_STYLUS_OVER_LASER), _preferAvatarFingerOverStylusSetting("preferAvatarFingerOverStylus", DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS), _defaultMouseCaptureVR("defaultMouseCaptureVR", DEFAULT_MOUSE_CAPTURE_VR), _constrainToolbarPosition("toolbar/constrainToolbarToCenterX", true), _awayStateWhenFocusLostInVREnabled("awayStateWhenFocusLostInVREnabled", DEFAULT_AWAY_STATE_WHEN_FOCUS_LOST_IN_VR_ENABLED), _preferredCursor("preferredCursor", DEFAULT_CURSOR_NAME), _darkTheme("darkTheme", true), _miniTabletEnabledSetting("miniTabletEnabled", DEFAULT_MINI_TABLET_ENABLED), // Entities _maxOctreePacketsPerSecond("maxOctreePPS", DEFAULT_MAX_OCTREE_PPS), _maxOctreePPS(_maxOctreePacketsPerSecond.get()), // Camera _fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES), _cameraClippingEnabled("cameraClippingEnabled", false) { setProperty(hifi::properties::CRASHED, _previousSessionCrashed); LogHandler::getInstance().moveToThread(thread()); LogHandler::getInstance().setupRepeatedMessageFlusher(); qInstallMessageHandler(messageHandler); DependencyManager::set<PathUtils>(); } Application::~Application() { // remove avatars from physics engine if (auto avatarManager = DependencyManager::get<AvatarManager>()) { // AvatarManager may not yet exist in case of an early exit avatarManager->clearOtherAvatars(); auto myCharacterController = getMyAvatar()->getCharacterController(); myCharacterController->clearDetailedMotionStates(); PhysicsEngine::Transaction transaction; avatarManager->buildPhysicsTransaction(transaction); _physicsEngine->processTransaction(transaction); avatarManager->handleProcessedPhysicsTransaction(transaction); avatarManager->deleteAllAvatars(); } if (_physicsEngine) { _physicsEngine->setCharacterController(nullptr); } // the _shapeManager should have zero references _shapeManager.collectGarbage(); assert(_shapeManager.getNumShapes() == 0); if (_graphicsEngine) { // shutdown graphics engine _graphicsEngine->shutdown(); } _gameWorkload.shutdown(); DependencyManager::destroy<Preferences>(); PlatformHelper::shutdown(); if (_entityClipboard) { _entityClipboard->eraseAllOctreeElements(); _entityClipboard.reset(); } if (_octreeProcessor) { _octreeProcessor->terminate(); } if (_entityEditSender) { _entityEditSender->terminate(); } if (auto pluginManager = PluginManager::getInstance()) { if (auto steamClient = pluginManager->getSteamClientPlugin()) { steamClient->shutdown(); } if (auto oculusPlatform = pluginManager->getOculusPlatformPlugin()) { oculusPlatform->shutdown(); } } DependencyManager::destroy<PluginManager>(); DependencyManager::destroy<CompositorHelper>(); // must be destroyed before the FramebufferCache DependencyManager::destroy<SoundCacheScriptingInterface>(); DependencyManager::destroy<AudioInjectorManager>(); DependencyManager::destroy<AvatarManager>(); DependencyManager::destroy<AnimationCacheScriptingInterface>(); DependencyManager::destroy<AnimationCache>(); DependencyManager::destroy<FramebufferCache>(); DependencyManager::destroy<MaterialCacheScriptingInterface>(); DependencyManager::destroy<MaterialCache>(); DependencyManager::destroy<TextureCacheScriptingInterface>(); DependencyManager::destroy<TextureCache>(); DependencyManager::destroy<ModelCacheScriptingInterface>(); DependencyManager::destroy<ModelCache>(); DependencyManager::destroy<ModelFormatRegistry>(); DependencyManager::destroy<ScriptCache>(); DependencyManager::destroy<SoundCacheScriptingInterface>(); DependencyManager::destroy<SoundCache>(); DependencyManager::destroy<OctreeStatsProvider>(); DependencyManager::destroy<GeometryCache>(); if (auto resourceManager = DependencyManager::get<ResourceManager>()) { resourceManager->cleanup(); } // remove the NodeList from the DependencyManager DependencyManager::destroy<NodeList>(); #if 0 ConnexionClient::getInstance().destroy(); #endif // The window takes ownership of the menu, so this has the side effect of destroying it. _window->setMenuBar(nullptr); _window->deleteLater(); // make sure that the quit event has finished sending before we take the application down if (auto closeEventSender = DependencyManager::get<CloseEventSender>()) { while (!closeEventSender->hasFinishedQuitEvent() && !closeEventSender->hasTimedOutQuitEvent()) { // sleep a little so we're not spinning at 100% std::this_thread::sleep_for(std::chrono::milliseconds(10)); } // quit the thread used by the closure event sender closeEventSender->thread()->quit(); } // Can't log to file past this point, FileLogger about to be deleted qInstallMessageHandler(LogHandler::verboseMessageHandler); #ifdef Q_OS_MAC // 26 Feb 2021 - Tried re-enabling this call but OSX still crashes on exit. // // 10/16/2019 - Disabling this call. This causes known crashes (A), and it is not // fully understood whether it might cause other unknown crashes (B). // // (A) Although we try to shutdown the ScriptEngine threads in onAboutToQuit, there is // currently no guarantee that they have stopped. Waiting on them to stop has so far appeared to // never return on Mac, causing the application to hang on shutdown. Because ScriptEngines // may still be running, they may end up receiving events that are triggered from this processEvents call, // and then try to access resources that are no longer available at this point in time. // If the ScriptEngine threads were fully destroyed before getting here, this would // not be an issue. // // (B) It seems likely that a bunch of potential event handlers are dependent on Application // and other common dependencies to be available and not destroyed or in the middle of being // destroyed. // Clear the event queue before application is totally destructed. // This will drain the messasge queue of pending "deleteLaters" queued up // during shutdown of the script engines. // We do this here because there is a possiblty that [NSApplication terminate:] // will be called during processEvents which will invoke all static destructors. // We want to postpone this utill the last possible moment. //QCoreApplication::processEvents(); #endif } bool Application::isServerlessMode() const { auto tree = getEntities()->getTree(); if (tree) { return tree->isServerlessMode(); } return false; } QString Application::getUserAgent() { if (QThread::currentThread() != thread()) { QString userAgent; BLOCKING_INVOKE_METHOD(this, "getUserAgent", Q_RETURN_ARG(QString, userAgent)); return userAgent; } QString userAgent = NetworkingConstants::OVERTE_USER_AGENT + "/" + BuildInfo::VERSION + "; " + QSysInfo::productType() + " " + QSysInfo::productVersion() + ")"; auto formatPluginName = [](QString name) -> QString { return name.trimmed().replace(" ", "-"); }; // For each plugin, add to userAgent const auto& displayPlugins = PluginManager::getInstance()->getDisplayPlugins(); for (const auto& dp : displayPlugins) { if (dp->isActive() && dp->isHmd()) { userAgent += " " + formatPluginName(dp->getName()); } } const auto& inputPlugins = PluginManager::getInstance()->getInputPlugins(); for (const auto& ip : inputPlugins) { if (ip->isActive()) { userAgent += " " + formatPluginName(ip->getName()); } } // for codecs, we include all of them, even if not active const auto& codecPlugins = PluginManager::getInstance()->getCodecPlugins(); for (const auto& cp : codecPlugins) { userAgent += " " + formatPluginName(cp->getName()); } return userAgent; } static const QString CONTENT_SET_NAME_QUERY_PARAM = "name"; void Application::replaceDomainContent(const QString& url, const QString& itemName) { qCDebug(interfaceapp) << "Attempting to replace domain content"; QUrl msgUrl(url); QUrlQuery urlQuery(msgUrl.query()); urlQuery.addQueryItem(CONTENT_SET_NAME_QUERY_PARAM, itemName); msgUrl.setQuery(urlQuery.query(QUrl::QUrl::FullyEncoded)); QByteArray urlData(msgUrl.toString(QUrl::QUrl::FullyEncoded).toUtf8()); auto limitedNodeList = DependencyManager::get<NodeList>(); const auto& domainHandler = limitedNodeList->getDomainHandler(); auto octreeFilePacket = NLPacket::create(PacketType::DomainContentReplacementFromUrl, urlData.size(), true); octreeFilePacket->write(urlData); limitedNodeList->sendPacket(std::move(octreeFilePacket), domainHandler.getSockAddr()); auto addressManager = DependencyManager::get<AddressManager>(); addressManager->handleLookupString(DOMAIN_SPAWNING_POINT); QString newHomeAddress = addressManager->getHost() + DOMAIN_SPAWNING_POINT; qCDebug(interfaceapp) << "Setting new home bookmark to: " << newHomeAddress; DependencyManager::get<LocationBookmarks>()->setHomeLocationToAddress(newHomeAddress); } void Application::openDirectory(const QString& path) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "openDirectory", Q_ARG(const QString&, path)); return; } QString dirPath = path; #if defined(Q_OS_WIN) const QString FILE_SCHEME = "file:///"; #else const QString FILE_SCHEME = "file://"; #endif if (dirPath.startsWith(FILE_SCHEME)) { dirPath.remove(0, FILE_SCHEME.length()); } QFileInfo fileInfo(dirPath); if (fileInfo.isDir()) { auto scheme = QUrl(path).scheme(); QDesktopServices::unsetUrlHandler(scheme); QDesktopServices::openUrl(path); QDesktopServices::setUrlHandler(scheme, this, "showUrlHandler"); } } void Application::forceLoginWithTokens(const QString& tokens) { DependencyManager::get<AccountManager>()->setAccessTokens(tokens); Setting::Handle<bool>(KEEP_ME_LOGGED_IN_SETTING_NAME, true).set(true); }