//// 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 this before QGLWidget, which includes an earlier version of OpenGL#include "InterfaceConfig.h" #include <QActionGroup>#include <QColorDialog>#include <QDesktopWidget>#include <QCheckBox>#include <QImage>#include <QInputDialog>#include <QKeyEvent>#include <QMenuBar>#include <QMouseEvent>#include <QNetworkReply>#include <QNetworkDiskCache>#include <QOpenGLFramebufferObject>#include <QObject>#include <QWheelEvent>#include <QSettings>#include <QShortcut>#include <QTimer>#include <QUrl>#include <QWindow>#include <QtDebug>#include <QFileDialog>#include <QDesktopServices>#include <QXmlStreamReader>#include <QXmlStreamAttributes>#include <QMediaPlayer>#include <QMimeData>#include <QMessageBox> #include <AddressManager.h>#include <AccountManager.h>#include <AudioInjector.h>#include <EntityScriptingInterface.h>#include <HFActionEvent.h>#include <HFBackEvent.h>#include <LocalVoxelsList.h>#include <LogHandler.h>#include <NetworkAccessManager.h>#include <OctalCode.h>#include <OctreeSceneStats.h>#include <PacketHeaders.h>#include <PerfStat.h>#include <ResourceCache.h>#include <UserActivityLogger.h>#include <UUID.h> #include "Application.h"#include "InterfaceVersion.h"#include "Menu.h"#include "ModelUploader.h"#include "Util.h" #include "devices/Leapmotion.h"#include "devices/MIDIManager.h"#include "devices/OculusManager.h"#include "devices/TV3DManager.h" #include "renderer/ProgramObject.h"#include "gpu/Batch.h" #include "scripting/AccountScriptingInterface.h"#include "scripting/AudioDeviceScriptingInterface.h"#include "scripting/ClipboardScriptingInterface.h"#include "scripting/JoystickScriptingInterface.h"#include "scripting/GlobalServicesScriptingInterface.h"#include "scripting/LocationScriptingInterface.h"#include "scripting/MenuScriptingInterface.h"#include "scripting/SettingsScriptingInterface.h"#include "scripting/WindowScriptingInterface.h"#include "scripting/WebWindow.h" #include "ui/DataWebDialog.h"#include "ui/InfoView.h"#include "ui/Snapshot.h"#include "ui/Stats.h"#include "ui/TextRenderer.h" using namespace std; // Starfield informationstatic unsigned STARFIELD_NUM_STARS = 50000;static unsigned STARFIELD_SEED = 1; static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged clicks are ignored const unsigned MAXIMUM_CACHE_SIZE = 10737418240; // 10GB static QTimer* idleTimer = NULL; const QString CHECK_VERSION_URL = "https://highfidelity.io/latestVersion.xml";const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/hifi.skipversion"; const QString DEFAULT_SCRIPTS_JS_URL = "http://public.highfidelity.io/scripts/defaultScripts.js"; void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { QString logMessage = LogHandler::getInstance().printMessage((LogMsgType) type, context, message); if (!logMessage.isEmpty()) { Application::getInstance()->getLogger()->addMessage(qPrintable(logMessage)); }} QString& Application::resourcesPath() {#ifdef Q_OS_MAC static QString staticResourcePath = QCoreApplication::applicationDirPath() + "/../Resources/";#else static QString staticResourcePath = QCoreApplication::applicationDirPath() + "/resources/";#endif return staticResourcePath;} Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : QApplication(argc, argv), _window(new MainWindow(desktop())), _glWidget(new GLCanvas()), _nodeThread(new QThread(this)), _datagramProcessor(), _undoStack(), _undoStackScriptingInterface(&_undoStack), _frameCount(0), _fps(60.0f), _justStarted(true), _voxelImportDialog(NULL), _voxelImporter(), _importSucceded(false), _sharedVoxelSystem(TREE_SCALE, DEFAULT_MAX_VOXELS_PER_SYSTEM, &_clipboard), _entities(true), _entityCollisionSystem(), _entityClipboardRenderer(false), _entityClipboard(), _wantToKillLocalVoxels(false), _viewFrustum(), _lastQueriedViewFrustum(), _lastQueriedTime(usecTimestampNow()), _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)), _scaleMirror(1.0f), _rotateMirror(0.0f), _raiseMirror(0.0f), _mouseX(0), _mouseY(0), _lastMouseMove(usecTimestampNow()), _lastMouseMoveWasSimulated(false), _mouseHidden(false), _seenMouseMove(false), _touchAvgX(0.0f), _touchAvgY(0.0f), _isTouchPressed(false), _mousePressed(false), _audio(), _enableProcessVoxelsThread(true), _octreeProcessor(), _voxelHideShowThread(&_voxels), _packetsPerSecond(0), _bytesPerSecond(0), _nodeBoundsDisplay(this), _previousScriptLocation(), _applicationOverlay(), _runningScriptsWidget(NULL), _runningScriptsWidgetWasVisible(false), _trayIcon(new QSystemTrayIcon(_window)), _lastNackTime(usecTimestampNow()), _lastSendDownstreamAudioStats(usecTimestampNow()), _isVSyncOn(true){ // read the ApplicationInfo.ini file for Name/Version/Domain information QSettings applicationInfo(Application::resourcesPath() + "info/ApplicationInfo.ini", QSettings::IniFormat); // set the associated application properties applicationInfo.beginGroup("INFO"); setApplicationName(applicationInfo.value("name").toString()); setApplicationVersion(BUILD_VERSION); setOrganizationName(applicationInfo.value("organizationName").toString()); setOrganizationDomain(applicationInfo.value("organizationDomain").toString()); _logger = new FileLogger(this); // After setting organization name in order to get correct directory QSettings::setDefaultFormat(QSettings::IniFormat); _myAvatar = _avatarManager.getMyAvatar(); _applicationStartupTime = startup_time; QFontDatabase::addApplicationFont(Application::resourcesPath() + "styles/Inconsolata.otf"); _window->setWindowTitle("Interface"); qInstallMessageHandler(messageHandler); qDebug() << "[VERSION] Build sequence: " << qPrintable(applicationVersion()); // call Menu getInstance static method to set up the menu _window->setMenuBar(Menu::getInstance()); _runningScriptsWidget = new RunningScriptsWidget(_window); 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); } // start the nodeThread so its event loop is running _nodeThread->start(); // make sure the node thread is given highest priority _nodeThread->setPriority(QThread::TimeCriticalPriority); // put the NodeList and datagram processing on the node thread NodeList* nodeList = NodeList::createInstance(NodeType::Agent, listenPort); nodeList->moveToThread(_nodeThread); _datagramProcessor.moveToThread(_nodeThread); // connect the DataProcessor processDatagrams slot to the QUDPSocket readyRead() signal connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), &_datagramProcessor, SLOT(processDatagrams())); // put the audio processing on a separate thread QThread* audioThread = new QThread(this); _audio.moveToThread(audioThread); connect(audioThread, SIGNAL(started()), &_audio, SLOT(start())); 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, &DomainHandler::settingsReceived, this, &Application::domainSettingsReceived); connect(&domainHandler, &DomainHandler::hostnameChanged, Menu::getInstance(), &Menu::clearLoginDialogDisplayedFlag); // update our location every 5 seconds in the data-server, assuming that we are authenticated with one const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000; QTimer* locationUpdateTimer = new QTimer(this); connect(locationUpdateTimer, &QTimer::timeout, this, &Application::updateLocationInServer); locationUpdateTimer->start(DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS); connect(nodeList, &NodeList::nodeAdded, this, &Application::nodeAdded); connect(nodeList, &NodeList::nodeKilled, this, &Application::nodeKilled); connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer))); connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), &_voxels, SLOT(nodeAdded(SharedNodePointer))); connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), &_voxels, SLOT(nodeKilled(SharedNodePointer))); connect(nodeList, &NodeList::uuidChanged, _myAvatar, &MyAvatar::setSessionUUID); connect(nodeList, &NodeList::limitOfSilentDomainCheckInsReached, nodeList, &NodeList::reset); // connect to appropriate slots on AccountManager AccountManager& accountManager = AccountManager::getInstance(); const qint64 BALANCE_UPDATE_INTERVAL_MSECS = 5 * 1000; QTimer* balanceUpdateTimer = new QTimer(this); connect(balanceUpdateTimer, &QTimer::timeout, &accountManager, &AccountManager::updateBalance); balanceUpdateTimer->start(BALANCE_UPDATE_INTERVAL_MSECS); connect(&accountManager, &AccountManager::balanceChanged, this, &Application::updateWindowTitle); connect(&accountManager, &AccountManager::authRequired, Menu::getInstance(), &Menu::loginForCurrentDomain); connect(&accountManager, &AccountManager::usernameChanged, this, &Application::updateWindowTitle); // once we have a profile in account manager make sure we generate a new keypair connect(&accountManager, &AccountManager::profileChanged, &accountManager, &AccountManager::generateNewKeypair); // set the account manager's root URL and trigger a login request if we don't have the access token accountManager.setAuthURL(DEFAULT_NODE_AUTH_URL); UserActivityLogger::getInstance().launch(applicationVersion()); // once the event loop has started, check and signal for an access token QMetaObject::invokeMethod(&accountManager, "checkAndSignalForAccessToken", Qt::QueuedConnection); AddressManager& addressManager = AddressManager::getInstance(); // use our MyAvatar position and quat for address manager path addressManager.setPositionGetter(getPositionForPath); addressManager.setOrientationGetter(getOrientationForPath); // handle domain change signals from AddressManager connect(&addressManager, &AddressManager::possibleDomainChangeRequiredToHostname, this, &Application::changeDomainHostname); connect(&addressManager, &AddressManager::possibleDomainChangeRequiredViaICEForID, &domainHandler, &DomainHandler::setIceServerHostnameAndID); _settings = new QSettings(this); _numChangedSettings = 0; // Check to see if the user passed in a command line option for loading a local // Voxel File. _voxelsFilename = getCmdOption(argc, constArgv, "-i"); #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::VoxelServer << NodeType::EntityServer << NodeType::MetavoxelServer); // connect to the packet sent signal of the _voxelEditSender and the _entityEditSender connect(&_voxelEditSender, &VoxelEditPacketSender::packetSent, this, &Application::packetSent); connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent); // move the silentNodeTimer to the _nodeThread QTimer* silentNodeTimer = new QTimer(); connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes())); silentNodeTimer->start(NODE_SILENCE_THRESHOLD_MSECS); silentNodeTimer->moveToThread(_nodeThread); // send the identity packet for our avatar each second to our avatar mixer QTimer* identityPacketTimer = new QTimer(); connect(identityPacketTimer, &QTimer::timeout, _myAvatar, &MyAvatar::sendIdentityPacket); identityPacketTimer->start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); // send the billboard packet for our avatar every few seconds QTimer* billboardPacketTimer = new QTimer(); connect(billboardPacketTimer, &QTimer::timeout, _myAvatar, &MyAvatar::sendBillboardPacket); billboardPacketTimer->start(AVATAR_BILLBOARD_PACKET_SEND_INTERVAL_MSECS); QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkDiskCache* cache = new QNetworkDiskCache(); cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE); cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "interfaceCache"); networkAccessManager.setCache(cache); ResourceCache::setRequestLimit(3); _window->setCentralWidget(_glWidget); restoreSizeAndPosition(); _window->setVisible(true); _glWidget->setFocusPolicy(Qt::StrongFocus); _glWidget->setFocus(); // enable mouse tracking; otherwise, we only get drag events _glWidget->setMouseTracking(true); // initialization continues in initializeGL when OpenGL context is ready // Tell our voxel edit sender about our known jurisdictions _voxelEditSender.setVoxelServerJurisdictions(&_voxelServerJurisdictions); _entityEditSender.setServerJurisdictions(&_entityServerJurisdictions); // For now we're going to set the PPS for outbound packets to be super high, this is // probably not the right long term solution. But for now, we're going to do this to // allow you to move an entity around in your hand _entityEditSender.setPacketsPerSecond(3000); // super high!! checkVersion(); _overlays.init(_glWidget); // do this before scripts load LocalVoxelsList::getInstance()->addPersistantTree(DOMAIN_TREE_NAME, _voxels.getTree()); LocalVoxelsList::getInstance()->addPersistantTree(CLIPBOARD_TREE_NAME, &_clipboard); _runningScriptsWidget->setRunningScripts(getRunningScripts()); connect(_runningScriptsWidget, &RunningScriptsWidget::stopScriptName, this, &Application::stopScript); connect(this, SIGNAL(aboutToQuit()), this, SLOT(saveScripts())); // check first run... QVariant firstRunValue = _settings->value("firstRun",QVariant(true)); if (firstRunValue.isValid() && firstRunValue.toBool()) { qDebug() << "This is a first run..."; // clear the scripts, and set out script to our default scripts clearScriptsBeforeRunning(); loadScript(DEFAULT_SCRIPTS_JS_URL); QMutexLocker locker(&_settingsMutex); _settings->setValue("firstRun",QVariant(false)); } else { // do this as late as possible so that all required subsystems are initialized loadScripts(); QMutexLocker locker(&_settingsMutex); _previousScriptLocation = _settings->value("LastScriptLocation", QVariant("")).toString(); } connect(_window, &MainWindow::windowGeometryChanged, _runningScriptsWidget, &RunningScriptsWidget::setBoundary); _trayIcon->show(); #ifdef HAVE_RTMIDI // setup the MIDIManager MIDIManager& midiManagerInstance = MIDIManager::getInstance(); midiManagerInstance.openDefaultPort();#endif this->installEventFilter(this);} Application::~Application() { qInstallMessageHandler(NULL); saveSettings(); storeSizeAndPosition(); int DELAY_TIME = 1000; UserActivityLogger::getInstance().close(DELAY_TIME); // make sure we don't call the idle timer any more delete idleTimer; _sharedVoxelSystem.changeTree(new VoxelTree); delete _voxelImportDialog; // let the avatar mixer know we're out MyAvatar::sendKillAvatar(); // ask the datagram processing thread to quit and wait until it is done _nodeThread->quit(); _nodeThread->wait(); // stop the audio process QMetaObject::invokeMethod(&_audio, "stop"); // ask the audio thread to quit and wait until it is done _audio.thread()->quit(); _audio.thread()->wait(); _octreeProcessor.terminate(); _voxelHideShowThread.terminate(); _voxelEditSender.terminate(); _entityEditSender.terminate(); VoxelTreeElement::removeDeleteHook(&_voxels); // we don't need to do this processing on shutdown Menu::getInstance()->deleteLater(); _myAvatar = NULL; delete _glWidget;} void Application::saveSettings() { Menu::getInstance()->saveSettings(); _rearMirrorTools->saveSettings(_settings); if (_voxelImportDialog) { _voxelImportDialog->saveSettings(_settings); } _settings->sync(); _numChangedSettings = 0;} void Application::restoreSizeAndPosition() { QRect available = desktop()->availableGeometry(); QMutexLocker locker(&_settingsMutex); _settings->beginGroup("Window"); int x = (int)loadSetting(_settings, "x", 0); int y = (int)loadSetting(_settings, "y", 0); _window->move(x, y); int width = (int)loadSetting(_settings, "width", available.width()); int height = (int)loadSetting(_settings, "height", available.height()); _window->resize(width, height); _settings->endGroup();} void Application::storeSizeAndPosition() { QMutexLocker locker(&_settingsMutex); _settings->beginGroup("Window"); Lines 1–500 of 4350. Load more
// // 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 this before QGLWidget, which includes an earlier version of OpenGL #include "InterfaceConfig.h" #include <QActionGroup> #include <QColorDialog> #include <QDesktopWidget> #include <QCheckBox> #include <QImage> #include <QInputDialog> #include <QKeyEvent> #include <QMenuBar> #include <QMouseEvent> #include <QNetworkReply> #include <QNetworkDiskCache> #include <QOpenGLFramebufferObject> #include <QObject> #include <QWheelEvent> #include <QSettings> #include <QShortcut> #include <QTimer> #include <QUrl> #include <QWindow> #include <QtDebug> #include <QFileDialog> #include <QDesktopServices> #include <QXmlStreamReader> #include <QXmlStreamAttributes> #include <QMediaPlayer> #include <QMimeData> #include <QMessageBox> #include <AddressManager.h> #include <AccountManager.h> #include <AudioInjector.h> #include <EntityScriptingInterface.h> #include <HFActionEvent.h> #include <HFBackEvent.h> #include <LocalVoxelsList.h> #include <LogHandler.h> #include <NetworkAccessManager.h> #include <OctalCode.h> #include <OctreeSceneStats.h> #include <PacketHeaders.h> #include <PerfStat.h> #include <ResourceCache.h> #include <UserActivityLogger.h> #include <UUID.h> #include "Application.h" #include "InterfaceVersion.h" #include "Menu.h" #include "ModelUploader.h" #include "Util.h" #include "devices/Leapmotion.h" #include "devices/MIDIManager.h" #include "devices/OculusManager.h" #include "devices/TV3DManager.h" #include "renderer/ProgramObject.h" #include "gpu/Batch.h" #include "scripting/AccountScriptingInterface.h" #include "scripting/AudioDeviceScriptingInterface.h" #include "scripting/ClipboardScriptingInterface.h" #include "scripting/JoystickScriptingInterface.h" #include "scripting/GlobalServicesScriptingInterface.h" #include "scripting/LocationScriptingInterface.h" #include "scripting/MenuScriptingInterface.h" #include "scripting/SettingsScriptingInterface.h" #include "scripting/WindowScriptingInterface.h" #include "scripting/WebWindow.h" #include "ui/DataWebDialog.h" #include "ui/InfoView.h" #include "ui/Snapshot.h" #include "ui/Stats.h" #include "ui/TextRenderer.h" using namespace std; // Starfield information static unsigned STARFIELD_NUM_STARS = 50000; static unsigned STARFIELD_SEED = 1; static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged clicks are ignored const unsigned MAXIMUM_CACHE_SIZE = 10737418240; // 10GB static QTimer* idleTimer = NULL; const QString CHECK_VERSION_URL = "https://highfidelity.io/latestVersion.xml"; const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/hifi.skipversion"; const QString DEFAULT_SCRIPTS_JS_URL = "http://public.highfidelity.io/scripts/defaultScripts.js"; void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { QString logMessage = LogHandler::getInstance().printMessage((LogMsgType) type, context, message); if (!logMessage.isEmpty()) { Application::getInstance()->getLogger()->addMessage(qPrintable(logMessage)); } } QString& Application::resourcesPath() { #ifdef Q_OS_MAC static QString staticResourcePath = QCoreApplication::applicationDirPath() + "/../Resources/"; #else static QString staticResourcePath = QCoreApplication::applicationDirPath() + "/resources/"; #endif return staticResourcePath; } Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : QApplication(argc, argv), _window(new MainWindow(desktop())), _glWidget(new GLCanvas()), _nodeThread(new QThread(this)), _datagramProcessor(), _undoStack(), _undoStackScriptingInterface(&_undoStack), _frameCount(0), _fps(60.0f), _justStarted(true), _voxelImportDialog(NULL), _voxelImporter(), _importSucceded(false), _sharedVoxelSystem(TREE_SCALE, DEFAULT_MAX_VOXELS_PER_SYSTEM, &_clipboard), _entities(true), _entityCollisionSystem(), _entityClipboardRenderer(false), _entityClipboard(), _wantToKillLocalVoxels(false), _viewFrustum(), _lastQueriedViewFrustum(), _lastQueriedTime(usecTimestampNow()), _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)), _scaleMirror(1.0f), _rotateMirror(0.0f), _raiseMirror(0.0f), _mouseX(0), _mouseY(0), _lastMouseMove(usecTimestampNow()), _lastMouseMoveWasSimulated(false), _mouseHidden(false), _seenMouseMove(false), _touchAvgX(0.0f), _touchAvgY(0.0f), _isTouchPressed(false), _mousePressed(false), _audio(), _enableProcessVoxelsThread(true), _octreeProcessor(), _voxelHideShowThread(&_voxels), _packetsPerSecond(0), _bytesPerSecond(0), _nodeBoundsDisplay(this), _previousScriptLocation(), _applicationOverlay(), _runningScriptsWidget(NULL), _runningScriptsWidgetWasVisible(false), _trayIcon(new QSystemTrayIcon(_window)), _lastNackTime(usecTimestampNow()), _lastSendDownstreamAudioStats(usecTimestampNow()), _isVSyncOn(true) { // read the ApplicationInfo.ini file for Name/Version/Domain information QSettings applicationInfo(Application::resourcesPath() + "info/ApplicationInfo.ini", QSettings::IniFormat); // set the associated application properties applicationInfo.beginGroup("INFO"); setApplicationName(applicationInfo.value("name").toString()); setApplicationVersion(BUILD_VERSION); setOrganizationName(applicationInfo.value("organizationName").toString()); setOrganizationDomain(applicationInfo.value("organizationDomain").toString()); _logger = new FileLogger(this); // After setting organization name in order to get correct directory QSettings::setDefaultFormat(QSettings::IniFormat); _myAvatar = _avatarManager.getMyAvatar(); _applicationStartupTime = startup_time; QFontDatabase::addApplicationFont(Application::resourcesPath() + "styles/Inconsolata.otf"); _window->setWindowTitle("Interface"); qInstallMessageHandler(messageHandler); qDebug() << "[VERSION] Build sequence: " << qPrintable(applicationVersion()); // call Menu getInstance static method to set up the menu _window->setMenuBar(Menu::getInstance()); _runningScriptsWidget = new RunningScriptsWidget(_window); 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); } // start the nodeThread so its event loop is running _nodeThread->start(); // make sure the node thread is given highest priority _nodeThread->setPriority(QThread::TimeCriticalPriority); // put the NodeList and datagram processing on the node thread NodeList* nodeList = NodeList::createInstance(NodeType::Agent, listenPort); nodeList->moveToThread(_nodeThread); _datagramProcessor.moveToThread(_nodeThread); // connect the DataProcessor processDatagrams slot to the QUDPSocket readyRead() signal connect(&nodeList->getNodeSocket(), SIGNAL(readyRead()), &_datagramProcessor, SLOT(processDatagrams())); // put the audio processing on a separate thread QThread* audioThread = new QThread(this); _audio.moveToThread(audioThread); connect(audioThread, SIGNAL(started()), &_audio, SLOT(start())); 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, &DomainHandler::settingsReceived, this, &Application::domainSettingsReceived); connect(&domainHandler, &DomainHandler::hostnameChanged, Menu::getInstance(), &Menu::clearLoginDialogDisplayedFlag); // update our location every 5 seconds in the data-server, assuming that we are authenticated with one const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000; QTimer* locationUpdateTimer = new QTimer(this); connect(locationUpdateTimer, &QTimer::timeout, this, &Application::updateLocationInServer); locationUpdateTimer->start(DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS); connect(nodeList, &NodeList::nodeAdded, this, &Application::nodeAdded); connect(nodeList, &NodeList::nodeKilled, this, &Application::nodeKilled); connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer))); connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), &_voxels, SLOT(nodeAdded(SharedNodePointer))); connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), &_voxels, SLOT(nodeKilled(SharedNodePointer))); connect(nodeList, &NodeList::uuidChanged, _myAvatar, &MyAvatar::setSessionUUID); connect(nodeList, &NodeList::limitOfSilentDomainCheckInsReached, nodeList, &NodeList::reset); // connect to appropriate slots on AccountManager AccountManager& accountManager = AccountManager::getInstance(); const qint64 BALANCE_UPDATE_INTERVAL_MSECS = 5 * 1000; QTimer* balanceUpdateTimer = new QTimer(this); connect(balanceUpdateTimer, &QTimer::timeout, &accountManager, &AccountManager::updateBalance); balanceUpdateTimer->start(BALANCE_UPDATE_INTERVAL_MSECS); connect(&accountManager, &AccountManager::balanceChanged, this, &Application::updateWindowTitle); connect(&accountManager, &AccountManager::authRequired, Menu::getInstance(), &Menu::loginForCurrentDomain); connect(&accountManager, &AccountManager::usernameChanged, this, &Application::updateWindowTitle); // once we have a profile in account manager make sure we generate a new keypair connect(&accountManager, &AccountManager::profileChanged, &accountManager, &AccountManager::generateNewKeypair); // set the account manager's root URL and trigger a login request if we don't have the access token accountManager.setAuthURL(DEFAULT_NODE_AUTH_URL); UserActivityLogger::getInstance().launch(applicationVersion()); // once the event loop has started, check and signal for an access token QMetaObject::invokeMethod(&accountManager, "checkAndSignalForAccessToken", Qt::QueuedConnection); AddressManager& addressManager = AddressManager::getInstance(); // use our MyAvatar position and quat for address manager path addressManager.setPositionGetter(getPositionForPath); addressManager.setOrientationGetter(getOrientationForPath); // handle domain change signals from AddressManager connect(&addressManager, &AddressManager::possibleDomainChangeRequiredToHostname, this, &Application::changeDomainHostname); connect(&addressManager, &AddressManager::possibleDomainChangeRequiredViaICEForID, &domainHandler, &DomainHandler::setIceServerHostnameAndID); _settings = new QSettings(this); _numChangedSettings = 0; // Check to see if the user passed in a command line option for loading a local // Voxel File. _voxelsFilename = getCmdOption(argc, constArgv, "-i"); #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::VoxelServer << NodeType::EntityServer << NodeType::MetavoxelServer); // connect to the packet sent signal of the _voxelEditSender and the _entityEditSender connect(&_voxelEditSender, &VoxelEditPacketSender::packetSent, this, &Application::packetSent); connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent); // move the silentNodeTimer to the _nodeThread QTimer* silentNodeTimer = new QTimer(); connect(silentNodeTimer, SIGNAL(timeout()), nodeList, SLOT(removeSilentNodes())); silentNodeTimer->start(NODE_SILENCE_THRESHOLD_MSECS); silentNodeTimer->moveToThread(_nodeThread); // send the identity packet for our avatar each second to our avatar mixer QTimer* identityPacketTimer = new QTimer(); connect(identityPacketTimer, &QTimer::timeout, _myAvatar, &MyAvatar::sendIdentityPacket); identityPacketTimer->start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); // send the billboard packet for our avatar every few seconds QTimer* billboardPacketTimer = new QTimer(); connect(billboardPacketTimer, &QTimer::timeout, _myAvatar, &MyAvatar::sendBillboardPacket); billboardPacketTimer->start(AVATAR_BILLBOARD_PACKET_SEND_INTERVAL_MSECS); QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkDiskCache* cache = new QNetworkDiskCache(); cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE); cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "interfaceCache"); networkAccessManager.setCache(cache); ResourceCache::setRequestLimit(3); _window->setCentralWidget(_glWidget); restoreSizeAndPosition(); _window->setVisible(true); _glWidget->setFocusPolicy(Qt::StrongFocus); _glWidget->setFocus(); // enable mouse tracking; otherwise, we only get drag events _glWidget->setMouseTracking(true); // initialization continues in initializeGL when OpenGL context is ready // Tell our voxel edit sender about our known jurisdictions _voxelEditSender.setVoxelServerJurisdictions(&_voxelServerJurisdictions); _entityEditSender.setServerJurisdictions(&_entityServerJurisdictions); // For now we're going to set the PPS for outbound packets to be super high, this is // probably not the right long term solution. But for now, we're going to do this to // allow you to move an entity around in your hand _entityEditSender.setPacketsPerSecond(3000); // super high!! checkVersion(); _overlays.init(_glWidget); // do this before scripts load LocalVoxelsList::getInstance()->addPersistantTree(DOMAIN_TREE_NAME, _voxels.getTree()); LocalVoxelsList::getInstance()->addPersistantTree(CLIPBOARD_TREE_NAME, &_clipboard); _runningScriptsWidget->setRunningScripts(getRunningScripts()); connect(_runningScriptsWidget, &RunningScriptsWidget::stopScriptName, this, &Application::stopScript); connect(this, SIGNAL(aboutToQuit()), this, SLOT(saveScripts())); // check first run... QVariant firstRunValue = _settings->value("firstRun",QVariant(true)); if (firstRunValue.isValid() && firstRunValue.toBool()) { qDebug() << "This is a first run..."; // clear the scripts, and set out script to our default scripts clearScriptsBeforeRunning(); loadScript(DEFAULT_SCRIPTS_JS_URL); QMutexLocker locker(&_settingsMutex); _settings->setValue("firstRun",QVariant(false)); } else { // do this as late as possible so that all required subsystems are initialized loadScripts(); QMutexLocker locker(&_settingsMutex); _previousScriptLocation = _settings->value("LastScriptLocation", QVariant("")).toString(); } connect(_window, &MainWindow::windowGeometryChanged, _runningScriptsWidget, &RunningScriptsWidget::setBoundary); _trayIcon->show(); #ifdef HAVE_RTMIDI // setup the MIDIManager MIDIManager& midiManagerInstance = MIDIManager::getInstance(); midiManagerInstance.openDefaultPort(); #endif this->installEventFilter(this); } Application::~Application() { qInstallMessageHandler(NULL); saveSettings(); storeSizeAndPosition(); int DELAY_TIME = 1000; UserActivityLogger::getInstance().close(DELAY_TIME); // make sure we don't call the idle timer any more delete idleTimer; _sharedVoxelSystem.changeTree(new VoxelTree); delete _voxelImportDialog; // let the avatar mixer know we're out MyAvatar::sendKillAvatar(); // ask the datagram processing thread to quit and wait until it is done _nodeThread->quit(); _nodeThread->wait(); // stop the audio process QMetaObject::invokeMethod(&_audio, "stop"); // ask the audio thread to quit and wait until it is done _audio.thread()->quit(); _audio.thread()->wait(); _octreeProcessor.terminate(); _voxelHideShowThread.terminate(); _voxelEditSender.terminate(); _entityEditSender.terminate(); VoxelTreeElement::removeDeleteHook(&_voxels); // we don't need to do this processing on shutdown Menu::getInstance()->deleteLater(); _myAvatar = NULL; delete _glWidget; } void Application::saveSettings() { Menu::getInstance()->saveSettings(); _rearMirrorTools->saveSettings(_settings); if (_voxelImportDialog) { _voxelImportDialog->saveSettings(_settings); } _settings->sync(); _numChangedSettings = 0; } void Application::restoreSizeAndPosition() { QRect available = desktop()->availableGeometry(); QMutexLocker locker(&_settingsMutex); _settings->beginGroup("Window"); int x = (int)loadSetting(_settings, "x", 0); int y = (int)loadSetting(_settings, "y", 0); _window->move(x, y); int width = (int)loadSetting(_settings, "width", available.width()); int height = (int)loadSetting(_settings, "height", available.height()); _window->resize(width, height); _settings->endGroup(); } void Application::storeSizeAndPosition() { QMutexLocker locker(&_settingsMutex); _settings->beginGroup("Window");