Merge branch 'master' of https://github.com/highfidelity/hifi into vive-ui

This commit is contained in:
howard-stearns 2016-06-22 15:58:53 -07:00
commit 67f76db7e4
37 changed files with 431 additions and 51 deletions

View file

@ -13,13 +13,13 @@ We no longer require install of qt5 via our [homebrew formulas repository](https
Assuming you've installed OpenSSL or Qt 5 using the homebrew instructions above, you'll need to set OPENSSL_ROOT_DIR and QT_CMAKE_PREFIX_PATH so CMake can find your installations. Assuming you've installed OpenSSL or Qt 5 using the homebrew instructions above, you'll need to set OPENSSL_ROOT_DIR and QT_CMAKE_PREFIX_PATH so CMake can find your installations.
For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR: For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR:
export OPENSSL_ROOT_DIR=/usr/local/Cellar/openssl/1.0.2d_1 export OPENSSL_ROOT_DIR=/usr/local/Cellar/openssl/1.0.2h_1/
For Qt 5.5.1 installed via homebrew, set QT_CMAKE_PREFIX_PATH as follows. For Qt 5.5.1 installed via homebrew, set QT_CMAKE_PREFIX_PATH as follows.
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.5.1_2/lib/cmake export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt55/5.5.1/lib/cmake
Not that these use the versions from homebrew formulae at the time of this writing, and the version in the path will likely change. Note that these use the versions from homebrew formulae at the time of this writing, and the version in the path will likely change.
###Xcode ###Xcode
If Xcode is your editor of choice, you can ask CMake to generate Xcode project files instead of Unix Makefiles. If Xcode is your editor of choice, you can ask CMake to generate Xcode project files instead of Unix Makefiles.

View file

@ -4,8 +4,8 @@ set(EXTERNAL_NAME neuron)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
set(NEURON_URL "https://s3.amazonaws.com/hifi-public/dependencies/neuron_datareader_b.12.zip") set(NEURON_URL "https://s3.amazonaws.com/hifi-public/dependencies/neuron_datareader_b.12.2.zip")
set(NEURON_URL_MD5 "0ab54ca04c9cc8094e0fa046c226e574") set(NEURON_URL_MD5 "84273ad2200bf86a9279d1f412a822ca")
ExternalProject_Add(${EXTERNAL_NAME} ExternalProject_Add(${EXTERNAL_NAME}
URL ${NEURON_URL} URL ${NEURON_URL}

View file

@ -63,12 +63,16 @@ const QString DomainMetadata::Descriptors::Hours::CLOSE = "close";
DomainMetadata::DomainMetadata(QObject* domainServer) : QObject(domainServer) { DomainMetadata::DomainMetadata(QObject* domainServer) : QObject(domainServer) {
// set up the structure necessary for casting during parsing (see parseHours, esp.) // set up the structure necessary for casting during parsing (see parseHours, esp.)
_metadata[USERS] = QVariantMap {}; _metadata[USERS] = QVariantMap {};
_metadata[DESCRIPTORS] = QVariantMap { _metadata[DESCRIPTORS] = QVariantMap { {
{ Descriptors::HOURS, QVariantMap { Descriptors::HOURS, QVariantMap {
{ Descriptors::Hours::WEEKDAY, QVariantList { QVariantMap{} } }, { Descriptors::Hours::WEEKDAY, QVariantList {
{ Descriptors::Hours::WEEKEND, QVariantList { QVariantMap{} } } QVariantList{ QVariant{}, QVariant{} } }
} } },
}; { Descriptors::Hours::WEEKEND, QVariantList {
QVariantList{ QVariant{}, QVariant{} } }
}
}
} };
assert(dynamic_cast<DomainServer*>(domainServer)); assert(dynamic_cast<DomainServer*>(domainServer));
DomainServer* server = static_cast<DomainServer*>(domainServer); DomainServer* server = static_cast<DomainServer*>(domainServer);
@ -96,33 +100,37 @@ QJsonObject DomainMetadata::get(const QString& group) {
return QJsonObject::fromVariantMap(_metadata[group].toMap()); return QJsonObject::fromVariantMap(_metadata[group].toMap());
} }
// merge delta into target
// target should be of the form [ OpenTime, CloseTime ],
// delta should be of the form [ { open: Time, close: Time } ]
void parseHours(QVariant delta, QVariant& target) { void parseHours(QVariant delta, QVariant& target) {
using Hours = DomainMetadata::Descriptors::Hours; using Hours = DomainMetadata::Descriptors::Hours;
// hours should be of the form [ { open: Time, close: Time } ]
assert(target.canConvert<QVariantList>()); assert(target.canConvert<QVariantList>());
auto& targetList = *static_cast<QVariantList*>(target.data()); auto& targetList = *static_cast<QVariantList*>(target.data());
// if/when multiple ranges are allowed, this list will need to be iterated // if/when multiple ranges are allowed, this list will need to be iterated
assert(targetList[0].canConvert<QVariantMap>()); assert(targetList[0].canConvert<QVariantList>());
auto& targetMap = *static_cast<QVariantMap*>(targetList[0].data()); auto& hours = *static_cast<QVariantList*>(targetList[0].data());
auto deltaMap = delta.toList()[0].toMap(); auto deltaHours = delta.toList()[0].toMap();
if (deltaMap.isEmpty()) { if (deltaHours.isEmpty()) {
return; return;
} }
// merge delta into base // merge delta into base
auto open = deltaMap.find(Hours::OPEN); static const int OPEN_INDEX = 0;
if (open != deltaMap.end()) { static const int CLOSE_INDEX = 1;
targetMap[Hours::OPEN] = open.value(); auto open = deltaHours.find(Hours::OPEN);
if (open != deltaHours.end()) {
hours[OPEN_INDEX] = open.value();
} }
assert(targetMap[Hours::OPEN].canConvert<QString>()); assert(hours[OPEN_INDEX].canConvert<QString>());
auto close = deltaMap.find(Hours::CLOSE); auto close = deltaHours.find(Hours::CLOSE);
if (close != deltaMap.end()) { if (close != deltaHours.end()) {
targetMap[Hours::CLOSE] = close.value(); hours[CLOSE_INDEX] = close.value();
} }
assert(targetMap[Hours::CLOSE].canConvert<QString>()); assert(hours[CLOSE_INDEX].canConvert<QString>());
} }
void DomainMetadata::descriptorsChanged() { void DomainMetadata::descriptorsChanged() {

View file

@ -68,6 +68,7 @@
#include <input-plugins/InputPlugin.h> #include <input-plugins/InputPlugin.h>
#include <controllers/UserInputMapper.h> #include <controllers/UserInputMapper.h>
#include <controllers/StateController.h> #include <controllers/StateController.h>
#include <UserActivityLoggerScriptingInterface.h>
#include <LogHandler.h> #include <LogHandler.h>
#include <MainWindow.h> #include <MainWindow.h>
#include <MessagesClient.h> #include <MessagesClient.h>
@ -150,6 +151,8 @@
#include "InterfaceParentFinder.h" #include "InterfaceParentFinder.h"
#include "FrameTimingsScriptingInterface.h" #include "FrameTimingsScriptingInterface.h"
#include <GPUIdent.h>
#include <gl/GLHelpers.h>
// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU // On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
// FIXME seems to be broken. // FIXME seems to be broken.
@ -437,7 +440,7 @@ bool setupEssentials(int& argc, char** argv) {
DependencyManager::set<HMDScriptingInterface>(); DependencyManager::set<HMDScriptingInterface>();
DependencyManager::set<ResourceScriptingInterface>(); DependencyManager::set<ResourceScriptingInterface>();
DependencyManager::set<ToolbarScriptingInterface>(); DependencyManager::set<ToolbarScriptingInterface>();
DependencyManager::set<UserActivityLoggerScriptingInterface>();
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) #if defined(Q_OS_MAC) || defined(Q_OS_WIN)
DependencyManager::set<SpeechRecognizer>(); DependencyManager::set<SpeechRecognizer>();
@ -675,10 +678,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
accountManager->setIsAgent(true); accountManager->setIsAgent(true);
accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL); accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL);
// sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value.
// The value will be 0 if the user blew away settings this session, which is both a feature and a bug.
UserActivityLogger::getInstance().launch(applicationVersion(), _previousSessionCrashed, sessionRunTime.get());
auto addressManager = DependencyManager::get<AddressManager>(); auto addressManager = DependencyManager::get<AddressManager>();
// use our MyAvatar position and quat for address manager path // use our MyAvatar position and quat for address manager path
@ -768,6 +767,39 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
// Make sure we don't time out during slow operations at startup // Make sure we don't time out during slow operations at startup
updateHeartbeat(); updateHeartbeat();
// sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value.
// The value will be 0 if the user blew away settings this session, which is both a feature and a bug.
auto gpuIdent = GPUIdent::getInstance();
auto glContextData = getGLContextData();
QJsonObject properties = {
{ "previousSessionCrashed", _previousSessionCrashed },
{ "previousSessionRuntime", sessionRunTime.get() },
{ "cpu_architecture", QSysInfo::currentCpuArchitecture() },
{ "kernel_type", QSysInfo::kernelType() },
{ "kernel_version", QSysInfo::kernelVersion() },
{ "os_type", QSysInfo::productType() },
{ "os_version", QSysInfo::productVersion() },
{ "gpu_name", gpuIdent->getName() },
{ "gpu_driver", gpuIdent->getDriver() },
{ "gpu_memory", static_cast<qint64>(gpuIdent->getMemory()) },
{ "gl_version_int", glVersionToInteger(glContextData.value("version").toString()) },
{ "gl_version", glContextData["version"] },
{ "gl_vender", glContextData["vendor"] },
{ "gl_sl_version", glContextData["slVersion"] },
{ "gl_renderer", glContextData["renderer"] }
};
auto macVersion = QSysInfo::macVersion();
if (macVersion != QSysInfo::MV_None) {
properties["os_osx_version"] = QSysInfo::macVersion();
}
auto windowsVersion = QSysInfo::windowsVersion();
if (windowsVersion != QSysInfo::WV_None) {
properties["os_win_version"] = QSysInfo::windowsVersion();
}
UserActivityLogger::getInstance().logAction("launch", properties);
// Tell our entity edit sender about our known jurisdictions // Tell our entity edit sender about our known jurisdictions
_entityEditSender.setServerJurisdictions(&_entityServerJurisdictions); _entityEditSender.setServerJurisdictions(&_entityServerJurisdictions);
_entityEditSender.setMyAvatar(getMyAvatar()); _entityEditSender.setMyAvatar(getMyAvatar());
@ -1063,6 +1095,89 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
} }
}); });
// 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 // Make sure we don't time out during slow operations at startup
updateHeartbeat(); updateHeartbeat();
@ -4578,6 +4693,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGlobalObject("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data()); scriptEngine->registerGlobalObject("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());
scriptEngine->registerGlobalObject("Reticle", getApplicationCompositor().getReticleInterface()); scriptEngine->registerGlobalObject("Reticle", getApplicationCompositor().getReticleInterface());
scriptEngine->registerGlobalObject("UserActivityLogger", DependencyManager::get<UserActivityLoggerScriptingInterface>().data());
} }
bool Application::canAcceptURL(const QString& urlString) const { bool Application::canAcceptURL(const QString& urlString) const {
@ -5180,6 +5297,11 @@ void Application::updateDisplayMode() {
return; return;
} }
UserActivityLogger::getInstance().logAction("changed_display_mode", {
{ "previous_display_mode", _displayPlugin ? _displayPlugin->getName() : "" },
{ "display_mode", newDisplayPlugin ? newDisplayPlugin->getName() : "" }
});
auto offscreenUi = DependencyManager::get<OffscreenUi>(); auto offscreenUi = DependencyManager::get<OffscreenUi>();
// Make the switch atomic from the perspective of other threads // Make the switch atomic from the perspective of other threads

View file

@ -211,6 +211,8 @@ public:
float getRenderResolutionScale() const; float getRenderResolutionScale() const;
qint64 getCurrentSessionRuntime() const { return _sessionRunTimer.elapsed(); }
bool isAboutToQuit() const { return _aboutToQuit; } bool isAboutToQuit() const { return _aboutToQuit; }
// the isHMDMode is true whenever we use the interface from an HMD and not a standard flat display // the isHMDMode is true whenever we use the interface from an HMD and not a standard flat display

View file

@ -80,7 +80,8 @@ void DiscoverabilityManager::updateLocation() {
locationObject.insert(FRIENDS_ONLY_KEY_IN_LOCATION, (_mode.get() == Discoverability::Friends)); locationObject.insert(FRIENDS_ONLY_KEY_IN_LOCATION, (_mode.get() == Discoverability::Friends));
// if we have a session ID add it now, otherwise add a null value // if we have a session ID add it now, otherwise add a null value
rootObject[SESSION_ID_KEY] = _sessionID.isEmpty() ? QJsonValue() : _sessionID; auto sessionID = accountManager->getSessionID();
rootObject[SESSION_ID_KEY] = sessionID.isNull() ? QJsonValue() : sessionID.toString();
JSONCallbackParameters callbackParameters; JSONCallbackParameters callbackParameters;
callbackParameters.jsonCallbackReceiver = this; callbackParameters.jsonCallbackReceiver = this;
@ -110,11 +111,8 @@ void DiscoverabilityManager::updateLocation() {
callbackParameters.jsonCallbackMethod = "handleHeartbeatResponse"; callbackParameters.jsonCallbackMethod = "handleHeartbeatResponse";
QJsonObject heartbeatObject; QJsonObject heartbeatObject;
if (!_sessionID.isEmpty()) { auto sessionID = accountManager->getSessionID();
heartbeatObject[SESSION_ID_KEY] = _sessionID; heartbeatObject[SESSION_ID_KEY] = sessionID.isNull() ? QJsonValue() : sessionID.toString();
} else {
heartbeatObject[SESSION_ID_KEY] = QJsonValue();
}
accountManager->sendRequest(API_USER_HEARTBEAT_PATH, AccountManagerAuth::Optional, accountManager->sendRequest(API_USER_HEARTBEAT_PATH, AccountManagerAuth::Optional,
QNetworkAccessManager::PutOperation, callbackParameters, QNetworkAccessManager::PutOperation, callbackParameters,
@ -126,11 +124,11 @@ void DiscoverabilityManager::handleHeartbeatResponse(QNetworkReply& requestReply
auto dataObject = AccountManager::dataObjectFromResponse(requestReply); auto dataObject = AccountManager::dataObjectFromResponse(requestReply);
if (!dataObject.isEmpty()) { if (!dataObject.isEmpty()) {
_sessionID = dataObject[SESSION_ID_KEY].toString(); auto sessionID = dataObject[SESSION_ID_KEY].toString();
// give that session ID to the account manager // give that session ID to the account manager
auto accountManager = DependencyManager::get<AccountManager>(); auto accountManager = DependencyManager::get<AccountManager>();
accountManager->setSessionID(_sessionID); accountManager->setSessionID(sessionID);
} }
} }

View file

@ -49,7 +49,6 @@ private:
DiscoverabilityManager(); DiscoverabilityManager();
Setting::Handle<int> _mode; Setting::Handle<int> _mode;
QString _sessionID;
QJsonObject _lastLocationObject; QJsonObject _lastLocationObject;
}; };

View file

@ -44,6 +44,20 @@ bool AvatarHashMap::isAvatarInRange(const glm::vec3& position, const float range
return false; return false;
} }
int AvatarHashMap::numberOfAvatarsInRange(const glm::vec3& position, float rangeMeters) {
auto hashCopy = getHashCopy();
auto rangeMeters2 = rangeMeters * rangeMeters;
int count = 0;
for (const AvatarSharedPointer& sharedAvatar : hashCopy) {
glm::vec3 avatarPosition = sharedAvatar->getPosition();
auto distance2 = glm::distance2(avatarPosition, position);
if (distance2 < rangeMeters2) {
++count;
}
}
return count;
}
AvatarSharedPointer AvatarHashMap::newSharedAvatar() { AvatarSharedPointer AvatarHashMap::newSharedAvatar() {
return std::make_shared<AvatarData>(); return std::make_shared<AvatarData>();
} }

View file

@ -39,6 +39,7 @@ public:
Q_INVOKABLE AvatarData* getAvatar(QUuid avatarID); Q_INVOKABLE AvatarData* getAvatar(QUuid avatarID);
virtual AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID) { return findAvatar(sessionID); } virtual AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID) { return findAvatar(sessionID); }
int numberOfAvatarsInRange(const glm::vec3& position, float rangeMeters);
signals: signals:
void avatarAddedEvent(const QUuid& sessionUUID); void avatarAddedEvent(const QUuid& sessionUUID);

View file

@ -5,6 +5,7 @@
#include <QtGui/QSurfaceFormat> #include <QtGui/QSurfaceFormat>
#include <QtOpenGL/QGL> #include <QtOpenGL/QGL>
#include <QOpenGLContext> #include <QOpenGLContext>
#include <QtCore/QRegularExpression>
const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() { const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() {
static QSurfaceFormat format; static QSurfaceFormat format;
@ -39,6 +40,13 @@ const QGLFormat& getDefaultGLFormat() {
return glFormat; return glFormat;
} }
int glVersionToInteger(QString glVersion) {
QStringList versionParts = glVersion.split(QRegularExpression("[\\.\\s]"));
int majorNumber = versionParts[0].toInt();
int minorNumber = versionParts[1].toInt();
return majorNumber * 100 + minorNumber * 10;
}
QJsonObject getGLContextData() { QJsonObject getGLContextData() {
if (!QOpenGLContext::currentContext()) { if (!QOpenGLContext::currentContext()) {
return QJsonObject(); return QJsonObject();

View file

@ -27,5 +27,6 @@ void setGLFormatVersion(F& format, int major = 4, int minor = 5) { format.setVer
const QSurfaceFormat& getDefaultOpenGLSurfaceFormat(); const QSurfaceFormat& getDefaultOpenGLSurfaceFormat();
const QGLFormat& getDefaultGLFormat(); const QGLFormat& getDefaultGLFormat();
QJsonObject getGLContextData(); QJsonObject getGLContextData();
int glVersionToInteger(QString glVersion);
#endif #endif

View file

@ -44,6 +44,7 @@ Q_DECLARE_METATYPE(QNetworkAccessManager::Operation)
Q_DECLARE_METATYPE(JSONCallbackParameters) Q_DECLARE_METATYPE(JSONCallbackParameters)
const QString ACCOUNTS_GROUP = "accounts"; const QString ACCOUNTS_GROUP = "accounts";
static const auto METAVERSE_SESSION_ID_HEADER = QString("HFM-SessionID").toLocal8Bit();
JSONCallbackParameters::JSONCallbackParameters(QObject* jsonCallbackReceiver, const QString& jsonCallbackMethod, JSONCallbackParameters::JSONCallbackParameters(QObject* jsonCallbackReceiver, const QString& jsonCallbackMethod,
QObject* errorCallbackReceiver, const QString& errorCallbackMethod, QObject* errorCallbackReceiver, const QString& errorCallbackMethod,
@ -222,8 +223,7 @@ void AccountManager::sendRequest(const QString& path,
// if we're allowed to send usage data, include whatever the current session ID is with this request // if we're allowed to send usage data, include whatever the current session ID is with this request
auto& activityLogger = UserActivityLogger::getInstance(); auto& activityLogger = UserActivityLogger::getInstance();
if (activityLogger.isEnabled()) { if (activityLogger.isEnabled()) {
static const QString METAVERSE_SESSION_ID_HEADER = "HFM-SessionID"; networkRequest.setRawHeader(METAVERSE_SESSION_ID_HEADER,
networkRequest.setRawHeader(METAVERSE_SESSION_ID_HEADER.toLocal8Bit(),
uuidStringWithoutCurlyBraces(_sessionID).toLocal8Bit()); uuidStringWithoutCurlyBraces(_sessionID).toLocal8Bit());
} }
@ -322,6 +322,9 @@ void AccountManager::processReply() {
QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender()); QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender());
if (requestReply->error() == QNetworkReply::NoError) { if (requestReply->error() == QNetworkReply::NoError) {
if (requestReply->hasRawHeader(METAVERSE_SESSION_ID_HEADER)) {
_sessionID = requestReply->rawHeader(METAVERSE_SESSION_ID_HEADER);
}
passSuccessToCallback(requestReply); passSuccessToCallback(requestReply);
} else { } else {
passErrorToCallback(requestReply); passErrorToCallback(requestReply);

View file

@ -26,9 +26,9 @@
class JSONCallbackParameters { class JSONCallbackParameters {
public: public:
JSONCallbackParameters(QObject* jsonCallbackReceiver = NULL, const QString& jsonCallbackMethod = QString(), JSONCallbackParameters(QObject* jsonCallbackReceiver = nullptr, const QString& jsonCallbackMethod = QString(),
QObject* errorCallbackReceiver = NULL, const QString& errorCallbackMethod = QString(), QObject* errorCallbackReceiver = nullptr, const QString& errorCallbackMethod = QString(),
QObject* updateReceiver = NULL, const QString& updateSlot = QString()); QObject* updateReceiver = nullptr, const QString& updateSlot = QString());
bool isEmpty() const { return !jsonCallbackReceiver && !errorCallbackReceiver; } bool isEmpty() const { return !jsonCallbackReceiver && !errorCallbackReceiver; }
@ -86,6 +86,7 @@ public:
static QJsonObject dataObjectFromResponse(QNetworkReply& requestReply); static QJsonObject dataObjectFromResponse(QNetworkReply& requestReply);
QUuid getSessionID() const { return _sessionID; }
void setSessionID(const QUuid& sessionID) { _sessionID = sessionID; } void setSessionID(const QUuid& sessionID) { _sessionID = sessionID; }
public slots: public slots:
@ -139,7 +140,7 @@ private:
bool _isWaitingForKeypairResponse { false }; bool _isWaitingForKeypairResponse { false };
QByteArray _pendingPrivateKey; QByteArray _pendingPrivateKey;
QUuid _sessionID; QUuid _sessionID { QUuid::createUuid() };
}; };
#endif // hifi_AccountManager_h #endif // hifi_AccountManager_h

View file

@ -23,6 +23,7 @@
#include "AddressManager.h" #include "AddressManager.h"
#include "NodeList.h" #include "NodeList.h"
#include "NetworkLogging.h" #include "NetworkLogging.h"
#include "UserActivityLogger.h"
const QString ADDRESS_MANAGER_SETTINGS_GROUP = "AddressManager"; const QString ADDRESS_MANAGER_SETTINGS_GROUP = "AddressManager";
@ -130,6 +131,10 @@ const JSONCallbackParameters& AddressManager::apiCallbackParameters() {
} }
bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) { bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) {
static QString URL_TYPE_USER = "user";
static QString URL_TYPE_DOMAIN_ID = "domain_id";
static QString URL_TYPE_PLACE = "place";
static QString URL_TYPE_NETWORK_ADDRESS = "network_address";
if (lookupUrl.scheme() == HIFI_URL_SCHEME) { if (lookupUrl.scheme() == HIFI_URL_SCHEME) {
qCDebug(networking) << "Trying to go to URL" << lookupUrl.toString(); qCDebug(networking) << "Trying to go to URL" << lookupUrl.toString();
@ -147,6 +152,8 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) {
if (handleUsername(lookupUrl.authority())) { if (handleUsername(lookupUrl.authority())) {
// handled a username for lookup // handled a username for lookup
UserActivityLogger::getInstance().wentTo(trigger, URL_TYPE_USER, lookupUrl.toString());
// in case we're failing to connect to where we thought this user was // in case we're failing to connect to where we thought this user was
// store their username as previous lookup so we can refresh their location via API // store their username as previous lookup so we can refresh their location via API
_previousLookup = lookupUrl; _previousLookup = lookupUrl;
@ -157,6 +164,8 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) {
if (handleNetworkAddress(lookupUrl.host() if (handleNetworkAddress(lookupUrl.host()
+ (lookupUrl.port() == -1 ? "" : ":" + QString::number(lookupUrl.port())), trigger, hostChanged)) { + (lookupUrl.port() == -1 ? "" : ":" + QString::number(lookupUrl.port())), trigger, hostChanged)) {
UserActivityLogger::getInstance().wentTo(trigger, URL_TYPE_NETWORK_ADDRESS, lookupUrl.toString());
// a network address lookup clears the previous lookup since we don't expect to re-attempt it // a network address lookup clears the previous lookup since we don't expect to re-attempt it
_previousLookup.clear(); _previousLookup.clear();
@ -174,6 +183,8 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) {
// we may have a path that defines a relative viewpoint - if so we should jump to that now // we may have a path that defines a relative viewpoint - if so we should jump to that now
handlePath(path, trigger); handlePath(path, trigger);
} else if (handleDomainID(lookupUrl.host())){ } else if (handleDomainID(lookupUrl.host())){
UserActivityLogger::getInstance().wentTo(trigger, URL_TYPE_DOMAIN_ID, lookupUrl.toString());
// store this domain ID as the previous lookup in case we're failing to connect and want to refresh API info // store this domain ID as the previous lookup in case we're failing to connect and want to refresh API info
_previousLookup = lookupUrl; _previousLookup = lookupUrl;
@ -181,6 +192,8 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) {
// try to look up the domain ID on the metaverse API // try to look up the domain ID on the metaverse API
attemptDomainIDLookup(lookupUrl.host(), lookupUrl.path(), trigger); attemptDomainIDLookup(lookupUrl.host(), lookupUrl.path(), trigger);
} else { } else {
UserActivityLogger::getInstance().wentTo(trigger, URL_TYPE_PLACE, lookupUrl.toString());
// store this place name as the previous lookup in case we fail to connect and want to refresh API info // store this place name as the previous lookup in case we fail to connect and want to refresh API info
_previousLookup = lookupUrl; _previousLookup = lookupUrl;

View file

@ -18,6 +18,7 @@
#include "UserActivityLogger.h" #include "UserActivityLogger.h"
#include <DependencyManager.h> #include <DependencyManager.h>
#include "AddressManager.h"
static const QString USER_ACTIVITY_URL = "/api/v1/user_activities"; static const QString USER_ACTIVITY_URL = "/api/v1/user_activities";
@ -125,6 +126,19 @@ void UserActivityLogger::changedDomain(QString domainURL) {
} }
void UserActivityLogger::connectedDevice(QString typeOfDevice, QString deviceName) { void UserActivityLogger::connectedDevice(QString typeOfDevice, QString deviceName) {
static QStringList DEVICE_BLACKLIST = {
"Desktop",
"NullDisplayPlugin",
"3D TV - Side by Side Stereo",
"3D TV - Interleaved",
"Keyboard/Mouse"
};
if (DEVICE_BLACKLIST.contains(deviceName)) {
return;
}
const QString ACTION_NAME = "connected_device"; const QString ACTION_NAME = "connected_device";
QJsonObject actionDetails; QJsonObject actionDetails;
const QString TYPE_OF_DEVICE = "type_of_device"; const QString TYPE_OF_DEVICE = "type_of_device";
@ -148,12 +162,34 @@ void UserActivityLogger::loadedScript(QString scriptName) {
} }
void UserActivityLogger::wentTo(QString destinationType, QString destinationName) { void UserActivityLogger::wentTo(AddressManager::LookupTrigger lookupTrigger, QString destinationType, QString destinationName) {
// Only accept these types of triggers. Other triggers are usually used internally in AddressManager.
QString trigger;
switch (lookupTrigger) {
case AddressManager::UserInput:
trigger = "UserInput";
break;
case AddressManager::Back:
trigger = "Back";
break;
case AddressManager::Forward:
trigger = "Forward";
break;
case AddressManager::StartupFromSettings:
trigger = "StartupFromSettings";
break;
default:
return;
}
const QString ACTION_NAME = "went_to"; const QString ACTION_NAME = "went_to";
QJsonObject actionDetails; QJsonObject actionDetails;
const QString TRIGGER_TYPE_KEY = "trigger";
const QString DESTINATION_TYPE_KEY = "destination_type"; const QString DESTINATION_TYPE_KEY = "destination_type";
const QString DESTINATION_NAME_KEY = "detination_name"; const QString DESTINATION_NAME_KEY = "detination_name";
actionDetails.insert(TRIGGER_TYPE_KEY, trigger);
actionDetails.insert(DESTINATION_TYPE_KEY, destinationType); actionDetails.insert(DESTINATION_TYPE_KEY, destinationType);
actionDetails.insert(DESTINATION_NAME_KEY, destinationName); actionDetails.insert(DESTINATION_NAME_KEY, destinationName);

View file

@ -20,6 +20,7 @@
#include <QNetworkReply> #include <QNetworkReply>
#include <SettingHandle.h> #include <SettingHandle.h>
#include "AddressManager.h"
class UserActivityLogger : public QObject { class UserActivityLogger : public QObject {
Q_OBJECT Q_OBJECT
@ -42,7 +43,7 @@ public slots:
void changedDomain(QString domainURL); void changedDomain(QString domainURL);
void connectedDevice(QString typeOfDevice, QString deviceName); void connectedDevice(QString typeOfDevice, QString deviceName);
void loadedScript(QString scriptName); void loadedScript(QString scriptName);
void wentTo(QString destinationType, QString destinationName); void wentTo(AddressManager::LookupTrigger trigger, QString destinationType, QString destinationName);
private slots: private slots:
void requestError(QNetworkReply& errorReply); void requestError(QNetworkReply& errorReply);

View file

@ -0,0 +1,31 @@
//
// UserActivityLoggerScriptingInterface.h
// libraries/networking/src
//
// Created by Ryan Huffman on 6/06/16.
// Copyright 2016 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 "UserActivityLoggerScriptingInterface.h"
#include "UserActivityLogger.h"
void UserActivityLoggerScriptingInterface::enabledEdit() {
logAction("enabled_edit");
}
void UserActivityLoggerScriptingInterface::openedMarketplace() {
logAction("opened_marketplace");
}
void UserActivityLoggerScriptingInterface::toggledAway(bool isAway) {
logAction("toggled_away", { { "is_away", isAway } });
}
void UserActivityLoggerScriptingInterface::logAction(QString action, QJsonObject details) {
QMetaObject::invokeMethod(&UserActivityLogger::getInstance(), "logAction",
Q_ARG(QString, action),
Q_ARG(QJsonObject, details));
}

View file

@ -0,0 +1,31 @@
//
// UserActivityLoggerScriptingInterface.h
// libraries/networking/src
//
// Created by Ryan Huffman on 6/06/16.
// Copyright 2016 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
//
#ifndef hifi_UserActivityLoggerScriptingInterface_h
#define hifi_UserActivityLoggerScriptingInterface_h
#include <QObject>
#include <QJsonObject>
#include <DependencyManager.h>
class UserActivityLoggerScriptingInterface : public QObject, public Dependency {
Q_OBJECT
public:
Q_INVOKABLE void enabledEdit();
Q_INVOKABLE void openedMarketplace();
Q_INVOKABLE void toggledAway(bool isAway);
private:
void logAction(QString action, QJsonObject details = {});
};
#endif // hifi_UserActivityLoggerScriptingInterface_h

View file

@ -15,6 +15,7 @@
#include "Forward.h" #include "Forward.h"
class Plugin : public QObject { class Plugin : public QObject {
Q_OBJECT
public: public:
/// \return human-readable name /// \return human-readable name
virtual const QString& getName() const = 0; virtual const QString& getName() const = 0;
@ -63,6 +64,13 @@ public:
virtual void saveSettings() const {} virtual void saveSettings() const {}
virtual void loadSettings() {} virtual void loadSettings() {}
signals:
// These signals should be emitted when a device is first known to be available. In some cases this will
// be in `init()`, in other cases, like Neuron, this isn't known until activation.
// SDL2 isn't a device itself, but can have 0+ subdevices. subdeviceConnected is used in this case.
void deviceConnected(QString pluginName) const;
void subdeviceConnected(QString pluginName, QString subdeviceName) const;
protected: protected:
bool _active { false }; bool _active { false };
PluginContainer* _container { nullptr }; PluginContainer* _container { nullptr };

View file

@ -14,6 +14,9 @@
#include <QtCore/QDebug> #include <QtCore/QDebug>
#include <QtCore/QPluginLoader> #include <QtCore/QPluginLoader>
#include <DependencyManager.h>
#include <UserActivityLogger.h>
#include "RuntimePlugin.h" #include "RuntimePlugin.h"
#include "DisplayPlugin.h" #include "DisplayPlugin.h"
#include "InputPlugin.h" #include "InputPlugin.h"
@ -119,6 +122,15 @@ static DisplayPluginList displayPlugins;
const DisplayPluginList& PluginManager::getDisplayPlugins() { const DisplayPluginList& PluginManager::getDisplayPlugins() {
static std::once_flag once; static std::once_flag once;
static auto deviceAddedCallback = [](QString deviceName) {
qDebug() << "Added device: " << deviceName;
UserActivityLogger::getInstance().connectedDevice("display", deviceName);
};
static auto subdeviceAddedCallback = [](QString pluginName, QString deviceName) {
qDebug() << "Added subdevice: " << deviceName;
UserActivityLogger::getInstance().connectedDevice("display", pluginName + " | " + deviceName);
};
std::call_once(once, [&] { std::call_once(once, [&] {
// Grab the built in plugins // Grab the built in plugins
displayPlugins = ::getDisplayPlugins(); displayPlugins = ::getDisplayPlugins();
@ -133,6 +145,8 @@ const DisplayPluginList& PluginManager::getDisplayPlugins() {
} }
} }
for (auto plugin : displayPlugins) { for (auto plugin : displayPlugins) {
connect(plugin.get(), &Plugin::deviceConnected, this, deviceAddedCallback, Qt::QueuedConnection);
connect(plugin.get(), &Plugin::subdeviceConnected, this, subdeviceAddedCallback, Qt::QueuedConnection);
plugin->setContainer(_container); plugin->setContainer(_container);
plugin->init(); plugin->init();
} }
@ -154,6 +168,15 @@ void PluginManager::disableDisplayPlugin(const QString& name) {
const InputPluginList& PluginManager::getInputPlugins() { const InputPluginList& PluginManager::getInputPlugins() {
static InputPluginList inputPlugins; static InputPluginList inputPlugins;
static std::once_flag once; static std::once_flag once;
static auto deviceAddedCallback = [](QString deviceName) {
qDebug() << "Added device: " << deviceName;
UserActivityLogger::getInstance().connectedDevice("input", deviceName);
};
static auto subdeviceAddedCallback = [](QString pluginName, QString deviceName) {
qDebug() << "Added subdevice: " << deviceName;
UserActivityLogger::getInstance().connectedDevice("input", pluginName + " | " + deviceName);
};
std::call_once(once, [&] { std::call_once(once, [&] {
inputPlugins = ::getInputPlugins(); inputPlugins = ::getInputPlugins();
@ -170,6 +193,8 @@ const InputPluginList& PluginManager::getInputPlugins() {
} }
for (auto plugin : inputPlugins) { for (auto plugin : inputPlugins) {
connect(plugin.get(), &Plugin::deviceConnected, this, deviceAddedCallback, Qt::QueuedConnection);
connect(plugin.get(), &Plugin::subdeviceConnected, this, subdeviceAddedCallback, Qt::QueuedConnection);
plugin->setContainer(_container); plugin->setContainer(_container);
plugin->init(); plugin->init();
} }

View file

@ -122,7 +122,7 @@ GPUIdent* GPUIdent::ensureQuery(const QString& vendor, const QString& renderer)
} }
if (count > bestCount) { if (count > bestCount) {
bestCount = count; bestCount = count;
_name = sString; _name = QString(sString).trimmed();
hr = spInstance->Get(CComBSTR(_T("DriverVersion")), 0, &var, 0, 0); hr = spInstance->Get(CComBSTR(_T("DriverVersion")), 0, &var, 0, 0);
if (hr == S_OK) { if (hr == S_OK) {

View file

@ -28,6 +28,7 @@
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#include "CPUIdent.h" #include "CPUIdent.h"
#include <Psapi.h>
#endif #endif
@ -843,3 +844,29 @@ void printSystemInformation() {
(envVariables.contains(env) ? " = " + envVariables.value(env) : " NOT FOUND"); (envVariables.contains(env) ? " = " + envVariables.value(env) : " NOT FOUND");
} }
} }
bool getMemoryInfo(MemoryInfo& info) {
#ifdef Q_OS_WIN
MEMORYSTATUSEX ms;
ms.dwLength = sizeof(ms);
if (!GlobalMemoryStatusEx(&ms)) {
return false;
}
info.totalMemoryBytes = ms.ullTotalPhys;
info.availMemoryBytes = ms.ullAvailPhys;
info.usedMemoryBytes = ms.ullTotalPhys - ms.ullAvailPhys;
PROCESS_MEMORY_COUNTERS_EX pmc;
if (!GetProcessMemoryInfo(GetCurrentProcess(), reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmc), sizeof(pmc))) {
return false;
}
info.processUsedMemoryBytes = pmc.PrivateUsage;
info.processPeakUsedMemoryBytes = pmc.PeakPagefileUsage;
return true;
#endif
return false;
}

View file

@ -204,4 +204,14 @@ void disableQtBearerPoll();
void printSystemInformation(); void printSystemInformation();
struct MemoryInfo {
uint64_t totalMemoryBytes;
uint64_t availMemoryBytes;
uint64_t usedMemoryBytes;
uint64_t processUsedMemoryBytes;
uint64_t processPeakUsedMemoryBytes;
};
bool getMemoryInfo(MemoryInfo& info);
#endif // hifi_SharedUtil_h #endif // hifi_SharedUtil_h

View file

@ -387,6 +387,8 @@ bool NeuronPlugin::activate() {
} else { } else {
qCDebug(inputplugins) << "NeuronPlugin: success connecting to " << _serverAddress.c_str() << ":" << _serverPort; qCDebug(inputplugins) << "NeuronPlugin: success connecting to " << _serverAddress.c_str() << ":" << _serverPort;
emit deviceConnected(getName());
BRRegisterAutoSyncParmeter(_socketRef, Cmd_CombinationMode); BRRegisterAutoSyncParmeter(_socketRef, Cmd_CombinationMode);
return true; return true;
} }

View file

@ -66,6 +66,7 @@ void SDL2Manager::init() {
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
userInputMapper->registerDevice(joystick); userInputMapper->registerDevice(joystick);
emit joystickAdded(joystick.get()); emit joystickAdded(joystick.get());
emit subdeviceConnected(getName(), SDL_GameControllerName(controller));
} }
} }
} }
@ -157,6 +158,7 @@ void SDL2Manager::pluginUpdate(float deltaTime, const controller::InputCalibrati
_openJoysticks[id] = joystick; _openJoysticks[id] = joystick;
userInputMapper->registerDevice(joystick); userInputMapper->registerDevice(joystick);
emit joystickAdded(joystick.get()); emit joystickAdded(joystick.get());
emit subdeviceConnected(getName(), SDL_GameControllerName(controller));
} }
} else if (event.type == SDL_CONTROLLERDEVICEREMOVED) { } else if (event.type == SDL_CONTROLLERDEVICEREMOVED) {
if (_openJoysticks.contains(event.cdevice.which)) { if (_openJoysticks.contains(event.cdevice.which)) {

View file

@ -137,6 +137,12 @@ void SixenseManager::setSixenseFilter(bool filter) {
void SixenseManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { void SixenseManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) {
BAIL_IF_NOT_LOADED BAIL_IF_NOT_LOADED
static bool sixenseHasBeenConnected { false };
if (!sixenseHasBeenConnected && sixenseIsBaseConnected(0)) {
sixenseHasBeenConnected = true;
emit deviceConnected(getName());
}
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
userInputMapper->withLock([&, this]() { userInputMapper->withLock([&, this]() {
_inputDevice->update(deltaTime, inputCalibrationData); _inputDevice->update(deltaTime, inputCalibrationData);

View file

@ -58,7 +58,6 @@ bool SpacemouseManager::activate() {
if (instance->getDeviceID() == controller::Input::INVALID_DEVICE) { if (instance->getDeviceID() == controller::Input::INVALID_DEVICE) {
auto userInputMapper = DependencyManager::get<UserInputMapper>(); auto userInputMapper = DependencyManager::get<UserInputMapper>();
userInputMapper->registerDevice(instance); userInputMapper->registerDevice(instance);
UserActivityLogger::getInstance().connectedDevice("controller", NAME);
} }
return true; return true;
} }
@ -329,7 +328,6 @@ bool SpacemouseManager::RawInputEventFilter(void* msg, long* result) {
auto userInputMapper = DependencyManager::get<UserInputMapper>(); auto userInputMapper = DependencyManager::get<UserInputMapper>();
if (Is3dmouseAttached() && instance->getDeviceID() == controller::Input::INVALID_DEVICE) { if (Is3dmouseAttached() && instance->getDeviceID() == controller::Input::INVALID_DEVICE) {
userInputMapper->registerDevice(instance); userInputMapper->registerDevice(instance);
UserActivityLogger::getInstance().connectedDevice("controller", "Spacemouse");
} }
else if (!Is3dmouseAttached() && instance->getDeviceID() != controller::Input::INVALID_DEVICE) { else if (!Is3dmouseAttached() && instance->getDeviceID() != controller::Input::INVALID_DEVICE) {
userInputMapper->removeDevice(instance->getDeviceID()); userInputMapper->removeDevice(instance->getDeviceID());
@ -856,7 +854,7 @@ void SpacemouseManager::init() {
if (Is3dmouseAttached() && instance->getDeviceID() == controller::Input::INVALID_DEVICE) { if (Is3dmouseAttached() && instance->getDeviceID() == controller::Input::INVALID_DEVICE) {
auto userInputMapper = DependencyManager::get<UserInputMapper>(); auto userInputMapper = DependencyManager::get<UserInputMapper>();
userInputMapper->registerDevice(instance); userInputMapper->registerDevice(instance);
UserActivityLogger::getInstance().connectedDevice("controller", "Spacemouse"); emit deviceConnected(getName());
} }
//let one axis be dominant //let one axis be dominant
//ConnexionClientControl(fConnexionClientID, kConnexionCtlSetSwitches, kConnexionSwitchDominant | kConnexionSwitchEnableAll, NULL); //ConnexionClientControl(fConnexionClientID, kConnexionCtlSetSwitches, kConnexionSwitchDominant | kConnexionSwitchEnableAll, NULL);

View file

@ -28,6 +28,12 @@ bool OculusDisplayPlugin::internalActivate() {
return result; return result;
} }
void OculusDisplayPlugin::init() {
Plugin::init();
emit deviceConnected(getName());
}
void OculusDisplayPlugin::cycleDebugOutput() { void OculusDisplayPlugin::cycleDebugOutput() {
if (_session) { if (_session) {
currentDebugMode = static_cast<ovrPerfHudMode>((currentDebugMode + 1) % ovrPerfHud_Count); currentDebugMode = static_cast<ovrPerfHudMode>((currentDebugMode + 1) % ovrPerfHud_Count);

View file

@ -17,6 +17,8 @@ class OculusDisplayPlugin : public OculusBaseDisplayPlugin {
public: public:
const QString& getName() const override { return NAME; } const QString& getName() const override { return NAME; }
void init() override;
QString getPreferredAudioInDevice() const override; QString getPreferredAudioInDevice() const override;
QString getPreferredAudioOutDevice() const override; QString getPreferredAudioOutDevice() const override;

View file

@ -36,6 +36,12 @@ const QString OculusLegacyDisplayPlugin::NAME("Oculus Rift");
OculusLegacyDisplayPlugin::OculusLegacyDisplayPlugin() { OculusLegacyDisplayPlugin::OculusLegacyDisplayPlugin() {
} }
void OculusLegacyDisplayPlugin::init() {
Plugin::init();
emit deviceConnected(getName());
}
void OculusLegacyDisplayPlugin::resetSensors() { void OculusLegacyDisplayPlugin::resetSensors() {
ovrHmd_RecenterPose(_hmd); ovrHmd_RecenterPose(_hmd);
} }

View file

@ -23,6 +23,8 @@ public:
bool isSupported() const override; bool isSupported() const override;
const QString& getName() const override { return NAME; } const QString& getName() const override { return NAME; }
void init() override;
int getHmdScreen() const override; int getHmdScreen() const override;
// Stereo specific methods // Stereo specific methods

View file

@ -46,6 +46,12 @@ bool OpenVrDisplayPlugin::isSupported() const {
return openVrSupported(); return openVrSupported();
} }
void OpenVrDisplayPlugin::init() {
Plugin::init();
emit deviceConnected(getName());
}
bool OpenVrDisplayPlugin::internalActivate() { bool OpenVrDisplayPlugin::internalActivate() {
_openVrDisplayActive = true; _openVrDisplayActive = true;
_container->setIsOptionChecked(StandingHMDSensorMode, true); _container->setIsOptionChecked(StandingHMDSensorMode, true);

View file

@ -21,6 +21,8 @@ public:
bool isSupported() const override; bool isSupported() const override;
const QString& getName() const override { return NAME; } const QString& getName() const override { return NAME; }
void init() override;
float getTargetFrameRate() const override { return TARGET_RATE_OpenVr; } float getTargetFrameRate() const override { return TARGET_RATE_OpenVr; }
void customizeContext() override; void customizeContext() override;

View file

@ -228,7 +228,6 @@ void ViveControllerManager::pluginUpdate(float deltaTime, const controller::Inpu
if (!_registeredWithInputMapper && _inputDevice->_trackedControllers > 0) { if (!_registeredWithInputMapper && _inputDevice->_trackedControllers > 0) {
userInputMapper->registerDevice(_inputDevice); userInputMapper->registerDevice(_inputDevice);
_registeredWithInputMapper = true; _registeredWithInputMapper = true;
UserActivityLogger::getInstance().connectedDevice("spatial_controller", "steamVR");
} }
} }

View file

@ -158,6 +158,8 @@ function goAway() {
return; return;
} }
UserActivityLogger.toggledAway(true);
isAway = true; isAway = true;
print('going "away"'); print('going "away"');
wasMuted = AudioDevice.getMuted(); wasMuted = AudioDevice.getMuted();
@ -191,6 +193,9 @@ function goActive() {
if (!isAway) { if (!isAway) {
return; return;
} }
UserActivityLogger.toggledAway(false);
isAway = false; isAway = false;
print('going "active"'); print('going "active"');
if (!wasMuted) { if (!wasMuted) {

View file

@ -150,6 +150,8 @@ function showMarketplace(marketplaceID) {
marketplaceWindow.setURL(url); marketplaceWindow.setURL(url);
marketplaceWindow.setVisible(true); marketplaceWindow.setVisible(true);
marketplaceWindow.raise(); marketplaceWindow.raise();
UserActivityLogger.logAction("opened_marketplace");
} }
function hideMarketplace() { function hideMarketplace() {
@ -347,6 +349,7 @@ var toolBar = (function() {
selectionManager.clearSelections(); selectionManager.clearSelections();
cameraManager.disable(); cameraManager.disable();
} else { } else {
UserActivityLogger.enabledEdit();
hasShownPropertiesTool = false; hasShownPropertiesTool = false;
entityListTool.setVisible(true); entityListTool.setVisible(true);
gridTool.setVisible(true); gridTool.setVisible(true);

View file

@ -33,6 +33,8 @@ function showExamples(marketplaceID) {
print("setting examples URL to " + url); print("setting examples URL to " + url);
examplesWindow.setURL(url); examplesWindow.setURL(url);
examplesWindow.setVisible(true); examplesWindow.setVisible(true);
UserActivityLogger.openedMarketplace();
} }
function hideExamples() { function hideExamples() {