2578 lines · 110.9 KB · C++
RawDownload
////  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.