5476 lines · 222.9 KB · C++
RawDownload
    connect(ddeTracker.data(), &FaceTracker::muteToggled, this, &Application::faceTrackerMuteToggled);#endif #ifdef HAVE_IVIEWHMD    auto eyeTracker = DependencyManager::get<EyeTracker>();    eyeTracker->init();    setActiveEyeTracker();#endif     auto applicationUpdater = DependencyManager::get<AutoUpdater>();    connect(applicationUpdater.data(), &AutoUpdater::newVersionIsAvailable, dialogsManager.data(), &DialogsManager::showUpdateDialog);    applicationUpdater->checkForUpdate();     // Now that menu is initialized we can sync myAvatar with it's state.    getMyAvatar()->updateMotionBehaviorFromMenu(); // FIXME spacemouse code still needs cleanup#if 0    // the 3Dconnexion device wants to be initialized after a window is displayed.    SpacemouseManager::getInstance().init();#endif     // If the user clicks an an entity, we will check that it's an unlocked web entity, and if so, set the focus to it    auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();    connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickDownOnEntity,        [this, entityScriptingInterface](const EntityItemID& entityItemID, const MouseEvent& event) {        if (_keyboardFocusedItem != entityItemID) {            _keyboardFocusedItem = UNKNOWN_ENTITY_ID;            auto properties = entityScriptingInterface->getEntityProperties(entityItemID);            if (EntityTypes::Web == properties.getType() && !properties.getLocked() && properties.getVisible()) {                auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(entityItemID);                RenderableWebEntityItem* webEntity = dynamic_cast<RenderableWebEntityItem*>(entity.get());                if (webEntity) {                    webEntity->setProxyWindow(_window->windowHandle());                    if (_keyboardMouseDevice->isActive()) {                        _keyboardMouseDevice->pluginFocusOutEvent();                    }                    _keyboardFocusedItem = entityItemID;                    _lastAcceptedKeyPress = usecTimestampNow();                    if (_keyboardFocusHighlightID < 0 || !getOverlays().isAddedOverlay(_keyboardFocusHighlightID)) {                        _keyboardFocusHighlight = new Cube3DOverlay();                        _keyboardFocusHighlight->setAlpha(1.0f);                        _keyboardFocusHighlight->setBorderSize(1.0f);                        _keyboardFocusHighlight->setColor({ 0xFF, 0xEF, 0x00 });                        _keyboardFocusHighlight->setIsSolid(false);                        _keyboardFocusHighlight->setPulseMin(0.5);                        _keyboardFocusHighlight->setPulseMax(1.0);                        _keyboardFocusHighlight->setColorPulse(1.0);                        _keyboardFocusHighlight->setIgnoreRayIntersection(true);                        _keyboardFocusHighlight->setDrawInFront(true);                    }                    _keyboardFocusHighlight->setRotation(webEntity->getRotation());                    _keyboardFocusHighlight->setPosition(webEntity->getPosition());                    _keyboardFocusHighlight->setDimensions(webEntity->getDimensions() * 1.05f);                    _keyboardFocusHighlight->setVisible(true);                    _keyboardFocusHighlightID = getOverlays().addOverlay(_keyboardFocusHighlight);                }            }            if (_keyboardFocusedItem == UNKNOWN_ENTITY_ID && _keyboardFocusHighlight) {                _keyboardFocusHighlight->setVisible(false);            }         }    });     connect(entityScriptingInterface.data(), &EntityScriptingInterface::deletingEntity,        [=](const EntityItemID& entityItemID) {        if (entityItemID == _keyboardFocusedItem) {            _keyboardFocusedItem = UNKNOWN_ENTITY_ID;            if (_keyboardFocusHighlight) {                _keyboardFocusHighlight->setVisible(false);            }        }    });     // If the user clicks somewhere where there is NO entity at all, we will release focus    connect(getEntities(), &EntityTreeRenderer::mousePressOffEntity,        [=](const RayToEntityIntersectionResult& entityItemID, const QMouseEvent* event) {        _keyboardFocusedItem = UNKNOWN_ENTITY_ID;        if (_keyboardFocusHighlight) {            _keyboardFocusHighlight->setVisible(false);        }    });     connect(this, &Application::aboutToQuit, [=]() {        _keyboardFocusedItem = UNKNOWN_ENTITY_ID;        if (_keyboardFocusHighlight) {            _keyboardFocusHighlight->setVisible(false);        }    });     // Add periodic checks to send user activity data    static int CHECK_NEARBY_AVATARS_INTERVAL_MS = 10000;    static int SEND_STATS_INTERVAL_MS = 10000;    static int NEARBY_AVATAR_RADIUS_METERS = 10;     // Periodically send fps as a user activity event    QTimer* sendStatsTimer = new QTimer(this);    sendStatsTimer->setInterval(SEND_STATS_INTERVAL_MS);    connect(sendStatsTimer, &QTimer::timeout, this, [this]() {        QJsonObject properties = {};        MemoryInfo memInfo;        if (getMemoryInfo(memInfo)) {            properties["system_memory_total"] = static_cast<qint64>(memInfo.totalMemoryBytes);            properties["system_memory_used"] = static_cast<qint64>(memInfo.usedMemoryBytes);            properties["process_memory_used"] = static_cast<qint64>(memInfo.processUsedMemoryBytes);        }         auto displayPlugin = qApp->getActiveDisplayPlugin();         properties["fps"] = _frameCounter.rate();        properties["present_rate"] = displayPlugin->presentRate();        properties["new_frame_present_rate"] = displayPlugin->newFramePresentRate();        properties["dropped_frame_rate"] = displayPlugin->droppedFrameRate();        properties["sim_rate"] = getAverageSimsPerSecond();        properties["avatar_sim_rate"] = getAvatarSimrate();         auto bandwidthRecorder = DependencyManager::get<BandwidthRecorder>();        properties["packet_rate_in"] = bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond();        properties["packet_rate_out"] = bandwidthRecorder->getCachedTotalAverageOutputPacketsPerSecond();        properties["kbps_in"] = bandwidthRecorder->getCachedTotalAverageInputKilobitsPerSecond();        properties["kbps_out"] = bandwidthRecorder->getCachedTotalAverageOutputKilobitsPerSecond();         auto nodeList = DependencyManager::get<NodeList>();        SharedNodePointer entityServerNode = nodeList->soloNodeOfType(NodeType::EntityServer);        SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer);        SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer);        SharedNodePointer assetServerNode = nodeList->soloNodeOfType(NodeType::AssetServer);        SharedNodePointer messagesMixerNode = nodeList->soloNodeOfType(NodeType::MessagesMixer);        properties["entity_ping"] = entityServerNode ? entityServerNode->getPingMs() : -1;        properties["audio_ping"] = audioMixerNode ? audioMixerNode->getPingMs() : -1;        properties["avatar_ping"] = avatarMixerNode ? avatarMixerNode->getPingMs() : -1;        properties["asset_ping"] = assetServerNode ? assetServerNode->getPingMs() : -1;        properties["messages_ping"] = messagesMixerNode ? messagesMixerNode->getPingMs() : -1;         auto loadingRequests = ResourceCache::getLoadingRequests();        properties["active_downloads"] = loadingRequests.size();        properties["pending_downloads"] = ResourceCache::getPendingRequestCount();         properties["throttled"] = _displayPlugin ? _displayPlugin->isThrottled() : false;         UserActivityLogger::getInstance().logAction("stats", properties);    });    sendStatsTimer->start();      // Periodically check for count of nearby avatars    static int lastCountOfNearbyAvatars = -1;    QTimer* checkNearbyAvatarsTimer = new QTimer(this);    checkNearbyAvatarsTimer->setInterval(CHECK_NEARBY_AVATARS_INTERVAL_MS);    connect(checkNearbyAvatarsTimer, &QTimer::timeout, this, [this]() {        auto avatarManager = DependencyManager::get<AvatarManager>();        int nearbyAvatars = avatarManager->numberOfAvatarsInRange(avatarManager->getMyAvatar()->getPosition(),                                                                  NEARBY_AVATAR_RADIUS_METERS) - 1;        if (nearbyAvatars != lastCountOfNearbyAvatars) {            lastCountOfNearbyAvatars = nearbyAvatars;            UserActivityLogger::getInstance().logAction("nearby_avatars", { { "count", nearbyAvatars } });        }    });    checkNearbyAvatarsTimer->start();     // Track user activity event when we receive a mute packet    auto onMutedByMixer = []() {        UserActivityLogger::getInstance().logAction("received_mute_packet");    };    connect(DependencyManager::get<AudioClient>().data(), &AudioClient::mutedByMixer, this, onMutedByMixer);     // Track when the address bar is opened    auto onAddressBarToggled = [this]() {        // Record time        UserActivityLogger::getInstance().logAction("opened_address_bar", { { "uptime_ms", _sessionRunTimer.elapsed() } });    };    connect(DependencyManager::get<DialogsManager>().data(), &DialogsManager::addressBarToggled, this, onAddressBarToggled);     // Make sure we don't time out during slow operations at startup    updateHeartbeat();     OctreeEditPacketSender* packetSender = entityScriptingInterface->getPacketSender();    EntityEditPacketSender* entityPacketSender = static_cast<EntityEditPacketSender*>(packetSender);    entityPacketSender->setMyAvatar(getMyAvatar());     connect(this, &Application::applicationStateChanged, this, &Application::activeChanged);    qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTimer.elapsed() / 1000.0);     // After all of the constructor is completed, then set firstRun to false.    Setting::Handle<bool> firstRun{ Settings::firstRun, true };    firstRun.set(false);} void Application::domainConnectionRefused(const QString& reasonMessage, int reasonCode) {    switch (static_cast<DomainHandler::ConnectionRefusedReason>(reasonCode)) {        case DomainHandler::ConnectionRefusedReason::ProtocolMismatch:        case DomainHandler::ConnectionRefusedReason::TooManyUsers:        case DomainHandler::ConnectionRefusedReason::Unknown: {            QString message = "Unable to connect to the location you are visiting.\n";            message += reasonMessage;            OffscreenUi::warning("", message);            break;        }        default:            // nothing to do.            break;    }} QString Application::getUserAgent() {    if (QThread::currentThread() != thread()) {        QString userAgent;         QMetaObject::invokeMethod(this, "getUserAgent", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, userAgent));         return userAgent;    }     QString userAgent = "Mozilla/5.0 (HighFidelityInterface/" + BuildInfo::VERSION + "; "        + QSysInfo::productType() + " " + QSysInfo::productVersion() + ")";     auto formatPluginName = [](QString name) -> QString { return name.trimmed().replace(" ", "-");  };     // For each plugin, add to userAgent    auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins();    for (auto& dp : displayPlugins) {        if (dp->isActive() && dp->isHmd()) {            userAgent += " " + formatPluginName(dp->getName());        }    }    auto inputPlugins= PluginManager::getInstance()->getInputPlugins();    for (auto& ip : inputPlugins) {        if (ip->isActive()) {            userAgent += " " + formatPluginName(ip->getName());        }    }     return userAgent;} void Application::toggleMenuUnderReticle() const {    // In HMD, if the menu is near the mouse but not under it, the reticle can be at a significantly    // different depth. When you focus on the menu, the cursor can appear to your crossed eyes as both    // on the menu and off.    // Even in 2D, it is arguable whether the user would want the menu to be to the side.    const float X_LEFT_SHIFT = 50.0;    auto offscreenUi = DependencyManager::get<OffscreenUi>();    auto reticlePosition = getApplicationCompositor().getReticlePosition();    offscreenUi->toggleMenu(QPoint(reticlePosition.x - X_LEFT_SHIFT, reticlePosition.y));} void Application::checkChangeCursor() {    QMutexLocker locker(&_changeCursorLock);    if (_cursorNeedsChanging) {#ifdef Q_OS_MAC        auto cursorTarget = _window; // OSX doesn't seem to provide for hiding the cursor only on the GL widget#else        // On windows and linux, hiding the top level cursor also means it's invisible when hovering over the         // window menu, which is a pain, so only hide it for the GL surface        auto cursorTarget = _glWidget;#endif        cursorTarget->setCursor(_desiredCursor);         _cursorNeedsChanging = false;    }} void Application::showCursor(const QCursor& cursor) {    QMutexLocker locker(&_changeCursorLock);    _desiredCursor = cursor;    _cursorNeedsChanging = true;} void Application::updateHeartbeat() const {    static_cast<DeadlockWatchdogThread*>(_deadlockWatchdogThread)->updateHeartbeat();} void Application::aboutToQuit() {    emit beforeAboutToQuit();     foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) {        if (inputPlugin->isActive()) {            inputPlugin->deactivate();        }    }     getActiveDisplayPlugin()->deactivate();     // Hide Running Scripts dialog so that it gets destroyed in an orderly manner; prevents warnings at shutdown.    DependencyManager::get<OffscreenUi>()->hide("RunningScripts");     _aboutToQuit = true;     cleanupBeforeQuit();} void Application::cleanupBeforeQuit() {    // add a logline indicating if QTWEBENGINE_REMOTE_DEBUGGING is set or not    QString webengineRemoteDebugging = QProcessEnvironment::systemEnvironment().value("QTWEBENGINE_REMOTE_DEBUGGING", "false");    qCDebug(interfaceapp) << "QTWEBENGINE_REMOTE_DEBUGGING =" << webengineRemoteDebugging;     // Stop third party processes so that they're not left running in the event of a subsequent shutdown crash.#ifdef HAVE_DDE    DependencyManager::get<DdeFaceTracker>()->setEnabled(false);#endif#ifdef HAVE_IVIEWHMD    DependencyManager::get<EyeTracker>()->setEnabled(false, true);#endif    AnimDebugDraw::getInstance().shutdown();     // FIXME: once we move to shared pointer for the INputDevice we shoud remove this naked delete:    _applicationStateDevice.reset();     if (_keyboardFocusHighlightID > 0) {        getOverlays().deleteOverlay(_keyboardFocusHighlightID);        _keyboardFocusHighlightID = -1;    }    _keyboardFocusHighlight = nullptr;     auto nodeList = DependencyManager::get<NodeList>();     // send the domain a disconnect packet, force stoppage of domain-server check-ins    nodeList->getDomainHandler().disconnect();    nodeList->setIsShuttingDown(true);     // tell the packet receiver we're shutting down, so it can drop packets    nodeList->getPacketReceiver().setShouldDropPackets(true);     getEntities()->shutdown(); // tell the entities system we're shutting down, so it will stop running scripts     // Clear any queued processing (I/O, FBX/OBJ/Texture parsing)    QThreadPool::globalInstance()->clear();     DependencyManager::get<ScriptEngines>()->saveScripts();    DependencyManager::get<ScriptEngines>()->shutdownScripting(); // stop all currently running global scripts    DependencyManager::destroy<ScriptEngines>();     // Cleanup all overlays after the scripts, as scripts might add more    _overlays.cleanupAllOverlays();     // first stop all timers directly or by invokeMethod    // depending on what thread they run in    locationUpdateTimer.stop();    identityPacketTimer.stop();    pingTimer.stop();    QMetaObject::invokeMethod(&_settingsTimer, "stop", Qt::BlockingQueuedConnection);     // save state    _settingsThread.quit();    saveSettings();    _window->saveGeometry();     // stop the AudioClient    QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(),                              "stop", Qt::BlockingQueuedConnection);     // destroy the AudioClient so it and its thread have a chance to go down safely    DependencyManager::destroy<AudioClient>();     // destroy the AudioInjectorManager so it and its thread have a chance to go down safely    // this will also stop any ongoing network injectors    DependencyManager::destroy<AudioInjectorManager>();     // Destroy third party processes after scripts have finished using them.#ifdef HAVE_DDE    DependencyManager::destroy<DdeFaceTracker>();#endif#ifdef HAVE_IVIEWHMD    DependencyManager::destroy<EyeTracker>();#endif     DependencyManager::destroy<OffscreenUi>();} Application::~Application() {    _entityClipboard->eraseAllOctreeElements();    _entityClipboard.reset();     EntityTreePointer tree = getEntities()->getTree();    tree->setSimulation(nullptr);     _octreeProcessor.terminate();    _entityEditSender.terminate();     _physicsEngine->setCharacterController(nullptr);     // remove avatars from physics engine    DependencyManager::get<AvatarManager>()->clearAllAvatars();    VectorOfMotionStates motionStates;    DependencyManager::get<AvatarManager>()->getObjectsToRemoveFromPhysics(motionStates);    _physicsEngine->removeObjects(motionStates);     DependencyManager::destroy<OffscreenUi>();    DependencyManager::destroy<AvatarManager>();    DependencyManager::destroy<AnimationCache>();    DependencyManager::destroy<FramebufferCache>();    DependencyManager::destroy<TextureCache>();    DependencyManager::destroy<ModelCache>();    DependencyManager::destroy<GeometryCache>();    DependencyManager::destroy<ScriptCache>();    DependencyManager::destroy<SoundCache>();     ResourceManager::cleanup();     QThread* nodeThread = DependencyManager::get<NodeList>()->thread();     // remove the NodeList from the DependencyManager    DependencyManager::destroy<NodeList>();     // ask the node thread to quit and wait until it is done    nodeThread->quit();    nodeThread->wait();     Leapmotion::destroy(); #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();     qInstallMessageHandler(nullptr); // NOTE: Do this as late as possible so we continue to get our log messages} void Application::initializeGL() {    qCDebug(interfaceapp) << "Created Display Window.";     // initialize glut for shape drawing; Qt apparently initializes it on OS X    if (_isGLInitialized) {        return;    } else {        _isGLInitialized = true;    }     gpu::Context::init<gpu::gl::GLBackend>();    _gpuContext = std::make_shared<gpu::Context>();    // The gpu context can make child contexts for transfers, so     // we need to restore primary rendering context    _offscreenContext->makeCurrent();     initDisplay();    qCDebug(interfaceapp, "Initialized Display.");     // Set up the render engine    render::CullFunctor cullFunctor = LODManager::shouldRender;    _renderEngine->addJob<RenderShadowTask>("RenderShadowTask", cullFunctor);    _renderEngine->addJob<RenderDeferredTask>("RenderDeferredTask", cullFunctor);    _renderEngine->load();    _renderEngine->registerScene(_main3DScene);    // TODO: Load a cached config file     // The UI can't be created until the primary OpenGL    // context is created, because it needs to share    // texture resources    // Needs to happen AFTER the render engine initialization to access its configuration    initializeUi();    qCDebug(interfaceapp, "Initialized Offscreen UI.");    _offscreenContext->makeCurrent();     // call Menu getInstance static method to set up the menu    // Needs to happen AFTER the QML UI initialization    _window->setMenuBar(Menu::getInstance());     init();    qCDebug(interfaceapp, "init() complete.");     // create thread for parsing of octree data independent of the main network and rendering threads    _octreeProcessor.initialize(_enableProcessOctreeThread);    connect(&_octreeProcessor, &OctreePacketProcessor::packetVersionMismatch, this, &Application::notifyPacketVersionMismatch);    _entityEditSender.initialize(_enableProcessOctreeThread);     _idleLoopStdev.reset();     // update before the first render    update(0);} FrameTimingsScriptingInterface _frameTimingsScriptingInterface; extern void setupPreferences(); void Application::initializeUi() {    AddressBarDialog::registerType();    ErrorDialog::registerType();    LoginDialog::registerType();    Tooltip::registerType();    UpdateDialog::registerType();    qmlRegisterType<Preference>("Hifi", 1, 0, "Preference");      auto offscreenUi = DependencyManager::get<OffscreenUi>();    offscreenUi->create(_offscreenContext->getContext());     auto rootContext = offscreenUi->getRootContext();     offscreenUi->setProxyWindow(_window->windowHandle());    offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));    // OffscreenUi is a subclass of OffscreenQmlSurface specifically designed to    // support the window management and scripting proxies for VR use    offscreenUi->createDesktop(QString("hifi/Desktop.qml"));     // FIXME either expose so that dialogs can set this themselves or

Lines 1001–1500 of 5476. Previous lines