Sam GondelmanMerge with master19ec08011 years agoHistory
4770 lines · 188.7 KB · C++
RawDownload
////  Application.cpp//  interface/src////  Created by Andrzej Kapolka on 5/10/13.//  Copyright 2013 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 <sstream> #include <stdlib.h>#include <cmath>#include <math.h> #include <glm/glm.hpp>#include <glm/gtx/component_wise.hpp>#include <glm/gtx/quaternion.hpp>#include <glm/gtx/vector_angle.hpp>#include <glm/gtc/type_ptr.hpp> // include this before QGLWidget, which includes an earlier version of OpenGL#include "InterfaceConfig.h" #include <QAbstractNativeEventFilter>#include <QActionGroup>#include <QColorDialog>#include <QCoreApplication>#include <QDesktopWidget>#include <QCheckBox>#include <QImage>#include <QInputDialog>#include <QKeyEvent>#include <QMenuBar>#include <QMouseEvent>#include <QNetworkReply>#include <QNetworkDiskCache>#include <QObject>#include <QWheelEvent>#include <QScreen>#include <QShortcut>#include <QSystemTrayIcon>#include <QTimer>#include <QUrl>#include <QWindow>#include <QtDebug>#include <QFileDialog>#include <QDesktopServices>#include <QXmlStreamReader>#include <QXmlStreamAttributes>#include <QMediaPlayer>#include <QMimeData>#include <QMessageBox>#include <QJsonDocument> #include <AccountManager.h>#include <AddressManager.h>#include <CursorManager.h>#include <AmbientOcclusionEffect.h>#include <AudioInjector.h>#include <AutoUpdater.h>#include <DeferredLightingEffect.h>#include <DependencyManager.h>#include <EntityScriptingInterface.h>#include <ErrorDialog.h>#include <GlowEffect.h>#include <gpu/Batch.h>#include <gpu/Context.h>#include <gpu/GLBackend.h>#include <HFActionEvent.h>#include <HFBackEvent.h>#include <InfoView.h>#include <LogHandler.h>#include <MainWindow.h>#include <MessageDialog.h>#include <ModelEntityItem.h>#include <NetworkAccessManager.h>#include <NetworkingConstants.h>#include <ObjectMotionState.h>#include <OctalCode.h>#include <OctreeSceneStats.h>#include <PacketHeaders.h>#include <PathUtils.h>#include <PerfStat.h>#include <PhysicsEngine.h>#include <ProgramObject.h>#include <RenderDeferredTask.h>#include <ResourceCache.h>#include <SceneScriptingInterface.h>#include <ScriptCache.h>#include <SettingHandle.h>#include <SoundCache.h>#include <TextRenderer.h>#include <Tooltip.h>#include <UserActivityLogger.h>#include <UUID.h>#include <VrMenu.h> #include "Application.h"#include "AudioClient.h"#include "DiscoverabilityManager.h"#include "InterfaceVersion.h"#include "LODManager.h"#include "Menu.h"#include "ModelPackager.h"#include "Util.h"#include "InterfaceLogging.h"#include "InterfaceActionFactory.h" #include "avatar/AvatarManager.h" #include "audio/AudioIOStatsRenderer.h"#include "audio/AudioScope.h" #include "devices/DdeFaceTracker.h"#include "devices/Faceshift.h"#include "devices/Leapmotion.h"#include "devices/RealSense.h"#include "devices/SDL2Manager.h"#include "devices/MIDIManager.h"#include "devices/OculusManager.h"#include "devices/TV3DManager.h" #include "scripting/AccountScriptingInterface.h"#include "scripting/AudioDeviceScriptingInterface.h"#include "scripting/ClipboardScriptingInterface.h"#include "scripting/HMDScriptingInterface.h"#include "scripting/GlobalServicesScriptingInterface.h"#include "scripting/LocationScriptingInterface.h"#include "scripting/MenuScriptingInterface.h"#include "scripting/SettingsScriptingInterface.h"#include "scripting/WindowScriptingInterface.h"#include "scripting/WebWindowClass.h" #if defined(Q_OS_MAC) || defined(Q_OS_WIN)#include "SpeechRecognizer.h"#endif #include "ui/AvatarInputs.h"#include "ui/DataWebDialog.h"#include "ui/DialogsManager.h"#include "ui/LoginDialog.h"#include "ui/Snapshot.h"#include "ui/StandAloneJSConsole.h"#include "ui/Stats.h"#include "ui/AddressBarDialog.h"#include "ui/UpdateDialog.h"  // ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU#if defined(Q_OS_WIN)extern "C" { _declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;}#endif enum CustomEventTypes {    Lambda = QEvent::User + 1}; class LambdaEvent : public QEvent {    std::function<void()> _fun;public:    LambdaEvent(const std::function<void()> & fun) :        QEvent(static_cast<QEvent::Type>(Lambda)), _fun(fun) {    }    LambdaEvent(std::function<void()> && fun) :        QEvent(static_cast<QEvent::Type>(Lambda)), _fun(fun) {    }    void call() { _fun(); }};  using namespace std; //  Starfield informationstatic unsigned STARFIELD_NUM_STARS = 50000;static unsigned STARFIELD_SEED = 1; const qint64 MAXIMUM_CACHE_SIZE = 10 * BYTES_PER_GIGABYTES;  // 10GB static QTimer* locationUpdateTimer = NULL;static QTimer* balanceUpdateTimer = NULL;static QTimer* identityPacketTimer = NULL;static QTimer* billboardPacketTimer = NULL;static QTimer* checkFPStimer = NULL;static QTimer* idleTimer = NULL; const QString CHECK_VERSION_URL = "https://highfidelity.com/latestVersion.xml";const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/hifi.skipversion"; const QString DEFAULT_SCRIPTS_JS_URL = "http://s3.amazonaws.com/hifi-public/scripts/defaultScripts.js";Setting::Handle<int> maxOctreePacketsPerSecond("maxOctreePPS", DEFAULT_MAX_OCTREE_PPS); #ifdef Q_OS_WINclass MyNativeEventFilter : public QAbstractNativeEventFilter {public:    static MyNativeEventFilter& getInstance() {        static MyNativeEventFilter staticInstance;        return staticInstance;    }     bool nativeEventFilter(const QByteArray &eventType, void* msg, long* result) Q_DECL_OVERRIDE {        if (eventType == "windows_generic_MSG") {            MSG* message = (MSG*)msg;             if (message->message == UWM_IDENTIFY_INSTANCES) {                *result = UWM_IDENTIFY_INSTANCES;                return true;            }             if (message->message == UWM_SHOW_APPLICATION) {                MainWindow* applicationWindow = Application::getInstance()->getWindow();                if (applicationWindow->isMinimized()) {                    applicationWindow->showNormal();  // Restores to windowed or maximized state appropriately.                }                Application::getInstance()->setActiveWindow(applicationWindow);  // Flashes the taskbar icon if not focus.                return true;            }             if (message->message == WM_COPYDATA) {                COPYDATASTRUCT* pcds = (COPYDATASTRUCT*)(message->lParam);                QUrl url = QUrl((const char*)(pcds->lpData));                if (url.isValid() && url.scheme() == HIFI_URL_SCHEME) {                    DependencyManager::get<AddressManager>()->handleLookupString(url.toString());                    return true;                }            }        }        return false;    }};#endif 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_WIN        OutputDebugStringA(logMessage.toLocal8Bit().constData());        OutputDebugStringA("\n");#endif        Application::getInstance()->getLogger()->addMessage(qPrintable(logMessage + "\n"));    }} bool setupEssentials(int& argc, char** argv) {    unsigned int listenPort = 0; // bind to an ephemeral port by default    const char** constArgv = const_cast<const char**>(argv);    const char* portStr = getCmdOption(argc, constArgv, "--listenPort");    if (portStr) {        listenPort = atoi(portStr);    }    // Set build version    QCoreApplication::setApplicationVersion(BUILD_VERSION);     DependencyManager::registerInheritance<LimitedNodeList, NodeList>();    DependencyManager::registerInheritance<AvatarHashMap, AvatarManager>();    DependencyManager::registerInheritance<EntityActionFactoryInterface, InterfaceActionFactory>();     Setting::init();     // Set dependencies    auto addressManager = DependencyManager::set<AddressManager>();    auto nodeList = DependencyManager::set<NodeList>(NodeType::Agent, listenPort);    auto geometryCache = DependencyManager::set<GeometryCache>();    auto scriptCache = DependencyManager::set<ScriptCache>();    auto soundCache = DependencyManager::set<SoundCache>();    auto glowEffect = DependencyManager::set<GlowEffect>();    auto faceshift = DependencyManager::set<Faceshift>();    auto audio = DependencyManager::set<AudioClient>();    auto audioScope = DependencyManager::set<AudioScope>();    auto audioIOStatsRenderer = DependencyManager::set<AudioIOStatsRenderer>();    auto deferredLightingEffect = DependencyManager::set<DeferredLightingEffect>();    auto ambientOcclusionEffect = DependencyManager::set<AmbientOcclusionEffect>();    auto textureCache = DependencyManager::set<TextureCache>();    auto animationCache = DependencyManager::set<AnimationCache>();    auto ddeFaceTracker = DependencyManager::set<DdeFaceTracker>();    auto modelBlender = DependencyManager::set<ModelBlender>();    auto avatarManager = DependencyManager::set<AvatarManager>();    auto lodManager = DependencyManager::set<LODManager>();    auto jsConsole = DependencyManager::set<StandAloneJSConsole>();    auto dialogsManager = DependencyManager::set<DialogsManager>();    auto bandwidthRecorder = DependencyManager::set<BandwidthRecorder>();    auto resourceCacheSharedItems = DependencyManager::set<ResourceCacheSharedItems>();    auto entityScriptingInterface = DependencyManager::set<EntityScriptingInterface>();    auto windowScriptingInterface = DependencyManager::set<WindowScriptingInterface>();#if defined(Q_OS_MAC) || defined(Q_OS_WIN)    auto speechRecognizer = DependencyManager::set<SpeechRecognizer>();#endif    auto discoverabilityManager = DependencyManager::set<DiscoverabilityManager>();    auto sceneScriptingInterface = DependencyManager::set<SceneScriptingInterface>();    auto offscreenUi = DependencyManager::set<OffscreenUi>();    auto autoUpdater = DependencyManager::set<AutoUpdater>();    auto pathUtils = DependencyManager::set<PathUtils>();    auto actionFactory = DependencyManager::set<InterfaceActionFactory>();     return true;} Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :        QApplication(argc, argv),        _dependencyManagerIsSetup(setupEssentials(argc, argv)),        _window(new MainWindow(desktop())),        _toolWindow(NULL),        _friendsWindow(NULL),        _datagramProcessor(),        _undoStack(),        _undoStackScriptingInterface(&_undoStack),        _frameCount(0),        _fps(60.0f),        _justStarted(true),        _physicsEngine(glm::vec3(0.0f)),        _entities(true, this, this),        _entityClipboardRenderer(false, this, this),        _entityClipboard(),        _viewFrustum(),        _lastQueriedViewFrustum(),        _lastQueriedTime(usecTimestampNow()),        _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)),        _firstRun("firstRun", true),        _previousScriptLocation("LastScriptLocation"),        _scriptsLocationHandle("scriptsLocation"),        _fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES),        _viewTransform(),        _scaleMirror(1.0f),        _rotateMirror(0.0f),        _raiseMirror(0.0f),        _cursorVisible(true),        _lastMouseMove(usecTimestampNow()),        _lastMouseMoveWasSimulated(false),        _isTouchPressed(false),        _mousePressed(false),        _enableProcessOctreeThread(true),        _octreeProcessor(),        _nodeBoundsDisplay(this),        _runningScriptsWidget(NULL),        _runningScriptsWidgetWasVisible(false),        _trayIcon(new QSystemTrayIcon(_window)),        _lastNackTime(usecTimestampNow()),        _lastSendDownstreamAudioStats(usecTimestampNow()),        _isVSyncOn(true),        _aboutToQuit(false),        _notifiedPacketVersionMismatchThisDomain(false),        _domainConnectionRefusals(QList<QString>()),        _maxOctreePPS(maxOctreePacketsPerSecond.get()),        _lastFaceTrackerUpdate(0),        _applicationOverlay(){    setInstance(this);#ifdef Q_OS_WIN    installNativeEventFilter(&MyNativeEventFilter::getInstance());#endif     _logger = new FileLogger(this);  // After setting organization name in order to get correct directory     qInstallMessageHandler(messageHandler);     QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "styles/Inconsolata.otf");    _window->setWindowTitle("Interface");     Model::setAbstractViewStateInterface(this); // The model class will sometimes need to know view state details from us     auto nodeList = DependencyManager::get<NodeList>();     _myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();     _applicationStartupTime = startup_time;     qCDebug(interfaceapp) << "[VERSION] Build sequence: " << qPrintable(applicationVersion());     _bookmarks = new Bookmarks();  // Before setting up the menu     _runningScriptsWidget = new RunningScriptsWidget(_window);    _renderEngine->addTask(render::TaskPointer(new RenderDeferredTask()));    _renderEngine->registerScene(_main3DScene);          // start the nodeThread so its event loop is running    QThread* nodeThread = new QThread(this);    nodeThread->setObjectName("Datagram Processor Thread");    nodeThread->start();     // make sure the node thread is given highest priority    nodeThread->setPriority(QThread::TimeCriticalPriority);     _datagramProcessor = new DatagramProcessor(nodeList.data());     // have the NodeList use deleteLater from DM customDeleter    nodeList->setCustomDeleter([](Dependency* dependency) {        static_cast<NodeList*>(dependency)->deleteLater();    });     // put the NodeList and datagram processing on the node thread    nodeList->moveToThread(nodeThread);     // geometry background downloads need to happen on the Datagram Processor Thread.  The idle loop will    // emit checkBackgroundDownloads to cause the GeometryCache to check it's queue for requested background    // downloads.    QSharedPointer<GeometryCache> geometryCacheP = DependencyManager::get<GeometryCache>();    ResourceCache* geometryCache = geometryCacheP.data();    connect(this, &Application::checkBackgroundDownloads, geometryCache, &ResourceCache::checkAsynchronousGets);     // connect the DataProcessor processDatagrams slot to the QUDPSocket readyRead() signal    connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, _datagramProcessor, &DatagramProcessor::processDatagrams);     // put the audio processing on a separate thread    QThread* audioThread = new QThread();    audioThread->setObjectName("Audio Thread");     auto audioIO = DependencyManager::get<AudioClient>();     audioIO->setPositionGetter(getPositionForAudio);    audioIO->setOrientationGetter(getOrientationForAudio);     audioIO->moveToThread(audioThread);    connect(audioThread, &QThread::started, audioIO.data(), &AudioClient::start);    connect(audioIO.data(), &AudioClient::destroyed, audioThread, &QThread::quit);    connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater);    connect(audioIO.data(), &AudioClient::muteToggled, this, &Application::audioMuteToggled);    connect(audioIO.data(), &AudioClient::receivedFirstPacket,            &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::receivedFirstPacket);    connect(audioIO.data(), &AudioClient::disconnected,            &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::disconnected);     audioThread->start();     const DomainHandler& domainHandler = nodeList->getDomainHandler();     connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));    connect(&domainHandler, SIGNAL(connectedToDomain(const QString&)), SLOT(connectedToDomain(const QString&)));    connect(&domainHandler, SIGNAL(connectedToDomain(const QString&)), SLOT(updateWindowTitle()));    connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(updateWindowTitle()));    connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails()));    connect(&domainHandler, &DomainHandler::settingsReceived, this, &Application::domainSettingsReceived);    connect(&domainHandler, &DomainHandler::hostnameChanged,            DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress);     // update our location every 5 seconds in the metaverse server, assuming that we are authenticated with one    const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000;     locationUpdateTimer = new QTimer(this);    auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();    connect(locationUpdateTimer, &QTimer::timeout, discoverabilityManager.data(), &DiscoverabilityManager::updateLocation);    locationUpdateTimer->start(DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS);     // if we get a domain change, immediately attempt update location in metaverse server    connect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain,            discoverabilityManager.data(), &DiscoverabilityManager::updateLocation);     connect(nodeList.data(), &NodeList::nodeAdded, this, &Application::nodeAdded);    connect(nodeList.data(), &NodeList::nodeKilled, this, &Application::nodeKilled);    connect(nodeList.data(), SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer)));    connect(nodeList.data(), &NodeList::uuidChanged, _myAvatar, &MyAvatar::setSessionUUID);    connect(nodeList.data(), &NodeList::uuidChanged, this, &Application::setSessionUUID);    connect(nodeList.data(), &NodeList::limitOfSilentDomainCheckInsReached, nodeList.data(), &NodeList::reset);    connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &Application::notifyPacketVersionMismatch);     // connect to appropriate slots on AccountManager    AccountManager& accountManager = AccountManager::getInstance();     const qint64 BALANCE_UPDATE_INTERVAL_MSECS = 5 * 1000;     balanceUpdateTimer = new QTimer(this);    connect(balanceUpdateTimer, &QTimer::timeout, &accountManager, &AccountManager::updateBalance);    balanceUpdateTimer->start(BALANCE_UPDATE_INTERVAL_MSECS);     connect(&accountManager, &AccountManager::balanceChanged, this, &Application::updateWindowTitle);     auto dialogsManager = DependencyManager::get<DialogsManager>();    connect(&accountManager, &AccountManager::authRequired, dialogsManager.data(), &DialogsManager::showLoginDialog);    connect(&accountManager, &AccountManager::usernameChanged, this, &Application::updateWindowTitle);     // set the account manager's root URL and trigger a login request if we don't have the access token    accountManager.setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL);    UserActivityLogger::getInstance().launch(applicationVersion());     // once the event loop has started, check and signal for an access token    QMetaObject::invokeMethod(&accountManager, "checkAndSignalForAccessToken", Qt::QueuedConnection);     auto addressManager = DependencyManager::get<AddressManager>();     // use our MyAvatar position and quat for address manager path    addressManager->setPositionGetter(getPositionForPath);    addressManager->setOrientationGetter(getOrientationForPath);     connect(addressManager.data(), &AddressManager::hostChanged, this, &Application::updateWindowTitle);    connect(this, &QCoreApplication::aboutToQuit, addressManager.data(), &AddressManager::storeCurrentAddress);     #ifdef _WIN32    WSADATA WsaData;    int wsaresult = WSAStartup(MAKEWORD(2,2), &WsaData);    #endif     // tell the NodeList instance who to tell the domain server we care about    nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer                                                 << NodeType::EntityServer);     // connect to the packet sent signal of the _entityEditSender

Lines 1–500 of 4770.