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.
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.
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
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)
set(NEURON_URL "https://s3.amazonaws.com/hifi-public/dependencies/neuron_datareader_b.12.zip")
set(NEURON_URL_MD5 "0ab54ca04c9cc8094e0fa046c226e574")
set(NEURON_URL "https://s3.amazonaws.com/hifi-public/dependencies/neuron_datareader_b.12.2.zip")
set(NEURON_URL_MD5 "84273ad2200bf86a9279d1f412a822ca")
ExternalProject_Add(${EXTERNAL_NAME}
URL ${NEURON_URL}

View file

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

View file

@ -68,6 +68,7 @@
#include <input-plugins/InputPlugin.h>
#include <controllers/UserInputMapper.h>
#include <controllers/StateController.h>
#include <UserActivityLoggerScriptingInterface.h>
#include <LogHandler.h>
#include <MainWindow.h>
#include <MessagesClient.h>
@ -150,6 +151,8 @@
#include "InterfaceParentFinder.h"
#include "FrameTimingsScriptingInterface.h"
#include <GPUIdent.h>
#include <gl/GLHelpers.h>
// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
// FIXME seems to be broken.
@ -437,7 +440,7 @@ bool setupEssentials(int& argc, char** argv) {
DependencyManager::set<HMDScriptingInterface>();
DependencyManager::set<ResourceScriptingInterface>();
DependencyManager::set<ToolbarScriptingInterface>();
DependencyManager::set<UserActivityLoggerScriptingInterface>();
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
DependencyManager::set<SpeechRecognizer>();
@ -675,10 +678,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
accountManager->setIsAgent(true);
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>();
// 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
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
_entityEditSender.setServerJurisdictions(&_entityServerJurisdictions);
_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
updateHeartbeat();
@ -4578,6 +4693,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGlobalObject("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());
scriptEngine->registerGlobalObject("Reticle", getApplicationCompositor().getReticleInterface());
scriptEngine->registerGlobalObject("UserActivityLogger", DependencyManager::get<UserActivityLoggerScriptingInterface>().data());
}
bool Application::canAcceptURL(const QString& urlString) const {
@ -5180,6 +5297,11 @@ void Application::updateDisplayMode() {
return;
}
UserActivityLogger::getInstance().logAction("changed_display_mode", {
{ "previous_display_mode", _displayPlugin ? _displayPlugin->getName() : "" },
{ "display_mode", newDisplayPlugin ? newDisplayPlugin->getName() : "" }
});
auto offscreenUi = DependencyManager::get<OffscreenUi>();
// Make the switch atomic from the perspective of other threads

View file

@ -211,6 +211,8 @@ public:
float getRenderResolutionScale() const;
qint64 getCurrentSessionRuntime() const { return _sessionRunTimer.elapsed(); }
bool isAboutToQuit() const { return _aboutToQuit; }
// 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));
// 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;
callbackParameters.jsonCallbackReceiver = this;
@ -110,11 +111,8 @@ void DiscoverabilityManager::updateLocation() {
callbackParameters.jsonCallbackMethod = "handleHeartbeatResponse";
QJsonObject heartbeatObject;
if (!_sessionID.isEmpty()) {
heartbeatObject[SESSION_ID_KEY] = _sessionID;
} else {
heartbeatObject[SESSION_ID_KEY] = QJsonValue();
}
auto sessionID = accountManager->getSessionID();
heartbeatObject[SESSION_ID_KEY] = sessionID.isNull() ? QJsonValue() : sessionID.toString();
accountManager->sendRequest(API_USER_HEARTBEAT_PATH, AccountManagerAuth::Optional,
QNetworkAccessManager::PutOperation, callbackParameters,
@ -126,11 +124,11 @@ void DiscoverabilityManager::handleHeartbeatResponse(QNetworkReply& requestReply
auto dataObject = AccountManager::dataObjectFromResponse(requestReply);
if (!dataObject.isEmpty()) {
_sessionID = dataObject[SESSION_ID_KEY].toString();
auto sessionID = dataObject[SESSION_ID_KEY].toString();
// give that session ID to the account manager
auto accountManager = DependencyManager::get<AccountManager>();
accountManager->setSessionID(_sessionID);
accountManager->setSessionID(sessionID);
}
}

View file

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

View file

@ -44,6 +44,20 @@ bool AvatarHashMap::isAvatarInRange(const glm::vec3& position, const float range
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() {
return std::make_shared<AvatarData>();
}

View file

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

View file

@ -5,6 +5,7 @@
#include <QtGui/QSurfaceFormat>
#include <QtOpenGL/QGL>
#include <QOpenGLContext>
#include <QtCore/QRegularExpression>
const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() {
static QSurfaceFormat format;
@ -39,6 +40,13 @@ const QGLFormat& getDefaultGLFormat() {
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() {
if (!QOpenGLContext::currentContext()) {
return QJsonObject();

View file

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

View file

@ -44,6 +44,7 @@ Q_DECLARE_METATYPE(QNetworkAccessManager::Operation)
Q_DECLARE_METATYPE(JSONCallbackParameters)
const QString ACCOUNTS_GROUP = "accounts";
static const auto METAVERSE_SESSION_ID_HEADER = QString("HFM-SessionID").toLocal8Bit();
JSONCallbackParameters::JSONCallbackParameters(QObject* jsonCallbackReceiver, const QString& jsonCallbackMethod,
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
auto& activityLogger = UserActivityLogger::getInstance();
if (activityLogger.isEnabled()) {
static const QString METAVERSE_SESSION_ID_HEADER = "HFM-SessionID";
networkRequest.setRawHeader(METAVERSE_SESSION_ID_HEADER.toLocal8Bit(),
networkRequest.setRawHeader(METAVERSE_SESSION_ID_HEADER,
uuidStringWithoutCurlyBraces(_sessionID).toLocal8Bit());
}
@ -322,6 +322,9 @@ void AccountManager::processReply() {
QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender());
if (requestReply->error() == QNetworkReply::NoError) {
if (requestReply->hasRawHeader(METAVERSE_SESSION_ID_HEADER)) {
_sessionID = requestReply->rawHeader(METAVERSE_SESSION_ID_HEADER);
}
passSuccessToCallback(requestReply);
} else {
passErrorToCallback(requestReply);

View file

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

View file

@ -23,6 +23,7 @@
#include "AddressManager.h"
#include "NodeList.h"
#include "NetworkLogging.h"
#include "UserActivityLogger.h"
const QString ADDRESS_MANAGER_SETTINGS_GROUP = "AddressManager";
@ -130,6 +131,10 @@ const JSONCallbackParameters& AddressManager::apiCallbackParameters() {
}
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) {
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())) {
// 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
// store their username as previous lookup so we can refresh their location via API
_previousLookup = lookupUrl;
@ -157,6 +164,8 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) {
if (handleNetworkAddress(lookupUrl.host()
+ (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
_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
handlePath(path, trigger);
} 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
_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
attemptDomainIDLookup(lookupUrl.host(), lookupUrl.path(), trigger);
} 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
_previousLookup = lookupUrl;

View file

@ -18,6 +18,7 @@
#include "UserActivityLogger.h"
#include <DependencyManager.h>
#include "AddressManager.h"
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) {
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";
QJsonObject actionDetails;
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";
QJsonObject actionDetails;
const QString TRIGGER_TYPE_KEY = "trigger";
const QString DESTINATION_TYPE_KEY = "destination_type";
const QString DESTINATION_NAME_KEY = "detination_name";
actionDetails.insert(TRIGGER_TYPE_KEY, trigger);
actionDetails.insert(DESTINATION_TYPE_KEY, destinationType);
actionDetails.insert(DESTINATION_NAME_KEY, destinationName);

View file

@ -20,6 +20,7 @@
#include <QNetworkReply>
#include <SettingHandle.h>
#include "AddressManager.h"
class UserActivityLogger : public QObject {
Q_OBJECT
@ -42,7 +43,7 @@ public slots:
void changedDomain(QString domainURL);
void connectedDevice(QString typeOfDevice, QString deviceName);
void loadedScript(QString scriptName);
void wentTo(QString destinationType, QString destinationName);
void wentTo(AddressManager::LookupTrigger trigger, QString destinationType, QString destinationName);
private slots:
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"
class Plugin : public QObject {
Q_OBJECT
public:
/// \return human-readable name
virtual const QString& getName() const = 0;
@ -63,6 +64,13 @@ public:
virtual void saveSettings() const {}
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:
bool _active { false };
PluginContainer* _container { nullptr };

View file

@ -14,6 +14,9 @@
#include <QtCore/QDebug>
#include <QtCore/QPluginLoader>
#include <DependencyManager.h>
#include <UserActivityLogger.h>
#include "RuntimePlugin.h"
#include "DisplayPlugin.h"
#include "InputPlugin.h"
@ -119,6 +122,15 @@ static DisplayPluginList displayPlugins;
const DisplayPluginList& PluginManager::getDisplayPlugins() {
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, [&] {
// Grab the built in plugins
displayPlugins = ::getDisplayPlugins();
@ -133,6 +145,8 @@ const DisplayPluginList& PluginManager::getDisplayPlugins() {
}
}
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->init();
}
@ -154,6 +168,15 @@ void PluginManager::disableDisplayPlugin(const QString& name) {
const InputPluginList& PluginManager::getInputPlugins() {
static InputPluginList inputPlugins;
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, [&] {
inputPlugins = ::getInputPlugins();
@ -170,6 +193,8 @@ const InputPluginList& PluginManager::getInputPlugins() {
}
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->init();
}

View file

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

View file

@ -28,6 +28,7 @@
#ifdef Q_OS_WIN
#include "CPUIdent.h"
#include <Psapi.h>
#endif
@ -843,3 +844,29 @@ void printSystemInformation() {
(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();
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

View file

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

View file

@ -66,6 +66,7 @@ void SDL2Manager::init() {
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
userInputMapper->registerDevice(joystick);
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;
userInputMapper->registerDevice(joystick);
emit joystickAdded(joystick.get());
emit subdeviceConnected(getName(), SDL_GameControllerName(controller));
}
} else if (event.type == SDL_CONTROLLERDEVICEREMOVED) {
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) {
BAIL_IF_NOT_LOADED
static bool sixenseHasBeenConnected { false };
if (!sixenseHasBeenConnected && sixenseIsBaseConnected(0)) {
sixenseHasBeenConnected = true;
emit deviceConnected(getName());
}
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
userInputMapper->withLock([&, this]() {
_inputDevice->update(deltaTime, inputCalibrationData);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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